]> git.sommitrealweird.co.uk Git - quagga-debian.git/commitdiff
Import Upstream version 1.2.2 upstream/1.2.2
authorBrett Parker <brettp@mythic-beasts.com>
Sat, 17 Mar 2018 14:58:46 +0000 (14:58 +0000)
committerBrett Parker <brettp@mythic-beasts.com>
Sat, 17 Mar 2018 14:58:46 +0000 (14:58 +0000)
788 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
HACKING.md [new file with mode: 0644]
HACKING.pending [new file with mode: 0644]
INSTALL.quagga.txt [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
README.NetBSD [new file with mode: 0755]
REPORTING-BUGS [new file with mode: 0644]
SERVICES [new file with mode: 0644]
TODO [new file with mode: 0644]
bgpd/.gitignore [new file with mode: 0644]
bgpd/BGP4-MIB.txt [new file with mode: 0644]
bgpd/IMPLEMENTATION.txt [new file with mode: 0644]
bgpd/Makefile.am [new file with mode: 0644]
bgpd/bgp_advertise.c [new file with mode: 0644]
bgpd/bgp_advertise.h [new file with mode: 0644]
bgpd/bgp_aspath.c [new file with mode: 0644]
bgpd/bgp_aspath.h [new file with mode: 0644]
bgpd/bgp_attr.c [new file with mode: 0644]
bgpd/bgp_attr.h [new file with mode: 0644]
bgpd/bgp_btoa.c [new file with mode: 0644]
bgpd/bgp_clist.c [new file with mode: 0644]
bgpd/bgp_clist.h [new file with mode: 0644]
bgpd/bgp_community.c [new file with mode: 0644]
bgpd/bgp_community.h [new file with mode: 0644]
bgpd/bgp_damp.c [new file with mode: 0644]
bgpd/bgp_damp.h [new file with mode: 0644]
bgpd/bgp_debug.c [new file with mode: 0644]
bgpd/bgp_debug.h [new file with mode: 0644]
bgpd/bgp_dump.c [new file with mode: 0644]
bgpd/bgp_dump.h [new file with mode: 0644]
bgpd/bgp_ecommunity.c [new file with mode: 0644]
bgpd/bgp_ecommunity.h [new file with mode: 0644]
bgpd/bgp_encap.c [new file with mode: 0644]
bgpd/bgp_encap.h [new file with mode: 0644]
bgpd/bgp_encap_tlv.c [new file with mode: 0644]
bgpd/bgp_encap_tlv.h [new file with mode: 0644]
bgpd/bgp_encap_types.h [new file with mode: 0644]
bgpd/bgp_filter.c [new file with mode: 0644]
bgpd/bgp_filter.h [new file with mode: 0644]
bgpd/bgp_fsm.c [new file with mode: 0644]
bgpd/bgp_fsm.h [new file with mode: 0644]
bgpd/bgp_fsm_4271.dot [new file with mode: 0644]
bgpd/bgp_fsm_quagga.dot [new file with mode: 0644]
bgpd/bgp_lcommunity.c [new file with mode: 0644]
bgpd/bgp_lcommunity.h [new file with mode: 0644]
bgpd/bgp_main.c [new file with mode: 0644]
bgpd/bgp_mpath.c [new file with mode: 0644]
bgpd/bgp_mpath.h [new file with mode: 0644]
bgpd/bgp_mplsvpn.c [new file with mode: 0644]
bgpd/bgp_mplsvpn.h [new file with mode: 0644]
bgpd/bgp_network.c [new file with mode: 0644]
bgpd/bgp_network.h [new file with mode: 0644]
bgpd/bgp_nexthop.c [new file with mode: 0644]
bgpd/bgp_nexthop.h [new file with mode: 0644]
bgpd/bgp_nht.c [new file with mode: 0644]
bgpd/bgp_nht.h [new file with mode: 0644]
bgpd/bgp_open.c [new file with mode: 0644]
bgpd/bgp_open.h [new file with mode: 0644]
bgpd/bgp_packet.c [new file with mode: 0644]
bgpd/bgp_packet.h [new file with mode: 0644]
bgpd/bgp_regex.c [new file with mode: 0644]
bgpd/bgp_regex.h [new file with mode: 0644]
bgpd/bgp_route.c [new file with mode: 0644]
bgpd/bgp_route.h [new file with mode: 0644]
bgpd/bgp_routemap.c [new file with mode: 0644]
bgpd/bgp_snmp.c [new file with mode: 0644]
bgpd/bgp_snmp.h [new file with mode: 0644]
bgpd/bgp_table.c [new file with mode: 0644]
bgpd/bgp_table.h [new file with mode: 0644]
bgpd/bgp_vty.c [new file with mode: 0644]
bgpd/bgp_vty.h [new file with mode: 0644]
bgpd/bgp_zebra.c [new file with mode: 0644]
bgpd/bgp_zebra.h [new file with mode: 0644]
bgpd/bgpd.c [new file with mode: 0644]
bgpd/bgpd.conf.sample [new file with mode: 0644]
bgpd/bgpd.conf.sample2 [new file with mode: 0644]
bgpd/bgpd.h [new file with mode: 0644]
bootstrap.sh [new file with mode: 0755]
buildtest.sh [new file with mode: 0755]
common.am [new file with mode: 0644]
configure.ac [new file with mode: 0755]
doc/.gitignore [new file with mode: 0644]
doc/BGP-TypeCode [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/appendix.texi [new file with mode: 0644]
doc/basic.texi [new file with mode: 0644]
doc/bgpd.8 [new file with mode: 0644]
doc/bgpd.texi [new file with mode: 0644]
doc/defines.texi [new file with mode: 0644]
doc/draft-zebra-00.ms [new file with mode: 0644]
doc/fig-normal-processing.dia [new file with mode: 0644]
doc/fig-normal-processing.png [new file with mode: 0644]
doc/fig-normal-processing.txt [new file with mode: 0644]
doc/fig-rs-processing.dia [new file with mode: 0644]
doc/fig-rs-processing.png [new file with mode: 0644]
doc/fig-rs-processing.txt [new file with mode: 0644]
doc/fig_topologies_full.txt [new file with mode: 0644]
doc/fig_topologies_rs.txt [new file with mode: 0644]
doc/filter.texi [new file with mode: 0644]
doc/install.texi [new file with mode: 0644]
doc/ipv6.texi [new file with mode: 0644]
doc/isisd.8 [new file with mode: 0644]
doc/isisd.texi [new file with mode: 0644]
doc/kernel.texi [new file with mode: 0644]
doc/main.texi [new file with mode: 0644]
doc/mpls/.gitignore [new file with mode: 0644]
doc/mpls/ChangeLog.opaque.txt [new file with mode: 0644]
doc/mpls/cli_summary.txt [new file with mode: 0644]
doc/mpls/opaque_lsa.txt [new file with mode: 0644]
doc/mpls/ospfd.conf [new file with mode: 0644]
doc/next-hop-tracking.txt [new file with mode: 0644]
doc/nhrpd.8 [new file with mode: 0644]
doc/nhrpd.texi [new file with mode: 0644]
doc/ospf6d.8 [new file with mode: 0644]
doc/ospf6d.texi [new file with mode: 0644]
doc/ospf_fundamentals.texi [new file with mode: 0644]
doc/ospfclient.8 [new file with mode: 0644]
doc/ospfd.8 [new file with mode: 0644]
doc/ospfd.texi [new file with mode: 0644]
doc/overview.texi [new file with mode: 0644]
doc/pimd.8 [new file with mode: 0644]
doc/protocol.texi [new file with mode: 0644]
doc/quagga.texi [new file with mode: 0644]
doc/ripd.8 [new file with mode: 0644]
doc/ripd.texi [new file with mode: 0644]
doc/ripngd.8 [new file with mode: 0644]
doc/ripngd.texi [new file with mode: 0644]
doc/routemap.texi [new file with mode: 0644]
doc/routeserver.texi [new file with mode: 0644]
doc/snmp.texi [new file with mode: 0644]
doc/snmptrap.texi [new file with mode: 0644]
doc/texinfo.css [new file with mode: 0644]
doc/texinfo.tex [new file with mode: 0644]
doc/vtysh.1 [new file with mode: 0644]
doc/vtysh.texi [new file with mode: 0644]
doc/watchquagga.8 [new file with mode: 0644]
doc/zebra.8 [new file with mode: 0644]
fpm/.gitignore [new file with mode: 0644]
fpm/Makefile.am [new file with mode: 0644]
fpm/fpm.h [new file with mode: 0644]
fpm/fpm.proto [new file with mode: 0644]
fpm/fpm_pb.c [new file with mode: 0644]
fpm/fpm_pb.h [new file with mode: 0644]
gdb/lib.txt [new file with mode: 0644]
gdb/ospf.txt [new file with mode: 0644]
infra/buildbot/master/master.cfg [new file with mode: 0644]
infra/buildbot/master/pass.cfg [new file with mode: 0644]
infra/buildbot/worker/buildbot-slave.service [new file with mode: 0644]
infra/buildbot/worker/buildbot-slave.xml [new file with mode: 0644]
infra/patchwork/pass.py [new file with mode: 0644]
infra/patchwork/production.py [new file with mode: 0644]
infra/patchwork/systemd/patchwork-delivery.socket [new file with mode: 0644]
infra/patchwork/systemd/patchwork-delivery@.service [new file with mode: 0644]
infra/patchwork/systemd/patchwork.service [new file with mode: 0644]
init/.gitignore [new file with mode: 0644]
isisd/.gitignore [new file with mode: 0644]
isisd/AUTHORS [new file with mode: 0644]
isisd/Makefile.am [new file with mode: 0644]
isisd/README [new file with mode: 0644]
isisd/dict.c [new file with mode: 0644]
isisd/dict.h [new file with mode: 0644]
isisd/include-netbsd/.gitignore [new file with mode: 0644]
isisd/include-netbsd/clnp.h [new file with mode: 0644]
isisd/include-netbsd/esis.h [new file with mode: 0644]
isisd/include-netbsd/iso.h [new file with mode: 0644]
isisd/isis_adjacency.c [new file with mode: 0644]
isisd/isis_adjacency.h [new file with mode: 0644]
isisd/isis_bpf.c [new file with mode: 0644]
isisd/isis_circuit.c [new file with mode: 0644]
isisd/isis_circuit.h [new file with mode: 0644]
isisd/isis_common.h [new file with mode: 0644]
isisd/isis_constants.h [new file with mode: 0644]
isisd/isis_csm.c [new file with mode: 0644]
isisd/isis_csm.h [new file with mode: 0644]
isisd/isis_dlpi.c [new file with mode: 0644]
isisd/isis_dr.c [new file with mode: 0644]
isisd/isis_dr.h [new file with mode: 0644]
isisd/isis_dynhn.c [new file with mode: 0644]
isisd/isis_dynhn.h [new file with mode: 0644]
isisd/isis_events.c [new file with mode: 0644]
isisd/isis_events.h [new file with mode: 0644]
isisd/isis_flags.c [new file with mode: 0644]
isisd/isis_flags.h [new file with mode: 0644]
isisd/isis_lsp.c [new file with mode: 0644]
isisd/isis_lsp.h [new file with mode: 0644]
isisd/isis_main.c [new file with mode: 0644]
isisd/isis_misc.c [new file with mode: 0644]
isisd/isis_misc.h [new file with mode: 0644]
isisd/isis_network.h [new file with mode: 0644]
isisd/isis_pdu.c [new file with mode: 0644]
isisd/isis_pdu.h [new file with mode: 0644]
isisd/isis_pfpacket.c [new file with mode: 0644]
isisd/isis_redist.c [new file with mode: 0644]
isisd/isis_redist.h [new file with mode: 0644]
isisd/isis_route.c [new file with mode: 0644]
isisd/isis_route.h [new file with mode: 0644]
isisd/isis_routemap.c [new file with mode: 0644]
isisd/isis_routemap.h [new file with mode: 0644]
isisd/isis_spf.c [new file with mode: 0644]
isisd/isis_spf.h [new file with mode: 0644]
isisd/isis_te.c [new file with mode: 0644]
isisd/isis_te.h [new file with mode: 0644]
isisd/isis_tlv.c [new file with mode: 0644]
isisd/isis_tlv.h [new file with mode: 0644]
isisd/isis_vty.c [new file with mode: 0644]
isisd/isis_zebra.c [new file with mode: 0644]
isisd/isis_zebra.h [new file with mode: 0644]
isisd/isisd.c [new file with mode: 0644]
isisd/isisd.conf.sample [new file with mode: 0644]
isisd/isisd.h [new file with mode: 0644]
isisd/iso_checksum.c [new file with mode: 0644]
isisd/iso_checksum.h [new file with mode: 0644]
isisd/topology/.gitignore [new file with mode: 0644]
isisd/topology/Makefile.am [new file with mode: 0644]
isisd/topology/random.c [new file with mode: 0644]
isisd/topology/spacyc.c [new file with mode: 0644]
isisd/topology/spgrid.c [new file with mode: 0644]
isisd/topology/spgrid.h [new file with mode: 0644]
isisd/topology/sprand.c [new file with mode: 0644]
lib/.gitignore [new file with mode: 0644]
lib/Makefile.am [new file with mode: 0644]
lib/agentx.c [new file with mode: 0644]
lib/buffer.c [new file with mode: 0644]
lib/buffer.h [new file with mode: 0644]
lib/checksum.c [new file with mode: 0644]
lib/checksum.h [new file with mode: 0644]
lib/command.c [new file with mode: 0644]
lib/command.h [new file with mode: 0644]
lib/daemon.c [new file with mode: 0644]
lib/distribute.c [new file with mode: 0644]
lib/distribute.h [new file with mode: 0644]
lib/event_counter.c [new file with mode: 0644]
lib/event_counter.h [new file with mode: 0644]
lib/fifo.h [new file with mode: 0644]
lib/filter.c [new file with mode: 0644]
lib/filter.h [new file with mode: 0644]
lib/getopt.c [new file with mode: 0644]
lib/getopt.h [new file with mode: 0644]
lib/getopt1.c [new file with mode: 0644]
lib/gitversion.pl [new file with mode: 0644]
lib/hash.c [new file with mode: 0644]
lib/hash.h [new file with mode: 0644]
lib/if.c [new file with mode: 0644]
lib/if.h [new file with mode: 0644]
lib/if_rmap.c [new file with mode: 0644]
lib/if_rmap.h [new file with mode: 0644]
lib/jhash.c [new file with mode: 0644]
lib/jhash.h [new file with mode: 0644]
lib/keychain.c [new file with mode: 0644]
lib/keychain.h [new file with mode: 0644]
lib/libospf.h [new file with mode: 0644]
lib/linklist.c [new file with mode: 0644]
lib/linklist.h [new file with mode: 0644]
lib/log.c [new file with mode: 0644]
lib/log.h [new file with mode: 0644]
lib/md5.c [new file with mode: 0644]
lib/md5.h [new file with mode: 0644]
lib/memory.c [new file with mode: 0644]
lib/memory.h [new file with mode: 0644]
lib/memtypes.awk [new file with mode: 0644]
lib/memtypes.c [new file with mode: 0644]
lib/network.c [new file with mode: 0644]
lib/network.h [new file with mode: 0644]
lib/nexthop.c [new file with mode: 0644]
lib/nexthop.h [new file with mode: 0644]
lib/pid_output.c [new file with mode: 0644]
lib/plist.c [new file with mode: 0644]
lib/plist.h [new file with mode: 0644]
lib/plist_int.h [new file with mode: 0644]
lib/pqueue.c [new file with mode: 0644]
lib/pqueue.h [new file with mode: 0644]
lib/prefix.c [new file with mode: 0644]
lib/prefix.h [new file with mode: 0644]
lib/privs.c [new file with mode: 0644]
lib/privs.h [new file with mode: 0644]
lib/queue.h [new file with mode: 0644]
lib/regex-gnu.h [new file with mode: 0644]
lib/regex.c [new file with mode: 0644]
lib/route_types.pl [new file with mode: 0755]
lib/route_types.txt [new file with mode: 0644]
lib/routemap.c [new file with mode: 0644]
lib/routemap.h [new file with mode: 0644]
lib/sigevent.c [new file with mode: 0644]
lib/sigevent.h [new file with mode: 0644]
lib/smux.c [new file with mode: 0644]
lib/smux.h [new file with mode: 0644]
lib/snmp.c [new file with mode: 0644]
lib/sockopt.c [new file with mode: 0644]
lib/sockopt.h [new file with mode: 0644]
lib/sockunion.c [new file with mode: 0644]
lib/sockunion.h [new file with mode: 0644]
lib/str.c [new file with mode: 0644]
lib/str.h [new file with mode: 0644]
lib/stream.c [new file with mode: 0644]
lib/stream.h [new file with mode: 0644]
lib/table.c [new file with mode: 0644]
lib/table.h [new file with mode: 0644]
lib/thread.c [new file with mode: 0644]
lib/thread.h [new file with mode: 0644]
lib/vector.c [new file with mode: 0644]
lib/vector.h [new file with mode: 0644]
lib/version.h.in [new file with mode: 0644]
lib/vrf.c [new file with mode: 0644]
lib/vrf.h [new file with mode: 0644]
lib/vty.c [new file with mode: 0644]
lib/vty.h [new file with mode: 0644]
lib/workqueue.c [new file with mode: 0644]
lib/workqueue.h [new file with mode: 0644]
lib/zassert.h [new file with mode: 0644]
lib/zclient.c [new file with mode: 0644]
lib/zclient.h [new file with mode: 0644]
lib/zebra.h [new file with mode: 0644]
m4/.gitignore [new file with mode: 0644]
m4/Makefile.am [new file with mode: 0644]
m4/README.txt [new file with mode: 0644]
m4/ax_sys_weak_alias.m4 [new file with mode: 0644]
nhrpd/Makefile.am [new file with mode: 0644]
nhrpd/README.kernel [new file with mode: 0644]
nhrpd/README.nhrpd [new file with mode: 0644]
nhrpd/debug.h [new file with mode: 0644]
nhrpd/linux.c [new file with mode: 0644]
nhrpd/list.h [new file with mode: 0644]
nhrpd/netlink.h [new file with mode: 0644]
nhrpd/netlink_arp.c [new file with mode: 0644]
nhrpd/netlink_gre.c [new file with mode: 0644]
nhrpd/nhrp-events.lua [new file with mode: 0755]
nhrpd/nhrp_cache.c [new file with mode: 0644]
nhrpd/nhrp_event.c [new file with mode: 0644]
nhrpd/nhrp_interface.c [new file with mode: 0644]
nhrpd/nhrp_main.c [new file with mode: 0644]
nhrpd/nhrp_nhs.c [new file with mode: 0644]
nhrpd/nhrp_packet.c [new file with mode: 0644]
nhrpd/nhrp_peer.c [new file with mode: 0644]
nhrpd/nhrp_protocol.h [new file with mode: 0644]
nhrpd/nhrp_route.c [new file with mode: 0644]
nhrpd/nhrp_shortcut.c [new file with mode: 0644]
nhrpd/nhrp_vc.c [new file with mode: 0644]
nhrpd/nhrp_vty.c [new file with mode: 0644]
nhrpd/nhrpd.h [new file with mode: 0644]
nhrpd/os.h [new file with mode: 0644]
nhrpd/reqid.c [new file with mode: 0644]
nhrpd/resolver.c [new file with mode: 0644]
nhrpd/vici.c [new file with mode: 0644]
nhrpd/vici.h [new file with mode: 0644]
nhrpd/zbuf.c [new file with mode: 0644]
nhrpd/zbuf.h [new file with mode: 0644]
nhrpd/znl.c [new file with mode: 0644]
nhrpd/znl.h [new file with mode: 0644]
ospf6d/.gitignore [new file with mode: 0644]
ospf6d/Makefile.am [new file with mode: 0644]
ospf6d/OSPFv3-MIB.txt [new file with mode: 0644]
ospf6d/README [new file with mode: 0644]
ospf6d/ospf6_abr.c [new file with mode: 0644]
ospf6d/ospf6_abr.h [new file with mode: 0644]
ospf6d/ospf6_area.c [new file with mode: 0644]
ospf6d/ospf6_area.h [new file with mode: 0644]
ospf6d/ospf6_asbr.c [new file with mode: 0644]
ospf6d/ospf6_asbr.h [new file with mode: 0644]
ospf6d/ospf6_flood.c [new file with mode: 0644]
ospf6d/ospf6_flood.h [new file with mode: 0644]
ospf6d/ospf6_interface.c [new file with mode: 0644]
ospf6d/ospf6_interface.h [new file with mode: 0644]
ospf6d/ospf6_intra.c [new file with mode: 0644]
ospf6d/ospf6_intra.h [new file with mode: 0644]
ospf6d/ospf6_lsa.c [new file with mode: 0644]
ospf6d/ospf6_lsa.h [new file with mode: 0644]
ospf6d/ospf6_lsdb.c [new file with mode: 0644]
ospf6d/ospf6_lsdb.h [new file with mode: 0644]
ospf6d/ospf6_main.c [new file with mode: 0644]
ospf6d/ospf6_message.c [new file with mode: 0644]
ospf6d/ospf6_message.h [new file with mode: 0644]
ospf6d/ospf6_neighbor.c [new file with mode: 0644]
ospf6d/ospf6_neighbor.h [new file with mode: 0644]
ospf6d/ospf6_network.c [new file with mode: 0644]
ospf6d/ospf6_network.h [new file with mode: 0644]
ospf6d/ospf6_proto.c [new file with mode: 0644]
ospf6d/ospf6_proto.h [new file with mode: 0644]
ospf6d/ospf6_route.c [new file with mode: 0644]
ospf6d/ospf6_route.h [new file with mode: 0644]
ospf6d/ospf6_snmp.c [new file with mode: 0644]
ospf6d/ospf6_snmp.h [new file with mode: 0644]
ospf6d/ospf6_spf.c [new file with mode: 0644]
ospf6d/ospf6_spf.h [new file with mode: 0644]
ospf6d/ospf6_top.c [new file with mode: 0644]
ospf6d/ospf6_top.h [new file with mode: 0644]
ospf6d/ospf6_zebra.c [new file with mode: 0644]
ospf6d/ospf6_zebra.h [new file with mode: 0644]
ospf6d/ospf6d.c [new file with mode: 0644]
ospf6d/ospf6d.conf.sample [new file with mode: 0644]
ospf6d/ospf6d.h [new file with mode: 0644]
ospfclient/.gitignore [new file with mode: 0644]
ospfclient/AUTHORS [new file with mode: 0644]
ospfclient/COPYING [new file with mode: 0644]
ospfclient/INSTALL [new file with mode: 0644]
ospfclient/Makefile.am [new file with mode: 0644]
ospfclient/NEWS [new file with mode: 0644]
ospfclient/README [new file with mode: 0644]
ospfclient/ospf_apiclient.c [new file with mode: 0644]
ospfclient/ospf_apiclient.h [new file with mode: 0644]
ospfclient/ospfclient.c [new file with mode: 0644]
ospfd/.gitignore [new file with mode: 0644]
ospfd/ChangeLog.opaque.txt [new file with mode: 0644]
ospfd/Makefile.am [new file with mode: 0644]
ospfd/OSPF-ALIGNMENT.txt [new file with mode: 0644]
ospfd/OSPF-MIB.txt [new file with mode: 0644]
ospfd/OSPF-TRAP-MIB.txt [new file with mode: 0644]
ospfd/ospf_abr.c [new file with mode: 0644]
ospfd/ospf_abr.h [new file with mode: 0644]
ospfd/ospf_api.c [new file with mode: 0644]
ospfd/ospf_api.h [new file with mode: 0644]
ospfd/ospf_apiserver.c [new file with mode: 0644]
ospfd/ospf_apiserver.h [new file with mode: 0644]
ospfd/ospf_asbr.c [new file with mode: 0644]
ospfd/ospf_asbr.h [new file with mode: 0644]
ospfd/ospf_ase.c [new file with mode: 0644]
ospfd/ospf_ase.h [new file with mode: 0644]
ospfd/ospf_dump.c [new file with mode: 0644]
ospfd/ospf_dump.h [new file with mode: 0644]
ospfd/ospf_flood.c [new file with mode: 0644]
ospfd/ospf_flood.h [new file with mode: 0644]
ospfd/ospf_ia.c [new file with mode: 0644]
ospfd/ospf_ia.h [new file with mode: 0644]
ospfd/ospf_interface.c [new file with mode: 0644]
ospfd/ospf_interface.h [new file with mode: 0644]
ospfd/ospf_ism.c [new file with mode: 0644]
ospfd/ospf_ism.h [new file with mode: 0644]
ospfd/ospf_lsa.c [new file with mode: 0644]
ospfd/ospf_lsa.h [new file with mode: 0644]
ospfd/ospf_lsdb.c [new file with mode: 0644]
ospfd/ospf_lsdb.h [new file with mode: 0644]
ospfd/ospf_main.c [new file with mode: 0644]
ospfd/ospf_neighbor.c [new file with mode: 0644]
ospfd/ospf_neighbor.h [new file with mode: 0644]
ospfd/ospf_network.c [new file with mode: 0644]
ospfd/ospf_network.h [new file with mode: 0644]
ospfd/ospf_nsm.c [new file with mode: 0644]
ospfd/ospf_nsm.h [new file with mode: 0644]
ospfd/ospf_opaque.c [new file with mode: 0644]
ospfd/ospf_opaque.h [new file with mode: 0644]
ospfd/ospf_packet.c [new file with mode: 0644]
ospfd/ospf_packet.h [new file with mode: 0644]
ospfd/ospf_ri.c [new file with mode: 0644]
ospfd/ospf_ri.h [new file with mode: 0644]
ospfd/ospf_route.c [new file with mode: 0644]
ospfd/ospf_route.h [new file with mode: 0644]
ospfd/ospf_routemap.c [new file with mode: 0644]
ospfd/ospf_snmp.c [new file with mode: 0644]
ospfd/ospf_snmp.h [new file with mode: 0644]
ospfd/ospf_spf.c [new file with mode: 0644]
ospfd/ospf_spf.h [new file with mode: 0644]
ospfd/ospf_te.c [new file with mode: 0644]
ospfd/ospf_te.h [new file with mode: 0644]
ospfd/ospf_vty.c [new file with mode: 0644]
ospfd/ospf_vty.h [new file with mode: 0644]
ospfd/ospf_zebra.c [new file with mode: 0644]
ospfd/ospf_zebra.h [new file with mode: 0644]
ospfd/ospfd.c [new file with mode: 0644]
ospfd/ospfd.conf.sample [new file with mode: 0644]
ospfd/ospfd.h [new file with mode: 0644]
pimd/.gitignore [new file with mode: 0644]
pimd/AUTHORS [new file with mode: 0644]
pimd/CAVEATS [new file with mode: 0644]
pimd/COMMANDS [new file with mode: 0644]
pimd/COPYING [new file with mode: 0644]
pimd/DEBUG [new file with mode: 0644]
pimd/LINUX_KERNEL_MROUTE_MFC [new file with mode: 0644]
pimd/Makefile.am [new file with mode: 0644]
pimd/README [new file with mode: 0644]
pimd/TODO [new file with mode: 0644]
pimd/TROUBLESHOOTING [new file with mode: 0644]
pimd/WHY_SSM [new file with mode: 0644]
pimd/pim_assert.c [new file with mode: 0644]
pimd/pim_assert.h [new file with mode: 0644]
pimd/pim_cmd.c [new file with mode: 0644]
pimd/pim_cmd.h [new file with mode: 0644]
pimd/pim_hello.c [new file with mode: 0644]
pimd/pim_hello.h [new file with mode: 0644]
pimd/pim_iface.c [new file with mode: 0644]
pimd/pim_iface.h [new file with mode: 0644]
pimd/pim_ifchannel.c [new file with mode: 0644]
pimd/pim_ifchannel.h [new file with mode: 0644]
pimd/pim_igmp.c [new file with mode: 0644]
pimd/pim_igmp.h [new file with mode: 0644]
pimd/pim_igmp_join.c [new file with mode: 0644]
pimd/pim_igmp_join.h [new file with mode: 0644]
pimd/pim_igmpv3.c [new file with mode: 0644]
pimd/pim_igmpv3.h [new file with mode: 0644]
pimd/pim_int.c [new file with mode: 0644]
pimd/pim_int.h [new file with mode: 0644]
pimd/pim_join.c [new file with mode: 0644]
pimd/pim_join.h [new file with mode: 0644]
pimd/pim_macro.c [new file with mode: 0644]
pimd/pim_macro.h [new file with mode: 0644]
pimd/pim_main.c [new file with mode: 0644]
pimd/pim_mroute.c [new file with mode: 0644]
pimd/pim_mroute.h [new file with mode: 0644]
pimd/pim_msg.c [new file with mode: 0644]
pimd/pim_msg.h [new file with mode: 0644]
pimd/pim_neighbor.c [new file with mode: 0644]
pimd/pim_neighbor.h [new file with mode: 0644]
pimd/pim_oil.c [new file with mode: 0644]
pimd/pim_oil.h [new file with mode: 0644]
pimd/pim_pim.c [new file with mode: 0644]
pimd/pim_pim.h [new file with mode: 0644]
pimd/pim_routemap.c [new file with mode: 0644]
pimd/pim_rpf.c [new file with mode: 0644]
pimd/pim_rpf.h [new file with mode: 0644]
pimd/pim_signals.c [new file with mode: 0644]
pimd/pim_signals.h [new file with mode: 0644]
pimd/pim_sock.c [new file with mode: 0644]
pimd/pim_sock.h [new file with mode: 0644]
pimd/pim_ssmpingd.c [new file with mode: 0644]
pimd/pim_ssmpingd.h [new file with mode: 0644]
pimd/pim_static.c [new file with mode: 0644]
pimd/pim_static.h [new file with mode: 0644]
pimd/pim_str.c [new file with mode: 0644]
pimd/pim_str.h [new file with mode: 0644]
pimd/pim_time.c [new file with mode: 0644]
pimd/pim_time.h [new file with mode: 0644]
pimd/pim_tlv.c [new file with mode: 0644]
pimd/pim_tlv.h [new file with mode: 0644]
pimd/pim_upstream.c [new file with mode: 0644]
pimd/pim_upstream.h [new file with mode: 0644]
pimd/pim_util.c [new file with mode: 0644]
pimd/pim_util.h [new file with mode: 0644]
pimd/pim_version.c [new file with mode: 0644]
pimd/pim_version.h [new file with mode: 0644]
pimd/pim_vty.c [new file with mode: 0644]
pimd/pim_vty.h [new file with mode: 0644]
pimd/pim_zebra.c [new file with mode: 0644]
pimd/pim_zebra.h [new file with mode: 0644]
pimd/pim_zlookup.c [new file with mode: 0644]
pimd/pim_zlookup.h [new file with mode: 0644]
pimd/pimd.c [new file with mode: 0644]
pimd/pimd.conf.sample [new file with mode: 0644]
pimd/pimd.h [new file with mode: 0644]
pimd/test_igmpv3_join.c [new file with mode: 0644]
pkgsrc/.gitignore [new file with mode: 0644]
pkgsrc/Makefile.am [new file with mode: 0644]
pkgsrc/README.txt [new file with mode: 0644]
pkgsrc/bgpd.sh.in [new file with mode: 0644]
pkgsrc/ospf6d.sh.in [new file with mode: 0644]
pkgsrc/ospfd.sh.in [new file with mode: 0644]
pkgsrc/ripd.sh.in [new file with mode: 0644]
pkgsrc/ripngd.sh.in [new file with mode: 0644]
pkgsrc/zebra.sh.in [new file with mode: 0644]
ports/.gitignore [new file with mode: 0644]
ports/Makefile [new file with mode: 0644]
ports/README [new file with mode: 0644]
ports/files/.gitignore [new file with mode: 0644]
ports/files/md5 [new file with mode: 0644]
ports/pkg/.gitignore [new file with mode: 0644]
ports/pkg/COMMENT [new file with mode: 0644]
ports/pkg/DESCR [new file with mode: 0644]
ports/pkg/PLIST [new file with mode: 0644]
qpb/.gitignore [new file with mode: 0644]
qpb/Makefile.am [new file with mode: 0644]
qpb/README.txt [new file with mode: 0644]
qpb/linear_allocator.h [new file with mode: 0644]
qpb/qpb.c [new file with mode: 0644]
qpb/qpb.h [new file with mode: 0644]
qpb/qpb.proto [new file with mode: 0644]
qpb/qpb_allocator.c [new file with mode: 0644]
qpb/qpb_allocator.h [new file with mode: 0644]
redhat/.gitignore [new file with mode: 0644]
redhat/Makefile.am [new file with mode: 0644]
redhat/README.rpm_build.md [new file with mode: 0644]
redhat/bgpd.init [new file with mode: 0644]
redhat/bgpd.service [new file with mode: 0644]
redhat/isisd.init [new file with mode: 0644]
redhat/isisd.service [new file with mode: 0644]
redhat/nhrpd.service [new file with mode: 0644]
redhat/ospf6d.init [new file with mode: 0644]
redhat/ospf6d.service [new file with mode: 0644]
redhat/ospfd.init [new file with mode: 0644]
redhat/ospfd.service [new file with mode: 0644]
redhat/pimd.init [new file with mode: 0644]
redhat/pimd.service [new file with mode: 0644]
redhat/quagga-filter-perl-requires.sh [new file with mode: 0755]
redhat/quagga-tmpfs.conf [new file with mode: 0644]
redhat/quagga.logrotate [new file with mode: 0644]
redhat/quagga.pam [new file with mode: 0644]
redhat/quagga.spec.in [new file with mode: 0644]
redhat/quagga.sysconfig [new file with mode: 0644]
redhat/ripd.init [new file with mode: 0644]
redhat/ripd.service [new file with mode: 0644]
redhat/ripngd.init [new file with mode: 0644]
redhat/ripngd.service [new file with mode: 0644]
redhat/watchquagga.init [new file with mode: 0644]
redhat/zebra.init [new file with mode: 0644]
redhat/zebra.service [new file with mode: 0644]
release.sh [new file with mode: 0755]
ripd/.gitignore [new file with mode: 0644]
ripd/Makefile.am [new file with mode: 0644]
ripd/RIPv2-MIB.txt [new file with mode: 0644]
ripd/rip_debug.c [new file with mode: 0644]
ripd/rip_debug.h [new file with mode: 0644]
ripd/rip_interface.c [new file with mode: 0644]
ripd/rip_interface.h [new file with mode: 0644]
ripd/rip_main.c [new file with mode: 0644]
ripd/rip_offset.c [new file with mode: 0644]
ripd/rip_peer.c [new file with mode: 0644]
ripd/rip_routemap.c [new file with mode: 0644]
ripd/rip_snmp.c [new file with mode: 0644]
ripd/rip_zebra.c [new file with mode: 0644]
ripd/ripd.c [new file with mode: 0644]
ripd/ripd.conf.sample [new file with mode: 0644]
ripd/ripd.h [new file with mode: 0644]
ripngd/.gitignore [new file with mode: 0644]
ripngd/Makefile.am [new file with mode: 0644]
ripngd/ripng_debug.c [new file with mode: 0644]
ripngd/ripng_debug.h [new file with mode: 0644]
ripngd/ripng_interface.c [new file with mode: 0644]
ripngd/ripng_main.c [new file with mode: 0644]
ripngd/ripng_nexthop.c [new file with mode: 0644]
ripngd/ripng_nexthop.h [new file with mode: 0644]
ripngd/ripng_offset.c [new file with mode: 0644]
ripngd/ripng_peer.c [new file with mode: 0644]
ripngd/ripng_route.c [new file with mode: 0644]
ripngd/ripng_route.h [new file with mode: 0644]
ripngd/ripng_routemap.c [new file with mode: 0644]
ripngd/ripng_zebra.c [new file with mode: 0644]
ripngd/ripngd.c [new file with mode: 0644]
ripngd/ripngd.conf.sample [new file with mode: 0644]
ripngd/ripngd.h [new file with mode: 0644]
solaris/.gitignore [new file with mode: 0644]
solaris/Makefile.am [new file with mode: 0644]
solaris/README.txt [new file with mode: 0644]
solaris/depend.daemons.in [new file with mode: 0644]
solaris/depend.dev.in [new file with mode: 0644]
solaris/depend.doc.in [new file with mode: 0644]
solaris/depend.libs.in [new file with mode: 0644]
solaris/depend.smf.in [new file with mode: 0644]
solaris/pkginfo.daemons.tmpl.in [new file with mode: 0644]
solaris/pkginfo.dev.tmpl.in [new file with mode: 0644]
solaris/pkginfo.doc.tmpl.in [new file with mode: 0644]
solaris/pkginfo.libs.tmpl.in [new file with mode: 0644]
solaris/pkginfo.smf.tmpl.in [new file with mode: 0644]
solaris/pkginfo.tmpl.in [new file with mode: 0644]
solaris/prototype.daemons.in [new file with mode: 0644]
solaris/prototype.dev.in [new file with mode: 0644]
solaris/prototype.doc.in [new file with mode: 0644]
solaris/prototype.libs.in [new file with mode: 0644]
solaris/prototype.smf.in [new file with mode: 0644]
solaris/quagga.init.in [new file with mode: 0755]
solaris/quagga.xml.in [new file with mode: 0644]
stamp-h.in [new file with mode: 0644]
tests/.gitignore [new file with mode: 0644]
tests/Makefile.am [new file with mode: 0644]
tests/aspath_test.c [new file with mode: 0644]
tests/bgp_capability_test.c [new file with mode: 0644]
tests/bgp_mp_attr_test.c [new file with mode: 0644]
tests/bgp_mpath_test.c [new file with mode: 0644]
tests/bgpd.tests/Makefile.am [new file with mode: 0644]
tests/bgpd.tests/aspathtest.exp [new file with mode: 0644]
tests/bgpd.tests/ecommtest.exp [new file with mode: 0644]
tests/bgpd.tests/testbgpcap.exp [new file with mode: 0644]
tests/bgpd.tests/testbgpmpath.exp [new file with mode: 0644]
tests/bgpd.tests/testbgpmpattr.exp [new file with mode: 0644]
tests/common-cli.c [new file with mode: 0644]
tests/common-cli.h [new file with mode: 0644]
tests/config/unix.exp [new file with mode: 0644]
tests/ecommunity_test.c [new file with mode: 0644]
tests/global-conf.exp [new file with mode: 0644]
tests/heavy-thread.c [new file with mode: 0644]
tests/heavy-wq.c [new file with mode: 0644]
tests/heavy.c [new file with mode: 0644]
tests/lib/bgpd.exp [new file with mode: 0644]
tests/lib/libzebra.exp [new file with mode: 0644]
tests/libzebra.tests/Makefile.am [new file with mode: 0644]
tests/libzebra.tests/tabletest.exp [new file with mode: 0644]
tests/libzebra.tests/test-timer-correctness.exp [new file with mode: 0644]
tests/libzebra.tests/testcli.exp [new file with mode: 0644]
tests/libzebra.tests/testcommands.exp [new file with mode: 0644]
tests/libzebra.tests/testnexthopiter.exp [new file with mode: 0644]
tests/libzebra.tests/teststream.exp [new file with mode: 0644]
tests/main.c [new file with mode: 0644]
tests/prng.c [new file with mode: 0644]
tests/prng.h [new file with mode: 0644]
tests/table_test.c [new file with mode: 0644]
tests/test-buffer.c [new file with mode: 0644]
tests/test-checksum.c [new file with mode: 0644]
tests/test-cli.c [new file with mode: 0644]
tests/test-commands.c [new file with mode: 0644]
tests/test-memory.c [new file with mode: 0644]
tests/test-nexthop-iter.c [new file with mode: 0644]
tests/test-privs.c [new file with mode: 0644]
tests/test-segv.c [new file with mode: 0644]
tests/test-sig.c [new file with mode: 0644]
tests/test-stream.c [new file with mode: 0644]
tests/test-timer-correctness.c [new file with mode: 0644]
tests/test-timer-performance.c [new file with mode: 0644]
tests/testcli.in [new file with mode: 0644]
tests/testcli.refout [new file with mode: 0644]
tests/testcommands.in [new file with mode: 0644]
tests/testcommands.refout [new file with mode: 0644]
tests/tests.h [new file with mode: 0644]
tools/.gitignore [new file with mode: 0644]
tools/mrlg.txt [new file with mode: 0644]
tools/multiple-bgpd.sh [new file with mode: 0644]
tools/zebra.el [new file with mode: 0644]
update-autotools [new file with mode: 0755]
vtysh/.gitignore [new file with mode: 0644]
vtysh/Makefile.am [new file with mode: 0644]
vtysh/extract.pl.in [new file with mode: 0755]
vtysh/vtysh.c [new file with mode: 0644]
vtysh/vtysh.conf.sample [new file with mode: 0644]
vtysh/vtysh.h [new file with mode: 0644]
vtysh/vtysh_config.c [new file with mode: 0644]
vtysh/vtysh_main.c [new file with mode: 0644]
vtysh/vtysh_user.c [new file with mode: 0644]
vtysh/vtysh_user.h [new file with mode: 0644]
watchquagga/.gitignore [new file with mode: 0644]
watchquagga/Makefile.am [new file with mode: 0644]
watchquagga/watchquagga.c [new file with mode: 0644]
zebra/.gitignore [new file with mode: 0644]
zebra/GNOME-PRODUCT-ZEBRA-MIB [new file with mode: 0644]
zebra/GNOME-SMI [new file with mode: 0644]
zebra/Makefile.am [new file with mode: 0644]
zebra/client_main.c [new file with mode: 0644]
zebra/connected.c [new file with mode: 0644]
zebra/connected.h [new file with mode: 0644]
zebra/debug.c [new file with mode: 0644]
zebra/debug.h [new file with mode: 0644]
zebra/if_ioctl.c [new file with mode: 0644]
zebra/if_ioctl_solaris.c [new file with mode: 0644]
zebra/if_netlink.c [new file with mode: 0644]
zebra/if_sysctl.c [new file with mode: 0644]
zebra/interface.c [new file with mode: 0644]
zebra/interface.h [new file with mode: 0644]
zebra/ioctl.c [new file with mode: 0644]
zebra/ioctl.h [new file with mode: 0644]
zebra/ioctl_null.c [new file with mode: 0644]
zebra/ioctl_solaris.c [new file with mode: 0644]
zebra/ioctl_solaris.h [new file with mode: 0644]
zebra/ipforward.h [new file with mode: 0644]
zebra/ipforward_proc.c [new file with mode: 0644]
zebra/ipforward_solaris.c [new file with mode: 0644]
zebra/ipforward_sysctl.c [new file with mode: 0644]
zebra/irdp.h [new file with mode: 0644]
zebra/irdp_interface.c [new file with mode: 0644]
zebra/irdp_main.c [new file with mode: 0644]
zebra/irdp_packet.c [new file with mode: 0644]
zebra/kernel_netlink.c [new file with mode: 0644]
zebra/kernel_null.c [new file with mode: 0644]
zebra/kernel_socket.c [new file with mode: 0644]
zebra/kernel_socket.h [new file with mode: 0644]
zebra/main.c [new file with mode: 0644]
zebra/misc_null.c [new file with mode: 0644]
zebra/redistribute.c [new file with mode: 0644]
zebra/redistribute.h [new file with mode: 0644]
zebra/redistribute_null.c [new file with mode: 0644]
zebra/rib.h [new file with mode: 0644]
zebra/router-id.c [new file with mode: 0644]
zebra/router-id.h [new file with mode: 0644]
zebra/rt.h [new file with mode: 0644]
zebra/rt_netlink.c [new file with mode: 0644]
zebra/rt_netlink.h [new file with mode: 0644]
zebra/rt_socket.c [new file with mode: 0644]
zebra/rtadv.c [new file with mode: 0644]
zebra/rtadv.h [new file with mode: 0644]
zebra/rtread_getmsg.c [new file with mode: 0644]
zebra/rtread_netlink.c [new file with mode: 0644]
zebra/rtread_sysctl.c [new file with mode: 0644]
zebra/test_main.c [new file with mode: 0644]
zebra/testrib.conf [new file with mode: 0644]
zebra/zebra.conf.sample [new file with mode: 0644]
zebra/zebra_fpm.c [new file with mode: 0644]
zebra/zebra_fpm.h [new file with mode: 0644]
zebra/zebra_fpm_dt.c [new file with mode: 0644]
zebra/zebra_fpm_netlink.c [new file with mode: 0644]
zebra/zebra_fpm_private.h [new file with mode: 0644]
zebra/zebra_fpm_protobuf.c [new file with mode: 0644]
zebra/zebra_rib.c [new file with mode: 0644]
zebra/zebra_rnh.c [new file with mode: 0644]
zebra/zebra_rnh.h [new file with mode: 0644]
zebra/zebra_rnh_null.c [new file with mode: 0644]
zebra/zebra_routemap.c [new file with mode: 0644]
zebra/zebra_snmp.c [new file with mode: 0644]
zebra/zebra_vty.c [new file with mode: 0644]
zebra/zserv.c [new file with mode: 0644]
zebra/zserv.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a8da62f
--- /dev/null
@@ -0,0 +1,45 @@
+compile
+config.log
+config.h
+config.cache
+config.status
+config.guess
+config.sub
+ltmain.sh
+stamp-h
+stamp-h[0-9]*
+*-stamp
+Makefile
+INSTALL
+.deps
+depcomp
+missing
+install-sh
+mkinstalldirs
+autom4te*.cache
+configure.lineno
+configure
+config.h.in
+aclocal.m4
+Makefile.in
+zebra-[0-9.][0-9.][0-9.]*.tar.gz
+quagga-[0-9.][0-9.][0-9.]*.tar.gz
+quagga-[0-9.][0-9.][0-9.]*.tar.gz.asc
+.nfs*
+libtool
+.libs
+.arch-inventory
+.arch-ids
+{arch}
+build
+.msg
+.rebase-*
+*~
+*.o
+*.loT
+m4/*.m4
+!m4/ax_sys_weak_alias.m4
+cscope.*
+*.pb.h
+*.pb-c.h
+*.pb-c.c
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..61867a8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+Kunihiro Ishiguro <kunihiro@zebra.org>
+Toshiaki Takada <takada@zebra.org>
+Yasuhiro Ohara <yasu@sfc.wide.ad.jp>
+Alex D. Zinin <azinin@hotmail.com>
+Gleb Natapov <gleb@nbase.co.il>
+Akihiro Mizutani <mizutani@dml.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..b8cf3a1
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..2b4628e
--- /dev/null
@@ -0,0 +1,482 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..2ed5061
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,4 @@
+ChangeLog information for Quagga is now recorded in our source-code
+management system. Please see:
+
+       http://www.quagga.net/devel.php
diff --git a/HACKING.md b/HACKING.md
new file mode 100644 (file)
index 0000000..078de60
--- /dev/null
@@ -0,0 +1,741 @@
+---
+title: Conventions for working on Quagga 
+papersize: a4paper
+geometry: a4paper,scale=0.82
+fontsize: 11pt
+toc: true
+date: \today
+include-before: 
+  \large This is a living document describing the processes and guidelines
+   for working on Quagga. You *must* read Section
+   ["REQUIRED READING"](#sec:required), before contributing to Quagga.
+
+   Suggestions for updates, via the
+  [quagga-dev list](http://lists.quagga.net/mailman/listinfo/quagga-dev),
+  are welcome. \newpage
+...
+
+\newpage
+
+OBJECTIVES {#sec:goals}
+==========
+
+The objectives of the Quagga project are to develop and implement high
+quality routing protocols and related software, maximising:
+
+* Free software:
+    * Quagga is and will remain a copyleft, free software project
+    * Some non-core parts may be available under compatible, permissive
+      licenses to facilitate code sharing, where contributors agree.
+    * The test and integration orchestration infrastructure shall be free
+      software, developed similarly to the rest of Quagga. Proprietary
+      conformance suites may be among the test tools orchestrated.
+* Openness and transparency
+    * The business of the project shall be conducted on its public email
+      lists, to the greatest extent possible.
+    * The design of the software will be governed by open discussion on
+      the public email lists.
+    * Participants shall endeavour to be transparent about their interests
+      in the project, and any associations likely to be relevant.
+* Ethical behaviour:
+    * The licenses of all copyright holders will be respected, and the
+      project will err in their favour where there is reasonable doubt or
+      legal advice to that effect.
+    * Participants will behave with respect for others, and in a manner that
+      builds and maintains the trust needed for productive collaboration.
+
+See also the Section on [CODE OF CONDUCT](#sec:codeconduct).
+
+Governance {#sec:governance}
+==========
+
+Quagga is a Sociocracy, as it has been since its earliest days.
+
+Quagga was forked from GNU Zebra by Paul Jakma, who holds the domain name. 
+Governance was soon devolved to a collective group, the maintainers,
+consisting of those who regularly contributed and reviewed code.  The
+details can easily be changed.
+
+You are free to use reason to _persuade_ others to adopt some alternative. 
+If, after that, you truly can not abide by what is mutually agreeable, you
+are asked to do the honourable thing: take your copy of the code, make your
+apologies, and be on your way with good grace.
+
+Those who repeatedly violate the [Code of Conduct](#sec:codeconduct) will be
+asked to leave.
+
+Holding of project assets
+-------------------------
+
+One or more mature, independent trustees, with technical and free software
+experience, will be appointed as the executor(s) for key assets of the
+project to ensure continuity, such as the domain name.
+
+Should a corporate vehicle ever be created to hold such assets it __must__:
+
+* Publish up to date accounts on a regular business.
+* Generally operate openly and transparently.
+* Have control distributed, with a significant degree of control held
+  independent of any contributors with business interests in the software.
+* Carry out no other business itself that may be seen to conflict or compete
+  with the business of others in the community.
+* Have all officers disclose all interests that could be
+  seen to have a bearing on the project, as far as is reasonable.
+
+It not clear at this time that the overheads and potential liabilities of
+such a vehicle would be appropriate for this project.  These principles
+should though still be applied, where possible, to any non-corporate body
+formed around the project.
+
+CODE OF CONDUCT {#sec:codeconduct}
+===============
+
+Participants will treat each other with respect and integrity.  Participants
+will build and treasure the trust that is required for parties to
+successfully collaborate together on free software.  Particularly when those
+parties may have competing interests.  The following principles and
+guidelines should be followed to foster that trust:
+
+* Participants should be open about their goals, and their interests.
+    - Business associations with other participants should be disclosed,
+      so far as is reasonable and where applicable. E.g., if there is voting
+      on matters, or in endorsements or objections to contributions.
+    - Other associations and interests that may be relevant should be
+      disclosed, to the degree necessary to avoid any perception
+      by others of conflicts of interests or of deception.
+    - Be open about your goals, so as to maximise the common understanding
+      and minimise any misunderstandings and disputes.
+* Design should be done in the open
+    -  Do your design on list, ahead of significant implementation.  Avoid
+      "Surprise!" development where possible.
+    - Where significant implementation work must be done behind closed
+      doors, accept that you may be asked to rework it, potentially from
+      scratch once you take it public.
+    - Get "buy in" from others ahead of time, to avoid disappointment.
+* Interaction 
+    - Feedback on design should be constructive, thoughtful and 
+      understanding.
+    - Avoid personalising matters. Discuss the idea, the code, the abstract
+      subject and avoid unnecessary personal pronouns.
+    - Avoid language that paints either party into a corner. Leave some room
+      for doubt. Ask questions, rather than make assertions, where possible.
+* Disputes should be resolved through calm, analytic discussion
+    - Separate out as much of the matter under dispute into principles that
+      can be agreed on, and into the objective domain (by measurement or
+      logic). 
+    - Seek ways to resolve any remaining subjective differences by alternate
+      paths that can accommodate both sides, e.g., through abstraction or
+      modularisation.
+    - Aim for Win-Win.
+* Respect others
+    - Avoid passive-aggressive behaviours. E.g., tit-for-tat
+      non-constructive behaviour. Be explicit.
+    - It is acceptable for management to allocate resources on development
+      according to their need.  It is not acceptable to try use external,
+      management intervention to over-turn positions held by participants.
+
+REQUIRED READING {#sec:required}
+================
+
+Note well: By proposing a change to Quagga, by whatever means, you are
+implicitly agreeing:
+                                                                              
+-   To licence your contribution according to the licence of any files in
+    Quagga being modified, _and_ according to the COPYING file in the
+    top-level directory of Quagga, other than where the contribution
+    explicitly and clearly indicates otherwise.
+
+-   That it is your responsibility to ensure you hold whatever rights are
+    required to be able to contribute your changes under the licenses of the
+    files in Quagga being modified, and the top-level COPYING file.
+                                                                              
+-   That it is your responsibility to give with the contribution a full
+    account of all interests held and claims in the contribution; such as
+    through copyright, trademark and patent laws or otherwise; that are known
+    to you or your associates (e.g.  your employer).
+
+Before contributing to Quagga, you *must*  also read 
+[Section COMMIT MESSAGES](#sec:commit-messages).  
+
+You _should_ ideally read the entire document, as it contains useful
+information on the community norms and how to implement them.
+
+Please note that authorship and any relevant other rights information should
+be _explicitly_ stated with the contribution.  A "Signed-off-by" line is
+_not_ sufficient.  The "Signed-off-by" line is not used by the Quagga
+project.
+
+You may document applicable copyright claims to files being modified or
+added by your contribution.  For new files, the standard way is to add a
+string in the following format near the beginning of the file:
+
+    Copyright (C) <Year> <name of person/entity>[, optional contact details]
+
+When adding copyright claims for modifications to an existing file, please
+preface the claim with "Portions: " on a line before it and indent the
+"Copyright ..." string. If such a case already exists, add your indented
+claim immediately after. E.g.:
+
+    Portions:
+      Copyright (C) <Year> <Entity A> ....
+      Copyright (C) <Year> <Your details> [optional brief change description]
+
+
+GUIDELINES FOR HACKING ON QUAGGA {#sec:guidelines}
+================================
+
+GNU coding standards apply.  Indentation follows the result of
+invoking GNU indent (as of 2.2.8a) with the -–nut argument.
+
+Originally, tabs were used instead of spaces, with tabs are every 8 columns. 
+However, tab’s interoperability issues mean space characters are now preferred for
+new changes. We generally only clean up whitespace when code is unmaintainable
+due to whitespace issues, to minimise merging conflicts.
+
+Be particularly careful not to break platforms/protocols that you
+cannot test.
+
+Parsers or packet-writers of data from untrusted parties, particularly
+remote ones, *MUST* use the lib/stream bounded-buffer abstraction, and use
+its checked getters and putters.  Twiddling of pointers based on contents of
+untrusted data is _strongly_ discouraged - any such code is not acceptable,
+unless there are very good reasons (e.g.  compatibility with external or old
+code that is not easily rewritten).
+
+New code should have good comments, which explain why the code is correct.
+Changes to existing code should in many cases upgrade the comments when
+necessary for a reviewer to conclude that the change has no unintended
+consequences.
+
+Each file in the Git repository should have a git format-placeholder (like
+an RCS Id keyword), somewhere very near the top, commented out appropriately
+for the file type. The placeholder used for Quagga (replacing \<dollar\>
+with \$) is:
+
+`$QuaggaId: <dollar>Format:%an, %ai, %h<dollar> $`
+
+See line 2 of HACKING.tex, the source for this document, for an example.
+
+This placeholder string will be expanded out by the ‘git archive’ commands,
+which is used to generate the tar archives for snapshots and releases.
+
+Please document fully the proper use of a new function in the header file
+in which it is declared.  And please consult existing headers for
+documentation on how to use existing functions.  In particular, please consult
+these header files:
+
+<span>lib/log.h</span> logging levels and usage guidance
+
+<span>[more to be added]</span>
+
+If changing an exported interface, please try to deprecate the interface in
+an orderly manner. If at all possible, try to retain the old deprecated
+interface as is, or functionally equivalent. Make a note of when the
+interface was deprecated and guard the deprecated interface definitions in
+the header file, i.e.:
+
+    /* Deprecated: 20050406 */
+    #if !defined(QUAGGA_NO_DEPRECATED_INTERFACES)
+    #warning "Using deprecated <libname> (interface(s)|function(s))"
+    ...
+    #endif /* QUAGGA_NO_DEPRECATED_INTERFACES */
+
+This is to ensure that the core Quagga sources do not use the deprecated
+interfaces (you should update Quagga sources to use new interfaces, if
+applicable), while allowing external sources to continue to build. 
+Deprecated interfaces should be excised in the next unstable cycle.
+
+Note: If you wish, you can test for GCC and use a function
+marked with the ’deprecated’ attribute.  However, you must provide the
+warning for other compilers.
+
+If changing or removing a command definition, *ensure* that you
+properly deprecate it - use the \_DEPRECATED form of the appropriate DEFUN
+macro. This is *critical*.  Even if the command can no longer
+function, you *MUST* still implement it as a do-nothing stub.
+
+Failure to follow this causes grief for systems administrators, as an
+upgrade may cause daemons to fail to start because of unrecognised commands. 
+Deprecated commands should be excised in the next unstable cycle.  A list of
+deprecated commands should be collated for each release.
+
+See also [Section SHARED LIBRARY VERSIONING](#sec:dll-versioning) below.
+
+YOUR FIRST CONTRIBUTIONS
+========================
+
+Routing protocols can be very complex sometimes. Then, working with an
+Opensource community can be complex too, but usually friendly with
+anyone who is ready to be willing to do it properly.
+
+-   First, start doing simple tasks. Quagga’s patchwork is a good place
+    to start with. Pickup some patches, apply them on your git trie,
+    review them and send your ack’t or review comments. Then, a
+    maintainer will apply the patch if ack’t or the author will have to
+    provide a new update. It help a lot to drain the patchwork queues.
+    See <http://patchwork.quagga.net/project/quagga/list/>
+
+-   The more you’ll review patches from patchwork, the more the Quagga’s
+    maintainers will be willing to consider some patches you will be
+    sending.
+
+-   start using git clone, pwclient
+    <http://patchwork.quagga.net/help/pwclient/>
+
+        $ pwclient list -s new
+        ID    State        Name
+        --    -----        ----
+        179   New          [quagga-dev,6648] Re: quagga on FreeBSD 4.11 (gcc-2.95)
+        181   New          [quagga-dev,6660] proxy-arp patch
+        [...]
+
+        $ pwclient git-am 1046
+
+HANDY GUIDELINES FOR MAINTAINERS
+================================
+
+Get your cloned trie:
+
+      git clone vjardin@git.sv.gnu.org:/srv/git/quagga.git
+
+Apply some ack’t patches:
+
+      pwclient git-am 1046
+        Applying patch #1046 using 'git am'
+        Description: [quagga-dev,11595] zebra: route_unlock_node is missing in "show ip[v6] route <prefix>" commands
+        Applying: zebra: route_unlock_node is missing in "show ip[v6] route <prefix>" commands
+
+Run a quick review. If the ack’t was not done properly, you know who you have
+to blame.
+
+Push the patches:
+
+      git push
+
+Set the patch to accepted on patchwork
+
+      pwclient update -s Accepted 1046
+
+COMPILE-TIME CONDITIONAL CODE
+=============================
+
+Please think very carefully before making code conditional at compile time,
+as it increases maintenance burdens and user confusion. In particular,
+please avoid gratuitous -–enable-… switches to the configure script -
+typically code should be good enough to be in Quagga, or it shouldn’t be
+there at all.
+
+When code must be compile-time conditional, try have the compiler make it
+conditional rather than the C pre-processor - so that it will still be
+checked by the compiler, even if disabled. I.e.  this:
+
+        if (SOME_SYMBOL)
+          frobnicate();
+
+rather than:
+
+      #ifdef SOME_SYMBOL
+      frobnicate ();
+      #endif /* SOME_SYMBOL */
+
+Note that the former approach requires ensuring that SOME\_SYMBOL will
+be defined (watch your AC\_DEFINEs).
+
+COMMIT MESSAGES {#sec:commit-messages}
+======================================
+
+The commit message requirements are:
+
+-   The message *MUST* provide a suitable one-line summary followed by a
+    blank line as the very first line of the message, in the form:
+
+    `topic: high-level, one line summary`
+
+    Where topic would tend to be name of a subdirectory, and/or daemon, unless
+    there’s a more suitable topic (e.g. ’build’). This topic is used to
+    organise change summaries in release announcements.
+
+-   It should have a suitable “body”, which tries to address the
+    following areas, so as to help reviewers and future browsers of the
+    code-base understand why the change is correct (note also the code
+    comment requirements):
+
+    -   The motivation for the change (does it fix a bug, if so which?
+        add a feature?)
+
+    -   The general approach taken, and trade-offs versus any other
+        approaches.
+
+    -   Any testing undertaken or other information affecting the confidence
+        that can be had in the change.
+
+    -   Information to allow reviewers to be able to tell which specific
+        changes to the code are intended (and hence be able to spot any accidental
+        unintended changes).
+
+-   The commit message *must* give details of all the authors of the change,
+    beyond the person listed in the Author field.  Any and all affiliations
+    which may have a bearing on copyright in any way should be clearly
+    stated, unless those affiliations are already obvious from other
+    details, e.g.  from the email address.  This would cover employment and
+    contracting obligations (give details).
+
+    Note: Do not rely on "Signed-off-by" for this, be explicit.
+    
+-   If the change introduces a new dependency on any code or other
+    copyrighted material, please explicitly note this.  Give details of what
+    that external material is, the copyright licence the material may be
+    used under, and the nature of the dependency.
+
+The one-line summary must be limited to 54 characters, and all other
+lines to 72 characters.
+
+Commit message bodies in the Quagga project have typically taken the
+following form:
+
+-   An optional introduction, describing the change generally.
+
+-   A short description of each specific change made, preferably:
+
+    -   file by file
+
+        -   function by function (use of “ditto”, or globs is allowed)
+
+Contributors are strongly encouraged to follow this form.
+
+This itemised commit messages allows reviewers to have confidence that the
+author has self-reviewed every line of the patch, as well as providing
+reviewers a clear index of which changes are intended, and descriptions for
+them (C-to-english descriptions are not desirable - some discretion is
+useful).  For short patches, a per-function/file break-down may be
+redundant.  For longer patches, such a break-down may be essential.  A
+contrived example (where the general discussion is obviously somewhat
+redundant, given the one-line summary):
+
+>     zebra: Enhance frob FSM to detect loss of frob
+>
+>     Add a new DOWN state to the frob state machine to allow the barinator to
+>     detect loss of frob.
+>
+>     * frob.h: (struct frob) Add DOWN state flag.
+>     * frob.c: (frob_change) set/clear DOWN appropriately on state change.
+>     * bar.c: (barinate) Check frob for DOWN state.
+
+Please have a look at the git commit logs to get a feel for what the norms
+are.
+
+Note that the commit message format follows git norms, so that “git log
+–oneline” will have useful output.
+
+HACKING THE BUILD SYSTEM
+========================
+
+If you change or add to the build system (configure.ac, any Makefile.am,
+etc.), please heck that the following things still work:
+
+-   make dist
+
+-   resulting dist tarball builds
+
+-   out-of-tree builds
+
+This can be achieved by running 'make distcheck'
+
+The quagga.net site relies on make dist to work to generate snapshots. It
+must work. Common problems are to forget to have some additional file
+included in the dist, or to have a make rule refer to a source file without
+using the srcdir variable.
+
+RELEASE PROCEDURE
+=================
+
+To make a release:
+
+-   Edit configure.ac, bump the version and commit the change with
+    a "release: <version" subject.
+
+The 'release.sh' script should then be used. It should be run with 2
+arguments, the release tag for the release to be carried, and the tag of the
+previous release, e.g.:
+
+       release.sh quagga-1.1.1 quagga-1.1.0
+
+The 'release.sh' will carry out these steps for you:
+
+-   Tag the appropriate commit with a release tag (follow existing
+    conventions), with:
+
+       git tag -u <release signing key id> <quagga-release-tag>
+
+-   Create a fresh tar archive of the quagga.net repository, and do a
+    test build.  Use git archive to ensure it consists of files in the
+    repository, and to carry out the keyword expansions.  Do NOT do this in
+    a subdirectory of the Quagga sources, autoconf will think it’s a
+    sub-package and fail to include neccessary files.
+
+            git archive ... <quagga-release-tag> | tar xC ..
+
+            autoreconf -i && ./configure && make && make dist-gzip
+
+-   Similarly test the dist tarball produced. This is the tarball to be
+    released. This is important.
+
+-   Sign the dist tarball to be released
+            
+           gpg -u 54CD2E60 -a --detach-sign quagga-0.99.99.99.tar
+
+The 'release.sh' script, if finishes successfully,  will print out
+instructions on the files it has created and the details on remaining steps
+to be carried out to complete the release. Which roughly are:
+
+-   Upload the release tarball, its PGP signature, and the full changelog
+    to the public release area on Savannah
+
+-   Add the version number on https://bugzilla.quagga.net/, under
+    Administration, Products, “Quagga”, Edit versions, Add a version.
+
+-   Post a news entry on Savannah
+
+-   Send a mail to quagga-dev and quagga-users
+
+If any errors occur, move tags as needed and start over again with the
+release.sh script.  Do not try to append stuff to tarballs, as this has
+produced non-standards-conforming tarballs in the past.
+
+[TODO: collation of a list of deprecated commands. Possibly can be
+scripted to extract from vtysh/vtysh\_cmd.c]
+
+TOOL VERSIONS
+=============
+
+Require versions of support tools are listed in INSTALL.quagga.txt.
+Required versions should only be done with due deliberation, as it can
+cause environments to no longer be able to compile quagga.
+
+SHARED LIBRARY VERSIONING {#sec:dll-versioning}
+=========================
+
+[this section is at the moment just gdt’s opinion]
+
+Quagga builds several shared libaries (lib/libzebra, ospfd/libospf,
+ospfclient/libsopfapiclient).  These may be used by external programs,
+e.g. a new routing protocol that works with the zebra daemon, or
+ospfapi clients.  The libtool info pages (node Versioning) explain
+when major and minor version numbers should be changed.  These values
+are set in Makefile.am near the definition of the library.  If you
+make a change that requires changing the shared library version,
+please update Makefile.am.
+
+libospf exports far more than it should, and is needed by ospfapi
+clients.  Only bump libospf for changes to functions for which it is
+reasonable for a user of ospfapi to call, and please err on the side
+of not bumping.
+
+There is no support intended for installing part of zebra.  The core
+library libzebra and the included daemons should always be built and
+installed together.
+
+GIT COMMIT SUBMISSION {#sec:git-submission}
+=====================
+
+The preferred method for submitting changes is to provide git commits via a
+publicly-accessible git repository, which the maintainers can easily pull.
+
+The commits should be in a branch based off the Quagga.net master - a
+“feature branch”.  Ideally there should be no commits to this branch other
+than those in master, and those intended to be submitted.  However, merge
+commits to this branch from the Quagga master are permitted, though strongly
+discouraged - use another (potentially local and throw-away) branch to test
+merge with the latest Quagga master.
+
+Recommended practice is to keep different logical sets of changes on
+separate branches - “topic” or “feature” branches.  This allows you to still
+merge them together to one branch (potentially local and/or “throw-away”)
+for testing or use, while retaining smaller, independent branches that are
+easier to merge.
+
+All content guidelines in [Section PATCH SUBMISSION](#sec:patch-submission)
+apply.
+
+PATCH SUBMISSION {#sec:patch-submission}
+================
+
+-   For complex changes, contributors are strongly encouraged to first
+    start a design discussion on the quagga-dev list *before* starting
+    any coding.
+
+-   Send a clean diff against the ’master’ branch of the quagga.git
+    repository, in unified diff format, preferably with the ’-p’
+    argument to show C function affected by any chunk, and with the -w
+    and -b arguments to minimise changes. E.g:
+
+    git diff -up mybranch..remotes/quagga.net/master
+
+    It is preferable to use git format-patch, and even more preferred to
+    publish a git repository (see 
+    [Section GIT COMMIT SUBMISSION](#sec:git-submission)).
+
+    If not using git format-patch, Include the commit message in the
+    email.
+
+-   After a commit, code should have comments explaining to the reviewer
+    why it is correct, without reference to history. The commit message
+    should explain why the change is correct.
+
+-   Include NEWS entries as appropriate.
+
+-   Include only one semantic change or group of changes per patch.
+
+-   Do not make gratuitous changes to whitespace. See the w and b
+    arguments to diff.
+
+-   Changes should be arranged so that the least controversial and most
+    trivial are first, and the most complex or more controversial are
+    last. This will maximise how many the Quagga maintainers can merge,
+    even if some other commits need further work.
+
+-   Providing a unit-test is strongly encouraged. Doing so will make it
+    much easier for maintainers to have confidence that they will be
+    able to support your change.
+
+-   New code should be arranged so that it easy to verify and test. E.g.
+    stateful logic should be separated out from functional logic as much
+    as possible: wherever possible, move complex logic out to smaller
+    helper functions which access no state other than their arguments.
+
+-   State on which platforms and with what daemons the patch has been
+    tested. Understand that if the set of testing locations is small,
+    and the patch might have unforeseen or hard to fix consequences that
+    there may be a call for testers on quagga-dev, and that the patch
+    may be blocked until test results appear.
+
+    If there are no users for a platform on quagga-dev who are able and
+    willing to verify -current occasionally, that platform may be
+    dropped from the “should be checked” list.
+
+PATCH APPLICATION
+=================
+
+-   Only apply patches that meet the submission guidelines.
+
+-   If the patch might break something, issue a call for testing on the
+    mailing-list.
+
+-   Give an appropriate commit message (see above), and use the –author
+    argument to git-commit, if required, to ensure proper attribution
+    (you should still be listed as committer)
+
+-   Immediately after commiting, double-check (with git-log and/or
+    gitk). If there’s a small mistake you can easily fix it with ‘git
+    commit –amend ..’
+
+-   When merging a branch, always use an explicit merge commit. Giving
+    –no-ff ensures a merge commit is created which documents “this human
+    decided to merge this branch at this time”.
+
+STABLE PLATFORMS AND DAEMONS
+============================
+
+The list of platforms that should be tested follow.  This is a list
+derived from what quagga is thought to run on and for which
+maintainers can test or there are people on quagga-dev who are able
+and willing to verify that -current does or does not work correctly.
+
+-   BSD (Free, Net or Open, any platform)
+
+-   GNU/Linux (any distribution, i386)
+
+-   Solaris (strict alignment, any platform)
+
+-   future: NetBSD/sparc64
+
+The list of daemons that are thought to be stable and that should be
+tested are:
+
+-   zebra
+
+-   bgpd
+
+-   ripd
+
+-   ospfd
+
+-   ripngd
+
+Daemons which are in a testing phase are
+
+-   ospf6d
+
+-   isisd
+
+-   watchquagga
+
+USEFUL URLs
+===========
+
+*   The project homepage is at:
+
+    <https://www.quagga.net>
+
+
+*   Bugs can be reported via Bugzilla at:
+
+    <https://bugzilla.quagga.net>
+
+*   Buildbot runs CI tests, and is at:
+
+    <https://buildbot.quagga.net>
+
+    It tests commits and  jobs submitted on local changes via
+    'buildbot try ...' for developers.
+
+*   Patchwork tracks any patches emailed to the quagga-dev list, and is at:
+
+    <https://patchwork.quagga.net/project/quagga/list/>
+
+
+BUILDBOT
+========
+
+The buildbot client can be used to test changes before committing, with
+"buildbot try".
+
+-   Ask for a buildbot account
+
+-   Install the buildbot client
+
+-   Configure it, e.g.:
+
+    ~~~~~
+    $ cat ~/.buildbot/options
+    try_master = 'radia.quagga.net:8031'
+    try_username = 'paul'
+    try_password = 'password123'
+    try_vc = 'git'
+    try_branch = 'master'
+    try_wait = True
+    $ buildbot try -c pb --get-builder-names
+    using 'pb' connect method
+    The following builders are available for the try scheduler: 
+    build-fedora-24
+    ...
+    ~~~~~
+
+-   You can then submit your local changes to try build:
+
+    ~~~~
+    $ buildbot try -c pb
+    ~~~~
+
+    or use the -b argument to limit to a specific builder (recommended).
+
+    ~~~~~
+    $ buildbot try -c pb -b build-distcheck
+    ~~~~~
+
+-  To test a series of locally committed change use git diff:
+
+    ~~~~
+    git diff <base rev>.. | buildbot try -c pb --vc git \
+        -b build-centos-7  --branch=volatile/next --diff=- -p 1
+    ~~~~
\ No newline at end of file
diff --git a/HACKING.pending b/HACKING.pending
new file mode 100644 (file)
index 0000000..73d3194
--- /dev/null
@@ -0,0 +1,12 @@
+This file contains pointers to work done on quagga that is not in the
+quagga git repository or quagga bugzilla.
+
+* public git repositories
+
+** git remote add mtr http://github.com/tomhenderson/quagga-mtr.git
+
+Tom Henderson of Boeing has created a repository to work on
+multi-topology routing support for OSPF.  Work on this repository
+takes place on the branch mtr, which has a branch point of 0.99.17
+
+* posted patches
diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt
new file mode 100644 (file)
index 0000000..11c85b1
--- /dev/null
@@ -0,0 +1,112 @@
+--------------------------------------------------------------------------
+Building and Installing Quagga from releases or snapshots:
+
+The 'INSTALL' file contains generic instructions on how to use 'configure'
+scripts.
+
+Quagga requires a C compiler (and associated header files and
+libraries) supporting the C99 standard.
+
+Quagga requires a reasonable make.  It is considered a bug if quagga
+does not compile with the system make on recent FreeBSD, NetBSD or
+OpenBSD, and a very serious bug if it does not compile with GNU make.
+
+Quagga expects a POSIX.2 compliant system, more or less.  Clean
+workarounds for POSIX non-compliance are welcome.
+
+It is considered a bug if Quagga fails to build and run on any of the
+following systems (where .x indicates the most recent release), or
+such systems "-current" versions.  Or, it might be that this list is
+out of date and will be updated.  (Note that considering it a bug is
+not a guarantee of support, merely "we agree that it is broken".)
+
+  Dragonfly ?
+  FreeBSD (stable branches currently supported, plus perhaps one)
+  FreeBSD-current
+  Linux [kernel/distribution information needed]
+  NetBSD 4.x
+  NetBSD 5.x
+  NetBSD 6.x
+  NetBSD-current
+  OpenBSD ? [info needed on what should work]
+  Solaris (modern/supported versions, including OpenSolaris forks) 
+
+On BSD systems, installing libexecinfo is strongly recommended in order
+to get backtrace support.
+
+For further Quagga specific information on 'configure' and build-time
+configuration of the software, please read the Quagga info
+documentation, (doc/quagga.info).  To read the info page included with
+the Quagga sources without first installing Quagga:
+
+  cd doc
+  # one of the following, depending on your info viewer preferences
+  info quagga.info
+  pinfo -r quagga.info
+  emacs -eval '(info "quagga.info")'
+
+The Quagga website (http://www.quagga.net) currently has the info
+files available in various formats.
+
+--------------------------------------------------------------------------
+Building Quagga from git checkouts:
+
+In order to build from git, you will need recent versions of several GNU
+tools, particularly autoconf, automake, libtool, GNU awk and texinfo.  Note
+that the CVS snapshots on the Quagga website should not require these tools;
+everything is already setup ready to run 'configure'.  If you have trouble
+building from CVS checkout it is recommended that you try a CVS snapshot
+instead.
+
+We declare that the following versions should work for building from
+CVS checkouts.  Earlier versions may work, but failure to do so is not
+a bug.  Required versions can be moved earlier if no problems, or
+later after a judgement that a system without a higher version is
+deficient is made.
+
+       [TODO: this list is out of date as of 2013-07]
+       automake:       1.9.6 (released 2005-07-10)
+       autoconf:       2.59 (2.60 on 2006-06-26 is too recent to require)
+       libtool:        1.5.22 (released 2005-12-18)
+       texinfo:        4.7 (released 2004-04-10; 4.8 is not yet common)
+       GNU AWK:        3.1.5 (released 2005-08-12)
+
+For running tests, one also needs:
+
+       DejaGnu:
+
+[TODO: texinfo 4.6 is now ancient and this should be revisited/fixed]
+Because some systems provide texinfo 4.6 (4.7 is new), quagga.info is
+checked in so that texinfo will generally not be invoked.  When
+texinfo 4.7 is widespread, quagga.info will be removed from CVS and
+texinfo will become required again.  (4.7 has figure support, needed
+for the route server docs, which is why 4.6 doesn't work.)
+
+In order to create PostScript or PDF versions of the Texinfo documentation,
+you will need the convert utility, from the ImageMagick toolset installed,
+and epstopdf from the TeTeX suite.
+
+To create the required autotools files (Makefile.in, configure, etc.),
+run "./bootstrap.sh".  After this you may run configure as for a
+snapshot or release.
+
+Please refer to "Building and Installing Quagga" above for further
+instructions.
+
+--------------------------------------------------------------------------
+Notes on required versions:
+
+The general goal is to use a modern baseline of tools, while not
+imposing pain on those tracking supported (or almost supported) stable
+distributions.  The notes below explain what versions are present in
+various environments.
+
+NetBSD 4 provides texinfo 4.7.
+NetBSD 5 and 6 provides texinfo 4.8
+
+Fedora Core ? provides autoconf 2.59.
+
+OpenBSD 3.6 provides texinfo 4.2.
+OpenBSD [3.6] ports provides automake 1.4-p6 autoconf 2.5.9 libtool 1.5.8
+
+--------------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..3c3b65e
--- /dev/null
@@ -0,0 +1,26 @@
+## Process this file with automake to produce Makefile.in.
+
+SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @NHRPD@ \
+         @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
+         redhat @SOLARIS@ tests
+
+DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d nhrpd \
+         isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
+         solaris pimd
+
+EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \
+       update-autotools \
+       vtysh/Makefile.in vtysh/Makefile.am \
+       tools/zebra.el tools/multiple-bgpd.sh
+
+if HAVE_PANDOC
+
+HACKING.pdf: HACKING.md
+       pandoc -o $@ $<
+
+clean-local:
+       -$(LATEXMK) -C HACKING.tex
+
+endif
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..6db831e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,2594 @@
+Note: this file lists major user-visible changes only.
+
+* Changes in Quagga []
+
+- [zebra] "no link-detect" is no longer the default.  
+
+    The previous release of Quagga always explicitly writes-out the
+    link-detect configuration state.  Therefore, to retain current behavior
+    save your config with the prior release before updating.
+
+    Otherwise, review your configuration.  Note, most users will generally
+    want to have link-detect enabled, and so can just remove 'no
+    link-detect' from their interface configuration.
+
+    This release also adds a global configuration to specify the default,
+    which can be specified in the zebra configuration as:
+
+      default link-detect (on|off)
+
+    This will then apply to any interface which does not have link-detect
+    explicitly configured.
+
+* Changes in Quagga 0.99.24
+
+User-visible changes:
+- [pimd] New daemon: pimd provides IPv4 PIM-SSM multicast routing.
+- [bgpd] New feature: "next-hop-self all" to override nexthop on iBGP route
+    reflector setups.
+- [bgpd] route-maps have a new action "set ipv6 next-hop peer-address"
+- [bgpd] route-maps have a new action "set as-path prepend last-as"
+- [bgpd] Update validity checking (particularly MP-BGP / IPv6 routes) was
+    touched up significantly.  Please report possible bugs.
+- [ripd] New feature: RIP for IPv4 now supports equal-cost multipath (ECMP)
+- [zebra] Multicast RIB support has been extended.  It still is IPv4 only.
+- [zebra] "no link-detect" is now printed in configurations since it won't
+    be the default anymore soon.  To retain current behaviour, re-save your
+    configuration after updating to 0.99.24.
+
+Distributor-visible changes:
+- --enable-pimd is added to enable pimd.  It is considered experimental, though
+    unless the distribution target is embedded systems with little flash, there
+    is no reason to not include it in packages.
+- --disable-ipv6 no longer exists as an option.  It's 2015, your C library
+    really needs to have IPv6 support by now.
+- --disable-netlink no longer exists as an option.  It didn't work anyway.
+- --disable-solaris no longer exists as an option.  It only controlled some
+    init scripts.
+- --enable-isisd is now the default.
+- mrlg.cgi is no longer included (it was severely outdated).  It can be found
+    independently at http://mrlg.op-sec.us/
+- build on Linux with the musl C library should now work
+
+* Changes in Quagga 0.99.23
+
+Known issues:
+- [bgpd] setting an extcommunity in a route map on a route that already has
+  an extcommunity attribute will cause bgpd to crash.  This issue will be
+  fixed in a followup minor release.
+
+User-visible changes:
+- [lib] Performance enhancements on hashes and timers.
+- [bgpd] New feature: iBGP TTL security.
+- [bgpd] New feature: relaxed bestpath criteria for multipath and improved
+  display of multipath routes in "show ip bgp".  Scripts parsing this output
+  may need to be updated.
+- [bgpd] Multiprotocol peerings over IPv6 now try to find a more appropriate
+  IPv4 nexthop by looking at the interface.
+- [ospf6d] A large amount of changes has been merged for ospf6d.  Careful
+  evaluation prior to deployment is recommended.
+- [zebra] Recursive route support has been overhauled.  Scripts parsing
+  "show ip route" output may need adaptation.
+- [zebra] IPv6 address management has been improved regarding tentative
+  addresses.  This is visible in that a freshly configured address will not
+  immediately be marked as usable.
+- [*] a lot of bugs have been fixed, please refer to the git log
+
+* Changes in Quagga 0.99.22
+
+- [bgpd] The semantics of default-originate route-map have changed.
+  The route-map is now used to advertise the default route conditionally.
+  The old behaviour which allowed to set attributes on the originated
+  default route is no longer supported.
+- [bgpd] There is now a replace-as option to neighbor ... local-as ...
+  no-prepend.  For details, refer to the user documentation.
+- [zebra] An FPM interface has been added.  This provides an alternate
+  interface to routing information and is geared at OpenFlow & co.
+- [snmp] AgentX is now supported;  the old smux backend is considered
+  deprecated.  ospf6d has also had OSPFV3-MIB added.
+- [*] several issues with configuration save/load/apply have been fixed,
+  in particular on ospf "max-metric router-lsa administrative" and
+  "distribute-list", bgpd "no neighbor activate", isisd "metric-style",
+- [*] a lot of bugs have been fixed, please refer to the git log
+
+* Changes in Quagga 0.99.21
+
+- [bgpd] BGP multipath support has been merged
+- [bgpd] SAFI (Multicast topology) support has been extended to propagate
+  the topology to zebra.
+- [bgpd] AS path limit functionality has been removed
+- [babeld] a new routing daemon implementing the BABEL ad-hoc mesh routing
+  protocol has been merged.
+- [isisd] a major overhaul has been picked up. Please note that isisd is
+  STILL NOT SUITABLE FOR PRODUCTION USE.
+- [*] a lot of bugs have been fixed, please refer to the git log
+
+* Changes in Quagga 0.99.10
+
+- [bgpd] 4-byte AS support added
+- [bgpd] MRT format changes to version 2. Those relying on
+  bgpd MRT table dumps may need to update their tools.
+- [bgpd] Added new route-map set statement: "as-path exclude"
+- Zebra RIB updates queue has evolved into a multi-level
+  structure to address RIB consistency issues.
+
+* Changes in Quagga 0.99.2
+
+- [bgpd] Work queues added to bgpd to split up update processing,
+  particularly beneficial when a peer session goes down. AS_PATH
+  parsing rewritten to be clearer, more robust and ready for 4-byte.
+
+- [ripd] Simple authentication is no longer the default authentication
+  mode for ripd. The default is now no-authentication. Any setups which
+  used simple authentication will probably need to update their
+  configuration manually.
+
+- [ospfd] 1s dead-interval with sub-second Hellos feature added.
+  SPF timers now specified in milliseconds, and with adaptive
+  hold-time support. RFC3137 Stub-router support added. Default ABR
+  type is now 'cisco'.
+
+- Solaris least privileges support added.
+
+* Changes in Quagga 0.99.1
+
+- Zserv is now buffered via threads and non-blocking in most cases for both
+  clients and zebra, which should improve responsiveness of daemons when
+  they must send many messages to zebra.
+
+- 'show thread cpu' now displays both cpu+system and wall-clock time,
+  where getrusage() is available.
+
+- Background threads added and workqueue API added, with a
+  'show work-queues' command. Thread scheduling improved slightly.
+
+- Zebra now has a work-queue for RIB processing. See 'show work-queues' in
+  the zebra daemon vty.
+
+- Support for interface renaming on Linux netlink systems.
+
+- GNU Zebra bgpd merges, including BGP Graceful-restart and "match ip
+  route-source" command.
+
+- Automatic logging of backtraces should daemons crash to assist in
+  diagnosis. See the documentation for more information on configuring
+  logging correctly, and set --enable-gcc-rdynamic if compiling with gcc.
+
+* Changes in Quagga 0.98.0
+
+- Logging facilities upgraded.  One can now specify a severity level
+  for each logging destination.  And a new "show logging" command gives
+  thorough information on the current logging system configuration.
+
+- Watchquagga daemon added.  This is not well tested yet.  Please try
+  monitor mode first before enabling restart features.  It is important
+  to make sure that the various timers are configured with appropriate
+  values for your site.
+
+- BGP route-server support added. See the texinfo documentation.
+
+- OSPF API initialisation is disabled by default even if compiled in. You
+  can enable it with -a/--apiserver command line switch.
+
+- "write-config integrated" vtysh command replaced with "service
+  integrated-vtysh-config" command.
+
+- Router id is now handled by zebra daemon and all daemons receive changes
+  from it. Router id can be overriden in daemons' configurations of course.
+  To fix common router id in zebra daemon you can either install non-127
+  address on loopback or use "router-id x.x.x.x" command.
+
+- "secondary" keyword is removed from ip address configuration. All
+  supported OS'es have their own vision what's secondary address and
+  how to handle it.
+
+- Zebra no longer enables forwarding by default. If you rely on zebra to
+  enable forwarding make sure to add '<ip|ip6> forwarding' statements
+  to your zebra configuration file.
+
+- All libraries are built and used shared, on platforms where libtool
+  supports shared libraries.
+
+- Router advertisement syntax is changed. In usual cases (if you didn't do
+  any fancy stuff) it's enough to change lines in configuration from:
+  "ipv6 nd prefix-advertisement X:X:X:X::/X 2592000 604800 autoconfig on-link"
+  to:
+  "ipv6 nd prefix X:X:X:X::/X"
+
+  All router advertisement options are documented in texi documentation.
+
+- --enable-nssa configure switch is removed. NSSA support is stable enough.
+
+- Daemons don't look at current directory for config file any more.
+
+* Changes in Quagga 0.96.5
+
+- include files are installed in $(prefix)/include/quagga.  Programs
+  building against these includes should -I$(prefix)/include and e.g.
+  #include <quagga/routemap.h>
+
+- New option --enable-exampledir puts example files in a separate
+  directory from $(sysconfdir), easing NetBSD pkgsrc hierarchy rules
+  compliance.
+
+- New configure options --enable-configfile-mask and
+  --enable-logfile-mask to set umask values for config and log
+  values.  Masks default to 0600, matching previous behavior.
+
+- Import current CVS isisd from SourceForge, then merge it with
+  the Quagga's Framework.
+
+* Changes in Quagga 0.96.4
+
+- Further fixes to ospfd, some relating to the PtP revert. Interface
+lookups should be a lot more robust now. 
+
+- Fix for a remote triggerable crash in vty layer.
+
+- Improvements to ripd, and addition of split horizon support.
+
+- Improved bgpd table support, now dumps at time of day intervals rather
+than time from startup intervals. Much improved support for IPv6 table
+dumps. show commands for views improved.
+
+* Changes in Quagga 0.96.3
+
+- revert the 'generic PtP' patch. Means Quagga will no longer work with
+FreeSWAN, however, on the plus side this gets rid of a lot of niggly bugs
+which the PtP patch introduced.
+
+* Changes in Quagga 0.96.2
+
+- Fix crash in ospfd
+
+* Changes in Quagga 0.96.1
+
+- Iron out problem with the privileges definitions
+
+* Changes in Quagga 0.96
+
+- Privilege support, daemons now run with the minimal privileges needed, see
+  the documentation for details.
+  
+- NSSA ABR support in ospfd.
+
+- OSPF-API support merged in.
+
+- 6WIND patch merged in.
+
+* Changes in zebra-0.93
+
+* Changes in bgpd
+
+** Configuration is changed to new format.
+
+* Changes in ospfd
+
+** Crush bugs which reported on Zebra ML is fixed.
+
+** Opaque LSA and TE LSA support is added by KDD R&D Laboratories,
+   Inc.
+
+* Chages in ospf6d
+
+** Many bugs are fixed.
+
+* Changes in zebra-0.92a
+
+* Changes in bgpd
+
+** Fix "^$" community list bug.
+
+** Below command's Address Family specific configurations are added
+
+  nexthop-self
+  route-reflector-client
+  route-server-client
+  soft-reconfiguration inbound
+
+* Changes in zebra
+
+** Treat kernel type routes as EGP routes.
+
+* Changes in zebra-0.92
+
+** Overall security is improved.  Default umask is 0077.
+
+* Changes in ripd
+
+** If output interface is in simple password authentication mode,
+substruct one from rtemax.
+
+* Changes in bgpd
+
+** IPv4 multicast and IPv6 unicast configuration is changed to so
+called new config.  All of AFI and SAFI specific configuration is
+moved to "address-family" node.  When you have many IPv6 only
+configuration, you will see many "no neighbor X:X::X:X activate" line
+in your configuration to disable IPv4 unicast NLRI exchange.  In that
+case please use "no bgp default ipv4-unicast" command to suppress the
+output.  Until zebra-0.93, old config is still left for compatibility.
+
+Old config
+==========
+router bgp 7675
+ bgp router-id 10.0.0.1
+ redistribute connected
+ network 192.168.0.0/24
+ neighbor 10.0.0.2 remote-as 7675
+ ipv6 bgp network 3ffe:506::/33
+ ipv6 bgp network 3ffe:1800:e800::/40
+ ipv6 bgp aggregate-address 3ffe:506::/32
+ ipv6 bgp redistribute connected
+ ipv6 bgp neighbor 3ffe:506:1000::2 remote-as 1
+
+New config
+==========
+router bgp 7675
+ bgp router-id 10.0.0.1
+ network 192.168.0.0/24
+ redistribute connected
+ neighbor 10.0.0.2 remote-as 7675
+ neighbor 3ffe:506:1000::2 remote-as 1
+ no neighbor 3ffe:506:1000::2 activate
+!
+ address-family ipv6
+  network 3ffe:506::/33
+  network 3ffe:1800:e800::/40
+  aggregate-address 3ffe:506::/32
+  redistribute connected
+  neighbor 3ffe:506:1000::2 activate
+ exit-address-family
+
+* Changes in ospfd
+
+** Internal interface treatment is changed.  Now ospfd can handle
+multiple IP address for an interface.
+
+** Redistribution of loopback interface's address works fine.
+
+* Changes in zebra-0.91
+
+** --enable-oldrib configure option is removed.
+
+** HAVE_IF_PSEUDO part is removed.  Same feature is now supported by
+default.
+
+* Changes in ripd
+
+** When redistributed route is withdrawn, perform poisoned reverse.
+
+* Changes in zebra
+
+** When interface's address is removed, kernel route pointing out to
+the address is removed.
+
+** IPv6 RIB is now based upon new RIB code.
+
+** zebra can handle same connected route to one interface.
+
+** New command for interface address.  Currently this commands are
+only supported on GNU/Linux with netlink interface.
+
+"ip address A.B.C.D secondary"
+"ip address A.B.C.D label LABEL"
+
+* Changes in bgpd
+
+** BGP flap dampening bugs are fixed.
+
+** BGP non-blocking TCP connection bug is fixed.
+
+** "show ip bgp summary" shows AS path and community entry number.
+
+** New commands have been added.
+  "show ip bgp cidr-only"
+  "show ip bgp ipv4 (unicast|multicast) cidr-only"
+  "show ip bgp A.B.C.D/M longer-prefixes"
+  "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes"
+  "show ipv6 bgp X:X::X:X/M longer-prefixes"
+  "show ipv6 mbgp X:X::X:X/M longer-prefixes"
+
+** IPv6 IBGP nexthop change is monitored.
+
+** Unknown transitive attribute is passed with partial flag bit on.
+
+* Changes in ospfd
+
+** Fix bug of LSA MaxAge flood.
+
+** Fix bug of NSSA codes.
+
+* Changes in zebra-0.90
+
+** From this beta release, --enable-unixdomain and --enable-newrib
+becomes default.  So both options are removed from configure.in.  To
+revert old behavior please specify below option.
+
+--enable-tcp-zebra # TCP/IP socket is used for protocol daemon and zebra.
+--enable-oldrib    # Turn on old RIB implementation.
+
+Old RIB implementation will be removed in zebra-0.91.
+
+** From this beta release --enable-multipath is supported.  This
+option is only effective on GNU/Linux kernel with
+CONFIG_IP_ADVANCED_ROUTER and CONFIG_IP_ROUTE_MULTIPATH is set.
+
+--enable-multipath=ARG  # ARG must be digit.  When ARG is 0 unlimit multipath number.
+
+** From this release we do not include guile files.
+
+* Changes in lib
+
+** newlist.[ch] is merged with linklist.[ch].
+
+** Now Zebra works on MacOS X public beta.
+
+** Access-list can have remark.  "access-list WORD remark LINE" define
+remark for specified access-list.
+
+** Key of key-chain is sorted by it's idetifier value.
+
+** prefix-list rule is slightly changed.  The rule of "len <= ge-value
+<= le-value" is changed to "len < ge-value <= le-value".
+
+** According to above prefix-list rule change, add automatic
+conversion function of an old rule. ex.) 10.0.0.0/8 ge 8 -> 10.0.0.0/8
+le 32
+
+** SMUX can handle SNMP trap.
+
+** In our event library, event thread is executed before any other
+thread like timer, read and write event.
+
+** Robust method for writing configuration file and recover from
+backing up config file.
+
+** Display "end" at the end of configuration.
+
+** Fix memory leak in vtysh_read().
+
+** Fix memroy leak about access-list and prefix-list name.
+
+* Changes in zebra
+
+** UNIX domain socket server of zebra protocol is added.
+
+** Fix PointoPoint interface network bug.  The destination network
+should be installed into routing table instead of local network.
+
+** Metric value is reflected to kernel routing table.
+
+** "show ip route" display uptime of RIP,OSPF,BGP routes.
+
+** New RIB implementation is added.
+
+Now we have enhanced RIB (routing information base) implementation in
+zebra.  New RIB has many new features and fixed some bugs which exist
+in old RIB code.
+
+*** Static route with distance value
+
+    Static route can be specified with administrative distance.  The
+  distance value 255 means it is not installed into the kernel.
+  Default value of distance for static route is 1.
+  
+    ip route A.B.C.D/M A.B.C.D <1-255>
+    ip route A.B.C.D/M IFNAME <1-255>
+  
+    If the least distance value's route's nexthop are unreachable,
+  select the least distance value route which has reachable nexthop is
+  selected.
+  
+    ip route 0.0.0.0/0 10.0.0.1
+    ip route 0.0.0.0/0 11.0.0.1 2
+  
+    In this case, when 10.0.0.1 is unreachable and 11.0.0.1 is
+  reachable.  The route with nexthop 11.0.0.1 will be installed into
+  forwarding table.
+  
+    zebra> show ip route
+    S>* 0.0.0.0/0 [2/0] via 11.0.0.1
+    S   0.0.0.0/0 [1/0] via 10.0.0.1 inactive
+  
+    If the nexthop is unreachable "inactive" is displayed.  You can
+  specify any string to IFNAME.  There is no need of the interface is
+  there when you configure the route.
+  
+    ip route 1.1.1.1/32 ppp0
+  
+  When ppp0 comes up, the route is installed properly.
+
+*** Multiple nexthop routes for one prefix
+
+    Multiple nexthop routes can be specified for one prefix.  Even the
+  kernel support only one nexthop for one prefix user can configure
+  multiple nexthop.
+  
+    When you configure routes like below, prefix 10.0.0.1 has three
+  nexthop.
+  
+    ip route 10.0.0.1/32 10.0.0.2
+    ip route 10.0.0.1/32 10.0.0.3
+    ip route 10.0.0.1/32 eth0
+  
+    If there is no route to 10.0.0.2 and 10.0.0.3.  And interface eth0
+    is reachable, then the last route is installed into the kernel.
+  
+    zebra> show ip route
+    S>  10.0.0.1/32 [1/0] via 10.0.0.2 inactive
+                          via 10.0.0.3 inactive
+      *                   is directly connected, eth0
+  
+    '*' means this nexthop is installed into the kernel. 
+
+*** Multipath (more than one nexthop for one prefix) can be installed into the kernel.
+
+    When the kernel support multipath, zebra can install multipath
+  routes into the kernel.  Before doing that please make it sure that
+  setting --enable-multipath=ARG to configure script.  ARG must be digit
+  value.  When specify 0 to ARG, there is no limitation of the number
+  of the multipath.  Currently only GNU/Linux with netlink interface is
+  supported.
+  
+    ip route 10.0.0.1/32 10.0.0.2
+    ip route 10.0.0.1/32 10.0.0.3
+    ip route 10.0.0.1/32 eth0
+  
+    zebra> show ip route
+    S>* 10.0.0.1/32 [1/0] via 10.0.0.2
+      *                   via 10.0.0.3
+                          is directly connected, eth0
+
+*** Kernel message delete installed route.
+
+    After zebra install static or dynamic route into the kernel.
+  
+    R>* 0.0.0.0/0 [120/3] via 10.0.0.1
+  
+    If you delete this route outside zebra, old zebra does not reinstall
+  route again.  Now the route is re-processed and properly reinstall the
+  static or dynamic route into the kernel.
+
+** GNU/Linux netlink socket handling is improved to fix race condition
+between kernel message and user command responce.
+
+* Changes in bgpd
+
+** Add show neighbor's routes command.
+
+  "show ip bgp neighbors (A.B.C.D|X:X::X:X) routes"
+  "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) routes"
+  "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) routes"
+  "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) routes"
+
+** BGP passive peer support problem is fixed.
+
+** Redistributed IGP nexthop is passed to BGP nexthop.
+
+** On multiaccess media, if the nexthop is reachable nexthop is passed
+as it is.
+
+** Remove zebra-0.88 compatibility commands.
+
+  "match ip prefix-list WORD"
+  "match ipv6 prefix-list WORD"
+
+  Instead of above please use below commands.
+
+  "match ip address prefix-list WORD"
+  "match ipv6 address prefix-list WORD"
+
+** Fix bug of holdtimer is not reset when bgp cleared.
+
+** "show ip bgp summary" display peer establish/drop count.
+
+** Change "match ip next-hop" argument from IP address to access-list
+name.
+
+** When "bgp enforce-first-as" is enabled, check EBGP peer's update
+has it's AS number in the first AS number in AS sequence.
+
+** New route-map command "set community-delete COMMUNITY-LIST" is
+added.  Community matched the CoMMUNITY-LIST is removed from the
+community.
+
+** BGP-MIB implementation is finished.
+
+** When BGP connection comes from unconfigured IP address, close
+socket immediately.
+
+** Do not compare router ID when the routes comes from EBGP peer.
+When originator ID is same, take shorter cluster-list route.  If
+cluster-list is same take smaller IP address neighbor's route.
+
+** Add "bgp bestpath as-path ignore" command.  When this option is
+set, do not concider AS path length when route selection.
+
+** Add "bgp bestpath compare-routerid".  When this option is set,
+compare router ID when the routes comes from EBGP peer.
+
+** Add "bgp deterministic-med" process.
+
+** BGP flap dampening feature is added.
+
+** When IBGP nexthop is changed, it is reflected to RIB.
+
+** Change "neighbor route-refresh" command to "neighbor capability
+route-refresh".
+
+* Changes in ripd
+
+** Change "match ip next-hop" argument from IP address to access-list
+name.
+
+** "no ip rip (send|receive)" command accept version number argument.
+
+** Memory leak related classfull network generation is fixed.
+
+** When a route is in garbage collection process (invalid with metric
+16) and a router receives the same route with valid metric then route
+was not installed into zebra rib, but only into ripd rib. Moreover ,
+it will never get into zebra rib, because ripd wrongly assumes it's
+already there.
+
+* Change in ospfd
+
+** Fix bug of refreshing default route.
+
+** --enable-nssa turn on undergoing NSSA feature.
+
+** Fix bug of Hello packet's option is not properly set when interface
+comes up.
+
+** Reduce unconditional logging.
+
+** Add nexthop to OSPF path only when it is not there.
+
+** When there is no DR on network (suppose you have only one router
+with interface priority 0). It's router LSA does not contain the link
+information about this network.
+
+** When you change a priority of interface from/to 0
+ISM_NeighborChange event should be scheduled in order to elect new
+DR/BDR on the network.
+
+** When we add some LSA into retransmit list we need to check whether
+the present old LSA in retransmit list is not more recent than the new
+one.
+
+** In states Loading and Full the slave must resend its last Database
+Description packet in response to duplicate Database Description
+packets received from the master.  For this reason the slave must wait
+RouterDeadInterval seconds before freeing the last Database
+Description packet. Reception of a Database Description packet from
+the master after this interval will generate a SeqNumberMismatch
+neighbor event. RFC2328 Section 10.8
+
+** Virtual link can not configured in stub area.
+
+** Clear a ls_upd_queue queue of the interface when interface goes
+down.
+
+** "no router ospf" unregister redistribution requests from zebra.
+
+** New command for virtual-link configuration is added.
+
+  "area A.B.C.D virtual-link A.B.C.D"
+  "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535>"
+  "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> authentication-key AUTH_KEY"
+  "area A.B.C.D virtual-link A.B.C.D authentication-key AUTH_KEY"
+  "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> message-digest-key <1-255> md5 KEY"
+  "area A.B.C.D virtual-link A.B.C.D message-digest-key <1-255> md5 KEY"
+
+** Clear cryptographic sequence number when neighbor status is changed
+to NSM down.
+
+** Make Summary LSA's origination and refreshment as same as other
+type of LSA.
+
+** New OSPF pakcet read method. Now maximum packet length may be 65535
+bytes (maximum IP packet length).
+
+** Checking the age of the found LSA and if the LSA is MAXAGE we
+should call refresh instead of originate.
+
+** Install multipath information to zebra.
+
+** Fix socket descriptor leak when system call failed.
+
+* Changes in ospf6d
+
+** Whole functionality has been rewritten as new code. new command
+"show ipv6 ospf6 spf node", "show ipv6 ospf6 spf tree", "show ipv6
+ospf6 spf table" has been added.
+
+** Change to do not send garbage route whose nexthop is not linklocal
+address.
+
+** "redistribute ospf6" was generated in "router ospf6" in config
+file. It is fixed.
+
+** LSDB sync bug is fixed.
+
+** Fix bug of using unavailable route.
+
+* Changes in vtysh
+
+** route-map and access-list configuration is merged into one
+configuration.
+
+** /usr/local/etc/Zebra.conf is integrated configuration file.  "write
+memory" in vtysh will write whole configuration to this file.
+
+** When -b option is specified to vtysh, vtysh read
+/usr/local/etc/Zebra.conf file then pass the confuguration to proper
+protocol daemon.  So make all protocol daemon's configuration file
+empty then invoke all daemon.  After that vtysh -b will setup saved
+configuration.
+
+zebrastart.sh
+=============
+/usr/local/sbin/zebra -d
+/usr/local/sbin/ripd -d
+/usr/local/sbin/ospfd -d
+/usr/local/sbin/bgpd -d
+/usr/local/bin/vtysh -b
+
+* Changes in zebra-0.89
+
+* Changes in lib
+
+** distribute-list can set all interface's access-list and prefix-list
+configuration.
+
+* Changes in ripd
+
+** "show ip protocols" display proper distribute-list settings and
+distance settings.
+
+** When metric infinity route received withdraw the route from kernel
+immediately it used to be wait garbage collection.
+
+** key-chain can be used for simple password authentication.
+
+** RIPv2 MIB getnext interface bug is fixed.
+
+* Changes in vtysh
+
+** --with-libpam enable PAM authentication for vtysh.
+
+** Now vtysh read vtysh.conf.  This file should be
+${SYSCONFDIR}/etc/vtysh.conf for security reason.  Usually it is
+/usr/local/etc/vtysh.conf.
+
+** "username WORD nopassword" command is added to vtysh.
+
+* Chagees in ospfd
+
+** NBMA interface support is added.
+
+** OSPF area is sorted by area ID.
+
+** New implementation of OSPF refreesh.
+
+** OSPF-MIB read function is partly added.
+
+* Changes in bgpd
+
+** When the peering is done by ebgp-multihop, nexthop is looked up
+like IBGP routes.
+
+** "show ip mbgp" commands are changed to "show ip bgp ipv4
+multicast".
+
+** New terminal commands are added.
+  "show ip bgp ipv4 (unicast|multicast) filter-list WORD"
+  "show ip bgp ipv4 (unicast|multicast) community"
+  "show ip bgp ipv4 (unicast|multicast) community-list WORD"
+  "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match"
+
+** MBGP soft-reconfiguration command is added.
+  "clear ip bgp x.x.x.x ipv4 (unicast|multicast) in"
+  "clear ip bgp x.x.x.x ipv4 (unicast|multicast) out"
+  "clear ip bgp x.x.x.x ipv4 (unicast|multicast) soft"
+  "clear ip bgp <1-65535> ipv4 (unicast|multicast) in"
+  "clear ip bgp <1-65535> ipv4 (unicast|multicast) out"
+  "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft"
+  "clear ip bgp * ipv4 (unicast|multicast) in"
+  "clear ip bgp * ipv4 (unicast|multicast) out"
+  "clear ip bgp * ipv4 (unicast|multicast) soft"
+
+** MED related commands are added.
+  "bgp deterministic-med"
+  "bgp bestpath med confed"
+  "bgp bestpath med missing-as-worst"
+
+** "bgp default local-preference" command is added.
+
+** BGP confederation peer's routes are passed to zebra like IBGP route.
+
+** Community match command is added.
+  "show ip bgp community <val>"
+  "show ip bgp community <val> exact-match"
+
+** EBGP multihop route treatment bug is fixed.  Now nexthop is
+resolved by IGP routes.
+
+** Some commands are added to show routes by filter-list and community
+value.
+  "show ip bgp ipv4 (unicast|multicast) filter-list WORD"
+  "show ip bgp ipv4 (unicast|multicast) community"
+  "show ip bgp ipv4 (unicast|multicast) community-list WORD"
+  "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match"
+
+* Changes in zebra
+
+** zebra read interface's address information using getifaddrs() when
+it is available.
+
+** Reflect IPv6 interface's address change to protocol daemons.
+
+* Changes in zebra-0.88
+
+* Changes in lib
+
+** "exact-match" option is added to "access-list" and "ipv6
+access-list" command.  If this option is specified, the prefix and
+prefix length is compared as exact match mode.
+
+* Changes in zebra
+
+** New Zebra message ZEBRA_REDISTRIBUTE_DEFAULT_ADD and
+ZEBRA_REDISTRIBUTE_DEFAULT_DELTE are added.
+
+** Default administrative distance value is changed.
+
+            Old         New
+------------------------------------------
+system      10           0
+kernel      20           0
+connected   30           0
+static      40           1
+rip         50         120
+ripng       50         120
+ospf        60         110
+ospf6       49         110
+bgp         70         200(iBGP)  20(eBGP)
+------------------------------------------
+
+** Distance value can be passed from protocol daemon to zebra.
+
+** "show ip route" shows [metric/distance] value pair.
+
+** Zebra Protocol is changed to support multi-path route and distance
+value.
+
+* Changes in ospfd
+
+** "default-information originate [always]" command is added.
+
+** "default-metric <0-16777214>" command is added.
+
+** "show ip ospf database" command is integrated.  LS-ID and AdvRouter can
+   be specifed.  The commands are
+
+   show ip ospf database TYPE LS-ID
+   show ip ospf database TYPE LS-ID ADV-ROUTER
+   show ip ospf database TYPE LS-ID self-originate
+   show ip ospf database TYPE self-originate
+
+** route-map support for `redistribute' command are added.
+   Supported `match' statements are
+
+   match interface
+   match ip address
+   match next-hop
+
+   Supported `set' statements are
+
+   set metric
+   set metric-type
+
+** Pass OSPF metric value to zebra daemon.
+
+* Changes in ripd
+
+** When specified route-map does not exist, it means all deny.
+
+** "default-metric <1-16>" command is added.
+
+** "offset-list ACCESS-LIST-NAME <0-16>" and "offset-list
+ACCESS-LIST-NAME <0-16> IFNAME" commands are added.
+
+** "redistribute ROUTE-TYPE metric <0-16>" command is added.
+
+** "default-information originate" command is added.
+
+** "ip split-horizon" and "no ip split-horizon" is added to interface
+configuration.
+
+** "no router rip" command is added.
+
+** "ip rip authentication mode (md5|text)" is added to interface
+configuration.
+
+** "ip rip authentication key-chain KEY-CHAIN" is added to interface
+configuration.
+
+** Pass RIP metric value to zebra daemon.
+
+** Distance manipulation functions are added.
+
+* Changes in bgpd
+
+** Fix bug of next hop treatment for MPLS-VPN route exchange.
+
+** BGP peer MIB is updated.
+
+** Aggregated route has origin IGP, atomic-aggregate and proper
+aggregator attribute.
+
+** Suppressed route now installed into BGP table.  It is only
+suppressed from announcement.
+
+** BGP router-id is properly set after "no router bgp ASN" and "router
+bgp ASN".
+
+** Add check for nexthop is accessible or not for IBGP routes.
+
+** Add cehck for nexthop is on connected or not for EBGP routes.
+
+** "dump bgp route" command is changed to "dump bgp route-mrt" for
+generating MRT compatible dump output.
+
+** Soft reconfiguration inbound and outbound is supported.
+
+** Route refresh feature is supported.
+
+* Changes in vtysh
+
+** VTY shell is now included into the distribution.
+
+* Changes in zebra-0.87
+
+* Changes in lib
+
+** "show startup-config" command is added.
+
+** "show history" command is added.
+
+** Memory statistics command is changed.  New command
+
+   show memory all
+   show memory lib
+   show memory rip
+   show memory ospf
+   show memory bgp
+
+are added.
+
+** Filters can be removed only specify it's name.  New command
+
+   no access-list NAME
+   no ip community-list NAME
+   no ip as-path access-list NAME
+   no route-map NAME
+
+are added.
+
+** At any node, user can view/save user configuration.
+
+   write terminal
+   write file
+   wirte memory
+
+are added to every node in default.
+
+** LCD completion is added.  For example both "ip" and "ipv6" command
+are exist, "i" then press TAB will be expanded to "ip".
+
+* Changes in bgpd
+
+** "show ip bgp" family shows total number of prefixes.
+
+** "no bgp default ipv4-unicast" command is added.
+
+** Extended Communities support is added.
+
+** "no neighbor PEER send-community extended" command is added.
+
+** MPLS-VPN PE-RR support is added.
+
+ New address family vpnv4 unicast is introduced.
+
+  !
+  address-family vpnv4 unicast
+   neighobr PEER activate
+   network A.B.C.D rd RD tag TAG
+  exit-address-family
+  !
+
+ To make it route-reflector, please configure it under normal router
+bgp ASN.
+
+  !
+  router bgp 7675
+   no bgp default ipv4-unicast
+   bgp router-id 10.0.0.100
+   bgp cluster-id 10.0.0.100
+   neighbor 10.0.0.1 remote-as 65535
+   neighbor 10.0.0.1 route-reflector-client
+   neighbor 10.0.0.2 remote-as 65535
+   neighbor 10.0.0.2 route-reflector-client
+   neighbor 10.0.0.3 remote-as 65535
+   neighbor 10.0.0.3 route-reflector-client
+  !
+  address-family vpnv4 unicast
+   neighbor 10.0.0.1 activate
+   neighbor 10.0.0.2 activate
+   neighbor 10.0.0.3 activate
+  exit-address-family
+  !
+
+* Changes in ospfd
+
+** Many many bugs are fixed.
+
+* Changes in ripd
+
+** Better interface up/down event handle.
+
+* Changes in zebra
+
+** Better interface up/down event handle.
+
+* Changes in zebra-0.86
+
+* Changes in lib
+
+** Fix bug of exec-timeout command which may cause crush.
+
+** Multiple same policy for "access-list", "ip prefix-list, "as-path
+access-list", "ip community-list" is not duplicated.
+
+** It used to be "ip prefix-list A.B.C.D/M" match routes which mask >=
+M.  Now default behavior is exact match so it only match routes which
+mask == M.
+
+* Changes in bgpd
+
+** "match ip address prefix-list" is added to route-map.
+
+** A route without local preference is evaluated as 100 local preference.
+
+** Select smaller router-id route when other values are same.
+
+** Compare MED only both routes comes from same neighboring AS.
+
+** "bgp always-compare-med" command is added.
+
+** Now MED value is passed to IBGP peer.
+
+** When neighbor's filter is configured with non-existent access-list,
+as-path access-list, ip prefix-list, route-map.  The behavior is
+changed from all permit to all deny.
+
+* Changes in ospfd
+
+** Fix bug of external route tag byte order.
+
+** OSPF Neighbor deletion bug which cause crush is fixed.
+
+** Some route calculation bug are fixed.
+
+** Add sanity check with router        routing table.
+
+** Fix bug of memory leak about linklist.
+
+** Fix bug of 1-WayReceived in NSM.
+
+** Take care of BIGENDIAN architecture.
+
+** Fix bug of NSM state flapping between ExStart and Exchange.
+
+** Fix bug of Network-LSA originated in stub network.
+
+** Fix bug of MS flag unset.
+
+** Add to schedule router_lsa origination when the interface cost
+changes.
+
+** Increment LS age by configured interface transmit_delay.
+
+** distribute-list is reimplemented.
+
+** Fix bug of refresh never occurs.
+
+** Fix bug of summary-LSAs reorigination.  Correctly copy
+OSPF_LSA_APPROVED flag to new LSA. when summary-LSA is reoriginatd.
+
+** Fix bug of re-origination when a neighbor disappears.
+
+** Fix bug of segmentation fault with DD retransmission.
+
+** Fix network-LSA re-origination problem.
+
+** Fix problem of remaining withdrawn routes on zebra.
+
+* Changes in ripd
+
+** Do not leave from multicast group when interface goes down bug is
+fixed.
+
+* Changes in zebra
+
+** Remove client structure when client dies.
+
+** Take care static route when interface goes up/down.
+
+* Changes in zebra-0.85
+
+* Changes in bgpd
+
+** "transparent-nexthop" and "transparenet-as" commands are added.
+
+** Route reflector's originator-id bug is fixed.
+
+* Changes in ospfd
+
+** Fix bug of OSPF LSA memory leak.
+
+** Fix bug of OSPF external route memory leak.
+
+** AS-external-LSA origination bug was fixed.
+
+** LS request treatment is completely rewritten.  Now performance is
+drastically improved.
+
+* Changes in ripd
+
+** RIPv1 update is done by class-full manner.
+
+* Changes in zebra-0.84b
+
+* Changes in lib
+
+** Fix bug of inet_pton return value handling
+
+* Changes in bgpd
+
+** Fix bug of BGP-4+ link-local address nexthop check for IBGP peer.
+
+** Don't allocate whole buffer for displaying "show ip bgp".  Now it
+consume only one screen size memory.
+
+* Changes in ripd
+
+** Fix debug output string.
+
+** Add RIP peer handling.  RIP peer are shown by "show ip protocols".
+
+* Changes in zebra-0.84a
+
+* Changes in bgpd
+
+** Fix serious bug of BGP-4+ peering under IPv6 link-local address.
+   Due to the bug BGP-4+ peering may not be established.
+
+* Changes in zebra-0.84
+
+* Changes in lib
+
+** IPv6 address and prefix parser is added to VTY by Toshiaki Takada
+   <takada@zebra.org>.  DEFUN string is "X:X::X:X" for IPv6 address,
+   "X:X::X:X/M" for IPv6 prefix.  You can use it like this.
+
+   DEFUN (func, cmd, "neighbor (A.B.C.D|X:X::X:X) remote-as <1-65535>")
+
+** VTY configuration is locked during configuration.  This is for
+   avoiding unconditional crush from two terminals modify the
+   configuration at the same time.  "who" command shows which termnal
+   lock the configuration.  VTY which has '*' character at the head of
+   line is locking the configuration.
+
+** Old logging functions are removed.  Functions like
+   log_open,log_close,openlog are deleted.  Instead of that please use
+   zlog_* functions.  zvlog_* used in ospf6d are deleted also.
+
+** "terminal monitor" command is added.  "no terminal monitor" is for
+   disabling.  This command simply display logging information to the
+   VTY.
+
+** dropline.[ch] files are deleted.
+
+* Changes in bgpd
+
+** BGP neighbor configuration are sorted by it's IP address.
+
+** BGP peer configuration and actual peer is separated.  This is
+   preparation for Route Server support.
+
+** "no neighbor PEER" command is added. You can delete neighbor
+   without specifying AS number.
+
+** "no neighbor ebgp-multihop" command is added.
+
+** "no neighbor port PORT" command is added.
+
+** To conform RFC1771, "neighbor PEER send-community" is default
+   behavior.  If you want to disable sending community attribute,
+   please specify "no neighbor PEER send-community" to the peer.
+
+** "neighbor maximum-prefix NUMBER" command is added.
+
+** Multi-protocol extention NLRI is proceeded only when the peer is
+   configured proper Address Family and Subsequent Address Family.  If
+   not, those NLRI are simply ignored.
+
+** Aggregate-address support is improved.  Currently below commands
+   works.
+
+  "aggregate-address"
+  "aggregate-address summary-only"
+  "no aggregate-address"
+  "no aggregate-address summary-only"
+
+  "ipv6 bgp aggregate-address"
+  "ipv6 bgp aggregate-address summary-only"
+  "no ipv6 bgp aggregate-address"
+  "no ipv6 bgp aggregate-address summary-only"
+
+** redistribute route-map bug is fixed.
+
+** MBGP support becomes default.  "configure" option --enable-mbgp is
+   removed.
+
+** New command "neighbor PEER timers connect <1-65535>" is added.
+
+** New command "neighbor PEER override-capability" is added.
+
+** New command "show ip bgp neighbor A.B.C.D advertised-route" is added.
+
+** New command "show ip bgp neighbor A.B.C.D routes" is added.  To use
+   this command, you have to configure neighbor with
+   "neighbor A.B.C.D soft-reconfiguration inbound" beforehand.
+   
+
+* Changes in zebra-0.83
+
+* bgpd
+
+** Serious bug fix about fetching global and link-local address at the
+same time.  Due to this bug, corrupted IPv6 prefix is generated.  If
+you uses bgpd for BGP-4+ please update to this version.  The bug is
+introduced in zebra-0.82.
+
+** When bgpd send Notify message, don't use thread manager.  It is now
+send to neighbor immediately.
+
+* Changes in zebra-0.82
+
+** Solaris 2.6 support is added by Michael Handler
+<handler@sub-rosa.com>.
+
+** MBGP support is added by Robert Olsson <Robert.Olsson@data.slu.se>.
+Please specify --enable-mbgp to configure script.  This option will be
+removed in the future and MBGP support will be default.
+
+* Changes in zebra
+
+** When interface goes down, withdraw connected routes from routing
+table.  When interface goes up, restore the routes to the routing
+table.
+
+** `show interface' show interface's statistics on Linux and BSD with
+routing socket.
+
+** Now zebra can get MTU value on BSDI/OS.
+
+* Changes in bgpd
+
+** Add capability option support based upon
+draft-ietf-idr-bgp4-cap-neg-04.txt.
+
+** Add `show ipv6 bgp prefix-list' command.
+
+** Check self AS appeared in received routes.
+
+** redistribute route-map support is added.
+
+** BGP packet dump feature compatible with MRT.
+
+* Changes in ripd
+
+** Fix bug of `timers basic' command's argument format.
+
+* Changes in ripngd
+
+** Calculate max RTE using interface's MTU value.
+
+* Changes in ospfd
+
+** Some correction to LSU processing.
+
+** Add check for lsa->refresh_list.
+
+* Changes in ospf6d
+
+** Many debug feature is added.
+
+* Changes in zebra-0.81
+
+** SNMP support is disabled in default.--enable-snmp option is added
+to configure script.
+
+* Changes in bgpd
+
+** Fix FSM bug which introduced in zebra-0.80.
+
+* Changes in zebra-0.80
+
+* access-list
+
+  New access-list name space `ipv6 access-list' is added.  At the same
+  time, `access-list' statemant only accepts IPv4 prefix.  Please be
+  careful if you use IPv6 filtering.  You will need to change your
+  configuration.  For IPv6 filtering please use `ipv6 access-list'.
+  
+  As of zebra-0.7x, user can use `access-list' for both IPv4 and IPv6
+  filtering.
+  
+  ! zebra-0.7x
+  access-list DML-net permit 203.181.89.0/24
+  access-list DML-net permit 3ffe:506::0/32
+  access-list DML-net deny any
+  !
+  
+  Above configuration is not valid for zebra-08x.  Please add `ipv6'
+  before 'access-list' when you configure IPv6 filtering.
+  
+  ! zebra-0.8x
+  access-list DML-net permit 203.181.89.0/24
+  access-list DML-net deny any
+  !
+  ipv6 access-list DML-net permit 3ffe:506::0/32
+  ipv6 access-list DML-net deny any
+  !
+
+* prefix-list
+
+  And also new prefix-list name space `ipv6 prefix-list' is added.  It
+  is the same as the change of `access-list'. `ip prefix-list' now only
+  accept IPv4 prefix.  It was source of confusion that `ip prefix-list'
+  can be used both IPv4 and IPv6 filtering.  Now name space is separated
+  to clear the meaning of the filter.
+  
+  If you use `ip prefix-list' for IPv6 filtering, please change the
+  stetement.
+  
+  ! zebra-0.7x
+  ip prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24
+  ip prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28
+  ip prefix-list 6bone-filter seq 12 deny 3ffe::/16
+  ip prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16
+  ip prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35
+  ip prefix-list 6bone-filter seq 30 deny any
+  !
+  
+  Now user can explicitly configure it as IPv6 prefix-list.
+  
+  ! zebra-0.8x
+  ipv6 prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24
+  ipv6 prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28
+  ipv6 prefix-list 6bone-filter seq 12 deny 3ffe::/16
+  ipv6 prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16
+  ipv6 prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35
+  ipv6 prefix-list 6bone-filter seq 30 deny any
+  !
+
+* RIP configuration
+
+  If you want to filter only default route (0.0.0.0/0) and permit other
+  routes, it was hard to do that.  Now `ip prefix-list' can be used for
+  RIP route filtering.
+  
+  New statement:
+  
+  `distribute-list prefix PLIST_NAME (in|out) IFNAME'
+  
+  is added to ripd.  So you can configure on eth0 interface accept all
+  routes other than default routes.
+  
+  !
+  router rip
+   distribute-list prefix filter-default in eth0
+  !
+  ip prefix-list filter-default deny 0.0.0.0/0 le 0
+  ip prefix-list filter-default permit any
+  !
+
+* RIPng configuration
+
+  Same change is done for ripngd.  You can use `ipv6 prefix-list' for
+  filtering.
+  
+  !
+  router ripng
+   distribute-list prefix filter-default in eth0
+  !
+  ipv6 prefix-list filter-default deny ::/0 le 0
+  ipv6 prefix-list filter-default permit any
+  !
+
+* BGP configuration
+
+  So far, Multiprotocol Extensions for BGP-4 (RFC2283) configuration is
+  done with traditional IPv4 peering statement like blow.
+  
+  !
+  router bgp 7675
+   neighbor 3ffe:506::1 remote-as 2500
+   neighbor 3ffe:506::1 prefix-list 6bone-filter out
+  !
+  
+  For separating configuration IPv4 and IPv6, and for retaining Cisco
+  configuration compatibility, now IPv6 configuration is done by IPv6
+  specific statement.  IPv6 BGP configuration is done by statement which
+  start from `ipv6 bgp'.
+  
+  !
+  router bgp 7675
+  !
+  ipv6 bgp neighbor 3ffe:506::1 remote-as 2500
+  ipv6 bgp neighbor 3ffe:506::1 prefix-list 6bone-filter out
+  !
+  
+  At the same time some IPv6 specific commands are deleted from IPv4
+  configuration.
+  
+  o redistribute ripng
+  o redistribute ospf6
+  o neighbor PEER version BGP_VERSION
+  o neighbor PEER interface IFNAME
+  
+  Those commands are only accepted as like below.
+  
+  o ipv6 bgp redistribute ripng
+  o ipv6 bgp redistribute ospf6
+  o ipv6 bgp neighbor PEER version BGP_VERSION
+  o ipv6 bgp neighbor PEER interface IFNAME
+  
+  And below new commands are added.
+  
+  o ipv6 bgp network IPV6_PREFIX
+  o ipv6 bgp redistribute static
+  o ipv6 bgp redistribute connected
+  o ipv6 bgp neighbor PEER remote-as <1-65535> [passive]
+  o ipv6 bgp neighbor PEER ebgp-multihop [TTL]
+  o ipv6 bgp neighbor PEER description DESCRIPTION
+  o ipv6 bgp neighbor PEER shutdown
+  o ipv6 bgp neighbor PEER route-reflector-client
+  o ipv6 bgp neighbor PEER update-source IFNAME
+  o ipv6 bgp neighbor PEER next-hop-self
+  o ipv6 bgp neighbor PEER timers holdtime <0-65535>
+  o ipv6 bgp neighbor PEER timers keepalive <0-65535>
+  o ipv6 bgp neighbor PEER send-community
+  o ipv6 bgp neighbor PEER weight <0-65535>
+  o ipv6 bgp neighbor PEER default-originate
+  o ipv6 bgp neighbor PEER filter-list FILTER_LIST_NAME (in|out)
+  o ipv6 bgp neighbor PEER prefix-list PREFIX_LIST_NAME (in|out)
+  o ipv6 bgp neighbor PEER distribute-list AS_LIST_NAME (in|out)
+  o ipv6 bgp neighbor PEER route-map ROUTE_MAP_NAME (in|out)
+  
+  And some utility commands are introduced.
+  
+  o clear ipv6 bgp [PEER]
+  o show ipv6 bgp neighbors [PEER]
+  o show ipv6 bgp summary
+  
+  I hope these changes are easy to understand for current Zebra users...
+
+* To restrict connection to VTY interface.
+
+  It used to be both IPv4 and IPv6 filter can be specified with one
+  access-list.  Then the access-list can be appried to VTY interface
+  with `access-class' stetement in `line vty' node.  Below is example in
+  zebra-0.7x.
+  
+  !
+  access-list local-only permit 127.0.0.1/32
+  access-list local-only permit ::1/128
+  access-list local-only deny any
+  !
+  line vty
+   access-class local-only
+  !
+  
+  Now IPv4 and IPv6 filter have each name space.  It is not possible to
+  specify IPv4 and IPv6 filter with one access-list.  For setting IPv6
+  access-list in `line vty', `ipv6 access-class' statement is
+  introduced.  Let me show the configuration in zebra-0.8x.
+  
+  !
+  access-list local-only permit 127.0.0.1/32
+  access-list local-only deny any
+  !
+  ipv6 access-list local-only permit ::1/128
+  ipv6 access-list local-only dny any
+  !
+  line vty
+   access-class local-only
+   ipv6 access-class local-only
+  !
+
+* route-map
+
+  New IPv6 related route-map match commands are added.
+  
+  o match ipv6 address
+  o match ipv6 next-hop
+  
+  Please change your configuration if you use IP match statement for
+  IPv6 route.
+  
+  zebra-0.7x config
+  =================
+  !
+  access-list all permit any
+  !
+  route-map set-nexthop permit 10
+   match ip address all
+   set ipv6 next-hop global 3ffe:506::1
+   set ipv6 next-hop local fe80::cbb5:591a
+  !
+  
+  zebra-0.8x config
+  =================
+  !
+  ipv6 access-list all permit any
+  !
+  route-map set-nexthop permit 10
+   match ipv6 address all
+   set ipv6 next-hop global 3ffe:506::1
+   set ipv6 next-hop local fe80::cbb5:591a
+  !
+
+* zebra connection
+
+  Protocol daemon such as ripd, bgpd, ospfd will reconnect zebra daemon
+  when the connection fail.  Those daemons try to connect zebra every 10
+  seconds first three trial, then the interval changed to 60 seconds.
+  After all, if ten connections are fail, protocol daemon give up the
+  connection to the zebra daemon.
+
+* SNMP support (is not yet finished)
+
+  Zebra uses SMUX protocol (RFC1227) for making communication with SNMP
+  agent.  Currently lib/smux.c can be compiled only with ucd-snmp-4.0.1
+  and http://ucd-snmp.ucdavis.edu/patches/012.patch.  It can not be
+  compiled with ucd-snmp-3.6.2.
+  
+  After applying the patch to ucd-snmp-4.0.1, please configure it with
+  SMUX module.
+  
+  % configure --with-mib-modules=smux
+  
+  After compile & install ucd-snmp-4.0.1, you will need to configure
+  smuxpeer.  I'm now using below configuration.
+  
+  /usr/local/share/snmp/snmpd.conf
+  ================================
+  smuxpeer 1.3.6.1.6.3.1 test
+  
+  Above 1.3.6.1.6.3.1 and test is temporary configuration which is hard
+  coded in lib/smux.c. Yes, I know it is bad, I'll change it ASAP.
+
+* HUP signal treatment
+
+  From zebra-0.80, ripd will reload it's configuration file when ripd
+  receives HUP signal.  Other daemon such as bgpd, ospfd will support
+  HUP signal treatment soon.
+
+* Changes in zebra-0.79
+
+* Changes in zebra
+
+** Broadcast address setting on Linux box bug is fixed.
+
+** Protocol daemon can install connected IPv6 route into the kernel.
+
+** Now zebra can handle blackhole route.
+
+* Changes in ripd
+
+** Add route-map feature for RIP protocol.
+
+** In case of RIP version 2 routing table entry has IPv4 address and
+netmask pair which host part bit is on, ignore the entry.
+
+* Changes in ripngd
+
+** Change CMSG_DATA cast from (u_char *) to (int *).  (u_char *) does
+not work for NetBSD-currnet on SparcStation 10.
+
+* Changes in ospfd
+
+** MaxAge LSA treatment is added.
+
+** ABR/ASBR functionality is added.
+
+** Virtual Link funtionality is added.
+
+** ABR behaviors IBM/Cisco/Shortcut is added.
+
+* Changes in ospf6d
+
+** Enclosed KAME specific part with #ifdef #endif
+
+* Changes in zebra-0.78
+
+* Changes in lib
+
+** SNMP support is started.
+
+** Now Zebra can work on BSD/OS 4.X.
+
+** Now Zebra can compiled on vanilla OpenBSD 2.5 but not yet working correcltly.
+
+* Changes in zebra
+
+** Interface index detection using ioctl() bug is fixed.
+
+** Interface information protocol is changed.  Now interface
+addition/deletion and interface's address addition/deletion is
+separated.
+
+* Changes in bgpd
+
+** BGP hold timer bug is fixed.
+
+** BGP keepavlie timer becomes configurable.
+
+* Changes in ripd
+
+** When making reply to rip's REQUEST message, fill in
+RIP_METRIC_INFINITY with network byte order using htonl ().
+
+** Pass host byte order address to IN_CLASSC and IN_CLASSB macro.
+
+* Changes in ospfd
+
+** LSA flooding works.
+
+** Fix bug of DD processing.
+
+** Fix bug of originating router-LSA bug is fixed.
+
+** LSA structure is changed to support LSA aging.
+
+* Changes in ospf6d
+
+** `ip6' statement in configuration is changed to `ipv6'.
+
+* Changes in zebra-0.77
+
+* Changes in lib
+
+** SIGUSR1 reopen logging file.
+
+** route-map is extended to support multi-protocol routing
+information.
+
+** When compiling under GNU libc 2.1 environment don't use inet6-apps.
+
+* Changes in zebra
+
+** Basic IPv6 router advertisement codes added.  It is not yet usable.
+
+** Fix IPv6 route addition/deletion bug is fixed.
+
+** `show ip route A.B.C.D' works
+
+* Changes in bgpd
+
+** When invalid unfeasible routes length comes, bgpd send notify then
+continue to process the packet.  Now bgpd stop parsing invalid packet
+then return to main loop.
+
+** BGP-4+ withdrawn routes parse bug is fixed.
+
+** When BGP-4+ information passed to non shared network's peer, trim
+link-local next-hop information.
+
+** `no redistribute ROUTE_TYPE' withdraw installed routes from BGP
+routing information.
+
+** `show ipv6 route IPV6ADDR' command added.
+
+** BGP start timer has jitter.
+
+** Holdtimer configuration bug is fixed.  Now configuration does not
+show unconfigured hold time value.
+
+* Changes in ripngd
+
+** Now update timer (default 30 seconds) has +/- 50% jitter value.
+
+** Add timers basic command.
+
+** `network' configuration is dynamically reflected.
+
+** `timers basic <update> <timeout> <garbage>' added.
+
+* Changes in ripd
+
+** Reconstruct almost codes.
+
+** `network' configuration is dynamically reflected.
+
+** RIP timers now conforms to RFC2453.  So user can configure update,
+timeout, garbage timer.
+
+** `timers basic <update> <timeout> <garbage>' works.
+
+* Changes in ospfd
+
+** Bug of originating network LSA is fixed.
+
+** `no router ospf' core dump bug is fixed.
+
+* Changes in ospf6d
+
+** Redistribute route works.
+
+* Changes in zebra-0.76
+
+* Changes in lib
+
+** configure.in Linux IPv6 detection problem is fixed.
+
+** Include SERVICES file to the distribution
+
+** Update zebra.texi to zebra-0.76.
+
+* Changes in zebra-0.75
+
+* Changes in lib
+
+** `termnal length 0' bug is fixed.
+
+* Changes in zebra
+
+** When zebra starts up, sweep all zebra installed routes.  If -k or
+--keep_kernel option is specified to zebra dameon.  This function is
+not performed.
+
+* Changes in ripngd
+
+** Aggreagte address command supported.  In router ripngd,
+`aggregate-address IPV6PREFIX' works.
+
+* Changes in bgpd
+
+** Input route-map's bug which cause segmentation violation is fixed.
+
+** route-map method improved.
+
+** BGP-4+ nexthop detection improved.
+
+** BGP-4+ route re-selection bug is fixed.
+
+** BGP-4+ iBGP route's nexthop calculation works.
+
+** After connection Established `show ip bgp neighbor' display BGP TCP
+connection's source and destination address.
+
+** In case of BGP-4+ `show ip bgp neighbor' display BGP-4+ global and
+local nexthop which used for originated route.  This address will be
+used when `next-hop-self'.
+
+* Changes in ospfd
+
+** Fix bug of DR election.
+
+** Set IP precedence field with IPTOS_PREC_INTERNET_CONTROL.
+
+** Schedule NeighborChange event if NSM status change.
+
+** Never include a neighbor in Hello packet, when the neighbor goes
+down.
+
+* Changes in zebra-0.74
+
+* Changes in lib
+
+** Now `terminal length 0' means no line output control.
+
+** `line LINES' command deleted.  Instead of this please use `terminal
+length <0-512>'.
+
+** `terminal length <0-512>' is each vty specific configuration so it
+can not be configured in the configuration file.  If you want to
+configure system wide line control, please use `service
+terminal-length <0-512>'.  This configuration affects to the all vty
+interface.
+
+* Changes in zebra
+
+** Installation of IPv6 route bug is fixed.
+
+* Changes in bgpd
+
+** Very serious bug of bgp_stop () is fixed. When multiple route to
+the same destination exist, bgpd try to announce the information to
+stopped peer.  Then add orphan write thread is added.  This cause
+many strange behavior of bgpd.
+
+** Router-id parsing bug is fixed.
+
+** With BGP-4+ nexthop installation was done with global address but
+it should be link-local address.  This bug is fixed now.
+
+** When incoming route-map prepend AS, old AS path remained.  Now bgpd
+free old AS path.
+
+** `neighbor PEER weight <0-65535>' command added.
+
+* Changes in ripngd
+
+** Almost codes are rewritten to conform to RFC2080.
+
+* Changes in ospfd
+
+** SPF calculation timer is added.  Currently it is set to 30 seconds.
+
+** SPF calculation works now.
+
+** OSPF routing table codes are added.
+
+** OSPF's internal routes installed into the kernel routing table.
+
+** Now `ospfd' works as non-area, non-external route support OSPF
+router.
+
+** Call of log_rotate() is removed.
+
+* Changes in ospf6d
+
+** LSA data structure is changed.
+
+** Call of log_rotate() is removed.
+
+* Changes in zebra-0.73
+
+* Changes in lib
+
+** `config terminal' is changed to `configure terminal'.
+
+** `terminal length <0-512>' command is added.
+
+** Variable length argument was specified by `...'.  Now all strings
+started with character `.' is variable length argument.
+
+* Changes in zebra
+
+** Internal route (such as iBGP, internal OSPF route) handling works
+correctly.
+
+** In interface node, `ipv6 address' and `no ipv6 address' works.
+
+** Interface's address remain after `no ip address' bug is fixed.
+
+** Host route such as IPv4 with /32 mask and IPv6 with /128 mask
+didn't set RTF_GATEWAY even it has gateway.  This bug if fixed now.
+
+* Changes in bgpd
+
+** `match as-path' argument is used to be specify AS PATH value itself
+directly (e.g. ^$).  But it is changed to specify `ip as-apth
+access-list' name.
+
+** iBGP route handle works without getting error from the kernel.
+
+** `set aggregator as AS A.B.C.D' command is added to route-map.
+
+** `set atomic-aggregate' command is added to bgpd's routemap.
+
+** Announcement of atomic aggregate attribute and aggregator attribute
+works.
+
+** `update-source' bug is fixed.
+
+** When a route learned from eBGP is announced to iBGP, local
+preference was set to zero.  But now it set to
+DEFAULT_LOCAL_PREF(100).
+
+* Changes in ripd
+
+** RIPv1 route filter bug is fixed.
+
+** Some memory leak is fixed.
+
+* Changes in ospfd
+
+** Fix bug of DR Election.
+
+** Fix bug of adjacency forming.
+
+* Changes in ospf6d
+
+** Clean up logging message.
+
+** Reflect routing information to zebra daemon.
+
+* Changes in zebra-0.72
+
+* Changes in lib
+
+** When getsockname return IPv4 mapped IPv6 address.  Convert it to
+IPv4 address.
+
+* Changes in bgpd
+
+** Change route-map's next-hop related settings.
+
+set ip nexthop          -> set ip next-hop
+set ipv6 nexthop global -> set ipv6 next-hop global
+set ipv6 nexthop local  -> set ipv6 next-hop local
+
+** Add `next-hop-self' command.
+
+* Changes in ospfd
+
+** Fix bug of multiple `network area' directive crashes.
+
+* Changes in zebra-0.71
+
+* Changes in lib
+
+** `log syslog' command is added.
+
+** Use getaddrinfo function to bind IPv4/IPv6 server socket.
+
+** `no banner motd' will suppress motd output when user connect to VTY.
+
+** Bind `quit' command to major nodes.
+
+* Changes in zebra
+
+** Point-to-point link address handling bug is fixed.
+
+* Changes in bgpd
+
+** AS path validity check is added.  If malformed AS path is received
+NOTIFY Malformed AS path is send to the peer.
+
+** Use getaddrinfo function to bind IPv4/IPv6 server socket.
+
+* Changes in ripd
+
+** Connected network announcement bug is fixed.
+
+** `broadcast' command is deleted.
+
+** `network' command is added.
+
+** `neighbor' command is added.
+
+** `redistribute' command is added.
+
+** `timers basic' command is added.
+
+** `route' command is added.
+
+* Changes in ripngd
+
+** Fix metric calculation bug.
+
+* Changes in ospfd
+
+** Check sum bug is fixed.
+
+* Chanegs in ospf6d
+
+** Routing table code is rewritten.
+
+* Changes in zebra-0.70
+
+* Changes in zebra
+
+** Critical routing information base calculation bug check is fixed.
+
+** zebra ipv4 message is extended to support external/internal route
+flavor.
+
+** Now if internal route doesn't has direct connected nexthop, then
+nexthop is calculated by looking up IGP routing table.
+
+* Changes in bgpd
+
+** `neighbor PEER update-source IFNAME' command added as ALIAS to
+`neighbor PEER interface IFNAME'.
+
+* Changes in ospfd
+
+** DD null pointer bug is fixed.
+
+* Changes in zebra-0.69
+
+* Changes in zebra
+
+** zebra redistirbution supports dynamic notification of the route
+change.  If you add static route while running zebra, it will be
+reflected to other protocol daemon which set `redistribute static'.
+
+** If static route installation is failed due to the error.  The
+static route is not added to the configuration and zebra routing
+table.
+
+** zebra sets forwarding flag to on when it starts up.
+
+** `no ip forwarding' turn off IPv4 forwarding.
+
+** `no ipv6 forwarding' turn off IPv6 forwarding.
+
+** Change `show ipforward' command to `show ip forwarding'.
+
+** Change `show ipv6forward' command to `show ipv6 forwarding'.
+
+** `ip route A.B.C.D/M INTERFACE' works.  So you can set `ip route
+10.0.0.0/8 eth0'.
+
+* Changes in bgpd
+
+** `neighbor PEER send-community' command is added.  If the option is
+set, bgpd will send community attribute to the peer.
+
+** When a BGP route has no-export community attribute and
+send-community is set to the peer, the route is not announced to the
+peer.
+
+* Changes in ripngd
+
+** When ripngd terminates, delete all installed route.
+
+** `redistribute static', `redistribute connected' works.
+
+** Change `debug ripng event' to `debug ripng events'.
+
+** Change `show debug ripng' to `show debugging ripng'.
+
+** Bug of static route deletion is fixed.
+
+* Changes in ospfd
+
+** LS request and LS update can be send and received.
+
+* Changes in zebra-0.68
+
+* Changes in lib
+
+** DEFUN() is extended to support (a|b|c) statement.
+
+** Input buffer overflow bug is fixed.
+
+* Changes in bgpd
+
+** `ip community-list' is added.
+
+** set community and match community is added to route-map statement.
+
+** aggregate-address A.B.C.D/M partly works.  Now it works only
+summary-only mode.
+
+* Changes in zebra
+
+** IPv6 network address delete bug is fixed.
+
+* Changes in ospfd
+
+** DR election bug fixed.
+
+** Now Database Description can be send or received.
+
+** Neighbor State Machine goes to Full state.
+
+* Changes in ospf6d
+
+** router zebra related bug is fixed.
+
+* Changes in zebra-0.67
+
+* Changes in lib
+
+** `service password-encryption' is added for encrypted password.
+
+* Changes in bgpd
+
+** `set as-path prepend ASPATH' is added to route-map command.
+
+** `set weight WEIGHT' is added to route-map command.
+
+** `no set ipv6 nexthop global' and `no set ipv6 nexthop local'
+command is added to route-map.
+
+** `neighbor IP_ADDR version BGP_VERSION' command's BGP_VERSION
+argument changed.
+
+Old               New
+=====================
+bgp4              4
+bgp4+             4+
+bgp4+-draft-00    4-
+=====================
+
+If you want to peer with old draft version of BGP-4+, please configure
+like below:
+
+router bgp ASN
+ neighbor PEER version 4-
+
+** Some AS path isn't correctly compared during route selection.  Now
+it is fixed.
+
+* Changes in ospfd
+
+** `router zebra' is default behavior.
+
+* Changes in ospf6d
+
+** `router zebra' is default behavior.
+
+* Changes in zebra-0.66
+
+* Changes in zebra
+
+** When other daemon such as gated install routes into the kernel then
+zebra blocks.  This is only occur with netlink socket.  Now socket is
+set as NONBLOCKING and problem is fixed.  Reported and fixed by
+Patrick Koppen <koppen@rhrk.uni-kl.de>
+
+* Changes in bgpd
+
+** Now `router zebra' is not needed to insert BGP routes into the
+kernel.  It is default behavior.  If you don't want to install the BGP
+routes to the kernel, please configure like below:
+
+!
+router zebra
+ no redistribute bgp
+!
+
+** redistribute connected works.
+
+** redistribute static now filter local loopback routes and link local
+network.
+
+* Changes in ripd
+
+** Some network check is added.  Patch is done by Carlos Alberto
+Barcenilla <barce@frlp.utn.edu.ar>
+
+* Changes in ripngd
+
+** Sometimes ripngd install wrong nexthop into the kernel.  This bug
+is fixed now.
+
+** Now `router zebra' is not needed to insert RIPng routes into the
+kernel.  It is default behavior. If you don't want to install the BGP
+routes to the kernel, please configure like below:
+
+!
+router zebra
+ no redistribute ripng
+!
+
+* Changes in zebra-0.65
+
+* Changes in lib
+
+** `C-c' changes current node to ENABLE_NODE.  Previously it doesn't.
+
+** In ENABLE_NODE, `exit' command close vty connection.
+
+** `service advanced-vty' enable advanced vty function.  If this
+service is specified one can directly connect to ENABLE_NODE when
+enable password is not set.
+
+** `lines LINES' command is added by Stephen R. van den Berg
+<srb@cuci.nl>.
+
+* Changes in zebra
+
+** Basic Linux policy based routing table support is added by Stephen
+R. van den Berg <srb@cuci.nl>.
+
+* Changes in bgpd
+
+** route-map command is improved:
+  `match ip next-hop': New command.
+  `match metric': New command.
+  `set metric': Doc fixed.
+  `set local-preference': DEFUN added.
+
+* Changes in ripd
+
+** Check of announced network is added.  Now multicast address is
+filtered.  Reported by Carlos Alberto Barcenilla
+<barce@frlp.utn.edu.ar>
+
+** Check of network 127 is added.  Reported by Carlos Alberto
+Barcenilla <barce@frlp.utn.edu.ar>
+
+* Changes in ripngd
+
+** Aging route bug is fixed.
+
+** `router zebra' semantics changed.  ripngd automatically connect to
+zebra.
+
+* Changes in ospfd
+
+** `no router ospf' works.
+
+* Changes in ospf6d
+
+** Bug fix about network vertex.
+
+* Changes in zebra-0.64.1.
+
+This is bug fix release.
+
+* Changes in lib
+
+** Add check of sin6_scope_id in struct sockaddr_in6.  For compilation
+on implementation which doesn't have sin6_scope_id.  Reported by Wim
+Biemolt <Wim.Biemolt@ipv6.surfnet.nl>.
+
+* Changes in zebra
+
+** Fix bug of display BGP routes as "O" instead of "B".  Reported by
+"William F. Maton" <wmaton@enterprise.ic.gc.ca> and Dave Hartzell
+<hartzell@greatplains.net>.
+
+* Changes in bgpd
+
+** `no network IPV6_NETWORK' statement and `no neighbor IP_ADDR timers
+holdtime [TIMER]' statement doesn't work. Reported by Georg Hitsch
+<georg@atnet.at>.  Now both statement work.
+
+* Changes in ospfd
+
+** Last interface is not updated by ospf_if_update().  Reported by
+Dave Hartzell <hartzell@greatplains.net>.
+
+* Changes in ospf6d
+
+** Byte order of ifid is changed.  Due to this change, this code will
+not work with previous version, sorry.
+
+** Fix `show ip route' route type mismatch.
+
+** Fix bug of no network IPV6_NETWORK.
+
+** Important bug fix about intra-area-prefix-lsa.
+
+* Changes in zebra-0.64.
+
+* Changes in lib
+
+** prefix-list based filtering routine is added.  Currently used in
+bgpd but it will be in other daemons.
+
+* Changes in bgpd
+
+** `no router bgp' works.  But network statement is not cleared.  This
+should be fixed in next beta.
+
+** Route reflector related statement is added.
+
+  router bgp ASN
+    bgp cluster-id a.b.c.d
+    neighbor a.b.c.d route-reflector-client
+
+  is added.
+
+** Prefix list based filtering is added.
+
+  router bgp ASN
+    neighbor a.b.c.d prefix-list PREFIX_LIST_NAME
+
+** Prefix list based routing display works.
+
+  show ip bgp prefix-list PREFIX_LIST_NAME
+
+* Changes in ripd
+
+** Fix route metric check bug.  Reported from Mr. Carlos Alberto
+Barcenilla.
+
+* Changes in ospf6d
+
+** There are many changes.  If you have interested in ospf6d please
+visit ospf6d/README file.
+
+* Changes in zebra-0.63 first beta package.
+
+* Changes in lib
+
+** `copy running-config stgartup-config' command is added.
+
+** prefix length check bug is fixed.  Thanks Marlos Barcenilla
+<barce@frip.utn.edu.ar>.
+
+* Changes in ospfd
+
+** DR and BDR election works.
+
+** OSPF Hello simple authentication works.
+
+* Changes in ospf6d
+
+** Now ospf6d can be compiled on both Linux and *BSD system.
+
+* Changes in zebra-19990420 snapshot
+
+** `make dist' at top directory works now.
+
+* Changes in lib
+
+** VTY has now access-class to restrict remote connection.
+Implemented by Alex Bligh <amb@gxn.net>.
+
+!
+line vty
+  access-class ACCESS-LIST-NAME
+!
+
+** `show version' command added.  Implemented by Carlos Alberto
+Barcenilla <barce@frlp.utn.edu.ar>
+
+* Changes in zebra
+
+** `ip address' command on *BSD bug is fixed.
+
+** `no ip address' works now for IPv4 address.
+
+** Now `write terminal' display `ip address' configuration.
+
+* Changes in bgpd
+
+** Redistribute static works now.  Please run both zebra and bgpd.
+bgpd.conf should be like this:
+
+!
+router zebra
+!
+router bgp ASN
+  redisitribute static
+!
+
+* Changes in guile
+
+** configure --enable-guile turns on zebra-guile build.
+
+** (router-bgp ASN) allocates real bgp structre.
+
+* Changes in zebra-19990416 snapshot
+
+** Set version to 0.60 for preparation of beta release.
+
+** New directory guile is added for linking with guile interpreter.
+
+* Changes in zebra
+
+** On GNU/Linux Kernel 2.2.x (with netlink support), zebra detects
+asynchronous routing updates.  *BSD support is not yet finished.
+
+* Changes in bgpd
+
+** `show ip bgp regexp ASPATH_REGEX' uses CISCO like regular expression 
+instead of RPSL like regular expression.  I'm planing to provide RPSL
+like regular expression with `show ip bgp rpsl' or something.
+
+* Changes in lib
+
+** Press '?' at variable mandatory argument, vty prints nothing.  Now
+vty outputs description about the argument.  Fixed by Alex Bligh
+<amb@gxn.net>
+
+** buffer.c has some ugly bugs.  Due to the bug, vty interface hangs
+when large output date exists.  This bug is fixed. Reported by Alex
+Bligh <amb@gxn.net>.
+
+* Changes in ospfd
+
+** DR and BDR information is shown by `show ip ospf interface' command.
+
+* Changes in zebra-19990408 snapshot
+
+* Changes in bgpd
+
+** Old BGP-4+ specification (described in old draft) treatment bug is
+fixed.  It seems that mrtd uses this format as default.  So if you
+have problem peering with mrtd and want to use old draft format please
+use version statement like this.
+
+neighbor PEER_ADDRESS remote-as ASN
+neighbor PEER_ADDRESS version bgp4+-draft-00
+
+** When AS path is epmty (routes generated by bgpd), SEGV is occur
+when announce the routes to eBGP peer.  Reported by
+kad@gibson.skif.net.
+
+** ip as-path access-list command is added.
+
+** neighbor PEER_ADDRESS filter-list AS_LIST [in|out] command is added.
+
+** neighbor PEER_ADDRESS timers holdtimer TIMER command is added.
+
+* Changes in all daemons
+
+** With KAME stack, terminal interface is now bind AF_INET socket
+instead of AF_INET6 one.
+
+* Changes in zebra-19990403 snapshot
+
+* Changes in bgpd
+
+** When bgpd has 'router zebra', bgpd automatically select it's router
+ID as most highest interface's IP Address.
+
+** When AS path is empty (in case of iBGP), it doesn't include any AS
+segment.  This change is for announcement to gated under iBGP.
+
+* Changes in ospfd
+
+** OSPF hello packet send/receive works.
+
+* Changes in ospf6d
+
+** Yasuhiro Ohara's ospf6d codes is imported.  It is under development
+and can't be compiled on any platform.
+
+* Changes in zebra-19990327 snapshot
+
+* Changes in bgpd
+
+** When BGP-4+ connection is done by IPv6 link-local address.  One
+have to specify interface index for the connection.  So I've added
+interface statement to the neighbor commmand.  Please specify
+interface name for getting interface index like below.  This statement
+only works on GNU/Linux.  I'll support BSD ASAP.
+
+router bgp 7675
+ neighbor fe80::200:f8ff:fe01:5fd3 remote-as 2500
+ neighbor fe80::200:f8ff:fe01:5fd3 interface sit3
+
+** For disable BGP peering `shutdown' command is added.
+
+router bgp 7675
+ neighbor 10.0.0.1 shutdown
+
+** `description' command is added to neighbor statement.
+
+router bgp 7675
+ neighbor 10.0.0.1 description peering with Norway.
+
+** `show ip bgp regexp AS-REGEXP' works again.
+
+show ip bgp regexp AS7675
+
+will show routes which include AS7675.
+
+** When a route which is made from `network' statement is send to
+neighbor.  Set it's nexthop to self.  So 10.0.0.0/8 is announced to
+the peer A with source address 192.168.1.1.  The routes nexthop is set
+to 192.168.1.1.
+
+* Changes in zebra
+
+** In zebra/rtread_sysctl.c, function rtm_read() may overrun allocated
+buffer when the address family is not supported and the length is big
+(i.e link address).  Reported Achim Patzner <ap@bnc.net>.
+
+* Changes in ospfd
+
+** Now ospfd receive OSPF packet.
+
+* Changes in zebra-19990319 snapshot
+
+* Changes in configuration and libraries
+
+** User can disable IPv6 feature and/or pthread feature by configure
+   option.
+
+  To disable IPv6:    configure --disable-ipv6
+  To disable pthread: configure --disable-pthread
+
+** User can disable specified daemon by configure option.
+
+  Don't make zebra:  configure --disable-zebra
+  Don't make bgpd:   configure --disable-bgpd
+  Don't make ripd:   configure --disable-ripd
+  Don't make ripngd: configure --disable-ripngd
+  Don't make ospfd:  configure --disable-ospfd
+  Don't make ospf6d: configure --disable-ospf6d
+
+** Sample configuration files are installed as 600 file flag.
+   Suggested by Jeroen Ruigrok/Asmodai <asmodai@wxs.nl>.
+
+** syslog logging feature is added by Peter Galbavy
+   <Peter.Galbavy@knowledge.com>
+
+** Inclusion of standard header files is reworked by Peter Galbavy
+   <Peter.Galbavy@knowledge.com>
+
+** Change description from GNU/Linux 2.1.X to GNU/Linux 2.2.X
+
+** If daemon function exists in standard C library use it.
+
+** To generate configure script we upgrade autoconf to 2.13.  To
+generate Makefile.in we upgrade automake to 1.4.
+
+** doc/texinfo.tex is added to distribution.
+
+** Update ports/pkg/DESCR description.
+
+** Update doc/zebra.texi.
+
+** logfile FILENAME statement deleted.  Instead of that please use log
+file FILENAME.
+
+* Changes in zebra
+
+* Changes in bgpd
+
+** Communication between zebra and bgpd works now.  So if there is
+   `router zebra' line in bgpd.conf, selected route is installed
+   into kernel routing table.
+
+** Delete all routes which inserted by bgpd when bgpd dies.  If you
+want to retain routes even bgpd dies please specify [-r|--retain]
+option to bgpd.
+
+** BGP announcement code is reworked.  Now bgpd announce selected
+   routes to other peer.
+
+** All output bgp packet is buffered.  It's written to the socket when
+   it gets ready.
+
+** Output route-map works now.  You can specify output route-map by:
+
+   neighbor IP_ADDR route-map ROUTE_MAP_NAME out
+
+** New route-map command added.
+
+   set ip nexthop IP_ADDR
+   set ipv6 nexthop global IP_ADDR
+
+** Fix bug about unlock of the route_node structure.
+
+** BGP-4+ support is added.  bgpd can listen and speak BGP-4+ packet
+specified in RFC2283. You can view IPv6 bgp table by: `show ipv6 bgp'.
+
+** Meny packet overflow check is added.
+
+* Changes in ripd
+
+* Changes in ripngd
+
+* Changes in ospfd
+
+** ospfd work is started by Toshiaki Takada <takada@zebra.org>.  Now
+several files are included in ospfd directory.
+
+** ospf6d codes are merged from Yasuhiro Ohara <yasu@sfc.wide.ad.jp>'s
+ospfd work.  Now codes are located in ospf6d directory.
+
+
+Local variables:
+mode: outline
+paragraph-separate: "[         ]*$"
+end:
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..f1b25d8
--- /dev/null
+++ b/README
@@ -0,0 +1,11 @@
+Quagga is free software that manages various IPv4 and IPv6 routing
+protocols.
+
+Currently Quagga supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1,
+RIPv2, and RIPng as well as very early support for IS-IS.
+  
+See the file INSTALL.quagga.txt for building and installation instructions.
+  
+See the file REPORTING-BUGS to report bugs.
+  
+Quagga is free software. See the file COPYING for copying conditions.
diff --git a/README.NetBSD b/README.NetBSD
new file mode 100755 (executable)
index 0000000..6bbc680
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# $QuaggaId: Format:%an, %ai, %h$ $
+
+# This file is helpful for building quagga from cvs on NetBSD, and
+# probably on any system using pkgsrc.  
+# One should have readline installed already (pkgsrc/devel/readline).
+
+MAKE=make
+# Quagga is currently documented not to require GNU make, but sometimes
+# BSD make fails.  Enable this if statement as a workaround.
+if false; then
+    MAKE=gmake
+    echo "WARNING: using gmake to work around nonportable makefiles"
+fi
+
+# Use /usr/quagga to be independent, and /usr/pkg to overwrite pkgsrc.
+PREFIX=/usr/pkg
+
+case $1 in
+
+    build)
+       # Omitted because it is now default:
+       #   --enable-opaque-lsa
+       ./bootstrap.sh
+       LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" CPPFLAGS="-I/usr/pkg/include" \
+       ./configure --prefix=${PREFIX} \
+           --sysconfdir=/etc/zebra --localstatedir=/var/run/zebra \
+           --enable-exampledir=${PREFIX}/share/examples/zebra \
+           --enable-pkgsrcrcdir=${PREFIX}/etc/rc.d \
+           --enable-vtysh
+       ${MAKE}
+       ;;
+
+    install)
+       ${MAKE} install
+       ;;
+
+    clean)
+       ${MAKE} clean
+       ;;
+
+    *)
+       echo "Usage: README.NetBSD (build|install|clean)"
+       exit 1
+       ;;
+
+esac
diff --git a/REPORTING-BUGS b/REPORTING-BUGS
new file mode 100644 (file)
index 0000000..83f4eb9
--- /dev/null
@@ -0,0 +1,34 @@
+This file describes the procedure for reporting Quagga bugs. You are not
+obliged to follow this format, but it would be great help for Quagga developers
+if you report a bug as described below.
+
+Bugs submitted with woefully incomplete information may be summarily
+closed.  Submitters of bugs against old versions may be asked to
+retest against the latest release.  Submitters may be asked for
+additional information.  Bugs may be closed after 30 days of
+non-response to requests to reconfirm or supply additional
+information.
+
+Report bugs http://bugzilla.quagga.net
+
+Please supply the following information:
+1. Your Quagga version or if it is from git then the  commit reference.
+   Please try to report bugs against git master or the latest release.
+2. Quagga daemons you run e.g. bgpd or ripd and full name of your OS. Any
+   specific options you compiled Quagga with. 
+3. Problem description. Copy and paste relative commands and their output to
+   describe your network setup e.g. "zebra>show ip route".
+   Please, also give your simple network layout and output of relative OS
+   commands (e.g., ifconfig (BSD) or ip (Linux)).
+4. All Quagga configuration files you use. If you don't want to publish your
+   network numbers change 2 middle bytes in IPv4 address to be XXX (e.g.
+   192.XXX.XXX.32/24). Similar could be done with IPv6.
+5. If any Quagga daemon core dumped, please, supply stack trace using the
+   following commands: host> gdb exec_file core_file , (gdb) bt .
+6. Run all Quagga daemons with full debugging on (see documentation on
+   debugging) and send _only_ part of logs which are relative to your problem.
+7. If the problem is difficult to reproduce please send a shell script to
+   reproduce it.
+8. Patches, workarounds, fixes are always welcome.
+
+Thank You.
diff --git a/SERVICES b/SERVICES
new file mode 100644 (file)
index 0000000..0322d45
--- /dev/null
+++ b/SERVICES
@@ -0,0 +1,21 @@
+# As long as this software is in alpha testing it is not yet included
+# in /etc/services files. This means that you may need to add the following
+# lines into your /etc/services file on your hosts.
+# 
+# --- Please add this to your /etc/services ---
+
+#
+# GNU Zebra services
+#
+
+zebrasrv       2600/tcp
+zebra          2601/tcp
+ripd           2602/tcp
+ripng          2603/tcp
+ospfd          2604/tcp
+bgpd           2605/tcp
+ospf6d         2606/tcp
+ospfapi                2607/tcp
+isisd          2608/tcp
+pimd           2611/tcp
+nhrpd          2612/tcp
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..246cc25
--- /dev/null
+++ b/TODO
@@ -0,0 +1,209 @@
+
+                          Quagga TODO list
+                             2013-03-29
+
+
+This is the Quagga primary TODO list.  It is on git because that way changes
+pass through the usual process just like code does, therefore they will have
+the same visibility.
+
+If you are working on something beyond a simple fix, to avoid double work it
+is a good idea to submit a patch to this TODO list when you are starting,
+listing what you're doing.  Also, as others may have done just that, check
+the list before starting.
+
+Google Summer of Code 2013 note:  this list double-serves as idea list for the
+Summer of Code.  Ideas considered suitable for students are marked with a star
+after the number, like this: "[Q999*] achieve world peace".  They will also
+have extended descriptions.  Nevertheless, if you'd like to do something else,
+just write a mail to the mailing list: quagga-dev@lists.quagga.net
+
+"GSoC-Mentors:" listings are preliminary at this point.
+
+
+Overall
+=======
+
+[Q000] improve unit test architecture
+
+[Q001] kick invalid runtime tests from configure.ac, use list of supported
+               OSes and their APIs instead.
+       Priority: low
+       State: patch half-done 2013-03-29 David Lamparter
+
+[Q002*] clean up zebra IPC, remove code duplication, align to common API
+       Priority: high
+       GSoC-Mentors: David Lamparter, Christian Franke
+
+       Quagga posesses an IPC mechanism to exchange route information among
+       the different daemons and Zebra, the kernel-interface.  This mechanism
+       is implemented in libzebra, but is currently used in all sorts of
+       different ways in the individual protocol daemons.  Also, in the future
+       the entire protocol needs to be redone in an extensible way, so we're
+       able to support MPLS, BFD, Multi-Topology/Instance, VRFs, ...
+
+       This TODO entry only refers to the first-step API cleanup.  All the
+       daemons need to use a single, well-defined libzebra API.  Only after
+       this has been addressed can we look upon changing the protocol itself,
+       since by then it will be encapsulated inside libzebra.
+
+[Q003] add multi-instance / multi-topology support to the individual protocols
+
+[Q004] MPLS support
+       State: work in progress 2013-03-29 Renato Westphal, Timo Teräs
+
+[Q005] BFD support
+       State: two old implementations exist, contact Hasso Tepper
+
+
+library
+=======
+
+[L000] improve route_table speed, eg strided lookups for common prefix depths.
+
+[L001] ipv6 addresses need concept of valid/preferred
+
+[L002] implement a generic daemon access/control protocol (eg D-Bus like?
+               simplified SNMP-a-like?  NETCONF?)
+
+[L003] extend vty command definitions to allow them to be self-documenting
+               i18n command help strings
+
+[L004] create a common libspf (for ospfd, ospf6d and possibly isisd and more).
+               cf. TODO item [O000] for the ospfd/ospf6d specific variant
+
+[L005] stabilise the API (possibly including symbol/library versioning voodoo)
+
+[L006] Document the exported API (DocBook/Doxygen?)
+
+[LE00] incorporate library changes from Euro-IX branch, except threading
+
+[LE01] incorporate threading library support from Euro-IX branch
+
+
+zebra
+=====
+
+[Z000] Pointopoint address configuration.
+       Priority: low
+       State: patch done & tested 2013-03-29 David Lamparter
+
+[Z001] Add support for valid and preferred lifetimes to IPv6 addresses
+
+[Z002] proper support for (at least) 1-level recursive routes
+       Priority: high
+
+[Z003] Ability to set src on routes, where systems support it.
+
+[Z004] Ability to apply route-maps to daemon route updates.
+
+
+bgpd
+====
+
+[B000] HUP signal support (reload configuration file).
+
+[B001*] BGP multi-path extension, relaxed mode
+       Priority: medium
+       Implemented, patch will be sent shortly
+       Pradosh Mohapatra, Cumulus Networks
+
+[B002] move FSM state to be per-connection, not per-peer.
+
+[B003] Add support for internal and minimum-metric MED setting
+
+
+ripd
+====
+
+[R000] Multipath support.
+
+
+ospfd/ospf6d
+============
+
+[O000] move SPF to common code
+
+[O001] extend code sharing between ospfd and ospf6d beyond SPF
+
+[O002*] OSPF testing replay tool
+       Priority: medium
+       GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter
+
+       In order to extensively test OSPF implementations, a tool to fake an
+       OSPF neighbor is immensely useful.  This tool needs to be capable of
+       forming an adjacency and pushing LSAs to the device to be tested.  To
+       maintain the adjacency, some minimal state tracking is useful.
+
+       In total, the tool needs to form an adjacency, read and push LSAs, and
+       output received LSAs.  Additional tools to generate LSAs from
+       specifications as well as verify received LSA correctness can then be
+       built on top of that.
+
+       The tool needs to support IPv4 and IPv6, possibly split into 2 tools
+       with some code sharing.
+
+ospfd:
+
+[O400] Demand circuits.
+       Priority: very low
+
+[O401] Multiple instances.
+       Priority: medium
+
+[O402] HUP signal treatment.
+       Priority: medium
+       State: patch on ML needs review 2012-06-04 Mattias Walström
+
+ospf6d:
+
+[O600*] fix ospf6d in general
+       Priority: high
+       State: patches tickling in from Cumulus Networks 2013-03-29 Dinesh Dutt
+        Implemented: p2p link support, ABR, Stub area/Totally Stubby area,
+        SPF throttling, Improving state machine to get performance/scale,
+        max-metric support, Improving ECMP to be > 4, Various other bug fixes
+
+
+[O601*] OSPFv3 autoconfiguration, prefix assignment and sourcedest routing
+       Priority: medium
+       State: work in progress 2013-03-29 Edward Seabrook
+       GSoC-Mentors: David Lamparter
+
+       OSPFv3 application in the homenet is being designed to use several
+       extensions to the base protocol.  In order of dependency,
+       autoconfiguration, prefix assignment and sourcedest routing should
+       be implemented.
+
+       This task requires a good level of OSPF understanding plus proper
+       ability to follow IETF discussion about these points.  Also, since work
+       has already started on this, improvements must obviously build on top
+       of that.
+
+isisd
+=====
+
+[I000] reassess isisd TODO
+
+[I001*] IS-IS testing replay tool
+       Priority: medium
+       GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter
+
+       see [O002*].
+
+[I002] Mesh groups (RFC2973)
+
+[I003] Crypto authentication (RFC3567)
+
+
+vtysh
+=====
+
+[V000] untangle readline specific bits
+
+[V001] add a vtyd with a vty (ie telnet) frontend (as opposed to readline)
+
+[V002] (=> [L002]) use daemon control protocol
+
+[V003] better AAA support than just PAM, eg krb5, SASL, LDAP...
+
diff --git a/bgpd/.gitignore b/bgpd/.gitignore
new file mode 100644 (file)
index 0000000..105be22
--- /dev/null
@@ -0,0 +1,18 @@
+Makefile
+Makefile.in
+*.o
+bgpd
+bgp_btoa
+bgpd.conf
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.a
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/bgpd/BGP4-MIB.txt b/bgpd/BGP4-MIB.txt
new file mode 100644 (file)
index 0000000..c911316
--- /dev/null
@@ -0,0 +1,929 @@
+    BGP4-MIB DEFINITIONS ::= BEGIN
+
+        IMPORTS
+            MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE,
+            IpAddress, Integer32, Counter32, Gauge32, mib-2
+                FROM SNMPv2-SMI
+            MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP
+                FROM SNMPv2-CONF;
+
+        bgp MODULE-IDENTITY
+            LAST-UPDATED "9902100000Z"
+            ORGANIZATION "IETF IDR Working Group"
+            CONTACT-INFO "E-mail:  idr@merit.net
+
+                          Susan Hares  (Editor)
+                          Merit Network
+                          4251 Plymouth Road
+                          Suite C
+                          Ann Arbor, MI 48105-2785
+                          Tel: +1 734 936 2095
+                          Fax: +1 734 647 3185
+                          E-mail: skh@merit.edu
+
+                          Jeff Johnson (Editor)
+                          RedBack Networks, Inc.
+                          1389 Moffett Park Drive
+                          Sunnyvale, CA  94089-1134
+                          Tel: +1 408 548 3516
+                          Fax: +1 408 548 3599
+                          E-mail: jeff@redback.com"
+            DESCRIPTION
+                    "The MIB module for BGP-4."
+            REVISION    "9902100000Z"
+            DESCRIPTION
+                    "Corrected duplicate OBJECT IDENTIFIER
+                     assignment in the conformance information."
+            REVISION    "9601080000Z"
+            DESCRIPTION
+                    "1) Fixed the definitions of the traps to
+                     make them equivalent to their initial
+                     definition in RFC 1269.
+                     2) Added compliance and conformance info."
+            ::= { mib-2 15 }
+
+        bgpVersion OBJECT-TYPE
+            SYNTAX     OCTET STRING (SIZE (1..255))
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Vector of supported BGP protocol version
+                    numbers.  Each peer negotiates the version
+                    from this vector.  Versions are identified
+                    via the string of bits contained within this
+                    object.  The first octet contains bits 0 to
+                    7, the second octet contains bits 8 to 15,
+                    and so on, with the most significant bit
+                    referring to the lowest bit number in the
+                    octet (e.g., the MSB of the first octet
+                    refers to bit 0).  If a bit, i, is present
+                    and set, then the version (i+1) of the BGP
+                    is supported."
+            ::= { bgp 1 }
+
+        bgpLocalAs OBJECT-TYPE
+            SYNTAX     INTEGER (0..65535)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The local autonomous system number."
+            ::= { bgp 2 }
+
+
+
+        -- BGP Peer table.  This table contains, one entry per BGP
+        -- peer, information about the BGP peer.
+
+        bgpPeerTable OBJECT-TYPE
+            SYNTAX     SEQUENCE OF BgpPeerEntry
+            MAX-ACCESS not-accessible
+            STATUS     current
+            DESCRIPTION
+                    "BGP peer table.  This table contains,
+                    one entry per BGP peer, information about the
+                    connections with BGP peers."
+            ::= { bgp 3 }
+
+        bgpPeerEntry OBJECT-TYPE
+            SYNTAX     BgpPeerEntry
+            MAX-ACCESS not-accessible
+            STATUS     current
+            DESCRIPTION
+                    "Entry containing information about the
+                    connection with a BGP peer."
+            INDEX { bgpPeerRemoteAddr }
+            ::= { bgpPeerTable 1 }
+
+        BgpPeerEntry ::= SEQUENCE {
+                bgpPeerIdentifier
+                    IpAddress,
+                bgpPeerState
+                    INTEGER,
+                bgpPeerAdminStatus
+                    INTEGER,
+                bgpPeerNegotiatedVersion
+                    Integer32,
+                bgpPeerLocalAddr
+                    IpAddress,
+                bgpPeerLocalPort
+                    INTEGER,
+                bgpPeerRemoteAddr
+                    IpAddress,
+                bgpPeerRemotePort
+                    INTEGER,
+                bgpPeerRemoteAs
+                    INTEGER,
+                bgpPeerInUpdates
+                    Counter32,
+                bgpPeerOutUpdates
+                    Counter32,
+                bgpPeerInTotalMessages
+                    Counter32,
+                bgpPeerOutTotalMessages
+                    Counter32,
+                bgpPeerLastError
+                    OCTET STRING,
+                bgpPeerFsmEstablishedTransitions
+                    Counter32,
+                bgpPeerFsmEstablishedTime
+                    Gauge32,
+                bgpPeerConnectRetryInterval
+                    INTEGER,
+                bgpPeerHoldTime
+                    INTEGER,
+                bgpPeerKeepAlive
+                    INTEGER,
+                bgpPeerHoldTimeConfigured
+                    INTEGER,
+                bgpPeerKeepAliveConfigured
+                    INTEGER,
+                bgpPeerMinASOriginationInterval
+                    INTEGER,
+                bgpPeerMinRouteAdvertisementInterval
+                    INTEGER,
+                bgpPeerInUpdateElapsedTime
+                    Gauge32
+                }
+
+        bgpPeerIdentifier OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The BGP Identifier of this entry's BGP peer."
+            ::= { bgpPeerEntry 1 }
+
+        bgpPeerState OBJECT-TYPE
+            SYNTAX     INTEGER {
+                                idle(1),
+                                connect(2),
+                                active(3),
+                                opensent(4),
+                                openconfirm(5),
+                                established(6)
+                       }
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The BGP peer connection state."
+            ::= { bgpPeerEntry 2 }
+
+        bgpPeerAdminStatus OBJECT-TYPE
+            SYNTAX     INTEGER {
+                                stop(1),
+                                start(2)
+                       }
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "The desired state of the BGP connection.  A
+                    transition from 'stop' to 'start' will cause
+                    the BGP Start Event to be generated.  A
+                    transition from 'start' to 'stop' will cause
+                    the BGP Stop Event to be generated.  This
+                    parameter can be used to restart BGP peer
+                    connections.  Care should be used in providing
+                    write access to this object without adequate
+                    authentication."
+            ::= { bgpPeerEntry 3 }
+
+        bgpPeerNegotiatedVersion OBJECT-TYPE
+            SYNTAX     Integer32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The negotiated version of BGP running between
+                    the two peers."
+            ::= { bgpPeerEntry 4 }
+
+        bgpPeerLocalAddr OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The local IP address of this entry's BGP
+                    connection."
+            ::= { bgpPeerEntry 5 }
+
+        bgpPeerLocalPort OBJECT-TYPE
+            SYNTAX     INTEGER (0..65535)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The local port for the TCP connection between
+                    the BGP peers."
+            ::= { bgpPeerEntry 6 }
+
+        bgpPeerRemoteAddr OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The remote IP address of this entry's BGP
+                    peer."
+            ::= { bgpPeerEntry 7 }
+
+        bgpPeerRemotePort OBJECT-TYPE
+            SYNTAX     INTEGER (0..65535)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The remote port for the TCP connection between
+                    the BGP peers.  Note that the objects
+                    bgpPeerLocalAddr, bgpPeerLocalPort,
+                    bgpPeerRemoteAddr and bgpPeerRemotePort
+                    provide the appropriate reference to the
+                    standard MIB TCP connection table."
+            ::= { bgpPeerEntry 8 }
+
+        bgpPeerRemoteAs OBJECT-TYPE
+            SYNTAX     INTEGER (0..65535)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The remote autonomous system number."
+            ::= { bgpPeerEntry 9 }
+
+        bgpPeerInUpdates OBJECT-TYPE
+            SYNTAX     Counter32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The number of BGP UPDATE messages received on
+                    this connection.  This object should be
+                    initialized to zero (0) when the connection is
+                    established."
+            ::= { bgpPeerEntry 10 }
+
+        bgpPeerOutUpdates OBJECT-TYPE
+            SYNTAX     Counter32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The number of BGP UPDATE messages transmitted
+                    on this connection.  This object should be
+                    initialized to zero (0) when the connection is
+                    established."
+            ::= { bgpPeerEntry 11 }
+
+        bgpPeerInTotalMessages OBJECT-TYPE
+            SYNTAX     Counter32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The total number of messages received from the
+                    remote peer on this connection.  This object
+                    should be initialized to zero when the
+                    connection is established."
+            ::= { bgpPeerEntry 12 }
+
+        bgpPeerOutTotalMessages OBJECT-TYPE
+            SYNTAX     Counter32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The total number of messages transmitted to
+                    the remote peer on this connection.  This object
+                    should be initialized to zero when the
+                    connection is established."
+            ::= { bgpPeerEntry 13 }
+
+        bgpPeerLastError OBJECT-TYPE
+            SYNTAX     OCTET STRING (SIZE (2))
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The last error code and subcode seen by this
+                    peer on this connection.  If no error has
+                    occurred, this field is zero.  Otherwise, the
+                    first byte of this two byte OCTET STRING
+                    contains the error code, and the second byte
+                    contains the subcode."
+            ::= { bgpPeerEntry 14 }
+
+        bgpPeerFsmEstablishedTransitions OBJECT-TYPE
+            SYNTAX     Counter32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The total number of times the BGP FSM
+                    transitioned into the established state."
+            ::= { bgpPeerEntry 15 }
+
+        bgpPeerFsmEstablishedTime OBJECT-TYPE
+            SYNTAX     Gauge32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "This timer indicates how long (in seconds) this
+                    peer has been in the Established state or how long
+                    since this peer was last in the Established state.
+                    It is set to zero when a new peer is configured or
+                    the router is booted."
+            ::= { bgpPeerEntry 16 }
+
+        bgpPeerConnectRetryInterval OBJECT-TYPE
+            SYNTAX     INTEGER (1..65535)
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the ConnectRetry
+                    timer.  The suggested value for this timer is
+                    120 seconds."
+            ::= { bgpPeerEntry 17 }
+
+        bgpPeerHoldTime OBJECT-TYPE
+            SYNTAX     INTEGER  ( 0 | 3..65535 )
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the Hold Timer
+                    established with the peer.  The value of this
+                    object is calculated by this BGP speaker by
+                    using the smaller of the value in
+                    bgpPeerHoldTimeConfigured and the Hold Time
+                    received in the OPEN message.  This value
+                    must be at lease three seconds if it is not
+                    zero (0) in which case the Hold Timer has
+                    not been established with the peer, or, the
+                    value of bgpPeerHoldTimeConfigured is zero (0)."
+            ::= { bgpPeerEntry 18 }
+
+        bgpPeerKeepAlive OBJECT-TYPE
+            SYNTAX     INTEGER ( 0 | 1..21845 )
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the KeepAlive
+                    timer established with the peer.  The value of
+                    this object is calculated by this BGP speaker
+                    such that, when compared with bgpPeerHoldTime,
+                    it has the same proportion as what
+                    bgpPeerKeepAliveConfigured has when compared
+                    with bgpPeerHoldTimeConfigured.  If the value
+                    of this object is zero (0), it indicates that
+                    the KeepAlive timer has not been established
+                    with the peer, or, the value of
+                    bgpPeerKeepAliveConfigured is zero (0)."
+            ::= { bgpPeerEntry 19 }
+
+        bgpPeerHoldTimeConfigured OBJECT-TYPE
+            SYNTAX     INTEGER ( 0 | 3..65535 )
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the Hold Time
+                    configured for this BGP speaker with this peer.
+                    This value is placed in an OPEN message sent to
+                    this peer by this BGP speaker, and is compared
+                    with the Hold Time field in an OPEN message
+                    received from the peer when determining the Hold
+                    Time (bgpPeerHoldTime) with the peer.  This value
+                    must not be less than three seconds if it is not
+                    zero (0) in which case the Hold Time is NOT to be
+                    established with the peer.  The suggested value for
+                    this timer is 90 seconds."
+            ::= { bgpPeerEntry 20 }
+
+        bgpPeerKeepAliveConfigured OBJECT-TYPE
+            SYNTAX     INTEGER ( 0 | 1..21845 )
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the KeepAlive timer
+                    configured for this BGP speaker with this peer.
+                    The value of this object will only determine the
+                    KEEPALIVE messages' frequency relative to the value
+                    specified in bgpPeerHoldTimeConfigured; the actual
+                    time interval for the KEEPALIVE messages is
+                    indicated by bgpPeerKeepAlive.  A reasonable
+                    maximum value for this timer would be configured to
+                    be one third of that of bgpPeerHoldTimeConfigured.
+                    If the value of this object is zero (0), no
+                    periodical KEEPALIVE messages are sent to the peer
+                    after the BGP connection has been established.  The
+                    suggested value for this timer is 30 seconds."
+            ::= { bgpPeerEntry 21 }
+
+        bgpPeerMinASOriginationInterval OBJECT-TYPE
+            SYNTAX     INTEGER (1..65535)
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the
+                    MinASOriginationInterval timer.
+                    The suggested value for this timer is 15 seconds."
+            ::= { bgpPeerEntry 22 }
+
+        bgpPeerMinRouteAdvertisementInterval OBJECT-TYPE
+            SYNTAX     INTEGER (1..65535)
+            MAX-ACCESS read-write
+            STATUS     current
+            DESCRIPTION
+                    "Time interval in seconds for the
+                    MinRouteAdvertisementInterval timer.
+                    The suggested value for this timer is 30 seconds."
+            ::= { bgpPeerEntry 23 }
+
+        bgpPeerInUpdateElapsedTime OBJECT-TYPE
+            SYNTAX     Gauge32
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Elapsed time in seconds since the last BGP
+                    UPDATE message was received from the peer.
+                    Each time bgpPeerInUpdates is incremented,
+                    the value of this object is set to zero (0)."
+            ::= { bgpPeerEntry 24 }
+
+
+
+        bgpIdentifier OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The BGP Identifier of local system."
+            ::= { bgp 4 }
+
+
+
+        -- Received Path Attribute Table.  This table contains,
+        -- one entry per path to a network, path attributes
+        -- received from all peers running BGP version 3 or less.
+        -- This table is obsolete, having been replaced in
+        -- functionality with the bgp4PathAttrTable.
+
+        bgpRcvdPathAttrTable OBJECT-TYPE
+            SYNTAX     SEQUENCE OF BgpPathAttrEntry
+            MAX-ACCESS not-accessible
+            STATUS     obsolete
+            DESCRIPTION
+                    "The BGP Received Path Attribute Table contains
+                    information about paths to destination networks
+                    received from all peers running BGP version 3 or
+                    less."
+            ::= { bgp 5 }
+
+        bgpPathAttrEntry OBJECT-TYPE
+            SYNTAX     BgpPathAttrEntry
+            MAX-ACCESS not-accessible
+            STATUS     obsolete
+            DESCRIPTION
+                    "Information about a path to a network."
+            INDEX { bgpPathAttrDestNetwork,
+                    bgpPathAttrPeer        }
+            ::= { bgpRcvdPathAttrTable 1 }
+
+        BgpPathAttrEntry ::= SEQUENCE {
+            bgpPathAttrPeer
+                 IpAddress,
+            bgpPathAttrDestNetwork
+                 IpAddress,
+            bgpPathAttrOrigin
+                 INTEGER,
+            bgpPathAttrASPath
+                 OCTET STRING,
+            bgpPathAttrNextHop
+                 IpAddress,
+            bgpPathAttrInterASMetric
+                 Integer32
+        }
+
+        bgpPathAttrPeer OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                    "The IP address of the peer where the path
+                    information was learned."
+            ::= { bgpPathAttrEntry 1 }
+
+        bgpPathAttrDestNetwork OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                    "The address of the destination network."
+            ::= { bgpPathAttrEntry 2 }
+
+        bgpPathAttrOrigin OBJECT-TYPE
+            SYNTAX     INTEGER {
+                           igp(1),-- networks are interior
+                           egp(2),-- networks learned via EGP
+                           incomplete(3) -- undetermined
+                       }
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                 "The ultimate origin of the path information."
+            ::= { bgpPathAttrEntry 3 }
+
+        bgpPathAttrASPath OBJECT-TYPE
+            SYNTAX     OCTET STRING (SIZE (2..255))
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                    "The set of ASs that must be traversed to reach
+                    the network.  This object is probably best
+                    represented as SEQUENCE OF INTEGER.  For SMI
+                    compatibility, though, it is represented as
+                    OCTET STRING.  Each AS is represented as a pair
+                    of octets according to the following algorithm:
+
+                        first-byte-of-pair = ASNumber / 256;
+                        second-byte-of-pair = ASNumber & 255;"
+            ::= { bgpPathAttrEntry 4 }
+
+        bgpPathAttrNextHop OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                    "The address of the border router that should
+                    be used for the destination network."
+            ::= { bgpPathAttrEntry 5 }
+
+        bgpPathAttrInterASMetric OBJECT-TYPE
+            SYNTAX     Integer32
+            MAX-ACCESS read-only
+            STATUS     obsolete
+            DESCRIPTION
+                    "The optional inter-AS metric.  If this
+                    attribute has not been provided for this route,
+                    the value for this object is 0."
+            ::= { bgpPathAttrEntry 6 }
+
+
+
+        -- BGP-4 Received Path Attribute Table.  This table contains,
+        -- one entry per path to a network, path attributes
+        -- received from all peers running BGP-4.
+
+        bgp4PathAttrTable OBJECT-TYPE
+            SYNTAX     SEQUENCE OF Bgp4PathAttrEntry
+            MAX-ACCESS not-accessible
+            STATUS     current
+            DESCRIPTION
+                    "The BGP-4 Received Path Attribute Table contains
+                    information about paths to destination networks
+                    received from all BGP4 peers."
+            ::= { bgp 6 }
+
+        bgp4PathAttrEntry OBJECT-TYPE
+            SYNTAX     Bgp4PathAttrEntry
+            MAX-ACCESS not-accessible
+            STATUS     current
+            DESCRIPTION
+                    "Information about a path to a network."
+            INDEX { bgp4PathAttrIpAddrPrefix,
+                    bgp4PathAttrIpAddrPrefixLen,
+                    bgp4PathAttrPeer            }
+            ::= { bgp4PathAttrTable 1 }
+
+        Bgp4PathAttrEntry ::= SEQUENCE {
+            bgp4PathAttrPeer
+                 IpAddress,
+            bgp4PathAttrIpAddrPrefixLen
+                 INTEGER,
+            bgp4PathAttrIpAddrPrefix
+                 IpAddress,
+            bgp4PathAttrOrigin
+                 INTEGER,
+            bgp4PathAttrASPathSegment
+                 OCTET STRING,
+            bgp4PathAttrNextHop
+                 IpAddress,
+            bgp4PathAttrMultiExitDisc
+                 INTEGER,
+            bgp4PathAttrLocalPref
+                 INTEGER,
+            bgp4PathAttrAtomicAggregate
+                 INTEGER,
+            bgp4PathAttrAggregatorAS
+                 INTEGER,
+            bgp4PathAttrAggregatorAddr
+                 IpAddress,
+            bgp4PathAttrCalcLocalPref
+                 INTEGER,
+            bgp4PathAttrBest
+                 INTEGER,
+            bgp4PathAttrUnknown
+                 OCTET STRING
+        }
+
+        bgp4PathAttrPeer OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The IP address of the peer where the path
+                    information was learned."
+            ::= { bgp4PathAttrEntry 1 }
+        bgp4PathAttrIpAddrPrefixLen OBJECT-TYPE
+            SYNTAX     INTEGER (0..32)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Length in bits of the IP address prefix in the
+                    Network Layer Reachability Information field."
+            ::= { bgp4PathAttrEntry 2 }
+
+        bgp4PathAttrIpAddrPrefix OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "An IP address prefix in the Network Layer
+                    Reachability Information field.  This object
+                    is an IP address containing the prefix with
+                    length specified by bgp4PathAttrIpAddrPrefixLen.
+                    Any bits beyond the length specified by
+                    bgp4PathAttrIpAddrPrefixLen are zeroed."
+            ::= { bgp4PathAttrEntry 3 }
+
+        bgp4PathAttrOrigin OBJECT-TYPE
+            SYNTAX     INTEGER {
+                                 igp(1),-- networks are interior
+                                 egp(2),-- networks learned via EGP
+                                 incomplete(3) -- undetermined
+                               }
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The ultimate origin of the path information."
+            ::= { bgp4PathAttrEntry 4 }
+
+        bgp4PathAttrASPathSegment OBJECT-TYPE
+            SYNTAX     OCTET STRING (SIZE (2..255))
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The sequence of AS path segments.  Each AS
+                    path segment is represented by a triple
+                    <type, length, value>.
+
+                    The type is a 1-octet field which has two
+                    possible values:
+                         1      AS_SET: unordered set of ASs a
+                                     route in the UPDATE message
+                                     has traversed
+                         2      AS_SEQUENCE: ordered set of ASs
+                                     a route in the UPDATE message
+                                     has traversed.
+
+                    The length is a 1-octet field containing the
+                    number of ASs in the value field.
+
+                    The value field contains one or more AS
+                    numbers, each AS is represented in the octet
+                    string as a pair of octets according to the
+                    following algorithm:
+
+                        first-byte-of-pair = ASNumber / 256;
+                        second-byte-of-pair = ASNumber & 255;"
+            ::= { bgp4PathAttrEntry 5 }
+
+        bgp4PathAttrNextHop OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The address of the border router that should
+                    be used for the destination network."
+            ::= { bgp4PathAttrEntry 6 }
+
+        bgp4PathAttrMultiExitDisc OBJECT-TYPE
+            SYNTAX     INTEGER (-1..2147483647)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "This metric is used to discriminate between
+                    multiple exit points to an adjacent autonomous
+                    system.  A value of -1 indicates the absence of
+                    this attribute."
+            ::= { bgp4PathAttrEntry 7 }
+
+        bgp4PathAttrLocalPref OBJECT-TYPE
+            SYNTAX     INTEGER (-1..2147483647)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The originating BGP4 speaker's degree of
+                    preference for an advertised route.  A value of
+                    -1 indicates the absence of this attribute."
+            ::= { bgp4PathAttrEntry 8 }
+
+        bgp4PathAttrAtomicAggregate OBJECT-TYPE
+            SYNTAX     INTEGER {
+                           lessSpecificRrouteNotSelected(1),
+                           lessSpecificRouteSelected(2)
+                       }
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "Whether or not a system has selected
+                    a less specific route without selecting a
+                    more specific route."
+            ::= { bgp4PathAttrEntry 9 }
+
+        bgp4PathAttrAggregatorAS OBJECT-TYPE
+            SYNTAX     INTEGER (0..65535)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The AS number of the last BGP4 speaker that
+                    performed route aggregation.  A value of zero (0)
+                    indicates the absence of this attribute."
+            ::= { bgp4PathAttrEntry 10 }
+
+        bgp4PathAttrAggregatorAddr OBJECT-TYPE
+            SYNTAX     IpAddress
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The IP address of the last BGP4 speaker that
+                     performed route aggregation.  A value of
+                     0.0.0.0 indicates the absence of this attribute."
+            ::= { bgp4PathAttrEntry 11 }
+
+        bgp4PathAttrCalcLocalPref OBJECT-TYPE
+            SYNTAX     INTEGER (-1..2147483647)
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "The degree of preference calculated by the
+                    receiving BGP4 speaker for an advertised route.
+                    A value of -1 indicates the absence of this
+                    attribute."
+            ::= { bgp4PathAttrEntry 12 }
+
+        bgp4PathAttrBest OBJECT-TYPE
+            SYNTAX     INTEGER {
+                           false(1),-- not chosen as best route
+                           true(2) -- chosen as best route
+                       }
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "An indication of whether or not this route
+                    was chosen as the best BGP4 route."
+            ::= { bgp4PathAttrEntry 13 }
+
+        bgp4PathAttrUnknown OBJECT-TYPE
+            SYNTAX     OCTET STRING (SIZE(0..255))
+            MAX-ACCESS read-only
+            STATUS     current
+            DESCRIPTION
+                    "One or more path attributes not understood
+                     by this BGP4 speaker.  Size zero (0) indicates
+                     the absence of such attribute(s).  Octets
+                     beyond the maximum size, if any, are not
+                     recorded by this object."
+            ::= { bgp4PathAttrEntry 14 }
+
+
+        -- Traps.
+
+        -- note that in RFC 1657, bgpTraps was incorrectly
+        -- assigned a value of { bgp 7 }, and each of the
+        -- traps had the bgpPeerRemoteAddr object inappropriately
+        -- removed from their OBJECTS clause.  The following
+        -- definitions restore the semantics of the traps as
+        -- they were initially defined in RFC 1269.
+
+        -- { bgp 7 } is unused
+
+        bgpTraps          OBJECT IDENTIFIER ::= { bgp 0 }
+
+        bgpEstablished NOTIFICATION-TYPE
+            OBJECTS { bgpPeerRemoteAddr,
+                      bgpPeerLastError,
+                      bgpPeerState      }
+            STATUS  current
+            DESCRIPTION
+                    "The BGP Established event is generated when
+                    the BGP FSM enters the ESTABLISHED state."
+            ::= { bgpTraps 1 }
+
+        bgpBackwardTransition NOTIFICATION-TYPE
+            OBJECTS { bgpPeerRemoteAddr,
+                      bgpPeerLastError,
+                      bgpPeerState      }
+            STATUS  current
+            DESCRIPTION
+                    "The BGPBackwardTransition Event is generated
+                    when the BGP FSM moves from a higher numbered
+                    state to a lower numbered state."
+            ::= { bgpTraps 2 }
+
+        -- conformance information
+
+        bgpMIBConformance OBJECT IDENTIFIER ::= { bgp 8 }
+        bgpMIBCompliances OBJECT IDENTIFIER ::= { bgpMIBConformance 1 }
+        bgpMIBGroups      OBJECT IDENTIFIER ::= { bgpMIBConformance 2 }
+
+        -- compliance statements
+
+        bgpMIBCompliance MODULE-COMPLIANCE
+            STATUS  current
+            DESCRIPTION
+                    "The compliance statement for entities which
+                     implement the BGP4 mib."
+            MODULE  -- this module
+                MANDATORY-GROUPS { bgp4MIBGlobalsGroup,
+                                   bgp4MIBPeerGroup,
+                                   bgp4MIBPathAttrGroup,
+                                   bgp4MIBNotificationGroup }
+            ::= { bgpMIBCompliances 1 }
+
+        -- units of conformance
+
+        bgp4MIBGlobalsGroup OBJECT-GROUP
+            OBJECTS { bgpVersion,
+                      bgpLocalAs,
+                      bgpIdentifier }
+            STATUS  current
+            DESCRIPTION
+                    "A collection of objects providing information
+                     on global BGP state."
+            ::= { bgpMIBGroups 1 }
+
+        bgp4MIBPeerGroup OBJECT-GROUP
+            OBJECTS { bgpPeerIdentifier,
+                      bgpPeerState,
+                      bgpPeerAdminStatus,
+                      bgpPeerNegotiatedVersion,
+                      bgpPeerLocalAddr,
+                      bgpPeerLocalPort,
+                      bgpPeerRemoteAddr,
+                      bgpPeerRemotePort,
+                      bgpPeerRemoteAs,
+                      bgpPeerInUpdates,
+                      bgpPeerOutUpdates,
+                      bgpPeerInTotalMessages,
+                      bgpPeerOutTotalMessages,
+                      bgpPeerLastError,
+                      bgpPeerFsmEstablishedTransitions,
+                      bgpPeerFsmEstablishedTime,
+                      bgpPeerConnectRetryInterval,
+                      bgpPeerHoldTime,
+                      bgpPeerKeepAlive,
+                      bgpPeerHoldTimeConfigured,
+                      bgpPeerKeepAliveConfigured,
+                      bgpPeerMinASOriginationInterval,
+                      bgpPeerMinRouteAdvertisementInterval,
+                      bgpPeerInUpdateElapsedTime }
+            STATUS  current
+            DESCRIPTION
+                    "A collection of objects for managing
+                     BGP peers."
+            ::= { bgpMIBGroups 2 }
+
+        bgp4MIBRcvdPathAttrGroup OBJECT-GROUP
+            OBJECTS { bgpPathAttrPeer,
+                      bgpPathAttrDestNetwork,
+                      bgpPathAttrOrigin,
+                      bgpPathAttrASPath,
+                      bgpPathAttrNextHop,
+                      bgpPathAttrInterASMetric }
+            STATUS  obsolete
+            DESCRIPTION
+                    "A collection of objects for managing BGP
+                     path entries.
+
+                     This conformance group is obsolete,
+                     replaced by bgp4MIBPathAttrGroup."
+            ::= { bgpMIBGroups 3 }
+
+        bgp4MIBPathAttrGroup OBJECT-GROUP
+            OBJECTS { bgp4PathAttrPeer,
+                      bgp4PathAttrIpAddrPrefixLen,
+                      bgp4PathAttrIpAddrPrefix,
+                      bgp4PathAttrOrigin,
+                      bgp4PathAttrASPathSegment,
+                      bgp4PathAttrNextHop,
+                      bgp4PathAttrMultiExitDisc,
+                      bgp4PathAttrLocalPref,
+                      bgp4PathAttrAtomicAggregate,
+                      bgp4PathAttrAggregatorAS,
+                      bgp4PathAttrAggregatorAddr,
+                      bgp4PathAttrCalcLocalPref,
+                      bgp4PathAttrBest,
+                      bgp4PathAttrUnknown }
+            STATUS  current
+            DESCRIPTION
+                    "A collection of objects for managing
+                     BGP path entries."
+            ::= { bgpMIBGroups 4 }
+
+        bgp4MIBNotificationGroup NOTIFICATION-GROUP
+            NOTIFICATIONS { bgpEstablished,
+                            bgpBackwardTransition }
+            STATUS  current
+            DESCRIPTION
+                    "A collection of notifications for signaling
+                    changes in BGP peer relationships."
+            ::= { bgpMIBGroups 5 }
+
+    END
diff --git a/bgpd/IMPLEMENTATION.txt b/bgpd/IMPLEMENTATION.txt
new file mode 100644 (file)
index 0000000..fff360a
--- /dev/null
@@ -0,0 +1,169 @@
+$Id: IMPLEMENTATION.txt,v 1.2 2005/02/15 17:10:03 gdt Exp $
+
+This file contains notes about the internals of the BGP
+implementation.  The initial impetus is understanding the memory usage
+of Quagga'a BGP implementation.  There may be some inaccuracies; it is
+in the repository in the hopes that it will be significantly more
+helpful than not.
+
+* FILES
+
+bgp_advertise.[hc]:
+  data structures: advertised prefixes, attributes
+
+bgp_aspath.[hc]:
+  struct aspath:
+    These are stored in a hash, apparently in wire format.
+bgp_attr.[hc]:
+  struct attr: contains all attributes
+    size(ILP32) 26 words/104 bytes (poor packing, v6/multicast is 10)
+
+  bgp_attr_parse: origin, aspath, next hop probably most of interest
+  bgp_attr_origin: set flag bit
+  bgp_attr_aspath: put in refcounted hash table, so share pointer
+  bgp_attr_nexthop: store in attribute structure
+
+bgp_btoa.c: ? test program
+
+bgp_clist.[hc]:
+  data structures: community lists (including permit/deny state)
+
+bgp_community.[hc]:
+  data structures: community atttributes (multiple communities per struct)
+
+bgp_damp.[hc]:
+  per-route damping data, and damping control information
+
+bgp_debug.[hc]:
+  debugging support (vty config, dump of packets)
+
+bgp_dump.[hc]:
+  MRT-compatible dump format routines
+
+bgp_ecommunity.[hc]:
+  Extended communities attributes (multiple ecommmunities per struct)
+
+bgp_filter.[hc]:
+  AS path access list filtering
+
+bgp_fsm.[hc]:
+  Per-peer state machine for TCP connection, hold time, etc.
+
+bgp_main.c:
+  Daemon startup.
+
+bgp_mplsvpn.[hc]:
+  parsing of attribute structures for MPLS VPNs [need better description]
+
+bgp_network.[hc]:
+  Opening and binding of sockets, finding addresses for interfaces
+
+bgp_nexthop.[hc]:
+  data structures: Nexthop cache [not clear how used, if truly cache
+  in sense of memoization, or something else]
+
+  importing EGP routes into IGP (thread created)
+  "scanning" (thread created)
+  bgp_scan: has useful clues to data structure complexity.  Scanning
+  process iterates over database of received advertisements, and
+  builds 'cache' structure.
+bgp_open.[ch]:
+  Open messages, and capability negotiation
+
+bgp_packet.[hc]
+  sending and receiving of UPDATE/WITHDRAW
+  collision resolution for simultanteous opens
+  bgp_read: top-level read routine: reads whole packet (nonblocking)
+    and dispatches to per-message-type receive
+
+  bgp_update_receive:
+    calls bgp_attr_parse
+    reads nrli into struct bgp_nrli update
+
+    uninterning of aspath, community, ecommmunity, cluster,
+    transit which were interned in bgp_attr_parse
+
+bgp_regex.[ch]:
+  Glue to convert BGP regexps to standard (_ means many things).
+
+bgp_route.[hc]:
+  data structures: routes as received, static routes
+  Application of filters.  Lots of route processing.
+  bgp_nlri_parse:
+    sanity checks, then calls bgp_update with peer, prefix, attributes pointer
+
+  bgp_update: bgp_update_main, then RS processing
+
+  bgp_update_main:
+    find 'struct bgp_node *' for this afi/safi
+    look for route in table, then 'intern' attributes
+    ** interning is process of
+      looking for data in hash table, and putting there if missing, refcnt
+      using pointer to existing data
+    many validity checks
+    get new struct bgp_info (10 words/40 bytes)
+    call bgp_info_add with rn and bgp_info
+    call bgp_process
+
+bgp_routemap.c
+  implementation of route maps (match and set)
+
+bgp_snmp.c
+  SNMP glue.  Not particularly interesting except to add variables or
+  debug SNMP.
+
+bgp_table.[hc]
+  data structures: struct bgp_table, struct bgp_node
+  allocation/lookup/utility operations - not a lot of protocol processin
+
+bgp_vty.[hc]
+  protocol-wide vty hooks
+
+bgp_zebra.[hc]
+  Processing interface events from zebra, redistribution of routes.
+
+bgpd.h
+  struct bgp_master: daemon main data structure 
+  struct bgp: per-instance structure
+  struct peer_group
+  struct bgp_notify: (in-core representation of wire format?)
+  struct bgp_nexthop: (v4 and v6 addresses, *ifp)
+  struct bgp_rd: router distinguisher: 8 octects
+  struct bgp_filter: distribute, prefix, aslist, route_maps
+  struct peer: neighbor structure (very rich/complex)
+  struct bgp_nlri: reference to wire format
+  #define of protocol constants
+    attribute type codes
+  fsm states/events
+  timer values
+
+bgpd.c
+  instance/peer allocation
+  configuration
+  initialization/termination
+
+* DATA STRUCTURE SIZES
+
+Question: How much memory does quagga's bgpd use as a function of
+state received from peers?
+
+It seems that a struct bgp_info is kept for each prefix.  The "struct
+attr *" is interned, and variables within that are interned.  So, 40
+bytes are kept per received prefix, plus interned shared values.  This
+could be 36 if 'int suppress' where changed to a u_char and moved to
+be with the other u_chars.  Without MPLS, this could be 32 bytes.
+Note that 8 bytes of this is linked list overhead, meaning that 24
+bytes are the raw per-prefix storage requirements.
+
+Also, a struct bgp_damp_info is apparently maintained per route; this
+is fairly large (about 44 bytes).
+
+[TODO: the role of struct bgp_node.]
+
+* TIME COMPLEXITY
+
+It appears that received prefixes from each peer are stored in a
+linked list.
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
new file mode 100644 (file)
index 0000000..753b679
--- /dev/null
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = libbgp.a
+sbin_PROGRAMS = bgpd
+bin_PROGRAMS = bgp_btoa
+
+libbgp_a_SOURCES = \
+       bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \
+       bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \
+       bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \
+       bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_lcommunity.c \
+       bgp_mplsvpn.c bgp_nexthop.c \
+       bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \
+       bgp_encap.c bgp_encap_tlv.c bgp_nht.c
+
+noinst_HEADERS = \
+       bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \
+       bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \
+       bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
+       bgp_ecommunity.h bgp_lcommunity.h \
+       bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
+       bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h \
+       bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h bgp_nht.h
+
+bgpd_SOURCES = bgp_main.c
+bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
+
+bgp_btoa_SOURCES = bgp_btoa.c
+bgp_btoa_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2
+
+EXTRA_DIST = BGP4-MIB.txt
+
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c
new file mode 100644 (file)
index 0000000..1b17b66
--- /dev/null
@@ -0,0 +1,423 @@
+/* BGP advertisement and adjacency
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "hash.h"
+#include "thread.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+
+/* BGP advertise attribute is used for pack same attribute update into
+   one packet.  To do that we maintain attribute hash in struct
+   peer.  */
+static struct bgp_advertise_attr *
+baa_new (void)
+{
+  return (struct bgp_advertise_attr *)
+    XCALLOC (MTYPE_BGP_ADVERTISE_ATTR, sizeof (struct bgp_advertise_attr));
+}
+
+static void
+baa_free (struct bgp_advertise_attr *baa)
+{
+  XFREE (MTYPE_BGP_ADVERTISE_ATTR, baa);
+}
+
+static void *
+baa_hash_alloc (void *p)
+{
+  struct bgp_advertise_attr * ref = (struct bgp_advertise_attr *) p;
+  struct bgp_advertise_attr *baa;
+
+  baa = baa_new ();
+  baa->attr = ref->attr;
+  return baa;
+}
+
+static unsigned int
+baa_hash_key (void *p)
+{
+  struct bgp_advertise_attr * baa = (struct bgp_advertise_attr *) p;
+
+  return attrhash_key_make (baa->attr);
+}
+
+static int
+baa_hash_cmp (const void *p1, const void *p2)
+{
+  const struct bgp_advertise_attr * baa1 = p1;
+  const struct bgp_advertise_attr * baa2 = p2;
+
+  return attrhash_cmp (baa1->attr, baa2->attr);
+}
+
+/* BGP update and withdraw information is stored in BGP advertise
+   structure.  This structure is referred from BGP adjacency
+   information.  */
+static struct bgp_advertise *
+bgp_advertise_new (void)
+{
+  return (struct bgp_advertise *) 
+    XCALLOC (MTYPE_BGP_ADVERTISE, sizeof (struct bgp_advertise));
+}
+
+static void
+bgp_advertise_free (struct bgp_advertise *adv)
+{
+  if (adv->binfo)
+    bgp_info_unlock (adv->binfo); /* bgp_advertise bgp_info reference */
+  XFREE (MTYPE_BGP_ADVERTISE, adv);
+}
+
+static void
+bgp_advertise_add (struct bgp_advertise_attr *baa,
+                  struct bgp_advertise *adv)
+{
+  adv->next = baa->adv;
+  if (baa->adv)
+    baa->adv->prev = adv;
+  baa->adv = adv;
+}
+
+static void
+bgp_advertise_delete (struct bgp_advertise_attr *baa,
+                     struct bgp_advertise *adv)
+{
+  if (adv->next)
+    adv->next->prev = adv->prev;
+  if (adv->prev)
+    adv->prev->next = adv->next;
+  else
+    baa->adv = adv->next;
+}
+
+static struct bgp_advertise_attr *
+bgp_advertise_intern (struct hash *hash, struct attr *attr)
+{
+  struct bgp_advertise_attr ref;
+  struct bgp_advertise_attr *baa;
+
+  ref.attr = bgp_attr_intern (attr);
+  baa = (struct bgp_advertise_attr *) hash_get (hash, &ref, baa_hash_alloc);
+  baa->refcnt++;
+
+  return baa;
+}
+
+static void
+bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa)
+{
+  if (baa->refcnt)
+    baa->refcnt--;
+
+  if (baa->refcnt && baa->attr)
+    bgp_attr_unintern (&baa->attr);
+  else
+    {
+      if (baa->attr)
+       {
+         hash_release (hash, baa);
+         bgp_attr_unintern (&baa->attr);
+       }
+      baa_free (baa);
+    }
+}
+
+/* BGP adjacency keeps minimal advertisement information.  */
+static void
+bgp_adj_out_free (struct bgp_adj_out *adj)
+{
+  peer_unlock (adj->peer); /* adj_out peer reference */
+  XFREE (MTYPE_BGP_ADJ_OUT, adj);
+}
+
+int
+bgp_adj_out_lookup (struct peer *peer, struct prefix *p,
+                   afi_t afi, safi_t safi, struct bgp_node *rn)
+{
+  struct bgp_adj_out *adj;
+
+  for (adj = rn->adj_out; adj; adj = adj->next)
+    if (adj->peer == peer)
+      break;
+
+  if (! adj)
+    return 0;
+
+  return (adj->adv 
+         ? (adj->adv->baa ? 1 : 0)
+         : (adj->attr ? 1 : 0));
+}
+
+struct bgp_advertise *
+bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj,
+                    afi_t afi, safi_t safi)
+{
+  struct bgp_advertise *adv;
+  struct bgp_advertise_attr *baa;
+  struct bgp_advertise *next;
+  struct bgp_advertise_fifo *fhead;
+
+  adv = adj->adv;
+  baa = adv->baa;
+  next = NULL;
+  fhead = (struct bgp_advertise_fifo *)&peer->sync[afi][safi]->withdraw;
+
+  if (baa)
+    {
+      /* Unlink myself from advertise attribute FIFO.  */
+      bgp_advertise_delete (baa, adv);
+
+      /* Fetch next advertise candidate. */
+      next = baa->adv;
+
+      /* Unintern BGP advertise attribute.  */
+      bgp_advertise_unintern (peer->hash[afi][safi], baa);
+
+      fhead = (struct bgp_advertise_fifo *)&peer->sync[afi][safi]->update;
+    }
+
+  /* Unlink myself from advertisement FIFO.  */
+  BGP_ADV_FIFO_DEL (fhead, adv);
+
+  /* Free memory.  */
+  bgp_advertise_free (adj->adv);
+  adj->adv = NULL;
+
+  return next;
+}
+
+void
+bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p,
+                struct attr *attr, afi_t afi, safi_t safi,
+                struct bgp_info *binfo)
+{
+  struct bgp_adj_out *adj = NULL;
+  struct bgp_advertise *adv;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return;
+
+  /* Look for adjacency information. */
+  if (rn)
+    {
+      for (adj = rn->adj_out; adj; adj = adj->next)
+       if (adj->peer == peer)
+         break;
+    }
+
+  if (! adj)
+    {
+      adj = XCALLOC (MTYPE_BGP_ADJ_OUT, sizeof (struct bgp_adj_out));
+      adj->peer = peer_lock (peer); /* adj_out peer reference */
+      
+      if (rn)
+        {
+          BGP_ADJ_OUT_ADD (rn, adj);
+          bgp_lock_node (rn);
+        }
+    }
+
+  if (adj->adv)
+    bgp_advertise_clean (peer, adj, afi, safi);
+  
+  adj->adv = bgp_advertise_new ();
+
+  adv = adj->adv;
+  adv->rn = rn;
+  
+  assert (adv->binfo == NULL);
+  adv->binfo = bgp_info_lock (binfo); /* bgp_info adj_out reference */
+  
+  if (attr)
+    adv->baa = bgp_advertise_intern (peer->hash[afi][safi], attr);
+  else
+    adv->baa = baa_new ();
+  adv->adj = adj;
+
+  /* Add new advertisement to advertisement attribute list. */
+  bgp_advertise_add (adv->baa, adv);
+
+  BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo);
+}
+
+void
+bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p, 
+                  afi_t afi, safi_t safi)
+{
+  struct bgp_adj_out *adj;
+  struct bgp_advertise *adv;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return;
+
+  /* Lookup existing adjacency, if it is not there return immediately.  */
+  for (adj = rn->adj_out; adj; adj = adj->next)
+    if (adj->peer == peer)
+      break;
+
+  if (! adj)
+    return;
+
+  /* Clearn up previous advertisement.  */
+  if (adj->adv)
+    bgp_advertise_clean (peer, adj, afi, safi);
+
+  if (adj->attr)
+    {
+      /* We need advertisement structure.  */
+      adj->adv = bgp_advertise_new ();
+      adv = adj->adv;
+      adv->rn = rn;
+      adv->adj = adj;
+
+      /* Add to synchronization entry for withdraw announcement.  */
+      BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo);
+
+      /* Schedule packet write. */
+      BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+    }
+  else
+    {
+      /* Remove myself from adjacency. */
+      BGP_ADJ_OUT_DEL (rn, adj);
+      
+      /* Free allocated information.  */
+      bgp_adj_out_free (adj);
+
+      bgp_unlock_node (rn);
+    }
+}
+
+void
+bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, 
+                   struct peer *peer, afi_t afi, safi_t safi)
+{
+  if (adj->attr)
+    bgp_attr_unintern (&adj->attr);
+
+  if (adj->adv)
+    bgp_advertise_clean (peer, adj, afi, safi);
+
+  BGP_ADJ_OUT_DEL (rn, adj);
+  bgp_adj_out_free (adj);
+}
+
+void
+bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr)
+{
+  struct bgp_adj_in *adj;
+
+  for (adj = rn->adj_in; adj; adj = adj->next)
+    {
+      if (adj->peer == peer)
+       {
+         if (adj->attr != attr)
+           {
+             bgp_attr_unintern (&adj->attr);
+             adj->attr = bgp_attr_intern (attr);
+           }
+         return;
+       }
+    }
+  adj = XCALLOC (MTYPE_BGP_ADJ_IN, sizeof (struct bgp_adj_in));
+  adj->peer = peer_lock (peer); /* adj_in peer reference */
+  adj->attr = bgp_attr_intern (attr);
+  BGP_ADJ_IN_ADD (rn, adj);
+  bgp_lock_node (rn);
+}
+
+void
+bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai)
+{
+  bgp_attr_unintern (&bai->attr);
+  BGP_ADJ_IN_DEL (rn, bai);
+  peer_unlock (bai->peer); /* adj_in peer reference */
+  XFREE (MTYPE_BGP_ADJ_IN, bai);
+}
+
+int
+bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer)
+{
+  struct bgp_adj_in *adj;
+
+  for (adj = rn->adj_in; adj; adj = adj->next)
+    if (adj->peer == peer)
+      break;
+
+  if (! adj)
+    return 0;
+
+  bgp_adj_in_remove (rn, adj);
+  bgp_unlock_node (rn);
+  return 1;
+}
+
+void
+bgp_sync_init (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+  struct bgp_synchronize *sync;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       sync = XCALLOC (MTYPE_BGP_SYNCHRONISE, 
+                       sizeof (struct bgp_synchronize));
+       BGP_ADV_FIFO_INIT (&sync->update);
+       BGP_ADV_FIFO_INIT (&sync->withdraw);
+       BGP_ADV_FIFO_INIT (&sync->withdraw_low);
+       peer->sync[afi][safi] = sync;
+       peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp);
+      }
+}
+
+void
+bgp_sync_delete (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       if (peer->sync[afi][safi])
+         XFREE (MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]);
+       peer->sync[afi][safi] = NULL;
+       
+       if (peer->hash[afi][safi])
+         hash_free (peer->hash[afi][safi]);
+       peer->hash[afi][safi] = NULL;
+      }
+}
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
new file mode 100644 (file)
index 0000000..e2857a3
--- /dev/null
@@ -0,0 +1,173 @@
+/* BGP advertisement and adjacency
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ADVERTISE_H
+#define _QUAGGA_BGP_ADVERTISE_H
+
+#include <lib/fifo.h>
+
+/* BGP advertise FIFO.  */
+struct bgp_advertise_fifo
+{
+  struct bgp_advertise *next;
+  struct bgp_advertise *prev;
+  u_int32_t count;
+};
+
+/* BGP advertise attribute.  */
+struct bgp_advertise_attr
+{
+  /* Head of advertisement pointer. */
+  struct bgp_advertise *adv;
+
+  /* Reference counter.  */
+  unsigned long refcnt;
+
+  /* Attribute pointer to be announced.  */
+  struct attr *attr;
+};
+
+struct bgp_advertise
+{
+  /* FIFO for advertisement.  */
+  struct fifo fifo;
+
+  /* Link list for same attribute advertise.  */
+  struct bgp_advertise *next;
+  struct bgp_advertise *prev;
+
+  /* Prefix information.  */
+  struct bgp_node *rn;
+
+  /* Reference pointer.  */
+  struct bgp_adj_out *adj;
+
+  /* Advertisement attribute.  */
+  struct bgp_advertise_attr *baa;
+
+  /* BGP info.  */
+  struct bgp_info *binfo;
+};
+
+/* BGP adjacency out.  */
+struct bgp_adj_out
+{
+  /* Lined list pointer.  */
+  struct bgp_adj_out *next;
+  struct bgp_adj_out *prev;
+
+  /* Advertised peer.  */
+  struct peer *peer;
+
+  /* Advertised attribute.  */
+  struct attr *attr;
+
+  /* Advertisement information.  */
+  struct bgp_advertise *adv;
+};
+
+/* BGP adjacency in. */
+struct bgp_adj_in
+{
+  /* Linked list pointer.  */
+  struct bgp_adj_in *next;
+  struct bgp_adj_in *prev;
+
+  /* Received peer.  */
+  struct peer *peer;
+
+  /* Received attribute.  */
+  struct attr *attr;
+};
+
+/* BGP advertisement list.  */
+struct bgp_synchronize
+{
+  struct fifo update;
+  struct fifo withdraw;
+  struct fifo withdraw_low;
+};
+
+#define BGP_ADV_FIFO_HEAD(F) ((struct bgp_advertise *)FIFO_HEAD(F))
+
+/* BGP adjacency linked list.  */
+#define BGP_INFO_ADD(N,A,TYPE)                        \
+  do {                                                \
+    (A)->prev = NULL;                                 \
+    (A)->next = (N)->TYPE;                            \
+    if ((N)->TYPE)                                    \
+      (N)->TYPE->prev = (A);                          \
+    (N)->TYPE = (A);                                  \
+  } while (0)
+
+#define BGP_INFO_DEL(N,A,TYPE)                        \
+  do {                                                \
+    if ((A)->next)                                    \
+      (A)->next->prev = (A)->prev;                    \
+    if ((A)->prev)                                    \
+      (A)->prev->next = (A)->next;                    \
+    else                                              \
+      (N)->TYPE = (A)->next;                          \
+  } while (0)
+
+#define BGP_ADJ_IN_ADD(N,A)    BGP_INFO_ADD(N,A,adj_in)
+#define BGP_ADJ_IN_DEL(N,A)    BGP_INFO_DEL(N,A,adj_in)
+#define BGP_ADJ_OUT_ADD(N,A)   BGP_INFO_ADD(N,A,adj_out)
+#define BGP_ADJ_OUT_DEL(N,A)   BGP_INFO_DEL(N,A,adj_out)
+
+#define BGP_ADV_FIFO_ADD(F, N)                 \
+  do {                                         \
+    FIFO_ADD((F), (N));                                \
+    (F)->count++;                               \
+  } while (0)
+
+#define BGP_ADV_FIFO_DEL(F, N)                 \
+  do {                                         \
+    FIFO_DEL((N));                             \
+    (F)->count--;                              \
+  } while (0)
+
+#define BGP_ADV_FIFO_INIT(F)                   \
+  do {                                         \
+    FIFO_INIT((F));                            \
+    (F)->count = 0;                            \
+  } while (0)
+
+/* Prototypes.  */
+extern void bgp_adj_out_set (struct bgp_node *, struct peer *, struct prefix *,
+                     struct attr *, afi_t, safi_t, struct bgp_info *);
+extern void bgp_adj_out_unset (struct bgp_node *, struct peer *, struct prefix *,
+                       afi_t, safi_t);
+extern void bgp_adj_out_remove (struct bgp_node *, struct bgp_adj_out *, 
+                        struct peer *, afi_t, safi_t);
+extern int bgp_adj_out_lookup (struct peer *, struct prefix *, afi_t, safi_t,
+                       struct bgp_node *);
+
+extern void bgp_adj_in_set (struct bgp_node *, struct peer *, struct attr *);
+extern int bgp_adj_in_unset (struct bgp_node *, struct peer *);
+extern void bgp_adj_in_remove (struct bgp_node *, struct bgp_adj_in *);
+
+extern struct bgp_advertise *
+bgp_advertise_clean (struct peer *, struct bgp_adj_out *, afi_t, safi_t);
+
+extern void bgp_sync_init (struct peer *);
+extern void bgp_sync_delete (struct peer *);
+
+#endif /* _QUAGGA_BGP_ADVERTISE_H */
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
new file mode 100644 (file)
index 0000000..d813bfb
--- /dev/null
@@ -0,0 +1,2076 @@
+/* AS path management routines.
+   Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+   Copyright (C) 2005 Sun Microsystems, Inc.
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "vector.h"
+#include "vty.h"
+#include "str.h"
+#include "log.h"
+#include "stream.h"
+#include "jhash.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_attr.h"
+
+/* Attr. Flags and Attr. Type Code. */
+#define AS_HEADER_SIZE        2         
+
+/* Now FOUR octets are used for AS value. */
+#define AS_VALUE_SIZE         sizeof (as_t)
+/* This is the old one */
+#define AS16_VALUE_SIZE              sizeof (as16_t)
+
+/* Maximum protocol segment length value */
+#define AS_SEGMENT_MAX         255
+
+/* The following length and size macros relate specifically to Quagga's
+ * internal representation of AS-Segments, not per se to the on-wire
+ * sizes and lengths.  At present (200508) they sort of match, however
+ * the ONLY functions which should now about the on-wire syntax are
+ * aspath_put, assegment_put and assegment_parse.
+ *
+ * aspath_put returns bytes written, the only definitive record of
+ * size of wire-format attribute..
+ */
+
+/* Calculated size in bytes of ASN segment data to hold N ASN's */
+#define ASSEGMENT_DATA_SIZE(N,S) \
+       ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) )
+
+/* Calculated size of segment struct to hold N ASN's */
+#define ASSEGMENT_SIZE(N,S)  (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S))
+
+/* AS segment octet length. */
+#define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S)
+
+/* AS_SEQUENCE segments can be packed together */
+/* Can the types of X and Y be considered for packing? */
+#define ASSEGMENT_TYPES_PACKABLE(X,Y) \
+  ( ((X)->type == (Y)->type) \
+   && ((X)->type == AS_SEQUENCE))
+/* Types and length of X,Y suitable for packing? */
+#define ASSEGMENTS_PACKABLE(X,Y) \
+  ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \
+   && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) )
+
+/* As segment header - the on-wire representation 
+ * NOT the internal representation!
+ */
+struct assegment_header
+{
+  u_char type;
+  u_char length;
+};
+
+/* Hash for aspath.  This is the top level structure of AS path. */
+static struct hash *ashash;
+
+/* Stream for SNMP. See aspath_snmp_pathseg */
+static struct stream *snmp_stream;
+
+/* Callers are required to initialize the memory */
+static as_t *
+assegment_data_new (int num)
+{
+  return (XMALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1)));
+}
+
+static void
+assegment_data_free (as_t *asdata)
+{
+  XFREE (MTYPE_AS_SEG_DATA, asdata);
+}
+
+/* Get a new segment. Note that 0 is an allowed length,
+ * and will result in a segment with no allocated data segment.
+ * the caller should immediately assign data to the segment, as the segment
+ * otherwise is not generally valid
+ */
+static struct assegment *
+assegment_new (u_char type, u_short length)
+{
+  struct assegment *new;
+  
+  new = XCALLOC (MTYPE_AS_SEG, sizeof (struct assegment));
+  
+  if (length)
+    new->as = assegment_data_new (length);
+  
+  new->length = length;
+  new->type = type;
+  
+  return new;
+}
+
+static void
+assegment_free (struct assegment *seg)
+{
+  if (!seg)
+    return;
+  
+  if (seg->as)
+    assegment_data_free (seg->as);
+  memset (seg, 0xfe, sizeof(struct assegment));
+  XFREE (MTYPE_AS_SEG, seg);
+  
+  return;
+}
+
+/* free entire chain of segments */
+static void
+assegment_free_all (struct assegment *seg)
+{
+  struct assegment *prev;
+  
+  while (seg)
+    {
+      prev = seg;
+      seg = seg->next;
+      assegment_free (prev);
+    }
+}
+
+/* Duplicate just the given assegment and its data */
+static struct assegment *
+assegment_dup (struct assegment *seg)
+{
+  struct assegment *new;
+  
+  new = assegment_new (seg->type, seg->length);
+  memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) );
+    
+  return new;
+}
+
+/* Duplicate entire chain of assegments, return the head */
+static struct assegment *
+assegment_dup_all (struct assegment *seg)
+{
+  struct assegment *new = NULL;
+  struct assegment *head = NULL;
+  
+  while (seg)
+    {
+      if (head)
+        {
+          new->next = assegment_dup (seg);
+          new = new->next;
+        }
+      else
+        head = new = assegment_dup (seg);
+      
+      seg = seg->next;
+    }
+  return head;
+}
+
+/* prepend the as number to given segment, given num of times */
+static struct assegment *
+assegment_prepend_asns (struct assegment *seg, as_t asnum, int num)
+{
+  as_t *newas;
+  int i;
+  
+  if (!num)
+    return seg;
+  
+  if (num >= AS_SEGMENT_MAX)
+    return seg; /* we don't do huge prepends */
+  
+  if ((newas = assegment_data_new (seg->length + num)) == NULL)
+    return seg;
+  
+  for (i = 0; i < num; i++)
+    newas[i] = asnum;
+
+  memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1));
+  assegment_data_free (seg->as);
+  seg->as = newas;
+  seg->length += num;
+
+  return seg;
+}
+
+/* append given array of as numbers to the segment */
+static struct assegment *
+assegment_append_asns (struct assegment *seg, as_t *asnos, int num)
+{
+  as_t *newas;
+  
+  newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as,
+                     ASSEGMENT_DATA_SIZE (seg->length + num, 1));
+
+  if (newas)
+    {
+      seg->as = newas;
+      memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1));
+      seg->length += num;
+      return seg;
+    }
+
+  assegment_free_all (seg);
+  return NULL;
+}
+
+static int
+int_cmp (const void *p1, const void *p2)
+{
+  const as_t *as1 = p1;
+  const as_t *as2 = p2;
+  
+  return (*as1 == *as2) 
+          ? 0 : ( (*as1 > *as2) ? 1 : -1);
+}
+
+/* normalise the segment.
+ * In particular, merge runs of AS_SEQUENCEs into one segment
+ * Internally, we do not care about the wire segment length limit, and
+ * we want each distinct AS_PATHs to have the exact same internal
+ * representation - eg, so that our hashing actually works..
+ */
+static struct assegment *
+assegment_normalise (struct assegment *head)
+{
+  struct assegment *seg = head, *pin;
+  struct assegment *tmp;
+  
+  if (!head)
+    return head;
+  
+  while (seg)
+    {
+      pin = seg;
+      
+      /* Sort values SET segments, for determinism in paths to aid
+       * creation of hash values / path comparisons
+       * and because it helps other lesser implementations ;)
+       */
+      if (seg->type == AS_SET || seg->type == AS_CONFED_SET)
+       {
+         int tail = 0;
+         int i;
+         
+         qsort (seg->as, seg->length, sizeof(as_t), int_cmp);
+         
+         /* weed out dupes */
+         for (i=1; i < seg->length; i++)
+           {
+             if (seg->as[tail] == seg->as[i])
+               continue;
+             
+             tail++;
+             if (tail < i)
+               seg->as[tail] = seg->as[i];
+           }
+         /* seg->length can be 0.. */
+         if (seg->length)
+           seg->length = tail + 1;
+       }
+
+      /* read ahead from the current, pinned segment while the segments
+       * are packable/mergeable. Append all following packable segments
+       * to the segment we have pinned and remove these appended
+       * segments.
+       */      
+      while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next))
+        {
+          tmp = pin->next;
+          seg = pin->next;
+          
+          /* append the next sequence to the pinned sequence */
+          pin = assegment_append_asns (pin, seg->as, seg->length);
+          
+          /* bypass the next sequence */
+          pin->next = seg->next;
+          
+          /* get rid of the now referenceless segment */
+          assegment_free (tmp);
+          
+        }
+
+      seg = pin->next;
+    }
+  return head;
+}
+
+static struct aspath *
+aspath_new (void)
+{
+  return XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
+}
+
+/* Free AS path structure. */
+void
+aspath_free (struct aspath *aspath)
+{
+  if (!aspath)
+    return;
+  if (aspath->segments)
+    assegment_free_all (aspath->segments);
+  if (aspath->str)
+    XFREE (MTYPE_AS_STR, aspath->str);
+  XFREE (MTYPE_AS_PATH, aspath);
+}
+
+/* Unintern aspath from AS path bucket. */
+void
+aspath_unintern (struct aspath **aspath)
+{
+  struct aspath *ret;
+  struct aspath *asp = *aspath;
+  
+  if (asp->refcnt)
+    asp->refcnt--;
+
+  if (asp->refcnt == 0)
+    {
+      /* This aspath must exist in aspath hash table. */
+      ret = hash_release (ashash, asp);
+      assert (ret != NULL);
+      aspath_free (asp);
+      *aspath = NULL;
+    }
+}
+
+/* Return the start or end delimiters for a particular Segment type */
+#define AS_SEG_START 0
+#define AS_SEG_END 1
+static char
+aspath_delimiter_char (u_char type, u_char which)
+{
+  int i;
+  struct
+  {
+    int type;
+    char start;
+    char end;
+  } aspath_delim_char [] =
+    {
+      { AS_SET,             '{', '}' },
+      { AS_CONFED_SET,      '[', ']' },
+      { AS_CONFED_SEQUENCE, '(', ')' },
+      { 0 }
+    };
+
+  for (i = 0; aspath_delim_char[i].type != 0; i++)
+    {
+      if (aspath_delim_char[i].type == type)
+       {
+         if (which == AS_SEG_START)
+           return aspath_delim_char[i].start;
+         else if (which == AS_SEG_END)
+           return aspath_delim_char[i].end;
+       }
+    }
+  return ' ';
+}
+
+/* countup asns from this segment and index onward */
+static int
+assegment_count_asns (struct assegment *seg, int from)
+{
+  int count = 0;
+  while (seg)
+    {
+      if (!from)
+        count += seg->length;
+      else
+        {
+          count += (seg->length - from);
+          from = 0;
+        }
+      seg = seg->next;
+    }
+  return count;
+}
+
+unsigned int
+aspath_count_confeds (struct aspath *aspath)
+{
+  int count = 0;
+  struct assegment *seg = aspath->segments;
+  
+  while (seg)
+    {
+      if (seg->type == AS_CONFED_SEQUENCE)
+        count += seg->length;
+      else if (seg->type == AS_CONFED_SET)
+        count++;
+      
+      seg = seg->next;
+    }
+  return count;
+}
+
+unsigned int
+aspath_count_hops (const struct aspath *aspath)
+{
+  int count = 0;
+  struct assegment *seg = aspath->segments;
+  
+  while (seg)
+    {
+      if (seg->type == AS_SEQUENCE)
+        count += seg->length;
+      else if (seg->type == AS_SET)
+        count++;
+      
+      seg = seg->next;
+    }
+  return count;
+}
+
+/* Estimate size aspath /might/ take if encoded into an
+ * ASPATH attribute.
+ *
+ * This is a quick estimate, not definitive! aspath_put()
+ * may return a different number!!
+ */
+unsigned int
+aspath_size (struct aspath *aspath)
+{
+  int size = 0;
+  struct assegment *seg = aspath->segments;
+  
+  while (seg)
+    {
+      size += ASSEGMENT_SIZE(seg->length, 1);
+      seg = seg->next;
+    }
+  return size;
+}
+
+/* Return highest public ASN in path */
+as_t
+aspath_highest (struct aspath *aspath)
+{
+  struct assegment *seg = aspath->segments;
+  as_t highest = 0;
+  unsigned int i;
+  
+  while (seg)
+    {
+      for (i = 0; i < seg->length; i++)
+        if (seg->as[i] > highest 
+            && !BGP_AS_IS_PRIVATE(seg->as[i]))
+         highest = seg->as[i];
+      seg = seg->next;
+    }
+  return highest;
+}
+
+/* Return the left-most ASN in path */
+as_t
+aspath_leftmost (struct aspath *aspath)
+{
+  struct assegment *seg = aspath->segments;
+  as_t leftmost = 0;
+
+  if (seg && seg->length && seg->type == AS_SEQUENCE)
+    leftmost = seg->as[0];
+
+  return leftmost;
+}
+
+/* Return 1 if there are any 4-byte ASes in the path */
+unsigned int
+aspath_has_as4 (struct aspath *aspath)
+{
+  struct assegment *seg = aspath->segments;
+  unsigned int i;
+  
+  while (seg)
+    {
+      for (i = 0; i < seg->length; i++)
+        if (seg->as[i] > BGP_AS_MAX)
+         return 1;
+      seg = seg->next;
+    }
+  return 0;
+}
+
+/* Convert aspath structure to string expression. */
+static void
+aspath_make_str_count (struct aspath *as)
+{
+  struct assegment *seg;
+  int str_size;
+  int len = 0;
+  char *str_buf;
+
+  /* Empty aspath. */
+  if (!as->segments)
+    {
+      as->str = XMALLOC (MTYPE_AS_STR, 1);
+      as->str[0] = '\0';
+      as->str_len = 0;
+      return;
+    }
+  
+  seg = as->segments;
+  
+  /* ASN takes 5 to 10 chars plus seperator, see below.
+   * If there is one differing segment type, we need an additional
+   * 2 chars for segment delimiters, and the final '\0'.
+   * Hopefully this is large enough to avoid hitting the realloc
+   * code below for most common sequences.
+   *
+   * This was changed to 10 after the well-known BGP assertion, which
+   * had hit some parts of the Internet in May of 2009.
+   */
+#define ASN_STR_LEN (10 + 1)
+  str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1,
+                  ASPATH_STR_DEFAULT_LEN);
+  str_buf = XMALLOC (MTYPE_AS_STR, str_size);
+
+  while (seg)
+    {
+      int i;
+      char seperator;
+      
+      /* Check AS type validity. Set seperator for segment */
+      switch (seg->type)
+        {
+          case AS_SET:
+          case AS_CONFED_SET:
+            seperator = ',';
+            break;
+          case AS_SEQUENCE:
+          case AS_CONFED_SEQUENCE:
+            seperator = ' ';
+            break;
+          default:
+            XFREE (MTYPE_AS_STR, str_buf);
+            as->str = NULL;
+            as->str_len = 0;
+            return;
+        }
+      
+      /* We might need to increase str_buf, particularly if path has
+       * differing segments types, our initial guesstimate above will
+       * have been wrong. Need 10 chars for ASN, a seperator each and
+       * potentially two segment delimiters, plus a space between each
+       * segment and trailing zero.
+       *
+       * This definitely didn't work with the value of 5 bytes and
+       * 32-bit ASNs.
+       */
+#define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1)
+      if ( (len + SEGMENT_STR_LEN(seg)) > str_size)
+        {
+          str_size = len + SEGMENT_STR_LEN(seg);
+          str_buf = XREALLOC (MTYPE_AS_STR, str_buf, str_size);
+        }
+#undef ASN_STR_LEN
+#undef SEGMENT_STR_LEN
+      
+      if (seg->type != AS_SEQUENCE)
+        len += snprintf (str_buf + len, str_size - len, 
+                        "%c", 
+                         aspath_delimiter_char (seg->type, AS_SEG_START));
+      
+      /* write out the ASNs, with their seperators, bar the last one*/
+      for (i = 0; i < seg->length; i++)
+        {
+          len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]);
+          
+          if (i < (seg->length - 1))
+            len += snprintf (str_buf + len, str_size - len, "%c", seperator);
+        }
+      
+      if (seg->type != AS_SEQUENCE)
+        len += snprintf (str_buf + len, str_size - len, "%c", 
+                        aspath_delimiter_char (seg->type, AS_SEG_END));
+      if (seg->next)
+        len += snprintf (str_buf + len, str_size - len, " ");
+      
+      seg = seg->next;
+    }
+  
+  assert (len < str_size);
+  
+  str_buf[len] = '\0';
+  as->str = str_buf;
+  as->str_len = len;
+
+  return;
+}
+
+static void
+aspath_str_update (struct aspath *as)
+{
+  if (as->str)
+    XFREE (MTYPE_AS_STR, as->str);
+  aspath_make_str_count (as);
+}
+
+/* Intern allocated AS path. */
+struct aspath *
+aspath_intern (struct aspath *aspath)
+{
+  struct aspath *find;
+
+  /* Assert this AS path structure is not interned and has the string
+     representation built. */
+  assert (aspath->refcnt == 0);
+  assert (aspath->str);
+
+  /* Check AS path hash. */
+  find = hash_get (ashash, aspath, hash_alloc_intern);
+  if (find != aspath)
+    aspath_free (aspath);
+
+  find->refcnt++;
+
+  return find;
+}
+
+/* Duplicate aspath structure.  Created same aspath structure but
+   reference count and AS path string is cleared. */
+struct aspath *
+aspath_dup (struct aspath *aspath)
+{
+  unsigned short buflen = aspath->str_len + 1;
+  struct aspath *new;
+
+  new = XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
+
+  if (aspath->segments)
+    new->segments = assegment_dup_all (aspath->segments);
+
+  if (!aspath->str)
+    return new;
+
+  new->str = XMALLOC (MTYPE_AS_STR, buflen);
+  new->str_len = aspath->str_len;
+
+  /* copy the string data */
+  if (aspath->str_len > 0)
+    memcpy (new->str, aspath->str, buflen);
+  else
+    new->str[0] = '\0';
+
+  return new;
+}
+
+static void *
+aspath_hash_alloc (void *arg)
+{
+  const struct aspath *aspath = arg;
+  struct aspath *new;
+
+  /* Malformed AS path value. */
+  assert (aspath->str);
+  if (! aspath->str)
+    return NULL;
+
+  /* New aspath structure is needed. */
+  new = XMALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
+
+  /* Reuse segments and string representation */
+  new->refcnt = 0;
+  new->segments = aspath->segments;
+  new->str = aspath->str;
+  new->str_len = aspath->str_len;
+
+  return new;
+}
+
+/* parse as-segment byte stream in struct assegment */
+static int
+assegments_parse (struct stream *s, size_t length, 
+                  struct assegment **result, int use32bit)
+{
+  struct assegment_header segh;
+  struct assegment *seg, *prev = NULL, *head = NULL;
+  size_t bytes = 0;
+  
+  /* empty aspath (ie iBGP or somesuch) */
+  if (length == 0)
+    return 0;
+  
+  if (BGP_DEBUG (as4, AS4_SEGMENT))
+    zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
+               (unsigned long) length);
+  /* basic checks */
+  if ((STREAM_READABLE(s) < length)
+      || (STREAM_READABLE(s) < AS_HEADER_SIZE) 
+      || (length % AS16_VALUE_SIZE ))
+    return -1;
+  
+  while (bytes < length)
+    {
+      int i;
+      size_t seg_size;
+      
+      if ((length - bytes) <= AS_HEADER_SIZE)
+        {
+          if (head)
+            assegment_free_all (head);
+          return -1;
+        }
+      
+      /* softly softly, get the header first on its own */
+      segh.type = stream_getc (s);
+      segh.length = stream_getc (s);
+      
+      seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
+
+      if (BGP_DEBUG (as4, AS4_SEGMENT))
+       zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
+                    segh.type, segh.length);
+      
+      /* check it.. */
+      if ( ((bytes + seg_size) > length)
+          /* 1771bis 4.3b: seg length contains one or more */
+          || (segh.length == 0) 
+          /* Paranoia in case someone changes type of segment length.
+           * Shift both values by 0x10 to make the comparison operate
+           * on more, than 8 bits (otherwise it's a warning, bug #564).
+           */
+          || ((sizeof segh.length > 1) 
+              && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX)))
+        {
+          if (head)
+            assegment_free_all (head);
+          return -1;
+        }
+      
+      switch (segh.type)
+        {
+          case AS_SEQUENCE:
+          case AS_SET:
+          case AS_CONFED_SEQUENCE:
+          case AS_CONFED_SET:
+            break;
+          default:
+            if (head)
+              assegment_free_all (head);
+            return -1;
+        }
+      
+      /* now its safe to trust lengths */
+      seg = assegment_new (segh.type, segh.length);
+      
+      if (head)
+        prev->next = seg;
+      else /* it's the first segment */
+        head = prev = seg;
+      
+      for (i = 0; i < segh.length; i++)
+       seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
+
+      bytes += seg_size;
+      
+      if (BGP_DEBUG (as4, AS4_SEGMENT))
+       zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu",
+                   (unsigned long) bytes);
+      
+      prev = seg;
+    }
+  *result = assegment_normalise (head);
+  return 0;
+}
+
+/* AS path parse function.  pnt is a pointer to byte stream and length
+   is length of byte stream.  If there is same AS path in the the AS
+   path hash then return it else make new AS path structure. 
+   
+   On error NULL is returned.
+ */
+struct aspath *
+aspath_parse (struct stream *s, size_t length, int use32bit)
+{
+  struct aspath as;
+  struct aspath *find;
+
+  /* If length is odd it's malformed AS path. */
+  /* Nit-picking: if (use32bit == 0) it is malformed if odd,
+   * otherwise its malformed when length is larger than 2 and (length-2) 
+   * is not dividable by 4.
+   * But... this time we're lazy
+   */
+  if (length % AS16_VALUE_SIZE )
+    return NULL;
+
+  memset (&as, 0, sizeof (struct aspath));
+  if (assegments_parse (s, length, &as.segments, use32bit) < 0)
+    return NULL;
+
+  /* If already same aspath exist then return it. */
+  find = hash_get (ashash, &as, aspath_hash_alloc);
+
+  /* bug! should not happen, let the daemon crash below */
+  assert (find);
+
+  /* if the aspath was already hashed free temporary memory. */
+  if (find->refcnt)
+    {
+      assegment_free_all (as.segments);
+      /* aspath_key_make() always updates the string */
+      XFREE (MTYPE_AS_STR, as.str);
+    }
+
+  find->refcnt++;
+
+  return find;
+}
+
+static void
+assegment_data_put (struct stream *s, as_t *as, int num, int use32bit)
+{
+  int i;
+  assert (num <= AS_SEGMENT_MAX);
+  
+  for (i = 0; i < num; i++)
+    if ( use32bit )
+      stream_putl (s, as[i]);
+    else
+      {
+        if ( as[i] <= BGP_AS_MAX )
+         stream_putw(s, as[i]);
+       else
+         stream_putw(s, BGP_AS_TRANS);
+      }
+}
+
+static size_t
+assegment_header_put (struct stream *s, u_char type, int length)
+{
+  size_t lenp;
+  assert (length <= AS_SEGMENT_MAX);
+  stream_putc (s, type);
+  lenp = stream_get_endp (s);
+  stream_putc (s, length);
+  return lenp;
+}
+
+/* write aspath data to stream */
+size_t
+aspath_put (struct stream *s, struct aspath *as, int use32bit )
+{
+  struct assegment *seg = as->segments;
+  size_t bytes = 0;
+  
+  if (!seg || seg->length == 0)
+    return 0;
+  
+  if (seg)
+    {
+      /*
+       * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
+       * At the moment, we would write out a partial aspath, and our peer
+       * will complain and drop the session :-/
+       *
+       * The general assumption here is that many things tested will
+       * never happen.  And, in real live, up to now, they have not.
+       */
+      while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s)))
+        {
+          struct assegment *next = seg->next;
+          int written = 0;
+          int asns_packed = 0;
+          size_t lenp;
+          
+          /* Overlength segments have to be split up */
+          while ( (seg->length - written) > AS_SEGMENT_MAX)
+            {
+              assegment_header_put (s, seg->type, AS_SEGMENT_MAX);
+              assegment_data_put (s, seg->as, AS_SEGMENT_MAX, use32bit);
+              written += AS_SEGMENT_MAX;
+              bytes += ASSEGMENT_SIZE (AS_SEGMENT_MAX, use32bit);
+            }
+          
+          /* write the final segment, probably is also the first */
+          lenp = assegment_header_put (s, seg->type, seg->length - written);
+          assegment_data_put (s, (seg->as + written), seg->length - written, 
+                              use32bit);
+          
+          /* Sequence-type segments can be 'packed' together
+           * Case of a segment which was overlength and split up
+           * will be missed here, but that doesn't matter.
+           */
+          while (next && ASSEGMENTS_PACKABLE (seg, next))
+            {
+              /* NB: We should never normally get here given we
+               * normalise aspath data when parse them. However, better
+               * safe than sorry. We potentially could call
+               * assegment_normalise here instead, but it's cheaper and
+               * easier to do it on the fly here rather than go through
+               * the segment list twice every time we write out
+               * aspath's.
+               */
+              
+              /* Next segment's data can fit in this one */
+              assegment_data_put (s, next->as, next->length, use32bit);
+              
+              /* update the length of the segment header */
+             stream_putc_at (s, lenp, seg->length - written + next->length);
+              asns_packed += next->length;
+               
+             next = next->next;
+           }
+          
+          bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed, 
+                                  use32bit);
+          seg = next;
+        }
+    }
+  return bytes;
+}
+
+/* This is for SNMP BGP4PATHATTRASPATHSEGMENT
+ * We have no way to manage the storage, so we use a static stream
+ * wrapper around aspath_put.
+ */
+u_char *
+aspath_snmp_pathseg (struct aspath *as, size_t *varlen)
+{
+#define SNMP_PATHSEG_MAX 1024
+
+  if (!snmp_stream)
+    snmp_stream = stream_new (SNMP_PATHSEG_MAX);
+  else
+    stream_reset (snmp_stream);
+  
+  if (!as)
+    {
+      *varlen = 0;
+      return NULL;
+    }
+  aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */
+  
+  *varlen = stream_get_endp (snmp_stream);
+  return stream_pnt(snmp_stream);
+}
+      
+#define min(A,B) ((A) < (B) ? (A) : (B))
+
+static struct assegment *
+aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset,
+                            as_t as)
+{
+  int i;
+
+  /* If this is first AS set member, create new as-set segment. */
+  if (asset == NULL)
+    {
+      asset = assegment_new (AS_SET, 1);
+      if (! aspath->segments)
+       aspath->segments = asset;
+      else
+        {
+          struct assegment *seg = aspath->segments;
+          while (seg->next)
+            seg = seg->next;
+          seg->next = asset;
+        }
+      asset->type = AS_SET;
+      asset->length = 1;
+      asset->as[0] = as;
+    }
+  else
+    {
+      /* Check this AS value already exists or not. */
+      for (i = 0; i < asset->length; i++)
+       if (asset->as[i] == as)
+         return asset;
+      
+      asset->length++;
+      asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as, 
+                            asset->length * AS_VALUE_SIZE);
+      asset->as[asset->length - 1] = as;
+    }
+  
+
+  return asset;
+}
+
+/* Modify as1 using as2 for aggregation. */
+struct aspath *
+aspath_aggregate (struct aspath *as1, struct aspath *as2)
+{
+  int i;
+  int minlen;
+  int match;
+  int from;
+  struct assegment *seg1 = as1->segments;
+  struct assegment *seg2 = as2->segments;
+  struct aspath *aspath = NULL;
+  struct assegment *asset;
+  struct assegment *prevseg = NULL;
+
+  match = 0;
+  minlen = 0;
+  aspath = NULL;
+  asset = NULL;
+
+  /* First of all check common leading sequence. */
+  while (seg1 && seg2)
+    {      
+      /* Check segment type. */
+      if (seg1->type != seg2->type)
+       break;
+
+      /* Minimum segment length. */
+      minlen = min (seg1->length, seg2->length);
+
+      for (match = 0; match < minlen; match++)
+       if (seg1->as[match] != seg2->as[match])
+         break;
+
+      if (match)
+       {
+         struct assegment *seg = assegment_new (seg1->type, 0);
+         
+         seg = assegment_append_asns (seg, seg1->as, match);
+
+         if (! aspath)
+           {
+             aspath = aspath_new ();
+             aspath->segments = seg;
+            }
+         else
+           prevseg->next = seg;
+         
+         prevseg = seg;
+       }
+
+      if (match != minlen || match != seg1->length 
+         || seg1->length != seg2->length)
+       break;
+      /* We are moving on to the next segment to reset match */
+      else
+        match = 0;
+      
+      seg1 = seg1->next;
+      seg2 = seg2->next;
+    }
+
+  if (! aspath)
+    aspath = aspath_new();
+
+  /* Make as-set using rest of all information. */
+  from = match;
+  while (seg1)
+    {
+      for (i = from; i < seg1->length; i++)
+       asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]);
+      
+      from = 0;
+      seg1 = seg1->next;
+    }
+
+  from = match;
+  while (seg2)
+    {
+      for (i = from; i < seg2->length; i++)
+       asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]);
+
+      from = 0;
+      seg2 = seg2->next;
+    }
+  
+  assegment_normalise (aspath->segments);
+  aspath_str_update (aspath);
+  return aspath;
+}
+
+/* Modify as1 using as2 for aggregation for multipath, keeping the
+ * AS-Path length the same, and so minimising change to the preference
+ * of the mpath aggregrate route.
+ */
+struct aspath *
+aspath_aggregate_mpath (struct aspath *as1, struct aspath *as2)
+{
+  int i;
+  int minlen;
+  int match;
+  int from1,from2;
+  struct assegment *seg1 = as1->segments;
+  struct assegment *seg2 = as2->segments;
+  struct aspath *aspath = NULL;
+  struct assegment *asset;
+  struct assegment *prevseg = NULL;
+
+  match = 0;
+  minlen = 0;
+  aspath = NULL;
+  asset = NULL;
+
+  /* First of all check common leading sequence. */
+  while (seg1 && seg2)
+    {
+      /* Check segment type. */
+      if (seg1->type != seg2->type)
+       break;
+
+      /* Minimum segment length. */
+      minlen = min (seg1->length, seg2->length);
+
+      for (match = 0; match < minlen; match++)
+       if (seg1->as[match] != seg2->as[match])
+         break;
+
+      if (match)
+       {
+         struct assegment *seg = assegment_new (seg1->type, 0);
+
+         seg = assegment_append_asns (seg, seg1->as, match);
+
+         if (! aspath)
+           {
+             aspath = aspath_new ();
+             aspath->segments = seg;
+            }
+         else
+           prevseg->next = seg;
+
+         prevseg = seg;
+       }
+
+      if (match != minlen || match != seg1->length
+         || seg1->length != seg2->length)
+       break;
+
+      seg1 = seg1->next;
+      seg2 = seg2->next;
+    }
+
+  if (! aspath)
+    aspath = aspath_new();
+
+  /* Make as-set using rest of all information. */
+  from1 = from2 = match;
+  while (seg1 || seg2)
+    {
+      if (seg1)
+       {
+         if (seg1->type == AS_SEQUENCE)
+           {
+             asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[from1]);
+             from1++;
+             if (from1 >= seg1->length)
+               {
+                 from1 = 0;
+                 seg1 = seg1->next;
+               }
+           }
+         else
+           {
+             for (i = from1; i < seg1->length; i++)
+               asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]);
+
+             from1 = 0;
+             seg1 = seg1->next;
+           }
+         }
+
+      if (seg2)
+       {
+         if (seg2->type == AS_SEQUENCE)
+           {
+             asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[from2]);
+             from2++;
+             if (from2 >= seg2->length)
+               {
+                 from2 = 0;
+                 seg2 = seg2->next;
+               }
+           }
+         else
+           {
+             for (i = from2; i < seg2->length; i++)
+               asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]);
+
+             from2 = 0;
+             seg2 = seg2->next;
+           }
+       }
+
+      if (asset->length == 1)
+       asset->type = AS_SEQUENCE;
+      asset = NULL;
+    }
+
+  assegment_normalise (aspath->segments);
+  aspath_str_update (aspath);
+  return aspath;
+}
+
+/* When a BGP router receives an UPDATE with an MP_REACH_NLRI
+   attribute, check the leftmost AS number in the AS_PATH attribute is
+   or not the peer's AS number. */ 
+int
+aspath_firstas_check (struct aspath *aspath, as_t asno)
+{
+  if ( (aspath == NULL) || (aspath->segments == NULL) )
+    return 0;
+  
+  if (aspath->segments
+      && (aspath->segments->type == AS_SEQUENCE)
+      && (aspath->segments->as[0] == asno ))
+    return 1;
+
+  return 0;
+}
+
+/* AS path loop check.  If aspath contains asno then return >= 1. */
+int
+aspath_loop_check (struct aspath *aspath, as_t asno)
+{
+  struct assegment *seg;
+  int count = 0;
+
+  if ( (aspath == NULL) || (aspath->segments == NULL) )
+    return 0;
+  
+  seg = aspath->segments;
+  
+  while (seg)
+    {
+      int i;
+      
+      for (i = 0; i < seg->length; i++)
+       if (seg->as[i] == asno)
+         count++;
+      
+      seg = seg->next;
+    }
+  return count;
+}
+
+/* When all of AS path is private AS return 1.  */
+int
+aspath_private_as_check (struct aspath *aspath)
+{
+  struct assegment *seg;
+  
+  if ( !(aspath && aspath->segments) )
+    return 0;
+    
+  seg = aspath->segments;
+
+  while (seg)
+    {
+      int i;
+      
+      for (i = 0; i < seg->length; i++)
+       {
+         if (!BGP_AS_IS_PRIVATE(seg->as[i]))
+           return 0;
+       }
+      seg = seg->next;
+    }
+  return 1;
+}
+
+/* AS path confed check.  If aspath contains confed set or sequence then return 1. */
+int
+aspath_confed_check (struct aspath *aspath)
+{
+  struct assegment *seg;
+
+  if ( !(aspath && aspath->segments) )
+    return 0;
+
+  seg = aspath->segments;
+
+  while (seg)
+    {
+      if (seg->type == AS_CONFED_SET || seg->type == AS_CONFED_SEQUENCE)
+         return 1;
+      seg = seg->next;
+    }
+  return 0;
+}
+
+/* Leftmost AS path segment confed check.  If leftmost AS segment is of type
+  AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1.  */
+int
+aspath_left_confed_check (struct aspath *aspath)
+{
+
+  if ( !(aspath && aspath->segments) )
+    return 0;
+
+  if ( (aspath->segments->type == AS_CONFED_SEQUENCE)
+      || (aspath->segments->type == AS_CONFED_SET) )
+    return 1;
+
+  return 0;
+}
+
+/* Merge as1 to as2.  as2 should be uninterned aspath. */
+static struct aspath *
+aspath_merge (struct aspath *as1, struct aspath *as2)
+{
+  struct assegment *last, *new;
+
+  if (! as1 || ! as2)
+    return NULL;
+
+  last = new = assegment_dup_all (as1->segments);
+  
+  /* find the last valid segment */
+  while (last && last->next)
+    last = last->next;
+  
+  last->next = as2->segments;
+  as2->segments = new;
+  aspath_str_update (as2);
+  return as2;
+}
+
+/* Prepend as1 to as2.  as2 should be uninterned aspath. */
+struct aspath *
+aspath_prepend (struct aspath *as1, struct aspath *as2)
+{
+  struct assegment *seg1;
+  struct assegment *seg2;
+
+  if (! as1 || ! as2)
+    return NULL;
+  
+  seg1 = as1->segments;
+  seg2 = as2->segments;
+  
+  /* If as2 is empty, only need to dupe as1's chain onto as2 */
+  if (seg2 == NULL)
+    {
+      as2->segments = assegment_dup_all (as1->segments);
+      aspath_str_update (as2);
+      return as2;
+    }
+  
+  /* If as1 is empty AS, no prepending to do. */
+  if (seg1 == NULL)
+    return as2;
+  
+  /* find the tail as1's segment chain. */
+  while (seg1 && seg1->next)
+    seg1 = seg1->next;
+
+  /* Delete any AS_CONFED_SEQUENCE segment from as2. */
+  if (seg1->type == AS_SEQUENCE && seg2->type == AS_CONFED_SEQUENCE)
+    as2 = aspath_delete_confed_seq (as2);
+  
+  /* as2 may have been updated */
+  seg2 = as2->segments;
+  
+  /* as2 may be empty now due to aspath_delete_confed_seq, recheck */
+  if (seg2 == NULL)
+    {
+      as2->segments = assegment_dup_all (as1->segments);
+      aspath_str_update (as2);
+      return as2;
+    }
+  
+  /* Compare last segment type of as1 and first segment type of as2. */
+  if (seg1->type != seg2->type)
+    return aspath_merge (as1, as2);
+
+  if (seg1->type == AS_SEQUENCE)
+    {
+      /* We have two chains of segments, as1->segments and seg2, 
+       * and we have to attach them together, merging the attaching
+       * segments together into one.
+       * 
+       * 1. dupe as1->segments onto head of as2
+       * 2. merge seg2's asns onto last segment of this new chain
+       * 3. attach chain after seg2
+       */
+      
+      /* dupe as1 onto as2's head */
+      seg1 = as2->segments = assegment_dup_all (as1->segments);
+      
+      /* refind the tail of as2, reusing seg1 */
+      while (seg1 && seg1->next)
+        seg1 = seg1->next;
+      
+      /* merge the old head, seg2, into tail, seg1 */
+      seg1 = assegment_append_asns (seg1, seg2->as, seg2->length);
+      
+      /* bypass the merged seg2, and attach any chain after it to
+       * chain descending from as2's head
+       */
+      seg1->next = seg2->next;
+      
+      /* seg2 is now referenceless and useless*/
+      assegment_free (seg2);
+      
+      /* we've now prepended as1's segment chain to as2, merging
+       * the inbetween AS_SEQUENCE of seg2 in the process 
+       */
+      aspath_str_update (as2);
+      return as2;
+    }
+  else
+    {
+      /* AS_SET merge code is needed at here. */
+      return aspath_merge (as1, as2);
+    }
+  /* XXX: Ermmm, what if as1 has multiple segments?? */
+  
+  /* Not reached */
+}
+
+/* Iterate over AS_PATH segments and wipe all occurences of the
+ * listed AS numbers. Hence some segments may lose some or even
+ * all data on the way, the operation is implemented as a smarter
+ * version of aspath_dup(), which allocates memory to hold the new
+ * data, not the original. The new AS path is returned.
+ */
+struct aspath *
+aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list)
+{
+  struct assegment * srcseg, * exclseg, * lastseg;
+  struct aspath * newpath;
+
+  newpath = aspath_new();
+  lastseg = NULL;
+
+  for (srcseg = source->segments; srcseg; srcseg = srcseg->next)
+  {
+    unsigned i, y, newlen = 0, done = 0, skip_as;
+    struct assegment * newseg;
+
+    /* Find out, how much ASns are we going to pick from this segment.
+     * We can't perform filtering right inline, because the size of
+     * the new segment isn't known at the moment yet.
+     */
+    for (i = 0; i < srcseg->length; i++)
+    {
+      skip_as = 0;
+      for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
+        for (y = 0; y < exclseg->length; y++)
+          if (srcseg->as[i] == exclseg->as[y])
+          {
+            skip_as = 1;
+            // There's no sense in testing the rest of exclusion list, bail out.
+            break;
+          }
+      if (!skip_as)
+        newlen++;
+    }
+    /* newlen is now the number of ASns to copy */
+    if (!newlen)
+      continue;
+
+    /* Actual copying. Allocate memory and iterate once more, performing filtering. */
+    newseg = assegment_new (srcseg->type, newlen);
+    for (i = 0; i < srcseg->length; i++)
+    {
+      skip_as = 0;
+      for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
+        for (y = 0; y < exclseg->length; y++)
+          if (srcseg->as[i] == exclseg->as[y])
+          {
+            skip_as = 1;
+            break;
+          }
+      if (skip_as)
+        continue;
+      newseg->as[done++] = srcseg->as[i];
+    }
+    /* At his point newlen must be equal to done, and both must be positive. Append
+     * the filtered segment to the gross result. */
+    if (!lastseg)
+      newpath->segments = newseg;
+    else
+      lastseg->next = newseg;
+    lastseg = newseg;
+  }
+  aspath_str_update (newpath);
+  /* We are happy returning even an empty AS_PATH, because the administrator
+   * might expect this very behaviour. There's a mean to avoid this, if necessary,
+   * by having a match rule against certain AS_PATH regexps in the route-map index.
+   */
+  aspath_free (source);
+  return newpath;
+}
+
+/* Add specified AS to the leftmost of aspath. */
+static struct aspath *
+aspath_add_asns (struct aspath *aspath, as_t asno, u_char type, unsigned num)
+{
+  struct assegment *assegment = aspath->segments;
+  unsigned i;
+
+  if (assegment && assegment->type == type)
+    {
+      /* extend existing segment */
+      aspath->segments = assegment_prepend_asns (aspath->segments, asno, num);
+    }
+  else 
+    {
+      /* prepend with new segment */
+      struct assegment *newsegment = assegment_new (type, num);
+      for (i = 0; i < num; i++)
+       newsegment->as[i] = asno;
+
+      /* insert potentially replacing empty segment */
+      if (assegment && assegment->length == 0)
+       {
+         newsegment->next = assegment->next;
+         assegment_free (assegment);
+       }
+       else
+         newsegment->next = assegment;
+      aspath->segments = newsegment;
+    }
+
+  aspath_str_update (aspath);
+  return aspath;
+}
+
+/* Add specified AS to the leftmost of aspath num times. */
+struct aspath *
+aspath_add_seq_n (struct aspath *aspath, as_t asno, unsigned num)
+{
+  return aspath_add_asns (aspath, asno, AS_SEQUENCE, num);
+}
+
+/* Add specified AS to the leftmost of aspath. */
+struct aspath *
+aspath_add_seq (struct aspath *aspath, as_t asno)
+{
+  return aspath_add_asns (aspath, asno, AS_SEQUENCE, 1);
+}
+
+/* Compare leftmost AS value for MED check.  If as1's leftmost AS and
+   as2's leftmost AS is same return 1. */
+int
+aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2)
+{
+  const struct assegment *seg1;
+  const struct assegment *seg2;
+
+  if (!(aspath1 && aspath2))
+    return 0;
+
+  seg1 = aspath1->segments;
+  seg2 = aspath2->segments;
+
+  /* If both paths are originated in this AS then we do want to compare MED */
+  if (!seg1 && !seg2)
+    return 1;
+
+  /* find first non-confed segments for each */
+  while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE)
+                 || (seg1->type == AS_CONFED_SET)))
+    seg1 = seg1->next;
+
+  while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE)
+                 || (seg2->type == AS_CONFED_SET)))
+    seg2 = seg2->next;
+
+  /* Check as1's */
+  if (!(seg1 && seg2
+       && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE)))
+    return 0;
+  
+  if (seg1->as[0] == seg2->as[0])
+    return 1;
+
+  return 0;
+}
+
+/* Truncate an aspath after a number of hops, and put the hops remaining
+ * at the front of another aspath.  Needed for AS4 compat.
+ *
+ * Returned aspath is a /new/ aspath, which should either by free'd or
+ * interned by the caller, as desired.
+ */
+struct aspath *
+aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
+{
+  struct assegment *seg, *newseg, *prevseg = NULL;
+  struct aspath *newpath = NULL, *mergedpath;
+  int hops, cpasns = 0;
+  
+  if (!aspath)
+    return NULL;
+  
+  seg = aspath->segments;
+  
+  /* CONFEDs should get reconciled too.. */
+  hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath))
+         - aspath_count_hops (as4path);
+  
+  if (hops < 0)
+    {
+      if (BGP_DEBUG (as4, AS4))
+        zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH");
+      /* Something's gone wrong. The RFC says we should now ignore AS4_PATH,
+       * which is daft behaviour - it contains vital loop-detection
+       * information which must have been removed from AS_PATH.
+       */
+       hops = aspath_count_hops (aspath);
+    }
+  
+  if (!hops)
+   return aspath_dup (as4path);
+  
+  if ( BGP_DEBUG(as4, AS4))
+    zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
+               aspath->str, as4path->str);
+
+  while (seg && hops > 0)
+    {
+      switch (seg->type)
+        {
+          case AS_SET:
+          case AS_CONFED_SET:
+            hops--;
+            cpasns = seg->length;
+            break;
+          case AS_CONFED_SEQUENCE:
+           /* Should never split a confed-sequence, if hop-count
+            * suggests we must then something's gone wrong somewhere.
+            *
+            * Most important goal is to preserve AS_PATHs prime function
+            * as loop-detector, so we fudge the numbers so that the entire
+            * confed-sequence is merged in.
+            */
+           if (hops < seg->length)
+             {
+               if (BGP_DEBUG (as4, AS4))
+                 zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls"
+                             " across 2/4 ASN boundary somewhere, broken..");
+               hops = seg->length;
+             }
+         case AS_SEQUENCE:
+           cpasns = MIN(seg->length, hops);
+           hops -= seg->length;
+       }
+      
+      assert (cpasns <= seg->length);
+      
+      newseg = assegment_new (seg->type, 0);
+      newseg = assegment_append_asns (newseg, seg->as, cpasns);
+
+      if (!newpath)
+        {
+          newpath = aspath_new ();
+          newpath->segments = newseg;
+        }
+      else
+        prevseg->next = newseg;
+
+      prevseg = newseg;
+      seg = seg->next;
+    }
+    
+  /* We may be able to join some segments here, and we must
+   * do this because... we want normalised aspaths in out hash
+   * and we do not want to stumble in aspath_put.
+   */
+  mergedpath = aspath_merge (newpath, aspath_dup(as4path));
+  aspath_free (newpath);
+  mergedpath->segments = assegment_normalise (mergedpath->segments);
+  aspath_str_update (mergedpath);
+  
+  if ( BGP_DEBUG(as4, AS4))
+    zlog_debug ("[AS4] result of synthesizing is %s",
+                mergedpath->str);
+  
+  return mergedpath;
+}
+
+/* Compare leftmost AS value for MED check.  If as1's leftmost AS and
+   as2's leftmost AS is same return 1. (confederation as-path
+   only).  */
+int
+aspath_cmp_left_confed (const struct aspath *aspath1, const struct aspath *aspath2)
+{
+  if (! (aspath1 && aspath2) )
+    return 0;
+  
+  if ( !(aspath1->segments && aspath2->segments) )
+    return 0;
+  
+  if ( (aspath1->segments->type != AS_CONFED_SEQUENCE)
+      || (aspath2->segments->type != AS_CONFED_SEQUENCE) )
+    return 0;
+  
+  if (aspath1->segments->as[0] == aspath2->segments->as[0])
+    return 1;
+
+  return 0;
+}
+
+/* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath.
+ * See RFC3065, 6.1 c1 */
+struct aspath *
+aspath_delete_confed_seq (struct aspath *aspath)
+{
+  struct assegment *seg;
+
+  if (!(aspath && aspath->segments))
+    return aspath;
+
+  seg = aspath->segments;
+  
+  /* "if the first path segment of the AS_PATH is 
+   *  of type AS_CONFED_SEQUENCE,"
+   */
+  if (aspath->segments->type != AS_CONFED_SEQUENCE)
+    return aspath;
+
+  /* "... that segment and any immediately following segments 
+   *  of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed 
+   *  from the AS_PATH attribute,"
+   */
+  while (seg && 
+         (seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET))
+    {
+      aspath->segments = seg->next;
+      assegment_free (seg);
+      seg = aspath->segments;
+    }
+  aspath_str_update (aspath);
+  return aspath;
+}
+
+/* Add new AS number to the leftmost part of the aspath as
+   AS_CONFED_SEQUENCE.  */
+struct aspath*
+aspath_add_confed_seq (struct aspath *aspath, as_t asno)
+{
+  return aspath_add_asns (aspath, asno, AS_CONFED_SEQUENCE, 1);
+}
+
+/* Add new as value to as path structure. */
+static void
+aspath_as_add (struct aspath *as, as_t asno)
+{
+  struct assegment *seg = as->segments;
+
+  if (!seg)
+    return;
+  
+  /* Last segment search procedure. */
+  while (seg->next)
+    seg = seg->next;
+
+  assegment_append_asns (seg, &asno, 1);
+}
+
+/* Add new as segment to the as path. */
+static void
+aspath_segment_add (struct aspath *as, int type)
+{
+  struct assegment *seg = as->segments;
+  struct assegment *new = assegment_new (type, 0);
+
+  if (seg)
+    {
+      while (seg->next)
+       seg = seg->next;
+      seg->next = new;
+    }
+  else
+    as->segments = new;
+}
+
+struct aspath *
+aspath_empty (void)
+{
+  return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */
+}
+
+struct aspath *
+aspath_empty_get (void)
+{
+  struct aspath *aspath;
+
+  aspath = aspath_new ();
+  aspath_make_str_count (aspath);
+  return aspath;
+}
+
+unsigned long
+aspath_count (void)
+{
+  return ashash->count;
+}     
+
+/* 
+   Theoretically, one as path can have:
+
+   One BGP packet size should be less than 4096.
+   One BGP attribute size should be less than 4096 - BGP header size.
+   One BGP aspath size should be less than 4096 - BGP header size -
+       BGP mandantry attribute size.
+*/
+
+/* AS path string lexical token enum. */
+enum as_token
+{
+  as_token_asval,
+  as_token_set_start,
+  as_token_set_end,
+  as_token_confed_seq_start,
+  as_token_confed_seq_end,
+  as_token_confed_set_start,
+  as_token_confed_set_end,
+  as_token_unknown
+};
+
+/* Return next token and point for string parse. */
+static const char *
+aspath_gettoken (const char *buf, enum as_token *token, u_long *asno)
+{
+  const char *p = buf;
+
+  /* Skip seperators (space for sequences, ',' for sets). */
+  while (isspace ((int) *p) || *p == ',')
+    p++;
+
+  /* Check the end of the string and type specify characters
+     (e.g. {}()). */
+  switch (*p)
+    {
+    case '\0':
+      return NULL;
+    case '{':
+      *token = as_token_set_start;
+      p++;
+      return p;
+    case '}':
+      *token = as_token_set_end;
+      p++;
+      return p;
+    case '(':
+      *token = as_token_confed_seq_start;
+      p++;
+      return p;
+    case ')':
+      *token = as_token_confed_seq_end;
+      p++;
+      return p;
+    case '[':
+      *token = as_token_confed_set_start;
+      p++;
+      return p;
+    case ']':
+      *token = as_token_confed_set_end;
+      p++;
+      return p;
+    }
+
+  /* Check actual AS value. */
+  if (isdigit ((int) *p)) 
+    {
+      as_t asval;
+      
+      *token = as_token_asval;
+      asval = (*p - '0');
+      p++;
+      
+      while (isdigit ((int) *p)) 
+        {
+          asval *= 10;
+          asval += (*p - '0');
+          p++;
+        }
+      *asno = asval;
+      return p;
+    }
+  
+  /* There is no match then return unknown token. */
+  *token = as_token_unknown;
+  return  p++;
+}
+
+struct aspath *
+aspath_str2aspath (const char *str)
+{
+  enum as_token token = as_token_unknown;
+  u_short as_type;
+  u_long asno = 0;
+  struct aspath *aspath;
+  int needtype;
+
+  aspath = aspath_new ();
+
+  /* We start default type as AS_SEQUENCE. */
+  as_type = AS_SEQUENCE;
+  needtype = 1;
+
+  while ((str = aspath_gettoken (str, &token, &asno)) != NULL)
+    {
+      switch (token)
+       {
+       case as_token_asval:
+         if (needtype)
+           {
+             aspath_segment_add (aspath, as_type);
+             needtype = 0;
+           }
+         aspath_as_add (aspath, asno);
+         break;
+       case as_token_set_start:
+         as_type = AS_SET;
+         aspath_segment_add (aspath, as_type);
+         needtype = 0;
+         break;
+       case as_token_set_end:
+         as_type = AS_SEQUENCE;
+         needtype = 1;
+         break;
+       case as_token_confed_seq_start:
+         as_type = AS_CONFED_SEQUENCE;
+         aspath_segment_add (aspath, as_type);
+         needtype = 0;
+         break;
+       case as_token_confed_seq_end:
+         as_type = AS_SEQUENCE;
+         needtype = 1;
+         break;
+       case as_token_confed_set_start:
+         as_type = AS_CONFED_SET;
+         aspath_segment_add (aspath, as_type);
+         needtype = 0;
+         break;
+       case as_token_confed_set_end:
+         as_type = AS_SEQUENCE;
+         needtype = 1;
+         break;
+       case as_token_unknown:
+       default:
+         aspath_free (aspath);
+         return NULL;
+       }
+    }
+
+  aspath_make_str_count (aspath);
+
+  return aspath;
+}
+
+/* Make hash value by raw aspath data. */
+unsigned int
+aspath_key_make (void *p)
+{
+  struct aspath *aspath = (struct aspath *) p;
+  unsigned int key = 0;
+
+  if (!aspath->str)
+    aspath_str_update (aspath);
+
+  key = jhash (aspath->str, aspath->str_len, 2334325);
+
+  return key;
+}
+
+/* If two aspath have same value then return 1 else return 0 */
+int
+aspath_cmp (const void *arg1, const void *arg2)
+{
+  const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
+  const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
+  
+  while (seg1 || seg2)
+    {
+      int i;
+      if ((!seg1 && seg2) || (seg1 && !seg2))
+       return 0;
+      if (seg1->type != seg2->type)
+        return 0;      
+      if (seg1->length != seg2->length)
+        return 0;
+      for (i = 0; i < seg1->length; i++)
+        if (seg1->as[i] != seg2->as[i])
+          return 0;
+      seg1 = seg1->next;
+      seg2 = seg2->next;
+    }
+  return 1;
+}
+
+/* AS path hash initialize. */
+void
+aspath_init (void)
+{
+  ashash = hash_create_size (32768, aspath_key_make, aspath_cmp);
+}
+
+void
+aspath_finish (void)
+{
+  hash_clean (ashash, (void (*)(void *))aspath_free);
+  hash_free (ashash);
+  ashash = NULL;
+  
+  if (snmp_stream)
+    stream_free (snmp_stream);
+}
+
+/* return and as path value */
+const char *
+aspath_print (struct aspath *as)
+{
+  return (as ? as->str : NULL);
+}
+
+/* Printing functions */
+/* Feed the AS_PATH to the vty; the suffix string follows it only in case
+ * AS_PATH wasn't empty.
+ */
+void
+aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const char * suffix)
+{
+  assert (format);
+  vty_out (vty, format, as->str);
+  if (as->str_len && strlen (suffix))
+    vty_out (vty, "%s", suffix);
+}
+
+static void
+aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+  struct aspath *as;
+
+  as = (struct aspath *) backet->data;
+
+  vty_out (vty, "[%p:%u] (%ld) ", (void *)backet, backet->key, as->refcnt);
+  vty_out (vty, "%s%s", as->str, VTY_NEWLINE);
+}
+
+/* Print all aspath and hash information.  This function is used from
+   `show ip bgp paths' command. */
+void
+aspath_print_all_vty (struct vty *vty)
+{
+  hash_iterate (ashash, 
+               (void (*) (struct hash_backet *, void *))
+               aspath_show_all_iterator,
+               vty);
+}
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
new file mode 100644 (file)
index 0000000..c8f929f
--- /dev/null
@@ -0,0 +1,119 @@
+/* AS path related definitions.
+   Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ASPATH_H
+#define _QUAGGA_BGP_ASPATH_H
+
+/* AS path segment type.  */
+#define AS_SET                       1
+#define AS_SEQUENCE                  2
+#define AS_CONFED_SEQUENCE           3
+#define AS_CONFED_SET                4
+
+/* Private AS range defined in RFC2270.  */
+#define BGP_PRIVATE_AS_MIN       64512U
+#define BGP_PRIVATE_AS_MAX       65535U
+
+/* Private 4 byte AS range defined in RFC6996.  */
+#define BGP_PRIVATE_AS4_MIN     4200000000U
+#define BGP_PRIVATE_AS4_MAX     4294967294U
+
+/* we leave BGP_AS_MAX as the 16bit AS MAX number.  */
+#define BGP_AS_MAX                  65535U
+#define BGP_AS4_MAX            4294967295U
+/* Transition 16Bit AS as defined by IANA */
+#define BGP_AS_TRANS            23456U
+
+#define BGP_AS_IS_PRIVATE(ASN) \
+    (((ASN) >= BGP_PRIVATE_AS_MIN && (ASN) <= BGP_PRIVATE_AS_MAX) || \
+     ((ASN) >= BGP_PRIVATE_AS4_MIN && (ASN) <= BGP_PRIVATE_AS4_MAX))
+
+/* AS_PATH segment data in abstracted form, no limit is placed on length */
+struct assegment
+{
+  struct assegment *next;
+  as_t *as;
+  u_short length;
+  u_char type;
+};
+
+/* AS path may be include some AsSegments.  */
+struct aspath 
+{
+  /* Reference count to this aspath.  */
+  unsigned long refcnt;
+
+  /* segment data */
+  struct assegment *segments;
+  
+  /* String expression of AS path.  This string is used by vty output
+     and AS path regular expression match.  */
+  char *str;
+  unsigned short str_len;
+};
+
+#define ASPATH_STR_DEFAULT_LEN 32
+
+/* Prototypes. */
+extern void aspath_init (void);
+extern void aspath_finish (void);
+extern struct aspath *aspath_parse (struct stream *, size_t, int);
+extern struct aspath *aspath_dup (struct aspath *);
+extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *);
+extern struct aspath *aspath_aggregate_mpath (struct aspath *, struct aspath *);
+extern struct aspath *aspath_prepend (struct aspath *, struct aspath *);
+extern struct aspath *aspath_filter_exclude (struct aspath *, struct aspath *);
+extern struct aspath *aspath_add_seq_n (struct aspath *, as_t, unsigned);
+extern struct aspath *aspath_add_seq (struct aspath *, as_t);
+extern struct aspath *aspath_add_confed_seq (struct aspath *, as_t);
+extern int aspath_cmp (const void *, const void *);
+extern int aspath_cmp_left (const struct aspath *, const struct aspath *);
+extern int aspath_cmp_left_confed (const struct aspath *, const struct aspath *);
+extern struct aspath *aspath_delete_confed_seq (struct aspath *);
+extern struct aspath *aspath_empty (void);
+extern struct aspath *aspath_empty_get (void);
+extern struct aspath *aspath_str2aspath (const char *);
+extern void aspath_free (struct aspath *);
+extern struct aspath *aspath_intern (struct aspath *);
+extern void aspath_unintern (struct aspath **);
+extern const char *aspath_print (struct aspath *);
+extern void aspath_print_vty (struct vty *, const char *, struct aspath *, const char *);
+extern void aspath_print_all_vty (struct vty *);
+extern unsigned int aspath_key_make (void *);
+extern int aspath_loop_check (struct aspath *, as_t);
+extern int aspath_private_as_check (struct aspath *);
+extern int aspath_firstas_check (struct aspath *, as_t);
+extern int aspath_confed_check (struct aspath *);
+extern int aspath_left_confed_check (struct aspath *);
+extern unsigned long aspath_count (void);
+extern unsigned int aspath_count_hops (const struct aspath *);
+extern unsigned int aspath_count_confeds (struct aspath *);
+extern unsigned int aspath_size (struct aspath *);
+extern as_t aspath_highest (struct aspath *);
+extern as_t aspath_leftmost (struct aspath *);
+extern size_t aspath_put (struct stream *, struct aspath *, int);
+
+extern struct aspath *aspath_reconcile_as4 (struct aspath *, struct aspath *);
+extern unsigned int aspath_has_as4 (struct aspath *);
+
+/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
+extern u_char *aspath_snmp_pathseg (struct aspath *, size_t *);
+
+#endif /* _QUAGGA_BGP_ASPATH_H */
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
new file mode 100644 (file)
index 0000000..a79a03c
--- /dev/null
@@ -0,0 +1,3260 @@
+/* BGP attributes management routines.
+   Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "vector.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "hash.h"
+#include "jhash.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "table.h"
+#include "bgp_encap_types.h"
+
+/* Attribute strings for logging. */
+static const struct message attr_str [] = 
+{
+  { BGP_ATTR_ORIGIN,           "ORIGIN" }, 
+  { BGP_ATTR_AS_PATH,          "AS_PATH" }, 
+  { BGP_ATTR_NEXT_HOP,         "NEXT_HOP" }, 
+  { BGP_ATTR_MULTI_EXIT_DISC,  "MULTI_EXIT_DISC" }, 
+  { BGP_ATTR_LOCAL_PREF,       "LOCAL_PREF" }, 
+  { BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" }, 
+  { BGP_ATTR_AGGREGATOR,       "AGGREGATOR" }, 
+  { BGP_ATTR_COMMUNITIES,      "COMMUNITY" }, 
+  { BGP_ATTR_ORIGINATOR_ID,    "ORIGINATOR_ID" },
+  { BGP_ATTR_CLUSTER_LIST,     "CLUSTER_LIST" }, 
+  { BGP_ATTR_DPA,              "DPA" },
+  { BGP_ATTR_ADVERTISER,       "ADVERTISER"} ,
+  { BGP_ATTR_RCID_PATH,        "RCID_PATH" },
+  { BGP_ATTR_MP_REACH_NLRI,    "MP_REACH_NLRI" },
+  { BGP_ATTR_MP_UNREACH_NLRI,  "MP_UNREACH_NLRI" },
+  { BGP_ATTR_EXT_COMMUNITIES,  "EXT_COMMUNITIES" },
+  { BGP_ATTR_AS4_PATH,         "AS4_PATH" }, 
+  { BGP_ATTR_AS4_AGGREGATOR,   "AS4_AGGREGATOR" }, 
+  { BGP_ATTR_AS_PATHLIMIT,     "AS_PATHLIMIT" },
+  { BGP_ATTR_ENCAP,            "ENCAP" },
+  { 21,                        ""},
+  { 22,                        ""},
+  { 23,                        ""},
+  { 24,                        ""},
+  { 25,                        ""},
+  { 26,                        ""},
+  { 27,                        ""},
+  { 28,                        ""},
+  { 29,                        ""},
+  { 30,                        ""},
+  { 31,                        ""},
+  { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" }
+};
+static const int attr_str_max = array_size(attr_str);
+
+static const struct message attr_flag_str[] =
+{
+  { BGP_ATTR_FLAG_OPTIONAL, "Optional" },
+  { BGP_ATTR_FLAG_TRANS,    "Transitive" },
+  { BGP_ATTR_FLAG_PARTIAL,  "Partial" },
+  /* bgp_attr_flags_diagnose() relies on this bit being last in this list */
+  { BGP_ATTR_FLAG_EXTLEN,   "Extended Length" },
+};
+
+static struct hash *cluster_hash;
+
+static void *
+cluster_hash_alloc (void *p)
+{
+  struct cluster_list * val = (struct cluster_list *) p;
+  struct cluster_list *cluster;
+
+  cluster = XMALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list));
+  cluster->length = val->length;
+
+  if (cluster->length)
+    {
+      cluster->list = XMALLOC (MTYPE_CLUSTER_VAL, val->length);
+      memcpy (cluster->list, val->list, val->length);
+    }
+  else
+    cluster->list = NULL;
+
+  cluster->refcnt = 0;
+
+  return cluster;
+}
+
+/* Cluster list related functions. */
+static struct cluster_list *
+cluster_parse (struct in_addr * pnt, int length)
+{
+  struct cluster_list tmp;
+  struct cluster_list *cluster;
+
+  tmp.length = length;
+  tmp.list = pnt;
+
+  cluster = hash_get (cluster_hash, &tmp, cluster_hash_alloc);
+  cluster->refcnt++;
+  return cluster;
+}
+
+int
+cluster_loop_check (struct cluster_list *cluster, struct in_addr originator)
+{
+  int i;
+    
+  for (i = 0; i < cluster->length / 4; i++)
+    if (cluster->list[i].s_addr == originator.s_addr)
+      return 1;
+  return 0;
+}
+
+static unsigned int
+cluster_hash_key_make (void *p)
+{
+  const struct cluster_list *cluster = p;
+
+  return jhash(cluster->list, cluster->length, 0);
+}
+
+static int
+cluster_hash_cmp (const void *p1, const void *p2)
+{
+  const struct cluster_list * cluster1 = p1;
+  const struct cluster_list * cluster2 = p2;
+
+  return (cluster1->length == cluster2->length &&
+         memcmp (cluster1->list, cluster2->list, cluster1->length) == 0);
+}
+
+static void
+cluster_free (struct cluster_list *cluster)
+{
+  if (cluster->list)
+    XFREE (MTYPE_CLUSTER_VAL, cluster->list);
+  XFREE (MTYPE_CLUSTER, cluster);
+}
+
+#if 0
+static struct cluster_list *
+cluster_dup (struct cluster_list *cluster)
+{
+  struct cluster_list *new;
+
+  new = XCALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list));
+  new->length = cluster->length;
+
+  if (cluster->length)
+    {
+      new->list = XMALLOC (MTYPE_CLUSTER_VAL, cluster->length);
+      memcpy (new->list, cluster->list, cluster->length);
+    }
+  else
+    new->list = NULL;
+  
+  return new;
+}
+#endif
+
+static struct cluster_list *
+cluster_intern (struct cluster_list *cluster)
+{
+  struct cluster_list *find;
+
+  find = hash_get (cluster_hash, cluster, cluster_hash_alloc);
+  find->refcnt++;
+
+  return find;
+}
+
+void
+cluster_unintern (struct cluster_list *cluster)
+{
+  if (cluster->refcnt)
+    cluster->refcnt--;
+
+  if (cluster->refcnt == 0)
+    {
+      hash_release (cluster_hash, cluster);
+      cluster_free (cluster);
+    }
+}
+
+static void
+cluster_init (void)
+{
+  cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp);
+}
+
+static void
+cluster_finish (void)
+{
+  hash_clean (cluster_hash, (void (*)(void *))cluster_free);
+  hash_free (cluster_hash);
+  cluster_hash = NULL;
+}
+
+struct bgp_attr_encap_subtlv *
+encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
+{
+    struct bgp_attr_encap_subtlv *new;
+    struct bgp_attr_encap_subtlv *tail;
+    struct bgp_attr_encap_subtlv *p;
+
+    for (p = orig, tail = new = NULL; p; p = p->next) {
+       int size = sizeof(struct bgp_attr_encap_subtlv) - 1 + p->length;
+       if (tail) {
+           tail->next = XCALLOC(MTYPE_ENCAP_TLV, size);
+           tail = tail->next;
+       } else {
+           tail = new = XCALLOC(MTYPE_ENCAP_TLV, size);
+       }
+       assert(tail);
+       memcpy(tail, p, size);
+       tail->next = NULL;
+    }
+
+    return new;
+}
+
+static void
+encap_free(struct bgp_attr_encap_subtlv *p)
+{
+    struct bgp_attr_encap_subtlv *next;
+    while (p) {
+        next    = p->next;
+        p->next = NULL;
+        XFREE(MTYPE_ENCAP_TLV, p);
+        p       = next;
+    }
+}
+
+void
+bgp_attr_flush_encap(struct attr *attr)
+{
+    if (!attr || !attr->extra)
+       return;
+
+    if (attr->extra->encap_subtlvs) {
+       encap_free(attr->extra->encap_subtlvs);
+       attr->extra->encap_subtlvs = NULL;
+    }
+}
+
+/*
+ * Compare encap sub-tlv chains
+ *
+ *     1 = equivalent
+ *     0 = not equivalent
+ *
+ * This algorithm could be made faster if needed
+ */
+static int
+encap_same(struct bgp_attr_encap_subtlv *h1, struct bgp_attr_encap_subtlv *h2)
+{
+    struct bgp_attr_encap_subtlv *p;
+    struct bgp_attr_encap_subtlv *q;
+
+    if (!h1 && !h2)
+       return 1;
+    if (h1 && !h2)
+       return 0;
+    if (!h1 && h2)
+       return 0;
+    if (h1 == h2)
+       return 1;
+
+    for (p = h1; p; p = p->next) {
+       for (q = h2; q; q = q->next) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    for (p = h2; p; p = p->next) {
+       for (q = h1; q; q = q->next) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    return 1;
+}
+
+/* Unknown transit attribute. */
+static struct hash *transit_hash;
+
+static void
+transit_free (struct transit *transit)
+{
+  if (transit->val)
+    XFREE (MTYPE_TRANSIT_VAL, transit->val);
+  XFREE (MTYPE_TRANSIT, transit);
+}
+
+
+static void *
+transit_hash_alloc (void *p)
+{
+  /* Transit structure is already allocated.  */
+  return p;
+}
+
+static struct transit *
+transit_intern (struct transit *transit)
+{
+  struct transit *find;
+
+  find = hash_get (transit_hash, transit, transit_hash_alloc);
+  if (find != transit)
+    transit_free (transit);
+  find->refcnt++;
+
+  return find;
+}
+
+void
+transit_unintern (struct transit *transit)
+{
+  if (transit->refcnt)
+    transit->refcnt--;
+
+  if (transit->refcnt == 0)
+    {
+      hash_release (transit_hash, transit);
+      transit_free (transit);
+    }
+}
+
+static unsigned int
+transit_hash_key_make (void *p)
+{
+  const struct transit * transit = p;
+
+  return jhash(transit->val, transit->length, 0);
+}
+
+static int
+transit_hash_cmp (const void *p1, const void *p2)
+{
+  const struct transit * transit1 = p1;
+  const struct transit * transit2 = p2;
+
+  return (transit1->length == transit2->length &&
+         memcmp (transit1->val, transit2->val, transit1->length) == 0);
+}
+
+static void
+transit_init (void)
+{
+  transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp);
+}
+
+static void
+transit_finish (void)
+{
+  hash_clean (transit_hash, (void (*)(void *))transit_free);
+  hash_free (transit_hash);
+  transit_hash = NULL;
+}
+
+/* Attribute hash routines. */
+static struct hash *attrhash;
+
+static struct attr_extra *
+bgp_attr_extra_new (void)
+{
+  return XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
+}
+
+void
+bgp_attr_extra_free (struct attr *attr)
+{
+  if (attr->extra)
+    {
+      if (attr->extra->encap_subtlvs) {
+       encap_free(attr->extra->encap_subtlvs);
+       attr->extra->encap_subtlvs = NULL;
+      }
+      XFREE (MTYPE_ATTR_EXTRA, attr->extra);
+      attr->extra = NULL;
+    }
+}
+
+struct attr_extra *
+bgp_attr_extra_get (struct attr *attr)
+{
+  if (!attr->extra)
+    attr->extra = bgp_attr_extra_new();
+  return attr->extra;
+}
+
+/* Shallow copy of an attribute
+ * Though, not so shallow that it doesn't copy the contents
+ * of the attr_extra pointed to by 'extra'
+ */
+void
+bgp_attr_dup (struct attr *new, struct attr *orig)
+{
+  struct attr_extra *extra = new->extra;
+
+  *new = *orig;
+  /* if caller provided attr_extra space, use it in any case.
+   *
+   * This is neccesary even if orig->extra equals NULL, because otherwise
+   * memory may be later allocated on the heap by bgp_attr_extra_get.
+   *
+   * That memory would eventually be leaked, because the caller must not
+   * call bgp_attr_extra_free if he provided attr_extra on the stack.
+   */
+  if (extra)
+    {
+      new->extra = extra;
+      memset(new->extra, 0, sizeof(struct attr_extra));
+      if (orig->extra) {
+        *new->extra = *orig->extra;
+        if (orig->extra->encap_subtlvs) {
+          new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
+        }
+      }
+    }
+  else if (orig->extra)
+    {
+      new->extra = bgp_attr_extra_new();
+      *new->extra = *orig->extra;
+      if (orig->extra->encap_subtlvs) {
+       new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
+      }
+    }
+}
+
+unsigned long int
+attr_count (void)
+{
+  return attrhash->count;
+}
+
+unsigned long int
+attr_unknown_count (void)
+{
+  return transit_hash->count;
+}
+
+unsigned int
+attrhash_key_make (void *p)
+{
+  const struct attr *attr = (struct attr *) p;
+  const struct attr_extra *extra = attr->extra;
+  uint32_t key = 0;
+#define MIX(val)       key = jhash_1word(val, key)
+
+  MIX(attr->origin);
+  MIX(attr->nexthop.s_addr);
+  MIX(attr->med);
+  MIX(attr->local_pref);
+
+  key += attr->origin;
+  key += attr->nexthop.s_addr;
+  key += attr->med;
+  key += attr->local_pref;
+  
+  if (extra)
+    {
+      MIX(extra->aggregator_as);
+      MIX(extra->aggregator_addr.s_addr);
+      MIX(extra->weight);
+      MIX(extra->mp_nexthop_global_in.s_addr);
+      MIX(extra->originator_id.s_addr);
+      MIX(extra->tag);
+    }
+  
+  if (attr->aspath)
+    MIX(aspath_key_make (attr->aspath));
+  if (attr->community)
+    MIX(community_hash_make (attr->community));
+  
+  if (extra)
+    {
+      if (extra->lcommunity)
+       MIX(lcommunity_hash_make (extra->lcommunity));
+      if (extra->ecommunity)
+        MIX(ecommunity_hash_make (extra->ecommunity));
+      if (extra->cluster)
+        MIX(cluster_hash_key_make (extra->cluster));
+      if (extra->transit)
+        MIX(transit_hash_key_make (extra->transit));
+
+      MIX(extra->mp_nexthop_len);
+      key = jhash(extra->mp_nexthop_global.s6_addr, 16, key);
+      key = jhash(extra->mp_nexthop_local.s6_addr, 16, key);
+    }
+
+  return key;
+}
+
+int
+attrhash_cmp (const void *p1, const void *p2)
+{
+  const struct attr * attr1 = p1;
+  const struct attr * attr2 = p2;
+
+  if (attr1->flag == attr2->flag
+      && attr1->origin == attr2->origin
+      && attr1->nexthop.s_addr == attr2->nexthop.s_addr
+      && attr1->aspath == attr2->aspath
+      && attr1->community == attr2->community
+      && attr1->med == attr2->med
+      && attr1->local_pref == attr2->local_pref)
+    {
+      const struct attr_extra *ae1 = attr1->extra;
+      const struct attr_extra *ae2 = attr2->extra;
+      
+      if (ae1 && ae2
+          && ae1->aggregator_as == ae2->aggregator_as
+          && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr
+          && ae1->weight == ae2->weight
+          && ae1->tag == ae2->tag
+          && ae1->mp_nexthop_len == ae2->mp_nexthop_len
+          && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global)
+          && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local)
+          && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in)
+          && ae1->ecommunity == ae2->ecommunity
+         && ae1->lcommunity == ae2->lcommunity
+          && ae1->cluster == ae2->cluster
+          && ae1->transit == ae2->transit
+         && (ae1->encap_tunneltype == ae2->encap_tunneltype)
+         && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs)
+          && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id))
+        return 1;
+      else if (ae1 || ae2)
+        return 0;
+      /* neither attribute has extra attributes, so they're same */
+      return 1;
+    }
+  else
+    return 0;
+}
+
+static void
+attrhash_init (void)
+{
+  attrhash = hash_create (attrhash_key_make, attrhash_cmp);
+}
+
+/*
+ * special for hash_clean below
+ */
+static void
+attr_vfree (void *attr)
+{
+  bgp_attr_extra_free ((struct attr *)attr);
+  XFREE (MTYPE_ATTR, attr);
+}
+
+static void
+attrhash_finish (void)
+{
+  hash_clean(attrhash, attr_vfree);
+  hash_free (attrhash);
+  attrhash = NULL;
+}
+
+static void
+attr_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+  struct attr *attr = backet->data;
+
+  vty_out (vty, "attr[%ld] nexthop %s%s", attr->refcnt, 
+          inet_ntoa (attr->nexthop), VTY_NEWLINE);
+}
+
+void
+attr_show_all (struct vty *vty)
+{
+  hash_iterate (attrhash, 
+               (void (*)(struct hash_backet *, void *))
+               attr_show_all_iterator,
+               vty);
+}
+
+static void *
+bgp_attr_hash_alloc (void *p)
+{
+  struct attr * val = (struct attr *) p;
+  struct attr *attr;
+
+  attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr));
+  *attr = *val;
+  if (val->extra)
+    {
+      attr->extra = bgp_attr_extra_new ();
+      *attr->extra = *val->extra;
+
+      if (attr->extra->encap_subtlvs) {
+       attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs);
+      }
+    }
+  attr->refcnt = 0;
+  return attr;
+}
+
+/* Internet argument attribute. */
+struct attr *
+bgp_attr_intern (struct attr *attr)
+{
+  struct attr *find;
+
+  /* Intern referenced strucutre. */
+  if (attr->aspath)
+    {
+      if (! attr->aspath->refcnt)
+       attr->aspath = aspath_intern (attr->aspath);
+      else
+       attr->aspath->refcnt++;
+    }
+  if (attr->community)
+    {
+      if (! attr->community->refcnt)
+       attr->community = community_intern (attr->community);
+      else
+       attr->community->refcnt++;
+    }
+  if (attr->extra)
+    {
+      struct attr_extra *attre = attr->extra;
+      
+      if (attre->ecommunity)
+        {
+          if (! attre->ecommunity->refcnt)
+            attre->ecommunity = ecommunity_intern (attre->ecommunity);
+          else
+            attre->ecommunity->refcnt++;
+          
+        }
+      if (attre->lcommunity)
+        {
+          if (! attre->lcommunity->refcnt)
+            attre->lcommunity = lcommunity_intern (attre->lcommunity);
+          else
+            attre->lcommunity->refcnt++;
+        }
+      if (attre->cluster)
+        {
+          if (! attre->cluster->refcnt)
+            attre->cluster = cluster_intern (attre->cluster);
+          else
+            attre->cluster->refcnt++;
+        }
+      if (attre->transit)
+        {
+          if (! attre->transit->refcnt)
+            attre->transit = transit_intern (attre->transit);
+          else
+            attre->transit->refcnt++;
+        }
+    }
+  
+  find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
+  find->refcnt++;
+  
+  return find;
+}
+
+
+/* Make network statement's attribute. */
+struct attr *
+bgp_attr_default_set (struct attr *attr, u_char origin)
+{
+  memset (attr, 0, sizeof (struct attr));
+  bgp_attr_extra_get (attr);
+  
+  attr->origin = origin;
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+  attr->aspath = aspath_empty ();
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+  attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attr->extra->tag = 0;
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+  attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN;
+
+  return attr;
+}
+
+
+/* Make network statement's attribute. */
+struct attr *
+bgp_attr_default_intern (u_char origin)
+{
+  struct attr attr;
+  struct attr *new;
+
+  memset (&attr, 0, sizeof (struct attr));
+  bgp_attr_extra_get (&attr);
+
+  bgp_attr_default_set(&attr, origin);
+
+  new = bgp_attr_intern (&attr);
+  bgp_attr_extra_free (&attr);
+  
+  aspath_unintern (&new->aspath);
+  return new;
+}
+
+/* Create the attributes for an aggregate */
+struct attr *
+bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
+                          struct aspath *aspath,
+                          struct community *community, int as_set,
+                          u_char atomic_aggregate)
+{
+  struct attr attr;
+  struct attr *new;
+  struct attr_extra attre;
+
+  memset (&attr, 0, sizeof (struct attr));
+  memset (&attre, 0, sizeof (struct attr_extra));
+  attr.extra = &attre;
+
+  /* Origin attribute. */
+  attr.origin = origin;
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+
+  /* AS path attribute. */
+  if (aspath)
+    attr.aspath = aspath_intern (aspath);
+  else
+    attr.aspath = aspath_empty ();
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+
+  /* Next hop attribute.  */
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+
+  if (community)
+    {
+      attr.community = community;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+    }
+
+  attre.weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attre.mp_nexthop_len = IPV6_MAX_BYTELEN;
+
+  if (! as_set || atomic_aggregate)
+    attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
+  if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
+    attre.aggregator_as = bgp->confed_id;
+  else
+    attre.aggregator_as = bgp->as;
+  attre.aggregator_addr = bgp->router_id;
+
+  new = bgp_attr_intern (&attr);
+
+  aspath_unintern (&new->aspath);
+  return new;
+}
+
+/* Unintern just the sub-components of the attr, but not the attr */
+void
+bgp_attr_unintern_sub (struct attr *attr)
+{
+  /* aspath refcount shoud be decrement. */
+  if (attr->aspath)
+    aspath_unintern (&attr->aspath);
+  UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH));
+  
+  if (attr->community)
+    community_unintern (&attr->community);
+  UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES));
+  
+  if (attr->extra)
+    {
+      if (attr->extra->ecommunity)
+        ecommunity_unintern (&attr->extra->ecommunity);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
+
+      if (attr->extra->lcommunity)
+        lcommunity_unintern (&attr->extra->lcommunity);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
+      
+      if (attr->extra->cluster)
+        cluster_unintern (attr->extra->cluster);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
+      
+      if (attr->extra->transit)
+        transit_unintern (attr->extra->transit);
+    }
+}
+
+/* Free bgp attribute and aspath. */
+void
+bgp_attr_unintern (struct attr **pattr)
+{
+  struct attr *attr = *pattr;
+  struct attr *ret;
+  struct attr tmp;
+  struct attr_extra tmp_extra;
+  
+  /* Decrement attribute reference. */
+  attr->refcnt--;
+  
+  tmp = *attr;
+  
+  if (attr->extra)
+    {
+      tmp.extra = &tmp_extra;
+      memcpy (tmp.extra, attr->extra, sizeof (struct attr_extra));
+    }
+  
+  /* If reference becomes zero then free attribute object. */
+  if (attr->refcnt == 0)
+    {
+      ret = hash_release (attrhash, attr);
+      assert (ret != NULL);
+      bgp_attr_extra_free (attr);
+      XFREE (MTYPE_ATTR, attr);
+      *pattr = NULL;
+    }
+
+  bgp_attr_unintern_sub (&tmp);
+}
+
+void
+bgp_attr_flush (struct attr *attr)
+{
+  if (attr->aspath && ! attr->aspath->refcnt)
+    {
+      aspath_free (attr->aspath);
+      attr->aspath = NULL;
+    }
+  if (attr->community && ! attr->community->refcnt)
+    {
+      community_free (attr->community);
+      attr->community = NULL;
+    }
+  if (attr->extra)
+    {
+      struct attr_extra *attre = attr->extra;
+
+      if (attre->ecommunity && ! attre->ecommunity->refcnt)
+        ecommunity_free (&attre->ecommunity);
+      if (attre->lcommunity && ! attre->lcommunity->refcnt)
+       lcommunity_free (&attre->lcommunity);
+      if (attre->cluster && ! attre->cluster->refcnt)
+        {
+          cluster_free (attre->cluster);
+          attre->cluster = NULL;
+        }
+      if (attre->transit && ! attre->transit->refcnt)
+        {
+          transit_free (attre->transit);
+          attre->transit = NULL;
+        }
+      encap_free(attre->encap_subtlvs);
+      attre->encap_subtlvs = NULL;
+    }
+}
+
+/* Implement draft-scudder-idr-optional-transitive behaviour and
+ * avoid resetting sessions for malformed attributes which are
+ * are partial/optional and hence where the error likely was not
+ * introduced by the sending neighbour.
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode,
+                    bgp_size_t length)
+{
+  struct peer *const peer = args->peer; 
+  const u_int8_t flags = args->flags;
+  /* startp and length must be special-cased, as whether or not to
+   * send the attribute data with the NOTIFY depends on the error,
+   * the caller therefore signals this with the seperate length argument
+   */
+  u_char *notify_datap = (length > 0 ? args->startp : NULL);
+  
+  /* Only relax error handling for eBGP peers */
+  if (peer->sort != BGP_PEER_EBGP)
+    {
+      bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
+                                 notify_datap, length);
+      return BGP_ATTR_PARSE_ERROR;
+
+    }
+  
+  /* Adjust the stream getp to the end of the attribute, in case we can
+   * still proceed but the caller hasn't read all the attribute.
+   */
+  stream_set_getp (BGP_INPUT (peer),
+                   (args->startp - STREAM_DATA (BGP_INPUT (peer)))
+                    + args->total);
+  
+  switch (args->type) {
+    /* where an attribute is relatively inconsequential, e.g. it does not
+     * affect route selection, and can be safely ignored, then any such
+     * attributes which are malformed should just be ignored and the route
+     * processed as normal.
+     */
+    case BGP_ATTR_AS4_AGGREGATOR:
+    case BGP_ATTR_AGGREGATOR:
+    case BGP_ATTR_ATOMIC_AGGREGATE:
+      return BGP_ATTR_PARSE_PROCEED;
+    
+    /* Core attributes, particularly ones which may influence route
+     * selection, should always cause session resets
+     */
+    case BGP_ATTR_ORIGIN:
+    case BGP_ATTR_AS_PATH:
+    case BGP_ATTR_NEXT_HOP:
+    case BGP_ATTR_MULTI_EXIT_DISC:
+    case BGP_ATTR_LOCAL_PREF:
+    case BGP_ATTR_COMMUNITIES:
+    case BGP_ATTR_ORIGINATOR_ID:
+    case BGP_ATTR_CLUSTER_LIST:
+    case BGP_ATTR_MP_REACH_NLRI:
+    case BGP_ATTR_MP_UNREACH_NLRI:
+    case BGP_ATTR_EXT_COMMUNITIES:
+      bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
+                                 notify_datap, length);
+      return BGP_ATTR_PARSE_ERROR;
+  }
+  
+  /* Partial optional attributes that are malformed should not cause
+   * the whole session to be reset. Instead treat it as a withdrawal
+   * of the routes, if possible.
+   */
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
+    return BGP_ATTR_PARSE_WITHDRAW;
+  
+  /* default to reset */
+  return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+}
+
+/* Find out what is wrong with the path attribute flag bits and log the error.
+   "Flag bits" here stand for Optional, Transitive and Partial, but not for
+   Extended Length. Checking O/T/P bits at once implies, that the attribute
+   being diagnosed is defined by RFC as either a "well-known" or an "optional,
+   non-transitive" attribute. */
+static void
+bgp_attr_flags_diagnose (struct bgp_attr_parser_args *args,
+                         u_int8_t desired_flags /* how RFC says it must be */
+)
+{
+  u_char seen = 0, i;
+  u_char real_flags = args->flags;
+  const u_int8_t attr_code = args->type;
+  
+  desired_flags &= ~BGP_ATTR_FLAG_EXTLEN;
+  real_flags &= ~BGP_ATTR_FLAG_EXTLEN;
+  for (i = 0; i <= 2; i++) /* O,T,P, but not E */
+    if
+    (
+      CHECK_FLAG (desired_flags, attr_flag_str[i].key) !=
+      CHECK_FLAG (real_flags,    attr_flag_str[i].key)
+    )
+    {
+      zlog (args->peer->log, LOG_ERR, "%s attribute must%s be flagged as \"%s\"",
+            LOOKUP (attr_str, attr_code),
+            CHECK_FLAG (desired_flags, attr_flag_str[i].key) ? "" : " not",
+            attr_flag_str[i].str);
+      seen = 1;
+    }
+  if (!seen)
+    {
+      zlog (args->peer->log, LOG_DEBUG,
+            "Strange, %s called for attr %s, but no problem found with flags"
+            " (real flags 0x%x, desired 0x%x)",
+            __func__, LOOKUP (attr_str, attr_code),
+            real_flags, desired_flags);
+    }
+}
+
+/* Required flags for attributes. EXTLEN will be masked off when testing,
+ * as will PARTIAL for optional+transitive attributes.
+ */
+const u_int8_t attr_flags_values [] = {
+  [BGP_ATTR_ORIGIN] =           BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AS_PATH] =          BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_NEXT_HOP] =         BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_MULTI_EXIT_DISC] =  BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_LOCAL_PREF] =       BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_ATOMIC_AGGREGATE] = BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AGGREGATOR] =       BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_COMMUNITIES] =      BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_ORIGINATOR_ID] =    BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_CLUSTER_LIST] =     BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_MP_REACH_NLRI] =    BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_MP_UNREACH_NLRI] =  BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_EXT_COMMUNITIES] =  BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AS4_PATH] =         BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AS4_AGGREGATOR] =   BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+};
+static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
+
+static int
+bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
+{
+  u_int8_t mask = BGP_ATTR_FLAG_EXTLEN;
+  const u_int8_t flags = args->flags;
+  const u_int8_t attr_code = args->type;
+  struct peer *const peer = args->peer; 
+  
+  /* there may be attributes we don't know about */
+  if (attr_code > attr_flags_values_max)
+    return 0;
+  if (attr_flags_values[attr_code] == 0)
+    return 0;
+  
+  /* RFC4271, "For well-known attributes, the Transitive bit MUST be set to
+   * 1."
+   */
+  if (!CHECK_FLAG (BGP_ATTR_FLAG_OPTIONAL, flags)
+      && !CHECK_FLAG (BGP_ATTR_FLAG_TRANS, flags))
+    {
+      zlog (peer->log, LOG_ERR,
+            "%s well-known attributes must have transitive flag set (%x)",
+            LOOKUP (attr_str, attr_code), flags);
+      return 1;
+    }
+  
+  /* "For well-known attributes and for optional non-transitive attributes,
+   *  the Partial bit MUST be set to 0." 
+   */
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
+    {
+      if (!CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL))
+        {
+          zlog (peer->log, LOG_ERR,
+                "%s well-known attribute "
+                "must NOT have the partial flag set (%x)",
+                 LOOKUP (attr_str, attr_code), flags);
+          return 1;
+        }
+      if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+          && !CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
+        {
+          zlog (peer->log, LOG_ERR,
+                "%s optional + transitive attribute "
+                "must NOT have the partial flag set (%x)",
+                 LOOKUP (attr_str, attr_code), flags);
+          return 1;
+        }
+    }
+  
+  /* Optional transitive attributes may go through speakers that don't
+   * reocgnise them and set the Partial bit.
+   */
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
+    SET_FLAG (mask, BGP_ATTR_FLAG_PARTIAL);
+  
+  if ((flags & ~mask)
+      == attr_flags_values[attr_code])
+    return 0;
+  
+  bgp_attr_flags_diagnose (args, attr_flags_values[attr_code]);
+  return 1;
+}
+
+/* Get origin attribute of the update message. */
+static bgp_attr_parse_ret_t
+bgp_attr_origin (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer;
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* If any recognized attribute has Attribute Length that conflicts
+     with the expected length (based on the attribute type code), then
+     the Error Subcode is set to Attribute Length Error.  The Data
+     field contains the erroneous attribute (type, length and
+     value). */
+  if (length != 1)
+    {
+      zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
+           length);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  /* Fetch origin attribute. */
+  attr->origin = stream_getc (BGP_INPUT (peer));
+
+  /* If the ORIGIN attribute has an undefined value, then the Error
+     Subcode is set to Invalid Origin Attribute.  The Data field
+     contains the unrecognized attribute (type, length and value). */
+  if ((attr->origin != BGP_ORIGIN_IGP)
+      && (attr->origin != BGP_ORIGIN_EGP)
+      && (attr->origin != BGP_ORIGIN_INCOMPLETE))
+    {
+      zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
+             attr->origin);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
+                                 args->total);
+    }
+
+  /* Set oring attribute flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+
+  return 0;
+}
+
+/* Parse AS path information.  This function is wrapper of
+   aspath_parse. */
+static int
+bgp_attr_aspath (struct bgp_attr_parser_args *args)
+{
+  struct attr *const attr = args->attr;
+  struct peer *const peer = args->peer; 
+  const bgp_size_t length = args->length;
+  
+  /*
+   * peer with AS4 => will get 4Byte ASnums
+   * otherwise, will get 16 Bit
+   */
+  attr->aspath = aspath_parse (peer->ibuf, length, 
+                               CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV));
+
+  /* In case of IBGP, length will be zero. */
+  if (! attr->aspath)
+    {
+      zlog (peer->log, LOG_ERR,
+            "Malformed AS path from %s, length is %d",
+            peer->host, length);
+      return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0);
+    }
+
+  /* Set aspath attribute flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+static bgp_attr_parse_ret_t
+bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr)
+{
+  /* These checks were part of bgp_attr_aspath, but with
+   * as4 we should to check aspath things when
+   * aspath synthesizing with as4_path has already taken place.
+   * Otherwise we check ASPATH and use the synthesized thing, and that is
+   * not right.
+   * So do the checks later, i.e. here
+   */
+  struct bgp *bgp = peer->bgp;
+  struct aspath *aspath;
+
+  /* Confederation sanity check. */
+  if ((peer->sort == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) ||
+     (peer->sort == BGP_PEER_EBGP && aspath_confed_check (attr->aspath)))
+    {
+      zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host);
+      bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+                       BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+      return BGP_ATTR_PARSE_ERROR;
+    }
+
+  /* First AS check for EBGP. */
+  if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
+    {
+      if (peer->sort == BGP_PEER_EBGP
+         && ! aspath_firstas_check (attr->aspath, peer->as))
+       {
+         zlog (peer->log, LOG_ERR,
+               "%s incorrect first AS (must be %u)", peer->host, peer->as);
+          bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+                           BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+          return BGP_ATTR_PARSE_ERROR;
+       }
+    }
+
+  /* local-as prepend */
+  if (peer->change_local_as &&
+      ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+    {
+      aspath = aspath_dup (attr->aspath);
+      aspath = aspath_add_seq (aspath, peer->change_local_as);
+      aspath_unintern (&attr->aspath);
+      attr->aspath = aspath_intern (aspath);
+    }
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Parse AS4 path information.  This function is another wrapper of
+   aspath_parse. */
+static int
+bgp_attr_as4_path (struct bgp_attr_parser_args *args, struct aspath **as4_path)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  *as4_path = aspath_parse (peer->ibuf, length, 1);
+
+  /* In case of IBGP, length will be zero. */
+  if (!*as4_path)
+    {
+      zlog (peer->log, LOG_ERR,
+            "Malformed AS4 path from %s, length is %d",
+            peer->host, length);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+                                 0);
+    }
+
+  /* Set aspath attribute flag. */
+  if (as4_path)
+    attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Nexthop attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_nexthop (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  in_addr_t nexthop_h, nexthop_n;
+
+  /* Check nexthop attribute length. */
+  if (length != 4)
+    {
+      zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]",
+             length);
+
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP
+     attribute must result in a NOTIFICATION message (this is implemented below).
+     At the same time, semantically incorrect NEXT_HOP is more likely to be just
+     logged locally (this is implemented somewhere else). The UPDATE message
+     gets ignored in any of these cases. */
+  nexthop_n = stream_get_ipv4 (peer->ibuf);
+  nexthop_h = ntohl (nexthop_n);
+  if ((IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h))
+      && !BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) /* loopbacks may be used in testing */
+    {
+      char buf[INET_ADDRSTRLEN];
+      inet_ntop (AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN);
+      zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP,
+                                 args->total);
+    }
+
+  attr->nexthop.s_addr = nexthop_n;
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* MED atrribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_med (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* Length check. */
+  if (length != 4)
+    {
+      zlog (peer->log, LOG_ERR, 
+           "MED attribute length isn't four [%d]", length);
+
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  attr->med = stream_getl (peer->ibuf);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Local preference attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_local_pref (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* Length check. */
+  if (length != 4)
+  {
+    zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]",
+          length);
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+  }
+
+  /* If it is contained in an UPDATE message that is received from an
+     external peer, then this attribute MUST be ignored by the
+     receiving speaker. */
+  if (peer->sort == BGP_PEER_EBGP)
+    {
+      stream_forward_getp (peer->ibuf, length);
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+
+  attr->local_pref = stream_getl (peer->ibuf);
+
+  /* Set atomic aggregate flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Atomic aggregate. */
+static int
+bgp_attr_atomic (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* Length check. */
+  if (length != 0)
+    {
+      zlog (peer->log, LOG_ERR, "ATOMIC_AGGREGATE attribute length isn't 0 [%u]",
+            length);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  /* Set atomic aggregate flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Aggregator attribute */
+static int
+bgp_attr_aggregator (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  int wantedlen = 6;
+  struct attr_extra *attre = bgp_attr_extra_get (attr);
+  
+  /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
+    wantedlen = 8;
+  
+  if (length != wantedlen)
+    {
+      zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]",
+            wantedlen, length);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+  
+  if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
+    attre->aggregator_as = stream_getl (peer->ibuf);
+  else
+    attre->aggregator_as = stream_getw (peer->ibuf);
+  attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
+
+  /* Set atomic aggregate flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* New Aggregator attribute */
+static bgp_attr_parse_ret_t
+bgp_attr_as4_aggregator (struct bgp_attr_parser_args *args,
+                        as_t *as4_aggregator_as,
+                        struct in_addr *as4_aggregator_addr)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+      
+  if (length != 8)
+    {
+      zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]",
+            length);
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 0);
+    }
+  
+  *as4_aggregator_as = stream_getl (peer->ibuf);
+  as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_munge_as4_attrs (struct peer *const peer,
+                          struct attr *const attr,
+                          struct aspath *as4_path, as_t as4_aggregator,
+                          struct in_addr *as4_aggregator_addr)
+{
+  int ignore_as4_path = 0;
+  struct aspath *newpath;
+  struct attr_extra *attre = attr->extra;
+  
+  if (!attr->aspath)
+    {
+      /* NULL aspath shouldn't be possible as bgp_attr_parse should have
+       * checked that all well-known, mandatory attributes were present.
+       * 
+       * Can only be a problem with peer itself - hard error
+       */
+      return BGP_ATTR_PARSE_ERROR;
+    }
+  
+  if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
+    {
+      /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR
+       * if given.
+       * It is worth a warning though, because the peer really
+       * should not send them
+       */
+      if (BGP_DEBUG(as4, AS4))
+        {
+          if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))
+            zlog_debug ("[AS4] %s %s AS4_PATH",
+                        peer->host, "AS4 capable peer, yet it sent");
+          
+          if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR)))
+            zlog_debug ("[AS4] %s %s AS4_AGGREGATOR",
+                        peer->host, "AS4 capable peer, yet it sent");
+        }
+      
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+  
+  /* We have a asn16 peer.  First, look for AS4_AGGREGATOR
+   * because that may override AS4_PATH
+   */
+  if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) )
+    {
+      if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) )
+        {
+          assert (attre);
+          
+          /* received both.
+           * if the as_number in aggregator is not AS_TRANS,
+           *  then AS4_AGGREGATOR and AS4_PATH shall be ignored
+           *        and the Aggregator shall be taken as 
+           *        info on the aggregating node, and the AS_PATH
+           *        shall be taken as the AS_PATH
+           *  otherwise
+           *        the Aggregator shall be ignored and the
+           *        AS4_AGGREGATOR shall be taken as the
+           *        Aggregating node and the AS_PATH is to be
+           *        constructed "as in all other cases"
+           */
+          if (attre->aggregator_as != BGP_AS_TRANS)
+            {
+              /* ignore */
+              if ( BGP_DEBUG(as4, AS4))
+                zlog_debug ("[AS4] %s BGP not AS4 capable peer" 
+                            " send AGGREGATOR != AS_TRANS and"
+                            " AS4_AGGREGATOR, so ignore"
+                            " AS4_AGGREGATOR and AS4_PATH", peer->host);
+              ignore_as4_path = 1;
+            }
+          else
+            {
+              /* "New_aggregator shall be taken as aggregator" */
+              attre->aggregator_as = as4_aggregator;
+              attre->aggregator_addr.s_addr = as4_aggregator_addr->s_addr;
+            }
+        }
+      else
+        {
+          /* We received a AS4_AGGREGATOR but no AGGREGATOR.
+           * That is bogus - but reading the conditions
+           * we have to handle AS4_AGGREGATOR as if it were
+           * AGGREGATOR in that case
+           */
+          if ( BGP_DEBUG(as4, AS4))
+            zlog_debug ("[AS4] %s BGP not AS4 capable peer send"
+                        " AS4_AGGREGATOR but no AGGREGATOR, will take"
+                        " it as if AGGREGATOR with AS_TRANS had been there", peer->host);
+          (attre = bgp_attr_extra_get (attr))->aggregator_as = as4_aggregator;
+          /* sweep it under the carpet and simulate a "good" AGGREGATOR */
+          attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR));
+        }
+    }
+
+  /* need to reconcile NEW_AS_PATH and AS_PATH */
+  if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH))))
+    {
+      newpath = aspath_reconcile_as4 (attr->aspath, as4_path);
+      aspath_unintern (&attr->aspath);
+      attr->aspath = aspath_intern (newpath);
+    }
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_community (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;  
+  const bgp_size_t length = args->length;
+  
+  if (length == 0)
+    {
+      attr->community = NULL;
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+  
+  attr->community =
+    community_parse ((u_int32_t *)stream_pnt (peer->ibuf), length);
+  
+  /* XXX: fix community_parse to use stream API and remove this */
+  stream_forward_getp (peer->ibuf, length);
+
+  if (!attr->community)
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                               args->total);
+  
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Originator ID attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_originator_id (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* Length check. */
+  if (length != 4)
+    {
+      zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length);
+
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  (bgp_attr_extra_get (attr))->originator_id.s_addr 
+    = stream_get_ipv4 (peer->ibuf);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Cluster list attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_cluster_list (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
+  /* Check length. */
+  if (length % 4)
+    {
+      zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length);
+
+      return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
+    }
+
+  (bgp_attr_extra_get (attr))->cluster 
+    = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length);
+  
+  /* XXX: Fix cluster_parse to use stream API and then remove this */
+  stream_forward_getp (peer->ibuf, length);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Multiprotocol reachability information parse. */
+int
+bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
+                    struct bgp_nlri *mp_update)
+{
+  afi_t afi;
+  safi_t safi;
+  bgp_size_t nlri_len;
+  size_t start;
+  struct stream *s;
+  struct peer *const peer = args->peer;  
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  struct attr_extra *attre = bgp_attr_extra_get(attr);
+  
+  /* Set end of packet. */
+  s = BGP_INPUT(peer);
+  start = stream_get_getp(s);
+  
+  /* safe to read statically sized header? */
+#define BGP_MP_REACH_MIN_SIZE 5
+#define LEN_LEFT       (length - (stream_get_getp(s) - start))
+  if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE))
+    {
+      zlog_info ("%s: %s sent invalid length, %lu", 
+                __func__, peer->host, (unsigned long)length);
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+    }
+  
+  /* Load AFI, SAFI. */
+  afi = stream_getw (s);
+  safi = stream_getc (s);
+
+  /* Get nexthop length. */
+  attre->mp_nexthop_len = stream_getc (s);
+  
+  if (LEN_LEFT < attre->mp_nexthop_len)
+    {
+      zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", 
+                __func__, peer->host, attre->mp_nexthop_len);
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+    }
+  
+  /* Nexthop length check. */
+  switch (attre->mp_nexthop_len)
+    {
+    case 4:
+      stream_get (&attre->mp_nexthop_global_in, s, 4);
+      /* Probably needed for RFC 2283 */
+      if (attr->nexthop.s_addr == 0)
+        memcpy(&attr->nexthop.s_addr, &attre->mp_nexthop_global_in, 4);
+      break;
+    case 12:
+      stream_getl (s); /* RD high */
+      stream_getl (s); /* RD low */
+      stream_get (&attre->mp_nexthop_global_in, s, 4);
+      break;
+    case 24:
+      {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
+      /* fall through */
+    case 16:
+      stream_get (&attre->mp_nexthop_global, s, 16);
+      break;
+    case 32:
+    case 48:
+      if (attre->mp_nexthop_len == 48) {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
+      stream_get (&attre->mp_nexthop_global, s, 16);
+
+      if (attre->mp_nexthop_len == 48) {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
+      stream_get (&attre->mp_nexthop_local, s, 16);
+      if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local))
+       {
+         char buf1[INET6_ADDRSTRLEN];
+         char buf2[INET6_ADDRSTRLEN];
+
+         if (BGP_DEBUG (update, UPDATE_IN))
+           zlog_debug ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host,
+                      inet_ntop (AF_INET6, &attre->mp_nexthop_global,
+                                 buf1, INET6_ADDRSTRLEN),
+                      inet_ntop (AF_INET6, &attre->mp_nexthop_local,
+                                 buf2, INET6_ADDRSTRLEN));
+
+         attre->mp_nexthop_len = 16;
+       }
+      break;
+    default:
+      zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", 
+                __func__, peer->host, attre->mp_nexthop_len);
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+    }
+
+  if (!LEN_LEFT)
+    {
+      zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)",
+                 __func__, peer->host);
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+    }
+  
+  {
+    u_char val; 
+    if ((val = stream_getc (s)))
+    zlog_warn ("%s sent non-zero value, %u, for defunct SNPA-length field",
+                peer->host, val);
+  }
+  
+  /* must have nrli_len, what is left of the attribute */
+  nlri_len = LEN_LEFT;
+  if ((!nlri_len) || (nlri_len > STREAM_READABLE(s)))
+    {
+      zlog_info ("%s: (%s) Failed to read NLRI",
+                 __func__, peer->host);
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+    }
+  
+  mp_update->afi = afi;
+  mp_update->safi = safi;
+  mp_update->nlri = stream_pnt (s);
+  mp_update->length = nlri_len;
+
+  stream_forward_getp (s, nlri_len);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI);
+
+  return BGP_ATTR_PARSE_PROCEED;
+#undef LEN_LEFT
+}
+
+/* Multiprotocol unreachable parse */
+int
+bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
+                     struct bgp_nlri *mp_withdraw)
+{
+  struct stream *s;
+  afi_t afi;
+  safi_t safi;
+  u_int16_t withdraw_len;
+  struct peer *const peer = args->peer;  
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+
+  s = peer->ibuf;
+  
+#define BGP_MP_UNREACH_MIN_SIZE 3
+  if ((length > STREAM_READABLE(s)) || (length <  BGP_MP_UNREACH_MIN_SIZE))
+    return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+  
+  afi = stream_getw (s);
+  safi = stream_getc (s);
+  
+  withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
+
+  mp_withdraw->afi = afi;
+  mp_withdraw->safi = safi;
+  mp_withdraw->nlri = stream_pnt (s);
+  mp_withdraw->length = withdraw_len;
+
+  stream_forward_getp (s, withdraw_len);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Large Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_large_community (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer;
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+
+  if (length == 0)
+    {
+      if (attr->extra)
+        attr->extra->lcommunity = NULL;
+      /* Empty extcomm doesn't seem to be invalid per se */
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+
+  (bgp_attr_extra_get (attr))->lcommunity =
+    lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+  /* XXX: fix ecommunity_parse to use stream API */
+  stream_forward_getp (peer->ibuf, length);
+
+  if (attr->extra && !attr->extra->lcommunity)
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                               args->total);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Extended Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
+{
+  struct peer *const peer = args->peer;  
+  struct attr *const attr = args->attr;  
+  const bgp_size_t length = args->length;
+  
+  if (length == 0)
+    {
+      if (attr->extra)
+        attr->extra->ecommunity = NULL;
+      /* Empty extcomm doesn't seem to be invalid per se */
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+
+  (bgp_attr_extra_get (attr))->ecommunity =
+    ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+  /* XXX: fix ecommunity_parse to use stream API */
+  stream_forward_getp (peer->ibuf, length);
+  
+  if (attr->extra && !attr->extra->ecommunity)
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                               args->total);
+  
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Parse Tunnel Encap attribute in an UPDATE */
+static int
+bgp_attr_encap(
+  uint8_t      type,
+  struct peer  *peer,  /* IN */
+  bgp_size_t   length, /* IN: attr's length field */
+  struct attr  *attr,  /* IN: caller already allocated */
+  u_char       flag,   /* IN: attr's flags field */
+  u_char       *startp)
+{
+  bgp_size_t                   total;
+  struct attr_extra            *attre = NULL;
+  struct bgp_attr_encap_subtlv *stlv_last = NULL;
+  uint16_t                     tunneltype;
+
+  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+  if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS)
+       || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL))
+    {
+      zlog (peer->log, LOG_ERR,
+           "Tunnel Encap attribute flag isn't optional and transitive %d", flag);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+                                startp, total);
+      return -1;
+    }
+
+  if (BGP_ATTR_ENCAP == type) {
+    /* read outer TLV type and length */
+    uint16_t   tlv_length;
+
+    if (length < 4) {
+       zlog (peer->log, LOG_ERR,
+           "Tunnel Encap attribute not long enough to contain outer T,L");
+       bgp_notify_send_with_data(peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+       return -1;
+    }
+    tunneltype = stream_getw (BGP_INPUT (peer));
+    tlv_length = stream_getw (BGP_INPUT (peer));
+    length -= 4;
+
+    if (tlv_length != length) {
+       zlog (peer->log, LOG_ERR, "%s: tlv_length(%d) != length(%d)",
+           __func__, tlv_length, length);
+    }
+  }
+
+  while (length >= 4) {
+    uint16_t   subtype = 0;
+    uint16_t   sublength = 0;
+    struct bgp_attr_encap_subtlv *tlv;
+
+    if (BGP_ATTR_ENCAP == type) {
+        subtype   = stream_getc (BGP_INPUT (peer));
+        sublength = stream_getc (BGP_INPUT (peer));
+        length   -= 2;
+    }
+
+    if (sublength > length) {
+      zlog (peer->log, LOG_ERR,
+           "Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d",
+           sublength, length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+    }
+
+    /* alloc and copy sub-tlv */
+    /* TBD make sure these are freed when attributes are released */
+    tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+sublength);
+    tlv->type = subtype;
+    tlv->length = sublength;
+    stream_get(tlv->value, peer->ibuf, sublength);
+    length -= sublength;
+
+    /* attach tlv to encap chain */
+    if (!attre) {
+       attre = bgp_attr_extra_get(attr);
+       if (BGP_ATTR_ENCAP == type) {
+           for (stlv_last = attre->encap_subtlvs; stlv_last && stlv_last->next;
+               stlv_last = stlv_last->next);
+           if (stlv_last) {
+               stlv_last->next = tlv;
+           } else {
+               attre->encap_subtlvs = tlv;
+           }
+       }
+    } else {
+       stlv_last->next = tlv;
+    }
+    stlv_last = tlv;
+  }
+
+  if (attre && (BGP_ATTR_ENCAP == type)) {
+      attre->encap_tunneltype = tunneltype;
+  }
+
+  if (length) {
+    /* spurious leftover data */
+      zlog (peer->log, LOG_ERR,
+           "Tunnel Encap attribute length is bad: %d leftover octets", length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+  }
+
+  return 0;
+}
+
+/* BGP unknown attribute treatment. */
+static bgp_attr_parse_ret_t
+bgp_attr_unknown (struct bgp_attr_parser_args *args)
+{
+  bgp_size_t total = args->total;
+  struct transit *transit;
+  struct attr_extra *attre;
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  u_char *const startp = args->startp;
+  const u_char type = args->type;
+  const u_char flag = args->flags;  
+  const bgp_size_t length = args->length;
+  
+
+  if (BGP_DEBUG (normal, NORMAL))
+  zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
+             peer->host, type, length);
+  
+  if (BGP_DEBUG (events, EVENTS))
+    zlog (peer->log, LOG_DEBUG, 
+         "Unknown attribute type %d length %d is received", type, length);
+
+  /* Forward read pointer of input stream. */
+  stream_forward_getp (peer->ibuf, length);
+
+  /* If any of the mandatory well-known attributes are not recognized,
+     then the Error Subcode is set to Unrecognized Well-known
+     Attribute.  The Data field contains the unrecognized attribute
+     (type, length and value). */
+  if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
+    {
+      return bgp_attr_malformed (args,
+                                 BGP_NOTIFY_UPDATE_UNREC_ATTR,
+                                 args->total);
+    }
+
+  /* Unrecognized non-transitive optional attributes must be quietly
+     ignored and not passed along to other BGP peers. */
+  if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
+    return BGP_ATTR_PARSE_PROCEED;
+
+  /* If a path with recognized transitive optional attribute is
+     accepted and passed along to other BGP peers and the Partial bit
+     in the Attribute Flags octet is set to 1 by some previous AS, it
+     is not set back to 0 by the current AS. */
+  SET_FLAG (*startp, BGP_ATTR_FLAG_PARTIAL);
+
+  /* Store transitive attribute to the end of attr->transit. */
+  if (! ((attre = bgp_attr_extra_get(attr))->transit) )
+      attre->transit = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit));
+
+  transit = attre->transit;
+
+  if (transit->val)
+    transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val, 
+                            transit->length + total);
+  else
+    transit->val = XMALLOC (MTYPE_TRANSIT_VAL, total);
+
+  memcpy (transit->val + transit->length, startp, total);
+  transit->length += total;
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Well-known attribute check. */
+static int
+bgp_attr_check (struct peer *peer, struct attr *attr)
+{
+  u_char type = 0;
+  
+  /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an
+   * empty UPDATE.  */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag)
+    return BGP_ATTR_PARSE_PROCEED;
+  
+  /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required
+     to carry any other path attributes.", though if MP_REACH_NLRI or NLRI
+     are present, it should.  Check for any other attribute being present
+     instead.
+   */
+  if (attr->flag == ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI))
+    return BGP_ATTR_PARSE_PROCEED;
+  
+  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
+    type = BGP_ATTR_ORIGIN;
+
+  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
+    type = BGP_ATTR_AS_PATH;
+  
+  /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and
+   * NLRI is empty. We can't easily check NLRI empty here though.
+   */
+  if (!CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))
+      && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI)))
+    type = BGP_ATTR_NEXT_HOP;
+  
+  if (peer->sort == BGP_PEER_IBGP
+      && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+    type = BGP_ATTR_LOCAL_PREF;
+
+  if (type)
+    {
+      zlog (peer->log, LOG_WARNING, 
+           "%s Missing well-known attribute %d / %s",
+           peer->host, type, LOOKUP (attr_str, type));
+      bgp_notify_send_with_data (peer, 
+                                BGP_NOTIFY_UPDATE_ERR, 
+                                BGP_NOTIFY_UPDATE_MISS_ATTR,
+                                &type, 1);
+      return BGP_ATTR_PARSE_ERROR;
+    }
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Read attribute of update packet.  This function is called from
+   bgp_update_receive() in bgp_packet.c.  */
+bgp_attr_parse_ret_t
+bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
+               struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw)
+{
+  int ret;
+  u_char flag = 0;
+  u_char type = 0;
+  bgp_size_t length;
+  u_char *startp, *endp;
+  u_char *attr_endp;
+  u_char seen[BGP_ATTR_BITMAP_SIZE];
+  /* we need the as4_path only until we have synthesized the as_path with it */
+  /* same goes for as4_aggregator */
+  struct aspath *as4_path = NULL;
+  as_t as4_aggregator = 0;
+  struct in_addr as4_aggregator_addr = { .s_addr = 0 };
+
+  /* Initialize bitmap. */
+  memset (seen, 0, BGP_ATTR_BITMAP_SIZE);
+
+  /* End pointer of BGP attribute. */
+  endp = BGP_INPUT_PNT (peer) + size;
+  
+  /* Get attributes to the end of attribute length. */
+  while (BGP_INPUT_PNT (peer) < endp)
+    {
+      /* Check remaining length check.*/
+      if (endp - BGP_INPUT_PNT (peer) < BGP_ATTR_MIN_LEN)
+       {
+         /* XXX warning: long int format, int arg (arg 5) */
+         zlog (peer->log, LOG_WARNING, 
+               "%s: error BGP attribute length %lu is smaller than min len",
+               peer->host,
+               (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
+
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR, 
+                          BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+         return BGP_ATTR_PARSE_ERROR;
+       }
+
+      /* Fetch attribute flag and type. */
+      startp = BGP_INPUT_PNT (peer);
+      /* "The lower-order four bits of the Attribute Flags octet are
+         unused.  They MUST be zero when sent and MUST be ignored when
+         received." */
+      flag = 0xF0 & stream_getc (BGP_INPUT (peer));
+      type = stream_getc (BGP_INPUT (peer));
+
+      /* Check whether Extended-Length applies and is in bounds */
+      if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN)
+          && ((endp - startp) < (BGP_ATTR_MIN_LEN + 1)))
+       {
+         zlog (peer->log, LOG_WARNING, 
+               "%s: Extended length set, but just %lu bytes of attr header",
+               peer->host,
+               (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
+
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR, 
+                          BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+         return BGP_ATTR_PARSE_ERROR;
+       }
+      
+      /* Check extended attribue length bit. */
+      if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN))
+       length = stream_getw (BGP_INPUT (peer));
+      else
+       length = stream_getc (BGP_INPUT (peer));
+      
+      /* If any attribute appears more than once in the UPDATE
+        message, then the Error Subcode is set to Malformed Attribute
+        List. */
+
+      if (CHECK_BITMAP (seen, type))
+       {
+         zlog (peer->log, LOG_WARNING,
+               "%s: error BGP attribute type %d appears twice in a message",
+               peer->host, type);
+
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR, 
+                          BGP_NOTIFY_UPDATE_MAL_ATTR);
+         return BGP_ATTR_PARSE_ERROR;
+       }
+
+      /* Set type to bitmap to check duplicate attribute.  `type' is
+        unsigned char so it never overflow bitmap range. */
+
+      SET_BITMAP (seen, type);
+
+      /* Overflow check. */
+      attr_endp =  BGP_INPUT_PNT (peer) + length;
+
+      if (attr_endp > endp)
+       {
+         zlog (peer->log, LOG_WARNING, 
+               "%s: BGP type %d length %d is too large, attribute total length is %d.  attr_endp is %p.  endp is %p", peer->host, type, length, size, attr_endp, endp);
+         zlog_warn ("%s: BGP type %d length %d is too large, attribute total length is %d.  attr_endp is %p.  endp is %p", peer->host, type, length, size, attr_endp, endp);
+          bgp_notify_send_with_data (peer,
+                                     BGP_NOTIFY_UPDATE_ERR,
+                                     BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                     startp, attr_endp - startp);
+         return BGP_ATTR_PARSE_ERROR;
+       }
+       
+        struct bgp_attr_parser_args attr_args = {
+          .peer = peer,
+          .length = length,
+          .attr = attr,
+          .type = type,
+          .flags = flag,
+          .startp = startp,
+          .total = attr_endp - startp,
+        };
+      
+       
+      /* If any recognized attribute has Attribute Flags that conflict
+         with the Attribute Type Code, then the Error Subcode is set to
+         Attribute Flags Error.  The Data field contains the erroneous
+         attribute (type, length and value). */
+      if (bgp_attr_flag_invalid (&attr_args))
+        {
+          bgp_attr_parse_ret_t ret;
+          ret = bgp_attr_malformed (&attr_args,
+                                    BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+                                    attr_args.total);
+          if (ret == BGP_ATTR_PARSE_PROCEED)
+            continue;
+          return ret;
+        }
+
+      /* OK check attribute and store it's value. */
+      switch (type)
+       {
+       case BGP_ATTR_ORIGIN:
+         ret = bgp_attr_origin (&attr_args);
+         break;
+       case BGP_ATTR_AS_PATH:
+         ret = bgp_attr_aspath (&attr_args);
+         break;
+       case BGP_ATTR_AS4_PATH:
+         ret = bgp_attr_as4_path (&attr_args, &as4_path);
+         break;
+       case BGP_ATTR_NEXT_HOP: 
+         ret = bgp_attr_nexthop (&attr_args);
+         break;
+       case BGP_ATTR_MULTI_EXIT_DISC:
+         ret = bgp_attr_med (&attr_args);
+         break;
+       case BGP_ATTR_LOCAL_PREF:
+         ret = bgp_attr_local_pref (&attr_args);
+         break;
+       case BGP_ATTR_ATOMIC_AGGREGATE:
+         ret = bgp_attr_atomic (&attr_args);
+         break;
+       case BGP_ATTR_AGGREGATOR:
+         ret = bgp_attr_aggregator (&attr_args);
+         break;
+       case BGP_ATTR_AS4_AGGREGATOR:
+         ret = bgp_attr_as4_aggregator (&attr_args,
+                                        &as4_aggregator,
+                                        &as4_aggregator_addr);
+         break;
+       case BGP_ATTR_COMMUNITIES:
+         ret = bgp_attr_community (&attr_args);
+         break;
+       case BGP_ATTR_LARGE_COMMUNITIES:
+         ret = bgp_attr_large_community (&attr_args);
+         break;
+       case BGP_ATTR_ORIGINATOR_ID:
+         ret = bgp_attr_originator_id (&attr_args);
+         break;
+       case BGP_ATTR_CLUSTER_LIST:
+         ret = bgp_attr_cluster_list (&attr_args);
+         break;
+       case BGP_ATTR_MP_REACH_NLRI:
+         ret = bgp_mp_reach_parse (&attr_args, mp_update);
+         break;
+       case BGP_ATTR_MP_UNREACH_NLRI:
+         ret = bgp_mp_unreach_parse (&attr_args, mp_withdraw);
+         break;
+       case BGP_ATTR_EXT_COMMUNITIES:
+         ret = bgp_attr_ext_communities (&attr_args);
+         break;
+        case BGP_ATTR_ENCAP:
+          ret = bgp_attr_encap (type, peer, length, attr, flag, startp);
+          break;
+       default:
+         ret = bgp_attr_unknown (&attr_args);
+         break;
+       }
+      
+      if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS)
+       {
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR,
+                          BGP_NOTIFY_UPDATE_MAL_ATTR);
+         ret = BGP_ATTR_PARSE_ERROR;
+       }
+
+      /* If hard error occured immediately return to the caller. */
+      if (ret == BGP_ATTR_PARSE_ERROR)
+        {
+          zlog (peer->log, LOG_WARNING,
+                "%s: Attribute %s, parse error", 
+                peer->host, 
+                LOOKUP (attr_str, type));
+          if (as4_path)
+            aspath_unintern (&as4_path);
+          return ret;
+        }
+      if (ret == BGP_ATTR_PARSE_WITHDRAW)
+        {
+          
+          zlog (peer->log, LOG_WARNING,
+                "%s: Attribute %s, parse error - treating as withdrawal",
+                peer->host,
+                LOOKUP (attr_str, type));
+          if (as4_path)
+            aspath_unintern (&as4_path);
+          return ret;
+        }
+      
+      /* Check the fetched length. */
+      if (BGP_INPUT_PNT (peer) != attr_endp)
+       {
+         zlog (peer->log, LOG_WARNING, 
+               "%s: BGP attribute %s, fetch error", 
+                peer->host, LOOKUP (attr_str, type));
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR, 
+                          BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+          if (as4_path)
+            aspath_unintern (&as4_path);
+         return BGP_ATTR_PARSE_ERROR;
+       }
+    }
+  /* Check final read pointer is same as end pointer. */
+  if (BGP_INPUT_PNT (peer) != endp)
+    {
+      zlog (peer->log, LOG_WARNING, 
+           "%s: BGP attribute %s, length mismatch",
+           peer->host, LOOKUP (attr_str, type));
+      bgp_notify_send (peer, 
+                      BGP_NOTIFY_UPDATE_ERR, 
+                      BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+      if (as4_path)
+        aspath_unintern (&as4_path);
+      return BGP_ATTR_PARSE_ERROR;
+    }
+  
+  /* Check all mandatory well-known attributes are present */
+  {
+    bgp_attr_parse_ret_t ret;
+    if ((ret = bgp_attr_check (peer, attr)) < 0)
+      {
+        if (as4_path)
+          aspath_unintern (&as4_path);
+        return ret;
+      }
+  }
+  
+  /* 
+   * At this place we can see whether we got AS4_PATH and/or
+   * AS4_AGGREGATOR from a 16Bit peer and act accordingly.
+   * We can not do this before we've read all attributes because
+   * the as4 handling does not say whether AS4_PATH has to be sent
+   * after AS_PATH or not - and when AS4_AGGREGATOR will be send
+   * in relationship to AGGREGATOR.
+   * So, to be defensive, we are not relying on any order and read
+   * all attributes first, including these 32bit ones, and now,
+   * afterwards, we look what and if something is to be done for as4.
+   *
+   * It is possible to not have AS_PATH, e.g. GR EoR and sole
+   * MP_UNREACH_NLRI.
+   */
+  /* actually... this doesn't ever return failure currently, but
+   * better safe than sorry */
+  if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))
+      && bgp_attr_munge_as4_attrs (peer, attr, as4_path,
+                                as4_aggregator, &as4_aggregator_addr))
+    {
+      bgp_notify_send (peer, 
+                      BGP_NOTIFY_UPDATE_ERR,
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
+      if (as4_path)
+        aspath_unintern (&as4_path);
+      return BGP_ATTR_PARSE_ERROR;
+    }
+
+  /* At this stage, we have done all fiddling with as4, and the
+   * resulting info is in attr->aggregator resp. attr->aspath
+   * so we can chuck as4_aggregator and as4_path alltogether in
+   * order to save memory
+   */
+  if (as4_path)
+    {
+      aspath_unintern (&as4_path); /* unintern - it is in the hash */
+      /* The flag that we got this is still there, but that does not
+       * do any trouble
+       */
+    }
+  /*
+   * The "rest" of the code does nothing with as4_aggregator.
+   * there is no memory attached specifically which is not part
+   * of the attr.
+   * so ignoring just means do nothing.
+   */
+  /*
+   * Finally do the checks on the aspath we did not do yet
+   * because we waited for a potentially synthesized aspath.
+   */
+  if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
+    {
+      ret = bgp_attr_aspath_check (peer, attr);
+      if (ret != BGP_ATTR_PARSE_PROCEED)
+       return ret;
+    }
+
+  /* Finally intern unknown attribute. */
+  if (attr->extra && attr->extra->transit)
+    attr->extra->transit = transit_intern (attr->extra->transit);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
+int stream_put_prefix (struct stream *, struct prefix *);
+
+size_t
+bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi,
+                        struct attr *attr)
+{
+  size_t sizep;
+
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+  sizep = stream_get_endp (s);
+  stream_putw (s, 0);  /* Marker: Attribute length. */
+
+  stream_putw (s, afi);
+  stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi);
+
+  /* Nexthop */
+  switch (afi)
+    {
+    case AFI_IP:
+      switch (safi)
+       {
+       case SAFI_MULTICAST:
+         stream_putc (s, 4);
+         stream_put_ipv4 (s, attr->nexthop.s_addr);
+         break;
+       case SAFI_MPLS_VPN:
+         stream_putc (s, 12);
+         stream_putl (s, 0);   /* RD = 0, per RFC */
+         stream_putl (s, 0);
+         stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+         break;
+       case SAFI_ENCAP:
+         stream_putc (s, 4);
+         stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+         break;
+       case SAFI_UNICAST:      /* invalid for IPv4 */
+       default:
+         break;
+       }
+      break;
+    case AFI_IP6:
+      switch (safi)
+      {
+      case SAFI_UNICAST:
+      case SAFI_MULTICAST:
+       {
+         struct attr_extra *attre = attr->extra;
+
+         assert (attr->extra);
+         stream_putc (s, attre->mp_nexthop_len);
+         stream_put (s, &attre->mp_nexthop_global, 16);
+         if (attre->mp_nexthop_len == 32)
+           stream_put (s, &attre->mp_nexthop_local, 16);
+       }
+       break;
+      case SAFI_MPLS_VPN:
+       {
+         struct attr_extra *attre = attr->extra;
+
+         assert (attr->extra);
+          if (attre->mp_nexthop_len == 16) {
+            stream_putc (s, 24);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_global, 16);
+          } else if (attre->mp_nexthop_len == 32) {
+            stream_putc (s, 48);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_global, 16);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_local, 16);
+          }
+        }
+       break;
+       case SAFI_ENCAP:
+          assert (attr->extra);
+          stream_putc (s, 16);
+         stream_put (s, &attr->extra->mp_nexthop_global, 16);
+         break;
+      default:
+       break;
+      }
+      break;
+    default:
+      break;
+    }
+
+  /* SNPA */
+  stream_putc (s, 0);
+  return sizep;
+}
+
+void
+bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi,
+                         struct prefix *p, struct prefix_rd *prd,
+                         u_char *tag)
+{
+  if (safi == SAFI_MPLS_VPN)
+    {
+      /* Tag, RD, Prefix write. */
+      stream_putc (s, p->prefixlen + 88);
+      stream_put (s, tag, 3);
+      stream_put (s, prd->val, 8);
+      stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
+    }
+  else
+    stream_put_prefix (s, p);
+}
+
+size_t
+bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p)
+{
+  int size = PSIZE (p->prefixlen);
+  if (safi == SAFI_MPLS_VPN)
+      size += 88;
+  return size;
+}
+
+/*
+ * Encodes the tunnel encapsulation attribute
+ */
+static void
+bgp_packet_mpattr_tea(
+    struct bgp         *bgp,
+    struct peer                *peer,
+    struct stream      *s,
+    struct attr                *attr,
+    uint8_t            attrtype)
+{
+    unsigned int                       attrlenfield = 0;
+    unsigned int                       attrhdrlen   = 0;
+    struct bgp_attr_encap_subtlv       *subtlvs;
+    struct bgp_attr_encap_subtlv       *st;
+    const char                         *attrname;
+
+    if (!attr || !attr->extra)
+       return;
+
+    switch (attrtype) {
+       case BGP_ATTR_ENCAP:
+           attrname = "Tunnel Encap";
+           subtlvs = attr->extra->encap_subtlvs;
+
+           /*
+            * The tunnel encap attr has an "outer" tlv.
+            * T = tunneltype,
+            * L = total length of subtlvs,
+            * V = concatenated subtlvs.
+            */
+           attrlenfield = 2 + 2;       /* T + L */
+            attrhdrlen   = 1 + 1;      /* subTLV T + L */
+           break;
+
+       default:
+           assert(0);
+    }
+
+
+    /* if no tlvs, don't make attr */
+    if (subtlvs == NULL)
+       return;
+
+    /* compute attr length */
+    for (st = subtlvs; st; st = st->next) {
+       attrlenfield += (attrhdrlen + st->length);
+    }
+
+    if (attrlenfield > 0xffff) {
+       zlog (peer->log, LOG_ERR,
+           "%s attribute is too long (length=%d), can't send it",
+           attrname,
+           attrlenfield);
+       return;
+    }
+
+    if (attrlenfield > 0xff) {
+       /* 2-octet length field */
+       stream_putc (s,
+           BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+       stream_putc (s, attrtype);
+       stream_putw (s, attrlenfield & 0xffff);
+    } else {
+       /* 1-octet length field */
+       stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL);
+       stream_putc (s, attrtype);
+       stream_putc (s, attrlenfield & 0xff);
+    }
+
+    if (attrtype == BGP_ATTR_ENCAP) {
+       /* write outer T+L */
+       stream_putw(s, attr->extra->encap_tunneltype);
+       stream_putw(s, attrlenfield - 4);
+    }
+
+    /* write each sub-tlv */
+    for (st = subtlvs; st; st = st->next) {
+        if (attrtype == BGP_ATTR_ENCAP) {
+            stream_putc (s, st->type);
+            stream_putc (s, st->length);
+        }
+       stream_put (s, st->value, st->length);
+    }
+}
+
+void
+bgp_packet_mpattr_end (struct stream *s, size_t sizep)
+{
+  /* Set MP attribute length. Don't count the (2) bytes used to encode
+     the attr length */
+  stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2);
+}
+
+/* Make attribute packet. */
+bgp_size_t
+bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
+                     struct stream *s, struct attr *attr,
+                     struct prefix *p, afi_t afi, safi_t safi,
+                     struct peer *from, struct prefix_rd *prd, u_char *tag)
+{
+  size_t cp;
+  size_t aspath_sizep;
+  struct aspath *aspath;
+  int send_as4_path = 0;
+  int send_as4_aggregator = 0;
+  int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0;
+
+  if (! bgp)
+    bgp = bgp_get_default ();
+
+  /* Remember current pointer. */
+  cp = stream_get_endp (s);
+
+  if (p && !(afi == AFI_IP && safi == SAFI_UNICAST))
+    {
+      size_t mpattrlen_pos = 0;
+      mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr);
+      bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag);
+      bgp_packet_mpattr_end(s, mpattrlen_pos);
+    }
+
+  /* Origin attribute. */
+  stream_putc (s, BGP_ATTR_FLAG_TRANS);
+  stream_putc (s, BGP_ATTR_ORIGIN);
+  stream_putc (s, 1);
+  stream_putc (s, attr->origin);
+
+  /* AS path attribute. */
+
+  /* If remote-peer is EBGP */
+  if (peer->sort == BGP_PEER_EBGP
+      && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
+         || attr->aspath->segments == NULL)
+      && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)))
+    {    
+      aspath = aspath_dup (attr->aspath);
+
+      if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+       {
+         /* Strip the confed info, and then stuff our path CONFED_ID
+            on the front */
+         aspath = aspath_delete_confed_seq (aspath);
+         aspath = aspath_add_seq (aspath, bgp->confed_id);
+       }
+      else
+       {
+         if (peer->change_local_as) {
+            /* If replace-as is specified, we only use the change_local_as when
+               advertising routes. */
+            if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) {
+              aspath = aspath_add_seq (aspath, peer->local_as);
+            }
+           aspath = aspath_add_seq (aspath, peer->change_local_as);
+          } else {
+            aspath = aspath_add_seq (aspath, peer->local_as);
+          }
+       }
+    }
+  else if (peer->sort == BGP_PEER_CONFED)
+    {
+      /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */
+      aspath = aspath_dup (attr->aspath);
+      aspath = aspath_add_confed_seq (aspath, peer->local_as);
+    }
+  else
+    aspath = attr->aspath;
+
+  /* If peer is not AS4 capable, then:
+   * - send the created AS_PATH out as AS4_PATH (optional, transitive),
+   *   but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path segment
+   *   types are in it (i.e. exclude them if they are there)
+   *   AND do this only if there is at least one asnum > 65535 in the path!
+   * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and change
+   *   all ASnums > 65535 to BGP_AS_TRANS
+   */
+
+  stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_AS_PATH);
+  aspath_sizep = stream_get_endp (s);
+  stream_putw (s, 0);
+  stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, use32bit));
+  
+  /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs 
+   * in the path
+   */
+  if (!use32bit && aspath_has_as4 (aspath))
+      send_as4_path = 1; /* we'll do this later, at the correct place */
+  
+  /* Nexthop attribute. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP &&
+    safi ==  SAFI_UNICAST)   /* only write NH attr for unicast safi */
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_NEXT_HOP);
+      stream_putc (s, 4);
+      if (safi == SAFI_MPLS_VPN)
+       {
+         if (attr->nexthop.s_addr == 0)
+           stream_put_ipv4 (s, peer->nexthop.v4.s_addr);
+         else
+           stream_put_ipv4 (s, attr->nexthop.s_addr);
+       }
+      else
+       stream_put_ipv4 (s, attr->nexthop.s_addr);
+    }
+
+  /* MED attribute. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC);
+      stream_putc (s, 4);
+      stream_putl (s, attr->med);
+    }
+
+  /* Local preference. */
+  if (peer->sort == BGP_PEER_IBGP ||
+      peer->sort == BGP_PEER_CONFED)
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_LOCAL_PREF);
+      stream_putc (s, 4);
+      stream_putl (s, attr->local_pref);
+    }
+
+  /* Atomic aggregate. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE);
+      stream_putc (s, 0);
+    }
+
+  /* Aggregator. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
+    {
+      assert (attr->extra);
+      
+      /* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AGGREGATOR);
+      
+      if (use32bit)
+        {
+          /* AS4 capable peer */
+          stream_putc (s, 8);
+          stream_putl (s, attr->extra->aggregator_as);
+        }
+      else
+        {
+          /* 2-byte AS peer */
+          stream_putc (s, 6);
+          
+          /* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */
+          if ( attr->extra->aggregator_as > 65535 )
+            {
+              stream_putw (s, BGP_AS_TRANS);
+              
+              /* we have to send AS4_AGGREGATOR, too.
+               * we'll do that later in order to send attributes in ascending
+               * order.
+               */
+              send_as4_aggregator = 1;
+            }
+          else
+            stream_putw (s, (u_int16_t) attr->extra->aggregator_as);
+        }
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+    }
+
+  /* Community attribute. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) 
+      && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
+    {
+      if (attr->community->size * 4 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putw (s, attr->community->size * 4);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putc (s, attr->community->size * 4);
+       }
+      stream_put (s, attr->community->val, attr->community->size * 4);
+    }
+
+  /*
+   * Large Community attribute.
+   */
+  if (attr->extra &&
+      CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)
+      && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)))
+    {
+      if (attr->extra->lcommunity->size * 12 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putw (s, attr->extra->lcommunity->size * 12);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putc (s, attr->extra->lcommunity->size * 12);
+       }
+      stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+    }
+
+  /* Route Reflector. */
+  if (peer->sort == BGP_PEER_IBGP
+      && from
+      && from->sort == BGP_PEER_IBGP)
+    {
+      /* Originator ID. */
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc (s, BGP_ATTR_ORIGINATOR_ID);
+      stream_putc (s, 4);
+
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+       stream_put_in_addr (s, &attr->extra->originator_id);
+      else 
+        stream_put_in_addr (s, &from->remote_id);
+
+      /* Cluster list. */
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc (s, BGP_ATTR_CLUSTER_LIST);
+      
+      if (attr->extra && attr->extra->cluster)
+       {
+         stream_putc (s, attr->extra->cluster->length + 4);
+         /* If this peer configuration's parent BGP has cluster_id. */
+         if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+           stream_put_in_addr (s, &bgp->cluster_id);
+         else
+           stream_put_in_addr (s, &bgp->router_id);
+         stream_put (s, attr->extra->cluster->list, 
+                     attr->extra->cluster->length);
+       }
+      else
+       {
+         stream_putc (s, 4);
+         /* If this peer configuration's parent BGP has cluster_id. */
+         if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+           stream_put_in_addr (s, &bgp->cluster_id);
+         else
+           stream_put_in_addr (s, &bgp->router_id);
+       }
+    }
+
+  /* Extended Communities attribute. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) 
+      && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+    {
+      struct attr_extra *attre = attr->extra;
+      
+      assert (attre);
+      
+      if (peer->sort == BGP_PEER_IBGP
+          || peer->sort == BGP_PEER_CONFED)
+       {
+         if (attre->ecommunity->size * 8 > 255)
+           {
+             stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+             stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+             stream_putw (s, attre->ecommunity->size * 8);
+           }
+         else
+           {
+             stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+             stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+             stream_putc (s, attre->ecommunity->size * 8);
+           }
+         stream_put (s, attre->ecommunity->val, attre->ecommunity->size * 8);
+       }
+      else
+       {
+         u_int8_t *pnt;
+         int tbit;
+         int ecom_tr_size = 0;
+         int i;
+
+         for (i = 0; i < attre->ecommunity->size; i++)
+           {
+             pnt = attre->ecommunity->val + (i * 8);
+             tbit = *pnt;
+
+             if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
+               continue;
+
+             ecom_tr_size++;
+           }
+
+         if (ecom_tr_size)
+           {
+             if (ecom_tr_size * 8 > 255)
+               {
+                 stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+                 stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+                 stream_putw (s, ecom_tr_size * 8);
+               }
+             else
+               {
+                 stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+                 stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+                 stream_putc (s, ecom_tr_size * 8);
+               }
+
+             for (i = 0; i < attre->ecommunity->size; i++)
+               {
+                 pnt = attre->ecommunity->val + (i * 8);
+                 tbit = *pnt;
+
+                 if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
+                   continue;
+
+                 stream_put (s, pnt, 8);
+               }
+           }
+       }
+    }
+
+  if ( send_as4_path )
+    {
+      /* If the peer is NOT As4 capable, AND */
+      /* there are ASnums > 65535 in path  THEN
+       * give out AS4_PATH */
+
+      /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET
+       * path segments!
+       * Hm, I wonder...  confederation things *should* only be at
+       * the beginning of an aspath, right?  Then we should use
+       * aspath_delete_confed_seq for this, because it is already
+       * there! (JK) 
+       * Folks, talk to me: what is reasonable here!?
+       */
+      aspath = aspath_delete_confed_seq (aspath);
+
+      stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+      stream_putc (s, BGP_ATTR_AS4_PATH);
+      aspath_sizep = stream_get_endp (s);
+      stream_putw (s, 0);
+      stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, 1));
+    }
+
+  if (aspath != attr->aspath)
+    aspath_free (aspath);
+
+  if ( send_as4_aggregator ) 
+    {
+      assert (attr->extra);
+
+      /* send AS4_AGGREGATOR, at this place */
+      /* this section of code moved here in order to ensure the correct
+       * *ascending* order of attributes
+       */
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AS4_AGGREGATOR);
+      stream_putc (s, 8);
+      stream_putl (s, attr->extra->aggregator_as);
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+    }
+
+  if ((afi == AFI_IP || afi == AFI_IP6) &&
+      (safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN))
+    {
+       /* Tunnel Encap attribute */
+       bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP);
+    }
+
+  /* Unknown transit attribute. */
+  if (attr->extra && attr->extra->transit)
+    stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
+
+  /* Return total size of attribute. */
+  return stream_get_endp (s) - cp;
+}
+
+size_t
+bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi)
+{
+  unsigned long attrlen_pnt;
+
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
+
+  attrlen_pnt = stream_get_endp (s);
+  stream_putw (s, 0);          /* Length of this attribute. */
+
+  stream_putw (s, afi);
+  stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi);
+  return attrlen_pnt;
+}
+
+void
+bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
+                            afi_t afi, safi_t safi, struct prefix_rd *prd,
+                            u_char *tag)
+{
+  bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag);
+}
+
+void
+bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt)
+{
+  bgp_packet_mpattr_end (s, attrlen_pnt);
+}
+
+/* Initialization of attribute. */
+void
+bgp_attr_init (void)
+{
+  aspath_init ();
+  attrhash_init ();
+  community_init ();
+  ecommunity_init ();
+  lcommunity_init ();
+  cluster_init ();
+  transit_init ();
+}
+
+void
+bgp_attr_finish (void)
+{
+  aspath_finish ();
+  attrhash_finish ();
+  community_finish ();
+  ecommunity_finish ();
+  lcommunity_finish ();
+  cluster_finish ();
+  transit_finish ();
+}
+
+/* Make attribute packet. */
+void
+bgp_dump_routes_attr (struct stream *s, struct attr *attr, 
+                      struct prefix *prefix)
+{
+  unsigned long cp;
+  unsigned long len;
+  size_t aspath_lenp;
+  struct aspath *aspath;
+
+  /* Remember current pointer. */
+  cp = stream_get_endp (s);
+
+  /* Place holder of length. */
+  stream_putw (s, 0);
+
+  /* Origin attribute. */
+  stream_putc (s, BGP_ATTR_FLAG_TRANS);
+  stream_putc (s, BGP_ATTR_ORIGIN);
+  stream_putc (s, 1);
+  stream_putc (s, attr->origin);
+
+  aspath = attr->aspath;
+  
+  stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_AS_PATH);
+  aspath_lenp = stream_get_endp (s);
+  stream_putw (s, 0);
+  
+  stream_putw_at (s, aspath_lenp, aspath_put (s, aspath, 1));
+
+  /* Nexthop attribute. */
+  /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */
+  if(prefix != NULL
+     && prefix->family != AF_INET6
+     )
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_NEXT_HOP);
+      stream_putc (s, 4);
+      stream_put_ipv4 (s, attr->nexthop.s_addr);
+    }
+
+  /* MED attribute. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC);
+      stream_putc (s, 4);
+      stream_putl (s, attr->med);
+    }
+
+  /* Local preference. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_LOCAL_PREF);
+      stream_putc (s, 4);
+      stream_putl (s, attr->local_pref);
+    }
+
+  /* Atomic aggregate. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE);
+      stream_putc (s, 0);
+    }
+
+  /* Aggregator. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
+    {
+      assert (attr->extra);
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AGGREGATOR);
+      stream_putc (s, 8);
+      stream_putl (s, attr->extra->aggregator_as);
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+    }
+
+  /* Community attribute. */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))
+    {
+      if (attr->community->size * 4 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putw (s, attr->community->size * 4);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putc (s, attr->community->size * 4);
+       }
+      stream_put (s, attr->community->val, attr->community->size * 4);
+    }
+
+    /* Large Community attribute. */
+  if (attr->extra && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))
+    {
+      if (attr->extra->lcommunity->size * 12 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putw (s, attr->extra->lcommunity->size * 12);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_COMMUNITIES);
+         stream_putc (s, attr->extra->lcommunity->size * 12);
+       }
+
+      stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+    }
+
+  /* Add a MP_NLRI attribute to dump the IPv6 next hop */
+  if (prefix != NULL && prefix->family == AF_INET6 && attr->extra &&
+     (attr->extra->mp_nexthop_len == 16 || attr->extra->mp_nexthop_len == 32) )
+    {
+      int sizep;
+      struct attr_extra *attre = attr->extra;
+      
+      stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
+      sizep = stream_get_endp (s);
+
+      /* MP header */
+      stream_putc (s, 0);              /* Marker: Attribute length. */
+      stream_putw(s, AFI_IP6);         /* AFI */
+      stream_putc(s, SAFI_UNICAST);    /* SAFI */
+
+      /* Next hop */
+      stream_putc(s, attre->mp_nexthop_len);
+      stream_put(s, &attre->mp_nexthop_global, 16);
+      if (attre->mp_nexthop_len == 32)
+        stream_put(s, &attre->mp_nexthop_local, 16);
+
+      /* SNPA */
+      stream_putc(s, 0);
+
+      /* Prefix */
+      stream_put_prefix(s, prefix);
+
+      /* Set MP attribute length. */
+      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
+    }
+
+  /* Return total size of attribute. */
+  len = stream_get_endp (s) - cp - 2;
+  stream_putw_at (s, cp, len);
+}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
new file mode 100644 (file)
index 0000000..9ff074b
--- /dev/null
@@ -0,0 +1,238 @@
+/* BGP attributes. 
+   Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ATTR_H
+#define _QUAGGA_BGP_ATTR_H
+
+/* Simple bit mapping. */
+#define BITMAP_NBBY 8
+
+#define SET_BITMAP(MAP, NUM) \
+        SET_FLAG (MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
+
+#define CHECK_BITMAP(MAP, NUM) \
+        CHECK_FLAG (MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
+
+#define BGP_MED_MAX UINT32_MAX
+
+
+/* BGP Attribute type range. */
+#define BGP_ATTR_TYPE_RANGE     256
+#define BGP_ATTR_BITMAP_SIZE    (BGP_ATTR_TYPE_RANGE / BITMAP_NBBY)
+
+/* BGP Attribute flags. */
+#define BGP_ATTR_FLAG_OPTIONAL  0x80   /* Attribute is optional. */
+#define BGP_ATTR_FLAG_TRANS     0x40   /* Attribute is transitive. */
+#define BGP_ATTR_FLAG_PARTIAL   0x20   /* Attribute is partial. */
+#define BGP_ATTR_FLAG_EXTLEN    0x10   /* Extended length flag. */
+
+/* BGP attribute header must bigger than 2. */
+#define BGP_ATTR_MIN_LEN        3       /* Attribute flag, type length. */
+#define BGP_ATTR_DEFAULT_WEIGHT 32768
+
+struct bgp_attr_encap_subtlv {
+    struct bgp_attr_encap_subtlv       *next;          /* for chaining */
+    uint16_t                           type;
+    uint16_t                           length;
+    uint8_t                            value[1];       /* will be extended */
+};
+
+/* Additional/uncommon BGP attributes.
+ * lazily allocated as and when a struct attr
+ * requires it.
+ */
+struct attr_extra
+{
+  /* Multi-Protocol Nexthop, AFI IPv6 */
+  struct in6_addr mp_nexthop_global;
+  struct in6_addr mp_nexthop_local;
+
+  /* Extended Communities attribute. */
+  struct ecommunity *ecommunity;
+
+  /* Large Communities attribute. */
+  struct lcommunity *lcommunity;
+  
+  /* Route-Reflector Cluster attribute */
+  struct cluster_list *cluster;
+  
+  /* Unknown transitive attribute. */
+  struct transit *transit;
+
+  struct in_addr mp_nexthop_global_in;
+  
+  /* Aggregator Router ID attribute */
+  struct in_addr aggregator_addr;
+  
+  /* Route Reflector Originator attribute */
+  struct in_addr originator_id;
+  
+  /* Local weight, not actually an attribute */
+  u_int32_t weight;
+  
+  /* Aggregator ASN */
+  as_t aggregator_as;
+  
+  /* MP Nexthop length */
+  u_char mp_nexthop_len;
+
+  uint16_t                     encap_tunneltype;       /* grr */
+  struct bgp_attr_encap_subtlv *encap_subtlvs;         /* rfc5512 */
+
+  /* route tag */
+  route_tag_t tag;
+};
+
+/* BGP core attribute structure. */
+struct attr
+{
+  /* AS Path structure */
+  struct aspath *aspath;
+
+  /* Community structure */
+  struct community *community; 
+  
+  /* Lazily allocated pointer to extra attributes */
+  struct attr_extra *extra;
+  
+  /* Reference count of this attribute. */
+  unsigned long refcnt;
+
+  /* Flag of attribute is set or not. */
+  u_int32_t flag;
+  
+  /* Apart from in6_addr, the remaining static attributes */
+  struct in_addr nexthop;
+  u_int32_t med;
+  u_int32_t local_pref;
+  
+  /* Path origin attribute */
+  u_char origin;
+};
+
+/* Router Reflector related structure. */
+struct cluster_list
+{
+  unsigned long refcnt;
+  int length;
+  struct in_addr *list;
+};
+
+/* Unknown transit attribute. */
+struct transit
+{
+  unsigned long refcnt;
+  int length;
+  u_char *val;
+};
+
+#define ATTR_FLAG_BIT(X)  (1 << ((X) - 1))
+
+typedef enum {
+ BGP_ATTR_PARSE_PROCEED = 0,
+ BGP_ATTR_PARSE_ERROR = -1,
+ BGP_ATTR_PARSE_WITHDRAW = -2,
+
+ /* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR */
+ BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3,
+} bgp_attr_parse_ret_t;
+
+/* Prototypes. */
+extern void bgp_attr_init (void);
+extern void bgp_attr_finish (void);
+extern bgp_attr_parse_ret_t bgp_attr_parse (struct peer *, struct attr *,
+                                           bgp_size_t, struct bgp_nlri *,
+                                           struct bgp_nlri *);
+extern struct attr_extra *bgp_attr_extra_get (struct attr *);
+extern void bgp_attr_extra_free (struct attr *);
+extern void bgp_attr_dup (struct attr *, struct attr *);
+extern struct attr *bgp_attr_intern (struct attr *attr);
+extern void bgp_attr_unintern_sub (struct attr *);
+extern void bgp_attr_unintern (struct attr **);
+extern void bgp_attr_flush (struct attr *);
+extern struct attr *bgp_attr_default_set (struct attr *attr, u_char);
+extern struct attr *bgp_attr_default_intern (u_char);
+extern struct attr *bgp_attr_aggregate_intern (struct bgp *, u_char,
+                                        struct aspath *, 
+                                        struct community *, int as_set, u_char);
+extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *,
+                                       struct stream *, struct attr *,
+                                       struct prefix *, afi_t, safi_t,
+                                       struct peer *, struct prefix_rd *,
+                                       u_char *);
+extern void bgp_dump_routes_attr (struct stream *, struct attr *,
+                                 struct prefix *);
+extern int attrhash_cmp (const void *, const void *);
+extern unsigned int attrhash_key_make (void *);
+extern void attr_show_all (struct vty *);
+extern unsigned long int attr_count (void);
+extern unsigned long int attr_unknown_count (void);
+
+/* Cluster list prototypes. */
+extern int cluster_loop_check (struct cluster_list *, struct in_addr);
+extern void cluster_unintern (struct cluster_list *);
+
+/* Transit attribute prototypes. */
+void transit_unintern (struct transit *);
+
+/* Below exported for unit-test purposes only */
+struct bgp_attr_parser_args {
+  struct peer *peer;
+  bgp_size_t length; /* attribute data length; */
+  bgp_size_t total; /* total length, inc header */
+  struct attr *attr;
+  u_int8_t type;
+  u_int8_t flags;
+  u_char *startp;   
+};
+extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args, 
+                              struct bgp_nlri *);
+extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
+                                 struct bgp_nlri *);
+
+extern struct bgp_attr_encap_subtlv *
+encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
+
+extern void
+bgp_attr_flush_encap(struct attr *attr);
+
+/**
+ * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
+ * Typical call sequence is to call _start(), followed by multiple _prefix(),
+ * one for each NLRI that needs to be encoded into the UPDATE message, and
+ * finally the _end() function.
+ */
+extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi,
+                                     struct attr *attr);
+extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
+                                    struct prefix *p, struct prefix_rd *prd,
+                                    u_char *tag);
+extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
+                                            struct prefix *p);
+extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep);
+
+extern size_t bgp_packet_mpunreach_start (struct stream *s, afi_t afi,
+                                         safi_t safi);
+extern void bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
+                            afi_t afi, safi_t safi, struct prefix_rd *prd,
+                            u_char *tag);
+extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt);
+
+#endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c
new file mode 100644 (file)
index 0000000..b408efd
--- /dev/null
@@ -0,0 +1,313 @@
+/* BGP dump to ascii converter
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "zebra.h"
+#include "stream.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "privs.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+
+/* privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+    ZCAP_BIND,
+    ZCAP_NET_RAW,
+    ZCAP_NET_ADMIN,
+};
+
+struct zebra_privs_t bgpd_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = array_size(_caps_p),
+  .cap_num_i = 0,
+};
+
+enum MRT_MSG_TYPES {
+   MSG_NULL,
+   MSG_START,                   /* sender is starting up */
+   MSG_DIE,                     /* receiver should shut down */
+   MSG_I_AM_DEAD,               /* sender is shutting down */
+   MSG_PEER_DOWN,               /* sender's peer is down */
+   MSG_PROTOCOL_BGP,            /* msg is a BGP packet */
+   MSG_PROTOCOL_RIP,            /* msg is a RIP packet */
+   MSG_PROTOCOL_IDRP,           /* msg is an IDRP packet */
+   MSG_PROTOCOL_RIPNG,          /* msg is a RIPNG packet */
+   MSG_PROTOCOL_BGP4PLUS,       /* msg is a BGP4+ packet */
+   MSG_PROTOCOL_BGP4PLUS_01,    /* msg is a BGP4+ (draft 01) packet */
+   MSG_PROTOCOL_OSPF,           /* msg is an OSPF packet */
+   MSG_TABLE_DUMP               /* routing table dump */
+};
+
+static int
+attr_parse (struct stream *s, u_int16_t len)
+{
+  u_int flag;
+  u_int type;
+  u_int16_t length;
+  u_int16_t lim;
+
+  lim = s->getp + len;
+
+  printf ("attr_parse s->getp %zd, len %d, lim %d\n", s->getp, len, lim);
+
+  while (s->getp < lim)
+    {
+      flag = stream_getc (s);
+      type = stream_getc (s);
+
+      if (flag & BGP_ATTR_FLAG_EXTLEN)
+       length = stream_getw (s);
+      else
+       length = stream_getc (s);
+
+      printf ("FLAG: %d\n", flag);
+      printf ("TYPE: %d\n", type);
+      printf ("Len: %d\n", length);
+
+      switch (type)
+       {
+       case BGP_ATTR_ORIGIN:
+         {
+           u_char origin;
+           origin = stream_getc (s);
+           printf ("ORIGIN: %d\n", origin);
+         }
+         break;
+       case BGP_ATTR_AS_PATH:
+         {
+           struct aspath *aspath;
+
+           aspath = aspath_parse (s, length, 1);
+           printf ("ASPATH: %s\n", aspath->str);
+           aspath_free(aspath);
+         }
+         break;
+       case BGP_ATTR_NEXT_HOP:
+         {
+           struct in_addr nexthop;
+           nexthop.s_addr = stream_get_ipv4 (s);
+           printf ("NEXTHOP: %s\n", inet_ntoa (nexthop));
+         }
+         break;
+       default:
+         stream_getw_from (s, length);
+         break;
+       }
+    }
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  int ret;
+  FILE *fp;
+  struct stream *s;
+  time_t now;
+  int type;
+  int subtype;
+  size_t len;
+  int source_as;
+  int dest_as;
+  ifindex_t ifindex;
+  int family;
+  struct in_addr sip;
+  struct in_addr dip;
+  u_int16_t viewno, seq_num;
+  struct prefix_ipv4 p;
+
+  s = stream_new (10000);
+
+  if (argc != 2)
+    {
+      fprintf (stderr, "Usage: %s FILENAME\n", argv[0]);
+      exit (1);
+    }
+  fp = fopen (argv[1], "r");
+  if (!fp)
+    {
+      perror ("fopen");
+      exit (1);
+    }
+  
+  while (1)
+    {
+      stream_reset (s);
+
+      ret = fread (s->data, 12, 1, fp);
+      if (!ret || feof (fp))
+       {
+         printf ("END OF FILE\n");
+         break;
+       }
+      if (ferror (fp))
+       {
+         printf ("ERROR OF FREAD\n");
+         break;
+       }
+
+      /* Extract header. */
+      now = stream_getl (s);
+      type = stream_getw (s);
+      subtype = stream_getw (s);
+      len = stream_getl (s);
+
+      printf ("TIME: %s", ctime (&now));
+
+      /* printf ("TYPE: %d/%d\n", type, subtype); */
+
+      if (type == MSG_PROTOCOL_BGP4MP)
+       printf ("TYPE: BGP4MP");
+      else if (type == MSG_PROTOCOL_BGP4MP_ET)
+       printf ("TYPE: BGP4MP_ET");
+      else if (type == MSG_TABLE_DUMP)
+       printf ("TYPE: MSG_TABLE_DUMP");
+      else
+       printf ("TYPE: Unknown %d", type);
+
+      if (type == MSG_TABLE_DUMP)
+       switch (subtype)
+         {
+         case AFI_IP:
+           printf ("/AFI_IP\n");
+           break;
+         case AFI_IP6:
+           printf ("/AFI_IP6\n");
+           break;
+         default:
+           printf ("/UNKNOWN %d", subtype);
+           break;
+         }
+      else
+       {
+         switch (subtype)
+           {
+           case BGP4MP_STATE_CHANGE:
+             printf ("/CHANGE\n");
+             break;
+           case BGP4MP_MESSAGE:
+             printf ("/MESSAGE\n");
+             break;
+           case BGP4MP_ENTRY:
+             printf ("/ENTRY\n");
+             break;
+           case BGP4MP_SNAPSHOT:
+             printf ("/SNAPSHOT\n");
+             break;
+           default:
+             printf ("/UNKNOWN %d", subtype);
+             break;
+           }
+       }
+
+      printf ("len: %zd\n", len);
+
+      ret = fread (s->data + 12, len, 1, fp);
+      if (feof (fp))
+       {
+         printf ("ENDOF FILE 2\n");
+         break;
+       }
+      if (ferror (fp))
+       {
+         printf ("ERROR OF FREAD 2\n");
+         break;
+       }
+
+      /* printf ("now read %d\n", len); */
+
+      if (type == MSG_TABLE_DUMP)
+       {
+         u_char status;
+         time_t originated;
+         struct in_addr peer;
+         u_int16_t attrlen;
+
+         viewno = stream_getw (s);
+         seq_num = stream_getw (s);
+         printf ("VIEW: %d\n", viewno);
+         printf ("SEQUENCE: %d\n", seq_num);
+
+         /* start */
+         while (s->getp < len - 16)
+           {
+             p.prefix.s_addr = stream_get_ipv4 (s);
+             p.prefixlen = stream_getc (s);
+             printf ("PREFIX: %s/%d\n", inet_ntoa (p.prefix), p.prefixlen);
+
+             status = stream_getc (s);
+             originated = stream_getl (s);
+             peer.s_addr = stream_get_ipv4 (s);
+             source_as = stream_getw(s);
+
+             printf ("FROM: %s AS%d\n", inet_ntoa (peer), source_as);
+             printf ("ORIGINATED: %s", ctime (&originated));
+
+             attrlen = stream_getw (s);
+             printf ("ATTRLEN: %d\n", attrlen);
+
+             attr_parse (s, attrlen);
+
+             printf ("STATUS: 0x%x\n", status);
+           }
+       }
+      else
+       {
+         source_as = stream_getw (s);
+         dest_as = stream_getw (s);
+         printf ("source_as: %d\n", source_as);
+         printf ("dest_as: %d\n", dest_as);
+
+         ifindex = stream_getw (s);
+         family = stream_getw (s);
+
+         printf ("ifindex: %d\n", ifindex);
+         printf ("family: %d\n", family);
+
+         sip.s_addr = stream_get_ipv4 (s);
+         dip.s_addr = stream_get_ipv4 (s);
+         
+         printf ("saddr: %s\n", inet_ntoa (sip));
+         printf ("daddr: %s\n", inet_ntoa (dip));
+
+         printf ("\n");
+       }
+    }
+  fclose (fp);
+  return 0;
+}
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
new file mode 100644 (file)
index 0000000..13bdf8e
--- /dev/null
@@ -0,0 +1,1235 @@
+/* BGP community-list and extcommunity-list.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+
+/* Lookup master structure for community-list or
+   extcommunity-list.  */
+struct community_list_master *
+community_list_master_lookup (struct community_list_handler *ch, int master)
+{
+  if (ch)
+    switch (master)
+      {
+      case COMMUNITY_LIST_MASTER:
+       return &ch->community_list;
+      case EXTCOMMUNITY_LIST_MASTER:
+       return &ch->extcommunity_list;
+      case LARGE_COMMUNITY_LIST_MASTER:
+       return &ch->lcommunity_list;
+      }
+  return NULL;
+}
+
+/* Allocate a new community list entry.  */
+static struct community_entry *
+community_entry_new (void)
+{
+  return XCALLOC (MTYPE_COMMUNITY_LIST_ENTRY, sizeof (struct community_entry));
+}
+
+/* Free community list entry.  */
+static void
+community_entry_free (struct community_entry *entry)
+{
+  switch (entry->style)
+    {
+    case COMMUNITY_LIST_STANDARD:
+      if (entry->u.com)
+        community_free (entry->u.com);
+      break;
+    case LARGE_COMMUNITY_LIST_STANDARD:
+      if (entry->u.lcom)
+        lcommunity_free (&entry->u.lcom);
+      break;
+    case EXTCOMMUNITY_LIST_STANDARD:
+      /* In case of standard extcommunity-list, configuration string
+         is made by ecommunity_ecom2str().  */
+      if (entry->config)
+        XFREE (MTYPE_ECOMMUNITY_STR, entry->config);
+      if (entry->u.ecom)
+        ecommunity_free (&entry->u.ecom);
+      break;
+    case COMMUNITY_LIST_EXPANDED:
+    case EXTCOMMUNITY_LIST_EXPANDED:
+    case LARGE_COMMUNITY_LIST_EXPANDED:
+      if (entry->config)
+        XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
+      if (entry->reg)
+        bgp_regex_free (entry->reg);
+    default:
+      break;
+    }
+  XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry);
+}
+
+/* Allocate a new community-list.  */
+static struct community_list *
+community_list_new (void)
+{
+  return XCALLOC (MTYPE_COMMUNITY_LIST, sizeof (struct community_list));
+}
+
+/* Free community-list.  */
+static void
+community_list_free (struct community_list *list)
+{
+  if (list->name)
+    XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name);
+  XFREE (MTYPE_COMMUNITY_LIST, list);
+}
+
+static struct community_list *
+community_list_insert (struct community_list_handler *ch,
+                      const char *name, int master)
+{
+  size_t i;
+  long number;
+  struct community_list *new;
+  struct community_list *point;
+  struct community_list_list *list;
+  struct community_list_master *cm;
+
+  /* Lookup community-list master.  */
+  cm = community_list_master_lookup (ch, master);
+  if (!cm)
+    return NULL;
+
+  /* Allocate new community_list and copy given name. */
+  new = community_list_new ();
+  new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, name);
+
+  /* If name is made by all digit character.  We treat it as
+     number. */
+  for (number = 0, i = 0; i < strlen (name); i++)
+    {
+      if (isdigit ((int) name[i]))
+        number = (number * 10) + (name[i] - '0');
+      else
+        break;
+    }
+
+  /* In case of name is all digit character */
+  if (i == strlen (name))
+    {
+      new->sort = COMMUNITY_LIST_NUMBER;
+
+      /* Set access_list to number list. */
+      list = &cm->num;
+
+      for (point = list->head; point; point = point->next)
+        if (atol (point->name) >= number)
+          break;
+    }
+  else
+    {
+      new->sort = COMMUNITY_LIST_STRING;
+
+      /* Set access_list to string list. */
+      list = &cm->str;
+
+      /* Set point to insertion point. */
+      for (point = list->head; point; point = point->next)
+        if (strcmp (point->name, name) >= 0)
+          break;
+    }
+
+  /* Link to upper list.  */
+  new->parent = list;
+
+  /* In case of this is the first element of master. */
+  if (list->head == NULL)
+    {
+      list->head = list->tail = new;
+      return new;
+    }
+
+  /* In case of insertion is made at the tail of access_list. */
+  if (point == NULL)
+    {
+      new->prev = list->tail;
+      list->tail->next = new;
+      list->tail = new;
+      return new;
+    }
+
+  /* In case of insertion is made at the head of access_list. */
+  if (point == list->head)
+    {
+      new->next = list->head;
+      list->head->prev = new;
+      list->head = new;
+      return new;
+    }
+
+  /* Insertion is made at middle of the access_list. */
+  new->next = point;
+  new->prev = point->prev;
+
+  if (point->prev)
+    point->prev->next = new;
+  point->prev = new;
+
+  return new;
+}
+
+struct community_list *
+community_list_lookup (struct community_list_handler *ch,
+                      const char *name, int master)
+{
+  struct community_list *list;
+  struct community_list_master *cm;
+
+  if (!name)
+    return NULL;
+
+  cm = community_list_master_lookup (ch, master);
+  if (!cm)
+    return NULL;
+
+  for (list = cm->num.head; list; list = list->next)
+    if (strcmp (list->name, name) == 0)
+      return list;
+  for (list = cm->str.head; list; list = list->next)
+    if (strcmp (list->name, name) == 0)
+      return list;
+
+  return NULL;
+}
+
+static struct community_list *
+community_list_get (struct community_list_handler *ch,
+                   const char *name, int master)
+{
+  struct community_list *list;
+
+  list = community_list_lookup (ch, name, master);
+  if (!list)
+    list = community_list_insert (ch, name, master);
+  return list;
+}
+
+static void
+community_list_delete (struct community_list *list)
+{
+  struct community_list_list *clist;
+  struct community_entry *entry, *next;
+
+  for (entry = list->head; entry; entry = next)
+    {
+      next = entry->next;
+      community_entry_free (entry);
+    }
+
+  clist = list->parent;
+
+  if (list->next)
+    list->next->prev = list->prev;
+  else
+    clist->tail = list->prev;
+
+  if (list->prev)
+    list->prev->next = list->next;
+  else
+    clist->head = list->next;
+
+  community_list_free (list);
+}
+
+static int
+community_list_empty_p (struct community_list *list)
+{
+  return (list->head == NULL && list->tail == NULL) ? 1 : 0;
+}
+
+/* Add community-list entry to the list.  */
+static void
+community_list_entry_add (struct community_list *list,
+                          struct community_entry *entry)
+{
+  entry->next = NULL;
+  entry->prev = list->tail;
+
+  if (list->tail)
+    list->tail->next = entry;
+  else
+    list->head = entry;
+  list->tail = entry;
+}
+
+/* Delete community-list entry from the list.  */
+static void
+community_list_entry_delete (struct community_list *list,
+                             struct community_entry *entry, int style)
+{
+  if (entry->next)
+    entry->next->prev = entry->prev;
+  else
+    list->tail = entry->prev;
+
+  if (entry->prev)
+    entry->prev->next = entry->next;
+  else
+    list->head = entry->next;
+
+  community_entry_free (entry);
+
+  if (community_list_empty_p (list))
+    community_list_delete (list);
+}
+
+/* Lookup community-list entry from the list.  */
+static struct community_entry *
+community_list_entry_lookup (struct community_list *list, const void *arg,
+                             int direct)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      switch (entry->style)
+        {
+        case COMMUNITY_LIST_STANDARD:
+          if (community_cmp (entry->u.com, arg))
+            return entry;
+          break;
+       case LARGE_COMMUNITY_LIST_STANDARD:
+          if (lcommunity_cmp (entry->u.lcom, arg))
+            return entry;
+          break;
+        case EXTCOMMUNITY_LIST_STANDARD:
+          if (ecommunity_cmp (entry->u.ecom, arg))
+            return entry;
+          break;
+        case COMMUNITY_LIST_EXPANDED:
+        case EXTCOMMUNITY_LIST_EXPANDED:
+       case LARGE_COMMUNITY_LIST_EXPANDED:
+          if (strcmp (entry->config, arg) == 0)
+            return entry;
+          break;
+        default:
+          break;
+        }
+    }
+  return NULL;
+}
+
+static char *
+community_str_get (struct community *com, int i)
+{
+  int len;
+  u_int32_t comval;
+  u_int16_t as;
+  u_int16_t val;
+  char *str;
+  char *pnt;
+
+  memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
+  comval = ntohl (comval);
+
+  switch (comval)
+    {
+      case COMMUNITY_INTERNET:
+        len = strlen (" internet");
+        break;
+      case COMMUNITY_NO_EXPORT:
+        len = strlen (" no-export");
+        break;
+      case COMMUNITY_NO_ADVERTISE:
+        len = strlen (" no-advertise");
+        break;
+      case COMMUNITY_LOCAL_AS:
+        len = strlen (" local-AS");
+        break;
+      default:
+        len = strlen (" 65536:65535");
+        break;
+    }
+
+  /* Allocate memory.  */
+  str = pnt = XMALLOC (MTYPE_COMMUNITY_STR, len);
+
+  switch (comval)
+    {
+      case COMMUNITY_INTERNET:
+        strcpy (pnt, "internet");
+        pnt += strlen ("internet");
+        break;
+      case COMMUNITY_NO_EXPORT:
+        strcpy (pnt, "no-export");
+        pnt += strlen ("no-export");
+        break;
+      case COMMUNITY_NO_ADVERTISE:
+        strcpy (pnt, "no-advertise");
+        pnt += strlen ("no-advertise");
+        break;
+      case COMMUNITY_LOCAL_AS:
+        strcpy (pnt, "local-AS");
+        pnt += strlen ("local-AS");
+        break;
+      default:
+        as = (comval >> 16) & 0xFFFF;
+        val = comval & 0xFFFF;
+        sprintf (pnt, "%u:%d", as, val);
+        pnt += strlen (pnt);
+        break;
+    }
+
+  *pnt = '\0';
+
+  return str;
+}
+
+/* Internal function to perform regular expression match for
+ *  * a single community. */
+static int
+community_regexp_include (regex_t * reg, struct community *com, int i)
+{
+  char *str;
+  int rv;
+
+  /* When there is no communities attribute it is treated as empty
+ *      string.  */
+  if (com == NULL || com->size == 0)
+    str = XSTRDUP(MTYPE_COMMUNITY_STR, "");
+  else
+    str = community_str_get (com, i);
+
+  /* Regular expression match.  */
+  rv = regexec (reg, str, 0, NULL, 0);
+
+  XFREE(MTYPE_COMMUNITY_STR, str);
+
+  if (rv == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+/* Internal function to perform regular expression match for community
+   attribute.  */
+static int
+community_regexp_match (struct community *com, regex_t * reg)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+     string.  */
+  if (com == NULL || com->size == 0)
+    str = "";
+  else
+    str = community_str (com);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+static char *
+lcommunity_str_get (struct lcommunity *lcom, int i)
+{
+  struct lcommunity_val lcomval;
+  u_int32_t globaladmin;
+  u_int32_t localdata1;
+  u_int32_t localdata2;
+  char *str;
+  u_char *ptr;
+  char *pnt;
+
+  ptr = lcom->val;
+  ptr += (i * LCOMMUNITY_SIZE);
+
+  memcpy (&lcomval, ptr, LCOMMUNITY_SIZE);
+
+  /* Allocate memory.  48 bytes taken off bgp_lcommunity.c */
+  str = pnt = XMALLOC (MTYPE_LCOMMUNITY_STR, 48);
+
+  ptr = (u_char *)lcomval.val;
+  globaladmin = (*ptr++ << 24);
+  globaladmin |= (*ptr++ << 16);
+  globaladmin |= (*ptr++ << 8);
+  globaladmin |= (*ptr++);
+
+  localdata1 = (*ptr++ << 24);
+  localdata1 |= (*ptr++ << 16);
+  localdata1 |= (*ptr++ << 8);
+  localdata1 |= (*ptr++);
+
+  localdata2 = (*ptr++ << 24);
+  localdata2 |= (*ptr++ << 16);
+  localdata2 |= (*ptr++ << 8);
+  localdata2 |= (*ptr++);
+
+  sprintf (pnt, "%u:%u:%u", globaladmin, localdata1, localdata2);
+  pnt += strlen (pnt);
+  *pnt = '\0';
+
+  return str;
+}
+
+/* Internal function to perform regular expression match for
+ *  * a single community. */
+static int
+lcommunity_regexp_include (regex_t * reg, struct lcommunity *lcom, int i)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+ *      string.  */
+  if (lcom == NULL || lcom->size == 0)
+    str = "";
+  else
+    str = lcommunity_str_get (lcom, i);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+static int
+lcommunity_regexp_match (struct lcommunity *com, regex_t * reg)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+     string.  */
+  if (com == NULL || com->size == 0)
+    str = "";
+  else
+    str = lcommunity_str (com);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+
+static int
+ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+     string.  */
+  if (ecom == NULL || ecom->size == 0)
+    str = "";
+  else
+    str = ecommunity_str (ecom);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+/* When given community attribute matches to the community-list return
+   1 else return 0.  */
+int
+community_list_match (struct community *com, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->any)
+        return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+      if (entry->style == COMMUNITY_LIST_STANDARD)
+        {
+          if (community_include (entry->u.com, COMMUNITY_INTERNET))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+          if (community_match (com, entry->u.com))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+      else if (entry->style == COMMUNITY_LIST_EXPANDED)
+        {
+          if (community_regexp_match (com, entry->reg))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+    }
+  return 0;
+}
+
+int
+lcommunity_list_match (struct lcommunity *lcom, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->any)
+        return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+      if (entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+        {
+          if (lcommunity_match (lcom, entry->u.lcom))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+      else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED)
+        {
+          if (lcommunity_regexp_match (lcom, entry->reg))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+    }
+  return 0;
+}
+
+int
+ecommunity_list_match (struct ecommunity *ecom, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->any)
+        return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+      if (entry->style == EXTCOMMUNITY_LIST_STANDARD)
+        {
+          if (ecommunity_match (ecom, entry->u.ecom))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+      else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED)
+        {
+          if (ecommunity_regexp_match (ecom, entry->reg))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+    }
+  return 0;
+}
+
+/* Perform exact matching.  In case of expanded community-list, do
+   same thing as community_list_match().  */
+int
+community_list_exact_match (struct community *com,
+                            struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->any)
+        return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+      if (entry->style == COMMUNITY_LIST_STANDARD)
+        {
+          if (community_include (entry->u.com, COMMUNITY_INTERNET))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+          if (community_cmp (com, entry->u.com))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+      else if (entry->style == COMMUNITY_LIST_EXPANDED)
+        {
+          if (community_regexp_match (com, entry->reg))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+    }
+  return 0;
+}
+
+/* Delete all permitted communities in the list from com.  */
+struct community *
+community_list_match_delete (struct community *com,
+                             struct community_list *list)
+{
+  struct community_entry *entry;
+  u_int32_t val;
+  u_int32_t com_index_to_delete[com->size];
+  int delete_index = 0;
+  int i;
+
+  /* Loop over each community value and evaluate each against the
+   * community-list.  If we need to delete a community value add its index to
+   * com_index_to_delete.
+   */
+  for (i = 0; i < com->size; i++)
+    {
+      val = community_val_get (com, i);
+
+      for (entry = list->head; entry; entry = entry->next)
+        {
+          if (entry->any)
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == COMMUNITY_LIST_STANDARD)
+                   && (community_include (entry->u.com, COMMUNITY_INTERNET)
+                       || community_include (entry->u.com, val) ))
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == COMMUNITY_LIST_EXPANDED)
+                   && community_regexp_include (entry->reg, com, i))
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+         }
+     }
+
+  /* Delete all of the communities we flagged for deletion */
+  for (i = delete_index-1; i >= 0; i--)
+    {
+      val = community_val_get (com, com_index_to_delete[i]);
+      community_del_val (com, &val);
+    }
+
+  return com;
+}
+
+/* To avoid duplicated entry in the community-list, this function
+   compares specified entry to existing entry.  */
+static int
+community_list_dup_check (struct community_list *list,
+                          struct community_entry *new)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->style != new->style)
+        continue;
+
+      if (entry->direct != new->direct)
+        continue;
+
+      if (entry->any != new->any)
+        continue;
+
+      if (entry->any)
+        return 1;
+
+      switch (entry->style)
+        {
+        case COMMUNITY_LIST_STANDARD:
+          if (community_cmp (entry->u.com, new->u.com))
+            return 1;
+          break;
+        case EXTCOMMUNITY_LIST_STANDARD:
+          if (ecommunity_cmp (entry->u.ecom, new->u.ecom))
+            return 1;
+          break;
+        case LARGE_COMMUNITY_LIST_STANDARD:
+          if (lcommunity_cmp (entry->u.lcom, new->u.lcom))
+            return 1;
+          break;
+        case COMMUNITY_LIST_EXPANDED:
+        case EXTCOMMUNITY_LIST_EXPANDED:
+        case LARGE_COMMUNITY_LIST_EXPANDED:
+          if (entry->config && new->config
+              && strcmp (entry->config, new->config) == 0)
+            return 1;
+          if (!entry->config && !new->config)
+            return 1;
+          break;
+        default:
+          break;
+        }
+    }
+  return 0;
+}
+
+/* Set community-list.  */
+int
+community_list_set (struct community_list_handler *ch,
+                    const char *name, const char *str, int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct community *com = NULL;
+  regex_t *regex = NULL;
+
+  /* Get community list. */
+  list = community_list_get (ch, name, COMMUNITY_LIST_MASTER);
+
+  /* When community-list already has entry, new entry should have same
+     style.  If you want to have mixed style community-list, you can
+     comment out this check.  */
+  if (!community_list_empty_p (list))
+    {
+      struct community_entry *first;
+
+      first = list->head;
+
+      if (style != first->style)
+       {
+         return (first->style == COMMUNITY_LIST_STANDARD
+                 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+                 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+       }
+    }
+
+  if (str)
+    {
+      if (style == COMMUNITY_LIST_STANDARD)
+       com = community_str2com (str);
+      else
+       regex = bgp_regcomp (str);
+
+      if (! com && ! regex)
+       return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+    }
+
+  entry = community_entry_new ();
+  entry->direct = direct;
+  entry->style = style;
+  entry->any = (str ? 0 : 1);
+  entry->u.com = com;
+  entry->reg = regex;
+  entry->config = (regex ? XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
+
+  /* Do not put duplicated community entry.  */
+  if (community_list_dup_check (list, entry))
+    community_entry_free (entry);
+  else
+    community_list_entry_add (list, entry);
+
+  return 0;
+}
+
+/* Unset community-list.  When str is NULL, delete all of
+   community-list entry belongs to the specified name.  */
+int
+community_list_unset (struct community_list_handler *ch,
+                      const char *name, const char *str, 
+                      int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct community *com = NULL;
+  regex_t *regex = NULL;
+
+  /* Lookup community list.  */
+  list = community_list_lookup (ch, name, COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  /* Delete all of entry belongs to this community-list.  */
+  if (!str)
+    {
+      community_list_delete (list);
+      return 0;
+    }
+
+  if (style == COMMUNITY_LIST_STANDARD)
+    com = community_str2com (str);
+  else
+    regex = bgp_regcomp (str);
+
+  if (! com && ! regex)
+    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+  if (com)
+    entry = community_list_entry_lookup (list, com, direct);
+  else
+    entry = community_list_entry_lookup (list, str, direct);
+
+  if (com)
+    community_free (com);
+  if (regex)
+    bgp_regex_free (regex);
+
+  if (!entry)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  community_list_entry_delete (list, entry, style);
+
+  return 0;
+}
+
+/* Delete all permitted large communities in the list from com.  */
+struct lcommunity *
+lcommunity_list_match_delete (struct lcommunity *lcom,
+                             struct community_list *list)
+{
+  struct community_entry *entry;
+  u_int32_t com_index_to_delete[lcom->size];
+  u_char *ptr;
+  int delete_index = 0;
+  int i;
+
+  /* Loop over each lcommunity value and evaluate each against the
+   * community-list.  If we need to delete a community value add its index to
+   * com_index_to_delete.
+   */
+
+  for (i = 0; i < lcom->size; i++)
+    {
+      ptr = lcom->val + (i * LCOMMUNITY_SIZE);
+      for (entry = list->head; entry; entry = entry->next)
+        {
+          if (entry->any)
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+                   && lcommunity_include (entry->u.lcom, ptr) )
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+                   && entry->reg
+                  && lcommunity_regexp_include (entry->reg, lcom, i))
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+         }
+     }
+
+  /* Delete all of the communities we flagged for deletion */
+
+  for (i = delete_index-1; i >= 0; i--)
+    {
+      ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE);
+      lcommunity_del_val (lcom, ptr);
+    }
+
+  return lcom;
+}
+
+/* Set lcommunity-list.  */
+int
+lcommunity_list_set (struct community_list_handler *ch,
+                    const char *name, const char *str, int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct lcommunity *lcom = NULL;
+  regex_t *regex = NULL;
+
+  /* Get community list. */
+  list = community_list_get (ch, name, LARGE_COMMUNITY_LIST_MASTER);
+
+  /* When community-list already has entry, new entry should have same
+     style.  If you want to have mixed style community-list, you can
+     comment out this check.  */
+  if (!community_list_empty_p (list))
+    {
+      struct community_entry *first;
+
+      first = list->head;
+
+      if (style != first->style)
+       {
+         return (first->style == COMMUNITY_LIST_STANDARD
+                 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+                 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+       }
+    }
+
+  if (str)
+    {
+      if (style == LARGE_COMMUNITY_LIST_STANDARD)
+       lcom = lcommunity_str2com (str);
+      else
+       regex = bgp_regcomp (str);
+
+      if (! lcom && ! regex)
+       return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+    }
+
+  entry = community_entry_new ();
+  entry->direct = direct;
+  entry->style = style;
+  entry->any = (str ? 0 : 1);
+  entry->u.lcom = lcom;
+  entry->reg = regex;
+  if (lcom)
+    entry->config = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST);
+  else if (regex)
+    entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
+  else
+    entry->config = NULL;
+
+  /* Do not put duplicated community entry.  */
+  if (community_list_dup_check (list, entry))
+    community_entry_free (entry);
+  else
+    community_list_entry_add (list, entry);
+
+  return 0;
+}
+
+/* Unset community-list.  When str is NULL, delete all of
+   community-list entry belongs to the specified name.  */
+int
+lcommunity_list_unset (struct community_list_handler *ch,
+                      const char *name, const char *str,
+                      int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct lcommunity *lcom = NULL;
+  regex_t *regex = NULL;
+
+  /* Lookup community list.  */
+  list = community_list_lookup (ch, name, LARGE_COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  /* Delete all of entry belongs to this community-list.  */
+  if (!str)
+    {
+      community_list_delete (list);
+      return 0;
+    }
+
+  if (style == LARGE_COMMUNITY_LIST_STANDARD)
+    lcom = lcommunity_str2com (str);
+  else
+    regex = bgp_regcomp (str);
+
+  if (! lcom && ! regex)
+    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+  if (lcom)
+    entry = community_list_entry_lookup (list, lcom, direct);
+  else
+    entry = community_list_entry_lookup (list, str, direct);
+
+  if (lcom)
+    lcommunity_free (&lcom);
+  if (regex)
+    bgp_regex_free (regex);
+
+  if (!entry)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  community_list_entry_delete (list, entry, style);
+
+  return 0;
+}
+
+/* Set extcommunity-list.  */
+int
+extcommunity_list_set (struct community_list_handler *ch,
+                       const char *name, const char *str, 
+                       int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct ecommunity *ecom = NULL;
+  regex_t *regex = NULL;
+
+  entry = NULL;
+
+  /* Get community list. */
+  list = community_list_get (ch, name, EXTCOMMUNITY_LIST_MASTER);
+
+  /* When community-list already has entry, new entry should have same
+     style.  If you want to have mixed style community-list, you can
+     comment out this check.  */
+  if (!community_list_empty_p (list))
+    {
+      struct community_entry *first;
+
+      first = list->head;
+
+      if (style != first->style)
+       {
+         return (first->style == EXTCOMMUNITY_LIST_STANDARD
+                 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+                 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+       }
+    }
+
+  if (str)
+    {
+      if (style == EXTCOMMUNITY_LIST_STANDARD)
+       ecom = ecommunity_str2com (str, 0, 1);
+      else
+       regex = bgp_regcomp (str);
+
+      if (! ecom && ! regex)
+       return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+    }
+
+  if (ecom)
+    ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
+
+  entry = community_entry_new ();
+  entry->direct = direct;
+  entry->style = style;
+  entry->any = (str ? 0 : 1);
+  if (ecom)
+    entry->config = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
+  else if (regex)
+    entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
+  else
+    entry->config = NULL;
+  entry->u.ecom = ecom;
+  entry->reg = regex;
+
+  /* Do not put duplicated community entry.  */
+  if (community_list_dup_check (list, entry))
+    community_entry_free (entry);
+  else
+    community_list_entry_add (list, entry);
+
+  return 0;
+}
+
+/* Unset extcommunity-list.  When str is NULL, delete all of
+   extcommunity-list entry belongs to the specified name.  */
+int
+extcommunity_list_unset (struct community_list_handler *ch,
+                         const char *name, const char *str, 
+                         int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct ecommunity *ecom = NULL;
+  regex_t *regex = NULL;
+
+  /* Lookup extcommunity list.  */
+  list = community_list_lookup (ch, name, EXTCOMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  /* Delete all of entry belongs to this extcommunity-list.  */
+  if (!str)
+    {
+      community_list_delete (list);
+      return 0;
+    }
+
+  if (style == EXTCOMMUNITY_LIST_STANDARD)
+    ecom = ecommunity_str2com (str, 0, 1);
+  else
+    regex = bgp_regcomp (str);
+
+  if (! ecom && ! regex)
+    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+  if (ecom)
+    entry = community_list_entry_lookup (list, ecom, direct);
+  else
+    entry = community_list_entry_lookup (list, str, direct);
+
+  if (ecom)
+    ecommunity_free (&ecom);
+  if (regex)
+    bgp_regex_free (regex);
+
+  if (!entry)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  community_list_entry_delete (list, entry, style);
+
+  return 0;
+}
+
+/* Initializa community-list.  Return community-list handler.  */
+struct community_list_handler *
+community_list_init (void)
+{
+  struct community_list_handler *ch;
+  ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER,
+                sizeof (struct community_list_handler));
+  return ch;
+}
+
+/* Terminate community-list.  */
+void
+community_list_terminate (struct community_list_handler *ch)
+{
+  struct community_list_master *cm;
+  struct community_list *list;
+
+  cm = &ch->community_list;
+  while ((list = cm->num.head) != NULL)
+    community_list_delete (list);
+  while ((list = cm->str.head) != NULL)
+    community_list_delete (list);
+
+  cm = &ch->lcommunity_list;
+  while ((list = cm->num.head) != NULL)
+    community_list_delete (list);
+  while ((list = cm->str.head) != NULL)
+    community_list_delete (list);
+
+  cm = &ch->extcommunity_list;
+  while ((list = cm->num.head) != NULL)
+    community_list_delete (list);
+  while ((list = cm->str.head) != NULL)
+    community_list_delete (list);
+
+  XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
+}
diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h
new file mode 100644 (file)
index 0000000..d9db418
--- /dev/null
@@ -0,0 +1,172 @@
+/* BGP Community list.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_CLIST_H
+#define _QUAGGA_BGP_CLIST_H
+
+/* Master Community-list. */
+#define COMMUNITY_LIST_MASTER          0
+#define EXTCOMMUNITY_LIST_MASTER       1
+#define LARGE_COMMUNITY_LIST_MASTER    2
+
+/* Community-list deny and permit.  */
+#define COMMUNITY_DENY                 0
+#define COMMUNITY_PERMIT               1
+
+/* Number and string based community-list name.  */
+#define COMMUNITY_LIST_STRING          0
+#define COMMUNITY_LIST_NUMBER          1
+
+/* Community-list entry types.  */
+#define COMMUNITY_LIST_STANDARD        0 /* Standard community-list.  */
+#define COMMUNITY_LIST_EXPANDED        1 /* Expanded community-list.  */
+#define EXTCOMMUNITY_LIST_STANDARD     2 /* Standard extcommunity-list.  */
+#define EXTCOMMUNITY_LIST_EXPANDED     3 /* Expanded extcommunity-list.  */
+#define LARGE_COMMUNITY_LIST_STANDARD  4 /* Standard Large community-list.  */
+#define LARGE_COMMUNITY_LIST_EXPANDED  5 /* Expanded Large community-list.  */
+
+/* Community-list.  */
+struct community_list
+{
+  /* Name of the community-list.  */
+  char *name;
+
+  /* String or number.  */
+  int sort;
+
+  /* Link to upper list.  */
+  struct community_list_list *parent;
+
+  /* Linked list for other community-list.  */
+  struct community_list *next;
+  struct community_list *prev;
+
+  /* Community-list entry in this community-list.  */
+  struct community_entry *head;
+  struct community_entry *tail;
+};
+
+/* Each entry in community-list.  */
+struct community_entry
+{
+  struct community_entry *next;
+  struct community_entry *prev;
+
+  /* Permit or deny.  */
+  u_char direct;
+
+  /* Standard or expanded.  */
+  u_char style;
+
+  /* Any match.  */
+  u_char any;
+
+  /* Community structure.  */
+  union
+  {
+    struct community *com;
+    struct ecommunity *ecom;
+    struct lcommunity *lcom;
+  } u;
+
+  /* Configuration string.  */
+  char *config;
+
+  /* Expanded community-list regular expression.  */
+  regex_t *reg;
+};
+
+/* Linked list of community-list.  */
+struct community_list_list
+{
+  struct community_list *head;
+  struct community_list *tail;
+};
+
+/* Master structure of community-list and extcommunity-list.  */
+struct community_list_master
+{
+  struct community_list_list num;
+  struct community_list_list str;
+};
+
+/* Community-list handler.  community_list_init() returns this
+   structure as handler.  */
+struct community_list_handler
+{
+  /* Community-list.  */
+  struct community_list_master community_list;
+
+  /* Exteded community-list.  */
+  struct community_list_master extcommunity_list;
+
+  /* Large community-list.  */
+  struct community_list_master lcommunity_list;
+};
+
+/* Error code of community-list.  */
+#define COMMUNITY_LIST_ERR_CANT_FIND_LIST        -1
+#define COMMUNITY_LIST_ERR_MALFORMED_VAL         -2
+#define COMMUNITY_LIST_ERR_STANDARD_CONFLICT     -3
+#define COMMUNITY_LIST_ERR_EXPANDED_CONFLICT     -4
+
+/* Handler.  */
+extern struct community_list_handler *bgp_clist;
+
+/* Prototypes.  */
+extern struct community_list_handler *community_list_init (void);
+extern void community_list_terminate (struct community_list_handler *);
+
+extern int community_list_set (struct community_list_handler *ch,
+                              const char *name, const char *str, int direct,
+                              int style);
+extern int community_list_unset (struct community_list_handler *ch,
+                                const char *name, const char *str,
+                                int direct, int style);
+extern int extcommunity_list_set (struct community_list_handler *ch,
+                                 const char *name, const char *str,
+                                 int direct, int style);
+extern int extcommunity_list_unset (struct community_list_handler *ch,
+                                   const char *name, const char *str,
+                                   int direct, int style);
+extern int lcommunity_list_set (struct community_list_handler *ch,
+                               const char *name, const char *str,
+                               int direct, int style);
+extern int lcommunity_list_unset (struct community_list_handler *ch,
+                                 const char *name, const char *str,
+                                 int direct, int style);
+
+extern struct community_list_master *
+community_list_master_lookup (struct community_list_handler *, int);
+
+extern struct community_list *
+community_list_lookup (struct community_list_handler *, const char *, int);
+
+extern int community_list_match (struct community *, struct community_list *);
+extern int ecommunity_list_match (struct ecommunity *, struct community_list *);
+extern int lcommunity_list_match (struct lcommunity *, struct community_list *);
+extern int community_list_exact_match (struct community *,
+                                      struct community_list *);
+extern struct community *
+community_list_match_delete (struct community *, struct community_list *);
+extern struct lcommunity *
+lcommunity_list_match_delete (struct lcommunity *lcom,
+                             struct community_list *list);
+#endif /* _QUAGGA_BGP_CLIST_H */
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
new file mode 100644 (file)
index 0000000..f1997bd
--- /dev/null
@@ -0,0 +1,649 @@
+/* Community attribute related functions.
+   Copyright (C) 1998, 2001 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+
+#include "bgpd/bgp_community.h"
+
+/* Hash of community attribute. */
+static struct hash *comhash;
+
+/* Allocate a new communities value.  */
+static struct community *
+community_new (void)
+{
+  return (struct community *) XCALLOC (MTYPE_COMMUNITY,
+                                      sizeof (struct community));
+}
+
+/* Free communities value.  */
+void
+community_free (struct community *com)
+{
+  if (com->val)
+    XFREE (MTYPE_COMMUNITY_VAL, com->val);
+  if (com->str)
+    XFREE (MTYPE_COMMUNITY_STR, com->str);
+  XFREE (MTYPE_COMMUNITY, com);
+}
+
+/* Add one community value to the community. */
+static void
+community_add_val (struct community *com, u_int32_t val)
+{
+  com->size++;
+  if (com->val)
+    com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val, com_length (com));
+  else
+    com->val = XMALLOC (MTYPE_COMMUNITY_VAL, com_length (com));
+
+  val = htonl (val);
+  memcpy (com_lastval (com), &val, sizeof (u_int32_t));
+}
+
+/* Delete one community. */
+void
+community_del_val (struct community *com, u_int32_t *val)
+{
+  int i = 0;
+  int c = 0;
+
+  if (! com->val)
+    return;
+
+  while (i < com->size)
+    {
+      if (memcmp (com->val + i, val, sizeof (u_int32_t)) == 0)
+       {
+         c = com->size -i -1;
+
+         if (c > 0)
+           memmove (com->val + i, com->val + (i + 1), c * sizeof (*val));
+
+         com->size--;
+
+         if (com->size > 0)
+           com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val,
+                                com_length (com));
+         else
+           {
+             XFREE (MTYPE_COMMUNITY_VAL, com->val);
+             com->val = NULL;
+           }
+         return;
+       }
+      i++;
+    }
+}
+
+/* Delete all communities listed in com2 from com1 */
+struct community *
+community_delete (struct community *com1, struct community *com2)
+{
+  int i = 0;
+
+  while(i < com2->size)
+    {
+      community_del_val (com1, com2->val + i);
+      i++;
+    }
+
+  return com1;
+}
+
+/* Callback function from qsort(). */
+static int
+community_compare (const void *a1, const void *a2)
+{
+  u_int32_t v1;
+  u_int32_t v2;
+
+  memcpy (&v1, a1, sizeof (u_int32_t));
+  memcpy (&v2, a2, sizeof (u_int32_t));
+  v1 = ntohl (v1);
+  v2 = ntohl (v2);
+
+  if (v1 < v2)
+    return -1;
+  if (v1 > v2)
+    return 1;
+  return 0;
+}
+
+int
+community_include (struct community *com, u_int32_t val)
+{
+  int i;
+
+  val = htonl (val);
+
+  for (i = 0; i < com->size; i++)
+    if (memcmp (&val, com_nthval (com, i), sizeof (u_int32_t)) == 0)
+      return 1;
+
+  return 0;
+}
+
+u_int32_t
+community_val_get (struct community *com, int i)
+{
+  u_char *p;
+  u_int32_t val;
+
+  p = (u_char *) com->val;
+  p += (i * 4);
+
+  memcpy (&val, p, sizeof (u_int32_t));
+
+  return ntohl (val);
+}
+
+/* Sort and uniq given community. */
+struct community *
+community_uniq_sort (struct community *com)
+{
+  int i;
+  struct community *new;
+  u_int32_t val;
+
+  if (! com)
+    return NULL;
+  
+  new = community_new ();;
+  
+  for (i = 0; i < com->size; i++)
+    {
+      val = community_val_get (com, i);
+
+      if (! community_include (new, val))
+       community_add_val (new, val);
+    }
+
+  qsort (new->val, new->size, sizeof (u_int32_t), community_compare);
+
+  return new;
+}
+
+/* Convert communities attribute to string.
+
+   For Well-known communities value, below keyword is used.
+
+   0x0             "internet"    
+   0xFFFFFF01      "no-export"
+   0xFFFFFF02      "no-advertise"
+   0xFFFFFF03      "local-AS"
+
+   For other values, "AS:VAL" format is used.  */
+static char *
+community_com2str  (struct community *com)
+{
+  int i;
+  char *str;
+  char *pnt;
+  int len;
+  int first;
+  u_int32_t comval;
+  u_int16_t as;
+  u_int16_t val;
+
+  if (!com)
+    return NULL;
+  
+  /* When communities attribute is empty.  */
+  if (com->size == 0)
+    {
+      str = XMALLOC (MTYPE_COMMUNITY_STR, 1);
+      str[0] = '\0';
+      return str;
+    }
+
+  /* Memory allocation is time consuming work.  So we calculate
+     required string length first.  */
+  len = 0;
+
+  for (i = 0; i < com->size; i++)
+    {
+      memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
+      comval = ntohl (comval);
+
+      switch (comval) 
+       {
+       case COMMUNITY_INTERNET:
+         len += strlen (" internet");
+         break;
+       case COMMUNITY_NO_EXPORT:
+         len += strlen (" no-export");
+         break;
+       case COMMUNITY_NO_ADVERTISE:
+         len += strlen (" no-advertise");
+         break;
+       case COMMUNITY_LOCAL_AS:
+         len += strlen (" local-AS");
+         break;
+       default:
+         len += strlen (" 65536:65535");
+         break;
+       }
+    }
+
+  /* Allocate memory.  */
+  str = pnt = XMALLOC (MTYPE_COMMUNITY_STR, len);
+  first = 1;
+
+  /* Fill in string.  */
+  for (i = 0; i < com->size; i++)
+    {
+      memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
+      comval = ntohl (comval);
+
+      if (first)
+       first = 0;
+      else
+       *pnt++ = ' ';
+
+      switch (comval) 
+       {
+       case COMMUNITY_INTERNET:
+         strcpy (pnt, "internet");
+         pnt += strlen ("internet");
+         break;
+       case COMMUNITY_NO_EXPORT:
+         strcpy (pnt, "no-export");
+         pnt += strlen ("no-export");
+         break;
+       case COMMUNITY_NO_ADVERTISE:
+         strcpy (pnt, "no-advertise");
+         pnt += strlen ("no-advertise");
+         break;
+       case COMMUNITY_LOCAL_AS:
+         strcpy (pnt, "local-AS");
+         pnt += strlen ("local-AS");
+         break;
+       default:
+         as = (comval >> 16) & 0xFFFF;
+         val = comval & 0xFFFF;
+         sprintf (pnt, "%u:%d", as, val);
+         pnt += strlen (pnt);
+         break;
+       }
+    }
+  *pnt = '\0';
+
+  return str;
+}
+
+/* Intern communities attribute.  */
+struct community *
+community_intern (struct community *com)
+{
+  struct community *find;
+
+  /* Assert this community structure is not interned. */
+  assert (com->refcnt == 0);
+
+  /* Lookup community hash. */
+  find = (struct community *) hash_get (comhash, com, hash_alloc_intern);
+
+  /* Arguemnt com is allocated temporary.  So when it is not used in
+     hash, it should be freed.  */
+  if (find != com)
+    community_free (com);
+
+  /* Increment refrence counter.  */
+  find->refcnt++;
+
+  /* Make string.  */
+  if (! find->str)
+    find->str = community_com2str (find);
+
+  return find;
+}
+
+/* Free community attribute. */
+void
+community_unintern (struct community **com)
+{
+  struct community *ret;
+
+  if ((*com)->refcnt)
+    (*com)->refcnt--;
+
+  /* Pull off from hash.  */
+  if ((*com)->refcnt == 0)
+    {
+      /* Community value com must exist in hash. */
+      ret = (struct community *) hash_release (comhash, *com);
+      assert (ret != NULL);
+
+      community_free (*com);
+      *com = NULL;
+    }
+}
+
+/* Create new community attribute. */
+struct community *
+community_parse (u_int32_t *pnt, u_short length)
+{
+  struct community tmp;
+  struct community *new;
+
+  /* If length is malformed return NULL. */
+  if (length % 4)
+    return NULL;
+
+  /* Make temporary community for hash look up. */
+  tmp.size = length / 4;
+  tmp.val = pnt;
+
+  new = community_uniq_sort (&tmp);
+
+  return community_intern (new);
+}
+
+struct community *
+community_dup (struct community *com)
+{
+  struct community *new;
+
+  new = XCALLOC (MTYPE_COMMUNITY, sizeof (struct community));
+  new->size = com->size;
+  if (new->size)
+    {
+      new->val = XMALLOC (MTYPE_COMMUNITY_VAL, com->size * 4);
+      memcpy (new->val, com->val, com->size * 4);
+    }
+  else
+    new->val = NULL;
+  return new;
+}
+
+/* Retrun string representation of communities attribute. */
+char *
+community_str (struct community *com)
+{
+  if (!com)
+    return NULL;
+  
+  if (! com->str)
+    com->str = community_com2str (com);
+  return com->str;
+}
+
+/* Make hash value of community attribute. This function is used by
+   hash package.*/
+unsigned int
+community_hash_make (struct community *com)
+{
+  unsigned char *pnt = (unsigned char *)com->val;
+  int size = com->size * 4;
+  unsigned int key = 0;
+  int c;
+
+  for (c = 0; c < size; c += 4)
+    {
+      key += pnt[c];
+      key += pnt[c + 1];
+      key += pnt[c + 2];
+      key += pnt[c + 3];
+    }
+
+  return key;
+}
+
+int
+community_match (const struct community *com1, const struct community *com2)
+{
+  int i = 0;
+  int j = 0;
+
+  if (com1 == NULL && com2 == NULL)
+    return 1;
+
+  if (com1 == NULL || com2 == NULL)
+    return 0;
+
+  if (com1->size < com2->size)
+    return 0;
+
+  /* Every community on com2 needs to be on com1 for this to match */
+  while (i < com1->size && j < com2->size)
+    {
+      if (memcmp (com1->val + i, com2->val + j, sizeof (u_int32_t)) == 0)
+       j++;
+      i++;
+    }
+
+  if (j == com2->size)
+    return 1;
+  else
+    return 0;
+}
+
+/* If two aspath have same value then return 1 else return 0. This
+   function is used by hash package. */
+int
+community_cmp (const struct community *com1, const struct community *com2)
+{
+  if (com1 == NULL && com2 == NULL)
+    return 1;
+  if (com1 == NULL || com2 == NULL)
+    return 0;
+
+  if (com1->size == com2->size)
+    if (memcmp (com1->val, com2->val, com1->size * 4) == 0)
+      return 1;
+  return 0;
+}
+
+/* Add com2 to the end of com1. */
+struct community *
+community_merge (struct community *com1, struct community *com2)
+{
+  if (com1->val)
+    com1->val = XREALLOC (MTYPE_COMMUNITY_VAL, com1->val, 
+                         (com1->size + com2->size) * 4);
+  else
+    com1->val = XMALLOC (MTYPE_COMMUNITY_VAL, (com1->size + com2->size) * 4);
+
+  memcpy (com1->val + com1->size, com2->val, com2->size * 4);
+  com1->size += com2->size;
+
+  return com1;
+}
+
+/* Community token enum. */
+enum community_token
+{
+  community_token_val,
+  community_token_no_export,
+  community_token_no_advertise,
+  community_token_local_as,
+  community_token_unknown
+};
+
+/* Get next community token from string. */
+static const char *
+community_gettoken (const char *buf, enum community_token *token, 
+                    u_int32_t *val)
+{
+  const char *p = buf;
+
+  /* Skip white space. */
+  while (isspace ((int) *p))
+    p++;
+
+  /* Check the end of the line. */
+  if (*p == '\0')
+    return NULL;
+
+  /* Well known community string check. */
+  if (isalpha ((int) *p)) 
+    {
+      if (strncmp (p, "internet", strlen ("internet")) == 0)
+       {
+         *val = COMMUNITY_INTERNET;
+         *token = community_token_no_export;
+         p += strlen ("internet");
+         return p;
+       }
+      if (strncmp (p, "no-export", strlen ("no-export")) == 0)
+       {
+         *val = COMMUNITY_NO_EXPORT;
+         *token = community_token_no_export;
+         p += strlen ("no-export");
+         return p;
+       }
+      if (strncmp (p, "no-advertise", strlen ("no-advertise")) == 0)
+       {
+         *val = COMMUNITY_NO_ADVERTISE;
+         *token = community_token_no_advertise;
+         p += strlen ("no-advertise");
+         return p;
+       }
+      if (strncmp (p, "local-AS", strlen ("local-AS")) == 0)
+       {
+         *val = COMMUNITY_LOCAL_AS;
+         *token = community_token_local_as;
+         p += strlen ("local-AS");
+         return p;
+       }
+
+      /* Unknown string. */
+      *token = community_token_unknown;
+      return NULL;
+    }
+
+  /* Community value. */
+  if (isdigit ((int) *p)) 
+    {
+      int separator = 0;
+      int digit = 0;
+      u_int32_t community_low = 0;
+      u_int32_t community_high = 0;
+
+      while (isdigit ((int) *p) || *p == ':') 
+       {
+         if (*p == ':') 
+           {
+             if (separator)
+               {
+                 *token = community_token_unknown;
+                 return NULL;
+               }
+             else
+               {
+                 separator = 1;
+                 digit = 0;
+                 community_high = community_low << 16;
+                 community_low = 0;
+               }
+           }
+         else 
+           {
+             digit = 1;
+             community_low *= 10;
+             community_low += (*p - '0');
+           }
+         p++;
+       }
+      if (! digit)
+       {
+         *token = community_token_unknown;
+         return NULL;
+       }
+      *val = community_high + community_low;
+      *token = community_token_val;
+      return p;
+    }
+  *token = community_token_unknown;
+  return NULL;
+}
+
+/* convert string to community structure */
+struct community *
+community_str2com (const char *str)
+{
+  struct community *com = NULL;
+  struct community *com_sort = NULL;
+  u_int32_t val = 0;
+  enum community_token token = community_token_unknown;
+
+  do 
+    {
+      str = community_gettoken (str, &token, &val);
+      
+      switch (token)
+       {
+       case community_token_val:
+       case community_token_no_export:
+       case community_token_no_advertise:
+       case community_token_local_as:
+         if (com == NULL)
+           com = community_new();
+         community_add_val (com, val);
+         break;
+       case community_token_unknown:
+       default:
+         if (com)
+           community_free (com);
+         return NULL;
+       }
+    } while (str);
+  
+  if (! com)
+    return NULL;
+
+  com_sort = community_uniq_sort (com);
+  community_free (com);
+
+  return com_sort;
+}
+
+/* Return communities hash entry count.  */
+unsigned long
+community_count (void)
+{
+  return comhash->count;
+}
+
+/* Return communities hash.  */
+struct hash *
+community_hash (void)
+{
+  return comhash;
+}
+
+/* Initialize comminity related hash. */
+void
+community_init (void)
+{
+  comhash = hash_create ((unsigned int (*) (void *))community_hash_make,
+                        (int (*) (const void *, const void *))community_cmp);
+}
+
+void
+community_finish (void)
+{
+  hash_free (comhash);
+  comhash = NULL;
+}
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
new file mode 100644 (file)
index 0000000..c73dab3
--- /dev/null
@@ -0,0 +1,75 @@
+/* Community attribute related functions.
+   Copyright (C) 1998 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_COMMUNITY_H
+#define _QUAGGA_BGP_COMMUNITY_H
+
+/* Communities attribute.  */
+struct community 
+{
+  /* Reference count of communities value.  */
+  unsigned long refcnt;
+
+  /* Communities value size.  */
+  int size;
+
+  /* Communities value.  */
+  u_int32_t *val;
+
+  /* String of community attribute.  This sring is used by vty output
+     and expanded community-list for regular expression match.  */
+  char *str;
+};
+
+/* Well-known communities value.  */
+#define COMMUNITY_INTERNET              0x0
+#define COMMUNITY_NO_EXPORT             0xFFFFFF01
+#define COMMUNITY_NO_ADVERTISE          0xFFFFFF02
+#define COMMUNITY_NO_EXPORT_SUBCONFED   0xFFFFFF03
+#define COMMUNITY_LOCAL_AS              0xFFFFFF03
+
+/* Macros of community attribute.  */
+#define com_length(X)    ((X)->size * 4)
+#define com_lastval(X)   ((X)->val + (X)->size - 1)
+#define com_nthval(X,n)  ((X)->val + (n))
+
+/* Prototypes of communities attribute functions.  */
+extern void community_init (void);
+extern void community_finish (void);
+extern void community_free (struct community *);
+extern struct community *community_uniq_sort (struct community *);
+extern struct community *community_parse (u_int32_t *, u_short);
+extern struct community *community_intern (struct community *);
+extern void community_unintern (struct community **);
+extern char *community_str (struct community *);
+extern unsigned int community_hash_make (struct community *);
+extern struct community *community_str2com (const char *);
+extern int community_match (const struct community *, const struct community *);
+extern int community_cmp (const struct community *, const struct community *);
+extern struct community *community_merge (struct community *, struct community *);
+extern struct community *community_delete (struct community *, struct community *);
+extern struct community *community_dup (struct community *);
+extern int community_include (struct community *, u_int32_t);
+extern void community_del_val (struct community *, u_int32_t *);
+extern unsigned long community_count (void);
+extern struct hash *community_hash (void);
+extern u_int32_t community_val_get (struct community *com, int i);
+
+#endif /* _QUAGGA_BGP_COMMUNITY_H */
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
new file mode 100644 (file)
index 0000000..ac64723
--- /dev/null
@@ -0,0 +1,675 @@
+/* BGP flap dampening
+   Copyright (C) 2001 IP Infusion Inc.
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "command.h"
+#include "log.h"
+#include "thread.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h" 
+#include "bgpd/bgp_advertise.h"
+
+/* Global variable to access damping configuration */
+struct bgp_damp_config bgp_damp_cfg;
+static struct bgp_damp_config *damp = &bgp_damp_cfg;
+
+/* Utility macro to add and delete BGP dampening information to no
+   used list.  */
+#define BGP_DAMP_LIST_ADD(N,A)  BGP_INFO_ADD(N,A,no_reuse_list)
+#define BGP_DAMP_LIST_DEL(N,A)  BGP_INFO_DEL(N,A,no_reuse_list)
+
+/* Calculate reuse list index by penalty value.  */
+static int
+bgp_reuse_index (int penalty)
+{
+  unsigned int i;
+  int index;
+
+  i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
+  
+  if ( i >= damp->reuse_index_size )
+    i = damp->reuse_index_size - 1;
+
+  index = damp->reuse_index[i] - damp->reuse_index[0];
+
+  return (damp->reuse_offset + index) % damp->reuse_list_size;  
+}
+
+/* Add BGP dampening information to reuse list.  */
+static void 
+bgp_reuse_list_add (struct bgp_damp_info *bdi)
+{
+  int index;
+
+  index = bdi->index = bgp_reuse_index (bdi->penalty);
+
+  bdi->prev = NULL;
+  bdi->next = damp->reuse_list[index];
+  if (damp->reuse_list[index])
+    damp->reuse_list[index]->prev = bdi;
+  damp->reuse_list[index] = bdi;
+}
+
+/* Delete BGP dampening information from reuse list.  */
+static void
+bgp_reuse_list_delete (struct bgp_damp_info *bdi)
+{
+  if (bdi->next)
+    bdi->next->prev = bdi->prev;
+  if (bdi->prev)
+    bdi->prev->next = bdi->next;
+  else
+    damp->reuse_list[bdi->index] = bdi->next;
+}   
+
+/* Return decayed penalty value.  */
+int 
+bgp_damp_decay (time_t tdiff, int penalty)
+{
+  unsigned int i;
+
+  i = (int) ((double) tdiff / DELTA_T);
+
+  if (i == 0)
+    return penalty; 
+  
+  if (i >= damp->decay_array_size)
+    return 0;
+
+  return (int) (penalty * damp->decay_array[i]);
+}
+
+/* Handler of reuse timer event.  Each route in the current reuse-list
+   is evaluated.  RFC2439 Section 4.8.7.  */
+static int
+bgp_reuse_timer (struct thread *t)
+{
+  struct bgp_damp_info *bdi;
+  struct bgp_damp_info *next;
+  time_t t_now, t_diff;
+    
+  damp->t_reuse = NULL;
+  damp->t_reuse =
+    thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE);
+
+  t_now = bgp_clock ();
+
+  /* 1.  save a pointer to the current zeroth queue head and zero the
+     list head entry.  */
+  bdi = damp->reuse_list[damp->reuse_offset];
+  damp->reuse_list[damp->reuse_offset] = NULL;
+
+  /* 2.  set offset = modulo reuse-list-size ( offset + 1 ), thereby
+     rotating the circular queue of list-heads.  */
+  damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size;
+
+  /* 3. if ( the saved list head pointer is non-empty ) */
+  for (; bdi; bdi = next)
+    {
+      struct bgp *bgp = bdi->binfo->peer->bgp;
+      
+      next = bdi->next;
+
+      /* Set t-diff = t-now - t-updated.  */
+      t_diff = t_now - bdi->t_updated;
+
+      /* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
+      bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);   
+
+      /* Set t-updated = t-now.  */
+      bdi->t_updated = t_now;
+
+      /* if (figure-of-merit < reuse).  */
+      if (bdi->penalty < damp->reuse_limit)
+       {
+         /* Reuse the route.  */
+         bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_DAMPED);
+         bdi->suppress_time = 0;
+
+         if (bdi->lastrecord == BGP_RECORD_UPDATE)
+           {
+             bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_HISTORY);
+             bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo,
+                                      bdi->afi, bdi->safi);   
+             bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi);
+           }
+
+         if (bdi->penalty <= damp->reuse_limit / 2.0)
+           bgp_damp_info_free (bdi, 1);
+         else
+           BGP_DAMP_LIST_ADD (damp, bdi);
+       }
+      else
+       /* Re-insert into another list (See RFC2439 Section 4.8.6).  */
+       bgp_reuse_list_add (bdi);
+    }
+
+  return 0;
+}
+
+/* A route becomes unreachable (RFC2439 Section 4.8.2).  */
+int
+bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
+                  afi_t afi, safi_t safi, int attr_change)
+{
+  time_t t_now;
+  struct bgp_damp_info *bdi = NULL;
+  double last_penalty = 0;
+  
+  t_now = bgp_clock ();
+
+  /* Processing Unreachable Messages.  */
+  if (binfo->extra)
+    bdi = binfo->extra->damp_info;
+  
+  if (bdi == NULL)
+    {
+      /* If there is no previous stability history. */
+
+      /* RFC2439 said:
+        1. allocate a damping structure.
+         2. set figure-of-merit = 1.
+         3. withdraw the route.  */
+
+      bdi =  XCALLOC (MTYPE_BGP_DAMP_INFO, sizeof (struct bgp_damp_info));
+      bdi->binfo = binfo;
+      bdi->rn = rn;
+      bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
+      bdi->flap = 1;
+      bdi->start_time = t_now;
+      bdi->suppress_time = 0;
+      bdi->index = -1;
+      bdi->afi = afi;
+      bdi->safi = safi;
+      (bgp_info_extra_get (binfo))->damp_info = bdi;
+      BGP_DAMP_LIST_ADD (damp, bdi);
+    }
+  else
+    {
+      last_penalty = bdi->penalty;
+
+      /* 1. Set t-diff = t-now - t-updated.  */
+      bdi->penalty = 
+       (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty) 
+        + (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
+
+      if (bdi->penalty > damp->ceiling)
+       bdi->penalty = damp->ceiling;
+
+      bdi->flap++;
+    }
+  
+  assert ((rn == bdi->rn) && (binfo == bdi->binfo));
+  
+  bdi->lastrecord = BGP_RECORD_WITHDRAW;
+  bdi->t_updated = t_now;
+
+  /* Make this route as historical status.  */
+  bgp_info_set_flag (rn, binfo, BGP_INFO_HISTORY);
+
+  /* Remove the route from a reuse list if it is on one.  */
+  if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED))
+    {
+      /* If decay rate isn't equal to 0, reinsert brn. */  
+      if (bdi->penalty != last_penalty)
+       {
+         bgp_reuse_list_delete (bdi);
+         bgp_reuse_list_add (bdi);  
+       }
+      return BGP_DAMP_SUPPRESSED; 
+    }
+
+  /* If not suppressed before, do annonunce this withdraw and
+     insert into reuse_list.  */
+  if (bdi->penalty >= damp->suppress_value)
+    {
+      bgp_info_set_flag (rn, binfo, BGP_INFO_DAMPED);
+      bdi->suppress_time = t_now;
+      BGP_DAMP_LIST_DEL (damp, bdi);
+      bgp_reuse_list_add (bdi);
+    }
+
+  return BGP_DAMP_USED;
+}
+
+int
+bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn, 
+                afi_t afi, safi_t safi)
+{
+  time_t t_now;
+  struct bgp_damp_info *bdi;
+  int status;
+
+  if (!binfo->extra || !((bdi = binfo->extra->damp_info)))
+    return BGP_DAMP_USED;
+
+  t_now = bgp_clock ();
+  bgp_info_unset_flag (rn, binfo, BGP_INFO_HISTORY);
+
+  bdi->lastrecord = BGP_RECORD_UPDATE;
+  bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty);
+
+  if (! CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
+      && (bdi->penalty < damp->suppress_value))
+    status = BGP_DAMP_USED;
+  else if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
+          && (bdi->penalty < damp->reuse_limit) )
+    {
+      bgp_info_unset_flag (rn, binfo, BGP_INFO_DAMPED);
+      bgp_reuse_list_delete (bdi);
+      BGP_DAMP_LIST_ADD (damp, bdi);
+      bdi->suppress_time = 0;
+      status = BGP_DAMP_USED;
+    }
+  else
+    status = BGP_DAMP_SUPPRESSED;  
+
+  if (bdi->penalty > damp->reuse_limit / 2.0)
+    bdi->t_updated = t_now;
+  else
+    bgp_damp_info_free (bdi, 0);
+       
+  return status;
+}
+
+/* Remove dampening information and history route.  */
+int 
+bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
+{
+  time_t t_now, t_diff;
+  struct bgp_damp_info *bdi;
+  
+  assert (binfo->extra && binfo->extra->damp_info);
+  
+  t_now = bgp_clock ();
+  bdi = binfo->extra->damp_info;
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+    {
+      t_diff = t_now - bdi->suppress_time;
+
+      if (t_diff >= damp->max_suppress_time)
+        {
+          bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_DAMPED);
+          bgp_reuse_list_delete (bdi);
+         BGP_DAMP_LIST_ADD (damp, bdi);
+          bdi->penalty = damp->reuse_limit;
+          bdi->suppress_time = 0;
+          bdi->t_updated = t_now;
+          
+          /* Need to announce UPDATE once this binfo is usable again. */
+          if (bdi->lastrecord == BGP_RECORD_UPDATE)
+            return 1;
+          else
+            return 0;
+        }
+    }
+  else
+    {
+      t_diff = t_now - bdi->t_updated;
+      bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+      if (bdi->penalty <= damp->reuse_limit / 2.0)
+        {
+          /* release the bdi, bdi->binfo. */  
+          bgp_damp_info_free (bdi, 1);
+          return 0;
+        }            
+      else
+        bdi->t_updated = t_now;
+    }       
+  return 0;
+}
+
+void
+bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
+{
+  struct bgp_info *binfo;
+
+  if (! bdi)
+    return;
+
+  binfo = bdi->binfo;
+  binfo->extra->damp_info = NULL;
+
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+    bgp_reuse_list_delete (bdi);
+  else
+    BGP_DAMP_LIST_DEL (damp, bdi);
+
+  bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_HISTORY|BGP_INFO_DAMPED);
+
+  if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+    bgp_info_delete (bdi->rn, binfo);
+  
+  XFREE (MTYPE_BGP_DAMP_INFO, bdi);
+}
+
+static void
+bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
+{
+  double reuse_max_ratio;
+  unsigned int i;
+  double j;
+       
+  damp->suppress_value = sup;
+  damp->half_life = hlife;
+  damp->reuse_limit = reuse;
+  damp->max_suppress_time = maxsup;
+
+  /* Initialize params per bgp_damp_config. */
+  damp->reuse_index_size = REUSE_ARRAY_SIZE;
+
+  damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life))); 
+
+  /* Decay-array computations */
+  damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T);
+  damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
+                              sizeof(double) * (damp->decay_array_size));
+  damp->decay_array[0] = 1.0;
+  damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5));
+
+  /* Calculate decay values for all possible times */
+  for (i = 2; i < damp->decay_array_size; i++)
+    damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1];
+       
+  /* Reuse-list computations */
+  i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
+  if (i > REUSE_LIST_SIZE || i == 0)
+    i = REUSE_LIST_SIZE;
+  damp->reuse_list_size = i; 
+
+  damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY, 
+                             damp->reuse_list_size 
+                             * sizeof (struct bgp_reuse_node *));
+
+  /* Reuse-array computations */
+  damp->reuse_index = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
+                              sizeof(int) * damp->reuse_index_size);
+
+  reuse_max_ratio = (double)damp->ceiling/damp->reuse_limit;
+  j = (exp((double)damp->max_suppress_time/damp->half_life) * log10(2.0));
+  if ( reuse_max_ratio > j && j != 0 )
+    reuse_max_ratio = j;
+
+  damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1);
+
+  for (i = 0; i < damp->reuse_index_size; i++)
+    {
+      damp->reuse_index[i] = 
+       (int)(((double)damp->half_life / DELTA_REUSE)
+             * log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5));
+    }
+}
+
+int
+bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
+                unsigned int reuse, unsigned int suppress, time_t max)
+{
+  if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+    {
+      if (damp->half_life == half
+         && damp->reuse_limit == reuse
+         && damp->suppress_value == suppress
+         && damp->max_suppress_time == max)
+       return 0;
+      bgp_damp_disable (bgp, afi, safi);
+    }
+
+  SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+  bgp_damp_parameter_set (half, reuse, suppress, max);
+
+  /* Register reuse timer.  */
+  if (! damp->t_reuse)
+    damp->t_reuse = 
+      thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE);
+
+  return 0;
+}
+
+static void
+bgp_damp_config_clean (struct bgp_damp_config *damp)
+{
+  /* Free decay array */
+  XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array);
+
+  /* Free reuse index array */
+  XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index);
+
+  /* Free reuse list array. */
+  XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list);
+}
+
+/* Clean all the bgp_damp_info stored in reuse_list. */
+void
+bgp_damp_info_clean (void)
+{
+  unsigned int i;
+  struct bgp_damp_info *bdi, *next;
+
+  damp->reuse_offset = 0;
+
+  for (i = 0; i < damp->reuse_list_size; i++)
+    {
+      if (! damp->reuse_list[i])
+       continue;
+
+      for (bdi = damp->reuse_list[i]; bdi; bdi = next)
+       {
+         next = bdi->next;
+         bgp_damp_info_free (bdi, 1);
+       }
+      damp->reuse_list[i] = NULL;
+    }
+
+  for (bdi = damp->no_reuse_list; bdi; bdi = next)
+    {
+      next = bdi->next;
+      bgp_damp_info_free (bdi, 1);
+    }
+  damp->no_reuse_list = NULL;
+}
+
+int
+bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi)
+{
+  /* If it wasn't enabled, there's nothing to do. */
+  if (! CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+    return 0;
+
+  /* Cancel reuse thread. */
+  if (damp->t_reuse )
+    thread_cancel (damp->t_reuse);
+  damp->t_reuse = NULL;
+
+  /* Clean BGP dampening information.  */
+  bgp_damp_info_clean ();
+
+  /* Clear configuration */
+  bgp_damp_config_clean (&bgp_damp_cfg);
+
+  UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+  return 0;
+}
+
+void
+bgp_config_write_damp (struct vty *vty)
+{
+  if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE*60
+      && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
+      && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
+      && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
+    vty_out (vty, " bgp dampening%s", VTY_NEWLINE);
+  else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE*60
+          && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
+          && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
+          && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
+    vty_out (vty, " bgp dampening %lld%s",
+            bgp_damp_cfg.half_life/60LL,
+            VTY_NEWLINE);
+  else
+    vty_out (vty, " bgp dampening %lld %d %d %lld%s",
+            bgp_damp_cfg.half_life/60LL,
+            bgp_damp_cfg.reuse_limit,
+            bgp_damp_cfg.suppress_value,
+            bgp_damp_cfg.max_suppress_time/60LL,
+            VTY_NEWLINE);
+}
+
+static const char *
+bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len)
+{
+  time_t reuse_time = 0;
+  struct tm *tm = NULL;
+
+  if (penalty > damp->reuse_limit)
+    {
+      reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1])))); 
+
+      if (reuse_time > damp->max_suppress_time)
+       reuse_time = damp->max_suppress_time;
+
+      tm = gmtime (&reuse_time);
+    }
+  else 
+    reuse_time = 0;
+
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+  if (reuse_time == 0)
+    snprintf (buf, len, "00:00:00");
+  else if (reuse_time < ONE_DAY_SECOND)
+    snprintf (buf, len, "%02d:%02d:%02d", 
+              tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (reuse_time < ONE_WEEK_SECOND)
+    snprintf (buf, len, "%dd%02dh%02dm", 
+              tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    snprintf (buf, len, "%02dw%dd%02dh", 
+              tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); 
+
+  return buf;
+}
+void
+bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)  
+{
+  struct bgp_damp_info *bdi;
+  time_t t_now, t_diff;
+  char timebuf[BGP_UPTIME_LEN];
+  int penalty;
+
+  if (!binfo->extra)
+    return;
+  
+  /* BGP dampening information.  */
+  bdi = binfo->extra->damp_info;
+
+  /* If dampening is not enabled or there is no dampening information,
+     return immediately.  */
+  if (! damp || ! bdi)
+    return;
+
+  /* Calculate new penalty.  */
+  t_now = bgp_clock ();
+  t_diff = t_now - bdi->t_updated;
+  penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+  vty_out (vty, "      Dampinfo: penalty %d, flapped %d times in %s",
+           penalty, bdi->flap,
+          peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN));
+
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)
+      && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+    vty_out (vty, ", reuse in %s",
+            bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN));
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+const char *
+bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo,
+                         char *timebuf, size_t len)
+{
+  struct bgp_damp_info *bdi;
+  time_t t_now, t_diff;
+  int penalty;
+  
+  if (!binfo->extra)
+    return NULL;
+  
+  /* BGP dampening information.  */
+  bdi = binfo->extra->damp_info;
+
+  /* If dampening is not enabled or there is no dampening information,
+     return immediately.  */
+  if (! damp || ! bdi)
+    return NULL;
+
+  /* Calculate new penalty.  */
+  t_now = bgp_clock ();
+  t_diff = t_now - bdi->t_updated;
+  penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+  return  bgp_get_reuse_time (penalty, timebuf, len);
+}
+
+int
+bgp_show_dampening_parameters (struct vty *vty, afi_t afi, safi_t safi)
+{
+  struct bgp *bgp;
+  bgp = bgp_get_default();
+
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+    {
+      vty_out (vty, "Half-life time: %ld min%s",
+                    damp->half_life / 60, VTY_NEWLINE);
+      vty_out (vty, "Reuse penalty: %d%s",
+                    damp->reuse_limit, VTY_NEWLINE);
+      vty_out (vty, "Suppress penalty: %d%s",
+                    damp->suppress_value, VTY_NEWLINE);
+      vty_out (vty, "Max suppress time: %ld min%s",
+                    damp->max_suppress_time / 60, VTY_NEWLINE);
+      vty_out (vty, "Max supress penalty: %u%s",
+                    damp->ceiling, VTY_NEWLINE);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "dampening not enabled for %s%s",
+                  afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h
new file mode 100644 (file)
index 0000000..16fd367
--- /dev/null
@@ -0,0 +1,149 @@
+/* BGP flap dampening
+   Copyright (C) 2001 IP Infusion Inc.
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_DAMP_H
+#define _QUAGGA_BGP_DAMP_H
+
+/* Structure maintained on a per-route basis. */
+struct bgp_damp_info
+{
+  /* Doubly linked list.  This information must be linked to
+     reuse_list or no_reuse_list.  */
+  struct bgp_damp_info *next;
+  struct bgp_damp_info *prev;
+
+  /* Figure-of-merit.  */
+  unsigned int penalty;
+
+  /* Number of flapping.  */
+  unsigned int flap;
+       
+  /* First flap time  */
+  time_t start_time;
+  /* Last time penalty was updated.  */
+  time_t t_updated;
+
+  /* Time of route start to be suppressed.  */
+  time_t suppress_time;
+
+  /* Back reference to bgp_info. */
+  struct bgp_info *binfo;
+
+  /* Back reference to bgp_node. */
+  struct bgp_node *rn;
+
+  /* Current index in the reuse_list. */
+  int index;
+
+  /* Last time message type. */
+  u_char lastrecord;
+#define BGP_RECORD_UPDATE      1U
+#define BGP_RECORD_WITHDRAW    2U
+
+  afi_t afi;
+  safi_t safi;
+};
+
+/* Specified parameter set configuration. */
+struct bgp_damp_config
+{
+  /* Value over which routes suppressed.  */
+  unsigned int suppress_value;
+
+  /* Value below which suppressed routes reused.  */
+  unsigned int reuse_limit;
+
+  /* Max time a route can be suppressed.  */
+  time_t max_suppress_time;      
+
+  /* Time during which accumulated penalty reduces by half.  */
+  time_t half_life;
+
+  /* Non-configurable parameters but fixed at implementation time.
+   * To change this values, init_bgp_damp() should be modified.
+   */
+  time_t tmax;                  /* Max time previous instability retained */
+  unsigned int reuse_list_size;         /* Number of reuse lists */
+  unsigned int reuse_index_size; /* Size of reuse index array */
+
+  /* Non-configurable parameters.  Most of these are calculated from
+   * the configurable parameters above.
+   */
+  unsigned int ceiling;                        /* Max value a penalty can attain */
+  unsigned int decay_rate_per_tick;    /* Calculated from half-life */
+  unsigned int decay_array_size; /* Calculated using config parameters */
+  double scale_factor;
+  unsigned int reuse_scale_factor; 
+         
+  /* Decay array per-set based. */ 
+  double *decay_array; 
+
+  /* Reuse index array per-set based. */ 
+  int *reuse_index;
+
+  /* Reuse list array per-set based. */  
+  struct bgp_damp_info **reuse_list;
+  int reuse_offset;
+        
+  /* All dampening information which is not on reuse list.  */
+  struct bgp_damp_info *no_reuse_list;
+
+  /* Reuse timer thread per-set base. */
+  struct thread* t_reuse;
+};
+
+#define BGP_DAMP_NONE           0
+#define BGP_DAMP_USED          1
+#define BGP_DAMP_SUPPRESSED    2
+
+/* Time granularity for reuse lists */
+#define DELTA_REUSE              10
+
+/* Time granularity for decay arrays */
+#define DELTA_T                   5
+
+#define DEFAULT_PENALTY         1000
+
+#define DEFAULT_HALF_LIFE         15
+#define DEFAULT_REUSE           750
+#define DEFAULT_SUPPRESS       2000
+
+#define REUSE_LIST_SIZE          256
+#define REUSE_ARRAY_SIZE        1024
+
+extern int bgp_damp_enable (struct bgp *, afi_t, safi_t, time_t, unsigned int, 
+                     unsigned int, time_t);
+extern int bgp_damp_disable (struct bgp *, afi_t, safi_t);
+extern int bgp_damp_withdraw (struct bgp_info *, struct bgp_node *,
+                      afi_t, safi_t, int);
+extern int bgp_damp_update (struct bgp_info *, struct bgp_node *, afi_t, safi_t);
+extern int bgp_damp_scan (struct bgp_info *, afi_t, safi_t);
+extern void bgp_damp_info_free (struct bgp_damp_info *, int);
+extern void bgp_damp_info_clean (void);
+extern int bgp_damp_decay (time_t, int);
+extern void bgp_config_write_damp (struct vty *);
+extern void bgp_damp_info_vty (struct vty *, struct bgp_info *);
+extern const char * bgp_damp_reuse_time_vty (struct vty *, struct bgp_info *,
+                                             char *, size_t);
+
+extern int bgp_show_dampening_parameters (struct vty *vty, afi_t, safi_t);
+
+#endif /* _QUAGGA_BGP_DAMP_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
new file mode 100644 (file)
index 0000000..ba79722
--- /dev/null
@@ -0,0 +1,1046 @@
+/* BGP-4, BGP-4+ packet debug routine
+   Copyright (C) 1996, 97, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "prefix.h"
+#include "linklist.h"
+#include "stream.h"
+#include "command.h"
+#include "str.h"
+#include "log.h"
+#include "sockunion.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_community.h"
+
+unsigned long conf_bgp_debug_as4;
+unsigned long conf_bgp_debug_fsm;
+unsigned long conf_bgp_debug_events;
+unsigned long conf_bgp_debug_packet;
+unsigned long conf_bgp_debug_filter;
+unsigned long conf_bgp_debug_keepalive;
+unsigned long conf_bgp_debug_update;
+unsigned long conf_bgp_debug_normal;
+unsigned long conf_bgp_debug_zebra;
+unsigned long conf_bgp_debug_allow_martians;
+unsigned long conf_bgp_debug_nht;
+
+unsigned long term_bgp_debug_as4;
+unsigned long term_bgp_debug_fsm;
+unsigned long term_bgp_debug_events;
+unsigned long term_bgp_debug_packet;
+unsigned long term_bgp_debug_filter;
+unsigned long term_bgp_debug_keepalive;
+unsigned long term_bgp_debug_update;
+unsigned long term_bgp_debug_normal;
+unsigned long term_bgp_debug_zebra;
+unsigned long term_bgp_debug_allow_martians;
+unsigned long term_bgp_debug_nht;
+
+/* messages for BGP-4 status */
+const struct message bgp_status_msg[] = 
+{
+  { Idle, "Idle" },
+  { Connect, "Connect" },
+  { Active, "Active" },
+  { OpenSent, "OpenSent" },
+  { OpenConfirm, "OpenConfirm" },
+  { Established, "Established" },
+  { Clearing,    "Clearing"    },
+  { Deleted,     "Deleted"     },
+};
+const int bgp_status_msg_max = BGP_STATUS_MAX;
+
+/* BGP message type string. */
+const char *bgp_type_str[] =
+{
+  NULL,
+  "OPEN",
+  "UPDATE",
+  "NOTIFICATION",
+  "KEEPALIVE",
+  "ROUTE-REFRESH",
+  "CAPABILITY"
+};
+
+/* message for BGP-4 Notify */
+static const struct message bgp_notify_msg[] = 
+{
+  { BGP_NOTIFY_HEADER_ERR, "Message Header Error"},
+  { BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"},
+  { BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error"},
+  { BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"},
+  { BGP_NOTIFY_FSM_ERR, "Finite State Machine Error"},
+  { BGP_NOTIFY_CEASE, "Cease"},
+  { BGP_NOTIFY_CAPABILITY_ERR, "CAPABILITY Message Error"},
+};
+static const int bgp_notify_msg_max = BGP_NOTIFY_MAX;
+
+static const struct message bgp_notify_head_msg[] = 
+{
+  { BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized"},
+  { BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length"},
+  { BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type"}
+};
+static const int bgp_notify_head_msg_max = BGP_NOTIFY_HEADER_MAX;
+
+static const struct message bgp_notify_open_msg[] = 
+{
+  { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+  { BGP_NOTIFY_OPEN_UNSUP_VERSION, "/Unsupported Version Number" },
+  { BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"},
+  { BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"},
+  { BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"},
+  { BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"},
+  { BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"}, 
+  { BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
+};
+static const int bgp_notify_open_msg_max = BGP_NOTIFY_OPEN_MAX;
+
+static const struct message bgp_notify_update_msg[] = 
+{
+  { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+  { BGP_NOTIFY_UPDATE_MAL_ATTR, "/Malformed Attribute List"},
+  { BGP_NOTIFY_UPDATE_UNREC_ATTR, "/Unrecognized Well-known Attribute"},
+  { BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute"},
+  { BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"},
+  { BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"},
+  { BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"},
+  { BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop"},
+  { BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"},
+  { BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
+  { BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
+  { BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
+};
+static const int bgp_notify_update_msg_max = BGP_NOTIFY_UPDATE_MAX;
+
+static const struct message bgp_notify_cease_msg[] =
+{
+  { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+  { BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached"},
+  { BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administratively Shutdown"},
+  { BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer Unconfigured"},
+  { BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administratively Reset"},
+  { BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected"},
+  { BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change"},
+  { BGP_NOTIFY_CEASE_COLLISION_RESOLUTION, "/Connection collision resolution"},
+  { BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resource"},
+};
+static const int bgp_notify_cease_msg_max = BGP_NOTIFY_CEASE_MAX;
+
+static const struct message bgp_notify_capability_msg[] = 
+{
+  { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+  { BGP_NOTIFY_CAPABILITY_INVALID_ACTION, "/Invalid Action Value" },
+  { BGP_NOTIFY_CAPABILITY_INVALID_LENGTH, "/Invalid Capability Length"},
+  { BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value"},
+};
+static const int bgp_notify_capability_msg_max = BGP_NOTIFY_CAPABILITY_MAX;
+
+/* Origin strings. */
+const char *bgp_origin_str[] = {"i","e","?"};
+const char *bgp_origin_long_str[] = {"IGP","EGP","incomplete"};
+
+/* Dump attribute. */
+int
+bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size)
+{
+  if (! attr)
+    return 0;
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)))
+    snprintf (buf, size, "nexthop %s", inet_ntoa (attr->nexthop));
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
+    snprintf (buf + strlen (buf), size - strlen (buf), ", origin %s",
+             bgp_origin_str[attr->origin]);
+
+  if (attr->extra)
+    {
+      char addrbuf[BUFSIZ];
+
+      /* Add MP case. */
+      if (attr->extra->mp_nexthop_len == 16 
+          || attr->extra->mp_nexthop_len == 32)
+        snprintf (buf + strlen (buf), size - strlen (buf), ", mp_nexthop %s",
+                  inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, 
+                             addrbuf, BUFSIZ));
+
+      if (attr->extra->mp_nexthop_len == 32)
+        snprintf (buf + strlen (buf), size - strlen (buf), "(%s)",
+                  inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local, 
+                             addrbuf, BUFSIZ));
+    }
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+    snprintf (buf + strlen (buf), size - strlen (buf), ", localpref %u",
+             attr->local_pref);
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) 
+    snprintf (buf + strlen (buf), size - strlen (buf), ", metric %u",
+             attr->med);
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))) 
+    snprintf (buf + strlen (buf), size - strlen (buf), ", community %s",
+             community_str (attr->community));
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)))
+    snprintf (buf + strlen (buf), size - strlen (buf), ", atomic-aggregate");
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)))
+    snprintf (buf + strlen (buf), size - strlen (buf), ", aggregated by %u %s",
+             attr->extra->aggregator_as,
+             inet_ntoa (attr->extra->aggregator_addr));
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)))
+    snprintf (buf + strlen (buf), size - strlen (buf), ", originator %s",
+             inet_ntoa (attr->extra->originator_id));
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)))
+    {
+      int i;
+
+      snprintf (buf + strlen (buf), size - strlen (buf), ", clusterlist");
+      for (i = 0; i < attr->extra->cluster->length / 4; i++)
+       snprintf (buf + strlen (buf), size - strlen (buf), " %s",
+                 inet_ntoa (attr->extra->cluster->list[i]));
+    }
+
+  if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) 
+    snprintf (buf + strlen (buf), size - strlen (buf), ", path %s",
+             aspath_print (attr->aspath));
+
+  if (strlen (buf) > 1)
+    return 1;
+  else
+    return 0;
+}
+
+/* dump notify packet */
+void
+bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, 
+                 const char *direct)
+{
+  const char *subcode_str;
+  const char *code_str;
+
+  subcode_str = "";
+  code_str = LOOKUP_DEF (bgp_notify_msg, bgp_notify->code,
+                         "Unrecognized Error Code");
+
+  switch (bgp_notify->code)
+    {
+    case BGP_NOTIFY_HEADER_ERR:
+      subcode_str = LOOKUP_DEF (bgp_notify_head_msg, bgp_notify->subcode,
+                                "Unrecognized Error Subcode");
+      break;
+    case BGP_NOTIFY_OPEN_ERR:
+      subcode_str = LOOKUP_DEF (bgp_notify_open_msg, bgp_notify->subcode,
+                                "Unrecognized Error Subcode");
+      break;
+    case BGP_NOTIFY_UPDATE_ERR:
+      subcode_str = LOOKUP_DEF (bgp_notify_update_msg, bgp_notify->subcode,
+                                "Unrecognized Error Subcode");
+      break;
+    case BGP_NOTIFY_HOLD_ERR:
+      break;
+    case BGP_NOTIFY_FSM_ERR:
+      break;
+    case BGP_NOTIFY_CEASE:
+      subcode_str = LOOKUP_DEF (bgp_notify_cease_msg, bgp_notify->subcode,
+                                "Unrecognized Error Subcode");
+      break;
+    case BGP_NOTIFY_CAPABILITY_ERR:
+      subcode_str = LOOKUP_DEF (bgp_notify_capability_msg, bgp_notify->subcode,
+                                "Unrecognized Error Subcode");
+      break;
+    }
+
+  if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+    zlog_info ("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s",
+              strcmp (direct, "received") == 0 ? "received from" : "sent to",
+              peer->host, bgp_notify->code, bgp_notify->subcode,
+              code_str, subcode_str, bgp_notify->length,
+              bgp_notify->data ? bgp_notify->data : "");
+  else if (BGP_DEBUG (normal, NORMAL))
+    plog_debug (peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s",
+              peer ? peer->host : "",
+              direct, bgp_notify->code, bgp_notify->subcode,
+              code_str, subcode_str, bgp_notify->length,
+              bgp_notify->data ? bgp_notify->data : "");
+}
+
+/* Debug option setting interface. */
+unsigned long bgp_debug_option = 0;
+
+int  
+debug (unsigned int option)
+{
+  return bgp_debug_option & option; 
+}
+
+DEFUN (debug_bgp_as4,
+       debug_bgp_as4_cmd,
+       "debug bgp as4",
+       DEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (as4, AS4);
+  else
+    {
+      TERM_DEBUG_ON (as4, AS4);
+      vty_out (vty, "BGP as4 debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_as4,
+       no_debug_bgp_as4_cmd,
+       "no debug bgp as4",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (as4, AS4);
+  else
+    {
+      TERM_DEBUG_OFF (as4, AS4);
+      vty_out (vty, "BGP as4 debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_as4,
+       undebug_bgp_as4_cmd,
+       "undebug bgp as4",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n")
+
+DEFUN (debug_bgp_as4_segment,
+       debug_bgp_as4_segment_cmd,
+       "debug bgp as4 segment",
+       DEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n"
+       "BGP AS4 aspath segment handling\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (as4, AS4_SEGMENT);
+  else
+    {
+      TERM_DEBUG_ON (as4, AS4_SEGMENT);
+      vty_out (vty, "BGP as4 segment debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_as4_segment,
+       no_debug_bgp_as4_segment_cmd,
+       "no debug bgp as4 segment",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n"
+       "BGP AS4 aspath segment handling\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (as4, AS4_SEGMENT);
+  else
+    {
+      TERM_DEBUG_OFF (as4, AS4_SEGMENT);
+      vty_out (vty, "BGP as4 segment debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_as4_segment,
+       undebug_bgp_as4_segment_cmd,
+       "undebug bgp as4 segment",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP AS4 actions\n"
+       "BGP AS4 aspath segment handling\n")
+
+DEFUN (debug_bgp_fsm,
+       debug_bgp_fsm_cmd,
+       "debug bgp fsm",
+       DEBUG_STR
+       BGP_STR
+       "BGP Finite State Machine\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (fsm, FSM);
+  else
+    {
+      TERM_DEBUG_ON (fsm, FSM);
+      vty_out (vty, "BGP fsm debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_fsm,
+       no_debug_bgp_fsm_cmd,
+       "no debug bgp fsm",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "Finite State Machine\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (fsm, FSM);
+  else
+    {
+      TERM_DEBUG_OFF (fsm, FSM);
+      vty_out (vty, "BGP fsm debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_fsm,
+       undebug_bgp_fsm_cmd,
+       "undebug bgp fsm",
+       UNDEBUG_STR
+       BGP_STR
+       "Finite State Machine\n")
+
+DEFUN (debug_bgp_events,
+       debug_bgp_events_cmd,
+       "debug bgp events",
+       DEBUG_STR
+       BGP_STR
+       "BGP events\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (events, EVENTS);
+  else
+    {
+      TERM_DEBUG_ON (events, EVENTS);
+      vty_out (vty, "BGP events debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_events,
+       no_debug_bgp_events_cmd,
+       "no debug bgp events",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP events\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (events, EVENTS);
+  else
+    {
+      TERM_DEBUG_OFF (events, EVENTS);
+      vty_out (vty, "BGP events debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_events,
+       undebug_bgp_events_cmd,
+       "undebug bgp events",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP events\n")
+
+DEFUN (debug_bgp_nht,
+       debug_bgp_nht_cmd,
+       "debug bgp nht",
+       DEBUG_STR
+       BGP_STR
+       "BGP nexthop tracking events\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (nht, NHT);
+  else
+    {
+      TERM_DEBUG_ON (nht, NHT);
+      vty_out (vty, "BGP nexthop tracking debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_nht,
+       no_debug_bgp_nht_cmd,
+       "no debug bgp nht",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP nexthop tracking events\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (nht, NHT);
+  else
+    {
+      TERM_DEBUG_OFF (nht, NHT);
+      vty_out (vty, "BGP nexthop tracking debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_nht,
+       undebug_bgp_nht_cmd,
+       "undebug bgp nht",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP next-hop tracking updates\n")
+
+DEFUN (debug_bgp_filter,
+       debug_bgp_filter_cmd,
+       "debug bgp filters",
+       DEBUG_STR
+       BGP_STR
+       "BGP filters\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (filter, FILTER);
+  else
+    {
+      TERM_DEBUG_ON (filter, FILTER);
+      vty_out (vty, "BGP filters debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_filter,
+       no_debug_bgp_filter_cmd,
+       "no debug bgp filters",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP filters\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (filter, FILTER);
+  else
+    {
+      TERM_DEBUG_OFF (filter, FILTER);
+      vty_out (vty, "BGP filters debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_filter,
+       undebug_bgp_filter_cmd,
+       "undebug bgp filters",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP filters\n")
+
+DEFUN (debug_bgp_keepalive,
+       debug_bgp_keepalive_cmd,
+       "debug bgp keepalives",
+       DEBUG_STR
+       BGP_STR
+       "BGP keepalives\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (keepalive, KEEPALIVE);
+  else
+    {
+      TERM_DEBUG_ON (keepalive, KEEPALIVE);
+      vty_out (vty, "BGP keepalives debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_keepalive,
+       no_debug_bgp_keepalive_cmd,
+       "no debug bgp keepalives",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP keepalives\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (keepalive, KEEPALIVE);
+  else
+    {
+      TERM_DEBUG_OFF (keepalive, KEEPALIVE);
+      vty_out (vty, "BGP keepalives debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_keepalive,
+       undebug_bgp_keepalive_cmd,
+       "undebug bgp keepalives",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP keepalives\n")
+
+DEFUN (debug_bgp_update,
+       debug_bgp_update_cmd,
+       "debug bgp updates",
+       DEBUG_STR
+       BGP_STR
+       "BGP updates\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      DEBUG_ON (update, UPDATE_IN);
+      DEBUG_ON (update, UPDATE_OUT);
+    }
+  else
+    {
+      TERM_DEBUG_ON (update, UPDATE_IN);
+      TERM_DEBUG_ON (update, UPDATE_OUT);
+      vty_out (vty, "BGP updates debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_update_direct,
+       debug_bgp_update_direct_cmd,
+       "debug bgp updates (in|out)",
+       DEBUG_STR
+       BGP_STR
+       "BGP updates\n"
+       "Inbound updates\n"
+       "Outbound updates\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (strncmp ("i", argv[0], 1) == 0)
+       {
+         DEBUG_OFF (update, UPDATE_OUT);
+         DEBUG_ON (update, UPDATE_IN);
+       }
+      else
+       {       
+         DEBUG_OFF (update, UPDATE_IN);
+         DEBUG_ON (update, UPDATE_OUT);
+       }
+    }
+  else
+    {
+      if (strncmp ("i", argv[0], 1) == 0)
+       {
+         TERM_DEBUG_OFF (update, UPDATE_OUT);
+         TERM_DEBUG_ON (update, UPDATE_IN);
+         vty_out (vty, "BGP updates debugging is on (inbound)%s", VTY_NEWLINE);
+       }
+      else
+       {
+         TERM_DEBUG_OFF (update, UPDATE_IN);
+         TERM_DEBUG_ON (update, UPDATE_OUT);
+         vty_out (vty, "BGP updates debugging is on (outbound)%s", VTY_NEWLINE);
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update,
+       no_debug_bgp_update_cmd,
+       "no debug bgp updates",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP updates\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      DEBUG_OFF (update, UPDATE_IN);
+      DEBUG_OFF (update, UPDATE_OUT);
+    }
+  else
+    {
+      TERM_DEBUG_OFF (update, UPDATE_IN);
+      TERM_DEBUG_OFF (update, UPDATE_OUT);
+      vty_out (vty, "BGP updates debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_update,
+       undebug_bgp_update_cmd,
+       "undebug bgp updates",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP updates\n")
+
+DEFUN (debug_bgp_normal,
+       debug_bgp_normal_cmd,
+       "debug bgp",
+       DEBUG_STR
+       BGP_STR)
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (normal, NORMAL);
+  else
+    {
+      TERM_DEBUG_ON (normal, NORMAL);
+      vty_out (vty, "BGP debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_normal,
+       no_debug_bgp_normal_cmd,
+       "no debug bgp",
+       NO_STR
+       DEBUG_STR
+       BGP_STR)
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (normal, NORMAL);
+  else
+    {
+      TERM_DEBUG_OFF (normal, NORMAL);
+      vty_out (vty, "BGP debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_normal,
+       undebug_bgp_normal_cmd,
+       "undebug bgp",
+       UNDEBUG_STR
+       BGP_STR)
+
+DEFUN (debug_bgp_zebra,
+       debug_bgp_zebra_cmd,
+       "debug bgp zebra",
+       DEBUG_STR
+       BGP_STR
+       "BGP Zebra messages\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (zebra, ZEBRA);
+  else
+    {
+      TERM_DEBUG_ON (zebra, ZEBRA);
+      vty_out (vty, "BGP zebra debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_zebra,
+       no_debug_bgp_zebra_cmd,
+       "no debug bgp zebra",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP Zebra messages\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (zebra, ZEBRA);
+  else
+    {
+      TERM_DEBUG_OFF (zebra, ZEBRA);
+      vty_out (vty, "BGP zebra debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_zebra,
+       undebug_bgp_zebra_cmd,
+       "undebug bgp zebra",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP Zebra messages\n")
+
+DEFUN (debug_bgp_allow_martians,
+       debug_bgp_allow_martians_cmd,
+       "debug bgp allow-martians",
+       DEBUG_STR
+       BGP_STR
+       "BGP allow martian next hops\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_ON (allow_martians, ALLOW_MARTIANS);
+  else
+    {
+      TERM_DEBUG_ON (allow_martians, ALLOW_MARTIANS);
+      vty_out (vty, "BGP allow_martian next hop debugging is on%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_allow_martians,
+       no_debug_bgp_allow_martians_cmd,
+       "no debug bgp allow-martians",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "BGP allow martian next hops\n")
+{
+  if (vty->node == CONFIG_NODE)
+    DEBUG_OFF (allow_martians, ALLOW_MARTIANS);
+  else
+    {
+      TERM_DEBUG_OFF (allow_martians, ALLOW_MARTIANS);
+      vty_out (vty, "BGP allow martian next hop debugging is off%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_allow_martians,
+       undebug_bgp_allow_martians_cmd,
+       "undebug bgp allow-martians",
+       UNDEBUG_STR
+       BGP_STR
+       "BGP allow martian next hops\n")
+
+DEFUN (no_debug_bgp_all,
+       no_debug_bgp_all_cmd,
+       "no debug all bgp",
+       NO_STR
+       DEBUG_STR
+       "Enable all debugging\n"
+       BGP_STR)
+{
+  TERM_DEBUG_OFF (normal, NORMAL);
+  TERM_DEBUG_OFF (events, EVENTS);
+  TERM_DEBUG_OFF (keepalive, KEEPALIVE);
+  TERM_DEBUG_OFF (update, UPDATE_IN);
+  TERM_DEBUG_OFF (update, UPDATE_OUT);
+  TERM_DEBUG_OFF (as4, AS4);
+  TERM_DEBUG_OFF (as4, AS4_SEGMENT);
+  TERM_DEBUG_OFF (fsm, FSM);
+  TERM_DEBUG_OFF (filter, FILTER);
+  TERM_DEBUG_OFF (zebra, ZEBRA);
+  TERM_DEBUG_OFF (allow_martians, ALLOW_MARTIANS);
+  vty_out (vty, "All possible debugging has been turned off%s", VTY_NEWLINE);
+      
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_all,
+       undebug_bgp_all_cmd,
+       "undebug all bgp",
+       UNDEBUG_STR
+       "Enable all debugging\n"
+       BGP_STR)
+
+DEFUN (show_debugging_bgp,
+       show_debugging_bgp_cmd,
+       "show debugging bgp",
+       SHOW_STR
+       DEBUG_STR
+       BGP_STR)
+{
+  vty_out (vty, "BGP debugging status:%s", VTY_NEWLINE);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    vty_out (vty, "  BGP debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (events, EVENTS))
+    vty_out (vty, "  BGP events debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (keepalive, KEEPALIVE))
+    vty_out (vty, "  BGP keepalives debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (update, UPDATE_IN) && BGP_DEBUG (update, UPDATE_OUT))
+    vty_out (vty, "  BGP updates debugging is on%s", VTY_NEWLINE);
+  else if (BGP_DEBUG (update, UPDATE_IN))
+    vty_out (vty, "  BGP updates debugging is on (inbound)%s", VTY_NEWLINE);
+  else if (BGP_DEBUG (update, UPDATE_OUT))
+    vty_out (vty, "  BGP updates debugging is on (outbound)%s", VTY_NEWLINE);
+  if (BGP_DEBUG (fsm, FSM))
+    vty_out (vty, "  BGP fsm debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (filter, FILTER))
+    vty_out (vty, "  BGP filter debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (zebra, ZEBRA))
+    vty_out (vty, "  BGP zebra debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (as4, AS4))
+    vty_out (vty, "  BGP as4 debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (as4, AS4_SEGMENT))
+    vty_out (vty, "  BGP as4 aspath segment debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (allow_martians, ALLOW_MARTIANS))
+    vty_out (vty, "  BGP allow martian next hop debugging is on%s", VTY_NEWLINE);
+  if (BGP_DEBUG (nht, NHT))
+    vty_out (vty, "  BGP next-hop tracking debugging is on%s", VTY_NEWLINE);
+  vty_out (vty, "%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_config_write_debug (struct vty *vty)
+{
+  int write = 0;
+
+  if (CONF_BGP_DEBUG (normal, NORMAL))
+    {
+      vty_out (vty, "debug bgp%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (as4, AS4))
+    {
+      vty_out (vty, "debug bgp as4%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (as4, AS4_SEGMENT))
+    {
+      vty_out (vty, "debug bgp as4 segment%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (events, EVENTS))
+    {
+      vty_out (vty, "debug bgp events%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (keepalive, KEEPALIVE))
+    {
+      vty_out (vty, "debug bgp keepalives%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (update, UPDATE_IN) && CONF_BGP_DEBUG (update, UPDATE_OUT))
+    {
+      vty_out (vty, "debug bgp updates%s", VTY_NEWLINE);
+      write++;
+    }
+  else if (CONF_BGP_DEBUG (update, UPDATE_IN))
+    {
+      vty_out (vty, "debug bgp updates in%s", VTY_NEWLINE);
+      write++;
+    }
+  else if (CONF_BGP_DEBUG (update, UPDATE_OUT))
+    {
+      vty_out (vty, "debug bgp updates out%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (fsm, FSM))
+    {
+      vty_out (vty, "debug bgp fsm%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (filter, FILTER))
+    {
+      vty_out (vty, "debug bgp filters%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (zebra, ZEBRA))
+    {
+      vty_out (vty, "debug bgp zebra%s", VTY_NEWLINE);
+      write++;
+    }
+
+  if (CONF_BGP_DEBUG (allow_martians, ALLOW_MARTIANS))
+    {
+      vty_out (vty, "debug bgp allow-martians%s", VTY_NEWLINE);
+      write++;
+    }
+  
+  if (CONF_BGP_DEBUG (nht, NHT))
+    {
+      vty_out (vty, "debug bgp nht%s", VTY_NEWLINE);
+      write++;
+    }
+
+  return write;
+}
+
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",
+  1
+};
+
+void
+bgp_debug_init (void)
+{
+  install_node (&debug_node, bgp_config_write_debug);
+
+  install_element (ENABLE_NODE, &show_debugging_bgp_cmd);
+
+  install_element (ENABLE_NODE, &debug_bgp_as4_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_as4_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_as4_segment_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_as4_segment_cmd);
+
+  install_element (ENABLE_NODE, &debug_bgp_fsm_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_fsm_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_events_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_events_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_nht_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_nht_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_filter_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_filter_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_keepalive_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_keepalive_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_update_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_update_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_update_direct_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_update_direct_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_normal_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_normal_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_zebra_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_zebra_cmd);
+  install_element (ENABLE_NODE, &debug_bgp_allow_martians_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_allow_martians_cmd);
+
+  install_element (ENABLE_NODE, &no_debug_bgp_as4_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_as4_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_as4_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_as4_segment_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_as4_segment_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_as4_segment_cmd);
+
+  install_element (ENABLE_NODE, &no_debug_bgp_fsm_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_fsm_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_fsm_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_events_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_nht_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_nht_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_nht_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_filter_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_filter_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_filter_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_keepalive_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_keepalive_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_keepalive_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_update_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_update_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_update_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_normal_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_normal_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_normal_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_zebra_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_allow_martians_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_allow_martians_cmd);
+  install_element (CONFIG_NODE, &no_debug_bgp_allow_martians_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_all_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_all_cmd);
+}
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
new file mode 100644 (file)
index 0000000..253bd7f
--- /dev/null
@@ -0,0 +1,134 @@
+/* BGP message debug header.
+   Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_DEBUG_H
+#define _QUAGGA_BGP_DEBUG_H
+
+#include "bgp_attr.h"
+
+/* sort of packet direction */
+#define DUMP_ON        1
+#define DUMP_SEND      2
+#define DUMP_RECV      4
+
+/* for dump_update */
+#define DUMP_WITHDRAW  8
+#define DUMP_NLRI     16
+
+/* dump detail */
+#define DUMP_DETAIL   32
+
+extern int dump_open;
+extern int dump_update;
+extern int dump_keepalive;
+extern int dump_notify;
+
+extern int Debug_Event;
+extern int Debug_Keepalive;
+extern int Debug_Update;
+extern int Debug_Radix;
+
+#define        NLRI     1
+#define        WITHDRAW 2
+#define        NO_OPT   3
+#define        SEND     4
+#define        RECV     5
+#define        DETAIL   6
+
+/* Prototypes. */
+extern void bgp_debug_init (void);
+extern void bgp_packet_dump (struct stream *);
+
+extern int debug (unsigned int option);
+
+extern unsigned long conf_bgp_debug_as4;
+extern unsigned long conf_bgp_debug_fsm;
+extern unsigned long conf_bgp_debug_events;
+extern unsigned long conf_bgp_debug_packet;
+extern unsigned long conf_bgp_debug_filter;
+extern unsigned long conf_bgp_debug_keepalive;
+extern unsigned long conf_bgp_debug_update;
+extern unsigned long conf_bgp_debug_normal;
+extern unsigned long conf_bgp_debug_zebra;
+extern unsigned long conf_bgp_debug_allow_martians;
+extern unsigned long conf_bgp_debug_nht;
+
+extern unsigned long term_bgp_debug_as4;
+extern unsigned long term_bgp_debug_fsm;
+extern unsigned long term_bgp_debug_events;
+extern unsigned long term_bgp_debug_packet;
+extern unsigned long term_bgp_debug_filter;
+extern unsigned long term_bgp_debug_keepalive;
+extern unsigned long term_bgp_debug_update;
+extern unsigned long term_bgp_debug_normal;
+extern unsigned long term_bgp_debug_zebra;
+extern unsigned long term_bgp_debug_allow_martians;
+extern unsigned long term_bgp_debug_nht;
+
+#define BGP_DEBUG_AS4                 0x01
+#define BGP_DEBUG_AS4_SEGMENT         0x02
+
+#define BGP_DEBUG_FSM                 0x01
+#define BGP_DEBUG_EVENTS              0x01
+#define BGP_DEBUG_PACKET              0x01
+#define BGP_DEBUG_FILTER              0x01
+#define BGP_DEBUG_KEEPALIVE           0x01
+#define BGP_DEBUG_UPDATE_IN           0x01
+#define BGP_DEBUG_UPDATE_OUT          0x02
+#define BGP_DEBUG_NORMAL              0x01
+#define BGP_DEBUG_ZEBRA               0x01
+#define BGP_DEBUG_ALLOW_MARTIANS      0x01
+#define BGP_DEBUG_NHT                 0x01
+
+#define BGP_DEBUG_PACKET_SEND         0x01
+#define BGP_DEBUG_PACKET_SEND_DETAIL  0x02
+
+#define BGP_DEBUG_PACKET_RECV         0x01
+#define BGP_DEBUG_PACKET_RECV_DETAIL  0x02
+
+#define CONF_DEBUG_ON(a, b)    (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b))
+#define CONF_DEBUG_OFF(a, b)   (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b))
+
+#define TERM_DEBUG_ON(a, b)    (term_bgp_debug_ ## a |= (BGP_DEBUG_ ## b))
+#define TERM_DEBUG_OFF(a, b)   (term_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b))
+
+#define DEBUG_ON(a, b) \
+    do { \
+       CONF_DEBUG_ON(a, b); \
+       TERM_DEBUG_ON(a, b); \
+    } while (0)
+#define DEBUG_OFF(a, b) \
+    do { \
+       CONF_DEBUG_OFF(a, b); \
+       TERM_DEBUG_OFF(a, b); \
+    } while (0)
+
+#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)
+
+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 *);
+
+extern const struct message bgp_status_msg[];
+extern const int bgp_status_msg_max;
+
+#endif /* _QUAGGA_BGP_DEBUG_H */
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
new file mode 100644 (file)
index 0000000..01f9b41
--- /dev/null
@@ -0,0 +1,893 @@
+/* BGP-4 dump routine
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "stream.h"
+#include "sockunion.h"
+#include "command.h"
+#include "prefix.h"
+#include "thread.h"
+#include "linklist.h"
+#include "filter.h"
+
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_dump.h"
+
+enum bgp_dump_type
+{
+  BGP_DUMP_ALL,
+  BGP_DUMP_ALL_ET,
+  BGP_DUMP_UPDATES,
+  BGP_DUMP_UPDATES_ET,
+  BGP_DUMP_ROUTES
+};
+
+static const struct bgp_dump_type_map {
+  enum bgp_dump_type type;
+  const char *str;
+} bgp_dump_type_map[] =
+  {
+    {BGP_DUMP_ALL, "all"},
+    {BGP_DUMP_ALL_ET, "all-et"},
+    {BGP_DUMP_UPDATES, "updates"},
+    {BGP_DUMP_UPDATES_ET, "updates-et"},
+    {BGP_DUMP_ROUTES, "routes-mrt"},
+    {0, NULL},
+  };
+
+enum MRT_MSG_TYPES {
+   MSG_NULL,
+   MSG_START,                   /* sender is starting up */
+   MSG_DIE,                     /* receiver should shut down */
+   MSG_I_AM_DEAD,               /* sender is shutting down */
+   MSG_PEER_DOWN,               /* sender's peer is down */
+   MSG_PROTOCOL_BGP,            /* msg is a BGP packet */
+   MSG_PROTOCOL_RIP,            /* msg is a RIP packet */
+   MSG_PROTOCOL_IDRP,           /* msg is an IDRP packet */
+   MSG_PROTOCOL_RIPNG,          /* msg is a RIPNG packet */
+   MSG_PROTOCOL_BGP4PLUS,       /* msg is a BGP4+ packet */
+   MSG_PROTOCOL_BGP4PLUS_01,    /* msg is a BGP4+ (draft 01) packet */
+   MSG_PROTOCOL_OSPF,           /* msg is an OSPF packet */
+   MSG_TABLE_DUMP,              /* routing table dump */
+   MSG_TABLE_DUMP_V2            /* routing table dump, version 2 */
+};
+
+struct bgp_dump
+{
+  enum bgp_dump_type type;
+
+  char *filename;
+
+  FILE *fp;
+
+  unsigned int interval;
+
+  char *interval_str;
+
+  struct thread *t_interval;
+};
+
+static int bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump);
+static int bgp_dump_interval_func (struct thread *);
+
+/* BGP packet dump output buffer. */
+struct stream *bgp_dump_obuf;
+
+/* BGP dump strucuture for 'dump bgp all' */
+struct bgp_dump bgp_dump_all;
+
+/* BGP dump structure for 'dump bgp updates' */
+struct bgp_dump bgp_dump_updates;
+
+/* BGP dump structure for 'dump bgp routes' */
+struct bgp_dump bgp_dump_routes;
+
+static FILE *
+bgp_dump_open_file (struct bgp_dump *bgp_dump)
+{
+  int ret;
+  time_t clock;
+  struct tm *tm;
+  char fullpath[MAXPATHLEN];
+  char realpath[MAXPATHLEN];
+  mode_t oldumask;
+
+  time (&clock);
+  tm = localtime (&clock);
+
+  if (bgp_dump->filename[0] != DIRECTORY_SEP)
+    {
+      sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
+      ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
+    }
+  else
+    ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
+
+  if (ret == 0)
+    {
+      zlog_warn ("bgp_dump_open_file: strftime error");
+      return NULL;
+    }
+
+  if (bgp_dump->fp)
+    fclose (bgp_dump->fp);
+
+
+  oldumask = umask(0777 & ~LOGFILE_MASK);
+  bgp_dump->fp = fopen (realpath, "w");
+
+  if (bgp_dump->fp == NULL)
+    {
+      zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno));
+      umask(oldumask);
+      return NULL;
+    }
+  umask(oldumask);  
+
+  return bgp_dump->fp;
+}
+
+static int
+bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
+{
+  int secs_into_day;
+  time_t t;
+  struct tm *tm;
+
+  if (interval > 0)
+    {
+      /* Periodic dump every interval seconds */
+      if ((interval < 86400) && ((86400 % interval) == 0))
+       {
+         /* Dump at predictable times: if a day has a whole number of
+          * intervals, dump every interval seconds starting from midnight
+          */
+         (void) time(&t);
+         tm = localtime(&t);
+         secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*60*tm->tm_hour;
+         interval = interval - secs_into_day % interval; /* always > 0 */
+       }
+      bgp_dump->t_interval = thread_add_timer (bm->master, bgp_dump_interval_func,
+                                              bgp_dump, interval);
+    }
+  else
+    {
+      /* One-off dump: execute immediately, don't affect any scheduled dumps */
+      bgp_dump->t_interval = thread_add_event (bm->master, bgp_dump_interval_func,
+                                              bgp_dump, 0);
+    }
+
+  return 0;
+}
+
+/* Dump common header. */
+static void
+bgp_dump_header (struct stream *obuf, int type, int subtype, int dump_type)
+{
+  struct timeval clock;
+  long msecs;
+  time_t secs;
+
+  if ((dump_type == BGP_DUMP_ALL_ET || dump_type == BGP_DUMP_UPDATES_ET)
+      && type == MSG_PROTOCOL_BGP4MP)
+    type = MSG_PROTOCOL_BGP4MP_ET;
+
+  gettimeofday(&clock, NULL);
+
+  secs = clock.tv_sec;
+  msecs = clock.tv_usec;
+
+  /* Put dump packet header. */
+  stream_putl (obuf, secs);
+  stream_putw (obuf, type);
+  stream_putw (obuf, subtype);
+  stream_putl (obuf, 0);       /* len */
+
+  /* Adding microseconds for the MRT Extended Header */
+  if (type == MSG_PROTOCOL_BGP4MP_ET)
+    stream_putl (obuf, msecs);
+}
+
+static void
+bgp_dump_set_size (struct stream *s, int type)
+{
+  /*
+   * The BGP_DUMP_HEADER_SIZE stay at 12 event when ET:
+   * "The Microsecond Timestamp is included in the computation
+   *  of the Length field value." (RFC6396 2011)
+   */
+  stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE);
+}
+
+static void
+bgp_dump_routes_index_table(struct bgp *bgp)
+{
+  struct peer *peer;
+  struct listnode *node;
+  uint16_t peerno = 1;
+  struct stream *obuf;
+
+  obuf = bgp_dump_obuf;
+  stream_reset (obuf);
+
+  /* MRT header */
+  bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE,
+                  BGP_DUMP_ROUTES);
+
+  /* Collector BGP ID */
+  stream_put_in_addr (obuf, &bgp->router_id);
+
+  /* View name */
+  if(bgp->name)
+    {
+      stream_putw (obuf, strlen(bgp->name));
+      stream_put(obuf, bgp->name, strlen(bgp->name));
+    }
+  else
+    {
+      stream_putw(obuf, 0);
+    }
+
+  /* Peer count ( plus one extra internal peer ) */
+  stream_putw (obuf, listcount(bgp->peer) + 1);
+
+  /* Populate fake peer at index 0, for locally originated routes */
+  /* Peer type (IPv4) */
+  stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
+  /* Peer BGP ID (0.0.0.0) */
+  stream_putl (obuf, 0);
+  /* Peer IP address (0.0.0.0) */
+  stream_putl (obuf, 0);
+  /* Peer ASN (0) */
+  stream_putl (obuf, 0);
+
+  /* Walk down all peers */
+  for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+    {
+
+      /* Peer's type */
+      if (sockunion_family(&peer->su) == AF_INET)
+        {
+          stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
+        }
+      else if (sockunion_family(&peer->su) == AF_INET6)
+        {
+          stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
+        }
+
+      /* Peer's BGP ID */
+      stream_put_in_addr (obuf, &peer->remote_id);
+
+      /* Peer's IP address */
+      if (sockunion_family(&peer->su) == AF_INET)
+        {
+          stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
+        }
+      else if (sockunion_family(&peer->su) == AF_INET6)
+        {
+          stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
+                        IPV6_MAX_BYTELEN);
+        }
+
+      /* Peer's AS number. */
+      /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */
+      stream_putl (obuf, peer->as);
+
+      /* Store the peer number for this peer */
+      peer->table_dump_index = peerno;
+      peerno++;
+    }
+
+  bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
+
+  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
+  fflush (bgp_dump_routes.fp);
+}
+
+
+static struct bgp_info *
+bgp_dump_route_node_record (int afi, struct bgp_node *rn,
+                            struct bgp_info *info, unsigned int seq)
+{
+  struct stream *obuf;
+  size_t sizep;
+  size_t endp;
+
+  obuf = bgp_dump_obuf;
+  stream_reset (obuf);
+
+  /* MRT header */
+  if (afi == AFI_IP)
+    bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST,
+                     BGP_DUMP_ROUTES);
+  else if (afi == AFI_IP6)
+    bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST,
+                     BGP_DUMP_ROUTES);
+
+  /* Sequence number */
+  stream_putl (obuf, seq);
+
+  /* Prefix length */
+  stream_putc (obuf, rn->p.prefixlen);
+
+  /* Prefix */
+  if (afi == AFI_IP)
+    {
+      /* We'll dump only the useful bits (those not 0), but have to
+       * align on 8 bits */
+      stream_write (obuf, (u_char *) &rn->p.u.prefix4, 
+                    (rn->p.prefixlen + 7) / 8);
+    }
+  else if (afi == AFI_IP6)
+    {
+      /* We'll dump only the useful bits (those not 0), but have to
+       * align on 8 bits */
+      stream_write (obuf, (u_char *) &rn->p.u.prefix6,
+                    (rn->p.prefixlen + 7) / 8);
+    }
+
+  /* Save where we are now, so we can overwride the entry count later */
+  sizep = stream_get_endp (obuf);
+
+  /* Entry count */
+  uint16_t entry_count = 0;
+
+  /* Entry count, note that this is overwritten later */
+  stream_putw (obuf, 0);
+
+  endp = stream_get_endp (obuf);
+  for (; info; info = info->next)
+    {
+      size_t cur_endp;
+
+      /* Peer index */
+      stream_putw (obuf, info->peer->table_dump_index);
+
+      /* Originated */
+#ifdef HAVE_CLOCK_MONOTONIC
+      stream_putl (obuf, time (NULL) - (bgp_clock () - info->uptime));
+#else
+      stream_putl (obuf, info->uptime);
+#endif /* HAVE_CLOCK_MONOTONIC */
+
+      /* Dump attribute. */
+      /* Skip prefix & AFI/SAFI for MP_NLRI */
+      bgp_dump_routes_attr (obuf, info->attr, &rn->p);
+
+      cur_endp = stream_get_endp (obuf);
+      if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
+          + BGP_DUMP_HEADER_SIZE)
+        {
+          stream_set_endp (obuf, endp);
+          break;
+        }
+
+      entry_count++;
+      endp = cur_endp;
+    }
+
+  /* Overwrite the entry count, now that we know the right number */
+  stream_putw_at (obuf, sizep, entry_count);
+
+  bgp_dump_set_size (obuf, MSG_TABLE_DUMP_V2);
+  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
+
+  return info;
+}
+
+/* Runs under child process. */
+static unsigned int
+bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
+{
+  struct bgp_info *info;
+  struct bgp_node *rn;
+  struct bgp *bgp;
+  struct bgp_table *table;
+
+  bgp = bgp_get_default ();
+  if (!bgp)
+    return seq;
+
+  if (bgp_dump_routes.fp == NULL)
+    return seq;
+
+  /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
+     so this should only be done on the first call to bgp_dump_routes_func.
+     ( this function will be called once for ipv4 and once for ipv6 ) */
+  if(first_run)
+    bgp_dump_routes_index_table(bgp);
+
+  /* Walk down each BGP route. */
+  table = bgp->rib[afi][SAFI_UNICAST];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    {
+      info = rn->info;
+      while (info)
+      {
+        info = bgp_dump_route_node_record(afi, rn, info, seq);
+        seq++;
+      }
+    }
+
+  fflush (bgp_dump_routes.fp);
+
+  return seq;
+}
+
+static int
+bgp_dump_interval_func (struct thread *t)
+{
+  struct bgp_dump *bgp_dump;
+  bgp_dump = THREAD_ARG (t);
+  bgp_dump->t_interval = NULL;
+
+  /* Reschedule dump even if file couldn't be opened this time... */
+  if (bgp_dump_open_file (bgp_dump) != NULL)
+    {
+      /* In case of bgp_dump_routes, we need special route dump function. */
+      if (bgp_dump->type == BGP_DUMP_ROUTES)
+       {
+         unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0);
+         bgp_dump_routes_func (AFI_IP6, 0, seq);
+         /* Close the file now. For a RIB dump there's no point in leaving
+          * it open until the next scheduled dump starts. */
+         fclose(bgp_dump->fp); bgp_dump->fp = NULL;
+       }
+    }
+
+  /* if interval is set reschedule */
+  if (bgp_dump->interval > 0)
+    bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
+
+  return 0;
+}
+
+/* Dump common information. */
+static void
+bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4)
+{
+  char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+  /* Source AS number and Destination AS number. */
+  if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
+    {
+      stream_putl (obuf, peer->as);
+      stream_putl (obuf, peer->local_as);
+    }
+  else
+    {
+      stream_putw (obuf, peer->as);
+      stream_putw (obuf, peer->local_as);
+    }
+
+  if (peer->su.sa.sa_family == AF_INET)
+    {
+      stream_putw (obuf, peer->ifindex);
+      stream_putw (obuf, AFI_IP);
+
+      stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
+
+      if (peer->su_local)
+       stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN);
+      else
+       stream_put (obuf, empty, IPV4_MAX_BYTELEN);
+    }
+  else if (peer->su.sa.sa_family == AF_INET6)
+    {
+      /* Interface Index and Address family. */
+      stream_putw (obuf, peer->ifindex);
+      stream_putw (obuf, AFI_IP6);
+
+      /* Source IP Address and Destination IP Address. */
+      stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
+
+      if (peer->su_local)
+       stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
+      else
+       stream_put (obuf, empty, IPV6_MAX_BYTELEN);
+    }
+}
+
+/* Dump BGP status change. */
+void
+bgp_dump_state (struct peer *peer, int status_old, int status_new)
+{
+  struct stream *obuf;
+
+  /* If dump file pointer is disabled return immediately. */
+  if (bgp_dump_all.fp == NULL)
+    return;
+
+  /* Make dump stream. */
+  obuf = bgp_dump_obuf;
+  stream_reset (obuf);
+
+  bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4,
+                  bgp_dump_all.type);
+  bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/
+
+  stream_putw (obuf, status_old);
+  stream_putw (obuf, status_new);
+
+  /* Set length. */
+  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
+
+  /* Write to the stream. */
+  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp);
+  fflush (bgp_dump_all.fp);
+}
+
+static void
+bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
+                     struct stream *packet)
+{
+  struct stream *obuf;
+
+  /* If dump file pointer is disabled return immediately. */
+  if (bgp_dump->fp == NULL)
+    return;
+
+  /* Make dump stream. */
+  obuf = bgp_dump_obuf;
+  stream_reset (obuf);
+
+  /* Dump header and common part. */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
+    { 
+      bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4,
+                      bgp_dump->type);
+    }
+  else
+    {
+      bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
+                      bgp_dump->type);
+    }
+  bgp_dump_common (obuf, peer, 0);
+
+  /* Packet contents. */
+  stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
+  
+  /* Set length. */
+  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
+
+  /* Write to the stream. */
+  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
+  fflush (bgp_dump->fp);
+}
+
+/* Called from bgp_packet.c when BGP packet is received. */
+void
+bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
+{
+  /* bgp_dump_all. */
+  bgp_dump_packet_func (&bgp_dump_all, peer, packet);
+
+  /* bgp_dump_updates. */
+  if (type == BGP_MSG_UPDATE)
+    bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
+}
+
+static unsigned int
+bgp_dump_parse_time (const char *str)
+{
+  int i;
+  int len;
+  int seen_h;
+  int seen_m;
+  int time;
+  unsigned int total;
+
+  time = 0;
+  total = 0;
+  seen_h = 0;
+  seen_m = 0;
+  len = strlen (str);
+
+  for (i = 0; i < len; i++)
+    {
+      if (isdigit ((int) str[i]))
+       {
+         time *= 10;
+         time += str[i] - '0';
+       }
+      else if (str[i] == 'H' || str[i] == 'h')
+       {
+         if (seen_h)
+           return 0;
+         if (seen_m)
+           return 0;
+         total += time * 60 *60;
+         time = 0;
+         seen_h = 1;
+       }
+      else if (str[i] == 'M' || str[i] == 'm')
+       {
+         if (seen_m)
+           return 0;
+         total += time * 60;
+         time = 0;
+         seen_h = 1;
+       }
+      else
+       return 0;
+    }
+  return total + time;
+}
+
+static int
+bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump,
+              enum bgp_dump_type type, const char *path,
+              const char *interval_str)
+{
+  unsigned int interval;
+  
+  /* Don't schedule duplicate dumps if the dump command is given twice */
+  if (bgp_dump->filename && strcmp(path, bgp_dump->filename) == 0
+      && type == bgp_dump->type)
+    {
+      if (interval_str)
+       {
+          if (bgp_dump->interval_str &&
+             strcmp(bgp_dump->interval_str, interval_str) == 0)
+            return CMD_SUCCESS;
+        }
+      else
+        {
+          if (!bgp_dump->interval_str)
+            return CMD_SUCCESS;
+        }
+    }
+
+  /* Removing previous config */
+  bgp_dump_unset(vty, bgp_dump);
+
+  if (interval_str)
+    {
+      /* Check interval string. */
+      interval = bgp_dump_parse_time (interval_str);
+      if (interval == 0)
+       {
+         vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      /* Setting interval string */
+      bgp_dump->interval_str = strdup (interval_str);
+    }
+  else
+    {
+      interval = 0;
+    }
+
+  /* Set type. */
+  bgp_dump->type = type;
+
+  /* Set interval */
+  bgp_dump->interval = interval;
+
+  /* Set file name. */
+  bgp_dump->filename = strdup (path);
+
+  /* Create interval thread. */
+  bgp_dump_interval_add (bgp_dump, interval);
+
+  /* This should be called when interval is expired. */
+  bgp_dump_open_file (bgp_dump);
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
+{
+  /* Removing file name. */
+  if (bgp_dump->filename)
+    {
+      free (bgp_dump->filename);
+      bgp_dump->filename = NULL;
+    }
+
+  /* Closing file. */
+  if (bgp_dump->fp)
+    {
+      fclose (bgp_dump->fp);
+      bgp_dump->fp = NULL;
+    }
+
+  /* Removing interval thread. */
+  if (bgp_dump->t_interval)
+    {
+      thread_cancel (bgp_dump->t_interval);
+      bgp_dump->t_interval = NULL;
+    }
+
+  bgp_dump->interval = 0;
+
+  /* Removing interval string. */
+  if (bgp_dump->interval_str)
+    {
+      free (bgp_dump->interval_str);
+      bgp_dump->interval_str = NULL;
+    }
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (dump_bgp_all,
+       dump_bgp_all_cmd,
+       "dump bgp (all|all-et|updates|updates-et|routes-mrt) PATH [INTERVAL]",
+       "Dump packet\n"
+       "BGP packet dump\n"
+       "Dump all BGP packets\nDump all BGP packets (Extended Tiemstamp Header)\n"
+       "Dump BGP updates only\nDump BGP updates only (Extended Tiemstamp Header)\n"
+       "Dump whole BGP routing table\n"
+       "Output filename\n"
+       "Interval of output\n")
+{
+  int bgp_dump_type = 0;
+  const char *interval = NULL;
+  struct bgp_dump *bgp_dump_struct = NULL;
+  const struct bgp_dump_type_map *map = NULL;
+
+  for (map = bgp_dump_type_map; map->str; map++)
+    if (strcmp(argv[0], map->str) == 0)
+      bgp_dump_type = map->type;
+
+  switch (bgp_dump_type)
+    {
+      case BGP_DUMP_ALL:
+      case BGP_DUMP_ALL_ET:
+        bgp_dump_struct = &bgp_dump_all;
+        break;
+      case BGP_DUMP_UPDATES:
+      case BGP_DUMP_UPDATES_ET:
+        bgp_dump_struct = &bgp_dump_updates;
+        break;
+      case BGP_DUMP_ROUTES:
+      default:
+        bgp_dump_struct = &bgp_dump_routes;
+        break;
+    }
+
+  /* When an interval is given */
+  if (argc == 3)
+      interval = argv[2];
+
+  return bgp_dump_set (vty, bgp_dump_struct, bgp_dump_type,
+                       argv[1], interval);
+}
+
+DEFUN (no_dump_bgp_all,
+       no_dump_bgp_all_cmd,
+       "no dump bgp (all|updates|routes-mrt) [PATH] [INTERVAL]",
+       NO_STR
+       "Stop dump packet\n"
+       "Stop BGP packet dump\n"
+       "Stop dump process all/all-et\n"
+       "Stop dump process updates/updates-et\n"
+       "Stop dump process route-mrt\n")
+{
+  return bgp_dump_unset (vty, &bgp_dump_all);
+}
+
+/* BGP node structure. */
+static struct cmd_node bgp_dump_node =
+{
+  DUMP_NODE,
+  "",
+  1
+};
+
+#if 0
+char *
+config_time2str (unsigned int interval)
+{
+  static char buf[BUFSIZ];
+
+  buf[0] = '\0';
+
+  if (interval / 3600)
+    {
+      sprintf (buf, "%dh", interval / 3600);
+      interval %= 3600;
+    }
+  if (interval / 60)
+    {
+      sprintf (buf + strlen (buf), "%dm", interval /60);
+      interval %= 60;
+    }
+  if (interval)
+    {
+      sprintf (buf + strlen (buf), "%d", interval);
+    }
+  return buf;
+}
+#endif
+
+static int
+config_write_bgp_dump (struct vty *vty)
+{
+  if (bgp_dump_all.filename)
+    {
+      const char *type_str = "all";
+      if (bgp_dump_all.type == BGP_DUMP_ALL_ET)
+          type_str = "all-et";
+
+      if (bgp_dump_all.interval_str)
+       vty_out (vty, "dump bgp %s %s %s%s", type_str,
+                bgp_dump_all.filename, bgp_dump_all.interval_str,
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "dump bgp %s %s%s", type_str,
+                bgp_dump_all.filename, VTY_NEWLINE);
+    }
+  if (bgp_dump_updates.filename)
+    {
+      const char *type_str = "updates";
+      if (bgp_dump_updates.type == BGP_DUMP_UPDATES_ET)
+        type_str = "updates-et";
+
+      if (bgp_dump_updates.interval_str)
+       vty_out (vty, "dump bgp %s %s %s%s", type_str,
+                bgp_dump_updates.filename, bgp_dump_updates.interval_str,
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "dump bgp updates %s%s", 
+                bgp_dump_updates.filename, VTY_NEWLINE);
+    }
+  if (bgp_dump_routes.filename)
+    {
+      if (bgp_dump_routes.interval_str)
+       vty_out (vty, "dump bgp routes-mrt %s %s%s", 
+                bgp_dump_routes.filename, bgp_dump_routes.interval_str,
+                VTY_NEWLINE);
+    }
+  return 0;
+}
+
+/* Initialize BGP packet dump functionality. */
+void
+bgp_dump_init (void)
+{
+  memset (&bgp_dump_all, 0, sizeof (struct bgp_dump));
+  memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump));
+  memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump));
+
+  bgp_dump_obuf = stream_new ((BGP_MAX_PACKET_SIZE << 1)
+                              + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
+
+  install_node (&bgp_dump_node, config_write_bgp_dump);
+
+  install_element (CONFIG_NODE, &dump_bgp_all_cmd);
+  install_element (CONFIG_NODE, &no_dump_bgp_all_cmd);
+}
+
+void
+bgp_dump_finish (void)
+{
+  stream_free (bgp_dump_obuf);
+  bgp_dump_obuf = NULL;
+}
diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h
new file mode 100644 (file)
index 0000000..d1e66d9
--- /dev/null
@@ -0,0 +1,57 @@
+/* BGP dump routine.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_DUMP_H
+#define _QUAGGA_BGP_DUMP_H
+
+/* MRT compatible packet dump values.  */
+/* type value */
+#define MSG_PROTOCOL_BGP4MP    16
+#define MSG_PROTOCOL_BGP4MP_ET 17
+
+/* subtype value */
+#define BGP4MP_STATE_CHANGE          0
+#define BGP4MP_MESSAGE               1
+#define BGP4MP_ENTRY                 2
+#define BGP4MP_SNAPSHOT              3
+#define BGP4MP_MESSAGE_AS4           4
+#define BGP4MP_STATE_CHANGE_AS4      5
+
+#define BGP_DUMP_HEADER_SIZE 12
+#define BGP_DUMP_MSG_HEADER  40
+
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE   1
+#define TABLE_DUMP_V2_RIB_IPV4_UNICAST   2
+#define TABLE_DUMP_V2_RIB_IPV4_MULTICAST 3
+#define TABLE_DUMP_V2_RIB_IPV6_UNICAST   4
+#define TABLE_DUMP_V2_RIB_IPV6_MULTICAST 5
+#define TABLE_DUMP_V2_RIB_GENERIC        6
+
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP  0
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6 1
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS2 0
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4 2
+
+extern void bgp_dump_init (void);
+extern void bgp_dump_finish (void);
+extern void bgp_dump_state (struct peer *, int, int);
+extern void bgp_dump_packet (struct peer *, int, struct stream *);
+
+#endif /* _QUAGGA_BGP_DUMP_H */
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
new file mode 100644 (file)
index 0000000..5d69b42
--- /dev/null
@@ -0,0 +1,776 @@
+/* BGP Extended Communities Attribute
+   Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_aspath.h"
+
+/* Hash of community attribute. */
+static struct hash *ecomhash;
+
+/* Allocate a new ecommunities.  */
+static struct ecommunity *
+ecommunity_new (void)
+{
+  return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
+                                       sizeof (struct ecommunity));
+}
+
+/* Allocate ecommunities.  */
+void
+ecommunity_free (struct ecommunity **ecom)
+{
+  if ((*ecom)->val)
+    XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
+  if ((*ecom)->str)
+    XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str);
+  XFREE (MTYPE_ECOMMUNITY, *ecom);
+  ecom = NULL;
+}
+
+/* Add a new Extended Communities value to Extended Communities
+   Attribute structure.  When the value is already exists in the
+   structure, we don't add the value.  Newly added value is sorted by
+   numerical order.  When the value is added to the structure return 1
+   else return 0.  */
+static int
+ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
+{
+  u_int8_t *p;
+  int ret;
+  int c;
+
+  /* When this is fist value, just add it.  */
+  if (ecom->val == NULL)
+    {
+      ecom->size++;
+      ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
+      memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
+      return 1;
+    }
+
+  /* If the value already exists in the structure return 0.  */
+  c = 0;
+  for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
+    {
+      ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
+      if (ret == 0)
+        return 0;
+      if (ret > 0)
+        break;
+    }
+
+  /* Add the value to the structure with numerical sorting.  */
+  ecom->size++;
+  ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
+
+  memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
+          ecom->val + c * ECOMMUNITY_SIZE,
+          (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
+  memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
+
+  return 1;
+}
+
+/* This function takes pointer to Extended Communites strucutre then
+   create a new Extended Communities structure by uniq and sort each
+   Extended Communities value.  */
+struct ecommunity *
+ecommunity_uniq_sort (struct ecommunity *ecom)
+{
+  int i;
+  struct ecommunity *new;
+  struct ecommunity_val *eval;
+  
+  if (! ecom)
+    return NULL;
+  
+  new = ecommunity_new ();
+  
+  for (i = 0; i < ecom->size; i++)
+    {
+      eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+      ecommunity_add_val (new, eval);
+    }
+  return new;
+}
+
+/* Parse Extended Communites Attribute in BGP packet.  */
+struct ecommunity *
+ecommunity_parse (u_int8_t *pnt, u_short length)
+{
+  struct ecommunity tmp;
+  struct ecommunity *new;
+
+  /* Length check.  */
+  if (length % ECOMMUNITY_SIZE)
+    return NULL;
+
+  /* Prepare tmporary structure for making a new Extended Communities
+     Attribute.  */
+  tmp.size = length / ECOMMUNITY_SIZE;
+  tmp.val = pnt;
+
+  /* Create a new Extended Communities Attribute by uniq and sort each
+     Extended Communities value  */
+  new = ecommunity_uniq_sort (&tmp);
+
+  return ecommunity_intern (new);
+}
+
+/* Duplicate the Extended Communities Attribute structure.  */
+struct ecommunity *
+ecommunity_dup (struct ecommunity *ecom)
+{
+  struct ecommunity *new;
+
+  new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
+  new->size = ecom->size;
+  if (new->size)
+    {
+      new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
+      memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
+    }
+  else
+    new->val = NULL;
+  return new;
+}
+
+/* Retrun string representation of communities attribute. */
+char *
+ecommunity_str (struct ecommunity *ecom)
+{
+  if (! ecom->str)
+    ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
+  return ecom->str;
+}
+
+/* Merge two Extended Communities Attribute structure.  */
+struct ecommunity *
+ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
+{
+  if (ecom1->val)
+    ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val, 
+                          (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+  else
+    ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
+                         (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+
+  memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
+         ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
+  ecom1->size += ecom2->size;
+
+  return ecom1;
+}
+
+/* Intern Extended Communities Attribute.  */
+struct ecommunity *
+ecommunity_intern (struct ecommunity *ecom)
+{
+  struct ecommunity *find;
+
+  assert (ecom->refcnt == 0);
+
+  find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
+
+  if (find != ecom)
+    ecommunity_free (&ecom);
+
+  find->refcnt++;
+
+  if (! find->str)
+    find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
+
+  return find;
+}
+
+/* Unintern Extended Communities Attribute.  */
+void
+ecommunity_unintern (struct ecommunity **ecom)
+{
+  struct ecommunity *ret;
+
+  if ((*ecom)->refcnt)
+    (*ecom)->refcnt--;
+    
+  /* Pull off from hash.  */
+  if ((*ecom)->refcnt == 0)
+    {
+      /* Extended community must be in the hash.  */
+      ret = (struct ecommunity *) hash_release (ecomhash, *ecom);
+      assert (ret != NULL);
+
+      ecommunity_free (ecom);
+    }
+}
+
+/* Utinity function to make hash key.  */
+unsigned int
+ecommunity_hash_make (void *arg)
+{
+  const struct ecommunity *ecom = arg;
+  int size = ecom->size * ECOMMUNITY_SIZE;
+  u_int8_t *pnt = ecom->val;
+  unsigned int key = 0;
+  int c;
+
+  for (c = 0; c < size; c += ECOMMUNITY_SIZE)
+    {
+      key += pnt[c];
+      key += pnt[c + 1];
+      key += pnt[c + 2];
+      key += pnt[c + 3];
+      key += pnt[c + 4];
+      key += pnt[c + 5];
+      key += pnt[c + 6];
+      key += pnt[c + 7];
+    }
+
+  return key;
+}
+
+/* Compare two Extended Communities Attribute structure.  */
+int
+ecommunity_cmp (const void *arg1, const void *arg2)
+{
+  const struct ecommunity *ecom1 = arg1;
+  const struct ecommunity *ecom2 = arg2;
+  
+  return (ecom1->size == ecom2->size
+         && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
+}
+
+/* Initialize Extended Comminities related hash. */
+void
+ecommunity_init (void)
+{
+  ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
+}
+
+void
+ecommunity_finish (void)
+{
+  hash_free (ecomhash);
+  ecomhash = NULL;
+}
+
+/* Extended Communities token enum. */
+enum ecommunity_token
+{
+  ecommunity_token_unknown = 0,
+  ecommunity_token_rt,
+  ecommunity_token_soo,
+  ecommunity_token_val,
+};
+
+/* Get next Extended Communities token from the string. */
+static const char *
+ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
+                    enum ecommunity_token *token)
+{
+  int ret;
+  int dot = 0;
+  int digit = 0;
+  int separator = 0;
+  const char *p = str;
+  char *endptr;
+  struct in_addr ip;
+  as_t as = 0;
+  u_int32_t val = 0;
+  char buf[INET_ADDRSTRLEN + 1];
+
+  /* Skip white space. */
+  while (isspace ((int) *p))
+    {
+      p++;
+      str++;
+    }
+
+  /* Check the end of the line. */
+  if (*p == '\0')
+    return NULL;
+
+  /* "rt" and "soo" keyword parse. */
+  if (! isdigit ((int) *p)) 
+    {
+      /* "rt" match check.  */
+      if (tolower ((int) *p) == 'r')
+       {
+         p++;
+         if (tolower ((int) *p) == 't')
+           {
+             p++;
+             *token = ecommunity_token_rt;
+             return p;
+           }
+         if (isspace ((int) *p) || *p == '\0')
+           {
+             *token = ecommunity_token_rt;
+             return p;
+           }
+         goto error;
+       }
+      /* "soo" match check.  */
+      else if (tolower ((int) *p) == 's')
+       {
+         p++;
+         if (tolower ((int) *p) == 'o')
+           {
+             p++;
+             if (tolower ((int) *p) == 'o')
+               {
+                 p++;
+                 *token = ecommunity_token_soo;
+                 return p;
+               }
+             if (isspace ((int) *p) || *p == '\0')
+               {
+                 *token = ecommunity_token_soo;
+                 return p;
+               }
+             goto error;
+           }
+         if (isspace ((int) *p) || *p == '\0')
+           {
+             *token = ecommunity_token_soo;
+             return p;
+           }
+         goto error;
+       }
+      goto error;
+    }
+  
+  /* What a mess, there are several possibilities:
+   *
+   * a) A.B.C.D:MN
+   * b) EF:OPQR
+   * c) GHJK:MN
+   *
+   * A.B.C.D: Four Byte IP
+   * EF:      Two byte ASN
+   * GHJK:    Four-byte ASN
+   * MN:      Two byte value
+   * OPQR:    Four byte value
+   *
+   */
+  while (isdigit ((int) *p) || *p == ':' || *p == '.') 
+    {
+      if (*p == ':')
+       {
+         if (separator)
+           goto error;
+
+         separator = 1;
+         digit = 0;
+         
+         if ((p - str) > INET_ADDRSTRLEN)
+           goto error;
+          memset (buf, 0, INET_ADDRSTRLEN + 1);
+          memcpy (buf, str, p - str);
+          
+         if (dot)
+           {
+             /* Parsing A.B.C.D in:
+               * A.B.C.D:MN
+               */
+             ret = inet_aton (buf, &ip);
+             if (ret == 0)
+               goto error;
+           }
+          else
+            {
+              /* ASN */
+              as = strtoul (buf, &endptr, 10);
+              if (*endptr != '\0' || as == BGP_AS4_MAX)
+                goto error;
+            }
+       }
+      else if (*p == '.')
+       {
+         if (separator)
+           goto error;
+         dot++;
+         if (dot > 4)
+           goto error;
+       }
+      else
+       {
+         digit = 1;
+         
+         /* We're past the IP/ASN part */
+         if (separator)
+           {
+             val *= 10;
+             val += (*p - '0');
+            }
+       }
+      p++;
+    }
+
+  /* Low digit part must be there. */
+  if (!digit || !separator)
+    goto error;
+
+  /* Encode result into routing distinguisher.  */
+  if (dot)
+    {
+      if (val > UINT16_MAX)
+        goto error;
+      
+      eval->val[0] = ECOMMUNITY_ENCODE_IP;
+      eval->val[1] = 0;
+      memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+      eval->val[6] = (val >> 8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+  else if (as > BGP_AS_MAX)
+    {
+      if (val > UINT16_MAX)
+        goto error;
+      
+      eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+      eval->val[1] = 0;
+      eval->val[2] = (as >>24) & 0xff;
+      eval->val[3] = (as >>16) & 0xff;
+      eval->val[4] = (as >>8) & 0xff;
+      eval->val[5] =  as & 0xff;
+      eval->val[6] = (val >> 8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+  else
+    {
+      eval->val[0] = ECOMMUNITY_ENCODE_AS;
+      eval->val[1] = 0;
+      
+      eval->val[2] = (as >>8) & 0xff;
+      eval->val[3] = as & 0xff;
+      eval->val[4] = (val >>24) & 0xff;
+      eval->val[5] = (val >>16) & 0xff;
+      eval->val[6] = (val >>8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+  *token = ecommunity_token_val;
+  return p;
+
+ error:
+  *token = ecommunity_token_unknown;
+  return p;
+}
+
+/* Convert string to extended community attribute. 
+
+   When type is already known, please specify both str and type.  str
+   should not include keyword such as "rt" and "soo".  Type is
+   ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
+   keyword_included should be zero.
+
+   For example route-map's "set extcommunity" command case:
+
+   "rt 100:1 100:2 100:3"        -> str = "100:1 100:2 100:3"
+                                    type = ECOMMUNITY_ROUTE_TARGET
+                                    keyword_included = 0
+
+   "soo 100:1"                   -> str = "100:1"
+                                    type = ECOMMUNITY_SITE_ORIGIN
+                                    keyword_included = 0
+
+   When string includes keyword for each extended community value.
+   Please specify keyword_included as non-zero value.
+
+   For example standard extcommunity-list case:
+
+   "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
+                                    type = 0
+                                    keyword_include = 1
+*/
+struct ecommunity *
+ecommunity_str2com (const char *str, int type, int keyword_included)
+{
+  struct ecommunity *ecom = NULL;
+  enum ecommunity_token token = ecommunity_token_unknown;
+  struct ecommunity_val eval;
+  int keyword = 0;
+
+  while ((str = ecommunity_gettoken (str, &eval, &token)))
+    {
+      switch (token)
+       {
+       case ecommunity_token_rt:
+       case ecommunity_token_soo:
+         if (! keyword_included || keyword)
+           {
+             if (ecom)
+               ecommunity_free (&ecom);
+             return NULL;
+           }
+         keyword = 1;
+
+         if (token == ecommunity_token_rt)
+           {
+             type = ECOMMUNITY_ROUTE_TARGET;
+           }
+         if (token == ecommunity_token_soo)
+           {
+             type = ECOMMUNITY_SITE_ORIGIN;
+           }
+         break;
+       case ecommunity_token_val:
+         if (keyword_included)
+           {
+             if (! keyword)
+               {
+                 if (ecom)
+                   ecommunity_free (&ecom);
+                 return NULL;
+               }
+             keyword = 0;
+           }
+         if (ecom == NULL)
+           ecom = ecommunity_new ();
+         eval.val[1] = type;
+         ecommunity_add_val (ecom, &eval);
+         break;
+       case ecommunity_token_unknown:
+       default:
+         if (ecom)
+           ecommunity_free (&ecom);
+         return NULL;
+       }
+    }
+  return ecom;
+}
+
+/* Convert extended community attribute to string.  
+
+   Due to historical reason of industry standard implementation, there
+   are three types of format.
+
+   route-map set extcommunity format
+        "rt 100:1 100:2"
+        "soo 100:3"
+
+   extcommunity-list
+        "rt 100:1 rt 100:2 soo 100:3"
+
+   "show ip bgp" and extcommunity-list regular expression matching
+        "RT:100:1 RT:100:2 SoO:100:3"
+
+   For each formath please use below definition for format:
+
+   ECOMMUNITY_FORMAT_ROUTE_MAP
+   ECOMMUNITY_FORMAT_COMMUNITY_LIST
+   ECOMMUNITY_FORMAT_DISPLAY
+*/
+char *
+ecommunity_ecom2str (struct ecommunity *ecom, int format)
+{
+  int i;
+  u_int8_t *pnt;
+  int encode = 0;
+  int type = 0;
+#define ECOMMUNITY_STR_DEFAULT_LEN  27
+  int str_size;
+  int str_pnt;
+  char *str_buf;
+  const char *prefix;
+  int len = 0;
+  int first = 1;
+
+  /* For parse Extended Community attribute tupple. */
+  struct ecommunity_as
+  {
+    as_t as;
+    u_int32_t val;
+  } eas;
+
+  struct ecommunity_ip
+  {
+    struct in_addr ip;
+    u_int16_t val;
+  } eip;
+
+  if (ecom->size == 0)
+    {
+      str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
+      str_buf[0] = '\0';
+      return str_buf;
+    }
+
+  /* Prepare buffer.  */
+  str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
+  str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
+  str_pnt = 0;
+
+  for (i = 0; i < ecom->size; i++)
+    {
+      /* Make it sure size is enough.  */
+      while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
+       {
+         str_size *= 2;
+         str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
+       }
+
+      /* Space between each value.  */
+      if (! first)
+       str_buf[str_pnt++] = ' ';
+
+      pnt = ecom->val + (i * 8);
+
+      /* High-order octet of type. */
+      encode = *pnt++;
+
+      switch (encode)
+        {
+        case ECOMMUNITY_ENCODE_AS:
+        case ECOMMUNITY_ENCODE_IP:
+        case ECOMMUNITY_ENCODE_AS4:
+          break;
+
+        case ECOMMUNITY_ENCODE_OPAQUE:
+          if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
+            {
+              uint16_t tunneltype;
+              memcpy (&tunneltype, pnt + 5, 2);
+              tunneltype = ntohs(tunneltype);
+              len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
+              str_pnt += len;
+              first = 0;
+              continue;
+            }
+            /* fall through */
+
+        default:
+          len = sprintf (str_buf + str_pnt, "?");
+          str_pnt += len;
+          first = 0;
+          continue;
+        }
+
+      /* Low-order octet of type. */
+      type = *pnt++;
+      if (type !=  ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
+       {
+         len = sprintf (str_buf + str_pnt, "?");
+         str_pnt += len;
+         first = 0;
+         continue;
+       }
+
+      switch (format)
+       {
+       case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
+         prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
+         break;
+       case ECOMMUNITY_FORMAT_DISPLAY:
+         prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
+         break;
+       case ECOMMUNITY_FORMAT_ROUTE_MAP:
+         prefix = "";
+         break;
+       default:
+         prefix = "";
+         break;
+       }
+
+      /* Put string into buffer.  */
+      if (encode == ECOMMUNITY_ENCODE_AS4)
+       {
+         eas.as = (*pnt++ << 24);
+         eas.as |= (*pnt++ << 16);
+         eas.as |= (*pnt++ << 8);
+         eas.as |= (*pnt++);
+
+         eas.val = (*pnt++ << 8);
+         eas.val |= (*pnt++);
+
+         len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
+                        eas.as, eas.val );
+         str_pnt += len;
+         first = 0;
+       }
+      if (encode == ECOMMUNITY_ENCODE_AS)
+       {
+         eas.as = (*pnt++ << 8);
+         eas.as |= (*pnt++);
+
+         eas.val = (*pnt++ << 24);
+         eas.val |= (*pnt++ << 16);
+         eas.val |= (*pnt++ << 8);
+         eas.val |= (*pnt++);
+
+         len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix,
+                        eas.as, eas.val);
+         str_pnt += len;
+         first = 0;
+       }
+      else if (encode == ECOMMUNITY_ENCODE_IP)
+       {
+         memcpy (&eip.ip, pnt, 4);
+         pnt += 4;
+         eip.val = (*pnt++ << 8);
+         eip.val |= (*pnt++);
+
+         len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix,
+                        inet_ntoa (eip.ip), eip.val);
+         str_pnt += len;
+         first = 0;
+       }
+    }
+  return str_buf;
+}
+
+int
+ecommunity_match (const struct ecommunity *ecom1, 
+                  const struct ecommunity *ecom2)
+{
+  int i = 0;
+  int j = 0;
+
+  if (ecom1 == NULL && ecom2 == NULL)
+    return 1;
+
+  if (ecom1 == NULL || ecom2 == NULL)
+    return 0;
+
+  if (ecom1->size < ecom2->size)
+    return 0;
+
+  /* Every community on com2 needs to be on com1 for this to match */
+  while (i < ecom1->size && j < ecom2->size)
+    {
+      if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
+        j++;
+      i++;
+    }
+
+  if (j == ecom2->size)
+    return 1;
+  else
+    return 0;
+}
+
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
new file mode 100644 (file)
index 0000000..993fd5a
--- /dev/null
@@ -0,0 +1,88 @@
+/* BGP Extended Communities Attribute.
+   Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ECOMMUNITY_H
+#define _QUAGGA_BGP_ECOMMUNITY_H
+
+/* High-order octet of the Extended Communities type field.  */
+#define ECOMMUNITY_ENCODE_AS                0x00
+#define ECOMMUNITY_ENCODE_IP                0x01
+#define ECOMMUNITY_ENCODE_AS4               0x02
+#define ECOMMUNITY_ENCODE_OPAQUE            0x03
+
+/* Low-order octet of the Extended Communities type field.  */
+#define ECOMMUNITY_ROUTE_TARGET             0x02
+#define ECOMMUNITY_SITE_ORIGIN              0x03
+
+/* Low-order octet of the Extended Communities type field for OPAQUE types */
+#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP     0x0c
+
+/* Extended communities attribute string format.  */
+#define ECOMMUNITY_FORMAT_ROUTE_MAP            0
+#define ECOMMUNITY_FORMAT_COMMUNITY_LIST       1
+#define ECOMMUNITY_FORMAT_DISPLAY              2
+
+/* Extended Communities value is eight octet long.  */
+#define ECOMMUNITY_SIZE                        8
+
+/* Extended Communities type flag.  */
+#define ECOMMUNITY_FLAG_NON_TRANSITIVE      0x40  
+
+/* Extended Communities attribute.  */
+struct ecommunity
+{
+  /* Reference counter.  */
+  unsigned long refcnt;
+
+  /* Size of Extended Communities attribute.  */
+  int size;
+
+  /* Extended Communities value.  */
+  u_int8_t *val;
+
+  /* Human readable format string.  */
+  char *str;
+};
+
+/* Extended community value is eight octet.  */
+struct ecommunity_val
+{
+  char val[ECOMMUNITY_SIZE];
+};
+
+#define ecom_length(X)    ((X)->size * ECOMMUNITY_SIZE)
+
+extern void ecommunity_init (void);
+extern void ecommunity_finish (void);
+extern void ecommunity_free (struct ecommunity **);
+extern struct ecommunity *ecommunity_parse (u_int8_t *, u_short);
+extern struct ecommunity *ecommunity_dup (struct ecommunity *);
+extern struct ecommunity *ecommunity_merge (struct ecommunity *, struct ecommunity *);
+extern struct ecommunity *ecommunity_uniq_sort (struct ecommunity *);
+extern struct ecommunity *ecommunity_intern (struct ecommunity *);
+extern int ecommunity_cmp (const void *, const void *);
+extern void ecommunity_unintern (struct ecommunity **);
+extern unsigned int ecommunity_hash_make (void *);
+extern struct ecommunity *ecommunity_str2com (const char *, int, int);
+extern char *ecommunity_ecom2str (struct ecommunity *, int);
+extern int ecommunity_match (const struct ecommunity *, const struct ecommunity *);
+extern char *ecommunity_str (struct ecommunity *);
+
+#endif /* _QUAGGA_BGP_ECOMMUNITY_H */
diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c
new file mode 100644 (file)
index 0000000..cd58ac2
--- /dev/null
@@ -0,0 +1,954 @@
+
+/*
+ * This file created by LabN Consulting, L.L.C.
+ *
+ *
+ * This file is based on bgp_mplsvpn.c which is Copyright (C) 2000
+ * Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ */
+
+/* 
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_encap.h"
+
+static u_int16_t
+decode_rd_type (u_char *pnt)
+{
+  u_int16_t v;
+  
+  v = ((u_int16_t) *pnt++ << 8);
+  v |= (u_int16_t) *pnt;
+  return v;
+}
+
+
+static void
+decode_rd_as (u_char *pnt, struct rd_as *rd_as)
+{
+  rd_as->as  = (u_int16_t) *pnt++ << 8;
+  rd_as->as |= (u_int16_t) *pnt++;
+  
+  rd_as->val  = ((u_int32_t) *pnt++) << 24;
+  rd_as->val |= ((u_int32_t) *pnt++) << 16;
+  rd_as->val |= ((u_int32_t) *pnt++) << 8;
+  rd_as->val |= (u_int32_t) *pnt;
+}
+
+static void
+decode_rd_as4 (u_char *pnt, struct rd_as *rd_as)
+{
+  rd_as->as  = (u_int32_t) *pnt++ << 24;
+  rd_as->as |= (u_int32_t) *pnt++ << 16;
+  rd_as->as |= (u_int32_t) *pnt++ << 8;
+  rd_as->as |= (u_int32_t) *pnt++;
+  
+  rd_as->val  = ((u_int32_t) *pnt++ << 8);
+  rd_as->val |= (u_int32_t) *pnt;
+}
+
+static void
+decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
+{
+  memcpy (&rd_ip->ip, pnt, 4);
+  pnt += 4;
+  
+  rd_ip->val = ((u_int16_t) *pnt++ << 8);
+  rd_ip->val |= (u_int16_t) *pnt;
+}
+
+static void
+ecom2prd(struct ecommunity *ecom, struct prefix_rd *prd)
+{
+    int        i;
+
+    memset(prd, 0, sizeof(struct prefix_rd));
+    prd->family = AF_UNSPEC;
+    prd->prefixlen = 64;
+
+    if (!ecom)
+       return;
+
+    for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) {
+
+       uint8_t *ep;
+
+       ep = ecom->val + i;
+
+       switch (ep[0]) {
+           default:
+               continue;
+
+           case 0x80:
+           case 0x81:
+           case 0x82:
+               if (ep[1] == 0x0) {
+                   prd->val[1] = ep[0] & 0x03;
+                   memcpy(prd->val + 2, ep + 2, 6);
+                   return;
+               }
+       }
+    }
+}
+
+int
+bgp_nlri_parse_encap(
+    struct peer                *peer,
+    struct attr                *attr,          /* Need even for withdraw */
+    struct bgp_nlri    *packet)
+{
+  u_char *pnt;
+  u_char *lim;
+  afi_t afi = packet->afi;
+  struct prefix p;
+  int psize = 0;
+  int prefixlen;
+  struct rd_as rd_as;
+  struct rd_ip rd_ip;
+  struct prefix_rd prd;
+  struct ecommunity *pEcom = NULL;
+  u_int16_t rdtype = 0xffff;
+  char buf[BUFSIZ];
+
+  /* Check peer status. */
+  if (peer->status != Established)
+    return 0;
+  
+  /* Make prefix_rd */
+  if (attr && attr->extra && attr->extra->ecommunity)
+      pEcom = attr->extra->ecommunity;
+
+  ecom2prd(pEcom, &prd);
+  memset(&rd_as, 0, sizeof(rd_as));
+  memset(&rd_ip, 0, sizeof(rd_ip));
+
+  if (pEcom) {
+
+      rdtype = (prd.val[0] << 8) | prd.val[1];
+
+      /* Decode RD value. */
+      if (rdtype == RD_TYPE_AS)
+       decode_rd_as (prd.val + 2, &rd_as);
+      else if (rdtype == RD_TYPE_IP)
+       decode_rd_ip (prd.val + 2, &rd_ip);
+      else if (rdtype == RD_TYPE_AS4)
+       decode_rd_as4 (prd.val + 2, &rd_as);
+      else
+       {
+         zlog_err ("Invalid RD type %d", rdtype);
+       }
+
+  }
+
+  /*
+   * NB: this code was based on the MPLS VPN code, which supported RDs.
+   * For the moment we are retaining the underlying RIB structure that
+   * keeps a per-RD radix tree, but since the RDs are not carried over
+   * the wire, we set the RD internally to 0.
+   */
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+  memset(prd.val, 0, sizeof(prd.val));
+
+  pnt = packet->nlri;
+  lim = pnt + packet->length;
+
+  for (; pnt < lim; pnt += psize)
+    {
+      /* Clear prefix structure. */
+      memset (&p, 0, sizeof (struct prefix));
+
+      /* Fetch prefix length. */
+      prefixlen = *pnt++;
+      p.family = afi2family(afi);
+      if (p.family == 0) {
+       /* bad afi, shouldn't happen */
+       zlog_warn("%s: bad afi %d, dropping incoming route", __func__, afi);
+       continue;
+      }
+      psize = PSIZE (prefixlen);
+
+      p.prefixlen = prefixlen;
+      memcpy (&p.u.prefix, pnt, psize);
+
+      if (pnt + psize > lim)
+       return -1;
+
+
+      if (rdtype == RD_TYPE_AS)
+       zlog_info ("rd-as %u:%u prefix %s/%d", rd_as.as, rd_as.val,
+                  inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ),
+                  p.prefixlen);
+      else if (rdtype == RD_TYPE_IP)
+       zlog_info ("rd-ip %s:%u prefix %s/%d", inet_ntoa (rd_ip.ip),
+                  rd_ip.val,
+                  inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ),
+                  p.prefixlen);
+      else if (rdtype == RD_TYPE_AS4)
+       zlog_info ("rd-as4 %u:%u prefix %s/%d", rd_as.as, rd_as.val,
+                  inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ),
+                  p.prefixlen);
+      else
+       zlog_info ("rd unknown, default to 0:0 prefix %s/%d",
+           inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ),
+           p.prefixlen);
+
+      if (attr) {
+       bgp_update (peer, &p, attr, afi, SAFI_ENCAP,
+                   ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0);
+      } else {
+       bgp_withdraw (peer, &p, attr, afi, SAFI_ENCAP,
+                     ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL);
+      }
+    }
+
+  /* Packet length consistency check. */
+  if (pnt != lim)
+    return -1;
+
+  return 0;
+}
+
+
+/* TBD: these routes should probably all be host routes */
+
+/* For testing purpose, static route of ENCAP. */
+DEFUN (encap_network,
+       encap_network_cmd,
+       "network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify Route Distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "BGP tag\n"
+       "tag value\n")
+{
+  return bgp_static_set_safi (SAFI_ENCAP, vty, argv[0], argv[1], argv[2], NULL);
+}
+
+/* For testing purpose, static route of ENCAP. */
+DEFUN (no_encap_network,
+       no_encap_network_cmd,
+       "no network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify Route Distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "BGP tag\n"
+       "tag value\n")
+{
+  return bgp_static_unset_safi (SAFI_ENCAP, vty, argv[0], argv[1], argv[2]);
+}
+
+static int
+show_adj_route_encap (struct vty *vty, struct peer *peer, struct prefix_rd *prd)
+{
+  struct bgp *bgp;
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct attr *attr;
+  int rd_header;
+  int header = 1;
+  char v4_header[] = "   Network          Next Hop            Metric LocPrf Weight Path%s";
+
+  bgp = bgp_get_default ();
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_ENCAP]); rn;
+       rn = bgp_route_next (rn))
+    {
+      if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+        continue;
+
+      if ((table = rn->info) != NULL)
+        {
+          rd_header = 1;
+
+          for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+            if ((attr = rm->info) != NULL)
+              {
+                if (header)
+                  {
+                    vty_out (vty, "BGP table version is 0, local router ID is %s%s",
+                             inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                    vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
+                             VTY_NEWLINE);
+                    vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
+                             VTY_NEWLINE, VTY_NEWLINE);
+                    vty_out (vty, v4_header, VTY_NEWLINE);
+                    header = 0;
+                  }
+
+                if (rd_header)
+                  {
+                    u_int16_t type;
+                    struct rd_as rd_as;
+                    struct rd_ip rd_ip;
+                    u_char *pnt;
+
+                    pnt = rn->p.u.val;
+
+                    vty_out (vty, "Route Distinguisher: ");
+
+                    /* Decode RD type. */
+                    type = decode_rd_type (pnt);
+
+                   switch (type) {
+
+                   case RD_TYPE_AS:
+                      decode_rd_as (pnt + 2, &rd_as);
+                      vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
+                     break;
+
+                   case RD_TYPE_IP:
+                      decode_rd_ip (pnt + 2, &rd_ip);
+                      vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+                     break;
+
+                   default:
+                      vty_out (vty, "unknown RD type");
+                   }
+
+
+                    vty_out (vty, "%s", VTY_NEWLINE);
+                    rd_header = 0;
+                  }
+                route_vty_out_tmp (vty, &rm->p, attr, SAFI_ENCAP);
+              }
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+enum bgp_show_type
+{
+  bgp_show_type_normal,
+  bgp_show_type_regexp,
+  bgp_show_type_prefix_list,
+  bgp_show_type_filter_list,
+  bgp_show_type_neighbor,
+  bgp_show_type_cidr_only,
+  bgp_show_type_prefix_longer,
+  bgp_show_type_community_all,
+  bgp_show_type_community,
+  bgp_show_type_community_exact,
+  bgp_show_type_community_list,
+  bgp_show_type_community_list_exact
+};
+
+static int
+bgp_show_encap (
+    struct vty *vty,
+    afi_t afi,
+    struct prefix_rd *prd,
+    enum bgp_show_type type,
+    void *output_arg,
+    int tags)
+{
+  struct bgp *bgp;
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp_info *ri;
+  int rd_header;
+  int header = 1;
+  char v4_header[] = "   Network          Next Hop            Metric LocPrf Weight Path%s";
+  char v4_header_tag[] = "   Network          Next Hop      In tag/Out tag%s";
+
+  unsigned long output_count = 0;
+  unsigned long total_count  = 0;
+
+  bgp = bgp_get_default ();
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ((afi != AFI_IP) && (afi != AFI_IP6)) {
+      vty_out (vty, "Afi %d not supported%s", afi, VTY_NEWLINE);
+      return CMD_WARNING;
+  }
+  
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_ENCAP]); rn; rn = bgp_route_next (rn))
+    {
+      if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+       continue;
+
+      if ((table = rn->info) != NULL)
+       {
+         rd_header = 1;
+
+         for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+           for (ri = rm->info; ri; ri = ri->next)
+             {
+                total_count++;
+               if (type == bgp_show_type_neighbor)
+                 {
+                   union sockunion *su = output_arg;
+
+                   if (ri->peer->su_remote == NULL || ! sockunion_same(ri->peer->su_remote, su))
+                     continue;
+                 }
+               if (header)
+                 {
+                   if (tags)
+                     vty_out (vty, v4_header_tag, VTY_NEWLINE);
+                   else
+                     {
+                       vty_out (vty, "BGP table version is 0, local router ID is %s%s",
+                                inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                       vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
+                                VTY_NEWLINE);
+                       vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
+                                VTY_NEWLINE, VTY_NEWLINE);
+                       vty_out (vty, v4_header, VTY_NEWLINE);
+                     }
+                   header = 0;
+                 }
+
+               if (rd_header)
+                 {
+                   u_int16_t type;
+                   struct rd_as rd_as;
+                   struct rd_ip rd_ip;
+                   u_char *pnt;
+
+                   pnt = rn->p.u.val;
+
+                   /* Decode RD type. */
+                   type = decode_rd_type (pnt);
+
+                   vty_out (vty, "Route Distinguisher: ");
+
+                   switch (type) {
+
+                   case RD_TYPE_AS:
+                     decode_rd_as (pnt + 2, &rd_as);
+                     vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
+                     break;
+
+                   case RD_TYPE_IP:
+                     decode_rd_ip (pnt + 2, &rd_ip);
+                     vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+                     break;
+
+                   default:
+                     vty_out (vty, "Unknown RD type");
+                     break;
+                   }
+
+                   vty_out (vty, "%s", VTY_NEWLINE);             
+                   rd_header = 0;
+                 }
+               if (tags)
+                 route_vty_out_tag (vty, &rm->p, ri, 0, SAFI_ENCAP);
+               else
+                 route_vty_out (vty, &rm->p, ri, 0, SAFI_ENCAP);
+                output_count++;
+             }
+        }
+    }
+
+  if (output_count == 0)
+    {
+        vty_out (vty, "No prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s",
+            VTY_NEWLINE, output_count, total_count, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv4_encap,
+       show_bgp_ipv4_encap_cmd,
+       "show bgp ipv4 encap",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n")
+{
+  return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv6_encap,
+       show_bgp_ipv6_encap_cmd,
+       "show bgp ipv6 encap",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n")
+{
+  return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv4_encap_rd,
+       show_bgp_ipv4_encap_rd_cmd,
+       "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv6_encap_rd,
+       show_bgp_ipv6_encap_rd_cmd,
+       "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Display BGP tags for prefixes\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv4_encap_tags,
+       show_bgp_ipv4_encap_tags_cmd,
+       "show bgp ipv4 encap tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display BGP tags for prefixes\n")
+{
+  return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_normal, NULL,  1);
+}
+
+DEFUN (show_bgp_ipv6_encap_tags,
+       show_bgp_ipv6_encap_tags_cmd,
+       "show bgp ipv6 encap tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display BGP tags for prefixes\n")
+{
+  return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL,  1);
+}
+
+
+DEFUN (show_bgp_ipv4_encap_rd_tags,
+       show_bgp_ipv4_encap_rd_tags_cmd,
+       "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Display BGP tags for prefixes\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 1);
+}
+
+DEFUN (show_bgp_ipv6_encap_rd_tags,
+       show_bgp_ipv6_encap_rd_tags_cmd,
+       "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Display BGP tags for prefixes\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 1);
+}
+
+DEFUN (show_bgp_ipv4_encap_neighbor_routes,
+       show_bgp_ipv4_encap_neighbor_routes_cmd,
+       "show bgp ipv4 encap neighbors A.B.C.D routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  union sockunion su;
+  struct peer *peer;
+
+  if (str2sockunion(argv[0], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv6_encap_neighbor_routes,
+       show_bgp_ipv6_encap_neighbor_routes_cmd,
+       "show bgp ipv6 encap neighbors A.B.C.D routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  union sockunion su;
+  struct peer *peer;
+  
+  if (str2sockunion(argv[0], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv4_encap_rd_neighbor_routes,
+       show_bgp_ipv4_encap_rd_neighbor_routes_cmd,
+       "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  int ret;
+  union sockunion su;
+  struct peer *peer;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (str2sockunion(argv[1], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv6_encap_rd_neighbor_routes,
+       show_bgp_ipv6_encap_rd_neighbor_routes_cmd,
+       "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  int ret;
+  union sockunion su;
+  struct peer *peer;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (str2sockunion(argv[1], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv4_encap_neighbor_advertised_routes,
+       show_bgp_ipv4_encap_neighbor_advertised_routes_cmd,
+       "show bgp ipv4 encap neighbors A.B.C.D advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  union sockunion su;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_encap (vty, peer, NULL);
+}
+
+DEFUN (show_bgp_ipv6_encap_neighbor_advertised_routes,
+       show_bgp_ipv6_encap_neighbor_advertised_routes_cmd,
+       "show bgp ipv6 encap neighbors A.B.C.D advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  union sockunion su;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_encap (vty, peer, NULL);
+}
+
+DEFUN (show_bgp_ipv4_encap_rd_neighbor_advertised_routes,
+       show_bgp_ipv4_encap_rd_neighbor_advertised_routes_cmd,
+       "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  struct prefix_rd prd;
+  union sockunion su;
+
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_encap (vty, peer, &prd);
+}
+
+DEFUN (show_bgp_ipv6_encap_rd_neighbor_advertised_routes,
+       show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd,
+       "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display ENCAP NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  struct prefix_rd prd;
+  union sockunion su;
+
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_encap (vty, peer, &prd);
+}
+
+void
+bgp_encap_init (void)
+{
+  install_element (BGP_ENCAP_NODE, &encap_network_cmd);
+  install_element (BGP_ENCAP_NODE, &no_encap_network_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_neighbor_advertised_routes_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd);
+}
diff --git a/bgpd/bgp_encap.h b/bgpd/bgp_encap.h
new file mode 100644 (file)
index 0000000..7442c73
--- /dev/null
@@ -0,0 +1,29 @@
+/* 
+ *
+ * Copyright 2009-2015, LabN Consulting, L.L.C.
+ *
+ *
+ * This program 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
+ * 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 _QUAGGA_BGP_ENCAP_H
+#define _QUAGGA_BGP_ENCAP_H
+
+extern void bgp_encap_init (void);
+extern int bgp_nlri_parse_encap (struct peer *, struct attr *, struct bgp_nlri *);
+
+#include "bgp_encap_types.h"
+#endif /* _QUAGGA_BGP_ENCAP_H */
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
new file mode 100644 (file)
index 0000000..347b4b3
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "vty.h"
+#include "filter.h"
+
+#include "bgpd.h"
+#include "bgp_attr.h"
+
+#include "bgp_encap_types.h"
+#include "bgp_encap_tlv.h"
+
+/***********************************************************************
+ *                     SUBTLV ENCODE
+ ***********************************************************************/
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_l2tpv3_over_ip(
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                                        total = 4 + st->cookie_length;
+
+    /* sanity check */
+    assert(st->cookie_length <= sizeof(st->cookie));
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->sessionid & 0xff000000) >> 24;
+    *p++ = (st->sessionid & 0xff0000) >> 16;
+    *p++ = (st->sessionid & 0xff00) >> 8;
+    *p++ = (st->sessionid & 0xff);
+    memcpy(p, st->cookie, st->cookie_length);
+    return new;
+}
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_gre(
+    struct bgp_tea_subtlv_encap_gre_key *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                                        total = 4;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->gre_key & 0xff000000) >> 24;
+    *p++ = (st->gre_key & 0xff0000) >> 16;
+    *p++ = (st->gre_key & 0xff00) >> 8;
+    *p++ = (st->gre_key & 0xff);
+    return new;
+}
+
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_pbb(
+    struct bgp_tea_subtlv_encap_pbb    *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                total = 1 + 3 + 6 + 2;  /* flags + isid + madaddr + vid */
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->flag_isid? 0x80: 0) |
+            (st->flag_vid? 0x40: 0) |
+            0;
+    if (st->flag_isid) {
+       *p = (st->isid & 0xff0000) >> 16;
+       *(p+1) = (st->isid & 0xff00) >> 8;
+       *(p+2) = (st->isid & 0xff);
+    }
+    p += 3;
+    memcpy(p, st->macaddr, 6);
+    p += 6;
+    if (st->flag_vid) {
+       *p++ = (st->vid & 0xf00) >> 8;
+       *p++ = st->vid & 0xff;
+    }
+    return new;
+}
+
+/* rfc5512 4.2 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_proto_type(
+    struct bgp_tea_subtlv_proto_type   *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 2;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->proto & 0xff00) >> 8;
+    *p++ = (st->proto & 0xff);
+    return new;
+}
+
+/* rfc5512 4.3 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_color(
+    struct bgp_tea_subtlv_color        *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 8;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_COLOR;
+    new->length = total;
+    p = new->value;
+
+    *p++ = 0x03;                               /* transitive*/
+    *p++ = 0x0b;
+    *p++ = 0;                                  /* reserved */
+    *p++ = 0;                                  /* reserved */
+
+    *p++ = (st->color & 0xff000000) >> 24;
+    *p++ = (st->color & 0xff0000) >> 16;
+    *p++ = (st->color & 0xff00) >> 8;
+    *p++ = (st->color & 0xff);
+
+    return new;
+}
+
+/* rfc 5566 4. */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_ipsec_ta(
+    struct bgp_tea_subtlv_ipsec_ta     *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 2 + st->authenticator_length;
+
+    /* sanity check */
+    assert(st->authenticator_length <= sizeof(st->value));
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->authenticator_type & 0xff00) >> 8;
+    *p++ = st->authenticator_type & 0xff;
+    memcpy(p, st->value, st->authenticator_length);
+    return new;
+}
+
+/* draft-rosen-idr-tunnel-encaps 2.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_remote_endpoint(
+    struct bgp_tea_subtlv_remote_endpoint      *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    
+    int        total = (st->family==AF_INET?8:20);
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT;
+    new->length = total;
+    p = new->value;
+    if (st->family == AF_INET) {
+        memcpy (p, &(st->ip_address.v4.s_addr), 4);
+        p+=4;
+    } else {
+        assert (st->family == AF_INET6);
+        memcpy (p, &(st->ip_address.v6.s6_addr), 16);
+        p+=16;
+    }
+    memcpy (p, &(st->as4), 4);
+    return new;
+}
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+/*
+ * requires "extra" and "last" to be defined in caller
+ */
+#define ENC_SUBTLV(flag, function, field) do {\
+    struct bgp_attr_encap_subtlv       *new;\
+    if (CHECK_FLAG(bet->valid_subtlvs, (flag))) {\
+       new = function(&bet->field);\
+       if (last) {\
+           last->next = new;\
+       } else {\
+           extra->encap_subtlvs = new;\
+       }\
+       last = new;\
+    }\
+} while (0)
+
+void
+bgp_encap_type_l2tpv3overip_to_tlv(
+    struct bgp_encap_type_l2tpv3_over_ip       *bet,   /* input structure */
+    struct attr                                        *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP;
+
+    assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_l2tpv3_over_ip, st_encap);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void
+bgp_encap_type_gre_to_tlv(
+    struct bgp_encap_type_gre  *bet,   /* input structure */
+    struct attr                        *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_GRE;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_gre, st_encap);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void
+bgp_encap_type_ip_in_ip_to_tlv(
+    struct bgp_encap_type_ip_in_ip     *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void
+bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT;
+
+    /* no subtlvs for this type */
+}
+
+void
+bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
+}
+
+void
+bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
+}
+
+void
+bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
+}
+
+void
+bgp_encap_type_pbb_to_tlv(
+    struct bgp_encap_type_pbb  *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+    struct bgp_attr_encap_subtlv       *last;
+
+    /* advance to last subtlv */
+    for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_PBB;
+
+    assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
+    ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_pbb, st_encap);
+}
+
+void
+bgp_encap_type_vxlan_to_tlv(
+    struct bgp_encap_type_vxlan        *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN;
+}
+
+void
+bgp_encap_type_nvgre_to_tlv(
+    struct bgp_encap_type_nvgre        *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_NVGRE;
+}
+
+void
+bgp_encap_type_mpls_to_tlv(
+    struct bgp_encap_type_mpls *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS;
+}
+
+void
+bgp_encap_type_mpls_in_gre_to_tlv(
+    struct bgp_encap_type_mpls_in_gre  *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_GRE;
+}
+
+void
+bgp_encap_type_vxlan_gpe_to_tlv(
+    struct bgp_encap_type_vxlan_gpe    *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN_GPE;
+}
+
+void
+bgp_encap_type_mpls_in_udp_to_tlv(
+    struct bgp_encap_type_mpls_in_udp  *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_UDP;
+}
+
+
+/***********************************************************************
+ *                     SUBTLV DECODE
+ ***********************************************************************/
+/* rfc5512 4.1 */
+static int
+subtlv_decode_encap_l2tpv3_over_ip(
+    struct bgp_attr_encap_subtlv               *subtlv,
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+    if (subtlv->length < 4) {
+       zlog_debug("%s, subtlv length %d is less than 4",
+           __func__, subtlv->length);
+       return -1;
+    }
+
+    st->sessionid = (subtlv->value[0] << 24) |
+                   (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8)  |
+                   subtlv->value[3];
+    st->cookie_length = subtlv->length - 4;
+    if (st->cookie_length > sizeof(st->cookie)) {
+       zlog_debug("%s, subtlv length %d is greater than %d",
+           __func__, st->cookie_length, (int)sizeof(st->cookie));
+       return -1;
+    }
+    memcpy(st->cookie, subtlv->value + 4, st->cookie_length);
+    return 0;
+}
+
+/* rfc5512 4.1 */
+static int
+subtlv_decode_encap_gre(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_encap_gre_key *st)
+{
+    if (subtlv->length != 4) {
+       zlog_debug("%s, subtlv length %d does not equal 4",
+           __func__, subtlv->length);
+       return -1;
+    }
+    st->gre_key = (subtlv->value[0] << 24) |
+                   (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8)  |
+                   subtlv->value[3];
+    return 0;
+}
+
+static int
+subtlv_decode_encap_pbb(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_encap_pbb    *st)
+{
+    if (subtlv->length != 1 + 3 + 6 + 2) {
+       zlog_debug("%s, subtlv length %d does not equal %d",
+           __func__, subtlv->length, 1 + 3 + 6 + 2);
+       return -1;
+    }
+    if (subtlv->value[0] & 0x80) {
+       st->flag_isid = 1;
+       st->isid = (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8) |
+                   subtlv->value[3];
+    }
+    if (subtlv->value[0] & 0x40) {
+       st->flag_vid  = 1;
+       st->vid = ((subtlv->value[10] & 0x0f) << 8) | subtlv->value[11];
+    }
+    memcpy(st->macaddr, subtlv->value + 4, 6);
+    return 0;
+}
+
+/* rfc5512 4.2 */
+static int
+subtlv_decode_proto_type(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_proto_type   *st)
+{
+    if (subtlv->length != 2) {
+       zlog_debug("%s, subtlv length %d does not equal 2",
+           __func__, subtlv->length);
+       return -1;
+    }
+    st->proto = (subtlv->value[0] << 8) | subtlv->value[1];
+    return 0;
+}
+
+/* rfc5512 4.3 */
+static int
+subtlv_decode_color(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_color                *st)
+{
+    if (subtlv->length != 8) {
+       zlog_debug("%s, subtlv length %d does not equal 8",
+           __func__, subtlv->length);
+       return -1;
+    }
+    if ((subtlv->value[0] != 0x03) ||
+       (subtlv->value[1] != 0x0b) ||
+       (subtlv->value[2] != 0)    ||
+       (subtlv->value[3] != 0)) {
+       zlog_debug("%s, subtlv value 1st 4 bytes are not 0x030b0000", __func__);
+       return -1;
+    }
+    st->color = (subtlv->value[4] << 24) |
+               (subtlv->value[5] << 16) |
+               (subtlv->value[6] << 8)  |
+               subtlv->value[7];
+    return 0;
+}
+
+/* rfc 5566 4. */
+static int
+subtlv_decode_ipsec_ta(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_ipsec_ta     *st)
+{
+    st->authenticator_length = subtlv->length - 2;
+    if (st->authenticator_length > sizeof(st->value)) {
+       zlog_debug("%s, authenticator length %d exceeds storage maximum %d",
+           __func__, st->authenticator_length, (int)sizeof(st->value));
+       return -1;
+    }
+    st->authenticator_type = (subtlv->value[0] << 8) | subtlv->value[1];
+    memcpy(st->value, subtlv->value + 2,  st->authenticator_length);
+    return 0;
+}
+
+/* draft-rosen-idr-tunnel-encaps 2.1 */
+static int
+subtlv_decode_remote_endpoint(
+    struct bgp_attr_encap_subtlv         *subtlv,
+    struct bgp_tea_subtlv_remote_endpoint *st)
+{
+   int i;
+   if (subtlv->length != 8 && subtlv->length != 20 ) {
+       zlog_debug("%s, subtlv length %d does not equal 8 or 20",
+           __func__, subtlv->length);
+       return -1;
+    }
+    if (subtlv->length == 8) {
+        st->family = AF_INET;
+        st->ip_address.v4.s_addr = ((subtlv->value[0] << 24) |
+                                    (subtlv->value[1] << 16) |
+                                    (subtlv->value[2] << 8)  |
+                                    subtlv->value[3]);
+    } else {
+        st->family = AF_INET6;
+        memcpy (&(st->ip_address.v6.s6_addr), subtlv->value, 16);
+    }
+    i = subtlv->length - 4;
+    st->as4 = ((subtlv->value[i] << 24) |
+               (subtlv->value[i+1] << 16) |
+               (subtlv->value[i+2] << 8)  |
+               subtlv->value[i+3]);
+    return 0;
+}
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+int
+tlv_to_bgp_encap_type_l2tpv3overip(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_l2tpv3_over_ip       *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_l2tpv3_over_ip(st, &bet->st_encap);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_gre(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_gre          *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_gre(st, &bet->st_encap);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ip_in_ip(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ip_in_ip     *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_attr_encap_subtlv                       *stlv,
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_vxlan(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan                *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_nvgre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_nvgre                *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_gre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_gre  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_vxlan_gpe(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan_gpe    *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_udp(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_udp  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_pbb(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_pbb          *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_pbb(st, &bet->st_encap);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+               rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint);
+               SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
diff --git a/bgpd/bgp_encap_tlv.h b/bgpd/bgp_encap_tlv.h
new file mode 100644 (file)
index 0000000..d94d544
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * 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 _QUAGGA_BGP_ENCAP_TLV_H
+#define _QUAGGA_BGP_ENCAP_TLV_H
+
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+extern void
+bgp_encap_type_l2tpv3overip_to_tlv(
+    struct bgp_encap_type_l2tpv3_over_ip       *bet,
+    struct attr                                        *attr);
+
+extern void
+bgp_encap_type_gre_to_tlv(
+    struct bgp_encap_type_gre  *bet,
+    struct attr                        *attr);
+
+extern void
+bgp_encap_type_ip_in_ip_to_tlv(
+    struct bgp_encap_type_ip_in_ip     *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_pbb_to_tlv(
+    struct bgp_encap_type_pbb  *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_vxlan_to_tlv(
+    struct bgp_encap_type_vxlan        *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_nvgre_to_tlv(
+    struct bgp_encap_type_nvgre        *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_to_tlv(
+    struct bgp_encap_type_mpls *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_in_gre_to_tlv(
+    struct bgp_encap_type_mpls_in_gre  *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_vxlan_gpe_to_tlv(
+    struct bgp_encap_type_vxlan_gpe    *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_in_udp_to_tlv(
+    struct bgp_encap_type_mpls_in_udp  *bet,
+    struct attr                                *attr);
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+extern int
+tlv_to_bgp_encap_type_l2tpv3overip(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_l2tpv3_over_ip       *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_gre(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_gre          *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_ip_in_ip(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ip_in_ip     *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_attr_encap_subtlv                       *stlv,
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet);
+
+extern int
+tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet);
+
+extern int
+tlv_to_bgp_encap_type_vxlan(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan                *bet);
+
+extern int
+tlv_to_bgp_encap_type_nvgre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_nvgre                *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_gre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_gre  *bet);
+
+extern int
+tlv_to_bgp_encap_type_vxlan_gpe(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan_gpe    *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_udp(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_udp  *bet);
+
+extern int
+tlv_to_bgp_encap_type_pbb(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_pbb          *bet);  /* caller-allocated */
+
+#endif /* _QUAGGA_BGP_ENCAP_TLV_H */
diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h
new file mode 100644 (file)
index 0000000..603ff9d
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * 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 _QUAGGA_BGP_ENCAP_TYPES_H
+#define _QUAGGA_BGP_ENCAP_TYPES_H
+
+/* from http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types */
+typedef enum {
+    BGP_ENCAP_TYPE_RESERVED=0,
+    BGP_ENCAP_TYPE_L2TPV3_OVER_IP=1,
+    BGP_ENCAP_TYPE_GRE=2,
+    BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT=3,
+    BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE=4,
+    BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=5,
+    BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=6,
+    BGP_ENCAP_TYPE_IP_IN_IP=7,
+    BGP_ENCAP_TYPE_VXLAN=8,
+    BGP_ENCAP_TYPE_NVGRE=9,
+    BGP_ENCAP_TYPE_MPLS=10,
+    BGP_ENCAP_TYPE_MPLS_IN_GRE=11,
+    BGP_ENCAP_TYPE_VXLAN_GPE=12,
+    BGP_ENCAP_TYPE_MPLS_IN_UDP=13,
+    BGP_ENCAP_TYPE_PBB
+} bgp_encap_types;
+
+typedef enum {
+    BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION=1,
+    BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE=2,
+    BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA=3,
+    BGP_ENCAP_SUBTLV_TYPE_COLOR=4,
+    BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT=6 /* speculative, IANA assignment TBD */
+} bgp_encap_subtlv_types;
+
+/*
+ * Tunnel Encapsulation Attribute subtlvs
+ */
+struct bgp_tea_subtlv_encap_l2tpv3_over_ip {
+    uint32_t   sessionid;
+    uint8_t    cookie_length;
+    uint8_t    cookie[8];
+};
+
+struct bgp_tea_subtlv_encap_gre_key {
+    uint32_t   gre_key;
+};
+
+struct bgp_tea_subtlv_encap_pbb {
+    uint32_t   flag_isid:1;
+    uint32_t   flag_vid:1;
+    uint32_t   isid:24;
+    uint16_t   vid:12;
+    uint8_t    macaddr[6];
+};
+
+struct bgp_tea_subtlv_proto_type {
+    uint16_t   proto;                  /* ether-type */
+};
+
+struct bgp_tea_subtlv_color {
+    uint32_t   color;
+};
+
+/* per draft-rosen-idr-tunnel-encaps */
+struct bgp_tea_subtlv_remote_endpoint {
+    u_char family;               /* IPv4 or IPv6 */
+    union {
+        struct in_addr  v4;
+        struct in6_addr v6;
+    } ip_address;
+    as_t               as4;     /* always 4 bytes */
+};
+
+/*
+ * This is the length of the value part of the ipsec tunnel authenticator
+ * subtlv. Currently we only support the length for authenticator type 1.
+ */
+#define BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE 20
+
+struct bgp_tea_subtlv_ipsec_ta {
+    uint16_t   authenticator_type;     /* only type 1 is supported so far */
+    uint8_t    authenticator_length;   /* octets in value field */
+    uint8_t    value[BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE];
+};
+
+/*
+ * Subtlv valid flags
+ * TBD change names to add "VALID"
+ */
+#define BGP_TEA_SUBTLV_ENCAP           0x00000001
+#define BGP_TEA_SUBTLV_PROTO_TYPE      0x00000002
+#define BGP_TEA_SUBTLV_COLOR           0x00000004
+#define BGP_TEA_SUBTLV_IPSEC_TA                0x00000008
+#define BGP_TEA_SUBTLV_REMOTE_ENDPOINT 0x00000010
+
+#define CHECK_SUBTLV_FLAG(ptr, flag)  CHECK_FLAG((ptr)->valid_subtlvs, (flag))
+#define SET_SUBTLV_FLAG(ptr, flag)      SET_FLAG((ptr)->valid_subtlvs, (flag))
+#define UNSET_SUBTLV_FLAG(ptr, flag)  UNSET_FLAG((ptr)->valid_subtlvs, (flag))
+
+/*
+ * Tunnel Type-specific APIs
+ */
+struct bgp_encap_type_reserved {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_l2tpv3_over_ip {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip st_encap;
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_gre {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_encap_gre_key                st_encap;       /* optional */
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_transmit_tunnel_endpoint {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_ipsec_in_tunnel_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+};
+
+struct bgp_encap_type_vxlan {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_nvgre {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_gre {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_vxlan_gpe {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_udp {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_pbb {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_remote_endpoint       st_endpoint;    /* optional */
+    struct bgp_tea_subtlv_encap_pbb            st_encap;
+};
+
+#endif /* _QUAGGA_BGP_ENCAP_TYPES_H */
diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c
new file mode 100644 (file)
index 0000000..ab1e504
--- /dev/null
@@ -0,0 +1,719 @@
+/* AS path filter list.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "buffer.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_filter.h"
+
+/* List of AS filter list. */
+struct as_list_list
+{
+  struct as_list *head;
+  struct as_list *tail;
+};
+
+/* AS path filter master. */
+struct as_list_master
+{
+  /* List of access_list which name is number. */
+  struct as_list_list num;
+
+  /* List of access_list which name is string. */
+  struct as_list_list str;
+
+  /* Hook function which is executed when new access_list is added. */
+  void (*add_hook) (void);
+
+  /* Hook function which is executed when access_list is deleted. */
+  void (*delete_hook) (void);
+};
+
+/* Element of AS path filter. */
+struct as_filter
+{
+  struct as_filter *next;
+  struct as_filter *prev;
+
+  enum as_filter_type type;
+
+  regex_t *reg;
+  char *reg_str;
+};
+
+/* AS path filter list. */
+struct as_list
+{
+  char *name;
+
+  enum access_type type;
+
+  struct as_list *next;
+  struct as_list *prev;
+
+  struct as_filter *head;
+  struct as_filter *tail;
+};
+
+/* ip as-path access-list 10 permit AS1. */
+
+static struct as_list_master as_list_master =
+{
+  {NULL, NULL},
+  {NULL, NULL},
+  NULL,
+  NULL
+};
+
+/* Allocate new AS filter. */
+static struct as_filter *
+as_filter_new (void)
+{
+  return XCALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter));
+}
+
+/* Free allocated AS filter. */
+static void
+as_filter_free (struct as_filter *asfilter)
+{
+  if (asfilter->reg)
+    bgp_regex_free (asfilter->reg);
+  if (asfilter->reg_str)
+    XFREE (MTYPE_AS_FILTER_STR, asfilter->reg_str);
+  XFREE (MTYPE_AS_FILTER, asfilter);
+}
+
+/* Make new AS filter. */
+static struct as_filter *
+as_filter_make (regex_t *reg, const char *reg_str, enum as_filter_type type)
+{
+  struct as_filter *asfilter;
+
+  asfilter = as_filter_new ();
+  asfilter->reg = reg;
+  asfilter->type = type;
+  asfilter->reg_str = XSTRDUP (MTYPE_AS_FILTER_STR, reg_str);
+
+  return asfilter;
+}
+
+static struct as_filter *
+as_filter_lookup (struct as_list *aslist, const char *reg_str,
+                 enum as_filter_type type)
+{
+  struct as_filter *asfilter;
+
+  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+    if (strcmp (reg_str, asfilter->reg_str) == 0)
+      return asfilter;
+  return NULL;
+}
+
+static void
+as_list_filter_add (struct as_list *aslist, struct as_filter *asfilter)
+{
+  asfilter->next = NULL;
+  asfilter->prev = aslist->tail;
+
+  if (aslist->tail)
+    aslist->tail->next = asfilter;
+  else
+    aslist->head = asfilter;
+  aslist->tail = asfilter;
+}
+
+/* Lookup as_list from list of as_list by name. */
+struct as_list *
+as_list_lookup (const char *name)
+{
+  struct as_list *aslist;
+
+  if (name == NULL)
+    return NULL;
+
+  for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
+    if (strcmp (aslist->name, name) == 0)
+      return aslist;
+
+  for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+    if (strcmp (aslist->name, name) == 0)
+      return aslist;
+
+  return NULL;
+}
+
+static struct as_list *
+as_list_new (void)
+{
+  return XCALLOC (MTYPE_AS_LIST, sizeof (struct as_list));
+}
+
+static void
+as_list_free (struct as_list *aslist)
+{
+  if (aslist->name)
+    {
+      free (aslist->name);
+      aslist->name = NULL;
+    }
+  XFREE (MTYPE_AS_LIST, aslist);
+}
+
+/* Insert new AS list to list of as_list.  Each as_list is sorted by
+   the name. */
+static struct as_list *
+as_list_insert (const char *name)
+{
+  size_t i;
+  long number;
+  struct as_list *aslist;
+  struct as_list *point;
+  struct as_list_list *list;
+
+  /* Allocate new access_list and copy given name. */
+  aslist = as_list_new ();
+  aslist->name = strdup (name);
+  assert (aslist->name);
+
+  /* If name is made by all digit character.  We treat it as
+     number. */
+  for (number = 0, i = 0; i < strlen (name); i++)
+    {
+      if (isdigit ((int) name[i]))
+       number = (number * 10) + (name[i] - '0');
+      else
+       break;
+    }
+
+  /* In case of name is all digit character */
+  if (i == strlen (name))
+    {
+      aslist->type = ACCESS_TYPE_NUMBER;
+
+      /* Set access_list to number list. */
+      list = &as_list_master.num;
+
+      for (point = list->head; point; point = point->next)
+       if (atol (point->name) >= number)
+         break;
+    }
+  else
+    {
+      aslist->type = ACCESS_TYPE_STRING;
+
+      /* Set access_list to string list. */
+      list = &as_list_master.str;
+  
+      /* Set point to insertion point. */
+      for (point = list->head; point; point = point->next)
+       if (strcmp (point->name, name) >= 0)
+         break;
+    }
+
+  /* In case of this is the first element of master. */
+  if (list->head == NULL)
+    {
+      list->head = list->tail = aslist;
+      return aslist;
+    }
+
+  /* In case of insertion is made at the tail of access_list. */
+  if (point == NULL)
+    {
+      aslist->prev = list->tail;
+      list->tail->next = aslist;
+      list->tail = aslist;
+      return aslist;
+    }
+
+  /* In case of insertion is made at the head of access_list. */
+  if (point == list->head)
+    {
+      aslist->next = list->head;
+      list->head->prev = aslist;
+      list->head = aslist;
+      return aslist;
+    }
+
+  /* Insertion is made at middle of the access_list. */
+  aslist->next = point;
+  aslist->prev = point->prev;
+
+  if (point->prev)
+    point->prev->next = aslist;
+  point->prev = aslist;
+
+  return aslist;
+}
+
+static struct as_list *
+as_list_get (const char *name)
+{
+  struct as_list *aslist;
+
+  aslist = as_list_lookup (name);
+  if (aslist == NULL)
+    {
+      aslist = as_list_insert (name);
+
+      /* Run hook function. */
+      if (as_list_master.add_hook)
+       (*as_list_master.add_hook) ();
+    }
+
+  return aslist;
+}
+
+static const char *
+filter_type_str (enum as_filter_type type)
+{
+  switch (type)
+    {
+    case AS_FILTER_PERMIT:
+      return "permit";
+    case AS_FILTER_DENY:
+      return "deny";
+    default:
+      return "";
+    }
+}
+
+static void
+as_list_delete (struct as_list *aslist)
+{
+  struct as_list_list *list;
+  struct as_filter *filter, *next;
+
+  for (filter = aslist->head; filter; filter = next)
+    {
+      next = filter->next;
+      as_filter_free (filter);
+    }
+
+  if (aslist->type == ACCESS_TYPE_NUMBER)
+    list = &as_list_master.num;
+  else
+    list = &as_list_master.str;
+
+  if (aslist->next)
+    aslist->next->prev = aslist->prev;
+  else
+    list->tail = aslist->prev;
+
+  if (aslist->prev)
+    aslist->prev->next = aslist->next;
+  else
+    list->head = aslist->next;
+
+  as_list_free (aslist);
+}
+
+static int
+as_list_empty (struct as_list *aslist)
+{
+  if (aslist->head == NULL && aslist->tail == NULL)
+    return 1;
+  else
+    return 0;
+}
+
+static void
+as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter)
+{
+  if (asfilter->next)
+    asfilter->next->prev = asfilter->prev;
+  else
+    aslist->tail = asfilter->prev;
+
+  if (asfilter->prev)
+    asfilter->prev->next = asfilter->next;
+  else
+    aslist->head = asfilter->next;
+
+  as_filter_free (asfilter);
+
+  /* If access_list becomes empty delete it from access_master. */
+  if (as_list_empty (aslist))
+    as_list_delete (aslist);
+
+  /* Run hook function. */
+  if (as_list_master.delete_hook)
+    (*as_list_master.delete_hook) ();
+}
+
+static int
+as_filter_match (struct as_filter *asfilter, struct aspath *aspath)
+{
+  if (bgp_regexec (asfilter->reg, aspath) != REG_NOMATCH)
+    return 1;
+  return 0;
+}
+
+/* Apply AS path filter to AS. */
+enum as_filter_type
+as_list_apply (struct as_list *aslist, void *object)
+{
+  struct as_filter *asfilter;
+  struct aspath *aspath;
+
+  aspath = (struct aspath *) object;
+
+  if (aslist == NULL)
+    return AS_FILTER_DENY;
+
+  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+    {
+      if (as_filter_match (asfilter, aspath))
+       return asfilter->type;
+    }
+  return AS_FILTER_DENY;
+}
+
+/* Add hook function. */
+void
+as_list_add_hook (void (*func) (void))
+{
+  as_list_master.add_hook = func;
+}
+
+/* Delete hook function. */
+void
+as_list_delete_hook (void (*func) (void))
+{
+  as_list_master.delete_hook = func;
+}
+
+static int
+as_list_dup_check (struct as_list *aslist, struct as_filter *new)
+{
+  struct as_filter *asfilter;
+
+  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+    {
+      if (asfilter->type == new->type
+         && strcmp (asfilter->reg_str, new->reg_str) == 0)
+       return 1;
+    }
+  return 0;
+}
+
+DEFUN (ip_as_path, ip_as_path_cmd,
+       "ip as-path access-list WORD (deny|permit) .LINE",
+       IP_STR
+       "BGP autonomous system path filter\n"
+       "Specify an access list name\n"
+       "Regular expression access list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  enum as_filter_type type;
+  struct as_filter *asfilter;
+  struct as_list *aslist;
+  regex_t *regex;
+  char *regstr;
+
+  /* Check the filter type. */
+  if (strncmp (argv[1], "p", 1) == 0)
+    type = AS_FILTER_PERMIT;
+  else if (strncmp (argv[1], "d", 1) == 0)
+    type = AS_FILTER_DENY;
+  else
+    {
+      vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check AS path regex. */
+  regstr = argv_concat(argv, argc, 2);
+
+  regex = bgp_regcomp (regstr);
+  if (!regex)
+    {
+      XFREE (MTYPE_TMP, regstr);
+      vty_out (vty, "can't compile regexp %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  asfilter = as_filter_make (regex, regstr, type);
+  
+  XFREE (MTYPE_TMP, regstr);
+
+  /* Install new filter to the access_list. */
+  aslist = as_list_get (argv[0]);
+
+  /* Duplicate insertion check. */;
+  if (as_list_dup_check (aslist, asfilter))
+    as_filter_free (asfilter);
+  else
+    as_list_filter_add (aslist, asfilter);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_as_path,
+       no_ip_as_path_cmd,
+       "no ip as-path access-list WORD (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       "BGP autonomous system path filter\n"
+       "Specify an access list name\n"
+       "Regular expression access list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  enum as_filter_type type;
+  struct as_filter *asfilter;
+  struct as_list *aslist;
+  char *regstr;
+  regex_t *regex;
+
+  /* Lookup AS list from AS path list. */
+  aslist = as_list_lookup (argv[0]);
+  if (aslist == NULL)
+    {
+      vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check the filter type. */
+  if (strncmp (argv[1], "p", 1) == 0)
+    type = AS_FILTER_PERMIT;
+  else if (strncmp (argv[1], "d", 1) == 0)
+    type = AS_FILTER_DENY;
+  else
+    {
+      vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  /* Compile AS path. */
+  regstr = argv_concat(argv, argc, 2);
+
+  regex = bgp_regcomp (regstr);
+  if (!regex)
+    {
+      XFREE (MTYPE_TMP, regstr);
+      vty_out (vty, "can't compile regexp %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Lookup asfilter. */
+  asfilter = as_filter_lookup (aslist, regstr, type);
+
+  XFREE (MTYPE_TMP, regstr);
+  bgp_regex_free (regex);
+
+  if (asfilter == NULL)
+    {
+      vty_out (vty, "%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  as_list_filter_delete (aslist, asfilter);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_as_path_all,
+       no_ip_as_path_all_cmd,
+       "no ip as-path access-list WORD",
+       NO_STR
+       IP_STR
+       "BGP autonomous system path filter\n"
+       "Specify an access list name\n"
+       "Regular expression access list name\n")
+{
+  struct as_list *aslist;
+
+  aslist = as_list_lookup (argv[0]);
+  if (aslist == NULL)
+    {
+      vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  as_list_delete (aslist);
+
+  /* Run hook function. */
+  if (as_list_master.delete_hook)
+    (*as_list_master.delete_hook) ();
+
+  return CMD_SUCCESS;
+}
+
+static void
+as_list_show (struct vty *vty, struct as_list *aslist)
+{
+  struct as_filter *asfilter;
+
+  vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE);
+
+  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+    {
+      vty_out (vty, "    %s %s%s", filter_type_str (asfilter->type),
+              asfilter->reg_str, VTY_NEWLINE);
+    }
+}
+
+static void
+as_list_show_all (struct vty *vty)
+{
+  struct as_list *aslist;
+  struct as_filter *asfilter;
+
+  for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
+    {
+      vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE);
+
+      for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+       {
+         vty_out (vty, "    %s %s%s", filter_type_str (asfilter->type),
+                  asfilter->reg_str, VTY_NEWLINE);
+       }
+    }
+
+  for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+    {
+      vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE);
+
+      for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+       {
+         vty_out (vty, "    %s %s%s", filter_type_str (asfilter->type),
+                  asfilter->reg_str, VTY_NEWLINE);
+       }
+    }
+}
+
+DEFUN (show_ip_as_path_access_list,
+       show_ip_as_path_access_list_cmd,
+       "show ip as-path-access-list WORD",
+       SHOW_STR
+       IP_STR
+       "List AS path access lists\n"
+       "AS path access list name\n")
+{
+  struct as_list *aslist;
+
+  aslist = as_list_lookup (argv[0]);
+  if (aslist)
+    as_list_show (vty, aslist);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_as_path_access_list_all,
+       show_ip_as_path_access_list_all_cmd,
+       "show ip as-path-access-list",
+       SHOW_STR
+       IP_STR
+       "List AS path access lists\n")
+{
+  as_list_show_all (vty);
+  return CMD_SUCCESS;
+}
+
+static int
+config_write_as_list (struct vty *vty)
+{
+  struct as_list *aslist;
+  struct as_filter *asfilter;
+  int write = 0;
+
+  for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
+    for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+      {
+       vty_out (vty, "ip as-path access-list %s %s %s%s",
+                aslist->name, filter_type_str (asfilter->type), 
+                asfilter->reg_str,
+                VTY_NEWLINE);
+       write++;
+      }
+
+  for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+    for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+      {
+       vty_out (vty, "ip as-path access-list %s %s %s%s",
+                aslist->name, filter_type_str (asfilter->type), 
+                asfilter->reg_str,
+                VTY_NEWLINE);
+       write++;
+      }
+  return write;
+}
+
+static struct cmd_node as_list_node =
+{
+  AS_LIST_NODE,
+  "",
+  1
+};
+
+/* Register functions. */
+void
+bgp_filter_init (void)
+{
+  install_node (&as_list_node, config_write_as_list);
+
+  install_element (CONFIG_NODE, &ip_as_path_cmd);
+  install_element (CONFIG_NODE, &no_ip_as_path_cmd);
+  install_element (CONFIG_NODE, &no_ip_as_path_all_cmd);
+
+  install_element (VIEW_NODE, &show_ip_as_path_access_list_cmd);
+  install_element (VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
+}
+
+void
+bgp_filter_reset (void)
+{
+  struct as_list *aslist;
+  struct as_list *next;
+
+  for (aslist = as_list_master.num.head; aslist; aslist = next)
+    {
+      next = aslist->next;
+      as_list_delete (aslist);
+    }
+
+  for (aslist = as_list_master.str.head; aslist; aslist = next)
+    {
+      next = aslist->next;
+      as_list_delete (aslist);
+    }
+
+  assert (as_list_master.num.head == NULL);
+  assert (as_list_master.num.tail == NULL);
+
+  assert (as_list_master.str.head == NULL);
+  assert (as_list_master.str.tail == NULL);
+}
diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h
new file mode 100644 (file)
index 0000000..c1da904
--- /dev/null
@@ -0,0 +1,39 @@
+/* AS path filter list.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_FILTER_H
+#define _QUAGGA_BGP_FILTER_H
+
+enum as_filter_type
+{
+  AS_FILTER_DENY,
+  AS_FILTER_PERMIT
+};
+
+extern void bgp_filter_init (void);
+extern void bgp_filter_reset (void);
+
+extern enum as_filter_type as_list_apply (struct as_list *, void *);
+
+extern struct as_list *as_list_lookup (const char *);
+extern void as_list_add_hook (void (*func) (void));
+extern void as_list_delete_hook (void (*func) (void));
+
+#endif /* _QUAGGA_BGP_FILTER_H */
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
new file mode 100644 (file)
index 0000000..4198a8e
--- /dev/null
@@ -0,0 +1,1205 @@
+/* BGP-4 Finite State Machine   
+   From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+   Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "thread.h"
+#include "log.h"
+#include "stream.h"
+#include "memory.h"
+#include "plist.h"
+#include "workqueue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_nht.h"
+#ifdef HAVE_SNMP
+#include "bgpd/bgp_snmp.h"
+#endif /* HAVE_SNMP */
+
+/* BGP FSM (finite state machine) has three types of functions.  Type
+   one is thread functions.  Type two is event functions.  Type three
+   is FSM functions.  Timer functions are set by bgp_timer_set
+   function. */
+
+/* BGP event function. */
+int bgp_event (struct thread *);
+
+/* BGP thread functions. */
+static int bgp_start_timer (struct thread *);
+static int bgp_connect_timer (struct thread *);
+static int bgp_holdtime_timer (struct thread *);
+static int bgp_keepalive_timer (struct thread *);
+
+/* BGP FSM functions. */
+static int bgp_start (struct peer *);
+
+/* BGP start timer jitter. */
+static int
+bgp_start_jitter (int time)
+{
+  return ((random () % (time + 1)) - (time / 2));
+}
+
+/* Check if suppress start/restart of sessions to peer. */
+#define BGP_PEER_START_SUPPRESSED(P) \
+  (CHECK_FLAG ((P)->flags, PEER_FLAG_SHUTDOWN) \
+   || CHECK_FLAG ((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+
+/* Hook function called after bgp event is occered.  And vty's
+   neighbor command invoke this function after making neighbor
+   structure. */
+void
+bgp_timer_set (struct peer *peer)
+{
+  int jitter = 0;
+
+  switch (peer->status)
+    {
+    case Idle:
+      /* First entry point of peer's finite state machine.  In Idle
+        status start timer is on unless peer is shutdown or peer is
+        inactive.  All other timer must be turned off */
+      if (BGP_PEER_START_SUPPRESSED (peer) || ! peer_active (peer))
+       {
+         BGP_TIMER_OFF (peer->t_start);
+       }
+      else
+       {
+         jitter = bgp_start_jitter (peer->v_start);
+         BGP_TIMER_ON (peer->t_start, bgp_start_timer,
+                       peer->v_start + jitter);
+       }
+      BGP_TIMER_OFF (peer->t_connect);
+      BGP_TIMER_OFF (peer->t_holdtime);
+      BGP_TIMER_OFF (peer->t_keepalive);
+      BGP_TIMER_OFF (peer->t_routeadv);
+      break;
+
+    case Connect:
+      /* After start timer is expired, the peer moves to Connect
+         status.  Make sure start timer is off and connect timer is
+         on. */
+      BGP_TIMER_OFF (peer->t_start);
+      BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
+      BGP_TIMER_OFF (peer->t_holdtime);
+      BGP_TIMER_OFF (peer->t_keepalive);
+      BGP_TIMER_OFF (peer->t_routeadv);
+      break;
+
+    case Active:
+      /* Active is waiting connection from remote peer.  And if
+         connect timer is expired, change status to Connect. */
+      BGP_TIMER_OFF (peer->t_start);
+      /* If peer is passive mode, do not set connect timer. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)
+         || CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+       {
+         BGP_TIMER_OFF (peer->t_connect);
+       }
+      else
+       {
+         BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
+       }
+      BGP_TIMER_OFF (peer->t_holdtime);
+      BGP_TIMER_OFF (peer->t_keepalive);
+      BGP_TIMER_OFF (peer->t_routeadv);
+      break;
+
+    case OpenSent:
+      /* OpenSent status. */
+      BGP_TIMER_OFF (peer->t_start);
+      BGP_TIMER_OFF (peer->t_connect);
+      if (peer->v_holdtime != 0)
+       {
+         BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, 
+                       peer->v_holdtime);
+       }
+      else
+       {
+         BGP_TIMER_OFF (peer->t_holdtime);
+       }
+      BGP_TIMER_OFF (peer->t_keepalive);
+      BGP_TIMER_OFF (peer->t_routeadv);
+      break;
+
+    case OpenConfirm:
+      /* OpenConfirm status. */
+      BGP_TIMER_OFF (peer->t_start);
+      BGP_TIMER_OFF (peer->t_connect);
+
+      /* If the negotiated Hold Time value is zero, then the Hold Time
+         timer and KeepAlive timers are not started. */
+      if (peer->v_holdtime == 0)
+       {
+         BGP_TIMER_OFF (peer->t_holdtime);
+         BGP_TIMER_OFF (peer->t_keepalive);
+       }
+      else
+       {
+         BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
+                       peer->v_holdtime);
+         BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, 
+                       peer->v_keepalive);
+       }
+      BGP_TIMER_OFF (peer->t_routeadv);
+      break;
+
+    case Established:
+      /* In Established status start and connect timer is turned
+         off. */
+      BGP_TIMER_OFF (peer->t_start);
+      BGP_TIMER_OFF (peer->t_connect);
+
+      /* Same as OpenConfirm, if holdtime is zero then both holdtime
+         and keepalive must be turned off. */
+      if (peer->v_holdtime == 0)
+       {
+         BGP_TIMER_OFF (peer->t_holdtime);
+         BGP_TIMER_OFF (peer->t_keepalive);
+       }
+      else
+       {
+         BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
+                       peer->v_holdtime);
+         BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer,
+                       peer->v_keepalive);
+       }
+      break;
+    case Deleted:
+      BGP_TIMER_OFF (peer->t_gr_restart);
+      BGP_TIMER_OFF (peer->t_gr_stale);
+      BGP_TIMER_OFF (peer->t_pmax_restart);
+    case Clearing:
+      BGP_TIMER_OFF (peer->t_start);
+      BGP_TIMER_OFF (peer->t_connect);
+      BGP_TIMER_OFF (peer->t_holdtime);
+      BGP_TIMER_OFF (peer->t_keepalive);
+      BGP_TIMER_OFF (peer->t_routeadv);
+    }
+}
+
+/* BGP start timer.  This function set BGP_Start event to thread value
+   and process event. */
+static int
+bgp_start_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_start = NULL;
+
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG,
+         "%s [FSM] Timer (start timer expire).", peer->host);
+
+  THREAD_VAL (thread) = BGP_Start;
+  bgp_event (thread);  /* bgp_event unlocks peer */
+
+  return 0;
+}
+
+/* BGP connect retry timer. */
+static int
+bgp_connect_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_connect = NULL;
+
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)",
+         peer->host);
+
+  THREAD_VAL (thread) = ConnectRetry_timer_expired;
+  bgp_event (thread); /* bgp_event unlocks peer */
+
+  return 0;
+}
+
+/* BGP holdtime timer. */
+static int
+bgp_holdtime_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_holdtime = NULL;
+
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG,
+         "%s [FSM] Timer (holdtime timer expire)",
+         peer->host);
+
+  THREAD_VAL (thread) = Hold_Timer_expired;
+  bgp_event (thread); /* bgp_event unlocks peer */
+
+  return 0;
+}
+
+/* BGP keepalive fire ! */
+static int
+bgp_keepalive_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_keepalive = NULL;
+
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG,
+         "%s [FSM] Timer (keepalive timer expire)",
+         peer->host);
+
+  THREAD_VAL (thread) = KeepAlive_timer_expired;
+  bgp_event (thread); /* bgp_event unlocks peer */
+
+  return 0;
+}
+
+static int
+bgp_routeadv_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_routeadv = NULL;
+
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG,
+         "%s [FSM] Timer (routeadv timer expire)",
+         peer->host);
+
+  peer->synctime = bgp_clock ();
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+
+  BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
+               peer->v_routeadv);
+
+  return 0;
+}
+
+/* BGP Peer Down Cause */
+const char *peer_down_str[] =
+{
+  "",
+  "Router ID changed",
+  "Remote AS changed",
+  "Local AS change",
+  "Cluster ID changed",
+  "Confederation identifier changed",
+  "Confederation peer changed",
+  "RR client config change",
+  "RS client config change",
+  "Update source change",
+  "Address family activated",
+  "Admin. shutdown",
+  "User reset",
+  "BGP Notification received",
+  "BGP Notification send",
+  "Peer closed the session",
+  "Neighbor deleted",
+  "Peer-group add member",
+  "Peer-group delete member",
+  "Capability changed",
+  "Passive config change",
+  "Multihop config change",
+  "NSF peer closed the session"
+};
+
+static int
+bgp_graceful_restart_timer_expire (struct thread *thread)
+{
+  struct peer *peer;
+  afi_t afi;
+  safi_t safi;
+
+  peer = THREAD_ARG (thread);
+  peer->t_gr_restart = NULL;
+
+  /* NSF delete stale route */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
+      if (peer->nsf[afi][safi])
+       bgp_clear_stale_route (peer, afi, safi);
+
+  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+  BGP_TIMER_OFF (peer->t_gr_stale);
+
+  if (BGP_DEBUG (events, EVENTS))
+    {
+      zlog_debug ("%s graceful restart timer expired", peer->host);
+      zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+    }
+
+  bgp_timer_set (peer);
+
+  return 0;
+}
+
+static int
+bgp_graceful_stale_timer_expire (struct thread *thread)
+{
+  struct peer *peer;
+  afi_t afi;
+  safi_t safi;
+
+  peer = THREAD_ARG (thread);
+  peer->t_gr_stale = NULL;
+
+  if (BGP_DEBUG (events, EVENTS))
+    zlog_debug ("%s graceful restart stalepath timer expired", peer->host);
+
+  /* NSF delete stale route */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
+      if (peer->nsf[afi][safi])
+       bgp_clear_stale_route (peer, afi, safi);
+
+  return 0;
+}
+
+/* Called after event occured, this function change status and reset
+   read/write and timer thread. */
+void
+bgp_fsm_change_status (struct peer *peer, int status)
+{
+  bgp_dump_state (peer, peer->status, status);
+
+  /* Transition into Clearing or Deleted must /always/ clear all routes.. 
+   * (and must do so before actually changing into Deleted..
+   */
+  if (status >= Clearing)
+    {
+      bgp_clear_route_all (peer);
+
+      /* If no route was queued for the clear-node processing, generate the
+       * completion event here. This is needed because if there are no routes
+       * to trigger the background clear-node thread, the event won't get
+       * generated and the peer would be stuck in Clearing. Note that this
+       * event is for the peer and helps the peer transition out of Clearing
+       * state; it should not be generated per (AFI,SAFI). The event is
+       * directly posted here without calling clear_node_complete() as we
+       * shouldn't do an extra unlock. This event will get processed after
+       * the state change that happens below, so peer will be in Clearing
+       * (or Deleted).
+       */
+      if (!work_queue_is_scheduled (peer->clear_node_queue))
+        BGP_EVENT_ADD (peer, Clearing_Completed);
+    }
+  
+  /* Preserve old status and change into new status. */
+  peer->ostatus = peer->status;
+  peer->status = status;
+  
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s went from %s to %s",
+               peer->host,
+               LOOKUP (bgp_status_msg, peer->ostatus),
+               LOOKUP (bgp_status_msg, peer->status));
+}
+
+/* Flush the event queue and ensure the peer is shut down */
+static int
+bgp_clearing_completed (struct peer *peer)
+{
+  int rc = bgp_stop(peer);
+  BGP_EVENT_FLUSH (peer);
+
+  return rc;
+}
+
+/* Administrative BGP peer stop event. */
+/* May be called multiple times for the same peer */
+int
+bgp_stop (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+  char orf_name[BUFSIZ];
+
+  /* Can't do this in Clearing; events are used for state transitions */
+  if (peer->status != Clearing)
+    {
+      /* Delete all existing events of the peer */
+      BGP_EVENT_FLUSH (peer);
+    }
+
+  /* Increment Dropped count. */
+  if (peer->status == Established)
+    {
+      peer->dropped++;
+
+      /* bgp log-neighbor-changes of neighbor Down */
+      if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+       zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host,
+                   peer_down_str [(int) peer->last_reset]);
+
+      /* graceful restart */
+      if (peer->t_gr_stale)
+       {
+         BGP_TIMER_OFF (peer->t_gr_stale);
+         if (BGP_DEBUG (events, EVENTS))
+           zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+       }
+      if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+       {
+         if (BGP_DEBUG (events, EVENTS))
+           {
+             zlog_debug ("%s graceful restart timer started for %d sec",
+                         peer->host, peer->v_gr_restart);
+             zlog_debug ("%s graceful restart stalepath timer started for %d sec",
+                         peer->host, peer->bgp->stalepath_time);
+           }
+         BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire,
+                       peer->v_gr_restart);
+         BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire,
+                       peer->bgp->stalepath_time);
+       }
+      else
+       {
+         UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+
+         for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+           for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
+             peer->nsf[afi][safi] = 0;
+       }
+
+      /* set last reset time */
+      peer->resettime = peer->uptime = bgp_clock ();
+
+#ifdef HAVE_SNMP
+      bgpTrapBackwardTransition (peer);
+#endif /* HAVE_SNMP */
+
+      /* Reset peer synctime */
+      peer->synctime = 0;
+    }
+  
+  /* Stop read and write threads when exists. */
+  BGP_READ_OFF (peer->t_read);
+  BGP_WRITE_OFF (peer->t_write);
+
+  /* Stop all timers. */
+  BGP_TIMER_OFF (peer->t_start);
+  BGP_TIMER_OFF (peer->t_connect);
+  BGP_TIMER_OFF (peer->t_holdtime);
+  BGP_TIMER_OFF (peer->t_keepalive);
+  BGP_TIMER_OFF (peer->t_routeadv);
+
+  /* Stream reset. */
+  peer->packet_size = 0;
+
+  /* Clear input and output buffer.  */
+  if (peer->ibuf)
+    stream_reset (peer->ibuf);
+  if (peer->work)
+    stream_reset (peer->work);
+  if (peer->obuf)
+    stream_fifo_clean (peer->obuf);
+
+  /* Close of file descriptor. */
+  if (peer->fd >= 0)
+    {
+      close (peer->fd);
+      peer->fd = -1;
+    }
+
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+      {
+        /* Reset all negotiated variables */
+        peer->afc_nego[afi][safi] = 0;
+        peer->afc_adv[afi][safi] = 0;
+        peer->afc_recv[afi][safi] = 0;
+
+       /* peer address family capability flags*/
+       peer->af_cap[afi][safi] = 0;
+
+       /* peer address family status flags*/
+       peer->af_sflags[afi][safi] = 0;
+
+       /* Received ORF prefix-filter */
+       peer->orf_plist[afi][safi] = NULL;
+
+        /* ORF received prefix-filter pnt */
+        sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
+        prefix_bgp_orf_remove_all (afi, orf_name);
+      }
+
+  /* Reset keepalive and holdtime */
+  if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+    {
+      peer->v_keepalive = peer->keepalive;
+      peer->v_holdtime = peer->holdtime;
+    }
+  else
+    {
+      peer->v_keepalive = peer->bgp->default_keepalive;
+      peer->v_holdtime = peer->bgp->default_holdtime;
+    }
+
+  peer->update_time = 0;
+
+  /* Until we are sure that there is no problem about prefix count
+     this should be commented out.*/
+#if 0
+  /* Reset prefix count */
+  peer->pcount[AFI_IP][SAFI_UNICAST] = 0;
+  peer->pcount[AFI_IP][SAFI_MULTICAST] = 0;
+  peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0;
+  peer->pcount[AFI_IP6][SAFI_UNICAST] = 0;
+  peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
+#endif /* 0 */
+
+  return 0;
+}
+
+/* first-val * 2**x back-off, where x is the number of sucessive calls 
+ * originally used for peer v_start back-off 
+ */
+__attribute__((unused))
+static int
+back_off_exp2 (const int first, int val, const int max) 
+{
+  val <<= 1;
+  return (val < max ? val : max);
+}
+
+/* exponential back off, but biased downward by the initial value. 
+ * this bias is significant at lower values, and tends to
+ * insignificance fairly quickly, so it is equal to the previous at
+ * scale.  Is below first-val * 1.7**x at x == 6, and below first-val
+ * * 1.75**x at x=10.
+ *
+ * I.e., this function is useful to get slower growth for the initial
+ * points of x.
+ */
+__attribute__((unused))
+static int
+back_off_exp2_bias (const int first, int val, const int max)
+{
+  val = (val << 1) - (val > first ? first : 0);
+  return (val < max ? val : max);
+}
+
+/* BGP peer is stoped by the error. */
+static int
+bgp_stop_with_error (struct peer *peer)
+{
+  peer->v_start
+   = back_off_exp2_bias (BGP_INIT_START_TIMER, peer->v_start, 60);
+  bgp_stop (peer);
+  return 0;
+}
+
+
+/* something went wrong, send notify and tear down */
+static int
+bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code)
+{
+  /* Send notify to remote peer */
+  bgp_notify_send (peer, code, sub_code);
+
+  /* Sweep if it is temporary peer. */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    {
+      zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
+      peer_delete (peer);
+      return -1;
+    }
+
+  /* Clear start timer value to default. */
+  peer->v_start = BGP_INIT_START_TIMER;
+
+  /* bgp_stop needs to be invoked while in Established state */
+  bgp_stop(peer);
+
+  return 0;
+}
+
+
+/* TCP connection open.  Next we send open message to remote peer. And
+   add read thread for reading open message. */
+static int
+bgp_connect_success (struct peer *peer)
+{
+  struct peer *realpeer;
+  
+  if (peer->fd < 0)
+    {
+      zlog_err ("bgp_connect_success peer's fd is negative value %d",
+               peer->fd);
+      return -1;
+    }
+  BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    bgp_getsockname (peer);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    {
+      char buf1[SU_ADDRSTRLEN];
+
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+       zlog_debug ("%s open active, local address %s", peer->host,
+                   sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN));
+      else
+       zlog_debug ("%s passive open", peer->host);
+    }
+  
+  /* Generally we want to send OPEN ASAP. Except, some partial BGP
+   * implementations out there (e.g., conformance test tools / BGP
+   * traffic generators) seem to be a bit funny about connection collisions,
+   * and OPENs before they have sent.
+   *
+   * As a hack, delay sending OPEN on an inbound accept-peer session
+   * _IF_ we locally have an outbound connection in progress, i.e. 
+   * we're in middle of a connection collision. If we delay, we delay until
+   * an Open is received - as per old Quagga behaviour.
+   */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    {
+      realpeer = peer_lookup (peer->bgp, &peer->su);
+      
+      if (realpeer->status > Idle && realpeer->status <= Established)
+        {
+          SET_FLAG (peer->sflags, PEER_STATUS_OPEN_DEFERRED);
+          return 0;
+        }
+   }
+  
+  bgp_open_send (peer);
+
+  return 0;
+}
+
+/* TCP connect fail */
+static int
+bgp_connect_fail (struct peer *peer)
+{
+  bgp_stop (peer);
+  return 0;
+}
+
+/* This function is the first starting point of all BGP connection. It
+   try to connect to remote peer with non-blocking IO. */
+int
+bgp_start (struct peer *peer)
+{
+  int status;
+  int connected = 0;
+
+  if (BGP_PEER_START_SUPPRESSED (peer))
+    {
+      if (BGP_DEBUG (fsm, FSM))
+        plog_err (peer->log, "%s [FSM] Trying to start suppressed peer"
+                  " - this is never supposed to happen!", peer->host);
+      return -1;
+    }
+
+  /* Scrub some information that might be left over from a previous,
+   * session
+   */
+  /* Connection information. */
+  if (peer->su_local)
+    {
+      sockunion_free (peer->su_local);
+      peer->su_local = NULL;
+    }
+
+  if (peer->su_remote)
+    {
+      sockunion_free (peer->su_remote);
+      peer->su_remote = NULL;
+    }
+
+  /* Clear remote router-id. */
+  peer->remote_id.s_addr = 0;
+
+  /* Clear peer capability flag. */
+  peer->cap = 0;
+    
+  /* If the peer is passive mode, force to move to Active mode. */
+  if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
+    {
+      BGP_EVENT_ADD (peer, TCP_connection_open_failed);
+      return 0;
+    }
+
+  /* Register to be notified on peer up */
+  if ((peer_ttl(peer) == 1 || peer->gtsm_hops == 1) &&
+      ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+    connected = 1;
+
+  bgp_ensure_nexthop (NULL, peer, connected);
+  status = bgp_connect (peer);
+
+  switch (status)
+    {
+    case connect_error:
+      if (BGP_DEBUG (fsm, FSM))
+       plog_debug (peer->log, "%s [FSM] Connect error", peer->host);
+      BGP_EVENT_ADD (peer, TCP_connection_open_failed);
+      break;
+    case connect_success:
+      if (BGP_DEBUG (fsm, FSM))
+       plog_debug (peer->log, "%s [FSM] Connect immediately success",
+                  peer->host);
+      BGP_EVENT_ADD (peer, TCP_connection_open);
+      break;
+    case connect_in_progress:
+      /* To check nonblocking connect, we wait until socket is
+         readable or writable. */
+      if (BGP_DEBUG (fsm, FSM))
+       plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result",
+                  peer->host);
+      if (peer->fd < 0)
+       {
+         zlog_err ("bgp_start peer's fd is negative value %d",
+                   peer->fd);
+         return -1;
+       }
+      BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+      BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+      break;
+    }
+  return 0;
+}
+
+/* Connect retry timer is expired when the peer status is Connect. */
+static int
+bgp_reconnect (struct peer *peer)
+{
+  bgp_stop (peer);
+  bgp_start (peer);
+  return 0;
+}
+
+static int
+bgp_fsm_open (struct peer *peer)
+{
+  /* Send keepalive and make keepalive timer */
+  bgp_keepalive_send (peer);
+
+  /* Reset holdtimer value. */
+  BGP_TIMER_OFF (peer->t_holdtime);
+
+  return 0;
+}
+
+/* Keepalive send to peer. */
+static int
+bgp_fsm_keepalive_expire (struct peer *peer)
+{
+  bgp_keepalive_send (peer);
+  return 0;
+}
+
+/* FSM error, unexpected event.  This is error of BGP connection. So cut the
+   peer and change to Idle status. */
+static int
+bgp_fsm_event_error (struct peer *peer)
+{
+  plog_err (peer->log, "%s [FSM] unexpected packet received in state %s",
+           peer->host, LOOKUP (bgp_status_msg, peer->status));
+
+  return bgp_stop_with_notify (peer, BGP_NOTIFY_FSM_ERR, 0);
+}
+
+/* Hold timer expire.  This is error of BGP connection. So cut the
+   peer and change to Idle status. */
+static int
+bgp_fsm_holdtime_expire (struct peer *peer)
+{
+  if (BGP_DEBUG (fsm, FSM))
+    plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host);
+
+  return bgp_stop_with_notify (peer, BGP_NOTIFY_HOLD_ERR, 0);
+}
+
+/* Status goes to Established.  Send keepalive packet then make first
+   update information. */
+static int
+bgp_establish (struct peer *peer)
+{
+  struct bgp_notify *notify;
+  afi_t afi;
+  safi_t safi;
+  int nsf_af_count = 0;
+
+  /* Reset capability open status flag. */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
+    SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+  /* Clear last notification data. */
+  notify = &peer->notify;
+  if (notify->data)
+    XFREE (MTYPE_TMP, notify->data);
+  memset (notify, 0, sizeof (struct bgp_notify));
+
+  /* Clear start timer value to default. */
+  peer->v_start = BGP_INIT_START_TIMER;
+
+  /* Increment established count. */
+  peer->established++;
+  bgp_fsm_change_status (peer, Established);
+
+  /* bgp log-neighbor-changes of neighbor Up */
+  if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+    zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host);
+
+  /* graceful restart */
+  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
+      {
+       if (peer->afc_nego[afi][safi]
+           && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV)
+           && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV))
+         {
+           if (peer->nsf[afi][safi]
+               && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV))
+             bgp_clear_stale_route (peer, afi, safi);
+
+           peer->nsf[afi][safi] = 1;
+           nsf_af_count++;
+         }
+       else
+         {
+           if (peer->nsf[afi][safi])
+             bgp_clear_stale_route (peer, afi, safi);
+           peer->nsf[afi][safi] = 0;
+         }
+      }
+
+  if (nsf_af_count)
+    SET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+  else
+    {
+      UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+      if (peer->t_gr_stale)
+       {
+         BGP_TIMER_OFF (peer->t_gr_stale);
+         if (BGP_DEBUG (events, EVENTS))
+           zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+       }
+    }
+
+  if (peer->t_gr_restart)
+    {
+      BGP_TIMER_OFF (peer->t_gr_restart);
+      if (BGP_DEBUG (events, EVENTS))
+       zlog_debug ("%s graceful restart timer stopped", peer->host);
+    }
+
+#ifdef HAVE_SNMP
+  bgpTrapEstablished (peer);
+#endif /* HAVE_SNMP */
+
+  /* Reset uptime, send keepalive, send current table. */
+  peer->uptime = bgp_clock ();
+
+  /* Send route-refresh when ORF is enabled */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+      if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV))
+       {
+         if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
+           bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX,
+                                   REFRESH_IMMEDIATE, 0);
+         else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+           bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD,
+                                   REFRESH_IMMEDIATE, 0);
+       }
+
+  if (peer->v_keepalive)
+    bgp_keepalive_send (peer);
+
+  /* First update is deferred until ORF or ROUTE-REFRESH is received */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+      if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV))
+       if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+           || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
+         SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
+
+  bgp_announce_route_all (peer);
+
+  BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
+
+  return 0;
+}
+
+/* Keepalive packet is received. */
+static int
+bgp_fsm_keepalive (struct peer *peer)
+{
+  /* peer count update */
+  peer->keepalive_in++;
+
+  BGP_TIMER_OFF (peer->t_holdtime);
+  return 0;
+}
+
+/* Update packet is received. */
+static int
+bgp_fsm_update (struct peer *peer)
+{
+  BGP_TIMER_OFF (peer->t_holdtime);
+  return 0;
+}
+
+/* This is empty event. */
+static int
+bgp_ignore (struct peer *peer)
+{
+  if (BGP_DEBUG (fsm, FSM))
+    zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host);
+  return 0;
+}
+
+/* Finite State Machine structure */
+static const struct {
+  int (*func) (struct peer *);
+  int next_state;
+} FSM [BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] = 
+{
+  {
+    /* Idle state: In Idle state, all events other than BGP_Start is
+       ignored.  With BGP_Start event, finite state machine calls
+       bgp_start(). */
+    {bgp_start,  Connect},     /* BGP_Start                    */
+    {bgp_stop,   Idle},                /* BGP_Stop                     */
+    {bgp_stop,   Idle},                /* TCP_connection_open          */
+    {bgp_stop,   Idle},                /* TCP_connection_closed        */
+    {bgp_ignore, Idle},                /* TCP_connection_open_failed   */
+    {bgp_stop,   Idle},                /* TCP_fatal_error              */
+    {bgp_ignore, Idle},                /* ConnectRetry_timer_expired   */
+    {bgp_ignore, Idle},                /* Hold_Timer_expired           */
+    {bgp_ignore, Idle},                /* KeepAlive_timer_expired      */
+    {bgp_ignore, Idle},                /* Receive_OPEN_message         */
+    {bgp_ignore, Idle},                /* Receive_KEEPALIVE_message    */
+    {bgp_ignore, Idle},                /* Receive_UPDATE_message       */
+    {bgp_ignore, Idle},                /* Receive_NOTIFICATION_message */
+    {bgp_ignore, Idle},         /* Clearing_Completed           */
+    {bgp_ignore, Idle},         /* BGP_Stop_with_error          */
+  },
+  {
+    /* Connect */
+    {bgp_ignore,  Connect},    /* BGP_Start                    */
+    {bgp_stop,    Idle},       /* BGP_Stop                     */
+    {bgp_connect_success, OpenSent}, /* TCP_connection_open          */
+    {bgp_stop, Idle},          /* TCP_connection_closed        */
+    {bgp_connect_fail, Active}, /* TCP_connection_open_failed   */
+    {bgp_connect_fail, Idle},  /* TCP_fatal_error              */
+    {bgp_reconnect, Connect},  /* ConnectRetry_timer_expired   */
+    {bgp_ignore,  Idle},       /* Hold_Timer_expired           */
+    {bgp_ignore,  Idle},       /* KeepAlive_timer_expired      */
+    {bgp_ignore,  Idle},       /* Receive_OPEN_message         */
+    {bgp_ignore,  Idle},       /* Receive_KEEPALIVE_message    */
+    {bgp_ignore,  Idle},       /* Receive_UPDATE_message       */
+    {bgp_stop,    Idle},       /* Receive_NOTIFICATION_message */
+    {bgp_ignore,  Idle},         /* Clearing_Completed           */
+    {bgp_stop_with_error, Idle},/* BGP_Stop_with_error          */
+  },
+  {
+    /* Active, */
+    {bgp_ignore,  Active},     /* BGP_Start                    */
+    {bgp_stop,    Idle},       /* BGP_Stop                     */
+    {bgp_connect_success, OpenSent}, /* TCP_connection_open          */
+    {bgp_stop,    Idle},       /* TCP_connection_closed        */
+    {bgp_ignore,  Active},     /* TCP_connection_open_failed   */
+    {bgp_ignore,  Idle},       /* TCP_fatal_error              */
+    {bgp_start,   Connect},    /* ConnectRetry_timer_expired   */
+    {bgp_ignore,  Idle},       /* Hold_Timer_expired           */
+    {bgp_ignore,  Idle},       /* KeepAlive_timer_expired      */
+    {bgp_ignore,  Idle},       /* Receive_OPEN_message         */
+    {bgp_ignore,  Idle},       /* Receive_KEEPALIVE_message    */
+    {bgp_ignore,  Idle},       /* Receive_UPDATE_message       */
+    {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+    {bgp_ignore, Idle},         /* Clearing_Completed           */
+    {bgp_stop_with_error, Idle},/* BGP_Stop_with_error          */
+  },
+  {
+    /* OpenSent, */
+    {bgp_ignore,  OpenSent},   /* BGP_Start                    */
+    {bgp_stop,    Idle},       /* BGP_Stop                     */
+    {bgp_stop,    Active},     /* TCP_connection_open          */
+    {bgp_stop,    Active},     /* TCP_connection_closed        */
+    {bgp_stop,    Active},     /* TCP_connection_open_failed   */
+    {bgp_stop,    Active},     /* TCP_fatal_error              */
+    {bgp_ignore,  Idle},       /* ConnectRetry_timer_expired   */
+    {bgp_fsm_holdtime_expire, Idle},   /* Hold_Timer_expired           */
+    {bgp_ignore,  Idle},       /* KeepAlive_timer_expired      */
+    {bgp_fsm_open,    OpenConfirm},    /* Receive_OPEN_message         */
+    {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message    */
+    {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message       */
+    {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+    {bgp_ignore, Idle},         /* Clearing_Completed           */
+    {bgp_stop_with_error, Idle},/* BGP_Stop_with_error          */
+  },
+  {
+    /* OpenConfirm, */
+    {bgp_ignore,  OpenConfirm},        /* BGP_Start                    */
+    {bgp_stop,    Idle},       /* BGP_Stop                     */
+    {bgp_stop,    Idle},       /* TCP_connection_open          */
+    {bgp_stop,    Idle},       /* TCP_connection_closed        */
+    {bgp_stop,    Idle},       /* TCP_connection_open_failed   */
+    {bgp_stop,    Idle},       /* TCP_fatal_error              */
+    {bgp_ignore,  Idle},       /* ConnectRetry_timer_expired   */
+    {bgp_fsm_holdtime_expire, Idle},   /* Hold_Timer_expired           */
+    {bgp_ignore,  OpenConfirm},        /* KeepAlive_timer_expired      */
+    {bgp_ignore,  Idle},       /* Receive_OPEN_message         */
+    {bgp_establish, Established}, /* Receive_KEEPALIVE_message    */
+    {bgp_ignore,  Idle},       /* Receive_UPDATE_message       */
+    {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+    {bgp_ignore, Idle},         /* Clearing_Completed           */
+    {bgp_stop_with_error, Idle},/* BGP_Stop_with_error          */
+  },
+  {
+    /* Established, */
+    {bgp_ignore,               Established}, /* BGP_Start                    */
+    {bgp_stop,                    Clearing}, /* BGP_Stop                     */
+    {bgp_stop,                    Clearing}, /* TCP_connection_open          */
+    {bgp_stop,                    Clearing}, /* TCP_connection_closed        */
+    {bgp_stop,                 Clearing},      /* TCP_connection_open_failed   */
+    {bgp_stop,                    Clearing}, /* TCP_fatal_error              */
+    {bgp_stop,                 Clearing},      /* ConnectRetry_timer_expired   */
+    {bgp_fsm_holdtime_expire,     Clearing}, /* Hold_Timer_expired           */
+    {bgp_fsm_keepalive_expire, Established}, /* KeepAlive_timer_expired      */
+    {bgp_stop,                    Clearing}, /* Receive_OPEN_message         */
+    {bgp_fsm_keepalive,        Established}, /* Receive_KEEPALIVE_message    */
+    {bgp_fsm_update,           Established}, /* Receive_UPDATE_message       */
+    {bgp_stop_with_error,         Clearing}, /* Receive_NOTIFICATION_message */
+    {bgp_ignore,                      Idle}, /* Clearing_Completed           */
+    {bgp_stop_with_error,         Clearing}, /* BGP_Stop_with_error          */
+  },
+  {
+    /* Clearing, */
+    {bgp_ignore,  Clearing},   /* BGP_Start                    */
+    {bgp_stop,                 Clearing},      /* BGP_Stop                     */
+    {bgp_stop,                 Clearing},      /* TCP_connection_open          */
+    {bgp_stop,                 Clearing},      /* TCP_connection_closed        */
+    {bgp_stop,                 Clearing},      /* TCP_connection_open_failed   */
+    {bgp_stop,                 Clearing},      /* TCP_fatal_error              */
+    {bgp_stop,                 Clearing},      /* ConnectRetry_timer_expired   */
+    {bgp_stop,                 Clearing},      /* Hold_Timer_expired           */
+    {bgp_stop,                 Clearing},      /* KeepAlive_timer_expired      */
+    {bgp_stop,                 Clearing},      /* Receive_OPEN_message         */
+    {bgp_stop,                 Clearing},      /* Receive_KEEPALIVE_message    */
+    {bgp_stop,                 Clearing},      /* Receive_UPDATE_message       */
+    {bgp_stop,                 Clearing},      /* Receive_NOTIFICATION_message */
+    {bgp_clearing_completed,    Idle},         /* Clearing_Completed           */
+    {bgp_stop_with_error,       Clearing},      /* BGP_Stop_with_error          */
+  },
+  {
+    /* Deleted, */
+    {bgp_ignore,  Deleted},    /* BGP_Start                    */
+    {bgp_ignore,  Deleted},    /* BGP_Stop                     */
+    {bgp_ignore,  Deleted},    /* TCP_connection_open          */
+    {bgp_ignore,  Deleted},    /* TCP_connection_closed        */
+    {bgp_ignore,  Deleted},    /* TCP_connection_open_failed   */
+    {bgp_ignore,  Deleted},    /* TCP_fatal_error              */
+    {bgp_ignore,  Deleted},    /* ConnectRetry_timer_expired   */
+    {bgp_ignore,  Deleted},    /* Hold_Timer_expired           */
+    {bgp_ignore,  Deleted},    /* KeepAlive_timer_expired      */
+    {bgp_ignore,  Deleted},    /* Receive_OPEN_message         */
+    {bgp_ignore,  Deleted},    /* Receive_KEEPALIVE_message    */
+    {bgp_ignore,  Deleted},    /* Receive_UPDATE_message       */
+    {bgp_ignore,  Deleted},    /* Receive_NOTIFICATION_message */
+    {bgp_ignore,  Deleted},    /* Clearing_Completed           */
+    {bgp_ignore,  Deleted},     /* BGP_Stop_with_error          */
+  },
+};
+
+static const char *bgp_event_str[] =
+{
+  NULL,
+  "BGP_Start",
+  "BGP_Stop",
+  "TCP_connection_open",
+  "TCP_connection_closed",
+  "TCP_connection_open_failed",
+  "TCP_fatal_error",
+  "ConnectRetry_timer_expired",
+  "Hold_Timer_expired",
+  "KeepAlive_timer_expired",
+  "Receive_OPEN_message",
+  "Receive_KEEPALIVE_message",
+  "Receive_UPDATE_message",
+  "Receive_NOTIFICATION_message",
+  "Clearing_Completed",
+  "BGP_Stop_with_error",
+};
+
+/* Execute event process. */
+int
+bgp_event (struct thread *thread)
+{
+  int ret = 0;
+  int event;
+  int next;
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  event = THREAD_VAL (thread);
+
+  /* Logging this event. */
+  next = FSM [peer->status -1][event - 1].next_state;
+
+  if (BGP_DEBUG (fsm, FSM) && peer->status != next)
+    plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host, 
+              bgp_event_str[event],
+              LOOKUP (bgp_status_msg, peer->status),
+              LOOKUP (bgp_status_msg, next));
+
+  /* Call function. */
+  if (FSM [peer->status -1][event - 1].func)
+    ret = (*(FSM [peer->status - 1][event - 1].func))(peer);
+
+  /* When function do not want proceed next job return -1. */
+  if (ret >= 0)
+    {
+      /* If status is changed. */
+      if (next != peer->status)
+        bgp_fsm_change_status (peer, next);
+      
+      /* Make sure timer is set. */
+      bgp_timer_set (peer);
+    }
+  
+  return ret;
+}
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
new file mode 100644 (file)
index 0000000..752d6e2
--- /dev/null
@@ -0,0 +1,81 @@
+/* BGP-4 Finite State Machine   
+   From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+   Copyright (C) 1998 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_FSM_H
+#define _QUAGGA_BGP_FSM_H
+
+/* Macro for BGP read, write and timer thread.  */
+#define BGP_READ_ON(T,F,V)                     \
+  do {                                         \
+    if (!(T) && (peer->status != Deleted))     \
+      THREAD_READ_ON(bm->master,T,F,peer,V);   \
+  } while (0)
+
+#define BGP_READ_OFF(T)                                \
+  do {                                         \
+    if (T)                                     \
+      THREAD_READ_OFF(T);                      \
+  } while (0)
+
+#define BGP_WRITE_ON(T,F,V)                    \
+  do {                                         \
+    if (!(T) && (peer->status != Deleted))     \
+      THREAD_WRITE_ON(bm->master,(T),(F),peer,(V)); \
+  } while (0)
+    
+#define BGP_WRITE_OFF(T)                       \
+  do {                                         \
+    if (T)                                     \
+      THREAD_WRITE_OFF(T);                     \
+  } while (0)
+
+#define BGP_TIMER_ON(T,F,V)                    \
+  do {                                         \
+    if (!(T) && (peer->status != Deleted))     \
+      THREAD_TIMER_ON(bm->master,(T),(F),peer,(V)); \
+  } while (0)
+
+#define BGP_TIMER_OFF(T)                       \
+  do {                                         \
+    if (T)                                     \
+      THREAD_TIMER_OFF(T);                     \
+  } while (0)
+
+#define BGP_EVENT_ADD(P,E)                     \
+  do {                                         \
+    if ((P)->status != Deleted)                        \
+      thread_add_event (bm->master, bgp_event, (P), (E)); \
+  } while (0)
+
+#define BGP_EVENT_FLUSH(P)                     \
+  do {                                                 \
+    assert (peer);                             \
+    thread_cancel_event (bm->master, (P));     \
+  } while (0)
+
+/* Prototypes. */
+extern int bgp_event (struct thread *);
+extern int bgp_stop (struct peer *peer);
+extern void bgp_timer_set (struct peer *);
+extern void bgp_fsm_change_status (struct peer *peer, int status);
+extern const char *peer_down_str[];
+
+#endif /* _QUAGGA_BGP_FSM_H */
diff --git a/bgpd/bgp_fsm_4271.dot b/bgpd/bgp_fsm_4271.dot
new file mode 100644 (file)
index 0000000..c03939f
--- /dev/null
@@ -0,0 +1,34 @@
+digraph {
+  rankdir=LR
+  //concentrate=true
+  nojustify="true"
+
+  Idle -> Connect [ label="ManualStart\l|AutomaticStart" ]
+  Idle -> Active [ label="ManualStart_with_PassiveTcpEstablishment\l|AutomaticStart_with_PassiveTcpEstablishment" ]
+  
+  Connect -> Idle [ label="ManualStop"]
+  Connect -> Connect [ label="ConnectRetryTimer_Expires\l|TcpConnection_Valid\l|Tcp_CR_Invalid\l|Tcp_CR_Acked && DelayOpen == True\l|TcpConnectionConfirmed && DelayOpen == True\l" ]
+  Connect -> OpenSent [ label="DelayOpenTimer_Expires\l|Tcp_CR_Acked && DelayOpen == False\l|TcpConnectionConfirmed && DelayOpen == False\l" ]
+  Connect -> Active [ label="TcpConnectionFails && DelayOpenTimer == \"running\"\l" ]
+  Connect -> Idle [ label="TcpConnectionFails && DelayOpenTimer == \"not running\"\l" ]
+  Connect -> OpenConfirm [ label="BGPOpen && DelayOpenTimer == \"running\"" ]
+  Connect -> Idle [ label="NotifMsg|*\l" ]
+  
+  Active -> Idle [ label="ManualStop\l|TcpConnectionFails\l|NotifMsg|*" ]
+  Active -> Connect [ label="ConnectRetryTimer_Expires" ]
+  Active -> OpenSent [ label="DelayOpenTimer_Expires" ] 
+  Active -> Active [ label="(Tcp_CR_Acked\l|TcpConnectionConfirmed)\l&& DelayOpen = True" ]
+  Active -> OpenSent [ label="(Tcp_CR_Acked|TcpConnectionConfirmed)\l&& DelayOpen = False" ]
+  Active -> OpenConfirm [ label="BGPOpen && DelayOpenTimer == \"running\"" ]
+
+  OpenSent -> Idle [ label="ManualStop\l|AutomaticStop\l|HoldTimer_Expires\l|NotifMsg\l|OpenCollisionDump\l" ]
+  OpenSent -> Active [ label="TcpConnectionFails" ]
+  OpenSent -> OpenConfirm [ label="BGPOpen" ]
+  
+  OpenConfirm -> Idle [ label="ManualStop\l|AutomaticStop\l|HoldTimer_Expires\l|TcpConnectionFails\l|NotifMsg\l|BGPOpen|*\l"] 
+  OpenConfirm -> Established [ label="KeepAliveMsg|"]
+  OpenConfirm -> OpenConfirm [ label="KeepaliveTimer_Expires" ]  
+
+  Established -> Idle [ label="OpenCollisionDump|*"]
+  Established -> Established [ label="Tcp_CR_Invalid|KeepAliveMsg|UpdateMsg"]
+}
\ No newline at end of file
diff --git a/bgpd/bgp_fsm_quagga.dot b/bgpd/bgp_fsm_quagga.dot
new file mode 100644 (file)
index 0000000..2b9bee8
--- /dev/null
@@ -0,0 +1,59 @@
+digraph {
+  rankdir=LR
+  //concentrate=true
+  nojustify="true"
+
+  Idle
+  Connect
+  Active
+  OpenSent
+  OpenConfirm
+  Established
+  Clearing
+  Idle -> Deleted
+  Configured -> Idle
+  
+  Idle -> Connect [ label="BGP_Start\l/bgp_start\l" ]
+  Idle -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_fatal_error\l/bgp_stop\l"]
+
+  Connect -> Connect [ label="ConnectRetry_timer_expired\l/bgp_reconnect\l" ]
+  Connect -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|Receive_NOTIFICATION_message\l/bgp_stop\l" ]
+  Connect -> Idle [ label="TCP_fatal_error\l/bgp_connect_fail\l" ]
+  Connect -> Idle [ label="Hold_Timer_expired\l|KeepAlive_timer_expired\l|Receive_OPEN_message\l|Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore"]
+  Connect -> OpenSent [ label="TCP_connection_open\l/bgp_connect_success\l" ]
+  Connect -> Active [ label="TCP_connection_open_failed\l/bgp_connect_fail\l" ]
+
+  Active -> Idle [ label="BGP_Stop\l|TCP_connection_closed\l/bgp_stop\l" ]
+  Active -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error\l" ]
+  Active -> Idle [ label="TCP_fatal_error\l|Hold_Timer_expired\l|KeepAlive_timer_expired\l|Receive_OPEN_message\l|Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore\l" ]
+  Active -> OpenSent [ label="TCP_connection_open\l/bgp_connect_success\l" ]
+  Active -> Connect [ label="ConnectRetry_timer_expiredl/bgp_start\l" ]
+
+  Accept -> Active [ label="Raise TCP_connection_open on Active" ]
+
+  OpenSent -> Idle [ label="BGP_Stop\l/bgp_stop\l" ]
+  OpenSent -> Idle [ label="ConnectRetry_timer_expired\l|Clearing_Completed\l|KeepAlive_timer_expired\l/bgp_ignore\l" ]
+  OpenSent -> Idle [ label="Hold_Timer_expired\l/bgp_fsm_holdtime_expire\l"]
+  OpenSent -> Idle [ label="Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l/bgp_fsm_event_error" ]
+  OpenSent -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error" ]
+  OpenSent -> Active [ label="TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l/bgp_stop\l"]
+  OpenSent -> OpenConfirm [ label="Receive_OPEN_message\l/bgp_fsm_open" ]
+  
+  OpenConfirm -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l/bgp_stop\l"]
+  OpenConfirm -> Idle [ label="Hold_Timer_expired\l/bgp_fsm_holdtime_expire" ]
+  OpenConfirm -> Idle [ label="ConnectRetry_timer_expired\l|Receive_OPEN_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore"]
+  OpenConfirm -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error\l" ]
+  OpenConfirm -> Established [ label="Receive_KEEPALIVE_message\l/bgp_establish\l" ]
+
+  Established -> Clearing [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l|ConnectRetry_timer_expired\l|Hold_Timer_expired\l|Receive_OPEN_message\l/bgp_stop\l"]
+  Established -> Idle [ label="Clearing_Completed\l/bgp_ignore" ]
+  Established -> Clearing [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error"] 
+  
+  Clearing -> Idle [ label="Clearing_Completed\l/bgp_clearing_completed" ]
+  subgraph cluster_pre_collision_detect {
+    label="Prior to collision detection"
+    bgcolor=lightgray
+    Connect Accept Active OpenSent OpenConfirm
+  }
+
+}
\ No newline at end of file
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
new file mode 100644 (file)
index 0000000..cc67e12
--- /dev/null
@@ -0,0 +1,562 @@
+/* BGP Large Communities Attribute
+
+Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_aspath.h"
+
+/* Hash of community attribute. */
+static struct hash *lcomhash;
+
+/* Allocate a new lcommunities.  */
+static struct lcommunity *
+lcommunity_new (void)
+{
+  return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY,
+                                       sizeof (struct lcommunity));
+}
+
+/* Allocate lcommunities.  */
+void
+lcommunity_free (struct lcommunity **lcom)
+{
+  if ((*lcom)->val)
+    XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
+  if ((*lcom)->str)
+    XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str);
+  XFREE (MTYPE_LCOMMUNITY, *lcom);
+  lcom = NULL;
+}
+
+/* Add a new Large Communities value to Large Communities
+   Attribute structure.  When the value is already exists in the
+   structure, we don't add the value.  Newly added value is sorted by
+   numerical order.  When the value is added to the structure return 1
+   else return 0.  */
+static int
+lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval)
+{
+  u_int8_t *p;
+  int ret;
+  int c;
+
+  /* When this is fist value, just add it.  */
+  if (lcom->val == NULL)
+    {
+      lcom->size++;
+      lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom));
+      memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
+      return 1;
+    }
+
+  /* If the value already exists in the structure return 0.  */
+  c = 0;
+  for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++)
+    {
+      ret = memcmp (p, lval->val, LCOMMUNITY_SIZE);
+      if (ret == 0)
+        return 0;
+      if (ret > 0)
+        break;
+    }
+
+  /* Add the value to the structure with numerical sorting.  */
+  lcom->size++;
+  lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom));
+
+  memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE,
+          lcom->val + c * LCOMMUNITY_SIZE,
+          (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
+  memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
+
+  return 1;
+}
+
+/* This function takes pointer to Large Communites strucutre then
+   create a new Large Communities structure by uniq and sort each
+   Large Communities value.  */
+struct lcommunity *
+lcommunity_uniq_sort (struct lcommunity *lcom)
+{
+  int i;
+  struct lcommunity *new;
+  struct lcommunity_val *lval;
+
+  if (! lcom)
+    return NULL;
+
+  new = lcommunity_new ();
+
+  for (i = 0; i < lcom->size; i++)
+    {
+      lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
+      lcommunity_add_val (new, lval);
+    }
+  return new;
+}
+
+/* Parse Large Communites Attribute in BGP packet.  */
+struct lcommunity *
+lcommunity_parse (u_int8_t *pnt, u_short length)
+{
+  struct lcommunity tmp;
+  struct lcommunity *new;
+
+  /* Length check.  */
+  if (length % LCOMMUNITY_SIZE)
+    return NULL;
+
+  /* Prepare tmporary structure for making a new Large Communities
+     Attribute.  */
+  tmp.size = length / LCOMMUNITY_SIZE;
+  tmp.val = pnt;
+
+  /* Create a new Large Communities Attribute by uniq and sort each
+     Large Communities value  */
+  new = lcommunity_uniq_sort (&tmp);
+
+  return lcommunity_intern (new);
+}
+
+/* Duplicate the Large Communities Attribute structure.  */
+struct lcommunity *
+lcommunity_dup (struct lcommunity *lcom)
+{
+  struct lcommunity *new;
+
+  new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity));
+  new->size = lcom->size;
+  if (new->size)
+    {
+      new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE);
+      memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
+    }
+  else
+    new->val = NULL;
+  return new;
+}
+
+/* Retrun string representation of communities attribute. */
+char *
+lcommunity_str (struct lcommunity *lcom)
+{
+  if (! lcom->str)
+    lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY);
+  return lcom->str;
+}
+
+/* Merge two Large Communities Attribute structure.  */
+struct lcommunity *
+lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2)
+{
+  if (lcom1->val)
+    lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val,
+                          (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
+  else
+    lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL,
+                         (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
+
+  memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE),
+         lcom2->val, lcom2->size * LCOMMUNITY_SIZE);
+  lcom1->size += lcom2->size;
+
+  return lcom1;
+}
+
+/* Intern Large Communities Attribute.  */
+struct lcommunity *
+lcommunity_intern (struct lcommunity *lcom)
+{
+  struct lcommunity *find;
+
+  assert (lcom->refcnt == 0);
+
+  find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern);
+
+  if (find != lcom)
+    lcommunity_free (&lcom);
+
+  find->refcnt++;
+
+  if (! find->str)
+    find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY);
+
+  return find;
+}
+
+/* Unintern Large Communities Attribute.  */
+void
+lcommunity_unintern (struct lcommunity **lcom)
+{
+  struct lcommunity *ret;
+
+  if ((*lcom)->refcnt)
+    (*lcom)->refcnt--;
+
+  /* Pull off from hash.  */
+  if ((*lcom)->refcnt == 0)
+    {
+      /* Large community must be in the hash.  */
+      ret = (struct lcommunity *) hash_release (lcomhash, *lcom);
+      assert (ret != NULL);
+
+      lcommunity_free (lcom);
+    }
+}
+
+/* Utility function to make hash key.  */
+unsigned int
+lcommunity_hash_make (void *arg)
+{
+  const struct lcommunity *lcom = arg;
+  int size = lcom->size * LCOMMUNITY_SIZE;
+  u_int8_t *pnt = lcom->val;
+  unsigned int key = 0;
+  int c;
+
+  for (c = 0; c < size; c += LCOMMUNITY_SIZE)
+    {
+      key += pnt[c];
+      key += pnt[c + 1];
+      key += pnt[c + 2];
+      key += pnt[c + 3];
+      key += pnt[c + 4];
+      key += pnt[c + 5];
+      key += pnt[c + 6];
+      key += pnt[c + 7];
+      key += pnt[c + 8];
+      key += pnt[c + 9];
+      key += pnt[c + 10];
+      key += pnt[c + 11];
+    }
+
+  return key;
+}
+
+/* Compare two Large Communities Attribute structure.  */
+int
+lcommunity_cmp (const void *arg1, const void *arg2)
+{
+  const struct lcommunity *lcom1 = arg1;
+  const struct lcommunity *lcom2 = arg2;
+
+  return (lcom1->size == lcom2->size
+         && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
+}
+
+/* Return communities hash.  */
+struct hash *
+lcommunity_hash (void)
+{
+  return lcomhash;
+}
+
+/* Initialize Large Comminities related hash. */
+void
+lcommunity_init (void)
+{
+  lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp);
+}
+
+void
+lcommunity_finish (void)
+{
+  hash_free (lcomhash);
+  lcomhash = NULL;
+}
+
+/* Large Communities token enum. */
+enum lcommunity_token
+{
+  lcommunity_token_unknown = 0,
+  lcommunity_token_val,
+};
+
+/* Get next Large Communities token from the string. */
+static const char *
+lcommunity_gettoken (const char *str, struct lcommunity_val *lval,
+                    enum lcommunity_token *token)
+{
+  const char *p = str;
+
+  /* Skip white space. */
+  while (isspace ((int) *p))
+    {
+      p++;
+      str++;
+    }
+
+  /* Check the end of the line. */
+  if (*p == '\0')
+    return NULL;
+
+  /* Community value. */
+  if (isdigit ((int) *p))
+    {
+      int separator = 0;
+      int digit = 0;
+      u_int32_t globaladmin = 0;
+      u_int32_t localdata1 = 0;
+      u_int32_t localdata2 = 0;
+
+      while (isdigit ((int) *p) || *p == ':')
+       {
+         if (*p == ':')
+           {
+             if (separator == 2)
+               {
+                 *token = lcommunity_token_unknown;
+                 return NULL;
+               }
+             else
+               {
+                 separator++;
+                 digit = 0;
+                 if (separator == 1) {
+                   globaladmin = localdata2;
+                 } else {
+                   localdata1 = localdata2;
+                 }
+                 localdata2 = 0;
+               }
+           }
+         else
+           {
+             digit = 1;
+             localdata2 *= 10;
+             localdata2 += (*p - '0');
+           }
+         p++;
+       }
+      if (! digit)
+       {
+         *token = lcommunity_token_unknown;
+         return NULL;
+       }
+
+      /*
+       * Copy the large comm.
+       */
+      lval->val[0] = (globaladmin >> 24) & 0xff;
+      lval->val[1] = (globaladmin >> 16) & 0xff;
+      lval->val[2] = (globaladmin >> 8) & 0xff;
+      lval->val[3] = globaladmin & 0xff;
+      lval->val[4] = (localdata1 >> 24) & 0xff;
+      lval->val[5] = (localdata1 >> 16) & 0xff;
+      lval->val[6] = (localdata1 >> 8) & 0xff;
+      lval->val[7] = localdata1 & 0xff;
+      lval->val[8] = (localdata2 >> 24) & 0xff;
+      lval->val[9] = (localdata2 >> 16) & 0xff;
+      lval->val[10] = (localdata2 >> 8) & 0xff;
+      lval->val[11] = localdata2 & 0xff;
+
+      *token = lcommunity_token_val;
+      return p;
+    }
+  *token = lcommunity_token_unknown;
+  return p;
+}
+
+/*
+  Convert string to large community attribute.
+  When type is already known, please specify both str and type.
+
+  When string includes keyword for each large community value.
+  Please specify keyword_included as non-zero value.
+*/
+struct lcommunity *
+lcommunity_str2com (const char *str)
+{
+    struct lcommunity *lcom = NULL;
+    enum lcommunity_token token = lcommunity_token_unknown;
+    struct lcommunity_val lval;
+
+    while ((str = lcommunity_gettoken (str, &lval, &token)))
+    {
+        switch (token)
+        {
+            case lcommunity_token_val:
+                if (lcom == NULL)
+                    lcom = lcommunity_new ();
+                lcommunity_add_val (lcom, &lval);
+                break;
+            case lcommunity_token_unknown:
+            default:
+                if (lcom)
+                    lcommunity_free (&lcom);
+                return NULL;
+        }
+    }
+    return lcom;
+}
+
+int
+lcommunity_include (struct lcommunity *lcom, u_char *ptr)
+{
+  int i;
+  u_char *lcom_ptr;
+
+  for (i = 0; i < lcom->size; i++) {
+    lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
+    if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
+      return 1;
+  }
+  return 0;
+}
+
+/* Convert large community attribute to string.
+   The large coms will be in 65535:65531:0 format.
+*/
+char *
+lcommunity_lcom2str (struct lcommunity *lcom, int format)
+{
+  int i;
+  u_int8_t *pnt;
+#define LCOMMUNITY_STR_DEFAULT_LEN  40
+  int str_size;
+  int str_pnt;
+  char *str_buf;
+  int len = 0;
+  int first = 1;
+  u_int32_t  globaladmin, localdata1, localdata2;
+
+  if (lcom->size == 0)
+    {
+      str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
+      str_buf[0] = '\0';
+      return str_buf;
+    }
+
+  /* Prepare buffer.  */
+  str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
+  str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
+  str_pnt = 0;
+
+  for (i = 0; i < lcom->size; i++)
+    {
+      /* Make it sure size is enough.  */
+      while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
+       {
+         str_size *= 2;
+         str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
+       }
+
+      /* Space between each value.  */
+      if (! first)
+       str_buf[str_pnt++] = ' ';
+
+      pnt = lcom->val + (i * 12);
+
+      globaladmin = (*pnt++ << 24);
+      globaladmin |= (*pnt++ << 16);
+      globaladmin |= (*pnt++ << 8);
+      globaladmin |= (*pnt++);
+
+      localdata1 = (*pnt++ << 24);
+      localdata1 |= (*pnt++ << 16);
+      localdata1 |= (*pnt++ << 8);
+      localdata1 |= (*pnt++);
+
+      localdata2 = (*pnt++ << 24);
+      localdata2 |= (*pnt++ << 16);
+      localdata2 |= (*pnt++ << 8);
+      localdata2 |= (*pnt++);
+
+      len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
+                    localdata1, localdata2);
+      str_pnt += len;
+      first = 0;
+    }
+  return str_buf;
+}
+
+int
+lcommunity_match (const struct lcommunity *lcom1,
+                  const struct lcommunity *lcom2)
+{
+  int i = 0;
+  int j = 0;
+
+  if (lcom1 == NULL && lcom2 == NULL)
+    return 1;
+
+  if (lcom1 == NULL || lcom2 == NULL)
+    return 0;
+
+  if (lcom1->size < lcom2->size)
+    return 0;
+
+  /* Every community on com2 needs to be on com1 for this to match */
+  while (i < lcom1->size && j < lcom2->size)
+    {
+      if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
+        j++;
+      i++;
+    }
+
+  if (j == lcom2->size)
+    return 1;
+  else
+    return 0;
+}
+
+/* Delete one lcommunity. */
+void
+lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
+{
+  int i = 0;
+  int c = 0;
+
+  if (! lcom->val)
+    return;
+
+  while (i < lcom->size)
+    {
+      if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
+       {
+         c = lcom->size -i -1;
+
+         if (c > 0)
+           memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
+
+         lcom->size--;
+
+         if (lcom->size > 0)
+           lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
+                                lcom_length (lcom));
+         else
+           {
+             XFREE (MTYPE_COMMUNITY_VAL, lcom->val);
+             lcom->val = NULL;
+           }
+         return;
+       }
+      i++;
+    }
+}
diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h
new file mode 100644 (file)
index 0000000..7841b4b
--- /dev/null
@@ -0,0 +1,75 @@
+/* BGP Large Communities Attribute.
+
+Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_LCOMMUNITY_H
+#define _QUAGGA_BGP_LCOMMUNITY_H
+
+/* Extended communities attribute string format.  */
+#define LCOMMUNITY_FORMAT_ROUTE_MAP            0
+#define LCOMMUNITY_FORMAT_COMMUNITY_LIST       1
+#define LCOMMUNITY_FORMAT_DISPLAY              2
+
+/* Large Communities value is twelve octets long.  */
+#define LCOMMUNITY_SIZE                        12
+
+/* Large Communities attribute.  */
+struct lcommunity
+{
+  /* Reference counter.  */
+  unsigned long refcnt;
+
+  /* Size of Extended Communities attribute.  */
+  int size;
+
+  /* Extended Communities value.  */
+  u_int8_t *val;
+
+  /* Human readable format string.  */
+  char *str;
+};
+
+/* Extended community value is eight octet.  */
+struct lcommunity_val
+{
+  char val[LCOMMUNITY_SIZE];
+};
+
+#define lcom_length(X)    ((X)->size * LCOMMUNITY_SIZE)
+
+extern void lcommunity_init (void);
+extern void lcommunity_finish (void);
+extern void lcommunity_free (struct lcommunity **);
+extern struct lcommunity *lcommunity_parse (u_int8_t *, u_short);
+extern struct lcommunity *lcommunity_dup (struct lcommunity *);
+extern struct lcommunity *lcommunity_merge (struct lcommunity *, struct lcommunity *);
+extern struct lcommunity *lcommunity_uniq_sort (struct lcommunity *);
+extern struct lcommunity *lcommunity_intern (struct lcommunity *);
+extern int lcommunity_cmp (const void *, const void *);
+extern void lcommunity_unintern (struct lcommunity **);
+extern unsigned int lcommunity_hash_make (void *);
+extern struct hash *lcommunity_hash (void);
+extern struct lcommunity *lcommunity_str2com (const char *);
+extern char *lcommunity_lcom2str (struct lcommunity *, int);
+extern int lcommunity_match (const struct lcommunity *, const struct lcommunity *);
+extern char *lcommunity_str (struct lcommunity *);
+extern int lcommunity_include (struct lcommunity *lcom, u_char *ptr);
+extern void lcommunity_del_val (struct lcommunity *lcom, u_char *ptr);
+#endif /* _QUAGGA_BGP_LCOMMUNITY_H */
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
new file mode 100644 (file)
index 0000000..af9c030
--- /dev/null
@@ -0,0 +1,493 @@
+/* Main routine of bgpd.
+   Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "getopt.h"
+#include "thread.h"
+#include <lib/version.h>
+#include "memory.h"
+#include "prefix.h"
+#include "log.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "routemap.h"
+#include "filter.h"
+#include "plist.h"
+#include "stream.h"
+#include "vrf.h"
+#include "workqueue.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_zebra.h"
+
+/* bgpd options, we use GNU getopt library. */
+static const struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "bgp_port",    required_argument, NULL, 'p'},
+  { "listenon",    required_argument, NULL, 'l'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "retain",      no_argument,       NULL, 'r'},
+  { "no_kernel",   no_argument,       NULL, 'n'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "skip_runas",  no_argument,       NULL, 'S'},
+  { "version",     no_argument,       NULL, 'v'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+  { "help",        no_argument,       NULL, 'h'},
+  { 0 }
+};
+
+/* signal definitions */
+void sighup (void);
+void sigint (void);
+void sigusr1 (void);
+
+static void bgp_exit (int);
+
+static struct quagga_signal_t bgp_signals[] = 
+{
+  { 
+    .signal = SIGHUP, 
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};
+
+/* Configuration file and directory. */
+char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG;
+
+/* Route retain mode flag. */
+static int retain_mode = 0;
+
+/* Manually specified configuration file name.  */
+char *config_file = NULL;
+
+/* Process ID saved for use by init system */
+static const char *pid_file = PATH_BGPD_PID;
+
+/* VTY port number and address.  */
+int vty_port = BGP_VTY_PORT;
+char *vty_addr = NULL;
+
+/* privileges */
+static zebra_capabilities_t _caps_p [] =  
+{
+    ZCAP_BIND, 
+    ZCAP_NET_RAW,
+    ZCAP_NET_ADMIN,
+};
+
+struct zebra_privs_t bgpd_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = array_size(_caps_p),
+  .cap_num_i = 0,
+};
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\n\
+Daemon which manages kernel routing table management and \
+redistribution between different routing protocols.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-p, --bgp_port     Set bgp protocol's port number\n\
+-l, --listenon     Listen on specified address (implies -n)\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-r, --retain       When program terminates, retain added route by bgpd.\n\
+-n, --no_kernel    Do not install route to kernel.\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-S, --skip_runas   Skip user and group run as\n\
+-v, --version      Print program version\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+/* SIGHUP handler. */
+void 
+sighup (void)
+{
+  zlog (NULL, LOG_INFO, "SIGHUP received");
+
+  /* Terminate all thread. */
+  bgp_terminate ();
+  bgp_reset ();
+  zlog_info ("bgpd restarting!");
+
+  /* Reload config file. */
+  vty_read_config (config_file, config_default);
+
+  /* Create VTY's socket */
+  vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH);
+
+  /* Try to return to normal operation. */
+}
+
+/* SIGINT handler. */
+void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+
+  if (! retain_mode) 
+    {
+      bgp_terminate ();
+      if (bgpd_privs.user)      /* NULL if skip_runas flag set */
+        zprivs_terminate (&bgpd_privs);
+    }
+
+  bgp_exit (0);
+}
+
+/* SIGUSR1 handler. */
+void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+/*
+  Try to free up allocations we know about so that diagnostic tools such as
+  valgrind are able to better illuminate leaks.
+
+  Zebra route removal and protocol teardown are not meant to be done here.
+  For example, "retain_mode" may be set.
+*/
+static void
+bgp_exit (int status)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+  int *socket;
+  struct interface *ifp;
+
+  /* it only makes sense for this to be called on a clean exit */
+  assert (status == 0);
+
+  /* reverse bgp_master_init */
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    bgp_delete (bgp);
+  list_free (bm->bgp);
+  bm->bgp = NULL;
+  
+  /*
+   * bgp_delete can re-allocate the process queues after they were
+   * deleted in bgp_terminate. delete them again.
+   *
+   * It might be better to ensure the RIBs (including static routes)
+   * are cleared by bgp_terminate() during its call to bgp_cleanup_routes(),
+   * which currently only deletes the kernel routes.
+   */
+  if (bm->process_main_queue)
+    {
+     work_queue_free (bm->process_main_queue);
+     bm->process_main_queue = NULL;
+    }
+  if (bm->process_rsclient_queue)
+    {
+      work_queue_free (bm->process_rsclient_queue);
+      bm->process_rsclient_queue = NULL;
+    }
+  
+  /* reverse bgp_master_init */
+  for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, socket))
+    {
+      if (close ((int)(long)socket) == -1)
+        zlog_err ("close (%d): %s", (int)(long)socket, safe_strerror (errno));
+    }
+  list_delete (bm->listen_sockets);
+
+  /* reverse bgp_zebra_init/if_init */
+  if (retain_mode)
+    if_add_hook (IF_DELETE_HOOK, NULL);
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      struct listnode *c_node, *c_nnode;
+      struct connected *c;
+
+      for (ALL_LIST_ELEMENTS (ifp->connected, c_node, c_nnode, c))
+        bgp_connected_delete (c);
+    }
+
+  /* reverse bgp_attr_init */
+  bgp_attr_finish ();
+
+  /* reverse bgp_dump_init */
+  bgp_dump_finish ();
+
+  /* reverse bgp_route_init */
+  bgp_route_finish ();
+
+  /* reverse bgp_route_map_init/route_map_init */
+  route_map_finish ();
+
+  /* reverse access_list_init */
+  access_list_add_hook (NULL);
+  access_list_delete_hook (NULL);
+  access_list_reset ();
+
+  /* reverse bgp_filter_init */
+  as_list_add_hook (NULL);
+  as_list_delete_hook (NULL);
+  bgp_filter_reset ();
+
+  /* reverse prefix_list_init */
+  prefix_list_add_hook (NULL);
+  prefix_list_delete_hook (NULL);
+  prefix_list_reset ();
+
+  /* reverse community_list_init */
+  community_list_terminate (bgp_clist);
+
+  vrf_terminate ();
+  cmd_terminate ();
+  vty_terminate ();
+  bgp_address_destroy();
+  bgp_scan_destroy();
+  bgp_zebra_destroy();
+  if (bgp_nexthop_buf)
+    stream_free (bgp_nexthop_buf);
+  if (bgp_ifindices_buf)
+    stream_free (bgp_ifindices_buf);
+
+  /* reverse bgp_scan_init */
+  bgp_scan_finish ();
+
+  /* reverse bgp_master_init */
+  if (bm->master)
+    thread_master_free (bm->master);
+
+  if (zlog_default)
+    closezlog (zlog_default);
+
+  if (CONF_BGP_DEBUG (normal, NORMAL))
+    log_memstats_stderr ("bgpd");
+
+  exit (status);
+}
+
+/* Main routine of bgpd. Treatment of argument and start bgp finite
+   state machine is handled at here. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  int opt;
+  int daemon_mode = 0;
+  int dryrun = 0;
+  char *progname;
+  struct thread thread;
+  int tmp_port;
+  int skip_runas = 0;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* Preserve name of myself. */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  zlog_default = openzlog (progname, ZLOG_BGP,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  /* BGP master init. */
+  bgp_master_init ();
+
+  /* Command line argument treatment. */
+  while (1) 
+    {
+      opt = getopt_long (argc, argv, "df:i:z:hp:l:A:P:rnu:g:vCS", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+        case 'i':
+          pid_file = optarg;
+          break;
+       case 'z':
+         zclient_serv_path_set (optarg);
+         break;
+       case 'p':
+         tmp_port = atoi (optarg);
+         if (tmp_port <= 0 || tmp_port > 0xffff)
+           bm->port = BGP_PORT_DEFAULT;
+         else
+           bm->port = tmp_port;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+       case 'P':
+          /* Deal with atoi() returning 0 on failure, and bgpd not
+             listening on bgp port... */
+          if (strcmp(optarg, "0") == 0) 
+            {
+              vty_port = 0;
+              break;
+            } 
+          vty_port = atoi (optarg);
+         if (vty_port <= 0 || vty_port > 0xffff)
+           vty_port = BGP_VTY_PORT;
+         break;
+       case 'r':
+         retain_mode = 1;
+         break;
+       case 'l':
+         bm->address = optarg;
+         /* listenon implies -n */
+       case 'n':
+         bgp_option_set (BGP_OPT_NO_FIB);
+         break;
+       case 'u':
+         bgpd_privs.user = optarg;
+         break;
+       case 'g':
+         bgpd_privs.group = optarg;
+         break;
+       case 'S':   /* skip run as = override bgpd_privs */
+          skip_runas = 1;
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Initializations. */
+  srandom (time (NULL));
+  signal_init (bm->master, array_size(bgp_signals), bgp_signals);
+  if (skip_runas)
+    memset (&bgpd_privs, 0, sizeof (bgpd_privs));
+  zprivs_init (&bgpd_privs);
+  cmd_init (1);
+  vty_init (bm->master);
+  memory_init ();
+  vrf_init ();
+
+  /* BGP related initialization.  */
+  bgp_init ();
+
+  /* Parse config file. */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if(dryrun)
+    return(0);
+  
+  /* Turn into daemon if daemon_mode is set. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("BGPd daemon failed: %s", strerror(errno));
+      return (1);
+    }
+
+
+  /* Process ID file creation. */
+  pid_output (pid_file);
+
+  /* Make bgp vty socket. */
+  vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH);
+
+  /* Print banner. */
+  zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d pid %d", QUAGGA_VERSION,
+              vty_port, 
+              (bm->address ? bm->address : "<all>"),
+              bm->port,
+              getpid ());
+
+  /* Start finite state machine, here we go! */
+  while (thread_fetch (bm->master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  return (0);
+}
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
new file mode 100644 (file)
index 0000000..8195e47
--- /dev/null
@@ -0,0 +1,799 @@
+/* $QuaggaId: Format:%an, %ai, %h$ $
+ *
+ * BGP Multipath
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_mpath.h"
+
+bool
+bgp_mpath_is_configured_sort (struct bgp *bgp, bgp_peer_sort_t sort,
+                              afi_t afi, safi_t safi)
+{
+  struct bgp_maxpaths_cfg *cfg = &bgp->maxpaths[afi][safi];
+
+  /* XXX: BGP_DEFAULT_MAXPATHS is 1, and this test only seems to make sense
+   * if if it stays 1, so not sure the DEFAULT define is that useful.
+   */
+  switch (sort)
+    {
+      case BGP_PEER_IBGP:
+        return cfg->maxpaths_ibgp != BGP_DEFAULT_MAXPATHS;
+      case BGP_PEER_EBGP:
+        return cfg->maxpaths_ebgp != BGP_DEFAULT_MAXPATHS;
+      default:
+        return false;
+    }
+}
+
+bool
+bgp_mpath_is_configured (struct bgp *bgp, afi_t afi, safi_t safi)
+{
+  return bgp_mpath_is_configured_sort (bgp, BGP_PEER_IBGP, afi, safi)
+         || bgp_mpath_is_configured_sort (bgp, BGP_PEER_EBGP, afi, safi);
+}
+
+/*
+ * bgp_maximum_paths_set
+ *
+ * Record maximum-paths configuration for BGP instance
+ */
+int
+bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi,
+                       int peertype, u_int16_t maxpaths)
+{
+  if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
+    return -1;
+
+  switch (peertype)
+    {
+    case BGP_PEER_IBGP:
+      bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
+      break;
+    case BGP_PEER_EBGP:
+      bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
+      break;
+    default:
+      return -1;
+    }
+
+  return 0;
+}
+
+/*
+ * bgp_maximum_paths_unset
+ *
+ * Remove maximum-paths configuration from BGP instance
+ */
+int
+bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
+                         int peertype)
+{
+  if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
+    return -1;
+
+  switch (peertype)
+    {
+    case BGP_PEER_IBGP:
+      bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
+      break;
+    case BGP_PEER_EBGP:
+      bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
+      break;
+    default:
+      return -1;
+    }
+
+  return 0;
+}
+
+/*
+ * bgp_info_nexthop_cmp
+ *
+ * Compare the nexthops of two paths. Return value is less than, equal to,
+ * or greater than zero if bi1 is respectively less than, equal to,
+ * or greater than bi2.
+ */
+static int
+bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
+{
+  struct attr_extra *ae1, *ae2;
+  int compare;
+
+  ae1 = bi1->attr->extra;
+  ae2 = bi2->attr->extra;
+
+  compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop);
+
+  if (!compare && ae1 && ae2)
+    {
+      if (ae1->mp_nexthop_len == ae2->mp_nexthop_len)
+        {
+          switch (ae1->mp_nexthop_len)
+            {
+            case 4:
+            case 12:
+              compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in,
+                                       &ae2->mp_nexthop_global_in);
+              break;
+            case 16:
+              compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
+                                       &ae2->mp_nexthop_global);
+              break;
+            case 32:
+              compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
+                                       &ae2->mp_nexthop_global);
+              if (!compare)
+                compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local,
+                                         &ae2->mp_nexthop_local);
+              break;
+            }
+        }
+
+      /* This can happen if one IPv6 peer sends you global and link-local
+       * nexthops but another IPv6 peer only sends you global
+       */
+      else if (ae1->mp_nexthop_len == 16 || ae1->mp_nexthop_len == 32)
+        {
+          compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
+                                   &ae2->mp_nexthop_global);
+          if (!compare)
+            {
+              if (ae1->mp_nexthop_len < ae2->mp_nexthop_len)
+                compare = -1;
+              else
+                compare = 1;
+            }
+        }
+    }
+
+  return compare;
+}
+
+/*
+ * bgp_info_mpath_cmp
+ *
+ * This function determines our multipath list ordering. By ordering
+ * the list we can deterministically select which paths are included
+ * in the multipath set. The ordering also helps in detecting changes
+ * in the multipath selection so we can detect whether to send an
+ * update to zebra.
+ *
+ * The order of paths is determined first by received nexthop, and then
+ * by peer address if the nexthops are the same.
+ */
+static int
+bgp_info_mpath_cmp (void *val1, void *val2)
+{
+  struct bgp_info *bi1, *bi2;
+  int compare;
+
+  bi1 = val1;
+  bi2 = val2;
+
+  compare = bgp_info_nexthop_cmp (bi1, bi2);
+
+  if (!compare)
+    compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote);
+
+  return compare;
+}
+
+/*
+ * bgp_mp_list_init
+ *
+ * Initialize the mp_list, which holds the list of multipaths
+ * selected by bgp_best_selection
+ */
+void
+bgp_mp_list_init (struct list *mp_list)
+{
+  assert (mp_list);
+  memset (mp_list, 0, sizeof (struct list));
+  mp_list->cmp = bgp_info_mpath_cmp;
+}
+
+/*
+ * bgp_mp_list_clear
+ *
+ * Clears all entries out of the mp_list
+ */
+void
+bgp_mp_list_clear (struct list *mp_list)
+{
+  assert (mp_list);
+  list_delete_all_node (mp_list);
+}
+
+/*
+ * bgp_mp_list_add
+ *
+ * Adds a multipath entry to the mp_list
+ */
+void
+bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo)
+{
+  assert (mp_list && mpinfo);
+  listnode_add_sort (mp_list, mpinfo);
+}
+
+/*
+ * bgp_info_mpath_new
+ *
+ * Allocate and zero memory for a new bgp_info_mpath element
+ */
+static struct bgp_info_mpath *
+bgp_info_mpath_new (void)
+{
+  struct bgp_info_mpath *new_mpath;
+  new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath));
+  return new_mpath;
+}
+
+/*
+ * bgp_info_mpath_free
+ *
+ * Release resources for a bgp_info_mpath element and zero out pointer
+ */
+void
+bgp_info_mpath_free (struct bgp_info_mpath **mpath)
+{
+  if (mpath && *mpath)
+    {
+      if ((*mpath)->mp_attr)
+        bgp_attr_unintern (&(*mpath)->mp_attr);
+      XFREE (MTYPE_BGP_MPATH_INFO, *mpath);
+      *mpath = NULL;
+    }
+}
+
+/*
+ * bgp_info_mpath_get
+ *
+ * Fetch the mpath element for the given bgp_info. Used for
+ * doing lazy allocation.
+ */
+static struct bgp_info_mpath *
+bgp_info_mpath_get (struct bgp_info *binfo)
+{
+  struct bgp_info_mpath *mpath;
+  if (!binfo->mpath)
+    {
+      mpath = bgp_info_mpath_new();
+      if (!mpath)
+        return NULL;
+      binfo->mpath = mpath;
+      mpath->mp_info = binfo;
+    }
+  return binfo->mpath;
+}
+
+/*
+ * bgp_info_mpath_enqueue
+ *
+ * Enqueue a path onto the multipath list given the previous multipath
+ * list entry
+ */
+static void
+bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo)
+{
+  struct bgp_info_mpath *prev, *mpath;
+
+  prev = bgp_info_mpath_get (prev_info);
+  mpath = bgp_info_mpath_get (binfo);
+  if (!prev || !mpath)
+    return;
+
+  mpath->mp_next = prev->mp_next;
+  mpath->mp_prev = prev;
+  if (prev->mp_next)
+    prev->mp_next->mp_prev = mpath;
+  prev->mp_next = mpath;
+
+  SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
+}
+
+/*
+ * bgp_info_mpath_dequeue
+ *
+ * Remove a path from the multipath list
+ */
+void
+bgp_info_mpath_dequeue (struct bgp_info *binfo)
+{
+  struct bgp_info_mpath *mpath = binfo->mpath;
+  if (!mpath)
+    return;
+  if (mpath->mp_prev)
+    mpath->mp_prev->mp_next = mpath->mp_next;
+  if (mpath->mp_next)
+    mpath->mp_next->mp_prev = mpath->mp_prev;
+  mpath->mp_next = mpath->mp_prev = NULL;
+  UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
+}
+
+/*
+ * bgp_info_mpath_next
+ *
+ * Given a bgp_info, return the next multipath entry
+ */
+struct bgp_info *
+bgp_info_mpath_next (struct bgp_info *binfo)
+{
+  if (!binfo->mpath || !binfo->mpath->mp_next)
+    return NULL;
+  return binfo->mpath->mp_next->mp_info;
+}
+
+/*
+ * bgp_info_mpath_first
+ *
+ * Given bestpath bgp_info, return the first multipath entry.
+ */
+struct bgp_info *
+bgp_info_mpath_first (struct bgp_info *binfo)
+{
+  return bgp_info_mpath_next (binfo);
+}
+
+/*
+ * bgp_info_mpath_count
+ *
+ * Given the bestpath bgp_info, return the number of multipath entries
+ */
+u_int32_t
+bgp_info_mpath_count (struct bgp_info *binfo)
+{
+  if (!binfo->mpath)
+    return 0;
+  return binfo->mpath->mp_count;
+}
+
+/*
+ * bgp_info_mpath_count_set
+ *
+ * Sets the count of multipaths into bestpath's mpath element
+ */
+static void
+bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count)
+{
+  struct bgp_info_mpath *mpath;
+  if (!count && !binfo->mpath)
+    return;
+  mpath = bgp_info_mpath_get (binfo);
+  if (!mpath)
+    return;
+  mpath->mp_count = count;
+}
+
+/*
+ * bgp_info_mpath_attr
+ *
+ * Given bestpath bgp_info, return aggregated attribute set used
+ * for advertising the multipath route
+ */
+struct attr *
+bgp_info_mpath_attr (struct bgp_info *binfo)
+{
+  if (!binfo->mpath)
+    return NULL;
+  return binfo->mpath->mp_attr;
+}
+
+/*
+ * bgp_info_mpath_attr_set
+ *
+ * Sets the aggregated attribute into bestpath's mpath element
+ */
+static void
+bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr)
+{
+  struct bgp_info_mpath *mpath;
+  if (!attr && !binfo->mpath)
+    return;
+  mpath = bgp_info_mpath_get (binfo);
+  if (!mpath)
+    return;
+  mpath->mp_attr = attr;
+}
+
+/*
+ * bgp_info_mpath_update
+ *
+ * Compare and sync up the multipath list with the mp_list generated by
+ * bgp_best_selection
+ */
+void
+bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best,
+                       struct bgp_info *old_best, struct list *mp_list,
+                        afi_t afi, safi_t safi)
+{
+  u_int16_t maxpaths, mpath_count, old_mpath_count;
+  struct listnode *mp_node, *mp_next_node;
+  struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath;
+  int mpath_changed, debug;
+  char pfx_buf[INET6_ADDRSTRLEN], nh_buf[2][INET6_ADDRSTRLEN];
+  struct bgp_maxpaths_cfg *mpath_cfg = NULL;
+
+  mpath_changed = 0;
+  maxpaths = BGP_DEFAULT_MAXPATHS;
+  mpath_count = 0;
+  cur_mpath = NULL;
+  old_mpath_count = 0;
+  prev_mpath = new_best;
+  mp_node = listhead (mp_list);
+
+  debug = BGP_DEBUG (events, EVENTS);
+
+  if (debug)
+    prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf));
+
+  if (new_best)
+    {
+      mpath_cfg = &new_best->peer->bgp->maxpaths[afi][safi];
+      mpath_count++;
+      if (new_best != old_best)
+        bgp_info_mpath_dequeue (new_best);
+      maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ?
+        mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp;
+    }
+
+  if (old_best)
+    {
+      cur_mpath = bgp_info_mpath_first (old_best);
+      old_mpath_count = bgp_info_mpath_count (old_best);
+      bgp_info_mpath_count_set (old_best, 0);
+      bgp_info_mpath_dequeue (old_best);
+    }
+
+  /*
+   * We perform an ordered walk through both lists in parallel.
+   * The reason for the ordered walk is that if there are paths
+   * that were previously multipaths and are still multipaths, the walk
+   * should encounter them in both lists at the same time. Otherwise
+   * there will be paths that are in one list or another, and we
+   * will deal with these separately.
+   *
+   * Note that new_best might be somewhere in the mp_list, so we need
+   * to skip over it
+   */
+  while (mp_node || cur_mpath)
+    {
+      /*
+       * We can bail out of this loop if all existing paths on the
+       * multipath list have been visited (for cleanup purposes) and
+       * the maxpath requirement is fulfulled
+       */
+      if (!cur_mpath && (mpath_count >= maxpaths))
+        break;
+
+      mp_next_node = mp_node ? listnextnode (mp_node) : NULL;
+      next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL;
+
+      /*
+       * If equal, the path was a multipath and is still a multipath.
+       * Insert onto new multipath list if maxpaths allows.
+       */
+      if (mp_node && (listgetdata (mp_node) == cur_mpath))
+        {
+          list_delete_node (mp_list, mp_node);
+          bgp_info_mpath_dequeue (cur_mpath);
+          if ((mpath_count < maxpaths) &&
+              bgp_info_nexthop_cmp (prev_mpath, cur_mpath))
+            {
+              bgp_info_mpath_enqueue (prev_mpath, cur_mpath);
+              prev_mpath = cur_mpath;
+              mpath_count++;
+            }
+          else
+            {
+              mpath_changed = 1;
+              if (debug)
+                zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
+                            inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
+                                       nh_buf[0], sizeof (nh_buf[0])),
+                            sockunion2str (cur_mpath->peer->su_remote,
+                                           nh_buf[1], sizeof (nh_buf[1])));
+            }
+          mp_node = mp_next_node;
+          cur_mpath = next_mpath;
+          continue;
+        }
+
+      if (cur_mpath && (!mp_node ||
+                        (bgp_info_mpath_cmp (cur_mpath,
+                                             listgetdata (mp_node)) < 0)))
+        {
+          /*
+           * If here, we have an old multipath and either the mp_list
+           * is finished or the next mp_node points to a later
+           * multipath, so we need to purge this path from the
+           * multipath list
+           */
+          bgp_info_mpath_dequeue (cur_mpath);
+          mpath_changed = 1;
+          if (debug)
+            zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
+                        inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
+                                   nh_buf[0], sizeof (nh_buf[0])),
+                        sockunion2str (cur_mpath->peer->su_remote,
+                                       nh_buf[1], sizeof (nh_buf[1])));
+          cur_mpath = next_mpath;
+        }
+      else
+        {
+          /*
+           * If here, we have a path on the mp_list that was not previously
+           * a multipath (due to non-equivalance or maxpaths exceeded),
+           * or the matching multipath is sorted later in the multipath
+           * list. Before we enqueue the path on the new multipath list,
+           * make sure its not on the old_best multipath list or referenced
+           * via next_mpath:
+           * - If next_mpath points to this new path, update next_mpath to
+           *   point to the multipath after this one
+           * - Dequeue the path from the multipath list just to make sure
+           */
+          new_mpath = listgetdata (mp_node);
+          list_delete_node (mp_list, mp_node);
+          if ((mpath_count < maxpaths) && (new_mpath != new_best) &&
+              bgp_info_nexthop_cmp (prev_mpath, new_mpath))
+            {
+              if (new_mpath == next_mpath)
+                next_mpath = bgp_info_mpath_next (new_mpath);
+              bgp_info_mpath_dequeue (new_mpath);
+
+              bgp_info_mpath_enqueue (prev_mpath, new_mpath);
+              prev_mpath = new_mpath;
+              mpath_changed = 1;
+              mpath_count++;
+              if (debug)
+                zlog_debug ("%s add mpath nexthop %s peer %s", pfx_buf,
+                            inet_ntop (AF_INET, &new_mpath->attr->nexthop,
+                                       nh_buf[0], sizeof (nh_buf[0])),
+                            sockunion2str (new_mpath->peer->su_remote,
+                                           nh_buf[1], sizeof (nh_buf[1])));
+            }
+          mp_node = mp_next_node;
+        }
+    }
+
+  if (new_best)
+    {
+      bgp_info_mpath_count_set (new_best, mpath_count-1);
+      if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count))
+        SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG);
+    }
+}
+
+/*
+ * bgp_mp_dmed_deselect
+ *
+ * Clean up multipath information for BGP_INFO_DMED_SELECTED path that
+ * is not selected as best path
+ */
+void
+bgp_mp_dmed_deselect (struct bgp_info *dmed_best)
+{
+  struct bgp_info *mpinfo, *mpnext;
+
+  if (!dmed_best)
+    return;
+
+  for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext)
+    {
+      mpnext = bgp_info_mpath_next (mpinfo);
+      bgp_info_mpath_dequeue (mpinfo);
+    }
+
+  bgp_info_mpath_count_set (dmed_best, 0);
+  UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG);
+  assert (bgp_info_mpath_first (dmed_best) == 0);
+}
+
+/*
+ * bgp_info_mpath_aggregate_update
+ *
+ * Set the multipath aggregate attribute. We need to see if the
+ * aggregate has changed and then set the ATTR_CHANGED flag on the
+ * bestpath info so that a peer update will be generated. The
+ * change is detected by generating the current attribute,
+ * interning it, and then comparing the interned pointer with the
+ * current value. We can skip this generate/compare step if there
+ * is no change in multipath selection and no attribute change in
+ * any multipath.
+ */
+void
+bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
+                                 struct bgp_info *old_best)
+{
+  struct bgp_info *mpinfo;
+  struct aspath *aspath;
+  struct aspath *asmerge;
+  struct attr *new_attr, *old_attr;
+  u_char origin, attr_chg;
+  struct community *community, *commerge;
+  struct ecommunity *ecomm, *ecommerge;
+  struct lcommunity *lcomm, *lcommerge;
+  struct attr_extra *ae;
+  struct attr attr = { 0 };
+
+  if (old_best && (old_best != new_best) &&
+      (old_attr = bgp_info_mpath_attr (old_best)))
+    {
+      bgp_attr_unintern (&old_attr);
+      bgp_info_mpath_attr_set (old_best, NULL);
+    }
+
+  if (!new_best)
+    return;
+
+  if (!bgp_info_mpath_count (new_best))
+    {
+      if ((new_attr = bgp_info_mpath_attr (new_best)))
+        {
+          bgp_attr_unintern (&new_attr);
+          bgp_info_mpath_attr_set (new_best, NULL);
+          SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
+        }
+      return;
+    }
+
+  /*
+   * Bail out here if the following is true:
+   * - MULTIPATH_CHG bit is not set on new_best, and
+   * - No change in bestpath, and
+   * - ATTR_CHANGED bit is not set on new_best or any of the multipaths
+   */
+  if (!CHECK_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG) &&
+      (old_best == new_best))
+    {
+      attr_chg = 0;
+
+      if (CHECK_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED))
+        attr_chg = 1;
+      else
+        for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
+             mpinfo = bgp_info_mpath_next (mpinfo))
+          {
+            if (CHECK_FLAG (mpinfo->flags, BGP_INFO_ATTR_CHANGED))
+              {
+                attr_chg = 1;
+                break;
+              }
+          }
+
+      if (!attr_chg)
+        {
+          assert (bgp_info_mpath_attr (new_best));
+          return;
+        }
+    }
+
+  bgp_attr_dup (&attr, new_best->attr);
+
+  /* aggregate attribute from multipath constituents */
+  aspath = aspath_dup (attr.aspath);
+  origin = attr.origin;
+  community = attr.community ? community_dup (attr.community) : NULL;
+  ae = attr.extra;
+  ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
+
+  lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL;
+
+  for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
+       mpinfo = bgp_info_mpath_next (mpinfo))
+    {
+      asmerge = aspath_aggregate_mpath (aspath, mpinfo->attr->aspath);
+      aspath_free (aspath);
+      aspath = asmerge;
+
+      if (origin < mpinfo->attr->origin)
+        origin = mpinfo->attr->origin;
+
+      if (mpinfo->attr->community)
+        {
+          if (community)
+            {
+              commerge = community_merge (community, mpinfo->attr->community);
+              community = community_uniq_sort (commerge);
+              community_free (commerge);
+            }
+          else
+            community = community_dup (mpinfo->attr->community);
+        }
+
+      ae = mpinfo->attr->extra;
+      if (ae && ae->ecommunity)
+        {
+          if (ecomm)
+            {
+              ecommerge = ecommunity_merge (ecomm, ae->ecommunity);
+              ecomm = ecommunity_uniq_sort (ecommerge);
+              ecommunity_free (&ecommerge);
+            }
+          else
+            ecomm = ecommunity_dup (ae->ecommunity);
+        }
+
+      if (ae && ae->lcommunity)
+        {
+          if (lcomm)
+            {
+              lcommerge = lcommunity_merge (lcomm, ae->lcommunity);
+              lcomm = lcommunity_uniq_sort (lcommerge);
+              lcommunity_free (&lcommerge);
+            }
+          else
+            lcomm = lcommunity_dup (ae->lcommunity);
+        }
+    }
+
+  attr.aspath = aspath;
+  attr.origin = origin;
+  if (community)
+    {
+      attr.community = community;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+    }
+  if (ecomm)
+    {
+      ae = bgp_attr_extra_get (&attr);
+      ae->ecommunity = ecomm;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+    }
+
+  /* Zap multipath attr nexthop so we set nexthop to self */
+  attr.nexthop.s_addr = 0;
+  if (attr.extra)
+    memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr));
+
+  /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
+
+  new_attr = bgp_attr_intern (&attr);
+  bgp_attr_extra_free (&attr);
+
+  if (new_attr != bgp_info_mpath_attr (new_best))
+    {
+      if ((old_attr = bgp_info_mpath_attr (new_best)))
+        bgp_attr_unintern (&old_attr);
+      bgp_info_mpath_attr_set (new_best, new_attr);
+      SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
+    }
+  else
+    bgp_attr_unintern (&new_attr);
+}
diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h
new file mode 100644 (file)
index 0000000..2a84d5e
--- /dev/null
@@ -0,0 +1,82 @@
+/* $QuaggaId: Format:%an, %ai, %h$ $
+ *
+ * BGP Multipath
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_MPATH_H
+#define _QUAGGA_BGP_MPATH_H
+
+/* BGP default maximum-paths */
+#define BGP_DEFAULT_MAXPATHS 1
+
+/* Supplemental information linked to bgp_info for keeping track of
+ * multipath selections, lazily allocated to save memory
+ */
+struct bgp_info_mpath
+{
+  /* Points to the first multipath (on bestpath) or the next multipath */
+  struct bgp_info_mpath *mp_next;
+
+  /* Points to the previous multipath or NULL on bestpath */
+  struct bgp_info_mpath *mp_prev;
+
+  /* Points to bgp_info associated with this multipath info */
+  struct bgp_info *mp_info;
+
+  /* When attached to best path, the number of selected multipaths */
+  u_int32_t mp_count;
+
+  /* Aggregated attribute for advertising multipath route */
+  struct attr *mp_attr;
+};
+
+/* Functions to support maximum-paths configuration */
+extern int bgp_maximum_paths_set (struct bgp *, afi_t, safi_t, int, u_int16_t);
+extern int bgp_maximum_paths_unset (struct bgp *, afi_t, safi_t, int);
+bool bgp_mpath_is_configured_sort (struct bgp *, bgp_peer_sort_t, afi_t, safi_t);
+bool bgp_mpath_is_configured (struct bgp *, afi_t, safi_t);
+
+/* Functions used by bgp_best_selection to record current
+ * multipath selections
+ */
+extern void bgp_mp_list_init (struct list *);
+extern void bgp_mp_list_clear (struct list *);
+extern void bgp_mp_list_add (struct list *, struct bgp_info *);
+extern void bgp_mp_dmed_deselect (struct bgp_info *);
+extern void bgp_info_mpath_update (struct bgp_node *, struct bgp_info *,
+                                   struct bgp_info *, struct list *,
+                                   afi_t, safi_t);
+extern void bgp_info_mpath_aggregate_update (struct bgp_info *,
+                                             struct bgp_info *);
+
+/* Unlink and free multipath information associated with a bgp_info */
+extern void bgp_info_mpath_dequeue (struct bgp_info *);
+extern void bgp_info_mpath_free (struct bgp_info_mpath **);
+
+/* Walk list of multipaths associated with a best path */
+extern struct bgp_info *bgp_info_mpath_first (struct bgp_info *);
+extern struct bgp_info *bgp_info_mpath_next (struct bgp_info *);
+
+/* Accessors for multipath information */
+extern u_int32_t bgp_info_mpath_count (struct bgp_info *);
+extern struct attr *bgp_info_mpath_attr (struct bgp_info *);
+
+#endif /* _QUAGGA_BGP_MPATH_H */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
new file mode 100644 (file)
index 0000000..ac3ee08
--- /dev/null
@@ -0,0 +1,1063 @@
+/* MPLS-VPN
+   Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_packet.h"
+
+static u_int16_t
+decode_rd_type (u_char *pnt)
+{
+  u_int16_t v;
+  
+  v = ((u_int16_t) *pnt++ << 8);
+  v |= (u_int16_t) *pnt;
+  return v;
+}
+
+u_int32_t
+decode_label (u_char *pnt)
+{
+  u_int32_t l;
+
+  l = ((u_int32_t) *pnt++ << 12);
+  l |= (u_int32_t) *pnt++ << 4;
+  l |= (u_int32_t) ((*pnt & 0xf0) >> 4);
+  return l;
+}
+
+/* type == RD_TYPE_AS */
+static void
+decode_rd_as (u_char *pnt, struct rd_as *rd_as)
+{
+  rd_as->as = (u_int16_t) *pnt++ << 8;
+  rd_as->as |= (u_int16_t) *pnt++;
+  
+  rd_as->val = ((u_int32_t) *pnt++ << 24);
+  rd_as->val |= ((u_int32_t) *pnt++ << 16);
+  rd_as->val |= ((u_int32_t) *pnt++ << 8);
+  rd_as->val |= (u_int32_t) *pnt;
+}
+
+/* type == RD_TYPE_AS4 */
+static void
+decode_rd_as4 (u_char *pnt, struct rd_as *rd_as)
+{
+  rd_as->as  = (u_int32_t) *pnt++ << 24;
+  rd_as->as |= (u_int32_t) *pnt++ << 16;
+  rd_as->as |= (u_int32_t) *pnt++ << 8;
+  rd_as->as |= (u_int32_t) *pnt++;
+
+  rd_as->val  = ((u_int16_t) *pnt++ << 8);
+  rd_as->val |= (u_int16_t) *pnt;
+}
+
+/* type == RD_TYPE_IP */
+static void
+decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
+{
+  memcpy (&rd_ip->ip, pnt, 4);
+  pnt += 4;
+  
+  rd_ip->val = ((u_int16_t) *pnt++ << 8);
+  rd_ip->val |= (u_int16_t) *pnt;
+}
+
+int
+bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, 
+                    struct bgp_nlri *packet)
+{
+  u_char *pnt;
+  u_char *lim;
+  struct prefix p;
+  int psize = 0;
+  int prefixlen;
+  u_int16_t type;
+  struct rd_as rd_as;
+  struct rd_ip rd_ip;
+  struct prefix_rd prd;
+  u_char *tagpnt;
+
+  /* Check peer status. */
+  if (peer->status != Established)
+    return 0;
+  
+  /* Make prefix_rd */
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  pnt = packet->nlri;
+  lim = pnt + packet->length;
+
+#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */
+  for (; pnt < lim; pnt += psize)
+    {
+      /* Clear prefix structure. */
+      memset (&p, 0, sizeof (struct prefix));
+
+      /* Fetch prefix length. */
+      prefixlen = *pnt++;
+      p.family = afi2family (packet->afi);
+      psize = PSIZE (prefixlen);
+      
+      /* sanity check against packet data */
+      if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8)
+        {
+          plog_err (peer->log, 
+                    "%s [Error] Update packet error / VPNv4"
+                     " (prefix length %d less than VPNv4 min length)",
+                    peer->host, prefixlen);
+          return -1;
+        }
+      if ((pnt + psize) > lim)
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error / VPNv4"
+                    " (psize %u exceeds packet size (%u)",
+                    peer->host, 
+                    prefixlen, (uint)(lim-pnt));
+          return -1;
+        }
+      
+      /* sanity check against storage for the IP address portion */
+      if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u))
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error / VPNv4"
+                    " (psize %u exceeds storage size (%zu)",
+                    peer->host,
+                    prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, sizeof(p.u));
+          return -1;
+        }
+      
+      /* Sanity check against max bitlen of the address family */
+      if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen (&p))
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error / VPNv4"
+                    " (psize %u exceeds family (%u) max byte len %u)",
+                    peer->host,
+                    prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, 
+                    p.family, prefix_blen (&p));
+          return -1;
+        }
+      
+      /* Copyr label to prefix. */
+      tagpnt = pnt;
+
+      /* Copy routing distinguisher to rd. */
+      memcpy (&prd.val, pnt + 3, 8);
+
+      /* Decode RD type. */
+      type = decode_rd_type (pnt + 3);
+
+      switch (type)
+        {
+        case RD_TYPE_AS:
+          decode_rd_as (pnt + 5, &rd_as);
+          break;
+
+        case RD_TYPE_AS4:
+          decode_rd_as4 (pnt + 5, &rd_as);
+          break;
+
+        case RD_TYPE_IP:
+          decode_rd_ip (pnt + 5, &rd_ip);
+          break;
+
+       default:
+         zlog_err ("Unknown RD type %d", type);
+          break;  /* just report */
+      }
+
+      p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8;
+      memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, 
+              psize - VPN_PREFIXLEN_MIN_BYTES);
+
+      if (attr)
+        bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+                    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+      else
+        bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+    }
+  /* Packet length consistency check. */
+  if (pnt != lim)
+    {
+      plog_err (peer->log,
+                "%s [Error] Update packet error / VPNv4"
+                " (%zu data remaining after parsing)",
+                peer->host, lim - pnt);
+      return -1;
+    }
+  
+  return 0;
+#undef VPN_PREFIXLEN_MIN_BYTES
+}
+
+int
+str2prefix_rd (const char *str, struct prefix_rd *prd)
+{
+  int ret; /* ret of called functions */
+  int lret; /* local ret, of this func */
+  char *p;
+  char *p2;
+  struct stream *s = NULL;
+  char *half = NULL;
+  struct in_addr addr;
+
+  s = stream_new (8);
+
+  prd->family = AF_UNSPEC;
+  prd->prefixlen = 64;
+
+  lret = 0;
+  p = strchr (str, ':');
+  if (! p)
+    goto out;
+
+  if (! all_digit (p + 1))
+    goto out;
+
+  half = XMALLOC (MTYPE_TMP, (p - str) + 1);
+  memcpy (half, str, (p - str));
+  half[p - str] = '\0';
+
+  p2 = strchr (str, '.');
+
+  if (! p2)
+    {
+      if (! all_digit (half))
+        goto out;
+      
+      stream_putw (s, RD_TYPE_AS);
+      stream_putw (s, atoi (half));
+      stream_putl (s, atol (p + 1));
+    }
+  else
+    {
+      ret = inet_aton (half, &addr);
+      if (! ret)
+        goto out;
+      
+      stream_putw (s, RD_TYPE_IP);
+      stream_put_in_addr (s, &addr);
+      stream_putw (s, atol (p + 1));
+    }
+  memcpy (prd->val, s->data, 8);
+  lret = 1;
+
+out:
+  if (s)
+    stream_free (s);
+  if (half)
+    XFREE(MTYPE_TMP, half);
+  return lret;
+}
+
+int
+str2tag (const char *str, u_char *tag)
+{
+  unsigned long l;
+  char *endptr;
+  u_int32_t t;
+
+  if (*str == '-')
+    return 0;
+  
+  errno = 0;
+  l = strtoul (str, &endptr, 10);
+
+  if (*endptr != '\0' || errno || l > UINT32_MAX)
+    return 0;
+
+  t = (u_int32_t) l;
+  
+  tag[0] = (u_char)(t >> 12);
+  tag[1] = (u_char)(t >> 4);
+  tag[2] = (u_char)(t << 4);
+
+  return 1;
+}
+
+char *
+prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size)
+{
+  u_char *pnt;
+  u_int16_t type;
+  struct rd_as rd_as;
+  struct rd_ip rd_ip;
+
+  if (size < RD_ADDRSTRLEN)
+    return NULL;
+
+  pnt = prd->val;
+
+  type = decode_rd_type (pnt);
+
+  if (type == RD_TYPE_AS)
+    {
+      decode_rd_as (pnt + 2, &rd_as);
+      snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
+      return buf;
+    }
+  else if (type == RD_TYPE_AS4)
+    {
+      decode_rd_as4 (pnt + 2, &rd_as);
+      snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
+      return buf;
+    }
+  else if (type == RD_TYPE_IP)
+    {
+      decode_rd_ip (pnt + 2, &rd_ip);
+      snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+      return buf;
+    }
+  return NULL;
+}
+
+/* For testing purpose, static route of MPLS-VPN. */
+DEFUN (vpnv4_network,
+       vpnv4_network_cmd,
+       "network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify Route Distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "BGP tag\n"
+       "tag value\n")
+{
+  return bgp_static_set_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (vpnv4_network_route_map,
+       vpnv4_network_route_map_cmd,
+       "network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD route-map WORD",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify Route Distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "BGP tag\n"
+       "tag value\n"
+       "route map\n"
+       "route map name\n")
+{
+  return bgp_static_set_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+/* For testing purpose, static route of MPLS-VPN. */
+DEFUN (no_vpnv4_network,
+       no_vpnv4_network_cmd,
+       "no network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify Route Distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "BGP tag\n"
+       "tag value\n")
+{
+  return bgp_static_unset_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2]);
+}
+
+static int
+show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd)
+{
+  struct bgp *bgp;
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct attr *attr;
+  int rd_header;
+  int header = 1;
+  char v4_header[] = "   Network          Next Hop            Metric LocPrf Weight Path%s";
+
+  bgp = bgp_get_default ();
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn;
+       rn = bgp_route_next (rn))
+    {
+      if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+        continue;
+
+      if ((table = rn->info) != NULL)
+        {
+          rd_header = 1;
+
+          for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+            if ((attr = rm->info) != NULL)
+              {
+                if (header)
+                  {
+                    vty_out (vty, "BGP table version is 0, local router ID is %s%s",
+                             inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                    vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
+                             VTY_NEWLINE);
+                    vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
+                             VTY_NEWLINE, VTY_NEWLINE);
+                    vty_out (vty, v4_header, VTY_NEWLINE);
+                    header = 0;
+                  }
+
+                if (rd_header)
+                  {
+                    u_int16_t type;
+                    struct rd_as rd_as;
+                    struct rd_ip rd_ip;
+                    u_char *pnt;
+
+                    pnt = rn->p.u.val;
+
+                    /* Decode RD type. */
+                    type = decode_rd_type (pnt);
+                    /* Decode RD value. */
+                    if (type == RD_TYPE_AS)
+                      decode_rd_as (pnt + 2, &rd_as);
+                    else if (type == RD_TYPE_AS4)
+                      decode_rd_as4 (pnt + 2, &rd_as);
+                    else if (type == RD_TYPE_IP)
+                      decode_rd_ip (pnt + 2, &rd_ip);
+
+                    vty_out (vty, "Route Distinguisher: ");
+
+                    if (type == RD_TYPE_AS)
+                      vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
+                    else if (type == RD_TYPE_AS4)
+                      vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
+                    else if (type == RD_TYPE_IP)
+                      vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+
+                    vty_out (vty, "%s", VTY_NEWLINE);
+                    rd_header = 0;
+                  }
+                route_vty_out_tmp (vty, &rm->p, attr, SAFI_MPLS_VPN);
+              }
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+enum bgp_show_type
+{
+  bgp_show_type_normal,
+  bgp_show_type_regexp,
+  bgp_show_type_prefix_list,
+  bgp_show_type_filter_list,
+  bgp_show_type_neighbor,
+  bgp_show_type_cidr_only,
+  bgp_show_type_prefix_longer,
+  bgp_show_type_community_all,
+  bgp_show_type_community,
+  bgp_show_type_community_exact,
+  bgp_show_type_community_list,
+  bgp_show_type_community_list_exact
+};
+
+static int
+bgp_show_mpls_vpn(
+    struct vty *vty,
+    afi_t afi,
+    struct prefix_rd *prd,
+    enum bgp_show_type type,
+    void *output_arg,
+    int tags)
+{
+  struct bgp *bgp;
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp_info *ri;
+  int rd_header;
+  int header = 1;
+  char v4_header[] = "   Network          Next Hop            Metric LocPrf Weight Path%s";
+  char v4_header_tag[] = "   Network          Next Hop      In tag/Out tag%s";
+
+  unsigned long output_count = 0;
+  unsigned long total_count  = 0;
+
+  bgp = bgp_get_default ();
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  if ((afi != AFI_IP) && (afi != AFI_IP6))
+    {
+      vty_out (vty, "Afi %d not supported%s", afi, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
+    {
+      if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+       continue;
+
+      if ((table = rn->info) != NULL)
+       {
+         rd_header = 1;
+
+         for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+           for (ri = rm->info; ri; ri = ri->next)
+             {
+                total_count++;
+               if (type == bgp_show_type_neighbor)
+                 {
+                   union sockunion *su = output_arg;
+
+                   if (ri->peer->su_remote == NULL || ! sockunion_same(ri->peer->su_remote, su))
+                     continue;
+                 }
+               if (header)
+                 {
+                   if (tags)
+                     vty_out (vty, v4_header_tag, VTY_NEWLINE);
+                   else
+                     {
+                       vty_out (vty, "BGP table version is 0, local router ID is %s%s",
+                                inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                       vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s",
+                                VTY_NEWLINE);
+                       vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s",
+                                VTY_NEWLINE, VTY_NEWLINE);
+                       vty_out (vty, v4_header, VTY_NEWLINE);
+                     }
+                   header = 0;
+                 }
+
+               if (rd_header)
+                 {
+                   u_int16_t type;
+                   struct rd_as rd_as;
+                   struct rd_ip rd_ip;
+                   u_char *pnt;
+
+                   pnt = rn->p.u.val;
+
+                   /* Decode RD type. */
+                   type = decode_rd_type (pnt);
+                   /* Decode RD value. */
+                   if (type == RD_TYPE_AS)
+                     decode_rd_as (pnt + 2, &rd_as);
+                   else if (type == RD_TYPE_AS4)
+                     decode_rd_as4 (pnt + 2, &rd_as);
+                   else if (type == RD_TYPE_IP)
+                     decode_rd_ip (pnt + 2, &rd_ip);
+
+                   vty_out (vty, "Route Distinguisher: ");
+
+                   if (type == RD_TYPE_AS)
+                     vty_out (vty, "as2 %u:%d", rd_as.as, rd_as.val);
+                   else if (type == RD_TYPE_AS4)
+                     vty_out (vty, "as4 %u:%d", rd_as.as, rd_as.val);
+                   else if (type == RD_TYPE_IP)
+                     vty_out (vty, "ip %s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+                 
+                   vty_out (vty, "%s", VTY_NEWLINE);             
+                   rd_header = 0;
+                 }
+               if (tags)
+                 route_vty_out_tag (vty, &rm->p, ri, 0, SAFI_MPLS_VPN);
+               else
+                 route_vty_out (vty, &rm->p, ri, 0, SAFI_MPLS_VPN);
+                output_count++;
+             }
+        }
+    }
+
+  if (output_count == 0)
+    {
+      vty_out (vty, "No prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s",
+            VTY_NEWLINE, output_count, total_count, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv4_vpn,
+       show_bgp_ipv4_vpn_cmd,
+       "show bgp ipv4 vpn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n")
+{
+  return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv6_vpn,
+       show_bgp_ipv6_vpn_cmd,
+       "show bgp ipv6 vpn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n")
+{
+  return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv4_vpn_rd,
+       show_bgp_ipv4_vpn_rd_cmd,
+       "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 0);
+}
+
+DEFUN (show_bgp_ipv6_vpn_rd,
+       show_bgp_ipv6_vpn_rd_cmd,
+       "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 0);
+}
+
+
+DEFUN (show_bgp_ipv4_vpn_tags,
+       show_bgp_ipv4_vpn_tags_cmd,
+       "show bgp ipv4 vpn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display BGP tags for prefixes\n")
+{
+  return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_normal, NULL,  1);
+}
+DEFUN (show_bgp_ipv6_vpn_tags,
+       show_bgp_ipv6_vpn_tags_cmd,
+       "show bgp ipv6 vpn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display BGP tags for prefixes\n")
+{
+  return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL,  1);
+}
+
+DEFUN (show_bgp_ipv4_vpn_rd_tags,
+       show_bgp_ipv4_vpn_rd_tags_cmd,
+       "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Display BGP tags for prefixes\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 1);
+}
+DEFUN (show_bgp_ipv6_vpn_rd_tags,
+       show_bgp_ipv6_vpn_rd_tags_cmd,
+       "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn tags",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Display BGP tags for prefixes\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 1);
+}
+
+DEFUN (show_bgp_ipv4_vpn_neighbor_routes,
+       show_bgp_ipv4_vpn_neighbor_routes_cmd,
+       "show bgp ipv4 vpn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  union sockunion su;
+  struct peer *peer;
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv6_vpn_neighbor_routes,
+       show_bgp_ipv6_vpn_neighbor_routes_cmd,
+       "show bgp ipv6 vpn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  union sockunion su;
+  struct peer *peer;
+
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_neighbor, &su, 0);
+}
+
+DEFUN (show_bgp_ipv4_vpn_neighbor_advertised_routes,
+       show_bgp_ipv4_vpn_neighbor_advertised_routes_cmd,
+       "show bgp ipv4 vpn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  union sockunion su;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_vpn (vty, peer, NULL);
+}
+DEFUN (show_bgp_ipv6_vpn_neighbor_advertised_routes,
+       show_bgp_ipv6_vpn_neighbor_advertised_routes_cmd,
+       "show bgp ipv6 vpn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  union sockunion su;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_vpn (vty, peer, NULL);
+}
+
+DEFUN (show_ip_bgp_vpnv4_rd_neighbor_advertised_routes,
+       show_bgp_ipv4_vpn_rd_neighbor_advertised_routes_cmd,
+       "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  struct prefix_rd prd;
+  union sockunion su;
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_vpn (vty, peer, &prd);
+}
+DEFUN (show_ip_bgp_vpnv6_rd_neighbor_advertised_routes,
+       show_bgp_ipv6_vpn_rd_neighbor_advertised_routes_cmd,
+       "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+  struct prefix_rd prd;
+  union sockunion su;
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return show_adj_route_vpn (vty, peer, &prd);
+}
+
+DEFUN (show_bgp_ipv4_vpn_rd_neighbor_routes,
+       show_bgp_ipv4_vpn_rd_neighbor_routes_cmd,
+       "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  int ret;
+  union sockunion su;
+  struct peer *peer;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (str2sockunion(argv[1], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_neighbor, &su, 0);
+}
+DEFUN (show_bgp_ipv6_vpn_rd_neighbor_routes,
+       show_bgp_ipv6_vpn_rd_neighbor_routes_cmd,
+       "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  int ret;
+  union sockunion su;
+  struct peer *peer;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (str2sockunion(argv[1], &su))
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_neighbor, &su, 0);
+}
+
+void
+bgp_mplsvpn_init (void)
+{
+  install_element (BGP_VPNV4_NODE, &vpnv4_network_cmd);
+  install_element (BGP_VPNV4_NODE, &vpnv4_network_route_map_cmd);
+  install_element (BGP_VPNV4_NODE, &no_vpnv4_network_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_neighbor_routes_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_tags_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_neighbor_advertised_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_neighbor_routes_cmd);
+}
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
new file mode 100644 (file)
index 0000000..3299b9c
--- /dev/null
@@ -0,0 +1,51 @@
+/* MPLS-VPN
+   Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_MPLSVPN_H
+#define _QUAGGA_BGP_MPLSVPN_H
+
+#define RD_TYPE_AS      0
+#define RD_TYPE_IP      1
+#define RD_TYPE_AS4     2
+
+#define RD_ADDRSTRLEN  28
+
+struct rd_as
+{
+  u_int16_t type;
+  as_t as;
+  u_int32_t val;
+};
+
+struct rd_ip
+{
+  u_int16_t type;
+  struct in_addr ip;
+  u_int16_t val;
+};
+
+extern void bgp_mplsvpn_init (void);
+extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *);
+extern u_int32_t decode_label (u_char *);
+extern int str2prefix_rd (const char *, struct prefix_rd *);
+extern int str2tag (const char *, u_char *);
+extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
+
+#endif /* _QUAGGA_BGP_MPLSVPN_H */
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
new file mode 100644 (file)
index 0000000..1604239
--- /dev/null
@@ -0,0 +1,567 @@
+/* BGP network related fucntions
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "memory.h"
+#include "log.h"
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "privs.h"
+#include "linklist.h"
+#include "network.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_network.h"
+
+extern struct zebra_privs_t bgpd_privs;
+
+/* BGP listening socket. */
+struct bgp_listener
+{
+  int fd;
+  union sockunion su;
+  struct thread *thread;
+};
+
+/*
+ * Set MD5 key for the socket, for the given IPv4 peer address.
+ * If the password is NULL or zero-length, the option will be disabled.
+ */
+static int
+bgp_md5_set_socket (int socket, union sockunion *su, const char *password)
+{
+  int ret = -1;
+  int en = ENOSYS;
+  
+  assert (socket >= 0);
+  
+#if HAVE_DECL_TCP_MD5SIG  
+  ret = sockopt_tcp_signature (socket, su, password);
+  en  = errno;
+#endif /* HAVE_TCP_MD5SIG */
+  
+  if (ret < 0)
+    zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s",
+          socket, safe_strerror (en));
+
+  return ret;
+}
+
+/* Helper for bgp_connect */
+static int
+bgp_md5_set_connect (int socket, union sockunion *su, const char *password)
+{
+  int ret = -1;
+
+#if HAVE_DECL_TCP_MD5SIG  
+  if ( bgpd_privs.change (ZPRIVS_RAISE) )
+    {
+      zlog_err ("%s: could not raise privs", __func__);
+      return ret;
+    }
+  
+  ret = bgp_md5_set_socket (socket, su, password);
+
+  if (bgpd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("%s: could not lower privs", __func__);
+#endif /* HAVE_TCP_MD5SIG */
+  
+  return ret;
+}
+
+int
+bgp_md5_set (struct peer *peer)
+{
+  struct listnode *node;
+  int ret = 0;
+  struct bgp_listener *listener;
+
+  if ( bgpd_privs.change (ZPRIVS_RAISE) )
+    {
+      zlog_err ("%s: could not raise privs", __func__);
+      return -1;
+    }
+  
+  /* Just set the password on the listen socket(s). Outbound connections
+   * are taken care of in bgp_connect() below.
+   */
+  for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
+    if (listener->su.sa.sa_family == peer->su.sa.sa_family)
+      {
+       ret = bgp_md5_set_socket (listener->fd, &peer->su, peer->password);
+       break;
+      }
+
+  if (bgpd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("%s: could not lower privs", __func__);
+  
+  return ret;
+}
+
+/* Update BGP socket send buffer size */
+static void
+bgp_update_sock_send_buffer_size (int fd)
+{
+  int size = BGP_SOCKET_SNDBUF_SIZE;
+  int optval;
+  socklen_t optlen = sizeof(optval);
+
+  if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0)
+    {
+      zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno));
+      return;
+    }
+  if (optval < size)
+    {
+      if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
+        {
+          zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno));
+        }
+    }
+}
+
+void
+bgp_set_socket_ttl (struct peer *peer, int bgp_sock)
+{
+  char buf[INET_ADDRSTRLEN];
+  int ret, ttl, minttl;
+
+  if (bgp_sock < 0)
+    return;
+
+  if (peer->gtsm_hops)
+    {
+      ttl = 255;
+      minttl = 256 - peer->gtsm_hops;
+    }
+  else
+    {
+      ttl = peer_ttl (peer);
+      minttl = 0;
+    }
+
+  ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, ttl);
+  if (ret)
+    zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d",
+              __func__,
+              inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
+              errno);
+
+  ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock, minttl);
+  if (ret && (errno != ENOTSUP || minttl))
+    zlog_err ("%s: Can't set MinTTL on peer (rtrid %s) socket, err = %d",
+              __func__,
+              inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
+              errno);
+}
+
+/* Accept bgp connection. */
+static int
+bgp_accept (struct thread *thread)
+{
+  int bgp_sock;
+  int accept_sock;
+  union sockunion su;
+  struct bgp_listener *listener = THREAD_ARG(thread);
+  struct peer *peer;
+  struct peer *peer1;
+  char buf[SU_ADDRSTRLEN];
+
+  /* Register accept thread. */
+  accept_sock = THREAD_FD (thread);
+  if (accept_sock < 0)
+    {
+      zlog_err ("accept_sock is nevative value %d", accept_sock);
+      return -1;
+    }
+  listener->thread = thread_add_read (bm->master, bgp_accept, listener, accept_sock);
+
+  /* Accept client connection. */
+  bgp_sock = sockunion_accept (accept_sock, &su);
+  if (bgp_sock < 0)
+    {
+      zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno));
+      return -1;
+    }
+  set_nonblocking (bgp_sock);
+
+  /* Set socket send buffer size */
+  bgp_update_sock_send_buffer_size(bgp_sock);
+
+  if (BGP_DEBUG (events, EVENTS))
+    zlog_debug ("[Event] BGP connection from host %s:%d", 
+                inet_sutop (&su, buf), sockunion_get_port (&su));
+  
+  /* Check remote IP address */
+  peer1 = peer_lookup (NULL, &su);
+  /* We could perhaps just drop new connections from already Established
+   * peers here.
+   */
+  if (! peer1 || peer1->status == Idle || peer1->status > Established)
+    {
+      if (BGP_DEBUG (events, EVENTS))
+       {
+         if (! peer1)
+           zlog_debug ("[Event] BGP connection IP address %s is not configured",
+                      inet_sutop (&su, buf));
+         else
+           zlog_debug ("[Event] BGP connection IP address %s is %s state",
+                      inet_sutop (&su, buf),
+                      LOOKUP (bgp_status_msg, peer1->status));
+       }
+      close (bgp_sock);
+      return -1;
+    }
+
+  bgp_set_socket_ttl (peer1, bgp_sock);
+
+  /* Make dummy peer until read Open packet. */
+  if (BGP_DEBUG (events, EVENTS))
+    zlog_debug ("[Event] Make dummy peer structure until read Open packet");
+
+  {
+    char buf[SU_ADDRSTRLEN];
+
+    peer = peer_create_accept (peer1->bgp);
+    peer->su = su;
+    peer->fd = bgp_sock;
+    peer->status = Active;
+
+    /* Config state that should affect OPEN packet must be copied over */
+    peer->local_id = peer1->local_id;
+    peer->v_holdtime = peer1->v_holdtime;
+    peer->v_keepalive = peer1->v_keepalive;
+    peer->local_as = peer1->local_as;
+    peer->change_local_as = peer1->change_local_as;
+    peer->flags = peer1->flags;
+    peer->sflags = peer1->sflags;
+  #define PEER_ARRAY_COPY(D,S,A) \
+    memcpy ((D)->A, (S)->A, sizeof (((D)->A)[0][0])*AFI_MAX*SAFI_MAX);
+    PEER_ARRAY_COPY(peer, peer1, afc);
+    PEER_ARRAY_COPY(peer, peer1, af_flags);
+  #undef PEER_ARRAY_COPY
+  
+    /* Make peer's address string. */
+    sockunion2str (&su, buf, SU_ADDRSTRLEN);
+    peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
+    
+    SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER);
+  }
+
+  BGP_EVENT_ADD (peer, TCP_connection_open);
+
+  return 0;
+}
+
+/* BGP socket bind. */
+static int
+bgp_bind (struct peer *peer)
+{
+#ifdef SO_BINDTODEVICE
+  int ret;
+  struct ifreq ifreq;
+  int myerrno;
+
+  if (! peer->ifname)
+    return 0;
+
+  strncpy ((char *)&ifreq.ifr_name, peer->ifname, sizeof (ifreq.ifr_name));
+
+  if ( bgpd_privs.change (ZPRIVS_RAISE) )
+       zlog_err ("bgp_bind: could not raise privs");
+  
+  ret = setsockopt (peer->fd, SOL_SOCKET, SO_BINDTODEVICE, 
+                   &ifreq, sizeof (ifreq));
+  myerrno = errno;
+  
+  if (bgpd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("bgp_bind: could not lower privs");
+
+  if (ret < 0)
+    {
+      zlog (peer->log, LOG_INFO, "bind to interface %s failed, errno=%d",
+            peer->ifname, myerrno);
+      return ret;
+    }
+#endif /* SO_BINDTODEVICE */
+  return 0;
+}
+
+static int
+bgp_update_address (struct interface *ifp, const union sockunion *dst,
+                   union sockunion *addr)
+{
+  struct prefix *p, *sel, d;
+  struct connected *connected;
+  struct listnode *node;
+  int common;
+
+  sockunion2hostprefix (dst, &d);
+  sel = NULL;
+  common = -1;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+    {
+      p = connected->address;
+      if (p->family != d.family)
+       continue;
+      if (prefix_common_bits (p, &d) > common)
+       {
+         sel = p;
+         common = prefix_common_bits (sel, &d);
+       }
+    }
+
+  if (!sel)
+    return 1;
+
+  prefix2sockunion (sel, addr);
+  return 0;
+}
+
+/* Update source selection.  */
+static void
+bgp_update_source (struct peer *peer)
+{
+  struct interface *ifp;
+  union sockunion addr;
+
+  /* Source is specified with interface name.  */
+  if (peer->update_if)
+    {
+      ifp = if_lookup_by_name (peer->update_if);
+      if (! ifp)
+       return;
+
+      if (bgp_update_address (ifp, &peer->su, &addr))
+       return;
+
+      sockunion_bind (peer->fd, &addr, 0, &addr);
+    }
+
+  /* Source is specified with IP address.  */
+  if (peer->update_source)
+    sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source);
+}
+
+/* BGP try to connect to the peer.  */
+int
+bgp_connect (struct peer *peer)
+{
+  ifindex_t ifindex = 0;
+
+  /* Make socket for the peer. */
+  peer->fd = sockunion_socket (&peer->su);
+  if (peer->fd < 0)
+    return -1;
+
+  set_nonblocking (peer->fd);
+
+  /* Set socket send buffer size */
+  bgp_update_sock_send_buffer_size(peer->fd);
+
+  bgp_set_socket_ttl (peer, peer->fd);
+
+  sockopt_reuseaddr (peer->fd);
+  sockopt_reuseport (peer->fd);
+  
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  if (bgpd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs", __func__);
+  if (sockunion_family (&peer->su) == AF_INET)
+    setsockopt_ipv4_tos (peer->fd, IPTOS_PREC_INTERNETCONTROL);
+  else if (sockunion_family (&peer->su) == AF_INET6)
+    setsockopt_ipv6_tclass (peer->fd, IPTOS_PREC_INTERNETCONTROL);
+  if (bgpd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs", __func__);
+#endif
+
+  if (peer->password)
+    bgp_md5_set_connect (peer->fd, &peer->su, peer->password);
+
+  /* Bind socket. */
+  bgp_bind (peer);
+
+  /* Update source bind. */
+  bgp_update_source (peer);
+
+  if (peer->ifname)
+    ifindex = ifname2ifindex (peer->ifname);
+
+  if (BGP_DEBUG (events, EVENTS))
+    plog_debug (peer->log, "%s [Event] Connect start to %s fd %d",
+              peer->host, peer->host, peer->fd);
+
+  /* Connect to the remote peer. */
+  return sockunion_connect (peer->fd, &peer->su, htons (peer->port), ifindex);
+}
+
+/* After TCP connection is established.  Get local address and port. */
+void
+bgp_getsockname (struct peer *peer)
+{
+  if (peer->su_local)
+    {
+      sockunion_free (peer->su_local);
+      peer->su_local = NULL;
+    }
+
+  if (peer->su_remote)
+    {
+      sockunion_free (peer->su_remote);
+      peer->su_remote = NULL;
+    }
+
+  peer->su_local = sockunion_getsockname (peer->fd);
+  peer->su_remote = sockunion_getpeername (peer->fd);
+
+  bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer);
+}
+
+
+static int
+bgp_listener (int sock, struct sockaddr *sa, socklen_t salen)
+{
+  struct bgp_listener *listener;
+  int ret, en;
+
+  sockopt_reuseaddr (sock);
+  sockopt_reuseport (sock);
+
+  if (bgpd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs", __func__);
+
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  if (sa->sa_family == AF_INET)
+    setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL);
+  else if (sa->sa_family == AF_INET6)
+    setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL);
+#endif
+
+  sockopt_v6only (sa->sa_family, sock);
+
+  ret = bind (sock, sa, salen);
+  en = errno;
+  if (bgpd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs", __func__);
+
+  if (ret < 0)
+    {
+      zlog_err ("bind: %s", safe_strerror (en));
+      return ret;
+    }
+
+  ret = listen (sock, 3);
+  if (ret < 0)
+    {
+      zlog_err ("listen: %s", safe_strerror (errno));
+      return ret;
+    }
+
+  listener = XMALLOC (MTYPE_BGP_LISTENER, sizeof(*listener));
+  listener->fd = sock;
+  memcpy(&listener->su, sa, salen);
+  listener->thread = thread_add_read (bm->master, bgp_accept, listener, sock);
+  listnode_add (bm->listen_sockets, listener);
+
+  return 0;
+}
+
+/* IPv6 supported version of BGP server socket setup.  */
+int
+bgp_socket (unsigned short port, const char *address)
+{
+  struct addrinfo *ainfo;
+  struct addrinfo *ainfo_save;
+  static const struct addrinfo req = {
+    .ai_family = AF_UNSPEC,
+    .ai_flags = AI_PASSIVE,
+    .ai_socktype = SOCK_STREAM,
+  };
+  int ret, count;
+  char port_str[BUFSIZ];
+
+  snprintf (port_str, sizeof(port_str), "%d", port);
+  port_str[sizeof (port_str) - 1] = '\0';
+
+  ret = getaddrinfo (address, port_str, &req, &ainfo_save);
+  if (ret != 0)
+    {
+      zlog_err ("getaddrinfo: %s", gai_strerror (ret));
+      return -1;
+    }
+
+  count = 0;
+  for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
+    {
+      int sock;
+
+      if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
+       continue;
+     
+      sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
+      if (sock < 0)
+       {
+         zlog_err ("socket: %s", safe_strerror (errno));
+         continue;
+       }
+       
+      /* if we intend to implement ttl-security, this socket needs ttl=255 */
+      sockopt_ttl (ainfo->ai_family, sock, MAXTTL);
+      
+      ret = bgp_listener (sock, ainfo->ai_addr, ainfo->ai_addrlen);
+      if (ret == 0)
+       ++count;
+      else
+       close(sock);
+    }
+  freeaddrinfo (ainfo_save);
+  if (count == 0)
+    {
+      zlog_err ("%s: no usable addresses", __func__);
+      return -1;
+    }
+
+  return 0;
+}
+
+void
+bgp_close (void)
+{
+  struct listnode *node, *next;
+  struct bgp_listener *listener;
+
+  for (ALL_LIST_ELEMENTS (bm->listen_sockets, node, next, listener))
+    {
+      thread_cancel (listener->thread);
+      close (listener->fd);
+      listnode_delete (bm->listen_sockets, listener);
+      XFREE (MTYPE_BGP_LISTENER, listener);
+    }
+}
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
new file mode 100644 (file)
index 0000000..31995ca
--- /dev/null
@@ -0,0 +1,34 @@
+/* BGP network related header
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_NETWORK_H
+#define _QUAGGA_BGP_NETWORK_H
+
+#define BGP_SOCKET_SNDBUF_SIZE 65536
+
+extern int bgp_socket (unsigned short, const char *);
+extern void bgp_close (void);
+extern int bgp_connect (struct peer *);
+extern void bgp_getsockname (struct peer *);
+
+extern void bgp_set_socket_ttl (struct peer *peer, int bgp_sock);
+extern int bgp_md5_set (struct peer *);
+
+#endif /* _QUAGGA_BGP_NETWORK_H */
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
new file mode 100644 (file)
index 0000000..479ef94
--- /dev/null
@@ -0,0 +1,567 @@
+/* BGP nexthop scan
+   Copyright (C) 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "log.h"
+#include "memory.h"
+#include "hash.h"
+#include "jhash.h"
+#include "filter.h"
+#include "nexthop.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_damp.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"       /* For ZEBRA_SERV_PATH. */
+
+
+/* Route table for next-hop lookup cache. */
+struct bgp_table *bgp_nexthop_cache_table[AFI_MAX];
+static struct bgp_table *cache1_table[AFI_MAX];
+
+/* Route table for connected route. */
+static struct bgp_table *bgp_connected_table[AFI_MAX];
+
+char *
+bnc_str (struct bgp_nexthop_cache *bnc, char *buf, int size)
+{
+  prefix2str(&(bnc->node->p), buf, size);
+  return buf;
+}
+
+void
+bnc_nexthop_free (struct bgp_nexthop_cache *bnc)
+{
+  struct nexthop *nexthop;
+  struct nexthop *next = NULL;
+
+  for (nexthop = bnc->nexthop; nexthop; nexthop = next)
+    {
+      next = nexthop->next;
+      XFREE (MTYPE_NEXTHOP, nexthop);
+    }
+}
+
+struct bgp_nexthop_cache *
+bnc_new (void)
+{
+  struct bgp_nexthop_cache *bnc;
+
+  bnc = XCALLOC (MTYPE_BGP_NEXTHOP_CACHE, sizeof (struct bgp_nexthop_cache));
+  LIST_INIT(&(bnc->paths));
+  return bnc;
+}
+
+void
+bnc_free (struct bgp_nexthop_cache *bnc)
+{
+  bnc_nexthop_free (bnc);
+  XFREE (MTYPE_BGP_NEXTHOP_CACHE, bnc);
+}
+
+/* If nexthop exists on connected network return 1. */
+int
+bgp_nexthop_onlink (afi_t afi, struct attr *attr)
+{
+  struct bgp_node *rn;
+  
+  /* Lookup the address is onlink or not. */
+  if (afi == AFI_IP)
+    {
+      rn = bgp_node_match_ipv4 (bgp_connected_table[AFI_IP], &attr->nexthop);
+      if (rn)
+       {
+         bgp_unlock_node (rn);
+         return 1;
+       }
+    }
+  else if (afi == AFI_IP6)
+    {
+      if (attr->extra->mp_nexthop_len == 32)
+       return 1;
+      else if (attr->extra->mp_nexthop_len == 16)
+       {
+         if (IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_global))
+           return 1;
+
+         rn = bgp_node_match_ipv6 (bgp_connected_table[AFI_IP6],
+                                     &attr->extra->mp_nexthop_global);
+         if (rn)
+           {
+             bgp_unlock_node (rn);
+             return 1;
+           }
+       }
+    }
+  return 0;
+}
+
+/* BGP own address structure */
+struct bgp_addr
+{
+  struct in_addr addr;
+  int refcnt;
+};
+
+static struct hash *bgp_address_hash;
+
+static void *
+bgp_address_hash_alloc (void *p)
+{
+  struct in_addr *val = p;
+  struct bgp_addr *addr;
+
+  addr = XMALLOC (MTYPE_BGP_ADDR, sizeof (struct bgp_addr));
+  addr->refcnt = 0;
+  addr->addr.s_addr = val->s_addr;
+
+  return addr;
+}
+
+static unsigned int
+bgp_address_hash_key_make (void *p)
+{
+  const struct bgp_addr *addr = p;
+
+  return jhash_1word(addr->addr.s_addr, 0);
+}
+
+static int
+bgp_address_hash_cmp (const void *p1, const void *p2)
+{
+  const struct bgp_addr *addr1 = p1;
+  const struct bgp_addr *addr2 = p2;
+
+  return addr1->addr.s_addr == addr2->addr.s_addr;
+}
+
+void
+bgp_address_init (void)
+{
+  bgp_address_hash = hash_create (bgp_address_hash_key_make,
+                                  bgp_address_hash_cmp);
+}
+
+void
+bgp_address_destroy (void)
+{
+  if (bgp_address_hash == NULL)
+    return;
+
+  hash_clean(bgp_address_hash, NULL);
+  hash_free(bgp_address_hash);
+  bgp_address_hash = NULL;
+}
+
+static void
+bgp_address_add (struct prefix *p)
+{
+  struct bgp_addr tmp;
+  struct bgp_addr *addr;
+
+  tmp.addr = p->u.prefix4;
+
+  addr = hash_get (bgp_address_hash, &tmp, bgp_address_hash_alloc);
+  if (!addr)
+    return;
+
+  addr->refcnt++;
+}
+
+static void
+bgp_address_del (struct prefix *p)
+{
+  struct bgp_addr tmp;
+  struct bgp_addr *addr;
+
+  tmp.addr = p->u.prefix4;
+
+  addr = hash_lookup (bgp_address_hash, &tmp);
+  /* may have been deleted earlier by bgp_interface_down() */
+  if (addr == NULL)
+    return;
+
+  addr->refcnt--;
+
+  if (addr->refcnt == 0)
+    {
+      hash_release (bgp_address_hash, addr);
+      XFREE (MTYPE_BGP_ADDR, addr);
+    }
+}
+
+
+struct bgp_connected_ref
+{
+  unsigned int refcnt;
+};
+
+void
+bgp_connected_add (struct connected *ifc)
+{
+  struct prefix p;
+  struct prefix *addr;
+  struct interface *ifp;
+  struct bgp_node *rn;
+  struct bgp_connected_ref *bc;
+
+  ifp = ifc->ifp;
+
+  if (! ifp)
+    return;
+
+  if (if_is_loopback (ifp))
+    return;
+
+  addr = ifc->address;
+
+  p = *(CONNECTED_PREFIX(ifc));
+  if (addr->family == AF_INET)
+    {
+      apply_mask_ipv4 ((struct prefix_ipv4 *) &p);
+
+      if (prefix_ipv4_any ((struct prefix_ipv4 *) &p))
+       return;
+
+      bgp_address_add (addr);
+
+      rn = bgp_node_get (bgp_connected_table[AFI_IP], (struct prefix *) &p);
+      if (rn->info)
+       {
+         bc = rn->info;
+         bc->refcnt++;
+       }
+      else
+       {
+         bc = XCALLOC (MTYPE_BGP_CONN, sizeof (struct bgp_connected_ref));
+         bc->refcnt = 1;
+         rn->info = bc;
+       }
+    }
+  else if (addr->family == AF_INET6)
+    {
+      apply_mask_ipv6 ((struct prefix_ipv6 *) &p);
+
+      if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6))
+       return;
+
+      if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6))
+       return;
+
+      rn = bgp_node_get (bgp_connected_table[AFI_IP6], (struct prefix *) &p);
+      if (rn->info)
+       {
+         bc = rn->info;
+         bc->refcnt++;
+       }
+      else
+       {
+         bc = XCALLOC (MTYPE_BGP_CONN, sizeof (struct bgp_connected_ref));
+         bc->refcnt = 1;
+         rn->info = bc;
+       }
+    }
+}
+
+void
+bgp_connected_delete (struct connected *ifc)
+{
+  struct prefix p;
+  struct prefix *addr;
+  struct interface *ifp;
+  struct bgp_node *rn;
+  struct bgp_connected_ref *bc;
+
+  ifp = ifc->ifp;
+
+  if (if_is_loopback (ifp))
+    return;
+
+  addr = ifc->address;
+
+  p = *(CONNECTED_PREFIX(ifc));
+  if (addr->family == AF_INET)
+    {
+      apply_mask_ipv4 ((struct prefix_ipv4 *) &p);
+
+      if (prefix_ipv4_any ((struct prefix_ipv4 *) &p))
+       return;
+
+      bgp_address_del (addr);
+
+      rn = bgp_node_lookup (bgp_connected_table[AFI_IP], &p);
+      if (! rn)
+       return;
+
+      bc = rn->info;
+      bc->refcnt--;
+      if (bc->refcnt == 0)
+       {
+         XFREE (MTYPE_BGP_CONN, bc);
+         rn->info = NULL;
+       }
+      bgp_unlock_node (rn);
+      bgp_unlock_node (rn);
+    }
+  else if (addr->family == AF_INET6)
+    {
+      apply_mask_ipv6 ((struct prefix_ipv6 *) &p);
+
+      if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6))
+       return;
+
+      if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6))
+       return;
+
+      rn = bgp_node_lookup (bgp_connected_table[AFI_IP6], (struct prefix *) &p);
+      if (! rn)
+       return;
+
+      bc = rn->info;
+      bc->refcnt--;
+      if (bc->refcnt == 0)
+       {
+         XFREE (MTYPE_BGP_CONN, bc);
+         rn->info = NULL;
+       }
+      bgp_unlock_node (rn);
+      bgp_unlock_node (rn);
+    }
+}
+
+int
+bgp_nexthop_self (struct attr *attr)
+{
+  struct bgp_addr tmp, *addr;
+
+  tmp.addr = attr->nexthop;
+
+  addr = hash_lookup (bgp_address_hash, &tmp);
+  if (addr)
+    return 1;
+
+  return 0;
+}
+
+int
+bgp_multiaccess_check_v4 (struct in_addr nexthop, struct peer *peer)
+{
+  struct bgp_node *rn1;
+  struct bgp_node *rn2;
+  struct prefix p;
+  int ret;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.u.prefix4 = nexthop;
+
+  rn1 = bgp_node_match (bgp_connected_table[AFI_IP], &p);
+  if (!rn1)
+    return 0;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.u.prefix4 = peer->su.sin.sin_addr;
+
+  rn2 = bgp_node_match (bgp_connected_table[AFI_IP], &p);
+  if (!rn2)
+    {
+      bgp_unlock_node(rn1);
+      return 0;
+    }
+
+  ret = (rn1 == rn2) ? 1 : 0;
+
+  bgp_unlock_node(rn1);
+  bgp_unlock_node(rn2);
+
+  return (ret);
+}
+
+static int
+show_ip_bgp_nexthop_table (struct vty *vty, int detail)
+{
+  struct bgp_node *rn;
+  struct bgp_nexthop_cache *bnc;
+  char buf[INET6_ADDRSTRLEN];
+  struct nexthop *nexthop;
+  time_t tbuf;
+  afi_t afi;
+
+  vty_out (vty, "Current BGP nexthop cache:%s", VTY_NEWLINE);
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    {
+      if (!bgp_nexthop_cache_table[afi])
+        continue;
+      
+      for (rn = bgp_table_top (bgp_nexthop_cache_table[afi]); rn; rn = bgp_route_next (rn))
+       {
+         if ((bnc = rn->info) != NULL)
+           {
+             if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID))
+               {
+                 vty_out (vty, " %s valid [IGP metric %d], #paths %d%s",
+                          inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)),
+                          bnc->metric, bnc->path_count, VTY_NEWLINE);
+                 if (detail)
+                   for (nexthop = bnc->nexthop ; nexthop; nexthop = nexthop->next)
+                     switch (nexthop->type)
+                       {
+                       case NEXTHOP_TYPE_IPV6:
+                         vty_out (vty, "  gate %s%s",
+                                  inet_ntop (AF_INET6, &nexthop->gate.ipv6,
+                                             buf, INET6_ADDRSTRLEN), VTY_NEWLINE);
+                         break;
+                       case NEXTHOP_TYPE_IPV6_IFINDEX:
+                         vty_out(vty, "  gate %s, if %s%s",
+                                 inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+                                           INET6_ADDRSTRLEN),
+                                 ifindex2ifname(nexthop->ifindex),
+                                 VTY_NEWLINE);
+                         break;
+                       case NEXTHOP_TYPE_IPV4:
+                         vty_out (vty, "  gate %s%s",
+                                  inet_ntop (AF_INET, &nexthop->gate.ipv4, buf,
+                                             INET6_ADDRSTRLEN), VTY_NEWLINE);
+                         break;
+                       case NEXTHOP_TYPE_IFINDEX:
+                         vty_out (vty, "  if %s%s",
+                                  ifindex2ifname(nexthop->ifindex), VTY_NEWLINE);
+                         break;
+                       case NEXTHOP_TYPE_IPV4_IFINDEX:
+                         vty_out (vty, "  gate %s, if %s%s",
+                                  inet_ntop(AF_INET, &nexthop->gate.ipv4, buf,
+                                            INET6_ADDRSTRLEN),
+                                  ifindex2ifname(nexthop->ifindex), VTY_NEWLINE);
+                         break;
+                       default:
+                         vty_out (vty, "  invalid nexthop type %u%s",
+                                  nexthop->type, VTY_NEWLINE);
+                       }
+               }
+             else
+               vty_out (vty, " %s invalid%s",
+                        inet_ntop (AF_INET, &rn->p.u.prefix, buf, sizeof (buf)), VTY_NEWLINE);
+#ifdef HAVE_CLOCK_MONOTONIC
+             tbuf = time(NULL) - (bgp_clock() - bnc->last_update);
+             vty_out (vty, "  Last update: %s", ctime(&tbuf));
+#else
+             vty_out (vty, "  Last update: %s", ctime(&bnc->uptime));
+#endif /* HAVE_CLOCK_MONOTONIC */
+             vty_out(vty, "%s", VTY_NEWLINE);
+           }
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_nexthop,
+       show_ip_bgp_nexthop_cmd,
+       "show ip bgp nexthop",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP nexthop table\n")
+{
+  return show_ip_bgp_nexthop_table (vty, 0);
+}
+
+DEFUN (show_ip_bgp_nexthop_detail,
+       show_ip_bgp_nexthop_detail_cmd,
+       "show ip bgp nexthop detail",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP nexthop table\n")
+{
+  return show_ip_bgp_nexthop_table (vty, 1);
+}
+
+void
+bgp_scan_init (void)
+{
+  cache1_table[AFI_IP] = bgp_table_init (AFI_IP, SAFI_UNICAST);
+  bgp_nexthop_cache_table[AFI_IP] = cache1_table[AFI_IP];
+
+  bgp_connected_table[AFI_IP] = bgp_table_init (AFI_IP, SAFI_UNICAST);
+
+  cache1_table[AFI_IP6] = bgp_table_init (AFI_IP6, SAFI_UNICAST);
+  bgp_nexthop_cache_table[AFI_IP6] = cache1_table[AFI_IP6];
+  bgp_connected_table[AFI_IP6] = bgp_table_init (AFI_IP6, SAFI_UNICAST);
+  
+  cache1_table[AFI_ETHER] = bgp_table_init (AFI_ETHER, SAFI_UNICAST);
+  bgp_nexthop_cache_table[AFI_ETHER] = cache1_table[AFI_ETHER];
+  bgp_connected_table[AFI_ETHER] = bgp_table_init (AFI_ETHER, SAFI_UNICAST);
+}
+
+void
+bgp_scan_vty_init()
+{
+  install_element (VIEW_NODE, &show_ip_bgp_nexthop_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_nexthop_detail_cmd);
+}
+
+void
+bgp_scan_finish (void)
+{
+  if (cache1_table[AFI_IP])
+    bgp_table_unlock (cache1_table[AFI_IP]);
+  cache1_table[AFI_IP] = NULL;
+
+  if (bgp_connected_table[AFI_IP])
+    bgp_table_unlock (bgp_connected_table[AFI_IP]);
+  bgp_connected_table[AFI_IP] = NULL;
+
+  if (cache1_table[AFI_IP6])
+    bgp_table_unlock (cache1_table[AFI_IP6]);
+  cache1_table[AFI_IP6] = NULL;
+
+  if (bgp_connected_table[AFI_IP6])
+    bgp_table_unlock (bgp_connected_table[AFI_IP6]);
+  bgp_connected_table[AFI_IP6] = NULL;
+
+  if (cache1_table[AFI_ETHER])
+    bgp_table_unlock (cache1_table[AFI_ETHER]);
+  cache1_table[AFI_ETHER] = NULL;
+  
+  if (bgp_connected_table[AFI_ETHER])
+    bgp_table_unlock (bgp_connected_table[AFI_ETHER]);
+  bgp_connected_table[AFI_ETHER] = NULL;
+
+}
+
+void
+bgp_scan_destroy (void)
+{
+  bgp_scan_finish();
+}
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
new file mode 100644 (file)
index 0000000..fe4f5ad
--- /dev/null
@@ -0,0 +1,84 @@
+/* BGP nexthop scan
+   Copyright (C) 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_NEXTHOP_H
+#define _QUAGGA_BGP_NEXTHOP_H
+
+#include "if.h"
+#include "queue.h"
+#include "prefix.h"
+
+#define NEXTHOP_FAMILY(nexthop_len) ( \
+  ((nexthop_len) ==  4 ||             \
+   (nexthop_len) == 12 ? AF_INET :    \
+  ((nexthop_len) == 16 ||             \
+   (nexthop_len) == 24 ||             \
+   (nexthop_len) == 48 ? AF_INET6 :   \
+  AF_UNSPEC))                         \
+)
+
+/* BGP nexthop cache value structure. */
+struct bgp_nexthop_cache
+{
+  /* IGP route's metric. */
+  u_int32_t metric;
+
+  /* Nexthop number and nexthop linked list.*/
+  u_char nexthop_num;
+  struct nexthop *nexthop;
+  time_t last_update;
+  u_int16_t flags;
+
+#define BGP_NEXTHOP_VALID             (1 << 0)
+#define BGP_NEXTHOP_REGISTERED        (1 << 1)
+#define BGP_NEXTHOP_CONNECTED         (1 << 2)
+#define BGP_NEXTHOP_PEER_NOTIFIED     (1 << 3)
+
+  u_int16_t change_flags;
+
+#define BGP_NEXTHOP_CHANGED           (1 << 0)
+#define BGP_NEXTHOP_METRIC_CHANGED    (1 << 1)
+#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
+
+  struct bgp_node *node;
+  void *nht_info;              /* In BGP, peer session */
+  LIST_HEAD(path_list, bgp_info) paths;
+  unsigned int path_count;
+};
+
+extern int bgp_nexthop_lookup (afi_t, struct peer *peer, struct bgp_info *,
+                       int *, int *);
+extern void bgp_connected_add (struct connected *c);
+extern void bgp_connected_delete (struct connected *c);
+extern int bgp_multiaccess_check_v4 (struct in_addr, struct peer *);
+extern int bgp_config_write_scan_time (struct vty *);
+extern int bgp_nexthop_onlink (afi_t, struct attr *);
+extern int bgp_nexthop_self (struct attr *);
+extern void bgp_address_init (void);
+extern void bgp_address_destroy (void);
+extern void bgp_scan_destroy (void);
+extern struct bgp_nexthop_cache *bnc_new(void);
+extern void bnc_free(struct bgp_nexthop_cache *bnc);
+extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc);
+extern char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size);
+
+extern void bgp_scan_init (void);
+extern void bgp_scan_vty_init (void);
+#endif /* _QUAGGA_BGP_NEXTHOP_H */
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
new file mode 100644 (file)
index 0000000..1158ab1
--- /dev/null
@@ -0,0 +1,612 @@
+/* BGP Nexthop tracking
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "log.h"
+#include "memory.h"
+#include "nexthop.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_zebra.h"
+
+extern struct zclient *zclient;
+extern struct bgp_table *bgp_nexthop_cache_table[AFI_MAX];
+
+static void register_nexthop(struct bgp_nexthop_cache *bnc);
+static void unregister_nexthop (struct bgp_nexthop_cache *bnc);
+static void evaluate_paths(struct bgp_nexthop_cache *bnc);
+static int make_prefix(int afi, struct bgp_info *ri, struct prefix *p);
+static void path_nh_map(struct bgp_info *path, struct bgp_nexthop_cache *bnc,
+                       int keep);
+
+int
+bgp_nexthop_check (struct bgp_info *path, int connected)
+{
+  struct bgp_nexthop_cache *bnc = path->nexthop;
+
+  if (!bnc)
+    return 0;
+
+  if (BGP_DEBUG(nht, NHT))
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("%s: NHT checking %s", 
+                 __FUNCTION__,
+                 bnc_str (bnc, buf, INET6_ADDRSTRLEN));
+    }
+
+  if (connected && !(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)))
+    return 0;
+
+  return (bgp_zebra_num_connects() == 0 ||
+          CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID));
+}
+
+/* Helper to get the rn for the appropriate nexthop for path or peer.
+ * returns the locked rn - caller must bump down the refcnt.
+ *
+ * may return NULL in error cases.
+ */
+static
+struct bgp_node *
+bgp_get_nexthop_rn (struct bgp_info *path, struct peer *peer)
+{
+  struct prefix p;
+  afi_t afi;
+  
+  assert (path || peer);
+  
+  if (!(path || peer))
+    return NULL;
+  
+  if (path)
+    {
+      afi = family2afi (path->net->p.family);
+      if (make_prefix(afi, path, &p) < 0)
+       return NULL;
+    }
+  else
+    {
+      afi = family2afi(peer->su.sa.sa_family);
+      if (afi == AFI_IP)
+       {
+         p.family = AF_INET;
+         p.prefixlen = IPV4_MAX_BITLEN;
+         p.u.prefix4 = peer->su.sin.sin_addr;
+       }
+      else if (afi == AFI_IP6)
+       {
+         p.family = AF_INET6;
+         p.prefixlen = IPV6_MAX_BITLEN;
+         p.u.prefix6 = peer->su.sin6.sin6_addr;
+       }
+      else
+        return NULL;
+    }
+  
+  return bgp_node_get (bgp_nexthop_cache_table[afi], &p);
+}
+
+static
+struct bgp_nexthop_cache *
+bgp_find_nexthop (struct bgp_info *path, struct peer *peer)
+{
+  struct bgp_nexthop_cache *bnc = NULL;
+  struct bgp_node *rn = bgp_get_nexthop_rn (path, peer);
+  
+  if (!rn)
+    return NULL;
+  
+  bnc = rn->info;
+  bgp_unlock_node (rn);
+  
+  return bnc;
+}
+
+static void
+bgp_unlink_nexthop_check (struct bgp_nexthop_cache *bnc)
+{
+  if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info)
+    {
+      if (BGP_DEBUG(nht, NHT))
+       {
+         char buf[INET6_ADDRSTRLEN];
+         zlog_debug("bgp_unlink_nexthop: freeing bnc %s",
+                    bnc_str (bnc, buf, INET6_ADDRSTRLEN));
+       }
+      unregister_nexthop(bnc);
+      bnc->node->info = NULL;
+      bgp_unlock_node (bnc->node);
+      bnc->node = NULL;
+      bnc_free (bnc);
+    }
+}
+
+void
+bgp_unlink_nexthop (struct bgp_info *path)
+{
+  struct bgp_nexthop_cache *bnc = path->nexthop;
+
+  if (!bnc)
+    return;
+
+  if (BGP_DEBUG(nht, NHT))
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("%s: NHT unlinking %s", 
+                 __FUNCTION__, bnc_str (bnc, buf, INET6_ADDRSTRLEN));
+    }
+  
+  path_nh_map(path, NULL, 0);
+  
+  bgp_unlink_nexthop_check (bnc);
+}
+
+void
+bgp_unlink_nexthop_by_peer (struct peer *peer)
+{
+  struct bgp_nexthop_cache *bnc = bgp_find_nexthop (NULL, peer);
+     
+  if (!bnc)
+    return;
+
+  if (BGP_DEBUG(nht, NHT))
+    zlog_debug("%s: NHT unlinking %s", 
+                __FUNCTION__, peer->host);
+  
+  bnc->nht_info = NULL;
+  
+  bgp_unlink_nexthop_check (bnc);
+}
+
+int
+bgp_ensure_nexthop (struct bgp_info *ri, struct peer *peer,
+                    int connected)
+{
+  struct bgp_node *rn;
+  struct bgp_nexthop_cache *bnc;
+  
+  rn = bgp_get_nexthop_rn (ri, peer);
+  
+  if (!rn)
+    {
+      zlog_debug("%s: NHT could not ensure, failed to get rn!", 
+                 __FUNCTION__);
+      return 0;
+    }
+  
+  if (!rn->info)
+    {
+      bnc = bnc_new();
+      rn->info = bnc;
+      bnc->node = rn;
+      bgp_lock_node(rn);
+      if (connected)
+       SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED);
+    }
+
+  bnc = rn->info;
+  bgp_unlock_node (rn);
+
+  if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED))
+    register_nexthop(bnc);
+
+  if (ri)
+    {
+      path_nh_map(ri, bnc, 1); /* updates NHT ri list reference */
+
+      if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric)
+       (bgp_info_extra_get(ri))->igpmetric = bnc->metric;
+      else if (ri->extra)
+       ri->extra->igpmetric = 0;
+    }
+  else if (peer)
+    bnc->nht_info = (void *)peer; /* NHT peer reference */
+
+  if (BGP_DEBUG(nht, NHT))
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("%s: NHT ensured %s", 
+                 __FUNCTION__, bnc_str (bnc, buf, INET6_ADDRSTRLEN));
+    }
+  
+  return (bgp_zebra_num_connects() == 0 ||
+          CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID));
+}
+
+void
+bgp_parse_nexthop_update (void)
+{
+  struct stream *s;
+  struct bgp_node *rn;
+  struct bgp_nexthop_cache *bnc;
+  struct nexthop *nexthop;
+  struct nexthop *oldnh;
+  struct nexthop *nhlist_head = NULL;
+  struct nexthop *nhlist_tail = NULL;
+  uint32_t metric;
+  u_char nexthop_num;
+  struct prefix p;
+  int i;
+
+  s = zclient->ibuf;
+
+  memset(&p, 0, sizeof(struct prefix));
+  p.family = stream_getw(s);
+  p.prefixlen = stream_getc(s);
+  switch (p.family)
+    {
+    case AF_INET:
+      p.u.prefix4.s_addr = stream_get_ipv4 (s);
+      break;
+    case AF_INET6:
+      stream_get(&p.u.prefix6, s, 16);
+      break;
+    default:
+      break;
+    }
+
+  rn = bgp_node_lookup(bgp_nexthop_cache_table[family2afi(p.family)], &p);
+  if (!rn || !rn->info)
+    {
+      if (BGP_DEBUG(nht, NHT))
+       {
+         char buf[INET6_ADDRSTRLEN];
+         prefix2str(&p, buf, INET6_ADDRSTRLEN);
+         zlog_debug("parse nexthop update(%s): rn not found", buf);
+       }
+      if (rn)
+        bgp_unlock_node (rn);
+      return;
+    }
+
+  bnc = rn->info;
+  bgp_unlock_node (rn);
+  bnc->last_update = bgp_clock();
+  bnc->change_flags = 0;
+  metric = stream_getl (s);
+  nexthop_num = stream_getc (s);
+
+  /* debug print the input */
+  if (BGP_DEBUG(nht, NHT))
+    {
+      char buf[INET6_ADDRSTRLEN];
+      prefix2str(&p, buf, INET6_ADDRSTRLEN);
+      zlog_debug("parse nexthop update(%s): metric=%d, #nexthop=%d", buf,
+                metric, nexthop_num);
+    }
+
+  if (metric != bnc->metric)
+    bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED;
+
+  if(nexthop_num != bnc->nexthop_num)
+    bnc->change_flags |= BGP_NEXTHOP_CHANGED;
+
+  if (nexthop_num)
+    {
+      bnc->flags |= BGP_NEXTHOP_VALID;
+      bnc->metric = metric;
+      bnc->nexthop_num = nexthop_num;
+
+      for (i = 0; i < nexthop_num; i++)
+       {
+         nexthop = nexthop_new();
+         nexthop->type = stream_getc (s);
+         switch (nexthop->type)
+           {
+           case ZEBRA_NEXTHOP_IPV4:
+             nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s);
+             break;
+           case ZEBRA_NEXTHOP_IFINDEX:
+           case ZEBRA_NEXTHOP_IFNAME:
+             nexthop->ifindex = stream_getl (s);
+             break;
+            case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+           case ZEBRA_NEXTHOP_IPV4_IFNAME:
+             nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s);
+             nexthop->ifindex = stream_getl (s);
+             break;
+#ifdef HAVE_IPV6
+            case ZEBRA_NEXTHOP_IPV6:
+             stream_get (&nexthop->gate.ipv6, s, 16);
+             break;
+            case ZEBRA_NEXTHOP_IPV6_IFINDEX:
+           case ZEBRA_NEXTHOP_IPV6_IFNAME:
+             stream_get (&nexthop->gate.ipv6, s, 16);
+             nexthop->ifindex = stream_getl (s);
+             break;
+#endif
+            default:
+              /* do nothing */
+              break;
+           }
+
+         if (nhlist_tail)
+           {
+             nhlist_tail->next = nexthop;
+             nhlist_tail = nexthop;
+           }
+         else
+           {
+             nhlist_tail = nexthop;
+             nhlist_head = nexthop;
+           }
+
+         /* No need to evaluate the nexthop if we have already determined
+          * that there has been a change.
+          */
+         if (bnc->change_flags & BGP_NEXTHOP_CHANGED)
+           continue;
+
+         for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next)
+             if (nexthop_same_no_recurse(oldnh, nexthop))
+                 break;
+
+         if (!oldnh)
+           bnc->change_flags |= BGP_NEXTHOP_CHANGED;
+       }
+      bnc_nexthop_free(bnc);
+      bnc->nexthop = nhlist_head;
+    }
+  else
+    {
+      bnc->flags &= ~BGP_NEXTHOP_VALID;
+      UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+      bnc_nexthop_free(bnc);
+      bnc->nexthop = NULL;
+    }
+
+  evaluate_paths(bnc);
+}
+
+/**
+ * make_prefix - make a prefix structure from the path (essentially
+ * path's node.
+ */
+static int
+make_prefix (int afi, struct bgp_info *ri, struct prefix *p)
+{
+  memset (p, 0, sizeof (struct prefix));
+  switch (afi)
+    {
+    case AFI_IP:
+      p->family = AF_INET;
+      p->prefixlen = IPV4_MAX_BITLEN;
+      p->u.prefix4 = ri->attr->nexthop;
+      break;
+#ifdef HAVE_IPV6
+    case AFI_IP6:
+      if (ri->attr->extra->mp_nexthop_len != 16
+         || IN6_IS_ADDR_LINKLOCAL (&ri->attr->extra->mp_nexthop_global))
+       return -1;
+
+      p->family = AF_INET6;
+      p->prefixlen = IPV6_MAX_BITLEN;
+      p->u.prefix6 = ri->attr->extra->mp_nexthop_global;
+      break;
+#endif
+    default:
+      break;
+    }
+  return 0;
+}
+
+/**
+ * sendmsg_nexthop -- Format and send a nexthop register/Unregister
+ *   command to Zebra.
+ * ARGUMENTS:
+ *   struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ *   int command -- either ZEBRA_NEXTHOP_REGISTER or ZEBRA_NEXTHOP_UNREGISTER
+ * RETURNS:
+ *   void.
+ */
+static void
+sendmsg_nexthop (struct bgp_nexthop_cache *bnc, int command)
+{
+  struct stream *s;
+  struct prefix *p;
+  int ret;
+
+  /* Check socket. */
+  if (!zclient || zclient->sock < 0)
+    {
+      zlog_debug("%s: Can't send NH register, Zebra client not established",
+                __FUNCTION__);
+      return;
+    }
+
+  p = &(bnc->node->p);
+  s = zclient->obuf;
+  stream_reset (s);
+  zclient_create_header (s, command, VRF_DEFAULT);
+  if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
+    stream_putc(s, 1);
+  else
+    stream_putc(s, 0);
+
+  stream_putw(s, PREFIX_FAMILY(p));
+  stream_putc(s, p->prefixlen);
+  switch (PREFIX_FAMILY(p))
+    {
+    case AF_INET:
+      stream_put_in_addr (s, &p->u.prefix4);
+      break;
+    case AF_INET6:
+      stream_put(s, &(p->u.prefix6), 16);
+      break;
+    default:
+      break;
+    }
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  ret = zclient_send_message(zclient);
+  /* TBD: handle the failure */
+  if (ret < 0)
+    zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
+
+  if (command == ZEBRA_NEXTHOP_REGISTER)
+    SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+  else if (command == ZEBRA_NEXTHOP_UNREGISTER)
+    UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+  return;
+}
+
+/**
+ * register_nexthop - register a nexthop with Zebra for notification
+ *    when the route to the nexthop changes.
+ * ARGUMENTS:
+ *   struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ * RETURNS:
+ *   void.
+ */
+static void
+register_nexthop (struct bgp_nexthop_cache *bnc)
+{
+  /* Check if we have already registered */
+  if (bnc->flags & BGP_NEXTHOP_REGISTERED)
+    return;
+  sendmsg_nexthop(bnc, ZEBRA_NEXTHOP_REGISTER);
+}
+
+/**
+ * unregister_nexthop -- Unregister the nexthop from Zebra.
+ * ARGUMENTS:
+ *   struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ * RETURNS:
+ *   void.
+ */
+static void
+unregister_nexthop (struct bgp_nexthop_cache *bnc)
+{
+  /* Check if we have already registered */
+  if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED))
+    return;
+
+  sendmsg_nexthop(bnc, ZEBRA_NEXTHOP_UNREGISTER);
+}
+
+/**
+ * evaluate_paths - Evaluate the paths/nets associated with a nexthop.
+ * ARGUMENTS:
+ *   struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ * RETURNS:
+ *   void.
+ */
+static void
+evaluate_paths (struct bgp_nexthop_cache *bnc)
+{
+  struct bgp_node *rn;
+  struct bgp_info *path;
+  struct bgp *bgp = bgp_get_default();
+  int afi;
+  struct peer *peer = (struct peer *)bnc->nht_info;
+
+  LIST_FOREACH(path, &(bnc->paths), nh_thread)
+    {
+      if (!(path->type == ZEBRA_ROUTE_BGP &&
+           path->sub_type == BGP_ROUTE_NORMAL))
+       continue;
+
+      rn = path->net;
+      afi = family2afi(rn->p.family);
+
+      /* Path becomes valid/invalid depending on whether the nexthop
+       * reachable/unreachable.
+       */
+      if ((CHECK_FLAG(path->flags, BGP_INFO_VALID) ? 1 : 0) !=
+         (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) ? 1 : 0))
+       {
+         if (CHECK_FLAG (path->flags, BGP_INFO_VALID))
+           {
+             bgp_aggregate_decrement (bgp, &rn->p, path,
+                                      afi, SAFI_UNICAST);
+             bgp_info_unset_flag (rn, path, BGP_INFO_VALID);
+           }
+         else
+           {
+             bgp_info_set_flag (rn, path, BGP_INFO_VALID);
+             bgp_aggregate_increment (bgp, &rn->p, path,
+                                      afi, SAFI_UNICAST);
+           }
+       }
+
+      /* Copy the metric to the path. Will be used for bestpath computation */
+      if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric)
+       (bgp_info_extra_get(path))->igpmetric = bnc->metric;
+      else if (path->extra)
+       path->extra->igpmetric = 0;
+
+      if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_METRIC_CHANGED) ||
+         CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CHANGED))
+       SET_FLAG(path->flags, BGP_INFO_IGP_CHANGED);
+
+      bgp_process(bgp, rn, afi, SAFI_UNICAST);
+    }
+
+  if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED))
+    {
+      if (BGP_DEBUG(nht, NHT))
+       zlog_debug("%s: Updating peer (%s) status with NHT", __FUNCTION__, peer->host);
+      SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+    }
+
+  RESET_FLAG(bnc->change_flags);
+}
+
+/**
+ * path_nh_map - make or break path-to-nexthop association.
+ * ARGUMENTS:
+ *   path - pointer to the path structure
+ *   bnc - pointer to the nexthop structure
+ *   make - if set, make the association. if unset, just break the existing
+ *          association.
+ */
+static void
+path_nh_map (struct bgp_info *path, struct bgp_nexthop_cache *bnc, int make)
+{
+  if (path->nexthop)
+    {
+      LIST_REMOVE(path, nh_thread);
+      path->nexthop->path_count--;
+      path->nexthop = NULL;
+    }
+  if (make)
+    {
+      LIST_INSERT_HEAD(&(bnc->paths), path, nh_thread);
+      path->nexthop = bnc;
+      path->nexthop->path_count++;
+    }
+}
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
new file mode 100644 (file)
index 0000000..dd6300e
--- /dev/null
@@ -0,0 +1,66 @@
+/* BGP Nexthop tracking
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _BGP_NHT_H
+#define _BGP_NHT_H
+
+/**
+ * bgp_parse_nexthop_update() - parse a nexthop update message from Zebra.
+ */
+void bgp_parse_nexthop_update (void);
+
+/**
+ * bgp_nexthop_check() - check if the bnc object is valid.
+ * ARGUMENTS:
+ *   p - path for which the nexthop object is being looked up
+ *   connected - True if NH MUST be a connected route
+ */
+int bgp_nexthop_check (struct bgp_info *, int connected);
+
+/**
+ * bgp_ensure_nexthop() - Ensure a bgp_nexthop_cache object exists for 
+ *  the given prefix or peer.  If an existing one is not found,
+ *  create a new object and register with ZEBRA for nexthop
+ *  notification.
+ * ARGUMENTS:
+ *   afi: AFI_IP or AF_IP6
+ *     struct bgp_info *: path for which the nexthop object is 
+ *                        being looked up
+ *   OR
+ *     struct peer The BGP peer associated with this NHT
+ *   connected - True if NH MUST be a connected route
+ */
+int bgp_ensure_nexthop (struct bgp_info *, struct peer *, int connected);
+
+/**
+ * bgp_unlink_nexthop() - Unlink the nexthop object from the path structure.
+ * ARGUMENTS:
+ *   struct bgp_info *: path structure.
+ */
+void bgp_unlink_nexthop (struct bgp_info *);
+
+/**
+ * bgp_unlink_nexthop() - Unlink the nexthop object for the given peer.
+ */
+extern void bgp_unlink_nexthop(struct bgp_info *p);
+void bgp_unlink_nexthop_by_peer (struct peer *);
+
+#endif /* _BGP_NHT_H */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
new file mode 100644 (file)
index 0000000..2800423
--- /dev/null
@@ -0,0 +1,1144 @@
+/* BGP open message handling
+   Copyright (C) 1998, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "stream.h"
+#include "thread.h"
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_vty.h"
+
+/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
+   negotiate remote peer supports extentions or not. But if
+   remote-peer doesn't supports negotiation process itself.  We would
+   like to do manual configuration.
+
+   So there is many configurable point.  First of all we want set each
+   peer whether we send capability negotiation to the peer or not.
+   Next, if we send capability to the peer we want to set my capabilty
+   inforation at each peer. */
+
+void
+bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+{
+  char *pnt;
+  char *end;
+  struct capability_mp_data mpc;
+  struct capability_header *hdr;
+
+  pnt = peer->notify.data;
+  end = pnt + peer->notify.length;
+  
+  while (pnt < end)
+    {
+      if (pnt + sizeof (struct capability_mp_data) + 2 > end)
+       return;
+      
+      hdr = (struct capability_header *)pnt;
+      if (pnt + hdr->length + 2 > end)
+       return;
+
+      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 (mpc.afi))
+           {
+           case AFI_IP:
+             vty_out (vty, "AFI IPv4, ");
+             break;
+           case AFI_IP6:
+             vty_out (vty, "AFI IPv6, ");
+             break;
+           default:
+             vty_out (vty, "AFI Unknown %d, ", ntohs (mpc.afi));
+             break;
+           }
+         switch (mpc.safi)
+           {
+           case SAFI_UNICAST:
+             vty_out (vty, "SAFI Unicast");
+             break;
+           case SAFI_MULTICAST:
+             vty_out (vty, "SAFI Multicast");
+             break;
+           case SAFI_MPLS_LABELED_VPN:
+             vty_out (vty, "SAFI MPLS-labeled VPN");
+             break;
+           case SAFI_ENCAP:
+             vty_out (vty, "SAFI ENCAP");
+             break;
+           default:
+             vty_out (vty, "SAFI Unknown %d ", mpc.safi);
+             break;
+           }
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+      else if (hdr->code >= 128)
+       vty_out (vty, "  Capability error: vendor specific capability code %d",
+                hdr->code);
+      else
+       vty_out (vty, "  Capability error: unknown capability code %d", 
+                hdr->code);
+
+      pnt += hdr->length + 2;
+    }
+}
+
+static void 
+bgp_capability_mp_data (struct stream *s, struct capability_mp_data *mpc)
+{
+  mpc->afi = stream_getw (s);
+  mpc->reserved = stream_getc (s);
+  mpc->safi = stream_getc (s);
+}
+
+int
+bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi)
+{
+  switch (afi)
+    {
+    case AFI_IP:
+    case AFI_IP6:
+      switch (*safi)
+       {
+         /* BGP MPLS-labeled VPN SAFI isn't contigious with others, remap */
+       case SAFI_MPLS_LABELED_VPN:
+         *safi = SAFI_MPLS_VPN;
+       case SAFI_UNICAST:
+       case SAFI_MULTICAST:
+       case SAFI_MPLS_VPN:
+       case SAFI_ENCAP:
+         return 1;
+       }
+    case AFI_ETHER:
+    default:
+      break;
+    }
+
+  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.afi][mpc.safi] = 1;
+  else 
+    return -1;
+
+  return 0;
+}
+
+static void
+bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi,
+                               u_char type, u_char mode)
+{
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s Addr-family %d/%d has ORF type/mode %d/%d not supported",
+              peer->host, afi, safi, type, mode);
+}
+
+static const struct message orf_type_str[] =
+{
+  { ORF_TYPE_PREFIX,           "Prefixlist"            },
+  { ORF_TYPE_PREFIX_OLD,       "Prefixlist (old)"      },
+};
+static const int orf_type_str_max = array_size(orf_type_str);
+
+static const struct message orf_mode_str[] =
+{
+  { ORF_MODE_RECEIVE,  "Receive"       },
+  { ORF_MODE_SEND,     "Send"          },
+  { ORF_MODE_BOTH,     "Both"          },
+};
+static const int orf_mode_str_max = array_size(orf_mode_str);
+
+static int
+bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
+{
+  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;
+
+  /* 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 ORF Cap entry for afi/safi: %u/%u",
+               peer->host, entry.mpc.afi, entry.mpc.safi);
+
+  /* Check AFI and SAFI. */
+  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 (CAPABILITY_CODE_ORF_LEN + (entry.num * 2) > hdr->length)
+    {
+      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_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC);
+      return -1;
+    }
+
+  for (i = 0 ; i < entry.num ; i++)
+    {
+      type = stream_getc(s);
+      mode = stream_getc(s);
+      
+      /* ORF Mode error check */
+      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);
+
+      if (hdr->code == CAPABILITY_CODE_ORF)
+       {
+          sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+          rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+       }
+      else if (hdr->code == CAPABILITY_CODE_ORF_OLD)
+       {
+          sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV;
+          rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV;
+       }
+      else
+       {
+         bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+         continue;
+       }
+
+      switch (mode)
+       {
+         case ORF_MODE_BOTH:
+           SET_FLAG (peer->af_cap[afi][safi], sm_cap);
+           SET_FLAG (peer->af_cap[afi][safi], rm_cap);
+           break;
+         case ORF_MODE_SEND:
+           SET_FLAG (peer->af_cap[afi][safi], sm_cap);
+           break;
+         case ORF_MODE_RECEIVE:
+           SET_FLAG (peer->af_cap[afi][safi], rm_cap);
+           break;
+       }
+    }
+  return 0;
+}
+
+static int
+bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
+{
+  struct stream *s = BGP_INPUT (peer);
+  u_int16_t restart_flag_time;
+  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))
+    SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV);
+  
+  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,
+                  CHECK_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV) ? " " 
+                                                                   : " not ",
+                  peer->v_gr_restart);
+    }
+
+  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 as_t
+bgp_capability_as4 (struct peer *peer, struct capability_header *hdr)
+{
+  SET_FLAG (peer->cap, PEER_CAP_AS4_RCV);
+  
+  if (hdr->length != CAPABILITY_CODE_AS4_LEN)
+    {
+      zlog_err ("%s AS4 capability has incorrect data length %d",
+                peer->host, hdr->length);
+      return 0;
+    }
+  
+  as_t as4 = stream_getl (BGP_INPUT(peer));
+  
+  if (BGP_DEBUG (as4, AS4))
+    zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u",
+                peer->host, as4);
+  return as4;
+}
+
+static const struct message capcode_str[] =
+{
+  { 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)"                     },
+};
+static const int capcode_str_max = array_size(capcode_str);
+
+/* Minimum sizes for length field of each cap (so not inc. the header) */
+static const size_t cap_minsizes[] = 
+{
+  [CAPABILITY_CODE_MP]         = CAPABILITY_CODE_MP_LEN,
+  [CAPABILITY_CODE_REFRESH]    = CAPABILITY_CODE_REFRESH_LEN,
+  [CAPABILITY_CODE_ORF]                = CAPABILITY_CODE_ORF_LEN,
+  [CAPABILITY_CODE_RESTART]    = CAPABILITY_CODE_RESTART_LEN,
+  [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]    = CAPABILITY_CODE_ORF_LEN,
+};
+
+/* value the capability must be a multiple of.
+ * 0-data capabilities won't be checked against this.
+ * Other capabilities whose data doesn't fall on convenient boundaries for this
+ * table should be set to 1.
+ */
+static const size_t cap_modsizes[] =
+{
+  [CAPABILITY_CODE_MP]          = 4,
+  [CAPABILITY_CODE_REFRESH]     = 1,
+  [CAPABILITY_CODE_ORF]         = 1,
+  [CAPABILITY_CODE_RESTART]     = 1,
+  [CAPABILITY_CODE_AS4]         = 4,
+  [CAPABILITY_CODE_DYNAMIC]     = 1,
+  [CAPABILITY_CODE_REFRESH_OLD] = 1,
+  [CAPABILITY_CODE_ORF_OLD]     = 1,
+};
+
+/**
+ * Parse given capability.
+ * XXX: This is reading into a stream, but not using stream API
+ *
+ * @param[out] mp_capability Set to 1 on return iff one or more Multiprotocol
+ *                           capabilities were encountered.
+ */
+static int
+bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability,
+                     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 (stream_get_getp(s) + 2 > end)
+       {
+         zlog_info ("%s Capability length error (< header)", peer->host);
+         bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC);
+         return -1;
+       }
+      
+      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 (< length)", peer->host);
+         bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC);
+         return -1;
+       }
+      
+      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_AS4:
+          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, 
+                            (unsigned) cap_minsizes[caphdr.code]);
+                  bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                                  BGP_NOTIFY_OPEN_UNSPECIFIC);
+                  return -1;
+                }
+              if (caphdr.length
+                  && caphdr.length % cap_modsizes[caphdr.code] != 0)
+                {
+                  zlog_info ("%s %s Capability length error: got %u,"
+                             " expected a multiple of %u",
+                             peer->host,
+                             LOOKUP (capcode_str, caphdr.code),
+                             caphdr.length,
+                            (unsigned) cap_modsizes[caphdr.code]);
+                  bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                                         BGP_NOTIFY_OPEN_UNSPECIFIC);
+                  return -1;
+                }
+          /* we deliberately ignore unknown codes, see below */
+          default:
+            break;
+        }
+      
+      switch (caphdr.code)
+        {
+          case CAPABILITY_CODE_MP:
+            {
+             *mp_capability = 1;
+
+              /* 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_entry (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;
+          case CAPABILITY_CODE_AS4:
+              /* Already handled as a special-case parsing of the capabilities
+               * at the beginning of OPEN processing. So we care not a jot
+               * for the value really, only error case.
+               */
+              if (!bgp_capability_as4 (peer, &caphdr))
+                return -1;
+              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, size_t length)
+{
+  bgp_notify_send (peer, 
+                  BGP_NOTIFY_OPEN_ERR, 
+                  BGP_NOTIFY_OPEN_AUTH_FAILURE); 
+  return -1;
+}
+
+static int
+strict_capability_same (struct peer *peer)
+{
+  int i, j;
+
+  for (i = AFI_IP; i < AFI_MAX; i++)
+    for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+      if (peer->afc[i][j] != peer->afc_nego[i][j])
+       return 0;
+  return 1;
+}
+
+/* peek into option, stores ASN to *as4 if the AS4 capability was found.
+ * Returns  0 if no as4 found, as4cap value otherwise.
+ */
+as_t
+peek_for_as4_capability (struct peer *peer, u_char length)
+{
+  struct stream *s = BGP_INPUT (peer);
+  size_t orig_getp = stream_get_getp (s);
+  size_t end = orig_getp + length;
+  as_t as4 = 0;
+  
+  /* The full capability parser will better flag the error.. */
+  if (STREAM_READABLE(s) < length)
+    return 0;
+
+  if (BGP_DEBUG (as4, AS4))
+    zlog_info ("%s [AS4] rcv OPEN w/ OPTION parameter len: %u,"
+                " peeking for as4",
+               peer->host, length);
+  /* the error cases we DONT handle, we ONLY try to read as4 out of
+   * correctly formatted options.
+   */
+  while (stream_get_getp(s) < end) 
+    {
+      u_char opt_type;
+      u_char opt_length;
+      
+      /* Check the length. */
+      if (stream_get_getp (s) + 2 > end)
+        goto end;
+      
+      /* Fetch option type and length. */
+      opt_type = stream_getc (s);
+      opt_length = stream_getc (s);
+      
+      /* Option length check. */
+      if (stream_get_getp (s) + opt_length > end)
+        goto end;
+      
+      if (opt_type == BGP_OPEN_OPT_CAP)
+        {
+          unsigned long capd_start = stream_get_getp (s);
+          unsigned long capd_end = capd_start + opt_length;
+          
+          assert (capd_end <= end);
+          
+         while (stream_get_getp (s) < capd_end)
+           {
+             struct capability_header hdr;
+             
+             if (stream_get_getp (s) + 2 > capd_end)
+                goto end;
+              
+              hdr.code = stream_getc (s);
+              hdr.length = stream_getc (s);
+              
+             if ((stream_get_getp(s) +  hdr.length) > capd_end)
+               goto end;
+
+             if (hdr.code == CAPABILITY_CODE_AS4)
+               {
+                 if (BGP_DEBUG (as4, AS4))
+                   zlog_info ("[AS4] found AS4 capability, about to parse");
+                 as4 = bgp_capability_as4 (peer, &hdr);
+                 
+                 goto end;
+                }
+              stream_forward_getp (s, hdr.length);
+           }
+       }
+    }
+
+end:
+  stream_set_getp (s, orig_getp);
+  return as4;
+}
+
+/**
+ * Parse open option.
+ *
+ * @param[out] mp_capability @see bgp_capability_parse() for semantics.
+ */
+int
+bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability)
+{
+  int ret;
+  u_char *error;
+  u_char error_data[BGP_MAX_PACKET_SIZE];
+  struct stream *s = BGP_INPUT(peer);
+  size_t end = stream_get_getp (s) + length;
+
+  ret = 0;
+  error = error_data;
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u",
+              peer->host, length);
+  
+  while (stream_get_getp(s) < 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_OPEN_ERR,
+                                BGP_NOTIFY_OPEN_UNSPECIFIC);
+         return -1;
+       }
+
+      /* Fetch option type and length. */
+      opt_type = stream_getc (s);
+      opt_length = stream_getc (s);
+      
+      /* Option length check. */
+      if (STREAM_READABLE (s) < opt_length)
+       {
+         zlog_info ("%s Option length error", peer->host);
+         bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                                BGP_NOTIFY_OPEN_UNSPECIFIC);
+         return -1;
+       }
+
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
+                  peer->host, opt_type,
+                  opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" :
+                  opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown",
+                  opt_length);
+  
+      switch (opt_type)
+       {
+       case BGP_OPEN_OPT_AUTH:
+         ret = bgp_auth_parse (peer, opt_length);
+         break;
+       case BGP_OPEN_OPT_CAP:
+         ret = bgp_capability_parse (peer, opt_length, mp_capability, &error);
+         break;
+       default:
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_OPEN_ERR, 
+                          BGP_NOTIFY_OPEN_UNSUP_PARAM); 
+         ret = -1;
+         break;
+       }
+
+      /* Parse error.  To accumulate all unsupported capability codes,
+         bgp_capability_parse does not return -1 when encounter
+         unsupported capability code.  To detect that, please check
+         error and erro_data pointer, like below.  */
+      if (ret < 0)
+       return -1;
+    }
+
+  /* All OPEN option is parsed.  Check capability when strict compare
+     flag is enabled.*/
+  if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
+    {
+      /* If Unsupported Capability exists. */
+      if (error != error_data)
+       {
+         bgp_notify_send_with_data (peer, 
+                                    BGP_NOTIFY_OPEN_ERR, 
+                                    BGP_NOTIFY_OPEN_UNSUP_CAPBL, 
+                                    error_data, error - error_data);
+         return -1;
+       }
+
+      /* Check local capability does not negotiated with remote
+         peer. */
+      if (! strict_capability_same (peer))
+       {
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_OPEN_ERR, 
+                          BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+         return -1;
+       }
+    }
+
+  /* Check there are no common AFI/SAFIs and send Unsupported Capability
+     error. */
+  if (*mp_capability &&
+      ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+    {
+      if (! peer->afc_nego[AFI_IP][SAFI_UNICAST] 
+         && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+         && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+         && ! peer->afc_nego[AFI_IP][SAFI_ENCAP]
+         && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+         && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+         && ! peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+         && ! peer->afc_nego[AFI_IP6][SAFI_ENCAP])
+       {
+         plog_err (peer->log, "%s [Error] Configured AFI/SAFIs do not "
+                   "overlap with received MP capabilities",
+                   peer->host);
+
+         if (error != error_data)
+
+           bgp_notify_send_with_data (peer, 
+                                      BGP_NOTIFY_OPEN_ERR, 
+                                      BGP_NOTIFY_OPEN_UNSUP_CAPBL, 
+                                      error_data, error - error_data);
+         else
+           bgp_notify_send (peer, 
+                            BGP_NOTIFY_OPEN_ERR, 
+                            BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+         return -1;
+       }
+    }
+  return 0;
+}
+
+static void
+bgp_open_capability_orf (struct stream *s, struct peer *peer,
+                         afi_t afi, safi_t safi, u_char code)
+{
+  u_char cap_len;
+  u_char orf_len;
+  unsigned long capp;
+  unsigned long orfp;
+  unsigned long numberp;
+  int number_of_orfs = 0;
+
+  if (safi == SAFI_MPLS_VPN)
+    safi = SAFI_MPLS_LABELED_VPN;
+
+  stream_putc (s, BGP_OPEN_OPT_CAP);
+  capp = stream_get_endp (s);           /* Set Capability Len Pointer */
+  stream_putc (s, 0);                   /* Capability Length */
+  stream_putc (s, code);                /* Capability Code */
+  orfp = stream_get_endp (s);           /* Set ORF Len Pointer */
+  stream_putc (s, 0);                   /* ORF Length */
+  stream_putw (s, afi);
+  stream_putc (s, 0);
+  stream_putc (s, safi);
+  numberp = stream_get_endp (s);        /* Set Number Pointer */
+  stream_putc (s, 0);                   /* Number of ORFs */
+
+  /* Address Prefix ORF */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+    {
+      stream_putc (s, (code == CAPABILITY_CODE_ORF ?
+                  ORF_TYPE_PREFIX : ORF_TYPE_PREFIX_OLD));
+
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+         && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+       {
+         SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV);
+         SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV);
+         stream_putc (s, ORF_MODE_BOTH);
+       }
+      else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM))
+       {
+         SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV);
+         stream_putc (s, ORF_MODE_SEND);
+       }
+      else
+       {
+         SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV);
+         stream_putc (s, ORF_MODE_RECEIVE);
+       }
+      number_of_orfs++;
+    }
+
+  /* Total Number of ORFs. */
+  stream_putc_at (s, numberp, number_of_orfs);
+
+  /* Total ORF Len. */
+  orf_len = stream_get_endp (s) - orfp - 1;
+  stream_putc_at (s, orfp, orf_len);
+
+  /* Total Capability Len. */
+  cap_len = stream_get_endp (s) - capp - 1;
+  stream_putc_at (s, capp, cap_len);
+}
+
+/* Fill in capability open option to the packet. */
+void
+bgp_open_capability (struct stream *s, struct peer *peer)
+{
+  u_char len;
+  unsigned long cp, capp, rcapp;
+  afi_t afi;
+  safi_t safi;
+  as_t local_as;
+  u_int32_t restart_time;
+
+  /* Remember current pointer for Opt Parm Len. */
+  cp = stream_get_endp (s);
+
+  /* Opt Parm Len. */
+  stream_putc (s, 0);
+
+  /* Do not send capability. */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN) 
+      || CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
+    return;
+
+  /* IPv4 unicast. */
+  if (peer->afc[AFI_IP][SAFI_UNICAST])
+    {
+      peer->afc_adv[AFI_IP][SAFI_UNICAST] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_UNICAST);
+    }
+  /* IPv4 multicast. */
+  if (peer->afc[AFI_IP][SAFI_MULTICAST])
+    {
+      peer->afc_adv[AFI_IP][SAFI_MULTICAST] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_MULTICAST);
+    }
+  /* IPv4 VPN */
+  if (peer->afc[AFI_IP][SAFI_MPLS_VPN])
+    {
+      peer->afc_adv[AFI_IP][SAFI_MPLS_VPN] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_MPLS_LABELED_VPN);
+    }
+  /* ENCAP */
+  if (peer->afc[AFI_IP][SAFI_ENCAP])
+    {
+      peer->afc_adv[AFI_IP][SAFI_ENCAP] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_ENCAP);
+    }
+  /* IPv6 unicast. */
+  if (peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      peer->afc_adv[AFI_IP6][SAFI_UNICAST] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP6);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_UNICAST);
+    }
+  /* IPv6 multicast. */
+  if (peer->afc[AFI_IP6][SAFI_MULTICAST])
+    {
+      peer->afc_adv[AFI_IP6][SAFI_MULTICAST] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP6);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_MULTICAST);
+    }
+  /* IPv6 VPN. */
+  if (peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      peer->afc_adv[AFI_IP6][SAFI_MPLS_VPN] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP6);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_MPLS_LABELED_VPN);
+    }
+  /* IPv6 ENCAP. */
+  if (peer->afc[AFI_IP6][SAFI_ENCAP])
+    {
+      peer->afc_adv[AFI_IP6][SAFI_ENCAP] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP6);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_ENCAP);
+    }
+
+  /* Route refresh. */
+  SET_FLAG (peer->cap, PEER_CAP_REFRESH_ADV);
+  stream_putc (s, BGP_OPEN_OPT_CAP);
+  stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2);
+  stream_putc (s, CAPABILITY_CODE_REFRESH_OLD);
+  stream_putc (s, CAPABILITY_CODE_REFRESH_LEN);
+  stream_putc (s, BGP_OPEN_OPT_CAP);
+  stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2);
+  stream_putc (s, CAPABILITY_CODE_REFRESH);
+  stream_putc (s, CAPABILITY_CODE_REFRESH_LEN);
+
+  /* AS4 */
+  SET_FLAG (peer->cap, PEER_CAP_AS4_ADV);
+  stream_putc (s, BGP_OPEN_OPT_CAP);
+  stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2);
+  stream_putc (s, CAPABILITY_CODE_AS4);
+  stream_putc (s, CAPABILITY_CODE_AS4_LEN);
+  if ( peer->change_local_as )
+    local_as = peer->change_local_as;
+  else
+    local_as = peer->local_as;
+  stream_putl (s, local_as );
+
+  /* ORF capability. */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+         || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+       {
+         bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF_OLD);
+         bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF);
+       }
+
+  /* Dynamic capability. */
+  if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+    {
+      SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV);
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_DYNAMIC);
+      stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN);
+    }
+
+  /* Sending base graceful-restart capability irrespective of the config */
+  SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV);
+  stream_putc (s, BGP_OPEN_OPT_CAP);
+  capp = stream_get_endp (s);           /* Set Capability Len Pointer */
+  stream_putc (s, 0);                   /* Capability Length */
+  stream_putc (s, CAPABILITY_CODE_RESTART);
+  rcapp = stream_get_endp (s);          /* Set Restart Capability Len Pointer */
+  stream_putc (s, 0);
+  restart_time = peer->bgp->restart_time;
+  if (peer->bgp->t_startup)
+    {
+      SET_FLAG (restart_time, RESTART_R_BIT);
+      SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_ADV);
+    }
+  stream_putw (s, restart_time);
+
+  /* Send address-family specific graceful-restart capability only when GR config
+     is present */
+  if (bgp_flag_check (peer->bgp, BGP_FLAG_GRACEFUL_RESTART))
+    {
+      for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+        for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+          if (peer->afc[afi][safi])
+            {
+              stream_putw (s, afi);
+              stream_putc (s, safi);
+              stream_putc (s, 0); //Forwarding is not retained as of now.
+            }
+    }
+
+  /* Total Graceful restart capability Len. */
+  len = stream_get_endp (s) - rcapp - 1;
+  stream_putc_at (s, rcapp, len);
+
+  /* Total Capability Len. */
+  len = stream_get_endp (s) - capp - 1;
+  stream_putc_at (s, capp, len);
+
+  /* Total Opt Parm Len. */
+  len = stream_get_endp (s) - cp - 1;
+  stream_putc_at (s, cp, len);
+}
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
new file mode 100644 (file)
index 0000000..6233375
--- /dev/null
@@ -0,0 +1,112 @@
+/* BGP open message handling
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_OPEN_H
+#define _QUAGGA_BGP_OPEN_H
+
+/* Standard header for capability TLV */
+struct capability_header
+{
+  u_char code;
+  u_char length;
+};
+
+/* Generic MP capability data */
+struct capability_mp_data
+{
+  afi_t afi;
+  u_char reserved;
+  safi_t safi;
+};
+
+#pragma pack(1)
+struct capability_orf_entry 
+{
+  struct capability_mp_data mpc;
+  u_char num;
+  struct {
+    u_char type;
+    u_char mode;
+  } orfs[];
+} __attribute__ ((packed));
+#pragma pack()
+
+struct capability_as4
+{
+  uint32_t as4;
+};
+
+struct graceful_restart_af
+{
+  afi_t afi;
+  safi_t safi;
+  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_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) */
+
+/* Capability Length */
+#define CAPABILITY_CODE_MP_LEN          4
+#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
+#define CAPABILITY_CODE_ORF_LEN         5
+
+/* Cooperative Route Filtering Capability.  */
+
+/* ORF Type */
+#define ORF_TYPE_PREFIX                64 
+#define ORF_TYPE_PREFIX_OLD           128
+
+/* ORF Mode */
+#define ORF_MODE_RECEIVE                1 
+#define ORF_MODE_SEND                   2 
+#define ORF_MODE_BOTH                   3 
+
+/* Capability Message Action.  */
+#define CAPABILITY_ACTION_SET           0
+#define CAPABILITY_ACTION_UNSET         1
+
+/* Graceful Restart */
+#define RESTART_R_BIT              0x8000
+#define RESTART_F_BIT              0x80
+
+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 as_t peek_for_as4_capability (struct peer *, u_char);
+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
new file mode 100644 (file)
index 0000000..b497e45
--- /dev/null
@@ -0,0 +1,2717 @@
+/* BGP packet management routine.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "stream.h"
+#include "network.h"
+#include "prefix.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "sockunion.h"         /* for inet_ntop () */
+#include "sockopt.h"
+#include "linklist.h"
+#include "plist.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_encap.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_vty.h"
+
+int stream_put_prefix (struct stream *, struct prefix *);
+
+/* Set up BGP packet marker and packet type. */
+static int
+bgp_packet_set_marker (struct stream *s, u_char type)
+{
+  int i;
+
+  /* Fill in marker. */
+  for (i = 0; i < BGP_MARKER_SIZE; i++)
+    stream_putc (s, 0xff);
+
+  /* Dummy total length. This field is should be filled in later on. */
+  stream_putw (s, 0);
+
+  /* BGP packet type. */
+  stream_putc (s, type);
+
+  /* Return current stream size. */
+  return stream_get_endp (s);
+}
+
+/* Set BGP packet header size entry.  If size is zero then use current
+   stream size. */
+static int
+bgp_packet_set_size (struct stream *s)
+{
+  int cp;
+
+  /* Preserve current pointer. */
+  cp = stream_get_endp (s);
+  stream_putw_at (s, BGP_MARKER_SIZE, cp);
+
+  return cp;
+}
+
+/* Add new packet to the peer. */
+static void
+bgp_packet_add (struct peer *peer, struct stream *s)
+{
+  /* Add packet to the end of list. */
+  stream_fifo_push (peer->obuf, s);
+}
+
+/* Free first packet. */
+static void
+bgp_packet_delete (struct peer *peer)
+{
+  stream_free (stream_fifo_pop (peer->obuf));
+}
+
+/* Check file descriptor whether connect is established. */
+static void
+bgp_connect_check (struct peer *peer)
+{
+  int status;
+  socklen_t slen;
+  int ret;
+
+  /* Anyway I have to reset read and write thread. */
+  BGP_READ_OFF (peer->t_read);
+  BGP_WRITE_OFF (peer->t_write);
+
+  /* Check file descriptor. */
+  slen = sizeof (status);
+  ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen);
+
+  /* If getsockopt is fail, this is fatal error. */
+  if (ret < 0)
+    {
+      zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect");
+      BGP_EVENT_ADD (peer, TCP_fatal_error);
+      return;
+    }      
+
+  /* When status is 0 then TCP connection is established. */
+  if (status == 0)
+    {
+      BGP_EVENT_ADD (peer, TCP_connection_open);
+    }
+  else
+    {
+      if (BGP_DEBUG (events, EVENTS))
+         plog_debug (peer->log, "%s [Event] Connect failed (%s)",
+                    peer->host, safe_strerror (errno));
+      BGP_EVENT_ADD (peer, TCP_connection_open_failed);
+    }
+}
+
+/* Make BGP update packet.  */
+static struct stream *
+bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct stream *s;
+  struct stream *snlri;
+  struct bgp_adj_out *adj;
+  struct bgp_advertise *adv;
+  struct stream *packet;
+  struct bgp_node *rn = NULL;
+  struct bgp_info *binfo = NULL;
+  bgp_size_t total_attr_len = 0;
+  unsigned long attrlen_pos = 0;
+  int space_remaining = 0;
+  int space_needed = 0;
+  size_t mpattrlen_pos = 0;
+  size_t mpattr_pos = 0;
+
+  s = peer->work;
+  stream_reset (s);
+  snlri = peer->scratch;
+  stream_reset (snlri);
+
+  adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update);
+
+  while (adv)
+    {
+      assert (adv->rn);
+      rn = adv->rn;
+      adj = adv->adj;
+      if (adv->binfo)
+        binfo = adv->binfo;
+
+      space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) -
+                        BGP_MAX_PACKET_SIZE_OVERFLOW;
+      space_needed = BGP_NLRI_LENGTH + bgp_packet_mpattr_prefix_size (afi, safi, &rn->p);
+
+      /* When remaining space can't include NLRI and it's length.  */
+      if (space_remaining < space_needed)
+       break;
+
+      /* If packet is empty, set attribute. */
+      if (stream_empty (s))
+       {
+         struct prefix_rd *prd = NULL;
+         u_char *tag = NULL;
+         struct peer *from = NULL;
+
+         if (rn->prn)
+           prd = (struct prefix_rd *) &rn->prn->p;
+          if (binfo)
+            {
+              from = binfo->peer;
+              if (binfo->extra)
+                tag = binfo->extra->tag;
+            }
+
+         /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length,
+          * one byte message type.
+          */
+         bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+         /* 2: withdrawn routes length */
+         stream_putw (s, 0);
+
+         /* 3: total attributes length - attrlen_pos stores the position */
+         attrlen_pos = stream_get_endp (s);
+         stream_putw (s, 0);
+
+         /* 4: if there is MP_REACH_NLRI attribute, that should be the first
+          * attribute, according to draft-ietf-idr-error-handling. Save the
+          * position.
+          */
+         mpattr_pos = stream_get_endp(s);
+
+         /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
+         total_attr_len = bgp_packet_attribute (NULL, peer, s,
+                                                adv->baa->attr,
+                                                 ((afi == AFI_IP && safi == SAFI_UNICAST) ?
+                                                  &rn->p : NULL),
+                                                 afi, safi,
+                                                from, prd, tag);
+          space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) -
+                            BGP_MAX_PACKET_SIZE_OVERFLOW;
+          space_needed = BGP_NLRI_LENGTH + bgp_packet_mpattr_prefix_size (afi, safi, &rn->p);;
+
+          /* If the attributes alone do not leave any room for NLRI then
+           * return */
+          if (space_remaining < space_needed)
+            {
+              zlog_err ("%s cannot send UPDATE, the attributes do not leave "
+                        "room for NLRI", peer->host);
+              /* Flush the FIFO update queue */
+              while (adv)
+                adv = bgp_advertise_clean (peer, adv->adj, afi, safi);
+              return NULL;
+            } 
+
+       }
+
+      if (afi == AFI_IP && safi == SAFI_UNICAST)
+       stream_put_prefix (s, &rn->p);
+      else
+       {
+         /* Encode the prefix in MP_REACH_NLRI attribute */
+         struct prefix_rd *prd = NULL;
+         u_char *tag = NULL;
+
+         if (rn->prn)
+           prd = (struct prefix_rd *) &rn->prn->p;
+         if (binfo && binfo->extra)
+           tag = binfo->extra->tag;
+
+         if (stream_empty(snlri))
+           mpattrlen_pos = bgp_packet_mpattr_start(snlri, afi, safi,
+                                                   adv->baa->attr);
+         bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, tag);
+       }
+      if (BGP_DEBUG (update, UPDATE_OUT))
+        {
+          char buf[INET6_BUFSIZ];
+
+          zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d",
+                peer->host,
+                inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ),
+                rn->p.prefixlen);
+        }
+
+      /* Synchnorize attribute.  */
+      if (adj->attr)
+       bgp_attr_unintern (&adj->attr);
+      else
+       peer->scount[afi][safi]++;
+
+      adj->attr = bgp_attr_intern (adv->baa->attr);
+
+      adv = bgp_advertise_clean (peer, adj, afi, safi);
+    }
+
+  if (! stream_empty (s))
+    {
+      if (!stream_empty(snlri))
+       {
+         bgp_packet_mpattr_end(snlri, mpattrlen_pos);
+         total_attr_len += stream_get_endp(snlri);
+       }
+
+      /* set the total attribute length correctly */
+      stream_putw_at (s, attrlen_pos, total_attr_len);
+
+      if (!stream_empty(snlri))
+       packet = stream_dupcat(s, snlri, mpattr_pos);
+      else
+       packet = stream_dup (s);
+      bgp_packet_set_size (packet);
+      bgp_packet_add (peer, packet);
+      BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+      stream_reset (s);
+      stream_reset (snlri);
+      return packet;
+    }
+  return NULL;
+}
+
+static struct stream *
+bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct stream *s;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return NULL;
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host);
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make BGP update packet. */
+  bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+  /* Unfeasible Routes Length */
+  stream_putw (s, 0);
+
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    {
+      /* Total Path Attribute Length */
+      stream_putw (s, 0);
+    }
+  else
+    {
+      /* Total Path Attribute Length */
+      stream_putw (s, 6);
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+      stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
+      stream_putc (s, 3);
+      stream_putw (s, afi);
+      stream_putc (s, safi);
+    }
+
+  bgp_packet_set_size (s);
+  bgp_packet_add (peer, s);
+  return s;
+}
+
+/* Make BGP withdraw packet.  */
+/* For ipv4 unicast:
+   16-octet marker | 2-octet length | 1-octet type |
+    2-octet withdrawn route length | withdrawn prefixes | 2-octet attrlen (=0)
+*/
+/* For other afi/safis:
+   16-octet marker | 2-octet length | 1-octet type |
+    2-octet withdrawn route length (=0) | 2-octet attrlen |
+     mp_unreach attr type | attr len | afi | safi | withdrawn prefixes
+*/
+static struct stream *
+bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct stream *s;
+  struct stream *packet;
+  struct bgp_adj_out *adj;
+  struct bgp_advertise *adv;
+  struct bgp_node *rn;
+  bgp_size_t unfeasible_len;
+  bgp_size_t total_attr_len;
+  size_t mp_start = 0;
+  size_t attrlen_pos = 0;
+  size_t mplen_pos = 0;
+  u_char first_time = 1;
+  int space_remaining = 0;
+  int space_needed = 0;
+
+  s = peer->work;
+  stream_reset (s);
+
+  while ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL)
+    {
+      assert (adv->rn);
+      adj = adv->adj;
+      rn = adv->rn;
+
+      space_remaining = STREAM_REMAIN (s) -
+                        BGP_MAX_PACKET_SIZE_OVERFLOW;
+      space_needed = (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN +
+                      bgp_packet_mpattr_prefix_size (afi, safi, &rn->p));
+
+      if (space_remaining < space_needed)
+       break;
+
+      if (stream_empty (s))
+       {
+         bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+         stream_putw (s, 0); /* unfeasible routes length */
+       }
+      else
+       first_time = 0;
+
+      if (afi == AFI_IP && safi == SAFI_UNICAST)
+       stream_put_prefix (s, &rn->p);
+      else
+       {
+         struct prefix_rd *prd = NULL;
+
+         if (rn->prn)
+           prd = (struct prefix_rd *) &rn->prn->p;
+
+         /* If first time, format the MP_UNREACH header */
+         if (first_time)
+           {
+             attrlen_pos = stream_get_endp (s);
+             /* total attr length = 0 for now. reevaluate later */
+             stream_putw (s, 0);
+             mp_start = stream_get_endp (s);
+             mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+           }
+
+         bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL);
+       }
+
+      if (BGP_DEBUG (update, UPDATE_OUT))
+        {
+          char buf[INET6_BUFSIZ];
+
+          zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable",
+                peer->host,
+                inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ),
+                rn->p.prefixlen);
+        }
+
+      peer->scount[afi][safi]--;
+
+      bgp_adj_out_remove (rn, adj, peer, afi, safi);
+      bgp_unlock_node (rn);
+    }
+
+  if (! stream_empty (s))
+    {
+      if (afi == AFI_IP && safi == SAFI_UNICAST)
+       {
+         unfeasible_len
+           = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN;
+         stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len);
+         stream_putw (s, 0);
+       }
+      else
+       {
+         /* Set the mp_unreach attr's length */
+         bgp_packet_mpunreach_end(s, mplen_pos);
+
+         /* Set total path attribute length. */
+         total_attr_len = stream_get_endp(s) - mp_start;
+         stream_putw_at (s, attrlen_pos, total_attr_len);
+       }
+      bgp_packet_set_size (s);
+      packet = stream_dup (s);
+      bgp_packet_add (peer, packet);
+      stream_reset (s);
+      return packet;
+    }
+
+  return NULL;
+}
+
+void
+bgp_default_update_send (struct peer *peer, struct attr *attr,
+                        afi_t afi, safi_t safi, struct peer *from)
+{
+  struct stream *s;
+  struct prefix p;
+  unsigned long pos;
+  bgp_size_t total_attr_len;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return;
+
+  if (afi == AFI_IP)
+    str2prefix ("0.0.0.0/0", &p);
+  else 
+    str2prefix ("::/0", &p);
+
+  /* Logging the attribute. */
+  if (BGP_DEBUG (update, UPDATE_OUT))
+    {
+      char attrstr[BUFSIZ];
+      char buf[INET6_BUFSIZ];
+      attrstr[0] = '\0';
+
+      bgp_dump_attr (peer, attr, attrstr, BUFSIZ);
+      zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s",
+           peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ),
+           p.prefixlen, attrstr);
+    }
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make BGP update packet. */
+  bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+  /* Unfeasible Routes Length. */
+  stream_putw (s, 0);
+
+  /* Make place for total attribute length.  */
+  pos = stream_get_endp (s);
+  stream_putw (s, 0);
+  total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, from, NULL, NULL);
+
+  /* Set Total Path Attribute Length. */
+  stream_putw_at (s, pos, total_attr_len);
+
+  /* NLRI set. */
+  if (p.family == AF_INET && safi == SAFI_UNICAST)
+    stream_put_prefix (s, &p);
+
+  /* Set size. */
+  bgp_packet_set_size (s);
+
+  /* Dump packet if debug option is set. */
+#ifdef DEBUG
+  /* bgp_packet_dump (packet); */
+#endif /* DEBUG */
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+void
+bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct stream *s;
+  struct prefix p;
+  unsigned long attrlen_pos = 0;
+  unsigned long cp;
+  bgp_size_t unfeasible_len;
+  bgp_size_t total_attr_len;
+  size_t mp_start = 0;
+  size_t mplen_pos = 0;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return;
+
+  if (afi == AFI_IP)
+    str2prefix ("0.0.0.0/0", &p);
+  else 
+    str2prefix ("::/0", &p);
+
+  total_attr_len = 0;
+
+  if (BGP_DEBUG (update, UPDATE_OUT))
+    {
+      char buf[INET6_BUFSIZ];
+
+      zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable",
+            peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ),
+            p.prefixlen);
+    }
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make BGP update packet. */
+  bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+  /* Unfeasible Routes Length. */;
+  cp = stream_get_endp (s);
+  stream_putw (s, 0);
+
+  /* Withdrawn Routes. */
+  if (p.family == AF_INET && safi == SAFI_UNICAST)
+    {
+      stream_put_prefix (s, &p);
+
+      unfeasible_len = stream_get_endp (s) - cp - 2;
+
+      /* Set unfeasible len.  */
+      stream_putw_at (s, cp, unfeasible_len);
+
+      /* Set total path attribute length. */
+      stream_putw (s, 0);
+    }
+  else
+    {
+      attrlen_pos = stream_get_endp (s);
+      stream_putw (s, 0);
+      mp_start = stream_get_endp (s);
+      mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+      bgp_packet_mpunreach_prefix(s, &p, afi, safi, NULL, NULL);
+
+      /* Set the mp_unreach attr's length */
+      bgp_packet_mpunreach_end(s, mplen_pos);
+
+      /* Set total path attribute length. */
+      total_attr_len = stream_get_endp(s) - mp_start;
+      stream_putw_at (s, attrlen_pos, total_attr_len);
+    }
+
+  bgp_packet_set_size (s);
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+/* Get next packet to be written.  */
+static struct stream *
+bgp_write_packet (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+  struct stream *s = NULL;
+  struct bgp_advertise *adv;
+
+  s = stream_fifo_head (peer->obuf);
+  if (s)
+    return s;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw);
+       if (adv)
+         {
+           s = bgp_withdraw_packet (peer, afi, safi);
+           if (s)
+             return s;
+         }
+      }
+    
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update);
+       if (adv)
+         {
+            if (adv->binfo && adv->binfo->uptime < peer->synctime)
+             {
+               if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV)
+                   && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV)
+                   && ! (CHECK_FLAG (adv->binfo->peer->cap,
+                                      PEER_CAP_RESTART_BIT_RCV) &&
+                         CHECK_FLAG (adv->binfo->peer->cap,
+                                      PEER_CAP_RESTART_BIT_ADV))
+                   && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE)
+                   && safi != SAFI_MPLS_VPN)
+                 {
+                   if (CHECK_FLAG (adv->binfo->peer->af_sflags[afi][safi],
+                       PEER_STATUS_EOR_RECEIVED))
+                     s = bgp_update_packet (peer, afi, safi);
+                 }
+               else
+                 s = bgp_update_packet (peer, afi, safi);
+             }
+
+           if (s)
+             return s;
+         }
+
+       if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV))
+         {
+           if (peer->afc_nego[afi][safi] && peer->synctime
+               && ! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND)
+               && safi != SAFI_MPLS_VPN)
+             {
+               SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND);
+               return bgp_update_packet_eor (peer, afi, safi);
+             }
+         }
+      }
+
+  return NULL;
+}
+
+/* Is there partially written packet or updates we can send right
+   now.  */
+static int
+bgp_write_proceed (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+  struct bgp_advertise *adv;
+
+  if (stream_fifo_head (peer->obuf))
+    return 1;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      if (FIFO_HEAD (&peer->sync[afi][safi]->withdraw))
+       return 1;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      if ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL)
+       if (adv->binfo->uptime < peer->synctime)
+         return 1;
+
+  return 0;
+}
+
+/* Write packet to the peer. */
+int
+bgp_write (struct thread *thread)
+{
+  struct peer *peer;
+  u_char type;
+  struct stream *s; 
+  int num;
+  unsigned int count = 0;
+
+  /* Yes first of all get peer pointer. */
+  peer = THREAD_ARG (thread);
+  peer->t_write = NULL;
+
+  /* For non-blocking IO check. */
+  if (peer->status == Connect)
+    {
+      bgp_connect_check (peer);
+      return 0;
+    }
+
+  s = bgp_write_packet (peer);
+  if (!s)
+    return 0;  /* nothing to send */
+
+  sockopt_cork (peer->fd, 1);
+
+  /* Nonblocking write until TCP output buffer is full.  */
+  do
+    {
+      int writenum;
+
+      /* Number of bytes to be sent.  */
+      writenum = stream_get_endp (s) - stream_get_getp (s);
+
+      /* Call write() system call.  */
+      num = write (peer->fd, STREAM_PNT (s), writenum);
+      if (num < 0)
+       {
+         /* write failed either retry needed or error */
+         if (ERRNO_IO_RETRY(errno))
+               break;
+
+          BGP_EVENT_ADD (peer, TCP_fatal_error);
+         return 0;
+       }
+
+      if (num != writenum)
+       {
+         /* Partial write */
+         stream_forward_getp (s, num);
+         break;
+       }
+
+      /* Retrieve BGP packet type. */
+      stream_set_getp (s, BGP_MARKER_SIZE + 2);
+      type = stream_getc (s);
+
+      switch (type)
+       {
+       case BGP_MSG_OPEN:
+         peer->open_out++;
+         break;
+       case BGP_MSG_UPDATE:
+         peer->update_out++;
+         break;
+       case BGP_MSG_NOTIFY:
+         peer->notify_out++;
+
+         /* Flush any existing events */
+         BGP_EVENT_ADD (peer, BGP_Stop_with_error);
+         goto done;
+
+       case BGP_MSG_KEEPALIVE:
+         peer->keepalive_out++;
+         break;
+       case BGP_MSG_ROUTE_REFRESH_NEW:
+       case BGP_MSG_ROUTE_REFRESH_OLD:
+         peer->refresh_out++;
+         break;
+       case BGP_MSG_CAPABILITY:
+         peer->dynamic_cap_out++;
+         break;
+       }
+
+      /* OK we send packet so delete it. */
+      bgp_packet_delete (peer);
+    }
+  while (++count < BGP_WRITE_PACKET_MAX &&
+        (s = bgp_write_packet (peer)) != NULL);
+  
+  if (bgp_write_proceed (peer))
+    BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+
+ done:
+  sockopt_cork (peer->fd, 0);
+  return 0;
+}
+
+/* This is only for sending NOTIFICATION message to neighbor. */
+static int
+bgp_write_notify (struct peer *peer)
+{
+  int ret, val;
+  u_char type;
+  struct stream *s; 
+
+  /* There should be at least one packet. */
+  s = stream_fifo_head (peer->obuf);
+  if (!s)
+    return 0;
+  assert (stream_get_endp (s) >= BGP_HEADER_SIZE);
+
+  /* Stop collecting data within the socket */
+  sockopt_cork (peer->fd, 0);
+
+  /* socket is in nonblocking mode, if we can't deliver the NOTIFY, well,
+   * we only care about getting a clean shutdown at this point. */
+  ret = write (peer->fd, STREAM_DATA (s), stream_get_endp (s));
+
+  /* only connection reset/close gets counted as TCP_fatal_error, failure
+   * to write the entire NOTIFY doesn't get different FSM treatment */
+  if (ret <= 0)
+    {
+      BGP_EVENT_ADD (peer, TCP_fatal_error);
+      return 0;
+    }
+
+  /* Disable Nagle, make NOTIFY packet go out right away */
+  val = 1;
+  (void) setsockopt (peer->fd, IPPROTO_TCP, TCP_NODELAY,
+                            (char *) &val, sizeof (val));
+
+  /* Retrieve BGP packet type. */
+  stream_set_getp (s, BGP_MARKER_SIZE + 2);
+  type = stream_getc (s);
+
+  assert (type == BGP_MSG_NOTIFY);
+
+  /* Type should be notify. */
+  peer->notify_out++;
+
+  BGP_EVENT_ADD (peer, BGP_Stop_with_error);
+
+  return 0;
+}
+
+/* Make keepalive packet and send it to the peer. */
+void
+bgp_keepalive_send (struct peer *peer)
+{
+  struct stream *s;
+  int length;
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make keepalive packet. */
+  bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE);
+
+  /* Set packet size. */
+  length = bgp_packet_set_size (s);
+
+  /* Dump packet if debug option is set. */
+  /* bgp_packet_dump (s); */
+  if (BGP_DEBUG (keepalive, KEEPALIVE))  
+    zlog_debug ("%s sending KEEPALIVE", peer->host); 
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s send message type %d, length (incl. header) %d",
+               peer->host, BGP_MSG_KEEPALIVE, length);
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+/* Make open packet and send it to the peer. */
+void
+bgp_open_send (struct peer *peer)
+{
+  struct stream *s;
+  int length;
+  u_int16_t send_holdtime;
+  as_t local_as;
+
+  if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+    send_holdtime = peer->holdtime;
+  else
+    send_holdtime = peer->bgp->default_holdtime;
+
+  /* local-as Change */
+  if (peer->change_local_as)
+    local_as = peer->change_local_as; 
+  else
+    local_as = peer->local_as; 
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make open packet. */
+  bgp_packet_set_marker (s, BGP_MSG_OPEN);
+
+  /* Set open packet values. */
+  stream_putc (s, BGP_VERSION_4);        /* BGP version */
+  stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as 
+                                           : BGP_AS_TRANS);
+  stream_putw (s, send_holdtime);       /* Hold Time */
+  stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */
+
+  /* Set capability code. */
+  bgp_open_capability (s, peer);
+
+  /* Set BGP packet length. */
+  length = bgp_packet_set_size (s);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", 
+              peer->host, BGP_VERSION_4, local_as,
+              send_holdtime, inet_ntoa (peer->local_id));
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s send message type %d, length (incl. header) %d",
+              peer->host, BGP_MSG_OPEN, length);
+
+  /* Dump packet if debug option is set. */
+  /* bgp_packet_dump (s); */
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+/* Send BGP notify packet with data potion. */
+void
+bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code,
+                          u_char *data, size_t datalen)
+{
+  struct stream *s;
+  int length;
+
+  /* Allocate new stream. */
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make nitify packet. */
+  bgp_packet_set_marker (s, BGP_MSG_NOTIFY);
+
+  /* Set notify packet values. */
+  stream_putc (s, code);        /* BGP notify code */
+  stream_putc (s, sub_code);   /* BGP notify sub_code */
+
+  /* If notify data is present. */
+  if (data)
+    stream_write (s, data, datalen);
+  
+  /* Set BGP packet length. */
+  length = bgp_packet_set_size (s);
+  
+  /* Add packet to the peer. */
+  stream_fifo_clean (peer->obuf);
+  bgp_packet_add (peer, s);
+
+  /* For debug */
+  {
+    struct bgp_notify bgp_notify;
+    int first = 0;
+    int i;
+    char c[4];
+
+    bgp_notify.code = code;
+    bgp_notify.subcode = sub_code;
+    bgp_notify.data = NULL;
+    bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE;
+    
+    if (bgp_notify.length)
+      {
+       bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3);
+       for (i = 0; i < bgp_notify.length; i++)
+         if (first)
+           {
+             sprintf (c, " %02x", data[i]);
+             strcat (bgp_notify.data, c);
+           }
+         else
+           {
+             first = 1;
+             sprintf (c, "%02x", data[i]);
+             strcpy (bgp_notify.data, c);
+           }
+      }
+    bgp_notify_print (peer, &bgp_notify, "sending");
+
+    if (bgp_notify.data)
+      {
+        XFREE (MTYPE_TMP, bgp_notify.data);
+        bgp_notify.data = NULL;
+        bgp_notify.length = 0;
+      }
+  }
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s send message type %d, length (incl. header) %d",
+              peer->host, BGP_MSG_NOTIFY, length);
+
+  /* peer reset cause */
+  if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE)
+    {
+      if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET)
+      {
+        peer->last_reset = PEER_DOWN_USER_RESET;
+        zlog_info ("Notification sent to neighbor %s:%u: User reset",
+                   peer->host, sockunion_get_port (&peer->su));
+      }
+      else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN)
+      {
+        peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
+        zlog_info ("Notification sent to neighbor %s:%u shutdown",
+                    peer->host, sockunion_get_port (&peer->su));
+      }
+      else
+      {
+        peer->last_reset = PEER_DOWN_NOTIFY_SEND;
+        zlog_info ("Notification sent to neighbor %s:%u: type %u/%u",
+                   peer->host, sockunion_get_port (&peer->su),
+                   code, sub_code);
+      }
+    }
+  else
+     zlog_info ("Notification sent to neighbor %s:%u: configuration change",
+                peer->host, sockunion_get_port (&peer->su));
+
+  /* Call immediately. */
+  BGP_WRITE_OFF (peer->t_write);
+
+  bgp_write_notify (peer);
+}
+
+/* Send BGP notify packet. */
+void
+bgp_notify_send (struct peer *peer, u_char code, u_char sub_code)
+{
+  bgp_notify_send_with_data (peer, code, sub_code, NULL, 0);
+}
+
+/* Send route refresh message to the peer. */
+void
+bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
+                       u_char orf_type, u_char when_to_refresh, int remove)
+{
+  struct stream *s;
+  int length;
+  struct bgp_filter *filter;
+  int orf_refresh = 0;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return;
+
+  filter = &peer->filter[afi][safi];
+
+  /* Adjust safi code. */
+  if (safi == SAFI_MPLS_VPN)
+    safi = SAFI_MPLS_LABELED_VPN;
+  
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make BGP update packet. */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+    bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW);
+  else
+    bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD);
+
+  /* Encode Route Refresh message. */
+  stream_putw (s, afi);
+  stream_putc (s, 0);
+  stream_putc (s, safi);
+  if (orf_type == ORF_TYPE_PREFIX
+      || orf_type == ORF_TYPE_PREFIX_OLD)
+    if (remove || filter->plist[FILTER_IN].plist)
+      {
+       u_int16_t orf_len;
+       unsigned long orfp;
+
+       orf_refresh = 1; 
+       stream_putc (s, when_to_refresh);
+       stream_putc (s, orf_type);
+       orfp = stream_get_endp (s);
+       stream_putw (s, 0);
+
+       if (remove)
+         {
+           UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
+           stream_putc (s, ORF_COMMON_PART_REMOVE_ALL);
+           if (BGP_DEBUG (normal, NORMAL))
+             zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", 
+                        peer->host, orf_type,
+                        (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
+                        afi, safi);
+         }
+       else
+         {
+           SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND);
+           prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].plist,
+                                 ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT,
+                                 ORF_COMMON_PART_DENY);
+           if (BGP_DEBUG (normal, NORMAL))
+             zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", 
+                        peer->host, orf_type,
+                        (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"),
+                        afi, safi);
+         }
+
+       /* Total ORF Entry Len. */
+       orf_len = stream_get_endp (s) - orfp - 2;
+       stream_putw_at (s, orfp, orf_len);
+      }
+
+  /* Set packet size. */
+  length = bgp_packet_set_size (s);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    {
+      if (! orf_refresh)
+       zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", 
+                  peer->host, afi, safi);
+      zlog_debug ("%s send message type %d, length (incl. header) %d",
+                peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ?
+                BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length);
+    }
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+/* Send capability message to the peer. */
+void
+bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi,
+                    int capability_code, int action)
+{
+  struct stream *s;
+  int length;
+
+  /* Adjust safi code. */
+  if (safi == SAFI_MPLS_VPN)
+    safi = SAFI_MPLS_LABELED_VPN;
+
+  s = stream_new (BGP_MAX_PACKET_SIZE);
+
+  /* Make BGP update packet. */
+  bgp_packet_set_marker (s, BGP_MSG_CAPABILITY);
+
+  /* Encode MP_EXT capability. */
+  if (capability_code == CAPABILITY_CODE_MP)
+    {
+      stream_putc (s, action);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, afi);
+      stream_putc (s, 0);
+      stream_putc (s, safi);
+
+      if (BGP_DEBUG (normal, NORMAL))
+        zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d",
+                  peer->host, action == CAPABILITY_ACTION_SET ?
+                  "Advertising" : "Removing", afi, safi);
+    }
+
+  /* Set packet size. */
+  length = bgp_packet_set_size (s);
+
+
+  /* Add packet to the peer. */
+  bgp_packet_add (peer, s);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s send message type %d, length (incl. header) %d",
+              peer->host, BGP_MSG_CAPABILITY, length);
+
+  BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+/* RFC1771 6.8 Connection collision detection. */
+static int
+bgp_collision_detect (struct peer *new, struct in_addr remote_id)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+
+  bgp = bgp_get_default ();
+  if (! bgp)
+    return 0;
+  
+  /* Upon receipt of an OPEN message, the local system must examine
+     all of its connections that are in the OpenConfirm state.  A BGP
+     speaker may also examine connections in an OpenSent state if it
+     knows the BGP Identifier of the peer by means outside of the
+     protocol.  If among these connections there is a connection to a
+     remote BGP speaker whose BGP Identifier equals the one in the
+     OPEN message, then the local system performs the following
+     collision resolution procedure: */
+
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (peer == new)
+        continue;
+      if (!sockunion_same (&peer->su, &new->su))
+        continue;
+      
+      /* Unless allowed via configuration, a connection collision with an
+         existing BGP connection that is in the Established state causes
+         closing of the newly created connection. */
+      if (peer->status == Established)
+        {
+          /* GR may do things slightly differently to classic RFC .  Punt to
+           * open_receive, see below 
+           */
+          if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE))
+            continue;
+          
+          if (new->fd >= 0)
+            {
+              if (BGP_DEBUG (events, EVENTS))
+                 zlog_debug ("%s:%u Existing Established peer, sending NOTIFY",
+                             new->host, sockunion_get_port (&new->su));
+              bgp_notify_send (new, BGP_NOTIFY_CEASE, 
+                               BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+            }
+          return -1;
+        }
+      
+      /* Note: Quagga historically orders explicitly only on the processing
+       * of the Opens, treating 'new' as the passive, inbound and connection
+       * and 'peer' as the active outbound connection.
+       */
+       
+      /* The local_id is always set, so we can match the given remote-ID
+       * from the OPEN against both OpenConfirm and OpenSent peers.
+       */
+      if (peer->status == OpenConfirm || peer->status == OpenSent)
+       {
+         struct peer *out = peer;
+         struct peer *in = new;
+         int ret_close_out = 1, ret_close_in = -1;
+         
+         if (!CHECK_FLAG (new->sflags, PEER_STATUS_ACCEPT_PEER))
+           {
+             out = new;
+             ret_close_out = -1;
+             in = peer;
+             ret_close_in = 1;
+           }
+          
+         /* 1. The BGP Identifier of the local system is compared to
+            the BGP Identifier of the remote system (as specified in
+            the OPEN message). */
+
+         if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr))
+           {
+             /* 2. If the value of the local BGP Identifier is less
+                than the remote one, the local system closes BGP
+                connection that already exists (the one that is
+                already in the OpenConfirm state), and accepts BGP
+                connection initiated by the remote system. */
+
+             if (out->fd >= 0)
+               {
+                 if (BGP_DEBUG (events, EVENTS))
+                    zlog_debug ("%s Collision resolution, remote ID higher,"
+                                " closing outbound", peer->host);
+                 bgp_notify_send (out, BGP_NOTIFY_CEASE, 
+                                  BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+                }
+             return ret_close_out;
+           }
+         else
+           {
+             /* 3. Otherwise, the local system closes newly created
+                BGP connection (the one associated with the newly
+                received OPEN message), and continues to use the
+                existing one (the one that is already in the
+                OpenConfirm state). */
+
+             if (in->fd >= 0)
+               {
+                 if (BGP_DEBUG (events, EVENTS))
+                    zlog_debug ("%s Collision resolution, local ID higher,"
+                                " closing inbound", peer->host);
+
+                  bgp_notify_send (in, BGP_NOTIFY_CEASE, 
+                                  BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+                }
+             return ret_close_in;
+           }
+       }
+    }
+  return 0;
+}
+
+static int
+bgp_open_receive (struct peer *peer, bgp_size_t size)
+{
+  int ret;
+  u_char version;
+  u_char optlen;
+  u_int16_t holdtime;
+  u_int16_t send_holdtime;
+  as_t remote_as;
+  as_t as4 = 0;
+  struct peer *realpeer;
+  struct in_addr remote_id;
+  int mp_capability;
+  u_int8_t notify_data_remote_as[2];
+  u_int8_t notify_data_remote_id[4];
+  u_int16_t *holdtime_ptr;
+
+  realpeer = NULL;
+  
+  /* Parse open packet. */
+  version = stream_getc (peer->ibuf);
+  memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2);
+  remote_as  = stream_getw (peer->ibuf);
+  holdtime_ptr = (u_int16_t *)stream_pnt (peer->ibuf);
+  holdtime = stream_getw (peer->ibuf);
+  memcpy (notify_data_remote_id, stream_pnt (peer->ibuf), 4);
+  remote_id.s_addr = stream_get_ipv4 (peer->ibuf);
+
+  /* Receive OPEN message log  */
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u,"
+                " holdtime %d, id %s, %sbound connection",
+               peer->host, version, remote_as, holdtime,
+               inet_ntoa (remote_id),
+               CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)
+                 ? "in" : "out");
+  
+  /* BEGIN to read the capability here, but dont do it yet */
+  mp_capability = 0;
+  optlen = stream_getc (peer->ibuf);
+  
+  if (optlen != 0)
+    {
+      /* We need the as4 capability value *right now* because
+       * if it is there, we have not got the remote_as yet, and without
+       * that we do not know which peer is connecting to us now.
+       */ 
+      as4 = peek_for_as4_capability (peer, optlen);
+    }
+  
+  /* Just in case we have a silly peer who sends AS4 capability set to 0 */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4)
+    {
+      zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0",
+                peer->host);
+      bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                       BGP_NOTIFY_OPEN_BAD_PEER_AS);
+      return -1;
+    }
+  
+  if (remote_as == BGP_AS_TRANS)
+    {
+         /* Take the AS4 from the capability.  We must have received the
+          * capability now!  Otherwise we have a asn16 peer who uses
+          * BGP_AS_TRANS, for some unknown reason.
+          */
+      if (as4 == BGP_AS_TRANS)
+        {
+          zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed",
+                    peer->host);
+          bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                 BGP_NOTIFY_OPEN_BAD_PEER_AS);
+          return -1;
+        }
+      
+      if (!as4 && BGP_DEBUG (as4, AS4))
+        zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4."
+                    " Odd, but proceeding.", peer->host);
+      else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4))
+        zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits "
+                    "in 2-bytes, very odd peer.", peer->host, as4);
+      if (as4)
+        remote_as = as4;
+    } 
+  else 
+    {
+      /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */
+      /* If we have got the capability, peer->as4cap must match remote_as */
+      if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)
+          && as4 != remote_as)
+        {
+         /* raise error, log this, close session */
+         zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u"
+                   " mismatch with 16bit 'myasn' %u in open",
+                   peer->host, as4, remote_as);
+         bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR,
+                          BGP_NOTIFY_OPEN_BAD_PEER_AS);
+         return -1;
+       }
+    }
+
+  /* Lookup peer from Open packet. */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    {
+      int as = 0;
+
+      realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as);
+
+      if (! realpeer)
+       {
+         /* Peer's source IP address is check in bgp_accept(), so this
+            must be AS number mismatch or remote-id configuration
+            mismatch. */
+         if (as)
+           {
+             if (BGP_DEBUG (normal, NORMAL))
+               zlog_debug ("%s bad OPEN, wrong router identifier %s",
+                           peer->host, inet_ntoa (remote_id));
+             bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, 
+                                        BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
+                                        notify_data_remote_id, 4);
+           }
+         else
+           {
+             if (BGP_DEBUG (normal, NORMAL))
+               zlog_debug ("%s bad OPEN, remote AS is %u, expected %u",
+                           peer->host, remote_as, peer->as);
+             bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR,
+                                        BGP_NOTIFY_OPEN_BAD_PEER_AS,
+                                        notify_data_remote_as, 2);
+           }
+         return -1;
+       }
+    }
+
+  /* When collision is detected and this peer is closed.  Retrun
+     immidiately. */
+  ret = bgp_collision_detect (peer, remote_id);
+  if (ret < 0)
+    return ret;
+
+  /* Bit hacky */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    { 
+      /* Connection FSM state is intertwined with our peer configuration
+       * (the RFC encourages this a bit).  At _this_ point we have a
+       * 'realpeer' which represents the configuration and any earlier FSM
+       * (outbound, unless the remote side has opened two connections to
+       * us), and a 'peer' which here represents an inbound connection that
+       * has not yet been reconciled with a 'realpeer'.  
+       * 
+       * As 'peer' has just sent an OPEN that reconciliation must now
+       * happen, as only the 'realpeer' can ever proceed to Established.
+       *
+       * bgp_collision_detect should have resolved any collisions with
+       * realpeers that are in states OpenSent, OpenConfirm or Established,
+       * and may have sent a notify on the 'realpeer' connection. 
+       * bgp_accept will have rejected any connections where the 'realpeer'
+       * is in Idle or >Established (though, that status may have changed
+       * since).
+       *
+       * Need to finish off any reconciliation here, and ensure that
+       * 'realpeer' is left holding any needed state from the appropriate
+       * connection (fd, buffers, etc.), and any state from the other
+       * connection is cleaned up.
+       */
+
+      /* Is realpeer in some globally-down state, that precludes any and all
+       * connections (Idle, Clearing, Deleted, etc.)?
+       */
+      if (realpeer->status == Idle || realpeer->status > Established)
+        {
+          if (BGP_DEBUG (events, EVENTS))
+            zlog_debug ("%s peer status is %s, closing the new connection",
+                        realpeer->host, 
+                        LOOKUP (bgp_status_msg, realpeer->status));
+          return -1;
+        }
+      
+      /* GR does things differently, and prefers any new connection attempts
+       * over an Established one (why not just rely on KEEPALIVE and avoid
+       * having to special case this?) */
+      if (realpeer->status == Established
+           && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE))
+       {
+         realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+         SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT);
+       }
+      else if (ret == 0) 
+       {
+         /* If we're here, RFC collision-detect did not reconcile the
+          * connections, and the 'realpeer' is still available.  So
+          * 'realpeer' must be 'Active' or 'Connect'.
+          *
+          * According to the RFC we should just let this connection (of the
+          * accepted 'peer') continue on to Established if the other
+          * onnection (the 'realpeer') is in a more larval state, and
+          * reconcile them when OPEN is sent on the 'realpeer'.
+          *
+          * However, the accepted 'peer' must be reconciled with 'peer' at
+          * this point, due to the implementation, if 'peer' is to be able
+          * to proceed.  So it should be allowed to go to Established, as
+          * long as the 'realpeer' was in Active or Connect state - which
+          * /should/ be the case if we're here.
+          *
+          * So we should only need to sanity check that that is the case
+          * here, and allow the code to get on with transferring the 'peer'
+          * connection state over.
+          */
+          if (realpeer->status != Active && realpeer->status != Connect)
+            {
+              if (BGP_DEBUG (events, EVENTS))
+                zlog_warn ("%s real peer status should be Active or Connect,"
+                            " but is %s",
+                            realpeer->host, 
+                            LOOKUP (bgp_status_msg, realpeer->status));
+             bgp_notify_send (realpeer, BGP_NOTIFY_CEASE,
+                              BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+            }
+       }
+
+      if (BGP_DEBUG (events, EVENTS))
+       zlog_debug ("%s:%u [Event] Transfer accept BGP peer to real (state %s)",
+                  peer->host, sockunion_get_port (&peer->su), 
+                  LOOKUP (bgp_status_msg, realpeer->status));
+
+      bgp_stop (realpeer);
+      
+      /* Transfer file descriptor. */
+      realpeer->fd = peer->fd;
+      peer->fd = -1;
+
+      /* Transfer input buffer. */
+      stream_free (realpeer->ibuf);
+      realpeer->ibuf = peer->ibuf;
+      realpeer->packet_size = peer->packet_size;
+      peer->ibuf = NULL;
+      
+      /* Transfer output buffer, there may be an OPEN queued to send */
+      stream_fifo_free (realpeer->obuf);
+      realpeer->obuf = peer->obuf;
+      peer->obuf = NULL;
+      
+      bool open_deferred
+        = CHECK_FLAG (peer->sflags, PEER_STATUS_OPEN_DEFERRED);
+      
+      /* Transfer status. */
+      realpeer->status = peer->status;
+      bgp_stop (peer);
+      
+      /* peer pointer change */
+      peer = realpeer;
+      
+      if (peer->fd < 0)
+       {
+         zlog_err ("bgp_open_receive peer's fd is negative value %d",
+                   peer->fd);
+         return -1;
+       }
+      BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+      if (stream_fifo_head (peer->obuf))
+        BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+      
+      /* hack: we may defer OPEN on accept peers, when there seems to be a
+       * realpeer in progress, when an accept peer connection is opened. This
+       * is to avoid interoperability issues, with test/conformance tools
+       * particularly. See bgp_fsm.c::bgp_connect_success
+       *
+       * If OPEN was deferred there, then we must send it now.
+       */
+      if (open_deferred)
+        bgp_open_send (peer);
+    }
+
+  /* remote router-id check. */
+  if (remote_id.s_addr == 0
+      || IPV4_CLASS_DE (ntohl (remote_id.s_addr))
+      || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr))
+    {
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s bad OPEN, wrong router identifier %s",
+                  peer->host, inet_ntoa (remote_id));
+      bgp_notify_send_with_data (peer, 
+                                BGP_NOTIFY_OPEN_ERR, 
+                                BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
+                                notify_data_remote_id, 4);
+      return -1;
+    }
+
+  /* Set remote router-id */
+  peer->remote_id = remote_id;
+
+  /* Peer BGP version check. */
+  if (version != BGP_VERSION_4)
+    {
+      u_int16_t maxver = htons(BGP_VERSION_4);
+      /* XXX this reply may not be correct if version < 4  XXX */
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s bad protocol version, remote requested %d, local request %d",
+                  peer->host, version, BGP_VERSION_4);
+      /* Data must be in network byte order here */
+      bgp_notify_send_with_data (peer, 
+                                BGP_NOTIFY_OPEN_ERR, 
+                                BGP_NOTIFY_OPEN_UNSUP_VERSION,
+                                (u_int8_t *) &maxver, 2);
+      return -1;
+    }
+
+  /* Check neighbor as number. */
+  if (remote_as != peer->as)
+    {
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s bad OPEN, remote AS is %u, expected %u",
+                  peer->host, remote_as, peer->as);
+      bgp_notify_send_with_data (peer, 
+                                BGP_NOTIFY_OPEN_ERR, 
+                                BGP_NOTIFY_OPEN_BAD_PEER_AS,
+                                notify_data_remote_as, 2);
+      return -1;
+    }
+
+  /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST
+     calculate the value of the Hold Timer by using the smaller of its
+     configured Hold Time and the Hold Time received in the OPEN message.
+     The Hold Time MUST be either zero or at least three seconds.  An
+     implementation may reject connections on the basis of the Hold Time. */
+
+  if (holdtime < 3 && holdtime != 0)
+    {
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_OPEN_ERR,
+                                BGP_NOTIFY_OPEN_UNACEP_HOLDTIME,
+                                 (u_int8_t *)holdtime_ptr, 2);
+      return -1;
+    }
+    
+  /* From the rfc: A reasonable maximum time between KEEPALIVE messages
+     would be one third of the Hold Time interval.  KEEPALIVE messages
+     MUST NOT be sent more frequently than one per second.  An
+     implementation MAY adjust the rate at which it sends KEEPALIVE
+     messages as a function of the Hold Time interval. */
+
+  if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+    send_holdtime = peer->holdtime;
+  else
+    send_holdtime = peer->bgp->default_holdtime;
+
+  if (holdtime < send_holdtime)
+    peer->v_holdtime = holdtime;
+  else
+    peer->v_holdtime = send_holdtime;
+
+  peer->v_keepalive = peer->v_holdtime / 3;
+
+  /* Open option part parse. */
+  if (optlen != 0) 
+    {
+      if ((ret = bgp_open_option_parse (peer, optlen, &mp_capability)) < 0)
+        {
+          bgp_notify_send (peer,
+                 BGP_NOTIFY_OPEN_ERR,
+                 BGP_NOTIFY_OPEN_UNSPECIFIC);
+         return ret;
+        }
+    }
+  else
+    {
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0",
+                  peer->host);
+    }
+
+  /* 
+   * Assume that the peer supports the locally configured set of
+   * AFI/SAFIs if the peer did not send us any Mulitiprotocol
+   * capabilities, or if 'override-capability' is configured.
+   */
+  if (! mp_capability ||
+      CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+    {
+      peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST];
+      peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST];
+      peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST];
+      peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST];
+    }
+
+  /* Get sockname. */
+  bgp_getsockname (peer);
+  peer->rtt = sockopt_tcp_rtt (peer->fd);
+
+  BGP_EVENT_ADD (peer, Receive_OPEN_message);
+
+  peer->packet_size = 0;
+  if (peer->ibuf)
+    stream_reset (peer->ibuf);
+
+  return 0;
+}
+
+/* Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers */
+int
+bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet)
+{
+  switch (packet->safi)
+    {
+      case SAFI_UNICAST:
+      case SAFI_MULTICAST:
+        return bgp_nlri_parse_ip (peer, attr, packet);
+      case SAFI_MPLS_VPN:
+      case SAFI_MPLS_LABELED_VPN:
+        return bgp_nlri_parse_vpn (peer, attr, packet);
+      case SAFI_ENCAP:
+        return bgp_nlri_parse_encap (peer, attr, packet);
+    }
+  return -1;
+}
+
+/* Parse BGP Update packet and make attribute object. */
+static int
+bgp_update_receive (struct peer *peer, bgp_size_t size)
+{
+  int ret, nlri_ret;
+  u_char *end;
+  struct stream *s;
+  struct attr attr;
+  struct attr_extra extra;
+  bgp_size_t attribute_len;
+  bgp_size_t update_len;
+  bgp_size_t withdraw_len;
+  int i;
+  
+  enum NLRI_TYPES {
+    NLRI_UPDATE,
+    NLRI_WITHDRAW,
+    NLRI_MP_UPDATE,
+    NLRI_MP_WITHDRAW,
+    NLRI_TYPE_MAX,
+  };
+  struct bgp_nlri nlris[NLRI_TYPE_MAX];
+
+  /* Status must be Established. */
+  if (peer->status != Established) 
+    {
+      zlog_err ("%s [FSM] Update packet received under status %s",
+               peer->host, LOOKUP (bgp_status_msg, peer->status));
+      bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+      return -1;
+    }
+
+  /* Set initial values. */
+  memset (&attr, 0, sizeof (struct attr));
+  memset (&extra, 0, sizeof (struct attr_extra));
+  memset (&nlris, 0, sizeof nlris);
+
+  attr.extra = &extra;
+
+  s = peer->ibuf;
+  end = stream_pnt (s) + size;
+
+  /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute
+     Length is too large (i.e., if Unfeasible Routes Length + Total
+     Attribute Length + 23 exceeds the message Length), then the Error
+     Subcode is set to Malformed Attribute List.  */
+  if (stream_pnt (s) + 2 > end)
+    {
+      zlog_err ("%s [Error] Update packet error"
+               " (packet length is short for unfeasible length)",
+               peer->host);
+      bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, 
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
+      return -1;
+    }
+
+  /* Unfeasible Route Length. */
+  withdraw_len = stream_getw (s);
+
+  /* Unfeasible Route Length check. */
+  if (stream_pnt (s) + withdraw_len > end)
+    {
+      zlog_err ("%s [Error] Update packet error"
+               " (packet unfeasible length overflow %d)",
+               peer->host, withdraw_len);
+      bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, 
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
+      return -1;
+    }
+
+  /* Unfeasible Route packet format check. */
+  if (withdraw_len > 0)
+    {
+      nlris[NLRI_WITHDRAW].afi = AFI_IP;
+      nlris[NLRI_WITHDRAW].safi = SAFI_UNICAST;
+      nlris[NLRI_WITHDRAW].nlri = stream_pnt (s);
+      nlris[NLRI_WITHDRAW].length = withdraw_len;
+      
+      if (BGP_DEBUG (packet, PACKET_RECV))
+       zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host);
+
+      stream_forward_getp (s, withdraw_len);
+    }
+  
+  /* Attribute total length check. */
+  if (stream_pnt (s) + 2 > end)
+    {
+      zlog_warn ("%s [Error] Packet Error"
+                " (update packet is short for attribute length)",
+                peer->host);
+      bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, 
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
+      return -1;
+    }
+
+  /* Fetch attribute total length. */
+  attribute_len = stream_getw (s);
+
+  /* Attribute length check. */
+  if (stream_pnt (s) + attribute_len > end)
+    {
+      zlog_warn ("%s [Error] Packet Error"
+                " (update packet attribute length overflow %d)",
+                peer->host, attribute_len);
+      bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, 
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
+      return -1;
+    }
+  
+  /* Certain attribute parsing errors should not be considered bad enough
+   * to reset the session for, most particularly any partial/optional
+   * attributes that have 'tunneled' over speakers that don't understand
+   * them. Instead we withdraw only the prefix concerned.
+   * 
+   * Complicates the flow a little though..
+   */
+  bgp_attr_parse_ret_t attr_parse_ret = BGP_ATTR_PARSE_PROCEED;
+  /* This define morphs the update case into a withdraw when lower levels
+   * have signalled an error condition where this is best.
+   */
+#define NLRI_ATTR_ARG (attr_parse_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL)
+
+  /* Parse attribute when it exists. */
+  if (attribute_len)
+    {
+      attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, 
+                           &nlris[NLRI_MP_UPDATE], &nlris[NLRI_MP_WITHDRAW]);
+      if (attr_parse_ret == BGP_ATTR_PARSE_ERROR)
+       {
+         bgp_attr_unintern_sub (&attr);
+          bgp_attr_flush (&attr);
+         return -1;
+       }
+    }
+  
+  /* Logging the attribute. */
+  if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW
+      || BGP_DEBUG (update, UPDATE_IN))
+    {
+      char attrstr[BUFSIZ];
+      attrstr[0] = '\0';
+
+      ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ);
+      int lvl = (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW)
+                 ? LOG_ERR : LOG_DEBUG;
+      
+      if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW)
+        zlog (peer->log, LOG_ERR,
+              "%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.",
+              peer->host);
+
+      if (ret)
+       zlog (peer->log, lvl, "%s rcvd UPDATE w/ attr: %s",
+             peer->host, attrstr);
+    }
+  
+  /* Network Layer Reachability Information. */
+  update_len = end - stream_pnt (s);
+
+  if (update_len)
+    {
+      /* Set NLRI portion to structure. */
+      nlris[NLRI_UPDATE].afi = AFI_IP;
+      nlris[NLRI_UPDATE].safi = SAFI_UNICAST;
+      nlris[NLRI_UPDATE].nlri = stream_pnt (s);
+      nlris[NLRI_UPDATE].length = update_len;
+      
+      stream_forward_getp (s, update_len);
+    }
+  
+  /* Parse any given NLRIs */
+  for (i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++)
+    {
+      if (!nlris[i].nlri) continue;
+      
+      /* We use afi and safi as indices into tables and what not.  It would
+       * be impossible, at this time, to support unknown afi/safis.  And
+       * anyway, the peer needs to be configured to enable the afi/safi
+       * explicitly which requires UI support.
+       *
+       * Ignore unknown afi/safi NLRIs.
+       *
+       * Note: this means nlri[x].afi/safi still can not be trusted for
+       * indexing later in this function!
+       *
+       * Note2: This will also remap the wire code-point for VPN safi to the
+       * internal safi_t point, as needs be.
+       */
+      if (!bgp_afi_safi_valid_indices (nlris[i].afi, &nlris[i].safi))
+        {
+          plog_info (peer->log,
+                     "%s [Info] UPDATE with unsupported AFI/SAFI %u/%u",
+                     peer->host, nlris[i].afi, nlris[i].safi);
+          continue;
+        }
+      
+      /* NLRI is processed only when the peer is configured specific
+         Address Family and Subsequent Address Family. */
+      if (!peer->afc[nlris[i].afi][nlris[i].safi])
+        {
+          plog_info (peer->log,
+                     "%s [Info] UPDATE for non-enabled AFI/SAFI %u/%u",
+                     peer->host, nlris[i].afi, nlris[i].safi);
+          continue;
+        }
+      
+      /* EoR handled later */
+      if (nlris[i].length == 0)
+        continue;
+      
+      switch (i)
+        {
+          case NLRI_UPDATE:
+          case NLRI_MP_UPDATE:
+            nlri_ret = bgp_nlri_parse (peer, NLRI_ATTR_ARG, &nlris[i]);
+            break;
+          case NLRI_WITHDRAW:
+          case NLRI_MP_WITHDRAW:
+            nlri_ret = bgp_nlri_parse (peer, NULL, &nlris[i]);
+        }
+      
+      if (nlri_ret < 0)
+        {
+          plog_err (peer->log, 
+                    "%s [Error] Error parsing NLRI", peer->host);
+          if (peer->status == Established)
+            bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+                             i <= NLRI_WITHDRAW 
+                               ? BGP_NOTIFY_UPDATE_INVAL_NETWORK
+                               : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
+          bgp_attr_unintern_sub (&attr);
+          return -1;
+        }
+    }
+  
+  /* EoR checks.
+   *
+   * Non-MP IPv4/Unicast EoR is a completely empty UPDATE
+   * and MP EoR should have only an empty MP_UNREACH
+   */
+  if (!update_len && !withdraw_len
+      && nlris[NLRI_MP_UPDATE].length == 0)
+    {
+      afi_t afi = 0;
+      safi_t safi;
+      
+      /* Non-MP IPv4/Unicast is a completely empty UPDATE - already
+       * checked update and withdraw NLRI lengths are 0.
+       */ 
+      if (!attribute_len)
+        {
+          afi = AFI_IP;
+          safi = SAFI_UNICAST;
+        }
+      /* otherwise MP AFI/SAFI is an empty update, other than an empty
+       * MP_UNREACH_NLRI attr (with an AFI/SAFI we recognise).
+       */
+      else if (attr.flag == BGP_ATTR_MP_UNREACH_NLRI
+               && nlris[NLRI_MP_WITHDRAW].length == 0
+               && bgp_afi_safi_valid_indices (nlris[NLRI_MP_WITHDRAW].afi,
+                                              &nlris[NLRI_MP_WITHDRAW].safi))
+        {
+          afi = nlris[NLRI_MP_WITHDRAW].afi;
+          safi = nlris[NLRI_MP_WITHDRAW].safi;
+        }
+      
+      if (afi && peer->afc[afi][safi])
+        {
+         /* End-of-RIB received */
+         SET_FLAG (peer->af_sflags[afi][safi],
+                   PEER_STATUS_EOR_RECEIVED);
+
+         /* NSF delete stale route */
+         if (peer->nsf[afi][safi])
+           bgp_clear_stale_route (peer, afi, safi);
+
+         if (BGP_DEBUG (normal, NORMAL))
+           zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for %s from %s",
+                 peer->host, afi_safi_print (afi, safi));
+        }
+    }
+  
+  /* Everything is done.  We unintern temporary structures which
+     interned in bgp_attr_parse(). */
+  bgp_attr_unintern_sub (&attr);
+  bgp_attr_flush (&attr);
+
+  /* If peering is stopped due to some reason, do not generate BGP
+     event.  */
+  if (peer->status != Established)
+    return 0;
+
+  /* Increment packet counter. */
+  peer->update_in++;
+  peer->update_time = bgp_clock ();
+
+  /* Rearm holdtime timer */
+  BGP_TIMER_OFF (peer->t_holdtime);
+  bgp_timer_set (peer);
+
+  return 0;
+}
+
+/* Notify message treatment function. */
+static void
+bgp_notify_receive (struct peer *peer, bgp_size_t size)
+{
+  struct bgp_notify bgp_notify;
+
+  if (peer->notify.data)
+    {
+      XFREE (MTYPE_TMP, peer->notify.data);
+      peer->notify.data = NULL;
+      peer->notify.length = 0;
+    }
+
+  bgp_notify.code = stream_getc (peer->ibuf);
+  bgp_notify.subcode = stream_getc (peer->ibuf);
+  bgp_notify.length = size - 2;
+  bgp_notify.data = NULL;
+
+  /* Preserv notify code and sub code. */
+  peer->notify.code = bgp_notify.code;
+  peer->notify.subcode = bgp_notify.subcode;
+  /* For further diagnostic record returned Data. */
+  if (bgp_notify.length)
+    {
+      peer->notify.length = size - 2;
+      peer->notify.data = XMALLOC (MTYPE_TMP, size - 2);
+      memcpy (peer->notify.data, stream_pnt (peer->ibuf), size - 2);
+    }
+
+  /* For debug */
+  {
+    int i;
+    int first = 0;
+    char c[4];
+
+    if (bgp_notify.length)
+      {
+       bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3);
+       for (i = 0; i < bgp_notify.length; i++)
+         if (first)
+           {
+             sprintf (c, " %02x", stream_getc (peer->ibuf));
+             strcat (bgp_notify.data, c);
+           }
+         else
+           {
+             first = 1;
+             sprintf (c, "%02x", stream_getc (peer->ibuf));
+             strcpy (bgp_notify.data, c);
+           }
+      }
+
+    bgp_notify_print(peer, &bgp_notify, "received");
+    if (bgp_notify.data)
+      {
+        XFREE (MTYPE_TMP, bgp_notify.data);
+        bgp_notify.data = NULL;
+        bgp_notify.length = 0;
+      }
+  }
+
+  /* peer count update */
+  peer->notify_in++;
+
+  if (peer->status == Established)
+    peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED;
+
+  /* We have to check for Notify with Unsupported Optional Parameter.
+     in that case we fallback to open without the capability option.
+     But this done in bgp_stop. We just mark it here to avoid changing
+     the fsm tables.  */
+  if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR &&
+      bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM )
+    UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+  BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message);
+}
+
+/* Keepalive treatment function -- get keepalive send keepalive */
+static void
+bgp_keepalive_receive (struct peer *peer, bgp_size_t size)
+{
+  if (BGP_DEBUG (keepalive, KEEPALIVE))  
+    zlog_debug ("%s KEEPALIVE rcvd", peer->host); 
+  
+  BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message);
+}
+
+/* Route refresh message is received. */
+static void
+bgp_route_refresh_receive (struct peer *peer, bgp_size_t size)
+{
+  afi_t afi;
+  safi_t safi;
+  struct stream *s;
+
+  /* If peer does not have the capability, send notification. */
+  if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV))
+    {
+      plog_err (peer->log, "%s [Error] BGP route refresh is not enabled",
+               peer->host);
+      bgp_notify_send (peer,
+                      BGP_NOTIFY_HEADER_ERR,
+                      BGP_NOTIFY_HEADER_BAD_MESTYPE);
+      return;
+    }
+
+  /* Status must be Established. */
+  if (peer->status != Established) 
+    {
+      plog_err (peer->log,
+               "%s [Error] Route refresh packet received under status %s",
+               peer->host, LOOKUP (bgp_status_msg, peer->status));
+      bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+      return;
+    }
+
+  s = peer->ibuf;
+  
+  /* Parse packet. */
+  afi = stream_getw (s);
+  /* reserved byte */
+  stream_getc (s);
+  safi = stream_getc (s);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d",
+              peer->host, afi, safi);
+
+  /* Check AFI and SAFI. */
+  if ((afi != AFI_IP && afi != AFI_IP6)
+      || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+         && safi != SAFI_MPLS_LABELED_VPN))
+    {
+      if (BGP_DEBUG (normal, NORMAL))
+       {
+         zlog_debug ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored",
+                    peer->host, afi, safi);
+       }
+      return;
+    }
+
+  /* Adjust safi code. */
+  if (safi == SAFI_MPLS_LABELED_VPN)
+    safi = SAFI_MPLS_VPN;
+
+  if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE)
+    {
+      u_char *end;
+      u_char when_to_refresh;
+      u_char orf_type;
+      u_int16_t orf_len;
+
+      if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5)
+        {
+          zlog_info ("%s ORF route refresh length error", peer->host);
+          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+          return;
+        }
+
+      when_to_refresh = stream_getc (s);
+      end = stream_pnt (s) + (size - 5);
+
+      while ((stream_pnt (s) + 2) < end)
+       {
+         orf_type = stream_getc (s); 
+         orf_len = stream_getw (s);
+         
+         /* orf_len in bounds? */
+         if ((stream_pnt (s) + orf_len) > end)
+           break; /* XXX: Notify instead?? */
+         if (orf_type == ORF_TYPE_PREFIX
+             || orf_type == ORF_TYPE_PREFIX_OLD)
+           {
+             uint8_t *p_pnt = stream_pnt (s);
+             uint8_t *p_end = stream_pnt (s) + orf_len;
+             struct orf_prefix orfp;
+             u_char common = 0;
+             u_int32_t seq;
+             int psize;
+             char name[BUFSIZ];
+             int ret;
+
+             if (BGP_DEBUG (normal, NORMAL))
+               {
+                 zlog_debug ("%s rcvd Prefixlist ORF(%d) length %d",
+                            peer->host, orf_type, orf_len);
+               }
+
+              /* we're going to read at least 1 byte of common ORF header,
+               * and 7 bytes of ORF Address-filter entry from the stream
+               */
+              if (orf_len < 7)
+                break; 
+                
+             /* ORF prefix-list name */
+             sprintf (name, "%s.%d.%d", peer->host, afi, safi);
+
+             while (p_pnt < p_end)
+               {
+                  /* If the ORF entry is malformed, want to read as much of it
+                   * as possible without going beyond the bounds of the entry,
+                   * to maximise debug information.
+                   */
+                 int ok;
+                 memset (&orfp, 0, sizeof (struct orf_prefix));
+                 common = *p_pnt++;
+                 /* after ++: p_pnt <= p_end */
+                 if (common & ORF_COMMON_PART_REMOVE_ALL)
+                   {
+                     if (BGP_DEBUG (normal, NORMAL))
+                       zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host);
+                     prefix_bgp_orf_remove_all (afi, name);
+                     break;
+                   }
+                 ok = ((size_t)(p_end - p_pnt) >= sizeof(u_int32_t)) ;
+                 if (ok)
+                   {
+                     memcpy (&seq, p_pnt, sizeof (u_int32_t));
+                      p_pnt += sizeof (u_int32_t);
+                      orfp.seq = ntohl (seq);
+                   }
+                 else
+                   p_pnt = p_end ;
+
+                 if ((ok = (p_pnt < p_end)))
+                   orfp.ge = *p_pnt++ ;      /* value checked in prefix_bgp_orf_set() */
+                 if ((ok = (p_pnt < p_end)))
+                   orfp.le = *p_pnt++ ;      /* value checked in prefix_bgp_orf_set() */
+                 if ((ok = (p_pnt < p_end)))
+                   orfp.p.prefixlen = *p_pnt++ ;
+                 orfp.p.family = afi2family (afi);   /* afi checked already  */
+
+                 psize = PSIZE (orfp.p.prefixlen);   /* 0 if not ok          */
+                 if (psize > prefix_blen(&orfp.p))   /* valid for family ?   */
+                   {
+                     ok = 0 ;
+                     psize = prefix_blen(&orfp.p) ;
+                   }
+                 if (psize > (p_end - p_pnt))        /* valid for packet ?   */
+                   {
+                     ok = 0 ;
+                     psize = p_end - p_pnt ;
+                   }
+
+                 if (psize > 0)
+                   memcpy (&orfp.p.u.prefix, p_pnt, psize);
+                 p_pnt += psize;
+
+                 if (BGP_DEBUG (normal, NORMAL))
+                   {
+                     char buf[INET6_BUFSIZ];
+
+                     zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s",
+                                peer->host,
+                                (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"),
+                                (common & ORF_COMMON_PART_DENY ? "deny" : "permit"),
+                                orfp.seq,
+                                inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, INET6_BUFSIZ),
+                                orfp.p.prefixlen, orfp.ge, orfp.le,
+                                ok ? "" : " MALFORMED");
+                   }
+
+                 if (ok)
+                   ret = prefix_bgp_orf_set (name, afi, &orfp,
+                                  (common & ORF_COMMON_PART_DENY ? 0 : 1 ),
+                                  (common & ORF_COMMON_PART_REMOVE ? 0 : 1));
+                  
+                 if (!ok || (ok && ret != CMD_SUCCESS))
+                   {
+                     if (BGP_DEBUG (normal, NORMAL))
+                       zlog_debug ("%s Received misformatted prefixlist ORF."
+                                   " Remove All pfxlist", peer->host);
+                     prefix_bgp_orf_remove_all (afi, name);
+                     break;
+                   }
+               }
+             peer->orf_plist[afi][safi] =
+                        prefix_bgp_orf_lookup (afi, name);
+           }
+         stream_forward_getp (s, orf_len);
+       }
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s rcvd Refresh %s ORF request", peer->host,
+                  when_to_refresh == REFRESH_DEFER ? "Defer" : "Immediate");
+      if (when_to_refresh == REFRESH_DEFER)
+       return;
+    }
+
+  /* First update is deferred until ORF or ROUTE-REFRESH is received */
+  if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH))
+    UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
+
+  /* Perform route refreshment to the peer */
+  bgp_announce_route (peer, afi, safi);
+}
+
+static int
+bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
+{
+  u_char *end;
+  struct capability_mp_data mpc;
+  struct capability_header *hdr;
+  u_char action;
+  afi_t afi;
+  safi_t safi;
+
+  end = pnt + length;
+
+  while (pnt < end)
+    {      
+      /* We need at least action, capability code and capability length. */
+      if (pnt + 3 > end)
+        {
+          zlog_info ("%s Capability length error", peer->host);
+          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+          return -1;
+        }
+      action = *pnt;
+      hdr = (struct capability_header *)(pnt + 1);
+      
+      /* Action value check.  */
+      if (action != CAPABILITY_ACTION_SET
+         && action != CAPABILITY_ACTION_UNSET)
+        {
+          zlog_info ("%s Capability Action Value error %d",
+                    peer->host, action);
+          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+          return -1;
+        }
+
+      if (BGP_DEBUG (normal, NORMAL))
+       zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u",
+                  peer->host, action, hdr->code, hdr->length);
+
+      /* Capability length check. */
+      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 (hdr->code == CAPABILITY_CODE_MP)
+        {
+         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 "
+                            "(%u/%u)", peer->host, afi, safi);
+              continue;
+            }
+          
+         /* Address family check.  */
+          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, BGP_CLEAR_ROUTE_NORMAL);
+              else
+                BGP_EVENT_ADD (peer, BGP_Stop);
+            }
+        }
+      else
+        {
+          zlog_warn ("%s unrecognized capability code: %d - ignored",
+                     peer->host, hdr->code);
+        }
+      pnt += hdr->length + 3;
+    }
+  return 0;
+}
+
+/* Dynamic Capability is received. 
+ *
+ * This is exported for unit-test purposes
+ */
+int
+bgp_capability_receive (struct peer *peer, bgp_size_t size)
+{
+  u_char *pnt;
+
+  /* Fetch pointer. */
+  pnt = stream_pnt (peer->ibuf);
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("%s rcv CAPABILITY", peer->host);
+
+  /* If peer does not have the capability, send notification. */
+  if (! CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV))
+    {
+      plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled",
+               peer->host);
+      bgp_notify_send (peer,
+                      BGP_NOTIFY_HEADER_ERR,
+                      BGP_NOTIFY_HEADER_BAD_MESTYPE);
+      return -1;
+    }
+
+  /* Status must be Established. */
+  if (peer->status != Established)
+    {
+      plog_err (peer->log,
+               "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status));
+      bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0);
+      return -1;
+    }
+
+  /* Parse packet. */
+  return bgp_capability_msg_parse (peer, pnt, size);
+}
+
+/* BGP read utility function. */
+static int
+bgp_read_packet (struct peer *peer)
+{
+  int nbytes;
+  int readsize;
+
+  readsize = peer->packet_size - stream_get_endp (peer->ibuf);
+
+  /* If size is zero then return. */
+  if (! readsize)
+    return 0;
+
+  /* Read packet from fd. */
+  nbytes = stream_read_try (peer->ibuf, peer->fd, readsize);
+
+  /* If read byte is smaller than zero then error occured. */
+  if (nbytes < 0) 
+    {
+      /* Transient error should retry */
+      if (nbytes == -2)
+       return -1;
+
+      plog_err (peer->log, "%s [Error] bgp_read_packet error: %s",
+                peer->host, safe_strerror (errno));
+
+      if (peer->status == Established) 
+       {
+         if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE))
+           {
+             peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+             SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+           }
+         else
+           peer->last_reset = PEER_DOWN_CLOSE_SESSION;
+       }
+
+      BGP_EVENT_ADD (peer, TCP_fatal_error);
+      return -1;
+    }  
+
+  /* When read byte is zero : clear bgp peer and return */
+  if (nbytes == 0) 
+    {
+      if (BGP_DEBUG (events, EVENTS))
+       plog_debug (peer->log, "%s [Event] BGP connection closed fd %d",
+                  peer->host, peer->fd);
+
+      if (peer->status == Established) 
+       {
+         if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE))
+           {
+             peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+             SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+           }
+         else
+           peer->last_reset = PEER_DOWN_CLOSE_SESSION;
+       }
+
+      BGP_EVENT_ADD (peer, TCP_connection_closed);
+      return -1;
+    }
+
+  /* We read partial packet. */
+  if (stream_get_endp (peer->ibuf) != peer->packet_size)
+    return -1;
+
+  return 0;
+}
+
+/* Marker check. */
+static int
+bgp_marker_all_one (struct stream *s, int length)
+{
+  int i;
+
+  for (i = 0; i < length; i++)
+    if (s->data[i] != 0xff)
+      return 0;
+
+  return 1;
+}
+
+/* Recent thread time.
+   On same clock base as bgp_clock (MONOTONIC)
+   but can be time of last context switch to bgp_read thread. */
+static time_t
+bgp_recent_clock (void)
+{
+  return recent_relative_time().tv_sec;
+}
+
+/* Starting point of packet process function. */
+int
+bgp_read (struct thread *thread)
+{
+  int ret;
+  u_char type = 0;
+  struct peer *peer;
+  bgp_size_t size;
+  char notify_data_length[2];
+
+  /* Yes first of all get peer pointer. */
+  peer = THREAD_ARG (thread);
+  peer->t_read = NULL;
+
+  /* For non-blocking IO check. */
+  if (peer->status == Connect)
+    {
+      bgp_connect_check (peer);
+      goto done;
+    }
+  else
+    {
+      if (peer->fd < 0)
+       {
+         zlog_err ("bgp_read peer's fd is negative value %d", peer->fd);
+         return -1;
+       }
+      BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+    }
+
+  /* Read packet header to determine type of the packet */
+  if (peer->packet_size == 0)
+    peer->packet_size = BGP_HEADER_SIZE;
+
+  if (stream_get_endp (peer->ibuf) < BGP_HEADER_SIZE)
+    {
+      ret = bgp_read_packet (peer);
+
+      /* Header read error or partial read packet. */
+      if (ret < 0) 
+       goto done;
+
+      /* Get size and type. */
+      stream_forward_getp (peer->ibuf, BGP_MARKER_SIZE);
+      memcpy (notify_data_length, stream_pnt (peer->ibuf), 2);
+      size = stream_getw (peer->ibuf);
+      type = stream_getc (peer->ibuf);
+
+      if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0)
+       zlog_debug ("%s rcv message type %d, length (excl. header) %d",
+                  peer->host, type, size - BGP_HEADER_SIZE);
+
+      /* Marker check */
+      if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE))
+         && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE))
+       {
+         bgp_notify_send (peer,
+                          BGP_NOTIFY_HEADER_ERR, 
+                          BGP_NOTIFY_HEADER_NOT_SYNC);
+         goto done;
+       }
+
+      /* BGP type check. */
+      if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE 
+         && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE 
+         && type != BGP_MSG_ROUTE_REFRESH_NEW
+         && type != BGP_MSG_ROUTE_REFRESH_OLD
+         && type != BGP_MSG_CAPABILITY)
+       {
+         if (BGP_DEBUG (normal, NORMAL))
+           plog_debug (peer->log,
+                     "%s unknown message type 0x%02x",
+                     peer->host, type);
+         bgp_notify_send_with_data (peer,
+                                    BGP_NOTIFY_HEADER_ERR,
+                                    BGP_NOTIFY_HEADER_BAD_MESTYPE,
+                                    &type, 1);
+         goto done;
+       }
+      /* Mimimum packet length check. */
+      if ((size < BGP_HEADER_SIZE)
+         || (size > BGP_MAX_PACKET_SIZE)
+         || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE)
+         || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE)
+         || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE)
+         || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE)
+         || (type == BGP_MSG_ROUTE_REFRESH_NEW && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
+         || (type == BGP_MSG_ROUTE_REFRESH_OLD && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
+         || (type == BGP_MSG_CAPABILITY && size < BGP_MSG_CAPABILITY_MIN_SIZE))
+       {
+         if (BGP_DEBUG (normal, NORMAL))
+           plog_debug (peer->log,
+                     "%s bad message length - %d for %s",
+                     peer->host, size, 
+                     type == 128 ? "ROUTE-REFRESH" :
+                     bgp_type_str[(int) type]);
+         bgp_notify_send_with_data (peer,
+                                    BGP_NOTIFY_HEADER_ERR,
+                                    BGP_NOTIFY_HEADER_BAD_MESLEN,
+                                    (u_char *) notify_data_length, 2);
+         goto done;
+       }
+
+      /* Adjust size to message length. */
+      peer->packet_size = size;
+    }
+
+  ret = bgp_read_packet (peer);
+  if (ret < 0) 
+    goto done;
+
+  /* Get size and type again. */
+  size = stream_getw_from (peer->ibuf, BGP_MARKER_SIZE);
+  type = stream_getc_from (peer->ibuf, BGP_MARKER_SIZE + 2);
+
+  /* BGP packet dump function. */
+  bgp_dump_packet (peer, type, peer->ibuf);
+  
+  size = (peer->packet_size - BGP_HEADER_SIZE);
+
+  /* Read rest of the packet and call each sort of packet routine */
+  switch (type) 
+    {
+    case BGP_MSG_OPEN:
+      peer->open_in++;
+      bgp_open_receive (peer, size); /* XXX return value ignored! */
+      break;
+    case BGP_MSG_UPDATE:
+      peer->readtime = bgp_recent_clock ();
+      bgp_update_receive (peer, size);
+      break;
+    case BGP_MSG_NOTIFY:
+      bgp_notify_receive (peer, size);
+      break;
+    case BGP_MSG_KEEPALIVE:
+      peer->readtime = bgp_recent_clock ();
+      bgp_keepalive_receive (peer, size);
+      break;
+    case BGP_MSG_ROUTE_REFRESH_NEW:
+    case BGP_MSG_ROUTE_REFRESH_OLD:
+      peer->refresh_in++;
+      bgp_route_refresh_receive (peer, size);
+      break;
+    case BGP_MSG_CAPABILITY:
+      peer->dynamic_cap_in++;
+      bgp_capability_receive (peer, size);
+      break;
+    }
+
+  /* Clear input buffer. */
+  peer->packet_size = 0;
+  if (peer->ibuf)
+    stream_reset (peer->ibuf);
+
+ done:
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+    {
+      if (BGP_DEBUG (events, EVENTS))
+       zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host);
+      peer_delete (peer);
+    }
+  return 0;
+}
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
new file mode 100644 (file)
index 0000000..6b0b7f4
--- /dev/null
@@ -0,0 +1,59 @@
+/* BGP packet management header.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_PACKET_H
+#define _QUAGGA_BGP_PACKET_H
+
+#define BGP_NLRI_LENGTH       1U
+#define BGP_TOTAL_ATTR_LEN    2U
+#define BGP_UNFEASIBLE_LEN    2U
+#define BGP_WRITE_PACKET_MAX 10U
+
+/* When to refresh */
+#define REFRESH_IMMEDIATE 1
+#define REFRESH_DEFER     2 
+
+/* ORF Common part flag */
+#define ORF_COMMON_PART_ADD        0x00 
+#define ORF_COMMON_PART_REMOVE     0x80 
+#define ORF_COMMON_PART_REMOVE_ALL 0xC0 
+#define ORF_COMMON_PART_PERMIT     0x00 
+#define ORF_COMMON_PART_DENY       0x20 
+
+/* Packet send and receive function prototypes. */
+extern int bgp_read (struct thread *);
+extern int bgp_write (struct thread *);
+
+extern void bgp_keepalive_send (struct peer *);
+extern void bgp_open_send (struct peer *);
+extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t);
+extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, 
+                                u_int8_t *, size_t);
+extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int);
+extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int);
+extern void bgp_default_update_send (struct peer *, struct attr *,
+                             afi_t, safi_t, struct peer *);
+extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t);
+
+extern int bgp_capability_receive (struct peer *, bgp_size_t);
+
+extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *);
+
+#endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_regex.c b/bgpd/bgp_regex.c
new file mode 100644 (file)
index 0000000..13fa829
--- /dev/null
@@ -0,0 +1,94 @@
+/* AS regular expression routine
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd.h"
+#include "bgp_aspath.h"
+#include "bgp_regex.h"
+
+/* Character `_' has special mean.  It represents [,{}() ] and the
+   beginning of the line(^) and the end of the line ($).  
+
+   (^|[,{}() ]|$) */
+
+regex_t *
+bgp_regcomp (const char *regstr)
+{
+  /* Convert _ character to generic regular expression. */
+  int i, j;
+  int len;
+  int magic = 0;
+  char *magic_str;
+  char magic_regexp[] = "(^|[,{}() ]|$)";
+  int ret;
+  regex_t *regex;
+
+  len = strlen (regstr);
+  for (i = 0; i < len; i++)
+    if (regstr[i] == '_')
+      magic++;
+
+  magic_str = XMALLOC (MTYPE_TMP, len + (14 * magic) + 1);
+  
+  for (i = 0, j = 0; i < len; i++)
+    {
+      if (regstr[i] == '_')
+       {
+         memcpy (magic_str + j, magic_regexp, strlen (magic_regexp));
+         j += strlen (magic_regexp);
+       }
+      else
+       magic_str[j++] = regstr[i];
+    }
+  magic_str[j] = '\0';
+
+  regex = XMALLOC (MTYPE_BGP_REGEXP, sizeof (regex_t));
+
+  ret = regcomp (regex, magic_str, REG_EXTENDED|REG_NOSUB);
+
+  XFREE (MTYPE_TMP, magic_str);
+
+  if (ret != 0)
+    {
+      XFREE (MTYPE_BGP_REGEXP, regex);
+      return NULL;
+    }
+
+  return regex;
+}
+
+int
+bgp_regexec (regex_t *regex, struct aspath *aspath)
+{
+  return regexec (regex, aspath->str, 0, NULL, 0);
+}
+
+void
+bgp_regex_free (regex_t *regex)
+{
+  regfree (regex);
+  XFREE (MTYPE_BGP_REGEXP, regex);
+}
diff --git a/bgpd/bgp_regex.h b/bgpd/bgp_regex.h
new file mode 100644 (file)
index 0000000..9fc8322
--- /dev/null
@@ -0,0 +1,40 @@
+/* AS regular expression routine
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_REGEX_H
+#define _QUAGGA_BGP_REGEX_H
+
+#include <zebra.h>
+
+#ifdef HAVE_LIBPCREPOSIX
+# include <pcreposix.h>
+#else
+# ifdef HAVE_GNU_REGEX
+#  include <regex.h>
+# else
+#  include "regex-gnu.h"
+# endif /* HAVE_GNU_REGEX */
+#endif /* HAVE_LIBPCREPOSIX */
+
+extern void bgp_regex_free (regex_t *regex);
+extern regex_t *bgp_regcomp (const char *str);
+extern int bgp_regexec (regex_t *regex, struct aspath *aspath);
+
+#endif /* _QUAGGA_BGP_REGEX_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
new file mode 100644 (file)
index 0000000..13596fb
--- /dev/null
@@ -0,0 +1,17989 @@
+/* BGP routing information
+   Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+   Copyright (C) 2016 Job Snijders <job@instituut.net>
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "linklist.h"
+#include "memory.h"
+#include "command.h"
+#include "stream.h"
+#include "filter.h"
+#include "str.h"
+#include "log.h"
+#include "routemap.h"
+#include "buffer.h"
+#include "sockunion.h"
+#include "plist.h"
+#include "thread.h"
+#include "workqueue.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nht.h"
+
+/* Extern from bgp_dump.c */
+extern const char *bgp_origin_str[];
+extern const char *bgp_origin_long_str[];
+
+static struct bgp_node *
+bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p,
+                 struct prefix_rd *prd)
+{
+  struct bgp_node *rn;
+  struct bgp_node *prn = NULL;
+  
+  assert (table);
+  if (!table)
+    return NULL;
+  
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    {
+      prn = bgp_node_get (table, (struct prefix *) prd);
+
+      if (prn->info == NULL)
+       prn->info = bgp_table_init (afi, safi);
+      else
+       bgp_unlock_node (prn);
+      table = prn->info;
+    }
+
+  rn = bgp_node_get (table, p);
+
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    rn->prn = prn;
+
+  return rn;
+}
+
+/* Allocate bgp_info_extra */
+static struct bgp_info_extra *
+bgp_info_extra_new (void)
+{
+  struct bgp_info_extra *new;
+  new = XCALLOC (MTYPE_BGP_ROUTE_EXTRA, sizeof (struct bgp_info_extra));
+  return new;
+}
+
+static void
+bgp_info_extra_free (struct bgp_info_extra **extra)
+{
+  if (extra && *extra)
+    {
+      if ((*extra)->damp_info)
+        bgp_damp_info_free ((*extra)->damp_info, 0);
+      
+      (*extra)->damp_info = NULL;
+      
+      XFREE (MTYPE_BGP_ROUTE_EXTRA, *extra);
+      
+      *extra = NULL;
+    }
+}
+
+/* Get bgp_info extra information for the given bgp_info, lazy allocated
+ * if required.
+ */
+struct bgp_info_extra *
+bgp_info_extra_get (struct bgp_info *ri)
+{
+  if (!ri->extra)
+    ri->extra = bgp_info_extra_new();
+  return ri->extra;
+}
+
+/* Free bgp route information. */
+static void
+bgp_info_free (struct bgp_info *binfo)
+{
+  if (binfo->attr)
+    bgp_attr_unintern (&binfo->attr);
+
+  bgp_unlink_nexthop (binfo);
+  bgp_info_extra_free (&binfo->extra);
+  bgp_info_mpath_free (&binfo->mpath);
+
+  peer_unlock (binfo->peer); /* bgp_info peer reference */
+
+  XFREE (MTYPE_BGP_ROUTE, binfo);
+}
+
+struct bgp_info *
+bgp_info_lock (struct bgp_info *binfo)
+{
+  binfo->lock++;
+  return binfo;
+}
+
+struct bgp_info *
+bgp_info_unlock (struct bgp_info *binfo)
+{
+  assert (binfo && binfo->lock > 0);
+  binfo->lock--;
+  
+  if (binfo->lock == 0)
+    {
+#if 0
+      zlog_debug ("%s: unlocked and freeing", __func__);
+      zlog_backtrace (LOG_DEBUG);
+#endif
+      bgp_info_free (binfo);
+      return NULL;
+    }
+
+#if 0
+  if (binfo->lock == 1)
+    {
+      zlog_debug ("%s: unlocked to 1", __func__);
+      zlog_backtrace (LOG_DEBUG);
+    }
+#endif
+  
+  return binfo;
+}
+
+void
+bgp_info_add (struct bgp_node *rn, struct bgp_info *ri)
+{
+  struct bgp_info *top;
+
+  top = rn->info;
+  
+  ri->next = rn->info;
+  ri->prev = NULL;
+  if (top)
+    top->prev = ri;
+  rn->info = ri;
+  
+  bgp_info_lock (ri);
+  bgp_lock_node (rn);
+  peer_lock (ri->peer); /* bgp_info peer reference */
+}
+
+/* Do the actual removal of info from RIB, for use by bgp_process 
+   completion callback *only* */
+static void
+bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri)
+{
+  if (ri->next)
+    ri->next->prev = ri->prev;
+  if (ri->prev)
+    ri->prev->next = ri->next;
+  else
+    rn->info = ri->next;
+  
+  bgp_info_mpath_dequeue (ri);
+  bgp_info_unlock (ri);
+  bgp_unlock_node (rn);
+}
+
+void
+bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri)
+{
+  bgp_info_set_flag (rn, ri, BGP_INFO_REMOVED);
+  /* set of previous already took care of pcount */
+  UNSET_FLAG (ri->flags, BGP_INFO_VALID);
+}
+
+/* undo the effects of a previous call to bgp_info_delete; typically
+   called when a route is deleted and then quickly re-added before the
+   deletion has been processed */
+static void
+bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri)
+{
+  bgp_info_unset_flag (rn, ri, BGP_INFO_REMOVED);
+  /* unset of previous already took care of pcount */
+  SET_FLAG (ri->flags, BGP_INFO_VALID);
+}
+
+/* Adjust pcount as required */   
+static void
+bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri)
+{
+  struct bgp_table *table;
+
+  assert (rn && bgp_node_table (rn));
+  assert (ri && ri->peer && ri->peer->bgp);
+
+  table = bgp_node_table (rn);
+
+  /* Ignore 'pcount' for RS-client tables */
+  if (table->type != BGP_TABLE_MAIN
+      || ri->peer == ri->peer->bgp->peer_self)
+    return;
+    
+  if (!BGP_INFO_COUNTABLE (ri)
+      && CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
+    {
+          
+      UNSET_FLAG (ri->flags, BGP_INFO_COUNTED);
+      
+      /* slight hack, but more robust against errors. */
+      if (ri->peer->pcount[table->afi][table->safi])
+        ri->peer->pcount[table->afi][table->safi]--;
+      else
+        {
+          zlog_warn ("%s: Asked to decrement 0 prefix count for peer %s",
+                     __func__, ri->peer->host);
+          zlog_backtrace (LOG_WARNING);
+          zlog_warn ("%s: Please report to Quagga bugzilla", __func__);
+        }      
+    }
+  else if (BGP_INFO_COUNTABLE (ri)
+           && !CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
+    {
+      SET_FLAG (ri->flags, BGP_INFO_COUNTED);
+      ri->peer->pcount[table->afi][table->safi]++;
+    }
+}
+
+
+/* Set/unset bgp_info flags, adjusting any other state as needed.
+ * This is here primarily to keep prefix-count in check.
+ */
+void
+bgp_info_set_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag)
+{
+  SET_FLAG (ri->flags, flag);
+  
+  /* early bath if we know it's not a flag that changes countability state */
+  if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED))
+    return;
+  
+  bgp_pcount_adjust (rn, ri);
+}
+
+void
+bgp_info_unset_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag)
+{
+  UNSET_FLAG (ri->flags, flag);
+  
+  /* early bath if we know it's not a flag that changes countability state */
+  if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED))
+    return;
+  
+  bgp_pcount_adjust (rn, ri);
+}
+
+/* Get MED value.  If MED value is missing and "bgp bestpath
+   missing-as-worst" is specified, treat it as the worst value. */
+static u_int32_t
+bgp_med_value (struct attr *attr, struct bgp *bgp)
+{
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+    return attr->med;
+  else
+    {
+      if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
+       return BGP_MED_MAX;
+      else
+       return 0;
+    }
+}
+
+/* Compare two bgp route entity.  Return -1 if new is preferred, 1 if exist
+ * is preferred, or 0 if they are the same (usually will only occur if
+ * multipath is enabled */
+static int
+bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
+              afi_t afi, safi_t safi)
+{
+  struct attr *newattr, *existattr;
+  struct attr_extra *newattre, *existattre;
+  bgp_peer_sort_t new_sort;
+  bgp_peer_sort_t exist_sort;
+  u_int32_t new_pref;
+  u_int32_t exist_pref;
+  u_int32_t new_med;
+  u_int32_t exist_med;
+  u_int32_t new_weight;
+  u_int32_t exist_weight;
+  uint32_t newm, existm;
+  struct in_addr new_id;
+  struct in_addr exist_id;
+  int new_cluster;
+  int exist_cluster;
+  int internal_as_route;
+  int confed_as_route;
+  int ret;
+
+  /* 0. Null check. */
+  if (new == NULL)
+    return 1;
+  if (exist == NULL)
+    return -1;
+
+  newattr = new->attr;
+  existattr = exist->attr;
+  newattre = newattr->extra;
+  existattre = existattr->extra;
+
+  /* 1. Weight check. */
+  new_weight = exist_weight = 0;
+
+  if (newattre)
+    new_weight = newattre->weight;
+  if (existattre)
+    exist_weight = existattre->weight;
+
+  if (new_weight > exist_weight)
+    return -1;
+  if (new_weight < exist_weight)
+    return 1;
+
+  /* 2. Local preference check. */
+  new_pref = exist_pref = bgp->default_local_pref;
+
+  if (newattr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+    new_pref = newattr->local_pref;
+  if (existattr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+    exist_pref = existattr->local_pref;
+
+  if (new_pref > exist_pref)
+    return -1;
+  if (new_pref < exist_pref)
+    return 1;
+
+  /* 3. Local route check. We prefer:
+   *  - BGP_ROUTE_STATIC
+   *  - BGP_ROUTE_AGGREGATE
+   *  - BGP_ROUTE_REDISTRIBUTE
+   */
+  if (! (new->sub_type == BGP_ROUTE_NORMAL))
+     return -1;
+  if (! (exist->sub_type == BGP_ROUTE_NORMAL))
+     return 1;
+
+  /* 4. AS path length check. */
+  if (! bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
+    {
+      int exist_hops = aspath_count_hops (existattr->aspath);
+      int exist_confeds = aspath_count_confeds (existattr->aspath);
+      
+      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
+       {
+         int aspath_hops;
+         
+         aspath_hops = aspath_count_hops (newattr->aspath);
+          aspath_hops += aspath_count_confeds (newattr->aspath);
+          
+         if ( aspath_hops < (exist_hops + exist_confeds))
+           return -1;
+         if ( aspath_hops > (exist_hops + exist_confeds))
+           return 1;
+       }
+      else
+       {
+         int newhops = aspath_count_hops (newattr->aspath);
+         
+         if (newhops < exist_hops)
+           return -1;
+          if (newhops > exist_hops)
+           return 1;
+       }
+    }
+
+  /* 5. Origin check. */
+  if (newattr->origin < existattr->origin)
+    return -1;
+  if (newattr->origin > existattr->origin)
+    return 1;
+
+  /* 6. MED check. */
+  internal_as_route = (aspath_count_hops (newattr->aspath) == 0
+                     && aspath_count_hops (existattr->aspath) == 0);
+  confed_as_route = (aspath_count_confeds (newattr->aspath) > 0
+                   && aspath_count_confeds (existattr->aspath) > 0
+                   && aspath_count_hops (newattr->aspath) == 0
+                   && aspath_count_hops (existattr->aspath) == 0);
+  
+  if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED)
+      || (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
+        && confed_as_route)
+      || aspath_cmp_left (newattr->aspath, existattr->aspath)
+      || aspath_cmp_left_confed (newattr->aspath, existattr->aspath)
+      || internal_as_route)
+    {
+      new_med = bgp_med_value (new->attr, bgp);
+      exist_med = bgp_med_value (exist->attr, bgp);
+
+      if (new_med < exist_med)
+       return -1;
+      if (new_med > exist_med)
+       return 1;
+    }
+
+  /* 7. Peer type check. */
+  new_sort = new->peer->sort;
+  exist_sort = exist->peer->sort;
+
+  if (new_sort == BGP_PEER_EBGP
+      && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED))
+    return -1;
+  if (exist_sort == BGP_PEER_EBGP
+      && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED))
+    return 1;
+
+  /* 8. IGP metric check. */
+  newm = existm = 0;
+
+  if (new->extra)
+    newm = new->extra->igpmetric;
+  if (exist->extra)
+    existm = exist->extra->igpmetric;
+
+  if (newm < existm)
+    return -1;
+  if (newm > existm)
+    return 1;
+
+  /* 9. Maximum path check. */
+  if (bgp_mpath_is_configured (bgp, afi, safi))
+    {
+      if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX))
+        {
+          /*
+           * For the two paths, all comparison steps till IGP metric
+           * have succeeded - including AS_PATH hop count. Since 'bgp
+           * bestpath as-path multipath-relax' knob is on, we don't need
+           * an exact match of AS_PATH. Thus, mark the paths are equal.
+           * That will trigger both these paths to get into the multipath
+           * array.
+           */
+          return 0;
+        }
+      else if (new->peer->sort == BGP_PEER_IBGP)
+        {
+          if (aspath_cmp (new->attr->aspath, exist->attr->aspath))
+            return 0;
+        }
+      else if (new->peer->as == exist->peer->as)
+        return 0;
+    }
+
+  /* 10. If both paths are external, prefer the path that was received
+     first (the oldest one).  This step minimizes route-flap, since a
+     newer path won't displace an older one, even if it was the
+     preferred route based on the additional decision criteria below.  */
+  if (! bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID)
+      && new_sort == BGP_PEER_EBGP
+      && exist_sort == BGP_PEER_EBGP)
+    {
+      if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED))
+       return -1;
+      if (CHECK_FLAG (exist->flags, BGP_INFO_SELECTED))
+       return 1;
+    }
+
+  /* 11. Router-ID comparision. */
+  /* If one of the paths is "stale", the corresponding peer router-id will
+   * be 0 and would always win over the other path. If originator id is
+   * used for the comparision, it will decide which path is better.
+   */
+  if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+    new_id.s_addr = newattre->originator_id.s_addr;
+  else
+    new_id.s_addr = new->peer->remote_id.s_addr;
+  if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+    exist_id.s_addr = existattre->originator_id.s_addr;
+  else
+    exist_id.s_addr = exist->peer->remote_id.s_addr;
+
+  if (ntohl (new_id.s_addr) < ntohl (exist_id.s_addr))
+    return -1;
+  if (ntohl (new_id.s_addr) > ntohl (exist_id.s_addr))
+    return 1;
+
+  /* 12. Cluster length comparision. */
+  new_cluster = exist_cluster = 0;
+
+  if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))
+    new_cluster = newattre->cluster->length;
+  if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))
+    exist_cluster = existattre->cluster->length;
+
+  if (new_cluster < exist_cluster)
+    return -1;
+  if (new_cluster > exist_cluster)
+    return 1;
+
+  /* 13. Neighbor address comparision. */
+  /* Do this only if neither path is "stale" as stale paths do not have
+   * valid peer information (as the connection may or may not be up).
+   */
+  if (CHECK_FLAG (exist->flags, BGP_INFO_STALE))
+    return -1;
+  if (CHECK_FLAG (new->flags, BGP_INFO_STALE))
+    return 1;
+  /* locally configured routes to advertise do not have su_remote */
+  if (new->peer->su_remote == NULL)
+    return 1;
+  if (exist->peer->su_remote == NULL)
+    return -1;
+  
+  ret = sockunion_cmp (new->peer->su_remote, exist->peer->su_remote);
+
+  if (ret == 1)
+    return 1;
+  if (ret == -1)
+    return -1;
+
+  return -1;
+}
+
+static enum filter_type
+bgp_input_filter (struct peer *peer, struct prefix *p, struct attr *attr,
+                 afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+
+  filter = &peer->filter[afi][safi];
+
+#define FILTER_EXIST_WARN(F,f,filter) \
+  if (BGP_DEBUG (update, UPDATE_IN) \
+      && !(F ## _IN (filter))) \
+    plog_warn (peer->log, "%s: Could not find configured input %s-list %s!", \
+               peer->host, #f, F ## _IN_NAME(filter));
+  
+  if (DISTRIBUTE_IN_NAME (filter)) {
+    FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
+      
+    if (access_list_apply (DISTRIBUTE_IN (filter), p) == FILTER_DENY)
+      return FILTER_DENY;
+  }
+
+  if (PREFIX_LIST_IN_NAME (filter)) {
+    FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
+    
+    if (prefix_list_apply (PREFIX_LIST_IN (filter), p) == PREFIX_DENY)
+      return FILTER_DENY;
+  }
+  
+  if (FILTER_LIST_IN_NAME (filter)) {
+    FILTER_EXIST_WARN(FILTER_LIST, as, filter);
+    
+    if (as_list_apply (FILTER_LIST_IN (filter), attr->aspath)== AS_FILTER_DENY)
+      return FILTER_DENY;
+  }
+  
+  return FILTER_PERMIT;
+#undef FILTER_EXIST_WARN
+}
+
+static enum filter_type
+bgp_output_filter (struct peer *peer, struct prefix *p, struct attr *attr,
+                  afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+
+  filter = &peer->filter[afi][safi];
+
+#define FILTER_EXIST_WARN(F,f,filter) \
+  if (BGP_DEBUG (update, UPDATE_OUT) \
+      && !(F ## _OUT (filter))) \
+    plog_warn (peer->log, "%s: Could not find configured output %s-list %s!", \
+               peer->host, #f, F ## _OUT_NAME(filter));
+
+  if (DISTRIBUTE_OUT_NAME (filter)) {
+    FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
+    
+    if (access_list_apply (DISTRIBUTE_OUT (filter), p) == FILTER_DENY)
+      return FILTER_DENY;
+  }
+
+  if (PREFIX_LIST_OUT_NAME (filter)) {
+    FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
+    
+    if (prefix_list_apply (PREFIX_LIST_OUT (filter), p) == PREFIX_DENY)
+      return FILTER_DENY;
+  }
+
+  if (FILTER_LIST_OUT_NAME (filter)) {
+    FILTER_EXIST_WARN(FILTER_LIST, as, filter);
+    
+    if (as_list_apply (FILTER_LIST_OUT (filter), attr->aspath) == AS_FILTER_DENY)
+      return FILTER_DENY;
+  }
+
+  return FILTER_PERMIT;
+#undef FILTER_EXIST_WARN
+}
+
+/* If community attribute includes no_export then return 1. */
+static int
+bgp_community_filter (struct peer *peer, struct attr *attr)
+{
+  if (attr->community)
+    {
+      /* NO_ADVERTISE check. */
+      if (community_include (attr->community, COMMUNITY_NO_ADVERTISE))
+       return 1;
+
+      /* NO_EXPORT check. */
+      if (peer->sort == BGP_PEER_EBGP &&
+         community_include (attr->community, COMMUNITY_NO_EXPORT))
+       return 1;
+
+      /* NO_EXPORT_SUBCONFED check. */
+      if (peer->sort == BGP_PEER_EBGP
+         || peer->sort == BGP_PEER_CONFED)
+       if (community_include (attr->community, COMMUNITY_NO_EXPORT_SUBCONFED))
+         return 1;
+    }
+  return 0;
+}
+
+/* Route reflection loop check.  */
+static int
+bgp_cluster_filter (struct peer *peer, struct attr *attr)
+{
+  struct in_addr cluster_id;
+
+  if (attr->extra && attr->extra->cluster)
+    {
+      if (peer->bgp->config & BGP_CONFIG_CLUSTER_ID)
+       cluster_id = peer->bgp->cluster_id;
+      else
+       cluster_id = peer->bgp->router_id;
+      
+      if (cluster_loop_check (attr->extra->cluster, cluster_id))
+       return 1;
+    }
+  return 0;
+}
+
+static int
+bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr,
+                   afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  struct bgp_info info;
+  route_map_result_t ret;
+
+  filter = &peer->filter[afi][safi];
+
+  /* Apply default weight value. */
+  if (peer->weight)
+    (bgp_attr_extra_get (attr))->weight = peer->weight;
+
+  /* Route map apply. */
+  if (ROUTE_MAP_IN_NAME (filter))
+    {
+      /* Duplicate current value to new strucutre for modification. */
+      info.peer = peer;
+      info.attr = attr;
+
+      SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN); 
+
+      /* Apply BGP route map to the attribute. */
+      ret = route_map_apply (ROUTE_MAP_IN (filter), p, RMAP_BGP, &info);
+
+      peer->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+       /* caller has multiple error paths with bgp_attr_flush() */
+       return RMAP_DENY;
+    }
+  return RMAP_PERMIT;
+}
+
+static int
+bgp_export_modifier (struct peer *rsclient, struct peer *peer,
+        struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  struct bgp_info info;
+  route_map_result_t ret;
+
+  filter = &peer->filter[afi][safi];
+
+  /* Route map apply. */
+  if (ROUTE_MAP_EXPORT_NAME (filter))
+    {
+      /* Duplicate current value to new strucutre for modification. */
+      info.peer = rsclient;
+      info.attr = attr;
+
+      SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT);
+
+      /* Apply BGP route map to the attribute. */
+      ret = route_map_apply (ROUTE_MAP_EXPORT (filter), p, RMAP_BGP, &info);
+
+      rsclient->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          /* Free newly generated AS path and community by route-map. */
+          bgp_attr_flush (attr);
+          return RMAP_DENY;
+        }
+    }
+  return RMAP_PERMIT;
+}
+
+static int
+bgp_import_modifier (struct peer *rsclient, struct peer *peer,
+        struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  struct bgp_info info;
+  route_map_result_t ret;
+
+  filter = &rsclient->filter[afi][safi];
+
+  /* Apply default weight value. */
+  if (peer->weight)
+    (bgp_attr_extra_get (attr))->weight = peer->weight;
+
+  /* Route map apply. */
+  if (ROUTE_MAP_IMPORT_NAME (filter))
+    {
+      /* Duplicate current value to new strucutre for modification. */
+      info.peer = peer;
+      info.attr = attr;
+
+      SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT);
+
+      /* Apply BGP route map to the attribute. */
+      ret = route_map_apply (ROUTE_MAP_IMPORT (filter), p, RMAP_BGP, &info);
+
+      peer->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          /* Free newly generated AS path and community by route-map. */
+          bgp_attr_flush (attr);
+          return RMAP_DENY;
+        }
+    }
+  return RMAP_PERMIT;
+}
+
+static int
+bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p,
+                   struct attr *attr, afi_t afi, safi_t safi)
+{
+  int ret;
+  char buf[SU_ADDRSTRLEN];
+  struct bgp_filter *filter;
+  struct peer *from;
+  struct bgp *bgp;
+  int transparent;
+  int reflect;
+  struct attr *riattr;
+
+  from = ri->peer;
+  filter = &peer->filter[afi][safi];
+  bgp = peer->bgp;
+  riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr;
+  
+  if (DISABLE_BGP_ANNOUNCE)
+    return 0;
+
+  /* Do not send announces to RS-clients from the 'normal' bgp_table. */
+  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    return 0;
+
+  /* Do not send back route to sender. */
+  if (from == peer)
+    return 0;
+
+  /* Aggregate-address suppress check. */
+  if (ri->extra && ri->extra->suppress)
+    if (! UNSUPPRESS_MAP_NAME (filter))
+      return 0;
+
+  /* Default route check.  */
+  if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE))
+    {
+      if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY)
+       return 0;
+      else if (p->family == AF_INET6 && p->prefixlen == 0)
+       return 0;
+    }
+
+  /* Transparency check. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+      && CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    transparent = 1;
+  else
+    transparent = 0;
+
+  /* If community is not disabled check the no-export and local. */
+  if (! transparent && bgp_community_filter (peer, riattr))
+    return 0;
+
+  /* If the attribute has originator-id and it is same as remote
+     peer's id. */
+  if (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID))
+    {
+      if (IPV4_ADDR_SAME (&peer->remote_id, &riattr->extra->originator_id))
+       {
+         if (BGP_DEBUG (filter, FILTER))  
+           zlog (peer->log, LOG_DEBUG,
+                 "%s [Update:SEND] %s/%d originator-id is same as remote router-id",
+                 peer->host,
+                 inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                 p->prefixlen);
+         return 0;
+       }
+    }
+  /* ORF prefix-list filter check */
+  if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
+      && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+         || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)))
+    if (peer->orf_plist[afi][safi])
+      {
+       if (prefix_list_apply (peer->orf_plist[afi][safi], p) == PREFIX_DENY)
+          return 0;
+      }
+
+  /* Output filter check. */
+  if (bgp_output_filter (peer, p, riattr, afi, safi) == FILTER_DENY)
+    {
+      if (BGP_DEBUG (filter, FILTER))
+       zlog (peer->log, LOG_DEBUG,
+             "%s [Update:SEND] %s/%d is filtered",
+             peer->host,
+             inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+             p->prefixlen);
+      return 0;
+    }
+
+#ifdef BGP_SEND_ASPATH_CHECK
+  /* AS path loop check. */
+  if (aspath_loop_check (riattr->aspath, peer->as))
+    {
+      if (BGP_DEBUG (filter, FILTER))  
+        zlog (peer->log, LOG_DEBUG, 
+             "%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
+             peer->host, peer->as);
+      return 0;
+    }
+
+  /* If we're a CONFED we need to loop check the CONFED ID too */
+  if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+    {
+      if (aspath_loop_check(riattr->aspath, bgp->confed_id))
+       {
+         if (BGP_DEBUG (filter, FILTER))  
+           zlog (peer->log, LOG_DEBUG, 
+                 "%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
+                 peer->host,
+                 bgp->confed_id);
+         return 0;
+       }      
+    }
+#endif /* BGP_SEND_ASPATH_CHECK */
+
+  /* Route-Reflect check. */
+  if (from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
+    reflect = 1;
+  else
+    reflect = 0;
+
+  /* IBGP reflection check. */
+  if (reflect)
+    {
+      /* A route from a Client peer. */
+      if (CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+       {
+         /* Reflect to all the Non-Client peers and also to the
+             Client peers other than the originator.  Originator check
+             is already done.  So there is noting to do. */
+         /* no bgp client-to-client reflection check. */
+         if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
+           if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+             return 0;
+       }
+      else
+       {
+         /* A route from a Non-client peer. Reflect to all other
+            clients. */
+         if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+           return 0;
+       }
+    }
+  
+  /* For modify attribute, copy it to temporary structure. */
+  bgp_attr_dup (attr, riattr);
+  
+  /* If local-preference is not set. */
+  if ((peer->sort == BGP_PEER_IBGP
+       || peer->sort == BGP_PEER_CONFED)
+      && (! (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))))
+    {
+      attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+      attr->local_pref = bgp->default_local_pref;
+    }
+
+  /* If originator-id is not set and the route is to be reflected,
+     set the originator id */
+  if (peer && from && peer->sort == BGP_PEER_IBGP &&
+      from->sort == BGP_PEER_IBGP &&
+      (! (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))))
+    {
+      attr->extra = bgp_attr_extra_get(attr);
+      IPV4_ADDR_COPY(&(attr->extra->originator_id), &(from->remote_id));
+      SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID);
+    }
+
+  /* Remove MED if its an EBGP peer - will get overwritten by route-maps */
+  if (peer->sort == BGP_PEER_EBGP
+      && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+    {
+      if (ri->peer != bgp->peer_self && ! transparent
+         && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+       attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC));
+    }
+
+
+#define NEXTHOP_IS_V4 (\
+    (safi != SAFI_ENCAP && p->family == AF_INET) || \
+    (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 4))
+
+#define NEXTHOP_IS_V6 (\
+    (safi != SAFI_ENCAP && p->family == AF_INET6) || \
+    (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 16))
+
+  /* next-hop-set */
+  if (transparent
+      || (reflect && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF_ALL))
+      || (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
+         && ((NEXTHOP_IS_V4 && attr->nexthop.s_addr)
+             || (NEXTHOP_IS_V6 &&
+                  ! IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
+             )))
+    {
+      /* NEXT-HOP Unchanged. */
+    }
+  else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)
+          || (NEXTHOP_IS_V4 && attr->nexthop.s_addr == 0)
+          || (NEXTHOP_IS_V6 &&
+               IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
+          || (peer->sort == BGP_PEER_EBGP
+               && (bgp_multiaccess_check_v4 (attr->nexthop, peer) == 0)))
+    {
+      /* Set IPv4 nexthop. */
+      if (NEXTHOP_IS_V4)
+       {
+         if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+           memcpy (&attr->extra->mp_nexthop_global_in, &peer->nexthop.v4,
+                   IPV4_MAX_BYTELEN);
+         else
+           memcpy (&attr->nexthop, &peer->nexthop.v4, IPV4_MAX_BYTELEN);
+       }
+      /* Set IPv6 nexthop. */
+      if (NEXTHOP_IS_V6)
+       {
+         /* IPv6 global nexthop must be included. */
+         memcpy (&attr->extra->mp_nexthop_global, &peer->nexthop.v6_global, 
+                 IPV6_MAX_BYTELEN);
+         attr->extra->mp_nexthop_len = 16;
+       }
+    }
+
+  if (p->family == AF_INET6 && safi != SAFI_ENCAP)
+    {
+      /* Left nexthop_local unchanged if so configured. */ 
+      if ( CHECK_FLAG (peer->af_flags[afi][safi], 
+           PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) )
+        {
+          if ( IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_local) )
+            attr->extra->mp_nexthop_len=32;
+          else
+            attr->extra->mp_nexthop_len=16;
+        }
+
+      /* Default nexthop_local treatment for non-RS-Clients */
+      else 
+        {
+      /* Link-local address should not be transit to different peer. */
+      attr->extra->mp_nexthop_len = 16;
+
+      /* Set link-local address for shared network peer. */
+      if (peer->shared_network 
+         && ! IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local))
+       {
+         memcpy (&attr->extra->mp_nexthop_local, &peer->nexthop.v6_local, 
+                 IPV6_MAX_BYTELEN);
+         attr->extra->mp_nexthop_len = 32;
+       }
+
+      /* If bgpd act as BGP-4+ route-reflector, do not send link-local
+        address.*/
+      if (reflect)
+       attr->extra->mp_nexthop_len = 16;
+
+      /* If BGP-4+ link-local nexthop is not link-local nexthop. */
+      if (! IN6_IS_ADDR_LINKLOCAL (&peer->nexthop.v6_local))
+       attr->extra->mp_nexthop_len = 16;
+    }
+
+    }
+
+  /* If this is EBGP peer and remove-private-AS is set.  */
+  if (peer->sort == BGP_PEER_EBGP
+      && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
+      && aspath_private_as_check (attr->aspath))
+    attr->aspath = aspath_empty_get ();
+
+  /* Route map & unsuppress-map apply. */
+  if (ROUTE_MAP_OUT_NAME (filter)
+      || (ri->extra && ri->extra->suppress) )
+    {
+      struct bgp_info info;
+      struct attr dummy_attr;
+      struct attr_extra dummy_extra;
+
+      dummy_attr.extra = &dummy_extra;
+
+      info.peer = peer;
+      info.attr = attr;
+
+      /* The route reflector is not allowed to modify the attributes
+        of the reflected IBGP routes, unless configured to allow it */
+      if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) &&
+         !bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY))
+       {
+         bgp_attr_dup (&dummy_attr, attr);
+         info.attr = &dummy_attr;
+       }
+
+      SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT); 
+
+      if (ri->extra && ri->extra->suppress)
+       ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info);
+      else
+       ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info);
+
+      peer->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+       {
+         bgp_attr_flush (attr);
+         return 0;
+       }
+    }
+  return 1;
+}
+
+static int
+bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient,
+        struct prefix *p, struct attr *attr, afi_t afi, safi_t safi)
+{
+  int ret;
+  char buf[SU_ADDRSTRLEN];
+  struct bgp_filter *filter;
+  struct bgp_info info;
+  struct peer *from;
+  struct attr *riattr;
+
+  from = ri->peer;
+  filter = &rsclient->filter[afi][safi];
+  riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr;
+
+  if (DISABLE_BGP_ANNOUNCE)
+    return 0;
+
+  /* Do not send back route to sender. */
+  if (from == rsclient)
+    return 0;
+
+  /* Aggregate-address suppress check. */
+  if (ri->extra && ri->extra->suppress)
+    if (! UNSUPPRESS_MAP_NAME (filter))
+      return 0;
+
+  /* Default route check.  */
+  if (CHECK_FLAG (rsclient->af_sflags[afi][safi],
+          PEER_STATUS_DEFAULT_ORIGINATE))
+    {
+      if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY)
+        return 0;
+      else if (p->family == AF_INET6 && p->prefixlen == 0)
+        return 0;
+    }
+
+  /* If the attribute has originator-id and it is same as remote
+     peer's id. */
+  if (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID))
+    {
+      if (IPV4_ADDR_SAME (&rsclient->remote_id, 
+                          &riattr->extra->originator_id))
+        {
+         if (BGP_DEBUG (filter, FILTER))
+           zlog (rsclient->log, LOG_DEBUG,
+                 "%s [Update:SEND] %s/%d originator-id is same as remote router-id",
+                 rsclient->host,
+                 inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                 p->prefixlen);
+         return 0;
+       }
+    }
+
+  /* ORF prefix-list filter check */
+  if (CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
+      && (CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+         || CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)))
+    if (rsclient->orf_plist[afi][safi])
+      {
+       if (prefix_list_apply (rsclient->orf_plist[afi][safi], p) == PREFIX_DENY)
+          return 0;
+      }
+
+  /* Output filter check. */
+  if (bgp_output_filter (rsclient, p, riattr, afi, safi) == FILTER_DENY)
+    {
+      if (BGP_DEBUG (filter, FILTER))
+       zlog (rsclient->log, LOG_DEBUG,
+             "%s [Update:SEND] %s/%d is filtered",
+             rsclient->host,
+             inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+             p->prefixlen);
+      return 0;
+    }
+
+#ifdef BGP_SEND_ASPATH_CHECK
+  /* AS path loop check. */
+  if (aspath_loop_check (riattr->aspath, rsclient->as))
+    {
+      if (BGP_DEBUG (filter, FILTER))
+        zlog (rsclient->log, LOG_DEBUG,
+             "%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
+             rsclient->host, rsclient->as);
+      return 0;
+    }
+#endif /* BGP_SEND_ASPATH_CHECK */
+
+  /* For modify attribute, copy it to temporary structure. */
+  bgp_attr_dup (attr, riattr);
+
+  /* next-hop-set */
+  if ((p->family == AF_INET && attr->nexthop.s_addr == 0)
+          || (p->family == AF_INET6 &&
+              IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
+     )
+  {
+    /* Set IPv4 nexthop. */
+    if (p->family == AF_INET)
+      {
+        if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+          memcpy (&attr->extra->mp_nexthop_global_in, &rsclient->nexthop.v4,
+                  IPV4_MAX_BYTELEN);
+        else
+          memcpy (&attr->nexthop, &rsclient->nexthop.v4, IPV4_MAX_BYTELEN);
+      }
+    /* Set IPv6 nexthop. */
+    if (p->family == AF_INET6)
+      {
+        /* IPv6 global nexthop must be included. */
+        memcpy (&attr->extra->mp_nexthop_global, &rsclient->nexthop.v6_global,
+                IPV6_MAX_BYTELEN);
+        attr->extra->mp_nexthop_len = 16;
+      }
+  }
+
+  if (p->family == AF_INET6)
+    {
+      struct attr_extra *attre = attr->extra;
+
+      /* Left nexthop_local unchanged if so configured. */
+      if ( CHECK_FLAG (rsclient->af_flags[afi][safi], 
+           PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) )
+        {
+          if ( IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local) )
+            attre->mp_nexthop_len=32;
+          else
+            attre->mp_nexthop_len=16;
+        }
+        
+      /* Default nexthop_local treatment for RS-Clients */
+      else 
+        { 
+          /* Announcer and RS-Client are both in the same network */      
+          if (rsclient->shared_network && from->shared_network &&
+              (rsclient->ifindex == from->ifindex))
+            {
+              if ( IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local) )
+                attre->mp_nexthop_len=32;
+              else
+                attre->mp_nexthop_len=16;
+            }
+
+          /* Set link-local address for shared network peer. */
+          else if (rsclient->shared_network
+              && IN6_IS_ADDR_LINKLOCAL (&rsclient->nexthop.v6_local))
+            {
+              memcpy (&attre->mp_nexthop_local, &rsclient->nexthop.v6_local,
+                      IPV6_MAX_BYTELEN);
+              attre->mp_nexthop_len = 32;
+            }
+
+          else
+            attre->mp_nexthop_len = 16;
+        }
+
+    }
+
+  /* If this is EBGP peer and remove-private-AS is set.  */
+  if (rsclient->sort == BGP_PEER_EBGP
+      && peer_af_flag_check (rsclient, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
+      && aspath_private_as_check (attr->aspath))
+    attr->aspath = aspath_empty_get ();
+
+  /* Route map & unsuppress-map apply. */
+  if (ROUTE_MAP_OUT_NAME (filter) || (ri->extra && ri->extra->suppress) )
+    {
+      info.peer = rsclient;
+      info.attr = attr;
+
+      SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_OUT);
+
+      if (ri->extra && ri->extra->suppress)
+        ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info);
+      else
+        ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info);
+
+      rsclient->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+       {
+         bgp_attr_flush (attr);
+         return 0;
+       }
+    }
+
+  return 1;
+}
+
+struct bgp_info_pair
+{
+  struct bgp_info *old;
+  struct bgp_info *new;
+};
+
+static void
+bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
+                   struct bgp_info_pair *result,
+                   afi_t afi, safi_t safi)
+{
+  struct bgp_info *new_select;
+  struct bgp_info *old_select;
+  struct bgp_info *ri;
+  struct bgp_info *ri1;
+  struct bgp_info *ri2;
+  struct bgp_info *nextri = NULL;
+  int cmpret, do_mpath;
+  struct list mp_list;
+    
+  result->old = result->new = NULL;
+  
+  if (rn->info == NULL)
+    {
+      char buf[PREFIX_STRLEN];
+      zlog_warn ("%s: Called for route_node %s with no routing entries!",
+                 __func__,
+                 prefix2str (&(bgp_node_to_rnode (rn)->p), buf, sizeof(buf)));
+      return;
+    }
+  
+  bgp_mp_list_init (&mp_list);
+  do_mpath = bgp_mpath_is_configured (bgp, afi, safi);
+
+  /* bgp deterministic-med */
+  new_select = NULL;
+  if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
+    for (ri1 = rn->info; ri1; ri1 = ri1->next)
+      {
+       if (CHECK_FLAG (ri1->flags, BGP_INFO_DMED_CHECK))
+         continue;
+       if (BGP_INFO_HOLDDOWN (ri1))
+         continue;
+        if (ri1->peer && ri1->peer != bgp->peer_self)
+          if (ri1->peer->status != Established)
+            continue;
+
+       new_select = ri1;
+       if (do_mpath)
+         bgp_mp_list_add (&mp_list, ri1);
+       old_select = CHECK_FLAG (ri1->flags, BGP_INFO_SELECTED) ? ri1 : NULL;
+       if (ri1->next)
+         for (ri2 = ri1->next; ri2; ri2 = ri2->next)
+           {
+             if (CHECK_FLAG (ri2->flags, BGP_INFO_DMED_CHECK))
+               continue;
+             if (BGP_INFO_HOLDDOWN (ri2))
+               continue;
+              if (ri2->peer &&
+                  ri2->peer != bgp->peer_self &&
+                  !CHECK_FLAG (ri2->peer->sflags, PEER_STATUS_NSF_WAIT))
+                if (ri2->peer->status != Established)
+                  continue;
+
+             if (aspath_cmp_left (ri1->attr->aspath, ri2->attr->aspath)
+                 || aspath_cmp_left_confed (ri1->attr->aspath,
+                                            ri2->attr->aspath))
+               {
+                 if (CHECK_FLAG (ri2->flags, BGP_INFO_SELECTED))
+                   old_select = ri2;
+                 if ((cmpret = bgp_info_cmp (bgp, ri2, new_select, afi, safi))
+                      == -1)
+                   {
+                     bgp_info_unset_flag (rn, new_select, BGP_INFO_DMED_SELECTED);
+                     new_select = ri2;
+                   }
+
+                 if (do_mpath)
+                   {
+                      if (cmpret != 0)
+                        bgp_mp_list_clear (&mp_list);
+
+                      if (cmpret == 0 || cmpret == -1)
+                        bgp_mp_list_add (&mp_list, ri2);
+                    }
+
+                 bgp_info_set_flag (rn, ri2, BGP_INFO_DMED_CHECK);
+               }
+           }
+       bgp_info_set_flag (rn, new_select, BGP_INFO_DMED_CHECK);
+       bgp_info_set_flag (rn, new_select, BGP_INFO_DMED_SELECTED);
+
+       bgp_info_mpath_update (rn, new_select, old_select, &mp_list, afi, safi);
+       bgp_mp_list_clear (&mp_list);
+      }
+
+  /* Check old selected route and new selected route. */
+  old_select = NULL;
+  new_select = NULL;
+  for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); ri = nextri)
+    {
+      if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))
+       old_select = ri;
+
+      if (BGP_INFO_HOLDDOWN (ri))
+        {
+          /* reap REMOVED routes, if needs be 
+           * selected route must stay for a while longer though
+           */
+          if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
+              && (ri != old_select))
+              bgp_info_reap (rn, ri);
+          
+          continue;
+        }
+
+      if (ri->peer &&
+          ri->peer != bgp->peer_self &&
+          !CHECK_FLAG (ri->peer->sflags, PEER_STATUS_NSF_WAIT))
+        if (ri->peer->status != Established)
+          continue;
+
+      if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)
+          && (! CHECK_FLAG (ri->flags, BGP_INFO_DMED_SELECTED)))
+       {
+         bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK);
+         continue;
+        }
+      bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK);
+      bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_SELECTED);
+
+      if ((cmpret = bgp_info_cmp (bgp, ri, new_select, afi, safi)) == -1)
+       {
+         if (do_mpath && bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
+           bgp_mp_dmed_deselect (new_select);
+
+         new_select = ri;
+       }
+      else if (cmpret == 1 && do_mpath
+               && bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
+       bgp_mp_dmed_deselect (ri);
+
+      if (do_mpath)
+        {
+          if (cmpret != 0)
+            bgp_mp_list_clear (&mp_list);
+
+          if (cmpret == 0 || cmpret == -1)
+            bgp_mp_list_add (&mp_list, ri);
+        }
+    }
+
+  if (!bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
+    bgp_info_mpath_update (rn, new_select, old_select, &mp_list, afi, safi);
+
+  bgp_info_mpath_aggregate_update (new_select, old_select);
+  bgp_mp_list_clear (&mp_list);
+
+  result->old = old_select;
+  result->new = new_select;
+
+  return;
+}
+
+static int
+bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected,
+                               struct bgp_node *rn, afi_t afi, safi_t safi)
+{
+  struct prefix *p;
+  struct attr attr;
+  struct attr_extra extra;
+
+  memset (&attr, 0, sizeof(struct attr));
+  memset (&extra, 0, sizeof(struct attr_extra));
+
+  p = &rn->p;
+
+  /* Announce route to Established peer. */
+  if (peer->status != Established)
+    return 0;
+
+  /* Address family configuration check. */
+  if (! peer->afc_nego[afi][safi])
+    return 0;
+
+  /* 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;
+
+  /* It's initialized in bgp_announce_[check|check_rsclient]() */
+  attr.extra = &extra;
+
+  switch (bgp_node_table (rn)->type)
+    {
+      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);
+        else
+          bgp_adj_out_unset (rn, peer, p, afi, safi);
+        break;
+      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);
+        break;
+    }
+
+  bgp_attr_flush (&attr);
+  return 0;
+}
+
+struct bgp_process_queue 
+{
+  struct bgp *bgp;
+  struct bgp_node *rn;
+  afi_t afi;
+  safi_t safi;
+};
+
+static wq_item_status
+bgp_process_rsclient (struct work_queue *wq, void *data)
+{
+  struct bgp_process_queue *pq = data;
+  struct bgp *bgp = pq->bgp;
+  struct bgp_node *rn = pq->rn;
+  afi_t afi = pq->afi;
+  safi_t safi = pq->safi;
+  struct bgp_info *new_select;
+  struct bgp_info *old_select;
+  struct bgp_info_pair old_and_new;
+  struct listnode *node, *nnode;
+  struct peer *rsclient = bgp_node_table (rn)->owner;
+  
+  /* Best path selection. */
+  bgp_best_selection (bgp, rn, &old_and_new, afi, safi);
+  new_select = old_and_new.new;
+  old_select = old_and_new.old;
+
+  if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_GROUP))
+    {
+      if (rsclient->group)
+        for (ALL_LIST_ELEMENTS (rsclient->group->peer, node, nnode, rsclient))
+          {
+            /* Nothing to do. */
+            if (old_select && old_select == new_select)
+              if (!CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED))
+                continue;
+
+            if (old_select)
+              bgp_info_unset_flag (rn, old_select, BGP_INFO_SELECTED);
+            if (new_select)
+              {
+                bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED);
+                bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+               UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG);
+             }
+
+            bgp_process_announce_selected (rsclient, new_select, rn,
+                                           afi, safi);
+          }
+    }
+  else
+    {
+      if (old_select)
+       bgp_info_unset_flag (rn, old_select, BGP_INFO_SELECTED);
+      if (new_select)
+       {
+         bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED);
+         bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+         UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG);
+       }
+      bgp_process_announce_selected (rsclient, new_select, rn, afi, safi);
+    }
+
+  if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
+    bgp_info_reap (rn, old_select);
+  
+  UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+  return WQ_SUCCESS;
+}
+
+static wq_item_status
+bgp_process_main (struct work_queue *wq, void *data)
+{
+  struct bgp_process_queue *pq = data;
+  struct bgp *bgp = pq->bgp;
+  struct bgp_node *rn = pq->rn;
+  afi_t afi = pq->afi;
+  safi_t safi = pq->safi;
+  struct prefix *p = &rn->p;
+  struct bgp_info *new_select;
+  struct bgp_info *old_select;
+  struct bgp_info_pair old_and_new;
+  struct listnode *node, *nnode;
+  struct peer *peer;
+  
+  /* Best path selection. */
+  bgp_best_selection (bgp, rn, &old_and_new, afi, safi);
+  old_select = old_and_new.old;
+  new_select = old_and_new.new;
+
+  /* Nothing to do. */
+  if (old_select && old_select == new_select 
+      && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR))
+    {
+      if (! CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED))
+        {
+          if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED) ||
+             CHECK_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG))
+            bgp_zebra_announce (p, old_select, bgp, safi);
+          
+         UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG);
+          UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+          return WQ_SUCCESS;
+        }
+    }
+
+  /* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set */
+  UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR);
+
+  if (old_select)
+    bgp_info_unset_flag (rn, old_select, BGP_INFO_SELECTED);
+  if (new_select)
+    {
+      bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED);
+      bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+      UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG);
+    }
+
+
+  /* Check each BGP peer. */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      bgp_process_announce_selected (peer, new_select, rn, afi, safi);
+    }
+
+  /* FIB update. */
+  if ((safi == SAFI_UNICAST || safi == SAFI_MULTICAST) && (! bgp->name &&
+      ! bgp_option_check (BGP_OPT_NO_FIB)))
+    {
+      if (new_select 
+         && new_select->type == ZEBRA_ROUTE_BGP 
+         && new_select->sub_type == BGP_ROUTE_NORMAL)
+       bgp_zebra_announce (p, new_select, bgp, safi);
+      else
+       {
+         /* Withdraw the route from the kernel. */
+         if (old_select 
+             && old_select->type == ZEBRA_ROUTE_BGP
+             && old_select->sub_type == BGP_ROUTE_NORMAL)
+           bgp_zebra_withdraw (p, old_select, safi);
+       }
+    }
+    
+  /* Reap old select bgp_info, if it has been removed */
+  if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
+    bgp_info_reap (rn, old_select);
+  
+  UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+  return WQ_SUCCESS;
+}
+
+static void
+bgp_processq_del (struct work_queue *wq, void *data)
+{
+  struct bgp_process_queue *pq = data;
+  struct bgp_table *table = bgp_node_table (pq->rn);
+  
+  bgp_unlock (pq->bgp);
+  bgp_unlock_node (pq->rn);
+  bgp_table_unlock (table);
+  XFREE (MTYPE_BGP_PROCESS_QUEUE, pq);
+}
+
+static void
+bgp_process_queue_init (void)
+{
+  bm->process_main_queue
+    = work_queue_new (bm->master, "process_main_queue");
+  bm->process_rsclient_queue
+    = work_queue_new (bm->master, "process_rsclient_queue");
+  
+  if ( !(bm->process_main_queue && bm->process_rsclient_queue) )
+    {
+      zlog_err ("%s: Failed to allocate work queue", __func__);
+      exit (1);
+    }
+  
+  bm->process_main_queue->spec.workfunc = &bgp_process_main;
+  bm->process_main_queue->spec.del_item_data = &bgp_processq_del;
+  bm->process_main_queue->spec.max_retries = 0;
+  bm->process_main_queue->spec.hold = 50;
+  
+  bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient;
+  bm->process_rsclient_queue->spec.del_item_data = &bgp_processq_del;
+  bm->process_rsclient_queue->spec.max_retries = 0;
+  bm->process_rsclient_queue->spec.hold = 50;
+}
+
+void
+bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi)
+{
+  struct bgp_process_queue *pqnode;
+  
+  /* already scheduled for processing? */
+  if (CHECK_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED))
+    return;
+  
+  if (rn->info == NULL)
+    {
+      /* XXX: Perhaps remove before next release, after we've flushed out
+       * any obvious cases
+       */
+      assert (rn->info != NULL);
+      char buf[PREFIX_STRLEN];
+      zlog_warn ("%s: Called for route_node %s with no routing entries!",
+                 __func__,
+                 prefix2str (&(bgp_node_to_rnode (rn)->p), buf, sizeof(buf)));
+      return;
+    }
+  
+  if ( (bm->process_main_queue == NULL) ||
+       (bm->process_rsclient_queue == NULL) )
+    bgp_process_queue_init ();
+  
+  pqnode = XCALLOC (MTYPE_BGP_PROCESS_QUEUE, 
+                    sizeof (struct bgp_process_queue));
+  if (!pqnode)
+    return;
+
+  /* all unlocked in bgp_processq_del */
+  bgp_table_lock (bgp_node_table (rn));
+  pqnode->rn = bgp_lock_node (rn);
+  pqnode->bgp = bgp;
+  bgp_lock (bgp);
+  pqnode->afi = afi;
+  pqnode->safi = safi;
+  
+  switch (bgp_node_table (rn)->type)
+    {
+      case BGP_TABLE_MAIN:
+        work_queue_add (bm->process_main_queue, pqnode);
+        break;
+      case BGP_TABLE_RSCLIENT:
+        work_queue_add (bm->process_rsclient_queue, pqnode);
+        break;
+    }
+  
+  SET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+  return;
+}
+
+static int
+bgp_maximum_prefix_restart_timer (struct thread *thread)
+{
+  struct peer *peer;
+
+  peer = THREAD_ARG (thread);
+  peer->t_pmax_restart = NULL;
+
+  if (BGP_DEBUG (events, EVENTS))
+    zlog_debug ("%s Maximum-prefix restart timer expired, restore peering",
+               peer->host);
+
+  peer_clear (peer);
+
+  return 0;
+}
+
+int
+bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, 
+                             safi_t safi, int always)
+{
+  if (!CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
+    return 0;
+
+  if (peer->pcount[afi][safi] > peer->pmax[afi][safi])
+    {
+      if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT)
+         && ! always)
+       return 0;
+
+      zlog (peer->log, LOG_INFO,
+           "%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, "
+           "limit %ld", afi_safi_print (afi, safi), peer->host,
+           peer->pcount[afi][safi], peer->pmax[afi][safi]);
+      SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
+
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
+       return 0;
+
+      {
+       u_int8_t ndata[7];
+
+       if (safi == SAFI_MPLS_VPN)
+         safi = SAFI_MPLS_LABELED_VPN;
+         
+       ndata[0] = (afi >>  8);
+       ndata[1] = afi;
+       ndata[2] = safi;
+       ndata[3] = (peer->pmax[afi][safi] >> 24);
+       ndata[4] = (peer->pmax[afi][safi] >> 16);
+       ndata[5] = (peer->pmax[afi][safi] >> 8);
+       ndata[6] = (peer->pmax[afi][safi]);
+
+       SET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+       bgp_notify_send_with_data (peer, BGP_NOTIFY_CEASE,
+                                  BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7);
+      }
+
+      /* restart timer start */
+      if (peer->pmax_restart[afi][safi])
+       {
+         peer->v_pmax_restart = peer->pmax_restart[afi][safi] * 60;
+
+         if (BGP_DEBUG (events, EVENTS))
+           zlog_debug ("%s Maximum-prefix restart timer started for %d secs",
+                       peer->host, peer->v_pmax_restart);
+
+         BGP_TIMER_ON (peer->t_pmax_restart, bgp_maximum_prefix_restart_timer,
+                       peer->v_pmax_restart);
+       }
+
+      return 1;
+    }
+  else
+    UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
+
+  if (peer->pcount[afi][safi] > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100))
+    {
+      if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD)
+         && ! always)
+       return 0;
+
+      zlog (peer->log, LOG_INFO,
+           "%%MAXPFX: No. of %s prefix received from %s reaches %ld, max %ld",
+           afi_safi_print (afi, safi), peer->host, peer->pcount[afi][safi],
+           peer->pmax[afi][safi]);
+      SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD);
+    }
+  else
+    UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD);
+  return 0;
+}
+
+/* Unconditionally remove the route from the RIB, without taking
+ * damping into consideration (eg, because the session went down)
+ */
+static void
+bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
+               afi_t afi, safi_t safi)
+{
+  bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi);
+  
+  if (!CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+    bgp_info_delete (rn, ri); /* keep historical info */
+    
+  bgp_process (peer->bgp, rn, afi, safi);
+}
+
+static void
+bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
+                 afi_t afi, safi_t safi, struct prefix_rd *prd)
+{
+  int status = BGP_DAMP_NONE;
+
+  /* apply dampening, if result is suppressed, we'll be retaining 
+   * the bgp_info in the RIB for historical reference.
+   */
+  if (CHECK_FLAG (peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+      && peer->sort == BGP_PEER_EBGP)
+    if ( (status = bgp_damp_withdraw (ri, rn, afi, safi, 0)) 
+         == BGP_DAMP_SUPPRESSED)
+      {
+        bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi);
+        return;
+      }
+    
+  bgp_rib_remove (rn, ri, peer, afi, safi);
+}
+
+static struct bgp_info *
+info_make (int type, int sub_type, struct peer *peer, struct attr *attr,
+          struct bgp_node *rn)
+{
+  struct bgp_info *new;
+
+  /* Make new BGP info. */
+  new = XCALLOC (MTYPE_BGP_ROUTE, sizeof (struct bgp_info));
+  new->type = type;
+  new->sub_type = sub_type;
+  new->peer = peer;
+  new->attr = attr;
+  new->uptime = bgp_clock ();
+  new->net = rn;
+  return new;
+}
+
+static void
+bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
+      struct attr *attr, struct peer *peer, struct prefix *p, int type,
+      int sub_type, struct prefix_rd *prd, u_char *tag)
+{
+  struct bgp_node *rn;
+  struct bgp *bgp;
+  struct attr new_attr;
+  struct attr_extra new_extra;
+  struct attr *attr_new;
+  struct attr *attr_new2;
+  struct bgp_info *ri;
+  struct bgp_info *new;
+  const char *reason;
+  char buf[SU_ADDRSTRLEN];
+
+  /* Do not insert announces from a rsclient into its own 'bgp_table'. */
+  if (peer == rsclient)
+    return;
+
+  bgp = peer->bgp;
+  rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd);
+
+  /* Check previously received route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
+      break;
+
+  /* AS path loop check. */
+  if (aspath_loop_check (attr->aspath, rsclient->as) > rsclient->allowas_in[afi][safi])
+    {
+      reason = "as-path contains our own AS;";
+      goto filtered;
+    }
+
+  /* Route reflector originator ID check.  */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)
+      && IPV4_ADDR_SAME (&rsclient->remote_id, &attr->extra->originator_id))
+    {
+      reason = "originator is us;";
+      goto filtered;
+    }
+  
+  new_attr.extra = &new_extra;
+  bgp_attr_dup (&new_attr, attr);
+
+  /* Apply export policy. */
+  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) &&
+        bgp_export_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY)
+    {
+      reason = "export-policy;";
+      goto filtered;
+    }
+
+  attr_new2 = bgp_attr_intern (&new_attr);
+  
+  /* Apply import policy. */
+  if (bgp_import_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY)
+    {
+      bgp_attr_unintern (&attr_new2);
+
+      reason = "import-policy;";
+      goto filtered;
+    }
+
+  attr_new = bgp_attr_intern (&new_attr);
+  bgp_attr_unintern (&attr_new2);
+
+  /* IPv4 unicast next hop check.  */
+  if ((afi == AFI_IP) && ((safi == SAFI_UNICAST) || safi == SAFI_MULTICAST))
+    {
+     /* Next hop must not be 0.0.0.0 nor Class D/E address. */
+      if (new_attr.nexthop.s_addr == 0
+         || IPV4_CLASS_DE (ntohl (new_attr.nexthop.s_addr)))
+       {
+         bgp_attr_unintern (&attr_new);
+
+         reason = "martian next-hop;";
+         goto filtered;
+       }
+    }
+
+  /* If the update is implicit withdraw. */
+  if (ri)
+    {
+      ri->uptime = bgp_clock ();
+
+      /* Same attribute comes in. */
+      if (!CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)
+          && attrhash_cmp (ri->attr, attr_new))
+        {
+
+
+          if (BGP_DEBUG (update, UPDATE_IN))
+            zlog (peer->log, LOG_DEBUG,
+                    "%s rcvd %s/%d for RS-client %s...duplicate ignored",
+                    peer->host,
+                    inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                    p->prefixlen, rsclient->host);
+
+          bgp_unlock_node (rn);
+          bgp_attr_unintern (&attr_new);
+          bgp_attr_flush(&new_attr);
+          return;
+        }
+
+      /* Withdraw/Announce before we fully processed the withdraw */
+      if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+        bgp_info_restore (rn, ri);
+      
+      /* Received Logging. */
+      if (BGP_DEBUG (update, UPDATE_IN))
+        zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s",
+                peer->host,
+                inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                p->prefixlen, rsclient->host);
+
+      /* The attribute is changed. */
+      bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+
+      /* Update to new attribute.  */
+      bgp_attr_unintern (&ri->attr);
+      ri->attr = attr_new;
+
+      /* Update MPLS tag.  */
+      if (safi == SAFI_MPLS_VPN)
+        memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
+
+      bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+
+      /* Process change. */
+      bgp_process (bgp, rn, afi, safi);
+      bgp_unlock_node (rn);
+
+      return;
+    }
+
+  /* Received Logging. */
+  if (BGP_DEBUG (update, UPDATE_IN))
+    {
+      zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s",
+              peer->host,
+              inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+              p->prefixlen, rsclient->host);
+    }
+
+  new = info_make(type, sub_type, peer, attr_new, rn);
+
+  /* Update MPLS tag. */
+  if (safi == SAFI_MPLS_VPN)
+    memcpy ((bgp_info_extra_get (new))->tag, tag, 3);
+
+  bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+
+  /* Register new BGP information. */
+  bgp_info_add (rn, new);
+  
+  /* route_node_get lock */
+  bgp_unlock_node (rn);
+  
+  /* Process change. */
+  bgp_process (bgp, rn, afi, safi);
+
+  return;
+
+ filtered: 
+
+  /* This BGP update is filtered.  Log the reason then update BGP entry.  */
+  if (BGP_DEBUG (update, UPDATE_IN))
+        zlog (peer->log, LOG_DEBUG,
+        "%s rcvd UPDATE about %s/%d -- DENIED for RS-client %s due to: %s",
+        peer->host,
+        inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+        p->prefixlen, rsclient->host, reason);
+
+  if (ri)
+    bgp_rib_remove (rn, ri, peer, afi, safi);
+
+  bgp_unlock_node (rn);
+
+  return;
+}
+
+static void
+bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
+      struct peer *peer, struct prefix *p, int type, int sub_type,
+      struct prefix_rd *prd, u_char *tag)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  char buf[SU_ADDRSTRLEN];
+
+  if (rsclient == peer)
+    return;
+
+  rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd);
+
+  /* Lookup withdrawn route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
+      break;
+
+  /* Withdraw specified route from routing table. */
+  if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+    bgp_rib_withdraw (rn, ri, peer, afi, safi, prd);
+  else if (BGP_DEBUG (update, UPDATE_IN))
+    zlog (peer->log, LOG_DEBUG,
+          "%s Can't find the route %s/%d", peer->host,
+          inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+          p->prefixlen);
+
+  /* Unlock bgp_node_get() lock. */
+  bgp_unlock_node (rn);
+}
+
+static int
+bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
+           afi_t afi, safi_t safi, int type, int sub_type,
+           struct prefix_rd *prd, u_char *tag, int soft_reconfig)
+{
+  int ret;
+  int aspath_loop_count = 0;
+  struct bgp_node *rn;
+  struct bgp *bgp;
+  struct attr new_attr;
+  struct attr_extra new_extra;
+  struct attr *attr_new;
+  struct bgp_info *ri;
+  struct bgp_info *new;
+  const char *reason;
+  char buf[SU_ADDRSTRLEN];
+  int connected = 0;
+
+  memset (&new_attr, 0, sizeof(struct attr));
+  memset (&new_extra, 0, sizeof(struct attr_extra));
+
+  bgp = peer->bgp;
+  rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+  
+  /* When peer's soft reconfiguration enabled.  Record input packet in
+     Adj-RIBs-In.  */
+  if (! soft_reconfig && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)
+      && peer != bgp->peer_self)
+    bgp_adj_in_set (rn, peer, attr);
+
+  /* Check previously received route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
+      break;
+
+  /* AS path local-as loop check. */
+  if (peer->change_local_as)
+    {
+      if (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+       aspath_loop_count = 1;
+
+      if (aspath_loop_check (attr->aspath, peer->change_local_as) > aspath_loop_count) 
+       {
+         reason = "as-path contains our own AS;";
+         goto filtered;
+       }
+    }
+
+  /* AS path loop check. */
+  if (aspath_loop_check (attr->aspath, bgp->as) > peer->allowas_in[afi][safi]
+      || (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)
+         && aspath_loop_check(attr->aspath, bgp->confed_id)
+         > peer->allowas_in[afi][safi]))
+    {
+      reason = "as-path contains our own AS;";
+      goto filtered;
+    }
+
+  /* Route reflector originator ID check.  */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)
+      && IPV4_ADDR_SAME (&bgp->router_id, &attr->extra->originator_id))
+    {
+      reason = "originator is us;";
+      goto filtered;
+    }
+
+  /* Route reflector cluster ID check.  */
+  if (bgp_cluster_filter (peer, attr))
+    {
+      reason = "reflected from the same cluster;";
+      goto  filtered;
+    }
+
+  /* Apply incoming filter.  */
+  if (bgp_input_filter (peer, p, attr, afi, safi) == FILTER_DENY)
+    {
+      reason = "filter;";
+      goto filtered;
+    }
+
+  new_attr.extra = &new_extra;
+  bgp_attr_dup (&new_attr, attr);
+
+  /* Apply incoming route-map.
+   * NB: new_attr may now contain newly allocated values from route-map "set"
+   * commands, so we need bgp_attr_flush in the error paths, until we intern
+   * the attr (which takes over the memory references) */
+  if (bgp_input_modifier (peer, p, &new_attr, afi, safi) == RMAP_DENY)
+    {
+      reason = "route-map;";
+      bgp_attr_flush (&new_attr);
+      goto filtered;
+    }
+
+  /* IPv4 unicast next hop check.  */
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    {
+      /* Next hop must not be 0.0.0.0 nor Class D/E address. Next hop
+        must not be my own address.  */
+      if (new_attr.nexthop.s_addr == 0
+         || IPV4_CLASS_DE (ntohl (new_attr.nexthop.s_addr))
+         || bgp_nexthop_self (&new_attr))
+       {
+         reason = "martian next-hop;";
+         bgp_attr_flush (&new_attr);
+         goto filtered;
+       }
+    }
+
+  attr_new = bgp_attr_intern (&new_attr);
+
+  /* If the update is implicit withdraw. */
+  if (ri)
+    {
+      ri->uptime = bgp_clock ();
+
+      /* Same attribute comes in. */
+      if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) 
+          && attrhash_cmp (ri->attr, attr_new))
+       {
+         if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+             && peer->sort == BGP_PEER_EBGP
+             && CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+           {
+             if (BGP_DEBUG (update, UPDATE_IN))  
+                 zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
+                 peer->host,
+                 inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                 p->prefixlen);
+
+             if (bgp_damp_update (ri, rn, afi, safi) != BGP_DAMP_SUPPRESSED)
+               {
+                  bgp_aggregate_increment (bgp, p, ri, afi, safi);
+                  bgp_process (bgp, rn, afi, safi);
+                }
+           }
+          else /* Duplicate - odd */
+           {
+             if (BGP_DEBUG (update, UPDATE_IN))  
+               zlog (peer->log, LOG_DEBUG,
+               "%s rcvd %s/%d...duplicate ignored",
+               peer->host,
+               inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+               p->prefixlen);
+
+             /* graceful restart STALE flag unset. */
+             if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
+               {
+                 bgp_info_unset_flag (rn, ri, BGP_INFO_STALE);
+                 bgp_process (bgp, rn, afi, safi);
+               }
+           }
+
+         bgp_unlock_node (rn);
+         bgp_attr_unintern (&attr_new);
+          bgp_attr_flush (&new_attr);
+
+         return 0;
+       }
+
+      /* Withdraw/Announce before we fully processed the withdraw */
+      if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+        {
+          if (BGP_DEBUG (update, UPDATE_IN))
+            zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d, flapped quicker than processing",
+            peer->host,
+            inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+            p->prefixlen);
+          bgp_info_restore (rn, ri);
+        }
+
+      /* Received Logging. */
+      if (BGP_DEBUG (update, UPDATE_IN))  
+       zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
+             peer->host,
+             inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+             p->prefixlen);
+
+      /* graceful restart STALE flag unset. */
+      if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
+       bgp_info_unset_flag (rn, ri, BGP_INFO_STALE);
+
+      /* The attribute is changed. */
+      bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+      
+      /* implicit withdraw, decrement aggregate and pcount here.
+       * only if update is accepted, they'll increment below.
+       */
+      bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+      
+      /* Update bgp route dampening information.  */
+      if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+         && peer->sort == BGP_PEER_EBGP)
+       {
+         /* This is implicit withdraw so we should update dampening
+            information.  */
+         if (! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+           bgp_damp_withdraw (ri, rn, afi, safi, 1);  
+       }
+       
+      /* Update to new attribute.  */
+      bgp_attr_unintern (&ri->attr);
+      ri->attr = attr_new;
+
+      /* Update MPLS tag.  */
+      if (safi == SAFI_MPLS_VPN)
+        memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
+
+      bgp_attr_flush (&new_attr);
+
+      /* Update bgp route dampening information.  */
+      if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+         && peer->sort == BGP_PEER_EBGP)
+       {
+         /* Now we do normal update dampening.  */
+         ret = bgp_damp_update (ri, rn, afi, safi);
+         if (ret == BGP_DAMP_SUPPRESSED)
+           {
+             bgp_unlock_node (rn);
+             return 0;
+           }
+       }
+
+      /* Nexthop reachability check. */
+      if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)
+       {
+         if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 &&
+             ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+           connected = 1;
+         else
+           connected = 0;
+
+         if (bgp_ensure_nexthop (ri, NULL, connected))
+           bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+         else
+           {
+             if (BGP_DEBUG(nht, NHT))
+               {
+                 char buf1[INET6_ADDRSTRLEN];
+                 inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, INET6_ADDRSTRLEN);
+                 zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+               }
+             bgp_info_unset_flag (rn, ri, BGP_INFO_VALID);
+           }
+       }
+      else
+       bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+
+      bgp_attr_flush (&new_attr);
+
+      /* Process change. */
+      bgp_aggregate_increment (bgp, p, ri, afi, safi);
+
+      bgp_process (bgp, rn, afi, safi);
+      bgp_unlock_node (rn);
+
+      return 0;
+    }
+
+  /* Received Logging. */
+  if (BGP_DEBUG (update, UPDATE_IN))  
+    {
+      zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d",
+           peer->host,
+           inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+           p->prefixlen);
+    }
+
+  /* Make new BGP info. */
+  new = info_make(type, sub_type, peer, attr_new, rn);
+
+  /* Update MPLS tag. */
+  if (safi == SAFI_MPLS_VPN)
+    memcpy ((bgp_info_extra_get (new))->tag, tag, 3);
+
+  /* Nexthop reachability check. */
+  if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)
+    {
+      if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 &&
+         ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+       connected = 1;
+      else
+       connected = 0;
+
+      if (bgp_ensure_nexthop (new, NULL, connected))
+       bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+      else
+       {
+         if (BGP_DEBUG(nht, NHT))
+           {
+             char buf1[INET6_ADDRSTRLEN];
+             inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, INET6_ADDRSTRLEN);
+             zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+           }
+         bgp_info_unset_flag (rn, new, BGP_INFO_VALID);
+       }
+    }
+  else
+    bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+
+  /* Increment prefix */
+  bgp_aggregate_increment (bgp, p, new, afi, safi);
+  
+  /* Register new BGP information. */
+  bgp_info_add (rn, new);
+  
+  /* route_node_get lock */
+  bgp_unlock_node (rn);
+
+  bgp_attr_flush (&new_attr);
+
+  /* If maximum prefix count is configured and current prefix
+     count exeed it. */
+  if (bgp_maximum_prefix_overflow (peer, afi, safi, 0))
+    return -1;
+
+  /* Process change. */
+  bgp_process (bgp, rn, afi, safi);
+
+  return 0;
+
+  /* This BGP update is filtered.  Log the reason then update BGP
+     entry.  */
+ filtered:
+  if (BGP_DEBUG (update, UPDATE_IN))
+    zlog (peer->log, LOG_DEBUG,
+         "%s rcvd UPDATE about %s/%d -- DENIED due to: %s",
+         peer->host,
+         inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+         p->prefixlen, reason);
+
+  if (ri)
+    bgp_rib_remove (rn, ri, peer, afi, safi);
+
+  bgp_unlock_node (rn);
+  bgp_attr_flush (&new_attr);
+
+  return 0;
+}
+
+int
+bgp_update (struct peer *peer, struct prefix *p, struct attr *attr,
+            afi_t afi, safi_t safi, int type, int sub_type,
+            struct prefix_rd *prd, u_char *tag, int soft_reconfig)
+{
+  struct peer *rsclient;
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+  int ret;
+
+  ret = bgp_update_main (peer, p, attr, afi, safi, type, sub_type, prd, tag,
+          soft_reconfig);
+
+  bgp = peer->bgp;
+
+  /* Process the update for each RS-client. */
+  for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
+    {
+      if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+        bgp_update_rsclient (rsclient, afi, safi, attr, peer, p, type,
+                sub_type, prd, tag);
+    }
+
+  return ret;
+}
+
+int
+bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, 
+            afi_t afi, safi_t safi, int type, int sub_type, 
+            struct prefix_rd *prd, u_char *tag)
+{
+  struct bgp *bgp;
+  char buf[SU_ADDRSTRLEN];
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct peer *rsclient;
+  struct listnode *node, *nnode;
+
+  bgp = peer->bgp;
+
+  /* Lookup node. */
+  rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+
+  /* Cisco IOS 12.4(24)T4 on session establishment sends withdraws for all
+   * routes that are filtered.  This tanks out Quagga RS pretty badly due to
+   * the iteration over all RS clients.
+   * Since we need to remove the entry from adj_in anyway, do that first and
+   * if there was no entry, we don't need to do anything more. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)
+      && peer != bgp->peer_self)
+    if (!bgp_adj_in_unset (rn, peer))
+      {
+        if (BGP_DEBUG (update, UPDATE_IN))
+          zlog (peer->log, LOG_DEBUG, "%s withdrawing route %s/%d "
+                "not in adj-in", peer->host,
+                inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                p->prefixlen);
+        bgp_unlock_node (rn);
+        return 0;
+      }
+
+  /* Process the withdraw for each RS-client. */
+  for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
+    {
+      if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+        bgp_withdraw_rsclient (rsclient, afi, safi, peer, p, type, sub_type, prd, tag);
+    }
+
+  /* Logging. */
+  if (BGP_DEBUG (update, UPDATE_IN))  
+    zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE about %s/%d -- withdrawn",
+         peer->host,
+         inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+         p->prefixlen);
+
+  /* Lookup withdrawn route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type)
+      break;
+
+  /* Withdraw specified route from routing table. */
+  if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+    bgp_rib_withdraw (rn, ri, peer, afi, safi, prd);
+  else if (BGP_DEBUG (update, UPDATE_IN))
+    zlog (peer->log, LOG_DEBUG, 
+         "%s Can't find the route %s/%d", peer->host,
+         inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+         p->prefixlen);
+
+  /* Unlock bgp_node_get() lock. */
+  bgp_unlock_node (rn);
+
+  return 0;
+}
+
+void
+bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
+{
+  struct bgp *bgp;
+  struct attr attr;
+  struct aspath *aspath;
+  struct prefix p;
+  struct peer *from;
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  int ret = RMAP_DENYMATCH;
+  
+  if (!(afi == AFI_IP || afi == AFI_IP6))
+    return;
+  
+  bgp = peer->bgp;
+  from = bgp->peer_self;
+  
+  bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+  aspath = attr.aspath;
+  attr.local_pref = bgp->default_local_pref;
+  memcpy (&attr.nexthop, &peer->nexthop.v4, IPV4_MAX_BYTELEN);
+
+  if (afi == AFI_IP)
+    str2prefix ("0.0.0.0/0", &p);
+  else if (afi == AFI_IP6)
+    {
+      struct attr_extra *ae = attr.extra;
+
+      str2prefix ("::/0", &p);
+
+      /* IPv6 global nexthop must be included. */
+      memcpy (&ae->mp_nexthop_global, &peer->nexthop.v6_global, 
+             IPV6_MAX_BYTELEN);
+             ae->mp_nexthop_len = 16;
+      /* If the peer is on shared nextwork and we have link-local
+        nexthop set it. */
+      if (peer->shared_network 
+         && !IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local))
+       {
+         memcpy (&ae->mp_nexthop_local, &peer->nexthop.v6_local, 
+                 IPV6_MAX_BYTELEN);
+         ae->mp_nexthop_len = 32;
+       }
+    }
+
+  if (peer->default_rmap[afi][safi].name)
+    {
+      SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
+      for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn))
+        {
+          for (ri = rn->info; ri; ri = ri->next)
+            {
+              struct attr dummy_attr;
+              struct attr_extra dummy_extra;
+              struct bgp_info info;
+
+              /* Provide dummy so the route-map can't modify the attributes */
+              dummy_attr.extra = &dummy_extra;
+              bgp_attr_dup(&dummy_attr, ri->attr);
+              info.peer = ri->peer;
+              info.attr = &dummy_attr;
+
+              ret = route_map_apply(peer->default_rmap[afi][safi].map, &rn->p,
+                                    RMAP_BGP, &info);
+
+              /* The route map might have set attributes. If we don't flush them
+               * here, they will be leaked. */
+              bgp_attr_flush(&dummy_attr);
+              if (ret != RMAP_DENYMATCH)
+                break;
+            }
+          if (ret != RMAP_DENYMATCH)
+            break;
+        }
+      bgp->peer_self->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+        withdraw = 1;
+    }
+
+  if (withdraw)
+    {
+      if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE))
+       bgp_default_withdraw_send (peer, afi, safi);
+      UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE);
+    }
+  else
+    {
+      if (! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE))
+        {
+          SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE);
+          bgp_default_update_send (peer, &attr, afi, safi, from);
+        }
+    }
+  
+  bgp_attr_extra_free (&attr);
+  aspath_unintern (&aspath);
+}
+
+static void
+bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
+                   struct bgp_table *table, int rsclient)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct attr attr;
+  struct attr_extra extra;
+
+  memset(&extra, 0, sizeof(extra));
+
+  if (! table)
+    table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi];
+
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
+      && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
+    bgp_default_originate (peer, afi, safi, 0);
+
+  /* It's initialized in bgp_announce_[check|check_rsclient]() */
+  attr.extra = &extra;
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next(rn))
+    for (ri = rn->info; ri; ri = ri->next)
+      if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) && ri->peer != peer)
+       {
+         if ( (rsclient) ?
+              (bgp_announce_check_rsclient (ri, peer, &rn->p, &attr, afi, safi))
+              : (bgp_announce_check (ri, peer, &rn->p, &attr, afi, safi)))
+           bgp_adj_out_set (rn, peer, &rn->p, &attr, afi, safi, ri);
+         else
+           bgp_adj_out_unset (rn, peer, &rn->p, afi, safi);
+       }
+
+  bgp_attr_flush_encap(&attr);
+}
+
+void
+bgp_announce_route (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_table *table;
+
+  if (peer->status != Established)
+    return;
+
+  if (! peer->afc_nego[afi][safi])
+    return;
+
+  /* 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;
+
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
+    bgp_announce_table (peer, afi, safi, NULL, 0);
+  else
+    for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
+        rn = bgp_route_next(rn))
+      if ((table = (rn->info)) != NULL)
+       bgp_announce_table (peer, afi, safi, table, 0);
+
+  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    bgp_announce_table (peer, afi, safi, NULL, 1);
+}
+
+void
+bgp_announce_route_all (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+  
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      bgp_announce_route (peer, afi, safi);
+}
+
+static void
+bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi,
+        safi_t safi, struct bgp_table *table, struct prefix_rd *prd)
+{
+  struct bgp_node *rn;
+  struct bgp_adj_in *ain;
+
+  if (! table)
+    table = rsclient->bgp->rib[afi][safi];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    for (ain = rn->adj_in; ain; ain = ain->next)
+      {
+        struct bgp_info *ri = rn->info;
+        u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL;
+
+        bgp_update_rsclient (rsclient, afi, safi, ain->attr, ain->peer,
+                &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, tag);
+      }
+}
+
+void
+bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi)
+{
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
+    bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, NULL, NULL);
+
+  else
+    for (rn = bgp_table_top (rsclient->bgp->rib[afi][safi]); rn;
+            rn = bgp_route_next (rn))
+      if ((table = rn->info) != NULL)
+        {
+          struct prefix_rd prd;
+          prd.family = AF_UNSPEC;
+          prd.prefixlen = 64;
+          memcpy(&prd.val, rn->p.u.val, 8);
+
+          bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, table, &prd);
+        }
+}
+
+static void
+bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi,
+                        struct bgp_table *table, struct prefix_rd *prd)
+{
+  int ret;
+  struct bgp_node *rn;
+  struct bgp_adj_in *ain;
+
+  if (! table)
+    table = peer->bgp->rib[afi][safi];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    for (ain = rn->adj_in; ain; ain = ain->next)
+      {
+       if (ain->peer == peer)
+         {
+           struct bgp_info *ri = rn->info;
+           u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL;
+
+           ret = bgp_update (peer, &rn->p, ain->attr, afi, safi,
+                             ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+                             prd, tag, 1);
+
+           if (ret < 0)
+             {
+               bgp_unlock_node (rn);
+               return;
+             }
+           continue;
+         }
+      }
+}
+
+void
+bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_table *table;
+
+  if (peer->status != Established)
+    return;
+
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
+    bgp_soft_reconfig_table (peer, afi, safi, NULL, NULL);
+  else
+    for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
+        rn = bgp_route_next (rn))
+      if ((table = rn->info) != NULL)
+        {
+          struct prefix_rd prd;
+          prd.family = AF_UNSPEC;
+          prd.prefixlen = 64;
+          memcpy(&prd.val, rn->p.u.val, 8);
+
+          bgp_soft_reconfig_table (peer, afi, safi, table, &prd);
+        }
+}
+
+
+struct bgp_clear_node_queue
+{
+  struct bgp_node *rn;
+  enum bgp_clear_route_type purpose;
+};
+
+static wq_item_status
+bgp_clear_route_node (struct work_queue *wq, void *data)
+{
+  struct bgp_clear_node_queue *cnq = data;
+  struct bgp_node *rn = cnq->rn;
+  struct peer *peer = wq->spec.data;
+  struct bgp_info *ri;
+  afi_t afi = bgp_node_table (rn)->afi;
+  safi_t safi = bgp_node_table (rn)->safi;
+  
+  assert (rn && peer);
+  
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == peer || cnq->purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
+      {
+        /* graceful restart STALE flag set. */
+        if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)
+            && peer->nsf[afi][safi]
+            && ! CHECK_FLAG (ri->flags, BGP_INFO_STALE)
+            && ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
+          bgp_info_set_flag (rn, ri, BGP_INFO_STALE);
+        else
+          bgp_rib_remove (rn, ri, peer, afi, safi);
+        break;
+      }
+  return WQ_SUCCESS;
+}
+
+static void
+bgp_clear_node_queue_del (struct work_queue *wq, void *data)
+{
+  struct bgp_clear_node_queue *cnq = data;
+  struct bgp_node *rn = cnq->rn;
+  struct bgp_table *table = bgp_node_table (rn);
+  
+  bgp_unlock_node (rn); 
+  bgp_table_unlock (table);
+  XFREE (MTYPE_BGP_CLEAR_NODE_QUEUE, cnq);
+}
+
+static void
+bgp_clear_node_complete (struct work_queue *wq)
+{
+  struct peer *peer = wq->spec.data;
+  
+  /* Tickle FSM to start moving again */
+  BGP_EVENT_ADD (peer, Clearing_Completed);
+
+  peer_unlock (peer); /* bgp_clear_route */
+}
+
+static void
+bgp_clear_node_queue_init (struct peer *peer)
+{
+  char wname[sizeof("clear xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")];
+  
+  snprintf (wname, sizeof(wname), "clear %s", peer->host);
+#undef CLEAR_QUEUE_NAME_LEN
+
+  if ( (peer->clear_node_queue = work_queue_new (bm->master, wname)) == NULL)
+    {
+      zlog_err ("%s: Failed to allocate work queue", __func__);
+      exit (1);
+    }
+  peer->clear_node_queue->spec.hold = 10;
+  peer->clear_node_queue->spec.workfunc = &bgp_clear_route_node;
+  peer->clear_node_queue->spec.del_item_data = &bgp_clear_node_queue_del;
+  peer->clear_node_queue->spec.completion_func = &bgp_clear_node_complete;
+  peer->clear_node_queue->spec.max_retries = 0;
+  
+  /* we only 'lock' this peer reference when the queue is actually active */
+  peer->clear_node_queue->spec.data = peer;
+}
+
+static void
+bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi,
+                       struct bgp_table *table, struct peer *rsclient,
+                       enum bgp_clear_route_type purpose)
+{
+  struct bgp_node *rn;
+  
+  
+  if (! table)
+    table = (rsclient) ? rsclient->rib[afi][safi] : peer->bgp->rib[afi][safi];
+  
+  /* If still no table => afi/safi isn't configured at all or smth. */
+  if (! table)
+    return;
+  
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    {
+      struct bgp_info *ri;
+      struct bgp_adj_in *ain;
+      struct bgp_adj_out *aout;
+
+      /* XXX:TODO: This is suboptimal, every non-empty route_node is
+       * queued for every clearing peer, regardless of whether it is
+       * relevant to the peer at hand.
+       *
+       * Overview: There are 3 different indices which need to be
+       * scrubbed, potentially, when a peer is removed:
+       *
+       * 1 peer's routes visible via the RIB (ie accepted routes)
+       * 2 peer's routes visible by the (optional) peer's adj-in index
+       * 3 other routes visible by the peer's adj-out index
+       *
+       * 3 there is no hurry in scrubbing, once the struct peer is
+       * removed from bgp->peer, we could just GC such deleted peer's
+       * adj-outs at our leisure.
+       *
+       * 1 and 2 must be 'scrubbed' in some way, at least made
+       * invisible via RIB index before peer session is allowed to be
+       * brought back up. So one needs to know when such a 'search' is
+       * complete.
+       *
+       * Ideally:
+       *
+       * - there'd be a single global queue or a single RIB walker
+       * - rather than tracking which route_nodes still need to be
+       *   examined on a peer basis, we'd track which peers still
+       *   aren't cleared
+       *
+       * Given that our per-peer prefix-counts now should be reliable,
+       * this may actually be achievable. It doesn't seem to be a huge
+       * problem at this time,
+       */
+      for (ain = rn->adj_in; ain; ain = ain->next)
+        if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
+          {
+            bgp_adj_in_remove (rn, ain);
+            bgp_unlock_node (rn);
+            break;
+          }
+      for (aout = rn->adj_out; aout; aout = aout->next)
+        if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
+          {
+            bgp_adj_out_remove (rn, aout, peer, afi, safi);
+            bgp_unlock_node (rn);
+            break;
+          }
+
+      for (ri = rn->info; ri; ri = ri->next)
+        if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT)
+          {
+            struct bgp_clear_node_queue *cnq;
+
+            /* both unlocked in bgp_clear_node_queue_del */
+            bgp_table_lock (bgp_node_table (rn));
+            bgp_lock_node (rn);
+            cnq = XCALLOC (MTYPE_BGP_CLEAR_NODE_QUEUE,
+                           sizeof (struct bgp_clear_node_queue));
+            cnq->rn = rn;
+            cnq->purpose = purpose;
+            work_queue_add (peer->clear_node_queue, cnq);
+            break;
+          }
+    }
+  return;
+}
+
+void
+bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi,
+                 enum bgp_clear_route_type purpose)
+{
+  struct bgp_node *rn;
+  struct bgp_table *table;
+  struct peer *rsclient;
+  struct listnode *node, *nnode;
+
+  if (peer->clear_node_queue == NULL)
+    bgp_clear_node_queue_init (peer);
+  
+  /* bgp_fsm.c keeps sessions in state Clearing, not transitioning to
+   * Idle until it receives a Clearing_Completed event. This protects
+   * against peers which flap faster than we can we clear, which could
+   * lead to:
+   *
+   * a) race with routes from the new session being installed before
+   *    clear_route_node visits the node (to delete the route of that
+   *    peer)
+   * b) resource exhaustion, clear_route_node likely leads to an entry
+   *    on the process_main queue. Fast-flapping could cause that queue
+   *    to grow and grow.
+   */
+
+  /* lock peer in assumption that clear-node-queue will get nodes; if so,
+   * the unlock will happen upon work-queue completion; other wise, the
+   * unlock happens at the end of this function.
+   */
+  if (!peer->clear_node_queue->thread)
+    peer_lock (peer); /* bgp_clear_node_complete */
+  switch (purpose)
+    {
+    case BGP_CLEAR_ROUTE_NORMAL:
+      if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
+        bgp_clear_route_table (peer, afi, safi, NULL, NULL, purpose);
+      else
+        for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
+             rn = bgp_route_next (rn))
+          if ((table = rn->info) != NULL)
+            bgp_clear_route_table (peer, afi, safi, table, NULL, purpose);
+
+      for (ALL_LIST_ELEMENTS (peer->bgp->rsclient, node, nnode, rsclient))
+        if (CHECK_FLAG(rsclient->af_flags[afi][safi],
+                       PEER_FLAG_RSERVER_CLIENT))
+          bgp_clear_route_table (peer, afi, safi, NULL, rsclient, purpose);
+      break;
+
+    case BGP_CLEAR_ROUTE_MY_RSCLIENT:
+      /*
+       * gpz 091009: TBD why don't we have special handling for
+       * SAFI_MPLS_VPN here in the original quagga code?
+       * (and, by extension, for SAFI_ENCAP)
+       */
+      bgp_clear_route_table (peer, afi, safi, NULL, peer, purpose);
+      break;
+
+    default:
+      assert (0);
+      break;
+    }
+
+  /* unlock if no nodes got added to the clear-node-queue. */
+  if (!peer->clear_node_queue->thread)
+    peer_unlock (peer);
+
+}
+  
+void
+bgp_clear_route_all (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
+}
+
+/*
+ * Finish freeing things when exiting
+ */
+static void
+bgp_drain_workqueue_immediate (struct work_queue *wq)
+{
+  if (!wq)
+    return;
+
+  if (!wq->thread)
+    {
+      /*
+       * no thread implies no queued items
+       */
+      assert(!wq->items->count);
+      return;
+    }
+
+   while (wq->items->count)
+     {
+       if (wq->thread)
+         thread_cancel(wq->thread);
+       work_queue_run(wq->thread);
+     }
+}
+
+/*
+ * Special function to process clear node queue when bgpd is exiting
+ * and the thread scheduler is no longer running.
+ */
+void
+bgp_peer_clear_node_queue_drain_immediate(struct peer *peer)
+{
+  if (!peer)
+    return;
+
+  bgp_drain_workqueue_immediate(peer->clear_node_queue);
+}
+
+/*
+ * The work queues are not specific to a BGP instance, but the
+ * items in them refer to BGP instances, so this should be called
+ * before each BGP instance is deleted.
+ */
+void
+bgp_process_queues_drain_immediate(void)
+{
+  bgp_drain_workqueue_immediate(bm->process_main_queue);
+  bgp_drain_workqueue_immediate(bm->process_rsclient_queue);
+}
+
+void
+bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct bgp_table *table;
+  struct bgp_node *rn;
+  struct bgp_adj_in *ain;
+
+  table = peer->bgp->rib[afi][safi];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    for (ain = rn->adj_in; ain ; ain = ain->next)
+      if (ain->peer == peer)
+       {
+          bgp_adj_in_remove (rn, ain);
+          bgp_unlock_node (rn);
+          break;
+       }
+}
+
+void
+bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct bgp_table *table;
+
+  table = peer->bgp->rib[afi][safi];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    {
+      for (ri = rn->info; ri; ri = ri->next)
+       if (ri->peer == peer)
+         {
+           if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
+             bgp_rib_remove (rn, ri, peer, afi, safi);
+           break;
+         }
+    }
+}
+
+static void
+bgp_cleanup_table(struct bgp_table *table, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct bgp_info *next;
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    for (ri = rn->info; ri; ri = next)
+      {
+        next = ri->next;
+        if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)
+            && ri->type == ZEBRA_ROUTE_BGP
+            && ri->sub_type == BGP_ROUTE_NORMAL)
+          bgp_zebra_withdraw (&rn->p, ri, safi);
+      }
+}
+
+/* Delete all kernel routes. */
+void
+bgp_cleanup_routes (void)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+  afi_t afi;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    {
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+       {
+         struct bgp_node *rn;
+
+         bgp_cleanup_table(bgp->rib[afi][SAFI_UNICAST], SAFI_UNICAST);
+
+         /*
+          * VPN and ENCAP tables are two-level (RD is top level)
+          */
+         for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn;
+               rn = bgp_route_next (rn))
+           {
+             if (rn->info)
+                {
+                 bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_MPLS_VPN);
+                 bgp_table_finish ((struct bgp_table **)&(rn->info));
+                 rn->info = NULL;
+                 bgp_unlock_node(rn);
+                }
+           }
+
+         for (rn = bgp_table_top(bgp->rib[afi][SAFI_ENCAP]); rn;
+               rn = bgp_route_next (rn))
+           {
+             if (rn->info)
+               {
+                 bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_ENCAP);
+                 bgp_table_finish ((struct bgp_table **)&(rn->info));
+                 rn->info = NULL;
+                 bgp_unlock_node(rn);
+               }
+           }
+       }
+    }
+}
+
+void
+bgp_reset (void)
+{
+  vty_reset ();
+  bgp_zclient_reset ();
+  access_list_reset ();
+  prefix_list_reset ();
+}
+
+/* Parse NLRI stream.  Withdraw NLRI is recognized by NULL attr
+   value. */
+int
+bgp_nlri_parse_ip (struct peer *peer, struct attr *attr,
+                   struct bgp_nlri *packet)
+{
+  u_char *pnt;
+  u_char *lim;
+  struct prefix p;
+  int psize;
+  int ret;
+
+  /* Check peer status. */
+  if (peer->status != Established)
+    return 0;
+  
+  pnt = packet->nlri;
+  lim = pnt + packet->length;
+
+  /* RFC4771 6.3 The NLRI field in the UPDATE message is checked for
+     syntactic validity.  If the field is syntactically incorrect,
+     then the Error Subcode is set to Invalid Network Field. */
+  for (; pnt < lim; pnt += psize)
+    {
+      /* Clear prefix structure. */
+      memset (&p, 0, sizeof (struct prefix));
+
+      /* Fetch prefix length. */
+      p.prefixlen = *pnt++;
+      /* afi/safi validity already verified by caller, bgp_update_receive */
+      p.family = afi2family (packet->afi);
+      
+      /* Prefix length check. */
+      if (p.prefixlen > prefix_blen (&p) * 8)
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error"
+                    " (wrong prefix length %u for afi %u)",
+                    peer->host, p.prefixlen, packet->afi);
+          return -1;
+        }
+      
+      /* Packet size overflow check. */
+      psize = PSIZE (p.prefixlen);
+
+      /* When packet overflow occur return immediately. */
+      if (pnt + psize > lim)
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error"
+                    " (prefix length %u overflows packet)",
+                    peer->host, p.prefixlen);
+          return -1;
+        }
+      
+      /* Defensive coding, double-check the psize fits in a struct prefix */  
+      if (psize > (ssize_t) sizeof(p.u))
+        {
+          plog_err (peer->log,
+                    "%s [Error] Update packet error"
+                    " (prefix length %u too large for prefix storage %zu!?!!",
+                    peer->host, p.prefixlen, sizeof(p.u));
+          return -1;
+        }
+
+      /* Fetch prefix from NLRI packet. */
+      memcpy (&p.u.prefix, pnt, psize);
+
+      /* Check address. */
+      if (packet->afi == AFI_IP && packet->safi == SAFI_UNICAST)
+       {
+         if (IN_CLASSD (ntohl (p.u.prefix4.s_addr)))
+           {
+            /* 
+             * From RFC4271 Section 6.3: 
+             * 
+             * If a prefix in the NLRI field is semantically incorrect
+             * (e.g., an unexpected multicast IP address), an error SHOULD
+             * be logged locally, and the prefix SHOULD be ignored.
+             */
+             zlog (peer->log, LOG_ERR, 
+                   "%s: IPv4 unicast NLRI is multicast address %s, ignoring",
+                   peer->host, inet_ntoa (p.u.prefix4));
+             continue;
+           }
+       }
+
+      /* Check address. */
+      if (packet->afi == AFI_IP6 && packet->safi == SAFI_UNICAST)
+       {
+         if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6))
+           {
+             char buf[BUFSIZ];
+
+             zlog (peer->log, LOG_ERR, 
+                   "%s: IPv6 unicast NLRI is link-local address %s, ignoring",
+                   peer->host,
+                   inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ));
+             continue;
+           }
+         if (IN6_IS_ADDR_MULTICAST (&p.u.prefix6))
+           {
+             char buf[BUFSIZ];
+
+             zlog (peer->log, LOG_ERR, 
+                   "%s: IPv6 unicast NLRI is multicast address %s, ignoring",
+                   peer->host,
+                   inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ));
+             continue;
+           }
+        }
+
+      /* Normal process. */
+      if (attr)
+       ret = bgp_update (peer, &p, attr, packet->afi, packet->safi, 
+                         ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0);
+      else
+       ret = bgp_withdraw (peer, &p, attr, packet->afi, packet->safi, 
+                           ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL);
+
+      /* Address family configuration mismatch or maximum-prefix count
+         overflow. */
+      if (ret < 0)
+       return -1;
+    }
+
+  /* Packet length consistency check. */
+  if (pnt != lim)
+    {
+      plog_err (peer->log,
+                "%s [Error] Update packet error"
+                " (prefix length mismatch with total length)",
+                peer->host);
+      return -1;
+    }
+  
+  return 0;
+}
+
+static struct bgp_static *
+bgp_static_new (void)
+{
+  return XCALLOC (MTYPE_BGP_STATIC, sizeof (struct bgp_static));
+}
+
+static void
+bgp_static_free (struct bgp_static *bgp_static)
+{
+  if (bgp_static->rmap.name)
+    free (bgp_static->rmap.name);
+  XFREE (MTYPE_BGP_STATIC, bgp_static);
+}
+
+static void
+bgp_static_withdraw_rsclient (struct bgp *bgp, struct peer *rsclient,
+        struct prefix *p, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+
+  rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL);
+
+  /* Check selected route and self inserted route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self
+       && ri->type == ZEBRA_ROUTE_BGP
+       && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  /* Withdraw static BGP route from routing table. */
+  if (ri)
+    {
+      bgp_info_delete (rn, ri);
+      bgp_process (bgp, rn, afi, safi);
+    }
+
+  /* Unlock bgp_node_lookup. */
+  bgp_unlock_node (rn);
+}
+
+static void
+bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p,
+                            struct bgp_static *bgp_static,
+                            afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct bgp_info *new;
+  struct bgp_info info;
+  struct attr *attr_new;
+  struct attr attr;
+  struct attr new_attr;
+  struct attr_extra new_extra;
+  struct bgp *bgp;
+  int ret;
+  char buf[SU_ADDRSTRLEN];
+
+  bgp = rsclient->bgp;
+
+  assert (bgp_static);
+  if (!bgp_static)
+    return;
+
+  rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL);
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+
+  attr.nexthop = bgp_static->igpnexthop;
+  attr.med = bgp_static->igpmetric;
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+  
+  if (bgp_static->atomic)
+    attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+  
+  /* Apply network route-map for export to this rsclient. */
+  if (bgp_static->rmap.name)
+    {
+      struct attr attr_tmp = attr;
+      info.peer = rsclient;
+      info.attr = &attr_tmp;
+      
+      SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT);
+      SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_NETWORK);
+
+      ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info);
+
+      rsclient->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          /* Free uninterned attribute. */
+          bgp_attr_flush (&attr_tmp);
+
+          /* Unintern original. */
+          aspath_unintern (&attr.aspath);
+          bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi);
+          bgp_attr_extra_free (&attr);
+          
+          return;
+        }
+      attr_new = bgp_attr_intern (&attr_tmp);
+    }
+  else
+    attr_new = bgp_attr_intern (&attr);
+
+  new_attr.extra = &new_extra;
+  bgp_attr_dup(&new_attr, attr_new);
+  
+  SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
+
+  if (bgp_import_modifier (rsclient, bgp->peer_self, p, &new_attr, afi, safi) 
+        == RMAP_DENY)
+    {
+      /* This BGP update is filtered.  Log the reason then update BGP entry.  */
+      if (BGP_DEBUG (update, UPDATE_IN))
+              zlog (rsclient->log, LOG_DEBUG,
+              "Static UPDATE about %s/%d -- DENIED for RS-client %s due to: import-policy",
+              inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+              p->prefixlen, rsclient->host);
+
+      bgp->peer_self->rmap_type = 0;
+
+      bgp_attr_unintern (&attr_new);
+      aspath_unintern (&attr.aspath);
+      bgp_attr_extra_free (&attr);
+
+      bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi);
+      
+      return;
+    }
+
+  bgp->peer_self->rmap_type = 0;
+
+  bgp_attr_unintern (&attr_new);
+  attr_new = bgp_attr_intern (&new_attr);
+
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
+            && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  if (ri)
+       {
+      if (attrhash_cmp (ri->attr, attr_new) &&
+         !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+        {
+          bgp_unlock_node (rn);
+          bgp_attr_unintern (&attr_new);
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+          return;
+       }
+      else
+        {
+          /* The attribute is changed. */
+          bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+
+          /* Rewrite BGP route information. */
+         if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+           bgp_info_restore(rn, ri);
+          bgp_attr_unintern (&ri->attr);
+          ri->attr = attr_new;
+          ri->uptime = bgp_clock ();
+
+         /* Nexthop reachability check. */
+         if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
+           {
+             if (bgp_ensure_nexthop (ri, NULL, 0))
+               bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+             else
+               {
+                 if (BGP_DEBUG(nht, NHT))
+                   {
+                     char buf1[INET6_ADDRSTRLEN];
+                     inet_ntop(AF_INET, (const void *)&attr_new->nexthop,
+                               buf1, INET6_ADDRSTRLEN);
+                     zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+                   }
+                 bgp_info_unset_flag (rn, ri, BGP_INFO_VALID);
+               }
+           }
+          /* Process change. */
+          bgp_process (bgp, rn, afi, safi);
+          bgp_unlock_node (rn);
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+          return;
+        }
+    }
+
+  /* Make new BGP info. */
+  new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self,
+                 attr_new, rn);
+  /* Nexthop reachability check. */
+  if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
+    {
+      if (bgp_ensure_nexthop (new, NULL, 0))
+       bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+      else
+       {
+         if (BGP_DEBUG(nht, NHT))
+           {
+             char buf1[INET6_ADDRSTRLEN];
+             inet_ntop(AF_INET, (const void *)&attr_new->nexthop,
+                       buf1, INET6_ADDRSTRLEN);
+             zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+           }
+         bgp_info_unset_flag (rn, new, BGP_INFO_VALID);
+       }
+    }
+  else
+    bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+
+  /* Register new BGP information. */
+  bgp_info_add (rn, new);
+  
+  /* route_node_get lock */
+  bgp_unlock_node (rn);
+  
+  /* Process change. */
+  bgp_process (bgp, rn, afi, safi);
+
+  /* Unintern original. */
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+static void
+bgp_static_update_main (struct bgp *bgp, struct prefix *p,
+                       struct bgp_static *bgp_static, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct bgp_info *new;
+  struct bgp_info info;
+  struct attr attr;
+  struct attr *attr_new;
+  int ret;
+
+  assert (bgp_static);
+  if (!bgp_static)
+    return;
+
+  rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, NULL);
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+  
+  attr.nexthop = bgp_static->igpnexthop;
+  attr.med = bgp_static->igpmetric;
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+
+  if (bgp_static->atomic)
+    attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+
+  /* Apply route-map. */
+  if (bgp_static->rmap.name)
+    {
+      struct attr attr_tmp = attr;
+      info.peer = bgp->peer_self;
+      info.attr = &attr_tmp;
+
+      SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
+
+      ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info);
+
+      bgp->peer_self->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+       {    
+         /* Free uninterned attribute. */
+         bgp_attr_flush (&attr_tmp);
+
+         /* Unintern original. */
+         aspath_unintern (&attr.aspath);
+         bgp_attr_extra_free (&attr);
+         bgp_static_withdraw (bgp, p, afi, safi);
+         return;
+       }
+      attr_new = bgp_attr_intern (&attr_tmp);
+    }
+  else
+    attr_new = bgp_attr_intern (&attr);
+
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
+       && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  if (ri)
+    {
+      if (attrhash_cmp (ri->attr, attr_new) &&
+         !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+       {
+         bgp_unlock_node (rn);
+         bgp_attr_unintern (&attr_new);
+         aspath_unintern (&attr.aspath);
+         bgp_attr_extra_free (&attr);
+         return;
+       }
+      else
+       {
+         /* The attribute is changed. */
+         bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+
+         /* Rewrite BGP route information. */
+         if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+           bgp_info_restore(rn, ri);
+         else
+           bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+         bgp_attr_unintern (&ri->attr);
+         ri->attr = attr_new;
+         ri->uptime = bgp_clock ();
+
+         /* Nexthop reachability check. */
+         if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
+           {
+             if (bgp_ensure_nexthop (ri, NULL, 0))
+               bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
+             else
+               {
+                 if (BGP_DEBUG(nht, NHT))
+                   {
+                     char buf1[INET6_ADDRSTRLEN];
+                     inet_ntop(AF_INET, (const void *)&attr_new->nexthop,
+                               buf1, INET6_ADDRSTRLEN);
+                     zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+                   }
+                 bgp_info_unset_flag (rn, ri, BGP_INFO_VALID);
+               }
+           }
+         /* Process change. */
+         bgp_aggregate_increment (bgp, p, ri, afi, safi);
+         bgp_process (bgp, rn, afi, safi);
+         bgp_unlock_node (rn);
+         aspath_unintern (&attr.aspath);
+         bgp_attr_extra_free (&attr);
+         return;
+       }
+    }
+
+  /* Make new BGP info. */
+  new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self, attr_new,
+                 rn);
+  /* Nexthop reachability check. */
+  if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
+    {
+      if (bgp_ensure_nexthop (new, NULL, 0))
+       bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+      else
+       {
+         if (BGP_DEBUG(nht, NHT))
+           {
+             char buf1[INET6_ADDRSTRLEN];
+             inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1,
+                       INET6_ADDRSTRLEN);
+             zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1);
+           }
+         bgp_info_unset_flag (rn, new, BGP_INFO_VALID);
+       }
+    }
+  else
+    bgp_info_set_flag (rn, new, BGP_INFO_VALID);
+
+  /* Aggregate address increment. */
+  bgp_aggregate_increment (bgp, p, new, afi, safi);
+  
+  /* Register new BGP information. */
+  bgp_info_add (rn, new);
+  
+  /* route_node_get lock */
+  bgp_unlock_node (rn);
+  
+  /* Process change. */
+  bgp_process (bgp, rn, afi, safi);
+
+  /* Unintern original. */
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+void
+bgp_static_update (struct bgp *bgp, struct prefix *p,
+                  struct bgp_static *bgp_static, afi_t afi, safi_t safi)
+{
+  struct peer *rsclient;
+  struct listnode *node, *nnode;
+
+  bgp_static_update_main (bgp, p, bgp_static, afi, safi);
+
+  for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
+    {
+      if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+        bgp_static_update_rsclient (rsclient, p, bgp_static, afi, safi);
+    }
+}
+
+void
+bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi,
+                    safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+
+  /* Make new BGP info. */
+  rn = bgp_node_get (bgp->rib[afi][safi], p);
+
+  /* Check selected route and self inserted route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self 
+       && ri->type == ZEBRA_ROUTE_BGP
+       && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  /* Withdraw static BGP route from routing table. */
+  if (ri)
+    {
+      bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+      bgp_unlink_nexthop(ri);
+      bgp_info_delete (rn, ri);
+      bgp_process (bgp, rn, afi, safi);
+    }
+
+  /* Unlock bgp_node_lookup. */
+  bgp_unlock_node (rn);
+}
+
+void
+bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi)
+{
+  struct bgp_static *bgp_static;
+  struct bgp *bgp;
+  struct bgp_node *rn;
+  struct prefix *p;
+
+  bgp = rsclient->bgp;
+
+  for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
+    if ((bgp_static = rn->info) != NULL)
+      {
+        p = &rn->p;
+
+        bgp_static_update_rsclient (rsclient, p, bgp_static,
+                afi, safi);
+      }
+}
+
+/*
+ * Used for SAFI_MPLS_VPN and SAFI_ENCAP
+ */
+static void
+bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi,
+                          safi_t safi, struct prefix_rd *prd, u_char *tag)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+
+  rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+
+  /* Check selected route and self inserted route. */
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self 
+       && ri->type == ZEBRA_ROUTE_BGP
+       && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  /* Withdraw static BGP route from routing table. */
+  if (ri)
+    {
+      bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+      bgp_info_delete (rn, ri);
+      bgp_process (bgp, rn, afi, safi);
+    }
+
+  /* Unlock bgp_node_lookup. */
+  bgp_unlock_node (rn);
+}
+
+static void
+bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
+                        struct bgp_static *bgp_static, afi_t afi, safi_t safi)
+{
+  struct bgp_node *rn;
+  struct bgp_info *new;
+  struct attr *attr_new;
+  struct attr attr = { 0 };
+  struct bgp_info *ri;
+
+  assert (bgp_static);
+
+  rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, &bgp_static->prd);
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+
+  attr.nexthop = bgp_static->igpnexthop;
+  attr.med = bgp_static->igpmetric;
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+
+  /* Apply route-map. */
+  if (bgp_static->rmap.name)
+    {
+      struct attr attr_tmp = attr;
+      struct bgp_info info;
+      int ret;
+
+      info.peer = bgp->peer_self;
+      info.attr = &attr_tmp;
+
+      SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
+
+      ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info);
+
+      bgp->peer_self->rmap_type = 0;
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          /* Free uninterned attribute. */
+          bgp_attr_flush (&attr_tmp);
+
+          /* Unintern original. */
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+          bgp_static_withdraw_safi (bgp, p, afi, safi, &bgp_static->prd,
+                                    bgp_static->tag);
+          return;
+        }
+
+      attr_new = bgp_attr_intern (&attr_tmp);
+    }
+  else
+    {
+      attr_new = bgp_attr_intern (&attr);
+    }
+
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
+        && ri->sub_type == BGP_ROUTE_STATIC)
+      break;
+
+  if (ri)
+    {
+      if (attrhash_cmp (ri->attr, attr_new) &&
+          !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+        {
+          bgp_unlock_node (rn);
+          bgp_attr_unintern (&attr_new);
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+          return;
+        }
+      else
+        {
+          /* The attribute is changed. */
+          bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+
+          /* Rewrite BGP route information. */
+          if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+            bgp_info_restore(rn, ri);
+          else
+            bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+          bgp_attr_unintern (&ri->attr);
+          ri->attr = attr_new;
+          ri->uptime = bgp_clock ();
+
+          /* Process change. */
+          bgp_aggregate_increment (bgp, p, ri, afi, safi);
+          bgp_process (bgp, rn, afi, safi);
+          bgp_unlock_node (rn);
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+          return;
+        }
+    }
+
+
+  /* Make new BGP info. */
+  new = info_make (ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self,
+                  attr_new, rn);
+  SET_FLAG (new->flags, BGP_INFO_VALID);
+  new->extra = bgp_info_extra_new();
+  memcpy (new->extra->tag, bgp_static->tag, 3);
+
+  /* Aggregate address increment. */
+  bgp_aggregate_increment (bgp, p, new, afi, safi);
+
+  /* Register new BGP information. */
+  bgp_info_add (rn, new);
+
+  /* route_node_get lock */
+  bgp_unlock_node (rn);
+
+  /* Process change. */
+  bgp_process (bgp, rn, afi, safi);
+
+  /* Unintern original. */
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+/* Configure static BGP network.  When user don't run zebra, static
+   route should be installed as valid.  */
+static int
+bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str, 
+                afi_t afi, safi_t safi, const char *rmap, int backdoor)
+{
+  int ret;
+  struct prefix p;
+  struct bgp_static *bgp_static;
+  struct bgp_node *rn;
+  u_char need_update = 0;
+
+  /* Convert IP prefix string to struct prefix. */
+  ret = str2prefix (ip_str, &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6))
+    {
+      vty_out (vty, "%% Malformed prefix (link-local address)%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  apply_mask (&p);
+
+  /* Set BGP static route configuration. */
+  rn = bgp_node_get (bgp->route[afi][safi], &p);
+
+  if (rn->info)
+    {
+      /* Configuration change. */
+      bgp_static = rn->info;
+
+      /* Check previous routes are installed into BGP.  */
+      if (bgp_static->valid && bgp_static->backdoor != backdoor)
+        need_update = 1;
+      
+      bgp_static->backdoor = backdoor;
+      
+      if (rmap)
+       {
+         if (bgp_static->rmap.name)
+           free (bgp_static->rmap.name);
+         bgp_static->rmap.name = strdup (rmap);
+         bgp_static->rmap.map = route_map_lookup_by_name (rmap);
+       }
+      else
+       {
+         if (bgp_static->rmap.name)
+           free (bgp_static->rmap.name);
+         bgp_static->rmap.name = NULL;
+         bgp_static->rmap.map = NULL;
+         bgp_static->valid = 0;
+       }
+      bgp_unlock_node (rn);
+    }
+  else
+    {
+      /* New configuration. */
+      bgp_static = bgp_static_new ();
+      bgp_static->backdoor = backdoor;
+      bgp_static->valid = 0;
+      bgp_static->igpmetric = 0;
+      bgp_static->igpnexthop.s_addr = 0;
+      
+      if (rmap)
+       {
+         if (bgp_static->rmap.name)
+           free (bgp_static->rmap.name);
+         bgp_static->rmap.name = strdup (rmap);
+         bgp_static->rmap.map = route_map_lookup_by_name (rmap);
+       }
+      rn->info = bgp_static;
+    }
+
+  bgp_static->valid = 1;
+  if (need_update)
+    bgp_static_withdraw (bgp, &p, afi, safi);
+
+  if (! bgp_static->backdoor)
+    bgp_static_update (bgp, &p, bgp_static, afi, safi);
+
+  return CMD_SUCCESS;
+}
+
+/* Configure static BGP network. */
+static int
+bgp_static_unset (struct vty *vty, struct bgp *bgp, const char *ip_str,
+                 afi_t afi, safi_t safi)
+{
+  int ret;
+  struct prefix p;
+  struct bgp_static *bgp_static;
+  struct bgp_node *rn;
+
+  /* Convert IP prefix string to struct prefix. */
+  ret = str2prefix (ip_str, &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6))
+    {
+      vty_out (vty, "%% Malformed prefix (link-local address)%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  apply_mask (&p);
+
+  rn = bgp_node_lookup (bgp->route[afi][safi], &p);
+  if (! rn)
+    {
+      vty_out (vty, "%% Can't find specified static route configuration.%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_static = rn->info;
+  
+  /* Update BGP RIB. */
+  if (! bgp_static->backdoor)
+    bgp_static_withdraw (bgp, &p, afi, safi);
+
+  /* Clear configuration. */
+  bgp_static_free (bgp_static);
+  rn->info = NULL;
+  bgp_unlock_node (rn);
+  bgp_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+/* Called from bgp_delete().  Delete all static routes from the BGP
+   instance. */
+void
+bgp_static_delete (struct bgp *bgp)
+{
+  afi_t afi;
+  safi_t safi;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp_table *table;
+  struct bgp_static *bgp_static;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
+       if (rn->info != NULL)
+         {      
+           if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+             {
+               table = rn->info;
+
+               for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+                 {
+                   bgp_static = rn->info;
+                   bgp_static_withdraw_safi (bgp, &rm->p,
+                                              AFI_IP, safi,
+                                              (struct prefix_rd *)&rn->p,
+                                              bgp_static->tag);
+                   bgp_static_free (bgp_static);
+                   rn->info = NULL;
+                   bgp_unlock_node (rn);
+                 }
+             }
+           else
+             {
+               bgp_static = rn->info;
+               bgp_static_withdraw (bgp, &rn->p, afi, safi);
+               bgp_static_free (bgp_static);
+               rn->info = NULL;
+               bgp_unlock_node (rn);
+             }
+         }
+}
+
+/*
+ * gpz 110624
+ * Currently this is used to set static routes for VPN and ENCAP.
+ * I think it can probably be factored with bgp_static_set.
+ */
+int
+bgp_static_set_safi (safi_t safi, struct vty *vty, const char *ip_str,
+                     const char *rd_str, const char *tag_str,
+                     const char *rmap_str)
+{
+  int ret;
+  struct prefix p;
+  struct prefix_rd prd;
+  struct bgp *bgp;
+  struct bgp_node *prn;
+  struct bgp_node *rn;
+  struct bgp_table *table;
+  struct bgp_static *bgp_static;
+  u_char tag[3];
+
+  bgp = vty->index;
+
+  ret = str2prefix (ip_str, &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask (&p);
+
+  ret = str2prefix_rd (rd_str, &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2tag (tag_str, tag);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  prn = bgp_node_get (bgp->route[AFI_IP][safi],
+                       (struct prefix *)&prd);
+  if (prn->info == NULL)
+    prn->info = bgp_table_init (AFI_IP, safi);
+  else
+    bgp_unlock_node (prn);
+  table = prn->info;
+
+  rn = bgp_node_get (table, &p);
+
+  if (rn->info)
+    {
+      vty_out (vty, "%% Same network configuration exists%s", VTY_NEWLINE);
+      bgp_unlock_node (rn);
+    }
+  else
+    {
+      /* New configuration. */
+      bgp_static = bgp_static_new ();
+      bgp_static->backdoor = 0;
+      bgp_static->valid = 0;
+      bgp_static->igpmetric = 0;
+      bgp_static->igpnexthop.s_addr = 0;
+      memcpy(bgp_static->tag, tag, 3);
+      bgp_static->prd = prd;
+
+      if (rmap_str)
+       {
+         if (bgp_static->rmap.name)
+           free (bgp_static->rmap.name);
+         bgp_static->rmap.name = strdup (rmap_str);
+         bgp_static->rmap.map = route_map_lookup_by_name (rmap_str);
+       }
+      rn->info = bgp_static;
+
+      bgp_static->valid = 1;
+      bgp_static_update_safi (bgp, &p, bgp_static, AFI_IP, safi);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Configure static BGP network. */
+int
+bgp_static_unset_safi(safi_t safi, struct vty *vty, const char *ip_str,
+                      const char *rd_str, const char *tag_str)
+{
+  int ret;
+  struct bgp *bgp;
+  struct prefix p;
+  struct prefix_rd prd;
+  struct bgp_node *prn;
+  struct bgp_node *rn;
+  struct bgp_table *table;
+  struct bgp_static *bgp_static;
+  u_char tag[3];
+
+  bgp = vty->index;
+
+  /* Convert IP prefix string to struct prefix. */
+  ret = str2prefix (ip_str, &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask (&p);
+
+  ret = str2prefix_rd (rd_str, &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2tag (tag_str, tag);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  prn = bgp_node_get (bgp->route[AFI_IP][safi],
+                       (struct prefix *)&prd);
+  if (prn->info == NULL)
+    prn->info = bgp_table_init (AFI_IP, safi);
+  else
+    bgp_unlock_node (prn);
+  table = prn->info;
+
+  rn = bgp_node_lookup (table, &p);
+
+  if (rn)
+    {
+      bgp_static_withdraw_safi (bgp, &p, AFI_IP, safi, &prd, tag);
+
+      bgp_static = rn->info;
+      bgp_static_free (bgp_static);
+      rn->info = NULL;
+      bgp_unlock_node (rn);
+      bgp_unlock_node (rn);
+    }
+  else
+    vty_out (vty, "%% Can't find the route%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_network,
+       bgp_network_cmd,
+       "network A.B.C.D/M",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_static_set (vty, vty->index, argv[0],
+                        AFI_IP, bgp_node_safi (vty), NULL, 0);
+}
+
+DEFUN (bgp_network_route_map,
+       bgp_network_route_map_cmd,
+       "network A.B.C.D/M route-map WORD",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+{
+  return bgp_static_set (vty, vty->index, argv[0],
+                        AFI_IP, bgp_node_safi (vty), argv[1], 0);
+}
+
+DEFUN (bgp_network_backdoor,
+       bgp_network_backdoor_cmd,
+       "network A.B.C.D/M backdoor",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify a BGP backdoor route\n")
+{
+  return bgp_static_set (vty, vty->index, argv[0], AFI_IP, SAFI_UNICAST,
+                         NULL, 1);
+}
+
+DEFUN (bgp_network_mask,
+       bgp_network_mask_cmd,
+       "network A.B.C.D mask A.B.C.D",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+  
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str,
+                        AFI_IP, bgp_node_safi (vty), NULL, 0);
+}
+
+DEFUN (bgp_network_mask_route_map,
+       bgp_network_mask_route_map_cmd,
+       "network A.B.C.D mask A.B.C.D route-map WORD",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+  
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str,
+                        AFI_IP, bgp_node_safi (vty), argv[2], 0);
+}
+
+DEFUN (bgp_network_mask_backdoor,
+       bgp_network_mask_backdoor_cmd,
+       "network A.B.C.D mask A.B.C.D backdoor",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Specify a BGP backdoor route\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+  
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str, AFI_IP, SAFI_UNICAST,
+                         NULL, 1);
+}
+
+DEFUN (bgp_network_mask_natural,
+       bgp_network_mask_natural_cmd,
+       "network A.B.C.D",
+       "Specify a network to announce via BGP\n"
+       "Network number\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], NULL, prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str,
+                        AFI_IP, bgp_node_safi (vty), NULL, 0);
+}
+
+DEFUN (bgp_network_mask_natural_route_map,
+       bgp_network_mask_natural_route_map_cmd,
+       "network A.B.C.D route-map WORD",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], NULL, prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str,
+                        AFI_IP, bgp_node_safi (vty), argv[1], 0);
+}
+
+DEFUN (bgp_network_mask_natural_backdoor,
+       bgp_network_mask_natural_backdoor_cmd,
+       "network A.B.C.D backdoor",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Specify a BGP backdoor route\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], NULL, prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_set (vty, vty->index, prefix_str, AFI_IP, SAFI_UNICAST,
+                         NULL, 1);
+}
+
+DEFUN (no_bgp_network,
+       no_bgp_network_cmd,
+       "no network A.B.C.D/M",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_static_unset (vty, vty->index, argv[0], AFI_IP, 
+                          bgp_node_safi (vty));
+}
+
+ALIAS (no_bgp_network,
+       no_bgp_network_route_map_cmd,
+       "no network A.B.C.D/M route-map WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+
+ALIAS (no_bgp_network,
+       no_bgp_network_backdoor_cmd,
+       "no network A.B.C.D/M backdoor",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify a BGP backdoor route\n")
+
+DEFUN (no_bgp_network_mask,
+       no_bgp_network_mask_cmd,
+       "no network A.B.C.D mask A.B.C.D",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP, 
+                          bgp_node_safi (vty));
+}
+
+ALIAS (no_bgp_network_mask,
+       no_bgp_network_mask_route_map_cmd,
+       "no network A.B.C.D mask A.B.C.D route-map WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+
+ALIAS (no_bgp_network_mask,
+       no_bgp_network_mask_backdoor_cmd,
+       "no network A.B.C.D mask A.B.C.D backdoor",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Specify a BGP backdoor route\n")
+
+DEFUN (no_bgp_network_mask_natural,
+       no_bgp_network_mask_natural_cmd,
+       "no network A.B.C.D",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], NULL, prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP, 
+                          bgp_node_safi (vty));
+}
+
+ALIAS (no_bgp_network_mask_natural,
+       no_bgp_network_mask_natural_route_map_cmd,
+       "no network A.B.C.D route-map WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+
+ALIAS (no_bgp_network_mask_natural,
+       no_bgp_network_mask_natural_backdoor_cmd,
+       "no network A.B.C.D backdoor",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Specify a BGP backdoor route\n")
+
+DEFUN (ipv6_bgp_network,
+       ipv6_bgp_network_cmd,
+       "network X:X::X:X/M",
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n")
+{
+  return bgp_static_set (vty, vty->index, argv[0], AFI_IP6, bgp_node_safi(vty),
+                         NULL, 0);
+}
+
+DEFUN (ipv6_bgp_network_route_map,
+       ipv6_bgp_network_route_map_cmd,
+       "network X:X::X:X/M route-map WORD",
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+{
+  return bgp_static_set (vty, vty->index, argv[0], AFI_IP6,
+                        bgp_node_safi (vty), argv[1], 0);
+}
+
+DEFUN (no_ipv6_bgp_network,
+       no_ipv6_bgp_network_cmd,
+       "no network X:X::X:X/M",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n")
+{
+  return bgp_static_unset (vty, vty->index, argv[0], AFI_IP6, bgp_node_safi(vty));
+}
+
+ALIAS (no_ipv6_bgp_network,
+       no_ipv6_bgp_network_route_map_cmd,
+       "no network X:X::X:X/M route-map WORD",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n"
+       "Route-map to modify the attributes\n"
+       "Name of the route map\n")
+
+ALIAS (ipv6_bgp_network,
+       old_ipv6_bgp_network_cmd,
+       "ipv6 bgp network X:X::X:X/M",
+       IPV6_STR
+       BGP_STR
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+
+ALIAS (no_ipv6_bgp_network,
+       old_no_ipv6_bgp_network_cmd,
+       "no ipv6 bgp network X:X::X:X/M",
+       NO_STR
+       IPV6_STR
+       BGP_STR
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+
+/* stubs for removed AS-Pathlimit commands, kept for config compatibility */
+ALIAS_DEPRECATED (bgp_network,
+       bgp_network_ttl_cmd,
+       "network A.B.C.D/M pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (bgp_network_backdoor,
+       bgp_network_backdoor_ttl_cmd,
+       "network A.B.C.D/M backdoor pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (bgp_network_mask,
+       bgp_network_mask_ttl_cmd,
+       "network A.B.C.D mask A.B.C.D pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (bgp_network_mask_backdoor,
+       bgp_network_mask_backdoor_ttl_cmd,
+       "network A.B.C.D mask A.B.C.D backdoor pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (bgp_network_mask_natural,
+       bgp_network_mask_natural_ttl_cmd,
+       "network A.B.C.D pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (bgp_network_mask_natural_backdoor,
+       bgp_network_mask_natural_backdoor_ttl_cmd,
+       "network A.B.C.D backdoor pathlimit <1-255>",
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network,
+       no_bgp_network_ttl_cmd,
+       "no network A.B.C.D/M pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network,
+       no_bgp_network_backdoor_ttl_cmd,
+       "no network A.B.C.D/M backdoor pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network,
+       no_bgp_network_mask_ttl_cmd,
+       "no network A.B.C.D mask A.B.C.D pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network_mask,
+       no_bgp_network_mask_backdoor_ttl_cmd,
+       "no network A.B.C.D mask A.B.C.D  backdoor pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Network mask\n"
+       "Network mask\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network_mask_natural,
+       no_bgp_network_mask_natural_ttl_cmd,
+       "no network A.B.C.D pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_bgp_network_mask_natural,
+       no_bgp_network_mask_natural_backdoor_ttl_cmd,
+       "no network A.B.C.D backdoor pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "Network number\n"
+       "Specify a BGP backdoor route\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (ipv6_bgp_network,
+       ipv6_bgp_network_ttl_cmd,
+       "network X:X::X:X/M pathlimit <0-255>",
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+ALIAS_DEPRECATED (no_ipv6_bgp_network,
+       no_ipv6_bgp_network_ttl_cmd,
+       "no network X:X::X:X/M pathlimit <0-255>",
+       NO_STR
+       "Specify a network to announce via BGP\n"
+       "IPv6 prefix <network>/<length>\n"
+       "AS-Path hopcount limit attribute\n"
+       "AS-Pathlimit TTL, in number of AS-Path hops\n")
+
+/* Aggreagete address:
+
+  advertise-map  Set condition to advertise attribute
+  as-set         Generate AS set path information
+  attribute-map  Set attributes of aggregate
+  route-map      Set parameters of aggregate
+  summary-only   Filter more specific routes from updates
+  suppress-map   Conditionally filter more specific routes from updates
+  <cr>
+ */
+struct bgp_aggregate
+{
+  /* Summary-only flag. */
+  u_char summary_only;
+
+  /* AS set generation. */
+  u_char as_set;
+
+  /* Route-map for aggregated route. */
+  struct route_map *map;
+
+  /* Suppress-count. */
+  unsigned long count;
+
+  /* SAFI configuration. */
+  safi_t safi;
+};
+
+static struct bgp_aggregate *
+bgp_aggregate_new (void)
+{
+  return XCALLOC (MTYPE_BGP_AGGREGATE, sizeof (struct bgp_aggregate));
+}
+
+static void
+bgp_aggregate_free (struct bgp_aggregate *aggregate)
+{
+  XFREE (MTYPE_BGP_AGGREGATE, aggregate);
+}     
+
+/* Update an aggregate as routes are added/removed from the BGP table */
+static void
+bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew,
+                    afi_t afi, safi_t safi, struct bgp_info *del, 
+                    struct bgp_aggregate *aggregate)
+{
+  struct bgp_table *table;
+  struct bgp_node *top;
+  struct bgp_node *rn;
+  u_char origin;
+  struct aspath *aspath = NULL;
+  struct aspath *asmerge = NULL;
+  struct community *community = NULL;
+  struct community *commerge = NULL;
+  struct bgp_info *ri;
+  struct bgp_info *new;
+  int first = 1;
+  unsigned long match = 0;
+  u_char atomic_aggregate = 0;
+
+  /* ORIGIN attribute: If at least one route among routes that are
+     aggregated has ORIGIN with the value INCOMPLETE, then the
+     aggregated route must have the ORIGIN attribute with the value
+     INCOMPLETE. Otherwise, if at least one route among routes that
+     are aggregated has ORIGIN with the value EGP, then the aggregated
+     route must have the origin attribute with the value EGP. In all
+     other case the value of the ORIGIN attribute of the aggregated
+     route is INTERNAL. */
+  origin = BGP_ORIGIN_IGP;
+
+  table = bgp->rib[afi][safi];
+
+  top = bgp_node_get (table, p);
+  for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top))
+    if (rn->p.prefixlen > p->prefixlen)
+      {
+       match = 0;
+
+       for (ri = rn->info; ri; ri = ri->next)
+         {
+           if (BGP_INFO_HOLDDOWN (ri))
+             continue;
+
+           if (del && ri == del)
+             continue;
+
+           if (! rinew && first)
+              first = 0;
+
+#ifdef AGGREGATE_NEXTHOP_CHECK
+           if (! IPV4_ADDR_SAME (&ri->attr->nexthop, &nexthop)
+               || ri->attr->med != med)
+             {
+               if (aspath)
+                 aspath_free (aspath);
+               if (community)
+                 community_free (community);
+               bgp_unlock_node (rn);
+               bgp_unlock_node (top);
+               return;
+             }
+#endif /* AGGREGATE_NEXTHOP_CHECK */
+
+            if (ri->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
+              atomic_aggregate = 1;
+
+           if (ri->sub_type != BGP_ROUTE_AGGREGATE)
+             {
+               if (aggregate->summary_only)
+                 {
+                   (bgp_info_extra_get (ri))->suppress++;
+                   bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+                   match++;
+                 }
+
+               aggregate->count++;
+
+               if (origin < ri->attr->origin)
+                 origin = ri->attr->origin;
+
+               if (aggregate->as_set)
+                 {
+                   if (aspath)
+                     {
+                       asmerge = aspath_aggregate (aspath, ri->attr->aspath);
+                       aspath_free (aspath);
+                       aspath = asmerge;
+                     }
+                   else
+                     aspath = aspath_dup (ri->attr->aspath);
+
+                   if (ri->attr->community)
+                     {
+                       if (community)
+                         {
+                           commerge = community_merge (community,
+                                                       ri->attr->community);
+                           community = community_uniq_sort (commerge);
+                           community_free (commerge);
+                         }
+                       else
+                         community = community_dup (ri->attr->community);
+                     }
+                 }
+             }
+         }
+       if (match)
+         bgp_process (bgp, rn, afi, safi);
+      }
+  bgp_unlock_node (top);
+
+  if (rinew)
+    {
+      aggregate->count++;
+      
+      if (aggregate->summary_only)
+        (bgp_info_extra_get (rinew))->suppress++;
+
+      if (origin < rinew->attr->origin)
+        origin = rinew->attr->origin;
+
+      if (aggregate->as_set)
+       {
+         if (aspath)
+           {
+             asmerge = aspath_aggregate (aspath, rinew->attr->aspath);
+             aspath_free (aspath);
+             aspath = asmerge;
+           }
+         else
+           aspath = aspath_dup (rinew->attr->aspath);
+
+         if (rinew->attr->community)
+           {
+             if (community)
+               {
+                 commerge = community_merge (community,
+                                             rinew->attr->community);
+                 community = community_uniq_sort (commerge);
+                 community_free (commerge);
+               }
+             else
+               community = community_dup (rinew->attr->community);
+           }
+       }
+    }
+
+  if (aggregate->count > 0)
+    {
+      rn = bgp_node_get (table, p);
+      new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, bgp->peer_self,
+                     bgp_attr_aggregate_intern(bgp, origin, aspath, community,
+                                               aggregate->as_set,
+                                                atomic_aggregate), rn);
+      SET_FLAG (new->flags, BGP_INFO_VALID);
+
+      bgp_info_add (rn, new);
+      bgp_unlock_node (rn);
+      bgp_process (bgp, rn, afi, safi);
+    }
+  else
+    {
+      if (aspath)
+       aspath_free (aspath);
+      if (community)
+       community_free (community);
+    }
+}
+
+void bgp_aggregate_delete (struct bgp *, struct prefix *, afi_t, safi_t,
+                          struct bgp_aggregate *);
+
+void
+bgp_aggregate_increment (struct bgp *bgp, struct prefix *p,
+                        struct bgp_info *ri, afi_t afi, safi_t safi)
+{
+  struct bgp_node *child;
+  struct bgp_node *rn;
+  struct bgp_aggregate *aggregate;
+  struct bgp_table *table;
+
+  /* MPLS-VPN aggregation is not yet supported. */
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    return;
+
+  table = bgp->aggregate[afi][safi];
+
+  /* No aggregates configured. */
+  if (bgp_table_top_nolock (table) == NULL)
+    return;
+
+  if (p->prefixlen == 0)
+    return;
+
+  if (BGP_INFO_HOLDDOWN (ri))
+    return;
+
+  child = bgp_node_get (table, p);
+
+  /* Aggregate address configuration check. */
+  for (rn = child; rn; rn = bgp_node_parent_nolock (rn))
+    if ((aggregate = rn->info) != NULL && rn->p.prefixlen < p->prefixlen)
+      {
+       bgp_aggregate_delete (bgp, &rn->p, afi, safi, aggregate);
+       bgp_aggregate_route (bgp, &rn->p, ri, afi, safi, NULL, aggregate);
+      }
+  bgp_unlock_node (child);
+}
+
+void
+bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p, 
+                        struct bgp_info *del, afi_t afi, safi_t safi)
+{
+  struct bgp_node *child;
+  struct bgp_node *rn;
+  struct bgp_aggregate *aggregate;
+  struct bgp_table *table;
+
+  /* MPLS-VPN aggregation is not yet supported. */
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    return;
+
+  table = bgp->aggregate[afi][safi];
+
+  /* No aggregates configured. */
+  if (bgp_table_top_nolock (table) == NULL)
+    return;
+
+  if (p->prefixlen == 0)
+    return;
+
+  child = bgp_node_get (table, p);
+
+  /* Aggregate address configuration check. */
+  for (rn = child; rn; rn = bgp_node_parent_nolock (rn))
+    if ((aggregate = rn->info) != NULL && rn->p.prefixlen < p->prefixlen)
+      {
+       bgp_aggregate_delete (bgp, &rn->p, afi, safi, aggregate);
+       bgp_aggregate_route (bgp, &rn->p, NULL, afi, safi, del, aggregate);
+      }
+  bgp_unlock_node (child);
+}
+
+/* Called via bgp_aggregate_set when the user configures aggregate-address */
+static void
+bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi,
+                  struct bgp_aggregate *aggregate)
+{
+  struct bgp_table *table;
+  struct bgp_node *top;
+  struct bgp_node *rn;
+  struct bgp_info *new;
+  struct bgp_info *ri;
+  unsigned long match;
+  u_char origin = BGP_ORIGIN_IGP;
+  struct aspath *aspath = NULL;
+  struct aspath *asmerge = NULL;
+  struct community *community = NULL;
+  struct community *commerge = NULL;
+  u_char atomic_aggregate = 0;
+
+  table = bgp->rib[afi][safi];
+
+  /* Sanity check. */
+  if (afi == AFI_IP && p->prefixlen == IPV4_MAX_BITLEN)
+    return;
+  if (afi == AFI_IP6 && p->prefixlen == IPV6_MAX_BITLEN)
+    return;
+    
+  /* If routes exists below this node, generate aggregate routes. */
+  top = bgp_node_get (table, p);
+  for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top))
+    if (rn->p.prefixlen > p->prefixlen)
+      {
+       match = 0;
+
+       for (ri = rn->info; ri; ri = ri->next)
+         {
+           if (BGP_INFO_HOLDDOWN (ri))
+             continue;
+
+            if (ri->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
+              atomic_aggregate = 1;
+
+           if (ri->sub_type != BGP_ROUTE_AGGREGATE)
+             {
+               /* summary-only aggregate route suppress aggregated
+                  route announcement.  */
+               if (aggregate->summary_only)
+                 {
+                   (bgp_info_extra_get (ri))->suppress++;
+                   bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+                   match++;
+                 }
+
+                /* If at least one route among routes that are aggregated has
+                 * ORIGIN with the value INCOMPLETE, then the aggregated route
+                 * MUST have the ORIGIN attribute with the value INCOMPLETE.
+                 * Otherwise, if at least one route among routes that are
+                 * aggregated has ORIGIN with the value EGP, then the aggregated
+                 * route MUST have the ORIGIN attribute with the value EGP.
+                 */
+                if (origin < ri->attr->origin)
+                    origin = ri->attr->origin;
+
+               /* as-set aggregate route generate origin, as path,
+                  community aggregation.  */
+               if (aggregate->as_set)
+                 {
+                   if (aspath)
+                     {
+                       asmerge = aspath_aggregate (aspath, ri->attr->aspath);
+                       aspath_free (aspath);
+                       aspath = asmerge;
+                     }
+                   else
+                     aspath = aspath_dup (ri->attr->aspath);
+
+                   if (ri->attr->community)
+                     {
+                       if (community)
+                         {
+                           commerge = community_merge (community,
+                                                       ri->attr->community);
+                           community = community_uniq_sort (commerge);
+                           community_free (commerge);
+                         }
+                       else
+                         community = community_dup (ri->attr->community);
+                     }
+                 }
+               aggregate->count++;
+             }
+         }
+       
+       /* If this node is suppressed, process the change. */
+       if (match)
+         bgp_process (bgp, rn, afi, safi);
+      }
+  bgp_unlock_node (top);
+
+  /* Add aggregate route to BGP table. */
+  if (aggregate->count)
+    {
+      rn = bgp_node_get (table, p);
+      new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, bgp->peer_self,
+                     bgp_attr_aggregate_intern(bgp, origin, aspath, community,
+                                               aggregate->as_set,
+                                                atomic_aggregate), rn);
+      SET_FLAG (new->flags, BGP_INFO_VALID);
+
+      bgp_info_add (rn, new);
+      bgp_unlock_node (rn);
+      
+      /* Process change. */
+      bgp_process (bgp, rn, afi, safi);
+    }
+  else
+    {
+      if (aspath)
+       aspath_free (aspath);
+      if (community)
+       community_free (community);
+    }
+}
+
+void
+bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi, 
+                     safi_t safi, struct bgp_aggregate *aggregate)
+{
+  struct bgp_table *table;
+  struct bgp_node *top;
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  unsigned long match;
+
+  table = bgp->rib[afi][safi];
+
+  if (afi == AFI_IP && p->prefixlen == IPV4_MAX_BITLEN)
+    return;
+  if (afi == AFI_IP6 && p->prefixlen == IPV6_MAX_BITLEN)
+    return;
+
+  /* If routes exists below this node, generate aggregate routes. */
+  top = bgp_node_get (table, p);
+  for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top))
+    if (rn->p.prefixlen > p->prefixlen)
+      {
+       match = 0;
+
+       for (ri = rn->info; ri; ri = ri->next)
+         {
+           if (BGP_INFO_HOLDDOWN (ri))
+             continue;
+
+           if (ri->sub_type != BGP_ROUTE_AGGREGATE)
+             {
+               if (aggregate->summary_only && ri->extra)
+                 {
+                   ri->extra->suppress--;
+
+                   if (ri->extra->suppress == 0)
+                     {
+                       bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
+                       match++;
+                     }
+                 }
+               aggregate->count--;
+             }
+         }
+
+       /* If this node was suppressed, process the change. */
+       if (match)
+         bgp_process (bgp, rn, afi, safi);
+      }
+  bgp_unlock_node (top);
+
+  /* Delete aggregate route from BGP table. */
+  rn = bgp_node_get (table, p);
+
+  for (ri = rn->info; ri; ri = ri->next)
+    if (ri->peer == bgp->peer_self 
+       && ri->type == ZEBRA_ROUTE_BGP
+       && ri->sub_type == BGP_ROUTE_AGGREGATE)
+      break;
+
+  /* Withdraw static BGP route from routing table. */
+  if (ri)
+    {
+      bgp_info_delete (rn, ri);
+      bgp_process (bgp, rn, afi, safi);
+    }
+
+  /* Unlock bgp_node_lookup. */
+  bgp_unlock_node (rn);
+}
+
+/* Aggregate route attribute. */
+#define AGGREGATE_SUMMARY_ONLY 1
+#define AGGREGATE_AS_SET       1
+
+static int
+bgp_aggregate_unset (struct vty *vty, const char *prefix_str,
+                     afi_t afi, safi_t safi)
+{
+  int ret;
+  struct prefix p;
+  struct bgp_node *rn;
+  struct bgp *bgp;
+  struct bgp_aggregate *aggregate;
+
+  /* Convert string to prefix structure. */
+  ret = str2prefix (prefix_str, &p);
+  if (!ret)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask (&p);
+
+  /* Get BGP structure. */
+  bgp = vty->index;
+
+  /* Old configuration check. */
+  rn = bgp_node_lookup (bgp->aggregate[afi][safi], &p);
+  if (! rn)
+    {
+      vty_out (vty, "%% There is no aggregate-address configuration.%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  aggregate = rn->info;
+  if (aggregate->safi & SAFI_UNICAST)
+    bgp_aggregate_delete (bgp, &p, afi, SAFI_UNICAST, aggregate);
+  if (aggregate->safi & SAFI_MULTICAST)
+    bgp_aggregate_delete (bgp, &p, afi, SAFI_MULTICAST, aggregate);
+
+  /* Unlock aggregate address configuration. */
+  rn->info = NULL;
+  bgp_aggregate_free (aggregate);
+  bgp_unlock_node (rn);
+  bgp_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_aggregate_set (struct vty *vty, const char *prefix_str,
+                   afi_t afi, safi_t safi,
+                  u_char summary_only, u_char as_set)
+{
+  int ret;
+  struct prefix p;
+  struct bgp_node *rn;
+  struct bgp *bgp;
+  struct bgp_aggregate *aggregate;
+
+  /* Convert string to prefix structure. */
+  ret = str2prefix (prefix_str, &p);
+  if (!ret)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask (&p);
+
+  /* Get BGP structure. */
+  bgp = vty->index;
+
+  /* Old configuration check. */
+  rn = bgp_node_get (bgp->aggregate[afi][safi], &p);
+
+  if (rn->info)
+    {
+      vty_out (vty, "There is already same aggregate network.%s", VTY_NEWLINE);
+      /* try to remove the old entry */
+      ret = bgp_aggregate_unset (vty, prefix_str, afi, safi);
+      if (ret)
+        {
+          vty_out (vty, "Error deleting aggregate.%s", VTY_NEWLINE);
+         bgp_unlock_node (rn);
+         return CMD_WARNING;
+        }
+    }
+
+  /* Make aggregate address structure. */
+  aggregate = bgp_aggregate_new ();
+  aggregate->summary_only = summary_only;
+  aggregate->as_set = as_set;
+  aggregate->safi = safi;
+  rn->info = aggregate;
+
+  /* Aggregate address insert into BGP routing table. */
+  if (safi & SAFI_UNICAST)
+    bgp_aggregate_add (bgp, &p, afi, SAFI_UNICAST, aggregate);
+  if (safi & SAFI_MULTICAST)
+    bgp_aggregate_add (bgp, &p, afi, SAFI_MULTICAST, aggregate);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (aggregate_address,
+       aggregate_address_cmd,
+       "aggregate-address A.B.C.D/M",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty), 0, 0);
+}
+
+DEFUN (aggregate_address_mask,
+       aggregate_address_mask_cmd,
+       "aggregate-address A.B.C.D A.B.C.D",
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty),
+                           0, 0);
+}
+
+DEFUN (aggregate_address_summary_only,
+       aggregate_address_summary_only_cmd,
+       "aggregate-address A.B.C.D/M summary-only",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty),
+                           AGGREGATE_SUMMARY_ONLY, 0);
+}
+
+DEFUN (aggregate_address_mask_summary_only,
+       aggregate_address_mask_summary_only_cmd,
+       "aggregate-address A.B.C.D A.B.C.D summary-only",
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Filter more specific routes from updates\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty),
+                           AGGREGATE_SUMMARY_ONLY, 0);
+}
+
+DEFUN (aggregate_address_as_set,
+       aggregate_address_as_set_cmd,
+       "aggregate-address A.B.C.D/M as-set",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Generate AS set path information\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty),
+                           0, AGGREGATE_AS_SET);
+}
+
+DEFUN (aggregate_address_mask_as_set,
+       aggregate_address_mask_as_set_cmd,
+       "aggregate-address A.B.C.D A.B.C.D as-set",
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Generate AS set path information\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty),
+                           0, AGGREGATE_AS_SET);
+}
+
+
+DEFUN (aggregate_address_as_set_summary,
+       aggregate_address_as_set_summary_cmd,
+       "aggregate-address A.B.C.D/M as-set summary-only",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Generate AS set path information\n"
+       "Filter more specific routes from updates\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty),
+                           AGGREGATE_SUMMARY_ONLY, AGGREGATE_AS_SET);
+}
+
+ALIAS (aggregate_address_as_set_summary,
+       aggregate_address_summary_as_set_cmd,
+       "aggregate-address A.B.C.D/M summary-only as-set",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n"
+       "Generate AS set path information\n")
+
+DEFUN (aggregate_address_mask_as_set_summary,
+       aggregate_address_mask_as_set_summary_cmd,
+       "aggregate-address A.B.C.D A.B.C.D as-set summary-only",
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Generate AS set path information\n"
+       "Filter more specific routes from updates\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty),
+                           AGGREGATE_SUMMARY_ONLY, AGGREGATE_AS_SET);
+}
+
+ALIAS (aggregate_address_mask_as_set_summary,
+       aggregate_address_mask_summary_as_set_cmd,
+       "aggregate-address A.B.C.D A.B.C.D summary-only as-set",
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Filter more specific routes from updates\n"
+       "Generate AS set path information\n")
+
+DEFUN (no_aggregate_address,
+       no_aggregate_address_cmd,
+       "no aggregate-address A.B.C.D/M",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+{
+  return bgp_aggregate_unset (vty, argv[0], AFI_IP, bgp_node_safi (vty));
+}
+
+ALIAS (no_aggregate_address,
+       no_aggregate_address_summary_only_cmd,
+       "no aggregate-address A.B.C.D/M summary-only",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+
+ALIAS (no_aggregate_address,
+       no_aggregate_address_as_set_cmd,
+       "no aggregate-address A.B.C.D/M as-set",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Generate AS set path information\n")
+
+ALIAS (no_aggregate_address,
+       no_aggregate_address_as_set_summary_cmd,
+       "no aggregate-address A.B.C.D/M as-set summary-only",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Generate AS set path information\n"
+       "Filter more specific routes from updates\n")
+
+ALIAS (no_aggregate_address,
+       no_aggregate_address_summary_as_set_cmd,
+       "no aggregate-address A.B.C.D/M summary-only as-set",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n"
+       "Generate AS set path information\n")
+
+DEFUN (no_aggregate_address_mask,
+       no_aggregate_address_mask_cmd,
+       "no aggregate-address A.B.C.D A.B.C.D",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_aggregate_unset (vty, prefix_str, AFI_IP, bgp_node_safi (vty));
+}
+
+ALIAS (no_aggregate_address_mask,
+       no_aggregate_address_mask_summary_only_cmd,
+       "no aggregate-address A.B.C.D A.B.C.D summary-only",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Filter more specific routes from updates\n")
+
+ALIAS (no_aggregate_address_mask,
+       no_aggregate_address_mask_as_set_cmd,
+       "no aggregate-address A.B.C.D A.B.C.D as-set",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Generate AS set path information\n")
+
+ALIAS (no_aggregate_address_mask,
+       no_aggregate_address_mask_as_set_summary_cmd,
+       "no aggregate-address A.B.C.D A.B.C.D as-set summary-only",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Generate AS set path information\n"
+       "Filter more specific routes from updates\n")
+
+ALIAS (no_aggregate_address_mask,
+       no_aggregate_address_mask_summary_as_set_cmd,
+       "no aggregate-address A.B.C.D A.B.C.D summary-only as-set",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate address\n"
+       "Aggregate mask\n"
+       "Filter more specific routes from updates\n"
+       "Generate AS set path information\n")
+
+DEFUN (ipv6_aggregate_address,
+       ipv6_aggregate_address_cmd,
+       "aggregate-address X:X::X:X/M",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST, 0, 0);
+}
+
+DEFUN (ipv6_aggregate_address_summary_only,
+       ipv6_aggregate_address_summary_only_cmd,
+       "aggregate-address X:X::X:X/M summary-only",
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+{
+  return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST, 
+                           AGGREGATE_SUMMARY_ONLY, 0);
+}
+
+DEFUN (no_ipv6_aggregate_address,
+       no_ipv6_aggregate_address_cmd,
+       "no aggregate-address X:X::X:X/M",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+{
+  return bgp_aggregate_unset (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
+DEFUN (no_ipv6_aggregate_address_summary_only,
+       no_ipv6_aggregate_address_summary_only_cmd,
+       "no aggregate-address X:X::X:X/M summary-only",
+       NO_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+{
+  return bgp_aggregate_unset (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (ipv6_aggregate_address,
+       old_ipv6_aggregate_address_cmd,
+       "ipv6 bgp aggregate-address X:X::X:X/M",
+       IPV6_STR
+       BGP_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+
+ALIAS (ipv6_aggregate_address_summary_only,
+       old_ipv6_aggregate_address_summary_only_cmd,
+       "ipv6 bgp aggregate-address X:X::X:X/M summary-only",
+       IPV6_STR
+       BGP_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+
+ALIAS (no_ipv6_aggregate_address,
+       old_no_ipv6_aggregate_address_cmd,
+       "no ipv6 bgp aggregate-address X:X::X:X/M",
+       NO_STR
+       IPV6_STR
+       BGP_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n")
+
+ALIAS (no_ipv6_aggregate_address_summary_only,
+       old_no_ipv6_aggregate_address_summary_only_cmd,
+       "no ipv6 bgp aggregate-address X:X::X:X/M summary-only",
+       NO_STR
+       IPV6_STR
+       BGP_STR
+       "Configure BGP aggregate entries\n"
+       "Aggregate prefix\n"
+       "Filter more specific routes from updates\n")
+
+/* Redistribute route treatment. */
+void
+bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop,
+                     const struct in6_addr *nexthop6,
+                     u_int32_t metric, u_char type, route_tag_t tag)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+  struct bgp_info *new;
+  struct bgp_info *bi;
+  struct bgp_info info;
+  struct bgp_node *bn;
+  struct attr attr;
+  struct attr *new_attr;
+  afi_t afi;
+  int ret;
+
+  /* Make default attribute. */
+  bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
+  if (nexthop)
+    attr.nexthop = *nexthop;
+
+  if (nexthop6)
+    {
+      struct attr_extra *extra = bgp_attr_extra_get(&attr);
+      extra->mp_nexthop_global = *nexthop6;
+      extra->mp_nexthop_len = 16;
+    }
+
+  attr.med = metric;
+  attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+  attr.extra->tag = tag;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    {
+      afi = family2afi (p->family);
+
+      if (bgp->redist[afi][type])
+       {
+         struct attr attr_new;
+         struct attr_extra extra_new;
+
+         /* Copy attribute for modification. */
+         attr_new.extra = &extra_new;
+         bgp_attr_dup (&attr_new, &attr);
+
+         if (bgp->redist_metric_flag[afi][type])
+           attr_new.med = bgp->redist_metric[afi][type];
+
+         /* Apply route-map. */
+         if (bgp->rmap[afi][type].name)
+           {
+             info.peer = bgp->peer_self;
+             info.attr = &attr_new;
+
+              SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE);
+
+             ret = route_map_apply (bgp->rmap[afi][type].map, p, RMAP_BGP,
+                                    &info);
+
+              bgp->peer_self->rmap_type = 0;
+
+             if (ret == RMAP_DENYMATCH)
+               {
+                 /* Free uninterned attribute. */
+                 bgp_attr_flush (&attr_new);
+
+                 /* Unintern original. */
+                 aspath_unintern (&attr.aspath);
+                 bgp_attr_extra_free (&attr);
+                 bgp_redistribute_delete (p, type);
+                 return;
+               }
+           }
+
+          bn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], 
+                                 afi, SAFI_UNICAST, p, NULL);
+          
+         new_attr = bgp_attr_intern (&attr_new);
+
+         for (bi = bn->info; bi; bi = bi->next)
+           if (bi->peer == bgp->peer_self
+               && bi->sub_type == BGP_ROUTE_REDISTRIBUTE)
+             break;
+         if (bi)
+           {
+             if (attrhash_cmp (bi->attr, new_attr) &&
+                 !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED))
+               {
+                 bgp_attr_unintern (&new_attr);
+                 aspath_unintern (&attr.aspath);
+                 bgp_attr_extra_free (&attr);
+                 bgp_unlock_node (bn);
+                 return;
+               }
+             else
+               {
+                 /* The attribute is changed. */
+                 bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED);
+                 /* Rewrite BGP route information. */
+                 if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED))
+                   bgp_info_restore(bn, bi);
+                 else
+                   bgp_aggregate_decrement (bgp, p, bi, afi, SAFI_UNICAST);
+                 bgp_attr_unintern (&bi->attr);
+                 bi->attr = new_attr;
+                 bi->uptime = bgp_clock ();
+                 /* Process change. */
+                 bgp_aggregate_increment (bgp, p, bi, afi, SAFI_UNICAST);
+                 bgp_process (bgp, bn, afi, SAFI_UNICAST);
+                 bgp_unlock_node (bn);
+                 aspath_unintern (&attr.aspath);
+                 bgp_attr_extra_free (&attr);
+                 return;
+               }
+           }
+
+         new = info_make(type, BGP_ROUTE_REDISTRIBUTE, bgp->peer_self,
+                         new_attr, bn);
+         SET_FLAG (new->flags, BGP_INFO_VALID);
+
+         bgp_aggregate_increment (bgp, p, new, afi, SAFI_UNICAST);
+         bgp_info_add (bn, new);
+         bgp_unlock_node (bn);
+         bgp_process (bgp, bn, afi, SAFI_UNICAST);
+       }
+    }
+
+  /* Unintern original. */
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+void
+bgp_redistribute_delete (struct prefix *p, u_char type)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+  afi_t afi;
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    {
+      afi = family2afi (p->family);
+
+      if (bgp->redist[afi][type])
+       {
+         rn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, p, NULL);
+
+         for (ri = rn->info; ri; ri = ri->next)
+           if (ri->peer == bgp->peer_self
+               && ri->type == type)
+             break;
+
+         if (ri)
+           {
+             bgp_aggregate_decrement (bgp, p, ri, afi, SAFI_UNICAST);
+             bgp_info_delete (rn, ri);
+             bgp_process (bgp, rn, afi, SAFI_UNICAST);
+           }
+         bgp_unlock_node (rn);
+       }
+    }
+}
+
+/* Withdraw specified route type's route. */
+void
+bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type)
+{
+  struct bgp_node *rn;
+  struct bgp_info *ri;
+  struct bgp_table *table;
+
+  table = bgp->rib[afi][SAFI_UNICAST];
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    {
+      for (ri = rn->info; ri; ri = ri->next)
+       if (ri->peer == bgp->peer_self
+           && ri->type == type)
+         break;
+
+      if (ri)
+       {
+         bgp_aggregate_decrement (bgp, &rn->p, ri, afi, SAFI_UNICAST);
+         bgp_info_delete (rn, ri);
+         bgp_process (bgp, rn, afi, SAFI_UNICAST);
+       }
+    }
+}
+
+/* Static function to display route. */
+static void
+route_vty_out_route (struct prefix *p, struct vty *vty)
+{
+  int len;
+  u_int32_t destination; 
+  char buf[BUFSIZ];
+
+  if (p->family == AF_INET)
+    {
+      len = vty_out (vty, "%s", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ));
+      destination = ntohl (p->u.prefix4.s_addr);
+
+      if ((IN_CLASSC (destination) && p->prefixlen == 24)
+         || (IN_CLASSB (destination) && p->prefixlen == 16)
+         || (IN_CLASSA (destination) && p->prefixlen == 8)
+         || p->u.prefix4.s_addr == 0)
+       {
+         /* When mask is natural, mask is not displayed. */
+       }
+      else
+       len += vty_out (vty, "/%d", p->prefixlen);
+    }
+  else
+    len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                  p->prefixlen);
+
+  len = 17 - len;
+  if (len < 1)
+    vty_out (vty, "%s%*s", VTY_NEWLINE, 20, " ");
+  else
+    vty_out (vty, "%*s", len, " ");
+}
+
+enum bgp_display_type
+{
+  normal_list,
+};
+
+/* Print the short form route status for a bgp_info */
+static void
+route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo)
+{
+ /* Route status display. */
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED))
+    vty_out (vty, "R");
+  else if (CHECK_FLAG (binfo->flags, BGP_INFO_STALE))
+    vty_out (vty, "S");
+  else if (binfo->extra && binfo->extra->suppress)
+    vty_out (vty, "s");
+  else if (CHECK_FLAG (binfo->flags, BGP_INFO_VALID) &&
+           ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+    vty_out (vty, "*");
+  else
+    vty_out (vty, " ");
+
+  /* Selected */
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+    vty_out (vty, "h");
+  else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+    vty_out (vty, "d");
+  else if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED))
+    vty_out (vty, ">");
+  else if (CHECK_FLAG (binfo->flags, BGP_INFO_MULTIPATH))
+    vty_out (vty, "=");
+  else
+    vty_out (vty, " ");
+
+  /* Internal route. */
+    if ((binfo->peer->as) && (binfo->peer->as == binfo->peer->local_as))
+      vty_out (vty, "i");
+    else
+      vty_out (vty, " "); 
+}
+
+/* called from terminal list command */
+void
+route_vty_out(
+    struct vty *vty,
+    struct prefix *p,
+    struct bgp_info *binfo,
+    int display,
+    safi_t safi)
+{
+  struct attr *attr;
+  
+  /* short status lead text */ 
+  route_vty_short_status_out (vty, binfo);
+  
+  /* print prefix and mask */
+  if (!display)
+    route_vty_out_route (p, vty);
+  else
+    vty_out (vty, "%*s", 17, " ");
+
+  /* Print attribute */
+  attr = binfo->attr;
+  if (attr) 
+    {
+
+      /*
+       * NEXTHOP start
+       */
+
+      /*
+       * For ENCAP routes, nexthop address family is not
+       * neccessarily the same as the prefix address family.
+       * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
+       */
+      if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) {
+       if (attr->extra) {
+           char        buf[BUFSIZ];
+           int         af = NEXTHOP_FAMILY(attr->extra->mp_nexthop_len);
+
+           switch (af) {
+               case AF_INET:
+                   vty_out (vty, "%s", inet_ntop(af,
+                       &attr->extra->mp_nexthop_global_in, buf, BUFSIZ));
+                   break;
+               case AF_INET6:
+                   vty_out (vty, "%s", inet_ntop(af,
+                       &attr->extra->mp_nexthop_global, buf, BUFSIZ));
+                   break;
+               default:
+                   vty_out(vty, "?");
+           }
+       } else {
+           vty_out(vty, "?");
+       }
+      } else {
+
+         if (p->family == AF_INET)
+           {
+               vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+           }
+         else if (p->family == AF_INET6)
+           {
+             int len;
+             char buf[BUFSIZ];
+
+             len = vty_out (vty, "%s",
+                            inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+                            buf, BUFSIZ));
+             len = 16 - len;
+             if (len < 1)
+               vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " ");
+             else
+               vty_out (vty, "%*s", len, " ");
+           }
+         else
+          {
+            vty_out(vty, "?");
+          }
+      }
+
+      /*
+       * NEXTHOP end
+       */
+
+
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+       vty_out (vty, "%10u", attr->med);
+      else
+         vty_out (vty, "          ");
+
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+       vty_out (vty, "%7u", attr->local_pref);
+      else
+         vty_out (vty, "       ");
+
+      vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
+    
+      /* Print aspath */
+      if (attr->aspath)
+        aspath_print_vty (vty, "%s", attr->aspath, " ");
+
+      /* Print origin */
+      vty_out (vty, "%s", bgp_origin_str[attr->origin]);
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}  
+
+/* called from terminal list command */
+void
+route_vty_out_tmp (struct vty *vty, struct prefix *p,
+                  struct attr *attr, safi_t safi)
+{
+  /* Route status display. */
+  vty_out (vty, "*");
+  vty_out (vty, ">");
+  vty_out (vty, " ");
+
+  /* print prefix and mask */
+  route_vty_out_route (p, vty);
+
+  /* Print attribute */
+  if (attr) 
+    {
+      if (p->family == AF_INET)
+       {
+         if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+           vty_out (vty, "%-16s",
+                     inet_ntoa (attr->extra->mp_nexthop_global_in));
+         else
+           vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+       }
+      else if (p->family == AF_INET6)
+        {
+          int len;
+          char buf[BUFSIZ];
+          
+          assert (attr->extra);
+
+          len = vty_out (vty, "%s",
+                         inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+                         buf, BUFSIZ));
+          len = 16 - len;
+          if (len < 1)
+            vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " ");
+          else
+            vty_out (vty, "%*s", len, " ");
+        }
+
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+       vty_out (vty, "%10u ", attr->med);
+      else
+       vty_out (vty, "          ");
+
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+       vty_out (vty, "%7u ", attr->local_pref);
+      else
+       vty_out (vty, "       ");
+      
+      vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
+      
+      /* Print aspath */
+      if (attr->aspath)
+        aspath_print_vty (vty, "%s", attr->aspath, " ");
+
+      /* Print origin */
+      vty_out (vty, "%s", bgp_origin_str[attr->origin]);
+    }
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}  
+
+void
+route_vty_out_tag (struct vty *vty, struct prefix *p,
+                  struct bgp_info *binfo, int display, safi_t safi)
+{
+  struct attr *attr;
+  u_int32_t label = 0;
+  
+  if (!binfo->extra)
+    return;
+  
+  /* short status lead text */ 
+  route_vty_short_status_out (vty, binfo);
+    
+  /* print prefix and mask */
+  if (! display)
+    route_vty_out_route (p, vty);
+  else
+    vty_out (vty, "%*s", 17, " ");
+
+  /* Print attribute */
+  attr = binfo->attr;
+  if (attr) 
+    {
+      if (p->family == AF_INET)
+       {
+         if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+           vty_out (vty, "%-16s",
+                     inet_ntoa (attr->extra->mp_nexthop_global_in));
+         else
+           vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+       }
+      else if (p->family == AF_INET6)
+       {
+         assert (attr->extra);
+         char buf[BUFSIZ];
+         char buf1[BUFSIZ];
+         if (attr->extra->mp_nexthop_len == 16)
+           vty_out (vty, "%s", 
+                    inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+                     buf, BUFSIZ));
+         else if (attr->extra->mp_nexthop_len == 32)
+           vty_out (vty, "%s(%s)",
+                    inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+                               buf, BUFSIZ),
+                    inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+                               buf1, BUFSIZ));
+         
+       }
+    }
+
+  label = decode_label (binfo->extra->tag);
+
+  vty_out (vty, "notag/%d", label);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}  
+
+/* dampening route */
+static void
+damp_route_vty_out (struct vty *vty, struct prefix *p,
+                   struct bgp_info *binfo, int display, safi_t safi)
+{
+  struct attr *attr;
+  int len;
+  char timebuf[BGP_UPTIME_LEN];
+
+  /* short status lead text */ 
+  route_vty_short_status_out (vty, binfo);
+  
+  /* print prefix and mask */
+  if (! display)
+    route_vty_out_route (p, vty);
+  else
+    vty_out (vty, "%*s", 17, " ");
+
+  len = vty_out (vty, "%s", binfo->peer->host);
+  len = 17 - len;
+  if (len < 1)
+    vty_out (vty, "%s%*s", VTY_NEWLINE, 34, " ");
+  else
+    vty_out (vty, "%*s", len, " ");
+
+  vty_out (vty, "%s ", bgp_damp_reuse_time_vty (vty, binfo, timebuf, BGP_UPTIME_LEN));
+
+  /* Print attribute */
+  attr = binfo->attr;
+  if (attr)
+    {
+      /* Print aspath */
+      if (attr->aspath)
+       aspath_print_vty (vty, "%s", attr->aspath, " ");
+
+      /* Print origin */
+      vty_out (vty, "%s", bgp_origin_str[attr->origin]);
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+/* flap route */
+static void
+flap_route_vty_out (struct vty *vty, struct prefix *p,
+                   struct bgp_info *binfo, int display, safi_t safi)
+{
+  struct attr *attr;
+  struct bgp_damp_info *bdi;
+  char timebuf[BGP_UPTIME_LEN];
+  int len;
+  
+  if (!binfo->extra)
+    return;
+  
+  bdi = binfo->extra->damp_info;
+
+  /* short status lead text */
+  route_vty_short_status_out (vty, binfo);
+  
+  /* print prefix and mask */
+  if (! display)
+    route_vty_out_route (p, vty);
+  else
+    vty_out (vty, "%*s", 17, " ");
+
+  len = vty_out (vty, "%s", binfo->peer->host);
+  len = 16 - len;
+  if (len < 1)
+    vty_out (vty, "%s%*s", VTY_NEWLINE, 33, " ");
+  else
+    vty_out (vty, "%*s", len, " ");
+
+  len = vty_out (vty, "%d", bdi->flap);
+  len = 5 - len;
+  if (len < 1)
+    vty_out (vty, " ");
+  else
+    vty_out (vty, "%*s ", len, " ");
+    
+  vty_out (vty, "%s ", peer_uptime (bdi->start_time,
+          timebuf, BGP_UPTIME_LEN));
+
+  if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)
+      && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+    vty_out (vty, "%s ", bgp_damp_reuse_time_vty (vty, binfo, timebuf, BGP_UPTIME_LEN));
+  else
+    vty_out (vty, "%*s ", 8, " ");
+
+  /* Print attribute */
+  attr = binfo->attr;
+  if (attr)
+    {
+      /* Print aspath */
+      if (attr->aspath)
+       aspath_print_vty (vty, "%s", attr->aspath, " ");
+
+      /* Print origin */
+      vty_out (vty, "%s", bgp_origin_str[attr->origin]);
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, 
+                     struct bgp_info *binfo, afi_t afi, safi_t safi)
+{
+  char buf[INET6_ADDRSTRLEN];
+  char buf1[BUFSIZ];
+  struct attr *attr;
+  int sockunion_vty_out (struct vty *, union sockunion *);
+#ifdef HAVE_CLOCK_MONOTONIC
+  time_t tbuf;
+#endif
+       
+  attr = binfo->attr;
+
+  if (attr)
+    {
+      /* Line1 display AS-path, Aggregator */
+      if (attr->aspath)
+       {
+         vty_out (vty, "  ");
+         if (aspath_count_hops (attr->aspath) == 0)
+           vty_out (vty, "Local");
+         else
+           aspath_print_vty (vty, "%s", attr->aspath, "");
+       }
+
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED))
+        vty_out (vty, ", (removed)");
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_STALE))
+       vty_out (vty, ", (stale)");
+      if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)))
+       vty_out (vty, ", (aggregated by %u %s)", 
+                attr->extra->aggregator_as,
+                inet_ntoa (attr->extra->aggregator_addr));
+      if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+       vty_out (vty, ", (Received from a RR-client)");
+      if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+       vty_out (vty, ", (Received from a RS-client)");
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+       vty_out (vty, ", (history entry)");
+      else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+       vty_out (vty, ", (suppressed due to dampening)");
+      vty_out (vty, "%s", VTY_NEWLINE);
+         
+      /* Line2 display Next-hop, Neighbor, Router-id */
+      if (p->family == AF_INET)
+       {
+         vty_out (vty, "    %s", ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) ?
+                  inet_ntoa (attr->extra->mp_nexthop_global_in) :
+                  inet_ntoa (attr->nexthop));
+       }
+      else
+       {
+         assert (attr->extra);
+         vty_out (vty, "    %s",
+                  inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+                             buf, INET6_ADDRSTRLEN));
+       }
+
+      if (binfo->peer == bgp->peer_self)
+       {
+         vty_out (vty, " from %s ", 
+                  p->family == AF_INET ? "0.0.0.0" : "::");
+         vty_out (vty, "(%s)", inet_ntoa(bgp->router_id));
+       }
+      else
+       {
+         if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID))
+           vty_out (vty, " (inaccessible)"); 
+         else if (binfo->extra && binfo->extra->igpmetric)
+           vty_out (vty, " (metric %u)", binfo->extra->igpmetric);
+         if (!sockunion2str (&binfo->peer->su, buf, sizeof(buf))) {
+           buf[0] = '?';
+           buf[1] = 0;
+         }
+         vty_out (vty, " from %s", buf);
+         if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+           vty_out (vty, " (%s)", inet_ntoa (attr->extra->originator_id));
+         else
+           vty_out (vty, " (%s)", inet_ntop (AF_INET, &binfo->peer->remote_id, buf1, BUFSIZ));
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      /* display nexthop local */
+      if (attr->extra && attr->extra->mp_nexthop_len == 32)
+       {
+         vty_out (vty, "    (%s)%s",
+                  inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+                             buf, INET6_ADDRSTRLEN),
+                  VTY_NEWLINE);
+       }
+
+      /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid, Int/Ext/Local, Atomic, best */
+      vty_out (vty, "      Origin %s", bgp_origin_long_str[attr->origin]);
+         
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+       vty_out (vty, ", metric %u", attr->med);
+         
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+       vty_out (vty, ", localpref %u", attr->local_pref);
+      else
+       vty_out (vty, ", localpref %u", bgp->default_local_pref);
+
+      if (attr->extra && attr->extra->weight != 0)
+       vty_out (vty, ", weight %u", attr->extra->weight);
+
+      if (attr->extra && attr->extra->tag != 0)
+        vty_out (vty, ", tag %d", attr->extra->tag);
+       
+      if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID))
+       vty_out (vty, ", invalid");
+      else if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+       vty_out (vty, ", valid");
+
+      if (binfo->peer != bgp->peer_self)
+       {
+         if (binfo->peer->as == binfo->peer->local_as)
+           vty_out (vty, ", internal");
+         else 
+           vty_out (vty, ", %s", 
+                    (bgp_confederation_peers_check(bgp, binfo->peer->as) ? "confed-external" : "external"));
+       }
+      else if (binfo->sub_type == BGP_ROUTE_AGGREGATE)
+       vty_out (vty, ", aggregated, local");
+      else if (binfo->type != ZEBRA_ROUTE_BGP)
+       vty_out (vty, ", sourced");
+      else
+       vty_out (vty, ", sourced, local");
+
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
+       vty_out (vty, ", atomic-aggregate");
+         
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_MULTIPATH) ||
+         (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED) &&
+          bgp_info_mpath_count (binfo)))
+       vty_out (vty, ", multipath");
+
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED))
+       vty_out (vty, ", best");
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+         
+      /* Line 4 display Community */
+      if (attr->community)
+       vty_out (vty, "      Community: %s%s", attr->community->str,
+                VTY_NEWLINE);
+         
+      /* Line 5 display Extended-community */
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))
+       vty_out (vty, "      Extended Community: %s%s", 
+                attr->extra->ecommunity->str, VTY_NEWLINE);
+
+      /* Line 6 display Large community */
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))
+       vty_out (vty, "      Large Community: %s%s",
+                attr->extra->lcommunity->str, VTY_NEWLINE);
+
+      /* Line 7 display Originator, Cluster-id */
+      if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) ||
+         (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)))
+       {
+         assert (attr->extra);
+         if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+           vty_out (vty, "      Originator: %s", 
+                    inet_ntoa (attr->extra->originator_id));
+
+         if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))
+           {
+             int i;
+             vty_out (vty, ", Cluster list: ");
+             for (i = 0; i < attr->extra->cluster->length / 4; i++)
+               vty_out (vty, "%s ", 
+                        inet_ntoa (attr->extra->cluster->list[i]));
+           }
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+      
+      if (binfo->extra && binfo->extra->damp_info)
+       bgp_damp_info_vty (vty, binfo);
+
+      /* Line 8 display Uptime */
+#ifdef HAVE_CLOCK_MONOTONIC
+      tbuf = time(NULL) - (bgp_clock() - binfo->uptime);
+      vty_out (vty, "      Last update: %s", ctime(&tbuf));
+#else
+      vty_out (vty, "      Last update: %s", ctime(&binfo->uptime));
+#endif /* HAVE_CLOCK_MONOTONIC */
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, "\
+                             "h history, * valid, > best, = multipath,%s"\
+               "              i internal, r RIB-failure, S Stale, R Removed%s"
+#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s"
+#define BGP_SHOW_HEADER "   Network          Next Hop            Metric LocPrf Weight Path%s"
+#define BGP_SHOW_DAMP_HEADER "   Network          From             Reuse    Path%s"
+#define BGP_SHOW_FLAP_HEADER "   Network          From            Flaps Duration Reuse    Path%s"
+
+enum bgp_show_type
+{
+  bgp_show_type_normal,
+  bgp_show_type_regexp,
+  bgp_show_type_prefix_list,
+  bgp_show_type_filter_list,
+  bgp_show_type_route_map,
+  bgp_show_type_neighbor,
+  bgp_show_type_cidr_only,
+  bgp_show_type_prefix_longer,
+  bgp_show_type_community_all,
+  bgp_show_type_community,
+  bgp_show_type_community_exact,
+  bgp_show_type_community_list,
+  bgp_show_type_community_list_exact,
+  bgp_show_type_lcommunity_all,
+  bgp_show_type_lcommunity,
+  bgp_show_type_lcommunity_list,
+  bgp_show_type_flap_statistics,
+  bgp_show_type_flap_address,
+  bgp_show_type_flap_prefix,
+  bgp_show_type_flap_cidr_only,
+  bgp_show_type_flap_regexp,
+  bgp_show_type_flap_filter_list,
+  bgp_show_type_flap_prefix_list,
+  bgp_show_type_flap_prefix_longer,
+  bgp_show_type_flap_route_map,
+  bgp_show_type_flap_neighbor,
+  bgp_show_type_dampend_paths,
+  bgp_show_type_damp_neighbor
+};
+
+static int
+bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router_id,
+         enum bgp_show_type type, void *output_arg)
+{
+  struct bgp_info *ri;
+  struct bgp_node *rn;
+  int header = 1;
+  int display;
+  unsigned long output_count;
+  unsigned long total_count;
+
+  /* This is first entry point, so reset total line. */
+  output_count = 0;
+  total_count  = 0;
+
+  /* Start processing of routes. */
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) 
+    if (rn->info != NULL)
+      {
+       display = 0;
+
+       for (ri = rn->info; ri; ri = ri->next)
+         {
+            total_count++;
+           if (type == bgp_show_type_flap_statistics
+               || type == bgp_show_type_flap_address
+               || type == bgp_show_type_flap_prefix
+               || type == bgp_show_type_flap_cidr_only
+               || type == bgp_show_type_flap_regexp
+               || type == bgp_show_type_flap_filter_list
+               || type == bgp_show_type_flap_prefix_list
+               || type == bgp_show_type_flap_prefix_longer
+               || type == bgp_show_type_flap_route_map
+               || type == bgp_show_type_flap_neighbor
+               || type == bgp_show_type_dampend_paths
+               || type == bgp_show_type_damp_neighbor)
+             {
+               if (!(ri->extra && ri->extra->damp_info))
+                 continue;
+             }
+           if (type == bgp_show_type_regexp
+               || type == bgp_show_type_flap_regexp)
+             {
+               regex_t *regex = output_arg;
+                   
+               if (bgp_regexec (regex, ri->attr->aspath) == REG_NOMATCH)
+                 continue;
+             }
+           if (type == bgp_show_type_prefix_list
+               || type == bgp_show_type_flap_prefix_list)
+             {
+               struct prefix_list *plist = output_arg;
+                   
+               if (prefix_list_apply (plist, &rn->p) != PREFIX_PERMIT)
+                 continue;
+             }
+           if (type == bgp_show_type_filter_list
+               || type == bgp_show_type_flap_filter_list)
+             {
+               struct as_list *as_list = output_arg;
+
+               if (as_list_apply (as_list, ri->attr->aspath) != AS_FILTER_PERMIT)
+                 continue;
+             }
+           if (type == bgp_show_type_route_map
+               || type == bgp_show_type_flap_route_map)
+             {
+               struct route_map *rmap = output_arg;
+               struct bgp_info binfo;
+               struct attr dummy_attr;
+               struct attr_extra dummy_extra;
+               int ret;
+
+               dummy_attr.extra = &dummy_extra;
+               bgp_attr_dup (&dummy_attr, ri->attr);
+
+               binfo.peer = ri->peer;
+               binfo.attr = &dummy_attr;
+
+               ret = route_map_apply (rmap, &rn->p, RMAP_BGP, &binfo);
+               if (ret == RMAP_DENYMATCH)
+                 continue;
+             }
+           if (type == bgp_show_type_neighbor
+               || type == bgp_show_type_flap_neighbor
+               || type == bgp_show_type_damp_neighbor)
+             {
+               union sockunion *su = output_arg;
+
+               if (ri->peer->su_remote == NULL || ! sockunion_same(ri->peer->su_remote, su))
+                 continue;
+             }
+           if (type == bgp_show_type_cidr_only
+               || type == bgp_show_type_flap_cidr_only)
+             {
+               u_int32_t destination;
+
+               destination = ntohl (rn->p.u.prefix4.s_addr);
+               if (IN_CLASSC (destination) && rn->p.prefixlen == 24)
+                 continue;
+               if (IN_CLASSB (destination) && rn->p.prefixlen == 16)
+                 continue;
+               if (IN_CLASSA (destination) && rn->p.prefixlen == 8)
+                 continue;
+             }
+           if (type == bgp_show_type_prefix_longer
+               || type == bgp_show_type_flap_prefix_longer)
+             {
+               struct prefix *p = output_arg;
+
+               if (! prefix_match (p, &rn->p))
+                 continue;
+             }
+           if (type == bgp_show_type_community_all)
+             {
+               if (! ri->attr->community)
+                 continue;
+             }
+           if (type == bgp_show_type_community)
+             {
+               struct community *com = output_arg;
+
+               if (! ri->attr->community ||
+                   ! community_match (ri->attr->community, com))
+                 continue;
+             }
+           if (type == bgp_show_type_community_exact)
+             {
+               struct community *com = output_arg;
+
+               if (! ri->attr->community ||
+                   ! community_cmp (ri->attr->community, com))
+                 continue;
+             }
+           if (type == bgp_show_type_community_list)
+             {
+               struct community_list *list = output_arg;
+
+               if (! community_list_match (ri->attr->community, list))
+                 continue;
+             }
+           if (type == bgp_show_type_community_list_exact)
+             {
+               struct community_list *list = output_arg;
+
+               if (! community_list_exact_match (ri->attr->community, list))
+                 continue;
+             }
+           if (type == bgp_show_type_community_all)
+             {
+               if (! ri->attr->community)
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity)
+             {
+               struct lcommunity *lcom = output_arg;
+
+               if (! ri->attr->extra || ! ri->attr->extra->lcommunity ||
+                   ! lcommunity_match (ri->attr->extra->lcommunity, lcom))
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity_list)
+             {
+               struct community_list *list = output_arg;
+
+               if (! ri->attr->extra ||
+                   ! lcommunity_list_match (ri->attr->extra->lcommunity, list))
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity_all)
+             {
+               if (! ri->attr->extra || ! ri->attr->extra->lcommunity)
+                 continue;
+             }
+           if (type == bgp_show_type_flap_address
+               || type == bgp_show_type_flap_prefix)
+             {
+               struct prefix *p = output_arg;
+
+               if (! prefix_match (&rn->p, p))
+                 continue;
+
+               if (type == bgp_show_type_flap_prefix)
+                 if (p->prefixlen != rn->p.prefixlen)
+                   continue;
+             }
+           if (type == bgp_show_type_dampend_paths
+               || type == bgp_show_type_damp_neighbor)
+             {
+               if (! CHECK_FLAG (ri->flags, BGP_INFO_DAMPED)
+                   || CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+                 continue;
+             }
+
+           if (header)
+             {
+               vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (*router_id), VTY_NEWLINE);
+               vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+               vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+               if (type == bgp_show_type_dampend_paths
+                   || type == bgp_show_type_damp_neighbor)
+                 vty_out (vty, BGP_SHOW_DAMP_HEADER, VTY_NEWLINE);
+               else if (type == bgp_show_type_flap_statistics
+                        || type == bgp_show_type_flap_address
+                        || type == bgp_show_type_flap_prefix
+                        || type == bgp_show_type_flap_cidr_only
+                        || type == bgp_show_type_flap_regexp
+                        || type == bgp_show_type_flap_filter_list
+                        || type == bgp_show_type_flap_prefix_list
+                        || type == bgp_show_type_flap_prefix_longer
+                        || type == bgp_show_type_flap_route_map
+                        || type == bgp_show_type_flap_neighbor)
+                 vty_out (vty, BGP_SHOW_FLAP_HEADER, VTY_NEWLINE);
+               else
+                 vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE);
+               header = 0;
+             }
+
+           if (type == bgp_show_type_dampend_paths
+               || type == bgp_show_type_damp_neighbor)
+             damp_route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST);
+           else if (type == bgp_show_type_flap_statistics
+                    || type == bgp_show_type_flap_address
+                    || type == bgp_show_type_flap_prefix
+                    || type == bgp_show_type_flap_cidr_only
+                    || type == bgp_show_type_flap_regexp
+                    || type == bgp_show_type_flap_filter_list
+                    || type == bgp_show_type_flap_prefix_list
+                    || type == bgp_show_type_flap_prefix_longer
+                    || type == bgp_show_type_flap_route_map
+                    || type == bgp_show_type_flap_neighbor)
+             flap_route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST);
+           else
+             route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST);
+           display++;
+         }
+       if (display)
+         output_count++;
+      }
+
+  /* No route is displayed */
+  if (output_count == 0)
+    {
+      if (type == bgp_show_type_normal)
+        vty_out (vty, "No BGP prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "%sDisplayed  %ld out of %ld total prefixes%s",
+            VTY_NEWLINE, output_count, total_count, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_show (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
+         enum bgp_show_type type, void *output_arg)
+{
+  struct bgp_table *table;
+
+  if (bgp == NULL) {
+    bgp = bgp_get_default ();
+  }
+
+  if (bgp == NULL)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+
+  table = bgp->rib[afi][safi];
+
+  return bgp_show_table (vty, table, &bgp->router_id, type, output_arg);
+}
+
+/* Header of detailed BGP route information */
+static void
+route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
+                            struct bgp_node *rn,
+                             struct prefix_rd *prd, afi_t afi, safi_t safi)
+{
+  struct bgp_info *ri;
+  struct prefix *p;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  char buf1[INET6_ADDRSTRLEN];
+  char buf2[INET6_ADDRSTRLEN];
+  int count = 0;
+  int best = 0;
+  int suppress = 0;
+  int no_export = 0;
+  int no_advertise = 0;
+  int local_as = 0;
+  int first = 0;
+  int printrd = ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP));
+
+  p = &rn->p;
+  vty_out (vty, "BGP routing table entry for %s%s%s/%d%s",
+          (printrd ?  prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""),
+          printrd ?  ":" : "",
+          inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN),
+          p->prefixlen, VTY_NEWLINE);
+
+  for (ri = rn->info; ri; ri = ri->next)
+    {
+      count++;
+      if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))
+       {
+         best = count;
+         if (ri->extra && ri->extra->suppress)
+           suppress = 1;
+         if (ri->attr->community != NULL)
+           {
+             if (community_include (ri->attr->community, COMMUNITY_NO_ADVERTISE))
+               no_advertise = 1;
+             if (community_include (ri->attr->community, COMMUNITY_NO_EXPORT))
+               no_export = 1;
+             if (community_include (ri->attr->community, COMMUNITY_LOCAL_AS))
+               local_as = 1;
+           }
+       }
+    }
+
+  vty_out (vty, "Paths: (%d available", count);
+  if (best)
+    {
+      vty_out (vty, ", best #%d", best);
+      if (safi == SAFI_UNICAST)
+       vty_out (vty, ", table Default-IP-Routing-Table");
+    }
+  else
+    vty_out (vty, ", no best path");
+  if (no_advertise)
+    vty_out (vty, ", not advertised to any peer");
+  else if (no_export)
+    vty_out (vty, ", not advertised to EBGP peer");
+  else if (local_as)
+    vty_out (vty, ", not advertised outside local AS");
+  if (suppress)
+    vty_out (vty, ", Advertisements suppressed by an aggregate.");
+  vty_out (vty, ")%s", VTY_NEWLINE);
+
+  /* advertised peer */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (bgp_adj_out_lookup (peer, p, afi, safi, rn))
+       {
+         if (! first)
+           vty_out (vty, "  Advertised to non peer-group peers:%s ", VTY_NEWLINE);
+         vty_out (vty, " %s", sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN));
+         first = 1;
+       }
+    }
+  if (! first)
+    vty_out (vty, "  Not advertised to any peer");
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+/* Display specified route of BGP table. */
+static int
+bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, 
+                         struct bgp_table *rib, const char *ip_str,
+                         afi_t afi, safi_t safi, struct prefix_rd *prd,
+                         int prefix_check, enum bgp_path_type pathtype)
+{
+  int ret;
+  int header;
+  int display = 0;
+  struct prefix match;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp_info *ri;
+  struct bgp_table *table;
+
+  memset (&match, 0, sizeof (struct prefix)); /* keep valgrind happy */
+  /* Check IP address argument. */
+  ret = str2prefix (ip_str, &match);
+  if (! ret)
+    {
+      vty_out (vty, "address is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  match.family = afi2family (afi);
+
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    {
+      for (rn = bgp_table_top (rib); rn; rn = bgp_route_next (rn))
+        {
+          if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+            continue;
+
+          if ((table = rn->info) != NULL)
+            {
+              header = 1;
+
+              if ((rm = bgp_node_match (table, &match)) != NULL)
+                {
+                  if (prefix_check && rm->p.prefixlen != match.prefixlen)
+                    {
+                      bgp_unlock_node (rm);
+                      continue;
+                    }
+
+                  for (ri = rm->info; ri; ri = ri->next)
+                    {
+                      if (header)
+                        {
+                          route_vty_out_detail_header (vty, bgp, rm, (struct prefix_rd *)&rn->p,
+                                                       AFI_IP, safi);
+
+                          header = 0;
+                        }
+                      display++;
+
+                      if (pathtype == BGP_PATH_ALL ||
+                          (pathtype == BGP_PATH_BESTPATH && CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) ||
+                          (pathtype == BGP_PATH_MULTIPATH &&
+                           (CHECK_FLAG (ri->flags, BGP_INFO_MULTIPATH) || CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))))
+                        route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, safi);
+                    }
+
+                  bgp_unlock_node (rm);
+                }
+            }
+        }
+    }
+  else
+    {
+      header = 1;
+
+      if ((rn = bgp_node_match (rib, &match)) != NULL)
+        {
+          if (! prefix_check || rn->p.prefixlen == match.prefixlen)
+            {
+              for (ri = rn->info; ri; ri = ri->next)
+                {
+                  if (header)
+                    {
+                      route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi);
+                      header = 0;
+                    }
+                  display++;
+
+                  if (pathtype == BGP_PATH_ALL ||
+                      (pathtype == BGP_PATH_BESTPATH && CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) ||
+                      (pathtype == BGP_PATH_MULTIPATH &&
+                       (CHECK_FLAG (ri->flags, BGP_INFO_MULTIPATH) || CHECK_FLAG (ri->flags, BGP_INFO_SELECTED))))
+                    route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi);
+                }
+            }
+
+          bgp_unlock_node (rn);
+        }
+    }
+
+  if (! display)
+    {
+      vty_out (vty, "%% Network not in table%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Display specified route of Main RIB */
+static int
+bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str,
+               afi_t afi, safi_t safi, struct prefix_rd *prd,
+               int prefix_check, enum bgp_path_type pathtype)
+{
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str, 
+                                  afi, safi, prd, prefix_check, pathtype);
+}
+
+/* BGP route print out function. */
+DEFUN (show_ip_bgp,
+       show_ip_bgp_cmd,
+       "show ip bgp",
+       SHOW_STR
+       IP_STR
+       BGP_STR)
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_ip_bgp_ipv4,
+       show_ip_bgp_ipv4_cmd,
+       "show ip bgp ipv4 (unicast|multicast)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, bgp_show_type_normal,
+                     NULL);
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_ip_bgp_route,
+       show_ip_bgp_route_cmd,
+       "show ip bgp A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_route_pathtype,
+       show_ip_bgp_route_pathtype_cmd,
+       "show ip bgp A.B.C.D (bestpath|multipath)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[1], "b", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH);
+  else
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH);
+}
+
+DEFUN (show_bgp_ipv4_safi_route_pathtype,
+       show_bgp_ipv4_safi_route_pathtype_cmd,
+       "show bgp ipv4 (unicast|multicast) A.B.C.D (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_MULTIPATH);
+  else
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH);
+}
+
+DEFUN (show_ip_bgp_ipv4_route,
+       show_ip_bgp_ipv4_route_cmd,
+       "show ip bgp ipv4 (unicast|multicast) A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Network in the BGP routing table to display\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_vpnv4_all_route,
+       show_ip_bgp_vpnv4_all_route_cmd,
+       "show ip bgp vpnv4 all A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL);
+}
+
+
+DEFUN (show_ip_bgp_vpnv4_rd_route,
+       show_ip_bgp_vpnv4_rd_route_cmd,
+       "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Network in the BGP routing table to display\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_prefix,
+       show_ip_bgp_prefix_cmd,
+       "show ip bgp A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_prefix_pathtype,
+       show_ip_bgp_prefix_pathtype_cmd,
+       "show ip bgp A.B.C.D/M (bestpath|multipath)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[1], "b", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH);
+  else
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH);
+}
+
+DEFUN (show_ip_bgp_ipv4_prefix,
+       show_ip_bgp_ipv4_prefix_cmd,
+       "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_ipv4_prefix_pathtype,
+       show_ip_bgp_ipv4_prefix_pathtype_cmd,
+       "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M (bestpath|multipath)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_MULTIPATH);
+  else
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH);
+}
+
+ALIAS (show_ip_bgp_ipv4_prefix_pathtype,
+       show_bgp_ipv4_safi_prefix_pathtype_cmd,
+       "show bgp ipv4 (unicast|multicast) A.B.C.D/M (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+
+DEFUN (show_ip_bgp_vpnv4_all_prefix,
+       show_ip_bgp_vpnv4_all_prefix_cmd,
+       "show ip bgp vpnv4 all A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_vpnv4_rd_prefix,
+       show_ip_bgp_vpnv4_rd_prefix_cmd,
+       "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_view,
+       show_ip_bgp_view_cmd,
+       "show ip bgp view WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n")
+{
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+  return bgp_show (vty, bgp, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_ip_bgp_view_route,
+       show_ip_bgp_view_route_cmd,
+       "show ip bgp view WORD A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_view_prefix,
+       show_ip_bgp_view_prefix_cmd,
+       "show ip bgp view WORD A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp,
+       show_bgp_cmd,
+       "show bgp",
+       SHOW_STR
+       BGP_STR)
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal,
+                   NULL);
+}
+
+ALIAS (show_bgp,
+       show_bgp_ipv6_cmd,
+       "show bgp ipv6",
+       SHOW_STR
+       BGP_STR
+       "Address family\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp,
+       show_ipv6_bgp_cmd,
+       "show ipv6 bgp",
+       SHOW_STR
+       IP_STR
+       BGP_STR)
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal,
+                   NULL);
+}
+
+DEFUN (show_bgp_route,
+       show_bgp_route_cmd,
+       "show bgp X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_safi,
+       show_bgp_ipv4_safi_cmd,
+       "show bgp ipv4 (unicast|multicast)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, bgp_show_type_normal,
+                     NULL);
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_bgp_ipv4_safi_route,
+       show_bgp_ipv4_safi_route_cmd,
+       "show bgp ipv4 (unicast|multicast) A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Network in the BGP routing table to display\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_route_pathtype,
+       show_bgp_route_pathtype_cmd,
+       "show bgp X:X::X:X (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Network in the BGP routing table to display\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[1], "b", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH);
+  else
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH);
+}
+
+ALIAS (show_bgp_route_pathtype,
+       show_bgp_ipv6_route_pathtype_cmd,
+       "show bgp ipv6 X:X::X:X (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Network in the BGP routing table to display\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+
+DEFUN (show_bgp_ipv6_safi_route_pathtype,
+       show_bgp_ipv6_safi_route_pathtype_cmd,
+       "show bgp ipv6 (unicast|multicast) X:X::X:X (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Network in the BGP routing table to display\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_MULTIPATH);
+  else
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH);
+}
+
+DEFUN (show_bgp_ipv4_vpn_route,
+       show_bgp_ipv4_vpn_route_cmd,
+       "show bgp ipv4 vpn A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_vpn_route,
+       show_bgp_ipv6_vpn_route_cmd,
+       "show bgp ipv6 vpn X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_vpn_rd_route,
+       show_bgp_ipv4_vpn_rd_route_cmd,
+       "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Network in the BGP routing table to display\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_vpn_rd_route,
+       show_bgp_ipv6_vpn_rd_route_cmd,
+       "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Network in the BGP routing table to display\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_prefix_pathtype,
+       show_bgp_prefix_pathtype_cmd,
+       "show bgp X:X::X:X/M (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "IPv6 prefix <network>/<length>\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[1], "b", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH);
+  else
+    return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH);
+}
+
+ALIAS (show_bgp_prefix_pathtype,
+       show_bgp_ipv6_prefix_pathtype_cmd,
+       "show bgp ipv6 X:X::X:X/M (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "IPv6 prefix <network>/<length>\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+
+DEFUN (show_bgp_ipv6_safi_prefix_pathtype,
+       show_bgp_ipv6_safi_prefix_pathtype_cmd,
+       "show bgp ipv6 (unicast|multicast) X:X::X:X/M (bestpath|multipath)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Display only the bestpath\n"
+       "Display only multipaths\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_MULTIPATH);
+  else
+    if (strncmp (argv[2], "b", 1) == 0)
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH);
+    else
+      return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH);
+}
+
+DEFUN (show_bgp_ipv4_encap_route,
+       show_bgp_ipv4_encap_route_cmd,
+       "show bgp ipv4 encap A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display ENCAP NLRI specific information\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_encap_route,
+       show_bgp_ipv6_encap_route_cmd,
+       "show bgp ipv6 encap X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       IP6_STR
+       "Display ENCAP NLRI specific information\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_ENCAP, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_safi_rd_route,
+       show_bgp_ipv4_safi_rd_route_cmd,
+       "show bgp ipv4 (encap|vpn) rd ASN:nn_or_IP-address:nn A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Network in the BGP routing table to display\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  ret = str2prefix_rd (argv[1], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[2], AFI_IP, safi, &prd, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_safi_rd_route,
+       show_bgp_ipv6_safi_rd_route_cmd,
+       "show bgp ipv6 (encap|vpn) rd ASN:nn_or_IP-address:nn X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "Network in the BGP routing table to display\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  ret = str2prefix_rd (argv[1], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[2], AFI_IP6, SAFI_ENCAP, &prd, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_prefix,
+       show_bgp_ipv4_prefix_cmd,
+       "show bgp ipv4 A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_safi_prefix,
+       show_bgp_ipv4_safi_prefix_cmd,
+       "show bgp ipv4 (unicast|multicast) A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_vpn_prefix,
+       show_bgp_ipv4_vpn_prefix_cmd,
+       "show bgp ipv4 vpn A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display VPN NLRI specific information\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_vpn_prefix,
+       show_bgp_ipv6_vpn_prefix_cmd,
+       "show bgp ipv6 vpn X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Display VPN NLRI specific information\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_encap_prefix,
+       show_bgp_ipv4_encap_prefix_cmd,
+       "show bgp ipv4 encap A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display ENCAP NLRI specific information\n"
+       "Display information about ENCAP NLRIs\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_encap_prefix,
+       show_bgp_ipv6_encap_prefix_cmd,
+       "show bgp ipv6 encap X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display ENCAP NLRI specific information\n"
+       "Display information about ENCAP NLRIs\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv4_safi_rd_prefix,
+       show_bgp_ipv4_safi_rd_prefix_cmd,
+       "show bgp ipv4 (encap|vpn) rd ASN:nn_or_IP-address:nn A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = str2prefix_rd (argv[1], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[2], AFI_IP, safi, &prd, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_safi_rd_prefix,
+       show_bgp_ipv6_safi_rd_prefix_cmd,
+       "show bgp ipv6 (encap|vpn) rd ASN:nn_or_IP-address:nn X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display information for a route distinguisher\n"
+       "ENCAP Route Distinguisher\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = str2prefix_rd (argv[1], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route (vty, NULL, argv[2], AFI_IP6, safi, &prd, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_afi_safi_view,
+       show_bgp_afi_safi_view_cmd,
+       "show bgp view WORD (ipv4|ipv6) (encap|mulicast|unicast|vpn)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "BGP view name\n"
+       "Address Family\n"
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       )
+{
+  struct bgp *bgp;
+  safi_t       safi;
+  afi_t                afi;
+
+  if (bgp_parse_afi(argv[1], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (bgp_parse_safi(argv[2], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_bgp_view_afi_safi_route,
+       show_bgp_view_afi_safi_route_cmd,
+       "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address Family\n"
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Network in the BGP routing table to display\n")
+{
+  safi_t       safi;
+  afi_t                afi;
+
+  if (bgp_parse_afi(argv[1], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (bgp_parse_safi(argv[2], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route (vty, argv[0], argv[3], afi, safi, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_afi_safi_prefix,
+       show_bgp_view_afi_safi_prefix_cmd,
+       "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address Family\n"
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  safi_t       safi;
+  afi_t                afi;
+
+  if (bgp_parse_afi(argv[1], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (bgp_parse_safi(argv[2], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route (vty, argv[0], argv[3], afi, safi, NULL, 1, BGP_PATH_ALL);
+}
+
+/* new001 */
+DEFUN (show_bgp_afi,
+       show_bgp_afi_cmd,
+       "show bgp (ipv4|ipv6)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family\n")
+{
+  afi_t        afi;
+
+  if (bgp_parse_afi(argv[0], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show (vty, NULL, afi, SAFI_UNICAST, bgp_show_type_normal,
+                   NULL);
+}
+
+DEFUN (show_bgp_ipv6_safi,
+       show_bgp_ipv6_safi_cmd,
+       "show bgp ipv6 (unicast|multicast)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal,
+                     NULL);
+
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_bgp_ipv6_route,
+       show_bgp_ipv6_route_cmd,
+       "show bgp ipv6 X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_ipv6_safi_route,
+       show_bgp_ipv6_safi_route_cmd,
+       "show bgp ipv6 (unicast|multicast) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Network in the BGP routing table to display\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_route,
+       show_ipv6_bgp_route_cmd,
+       "show ipv6 bgp X:X::X:X",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_prefix,
+       show_bgp_prefix_cmd,
+       "show bgp X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "IPv6 prefix <network>/<length>\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+
+/* new002 */
+DEFUN (show_bgp_ipv6_prefix,
+       show_bgp_ipv6_prefix_cmd,
+       "show bgp ipv6 X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+DEFUN (show_bgp_ipv6_safi_prefix,
+       show_bgp_ipv6_safi_prefix_cmd,
+       "show bgp ipv6 (unicast|multicast) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL);
+
+  return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_prefix,
+       show_ipv6_bgp_prefix_cmd,
+       "show ipv6 bgp X:X::X:X/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view,
+       show_bgp_view_cmd,
+       "show bgp view WORD",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n")
+{
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+  
+  return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+
+DEFUN (show_bgp_view_ipv6,
+       show_bgp_view_ipv6_cmd,
+       "show bgp view WORD ipv6",
+       SHOW_STR
+       BGP_STR             
+       "BGP view\n"
+       "View name\n"
+       "Address family\n")
+{
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+  
+  return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL);
+}
+  
+DEFUN (show_bgp_view_route,
+       show_bgp_view_route_cmd,
+       "show bgp view WORD X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_ipv6_route,
+       show_bgp_view_ipv6_route_cmd,
+       "show bgp view WORD ipv6 X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp,
+       show_ipv6_mbgp_cmd,
+       "show ipv6 mbgp",
+       SHOW_STR
+       IP_STR
+       MBGP_STR)
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal,
+                   NULL);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_route,
+       show_ipv6_mbgp_route_cmd,
+       "show ipv6 mbgp X:X::X:X",
+       SHOW_STR
+       IP_STR
+       MBGP_STR
+       "Network in the MBGP routing table to display\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_prefix,
+       show_ipv6_mbgp_prefix_cmd,
+       "show ipv6 mbgp X:X::X:X/M",
+       SHOW_STR
+       IP_STR
+       MBGP_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_prefix,
+       show_bgp_view_prefix_cmd,
+       "show bgp view WORD X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"       
+       "IPv6 prefix <network>/<length>\n")
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_ipv6_prefix,
+       show_bgp_view_ipv6_prefix_cmd,
+       "show bgp view WORD ipv6 X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "IPv6 prefix <network>/<length>\n")  
+{
+  return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+static int
+bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi,
+                safi_t safi, enum bgp_show_type type)
+{
+  int i;
+  struct buffer *b;
+  char *regstr;
+  int first;
+  regex_t *regex;
+  int rc;
+  
+  first = 0;
+  b = buffer_new (1024);
+  for (i = 0; i < argc; i++)
+    {
+      if (first)
+       buffer_putc (b, ' ');
+      else
+       {
+         if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0))
+           continue;
+         first = 1;
+       }
+
+      buffer_putstr (b, argv[i]);
+    }
+  buffer_putc (b, '\0');
+
+  regstr = buffer_getstr (b);
+  buffer_free (b);
+
+  regex = bgp_regcomp (regstr);
+  XFREE(MTYPE_TMP, regstr);
+  if (! regex)
+    {
+      vty_out (vty, "Can't compile regexp %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rc = bgp_show (vty, NULL, afi, safi, type, regex);
+  bgp_regex_free (regex);
+  return rc;
+}
+
+
+DEFUN (show_ip_bgp_regexp, 
+       show_ip_bgp_regexp_cmd,
+       "show ip bgp regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST,
+                         bgp_show_type_regexp);
+}
+
+DEFUN (show_ip_bgp_flap_regexp, 
+       show_ip_bgp_flap_regexp_cmd,
+       "show ip bgp flap-statistics regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST,
+                         bgp_show_type_flap_regexp);
+}
+
+ALIAS (show_ip_bgp_flap_regexp,
+       show_ip_bgp_damp_flap_regexp_cmd,
+       "show ip bgp dampening flap-statistics regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+
+DEFUN (show_ip_bgp_ipv4_regexp, 
+       show_ip_bgp_ipv4_regexp_cmd,
+       "show ip bgp ipv4 (unicast|multicast) regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_MULTICAST,
+                           bgp_show_type_regexp);
+
+  return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST,
+                         bgp_show_type_regexp);
+}
+
+DEFUN (show_bgp_regexp, 
+       show_bgp_regexp_cmd,
+       "show bgp regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST,
+                         bgp_show_type_regexp);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_regexp, 
+       show_ipv6_bgp_regexp_cmd,
+       "show ipv6 bgp regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST,
+                         bgp_show_type_regexp);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_regexp, 
+       show_ipv6_mbgp_regexp_cmd,
+       "show ipv6 mbgp regexp .LINE",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the MBGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_MULTICAST,
+                         bgp_show_type_regexp);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_regexp,
+       show_bgp_ipv4_safi_flap_regexp_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+    return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP, safi,
+       bgp_show_type_flap_regexp);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_regexp,
+       show_bgp_ipv4_safi_damp_flap_regexp_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_regexp,
+       show_bgp_ipv6_safi_flap_regexp_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+    return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP6, safi,
+       bgp_show_type_flap_regexp);
+}
+
+ALIAS (show_bgp_ipv6_safi_flap_regexp,
+       show_bgp_ipv6_safi_damp_flap_regexp_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+
+DEFUN (show_bgp_ipv4_safi_regexp, 
+       show_bgp_ipv4_safi_regexp_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP, safi,
+                         bgp_show_type_regexp);
+}
+
+DEFUN (show_bgp_ipv6_safi_regexp, 
+       show_bgp_ipv6_safi_regexp_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP6, safi,
+                         bgp_show_type_regexp);
+}
+
+DEFUN (show_bgp_ipv6_regexp, 
+       show_bgp_ipv6_regexp_cmd,
+       "show bgp ipv6 regexp .LINE",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the AS path regular expression\n"
+       "A regular-expression to match the BGP AS paths\n")
+{
+  return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST,
+                         bgp_show_type_regexp);
+}
+
+static int
+bgp_show_prefix_list (struct vty *vty, const char *prefix_list_str, afi_t afi,
+                     safi_t safi, enum bgp_show_type type)
+{
+  struct prefix_list *plist;
+
+  plist = prefix_list_lookup (afi, prefix_list_str);
+  if (plist == NULL)
+    {
+      vty_out (vty, "%% %s is not a valid prefix-list name%s",
+               prefix_list_str, VTY_NEWLINE);      
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, NULL, afi, safi, type, plist);
+}
+DEFUN (show_ip_bgp_prefix_list, 
+       show_ip_bgp_prefix_list_cmd,
+       "show ip bgp prefix-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_prefix_list);
+}
+
+DEFUN (show_ip_bgp_flap_prefix_list, 
+       show_ip_bgp_flap_prefix_list_cmd,
+       "show ip bgp flap-statistics prefix-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_flap_prefix_list);
+}
+
+ALIAS (show_ip_bgp_flap_prefix_list,
+       show_ip_bgp_damp_flap_prefix_list_cmd,
+       "show ip bgp dampening flap-statistics prefix-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+
+DEFUN (show_ip_bgp_ipv4_prefix_list, 
+       show_ip_bgp_ipv4_prefix_list_cmd,
+       "show ip bgp ipv4 (unicast|multicast) prefix-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_MULTICAST,
+                                bgp_show_type_prefix_list);
+
+  return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_prefix_list);
+}
+
+DEFUN (show_bgp_prefix_list, 
+       show_bgp_prefix_list_cmd,
+       "show bgp prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Display routes conforming to the prefix-list\n"
+       "IPv6 prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                              bgp_show_type_prefix_list);
+}
+
+ALIAS (show_bgp_prefix_list, 
+       show_bgp_ipv6_prefix_list_cmd,
+       "show bgp ipv6 prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes conforming to the prefix-list\n"
+       "IPv6 prefix-list name\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_prefix_list, 
+       show_ipv6_bgp_prefix_list_cmd,
+       "show ipv6 bgp prefix-list WORD",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the prefix-list\n"
+       "IPv6 prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                              bgp_show_type_prefix_list);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_prefix_list, 
+       show_ipv6_mbgp_prefix_list_cmd,
+       "show ipv6 mbgp prefix-list WORD",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the prefix-list\n"
+       "IPv6 prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST,
+                              bgp_show_type_prefix_list);
+}
+
+DEFUN (show_bgp_ipv4_prefix_list, 
+       show_bgp_ipv4_prefix_list_cmd,
+       "show bgp ipv4 prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_prefix_list);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_prefix_list, 
+       show_bgp_ipv4_safi_flap_prefix_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_list (vty, argv[1], AFI_IP, safi,
+                              bgp_show_type_flap_prefix_list);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_prefix_list, 
+       show_bgp_ipv4_safi_damp_flap_prefix_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_prefix_list, 
+       show_bgp_ipv6_safi_flap_prefix_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_list (vty, argv[1], AFI_IP6, safi,
+                              bgp_show_type_flap_prefix_list);
+}
+ALIAS (show_bgp_ipv6_safi_flap_prefix_list, 
+       show_bgp_ipv6_safi_damp_flap_prefix_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+
+DEFUN (show_bgp_ipv4_safi_prefix_list, 
+       show_bgp_ipv4_safi_prefix_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_list (vty, argv[1], AFI_IP, safi,
+                              bgp_show_type_prefix_list);
+}
+
+DEFUN (show_bgp_ipv6_safi_prefix_list, 
+       show_bgp_ipv6_safi_prefix_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) prefix-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the prefix-list\n"
+       "IP prefix-list name\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_list (vty, argv[1], AFI_IP6, safi,
+                              bgp_show_type_prefix_list);
+}
+
+static int
+bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi,
+                     safi_t safi, enum bgp_show_type type)
+{
+  struct as_list *as_list;
+
+  as_list = as_list_lookup (filter);
+  if (as_list == NULL)
+    {
+      vty_out (vty, "%% %s is not a valid AS-path access-list name%s", filter, VTY_NEWLINE);       
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, NULL, afi, safi, type, as_list);
+}
+
+DEFUN (show_ip_bgp_filter_list, 
+       show_ip_bgp_filter_list_cmd,
+       "show ip bgp filter-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+DEFUN (show_ip_bgp_flap_filter_list, 
+       show_ip_bgp_flap_filter_list_cmd,
+       "show ip bgp flap-statistics filter-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_flap_filter_list);
+}
+
+ALIAS (show_ip_bgp_flap_filter_list, 
+       show_ip_bgp_damp_flap_filter_list_cmd,
+       "show ip bgp dampening flap-statistics filter-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+
+DEFUN (show_ip_bgp_ipv4_filter_list, 
+       show_ip_bgp_ipv4_filter_list_cmd,
+       "show ip bgp ipv4 (unicast|multicast) filter-list WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_MULTICAST,
+                                bgp_show_type_filter_list);
+  
+  return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+DEFUN (show_bgp_filter_list, 
+       show_bgp_filter_list_cmd,
+       "show bgp filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_filter_list, 
+       show_ipv6_bgp_filter_list_cmd,
+       "show ipv6 bgp filter-list WORD",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_filter_list, 
+       show_ipv6_mbgp_filter_list_cmd,
+       "show ipv6 mbgp filter-list WORD",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST,
+                              bgp_show_type_filter_list);
+}
+
+DEFUN (show_ip_bgp_dampening_info,
+       show_ip_bgp_dampening_params_cmd,
+       "show ip bgp dampening parameters",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display detail of configured dampening parameters\n")
+{
+    return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_filter_list, 
+       show_bgp_ipv4_filter_list_cmd,
+       "show bgp ipv4 filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_filter_list, 
+       show_bgp_ipv4_safi_flap_filter_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_filter_list (vty, argv[1], AFI_IP, safi,
+                              bgp_show_type_flap_filter_list);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_filter_list, 
+       show_bgp_ipv4_safi_damp_flap_filter_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_filter_list, 
+       show_bgp_ipv6_safi_flap_filter_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_filter_list (vty, argv[1], AFI_IP6, safi,
+                              bgp_show_type_flap_filter_list);
+}
+ALIAS (show_bgp_ipv6_safi_flap_filter_list, 
+       show_bgp_ipv6_safi_damp_flap_filter_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+
+DEFUN (show_bgp_ipv4_safi_filter_list, 
+       show_bgp_ipv4_safi_filter_list_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_filter_list (vty, argv[1], AFI_IP, safi,
+                                bgp_show_type_filter_list);
+}
+
+DEFUN (show_bgp_ipv6_safi_filter_list, 
+       show_bgp_ipv6_safi_filter_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_filter_list (vty, argv[1], AFI_IP6, safi,
+                                bgp_show_type_filter_list);
+}
+
+DEFUN (show_bgp_ipv6_filter_list, 
+       show_bgp_ipv6_filter_list_cmd,
+       "show bgp ipv6 filter-list WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes conforming to the filter-list\n"
+       "Regular expression access list name\n")
+{
+  return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                              bgp_show_type_filter_list);
+}
+
+
+DEFUN (show_ip_bgp_ipv4_dampening_parameters, 
+       show_ip_bgp_ipv4_dampening_parameters_cmd,
+       "show ip bgp ipv4 (unicast|multicast) dampening parameters",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display detail of configured dampening parameters\n")
+{
+    if (strncmp(argv[0], "m", 1) == 0)
+      return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_MULTICAST);
+
+    return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST);
+}
+
+
+DEFUN (show_ip_bgp_ipv4_dampening_flap_stats,
+       show_ip_bgp_ipv4_dampening_flap_stats_cmd,
+       "show ip bgp ipv4 (unicast|multicast) dampening flap-statistics",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n")
+{
+    if (strncmp(argv[0], "m", 1) == 0)      
+      return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                     bgp_show_type_flap_statistics, NULL);
+
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                 bgp_show_type_flap_statistics, NULL);
+}
+
+DEFUN (show_ip_bgp_ipv4_dampening_dampd_paths, 
+       show_ip_bgp_ipv4_dampening_dampd_paths_cmd,
+       "show ip bgp ipv4 (unicast|multicast) dampening dampened-paths",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display paths suppressed due to dampening\n")
+{
+    if (strncmp(argv[0], "m", 1) == 0)      
+      return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                     bgp_show_type_dampend_paths, NULL);
+
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                 bgp_show_type_dampend_paths, NULL);
+}
+
+static int
+bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi,
+                   safi_t safi, enum bgp_show_type type)
+{
+  struct route_map *rmap;
+
+  rmap = route_map_lookup_by_name (rmap_str);
+  if (! rmap)
+    {
+      vty_out (vty, "%% %s is not a valid route-map name%s",
+              rmap_str, VTY_NEWLINE);      
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, NULL, afi, safi, type, rmap);
+}
+
+DEFUN (show_ip_bgp_route_map, 
+       show_ip_bgp_route_map_cmd,
+       "show ip bgp route-map WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_ip_bgp_flap_route_map, 
+       show_ip_bgp_flap_route_map_cmd,
+       "show ip bgp flap-statistics route-map WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                            bgp_show_type_flap_route_map);
+}
+
+ALIAS (show_ip_bgp_flap_route_map, 
+       show_ip_bgp_damp_flap_route_map_cmd,
+       "show ip bgp dampening flap-statistics route-map WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+
+DEFUN (show_ip_bgp_ipv4_route_map, 
+       show_ip_bgp_ipv4_route_map_cmd,
+       "show ip bgp ipv4 (unicast|multicast) route-map WORD",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_MULTICAST,
+                              bgp_show_type_route_map);
+
+  return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_UNICAST,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_bgp_route_map, 
+       show_bgp_route_map_cmd,
+       "show bgp route-map WORD",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  return bgp_show_route_map (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_ip_bgp_cidr_only,
+       show_ip_bgp_cidr_only_cmd,
+       "show ip bgp cidr-only",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display only routes with non-natural netmasks\n")
+{
+    return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_cidr_only, NULL);
+}
+
+DEFUN (show_ip_bgp_flap_cidr_only,
+       show_ip_bgp_flap_cidr_only_cmd,
+       "show ip bgp flap-statistics cidr-only",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Display only routes with non-natural netmasks\n")
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                  bgp_show_type_flap_cidr_only, NULL);
+}
+
+ALIAS (show_ip_bgp_flap_cidr_only,
+       show_ip_bgp_damp_flap_cidr_only_cmd,
+       "show ip bgp dampening flap-statistics cidr-only",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display only routes with non-natural netmasks\n")
+
+DEFUN (show_ip_bgp_ipv4_cidr_only,
+       show_ip_bgp_ipv4_cidr_only_cmd,
+       "show ip bgp ipv4 (unicast|multicast) cidr-only",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display only routes with non-natural netmasks\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                    bgp_show_type_cidr_only, NULL);
+
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_cidr_only, NULL);
+}
+
+DEFUN (show_ip_bgp_community_all,
+       show_ip_bgp_community_all_cmd,
+       "show ip bgp community",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_community_all, NULL);
+}
+
+DEFUN (show_ip_bgp_ipv4_community_all,
+       show_ip_bgp_ipv4_community_all_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                    bgp_show_type_community_all, NULL);
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                  bgp_show_type_community_all, NULL);
+}
+
+DEFUN (show_bgp_community_all,
+       show_bgp_community_all_cmd,
+       "show bgp community",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST,
+                  bgp_show_type_community_all, NULL);
+}
+
+ALIAS (show_bgp_community_all,
+       show_bgp_ipv6_community_all_cmd,
+       "show bgp ipv6 community",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_community_all,
+       show_ipv6_bgp_community_all_cmd,
+       "show ipv6 bgp community",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST,
+                  bgp_show_type_community_all, NULL);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_community_all,
+       show_ipv6_mbgp_community_all_cmd,
+       "show ipv6 mbgp community",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST,
+                  bgp_show_type_community_all, NULL);
+}
+
+/* large-community */
+DEFUN (show_ip_bgp_lcommunity_all,
+       show_ip_bgp_lcommunity_all_cmd,
+       "show ip bgp large-community",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_lcommunity_all, NULL);
+}
+
+DEFUN (show_ip_bgp_ipv4_lcommunity_all,
+       show_ip_bgp_ipv4_lcommunity_all_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                    bgp_show_type_lcommunity_all, NULL);
+
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                  bgp_show_type_lcommunity_all, NULL);
+}
+
+DEFUN (show_bgp_lcommunity_all,
+       show_bgp_lcommunity_all_cmd,
+       "show bgp large-community",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST,
+                  bgp_show_type_lcommunity_all, NULL);
+}
+
+ALIAS (show_bgp_lcommunity_all,
+       show_bgp_ipv6_lcommunity_all_cmd,
+       "show bgp ipv6 large-community",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-communities\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_lcommunity_all,
+       show_ipv6_bgp_lcommunity_all_cmd,
+       "show ipv6 bgp large-community",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST,
+                  bgp_show_type_lcommunity_all, NULL);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_lcommunity_all,
+       show_ipv6_mbgp_lcommunity_all_cmd,
+       "show ipv6 mbgp large-community",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-communities\n")
+{
+  return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST,
+                  bgp_show_type_lcommunity_all, NULL);
+}
+
+
+DEFUN (show_bgp_ipv4_route_map, 
+       show_bgp_ipv4_route_map_cmd,
+       "show bgp ipv4 route-map WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_route_map, 
+       show_bgp_ipv4_safi_flap_route_map_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics route-map WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route_map (vty, argv[1], AFI_IP, safi,
+                            bgp_show_type_flap_route_map);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_route_map, 
+       show_bgp_ipv4_safi_damp_flap_route_map_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics route-map WORD",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_route_map, 
+       show_bgp_ipv6_safi_flap_route_map_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics route-map WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route_map (vty, argv[1], AFI_IP6, safi,
+                            bgp_show_type_flap_route_map);
+}
+ALIAS (show_bgp_ipv6_safi_flap_route_map, 
+       show_bgp_ipv6_safi_damp_flap_route_map_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics route-map WORD",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+
+DEFUN (show_bgp_ipv4_safi_route_map, 
+       show_bgp_ipv4_safi_route_map_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) route-map WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route_map (vty, argv[1], AFI_IP, safi,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_bgp_ipv6_safi_route_map, 
+       show_bgp_ipv6_safi_route_map_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) route-map WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  safi_t       safi;
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_route_map (vty, argv[1], AFI_IP6, safi,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_bgp_ipv6_route_map, 
+       show_bgp_ipv6_route_map_cmd,
+       "show bgp ipv6 route-map WORD",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the route-map\n"
+       "A route-map to match on\n")
+{
+  return bgp_show_route_map (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                            bgp_show_type_route_map);
+}
+
+DEFUN (show_bgp_ipv4_cidr_only,
+       show_bgp_ipv4_cidr_only_cmd,
+       "show bgp ipv4 cidr-only",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display only routes with non-natural netmasks\n")
+{
+    return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_cidr_only, NULL);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_cidr_only,
+       show_bgp_ipv4_safi_flap_cidr_only_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics cidr-only",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display flap statistics of routes\n"
+       "Display only routes with non-natural netmasks\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_flap_cidr_only, NULL);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_cidr_only,
+       show_bgp_ipv4_safi_damp_flap_cidr_only_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics cidr-only",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Display only routes with non-natural netmasks\n")
+
+DEFUN (show_bgp_ipv4_safi_cidr_only,
+       show_bgp_ipv4_safi_cidr_only_cmd,
+       "show bgp ipv4 (unicast|multicast) cidr-only",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display only routes with non-natural netmasks\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST,
+                    bgp_show_type_cidr_only, NULL);
+
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                    bgp_show_type_cidr_only, NULL);
+}
+
+/* new046 */
+DEFUN (show_bgp_afi_safi_community_all,
+       show_bgp_afi_safi_community_all_cmd,
+       "show bgp (ipv4|ipv6) (encap|multicast|unicast|vpn) community",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n")
+{
+  safi_t       safi;
+  afi_t                afi;
+
+  if (bgp_parse_afi(argv[0], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (bgp_parse_safi(argv[1], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show (vty, NULL, afi, safi, bgp_show_type_community_all, NULL);
+}
+DEFUN (show_bgp_afi_community_all,
+       show_bgp_afi_community_all_cmd,
+       "show bgp (ipv4|ipv6) community",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family\n"
+       "Display routes matching the communities\n")
+{
+  afi_t                afi;
+  safi_t       safi = SAFI_UNICAST;
+
+  if (bgp_parse_afi(argv[0], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show (vty, NULL, afi, safi, bgp_show_type_community_all, NULL);
+}
+
+static int
+bgp_show_community (struct vty *vty, const char *view_name, int argc,
+                   const char **argv, int exact, afi_t afi, safi_t safi)
+{
+  struct community *com;
+  struct buffer *b;
+  struct bgp *bgp;
+  int i, rv;
+  char *str;
+  int first = 0;
+
+  /* BGP structure lookup */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  b = buffer_new (1024);
+  for (i = 0; i < argc; i++)
+    {
+      if (first)
+        buffer_putc (b, ' ');
+      else
+       {
+         if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0))
+           continue;
+         first = 1;
+       }
+      
+      buffer_putstr (b, argv[i]);
+    }
+  buffer_putc (b, '\0');
+
+  str = buffer_getstr (b);
+  buffer_free (b);
+
+  com = community_str2com (str);
+  XFREE (MTYPE_TMP, str);
+  if (! com)
+    {
+      vty_out (vty, "%% Community malformed: %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rv = bgp_show (vty, bgp, afi, safi,
+                 (exact ? bgp_show_type_community_exact :
+                         bgp_show_type_community), com);
+  community_free(com);
+  return rv;
+}
+
+DEFUN (show_ip_bgp_community,
+       show_ip_bgp_community_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_community,
+       show_ip_bgp_community2_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_ip_bgp_community,
+       show_ip_bgp_community3_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_ip_bgp_community,
+       show_ip_bgp_community4_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_ip_bgp_ipv4_community,
+       show_ip_bgp_ipv4_community_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_ipv4_community,
+       show_ip_bgp_ipv4_community2_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_ip_bgp_ipv4_community,
+       show_ip_bgp_ipv4_community3_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_ip_bgp_ipv4_community,
+       show_ip_bgp_ipv4_community4_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_ip_bgp_community_exact,
+       show_ip_bgp_community_exact_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_community_exact,
+       show_ip_bgp_community2_exact_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_ip_bgp_community_exact,
+       show_ip_bgp_community3_exact_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_ip_bgp_community_exact,
+       show_ip_bgp_community4_exact_cmd,
+       "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+DEFUN (show_ip_bgp_ipv4_community_exact,
+       show_ip_bgp_ipv4_community_exact_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_ipv4_community_exact,
+       show_ip_bgp_ipv4_community2_exact_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_ip_bgp_ipv4_community_exact,
+       show_ip_bgp_ipv4_community3_exact_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+       
+ALIAS (show_ip_bgp_ipv4_community_exact,
+       show_ip_bgp_ipv4_community4_exact_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+DEFUN (show_bgp_community,
+       show_bgp_community_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_community,
+       show_bgp_ipv6_community_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_community2_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_ipv6_community2_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_community3_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_ipv6_community3_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_community4_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_community,
+       show_bgp_ipv6_community4_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_community,
+       show_ipv6_bgp_community_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_bgp_community,
+       show_ipv6_bgp_community2_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+ALIAS (show_ipv6_bgp_community,
+       show_ipv6_bgp_community3_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+ALIAS (show_ipv6_bgp_community,
+       show_ipv6_bgp_community4_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_bgp_community_exact,
+       show_bgp_community_exact_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_community_exact_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_community2_exact_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_community2_exact_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_community3_exact_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_community3_exact_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_community4_exact_cmd,
+       "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_community4_exact_cmd,
+       "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+/* old command */
+DEFUN (show_ipv6_bgp_community_exact,
+       show_ipv6_bgp_community_exact_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_bgp_community_exact,
+       show_ipv6_bgp_community2_exact_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+/* old command */
+ALIAS (show_ipv6_bgp_community_exact,
+       show_ipv6_bgp_community3_exact_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+/* old command */
+ALIAS (show_ipv6_bgp_community_exact,
+       show_ipv6_bgp_community4_exact_cmd,
+       "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+/* old command */
+DEFUN (show_ipv6_mbgp_community,
+       show_ipv6_mbgp_community_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_MULTICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community,
+       show_ipv6_mbgp_community2_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community,
+       show_ipv6_mbgp_community3_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community,
+       show_ipv6_mbgp_community4_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+/* old command */
+DEFUN (show_ipv6_mbgp_community_exact,
+       show_ipv6_mbgp_community_exact_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_MULTICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community_exact,
+       show_ipv6_mbgp_community2_exact_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community_exact,
+       show_ipv6_mbgp_community3_exact_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_community_exact,
+       show_ipv6_mbgp_community4_exact_cmd,
+       "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+DEFUN (show_bgp_ipv4_community,
+       show_bgp_ipv4_community_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_community,
+       show_bgp_ipv4_community2_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_bgp_ipv4_community,
+       show_bgp_ipv4_community3_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_bgp_ipv4_community,
+       show_bgp_ipv4_community4_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_bgp_ipv4_safi_community,
+       show_bgp_ipv4_safi_community_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_safi_community,
+       show_bgp_ipv4_safi_community2_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_bgp_ipv4_safi_community,
+       show_bgp_ipv4_safi_community3_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_bgp_ipv4_safi_community,
+       show_bgp_ipv4_safi_community4_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_bgp_view_afi_safi_community_all,
+       show_bgp_view_afi_safi_community_all_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n")
+{
+  int afi;
+  int safi;
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+    {
+      vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+  safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_community_all, NULL);
+}
+
+DEFUN (show_bgp_view_afi_safi_community,
+       show_bgp_view_afi_safi_community_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  int afi;
+  int safi;
+
+  afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+  safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  return bgp_show_community (vty, argv[0], argc-3, &argv[3], 0, afi, safi);
+}
+
+ALIAS (show_bgp_view_afi_safi_community,
+       show_bgp_view_afi_safi_community2_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_view_afi_safi_community,
+       show_bgp_view_afi_safi_community3_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_view_afi_safi_community,
+       show_bgp_view_afi_safi_community4_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+DEFUN (show_bgp_ipv4_community_exact,
+       show_bgp_ipv4_community_exact_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_community_exact,
+       show_bgp_ipv4_community2_exact_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_ipv4_community_exact,
+       show_bgp_ipv4_community3_exact_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_ipv4_community_exact,
+       show_bgp_ipv4_community4_exact_cmd,
+       "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+DEFUN (show_bgp_ipv4_safi_community4_exact,
+       show_bgp_ipv4_safi_community_exact_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_safi_community4_exact,
+       show_bgp_ipv4_safi_community2_exact_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_ipv4_safi_community4_exact,
+       show_bgp_ipv4_safi_community3_exact_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+       
+ALIAS (show_bgp_ipv4_safi_community4_exact,
+       show_bgp_ipv4_safi_community4_exact_cmd,
+       "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+DEFUN (show_bgp_ipv6_safi_community,
+       show_bgp_ipv6_safi_community_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_community (vty, NULL, argc-1, argv+1, 0, AFI_IP6, safi);
+}
+
+ALIAS (show_bgp_ipv6_safi_community,
+       show_bgp_ipv6_safi_community2_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+       
+ALIAS (show_bgp_ipv6_safi_community,
+       show_bgp_ipv6_safi_community3_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+ALIAS (show_bgp_ipv6_safi_community,
+       show_bgp_ipv6_safi_community4_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n")
+
+
+DEFUN (show_bgp_ipv6_safi_community_exact,
+       show_bgp_ipv6_safi_community_exact_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_community (vty, NULL, argc-1, argv+1, 1, AFI_IP6, safi);
+}
+
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_safi_community2_exact_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_safi_community3_exact_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+ALIAS (show_bgp_community_exact,
+       show_bgp_ipv6_safi_community4_exact_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the communities\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "community number\n"
+       "Do not send outside local AS (well-known community)\n"
+       "Do not advertise to any peer (well-known community)\n"
+       "Do not export to next AS (well-known community)\n"
+       "Exact match of the communities")
+
+static int
+bgp_show_community_list (struct vty *vty, const char *com, int exact,
+                        afi_t afi, safi_t safi)
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, com, COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    {
+      vty_out (vty, "%% %s is not a valid community-list name%s", com,
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, NULL, afi, safi,
+                   (exact ? bgp_show_type_community_list_exact :
+                           bgp_show_type_community_list), list);
+}
+
+DEFUN (show_ip_bgp_community_list,
+       show_ip_bgp_community_list_cmd,
+       "show ip bgp community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  return bgp_show_community_list (vty, argv[0], 0, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_ipv4_community_list,
+       show_ip_bgp_ipv4_community_list_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST);
+  
+  return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_community_list_exact,
+       show_ip_bgp_community_list_exact_cmd,
+       "show ip bgp community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  return bgp_show_community_list (vty, argv[0], 1, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_ipv4_community_list_exact,
+       show_ip_bgp_ipv4_community_list_exact_cmd,
+       "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_community_list,
+       show_bgp_community_list_cmd,
+       "show bgp community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_community_list,
+       show_bgp_ipv6_community_list_cmd,
+       "show bgp ipv6 community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_community_list,
+       show_ipv6_bgp_community_list_cmd,
+       "show ipv6 bgp community-list WORD",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list name\n")
+{
+  return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_community_list,
+       show_ipv6_mbgp_community_list_cmd,
+       "show ipv6 mbgp community-list WORD",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the community-list\n"
+       "community-list name\n")
+{
+  return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_MULTICAST);
+}
+
+DEFUN (show_bgp_community_list_exact,
+       show_bgp_community_list_exact_cmd,
+       "show bgp community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_community_list_exact,
+       show_bgp_ipv6_community_list_exact_cmd,
+       "show bgp ipv6 community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_community_list_exact,
+       show_ipv6_bgp_community_list_exact_cmd,
+       "show ipv6 bgp community-list WORD exact-match",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the community-list\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_community_list_exact,
+       show_ipv6_mbgp_community_list_exact_cmd,
+       "show ipv6 mbgp community-list WORD exact-match",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the community-list\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST);
+}
+
+DEFUN (show_bgp_ipv4_community_list,
+       show_bgp_ipv4_community_list_cmd,
+       "show bgp ipv4 community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  return bgp_show_community_list (vty, argv[0], 0, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_safi_community_list,
+       show_bgp_ipv4_safi_community_list_cmd,
+       "show bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST);
+  
+  return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_community_list_exact,
+       show_bgp_ipv4_community_list_exact_cmd,
+       "show bgp ipv4 community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  return bgp_show_community_list (vty, argv[0], 1, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_safi_community_list_exact,
+       show_bgp_ipv4_safi_community_list_exact_cmd,
+       "show bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST);
+  return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv6_safi_community_list,
+       show_bgp_ipv6_safi_community_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_community_list (vty, argv[1], 0, AFI_IP6, safi);
+}
+
+DEFUN (show_bgp_ipv6_safi_community_list_exact,
+       show_bgp_ipv6_safi_community_list_exact_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) community-list (<1-500>|WORD) exact-match",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the community-list\n"
+       "community-list number\n"
+       "community-list name\n"
+       "Exact match of the communities\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_community_list (vty, argv[1], 1, AFI_IP6, safi);
+}
+
+/*
+ * Large Community show commands.
+ */
+
+DEFUN (show_bgp_afi_lcommunity_all,
+       show_bgp_afi_lcommunity_all_cmd,
+       "show bgp (ipv4|ipv6) large-community",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family\n"
+       "Display routes matching the large-communities\n")
+{
+  afi_t                afi;
+  safi_t       safi = SAFI_UNICAST;
+
+  if (bgp_parse_afi(argv[0], &afi)) {
+    vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show (vty, NULL, afi, safi, bgp_show_type_lcommunity_all, NULL);
+}
+
+static int
+bgp_show_lcommunity (struct vty *vty, const char *view_name, int argc,
+                    const char **argv, afi_t afi, safi_t safi)
+{
+  struct lcommunity *lcom;
+  struct buffer *b;
+  struct bgp *bgp;
+  int i;
+  char *str;
+  int first = 0;
+
+  /* BGP structure lookup */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  b = buffer_new (1024);
+  for (i = 0; i < argc; i++)
+    {
+      if (first)
+        buffer_putc (b, ' ');
+      else
+       {
+         if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0))
+           continue;
+         first = 1;
+       }
+
+      buffer_putstr (b, argv[i]);
+    }
+  buffer_putc (b, '\0');
+
+  str = buffer_getstr (b);
+  buffer_free (b);
+
+  lcom = lcommunity_str2com (str);
+  XFREE (MTYPE_TMP, str);
+  if (! lcom)
+    {
+      vty_out (vty, "%% Large-community malformed: %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity, lcom);
+}
+
+DEFUN (show_ip_bgp_lcommunity,
+       show_ip_bgp_lcommunity_cmd,
+       "show ip bgp large-community (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_lcommunity,
+       show_ip_bgp_lcommunity2_cmd,
+       "show ip bgp large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_ip_bgp_lcommunity,
+       show_ip_bgp_lcommunity3_cmd,
+       "show ip bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "largecommunity number\n"
+       "largecommunity number\n"
+       "largecommunity number\n")
+
+ALIAS (show_ip_bgp_lcommunity,
+       show_ip_bgp_lcommunity4_cmd,
+       "show ip bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_ip_bgp_ipv4_lcommunity,
+       show_ip_bgp_ipv4_lcommunity_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_ip_bgp_ipv4_lcommunity,
+       show_ip_bgp_ipv4_lcommunity2_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_ip_bgp_ipv4_lcommunity,
+       show_ip_bgp_ipv4_lcommunity3_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_ip_bgp_ipv4_lcommunity,
+       show_ip_bgp_ipv4_lcommunity4_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_bgp_lcommunity,
+       show_bgp_lcommunity_cmd,
+       "show bgp large-community (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_ipv6_lcommunity_cmd,
+       "show bgp ipv6 large-community (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_lcommunity2_cmd,
+       "show bgp large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_ipv6_lcommunity2_cmd,
+       "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_lcommunity3_cmd,
+       "show bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_ipv6_lcommunity3_cmd,
+       "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_lcommunity4_cmd,
+       "show bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_lcommunity,
+       show_bgp_ipv6_lcommunity4_cmd,
+       "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_lcommunity,
+       show_ipv6_bgp_lcommunity_cmd,
+       "show ipv6 bgp large-community (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_bgp_lcommunity,
+       show_ipv6_bgp_lcommunity2_cmd,
+       "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+ALIAS (show_ipv6_bgp_lcommunity,
+       show_ipv6_bgp_lcommunity3_cmd,
+       "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+ALIAS (show_ipv6_bgp_lcommunity,
+       show_ipv6_bgp_lcommunity4_cmd,
+       "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+DEFUN (show_ipv6_mbgp_lcommunity,
+       show_ipv6_mbgp_lcommunity_cmd,
+       "show ipv6 mbgp large-community (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_MULTICAST);
+}
+
+/* old command */
+ALIAS (show_ipv6_mbgp_lcommunity,
+       show_ipv6_mbgp_lcommunity2_cmd,
+       "show ipv6 mbgp large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_lcommunity,
+       show_ipv6_mbgp_lcommunity3_cmd,
+       "show ipv6 mbgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+/* old command */
+ALIAS (show_ipv6_mbgp_lcommunity,
+       show_ipv6_mbgp_lcommunity4_cmd,
+       "show ipv6 mbgp laarge-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_bgp_ipv4_lcommunity,
+       show_bgp_ipv4_lcommunity_cmd,
+       "show bgp ipv4 large-community (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_lcommunity,
+       show_bgp_ipv4_lcommunity2_cmd,
+       "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv4_lcommunity,
+       show_bgp_ipv4_lcommunity3_cmd,
+       "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv4_lcommunity,
+       show_bgp_ipv4_lcommunity4_cmd,
+       "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_bgp_ipv4_safi_lcommunity,
+       show_bgp_ipv4_safi_lcommunity_cmd,
+       "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_ipv4_safi_lcommunity,
+       show_bgp_ipv4_safi_lcommunity2_cmd,
+       "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv4_safi_lcommunity,
+       show_bgp_ipv4_safi_lcommunity3_cmd,
+       "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv4_safi_lcommunity,
+       show_bgp_ipv4_safi_lcommunity4_cmd,
+       "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_bgp_view_afi_safi_lcommunity_all,
+       show_bgp_view_afi_safi_lcommunity_all_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-communities\n")
+{
+  int afi;
+  int safi;
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+    {
+      vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+  safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_all, NULL);
+}
+
+DEFUN (show_bgp_view_afi_safi_lcommunity,
+       show_bgp_view_afi_safi_lcommunity_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  int afi;
+  int safi;
+
+  afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+  safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  return bgp_show_lcommunity (vty, argv[0], argc-3, &argv[3], afi, safi);
+}
+
+ALIAS (show_bgp_view_afi_safi_lcommunity,
+       show_bgp_view_afi_safi_lcommunity2_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_view_afi_safi_lcommunity,
+       show_bgp_view_afi_safi_lcommunity3_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_view_afi_safi_lcommunity,
+       show_bgp_view_afi_safi_lcommunity4_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+DEFUN (show_bgp_ipv6_safi_lcommunity,
+       show_bgp_ipv6_safi_lcommunity_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) large-community AA:BB:CC",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_lcommunity (vty, NULL, argc-1, argv+1, AFI_IP6, safi);
+}
+
+ALIAS (show_bgp_ipv6_safi_lcommunity,
+       show_bgp_ipv6_safi_lcommunity2_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv6_safi_lcommunity,
+       show_bgp_ipv6_safi_lcommunity3_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+ALIAS (show_bgp_ipv6_safi_lcommunity,
+       show_bgp_ipv6_safi_lcommunity4_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n")
+
+static int
+bgp_show_lcommunity_list (struct vty *vty, const char *lcom,
+                         afi_t afi, safi_t safi)
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, lcom, LARGE_COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    {
+      vty_out (vty, "%% %s is not a valid large-community-list name%s", lcom,
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, NULL, afi, safi, bgp_show_type_lcommunity_list, list);
+}
+
+DEFUN (show_ip_bgp_lcommunity_list,
+       show_ip_bgp_lcommunity_list_cmd,
+       "show ip bgp large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  return bgp_show_lcommunity_list (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_ipv4_lcommunity_list,
+       show_ip_bgp_ipv4_lcommunity_list_cmd,
+       "show ip bgp ipv4 (unicast|multicast) large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_lcommunity_list,
+       show_bgp_lcommunity_list_cmd,
+       "show bgp large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
+ALIAS (show_bgp_lcommunity_list,
+       show_bgp_ipv6_lcommunity_list_cmd,
+       "show bgp ipv6 large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+
+/* old command */
+DEFUN (show_ipv6_bgp_lcommunity_list,
+       show_ipv6_bgp_lcommunity_list_cmd,
+       "show ipv6 bgp large-community-list WORD",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Display routes matching the large-community-list\n"
+       "large-community-list name\n")
+{
+  return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_lcommunity_list,
+       show_ipv6_mbgp_lcommunity_list_cmd,
+       "show ipv6 mbgp large-community-list WORD",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Display routes matching the large-community-list\n"
+       "large-community-list name\n")
+{
+  return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+}
+
+DEFUN (show_bgp_ipv4_lcommunity_list,
+       show_bgp_ipv4_lcommunity_list_cmd,
+       "show bgp ipv4 large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  return bgp_show_lcommunity_list (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_safi_lcommunity_list,
+       show_bgp_ipv4_safi_lcommunity_list_cmd,
+       "show bgp ipv4 (unicast|multicast) large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv6_safi_lcommunity_list,
+       show_bgp_ipv6_safi_lcommunity_list_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_lcommunity_list (vty, argv[1], AFI_IP6, safi);
+}
+
+static int
+bgp_show_prefix_longer (struct vty *vty, const char *prefix, afi_t afi,
+                       safi_t safi, enum bgp_show_type type)
+{
+  int ret;
+  struct prefix *p;
+
+  p = prefix_new();
+
+  ret = str2prefix (prefix, p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = bgp_show (vty, NULL, afi, safi, type, p);
+  prefix_free(p);
+  return ret;
+}
+
+DEFUN (show_ip_bgp_prefix_longer,
+       show_ip_bgp_prefix_longer_cmd,
+       "show ip bgp A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_ip_bgp_flap_prefix_longer,
+       show_ip_bgp_flap_prefix_longer_cmd,
+       "show ip bgp flap-statistics A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_flap_prefix_longer);
+}
+
+ALIAS (show_ip_bgp_flap_prefix_longer,
+       show_ip_bgp_damp_flap_prefix_longer_cmd,
+       "show ip bgp dampening flap-statistics A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+
+DEFUN (show_ip_bgp_ipv4_prefix_longer,
+       show_ip_bgp_ipv4_prefix_longer_cmd,
+       "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_MULTICAST,
+                                  bgp_show_type_prefix_longer);
+
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_ip_bgp_flap_address,
+       show_ip_bgp_flap_address_cmd,
+       "show ip bgp flap-statistics A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_flap_address);
+}
+
+ALIAS (show_ip_bgp_flap_address,
+       show_ip_bgp_damp_flap_address_cmd,
+       "show ip bgp dampening flap-statistics A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+
+DEFUN (show_ip_bgp_flap_prefix,
+       show_ip_bgp_flap_prefix_cmd,
+       "show ip bgp flap-statistics A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_flap_prefix);
+}
+
+ALIAS (show_ip_bgp_flap_prefix,
+       show_ip_bgp_damp_flap_prefix_cmd,
+       "show ip bgp dampening flap-statistics A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+DEFUN (show_bgp_prefix_longer,
+       show_bgp_prefix_longer_cmd,
+       "show bgp X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "IPv6 prefix <network>/<length>\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_prefix_longer,
+       show_ipv6_bgp_prefix_longer_cmd,
+       "show ipv6 bgp X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_prefix_longer,
+       show_ipv6_mbgp_prefix_longer_cmd,
+       "show ipv6 mbgp X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_MULTICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_bgp_ipv4_prefix_longer,
+       show_bgp_ipv4_prefix_longer_cmd,
+       "show bgp ipv4 A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_prefix_longer,
+       show_bgp_ipv4_safi_flap_prefix_longer_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi,
+                                bgp_show_type_flap_prefix_longer);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_prefix_longer,
+       show_bgp_ipv4_safi_damp_flap_prefix_longer_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_prefix_longer,
+       show_bgp_ipv6_safi_flap_prefix_longer_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP6, safi,
+                                bgp_show_type_flap_prefix_longer);
+}
+ALIAS (show_bgp_ipv6_safi_flap_prefix_longer,
+       show_bgp_ipv6_safi_damp_flap_prefix_longer_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+
+DEFUN (show_bgp_ipv4_safi_prefix_longer,
+       show_bgp_ipv4_safi_prefix_longer_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi,
+                                  bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_bgp_ipv6_safi_prefix_longer,
+       show_bgp_ipv6_safi_prefix_longer_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Display route and more specific routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP6, safi,
+                                  bgp_show_type_prefix_longer);
+}
+
+DEFUN (show_bgp_ipv4_safi_flap_address,
+       show_bgp_ipv4_safi_flap_address_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi,
+                                bgp_show_type_flap_address);
+}
+ALIAS (show_bgp_ipv4_safi_flap_address,
+       show_bgp_ipv4_safi_damp_flap_address_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+
+DEFUN (show_bgp_ipv6_flap_address,
+       show_bgp_ipv6_flap_address_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi,
+                                bgp_show_type_flap_address);
+}
+ALIAS (show_bgp_ipv6_flap_address,
+       show_bgp_ipv6_damp_flap_address_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "Network in the BGP routing table to display\n")
+
+DEFUN (show_bgp_ipv4_safi_flap_prefix,
+       show_bgp_ipv4_safi_flap_prefix_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP, safi,
+                                bgp_show_type_flap_prefix);
+}
+
+ALIAS (show_bgp_ipv4_safi_flap_prefix,
+       show_bgp_ipv4_safi_damp_flap_prefix_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_prefix,
+       show_bgp_ipv6_safi_flap_prefix_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, safi,
+                                bgp_show_type_flap_prefix);
+}
+
+ALIAS (show_bgp_ipv6_safi_flap_prefix,
+       show_bgp_ipv6_safi_damp_flap_prefix_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+DEFUN (show_bgp_ipv6_prefix_longer,
+       show_bgp_ipv6_prefix_longer_cmd,
+       "show bgp ipv6 X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "IPv6 prefix <network>/<length>\n"
+       "Display route and more specific routes\n")
+{
+  return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST,
+                                bgp_show_type_prefix_longer);
+}
+
+static struct peer *
+peer_lookup_in_view (struct vty *vty, const char *view_name, 
+                     const char *ip_str)
+{
+  int ret;
+  struct bgp *bgp;
+  struct peer *peer;
+  union sockunion su;
+
+  /* BGP structure lookup. */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (! bgp)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+          return NULL;
+        }      
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (! bgp)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return NULL;
+        }
+    }
+
+  /* Get peer sockunion. */  
+  ret = str2sockunion (ip_str, &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", ip_str, VTY_NEWLINE);
+      return NULL;
+    }
+
+  /* Peer structure lookup. */
+  peer = peer_lookup (bgp, &su);
+  if (! peer)
+    {
+      vty_out (vty, "No such neighbor%s", VTY_NEWLINE);
+      return NULL;
+    }
+  
+  return peer;
+}
+
+enum bgp_stats
+{
+  BGP_STATS_MAXBITLEN = 0,
+  BGP_STATS_RIB,
+  BGP_STATS_PREFIXES,
+  BGP_STATS_TOTPLEN,
+  BGP_STATS_UNAGGREGATEABLE,
+  BGP_STATS_MAX_AGGREGATEABLE,
+  BGP_STATS_AGGREGATES,
+  BGP_STATS_SPACE,
+  BGP_STATS_ASPATH_COUNT,
+  BGP_STATS_ASPATH_MAXHOPS,
+  BGP_STATS_ASPATH_TOTHOPS,
+  BGP_STATS_ASPATH_MAXSIZE,
+  BGP_STATS_ASPATH_TOTSIZE,
+  BGP_STATS_ASN_HIGHEST,
+  BGP_STATS_MAX,
+};
+
+static const char *table_stats_strs[] =
+{
+  [BGP_STATS_PREFIXES]            = "Total Prefixes",
+  [BGP_STATS_TOTPLEN]             = "Average prefix length",
+  [BGP_STATS_RIB]                 = "Total Advertisements",
+  [BGP_STATS_UNAGGREGATEABLE]     = "Unaggregateable prefixes",
+  [BGP_STATS_MAX_AGGREGATEABLE]   = "Maximum aggregateable prefixes",
+  [BGP_STATS_AGGREGATES]          = "BGP Aggregate advertisements",
+  [BGP_STATS_SPACE]               = "Address space advertised",
+  [BGP_STATS_ASPATH_COUNT]        = "Advertisements with paths",
+  [BGP_STATS_ASPATH_MAXHOPS]      = "Longest AS-Path (hops)",
+  [BGP_STATS_ASPATH_MAXSIZE]      = "Largest AS-Path (bytes)",
+  [BGP_STATS_ASPATH_TOTHOPS]      = "Average AS-Path length (hops)",
+  [BGP_STATS_ASPATH_TOTSIZE]      = "Average AS-Path size (bytes)",
+  [BGP_STATS_ASN_HIGHEST]         = "Highest public ASN",
+  [BGP_STATS_MAX] = NULL,
+};
+
+struct bgp_table_stats
+{
+  struct bgp_table *table;
+  unsigned long long counts[BGP_STATS_MAX];
+};
+
+#if 0
+#define TALLY_SIGFIG 100000
+static unsigned long
+ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval)
+{
+  unsigned long newtot = (count-1) * oldavg + (newval * TALLY_SIGFIG);
+  unsigned long res = (newtot * TALLY_SIGFIG) / count;
+  unsigned long ret = newtot / count;
+  
+  if ((res % TALLY_SIGFIG) > (TALLY_SIGFIG/2))
+    return ret + 1;
+  else
+    return ret;
+}
+#endif
+
+static int
+bgp_table_stats_walker (struct thread *t)
+{
+  struct bgp_node *rn;
+  struct bgp_node *top;
+  struct bgp_table_stats *ts = THREAD_ARG (t);
+  unsigned int space = 0;
+  
+  if (!(top = bgp_table_top (ts->table)))
+    return 0;
+
+  switch (top->p.family)
+    {
+      case AF_INET:
+        space = IPV4_MAX_BITLEN;
+        break;
+      case AF_INET6:
+        space = IPV6_MAX_BITLEN;
+        break;
+    }
+    
+  ts->counts[BGP_STATS_MAXBITLEN] = space;
+
+  for (rn = top; rn; rn = bgp_route_next (rn))
+    {
+      struct bgp_info *ri;
+      struct bgp_node *prn = bgp_node_parent_nolock (rn);
+      unsigned int rinum = 0;
+      
+      if (rn == top)
+        continue;
+      
+      if (!rn->info)
+        continue;
+      
+      ts->counts[BGP_STATS_PREFIXES]++;
+      ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen;
+
+#if 0
+      ts->counts[BGP_STATS_AVGPLEN]
+        = ravg_tally (ts->counts[BGP_STATS_PREFIXES],
+                      ts->counts[BGP_STATS_AVGPLEN],
+                      rn->p.prefixlen);
+#endif
+      
+      /* check if the prefix is included by any other announcements */
+      while (prn && !prn->info)
+        prn = bgp_node_parent_nolock (prn);
+      
+      if (prn == NULL || prn == top)
+        {
+          ts->counts[BGP_STATS_UNAGGREGATEABLE]++;
+          /* announced address space */
+          if (space)
+            ts->counts[BGP_STATS_SPACE] += 1 << (space - rn->p.prefixlen);
+        }
+      else if (prn->info)
+        ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++;
+      
+      for (ri = rn->info; ri; ri = ri->next)
+        {
+          rinum++;
+          ts->counts[BGP_STATS_RIB]++;
+          
+          if (ri->attr &&
+              (CHECK_FLAG (ri->attr->flag,
+                           ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))))
+            ts->counts[BGP_STATS_AGGREGATES]++;
+          
+          /* as-path stats */
+          if (ri->attr && ri->attr->aspath)
+            {
+              unsigned int hops = aspath_count_hops (ri->attr->aspath);
+              unsigned int size = aspath_size (ri->attr->aspath);
+              as_t highest = aspath_highest (ri->attr->aspath);
+              
+              ts->counts[BGP_STATS_ASPATH_COUNT]++;
+              
+              if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS])
+                ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops;
+              
+              if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE])
+                ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size;
+              
+              ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops;
+              ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size;
+#if 0
+              ts->counts[BGP_STATS_ASPATH_AVGHOPS] 
+                = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT],
+                              ts->counts[BGP_STATS_ASPATH_AVGHOPS],
+                              hops);
+              ts->counts[BGP_STATS_ASPATH_AVGSIZE]
+                = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT],
+                              ts->counts[BGP_STATS_ASPATH_AVGSIZE],
+                              size);
+#endif
+              if (highest > ts->counts[BGP_STATS_ASN_HIGHEST])
+                ts->counts[BGP_STATS_ASN_HIGHEST] = highest;
+            }
+        }
+    }
+  return 0;
+}
+
+static int
+bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
+{
+  struct bgp_table_stats ts;
+  unsigned int i;
+  
+  if (!bgp->rib[afi][safi])
+    {
+      vty_out (vty, "%% No RIB exists for the specified AFI(%d)/SAFI(%d) %s",
+               afi, safi, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  memset (&ts, 0, sizeof (ts));
+  ts.table = bgp->rib[afi][safi];
+  thread_execute (bm->master, bgp_table_stats_walker, &ts, 0);
+
+  vty_out (vty, "BGP %s RIB statistics%s%s",
+           afi_safi_print (afi, safi), VTY_NEWLINE, VTY_NEWLINE);
+  
+  for (i = 0; i < BGP_STATS_MAX; i++)
+    {
+      if (!table_stats_strs[i])
+        continue;
+      
+      switch (i)
+        {
+#if 0
+          case BGP_STATS_ASPATH_AVGHOPS:
+          case BGP_STATS_ASPATH_AVGSIZE:
+          case BGP_STATS_AVGPLEN:
+            vty_out (vty, "%-30s: ", table_stats_strs[i]);
+            vty_out (vty, "%12.2f",
+                     (float)ts.counts[i] / (float)TALLY_SIGFIG);
+            break;
+#endif
+          case BGP_STATS_ASPATH_TOTHOPS:
+          case BGP_STATS_ASPATH_TOTSIZE:
+            vty_out (vty, "%-30s: ", table_stats_strs[i]);
+            vty_out (vty, "%12.2f",
+                     ts.counts[i] ?
+                     (float)ts.counts[i] / 
+                      (float)ts.counts[BGP_STATS_ASPATH_COUNT]
+                     : 0);
+            break;
+          case BGP_STATS_TOTPLEN:
+            vty_out (vty, "%-30s: ", table_stats_strs[i]);
+            vty_out (vty, "%12.2f",
+                     ts.counts[i] ?
+                     (float)ts.counts[i] / 
+                      (float)ts.counts[BGP_STATS_PREFIXES]
+                     : 0);
+            break;
+          case BGP_STATS_SPACE:
+            vty_out (vty, "%-30s: ", table_stats_strs[i]);
+            vty_out (vty, "%12llu%s", ts.counts[i], VTY_NEWLINE);
+            if (ts.counts[BGP_STATS_MAXBITLEN] < 9)
+              break;
+            vty_out (vty, "%30s: ", "%% announced ");
+            vty_out (vty, "%12.2f%s", 
+                     100 * (float)ts.counts[BGP_STATS_SPACE] / 
+                       (float)((uint64_t)1UL << ts.counts[BGP_STATS_MAXBITLEN]),
+                       VTY_NEWLINE);
+            vty_out (vty, "%30s: ", "/8 equivalent ");
+            vty_out (vty, "%12.2f%s", 
+                     (float)ts.counts[BGP_STATS_SPACE] / 
+                       (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 8)),
+                     VTY_NEWLINE);
+            if (ts.counts[BGP_STATS_MAXBITLEN] < 25)
+              break;
+            vty_out (vty, "%30s: ", "/24 equivalent ");
+            vty_out (vty, "%12.2f", 
+                     (float)ts.counts[BGP_STATS_SPACE] / 
+                       (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 24)));
+            break;
+          default:
+            vty_out (vty, "%-30s: ", table_stats_strs[i]);
+            vty_out (vty, "%12llu", ts.counts[i]);
+        }
+        
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_table_stats_vty (struct vty *vty, const char *name,
+                     const char *afi_str, const char *safi_str)
+{
+  struct bgp *bgp;
+  afi_t afi;
+  safi_t safi;
+  
+ if (name)
+    bgp = bgp_lookup_by_name (name);
+  else
+    bgp = bgp_get_default ();
+
+  if (!bgp)
+    {
+      vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (strncmp (afi_str, "ipv", 3) == 0)
+    {
+      if (strncmp (afi_str, "ipv4", 4) == 0)
+        afi = AFI_IP;
+      else if (strncmp (afi_str, "ipv6", 4) == 0)
+        afi = AFI_IP6;
+      else
+        {
+          vty_out (vty, "%% Invalid address family %s%s",
+                   afi_str, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      switch (safi_str[0]) {
+       case 'm':
+           safi = SAFI_MULTICAST;
+           break;
+       case 'u':
+           safi = SAFI_UNICAST;
+           break;
+       case 'v':
+           safi =  SAFI_MPLS_VPN;
+           break;
+       case 'e':
+           safi = SAFI_ENCAP;
+           break;
+       default:
+           vty_out (vty, "%% Invalid subsequent address family %s%s",
+                   safi_str, VTY_NEWLINE);
+            return CMD_WARNING;
+      }
+    }
+  else
+    {
+      vty_out (vty, "%% Invalid address family \"%s\"%s",
+               afi_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_table_stats (vty, bgp, afi, safi);
+}
+
+DEFUN (show_bgp_statistics,
+       show_bgp_statistics_cmd,
+       "show bgp (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "BGP RIB advertisement statistics\n")
+{
+  return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
+}
+
+ALIAS (show_bgp_statistics,
+       show_bgp_statistics_vpnv4_cmd,
+       "show bgp (ipv4) (vpnv4) statistics",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "BGP RIB advertisement statistics\n")
+
+DEFUN (show_bgp_statistics_view,
+       show_bgp_statistics_view_cmd,
+       "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "Address family\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "BGP RIB advertisement statistics\n")
+{
+  return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
+}
+
+ALIAS (show_bgp_statistics_view,
+       show_bgp_statistics_view_vpnv4_cmd,
+       "show bgp view WORD (ipv4) (vpnv4) statistics",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "BGP RIB advertisement statistics\n")
+
+enum bgp_pcounts
+{
+  PCOUNT_ADJ_IN = 0,
+  PCOUNT_DAMPED,
+  PCOUNT_REMOVED,
+  PCOUNT_HISTORY,
+  PCOUNT_STALE,
+  PCOUNT_VALID,
+  PCOUNT_ALL,
+  PCOUNT_COUNTED,
+  PCOUNT_PFCNT, /* the figure we display to users */
+  PCOUNT_MAX,
+};
+
+static const char *pcount_strs[] =
+{
+  [PCOUNT_ADJ_IN]  = "Adj-in",
+  [PCOUNT_DAMPED]  = "Damped",
+  [PCOUNT_REMOVED] = "Removed",
+  [PCOUNT_HISTORY] = "History",
+  [PCOUNT_STALE]   = "Stale",
+  [PCOUNT_VALID]   = "Valid",
+  [PCOUNT_ALL]     = "All RIB",
+  [PCOUNT_COUNTED] = "PfxCt counted",
+  [PCOUNT_PFCNT]   = "Useable",
+  [PCOUNT_MAX]     = NULL,
+};
+
+struct peer_pcounts
+{
+  unsigned int count[PCOUNT_MAX];
+  const struct peer *peer;
+  const struct bgp_table *table;
+};
+
+static int
+bgp_peer_count_walker (struct thread *t)
+{
+  struct bgp_node *rn;
+  struct peer_pcounts *pc = THREAD_ARG (t);
+  const struct peer *peer = pc->peer;
+  
+  for (rn = bgp_table_top (pc->table); rn; rn = bgp_route_next (rn))
+    {
+      struct bgp_adj_in *ain;
+      struct bgp_info *ri;
+      
+      for (ain = rn->adj_in; ain; ain = ain->next)
+        if (ain->peer == peer)
+          pc->count[PCOUNT_ADJ_IN]++;
+
+      for (ri = rn->info; ri; ri = ri->next)
+        {
+          char buf[SU_ADDRSTRLEN];
+          
+          if (ri->peer != peer)
+            continue;
+          
+          pc->count[PCOUNT_ALL]++;
+          
+          if (CHECK_FLAG (ri->flags, BGP_INFO_DAMPED))
+            pc->count[PCOUNT_DAMPED]++;
+          if (CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
+            pc->count[PCOUNT_HISTORY]++;
+          if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED))
+            pc->count[PCOUNT_REMOVED]++;
+          if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
+            pc->count[PCOUNT_STALE]++;
+          if (CHECK_FLAG (ri->flags, BGP_INFO_VALID))
+            pc->count[PCOUNT_VALID]++;
+          if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
+            pc->count[PCOUNT_PFCNT]++;
+          
+          if (CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
+            {
+              pc->count[PCOUNT_COUNTED]++;
+              if (CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
+                plog_warn (peer->log,
+                           "%s [pcount] %s/%d is counted but flags 0x%x",
+                           peer->host,
+                           inet_ntop(rn->p.family, &rn->p.u.prefix,
+                                     buf, SU_ADDRSTRLEN),
+                           rn->p.prefixlen,
+                           ri->flags);
+            }
+          else
+            {
+              if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
+                plog_warn (peer->log,
+                           "%s [pcount] %s/%d not counted but flags 0x%x",
+                           peer->host,
+                           inet_ntop(rn->p.family, &rn->p.u.prefix,
+                                     buf, SU_ADDRSTRLEN),
+                           rn->p.prefixlen,
+                           ri->flags);
+            }
+        }
+    }
+  return 0;
+}
+
+static int
+bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer_pcounts pcounts = { .peer = peer };
+  unsigned int i;
+  
+  if (!peer || !peer->bgp || !peer->afc[afi][safi]
+      || !peer->bgp->rib[afi][safi])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  memset (&pcounts, 0, sizeof(pcounts));
+  pcounts.peer = peer;
+  pcounts.table = peer->bgp->rib[afi][safi];
+  
+  /* in-place call via thread subsystem so as to record execution time
+   * stats for the thread-walk (i.e. ensure this can't be blamed on
+   * on just vty_read()).
+   */
+  thread_execute (bm->master, bgp_peer_count_walker, &pcounts, 0);
+  
+  vty_out (vty, "Prefix counts for %s, %s%s", 
+           peer->host, afi_safi_print (afi, safi), VTY_NEWLINE);
+  vty_out (vty, "PfxCt: %ld%s", peer->pcount[afi][safi], VTY_NEWLINE);
+  vty_out (vty, "%sCounts from RIB table walk:%s%s", 
+           VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  for (i = 0; i < PCOUNT_MAX; i++)
+      vty_out (vty, "%20s: %-10d%s",
+               pcount_strs[i], pcounts.count[i], VTY_NEWLINE);
+
+  if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi])
+    {
+      vty_out (vty, "%s [pcount] PfxCt drift!%s",
+               peer->host, VTY_NEWLINE);
+      vty_out (vty, "Please report this bug, with the above command output%s",
+              VTY_NEWLINE);
+    }
+               
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_neighbor_prefix_counts,
+       show_ip_bgp_neighbor_prefix_counts_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);  
+  if (! peer) 
+    return CMD_WARNING;
+  return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv6_neighbor_prefix_counts,
+       show_bgp_ipv6_neighbor_prefix_counts_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);  
+  if (! peer) 
+    return CMD_WARNING;
+  return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_ipv4_neighbor_prefix_counts,
+       show_ip_bgp_ipv4_neighbor_prefix_counts_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_vpnv4_neighbor_prefix_counts,
+       show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd,
+       "show ip bgp vpnv4 all neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+  
+  return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN);
+}
+
+DEFUN (show_bgp_ipv4_safi_neighbor_prefix_counts,
+       show_bgp_ipv4_safi_neighbor_prefix_counts_cmd,
+       "show bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_peer_counts (vty, peer, AFI_IP, safi);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_prefix_counts,
+       show_bgp_ipv6_safi_neighbor_prefix_counts_cmd,
+       "show bgp ipv6 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_peer_counts (vty, peer, AFI_IP6, safi);
+}
+
+DEFUN (show_ip_bgp_encap_neighbor_prefix_counts,
+       show_ip_bgp_encap_neighbor_prefix_counts_cmd,
+       "show ip bgp encap all neighbors (A.B.C.D|X:X::X:X) prefix-counts",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display detailed prefix count information\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+  
+  return bgp_peer_counts (vty, peer, AFI_IP, SAFI_ENCAP);
+}
+
+
+static void
+show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
+               int in)
+{
+  struct bgp_table *table;
+  struct bgp_adj_in *ain;
+  struct bgp_adj_out *adj;
+  unsigned long output_count;
+  struct bgp_node *rn;
+  int header1 = 1;
+  struct bgp *bgp;
+  int header2 = 1;
+
+  bgp = peer->bgp;
+
+  if (! bgp)
+    return;
+
+  table = bgp->rib[afi][safi];
+
+  output_count = 0;
+       
+  if (! in && CHECK_FLAG (peer->af_sflags[afi][safi],
+                         PEER_STATUS_DEFAULT_ORIGINATE))
+    {
+      vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+      vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+      vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+
+      vty_out (vty, "Originating default network 0.0.0.0%s%s",
+              VTY_NEWLINE, VTY_NEWLINE);
+      header1 = 0;
+    }
+
+  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+    if (in)
+      {
+       for (ain = rn->adj_in; ain; ain = ain->next)
+         if (ain->peer == peer)
+           {
+             if (header1)
+               {
+                 vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                 vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+                 vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+                 header1 = 0;
+               }
+             if (header2)
+               {
+                 vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE);
+                 header2 = 0;
+               }
+             if (ain->attr)
+               { 
+                 route_vty_out_tmp (vty, &rn->p, ain->attr, safi);
+                 output_count++;
+               }
+           }
+      }
+    else
+      {
+       for (adj = rn->adj_out; adj; adj = adj->next)
+         if (adj->peer == peer)
+           {
+             if (header1)
+               {
+                 vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE);
+                 vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+                 vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE);
+                 header1 = 0;
+               }
+             if (header2)
+               {
+                 vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE);
+                 header2 = 0;
+               }
+             if (adj->attr)
+               {       
+                 route_vty_out_tmp (vty, &rn->p, adj->attr, safi);
+                 output_count++;
+               }
+           }
+      }
+  
+  if (output_count != 0)
+    vty_out (vty, "%sTotal number of prefixes %ld%s",
+            VTY_NEWLINE, output_count, VTY_NEWLINE);
+}
+
+static int
+peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, int in)
+{    
+  if (! peer || ! peer->afc[afi][safi])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (in && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+    {
+      vty_out (vty, "%% Inbound soft reconfiguration not enabled%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  show_adj_route (vty, peer, afi, safi, in);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_view_neighbor_advertised_route,
+       show_ip_bgp_view_neighbor_advertised_route_cmd,
+       "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer) 
+    return CMD_WARNING;
+  return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0);
+}
+
+ALIAS (show_ip_bgp_view_neighbor_advertised_route,
+       show_ip_bgp_neighbor_advertised_route_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+
+DEFUN (show_ip_bgp_ipv4_neighbor_advertised_route,
+       show_ip_bgp_ipv4_neighbor_advertised_route_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (strncmp (argv[0], "m", 1) == 0)
+    return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 0);
+
+  return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0);
+}
+
+DEFUN (show_bgp_view_neighbor_advertised_route,
+       show_bgp_view_neighbor_advertised_route_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;    
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0);
+}
+
+DEFUN (show_bgp_view_neighbor_received_routes,
+       show_bgp_view_neighbor_received_routes_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1);
+}
+
+ALIAS (show_bgp_view_neighbor_advertised_route,
+       show_bgp_neighbor_advertised_route_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+       
+ALIAS (show_bgp_view_neighbor_advertised_route,
+       show_bgp_ipv6_neighbor_advertised_route_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+
+/* old command */
+ALIAS (show_bgp_view_neighbor_advertised_route,
+       ipv6_bgp_neighbor_advertised_route_cmd,
+       "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+  
+/* old command */
+DEFUN (ipv6_mbgp_neighbor_advertised_route,
+       ipv6_mbgp_neighbor_advertised_route_cmd,
+       "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;  
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 0);
+}
+
+DEFUN (show_ip_bgp_view_neighbor_received_routes,
+       show_ip_bgp_view_neighbor_received_routes_cmd,
+       "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1);
+}
+
+ALIAS (show_ip_bgp_view_neighbor_received_routes,
+       show_ip_bgp_neighbor_received_routes_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+
+ALIAS (show_bgp_view_neighbor_received_routes,
+       show_bgp_ipv6_neighbor_received_routes_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+
+DEFUN (show_bgp_neighbor_received_prefix_filter,
+       show_bgp_neighbor_received_prefix_filter_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP6, name);
+  if (count)
+    {
+      vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP6, name);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* old command */
+ALIAS (show_bgp_view_neighbor_received_routes,
+       ipv6_bgp_neighbor_received_routes_cmd,
+       "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+
+/* old command */
+DEFUN (ipv6_mbgp_neighbor_received_routes,
+       ipv6_mbgp_neighbor_received_routes_cmd,
+       "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 1);
+}
+
+DEFUN (show_bgp_view_neighbor_received_prefix_filter,
+       show_bgp_view_neighbor_received_prefix_filter_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  struct bgp *bgp;
+  int count, ret;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+  {  
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+  
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (bgp, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP6, name);
+  if (count)
+    {
+      vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP6, name);
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (show_ip_bgp_ipv4_neighbor_received_routes,
+       show_ip_bgp_ipv4_neighbor_received_routes_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  
+  if (strncmp (argv[0], "m", 1) == 0)
+    return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 1);
+
+  return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1);
+}
+
+DEFUN (show_bgp_ipv4_safi_neighbor_advertised_route,
+       show_bgp_ipv4_safi_neighbor_advertised_route_cmd,
+       "show bgp ipv4 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP, safi, 0);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_advertised_route,
+       show_bgp_ipv6_safi_neighbor_advertised_route_cmd,
+       "show bgp ipv6 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP6, safi, 0);
+}
+
+DEFUN (show_bgp_view_ipv6_neighbor_advertised_route,
+       show_bgp_view_ipv6_neighbor_advertised_route_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the routes advertised to a BGP neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;    
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0);
+}
+
+DEFUN (show_bgp_view_ipv6_neighbor_received_routes,
+       show_bgp_view_ipv6_neighbor_received_routes_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1);
+}
+
+DEFUN (show_bgp_ipv4_safi_neighbor_received_routes,
+       show_bgp_ipv4_safi_neighbor_received_routes_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  
+  return peer_adj_routes (vty, peer, AFI_IP, safi, 1);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_received_routes,
+       show_bgp_ipv6_safi_neighbor_received_routes_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  
+  return peer_adj_routes (vty, peer, AFI_IP6, safi, 1);
+}
+
+DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes,
+       show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd,
+       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address family\n"
+       "Address family modifier\n"
+       "Address family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the advertised routes to neighbor\n"
+       "Display the received routes from neighbor\n")
+{
+  int afi;
+  int safi;
+  int in;
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, argv[0], argv[3]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP;
+  safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  in = (strncmp (argv[4], "r", 1) == 0) ? 1 : 0;
+
+  return peer_adj_routes (vty, peer, afi, safi, in);
+}
+
+DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
+       show_ip_bgp_neighbor_received_prefix_filter_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_UNICAST);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP, name);
+  if (count)
+    {
+      vty_out (vty, "Address family: IPv4 Unicast%s", VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP, name);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_ipv4_neighbor_received_prefix_filter,
+       show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (strncmp (argv[0], "m", 1) == 0)
+    {
+      sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_MULTICAST);
+      count =  prefix_bgp_show_prefix_list (NULL, AFI_IP, name);
+      if (count)
+       {
+         vty_out (vty, "Address family: IPv4 Multicast%s", VTY_NEWLINE);
+         prefix_bgp_show_prefix_list (vty, AFI_IP, name);
+       }
+    }
+  else 
+    {
+      sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_UNICAST);
+      count =  prefix_bgp_show_prefix_list (NULL, AFI_IP, name);
+      if (count)
+       {
+         vty_out (vty, "Address family: IPv4 Unicast%s", VTY_NEWLINE);
+         prefix_bgp_show_prefix_list (vty, AFI_IP, name);
+       }
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_bgp_view_neighbor_received_routes,
+       show_bgp_neighbor_received_routes_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) received-routes",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the received routes from neighbor\n")
+
+DEFUN (show_bgp_ipv4_safi_neighbor_received_prefix_filter,
+       show_bgp_ipv4_safi_neighbor_received_prefix_filter_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP, safi);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP, name);
+  if (count) {
+      vty_out (vty, "Address family: IPv4 %s%s", safi2str(safi), VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP, name);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_received_prefix_filter,
+       show_bgp_ipv6_safi_neighbor_received_prefix_filter_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, safi);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP6, name);
+  if (count) {
+      vty_out (vty, "Address family: IPv6 %s%s", safi2str(safi), VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP6, name);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv6_neighbor_received_prefix_filter,
+       show_bgp_ipv6_neighbor_received_prefix_filter_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  int count, ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (NULL, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP6, name);
+  if (count)
+    {
+      vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP6, name);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_view_ipv6_neighbor_received_prefix_filter,
+       show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display information received from a BGP neighbor\n"
+       "Display the prefixlist filter\n")
+{
+  char name[BUFSIZ];
+  union sockunion su;
+  struct peer *peer;
+  struct bgp *bgp;
+  int count, ret;
+
+  /* BGP structure lookup. */
+  bgp = bgp_lookup_by_name (argv[0]);
+  if (bgp == NULL)
+  {  
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+  
+  ret = str2sockunion (argv[1], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  peer = peer_lookup (bgp, &su);
+  if (! peer)
+    return CMD_WARNING;
+
+  sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST);
+  count =  prefix_bgp_show_prefix_list (NULL, AFI_IP6, name);
+  if (count)
+    {
+      vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE);
+      prefix_bgp_show_prefix_list (vty, AFI_IP6, name);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi,
+                        safi_t safi, enum bgp_show_type type)
+{
+  if (! peer || ! peer->afc[afi][safi])
+    {
+      vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show (vty, peer->bgp, afi, safi, type, &peer->su);
+}
+DEFUN (show_ip_bgp_neighbor_routes,
+       show_ip_bgp_neighbor_routes_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
+                                 bgp_show_type_neighbor);
+}
+
+DEFUN (show_ip_bgp_neighbor_flap,
+       show_ip_bgp_neighbor_flap_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
+                                 bgp_show_type_flap_neighbor);
+}
+
+DEFUN (show_ip_bgp_neighbor_damp,
+       show_ip_bgp_neighbor_damp_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
+                                 bgp_show_type_damp_neighbor);
+}
+
+DEFUN (show_ip_bgp_ipv4_neighbor_routes,
+       show_ip_bgp_ipv4_neighbor_routes_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_MULTICAST,
+                                   bgp_show_type_neighbor);
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST,
+                                 bgp_show_type_neighbor);
+}
+
+DEFUN (show_ip_bgp_view_rsclient,
+       show_ip_bgp_view_rsclient_cmd,
+       "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+{
+  struct bgp_table *table;
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP][SAFI_UNICAST];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_ip_bgp_view_rsclient,
+       show_ip_bgp_rsclient_cmd,
+       "show ip bgp rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+
+DEFUN (show_bgp_view_ipv4_safi_rsclient,
+       show_bgp_view_ipv4_safi_rsclient_cmd,
+       "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+{
+  struct bgp_table *table;
+  struct peer *peer;
+  safi_t safi;
+
+  if (argc == 3) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP][safi];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient,
+       show_bgp_ipv4_safi_rsclient_cmd,
+       "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+
+DEFUN (show_ip_bgp_view_rsclient_route,
+       show_ip_bgp_view_rsclient_route_cmd,
+       "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST], 
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+ALIAS (show_ip_bgp_view_rsclient_route,
+       show_ip_bgp_rsclient_route_cmd,
+       "show ip bgp rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+
+DEFUN (show_bgp_ipv4_safi_neighbor_flap,
+       show_bgp_ipv4_safi_neighbor_flap_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, safi,
+                                 bgp_show_type_flap_neighbor);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_flap,
+       show_bgp_ipv6_safi_neighbor_flap_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi,
+                                 bgp_show_type_flap_neighbor);
+}
+
+DEFUN (show_bgp_ipv4_safi_neighbor_damp,
+       show_bgp_ipv4_safi_neighbor_damp_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, safi,
+                                 bgp_show_type_damp_neighbor);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_damp,
+       show_bgp_ipv6_safi_neighbor_damp_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi,
+                                 bgp_show_type_damp_neighbor);
+}
+
+DEFUN (show_bgp_ipv4_safi_neighbor_routes,
+       show_bgp_ipv4_safi_neighbor_routes_cmd,
+       "show bgp ipv4 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  return bgp_show_neighbor_route (vty, peer, AFI_IP, safi,
+                                 bgp_show_type_neighbor);
+}
+
+DEFUN (show_bgp_ipv6_safi_neighbor_routes,
+       show_bgp_ipv6_safi_neighbor_routes_cmd,
+       "show bgp ipv6 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       NEIGHBOR_ADDR_STR
+       NEIGHBOR_ADDR_STR
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  peer = peer_lookup_in_view (vty, NULL, argv[1]);
+  if (! peer)
+    return CMD_WARNING;
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi,
+                                 bgp_show_type_neighbor);
+}
+
+DEFUN (show_bgp_view_ipv4_safi_rsclient_route,
+       show_bgp_view_ipv4_safi_rsclient_route_cmd,
+       "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  safi_t safi;
+
+  /* BGP structure lookup. */
+  if (argc == 4)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 4) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi],
+                                  (argc == 4) ? argv[3] : argv[2],
+                                  AFI_IP, safi, NULL, 0, BGP_PATH_ALL);
+}
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient_route,
+       show_bgp_ipv4_safi_rsclient_route_cmd,
+       "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+
+
+DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix,
+       show_bgp_view_ipv4_safi_rsclient_prefix_cmd,
+       "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  safi_t safi;
+
+  /* BGP structure lookup. */
+  if (argc == 4)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 4) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+{
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+    return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi],
+                                  (argc == 4) ? argv[3] : argv[2],
+                                  AFI_IP, safi, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_ip_bgp_view_rsclient_prefix,
+       show_ip_bgp_view_rsclient_prefix_cmd,
+       "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+    
+  if (! peer->afc[AFI_IP][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+{
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+    return CMD_WARNING;
+    }
+    
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST], 
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+ALIAS (show_ip_bgp_view_rsclient_prefix,
+       show_ip_bgp_rsclient_prefix_cmd,
+       "show ip bgp rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+ALIAS (show_bgp_view_ipv4_safi_rsclient_prefix,
+       show_bgp_ipv4_safi_rsclient_prefix_cmd,
+       "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+DEFUN (show_bgp_view_ipv6_neighbor_routes,
+       show_bgp_view_ipv6_neighbor_routes_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+   
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_neighbor);
+}
+
+DEFUN (show_bgp_view_neighbor_damp,
+       show_bgp_view_neighbor_damp_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_damp_neighbor);
+}
+
+DEFUN (show_bgp_view_ipv6_neighbor_damp,
+       show_bgp_view_ipv6_neighbor_damp_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_damp_neighbor);
+}
+
+DEFUN (show_bgp_view_ipv6_neighbor_flap,
+       show_bgp_view_ipv6_neighbor_flap_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_flap_neighbor);
+}
+
+DEFUN (show_bgp_view_neighbor_flap,
+       show_bgp_view_neighbor_flap_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_flap_neighbor);
+}
+
+ALIAS (show_bgp_view_neighbor_flap,
+       show_bgp_neighbor_flap_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+
+ALIAS (show_bgp_view_neighbor_damp,
+       show_bgp_neighbor_damp_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+
+DEFUN (show_bgp_view_neighbor_routes,
+       show_bgp_view_neighbor_routes_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+   
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST,
+                                 bgp_show_type_neighbor);
+}
+
+ALIAS (show_bgp_view_neighbor_routes,
+       show_bgp_neighbor_routes_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+
+ALIAS (show_bgp_view_neighbor_routes,
+       show_bgp_ipv6_neighbor_routes_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+
+/* old command */
+ALIAS (show_bgp_view_neighbor_routes,
+       ipv6_bgp_neighbor_routes_cmd,
+       "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+
+/* old command */
+DEFUN (ipv6_mbgp_neighbor_routes,
+       ipv6_mbgp_neighbor_routes_cmd,
+       "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) routes",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display routes learned from neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_lookup_in_view (vty, NULL, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+  return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_MULTICAST,
+                                 bgp_show_type_neighbor);
+}
+
+ALIAS (show_bgp_view_neighbor_flap,
+       show_bgp_ipv6_neighbor_flap_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display flap statistics of the routes learned from neighbor\n")
+
+ALIAS (show_bgp_view_neighbor_damp,
+       show_bgp_ipv6_neighbor_damp_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) dampened-routes",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n"
+       "Display the dampened routes received from neighbor\n")
+
+DEFUN (show_bgp_view_rsclient,
+       show_bgp_view_rsclient_cmd,
+       "show bgp view WORD rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+{
+  struct bgp_table *table;
+  struct peer *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP6][SAFI_UNICAST];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_rsclient,
+       show_bgp_rsclient_cmd,
+       "show bgp rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+
+DEFUN (show_bgp_view_ipv4_rsclient,
+       show_bgp_view_ipv4_rsclient_cmd,
+       "show bgp view WORD ipv4 rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address Family\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR2)
+{
+  struct bgp_table     *table;
+  struct peer          *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP][SAFI_UNICAST];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+DEFUN (show_bgp_view_ipv6_rsclient,
+       show_bgp_view_ipv6_rsclient_cmd,
+       "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "BGP view name\n"
+       "Address Family\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR2)
+{
+  struct bgp_table     *table;
+  struct peer          *peer;
+
+  if (argc == 2)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP6][SAFI_UNICAST];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_ipv4_rsclient,
+       show_bgp_ipv4_rsclient_cmd,
+       "show bgp ipv4 rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR2)
+
+ALIAS (show_bgp_view_ipv6_rsclient,
+       show_bgp_ipv6_rsclient_cmd,
+       "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR2)
+
+DEFUN (show_bgp_view_ipv6_safi_rsclient,
+       show_bgp_view_ipv6_safi_rsclient_cmd,
+       "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+{
+  struct bgp_table *table;
+  struct peer *peer;
+  safi_t safi;
+
+  if (argc == 3) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  table = peer->rib[AFI_IP6][safi];
+
+  return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient,
+       show_bgp_ipv6_safi_rsclient_cmd,
+       "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR)
+
+DEFUN (show_bgp_view_rsclient_route,
+       show_bgp_view_rsclient_route_cmd,
+       "show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST],
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_ipv6_rsclient_route,
+       show_bgp_view_ipv6_rsclient_route_cmd,
+       "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "BGP view name\n"
+       "IP6_STR"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST],
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL);
+}
+
+ALIAS (show_bgp_view_ipv6_rsclient_route,
+       show_bgp_rsclient_route_cmd,
+       "show bgp rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+
+ALIAS (show_bgp_view_ipv6_rsclient_route,
+       show_bgp_ipv6_rsclient_route_cmd,
+       "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       IP6_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+
+DEFUN (show_bgp_view_ipv6_safi_rsclient_route,
+       show_bgp_view_ipv6_safi_rsclient_route_cmd,
+       "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  safi_t safi;
+
+  /* BGP structure lookup. */
+  if (argc == 4)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 4) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi],
+                                  (argc == 4) ? argv[3] : argv[2],
+                                  AFI_IP6, safi, NULL, 0, BGP_PATH_ALL);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient_route,
+       show_bgp_ipv6_safi_rsclient_route_cmd,
+       "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "Network in the BGP routing table to display\n")
+
+
+DEFUN (show_bgp_view_rsclient_prefix,
+       show_bgp_view_rsclient_prefix_cmd,
+       "show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST],
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+DEFUN (show_bgp_view_ipv6_rsclient_prefix,
+       show_bgp_view_ipv6_rsclient_prefix_cmd,
+       "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       IP6_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+
+  /* BGP structure lookup. */
+  if (argc == 3)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (argc == 3)
+    peer = peer_lookup_in_view (vty, argv[0], argv[1]);
+  else
+    peer = peer_lookup_in_view (vty, NULL, argv[0]);
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][SAFI_UNICAST])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+              PEER_FLAG_RSERVER_CLIENT))
+    {
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST],
+                                  (argc == 3) ? argv[2] : argv[1],
+                                  AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL);
+}
+
+ALIAS (show_bgp_view_ipv6_rsclient_prefix,
+       show_bgp_rsclient_prefix_cmd,
+       "show bgp rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+
+ALIAS (show_bgp_view_ipv6_rsclient_prefix,
+       show_bgp_ipv6_rsclient_prefix_cmd,
+       "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+
+DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix,
+       show_bgp_view_ipv6_safi_rsclient_prefix_cmd,
+       "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  safi_t safi;
+
+  /* BGP structure lookup. */
+  if (argc == 4)
+    {
+      bgp = bgp_lookup_by_name (argv[0]);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (argc == 4) {
+    peer = peer_lookup_in_view (vty, argv[0], argv[2]);
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  } else {
+    peer = peer_lookup_in_view (vty, NULL, argv[1]);
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+  }
+
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! peer->afc[AFI_IP6][safi])
+    {
+      vty_out (vty, "%% Activate the neighbor for the address family first%s",
+            VTY_NEWLINE);
+      return CMD_WARNING;
+}
+
+  if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi],
+              PEER_FLAG_RSERVER_CLIENT))
+{
+      vty_out (vty, "%% Neighbor is not a Route-Server client%s",
+            VTY_NEWLINE);
+    return CMD_WARNING;
+    }
+
+  return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi],
+                                  (argc == 4) ? argv[3] : argv[2],
+                                  AFI_IP6, safi, NULL, 1, BGP_PATH_ALL);
+}
+
+ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix,
+       show_bgp_ipv6_safi_rsclient_prefix_cmd,
+       "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Client\n"
+       NEIGHBOR_ADDR_STR
+       "IP prefix <network>/<length>, e.g., 3ffe::/16\n")
+
+struct bgp_table *bgp_distance_table;
+
+struct bgp_distance
+{
+  /* Distance value for the IP source prefix. */
+  u_char distance;
+
+  /* Name of the access-list to be matched. */
+  char *access_list;
+};
+
+static struct bgp_distance *
+bgp_distance_new (void)
+{
+  return XCALLOC (MTYPE_BGP_DISTANCE, sizeof (struct bgp_distance));
+}
+
+static void
+bgp_distance_free (struct bgp_distance *bdistance)
+{
+  XFREE (MTYPE_BGP_DISTANCE, bdistance);
+}
+
+static int
+bgp_distance_set (struct vty *vty, const char *distance_str, 
+                  const char *ip_str, const char *access_list_str)
+{
+  int ret;
+  struct prefix p;
+  u_char distance;
+  struct bgp_node *rn;
+  struct bgp_distance *bdistance;
+
+  ret = str2prefix (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  /* Get BGP distance node. */
+  rn = bgp_node_get (bgp_distance_table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      bdistance = rn->info;
+      bgp_unlock_node (rn);
+    }
+  else
+    {
+      bdistance = bgp_distance_new ();
+      rn->info = bdistance;
+    }
+
+  /* Set distance value. */
+  bdistance->distance = distance;
+
+  /* Reset access-list configuration. */
+  if (bdistance->access_list)
+    {
+      free (bdistance->access_list);
+      bdistance->access_list = NULL;
+    }
+  if (access_list_str)
+    bdistance->access_list = strdup (access_list_str);
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_distance_unset (struct vty *vty, const char *distance_str, 
+                    const char *ip_str, const char *access_list_str)
+{
+  int ret;
+  struct prefix p;
+  u_char distance;
+  struct bgp_node *rn;
+  struct bgp_distance *bdistance;
+
+  ret = str2prefix (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  rn = bgp_node_lookup (bgp_distance_table, (struct prefix *)&p);
+  if (! rn)
+    {
+      vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bdistance = rn->info;
+  
+  if (bdistance->distance != distance)
+    {
+       vty_out (vty, "Distance does not match configured%s", VTY_NEWLINE);
+       return CMD_WARNING;
+    }
+  
+  if (bdistance->access_list)
+    free (bdistance->access_list);
+  bgp_distance_free (bdistance);
+
+  rn->info = NULL;
+  bgp_unlock_node (rn);
+  bgp_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+/* Apply BGP information to distance method. */
+u_char
+bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp)
+{
+  struct bgp_node *rn;
+  struct prefix_ipv4 q;
+  struct peer *peer;
+  struct bgp_distance *bdistance;
+  struct access_list *alist;
+  struct bgp_static *bgp_static;
+
+  if (! bgp)
+    return 0;
+
+  if (p->family != AF_INET)
+    return 0;
+
+  peer = rinfo->peer;
+
+  if (peer->su.sa.sa_family != AF_INET)
+    return 0;
+
+  memset (&q, 0, sizeof (struct prefix_ipv4));
+  q.family = AF_INET;
+  q.prefix = peer->su.sin.sin_addr;
+  q.prefixlen = IPV4_MAX_BITLEN;
+
+  /* Check source address. */
+  rn = bgp_node_match (bgp_distance_table, (struct prefix *) &q);
+  if (rn)
+    {
+      bdistance = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bdistance->access_list)
+       {
+         alist = access_list_lookup (AFI_IP, bdistance->access_list);
+         if (alist && access_list_apply (alist, p) == FILTER_PERMIT)
+           return bdistance->distance;
+       }
+      else
+       return bdistance->distance;
+    }
+
+  /* Backdoor check. */
+  rn = bgp_node_lookup (bgp->route[AFI_IP][SAFI_UNICAST], p);
+  if (rn)
+    {
+      bgp_static = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bgp_static->backdoor)
+       {
+         if (bgp->distance_local)
+           return bgp->distance_local;
+         else
+           return ZEBRA_IBGP_DISTANCE_DEFAULT;
+       }
+    }
+
+  if (peer->sort == BGP_PEER_EBGP)
+    {
+      if (bgp->distance_ebgp)
+       return bgp->distance_ebgp;
+      return ZEBRA_EBGP_DISTANCE_DEFAULT;
+    }
+  else
+    {
+      if (bgp->distance_ibgp)
+       return bgp->distance_ibgp;
+      return ZEBRA_IBGP_DISTANCE_DEFAULT;
+    }
+}
+
+#ifdef HAVE_IPV6
+/* Apply BGP information to ipv6 distance method. */
+u_char
+ipv6_bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp)
+{
+  struct bgp_node *rn;
+  struct prefix_ipv6 q;
+  struct peer *peer;
+  struct bgp_distance *bdistance;
+  struct access_list *alist;
+  struct bgp_static *bgp_static;
+
+  if (! bgp)
+    return 0;
+
+  if (p->family != AF_INET6)
+    return 0;
+
+  peer = rinfo->peer;
+
+  if (peer->su.sa.sa_family != AF_INET6)
+    return 0;
+
+  memset (&q, 0, sizeof (struct prefix_ipv6));
+  q.family = AF_INET;
+  q.prefix = peer->su.sin6.sin6_addr;
+  q.prefixlen = IPV6_MAX_BITLEN;
+
+  /* Check source address. */
+  rn = bgp_node_match (bgp_distance_table, (struct prefix *) &q);
+  if (rn)
+    {
+      bdistance = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bdistance->access_list)
+        {
+          alist = access_list_lookup (AFI_IP6, bdistance->access_list);
+          if (alist && access_list_apply (alist, p) == FILTER_PERMIT)
+            return bdistance->distance;
+        }
+      else
+        return bdistance->distance;
+    }
+  /* Backdoor check. */
+  rn = bgp_node_lookup (bgp->route[AFI_IP6][SAFI_UNICAST], p);
+  if (rn)
+    {
+      bgp_static = rn->info;
+      bgp_unlock_node (rn);
+
+      if (bgp_static->backdoor)
+        {
+          if (bgp->ipv6_distance_local)
+            return bgp->ipv6_distance_local;
+          else
+            return ZEBRA_IBGP_DISTANCE_DEFAULT;
+        }
+    }
+
+  if (peer_sort (peer) == BGP_PEER_EBGP)
+    {
+      if (bgp->ipv6_distance_ebgp)
+        return bgp->ipv6_distance_ebgp;
+      return ZEBRA_EBGP_DISTANCE_DEFAULT;
+    }
+  else
+    {
+      if (bgp->ipv6_distance_ibgp)
+        return bgp->ipv6_distance_ibgp;
+      return ZEBRA_IBGP_DISTANCE_DEFAULT;
+    }
+}
+#endif /* HAVE_IPV6 */
+
+DEFUN (bgp_distance,
+       bgp_distance_cmd,
+       "distance bgp <1-255> <1-255> <1-255>",
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->distance_ebgp = atoi (argv[0]);
+  bgp->distance_ibgp = atoi (argv[1]);
+  bgp->distance_local = atoi (argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance,
+       no_bgp_distance_cmd,
+       "no distance bgp <1-255> <1-255> <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->distance_ebgp= 0;
+  bgp->distance_ibgp = 0;
+  bgp->distance_local = 0;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_distance,
+       no_bgp_distance2_cmd,
+       "no distance bgp",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n")
+
+DEFUN (bgp_distance_source,
+       bgp_distance_source_cmd,
+       "distance <1-255> A.B.C.D/M",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance_source,
+       no_bgp_distance_source_cmd,
+       "no distance <1-255> A.B.C.D/M",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_distance_source_access_list,
+       bgp_distance_source_access_list_cmd,
+       "distance <1-255> A.B.C.D/M WORD",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance_source_access_list,
+       no_bgp_distance_source_access_list_cmd,
+       "no distance <1-255> A.B.C.D/M WORD",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+#ifdef HAVE_IPV6
+DEFUN (ipv6_bgp_distance,
+       ipv6_bgp_distance_cmd,
+       "distance bgp <1-255> <1-255> <1-255>",
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->ipv6_distance_ebgp = atoi (argv[0]);
+  bgp->ipv6_distance_ibgp = atoi (argv[1]);
+  bgp->ipv6_distance_local = atoi (argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance,
+       no_ipv6_bgp_distance_cmd,
+       "no distance bgp <1-255> <1-255> <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n"
+       "Distance for routes external to the AS\n"
+       "Distance for routes internal to the AS\n"
+       "Distance for local routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->ipv6_distance_ebgp= 0;
+  bgp->ipv6_distance_ibgp = 0;
+  bgp->ipv6_distance_local = 0;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_bgp_distance,
+       no_ipv6_bgp_distance2_cmd,
+       "no distance bgp",
+       NO_STR
+       "Define an administrative distance\n"
+       "BGP distance\n")
+
+DEFUN (ipv6_bgp_distance_source,
+       ipv6_bgp_distance_source_cmd,
+       "distance <1-255> X:X::X:X/M",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source,
+       no_ipv6_bgp_distance_source_cmd,
+       "no distance <1-255> X:X::X:X/M",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_bgp_distance_source_access_list,
+       ipv6_bgp_distance_source_access_list_cmd,
+       "distance <1-255> X:X::X:X/M WORD",
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_set (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source_access_list,
+       no_ipv6_bgp_distance_source_access_list_cmd,
+       "no distance <1-255> X:X::X:X/M WORD",
+       NO_STR
+       "Define an administrative distance\n"
+       "Administrative distance\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  bgp_distance_unset (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+#endif
+
+DEFUN (bgp_damp_set,
+       bgp_damp_set_cmd,
+       "bgp dampening <1-45> <1-20000> <1-20000> <1-255>",
+       "BGP Specific commands\n"
+       "Enable route-flap dampening\n"
+       "Half-life time for the penalty\n"
+       "Value to start reusing a route\n"
+       "Value to start suppressing a route\n"
+       "Maximum duration to suppress a stable route\n")
+{
+  struct bgp *bgp;
+  int half = DEFAULT_HALF_LIFE * 60;
+  int reuse = DEFAULT_REUSE;
+  int suppress = DEFAULT_SUPPRESS;
+  int max = 4 * half;
+
+  if (argc == 4)
+    {
+      half = atoi (argv[0]) * 60;
+      reuse = atoi (argv[1]);
+      suppress = atoi (argv[2]);
+      max = atoi (argv[3]) * 60;
+    }
+  else if (argc == 1)
+    {
+      half = atoi (argv[0]) * 60;
+      max = 4 * half;
+    }
+
+  bgp = vty->index;
+
+  if (suppress < reuse)
+    {
+      vty_out (vty, "Suppress value cannot be less than reuse value %s",
+                    VTY_NEWLINE);
+      return 0;
+    }
+
+  return bgp_damp_enable (bgp, bgp_node_afi (vty), bgp_node_safi (vty),
+                         half, reuse, suppress, max);
+}
+
+ALIAS (bgp_damp_set,
+       bgp_damp_set2_cmd,
+       "bgp dampening <1-45>",
+       "BGP Specific commands\n"
+       "Enable route-flap dampening\n"
+       "Half-life time for the penalty\n")
+
+ALIAS (bgp_damp_set,
+       bgp_damp_set3_cmd,
+       "bgp dampening",
+       "BGP Specific commands\n"
+       "Enable route-flap dampening\n")
+
+DEFUN (bgp_damp_unset,
+       bgp_damp_unset_cmd,
+       "no bgp dampening",
+       NO_STR
+       "BGP Specific commands\n"
+       "Enable route-flap dampening\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  return bgp_damp_disable (bgp, bgp_node_afi (vty), bgp_node_safi (vty));
+}
+
+ALIAS (bgp_damp_unset,
+       bgp_damp_unset2_cmd,
+       "no bgp dampening <1-45> <1-20000> <1-20000> <1-255>",
+       NO_STR
+       "BGP Specific commands\n"
+       "Enable route-flap dampening\n"
+       "Half-life time for the penalty\n"
+       "Value to start reusing a route\n"
+       "Value to start suppressing a route\n"
+       "Maximum duration to suppress a stable route\n")
+
+DEFUN (show_ip_bgp_dampened_paths,
+       show_ip_bgp_dampened_paths_cmd,
+       "show ip bgp dampened-paths",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display paths suppressed due to dampening\n")
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_dampend_paths,
+                   NULL);
+}
+
+ALIAS (show_ip_bgp_dampened_paths,
+       show_ip_bgp_damp_dampened_paths_cmd,
+       "show ip bgp dampening dampened-paths",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display paths suppressed due to dampening\n")
+
+DEFUN (show_ip_bgp_flap_statistics,
+       show_ip_bgp_flap_statistics_cmd,
+       "show ip bgp flap-statistics",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display flap statistics of routes\n")
+{
+  return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST,
+                   bgp_show_type_flap_statistics, NULL);
+}
+
+ALIAS (show_ip_bgp_flap_statistics,
+       show_ip_bgp_damp_flap_statistics_cmd,
+       "show ip bgp dampening flap-statistics",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n")
+
+DEFUN (show_bgp_ipv4_safi_dampened_paths,
+       show_bgp_ipv4_safi_dampened_paths_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampened-paths",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display paths suppressed due to dampening\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_dampend_paths, NULL);
+}
+ALIAS (show_bgp_ipv4_safi_dampened_paths,
+       show_bgp_ipv4_safi_damp_dampened_paths_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening dampened-paths",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display paths suppressed due to dampening\n")
+
+DEFUN (show_bgp_ipv6_safi_dampened_paths,
+       show_bgp_ipv6_safi_dampened_paths_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampened-paths",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display paths suppressed due to dampening\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show (vty, NULL, AFI_IP6, safi, bgp_show_type_dampend_paths, NULL);
+}
+ALIAS (show_bgp_ipv6_safi_dampened_paths,
+       show_bgp_ipv6_safi_damp_dampened_paths_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening dampened-paths",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display paths suppressed due to dampening\n")
+
+DEFUN (show_bgp_ipv4_safi_flap_statistics,
+       show_bgp_ipv4_safi_flap_statistics_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_flap_statistics, NULL);
+}
+ALIAS (show_bgp_ipv4_safi_flap_statistics,
+       show_bgp_ipv4_safi_damp_flap_statistics_cmd,
+       "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n")
+
+DEFUN (show_bgp_ipv6_safi_flap_statistics,
+       show_bgp_ipv6_safi_flap_statistics_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display flap statistics of routes\n")
+{
+  safi_t       safi;
+
+  if (bgp_parse_safi(argv[0], &safi)) {
+    vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return bgp_show (vty, NULL, AFI_IP6, safi, bgp_show_type_flap_statistics, NULL);
+}
+ALIAS (show_bgp_ipv6_safi_flap_statistics,
+       show_bgp_ipv6_safi_damp_flap_statistics_cmd,
+       "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics",
+       SHOW_STR
+       BGP_STR
+       "Address Family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Display detailed information about dampening\n"
+       "Display flap statistics of routes\n")
+
+/* Display specified route of BGP table. */
+static int
+bgp_clear_damp_route (struct vty *vty, const char *view_name, 
+                      const char *ip_str, afi_t afi, safi_t safi, 
+                      struct prefix_rd *prd, int prefix_check)
+{
+  int ret;
+  struct prefix match;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp_info *ri;
+  struct bgp_info *ri_temp;
+  struct bgp *bgp;
+  struct bgp_table *table;
+
+  /* BGP structure lookup. */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (bgp == NULL)
+       {
+         vty_out (vty, "%% Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+       {
+         vty_out (vty, "%% No BGP process is configured%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  /* Check IP address argument. */
+  ret = str2prefix (ip_str, &match);
+  if (! ret)
+    {
+      vty_out (vty, "%% address is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  match.family = afi2family (afi);
+
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+    {
+      for (rn = bgp_table_top (bgp->rib[AFI_IP][safi]); rn; rn = bgp_route_next (rn))
+        {
+          if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+            continue;
+
+         if ((table = rn->info) != NULL)
+           if ((rm = bgp_node_match (table, &match)) != NULL)
+              {
+                if (! prefix_check || rm->p.prefixlen == match.prefixlen)
+                  {
+                    ri = rm->info;
+                    while (ri)
+                      {
+                        if (ri->extra && ri->extra->damp_info)
+                          {
+                            ri_temp = ri->next;
+                            bgp_damp_info_free (ri->extra->damp_info, 1);
+                            ri = ri_temp;
+                          }
+                        else
+                          ri = ri->next;
+                      }
+                  }
+
+                bgp_unlock_node (rm);
+              }
+        }
+    }
+  else
+    {
+      if ((rn = bgp_node_match (bgp->rib[afi][safi], &match)) != NULL)
+        {
+          if (! prefix_check || rn->p.prefixlen == match.prefixlen)
+            {
+              ri = rn->info;
+              while (ri)
+                {
+                  if (ri->extra && ri->extra->damp_info)
+                    {
+                      ri_temp = ri->next;
+                      bgp_damp_info_free (ri->extra->damp_info, 1);
+                      ri = ri_temp;
+                    }
+                  else
+                    ri = ri->next;
+                }
+            }
+
+          bgp_unlock_node (rn);
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_bgp_dampening,
+       clear_ip_bgp_dampening_cmd,
+       "clear ip bgp dampening",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear route flap dampening information\n")
+{
+  bgp_damp_info_clean ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_bgp_dampening_prefix,
+       clear_ip_bgp_dampening_prefix_cmd,
+       "clear ip bgp dampening A.B.C.D/M",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear route flap dampening information\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_clear_damp_route (vty, NULL, argv[0], AFI_IP,
+                              SAFI_UNICAST, NULL, 1);
+}
+
+DEFUN (clear_ip_bgp_dampening_address,
+       clear_ip_bgp_dampening_address_cmd,
+       "clear ip bgp dampening A.B.C.D",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear route flap dampening information\n"
+       "Network to clear damping information\n")
+{
+  return bgp_clear_damp_route (vty, NULL, argv[0], AFI_IP,
+                              SAFI_UNICAST, NULL, 0);
+}
+
+DEFUN (clear_ip_bgp_dampening_address_mask,
+       clear_ip_bgp_dampening_address_mask_cmd,
+       "clear ip bgp dampening A.B.C.D A.B.C.D",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear route flap dampening information\n"
+       "Network to clear damping information\n"
+       "Network mask\n")
+{
+  int ret;
+  char prefix_str[BUFSIZ];
+
+  ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str);
+  if (! ret)
+    {
+      vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_clear_damp_route (vty, NULL, prefix_str, AFI_IP,
+                              SAFI_UNICAST, NULL, 0);
+}
+
+/* also used for encap safi */
+static int
+bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp,
+                               afi_t afi, safi_t safi, int *write)
+{
+  struct bgp_node *prn;
+  struct bgp_node *rn;
+  struct bgp_table *table;
+  struct prefix *p;
+  struct prefix_rd *prd;
+  struct bgp_static *bgp_static;
+  u_int32_t label;
+  char buf[SU_ADDRSTRLEN];
+  char rdbuf[RD_ADDRSTRLEN];
+  
+  /* Network configuration. */
+  for (prn = bgp_table_top (bgp->route[afi][safi]); prn; prn = bgp_route_next (prn))
+    if ((table = prn->info) != NULL)
+      for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) 
+       if ((bgp_static = rn->info) != NULL)
+         {
+           p = &rn->p;
+           prd = (struct prefix_rd *) &prn->p;
+
+           /* "address-family" display.  */
+           bgp_config_write_family_header (vty, afi, safi, write);
+
+           /* "network" configuration display.  */
+           prefix_rd2str (prd, rdbuf, RD_ADDRSTRLEN);
+           label = decode_label (bgp_static->tag);
+
+           vty_out (vty, " network %s/%d rd %s tag %d",
+                    inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), 
+                    p->prefixlen,
+                    rdbuf, label);
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+  return 0;
+}
+
+/* Configuration of static route announcement and aggregate
+   information. */
+int
+bgp_config_write_network (struct vty *vty, struct bgp *bgp,
+                         afi_t afi, safi_t safi, int *write)
+{
+  struct bgp_node *rn;
+  struct prefix *p;
+  struct bgp_static *bgp_static;
+  struct bgp_aggregate *bgp_aggregate;
+  char buf[SU_ADDRSTRLEN];
+  
+  if (afi == AFI_IP && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)))
+    return bgp_config_write_network_vpnv4 (vty, bgp, afi, safi, write);
+
+  /* Network configuration. */
+  for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn)) 
+    if ((bgp_static = rn->info) != NULL)
+      {
+       p = &rn->p;
+
+       /* "address-family" display.  */
+       bgp_config_write_family_header (vty, afi, safi, write);
+
+       /* "network" configuration display.  */
+       if (bgp_option_check (BGP_OPT_CONFIG_CISCO) && afi == AFI_IP)
+         {
+           u_int32_t destination; 
+           struct in_addr netmask;
+
+           destination = ntohl (p->u.prefix4.s_addr);
+           masklen2ip (p->prefixlen, &netmask);
+           vty_out (vty, " network %s",
+                    inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN));
+
+           if ((IN_CLASSC (destination) && p->prefixlen == 24)
+               || (IN_CLASSB (destination) && p->prefixlen == 16)
+               || (IN_CLASSA (destination) && p->prefixlen == 8)
+               || p->u.prefix4.s_addr == 0)
+             {
+               /* Natural mask is not display. */
+             }
+           else
+             vty_out (vty, " mask %s", inet_ntoa (netmask));
+         }
+       else
+         {
+           vty_out (vty, " network %s/%d",
+                    inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), 
+                    p->prefixlen);
+         }
+
+       if (bgp_static->rmap.name)
+         vty_out (vty, " route-map %s", bgp_static->rmap.name);
+       else 
+         {
+           if (bgp_static->backdoor)
+             vty_out (vty, " backdoor");
+          }
+
+       vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+  /* Aggregate-address configuration. */
+  for (rn = bgp_table_top (bgp->aggregate[afi][safi]); rn; rn = bgp_route_next (rn))
+    if ((bgp_aggregate = rn->info) != NULL)
+      {
+       p = &rn->p;
+
+       /* "address-family" display.  */
+       bgp_config_write_family_header (vty, afi, safi, write);
+
+       if (bgp_option_check (BGP_OPT_CONFIG_CISCO) && afi == AFI_IP)
+         {
+           struct in_addr netmask;
+
+           masklen2ip (p->prefixlen, &netmask);
+           vty_out (vty, " aggregate-address %s %s",
+                    inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                    inet_ntoa (netmask));
+         }
+       else
+         {
+           vty_out (vty, " aggregate-address %s/%d",
+                    inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
+                    p->prefixlen);
+         }
+
+       if (bgp_aggregate->as_set)
+         vty_out (vty, " as-set");
+       
+       if (bgp_aggregate->summary_only)
+         vty_out (vty, " summary-only");
+
+       vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+  return 0;
+}
+
+int
+bgp_config_write_distance (struct vty *vty, struct bgp *bgp,
+                           afi_t afi, safi_t safi, int *write)
+{
+  struct bgp_node *rn;
+  struct bgp_distance *bdistance;
+
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    {
+      /* Distance configuration. */
+      if (bgp->distance_ebgp
+          && bgp->distance_ibgp
+          && bgp->distance_local
+          && (bgp->distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT
+              || bgp->distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT
+              || bgp->distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT))
+        vty_out (vty, " distance bgp %d %d %d%s",
+                 bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local,
+                 VTY_NEWLINE);
+
+      for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
+        if ((bdistance = rn->info) != NULL)
+          {
+            vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
+                     inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+                     bdistance->access_list ? bdistance->access_list : "",
+                     VTY_NEWLINE);
+          }
+    }
+
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6 && safi == SAFI_UNICAST)
+    {
+      bgp_config_write_family_header (vty, afi, safi, write);
+      if (bgp->ipv6_distance_ebgp
+          && bgp->ipv6_distance_ibgp
+          && bgp->ipv6_distance_local
+          && (bgp->ipv6_distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT
+              || bgp->ipv6_distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT
+              || bgp->ipv6_distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT))
+        vty_out (vty, " distance bgp %d %d %d%s",
+                 bgp->ipv6_distance_ebgp, bgp->ipv6_distance_ibgp, bgp->ipv6_distance_local,
+                 VTY_NEWLINE);
+
+      for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn))
+        if ((bdistance = rn->info) != NULL)
+          {
+            vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance,
+                     inet6_ntoa (rn->p.u.prefix6), rn->p.prefixlen,
+                     bdistance->access_list ? bdistance->access_list : "",
+                     VTY_NEWLINE);
+          }
+    }
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/* Allocate routing table structure and install commands. */
+void
+bgp_route_init (void)
+{
+  /* Init BGP distance table. */
+  bgp_distance_table = bgp_table_init (AFI_IP, SAFI_UNICAST);
+
+  /* IPv4 BGP commands. */
+  install_element (BGP_NODE, &bgp_network_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_natural_cmd);
+  install_element (BGP_NODE, &bgp_network_route_map_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_route_map_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_natural_route_map_cmd);
+  install_element (BGP_NODE, &bgp_network_backdoor_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_backdoor_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_cmd);
+  install_element (BGP_NODE, &no_bgp_network_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_natural_cmd);
+  install_element (BGP_NODE, &no_bgp_network_route_map_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_route_map_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_natural_route_map_cmd);
+  install_element (BGP_NODE, &no_bgp_network_backdoor_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_backdoor_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_cmd);
+
+  install_element (BGP_NODE, &aggregate_address_cmd);
+  install_element (BGP_NODE, &aggregate_address_mask_cmd);
+  install_element (BGP_NODE, &aggregate_address_summary_only_cmd);
+  install_element (BGP_NODE, &aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_NODE, &aggregate_address_as_set_cmd);
+  install_element (BGP_NODE, &aggregate_address_mask_as_set_cmd);
+  install_element (BGP_NODE, &aggregate_address_as_set_summary_cmd);
+  install_element (BGP_NODE, &aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_NODE, &aggregate_address_summary_as_set_cmd);
+  install_element (BGP_NODE, &aggregate_address_mask_summary_as_set_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_summary_only_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_as_set_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_as_set_summary_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_summary_as_set_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_mask_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_mask_as_set_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_NODE, &no_aggregate_address_mask_summary_as_set_cmd);
+
+  /* IPv4 unicast configuration.  */
+  install_element (BGP_IPV4_NODE, &bgp_network_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_route_map_cmd);
+  
+  install_element (BGP_IPV4_NODE, &aggregate_address_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_mask_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_summary_only_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_mask_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_as_set_summary_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_summary_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &aggregate_address_mask_summary_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_summary_only_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_as_set_summary_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_summary_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_as_set_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_summary_as_set_cmd);
+
+  /* IPv4 multicast configuration.  */
+  install_element (BGP_IPV4M_NODE, &bgp_network_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_mask_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_summary_only_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_mask_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_as_set_summary_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_summary_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &aggregate_address_mask_summary_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_summary_only_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_as_set_summary_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_summary_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_summary_only_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_as_set_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_as_set_summary_cmd);
+  install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_summary_as_set_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rd_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rd_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rd_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rd_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_afi_safi_view_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_list_exact_cmd);
+
+  /* large-communities */
+  install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity_list_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_address_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_flap_address_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_address_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
+  
+  /* Restricted node: VIEW_NODE - (set of dangerous commands) */
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_rd_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_rd_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rd_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rd_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_encap_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_encap_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rd_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rd_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_all_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community4_exact_cmd);
+
+  /* large-community */
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity_all_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity4_cmd);
+
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd);
+
+  /* BGP dampening clear commands */
+  install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd);
+
+  install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd);
+
+  /* New config IPv6 BGP commands.  */
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_network_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_route_map_cmd);
+
+  install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_summary_only_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_summary_only_cmd);
+
+  install_element (BGP_IPV6M_NODE, &ipv6_bgp_network_cmd);
+  install_element (BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd);
+
+  /* Old config IPv6 BGP commands.  */
+  install_element (BGP_NODE, &old_ipv6_bgp_network_cmd);
+  install_element (BGP_NODE, &old_no_ipv6_bgp_network_cmd);
+
+  install_element (BGP_NODE, &old_ipv6_aggregate_address_cmd);
+  install_element (BGP_NODE, &old_ipv6_aggregate_address_summary_only_cmd);
+  install_element (BGP_NODE, &old_no_ipv6_aggregate_address_cmd);
+  install_element (BGP_NODE, &old_no_ipv6_aggregate_address_summary_only_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_community_list_cmd);
+
+  /* large-community */
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity_list_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd); 
+  install_element (VIEW_NODE, &show_bgp_view_ipv4_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
+  
+  /* Restricted:
+   * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev) 
+   */
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd);
+
+  /* Statistics */
+  install_element (ENABLE_NODE, &show_bgp_statistics_cmd);
+  install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd);  
+
+  install_element (BGP_NODE, &bgp_distance_cmd);
+  install_element (BGP_NODE, &no_bgp_distance_cmd);
+  install_element (BGP_NODE, &no_bgp_distance2_cmd);
+  install_element (BGP_NODE, &bgp_distance_source_cmd);
+  install_element (BGP_NODE, &no_bgp_distance_source_cmd);
+  install_element (BGP_NODE, &bgp_distance_source_access_list_cmd);
+  install_element (BGP_NODE, &no_bgp_distance_source_access_list_cmd);
+#ifdef HAVE_IPV6
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance2_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_cmd);
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_access_list_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_access_list_cmd);
+#endif /* HAVE_IPV6 */
+
+  install_element (BGP_NODE, &bgp_damp_set_cmd);
+  install_element (BGP_NODE, &bgp_damp_set2_cmd);
+  install_element (BGP_NODE, &bgp_damp_set3_cmd);
+  install_element (BGP_NODE, &bgp_damp_unset_cmd);
+  install_element (BGP_NODE, &bgp_damp_unset2_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_damp_set_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_damp_set2_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_damp_set3_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_damp_unset_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_damp_unset2_cmd);
+
+  /* IPv4 Multicast Mode */
+  install_element (BGP_IPV4M_NODE, &bgp_damp_set_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_damp_set2_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_damp_set3_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_damp_unset_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_damp_unset2_cmd);
+
+  
+  /* Deprecated AS-Pathlimit commands */
+  install_element (BGP_NODE, &bgp_network_ttl_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_ttl_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_NODE, &bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
+  
+  install_element (BGP_NODE, &no_bgp_network_ttl_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_ttl_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_NODE, &no_bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd);
+  
+  install_element (BGP_IPV4_NODE, &bgp_network_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
+  
+  install_element (BGP_IPV4_NODE, &no_bgp_network_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd);
+  
+  install_element (BGP_IPV4M_NODE, &bgp_network_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd);
+  
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_backdoor_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_backdoor_ttl_cmd);
+  install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd);
+
+  install_element (BGP_IPV6_NODE, &ipv6_bgp_network_ttl_cmd);
+  install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_ttl_cmd);
+
+  /* old style commands */
+  install_element (VIEW_NODE, &show_ip_bgp_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_route_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_regexp_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_regexp_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_route_map_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_route_map_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community_all_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_all_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community2_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community3_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community4_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community2_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community3_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community4_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_dampening_params_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_parameters_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_dampd_paths_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_flap_stats_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_dampened_paths_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_statistics_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_address_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_address_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_cidr_only_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_regexp_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_damp_flap_route_map_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_rsclient_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_rsclient_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
+  
+  install_element (RESTRICTED_NODE, &show_ip_bgp_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_route_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_view_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_view_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_prefix_cmd);
+  
+  install_element (VIEW_NODE, &show_ip_bgp_neighbor_prefix_counts_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_prefix_counts_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_cmd);
+  install_element (VIEW_NODE, &show_bgp_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_route_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_route_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd);
+  install_element (VIEW_NODE, &show_bgp_regexp_cmd);
+  install_element (VIEW_NODE, &show_bgp_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_filter_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_route_map_cmd);
+  install_element (VIEW_NODE, &show_bgp_community_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_community_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_bgp_rsclient_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_prefix_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_flap_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_neighbor_damp_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_rsclient_cmd);
+  
+  install_element (RESTRICTED_NODE, &show_bgp_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_route_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd);
+
+  install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd);
+  install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_bgp_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_route_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_regexp_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community2_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community3_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community4_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_route_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_regexp_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_filter_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community2_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community3_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community4_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community4_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_exact_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_longer_cmd);
+  install_element (VIEW_NODE, &ipv6_bgp_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd);
+  install_element (VIEW_NODE, &ipv6_bgp_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &ipv6_mbgp_neighbor_received_routes_cmd);
+  install_element (VIEW_NODE, &ipv6_bgp_neighbor_routes_cmd);
+  install_element (VIEW_NODE, &ipv6_mbgp_neighbor_routes_cmd);
+  /* old with name safi collision */
+  install_element (VIEW_NODE, &show_bgp_ipv6_community_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community2_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community3_exact_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community4_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_exact_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_exact_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_community_list_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_community_list_exact_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity2_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity3_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity4_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity2_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity3_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity4_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_list_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_rsclient_prefix_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_view_rsclient_route_cmd);
+  install_element (VIEW_NODE, &show_bgp_view_rsclient_prefix_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_route_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_prefix_cmd);
+}
+
+void
+bgp_route_finish (void)
+{
+  bgp_table_unlock (bgp_distance_table);
+  bgp_distance_table = NULL;
+}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
new file mode 100644 (file)
index 0000000..332714c
--- /dev/null
@@ -0,0 +1,273 @@
+/* BGP routing information base
+   Copyright (C) 1996, 97, 98, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ROUTE_H
+#define _QUAGGA_BGP_ROUTE_H
+
+#include "queue.h"
+#include "bgp_table.h"
+
+struct bgp_nexthop_cache;
+
+/* Ancillary information to struct bgp_info, 
+ * used for uncommonly used data (aggregation, MPLS, etc.)
+ * and lazily allocated to save memory.
+ */
+struct bgp_info_extra
+{
+  /* Pointer to dampening structure.  */
+  struct bgp_damp_info *damp_info;
+
+  /* This route is suppressed with aggregation.  */
+  int suppress;
+
+  /* Nexthop reachability check.  */
+  u_int32_t igpmetric;
+
+  /* MPLS label.  */
+  u_char tag[3];  
+};
+
+struct bgp_info
+{
+  /* For linked list. */
+  struct bgp_info *next;
+  struct bgp_info *prev;
+
+  /* For nexthop linked list */
+  LIST_ENTRY(bgp_info) nh_thread;
+
+  /* Back pointer to the prefix node */
+  struct bgp_node *net;
+
+  /* Back pointer to the nexthop structure */
+  struct bgp_nexthop_cache *nexthop;
+
+  /* Peer structure.  */
+  struct peer *peer;
+
+  /* Attribute structure.  */
+  struct attr *attr;
+  
+  /* Extra information */
+  struct bgp_info_extra *extra;
+  
+
+  /* Multipath information */
+  struct bgp_info_mpath *mpath;
+
+  /* Uptime.  */
+  time_t uptime;
+
+  /* reference count */
+  int lock;
+  
+  /* BGP information status.  */
+  u_int16_t flags;
+#define BGP_INFO_IGP_CHANGED    (1 << 0)
+#define BGP_INFO_DAMPED         (1 << 1)
+#define BGP_INFO_HISTORY        (1 << 2)
+#define BGP_INFO_SELECTED       (1 << 3)
+#define BGP_INFO_VALID          (1 << 4)
+#define BGP_INFO_ATTR_CHANGED   (1 << 5)
+#define BGP_INFO_DMED_CHECK     (1 << 6)
+#define BGP_INFO_DMED_SELECTED  (1 << 7)
+#define BGP_INFO_STALE          (1 << 8)
+#define BGP_INFO_REMOVED        (1 << 9)
+#define BGP_INFO_COUNTED       (1 << 10)
+#define BGP_INFO_MULTIPATH      (1 << 11)
+#define BGP_INFO_MULTIPATH_CHG  (1 << 12)
+
+  /* BGP route type.  This can be static, RIP, OSPF, BGP etc.  */
+  u_char type;
+
+  /* When above type is BGP.  This sub type specify BGP sub type
+     information.  */
+  u_char sub_type;
+#define BGP_ROUTE_NORMAL       0
+#define BGP_ROUTE_STATIC       1
+#define BGP_ROUTE_AGGREGATE    2
+#define BGP_ROUTE_REDISTRIBUTE 3 
+};
+
+/* BGP static route configuration. */
+struct bgp_static
+{
+  /* Backdoor configuration.  */
+  int backdoor;
+
+  /* Import check status.  */
+  u_char valid;
+
+  /* IGP metric. */
+  u_int32_t igpmetric;
+
+  /* IGP nexthop. */
+  struct in_addr igpnexthop;
+
+  /* Atomic set reference count (ie cause of pathlimit) */
+  u_int32_t atomic;
+  
+  /* BGP redistribute route-map.  */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } rmap;
+
+  /* Route Distinguisher */
+  struct prefix_rd     prd;
+
+  /* MPLS label.  */
+  u_char tag[3];
+};
+
+#define BGP_INFO_COUNTABLE(BI) \
+  (! CHECK_FLAG ((BI)->flags, BGP_INFO_HISTORY) \
+   && ! CHECK_FLAG ((BI)->flags, BGP_INFO_REMOVED))
+
+/* Flags which indicate a route is unuseable in some form */
+#define BGP_INFO_UNUSEABLE \
+  (BGP_INFO_HISTORY|BGP_INFO_DAMPED|BGP_INFO_REMOVED)
+/* Macro to check BGP information is alive or not.  Sadly,
+ * not equivalent to just checking previous, because of the
+ * sense of the additional VALID flag.
+ */
+#define BGP_INFO_HOLDDOWN(BI) \
+  (! CHECK_FLAG ((BI)->flags, BGP_INFO_VALID) \
+   || CHECK_FLAG ((BI)->flags, BGP_INFO_UNUSEABLE))
+
+#define DISTRIBUTE_IN_NAME(F)   ((F)->dlist[FILTER_IN].name)
+#define DISTRIBUTE_IN(F)        ((F)->dlist[FILTER_IN].alist)
+#define DISTRIBUTE_OUT_NAME(F)  ((F)->dlist[FILTER_OUT].name)
+#define DISTRIBUTE_OUT(F)       ((F)->dlist[FILTER_OUT].alist)
+
+#define PREFIX_LIST_IN_NAME(F)  ((F)->plist[FILTER_IN].name)
+#define PREFIX_LIST_IN(F)       ((F)->plist[FILTER_IN].plist)
+#define PREFIX_LIST_OUT_NAME(F) ((F)->plist[FILTER_OUT].name)
+#define PREFIX_LIST_OUT(F)      ((F)->plist[FILTER_OUT].plist)
+
+#define FILTER_LIST_IN_NAME(F)  ((F)->aslist[FILTER_IN].name)
+#define FILTER_LIST_IN(F)       ((F)->aslist[FILTER_IN].aslist)
+#define FILTER_LIST_OUT_NAME(F) ((F)->aslist[FILTER_OUT].name)
+#define FILTER_LIST_OUT(F)      ((F)->aslist[FILTER_OUT].aslist)
+
+#define ROUTE_MAP_IN_NAME(F)    ((F)->map[RMAP_IN].name)
+#define ROUTE_MAP_IN(F)         ((F)->map[RMAP_IN].map)
+#define ROUTE_MAP_OUT_NAME(F)   ((F)->map[RMAP_OUT].name)
+#define ROUTE_MAP_OUT(F)        ((F)->map[RMAP_OUT].map)
+
+#define ROUTE_MAP_IMPORT_NAME(F)    ((F)->map[RMAP_IMPORT].name)
+#define ROUTE_MAP_IMPORT(F)    ((F)->map[RMAP_IMPORT].map)
+#define ROUTE_MAP_EXPORT_NAME(F)    ((F)->map[RMAP_EXPORT].name)
+#define ROUTE_MAP_EXPORT(F)    ((F)->map[RMAP_EXPORT].map)
+
+#define UNSUPPRESS_MAP_NAME(F)  ((F)->usmap.name)
+#define UNSUPPRESS_MAP(F)       ((F)->usmap.map)
+
+enum bgp_clear_route_type
+{
+  BGP_CLEAR_ROUTE_NORMAL,
+  BGP_CLEAR_ROUTE_MY_RSCLIENT
+};
+
+enum bgp_path_type
+{
+  BGP_PATH_ALL,
+  BGP_PATH_BESTPATH,
+  BGP_PATH_MULTIPATH
+};
+
+/* Prototypes. */
+extern void bgp_route_init (void);
+extern void bgp_route_finish (void);
+extern void bgp_cleanup_routes (void);
+extern void bgp_announce_route (struct peer *, afi_t, safi_t);
+extern void bgp_announce_route_all (struct peer *);
+extern void bgp_default_originate (struct peer *, afi_t, safi_t, int);
+extern void bgp_soft_reconfig_in (struct peer *, afi_t, safi_t);
+extern void bgp_soft_reconfig_rsclient (struct peer *, afi_t, safi_t);
+extern void bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi);
+extern void bgp_clear_route (struct peer *, afi_t, safi_t,
+                             enum bgp_clear_route_type);
+extern void bgp_clear_route_all (struct peer *);
+extern void bgp_clear_adj_in (struct peer *, afi_t, safi_t);
+extern void bgp_clear_stale_route (struct peer *, afi_t, safi_t);
+
+extern struct bgp_info *bgp_info_lock (struct bgp_info *);
+extern struct bgp_info *bgp_info_unlock (struct bgp_info *);
+extern void bgp_info_add (struct bgp_node *rn, struct bgp_info *ri);
+extern void bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri);
+extern struct bgp_info_extra *bgp_info_extra_get (struct bgp_info *);
+extern void bgp_info_set_flag (struct bgp_node *, struct bgp_info *, u_int32_t);
+extern void bgp_info_unset_flag (struct bgp_node *, struct bgp_info *, u_int32_t);
+
+extern int bgp_nlri_parse_ip (struct peer *, struct attr *, struct bgp_nlri *);
+
+extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int);
+
+extern void bgp_redistribute_add (struct prefix *, const struct in_addr *,
+                                 const struct in6_addr *,
+                                 u_int32_t, u_char, route_tag_t);
+extern void bgp_redistribute_delete (struct prefix *, u_char);
+extern void bgp_redistribute_withdraw (struct bgp *, afi_t, int);
+
+extern void bgp_static_delete (struct bgp *);
+extern void bgp_static_update (struct bgp *, struct prefix *, struct bgp_static *,
+                       afi_t, safi_t);
+extern void bgp_static_withdraw (struct bgp *, struct prefix *, afi_t, safi_t);
+                     
+extern int bgp_static_set_safi (safi_t safi, struct vty *vty, const char *,
+                          const char *, const char *, const char *);
+
+extern int bgp_static_unset_safi (safi_t safi, struct vty *, const char *,
+                            const char *, const char *);
+
+/* this is primarily for MPLS-VPN */
+extern int bgp_update (struct peer *, struct prefix *, struct attr *,
+                      afi_t, safi_t, int, int, struct prefix_rd *, 
+                      u_char *, int);
+extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *,
+                        afi_t, safi_t, int, int, struct prefix_rd *, u_char *);
+
+/* for bgp_nexthop and bgp_damp */
+extern void bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
+extern int bgp_config_write_network (struct vty *, struct bgp *, afi_t, safi_t, int *);
+extern int bgp_config_write_distance (struct vty *, struct bgp *, afi_t, safi_t, int *);
+
+extern void bgp_aggregate_increment (struct bgp *, struct prefix *, struct bgp_info *,
+                             afi_t, safi_t);
+extern void bgp_aggregate_decrement (struct bgp *, struct prefix *, struct bgp_info *,
+                             afi_t, safi_t);
+
+extern u_char bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *);
+extern u_char ipv6_bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *);
+
+extern afi_t bgp_node_afi (struct vty *);
+extern safi_t bgp_node_safi (struct vty *);
+
+extern void route_vty_out (struct vty *, struct prefix *, struct bgp_info *, int, safi_t);
+extern void route_vty_out_tag (struct vty *, struct prefix *, struct bgp_info *, int, safi_t);
+extern void route_vty_out_tmp (struct vty *, struct prefix *, struct attr *, safi_t);
+
+extern void bgp_peer_clear_node_queue_drain_immediate (struct peer *peer);
+extern void bgp_process_queues_drain_immediate (void);
+
+#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
new file mode 100644 (file)
index 0000000..ccd73b6
--- /dev/null
@@ -0,0 +1,4690 @@
+/* Route map function of bgpd.
+   Copyright (C) 1998, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "routemap.h"
+#include "command.h"
+#include "linklist.h"
+#include "plist.h"
+#include "memory.h"
+#include "log.h"
+#ifdef HAVE_LIBPCREPOSIX
+# include <pcreposix.h>
+#else
+# ifdef HAVE_GNU_REGEX
+#  include <regex.h>
+# else
+#  include "regex-gnu.h"
+# endif /* HAVE_GNU_REGEX */
+#endif /* HAVE_LIBPCREPOSIX */
+#include "buffer.h"
+#include "sockunion.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_vty.h"
+
+/* Memo of route-map commands.
+
+o Cisco route-map
+
+ match as-path          :  Done
+       community        :  Done
+       lcommunity       :  Done
+       interface        :  Not yet
+       ip address       :  Done
+       ip next-hop      :  Done
+       ip route-source  :  Done
+       ip prefix-list   :  Done
+       ipv6 address     :  Done
+       ipv6 next-hop    :  Done
+       ipv6 route-source:  (This will not be implemented by bgpd)
+       ipv6 prefix-list :  Done
+       length           :  (This will not be implemented by bgpd)
+       metric           :  Done
+       route-type       :  (This will not be implemented by bgpd)
+       tag              :  Done
+       local-preference :  Done
+
+ set  as-path prepend   :  Done
+      as-path tag       :  Not yet
+      automatic-tag     :  (This will not be implemented by bgpd)
+      community         :  Done
+      large-community   :  Done
+      large-comm-list   :  Done
+      comm-list         :  Not yet
+      dampning          :  Not yet
+      default           :  (This will not be implemented by bgpd)
+      interface         :  (This will not be implemented by bgpd)
+      ip default        :  (This will not be implemented by bgpd)
+      ip next-hop       :  Done
+      ip precedence     :  (This will not be implemented by bgpd)
+      ip tos            :  (This will not be implemented by bgpd)
+      level             :  (This will not be implemented by bgpd)
+      local-preference  :  Done
+      metric            :  Done
+      metric-type       :  Not yet
+      origin            :  Done
+      tag               :  Done
+      weight            :  Done
+
+o Local extensions
+
+  set ipv6 next-hop global: Done
+  set ipv6 next-hop local : Done
+  set as-path exclude     : Done
+
+*/ 
+
+ /* generic value manipulation to be shared in multiple rules */
+
+#define RMAP_VALUE_SET 0
+#define RMAP_VALUE_ADD 1
+#define RMAP_VALUE_SUB 2
+
+struct rmap_value
+{
+  u_int8_t action;
+  u_int8_t variable;
+  u_int32_t value;
+};
+
+static int
+route_value_match (struct rmap_value *rv, u_int32_t value)
+{
+  if (rv->variable == 0 && value == rv->value)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static u_int32_t
+route_value_adjust (struct rmap_value *rv, u_int32_t current, struct peer *peer)
+{
+  u_int32_t value;
+
+  switch (rv->variable)
+    {
+    case 1:
+      value = peer->rtt;
+      break;
+    default:
+      value = rv->value;
+      break;
+    }
+
+  switch (rv->action)
+    {
+    case RMAP_VALUE_ADD:
+      if (current > UINT32_MAX-value)
+        return UINT32_MAX;
+      return current + value;
+    case RMAP_VALUE_SUB:
+      if (current <= value)
+        return 0;
+      return current - value;
+    default:
+      return value;
+    }
+}
+
+static void *
+route_value_compile (const char *arg)
+{
+  u_int8_t action = RMAP_VALUE_SET, var = 0;
+  unsigned long larg = 0;
+  char *endptr = NULL;
+  struct rmap_value *rv;
+
+  if (arg[0] == '+')
+    {
+      action = RMAP_VALUE_ADD;
+      arg++;
+    }
+  else if (arg[0] == '-')
+    {
+      action = RMAP_VALUE_SUB;
+      arg++;
+    }
+
+  if (all_digit(arg))
+    {
+      errno = 0;
+      larg = strtoul (arg, &endptr, 10);
+      if (*arg == 0 || *endptr != 0 || errno || larg > UINT32_MAX)
+        return NULL;
+    }
+  else
+    {
+      if (strcmp(arg, "rtt") == 0)
+        var = 1;
+      else
+        return NULL;
+    }
+
+  rv = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_value));
+  if (!rv)
+    return NULL;
+
+  rv->action = action;
+  rv->variable = var;
+  rv->value = larg;
+  return rv;
+}
+
+static void
+route_value_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+ /* generic as path object to be shared in multiple rules */
+
+static void *
+route_aspath_compile (const char *arg)
+{
+  struct aspath *aspath;
+
+  aspath = aspath_str2aspath (arg);
+  if (! aspath)
+    return NULL;
+  return aspath;
+}
+
+static void
+route_aspath_free (void *rule)
+{
+  struct aspath *aspath = rule;
+  aspath_free (aspath);
+}
+
+ /* 'match peer (A.B.C.D|X:X::X:X)' */
+
+/* Compares the peer specified in the 'match peer' clause with the peer
+    received in bgp_info->peer. If it is the same, or if the peer structure
+    received is a peer_group containing it, returns RMAP_MATCH. */
+static route_map_result_t
+route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type,
+      void *object)
+{
+  union sockunion *su;
+  union sockunion su_def = { .sin = { .sin_family = AF_INET,
+                                      .sin_addr.s_addr = INADDR_ANY } };
+  struct peer_group *group;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (type == RMAP_BGP)
+    {
+      su = rule;
+      peer = ((struct bgp_info *) object)->peer;
+
+      if ( ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT) &&
+           ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_EXPORT) )
+        return RMAP_NOMATCH;
+
+      /* If su='0.0.0.0' (command 'match peer local'), and it's a NETWORK,
+          REDISTRIBUTE or DEFAULT_GENERATED route => return RMAP_MATCH */
+      if (sockunion_same (su, &su_def))
+        {
+          int ret;
+          if ( CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_NETWORK) ||
+               CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE) ||
+               CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_DEFAULT))
+            ret = RMAP_MATCH;
+          else
+            ret = RMAP_NOMATCH;
+          return ret;
+        }
+
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+        {
+          if (sockunion_same (su, &peer->su))
+            return RMAP_MATCH;
+
+          return RMAP_NOMATCH;
+        }
+      else
+        {
+          group = peer->group;
+          for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+            {
+              if (sockunion_same (su, &peer->su))
+                return RMAP_MATCH;
+            }
+          return RMAP_NOMATCH;
+        }
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_peer_compile (const char *arg)
+{
+  union sockunion *su;
+  int ret;
+
+  su = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union sockunion));
+
+  ret = str2sockunion (strcmp(arg, "local") ? arg : "0.0.0.0", su);
+  if (ret < 0) {
+    XFREE (MTYPE_ROUTE_MAP_COMPILED, su);
+    return NULL;
+  }
+
+  return su;
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_peer_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_peer_cmd =
+{
+  "peer",
+  route_match_peer,
+  route_match_peer_compile,
+  route_match_peer_free
+};
+
+/* `match ip address IP_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+route_match_ip_address (void *rule, struct prefix *prefix, 
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  /* struct prefix_ipv4 match; */
+
+  if (type == RMAP_BGP)
+    {
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (access_list_apply (alist, prefix) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip address' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_address_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_address_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_ip_address_cmd =
+{
+  "ip address",
+  route_match_ip_address,
+  route_match_ip_address_compile,
+  route_match_ip_address_free
+};
+
+/* `match ip next-hop IP_ADDRESS' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_ip_next_hop (void *rule, struct prefix *prefix, 
+                        route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  struct bgp_info *bgp_info;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      p.family = AF_INET;
+      p.prefix = bgp_info->attr->nexthop;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, &p) == FILTER_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip next-hop' match statement. `arg' is
+   access-list name. */
+static void *
+route_match_ip_next_hop_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_next_hop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip next-hop matching. */
+struct route_map_rule_cmd route_match_ip_next_hop_cmd =
+{
+  "ip next-hop",
+  route_match_ip_next_hop,
+  route_match_ip_next_hop_compile,
+  route_match_ip_next_hop_free
+};
+
+/* `match ip route-source ACCESS-LIST' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_ip_route_source (void *rule, struct prefix *prefix, 
+                            route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  struct bgp_info *bgp_info;
+  struct peer *peer;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      peer = bgp_info->peer;
+
+      if (! peer || sockunion_family (&peer->su) != AF_INET)
+       return RMAP_NOMATCH;
+
+      p.family = AF_INET;
+      p.prefix = peer->su.sin.sin_addr;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, &p) == FILTER_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip route-source' match statement. `arg' is
+   access-list name. */
+static void *
+route_match_ip_route_source_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_route_source_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip route-source matching. */
+struct route_map_rule_cmd route_match_ip_route_source_cmd =
+{
+  "ip route-source",
+  route_match_ip_route_source,
+  route_match_ip_route_source_compile,
+  route_match_ip_route_source_free
+};
+
+/* `match ip address prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_address_prefix_list (void *rule, struct prefix *prefix, 
+                                   route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type == RMAP_BGP)
+    {
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
+{
+  "ip address prefix-list",
+  route_match_ip_address_prefix_list,
+  route_match_ip_address_prefix_list_compile,
+  route_match_ip_address_prefix_list_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+  struct bgp_info *bgp_info;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      p.family = AF_INET;
+      p.prefix = bgp_info->attr->nexthop;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, &p) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_next_hop_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_next_hop_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd =
+{
+  "ip next-hop prefix-list",
+  route_match_ip_next_hop_prefix_list,
+  route_match_ip_next_hop_prefix_list_compile,
+  route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ip route-source prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_route_source_prefix_list (void *rule, struct prefix *prefix,
+                                        route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+  struct bgp_info *bgp_info;
+  struct peer *peer;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      peer = bgp_info->peer;
+
+      if (! peer || sockunion_family (&peer->su) != AF_INET)
+       return RMAP_NOMATCH;
+
+      p.family = AF_INET;
+      p.prefix = peer->su.sin.sin_addr;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, &p) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_route_source_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_route_source_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd =
+{
+  "ip route-source prefix-list",
+  route_match_ip_route_source_prefix_list,
+  route_match_ip_route_source_prefix_list_compile,
+  route_match_ip_route_source_prefix_list_free
+};
+
+/* `match local-preference LOCAL-PREF' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_local_pref (void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  u_int32_t *local_pref;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      local_pref = rule;
+      bgp_info = object;
+
+      if (bgp_info->attr->local_pref == *local_pref)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match local-preference' match statement.
+   `arg' is local-pref value */
+static void *
+route_match_local_pref_compile (const char *arg)
+{
+  u_int32_t *local_pref;
+  char *endptr = NULL;
+  unsigned long tmpval;
+
+  /* Locpref value shoud be integer. */
+  if (! all_digit (arg))
+    return NULL;
+
+  errno = 0;
+  tmpval = strtoul (arg, &endptr, 10);
+  if (*endptr != '\0' || errno || tmpval > UINT32_MAX)
+    return NULL;
+
+  local_pref = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+
+  if (!local_pref)
+    return local_pref;
+
+  *local_pref = tmpval;
+  return local_pref;
+}
+
+/* Free route map's compiled `match local-preference' value. */
+static void
+route_match_local_pref_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+struct route_map_rule_cmd route_match_local_pref_cmd =
+{
+  "local-preference",
+  route_match_local_pref,
+  route_match_local_pref_compile,
+  route_match_local_pref_free
+};
+
+/* `match metric METRIC' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_metric (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  struct rmap_value *rv;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      rv = rule;
+      bgp_info = object;
+      return route_value_match(rv, bgp_info->attr->med);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map commands for metric matching. */
+struct route_map_rule_cmd route_match_metric_cmd =
+{
+  "metric",
+  route_match_metric,
+  route_value_compile,
+  route_value_free,
+};
+
+/* `match as-path ASPATH' */
+
+/* Match function for as-path match.  I assume given object is */
+static route_map_result_t
+route_match_aspath (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  
+  struct as_list *as_list;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      as_list = as_list_lookup ((char *) rule);
+      if (as_list == NULL)
+       return RMAP_NOMATCH;
+    
+      bgp_info = object;
+    
+      /* Perform match. */
+      return ((as_list_apply (as_list, bgp_info->attr->aspath) == AS_FILTER_DENY) ? RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Compile function for as-path match. */
+static void *
+route_match_aspath_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Compile function for as-path match. */
+static void
+route_match_aspath_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for aspath matching. */
+struct route_map_rule_cmd route_match_aspath_cmd = 
+{
+  "as-path",
+  route_match_aspath,
+  route_match_aspath_compile,
+  route_match_aspath_free
+};
+
+/* `match community COMMUNIY' */
+struct rmap_community
+{
+  char *name;
+  int exact;
+};
+
+/* Match function for community match. */
+static route_map_result_t
+route_match_community (void *rule, struct prefix *prefix, 
+                      route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct bgp_info *bgp_info;
+  struct rmap_community *rcom;
+
+  if (type == RMAP_BGP) 
+    {
+      bgp_info = object;
+      rcom = rule;
+
+      list = community_list_lookup (bgp_clist, rcom->name, COMMUNITY_LIST_MASTER);
+      if (! list)
+       return RMAP_NOMATCH;
+
+      if (rcom->exact)
+       {
+         if (community_list_exact_match (bgp_info->attr->community, list))
+           return RMAP_MATCH;
+       }
+      else
+       {
+         if (community_list_match (bgp_info->attr->community, list))
+           return RMAP_MATCH;
+       }
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Compile function for community match. */
+static void *
+route_match_community_compile (const char *arg)
+{
+  struct rmap_community *rcom;
+  int len;
+  char *p;
+
+  rcom = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_community));
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      rcom->name = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (rcom->name, arg, len);
+      rcom->exact = 1;
+    }
+  else
+    {
+      rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+      rcom->exact = 0;
+    }
+  return rcom;
+}
+
+/* Compile function for community match. */
+static void
+route_match_community_free (void *rule)
+{
+  struct rmap_community *rcom = rule;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name); 
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Route map commands for community matching. */
+struct route_map_rule_cmd route_match_community_cmd = 
+{
+  "community",
+  route_match_community,
+  route_match_community_compile,
+  route_match_community_free
+};
+
+/* Match function for lcommunity match. */
+static route_map_result_t
+route_match_lcommunity (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct bgp_info *bgp_info;
+  struct rmap_community *rcom;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      rcom = rule;
+
+      list = community_list_lookup (bgp_clist, rcom->name,
+                                   LARGE_COMMUNITY_LIST_MASTER);
+      if (! list)
+       return RMAP_NOMATCH;
+
+      if (bgp_info->attr->extra &&
+         lcommunity_list_match (bgp_info->attr->extra->lcommunity, list))
+       return RMAP_MATCH;
+
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Compile function for community match. */
+static void *
+route_match_lcommunity_compile (const char *arg)
+{
+  struct rmap_community *rcom;
+  int len;
+  char *p;
+
+  rcom = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_community));
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      rcom->name = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (rcom->name, arg, len);
+    }
+  else
+    {
+      rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+      rcom->exact = 0;
+    }
+  return rcom;
+}
+
+/* Compile function for community match. */
+static void
+route_match_lcommunity_free (void *rule)
+{
+  struct rmap_community *rcom = rule;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Route map commands for community matching. */
+struct route_map_rule_cmd route_match_lcommunity_cmd =
+{
+  "large-community",
+  route_match_lcommunity,
+  route_match_lcommunity_compile,
+  route_match_lcommunity_free
+};
+
+
+/* Match function for extcommunity match. */
+static route_map_result_t
+route_match_ecommunity (void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+
+      if (!bgp_info->attr->extra)
+        return RMAP_NOMATCH;
+
+      list = community_list_lookup (bgp_clist, (char *) rule,
+                                   EXTCOMMUNITY_LIST_MASTER);
+      if (! list)
+       return RMAP_NOMATCH;
+
+      if (ecommunity_list_match (bgp_info->attr->extra->ecommunity, list))
+       return RMAP_MATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Compile function for extcommunity match. */
+static void *
+route_match_ecommunity_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Compile function for extcommunity match. */
+static void
+route_match_ecommunity_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for community matching. */
+struct route_map_rule_cmd route_match_ecommunity_cmd = 
+{
+  "extcommunity",
+  route_match_ecommunity,
+  route_match_ecommunity_compile,
+  route_match_ecommunity_free
+};
+
+/* `match nlri` and `set nlri` are replaced by `address-family ipv4`
+   and `address-family vpnv4'.  */
+
+/* `match origin' */
+static route_map_result_t
+route_match_origin (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  u_char *origin;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      origin = rule;
+      bgp_info = object;
+    
+      if (bgp_info->attr->origin == *origin)
+       return RMAP_MATCH;
+    }
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_origin_compile (const char *arg)
+{
+  u_char *origin;
+
+  origin = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_char));
+
+  if (strcmp (arg, "igp") == 0)
+    *origin = 0;
+  else if (strcmp (arg, "egp") == 0)
+    *origin = 1;
+  else
+    *origin = 2;
+
+  return origin;
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_origin_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for origin matching. */
+struct route_map_rule_cmd route_match_origin_cmd =
+{
+  "origin",
+  route_match_origin,
+  route_match_origin_compile,
+  route_match_origin_free
+};
+
+/* match probability  { */
+
+static route_map_result_t
+route_match_probability (void *rule, struct prefix *prefix,
+                   route_map_object_t type, void *object)
+{
+  long r = random();
+
+  switch (*(long *) rule)
+  {
+    case 0: break;
+    case RAND_MAX: return RMAP_MATCH;
+    default:
+      if (r < *(long *) rule)
+        {
+          return RMAP_MATCH;
+        }
+  }
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_probability_compile (const char *arg)
+{
+  long *lobule;
+  unsigned  perc;
+
+  perc    = atoi (arg);
+  lobule  = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (long));
+
+  switch (perc)
+    {
+      case 0:   *lobule = 0; break;
+      case 100: *lobule = RAND_MAX; break;
+      default:  *lobule = RAND_MAX / 100 * perc;
+    }
+
+  return lobule;
+}
+
+static void
+route_match_probability_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_probability_cmd =
+{
+  "probability",
+  route_match_probability,
+  route_match_probability_compile,
+  route_match_probability_free
+};
+
+/* } */
+
+/* `set ip next-hop IP_ADDRESS' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix,
+                 route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      tag = rule;
+      bgp_info = object;
+
+      if (!bgp_info->attr->extra)
+         return RMAP_NOMATCH;
+
+      return ((bgp_info->attr->extra->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH);
+    }
+
+  return RMAP_NOMATCH;
+}
+
+/* Route map commands for tag matching. */
+static struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+struct rmap_ip_nexthop_set
+{
+  struct in_addr *address;
+  int peer_address;
+};
+
+static route_map_result_t
+route_set_ip_nexthop (void *rule, struct prefix *prefix,
+                     route_map_object_t type, void *object)
+{
+  struct rmap_ip_nexthop_set *rins = rule;
+  struct bgp_info *bgp_info;
+  struct peer *peer;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      peer = bgp_info->peer;
+
+      if (rins->peer_address)
+       {
+         if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) ||
+           CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
+             && peer->su_remote 
+             && sockunion_family (peer->su_remote) == AF_INET)
+           {
+             bgp_info->attr->nexthop.s_addr = sockunion2ip (peer->su_remote);
+             bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+           }
+         else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT)
+                  && peer->su_local
+                  && sockunion_family (peer->su_local) == AF_INET)
+           {
+             bgp_info->attr->nexthop.s_addr = sockunion2ip (peer->su_local);
+             bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+           }
+       }
+      else
+       {
+         /* Set next hop value. */ 
+         bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+         bgp_info->attr->nexthop = *rins->address;
+       }
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function.  Given string is converted
+   to struct in_addr structure. */
+static void *
+route_set_ip_nexthop_compile (const char *arg)
+{
+  struct rmap_ip_nexthop_set *rins;
+  struct in_addr *address = NULL;
+  int peer_address = 0;
+  int ret;
+
+  if (strcmp (arg, "peer-address") == 0)
+    peer_address = 1;
+  else
+    {
+      address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+      ret = inet_aton (arg, address);
+
+      if (ret == 0)
+       {
+         XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+         return NULL;
+       }
+    }
+
+  rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_ip_nexthop_set));
+
+  rins->address = address;
+  rins->peer_address = peer_address;
+
+  return rins;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void
+route_set_ip_nexthop_free (void *rule)
+{
+  struct rmap_ip_nexthop_set *rins = rule;
+
+  if (rins->address)
+    XFREE (MTYPE_ROUTE_MAP_COMPILED, rins->address);
+    
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rins);
+}
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_ip_nexthop_cmd =
+{
+  "ip next-hop",
+  route_set_ip_nexthop,
+  route_set_ip_nexthop_compile,
+  route_set_ip_nexthop_free
+};
+
+/* `set local-preference LOCAL_PREF' */
+
+/* Set local preference. */
+static route_map_result_t
+route_set_local_pref (void *rule, struct prefix *prefix,
+                     route_map_object_t type, void *object)
+{
+  struct rmap_value *rv;
+  struct bgp_info *bgp_info;
+  u_int32_t locpref = 0;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      rv = rule;
+      bgp_info = object;
+    
+      /* Set local preference value. */ 
+      if (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+       locpref = bgp_info->attr->local_pref;
+
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+      bgp_info->attr->local_pref = route_value_adjust(rv, locpref, bgp_info->peer);
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Set local preference rule structure. */
+struct route_map_rule_cmd route_set_local_pref_cmd = 
+{
+  "local-preference",
+  route_set_local_pref,
+  route_value_compile,
+  route_value_free,
+};
+
+/* `set weight WEIGHT' */
+
+/* Set weight. */
+static route_map_result_t
+route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type,
+                 void *object)
+{
+  struct rmap_value *rv;
+  struct bgp_info *bgp_info;
+  u_int32_t weight;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      rv = rule;
+      bgp_info = object;
+    
+      /* Set weight value. */ 
+      weight = route_value_adjust(rv, 0, bgp_info->peer);
+      if (weight)
+        (bgp_attr_extra_get (bgp_info->attr))->weight = weight;
+      else if (bgp_info->attr->extra)
+        bgp_info->attr->extra->weight = 0;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Set local preference rule structure. */
+struct route_map_rule_cmd route_set_weight_cmd = 
+{
+  "weight",
+  route_set_weight,
+  route_value_compile,
+  route_value_free,
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+static route_map_result_t
+route_set_metric (void *rule, struct prefix *prefix, 
+                 route_map_object_t type, void *object)
+{
+  struct rmap_value *rv;
+  struct bgp_info *bgp_info;
+  u_int32_t med = 0;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      rv = rule;
+      bgp_info = object;
+
+      if (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+       med = bgp_info->attr->med;
+
+      bgp_info->attr->med = route_value_adjust(rv, med, bgp_info->peer);
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+    }
+  return RMAP_OKAY;
+}
+
+/* Set metric rule structure. */
+struct route_map_rule_cmd route_set_metric_cmd = 
+{
+  "metric",
+  route_set_metric,
+  route_value_compile,
+  route_value_free,
+};
+
+/* `set as-path prepend ASPATH' */
+
+/* For AS path prepend mechanism. */
+static route_map_result_t
+route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t type, void *object)
+{
+  struct aspath *aspath;
+  struct aspath *new;
+  struct bgp_info *binfo;
+
+  if (type == RMAP_BGP)
+    {
+      binfo = object;
+    
+      if (binfo->attr->aspath->refcnt)
+       new = aspath_dup (binfo->attr->aspath);
+      else
+       new = binfo->attr->aspath;
+
+      if ((uintptr_t)rule > 10)
+      {
+       aspath = rule;
+       aspath_prepend (aspath, new);
+      }
+      else
+      {
+       as_t as = aspath_leftmost(new);
+       if (!as) as = binfo->peer->as;
+       new = aspath_add_seq_n (new, as, (uintptr_t) rule);
+      }
+
+      binfo->attr->aspath = new;
+    }
+
+  return RMAP_OKAY;
+}
+
+static void *
+route_set_aspath_prepend_compile (const char *arg)
+{
+  unsigned int num;
+
+  if (sscanf(arg, "last-as %u", &num) == 1 && num > 0 && num < 10)
+    return (void*)(uintptr_t)num;
+
+  return route_aspath_compile(arg);
+}
+
+static void
+route_set_aspath_prepend_free (void *rule)
+{
+  if ((uintptr_t)rule > 10)
+    route_aspath_free(rule);
+}
+
+
+/* Set as-path prepend rule structure. */
+struct route_map_rule_cmd route_set_aspath_prepend_cmd = 
+{
+  "as-path prepend",
+  route_set_aspath_prepend,
+  route_set_aspath_prepend_compile,
+  route_set_aspath_prepend_free,
+};
+
+/* `set as-path exclude ASn' */
+
+/* For ASN exclude mechanism.
+ * Iterate over ASns requested and filter them from the given AS_PATH one by one.
+ * Make a deep copy of existing AS_PATH, but for the first ASn only.
+ */
+static route_map_result_t
+route_set_aspath_exclude (void *rule, struct prefix *dummy, route_map_object_t type, void *object)
+{
+  struct aspath * new_path, * exclude_path;
+  struct bgp_info *binfo;
+
+  if (type == RMAP_BGP)
+  {
+    exclude_path = rule;
+    binfo = object;
+    if (binfo->attr->aspath->refcnt)
+      new_path = aspath_dup (binfo->attr->aspath);
+    else
+      new_path = binfo->attr->aspath;
+    binfo->attr->aspath = aspath_filter_exclude (new_path, exclude_path);
+  }
+  return RMAP_OKAY;
+}
+
+/* Set ASn exlude rule structure. */
+struct route_map_rule_cmd route_set_aspath_exclude_cmd = 
+{
+  "as-path exclude",
+  route_set_aspath_exclude,
+  route_aspath_compile,
+  route_aspath_free,
+};
+
+/* `set community COMMUNITY' */
+struct rmap_com_set
+{
+  struct community *com;
+  int additive;
+  int none;
+};
+
+/* For community set mechanism. */
+static route_map_result_t
+route_set_community (void *rule, struct prefix *prefix,
+                    route_map_object_t type, void *object)
+{
+  struct rmap_com_set *rcs;
+  struct bgp_info *binfo;
+  struct attr *attr;
+  struct community *new = NULL;
+  struct community *old;
+  struct community *merge;
+  
+  if (type == RMAP_BGP)
+    {
+      rcs = rule;
+      binfo = object;
+      attr = binfo->attr;
+      old = attr->community;
+
+      /* "none" case.  */
+      if (rcs->none)
+       {
+         attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES));
+         attr->community = NULL;
+         /* See the longer comment down below. */
+         if (old && old->refcnt == 0)
+           community_free(old);
+         return RMAP_OKAY;
+       }
+
+      /* "additive" case.  */
+      if (rcs->additive && old)
+       {
+         merge = community_merge (community_dup (old), rcs->com);
+         
+         /* HACK: if the old community is not intern'd, 
+           * we should free it here, or all reference to it may be lost.
+           * Really need to cleanup attribute caching sometime.
+           */
+         if (old->refcnt == 0)
+           community_free (old);
+         new = community_uniq_sort (merge);
+         community_free (merge);
+       }
+      else
+       new = community_dup (rcs->com);
+      
+      /* will be interned by caller if required */
+      attr->community = new;
+
+      attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *
+route_set_community_compile (const char *arg)
+{
+  struct rmap_com_set *rcs;
+  struct community *com = NULL;
+  char *sp;
+  int additive = 0;
+  int none = 0;
+  
+  if (strcmp (arg, "none") == 0)
+    none = 1;
+  else
+    {
+      sp = strstr (arg, "additive");
+
+      if (sp && sp > arg)
+       {
+         /* "additive" keyworkd is included.  */
+         additive = 1;
+         *(sp - 1) = '\0';
+       }
+
+      com = community_str2com (arg);
+
+      if (additive)
+       *(sp - 1) = ' ';
+
+      if (! com)
+       return NULL;
+    }
+  
+  rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set));
+  rcs->com = com;
+  rcs->additive = additive;
+  rcs->none = none;
+  
+  return rcs;
+}
+
+/* Free function for set community. */
+static void
+route_set_community_free (void *rule)
+{
+  struct rmap_com_set *rcs = rule;
+
+  if (rcs->com)
+    community_free (rcs->com);
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_community_cmd = 
+{
+  "community",
+  route_set_community,
+  route_set_community_compile,
+  route_set_community_free,
+};
+
+/* `set community COMMUNITY' */
+struct rmap_lcom_set
+{
+  struct lcommunity *lcom;
+  int additive;
+  int none;
+};
+
+
+/* For lcommunity set mechanism. */
+static route_map_result_t
+route_set_lcommunity (void *rule, struct prefix *prefix,
+                    route_map_object_t type, void *object)
+{
+  struct rmap_lcom_set *rcs;
+  struct bgp_info *binfo;
+  struct attr *attr;
+  struct lcommunity *new = NULL;
+  struct lcommunity *old;
+  struct lcommunity *merge;
+
+  if (type == RMAP_BGP)
+    {
+      rcs = rule;
+      binfo = object;
+      attr = binfo->attr;
+      old = (attr->extra) ? attr->extra->lcommunity : NULL;
+
+      /* "none" case.  */
+      if (rcs->none)
+       {
+         attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
+         if (attr->extra) {
+           attr->extra->lcommunity = NULL;
+         }
+         /* See the longer comment down below. */
+         if (old && old->refcnt == 0)
+           lcommunity_free(&old);
+         return RMAP_OKAY;
+       }
+
+      if (rcs->additive && old)
+       {
+         merge = lcommunity_merge (lcommunity_dup (old), rcs->lcom);
+
+         /* HACK: if the old large-community is not intern'd,
+           * we should free it here, or all reference to it may be lost.
+           * Really need to cleanup attribute caching sometime.
+           */
+         if (old->refcnt == 0)
+           lcommunity_free (&old);
+         new = lcommunity_uniq_sort (merge);
+         lcommunity_free (&merge);
+       }
+      else
+        {
+          new = lcommunity_dup (rcs->lcom);
+        }
+
+      /* will be interned by caller if required */
+      bgp_attr_extra_get (attr)->lcommunity = new;
+      attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *
+route_set_lcommunity_compile (const char *arg)
+{
+  struct rmap_lcom_set *rcs;
+  struct lcommunity *lcom = NULL;
+  char *sp;
+  int additive = 0;
+  int none = 0;
+
+  if (strcmp (arg, "none") == 0)
+    none = 1;
+  else
+    {
+      sp = strstr (arg, "additive");
+
+      if (sp && sp > arg)
+       {
+         /* "additive" keyworkd is included.  */
+         additive = 1;
+         *(sp - 1) = '\0';
+       }
+
+      lcom = lcommunity_str2com (arg);
+
+     if (additive)
+       *(sp - 1) = ' ';
+
+     if (! lcom)
+       return NULL;
+    }
+
+  rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set));
+  rcs->lcom = lcom;
+  rcs->additive = additive;
+  rcs->none = none;
+
+  return rcs;
+}
+
+/* Free function for set lcommunity. */
+static void
+route_set_lcommunity_free (void *rule)
+{
+  struct rmap_lcom_set *rcs = rule;
+
+  if (rcs->lcom) {
+    lcommunity_free (&rcs->lcom);
+  }
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_lcommunity_cmd =
+{
+  "large-community",
+  route_set_lcommunity,
+  route_set_lcommunity_compile,
+  route_set_lcommunity_free,
+};
+
+/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */
+
+/* For large community set mechanism. */
+static route_map_result_t
+route_set_lcommunity_delete (void *rule, struct prefix *prefix,
+                            route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct lcommunity *merge;
+  struct lcommunity *new;
+  struct lcommunity *old;
+  struct bgp_info *binfo;
+
+  if (type == RMAP_BGP)
+    {
+      if (! rule)
+       return RMAP_OKAY;
+
+      binfo = object;
+      list = community_list_lookup (bgp_clist, rule,
+                                   LARGE_COMMUNITY_LIST_MASTER);
+      old = ((binfo->attr->extra) ? binfo->attr->extra->lcommunity : NULL);
+
+      if (list && old)
+       {
+         merge = lcommunity_list_match_delete (lcommunity_dup (old), list);
+         new = lcommunity_uniq_sort (merge);
+         lcommunity_free (&merge);
+
+         /* HACK: if the old community is not intern'd,
+          * we should free it here, or all reference to it may be lost.
+          * Really need to cleanup attribute caching sometime.
+          */
+         if (old->refcnt == 0)
+           lcommunity_free (&old);
+
+         if (new->size == 0)
+           {
+             binfo->attr->extra->lcommunity = NULL;
+             binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+             lcommunity_free (&new);
+           }
+         else
+           {
+             binfo->attr->extra->lcommunity = new;
+             binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+           }
+       }
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set lcommunity. */
+static void *
+route_set_lcommunity_delete_compile (const char *arg)
+{
+  char *p;
+  char *str;
+  int len;
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      str = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (str, arg, len);
+    }
+  else
+    str = NULL;
+
+  return str;
+}
+
+/* Free function for set lcommunity. */
+static void
+route_set_lcommunity_delete_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set lcommunity rule structure. */
+struct route_map_rule_cmd route_set_lcommunity_delete_cmd =
+{
+  "large-comm-list",
+  route_set_lcommunity_delete,
+  route_set_lcommunity_delete_compile,
+  route_set_lcommunity_delete_free,
+};
+
+
+/* `set comm-list (<1-99>|<100-500>|WORD) delete' */
+
+/* For community set mechanism. */
+static route_map_result_t
+route_set_community_delete (void *rule, struct prefix *prefix,
+                           route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct community *merge;
+  struct community *new;
+  struct community *old;
+  struct bgp_info *binfo;
+
+  if (type == RMAP_BGP)
+    {
+      if (! rule)
+       return RMAP_OKAY;
+
+      binfo = object;
+      list = community_list_lookup (bgp_clist, rule, COMMUNITY_LIST_MASTER);
+      old = binfo->attr->community;
+
+      if (list && old)
+       {
+         merge = community_list_match_delete (community_dup (old), list);
+         new = community_uniq_sort (merge);
+         community_free (merge);
+
+         /* HACK: if the old community is not intern'd,
+          * we should free it here, or all reference to it may be lost.
+          * Really need to cleanup attribute caching sometime.
+          */
+         if (old->refcnt == 0)
+           community_free (old);
+
+         if (new->size == 0)
+           {
+             binfo->attr->community = NULL;
+             binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+             community_free (new);
+           }
+         else
+           {
+             binfo->attr->community = new;
+             binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+           }
+       }
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *
+route_set_community_delete_compile (const char *arg)
+{
+  char *p;
+  char *str;
+  int len;
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      str = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (str, arg, len);
+    }
+  else
+    str = NULL;
+
+  return str;
+}
+
+/* Free function for set community. */
+static void
+route_set_community_delete_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_community_delete_cmd =
+{
+  "comm-list",
+  route_set_community_delete,
+  route_set_community_delete_compile,
+  route_set_community_delete_free,
+};
+
+/* `set extcommunity rt COMMUNITY' */
+
+/* For community set mechanism.  Used by _rt and _soo. */
+static route_map_result_t
+route_set_ecommunity (void *rule, struct prefix *prefix,
+                     route_map_object_t type, void *object)
+{
+  struct ecommunity *ecom;
+  struct ecommunity *new_ecom;
+  struct ecommunity *old_ecom;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      ecom = rule;
+      bgp_info = object;
+    
+      if (! ecom)
+       return RMAP_OKAY;
+    
+      /* We assume additive for Extended Community. */
+      old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity;
+
+      if (old_ecom)
+       {
+         new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom);
+
+         /* old_ecom->refcnt = 1 => owned elsewhere, e.g. bgp_update_receive()
+          *         ->refcnt = 0 => set by a previous route-map statement */
+         if (!old_ecom->refcnt)
+           ecommunity_free (&old_ecom);
+       }
+      else
+       new_ecom = ecommunity_dup (ecom);
+
+      /* will be intern()'d or attr_flush()'d by bgp_update_main() */
+      bgp_info->attr->extra->ecommunity = new_ecom;
+
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+    }
+  return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *
+route_set_ecommunity_rt_compile (const char *arg)
+{
+  struct ecommunity *ecom;
+
+  ecom = ecommunity_str2com (arg, ECOMMUNITY_ROUTE_TARGET, 0);
+  if (! ecom)
+    return NULL;
+  return ecommunity_intern (ecom);
+}
+
+/* Free function for set community.  Used by _rt and _soo */
+static void
+route_set_ecommunity_free (void *rule)
+{
+  struct ecommunity *ecom = rule;
+  ecommunity_unintern (&ecom);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_ecommunity_rt_cmd = 
+{
+  "extcommunity rt",
+  route_set_ecommunity,
+  route_set_ecommunity_rt_compile,
+  route_set_ecommunity_free,
+};
+
+/* `set extcommunity soo COMMUNITY' */
+
+/* Compile function for set community. */
+static void *
+route_set_ecommunity_soo_compile (const char *arg)
+{
+  struct ecommunity *ecom;
+
+  ecom = ecommunity_str2com (arg, ECOMMUNITY_SITE_ORIGIN, 0);
+  if (! ecom)
+    return NULL;
+  
+  return ecommunity_intern (ecom);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_ecommunity_soo_cmd = 
+{
+  "extcommunity soo",
+  route_set_ecommunity,
+  route_set_ecommunity_soo_compile,
+  route_set_ecommunity_free,
+};
+
+/* `set origin ORIGIN' */
+
+/* For origin set. */
+static route_map_result_t
+route_set_origin (void *rule, struct prefix *prefix, route_map_object_t type, void *object)
+{
+  u_char *origin;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      origin = rule;
+      bgp_info = object;
+    
+      bgp_info->attr->origin = *origin;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for origin set. */
+static void *
+route_set_origin_compile (const char *arg)
+{
+  u_char *origin;
+
+  origin = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_char));
+
+  if (strcmp (arg, "igp") == 0)
+    *origin = 0;
+  else if (strcmp (arg, "egp") == 0)
+    *origin = 1;
+  else
+    *origin = 2;
+
+  return origin;
+}
+
+/* Compile function for origin set. */
+static void
+route_set_origin_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set origin rule structure. */
+struct route_map_rule_cmd route_set_origin_cmd = 
+{
+  "origin",
+  route_set_origin,
+  route_set_origin_compile,
+  route_set_origin_free,
+};
+
+/* `set atomic-aggregate' */
+
+/* For atomic aggregate set. */
+static route_map_result_t
+route_set_atomic_aggregate (void *rule, struct prefix *prefix,
+                           route_map_object_t type, void *object)
+{
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for atomic aggregate. */
+static void *
+route_set_atomic_aggregate_compile (const char *arg)
+{
+  return (void *)1;
+}
+
+/* Compile function for atomic aggregate. */
+static void
+route_set_atomic_aggregate_free (void *rule)
+{
+  return;
+}
+
+/* Set atomic aggregate rule structure. */
+struct route_map_rule_cmd route_set_atomic_aggregate_cmd = 
+{
+  "atomic-aggregate",
+  route_set_atomic_aggregate,
+  route_set_atomic_aggregate_compile,
+  route_set_atomic_aggregate_free,
+};
+
+/* `set aggregator as AS A.B.C.D' */
+struct aggregator
+{
+  as_t as;
+  struct in_addr address;
+};
+
+static route_map_result_t
+route_set_aggregator_as (void *rule, struct prefix *prefix, 
+                        route_map_object_t type, void *object)
+{
+  struct bgp_info *bgp_info;
+  struct aggregator *aggregator;
+  struct attr_extra *ae;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      aggregator = rule;
+      ae = bgp_attr_extra_get (bgp_info->attr);
+      
+      ae->aggregator_as = aggregator->as;
+      ae->aggregator_addr = aggregator->address;
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
+    }
+
+  return RMAP_OKAY;
+}
+
+static void *
+route_set_aggregator_as_compile (const char *arg)
+{
+  struct aggregator *aggregator;
+  char as[10];
+  char address[20];
+
+  aggregator = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct aggregator));
+  sscanf (arg, "%s %s", as, address);
+
+  aggregator->as = strtoul (as, NULL, 10);
+  inet_aton (address, &aggregator->address);
+
+  return aggregator;
+}
+
+static void
+route_set_aggregator_as_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_set_aggregator_as_cmd = 
+{
+  "aggregator as",
+  route_set_aggregator_as,
+  route_set_aggregator_as_compile,
+  route_set_aggregator_as_free,
+};
+
+/* Set tag to object. object must be pointer to struct bgp_info */
+static route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix,
+               route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct bgp_info *bgp_info;
+  struct attr_extra *ae;
+
+  if (type == RMAP_BGP)
+    {
+      tag = rule;
+      bgp_info = object;
+      ae = bgp_attr_extra_get (bgp_info->attr);
+
+      /* Set tag value */
+      ae->tag=*tag;
+
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+
+/* `match ipv6 address IP_ACCESS_LIST' */
+
+static route_map_result_t
+route_match_ipv6_address (void *rule, struct prefix *prefix, 
+                         route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+
+  if (type == RMAP_BGP)
+    {
+      alist = access_list_lookup (AFI_IP6, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (access_list_apply (alist, prefix) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ipv6_address_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ipv6_address_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_ipv6_address_cmd =
+{
+  "ipv6 address",
+  route_match_ipv6_address,
+  route_match_ipv6_address_compile,
+  route_match_ipv6_address_free
+};
+
+/* `match ipv6 next-hop IP_ADDRESS' */
+
+static route_map_result_t
+route_match_ipv6_next_hop (void *rule, struct prefix *prefix, 
+                          route_map_object_t type, void *object)
+{
+  struct in6_addr *addr = rule;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      
+      if (!bgp_info->attr->extra)
+        return RMAP_NOMATCH;
+      
+      if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, addr))
+       return RMAP_MATCH;
+
+      if (bgp_info->attr->extra->mp_nexthop_len == 32 &&
+         IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, addr))
+       return RMAP_MATCH;
+
+      return RMAP_NOMATCH;
+    }
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ipv6_next_hop_compile (const char *arg)
+{
+  struct in6_addr *address;
+  int ret;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+  if (!ret)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+static void
+route_match_ipv6_next_hop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ipv6_next_hop_cmd =
+{
+  "ipv6 next-hop",
+  route_match_ipv6_next_hop,
+  route_match_ipv6_next_hop_compile,
+  route_match_ipv6_next_hop_free
+};
+
+/* `match ipv6 address prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ipv6_address_prefix_list (void *rule, struct prefix *prefix, 
+                             route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type == RMAP_BGP)
+    {
+      plist = prefix_list_lookup (AFI_IP6, (char *) rule);
+      if (plist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ipv6_address_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ipv6_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd =
+{
+  "ipv6 address prefix-list",
+  route_match_ipv6_address_prefix_list,
+  route_match_ipv6_address_prefix_list_compile,
+  route_match_ipv6_address_prefix_list_free
+};
+
+/* `set ipv6 nexthop global IP_ADDRESS' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix, 
+                              route_map_object_t type, void *object)
+{
+  struct in6_addr *address;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      bgp_info = object;
+    
+      /* Set next hop value. */ 
+      (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = *address;
+    
+      /* Set nexthop length. */
+      if (bgp_info->attr->extra->mp_nexthop_len == 0)
+       bgp_info->attr->extra->mp_nexthop_len = 16;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ip next-hop' compile function.  Given string is converted
+   to struct in_addr structure. */
+static void *
+route_set_ipv6_nexthop_global_compile (const char *arg)
+{
+  int ret;
+  struct in6_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Free route map's compiled `ip next-hop' value. */
+static void
+route_set_ipv6_nexthop_global_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd =
+{
+  "ipv6 next-hop global",
+  route_set_ipv6_nexthop_global,
+  route_set_ipv6_nexthop_global_compile,
+  route_set_ipv6_nexthop_global_free
+};
+
+/* `set ipv6 nexthop local IP_ADDRESS' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix, 
+                             route_map_object_t type, void *object)
+{
+  struct in6_addr *address;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      bgp_info = object;
+    
+      /* Set next hop value. */ 
+      (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = *address;
+    
+      /* Set nexthop length. */
+      if (bgp_info->attr->extra->mp_nexthop_len != 32)
+       bgp_info->attr->extra->mp_nexthop_len = 32;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function.  Given string is converted
+   to struct in_addr structure. */
+static void *
+route_set_ipv6_nexthop_local_compile (const char *arg)
+{
+  int ret;
+  struct in6_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void
+route_set_ipv6_nexthop_local_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd =
+{
+  "ipv6 next-hop local",
+  route_set_ipv6_nexthop_local,
+  route_set_ipv6_nexthop_local_compile,
+  route_set_ipv6_nexthop_local_free
+};
+
+/* `set ipv6 nexthop peer-address' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix,
+                            route_map_object_t type, void *object)
+{
+  struct in6_addr peer_address;
+  struct bgp_info *bgp_info;
+  struct peer *peer;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      bgp_info = object;
+      peer = bgp_info->peer;
+
+      if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) ||
+           CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
+         && peer->su_remote
+         && sockunion_family (peer->su_remote) == AF_INET6)
+       {
+         peer_address = peer->su_remote->sin6.sin6_addr;
+       }
+      else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT)
+              && peer->su_local
+              && sockunion_family (peer->su_local) == AF_INET6)
+       {
+         peer_address = peer->su_local->sin6.sin6_addr;
+       }
+
+      if (IN6_IS_ADDR_LINKLOCAL(&peer_address))
+       {
+         /* Set next hop value. */
+         (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = peer_address;
+
+         /* Set nexthop length. */
+         if (bgp_info->attr->extra->mp_nexthop_len != 32)
+           bgp_info->attr->extra->mp_nexthop_len = 32;
+       }
+      else
+       {
+         /* Set next hop value. */
+         (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = peer_address;
+
+         /* Set nexthop length. */
+         if (bgp_info->attr->extra->mp_nexthop_len == 0)
+           bgp_info->attr->extra->mp_nexthop_len = 16;
+       }
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ip next-hop' compile function.  Given string is converted
+   to struct in_addr structure. */
+static void *
+route_set_ipv6_nexthop_peer_compile (const char *arg)
+{
+  int *rins = NULL;
+
+  rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (int));
+  *rins = 1;
+
+  return rins;
+}
+
+/* Free route map's compiled `ip next-hop' value. */
+static void
+route_set_ipv6_nexthop_peer_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd =
+{
+  "ipv6 next-hop peer-address",
+  route_set_ipv6_nexthop_peer,
+  route_set_ipv6_nexthop_peer_compile,
+  route_set_ipv6_nexthop_peer_free
+};
+
+/* `set vpnv4 nexthop A.B.C.D' */
+
+static route_map_result_t
+route_set_vpnv4_nexthop (void *rule, struct prefix *prefix, 
+                        route_map_object_t type, void *object)
+{
+  struct in_addr *address;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      bgp_info = object;
+    
+      /* Set next hop value. */ 
+      (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global_in = *address;
+      (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_len = 4;
+    }
+
+  return RMAP_OKAY;
+}
+
+static void *
+route_set_vpnv4_nexthop_compile (const char *arg)
+{
+  int ret;
+  struct in_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+
+  ret = inet_aton (arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+static void
+route_set_vpnv4_nexthop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd =
+{
+  "vpnv4 next-hop",
+  route_set_vpnv4_nexthop,
+  route_set_vpnv4_nexthop_compile,
+  route_set_vpnv4_nexthop_free
+};
+
+/* `set originator-id' */
+
+/* For origin set. */
+static route_map_result_t
+route_set_originator_id (void *rule, struct prefix *prefix, route_map_object_t type, void *object)
+{
+  struct in_addr *address;
+  struct bgp_info *bgp_info;
+
+  if (type == RMAP_BGP) 
+    {
+      address = rule;
+      bgp_info = object;
+    
+      bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
+      (bgp_attr_extra_get (bgp_info->attr))->originator_id = *address;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for originator-id set. */
+static void *
+route_set_originator_id_compile (const char *arg)
+{
+  int ret;
+  struct in_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+
+  ret = inet_aton (arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Compile function for originator_id set. */
+static void
+route_set_originator_id_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set originator-id rule structure. */
+struct route_map_rule_cmd route_set_originator_id_cmd = 
+{
+  "originator-id",
+  route_set_originator_id,
+  route_set_originator_id_compile,
+  route_set_originator_id_free,
+};
+
+/* Add bgp route map rule. */
+static int
+bgp_route_match_add (struct vty *vty, struct route_map_index *index,
+                    const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete bgp route map rule. */
+static int
+bgp_route_match_delete (struct vty *vty, struct route_map_index *index,
+                       const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Add bgp route map rule. */
+static int
+bgp_route_set_add (struct vty *vty, struct route_map_index *index,
+                  const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete bgp route map rule. */
+static int
+bgp_route_set_delete (struct vty *vty, struct route_map_index *index,
+                     const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Hook function for updating route_map assignment. */
+static void
+bgp_route_map_update (const char *unused)
+{
+  int i;
+  afi_t afi;
+  safi_t safi;
+  int direct;
+  struct listnode *node, *nnode;
+  struct listnode *mnode, *mnnode;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct bgp_filter *filter;
+  struct bgp_node *bn;
+  struct bgp_static *bgp_static;
+
+  if (bm->bgp == NULL)          /* may be called during cleanup */
+    return;
+
+  /* For neighbor route-map updates. */
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &peer->filter[afi][safi];
+         
+               for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
+                 {
+                   if (filter->map[direct].name)
+                     filter->map[direct].map = 
+                       route_map_lookup_by_name (filter->map[direct].name);
+                   else
+                     filter->map[direct].map = NULL;
+                 }
+
+               if (filter->usmap.name)
+                 filter->usmap.map = route_map_lookup_by_name (filter->usmap.name);
+               else
+                 filter->usmap.map = NULL;
+             }
+       }
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &group->conf->filter[afi][safi];
+         
+               for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
+                 {
+                   if (filter->map[direct].name)
+                     filter->map[direct].map = 
+                       route_map_lookup_by_name (filter->map[direct].name);
+                   else
+                     filter->map[direct].map = NULL;
+                 }
+
+               if (filter->usmap.name)
+                 filter->usmap.map = route_map_lookup_by_name (filter->usmap.name);
+               else
+                 filter->usmap.map = NULL;
+             }
+       }
+    }
+
+  /* For default-originate route-map updates. */
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               if (peer->default_rmap[afi][safi].name)
+                 peer->default_rmap[afi][safi].map =
+                   route_map_lookup_by_name (peer->default_rmap[afi][safi].name);
+               else
+                 peer->default_rmap[afi][safi].map = NULL;
+             }
+       }
+    }
+
+  /* For network route-map updates. */
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (afi = AFI_IP; afi < AFI_MAX; afi++)
+       for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+         for (bn = bgp_table_top (bgp->route[afi][safi]); bn;
+              bn = bgp_route_next (bn))
+           if ((bgp_static = bn->info) != NULL)
+             {
+               if (bgp_static->rmap.name)
+                 bgp_static->rmap.map =
+                        route_map_lookup_by_name (bgp_static->rmap.name);
+               else
+                 bgp_static->rmap.map = NULL;
+             }
+    }
+
+  /* For redistribute route-map updates. */
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+       {
+         if (bgp->rmap[AFI_IP][i].name)
+           bgp->rmap[AFI_IP][i].map =
+             route_map_lookup_by_name (bgp->rmap[AFI_IP][i].name);
+         if (bgp->rmap[AFI_IP6][i].name)
+           bgp->rmap[AFI_IP6][i].map =
+             route_map_lookup_by_name (bgp->rmap[AFI_IP6][i].name);
+       }
+    }
+}
+
+DEFUN (match_peer,
+       match_peer_cmd,
+       "match peer (A.B.C.D|X:X::X:X)",
+       MATCH_STR
+       "Match peer address\n"
+       "IP address of peer\n"
+       "IPv6 address of peer\n")
+{
+  return bgp_route_match_add (vty, vty->index, "peer", argv[0]);
+}
+
+DEFUN (match_peer_local,
+        match_peer_local_cmd,
+        "match peer local",
+        MATCH_STR
+        "Match peer address\n"
+        "Static or Redistributed routes\n")
+{
+  return bgp_route_match_add (vty, vty->index, "peer", "local");
+}
+
+DEFUN (no_match_peer,
+       no_match_peer_cmd,
+       "no match peer",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n")
+{
+ if (argc == 0)
+   return bgp_route_match_delete (vty, vty->index, "peer", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "peer", argv[0]);
+}
+
+ALIAS (no_match_peer,
+       no_match_peer_val_cmd,
+       "no match peer (A.B.C.D|X:X::X:X)",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n"
+       "IP address of peer\n"
+       "IPv6 address of peer\n")
+
+ALIAS (no_match_peer,
+       no_match_peer_local_cmd,
+       "no match peer local",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n"
+       "Static or Redistributed routes\n")
+
+DEFUN (match_ip_address, 
+       match_ip_address_cmd,
+       "match ip address (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip address", argv[0]);
+}
+
+DEFUN (no_match_ip_address, 
+       no_match_ip_address_cmd,
+       "no match ip address",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip address", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip address", argv[0]);
+}
+
+ALIAS (no_match_ip_address, 
+       no_match_ip_address_val_cmd,
+       "no match ip address (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+DEFUN (match_ip_next_hop, 
+       match_ip_next_hop_cmd,
+       "match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop,
+       no_match_ip_next_hop_cmd,
+       "no match ip next-hop",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip next-hop", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop,
+       no_match_ip_next_hop_val_cmd,
+       "no match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+/* match probability { */
+
+DEFUN (match_probability,
+       match_probability_cmd,
+       "match probability <0-100>",
+       MATCH_STR
+       "Match portion of routes defined by percentage value\n"
+       "Percentage of routes\n")
+{
+  return bgp_route_match_add (vty, vty->index, "probability", argv[0]);
+}
+
+DEFUN (no_match_probability,
+       no_match_probability_cmd,
+       "no match probability",
+       NO_STR
+       MATCH_STR
+       "Match portion of routes defined by percentage value\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "probability", argc ? argv[0] : NULL);
+}
+
+ALIAS (no_match_probability,
+       no_match_probability_val_cmd,
+       "no match probability <1-99>",
+       NO_STR
+       MATCH_STR
+       "Match portion of routes defined by percentage value\n"
+       "Percentage of routes\n")
+
+/* } */
+
+DEFUN (match_ip_route_source, 
+       match_ip_route_source_cmd,
+       "match ip route-source (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP standard access-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip route-source", argv[0]);
+}
+
+DEFUN (no_match_ip_route_source,
+       no_match_ip_route_source_cmd,
+       "no match ip route-source",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip route-source", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip route-source", argv[0]);
+}
+
+ALIAS (no_match_ip_route_source,
+       no_match_ip_route_source_val_cmd,
+       "no match ip route-source (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP standard access-list name\n")
+
+DEFUN (match_ip_address_prefix_list, 
+       match_ip_address_prefix_list_cmd,
+       "match ip address prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_cmd,
+       "no match ip address prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip address prefix-list", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_val_cmd,
+       "no match ip address prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_ip_next_hop_prefix_list, 
+       match_ip_next_hop_prefix_list_cmd,
+       "match ip next-hop prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_cmd,
+       "no match ip next-hop prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_val_cmd,
+       "no match ip next-hop prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_ip_route_source_prefix_list, 
+       match_ip_route_source_prefix_list_cmd,
+       "match ip route-source prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ip route-source prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_route_source_prefix_list,
+       no_match_ip_route_source_prefix_list_cmd,
+       "no match ip route-source prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "ip route-source prefix-list", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "ip route-source prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_route_source_prefix_list,
+       no_match_ip_route_source_prefix_list_val_cmd,
+       "no match ip route-source prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match advertising source address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_metric, 
+       match_metric_cmd,
+       "match metric <0-4294967295>",
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+{
+  return bgp_route_match_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_match_metric,
+       no_match_metric_cmd,
+       "no match metric",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "metric", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_match_metric,
+       no_match_metric_val_cmd,
+       "no match metric <0-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+
+DEFUN (match_local_pref,
+       match_local_pref_cmd,
+       "match local-preference <0-4294967295>",
+       MATCH_STR
+       "Match local-preference of route\n"
+       "Metric value\n")
+{
+  return bgp_route_match_add (vty, vty->index, "local-preference", argv[0]);
+}
+
+DEFUN (no_match_local_pref,
+       no_match_local_pref_cmd,
+       "no match local-preference",
+       NO_STR
+       MATCH_STR
+       "Match local preference of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "local-preference", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "local-preference", argv[0]);
+}
+
+ALIAS (no_match_local_pref,
+       no_match_local_pref_val_cmd,
+       "no match local-preference <0-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match local preference of route\n"
+       "Local preference value\n")
+
+DEFUN (match_community, 
+       match_community_cmd,
+       "match community (<1-99>|<100-500>|WORD)",
+       MATCH_STR
+       "Match BGP community list\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "community", argv[0]);
+}
+
+DEFUN (match_community_exact, 
+       match_community_exact_cmd,
+       "match community (<1-99>|<100-500>|WORD) exact-match",
+       MATCH_STR
+       "Match BGP community list\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n"
+       "Do exact matching of communities\n")
+{
+  int ret;
+  char *argstr;
+
+  argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED,
+                   strlen (argv[0]) + strlen ("exact-match") + 2);
+
+  sprintf (argstr, "%s exact-match", argv[0]);
+
+  ret = bgp_route_match_add (vty, vty->index, "community", argstr);
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr);
+
+  return ret;
+}
+
+DEFUN (no_match_community,
+       no_match_community_cmd,
+       "no match community",
+       NO_STR
+       MATCH_STR
+       "Match BGP community list\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "community", NULL);
+}
+
+ALIAS (no_match_community,
+       no_match_community_val_cmd,
+       "no match community (<1-99>|<100-500>|WORD)",
+       NO_STR
+       MATCH_STR
+       "Match BGP community list\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n")
+
+ALIAS (no_match_community,
+       no_match_community_exact_cmd,
+       "no match community (<1-99>|<100-500>|WORD) exact-match",
+       NO_STR
+       MATCH_STR
+       "Match BGP community list\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n"
+       "Do exact matching of communities\n")
+
+DEFUN (match_lcommunity,
+       match_lcommunity_cmd,
+       "match large-community (<1-99>|<100-500>|WORD)",
+       MATCH_STR
+       "Match BGP large community list\n"
+       "Large Community-list number (standard)\n"
+       "Large Community-list number (expanded)\n"
+       "Large Community-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "large-community", argv[0]);
+}
+
+DEFUN (no_match_lcommunity,
+       no_match_lcommunity_cmd,
+       "no match large-community (<1-99>|<100-500>|WORD)",
+       NO_STR
+       MATCH_STR
+       "Match BGP large community list\n"
+       "Large Community-list number (standard)\n"
+       "Large Community-list number (expanded)\n"
+       "Large Community-list name\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "large-community", NULL);
+}
+
+
+DEFUN (match_ecommunity, 
+       match_ecommunity_cmd,
+       "match extcommunity (<1-99>|<100-500>|WORD)",
+       MATCH_STR
+       "Match BGP/VPN extended community list\n"
+       "Extended community-list number (standard)\n"
+       "Extended community-list number (expanded)\n"
+       "Extended community-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "extcommunity", argv[0]);
+}
+
+DEFUN (no_match_ecommunity,
+       no_match_ecommunity_cmd,
+       "no match extcommunity",
+       NO_STR
+       MATCH_STR
+       "Match BGP/VPN extended community list\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "extcommunity", NULL);
+}
+
+ALIAS (no_match_ecommunity,
+       no_match_ecommunity_val_cmd,
+       "no match extcommunity (<1-99>|<100-500>|WORD)",
+       NO_STR
+       MATCH_STR
+       "Match BGP/VPN extended community list\n"
+       "Extended community-list number (standard)\n"
+       "Extended community-list number (expanded)\n"
+       "Extended community-list name\n")
+
+DEFUN (match_aspath,
+       match_aspath_cmd,
+       "match as-path WORD",
+       MATCH_STR
+       "Match BGP AS path list\n"
+       "AS path access-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "as-path", argv[0]);
+}
+
+DEFUN (no_match_aspath,
+       no_match_aspath_cmd,
+       "no match as-path",
+       NO_STR
+       MATCH_STR
+       "Match BGP AS path list\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "as-path", NULL);
+}
+
+ALIAS (no_match_aspath,
+       no_match_aspath_val_cmd,
+       "no match as-path WORD",
+       NO_STR
+       MATCH_STR
+       "Match BGP AS path list\n"
+       "AS path access-list name\n")
+
+DEFUN (match_origin,
+       match_origin_cmd,
+       "match origin (egp|igp|incomplete)",
+       MATCH_STR
+       "BGP origin code\n"
+       "remote EGP\n"
+       "local IGP\n"
+       "unknown heritage\n")
+{
+  if (strncmp (argv[0], "igp", 2) == 0)
+    return bgp_route_match_add (vty, vty->index, "origin", "igp");
+  if (strncmp (argv[0], "egp", 1) == 0)
+    return bgp_route_match_add (vty, vty->index, "origin", "egp");
+  if (strncmp (argv[0], "incomplete", 2) == 0)
+    return bgp_route_match_add (vty, vty->index, "origin", "incomplete");
+
+  return CMD_WARNING;
+}
+
+DEFUN (no_match_origin,
+       no_match_origin_cmd,
+       "no match origin",
+       NO_STR
+       MATCH_STR
+       "BGP origin code\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "origin", NULL);
+}
+
+ALIAS (no_match_origin,
+       no_match_origin_val_cmd,
+       "no match origin (egp|igp|incomplete)",
+       NO_STR
+       MATCH_STR
+       "BGP origin code\n"
+       "remote EGP\n"
+       "local IGP\n"
+       "unknown heritage\n")
+
+DEFUN (match_tag,
+       match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+{
+  return bgp_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return bgp_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+
+DEFUN (set_ip_nexthop,
+       set_ip_nexthop_cmd,
+       "set ip next-hop A.B.C.D",
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "IP address of next hop\n")
+{
+  union sockunion su;
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed Next-hop address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_route_set_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (set_ip_nexthop_peer,
+       set_ip_nexthop_peer_cmd,
+       "set ip next-hop peer-address",
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "Use peer address (for BGP only)\n")
+{
+  return bgp_route_set_add (vty, vty->index, "ip next-hop", "peer-address");
+}
+
+DEFUN_DEPRECATED (no_set_ip_nexthop_peer,
+       no_set_ip_nexthop_peer_cmd,
+       "no set ip next-hop peer-address",
+       NO_STR
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "Use peer address (for BGP only)\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "ip next-hop", NULL);
+}
+
+
+DEFUN (no_set_ip_nexthop,
+       no_set_ip_nexthop_cmd,
+       "no set ip next-hop",
+       NO_STR
+       SET_STR
+       "Next hop address\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "ip next-hop", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_set_ip_nexthop,
+       no_set_ip_nexthop_val_cmd,
+       "no set ip next-hop A.B.C.D",
+       NO_STR
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "IP address of next hop\n")
+
+DEFUN (set_metric,
+       set_metric_cmd,
+       "set metric <0-4294967295>",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+{
+  return bgp_route_set_add (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (set_metric,
+       set_metric_addsub_cmd,
+       "set metric <+/-metric>",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Add or subtract metric\n")
+
+ALIAS (set_metric,
+       set_metric_rtt_cmd,
+       "set metric (rtt|+rtt|-rtt)",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Assign round trip time\n"
+       "Add round trip time\n"
+       "Subtract round trip time\n")
+
+DEFUN (no_set_metric,
+       no_set_metric_cmd,
+       "no set metric",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "metric", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_set_metric,
+       no_set_metric_val_cmd,
+       "no set metric <0-4294967295>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+
+DEFUN (set_local_pref,
+       set_local_pref_cmd,
+       "set local-preference <0-4294967295>",
+       SET_STR
+       "BGP local preference path attribute\n"
+       "Preference value\n")
+{
+  return bgp_route_set_add (vty, vty->index, "local-preference", argv[0]);
+}
+
+DEFUN (no_set_local_pref,
+       no_set_local_pref_cmd,
+       "no set local-preference",
+       NO_STR
+       SET_STR
+       "BGP local preference path attribute\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "local-preference", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "local-preference", argv[0]);
+}
+
+ALIAS (no_set_local_pref,
+       no_set_local_pref_val_cmd,
+       "no set local-preference <0-4294967295>",
+       NO_STR
+       SET_STR
+       "BGP local preference path attribute\n"
+       "Preference value\n")
+
+DEFUN (set_weight,
+       set_weight_cmd,
+       "set weight <0-4294967295>",
+       SET_STR
+       "BGP weight for routing table\n"
+       "Weight value\n")
+{
+  return bgp_route_set_add (vty, vty->index, "weight", argv[0]);
+}
+
+DEFUN (no_set_weight,
+       no_set_weight_cmd,
+       "no set weight",
+       NO_STR
+       SET_STR
+       "BGP weight for routing table\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "weight", NULL);
+  
+  return bgp_route_set_delete (vty, vty->index, "weight", argv[0]);
+}
+
+ALIAS (no_set_weight,
+       no_set_weight_val_cmd,
+       "no set weight <0-4294967295>",
+       NO_STR
+       SET_STR
+       "BGP weight for routing table\n"
+       "Weight value\n")
+
+DEFUN (set_aspath_prepend,
+       set_aspath_prepend_cmd,
+       "set as-path prepend ." CMD_AS_RANGE,
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Prepend to the as-path\n"
+       "AS number\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_add (vty, vty->index, "as-path prepend", str);
+  XFREE (MTYPE_TMP, str);
+
+  return ret;
+}
+
+ALIAS (set_aspath_prepend,
+       set_aspath_prepend_lastas_cmd,
+       "set as-path prepend (last-as) <1-10>",
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Prepend to the as-path\n"
+       "Use the peer's AS-number\n"
+       "Number of times to insert")
+
+DEFUN (no_set_aspath_prepend,
+       no_set_aspath_prepend_cmd,
+       "no set as-path prepend",
+       NO_STR
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Prepend to the as-path\n")
+{
+  int ret;
+  char *str;
+
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "as-path prepend", NULL);
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_delete (vty, vty->index, "as-path prepend", str);
+  XFREE (MTYPE_TMP, str);
+  return ret;
+}
+
+ALIAS (no_set_aspath_prepend,
+       no_set_aspath_prepend_val_cmd,
+       "no set as-path prepend ." CMD_AS_RANGE,
+       NO_STR
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Prepend to the as-path\n"
+       "AS number\n")
+
+DEFUN (set_aspath_exclude,
+       set_aspath_exclude_cmd,
+       "set as-path exclude ." CMD_AS_RANGE,
+       SET_STR
+       "Transform BGP AS-path attribute\n"
+       "Exclude from the as-path\n"
+       "AS number\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_add (vty, vty->index, "as-path exclude", str);
+  XFREE (MTYPE_TMP, str);
+  return ret;
+}
+
+DEFUN (no_set_aspath_exclude,
+       no_set_aspath_exclude_cmd,
+       "no set as-path exclude",
+       NO_STR
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Exclude from the as-path\n")
+{
+  int ret;
+  char *str;
+
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "as-path exclude", NULL);
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_delete (vty, vty->index, "as-path exclude", str);
+  XFREE (MTYPE_TMP, str);
+  return ret;
+}
+
+ALIAS (no_set_aspath_exclude,
+       no_set_aspath_exclude_val_cmd,
+       "no set as-path exclude ." CMD_AS_RANGE,
+       NO_STR
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Exclude from the as-path\n"
+       "AS number\n")
+
+DEFUN (set_community,
+       set_community_cmd,
+       "set community .AA:NN",
+       SET_STR
+       "BGP community attribute\n"
+       "Community number in aa:nn format or local-AS|no-advertise|no-export|internet or additive\n")
+{
+  int i;
+  int first = 0;
+  int additive = 0;
+  struct buffer *b;
+  struct community *com = NULL;
+  char *str;
+  char *argstr;
+  int ret;
+
+  b = buffer_new (1024);
+
+  for (i = 0; i < argc; i++)
+    {
+      if (strncmp (argv[i], "additive", strlen (argv[i])) == 0)
+       {
+         additive = 1;
+         continue;
+       }
+
+      if (first)
+       buffer_putc (b, ' ');
+      else
+       first = 1;
+
+      if (strncmp (argv[i], "internet", strlen (argv[i])) == 0)
+       {
+         buffer_putstr (b, "internet");
+         continue;
+       }
+      if (strncmp (argv[i], "local-AS", strlen (argv[i])) == 0)
+       {
+         buffer_putstr (b, "local-AS");
+         continue;
+       }
+      if (strncmp (argv[i], "no-a", strlen ("no-a")) == 0
+         && strncmp (argv[i], "no-advertise", strlen (argv[i])) == 0)
+       {
+         buffer_putstr (b, "no-advertise");
+         continue;
+       }
+      if (strncmp (argv[i], "no-e", strlen ("no-e"))== 0
+         && strncmp (argv[i], "no-export", strlen (argv[i])) == 0)
+       {
+         buffer_putstr (b, "no-export");
+         continue;
+       }
+      buffer_putstr (b, argv[i]);
+    }
+  buffer_putc (b, '\0');
+
+  /* Fetch result string then compile it to communities attribute.  */
+  str = buffer_getstr (b);
+  buffer_free (b);
+
+  if (str)
+    {
+      com = community_str2com (str);
+      XFREE (MTYPE_TMP, str);
+    }
+
+  /* Can't compile user input into communities attribute.  */
+  if (! com)
+    {
+      vty_out (vty, "%% Malformed communities attribute%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Set communites attribute string.  */
+  str = community_str (com);
+
+  if (additive)
+    {
+      argstr = XCALLOC (MTYPE_TMP, strlen (str) + strlen (" additive") + 1);
+      strcpy (argstr, str);
+      strcpy (argstr + strlen (str), " additive");
+      ret =  bgp_route_set_add (vty, vty->index, "community", argstr);
+      XFREE (MTYPE_TMP, argstr);
+    }
+  else
+    ret =  bgp_route_set_add (vty, vty->index, "community", str);
+
+  community_free (com);
+
+  return ret;
+}
+
+DEFUN (set_community_none,
+       set_community_none_cmd,
+       "set community none",
+       SET_STR
+       "BGP community attribute\n"
+       "No community attribute\n")
+{
+  return bgp_route_set_add (vty, vty->index, "community", "none");
+}
+
+DEFUN (no_set_community,
+       no_set_community_cmd,
+       "no set community",
+       NO_STR
+       SET_STR
+       "BGP community attribute\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "community", NULL);
+}
+
+ALIAS (no_set_community,
+       no_set_community_val_cmd,
+       "no set community .AA:NN",
+       NO_STR
+       SET_STR
+       "BGP community attribute\n"
+       "Community number in aa:nn format or local-AS|no-advertise|no-export|internet or additive\n")
+
+ALIAS (no_set_community,
+       no_set_community_none_cmd,
+       "no set community none",
+       NO_STR
+       SET_STR
+       "BGP community attribute\n"
+       "No community attribute\n")
+
+DEFUN (set_community_delete,
+       set_community_delete_cmd,
+       "set comm-list (<1-99>|<100-500>|WORD) delete",
+       SET_STR
+       "set BGP community list (for deletion)\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n"
+       "Delete matching communities\n")
+{
+  char *str;
+
+  str = XCALLOC (MTYPE_TMP, strlen (argv[0]) + strlen (" delete") + 1);
+  strcpy (str, argv[0]);
+  strcpy (str + strlen (argv[0]), " delete");
+
+  bgp_route_set_add (vty, vty->index, "comm-list", str);
+
+  XFREE (MTYPE_TMP, str);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_set_community_delete,
+       no_set_community_delete_cmd,
+       "no set comm-list",
+       NO_STR
+       SET_STR
+       "set BGP community list (for deletion)\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "comm-list", NULL);
+}
+
+ALIAS (no_set_community_delete,
+       no_set_community_delete_val_cmd,
+       "no set comm-list (<1-99>|<100-500>|WORD) delete",
+       NO_STR
+       SET_STR
+       "set BGP community list (for deletion)\n"
+       "Community-list number (standard)\n"
+       "Community-list number (expanded)\n"
+       "Community-list name\n"
+       "Delete matching communities\n")
+
+
+DEFUN (set_lcommunity,
+       set_lcommunity_cmd,
+       "set large-community .AA:BB:CC",
+       SET_STR
+       "BGP large community attribute\n"
+       "Large Community number in aa:bb:cc format or additive\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_add (vty, vty->index, "large-community", str);
+  XFREE (MTYPE_TMP, str);
+
+  return ret;
+}
+
+DEFUN (set_lcommunity_none,
+       set_lcommunity_none_cmd,
+       "set large-community none",
+       SET_STR
+       "BGP large community attribute\n"
+       "No large community attribute\n")
+{
+  return bgp_route_set_add (vty, vty->index, "large-community", "none");
+}
+
+DEFUN (no_set_lcommunity,
+       no_set_lcommunity_cmd,
+       "no set large-community",
+       NO_STR
+       SET_STR
+       "BGP large community attribute\n"
+       "Large community\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "large-community", NULL);
+}
+
+ALIAS (no_set_lcommunity,
+       no_set_lcommunity_val_cmd,
+       "no set large-community .AA:BB:CC",
+       NO_STR
+       SET_STR
+       "BGP large community attribute\n"
+       "Large community in .AA:BB:CC format or additive\n")
+
+ALIAS (no_set_lcommunity,
+       no_set_lcommunity_none_cmd,
+       "no set large-community none",
+       NO_STR
+       SET_STR
+       "BGP community attribute\n"
+       "No community attribute\n")
+
+DEFUN (set_lcommunity_delete,
+       set_lcommunity_delete_cmd,
+       "set large-comm-list (<1-99>|<100-500>|WORD) delete",
+       SET_STR
+       "set BGP large community list (for deletion)\n"
+       "Large Community-list number (standard)\n"
+       "Large Communitly-list number (expanded)\n"
+       "Large Community-list name\n"
+       "Delete matching large communities\n")
+{
+  char *str;
+
+  str = XCALLOC (MTYPE_TMP, strlen (argv[0]) + strlen (" delete") + 1);
+  strcpy (str, argv[0]);
+  strcpy (str + strlen (argv[0]), " delete");
+
+  bgp_route_set_add (vty, vty->index, "large-comm-list", str);
+
+  XFREE (MTYPE_TMP, str);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_set_lcommunity_delete,
+       no_set_lcommunity_delete_cmd,
+       "no set large-comm-list",
+       NO_STR
+       SET_STR
+       "set BGP large community list (for deletion)\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "large-comm-list", NULL);
+}
+
+ALIAS (no_set_lcommunity_delete,
+       no_set_lcommunity_delete_val_cmd,
+       "no set large-comm-list (<1-99>|<100-500>|WORD) delete",
+       NO_STR
+       SET_STR
+       "set BGP large community list (for deletion)\n"
+       "Large Community-list number (standard)\n"
+       "Large Communitly-list number (expanded)\n"
+       "Large Community-list name\n"
+       "Delete matching large communities\n")
+
+DEFUN (set_ecommunity_rt,
+       set_ecommunity_rt_cmd,
+       "set extcommunity rt .ASN:nn_or_IP-address:nn",
+       SET_STR
+       "BGP extended community attribute\n"
+       "Route Target extended community\n"
+       "VPN extended community\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_add (vty, vty->index, "extcommunity rt", str);
+  XFREE (MTYPE_TMP, str);
+
+  return ret;
+}
+
+DEFUN (no_set_ecommunity_rt,
+       no_set_ecommunity_rt_cmd,
+       "no set extcommunity rt",
+       NO_STR
+       SET_STR
+       "BGP extended community attribute\n"
+       "Route Target extended community\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "extcommunity rt", NULL);
+}
+
+ALIAS (no_set_ecommunity_rt,
+       no_set_ecommunity_rt_val_cmd,
+       "no set extcommunity rt .ASN:nn_or_IP-address:nn",
+       NO_STR
+       SET_STR
+       "BGP extended community attribute\n"
+       "Route Target extended community\n"
+       "VPN extended community\n")
+
+DEFUN (set_ecommunity_soo,
+       set_ecommunity_soo_cmd,
+       "set extcommunity soo .ASN:nn_or_IP-address:nn",
+       SET_STR
+       "BGP extended community attribute\n"
+       "Site-of-Origin extended community\n"
+       "VPN extended community\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 0);
+  ret = bgp_route_set_add (vty, vty->index, "extcommunity soo", str);
+  XFREE (MTYPE_TMP, str);
+  return ret;
+}
+
+DEFUN (no_set_ecommunity_soo,
+       no_set_ecommunity_soo_cmd,
+       "no set extcommunity soo",
+       NO_STR
+       SET_STR
+       "BGP extended community attribute\n"
+       "Site-of-Origin extended community\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "extcommunity soo", NULL);
+}
+
+ALIAS (no_set_ecommunity_soo,
+       no_set_ecommunity_soo_val_cmd,
+       "no set extcommunity soo .ASN:nn_or_IP-address:nn",
+       NO_STR
+       SET_STR
+       "BGP extended community attribute\n"
+       "Site-of-Origin extended community\n"
+       "VPN extended community\n")
+
+DEFUN (set_origin,
+       set_origin_cmd,
+       "set origin (egp|igp|incomplete)",
+       SET_STR
+       "BGP origin code\n"
+       "remote EGP\n"
+       "local IGP\n"
+       "unknown heritage\n")
+{
+  if (strncmp (argv[0], "igp", 2) == 0)
+    return bgp_route_set_add (vty, vty->index, "origin", "igp");
+  if (strncmp (argv[0], "egp", 1) == 0)
+    return bgp_route_set_add (vty, vty->index, "origin", "egp");
+  if (strncmp (argv[0], "incomplete", 2) == 0)
+    return bgp_route_set_add (vty, vty->index, "origin", "incomplete");
+
+  return CMD_WARNING;
+}
+
+DEFUN (no_set_origin,
+       no_set_origin_cmd,
+       "no set origin",
+       NO_STR
+       SET_STR
+       "BGP origin code\n")
+{
+  return bgp_route_set_delete (vty, vty->index, "origin", NULL);
+}
+
+ALIAS (no_set_origin,
+       no_set_origin_val_cmd,
+       "no set origin (egp|igp|incomplete)",
+       NO_STR
+       SET_STR
+       "BGP origin code\n"
+       "remote EGP\n"
+       "local IGP\n"
+       "unknown heritage\n")
+
+DEFUN (set_atomic_aggregate,
+       set_atomic_aggregate_cmd,
+       "set atomic-aggregate",
+       SET_STR
+       "BGP atomic aggregate attribute\n" )
+{
+  return bgp_route_set_add (vty, vty->index, "atomic-aggregate", NULL);
+}
+
+DEFUN (no_set_atomic_aggregate,
+       no_set_atomic_aggregate_cmd,
+       "no set atomic-aggregate",
+       NO_STR
+       SET_STR
+       "BGP atomic aggregate attribute\n" )
+{
+  return bgp_route_set_delete (vty, vty->index, "atomic-aggregate", NULL);
+}
+
+DEFUN (set_aggregator_as,
+       set_aggregator_as_cmd,
+       "set aggregator as " CMD_AS_RANGE " A.B.C.D",
+       SET_STR
+       "BGP aggregator attribute\n"
+       "AS number of aggregator\n"
+       "AS number\n"
+       "IP address of aggregator\n")
+{
+  int ret;
+  as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */
+  struct in_addr address;
+  char *argstr;
+
+  VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+  
+  ret = inet_aton (argv[1], &address);
+  if (ret == 0)
+    {
+      vty_out (vty, "Aggregator IP address is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED,
+                   strlen (argv[0]) + strlen (argv[1]) + 2);
+
+  sprintf (argstr, "%s %s", argv[0], argv[1]);
+
+  ret = bgp_route_set_add (vty, vty->index, "aggregator as", argstr);
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr);
+
+  return ret;
+}
+
+DEFUN (no_set_aggregator_as,
+       no_set_aggregator_as_cmd,
+       "no set aggregator as",
+       NO_STR
+       SET_STR
+       "BGP aggregator attribute\n"
+       "AS number of aggregator\n")
+{
+  int ret;
+  as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */
+  struct in_addr address;
+  char *argstr;
+
+  if (argv == 0)
+    return bgp_route_set_delete (vty, vty->index, "aggregator as", NULL);
+  
+  VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+
+  ret = inet_aton (argv[1], &address);
+  if (ret == 0)
+    {
+      vty_out (vty, "Aggregator IP address is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED,
+                   strlen (argv[0]) + strlen (argv[1]) + 2);
+
+  sprintf (argstr, "%s %s", argv[0], argv[1]);
+
+  ret = bgp_route_set_delete (vty, vty->index, "aggregator as", argstr);
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr);
+
+  return ret;
+}
+
+ALIAS (no_set_aggregator_as,
+       no_set_aggregator_as_val_cmd,
+       "no set aggregator as " CMD_AS_RANGE " A.B.C.D",
+       NO_STR
+       SET_STR
+       "BGP aggregator attribute\n"
+       "AS number of aggregator\n"
+       "AS number\n"
+       "IP address of aggregator\n")
+
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <1-4294967295>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return bgp_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+      bgp_route_set_delete(vty, vty->index, "tag", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+
+DEFUN (match_ipv6_address, 
+       match_ipv6_address_cmd,
+       "match ipv6 address WORD",
+       MATCH_STR
+       IPV6_STR
+       "Match IPv6 address of route\n"
+       "IPv6 access-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ipv6 address", argv[0]);
+}
+
+DEFUN (no_match_ipv6_address, 
+       no_match_ipv6_address_cmd,
+       "no match ipv6 address WORD",
+       NO_STR
+       MATCH_STR
+       IPV6_STR
+       "Match IPv6 address of route\n"
+       "IPv6 access-list name\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "ipv6 address", argv[0]);
+}
+
+DEFUN (match_ipv6_next_hop, 
+       match_ipv6_next_hop_cmd,
+       "match ipv6 next-hop X:X::X:X",
+       MATCH_STR
+       IPV6_STR
+       "Match IPv6 next-hop address of route\n"
+       "IPv6 address of next hop\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ipv6 next-hop", argv[0]);
+}
+
+DEFUN (no_match_ipv6_next_hop,
+       no_match_ipv6_next_hop_cmd,
+       "no match ipv6 next-hop X:X::X:X",
+       NO_STR
+       MATCH_STR
+       IPV6_STR
+       "Match IPv6 next-hop address of route\n"
+       "IPv6 address of next hop\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "ipv6 next-hop", argv[0]);
+}
+
+DEFUN (match_ipv6_address_prefix_list, 
+       match_ipv6_address_prefix_list_cmd,
+       "match ipv6 address prefix-list WORD",
+       MATCH_STR
+       IPV6_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return bgp_route_match_add (vty, vty->index, "ipv6 address prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ipv6_address_prefix_list,
+       no_match_ipv6_address_prefix_list_cmd,
+       "no match ipv6 address prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IPV6_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return bgp_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]);
+}
+
+DEFUN (set_ipv6_nexthop_peer,
+       set_ipv6_nexthop_peer_cmd,
+       "set ipv6 next-hop peer-address",
+       SET_STR
+       IPV6_STR
+       "Next hop address\n"
+       "Use peer address (for BGP only)\n")
+{
+  return bgp_route_set_add (vty, vty->index, "ipv6 next-hop peer-address", NULL);
+}
+
+DEFUN (no_set_ipv6_nexthop_peer,
+       no_set_ipv6_nexthop_peer_cmd,
+       "no set ipv6 next-hop peer-address",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       )
+{
+  return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop", argv[0]);
+}
+
+DEFUN (set_ipv6_nexthop_global,
+       set_ipv6_nexthop_global_cmd,
+       "set ipv6 next-hop global X:X::X:X",
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 global address\n"
+       "IPv6 address of next hop\n")
+{
+  return bgp_route_set_add (vty, vty->index, "ipv6 next-hop global", argv[0]);
+}
+
+DEFUN (no_set_ipv6_nexthop_global,
+       no_set_ipv6_nexthop_global_cmd,
+       "no set ipv6 next-hop global",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 global address\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop global", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop global", argv[0]);
+}
+
+ALIAS (no_set_ipv6_nexthop_global,
+       no_set_ipv6_nexthop_global_val_cmd,
+       "no set ipv6 next-hop global X:X::X:X",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 global address\n"
+       "IPv6 address of next hop\n")
+
+DEFUN (set_ipv6_nexthop_local,
+       set_ipv6_nexthop_local_cmd,
+       "set ipv6 next-hop local X:X::X:X",
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+{
+  return bgp_route_set_add (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+DEFUN (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_cmd,
+       "no set ipv6 next-hop local",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL);
+  
+  return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+ALIAS (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_val_cmd,
+       "no set ipv6 next-hop local X:X::X:X",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+
+DEFUN (set_vpnv4_nexthop,
+       set_vpnv4_nexthop_cmd,
+       "set vpnv4 next-hop A.B.C.D",
+       SET_STR
+       "VPNv4 information\n"
+       "VPNv4 next-hop address\n"
+       "IP address of next hop\n")
+{
+  return bgp_route_set_add (vty, vty->index, "vpnv4 next-hop", argv[0]);
+}
+
+DEFUN (no_set_vpnv4_nexthop,
+       no_set_vpnv4_nexthop_cmd,
+       "no set vpnv4 next-hop",
+       NO_STR
+       SET_STR
+       "VPNv4 information\n"
+       "VPNv4 next-hop address\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "vpnv4 next-hop", NULL);
+
+  return bgp_route_set_delete (vty, vty->index, "vpnv4 next-hop", argv[0]);
+}
+
+ALIAS (no_set_vpnv4_nexthop,
+       no_set_vpnv4_nexthop_val_cmd,
+       "no set vpnv4 next-hop A.B.C.D",
+       NO_STR
+       SET_STR
+       "VPNv4 information\n"
+       "VPNv4 next-hop address\n"
+       "IP address of next hop\n")
+
+DEFUN (set_originator_id,
+       set_originator_id_cmd,
+       "set originator-id A.B.C.D",
+       SET_STR
+       "BGP originator ID attribute\n"
+       "IP address of originator\n")
+{
+  return bgp_route_set_add (vty, vty->index, "originator-id", argv[0]);
+}
+
+DEFUN (no_set_originator_id,
+       no_set_originator_id_cmd,
+       "no set originator-id",
+       NO_STR
+       SET_STR
+       "BGP originator ID attribute\n")
+{
+  if (argc == 0)
+    return bgp_route_set_delete (vty, vty->index, "originator-id", NULL);
+  
+  return bgp_route_set_delete (vty, vty->index, "originator-id", argv[0]);
+}
+
+ALIAS (no_set_originator_id,
+       no_set_originator_id_val_cmd,
+       "no set originator-id A.B.C.D",
+       NO_STR
+       SET_STR
+       "BGP originator ID attribute\n"
+       "IP address of originator\n")
+
+DEFUN_DEPRECATED (set_pathlimit_ttl,
+       set_pathlimit_ttl_cmd,
+       "set pathlimit ttl <1-255>",
+       SET_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Set AS-Path Hop-count TTL\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (no_set_pathlimit_ttl,
+       no_set_pathlimit_ttl_cmd,
+       "no set pathlimit ttl",
+       NO_STR
+       SET_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Set AS-Path Hop-count TTL\n")
+{
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_set_pathlimit_ttl,
+       no_set_pathlimit_ttl_val_cmd,
+       "no set pathlimit ttl <1-255>",
+       NO_STR
+       MATCH_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Set AS-Path Hop-count TTL\n")
+
+DEFUN_DEPRECATED (match_pathlimit_as,
+       match_pathlimit_as_cmd,
+       "match pathlimit as <1-65535>",
+       MATCH_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Match Pathlimit AS number\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (no_match_pathlimit_as,
+       no_match_pathlimit_as_cmd,
+       "no match pathlimit as",
+       NO_STR
+       MATCH_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Match Pathlimit AS number\n")
+{
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_match_pathlimit_as,
+       no_match_pathlimit_as_val_cmd,
+       "no match pathlimit as <1-65535>",
+       NO_STR
+       MATCH_STR
+       "BGP AS-Pathlimit attribute\n"
+       "Match Pathlimit ASN\n")
+
+
+/* Initialization of route map. */
+void
+bgp_route_map_init (void)
+{
+  route_map_init ();
+  route_map_init_vty ();
+  route_map_add_hook (bgp_route_map_update);
+  route_map_delete_hook (bgp_route_map_update);
+
+  route_map_install_match (&route_match_peer_cmd);
+  route_map_install_match (&route_match_local_pref_cmd);
+  route_map_install_match (&route_match_ip_address_cmd);
+  route_map_install_match (&route_match_ip_next_hop_cmd);
+  route_map_install_match (&route_match_ip_route_source_cmd);
+  route_map_install_match (&route_match_ip_address_prefix_list_cmd);
+  route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd);
+  route_map_install_match (&route_match_ip_route_source_prefix_list_cmd);
+  route_map_install_match (&route_match_aspath_cmd);
+  route_map_install_match (&route_match_community_cmd);
+  route_map_install_match (&route_match_lcommunity_cmd);
+  route_map_install_match (&route_match_ecommunity_cmd);
+  route_map_install_match (&route_match_local_pref_cmd);
+  route_map_install_match (&route_match_metric_cmd);
+  route_map_install_match (&route_match_origin_cmd);
+  route_map_install_match (&route_match_probability_cmd);
+  route_map_install_match (&route_match_tag_cmd);
+
+  route_map_install_set (&route_set_ip_nexthop_cmd);
+  route_map_install_set (&route_set_local_pref_cmd);
+  route_map_install_set (&route_set_weight_cmd);
+  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_set (&route_set_aspath_prepend_cmd);
+  route_map_install_set (&route_set_aspath_exclude_cmd);
+  route_map_install_set (&route_set_origin_cmd);
+  route_map_install_set (&route_set_atomic_aggregate_cmd);
+  route_map_install_set (&route_set_aggregator_as_cmd);
+  route_map_install_set (&route_set_community_cmd);
+  route_map_install_set (&route_set_community_delete_cmd);
+  route_map_install_set (&route_set_lcommunity_cmd);
+  route_map_install_set (&route_set_lcommunity_delete_cmd);
+  route_map_install_set (&route_set_vpnv4_nexthop_cmd);
+  route_map_install_set (&route_set_originator_id_cmd);
+  route_map_install_set (&route_set_ecommunity_rt_cmd);
+  route_map_install_set (&route_set_ecommunity_soo_cmd);
+  route_map_install_set (&route_set_tag_cmd);
+
+  install_element (RMAP_NODE, &match_peer_cmd);
+  install_element (RMAP_NODE, &match_peer_local_cmd);
+  install_element (RMAP_NODE, &no_match_peer_cmd);
+  install_element (RMAP_NODE, &no_match_peer_val_cmd);
+  install_element (RMAP_NODE, &no_match_peer_local_cmd);
+  install_element (RMAP_NODE, &match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_val_cmd);
+  install_element (RMAP_NODE, &match_ip_next_hop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd);
+  install_element (RMAP_NODE, &match_ip_route_source_cmd);
+  install_element (RMAP_NODE, &no_match_ip_route_source_cmd);
+  install_element (RMAP_NODE, &no_match_ip_route_source_val_cmd);
+  install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_ip_route_source_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_route_source_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_route_source_prefix_list_val_cmd);
+
+  install_element (RMAP_NODE, &match_aspath_cmd);
+  install_element (RMAP_NODE, &no_match_aspath_cmd);
+  install_element (RMAP_NODE, &no_match_aspath_val_cmd);
+  install_element (RMAP_NODE, &match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_val_cmd);
+  install_element (RMAP_NODE, &match_local_pref_cmd);
+  install_element (RMAP_NODE, &no_match_local_pref_cmd);
+  install_element (RMAP_NODE, &no_match_local_pref_val_cmd);
+  install_element (RMAP_NODE, &match_community_cmd);
+  install_element (RMAP_NODE, &match_community_exact_cmd);
+  install_element (RMAP_NODE, &no_match_community_cmd);
+  install_element (RMAP_NODE, &no_match_community_val_cmd);
+  install_element (RMAP_NODE, &no_match_community_exact_cmd);
+  install_element (RMAP_NODE, &match_lcommunity_cmd);
+  install_element (RMAP_NODE, &no_match_lcommunity_cmd);
+  install_element (RMAP_NODE, &match_ecommunity_cmd);
+  install_element (RMAP_NODE, &no_match_ecommunity_cmd);
+  install_element (RMAP_NODE, &no_match_ecommunity_val_cmd);
+  install_element (RMAP_NODE, &match_origin_cmd);
+  install_element (RMAP_NODE, &no_match_origin_cmd);
+  install_element (RMAP_NODE, &no_match_origin_val_cmd);
+  install_element (RMAP_NODE, &match_probability_cmd);
+  install_element (RMAP_NODE, &no_match_probability_cmd);
+  install_element (RMAP_NODE, &no_match_probability_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
+
+  install_element (RMAP_NODE, &set_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &set_ip_nexthop_peer_cmd);
+  install_element (RMAP_NODE, &no_set_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &no_set_ip_nexthop_val_cmd);
+  install_element (RMAP_NODE, &set_local_pref_cmd);
+  install_element (RMAP_NODE, &no_set_local_pref_cmd);
+  install_element (RMAP_NODE, &no_set_local_pref_val_cmd);
+  install_element (RMAP_NODE, &set_weight_cmd);
+  install_element (RMAP_NODE, &no_set_weight_cmd);
+  install_element (RMAP_NODE, &no_set_weight_val_cmd);
+  install_element (RMAP_NODE, &set_metric_cmd);
+  install_element (RMAP_NODE, &set_metric_addsub_cmd);
+  install_element (RMAP_NODE, &set_metric_rtt_cmd);
+  install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+  install_element (RMAP_NODE, &set_aspath_prepend_cmd);
+  install_element (RMAP_NODE, &set_aspath_prepend_lastas_cmd);
+  install_element (RMAP_NODE, &set_aspath_exclude_cmd);
+  install_element (RMAP_NODE, &no_set_aspath_prepend_cmd);
+  install_element (RMAP_NODE, &no_set_aspath_prepend_val_cmd);
+  install_element (RMAP_NODE, &no_set_aspath_exclude_cmd);
+  install_element (RMAP_NODE, &no_set_aspath_exclude_val_cmd);
+  install_element (RMAP_NODE, &set_origin_cmd);
+  install_element (RMAP_NODE, &no_set_origin_cmd);
+  install_element (RMAP_NODE, &no_set_origin_val_cmd);
+  install_element (RMAP_NODE, &set_atomic_aggregate_cmd);
+  install_element (RMAP_NODE, &no_set_atomic_aggregate_cmd);
+  install_element (RMAP_NODE, &set_aggregator_as_cmd);
+  install_element (RMAP_NODE, &no_set_aggregator_as_cmd);
+  install_element (RMAP_NODE, &no_set_aggregator_as_val_cmd);
+  install_element (RMAP_NODE, &set_community_cmd);
+  install_element (RMAP_NODE, &set_community_none_cmd);
+  install_element (RMAP_NODE, &no_set_community_cmd);
+  install_element (RMAP_NODE, &no_set_community_val_cmd);
+  install_element (RMAP_NODE, &no_set_community_none_cmd);
+  install_element (RMAP_NODE, &set_community_delete_cmd);
+  install_element (RMAP_NODE, &no_set_community_delete_cmd);
+  install_element (RMAP_NODE, &no_set_community_delete_val_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_none_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_val_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_none_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_delete_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_delete_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_delete_val_cmd);
+  install_element (RMAP_NODE, &set_ecommunity_rt_cmd);
+  install_element (RMAP_NODE, &no_set_ecommunity_rt_cmd);
+  install_element (RMAP_NODE, &no_set_ecommunity_rt_val_cmd);
+  install_element (RMAP_NODE, &set_ecommunity_soo_cmd);
+  install_element (RMAP_NODE, &no_set_ecommunity_soo_cmd);
+  install_element (RMAP_NODE, &no_set_ecommunity_soo_val_cmd);
+  install_element (RMAP_NODE, &set_vpnv4_nexthop_cmd);
+  install_element (RMAP_NODE, &no_set_vpnv4_nexthop_cmd);
+  install_element (RMAP_NODE, &no_set_vpnv4_nexthop_val_cmd);
+  install_element (RMAP_NODE, &set_originator_id_cmd);
+  install_element (RMAP_NODE, &no_set_originator_id_cmd);
+  install_element (RMAP_NODE, &no_set_originator_id_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
+
+  route_map_install_match (&route_match_ipv6_address_cmd);
+  route_map_install_match (&route_match_ipv6_next_hop_cmd);
+  route_map_install_match (&route_match_ipv6_address_prefix_list_cmd);
+  route_map_install_set (&route_set_ipv6_nexthop_global_cmd);
+  route_map_install_set (&route_set_ipv6_nexthop_local_cmd);
+  route_map_install_set (&route_set_ipv6_nexthop_peer_cmd);
+
+  install_element (RMAP_NODE, &match_ipv6_address_cmd);
+  install_element (RMAP_NODE, &no_match_ipv6_address_cmd);
+  install_element (RMAP_NODE, &match_ipv6_next_hop_cmd);
+  install_element (RMAP_NODE, &no_match_ipv6_next_hop_cmd);
+  install_element (RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &set_ipv6_nexthop_global_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_global_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_global_val_cmd);
+  install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd);
+  install_element (RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
+
+  /* AS-Pathlimit: functionality removed, commands kept for
+   * compatibility.
+   */
+  install_element (RMAP_NODE, &set_pathlimit_ttl_cmd);
+  install_element (RMAP_NODE, &no_set_pathlimit_ttl_cmd);
+  install_element (RMAP_NODE, &no_set_pathlimit_ttl_val_cmd);
+  install_element (RMAP_NODE, &match_pathlimit_as_cmd);
+  install_element (RMAP_NODE, &no_match_pathlimit_as_cmd);
+  install_element (RMAP_NODE, &no_match_pathlimit_as_val_cmd);
+}
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
new file mode 100644 (file)
index 0000000..c4490bf
--- /dev/null
@@ -0,0 +1,887 @@
+/* BGP4 SNMP support
+   Copyright (C) 1999, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "thread.h"
+#include "smux.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_snmp.h"
+
+/* BGP4-MIB described in RFC1657. */
+#define BGP4MIB 1,3,6,1,2,1,15
+
+/* BGP TRAP. */
+#define BGPESTABLISHED                 1
+#define BGPBACKWARDTRANSITION          2       
+
+/* BGP MIB bgpVersion. */
+#define BGPVERSION                           0
+
+/* BGP MIB bgpLocalAs. */
+#define BGPLOCALAS                           0
+
+/* BGP MIB bgpPeerTable. */
+#define BGPPEERIDENTIFIER                     1
+#define BGPPEERSTATE                          2
+#define BGPPEERADMINSTATUS                    3
+#define BGPPEERNEGOTIATEDVERSION              4
+#define BGPPEERLOCALADDR                      5
+#define BGPPEERLOCALPORT                      6
+#define BGPPEERREMOTEADDR                     7
+#define BGPPEERREMOTEPORT                     8
+#define BGPPEERREMOTEAS                       9
+#define BGPPEERINUPDATES                     10
+#define BGPPEEROUTUPDATES                    11
+#define BGPPEERINTOTALMESSAGES               12
+#define BGPPEEROUTTOTALMESSAGES              13
+#define BGPPEERLASTERROR                     14
+#define BGPPEERFSMESTABLISHEDTRANSITIONS     15
+#define BGPPEERFSMESTABLISHEDTIME            16
+#define BGPPEERCONNECTRETRYINTERVAL          17
+#define BGPPEERHOLDTIME                      18
+#define BGPPEERKEEPALIVE                     19
+#define BGPPEERHOLDTIMECONFIGURED            20
+#define BGPPEERKEEPALIVECONFIGURED           21
+#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22
+#define BGPPEERINUPDATEELAPSEDTIME           23
+
+/* BGP MIB bgpIdentifier. */
+#define BGPIDENTIFIER                         0
+
+/* BGP MIB bgpRcvdPathAttrTable */
+#define BGPPATHATTRPEER                       1
+#define BGPPATHATTRDESTNETWORK                2
+#define BGPPATHATTRORIGIN                     3
+#define BGPPATHATTRASPATH                     4
+#define BGPPATHATTRNEXTHOP                    5
+#define BGPPATHATTRINTERASMETRIC              6
+
+/* BGP MIB bgp4PathAttrTable. */
+#define BGP4PATHATTRPEER                      1
+#define BGP4PATHATTRIPADDRPREFIXLEN           2
+#define BGP4PATHATTRIPADDRPREFIX              3
+#define BGP4PATHATTRORIGIN                    4
+#define BGP4PATHATTRASPATHSEGMENT             5
+#define BGP4PATHATTRNEXTHOP                   6
+#define BGP4PATHATTRMULTIEXITDISC             7
+#define BGP4PATHATTRLOCALPREF                 8
+#define BGP4PATHATTRATOMICAGGREGATE           9
+#define BGP4PATHATTRAGGREGATORAS             10
+#define BGP4PATHATTRAGGREGATORADDR           11
+#define BGP4PATHATTRCALCLOCALPREF            12
+#define BGP4PATHATTRBEST                     13
+#define BGP4PATHATTRUNKNOWN                  14
+
+/* SNMP value hack. */
+#define INTEGER ASN_INTEGER
+#define INTEGER32 ASN_INTEGER
+#define COUNTER32 ASN_COUNTER
+#define OCTET_STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+#define GAUGE32 ASN_UNSIGNED
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* BGP-MIB instances. */
+oid bgp_oid [] = { BGP4MIB };
+oid bgp_trap_oid [] = { BGP4MIB, 0 };
+
+/* IP address 0.0.0.0. */
+static struct in_addr bgp_empty_addr = { .s_addr = 0 };
+
+/* Hook functions. */
+static u_char *bgpVersion (struct variable *, oid [], size_t *, int,
+                          size_t *, WriteMethod **);
+static u_char *bgpLocalAs (struct variable *, oid [], size_t *,
+                          int, size_t *, WriteMethod **);
+static u_char *bgpPeerTable (struct variable *, oid [], size_t *,
+                            int, size_t *, WriteMethod **);
+static u_char *bgpRcvdPathAttrTable (struct variable *, oid [], size_t *,
+                                    int, size_t *, WriteMethod **);
+static u_char *bgpIdentifier (struct variable *, oid [], size_t *,
+                             int, size_t *, WriteMethod **);
+static u_char *bgp4PathAttrTable (struct variable *, oid [], size_t *,
+                                 int, size_t *, WriteMethod **);
+/* static u_char *bgpTraps (); */
+
+struct variable bgp_variables[] = 
+{
+  /* BGP version. */
+  {BGPVERSION,                OCTET_STRING, RONLY, bgpVersion,
+   1, {1}},
+  /* BGP local AS. */
+  {BGPLOCALAS,                INTEGER, RONLY, bgpLocalAs,
+   1, {2}},
+  /* BGP peer table. */
+  {BGPPEERIDENTIFIER,         IPADDRESS, RONLY, bgpPeerTable,
+   3, {3, 1, 1}},
+  {BGPPEERSTATE,              INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 2}},
+  {BGPPEERADMINSTATUS,        INTEGER, RWRITE, bgpPeerTable,
+   3, {3, 1, 3}},
+  {BGPPEERNEGOTIATEDVERSION,  INTEGER32, RONLY, bgpPeerTable,
+   3, {3, 1, 4}},
+  {BGPPEERLOCALADDR,          IPADDRESS, RONLY, bgpPeerTable,
+   3, {3, 1, 5}},
+  {BGPPEERLOCALPORT,          INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 6}},
+  {BGPPEERREMOTEADDR,         IPADDRESS, RONLY, bgpPeerTable,
+   3, {3, 1, 7}},
+  {BGPPEERREMOTEPORT,         INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 8}},
+  {BGPPEERREMOTEAS,           INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 9}},
+  {BGPPEERINUPDATES,          COUNTER32, RONLY, bgpPeerTable,
+   3, {3, 1, 10}},
+  {BGPPEEROUTUPDATES,         COUNTER32, RONLY, bgpPeerTable,
+   3, {3, 1, 11}},
+  {BGPPEERINTOTALMESSAGES,    COUNTER32, RONLY, bgpPeerTable,
+   3, {3, 1, 12}},
+  {BGPPEEROUTTOTALMESSAGES,   COUNTER32, RONLY, bgpPeerTable,
+   3, {3, 1, 13}},
+  {BGPPEERLASTERROR,          OCTET_STRING, RONLY, bgpPeerTable,
+   3, {3, 1, 14}},
+  {BGPPEERFSMESTABLISHEDTRANSITIONS, COUNTER32, RONLY, bgpPeerTable,
+   3, {3, 1, 15}},
+  {BGPPEERFSMESTABLISHEDTIME, GAUGE32, RONLY, bgpPeerTable,
+   3, {3, 1, 16}},
+  {BGPPEERCONNECTRETRYINTERVAL, INTEGER, RWRITE, bgpPeerTable,
+   3, {3, 1, 17}},
+  {BGPPEERHOLDTIME,           INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 18}},
+  {BGPPEERKEEPALIVE,          INTEGER, RONLY, bgpPeerTable,
+   3, {3, 1, 19}},
+  {BGPPEERHOLDTIMECONFIGURED, INTEGER, RWRITE, bgpPeerTable,
+   3, {3, 1, 20}},
+  {BGPPEERKEEPALIVECONFIGURED, INTEGER, RWRITE, bgpPeerTable,
+   3, {3, 1, 21}},
+  {BGPPEERMINROUTEADVERTISEMENTINTERVAL, INTEGER, RWRITE, bgpPeerTable,
+   3, {3, 1, 23}},
+  {BGPPEERINUPDATEELAPSEDTIME, GAUGE32, RONLY, bgpPeerTable,
+   3, {3, 1, 24}},
+  /* BGP identifier. */
+  {BGPIDENTIFIER,             IPADDRESS, RONLY, bgpIdentifier,
+   1, {4}},
+  /* BGP received path attribute table. */
+  {BGPPATHATTRPEER,           IPADDRESS, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 1}},
+  {BGPPATHATTRDESTNETWORK,    IPADDRESS, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 2}},
+  {BGPPATHATTRORIGIN,         INTEGER, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 3}},
+  {BGPPATHATTRASPATH,         OCTET_STRING, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 4}},
+  {BGPPATHATTRNEXTHOP,        IPADDRESS, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 5}},
+  {BGPPATHATTRINTERASMETRIC,  INTEGER32, RONLY, bgpRcvdPathAttrTable,
+   3, {5, 1, 6}},
+  /* BGP-4 received path attribute table. */
+  {BGP4PATHATTRPEER, IPADDRESS, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 1}},
+  {BGP4PATHATTRIPADDRPREFIXLEN, INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 2}},
+  {BGP4PATHATTRIPADDRPREFIX,  IPADDRESS, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 3}},
+  {BGP4PATHATTRORIGIN,        INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 4}},
+  {BGP4PATHATTRASPATHSEGMENT, OCTET_STRING, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 5}},
+  {BGP4PATHATTRNEXTHOP,       IPADDRESS, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 6}},
+  {BGP4PATHATTRMULTIEXITDISC, INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 7}},
+  {BGP4PATHATTRLOCALPREF,     INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 8}},
+  {BGP4PATHATTRATOMICAGGREGATE, INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 9}},
+  {BGP4PATHATTRAGGREGATORAS,  INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 10}},
+  {BGP4PATHATTRAGGREGATORADDR, IPADDRESS, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 11}},
+  {BGP4PATHATTRCALCLOCALPREF, INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 12}},
+  {BGP4PATHATTRBEST,          INTEGER, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 13}},
+  {BGP4PATHATTRUNKNOWN,       OCTET_STRING, RONLY, bgp4PathAttrTable,
+   3, {6, 1, 14}},
+};
+
+
+static u_char *
+bgpVersion (struct variable *v, oid name[], size_t *length, int exact,
+           size_t *var_len, WriteMethod **write_method)
+{
+  static u_char version;
+
+  if (smux_header_generic(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Retrun BGP version.  Zebra bgpd only support version 4. */
+  version = (0x80 >> (BGP_VERSION_4 - 1));
+
+  /* Return octet string length 1. */
+  *var_len = 1;
+  return (u_char *)&version;
+}
+
+static u_char *
+bgpLocalAs (struct variable *v, oid name[], size_t *length,
+           int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct bgp *bgp;
+
+  if (smux_header_generic(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Get BGP structure. */
+  bgp = bgp_get_default ();
+  if (! bgp)
+    return NULL;
+
+  return SNMP_INTEGER (bgp->as);
+}
+
+static struct peer *
+peer_lookup_addr_ipv4 (struct in_addr *src)
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  struct listnode *node;
+  struct in_addr addr;
+  int ret;
+
+  bgp = bgp_get_default ();
+  if (! bgp)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+    {
+      ret = inet_pton (AF_INET, peer->host, &addr);
+      if (ret > 0)
+       {
+         if (IPV4_ADDR_SAME (&addr, src))
+           return peer;
+       }
+    }
+  return NULL;
+}
+
+static struct peer *
+bgp_peer_lookup_next (struct in_addr *src)
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  struct listnode *node;
+  struct in_addr *p;
+  union sockunion su;
+  int ret;
+
+  memset (&su, 0, sizeof (union sockunion));
+
+  bgp = bgp_get_default ();
+  if (! bgp)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+    {
+      ret = inet_pton (AF_INET, peer->host, &su.sin.sin_addr);
+      if (ret > 0)
+       {
+         p = &su.sin.sin_addr;
+
+         if (ntohl (p->s_addr) > ntohl (src->s_addr))
+           {
+             src->s_addr = p->s_addr;
+             return peer;
+           }
+       }
+    }
+  return NULL;
+}
+
+/* 1.3.6.1.2.1.15.3.1.x  = 10 */
+#define PEERTAB_NAMELEN 10
+
+static struct peer *
+bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length, 
+                    struct in_addr *addr, int exact)
+{
+  struct peer *peer = NULL;
+  size_t namelen = v ? v->namelen : PEERTAB_NAMELEN;
+  int len;
+
+  if (exact)
+    {
+      /* Check the length. */
+      if (*length - namelen != sizeof (struct in_addr))
+       return NULL;
+
+      oid2in_addr (name + namelen, IN_ADDR_SIZE, addr);
+
+      peer = peer_lookup_addr_ipv4 (addr);
+      return peer;
+    }
+  else
+    {
+      len = *length - namelen;
+      if (len > 4) len = 4;
+      
+      oid2in_addr (name + namelen, len, addr);
+      
+      peer = bgp_peer_lookup_next (addr);
+
+      if (peer == NULL)
+       return NULL;
+
+      oid_copy_addr (name + namelen, addr, sizeof (struct in_addr));
+      *length = sizeof (struct in_addr) + namelen;
+
+      return peer;
+    }
+  return NULL;
+}
+
+/* BGP write methods. */
+static int
+write_bgpPeerTable (int action, u_char *var_val,
+                   u_char var_val_type, size_t var_val_len,
+                   u_char *statP, oid *name, size_t length)
+{
+  struct in_addr addr;
+  struct peer *peer;
+  long intval;
+
+  if (var_val_type != ASN_INTEGER) 
+    {
+      return SNMP_ERR_WRONGTYPE;
+    }
+  if (var_val_len != sizeof (long)) 
+    {
+      return SNMP_ERR_WRONGLENGTH;
+    }
+
+  intval = *(long *)var_val;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+
+  peer = bgpPeerTable_lookup (NULL, name, &length, &addr, 1);
+  if (! peer)
+    return SNMP_ERR_NOSUCHNAME;
+
+  if (action != SNMP_MSG_INTERNAL_SET_COMMIT)
+    return SNMP_ERR_NOERROR;
+
+  zlog_info ("%s: SNMP write .%ld = %ld",
+             peer->host, (long)name[PEERTAB_NAMELEN - 1], intval);
+
+  switch (name[PEERTAB_NAMELEN - 1])
+    {
+    case BGPPEERADMINSTATUS:
+#define BGP_PeerAdmin_stop  1
+#define BGP_PeerAdmin_start 2
+      /* When the peer is established,   */
+      if (intval == BGP_PeerAdmin_stop)
+       BGP_EVENT_ADD (peer, BGP_Stop);
+      else if (intval == BGP_PeerAdmin_start)
+       ;                       /* Do nothing. */
+      else
+       return SNMP_ERR_NOSUCHNAME;
+      break;
+    case BGPPEERCONNECTRETRYINTERVAL:
+      SET_FLAG (peer->config, PEER_CONFIG_CONNECT);
+      peer->connect = intval;
+      peer->v_connect = intval;
+      break;
+    case BGPPEERHOLDTIMECONFIGURED:
+      SET_FLAG (peer->config, PEER_CONFIG_TIMER);
+      peer->holdtime = intval;
+      peer->v_holdtime = intval;
+      break;
+    case BGPPEERKEEPALIVECONFIGURED:
+      SET_FLAG (peer->config, PEER_CONFIG_TIMER);
+      peer->keepalive = intval;
+      peer->v_keepalive = intval;
+      break;
+    case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+      peer->v_routeadv = intval;
+      break;
+    }
+  return SNMP_ERR_NOERROR;
+}
+
+static u_char *
+bgpPeerTable (struct variable *v, oid name[], size_t *length,
+             int exact, size_t *var_len, WriteMethod **write_method)
+{
+  static struct in_addr addr;
+  struct peer *peer;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+  memset (&addr, 0, sizeof (struct in_addr));
+
+  peer = bgpPeerTable_lookup (v, name, length, &addr, exact);
+  if (! peer)
+    return NULL;
+
+  switch (v->magic)
+    {
+    case BGPPEERIDENTIFIER:
+      return SNMP_IPADDRESS (peer->remote_id);
+      break;
+    case BGPPEERSTATE:
+      return SNMP_INTEGER (peer->status);
+      break;
+    case BGPPEERADMINSTATUS:
+      *write_method = write_bgpPeerTable;
+#define BGP_PeerAdmin_stop  1
+#define BGP_PeerAdmin_start 2
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
+       return SNMP_INTEGER (BGP_PeerAdmin_stop);
+      else
+       return SNMP_INTEGER (BGP_PeerAdmin_start);
+      break;
+    case BGPPEERNEGOTIATEDVERSION:
+      return SNMP_INTEGER (BGP_VERSION_4);
+      break;
+    case BGPPEERLOCALADDR:
+      if (peer->su_local)
+       return SNMP_IPADDRESS (peer->su_local->sin.sin_addr);
+      else
+       return SNMP_IPADDRESS (bgp_empty_addr);
+      break;
+    case BGPPEERLOCALPORT:
+      if (peer->su_local)
+       return SNMP_INTEGER (ntohs (peer->su_local->sin.sin_port));
+      else
+       return SNMP_INTEGER (0);
+      break;
+    case BGPPEERREMOTEADDR:
+      if (peer->su_remote)
+       return SNMP_IPADDRESS (peer->su_remote->sin.sin_addr);
+      else
+       return SNMP_IPADDRESS (bgp_empty_addr);
+      break;
+    case BGPPEERREMOTEPORT:
+      if (peer->su_remote)
+       return SNMP_INTEGER (ntohs (peer->su_remote->sin.sin_port));
+      else
+       return SNMP_INTEGER (0);
+      break;
+    case BGPPEERREMOTEAS:
+      return SNMP_INTEGER (peer->as);
+      break;
+    case BGPPEERINUPDATES:
+      return SNMP_INTEGER (peer->update_in);
+      break;
+    case BGPPEEROUTUPDATES:
+      return SNMP_INTEGER (peer->update_out);
+      break;
+    case BGPPEERINTOTALMESSAGES:
+      return SNMP_INTEGER (peer->open_in + peer->update_in
+                          + peer->keepalive_in + peer->notify_in
+                          + peer->refresh_in + peer->dynamic_cap_in);
+      break;
+    case BGPPEEROUTTOTALMESSAGES:
+      return SNMP_INTEGER (peer->open_out + peer->update_out
+                          + peer->keepalive_out + peer->notify_out
+                          + peer->refresh_out + peer->dynamic_cap_out);
+      break;
+    case BGPPEERLASTERROR:
+      {
+       static u_char lasterror[2];
+       lasterror[0] = peer->notify.code;
+       lasterror[1] = peer->notify.subcode;
+       *var_len = 2;
+       return (u_char *)&lasterror;
+      }
+      break;
+    case BGPPEERFSMESTABLISHEDTRANSITIONS:
+      return SNMP_INTEGER (peer->established);
+      break;
+    case BGPPEERFSMESTABLISHEDTIME:
+      if (peer->uptime == 0)
+       return SNMP_INTEGER (0);
+      else
+       return SNMP_INTEGER (bgp_clock () - peer->uptime);
+      break;
+    case BGPPEERCONNECTRETRYINTERVAL:
+      *write_method = write_bgpPeerTable;
+      return SNMP_INTEGER (peer->v_connect);
+      break;
+    case BGPPEERHOLDTIME:
+      return SNMP_INTEGER (peer->v_holdtime);
+      break;
+    case BGPPEERKEEPALIVE:
+      return SNMP_INTEGER (peer->v_keepalive);
+      break;
+    case BGPPEERHOLDTIMECONFIGURED:
+      *write_method = write_bgpPeerTable;
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+       return SNMP_INTEGER (peer->holdtime);
+      else
+       return SNMP_INTEGER (peer->v_holdtime);
+      break;
+    case BGPPEERKEEPALIVECONFIGURED:
+      *write_method = write_bgpPeerTable;
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+       return SNMP_INTEGER (peer->keepalive);
+      else
+       return SNMP_INTEGER (peer->v_keepalive);
+      break;
+    case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+      *write_method = write_bgpPeerTable;
+      return SNMP_INTEGER (peer->v_routeadv);
+      break;
+    case BGPPEERINUPDATEELAPSEDTIME:
+      if (peer->update_time == 0)
+       return SNMP_INTEGER (0);
+      else
+       return SNMP_INTEGER (bgp_clock () - peer->update_time);
+      break;
+    default:
+      return NULL;
+      break;
+    }  
+  return NULL;
+}
+
+static u_char *
+bgpIdentifier (struct variable *v, oid name[], size_t *length,
+              int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct bgp *bgp;
+
+  if (smux_header_generic(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  bgp = bgp_get_default ();
+  if (!bgp)
+    return NULL;
+
+  return SNMP_IPADDRESS (bgp->router_id);
+}
+
+static u_char *
+bgpRcvdPathAttrTable (struct variable *v, oid name[], size_t *length,
+                     int exact, size_t *var_len, WriteMethod **write_method)
+{
+  /* Received Path Attribute Table.  This table contains, one entry
+     per path to a network, path attributes received from all peers
+     running BGP version 3 or less.  This table is obsolete, having
+     been replaced in functionality with the bgp4PathAttrTable.  */
+  return NULL;
+}
+
+static struct bgp_info *
+bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
+                   struct bgp *bgp, struct prefix_ipv4 *addr, int exact)
+{
+  oid *offset;
+  int offsetlen;
+  struct bgp_info *binfo;
+  struct bgp_info *min;
+  struct bgp_node *rn;
+  union sockunion su;
+  unsigned int len;
+  struct in_addr paddr;
+
+#define BGP_PATHATTR_ENTRY_OFFSET \
+          (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE)
+
+  if (exact)
+    {
+      if (*length - v->namelen != BGP_PATHATTR_ENTRY_OFFSET)
+       return NULL;
+
+      /* Set OID offset for prefix. */
+      offset = name + v->namelen;
+      oid2in_addr (offset, IN_ADDR_SIZE, &addr->prefix);
+      offset += IN_ADDR_SIZE;
+
+      /* Prefix length. */
+      addr->prefixlen = *offset;
+      offset++;
+
+      /* Peer address. */
+      su.sin.sin_family = AF_INET;
+      oid2in_addr (offset, IN_ADDR_SIZE, &su.sin.sin_addr);
+
+      /* Lookup node. */
+      rn = bgp_node_lookup (bgp->rib[AFI_IP][SAFI_UNICAST], 
+                             (struct prefix *) addr);
+      if (rn)
+       {
+         bgp_unlock_node (rn);
+
+         for (binfo = rn->info; binfo; binfo = binfo->next)
+           if (sockunion_same (&binfo->peer->su, &su))
+             return binfo;
+       }
+    }
+  else
+    {
+      offset = name + v->namelen;
+      offsetlen = *length - v->namelen;
+      len = offsetlen;
+
+      if (offsetlen == 0)
+       rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_UNICAST]);
+      else
+       {
+         if (len > IN_ADDR_SIZE)
+           len = IN_ADDR_SIZE;
+      
+         oid2in_addr (offset, len, &addr->prefix);
+
+         offset += IN_ADDR_SIZE;
+         offsetlen -= IN_ADDR_SIZE;
+
+         if (offsetlen > 0)
+           addr->prefixlen = *offset;
+         else
+           addr->prefixlen = len * 8;
+
+         rn = bgp_node_get (bgp->rib[AFI_IP][SAFI_UNICAST],
+                              (struct prefix *) addr);
+
+         offset++;
+         offsetlen--;
+       }
+
+      if (offsetlen > 0)
+       {
+         len = offsetlen;
+         if (len > IN_ADDR_SIZE)
+           len = IN_ADDR_SIZE;
+
+         oid2in_addr (offset, len, &paddr);
+       }
+      else
+       paddr.s_addr = 0;
+
+      if (! rn)
+       return NULL;
+
+      do
+       {
+         min = NULL;
+
+         for (binfo = rn->info; binfo; binfo = binfo->next)
+           {
+             if (binfo->peer->su.sin.sin_family == AF_INET
+                 && ntohl (paddr.s_addr) 
+                 < ntohl (binfo->peer->su.sin.sin_addr.s_addr))
+               {
+                 if (min)
+                   {
+                     if (ntohl (binfo->peer->su.sin.sin_addr.s_addr) 
+                         < ntohl (min->peer->su.sin.sin_addr.s_addr))
+                       min = binfo;
+                   }
+                 else
+                   min = binfo;
+               }
+           }
+
+         if (min)
+           {
+             *length = v->namelen + BGP_PATHATTR_ENTRY_OFFSET;
+
+             offset = name + v->namelen;
+             oid_copy_addr (offset, &rn->p.u.prefix4, IN_ADDR_SIZE);
+             offset += IN_ADDR_SIZE;
+             *offset = rn->p.prefixlen;
+             offset++;
+             oid_copy_addr (offset, &min->peer->su.sin.sin_addr, 
+                            IN_ADDR_SIZE);
+             addr->prefix = rn->p.u.prefix4;
+             addr->prefixlen = rn->p.prefixlen;
+
+             bgp_unlock_node (rn);
+
+             return min;
+           }
+
+         paddr.s_addr = 0;
+       }
+      while ((rn = bgp_route_next (rn)) != NULL);
+    }
+  return NULL;
+}
+
+static u_char *
+bgp4PathAttrTable (struct variable *v, oid name[], size_t *length,
+                  int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct bgp *bgp;
+  struct bgp_info *binfo;
+  struct prefix_ipv4 addr;
+  
+  bgp = bgp_get_default ();
+  if (! bgp)
+    return NULL;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+  memset (&addr, 0, sizeof (struct prefix_ipv4));
+
+  binfo = bgp4PathAttrLookup (v, name, length, bgp, &addr, exact);
+  if (! binfo)
+    return NULL;
+
+  switch (v->magic)
+    {
+    case BGP4PATHATTRPEER:     /* 1 */
+      return SNMP_IPADDRESS (binfo->peer->su.sin.sin_addr);
+      break;
+    case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */
+      return SNMP_INTEGER (addr.prefixlen);
+      break;
+    case BGP4PATHATTRIPADDRPREFIX: /* 3 */
+      return SNMP_IPADDRESS (addr.prefix);
+      break;
+    case BGP4PATHATTRORIGIN:   /* 4 */
+      return SNMP_INTEGER (binfo->attr->origin);
+      break;
+    case BGP4PATHATTRASPATHSEGMENT: /* 5 */
+      return aspath_snmp_pathseg (binfo->attr->aspath, var_len);
+      break;
+    case BGP4PATHATTRNEXTHOP:  /* 6 */
+      return SNMP_IPADDRESS (binfo->attr->nexthop);
+      break;
+    case BGP4PATHATTRMULTIEXITDISC: /* 7 */
+      return SNMP_INTEGER (binfo->attr->med);
+      break;
+    case BGP4PATHATTRLOCALPREF:        /* 8 */
+      return SNMP_INTEGER (binfo->attr->local_pref);
+      break;
+    case BGP4PATHATTRATOMICAGGREGATE: /* 9 */
+      return SNMP_INTEGER (1);
+      break;
+    case BGP4PATHATTRAGGREGATORAS: /* 10 */
+      if (binfo->attr->extra)
+        return SNMP_INTEGER (binfo->attr->extra->aggregator_as);
+      else
+        return SNMP_INTEGER (0);
+      break;
+    case BGP4PATHATTRAGGREGATORADDR: /* 11 */
+      if (binfo->attr->extra)
+        return SNMP_IPADDRESS (binfo->attr->extra->aggregator_addr);
+      else
+        return SNMP_INTEGER (0);
+      break;
+    case BGP4PATHATTRCALCLOCALPREF: /* 12 */
+      return SNMP_INTEGER (-1);
+      break;
+    case BGP4PATHATTRBEST:     /* 13 */
+#define BGP4_PathAttrBest_false 1
+#define BGP4_PathAttrBest_true  2
+      if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED))
+       return SNMP_INTEGER (BGP4_PathAttrBest_true);
+      else
+       return SNMP_INTEGER (BGP4_PathAttrBest_false);
+      break;
+    case BGP4PATHATTRUNKNOWN:  /* 14 */
+      *var_len = 0;
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+/* BGP Traps. */
+struct trap_object bgpTrapList[] =
+{
+  {3, {3, 1, BGPPEERLASTERROR}},
+  {3, {3, 1, BGPPEERSTATE}}
+};
+
+void
+bgpTrapEstablished (struct peer *peer)
+{
+  int ret;
+  struct in_addr addr;
+  oid index[sizeof (oid) * IN_ADDR_SIZE];
+
+  ret = inet_aton (peer->host, &addr);
+  if (ret == 0)
+    return;
+
+  oid_copy_addr (index, &addr, IN_ADDR_SIZE);
+
+  smux_trap (bgp_variables, sizeof bgp_variables / sizeof (struct variable),
+            bgp_trap_oid, sizeof bgp_trap_oid / sizeof (oid),
+            bgp_oid, sizeof bgp_oid / sizeof (oid),
+            index, IN_ADDR_SIZE,
+            bgpTrapList, sizeof bgpTrapList / sizeof (struct trap_object),
+            BGPESTABLISHED);
+}
+
+void
+bgpTrapBackwardTransition (struct peer *peer)
+{
+  int ret;
+  struct in_addr addr;
+  oid index[sizeof (oid) * IN_ADDR_SIZE];
+
+  ret = inet_aton (peer->host, &addr);
+  if (ret == 0)
+    return;
+
+  oid_copy_addr (index, &addr, IN_ADDR_SIZE);
+
+  smux_trap (bgp_variables, sizeof bgp_variables / sizeof (struct variable),
+            bgp_trap_oid, sizeof bgp_trap_oid / sizeof (oid),
+            bgp_oid, sizeof bgp_oid / sizeof (oid),
+            index, IN_ADDR_SIZE,
+            bgpTrapList, sizeof bgpTrapList / sizeof (struct trap_object),
+            BGPBACKWARDTRANSITION);
+}
+
+void
+bgp_snmp_init (void)
+{
+  smux_init (bm->master);
+  REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid);
+}
+#endif /* HAVE_SNMP */
diff --git a/bgpd/bgp_snmp.h b/bgpd/bgp_snmp.h
new file mode 100644 (file)
index 0000000..7a0d9dd
--- /dev/null
@@ -0,0 +1,28 @@
+/* BGP4 SNMP support
+   Copyright (C) 1999, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_SNMP_H
+#define _QUAGGA_BGP_SNMP_H
+
+extern void bgp_snmp_init (void);
+extern void bgpTrapEstablished (struct peer *);
+extern void bgpTrapBackwardTransition (struct peer *);
+
+#endif /* _QUAGGA_BGP_SNMP_H */
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
new file mode 100644 (file)
index 0000000..92bb957
--- /dev/null
@@ -0,0 +1,126 @@
+/* BGP routing table
+   Copyright (C) 1998, 2001 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "sockunion.h"
+#include "vty.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+
+void
+bgp_table_lock (struct bgp_table *rt)
+{
+  rt->lock++;
+}
+
+void
+bgp_table_unlock (struct bgp_table *rt)
+{
+  assert (rt->lock > 0);
+  rt->lock--;
+
+  if (rt->lock != 0) 
+    {
+      return;
+    }
+
+  route_table_finish (rt->route_table);
+  rt->route_table = NULL;
+
+  if (rt->owner)
+    {
+      peer_unlock (rt->owner);
+      rt->owner = NULL;
+    }
+
+  XFREE (MTYPE_BGP_TABLE, rt);
+}
+
+void
+bgp_table_finish (struct bgp_table **rt)
+{
+  if (*rt != NULL)
+    {
+      bgp_table_unlock(*rt);
+      *rt = NULL;
+    }
+}
+
+/*
+ * bgp_node_create
+ */
+static struct route_node *
+bgp_node_create (route_table_delegate_t *delegate, struct route_table *table)
+{
+  struct bgp_node *node;
+  node = XCALLOC (MTYPE_BGP_NODE, sizeof (struct bgp_node));
+  return bgp_node_to_rnode (node);
+}
+
+/*
+ * bgp_node_destroy
+ */
+static void
+bgp_node_destroy (route_table_delegate_t *delegate,
+                 struct route_table *table, struct route_node *node)
+{
+  struct bgp_node *bgp_node;
+  bgp_node = bgp_node_from_rnode (node);
+  XFREE (MTYPE_BGP_NODE, bgp_node);
+}
+
+/*
+ * Function vector to customize the behavior of the route table
+ * library for BGP route tables.
+ */
+route_table_delegate_t bgp_table_delegate = {
+  .create_node = bgp_node_create,
+  .destroy_node = bgp_node_destroy
+};
+
+/*
+ * bgp_table_init
+ */
+struct bgp_table *
+bgp_table_init (afi_t afi, safi_t safi)
+{
+  struct bgp_table *rt;
+
+  rt = XCALLOC (MTYPE_BGP_TABLE, sizeof (struct bgp_table));
+
+  rt->route_table = route_table_init_with_delegate (&bgp_table_delegate);
+
+  /*
+   * Set up back pointer to bgp_table.
+   */
+  rt->route_table->info = rt;
+
+  bgp_table_lock (rt);
+  rt->type = BGP_TABLE_MAIN;
+  rt->afi = afi;
+  rt->safi = safi;
+
+  return rt;
+}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
new file mode 100644 (file)
index 0000000..8e963ae
--- /dev/null
@@ -0,0 +1,314 @@
+/* BGP routing table
+   Copyright (C) 1998, 2001 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_TABLE_H
+#define _QUAGGA_BGP_TABLE_H
+
+#include "table.h"
+
+typedef enum
+{
+  BGP_TABLE_MAIN,
+  BGP_TABLE_RSCLIENT,
+} bgp_table_t;
+
+struct bgp_table
+{
+  bgp_table_t type;
+  
+  /* afi/safi of this table */
+  afi_t afi;
+  safi_t safi;
+  
+  int lock;
+
+  /* The owner of this 'bgp_table' structure. */
+  struct peer *owner;
+
+  struct route_table *route_table;
+};
+
+struct bgp_node
+{
+  /*
+   * CAUTION
+   *
+   * These fields must be the very first fields in this structure.
+   *
+   * @see bgp_node_to_rnode
+   * @see bgp_node_from_rnode
+   */
+  ROUTE_NODE_FIELDS
+
+  struct bgp_adj_out *adj_out;
+
+  struct bgp_adj_in *adj_in;
+
+  struct bgp_node *prn;
+
+  u_char flags;
+#define BGP_NODE_PROCESS_SCHEDULED     (1 << 0)
+#define BGP_NODE_USER_CLEAR             (1 << 1)
+};
+
+/*
+ * bgp_table_iter_t
+ * 
+ * Structure that holds state for iterating over a bgp table.
+ */
+typedef struct bgp_table_iter_t_
+{
+  struct bgp_table *table;
+  route_table_iter_t rt_iter;
+} bgp_table_iter_t;
+
+extern struct bgp_table *bgp_table_init (afi_t, safi_t);
+extern void bgp_table_lock (struct bgp_table *);
+extern void bgp_table_unlock (struct bgp_table *);
+extern void bgp_table_finish (struct bgp_table **);
+
+
+/*
+ * bgp_node_from_rnode
+ *
+ * Returns the bgp_node structure corresponding to a route_node.
+ */
+static inline struct bgp_node *
+bgp_node_from_rnode (struct route_node *rnode)
+{
+  return (struct bgp_node *) rnode;
+}
+
+/*
+ * bgp_node_to_rnode
+ *
+ * Returns the route_node structure corresponding to a bgp_node.
+ */
+static inline struct route_node *
+bgp_node_to_rnode (struct bgp_node *node)
+{
+  return (struct route_node *) node;
+}
+
+/*
+ * bgp_node_table
+ *
+ * Returns the bgp_table that the given node is in.
+ */
+static inline struct bgp_table *
+bgp_node_table (struct bgp_node *node)
+{
+  return bgp_node_to_rnode (node)->table->info;
+}
+
+/*
+ * bgp_node_parent_nolock
+ *
+ * Gets the parent node of the given node without locking it.
+ */
+static inline struct bgp_node *
+bgp_node_parent_nolock (struct bgp_node *node)
+{
+  return bgp_node_from_rnode (node->parent);
+}
+
+/*
+ * bgp_unlock_node
+ */
+static inline void
+bgp_unlock_node (struct bgp_node *node)
+{
+  route_unlock_node (bgp_node_to_rnode (node));
+}
+
+/*
+ * bgp_table_top_nolock
+ *
+ * Gets the top node in the table without locking it.
+ *
+ * @see bgp_table_top
+ */
+static inline struct bgp_node *
+bgp_table_top_nolock (const struct bgp_table *const table)
+{
+  return bgp_node_from_rnode (table->route_table->top);
+}
+
+/*
+ * bgp_table_top
+ */
+static inline struct bgp_node *
+bgp_table_top (const struct bgp_table *const table)
+{
+  return bgp_node_from_rnode (route_top (table->route_table));
+}
+
+/*
+ * bgp_route_next
+ */
+static inline struct bgp_node *
+bgp_route_next (struct bgp_node *node)
+{
+  return bgp_node_from_rnode (route_next (bgp_node_to_rnode (node)));
+}
+
+/*
+ * bgp_route_next_until
+ */
+static inline struct bgp_node *
+bgp_route_next_until (struct bgp_node *node, struct bgp_node *limit)
+{
+  struct route_node *rnode;
+
+  rnode = route_next_until (bgp_node_to_rnode (node),
+                           bgp_node_to_rnode (limit));
+  return bgp_node_from_rnode (rnode);
+}
+
+/*
+ * bgp_node_get
+ */
+static inline struct bgp_node *
+bgp_node_get (struct bgp_table *const table, struct prefix *p)
+{
+  return bgp_node_from_rnode (route_node_get (table->route_table, p));
+}
+
+/*
+ * bgp_node_lookup
+ */
+static inline struct bgp_node *
+bgp_node_lookup (const struct bgp_table *const table, struct prefix *p)
+{
+  return bgp_node_from_rnode (route_node_lookup (table->route_table, p));
+}
+
+/*
+ * bgp_lock_node
+ */
+static inline struct bgp_node *
+bgp_lock_node (struct bgp_node *node)
+{
+  return bgp_node_from_rnode (route_lock_node (bgp_node_to_rnode (node)));
+}
+
+/*
+ * bgp_node_match
+ */
+static inline struct bgp_node *
+bgp_node_match (const struct bgp_table *table, struct prefix *p)
+{
+  return bgp_node_from_rnode (route_node_match (table->route_table, p));
+}
+
+/*
+ * bgp_node_match_ipv4
+ */
+static inline struct bgp_node *
+bgp_node_match_ipv4 (const struct bgp_table *table, struct in_addr *addr)
+{
+  return bgp_node_from_rnode (route_node_match_ipv4 (table->route_table, 
+                                                    addr));
+}
+
+/*
+ * bgp_node_match_ipv6
+ */
+static inline struct bgp_node *
+bgp_node_match_ipv6 (const struct bgp_table *table, struct in6_addr *addr)
+{
+  return bgp_node_from_rnode (route_node_match_ipv6 (table->route_table,
+                                                    addr));
+}
+
+static inline unsigned long
+bgp_table_count (const struct bgp_table *const table)
+{
+  return route_table_count (table->route_table);
+}
+
+/*
+ * bgp_table_get_next
+ */
+static inline struct bgp_node *
+bgp_table_get_next (const struct bgp_table *table, struct prefix *p)
+{
+  return bgp_node_from_rnode (route_table_get_next (table->route_table, p));
+}
+
+/*
+ * bgp_table_iter_init
+ */
+static inline void
+bgp_table_iter_init (bgp_table_iter_t * iter, struct bgp_table *table)
+{
+  bgp_table_lock (table);
+  iter->table = table;
+  route_table_iter_init (&iter->rt_iter, table->route_table);
+}
+
+/*
+ * bgp_table_iter_next
+ */
+static inline struct bgp_node *
+bgp_table_iter_next (bgp_table_iter_t * iter)
+{
+  return bgp_node_from_rnode (route_table_iter_next (&iter->rt_iter));
+}
+
+/*
+ * bgp_table_iter_cleanup
+ */
+static inline void
+bgp_table_iter_cleanup (bgp_table_iter_t * iter)
+{
+  route_table_iter_cleanup (&iter->rt_iter);
+  bgp_table_unlock (iter->table);
+  iter->table = NULL;
+}
+
+/*
+ * bgp_table_iter_pause
+ */
+static inline void
+bgp_table_iter_pause (bgp_table_iter_t * iter)
+{
+  route_table_iter_pause (&iter->rt_iter);
+}
+
+/*
+ * bgp_table_iter_is_done
+ */
+static inline int
+bgp_table_iter_is_done (bgp_table_iter_t * iter)
+{
+  return route_table_iter_is_done (&iter->rt_iter);
+}
+
+/*
+ * bgp_table_iter_started
+ */
+static inline int
+bgp_table_iter_started (bgp_table_iter_t * iter)
+{
+  return route_table_iter_started (&iter->rt_iter);
+}
+
+#endif /* _QUAGGA_BGP_TABLE_H */
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
new file mode 100644 (file)
index 0000000..0040d62
--- /dev/null
@@ -0,0 +1,12619 @@
+/* BGP VTY interface.
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "plist.h"
+#include "buffer.h"
+#include "linklist.h"
+#include "stream.h"
+#include "thread.h"
+#include "log.h"
+#include "memory.h"
+#include "hash.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+
+/* Utility function to get address family from current node.  */
+afi_t
+bgp_node_afi (struct vty *vty)
+{
+  afi_t afi;
+  switch (vty->node)
+    {
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_ENCAPV6_NODE:
+      afi = AFI_IP6;
+      break;
+    default:
+      afi = AFI_IP;
+      break;
+    }
+  return afi;
+}
+
+/* Utility function to get subsequent address family from current
+   node.  */
+safi_t
+bgp_node_safi (struct vty *vty)
+{
+  safi_t safi;
+  switch (vty->node)
+    {
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+      safi = SAFI_ENCAP;
+      break;
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+      safi = SAFI_MPLS_VPN;
+      break;
+    case BGP_IPV4M_NODE:
+    case BGP_IPV6M_NODE:
+      safi = SAFI_MULTICAST;
+      break;
+    default:
+      safi = SAFI_UNICAST;
+      break;
+  }
+  return safi;
+}
+
+int
+bgp_parse_afi(const char *str, afi_t *afi)
+{
+    if (!strcmp(str, "ipv4")) {
+       *afi = AFI_IP;
+       return 0;
+    }
+    if (!strcmp(str, "ipv6")) {
+       *afi = AFI_IP6;
+       return 0;
+    }
+    return -1;
+}
+
+int
+bgp_parse_safi(const char *str, safi_t *safi)
+{
+    if (!strcmp(str, "encap")) {
+       *safi = SAFI_ENCAP;
+       return 0;
+    }
+    if (!strcmp(str, "multicast")) {
+       *safi =  SAFI_MULTICAST;
+       return 0;
+    }
+    if (!strcmp(str, "unicast")) {
+       *safi =  SAFI_UNICAST;
+       return 0;
+    }
+    if (!strcmp(str, "vpn")) {
+       *safi =  SAFI_MPLS_VPN;
+       return 0;
+    }
+    return -1;
+}
+
+static int
+peer_address_self_check (union sockunion *su)
+{
+  struct interface *ifp = NULL;
+
+  if (su->sa.sa_family == AF_INET)
+    ifp = if_lookup_by_ipv4_exact (&su->sin.sin_addr);
+  else if (su->sa.sa_family == AF_INET6)
+    ifp = if_lookup_by_ipv6_exact (&su->sin6.sin6_addr);
+
+  if (ifp)
+    return 1;
+
+  return 0;
+}
+
+/* Utility function for looking up peer from VTY.  */
+static struct peer *
+peer_lookup_vty (struct vty *vty, const char *ip_str)
+{
+  int ret;
+  struct bgp *bgp;
+  union sockunion su;
+  struct peer *peer;
+
+  bgp = vty->index;
+
+  ret = str2sockunion (ip_str, &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", ip_str, VTY_NEWLINE);
+      return NULL;
+    }
+
+  peer = peer_lookup (bgp, &su);
+  if (! peer)
+    {
+      vty_out (vty, "%% Specify remote-as or peer-group commands first%s", VTY_NEWLINE);
+      return NULL;
+    }
+  return peer;
+}
+
+/* Utility function for looking up peer or peer group.  */
+static struct peer *
+peer_and_group_lookup_vty (struct vty *vty, const char *peer_str)
+{
+  int ret;
+  struct bgp *bgp;
+  union sockunion su;
+  struct peer *peer;
+  struct peer_group *group;
+
+  bgp = vty->index;
+
+  ret = str2sockunion (peer_str, &su);
+  if (ret == 0)
+    {
+      peer = peer_lookup (bgp, &su);
+      if (peer)
+       return peer;
+    }
+  else
+    {
+      group = peer_group_lookup (bgp, peer_str);
+      if (group)
+       return group->conf;
+    }
+
+  vty_out (vty, "%% Specify remote-as or peer-group commands first%s",
+          VTY_NEWLINE);
+
+  return NULL;
+}
+
+static int
+bgp_vty_return (struct vty *vty, int ret)
+{
+  const char *str = NULL;
+
+  switch (ret)
+    {
+    case BGP_ERR_INVALID_VALUE:
+      str = "Invalid value";
+      break;
+    case BGP_ERR_INVALID_FLAG:
+      str = "Invalid flag";
+      break;
+    case BGP_ERR_PEER_INACTIVE:
+      str = "Activate the neighbor for the address family first";
+      break;
+    case BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER:
+      str = "Invalid command for a peer-group member";
+      break;
+    case BGP_ERR_PEER_GROUP_SHUTDOWN:
+      str = "Peer-group has been shutdown. Activate the peer-group first";
+      break;
+    case BGP_ERR_PEER_GROUP_HAS_THE_FLAG:
+      str = "This peer is a peer-group member.  Please change peer-group configuration";
+      break;
+    case BGP_ERR_PEER_FLAG_CONFLICT:
+      str = "Can't set override-capability and strict-capability-match at the same time";
+      break;
+    case BGP_ERR_PEER_GROUP_MEMBER_EXISTS:
+      str = "No activate for peergroup can be given only if peer-group has no members";
+      break;
+    case BGP_ERR_PEER_BELONGS_TO_GROUP:
+      str = "No activate for an individual peer-group member is invalid";
+      break;
+    case BGP_ERR_PEER_GROUP_AF_UNCONFIGURED:
+      str = "Activate the peer-group for the address family first";
+      break;
+    case BGP_ERR_PEER_GROUP_NO_REMOTE_AS:
+      str = "Specify remote-as or peer-group remote AS first";
+      break;
+    case BGP_ERR_PEER_GROUP_CANT_CHANGE:
+      str = "Cannot change the peer-group. Deconfigure first";
+      break;
+    case BGP_ERR_PEER_GROUP_MISMATCH:
+      str = "Cannot have different peer-group for the neighbor";
+      break;
+    case BGP_ERR_PEER_FILTER_CONFLICT:
+      str = "Prefix/distribute list can not co-exist";
+      break;
+    case BGP_ERR_NOT_INTERNAL_PEER:
+      str = "Invalid command. Not an internal neighbor";
+      break;
+    case BGP_ERR_REMOVE_PRIVATE_AS:
+      str = "Private AS cannot be removed for IBGP peers";
+      break;
+    case BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP:
+      str = "Local-AS allowed only for EBGP peers";
+      break;
+    case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS:
+      str = "Cannot have local-as same as BGP AS number";
+      break;
+    case BGP_ERR_TCPSIG_FAILED:
+      str = "Error while applying TCP-Sig to session(s)";
+      break;
+    case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK:
+      str = "ebgp-multihop and ttl-security cannot be configured together";
+      break;
+    case BGP_ERR_NO_IBGP_WITH_TTLHACK:
+      str = "ttl-security only allowed for EBGP peers";
+      break;
+    }
+  if (str)
+    {
+      vty_out (vty, "%% %s%s", str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+/* BGP global configuration.  */
+
+DEFUN (bgp_multiple_instance_func,
+       bgp_multiple_instance_cmd,
+       "bgp multiple-instance",
+       BGP_STR
+       "Enable bgp multiple instance\n")
+{
+  bgp_option_set (BGP_OPT_MULTIPLE_INSTANCE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_multiple_instance,
+       no_bgp_multiple_instance_cmd,
+       "no bgp multiple-instance",
+       NO_STR
+       BGP_STR
+       "BGP multiple instance\n")
+{
+  int ret;
+
+  ret = bgp_option_unset (BGP_OPT_MULTIPLE_INSTANCE);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% There are more than two BGP instances%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_config_type,
+       bgp_config_type_cmd,
+       "bgp config-type (cisco|zebra)",
+       BGP_STR
+       "Configuration type\n"
+       "cisco\n"
+       "zebra\n")
+{
+  if (strncmp (argv[0], "c", 1) == 0)
+    bgp_option_set (BGP_OPT_CONFIG_CISCO);
+  else
+    bgp_option_unset (BGP_OPT_CONFIG_CISCO);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_config_type,
+       no_bgp_config_type_cmd,
+       "no bgp config-type",
+       NO_STR
+       BGP_STR
+       "Display configuration type\n")
+{
+  bgp_option_unset (BGP_OPT_CONFIG_CISCO);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_synchronization,
+       no_synchronization_cmd,
+       "no synchronization",
+       NO_STR
+       "Perform IGP synchronization\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_auto_summary,
+       no_auto_summary_cmd,
+       "no auto-summary",
+       NO_STR
+       "Enable automatic network number summarization\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (neighbor_version,
+                 neighbor_version_cmd,
+                 NEIGHBOR_CMD "version (4|4-)",
+                 NEIGHBOR_STR
+                 NEIGHBOR_ADDR_STR
+                 "Set the BGP version to match a neighbor\n"
+                 "Neighbor's BGP version\n")
+{
+  return CMD_SUCCESS;
+}
+
+/* "router bgp" commands. */
+DEFUN (router_bgp, 
+       router_bgp_cmd, 
+       "router bgp " CMD_AS_RANGE,
+       ROUTER_STR
+       BGP_STR
+       AS_STR)
+{
+  int ret;
+  as_t as;
+  struct bgp *bgp;
+  const char *name = NULL;
+
+  VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+
+  if (argc == 2)
+    name = argv[1];
+
+  ret = bgp_get (&bgp, &as, name);
+  switch (ret)
+    {
+    case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET:
+      vty_out (vty, "Please specify 'bgp multiple-instance' first%s", 
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    case BGP_ERR_AS_MISMATCH:
+      vty_out (vty, "BGP is already running; AS is %u%s", as, VTY_NEWLINE);
+      return CMD_WARNING;
+    case BGP_ERR_INSTANCE_MISMATCH:
+      vty_out (vty, "BGP view name and AS number mismatch%s", VTY_NEWLINE);
+      vty_out (vty, "BGP instance is already running; AS is %u%s",
+              as, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vty->node = BGP_NODE;
+  vty->index = bgp;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (router_bgp,
+       router_bgp_view_cmd,
+       "router bgp " CMD_AS_RANGE " view WORD",
+       ROUTER_STR
+       BGP_STR
+       AS_STR
+       "BGP view\n"
+       "view name\n")
+
+/* "no router bgp" commands. */
+DEFUN (no_router_bgp,
+       no_router_bgp_cmd,
+       "no router bgp " CMD_AS_RANGE,
+       NO_STR
+       ROUTER_STR
+       BGP_STR
+       AS_STR)
+{
+  as_t as;
+  struct bgp *bgp;
+  const char *name = NULL;
+
+  VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+
+  if (argc == 2)
+    name = argv[1];
+
+  /* Lookup bgp structure. */
+  bgp = bgp_lookup (as, name);
+  if (! bgp)
+    {
+      vty_out (vty, "%% Can't find BGP instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_delete (bgp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_router_bgp,
+       no_router_bgp_view_cmd,
+       "no router bgp " CMD_AS_RANGE " view WORD",
+       NO_STR
+       ROUTER_STR
+       BGP_STR
+       AS_STR
+       "BGP view\n"
+       "view name\n")
+
+/* BGP router-id.  */
+
+DEFUN (bgp_router_id,
+       bgp_router_id_cmd,
+       "bgp router-id A.B.C.D",
+       BGP_STR
+       "Override configured router identifier\n"
+       "Manually configured router identifier\n")
+{
+  int ret;
+  struct in_addr id;
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  ret = inet_aton (argv[0], &id);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed bgp router identifier%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_router_id_static_set (bgp, id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_router_id,
+       no_bgp_router_id_cmd,
+       "no bgp router-id",
+       NO_STR
+       BGP_STR
+       "Override configured router identifier\n")
+{
+  int ret;
+  struct in_addr id;
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  if (argc == 1)
+    {
+      ret = inet_aton (argv[0], &id);
+      if (! ret)
+       {
+         vty_out (vty, "%% Malformed BGP router identifier%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      if (! IPV4_ADDR_SAME (&bgp->router_id_static, &id))
+       {
+         vty_out (vty, "%% BGP router-id doesn't match%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  id.s_addr = 0;
+  bgp_router_id_static_set (bgp, id);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_router_id,
+       no_bgp_router_id_val_cmd,
+       "no bgp router-id A.B.C.D",
+       NO_STR
+       BGP_STR
+       "Override configured router identifier\n"
+       "Manually configured router identifier\n")
+
+/* BGP Cluster ID.  */
+
+DEFUN (bgp_cluster_id,
+       bgp_cluster_id_cmd,
+       "bgp cluster-id A.B.C.D",
+       BGP_STR
+       "Configure Route-Reflector Cluster-id\n"
+       "Route-Reflector Cluster-id in IP address format\n")
+{
+  int ret;
+  struct bgp *bgp;
+  struct in_addr cluster;
+
+  bgp = vty->index;
+
+  ret = inet_aton (argv[0], &cluster);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed bgp cluster identifier%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_cluster_id_set (bgp, &cluster);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (bgp_cluster_id,
+       bgp_cluster_id32_cmd,
+       "bgp cluster-id <1-4294967295>",
+       BGP_STR
+       "Configure Route-Reflector Cluster-id\n"
+       "Route-Reflector Cluster-id as 32 bit quantity\n")
+
+DEFUN (no_bgp_cluster_id,
+       no_bgp_cluster_id_cmd,
+       "no bgp cluster-id",
+       NO_STR
+       BGP_STR
+       "Configure Route-Reflector Cluster-id\n")
+{
+  int ret;
+  struct bgp *bgp;
+  struct in_addr cluster;
+
+  bgp = vty->index;
+
+  if (argc == 1)
+    {
+      ret = inet_aton (argv[0], &cluster);
+      if (! ret)
+       {
+         vty_out (vty, "%% Malformed bgp cluster identifier%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  bgp_cluster_id_unset (bgp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_cluster_id,
+       no_bgp_cluster_id_arg_cmd,
+       "no bgp cluster-id A.B.C.D",
+       NO_STR
+       BGP_STR
+       "Configure Route-Reflector Cluster-id\n"
+       "Route-Reflector Cluster-id in IP address format\n")
+
+DEFUN (bgp_confederation_identifier,
+       bgp_confederation_identifier_cmd,
+       "bgp confederation identifier " CMD_AS_RANGE,
+       "BGP specific commands\n"
+       "AS confederation parameters\n"
+       "AS number\n"
+       "Set routing domain confederation AS\n")
+{
+  struct bgp *bgp;
+  as_t as;
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+
+  bgp_confederation_id_set (bgp, as);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_confederation_identifier,
+       no_bgp_confederation_identifier_cmd,
+       "no bgp confederation identifier",
+       NO_STR
+       "BGP specific commands\n"
+       "AS confederation parameters\n"
+       "AS number\n")
+{
+  struct bgp *bgp;
+  as_t as __attribute__((unused)); /* Dummy for VTY_GET_INTEGER_RANGE */
+
+  bgp = vty->index;
+
+  if (argc == 1)
+    VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX);
+
+  bgp_confederation_id_unset (bgp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_confederation_identifier,
+       no_bgp_confederation_identifier_arg_cmd,
+       "no bgp confederation identifier " CMD_AS_RANGE,
+       NO_STR
+       "BGP specific commands\n"
+       "AS confederation parameters\n"
+       "AS number\n"
+       "Set routing domain confederation AS\n")
+
+DEFUN (bgp_confederation_peers,
+       bgp_confederation_peers_cmd,
+       "bgp confederation peers ." CMD_AS_RANGE,
+       "BGP specific commands\n"
+       "AS confederation parameters\n"
+       "Peer ASs in BGP confederation\n"
+       AS_STR)
+{
+  struct bgp *bgp;
+  as_t as;
+  int i;
+
+  bgp = vty->index;
+
+  for (i = 0; i < argc; i++)
+    {
+      VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX);
+
+      if (bgp->as == as)
+       {
+         vty_out (vty, "%% Local member-AS not allowed in confed peer list%s",
+                  VTY_NEWLINE);
+         continue;
+       }
+
+      bgp_confederation_peers_add (bgp, as);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_confederation_peers,
+       no_bgp_confederation_peers_cmd,
+       "no bgp confederation peers ." CMD_AS_RANGE,
+       NO_STR
+       "BGP specific commands\n"
+       "AS confederation parameters\n"
+       "Peer ASs in BGP confederation\n"
+       AS_STR)
+{
+  struct bgp *bgp;
+  as_t as;
+  int i;
+
+  bgp = vty->index;
+
+  for (i = 0; i < argc; i++)
+    {
+      VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX);
+
+      bgp_confederation_peers_remove (bgp, as);
+    }
+  return CMD_SUCCESS;
+}
+
+/* Maximum-paths configuration */
+DEFUN (bgp_maxpaths,
+       bgp_maxpaths_cmd,
+       "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM),
+       "Forward packets over multiple paths\n"
+       "Number of paths\n")
+{
+  struct bgp *bgp;
+  u_int16_t maxpaths;
+  int ret;
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, argv[0], 1, 255);
+
+  ret = bgp_maximum_paths_set (bgp, bgp_node_afi (vty), bgp_node_safi(vty),
+                              BGP_PEER_EBGP, maxpaths);
+  if (ret < 0)
+    {
+      vty_out (vty,
+              "%% Failed to set maximum-paths %u for afi %u, safi %u%s",
+              maxpaths, bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_maxpaths_ibgp,
+       bgp_maxpaths_ibgp_cmd,
+       "maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM),
+       "Forward packets over multiple paths\n"
+       "iBGP-multipath\n"
+       "Number of paths\n")
+{
+  struct bgp *bgp;
+  u_int16_t maxpaths;
+  int ret;
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, argv[0], 1, 255);
+
+  ret = bgp_maximum_paths_set (bgp, bgp_node_afi (vty), bgp_node_safi(vty),
+                              BGP_PEER_IBGP, maxpaths);
+  if (ret < 0)
+    {
+      vty_out (vty,
+              "%% Failed to set maximum-paths ibgp %u for afi %u, safi %u%s",
+              maxpaths, bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_maxpaths,
+       no_bgp_maxpaths_cmd,
+       "no maximum-paths",
+       NO_STR
+       "Forward packets over multiple paths\n"
+       "Number of paths\n")
+{
+  struct bgp *bgp;
+  int ret;
+
+  bgp = vty->index;
+
+  ret = bgp_maximum_paths_unset (bgp, bgp_node_afi (vty), bgp_node_safi(vty),
+                                BGP_PEER_EBGP);
+  if (ret < 0)
+    {
+      vty_out (vty,
+              "%% Failed to unset maximum-paths for afi %u, safi %u%s",
+              bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_maxpaths,
+       no_bgp_maxpaths_arg_cmd,
+       "no maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM),
+       NO_STR
+       "Forward packets over multiple paths\n"
+       "Number of paths\n")
+
+DEFUN (no_bgp_maxpaths_ibgp,
+       no_bgp_maxpaths_ibgp_cmd,
+       "no maximum-paths ibgp",
+       NO_STR
+       "Forward packets over multiple paths\n"
+       "iBGP-multipath\n"
+       "Number of paths\n")
+{
+  struct bgp *bgp;
+  int ret;
+
+  bgp = vty->index;
+
+  ret = bgp_maximum_paths_unset (bgp, bgp_node_afi (vty), bgp_node_safi(vty),
+                                BGP_PEER_IBGP);
+  if (ret < 0)
+    {
+      vty_out (vty,
+              "%% Failed to unset maximum-paths ibgp for afi %u, safi %u%s",
+              bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_maxpaths_ibgp,
+       no_bgp_maxpaths_ibgp_arg_cmd,
+       "no maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM),
+       NO_STR
+       "Forward packets over multiple paths\n"
+       "iBGP-multipath\n"
+       "Number of paths\n")
+
+int
+bgp_config_write_maxpaths (struct vty *vty, struct bgp *bgp, afi_t afi,
+                          safi_t safi, int *write)
+{
+  if (bgp->maxpaths[afi][safi].maxpaths_ebgp != BGP_DEFAULT_MAXPATHS)
+    {
+      bgp_config_write_family_header (vty, afi, safi, write);
+      vty_out (vty, " maximum-paths %d%s",
+              bgp->maxpaths[afi][safi].maxpaths_ebgp, VTY_NEWLINE);
+    }
+
+  if (bgp->maxpaths[afi][safi].maxpaths_ibgp != BGP_DEFAULT_MAXPATHS)
+    {
+      bgp_config_write_family_header (vty, afi, safi, write);
+      vty_out (vty, " maximum-paths ibgp %d%s",
+              bgp->maxpaths[afi][safi].maxpaths_ibgp, VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+/* BGP timers.  */
+
+DEFUN (bgp_timers,
+       bgp_timers_cmd,
+       "timers bgp <0-65535> <0-65535>",
+       "Adjust routing timers\n"
+       "BGP timers\n"
+       "Keepalive interval\n"
+       "Holdtime\n")
+{
+  struct bgp *bgp;
+  unsigned long keepalive = 0;
+  unsigned long holdtime = 0;
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER ("keepalive", keepalive, argv[0]);
+  VTY_GET_INTEGER ("holdtime", holdtime, argv[1]);
+
+  /* Holdtime value check. */
+  if (holdtime < 3 && holdtime != 0)
+    {
+      vty_out (vty, "%% hold time value must be either 0 or greater than 3%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_timers_set (bgp, keepalive, holdtime);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_timers,
+       no_bgp_timers_cmd,
+       "no timers bgp",
+       NO_STR
+       "Adjust routing timers\n"
+       "BGP timers\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_timers_unset (bgp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_timers,
+       no_bgp_timers_arg_cmd,
+       "no timers bgp <0-65535> <0-65535>",
+       NO_STR
+       "Adjust routing timers\n"
+       "BGP timers\n"
+       "Keepalive interval\n"
+       "Holdtime\n")
+
+DEFUN (bgp_client_to_client_reflection,
+       bgp_client_to_client_reflection_cmd,
+       "bgp client-to-client reflection",
+       "BGP specific commands\n"
+       "Configure client to client route reflection\n"
+       "reflection of routes allowed\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_client_to_client_reflection,
+       no_bgp_client_to_client_reflection_cmd,
+       "no bgp client-to-client reflection",
+       NO_STR
+       "BGP specific commands\n"
+       "Configure client to client route reflection\n"
+       "reflection of routes allowed\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT);
+  return CMD_SUCCESS;
+}
+
+/* "bgp always-compare-med" configuration. */
+DEFUN (bgp_always_compare_med,
+       bgp_always_compare_med_cmd,
+       "bgp always-compare-med",
+       "BGP specific commands\n"
+       "Allow comparing MED from different neighbors\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ALWAYS_COMPARE_MED);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_always_compare_med,
+       no_bgp_always_compare_med_cmd,
+       "no bgp always-compare-med",
+       NO_STR
+       "BGP specific commands\n"
+       "Allow comparing MED from different neighbors\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ALWAYS_COMPARE_MED);
+  return CMD_SUCCESS;
+}
+
+/* "bgp deterministic-med" configuration. */
+DEFUN (bgp_deterministic_med,
+       bgp_deterministic_med_cmd,
+       "bgp deterministic-med",
+       "BGP specific commands\n"
+       "Pick the best-MED path among paths advertised from the neighboring AS\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_DETERMINISTIC_MED);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_deterministic_med,
+       no_bgp_deterministic_med_cmd,
+       "no bgp deterministic-med",
+       NO_STR
+       "BGP specific commands\n"
+       "Pick the best-MED path among paths advertised from the neighboring AS\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_DETERMINISTIC_MED);
+  return CMD_SUCCESS;
+}
+
+/* "bgp graceful-restart" configuration. */
+DEFUN (bgp_graceful_restart,
+       bgp_graceful_restart_cmd,
+       "bgp graceful-restart",
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_GRACEFUL_RESTART);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart,
+       no_bgp_graceful_restart_cmd,
+       "no bgp graceful-restart",
+       NO_STR
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_GRACEFUL_RESTART);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_stalepath_time,
+       bgp_graceful_restart_stalepath_time_cmd,
+       "bgp graceful-restart stalepath-time <1-3600>",
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the max time to hold onto restarting peer's stale paths\n"
+       "Delay value (seconds)\n")
+{
+  struct bgp *bgp;
+  u_int32_t stalepath;
+
+  bgp = vty->index;
+  if (! bgp)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER_RANGE ("stalepath-time", stalepath, argv[0], 1, 3600);
+  bgp->stalepath_time = stalepath;
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_restart_time,
+       bgp_graceful_restart_restart_time_cmd,
+       "bgp graceful-restart restart-time <1-3600>",
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the time to wait to delete stale routes before a BGP open message is received\n"
+       "Delay value (seconds)\n")
+{
+  struct bgp *bgp;
+  u_int32_t restart;
+
+  bgp = vty->index;
+  if (! bgp)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER_RANGE ("restart-time", restart, argv[0], 1, 3600);
+  bgp->restart_time = restart;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_stalepath_time,
+       no_bgp_graceful_restart_stalepath_time_cmd,
+       "no bgp graceful-restart stalepath-time",
+       NO_STR
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the max time to hold onto restarting peer's stale paths\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  if (! bgp)
+    return CMD_WARNING;
+
+  bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_restart_time,
+       no_bgp_graceful_restart_restart_time_cmd,
+       "no bgp graceful-restart restart-time",
+       NO_STR
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the time to wait to delete stale routes before a BGP open message is received\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  if (! bgp)
+    return CMD_WARNING;
+
+  bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_graceful_restart_stalepath_time,
+       no_bgp_graceful_restart_stalepath_time_val_cmd,
+       "no bgp graceful-restart stalepath-time <1-3600>",
+       NO_STR
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the max time to hold onto restarting peer's stale paths\n"
+       "Delay value (seconds)\n")
+
+ALIAS (no_bgp_graceful_restart_restart_time,
+       no_bgp_graceful_restart_restart_time_val_cmd,
+       "no bgp graceful-restart restart-time <1-3600>",
+       NO_STR
+       "BGP specific commands\n"
+       "Graceful restart capability parameters\n"
+       "Set the time to wait to delete stale routes before a BGP open message is received\n"
+       "Delay value (seconds)\n")
+
+/* "bgp fast-external-failover" configuration. */
+DEFUN (bgp_fast_external_failover,
+       bgp_fast_external_failover_cmd,
+       "bgp fast-external-failover",
+       BGP_STR
+       "Immediately reset session if a link to a directly connected external peer goes down\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_fast_external_failover,
+       no_bgp_fast_external_failover_cmd,
+       "no bgp fast-external-failover",
+       NO_STR
+       BGP_STR
+       "Immediately reset session if a link to a directly connected external peer goes down\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER);
+  return CMD_SUCCESS;
+}
+
+/* "bgp enforce-first-as" configuration. */
+DEFUN (bgp_enforce_first_as,
+       bgp_enforce_first_as_cmd,
+       "bgp enforce-first-as",
+       BGP_STR
+       "Enforce the first AS for EBGP routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ENFORCE_FIRST_AS);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_enforce_first_as,
+       no_bgp_enforce_first_as_cmd,
+       "no bgp enforce-first-as",
+       NO_STR
+       BGP_STR
+       "Enforce the first AS for EBGP routes\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ENFORCE_FIRST_AS);
+  return CMD_SUCCESS;
+}
+
+/* "bgp bestpath compare-routerid" configuration.  */
+DEFUN (bgp_bestpath_compare_router_id,
+       bgp_bestpath_compare_router_id_cmd,
+       "bgp bestpath compare-routerid",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "Compare router-id for identical EBGP paths\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_COMPARE_ROUTER_ID);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_compare_router_id,
+       no_bgp_bestpath_compare_router_id_cmd,
+       "no bgp bestpath compare-routerid",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "Compare router-id for identical EBGP paths\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_COMPARE_ROUTER_ID);
+  return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path ignore" configuration.  */
+DEFUN (bgp_bestpath_aspath_ignore,
+       bgp_bestpath_aspath_ignore_cmd,
+       "bgp bestpath as-path ignore",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Ignore as-path length in selecting a route\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ASPATH_IGNORE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_ignore,
+       no_bgp_bestpath_aspath_ignore_cmd,
+       "no bgp bestpath as-path ignore",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Ignore as-path length in selecting a route\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ASPATH_IGNORE);
+  return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path confed" configuration.  */
+DEFUN (bgp_bestpath_aspath_confed,
+       bgp_bestpath_aspath_confed_cmd,
+       "bgp bestpath as-path confed",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Compare path lengths including confederation sets & sequences in selecting a route\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ASPATH_CONFED);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_confed,
+       no_bgp_bestpath_aspath_confed_cmd,
+       "no bgp bestpath as-path confed",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Compare path lengths including confederation sets & sequences in selecting a route\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED);
+  return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path multipath-relax" configuration.  */
+DEFUN (bgp_bestpath_aspath_multipath_relax,
+       bgp_bestpath_aspath_multipath_relax_cmd,
+       "bgp bestpath as-path multipath-relax",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Allow load sharing across routes that have different AS paths (but same length)\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_multipath_relax,
+       no_bgp_bestpath_aspath_multipath_relax_cmd,
+       "no bgp bestpath as-path multipath-relax",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Allow load sharing across routes that have different AS paths (but same length)\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+  return CMD_SUCCESS;
+}
+
+/* "bgp log-neighbor-changes" configuration.  */
+DEFUN (bgp_log_neighbor_changes,
+       bgp_log_neighbor_changes_cmd,
+       "bgp log-neighbor-changes",
+       "BGP specific commands\n"
+       "Log neighbor up/down and reset reason\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_log_neighbor_changes,
+       no_bgp_log_neighbor_changes_cmd,
+       "no bgp log-neighbor-changes",
+       NO_STR
+       "BGP specific commands\n"
+       "Log neighbor up/down and reset reason\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+  return CMD_SUCCESS;
+}
+
+/* "bgp bestpath med" configuration. */
+DEFUN (bgp_bestpath_med,
+       bgp_bestpath_med_cmd,
+       "bgp bestpath med (confed|missing-as-worst)",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Compare MED among confederation paths\n"
+       "Treat missing MED as the least preferred one\n")
+{
+  struct bgp *bgp;
+  
+  bgp = vty->index;
+
+  if (strncmp (argv[0], "confed", 1) == 0)
+    bgp_flag_set (bgp, BGP_FLAG_MED_CONFED);
+  else
+    bgp_flag_set (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_bestpath_med2,
+       bgp_bestpath_med2_cmd,
+       "bgp bestpath med confed missing-as-worst",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Compare MED among confederation paths\n"
+       "Treat missing MED as the least preferred one\n")
+{
+  struct bgp *bgp;
+  
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_MED_CONFED);
+  bgp_flag_set (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
+  return CMD_SUCCESS;
+}
+
+ALIAS (bgp_bestpath_med2,
+       bgp_bestpath_med3_cmd,
+       "bgp bestpath med missing-as-worst confed",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Treat missing MED as the least preferred one\n"
+       "Compare MED among confederation paths\n")
+
+DEFUN (no_bgp_bestpath_med,
+       no_bgp_bestpath_med_cmd,
+       "no bgp bestpath med (confed|missing-as-worst)",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Compare MED among confederation paths\n"
+       "Treat missing MED as the least preferred one\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  
+  if (strncmp (argv[0], "confed", 1) == 0)
+    bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED);
+  else
+    bgp_flag_unset (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_med2,
+       no_bgp_bestpath_med2_cmd,
+       "no bgp bestpath med confed missing-as-worst",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Compare MED among confederation paths\n"
+       "Treat missing MED as the least preferred one\n")
+{
+  struct bgp *bgp;
+  
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED);
+  bgp_flag_unset (bgp, BGP_FLAG_MED_MISSING_AS_WORST);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_bestpath_med2,
+       no_bgp_bestpath_med3_cmd,
+       "no bgp bestpath med missing-as-worst confed",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "MED attribute\n"
+       "Treat missing MED as the least preferred one\n"
+       "Compare MED among confederation paths\n")
+
+/* "no bgp default ipv4-unicast". */
+DEFUN (no_bgp_default_ipv4_unicast,
+       no_bgp_default_ipv4_unicast_cmd,
+       "no bgp default ipv4-unicast",
+       NO_STR
+       "BGP specific commands\n"
+       "Configure BGP defaults\n"
+       "Activate ipv4-unicast for a peer by default\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_NO_DEFAULT_IPV4);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_default_ipv4_unicast,
+       bgp_default_ipv4_unicast_cmd,
+       "bgp default ipv4-unicast",
+       "BGP specific commands\n"
+       "Configure BGP defaults\n"
+       "Activate ipv4-unicast for a peer by default\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_NO_DEFAULT_IPV4);
+  return CMD_SUCCESS;
+}
+
+/* "bgp import-check" configuration.  */
+DEFUN (bgp_network_import_check,
+       bgp_network_import_check_cmd,
+       "bgp network import-check",
+       "BGP specific commands\n"
+       "BGP network command\n"
+       "Check BGP network route exists in IGP\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_IMPORT_CHECK);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_network_import_check,
+       no_bgp_network_import_check_cmd,
+       "no bgp network import-check",
+       NO_STR
+       "BGP specific commands\n"
+       "BGP network command\n"
+       "Check BGP network route exists in IGP\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_IMPORT_CHECK);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_default_local_preference,
+       bgp_default_local_preference_cmd,
+       "bgp default local-preference <0-4294967295>",
+       "BGP specific commands\n"
+       "Configure BGP defaults\n"
+       "local preference (higher=more preferred)\n"
+       "Configure default local preference value\n")
+{
+  struct bgp *bgp;
+  u_int32_t local_pref;
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER ("local preference", local_pref, argv[0]);
+
+  bgp_default_local_preference_set (bgp, local_pref);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_default_local_preference,
+       no_bgp_default_local_preference_cmd,
+       "no bgp default local-preference",
+       NO_STR
+       "BGP specific commands\n"
+       "Configure BGP defaults\n"
+       "local preference (higher=more preferred)\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_default_local_preference_unset (bgp);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bgp_default_local_preference,
+       no_bgp_default_local_preference_val_cmd,
+       "no bgp default local-preference <0-4294967295>",
+       NO_STR
+       "BGP specific commands\n"
+       "Configure BGP defaults\n"
+       "local preference (higher=more preferred)\n"
+       "Configure default local preference value\n")
+
+static void
+peer_announce_routes_if_rmap_out (struct bgp *bgp)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  struct bgp_filter *filter;
+  afi_t afi;
+  safi_t safi;
+
+  /* Reannounce all routes to appropriate neighbors */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      for (afi = AFI_IP; afi < AFI_MAX; afi++)
+       for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+         {
+           if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+             {
+               /* check if there's an out route-map on this client */
+               filter = &peer->filter[afi][safi];
+               if (ROUTE_MAP_OUT_NAME(filter))
+                 {
+                   if (BGP_DEBUG(update, UPDATE_OUT))
+                     zlog_debug("%s: Announcing routes again for peer %s"
+                                "(afi=%d, safi=%d", __func__, peer->host, afi,
+                                safi);
+
+                   bgp_announce_route_all(peer);
+                 }
+             }
+       }
+    }
+}
+
+DEFUN (bgp_rr_allow_outbound_policy,
+       bgp_rr_allow_outbound_policy_cmd,
+       "bgp route-reflector allow-outbound-policy",
+       "BGP specific commands\n"
+       "Allow modifications made by out route-map\n"
+       "on ibgp neighbors\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  if (!bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY))
+    {
+      bgp_flag_set(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY);
+      peer_announce_routes_if_rmap_out(bgp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_rr_allow_outbound_policy,
+       no_bgp_rr_allow_outbound_policy_cmd,
+       "no bgp route-reflector allow-outbound-policy",
+       NO_STR
+       "BGP specific commands\n"
+       "Allow modifications made by out route-map\n"
+       "on ibgp neighbors\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY))
+    {
+      bgp_flag_unset(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY);
+      peer_announce_routes_if_rmap_out(bgp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+peer_remote_as_vty (struct vty *vty, const char *peer_str, 
+                    const char *as_str, afi_t afi, safi_t safi)
+{
+  int ret;
+  struct bgp *bgp;
+  as_t as;
+  union sockunion su;
+
+  bgp = vty->index;
+
+  /* Get AS number.  */
+  VTY_GET_INTEGER_RANGE ("AS", as, as_str, 1, BGP_AS4_MAX);
+
+  /* If peer is peer group, call proper function.  */
+  ret = str2sockunion (peer_str, &su);
+  if (ret < 0)
+    {
+      ret = peer_group_remote_as (bgp, peer_str, &as);
+      if (ret < 0)
+       {
+         vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      return CMD_SUCCESS;
+    }
+
+  if (peer_address_self_check (&su))
+    {
+      vty_out (vty, "%% Can not configure the local system as neighbor%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = peer_remote_as (bgp, &su, &as, afi, safi);
+
+  /* This peer belongs to peer group.  */
+  switch (ret)
+    {
+    case BGP_ERR_PEER_GROUP_MEMBER:
+      vty_out (vty, "%% Peer-group AS %u. Cannot configure remote-as for member%s", as, VTY_NEWLINE);
+      return CMD_WARNING;
+    case BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT:
+      vty_out (vty, "%% The AS# can not be changed from %u to %s, peer-group members must be all internal or all external%s", as, as_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_remote_as,
+       neighbor_remote_as_cmd,
+       NEIGHBOR_CMD2 "remote-as " CMD_AS_RANGE,
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a BGP neighbor\n"
+       AS_STR)
+{
+  return peer_remote_as_vty (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (neighbor_peer_group,
+       neighbor_peer_group_cmd,
+       "neighbor WORD peer-group",
+       NEIGHBOR_STR
+       "Neighbor tag\n"
+       "Configure peer-group\n")
+{
+  struct bgp *bgp;
+  struct peer_group *group;
+
+  bgp = vty->index;
+
+  group = peer_group_get (bgp, argv[0]);
+  if (! group)
+    return CMD_WARNING;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor,
+       no_neighbor_cmd,
+       NO_NEIGHBOR_CMD2,
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2)
+{
+  int ret;
+  union sockunion su;
+  struct peer_group *group;
+  struct peer *peer;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      group = peer_group_lookup (vty->index, argv[0]);
+      if (group)
+       peer_group_delete (group);
+      else
+       {
+         vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  else
+    {
+      peer = peer_lookup (vty->index, &su);
+      if (peer)
+        peer_delete (peer);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_neighbor,
+       no_neighbor_remote_as_cmd,
+       NO_NEIGHBOR_CMD "remote-as " CMD_AS_RANGE,
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Specify a BGP neighbor\n"
+       AS_STR)
+
+DEFUN (no_neighbor_peer_group,
+       no_neighbor_peer_group_cmd,
+       "no neighbor WORD peer-group",
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor tag\n"
+       "Configure peer-group\n")
+{
+  struct peer_group *group;
+
+  group = peer_group_lookup (vty->index, argv[0]);
+  if (group)
+    peer_group_delete (group);
+  else
+    {
+      vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_peer_group_remote_as,
+       no_neighbor_peer_group_remote_as_cmd,
+       "no neighbor WORD remote-as " CMD_AS_RANGE,
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor tag\n"
+       "Specify a BGP neighbor\n"
+       AS_STR)
+{
+  struct peer_group *group;
+
+  group = peer_group_lookup (vty->index, argv[0]);
+  if (group)
+    peer_group_remote_as_delete (group);
+  else
+    {
+      vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (neighbor_local_as,
+       neighbor_local_as_cmd,
+       NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE,
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_local_as_set (peer, atoi (argv[1]), 0, 0);
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_local_as_no_prepend,
+       neighbor_local_as_no_prepend_cmd,
+       NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n"
+       "Do not prepend local-as to updates from ebgp peers\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_local_as_set (peer, atoi (argv[1]), 1, 0);
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_local_as_no_prepend_replace_as,
+       neighbor_local_as_no_prepend_replace_as_cmd,
+       NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n"
+       "Do not prepend local-as to updates from ebgp peers\n"
+       "Do not prepend local-as to updates from ibgp peers\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_local_as_set (peer, atoi (argv[1]), 1, 1);
+  return bgp_vty_return (vty, ret);
+}
+
+
+DEFUN (no_neighbor_local_as,
+       no_neighbor_local_as_cmd,
+       NO_NEIGHBOR_CMD2 "local-as",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_local_as_unset (peer);
+  return bgp_vty_return (vty, ret);
+}
+
+ALIAS (no_neighbor_local_as,
+       no_neighbor_local_as_val_cmd,
+       NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE,
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n")
+
+ALIAS (no_neighbor_local_as,
+       no_neighbor_local_as_val2_cmd,
+       NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n"
+       "Do not prepend local-as to updates from ebgp peers\n")
+
+ALIAS (no_neighbor_local_as,
+       no_neighbor_local_as_val3_cmd,
+       NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify a local-as number\n"
+       "AS number used as local AS\n"
+       "Do not prepend local-as to updates from ebgp peers\n"
+       "Do not prepend local-as to updates from ibgp peers\n")
+
+DEFUN (neighbor_password,
+       neighbor_password_cmd,
+       NEIGHBOR_CMD2 "password LINE",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Set a password\n"
+       "The password\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_password_set (peer, argv[1]);
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (no_neighbor_password,
+       no_neighbor_password_cmd,
+       NO_NEIGHBOR_CMD2 "password",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Set a password\n")
+{
+  struct peer *peer;
+  int ret;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_password_unset (peer);
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_activate,
+       neighbor_activate_cmd,
+       NEIGHBOR_CMD2 "activate",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Enable the Address Family for this Neighbor\n")
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  peer_activate (peer, bgp_node_afi (vty), bgp_node_safi (vty));
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_activate,
+       no_neighbor_activate_cmd,
+       NO_NEIGHBOR_CMD2 "activate",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Enable the Address Family for this Neighbor\n")
+{
+  int ret;
+  struct peer *peer;
+
+  /* Lookup peer. */
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_deactivate (peer, bgp_node_afi (vty), bgp_node_safi (vty));
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_set_peer_group,
+       neighbor_set_peer_group_cmd,
+       NEIGHBOR_CMD "peer-group WORD",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Member of the peer-group\n"
+       "peer-group name\n")
+{
+  int ret;
+  as_t as;
+  union sockunion su;
+  struct bgp *bgp;
+  struct peer_group *group;
+
+  bgp = vty->index;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  group = peer_group_lookup (bgp, argv[1]);
+  if (! group)
+    {
+      vty_out (vty, "%% Configure the peer-group first%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (peer_address_self_check (&su))
+    {
+      vty_out (vty, "%% Can not configure the local system as neighbor%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = peer_group_bind (bgp, &su, group, bgp_node_afi (vty), 
+                        bgp_node_safi (vty), &as);
+
+  if (ret == BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT)
+    {
+      vty_out (vty, "%% Peer with AS %u cannot be in this peer-group, members must be all internal or all external%s", as, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (no_neighbor_set_peer_group,
+       no_neighbor_set_peer_group_cmd,
+       NO_NEIGHBOR_CMD "peer-group WORD",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Member of the peer-group\n"
+       "peer-group name\n")
+{
+  int ret;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+
+  bgp = vty->index;
+
+  peer = peer_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  group = peer_group_lookup (bgp, argv[1]);
+  if (! group)
+    {
+      vty_out (vty, "%% Configure the peer-group first%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = peer_group_unbind (bgp, peer, group, bgp_node_afi (vty),
+                          bgp_node_safi (vty));
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_flag_modify_vty (struct vty *vty, const char *ip_str, 
+                      u_int16_t flag, int set)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (set)
+    ret = peer_flag_set (peer, flag);
+  else
+    ret = peer_flag_unset (peer, flag);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_flag_set_vty (struct vty *vty, const char *ip_str, u_int16_t flag)
+{
+  return peer_flag_modify_vty (vty, ip_str, flag, 1);
+}
+
+static int
+peer_flag_unset_vty (struct vty *vty, const char *ip_str, u_int16_t flag)
+{
+  return peer_flag_modify_vty (vty, ip_str, flag, 0);
+}
+
+/* neighbor passive. */
+DEFUN (neighbor_passive,
+       neighbor_passive_cmd,
+       NEIGHBOR_CMD2 "passive",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Don't send open messages to this neighbor\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_PASSIVE);
+}
+
+DEFUN (no_neighbor_passive,
+       no_neighbor_passive_cmd,
+       NO_NEIGHBOR_CMD2 "passive",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Don't send open messages to this neighbor\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE);
+}
+
+/* neighbor shutdown. */
+DEFUN (neighbor_shutdown,
+       neighbor_shutdown_cmd,
+       NEIGHBOR_CMD2 "shutdown",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Administratively shut down this neighbor\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_SHUTDOWN);
+}
+
+DEFUN (no_neighbor_shutdown,
+       no_neighbor_shutdown_cmd,
+       NO_NEIGHBOR_CMD2 "shutdown",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Administratively shut down this neighbor\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_SHUTDOWN);
+}
+
+/* Deprecated neighbor capability route-refresh. */
+DEFUN_DEPRECATED (neighbor_capability_route_refresh,
+                 neighbor_capability_route_refresh_cmd,
+                 NEIGHBOR_CMD2 "capability route-refresh",
+                 NEIGHBOR_STR
+                 NEIGHBOR_ADDR_STR2
+                 "Advertise capability to the peer\n"
+                 "Advertise route-refresh capability to this neighbor\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (no_neighbor_capability_route_refresh,
+                 no_neighbor_capability_route_refresh_cmd,
+                 NO_NEIGHBOR_CMD2 "capability route-refresh",
+                 NO_STR
+                 NEIGHBOR_STR
+                 NEIGHBOR_ADDR_STR2
+                 "Advertise capability to the peer\n"
+                 "Advertise route-refresh capability to this neighbor\n")
+{
+  return CMD_SUCCESS;
+}
+
+/* neighbor capability dynamic. */
+DEFUN (neighbor_capability_dynamic,
+       neighbor_capability_dynamic_cmd,
+       NEIGHBOR_CMD2 "capability dynamic",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Advertise capability to the peer\n"
+       "Advertise dynamic capability to this neighbor\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY);
+}
+
+DEFUN (no_neighbor_capability_dynamic,
+       no_neighbor_capability_dynamic_cmd,
+       NO_NEIGHBOR_CMD2 "capability dynamic",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Advertise capability to the peer\n"
+       "Advertise dynamic capability to this neighbor\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY);
+}
+
+/* neighbor dont-capability-negotiate */
+DEFUN (neighbor_dont_capability_negotiate,
+       neighbor_dont_capability_negotiate_cmd,
+       NEIGHBOR_CMD2 "dont-capability-negotiate",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Do not perform capability negotiation\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY);
+}
+
+DEFUN (no_neighbor_dont_capability_negotiate,
+       no_neighbor_dont_capability_negotiate_cmd,
+       NO_NEIGHBOR_CMD2 "dont-capability-negotiate",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Do not perform capability negotiation\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY);
+}
+
+static int
+peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi,
+                        safi_t safi, u_int32_t flag, int set)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, peer_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (set)
+    ret = peer_af_flag_set (peer, afi, safi, flag);
+  else
+    ret = peer_af_flag_unset (peer, afi, safi, flag);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_af_flag_set_vty (struct vty *vty, const char *peer_str, afi_t afi,
+                     safi_t safi, u_int32_t flag)
+{
+  return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 1);
+}
+
+static int
+peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi,
+                       safi_t safi, u_int32_t flag)
+{
+  return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 0);
+}
+
+/* neighbor capability orf prefix-list. */
+DEFUN (neighbor_capability_orf_prefix,
+       neighbor_capability_orf_prefix_cmd,
+       NEIGHBOR_CMD2 "capability orf prefix-list (both|send|receive)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Advertise capability to the peer\n"
+       "Advertise ORF capability to the peer\n"
+       "Advertise prefixlist ORF capability to this neighbor\n"
+       "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+       "Capability to RECEIVE the ORF from this neighbor\n"
+       "Capability to SEND the ORF to this neighbor\n")
+{
+  u_int16_t flag = 0;
+
+  if (strncmp (argv[1], "s", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_SM;
+  else if (strncmp (argv[1], "r", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_RM;
+  else if (strncmp (argv[1], "b", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_SM|PEER_FLAG_ORF_PREFIX_RM;
+  else
+    return CMD_WARNING;
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flag);
+}
+
+DEFUN (no_neighbor_capability_orf_prefix,
+       no_neighbor_capability_orf_prefix_cmd,
+       NO_NEIGHBOR_CMD2 "capability orf prefix-list (both|send|receive)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Advertise capability to the peer\n"
+       "Advertise ORF capability to the peer\n"
+       "Advertise prefixlist ORF capability to this neighbor\n"
+       "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+       "Capability to RECEIVE the ORF from this neighbor\n"
+       "Capability to SEND the ORF to this neighbor\n")
+{
+  u_int16_t flag = 0;
+
+  if (strncmp (argv[1], "s", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_SM;
+  else if (strncmp (argv[1], "r", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_RM;
+  else if (strncmp (argv[1], "b", 1) == 0)
+    flag = PEER_FLAG_ORF_PREFIX_SM|PEER_FLAG_ORF_PREFIX_RM;
+  else
+    return CMD_WARNING;
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty), flag);
+}
+
+/* neighbor next-hop-self. */
+DEFUN (neighbor_nexthop_self,
+       neighbor_nexthop_self_cmd,
+       NEIGHBOR_CMD2 "next-hop-self {all}",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Disable the next hop calculation for this neighbor\n"
+       "Apply also to ibgp-learned routes when acting as a route reflector\n")
+{
+  u_int32_t flags = PEER_FLAG_NEXTHOP_SELF, unset = 0;
+  int rc;
+
+  /* Check if "all" is specified */
+  if (argv[1] != NULL)
+    flags |= PEER_FLAG_NEXTHOP_SELF_ALL;
+  else
+    unset |= PEER_FLAG_NEXTHOP_SELF_ALL;
+
+  rc = peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                            bgp_node_safi (vty), flags);
+  if ( rc == CMD_SUCCESS && unset )
+    rc = peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty), unset);
+  return rc;
+}
+
+DEFUN (no_neighbor_nexthop_self,
+       no_neighbor_nexthop_self_cmd,
+       NO_NEIGHBOR_CMD2 "next-hop-self {all}",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Disable the next hop calculation for this neighbor\n"
+       "Apply also to ibgp-learned routes when acting as a route reflector\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_NEXTHOP_SELF|PEER_FLAG_NEXTHOP_SELF_ALL);
+}
+
+/* neighbor remove-private-AS. */
+DEFUN (neighbor_remove_private_as,
+       neighbor_remove_private_as_cmd,
+       NEIGHBOR_CMD2 "remove-private-AS",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Remove private AS number from outbound updates\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              PEER_FLAG_REMOVE_PRIVATE_AS);
+}
+
+DEFUN (no_neighbor_remove_private_as,
+       no_neighbor_remove_private_as_cmd,
+       NO_NEIGHBOR_CMD2 "remove-private-AS",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Remove private AS number from outbound updates\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_REMOVE_PRIVATE_AS);
+}
+
+/* neighbor send-community. */
+DEFUN (neighbor_send_community,
+       neighbor_send_community_cmd,
+       NEIGHBOR_CMD2 "send-community",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Send Community attribute to this neighbor\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              PEER_FLAG_SEND_COMMUNITY);
+}
+
+DEFUN (no_neighbor_send_community,
+       no_neighbor_send_community_cmd,
+       NO_NEIGHBOR_CMD2 "send-community",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Send Community attribute to this neighbor\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_SEND_COMMUNITY);
+}
+
+/* neighbor send-community extended. */
+DEFUN (neighbor_send_community_type,
+       neighbor_send_community_type_cmd,
+       NEIGHBOR_CMD2 "send-community (both|all|extended|standard|large)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Send Community attribute to this neighbor\n"
+       "Send Standard, Large and Extended Community attributes\n"
+       "Send Standard, Large and Extended Community attributes\n"
+       "Send Extended Community attributes\n"
+       "Send Standard Community attributes\n"
+       "Send Large Community attributes\n")
+{
+  if (strncmp (argv[1], "s", 1) == 0)
+    return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_SEND_COMMUNITY);
+  if (strncmp (argv[1], "e", 1) == 0)
+    return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_SEND_EXT_COMMUNITY);
+  if (strncmp (argv[1], "l", 1) == 0)
+    return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              (PEER_FLAG_SEND_COMMUNITY|
+                               PEER_FLAG_SEND_EXT_COMMUNITY|
+                               PEER_FLAG_SEND_LARGE_COMMUNITY));
+}
+
+DEFUN (no_neighbor_send_community_type,
+       no_neighbor_send_community_type_cmd,
+       NO_NEIGHBOR_CMD2 "send-community (both|all|extended|standard|large)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Send Community attribute to this neighbor\n"
+       "Send Standard, Large and Extended Community attributes\n"
+       "Send Standard, Large and Extended Community attributes\n"
+       "Send Extended Community attributes\n"
+       "Send Standard Community attributes\n"
+       "Send Large Community attributes\n")
+{
+  if (strncmp (argv[1], "s", 1) == 0)
+    return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                  bgp_node_safi (vty),
+                                  PEER_FLAG_SEND_COMMUNITY);
+  if (strncmp (argv[1], "e", 1) == 0)
+    return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                  bgp_node_safi (vty),
+                                  PEER_FLAG_SEND_EXT_COMMUNITY);
+  if (strncmp (argv[1], "l", 1) == 0)
+    return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                  bgp_node_safi (vty),
+                                  PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                (PEER_FLAG_SEND_COMMUNITY |
+                                 PEER_FLAG_SEND_EXT_COMMUNITY|
+                                 PEER_FLAG_SEND_LARGE_COMMUNITY));
+}
+
+/* neighbor soft-reconfig. */
+DEFUN (neighbor_soft_reconfiguration,
+       neighbor_soft_reconfiguration_cmd,
+       NEIGHBOR_CMD2 "soft-reconfiguration inbound",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Per neighbor soft reconfiguration\n"
+       "Allow inbound soft reconfiguration for this neighbor\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0],
+                              bgp_node_afi (vty), bgp_node_safi (vty),
+                              PEER_FLAG_SOFT_RECONFIG);
+}
+
+DEFUN (no_neighbor_soft_reconfiguration,
+       no_neighbor_soft_reconfiguration_cmd,
+       NO_NEIGHBOR_CMD2 "soft-reconfiguration inbound",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Per neighbor soft reconfiguration\n"
+       "Allow inbound soft reconfiguration for this neighbor\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0],
+                                bgp_node_afi (vty), bgp_node_safi (vty),
+                                PEER_FLAG_SOFT_RECONFIG);
+}
+
+DEFUN (neighbor_route_reflector_client,
+       neighbor_route_reflector_client_cmd,
+       NEIGHBOR_CMD2 "route-reflector-client",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure a neighbor as Route Reflector client\n")
+{
+  struct peer *peer;
+
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              PEER_FLAG_REFLECTOR_CLIENT);
+}
+
+DEFUN (no_neighbor_route_reflector_client,
+       no_neighbor_route_reflector_client_cmd,
+       NO_NEIGHBOR_CMD2 "route-reflector-client",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure a neighbor as Route Reflector client\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_REFLECTOR_CLIENT);
+}
+
+static int
+peer_rsclient_set_vty (struct vty *vty, const char *peer_str, 
+                       int afi, int safi)
+{
+  int ret;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+  struct bgp_filter *pfilter;
+  struct bgp_filter *gfilter;
+  int locked_and_added = 0;
+
+  bgp = vty->index;
+
+  peer = peer_and_group_lookup_vty (vty, peer_str);
+  if ( ! peer )
+    return CMD_WARNING;
+
+  /* If it is already a RS-Client, don't do anything. */
+  if ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) )
+    return CMD_SUCCESS;
+
+  if ( ! peer_rsclient_active (peer) )
+    {
+      peer = peer_lock (peer); /* rsclient peer list reference */
+      listnode_add_sort (bgp->rsclient, peer);
+      locked_and_added = 1;
+    }
+
+  ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
+  if (ret < 0)
+    {
+      if (locked_and_added)
+        {
+          listnode_delete (bgp->rsclient, peer);
+          peer_unlock (peer); /* rsclient peer list reference */
+        }
+
+      return bgp_vty_return (vty, ret);
+    }
+
+  peer->rib[afi][safi] = bgp_table_init (afi, safi);
+  peer->rib[afi][safi]->type = BGP_TABLE_RSCLIENT;
+  /* RIB peer reference.  Released when table is free'd in bgp_table_free. */
+  peer->rib[afi][safi]->owner = peer_lock (peer);
+
+  /* Check for existing 'network' and 'redistribute' routes. */
+  bgp_check_local_routes_rsclient (peer, afi, safi);
+
+  /* Check for routes for peers configured with 'soft-reconfiguration'. */
+  bgp_soft_reconfig_rsclient (peer, afi, safi);
+
+  if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+      gfilter = &peer->filter[afi][safi];
+
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+        {
+          pfilter = &peer->filter[afi][safi];
+
+          /* Members of a non-RS-Client group should not be RS-Clients, as that 
+             is checked when the become part of the peer-group */
+          ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
+          if (ret < 0)
+            return bgp_vty_return (vty, ret);
+
+          /* Make peer's RIB point to group's RIB. */
+          peer->rib[afi][safi] = group->conf->rib[afi][safi];
+
+          /* Import policy. */
+          if (pfilter->map[RMAP_IMPORT].name)
+            free (pfilter->map[RMAP_IMPORT].name);
+          if (gfilter->map[RMAP_IMPORT].name)
+            {
+              pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
+              pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
+            }
+          else
+            {
+              pfilter->map[RMAP_IMPORT].name = NULL;
+              pfilter->map[RMAP_IMPORT].map =NULL;
+            }
+
+          /* Export policy. */
+          if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
+            {
+              pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
+              pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
+            }
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, 
+                         int afi, int safi)
+{
+  int ret;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  bgp = vty->index;
+
+  peer = peer_and_group_lookup_vty (vty, peer_str);
+  if ( ! peer )
+    return CMD_WARNING;
+
+  /* If it is not a RS-Client, don't do anything. */
+  if ( ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) )
+    return CMD_SUCCESS;
+
+  if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+        {
+          ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
+          if (ret < 0)
+            return bgp_vty_return (vty, ret);
+
+          peer->rib[afi][safi] = NULL;
+        }
+
+        peer = group->conf;
+    }
+
+  ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT);
+  if (ret < 0)
+    return bgp_vty_return (vty, ret);
+
+  if ( ! peer_rsclient_active (peer) )
+    {
+      bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
+      listnode_delete (bgp->rsclient, peer);
+      peer_unlock (peer); /* peer bgp rsclient reference */
+    }
+
+  bgp_table_finish (&peer->rib[bgp_node_afi(vty)][bgp_node_safi(vty)]);
+
+  return CMD_SUCCESS;
+}
+
+/* neighbor route-server-client. */
+DEFUN (neighbor_route_server_client,
+       neighbor_route_server_client_cmd,
+       NEIGHBOR_CMD2 "route-server-client",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure a neighbor as Route Server client\n")
+{
+  return peer_rsclient_set_vty (vty, argv[0], bgp_node_afi(vty),
+                  bgp_node_safi(vty));
+}
+
+DEFUN (no_neighbor_route_server_client,
+       no_neighbor_route_server_client_cmd,
+       NO_NEIGHBOR_CMD2 "route-server-client",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure a neighbor as Route Server client\n")
+{
+  return peer_rsclient_unset_vty (vty, argv[0], bgp_node_afi(vty),
+                  bgp_node_safi(vty));
+}
+
+DEFUN (neighbor_nexthop_local_unchanged,
+       neighbor_nexthop_local_unchanged_cmd,
+       NEIGHBOR_CMD2 "nexthop-local unchanged",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure treatment of outgoing link-local nexthop attribute\n"
+       "Leave link-local nexthop unchanged for this peer\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED );
+}
+
+DEFUN (no_neighbor_nexthop_local_unchanged,
+       no_neighbor_nexthop_local_unchanged_cmd,
+       NO_NEIGHBOR_CMD2 "nexthop-local unchanged",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Configure treatment of outgoing link-local-nexthop attribute\n"
+       "Leave link-local nexthop unchanged for this peer\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED );
+}
+
+DEFUN (neighbor_attr_unchanged,
+       neighbor_attr_unchanged_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              (PEER_FLAG_AS_PATH_UNCHANGED |
+                               PEER_FLAG_NEXTHOP_UNCHANGED |
+                               PEER_FLAG_MED_UNCHANGED));
+}
+
+DEFUN (neighbor_attr_unchanged1,
+       neighbor_attr_unchanged1_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged (as-path|next-hop|med)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = 0;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+}
+
+DEFUN (neighbor_attr_unchanged2,
+       neighbor_attr_unchanged2_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged as-path (next-hop|med)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_AS_PATH_UNCHANGED;
+
+  if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+
+}
+
+DEFUN (neighbor_attr_unchanged3,
+       neighbor_attr_unchanged3_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged next-hop (as-path|med)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_NEXTHOP_UNCHANGED;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+}
+
+DEFUN (neighbor_attr_unchanged4,
+       neighbor_attr_unchanged4_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged med (as-path|next-hop)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_MED_UNCHANGED;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+}
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged5_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged as-path next-hop med",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged6_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged as-path med next-hop",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Med attribute\n"
+       "Nexthop attribute\n")
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged7_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged next-hop med as-path",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "Med attribute\n"
+       "As-path attribute\n")
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged8_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged next-hop as-path med",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n"
+       "Med attribute\n")
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged9_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged med next-hop as-path",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n")
+
+ALIAS (neighbor_attr_unchanged,
+       neighbor_attr_unchanged10_cmd,
+       NEIGHBOR_CMD2 "attribute-unchanged med as-path next-hop",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n")
+
+DEFUN (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged",
+       NO_STR   
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n")
+{
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty),
+                                (PEER_FLAG_AS_PATH_UNCHANGED |
+                                 PEER_FLAG_NEXTHOP_UNCHANGED |
+                                 PEER_FLAG_MED_UNCHANGED));
+}
+
+DEFUN (no_neighbor_attr_unchanged1,
+       no_neighbor_attr_unchanged1_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged (as-path|next-hop|med)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = 0;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty), flags);
+}
+
+DEFUN (no_neighbor_attr_unchanged2,
+       no_neighbor_attr_unchanged2_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged as-path (next-hop|med)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_AS_PATH_UNCHANGED;
+
+  if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+}
+
+DEFUN (no_neighbor_attr_unchanged3,
+       no_neighbor_attr_unchanged3_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop (as-path|med)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n"
+       "Med attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_NEXTHOP_UNCHANGED;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "med", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED);
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty), flags);
+}
+
+DEFUN (no_neighbor_attr_unchanged4,
+       no_neighbor_attr_unchanged4_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged med (as-path|next-hop)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n")
+{
+  u_int16_t flags = PEER_FLAG_MED_UNCHANGED;
+
+  if (strncmp (argv[1], "as-path", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED);
+  else if (strncmp (argv[1], "next-hop", 1) == 0)
+    SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED);
+
+  return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty), flags);
+}
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged5_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged as-path next-hop med",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n"
+       "Med attribute\n")
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged6_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged as-path med next-hop",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "As-path attribute\n"
+       "Med attribute\n"
+       "Nexthop attribute\n")
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged7_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop med as-path",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "Med attribute\n"
+       "As-path attribute\n")
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged8_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop as-path med",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n"
+       "Med attribute\n")
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged9_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged med next-hop as-path",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "Nexthop attribute\n"
+       "As-path attribute\n")
+
+ALIAS (no_neighbor_attr_unchanged,
+       no_neighbor_attr_unchanged10_cmd,
+       NO_NEIGHBOR_CMD2 "attribute-unchanged med as-path next-hop",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP attribute is propagated unchanged to this neighbor\n"
+       "Med attribute\n"
+       "As-path attribute\n"
+       "Nexthop attribute\n")
+
+/* For old version Zebra compatibility.  */
+DEFUN_DEPRECATED (neighbor_transparent_as,
+                 neighbor_transparent_as_cmd,
+                 NEIGHBOR_CMD "transparent-as",
+                 NEIGHBOR_STR
+                 NEIGHBOR_ADDR_STR
+                 "Do not append my AS number even peer is EBGP peer\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              PEER_FLAG_AS_PATH_UNCHANGED);
+}
+
+DEFUN_DEPRECATED (neighbor_transparent_nexthop,
+                 neighbor_transparent_nexthop_cmd,
+                 NEIGHBOR_CMD "transparent-nexthop",
+                 NEIGHBOR_STR
+                 NEIGHBOR_ADDR_STR
+                 "Do not change nexthop even peer is EBGP peer\n")
+{
+  return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty),
+                              bgp_node_safi (vty),
+                              PEER_FLAG_NEXTHOP_UNCHANGED);
+}
+
+/* EBGP multihop configuration. */
+static int
+peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str, 
+                            const char *ttl_str)
+{
+  struct peer *peer;
+  unsigned int ttl;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! ttl_str)
+    ttl = TTL_MAX;
+  else
+    VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, 255);
+
+  return bgp_vty_return (vty,  peer_ebgp_multihop_set (peer, ttl));
+}
+
+static int
+peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str) 
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, 0));
+}
+
+/* neighbor ebgp-multihop. */
+DEFUN (neighbor_ebgp_multihop,
+       neighbor_ebgp_multihop_cmd,
+       NEIGHBOR_CMD2 "ebgp-multihop",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Allow EBGP neighbors not on directly connected networks\n")
+{
+  return peer_ebgp_multihop_set_vty (vty, argv[0], NULL);
+}
+
+DEFUN (neighbor_ebgp_multihop_ttl,
+       neighbor_ebgp_multihop_ttl_cmd,
+       NEIGHBOR_CMD2 "ebgp-multihop <1-255>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Allow EBGP neighbors not on directly connected networks\n"
+       "maximum hop count\n")
+{
+  return peer_ebgp_multihop_set_vty (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_neighbor_ebgp_multihop,
+       no_neighbor_ebgp_multihop_cmd,
+       NO_NEIGHBOR_CMD2 "ebgp-multihop",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Allow EBGP neighbors not on directly connected networks\n")
+{
+  return peer_ebgp_multihop_unset_vty (vty, argv[0]);
+}
+
+ALIAS (no_neighbor_ebgp_multihop,
+       no_neighbor_ebgp_multihop_ttl_cmd,
+       NO_NEIGHBOR_CMD2 "ebgp-multihop <1-255>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Allow EBGP neighbors not on directly connected networks\n"
+       "maximum hop count\n")
+
+/* disable-connected-check */
+DEFUN (neighbor_disable_connected_check,
+       neighbor_disable_connected_check_cmd,
+       NEIGHBOR_CMD2 "disable-connected-check",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "one-hop away EBGP peer using loopback address\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DISABLE_CONNECTED_CHECK);
+}
+
+DEFUN (no_neighbor_disable_connected_check,
+       no_neighbor_disable_connected_check_cmd,
+       NO_NEIGHBOR_CMD2 "disable-connected-check",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "one-hop away EBGP peer using loopback address\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DISABLE_CONNECTED_CHECK);
+}
+
+/* Enforce multihop.  */
+ALIAS (neighbor_disable_connected_check,
+       neighbor_enforce_multihop_cmd,
+       NEIGHBOR_CMD2 "enforce-multihop",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Enforce EBGP neighbors perform multihop\n")
+
+/* Enforce multihop.  */
+ALIAS (no_neighbor_disable_connected_check,
+       no_neighbor_enforce_multihop_cmd,
+       NO_NEIGHBOR_CMD2 "enforce-multihop",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Enforce EBGP neighbors perform multihop\n")
+
+DEFUN (neighbor_description,
+       neighbor_description_cmd,
+       NEIGHBOR_CMD2 "description .LINE",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Neighbor specific description\n"
+       "Up to 80 characters describing this neighbor\n")
+{
+  struct peer *peer;
+  char *str;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (argc == 1)
+    return CMD_SUCCESS;
+
+  str = argv_concat(argv, argc, 1);
+
+  peer_description_set (peer, str);
+
+  XFREE (MTYPE_TMP, str);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_description,
+       no_neighbor_description_cmd,
+       NO_NEIGHBOR_CMD2 "description",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Neighbor specific description\n")
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  peer_description_unset (peer);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_neighbor_description,
+       no_neighbor_description_val_cmd,
+       NO_NEIGHBOR_CMD2 "description .LINE",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Neighbor specific description\n"
+       "Up to 80 characters describing this neighbor\n")
+
+/* Neighbor update-source. */
+static int
+peer_update_source_vty (struct vty *vty, const char *peer_str, 
+                        const char *source_str)
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, peer_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (source_str)
+    {
+      union sockunion su;
+      int ret = str2sockunion (source_str, &su);
+
+      if (ret == 0)
+       peer_update_source_addr_set (peer, &su);
+      else
+       peer_update_source_if_set (peer, source_str);
+    }
+  else
+    peer_update_source_unset (peer);
+
+  return CMD_SUCCESS;
+}
+
+#define BGP_UPDATE_SOURCE_STR "(A.B.C.D|X:X::X:X|WORD)"
+#define BGP_UPDATE_SOURCE_HELP_STR \
+  "IPv4 address\n" \
+  "IPv6 address\n" \
+  "Interface name (requires zebra to be running)\n"
+
+DEFUN (neighbor_update_source,
+       neighbor_update_source_cmd,
+       NEIGHBOR_CMD2 "update-source " BGP_UPDATE_SOURCE_STR,
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Source of routing updates\n"
+       BGP_UPDATE_SOURCE_HELP_STR)
+{
+  return peer_update_source_vty (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_neighbor_update_source,
+       no_neighbor_update_source_cmd,
+       NO_NEIGHBOR_CMD2 "update-source",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Source of routing updates\n")
+{
+  return peer_update_source_vty (vty, argv[0], NULL);
+}
+
+static int
+peer_default_originate_set_vty (struct vty *vty, const char *peer_str, 
+                                afi_t afi, safi_t safi, 
+                                const char *rmap, int set)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, peer_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (set)
+    ret = peer_default_originate_set (peer, afi, safi, rmap);
+  else
+    ret = peer_default_originate_unset (peer, afi, safi);
+
+  return bgp_vty_return (vty, ret);
+}
+
+/* neighbor default-originate. */
+DEFUN (neighbor_default_originate,
+       neighbor_default_originate_cmd,
+       NEIGHBOR_CMD2 "default-originate",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Originate default route to this neighbor\n")
+{
+  return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                        bgp_node_safi (vty), NULL, 1);
+}
+
+DEFUN (neighbor_default_originate_rmap,
+       neighbor_default_originate_rmap_cmd,
+       NEIGHBOR_CMD2 "default-originate route-map WORD",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Originate default route to this neighbor\n"
+       "Route-map to specify criteria to originate default\n"
+       "route-map name\n")
+{
+  return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                        bgp_node_safi (vty), argv[1], 1);
+}
+
+DEFUN (no_neighbor_default_originate,
+       no_neighbor_default_originate_cmd,
+       NO_NEIGHBOR_CMD2 "default-originate",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Originate default route to this neighbor\n")
+{
+  return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                        bgp_node_safi (vty), NULL, 0);
+}
+
+ALIAS (no_neighbor_default_originate,
+       no_neighbor_default_originate_rmap_cmd,
+       NO_NEIGHBOR_CMD2 "default-originate route-map WORD",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Originate default route to this neighbor\n"
+       "Route-map to specify criteria to originate default\n"
+       "route-map name\n")
+
+/* Set neighbor's BGP port.  */
+static int
+peer_port_vty (struct vty *vty, const char *ip_str, int afi, 
+               const char *port_str)
+{
+  struct peer *peer;
+  u_int16_t port;
+  struct servent *sp;
+
+  peer = peer_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (! port_str)
+    { 
+      sp = getservbyname ("bgp", "tcp");
+      port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
+    }
+  else
+    {
+      VTY_GET_INTEGER("port", port, port_str);
+    }
+
+  peer_port_set (peer, port);
+
+  return CMD_SUCCESS;
+}
+
+/* Set specified peer's BGP port.  */
+DEFUN (neighbor_port,
+       neighbor_port_cmd,
+       NEIGHBOR_CMD "port <0-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Neighbor's BGP port\n"
+       "TCP port number\n")
+{
+  return peer_port_vty (vty, argv[0], AFI_IP, argv[1]);
+}
+
+DEFUN (no_neighbor_port,
+       no_neighbor_port_cmd,
+       NO_NEIGHBOR_CMD "port",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Neighbor's BGP port\n")
+{
+  return peer_port_vty (vty, argv[0], AFI_IP, NULL);
+}
+
+ALIAS (no_neighbor_port,
+       no_neighbor_port_val_cmd,
+       NO_NEIGHBOR_CMD "port <0-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Neighbor's BGP port\n"
+       "TCP port number\n")
+
+/* neighbor weight. */
+static int
+peer_weight_set_vty (struct vty *vty, const char *ip_str, 
+                     const char *weight_str)
+{
+  struct peer *peer;
+  unsigned long weight;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER_RANGE("weight", weight, weight_str, 0, 65535);
+
+  return bgp_vty_return (vty, peer_weight_set (peer, weight));
+}
+
+static int
+peer_weight_unset_vty (struct vty *vty, const char *ip_str)
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_vty_return (vty, peer_weight_unset (peer));
+}
+
+DEFUN (neighbor_weight,
+       neighbor_weight_cmd,
+       NEIGHBOR_CMD2 "weight <0-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Set default weight for routes from this neighbor\n"
+       "default weight\n")
+{
+  return peer_weight_set_vty (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_neighbor_weight,
+       no_neighbor_weight_cmd,
+       NO_NEIGHBOR_CMD2 "weight",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Set default weight for routes from this neighbor\n")
+{
+  return peer_weight_unset_vty (vty, argv[0]);
+}
+
+ALIAS (no_neighbor_weight,
+       no_neighbor_weight_val_cmd,
+       NO_NEIGHBOR_CMD2 "weight <0-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Set default weight for routes from this neighbor\n"
+       "default weight\n")
+
+/* Override capability negotiation. */
+DEFUN (neighbor_override_capability,
+       neighbor_override_capability_cmd,
+       NEIGHBOR_CMD2 "override-capability",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Override capability negotiation result\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY);
+}
+
+DEFUN (no_neighbor_override_capability,
+       no_neighbor_override_capability_cmd,
+       NO_NEIGHBOR_CMD2 "override-capability",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Override capability negotiation result\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY);
+}
+
+DEFUN (neighbor_strict_capability,
+       neighbor_strict_capability_cmd,
+       NEIGHBOR_CMD "strict-capability-match",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Strict capability negotiation match\n")
+{
+  return peer_flag_set_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH);
+}
+
+DEFUN (no_neighbor_strict_capability,
+       no_neighbor_strict_capability_cmd,
+       NO_NEIGHBOR_CMD "strict-capability-match",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Strict capability negotiation match\n")
+{
+  return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH);
+}
+
+static int
+peer_timers_set_vty (struct vty *vty, const char *ip_str, 
+                     const char *keep_str, const char *hold_str)
+{
+  int ret;
+  struct peer *peer;
+  u_int32_t keepalive;
+  u_int32_t holdtime;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER_RANGE ("Keepalive", keepalive, keep_str, 0, 65535);
+  VTY_GET_INTEGER_RANGE ("Holdtime", holdtime, hold_str, 0, 65535);
+
+  ret = peer_timers_set (peer, keepalive, holdtime);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_timers_unset_vty (struct vty *vty, const char *ip_str)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_timers_unset (peer);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_timers,
+       neighbor_timers_cmd,
+       NEIGHBOR_CMD2 "timers <0-65535> <0-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP per neighbor timers\n"
+       "Keepalive interval\n"
+       "Holdtime\n")
+{
+  return peer_timers_set_vty (vty, argv[0], argv[1], argv[2]);
+}
+
+DEFUN (no_neighbor_timers,
+       no_neighbor_timers_cmd,
+       NO_NEIGHBOR_CMD2 "timers",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP per neighbor timers\n")
+{
+  return peer_timers_unset_vty (vty, argv[0]);
+}
+
+static int
+peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, 
+                             const char *time_str)
+{
+  struct peer *peer;
+  u_int32_t connect;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER_RANGE ("Connect time", connect, time_str, 0, 65535);
+
+  return bgp_vty_return (vty, peer_timers_connect_set (peer, connect));
+}
+
+static int
+peer_timers_connect_unset_vty (struct vty *vty, const char *ip_str)
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_vty_return (vty, peer_timers_connect_unset (peer));
+}
+
+DEFUN (neighbor_timers_connect,
+       neighbor_timers_connect_cmd,
+       NEIGHBOR_CMD2 "timers connect <1-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP per neighbor timers\n"
+       "BGP connect timer\n"
+       "Connect timer\n")
+{
+  return peer_timers_connect_set_vty (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_neighbor_timers_connect,
+       no_neighbor_timers_connect_cmd,
+       NO_NEIGHBOR_CMD2 "timers connect",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP per neighbor timers\n"
+       "BGP connect timer\n")
+{
+  return peer_timers_connect_unset_vty (vty, argv[0]);
+}
+
+ALIAS (no_neighbor_timers_connect,
+       no_neighbor_timers_connect_val_cmd,
+       NO_NEIGHBOR_CMD2 "timers connect <1-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "BGP per neighbor timers\n"
+       "BGP connect timer\n"
+       "Connect timer\n")
+
+static int
+peer_advertise_interval_vty (struct vty *vty, const char *ip_str, 
+                             const char *time_str, int set)  
+{
+  int ret;
+  struct peer *peer;
+  u_int32_t routeadv = 0;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (time_str)
+    VTY_GET_INTEGER_RANGE ("advertise interval", routeadv, time_str, 0, 600);
+
+  if (set)
+    ret = peer_advertise_interval_set (peer, routeadv);
+  else
+    ret = peer_advertise_interval_unset (peer);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_advertise_interval,
+       neighbor_advertise_interval_cmd,
+       NEIGHBOR_CMD2 "advertisement-interval <0-600>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Minimum interval between sending BGP routing updates\n"
+       "time in seconds\n")
+{
+  return peer_advertise_interval_vty (vty, argv[0], argv[1], 1);
+}
+
+DEFUN (no_neighbor_advertise_interval,
+       no_neighbor_advertise_interval_cmd,
+       NO_NEIGHBOR_CMD2 "advertisement-interval",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Minimum interval between sending BGP routing updates\n")
+{
+  return peer_advertise_interval_vty (vty, argv[0], NULL, 0);
+}
+
+ALIAS (no_neighbor_advertise_interval,
+       no_neighbor_advertise_interval_val_cmd,
+       NO_NEIGHBOR_CMD2 "advertisement-interval <0-600>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Minimum interval between sending BGP routing updates\n"
+       "time in seconds\n")
+
+/* neighbor interface */
+static int
+peer_interface_vty (struct vty *vty, const char *ip_str, const char *str)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (str)
+    ret = peer_interface_set (peer, str);
+  else
+    ret = peer_interface_unset (peer);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_interface,
+       neighbor_interface_cmd,
+       NEIGHBOR_CMD "interface WORD",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Interface\n"
+       "Interface name\n")
+{
+  return peer_interface_vty (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_neighbor_interface,
+       no_neighbor_interface_cmd,
+       NO_NEIGHBOR_CMD "interface WORD",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR
+       "Interface\n"
+       "Interface name\n")
+{
+  return peer_interface_vty (vty, argv[0], NULL);
+}
+
+/* Set distribute list to the peer. */
+static int
+peer_distribute_set_vty (struct vty *vty, const char *ip_str, 
+                         afi_t afi, safi_t safi,
+                        const char *name_str, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_distribute_set (peer, afi, safi, direct, name_str);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_distribute_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                          safi_t safi, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_distribute_unset (peer, afi, safi, direct);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_distribute_list,
+       neighbor_distribute_list_cmd,
+       NEIGHBOR_CMD2 "distribute-list (<1-199>|<1300-2699>|WORD) (in|out)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Filter updates to/from this neighbor\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n"
+       "Filter incoming updates\n"
+       "Filter outgoing updates\n")
+{
+  return peer_distribute_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                 bgp_node_safi (vty), argv[1], argv[2]);
+}
+
+DEFUN (no_neighbor_distribute_list,
+       no_neighbor_distribute_list_cmd,
+       NO_NEIGHBOR_CMD2 "distribute-list (<1-199>|<1300-2699>|WORD) (in|out)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Filter updates to/from this neighbor\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n"
+       "Filter incoming updates\n"
+       "Filter outgoing updates\n")
+{
+  return peer_distribute_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                   bgp_node_safi (vty), argv[2]);
+}
+
+/* Set prefix list to the peer. */
+static int
+peer_prefix_list_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                         safi_t safi, const char *name_str, 
+                          const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_prefix_list_set (peer, afi, safi, direct, name_str);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_prefix_list_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                           safi_t safi, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+  
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_prefix_list_unset (peer, afi, safi, direct);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_prefix_list,
+       neighbor_prefix_list_cmd,
+       NEIGHBOR_CMD2 "prefix-list WORD (in|out)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Filter updates to/from this neighbor\n"
+       "Name of a prefix list\n"
+       "Filter incoming updates\n"
+       "Filter outgoing updates\n")
+{
+  return peer_prefix_list_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                  bgp_node_safi (vty), argv[1], argv[2]);
+}
+
+DEFUN (no_neighbor_prefix_list,
+       no_neighbor_prefix_list_cmd,
+       NO_NEIGHBOR_CMD2 "prefix-list WORD (in|out)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Filter updates to/from this neighbor\n"
+       "Name of a prefix list\n"
+       "Filter incoming updates\n"
+       "Filter outgoing updates\n")
+{
+  return peer_prefix_list_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                    bgp_node_safi (vty), argv[2]);
+}
+
+static int
+peer_aslist_set_vty (struct vty *vty, const char *ip_str, 
+                     afi_t afi, safi_t safi,
+                    const char *name_str, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_aslist_set (peer, afi, safi, direct, name_str);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_aslist_unset_vty (struct vty *vty, const char *ip_str, 
+                       afi_t afi, safi_t safi,
+                      const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = FILTER_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = FILTER_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = FILTER_OUT;
+
+  ret = peer_aslist_unset (peer, afi, safi, direct);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_filter_list,
+       neighbor_filter_list_cmd,
+       NEIGHBOR_CMD2 "filter-list WORD (in|out)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Establish BGP filters\n"
+       "AS path access-list name\n"
+       "Filter incoming routes\n"
+       "Filter outgoing routes\n")
+{
+  return peer_aslist_set_vty (vty, argv[0], bgp_node_afi (vty),
+                             bgp_node_safi (vty), argv[1], argv[2]);
+}
+
+DEFUN (no_neighbor_filter_list,
+       no_neighbor_filter_list_cmd,
+       NO_NEIGHBOR_CMD2 "filter-list WORD (in|out)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Establish BGP filters\n"
+       "AS path access-list name\n"
+       "Filter incoming routes\n"
+       "Filter outgoing routes\n")
+{
+  return peer_aslist_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                               bgp_node_safi (vty), argv[2]);
+}
+
+/* Set route-map to the peer. */
+static int
+peer_route_map_set_vty (struct vty *vty, const char *ip_str, 
+                        afi_t afi, safi_t safi,
+                       const char *name_str, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = RMAP_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "in", 2) == 0)
+    direct = RMAP_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RMAP_OUT;
+  else if (strncmp (direct_str, "im", 2) == 0)
+    direct = RMAP_IMPORT;
+  else if (strncmp (direct_str, "e", 1) == 0)
+    direct = RMAP_EXPORT;
+
+  ret = peer_route_map_set (peer, afi, safi, direct, name_str);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_route_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                         safi_t safi, const char *direct_str)
+{
+  int ret;
+  struct peer *peer;
+  int direct = RMAP_IN;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  /* Check filter direction. */
+  if (strncmp (direct_str, "in", 2) == 0)
+    direct = RMAP_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RMAP_OUT;
+  else if (strncmp (direct_str, "im", 2) == 0)
+    direct = RMAP_IMPORT;
+  else if (strncmp (direct_str, "e", 1) == 0)
+    direct = RMAP_EXPORT;
+
+  ret = peer_route_map_unset (peer, afi, safi, direct);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_route_map,
+       neighbor_route_map_cmd,
+       NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Apply route map to neighbor\n"
+       "Name of route map\n"
+       "Apply map to incoming routes\n"
+       "Apply map to outbound routes\n"
+       "Apply map to routes going into a Route-Server client's table\n"
+       "Apply map to routes coming from a Route-Server client")
+{
+  return peer_route_map_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                bgp_node_safi (vty), argv[1], argv[2]);
+}
+
+DEFUN (no_neighbor_route_map,
+       no_neighbor_route_map_cmd,
+       NO_NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Apply route map to neighbor\n"
+       "Name of route map\n"
+       "Apply map to incoming routes\n"
+       "Apply map to outbound routes\n"
+       "Apply map to routes going into a Route-Server client's table\n"
+       "Apply map to routes coming from a Route-Server client")
+{
+  return peer_route_map_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                  bgp_node_safi (vty), argv[2]);
+}
+
+/* Set unsuppress-map to the peer. */
+static int
+peer_unsuppress_map_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                            safi_t safi, const char *name_str)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_unsuppress_map_set (peer, afi, safi, name_str);
+
+  return bgp_vty_return (vty, ret);
+}
+
+/* Unset route-map from the peer. */
+static int
+peer_unsuppress_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                              safi_t safi)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_unsuppress_map_unset (peer, afi, safi);
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_unsuppress_map,
+       neighbor_unsuppress_map_cmd,
+       NEIGHBOR_CMD2 "unsuppress-map WORD",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Route-map to selectively unsuppress suppressed routes\n"
+       "Name of route map\n")
+{
+  return peer_unsuppress_map_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1]);
+}
+
+DEFUN (no_neighbor_unsuppress_map,
+       no_neighbor_unsuppress_map_cmd,
+       NO_NEIGHBOR_CMD2 "unsuppress-map WORD",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Route-map to selectively unsuppress suppressed routes\n"
+       "Name of route map\n")
+{
+  return peer_unsuppress_map_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                       bgp_node_safi (vty));
+}
+
+static int
+peer_maximum_prefix_set_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                            safi_t safi, const char *num_str,  
+                            const char *threshold_str, int warning,
+                            const char *restart_str)
+{
+  int ret;
+  struct peer *peer;
+  u_int32_t max;
+  u_char threshold;
+  u_int16_t restart;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("maximum number", max, num_str);
+  if (threshold_str)
+    threshold = atoi (threshold_str);
+  else
+    threshold = MAXIMUM_PREFIX_THRESHOLD_DEFAULT;
+
+  if (restart_str)
+    restart = atoi (restart_str);
+  else
+    restart = 0;
+
+  ret = peer_maximum_prefix_set (peer, afi, safi, max, threshold, warning, restart);
+
+  return bgp_vty_return (vty, ret);
+}
+
+static int
+peer_maximum_prefix_unset_vty (struct vty *vty, const char *ip_str, afi_t afi,
+                              safi_t safi)
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, ip_str);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_maximum_prefix_unset (peer, afi, safi);
+
+  return bgp_vty_return (vty, ret);
+}
+
+/* Maximum number of prefix configuration.  prefix count is different
+   for each peer configuration.  So this configuration can be set for
+   each peer configuration. */
+DEFUN (neighbor_maximum_prefix,
+       neighbor_maximum_prefix_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], NULL, 0,
+                                     NULL);
+}
+
+DEFUN (neighbor_maximum_prefix_threshold,
+       neighbor_maximum_prefix_threshold_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], argv[2], 0,
+                                     NULL);
+}
+
+DEFUN (neighbor_maximum_prefix_warning,
+       neighbor_maximum_prefix_warning_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Only give warning message when limit is exceeded\n")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], NULL, 1,
+                                     NULL);
+}
+
+DEFUN (neighbor_maximum_prefix_threshold_warning,
+       neighbor_maximum_prefix_threshold_warning_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> warning-only",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n"
+       "Only give warning message when limit is exceeded\n")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], argv[2], 1, NULL);
+}
+
+DEFUN (neighbor_maximum_prefix_restart,
+       neighbor_maximum_prefix_restart_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> restart <1-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Restart bgp connection after limit is exceeded\n"
+       "Restart interval in minutes")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], NULL, 0, argv[2]);
+}
+
+DEFUN (neighbor_maximum_prefix_threshold_restart,
+       neighbor_maximum_prefix_threshold_restart_cmd,
+       NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> restart <1-65535>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n"
+       "Restart bgp connection after limit is exceeded\n"
+       "Restart interval in minutes")
+{
+  return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty),
+                                     bgp_node_safi (vty), argv[1], argv[2], 0, argv[3]);
+}
+
+DEFUN (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n")
+{
+  return peer_maximum_prefix_unset_vty (vty, argv[0], bgp_node_afi (vty),
+                                       bgp_node_safi (vty));
+}
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_val_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n")
+
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_threshold_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n")
+
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_warning_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Only give warning message when limit is exceeded\n")
+
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_threshold_warning_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> warning-only",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n"
+       "Only give warning message when limit is exceeded\n")
+
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_restart_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> restart <1-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Restart bgp connection after limit is exceeded\n"
+       "Restart interval in minutes")
+
+ALIAS (no_neighbor_maximum_prefix,
+       no_neighbor_maximum_prefix_threshold_restart_cmd,
+       NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> restart <1-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Maximum number of prefix accept from this peer\n"
+       "maximum no. of prefix limit\n"
+       "Threshold value (%) at which to generate a warning msg\n"
+       "Restart bgp connection after limit is exceeded\n"
+       "Restart interval in minutes")
+
+/* "neighbor allowas-in" */
+DEFUN (neighbor_allowas_in,
+       neighbor_allowas_in_cmd,
+       NEIGHBOR_CMD2 "allowas-in",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Accept as-path with my AS present in it\n")
+{
+  int ret;
+  struct peer *peer;
+  unsigned int allow_num;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  if (argc == 1)
+    allow_num = 3;
+  else
+    VTY_GET_INTEGER_RANGE ("AS number", allow_num, argv[1], 1, 10);
+
+  ret = peer_allowas_in_set (peer, bgp_node_afi (vty), bgp_node_safi (vty),
+                            allow_num);
+
+  return bgp_vty_return (vty, ret);
+}
+
+ALIAS (neighbor_allowas_in,
+       neighbor_allowas_in_arg_cmd,
+       NEIGHBOR_CMD2 "allowas-in <1-10>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Accept as-path with my AS present in it\n"
+       "Number of occurances of AS number\n")
+
+DEFUN (no_neighbor_allowas_in,
+       no_neighbor_allowas_in_cmd,
+       NO_NEIGHBOR_CMD2 "allowas-in",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "allow local ASN appears in aspath attribute\n")
+{
+  int ret;
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  ret = peer_allowas_in_unset (peer, bgp_node_afi (vty), bgp_node_safi (vty));
+
+  return bgp_vty_return (vty, ret);
+}
+
+DEFUN (neighbor_ttl_security,
+       neighbor_ttl_security_cmd,
+       NEIGHBOR_CMD2 "ttl-security hops <1-254>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify the maximum number of hops to the BGP peer\n")
+{
+  struct peer *peer;
+  int gtsm_hops;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+    
+  VTY_GET_INTEGER_RANGE ("", gtsm_hops, argv[1], 1, 254);
+
+  return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, gtsm_hops));
+}
+
+DEFUN (no_neighbor_ttl_security,
+       no_neighbor_ttl_security_cmd,
+       NO_NEIGHBOR_CMD2 "ttl-security hops <1-254>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Specify the maximum number of hops to the BGP peer\n")
+{
+  struct peer *peer;
+
+  peer = peer_and_group_lookup_vty (vty, argv[0]);
+  if (! peer)
+    return CMD_WARNING;
+
+  return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, 0));
+}
+
+/* Address family configuration.  */
+DEFUN (address_family_ipv4,
+       address_family_ipv4_cmd,
+       "address-family ipv4",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_IPV4_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (address_family_ipv4_safi,
+       address_family_ipv4_safi_cmd,
+       "address-family ipv4 (unicast|multicast)",
+       "Enter Address Family command mode\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    vty->node = BGP_IPV4M_NODE;
+  else
+    vty->node = BGP_IPV4_NODE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (address_family_ipv6,
+       address_family_ipv6_cmd,
+       "address-family ipv6",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_IPV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (address_family_ipv6_safi,
+       address_family_ipv6_safi_cmd,
+       "address-family ipv6 (unicast|multicast)",
+       "Enter Address Family command mode\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    vty->node = BGP_IPV6M_NODE;
+  else
+    vty->node = BGP_IPV6_NODE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (address_family_vpnv4,
+       address_family_vpnv4_cmd,
+       "address-family vpnv4",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_VPNV4_NODE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (address_family_vpnv4,
+       address_family_vpnv4_unicast_cmd,
+       "address-family vpnv4 unicast",
+       "Enter Address Family command mode\n"
+       "Address family\n"
+       "Address Family Modifier\n")
+
+DEFUN (address_family_vpnv6,
+       address_family_vpnv6_cmd,
+       "address-family vpnv6",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_VPNV6_NODE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (address_family_vpnv6,
+       address_family_vpnv6_unicast_cmd,
+       "address-family vpnv6 unicast",
+       "Enter Address Family command mode\n"
+       "Address family\n"
+       "Address Family Modifier\n")
+
+DEFUN (address_family_encap,
+       address_family_encap_cmd,
+       "address-family encap",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_ENCAP_NODE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (address_family_encap,
+       address_family_encapv4_cmd,
+       "address-family encapv4",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+
+DEFUN (address_family_encapv6,
+       address_family_encapv6_cmd,
+       "address-family encapv6",
+       "Enter Address Family command mode\n"
+       "Address family\n")
+{
+  vty->node = BGP_ENCAPV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (exit_address_family,
+       exit_address_family_cmd,
+       "exit-address-family",
+       "Exit from Address Family configuration mode\n")
+{
+  /* should match list in command.c:config_exit */
+  if (vty->node == BGP_IPV4_NODE
+      || vty->node == BGP_ENCAP_NODE
+      || vty->node == BGP_ENCAPV6_NODE
+      || vty->node == BGP_IPV4M_NODE
+      || vty->node == BGP_VPNV4_NODE
+      || vty->node == BGP_VPNV6_NODE
+      || vty->node == BGP_IPV6_NODE
+      || vty->node == BGP_IPV6M_NODE)
+    vty->node = BGP_NODE;
+  return CMD_SUCCESS;
+}
+
+/* BGP clear sort. */
+enum clear_sort
+{
+  clear_all,
+  clear_peer,
+  clear_group,
+  clear_external,
+  clear_as
+};
+
+static void
+bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi,
+                    safi_t safi, int error)
+{
+  switch (error)
+    {
+    case BGP_ERR_AF_UNCONFIGURED:
+      vty_out (vty,
+              "%%BGP: Enable %s %s address family for the neighbor %s%s",
+              afi == AFI_IP6 ? "IPv6" : safi == SAFI_MPLS_VPN ? "VPNv4" : "IPv4",
+              safi == SAFI_MULTICAST ? "Multicast" : "Unicast",
+              peer->host, VTY_NEWLINE);
+      break;
+    case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:
+      vty_out (vty, "%%BGP: Inbound soft reconfig for %s not possible as it%s      has neither refresh capability, nor inbound soft reconfig%s", peer->host, VTY_NEWLINE, VTY_NEWLINE);
+      break;
+    default:
+      break;
+    }
+}
+
+/* `clear ip bgp' functions. */
+static int
+bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, safi_t safi,
+           enum clear_sort sort,enum bgp_clear_type stype, const char *arg)
+{
+  int ret;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  /* Clear all neighbors. */
+  if (sort == clear_all)
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (stype == BGP_CLEAR_SOFT_NONE)
+           ret = peer_clear (peer);
+         else
+           ret = peer_clear_soft (peer, afi, safi, stype);
+
+         if (ret < 0)
+           bgp_clear_vty_error (vty, peer, afi, safi, ret);
+       }
+      return CMD_SUCCESS;
+    }
+
+  /* Clear specified neighbors. */
+  if (sort == clear_peer)
+    {
+      union sockunion su;
+      int ret;
+
+      /* Make sockunion for lookup. */
+      ret = str2sockunion (arg, &su);
+      if (ret < 0)
+       {
+         vty_out (vty, "Malformed address: %s%s", arg, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      peer = peer_lookup (bgp, &su);
+      if (! peer)
+       {
+         vty_out (vty, "%%BGP: Unknown neighbor - \"%s\"%s", arg, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      if (stype == BGP_CLEAR_SOFT_NONE)
+       ret = peer_clear (peer);
+      else
+       ret = peer_clear_soft (peer, afi, safi, stype);
+
+      if (ret < 0)
+       bgp_clear_vty_error (vty, peer, afi, safi, ret);
+
+      return CMD_SUCCESS;
+    }
+
+  /* Clear all peer-group members. */
+  if (sort == clear_group)
+    {
+      struct peer_group *group;
+
+      group = peer_group_lookup (bgp, arg);
+      if (! group)
+       {
+         vty_out (vty, "%%BGP: No such peer-group %s%s", arg, VTY_NEWLINE);
+         return CMD_WARNING; 
+       }
+
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+       {
+         if (stype == BGP_CLEAR_SOFT_NONE)
+           {
+             ret = peer_clear (peer);
+             continue;
+           }
+
+         if (! peer->af_group[afi][safi])
+           continue;
+
+         ret = peer_clear_soft (peer, afi, safi, stype);
+
+         if (ret < 0)
+           bgp_clear_vty_error (vty, peer, afi, safi, ret);
+       }
+      return CMD_SUCCESS;
+    }
+
+  if (sort == clear_external)
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (peer->sort == BGP_PEER_IBGP)
+           continue;
+
+         if (stype == BGP_CLEAR_SOFT_NONE)
+           ret = peer_clear (peer);
+         else
+           ret = peer_clear_soft (peer, afi, safi, stype);
+
+         if (ret < 0)
+           bgp_clear_vty_error (vty, peer, afi, safi, ret);
+       }
+      return CMD_SUCCESS;
+    }
+
+  if (sort == clear_as)
+    {
+      as_t as;
+      int find = 0;
+
+      VTY_GET_INTEGER_RANGE ("AS", as, arg, 1, BGP_AS4_MAX);
+      
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (peer->as != as) 
+           continue;
+
+         find = 1;
+         if (stype == BGP_CLEAR_SOFT_NONE)
+           ret = peer_clear (peer);
+         else
+           ret = peer_clear_soft (peer, afi, safi, stype);
+
+         if (ret < 0)
+           bgp_clear_vty_error (vty, peer, afi, safi, ret);
+       }
+      if (! find)
+       vty_out (vty, "%%BGP: No peer is configured with AS %s%s", arg,
+                VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Recalculate bestpath and re-advertise a prefix */
+static int
+bgp_clear_prefix (struct vty *vty, char *view_name, const char *ip_str,
+                  afi_t afi, safi_t safi, struct prefix_rd *prd)
+{
+  int ret;
+  struct prefix match;
+  struct bgp_node *rn;
+  struct bgp_node *rm;
+  struct bgp *bgp;
+  struct bgp_table *table;
+  struct bgp_table *rib;
+
+  /* BGP structure lookup. */
+  if (view_name)
+    {
+      bgp = bgp_lookup_by_name (view_name);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "%% Can't find BGP view %s%s", view_name, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "%% No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  /* Check IP address argument. */
+  ret = str2prefix (ip_str, &match);
+  if (! ret)
+    {
+      vty_out (vty, "%% address is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  match.family = afi2family (afi);
+  rib = bgp->rib[afi][safi];
+
+  if (safi == SAFI_MPLS_VPN)
+    {
+      for (rn = bgp_table_top (rib); rn; rn = bgp_route_next (rn))
+        {
+          if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
+            continue;
+
+          if ((table = rn->info) != NULL)
+            {
+              if ((rm = bgp_node_match (table, &match)) != NULL)
+                {
+                  if (rm->p.prefixlen == match.prefixlen)
+                    {
+                      SET_FLAG (rn->flags, BGP_NODE_USER_CLEAR);
+                      bgp_process (bgp, rm, afi, safi);
+                    }
+                  bgp_unlock_node (rm);
+                }
+            }
+        }
+    }
+  else
+    {
+      if ((rn = bgp_node_match (rib, &match)) != NULL)
+        {
+          if (rn->p.prefixlen == match.prefixlen)
+            {
+              SET_FLAG (rn->flags, BGP_NODE_USER_CLEAR);
+              bgp_process (bgp, rn, afi, safi);
+            }
+          bgp_unlock_node (rn);
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi,
+               enum clear_sort sort, enum bgp_clear_type stype, 
+               const char *arg)
+{
+  struct bgp *bgp;
+
+  /* BGP structure lookup. */
+  if (name)
+    {
+      bgp = bgp_lookup_by_name (name);
+      if (bgp == NULL)
+        {
+          vty_out (vty, "Can't find BGP view %s%s", name, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      bgp = bgp_get_default ();
+      if (bgp == NULL)
+        {
+          vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return bgp_clear (vty, bgp, afi, safi, sort, stype, arg);
+}
+  
+DEFUN (clear_ip_bgp_all,
+       clear_ip_bgp_all_cmd,
+       "clear ip bgp *",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n")
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL);    
+
+  return bgp_clear_vty (vty, NULL, 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL);
+}
+
+ALIAS (clear_ip_bgp_all,
+       clear_bgp_all_cmd,
+       "clear bgp *",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n")
+
+ALIAS (clear_ip_bgp_all,
+       clear_bgp_ipv6_all_cmd,
+       "clear bgp ipv6 *",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n")
+
+ALIAS (clear_ip_bgp_all,
+       clear_ip_bgp_instance_all_cmd,
+       "clear ip bgp view WORD *",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n")
+
+ALIAS (clear_ip_bgp_all,
+       clear_bgp_instance_all_cmd,
+       "clear bgp view WORD *",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n")
+
+DEFUN (clear_ip_bgp_peer,
+       clear_ip_bgp_peer_cmd, 
+       "clear ip bgp (A.B.C.D|X:X::X:X)",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n")
+{
+  return bgp_clear_vty (vty, NULL, 0, 0, clear_peer, BGP_CLEAR_SOFT_NONE, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer,
+       clear_bgp_peer_cmd, 
+       "clear bgp (A.B.C.D|X:X::X:X)",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n")
+
+ALIAS (clear_ip_bgp_peer,
+       clear_bgp_ipv6_peer_cmd, 
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X)",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n")
+
+DEFUN (clear_ip_bgp_peer_group,
+       clear_ip_bgp_peer_group_cmd, 
+       "clear ip bgp peer-group WORD",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n")
+{
+  return bgp_clear_vty (vty, NULL, 0, 0, clear_group, BGP_CLEAR_SOFT_NONE, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_group,
+       clear_bgp_peer_group_cmd, 
+       "clear bgp peer-group WORD",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n")
+
+ALIAS (clear_ip_bgp_peer_group,
+       clear_bgp_ipv6_peer_group_cmd, 
+       "clear bgp ipv6 peer-group WORD",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n")
+
+DEFUN (clear_ip_bgp_external,
+       clear_ip_bgp_external_cmd,
+       "clear ip bgp external",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n")
+{
+  return bgp_clear_vty (vty, NULL, 0, 0, clear_external, BGP_CLEAR_SOFT_NONE, NULL);
+}
+
+ALIAS (clear_ip_bgp_external,
+       clear_bgp_external_cmd, 
+       "clear bgp external",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n")
+
+ALIAS (clear_ip_bgp_external,
+       clear_bgp_ipv6_external_cmd, 
+       "clear bgp ipv6 external",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n")
+
+DEFUN (clear_ip_bgp_prefix,
+       clear_ip_bgp_prefix_cmd,
+       "clear ip bgp prefix A.B.C.D/M",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear bestpath and re-advertise\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return bgp_clear_prefix (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL);
+}
+
+ALIAS (clear_ip_bgp_prefix,
+       clear_bgp_prefix_cmd,
+       "clear bgp prefix A.B.C.D/M",
+       CLEAR_STR
+       BGP_STR
+       "Clear bestpath and re-advertise\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+
+
+DEFUN (clear_ip_bgp_as,
+       clear_ip_bgp_as_cmd,
+       "clear ip bgp " CMD_AS_RANGE,
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n")
+{
+  return bgp_clear_vty (vty, NULL, 0, 0, clear_as, BGP_CLEAR_SOFT_NONE, argv[0]);
+}       
+
+ALIAS (clear_ip_bgp_as,
+       clear_bgp_as_cmd,
+       "clear bgp " CMD_AS_RANGE,
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n")
+
+ALIAS (clear_ip_bgp_as,
+       clear_bgp_ipv6_as_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE,
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n")
+
+/* Outbound soft-reconfiguration */
+DEFUN (clear_ip_bgp_all_soft_out,
+       clear_ip_bgp_all_soft_out_cmd,
+       "clear ip bgp * soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_OUT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_soft_out,
+       clear_ip_bgp_all_out_cmd,
+       "clear ip bgp * out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_ip_bgp_all_soft_out,
+       clear_ip_bgp_instance_all_soft_out_cmd,
+       "clear ip bgp view WORD * soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_all_ipv4_soft_out,
+       clear_ip_bgp_all_ipv4_soft_out_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
+                         BGP_CLEAR_SOFT_OUT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_ipv4_soft_out,
+       clear_ip_bgp_all_ipv4_out_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_instance_all_ipv4_soft_out,
+       clear_ip_bgp_instance_all_ipv4_soft_out_cmd,
+       "clear ip bgp view WORD * ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all,
+                          BGP_CLEAR_SOFT_OUT, NULL);
+
+  return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+DEFUN (clear_ip_bgp_all_vpnv4_soft_out,
+       clear_ip_bgp_all_vpnv4_soft_out_cmd,
+       "clear ip bgp * vpnv4 unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_vpnv4_soft_out,
+       clear_ip_bgp_all_vpnv4_out_cmd,
+       "clear ip bgp * vpnv4 unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_all_encap_soft_out,
+       clear_ip_bgp_all_encap_soft_out_cmd,
+       "clear ip bgp * encap unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig outbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_encap_soft_out,
+       clear_ip_bgp_all_encap_out_cmd,
+       "clear ip bgp * encap unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig outbound update\n")
+
+DEFUN (clear_bgp_all_soft_out,
+       clear_bgp_all_soft_out_cmd,
+       "clear bgp * soft out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_OUT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_bgp_all_soft_out,
+       clear_bgp_instance_all_soft_out_cmd,
+       "clear bgp view WORD * soft out",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_all_soft_out,
+       clear_bgp_all_out_cmd,
+       "clear bgp * out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_all_soft_out,
+       clear_bgp_ipv6_all_soft_out_cmd,
+       "clear bgp ipv6 * soft out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_all_soft_out,
+       clear_bgp_ipv6_all_out_cmd,
+       "clear bgp ipv6 * out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_bgp_ipv6_safi_prefix,
+       clear_bgp_ipv6_safi_prefix_cmd,
+       "clear bgp ipv6 (unicast|multicast) prefix X:X::X:X/M",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Clear bestpath and re-advertise\n"
+       "IPv6 prefix <network>/<length>,  e.g.,  3ffe::/16\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_prefix (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL);
+  else
+    return bgp_clear_prefix (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL);
+}
+
+DEFUN (clear_ip_bgp_peer_soft_out,
+       clear_ip_bgp_peer_soft_out_cmd,
+       "clear ip bgp A.B.C.D soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_soft_out,
+       clear_ip_bgp_peer_out_cmd,
+       "clear ip bgp A.B.C.D out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_peer_ipv4_soft_out,
+       clear_ip_bgp_peer_ipv4_soft_out_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer,
+                         BGP_CLEAR_SOFT_OUT, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_ipv4_soft_out,
+       clear_ip_bgp_peer_ipv4_out_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_peer_vpnv4_soft_out,
+       clear_ip_bgp_peer_vpnv4_soft_out_cmd,
+       "clear ip bgp A.B.C.D vpnv4 unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_vpnv4_soft_out,
+       clear_ip_bgp_peer_vpnv4_out_cmd,
+       "clear ip bgp A.B.C.D vpnv4 unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_peer_encap_soft_out,
+       clear_ip_bgp_peer_encap_soft_out_cmd,
+       "clear ip bgp A.B.C.D encap unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig outbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_encap_soft_out,
+       clear_ip_bgp_peer_encap_out_cmd,
+       "clear ip bgp A.B.C.D encap unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig outbound update\n")
+
+DEFUN (clear_bgp_peer_soft_out,
+       clear_bgp_peer_soft_out_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) soft out",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_soft_out,
+       clear_bgp_ipv6_peer_soft_out_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_peer_soft_out,
+       clear_bgp_peer_out_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) out",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_peer_soft_out,
+       clear_bgp_ipv6_peer_out_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_peer_group_soft_out,
+       clear_ip_bgp_peer_group_soft_out_cmd, 
+       "clear ip bgp peer-group WORD soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_group_soft_out,
+       clear_ip_bgp_peer_group_out_cmd, 
+       "clear ip bgp peer-group WORD out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_peer_group_ipv4_soft_out,
+       clear_ip_bgp_peer_group_ipv4_soft_out_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group,
+                         BGP_CLEAR_SOFT_OUT, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_group_ipv4_soft_out,
+       clear_ip_bgp_peer_group_ipv4_out_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_bgp_peer_group_soft_out,
+       clear_bgp_peer_group_soft_out_cmd,
+       "clear bgp peer-group WORD soft out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_group_soft_out,
+       clear_bgp_ipv6_peer_group_soft_out_cmd,
+       "clear bgp ipv6 peer-group WORD soft out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_peer_group_soft_out,
+       clear_bgp_peer_group_out_cmd,
+       "clear bgp peer-group WORD out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_peer_group_soft_out,
+       clear_bgp_ipv6_peer_group_out_cmd,
+       "clear bgp ipv6 peer-group WORD out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_external_soft_out,
+       clear_ip_bgp_external_soft_out_cmd, 
+       "clear ip bgp external soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_external_soft_out,
+       clear_ip_bgp_external_out_cmd, 
+       "clear ip bgp external out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_external_ipv4_soft_out,
+       clear_ip_bgp_external_ipv4_soft_out_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external,
+                         BGP_CLEAR_SOFT_OUT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_ip_bgp_external_ipv4_soft_out,
+       clear_ip_bgp_external_ipv4_out_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_bgp_external_soft_out,
+       clear_bgp_external_soft_out_cmd,
+       "clear bgp external soft out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+ALIAS (clear_bgp_external_soft_out,
+       clear_bgp_ipv6_external_soft_out_cmd,
+       "clear bgp ipv6 external soft out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_external_soft_out,
+       clear_bgp_external_out_cmd,
+       "clear bgp external out",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_external_soft_out,
+       clear_bgp_ipv6_external_out_cmd,
+       "clear bgp ipv6 external WORD out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_as_soft_out,
+       clear_ip_bgp_as_soft_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_soft_out,
+       clear_ip_bgp_as_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_as_ipv4_soft_out,
+       clear_ip_bgp_as_ipv4_soft_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as,
+                         BGP_CLEAR_SOFT_OUT, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_ipv4_soft_out,
+       clear_ip_bgp_as_ipv4_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_as_vpnv4_soft_out,
+       clear_ip_bgp_as_vpnv4_soft_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_vpnv4_soft_out,
+       clear_ip_bgp_as_vpnv4_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       BGP_SOFT_OUT_STR)
+
+DEFUN (clear_ip_bgp_as_encap_soft_out,
+       clear_ip_bgp_as_encap_soft_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " encap unicast soft out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig outbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_encap_soft_out,
+       clear_ip_bgp_as_encap_out_cmd,
+       "clear ip bgp " CMD_AS_RANGE " encap unicast out",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Soft reconfig outbound update\n")
+
+DEFUN (clear_bgp_as_soft_out,
+       clear_bgp_as_soft_out_cmd,
+       "clear bgp " CMD_AS_RANGE " soft out",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_OUT, argv[0]);
+}
+
+ALIAS (clear_bgp_as_soft_out,
+       clear_bgp_ipv6_as_soft_out_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " soft out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_as_soft_out,
+       clear_bgp_as_out_cmd,
+       "clear bgp " CMD_AS_RANGE " out",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_OUT_STR)
+
+ALIAS (clear_bgp_as_soft_out,
+       clear_bgp_ipv6_as_out_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " out",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_OUT_STR)
+
+/* Inbound soft-reconfiguration */
+DEFUN (clear_ip_bgp_all_soft_in,
+       clear_ip_bgp_all_soft_in_cmd,
+       "clear ip bgp * soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_IN, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_soft_in,
+       clear_ip_bgp_instance_all_soft_in_cmd,
+       "clear ip bgp view WORD * soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_ip_bgp_all_soft_in,
+       clear_ip_bgp_all_in_cmd,
+       "clear ip bgp * in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_all_in_prefix_filter,
+       clear_ip_bgp_all_in_prefix_filter_cmd,
+       "clear ip bgp * in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (argc== 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_in_prefix_filter,
+       clear_ip_bgp_instance_all_in_prefix_filter_cmd,
+       "clear ip bgp view WORD * in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+
+
+DEFUN (clear_ip_bgp_all_ipv4_soft_in,
+       clear_ip_bgp_all_ipv4_soft_in_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
+                         BGP_CLEAR_SOFT_IN, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_ipv4_soft_in,
+       clear_ip_bgp_all_ipv4_in_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_instance_all_ipv4_soft_in,
+       clear_ip_bgp_instance_all_ipv4_soft_in_cmd,
+       "clear ip bgp view WORD * ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all,
+                          BGP_CLEAR_SOFT_IN, NULL);
+
+  return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_IN, NULL);
+}
+
+DEFUN (clear_ip_bgp_all_ipv4_in_prefix_filter,
+       clear_ip_bgp_all_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
+                         BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+DEFUN (clear_ip_bgp_instance_all_ipv4_in_prefix_filter,
+       clear_ip_bgp_instance_all_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp view WORD * ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all,
+                          BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+
+  return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+DEFUN (clear_ip_bgp_all_vpnv4_soft_in,
+       clear_ip_bgp_all_vpnv4_soft_in_cmd,
+       "clear ip bgp * vpnv4 unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_vpnv4_soft_in,
+       clear_ip_bgp_all_vpnv4_in_cmd,
+       "clear ip bgp * vpnv4 unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_all_encap_soft_in,
+       clear_ip_bgp_all_encap_soft_in_cmd,
+       "clear ip bgp * encap unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig inbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_encap_soft_in,
+       clear_ip_bgp_all_encap_in_cmd,
+       "clear ip bgp * encap unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig inbound update\n")
+
+DEFUN (clear_bgp_all_soft_in,
+       clear_bgp_all_soft_in_cmd,
+       "clear bgp * soft in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_IN, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_bgp_all_soft_in,
+       clear_bgp_instance_all_soft_in_cmd,
+       "clear bgp view WORD * soft in",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_all_soft_in,
+       clear_bgp_ipv6_all_soft_in_cmd,
+       "clear bgp ipv6 * soft in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_all_soft_in,
+       clear_bgp_all_in_cmd,
+       "clear bgp * in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_all_soft_in,
+       clear_bgp_ipv6_all_in_cmd,
+       "clear bgp ipv6 * in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_bgp_all_in_prefix_filter,
+       clear_bgp_all_in_prefix_filter_cmd,
+       "clear bgp * in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+ALIAS (clear_bgp_all_in_prefix_filter,
+       clear_bgp_ipv6_all_in_prefix_filter_cmd,
+       "clear bgp ipv6 * in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+
+DEFUN (clear_ip_bgp_peer_soft_in,
+       clear_ip_bgp_peer_soft_in_cmd,
+       "clear ip bgp A.B.C.D soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_soft_in,
+       clear_ip_bgp_peer_in_cmd,
+       "clear ip bgp A.B.C.D in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_IN_STR)
+       
+DEFUN (clear_ip_bgp_peer_in_prefix_filter,
+       clear_ip_bgp_peer_in_prefix_filter_cmd,
+       "clear ip bgp A.B.C.D in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_IN_STR
+       "Push out the existing ORF prefix-list\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_ipv4_soft_in,
+       clear_ip_bgp_peer_ipv4_soft_in_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer,
+                         BGP_CLEAR_SOFT_IN, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_ipv4_soft_in,
+       clear_ip_bgp_peer_ipv4_in_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_peer_ipv4_in_prefix_filter,
+       clear_ip_bgp_peer_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out the existing ORF prefix-list\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer,
+                         BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_vpnv4_soft_in,
+       clear_ip_bgp_peer_vpnv4_soft_in_cmd,
+       "clear ip bgp A.B.C.D vpnv4 unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_vpnv4_soft_in,
+       clear_ip_bgp_peer_vpnv4_in_cmd,
+       "clear ip bgp A.B.C.D vpnv4 unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_peer_encap_soft_in,
+       clear_ip_bgp_peer_encap_soft_in_cmd,
+       "clear ip bgp A.B.C.D encap unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig inbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_encap_soft_in,
+       clear_ip_bgp_peer_encap_in_cmd,
+       "clear ip bgp A.B.C.D encap unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig inbound update\n")
+
+DEFUN (clear_bgp_peer_soft_in,
+       clear_bgp_peer_soft_in_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) soft in",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_soft_in,
+       clear_bgp_ipv6_peer_soft_in_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_peer_soft_in,
+       clear_bgp_peer_in_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) in",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_peer_soft_in,
+       clear_bgp_ipv6_peer_in_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_bgp_peer_in_prefix_filter,
+       clear_bgp_peer_in_prefix_filter_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_IN_STR
+       "Push out the existing ORF prefix-list\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_in_prefix_filter,
+       clear_bgp_ipv6_peer_in_prefix_filter_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_IN_STR
+       "Push out the existing ORF prefix-list\n")
+
+DEFUN (clear_ip_bgp_peer_group_soft_in,
+       clear_ip_bgp_peer_group_soft_in_cmd,
+       "clear ip bgp peer-group WORD soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_group_soft_in,
+       clear_ip_bgp_peer_group_in_cmd,
+       "clear ip bgp peer-group WORD in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_peer_group_in_prefix_filter,
+       clear_ip_bgp_peer_group_in_prefix_filter_cmd,
+       "clear ip bgp peer-group WORD in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_group_ipv4_soft_in,
+       clear_ip_bgp_peer_group_ipv4_soft_in_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group,
+                         BGP_CLEAR_SOFT_IN, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_group_ipv4_soft_in,
+       clear_ip_bgp_peer_group_ipv4_in_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_peer_group_ipv4_in_prefix_filter,
+       clear_ip_bgp_peer_group_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group,
+                         BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_bgp_peer_group_soft_in,
+       clear_bgp_peer_group_soft_in_cmd,
+       "clear bgp peer-group WORD soft in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_group_soft_in,
+       clear_bgp_ipv6_peer_group_soft_in_cmd,
+       "clear bgp ipv6 peer-group WORD soft in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_peer_group_soft_in,
+       clear_bgp_peer_group_in_cmd,
+       "clear bgp peer-group WORD in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_peer_group_soft_in,
+       clear_bgp_ipv6_peer_group_in_cmd,
+       "clear bgp ipv6 peer-group WORD in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_bgp_peer_group_in_prefix_filter,
+       clear_bgp_peer_group_in_prefix_filter_cmd,
+       "clear bgp peer-group WORD in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_group_in_prefix_filter,
+       clear_bgp_ipv6_peer_group_in_prefix_filter_cmd,
+       "clear bgp ipv6 peer-group WORD in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+
+DEFUN (clear_ip_bgp_external_soft_in,
+       clear_ip_bgp_external_soft_in_cmd,
+       "clear ip bgp external soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_external_soft_in,
+       clear_ip_bgp_external_in_cmd,
+       "clear ip bgp external in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_external_in_prefix_filter,
+       clear_ip_bgp_external_in_prefix_filter_cmd,
+       "clear ip bgp external in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+DEFUN (clear_ip_bgp_external_ipv4_soft_in,
+       clear_ip_bgp_external_ipv4_soft_in_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external,
+                         BGP_CLEAR_SOFT_IN, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_ip_bgp_external_ipv4_soft_in,
+       clear_ip_bgp_external_ipv4_in_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_external_ipv4_in_prefix_filter,
+       clear_ip_bgp_external_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external,
+                         BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+DEFUN (clear_bgp_external_soft_in,
+       clear_bgp_external_soft_in_cmd,
+       "clear bgp external soft in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN, NULL);
+}
+
+ALIAS (clear_bgp_external_soft_in,
+       clear_bgp_ipv6_external_soft_in_cmd,
+       "clear bgp ipv6 external soft in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_external_soft_in,
+       clear_bgp_external_in_cmd,
+       "clear bgp external in",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_external_soft_in,
+       clear_bgp_ipv6_external_in_cmd,
+       "clear bgp ipv6 external WORD in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_bgp_external_in_prefix_filter,
+       clear_bgp_external_in_prefix_filter_cmd,
+       "clear bgp external in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL);
+}
+
+ALIAS (clear_bgp_external_in_prefix_filter,
+       clear_bgp_ipv6_external_in_prefix_filter_cmd,
+       "clear bgp ipv6 external in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+
+DEFUN (clear_ip_bgp_as_soft_in,
+       clear_ip_bgp_as_soft_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_soft_in,
+       clear_ip_bgp_as_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_as_in_prefix_filter,
+       clear_ip_bgp_as_in_prefix_filter_cmd,
+       "clear ip bgp " CMD_AS_RANGE " in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_as_ipv4_soft_in,
+       clear_ip_bgp_as_ipv4_soft_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as,
+                         BGP_CLEAR_SOFT_IN, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_ipv4_soft_in,
+       clear_ip_bgp_as_ipv4_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter,
+       clear_ip_bgp_as_ipv4_in_prefix_filter_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) in prefix-filter",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as,
+                         BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_as_vpnv4_soft_in,
+       clear_ip_bgp_as_vpnv4_soft_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_vpnv4_soft_in,
+       clear_ip_bgp_as_vpnv4_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_ip_bgp_as_encap_soft_in,
+       clear_ip_bgp_as_encap_soft_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " encap unicast soft in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Soft reconfig\n"
+       "Soft reconfig inbound update\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_as_encap_soft_in,
+       clear_ip_bgp_as_encap_in_cmd,
+       "clear ip bgp " CMD_AS_RANGE " encap unicast in",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Soft reconfig inbound update\n")
+
+DEFUN (clear_bgp_as_soft_in,
+       clear_bgp_as_soft_in_cmd,
+       "clear bgp " CMD_AS_RANGE " soft in",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN, argv[0]);
+}
+
+ALIAS (clear_bgp_as_soft_in,
+       clear_bgp_ipv6_as_soft_in_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " soft in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_as_soft_in,
+       clear_bgp_as_in_cmd,
+       "clear bgp " CMD_AS_RANGE " in",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR)
+
+ALIAS (clear_bgp_as_soft_in,
+       clear_bgp_ipv6_as_in_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " in",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR)
+
+DEFUN (clear_bgp_as_in_prefix_filter,
+       clear_bgp_as_in_prefix_filter_cmd,
+       "clear bgp " CMD_AS_RANGE " in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]);
+}
+
+ALIAS (clear_bgp_as_in_prefix_filter,
+       clear_bgp_ipv6_as_in_prefix_filter_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " in prefix-filter",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_IN_STR
+       "Push out prefix-list ORF and do inbound soft reconfig\n")
+
+/* Both soft-reconfiguration */
+DEFUN (clear_ip_bgp_all_soft,
+       clear_ip_bgp_all_soft_cmd,
+       "clear ip bgp * soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_BOTH, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_soft,
+       clear_ip_bgp_instance_all_soft_cmd,
+       "clear ip bgp view WORD * soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR)
+
+
+DEFUN (clear_ip_bgp_all_ipv4_soft,
+       clear_ip_bgp_all_ipv4_soft_cmd,
+       "clear ip bgp * ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
+                         BGP_CLEAR_SOFT_BOTH, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+DEFUN (clear_ip_bgp_instance_all_ipv4_soft,
+       clear_ip_bgp_instance_all_ipv4_soft_cmd,
+       "clear ip bgp view WORD * ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all,
+                          BGP_CLEAR_SOFT_BOTH, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+DEFUN (clear_ip_bgp_all_vpnv4_soft,
+       clear_ip_bgp_all_vpnv4_soft_cmd,
+       "clear ip bgp * vpnv4 unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_all_encap_soft,
+       clear_ip_bgp_all_encap_soft_cmd,
+       "clear ip bgp * encap unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_bgp_all_soft,
+       clear_bgp_all_soft_cmd,
+       "clear bgp * soft",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_BOTH, argv[0]);
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+ALIAS (clear_bgp_all_soft,
+       clear_bgp_instance_all_soft_cmd,
+       "clear bgp view WORD * soft",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR)
+
+ALIAS (clear_bgp_all_soft,
+       clear_bgp_ipv6_all_soft_cmd,
+       "clear bgp ipv6 * soft",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_STR)
+
+DEFUN (clear_ip_bgp_peer_soft,
+       clear_ip_bgp_peer_soft_cmd,
+       "clear ip bgp A.B.C.D soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_ipv4_soft,
+       clear_ip_bgp_peer_ipv4_soft_cmd,
+       "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer,
+                         BGP_CLEAR_SOFT_BOTH, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_vpnv4_soft,
+       clear_ip_bgp_peer_vpnv4_soft_cmd,
+       "clear ip bgp A.B.C.D vpnv4 unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_encap_soft,
+       clear_ip_bgp_peer_encap_soft_cmd,
+       "clear ip bgp A.B.C.D encap unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_bgp_peer_soft,
+       clear_bgp_peer_soft_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) soft",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_soft,
+       clear_bgp_ipv6_peer_soft_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_STR)
+
+DEFUN (clear_ip_bgp_peer_group_soft,
+       clear_ip_bgp_peer_group_soft_cmd,
+       "clear ip bgp peer-group WORD soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_peer_group_ipv4_soft,
+       clear_ip_bgp_peer_group_ipv4_soft_cmd,
+       "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group,
+                         BGP_CLEAR_SOFT_BOTH, argv[0]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_bgp_peer_group_soft,
+       clear_bgp_peer_group_soft_cmd,
+       "clear bgp peer-group WORD soft",
+       CLEAR_STR
+       BGP_STR
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_group_soft,
+       clear_bgp_ipv6_peer_group_soft_cmd,
+       "clear bgp ipv6 peer-group WORD soft",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all members of peer-group\n"
+       "BGP peer-group name\n"
+       BGP_SOFT_STR)
+
+DEFUN (clear_ip_bgp_external_soft,
+       clear_ip_bgp_external_soft_cmd,
+       "clear ip bgp external soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+DEFUN (clear_ip_bgp_external_ipv4_soft,
+       clear_ip_bgp_external_ipv4_soft_cmd,
+       "clear ip bgp external ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all external peers\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external,
+                         BGP_CLEAR_SOFT_BOTH, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+DEFUN (clear_bgp_external_soft,
+       clear_bgp_external_soft_cmd,
+       "clear bgp external soft",
+       CLEAR_STR
+       BGP_STR
+       "Clear all external peers\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external,
+                       BGP_CLEAR_SOFT_BOTH, NULL);
+}
+
+ALIAS (clear_bgp_external_soft,
+       clear_bgp_ipv6_external_soft_cmd,
+       "clear bgp ipv6 external soft",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all external peers\n"
+       BGP_SOFT_STR)
+
+DEFUN (clear_ip_bgp_as_soft,
+       clear_ip_bgp_as_soft_cmd,
+       "clear ip bgp " CMD_AS_RANGE " soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_as_ipv4_soft,
+       clear_ip_bgp_as_ipv4_soft_cmd,
+       "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as,
+                         BGP_CLEAR_SOFT_BOTH, argv[0]);
+
+  return bgp_clear_vty (vty, NULL,AFI_IP, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_as_vpnv4_soft,
+       clear_ip_bgp_as_vpnv4_soft_cmd,
+       "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_ip_bgp_as_encap_soft,
+       clear_ip_bgp_as_encap_soft_cmd,
+       "clear ip bgp " CMD_AS_RANGE " encap unicast soft",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       "Address family\n"
+       "Address Family Modifier\n"
+       "Soft reconfig\n")
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+DEFUN (clear_bgp_as_soft,
+       clear_bgp_as_soft_cmd,
+       "clear bgp " CMD_AS_RANGE " soft",
+       CLEAR_STR
+       BGP_STR
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR)
+{
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as,
+                       BGP_CLEAR_SOFT_BOTH, argv[0]);
+}
+
+ALIAS (clear_bgp_as_soft,
+       clear_bgp_ipv6_as_soft_cmd,
+       "clear bgp ipv6 " CMD_AS_RANGE " soft",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear peers with the AS number\n"
+       BGP_SOFT_STR)
+
+/* RS-client soft reconfiguration. */
+DEFUN (clear_bgp_all_rsclient,
+       clear_bgp_all_rsclient_cmd,
+       "clear bgp * rsclient",
+       CLEAR_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_RSCLIENT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_RSCLIENT, NULL);
+}
+
+ALIAS (clear_bgp_all_rsclient,
+       clear_bgp_ipv6_all_rsclient_cmd,
+       "clear bgp ipv6 * rsclient",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+ALIAS (clear_bgp_all_rsclient,
+       clear_bgp_instance_all_rsclient_cmd,
+       "clear bgp view WORD * rsclient",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+ALIAS (clear_bgp_all_rsclient,
+       clear_bgp_ipv6_instance_all_rsclient_cmd,
+       "clear bgp ipv6 view WORD * rsclient",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+DEFUN (clear_ip_bgp_all_rsclient,
+       clear_ip_bgp_all_rsclient_cmd,
+       "clear ip bgp * rsclient",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+{
+  if (argc == 1)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all,
+                          BGP_CLEAR_SOFT_RSCLIENT, NULL);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all,
+                        BGP_CLEAR_SOFT_RSCLIENT, NULL);
+}
+
+ALIAS (clear_ip_bgp_all_rsclient,
+       clear_ip_bgp_instance_all_rsclient_cmd,
+       "clear ip bgp view WORD * rsclient",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "Clear all peers\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+DEFUN (clear_bgp_peer_rsclient,
+       clear_bgp_peer_rsclient_cmd,
+       "clear bgp (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       BGP_STR
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+{
+  if (argc == 2)
+    return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_peer,
+                          BGP_CLEAR_SOFT_RSCLIENT, argv[1]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer,
+                        BGP_CLEAR_SOFT_RSCLIENT, argv[0]);
+}
+
+ALIAS (clear_bgp_peer_rsclient,
+       clear_bgp_ipv6_peer_rsclient_cmd,
+       "clear bgp ipv6 (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+ALIAS (clear_bgp_peer_rsclient,
+       clear_bgp_instance_peer_rsclient_cmd,
+       "clear bgp view WORD (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+ALIAS (clear_bgp_peer_rsclient,
+       clear_bgp_ipv6_instance_peer_rsclient_cmd,
+       "clear bgp ipv6 view WORD (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       BGP_STR
+       "Address family\n"
+       "BGP view\n"
+       "view name\n"
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+DEFUN (clear_ip_bgp_peer_rsclient,
+       clear_ip_bgp_peer_rsclient_cmd,
+       "clear ip bgp (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+{
+  if (argc == 2)
+    return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_peer,
+                          BGP_CLEAR_SOFT_RSCLIENT, argv[1]);
+
+  return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer,
+                        BGP_CLEAR_SOFT_RSCLIENT, argv[0]);
+}
+
+ALIAS (clear_ip_bgp_peer_rsclient,
+       clear_ip_bgp_instance_peer_rsclient_cmd,
+       "clear ip bgp view WORD (A.B.C.D|X:X::X:X) rsclient",
+       CLEAR_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "view name\n"
+       "BGP neighbor IP address to clear\n"
+       "BGP IPv6 neighbor to clear\n"
+       BGP_SOFT_RSCLIENT_RIB_STR)
+
+DEFUN (show_bgp_views,
+       show_bgp_views_cmd,
+       "show bgp views",
+       SHOW_STR
+       BGP_STR
+       "Show the defined BGP views\n")
+{
+  struct list *inst = bm->bgp;
+  struct listnode *node;
+  struct bgp *bgp;
+
+  if (!bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
+    {
+      vty_out (vty, "Multiple BGP views are not defined%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  vty_out (vty, "Defined BGP views:%s", VTY_NEWLINE);
+  for (ALL_LIST_ELEMENTS_RO(inst, node, bgp))
+    vty_out (vty, "\t%s (AS%u)%s", 
+             bgp->name ? bgp->name : "(null)",
+             bgp->as, VTY_NEWLINE);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_memory, 
+       show_bgp_memory_cmd,
+       "show bgp memory",
+       SHOW_STR
+       BGP_STR
+       "Global BGP memory statistics\n")
+{
+  char memstrbuf[MTYPE_MEMSTR_LEN];
+  unsigned long count;
+  
+  /* RIB related usage stats */
+  count = mtype_stats_alloc (MTYPE_BGP_NODE);
+  vty_out (vty, "%ld RIB nodes, using %s of memory%s", count,
+           mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct bgp_node)),
+           VTY_NEWLINE);
+  
+  count = mtype_stats_alloc (MTYPE_BGP_ROUTE);
+  vty_out (vty, "%ld BGP routes, using %s of memory%s", count,
+           mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct bgp_info)),
+           VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_BGP_ROUTE_EXTRA)))
+    vty_out (vty, "%ld BGP route ancillaries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct bgp_info_extra)),
+             VTY_NEWLINE);
+  
+  if ((count = mtype_stats_alloc (MTYPE_BGP_STATIC)))
+    vty_out (vty, "%ld Static routes, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct bgp_static)),
+             VTY_NEWLINE);
+  
+  /* Adj-In/Out */
+  if ((count = mtype_stats_alloc (MTYPE_BGP_ADJ_IN)))
+    vty_out (vty, "%ld Adj-In entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct bgp_adj_in)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_BGP_ADJ_OUT)))
+    vty_out (vty, "%ld Adj-Out entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct bgp_adj_out)),
+             VTY_NEWLINE);
+  
+  if ((count = mtype_stats_alloc (MTYPE_BGP_NEXTHOP_CACHE)))
+    vty_out (vty, "%ld Nexthop cache entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct bgp_nexthop_cache)),
+             VTY_NEWLINE);
+
+  if ((count = mtype_stats_alloc (MTYPE_BGP_DAMP_INFO)))
+    vty_out (vty, "%ld Dampening entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct bgp_damp_info)),
+             VTY_NEWLINE);
+
+  /* Attributes */
+  count = attr_count();
+  vty_out (vty, "%ld BGP attributes, using %s of memory%s", count, 
+           mtype_memstr (memstrbuf, sizeof (memstrbuf), 
+                         count * sizeof(struct attr)), 
+           VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_ATTR_EXTRA)))
+    vty_out (vty, "%ld BGP extra attributes, using %s of memory%s", count, 
+             mtype_memstr (memstrbuf, sizeof (memstrbuf), 
+                           count * sizeof(struct attr_extra)), 
+             VTY_NEWLINE);
+  
+  if ((count = attr_unknown_count()))
+    vty_out (vty, "%ld unknown attributes%s", count, VTY_NEWLINE);
+  
+  /* AS_PATH attributes */
+  count = aspath_count ();
+  vty_out (vty, "%ld BGP AS-PATH entries, using %s of memory%s", count,
+           mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct aspath)),
+           VTY_NEWLINE);
+  
+  count = mtype_stats_alloc (MTYPE_AS_SEG);
+  vty_out (vty, "%ld BGP AS-PATH segments, using %s of memory%s", count,
+           mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct assegment)),
+           VTY_NEWLINE);
+  
+  /* Other attributes */
+  if ((count = community_count ()))
+    vty_out (vty, "%ld BGP community entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct community)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_ECOMMUNITY)))
+    vty_out (vty, "%ld BGP community entries, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct ecommunity)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_LCOMMUNITY)))
+    vty_out (vty, "%ld BGP large-community entries, using %s of memory%s",
+            count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct lcommunity)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_CLUSTER)))
+    vty_out (vty, "%ld Cluster lists, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct cluster_list)),
+             VTY_NEWLINE);
+  
+  /* Peer related usage */
+  count = mtype_stats_alloc (MTYPE_BGP_PEER);
+  vty_out (vty, "%ld peers, using %s of memory%s", count,
+           mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct peer)),
+           VTY_NEWLINE);
+  
+  if ((count = mtype_stats_alloc (MTYPE_PEER_GROUP)))
+    vty_out (vty, "%ld peer groups, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct peer_group)),
+             VTY_NEWLINE);
+  
+  /* Other */
+  if ((count = mtype_stats_alloc (MTYPE_HASH)))
+    vty_out (vty, "%ld hash tables, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct hash)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_HASH_BACKET)))
+    vty_out (vty, "%ld hash buckets, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (struct hash_backet)),
+             VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_BGP_REGEXP)))
+    vty_out (vty, "%ld compiled regexes, using %s of memory%s", count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                           count * sizeof (regex_t)),
+             VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/* Show BGP peer's summary information. */
+static int
+bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  unsigned int count = 0;
+  unsigned int totrcount = 0;
+  unsigned int totecount = 0;
+  char timebuf[BGP_UPTIME_LEN];
+  int len;
+
+  /* Header string for each address family. */
+  static char header[] = "Neighbor        V         AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd";
+  
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (peer->afc[afi][safi])
+       {
+          if (!count)
+            {
+              unsigned long ents;
+              char memstrbuf[MTYPE_MEMSTR_LEN];
+              
+              /* Usage summary and header */
+              vty_out (vty,
+                       "BGP router identifier %s, local AS number %u%s",
+                       inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE);
+
+              ents = bgp_table_count (bgp->rib[afi][safi]);
+              vty_out (vty, "RIB entries %ld, using %s of memory%s", ents,
+                       mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                                     ents * sizeof (struct bgp_node)),
+                       VTY_NEWLINE);
+              
+              /* Peer related usage */
+              ents = listcount (bgp->peer);
+              vty_out (vty, "Peers %ld, using %s of memory%s",
+                       ents,
+                       mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                                     ents * sizeof (struct peer)),
+                       VTY_NEWLINE);
+              
+              if ((ents = listcount (bgp->rsclient)))
+                vty_out (vty, "RS-Client peers %ld, using %s of memory%s",
+                         ents,
+                         mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                                       ents * sizeof (struct peer)),
+                         VTY_NEWLINE);
+              
+              if ((ents = listcount (bgp->group)))
+                vty_out (vty, "Peer groups %ld, using %s of memory%s", ents,
+                         mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                                       ents * sizeof (struct peer_group)),
+                         VTY_NEWLINE);
+
+              if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+                vty_out (vty, "Dampening enabled.%s", VTY_NEWLINE);
+              vty_out (vty, "%s", VTY_NEWLINE);
+              vty_out (vty, "%s%s", header, VTY_NEWLINE);
+            }
+          
+         count++;
+
+         len = vty_out (vty, "%s", peer->host);
+         len = 16 - len;
+         if (len < 1)
+           vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " ");
+         else
+           vty_out (vty, "%*s", len, " ");
+
+         vty_out (vty, "4 ");
+
+         vty_out (vty, "%5u %7d %7d %8d %4d %4d ",
+                  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->sync[afi][safi]->update.count +
+                  peer->sync[afi][safi]->withdraw.count);
+
+         vty_out (vty, "%8s", 
+                  peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN));
+
+         if (peer->status == Established)
+           {
+             vty_out (vty, " %8ld", peer->pcount[afi][safi]);
+             totrcount += peer->pcount[afi][safi];
+             totecount++;
+           }
+         else
+           {
+             if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
+               vty_out (vty, " Idle (Admin)");
+             else if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+               vty_out (vty, " Idle (PfxCt)");
+             else
+               vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, peer->status));
+           }
+
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+
+  if (count)
+    {
+      vty_out (vty, "%sTotal number of neighbors %d%s", VTY_NEWLINE,
+              count, VTY_NEWLINE);
+      vty_out (vty, "%sTotal num. Established sessions %d%s", VTY_NEWLINE,
+              totecount, VTY_NEWLINE);
+      vty_out (vty, "Total num. of routes received     %d%s",
+               totrcount, VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "No %s neighbor is configured%s",
+            afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+static int 
+bgp_show_summary_vty (struct vty *vty, const char *name, 
+                      afi_t afi, safi_t safi)
+{
+  struct bgp *bgp;
+
+  if (name)
+    {
+      bgp = bgp_lookup_by_name (name);
+      
+      if (! bgp)
+       {
+         vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); 
+         return CMD_WARNING;
+       }
+
+      bgp_show_summary (vty, bgp, afi, safi);
+      return CMD_SUCCESS;
+    }
+  
+  bgp = bgp_get_default ();
+
+  if (bgp)
+    bgp_show_summary (vty, bgp, afi, safi);    
+  return CMD_SUCCESS;
+}
+
+/* `show ip bgp summary' commands. */
+DEFUN (show_ip_bgp_summary, 
+       show_ip_bgp_summary_cmd,
+       "show ip bgp summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_instance_summary,
+       show_ip_bgp_instance_summary_cmd,
+       "show ip bgp view WORD summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);  
+}
+
+DEFUN (show_ip_bgp_ipv4_summary, 
+       show_ip_bgp_ipv4_summary_cmd,
+       "show ip bgp ipv4 (unicast|multicast) summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_instance_ipv4_summary,
+       show_ip_bgp_instance_ipv4_summary_cmd,
+       "show ip bgp view WORD ipv4 (unicast|multicast) summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+  else
+    return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_vpnv4_all_summary,
+       show_ip_bgp_vpnv4_all_summary_cmd,
+       "show ip bgp vpnv4 all summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+}
+
+DEFUN (show_ip_bgp_vpnv4_rd_summary,
+       show_ip_bgp_vpnv4_rd_summary_cmd,
+       "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Summary of BGP neighbor status\n")
+{
+  int ret;
+  struct prefix_rd prd;
+
+  ret = str2prefix_rd (argv[0], &prd);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+}
+
+DEFUN (show_bgp_ipv4_safi_summary,
+       show_bgp_ipv4_safi_summary_cmd,
+       "show bgp ipv4 (unicast|multicast) summary",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_instance_ipv4_safi_summary,
+       show_bgp_instance_ipv4_safi_summary_cmd,
+       "show bgp view WORD ipv4 (unicast|multicast) summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+  else
+    return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_ipv4_vpn_summary,
+       show_bgp_ipv4_vpn_summary_cmd,
+       "show bgp ipv4 vpn summary",
+       SHOW_STR
+       BGP_STR
+       "IPv4\n"
+       "Display VPN NLRI specific information\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+}
+
+/* `show ip bgp summary' commands. */
+DEFUN (show_bgp_ipv6_vpn_summary,
+       show_bgp_ipv6_vpn_summary_cmd,
+       "show bgp ipv6 vpn summary",
+       SHOW_STR
+       BGP_STR
+       "IPv6\n"
+       "Display VPN NLRI specific information\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+}
+
+DEFUN (show_bgp_ipv4_encap_summary,
+       show_bgp_ipv4_encap_summary_cmd,
+       "show bgp ipv4 encap summary",
+       SHOW_STR
+       BGP_STR
+       "IPv4\n"
+       "Display ENCAP NLRI specific information\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP);
+}
+
+DEFUN (show_bgp_ipv6_encap_summary,
+       show_bgp_ipv6_encap_summary_cmd,
+       "show bgp ipv6 encap summary",
+       SHOW_STR
+       BGP_STR
+       "IPv6\n"
+       "Display ENCAP NLRI specific information\n"
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+}
+
+DEFUN (show_bgp_instance_summary,
+       show_bgp_instance_summary_cmd,
+       "show bgp view WORD summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Summary of BGP neighbor status\n")
+{
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP);
+
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_ipv4_summary,
+       show_bgp_instance_ipv4_summary_cmd,
+       "show bgp view WORD ipv4 summary",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "BGP view\n"
+       "View name\n"
+       "Summary of BGP neighbor status\n")
+{
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_ipv6_summary,
+       show_bgp_instance_ipv6_summary_cmd,
+       "show bgp view WORD ipv6 summary",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "BGP view\n"
+       "View name\n"
+       "Summary of BGP neighbor status\n")
+{
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv6_safi_summary,
+       show_bgp_ipv6_safi_summary_cmd,
+       "show bgp ipv6 (unicast|multicast) summary",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+
+  return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_instance_ipv6_safi_summary,
+       show_bgp_instance_ipv6_safi_summary_cmd,
+       "show bgp view WORD ipv6 (unicast|multicast) summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+
+  return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+DEFUN (show_ipv6_bgp_summary, 
+       show_ipv6_bgp_summary_cmd,
+       "show ipv6 bgp summary",
+       SHOW_STR
+       IPV6_STR
+       BGP_STR
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+}
+
+/* old command */
+DEFUN (show_ipv6_mbgp_summary, 
+       show_ipv6_mbgp_summary_cmd,
+       "show ipv6 mbgp summary",
+       SHOW_STR
+       IPV6_STR
+       MBGP_STR
+       "Summary of BGP neighbor status\n")
+{
+  return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+}
+
+/* variations of show bgp [...] summary */
+
+/* This one is for the 0-keyword variant */
+DEFUN (show_bgp_summary,
+       show_bgp_summary_cmd,
+       "show bgp summary",
+       SHOW_STR
+       BGP_STR
+       "Summary of BGP neighbor status\n")
+{
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP);
+
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+ALIAS (show_bgp_summary, 
+       show_bgp_ipv6_summary_cmd,
+       "show bgp ipv6 summary",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Summary of BGP neighbor status\n")
+
+DEFUN (show_bgp_summary_1w,
+       show_bgp_summary_1w_cmd,
+       "show bgp (ipv4|ipv6|unicast|multicast|vpn|encap) summary",
+       SHOW_STR
+       BGP_STR
+       IP_STR
+       IP6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Summary of BGP neighbor status\n")
+{
+  if (strcmp (argv[0], "ipv4") == 0) {
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP);
+    return CMD_SUCCESS;
+  }
+
+  if (strcmp (argv[0], "ipv6") == 0) {
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+    return CMD_SUCCESS;
+  }
+
+  if (strcmp (argv[0], "unicast") == 0) {
+    vty_out(vty, "IPv4 Unicast Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%s", VTY_NEWLINE);
+    vty_out(vty, "IPv6 Unicast Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+    return CMD_SUCCESS;
+  }
+  if (strcmp (argv[0], "multicast") == 0) {
+    vty_out(vty, "IPv4 Multicast Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%s", VTY_NEWLINE);
+    vty_out(vty, "IPv6 Multicast Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+    return CMD_SUCCESS;
+  }
+  if (strcmp (argv[0], "vpn") == 0) {
+    vty_out(vty, "IPv4 VPN Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%s", VTY_NEWLINE);
+    vty_out(vty, "IPv6 VPN Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+    return CMD_SUCCESS;
+  }
+  if (strcmp (argv[0], "encap") == 0) {
+    vty_out(vty, "IPv4 Encap Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP);
+    vty_out(vty, "%s", VTY_NEWLINE);
+    vty_out(vty, "IPv6 Encap Summary:%s", VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+    return CMD_SUCCESS;
+  }
+  vty_out(vty, "Unknown keyword: %s%s", argv[0], VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+
+
+const char *
+afi_safi_print (afi_t afi, safi_t safi)
+{
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    return "IPv4 Unicast";
+  else if (afi == AFI_IP && safi == SAFI_MULTICAST)
+    return "IPv4 Multicast";
+  else if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
+    return "VPN-IPv4 Unicast";
+  else if (afi == AFI_IP && safi == SAFI_ENCAP)
+    return "ENCAP-IPv4 Unicast";
+  else if (afi == AFI_IP6 && safi == SAFI_UNICAST)
+    return "IPv6 Unicast";
+  else if (afi == AFI_IP6 && safi == SAFI_MULTICAST)
+    return "IPv6 Multicast";
+  else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN)
+    return "VPN-IPv6 Unicast";
+  else if (afi == AFI_IP6 && safi == SAFI_ENCAP)
+    return "ENCAP-IPv6 Unicast";
+  else
+    return "Unknown";
+}
+
+/* Show BGP peer's information. */
+enum show_type
+{
+  show_all,
+  show_peer
+};
+
+static void
+bgp_show_peer_afi_orf_cap (struct vty *vty, struct peer *p,
+                          afi_t afi, safi_t safi,
+                          u_int16_t adv_smcap, u_int16_t adv_rmcap,
+                          u_int16_t rcv_smcap, u_int16_t rcv_rmcap)
+{
+  /* Send-Mode */
+  if (CHECK_FLAG (p->af_cap[afi][safi], adv_smcap)
+      || CHECK_FLAG (p->af_cap[afi][safi], rcv_smcap))
+    {
+      vty_out (vty, "      Send-mode: ");
+      if (CHECK_FLAG (p->af_cap[afi][safi], adv_smcap))
+       vty_out (vty, "advertised");
+      if (CHECK_FLAG (p->af_cap[afi][safi], rcv_smcap))
+       vty_out (vty, "%sreceived",
+                CHECK_FLAG (p->af_cap[afi][safi], adv_smcap) ?
+                ", " : "");
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  /* Receive-Mode */
+  if (CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap)
+      || CHECK_FLAG (p->af_cap[afi][safi], rcv_rmcap))
+    {
+      vty_out (vty, "      Receive-mode: ");
+      if (CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap))
+       vty_out (vty, "advertised");
+      if (CHECK_FLAG (p->af_cap[afi][safi], rcv_rmcap))
+       vty_out (vty, "%sreceived",
+                CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap) ?
+                ", " : "");
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+static void
+bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  char orf_pfx_name[BUFSIZ];
+  int orf_pfx_count;
+
+  filter = &p->filter[afi][safi];
+
+  vty_out (vty, " For address family: %s%s", afi_safi_print (afi, safi),
+          VTY_NEWLINE);
+
+  if (p->af_group[afi][safi])
+    vty_out (vty, "  %s peer-group member%s", p->group->name, VTY_NEWLINE);
+
+  if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+    vty_out (vty, "  AF-dependant capabilities:%s", VTY_NEWLINE);
+
+  if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
+    {
+      vty_out (vty, "    Outbound Route Filter (ORF) type (%d) Prefix-list:%s",
+              ORF_TYPE_PREFIX, VTY_NEWLINE);
+      bgp_show_peer_afi_orf_cap (vty, p, afi, safi,
+                                PEER_CAP_ORF_PREFIX_SM_ADV,
+                                PEER_CAP_ORF_PREFIX_RM_ADV,
+                                PEER_CAP_ORF_PREFIX_SM_RCV,
+                                PEER_CAP_ORF_PREFIX_RM_RCV);
+    }
+  if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)
+      || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+    {
+      vty_out (vty, "    Outbound Route Filter (ORF) type (%d) Prefix-list:%s",
+              ORF_TYPE_PREFIX_OLD, VTY_NEWLINE);
+      bgp_show_peer_afi_orf_cap (vty, p, afi, safi,
+                                PEER_CAP_ORF_PREFIX_SM_ADV,
+                                PEER_CAP_ORF_PREFIX_RM_ADV,
+                                PEER_CAP_ORF_PREFIX_SM_OLD_RCV,
+                                PEER_CAP_ORF_PREFIX_RM_OLD_RCV);
+    }
+
+  sprintf (orf_pfx_name, "%s.%d.%d", p->host, afi, safi);
+  orf_pfx_count =  prefix_bgp_show_prefix_list (NULL, afi, orf_pfx_name);
+
+  if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)
+      || orf_pfx_count)
+    {
+      vty_out (vty, "  Outbound Route Filter (ORF):");
+      if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
+         vty_out (vty, " sent;");
+      if (orf_pfx_count)
+       vty_out (vty, " received (%d entries)", orf_pfx_count);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH))
+      vty_out (vty, "  First update is deferred until ORF or ROUTE-REFRESH is received%s", VTY_NEWLINE);
+
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+    vty_out (vty, "  Route-Reflector Client%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    vty_out (vty, "  Route-Server Client%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+    vty_out (vty, "  Inbound soft reconfiguration allowed%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REMOVE_PRIVATE_AS))
+    vty_out (vty, "  Private AS number removed from updates to this neighbor%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF))
+    vty_out (vty, "  NEXT_HOP is always this router%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED))
+    vty_out (vty, "  AS_PATH is propagated unchanged to this neighbor%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED))
+    vty_out (vty, "  NEXT_HOP is propagated unchanged to this neighbor%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+    vty_out (vty, "  MED is propagated unchanged to this neighbor%s", VTY_NEWLINE);
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+      || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+      || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
+    {
+      vty_out (vty, "  Community attribute sent to this neighbor");
+      if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+         && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+         && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
+       vty_out (vty, "(all)%s", VTY_NEWLINE);
+      else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY))
+       vty_out (vty, "(extended)%s", VTY_NEWLINE);
+      else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
+       vty_out (vty, "(large)%s", VTY_NEWLINE);
+      else 
+       vty_out (vty, "(standard)%s", VTY_NEWLINE);
+    }
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
+    {
+      vty_out (vty, "  Default information originate,");
+
+      if (p->default_rmap[afi][safi].name)
+       vty_out (vty, " default route-map %s%s,",
+                p->default_rmap[afi][safi].map ? "*" : "",
+                p->default_rmap[afi][safi].name);
+      if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE))
+       vty_out (vty, " default sent%s", VTY_NEWLINE);
+      else
+       vty_out (vty, " default not sent%s", VTY_NEWLINE);
+    }
+
+  if (filter->plist[FILTER_IN].name
+      || filter->dlist[FILTER_IN].name
+      || filter->aslist[FILTER_IN].name
+      || filter->map[RMAP_IN].name)
+    vty_out (vty, "  Inbound path policy configured%s", VTY_NEWLINE);
+  if (filter->plist[FILTER_OUT].name
+      || filter->dlist[FILTER_OUT].name
+      || filter->aslist[FILTER_OUT].name
+      || filter->map[RMAP_OUT].name
+      || filter->usmap.name)
+    vty_out (vty, "  Outbound path policy configured%s", VTY_NEWLINE);
+  if (filter->map[RMAP_IMPORT].name)
+    vty_out (vty, "  Import policy for this RS-client configured%s", VTY_NEWLINE);
+  if (filter->map[RMAP_EXPORT].name)
+    vty_out (vty, "  Export policy for this RS-client configured%s", VTY_NEWLINE);
+
+  /* prefix-list */
+  if (filter->plist[FILTER_IN].name)
+    vty_out (vty, "  Incoming update prefix filter list is %s%s%s",
+            filter->plist[FILTER_IN].plist ? "*" : "",
+            filter->plist[FILTER_IN].name,
+            VTY_NEWLINE);
+  if (filter->plist[FILTER_OUT].name)
+    vty_out (vty, "  Outgoing update prefix filter list is %s%s%s",
+            filter->plist[FILTER_OUT].plist ? "*" : "",
+            filter->plist[FILTER_OUT].name,
+            VTY_NEWLINE);
+
+  /* distribute-list */
+  if (filter->dlist[FILTER_IN].name)
+    vty_out (vty, "  Incoming update network filter list is %s%s%s",
+            filter->dlist[FILTER_IN].alist ? "*" : "",
+            filter->dlist[FILTER_IN].name,
+            VTY_NEWLINE);
+  if (filter->dlist[FILTER_OUT].name)
+    vty_out (vty, "  Outgoing update network filter list is %s%s%s",
+            filter->dlist[FILTER_OUT].alist ? "*" : "",
+            filter->dlist[FILTER_OUT].name,
+            VTY_NEWLINE);
+
+  /* filter-list. */
+  if (filter->aslist[FILTER_IN].name)
+    vty_out (vty, "  Incoming update AS path filter list is %s%s%s",
+            filter->aslist[FILTER_IN].aslist ? "*" : "",
+            filter->aslist[FILTER_IN].name,
+            VTY_NEWLINE);
+  if (filter->aslist[FILTER_OUT].name)
+    vty_out (vty, "  Outgoing update AS path filter list is %s%s%s",
+            filter->aslist[FILTER_OUT].aslist ? "*" : "",
+            filter->aslist[FILTER_OUT].name,
+            VTY_NEWLINE);
+
+  /* route-map. */
+  if (filter->map[RMAP_IN].name)
+    vty_out (vty, "  Route map for incoming advertisements is %s%s%s",
+            filter->map[RMAP_IN].map ? "*" : "",
+            filter->map[RMAP_IN].name,
+            VTY_NEWLINE);
+  if (filter->map[RMAP_OUT].name)
+    vty_out (vty, "  Route map for outgoing advertisements is %s%s%s",
+            filter->map[RMAP_OUT].map ? "*" : "",
+            filter->map[RMAP_OUT].name,
+            VTY_NEWLINE);
+  if (filter->map[RMAP_IMPORT].name)
+    vty_out (vty, "  Route map for advertisements going into this RS-client's table is %s%s%s",
+            filter->map[RMAP_IMPORT].map ? "*" : "",
+            filter->map[RMAP_IMPORT].name,
+            VTY_NEWLINE);
+  if (filter->map[RMAP_EXPORT].name)
+    vty_out (vty, "  Route map for advertisements coming from this RS-client is %s%s%s",
+            filter->map[RMAP_EXPORT].map ? "*" : "",
+            filter->map[RMAP_EXPORT].name,
+            VTY_NEWLINE);
+
+  /* unsuppress-map */
+  if (filter->usmap.name)
+    vty_out (vty, "  Route map for selective unsuppress is %s%s%s",
+            filter->usmap.map ? "*" : "",
+            filter->usmap.name, VTY_NEWLINE);
+
+  /* Receive prefix count */
+  vty_out (vty, "  %ld accepted prefixes%s", p->pcount[afi][safi], VTY_NEWLINE);
+
+  /* Maximum prefix */
+  if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
+    {
+      vty_out (vty, "  Maximum prefixes allowed %ld%s%s", p->pmax[afi][safi],
+              CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)
+              ? " (warning-only)" : "", VTY_NEWLINE);
+      vty_out (vty, "  Threshold for warning message %d%%",
+              p->pmax_threshold[afi][safi]);
+      if (p->pmax_restart[afi][safi])
+       vty_out (vty, ", restart interval %d min", p->pmax_restart[afi][safi]);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+bgp_show_peer (struct vty *vty, struct peer *p)
+{
+  struct bgp *bgp;
+  char buf1[BUFSIZ];
+  char timebuf[BGP_UPTIME_LEN];
+  afi_t afi;
+  safi_t safi;
+  int ttl;
+
+  bgp = p->bgp;
+
+  /* Configured IP address. */
+  vty_out (vty, "BGP neighbor is %s, ", p->host);
+  vty_out (vty, "remote AS %u, ", p->as);
+  vty_out (vty, "local AS %u%s%s, ",
+          p->change_local_as ? p->change_local_as : p->local_as,
+          CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ?
+          " no-prepend" : "",
+          CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ?
+          " replace-as" : "");
+  vty_out (vty, "%s link%s",
+          p->as == p->local_as ? "internal" : "external",
+          VTY_NEWLINE);
+
+  /* Description. */
+  if (p->desc)
+    vty_out (vty, " Description: %s%s", p->desc, VTY_NEWLINE);
+  
+  /* Peer-group */
+  if (p->group)
+    vty_out (vty, " Member of peer-group %s for session parameters%s",
+            p->group->name, VTY_NEWLINE);
+
+  /* Administrative shutdown. */
+  if (CHECK_FLAG (p->flags, PEER_FLAG_SHUTDOWN))
+    vty_out (vty, " Administratively shut down%s", VTY_NEWLINE);
+
+  /* BGP Version. */
+  vty_out (vty, "  BGP version 4");
+  vty_out (vty, ", remote router ID %s%s", 
+          inet_ntop (AF_INET, &p->remote_id, buf1, BUFSIZ),
+          VTY_NEWLINE);
+
+  /* Confederation */
+  if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)
+      && bgp_confederation_peers_check (bgp, p->as))
+    vty_out (vty, "  Neighbor under common administration%s", VTY_NEWLINE);
+  
+  /* Status. */
+  vty_out (vty, "  BGP state = %s",  
+          LOOKUP (bgp_status_msg, p->status));
+  if (p->status == Established) 
+    vty_out (vty, ", up for %8s", 
+            peer_uptime (p->uptime, timebuf, BGP_UPTIME_LEN));
+  else if (p->status == Active)
+    {
+      if (CHECK_FLAG (p->flags, PEER_FLAG_PASSIVE))
+       vty_out (vty, " (passive)"); 
+      else if (CHECK_FLAG (p->sflags, PEER_STATUS_NSF_WAIT))
+       vty_out (vty, " (NSF passive)"); 
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+  
+  /* read timer */
+  vty_out (vty, "  Last read %s", peer_uptime (p->readtime, timebuf, BGP_UPTIME_LEN));
+
+  /* Configured timer values. */
+  vty_out (vty, ", hold time is %d, keepalive interval is %d seconds%s",
+          p->v_holdtime, p->v_keepalive, VTY_NEWLINE);
+  if (CHECK_FLAG (p->config, PEER_CONFIG_TIMER))
+    {
+      vty_out (vty, "  Configured hold time is %d", p->holdtime);
+      vty_out (vty, ", keepalive interval is %d seconds%s",
+              p->keepalive, VTY_NEWLINE);
+    }
+
+  /* Capability. */
+  if (p->status == Established) 
+    {
+      if (p->cap
+         || p->afc_adv[AFI_IP][SAFI_UNICAST]
+         || p->afc_recv[AFI_IP][SAFI_UNICAST]
+         || p->afc_adv[AFI_IP][SAFI_MULTICAST]
+         || p->afc_recv[AFI_IP][SAFI_MULTICAST]
+         || p->afc_adv[AFI_IP6][SAFI_UNICAST]
+         || p->afc_recv[AFI_IP6][SAFI_UNICAST]
+         || p->afc_adv[AFI_IP6][SAFI_MULTICAST]
+         || p->afc_recv[AFI_IP6][SAFI_MULTICAST]
+         || p->afc_adv[AFI_IP6][SAFI_MPLS_VPN]
+         || p->afc_recv[AFI_IP6][SAFI_MPLS_VPN]
+         || p->afc_adv[AFI_IP6][SAFI_ENCAP]
+         || p->afc_recv[AFI_IP6][SAFI_ENCAP]
+         || p->afc_adv[AFI_IP][SAFI_ENCAP]
+         || p->afc_recv[AFI_IP][SAFI_ENCAP]
+         || p->afc_adv[AFI_IP][SAFI_MPLS_VPN]
+         || p->afc_recv[AFI_IP][SAFI_MPLS_VPN])
+       {
+         vty_out (vty, "  Neighbor capabilities:%s", VTY_NEWLINE);
+
+         /* AS4 */
+         if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV)
+             || CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV))
+           {
+             vty_out (vty, "    4 Byte AS:");
+             if (CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV))
+               vty_out (vty, " advertised");
+             if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV))
+               vty_out (vty, " %sreceived",
+                        CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV) ? "and " : "");
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+         /* Dynamic */
+         if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_RCV)
+             || CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV))
+           {
+             vty_out (vty, "    Dynamic:");
+             if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV))
+               vty_out (vty, " advertised");
+             if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_RCV))
+               vty_out (vty, " %sreceived",
+                        CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV) ? "and " : "");
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+
+         /* Route Refresh */
+         if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV)
+             || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV)
+             || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV))
+           {
+             vty_out (vty, "    Route refresh:");
+             if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV))
+               vty_out (vty, " advertised");
+             if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV)
+                 || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV))
+               vty_out (vty, " %sreceived(%s)",
+                        CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV) ? "and " : "",
+                        (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV)
+                         && CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV)) ?
+                        "old & new" : CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV) ? "old" : "new");
+
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+
+         /* Multiprotocol Extensions */
+         for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+           for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+             if (p->afc_adv[afi][safi] || p->afc_recv[afi][safi])
+               {
+                 vty_out (vty, "    Address family %s:", afi_safi_print (afi, safi));
+                 if (p->afc_adv[afi][safi]) 
+                   vty_out (vty, " advertised");
+                 if (p->afc_recv[afi][safi])
+                   vty_out (vty, " %sreceived", p->afc_adv[afi][safi] ? "and " : "");
+                 vty_out (vty, "%s", VTY_NEWLINE);
+               } 
+
+         /* Gracefull Restart */
+         if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV)
+             || CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV))
+           {
+             vty_out (vty, "    Graceful Restart Capabilty:");
+             if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV))
+               vty_out (vty, " advertised");
+             if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV))
+               vty_out (vty, " %sreceived",
+                        CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV) ? "and " : "");
+             vty_out (vty, "%s", VTY_NEWLINE);
+
+             if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV))
+               {
+                 int restart_af_count = 0;
+
+                 vty_out (vty, "      Remote Restart timer is %d seconds%s",
+                          p->v_gr_restart, VTY_NEWLINE);       
+                 vty_out (vty, "      Address families by peer:%s        ", VTY_NEWLINE);
+
+                 for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+                   for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+                     if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV))
+                       {
+                         vty_out (vty, "%s%s(%s)", restart_af_count ? ", " : "",
+                                  afi_safi_print (afi, safi),
+                                  CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV) ?
+                                  "preserved" : "not preserved");
+                         restart_af_count++;
+                       }
+                 if (! restart_af_count)
+                   vty_out (vty, "none");
+                 vty_out (vty, "%s", VTY_NEWLINE);
+               }
+           }
+       }
+    }
+
+  /* graceful restart information */
+  if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV)
+      || p->t_gr_restart
+      || p->t_gr_stale)
+    {
+      int eor_send_af_count = 0;
+      int eor_receive_af_count = 0;
+
+      vty_out (vty, "  Graceful restart informations:%s", VTY_NEWLINE);
+      if (p->status == Established) 
+       {
+         vty_out (vty, "    End-of-RIB send: ");
+         for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+           for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+             if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_EOR_SEND))
+               {
+                 vty_out (vty, "%s%s", eor_send_af_count ? ", " : "",
+                          afi_safi_print (afi, safi));
+                 eor_send_af_count++;
+               }
+         vty_out (vty, "%s", VTY_NEWLINE);
+
+         vty_out (vty, "    End-of-RIB received: ");
+         for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+           for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+             if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED))
+               {
+                 vty_out (vty, "%s%s", eor_receive_af_count ? ", " : "",
+                          afi_safi_print (afi, safi));
+                 eor_receive_af_count++;
+               }
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+
+      if (p->t_gr_restart)
+        vty_out (vty, "    The remaining time of restart timer is %ld%s",
+                 thread_timer_remain_second (p->t_gr_restart), VTY_NEWLINE);
+      
+      if (p->t_gr_stale)
+        vty_out (vty, "    The remaining time of stalepath timer is %ld%s",
+                 thread_timer_remain_second (p->t_gr_stale), VTY_NEWLINE);
+    }
+
+  /* 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 %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);
+  vty_out (vty, "    Updates:       %10d %10d%s", p->update_out, p->update_in, VTY_NEWLINE);
+  vty_out (vty, "    Keepalives:    %10d %10d%s", p->keepalive_out, p->keepalive_in, VTY_NEWLINE);
+  vty_out (vty, "    Route Refresh: %10d %10d%s", p->refresh_out, p->refresh_in, VTY_NEWLINE);
+  vty_out (vty, "    Capability:    %10d %10d%s", p->dynamic_cap_out, p->dynamic_cap_in, VTY_NEWLINE);
+  vty_out (vty, "    Total:         %10d %10d%s", p->open_out + p->notify_out +
+          p->update_out + p->keepalive_out + p->refresh_out + p->dynamic_cap_out,
+          p->open_in + p->notify_in + p->update_in + p->keepalive_in + p->refresh_in +
+          p->dynamic_cap_in, VTY_NEWLINE);
+
+  /* advertisement-interval */
+  vty_out (vty, "  Minimum time between advertisement runs is %d seconds%s",
+          p->v_routeadv, VTY_NEWLINE);
+
+  /* Update-source. */
+  if (p->update_if || p->update_source)
+    {
+      vty_out (vty, "  Update source is ");
+      if (p->update_if)
+       vty_out (vty, "%s", p->update_if);
+      else if (p->update_source)
+       vty_out (vty, "%s",
+                sockunion2str (p->update_source, buf1, SU_ADDRSTRLEN));
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  /* Default weight */
+  if (CHECK_FLAG (p->config, PEER_CONFIG_WEIGHT))
+    vty_out (vty, "  Default weight %d%s", p->weight,
+            VTY_NEWLINE);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  /* Address Family Information */
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+      if (p->afc[afi][safi])
+       bgp_show_peer_afi (vty, p, afi, safi);
+
+  vty_out (vty, "  Connections established %d; dropped %d%s",
+          p->established, p->dropped,
+          VTY_NEWLINE);
+
+  if (! p->dropped)
+    vty_out (vty, "  Last reset never%s", VTY_NEWLINE);
+  else
+    vty_out (vty, "  Last reset %s, due to %s%s",
+            peer_uptime (p->resettime, timebuf, BGP_UPTIME_LEN),
+            peer_down_str[(int) p->last_reset], VTY_NEWLINE);
+
+  if (CHECK_FLAG (p->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+    {
+      vty_out (vty, "  Peer had exceeded the max. no. of prefixes configured.%s", VTY_NEWLINE);
+
+      if (p->t_pmax_restart)
+       vty_out (vty, "  Reduce the no. of prefix from %s, will restart in %ld seconds%s",
+                p->host, thread_timer_remain_second (p->t_pmax_restart),
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "  Reduce the no. of prefix and clear ip bgp %s to restore peering%s",
+                p->host, VTY_NEWLINE);
+    }
+
+  /* EBGP Multihop and GTSM */
+  ttl = p->gtsm_hops;
+  if (! ttl)
+    ttl = peer_ttl (p);
+  vty_out (vty, "  %s BGP neighbor may be up to %d hops away.%s",
+           p->sort == BGP_PEER_IBGP ? "Internal" : "External",
+           ttl, VTY_NEWLINE);
+
+  /* Local address. */
+  if (p->su_local)
+    {
+      vty_out (vty, "Local host: %s, Local port: %d%s",
+              sockunion2str (p->su_local, buf1, SU_ADDRSTRLEN),
+              ntohs (p->su_local->sin.sin_port),
+              VTY_NEWLINE);
+    }
+      
+  /* Remote address. */
+  if (p->su_remote)
+    {
+      vty_out (vty, "Foreign host: %s, Foreign port: %d%s",
+              sockunion2str (p->su_remote, buf1, SU_ADDRSTRLEN),
+              ntohs (p->su_remote->sin.sin_port),
+              VTY_NEWLINE);
+    }
+
+  /* Nexthop display. */
+  if (p->su_local)
+    {
+      vty_out (vty, "Nexthop: %s%s", 
+              inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ),
+              VTY_NEWLINE);
+      vty_out (vty, "Nexthop global: %s%s", 
+              inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ),
+              VTY_NEWLINE);
+      vty_out (vty, "Nexthop local: %s%s",
+              inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, BUFSIZ),
+              VTY_NEWLINE);
+      vty_out (vty, "BGP connection: %s%s",
+              p->shared_network ? "shared network" : "non shared network",
+              VTY_NEWLINE);
+    }
+
+  /* TCP metrics. */
+  if (p->status == Established && p->rtt)
+    vty_out (vty, "Estimated round trip time: %d ms%s",
+            p->rtt, VTY_NEWLINE);
+
+  /* Timer information. */
+  if (p->t_start)
+    vty_out (vty, "Next start timer due in %ld seconds%s",
+            thread_timer_remain_second (p->t_start), VTY_NEWLINE);
+  if (p->t_connect)
+    vty_out (vty, "Next connect timer due in %ld seconds%s",
+            thread_timer_remain_second (p->t_connect), VTY_NEWLINE);
+  
+  vty_out (vty, "Read thread: %s  Write thread: %s%s", 
+          p->t_read ? "on" : "off",
+          p->t_write ? "on" : "off",
+          VTY_NEWLINE);
+
+  if (p->notify.code == BGP_NOTIFY_OPEN_ERR
+      && p->notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL)
+    bgp_capability_vty_out (vty, p);
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static int
+bgp_show_neighbor (struct vty *vty, struct bgp *bgp,
+                  enum show_type type, union sockunion *su)
+{
+  struct listnode *node, *nnode;
+  struct peer *peer;
+  int find = 0;
+
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      switch (type)
+       {
+       case show_all:
+         bgp_show_peer (vty, peer);
+         break;
+       case show_peer:
+         if (sockunion_same (&peer->su, su))
+           {
+             find = 1;
+             bgp_show_peer (vty, peer);
+           }
+         break;
+       }
+    }
+
+  if (type == show_peer && ! find)
+    vty_out (vty, "%% No such neighbor%s", VTY_NEWLINE);
+  
+  return CMD_SUCCESS;
+}
+
+static int 
+bgp_show_neighbor_vty (struct vty *vty, const char *name, 
+                       enum show_type type, const char *ip_str)
+{
+  int ret;
+  struct bgp *bgp;
+  union sockunion su;
+
+  if (ip_str)
+    {
+      ret = str2sockunion (ip_str, &su);
+      if (ret < 0)
+        {
+          vty_out (vty, "%% Malformed address: %s%s", ip_str, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (name)
+    {
+      bgp = bgp_lookup_by_name (name);
+      
+      if (! bgp)
+        {
+          vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); 
+          return CMD_WARNING;
+        }
+
+      bgp_show_neighbor (vty, bgp, type, &su);
+
+      return CMD_SUCCESS;
+    }
+
+  bgp = bgp_get_default ();
+
+  if (bgp)
+    bgp_show_neighbor (vty, bgp, type, &su);
+
+  return CMD_SUCCESS;
+}
+
+/* "show ip bgp neighbors" commands.  */DEFUN (show_ip_bgp_neighbors,
+       show_ip_bgp_neighbors_cmd,
+       "show ip bgp neighbors",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n")
+{
+  return bgp_show_neighbor_vty (vty, NULL, show_all, NULL);
+}
+
+ALIAS (show_ip_bgp_neighbors,
+       show_ip_bgp_ipv4_neighbors_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+
+ALIAS (show_ip_bgp_neighbors,
+       show_ip_bgp_vpnv4_all_neighbors_cmd,
+       "show ip bgp vpnv4 all neighbors",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+
+ALIAS (show_ip_bgp_neighbors,
+       show_ip_bgp_vpnv4_rd_neighbors_cmd,
+       "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information for a route distinguisher\n"
+       "VPN Route Distinguisher\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+
+ALIAS (show_ip_bgp_neighbors,
+       show_bgp_ipv6_neighbors_cmd,
+       "show bgp ipv6 neighbors",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+
+DEFUN (show_ip_bgp_neighbors_peer,
+       show_ip_bgp_neighbors_peer_cmd,
+       "show ip bgp neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+{
+  return bgp_show_neighbor_vty (vty, NULL, show_peer, argv[argc - 1]);
+}
+
+ALIAS (show_ip_bgp_neighbors_peer,
+       show_ip_bgp_ipv4_neighbors_peer_cmd,
+       "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+
+ALIAS (show_ip_bgp_neighbors_peer,
+       show_ip_bgp_vpnv4_all_neighbors_peer_cmd,
+       "show ip bgp vpnv4 all neighbors A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n")
+
+ALIAS (show_ip_bgp_neighbors_peer,
+       show_ip_bgp_vpnv4_rd_neighbors_peer_cmd,
+       "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors A.B.C.D",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Display VPNv4 NLRI specific information\n"
+       "Display information about all VPNv4 NLRIs\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n")
+
+ALIAS (show_ip_bgp_neighbors_peer,
+       show_bgp_ipv6_neighbors_peer_cmd,
+       "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+
+DEFUN (show_ip_bgp_instance_neighbors,
+       show_ip_bgp_instance_neighbors_cmd,
+       "show ip bgp view WORD neighbors",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+{
+  return bgp_show_neighbor_vty (vty, argv[0], show_all, NULL);
+}
+
+ALIAS (show_ip_bgp_instance_neighbors,
+       show_bgp_instance_ipv6_neighbors_cmd,
+       "show bgp view WORD ipv6 neighbors",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+
+DEFUN (show_ip_bgp_instance_neighbors_peer,
+       show_ip_bgp_instance_neighbors_peer_cmd,
+       "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+{
+  return bgp_show_neighbor_vty (vty, argv[0], show_peer, argv[1]);
+}
+
+/* Show BGP's AS paths internal data.  There are both `show ip bgp
+   paths' and `show ip mbgp paths'.  Those functions results are the
+   same.*/
+DEFUN (show_ip_bgp_paths, 
+       show_ip_bgp_paths_cmd,
+       "show ip bgp paths",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Path information\n")
+{
+  vty_out (vty, "Address Refcnt Path%s", VTY_NEWLINE);
+  aspath_print_all_vty (vty);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_ipv4_paths, 
+       show_ip_bgp_ipv4_paths_cmd,
+       "show ip bgp ipv4 (unicast|multicast) paths",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Path information\n")
+{
+  vty_out (vty, "Address Refcnt Path\r\n");
+  aspath_print_all_vty (vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_neighbors,
+       show_bgp_neighbors_cmd,
+       "show bgp neighbors",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n")
+{
+  return bgp_show_neighbor_vty (vty, NULL, show_all, NULL);
+}
+
+DEFUN (show_bgp_neighbors_peer,
+       show_bgp_neighbors_peer_cmd,
+       "show bgp neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+{
+  return bgp_show_neighbor_vty (vty, NULL, show_peer, argv[argc - 1]);
+}
+
+DEFUN (show_bgp_instance_neighbors,
+       show_bgp_instance_neighbors_cmd,
+       "show bgp view WORD neighbors",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n")
+{
+  return bgp_show_neighbor_vty (vty, argv[0], show_all, NULL);
+}
+
+DEFUN (show_bgp_instance_neighbors_peer,
+       show_bgp_instance_neighbors_peer_cmd,
+       "show bgp view WORD neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+{
+  return bgp_show_neighbor_vty (vty, argv[0], show_peer, argv[1]);
+}
+
+ALIAS (show_bgp_instance_neighbors_peer,
+       show_bgp_instance_ipv6_neighbors_peer_cmd,
+       "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Detailed information on TCP and BGP neighbor connections\n"
+       "Neighbor to display information about\n"
+       "Neighbor to display information about\n")
+       
+/* Show BGP's AS paths internal data.  There are both `show ip bgp
+   paths' and `show ip mbgp paths'.  Those functions results are the
+   same.*/
+DEFUN (show_bgp_ipv4_paths, 
+       show_bgp_ipv4_paths_cmd,
+       "show bgp paths",
+       SHOW_STR
+       BGP_STR
+       "Path information\n")
+{
+  vty_out (vty, "Address Refcnt Path%s", VTY_NEWLINE);
+  aspath_print_all_vty (vty);
+  return CMD_SUCCESS;
+}
+
+#include "hash.h"
+
+static void
+community_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+  struct community *com;
+
+  com = (struct community *) backet->data;
+  vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, com->refcnt,
+          community_str (com), VTY_NEWLINE);
+}
+
+/* Show BGP's community internal data. */
+DEFUN (show_ip_bgp_community_info, 
+       show_ip_bgp_community_info_cmd,
+       "show ip bgp community-info",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "List all bgp community information\n")
+{
+  vty_out (vty, "Address Refcnt Community%s", VTY_NEWLINE);
+
+  hash_iterate (community_hash (), 
+               (void (*) (struct hash_backet *, void *))
+               community_show_all_iterator,
+               vty);
+
+  return CMD_SUCCESS;
+}
+
+static void
+lcommunity_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+  struct lcommunity *lcom;
+
+  lcom = (struct lcommunity *) backet->data;
+  vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, lcom->refcnt,
+          lcommunity_str (lcom), VTY_NEWLINE);
+}
+
+/* Show BGP's community internal data. */
+DEFUN (show_ip_bgp_lcommunity_info,
+       show_ip_bgp_lcommunity_info_cmd,
+       "show ip bgp large-community-info",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "List all bgp large-community information\n")
+{
+  vty_out (vty, "Address Refcnt Large-community%s", VTY_NEWLINE);
+
+  hash_iterate (lcommunity_hash (),
+               (void (*) (struct hash_backet *, void *))
+               lcommunity_show_all_iterator,
+               vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_attr_info, 
+       show_ip_bgp_attr_info_cmd,
+       "show ip bgp attribute-info",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "List all bgp attribute information\n")
+{
+  attr_show_all (vty);
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient,
+        afi_t afi, safi_t safi)
+{
+  char timebuf[BGP_UPTIME_LEN];
+  char rmbuf[14];
+  const char *rmname;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  int len;
+  int count = 0;
+
+  if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_GROUP))
+    {
+      for (ALL_LIST_ELEMENTS (rsclient->group->peer, node, nnode, peer))
+        {
+          count++;
+          bgp_write_rsclient_summary (vty, peer, afi, safi);
+        }
+      return count;
+    }
+
+  len = vty_out (vty, "%s", rsclient->host);
+  len = 16 - len;
+
+  if (len < 1)
+    vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " ");
+  else
+    vty_out (vty, "%*s", len, " ");
+
+  vty_out (vty, "4 ");
+
+  vty_out (vty, "%10u ", rsclient->as);
+
+  rmname = ROUTE_MAP_EXPORT_NAME(&rsclient->filter[afi][safi]);
+  if ( rmname && strlen (rmname) > 13 )
+    {
+      sprintf (rmbuf, "%13s", "...");
+      rmname = strncpy (rmbuf, rmname, 10);
+    }
+  else if (! rmname)
+    rmname = "<none>";
+  vty_out (vty, " %13s ", rmname);
+
+  rmname = ROUTE_MAP_IMPORT_NAME(&rsclient->filter[afi][safi]);
+  if ( rmname && strlen (rmname) > 13 )
+    {
+      sprintf (rmbuf, "%13s", "...");
+      rmname = strncpy (rmbuf, rmname, 10);
+    }
+  else if (! rmname)
+    rmname = "<none>";
+  vty_out (vty, " %13s ", rmname);
+
+  vty_out (vty, "%8s", peer_uptime (rsclient->uptime, timebuf, BGP_UPTIME_LEN));
+
+  if (CHECK_FLAG (rsclient->flags, PEER_FLAG_SHUTDOWN))
+    vty_out (vty, " Idle (Admin)");
+  else if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+    vty_out (vty, " Idle (PfxCt)");
+  else
+    vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, rsclient->status));
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  return 1;
+}
+
+static int
+bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp, 
+                           afi_t afi, safi_t safi)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  int count = 0;
+
+  /* Header string for each address family. */
+  static char header[] = "Neighbor        V         AS  Export-Policy  Import-Policy  Up/Down  State";
+
+  for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, peer))
+    {
+      if (peer->afc[afi][safi] &&
+         CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+       {
+         if (! count)
+           {
+             vty_out (vty,
+                      "Route Server's BGP router identifier %s%s",
+                      inet_ntoa (bgp->router_id), VTY_NEWLINE);
+             vty_out (vty,
+              "Route Server's local AS number %u%s", bgp->as,
+                       VTY_NEWLINE);
+
+             vty_out (vty, "%s", VTY_NEWLINE);
+             vty_out (vty, "%s%s", header, VTY_NEWLINE);
+           }
+
+         count += bgp_write_rsclient_summary (vty, peer, afi, safi);
+       }
+    }
+
+  if (count)
+    vty_out (vty, "%sTotal number of Route Server Clients %d%s", VTY_NEWLINE,
+            count, VTY_NEWLINE);
+  else
+    vty_out (vty, "No %s Route Server Client is configured%s",
+            afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_show_rsclient_summary_vty (struct vty *vty, const char *name, 
+                               afi_t afi, safi_t safi)
+{
+  struct bgp *bgp;
+
+  if (name)
+    {
+      bgp = bgp_lookup_by_name (name);
+
+      if (! bgp)
+       {
+         vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      bgp_show_rsclient_summary (vty, bgp, afi, safi);
+      return CMD_SUCCESS;
+    }
+
+  bgp = bgp_get_default ();
+
+  if (bgp)
+    bgp_show_rsclient_summary (vty, bgp, afi, safi);
+
+  return CMD_SUCCESS;
+}
+
+/* 'show bgp rsclient' commands. */
+DEFUN (show_ip_bgp_rsclient_summary,
+       show_ip_bgp_rsclient_summary_cmd,
+       "show ip bgp rsclient summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_instance_rsclient_summary,
+       show_ip_bgp_instance_rsclient_summary_cmd,
+       "show ip bgp view WORD rsclient summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_ipv4_rsclient_summary,
+      show_ip_bgp_ipv4_rsclient_summary_cmd,
+      "show ip bgp ipv4 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  if (strncmp (argv[0], "m", 1) == 0)
+    return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_ip_bgp_instance_ipv4_rsclient_summary,
+      show_ip_bgp_instance_ipv4_rsclient_summary_cmd,
+      "show ip bgp view WORD ipv4 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  if (strncmp (argv[1], "m", 1) == 0)
+    return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+
+  return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+}
+
+DEFUN (show_bgp_instance_ipv4_safi_rsclient_summary,
+       show_bgp_instance_ipv4_safi_rsclient_summary_cmd,
+       "show bgp view WORD ipv4 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  safi_t safi;
+
+  if (argc == 2) {
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+    return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, safi);
+  } else {
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+    return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, safi);
+  }
+}
+
+ALIAS (show_bgp_instance_ipv4_safi_rsclient_summary,
+       show_bgp_ipv4_safi_rsclient_summary_cmd,
+       "show bgp ipv4 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+
+DEFUN (show_bgp_rsclient_summary,
+       show_bgp_rsclient_summary_cmd,
+       "show bgp rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP);
+
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_rsclient_summary,
+       show_bgp_instance_rsclient_summary_cmd,
+       "show bgp view WORD rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+    vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST);
+    vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP);
+
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_ipv6_rsclient_summary,
+      show_bgp_ipv6_rsclient_summary_cmd,
+      "show bgp ipv6 rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "Address family\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_ipv6_rsclient_summary,
+      show_bgp_instance_ipv6_rsclient_summary_cmd,
+       "show bgp view WORD ipv6 rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+    vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "---------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST);
+    vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST);
+    vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-----------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN);
+    vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE);
+    vty_out(vty, "-------------------%s", VTY_NEWLINE);
+    bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP);
+
+    return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_ipv6_safi_rsclient_summary,
+       show_bgp_instance_ipv6_safi_rsclient_summary_cmd,
+       "show bgp view WORD ipv6 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       BGP_STR
+       "BGP view\n"
+       "View name\n"
+       "Address family\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+{
+  safi_t safi;
+
+  if (argc == 2) {
+    safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+    return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, safi);
+  } else {
+    safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST;
+    return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, safi);
+  }
+}
+
+ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary,
+       show_bgp_ipv6_safi_rsclient_summary_cmd,
+       "show bgp ipv6 (unicast|multicast) rsclient summary",
+       SHOW_STR
+       BGP_STR
+       IPV6_STR
+       "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Information about Route Server Clients\n"
+       "Summary of all Route Server Clients\n")
+
+/* Redistribute VTY commands.  */
+
+DEFUN (bgp_redistribute_ipv4,
+       bgp_redistribute_ipv4_cmd,
+       "redistribute " QUAGGA_IP_REDIST_STR_BGPD,
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD)
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_redistribute_set (vty->index, AFI_IP, type);
+}
+
+DEFUN (bgp_redistribute_ipv4_rmap,
+       bgp_redistribute_ipv4_rmap_cmd,
+       "redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_redistribute_rmap_set (vty->index, AFI_IP, type, argv[1]);
+  return bgp_redistribute_set (vty->index, AFI_IP, type);
+}
+
+DEFUN (bgp_redistribute_ipv4_metric,
+       bgp_redistribute_ipv4_metric_cmd,
+       "redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295>",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[1]);
+
+  bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric);
+  return bgp_redistribute_set (vty->index, AFI_IP, type);
+}
+
+DEFUN (bgp_redistribute_ipv4_rmap_metric,
+       bgp_redistribute_ipv4_rmap_metric_cmd,
+       "redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n"
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[2]);
+
+  bgp_redistribute_rmap_set (vty->index, AFI_IP, type, argv[1]);
+  bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric);
+  return bgp_redistribute_set (vty->index, AFI_IP, type);
+}
+
+DEFUN (bgp_redistribute_ipv4_metric_rmap,
+       bgp_redistribute_ipv4_metric_rmap_cmd,
+       "redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[1]);
+
+  bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric);
+  bgp_redistribute_rmap_set (vty->index, AFI_IP, type, argv[2]);
+  return bgp_redistribute_set (vty->index, AFI_IP, type);
+}
+
+DEFUN (no_bgp_redistribute_ipv4,
+       no_bgp_redistribute_ipv4_cmd,
+       "no redistribute " QUAGGA_IP_REDIST_STR_BGPD,
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD)
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_redistribute_unset (vty->index, AFI_IP, type);
+}
+
+ALIAS (no_bgp_redistribute_ipv4,
+       no_bgp_redistribute_ipv4_rmap_cmd,
+       "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+ALIAS (no_bgp_redistribute_ipv4,
+       no_bgp_redistribute_ipv4_metric_cmd,
+       "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295>",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+
+ALIAS (no_bgp_redistribute_ipv4,
+       no_bgp_redistribute_ipv4_rmap_metric_cmd,
+       "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n"
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+
+ALIAS (no_bgp_redistribute_ipv4,
+       no_bgp_redistribute_ipv4_metric_rmap_cmd,
+       "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+DEFUN (bgp_redistribute_ipv6,
+       bgp_redistribute_ipv6_cmd,
+       "redistribute " QUAGGA_IP6_REDIST_STR_BGPD,
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD)
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_redistribute_set (vty->index, AFI_IP6, type);
+}
+
+DEFUN (bgp_redistribute_ipv6_rmap,
+       bgp_redistribute_ipv6_rmap_cmd,
+       "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp_redistribute_rmap_set (vty->index, AFI_IP6, type, argv[1]);
+  return bgp_redistribute_set (vty->index, AFI_IP6, type);
+}
+
+DEFUN (bgp_redistribute_ipv6_metric,
+       bgp_redistribute_ipv6_metric_cmd,
+       "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295>",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[1]);
+
+  bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric);
+  return bgp_redistribute_set (vty->index, AFI_IP6, type);
+}
+
+DEFUN (bgp_redistribute_ipv6_rmap_metric,
+       bgp_redistribute_ipv6_rmap_metric_cmd,
+       "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n"
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[2]);
+
+  bgp_redistribute_rmap_set (vty->index, AFI_IP6, type, argv[1]);
+  bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric);
+  return bgp_redistribute_set (vty->index, AFI_IP6, type);
+}
+
+DEFUN (bgp_redistribute_ipv6_metric_rmap,
+       bgp_redistribute_ipv6_metric_rmap_cmd,
+       "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD",
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+  u_int32_t metric;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  VTY_GET_INTEGER ("metric", metric, argv[1]);
+
+  bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric);
+  bgp_redistribute_rmap_set (vty->index, AFI_IP6, type, argv[2]);
+  return bgp_redistribute_set (vty->index, AFI_IP6, type);
+}
+
+DEFUN (no_bgp_redistribute_ipv6,
+       no_bgp_redistribute_ipv6_cmd,
+       "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD,
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD)
+{
+  int type;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_BGP)
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_redistribute_unset (vty->index, AFI_IP6, type);
+}
+
+ALIAS (no_bgp_redistribute_ipv6,
+       no_bgp_redistribute_ipv6_rmap_cmd,
+       "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+ALIAS (no_bgp_redistribute_ipv6,
+       no_bgp_redistribute_ipv6_metric_cmd,
+       "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295>",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+
+ALIAS (no_bgp_redistribute_ipv6,
+       no_bgp_redistribute_ipv6_rmap_metric_cmd,
+       "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n"
+       "Metric for redistributed routes\n"
+       "Default metric\n")
+
+ALIAS (no_bgp_redistribute_ipv6,
+       no_bgp_redistribute_ipv6_metric_rmap_cmd,
+       "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       QUAGGA_IP6_REDIST_HELP_STR_BGPD
+       "Metric for redistributed routes\n"
+       "Default metric\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+int
+bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi,
+                              safi_t safi, int *write)
+{
+  int i;
+
+  /* Unicast redistribution only.  */
+  if (safi != SAFI_UNICAST)
+    return 0;
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    {
+      /* Redistribute BGP does not make sense.  */
+      if (bgp->redist[afi][i] && i != ZEBRA_ROUTE_BGP)
+       {
+         /* Display "address-family" when it is not yet diplayed.  */
+         bgp_config_write_family_header (vty, afi, safi, write);
+
+         /* "redistribute" configuration.  */
+         vty_out (vty, " redistribute %s", zebra_route_string(i));
+
+         if (bgp->redist_metric_flag[afi][i])
+           vty_out (vty, " metric %u", bgp->redist_metric[afi][i]);
+
+         if (bgp->rmap[afi][i].name)
+           vty_out (vty, " route-map %s", bgp->rmap[afi][i].name);
+
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+  return *write;
+}
+
+/* BGP node structure. */
+static struct cmd_node bgp_node =
+{
+  BGP_NODE,
+  "%s(config-router)# ",
+  1,
+};
+
+static struct cmd_node bgp_ipv4_unicast_node =
+{
+  BGP_IPV4_NODE,
+  "%s(config-router-af)# ",
+  1,
+};
+
+static struct cmd_node bgp_ipv4_multicast_node =
+{
+  BGP_IPV4M_NODE,
+  "%s(config-router-af)# ",
+  1,
+};
+
+static struct cmd_node bgp_ipv6_unicast_node =
+{
+  BGP_IPV6_NODE,
+  "%s(config-router-af)# ",
+  1,
+};
+
+static struct cmd_node bgp_ipv6_multicast_node =
+{
+  BGP_IPV6M_NODE,
+  "%s(config-router-af)# ",
+  1,
+};
+
+static struct cmd_node bgp_vpnv4_node =
+{
+  BGP_VPNV4_NODE,
+  "%s(config-router-af)# ",
+  1
+};
+
+static struct cmd_node bgp_vpnv6_node =
+{
+  BGP_VPNV6_NODE,
+  "%s(config-router-af-vpnv6)# ",
+  1
+};
+
+static struct cmd_node bgp_encap_node =
+{
+  BGP_ENCAP_NODE,
+  "%s(config-router-af-encap)# ",
+  1
+};
+
+static struct cmd_node bgp_encapv6_node =
+{
+  BGP_ENCAPV6_NODE,
+  "%s(config-router-af-encapv6)# ",
+  1
+};
+
+static void community_list_vty (void);
+
+void
+bgp_vty_init (void)
+{
+  /* Install bgp top node. */
+  install_node (&bgp_node, bgp_config_write);
+  install_node (&bgp_ipv4_unicast_node, NULL);
+  install_node (&bgp_ipv4_multicast_node, NULL);
+  install_node (&bgp_ipv6_unicast_node, NULL);
+  install_node (&bgp_ipv6_multicast_node, NULL);
+  install_node (&bgp_vpnv4_node, NULL);
+  install_node (&bgp_vpnv6_node, NULL);
+  install_node (&bgp_encap_node, NULL);
+  install_node (&bgp_encapv6_node, NULL);
+
+  /* Install default VTY commands to new nodes.  */
+  install_default (BGP_NODE);
+  install_default (BGP_IPV4_NODE);
+  install_default (BGP_IPV4M_NODE);
+  install_default (BGP_IPV6_NODE);
+  install_default (BGP_IPV6M_NODE);
+  install_default (BGP_VPNV4_NODE);
+  install_default (BGP_VPNV6_NODE);
+  install_default (BGP_ENCAP_NODE);
+  install_default (BGP_ENCAPV6_NODE);
+  
+  /* "bgp multiple-instance" commands. */
+  install_element (CONFIG_NODE, &bgp_multiple_instance_cmd);
+  install_element (CONFIG_NODE, &no_bgp_multiple_instance_cmd);
+
+  /* "bgp config-type" commands. */
+  install_element (CONFIG_NODE, &bgp_config_type_cmd);
+  install_element (CONFIG_NODE, &no_bgp_config_type_cmd);
+
+  /* Dummy commands (Currently not supported) */
+  install_element (BGP_NODE, &no_synchronization_cmd);
+  install_element (BGP_NODE, &no_auto_summary_cmd);
+
+  /* "router bgp" commands. */
+  install_element (CONFIG_NODE, &router_bgp_cmd);
+  install_element (CONFIG_NODE, &router_bgp_view_cmd);
+
+  /* "no router bgp" commands. */
+  install_element (CONFIG_NODE, &no_router_bgp_cmd);
+  install_element (CONFIG_NODE, &no_router_bgp_view_cmd);
+
+  /* "bgp router-id" commands. */
+  install_element (BGP_NODE, &bgp_router_id_cmd);
+  install_element (BGP_NODE, &no_bgp_router_id_cmd);
+  install_element (BGP_NODE, &no_bgp_router_id_val_cmd);
+
+  /* "bgp cluster-id" commands. */
+  install_element (BGP_NODE, &bgp_cluster_id_cmd);
+  install_element (BGP_NODE, &bgp_cluster_id32_cmd);
+  install_element (BGP_NODE, &no_bgp_cluster_id_cmd);
+  install_element (BGP_NODE, &no_bgp_cluster_id_arg_cmd);
+
+  /* "bgp confederation" commands. */
+  install_element (BGP_NODE, &bgp_confederation_identifier_cmd);
+  install_element (BGP_NODE, &no_bgp_confederation_identifier_cmd);
+  install_element (BGP_NODE, &no_bgp_confederation_identifier_arg_cmd);
+
+  /* "bgp confederation peers" commands. */
+  install_element (BGP_NODE, &bgp_confederation_peers_cmd);
+  install_element (BGP_NODE, &no_bgp_confederation_peers_cmd);
+
+  /* "maximum-paths" commands. */
+  install_element (BGP_NODE, &bgp_maxpaths_cmd);
+  install_element (BGP_NODE, &no_bgp_maxpaths_cmd);
+  install_element (BGP_NODE, &no_bgp_maxpaths_arg_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_maxpaths_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_arg_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_maxpaths_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_arg_cmd);
+  install_element (BGP_NODE, &bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_NODE, &no_bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_NODE, &no_bgp_maxpaths_ibgp_arg_cmd);
+  install_element (BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_arg_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_arg_cmd);
+
+  /* "timers bgp" commands. */
+  install_element (BGP_NODE, &bgp_timers_cmd);
+  install_element (BGP_NODE, &no_bgp_timers_cmd);
+  install_element (BGP_NODE, &no_bgp_timers_arg_cmd);
+
+  /* "bgp client-to-client reflection" commands */
+  install_element (BGP_NODE, &no_bgp_client_to_client_reflection_cmd);
+  install_element (BGP_NODE, &bgp_client_to_client_reflection_cmd);
+
+  /* "bgp always-compare-med" commands */
+  install_element (BGP_NODE, &bgp_always_compare_med_cmd);
+  install_element (BGP_NODE, &no_bgp_always_compare_med_cmd);
+  
+  /* "bgp deterministic-med" commands */
+  install_element (BGP_NODE, &bgp_deterministic_med_cmd);
+  install_element (BGP_NODE, &no_bgp_deterministic_med_cmd);
+
+  /* "bgp graceful-restart" commands */
+  install_element (BGP_NODE, &bgp_graceful_restart_cmd);
+  install_element (BGP_NODE, &no_bgp_graceful_restart_cmd);
+  install_element (BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd);
+  install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd);
+  install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_val_cmd);
+  install_element (BGP_NODE, &bgp_graceful_restart_restart_time_cmd);
+  install_element (BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd);
+  install_element (BGP_NODE, &no_bgp_graceful_restart_restart_time_val_cmd);
+  /* "bgp fast-external-failover" commands */
+  install_element (BGP_NODE, &bgp_fast_external_failover_cmd);
+  install_element (BGP_NODE, &no_bgp_fast_external_failover_cmd);
+
+  /* "bgp enforce-first-as" commands */
+  install_element (BGP_NODE, &bgp_enforce_first_as_cmd);
+  install_element (BGP_NODE, &no_bgp_enforce_first_as_cmd);
+
+  /* "bgp bestpath compare-routerid" commands */
+  install_element (BGP_NODE, &bgp_bestpath_compare_router_id_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd);
+
+  /* "bgp bestpath as-path ignore" commands */
+  install_element (BGP_NODE, &bgp_bestpath_aspath_ignore_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_aspath_ignore_cmd);
+
+  /* "bgp bestpath as-path confed" commands */
+  install_element (BGP_NODE, &bgp_bestpath_aspath_confed_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_aspath_confed_cmd);
+
+  /* "bgp bestpath as-path multipath-relax" commands */
+  install_element (BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd);
+
+  /* "bgp log-neighbor-changes" commands */
+  install_element (BGP_NODE, &bgp_log_neighbor_changes_cmd);
+  install_element (BGP_NODE, &no_bgp_log_neighbor_changes_cmd);
+
+  /* "bgp bestpath med" commands */
+  install_element (BGP_NODE, &bgp_bestpath_med_cmd);
+  install_element (BGP_NODE, &bgp_bestpath_med2_cmd);
+  install_element (BGP_NODE, &bgp_bestpath_med3_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_med_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_med2_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_med3_cmd);
+
+  /* "no bgp default ipv4-unicast" commands. */
+  install_element (BGP_NODE, &no_bgp_default_ipv4_unicast_cmd);
+  install_element (BGP_NODE, &bgp_default_ipv4_unicast_cmd);
+  
+  /* "bgp network import-check" commands. */
+  install_element (BGP_NODE, &bgp_network_import_check_cmd);
+  install_element (BGP_NODE, &no_bgp_network_import_check_cmd);
+
+  /* "bgp default local-preference" commands. */
+  install_element (BGP_NODE, &bgp_default_local_preference_cmd);
+  install_element (BGP_NODE, &no_bgp_default_local_preference_cmd);
+  install_element (BGP_NODE, &no_bgp_default_local_preference_val_cmd);
+
+  /* bgp ibgp-allow-policy-mods command */
+  install_element (BGP_NODE, &bgp_rr_allow_outbound_policy_cmd);
+  install_element (BGP_NODE, &no_bgp_rr_allow_outbound_policy_cmd);
+
+  /* "neighbor remote-as" commands. */
+  install_element (BGP_NODE, &neighbor_remote_as_cmd);
+  install_element (BGP_NODE, &no_neighbor_cmd);
+  install_element (BGP_NODE, &no_neighbor_remote_as_cmd);
+
+  /* "neighbor peer-group" commands. */
+  install_element (BGP_NODE, &neighbor_peer_group_cmd);
+  install_element (BGP_NODE, &no_neighbor_peer_group_cmd);
+  install_element (BGP_NODE, &no_neighbor_peer_group_remote_as_cmd);
+
+  /* "neighbor local-as" commands. */
+  install_element (BGP_NODE, &neighbor_local_as_cmd);
+  install_element (BGP_NODE, &neighbor_local_as_no_prepend_cmd);
+  install_element (BGP_NODE, &neighbor_local_as_no_prepend_replace_as_cmd);
+  install_element (BGP_NODE, &no_neighbor_local_as_cmd);
+  install_element (BGP_NODE, &no_neighbor_local_as_val_cmd);
+  install_element (BGP_NODE, &no_neighbor_local_as_val2_cmd);
+  install_element (BGP_NODE, &no_neighbor_local_as_val3_cmd);
+
+  /* "neighbor password" commands. */
+  install_element (BGP_NODE, &neighbor_password_cmd);
+  install_element (BGP_NODE, &no_neighbor_password_cmd);
+
+  /* "neighbor activate" commands. */
+  install_element (BGP_NODE, &neighbor_activate_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_activate_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_activate_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_activate_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_activate_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_activate_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_activate_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_activate_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_activate_cmd);
+
+  /* "no neighbor activate" commands. */
+  install_element (BGP_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_activate_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_activate_cmd);
+
+  /* "neighbor peer-group set" commands. */
+  install_element (BGP_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_set_peer_group_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_set_peer_group_cmd);
+  
+  /* "no neighbor peer-group unset" commands. */
+  install_element (BGP_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_set_peer_group_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_set_peer_group_cmd);
+  
+  /* "neighbor softreconfiguration inbound" commands.*/
+  install_element (BGP_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_soft_reconfiguration_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_soft_reconfiguration_cmd);
+
+  /* "neighbor attribute-unchanged" commands.  */
+  install_element (BGP_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_NODE, &no_neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged10_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged10_cmd);
+
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged10_cmd);
+
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged10_cmd);
+
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged1_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged2_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged3_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged4_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged5_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged6_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged7_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged8_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged9_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged10_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged1_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged2_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged3_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged4_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged5_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged6_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged7_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged8_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged9_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged10_cmd);
+
+  /* "nexthop-local unchanged" commands */
+  install_element (BGP_IPV6_NODE, &neighbor_nexthop_local_unchanged_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_local_unchanged_cmd);
+
+  /* "transparent-as" and "transparent-nexthop" for old version
+     compatibility.  */
+  install_element (BGP_NODE, &neighbor_transparent_as_cmd);
+  install_element (BGP_NODE, &neighbor_transparent_nexthop_cmd);
+
+  /* "neighbor next-hop-self" commands. */
+  install_element (BGP_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_nexthop_self_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_nexthop_self_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_nexthop_self_cmd);
+
+  /* "neighbor remove-private-AS" commands. */
+  install_element (BGP_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_remove_private_as_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_remove_private_as_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_remove_private_as_cmd);
+
+  /* "neighbor send-community" commands.*/
+  install_element (BGP_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_send_community_type_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_send_community_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_send_community_type_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_send_community_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_send_community_type_cmd);
+
+  /* "neighbor route-reflector" commands.*/
+  install_element (BGP_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_route_reflector_client_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_route_reflector_client_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_reflector_client_cmd);
+
+  /* "neighbor route-server" commands.*/
+  install_element (BGP_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_route_server_client_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_route_server_client_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_server_client_cmd);
+
+  /* "neighbor passive" commands. */
+  install_element (BGP_NODE, &neighbor_passive_cmd);
+  install_element (BGP_NODE, &no_neighbor_passive_cmd);
+
+  /* "neighbor shutdown" commands. */
+  install_element (BGP_NODE, &neighbor_shutdown_cmd);
+  install_element (BGP_NODE, &no_neighbor_shutdown_cmd);
+
+  /* Deprecated "neighbor capability route-refresh" commands.*/
+  install_element (BGP_NODE, &neighbor_capability_route_refresh_cmd);
+  install_element (BGP_NODE, &no_neighbor_capability_route_refresh_cmd);
+
+  /* "neighbor capability orf prefix-list" commands.*/
+  install_element (BGP_NODE, &neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_NODE, &no_neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_capability_orf_prefix_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_capability_orf_prefix_cmd);
+
+  /* "neighbor capability dynamic" commands.*/
+  install_element (BGP_NODE, &neighbor_capability_dynamic_cmd);
+  install_element (BGP_NODE, &no_neighbor_capability_dynamic_cmd);
+
+  /* "neighbor dont-capability-negotiate" commands. */
+  install_element (BGP_NODE, &neighbor_dont_capability_negotiate_cmd);
+  install_element (BGP_NODE, &no_neighbor_dont_capability_negotiate_cmd);
+
+  /* "neighbor ebgp-multihop" commands. */
+  install_element (BGP_NODE, &neighbor_ebgp_multihop_cmd);
+  install_element (BGP_NODE, &neighbor_ebgp_multihop_ttl_cmd);
+  install_element (BGP_NODE, &no_neighbor_ebgp_multihop_cmd);
+  install_element (BGP_NODE, &no_neighbor_ebgp_multihop_ttl_cmd);
+
+  /* "neighbor disable-connected-check" commands.  */
+  install_element (BGP_NODE, &neighbor_disable_connected_check_cmd);
+  install_element (BGP_NODE, &no_neighbor_disable_connected_check_cmd);
+  install_element (BGP_NODE, &neighbor_enforce_multihop_cmd);
+  install_element (BGP_NODE, &no_neighbor_enforce_multihop_cmd);
+
+  /* "neighbor description" commands. */
+  install_element (BGP_NODE, &neighbor_description_cmd);
+  install_element (BGP_NODE, &no_neighbor_description_cmd);
+  install_element (BGP_NODE, &no_neighbor_description_val_cmd);
+
+  /* "neighbor update-source" commands. "*/
+  install_element (BGP_NODE, &neighbor_update_source_cmd);
+  install_element (BGP_NODE, &no_neighbor_update_source_cmd);
+
+  /* "neighbor default-originate" commands. */
+  install_element (BGP_NODE, &neighbor_default_originate_cmd);
+  install_element (BGP_NODE, &neighbor_default_originate_rmap_cmd);
+  install_element (BGP_NODE, &no_neighbor_default_originate_cmd);
+  install_element (BGP_NODE, &no_neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_default_originate_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_default_originate_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_default_originate_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_default_originate_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_default_originate_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_default_originate_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_default_originate_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_default_originate_rmap_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_default_originate_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_default_originate_rmap_cmd);
+
+  /* "neighbor port" commands. */
+  install_element (BGP_NODE, &neighbor_port_cmd);
+  install_element (BGP_NODE, &no_neighbor_port_cmd);
+  install_element (BGP_NODE, &no_neighbor_port_val_cmd);
+
+  /* "neighbor weight" commands. */
+  install_element (BGP_NODE, &neighbor_weight_cmd);
+  install_element (BGP_NODE, &no_neighbor_weight_cmd);
+  install_element (BGP_NODE, &no_neighbor_weight_val_cmd);
+
+  /* "neighbor override-capability" commands. */
+  install_element (BGP_NODE, &neighbor_override_capability_cmd);
+  install_element (BGP_NODE, &no_neighbor_override_capability_cmd);
+
+  /* "neighbor strict-capability-match" commands. */
+  install_element (BGP_NODE, &neighbor_strict_capability_cmd);
+  install_element (BGP_NODE, &no_neighbor_strict_capability_cmd);
+
+  /* "neighbor timers" commands. */
+  install_element (BGP_NODE, &neighbor_timers_cmd);
+  install_element (BGP_NODE, &no_neighbor_timers_cmd);
+
+  /* "neighbor timers connect" commands. */
+  install_element (BGP_NODE, &neighbor_timers_connect_cmd);
+  install_element (BGP_NODE, &no_neighbor_timers_connect_cmd);
+  install_element (BGP_NODE, &no_neighbor_timers_connect_val_cmd);
+
+  /* "neighbor advertisement-interval" commands. */
+  install_element (BGP_NODE, &neighbor_advertise_interval_cmd);
+  install_element (BGP_NODE, &no_neighbor_advertise_interval_cmd);
+  install_element (BGP_NODE, &no_neighbor_advertise_interval_val_cmd);
+
+  /* "neighbor version" commands. */
+  install_element (BGP_NODE, &neighbor_version_cmd);
+
+  /* "neighbor interface" commands. */
+  install_element (BGP_NODE, &neighbor_interface_cmd);
+  install_element (BGP_NODE, &no_neighbor_interface_cmd);
+
+  /* "neighbor distribute" commands. */
+  install_element (BGP_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_distribute_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_distribute_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_distribute_list_cmd);
+
+  /* "neighbor prefix-list" commands. */
+  install_element (BGP_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_prefix_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_prefix_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_prefix_list_cmd);
+
+  /* "neighbor filter-list" commands. */
+  install_element (BGP_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_filter_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_filter_list_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_filter_list_cmd);
+
+  /* "neighbor route-map" commands. */
+  install_element (BGP_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_route_map_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_route_map_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_map_cmd);
+
+  /* "neighbor unsuppress-map" commands. */
+  install_element (BGP_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_unsuppress_map_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_unsuppress_map_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_unsuppress_map_cmd);
+
+  /* "neighbor maximum-prefix" commands. */
+  install_element (BGP_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_val_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_warning_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_restart_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd);
+
+  /* "neighbor allowas-in" */
+  install_element (BGP_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_IPV4_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_IPV4_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_IPV4M_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_IPV4M_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_IPV6_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_IPV6_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_IPV6M_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_IPV6M_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_VPNV4_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_VPNV4_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_VPNV6_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_ENCAP_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_ENCAP_NODE, &no_neighbor_allowas_in_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_allowas_in_cmd);
+  install_element (BGP_ENCAPV6_NODE, &neighbor_allowas_in_arg_cmd);
+  install_element (BGP_ENCAPV6_NODE, &no_neighbor_allowas_in_cmd);
+
+  /* address-family commands. */
+  install_element (BGP_NODE, &address_family_ipv4_cmd);
+  install_element (BGP_NODE, &address_family_ipv4_safi_cmd);
+  install_element (BGP_NODE, &address_family_ipv6_cmd);
+  install_element (BGP_NODE, &address_family_ipv6_safi_cmd);
+  install_element (BGP_NODE, &address_family_vpnv4_cmd);
+  install_element (BGP_NODE, &address_family_vpnv4_unicast_cmd);
+
+  install_element (BGP_NODE, &address_family_vpnv6_cmd);
+  install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd);
+
+  install_element (BGP_NODE, &address_family_encap_cmd);
+  install_element (BGP_NODE, &address_family_encapv4_cmd);
+  install_element (BGP_NODE, &address_family_encapv6_cmd);
+
+  /* "exit-address-family" command. */
+  install_element (BGP_IPV4_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV4M_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV6_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV6M_NODE, &exit_address_family_cmd);
+  install_element (BGP_VPNV4_NODE, &exit_address_family_cmd);
+  install_element (BGP_VPNV6_NODE, &exit_address_family_cmd);
+  install_element (BGP_ENCAP_NODE, &exit_address_family_cmd);
+  install_element (BGP_ENCAPV6_NODE, &exit_address_family_cmd);
+
+  /* "clear ip bgp commands" */
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_all_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_cmd);
+
+  /* "clear ip bgp neighbor soft in" */
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_in_prefix_filter_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_in_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_in_prefix_filter_cmd);
+
+  /* clear ip bgp prefix  */
+  install_element (ENABLE_NODE, &clear_ip_bgp_prefix_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_safi_prefix_cmd);
+
+  /* "clear ip bgp neighbor soft out" */
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_out_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_out_cmd);
+
+  /* "clear ip bgp neighbor soft" */
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_external_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_as_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_cmd);
+
+  /* "clear ip bgp neighbor rsclient" */
+  install_element (ENABLE_NODE, &clear_ip_bgp_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_peer_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_ip_bgp_instance_peer_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_instance_all_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_peer_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_instance_peer_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_rsclient_cmd);
+  install_element (ENABLE_NODE, &clear_bgp_ipv6_instance_peer_rsclient_cmd);
+
+  /* "show ip bgp summary" commands. */
+  install_element (VIEW_NODE, &show_bgp_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_summary_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_summary_1w_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_summary_1w_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv4_summary_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv4_vpn_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_encap_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_vpn_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_encap_summary_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_instance_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd);
+
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_summary_cmd);
+
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_encap_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_encap_summary_cmd);
+
+  install_element (RESTRICTED_NODE, &show_bgp_instance_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_summary_cmd);
+
+  /* "show ip bgp neighbors" commands. */
+  install_element (VIEW_NODE, &show_bgp_instance_neighbors_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_neighbors_cmd);
+  install_element (VIEW_NODE, &show_bgp_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd);
+
+  /* "show ip bgp rsclient" commands. */
+  install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_rsclient_summary_cmd);
+
+  install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd);
+
+  /* "show ip bgp paths" commands. */
+  install_element (VIEW_NODE, &show_bgp_ipv4_paths_cmd);
+
+  /* "show ip bgp community" commands. */
+  install_element (VIEW_NODE, &show_ip_bgp_community_info_cmd);
+
+  /* "show ip bgp large-community" commands. */
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd);
+
+  /* "show ip bgp attribute-info" commands. */
+  install_element (VIEW_NODE, &show_ip_bgp_attr_info_cmd);
+
+  /* "redistribute" commands.  */
+  install_element (BGP_NODE, &bgp_redistribute_ipv4_cmd);
+  install_element (BGP_NODE, &no_bgp_redistribute_ipv4_cmd);
+  install_element (BGP_NODE, &bgp_redistribute_ipv4_rmap_cmd);
+  install_element (BGP_NODE, &no_bgp_redistribute_ipv4_rmap_cmd);
+  install_element (BGP_NODE, &bgp_redistribute_ipv4_metric_cmd);
+  install_element (BGP_NODE, &no_bgp_redistribute_ipv4_metric_cmd);
+  install_element (BGP_NODE, &bgp_redistribute_ipv4_rmap_metric_cmd);
+  install_element (BGP_NODE, &bgp_redistribute_ipv4_metric_rmap_cmd);
+  install_element (BGP_NODE, &no_bgp_redistribute_ipv4_rmap_metric_cmd);
+  install_element (BGP_NODE, &no_bgp_redistribute_ipv4_metric_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd);
+  install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_rmap_metric_cmd);
+  install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_rmap_cmd);
+
+  /* ttl_security commands */
+  install_element (BGP_NODE, &neighbor_ttl_security_cmd);
+  install_element (BGP_NODE, &no_neighbor_ttl_security_cmd);
+
+  /* "show bgp memory" commands. */
+  install_element (VIEW_NODE, &show_bgp_memory_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_memory_cmd);
+  
+  /* "show bgp views" commands. */
+  install_element (VIEW_NODE, &show_bgp_views_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_views_cmd);
+  
+  /* non afi/safi forms of commands */
+  install_element (VIEW_NODE, &show_ip_bgp_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_instance_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbors_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_instance_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_cmd);
+  install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_peer_cmd);
+  install_element (RESTRICTED_NODE, &show_bgp_ipv6_neighbors_peer_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_summary_cmd);
+  install_element (VIEW_NODE, &show_ipv6_mbgp_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_instance_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd);
+  install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_paths_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_ipv4_paths_cmd);
+  /* Community-list. */
+  community_list_vty ();
+}
+
+#include "memory.h"
+#include "bgp_regex.h"
+#include "bgp_clist.h"
+#include "bgp_ecommunity.h"
+
+/* VTY functions.  */
+
+/* Direction value to string conversion.  */
+static const char *
+community_direct_str (int direct)
+{
+  switch (direct)
+    {
+    case COMMUNITY_DENY:
+      return "deny";
+    case COMMUNITY_PERMIT:
+      return "permit";
+    default:
+      return "unknown";
+    }
+}
+
+/* Display error string.  */
+static void
+community_list_perror (struct vty *vty, int ret)
+{
+  switch (ret)
+    {
+    case COMMUNITY_LIST_ERR_CANT_FIND_LIST:
+      vty_out (vty, "%% Can't find community-list%s", VTY_NEWLINE);
+      break;
+    case COMMUNITY_LIST_ERR_MALFORMED_VAL:
+      vty_out (vty, "%% Malformed community-list value%s", VTY_NEWLINE);
+      break;
+    case COMMUNITY_LIST_ERR_STANDARD_CONFLICT:
+      vty_out (vty, "%% Community name conflict, previously defined as standard community%s", VTY_NEWLINE);
+      break;
+    case COMMUNITY_LIST_ERR_EXPANDED_CONFLICT:
+      vty_out (vty, "%% Community name conflict, previously defined as expanded community%s", VTY_NEWLINE);
+      break;
+    }
+}
+
+/* VTY interface for community_set() function.  */
+static int
+community_list_set_vty (struct vty *vty, int argc, const char **argv, 
+                        int style, int reject_all_digit_name)
+{
+  int ret;
+  int direct;
+  char *str;
+
+  /* Check the list type. */
+  if (strncmp (argv[1], "p", 1) == 0)
+    direct = COMMUNITY_PERMIT;
+  else if (strncmp (argv[1], "d", 1) == 0)
+    direct = COMMUNITY_DENY;
+  else
+    {
+      vty_out (vty, "%% Matching condition must be permit or deny%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* All digit name check.  */
+  if (reject_all_digit_name && all_digit (argv[0]))
+    {
+      vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Concat community string argument.  */
+  if (argc > 1)
+    str = argv_concat (argv, argc, 2);
+  else
+    str = NULL;
+
+  /* When community_list_set() return nevetive value, it means
+     malformed community string.  */
+  ret = community_list_set (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      /* Display error string.  */
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Communiyt-list entry delete.  */
+static int
+community_list_unset_vty (struct vty *vty, int argc, const char **argv,
+                         int style)
+{
+  int ret;
+  int direct = 0;
+  char *str = NULL;
+
+  if (argc > 1)
+    {
+      /* Check the list direct. */
+      if (strncmp (argv[1], "p", 1) == 0)
+       direct = COMMUNITY_PERMIT;
+      else if (strncmp (argv[1], "d", 1) == 0)
+       direct = COMMUNITY_DENY;
+      else
+       {
+         vty_out (vty, "%% Matching condition must be permit or deny%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      /* Concat community string argument.  */
+      str = argv_concat (argv, argc, 2);
+    }
+
+  /* Unset community list.  */
+  ret = community_list_unset (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* "community-list" keyword help string.  */
+#define COMMUNITY_LIST_STR "Add a community list entry\n"
+#define COMMUNITY_VAL_STR  "Community number in aa:nn format or internet|local-AS|no-advertise|no-export\n"
+
+DEFUN (ip_community_list_standard,
+       ip_community_list_standard_cmd,
+       "ip community-list <1-99> (deny|permit) .AA:NN",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       COMMUNITY_VAL_STR)
+{
+  return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD, 0);
+}
+
+ALIAS (ip_community_list_standard,
+       ip_community_list_standard2_cmd,
+       "ip community-list <1-99> (deny|permit)",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n")
+
+DEFUN (ip_community_list_expanded,
+       ip_community_list_expanded_cmd,
+       "ip community-list <100-500> (deny|permit) .LINE",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (expanded)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED, 0);
+}
+
+DEFUN (ip_community_list_name_standard,
+       ip_community_list_name_standard_cmd,
+       "ip community-list standard WORD (deny|permit) .AA:NN",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Add a standard community-list entry\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       COMMUNITY_VAL_STR)
+{
+  return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD, 1);
+}
+
+ALIAS (ip_community_list_name_standard,
+       ip_community_list_name_standard2_cmd,
+       "ip community-list standard WORD (deny|permit)",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Add a standard community-list entry\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n")
+
+DEFUN (ip_community_list_name_expanded,
+       ip_community_list_name_expanded_cmd,
+       "ip community-list expanded WORD (deny|permit) .LINE",
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Add an expanded community-list entry\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED, 1);
+}
+
+DEFUN (no_ip_community_list_standard_all,
+       no_ip_community_list_standard_all_cmd,
+       "no ip community-list <1-99>",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (standard)\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_community_list_expanded_all,
+       no_ip_community_list_expanded_all_cmd,
+       "no ip community-list <100-500>",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (expanded)\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_community_list_name_standard_all,
+       no_ip_community_list_name_standard_all_cmd,
+       "no ip community-list standard WORD",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Add a standard community-list entry\n"
+       "Community list name\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_community_list_name_expanded_all,
+       no_ip_community_list_name_expanded_all_cmd,
+       "no ip community-list expanded WORD",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Add an expanded community-list entry\n"
+       "Community list name\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_community_list_standard,
+       no_ip_community_list_standard_cmd,
+       "no ip community-list <1-99> (deny|permit) .AA:NN",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       COMMUNITY_VAL_STR)
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_community_list_expanded,
+       no_ip_community_list_expanded_cmd,
+       "no ip community-list <100-500> (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Community list number (expanded)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_community_list_name_standard,
+       no_ip_community_list_name_standard_cmd,
+       "no ip community-list standard WORD (deny|permit) .AA:NN",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Specify a standard community-list\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       COMMUNITY_VAL_STR)
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_community_list_name_expanded,
+       no_ip_community_list_name_expanded_cmd,
+       "no ip community-list expanded WORD (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       COMMUNITY_LIST_STR
+       "Specify an expanded community-list\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED);
+}
+
+static void
+community_list_show (struct vty *vty, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry == list->head)
+       {
+         if (all_digit (list->name))
+           vty_out (vty, "Community %s list %s%s",
+                    entry->style == COMMUNITY_LIST_STANDARD ?
+                    "standard" : "(expanded) access",
+                    list->name, VTY_NEWLINE);
+         else
+           vty_out (vty, "Named Community %s list %s%s",
+                    entry->style == COMMUNITY_LIST_STANDARD ?
+                    "standard" : "expanded",
+                    list->name, VTY_NEWLINE);
+       }
+      if (entry->any)
+       vty_out (vty, "    %s%s",
+                community_direct_str (entry->direct), VTY_NEWLINE);
+      else
+       vty_out (vty, "    %s %s%s",
+                community_direct_str (entry->direct),
+                entry->style == COMMUNITY_LIST_STANDARD
+                ? community_str (entry->u.com) : entry->config,
+                VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_community_list,
+       show_ip_community_list_cmd,
+       "show ip community-list",
+       SHOW_STR
+       IP_STR
+       "List community-list\n")
+{
+  struct community_list *list;
+  struct community_list_master *cm;
+
+  cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
+  if (! cm)
+    return CMD_SUCCESS;
+
+  for (list = cm->num.head; list; list = list->next)
+    community_list_show (vty, list);
+
+  for (list = cm->str.head; list; list = list->next)
+    community_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_community_list_arg,
+       show_ip_community_list_arg_cmd,
+       "show ip community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       "List community-list\n"
+       "Community-list number\n"
+       "Community-list name\n")
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, argv[0], COMMUNITY_LIST_MASTER);
+  if (! list)
+    {
+      vty_out (vty, "%% Can't find community-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  community_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+/*
+ * Large Community code.
+ */
+static int
+lcommunity_list_set_vty (struct vty *vty, int argc, const char **argv,
+                        int style, int reject_all_digit_name)
+{
+  int ret;
+  int direct;
+  char *str;
+
+  /* Check the list type. */
+  if (strncmp (argv[1], "p", 1) == 0)
+    direct = COMMUNITY_PERMIT;
+  else if (strncmp (argv[1], "d", 1) == 0)
+    direct = COMMUNITY_DENY;
+  else
+    {
+      vty_out (vty, "%% Matching condition must be permit or deny%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* All digit name check.  */
+  if (reject_all_digit_name && all_digit (argv[0]))
+    {
+      vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Concat community string argument.  */
+  if (argc > 1)
+    str = argv_concat (argv, argc, 2);
+  else
+    str = NULL;
+
+  ret = lcommunity_list_set (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+lcommunity_list_unset_vty (struct vty *vty, int argc, const char **argv,
+                          int style)
+{
+  int ret;
+  int direct = 0;
+  char *str = NULL;
+
+  if (argc > 1)
+    {
+      /* Check the list direct. */
+      if (strncmp (argv[1], "p", 1) == 0)
+       direct = COMMUNITY_PERMIT;
+      else if (strncmp (argv[1], "d", 1) == 0)
+       direct = COMMUNITY_DENY;
+      else
+       {
+         vty_out (vty, "%% Matching condition must be permit or deny%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      /* Concat community string argument.  */
+      str = argv_concat (argv, argc, 2);
+    }
+
+  /* Unset community list.  */
+  ret = lcommunity_list_unset (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* "large-community-list" keyword help string.  */
+#define LCOMMUNITY_LIST_STR "Add a large community list entry\n"
+#define LCOMMUNITY_VAL_STR  "large community in 'aa:bb:cc' format\n"
+
+DEFUN (ip_lcommunity_list_standard,
+       ip_lcommunity_list_standard_cmd,
+       "ip large-community-list <1-99> (deny|permit) .AA:BB:CC",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 0);
+}
+
+ALIAS (ip_lcommunity_list_standard,
+       ip_lcommunity_list_standard2_cmd,
+       "ip large-community-list <1-99> (deny|permit)",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n")
+
+DEFUN (ip_lcommunity_list_expanded,
+       ip_lcommunity_list_expanded_cmd,
+       "ip large-community-list <100-500> (deny|permit) .LINE",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (expanded)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 0);
+}
+
+DEFUN (ip_lcommunity_list_name_standard,
+       ip_lcommunity_list_name_standard_cmd,
+       "ip large-community-list standard WORD (deny|permit) .AA:BB.CC",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 1);
+}
+
+ALIAS (ip_lcommunity_list_name_standard,
+       ip_lcommunity_list_name_standard2_cmd,
+       "ip large-community-list standard WORD (deny|permit)",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n")
+
+DEFUN (ip_lcommunity_list_name_expanded,
+       ip_lcommunity_list_name_expanded_cmd,
+       "ip large-community-list expanded WORD (deny|permit) .LINE",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 1);
+}
+
+DEFUN (no_ip_lcommunity_list_standard_all,
+       no_ip_lcommunity_list_standard_all_cmd,
+       "no ip large-community-list <1-99>",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_expanded_all,
+       no_ip_lcommunity_list_expanded_all_cmd,
+       "no ip large-community-list <100-500>",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (expanded)\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_lcommunity_list_name_standard_all,
+       no_ip_lcommunity_list_name_standard_all_cmd,
+       "no ip large-community-list standard WORD",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_name_expanded_all,
+       no_ip_lcommunity_list_name_expanded_all_cmd,
+       "no ip large-community-list expanded WORD",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large Community list name\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_lcommunity_list_standard,
+       no_ip_lcommunity_list_standard_cmd,
+       "no ip large-community-list <1-99> (deny|permit) .AA:.AA:NN",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_expanded,
+       no_ip_lcommunity_list_expanded_cmd,
+       "no ip large-community-list <100-500> (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (expanded)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_lcommunity_list_name_standard,
+       no_ip_lcommunity_list_name_standard_cmd,
+       "no ip large-community-list standard WORD (deny|permit) .AA:.AA:NN",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_name_expanded,
+       no_ip_lcommunity_list_name_expanded_cmd,
+       "no ip large-community-list expanded WORD (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+static void
+lcommunity_list_show (struct vty *vty, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry == list->head)
+       {
+         if (all_digit (list->name))
+           vty_out (vty, "Large community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "(expanded) access",
+                    list->name, VTY_NEWLINE);
+         else
+           vty_out (vty, "Named large community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "expanded",
+                    list->name, VTY_NEWLINE);
+       }
+      if (entry->any)
+       vty_out (vty, "    %s%s",
+                community_direct_str (entry->direct), VTY_NEWLINE);
+      else
+       vty_out (vty, "    %s %s%s",
+                community_direct_str (entry->direct),
+                entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                entry->u.ecom->str : entry->config,
+                VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_lcommunity_list,
+       show_ip_lcommunity_list_cmd,
+       "show ip large-community-list",
+       SHOW_STR
+       IP_STR
+       "List large-community list\n")
+{
+  struct community_list *list;
+  struct community_list_master *cm;
+
+  cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER);
+  if (! cm)
+    return CMD_SUCCESS;
+
+  for (list = cm->num.head; list; list = list->next)
+    lcommunity_list_show (vty, list);
+
+  for (list = cm->str.head; list; list = list->next)
+    lcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_lcommunity_list_arg,
+       show_ip_lcommunity_list_arg_cmd,
+       "show ip large-community-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       "List large-community list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, argv[0], LARGE_COMMUNITY_LIST_MASTER);
+  if (! list)
+    {
+      vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  lcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+static int
+extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv, 
+                           int style, int reject_all_digit_name)
+{
+  int ret;
+  int direct;
+  char *str;
+
+  /* Check the list type. */
+  if (strncmp (argv[1], "p", 1) == 0)
+    direct = COMMUNITY_PERMIT;
+  else if (strncmp (argv[1], "d", 1) == 0)
+    direct = COMMUNITY_DENY;
+  else
+    {
+      vty_out (vty, "%% Matching condition must be permit or deny%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* All digit name check.  */
+  if (reject_all_digit_name && all_digit (argv[0]))
+    {
+      vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Concat community string argument.  */
+  if (argc > 1)
+    str = argv_concat (argv, argc, 2);
+  else
+    str = NULL;
+
+  ret = extcommunity_list_set (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+extcommunity_list_unset_vty (struct vty *vty, int argc, const char **argv,
+                            int style)
+{
+  int ret;
+  int direct = 0;
+  char *str = NULL;
+
+  if (argc > 1)
+    {
+      /* Check the list direct. */
+      if (strncmp (argv[1], "p", 1) == 0)
+       direct = COMMUNITY_PERMIT;
+      else if (strncmp (argv[1], "d", 1) == 0)
+       direct = COMMUNITY_DENY;
+      else
+       {
+         vty_out (vty, "%% Matching condition must be permit or deny%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      /* Concat community string argument.  */
+      str = argv_concat (argv, argc, 2);
+    }
+
+  /* Unset community list.  */
+  ret = extcommunity_list_unset (bgp_clist, argv[0], str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* "extcommunity-list" keyword help string.  */
+#define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n"
+#define EXTCOMMUNITY_VAL_STR  "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n"
+
+DEFUN (ip_extcommunity_list_standard,
+       ip_extcommunity_list_standard_cmd,
+       "ip extcommunity-list <1-99> (deny|permit) .AA:NN",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       EXTCOMMUNITY_VAL_STR)
+{
+  return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD, 0);
+}
+
+ALIAS (ip_extcommunity_list_standard,
+       ip_extcommunity_list_standard2_cmd,
+       "ip extcommunity-list <1-99> (deny|permit)",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n")
+
+DEFUN (ip_extcommunity_list_expanded,
+       ip_extcommunity_list_expanded_cmd,
+       "ip extcommunity-list <100-500> (deny|permit) .LINE",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (expanded)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED, 0);
+}
+
+DEFUN (ip_extcommunity_list_name_standard,
+       ip_extcommunity_list_name_standard_cmd,
+       "ip extcommunity-list standard WORD (deny|permit) .AA:NN",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify standard extcommunity-list\n"
+       "Extended Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       EXTCOMMUNITY_VAL_STR)
+{
+  return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD, 1);
+}
+
+ALIAS (ip_extcommunity_list_name_standard,
+       ip_extcommunity_list_name_standard2_cmd,
+       "ip extcommunity-list standard WORD (deny|permit)",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify standard extcommunity-list\n"
+       "Extended Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n")
+
+DEFUN (ip_extcommunity_list_name_expanded,
+       ip_extcommunity_list_name_expanded_cmd,
+       "ip extcommunity-list expanded WORD (deny|permit) .LINE",
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify expanded extcommunity-list\n"
+       "Extended Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED, 1);
+}
+
+DEFUN (no_ip_extcommunity_list_standard_all,
+       no_ip_extcommunity_list_standard_all_cmd,
+       "no ip extcommunity-list <1-99>",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (standard)\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_extcommunity_list_expanded_all,
+       no_ip_extcommunity_list_expanded_all_cmd,
+       "no ip extcommunity-list <100-500>",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (expanded)\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_extcommunity_list_name_standard_all,
+       no_ip_extcommunity_list_name_standard_all_cmd,
+       "no ip extcommunity-list standard WORD",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify standard extcommunity-list\n"
+       "Extended Community list name\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_extcommunity_list_name_expanded_all,
+       no_ip_extcommunity_list_name_expanded_all_cmd,
+       "no ip extcommunity-list expanded WORD",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify expanded extcommunity-list\n"
+       "Extended Community list name\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_extcommunity_list_standard,
+       no_ip_extcommunity_list_standard_cmd,
+       "no ip extcommunity-list <1-99> (deny|permit) .AA:NN",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (standard)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       EXTCOMMUNITY_VAL_STR)
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_extcommunity_list_expanded,
+       no_ip_extcommunity_list_expanded_cmd,
+       "no ip extcommunity-list <100-500> (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Extended Community list number (expanded)\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_extcommunity_list_name_standard,
+       no_ip_extcommunity_list_name_standard_cmd,
+       "no ip extcommunity-list standard WORD (deny|permit) .AA:NN",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify standard extcommunity-list\n"
+       "Extended Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       EXTCOMMUNITY_VAL_STR)
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_extcommunity_list_name_expanded,
+       no_ip_extcommunity_list_name_expanded_cmd,
+       "no ip extcommunity-list expanded WORD (deny|permit) .LINE",
+       NO_STR
+       IP_STR
+       EXTCOMMUNITY_LIST_STR
+       "Specify expanded extcommunity-list\n"
+       "Community list name\n"
+       "Specify community to reject\n"
+       "Specify community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED);
+}
+
+static void
+extcommunity_list_show (struct vty *vty, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry == list->head)
+       {
+         if (all_digit (list->name))
+           vty_out (vty, "Extended community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "(expanded) access",
+                    list->name, VTY_NEWLINE);
+         else
+           vty_out (vty, "Named extended community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "expanded",
+                    list->name, VTY_NEWLINE);
+       }
+      if (entry->any)
+       vty_out (vty, "    %s%s",
+                community_direct_str (entry->direct), VTY_NEWLINE);
+      else
+       vty_out (vty, "    %s %s%s",
+                community_direct_str (entry->direct),
+                entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                entry->u.ecom->str : entry->config,
+                VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_extcommunity_list,
+       show_ip_extcommunity_list_cmd,
+       "show ip extcommunity-list",
+       SHOW_STR
+       IP_STR
+       "List extended-community list\n")
+{
+  struct community_list *list;
+  struct community_list_master *cm;
+
+  cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+  if (! cm)
+    return CMD_SUCCESS;
+
+  for (list = cm->num.head; list; list = list->next)
+    extcommunity_list_show (vty, list);
+
+  for (list = cm->str.head; list; list = list->next)
+    extcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_extcommunity_list_arg,
+       show_ip_extcommunity_list_arg_cmd,
+       "show ip extcommunity-list (<1-500>|WORD)",
+       SHOW_STR
+       IP_STR
+       "List extended-community list\n"
+       "Extcommunity-list number\n"
+       "Extcommunity-list name\n")
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, argv[0], EXTCOMMUNITY_LIST_MASTER);
+  if (! list)
+    {
+      vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  extcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+/* Return configuration string of community-list entry.  */
+static const char *
+community_list_config_str (struct community_entry *entry)
+{
+  const char *str;
+
+  if (entry->any)
+    str = "";
+  else
+    {
+      if (entry->style == COMMUNITY_LIST_STANDARD)
+       str = community_str (entry->u.com);
+      else
+       str = entry->config;
+    }
+  return str;
+}
+
+/* Display community-list and extcommunity-list configuration.  */
+static int
+community_list_config_write (struct vty *vty)
+{
+  struct community_list *list;
+  struct community_entry *entry;
+  struct community_list_master *cm;
+  int write = 0;
+
+  /* Community-list.  */
+  cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
+
+  for (list = cm->num.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip community-list %s %s %s%s",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry),
+                VTY_NEWLINE);
+       write++;
+      }
+  for (list = cm->str.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip community-list %s %s %s %s%s",
+                entry->style == COMMUNITY_LIST_STANDARD
+                ? "standard" : "expanded",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry),
+                VTY_NEWLINE);
+       write++;
+      }
+
+  /* Extcommunity-list.  */
+  cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+
+  for (list = cm->num.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip extcommunity-list %s %s %s%s",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+  for (list = cm->str.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip extcommunity-list %s %s %s %s%s",
+                entry->style == EXTCOMMUNITY_LIST_STANDARD
+                ? "standard" : "expanded",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+
+
+    /* lcommunity-list.  */
+  cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER);
+
+  for (list = cm->num.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip large-community-list %s %s %s%s",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+  for (list = cm->str.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip large-community-list %s %s %s %s%s",
+                entry->style == LARGE_COMMUNITY_LIST_STANDARD
+                ? "standard" : "expanded",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+
+  return write;
+}
+
+static struct cmd_node community_list_node =
+{
+  COMMUNITY_LIST_NODE,
+  "",
+  1                            /* Export to vtysh.  */
+};
+
+static void
+community_list_vty (void)
+{
+  install_node (&community_list_node, community_list_config_write);
+
+  /* Community-list.  */
+  install_element (CONFIG_NODE, &ip_community_list_standard_cmd);
+  install_element (CONFIG_NODE, &ip_community_list_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_community_list_expanded_cmd);
+  install_element (CONFIG_NODE, &ip_community_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &ip_community_list_name_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_community_list_name_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_name_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_name_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_community_list_name_expanded_cmd);
+  install_element (VIEW_NODE, &show_ip_community_list_cmd);
+  install_element (VIEW_NODE, &show_ip_community_list_arg_cmd);
+
+  /* Extcommunity-list.  */
+  install_element (CONFIG_NODE, &ip_extcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &ip_extcommunity_list_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_extcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &ip_extcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &ip_extcommunity_list_name_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_extcommunity_list_name_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_expanded_cmd);
+  install_element (VIEW_NODE, &show_ip_extcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ip_extcommunity_list_arg_cmd);
+
+  /* Large Community List */
+  install_element (CONFIG_NODE, &ip_lcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard2_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_cmd);
+  install_element (VIEW_NODE, &show_ip_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ip_lcommunity_list_arg_cmd);
+}
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
new file mode 100644 (file)
index 0000000..7329c5f
--- /dev/null
@@ -0,0 +1,35 @@
+/* BGP VTY interface.
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_VTY_H
+#define _QUAGGA_BGP_VTY_H
+
+#define CMD_AS_RANGE "<1-4294967295>"
+
+extern void bgp_vty_init (void);
+extern const char *afi_safi_print (afi_t, safi_t);
+
+extern int
+bgp_parse_afi(const char *str, afi_t *afi);
+
+extern int
+bgp_parse_safi(const char *str, safi_t *safi);
+
+#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
new file mode 100644 (file)
index 0000000..40ecbce
--- /dev/null
@@ -0,0 +1,1222 @@
+/* zebra client
+   Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "stream.h"
+#include "network.h"
+#include "prefix.h"
+#include "log.h"
+#include "sockunion.h"
+#include "zclient.h"
+#include "routemap.h"
+#include "thread.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+struct in_addr router_id_zebra;
+
+/* Growable buffer for nexthops sent to zebra */
+struct stream *bgp_nexthop_buf = NULL;
+struct stream *bgp_ifindices_buf = NULL;
+
+int zclient_num_connects;
+
+/* Router-id update message from zebra. */
+static int
+bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct prefix router_id;
+
+  zebra_router_id_update_read(zclient->ibuf,&router_id);
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    {
+      char buf[128];
+      prefix2str(&router_id, buf, sizeof(buf));
+      zlog_debug("Zebra rcvd: router id update %s", buf);
+    }
+
+  router_id_zebra = router_id.u.prefix4;
+
+  bgp_router_id_zebra_bump ();
+  return 0;
+}
+
+/* Nexthop update message from zebra. */
+static int
+bgp_read_nexthop_update (int command, struct zclient *zclient,
+                        zebra_size_t length, vrf_id_t vrf_id)
+{
+  bgp_parse_nexthop_update();
+  return 0;
+}
+
+/* Inteface addition message from zebra. */
+static int
+bgp_interface_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+
+  if (BGP_DEBUG(zebra, ZEBRA) && ifp)
+    zlog_debug("Zebra rcvd: interface add %s", ifp->name);
+
+  return 0;
+}
+
+static int
+bgp_interface_delete (int command, struct zclient *zclient,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct interface *ifp;
+
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+  if (! ifp)
+    return 0;
+
+  ifp->ifindex = IFINDEX_INTERNAL;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    zlog_debug("Zebra rcvd: interface delete %s", ifp->name);
+
+  return 0;
+}
+
+static int
+bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct interface *ifp;
+  struct connected *c;
+  struct listnode *node, *nnode;
+
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (! ifp)
+    return 0;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    zlog_debug("Zebra rcvd: interface %s up", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c))
+    bgp_connected_add (c);
+
+  return 0;
+}
+
+static int
+bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct interface *ifp;
+  struct connected *c;
+  struct listnode *node, *nnode;
+
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+  if (! ifp)
+    return 0;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    zlog_debug("Zebra rcvd: interface %s down", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c))
+    bgp_connected_delete (c);
+
+  /* Fast external-failover */
+  {
+    struct listnode *mnode;
+    struct bgp *bgp;
+    struct peer *peer;
+
+    for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp))
+      {
+       if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
+         continue;
+
+       for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+         {
+            if (peer->gtsm_hops != 1 && peer_ttl (peer) != 1)
+              continue;
+            if (ifp == peer->nexthop.ifp)
+              BGP_EVENT_ADD (peer, BGP_Stop);
+         }
+      }
+  }
+
+  return 0;
+}
+
+static int
+bgp_interface_address_add (int command, struct zclient *zclient,
+                          zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *ifc;
+
+  ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id);
+
+  if (ifc == NULL)
+    return 0;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    {
+      char buf[128];
+      prefix2str(ifc->address, buf, sizeof(buf));
+      zlog_debug("Zebra rcvd: interface %s address add %s",
+                ifc->ifp->name, buf);
+    }
+
+  if (if_is_operative (ifc->ifp))
+    bgp_connected_add (ifc);
+
+  return 0;
+}
+
+static int
+bgp_interface_address_delete (int command, struct zclient *zclient,
+                             zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *ifc;
+
+  ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id);
+
+  if (ifc == NULL)
+    return 0;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    {
+      char buf[128];
+      prefix2str(ifc->address, buf, sizeof(buf));
+      zlog_debug("Zebra rcvd: interface %s address delete %s",
+                ifc->ifp->name, buf);
+    }
+
+  if (if_is_operative (ifc->ifp))
+    bgp_connected_delete (ifc);
+
+  connected_free (ifc);
+
+  return 0;
+}
+
+/* Zebra route add and delete treatment. */
+static int
+zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv4 api;
+  struct in_addr nexthop;
+  struct prefix_ipv4 p;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  nexthop.s_addr = 0;
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      nexthop.s_addr = stream_get_ipv4 (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      stream_getl (s); /* ifindex, unused */
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  if (command == ZEBRA_IPV4_ROUTE_ADD)
+    {
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET_ADDRSTRLEN];
+         zlog_debug("Zebra rcvd: IPv4 route add %s %s/%d nexthop %s metric %u tag %d",
+                    zebra_route_string(api.type),
+                    inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+                    p.prefixlen,
+                    inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+                    api.metric,
+                    api.tag);
+       }
+      bgp_redistribute_add ((struct prefix *)&p, &nexthop, NULL,
+                           api.metric, api.type, api.tag);
+    }
+  else
+    {
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET_ADDRSTRLEN];
+         zlog_debug("Zebra rcvd: IPv4 route delete %s %s/%d "
+                    "nexthop %s metric %u tag %d",
+                    zebra_route_string(api.type),
+                    inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+                    p.prefixlen,
+                    inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+                    api.metric,
+                    api.tag);
+       }
+      bgp_redistribute_delete((struct prefix *)&p, api.type);
+    }
+
+  return 0;
+}
+
+/* Zebra route add and delete treatment. */
+static int
+zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv6 api;
+  struct in6_addr nexthop;
+  struct prefix_ipv6 p;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  memset (&nexthop, 0, sizeof (struct in6_addr));
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv6 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      stream_get (&nexthop, s, 16);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      stream_getl (s); /* ifindex, unused */
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  /* Simply ignore link-local address. */
+  if (IN6_IS_ADDR_LINKLOCAL (&p.prefix))
+    return 0;
+
+  if (command == ZEBRA_IPV6_ROUTE_ADD)
+    {
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET6_ADDRSTRLEN];
+         zlog_debug("Zebra rcvd: IPv6 route add %s %s/%d nexthop %s metric %u tag %d",
+                    zebra_route_string(api.type),
+                    inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])),
+                    p.prefixlen,
+                    inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+                    api.metric,
+                    api.tag);
+       }
+      bgp_redistribute_add ((struct prefix *)&p, NULL, &nexthop,
+                           api.metric, api.type, api.tag);
+    }
+  else
+    {
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET6_ADDRSTRLEN];
+         zlog_debug("Zebra rcvd: IPv6 route delete %s %s/%d "
+                    "nexthop %s metric %u tag %d",
+                    zebra_route_string(api.type),
+                    inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])),
+                    p.prefixlen,
+                    inet_ntop(AF_INET6, &nexthop, buf[1], sizeof(buf[1])),
+                    api.metric,
+                    api.tag);
+       }
+      bgp_redistribute_delete ((struct prefix *) &p, api.type);
+    }
+  
+  return 0;
+}
+
+struct interface *
+if_lookup_by_ipv4 (struct in_addr *addr)
+{
+  struct listnode *ifnode;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *connected;
+  struct prefix_ipv4 p;
+  struct prefix *cp; 
+  
+  p.family = AF_INET;
+  p.prefix = *addr;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         cp = connected->address;
+           
+         if (cp->family == AF_INET)
+           if (prefix_match (cp, (struct prefix *)&p))
+             return ifp;
+       }
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_ipv4_exact (struct in_addr *addr)
+{
+  struct listnode *ifnode;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *connected;
+  struct prefix *cp; 
+  
+  for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         cp = connected->address;
+           
+         if (cp->family == AF_INET)
+           if (IPV4_ADDR_SAME (&cp->u.prefix4, addr))
+             return ifp;
+       }
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_ipv6 (struct in6_addr *addr)
+{
+  struct listnode *ifnode;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *connected;
+  struct prefix_ipv6 p;
+  struct prefix *cp; 
+  
+  p.family = AF_INET6;
+  p.prefix = *addr;
+  p.prefixlen = IPV6_MAX_BITLEN;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         cp = connected->address;
+           
+         if (cp->family == AF_INET6)
+           if (prefix_match (cp, (struct prefix *)&p))
+             return ifp;
+       }
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_ipv6_exact (struct in6_addr *addr)
+{
+  struct listnode *ifnode;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *connected;
+  struct prefix *cp; 
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         cp = connected->address;
+           
+         if (cp->family == AF_INET6)
+           if (IPV6_ADDR_SAME (&cp->u.prefix6, addr))
+             return ifp;
+       }
+    }
+  return NULL;
+}
+
+static int
+if_get_ipv6_global (struct interface *ifp, struct in6_addr *addr)
+{
+  struct listnode *cnode;
+  struct connected *connected;
+  struct prefix *cp; 
+  
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+    {
+      cp = connected->address;
+           
+      if (cp->family == AF_INET6)
+       if (! IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6))
+         {
+           memcpy (addr, &cp->u.prefix6, IPV6_MAX_BYTELEN);
+           return 1;
+         }
+    }
+  return 0;
+}
+
+static int
+if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr)
+{
+  struct listnode *cnode;
+  struct connected *connected;
+  struct prefix *cp; 
+  
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+    {
+      cp = connected->address;
+           
+      if (cp->family == AF_INET6)
+       if (IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6))
+         {
+           memcpy (addr, &cp->u.prefix6, IPV6_MAX_BYTELEN);
+           return 1;
+         }
+    }
+  return 0;
+}
+
+static int
+if_get_ipv4_address (struct interface *ifp, struct in_addr *addr)
+{
+  struct listnode *cnode;
+  struct connected *connected;
+  struct prefix *cp;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+    {
+      cp = connected->address;
+      if ((cp->family == AF_INET) && !ipv4_martian(&(cp->u.prefix4)))
+         {
+           *addr = cp->u.prefix4;
+           return 1;
+         }
+    }
+  return 0;
+}
+
+int
+bgp_nexthop_set (union sockunion *local, union sockunion *remote, 
+                struct bgp_nexthop *nexthop, struct peer *peer)
+{
+  int ret = 0;
+  struct interface *ifp = NULL;
+
+  memset (nexthop, 0, sizeof (struct bgp_nexthop));
+
+  if (!local)
+    return -1;
+  if (!remote)
+    return -1;
+
+  if (local->sa.sa_family == AF_INET)
+    {
+      nexthop->v4 = local->sin.sin_addr;
+      if (peer->update_if)
+        ifp = if_lookup_by_name (peer->update_if);
+      else
+        ifp = if_lookup_by_ipv4_exact (&local->sin.sin_addr);
+    }
+  if (local->sa.sa_family == AF_INET6)
+    {
+      if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr))
+       {
+         if (peer->ifname)
+           ifp = if_lookup_by_name (peer->ifname);
+       }
+      else if (peer->update_if)
+        ifp = if_lookup_by_name (peer->update_if);
+      else
+        ifp = if_lookup_by_ipv6_exact (&local->sin6.sin6_addr);
+    }
+
+  if (!ifp)
+    return -1;
+
+  nexthop->ifp = ifp;
+
+  /* IPv4 connection. */
+  if (local->sa.sa_family == AF_INET)
+    {
+      /* IPv6 nexthop*/
+      ret = if_get_ipv6_global (ifp, &nexthop->v6_global);
+
+      /* There is no global nexthop. */
+      if (!ret)
+       if_get_ipv6_local (ifp, &nexthop->v6_global);
+      else
+       if_get_ipv6_local (ifp, &nexthop->v6_local);
+    }
+
+  /* IPv6 connection. */
+  if (local->sa.sa_family == AF_INET6)
+    {
+      struct interface *direct = NULL;
+
+      /* IPv4 nexthop. */
+      ret = if_get_ipv4_address(ifp, &nexthop->v4);
+      if (!ret && peer->local_id.s_addr)
+       nexthop->v4 = peer->local_id;
+
+      /* Global address*/
+      if (! IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr))
+       {
+         memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, 
+                 IPV6_MAX_BYTELEN);
+
+         /* If directory connected set link-local address. */
+         direct = if_lookup_by_ipv6 (&remote->sin6.sin6_addr);
+         if (direct)
+           if_get_ipv6_local (ifp, &nexthop->v6_local);
+       }
+      else
+       /* Link-local address. */
+       {
+         ret = if_get_ipv6_global (ifp, &nexthop->v6_global);
+
+         /* If there is no global address.  Set link-local address as
+             global.  I know this break RFC specification... */
+         if (!ret)
+           memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, 
+                   IPV6_MAX_BYTELEN);
+         else
+           memcpy (&nexthop->v6_local, &local->sin6.sin6_addr, 
+                   IPV6_MAX_BYTELEN);
+       }
+    }
+
+  if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr) ||
+      if_lookup_by_ipv6 (&remote->sin6.sin6_addr))
+    peer->shared_network = 1;
+  else
+    peer->shared_network = 0;
+
+  /* KAME stack specific treatment.  */
+#ifdef KAME
+  if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_global)
+      && IN6_LINKLOCAL_IFINDEX (nexthop->v6_global))
+    {
+      SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_global, 0);
+    }
+  if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_local)
+      && IN6_LINKLOCAL_IFINDEX (nexthop->v6_local))
+    {
+      SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_local, 0);
+    }
+#endif /* KAME */
+  return ret;
+}
+
+void
+bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, safi_t safi)
+{
+  int flags;
+  u_char distance;
+  struct peer *peer;
+  struct bgp_info *mpinfo;
+  size_t oldsize, newsize;
+  u_int32_t nhcount;
+  route_tag_t tag = 0;
+
+  if (zclient->sock < 0)
+    return;
+
+  if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT))
+    return;
+
+  flags = 0;
+  peer = info->peer;
+
+  if ((info->attr->extra) && (info->attr->extra->tag != 0))
+    tag = info->attr->extra->tag;
+
+  if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED)
+    {
+      SET_FLAG (flags, ZEBRA_FLAG_IBGP);
+      SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
+    }
+
+  if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1)
+      || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+    SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
+
+  nhcount = 1 + bgp_info_mpath_count (info);
+
+  if (p->family == AF_INET)
+    {
+      struct zapi_ipv4 api;
+      struct in_addr *nexthop;
+
+      /* resize nexthop buffer size if necessary */
+      if ((oldsize = stream_get_size (bgp_nexthop_buf)) <
+          (sizeof (struct in_addr *) * nhcount))
+        {
+          newsize = (sizeof (struct in_addr *) * nhcount);
+          newsize = stream_resize (bgp_nexthop_buf, newsize);
+          if (newsize == oldsize)
+            {
+                 zlog_err ("can't resize nexthop buffer");
+                 return;
+            }
+        }
+      stream_reset (bgp_nexthop_buf);
+
+      api.vrf_id = VRF_DEFAULT;
+      api.flags = flags;
+      nexthop = &info->attr->nexthop;
+      stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *));
+      for (mpinfo = bgp_info_mpath_first (info); mpinfo;
+          mpinfo = bgp_info_mpath_next (mpinfo))
+       {
+         nexthop = &mpinfo->attr->nexthop;
+         stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *));
+       }
+
+      api.type = ZEBRA_ROUTE_BGP;
+      api.message = 0;
+      api.safi = safi;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      api.nexthop_num = nhcount;
+      api.nexthop = (struct in_addr **)STREAM_DATA (bgp_nexthop_buf);
+      api.ifindex_num = 0;
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = info->attr->med;
+
+      if (tag)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+          api.tag = tag;
+        }
+
+      distance = bgp_distance_apply (p, info, bgp);
+
+      if (distance)
+       {
+         SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+         api.distance = distance;
+       }
+
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         int i;
+         char buf[2][INET_ADDRSTRLEN];
+         zlog_debug("Zebra send: IPv4 route add %s/%d nexthop %s metric %u"
+                    " tag %u count %d",
+                    inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
+                    p->prefixlen,
+                    inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])),
+                    api.metric, api.tag, api.nexthop_num);
+         for (i = 1; i < api.nexthop_num; i++)
+           zlog_debug("Zebra send: IPv4 route add [nexthop %d] %s",
+                      i, inet_ntop(AF_INET, api.nexthop[i], buf[1],
+                                   sizeof(buf[1])));
+       }
+
+      zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, 
+                       (struct prefix_ipv4 *) p, &api);
+    }
+
+  /* We have to think about a IPv6 link-local address curse. */
+  if (p->family == AF_INET6)
+    {
+      ifindex_t ifindex;
+      struct in6_addr *nexthop;
+      struct zapi_ipv6 api;
+      int valid_nh_count = 0;
+
+      /* resize nexthop buffer size if necessary */
+      if ((oldsize = stream_get_size (bgp_nexthop_buf)) <
+          (sizeof (struct in6_addr *) * nhcount))
+        {
+          newsize = (sizeof (struct in6_addr *) * nhcount);
+          newsize = stream_resize (bgp_nexthop_buf, newsize);
+          if (newsize == oldsize)
+            {
+              zlog_err ("can't resize nexthop buffer");
+              return;
+            }
+        }
+      stream_reset (bgp_nexthop_buf);
+
+      /* resize ifindices buffer size if necessary */
+      if ((oldsize = stream_get_size (bgp_ifindices_buf)) <
+          (sizeof (unsigned int) * nhcount))
+        {
+          newsize = (sizeof (unsigned int) * nhcount);
+          newsize = stream_resize (bgp_ifindices_buf, newsize);
+          if (newsize == oldsize)
+            {
+              zlog_err ("can't resize nexthop buffer");
+              return;
+            }
+        }
+      stream_reset (bgp_ifindices_buf);
+
+      ifindex = 0;
+      nexthop = NULL;
+
+      assert (info->attr->extra);
+      
+      /* Only global address nexthop exists. */
+      if (info->attr->extra->mp_nexthop_len == 16)
+       nexthop = &info->attr->extra->mp_nexthop_global;
+      
+      /* If both global and link-local address present. */
+      if (info->attr->extra->mp_nexthop_len == 32)
+       {
+         /* Workaround for Cisco's nexthop bug.  */
+         if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global)
+             && peer->su_remote->sa.sa_family == AF_INET6)
+           nexthop = &peer->su_remote->sin6.sin6_addr;
+         else
+           nexthop = &info->attr->extra->mp_nexthop_local;
+
+         if (info->peer->nexthop.ifp)
+           ifindex = info->peer->nexthop.ifp->ifindex;
+       }
+
+      if (nexthop == NULL)
+       return;
+
+      if (!ifindex)
+       {
+         if (info->peer->ifname)
+           ifindex = ifname2ifindex (info->peer->ifname);
+         else if (info->peer->nexthop.ifp)
+           ifindex = info->peer->nexthop.ifp->ifindex;
+       }
+      stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *));
+      stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int));
+      valid_nh_count++;
+
+      for (mpinfo = bgp_info_mpath_first (info); mpinfo;
+           mpinfo = bgp_info_mpath_next (mpinfo))
+       {
+         ifindex = 0;
+
+          /* Only global address nexthop exists. */
+          if (mpinfo->attr->extra->mp_nexthop_len == 16)
+              nexthop = &mpinfo->attr->extra->mp_nexthop_global;
+
+          /* If both global and link-local address present. */
+         if (mpinfo->attr->extra->mp_nexthop_len == 32)
+            {
+              /* Workaround for Cisco's nexthop bug.  */
+              if (IN6_IS_ADDR_UNSPECIFIED (&mpinfo->attr->extra->mp_nexthop_global)
+                  && mpinfo->peer->su_remote->sa.sa_family == AF_INET6)
+                {
+                 nexthop = &mpinfo->peer->su_remote->sin6.sin6_addr;
+                }
+              else
+                {
+                 nexthop = &mpinfo->attr->extra->mp_nexthop_local;
+               }
+
+              if (mpinfo->peer->nexthop.ifp)
+                {
+                  ifindex = mpinfo->peer->nexthop.ifp->ifindex;
+                }
+            }
+
+         if (nexthop == NULL)
+           {
+             continue;
+           }
+
+          if (!ifindex)
+           {
+             if (mpinfo->peer->ifname)
+                {
+                 ifindex = if_nametoindex (mpinfo->peer->ifname);
+               }
+             else if (mpinfo->peer->nexthop.ifp)
+               {
+                 ifindex = mpinfo->peer->nexthop.ifp->ifindex;
+               }
+           }
+
+         if (ifindex == 0)
+           {
+             continue;
+           }
+
+          stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *));
+          stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int));
+          valid_nh_count++;
+       }
+
+      /* Make Zebra API structure. */
+      api.vrf_id = VRF_DEFAULT;
+      api.flags = flags;
+      api.type = ZEBRA_ROUTE_BGP;
+      api.message = 0;
+      api.safi = safi;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      api.nexthop_num = valid_nh_count;
+      api.nexthop = (struct in6_addr **)STREAM_DATA (bgp_nexthop_buf);
+      SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+      api.ifindex_num = valid_nh_count;
+      api.ifindex = (ifindex_t *)STREAM_DATA (bgp_ifindices_buf);
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = info->attr->med;
+
+      distance = ipv6_bgp_distance_apply (p, info, bgp);
+
+      if (distance)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+          api.distance = distance;
+        }
+      
+      if (tag)
+       {
+         SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+         api.tag = tag;
+       }
+
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET6_ADDRSTRLEN];
+         zlog_debug("Zebra send: IPv6 route add %s/%d nexthop %s metric %u"
+                     " tag %u",
+                    inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])),
+                    p->prefixlen,
+                    inet_ntop(AF_INET6, nexthop, buf[1], sizeof(buf[1])),
+                    api.metric, api.tag);
+       }
+
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, 
+                       (struct prefix_ipv6 *) p, &api);
+    }
+}
+
+void
+bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
+{
+  int flags;
+  struct peer *peer;
+
+  if (zclient->sock < 0)
+    return;
+
+  if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT))
+    return;
+
+  peer = info->peer;
+  flags = 0;
+
+  if (peer->sort == BGP_PEER_IBGP)
+    {
+      SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
+      SET_FLAG (flags, ZEBRA_FLAG_IBGP);
+    }
+
+  if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1)
+      || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+    SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
+
+  if (p->family == AF_INET)
+    {
+      struct zapi_ipv4 api;
+
+      api.vrf_id = VRF_DEFAULT;
+      api.flags = flags;
+
+      api.type = ZEBRA_ROUTE_BGP;
+      api.message = 0;
+      api.safi = safi;
+      api.nexthop_num = 0;
+      api.ifindex_num = 0;
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = info->attr->med;
+
+      if ((info->attr->extra) && (info->attr->extra->tag != 0))
+        {
+          SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+          api.tag = info->attr->extra->tag;
+        }
+
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET_ADDRSTRLEN];
+         zlog_debug("Zebra send: IPv4 route delete %s/%d metric %u tag %d",
+                    inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
+                    p->prefixlen,
+                    api.metric,
+                    api.tag);
+       }
+
+      zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, 
+                       (struct prefix_ipv4 *) p, &api);
+    }
+
+  /* We have to think about a IPv6 link-local address curse. */
+  if (p->family == AF_INET6)
+    {
+      struct zapi_ipv6 api;
+      
+      api.vrf_id = VRF_DEFAULT;
+      api.flags = flags;
+      api.type = ZEBRA_ROUTE_BGP;
+      api.message = 0;
+      api.safi = safi;
+      api.nexthop_num = 0;
+      api.ifindex_num = 0;
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = info->attr->med;
+
+      if ((info->attr->extra) && (info->attr->extra->tag != 0))
+        {
+          SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+          api.tag = info->attr->extra->tag;
+        }
+
+      if (BGP_DEBUG(zebra, ZEBRA))
+       {
+         char buf[2][INET6_ADDRSTRLEN];
+         zlog_debug("Zebra send: IPv6 route delete %s/%d metric %u tag %d",
+                    inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])),
+                    p->prefixlen,
+                    api.metric,
+                    api.tag);
+       }
+
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, 
+                       (struct prefix_ipv6 *) p, &api);
+    }
+}
+
+/* Other routes redistribution into BGP. */
+int
+bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type)
+{
+  /* Set flag to BGP instance. */
+  bgp->redist[afi][type] = 1;
+
+  /* Return if already redistribute flag is set. */
+  if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return CMD_WARNING;
+
+  vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT);
+
+  /* Return if zebra connection is not established. */
+  if (zclient->sock < 0)
+    return CMD_WARNING;
+
+  if (BGP_DEBUG(zebra, ZEBRA))
+    zlog_debug("Zebra send: redistribute add %s", zebra_route_string(type));
+    
+  /* Send distribute add message to zebra. */
+  zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+
+  return CMD_SUCCESS;
+}
+
+/* Redistribute with route-map specification.  */
+int
+bgp_redistribute_rmap_set (struct bgp *bgp, afi_t afi, int type, 
+                           const char *name)
+{
+  if (bgp->rmap[afi][type].name
+      && (strcmp (bgp->rmap[afi][type].name, name) == 0))
+    return 0;
+
+  if (bgp->rmap[afi][type].name)
+    free (bgp->rmap[afi][type].name);
+  bgp->rmap[afi][type].name = strdup (name);
+  bgp->rmap[afi][type].map = route_map_lookup_by_name (name);
+
+  return 1;
+}
+
+/* Redistribute with metric specification.  */
+int
+bgp_redistribute_metric_set (struct bgp *bgp, afi_t afi, int type,
+                            u_int32_t metric)
+{
+  if (bgp->redist_metric_flag[afi][type]
+      && bgp->redist_metric[afi][type] == metric)
+    return 0;
+
+  bgp->redist_metric_flag[afi][type] = 1;
+  bgp->redist_metric[afi][type] = metric;
+
+  return 1;
+}
+
+/* Unset redistribution.  */
+int
+bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type)
+{
+  /* Unset flag from BGP instance. */
+  bgp->redist[afi][type] = 0;
+
+  /* Unset route-map. */
+  if (bgp->rmap[afi][type].name)
+    free (bgp->rmap[afi][type].name);
+  bgp->rmap[afi][type].name = NULL;
+  bgp->rmap[afi][type].map = NULL;
+
+  /* Unset metric. */
+  bgp->redist_metric_flag[afi][type] = 0;
+  bgp->redist_metric[afi][type] = 0;
+
+  /* Return if zebra connection is disabled. */
+  if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return CMD_WARNING;
+  vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT);
+
+  if (bgp->redist[AFI_IP][type] == 0 
+      && bgp->redist[AFI_IP6][type] == 0 
+      && zclient->sock >= 0)
+    {
+      /* Send distribute delete message to zebra. */
+      if (BGP_DEBUG(zebra, ZEBRA))
+       zlog_debug("Zebra send: redistribute delete %s",
+                  zebra_route_string(type));
+      zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type,
+                               VRF_DEFAULT);
+    }
+  
+  /* Withdraw redistributed routes from current BGP's routing table. */
+  bgp_redistribute_withdraw (bgp, afi, type);
+
+  return CMD_SUCCESS;
+}
+
+void
+bgp_zclient_reset (void)
+{
+  zclient_reset (zclient);
+}
+
+static void
+bgp_zebra_connected (struct zclient *zclient)
+{
+  zclient_num_connects++;
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+void
+bgp_zebra_init (struct thread_master *master)
+{
+  zclient_num_connects = 0;
+
+  /* Set default values. */
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_BGP);
+  zclient->zebra_connected = bgp_zebra_connected;
+  zclient->router_id_update = bgp_router_id_update;
+  zclient->interface_add = bgp_interface_add;
+  zclient->interface_delete = bgp_interface_delete;
+  zclient->interface_address_add = bgp_interface_address_add;
+  zclient->interface_address_delete = bgp_interface_address_delete;
+  zclient->ipv4_route_add = zebra_read_ipv4;
+  zclient->ipv4_route_delete = zebra_read_ipv4;
+  zclient->interface_up = bgp_interface_up;
+  zclient->interface_down = bgp_interface_down;
+  zclient->ipv6_route_add = zebra_read_ipv6;
+  zclient->ipv6_route_delete = zebra_read_ipv6;
+  zclient->nexthop_update = bgp_read_nexthop_update;
+
+  bgp_nexthop_buf = stream_new(BGP_NEXTHOP_BUF_SIZE);
+  bgp_ifindices_buf = stream_new(BGP_IFINDICES_BUF_SIZE);
+}
+
+void
+bgp_zebra_destroy(void)
+{
+  if (zclient == NULL)
+    return;
+  zclient_stop(zclient);
+  zclient_free(zclient);
+  zclient = NULL;
+}
+
+int
+bgp_zebra_num_connects(void)
+{
+  return zclient_num_connects;
+}
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
new file mode 100644 (file)
index 0000000..5d4ed62
--- /dev/null
@@ -0,0 +1,53 @@
+/* zebra connection and redistribute fucntions.
+   Copyright (C) 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_ZEBRA_H
+#define _QUAGGA_BGP_ZEBRA_H
+
+#define BGP_NEXTHOP_BUF_SIZE (8 * sizeof (struct in_addr *))
+#define BGP_IFINDICES_BUF_SIZE (8 * sizeof (unsigned int))
+
+extern struct stream *bgp_nexthop_buf;
+extern struct in_addr router_id_zebra;
+extern struct stream *bgp_ifindices_buf;
+
+extern void bgp_zebra_init (struct thread_master *master);
+extern void bgp_zebra_destroy (void);
+extern int bgp_if_update_all (void);
+extern int bgp_config_write_maxpaths (struct vty *, struct bgp *, afi_t,
+                                     safi_t, int *);
+extern int bgp_config_write_redistribute (struct vty *, struct bgp *, afi_t, safi_t,
+                                  int *);
+extern void bgp_zebra_announce (struct prefix *, struct bgp_info *, struct bgp *, safi_t);
+extern void bgp_zebra_withdraw (struct prefix *, struct bgp_info *, safi_t);
+
+extern int bgp_redistribute_set (struct bgp *, afi_t, int);
+extern int bgp_redistribute_rmap_set (struct bgp *, afi_t, int, const char *);
+extern int bgp_redistribute_metric_set (struct bgp *, afi_t, int, u_int32_t);
+extern int bgp_redistribute_unset (struct bgp *, afi_t, int);
+
+extern struct interface *if_lookup_by_ipv4 (struct in_addr *);
+extern struct interface *if_lookup_by_ipv4_exact (struct in_addr *);
+extern struct interface *if_lookup_by_ipv6 (struct in6_addr *);
+extern struct interface *if_lookup_by_ipv6_exact (struct in6_addr *);
+
+extern int bgp_zebra_num_connects(void);
+
+#endif /* _QUAGGA_BGP_ZEBRA_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
new file mode 100644 (file)
index 0000000..6aeecb1
--- /dev/null
@@ -0,0 +1,5614 @@
+/* BGP-4, BGP-4+ daemon program
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "command.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "network.h"
+#include "memory.h"
+#include "filter.h"
+#include "routemap.h"
+#include "str.h"
+#include "log.h"
+#include "plist.h"
+#include "linklist.h"
+#include "workqueue.h"
+#include "table.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_encap.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nht.h"
+#ifdef HAVE_SNMP
+#include "bgpd/bgp_snmp.h"
+#endif /* HAVE_SNMP */
+
+/* BGP process wide configuration.  */
+static struct bgp_master bgp_master;
+
+extern struct in_addr router_id_zebra;
+
+/* BGP process wide configuration pointer to export.  */
+struct bgp_master *bm;
+
+/* BGP community-list.  */
+struct community_list_handler *bgp_clist;
+
+/* BGP global flag manipulation.  */
+int
+bgp_option_set (int flag)
+{
+  switch (flag)
+    {
+    case BGP_OPT_NO_FIB:
+    case BGP_OPT_MULTIPLE_INSTANCE:
+    case BGP_OPT_CONFIG_CISCO:
+    case BGP_OPT_NO_LISTEN:
+      SET_FLAG (bm->options, flag);
+      break;
+    default:
+      return BGP_ERR_INVALID_FLAG;
+    }
+  return 0;
+}
+
+int
+bgp_option_unset (int flag)
+{
+  switch (flag)
+    {
+    case BGP_OPT_MULTIPLE_INSTANCE:
+      if (listcount (bm->bgp) > 1)
+       return BGP_ERR_MULTIPLE_INSTANCE_USED;
+      /* Fall through.  */
+    case BGP_OPT_NO_FIB:
+    case BGP_OPT_CONFIG_CISCO:
+      UNSET_FLAG (bm->options, flag);
+      break;
+    default:
+      return BGP_ERR_INVALID_FLAG;
+    }
+  return 0;
+}
+
+int
+bgp_option_check (int flag)
+{
+  return CHECK_FLAG (bm->options, flag);
+}
+
+/* BGP flag manipulation.  */
+int
+bgp_flag_set (struct bgp *bgp, int flag)
+{
+  SET_FLAG (bgp->flags, flag);
+  return 0;
+}
+
+int
+bgp_flag_unset (struct bgp *bgp, int flag)
+{
+  UNSET_FLAG (bgp->flags, flag);
+  return 0;
+}
+
+int
+bgp_flag_check (struct bgp *bgp, int flag)
+{
+  return CHECK_FLAG (bgp->flags, flag);
+}
+
+/* Internal function to set BGP structure configureation flag.  */
+static void
+bgp_config_set (struct bgp *bgp, int config)
+{
+  SET_FLAG (bgp->config, config);
+}
+
+static void
+bgp_config_unset (struct bgp *bgp, int config)
+{
+  UNSET_FLAG (bgp->config, config);
+}
+
+static int
+bgp_config_check (struct bgp *bgp, int config)
+{
+  return CHECK_FLAG (bgp->config, config);
+}
+
+/* Set BGP router identifier. */
+static int
+bgp_router_id_set (struct bgp *bgp, struct in_addr *id)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (bgp_config_check (bgp, BGP_CONFIG_ROUTER_ID)
+      && IPV4_ADDR_SAME (&bgp->router_id, id))
+    return 0;
+
+  IPV4_ADDR_COPY (&bgp->router_id, id);
+  bgp_config_set (bgp, BGP_CONFIG_ROUTER_ID);
+
+  /* Set all peer's local identifier with this value. */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      IPV4_ADDR_COPY (&peer->local_id, id);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_RID_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+    }
+  return 0;
+}
+
+void
+bgp_router_id_zebra_bump (void)
+{
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    {
+      if (!bgp->router_id_static.s_addr)
+        bgp_router_id_set (bgp, &router_id_zebra);
+    }
+}
+
+int
+bgp_router_id_static_set (struct bgp *bgp, struct in_addr id)
+{
+  bgp->router_id_static = id;
+  bgp_router_id_set (bgp, id.s_addr ? &id : &router_id_zebra);
+  return 0;
+}
+
+/* BGP's cluster-id control. */
+int
+bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID)
+      && IPV4_ADDR_SAME (&bgp->cluster_id, cluster_id))
+    return 0;
+
+  IPV4_ADDR_COPY (&bgp->cluster_id, cluster_id);
+  bgp_config_set (bgp, BGP_CONFIG_CLUSTER_ID);
+
+  /* Clear all IBGP peer. */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (peer->sort != BGP_PEER_IBGP)
+       continue;
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_CLID_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+    }
+  return 0;
+}
+
+int
+bgp_cluster_id_unset (struct bgp *bgp)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (! bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID))
+    return 0;
+
+  bgp->cluster_id.s_addr = 0;
+  bgp_config_unset (bgp, BGP_CONFIG_CLUSTER_ID);
+
+  /* Clear all IBGP peer. */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (peer->sort != BGP_PEER_IBGP)
+       continue;
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_CLID_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+    }
+  return 0;
+}
+
+/* time_t value that is monotonicly increasing
+ * and uneffected by adjustments to system clock
+ */
+time_t bgp_clock (void)
+{
+  struct timeval tv;
+
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv);
+  return tv.tv_sec;
+}
+
+/* BGP timer configuration.  */
+int
+bgp_timers_set (struct bgp *bgp, u_int32_t keepalive, u_int32_t holdtime)
+{
+  bgp->default_keepalive = (keepalive < holdtime / 3 
+                           ? keepalive : holdtime / 3);
+  bgp->default_holdtime = holdtime;
+
+  return 0;
+}
+
+int
+bgp_timers_unset (struct bgp *bgp)
+{
+  bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+  bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+
+  return 0;
+}
+
+/* BGP confederation configuration.  */
+int
+bgp_confederation_id_set (struct bgp *bgp, as_t as)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  int already_confed;
+
+  if (as == 0)
+    return BGP_ERR_INVALID_AS;
+
+  /* Remember - were we doing confederation before? */
+  already_confed = bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION);
+  bgp->confed_id = as;
+  bgp_config_set (bgp, BGP_CONFIG_CONFEDERATION);
+
+  /* If we were doing confederation already, this is just an external
+     AS change.  Just Reset EBGP sessions, not CONFED sessions.  If we
+     were not doing confederation before, reset all EBGP sessions.  */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      /* We're looking for peers who's AS is not local or part of our
+        confederation.  */
+      if (already_confed)
+       {
+         if (peer_sort (peer) == BGP_PEER_EBGP)
+           {
+             peer->local_as = as;
+             if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+               {
+                 peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+
+             else
+               BGP_EVENT_ADD (peer, BGP_Stop);
+           }
+       }
+      else
+       {
+         /* Not doign confederation before, so reset every non-local
+            session */
+         if (peer_sort (peer) != BGP_PEER_IBGP)
+           {
+             /* Reset the local_as to be our EBGP one */
+             if (peer_sort (peer) == BGP_PEER_EBGP)
+               peer->local_as = as;
+             if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+               {
+                 peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+             else
+               BGP_EVENT_ADD (peer, BGP_Stop);
+           }
+       }
+    }
+  return 0;
+}
+
+int
+bgp_confederation_id_unset (struct bgp *bgp)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  bgp->confed_id = 0;
+  bgp_config_unset (bgp, BGP_CONFIG_CONFEDERATION);
+      
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      /* We're looking for peers who's AS is not local */
+      if (peer_sort (peer) != BGP_PEER_IBGP)
+       {
+         peer->local_as = bgp->as;
+         if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+           {
+             peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
+             bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+           }
+
+         else
+           BGP_EVENT_ADD (peer, BGP_Stop);
+       }
+    }
+  return 0;
+}
+
+/* Is an AS part of the confed or not? */
+int
+bgp_confederation_peers_check (struct bgp *bgp, as_t as)
+{
+  int i;
+
+  if (! bgp)
+    return 0;
+
+  for (i = 0; i < bgp->confed_peers_cnt; i++)
+    if (bgp->confed_peers[i] == as)
+      return 1;
+  
+  return 0;
+}
+
+/* Add an AS to the confederation set.  */
+int
+bgp_confederation_peers_add (struct bgp *bgp, as_t as)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (! bgp)
+    return BGP_ERR_INVALID_BGP;
+
+  if (bgp->as == as)
+    return BGP_ERR_INVALID_AS;
+
+  if (bgp_confederation_peers_check (bgp, as))
+    return -1;
+
+  if (bgp->confed_peers)
+    bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, 
+                                 bgp->confed_peers,
+                                 (bgp->confed_peers_cnt + 1) * sizeof (as_t));
+  else
+    bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST, 
+                                (bgp->confed_peers_cnt + 1) * sizeof (as_t));
+
+  bgp->confed_peers[bgp->confed_peers_cnt] = as;
+  bgp->confed_peers_cnt++;
+
+  if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (peer->as == as)
+           {
+             peer->local_as = bgp->as;
+             if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+               {
+                 peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+             else
+               BGP_EVENT_ADD (peer, BGP_Stop);
+           }
+       }
+    }
+  return 0;
+}
+
+/* Delete an AS from the confederation set.  */
+int
+bgp_confederation_peers_remove (struct bgp *bgp, as_t as)
+{
+  int i;
+  int j;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (! bgp)
+    return -1;
+
+  if (! bgp_confederation_peers_check (bgp, as))
+    return -1;
+
+  for (i = 0; i < bgp->confed_peers_cnt; i++)
+    if (bgp->confed_peers[i] == as)
+      for(j = i + 1; j < bgp->confed_peers_cnt; j++)
+       bgp->confed_peers[j - 1] = bgp->confed_peers[j];
+
+  bgp->confed_peers_cnt--;
+
+  if (bgp->confed_peers_cnt == 0)
+    {
+      if (bgp->confed_peers)
+       XFREE (MTYPE_BGP_CONFED_LIST, bgp->confed_peers);
+      bgp->confed_peers = NULL;
+    }
+  else
+    bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST,
+                                 bgp->confed_peers,
+                                 bgp->confed_peers_cnt * sizeof (as_t));
+
+  /* Now reset any peer who's remote AS has just been removed from the
+     CONFED */
+  if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (peer->as == as)
+           {
+             peer->local_as = bgp->confed_id;
+             if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+               {
+                 peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+             else
+               BGP_EVENT_ADD (peer, BGP_Stop);
+           }
+       }
+    }
+
+  return 0;
+}
+
+/* Local preference configuration.  */
+int
+bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref)
+{
+  if (! bgp)
+    return -1;
+
+  bgp->default_local_pref = local_pref;
+
+  return 0;
+}
+
+int
+bgp_default_local_preference_unset (struct bgp *bgp)
+{
+  if (! bgp)
+    return -1;
+
+  bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+
+  return 0;
+}
+
+/* If peer is RSERVER_CLIENT in at least one address family and is not member
+    of a peer_group for that family, return 1.
+    Used to check wether the peer is included in list bgp->rsclient. */
+int
+peer_rsclient_active (struct peer *peer)
+{
+  int i;
+  int j;
+
+  for (i=AFI_IP; i < AFI_MAX; i++)
+    for (j=SAFI_UNICAST; j < SAFI_MAX; j++)
+      if (CHECK_FLAG(peer->af_flags[i][j], PEER_FLAG_RSERVER_CLIENT)
+            && ! peer->af_group[i][j])
+        return 1;
+  return 0;
+}
+
+/* Peer comparison function for sorting.  */
+static int
+peer_cmp (struct peer *p1, struct peer *p2)
+{
+  return sockunion_cmp (&p1->su, &p2->su);
+}
+
+int
+peer_af_flag_check (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
+{
+  return CHECK_FLAG (peer->af_flags[afi][safi], flag);
+}
+
+/* Reset all address family specific configuration.  */
+static void
+peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi)
+{
+  int i;
+  struct bgp_filter *filter;
+  char orf_name[BUFSIZ];
+
+  filter = &peer->filter[afi][safi];
+
+  /* Clear neighbor filter and route-map */
+  for (i = FILTER_IN; i < FILTER_MAX; i++)
+    {
+      if (filter->dlist[i].name)
+       {
+         free (filter->dlist[i].name);
+         filter->dlist[i].name = NULL;
+       }
+      if (filter->plist[i].name)
+       {
+         free (filter->plist[i].name);
+         filter->plist[i].name = NULL; 
+       }
+      if (filter->aslist[i].name)
+       {
+         free (filter->aslist[i].name);
+         filter->aslist[i].name = NULL;
+       }
+   }
+ for (i = RMAP_IN; i < RMAP_MAX; i++)
+       {
+      if (filter->map[i].name)
+       {
+         free (filter->map[i].name);
+         filter->map[i].name = NULL;
+       }
+    }
+
+  /* Clear unsuppress map.  */
+  if (filter->usmap.name)
+    free (filter->usmap.name);
+  filter->usmap.name = NULL;
+  filter->usmap.map = NULL;
+
+  /* Clear neighbor's all address family flags.  */
+  peer->af_flags[afi][safi] = 0;
+
+  /* Clear neighbor's all address family sflags. */
+  peer->af_sflags[afi][safi] = 0;
+
+  /* Clear neighbor's all address family capabilities. */
+  peer->af_cap[afi][safi] = 0;
+
+  /* Clear ORF info */
+  peer->orf_plist[afi][safi] = NULL;
+  sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
+  prefix_bgp_orf_remove_all (afi, orf_name);
+
+  /* Set default neighbor send-community.  */
+  if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
+    {
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY);
+    }
+
+  /* Clear neighbor default_originate_rmap */
+  if (peer->default_rmap[afi][safi].name)
+    free (peer->default_rmap[afi][safi].name);
+  peer->default_rmap[afi][safi].name = NULL;
+  peer->default_rmap[afi][safi].map = NULL;
+
+  /* Clear neighbor maximum-prefix */
+  peer->pmax[afi][safi] = 0;
+  peer->pmax_threshold[afi][safi] = MAXIMUM_PREFIX_THRESHOLD_DEFAULT;
+}
+
+/* peer global config reset */
+static void
+peer_global_config_reset (struct peer *peer)
+{
+  peer->weight = 0;
+  peer->change_local_as = 0;
+  peer->ttl = 0;
+  peer->gtsm_hops = 0;
+  if (peer->update_source)
+    {
+      sockunion_free (peer->update_source);
+      peer->update_source = NULL;
+    }
+  if (peer->update_if)
+    {
+      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      peer->update_if = NULL;
+    }
+
+  if (peer_sort (peer) == BGP_PEER_IBGP)
+    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+  else
+    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+
+  peer->flags = 0;
+  peer->config = 0;
+  peer->holdtime = 0;
+  peer->keepalive = 0;
+  peer->connect = 0;
+  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+}
+
+/* Check peer's AS number and determines if this peer is IBGP or EBGP */
+static bgp_peer_sort_t
+peer_calc_sort (struct peer *peer)
+{
+  struct bgp *bgp;
+
+  bgp = peer->bgp;
+
+  /* Peer-group */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (peer->as)
+       return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP);
+      else
+       {
+         struct peer *peer1;
+         peer1 = listnode_head (peer->group->peer);
+         if (peer1)
+           return (peer1->local_as == peer1->as 
+                   ? BGP_PEER_IBGP : BGP_PEER_EBGP);
+       } 
+      return BGP_PEER_INTERNAL;
+    }
+
+  /* Normal peer */
+  if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
+    {
+      if (peer->local_as == 0)
+       return BGP_PEER_INTERNAL;
+
+      if (peer->local_as == peer->as)
+       {
+         if (peer->local_as == bgp->confed_id)
+           return BGP_PEER_EBGP;
+         else
+           return BGP_PEER_IBGP;
+       }
+
+      if (bgp_confederation_peers_check (bgp, peer->as))
+       return BGP_PEER_CONFED;
+
+      return BGP_PEER_EBGP;
+    }
+  else
+    {
+      return (peer->local_as == 0
+             ? BGP_PEER_INTERNAL : peer->local_as == peer->as
+             ? BGP_PEER_IBGP : BGP_PEER_EBGP);
+    }
+}
+
+/* Calculate and cache the peer "sort" */
+bgp_peer_sort_t
+peer_sort (struct peer *peer)
+{
+  peer->sort = peer_calc_sort (peer);
+  return peer->sort;
+}
+
+static void
+peer_free (struct peer *peer)
+{
+  assert (peer->status == Deleted);
+
+  /* this /ought/ to have been done already through bgp_stop earlier,
+   * but just to be sure.. 
+   */
+  bgp_timer_set (peer);
+  BGP_READ_OFF (peer->t_read);
+  BGP_WRITE_OFF (peer->t_write);
+  BGP_EVENT_FLUSH (peer);
+  
+  if (peer->desc)
+    {
+      XFREE (MTYPE_PEER_DESC, peer->desc);
+      peer->desc = NULL;
+    }
+  
+  /* Free allocated host character. */
+  if (peer->host)
+    {
+      XFREE (MTYPE_BGP_PEER_HOST, peer->host);
+      peer->host = NULL;
+    }
+
+  /* Update source configuration.  */
+  if (peer->update_source)
+    {
+      sockunion_free (peer->update_source);
+      peer->update_source = NULL;
+    }
+  
+  if (peer->update_if)
+    {
+      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      peer->update_if = NULL;
+    }
+    
+  if (peer->clear_node_queue)
+    {
+      work_queue_free(peer->clear_node_queue);
+      peer->clear_node_queue = NULL;
+    }
+  
+  if (peer->notify.data)
+    XFREE(MTYPE_TMP, peer->notify.data);
+  
+  bgp_sync_delete (peer);
+
+  bgp_unlock(peer->bgp);
+
+  memset (peer, 0, sizeof (struct peer));
+  
+  XFREE (MTYPE_BGP_PEER, peer);
+}
+                                                
+/* increase reference count on a struct peer */
+struct peer *
+peer_lock_with_caller (const char *name, struct peer *peer)
+{
+  assert (peer && (peer->lock >= 0));
+
+#if 0
+  zlog_debug("%s peer_lock %p %d", name, peer, peer->lock);
+#endif
+
+  peer->lock++;
+  
+  return peer;
+}
+
+/* decrease reference count on a struct peer
+ * struct peer is freed and NULL returned if last reference
+ */
+struct peer *
+peer_unlock_with_caller (const char *name, struct peer *peer)
+{
+  assert (peer && (peer->lock > 0));
+
+#if 0
+  zlog_debug("%s peer_unlock %p %d", name, peer, peer->lock);
+#endif
+
+  peer->lock--;
+  
+  if (peer->lock == 0)
+    {
+      peer_free (peer);
+      return NULL;
+    }
+
+  return peer;
+}
+  
+/* Allocate new peer object, implicitely locked.  */
+static struct peer *
+peer_new (struct bgp *bgp)
+{
+  afi_t afi;
+  safi_t safi;
+  struct peer *peer;
+  struct servent *sp;
+  
+  /* bgp argument is absolutely required */
+  assert (bgp);
+  if (!bgp)
+    return NULL;
+  
+  /* Allocate new peer. */
+  peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer));
+
+  /* Set default value. */
+  peer->fd = -1;
+  peer->v_start = BGP_INIT_START_TIMER;
+  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+  peer->status = Idle;
+  peer->ostatus = Idle;
+  peer->weight = 0;
+  peer->password = NULL;
+  peer->bgp = bgp;
+  peer = peer_lock (peer); /* initial reference */
+  bgp_lock (bgp);
+
+  /* Set default flags.  */
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
+         {
+           SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+           SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+           SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY);
+         }
+       peer->orf_plist[afi][safi] = NULL;
+      }
+  SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+  /* Create buffers.  */
+  peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
+  peer->obuf = stream_fifo_new ();
+
+  /* We use a larger buffer for peer->work in the event that:
+   * - We RX a BGP_UPDATE where the attributes alone are just
+   *   under BGP_MAX_PACKET_SIZE
+   * - The user configures an outbound route-map that does many as-path
+   *   prepends or adds many communities.  At most they can have CMD_ARGC_MAX
+   *   args in a route-map so there is a finite limit on how large they can
+   *   make the attributes.
+   *
+   * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid bounds
+   * checking for every single attribute as we construct an UPDATE.
+   */
+  peer->work = stream_new (BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
+  peer->scratch = stream_new (BGP_MAX_PACKET_SIZE);
+
+  bgp_sync_init (peer);
+
+  /* Get service port number.  */
+  sp = getservbyname ("bgp", "tcp");
+  peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
+
+  return peer;
+}
+
+/* Create new BGP peer.  */
+static struct peer *
+peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
+            as_t remote_as, afi_t afi, safi_t safi)
+{
+  int active;
+  struct peer *peer;
+  char buf[SU_ADDRSTRLEN];
+
+  peer = peer_new (bgp);
+  peer->su = *su;
+  peer->local_as = local_as;
+  peer->as = remote_as;
+  peer->local_id = bgp->router_id;
+  peer->v_holdtime = bgp->default_holdtime;
+  peer->v_keepalive = bgp->default_keepalive;
+  if (peer_sort (peer) == BGP_PEER_IBGP)
+    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+  else
+    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+    
+  peer = peer_lock (peer); /* bgp peer list reference */
+  listnode_add_sort (bgp->peer, peer);
+
+  active = peer_active (peer);
+
+  if (afi && safi)
+    peer->afc[afi][safi] = 1;
+
+  /* Last read and reset time set */
+  peer->readtime = peer->resettime = bgp_clock ();
+
+  /* Make peer's address string. */
+  sockunion2str (su, buf, SU_ADDRSTRLEN);
+  peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
+
+  /* Set up peer's events and timers. */
+  if (! active && peer_active (peer))
+    bgp_timer_set (peer);
+
+  return peer;
+}
+
+/* Make accept BGP peer.  Called from bgp_accept (). */
+struct peer *
+peer_create_accept (struct bgp *bgp)
+{
+  struct peer *peer;
+
+  peer = peer_new (bgp);
+  
+  peer = peer_lock (peer); /* bgp peer list reference */
+  listnode_add_sort (bgp->peer, peer);
+
+  return peer;
+}
+
+/* Change peer's AS number.  */
+static void
+peer_as_change (struct peer *peer, as_t as)
+{
+  struct peer *conf;
+
+  /* Stop peer. */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  peer->as = as;
+
+  if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION)
+      && ! bgp_confederation_peers_check (peer->bgp, as)
+      && peer->bgp->as != as)
+    peer->local_as = peer->bgp->confed_id;
+  else
+    peer->local_as = peer->bgp->as;
+
+  /* Advertisement-interval reset */
+  conf = NULL;
+  if (peer->group)
+    conf = peer->group->conf;
+
+  if (conf && CHECK_FLAG (conf->config, PEER_CONFIG_ROUTEADV))
+      peer->v_routeadv = conf->routeadv;
+  else
+    if (peer_sort (peer) == BGP_PEER_IBGP)
+      peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+    else
+      peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+
+  /* reflector-client reset */
+  if (peer_sort (peer) != BGP_PEER_IBGP)
+    {
+      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MPLS_VPN],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_ENCAP],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MPLS_VPN],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_ENCAP],
+                 PEER_FLAG_REFLECTOR_CLIENT);
+    }
+
+  /* local-as reset */
+  if (peer_sort (peer) != BGP_PEER_EBGP)
+    {
+      peer->change_local_as = 0;
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+    }
+}
+
+/* If peer does not exist, create new one.  If peer already exists,
+   set AS number to the peer.  */
+int
+peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as,
+               afi_t afi, safi_t safi)
+{
+  struct peer *peer;
+  as_t local_as;
+
+  peer = peer_lookup (bgp, su);
+
+  if (peer)
+    {
+      /* When this peer is a member of peer-group.  */
+      if (peer->group)
+       {
+         if (peer->group->conf->as)
+           {
+             /* Return peer group's AS number.  */
+             *as = peer->group->conf->as;
+             return BGP_ERR_PEER_GROUP_MEMBER;
+           }
+         if (peer_sort (peer->group->conf) == BGP_PEER_IBGP)
+           {
+             if (bgp->as != *as)
+               {
+                 *as = peer->as;
+                 return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+               }
+           }
+         else
+           {
+             if (bgp->as == *as)
+               {
+                 *as = peer->as;
+                 return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+               }
+           }
+       }
+
+      /* Existing peer's AS number change. */
+      if (peer->as != *as)
+       peer_as_change (peer, *as);
+    }
+  else
+    {
+
+      /* If the peer is not part of our confederation, and its not an
+        iBGP peer then spoof the source AS */
+      if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)
+         && ! bgp_confederation_peers_check (bgp, *as) 
+         && bgp->as != *as)
+       local_as = bgp->confed_id;
+      else
+       local_as = bgp->as;
+      
+      /* If this is IPv4 unicast configuration and "no bgp default
+         ipv4-unicast" is specified. */
+
+      if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)
+         && afi == AFI_IP && safi == SAFI_UNICAST)
+       peer_create (su, bgp, local_as, *as, 0, 0); 
+      else
+       peer_create (su, bgp, local_as, *as, afi, safi); 
+    }
+
+  return 0;
+}
+
+/* Activate the peer or peer group for specified AFI and SAFI.  */
+int
+peer_activate (struct peer *peer, afi_t afi, safi_t safi)
+{
+  int active;
+
+  if (peer->afc[afi][safi])
+    return 0;
+
+  /* Activate the address family configuration. */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    peer->afc[afi][safi] = 1;
+  else
+    {
+      active = peer_active (peer);
+
+      peer->afc[afi][safi] = 1;
+
+      if (! active && peer_active (peer))
+       bgp_timer_set (peer);
+      else
+       {
+         if (peer->status == Established)
+           {
+             if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
+               {
+                 peer->afc_adv[afi][safi] = 1;
+                 bgp_capability_send (peer, afi, safi,
+                                      CAPABILITY_CODE_MP,
+                                      CAPABILITY_ACTION_SET);
+                 if (peer->afc_recv[afi][safi])
+                   {
+                     peer->afc_nego[afi][safi] = 1;
+                     bgp_announce_route (peer, afi, safi);
+                   }
+               }
+             else
+               {
+                 peer->last_reset = PEER_DOWN_AF_ACTIVATE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+           }
+       }
+    }
+  return 0;
+}
+
+int
+peer_deactivate (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer_group *group;
+  struct peer *peer1;
+  struct listnode *node, *nnode;
+
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
+       {
+         if (peer1->af_group[afi][safi])
+           return BGP_ERR_PEER_GROUP_MEMBER_EXISTS;
+       }
+    }
+  else
+    {
+      if (peer->af_group[afi][safi])
+       return BGP_ERR_PEER_BELONGS_TO_GROUP;
+    }
+
+  if (! peer->afc[afi][safi])
+    return 0;
+
+  /* De-activate the address family configuration. */
+  peer->afc[afi][safi] = 0;
+  peer_af_flag_reset (peer, afi, safi);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {  
+      if (peer->status == Established)
+       {
+         if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV))
+           {
+             peer->afc_adv[afi][safi] = 0;
+             peer->afc_nego[afi][safi] = 0;
+
+             if (peer_active_nego (peer))
+               {
+                 bgp_capability_send (peer, afi, safi,
+                                      CAPABILITY_CODE_MP,
+                                      CAPABILITY_ACTION_UNSET);
+                 bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL);
+                 peer->pcount[afi][safi] = 0;
+               }
+             else
+               {
+                 peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+                 bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                                  BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+               }
+           }
+         else
+           {
+             peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+             bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                              BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+           }
+       }
+    }
+  return 0;
+}
+
+int
+peer_afc_set (struct peer *peer, afi_t afi, safi_t safi, int enable)
+{
+  if (enable)
+    return peer_activate (peer, afi, safi);
+  else
+    return peer_deactivate (peer, afi, safi);
+}
+
+static void
+peer_nsf_stop (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+
+  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
+  UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE);
+
+  for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+    for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++)
+      peer->nsf[afi][safi] = 0;
+
+  if (peer->t_gr_restart)
+    {
+      BGP_TIMER_OFF (peer->t_gr_restart);
+      if (BGP_DEBUG (events, EVENTS))
+       zlog_debug ("%s graceful restart timer stopped", peer->host);
+    }
+  if (peer->t_gr_stale)
+    {
+      BGP_TIMER_OFF (peer->t_gr_stale);
+      if (BGP_DEBUG (events, EVENTS))
+       zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
+    }
+  bgp_clear_route_all (peer);
+}
+
+/* Delete peer from confguration.
+ *
+ * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
+ * it to "cool off" and refcounts to hit 0, at which state it is freed.
+ *
+ * This function /should/ take care to be idempotent, to guard against
+ * it being called multiple times through stray events that come in
+ * that happen to result in this function being called again.  That
+ * said, getting here for a "Deleted" peer is a bug in the neighbour
+ * FSM.
+ */
+int
+peer_delete (struct peer *peer)
+{
+  int i;
+  afi_t afi;
+  safi_t safi;
+  struct bgp *bgp;
+  struct bgp_filter *filter;
+  struct listnode *pn;
+
+  assert (peer->status != Deleted);
+  
+  bgp = peer->bgp;
+
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+    peer_nsf_stop (peer);
+
+  /* If this peer belongs to peer group, clear up the
+     relationship.  */
+  if (peer->group)
+    {
+      if ((pn = listnode_lookup (peer->group->peer, peer)))
+        {
+          peer = peer_unlock (peer); /* group->peer list reference */
+          list_delete_node (peer->group->peer, pn);
+        }
+      peer->group = NULL;
+    }
+  
+  /* Withdraw all information from routing table.  We can not use
+   * BGP_EVENT_ADD (peer, BGP_Stop) at here.  Because the event is
+   * executed after peer structure is deleted.
+   */
+  peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+  bgp_stop (peer);
+  bgp_fsm_change_status (peer, Deleted);
+  
+  /* Remove from NHT */
+  bgp_unlink_nexthop_by_peer (peer);
+  
+  /* Password configuration */
+  if (peer->password)
+    {
+      XFREE (MTYPE_PEER_PASSWORD, peer->password);
+      peer->password = NULL;
+
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+       bgp_md5_set (peer);
+    }
+  
+  bgp_timer_set (peer); /* stops all timers for Deleted */
+  
+  /* Delete from all peer list. */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+      && (pn = listnode_lookup (bgp->peer, peer)))
+    {
+      peer_unlock (peer); /* bgp peer list reference */
+      list_delete_node (bgp->peer, pn);
+    }
+      
+  if (peer_rsclient_active (peer)
+      && (pn = listnode_lookup (bgp->rsclient, peer)))
+    {
+      peer_unlock (peer); /* rsclient list reference */
+      list_delete_node (bgp->rsclient, pn);
+
+      /* Clear our own rsclient ribs. */
+      for (afi = AFI_IP; afi < AFI_MAX; afi++)
+        for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+          if (CHECK_FLAG(peer->af_flags[afi][safi],
+                         PEER_FLAG_RSERVER_CLIENT))
+            bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
+    }
+
+  /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not
+      member of a peer_group. */
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      if (peer->rib[afi][safi] && ! peer->af_group[afi][safi])
+        bgp_table_finish (&peer->rib[afi][safi]);
+
+  /* Buffers.  */
+  if (peer->ibuf)
+    {
+      stream_free (peer->ibuf);
+      peer->ibuf = NULL;
+    }
+
+  if (peer->obuf)
+    {
+      stream_fifo_free (peer->obuf);
+      peer->obuf = NULL;
+    }
+
+  if (peer->work)
+    {
+      stream_free (peer->work);
+      peer->work = NULL;
+    }
+
+  if (peer->scratch)
+    {
+      stream_free(peer->scratch);
+      peer->scratch = NULL;
+    }
+
+  /* Local and remote addresses. */
+  if (peer->su_local)
+    {
+      sockunion_free (peer->su_local);
+      peer->su_local = NULL;
+    }
+
+  if (peer->su_remote)
+    {
+      sockunion_free (peer->su_remote);
+      peer->su_remote = NULL;
+    }
+  
+  /* Free filter related memory.  */
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       filter = &peer->filter[afi][safi];
+
+       for (i = FILTER_IN; i < FILTER_MAX; i++)
+         {
+           if (filter->dlist[i].name)
+              {
+                free(filter->dlist[i].name);
+                filter->dlist[i].name = NULL;
+              }
+
+           if (filter->plist[i].name)
+              {
+                free(filter->plist[i].name);
+                filter->plist[i].name = NULL;
+              }
+
+           if (filter->aslist[i].name)
+              {
+                free(filter->aslist[i].name);
+                filter->aslist[i].name = NULL;
+              }
+          }
+
+        for (i = RMAP_IN; i < RMAP_MAX; i++)
+          {
+           if (filter->map[i].name)
+              {
+               free (filter->map[i].name);
+                filter->map[i].name = NULL;
+              }
+         }
+
+       if (filter->usmap.name)
+          {
+           free (filter->usmap.name);
+            filter->usmap.name = NULL;
+          }
+
+       if (peer->default_rmap[afi][safi].name)
+          {
+           free (peer->default_rmap[afi][safi].name);
+            peer->default_rmap[afi][safi].name = NULL;
+          }
+      }
+  
+  if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETING))
+    bgp_peer_clear_node_queue_drain_immediate(peer);
+
+  peer_unlock (peer); /* initial reference */
+
+  return 0;
+}
+
+static int
+peer_group_cmp (struct peer_group *g1, struct peer_group *g2)
+{
+  return strcmp (g1->name, g2->name);
+}
+
+/* If peer is configured at least one address family return 1. */
+static int
+peer_group_active (struct peer *peer)
+{
+  if (peer->af_group[AFI_IP][SAFI_UNICAST]
+      || peer->af_group[AFI_IP][SAFI_MULTICAST]
+      || peer->af_group[AFI_IP][SAFI_MPLS_VPN]
+      || peer->af_group[AFI_IP][SAFI_ENCAP]
+      || peer->af_group[AFI_IP6][SAFI_UNICAST]
+      || peer->af_group[AFI_IP6][SAFI_MULTICAST]
+      || peer->af_group[AFI_IP6][SAFI_MPLS_VPN]
+      || peer->af_group[AFI_IP6][SAFI_ENCAP])
+    return 1;
+  return 0;
+}
+
+/* Peer group cofiguration. */
+static struct peer_group *
+peer_group_new (void)
+{
+  return (struct peer_group *) XCALLOC (MTYPE_PEER_GROUP,
+                                       sizeof (struct peer_group));
+}
+
+static void
+peer_group_free (struct peer_group *group)
+{
+  XFREE (MTYPE_PEER_GROUP, group);
+}
+
+struct peer_group *
+peer_group_lookup (struct bgp *bgp, const char *name)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+    {
+      if (strcmp (group->name, name) == 0)
+       return group;
+    }
+  return NULL;
+}
+
+struct peer_group *
+peer_group_get (struct bgp *bgp, const char *name)
+{
+  struct peer_group *group;
+
+  group = peer_group_lookup (bgp, name);
+  if (group)
+    return group;
+
+  group = peer_group_new ();
+  group->bgp = bgp;
+  group->name = strdup (name);
+  group->peer = list_new ();
+  group->conf = peer_new (bgp);
+  if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+    group->conf->afc[AFI_IP][SAFI_UNICAST] = 1;
+  group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name);
+  group->conf->group = group;
+  group->conf->as = 0; 
+  group->conf->ttl = 0;
+  group->conf->gtsm_hops = 0;
+  group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+  UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
+  UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT);
+  group->conf->keepalive = 0;
+  group->conf->holdtime = 0;
+  group->conf->connect = 0;
+  SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
+  listnode_add_sort (bgp->group, group);
+
+  return 0;
+}
+
+static void 
+peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
+                            afi_t afi, safi_t safi)
+{
+  int in = FILTER_IN;
+  int out = FILTER_OUT;
+  struct peer *conf;
+  struct bgp_filter *pfilter;
+  struct bgp_filter *gfilter;
+
+  conf = group->conf;
+  pfilter = &peer->filter[afi][safi];
+  gfilter = &conf->filter[afi][safi];
+
+  /* remote-as */
+  if (conf->as)
+    peer->as = conf->as;
+
+  /* remote-as */
+  if (conf->change_local_as)
+    peer->change_local_as = conf->change_local_as;
+
+  /* TTL */
+  peer->ttl = conf->ttl;
+
+  /* GTSM hops */
+  peer->gtsm_hops = conf->gtsm_hops;
+
+  /* Weight */
+  peer->weight = conf->weight;
+
+  /* peer flags apply */
+  peer->flags = conf->flags;
+  /* peer af_flags apply */
+  peer->af_flags[afi][safi] = conf->af_flags[afi][safi];
+  /* peer config apply */
+  peer->config = conf->config;
+
+  /* peer timers apply */
+  peer->holdtime = conf->holdtime;
+  peer->keepalive = conf->keepalive;
+  peer->connect = conf->connect;
+  if (CHECK_FLAG (conf->config, PEER_CONFIG_CONNECT))
+    peer->v_connect = conf->connect;
+  else
+    peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+
+  /* advertisement-interval reset */
+  if (CHECK_FLAG (conf->config, PEER_CONFIG_ROUTEADV))
+      peer->v_routeadv = conf->routeadv;
+  else
+      if (peer_sort (peer) == BGP_PEER_IBGP)
+        peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+      else
+        peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+
+  /* password apply */
+  if (conf->password && !peer->password)
+    peer->password =  XSTRDUP (MTYPE_PEER_PASSWORD, conf->password);
+
+  bgp_md5_set (peer);
+
+  /* maximum-prefix */
+  peer->pmax[afi][safi] = conf->pmax[afi][safi];
+  peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi];
+  peer->pmax_restart[afi][safi] = conf->pmax_restart[afi][safi];
+
+  /* allowas-in */
+  peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi];
+
+  /* route-server-client */
+  if (CHECK_FLAG(conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    {
+      /* Make peer's RIB point to group's RIB. */
+      peer->rib[afi][safi] = group->conf->rib[afi][safi];
+
+      /* Import policy. */
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      if (gfilter->map[RMAP_IMPORT].name)
+        {
+          pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
+          pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
+        }
+      else
+        {
+          pfilter->map[RMAP_IMPORT].name = NULL;
+          pfilter->map[RMAP_IMPORT].map = NULL;
+        }
+
+      /* Export policy. */
+      if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
+        {
+          pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
+          pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
+        }
+    }
+
+  /* default-originate route-map */
+  if (conf->default_rmap[afi][safi].name)
+    {
+      if (peer->default_rmap[afi][safi].name)
+       free (peer->default_rmap[afi][safi].name);
+      peer->default_rmap[afi][safi].name = strdup (conf->default_rmap[afi][safi].name);
+      peer->default_rmap[afi][safi].map = conf->default_rmap[afi][safi].map;
+    }
+
+  /* update-source apply */
+  if (conf->update_source)
+    {
+      if (peer->update_source)
+       sockunion_free (peer->update_source);
+      if (peer->update_if)
+       {
+         XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+         peer->update_if = NULL;
+       }
+      peer->update_source = sockunion_dup (conf->update_source);
+    }
+  else if (conf->update_if)
+    {
+      if (peer->update_if)
+       XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      if (peer->update_source)
+       {
+         sockunion_free (peer->update_source);
+         peer->update_source = NULL;
+       }
+      peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, conf->update_if);
+    }
+
+  /* inbound filter apply */
+  if (gfilter->dlist[in].name && ! pfilter->dlist[in].name)
+    {
+      if (pfilter->dlist[in].name)
+       free (pfilter->dlist[in].name);
+      pfilter->dlist[in].name = strdup (gfilter->dlist[in].name);
+      pfilter->dlist[in].alist = gfilter->dlist[in].alist;
+    }
+  if (gfilter->plist[in].name && ! pfilter->plist[in].name)
+    {
+      if (pfilter->plist[in].name)
+       free (pfilter->plist[in].name);
+      pfilter->plist[in].name = strdup (gfilter->plist[in].name);
+      pfilter->plist[in].plist = gfilter->plist[in].plist;
+    }
+  if (gfilter->aslist[in].name && ! pfilter->aslist[in].name)
+    {
+      if (pfilter->aslist[in].name)
+       free (pfilter->aslist[in].name);
+      pfilter->aslist[in].name = strdup (gfilter->aslist[in].name);
+      pfilter->aslist[in].aslist = gfilter->aslist[in].aslist;
+    }
+  if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name)
+    {
+      if (pfilter->map[RMAP_IN].name)
+        free (pfilter->map[RMAP_IN].name);
+      pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name);
+      pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map;
+    }
+
+  /* outbound filter apply */
+  if (gfilter->dlist[out].name)
+    {
+      if (pfilter->dlist[out].name)
+       free (pfilter->dlist[out].name);
+      pfilter->dlist[out].name = strdup (gfilter->dlist[out].name);
+      pfilter->dlist[out].alist = gfilter->dlist[out].alist;
+    }
+  else
+    {
+      if (pfilter->dlist[out].name)
+       free (pfilter->dlist[out].name);
+      pfilter->dlist[out].name = NULL;
+      pfilter->dlist[out].alist = NULL;
+    }
+  if (gfilter->plist[out].name)
+    {
+      if (pfilter->plist[out].name)
+       free (pfilter->plist[out].name);
+      pfilter->plist[out].name = strdup (gfilter->plist[out].name);
+      pfilter->plist[out].plist = gfilter->plist[out].plist;
+    }
+  else
+    {
+      if (pfilter->plist[out].name)
+       free (pfilter->plist[out].name);
+      pfilter->plist[out].name = NULL;
+      pfilter->plist[out].plist = NULL;
+    }
+  if (gfilter->aslist[out].name)
+    {
+      if (pfilter->aslist[out].name)
+       free (pfilter->aslist[out].name);
+      pfilter->aslist[out].name = strdup (gfilter->aslist[out].name);
+      pfilter->aslist[out].aslist = gfilter->aslist[out].aslist;
+    }
+  else
+    {
+      if (pfilter->aslist[out].name)
+       free (pfilter->aslist[out].name);
+      pfilter->aslist[out].name = NULL;
+      pfilter->aslist[out].aslist = NULL;
+    }
+  if (gfilter->map[RMAP_OUT].name)
+    {
+      if (pfilter->map[RMAP_OUT].name)
+        free (pfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].name = strdup (gfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].map = gfilter->map[RMAP_OUT].map;
+    }
+  else
+    {
+      if (pfilter->map[RMAP_OUT].name)
+        free (pfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].name = NULL;
+      pfilter->map[RMAP_OUT].map = NULL;
+    }
+
+ /* RS-client's import/export route-maps. */
+  if (gfilter->map[RMAP_IMPORT].name)
+    {
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
+    }
+  else
+    {
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].name = NULL;
+      pfilter->map[RMAP_IMPORT].map = NULL;
+    }
+  if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
+    {
+      if (pfilter->map[RMAP_EXPORT].name)
+        free (pfilter->map[RMAP_EXPORT].name);
+      pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
+      pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
+    }
+
+  if (gfilter->usmap.name)
+    {
+      if (pfilter->usmap.name)
+       free (pfilter->usmap.name);
+      pfilter->usmap.name = strdup (gfilter->usmap.name);
+      pfilter->usmap.map = gfilter->usmap.map;
+    }
+  else
+    {
+      if (pfilter->usmap.name)
+       free (pfilter->usmap.name);
+      pfilter->usmap.name = NULL;
+      pfilter->usmap.map = NULL;
+    }
+} 
+
+/* Peer group's remote AS configuration.  */
+int
+peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as)
+{
+  struct peer_group *group;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  group = peer_group_lookup (bgp, group_name);
+  if (! group)
+    return -1;
+
+  if (group->conf->as == *as)
+    return 0;
+
+  /* When we setup peer-group AS number all peer group member's AS
+     number must be updated to same number.  */
+  peer_as_change (group->conf, *as);
+
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (peer->as != *as)
+       peer_as_change (peer, *as);
+    }
+
+  return 0;
+}
+
+int
+peer_ttl (struct peer *peer)
+{
+  if (peer->ttl)
+    return peer->ttl;
+  if (peer->gtsm_hops || peer->sort == BGP_PEER_IBGP)
+    return 255;
+  return 1;
+}
+
+int
+peer_group_delete (struct peer_group *group)
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  bgp = group->bgp;
+
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer_delete (peer);
+    }
+  list_delete (group->peer);
+
+  free (group->name);
+  group->name = NULL;
+
+  group->conf->group = NULL;
+  peer_delete (group->conf);
+
+  /* Delete from all peer_group list. */
+  listnode_delete (bgp->group, group);
+
+  peer_group_free (group);
+
+  return 0;
+}
+
+int
+peer_group_remote_as_delete (struct peer_group *group)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (! group->conf->as)
+    return 0;
+
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer_delete (peer);
+    }
+  list_delete_all_node (group->peer);
+
+  group->conf->as = 0;
+
+  return 0;
+}
+
+/* Bind specified peer to peer group.  */
+int
+peer_group_bind (struct bgp *bgp, union sockunion *su,
+                struct peer_group *group, afi_t afi, safi_t safi, as_t *as)
+{
+  struct peer *peer;
+  int first_member = 0;
+
+  /* Check peer group's address family.  */
+  if (! group->conf->afc[afi][safi])
+    return BGP_ERR_PEER_GROUP_AF_UNCONFIGURED;
+
+  /* Lookup the peer.  */
+  peer = peer_lookup (bgp, su);
+
+  /* Create a new peer. */
+  if (! peer)
+    {
+      if (! group->conf->as)
+       return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;
+
+      peer = peer_create (su, bgp, bgp->as, group->conf->as, afi, safi);
+      peer->group = group;
+      peer->af_group[afi][safi] = 1;
+
+      peer = peer_lock (peer); /* group->peer list reference */
+      listnode_add (group->peer, peer);
+      peer_group2peer_config_copy (group, peer, afi, safi);
+
+      return 0;
+    }
+
+  /* When the peer already belongs to peer group, check the consistency.  */
+  if (peer->af_group[afi][safi])
+    {
+      if (strcmp (peer->group->name, group->name) != 0)
+       return BGP_ERR_PEER_GROUP_CANT_CHANGE;
+
+      return 0;
+    }
+
+  /* Check current peer group configuration.  */
+  if (peer_group_active (peer)
+      && strcmp (peer->group->name, group->name) != 0)
+    return BGP_ERR_PEER_GROUP_MISMATCH;
+
+  if (! group->conf->as)
+    {
+      if (peer_sort (group->conf) != BGP_PEER_INTERNAL
+         && peer_sort (group->conf) != peer_sort (peer))
+       {
+         if (as)
+           *as = peer->as;
+         return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+       }
+
+      if (peer_sort (group->conf) == BGP_PEER_INTERNAL)
+       first_member = 1;
+    }
+
+  peer->af_group[afi][safi] = 1;
+  peer->afc[afi][safi] = 1;
+  if (! peer->group)
+    {
+      peer->group = group;
+      
+      peer = peer_lock (peer); /* group->peer list reference */
+      listnode_add (group->peer, peer);
+    }
+  else
+    assert (group && peer->group == group);
+
+  if (first_member)
+    {
+      /* Advertisement-interval reset */
+      if (! CHECK_FLAG (group->conf->config, PEER_CONFIG_ROUTEADV))
+       {
+         if (peer_sort (group->conf) == BGP_PEER_IBGP)
+           group->conf->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+         else
+           group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+       }
+
+      /* local-as reset */
+      if (peer_sort (group->conf) != BGP_PEER_EBGP)
+       {
+         group->conf->change_local_as = 0;
+         UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+         UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+       }
+    }
+
+  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    {
+      struct listnode *pn;
+      
+      /* If it's not configured as RSERVER_CLIENT in any other address
+          family, without being member of a peer_group, remove it from
+          list bgp->rsclient.*/
+      if (! peer_rsclient_active (peer)
+          && (pn = listnode_lookup (bgp->rsclient, peer)))
+        {
+          peer_unlock (peer); /* peer rsclient reference */
+          list_delete_node (bgp->rsclient, pn);
+
+          /* Clear our own rsclient rib for this afi/safi. */
+          bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
+        }
+
+      bgp_table_finish (&peer->rib[afi][safi]);
+
+      /* Import policy. */
+      if (peer->filter[afi][safi].map[RMAP_IMPORT].name)
+        {
+          free (peer->filter[afi][safi].map[RMAP_IMPORT].name);
+          peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL;
+          peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL;
+        }
+
+      /* Export policy. */
+      if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+              && peer->filter[afi][safi].map[RMAP_EXPORT].name)
+        {
+          free (peer->filter[afi][safi].map[RMAP_EXPORT].name);
+          peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL;
+          peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL;
+        }
+    }
+
+  peer_group2peer_config_copy (group, peer, afi, safi);
+
+  if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+    {
+      peer->last_reset = PEER_DOWN_RMAP_BIND;
+      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                      BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+    }
+  else
+    BGP_EVENT_ADD (peer, BGP_Stop);
+
+  return 0;
+}
+
+int
+peer_group_unbind (struct bgp *bgp, struct peer *peer,
+                  struct peer_group *group, afi_t afi, safi_t safi)
+{
+  if (! peer->af_group[afi][safi])
+      return 0;
+
+  if (group != peer->group)
+    return BGP_ERR_PEER_GROUP_MISMATCH;
+
+  peer->af_group[afi][safi] = 0;
+  peer->afc[afi][safi] = 0;
+  peer_af_flag_reset (peer, afi, safi);
+
+  if (peer->rib[afi][safi])
+    peer->rib[afi][safi] = NULL;
+
+  if (! peer_group_active (peer))
+    {
+      assert (listnode_lookup (group->peer, peer));
+      peer_unlock (peer); /* peer group list reference */
+      listnode_delete (group->peer, peer);
+      peer->group = NULL;
+      if (group->conf->as)
+       {
+         peer_delete (peer);
+         return 0;
+       }
+      peer_global_config_reset (peer);
+    }
+
+  if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+    {
+      peer->last_reset = PEER_DOWN_RMAP_UNBIND;
+      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                      BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+    }
+  else
+    BGP_EVENT_ADD (peer, BGP_Stop);
+
+  return 0;
+}
+
+
+static int
+bgp_startup_timer_expire (struct thread *thread)
+{
+  struct bgp *bgp;
+
+  bgp = THREAD_ARG (thread);
+  bgp->t_startup = NULL;
+
+  return 0;
+}
+
+/* BGP instance creation by `router bgp' commands. */
+static struct bgp *
+bgp_create (as_t *as, const char *name)
+{
+  struct bgp *bgp;
+  afi_t afi;
+  safi_t safi;
+
+  if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL)
+    return NULL;
+  
+  bgp_lock (bgp);
+  bgp->peer_self = peer_new (bgp);
+  bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement");
+
+  bgp->peer = list_new ();
+  bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp;
+
+  bgp->group = list_new ();
+  bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;
+
+  bgp->rsclient = list_new ();
+  bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       bgp->route[afi][safi] = bgp_table_init (afi, safi);
+       bgp->aggregate[afi][safi] = bgp_table_init (afi, safi);
+       bgp->rib[afi][safi] = bgp_table_init (afi, safi);
+       bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
+       bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
+      }
+
+  bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+  bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+  bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+  bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+  bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+  bgp_flag_set (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+
+  bgp->as = *as;
+
+  if (name)
+    bgp->name = strdup (name);
+
+  THREAD_TIMER_ON (bm->master, bgp->t_startup, bgp_startup_timer_expire,
+                   bgp, bgp->restart_time);
+
+  return bgp;
+}
+
+/* Return first entry of BGP. */
+struct bgp *
+bgp_get_default (void)
+{
+  if (bm && bm->bgp && bm->bgp->head)
+    return (listgetdata (listhead (bm->bgp)));
+  return NULL;
+}
+
+/* Lookup BGP entry. */
+struct bgp *
+bgp_lookup (as_t as, const char *name)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    if (bgp->as == as
+       && ((bgp->name == NULL && name == NULL) 
+           || (bgp->name && name && strcmp (bgp->name, name) == 0)))
+      return bgp;
+  return NULL;
+}
+
+/* Lookup BGP structure by view name. */
+struct bgp *
+bgp_lookup_by_name (const char *name)
+{
+  struct bgp *bgp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+    if ((bgp->name == NULL && name == NULL)
+       || (bgp->name && name && strcmp (bgp->name, name) == 0))
+      return bgp;
+  return NULL;
+}
+
+/* Called from VTY commands. */
+int
+bgp_get (struct bgp **bgp_val, as_t *as, const char *name)
+{
+  struct bgp *bgp;
+
+  /* Multiple instance check. */
+  if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
+    {
+      if (name)
+       bgp = bgp_lookup_by_name (name);
+      else
+       bgp = bgp_get_default ();
+
+      /* Already exists. */
+      if (bgp)
+       {
+          if (bgp->as != *as)
+           {
+             *as = bgp->as;
+             return BGP_ERR_INSTANCE_MISMATCH;
+           }
+         *bgp_val = bgp;
+         return 0;
+       }
+    }
+  else
+    {
+      /* BGP instance name can not be specified for single instance.  */
+      if (name)
+       return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET;
+
+      /* Get default BGP structure if exists. */
+      bgp = bgp_get_default ();
+
+      if (bgp)
+       {
+         if (bgp->as != *as)
+           {
+             *as = bgp->as;
+             return BGP_ERR_AS_MISMATCH;
+           }
+         *bgp_val = bgp;
+         return 0;
+       }
+    }
+
+  bgp = bgp_create (as, name);
+  bgp_router_id_set(bgp, &router_id_zebra);
+  *bgp_val = bgp;
+
+  /* Create BGP server socket, if first instance.  */
+  if (list_isempty(bm->bgp)
+      && !bgp_option_check (BGP_OPT_NO_LISTEN))
+    {
+      if (bgp_socket (bm->port, bm->address) < 0)
+       return BGP_ERR_INVALID_VALUE;
+    }
+
+  listnode_add (bm->bgp, bgp);
+
+  return 0;
+}
+
+/* Delete BGP instance. */
+int
+bgp_delete (struct bgp *bgp)
+{
+  struct peer *peer;
+  struct peer_group *group;
+  struct listnode *node, *pnode;
+  struct listnode *next, *pnext;
+  afi_t afi;
+  int i;
+
+  SET_FLAG(bgp->flags, BGP_FLAG_DELETING);
+
+  THREAD_OFF (bgp->t_startup);
+
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
+    {
+      if (peer->status == Established ||
+          peer->status == OpenSent ||
+          peer->status == OpenConfirm)
+        {
+            bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                             BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+        }
+    }
+
+  /* Delete static route. */
+  bgp_static_delete (bgp);
+
+  /* Unset redistribution. */
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
+      if (i != ZEBRA_ROUTE_BGP)
+       bgp_redistribute_unset (bgp, afi, i);
+
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         /* Send notify to remote peer. */
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+       }
+
+      peer_delete (peer);
+    }
+
+  for (ALL_LIST_ELEMENTS (bgp->group, node, next, group))
+    {
+      for (ALL_LIST_ELEMENTS (group->peer, pnode, pnext, peer))
+       {
+         if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+           {
+             /* Send notify to remote peer. */
+             bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+           }
+       }
+      peer_group_delete (group);
+    }
+
+  assert (listcount (bgp->rsclient) == 0);
+
+  if (bgp->peer_self) {
+    peer_delete(bgp->peer_self);
+    bgp->peer_self = NULL;
+  }
+
+  /*
+   * Free pending deleted routes. Unfortunately, it also has to process
+   * all the pending activity for other instances of struct bgp.
+   *
+   * This call was added to achieve clean memory allocation at exit,
+   * for the sake of valgrind.
+   */
+  bgp_process_queues_drain_immediate();
+
+  /* Remove visibility via the master list - there may however still be
+   * routes to be processed still referencing the struct bgp.
+   */
+  listnode_delete (bm->bgp, bgp);
+  if (list_isempty(bm->bgp))
+    bgp_close ();
+
+  bgp_unlock(bgp);  /* initial reference */
+  
+  return 0;
+}
+
+static void bgp_free (struct bgp *);
+
+void
+bgp_lock (struct bgp *bgp)
+{
+  ++bgp->lock;
+}
+
+void
+bgp_unlock(struct bgp *bgp)
+{
+  assert(bgp->lock > 0);
+  if (--bgp->lock == 0)
+    bgp_free (bgp);
+}
+
+static void
+bgp_free (struct bgp *bgp)
+{
+  afi_t afi;
+  safi_t safi;
+
+  list_delete (bgp->group);
+  list_delete (bgp->peer);
+  list_delete (bgp->rsclient);
+
+  if (bgp->name)
+    free (bgp->name);
+  
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+       if (bgp->route[afi][safi])
+          bgp_table_finish (&bgp->route[afi][safi]);
+       if (bgp->aggregate[afi][safi])
+          bgp_table_finish (&bgp->aggregate[afi][safi]) ;
+       if (bgp->rib[afi][safi])
+          bgp_table_finish (&bgp->rib[afi][safi]);
+      }
+  XFREE (MTYPE_BGP, bgp);
+}
+
+struct peer *
+peer_lookup (struct bgp *bgp, union sockunion *su)
+{
+  struct peer *peer;
+  struct listnode *node, *nnode;
+
+  if (bgp != NULL)
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+        if (sockunion_same (&peer->su, su)
+            && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+          return peer;
+    }
+  else if (bm->bgp != NULL)
+    {
+      struct listnode *bgpnode, *nbgpnode;
+  
+      for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp))
+        for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+          if (sockunion_same (&peer->su, su)
+              && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+            return peer;
+    }
+  return NULL;
+}
+
+struct peer *
+peer_lookup_with_open (union sockunion *su, as_t remote_as,
+                      struct in_addr *remote_id, int *as)
+{
+  struct peer *peer;
+  struct listnode *node;
+  struct listnode *bgpnode;
+  struct bgp *bgp;
+
+  if (! bm->bgp)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (bm->bgp, bgpnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+        {
+          if (sockunion_same (&peer->su, su)
+              && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+            {
+              if (peer->as == remote_as
+                  && peer->remote_id.s_addr == remote_id->s_addr)
+                return peer;
+              if (peer->as == remote_as)
+                *as = 1;
+            }
+        }
+
+      for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+        {
+          if (sockunion_same (&peer->su, su)
+              &&  ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+            {
+              if (peer->as == remote_as
+                  && peer->remote_id.s_addr == 0)
+                return peer;
+              if (peer->as == remote_as)
+                *as = 1;
+            }
+        }
+    }
+  return NULL;
+}
+
+/* If peer is configured at least one address family return 1. */
+int
+peer_active (struct peer *peer)
+{
+  if (peer->afc[AFI_IP][SAFI_UNICAST]
+      || peer->afc[AFI_IP][SAFI_MULTICAST]
+      || peer->afc[AFI_IP][SAFI_MPLS_VPN]
+      || peer->afc[AFI_IP][SAFI_ENCAP]
+      || peer->afc[AFI_IP6][SAFI_UNICAST]
+      || peer->afc[AFI_IP6][SAFI_MULTICAST]
+      || peer->afc[AFI_IP6][SAFI_MPLS_VPN]
+      || peer->afc[AFI_IP6][SAFI_ENCAP])
+    return 1;
+  return 0;
+}
+
+/* If peer is negotiated at least one address family return 1. */
+int
+peer_active_nego (struct peer *peer)
+{
+  if (peer->afc_nego[AFI_IP][SAFI_UNICAST]
+      || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+      || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+      || peer->afc_nego[AFI_IP][SAFI_ENCAP]
+      || peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+      || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+      || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+      || peer->afc_nego[AFI_IP6][SAFI_ENCAP])
+    return 1;
+  return 0;
+}
+
+/* peer_flag_change_type. */
+enum peer_change_type
+{
+  peer_change_none,
+  peer_change_reset,
+  peer_change_reset_in,
+  peer_change_reset_out,
+};
+
+static void
+peer_change_action (struct peer *peer, afi_t afi, safi_t safi,
+                   enum peer_change_type type)
+{
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return;
+
+  if (peer->status != Established)
+    return;
+
+  if (type == peer_change_reset)
+    bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                    BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+  else if (type == peer_change_reset_in)
+    {
+      if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
+         || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+       bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
+      else
+       bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                        BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+    }
+  else if (type == peer_change_reset_out)
+    bgp_announce_route (peer, afi, safi);
+}
+
+struct peer_flag_action
+{
+  /* Peer's flag.  */
+  u_int32_t flag;
+
+  /* This flag can be set for peer-group member.  */
+  u_char not_for_member;
+
+  /* Action when the flag is changed.  */
+  enum peer_change_type type;
+
+  /* Peer down cause */
+  u_char peer_down;
+};
+
+static const struct peer_flag_action peer_flag_action_list[] =
+  {
+    { PEER_FLAG_PASSIVE,                  0, peer_change_reset },
+    { PEER_FLAG_SHUTDOWN,                 0, peer_change_reset },
+    { PEER_FLAG_DONT_CAPABILITY,          0, peer_change_none },
+    { PEER_FLAG_OVERRIDE_CAPABILITY,      0, peer_change_none },
+    { PEER_FLAG_STRICT_CAP_MATCH,         0, peer_change_none },
+    { PEER_FLAG_DYNAMIC_CAPABILITY,       0, peer_change_reset },
+    { PEER_FLAG_DISABLE_CONNECTED_CHECK,  0, peer_change_reset },
+    { 0, 0, 0 }
+  };
+
+static const struct peer_flag_action peer_af_flag_action_list[] =
+  {
+    { PEER_FLAG_NEXTHOP_SELF,             1, peer_change_reset_out },
+    { PEER_FLAG_SEND_COMMUNITY,           1, peer_change_reset_out },
+    { PEER_FLAG_SEND_EXT_COMMUNITY,       1, peer_change_reset_out },
+    { PEER_FLAG_SEND_LARGE_COMMUNITY,     1, peer_change_reset_out },
+    { PEER_FLAG_SOFT_RECONFIG,            0, peer_change_reset_in },
+    { PEER_FLAG_REFLECTOR_CLIENT,         1, peer_change_reset },
+    { PEER_FLAG_RSERVER_CLIENT,           1, peer_change_reset },
+    { PEER_FLAG_AS_PATH_UNCHANGED,        1, peer_change_reset_out },
+    { PEER_FLAG_NEXTHOP_UNCHANGED,        1, peer_change_reset_out },
+    { PEER_FLAG_MED_UNCHANGED,            1, peer_change_reset_out },
+    { PEER_FLAG_REMOVE_PRIVATE_AS,        1, peer_change_reset_out },
+    { PEER_FLAG_ALLOWAS_IN,               0, peer_change_reset_in },
+    { PEER_FLAG_ORF_PREFIX_SM,            1, peer_change_reset },
+    { PEER_FLAG_ORF_PREFIX_RM,            1, peer_change_reset },
+    { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED,  0, peer_change_reset_out },
+    { PEER_FLAG_NEXTHOP_SELF_ALL,         1, peer_change_reset_out },
+    { 0, 0, 0 }
+  };
+
+/* Proper action set. */
+static int
+peer_flag_action_set (const struct peer_flag_action *action_list, int size,
+                     struct peer_flag_action *action, u_int32_t flag)
+{
+  int i;
+  int found = 0;
+  int reset_in = 0;
+  int reset_out = 0;
+  const struct peer_flag_action *match = NULL;
+
+  /* Check peer's frag action.  */
+  for (i = 0; i < size; i++)
+    {
+      match = &action_list[i];
+
+      if (match->flag == 0)
+       break;
+
+      if (match->flag & flag)
+       {
+         found = 1;
+
+         if (match->type == peer_change_reset_in)
+           reset_in = 1;
+         if (match->type == peer_change_reset_out)
+           reset_out = 1;
+         if (match->type == peer_change_reset)
+           {
+             reset_in = 1;
+             reset_out = 1;
+           }
+         if (match->not_for_member)
+           action->not_for_member = 1;
+       }
+    }
+
+  /* Set peer clear type.  */
+  if (reset_in && reset_out)
+    action->type = peer_change_reset;
+  else if (reset_in)
+    action->type = peer_change_reset_in;
+  else if (reset_out)
+    action->type = peer_change_reset_out;
+  else
+    action->type = peer_change_none;
+
+  return found;
+}
+
+static void
+peer_flag_modify_action (struct peer *peer, u_int32_t flag)
+{
+  if (flag == PEER_FLAG_SHUTDOWN)
+    {
+      if (CHECK_FLAG (peer->flags, flag))
+       {
+         if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+           peer_nsf_stop (peer);
+
+         UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+         if (peer->t_pmax_restart)
+           {
+             BGP_TIMER_OFF (peer->t_pmax_restart);
+              if (BGP_DEBUG (events, EVENTS))
+               zlog_debug ("%s Maximum-prefix restart timer canceled",
+                           peer->host);
+           }
+
+      if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+       peer_nsf_stop (peer);
+
+         if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+           bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                            BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+         else
+           BGP_EVENT_ADD (peer, BGP_Stop);
+       }
+      else
+       {
+         peer->v_start = BGP_INIT_START_TIMER;
+         BGP_EVENT_ADD (peer, BGP_Stop);
+       }
+    }
+  else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+    {
+      if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
+       peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+      else if (flag == PEER_FLAG_PASSIVE)
+       peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
+      else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)
+       peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE;
+
+      bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                      BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+    }
+  else
+    BGP_EVENT_ADD (peer, BGP_Stop);
+}
+
+/* Change specified peer flag. */
+static int
+peer_flag_modify (struct peer *peer, u_int32_t flag, int set)
+{
+  int found;
+  int size;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+  struct peer_flag_action action;
+
+  memset (&action, 0, sizeof (struct peer_flag_action));
+  size = sizeof peer_flag_action_list / sizeof (struct peer_flag_action);
+
+  found = peer_flag_action_set (peer_flag_action_list, size, &action, flag);
+
+  /* No flag action is found.  */
+  if (! found)
+    return BGP_ERR_INVALID_FLAG;    
+
+  /* Not for peer-group member.  */
+  if (action.not_for_member && peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  /* When unset the peer-group member's flag we have to check
+     peer-group configuration.  */
+  if (! set && peer_group_active (peer))
+    if (CHECK_FLAG (peer->group->conf->flags, flag))
+      {
+       if (flag == PEER_FLAG_SHUTDOWN)
+         return BGP_ERR_PEER_GROUP_SHUTDOWN;
+       else
+         return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
+      }
+
+  /* Flag conflict check.  */
+  if (set
+      && CHECK_FLAG (peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH)
+      && CHECK_FLAG (peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY))
+    return BGP_ERR_PEER_FLAG_CONFLICT;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (set && CHECK_FLAG (peer->flags, flag) == flag)
+       return 0;
+      if (! set && ! CHECK_FLAG (peer->flags, flag))
+       return 0;
+    }
+
+  if (set)
+    SET_FLAG (peer->flags, flag);
+  else
+    UNSET_FLAG (peer->flags, flag);
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (action.type == peer_change_reset)
+       peer_flag_modify_action (peer, flag);
+
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (set && CHECK_FLAG (peer->flags, flag) == flag)
+       continue;
+
+      if (! set && ! CHECK_FLAG (peer->flags, flag))
+       continue;
+
+      if (set)
+       SET_FLAG (peer->flags, flag);
+      else
+       UNSET_FLAG (peer->flags, flag);
+
+      if (action.type == peer_change_reset)
+       peer_flag_modify_action (peer, flag);
+    }
+  return 0;
+}
+
+int
+peer_flag_set (struct peer *peer, u_int32_t flag)
+{
+  return peer_flag_modify (peer, flag, 1);
+}
+
+int
+peer_flag_unset (struct peer *peer, u_int32_t flag)
+{
+  return peer_flag_modify (peer, flag, 0);
+}
+
+static int
+peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi)
+{
+  if (peer->af_group[afi][safi])
+    return 1;
+  return 0;
+}
+
+static int
+peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag,
+                    int set)
+{
+  int found;
+  int size;
+  struct listnode *node, *nnode;
+  struct peer_group *group;
+  struct peer_flag_action action;
+
+  memset (&action, 0, sizeof (struct peer_flag_action));
+  size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action);
+  
+  found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag);
+  
+  /* No flag action is found.  */
+  if (! found)
+    return BGP_ERR_INVALID_FLAG;    
+
+  /* Adress family must be activated.  */
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  /* Not for peer-group member.  */
+  if (action.not_for_member && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+ /* Spcecial check for reflector client.  */
+  if (flag & PEER_FLAG_REFLECTOR_CLIENT
+      && peer_sort (peer) != BGP_PEER_IBGP)
+    return BGP_ERR_NOT_INTERNAL_PEER;
+
+  /* Spcecial check for remove-private-AS.  */
+  if (flag & PEER_FLAG_REMOVE_PRIVATE_AS
+      && peer_sort (peer) == BGP_PEER_IBGP)
+    return BGP_ERR_REMOVE_PRIVATE_AS;
+
+  /* When unset the peer-group member's flag we have to check
+     peer-group configuration.  */
+  if (! set && peer->af_group[afi][safi])
+    if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], flag))
+      return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
+
+  /* When current flag configuration is same as requested one.  */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
+       return 0;
+      if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
+       return 0;
+    }
+
+  if (set)
+    SET_FLAG (peer->af_flags[afi][safi], flag);
+  else
+    UNSET_FLAG (peer->af_flags[afi][safi], flag);
+
+  /* Execute action when peer is established.  */
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+      && peer->status == Established)
+    {
+      if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
+       bgp_clear_adj_in (peer, afi, safi);
+      else
+       {
+         if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+           peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
+         else if (flag == PEER_FLAG_RSERVER_CLIENT)
+           peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
+         else if (flag == PEER_FLAG_ORF_PREFIX_SM)
+           peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+         else if (flag == PEER_FLAG_ORF_PREFIX_RM)
+           peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+
+         peer_change_action (peer, afi, safi, action.type);
+       }
+
+    }
+
+  /* Peer group member updates.  */
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+      
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+       {
+         if (! peer->af_group[afi][safi])
+           continue;
+
+         if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag)
+           continue;
+
+         if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag))
+           continue;
+
+         if (set)
+           SET_FLAG (peer->af_flags[afi][safi], flag);
+         else
+           UNSET_FLAG (peer->af_flags[afi][safi], flag);
+
+         if (peer->status == Established)
+           {
+             if (! set && flag == PEER_FLAG_SOFT_RECONFIG)
+               bgp_clear_adj_in (peer, afi, safi);
+             else
+               {
+                 if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+                   peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
+                 else if (flag == PEER_FLAG_RSERVER_CLIENT)
+                   peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
+                 else if (flag == PEER_FLAG_ORF_PREFIX_SM)
+                   peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+                 else if (flag == PEER_FLAG_ORF_PREFIX_RM)
+                   peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+
+                 peer_change_action (peer, afi, safi, action.type);
+               }
+           }
+       }
+    }
+  return 0;
+}
+
+int
+peer_af_flag_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
+{
+  return peer_af_flag_modify (peer, afi, safi, flag, 1);
+}
+
+int
+peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag)
+{
+  return peer_af_flag_modify (peer, afi, safi, flag, 0);
+}
+
+/* EBGP multihop configuration. */
+int
+peer_ebgp_multihop_set (struct peer *peer, int ttl)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+  struct peer *peer1;
+
+  if (peer->sort == BGP_PEER_IBGP)
+    return BGP_ERR_NO_IBGP_WITH_TTLHACK;
+
+  if (peer->gtsm_hops != 0)
+    return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
+        {
+          if (peer1->gtsm_hops != 0)
+            return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+        }
+    }
+
+  peer->ttl = ttl;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      bgp_set_socket_ttl (peer, peer->fd);
+    }
+  else
+    {
+      group = peer->group;
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+        {
+          peer->ttl = ttl;
+          bgp_set_socket_ttl (peer, peer->fd);
+        }
+    }
+
+  return 0;
+}
+
+/* Neighbor description. */
+int
+peer_description_set (struct peer *peer, const char *desc)
+{
+  if (peer->desc)
+    XFREE (MTYPE_PEER_DESC, peer->desc);
+
+  peer->desc = XSTRDUP (MTYPE_PEER_DESC, desc);
+
+  return 0;
+}
+
+int
+peer_description_unset (struct peer *peer)
+{
+  if (peer->desc)
+    XFREE (MTYPE_PEER_DESC, peer->desc);
+
+  peer->desc = NULL;
+
+  return 0;
+}
+
+/* Neighbor update-source. */
+int
+peer_update_source_if_set (struct peer *peer, const char *ifname)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer->update_if)
+    {
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+         && strcmp (peer->update_if, ifname) == 0)
+       return 0;
+
+      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      peer->update_if = NULL;
+    }
+
+  if (peer->update_source)
+    {
+      sockunion_free (peer->update_source);
+      peer->update_source = NULL;
+    }
+
+  peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (peer->update_if)
+       {
+         if (strcmp (peer->update_if, ifname) == 0)
+           continue;
+
+         XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+         peer->update_if = NULL;
+       }
+
+      if (peer->update_source)
+       {
+         sockunion_free (peer->update_source);
+         peer->update_source = NULL;
+       }
+
+      peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  return 0;
+}
+
+int
+peer_update_source_addr_set (struct peer *peer, const union sockunion *su)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer->update_source)
+    {
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+         && sockunion_cmp (peer->update_source, su) == 0)
+       return 0;
+      sockunion_free (peer->update_source);
+      peer->update_source = NULL;
+    }
+
+  if (peer->update_if)
+    {
+      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      peer->update_if = NULL;
+    }
+
+  peer->update_source = sockunion_dup (su);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (peer->update_source)
+       {
+         if (sockunion_cmp (peer->update_source, su) == 0)
+           continue;
+         sockunion_free (peer->update_source);
+         peer->update_source = NULL;
+       }
+
+      if (peer->update_if)
+       {
+         XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+         peer->update_if = NULL;
+       }
+
+      peer->update_source = sockunion_dup (su);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  return 0;
+}
+
+int
+peer_update_source_unset (struct peer *peer)
+{
+  union sockunion *su;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+      && ! peer->update_source
+      && ! peer->update_if)
+    return 0;
+
+  if (peer->update_source)
+    {
+      sockunion_free (peer->update_source);
+      peer->update_source = NULL;
+    }
+  if (peer->update_if)
+    {
+      XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+      peer->update_if = NULL;
+    }
+
+  if (peer_group_active (peer))
+    {
+      group = peer->group;
+
+      if (group->conf->update_source)
+       {
+         su = sockunion_dup (group->conf->update_source);
+         peer->update_source = su;
+       }
+      else if (group->conf->update_if)
+       peer->update_if = 
+         XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, group->conf->update_if);
+    }
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (! peer->update_source && ! peer->update_if)
+       continue;
+
+      if (peer->update_source)
+       {
+         sockunion_free (peer->update_source);
+         peer->update_source = NULL;
+       }
+
+      if (peer->update_if)
+       {
+         XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+         peer->update_if = NULL;
+       }
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+       BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  return 0;
+}
+
+int
+peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi,
+                           const char *rmap)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  /* Adress family must be activated.  */
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  /* Default originate can't be used for peer group memeber.  */
+  if (peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)
+      || (rmap && ! peer->default_rmap[afi][safi].name)
+      || (rmap && strcmp (rmap, peer->default_rmap[afi][safi].name) != 0))
+    { 
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
+
+      if (rmap)
+       {
+         if (peer->default_rmap[afi][safi].name)
+           free (peer->default_rmap[afi][safi].name);
+         peer->default_rmap[afi][safi].name = strdup (rmap);
+         peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap);
+       }
+    }
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (peer->status == Established && peer->afc_nego[afi][safi])
+       bgp_default_originate (peer, afi, safi, 0);
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
+
+      if (rmap)
+       {
+         if (peer->default_rmap[afi][safi].name)
+           free (peer->default_rmap[afi][safi].name);
+         peer->default_rmap[afi][safi].name = strdup (rmap);
+         peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap);
+       }
+
+      if (peer->status == Established && peer->afc_nego[afi][safi])
+       bgp_default_originate (peer, afi, safi, 0);
+    }
+  return 0;
+}
+
+int
+peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  /* Adress family must be activated.  */
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  /* Default originate can't be used for peer group memeber.  */
+  if (peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
+    { 
+      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
+
+      if (peer->default_rmap[afi][safi].name)
+       free (peer->default_rmap[afi][safi].name);
+      peer->default_rmap[afi][safi].name = NULL;
+      peer->default_rmap[afi][safi].map = NULL;
+    }
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (peer->status == Established && peer->afc_nego[afi][safi])
+       bgp_default_originate (peer, afi, safi, 1);
+      return 0;
+    }
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE);
+
+      if (peer->default_rmap[afi][safi].name)
+       free (peer->default_rmap[afi][safi].name);
+      peer->default_rmap[afi][safi].name = NULL;
+      peer->default_rmap[afi][safi].map = NULL;
+
+      if (peer->status == Established && peer->afc_nego[afi][safi])
+       bgp_default_originate (peer, afi, safi, 1);
+    }
+  return 0;
+}
+
+int
+peer_port_set (struct peer *peer, u_int16_t port)
+{
+  peer->port = port;
+  return 0;
+}
+
+int
+peer_port_unset (struct peer *peer)
+{
+  peer->port = BGP_PORT_DEFAULT;
+  return 0;
+}
+
+/* neighbor weight. */
+int
+peer_weight_set (struct peer *peer, u_int16_t weight)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  SET_FLAG (peer->config, PEER_CONFIG_WEIGHT);
+  peer->weight = weight;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer->weight = group->conf->weight;
+    }
+  return 0;
+}
+
+int
+peer_weight_unset (struct peer *peer)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  /* Set default weight. */
+  if (peer_group_active (peer))
+    peer->weight = peer->group->conf->weight;
+  else
+    peer->weight = 0;
+
+  UNSET_FLAG (peer->config, PEER_CONFIG_WEIGHT);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer->weight = 0;
+    }
+  return 0;
+}
+
+int
+peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  /* Not for peer group memeber.  */
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  /* keepalive value check.  */
+  if (keepalive > 65535)
+    return BGP_ERR_INVALID_VALUE;
+
+  /* Holdtime value check.  */
+  if (holdtime > 65535)
+    return BGP_ERR_INVALID_VALUE;
+
+  /* Holdtime value must be either 0 or greater than 3.  */
+  if (holdtime < 3 && holdtime != 0)
+    return BGP_ERR_INVALID_VALUE;
+
+  /* Set value to the configuration. */
+  SET_FLAG (peer->config, PEER_CONFIG_TIMER);
+  peer->holdtime = holdtime;
+  peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      SET_FLAG (peer->config, PEER_CONFIG_TIMER);
+      peer->holdtime = group->conf->holdtime;
+      peer->keepalive = group->conf->keepalive;
+    }
+  return 0;
+}
+
+int
+peer_timers_unset (struct peer *peer)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  /* Clear configuration. */
+  UNSET_FLAG (peer->config, PEER_CONFIG_TIMER);
+  peer->keepalive = 0;
+  peer->holdtime = 0;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      UNSET_FLAG (peer->config, PEER_CONFIG_TIMER);
+      peer->holdtime = 0;
+      peer->keepalive = 0;
+    }
+
+  return 0;
+}
+
+int
+peer_timers_connect_set (struct peer *peer, u_int32_t connect)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (connect > 65535)
+    return BGP_ERR_INVALID_VALUE;
+
+  /* Set value to the configuration. */
+  SET_FLAG (peer->config, PEER_CONFIG_CONNECT);
+  peer->connect = connect;
+
+  /* Set value to timer setting. */
+  peer->v_connect = connect;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      SET_FLAG (peer->config, PEER_CONFIG_CONNECT);
+      peer->connect = connect;
+      peer->v_connect = connect;
+    }
+  return 0;
+}
+
+int
+peer_timers_connect_unset (struct peer *peer)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  /* Clear configuration. */
+  UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT);
+  peer->connect = 0;
+
+  /* Set timer setting to default value. */
+  peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT);
+      peer->connect = 0;
+      peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+    }
+   return 0;
+}
+
+int
+peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (routeadv > 600)
+    return BGP_ERR_INVALID_VALUE;
+
+  SET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
+  peer->routeadv = routeadv;
+  peer->v_routeadv = routeadv;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      SET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
+      peer->routeadv = routeadv;
+      peer->v_routeadv = routeadv;
+    }
+
+  return 0;
+}
+
+int
+peer_advertise_interval_unset (struct peer *peer)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
+  peer->routeadv = 0;
+
+  if (peer->sort == BGP_PEER_IBGP)
+    peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+  else
+    peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  /* peer-group member updates. */
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV);
+      peer->routeadv = 0;
+
+      if (peer->sort == BGP_PEER_IBGP)
+        peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+      else
+        peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+    }
+  
+  return 0;
+}
+
+/* neighbor interface */
+int
+peer_interface_set (struct peer *peer, const char *str)
+{
+  if (peer->ifname)
+    free (peer->ifname);
+  peer->ifname = strdup (str);
+
+  return 0;
+}
+
+int
+peer_interface_unset (struct peer *peer)
+{
+  if (peer->ifname)
+    free (peer->ifname);
+  peer->ifname = NULL;
+
+  return 0;
+}
+
+/* Allow-as in.  */
+int
+peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (allow_num < 1 || allow_num > 10)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (peer->allowas_in[afi][safi] != allow_num)
+    {
+      peer->allowas_in[afi][safi] = allow_num;
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+      peer_change_action (peer, afi, safi, peer_change_reset_in);
+    }
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (peer->allowas_in[afi][safi] != allow_num)
+       {
+         peer->allowas_in[afi][safi] = allow_num;
+         SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+         peer_change_action (peer, afi, safi, peer_change_reset_in);
+       }
+         
+    }
+  return 0;
+}
+
+int
+peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
+    {
+      peer->allowas_in[afi][safi] = 0;
+      peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+    }
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
+       {
+         peer->allowas_in[afi][safi] = 0;
+         peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+       }
+    }
+  return 0;
+}
+
+int
+peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as)
+{
+  struct bgp *bgp = peer->bgp;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_sort (peer) != BGP_PEER_EBGP
+      && peer_sort (peer) != BGP_PEER_INTERNAL)
+    return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP;
+
+  if (bgp->as == as)
+    return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (peer->as == as)
+    return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS;
+
+  if (peer->change_local_as == as &&
+      ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend)
+       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)) &&
+      ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && replace_as)
+       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && ! replace_as)))
+    return 0;
+
+  peer->change_local_as = as;
+  if (no_prepend)
+    SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+  else
+    UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+
+  if (replace_as)
+    SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+  else
+    UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+
+      return 0;
+    }
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer->change_local_as = as;
+      if (no_prepend)
+       SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+      else
+       UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+
+      if (replace_as)
+        SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+      else
+        UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+
+  return 0;
+}
+
+int
+peer_local_as_unset (struct peer *peer)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (peer_group_active (peer))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  if (! peer->change_local_as)
+    return 0;
+
+  peer->change_local_as = 0;
+  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+
+      return 0;
+    }
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      peer->change_local_as = 0;
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       {
+         peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+         bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                          BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+       }
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  return 0;
+}
+
+/* Set password for authenticating with the peer. */
+int
+peer_password_set (struct peer *peer, const char *password)
+{
+  struct listnode *nn, *nnode;
+  int len = password ? strlen(password) : 0;
+  int ret = BGP_SUCCESS;
+
+  if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN))
+    return BGP_ERR_INVALID_VALUE;
+
+  if (peer->password && strcmp (peer->password, password) == 0
+      && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  if (peer->password)
+    XFREE (MTYPE_PEER_PASSWORD, peer->password);
+  
+  peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+        
+      return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
+    }
+
+  for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
+    {
+      if (peer->password && strcmp (peer->password, password) == 0)
+       continue;
+      
+      if (peer->password)
+        XFREE (MTYPE_PEER_PASSWORD, peer->password);
+      
+      peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+      
+      if (bgp_md5_set (peer) < 0)
+        ret = BGP_ERR_TCPSIG_FAILED;
+    }
+
+  return ret;
+}
+
+int
+peer_password_unset (struct peer *peer)
+{
+  struct listnode *nn, *nnode;
+
+  if (!peer->password
+      && !CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  if (!CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      if (peer_group_active (peer)
+         && peer->group->conf->password
+         && strcmp (peer->group->conf->password, peer->password) == 0)
+       return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+
+      if (peer->password)
+        XFREE (MTYPE_PEER_PASSWORD, peer->password);
+      
+      peer->password = NULL;
+      
+      bgp_md5_set (peer);
+
+      return 0;
+    }
+
+  XFREE (MTYPE_PEER_PASSWORD, peer->password);
+  peer->password = NULL;
+
+  for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
+    {
+      if (!peer->password)
+       continue;
+
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+        bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+      
+      XFREE (MTYPE_PEER_PASSWORD, peer->password);
+      peer->password = NULL;
+
+      bgp_md5_set (peer);
+    }
+
+  return 0;
+}
+
+/* Set distribute list to the peer. */
+int
+peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
+                    const char *name)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  if (filter->plist[direct].name)
+    return BGP_ERR_PEER_FILTER_CONFLICT;
+
+  if (filter->dlist[direct].name)
+    free (filter->dlist[direct].name);
+  filter->dlist[direct].name = strdup (name);
+  filter->dlist[direct].alist = access_list_lookup (afi, name);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->dlist[direct].name)
+       free (filter->dlist[direct].name);
+      filter->dlist[direct].name = strdup (name);
+      filter->dlist[direct].alist = access_list_lookup (afi, name);
+    }
+
+  return 0;
+}
+
+int
+peer_distribute_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+  struct bgp_filter *filter;
+  struct bgp_filter *gfilter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  /* apply peer-group filter */
+  if (peer->af_group[afi][safi])
+    {
+      gfilter = &peer->group->conf->filter[afi][safi];
+
+      if (gfilter->dlist[direct].name)
+       {
+         if (filter->dlist[direct].name)
+           free (filter->dlist[direct].name);
+         filter->dlist[direct].name = strdup (gfilter->dlist[direct].name);
+         filter->dlist[direct].alist = gfilter->dlist[direct].alist;
+         return 0;
+       }
+    }
+
+  if (filter->dlist[direct].name)
+    free (filter->dlist[direct].name);
+  filter->dlist[direct].name = NULL;
+  filter->dlist[direct].alist = NULL;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+        continue;
+
+      if (filter->dlist[direct].name)
+        free (filter->dlist[direct].name);
+      filter->dlist[direct].name = NULL;
+      filter->dlist[direct].alist = NULL;
+    }
+
+  return 0;
+}
+
+/* Update distribute list. */
+static void
+peer_distribute_update (struct access_list *access)
+{
+  afi_t afi;
+  safi_t safi;
+  int direct;
+  struct listnode *mnode, *mnnode;
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct bgp_filter *filter;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &peer->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->dlist[direct].name)
+                     filter->dlist[direct].alist = 
+                       access_list_lookup (afi, filter->dlist[direct].name);
+                   else
+                     filter->dlist[direct].alist = NULL;
+                 }
+             }
+       }
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &group->conf->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->dlist[direct].name)
+                     filter->dlist[direct].alist = 
+                       access_list_lookup (afi, filter->dlist[direct].name);
+                   else
+                     filter->dlist[direct].alist = NULL;
+                 }
+             }
+       }
+    }
+}
+
+/* Set prefix list to the peer. */
+int
+peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
+                     const char *name)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  if (filter->dlist[direct].name)
+    return BGP_ERR_PEER_FILTER_CONFLICT;
+
+  if (filter->plist[direct].name)
+    free (filter->plist[direct].name);
+  filter->plist[direct].name = strdup (name);
+  filter->plist[direct].plist = prefix_list_lookup (afi, name);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->plist[direct].name)
+       free (filter->plist[direct].name);
+      filter->plist[direct].name = strdup (name);
+      filter->plist[direct].plist = prefix_list_lookup (afi, name);
+    }
+  return 0;
+}
+
+int
+peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+  struct bgp_filter *filter;
+  struct bgp_filter *gfilter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  /* apply peer-group filter */
+  if (peer->af_group[afi][safi])
+    {
+      gfilter = &peer->group->conf->filter[afi][safi];
+
+      if (gfilter->plist[direct].name)
+       {
+         if (filter->plist[direct].name)
+           free (filter->plist[direct].name);
+         filter->plist[direct].name = strdup (gfilter->plist[direct].name);
+         filter->plist[direct].plist = gfilter->plist[direct].plist;
+         return 0;
+       }
+    }
+
+  if (filter->plist[direct].name)
+    free (filter->plist[direct].name);
+  filter->plist[direct].name = NULL;
+  filter->plist[direct].plist = NULL;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->plist[direct].name)
+       free (filter->plist[direct].name);
+      filter->plist[direct].name = NULL;
+      filter->plist[direct].plist = NULL;
+    }
+
+  return 0;
+}
+
+/* Update prefix-list list. */
+static void
+peer_prefix_list_update (struct prefix_list *plist)
+{
+  struct listnode *mnode, *mnnode;
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct bgp_filter *filter;
+  afi_t afi;
+  safi_t safi;
+  int direct;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &peer->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->plist[direct].name)
+                     filter->plist[direct].plist = 
+                       prefix_list_lookup (afi, filter->plist[direct].name);
+                   else
+                     filter->plist[direct].plist = NULL;
+                 }
+             }
+       }
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &group->conf->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->plist[direct].name)
+                     filter->plist[direct].plist = 
+                       prefix_list_lookup (afi, filter->plist[direct].name);
+                   else
+                     filter->plist[direct].plist = NULL;
+                 }
+             }
+       }
+    }
+}
+
+int
+peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
+                const char *name)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  if (filter->aslist[direct].name)
+    free (filter->aslist[direct].name);
+  filter->aslist[direct].name = strdup (name);
+  filter->aslist[direct].aslist = as_list_lookup (name);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->aslist[direct].name)
+       free (filter->aslist[direct].name);
+      filter->aslist[direct].name = strdup (name);
+      filter->aslist[direct].aslist = as_list_lookup (name);
+    }
+  return 0;
+}
+
+int
+peer_aslist_unset (struct peer *peer,afi_t afi, safi_t safi, int direct)
+{
+  struct bgp_filter *filter;
+  struct bgp_filter *gfilter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != FILTER_IN && direct != FILTER_OUT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  /* apply peer-group filter */
+  if (peer->af_group[afi][safi])
+    {
+      gfilter = &peer->group->conf->filter[afi][safi];
+
+      if (gfilter->aslist[direct].name)
+       {
+         if (filter->aslist[direct].name)
+           free (filter->aslist[direct].name);
+         filter->aslist[direct].name = strdup (gfilter->aslist[direct].name);
+         filter->aslist[direct].aslist = gfilter->aslist[direct].aslist;
+         return 0;
+       }
+    }
+
+  if (filter->aslist[direct].name)
+    free (filter->aslist[direct].name);
+  filter->aslist[direct].name = NULL;
+  filter->aslist[direct].aslist = NULL;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->aslist[direct].name)
+       free (filter->aslist[direct].name);
+      filter->aslist[direct].name = NULL;
+      filter->aslist[direct].aslist = NULL;
+    }
+
+  return 0;
+}
+
+static void
+peer_aslist_update (void)
+{
+  afi_t afi;
+  safi_t safi;
+  int direct;
+  struct listnode *mnode, *mnnode;
+  struct listnode *node, *nnode;
+  struct bgp *bgp;
+  struct peer *peer;
+  struct peer_group *group;
+  struct bgp_filter *filter;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &peer->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->aslist[direct].name)
+                     filter->aslist[direct].aslist = 
+                       as_list_lookup (filter->aslist[direct].name);
+                   else
+                     filter->aslist[direct].aslist = NULL;
+                 }
+             }
+       }
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+       {
+         for (afi = AFI_IP; afi < AFI_MAX; afi++)
+           for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+             {
+               filter = &group->conf->filter[afi][safi];
+
+               for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+                 {
+                   if (filter->aslist[direct].name)
+                     filter->aslist[direct].aslist = 
+                       as_list_lookup (filter->aslist[direct].name);
+                   else
+                     filter->aslist[direct].aslist = NULL;
+                 }
+             }
+       }
+    }
+}
+
+/* Set route-map to the peer. */
+int
+peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, 
+                   const char *name)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != RMAP_IN && direct != RMAP_OUT &&
+      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
+      && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  if (filter->map[direct].name)
+    free (filter->map[direct].name);
+  
+  filter->map[direct].name = strdup (name);
+  filter->map[direct].map = route_map_lookup_by_name (name);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->map[direct].name)
+       free (filter->map[direct].name);
+      filter->map[direct].name = strdup (name);
+      filter->map[direct].map = route_map_lookup_by_name (name);
+    }
+  return 0;
+}
+
+/* Unset route-map from the peer. */
+int
+peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+  struct bgp_filter *filter;
+  struct bgp_filter *gfilter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (direct != RMAP_IN && direct != RMAP_OUT &&
+      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
+    return BGP_ERR_INVALID_VALUE;
+
+  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
+      && peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  /* apply peer-group filter */
+  if (peer->af_group[afi][safi])
+    {
+      gfilter = &peer->group->conf->filter[afi][safi];
+
+      if (gfilter->map[direct].name)
+       {
+         if (filter->map[direct].name)
+           free (filter->map[direct].name);
+         filter->map[direct].name = strdup (gfilter->map[direct].name);
+         filter->map[direct].map = gfilter->map[direct].map;
+         return 0;
+       }
+    }
+
+  if (filter->map[direct].name)
+    free (filter->map[direct].name);
+  filter->map[direct].name = NULL;
+  filter->map[direct].map = NULL;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->map[direct].name)
+       free (filter->map[direct].name);
+      filter->map[direct].name = NULL;
+      filter->map[direct].map = NULL;
+    }
+  return 0;
+}
+
+/* Set unsuppress-map to the peer. */
+int
+peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, 
+                         const char *name)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  if (peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+      
+  filter = &peer->filter[afi][safi];
+
+  if (filter->usmap.name)
+    free (filter->usmap.name);
+  
+  filter->usmap.name = strdup (name);
+  filter->usmap.map = route_map_lookup_by_name (name);
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->usmap.name)
+       free (filter->usmap.name);
+      filter->usmap.name = strdup (name);
+      filter->usmap.map = route_map_lookup_by_name (name);
+    }
+  return 0;
+}
+
+/* Unset route-map from the peer. */
+int
+peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+  
+  if (peer_is_group_member (peer, afi, safi))
+    return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+  filter = &peer->filter[afi][safi];
+
+  if (filter->usmap.name)
+    free (filter->usmap.name);
+  filter->usmap.name = NULL;
+  filter->usmap.map = NULL;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      filter = &peer->filter[afi][safi];
+
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      if (filter->usmap.name)
+       free (filter->usmap.name);
+      filter->usmap.name = NULL;
+      filter->usmap.map = NULL;
+    }
+  return 0;
+}
+
+int
+peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi,
+                        u_int32_t max, u_char threshold,
+                        int warning, u_int16_t restart)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+  peer->pmax[afi][safi] = max;
+  peer->pmax_threshold[afi][safi] = threshold;
+  peer->pmax_restart[afi][safi] = restart;
+  if (warning)
+    SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+  else
+    UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+       {
+         if (! peer->af_group[afi][safi])
+           continue;
+
+         SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+         peer->pmax[afi][safi] = max;
+         peer->pmax_threshold[afi][safi] = threshold;
+         peer->pmax_restart[afi][safi] = restart;
+         if (warning)
+           SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+         else
+           UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+
+         if ((peer->status == Established) && (peer->afc[afi][safi]))
+           bgp_maximum_prefix_overflow (peer, afi, safi, 1);
+       }
+    }
+  else
+    {
+      if ((peer->status == Established) && (peer->afc[afi][safi]))
+       bgp_maximum_prefix_overflow (peer, afi, safi, 1);
+    }
+
+  return 0;
+}
+
+int
+peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_PEER_INACTIVE;
+
+  /* apply peer-group config */
+  if (peer->af_group[afi][safi])
+    {
+      if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi],
+         PEER_FLAG_MAX_PREFIX))
+       SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+      else
+       UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+
+      if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi],
+         PEER_FLAG_MAX_PREFIX_WARNING))
+       SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+      else
+       UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+
+      peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi];
+      peer->pmax_threshold[afi][safi] = peer->group->conf->pmax_threshold[afi][safi];
+      peer->pmax_restart[afi][safi] = peer->group->conf->pmax_restart[afi][safi];
+      return 0;
+    }
+
+  UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+  UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+  peer->pmax[afi][safi] = 0;
+  peer->pmax_threshold[afi][safi] = 0;
+  peer->pmax_restart[afi][safi] = 0;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    return 0;
+
+  group = peer->group;
+  for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+    {
+      if (! peer->af_group[afi][safi])
+       continue;
+
+      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
+      UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
+      peer->pmax[afi][safi] = 0;
+      peer->pmax_threshold[afi][safi] = 0;
+      peer->pmax_restart[afi][safi] = 0;
+    }
+  return 0;
+}
+
+/* Set # of hops between us and BGP peer. */
+int
+peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
+{
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+  struct peer *peer1;
+
+  zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host);
+
+  if (peer->ttl != 0)
+    return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+  if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      group = peer->group;
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
+        {
+          if (peer1->ttl != 0)
+            return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+        }
+    }
+
+  peer->gtsm_hops = gtsm_hops;
+
+  if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
+      bgp_set_socket_ttl (peer, peer->fd);
+    }
+  else
+    {
+      group = peer->group;
+      for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+       {
+         peer->gtsm_hops = gtsm_hops;
+
+         /* Change setting of existing peer
+          *   established then change value (may break connectivity)
+          *   not established yet (teardown session and restart)
+          *   no session then do nothing (will get handled by next connection)
+          */
+         if (peer->status == Established)
+           {
+              bgp_set_socket_ttl (peer, peer->fd);
+           }
+         else if (peer->status < Established)
+           {
+             if (BGP_DEBUG (events, EVENTS))
+               zlog_debug ("%s Min-ttl changed", peer->host);
+             BGP_EVENT_ADD (peer, BGP_Stop);
+           }
+       }
+    }
+
+  return 0;
+}
+
+int
+peer_clear (struct peer *peer)
+{
+  if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
+    {
+      if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+       {
+         UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+         if (peer->t_pmax_restart)
+           {
+             BGP_TIMER_OFF (peer->t_pmax_restart);
+             if (BGP_DEBUG (events, EVENTS))
+               zlog_debug ("%s Maximum-prefix restart timer canceled",
+                           peer->host);
+           }
+         BGP_EVENT_ADD (peer, BGP_Start);
+         return 0;
+       }
+
+      peer->v_start = BGP_INIT_START_TIMER;
+      if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+       bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                        BGP_NOTIFY_CEASE_ADMIN_RESET);
+      else
+        BGP_EVENT_ADD (peer, BGP_Stop);
+    }
+  return 0;
+}
+
+int
+peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi,
+                enum bgp_clear_type stype)
+{
+  if (peer->status != Established)
+    return 0;
+
+  if (! peer->afc[afi][safi])
+    return BGP_ERR_AF_UNCONFIGURED;
+
+  peer->rtt = sockopt_tcp_rtt (peer->fd);
+
+  if (stype == BGP_CLEAR_SOFT_RSCLIENT)
+    {
+      if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+        return 0;
+      bgp_check_local_routes_rsclient (peer, afi, safi);
+      bgp_soft_reconfig_rsclient (peer, afi, safi);
+    }
+
+  if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH)
+    bgp_announce_route (peer, afi, safi);
+
+  if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX)
+    {
+      if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+         && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)
+             || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
+       {
+         struct bgp_filter *filter = &peer->filter[afi][safi];
+         u_char prefix_type;
+
+         if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
+           prefix_type = ORF_TYPE_PREFIX;
+         else
+           prefix_type = ORF_TYPE_PREFIX_OLD;
+
+         if (filter->plist[FILTER_IN].plist)
+           {
+             if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
+               bgp_route_refresh_send (peer, afi, safi,
+                                       prefix_type, REFRESH_DEFER, 1);
+             bgp_route_refresh_send (peer, afi, safi, prefix_type,
+                                     REFRESH_IMMEDIATE, 0);
+           }
+         else
+           {
+             if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND))
+               bgp_route_refresh_send (peer, afi, safi,
+                                       prefix_type, REFRESH_IMMEDIATE, 1);
+             else
+               bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
+           }
+         return 0;
+       }
+    }
+
+  if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH
+      || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX)
+    {
+      /* If neighbor has soft reconfiguration inbound flag.
+        Use Adj-RIB-In database. */
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+       bgp_soft_reconfig_in (peer, afi, safi);
+      else
+       {
+         /* If neighbor has route refresh capability, send route refresh
+            message to the peer. */
+         if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV)
+             || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+           bgp_route_refresh_send (peer, afi, safi, 0, 0, 0);
+         else
+           return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED;
+       }
+    }
+  return 0;
+}
+
+/* Display peer uptime.*/
+/* XXX: why does this function return char * when it takes buffer? */
+char *
+peer_uptime (time_t uptime2, char *buf, size_t len)
+{
+  time_t uptime1;
+  struct tm *tm;
+
+  /* Check buffer length. */
+  if (len < BGP_UPTIME_LEN)
+    {
+      zlog_warn ("peer_uptime (): buffer shortage %lu", (u_long)len);
+      /* XXX: should return status instead of buf... */
+      snprintf (buf, len, "<error> "); 
+      return buf;
+    }
+
+  /* If there is no connection has been done before print `never'. */
+  if (uptime2 == 0)
+    {
+      snprintf (buf, len, "never   ");
+      return buf;
+    }
+
+  /* Get current time. */
+  uptime1 = bgp_clock ();
+  uptime1 -= uptime2;
+  tm = gmtime (&uptime1);
+  
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND ONE_DAY_SECOND*7
+#define ONE_YEAR_SECOND ONE_DAY_SECOND*365
+
+  if (uptime1 < ONE_DAY_SECOND)
+    snprintf (buf, len, "%02d:%02d:%02d", 
+             tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (uptime1 < ONE_WEEK_SECOND)
+    snprintf (buf, len, "%dd%02dh%02dm", 
+             tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else if (uptime1 < ONE_YEAR_SECOND)
+    snprintf (buf, len, "%02dw%dd%02dh", 
+             tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  else
+    snprintf (buf, len, "%02dy%02dw%dd", 
+             tm->tm_year - 70, tm->tm_yday/7, 
+             tm->tm_yday - ((tm->tm_yday/7) * 7));
+  return buf;
+}
+
+static void
+bgp_config_write_filter (struct vty *vty, struct peer *peer,
+                        afi_t afi, safi_t safi)
+{
+  struct bgp_filter *filter;
+  struct bgp_filter *gfilter = NULL;
+  char *addr;
+  int in = FILTER_IN;
+  int out = FILTER_OUT;
+
+  addr = peer->host;
+  filter = &peer->filter[afi][safi];
+  if (peer->af_group[afi][safi])
+    gfilter = &peer->group->conf->filter[afi][safi];
+
+  /* distribute-list. */
+  if (filter->dlist[in].name)
+    if (! gfilter || ! gfilter->dlist[in].name
+       || strcmp (filter->dlist[in].name, gfilter->dlist[in].name) != 0)
+    vty_out (vty, " neighbor %s distribute-list %s in%s", addr, 
+            filter->dlist[in].name, VTY_NEWLINE);
+  if (filter->dlist[out].name && ! gfilter)
+    vty_out (vty, " neighbor %s distribute-list %s out%s", addr, 
+            filter->dlist[out].name, VTY_NEWLINE);
+
+  /* prefix-list. */
+  if (filter->plist[in].name)
+    if (! gfilter || ! gfilter->plist[in].name
+       || strcmp (filter->plist[in].name, gfilter->plist[in].name) != 0)
+    vty_out (vty, " neighbor %s prefix-list %s in%s", addr, 
+            filter->plist[in].name, VTY_NEWLINE);
+  if (filter->plist[out].name && ! gfilter)
+    vty_out (vty, " neighbor %s prefix-list %s out%s", addr, 
+            filter->plist[out].name, VTY_NEWLINE);
+
+  /* route-map. */
+  if (filter->map[RMAP_IN].name)
+    if (! gfilter || ! gfilter->map[RMAP_IN].name
+       || strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0)
+      vty_out (vty, " neighbor %s route-map %s in%s", addr, 
+              filter->map[RMAP_IN].name, VTY_NEWLINE);
+  if (filter->map[RMAP_OUT].name && ! gfilter)
+    vty_out (vty, " neighbor %s route-map %s out%s", addr, 
+            filter->map[RMAP_OUT].name, VTY_NEWLINE);
+  if (filter->map[RMAP_IMPORT].name && ! gfilter)
+    vty_out (vty, " neighbor %s route-map %s import%s", addr,
+        filter->map[RMAP_IMPORT].name, VTY_NEWLINE);
+  if (filter->map[RMAP_EXPORT].name)
+    if (! gfilter || ! gfilter->map[RMAP_EXPORT].name
+    || strcmp (filter->map[RMAP_EXPORT].name,
+                    gfilter->map[RMAP_EXPORT].name) != 0)
+    vty_out (vty, " neighbor %s route-map %s export%s", addr,
+        filter->map[RMAP_EXPORT].name, VTY_NEWLINE);
+
+  /* unsuppress-map */
+  if (filter->usmap.name && ! gfilter)
+    vty_out (vty, " neighbor %s unsuppress-map %s%s", addr,
+            filter->usmap.name, VTY_NEWLINE);
+
+  /* filter-list. */
+  if (filter->aslist[in].name)
+    if (! gfilter || ! gfilter->aslist[in].name
+       || strcmp (filter->aslist[in].name, gfilter->aslist[in].name) != 0)
+      vty_out (vty, " neighbor %s filter-list %s in%s", addr, 
+              filter->aslist[in].name, VTY_NEWLINE);
+  if (filter->aslist[out].name && ! gfilter)
+    vty_out (vty, " neighbor %s filter-list %s out%s", addr, 
+            filter->aslist[out].name, VTY_NEWLINE);
+}
+
+/* BGP peer configuration display function. */
+static void
+bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
+                      struct peer *peer, afi_t afi, safi_t safi)
+{
+  struct peer *g_peer = NULL;
+  char buf[SU_ADDRSTRLEN];
+  char *addr;
+
+  addr = peer->host;
+  if (peer_group_active (peer))
+    g_peer = peer->group->conf;
+
+  /************************************
+   ****** Global to the neighbor ******
+   ************************************/
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    {
+      /* remote-as. */
+      if (! peer_group_active (peer))
+       {
+         if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+           vty_out (vty, " neighbor %s peer-group%s", addr,
+                    VTY_NEWLINE);
+         if (peer->as)
+           vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
+                    VTY_NEWLINE);
+       }
+      else
+       {
+         if (! g_peer->as)
+           vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as,
+                    VTY_NEWLINE);
+         if (peer->af_group[AFI_IP][SAFI_UNICAST])
+           vty_out (vty, " neighbor %s peer-group %s%s", addr,
+                    peer->group->name, VTY_NEWLINE);
+       }
+
+      /* local-as. */
+      if (peer->change_local_as)
+       if (! peer_group_active (peer))
+         vty_out (vty, " neighbor %s local-as %u%s%s%s", addr,
+                  peer->change_local_as,
+                  CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ?
+                  " no-prepend" : "",
+                  CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ?
+                  " replace-as" : "", VTY_NEWLINE);
+
+      /* Description. */
+      if (peer->desc)
+       vty_out (vty, " neighbor %s description %s%s", addr, peer->desc,
+                VTY_NEWLINE);
+
+      /* Shutdown. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN))
+         vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE);
+
+      /* Password. */
+      if (peer->password)
+       if (!peer_group_active (peer)
+           || ! g_peer->password
+           || strcmp (peer->password, g_peer->password) != 0)
+         vty_out (vty, " neighbor %s password %s%s", addr, peer->password,
+                  VTY_NEWLINE);
+
+      /* BGP port. */
+      if (peer->port != BGP_PORT_DEFAULT)
+       vty_out (vty, " neighbor %s port %d%s", addr, peer->port,
+                VTY_NEWLINE);
+
+      /* Local interface name. */
+      if (peer->ifname)
+       vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname,
+                VTY_NEWLINE);
+  
+      /* Passive. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE))
+         vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE);
+
+      /* TTL option */
+      if (peer->gtsm_hops && ! peer_group_active (peer))
+        vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
+                 peer->gtsm_hops, VTY_NEWLINE);
+      else if (peer->ttl && ! peer_group_active (peer))
+        vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
+                 VTY_NEWLINE);
+
+      /* disable-connected-check.  */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+       if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+         vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE);
+
+      /* Update-source. */
+      if (peer->update_if)
+       if (! peer_group_active (peer) || ! g_peer->update_if
+           || strcmp (g_peer->update_if, peer->update_if) != 0)
+         vty_out (vty, " neighbor %s update-source %s%s", addr,
+                  peer->update_if, VTY_NEWLINE);
+      if (peer->update_source)
+       if (! peer_group_active (peer) || ! g_peer->update_source
+           || sockunion_cmp (g_peer->update_source,
+                             peer->update_source) != 0)
+         vty_out (vty, " neighbor %s update-source %s%s", addr,
+                  sockunion2str (peer->update_source, buf, SU_ADDRSTRLEN),
+                  VTY_NEWLINE);
+
+      /* advertisement-interval */
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV) &&
+          ! peer_group_active (peer))
+       vty_out (vty, " neighbor %s advertisement-interval %d%s",
+                addr, peer->v_routeadv, VTY_NEWLINE); 
+
+      /* timers. */
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)
+         && ! peer_group_active (peer))
+         vty_out (vty, " neighbor %s timers %d %d%s", addr, 
+         peer->keepalive, peer->holdtime, VTY_NEWLINE);
+
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT) &&
+          ! peer_group_active (peer))
+         vty_out (vty, " neighbor %s timers connect %d%s", addr, 
+         peer->connect, VTY_NEWLINE);
+
+      /* Default weight. */
+      if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT))
+        if (! peer_group_active (peer) ||
+           g_peer->weight != peer->weight)
+         vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight,
+                  VTY_NEWLINE);
+
+      /* Dynamic capability.  */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+       vty_out (vty, " neighbor %s capability dynamic%s", addr,
+            VTY_NEWLINE);
+
+      /* dont capability negotiation. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY))
+       vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr,
+                VTY_NEWLINE);
+
+      /* override capability negotiation. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+       vty_out (vty, " neighbor %s override-capability%s", addr,
+                VTY_NEWLINE);
+
+      /* strict capability negotiation. */
+      if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
+        if (! peer_group_active (peer) ||
+           ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH))
+       vty_out (vty, " neighbor %s strict-capability-match%s", addr,
+            VTY_NEWLINE);
+
+      if (! peer->af_group[AFI_IP][SAFI_UNICAST])
+       {
+         if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+           {
+             if (peer->afc[AFI_IP][SAFI_UNICAST])
+               vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE);
+           }
+          else
+           {
+             if (! peer->afc[AFI_IP][SAFI_UNICAST])
+               vty_out (vty, " no neighbor %s activate%s", addr, VTY_NEWLINE);
+           }
+       }
+    }
+
+
+  /************************************
+   ****** Per AF to the neighbor ******
+   ************************************/
+
+  if (! (afi == AFI_IP && safi == SAFI_UNICAST))
+    {
+      if (peer->af_group[afi][safi])
+       vty_out (vty, " neighbor %s peer-group %s%s", addr,
+                peer->group->name, VTY_NEWLINE);
+      else
+       vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE);
+    }
+
+  /* ORF capability.  */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+    if (! peer->af_group[afi][safi])
+    {
+      vty_out (vty, " neighbor %s capability orf prefix-list", addr);
+
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+         && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM))
+       vty_out (vty, " both");
+      else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM))
+       vty_out (vty, " send");
+      else
+       vty_out (vty, " receive");
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  /* Route reflector client. */
+  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)
+      && ! peer->af_group[afi][safi])
+    vty_out (vty, " neighbor %s route-reflector-client%s", addr, 
+            VTY_NEWLINE);
+
+  /* Nexthop self. */
+  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)
+      && ! peer->af_group[afi][safi])
+    vty_out (vty, " neighbor %s next-hop-self%s%s", addr,
+            peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF_ALL) ?
+            " all" : "", VTY_NEWLINE);
+
+  /* Remove private AS. */
+  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)
+      && ! peer->af_group[afi][safi])
+    vty_out (vty, " neighbor %s remove-private-AS%s",
+            addr, VTY_NEWLINE);
+
+  /* send-community print. */
+  if (! peer->af_group[afi][safi])
+    {
+      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
+       {
+         if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
+             && peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)
+             && peer_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
+           vty_out (vty, " neighbor %s send-community all%s", addr, VTY_NEWLINE);
+         else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))  
+           vty_out (vty, " neighbor %s send-community extended%s",
+                    addr, VTY_NEWLINE);
+         else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
+           vty_out (vty, " neighbor %s send-community large%s",
+                    addr, VTY_NEWLINE);
+         else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))
+           vty_out (vty, " neighbor %s send-community%s", addr, VTY_NEWLINE);
+       }
+      else
+       {
+         if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
+             && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)
+             && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
+           vty_out (vty, " no neighbor %s send-community all%s",
+                    addr, VTY_NEWLINE);
+         else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
+           vty_out (vty, " no neighbor %s send-community extended%s",
+                    addr, VTY_NEWLINE);
+         else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
+           vty_out (vty, " no neighbor %s send-community large%s",
+                    addr, VTY_NEWLINE);
+         else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))
+           vty_out (vty, " no neighbor %s send-community%s",
+                    addr, VTY_NEWLINE);
+       }
+    }
+
+  /* Default information */
+  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE)
+      && ! peer->af_group[afi][safi])
+    {
+      vty_out (vty, " neighbor %s default-originate", addr);
+      if (peer->default_rmap[afi][safi].name)
+       vty_out (vty, " route-map %s", peer->default_rmap[afi][safi].name);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  /* Soft reconfiguration inbound. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+    if (! peer->af_group[afi][safi] ||
+       ! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+    vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr,
+            VTY_NEWLINE);
+
+  /* maximum-prefix. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
+    if (! peer->af_group[afi][safi]
+       || g_peer->pmax[afi][safi] != peer->pmax[afi][safi]
+        || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi]
+       || CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)
+          != CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
+      {
+       vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]);
+       if (peer->pmax_threshold[afi][safi] != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
+         vty_out (vty, " %d", peer->pmax_threshold[afi][safi]);
+       if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING))
+         vty_out (vty, " warning-only");
+       if (peer->pmax_restart[afi][safi])
+         vty_out (vty, " restart %d", peer->pmax_restart[afi][safi]);
+       vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+  /* Route server client. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+      && ! peer->af_group[afi][safi])
+    vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE);
+
+  /* Nexthop-local unchanged. */
+  if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)
+      && ! peer->af_group[afi][safi])
+    vty_out (vty, " neighbor %s nexthop-local unchanged%s", addr, VTY_NEWLINE);
+
+  /* Allow AS in.  */
+  if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN))
+    if (! peer_group_active (peer)
+       || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN)
+       || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi])
+      {
+       if (peer->allowas_in[afi][safi] == 3)
+         vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE);
+       else
+         vty_out (vty, " neighbor %s allowas-in %d%s", addr,
+                  peer->allowas_in[afi][safi], VTY_NEWLINE);
+      }
+
+  /* Filter. */
+  bgp_config_write_filter (vty, peer, afi, safi);
+
+  /* atribute-unchanged. */
+  if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
+      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
+      || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+      && ! peer->af_group[afi][safi])
+    {
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
+          && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
+          && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+       vty_out (vty, " neighbor %s attribute-unchanged%s", addr, VTY_NEWLINE);
+      else
+       vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr, 
+            (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) ?
+            " as-path" : "",
+            (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) ?
+            " next-hop" : "",
+            (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) ?
+            " med" : "", VTY_NEWLINE);
+    }
+}
+
+/* Display "address-family" configuration header. */
+void
+bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi,
+                               int *write)
+{
+  if (*write)
+    return;
+
+  if (afi == AFI_IP && safi == SAFI_UNICAST)
+    return;
+
+  vty_out (vty, "!%s address-family ", VTY_NEWLINE);
+
+  if (afi == AFI_IP)
+    {
+      if (safi == SAFI_MULTICAST)
+       vty_out (vty, "ipv4 multicast");
+      else if (safi == SAFI_MPLS_VPN)
+       vty_out (vty, "vpnv4");
+      else if (safi == SAFI_ENCAP)
+       vty_out (vty, "encap");
+    }
+  else if (afi == AFI_IP6)
+    {
+      if (safi == SAFI_MPLS_VPN)
+        vty_out (vty, "vpnv6");
+      else if (safi == SAFI_ENCAP)
+        vty_out (vty, "encapv6");
+      else
+        {
+          vty_out (vty, "ipv6");
+          if (safi == SAFI_MULTICAST)
+            vty_out (vty, " multicast");
+        }
+    }
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  *write = 1;
+}
+
+/* Address family based peer configuration display.  */
+static int
+bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi,
+                        safi_t safi)
+{
+  int write = 0;
+  struct peer *peer;
+  struct peer_group *group;
+  struct listnode *node, *nnode;
+
+  bgp_config_write_network (vty, bgp, afi, safi, &write);
+
+  bgp_config_write_redistribute (vty, bgp, afi, safi, &write);
+
+  for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+    {
+      if (group->conf->afc[afi][safi])
+       {
+         bgp_config_write_family_header (vty, afi, safi, &write);
+         bgp_config_write_peer (vty, bgp, group->conf, afi, safi);
+       }
+    }
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    {
+      if (peer->afc[afi][safi])
+       {
+         if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+           {
+             bgp_config_write_family_header (vty, afi, safi, &write);
+             bgp_config_write_peer (vty, bgp, peer, afi, safi);
+           }
+       }
+    }
+
+  bgp_config_write_maxpaths (vty, bgp, afi, safi, &write);
+
+  bgp_config_write_distance (vty, bgp, afi, safi, &write);
+
+  if (write)
+    vty_out (vty, " exit-address-family%s", VTY_NEWLINE);
+
+  return write;
+}
+
+int
+bgp_config_write (struct vty *vty)
+{
+  int write = 0;
+  struct bgp *bgp;
+  struct peer_group *group;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  struct listnode *mnode, *mnnode;
+
+  /* BGP Multiple instance. */
+  if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
+    {    
+      vty_out (vty, "bgp multiple-instance%s", VTY_NEWLINE);
+      write++;
+    }
+
+  /* BGP Config type. */
+  if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
+    {    
+      vty_out (vty, "bgp config-type cisco%s", VTY_NEWLINE);
+      write++;
+    }
+
+  /* BGP configuration. */
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    {
+      if (write)
+       vty_out (vty, "!%s", VTY_NEWLINE);
+
+      /* Router bgp ASN */
+      vty_out (vty, "router bgp %u", bgp->as);
+
+      if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE))
+       {
+         if (bgp->name)
+           vty_out (vty, " view %s", bgp->name);
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      /* No Synchronization */
+      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
+       vty_out (vty, " no synchronization%s", VTY_NEWLINE);
+
+      /* BGP fast-external-failover. */
+      if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
+       vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE); 
+
+      /* BGP router ID. */
+      if (CHECK_FLAG (bgp->config, BGP_CONFIG_ROUTER_ID))
+       vty_out (vty, " bgp router-id %s%s", inet_ntoa (bgp->router_id), 
+                VTY_NEWLINE);
+
+      /* BGP log-neighbor-changes. */
+      if (!bgp_flag_check (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES))
+       vty_out (vty, " no bgp log-neighbor-changes%s", VTY_NEWLINE);
+
+      /* BGP configuration. */
+      if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
+       vty_out (vty, " bgp always-compare-med%s", VTY_NEWLINE);
+
+      /* BGP default ipv4-unicast. */
+      if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+       vty_out (vty, " no bgp default ipv4-unicast%s", VTY_NEWLINE);
+
+      /* BGP default local-preference. */
+      if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
+       vty_out (vty, " bgp default local-preference %d%s",
+                bgp->default_local_pref, VTY_NEWLINE);
+
+      /* BGP client-to-client reflection. */
+      if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
+       vty_out (vty, " no bgp client-to-client reflection%s", VTY_NEWLINE);
+      
+      /* BGP cluster ID. */
+      if (CHECK_FLAG (bgp->config, BGP_CONFIG_CLUSTER_ID))
+       vty_out (vty, " bgp cluster-id %s%s", inet_ntoa (bgp->cluster_id),
+                VTY_NEWLINE);
+
+      /* Confederation identifier*/
+      if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
+       vty_out (vty, " bgp confederation identifier %i%s", bgp->confed_id,
+                VTY_NEWLINE);
+
+      /* Confederation peer */
+      if (bgp->confed_peers_cnt > 0)
+       {
+         int i;
+
+         vty_out (vty, " bgp confederation peers");
+
+         for (i = 0; i < bgp->confed_peers_cnt; i++)
+           vty_out(vty, " %u", bgp->confed_peers[i]);
+
+          vty_out (vty, "%s", VTY_NEWLINE);
+       }
+
+      /* BGP enforce-first-as. */
+      if (bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
+       vty_out (vty, " bgp enforce-first-as%s", VTY_NEWLINE);
+
+      /* BGP deterministic-med. */
+      if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
+       vty_out (vty, " bgp deterministic-med%s", VTY_NEWLINE);
+
+      /* BGP graceful-restart. */
+      if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
+       vty_out (vty, " bgp graceful-restart stalepath-time %d%s",
+                bgp->stalepath_time, VTY_NEWLINE);
+      if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
+       vty_out (vty, " bgp graceful-restart restart-time %d%s",
+                bgp->restart_time, VTY_NEWLINE);
+      if (bgp_flag_check (bgp, BGP_FLAG_GRACEFUL_RESTART))
+       vty_out (vty, " bgp graceful-restart%s", VTY_NEWLINE);
+
+      /* BGP bestpath method. */
+      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
+       vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE);
+      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
+       vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE);
+      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+       vty_out (vty, " bgp bestpath as-path multipath-relax%s", VTY_NEWLINE);
+      }
+      if (bgp_flag_check (bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+       vty_out (vty, " bgp route-reflector allow-outbound-policy%s",
+                VTY_NEWLINE);
+      }
+      if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID))
+       vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE);
+      if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
+         || bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
+       {
+         vty_out (vty, " bgp bestpath med");
+         if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED))
+           vty_out (vty, " confed");
+         if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
+           vty_out (vty, " missing-as-worst");
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+
+      /* BGP network import check. */
+      if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
+       vty_out (vty, " bgp network import-check%s", VTY_NEWLINE);
+
+      /* BGP flag dampening. */
+      if (CHECK_FLAG (bgp->af_flags[AFI_IP][SAFI_UNICAST],
+         BGP_CONFIG_DAMPENING))
+       bgp_config_write_damp (vty);
+
+      /* BGP static route configuration. */
+      bgp_config_write_network (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
+
+      /* BGP redistribute configuration. */
+      bgp_config_write_redistribute (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
+
+      /* BGP timers configuration. */
+      if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE
+         && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
+       vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive, 
+                bgp->default_holdtime, VTY_NEWLINE);
+
+      /* peer-group */
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+       {
+         bgp_config_write_peer (vty, bgp, group->conf, AFI_IP, SAFI_UNICAST);
+       }
+
+      /* Normal neighbor configuration. */
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+       {
+         if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+           bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST);
+       }
+
+      /* maximum-paths */
+      bgp_config_write_maxpaths (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
+
+      /* Distance configuration. */
+      bgp_config_write_distance (vty, bgp, AFI_IP, SAFI_UNICAST, &write);
+      
+      /* No auto-summary */
+      if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
+       vty_out (vty, " no auto-summary%s", VTY_NEWLINE);
+
+      /* IPv4 multicast configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MULTICAST);
+
+      /* IPv4 VPN configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MPLS_VPN);
+
+      /* ENCAPv4 configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_ENCAP);
+
+      /* IPv6 unicast configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_UNICAST);
+
+      /* IPv6 multicast configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MULTICAST);
+
+      /* IPv6 VPN configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
+
+      /* ENCAPv6 configuration.  */
+      write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_ENCAP);
+
+      vty_out (vty, " exit%s", VTY_NEWLINE);
+
+      write++;
+    }
+  return write;
+}
+
+void
+bgp_master_init (void)
+{
+  memset (&bgp_master, 0, sizeof (struct bgp_master));
+
+  bm = &bgp_master;
+  bm->bgp = list_new ();
+  bm->listen_sockets = list_new ();
+  bm->port = BGP_PORT_DEFAULT;
+  bm->master = thread_master_create ();
+  bm->start_time = bgp_clock ();
+}
+
+
+void
+bgp_init (void)
+{
+
+  /* allocates some vital data structures used by peer commands in vty_init */
+  bgp_scan_init ();
+
+  /* Init zebra. */
+  bgp_zebra_init (bm->master);
+
+  /* BGP VTY commands installation.  */
+  bgp_vty_init ();
+
+  /* BGP inits. */
+  bgp_attr_init ();
+  bgp_debug_init ();
+  bgp_dump_init ();
+  bgp_route_init ();
+  bgp_route_map_init ();
+  bgp_address_init ();
+  bgp_scan_vty_init();
+  bgp_mplsvpn_init ();
+  bgp_encap_init ();
+
+  /* Access list initialize. */
+  access_list_init ();
+  access_list_add_hook (peer_distribute_update);
+  access_list_delete_hook (peer_distribute_update);
+
+  /* Filter list initialize. */
+  bgp_filter_init ();
+  as_list_add_hook (peer_aslist_update);
+  as_list_delete_hook (peer_aslist_update);
+
+  /* Prefix list initialize.*/
+  prefix_list_init ();
+  prefix_list_add_hook (peer_prefix_list_update);
+  prefix_list_delete_hook (peer_prefix_list_update);
+
+  /* Community list initialize. */
+  bgp_clist = community_list_init ();
+
+#ifdef HAVE_SNMP
+  bgp_snmp_init ();
+#endif /* HAVE_SNMP */
+}
+
+void
+bgp_terminate (void)
+{
+  struct bgp *bgp;
+  struct peer *peer;
+  struct listnode *node, *nnode;
+  struct listnode *mnode, *mnnode;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+      if (peer->status == Established)
+          bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+                           BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+  
+  bgp_cleanup_routes ();
+  
+  if (bm->process_main_queue)
+    {
+      work_queue_free (bm->process_main_queue);
+      bm->process_main_queue = NULL;
+    }
+  if (bm->process_rsclient_queue)
+    {
+      work_queue_free (bm->process_rsclient_queue);
+      bm->process_rsclient_queue = NULL;
+    }
+}
diff --git a/bgpd/bgpd.conf.sample b/bgpd/bgpd.conf.sample
new file mode 100644 (file)
index 0000000..b6a8b6f
--- /dev/null
@@ -0,0 +1,29 @@
+! -*- bgp -*-
+!
+! BGPd sample configuratin file
+!
+! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $
+!
+hostname bgpd
+password zebra
+!enable password please-set-at-here
+!
+!bgp mulitple-instance
+!
+router bgp 7675
+! bgp router-id 10.0.0.1
+! network 10.0.0.0/8
+! neighbor 10.0.0.2 remote-as 7675
+! neighbor 10.0.0.2 route-map set-nexthop out
+! neighbor 10.0.0.2 ebgp-multihop
+! neighbor 10.0.0.2 next-hop-self
+!
+! access-list all permit any
+!
+!route-map set-nexthop permit 10
+! match ip address all
+! set ip next-hop 10.0.0.1
+!
+!log file bgpd.log
+!
+log stdout
diff --git a/bgpd/bgpd.conf.sample2 b/bgpd/bgpd.conf.sample2
new file mode 100644 (file)
index 0000000..d376ad2
--- /dev/null
@@ -0,0 +1,77 @@
+!
+! Zebra configuration saved from vty
+!   2002/07/01 03:16:33
+!
+hostname bgpd
+password zebra
+log file bgpd.log
+log stdout
+!
+router bgp 7675
+ no bgp default ipv4-unicast
+ neighbor 3ffe:506:1000::2 remote-as 7675
+ neighbor fe80::200:c0ff:fe30:9be3 remote-as 9377
+ neighbor fe80::200:c0ff:fe30:9be3 interface sit3
+ neighbor fe80::210:5aff:fe6b:3cee remote-as 7675
+ neighbor fe80::210:5aff:fe6b:3cee interface eth0
+ neighbor fe80::290:27ff:fe51:84c7 remote-as 4691
+ neighbor fe80::290:27ff:fe51:84c7 description DTI 
+ neighbor fe80::290:27ff:fe51:84c7 interface sit7
+ neighbor fe80::2a0:c9ff:fec8:82ec remote-as 7530
+ neighbor fe80::2a0:c9ff:fec8:82ec description IRI 
+ neighbor fe80::2a0:c9ff:fec8:82ec interface sit8
+ neighbor fe80::2e0:18ff:fe98:2725 remote-as 2500
+ neighbor fe80::2e0:18ff:fe98:2725 description WIDE 
+ neighbor fe80::2e0:18ff:fe98:2725 interface sit5
+ neighbor fe80::2e0:18ff:fea8:bf5 remote-as 65000
+ neighbor fe80::2e0:18ff:fea8:bf5 interface sit6
+!
+ address-family ipv6
+ network 3ffe:506::/33
+ network 3ffe:1800:e800::/40
+ aggregate-address 3ffe:506::/32
+ redistribute connected
+ neighbor 3ffe:506:1000::2 activate
+ neighbor fe80::200:c0ff:fe30:9be3 activate
+ neighbor fe80::200:c0ff:fe30:9be3 route-map set-nexthop out
+ neighbor fe80::210:5aff:fe6b:3cee activate
+ neighbor fe80::290:27ff:fe51:84c7 activate
+ neighbor fe80::290:27ff:fe51:84c7 route-map set-nexthop out
+ neighbor fe80::2a0:c9ff:fec8:82ec activate
+ neighbor fe80::2a0:c9ff:fec8:82ec route-map set-nexthop out
+ neighbor fe80::2e0:18ff:fe98:2725 activate
+ neighbor fe80::2e0:18ff:fe98:2725 distribute-list nla1 out
+ neighbor fe80::2e0:18ff:fe98:2725 route-map set-nexthop out
+ neighbor fe80::2e0:18ff:fea8:bf5 activate
+ neighbor fe80::2e0:18ff:fea8:bf5 route-map set-nexthop out
+ exit-address-family
+!
+ipv6 access-list all permit any
+ipv6 access-list nla1 deny 3ffe:506::/33
+ipv6 access-list nla1 permit 3ffe:506::/32
+ipv6 access-list nla1 deny any
+ipv6 access-list ntt-nla1 permit 3ffe:1800:0:ffff::c/127
+ipv6 access-list ntt-nla1 deny 3ffe:1800:e800::/41
+ipv6 access-list ntt-nla1 permit 3ffe:1800:e800::/40
+ipv6 access-list ntt-nla1 deny any
+!
+ipv6 prefix-list 6bone-filter seq 5 permit 3ffe::/17 ge 24 le 24
+ipv6 prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 ge 28 le 28
+ipv6 prefix-list 6bone-filter seq 12 deny 3ffe::/16
+ipv6 prefix-list 6bone-filter seq 15 permit 2000::/3 ge 16 le 16
+ipv6 prefix-list 6bone-filter seq 20 permit 2001::/16 ge 35 le 35
+!
+route-map set-nexthop permit 10
+ match ipv6 address all
+ set ipv6 next-hop global 3ffe:506::1
+ set ipv6 next-hop local fe80::cbb5:591a
+ set ip next-hop 203.181.89.26
+ set community 7675:0
+!
+route-map set-link-local permit 10
+ match ipv6 address all
+ set ipv6 next-hop local fe80::cbb5:591a
+ set ipv6 next-hop global 3ffe:1800:0:ffff::d
+!
+line vty
+!
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
new file mode 100644 (file)
index 0000000..03df2f6
--- /dev/null
@@ -0,0 +1,1002 @@
+/* BGP message definition header.
+   Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGPD_H
+#define _QUAGGA_BGPD_H
+
+/* For union sockunion.  */
+#include "sockunion.h"
+
+/* Typedef BGP specific types.  */
+typedef u_int32_t as_t;
+typedef u_int16_t as16_t; /* we may still encounter 16 Bit asnums */
+typedef u_int16_t bgp_size_t;
+
+/* BGP master for system wide configurations and variables.  */
+struct bgp_master
+{
+  /* BGP instance list.  */
+  struct list *bgp;
+
+  /* BGP thread master.  */
+  struct thread_master *master;
+
+  /* work queues */
+  struct work_queue *process_main_queue;
+  struct work_queue *process_rsclient_queue;
+  
+  /* Listening sockets */
+  struct list *listen_sockets;
+  
+  /* BGP port number.  */
+  u_int16_t port;
+
+  /* Listener address */
+  char *address;
+
+  /* BGP start time.  */
+  time_t start_time;
+
+  /* Various BGP global configuration.  */
+  u_char options;
+#define BGP_OPT_NO_FIB                   (1 << 0)
+#define BGP_OPT_MULTIPLE_INSTANCE        (1 << 1)
+#define BGP_OPT_CONFIG_CISCO             (1 << 2)
+#define BGP_OPT_NO_LISTEN                (1 << 3)
+};
+
+/* BGP instance structure.  */
+struct bgp 
+{
+  /* AS number of this BGP instance.  */
+  as_t as;
+
+  /* Name of this BGP instance.  */
+  char *name;
+  
+  /* Reference count to allow peer_delete to finish after bgp_delete */
+  int lock;
+
+  /* Self peer.  */
+  struct peer *peer_self;
+
+  /* BGP peer. */
+  struct list *peer;
+
+  /* BGP peer group.  */
+  struct list *group;
+
+  /* BGP route-server-clients. */
+  struct list *rsclient;
+
+  /* BGP configuration.  */
+  u_int16_t config;
+#define BGP_CONFIG_ROUTER_ID              (1 << 0)
+#define BGP_CONFIG_CLUSTER_ID             (1 << 1)
+#define BGP_CONFIG_CONFEDERATION          (1 << 2)
+
+  /* BGP router identifier.  */
+  struct in_addr router_id;
+  struct in_addr router_id_static;
+
+  /* BGP route reflector cluster ID.  */
+  struct in_addr cluster_id;
+
+  /* BGP confederation information.  */
+  as_t confed_id;
+  as_t *confed_peers;
+  int confed_peers_cnt;
+
+  struct thread *t_startup;
+
+  /* BGP flags. */
+  u_int32_t flags;
+#define BGP_FLAG_ALWAYS_COMPARE_MED       (1 << 0)
+#define BGP_FLAG_DETERMINISTIC_MED        (1 << 1)
+#define BGP_FLAG_MED_MISSING_AS_WORST     (1 << 2)
+#define BGP_FLAG_MED_CONFED               (1 << 3)
+#define BGP_FLAG_NO_DEFAULT_IPV4          (1 << 4)
+#define BGP_FLAG_NO_CLIENT_TO_CLIENT      (1 << 5)
+#define BGP_FLAG_ENFORCE_FIRST_AS         (1 << 6)
+#define BGP_FLAG_COMPARE_ROUTER_ID        (1 << 7)
+#define BGP_FLAG_ASPATH_IGNORE            (1 << 8)
+#define BGP_FLAG_IMPORT_CHECK             (1 << 9)
+#define BGP_FLAG_NO_FAST_EXT_FAILOVER     (1 << 10)
+#define BGP_FLAG_LOG_NEIGHBOR_CHANGES     (1 << 11)
+#define BGP_FLAG_GRACEFUL_RESTART         (1 << 12)
+#define BGP_FLAG_ASPATH_CONFED            (1 << 13)
+#define BGP_FLAG_ASPATH_MULTIPATH_RELAX   (1 << 14)
+#define BGP_FLAG_DELETING                 (1 << 15)
+#define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 16)
+
+  /* BGP Per AF flags */
+  u_int16_t af_flags[AFI_MAX][SAFI_MAX];
+#define BGP_CONFIG_DAMPENING              (1 << 0)
+
+  /* Static route configuration.  */
+  struct bgp_table *route[AFI_MAX][SAFI_MAX];
+
+  /* Aggregate address configuration.  */
+  struct bgp_table *aggregate[AFI_MAX][SAFI_MAX];
+
+  /* BGP routing information base.  */
+  struct bgp_table *rib[AFI_MAX][SAFI_MAX];
+
+  /* BGP redistribute configuration. */
+  u_char redist[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+  /* BGP redistribute metric configuration. */
+  u_char redist_metric_flag[AFI_MAX][ZEBRA_ROUTE_MAX];
+  u_int32_t redist_metric[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+  /* BGP redistribute route-map.  */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } rmap[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+  /* BGP distance configuration.  */
+  u_char distance_ebgp;
+  u_char distance_ibgp;
+  u_char distance_local;
+
+  /* BGP ipv6 distance configuration.  */
+  u_char ipv6_distance_ebgp;
+  u_char ipv6_distance_ibgp;
+  u_char ipv6_distance_local;
+  
+  /* BGP default local-preference.  */
+  u_int32_t default_local_pref;
+
+  /* BGP default timer.  */
+  u_int32_t default_holdtime;
+  u_int32_t default_keepalive;
+
+  /* BGP graceful restart */
+  u_int32_t restart_time;
+  u_int32_t stalepath_time;
+
+  /* Maximum-paths configuration */
+  struct bgp_maxpaths_cfg {
+    u_int16_t maxpaths_ebgp;
+    u_int16_t maxpaths_ibgp;
+  } maxpaths[AFI_MAX][SAFI_MAX];
+};
+
+/* BGP peer-group support. */
+struct peer_group
+{
+  /* Name of the peer-group. */
+  char *name;
+
+  /* Pointer to BGP.  */
+  struct bgp *bgp;
+  
+  /* Peer-group client list. */
+  struct list *peer;
+
+  /* Peer-group config */
+  struct peer *conf;
+};
+
+/* BGP Notify message format. */
+struct bgp_notify 
+{
+  u_char code;
+  u_char subcode;
+  char *data;
+  bgp_size_t length;
+};
+
+/* Next hop self address. */
+struct bgp_nexthop
+{
+  struct interface *ifp;
+  struct in_addr v4;
+  struct in6_addr v6_global;
+  struct in6_addr v6_local;
+};
+
+/* BGP router distinguisher value.  */
+#define BGP_RD_SIZE                8
+
+struct bgp_rd
+{
+  u_char val[BGP_RD_SIZE];
+};
+
+#define RMAP_IN           0
+#define RMAP_OUT        1
+#define RMAP_IMPORT   2
+#define RMAP_EXPORT   3
+#define RMAP_MAX        4
+
+/* BGP filter structure. */
+struct bgp_filter
+{
+  /* Distribute-list.  */
+  struct 
+  {
+    char *name;
+    struct access_list *alist;
+  } dlist[FILTER_MAX];
+
+  /* Prefix-list.  */
+  struct
+  {
+    char *name;
+    struct prefix_list *plist;
+  } plist[FILTER_MAX];
+
+  /* Filter-list.  */
+  struct
+  {
+    char *name;
+    struct as_list *aslist;
+  } aslist[FILTER_MAX];
+
+  /* Route-map.  */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } map[RMAP_MAX];
+
+  /* Unsuppress-map.  */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } usmap;
+};
+
+/* IBGP/EBGP identifier.  We also have a CONFED peer, which is to say,
+   a peer who's AS is part of our Confederation.  */
+typedef enum
+{
+  BGP_PEER_IBGP = 1,
+  BGP_PEER_EBGP,
+  BGP_PEER_INTERNAL,
+  BGP_PEER_CONFED,
+} bgp_peer_sort_t;
+
+#define BGP_MAX_PACKET_SIZE_OVERFLOW          1024
+
+/* BGP neighbor structure. */
+struct peer
+{
+  /* BGP structure.  */
+  struct bgp *bgp;
+
+  /* reference count, primarily to allow bgp_process'ing of route_node's
+   * to be done after a struct peer is deleted.
+   *
+   * named 'lock' for hysterical reasons within Quagga.
+   */
+  int lock;
+
+  /* BGP peer group.  */
+  struct peer_group *group;
+  u_char af_group[AFI_MAX][SAFI_MAX];
+
+  /* Peer's remote AS number. */
+  as_t as;                     
+
+  /* Peer's local AS number. */
+  as_t local_as;
+
+  bgp_peer_sort_t sort;
+
+  /* Peer's Change local AS number. */
+  as_t change_local_as;
+
+  /* Remote router ID. */
+  struct in_addr remote_id;
+
+  /* Local router ID. */
+  struct in_addr local_id;
+
+  /* Peer specific RIB when configured as route-server-client. */
+  struct bgp_table *rib[AFI_MAX][SAFI_MAX];
+
+  /* Packet receive and send buffer. */
+  struct stream *ibuf;
+  struct stream_fifo *obuf;
+  struct stream *work;
+
+  /* We use a separate stream to encode MP_REACH_NLRI for efficient
+   * NLRI packing. peer->work stores all the other attributes. The
+   * actual packet is then constructed by concatenating the two.
+   */
+  struct stream *scratch;
+
+  /* Status of the peer. */
+  int status;
+  int ostatus;
+
+  /* Peer index, used for dumping TABLE_DUMP_V2 format */
+  uint16_t table_dump_index;
+
+  /* Peer information */
+  int fd;                      /* File descriptor */
+  int ttl;                     /* TTL of TCP connection to the peer. */
+  int rtt;                     /* Estimated round-trip-time from TCP_INFO */
+  int gtsm_hops;               /* minimum hopcount to peer */
+  char *desc;                  /* Description of the peer. */
+  unsigned short port;          /* Destination port for peer */
+  char *host;                  /* Printable address of the peer. */
+  union sockunion su;          /* Sockunion address of the peer. */
+  time_t uptime;               /* Last Up/Down time */
+  time_t readtime;             /* Last read time */
+  time_t resettime;            /* Last reset time */
+  
+  ifindex_t ifindex;           /* ifindex of the BGP connection. */
+  char *ifname;                        /* bind interface name. */
+  char *update_if;
+  union sockunion *update_source;
+  struct zlog *log;
+
+  union sockunion *su_local;   /* Sockunion of local address.  */
+  union sockunion *su_remote;  /* Sockunion of remote address.  */
+  int shared_network;          /* Is this peer shared same network. */
+  struct bgp_nexthop nexthop;  /* Nexthop */
+
+  /* Peer address family configuration. */
+  u_char afc[AFI_MAX][SAFI_MAX];
+  u_char afc_nego[AFI_MAX][SAFI_MAX];
+  u_char afc_adv[AFI_MAX][SAFI_MAX];
+  u_char afc_recv[AFI_MAX][SAFI_MAX];
+
+  /* Capability flags (reset in bgp_stop) */
+  u_int16_t cap;
+#define PEER_CAP_REFRESH_ADV                (1 << 0) /* refresh advertised */
+#define PEER_CAP_REFRESH_OLD_RCV            (1 << 1) /* refresh old received */
+#define PEER_CAP_REFRESH_NEW_RCV            (1 << 2) /* refresh rfc received */
+#define PEER_CAP_DYNAMIC_ADV                (1 << 3) /* dynamic advertised */
+#define PEER_CAP_DYNAMIC_RCV                (1 << 4) /* dynamic received */
+#define PEER_CAP_RESTART_ADV                (1 << 5) /* restart advertised */
+#define PEER_CAP_RESTART_RCV                (1 << 6) /* restart received */
+#define PEER_CAP_AS4_ADV                    (1 << 7) /* as4 advertised */
+#define PEER_CAP_AS4_RCV                    (1 << 8) /* as4 received */
+#define PEER_CAP_RESTART_BIT_ADV            (1 << 9) /* sent restart state */
+#define PEER_CAP_RESTART_BIT_RCV            (1 << 10) /* peer restart state */
+
+  /* Capability flags (reset in bgp_stop) */
+  u_int16_t af_cap[AFI_MAX][SAFI_MAX];
+#define PEER_CAP_ORF_PREFIX_SM_ADV          (1 << 0) /* send-mode advertised */
+#define PEER_CAP_ORF_PREFIX_RM_ADV          (1 << 1) /* receive-mode advertised */
+#define PEER_CAP_ORF_PREFIX_SM_RCV          (1 << 2) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_RCV          (1 << 3) /* receive-mode received */
+#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV      (1 << 4) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV      (1 << 5) /* receive-mode received */
+#define PEER_CAP_RESTART_AF_RCV             (1 << 6) /* graceful restart afi/safi received */
+#define PEER_CAP_RESTART_AF_PRESERVE_RCV    (1 << 7) /* graceful restart afi/safi F-bit received */
+
+  /* Global configuration flags. */
+  u_int32_t flags;
+#define PEER_FLAG_PASSIVE                   (1 << 0) /* passive mode */
+#define PEER_FLAG_SHUTDOWN                  (1 << 1) /* shutdown */
+#define PEER_FLAG_DONT_CAPABILITY           (1 << 2) /* dont-capability */
+#define PEER_FLAG_OVERRIDE_CAPABILITY       (1 << 3) /* override-capability */
+#define PEER_FLAG_STRICT_CAP_MATCH          (1 << 4) /* strict-match */
+#define PEER_FLAG_DYNAMIC_CAPABILITY        (1 << 5) /* dynamic capability */
+#define PEER_FLAG_DISABLE_CONNECTED_CHECK   (1 << 6) /* disable-connected-check */
+#define PEER_FLAG_LOCAL_AS_NO_PREPEND       (1 << 7) /* local-as no-prepend */
+#define PEER_FLAG_LOCAL_AS_REPLACE_AS       (1 << 8) /* local-as no-prepend replace-as */
+
+  /* NSF mode (graceful restart) */
+  u_char nsf[AFI_MAX][SAFI_MAX];
+
+  /* Per AF configuration flags. */
+  u_int32_t af_flags[AFI_MAX][SAFI_MAX];
+#define PEER_FLAG_SEND_COMMUNITY            (1 << 0) /* send-community */
+#define PEER_FLAG_SEND_EXT_COMMUNITY        (1 << 1) /* send-community ext. */
+#define PEER_FLAG_NEXTHOP_SELF              (1 << 2) /* next-hop-self */
+#define PEER_FLAG_REFLECTOR_CLIENT          (1 << 3) /* reflector-client */
+#define PEER_FLAG_RSERVER_CLIENT            (1 << 4) /* route-server-client */
+#define PEER_FLAG_SOFT_RECONFIG             (1 << 5) /* soft-reconfiguration */
+#define PEER_FLAG_AS_PATH_UNCHANGED         (1 << 6) /* transparent-as */
+#define PEER_FLAG_NEXTHOP_UNCHANGED         (1 << 7) /* transparent-next-hop */
+#define PEER_FLAG_MED_UNCHANGED             (1 << 8) /* transparent-next-hop */
+#define PEER_FLAG_DEFAULT_ORIGINATE         (1 << 9) /* default-originate */
+#define PEER_FLAG_REMOVE_PRIVATE_AS         (1 << 10) /* remove-private-as */
+#define PEER_FLAG_ALLOWAS_IN                (1 << 11) /* set allowas-in */
+#define PEER_FLAG_ORF_PREFIX_SM             (1 << 12) /* orf capability send-mode */
+#define PEER_FLAG_ORF_PREFIX_RM             (1 << 13) /* orf capability receive-mode */
+#define PEER_FLAG_MAX_PREFIX                (1 << 14) /* maximum prefix */
+#define PEER_FLAG_MAX_PREFIX_WARNING        (1 << 15) /* maximum prefix warning-only */
+#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED   (1 << 16) /* leave link-local nexthop unchanged */
+#define PEER_FLAG_NEXTHOP_SELF_ALL          (1 << 17) /* next-hop-self all */
+#define PEER_FLAG_SEND_LARGE_COMMUNITY      (1 << 18) /* Send large Communities */
+
+  /* MD5 password */
+  char *password;
+
+  /* default-originate route-map.  */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } default_rmap[AFI_MAX][SAFI_MAX];
+
+  /* Peer status flags. */
+  u_int16_t sflags;
+#define PEER_STATUS_ACCEPT_PEER              (1 << 0) /* accept peer */
+#define PEER_STATUS_PREFIX_OVERFLOW   (1 << 1) /* prefix-overflow */
+#define PEER_STATUS_CAPABILITY_OPEN   (1 << 2) /* capability open send */
+#define PEER_STATUS_OPEN_DEFERRED     (1 << 3) /* deferred to open_receive */
+#define PEER_STATUS_GROUP             (1 << 4) /* peer-group conf */
+#define PEER_STATUS_NSF_MODE          (1 << 5) /* NSF aware peer */
+#define PEER_STATUS_NSF_WAIT          (1 << 6) /* wait comeback peer */
+
+  /* Peer status af flags (reset in bgp_stop) */
+  u_int16_t af_sflags[AFI_MAX][SAFI_MAX];
+#define PEER_STATUS_ORF_PREFIX_SEND   (1 << 0) /* prefix-list send peer */
+#define PEER_STATUS_ORF_WAIT_REFRESH  (1 << 1) /* wait refresh received peer */
+#define PEER_STATUS_DEFAULT_ORIGINATE (1 << 2) /* default-originate peer */
+#define PEER_STATUS_PREFIX_THRESHOLD  (1 << 3) /* exceed prefix-threshold */
+#define PEER_STATUS_PREFIX_LIMIT      (1 << 4) /* exceed prefix-limit */
+#define PEER_STATUS_EOR_SEND          (1 << 5) /* end-of-rib send to peer */
+#define PEER_STATUS_EOR_RECEIVED      (1 << 6) /* end-of-rib received from peer */
+
+  /* Default attribute value for the peer. */
+  u_int32_t config;
+#define PEER_CONFIG_WEIGHT            (1 << 0) /* Default weight. */
+#define PEER_CONFIG_TIMER             (1 << 1) /* keepalive & holdtime */
+#define PEER_CONFIG_CONNECT           (1 << 2) /* connect */
+#define PEER_CONFIG_ROUTEADV          (1 << 3) /* route advertise */
+  u_int32_t weight;
+  u_int32_t holdtime;
+  u_int32_t keepalive;
+  u_int32_t connect;
+  u_int32_t routeadv;
+
+  /* Timer values. */
+  u_int32_t v_start;
+  u_int32_t v_connect;
+  u_int32_t v_holdtime;
+  u_int32_t v_keepalive;
+  u_int32_t v_routeadv;
+  u_int32_t v_pmax_restart;
+  u_int32_t v_gr_restart;
+
+  /* Threads. */
+  struct thread *t_read;
+  struct thread *t_write;
+  struct thread *t_start;
+  struct thread *t_connect;
+  struct thread *t_holdtime;
+  struct thread *t_keepalive;
+  struct thread *t_routeadv;
+  struct thread *t_pmax_restart;
+  struct thread *t_gr_restart;
+  struct thread *t_gr_stale;
+  
+  /* workqueues */
+  struct work_queue *clear_node_queue;
+  
+  /* Statistics field */
+  u_int32_t open_in;           /* Open message input count */
+  u_int32_t open_out;          /* Open message output count */
+  u_int32_t update_in;         /* Update message input count */
+  u_int32_t update_out;                /* Update message ouput count */
+  time_t update_time;          /* Update message received time. */
+  u_int32_t keepalive_in;      /* Keepalive input count */
+  u_int32_t keepalive_out;     /* Keepalive output count */
+  u_int32_t notify_in;         /* Notify input count */
+  u_int32_t notify_out;                /* Notify output count */
+  u_int32_t refresh_in;                /* Route Refresh input count */
+  u_int32_t refresh_out;       /* Route Refresh output count */
+  u_int32_t dynamic_cap_in;    /* Dynamic Capability input count.  */
+  u_int32_t dynamic_cap_out;   /* Dynamic Capability output count.  */
+
+  /* BGP state count */
+  u_int32_t established;       /* Established */
+  u_int32_t dropped;           /* Dropped */
+
+  /* Syncronization list and time.  */
+  struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX];
+  time_t synctime;
+
+  /* Send prefix count. */
+  unsigned long scount[AFI_MAX][SAFI_MAX];
+
+  /* Announcement attribute hash.  */
+  struct hash *hash[AFI_MAX][SAFI_MAX];
+
+  /* Notify data. */
+  struct bgp_notify notify;
+
+  /* Whole packet size to be read. */
+  unsigned long packet_size;
+
+  /* Filter structure. */
+  struct bgp_filter filter[AFI_MAX][SAFI_MAX];
+
+  /* ORF Prefix-list */
+  struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX];
+
+  /* Prefix count. */
+  unsigned long pcount[AFI_MAX][SAFI_MAX];
+
+  /* Max prefix count. */
+  unsigned long pmax[AFI_MAX][SAFI_MAX];
+  u_char pmax_threshold[AFI_MAX][SAFI_MAX];
+  u_int16_t pmax_restart[AFI_MAX][SAFI_MAX];
+#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
+
+  /* allowas-in. */
+  char allowas_in[AFI_MAX][SAFI_MAX];
+
+  /* peer reset cause */
+  char last_reset;
+#define PEER_DOWN_RID_CHANGE             1 /* bgp router-id command */
+#define PEER_DOWN_REMOTE_AS_CHANGE       2 /* neighbor remote-as command */
+#define PEER_DOWN_LOCAL_AS_CHANGE        3 /* neighbor local-as command */
+#define PEER_DOWN_CLID_CHANGE            4 /* bgp cluster-id command */
+#define PEER_DOWN_CONFED_ID_CHANGE       5 /* bgp confederation identifier command */
+#define PEER_DOWN_CONFED_PEER_CHANGE     6 /* bgp confederation peer command */
+#define PEER_DOWN_RR_CLIENT_CHANGE       7 /* neighbor route-reflector-client command */
+#define PEER_DOWN_RS_CLIENT_CHANGE       8 /* neighbor route-server-client command */
+#define PEER_DOWN_UPDATE_SOURCE_CHANGE   9 /* neighbor update-source command */
+#define PEER_DOWN_AF_ACTIVATE           10 /* neighbor activate command */
+#define PEER_DOWN_USER_SHUTDOWN         11 /* neighbor shutdown command */
+#define PEER_DOWN_USER_RESET            12 /* clear ip bgp command */
+#define PEER_DOWN_NOTIFY_RECEIVED       13 /* notification received */
+#define PEER_DOWN_NOTIFY_SEND           14 /* notification send */
+#define PEER_DOWN_CLOSE_SESSION         15 /* tcp session close */
+#define PEER_DOWN_NEIGHBOR_DELETE       16 /* neghbor delete */
+#define PEER_DOWN_RMAP_BIND             17 /* neghbor peer-group command */
+#define PEER_DOWN_RMAP_UNBIND           18 /* no neighbor peer-group command */
+#define PEER_DOWN_CAPABILITY_CHANGE     19 /* neighbor capability command */
+#define PEER_DOWN_PASSIVE_CHANGE        20 /* neighbor passive command */
+#define PEER_DOWN_MULTIHOP_CHANGE       21 /* neighbor multihop command */
+#define PEER_DOWN_NSF_CLOSE_SESSION     22 /* NSF tcp session close */
+
+  /* The kind of route-map Flags.*/
+  u_char rmap_type;
+#define PEER_RMAP_TYPE_IN             (1 << 0) /* neighbor route-map in */
+#define PEER_RMAP_TYPE_OUT            (1 << 1) /* neighbor route-map out */
+#define PEER_RMAP_TYPE_NETWORK        (1 << 2) /* network route-map */
+#define PEER_RMAP_TYPE_REDISTRIBUTE   (1 << 3) /* redistribute route-map */
+#define PEER_RMAP_TYPE_DEFAULT        (1 << 4) /* default-originate route-map */
+#define PEER_RMAP_TYPE_NOSET          (1 << 5) /* not allow to set commands */
+#define PEER_RMAP_TYPE_IMPORT         (1 << 6) /* neighbor route-map import */
+#define PEER_RMAP_TYPE_EXPORT         (1 << 7) /* neighbor route-map export */
+};
+
+#define PEER_PASSWORD_MINLEN   (1)
+#define PEER_PASSWORD_MAXLEN   (80)
+
+/* This structure's member directly points incoming packet data
+   stream. */
+struct bgp_nlri
+{
+  /* AFI.  */
+  afi_t afi;
+
+  /* SAFI.  */
+  safi_t safi;
+
+  /* Pointer to NLRI byte stream.  */
+  u_char *nlri;
+
+  /* Length of whole NLRI.  */
+  bgp_size_t length;
+};
+
+/* BGP versions.  */
+#define BGP_VERSION_4                           4
+
+/* Default BGP port number.  */
+#define BGP_PORT_DEFAULT                       179
+
+/* BGP message header and packet size.  */
+#define BGP_MARKER_SIZE                                16
+#define BGP_HEADER_SIZE                                19
+#define BGP_MAX_PACKET_SIZE                   4096
+
+/* BGP minimum message size.  */
+#define BGP_MSG_OPEN_MIN_SIZE                   (BGP_HEADER_SIZE + 10)
+#define BGP_MSG_UPDATE_MIN_SIZE                 (BGP_HEADER_SIZE + 4)
+#define BGP_MSG_NOTIFY_MIN_SIZE                 (BGP_HEADER_SIZE + 2)
+#define BGP_MSG_KEEPALIVE_MIN_SIZE              (BGP_HEADER_SIZE + 0)
+#define BGP_MSG_ROUTE_REFRESH_MIN_SIZE          (BGP_HEADER_SIZE + 4)
+#define BGP_MSG_CAPABILITY_MIN_SIZE             (BGP_HEADER_SIZE + 3)
+
+/* BGP message types.  */
+#define        BGP_MSG_OPEN                             1
+#define        BGP_MSG_UPDATE                           2
+#define        BGP_MSG_NOTIFY                           3
+#define        BGP_MSG_KEEPALIVE                        4
+#define BGP_MSG_ROUTE_REFRESH_NEW                5
+#define BGP_MSG_CAPABILITY                       6
+#define BGP_MSG_ROUTE_REFRESH_OLD              128
+
+/* BGP open optional parameter.  */
+#define BGP_OPEN_OPT_AUTH                        1
+#define BGP_OPEN_OPT_CAP                         2
+
+/* BGP4 attribute type codes.  */
+#define BGP_ATTR_ORIGIN                          1
+#define BGP_ATTR_AS_PATH                         2
+#define BGP_ATTR_NEXT_HOP                        3
+#define BGP_ATTR_MULTI_EXIT_DISC                 4
+#define BGP_ATTR_LOCAL_PREF                      5
+#define BGP_ATTR_ATOMIC_AGGREGATE                6
+#define BGP_ATTR_AGGREGATOR                      7
+#define BGP_ATTR_COMMUNITIES                     8
+#define BGP_ATTR_ORIGINATOR_ID                   9
+#define BGP_ATTR_CLUSTER_LIST                   10
+#define BGP_ATTR_DPA                            11
+#define BGP_ATTR_ADVERTISER                     12
+#define BGP_ATTR_RCID_PATH                      13
+#define BGP_ATTR_MP_REACH_NLRI                  14
+#define BGP_ATTR_MP_UNREACH_NLRI                15
+#define BGP_ATTR_EXT_COMMUNITIES                16
+#define BGP_ATTR_AS4_PATH                       17
+#define BGP_ATTR_AS4_AGGREGATOR                 18
+#define BGP_ATTR_AS_PATHLIMIT                   21
+#define BGP_ATTR_ENCAP                          23
+#define BGP_ATTR_LARGE_COMMUNITIES              32
+
+/* BGP update origin.  */
+#define BGP_ORIGIN_IGP                           0
+#define BGP_ORIGIN_EGP                           1
+#define BGP_ORIGIN_INCOMPLETE                    2
+
+/* BGP notify message codes.  */
+#define BGP_NOTIFY_HEADER_ERR                    1
+#define BGP_NOTIFY_OPEN_ERR                      2
+#define BGP_NOTIFY_UPDATE_ERR                    3
+#define BGP_NOTIFY_HOLD_ERR                      4
+#define BGP_NOTIFY_FSM_ERR                       5
+#define BGP_NOTIFY_CEASE                         6
+#define BGP_NOTIFY_CAPABILITY_ERR                7
+#define BGP_NOTIFY_MAX                          8
+
+#define BGP_NOTIFY_SUBCODE_UNSPECIFIC            0
+
+/* BGP_NOTIFY_HEADER_ERR sub codes.  */
+#define BGP_NOTIFY_HEADER_NOT_SYNC               1
+#define BGP_NOTIFY_HEADER_BAD_MESLEN             2
+#define BGP_NOTIFY_HEADER_BAD_MESTYPE            3
+#define BGP_NOTIFY_HEADER_MAX                    4
+
+/* BGP_NOTIFY_OPEN_ERR sub codes.  */
+#define BGP_NOTIFY_OPEN_UNSPECIFIC               0
+#define BGP_NOTIFY_OPEN_UNSUP_VERSION            1
+#define BGP_NOTIFY_OPEN_BAD_PEER_AS              2
+#define BGP_NOTIFY_OPEN_BAD_BGP_IDENT            3
+#define BGP_NOTIFY_OPEN_UNSUP_PARAM              4
+#define BGP_NOTIFY_OPEN_AUTH_FAILURE             5
+#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME          6
+#define BGP_NOTIFY_OPEN_UNSUP_CAPBL              7
+#define BGP_NOTIFY_OPEN_MAX                      8
+
+/* BGP_NOTIFY_UPDATE_ERR sub codes.  */
+#define BGP_NOTIFY_UPDATE_MAL_ATTR               1
+#define BGP_NOTIFY_UPDATE_UNREC_ATTR             2
+#define BGP_NOTIFY_UPDATE_MISS_ATTR              3
+#define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR          4
+#define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR          5
+#define BGP_NOTIFY_UPDATE_INVAL_ORIGIN           6
+#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP          7
+#define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP         8
+#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR           9
+#define BGP_NOTIFY_UPDATE_INVAL_NETWORK         10
+#define BGP_NOTIFY_UPDATE_MAL_AS_PATH           11
+#define BGP_NOTIFY_UPDATE_MAX                   12
+
+/* BGP_NOTIFY_CEASE sub codes (RFC 4486).  */
+#define BGP_NOTIFY_CEASE_MAX_PREFIX              1
+#define BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN          2
+#define BGP_NOTIFY_CEASE_PEER_UNCONFIG           3
+#define BGP_NOTIFY_CEASE_ADMIN_RESET             4
+#define BGP_NOTIFY_CEASE_CONNECT_REJECT          5
+#define BGP_NOTIFY_CEASE_CONFIG_CHANGE           6
+#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION    7
+#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE         8
+#define BGP_NOTIFY_CEASE_MAX                     9
+
+/* BGP_NOTIFY_CAPABILITY_ERR sub codes (draft-ietf-idr-dynamic-cap-02). */
+#define BGP_NOTIFY_CAPABILITY_INVALID_ACTION     1
+#define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH     2
+#define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE     3
+#define BGP_NOTIFY_CAPABILITY_MAX                4
+
+/* BGP finite state machine status.  */
+#define Idle                                     1
+#define Connect                                  2
+#define Active                                   3
+#define OpenSent                                 4
+#define OpenConfirm                              5
+#define Established                              6
+#define Clearing                                 7
+#define Deleted                                  8
+#define BGP_STATUS_MAX                           9
+
+/* BGP finite state machine events.  */
+#define BGP_Start                                1
+#define BGP_Stop                                 2
+#define TCP_connection_open                      3
+#define TCP_connection_closed                    4
+#define TCP_connection_open_failed               5
+#define TCP_fatal_error                          6
+#define ConnectRetry_timer_expired               7
+#define Hold_Timer_expired                       8
+#define KeepAlive_timer_expired                  9
+#define Receive_OPEN_message                    10
+#define Receive_KEEPALIVE_message               11
+#define Receive_UPDATE_message                  12
+#define Receive_NOTIFICATION_message            13
+#define Clearing_Completed                      14
+#define BGP_Stop_with_error                     15
+#define BGP_EVENTS_MAX                          16
+
+/* BGP timers default value.  */
+#define BGP_INIT_START_TIMER                     1
+#define BGP_DEFAULT_HOLDTIME                   180
+#define BGP_DEFAULT_KEEPALIVE                   60 
+#define BGP_DEFAULT_EBGP_ROUTEADV                3
+#define BGP_DEFAULT_IBGP_ROUTEADV                1
+#define BGP_DEFAULT_CONNECT_RETRY                5
+
+/* BGP default local preference.  */
+#define BGP_DEFAULT_LOCAL_PREF                 100
+
+/* BGP graceful restart  */
+#define BGP_DEFAULT_RESTART_TIME               120
+#define BGP_DEFAULT_STALEPATH_TIME             360
+
+/* RFC4364 */
+#define SAFI_MPLS_LABELED_VPN                  128
+
+/* Max TTL value.  */
+#define TTL_MAX                                255
+
+/* BGP uptime string length.  */
+#define BGP_UPTIME_LEN 25
+
+/* Default configuration settings for bgpd.  */
+#define BGP_VTY_PORT                          2605
+#define BGP_DEFAULT_CONFIG             "bgpd.conf"
+
+/* Check AS path loop when we send NLRI.  */
+/* #define BGP_SEND_ASPATH_CHECK */
+
+/* Flag for peer_clear_soft().  */
+enum bgp_clear_type
+{
+  BGP_CLEAR_SOFT_NONE,
+  BGP_CLEAR_SOFT_OUT,
+  BGP_CLEAR_SOFT_IN,
+  BGP_CLEAR_SOFT_BOTH,
+  BGP_CLEAR_SOFT_IN_ORF_PREFIX,
+  BGP_CLEAR_SOFT_RSCLIENT
+};
+
+/* Macros. */
+#define BGP_INPUT(P)         ((P)->ibuf)
+#define BGP_INPUT_PNT(P)     (STREAM_PNT(BGP_INPUT(P)))
+#define BGP_IS_VALID_STATE_FOR_NOTIF(S)\
+        (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established))
+
+/* BGP error codes.  */
+#define BGP_SUCCESS                               0
+#define BGP_ERR_INVALID_VALUE                    -1
+#define BGP_ERR_INVALID_FLAG                     -2
+#define BGP_ERR_INVALID_AS                       -3
+#define BGP_ERR_INVALID_BGP                      -4
+#define BGP_ERR_PEER_GROUP_MEMBER                -5
+#define BGP_ERR_MULTIPLE_INSTANCE_USED           -6
+#define BGP_ERR_PEER_GROUP_MEMBER_EXISTS         -7
+#define BGP_ERR_PEER_BELONGS_TO_GROUP            -8
+#define BGP_ERR_PEER_GROUP_AF_UNCONFIGURED       -9
+#define BGP_ERR_PEER_GROUP_NO_REMOTE_AS         -10
+#define BGP_ERR_PEER_GROUP_CANT_CHANGE          -11
+#define BGP_ERR_PEER_GROUP_MISMATCH             -12
+#define BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT  -13
+#define BGP_ERR_MULTIPLE_INSTANCE_NOT_SET       -14
+#define BGP_ERR_AS_MISMATCH                     -15
+#define BGP_ERR_PEER_INACTIVE                   -16
+#define BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER   -17
+#define BGP_ERR_PEER_GROUP_HAS_THE_FLAG         -18
+#define BGP_ERR_PEER_FLAG_CONFLICT              -19
+#define BGP_ERR_PEER_GROUP_SHUTDOWN             -20
+#define BGP_ERR_PEER_FILTER_CONFLICT            -21
+#define BGP_ERR_NOT_INTERNAL_PEER               -22
+#define BGP_ERR_REMOVE_PRIVATE_AS               -23
+#define BGP_ERR_AF_UNCONFIGURED                 -24
+#define BGP_ERR_SOFT_RECONFIG_UNCONFIGURED      -25
+#define BGP_ERR_INSTANCE_MISMATCH               -26
+#define BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP  -27
+#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS    -28
+#define BGP_ERR_TCPSIG_FAILED                  -29
+#define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK  -30
+#define BGP_ERR_NO_IBGP_WITH_TTLHACK           -31
+#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS    -32
+#define BGP_ERR_MAX                            -33
+
+extern struct bgp_master *bm;
+
+/* Prototypes. */
+extern void bgp_terminate (void);
+extern void bgp_reset (void);
+extern time_t bgp_clock (void);
+extern void bgp_zclient_reset (void);
+extern int bgp_nexthop_set (union sockunion *, union sockunion *, 
+                    struct bgp_nexthop *, struct peer *);
+extern struct bgp *bgp_get_default (void);
+extern struct bgp *bgp_lookup (as_t, const char *);
+extern struct bgp *bgp_lookup_by_name (const char *);
+extern struct peer *peer_lookup (struct bgp *, union sockunion *);
+extern struct peer_group *peer_group_lookup (struct bgp *, const char *);
+extern struct peer_group *peer_group_get (struct bgp *, const char *);
+extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct in_addr *,
+                                   int *);
+
+/*
+ * Peers are incredibly easy to memory leak
+ * due to the various ways that they are actually used
+ * Provide some functionality to debug locks and unlocks
+ */
+extern struct peer *peer_lock_with_caller(const char *, struct peer *);
+extern struct peer *peer_unlock_with_caller(const char *, struct peer *);
+#define peer_unlock(A) peer_unlock_with_caller(__FUNCTION__, (A))
+#define peer_lock(B) peer_lock_with_caller(__FUNCTION__, (B))
+
+extern bgp_peer_sort_t peer_sort (struct peer *peer);
+extern int peer_active (struct peer *);
+extern int peer_active_nego (struct peer *);
+extern struct peer *peer_create_accept (struct bgp *);
+extern char *peer_uptime (time_t, char *, size_t);
+extern int bgp_config_write (struct vty *);
+extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *);
+
+extern void bgp_master_init (void);
+
+extern void bgp_init (void);
+extern void bgp_route_map_init (void);
+
+extern int bgp_option_set (int);
+extern int bgp_option_unset (int);
+extern int bgp_option_check (int);
+
+extern int bgp_get (struct bgp **, as_t *, const char *);
+extern int bgp_delete (struct bgp *);
+
+extern int bgp_flag_set (struct bgp *, int);
+extern int bgp_flag_unset (struct bgp *, int);
+extern int bgp_flag_check (struct bgp *, int);
+
+extern void bgp_lock (struct bgp *);
+extern void bgp_unlock (struct bgp *);
+
+extern void bgp_router_id_zebra_bump (void);
+extern int bgp_router_id_static_set (struct bgp *, struct in_addr);
+
+extern int bgp_cluster_id_set (struct bgp *, struct in_addr *);
+extern int bgp_cluster_id_unset (struct bgp *);
+
+extern int bgp_confederation_id_set (struct bgp *, as_t);
+extern int bgp_confederation_id_unset (struct bgp *);
+extern int bgp_confederation_peers_check (struct bgp *, as_t);
+
+extern int bgp_confederation_peers_add (struct bgp *, as_t);
+extern int bgp_confederation_peers_remove (struct bgp *, as_t);
+
+extern int bgp_timers_set (struct bgp *, u_int32_t keepalive, u_int32_t holdtime);
+extern int bgp_timers_unset (struct bgp *);
+
+extern int bgp_default_local_preference_set (struct bgp *, u_int32_t);
+extern int bgp_default_local_preference_unset (struct bgp *);
+
+extern int peer_rsclient_active (struct peer *);
+
+extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, safi_t);
+extern int peer_group_remote_as (struct bgp *, const char *, as_t *);
+extern int peer_ttl (struct peer *peer);
+extern int peer_delete (struct peer *peer);
+extern int peer_group_delete (struct peer_group *);
+extern int peer_group_remote_as_delete (struct peer_group *);
+
+extern int peer_activate (struct peer *, afi_t, safi_t);
+extern int peer_deactivate (struct peer *, afi_t, safi_t);
+extern int peer_afc_set (struct peer *, afi_t, safi_t, int);
+
+extern int peer_group_bind (struct bgp *, union sockunion *, struct peer_group *,
+                    afi_t, safi_t, as_t *);
+extern int peer_group_unbind (struct bgp *, struct peer *, struct peer_group *,
+                      afi_t, safi_t);
+
+extern int peer_flag_set (struct peer *, u_int32_t);
+extern int peer_flag_unset (struct peer *, u_int32_t);
+
+extern int peer_af_flag_set (struct peer *, afi_t, safi_t, u_int32_t);
+extern int peer_af_flag_unset (struct peer *, afi_t, safi_t, u_int32_t);
+extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t);
+
+extern int peer_ebgp_multihop_set (struct peer *, int);
+
+extern int peer_description_set (struct peer *, const char *);
+extern int peer_description_unset (struct peer *);
+
+extern int peer_update_source_if_set (struct peer *, const char *);
+extern int peer_update_source_addr_set (struct peer *, const union sockunion *);
+extern int peer_update_source_unset (struct peer *);
+
+extern int peer_default_originate_set (struct peer *, afi_t, safi_t, const char *);
+extern int peer_default_originate_unset (struct peer *, afi_t, safi_t);
+
+extern int peer_port_set (struct peer *, u_int16_t);
+extern int peer_port_unset (struct peer *);
+
+extern int peer_weight_set (struct peer *, u_int16_t);
+extern int peer_weight_unset (struct peer *);
+
+extern int peer_timers_set (struct peer *, u_int32_t keepalive, u_int32_t holdtime);
+extern int peer_timers_unset (struct peer *);
+
+extern int peer_timers_connect_set (struct peer *, u_int32_t);
+extern int peer_timers_connect_unset (struct peer *);
+
+extern int peer_advertise_interval_set (struct peer *, u_int32_t);
+extern int peer_advertise_interval_unset (struct peer *);
+
+extern int peer_interface_set (struct peer *, const char *);
+extern int peer_interface_unset (struct peer *);
+
+extern int peer_distribute_set (struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_distribute_unset (struct peer *, afi_t, safi_t, int);
+
+extern int peer_allowas_in_set (struct peer *, afi_t, safi_t, int);
+extern int peer_allowas_in_unset (struct peer *, afi_t, safi_t);
+
+extern int peer_local_as_set (struct peer *, as_t, int, int);
+extern int peer_local_as_unset (struct peer *);
+
+extern int peer_prefix_list_set (struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_prefix_list_unset (struct peer *, afi_t, safi_t, int);
+
+extern int peer_aslist_set (struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_aslist_unset (struct peer *,afi_t, safi_t, int);
+
+extern int peer_route_map_set (struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_route_map_unset (struct peer *, afi_t, safi_t, int);
+
+extern int peer_unsuppress_map_set (struct peer *, afi_t, safi_t, const char *);
+
+extern int peer_password_set (struct peer *, const char *);
+extern int peer_password_unset (struct peer *);
+
+extern int peer_unsuppress_map_unset (struct peer *, afi_t, safi_t);
+
+extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_char, int, u_int16_t);
+extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t);
+
+extern int peer_clear (struct peer *);
+extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type);
+
+extern int peer_ttl_security_hops_set (struct peer *, int);
+
+extern void bgp_scan_finish (void);
+#endif /* _QUAGGA_BGPD_H */
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755 (executable)
index 0000000..c0b95d3
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# This file exists to document the proper way to initialize autotools,
+# and so that those used to the presence of bootstrap.sh or autogen.sh
+# will have an eaiser time.
+
+autoreconf -i
diff --git a/buildtest.sh b/buildtest.sh
new file mode 100755 (executable)
index 0000000..04fc2cc
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/bash
+# written 2012-2013 by David Lamparter, placed in Public Domain.
+#
+# builds some git commit of Quagga in some different configurations
+# usage: buildtest.sh [commit [configurations...]]
+
+basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd"
+
+configs_base="gcc|$basecfg"
+
+configs_ext="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology"
+configs_snmp="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology --enable-snmp"
+configs_clang="clang|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology"
+configs_icc="icc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology"
+
+defconfigs="base ext"
+net-snmp-config --version      &> /dev/null && defconfigs="$defconfigs snmp"
+clang --version                        &> /dev/null && defconfigs="$defconfigs clang"
+icc --version                  &> /dev/null && defconfigs="$defconfigs icc"
+
+echo "enabled configurations: $defconfigs"
+
+cc_gcc="CC=gcc; export CC"
+cc_clang="CC=clang; export CC"
+cc_icc="CC=icc; export CC"
+
+###############################
+
+errfunc() {
+       echo "something went wrong! check $TEMP"
+       exit 1
+}
+
+set -e
+trap errfunc ERR
+
+COMMITREF="$1"
+COMMITISH="`git rev-list --max-count=1 ${COMMITREF:-HEAD}`"
+TEMP="`mktemp -t -d quaggabuild.XXXXXX`"
+BASE="`pwd`"
+CONFIGS="$2"
+
+echo using temporary directory: $TEMP
+echo git commit used:
+git --no-pager log -n 1 --pretty=oneline "$COMMITISH"
+
+cd "$TEMP"
+git clone "$BASE" "source"
+cd "source"
+git checkout -b build "$COMMITISH"
+git clean -d -f -x
+sh bootstrap.sh
+
+cd ..
+
+echo -e "\n\n\n\n\033[33;1mmaking dist tarball\033[m"
+
+mkdir build_dist
+cd build_dist
+../source/configure
+make distdir=sdist dist-gzip
+cd ..
+tar zxvf build_dist/sdist.tar.gz
+
+for cfg in ${CONFIGS:-$defconfigs}; do
+       echo -e "\n\n\n\n\033[33;1mbuilding configuration $cfg\033[m"
+       config="\${configs_$cfg}"
+       eval "config=$config"
+
+       cc="${config%%|*}"
+       args="${config#*|}"
+
+       ccset="\${cc_$cc}"
+       eval "ccset=$ccset"
+       eval "$ccset"
+
+       bdir="build_$cfg"
+       mkdir "$bdir"
+       cd "$bdir"
+       ../sdist/configure $args
+       make -j5
+       make check
+       make DESTDIR="$TEMP/inst_$cfg" install
+       cd ..
+done
+
+echo -e "\n\n\n\neverything seems ok. you may now\n\trm -rf $TEMP"
diff --git a/common.am b/common.am
new file mode 100644 (file)
index 0000000..ac7a323
--- /dev/null
+++ b/common.am
@@ -0,0 +1,41 @@
+#
+# Automake fragment intended to be shared by Makefile.am files in the
+# tree.
+#
+
+if HAVE_PROTOBUF
+
+# Uncomment to use an non-system version of libprotobuf-c.
+#
+# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src
+# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la
+
+Q_PROTOBUF_C_CLIENT_INCLUDES=
+Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c
+
+Q_PROTOC=protoc
+Q_PROTOC_C=protoc-c
+
+Q_PROTOBUF_CFILES = $(filter %.pb-c.c,$(SOURCES))
+
+Q_PROTOBUF_SRCS = $(Q_PROTOBUF_CFILES) $(Q_PROTOBUF_HFILES)
+
+# Rules
+%.pb.h: %.proto
+       $(Q_PROTOC) $(PROTOBUF_INCLUDES) --cpp_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^
+
+%.pb-c.c %.pb-c.h: %.proto
+       $(Q_PROTOC_C) $(PROTOBUF_INCLUDES) --c_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^
+
+#
+# Information about how to link to various libraries.
+#
+Q_QUAGGA_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libquagga_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS)
+
+Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfpm_pb.la $(Q_QUAGGA_PB_CLIENT_LDOPTS)
+
+endif  # HAVE_PROTOBUF
+
+Q_CLEANFILES = $(Q_PROTOBUF_SRCS)
+
+Q_BUILT_SRCS = $(Q_PROTOBUF_SRCS)
diff --git a/configure.ac b/configure.ac
new file mode 100755 (executable)
index 0000000..ae292f1
--- /dev/null
@@ -0,0 +1,1713 @@
+##
+## Configure template file for Quagga.
+## autoconf will generate configure script.
+##
+##  Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+##  Portions Copyright (c) 2003 Paul Jakma <paul@dishone.st>
+##
+AC_PREREQ(2.60)
+
+AC_INIT(Quagga, 1.2.2, [https://bugzilla.quagga.net])
+CONFIG_ARGS="$*"
+AC_SUBST(CONFIG_ARGS)
+AC_CONFIG_SRCDIR(lib/zebra.h)
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl -----------------------------------
+dnl Get hostname and other information.
+dnl -----------------------------------
+AC_CANONICAL_BUILD()
+AC_CANONICAL_HOST()
+AC_CANONICAL_TARGET()
+
+# Disable portability warnings -- our automake code (in particular
+# common.am) uses some constructs specific to gmake.
+AM_INIT_AUTOMAKE([1.6 -Wno-portability])
+
+m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])
+AM_SILENT_RULES([yes])
+AC_CONFIG_HEADERS(config.h)
+
+AC_PATH_PROG(PERL, perl)
+AC_CHECK_PROG([GAWK],[gawk],[gawk],[not-in-PATH])
+if test "x$GAWK" = "xnot-in-PATH" ; then
+       AC_MSG_ERROR([GNU awk is required for lib/memtype.h made by memtypes.awk.
+BSD awk complains: awk: gensub doesn't support backreferences (subst "\1") ])
+fi
+AC_ARG_VAR([GAWK],[GNU AWK])
+
+dnl default is to match previous behavior
+exampledir=${sysconfdir}
+AC_ARG_ENABLE([exampledir],
+         AS_HELP_STRING([--enable-exampledir],
+                        [specify alternate directory for examples]),
+                        exampledir="$enableval",)
+dnl XXX add --exampledir to autoconf standard directory list somehow
+AC_SUBST(exampledir)
+
+dnl default is to match previous behavior
+pkgsrcrcdir=""
+pkgsrcdir=""
+AC_ARG_ENABLE([pkgsrcrcdir],
+         AS_HELP_STRING([--enable-pkgsrcrcdir],
+                        [specify directory for rc.d scripts]),
+                        pkgsrcrcdir="$enableval"; pkgsrcdir="pkgsrc",)
+dnl XXX add --pkgsrcrcdir to autoconf standard directory list somehow
+AC_SUBST(pkgsrcdir)
+AC_SUBST(pkgsrcrcdir)
+
+dnl ------------
+dnl Check CFLAGS
+dnl ------------
+AC_ARG_WITH(cflags,
+[  --with-cflags           Set CFLAGS for use in compilation.])
+if test "x$with_cflags" != "x" ; then
+  CFLAGS="$with_cflags" ; cflags_specified=yes ;
+elif test -n "$CFLAGS" ; then
+  cflags_specified=yes ;
+fi
+
+dnl --------------------
+dnl Check CC and friends
+dnl --------------------
+AC_LANG([C])
+AC_PROG_CC
+AC_PROG_CPP
+AM_PROG_CC_C_O
+AC_PROG_RANLIB
+AC_PROG_EGREP
+PKG_PROG_PKG_CONFIG
+AC_PROG_CC_C99
+
+dnl libtool prereq.
+AC_USE_SYSTEM_EXTENSIONS
+
+dnl -------
+dnl libtool
+dnl -------
+LT_INIT
+
+dnl create libtool now, so we can test version below for
+dnl fstack-protector-strong
+LT_OUTPUT
+
+dnl autoconf 2.59 appears not to support AC_PROG_SED
+dnl AC_PROG_SED
+AC_CHECK_PROG([SED],[sed],[sed],[/bin/false])
+
+dnl Check for pdflatex and latexmk, in case someone wants to build
+dnl PDFs from TeX (as used to be case for HACKING)
+AC_CHECK_PROG([PDFLATEX],[pdflatex],[pdflatex],[/bin/false])
+AC_CHECK_PROG([LATEXMK],[latexmk],[latexmk],[/bin/false])
+if test "x$PDFLATEX" = "x/bin/false" -o "x$LATEXMK" = "x/bin/false"; then
+       AC_MSG_WARN([Will not be able to make PDF versions of TeX documents])
+else
+       HAVE_LATEX=true
+fi
+AM_CONDITIONAL([HAVE_LATEX], [test "x$HAVE_LATEX" = "xtrue"])
+dnl for making HACKING.pdf from HACKING.md using pandoc
+AC_CHECK_PROG([PANDOC],[pandoc],[pandoc],[/bin/false])
+if test "x$PDFLATEX" = "x/bin/false" -o "x$PANDOC" = "x/bin/false"; then
+       AC_MSG_WARN([Will not be able to make PDF versions of MD documents])
+else
+       HAVE_PANDOC=true
+fi
+AM_CONDITIONAL([HAVE_PANDOC], [test "x$HAVE_PANDOC" = "xtrue"])
+
+if test "x${GCC}" != "xyes" ; then
+  AC_MSG_CHECKING([whether we are using SunPro compiler])
+  AC_EGREP_CPP([^__SUNPRO_C.*0x5(7|8|9)], ["__SUNPRO_C" __SUNPRO_C],
+      [AC_MSG_RESULT([no])],
+      [COMPILER="SUNPRO"
+       AC_MSG_RESULT([yes])]
+  )
+fi
+
+dnl ---------------------------------------------
+dnl If CLFAGS doesn\'t exist set default value
+dnl AC_PROG_CC will have set minimal default
+dnl already, eg "-O2 -g" for gcc, "-g" for others
+dnl (Wall is gcc specific... have to make sure
+dnl  gcc is being used before setting it)
+dnl
+dnl Sun Studio 10 / SunPro 5.7 is also supported,
+dnl so lets set some sane CFLAGS for it.
+dnl ---------------------------------------------
+
+AC_DEFUN([AC_C_FLAG], [{
+       AC_LANG_PUSH(C)
+       ac_c_flag_save="$CFLAGS"
+       CFLAGS="$CFLAGS $1"
+       AC_MSG_CHECKING([[whether $CC supports $1]])
+       AC_COMPILE_IFELSE(
+               [AC_LANG_PROGRAM([[]])],
+               [
+                       AC_MSG_RESULT([yes])
+                       m4_if([$3], [], [], [
+                               CFLAGS="$ac_c_flag_save"
+                               $3
+                       ])
+               ], [
+                       CFLAGS="$ac_c_flag_save"
+                       AC_MSG_RESULT([no])
+                       $2
+               ])
+       AC_LANG_POP(C)
+       }])
+
+AC_MSG_CHECKING([which default CFLAGS to set])
+if test "x${cflags_specified}" = "x" ; then
+  case ${COMPILER} in
+    "SUNPRO")
+        AC_MSG_RESULT([Sun Studio])
+        AC_C_FLAG([-g])
+        AC_C_FLAG([-xO4])
+        AC_C_FLAG([-xspace])
+        AC_C_FLAG([-xstrconst])
+        AC_C_FLAG([-xc99])
+        AC_C_FLAG([-errfmt])
+        AC_C_FLAG([-xipo])
+       dnl AC_C_FLAG([-xlinkopt=2]) SPARC only dnl
+       dnl AC_C_FLAG([-xcode=pic32])dnl        
+        ;;
+    *)
+        AC_MSG_RESULT([autodetecting])
+
+        AC_C_FLAG([-diag-error 10006])
+        AC_C_FLAG([-g])
+        AC_C_FLAG([-Os], [
+          AC_C_FLAG([-O2])
+        ])
+        dnl fstack-protector-strong gives __stack_chk_fail_local
+        dnl being an 'Undefined symbol' on OpenIndiana hipster, with gcc 6.
+        dnl gcc -shared is being used to do the link, however the error is
+        dnl from ld. Disable. An issue with libtool < 2.4.6 dropping the
+        dnl -fstack-protector-strong argument from the shared link.
+        AC_MSG_CHECKING([whether libtool can support fstack-protector])
+        if test x"$(./libtool --version \
+                   | awk -F '[[ \t.]]' \
+                       'NR == 1 { \
+                               if ($(NF-2) <= 2 && $(NF-1) <= 4 && $NF < 6) \
+                                       print 0; \
+                               else print 1 \
+                       }')" = "x1" ; then
+               AC_MSG_RESULT([yes])
+               AC_C_FLAG([-fstack-protector-strong])
+               AC_C_FLAG([--param=ssp-buffer-size=4])
+        else
+               AC_MSG_RESULT([no])
+               AC_MSG_WARN([upgrade to libtool >= 2.4.6!])
+        fi
+        AC_C_FLAG([-D_FORTIFY_SOURCE=2])
+        AC_C_FLAG([-Wformat])
+        AC_C_FLAG([-Wformat-security])
+        AC_C_FLAG([-fpie])
+        AC_C_FLAG([-fno-omit-frame-pointer])
+        AC_C_FLAG([-Wall])
+        AC_C_FLAG([-Wextra])
+        AC_C_FLAG([-Wmissing-prototypes])
+        AC_C_FLAG([-Wmissing-declarations])
+        AC_C_FLAG([-Wpointer-arith])
+        AC_C_FLAG([-Wbad-function-cast])
+        AC_C_FLAG([-Wwrite-strings])
+        if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then
+          AC_C_FLAG([-Wcast-qual])
+          AC_C_FLAG([-Wstrict-prototypes])
+          AC_C_FLAG([-Wmissing-noreturn])
+          AC_C_FLAG([-Wmissing-format-attribute])
+          AC_C_FLAG([-Wunreachable-code])
+          AC_C_FLAG([-Wpacked])
+          AC_C_FLAG([-Wpadded])
+        else
+          AC_C_FLAG([-Wno-unused-result])
+        fi
+        AC_C_FLAG([-Wno-unused-parameter])
+        AC_C_FLAG([-Wno-missing-field-initializers])
+        # ICC emits a broken warning for const char *x = a ? "b" : "c";
+        # for some reason the string consts get 'promoted' to char *,
+        # triggering a const to non-const conversion warning.
+        AC_C_FLAG([-diag-disable 3179])
+        ;;
+  esac
+else
+  AC_MSG_RESULT([CFLAGS supplied by user])
+fi
+
+if test x"${enable_werror}" = x"yes" ; then
+  WERROR="-Werror"
+fi
+AC_SUBST(WERROR)
+
+dnl --------------
+dnl Check programs
+dnl --------------
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_CHECK_TOOL(AR, ar)
+
+dnl ---------------------------
+dnl We, perhaps unfortunately,
+dnl depend on GNU Make specific
+dnl constructs.
+dnl Give the user a warning if
+dnl not GNU Make.
+dnl ---------------------------
+AC_CACHE_CHECK([if ${MAKE-make} is GNU make], [quagga_cv_gnu_make],
+       [quagga_cv_gnu_make=no
+        if ${MAKE-make} --version 2>/dev/null | \
+               grep '^GNU Make ' >/dev/null ; then
+               quagga_cv_gnu_make=yes;
+        fi
+       ]
+)
+
+dnl -----------------
+dnl System extensions
+dnl -----------------
+AC_GNU_SOURCE
+
+
+dnl ----------------------
+dnl Packages configuration
+dnl ----------------------
+AC_ARG_WITH(pkg-extra-version,
+       AS_HELP_STRING([--with-pkg-extra-version=VER], [add extra version field, for packagers/distributions]),
+       [EXTRAVERSION=$withval],)
+AC_ARG_WITH(pkg-git-version,
+       AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]),
+       [ test "x$withval" != "xno" && with_pkg_git_version="yes" ])
+AC_ARG_ENABLE(vtysh,
+  AS_HELP_STRING([--disable-vtysh], [do not build integrated vty shell for Quagga]))
+AC_ARG_ENABLE(doc,
+  AS_HELP_STRING([--disable-doc], [do not build docs]))
+AC_ARG_ENABLE(zebra,
+  AS_HELP_STRING([--disable-zebra], [do not build zebra daemon]))
+AC_ARG_ENABLE(bgpd,
+  AS_HELP_STRING([--disable-bgpd], [do not build bgpd]))
+AC_ARG_ENABLE(ripd,
+  AS_HELP_STRING([--disable-ripd], [do not build ripd]))
+AC_ARG_ENABLE(ripngd,
+  AS_HELP_STRING([--disable-ripngd], [do not build ripngd]))
+AC_ARG_ENABLE(ospfd,
+  AS_HELP_STRING([--disable-ospfd], [do not build ospfd]))
+AC_ARG_ENABLE(ospf6d,
+  AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d]))
+AC_ARG_ENABLE(nhrpd,
+  AS_HELP_STRING([--disable-nhrpd], [do not build nhrpd]))
+AC_ARG_ENABLE(watchquagga,
+  AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga]))
+AC_ARG_ENABLE(isisd,
+  AS_HELP_STRING([--disable-isisd], [do not build isisd]))
+AC_ARG_ENABLE(pimd,
+  AS_HELP_STRING([--disable-pimd], [do not build pimd]))
+AC_ARG_ENABLE(bgp-announce,
+  AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))
+AC_ARG_ENABLE(snmp,
+  AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)]))
+AC_ARG_WITH(libpam,
+  AS_HELP_STRING([--with-libpam], [use libpam for PAM support in vtysh]))
+AC_ARG_ENABLE(tcp-zebra,
+  AS_HELP_STRING([--enable-tcp-zebra], [enable TCP/IP socket connection between zebra and protocol daemon]))
+AC_ARG_ENABLE(ospfapi,
+  AS_HELP_STRING([--disable-ospfapi], [do not build OSPFAPI to access the OSPF LSA Database]))
+AC_ARG_ENABLE(ospfclient,
+  AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI,
+                          (this is the default if --disable-ospfapi is set)]))
+AC_ARG_ENABLE(multipath,
+  AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit]))
+AC_ARG_ENABLE(user,
+  AS_HELP_STRING([--enable-user=USER], [user to run Quagga suite as (default quagga)]))
+AC_ARG_ENABLE(group,
+  AS_HELP_STRING([--enable-group=GROUP], [group to run Quagga suite as (default quagga)]))
+AC_ARG_ENABLE(vty_group,
+  AS_HELP_STRING([--enable-vty-group=ARG], [set vty sockets to have specified group as owner]))
+AC_ARG_ENABLE(configfile_mask,
+  AS_HELP_STRING([--enable-configfile-mask=ARG], [set mask for config files]))
+AC_ARG_ENABLE(logfile_mask,
+  AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files]))
+
+AC_ARG_ENABLE(rtadv,
+  AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature]))
+AC_ARG_ENABLE(irdp,
+  AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra]))
+AC_ARG_ENABLE(isis_topology,
+  AS_HELP_STRING([--enable-isis-topology], [enable IS-IS topology generator]))
+AC_ARG_ENABLE(capabilities,
+  AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities]))
+AC_ARG_ENABLE(rusage,
+  AS_HELP_STRING([--disable-rusage], [disable using getrusage]))
+AC_ARG_ENABLE(gcc_ultra_verbose,
+  AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings]))
+AC_ARG_ENABLE(linux24_tcp_md5,
+  AS_HELP_STRING([--enable-linux24-tcp-md5], [enable support for old, Linux-2.4 RFC2385 patch]))
+AC_ARG_ENABLE(gcc-rdynamic,
+  AS_HELP_STRING([--enable-gcc-rdynamic], [enable linking with -rdynamic for better backtraces (default if gcc)]))
+AC_ARG_ENABLE(backtrace,
+  AS_HELP_STRING([--disable-backtrace,], [disable crash backtraces (default autodetect)]))
+AC_ARG_ENABLE(time-check,
+  AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages]))
+AC_ARG_ENABLE(pcreposix,
+  AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions]))
+AC_ARG_ENABLE(fpm,
+  AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support]))
+AC_ARG_ENABLE(werror,
+  AS_HELP_STRING([--enable-werror], [enable -Werror (recommended for developers only)]))
+AC_ARG_ENABLE([protobuf],
+  AS_HELP_STRING([--enable-protobuf], [Enable experimental protobuf support]))
+
+AC_ARG_ENABLE([dev_build],
+    AS_HELP_STRING([--enable-dev-build], [build for development]))
+
+if test x"${enable_gcc_rdynamic}" != x"no" ; then
+  if test x"${enable_gcc_rdynamic}" = x"yes" -o x"$COMPILER" = x"GCC"; then
+    LDFLAGS="${LDFLAGS} -rdynamic"
+  fi
+fi
+
+if test x"${enable_time_check}" != x"no" ; then
+  if test x"${enable_time_check}" = x"yes" -o x"${enable_time_check}" = x ; then
+    AC_DEFINE(CONSUMED_TIME_CHECK,5000000,Consumed Time Check)
+  else
+    AC_DEFINE_UNQUOTED(CONSUMED_TIME_CHECK,$enable_time_check,Consumed Time Check)
+  fi
+fi
+
+if test "${enable_fpm}" = "yes"; then
+   AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support)
+fi
+
+if test "x${enable_dev_build}" = "xyes"; then
+   AC_DEFINE(DEV_BUILD,,Build for development)
+fi
+AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"])
+
+#
+# Logic for protobuf support.
+#
+if test "$enable_protobuf" = "yes"; then
+   have_protobuf=yes
+
+   # Check for protoc-c
+   AC_CHECK_PROG([PROTOC_C], [protoc-c], [protoc-c], [/bin/false])
+   if test "x$PROTOC_C" = "x/bin/false"; then
+      have_protobuf=no
+   else
+      found_protobuf_c=no
+      PKG_CHECK_MODULES([PROTOBUF_C], libprotobuf-c >= 0.14,
+                     [found_protobuf_c=yes],
+                     [AC_MSG_RESULT([pkg-config did not find libprotobuf-c])])
+
+      if test "x$found_protobuf_c" = "xyes"; then
+         LDFLAGS="$LDFLAGS $PROTOBUF_C_LIBS"
+         CFLAGS="$CFLAGS $PROTOBUF_C_CFLAGS"
+      else
+        AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [],
+                        [have_protobuf=no; AC_MSG_RESULT([Couldn't find google/protobuf-c.h])])
+      fi
+   fi
+fi
+
+# Fail if the user explicity enabled protobuf support and we couldn't
+# find the compiler or libraries.
+if test "x$have_protobuf" = "xno" && test "x$enable_protobuf" = "xyes"; then
+   AC_MSG_ERROR([Protobuf enabled explicitly but can't find libraries/tools])
+fi
+
+if test "x$have_protobuf" = "xyes"; then
+   AC_DEFINE(HAVE_PROTOBUF,, protobuf)
+fi
+
+AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$have_protobuf" = "xyes"])
+
+#
+# End of logic for protobuf support.
+#
+
+if test "${enable_tcp_zebra}" = "yes"; then
+  AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication)
+fi
+
+if test "${enable_linux24_tcp_md5}" = "yes"; then
+  AC_DEFINE(HAVE_TCP_MD5_LINUX24,,Old Linux 2.4 TCP MD5 Signature Patch)
+fi
+
+AC_MSG_CHECKING(if zebra should be configurable to send Route Advertisements)
+if test "${enable_rtadv}" != "no"; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_RTADV,,Enable IPv6 Routing Advertisement support)
+else
+  AC_MSG_RESULT(no)
+fi
+
+if test "${enable_irdp}" = "yes"; then
+  AC_DEFINE(HAVE_IRDP,, IRDP )
+fi
+
+if test "${enable_isisd}" != "no" && test "${enable_isis_topology}" = yes; then
+  AC_DEFINE(TOPOLOGY_GENERATE,,Enable IS-IS topology generator code)
+  ISIS_TOPOLOGY_INCLUDES="-I\$(srcdir)/topology"
+  ISIS_TOPOLOGY_DIR="topology"
+  ISIS_TOPOLOGY_LIB="./topology/libtopology.a"
+fi
+
+AC_SUBST(ISIS_TOPOLOGY_INCLUDES)
+AC_SUBST(ISIS_TOPOLOGY_DIR)
+AC_SUBST(ISIS_TOPOLOGY_LIB)
+
+if test x"${enable_user}" = x"no"; then
+  enable_user=""
+else
+  if test x"${enable_user}" = x"yes" || test x"${enable_user}" = x""; then
+    enable_user="quagga"
+  fi
+  AC_DEFINE_UNQUOTED(QUAGGA_USER, "${enable_user}", Quagga User)
+fi
+
+if test x"${enable_group}" = x"no"; then
+  enable_group=""
+else
+  if test x"${enable_group}" = x"yes" || test x"${enable_group}" = x""; then
+    enable_group="quagga"
+  fi
+  AC_DEFINE_UNQUOTED(QUAGGA_GROUP, "${enable_group}", Quagga Group)
+fi
+
+if test x"${enable_vty_group}" = x"yes" ; then
+  AC_MSG_ERROR([--enable-vty-group requires a group as argument, not yes])
+elif test x"${enable_vty_group}" != x""; then
+  if test x"${enable_vty_group}" != x"no"; then
+    AC_DEFINE_UNQUOTED(VTY_GROUP, "${enable_vty_group}", VTY Sockets Group)
+  fi
+fi
+AC_SUBST([enable_user])
+AC_SUBST([enable_group])
+AC_SUBST([enable_vty_group])
+
+enable_configfile_mask=${enable_configfile_mask:-0600}
+AC_DEFINE_UNQUOTED(CONFIGFILE_MASK, ${enable_configfile_mask}, Mask for config files)
+
+enable_logfile_mask=${enable_logfile_mask:-0600}
+AC_DEFINE_UNQUOTED(LOGFILE_MASK, ${enable_logfile_mask}, Mask for log files)
+
+MPATH_NUM=1
+
+case "${enable_multipath}" in
+  0)
+    MPATH_NUM=64
+    ;;
+  [[1-9]|[1-9][0-9]|[1-9][0-9][0-9]])
+    MPATH_NUM="${enable_multipath}"
+    ;;
+  "")
+    ;;
+  *)           
+    AC_MSG_FAILURE([Please specify digit to enable multipath ARG])
+    ;;
+esac
+
+AC_DEFINE_UNQUOTED(MULTIPATH_NUM, $MPATH_NUM, Maximum number of paths for a route)
+
+dnl -----------------------------------
+dnl Add extra version string to package
+dnl name, string and version fields.
+dnl -----------------------------------
+if test "x${EXTRAVERSION}" != "x" ; then
+  VERSION="${VERSION}${EXTRAVERSION}"
+  PACKAGE_VERSION="${PACKAGE_VERSION}${EXTRAVERSION}"
+  PACKAGE_STRING="${PACKAGE_STRING}${EXTRAVERSION}"
+fi
+
+if test "x$with_pkg_git_version" = "xyes"; then
+       if test -d "${srcdir}/.git"; then
+               AC_DEFINE(GIT_VERSION, [1], [include git version info])
+       else    with_pkg_git_version="no"
+               AC_MSG_WARN([--with-pkg-git-version given, but this is not a git checkout])
+       fi
+fi
+AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" = "xyes"])
+
+dnl ------------------------------------
+dnl Check C keywords and standard  types
+dnl ------------------------------------
+AC_C_CONST
+AC_C_INLINE
+AC_C_RESTRICT
+AC_C_VOLATILE
+AC_HEADER_STDC
+AC_HEADER_TIME
+AC_HEADER_SYS_WAIT
+AC_HEADER_STDBOOL
+dnl AC_TYPE_PID_T
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+
+dnl -------------------------
+dnl Check other header files.
+dnl -------------------------
+AC_CHECK_HEADERS([stropts.h sys/ksym.h sys/times.h sys/select.h \
+       sys/types.h linux/version.h netdb.h asm/types.h \
+       sys/cdefs.h sys/param.h limits.h signal.h \
+       sys/socket.h netinet/in.h time.h sys/time.h features.h])
+
+dnl Utility macro to avoid retyping includes all the time
+m4_define([QUAGGA_INCLUDES],
+[#ifdef SUNOS_5
+#define _XPG4_2
+#define __EXTENSIONS__
+#endif
+#include <stdio.h>
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+/* sys/conf.h depends on param.h on FBSD at least */
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+/* Required for MAXSIG */
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#ifdef __APPLE__
+# define __APPLE_USE_RFC_3542
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif /* TIME_WITH_SYS_TIME */
+])dnl
+
+dnl HAVE_NET_IF_H must be discovered by the time the longer AC_CHECK_HEADERS
+dnl round below execution begins, otherwise it doesn't properly detect
+dnl HAVE_NETINET6_IN6_VAR_H, HAVE_NET_IF_VAR_H and HAVE_STRUCT_IN6_ALIASREQ
+dnl on FreeBSD (BZ#408).
+
+AC_CHECK_HEADERS([net/if.h], [], [], QUAGGA_INCLUDES)
+
+m4_define([QUAGGA_INCLUDES],
+QUAGGA_INCLUDES
+[#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+])dnl
+
+dnl Same applies for HAVE_NET_IF_VAR_H, which HAVE_NETINET6_ND6_H and
+dnl HAVE_NETINET_IN_VAR_H depend upon. But if_var.h depends on if.h, hence
+dnl an additional round for it.
+
+AC_CHECK_HEADERS([net/if_var.h], [], [], QUAGGA_INCLUDES)
+
+m4_define([QUAGGA_INCLUDES],
+QUAGGA_INCLUDES
+[#if HAVE_NET_IF_VAR_H
+# include <net/if_var.h>
+#endif
+])dnl
+
+AC_CHECK_HEADERS([sys/un.h netinet/in_systm.h netinet/in_var.h \
+       net/if_dl.h net/netopt.h net/route.h \
+       inet/nd.h arpa/inet.h netinet/ip_icmp.h \
+       fcntl.h stddef.h sys/ioctl.h syslog.h wchar.h wctype.h \
+       sys/sysctl.h sys/sockio.h kvm.h sys/conf.h],
+       [], [], QUAGGA_INCLUDES)
+
+AC_CHECK_HEADERS([ucontext.h], [], [],
+[#ifndef __USE_GNU
+#define __USE_GNU
+#endif /* __USE_GNU */
+QUAGGA_INCLUDES
+])
+
+m4_define([UCONTEXT_INCLUDES],
+[#include <ucontext.h>])dnl
+
+AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.uc_regs],
+  [], [], [UCONTEXT_INCLUDES])
+AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs],
+  [AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs.nip],
+   [], [], [UCONTEXT_INCLUDES])],
+  [], [UCONTEXT_INCLUDES])
+AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.gregs],
+  [], [], [UCONTEXT_INCLUDES])
+
+m4_define([QUAGGA_INCLUDES],
+QUAGGA_INCLUDES
+[#if HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_VAR_H
+# include <netinet/in_var.h>
+#endif
+#if HAVE_NET_IF_DL_H
+# include <net/if_dl.h>
+#endif
+#if HAVE_NET_NETOPT_H
+# include <net/netopt.h>
+#endif
+#if HAVE_NET_ROUTE_H
+# include <net/route.h>
+#endif
+#if HAVE_INET_ND_H
+# include <inet/nd.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+/* Required for IDRP */
+#if HAVE_NETINET_IP_ICMP_H
+# include <netinet/ip_icmp.h>
+#endif
+])dnl
+
+dnl V6 headers are checked below, after we check for v6
+
+dnl Some systems (Solaris 2.x) require libnsl (Network Services Library)
+case "$host" in
+  [*-sunos5.[6-7]*] | [*-solaris2.[6-7]*])
+      opsys=sol2-6
+      AC_DEFINE(SUNOS_56, 1, SunOS 5.6 to 5.7)
+      AC_DEFINE(SUNOS_5, 1, SunOS 5)
+      AC_CHECK_LIB(xnet, main)
+      CURSES=-lcurses
+      SOLARIS="solaris"
+  ;;
+  [*-sunos5.[8-9]] \
+  | [*-sunos5.1[0-9]] \
+  | [*-sunos5.1[0-9].[0-9]] \
+  | [*-solaris2.[8-9]] \
+  | [*-solaris2.1[0-9]] \
+  | [*-solaris2.1[0-9].[0-9]])
+      opsys=sol8
+      AC_DEFINE(SUNOS_59, 1, [SunOS 5.8 up])
+      AC_DEFINE(SUNOS_5, 1, [SunOS 5])
+      AC_CHECK_LIB(socket, main)
+      AC_CHECK_LIB(nsl, main)
+      AC_CHECK_LIB(umem, main)
+      AC_CHECK_FUNCS([printstack],
+       [AC_DEFINE([HAVE_PRINTSTACK],1,[Solaris printstack])
+        AC_DEFINE([HAVE_STACK_TRACE],1,[Stack symbols decode functionality])
+       ])
+      CURSES=-lcurses
+      SOLARIS="solaris"
+  ;;
+  *-sunos5* | *-solaris2*)
+      AC_DEFINE(SUNOS_5,,SunOS 5, Unknown SunOS)
+      AC_CHECK_LIB(socket, main)
+      AC_CHECK_LIB(nsl, main)
+      CURSES=-lcurses
+      SOLARIS="solaris"
+  ;;
+  *-linux*)
+      opsys=gnu-linux
+      AC_DEFINE(GNU_LINUX,,GNU Linux)
+  ;;
+  *-openbsd*)
+      opsys=openbsd
+      AC_DEFINE(OPEN_BSD,,OpenBSD)
+  ;;
+esac
+
+AC_SYS_LARGEFILE
+
+dnl ---------------------
+dnl Integrated VTY option
+dnl ---------------------
+case "${enable_vtysh}" in
+  "no") VTYSH="";;
+  *)    VTYSH="vtysh";
+         AC_DEFINE(VTYSH,,VTY shell)
+dnl     Vtysh uses libreadline, which looks for termcap functions at
+dnl     configure time.  We follow readlines search order.
+dnl     The required procedures are in libtermcap on NetBSD, in
+dnl     [TODO] on Linux, and in [TODO] on Solaris.
+        AC_CHECK_LIB(termcap, tputs, LIBREADLINE="$LIBREADLINE -ltermcap",
+          [AC_CHECK_LIB(tinfo, tputs, LIBREADLINE="$LIBREADLINE -ltinfo",
+            [AC_CHECK_LIB(curses, tputs, LIBREADLINE="$LIBREADLINE -lcurses",
+              [AC_CHECK_LIB(ncurses, tputs, 
+                            LIBREADLINE="$LIBREADLINE -lncurses")]
+             )]
+           )]
+         )
+         AC_CHECK_LIB(readline, main, LIBREADLINE="-lreadline $LIBREADLINE",,
+                      "$LIBREADLINE")
+         if test $ac_cv_lib_readline_main = no; then
+           AC_MSG_ERROR([vtysh needs libreadline but was not found and usable on your system.])
+         fi
+        AC_CHECK_HEADER(readline/history.h)
+        if test $ac_cv_header_readline_history_h = no;then
+           AC_MSG_ERROR([readline is too old to have readline/history.h, please update to the latest readline library.])
+        fi
+        AC_CHECK_LIB(readline, rl_completion_matches, 
+                     LIBREADLINE="$LIBREADLINE",, "$LIBREADLINE")
+         if test $ac_cv_lib_readline_rl_completion_matches = no; then
+           AC_DEFINE(rl_completion_matches,completion_matches,Old readline)
+        fi
+        ;;
+  "no" ) VTYSH="";;
+esac
+AC_SUBST(LIBREADLINE)
+AM_CONDITIONAL(VTYSH, test "x$VTYSH" = "xvtysh")
+
+dnl ----------
+dnl PAM module
+dnl
+dnl Quagga detects the PAM library it is built against by checking for a
+dnl functional pam_misc.h (Linux-PAM) or openpam.h (OpenPAM) header. pam_misc.h
+dnl is known to #include pam_appl.h, the standard header of a PAM library, and
+dnl openpam.h doesn't do that, although depends on the header too. Hence a
+dnl little assistance to AC_CHECK_HEADER is necessary for the proper detection
+dnl of OpenPAM.
+dnl ----------
+if test "$with_libpam" = "yes"; then
+  AC_CHECK_HEADER([security/pam_misc.h],
+    [AC_DEFINE(HAVE_PAM_MISC_H,,Have pam_misc.h)
+     AC_DEFINE(PAM_CONV_FUNC,misc_conv,Have misc_conv)
+     pam_conv_func="misc_conv"
+    ],
+    [], QUAGGA_INCLUDES)
+  AC_CHECK_HEADER([security/openpam.h],
+    [AC_DEFINE(HAVE_OPENPAM_H,,Have openpam.h)
+     AC_DEFINE(PAM_CONV_FUNC,openpam_ttyconv,Have openpam_ttyconv)
+     pam_conv_func="openpam_ttyconv"
+    ],
+    [], QUAGGA_INCLUDES[#include <security/pam_appl.h>])
+  if test -z "$ac_cv_header_security_pam_misc_h$ac_cv_header_security_openpam_h" ; then
+    AC_MSG_WARN([*** pam support will not be built ***])
+    with_libpam="no"
+  fi
+fi
+
+if test "$with_libpam" = "yes"; then
+dnl took this test from proftpds configure.in and suited to our needs
+dnl -------------------------------------------------------------------------
+dnl
+dnl This next check looks funky due to a linker problem with some versions
+dnl of the PAM library.  Prior to 0.72 release, the Linux PAM shared library
+dnl omitted requiring libdl linking information. PAM-0.72 or better ships
+dnl with RedHat 6.2 and Debian 2.2 or better.
+AC_CHECK_LIB(pam, pam_start,
+  [AC_CHECK_LIB(pam, $pam_conv_func,
+    [AC_DEFINE(USE_PAM,,Use PAM for authentication)
+     LIBPAM="-lpam"],
+    [AC_DEFINE(USE_PAM,,Use PAM for authentication)
+     LIBPAM="-lpam -lpam_misc"]
+    )
+  ],
+
+  [AC_CHECK_LIB(pam, pam_end,
+    [AC_CHECK_LIB(pam, $pam_conv_func,
+      [AC_DEFINE(USE_PAM,,Use PAM for authentication)
+       LIBPAM="-lpam -ldl"],
+      [AC_DEFINE(USE_PAM,,Use PAM for authentication)
+       LIBPAM="-lpam -ldl -lpam_misc"]
+     )
+  ],AC_MSG_WARN([*** pam support will not be built ***]),
+  [-ldl])
+  ]
+)
+fi
+AC_SUBST(LIBPAM)
+
+dnl -------------------------------
+dnl Endian-ness check
+dnl -------------------------------
+AC_WORDS_BIGENDIAN
+
+dnl -------------------------------
+dnl check the size in byte of the C
+dnl -------------------------------
+dnl AC_CHECK_SIZEOF(char)
+dnl AC_CHECK_SIZEOF(int)
+dnl AC_CHECK_SIZEOF(short)
+dnl AC_CHECK_SIZEOF(long)
+
+dnl ----------------------------
+dnl check existance of functions
+dnl ----------------------------
+AC_FUNC_CHOWN
+AC_FUNC_FNMATCH
+AC_FUNC_FORK
+AC_FUNC_MEMCMP
+AC_FUNC_MKTIME
+AC_FUNC_STRFTIME
+AC_FUNC_STAT
+AC_FUNC_SELECT_ARGTYPES
+AC_FUNC_STRFTIME
+dnl Avoid AC_FUNC_STRNLEN because it pulls in AC_SYSTEM_EXTENSIONS which
+dnl can lead to strange side effects.  So we just check for strnlen
+dnl directly, see below.
+dnl AC_FUNC_STRNLENdnl
+AC_FUNC_VPRINTF
+
+dnl -------------------------------
+dnl bgpd needs pow() and hence libm
+dnl -------------------------------
+TMPLIBS="$LIBS"
+AC_CHECK_HEADER([math.h],
+  [AC_CHECK_LIB([m], [pow],
+    [LIBM="-lm"
+     LIBS="$LIBS $LIBM"
+     AC_DEFINE(HAVE_LIBM,, Have libm)
+     AC_CHECK_FUNCS(pow,[],[LIBM=""])
+    ])
+])
+if test x"$LIBM" = x ; then
+  AC_MSG_WARN([Unable to find working pow function - bgpd may not link])
+fi
+LIBS="$TMPLIBS"
+AC_SUBST(LIBM)
+
+dnl ---------------
+dnl other functions
+dnl ---------------
+AC_CHECK_FUNCS([dup2 ftruncate getcwd gethostbyname getpagesize gettimeofday \
+       inet_ntoa inet_aton strnlen \
+       memchr memmove memset select socket \
+       strcasecmp strchr strcspn strdup strerror \
+       strncasecmp strndup strrchr strspn strstr \
+       strtol strtoul strlcat strlcpy \
+       daemon snprintf vsnprintf \
+       if_nametoindex if_indextoname getifaddrs \
+       uname fcntl getgrouplist])
+
+
+AC_CHECK_HEADER([asm-generic/unistd.h],
+                [AC_CHECK_DECL(__NR_setns,
+                               AC_DEFINE(HAVE_NETNS,, Have netns),,
+                               QUAGGA_INCLUDES [#include <asm-generic/unistd.h>
+                               ])
+                 AC_CHECK_FUNCS(setns, AC_DEFINE(HAVE_SETNS,, Have setns))]
+               )
+
+dnl ------------------------------------
+dnl Determine routing get and set method
+dnl ------------------------------------
+AC_MSG_CHECKING(zebra between kernel interface method)
+if test x"$opsys" = x"gnu-linux"; then
+  AC_MSG_RESULT(netlink)
+  RT_METHOD=rt_netlink.o
+  AC_DEFINE(HAVE_NETLINK,,netlink)
+  netlink=yes
+else
+  AC_MSG_RESULT(Route socket)
+  KERNEL_METHOD="kernel_socket.o"
+  RT_METHOD="rt_socket.o"
+fi
+AC_SUBST(RT_METHOD)
+AC_SUBST(KERNEL_METHOD)
+AM_CONDITIONAL([HAVE_NETLINK], [test "x$netlink" = "xyes"])
+
+dnl --------------------------
+dnl Determine IS-IS I/O method
+dnl --------------------------
+AC_DEFINE(ISIS_METHOD_PFPACKET,        1, [ constant value for isis method pfpacket ])
+AC_DEFINE(ISIS_METHOD_DLPI,    2, [ constant value for isis method dlpi ])
+AC_DEFINE(ISIS_METHOD_BPF,     3, [ constant value for isis method bpf ])
+AC_CHECK_HEADER(net/bpf.h)
+AC_CHECK_HEADER(sys/dlpi.h)
+AC_MSG_CHECKING(zebra IS-IS I/O method)
+if test x"$opsys" = x"gnu-linux"; then
+  AC_MSG_RESULT(pfpacket)
+  ISIS_METHOD_MACRO="ISIS_METHOD_PFPACKET"
+elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
+  AC_MSG_RESULT(DLPI)
+  ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
+else
+  if test $ac_cv_header_net_bpf_h = no; then
+    if test $ac_cv_header_sys_dlpi_h = no; then
+      AC_MSG_RESULT(none)
+      AC_MSG_WARN([*** IS-IS support will not be built ***])
+      ISISD=""
+    else
+      AC_MSG_RESULT(DLPI)
+    fi
+    ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
+  else
+    AC_MSG_RESULT(BPF)
+    ISIS_METHOD_MACRO="ISIS_METHOD_BPF"
+  fi
+fi
+AC_DEFINE_UNQUOTED(ISIS_METHOD, $ISIS_METHOD_MACRO, [ selected method for isis, == one of the constants ])
+
+dnl ------------------------------------
+dnl check for broken CMSG_FIRSTHDR macro
+dnl ------------------------------------
+AC_MSG_CHECKING(for broken CMSG_FIRSTHDR)
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef SUNOS_5
+#define _XPG4_2
+#define __EXTENSIONS__
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+main()
+{
+  struct msghdr msg;
+  char buf[4];
+
+  msg.msg_control = buf;
+  msg.msg_controllen = 0;
+
+  if (CMSG_FIRSTHDR(&msg) != NULL)
+    exit(0);
+  exit (1);
+}]])],[AC_MSG_RESULT(yes - using workaround) AC_DEFINE(HAVE_BROKEN_CMSG_FIRSTHDR,,Broken CMSG_FIRSTHDR)],
+[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+
+dnl ------------------------------
+dnl check kernel route read method
+dnl ------------------------------
+AC_CACHE_CHECK([route read method], [quagga_cv_rtread_method],
+[if test "x$netlink" = xyes; then
+  quagga_cv_rtread_method="netlink"
+else
+for quagga_cv_rtread_method in /dev/ip /dev/null;
+do
+  test x`ls $quagga_cv_rtread_method 2>/dev/null` = x"$quagga_cv_rtread_method" && break
+done
+case $quagga_cv_rtread_method in
+  "/dev/ip")
+                     case "$host" in
+                       *-freebsd*)    quagga_cv_rtread_method="sysctl";;
+                       *)             quagga_cv_rtread_method="getmsg";;
+                     esac;;
+       *)
+                     quagga_cv_rtread_method="sysctl";;
+esac
+fi])
+RTREAD_METHOD=rtread_${quagga_cv_rtread_method}.o
+AC_SUBST(RTREAD_METHOD)
+
+dnl -----------------------------
+dnl check interface lookup method
+dnl -----------------------------
+IOCTL_METHOD=ioctl.o
+AC_MSG_CHECKING(interface looking up method)
+if test "$netlink" = yes; then
+  AC_MSG_RESULT(netlink)
+  IF_METHOD=if_netlink.o
+elif test "$opsys" = "sol2-6";then
+  AC_MSG_RESULT(Solaris GIF)
+  IF_METHOD=if_ioctl.o
+elif test "$opsys" = "sol8";then
+  AC_MSG_RESULT(Solaris GLIF)
+  IF_METHOD=if_ioctl_solaris.o
+  IOCTL_METHOD=ioctl_solaris.o
+elif test "$opsys" = "openbsd";then
+  AC_MSG_RESULT(openbsd)
+  IF_METHOD=if_ioctl.o
+elif grep NET_RT_IFLIST /usr/include/sys/socket.h >/dev/null 2>&1; then
+  AC_MSG_RESULT(sysctl)
+    IF_METHOD=if_sysctl.o
+    AC_DEFINE(HAVE_NET_RT_IFLIST,,NET_RT_IFLIST)
+else
+    AC_MSG_RESULT(ioctl)
+    IF_METHOD=if_ioctl.o
+fi
+AC_SUBST(IF_METHOD)
+AC_SUBST(IOCTL_METHOD)
+
+dnl ---------------------------------------------------------------
+dnl figure out how to specify an interface in multicast sockets API
+dnl ---------------------------------------------------------------
+AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], [], QUAGGA_INCLUDES)
+
+AC_CHECK_HEADERS([linux/mroute.h], [], [],
+[
+#if HAVE_NETINET_IN_H
+#include<netinet/in.h>
+#endif])
+AC_MSG_CHECKING([for BSD struct ip_mreq hack])
+AC_TRY_COMPILE([#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__sun)
+  return (0);
+#else
+  #error No support for BSD struct ip_mreq hack detected
+#endif],[AC_MSG_RESULT(yes)
+AC_DEFINE(HAVE_BSD_STRUCT_IP_MREQ_HACK,,[Can pass ifindex in struct ip_mreq])],
+AC_MSG_RESULT(no))
+
+AC_MSG_CHECKING([for RFC3678 protocol-independed API])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <netinet/in.h>
+], [struct group_req gr; int sock; setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void*)&gr, sizeof(gr));
+], [AC_MSG_RESULT(yes)
+AC_DEFINE(HAVE_RFC3678,1,[Have RFC3678 protocol-independed API])],
+AC_MSG_RESULT(no))
+
+dnl ---------------------------------------------------------------
+dnl figure out how to check link-state
+dnl ---------------------------------------------------------------
+AC_CHECK_HEADER([net/if.h],
+  [AC_CHECK_HEADER( [net/if_media.h],
+    [m4_define([LINK_DETECT_INCLUDES],
+      QUAGGA_INCLUDES
+      [#include <net/if_media.h>
+    ])
+    AC_CHECK_MEMBERS( [struct ifmediareq.ifm_status],
+      AC_DEFINE(HAVE_BSD_LINK_DETECT,,[BSD link-detect]),
+      [], LINK_DETECT_INCLUDES)], 
+    [],
+    QUAGGA_INCLUDES)], 
+  [], QUAGGA_INCLUDES )
+
+dnl ---------------------------------------------------------------
+dnl Additional, newer way to check link-state using ifi_link_state.
+dnl Not available in all BSD's when ifmediareq available
+dnl ---------------------------------------------------------------
+AC_CHECK_HEADER([net/if.h],
+    AC_CHECK_MEMBERS([struct if_data.ifi_link_state],
+      AC_DEFINE(HAVE_BSD_IFI_LINK_STATE,,[BSD ifi_link_state available]),
+      [], QUAGGA_INCLUDES),
+    ,)
+
+dnl ------------------------
+dnl TCP_MD5SIG socket option
+dnl ------------------------
+
+AC_CHECK_HEADER([netinet/tcp.h], 
+   [m4_define([MD5_INCLUDES],
+      QUAGGA_INCLUDES
+      [#include <netinet/tcp.h>
+    ])
+    AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)],
+   [],
+   QUAGGA_INCLUDES)
+if test $ac_cv_have_decl_TCP_MD5SIG = no; then
+  AC_CHECK_HEADER([linux/tcp.h],
+       [m4_define([MD5_INCLUDES],
+          QUAGGA_INCLUDES
+          [#include <linux/tcp.h>
+        ])
+       AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)])
+fi
+
+dnl -----------------------------
+dnl check ipforward detect method
+dnl -----------------------------
+AC_CACHE_CHECK([ipforward method], [quagga_cv_ipforward_method],
+[if test x$cross_compiling = xyes; then
+  if test x"$opsys" = x"gnu-linux"; then
+    quagga_cv_ipforward_method=/proc/net/snmp
+  else
+    quagga_cv_ipforward_method=/dev/ip
+  fi
+else
+  for quagga_cv_ipforward_method in /proc/net/snmp /dev/ip /dev/null;
+  do
+    test x`ls $quagga_cv_ipforward_method 2>/dev/null` = x"$quagga_cv_ipforward_method" && break
+  done
+fi
+case $quagga_cv_ipforward_method in
+  "/proc/net/snmp")  quagga_cv_ipforward_method="proc";;
+  "/dev/ip")         
+                     case "$host" in
+                       *-freebsd*)    quagga_cv_ipforward_method="sysctl";;
+                       *)             quagga_cv_ipforward_method="solaris";;
+                     esac;;
+  *)                 quagga_cv_ipforward_method="sysctl";;
+esac])
+IPFORWARD=ipforward_${quagga_cv_ipforward_method}.o
+AC_SUBST(IPFORWARD)
+
+AC_CHECK_FUNCS(getaddrinfo, [have_getaddrinfo=yes], [have_getaddrinfo=no])
+
+dnl ----------
+dnl IPv6 check
+dnl ----------
+AC_MSG_CHECKING(whether does this OS have IPv6 stack)
+dnl ---------
+dnl KAME IPv6
+dnl ---------
+  if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then
+    AC_DEFINE(KAME,1,KAME IPv6)
+    AC_MSG_RESULT(KAME)
+dnl ------------------------------------
+dnl Solaris 9, 10 and potentially higher
+dnl ------------------------------------
+  elif test x"$opsys" = x"sol8"; then
+    AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6)
+    AC_MSG_RESULT(Solaris IPv6)
+dnl ----------
+dnl Linux IPv6
+dnl ----------
+  elif test x"$opsys" = x"gnu-linux"; then
+    AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack)
+    AC_MSG_RESULT(Linux IPv6)
+  else
+    AC_MSG_ERROR([Failed to detect IPv6 stack])
+  fi
+
+dnl this is unconditial, for compatibility
+AC_DEFINE(HAVE_IPV6,1,IPv6)
+
+dnl ------------------
+dnl IPv6 header checks
+dnl ------------------
+AC_CHECK_HEADERS([netinet6/in6.h netinet/in6_var.h netinet/icmp6.h \
+       netinet6/in6_var.h netinet6/nd6.h], [], [],
+       QUAGGA_INCLUDES)
+
+m4_define([QUAGGA_INCLUDES],dnl
+QUAGGA_INCLUDES
+[#if HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#if HAVE_NETINET_IN6_VAR_H
+#include <netinet/in6_var.h>
+#endif
+#if HAVE_NETINET_ICMP6_H
+# include <netinet/icmp6.h>
+#endif
+#if HAVE_NETINET6_IN6_VAR_H
+# include <netinet6/in6_var.h>
+#endif
+#if HAVE_NETINET6_ND6_H
+# include <netinet6/nd6.h>
+#endif
+])dnl
+
+dnl disable doc check
+if test "${enable_doc}" = "no";then
+  DOC=""
+else
+  DOC="doc"
+fi
+
+dnl --------------------
+dnl Daemon disable check
+dnl --------------------
+if test "${enable_zebra}" = "no";then
+  ZEBRA=""
+else
+  ZEBRA="zebra"
+fi
+AM_CONDITIONAL(ZEBRA, test "x$ZEBRA" = "xzebra")
+
+if test "${enable_bgpd}" = "no";then
+  BGPD=""
+else
+  BGPD="bgpd"
+fi
+AM_CONDITIONAL(BGPD, test "x$BGPD" = "xbgpd")
+
+if test "${enable_ripd}" = "no";then
+  RIPD=""
+else
+  RIPD="ripd"
+fi
+AM_CONDITIONAL(RIPD, test "x$RIPD" = "xripd")
+
+if test "${enable_ospfd}" = "no";then
+  OSPFD=""
+else
+  OSPFD="ospfd"
+fi
+AM_CONDITIONAL(OSPFD, test "x$OSPFD" = "xospfd")
+
+if test x"$opsys" != x"gnu-linux"; then
+       dnl NHRPd works currently with Linux only.
+       enable_nhrpd="no"
+fi
+if test "${enable_nhrpd}" = "no";then
+  NHRPD=""
+else
+  NHRPD="nhrpd"
+fi
+AM_CONDITIONAL(NHRPD, test "x$NHRPD" = "xnhrpd")
+
+if test "${enable_watchquagga}" = "no";then
+  WATCHQUAGGA=""
+else
+  WATCHQUAGGA="watchquagga"
+fi
+AM_CONDITIONAL(WATCHQUAGGA, test "x$WATCHQUAGGA" = "xwatchquagga")
+
+OSPFCLIENT=""
+if test "${enable_ospfapi}" != "no";then
+    AC_DEFINE(SUPPORT_OSPF_API,,OSPFAPI)
+
+  if test "${enable_ospfclient}" != "no";then
+      OSPFCLIENT="ospfclient"
+  fi
+fi
+
+AM_CONDITIONAL(OSPFCLIENT, test "x$OSPFCLIENT" = "xospfclient")
+
+case "${enable_ripngd}" in
+  "no" ) RIPNGD="";;
+  *    ) RIPNGD="ripngd";;
+esac
+AM_CONDITIONAL(RIPNGD, test "x$RIPNGD" = "xripngd")
+
+case "${enable_ospf6d}" in
+  "no" ) OSPF6D="";;
+  *    ) OSPF6D="ospf6d";;
+esac
+AM_CONDITIONAL(OSPF6D, test "x$OSPF6D" = "xospf6d")
+
+case "${enable_isisd}" in
+  "no" ) ISISD="";;
+  *    ) ISISD="isisd";;
+esac
+AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd")
+
+case "${enable_pimd}" in
+  "no" ) PIMD="";;
+  *    ) PIMD="pimd";;
+esac
+AM_CONDITIONAL(PIMD, test "x$PIMD" = "xpimd")
+
+if test "${enable_bgp_announce}" = "no";then
+  AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra)
+else
+  AC_DEFINE(DISABLE_BGP_ANNOUNCE,0,Disable BGP installation to zebra)
+fi
+
+AC_SUBST(DOC)
+AC_SUBST(ZEBRA)
+AC_SUBST(BGPD)
+AC_SUBST(RIPD)
+AC_SUBST(RIPNGD)
+AC_SUBST(OSPFD)
+AC_SUBST(OSPF6D)
+AC_SUBST(NHRPD)
+AC_SUBST(WATCHQUAGGA)
+AC_SUBST(ISISD)
+AC_SUBST(PIMD)
+AC_SUBST(SOLARIS)
+AC_SUBST(VTYSH)
+AC_SUBST(CURSES)
+AC_SUBST(OSPFCLIENT)
+AC_SUBST(OSPFAPI)
+AC_CHECK_LIB(c, inet_ntop, [AC_DEFINE(HAVE_INET_NTOP,,inet_ntop)])
+AC_CHECK_LIB(c, inet_pton, [AC_DEFINE(HAVE_INET_PTON,,inet_pton)])
+AC_CHECK_LIB(crypt, crypt)
+AC_CHECK_LIB(resolv, res_init)
+
+dnl ---------------------------
+dnl check system has PCRE regexp
+dnl ---------------------------
+if test "x$enable_pcreposix" = "xyes"; then
+  AC_CHECK_LIB(pcreposix, pcreposix_regexec, ,[enable_pcreposix=no
+  AC_MSG_WARN([*** falling back to other regex library ***]) ])
+fi
+
+if test "x$enable_pcreposix" != "xyes"; then
+dnl ---------------------------
+dnl check system has GNU regexp
+dnl ---------------------------
+AC_MSG_CHECKING(whether system has GNU regex)
+AC_CHECK_LIB(c, regexec,
+[AC_DEFINE(HAVE_GNU_REGEX,,GNU regexp library)
+ LIB_REGEX=""],
+[LIB_REGEX="regex.o"])
+fi
+AC_SUBST(HAVE_LIBPCREPOSIX)
+AC_SUBST(LIB_REGEX)
+
+dnl ------------------
+dnl check C-Ares library
+dnl ------------------
+if test "${enable_nhrpd}" != "no";then
+   PKG_CHECK_MODULES([CARES], [libcares])
+fi
+
+
+dnl ------------------
+dnl check Net-SNMP library
+dnl ------------------
+if test "${enable_snmp}" != ""; then
+   AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], [no])
+   if test x"$NETSNMP_CONFIG" = x"no"; then
+      AC_MSG_ERROR([--enable-snmp given but unable to find net-snmp-config])
+   fi
+   LIBS="$LIBS `${NETSNMP_CONFIG} --agent-libs`"
+   CFLAGS="`${NETSNMP_CONFIG} --base-cflags` $CFLAGS"
+   AC_MSG_CHECKING([whether we can link to Net-SNMP])
+   AC_LINK_IFELSE([AC_LANG_PROGRAM([
+int main(void);
+],
+[
+{
+  return 0;
+}
+])],[AC_MSG_RESULT(yes)],[
+     AC_MSG_RESULT(no)
+     AC_MSG_ERROR([--enable-snmp given but not usable])])
+   AC_DEFINE(HAVE_SNMP,,SNMP)
+   case "${enable_snmp}" in
+     yes)
+      SNMP_METHOD=agentx
+      ;;
+     smux|agentx)
+      SNMP_METHOD="${enable_snmp}"
+      ;;
+     *)
+      AC_MSG_ERROR([--enable-snmp given with an unknown method (${enable_snmp}). Use smux or agentx])
+      ;;
+   esac
+   AH_TEMPLATE([SNMP_SMUX], [Use SNMP SMUX to interface with snmpd])
+   AH_TEMPLATE([SNMP_AGENTX], [Use SNMP AgentX to interface with snmpd])
+   AC_DEFINE_UNQUOTED(AS_TR_CPP(SNMP_${SNMP_METHOD}),,SNMP method to interface with snmpd)
+fi
+
+dnl ---------------------------
+dnl sockaddr and netinet checks
+dnl ---------------------------
+AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in,
+       struct sockaddr_in6, struct sockaddr_un, struct sockaddr_dl,
+       socklen_t, struct vifctl, struct mfcctl, struct sioc_sg_req,
+       vifi_t, struct sioc_vif_req, struct igmpmsg,
+       struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq,
+       struct nd_opt_adv_interval, struct rt_addrinfo,
+       struct nd_opt_homeagent_info, struct nd_opt_adv_interval],
+       [], [], QUAGGA_INCLUDES)
+
+AC_CHECK_MEMBERS([struct sockaddr.sa_len,
+       struct sockaddr_in.sin_len, struct sockaddr_un.sun_len,
+       struct sockaddr_in6.sin6_scope_id,
+       struct sockaddr_dl.sdl_len,
+       struct if6_aliasreq.ifra_lifetime,
+       struct nd_opt_adv_interval.nd_opt_ai_type],
+       [], [], QUAGGA_INCLUDES)
+
+dnl ---------------------------
+dnl IRDP/pktinfo/icmphdr checks
+dnl ---------------------------
+AC_CHECK_TYPES([struct in_pktinfo], 
+ [AC_CHECK_TYPES([struct icmphdr],
+   [if test "${enable_irdp}" != "no"; then
+      AC_DEFINE(HAVE_IRDP,, IRDP)
+    fi],
+   [if test "${enable_irdp}" = "yes"; then
+      AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
+    fi], [QUAGGA_INCLUDES])],
+ [if test "${enable_irdp}" = "yes"; then
+    AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
+  fi], [QUAGGA_INCLUDES])
+
+dnl -----------------------
+dnl checking for IP_PKTINFO
+dnl -----------------------
+AC_MSG_CHECKING(for IP_PKTINFO)
+AC_TRY_COMPILE([#include <netdb.h>], [
+  int opt = IP_PKTINFO;
+], [
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_IP_PKTINFO, 1, [Have IP_PKTINFO])
+], [
+  AC_MSG_RESULT(no)
+])
+
+dnl ---------------------------
+dnl checking for IP_RECVDSTADDR
+dnl ---------------------------
+AC_MSG_CHECKING(for IP_RECVDSTADDR)
+AC_TRY_COMPILE([#include <netinet/in.h>], [
+  int opt = IP_RECVDSTADDR;
+], [
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_IP_RECVDSTADDR, 1, [Have IP_RECVDSTADDR])
+], [
+  AC_MSG_RESULT(no)
+])
+
+dnl ----------------------
+dnl checking for IP_RECVIF
+dnl ----------------------
+AC_MSG_CHECKING(for IP_RECVIF)
+AC_TRY_COMPILE([#include <netinet/in.h>], [
+  int opt = IP_RECVIF;
+], [
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_IP_RECVIF, 1, [Have IP_RECVIF])
+], [
+  AC_MSG_RESULT(no)
+])
+
+dnl --------------------------------------
+dnl checking for getrusage struct and call
+dnl --------------------------------------
+if test "${enable_rusage}" != "no"; then
+  AC_MSG_CHECKING(whether getrusage is available)
+  AC_TRY_COMPILE([#include <sys/resource.h>],[struct rusage ac_x; getrusage (RUSAGE_SELF, &ac_x);],
+    [AC_MSG_RESULT(yes)
+     AC_DEFINE(HAVE_RUSAGE,,rusage)],
+      AC_MSG_RESULT(no))
+fi
+
+dnl --------------------------------------
+dnl checking for clock_time monotonic struct and call
+dnl --------------------------------------
+AC_CHECK_DECL(CLOCK_MONOTONIC,
+       [AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS -lrt"])
+        AC_DEFINE(HAVE_CLOCK_MONOTONIC,, Have monotonic clock)
+], [AC_MSG_RESULT(no)], [QUAGGA_INCLUDES])
+
+dnl -------------------
+dnl capabilities checks
+dnl -------------------
+if test "${enable_capabilities}" != "no"; then
+  AC_MSG_CHECKING(whether prctl PR_SET_KEEPCAPS is available)
+  AC_TRY_COMPILE([#include <sys/prctl.h>],[prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);],
+    [AC_MSG_RESULT(yes)
+     AC_DEFINE(HAVE_PR_SET_KEEPCAPS,,prctl)
+     quagga_ac_keepcaps="yes"],
+     AC_MSG_RESULT(no)
+  )
+  if test x"${quagga_ac_keepcaps}" = x"yes"; then
+    AC_CHECK_HEADERS(sys/capability.h)
+  fi
+  if test x"${ac_cv_header_sys_capability_h}" = x"yes"; then
+    AC_CHECK_LIB(cap, cap_init, 
+      [AC_DEFINE(HAVE_LCAPS,1,Capabilities)
+       LIBCAP="-lcap"
+       quagga_ac_lcaps="yes"]
+    )
+  else
+    AC_CHECK_HEADERS(priv.h,
+     [AC_MSG_CHECKING(Solaris style privileges are available)
+      AC_TRY_COMPILE([#include <priv.h>],[getpflags(PRIV_AWARE);],
+         [AC_MSG_RESULT(yes)
+          AC_DEFINE(HAVE_SOLARIS_CAPABILITIES,1,getpflags)
+          quagga_ac_scaps="yes"],
+          AC_MSG_RESULT(no)
+      )
+     ]
+   )
+  fi
+  if test x"${quagga_ac_scaps}" = x"yes" \
+       -o x"${quagga_ac_lcaps}" = x"yes"; then
+    AC_DEFINE(HAVE_CAPABILITIES,1,capabilities)
+  fi
+fi
+AC_SUBST(LIBCAP)
+
+dnl ---------------------------------------------------------------------------
+dnl http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html
+dnl Check for and set one of the following = 1
+dnl   HAVE_SYS_WEAK_ALIAS_ATTRIBUTE
+dnl   HAVE_SYS_WEAK_ALIAS_PRAGMA
+dnl   HAVE_SYS_WEAK_ALIAS_HPSECONDARY
+dnl   HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE
+dnl If any scheme is found, set
+dnl   HAVE_SYS_WEAK_ALIAS=1
+dnl The following variable is set to text value
+dnl   WEAK_ALIAS = "attribute" || "pragma" || "hpsecondary" || "criduplicate" || "no"
+dnl If weak alias can cross object file boundaries
+dnl   WEAK_ALIAS_CROSSFILE = "yes" || "no"
+dnl ---------------------------------------------------------------------------
+AX_SYS_WEAK_ALIAS
+
+dnl ---------------------------
+dnl check for glibc 'backtrace'
+dnl --------------------------- 
+if test x"${enable_backtrace}" != x"no" ; then
+  backtrace_ok=no
+  AC_CHECK_HEADER([execinfo.h], [
+    AC_SEARCH_LIBS([backtrace], [execinfo], [
+      AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace])
+      AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding])
+      backtrace_ok=yes
+    ],, [-lm])
+  ])
+
+  if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then
+    dnl user explicitly requested backtrace but we failed to find support
+    AC_MSG_FAILURE([failed to find backtrace support])
+  fi
+fi
+
+dnl -----------------------------------------
+dnl check for malloc mallinfo struct and call
+dnl this must try and link using LIBS, in
+dnl order to check no alternative allocator
+dnl has been specified, which might not provide
+dnl mallinfo, e.g. such as Umem on Solaris.
+dnl -----------------------------------------
+AC_CHECK_HEADER([malloc.h],
+ [AC_MSG_CHECKING(whether mallinfo is available)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <malloc.h>]],
+                        [[struct mallinfo ac_x; ac_x = mallinfo ();]])],
+      [AC_MSG_RESULT(yes)
+       AC_DEFINE(HAVE_MALLINFO,,mallinfo)],
+       AC_MSG_RESULT(no)
+  )
+ ], [], QUAGGA_INCLUDES)
+
+dnl ----------
+dnl configure date
+dnl ----------
+CONFDATE=`date '+%Y%m%d'`
+AC_SUBST(CONFDATE)
+
+dnl -------
+dnl DejaGNU
+dnl -------
+if test x"$DEJAGNU" = x
+then
+  DEJAGNU="\$(top_srcdir)/tests/global-conf.exp"
+fi
+RUNTESTDEFAULTFLAGS="-x --tool \$\$tool"
+
+AC_SUBST(DEJAGNU)
+AC_SUBST(RUNTESTDEFAULTFLAGS)
+
+dnl ------------------------------
+dnl set paths for state directory
+dnl ------------------------------
+AC_MSG_CHECKING(directory to use for state file)
+if test "${prefix}" = "NONE"; then
+  quagga_statedir_prefix="";
+else
+  quagga_statedir_prefix=${prefix}
+fi
+if test "${localstatedir}" = '${prefix}/var'; then
+  for QUAGGA_STATE_DIR in ${quagga_statedir_prefix}/var/run dnl
+                       ${quagga_statedir_prefix}/var/adm dnl
+                       ${quagga_statedir_prefix}/etc dnl
+                       /var/run        dnl
+                        /var/adm       dnl
+                        /etc           dnl
+                        /dev/null;
+  do
+    test -d $QUAGGA_STATE_DIR && break
+  done
+  quagga_statedir=$QUAGGA_STATE_DIR
+else
+  quagga_statedir=${localstatedir}
+fi
+if test $quagga_statedir = "/dev/null"; then
+    AC_MSG_ERROR('STATE DIRECTORY NOT FOUND! FIX OR SPECIFY --localstatedir!')
+fi
+AC_MSG_RESULT(${quagga_statedir})
+AC_SUBST(quagga_statedir)
+
+AC_DEFINE_UNQUOTED(PATH_ZEBRA_PID, "$quagga_statedir/zebra.pid",zebra PID)
+AC_DEFINE_UNQUOTED(PATH_RIPD_PID, "$quagga_statedir/ripd.pid",ripd PID)
+AC_DEFINE_UNQUOTED(PATH_RIPNGD_PID, "$quagga_statedir/ripngd.pid",ripngd PID)
+AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$quagga_statedir/bgpd.pid",bgpd PID)
+AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID)
+AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID)
+AC_DEFINE_UNQUOTED(PATH_NHRPD_PID, "$quagga_statedir/nhrpd.pid",nhrpd PID)
+AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID)
+AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID)
+AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID)
+AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$quagga_statedir/zserv.api",zebra api socket)
+AC_DEFINE_UNQUOTED(ZEBRA_VTYSH_PATH, "$quagga_statedir/zebra.vty",zebra vty socket)
+AC_DEFINE_UNQUOTED(RIP_VTYSH_PATH, "$quagga_statedir/ripd.vty",rip vty socket)
+AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$quagga_statedir/ripngd.vty",ripng vty socket)
+AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$quagga_statedir/bgpd.vty",bgpd vty socket)
+AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socket)
+AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket)
+AC_DEFINE_UNQUOTED(NHRP_VTYSH_PATH, "$quagga_statedir/nhrpd.vty",nhrpd vty socket)
+AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket)
+AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket)
+AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory)
+
+dnl -------------------------------
+dnl Quagga sources should always be 
+dnl current wrt interfaces. Dont
+dnl allow deprecated interfaces to
+dnl be exposed.
+dnl -------------------------------
+AC_DEFINE(QUAGGA_NO_DEPRECATED_INTERFACES, 1, Hide deprecated interfaces)
+
+dnl ---------------------------
+dnl Check htonl works correctly
+dnl ---------------------------
+AC_MSG_CHECKING(for working htonl)
+AC_CACHE_VAL(ac_cv_htonl_works,
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([QUAGGA_INCLUDES],[htonl (0);])],
+               [ac_cv_htonl_works=yes], [ac_cv_htonl_works=no])
+ ]
+)
+AC_MSG_RESULT($ac_cv_htonl_works)
+
+AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile 
+         ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile
+         ospf6d/Makefile isisd/Makefile vtysh/Makefile
+         doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile
+         pimd/Makefile nhrpd/Makefile
+         tests/bgpd.tests/Makefile
+         tests/libzebra.tests/Makefile
+         redhat/Makefile
+         pkgsrc/Makefile
+         fpm/Makefile
+         redhat/quagga.spec 
+         lib/version.h
+         isisd/topology/Makefile
+         pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh
+         pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh])
+AC_CONFIG_FILES([solaris/Makefile])
+
+AC_CONFIG_FILES([vtysh/extract.pl],[chmod +x vtysh/extract.pl])
+## Hack, but working solution to avoid rebuilding of quagga.info.
+## It's already in CVS until texinfo 4.7 is more common.
+AC_OUTPUT
+
+echo "
+Quagga configuration
+--------------------
+quagga version          : ${PACKAGE_VERSION}
+host operating system   : ${host_os}
+source code location    : ${srcdir}
+compiler                : ${CC}
+compiler flags          : ${CFLAGS}
+make                    : ${MAKE-make}
+linker flags            : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM}
+state file directory    : ${quagga_statedir}
+config file directory   : `eval echo \`echo ${sysconfdir}\``
+example directory       : `eval echo \`echo ${exampledir}\``
+user to run as         : ${enable_user}
+group to run as                : ${enable_group}
+group for vty sockets  : ${enable_vty_group}
+config file mask        : ${enable_configfile_mask}
+log file mask           : ${enable_logfile_mask}
+zebra protobuf enabled  : ${have_protobuf:-no}
+
+The above user and group must have read/write access to the state file
+directory and to the config files in the config file directory."
+
+if test x"$quagga_cv_gnu_make" = x"no"; then echo "
+Warning: The ${MAKE-make} programme detected, either in your path or
+via the MAKE variable, is not GNU Make. GNU make may be installed as
+gmake on some systems. and is required to complete a build of Quagga
+" > /dev/stderr 
+fi
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644 (file)
index 0000000..459531b
--- /dev/null
@@ -0,0 +1,32 @@
+Makefile
+Makefile.in
+mdate-sh
+draft-zebra-00.txt
+quagga.info-*
+zebra.html
+version.texi
+quagga.html
+quagga.info
+*.pdf
+*.eps
+quagga.ps
+quagga.dvi
+stamp-vti
+.nfs*
+*.aux
+*.cp
+*.cps
+*.fn
+*.fns
+*.ky
+*.kys
+*.log
+*.op
+*.pg
+*.toc
+*.tp
+*.vr
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/doc/BGP-TypeCode b/doc/BGP-TypeCode
new file mode 100644 (file)
index 0000000..b321807
--- /dev/null
@@ -0,0 +1,24 @@
+
+                BGP-4[+] UPDATE Attribute TypeCode list
+
+  Value  Attribute            References
+=========================================================================
+    1    ORIGIN               [RFC 4271]
+    2    AS_PATH              [RFC 4271]
+    3    NEXT_HOP             [RFC 4271]
+    4    MULTI_EXIT_DISC      [RFC 4271]
+    5    LOCAL_PREF           [RFC 4271]
+    6    ATOMIC_AGGREGATE     [RFC 4271]
+    7    AGGREGATOR           [RFC 4271]
+    8    COMMUNITIES          [RFC 1997]
+    9    ORIGINATOR_ID        [RFC 4456]
+   10    CLUSTER_LIST         [RFC 4456]
+   11    DPA                  [draft-ietf-idr-bgp-dpa-05.txt(expired)]
+   12    ADVERTISER           [RFC 1863]
+   13    RCID_PATH            [RFC 1863]
+   14    MP_REACH_NLRI        [RFC 4760]
+   15    MP_UNREACH_NLRI      [RFC 4760]
+   16    EXT_COMMUNITIES      [RFC 4360]
+   17    AS4_PATH             [RFC 4893]
+   18    AS4_AGGREGATOR       [RFC 4893]
+=========================================================================
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..38920c8
--- /dev/null
@@ -0,0 +1,125 @@
+## Process this file with automake to produce Makefile.in.
+
+# Dia, the version i have at least, doesn't do very good EPS output
+# (some of the text is scaled strangely). So this will work, but 
+# it is probably better to use something like gimp to convert the 
+# dia exported PNG files to EPS manually.
+#
+# Here we use 'convert' from the well known 'ImageMagick' package
+# to do conversion from png to eps/pdf for figures. 
+# PDF form is required for quagga.pdf, using PDFTex at least.
+#
+# TeX implementation, which we depend on already anyway.
+#
+# dia -> (dia) -> png -> (convert) -> eps -> (epstopdf) -> pdf
+SUFFIXES = .png .eps .dia .pdf
+DIATOPNG = dia -t png -e
+DIATOEPS = dia -t eps -e
+PNGTOEPS = convert -antialias -contrast -despeckle
+PNGTOPDF = $(PNGTOEPS)
+EPSTOPDF = epstopdf
+
+# The figure sources
+figures_names_parts = -normal-processing -rs-processing
+figures_sources = $(figures_names_parts:%=fig%.dia)
+figures_png = $(figures_names_parts:%=fig%.png)
+figures_pdf = $(figures_names_parts:%=fig%.pdf)
+figures_eps = $(figures_names_parts:%=fig%.eps)
+figures_txt = $(figures_names_parts:%=fig%.txt)
+
+# rather twisted logic because we have to build PDFs of the EPS figures for
+# PDFTex and yet build one PDF, quagga.pdf, from texi source. Which means we
+# cant rely on a single automatic rule for *.pdf, eg the one automatically
+# provided by automake. If you are an automake wizard, please feel free to
+# compact it somehow.
+
+#quagga.pdf: $(info_TEXINFOS) $(quagga_TEXINFOS)
+#      $(TEXI2PDF) -o "$@" $< || true
+
+info_TEXINFOS = quagga.texi
+
+quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi \
+       install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \
+       overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \
+       snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \
+       snmptrap.texi ospf_fundamentals.texi isisd.texi nhrpd.texi \
+       $(figures_txt)
+
+.png.eps:
+       $(PNGTOEPS) $< "$@"
+
+.png.pdf:
+       $(PNGTOPDF) $< "$@"
+
+.dia.png:
+       $(DIATOPNG) "$@" $<
+
+man_MANS =
+
+if PIMD
+man_MANS += pimd.8
+endif
+
+if BGPD
+man_MANS += bgpd.8
+endif
+
+if ISISD
+man_MANS += isisd.8
+endif
+
+if OSPF6D
+man_MANS += ospf6d.8
+endif
+
+if OSPFCLIENT
+man_MANS += ospfclient.8
+endif
+
+if OSPFD
+man_MANS += ospfd.8
+endif
+
+if RIPD
+man_MANS += ripd.8
+endif
+
+if RIPNGD
+man_MANS += ripngd.8
+endif
+
+if NHRPD
+man_MANS += nhrpd.8
+endif
+
+if VTYSH
+man_MANS += vtysh.1
+endif
+
+if WATCHQUAGGA
+man_MANS += watchquagga.8
+endif
+
+if ZEBRA
+man_MANS += zebra.8
+endif
+
+AM_MAKEINFOHTMLFLAGS = --css-include=$(srcdir)/texinfo.css
+
+EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
+       bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ripd.8 \
+       ripngd.8 nhrpd.8 pimd.8 vtysh.1 watchquagga.8 zebra.8 \
+       mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \
+       mpls/opaque_lsa.txt mpls/ospfd.conf \
+       $(figures_sources) $(figures_png) $(figures_txt) \
+       texinfo.tex texinfo.css
+
+draft-zebra-00.txt: draft-zebra-00.ms
+       groff -T ascii -ms $< > $@
+
+CLEANFILES = *.{fn,fns,cp,cps,ky,kys}
+DISTCLEANFILES = quagga.info*
+
+# do nothing for DVI, so we don't have to generate or distribute EPS
+# figures
+dvi: # nothing
diff --git a/doc/appendix.texi b/doc/appendix.texi
new file mode 100644 (file)
index 0000000..3904c5f
--- /dev/null
@@ -0,0 +1,257 @@
+@node  Packet Binary Dump Format
+@appendix Packet Binary Dump Format
+
+  Quagga can dump routing protocol packet into file with a binary format
+(@pxref{Dump BGP packets and table}).
+
+  It seems to be better that we share the MRT's header format for
+backward compatibility with MRT's dump logs. We should also define the
+binary format excluding the header, because we must support both IP
+v4 and v6 addresses as socket addresses and / or routing entries.
+
+  In the last meeting, we discussed to have a version field in the
+header. But Masaki told us that we can define new `type' value rather
+than having a `version' field, and it seems to be better because we
+don't need to change header format.
+
+  Here is the common header format. This is same as that of MRT.
+
+@example
+@group
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                              Time                             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             Type              |            Subtype            |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Length                            |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+  If `type' is PROTOCOL_BGP4MP_ET, the common header format will
+contain an additional microsecond field (RFC6396 2011).
+
+@example
+@group
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                              Time                             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             Type              |            Subtype            |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Length                            |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                          Microsecond                          |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+  If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_STATE_CHANGE, and
+Address Family == IP (version 4)
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source AS number       |     Destination AS number     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Interface Index        |      Address Family           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address                    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Old State          |           New State           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+Where State is the value defined in RFC1771.
+
+If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_STATE_CHANGE,
+and Address Family == IP version 6
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source AS number       |     Destination AS number     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Interface Index        |      Address Family           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address                    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Old State          |           New State           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_MESSAGE,
+and Address Family == IP (version 4)
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source AS number       |     Destination AS number     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Interface Index        |      Address Family           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address                    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                       BGP Message Packet                      |
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+Where BGP Message Packet is the whole contents of the
+BGP4 message including header portion.
+
+If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_MESSAGE,
+and Address Family == IP version 6
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source AS number       |     Destination AS number     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Interface Index        |      Address Family           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Source IP address (Cont'd)             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address                    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                     Destination IP address (Cont'd)           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                       BGP Message Packet                      |
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_ENTRY,
+and Address Family == IP (version 4)
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            View #             |            Status             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Time Last Change                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Address Family          |    SAFI       | Next-Hop-Len  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Next Hop Address                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Prefix Length |             Address Prefix [variable]         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Attribute Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      BGP Attribute [variable length]                         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_ENTRY,
+and Address Family == IP version 6
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            View #             |            Status             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Time Last Change                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Address Family          |    SAFI       | Next-Hop-Len  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Next Hop Address                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Next Hop Address (Cont'd)              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Next Hop Address (Cont'd)              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Next Hop Address (Cont'd)              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Prefix Length |             Address Prefix [variable]         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Address Prefix (cont'd) [variable]        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Attribute Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      BGP Attribute [variable length]                             |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+       BGP4 Attribute must not contain MP_UNREACH_NLRI.
+       If BGP Attribute has MP_REACH_NLRI field, it must has
+       zero length NLRI, e.g., MP_REACH_NLRI has only Address
+       Family, SAFI and next-hop values.
+
+If `type' is PROTOCOL_BGP4MP and `subtype' is BGP4MP_SNAPSHOT,
+
+@example
+@group
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|           View #              |       File Name [variable]    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@end group
+@end example
+
+    The file specified in "File Name" contains all routing entries,
+    which are in the format of ``subtype == BGP4MP_ENTRY''.
+
+@example
+@group
+Constants:
+  /* type value */
+  #define MSG_PROTOCOL_BGP4MP    16
+  #define MSG_PROTOCOL_BGP4MP_ET 17
+  /* subtype value */
+  #define BGP4MP_STATE_CHANGE 0
+  #define BGP4MP_MESSAGE 1
+  #define BGP4MP_ENTRY 2
+  #define BGP4MP_SNAPSHOT 3
+@end group
+@end example
diff --git a/doc/basic.texi b/doc/basic.texi
new file mode 100644 (file)
index 0000000..92e8d9e
--- /dev/null
@@ -0,0 +1,605 @@
+@node Basic commands
+@chapter Basic commands
+
+There are five routing daemons in use, and there is one manager daemon.
+These daemons may be located on separate machines from the manager
+daemon.  Each of these daemons will listen on a particular port for
+incoming VTY connections.  The routing daemons are:
+
+@itemize @bullet
+@item @command{ripd}, @command{ripngd}, @command{ospfd}, @command{ospf6d}, @command{bgpd}
+@item @command{zebra}
+@end itemize
+
+The following sections discuss commands common to all the routing
+daemons.
+
+@menu
+* Config Commands::             Commands used in config files
+* Terminal Mode Commands::      Common commands used in a VTY
+* Common Invocation Options::   Starting the daemons
+* Virtual Terminal Interfaces:: Interacting with the daemons
+@end menu
+
+
+
+@node Config Commands
+@section Config Commands
+
+@cindex Configuration files for running the software
+@c A -not configuration files for installing the software
+@cindex Files for running configurations
+@cindex Modifying the herd's behavior
+@cindex Getting the herd running
+
+
+@menu
+* Basic Config Commands::       Some of the generic config commands
+* Sample Config File::          An example config file
+@end menu
+
+
+In a config file, you can write the debugging options, a vty's password,
+routing daemon configurations, a log file name, and so forth. This
+information forms the initial command set for a routing beast as it is
+starting.
+
+Config files are generally found in:
+
+@itemize @w{}
+@item @file{@value{INSTALL_PREFIX_ETC}/*.conf}
+@end itemize
+
+Each of the daemons has its own
+config file.  For example, zebra's default config file name is:
+
+@itemize @w{}
+@item @file{@value{INSTALL_PREFIX_ETC}/zebra.conf}
+@end itemize
+
+The daemon name plus @file{.conf} is the default config file name. You
+can specify a config file using the @kbd{-f} or @kbd{--config-file}
+options when starting the daemon.
+
+
+
+@node Basic Config Commands
+@subsection Basic Config Commands
+
+@deffn Command {hostname @var{hostname}} {}
+Set hostname of the router.
+@end deffn
+
+@deffn Command {password @var{password}} {}
+Set password for vty interface.  If there is no password, a vty won't
+accept connections.
+@end deffn
+
+@deffn Command {enable password @var{password}} {}
+Set enable password.
+@end deffn
+
+@deffn Command {log trap @var{level}} {}
+@deffnx Command {no log trap} {}
+These commands are deprecated and are present only for historical compatibility.
+The log trap command sets the current logging level for all enabled
+logging destinations, and it sets the default for all future logging commands
+that do not specify a level.  The normal default
+logging level is debugging.  The @code{no} form of the command resets
+the default level for future logging commands to debugging, but it does
+not change the logging level of existing logging destinations.
+@end deffn
+
+
+@deffn Command {log stdout} {}
+@deffnx Command {log stdout @var{level}} {}
+@deffnx Command {no log stdout} {}
+Enable logging output to stdout. 
+If the optional second argument specifying the
+logging level is not present, the default logging level (typically debugging,
+but can be changed using the deprecated @code{log trap} command) will be used.
+The @code{no} form of the command disables logging to stdout.
+The @code{level} argument must have one of these values: 
+emergencies, alerts, critical, errors, warnings, notifications, informational, or debugging.  Note that the existing code logs its most important messages
+with severity @code{errors}.
+@end deffn
+
+@deffn Command {log file @var{filename}} {}
+@deffnx Command {log file @var{filename} @var{level}} {}
+@deffnx Command {no log file} {}
+If you want to log into a file, please specify @code{filename} as
+in this example:
+@example
+log file /var/log/quagga/bgpd.log informational
+@end example
+If the optional second argument specifying the
+logging level is not present, the default logging level (typically debugging,
+but can be changed using the deprecated @code{log trap} command) will be used.
+The @code{no} form of the command disables logging to a file.
+
+Note: if you do not configure any file logging, and a daemon crashes due
+to a signal or an assertion failure, it will attempt to save the crash
+information in a file named /var/tmp/quagga.<daemon name>.crashlog.
+For security reasons, this will not happen if the file exists already, so
+it is important to delete the file after reporting the crash information.
+@end deffn
+
+@deffn Command {log syslog} {}
+@deffnx Command {log syslog @var{level}} {}
+@deffnx Command {no log syslog} {}
+Enable logging output to syslog.
+If the optional second argument specifying the
+logging level is not present, the default logging level (typically debugging,
+but can be changed using the deprecated @code{log trap} command) will be used.
+The @code{no} form of the command disables logging to syslog.
+@end deffn
+
+@deffn Command {log monitor} {}
+@deffnx Command {log monitor @var{level}} {}
+@deffnx Command {no log monitor} {}
+Enable logging output to vty terminals that have enabled logging
+using the @code{terminal monitor} command.
+By default, monitor logging is enabled at the debugging level, but this
+command (or the deprecated @code{log trap} command) can be used to change 
+the monitor logging level.
+If the optional second argument specifying the
+logging level is not present, the default logging level (typically debugging,
+but can be changed using the deprecated @code{log trap} command) will be used.
+The @code{no} form of the command disables logging to terminal monitors.
+@end deffn
+
+@deffn Command {log facility @var{facility}} {}
+@deffnx Command {no log facility} {}
+This command changes the facility used in syslog messages.  The default
+facility is @code{daemon}.  The @code{no} form of the command resets
+the facility to the default @code{daemon} facility.
+@end deffn
+
+@deffn Command {log record-priority} {}
+@deffnx Command {no log record-priority} {}
+To include the severity in all messages logged to a file, to stdout, or to
+a terminal monitor (i.e. anything except syslog),
+use the @code{log record-priority} global configuration command.
+To disable this option, use the @code{no} form of the command.  By default,
+the severity level is not included in logged messages.  Note: some
+versions of syslogd (including Solaris) can be configured to include
+the facility and level in the messages emitted.
+@end deffn
+
+@deffn Command {log timestamp precision @var{<0-6>}} {}
+@deffnx Command {no log timestamp precision} {}
+This command sets the precision of log message timestamps to the
+given number of digits after the decimal point.  Currently,
+the value must be in the range 0 to 6 (i.e. the maximum precision
+is microseconds).
+To restore the default behavior (1-second accuracy), use the
+@code{no} form of the command, or set the precision explicitly to 0.
+
+@example
+@group
+log timestamp precision 3
+@end group
+@end example
+
+In this example, the precision is set to provide timestamps with
+millisecond accuracy.
+@end deffn
+
+@deffn Command {log commands} {}
+This command enables the logging of all commands typed by a user to
+all enabled log destinations.  The note that logging includes full
+command lines, including passwords.  Once set, command logging can only
+be turned off by restarting the daemon.
+@end deffn  
+
+@deffn Command {service password-encryption} {}
+Encrypt password.
+@end deffn
+
+@deffn Command {service advanced-vty} {}
+Enable advanced mode VTY.
+@end deffn
+
+@deffn Command {service terminal-length @var{<0-512>}} {}
+Set system wide line configuration.  This configuration command applies
+to all VTY interfaces.
+@end deffn
+
+@deffn Command {line vty} {}
+Enter vty configuration mode.
+@end deffn
+
+@deffn Command {banner motd default} {}
+Set default motd string.
+@end deffn
+
+@deffn Command {no banner motd} {}
+No motd banner string will be printed.
+@end deffn
+
+@deffn {Line Command} {exec-timeout @var{minute}} {}
+@deffnx {Line Command} {exec-timeout @var{minute} @var{second}} {}
+Set VTY connection timeout value.  When only one argument is specified
+it is used for timeout value in minutes.  Optional second argument is
+used for timeout value in seconds. Default timeout value is 10 minutes.
+When timeout value is zero, it means no timeout.
+@end deffn
+
+@deffn {Line Command} {no exec-timeout} {}
+Do not perform timeout at all.  This command is as same as
+@command{exec-timeout 0 0}.
+@end deffn
+
+@deffn {Line Command} {access-class @var{access-list}} {}
+Restrict vty connections with an access list.
+@end deffn
+
+@node Sample Config File
+@subsection Sample Config File
+
+
+Below is a sample configuration file for the zebra daemon.
+
+@example
+@group
+!
+! Zebra configuration file
+!
+hostname Router
+password zebra
+enable password zebra
+!
+log stdout
+!
+!
+@end group
+@end example
+
+'!' and '#' are comment characters.  If the first character of the word
+is one of the comment characters then from the rest of the line forward
+will be ignored as a comment.
+
+@example
+password zebra!password
+@end example
+
+If a comment character is not the first character of the word, it's a
+normal character. So in the above example '!' will not be regarded as a
+comment and the password is set to 'zebra!password'.
+
+
+
+@node Terminal Mode Commands
+@section Terminal Mode Commands
+
+@deffn Command {write terminal} {}
+Displays the current configuration to the vty interface.
+@end deffn
+
+@deffn Command {write file} {}
+Write current configuration to configuration file.
+@end deffn
+
+@deffn Command {configure terminal} {}
+Change to configuration mode.  This command is the first step to
+configuration.
+@end deffn
+
+@deffn Command {terminal length @var{<0-512>}} {}
+Set terminal display length to @var{<0-512>}.  If length is 0, no
+display control is performed.
+@end deffn
+
+@deffn Command {who} {}
+Show a list of currently connected vty sessions.
+@end deffn
+
+@deffn Command {list} {}
+List all available commands.
+@end deffn
+
+@deffn Command {show version} {}
+Show the current version of @value{PACKAGE_NAME} and its build host information.
+@end deffn
+
+@deffn Command {show logging} {}
+Shows the current configuration of the logging system.  This includes
+the status of all logging destinations.
+@end deffn
+
+@deffn Command {logmsg @var{level} @var{message}} {}
+Send a message to all logging destinations that are enabled for messages
+of the given severity.
+@end deffn
+
+
+
+
+@node Common Invocation Options
+@section Common Invocation Options
+@c COMMON_OPTIONS
+@c OPTIONS section of the man page
+
+These options apply to all @value{PACKAGE_NAME} daemons.
+
+@table @samp
+
+@item -d
+@itemx --daemon
+Runs in daemon mode.
+
+@item -f @var{file}
+@itemx --config_file=@var{file}
+Set configuration file name.
+
+@item -h
+@itemx --help
+Display this help and exit.
+
+@item -i @var{file}
+@itemx --pid_file=@var{file}
+
+Upon startup the process identifier of the daemon is written to a file,
+typically in @file{/var/run}.  This file can be used by the init system
+to implement commands such as @command{@dots{}/init.d/zebra status},
+@command{@dots{}/init.d/zebra restart} or @command{@dots{}/init.d/zebra
+stop}.
+
+The file name is an run-time option rather than a configure-time option
+so that multiple routing daemons can be run simultaneously.  This is
+useful when using @value{PACKAGE_NAME} to implement a routing looking glass.  One
+machine can be used to collect differing routing views from differing
+points in the network.
+
+@item -A @var{address}
+@itemx --vty_addr=@var{address}
+Set the VTY local address to bind to. If set, the VTY socket will only
+be bound to this address. 
+
+@item -P @var{port}
+@itemx --vty_port=@var{port}
+Set the VTY TCP port number. If set to 0 then the TCP VTY sockets will not
+be opened.
+
+@item -u @var{user}
+@itemx --vty_addr=@var{user}
+Set the user and group to run as.
+
+@item -v
+@itemx --version
+Print program version.
+
+@end table
+
+
+
+@node Virtual Terminal Interfaces
+@section Virtual Terminal Interfaces
+
+VTY -- Virtual Terminal [aka TeletYpe] Interface is a command line
+interface (CLI) for user interaction with the routing daemon.
+
+@menu
+* VTY Overview::                Basics about VTYs                
+* VTY Modes::                   View, Enable, and Other VTY modes
+* VTY CLI Commands::            Commands for movement, edition, and management
+@end menu
+
+
+
+@node VTY Overview
+@subsection VTY Overview
+
+
+VTY stands for Virtual TeletYpe interface.  It means you can connect to
+the daemon via the telnet protocol.
+
+To enable a VTY interface, you have to setup a VTY password.  If there
+is no VTY password, one cannot connect to the VTY interface at all.
+
+@example
+@group
+% telnet localhost 2601
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+
+Hello, this is @value{PACKAGE_NAME} (version @value{VERSION})
+@value{COPYRIGHT_STR}
+
+User Access Verification
+
+Password: XXXXX
+Router> ?
+  enable            Turn on privileged commands
+  exit              Exit current mode and down to previous mode
+  help              Description of the interactive help system
+  list              Print command list
+  show              Show running system information
+  who               Display who is on a vty
+Router> enable
+Password: XXXXX
+Router# configure terminal
+Router(config)# interface eth0
+Router(config-if)# ip address 10.0.0.1/8
+Router(config-if)# ^Z
+Router#
+@end group
+@end example
+
+'?' is very useful for looking up commands.
+
+@node VTY Modes
+@subsection VTY Modes
+
+There are three basic VTY modes:
+
+@menu
+* VTY View Mode::               Mode for read-only interaction               
+* VTY Enable Mode::             Mode for read-write interaction
+* VTY Other Modes::             Special modes (tftp, etc)
+@end menu
+
+There are commands that may be restricted to specific VTY modes. 
+
+@node VTY View Mode
+@subsubsection VTY View Mode
+@c to be written (gpoul)
+
+
+This mode is for read-only access to the CLI. One may exit the mode by
+leaving the system, or by entering @code{enable} mode.
+
+@node VTY Enable Mode
+@subsubsection VTY Enable Mode
+
+@c to be written (gpoul)
+This mode is for read-write access to the CLI. One may exit the mode by
+leaving the system, or by escaping to view mode.
+
+@node VTY Other Modes
+@subsubsection VTY Other Modes
+
+
+@c to be written (gpoul)
+This page is for describing other modes.
+
+@node VTY CLI Commands
+@subsection VTY CLI Commands
+
+Commands that you may use at the command-line are described in the following
+three subsubsections.
+
+@menu
+* CLI Movement Commands::       Commands for moving the cursor about
+* CLI Editing Commands::        Commands for changing text
+* CLI Advanced Commands::       Other commands, session management and so on
+@end menu
+
+@node CLI Movement Commands
+@subsubsection CLI Movement Commands
+
+These commands are used for moving the CLI cursor. The @key{C} character
+means press the Control Key.
+
+@table @kbd
+
+@item C-f
+@itemx @key{RIGHT}
+@kindex C-f
+@kindex @key{RIGHT}
+Move forward one character.
+
+@item C-b
+@itemx @key{LEFT}
+@kindex C-b
+@kindex @key{LEFT}
+Move backward one character.
+
+@item M-f
+@kindex M-f
+Move forward one word.
+
+@item M-b
+@kindex M-b
+Move backward one word.
+
+@item C-a
+@kindex C-a
+Move to the beginning of the line.
+
+@item C-e
+@kindex C-e
+Move to the end of the line.
+
+@end table
+
+@node CLI Editing Commands
+@subsubsection CLI Editing Commands
+
+These commands are used for editing text on a line. The @key{C}
+character means press the Control Key.
+
+@table @kbd
+
+@item C-h
+@itemx @key{DEL}
+@kindex C-h
+@kindex @key{DEL}
+Delete the character before point.
+
+@item C-d
+@kindex C-d
+Delete the character after point.
+
+@item M-d
+@kindex M-d
+Forward kill word.
+
+@item C-w
+@kindex C-w
+Backward kill word.
+
+@item C-k
+@kindex C-k
+Kill to the end of the line.
+
+@item C-u
+@kindex C-u
+Kill line from the beginning, erasing input.
+
+@item C-t
+@kindex C-t
+Transpose character.
+
+@item C-v
+@kindex C-v
+Interpret following character literally. Do not treat it specially.
+This can be used to, e.g., type in a literal @kbd{?} rather than do
+help completion.
+
+@end table
+
+@node CLI Advanced Commands
+@subsubsection CLI Advanced Commands
+
+There are several additional CLI commands for command line completions,
+insta-help, and VTY session management.
+
+@table @kbd
+
+@item C-c
+@kindex C-c
+Interrupt current input and moves to the next line.
+
+@item C-z
+@kindex C-z
+End current configuration session and move to top node.
+
+
+@item C-n
+@itemx @key{DOWN}
+@kindex C-n
+@kindex @key{DOWN}
+Move down to next line in the history buffer.
+
+@item C-p
+@itemx @key{UP}
+@kindex C-p
+@kindex @key{UP}
+Move up to previous line in the history buffer.
+
+@item TAB
+@kindex @key{TAB}
+Use command line completion by typing @key{TAB}.
+
+@item ?
+@kindex @key{?}
+You can use command line help by typing @code{help} at the beginning of
+the line.  Typing @kbd{?} at any point in the line will show possible
+completions.
+
+To enter an actual @kbd{?} character rather show completions, e.g. to
+enter into a regexp, use @kbd{@key{C}-v ?}.
+
+@end table
diff --git a/doc/bgpd.8 b/doc/bgpd.8
new file mode 100644 (file)
index 0000000..e680ddb
--- /dev/null
@@ -0,0 +1,125 @@
+.TH BGPD 8 "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
+
+.SH SYNOPSIS
+.B bgpd
+[
+.B \-dhrSv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-p
+.I bgp-port-number
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B bgpd 
+is a routing component that works with the 
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B bgpd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/bgpd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When bgpd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart bgpd.  The likely default is \fB\fI/var/run/bgpd.pid\fR.
+.TP
+\fB\-p\fR, \fB\-\-bgp_port \fR\fIbgp-port-number\fR
+Set the port that bgpd will listen to for bgp data.  
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the bgpd VTY will listen on. This defaults to
+2605, as specified in \fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the bgpd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-r\fR, \fB\-\-retain\fR 
+When the program terminates, retain routes added by \fBbgpd\fR.
+.TP
+\fB\-S\fR, \fB\-\-skip_runas\fR 
+Skip setting the process effective user and group.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/bgpd
+The default location of the 
+.B bgpd
+binary.
+.TP
+.BI /usr/local/etc/bgpd.conf
+The default location of the 
+.B bgpd
+config file.
+.TP
+.BI $(PWD)/bgpd.log 
+If the 
+.B bgpd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBbgpd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The bgpd process may log to standard output, to a VTY, to a log
+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 nhrpd (8),
+.BR zebra (8),
+.BR vtysh (1)
+.SH BUGS
+.B bgpd
+eats bugs for breakfast. If you have food for the maintainers try 
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/bgpd.texi b/doc/bgpd.texi
new file mode 100644 (file)
index 0000000..d5aa30c
--- /dev/null
@@ -0,0 +1,1976 @@
+@c -*-texinfo-*-
+@c This is part of the Quagga Manual.
+@c @value{COPYRIGHT_STR}
+@c Portions: 
+@c   Copyright @copyright{} 2015 Hewlett Packard Enterprise Development LP
+@c See file quagga.texi for copying conditions.
+@node BGP
+@chapter BGP
+
+@acronym{BGP} stands for a Border Gateway Protocol.  The lastest BGP version
+is 4.  It is referred as BGP-4.  BGP-4 is one of the Exterior Gateway
+Protocols and de-fact standard of Inter Domain routing protocol.
+BGP-4 is described in @cite{RFC1771, A Border Gateway Protocol
+4 (BGP-4)}.
+
+Many extensions have been added to @cite{RFC1771}.  @cite{RFC2858,
+Multiprotocol Extensions for BGP-4} provides multiprotocol support to
+BGP-4.
+
+@menu
+* Starting BGP::                
+* BGP router::                  
+* BGP MED::
+* BGP network::                 
+* BGP Peer::                    
+* BGP Peer Group::              
+* BGP Address Family::          
+* Autonomous System::           
+* BGP Communities Attribute::   
+* BGP Extended Communities Attribute::  
+* Displaying BGP routes::       
+* Capability Negotiation::      
+* Route Reflector::             
+* Route Server::                
+* How to set up a 6-Bone connection::  
+* Dump BGP packets and table::  
+* BGP Configuration Examples::
+@end menu
+
+@node Starting BGP
+@section Starting BGP
+
+Default configuration file of @command{bgpd} is @file{bgpd.conf}.
+@command{bgpd} searches the current directory first then
+@value{INSTALL_PREFIX_ETC}/bgpd.conf.  All of bgpd's command must be
+configured in @file{bgpd.conf}.
+
+@command{bgpd} specific invocation options are described below.  Common
+options may also be specified (@pxref{Common Invocation Options}).
+
+@table @samp
+@item -p @var{PORT}
+@itemx --bgp_port=@var{PORT}
+Set the bgp protocol's port number.
+
+@item -r
+@itemx --retain
+When program terminates, retain BGP routes added by zebra.
+
+@item -l
+@itemx --listenon
+Specify a specific IP address for bgpd to listen on, rather than its 
+default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd
+to an internal address, or to run multiple bgpd processes on one host.
+
+@end table
+
+@node BGP router
+@section BGP router
+
+  First of all you must configure BGP router with @command{router bgp}
+command.  To configure BGP router, you need AS number.  AS number is an
+identification of autonomous system.  BGP protocol uses the AS number
+for detecting whether the BGP connection is internal one or external one.
+
+@deffn Command {router bgp @var{asn}} {}
+Enable a BGP protocol process with the specified @var{asn}.  After
+this statement you can input any @code{BGP Commands}.  You can not
+create different BGP process under different @var{asn} without
+specifying @code{multiple-instance} (@pxref{Multiple instance}).
+@end deffn
+
+@deffn Command {no router bgp @var{asn}} {}
+Destroy a BGP protocol process with the specified @var{asn}.
+@end deffn
+
+@deffn {BGP} {bgp router-id @var{A.B.C.D}} {}
+This command specifies the router-ID.  If @command{bgpd} connects to @command{zebra} it gets
+interface and address information.  In that case default router ID value
+is selected as the largest IP Address of the interfaces.  When
+@code{router zebra} is not enabled @command{bgpd} can't get interface information
+so @code{router-id} is set to 0.0.0.0.  So please set router-id by hand.
+@end deffn
+
+@menu
+* BGP distance::                
+* BGP decision process::        
+* BGP route flap dampening::      
+@end menu
+
+@node BGP distance
+@subsection BGP distance
+
+@deffn {BGP} {distance bgp <1-255> <1-255> <1-255>} {}
+This command change distance value of BGP.  Each argument is distance
+value for external routes, internal routes and local routes.
+@end deffn
+
+@deffn {BGP} {distance <1-255> @var{A.B.C.D/M}} {}
+@deffnx {BGP} {distance <1-255> @var{A.B.C.D/M} @var{word}} {}
+This command set distance value to 
+@end deffn
+
+@node BGP decision process
+@subsection BGP decision process
+
+The decision process Quagga BGP uses to select routes is as follows:
+
+@table @asis
+@item 1. Weight check
+prefer higher local weight routes to lower routes.
+  
+@item 2. Local preference check
+prefer higher local preference routes to lower.
+
+@item 3. Local route check
+Prefer local routes (statics, aggregates, redistributed) to received routes.
+
+@item 4. AS path length check
+Prefer shortest hop-count AS_PATHs. 
+
+@item 5. Origin check
+Prefer the lowest origin type route.  That is, prefer IGP origin routes to
+EGP, to Incomplete routes. 
+
+@item 6. MED check
+Where routes with a MED were received from the same AS,
+prefer the route with the lowest MED. @xref{BGP MED}.
+
+@item 7. External check
+Prefer the route received from an external, eBGP peer
+over routes received from other types of peers.
+
+@item 8. IGP cost check
+Prefer the route with the lower IGP cost.
+
+@item 9. Multi-path check
+If multi-pathing is enabled, then check whether
+the routes not yet distinguished in preference may be considered equal. If
+@ref{bgp bestpath as-path multipath-relax} is set, all such routes are
+considered equal, otherwise routes received via iBGP with identical AS_PATHs
+or routes received from eBGP neighbours in the same AS are considered equal.
+
+@item 10 Already-selected external check
+
+Where both routes were received from eBGP peers, then prefer the route which
+is already selected.  Note that this check is not applied if @ref{bgp
+bestpath compare-routerid} is configured.  This check can prevent some cases
+of oscillation.
+
+@item 11. Router-ID check
+Prefer the route with the lowest @w{router-ID}.  If the
+route has an @w{ORIGINATOR_ID} attribute, through iBGP reflection, then that
+router ID is used, otherwise the @w{router-ID} of the peer the route was
+received from is used.
+
+@item 12. Cluster-List length check
+The route with the shortest cluster-list
+length is used.  The cluster-list reflects the iBGP reflection path the
+route has taken.
+
+@item 13. Peer address
+Prefer the route received from the peer with the higher
+transport layer address, as a last-resort tie-breaker.
+
+@end table
+
+@deffn {BGP} {bgp bestpath as-path confed} {}
+This command specifies that the length of confederation path sets and
+sequences should should be taken into account during the BGP best path
+decision process.
+@end deffn
+
+@deffn {BGP} {bgp bestpath as-path multipath-relax} {}
+@anchor{bgp bestpath as-path multipath-relax}
+This command specifies that BGP decision process should consider paths
+of equal AS_PATH length candidates for multipath computation. Without
+the knob, the entire AS_PATH must match for multipath computation.
+@end deffn
+
+@deffn {BGP} {bgp bestpath compare-routerid} {}
+@anchor{bgp bestpath compare-routerid}
+
+Ensure that when comparing routes where both are equal on most metrics,
+including local-pref, AS_PATH length, IGP cost, MED, that the tie is broken
+based on router-ID.
+
+If this option is enabled, then the already-selected check, where
+already selected eBGP routes are preferred, is skipped.
+
+If a route has an @w{ORIGINATOR_ID} attribute because it has been reflected,
+that @w{ORIGINATOR_ID} will be used.  Otherwise, the router-ID of the peer the
+route was received from will be used.
+
+The advantage of this is that the route-selection (at this point) will be
+more deterministic.  The disadvantage is that a few or even one lowest-ID
+router may attract all trafic to otherwise-equal paths because of this
+check.  It may increase the possibility of MED or IGP oscillation, unless
+other measures were taken to avoid these.  The exact behaviour will be
+sensitive to the iBGP and reflection topology.
+
+@end deffn
+
+
+@node BGP route flap dampening
+@subsection BGP route flap dampening
+
+@deffn {BGP} {bgp dampening @var{<1-45>} @var{<1-20000>} @var{<1-20000>} @var{<1-255>}} {}
+This command enables BGP route-flap dampening and specifies dampening parameters.
+
+@table @asis
+@item @asis{half-life}
+Half-life time for the penalty
+@item @asis{reuse-threshold}
+Value to start reusing a route
+@item @asis{suppress-threshold}
+Value to start suppressing a route
+@item @asis{max-suppress}
+Maximum duration to suppress a stable route
+@end table
+
+The route-flap damping algorithm is compatible with @cite{RFC2439}. The use of this command
+is not recommended nowadays, see @uref{http://www.ripe.net/ripe/docs/ripe-378,,RIPE-378}.
+@end deffn
+
+@node BGP MED
+@section BGP MED
+
+The BGP MED (Multi_Exit_Discriminator) attribute has properties which can
+cause subtle convergence problems in BGP.  These properties and problems
+have proven to be hard to understand, at least historically, and may still
+not be widely understood.  The following attempts to collect together and
+present what is known about MED, to help operators and Quagga users in
+designing and configuring their networks.
+
+The BGP @acronym{MED, Multi_Exit_Discriminator} attribute is intended to
+allow one AS to indicate its preferences for its ingress points to another
+AS.  The MED attribute will not be propagated on to another AS by the
+receiving AS - it is `non-transitive' in the BGP sense.
+
+E.g., if AS X and AS Y have 2 different BGP peering points, then AS X
+might set a MED of 100 on routes advertised at one and a MED of 200 at the
+other.  When AS Y selects between otherwise equal routes to or via
+AS X, AS Y should prefer to take the path via the lower MED peering of 100 with
+AS X.  Setting the MED allows an AS to influence the routing taken to it
+within another, neighbouring AS.
+
+In this use of MED it is not really meaningful to compare the MED value on
+routes where the next AS on the paths differs.  E.g., if AS Y also had a
+route for some destination via AS Z in addition to the routes from AS X, and
+AS Z had also set a MED, it wouldn't make sense for AS Y to compare AS Z's
+MED values to those of AS X.  The MED values have been set by different
+administrators, with different frames of reference.
+
+The default behaviour of BGP therefore is to not compare MED values across
+routes received from different neighbouring ASes.  In Quagga this is done by
+comparing the neighbouring, left-most AS in the received AS_PATHs of the
+routes and only comparing MED if those are the same.
+
+@c TeXInfo uses the old, non-UTF-8 capable, pdftex, and so 
+@c doesn't render TeX the unicode precedes character correctly in PDF, etc.
+@c Using a TeX code on the other hand doesn't work for non-TeX outputs
+@c (plaintext, e.g.). So, use an output-conditional macro.
+
+@iftex
+@macro mprec{}
+@math{\\prec}
+@end macro
+@end iftex
+
+@ifnottex
+@macro mprec{}
+@math{≺}
+@end macro
+@end ifnottex
+
+Unfortunately, this behaviour of MED, of sometimes being compared across
+routes and sometimes not, depending on the properties of those other routes,
+means MED can cause the order of preference over all the routes to be
+undefined.  That is, given routes A, B, and C, if A is preferred to B, and B
+is preferred to C, then a well-defined order should mean the preference is
+transitive (in the sense of orders @footnote{For some set of objects to have
+an order, there @emph{must} be some binary ordering relation that is defined
+for @emph{every} combination of those objects, and that relation @emph{must}
+be transitive.  I.e.@:, if the relation operator is @mprec{}, and if 
+a @mprec{} b and b @mprec{} c then that relation must carry over
+and it @emph{must} be that a @mprec{} c for the objects to have an
+order.  The ordering relation may allow for equality, i.e. 
+a @mprec{} b and b @mprec{} a may both be true amd imply that
+a and b are equal in the order and not distinguished by it, in
+which case the set has a partial order.  Otherwise, if there is an order,
+all the objects have a distinct place in the order and the set has a total
+order.}) and that A would be preferred to C.
+
+However, when MED is involved this need not be the case.  With MED it is
+possible that C is actually preferred over A.  So A is preferred to B, B is
+preferred to C, but C is preferred to A.  This can be true even where BGP
+defines a deterministic ``most preferred'' route out of the full set of
+A,B,C.  With MED, for any given set of routes there may be a
+deterministically preferred route, but there need not be any way to arrange
+them into any order of preference.  With unmodified MED, the order of
+preference of routes literally becomes undefined.
+
+That MED can induce non-transitive preferences over routes can cause issues. 
+Firstly, it may be perceived to cause routing table churn locally at
+speakers; secondly, and more seriously, it may cause routing instability in
+iBGP topologies, where sets of speakers continually oscillate between
+different paths.
+
+The first issue arises from how speakers often implement routing decisions. 
+Though BGP defines a selection process that will deterministically select
+the same route as best at any given speaker, even with MED, that process
+requires evaluating all routes together.  For performance and ease of
+implementation reasons, many implementations evaluate route preferences in a
+pair-wise fashion instead.  Given there is no well-defined order when MED is
+involved, the best route that will be chosen becomes subject to
+implementation details, such as the order the routes are stored in.  That
+may be (locally) non-deterministic, e.g.@: it may be the order the routes
+were received in.  
+
+This indeterminism may be considered undesirable, though it need not cause
+problems.  It may mean additional routing churn is perceived, as sometimes
+more updates may be produced than at other times in reaction to some event .
+
+This first issue can be fixed with a more deterministic route selection that
+ensures routes are ordered by the neighbouring AS during selection. 
+@xref{bgp deterministic-med}.  This may reduce the number of updates as
+routes are received, and may in some cases reduce routing churn.  Though, it
+could equally deterministically produce the largest possible set of updates
+in response to the most common sequence of received updates.
+
+A deterministic order of evaluation tends to imply an additional overhead of
+sorting over any set of n routes to a destination.  The implementation of
+deterministic MED in Quagga scales significantly worse than most sorting
+algorithms at present, with the number of paths to a given destination. 
+That number is often low enough to not cause any issues, but where there are
+many paths, the deterministic comparison may quickly become increasingly
+expensive in terms of CPU.
+
+Deterministic local evaluation can @emph{not} fix the second, more major,
+issue of MED however.  Which is that the non-transitive preference of routes
+MED can cause may lead to routing instability or oscillation across multiple
+speakers in iBGP topologies.  This can occur with full-mesh iBGP, but is
+particularly problematic in non-full-mesh iBGP topologies that further
+reduce the routing information known to each speaker.  This has primarily
+been documented with iBGP route-reflection topologies.  However, any
+route-hiding technologies potentially could also exacerbate oscillation with
+MED.
+
+This second issue occurs where speakers each have only a subset of routes,
+and there are cycles in the preferences between different combinations of
+routes - as the undefined order of preference of MED allows - and the routes
+are distributed in a way that causes the BGP speakers to 'chase' those
+cycles.  This can occur even if all speakers use a deterministic order of
+evaluation in route selection.
+
+E.g., speaker 4 in AS A might receive a route from speaker 2 in AS X, and
+from speaker 3 in AS Y; while speaker 5 in AS A might receive that route
+from speaker 1 in AS Y.  AS Y might set a MED of 200 at speaker 1, and 100
+at speaker 3. I.e, using ASN:ID:MED to label the speakers:
+
+@example
+
+           /---------------\
+ X:2------|--A:4-------A:5--|-Y:1:200
+ Y:3:100--|-/               |
+           \---------------/
+
+@end example
+
+Assuming all other metrics are equal (AS_PATH, ORIGIN, 0 IGP costs), then
+based on the RFC4271 decision process speaker 4 will choose X:2 over
+Y:3:100, based on the lower ID of 2.  Speaker 4 advertises X:2 to speaker 5. 
+Speaker 5 will continue to prefer Y:1:200 based on the ID, and advertise
+this to speaker 4.  Speaker 4 will now have the full set of routes, and the
+Y:1:200 it receives from 5 will beat X:2, but when speaker 4 compares
+Y:1:200 to Y:3:100 the MED check now becomes active as the ASes match, and
+now Y:3:100 is preferred.  Speaker 4 therefore now advertises Y:3:100 to 5,
+which will also agrees that Y:3:100 is preferred to Y:1:200, and so
+withdraws the latter route from 4.  Speaker 4 now has only X:2 and Y:3:100,
+and X:2 beats Y:3:100, and so speaker 4 implicitly updates its route to
+speaker 5 to X:2.  Speaker 5 sees that Y:1:200 beats X:2 based on the ID,
+and advertises Y:1:200 to speaker 4, and the cycle continues.
+
+The root cause is the lack of a clear order of preference caused by how MED
+sometimes is and sometimes is not compared, leading to this cycle in the
+preferences between the routes:
+
+@example
+
+       /---> X:2 ---beats---> Y:3:100 --\
+      |                                  |
+      |                                  |
+       \---beats--- Y:1:200 <---beats---/
+
+@end example
+
+This particular type of oscillation in full-mesh iBGP topologies can  be
+avoided by speakers preferring already selected, external routes rather than
+choosing to update to new a route based on a post-MED metric (e.g. 
+router-ID), at the cost of a non-deterministic selection process.  Quagga
+implements this, as do many other implementations, so long as it is not
+overridden by setting @ref{bgp bestpath compare-routerid}, and see also
+@ref{BGP decision process}, .
+
+However, more complex and insidious cycles of oscillation are possible with
+iBGP route-reflection, which are not so easily avoided.  These have been
+documented in various places.  See, e.g., @cite{McPherson, D.  and Gill, V. 
+and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation
+Condition", IETF RFC3345}, and @cite{Flavel, A.  and M.  Roughan, "Stable
+and flexible iBGP", ACM SIGCOMM 2009}, and @cite{Griffin, T.  and G.  Wilfong, 
+"On the correctness of IBGP configuration", ACM SIGCOMM 2002} for concrete 
+examples and further references.
+
+There is as of this writing @emph{no} known way to use MED for its original
+purpose; @emph{and} reduce routing information in iBGP topologies;
+@emph{and} be sure to avoid the instability problems of MED due the
+non-transitive routing preferences it can induce; in general on arbitrary
+networks.
+
+There may be iBGP topology specific ways to reduce the instability risks,
+even while using MED, e.g.@: by constraining the reflection topology and by
+tuning IGP costs between route-reflector clusters, see RFC3345 for details. 
+In the near future, the Add-Path extension to BGP may also solve MED
+oscillation while still allowing MED to be used as intended, by distributing
+"best-paths per neighbour AS".  This would be at the cost of distributing at
+least as many routes to all speakers as a full-mesh iBGP would, if not more,
+while also imposing similar CPU overheads as the "Deterministic MED" feature
+at each Add-Path reflector.
+
+More generally, the instability problems that MED can introduce on more
+complex, non-full-mesh, iBGP topologies may be avoided either by:
+
+@itemize 
+
+@item
+Setting @ref{bgp always-compare-med}, however this allows MED to be compared
+across values set by different neighbour ASes, which may not produce
+coherent desirable results, of itself.
+
+@item 
+Effectively ignoring MED by setting MED to the same value (e.g.@: 0) using
+@ref{routemap set metric} on all received routes, in combination with
+setting @ref{bgp always-compare-med} on all speakers. This is the simplest
+and most performant way to avoid MED oscillation issues, where an AS is happy
+not to allow neighbours to inject this problematic metric.
+
+@end itemize
+
+As MED is evaluated after the AS_PATH length check, another possible use for
+MED is for intra-AS steering of routes with equal AS_PATH length, as an
+extension of the last case above.  As MED is evaluated before IGP metric,
+this can allow cold-potato routing to be implemented to send traffic to
+preferred hand-offs with neighbours, rather than the closest hand-off
+according to the IGP metric.
+
+Note that even if action is taken to address the MED non-transitivity
+issues, other oscillations may still be possible.  E.g., on IGP cost if
+iBGP and IGP topologies are at cross-purposes with each other - see the
+Flavel and Roughan paper above for an example.  Hence the guideline that the
+iBGP topology should follow the IGP topology.
+
+@deffn {BGP} {bgp deterministic-med} {}
+@anchor{bgp deterministic-med}
+
+Carry out route-selection in way that produces deterministic answers
+locally, even in the face of MED and the lack of a well-defined order of
+preference it can induce on routes.  Without this option the preferred route
+with MED may be determined largely by the order that routes were received
+in.
+
+Setting this option will have a performance cost that may be noticeable when
+there are many routes for each destination.  Currently in Quagga it is
+implemented in a way that scales poorly as the number of routes per
+destination increases.
+
+The default is that this option is not set.
+@end deffn
+
+Note that there are other sources of indeterminism in the route selection
+process, specifically, the preference for older and already selected routes
+from eBGP peers, @xref{BGP decision process}.
+
+@deffn {BGP} {bgp always-compare-med} {}
+@anchor{bgp always-compare-med}
+
+Always compare the MED on routes, even when they were received from
+different neighbouring ASes.  Setting this option makes the order of
+preference of routes more defined, and should eliminate MED induced
+oscillations.
+
+If using this option, it may also be desirable to use @ref{routemap set
+metric} to set MED to 0 on routes received from external neighbours.
+
+This option can be used, together with @ref{routemap set metric} to use MED
+as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired
+exit points.
+@end deffn
+
+
+
+@node BGP network
+@section BGP network
+
+@menu
+* BGP route::                   
+* Route Aggregation::           
+* Redistribute to BGP::         
+@end menu
+
+@node BGP route
+@subsection BGP route
+
+@deffn {BGP} {network @var{A.B.C.D/M}} {}
+This command adds the announcement network.
+@example
+@group
+router bgp 1
+ network 10.0.0.0/8
+@end group
+@end example
+This configuration example says that network 10.0.0.0/8 will be
+announced to all neighbors.  Some vendors' routers don't advertise
+routes if they aren't present in their IGP routing tables; @code{bgpd}
+doesn't care about IGP routes when announcing its routes.
+@end deffn
+
+@deffn {BGP} {no network @var{A.B.C.D/M}} {}
+@end deffn
+
+@node Route Aggregation
+@subsection Route Aggregation
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M}} {}
+This command specifies an aggregate address.
+@end deffn
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M} as-set} {}
+This command specifies an aggregate address.  Resulting routes include
+AS set.
+@end deffn
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M} summary-only} {}
+This command specifies an aggregate address.  Aggreated routes will
+not be announce.
+@end deffn
+
+@deffn {BGP} {no aggregate-address @var{A.B.C.D/M}} {}
+@end deffn
+
+@node Redistribute to BGP
+@subsection Redistribute to BGP
+
+@deffn {BGP} {redistribute kernel} {}
+Redistribute kernel route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute static} {}
+Redistribute static route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute connected} {}
+Redistribute connected route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute rip} {}
+Redistribute RIP route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute ospf} {}
+Redistribute OSPF route to BGP process.
+@end deffn
+
+@node BGP Peer
+@section BGP Peer
+
+@menu
+* Defining Peer::               
+* BGP Peer commands::           
+* Peer filtering::              
+@end menu
+
+@node Defining Peer
+@subsection Defining Peer
+
+@deffn {BGP} {neighbor @var{peer} remote-as @var{asn}} {}
+Creates a new neighbor whose remote-as is @var{asn}.  @var{peer}
+can be an IPv4 address or an IPv6 address.
+@example
+@group
+router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+@end group
+@end example
+In this case my router, in AS-1, is trying to peer with AS-2 at
+10.0.0.1.
+
+This command must be the first command used when configuring a neighbor.
+If the remote-as is not specified, @command{bgpd} will complain like this:
+@example
+can't find neighbor 10.0.0.1
+@end example
+@end deffn
+
+@node BGP Peer commands
+@subsection BGP Peer commands
+
+In a @code{router bgp} clause there are neighbor specific configurations
+required.
+
+@deffn {BGP} {neighbor @var{peer} shutdown} {}
+@deffnx {BGP} {no neighbor @var{peer} shutdown} {}
+Shutdown the peer.  We can delete the neighbor's configuration by
+@code{no neighbor @var{peer} remote-as @var{as-number}} but all
+configuration of the neighbor will be deleted.  When you want to
+preserve the configuration, but want to drop the BGP peer, use this
+syntax.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} ebgp-multihop} {}
+@deffnx {BGP} {no neighbor @var{peer} ebgp-multihop} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} description ...} {}
+@deffnx {BGP} {no neighbor @var{peer} description ...} {}
+Set description of the peer.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} version @var{version}} {}
+Set up the neighbor's BGP version.  @var{version} can be @var{4},
+@var{4+} or @var{4-}.  BGP version @var{4} is the default value used for
+BGP peering.  BGP version @var{4+} means that the neighbor supports
+Multiprotocol Extensions for BGP-4.  BGP version @var{4-} is similar but
+the neighbor speaks the old Internet-Draft revision 00's Multiprotocol
+Extensions for BGP-4.  Some routing software is still using this
+version.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} interface @var{ifname}} {}
+@deffnx {BGP} {no neighbor @var{peer} interface @var{ifname}} {}
+When you connect to a BGP peer over an IPv6 link-local address, you 
+have to specify the @var{ifname} of the interface used for the 
+connection. To specify IPv4 session addresses, see the 
+@code{neighbor @var{peer} update-source} command below.
+
+This command is deprecated and may be removed in a future release. Its
+use should be avoided.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} next-hop-self [all]} {}
+@deffnx {BGP} {no neighbor @var{peer} next-hop-self [all]} {}
+This command specifies an announced route's nexthop as being equivalent
+to the address of the bgp router if it is learned via eBGP.
+If the optional keyword @code{all} is specified the modifiation is done
+also for routes learned via iBGP.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} update-source @var{<ifname|address>}} {}
+@deffnx {BGP} {no neighbor @var{peer} update-source} {}
+Specify the IPv4 source address to use for the @acronym{BGP} session to this
+neighbour, may be specified as either an IPv4 address directly or
+as an interface name (in which case the @command{zebra} daemon MUST be running
+in order for @command{bgpd} to be able to retrieve interface state).
+@example
+@group
+router bgp 64555
+ neighbor foo update-source 192.168.0.1
+ neighbor bar update-source lo0
+@end group
+@end example
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} default-originate} {}
+@deffnx {BGP} {no neighbor @var{peer} default-originate} {}
+@command{bgpd}'s default is to not announce the default route (0.0.0.0/0) even it
+is in routing table.  When you want to announce default routes to the
+peer, use this command.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} port @var{port}} {}
+@deffnx {BGP} {neighbor @var{peer} port @var{port}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} send-community} {}
+@deffnx {BGP} {neighbor @var{peer} send-community} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} weight @var{weight}} {}
+@deffnx {BGP} {no neighbor @var{peer} weight @var{weight}} {}
+This command specifies a default @var{weight} value for the neighbor's
+routes.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} maximum-prefix @var{number}} {}
+@deffnx {BGP} {no neighbor @var{peer} maximum-prefix @var{number}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} local-as @var{as-number}} {}
+@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend} {}
+@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend replace-as} {}
+@deffnx {BGP} {no neighbor @var{peer} local-as} {}
+Specify an alternate AS for this BGP process when interacting with the
+specified peer.  With no modifiers, the specified local-as is prepended to
+the received AS_PATH when receiving routing updates from the peer, and
+prepended to the outgoing AS_PATH (after the process local AS) when
+transmitting local routes to the peer.
+
+If the no-prepend attribute is specified, then the supplied local-as is not
+prepended to the received AS_PATH.
+
+If the replace-as attribute is specified, then only the supplied local-as is
+prepended to the AS_PATH when transmitting local-route updates to this peer.
+
+Note that replace-as can only be specified if no-prepend is.
+
+This command is only allowed for eBGP peers.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} ttl-security hops @var{number}} {}
+@deffnx {BGP} {no neighbor @var{peer} ttl-security hops @var{number}} {}
+This command enforces Generalized TTL Security Mechanism (GTSM), as
+specified in RFC 5082. With this command, only neighbors that are the
+specified number of hops away will be allowed to become neighbors. This
+command is mututally exclusive with @command{ebgp-multihop}.
+@end deffn
+
+@node Peer filtering
+@subsection Peer filtering
+
+@deffn {BGP} {neighbor @var{peer} distribute-list @var{name} [in|out]} {}
+This command specifies a distribute-list for the peer.  @var{direct} is
+@samp{in} or @samp{out}.
+@end deffn
+
+@deffn {BGP command} {neighbor @var{peer} prefix-list @var{name} [in|out]} {}
+@end deffn
+
+@deffn {BGP command} {neighbor @var{peer} filter-list @var{name} [in|out]} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} route-map @var{name} [in|out]} {}
+Apply a route-map on the neighbor.  @var{direct} must be @code{in} or
+@code{out}.
+@end deffn
+
+@deffn {BGP} {bgp route-reflector allow-outbound-policy} {}
+By default, attribute modification via route-map policy out is not reflected
+on reflected routes. This option allows the modifications to be reflected as
+well. Once enabled, it affects all reflected routes.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node BGP Peer Group
+@section BGP Peer Group
+
+@deffn {BGP} {neighbor @var{word} peer-group} {}
+This command defines a new peer group.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} peer-group @var{word}} {}
+This command bind specific peer to peer group @var{word}.
+@end deffn
+
+@node BGP Address Family
+@section BGP Address Family
+
+Multiprotocol BGP enables BGP to carry routing information for multiple
+Network Layer protocols. BGP supports multiple Address Family
+Identifier (AFI), namely IPv4 and IPv6. Support is also provided for
+multiple sets of per-AFI information via Subsequent Address Family
+Identifiers (SAFI).  In addition to unicast information, VPN information
+@cite{RFC4364} and @cite{RFC4659}, and Encapsulation information
+@cite{RFC5512} is supported.
+
+@deffn {Command} {show ip bgp vpnv4 all} {}
+@deffnx {Command} {show ipv6 bgp vpn all} {}
+Print active IPV4 or IPV6 routes advertised via the VPN SAFI.
+@end deffn
+
+@deffn {Command} {show ip bgp encap all} {}
+@deffnx {Command} {show ipv6 bgp encap all} {}
+Print active IPV4 or IPV6 routes advertised via the Encapsulation SAFI.
+@end deffn
+
+@deffn {Command} {show bgp ipv4 encap summary} {}
+@deffnx {Command} {show bgp ipv4 vpn summary} {}
+@deffnx {Command} {show bgp ipv6 encap summary} {}
+@deffnx {Command} {show bgp ipv6 vpn summary} {}
+Print a summary of neighbor connections for the specified AFI/SAFI combination.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node Autonomous System
+@section Autonomous System
+
+The @acronym{AS,Autonomous System} number is one of the essential
+element of BGP.  BGP is a distance vector routing protocol, and the
+AS-Path framework provides distance vector metric and loop detection to
+BGP. @cite{RFC1930, Guidelines for creation, selection, and
+registration of an Autonomous System (AS)} provides some background on
+the concepts of an AS.
+
+The AS number is a two octet value, ranging in value from 1 to 65535.
+The AS numbers 64512 through 65535 are defined as private AS numbers. 
+Private AS numbers must not to be advertised in the global Internet.
+
+@menu
+* AS Path Regular Expression::  
+* Display BGP Routes by AS Path::  
+* AS Path Access List::         
+* Using AS Path in Route Map::  
+* Private AS Numbers::          
+@end menu
+
+@node AS Path Regular Expression
+@subsection AS Path Regular Expression
+
+AS path regular expression can be used for displaying BGP routes and
+AS path access list.  AS path regular expression is based on
+@code{POSIX 1003.2} regular expressions.  Following description is
+just a subset of @code{POSIX} regular expression.  User can use full
+@code{POSIX} regular expression.  Adding to that special character '_'
+is added for AS path regular expression.
+
+@table @code
+@item .
+Matches any single character.
+@item *
+Matches 0 or more occurrences of pattern.
+@item +
+Matches 1 or more occurrences of pattern.
+@item ?
+Match 0 or 1 occurrences of pattern.
+@item ^
+Matches the beginning of the line.
+@item $
+Matches the end of the line.
+@item _
+Character @code{_} has special meanings in AS path regular expression.
+It matches to space and comma , and AS set delimiter @{ and @} and AS
+confederation delimiter @code{(} and @code{)}.  And it also matches to
+the beginning of the line and the end of the line.  So @code{_} can be
+used for AS value boundaries match.  @code{show ip bgp regexp _7675_}
+matches to all of BGP routes which as AS number include @var{7675}.
+@end table
+
+@node Display BGP Routes by AS Path
+@subsection Display BGP Routes by AS Path
+
+To show BGP routes which has specific AS path information @code{show
+ip bgp} command can be used.  
+
+@deffn Command {show ip bgp regexp @var{line}} {}
+This commands display BGP routes that matches AS path regular
+expression @var{line}.
+@end deffn
+
+@node AS Path Access List
+@subsection AS Path Access List
+
+AS path access list is user defined AS path.
+
+@deffn {Command} {ip as-path access-list @var{word} @{permit|deny@} @var{line}} {}
+This command defines a new AS path access list.
+@end deffn
+
+@deffn {Command} {no ip as-path access-list @var{word}} {}
+@deffnx {Command} {no ip as-path access-list @var{word} @{permit|deny@} @var{line}} {}
+@end deffn
+
+@node Using AS Path in Route Map
+@subsection Using AS Path in Route Map
+
+@deffn {Route Map} {match as-path @var{word}} {}
+@end deffn
+
+@deffn {Route Map} {set as-path prepend @var{as-path}} {}
+Prepend the given string of AS numbers to the AS_PATH.
+@end deffn
+
+@deffn {Route Map} {set as-path prepend last-as @var{num}} {}
+Prepend the existing last AS number (the leftmost ASN) to the AS_PATH.
+@end deffn
+
+@node Private AS Numbers
+@subsection Private AS Numbers
+
+@c -----------------------------------------------------------------------
+@node BGP Communities Attribute
+@section BGP Communities Attribute
+
+BGP communities attribute is widely used for implementing policy
+routing.  Network operators can manipulate BGP communities attribute
+based on their network policy.  BGP communities attribute is defined
+in @cite{RFC1997, BGP Communities Attribute} and
+@cite{RFC1998, An Application of the BGP Community Attribute
+in Multi-home Routing}.  It is an optional transitive attribute,
+therefore local policy can travel through different autonomous system.
+
+Communities attribute is a set of communities values.  Each
+communities value is 4 octet long.  The following format is used to
+define communities value.
+
+@table @code
+@item AS:VAL
+This format represents 4 octet communities value.  @code{AS} is high
+order 2 octet in digit format.  @code{VAL} is low order 2 octet in
+digit format.  This format is useful to define AS oriented policy
+value.  For example, @code{7675:80} can be used when AS 7675 wants to
+pass local policy value 80 to neighboring peer.
+@item internet
+@code{internet} represents well-known communities value 0.
+@item no-export
+@code{no-export} represents well-known communities value @code{NO_EXPORT}@*
+@r{(0xFFFFFF01)}.  All routes carry this value must not be advertised
+to outside a BGP confederation boundary.  If neighboring BGP peer is
+part of BGP confederation, the peer is considered as inside a BGP
+confederation boundary, so the route will be announced to the peer.
+@item no-advertise
+@code{no-advertise} represents well-known communities value
+@code{NO_ADVERTISE}@*@r{(0xFFFFFF02)}.  All routes carry this value
+must not be advertise to other BGP peers.
+@item local-AS
+@code{local-AS} represents well-known communities value
+@code{NO_EXPORT_SUBCONFED} @r{(0xFFFFFF03)}.  All routes carry this
+value must not be advertised to external BGP peers.  Even if the
+neighboring router is part of confederation, it is considered as
+external BGP peer, so the route will not be announced to the peer.
+@end table
+
+  When BGP communities attribute is received, duplicated communities
+value in the communities attribute is ignored and each communities
+values are sorted in numerical order.
+  
+@menu
+* BGP Community Lists::         
+* Numbered BGP Community Lists::  
+* BGP Community in Route Map::  
+* Display BGP Routes by Community::  
+* Using BGP Communities Attribute::  
+@end menu
+
+@node BGP Community Lists
+@subsection BGP Community Lists
+
+  BGP community list is a user defined BGP communites attribute list.
+BGP community list can be used for matching or manipulating BGP
+communities attribute in updates.
+
+There are two types of community list.  One is standard community
+list and another is expanded community list.  Standard community list
+defines communities attribute.  Expanded community list defines
+communities attribute string with regular expression.  Standard
+community list is compiled into binary format when user define it.
+Standard community list will be directly compared to BGP communities
+attribute in BGP updates.  Therefore the comparison is faster than
+expanded community list.
+
+@deffn Command {ip community-list standard @var{name} @{permit|deny@} @var{community}} {}
+This command defines a new standard community list.  @var{community}
+is communities value.  The @var{community} is compiled into community
+structure.  We can define multiple community list under same name.  In
+that case match will happen user defined order.  Once the
+community list matches to communities attribute in BGP updates it
+return permit or deny by the community list definition.  When there is
+no matched entry, deny will be returned.  When @var{community} is
+empty it matches to any routes.
+@end deffn
+
+@deffn Command {ip community-list expanded @var{name} @{permit|deny@} @var{line}} {}
+This command defines a new expanded community list.  @var{line} is a
+string expression of communities attribute.  @var{line} can include
+regular expression to match communities attribute in BGP updates.
+@end deffn
+
+@deffn Command {no ip community-list @var{name}} {}
+@deffnx Command {no ip community-list standard @var{name}} {}
+@deffnx Command {no ip community-list expanded @var{name}} {}
+These commands delete community lists specified by @var{name}.  All of
+community lists shares a single name space.  So community lists can be
+removed simpley specifying community lists name.
+@end deffn
+
+@deffn {Command} {show ip community-list} {}
+@deffnx {Command} {show ip community-list @var{name}} {}
+This command display current community list information.  When
+@var{name} is specified the specified community list's information is
+shown.
+
+@example
+# show ip community-list 
+Named Community standard list CLIST
+    permit 7675:80 7675:100 no-export
+    deny internet
+Named Community expanded list EXPAND
+    permit :
+
+# show ip community-list CLIST
+Named Community standard list CLIST
+    permit 7675:80 7675:100 no-export
+    deny internet
+@end example
+@end deffn
+
+@node Numbered BGP Community Lists
+@subsection Numbered BGP Community Lists
+
+When number is used for BGP community list name, the number has
+special meanings.  Community list number in the range from 1 and 99 is
+standard community list.  Community list number in the range from 100
+to 199 is expanded community list.  These community lists are called
+as numbered community lists.  On the other hand normal community lists
+is called as named community lists.
+
+@deffn Command {ip community-list <1-99> @{permit|deny@} @var{community}} {}
+This command defines a new community list.  <1-99> is standard
+community list number.  Community list name within this range defines
+standard community list.  When @var{community} is empty it matches to
+any routes.
+@end deffn
+
+@deffn Command {ip community-list <100-199> @{permit|deny@} @var{community}} {}
+This command defines a new community list.  <100-199> is expanded
+community list number.  Community list name within this range defines
+expanded community list.
+@end deffn
+
+@deffn Command {ip community-list @var{name} @{permit|deny@} @var{community}} {}
+When community list type is not specifed, the community list type is
+automatically detected.  If @var{community} can be compiled into
+communities attribute, the community list is defined as a standard
+community list.  Otherwise it is defined as an expanded community
+list.  This feature is left for backward compability.  Use of this
+feature is not recommended.
+@end deffn
+
+@node BGP Community in Route Map
+@subsection BGP Community in Route Map
+
+In Route Map (@pxref{Route Map}), we can match or set BGP
+communities attribute.  Using this feature network operator can
+implement their network policy based on BGP communities attribute.
+
+Following commands can be used in Route Map.
+
+@deffn {Route Map} {match community @var{word}} {}
+@deffnx {Route Map} {match community @var{word} exact-match} {}
+This command perform match to BGP updates using community list
+@var{word}.  When the one of BGP communities value match to the one of
+communities value in community list, it is match.  When
+@code{exact-match} keyword is spcified, match happen only when BGP
+updates have completely same communities value specified in the
+community list.
+@end deffn
+
+@deffn {Route Map} {set community none} {}
+@deffnx {Route Map} {set community @var{community}} {}
+@deffnx {Route Map} {set community @var{community} additive} {}
+This command manipulate communities value in BGP updates.  When
+@code{none} is specified as communities value, it removes entire
+communities attribute from BGP updates.  When @var{community} is not
+@code{none}, specified communities value is set to BGP updates.  If
+BGP updates already has BGP communities value, the existing BGP
+communities value is replaced with specified @var{community} value.
+When @code{additive} keyword is specified, @var{community} is appended
+to the existing communities value.
+@end deffn
+
+@deffn {Route Map} {set comm-list @var{word} delete} {}
+This command remove communities value from BGP communities attribute.
+The @var{word} is community list name.  When BGP route's communities
+value matches to the community list @var{word}, the communities value
+is removed.  When all of communities value is removed eventually, the
+BGP update's communities attribute is completely removed.
+@end deffn
+
+@node Display BGP Routes by Community
+@subsection Display BGP Routes by Community
+
+To show BGP routes which has specific BGP communities attribute,
+@code{show ip bgp} command can be used.  The @var{community} value and
+community list can be used for @code{show ip bgp} command.
+
+@deffn Command {show ip bgp community} {}
+@deffnx Command {show ip bgp community @var{community}} {}
+@deffnx Command {show ip bgp community @var{community} exact-match} {}
+@code{show ip bgp community} displays BGP routes which has communities
+attribute.  When @var{community} is specified, BGP routes that matches
+@var{community} value is displayed.  For this command, @code{internet}
+keyword can't be used for @var{community} value.  When
+@code{exact-match} is specified, it display only routes that have an
+exact match.
+@end deffn
+
+@deffn Command {show ip bgp community-list @var{word}} {}
+@deffnx Command {show ip bgp community-list @var{word} exact-match} {}
+This commands display BGP routes that matches community list
+@var{word}.  When @code{exact-match} is specified, display only routes
+that have an exact match.
+@end deffn
+
+@node Using BGP Communities Attribute
+@subsection Using BGP Communities Attribute
+
+Following configuration is the most typical usage of BGP communities
+attribute.  AS 7675 provides upstream Internet connection to AS 100.
+When following configuration exists in AS 7675, AS 100 networks
+operator can set local preference in AS 7675 network by setting BGP
+communities attribute to the updates.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ neighbor 192.168.0.1 route-map RMAP in
+!
+ip community-list 70 permit 7675:70
+ip community-list 70 deny
+ip community-list 80 permit 7675:80
+ip community-list 80 deny
+ip community-list 90 permit 7675:90
+ip community-list 90 deny
+!
+route-map RMAP permit 10
+ match community 70
+ set local-preference 70
+!
+route-map RMAP permit 20
+ match community 80
+ set local-preference 80
+!
+route-map RMAP permit 30
+ match community 90
+ set local-preference 90
+@end example
+
+Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675.
+The route has communities value 7675:80 so when above configuration
+exists in AS 7675, announced route's local preference will be set to
+value 80.
+
+@example
+router bgp 100
+ network 10.0.0.0/8
+ neighbor 192.168.0.2 remote-as 7675
+ neighbor 192.168.0.2 route-map RMAP out
+!
+ip prefix-list PLIST permit 10.0.0.0/8
+!
+route-map RMAP permit 10
+ match ip address prefix-list PLIST
+ set community 7675:80
+@end example
+
+Following configuration is an example of BGP route filtering using
+communities attribute.  This configuration only permit BGP routes
+which has BGP communities value 0:80 or 0:90.  Network operator can
+put special internal communities value at BGP border router, then
+limit the BGP routes announcement into the internal network.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ neighbor 192.168.0.1 route-map RMAP in
+!
+ip community-list 1 permit 0:80 0:90
+!
+route-map RMAP permit in
+ match community 1
+@end example
+
+Following exmaple filter BGP routes which has communities value 1:1.
+When there is no match community-list returns deny.  To avoid
+filtering all of routes, we need to define permit any at last.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ neighbor 192.168.0.1 route-map RMAP in
+!
+ip community-list standard FILTER deny 1:1
+ip community-list standard FILTER permit
+!
+route-map RMAP permit 10
+ match community FILTER
+@end example
+
+Communities value keyword @code{internet} has special meanings in
+standard community lists.  In below example @code{internet} act as
+match any.  It matches all of BGP routes even if the route does not
+have communities attribute at all.  So community list @code{INTERNET}
+is same as above example's @code{FILTER}.
+
+@example
+ip community-list standard INTERNET deny 1:1
+ip community-list standard INTERNET permit internet
+@end example
+
+Following configuration is an example of communities value deletion.
+With this configuration communities value 100:1 and 100:2 is removed
+from BGP updates.  For communities value deletion, only @code{permit}
+community-list is used.  @code{deny} community-list is ignored.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ neighbor 192.168.0.1 route-map RMAP in
+!
+ip community-list standard DEL permit 100:1 100:2
+!
+route-map RMAP permit 10
+ set comm-list DEL delete
+@end example
+
+@c -----------------------------------------------------------------------
+@node BGP Extended Communities Attribute
+@section BGP Extended Communities Attribute
+
+BGP extended communities attribute is introduced with MPLS VPN/BGP
+technology.  MPLS VPN/BGP expands capability of network infrastructure
+to provide VPN functionality.  At the same time it requires a new
+framework for policy routing.  With BGP Extended Communities Attribute
+we can use Route Target or Site of Origin for implementing network
+policy for MPLS VPN/BGP.
+
+BGP Extended Communities Attribute is similar to BGP Communities
+Attribute.  It is an optional transitive attribute.  BGP Extended
+Communities Attribute can carry multiple Extended Community value.
+Each Extended Community value is eight octet length.
+
+BGP Extended Communities Attribute provides an extended range
+compared with BGP Communities Attribute.  Adding to that there is a
+type field in each value to provides community space structure.
+
+There are two format to define Extended Community value.  One is AS
+based format the other is IP address based format.
+
+@table @code
+@item AS:VAL
+This is a format to define AS based Extended Community value.
+@code{AS} part is 2 octets Global Administrator subfield in Extended
+Community value.  @code{VAL} part is 4 octets Local Administrator
+subfield.  @code{7675:100} represents AS 7675 policy value 100.
+@item IP-Address:VAL
+This is a format to define IP address based Extended Community value.
+@code{IP-Address} part is 4 octets Global Administrator subfield.
+@code{VAL} part is 2 octets Local Administrator subfield.
+@code{10.0.0.1:100} represents 
+@end table
+
+@menu
+* BGP Extended Community Lists::  
+* BGP Extended Communities in Route Map::  
+@end menu
+
+@node BGP Extended Community Lists
+@subsection BGP Extended Community Lists
+
+Expanded Community Lists is a user defined BGP Expanded Community
+Lists.
+
+@deffn Command {ip extcommunity-list standard @var{name} @{permit|deny@} @var{extcommunity}} {}
+This command defines a new standard extcommunity-list.
+@var{extcommunity} is extended communities value.  The
+@var{extcommunity} is compiled into extended community structure.  We
+can define multiple extcommunity-list under same name.  In that case
+match will happen user defined order.  Once the extcommunity-list
+matches to extended communities attribute in BGP updates it return
+permit or deny based upon the extcommunity-list definition.  When
+there is no matched entry, deny will be returned.  When
+@var{extcommunity} is empty it matches to any routes.
+@end deffn
+
+@deffn Command {ip extcommunity-list expanded @var{name} @{permit|deny@} @var{line}} {}
+This command defines a new expanded extcommunity-list.  @var{line} is
+a string expression of extended communities attribute.  @var{line} can
+include regular expression to match extended communities attribute in
+BGP updates.
+@end deffn
+
+@deffn Command {no ip extcommunity-list @var{name}} {}
+@deffnx Command {no ip extcommunity-list standard @var{name}} {}
+@deffnx Command {no ip extcommunity-list expanded @var{name}} {}
+These commands delete extended community lists specified by
+@var{name}.  All of extended community lists shares a single name
+space.  So extended community lists can be removed simpley specifying
+the name.
+@end deffn
+
+@deffn {Command} {show ip extcommunity-list} {}
+@deffnx {Command} {show ip extcommunity-list @var{name}} {}
+This command display current extcommunity-list information.  When
+@var{name} is specified the community list's information is shown.
+
+@example
+# show ip extcommunity-list 
+@end example
+@end deffn
+
+@node BGP Extended Communities in Route Map
+@subsection BGP Extended Communities in Route Map
+
+@deffn {Route Map} {match extcommunity @var{word}} {}
+@end deffn
+
+@deffn {Route Map} {set extcommunity rt @var{extcommunity}} {}
+This command set Route Target value.
+@end deffn
+
+@deffn {Route Map} {set extcommunity soo @var{extcommunity}} {}
+This command set Site of Origin value.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node Displaying BGP routes
+@section Displaying BGP Routes
+
+@menu
+* Show IP BGP::                 
+* More Show IP BGP::            
+@end menu
+
+@node Show IP BGP
+@subsection Show IP BGP
+
+@deffn {Command} {show ip bgp} {}
+@deffnx {Command} {show ip bgp @var{A.B.C.D}} {}
+@deffnx {Command} {show ip bgp @var{X:X::X:X}} {}
+This command displays BGP routes.  When no route is specified it
+display all of IPv4 BGP routes.
+@end deffn
+
+@example
+BGP table version is 0, local router ID is 10.1.1.1
+Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
+Origin codes: i - IGP, e - EGP, ? - incomplete
+
+   Network          Next Hop            Metric LocPrf Weight Path
+*> 1.1.1.1/32       0.0.0.0                  0         32768 i
+
+Total number of prefixes 1
+@end example
+
+@node More Show IP BGP
+@subsection More Show IP BGP
+
+@deffn {Command} {show ip bgp regexp @var{line}} {}
+This command display BGP routes using AS path regular expression (@pxref{Display BGP Routes by AS Path}).
+@end deffn
+
+@deffn Command {show ip bgp community @var{community}} {}
+@deffnx Command {show ip bgp community @var{community} exact-match} {}
+This command display BGP routes using @var{community} (@pxref{Display
+BGP Routes by Community}).
+@end deffn
+
+@deffn Command {show ip bgp community-list @var{word}} {}
+@deffnx Command {show ip bgp community-list @var{word} exact-match} {}
+This command display BGP routes using community list (@pxref{Display
+BGP Routes by Community}).
+@end deffn
+
+@deffn {Command} {show ip bgp summary} {}
+@end deffn
+
+@deffn {Command} {show ip bgp neighbor [@var{peer}]} {}
+@end deffn
+
+@deffn {Command} {clear ip bgp @var{peer}} {}
+Clear peers which have addresses of X.X.X.X
+@end deffn
+
+@deffn {Command} {clear ip bgp @var{peer} soft in} {}
+Clear peer using soft reconfiguration.
+@end deffn
+
+@deffn {Command} {show ip bgp dampened-paths} {}
+Display paths suppressed due to dampening
+@end deffn
+
+@deffn {Command} {show ip bgp flap-statistics} {}
+Display flap statistics of routes
+@end deffn
+
+@deffn {Command} {show debug} {}
+@end deffn
+
+@deffn {Command} {debug event} {}
+@end deffn
+
+@deffn {Command} {debug update} {}
+@end deffn
+
+@deffn {Command} {debug keepalive} {}
+@end deffn
+
+@deffn {Command} {no debug event} {}
+@end deffn
+
+@deffn {Command} {no debug update} {}
+@end deffn
+
+@deffn {Command} {no debug keepalive} {}
+@end deffn
+
+@node Capability Negotiation
+@section Capability Negotiation
+
+When adding IPv6 routing information exchange feature to BGP.  There
+were some proposals.  @acronym{IETF,Internet Engineering Task Force}
+@acronym{IDR, Inter Domain Routing} @acronym{WG, Working group} adopted
+a proposal called Multiprotocol Extension for BGP.  The specification
+is described in @cite{RFC2283}.  The protocol does not define new protocols. 
+It defines new attributes to existing BGP.  When it is used exchanging
+IPv6 routing information it is called BGP-4+.  When it is used for
+exchanging multicast routing information it is called MBGP.
+
+@command{bgpd} supports Multiprotocol Extension for BGP.  So if remote
+peer supports the protocol, @command{bgpd} can exchange IPv6 and/or
+multicast routing information.
+
+Traditional BGP did not have the feature to detect remote peer's
+capabilities, e.g. whether it can handle prefix types other than IPv4
+unicast routes.  This was a big problem using Multiprotocol Extension
+for BGP to operational network.  @cite{RFC2842, Capabilities
+Advertisement with BGP-4} adopted a feature called Capability
+Negotiation. @command{bgpd} use this Capability Negotiation to detect
+the remote peer's capabilities.  If the peer is only configured as IPv4
+unicast neighbor, @command{bgpd} does not send these Capability
+Negotiation packets (at least not unless other optional BGP features
+require capability negotation).
+
+By default, Quagga will bring up peering with minimal common capability
+for the both sides.  For example, local router has unicast and
+multicast capabilitie and remote router has unicast capability.  In
+this case, the local router will establish the connection with unicast
+only capability. When there are no common capabilities, Quagga sends
+Unsupported Capability error and then resets the connection.
+
+If you want to completely match capabilities with remote peer.  Please
+use @command{strict-capability-match} command.
+  
+@deffn {BGP} {neighbor @var{peer} strict-capability-match} {}
+@deffnx {BGP} {no neighbor @var{peer} strict-capability-match} {}
+Strictly compares remote capabilities and local capabilities.  If capabilities
+are different, send Unsupported Capability error then reset connection.
+@end deffn
+
+You may want to disable sending Capability Negotiation OPEN message
+optional parameter to the peer when remote peer does not implement
+Capability Negotiation.  Please use @command{dont-capability-negotiate}
+command to disable the feature.
+
+@deffn {BGP} {neighbor @var{peer} dont-capability-negotiate} {}
+@deffnx {BGP} {no neighbor @var{peer} dont-capability-negotiate} {}
+Suppress sending Capability Negotiation as OPEN message optional
+parameter to the peer.  This command only affects the peer is configured
+other than IPv4 unicast configuration.
+@end deffn
+
+When remote peer does not have capability negotiation feature, remote
+peer will not send any capabilities at all.  In that case, bgp
+configures the peer with configured capabilities.
+
+You may prefer locally configured capabilities more than the negotiated
+capabilities even though remote peer sends capabilities.  If the peer
+is configured by @command{override-capability}, @command{bgpd} ignores
+received capabilities then override negotiated capabilities with
+configured values.
+
+@deffn {BGP} {neighbor @var{peer} override-capability} {}
+@deffnx {BGP} {no neighbor @var{peer} override-capability} {}
+Override the result of Capability Negotiation with local configuration.
+Ignore remote peer's capability value.
+@end deffn
+
+@node Route Reflector
+@section Route Reflector
+
+@deffn {BGP} {bgp cluster-id @var{a.b.c.d}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} route-reflector-client} {}
+@deffnx {BGP} {no neighbor @var{peer} route-reflector-client} {}
+@end deffn
+
+@node Route Server
+@section Route Server
+
+At an Internet Exchange point, many ISPs are connected to each other by
+external BGP peering.  Normally these external BGP connection are done by
+@samp{full mesh} method.  As with internal BGP full mesh formation,
+this method has a scaling problem.
+
+This scaling problem is well known.  Route Server is a method to resolve
+the problem.  Each ISP's BGP router only peers to Route Server.  Route
+Server serves as BGP information exchange to other BGP routers.  By
+applying this method, numbers of BGP connections is reduced from
+O(n*(n-1)/2) to O(n).
+
+Unlike normal BGP router, Route Server must have several routing tables
+for managing different routing policies for each BGP speaker.  We call the
+routing tables as different @code{view}s.  @command{bgpd} can work as
+normal BGP router or Route Server or both at the same time.
+
+@menu
+* Multiple instance::           
+* BGP instance and view::       
+* Routing policy::              
+* Viewing the view::            
+@end menu
+
+@node Multiple instance
+@subsection Multiple instance
+
+To enable multiple view function of @code{bgpd}, you must turn on
+multiple instance feature beforehand.
+
+@deffn {Command} {bgp multiple-instance} {}
+Enable BGP multiple instance feature.  After this feature is enabled,
+you can make multiple BGP instances or multiple BGP views.
+@end deffn
+
+@deffn {Command} {no bgp multiple-instance} {}
+Disable BGP multiple instance feature.  You can not disable this feature
+when BGP multiple instances or views exist.
+@end deffn
+
+When you want to make configuration more Cisco like one, 
+
+@deffn {Command} {bgp config-type cisco} {}
+Cisco compatible BGP configuration output.
+@end deffn
+
+When bgp config-type cisco is specified, 
+
+``no synchronization'' is displayed.
+``no auto-summary'' is displayed.
+
+``network'' and ``aggregate-address'' argument is displayed as
+``A.B.C.D M.M.M.M''
+
+Quagga: network 10.0.0.0/8
+Cisco: network 10.0.0.0
+
+Quagga: aggregate-address 192.168.0.0/24
+Cisco: aggregate-address 192.168.0.0 255.255.255.0
+
+Community attribute handling is also different.  If there is no
+configuration is specified community attribute and extended community
+attribute are sent to neighbor.  When user manually disable the
+feature community attribute is not sent to the neighbor.  In case of
+@command{bgp config-type cisco} is specified, community attribute is not
+sent to the neighbor by default.  To send community attribute user has
+to specify @command{neighbor A.B.C.D send-community} command.
+
+@example
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 1
+ no neighbor 10.0.0.1 send-community
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 1
+ neighbor 10.0.0.1 send-community
+!
+@end example
+
+@deffn {Command} {bgp config-type zebra} {}
+Quagga style BGP configuration.  This is default.
+@end deffn
+
+@node BGP instance and view
+@subsection BGP instance and view
+
+BGP instance is a normal BGP process.  The result of route selection
+goes to the kernel routing table.  You can setup different AS at the
+same time when BGP multiple instance feature is enabled.
+
+@deffn {Command} {router bgp @var{as-number}} {}
+Make a new BGP instance.  You can use arbitrary word for the @var{name}.
+@end deffn
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+!
+router bgp 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
+@end group
+@end example
+
+BGP view is almost same as normal BGP process. The result of
+route selection does not go to the kernel routing table.  BGP view is
+only for exchanging BGP routing information.
+
+@deffn {Command} {router bgp @var{as-number} view @var{name}} {}
+Make a new BGP view.  You can use arbitrary word for the @var{name}.  This
+view's route selection result does not go to the kernel routing table.
+@end deffn
+
+With this command, you can setup Route Server like below.
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1 view 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+!
+router bgp 2 view 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
+@end group
+@end example
+
+@node Routing policy
+@subsection Routing policy
+
+You can set different routing policy for a peer.  For example, you can
+set different filter for a peer.
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1 view 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.1 distribute-list 1 in
+!
+router bgp 1 view 2
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.1 distribute-list 2 in
+@end group
+@end example
+
+This means BGP update from a peer 10.0.0.1 goes to both BGP view 1 and view
+2.  When the update is inserted into view 1, distribute-list 1 is
+applied.  On the other hand, when the update is inserted into view 2,
+distribute-list 2 is applied.
+
+@node Viewing the view
+@subsection Viewing the view
+
+To display routing table of BGP view, you must specify view name.
+
+@deffn {Command} {show ip bgp view @var{name}} {}
+Display routing table of BGP view @var{name}.
+@end deffn
+
+@node How to set up a 6-Bone connection
+@section How to set up a 6-Bone connection
+
+
+@example
+@group
+zebra configuration 
+=================== 
+!  
+! Actually there is no need to configure zebra 
+!
+
+bgpd configuration
+==================
+!
+! This means that routes go through zebra and into the kernel.
+!
+router zebra
+!
+! MP-BGP configuration
+!
+router bgp 7675
+ bgp router-id 10.0.0.1
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 remote-as @var{as-number}
+!
+ address-family ipv6
+ network 3ffe:506::/32
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 activate
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 route-map set-nexthop out
+ neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 remote-as @var{as-number}
+ neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 route-map set-nexthop out
+ exit-address-family
+!
+ipv6 access-list all permit any
+!
+! Set output nexthop address.
+!
+route-map set-nexthop permit 10
+ match ipv6 address all
+ set ipv6 nexthop global 3ffe:1cfa:0:2:2c0:4fff:fe68:a225
+ set ipv6 nexthop local fe80::2c0:4fff:fe68:a225
+!
+! logfile FILENAME is obsolete.  Please use log file FILENAME
+
+log file bgpd.log
+!
+@end group
+@end example
+
+@node Dump BGP packets and table
+@section Dump BGP packets and table
+
+@deffn Command {dump bgp all @var{path} [@var{interval}]} {}
+@deffnx Command {dump bgp all-et @var{path} [@var{interval}]} {}
+@deffnx Command {no dump bgp all [@var{path}] [@var{interval}]} {}
+Dump all BGP packet and events to @var{path} file.
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+The path @var{path} can be set with date and time formatting (strftime).
+The type ‘all-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}).
+(@pxref{Packet Binary Dump Format})
+@end deffn 
+
+@deffn Command {dump bgp updates @var{path} [@var{interval}]} {}
+@deffnx Command {dump bgp updates-et @var{path} [@var{interval}]} {}
+@deffnx Command {no dump bgp updates [@var{path}] [@var{interval}]} {}
+Dump only BGP updates messages to @var{path} file.
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+The path @var{path} can be set with date and time formatting (strftime).
+The type ‘updates-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}).
+@end deffn
+
+@deffn Command {dump bgp routes-mrt @var{path}} {}
+@deffnx Command {dump bgp routes-mrt @var{path} @var{interval}} {}
+@deffnx Command {no dump bgp route-mrt [@var{path}] [@var{interval}]} {}
+Dump whole BGP routing table to @var{path}.  This is heavy process.
+The path @var{path} can be set with date and time formatting (strftime).
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+@end deffn
+
+Note: the interval variable can also be set using hours and minutes: 04h20m00.
+
+
+@node BGP Configuration Examples
+@section BGP Configuration Examples
+
+Example of a session to an upstream, advertising only one prefix to it.
+
+@example
+router bgp 64512
+ bgp router-id 10.236.87.1
+ network 10.236.87.0/24
+ neighbor upstream peer-group
+ neighbor upstream remote-as 64515
+ neighbor upstream capability dynamic
+ neighbor upstream prefix-list pl-allowed-adv out
+ neighbor 10.1.1.1 peer-group upstream
+ neighbor 10.1.1.1 description ACME ISP
+!
+ip prefix-list pl-allowed-adv seq 5 permit 82.195.133.0/25
+ip prefix-list pl-allowed-adv seq 10 deny any
+
+@end example
+
+A more complex example. With upstream, peer and customer sessions.
+Advertising global prefixes and NO_EXPORT prefixes and providing
+actions for customer routes based on community values. Extensive use of
+route-maps and the 'call' feature to support selective advertising of
+prefixes. This example is intended as guidance only, it has NOT been
+tested and almost certainly containts silly mistakes, if not serious
+flaws.
+
+@example
+router bgp 64512
+ bgp router-id 10.236.87.1
+ network 10.123.456.0/24
+ network 10.123.456.128/25 route-map rm-no-export
+ neighbor upstream capability dynamic
+ neighbor upstream route-map rm-upstream-out out
+ neighbor cust capability dynamic
+ neighbor cust route-map rm-cust-in in
+ neighbor cust route-map rm-cust-out out
+ neighbor cust send-community both
+ neighbor peer capability dynamic
+ neighbor peer route-map rm-peer-in in
+ neighbor peer route-map rm-peer-out out
+ neighbor peer send-community both
+ neighbor 10.1.1.1 remote-as 64515
+ neighbor 10.1.1.1 peer-group upstream
+ neighbor 10.2.1.1 remote-as 64516
+ neighbor 10.2.1.1 peer-group upstream
+ neighbor 10.3.1.1 remote-as 64517
+ neighbor 10.3.1.1 peer-group cust-default
+ neighbor 10.3.1.1 description customer1
+ neighbor 10.3.1.1 prefix-list pl-cust1-network in
+ neighbor 10.4.1.1 remote-as 64518
+ neighbor 10.4.1.1 peer-group cust
+ neighbor 10.4.1.1 prefix-list pl-cust2-network in
+ neighbor 10.4.1.1 description customer2
+ neighbor 10.5.1.1 remote-as 64519
+ neighbor 10.5.1.1 peer-group peer
+ neighbor 10.5.1.1 prefix-list pl-peer1-network in
+ neighbor 10.5.1.1 description peer AS 1
+ neighbor 10.6.1.1 remote-as 64520
+ neighbor 10.6.1.1 peer-group peer
+ neighbor 10.6.1.1 prefix-list pl-peer2-network in
+ neighbor 10.6.1.1 description peer AS 2
+!
+ip prefix-list pl-default permit 0.0.0.0/0
+!
+ip prefix-list pl-upstream-peers permit 10.1.1.1/32
+ip prefix-list pl-upstream-peers permit 10.2.1.1/32
+!
+ip prefix-list pl-cust1-network permit 10.3.1.0/24
+ip prefix-list pl-cust1-network permit 10.3.2.0/24
+!
+ip prefix-list pl-cust2-network permit 10.4.1.0/24
+!
+ip prefix-list pl-peer1-network permit 10.5.1.0/24
+ip prefix-list pl-peer1-network permit 10.5.2.0/24
+ip prefix-list pl-peer1-network permit 192.168.0.0/24
+!
+ip prefix-list pl-peer2-network permit 10.6.1.0/24
+ip prefix-list pl-peer2-network permit 10.6.2.0/24
+ip prefix-list pl-peer2-network permit 192.168.1.0/24
+ip prefix-list pl-peer2-network permit 192.168.2.0/24
+ip prefix-list pl-peer2-network permit 172.16.1/24
+!
+ip as-path access-list asp-own-as permit ^$
+ip as-path access-list asp-own-as permit _64512_
+!
+! #################################################################
+! Match communities we provide actions for, on routes receives from
+! customers. Communities values of <our-ASN>:X, with X, have actions:
+!
+! 100 - blackhole the prefix
+! 200 - set no_export
+! 300 - advertise only to other customers
+! 400 - advertise only to upstreams
+! 500 - set no_export when advertising to upstreams
+! 2X00 - set local_preference to X00
+!
+! blackhole the prefix of the route
+ip community-list standard cm-blackhole permit 64512:100
+!
+! set no-export community before advertising
+ip community-list standard cm-set-no-export permit 64512:200
+!
+! advertise only to other customers
+ip community-list standard cm-cust-only permit 64512:300
+!
+! advertise only to upstreams
+ip community-list standard cm-upstream-only permit 64512:400
+!
+! advertise to upstreams with no-export
+ip community-list standard cm-upstream-noexport permit 64512:500
+!
+! set local-pref to least significant 3 digits of the community
+ip community-list standard cm-prefmod-100 permit 64512:2100
+ip community-list standard cm-prefmod-200 permit 64512:2200
+ip community-list standard cm-prefmod-300 permit 64512:2300
+ip community-list standard cm-prefmod-400 permit 64512:2400
+ip community-list expanded cme-prefmod-range permit 64512:2...
+!
+! Informational communities
+!
+! 3000 - learned from upstream
+! 3100 - learned from customer
+! 3200 - learned from peer
+!
+ip community-list standard cm-learnt-upstream permit 64512:3000
+ip community-list standard cm-learnt-cust permit 64512:3100
+ip community-list standard cm-learnt-peer permit 64512:3200
+!
+! ###################################################################
+! Utility route-maps
+!
+! These utility route-maps generally should not used to permit/deny
+! routes, i.e. they do not have meaning as filters, and hence probably
+! should be used with 'on-match next'. These all finish with an empty
+! permit entry so as not interfere with processing in the caller.
+!
+route-map rm-no-export permit 10
+ set community additive no-export
+route-map rm-no-export permit 20
+!
+route-map rm-blackhole permit 10
+ description blackhole, up-pref and ensure it cant escape this AS
+ set ip next-hop 127.0.0.1
+ set local-preference 10
+ set community additive no-export
+route-map rm-blackhole permit 20
+!
+! Set local-pref as requested
+route-map rm-prefmod permit 10
+ match community cm-prefmod-100
+ set local-preference 100
+route-map rm-prefmod permit 20
+ match community cm-prefmod-200
+ set local-preference 200
+route-map rm-prefmod permit 30
+ match community cm-prefmod-300
+ set local-preference 300
+route-map rm-prefmod permit 40
+ match community cm-prefmod-400
+ set local-preference 400
+route-map rm-prefmod permit 50
+!
+! Community actions to take on receipt of route.
+route-map rm-community-in permit 10
+ description check for blackholing, no point continuing if it matches.
+ match community cm-blackhole
+ call rm-blackhole
+route-map rm-community-in permit 20
+ match community cm-set-no-export
+ call rm-no-export
+ on-match next
+route-map rm-community-in permit 30
+ match community cme-prefmod-range
+ call rm-prefmod
+route-map rm-community-in permit 40
+!
+! #####################################################################
+! Community actions to take when advertising a route.
+! These are filtering route-maps, 
+!
+! Deny customer routes to upstream with cust-only set.
+route-map rm-community-filt-to-upstream deny 10
+ match community cm-learnt-cust
+ match community cm-cust-only
+route-map rm-community-filt-to-upstream permit 20
+!
+! Deny customer routes to other customers with upstream-only set.
+route-map rm-community-filt-to-cust deny 10
+ match community cm-learnt-cust
+ match community cm-upstream-only
+route-map rm-community-filt-to-cust permit 20
+!
+! ###################################################################
+! The top-level route-maps applied to sessions. Further entries could
+! be added obviously..
+!
+! Customers
+route-map rm-cust-in permit 10
+ call rm-community-in
+ on-match next
+route-map rm-cust-in permit 20
+ set community additive 64512:3100
+route-map rm-cust-in permit 30
+!
+route-map rm-cust-out permit 10
+ call rm-community-filt-to-cust
+ on-match next
+route-map rm-cust-out permit 20
+!
+! Upstream transit ASes
+route-map rm-upstream-out permit 10
+ description filter customer prefixes which are marked cust-only
+ call rm-community-filt-to-upstream
+ on-match next
+route-map rm-upstream-out permit 20
+ description only customer routes are provided to upstreams/peers
+ match community cm-learnt-cust
+!
+! Peer ASes
+! outbound policy is same as for upstream
+route-map rm-peer-out permit 10
+ call rm-upstream-out
+!
+route-map rm-peer-in permit 10
+ set community additive 64512:3200
+@end example
diff --git a/doc/defines.texi b/doc/defines.texi
new file mode 100644 (file)
index 0000000..b7b529e
--- /dev/null
@@ -0,0 +1,14 @@
+@c -*- texinfo -*-
+
+@c Set variables
+@set PACKAGE_NAME Quagga
+@set PACKAGE_TARNAME quagga
+@set PACKAGE_STRING Quagga 1.2.0
+@set AUTHORS Kunihiro Ishiguro, et al.
+@set COPYRIGHT_YEAR 1999-2005
+@set COPYRIGHT_STR Copyright @copyright{} @value{COPYRIGHT_YEAR} @value{AUTHORS}
+
+@c These may vary with installation environment.
+@set INSTALL_PREFIX_ETC /etc/quagga
+@set INSTALL_PREFIX_SBIN /usr/sbin
+@set INSTALL_PREFIX_STATE /var/run/quagga
diff --git a/doc/draft-zebra-00.ms b/doc/draft-zebra-00.ms
new file mode 100644 (file)
index 0000000..2599472
--- /dev/null
@@ -0,0 +1,209 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Ishiguro
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH RFC DRAFT
+.ds RH March 1998
+.ds CH
+.hy 0
+.ad l
+Network Working Group                                        K. Ishiguro
+Request for Comments: DRAFT                     Digital Magic Labs, Inc.
+                                                              March 1998
+.sp 2
+.ce
+Zebra Protocol Draft
+.sp 2
+.fi
+.ne 4
+Status of this Memo
+.sp
+.in 3
+This draft is very eary beta version.
+.sp
+.in 0
+.ne 4
+Introduction
+.sp
+.in 3
+The zebra protocol is a communication protocol between kernel
+routing table manager and routing protocol daemon. It is built over
+TCP/IP protocol suite.
+.sp
+.in 0
+.ne 4
+Request message formats
+.sp
+.in 3
+zebra is TCP-based protocol.
+.sp
+Below is request packet format.
+.sp
+.in 0
+.DS
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|           Length (2)          |   Command (1) |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.DE
+.sp
+.in 3
+Length is total packet length.
+.sp
+Here is summary of command list.
+.sp
+.in 0
+.DS
+1 - ZEBRA_IPV4_ROUTE_ADD
+2 - ZEBRA_IPV4_ROUTE_DELETE
+3 - ZEBRA_IPV6_ROUTE_ADD
+4 - ZEBRA_IPV6_ROUTE_DELETE
+5 - ZEBRA_GET_ONE_INTERFACE
+6 - ZEBRA_GET_ALL_INTERFACE
+7 - ZEBRA_GET_HOSTINFO
+.DE
+.sp
+.in 0
+.ne 4
+IPv4 reply message formats
+.sp
+.in 0
+.DS
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+
+|    Type (1)   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                            Gateway (4)                        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.DE
+.sp
+.in 3
+Type field specify route's origin type.
+.sp
+.in 0
+.DS
+1 - ZEBRA_ROUTE_RESERVE
+2 - ZEBRA_ROUTE_CONNECT
+3 - ZEBRA_ROUTE_STATIC
+4 - ZEBRA_ROUTE_RIP
+5 - ZEBRA_ROUTE_RIPNG
+6 - ZEBRA_ROUTE_BGP
+7 - ZEBRA_ROUTE_RADIX
+.DE
+.sp
+.in 3
+After above message there can be variale length IPv4 prefix data.
+Each IPv4 prefix is encoded as a two tuple of the form <masklength,
+prefix>
+.sp
+.in 0
+.DS
++----------------------+
+|Subnet mask (1 octet) |
++----------------------+
+|IPv4 prefix (variable)|
++----------------------+
+.DE
+.sp
+.in 0
+.ne 4
+IPv6 reply message formats
+.sp
+.in 0
+.DS
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+
+|    Type (1)   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Gateway (16)                        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.DE
+.sp
+.in 3
+Type field specify route's origin type.
+.sp
+.in 0
+.DS
+1 - ZEBRA_ROUTE_RESERVE
+2 - ZEBRA_ROUTE_CONNECT
+3 - ZEBRA_ROUTE_STATIC
+4 - ZEBRA_ROUTE_RIP
+5 - ZEBRA_ROUTE_RIPNG
+6 - ZEBRA_ROUTE_BGP
+7 - ZEBRA_ROUTE_RADIX
+.DE
+.sp
+.in 0
+.DS
++----------------------+
+|  ifindex   (4 octet) |
++----------------------+
+|  prefixlen  (1 octet)|
++----------------------+
+|IPv6 prefix (variable)|
++----------------------+
+.DE
+.sp
+.in 3
+I am not sure but it seems some operation systems IPv6
+implementation may need interface index when add and delete
+linklocal routes.
+.sp
+I have added ifindex field to specify IPv6 routes interface
+index. If this index is value zero, it will ignored.
+.sp
+.in 0
+.ne 4
+Interface information message format.
+.sp
+.in 0
+.DS
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                      Interface name (20)                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Index (1)   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                       Inteface flag (4)                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                      Inteface metric (4)                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Inteface MTU (4)                       |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                    Inteface Address count (4)                 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.DE
+.sp
+.in 3
+Address message format.
+.sp
+.in 0
+.ne 4
+Host inforamtion message format.
+.sp
+.in 0
+.DS
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|IPv4 forwarding|IPv6 forwarding|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.DE
+.sp
+.in 3
+Host information contain IPv4/IPv6 forwarding information.
diff --git a/doc/fig-normal-processing.dia b/doc/fig-normal-processing.dia
new file mode 100644 (file)
index 0000000..c9e8e68
--- /dev/null
@@ -0,0 +1,1738 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#A4#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="5"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="5"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:object type="Standard - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="7.6,4.75"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="7.55,4.7;16.45,9.4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="7.6,4.75"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="8.8000000000000007"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="4.5999999999999996"/>
+      </dia:attribute>
+      <dia:attribute name="inner_color">
+        <dia:color val="#8f8f8f"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,2.95607"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.8989,2.49893;24.1055,3.50107"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20,2.95607"/>
+        <dia:point val="24,3"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O38" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.1,2.5;4.1,3.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="0,3"/>
+        <dia:point val="4,3"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O36" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="15.9914,5.70858"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="15.8513,2.57356;18.5463,5.84872"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="15.9914,5.70858"/>
+        <dia:point val="18.0879,2.95607"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O7" connection="2"/>
+        <dia:connection handle="1" to="O38" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.86018,2.86018;8.42045,6.03565"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,3"/>
+        <dia:point val="7.95858,5.65858"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O36" connection="4"/>
+        <dia:connection handle="1" to="O5" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O5">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7.9,5.6"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.85,5.55;11.95,8.65"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="7.9,5.6"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="4"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3fa9bd"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O6">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.9,6.6"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.725,6;11.075,8.55"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Best
+Path
+Selection#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9.9,6.6"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O7">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13.45,5.65"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="13.4,5.6;16.1,8.55"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="13.45,5.65"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6000000000000147"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2.8500000000000005"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3fa9bd"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O8">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.75,6.8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.075,6.2;15.425,7.95"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Local
+RIB#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="14.75,6.8"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.86335,5.86335;8.23706,7.58282"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,6"/>
+        <dia:point val="7.9,7.1"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O34" connection="4"/>
+        <dia:connection handle="1" to="O5" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.9,5.5;24.1,6.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20,6"/>
+        <dia:point val="24,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O40" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.05,7.075"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="15.9141,5.51385;18.329,7.21085"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.05,7.075"/>
+        <dia:point val="18,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O7" connection="4"/>
+        <dia:connection handle="1" to="O40" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.1,5.5;4.1,6.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="0,6"/>
+        <dia:point val="4,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O34" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.1,8.5;4.1,9.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="0,9"/>
+        <dia:point val="4,9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O32" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.9,8.5;24.1,9.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20,9"/>
+        <dia:point val="24,9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O42" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.05,7.075"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="15.9086,6.93358;18.4224,9.42608"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.05,7.075"/>
+        <dia:point val="18,9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O7" connection="4"/>
+        <dia:connection handle="1" to="O42" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.85858,6.67574;8.32426,9.14142"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,9"/>
+        <dia:point val="7.9,7.1"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O32" connection="4"/>
+        <dia:connection handle="1" to="O5" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.9,11.5;24.1,12.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20,12"/>
+        <dia:point val="24,12"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O44" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="15.9914,8.44142"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="15.8552,8.30518;18.4846,12.3329"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="15.9914,8.44142"/>
+        <dia:point val="18,12"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O7" connection="7"/>
+        <dia:connection handle="1" to="O44" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.86371,8.20802;8.44294,12.1363"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,12"/>
+        <dia:point val="7.95858,8.54142"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O30" connection="4"/>
+        <dia:connection handle="1" to="O5" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.1,11.5;4.1,12.5"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="0,12"/>
+        <dia:point val="4,12"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O30" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-5,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-5,2.22625;-0.82625,3.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From Peer A#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-5,3"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-5,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-5,5.22625;-0.82625,6.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From Peer B#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-5,6"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-5,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-5,8.22625;-0.77625,9.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From Peer C#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-5,9"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O24">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-5,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-5,11.2263;-0.77625,12.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From Peer D#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-5,12"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24,2.22625;27.3238,3.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#To Peer A#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="24,3"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O26">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24,5.22625;27.3238,6.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#To Peer B#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="24,6"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O27">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24,8.22625;27.3738,9.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#To Peer C#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="24,9"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="0" id="O28">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24,11.2263;27.3738,12.4975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#To Peer D#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="24,12"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O29">
+      <dia:attribute name="obj_pos">
+        <dia:point val="11.9,7.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.7476,6.07271;13.6161,8.07729"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="11.9,7.1"/>
+        <dia:point val="13.45,7.075"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.29999999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.59999999999999998"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.99999999999999967"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="4"/>
+        <dia:connection handle="1" to="O7" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,11"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,10.95;6.05,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,11"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O31">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,12.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,11.3;5.4,12.95"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,12.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O32">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,7.95;6.05,10.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O33">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,9.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,8.3;5.4,9.95"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,9.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O34">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,4.95;6.05,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O35">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.7,6.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.7,5.3;5.4,6.95"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.7,6.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O36">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,1.95;6.05,4.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O37">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.75,3.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.75,2.3;5.45,3.95"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#A#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.75,3.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O38">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.0879,2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.0379,1.95;20.05,3.96213"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="18.0879,2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.9121320343559631"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.9121320343559645"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3756d7"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O39">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.65,3.45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.65,2.4;19.35,4.05"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#A#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18.65,3.45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O40">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.95,4.95;20.05,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="18,5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3756d7"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O41">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.7,6.45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.7,5.4;19.4,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18.7,6.45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O42">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.95,7.95;20.05,10.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="18,8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3756d7"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O43">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.6,9.45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.6,8.4;19.35,10.05"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18.6,9.45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O44">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.0879,11"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.0379,10.95;20.05,12.9621"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="18.0879,11"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="1.9121320343559631"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="1.9121320343559649"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3756d7"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O45">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18.7,12.4"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.7,11.35;19.45,13"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18.7,12.4"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O46">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="12.95,14.95;22.05,19.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="13,15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O47">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14,16"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="13.95,15.95;15.9621,17.9621"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="14,16"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="1.9121320343559649"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="0" id="O48">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.6121,17.4"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.6121,16.35;15.3121,18"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.6121,17.4"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="0" id="O49">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19,17"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="16.525,15.95;21.475,19"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#“Out” Filter
+for Peer X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19,17"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O50">
+        <dia:attribute name="obj_pos">
+          <dia:point val="3,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="2.95,14.95;12.05,19.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="3,15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O51">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,17"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9,16.4;9,17.35"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9,17"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="0" id="O52">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,17"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6.675,15.95;11.325,19"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#“In” Filter
+for Peer X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9,17"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O53">
+          <dia:attribute name="obj_pos">
+            <dia:point val="4,16"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="3.95,15.95;6.05,18.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="4,16"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#eb2020"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="0" id="O54">
+          <dia:attribute name="obj_pos">
+            <dia:point val="4.65,17.35"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.65,16.3;5.35,17.95"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="4.65,17.35"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-normal-processing.png b/doc/fig-normal-processing.png
new file mode 100644 (file)
index 0000000..e4b8fdc
Binary files /dev/null and b/doc/fig-normal-processing.png differ
diff --git a/doc/fig-normal-processing.txt b/doc/fig-normal-processing.txt
new file mode 100644 (file)
index 0000000..01f0e17
--- /dev/null
@@ -0,0 +1,11 @@
+
+                  _______________________________
+                 /    _________     _________    \
+From Peer A --->|(A)-|Best     |   |         |-[A]|--->To Peer A
+From Peer B --->|(B)-|Path     |-->|Local-RIB|-[B]|--->To Peer B
+From Peer C --->|(C)-|Selection|   |         |-[C]|--->To Peer C
+From Peer D --->|(D)-|_________|   |_________|-[D]|--->To Peer D
+                 \_______________________________/
+
+Key:  (X) - 'In'  Filter applied to Peer X's announcements
+      [X] - 'Out' Filter applied to announcements to Peer X
diff --git a/doc/fig-rs-processing.dia b/doc/fig-rs-processing.dia
new file mode 100644 (file)
index 0000000..de7d79f
--- /dev/null
@@ -0,0 +1,4426 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#A4#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.8222000598907471"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O0">
+        <dia:attribute name="obj_pos">
+          <dia:point val="11.95,5.25"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.9,5.2;20.8,9.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="11.95,5.25"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="8.8000000000000007"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4.5999999999999996"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#8f8f8f"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:group>
+          <dia:object type="Standard - Box" version="0" id="O1">
+            <dia:attribute name="obj_pos">
+              <dia:point val="12.25,6.1"/>
+            </dia:attribute>
+            <dia:attribute name="obj_bb">
+              <dia:rectangle val="12.2,6.05;16.3,9.15"/>
+            </dia:attribute>
+            <dia:attribute name="elem_corner">
+              <dia:point val="12.25,6.1"/>
+            </dia:attribute>
+            <dia:attribute name="elem_width">
+              <dia:real val="4"/>
+            </dia:attribute>
+            <dia:attribute name="elem_height">
+              <dia:real val="3"/>
+            </dia:attribute>
+            <dia:attribute name="inner_color">
+              <dia:color val="#3fa9bd"/>
+            </dia:attribute>
+            <dia:attribute name="show_background">
+              <dia:boolean val="true"/>
+            </dia:attribute>
+            <dia:attribute name="corner_radius">
+              <dia:real val="0.20000000000000001"/>
+            </dia:attribute>
+          </dia:object>
+          <dia:object type="Standard - Text" version="1" id="O2">
+            <dia:attribute name="obj_pos">
+              <dia:point val="14.25,7.1"/>
+            </dia:attribute>
+            <dia:attribute name="obj_bb">
+              <dia:rectangle val="12.7737,6.505;15.7263,8.85"/>
+            </dia:attribute>
+            <dia:attribute name="text">
+              <dia:composite type="text">
+                <dia:attribute name="string">
+                  <dia:string>#Best
+Path
+Selection#</dia:string>
+                </dia:attribute>
+                <dia:attribute name="font">
+                  <dia:font family="sans" style="0" name="Helvetica"/>
+                </dia:attribute>
+                <dia:attribute name="height">
+                  <dia:real val="0.80000000000000004"/>
+                </dia:attribute>
+                <dia:attribute name="pos">
+                  <dia:point val="14.25,7.1"/>
+                </dia:attribute>
+                <dia:attribute name="color">
+                  <dia:color val="#000000"/>
+                </dia:attribute>
+                <dia:attribute name="alignment">
+                  <dia:enum val="1"/>
+                </dia:attribute>
+              </dia:composite>
+            </dia:attribute>
+            <dia:attribute name="valign">
+              <dia:enum val="3"/>
+            </dia:attribute>
+          </dia:object>
+        </dia:group>
+        <dia:object type="Standard - Line" version="0" id="O3">
+          <dia:attribute name="obj_pos">
+            <dia:point val="16.25,7.6"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="16.0976,6.76695;18.0343,8.40724"/>
+          </dia:attribute>
+          <dia:attribute name="conn_endpoints">
+            <dia:point val="16.25,7.6"/>
+            <dia:point val="17.8,7.575"/>
+          </dia:attribute>
+          <dia:attribute name="numcp">
+            <dia:int val="1"/>
+          </dia:attribute>
+          <dia:attribute name="line_width">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow">
+            <dia:enum val="3"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_length">
+            <dia:real val="0.59999999999999998"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_width">
+            <dia:real val="0.99999999999999967"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:group>
+          <dia:object type="Standard - Box" version="0" id="O4">
+            <dia:attribute name="obj_pos">
+              <dia:point val="17.8,6.15"/>
+            </dia:attribute>
+            <dia:attribute name="obj_bb">
+              <dia:rectangle val="17.75,6.1;20.45,9.05"/>
+            </dia:attribute>
+            <dia:attribute name="elem_corner">
+              <dia:point val="17.8,6.15"/>
+            </dia:attribute>
+            <dia:attribute name="elem_width">
+              <dia:real val="2.6000000000000147"/>
+            </dia:attribute>
+            <dia:attribute name="elem_height">
+              <dia:real val="2.8500000000000005"/>
+            </dia:attribute>
+            <dia:attribute name="inner_color">
+              <dia:color val="#3fa9bd"/>
+            </dia:attribute>
+            <dia:attribute name="show_background">
+              <dia:boolean val="true"/>
+            </dia:attribute>
+            <dia:attribute name="corner_radius">
+              <dia:real val="0.20000000000000001"/>
+            </dia:attribute>
+          </dia:object>
+          <dia:object type="Standard - Text" version="1" id="O5">
+            <dia:attribute name="obj_pos">
+              <dia:point val="19.15,7.4"/>
+            </dia:attribute>
+            <dia:attribute name="obj_bb">
+              <dia:rectangle val="17.9512,6.805;20.3487,8.35"/>
+            </dia:attribute>
+            <dia:attribute name="text">
+              <dia:composite type="text">
+                <dia:attribute name="string">
+                  <dia:string>#Main
+Loc-RIB#</dia:string>
+                </dia:attribute>
+                <dia:attribute name="font">
+                  <dia:font family="sans" style="0" name="Helvetica"/>
+                </dia:attribute>
+                <dia:attribute name="height">
+                  <dia:real val="0.80000000000000004"/>
+                </dia:attribute>
+                <dia:attribute name="pos">
+                  <dia:point val="19.15,7.4"/>
+                </dia:attribute>
+                <dia:attribute name="color">
+                  <dia:color val="#000000"/>
+                </dia:attribute>
+                <dia:attribute name="alignment">
+                  <dia:enum val="1"/>
+                </dia:attribute>
+              </dia:composite>
+            </dia:attribute>
+            <dia:attribute name="valign">
+              <dia:enum val="3"/>
+            </dia:attribute>
+          </dia:object>
+        </dia:group>
+      </dia:group>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-1,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-1.1,2.52639;4.22361,3.47361"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="-1,3"/>
+        <dia:point val="4,3"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="6"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O23" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20.4,7.575"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.2995,7.08026;25.3115,8.02746"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20.4,7.575"/>
+        <dia:point val="25.0879,7.55"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="4"/>
+        <dia:connection handle="1" to="O110" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.86581,2.86581;12.5085,6.29857"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,3"/>
+        <dia:point val="12.3086,6.15858"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="4"/>
+        <dia:connection handle="1" to="O1" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.87832,5.87832;12.4666,7.87935"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,6"/>
+        <dia:point val="12.25,7.6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O21" connection="4"/>
+        <dia:connection handle="1" to="O1" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-1,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-1.1,5.52639;4.22361,6.47361"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="-1,6"/>
+        <dia:point val="4,6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O21" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-1,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-1.1,8.52639;4.22361,9.47361"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="-1,9"/>
+        <dia:point val="4,9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O19" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.88056,7.29601;12.4682,9.11944"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,9"/>
+        <dia:point val="12.25,7.6"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O19" connection="4"/>
+        <dia:connection handle="1" to="O1" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.867,8.88692;12.511,12.133"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,12"/>
+        <dia:point val="12.3086,9.04142"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O17" connection="4"/>
+        <dia:connection handle="1" to="O1" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-1,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-1.1,11.5264;4.22361,12.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="-1,12"/>
+        <dia:point val="4,12"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="2"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O17" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-6,2.8"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-6,2.0575;-1.1875,2.9875"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From Peer A#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-6,2.8"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-7.2,5.6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-7.2,4.8575;-0.49,5.7875"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From RS-Client B#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-7.2,5.6"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,11"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,10.95;6.05,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,11"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,12.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,11.31;5.5125,12.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,12.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O19">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,7.95;6.05,10.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O20">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,9.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,8.31;5.4325,9.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,9.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O21">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,4.95;6.05,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O22">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.7,6.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.7,5.31;5.4675,6.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.7,6.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,1.95;6.05,4.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#eb2020"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.75,3.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.75,2.31;5.515,3.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#A#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.75,3.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Text" version="1" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-7.25,11.7"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-7.25,10.9575;-0.475,11.8875"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From RS-Client D#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-7.25,11.7"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O26">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-7.2,8.7"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-7.2,7.9575;-0.4825,8.8875"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#From RS-Client C#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="1"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="-7.2,8.7"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="12,26"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.95,25.95;20.85,30.65"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="12,26"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="8.8000000000000007"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4.5999999999999996"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#8f8f8f"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O28">
+          <dia:attribute name="obj_pos">
+            <dia:point val="12.3,26.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.25,26.8;16.35,29.9"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="12.3,26.85"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="4"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="3"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3fa9bd"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.20000000000000001"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O29">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.3,27.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.8238,27.255;15.7763,29.6"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Best
+Path
+Selection#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.3,27.85"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="1"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Box" version="0" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.85,26.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.8,26.85;20.5,29.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="17.85,26.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6000000000000147"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2.8500000000000005"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3fa9bd"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O31">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.1,28.2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.9013,27.605;20.2988,29.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Loc-RIB
+For C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.1,28.2"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O32">
+        <dia:attribute name="obj_pos">
+          <dia:point val="16.3,28.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="16.1476,27.5169;18.0843,29.1572"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="16.3,28.35"/>
+          <dia:point val="17.85,28.325"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.59999999999999998"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.99999999999999967"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="1" to="O30" connection="3"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O33">
+        <dia:attribute name="obj_pos">
+          <dia:point val="12,36"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.95,35.95;20.85,40.65"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="12,36"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="8.8000000000000007"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4.5999999999999996"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#8f8f8f"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O34">
+          <dia:attribute name="obj_pos">
+            <dia:point val="12.3,36.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.25,36.8;16.35,39.9"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="12.3,36.85"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="4"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="3"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3fa9bd"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.20000000000000001"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O35">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.3,37.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.8238,37.255;15.7763,39.6"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Best
+Path
+Selection#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.3,37.85"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="1"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Box" version="0" id="O36">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.85,36.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.8,36.85;20.5,39.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="17.85,36.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6000000000000147"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2.8500000000000005"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3fa9bd"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O37">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.1,38.2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.9013,37.605;20.2988,39.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Loc-RIB
+For D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.1,38.2"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O38">
+        <dia:attribute name="obj_pos">
+          <dia:point val="16.3,38.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="16.1476,37.5169;18.0843,39.1572"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="16.3,38.35"/>
+          <dia:point val="17.85,38.325"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.59999999999999998"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.99999999999999967"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="1" to="O36" connection="3"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O39">
+        <dia:attribute name="obj_pos">
+          <dia:point val="12,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.95,14.95;20.85,19.65"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="12,15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="8.8000000000000007"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="4.5999999999999996"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#8f8f8f"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O40">
+          <dia:attribute name="obj_pos">
+            <dia:point val="12.3,15.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.25,15.8;16.35,18.9"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="12.3,15.85"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="4"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="3"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3fa9bd"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.20000000000000001"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O41">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.3,16.85"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.8238,16.255;15.7763,18.6"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Best
+Path
+Selection#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.3,16.85"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="1"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Box" version="0" id="O42">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.85,15.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.8,15.85;20.5,18.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="17.85,15.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2.6000000000000147"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2.8500000000000005"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#3fa9bd"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O43">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.1,17.2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.9013,16.605;20.2988,18.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Loc-RIB
+For B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.1,17.2"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O44">
+        <dia:attribute name="obj_pos">
+          <dia:point val="16.3,17.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="16.1476,16.5169;18.0843,18.1572"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="16.3,17.35"/>
+          <dia:point val="17.85,17.325"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.59999999999999998"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.99999999999999967"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O45">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.666667,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.566667,11.9;4.22361,21.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="0.666667,12"/>
+        <dia:point val="0.666667,21"/>
+        <dia:point val="4,21"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O14" connection="0"/>
+        <dia:connection handle="1" to="O46" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O46">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,19.95;6.05,22.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,20"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O47">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,21.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,20.31;5.5125,21.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,21.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O48">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,21"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,20.5264;8.22361,21.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,21"/>
+        <dia:point val="8,21"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O46" connection="4"/>
+        <dia:connection handle="1" to="O56" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O49">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,17"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,16.95;6.05,19.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,17"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O50">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,18.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,17.31;5.4325,18.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,18.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O51">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,21"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.85865,18.6386;12.5218,21.1413"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,21"/>
+        <dia:point val="12.3586,18.7914"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O56" connection="4"/>
+        <dia:connection handle="1" to="O40" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O52">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,13.95;10.05,16.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,14"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O53">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,15.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,14.31;9.4175,15.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,15.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O54">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,17"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,16.95;10.05,19.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,17"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O55">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,18.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,17.31;9.4175,18.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,18.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O56">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,19.95;10.05,22.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,20"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O57">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,21.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,20.31;9.4175,21.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,21.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O58">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,24"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,23.95;10.05,26.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,24"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O59">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,25.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,24.31;9.4325,25.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,25.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O60">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,30"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,29.95;10.05,32.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,30"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O61">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,31.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,30.31;9.4325,31.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,31.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O62">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,27"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,26.95;10.05,29.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,27"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O63">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,28.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,27.31;9.4325,28.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,28.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O64">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,30"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,29.95;6.05,32.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,30"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O65">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,31.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,30.31;5.5125,31.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,31.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O66">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,27"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,26.95;6.05,29.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,27"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O67">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,28.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,27.31;5.4175,28.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,28.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O68">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,34"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,33.95;10.05,36.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,34"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O69">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,35.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,34.31;9.5125,35.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,35.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O70">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,40"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,39.95;10.05,42.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,40"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O71">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,41.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,40.31;9.5125,41.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,41.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O72">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,37"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,36.95;10.05,39.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="8,37"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#a8e400"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O73">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8.65,38.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.65,37.31;9.5125,38.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8.65,38.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O74">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,37"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,36.95;6.05,39.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,37"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O75">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,38.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,37.31;5.4175,38.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,38.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O76">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,40"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.95,39.95;6.05,42.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4,40"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="2"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#00c0bb"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="corner_radius">
+          <dia:real val="0.29999999999999999"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O77">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.65,41.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.65,40.31;5.4325,41.615"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4.65,41.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O78">
+      <dia:attribute name="obj_pos">
+        <dia:point val="1.5,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.4,8.9;4.22361,18.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="1.5,9"/>
+        <dia:point val="1.5,18"/>
+        <dia:point val="4,18"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="1"/>
+        <dia:connection handle="1" to="O49" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O79">
+      <dia:attribute name="obj_pos">
+        <dia:point val="1.5,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="1.4,8.9;4.22361,41.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="1.5,9"/>
+        <dia:point val="1.5,41"/>
+        <dia:point val="4,41"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="1"/>
+        <dia:connection handle="1" to="O76" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O80">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.75,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.65,5.9;4.22361,28.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="2.75,6"/>
+        <dia:point val="2.75,28"/>
+        <dia:point val="4,28"/>
+        <dia:point val="4,28"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O66" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O81">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.75,6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.65,5.9;4.22361,38.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="2.75,6"/>
+        <dia:point val="2.75,38"/>
+        <dia:point val="4,38"/>
+        <dia:point val="4,38"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O74" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O82">
+      <dia:attribute name="obj_pos">
+        <dia:point val="0.666667,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="0.566667,11.9;4.22361,31.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="0.666667,12"/>
+        <dia:point val="0.666667,31"/>
+        <dia:point val="4,31"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O14" connection="0"/>
+        <dia:connection handle="1" to="O64" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O83">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-0.285714,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.385714,2.9;8.22361,15.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="-0.285714,3"/>
+        <dia:point val="-0.285714,15"/>
+        <dia:point val="8,15"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O6" connection="0"/>
+        <dia:connection handle="1" to="O52" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O84">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-0.285714,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.385714,2.9;8.22361,25.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="-0.285714,3"/>
+        <dia:point val="-0.285714,25"/>
+        <dia:point val="8,25"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O6" connection="0"/>
+        <dia:connection handle="1" to="O58" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O85">
+      <dia:attribute name="obj_pos">
+        <dia:point val="-0.285714,3"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="-0.385714,2.9;8.22361,35.4736"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="-0.285714,3"/>
+        <dia:point val="-0.285714,35"/>
+        <dia:point val="8,35"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O6" connection="0"/>
+        <dia:connection handle="1" to="O68" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O86">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,31"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,30.5264;8.22361,31.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,31"/>
+        <dia:point val="8,31"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O64" connection="4"/>
+        <dia:connection handle="1" to="O60" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O87">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,31"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.8654,29.6516;12.5576,31.1346"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,31"/>
+        <dia:point val="12.3586,29.7914"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O60" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O88">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,41"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.8654,39.6516;12.5576,41.1346"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,41"/>
+        <dia:point val="12.3586,39.7914"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O70" connection="4"/>
+        <dia:connection handle="1" to="O34" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O89">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,41"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,40.5264;8.22361,41.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,41"/>
+        <dia:point val="8,41"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O76" connection="4"/>
+        <dia:connection handle="1" to="O70" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O90">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,18"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.87657,17.091;12.5152,18.1234"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,18"/>
+        <dia:point val="12.3,17.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O54" connection="4"/>
+        <dia:connection handle="1" to="O40" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O91">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,18"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,17.5264;8.22361,18.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,18"/>
+        <dia:point val="8,18"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O49" connection="4"/>
+        <dia:connection handle="1" to="O54" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O92">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,28"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,27.5264;8.22361,28.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,28"/>
+        <dia:point val="8,28"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O66" connection="4"/>
+        <dia:connection handle="1" to="O62" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O93">
+      <dia:attribute name="obj_pos">
+        <dia:point val="6,38"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="5.9,37.5264;8.22361,38.4736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="6,38"/>
+        <dia:point val="8,38"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O74" connection="4"/>
+        <dia:connection handle="1" to="O72" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O94">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,38"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.88609,37.7729;12.5211,38.7094"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,38"/>
+        <dia:point val="12.3,38.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O72" connection="4"/>
+        <dia:connection handle="1" to="O34" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O95">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,28"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.88609,27.7729;12.5211,28.7094"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,28"/>
+        <dia:point val="12.3,28.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="3"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O62" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O96">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,15"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.87074,14.8707;12.5673,16.1034"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,15"/>
+        <dia:point val="12.3586,15.9086"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O52" connection="4"/>
+        <dia:connection handle="1" to="O40" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O97">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,25"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.85936,24.8594;12.5324,27.0493"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,25"/>
+        <dia:point val="12.3586,26.9086"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O58" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O98">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,35"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.85936,34.8594;12.5324,37.0493"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="10,35"/>
+        <dia:point val="12.3586,36.9086"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#e5aa2a"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O68" connection="4"/>
+        <dia:connection handle="1" to="O34" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O99">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20.45,17.325"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.3481,16.7902;24.6236,17.7373"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20.45,17.325"/>
+        <dia:point val="24.4,17.25"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#3c8d37"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O42" connection="4"/>
+        <dia:connection handle="1" to="O104" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O100">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20.45,28.325"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.3494,27.8718;24.5736,28.819"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20.45,28.325"/>
+        <dia:point val="24.35,28.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#972360"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O30" connection="4"/>
+        <dia:connection handle="1" to="O107" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O101">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20.45,38.325"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.3494,37.8718;24.5615,38.8189"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="20.45,38.325"/>
+        <dia:point val="24.3379,38.35"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_color">
+        <dia:color val="#618ccd"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.20000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O36" connection="4"/>
+        <dia:connection handle="1" to="O115" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Line" version="0" id="O102">
+        <dia:attribute name="obj_pos">
+          <dia:point val="26.4,17.25"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="26.3,16.7764;30.6236,17.7236"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="26.4,17.25"/>
+          <dia:point val="30.4,17.25"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#3c8d37"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O103">
+        <dia:attribute name="obj_pos">
+          <dia:point val="30.4,17.25"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="30.4,16.5075;35.9675,17.4375"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#To RS-Client B#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="30.4,17.25"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O104">
+          <dia:attribute name="obj_pos">
+            <dia:point val="24.4,16.25"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="24.35,16.2;26.45,18.3"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="24.4,16.25"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O105">
+          <dia:attribute name="obj_pos">
+            <dia:point val="25.1,17.7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="25.1,16.66;25.8675,17.965"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#B#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="25.1,17.7"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Line" version="0" id="O106">
+        <dia:attribute name="obj_pos">
+          <dia:point val="26.35,28.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="26.2488,27.9176;30.6736,28.8647"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="26.35,28.35"/>
+          <dia:point val="30.45,28.4"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#972360"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O107">
+          <dia:attribute name="obj_pos">
+            <dia:point val="24.35,27.35"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="24.3,27.3;26.4,29.4"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="24.35,27.35"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O108">
+          <dia:attribute name="obj_pos">
+            <dia:point val="24.95,28.8"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="24.95,27.76;25.7325,29.065"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#C#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="24.95,28.8"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O109">
+        <dia:attribute name="obj_pos">
+          <dia:point val="30.35,28.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="30.35,27.6075;35.925,28.5375"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#To RS-Client C#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="30.35,28.35"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O110">
+          <dia:attribute name="obj_pos">
+            <dia:point val="25.0879,6.59393"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="25.0379,6.54393;27.05,8.55606"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="25.0879,6.59393"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="1.9121320343559645"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O111">
+          <dia:attribute name="obj_pos">
+            <dia:point val="25.65,8.04393"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="25.65,7.00393;26.415,8.30893"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#A#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="25.65,8.04393"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Line" version="0" id="O112">
+          <dia:attribute name="obj_pos">
+            <dia:point val="27,7.55"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="26.8988,7.11782;31.4237,8.06497"/>
+          </dia:attribute>
+          <dia:attribute name="conn_endpoints">
+            <dia:point val="27,7.55"/>
+            <dia:point val="31.2001,7.60001"/>
+          </dia:attribute>
+          <dia:attribute name="numcp">
+            <dia:int val="1"/>
+          </dia:attribute>
+          <dia:attribute name="line_color">
+            <dia:color val="#e5aa2a"/>
+          </dia:attribute>
+          <dia:attribute name="line_width">
+            <dia:real val="0.20000000000000001"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow">
+            <dia:enum val="22"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_length">
+            <dia:real val="0.5"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_width">
+            <dia:real val="0.5"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O113">
+          <dia:attribute name="obj_pos">
+            <dia:point val="31.3,7.6"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="31.3,6.8575;34.97,7.7875"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#To Peer A#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="31.3,7.6"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Line" version="0" id="O114">
+        <dia:attribute name="obj_pos">
+          <dia:point val="26.25,38.35"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="26.1499,37.8713;30.6615,38.8186"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="26.25,38.35"/>
+          <dia:point val="30.4379,38.3439"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="line_color">
+          <dia:color val="#618ccd"/>
+        </dia:attribute>
+        <dia:attribute name="line_width">
+          <dia:real val="0.20000000000000001"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O115">
+          <dia:attribute name="obj_pos">
+            <dia:point val="24.3379,37.3939"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="24.2879,37.3439;26.3,39.356"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="24.3379,37.3939"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="1.9121320343559649"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O116">
+          <dia:attribute name="obj_pos">
+            <dia:point val="24.95,38.7939"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="24.95,37.7539;25.8125,39.0589"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#D#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="24.95,38.7939"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O117">
+        <dia:attribute name="obj_pos">
+          <dia:point val="30.4379,38.3439"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="30.4379,37.6014;36.0704,38.5314"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#To RS-Client D#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="30.4379,38.3439"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O118">
+        <dia:attribute name="obj_pos">
+          <dia:point val="-5,43"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="-5.05,42.95;4.05,46.55"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="-5,43"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.5"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O119">
+        <dia:attribute name="obj_pos">
+          <dia:point val="0.5,45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="0.5,44.405;0.5,45.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="0.5,45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O120">
+        <dia:attribute name="obj_pos">
+          <dia:point val="1,44.5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="-1.74625,43.46;3.74625,46.165"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#“In” Filter
+for Peer X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="1,44.5"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O121">
+          <dia:attribute name="obj_pos">
+            <dia:point val="-4.5,44"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="-4.55,43.95;-2.45,46.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="-4.5,44"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#eb2020"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O122">
+          <dia:attribute name="obj_pos">
+            <dia:point val="-3.85,45.35"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="-3.85,44.31;-3.0825,45.615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="-3.85,45.35"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O123">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4.45,42.95;14.05,46.55"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="4.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.5"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O124">
+          <dia:attribute name="obj_pos">
+            <dia:point val="5,44"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.95,43.95;6.96213,45.9621"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="5,44"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="1.9121320343559649"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#3756d7"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O125">
+          <dia:attribute name="obj_pos">
+            <dia:point val="5.61213,45.4"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="5.61213,44.36;6.37963,45.665"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="5.61213,45.4"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O126">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10.5,44.5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.36,43.46;13.64,46.165"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#“Out” Filter
+for Peer X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="10.5,44.5"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O127">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.45,42.95;25.05,46.55"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="10.5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.5"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O128">
+        <dia:attribute name="obj_pos">
+          <dia:point val="20.5,45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="20.5,44.405;20.5,45.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="20.5,45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O129">
+        <dia:attribute name="obj_pos">
+          <dia:point val="21,44.5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.1537,43.4274;24.8789,46.2302"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Export Policy
+of RS-Client X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="21,44.5"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O130">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15,44"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.95,43.95;16.9621,45.9621"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="15,44"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="1.9121320343559631"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#00c0bb"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O131">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15.9561,44.9561"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="15.5723,44.3036;16.3398,45.6086"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="15.9561,45.3436"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="1"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="2"/>
+          </dia:attribute>
+          <dia:connections>
+            <dia:connection handle="0" to="O130" connection="8"/>
+          </dia:connections>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O132">
+        <dia:attribute name="obj_pos">
+          <dia:point val="25.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="25.45,42.95;36.05,46.55"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="25.5,43"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="10.5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.5"/>
+        </dia:attribute>
+        <dia:attribute name="inner_color">
+          <dia:color val="#b7b2b2"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O133">
+        <dia:attribute name="obj_pos">
+          <dia:point val="31.5,45"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="31.5,44.405;31.5,45.15"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="31.5,45"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O134">
+        <dia:attribute name="obj_pos">
+          <dia:point val="32,44.5"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="28.1537,43.46;35.8462,46.165"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Import Policy
+of RS-Client X#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.3999999999999999"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="32,44.5"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O135">
+          <dia:attribute name="obj_pos">
+            <dia:point val="26,44"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="25.95,43.95;28.05,46.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="26,44"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="inner_color">
+            <dia:color val="#a8e400"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="corner_radius">
+            <dia:real val="0.29999999999999999"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O136">
+          <dia:attribute name="obj_pos">
+            <dia:point val="27,45"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="26.6163,44.3475;27.3837,45.6525"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#X#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="1.3999999999999999"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="27,45.3875"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="1"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="2"/>
+          </dia:attribute>
+          <dia:connections>
+            <dia:connection handle="0" to="O135" connection="8"/>
+          </dia:connections>
+        </dia:object>
+      </dia:group>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-rs-processing.png b/doc/fig-rs-processing.png
new file mode 100644 (file)
index 0000000..c0cac5c
Binary files /dev/null and b/doc/fig-rs-processing.png differ
diff --git a/doc/fig-rs-processing.txt b/doc/fig-rs-processing.txt
new file mode 100644 (file)
index 0000000..eafe146
--- /dev/null
@@ -0,0 +1,47 @@
+From Peer A
+ | From RS-Client B
+ |  | From RS-Client C
+ |  |  | From RS-Client D
+ |  |  |  |
+ |  |  |  |           Main / Normal RIB
+ |  |  |  |      ________________________________
+ |  |  |  |     /    _________     _________     \
+ |  |  |  +--->|(D)-|Best     |   | Main    |     |
+ |  |  +--|--->|(C)-|Path     |-->|Local-RIB|->[A]|--->To Peer A
+ |  +--|--|--->|(B)-|Selection|   |         |     |
+ +--|--|--|--->|(A)-|_________|   |_________|     |
+ |  |  |  |     \________________________________/
+ |  |  |  |
+ |  |  |  |          ________________________________
+ |  |  |  |          /    _________     _________     \
+ |  |  |  +--->*D*->|{B}-|Best     |   |RS-Client|     |
+ |  |  +--|--->*C*->|{B}-|Path     |-->|Local-RIB|->[B]|--->To RS-Client B
+ |  |  |  |         |    |Selection|   |  for B  |     |
+ +--|--|--|-------->|{B}-|_________|   |_________|     |
+ |  |  |  |          \________________________________/
+ |  |  |  |
+ |  |  |  |          ________________________________
+ |  |  |  |          /    _________     _________     \
+ |  |  |  +--->*D*->|{C}-|Best     |   |RS-Client|     |
+ |  |  |  |         |    |Path     |-->|Local-RIB|->[C]|--->To RS-Client C
+ |  +--|--|--->*B*->|{C}-|Selection|   |  for C  |     |
+ +--|--|--|-------->|{C}-|_________|   |_________|     |
+ |  |  |             \________________________________/
+ |  |  |
+ |  |  |              ________________________________
+ |  |  |             /    _________     _________     \
+ |  |  |            |    |Best     |   |RS-Client|     |
+ |  |  +------>*C*->|{D}-|Path     |-->|Local-RIB|->[D]|--->To RS-Client D
+ |  +--------->*B*->|{D}-|Selection|   |  for D  |     |
+ +----------------->|{D}-|_________|   |_________|     |
+                     \________________________________/
+
+
+Key:  (X) - 'In'  Filter applied to Peer X's announcements before
+            considering announcement for the normal main Local-RIB
+      [X] - 'Out' Filter applied to announcements to Peer X
+      *X* - 'Export' Filter of RS-Client X, to apply X's policies
+           before its routes may be considered for other RS-Clients
+            RIBs.
+      {X} - 'Import' Filter of RS-Client X, to apply X's policies
+            on routes before allowing them into X's RIB.
diff --git a/doc/fig_topologies_full.txt b/doc/fig_topologies_full.txt
new file mode 100644 (file)
index 0000000..cc8025a
--- /dev/null
@@ -0,0 +1,6 @@
+(RF1)--(RF2)
+  | \  / |
+  |  \/  |
+  |  /\  |
+  | /  \ |
+(RF3)--(RF4)
diff --git a/doc/fig_topologies_rs.txt b/doc/fig_topologies_rs.txt
new file mode 100644 (file)
index 0000000..0bd1730
--- /dev/null
@@ -0,0 +1,5 @@
+(RF1)  (RF2)
+    \  /
+    [RS]
+    /  \
+(RF3)  (RF4)
diff --git a/doc/filter.texi b/doc/filter.texi
new file mode 100644 (file)
index 0000000..5a9a15e
--- /dev/null
@@ -0,0 +1,182 @@
+@node Filtering
+@comment  node-name,  next,  previous,  up
+@chapter Filtering
+
+Quagga provides many very flexible filtering features.  Filtering is used
+for both input and output of the routing information.  Once filtering is
+defined, it can be applied in any direction.
+
+@menu
+* IP Access List::              
+* IP Prefix List::              
+@end menu
+
+@node IP Access List
+@comment  node-name,  next,  previous,  up
+@section IP Access List
+
+@deffn {Command} {access-list @var{name} permit @var{ipv4-network}} {}
+@deffnx {Command} {access-list @var{name} deny @var{ipv4-network}} {}
+@end deffn
+
+Basic filtering is done by @code{access-list} as shown in the
+following example.
+
+@example
+access-list filter deny 10.0.0.0/9
+access-list filter permit 10.0.0.0/8
+@end example
+
+@node IP Prefix List
+@comment  node-name,  next,  previous,  up
+@section IP Prefix List
+
+@command{ip prefix-list} provides the most powerful prefix based
+filtering mechanism.  In addition to @command{access-list} functionality,
+@command{ip prefix-list} has prefix length range specification and
+sequential number specification.  You can add or delete prefix based
+filters to arbitrary points of prefix-list using sequential number specification.
+
+If no ip prefix-list is specified, it acts as permit.  If @command{ip prefix-list} 
+is defined, and no match is found, default deny is applied.
+
+@c @deffn {Command} {ip prefix-list @var{name} [seq @var{number}] permit|deny [le @var{prefixlen}] [ge @var{prefixlen}]} {}
+@deffn {Command} {ip prefix-list @var{name} (permit|deny) @var{prefix} [le @var{len}] [ge @var{len}]} {}
+@deffnx {Command} {ip prefix-list @var{name} seq @var{number} (permit|deny) @var{prefix} [le @var{len}] [ge @var{len}]} {}
+
+You can create @command{ip prefix-list} using above commands.
+
+@table @asis
+
+@item @asis{seq}
+seq @var{number} can be set either automatically or manually.  In the
+case that sequential numbers are set manually, the user may pick any
+number less than 4294967295.  In the case that sequential number are set
+automatically, the sequential number will increase by a unit of five (5)
+per list.  If a list with no specified sequential number is created
+after a list with a specified sequential number, the list will
+automatically pick the next multiple of five (5) as the list number.
+For example, if a list with number 2 already exists and a new list with
+no specified number is created, the next list will be numbered 5.  If
+lists 2 and 7 already exist and a new list with no specified number is
+created, the new list will be numbered 10.
+
+@item @asis{le}
+@command{le} command specifies prefix length.  The prefix list will be 
+applied if the prefix length is less than or equal to the le prefix length.
+
+@item @asis{ge}
+@command{ge} command specifies prefix length.  The prefix list will be 
+applied if the prefix length is greater than or equal to the ge prefix length.
+
+@end table
+
+@end deffn
+
+Less than or equal to prefix numbers and greater than or equal to
+prefix numbers can be used together.  The order of the le and ge
+commands does not matter.
+
+If a prefix list with a different sequential number but with the exact
+same rules as a previous list is created, an error will result.
+However, in the case that the sequential number and the rules are
+exactly similar, no error will result.
+
+If a list with the same sequential number as a previous list is created,
+the new list will overwrite the old list.
+
+Matching of IP Prefix is performed from the smaller sequential number to the
+larger.  The matching will stop once any rule has been applied.
+
+In the case of no le or ge command, the prefix length must match exactly the
+length specified in the prefix list.
+
+@deffn {Command} {no ip prefix-list @var{name}} {}
+@end deffn
+
+@menu
+* ip prefix-list description::  
+* ip prefix-list sequential number control::  
+* Showing ip prefix-list::      
+* Clear counter of ip prefix-list::  
+@end menu
+
+@node ip prefix-list description
+@subsection ip prefix-list description
+
+@deffn {Command} {ip prefix-list @var{name} description @var{desc}} {}
+Descriptions may be added to prefix lists.  This command adds a
+description to the prefix list.
+@end deffn
+
+@deffn {Command} {no ip prefix-list @var{name} description [@var{desc}]} {}
+Deletes the description from a prefix list.  It is possible to use the
+command without the full description.
+@end deffn
+
+@node  ip prefix-list sequential number control
+@subsection ip prefix-list sequential number control
+
+@deffn {Command} {ip prefix-list sequence-number} {}
+With this command, the IP prefix list sequential number is displayed.
+This is the default behavior.
+@end deffn
+
+@deffn {Command} {no ip prefix-list sequence-number} {}
+With this command, the IP prefix list sequential number is not
+displayed.
+@end deffn
+
+@node  Showing ip prefix-list
+@subsection Showing ip prefix-list
+
+@deffn {Command} {show ip prefix-list} {}
+Display all IP prefix lists.
+@end deffn
+
+@deffn {Command} {show ip prefix-list @var{name}} {}
+Show IP prefix list can be used with a prefix list name.
+@end deffn
+
+@deffn {Command} {show ip prefix-list @var{name} seq @var{num}} {}
+Show IP prefix list can be used with a prefix list name and sequential
+number.
+@end deffn
+
+@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m}} {}
+If the command longer is used, all prefix lists with prefix lengths equal to
+or longer than the specified length will be displayed.
+If the command first match is used, the first prefix length match will be
+displayed.
+@end deffn
+
+@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m} longer} {}
+@end deffn
+
+@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m} first-match} {}
+@end deffn
+
+@deffn {Command} {show ip prefix-list summary} {}
+@end deffn
+@deffn {Command} {show ip prefix-list summary @var{name}} {}
+@end deffn
+
+@deffn {Command} {show ip prefix-list detail} {}
+@end deffn
+@deffn {Command} {show ip prefix-list detail @var{name}} {}
+@end deffn
+
+@node  Clear counter of ip prefix-list
+@subsection Clear counter of ip prefix-list
+
+@deffn {Command} {clear ip prefix-list} {}
+Clears the counters of all IP prefix lists.  Clear IP Prefix List can be
+used with a specified name and prefix.
+@end deffn
+
+@deffn {Command} {clear ip prefix-list @var{name}} {}
+@end deffn
+
+@deffn {Command} {clear ip prefix-list @var{name} @var{a.b.c.d/m}} {}
+@end deffn
+
diff --git a/doc/install.texi b/doc/install.texi
new file mode 100644 (file)
index 0000000..4ad282a
--- /dev/null
@@ -0,0 +1,295 @@
+@node  Installation
+@chapter Installation
+
+@cindex How to install Quagga
+@cindex Installation
+@cindex Installing Quagga
+@cindex Building the system
+@cindex Making Quagga
+
+There are three steps for installing the software: configuration,
+compilation, and installation.
+
+@menu
+* Configure the Software::
+* Build the Software::
+* Install the Software::
+@end menu
+
+The easiest way to get Quagga running is to issue the following
+commands:
+
+@example
+% configure
+% make
+% make install
+@end example
+
+@node Configure the Software
+@section Configure the Software
+
+@menu
+* The Configure script and its options::
+* Least-Privilege support::
+* Linux notes::
+@end menu
+
+@node The Configure script and its options
+@subsection The Configure script and its options
+
+@cindex Configuration options
+@cindex Options for configuring
+@cindex Build options
+@cindex Distribution configuration
+@cindex Options to @code{./configure}
+Quagga has an excellent configure script which automatically detects most
+host configurations.  There are several additional configure options you can
+use to turn off IPv6 support, to disable the compilation of specific
+daemons, and to enable SNMP support.
+
+@table @option
+@item --disable-ipv6
+Turn off IPv6 related features and daemons.  Quagga configure script
+automatically detects IPv6 stack.  But sometimes you might want to
+disable IPv6 support of Quagga.
+@item --disable-zebra
+Do not build zebra daemon.
+@item --disable-ripd
+Do not build ripd.
+@item --disable-ripngd
+Do not build ripngd.
+@item --disable-ospfd
+Do not build ospfd.
+@item --disable-ospf6d
+Do not build ospf6d.
+@item --disable-bgpd
+Do not build bgpd.
+@item --disable-bgp-announce
+Make @command{bgpd} which does not make bgp announcements at all.  This
+feature is good for using @command{bgpd} as a BGP announcement listener.
+@item --enable-netlink
+Force to enable @sc{gnu}/Linux netlink interface.  Quagga configure
+script detects netlink interface by checking a header file.  When the header
+file does not match to the current running kernel, configure script will
+not turn on netlink support.
+@item --enable-snmp
+Enable SNMP support.  By default, SNMP support is disabled.
+@item --disable-opaque-lsa
+Disable support for Opaque LSAs (RFC2370) in ospfd.
+@item --disable-ospfapi
+Disable support for OSPF-API, an API to interface directly with ospfd.
+OSPF-API is enabled if --enable-opaque-lsa is set.
+@item --disable-ospfclient
+Disable building of the example OSPF-API client.
+@item --disable-ospf-te
+Disable support for OSPF Traffic Engineering Extension (RFC3630) this
+requires support for Opaque LSAs.
+@item --disable-ospf-ri
+Disable support for OSPF Router Information (RFC4970 & RFC5088) this
+requires support for Opaque LSAs and Traffic Engineering.
+@item --enable-isisd
+Build isisd.
+@item --enable-isis-topology
+Enable IS-IS topology generator.
+@item --enable-isis-te
+Enable Traffic Engineering Extension for ISIS (RFC5305)
+@item --enable-multipath=@var{ARG}
+Enable support for Equal Cost Multipath. @var{ARG} is the maximum number
+of ECMP paths to allow, set to 0 to allow unlimited number of paths.
+@item --disable-rtadv
+Disable support IPV6 router advertisement in zebra.
+@item --enable-gcc-rdynamic
+Pass the @command{-rdynamic} option to the linker driver.  This is in most
+cases neccessary for getting usable backtraces.  This option defaults to on
+if the compiler is detected as gcc, but giving an explicit enable/disable is
+suggested.
+@item --enable-backtrace
+Controls backtrace support for the crash handlers. This is autodetected by
+default. Using the switch will enforce the requested behaviour, failing with
+an error if support is requested but not available.  On BSD systems, this
+needs libexecinfo, while on glibc support for this is part of libc itself.
+@end table
+
+You may specify any combination of the above options to the configure
+script.  By default, the executables are placed in @file{/usr/local/sbin} 
+and the configuration files in @file{/usr/local/etc}. The @file{/usr/local/}
+installation prefix and other directories may be changed using the following 
+options to the configuration script.
+
+@table @option
+@item --prefix=@var{prefix}
+Install architecture-independent files in @var{prefix} [/usr/local].
+@item --sysconfdir=@var{dir}
+Look for configuration files in @var{dir} [@var{prefix}/etc]. Note
+that sample configuration files will be installed here.
+@item --localstatedir=@var{dir}
+Configure zebra to use @var{dir} for local state files, such
+as pid files and unix sockets.
+@end table
+
+@example
+% ./configure --disable-ipv6
+@end example
+
+This command will configure zebra and the routing daemons.
+
+@node Least-Privilege support
+@subsection Least-Privilege support
+
+@cindex Quagga Least-Privileges
+@cindex Quagga Privileges
+
+Additionally, you may configure zebra to drop its elevated privileges
+shortly after startup and switch to another user. The configure script will
+automatically try to configure this support. There are three configure
+options to control the behaviour of Quagga daemons.
+
+@table @option
+@item --enable-user=@var{user}
+Switch to user @var{ARG} shortly after startup, and run as user @var{ARG}
+in normal operation.
+@item --enable-group=@var{group}
+Switch real and effective group to @var{group} shortly after
+startup. 
+@item --enable-vty-group=@var{group}
+Create Unix Vty sockets (for use with vtysh) with group owndership set to
+@var{group}. This allows one to create a seperate group which is
+restricted to accessing only the Vty sockets, hence allowing one to
+delegate this group to individual users, or to run vtysh setgid to
+this group.
+@end table
+
+The default user and group which will be configured is 'quagga' if no user
+or group is specified. Note that this user or group requires write access to
+the local state directory (see --localstatedir) and requires at least read
+access, and write access if you wish to allow daemons to write out their
+configuration, to the configuration directory (see --sysconfdir).
+
+On systems which have the 'libcap' capabilities manipulation library
+(currently only linux), the quagga system will retain only minimal
+capabilities required, further it will only raise these capabilities for
+brief periods. On systems without libcap, quagga will run as the user
+specified and only raise its uid back to uid 0 for brief periods.
+
+@node Linux notes
+@subsection Linux Notes
+
+@cindex Configuring Quagga
+@cindex Building on Linux boxes
+@cindex Linux configurations
+
+There are several options available only to @sc{gnu}/Linux systems:
+@footnote{@sc{gnu}/Linux has very flexible kernel configuration features}.  If
+you use @sc{gnu}/Linux, make sure that the current kernel configuration is
+what you want.  Quagga will run with any kernel configuration but some
+recommendations do exist.
+
+@table @var
+
+@item CONFIG_NETLINK
+Kernel/User netlink socket. This is a brand new feature which enables an
+advanced interface between the Linux kernel and zebra (@pxref{Kernel Interface}).
+
+@item CONFIG_RTNETLINK
+Routing messages.
+This makes it possible to receive netlink routing messages.  If you
+specify this option, @command{zebra} can detect routing information
+updates directly from the kernel (@pxref{Kernel Interface}).
+
+@item CONFIG_IP_MULTICAST
+IP: multicasting.  
+This option should be specified when you use @command{ripd} (@pxref{RIP}) or
+@command{ospfd} (@pxref{OSPFv2}) because these protocols use multicast.
+
+@end table
+
+IPv6 support has been added in @sc{gnu}/Linux kernel version 2.2.  If you
+try to use the Quagga IPv6 feature on a @sc{gnu}/Linux kernel, please
+make sure the following libraries have been installed.  Please note that
+these libraries will not be needed when you uses @sc{gnu} C library 2.1
+or upper.
+
+@table @code
+
+@item inet6-apps
+The @code{inet6-apps} package includes basic IPv6 related libraries such
+as @code{inet_ntop} and @code{inet_pton}.  Some basic IPv6 programs such
+as @command{ping}, @command{ftp}, and @command{inetd} are also
+included. The @code{inet-apps} can be found at
+@uref{ftp://ftp.inner.net/pub/ipv6/}.
+
+@item net-tools
+The @code{net-tools} package provides an IPv6 enabled interface and
+routing utility.  It contains @command{ifconfig}, @command{route},
+@command{netstat}, and other tools.  @code{net-tools} may be found at
+@uref{http://www.tazenda.demon.co.uk/phil/net-tools/}.
+
+@end table
+@c A - end of footnote 
+
+@node Build the Software
+@section Build the Software
+
+After configuring the software, you will need to compile it for your
+system. Simply issue the command @command{make} in the root of the source
+directory and the software will be compiled. If you have *any* problems
+at this stage, be certain to send a bug report @xref{Bug Reports}.
+
+@example
+% ./configure
+.
+.
+.
+./configure output
+.
+.
+.
+% make
+@end example
+@c A - End of node, Building the Software
+
+
+@node Install the Software
+@comment  node-name,  next,  previous,  up
+@section Install the Software
+
+Installing the software to your system consists of copying the compiled
+programs and supporting files to a standard location. After the
+installation process has completed, these files have been copied
+from your work directory to @file{/usr/local/bin}, and @file{/usr/local/etc}.
+
+To install the Quagga suite, issue the following command at your shell
+prompt: @command{make install}.
+
+@example
+%
+% make install
+%
+@end example
+
+Quagga daemons have their own terminal interface or VTY.  After
+installation, you have to setup each beast's port number to connect to
+them.  Please add the following entries to @file{/etc/services}.
+
+@example
+zebrasrv      2600/tcp           # zebra service
+zebra         2601/tcp           # zebra vty
+ripd          2602/tcp           # RIPd vty
+ripngd        2603/tcp           # RIPngd vty
+ospfd         2604/tcp           # OSPFd vty
+bgpd          2605/tcp           # BGPd vty
+ospf6d        2606/tcp           # OSPF6d vty
+ospfapi       2607/tcp           # ospfapi
+isisd         2608/tcp           # ISISd vty
+pimd          2611/tcp           # PIMd vty
+nhrpd         2612/tcp           # nhrpd vty
+@end example
+
+If you use a FreeBSD newer than 2.2.8, the above entries are already
+added to @file{/etc/services} so there is no need to add it. If you
+specify a port number when starting the daemon, these entries may not be
+needed.
+
+You may need to make changes to the config files in
+@file{@value{INSTALL_PREFIX_ETC}/*.conf}. @xref{Config Commands}.
diff --git a/doc/ipv6.texi b/doc/ipv6.texi
new file mode 100644 (file)
index 0000000..5be2bab
--- /dev/null
@@ -0,0 +1,183 @@
+@node IPv6 Support
+@chapter IPv6 Support
+
+Quagga fully supports IPv6 routing.  As described so far, Quagga supports
+RIPng, OSPFv3, and BGP-4+.  You can give IPv6 addresses to an interface
+and configure static IPv6 routing information.  Quagga IPv6 also provides
+automatic address configuration via a feature called @code{address
+auto configuration}.  To do it, the router must send router advertisement
+messages to the all nodes that exist on the network.
+
+@menu
+* Router Advertisement::        
+@end menu
+
+@node Router Advertisement
+@section Router Advertisement
+
+@deffn {Interface Command} {no ipv6 nd suppress-ra} {}
+Send router advertisment messages.
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd suppress-ra} {}
+Don't send router advertisment messages.
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd prefix @var{ipv6prefix} [@var{valid-lifetime}] [@var{preferred-lifetime}] [off-link] [no-autoconfig] [router-address]} {}
+Configuring the IPv6 prefix to include in router advertisements. Several prefix
+specific optional parameters and flags may follow:
+@itemize @bullet
+@item
+@var{valid-lifetime} - the length of time in seconds during what the prefix is
+valid for the purpose of on-link determination. Value @var{infinite} represents
+infinity (i.e. a value of all one bits (@code{0xffffffff})).
+
+Range: @code{<0-4294967295>}  Default: @code{2592000}
+
+@item
+@var{preferred-lifetime} - the length of time in seconds during what addresses
+generated from the prefix remain preferred. Value @var{infinite} represents
+infinity.
+
+Range: @code{<0-4294967295>}  Default: @code{604800}
+
+@item
+@var{off-link} - indicates that advertisement makes no statement about on-link or
+off-link properties of the prefix.
+
+Default: not set, i.e. this prefix can be used for on-link determination.
+
+@item
+@var{no-autoconfig} - indicates to hosts on the local link that the specified prefix
+cannot be used for IPv6 autoconfiguration.
+
+Default: not set, i.e. prefix can be used for autoconfiguration.
+
+@item
+@var{router-address} - indicates to hosts on the local link that the specified 
+prefix 
+contains a complete IP address by setting R flag.
+
+Default: not set, i.e. hosts do not assume a complete IP address is placed.
+@end itemize
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd ra-interval <1-1800>} {}
+@deffnx {Interface Command} {no ipv6 nd ra-interval [<1-1800>]} {}
+The  maximum  time allowed between sending unsolicited multicast router
+advertisements from the interface, in seconds.
+
+Default: @code{600}
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd ra-interval msec <70-1800000>} {}
+@deffnx {Interface Command} {no ipv6 nd ra-interval [msec <70-1800000>]} {}
+The  maximum  time allowed between sending unsolicited multicast router
+advertisements from the interface, in milliseconds.
+
+Default: @code{600000}
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd ra-lifetime <0-9000>} {}
+@deffnx {Interface Command} {no ipv6 nd ra-lifetime [<0-9000>]} {}
+The value to be placed in the Router Lifetime field of router advertisements
+sent from the interface, in seconds. Indicates the usefulness of the router
+as a default router on this interface. Setting the value to zero indicates
+that the router should not be considered a default router on this interface.
+Must be either zero or between value specified with @var{ipv6 nd ra-interval}
+(or default) and 9000 seconds.
+
+Default: @code{1800}
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd reachable-time <1-3600000>} {}
+@deffnx {Interface Command} {no ipv6 nd reachable-time [<1-3600000>]} {}
+The value to be placed in the Reachable Time field in the Router Advertisement
+messages sent by the router, in milliseconds. The configured time enables the
+router to detect unavailable neighbors. The value zero means unspecified (by
+this router).
+
+Default: @code{0}
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd managed-config-flag} {}
+@deffnx {Interface Command} {no ipv6 nd managed-config-flag} {}
+Set/unset flag in IPv6 router advertisements which indicates to hosts that they
+should use managed (stateful) protocol for addresses autoconfiguration in
+addition to any addresses autoconfigured using stateless address
+autoconfiguration.
+
+Default: not set
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd other-config-flag} {}
+@deffnx {Interface Command} {no ipv6 nd other-config-flag} {}
+Set/unset flag in IPv6 router advertisements which indicates to hosts that
+they should use administered (stateful) protocol to obtain autoconfiguration
+information other than addresses.
+
+Default: not set
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd home-agent-config-flag} {}
+@deffnx {Interface Command} {no ipv6 nd home-agent-config-flag} {}
+Set/unset flag in IPv6 router advertisements which indicates to hosts that
+the router acts as a Home Agent and includes a Home Agent Option.
+
+Default: not set
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd home-agent-preference <0-65535>} {}
+@deffnx {Interface Command} {no ipv6 nd home-agent-preference [<0-65535>]} {}
+The value to be placed in Home Agent Option, when Home Agent config flag is set, 
+which indicates to hosts Home Agent preference. The default value of 0 stands
+for the lowest preference possible.
+
+Default: 0
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd home-agent-lifetime <0-65520>} {}
+@deffnx {Interface Command} {no ipv6 nd home-agent-lifetime [<0-65520>]} {}
+The value to be placed in Home Agent Option, when Home Agent config flag is set, 
+which indicates to hosts Home Agent Lifetime. The default value of 0 means to
+place the current Router Lifetime value.
+
+Default: 0
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd adv-interval-option} {}
+@deffnx {Interface Command} {no ipv6 nd adv-interval-option} {}
+Include an Advertisement Interval option which indicates to hosts the maximum time, 
+in milliseconds, between successive unsolicited Router Advertisements.
+
+Default: not set
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd router-preference (high|medium|low)} {}
+@deffnx {Interface Command} {no ipv6 nd router-preference [(high|medium|low)]} {}
+Set default router preference in IPv6 router advertisements per RFC4191.
+
+Default: medium
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd mtu <1-65535>} {}
+@deffnx {Interface Command} {no ipv6 nd mtu [<1-65535>]} {}
+Include an MTU (type 5) option in each RA packet to assist the attached hosts
+in proper interface configuration. The announced value is not verified to be
+consistent with router interface MTU.
+
+Default: don't advertise any MTU option
+@end deffn
+
+@example
+@group
+interface eth0
+ no ipv6 nd suppress-ra
+ ipv6 nd prefix 2001:0DB8:5009::/64
+@end group
+@end example
+
+For more information see @cite{RFC2462 (IPv6 Stateless Address Autoconfiguration)}
+, @cite{RFC4861 (Neighbor Discovery for IP Version 6 (IPv6))}
+, @cite{RFC6275 (Mobility Support in IPv6)}
+and @cite{RFC4191 (Default Router Preferences and More-Specific Routes)}.
diff --git a/doc/isisd.8 b/doc/isisd.8
new file mode 100644 (file)
index 0000000..84e6cf5
--- /dev/null
@@ -0,0 +1,111 @@
+.TH IS-IS 8 "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
+.B isisd
+[
+.B \-dhv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B isisd
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B isisd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/isisd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When isisd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart isisd.  The likely default is \fB\fI/var/run/isisd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the isisd VTY will listen on. This defaults to
+2608, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the isisd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/isisd
+The default location of the 
+.B isisd
+binary.
+.TP
+.BI /usr/local/etc/isisd.conf
+The default location of the 
+.B isisd
+config file.
+.TP
+.BI $(PWD)/isisd.log 
+If the 
+.B isisd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBisisd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The isisd process may log to standard output, to a VTY, to a log
+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 vtysh (1)
+.SH BUGS
+\fBisisd\fR is ALPHA quality at the moment and hasn't any way ready for
+production use.
+
+.B isisd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://isisd.sourceforge.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/isisd.texi b/doc/isisd.texi
new file mode 100644 (file)
index 0000000..bbc2896
--- /dev/null
@@ -0,0 +1,432 @@
+@cindex ISIS
+@node ISIS
+@chapter ISIS
+
+@acronym{ISIS,Intermediate System to Intermediate System} is a routing protocol
+which is described in @cite{ISO10589, RFC1195, RFC5308}.  ISIS is an
+@acronym{IGP,Interior Gateway Protocol}.  Compared with @acronym{RIP},
+@acronym{ISIS} can provide scalable network support and faster
+convergence times like @acronym{OSPF}. ISIS is widely used in large networks such as
+@acronym{ISP,Internet Service Provider} and carrier backbone networks.
+
+@menu
+* Configuring isisd::
+* ISIS router::
+* ISIS Timer::
+* ISIS region::
+* ISIS interface::
+* Showing ISIS information::
+* ISIS Traffic Engineering::
+* Debugging ISIS::
+* ISIS Configuration Examples::
+@end menu
+
+@node Configuring isisd
+@section Configuring isisd
+
+There are no @command{isisd} specific options.  Common options can be
+specified (@pxref{Common Invocation Options}) to @command{isisd}.
+@command{isisd} needs to acquire interface information from
+@command{zebra} in order to function. Therefore @command{zebra} must be
+running before invoking @command{isisd}. Also, if @command{zebra} is
+restarted then @command{isisd} must be too.
+
+Like other daemons, @command{isisd} configuration is done in @acronym{ISIS}
+specific configuration file @file{isisd.conf}.
+
+@node ISIS router
+@section ISIS router
+
+To start ISIS process you have to specify the ISIS router. As of this
+writing, @command{isisd} does not support multiple ISIS processes.
+
+@deffn Command {router isis WORD} {}
+@deffnx Command {no router isis WORD} {}
+@anchor{router isis WORD}Enable or disable the ISIS process by specifying the ISIS domain with 'WORD'.
+@command{isisd} does not yet support multiple ISIS processes but you must specify
+the name of ISIS process. The ISIS process name 'WORD' is then used for interface
+(see command @ref{ip router isis WORD}).
+@end deffn
+
+@deffn {ISIS Command} {net XX.XXXX. ... .XXX.XX} {}
+@deffnx {ISIS Command} {no net XX.XXXX. ... .XXX.XX} {}
+Set/Unset network entity title (NET) provided in ISO format.
+@end deffn
+
+@deffn {ISIS Command} {hostname dynamic} {}
+@deffnx {ISIS Command} {no hostname dynamic} {}
+Enable support for dynamic hostname.
+@end deffn
+
+@deffn {ISIS Command} {area-password [clear | md5] <password>} {}
+@deffnx {ISIS Command} {domain-password [clear | md5] <password>} {}
+@deffnx {ISIS Command} {no area-password} {}
+@deffnx {ISIS Command} {no domain-password} {}
+Configure the authentication password for an area, respectively a domain,
+as clear text or md5 one.
+@end deffn
+
+@deffn {ISIS Command} {log-adjacency-changes} {}
+@deffnx {ISIS Command} {no log-adjacency-changes} {}
+Log changes in adjacency state.
+@end deffn
+
+@deffn {ISIS Command} {metric-style [narrow | transition | wide]} {}
+@deffnx {ISIS Command} {no metric-style} {}
+@anchor{metric-style}Set old-style (ISO 10589) or new-style packet formats:
+  - narrow      Use old style of TLVs with narrow metric
+  - transition  Send and accept both styles of TLVs during transition
+  - wide        Use new style of TLVs to carry wider metric
+@end deffn
+
+@deffn {ISIS Command} {set-overload-bit} {}
+@deffnx {ISIS Command} {no set-overload-bit} {}
+Set overload bit to avoid any transit traffic.
+@end deffn
+
+@node ISIS Timer
+@section ISIS Timer
+
+@deffn {ISIS Command} {lsp-gen-interval <1-120>} {}
+@deffnx {ISIS Command} {lsp-gen-interval [level-1 | level-2] <1-120>} {}
+@deffnx {ISIS Command} {no lsp-gen-interval} {}
+@deffnx {ISIS Command} {no lsp-gen-interval [level-1 | level-2]} {}
+Set minimum interval in seconds between regenerating same LSP,
+globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {}
+@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {}
+Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {}
+@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {}
+Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {max-lsp-lifetime <360-65535>} {}
+@deffnx {ISIS Command} {max-lsp-lifetime [level-1 | level-2] <360-65535>} {}
+@deffnx {ISIS Command} {no max-lsp-lifetime} {}
+@deffnx {ISIS Command} {no max-lsp-lifetime [level-1 | level-2]} {}
+Set LSP maximum LSP lifetime in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {spf-interval <1-120>} {}
+@deffnx {ISIS Command} {spf-interval [level-1 | level-2] <1-120>} {}
+@deffnx {ISIS Command} {no spf-interval} {}
+@deffnx {ISIS Command} {no spf-interval [level-1 | level-2]} {}
+Set minimum interval between consecutive SPF calculations in seconds.
+@end deffn
+
+@node ISIS region
+@section ISIS region
+
+@deffn {ISIS Command} {is-type [level-1 | level-1-2 | level-2-only]} {}
+@deffnx {ISIS Command} {no is-type} {}
+Define the ISIS router behavior:
+ - level-1       Act as a station router only
+ - level-1-2     Act as both a station router and an area router
+ - level-2-only  Act as an area router only
+@end deffn
+
+@node ISIS interface
+@section ISIS interface
+
+@deffn {Interface Command} {ip router isis WORD} {}
+@deffnx {Interface Command} {no ip router isis WORD} {}
+@anchor{ip router isis WORD}Activate ISIS adjacency on this interface. Note that the name
+of ISIS instance must be the same as the one used to configure the ISIS process
+(see command @ref{router isis WORD}).
+@end deffn
+
+@deffn {Interface Command} {isis circuit-type [level-1 | level-1-2 | level-2]} {}
+@deffnx {Interface Command} {no isis circuit-type} {}
+Configure circuit type for interface:
+  - level-1       Level-1 only adjacencies are formed
+  - level-1-2     Level-1-2 adjacencies are formed
+  - level-2-only  Level-2 only adjacencies are formed
+@end deffn
+
+@deffn {Interface Command} {isis csnp-interval <1-600>} {}
+@deffnx {Interface Command} {isis csnp-interval <1-600> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis csnp-interval} {}
+@deffnx {Interface Command} {no isis csnp-interval [level-1 | level-2]} {}
+Set CSNP interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis hello padding} {}
+Add padding to IS-IS hello packets.
+@end deffn
+
+@deffn {Interface Command} {isis hello-interval <1-600>} {}
+@deffnx {Interface Command} {isis hello-interval <1-600> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis hello-interval} {}
+@deffnx {Interface Command} {no isis hello-interval [level-1 | level-2]} {}
+Set Hello interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis hello-multiplier <2-100>} {}
+@deffnx {Interface Command} {isis hello-multiplier <2-100> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis hello-multiplier} {}
+@deffnx {Interface Command} {no isis hello-multiplier [level-1 | level-2]} {}
+Set multiplier for Hello holding time globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis metric [<0-255> | <0-16777215>]} {}
+@deffnx {Interface Command} {isis metric [<0-255> | <0-16777215>] [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis metric} {}
+@deffnx {Interface Command} {no isis metric [level-1 | level-2]} {}
+Set default metric value globally, for an area (level-1) or a domain (level-2).
+Max value depend if metric support narrow or wide value (see command @ref{metric-style}).
+@end deffn
+
+@deffn {Interface Command} {isis network point-to-point} {}
+@deffnx {Interface Command} {no isis network point-to-point} {}
+Set network type to 'Point-to-Point' (broadcast by default).
+@end deffn
+
+@deffn {Interface Command} {isis passive} {}
+@deffnx {Interface Command} {no isis passive} {}
+Configure the passive mode for this interface.
+@end deffn
+
+@deffn {Interface Command} {isis password [clear | md5] <password>} {}
+@deffnx {Interface Command} {no isis password} {}
+Configure the authentication password (clear or encoded text) for the interface.
+@end deffn
+
+@deffn {Interface Command} {isis priority <0-127>} {}
+@deffnx {Interface Command} {isis priority <0-127> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis priority} {}
+@deffnx {Interface Command} {no isis priority [level-1 | level-2]} {}
+Set priority for Designated Router election, globally, for the area (level-1)
+or the domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis psnp-interval <1-120>} {}
+@deffnx {Interface Command} {isis psnp-interval <1-120> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis psnp-interval} {}
+@deffnx {Interface Command} {no isis psnp-interval [level-1 | level-2]} {}
+Set PSNP interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@node Showing ISIS information
+@section Showing ISIS information
+
+@deffn {Command} {show isis summary} {}
+Show summary information about ISIS.
+@end deffn
+
+@deffn {Command} {show isis hostname} {}
+Show information about ISIS node.
+@end deffn
+
+@deffn {Command} {show isis interface} {}
+@deffnx {Command} {show isis interface detail} {}
+@deffnx {Command} {show isis interface <interface name>} {}
+Show state and configuration of ISIS specified interface, or all
+interfaces if no interface is given with or without details.
+@end deffn
+
+@deffn {Command} {show isis neighbor} {}
+@deffnx {Command} {show isis neighbor <System Id>} {}
+@deffnx {Command} {show isis neighbor detail} {}
+Show state and information of ISIS specified neighbor, or all
+neighbors if no system id is given with or without details.
+@end deffn
+
+@deffn {Command} {show isis database} {}
+@deffnx {Command} {show isis database [detail]} {}
+@deffnx {Command} {show isis database <LSP id> [detail]} {}
+@deffnx {Command} {show isis database detail <LSP id>} {}
+Show the ISIS database globally, for a specific LSP id without or with details.
+@end deffn
+
+@deffn {Command} {show isis topology} {}
+@deffnx {Command} {show isis topology [level-1|level-2]} {}
+Show topology IS-IS paths to Intermediate Systems, globally,
+in area (level-1) or domain (level-2).
+@end deffn
+
+@deffn {Command} {show ip route isis} {}
+Show the ISIS routing table, as determined by the most recent SPF calculation.
+@end deffn
+
+@node ISIS Traffic Engineering
+@section Traffic Engineering
+
+@deffn {ISIS Command} {mpls-te on} {}
+@deffnx {ISIS Command} {no mpls-te} {}
+Enable Traffic Engineering LSP flooding.
+@end deffn
+
+@deffn {ISIS Command} {mpls-te router-address <A.B.C.D>} {}
+@deffnx {ISIS Command} {no mpls-te router-address} {}
+Configure stable IP address for MPLS-TE.
+@end deffn
+
+@deffn {Command} {show isis mpls-te interface} {}
+@deffnx {Command} {show isis mpls-te interface @var{interface}} {}
+Show MPLS Traffic Engineering parameters for all or specified interface.
+@end deffn
+
+@deffn {Command} {show isis mpls-te router} {}
+Show Traffic Engineering router parameters.
+@end deffn
+
+@node Debugging ISIS
+@section Debugging ISIS
+
+@deffn {Command} {debug isis adj-packets} {}
+@deffnx {Command} {no debug isis adj-packets} {}
+IS-IS Adjacency related packets.
+@end deffn
+
+@deffn {Command} {debug isis checksum-errors} {}
+@deffnx {Command} {no debug isis checksum-errors} {}
+IS-IS LSP checksum errors.
+@end deffn
+
+@deffn {Command} {debug isis events} {}
+@deffnx {Command} {no debug isis events} {}
+IS-IS Events.
+@end deffn
+
+@deffn {Command} {debug isis local-updates} {}
+@deffnx {Command} {no debug isis local-updates} {}
+IS-IS local update packets.
+@end deffn
+
+@deffn {Command} {debug isis packet-dump} {}
+@deffnx {Command} {no debug isis packet-dump} {}
+IS-IS packet dump.
+@end deffn
+
+@deffn {Command} {debug isis protocol-errors} {}
+@deffnx {Command} {no debug isis protocol-errors} {}
+IS-IS LSP protocol errors.
+@end deffn
+
+@deffn {Command} {debug isis route-events} {}
+@deffnx {Command} {no debug isis route-events} {}
+IS-IS Route related events.
+@end deffn
+
+@deffn {Command} {debug isis snp-packets} {}
+@deffnx {Command} {no debug isis snp-packets} {}
+IS-IS CSNP/PSNP packets.
+@end deffn
+
+@deffn {Command} {debug isis spf-events} {}
+@deffnx {Command} {debug isis spf-statistics} {}
+@deffnx {Command} {debug isis spf-triggers} {}
+@deffnx {Command} {no debug isis spf-events} {}
+@deffnx {Command} {no debug isis spf-statistics} {}
+@deffnx {Command} {no debug isis spf-triggers} {}
+IS-IS Shortest Path First Events, Timing and Statistic Data
+and triggering events.
+@end deffn
+
+@deffn {Command} {debug isis update-packets} {}
+@deffnx {Command} {no debug isis update-packets} {}
+Update related packets.
+@end deffn
+
+@deffn {Command} {show debugging isis} {}
+Print which ISIS debug level is activate.
+@end deffn
+
+@node ISIS Configuration Examples
+@section ISIS Configuration Examples
+A simple example, with MD5 authentication enabled:
+
+@example
+@group
+!
+interface eth0
+ ip router isis FOO
+ isis network point-to-point
+ isis circuit-type level-2-only
+!
+router isis FOO
+net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00
+ metric-style wide
+ is-type level-2-only
+@end group
+@end example
+
+
+A Traffic Engineering configuration, with Inter-ASv2 support.
+
+ - First, the 'zebra.conf' part:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/zebra.log
+!
+interface eth0
+ ip address 10.2.2.2/24
+ mpls-te on
+ mpls-te link metric 10
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xab
+!
+interface eth1
+ ip address 10.1.1.1/24
+ mpls-te on
+ mpls-te link metric 10
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xab
+ mpls-te neighbor 10.1.1.2 as 65000
+@end group
+@end example
+
+ - Then the 'isisd.conf' itself:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/isisd.log
+!
+!
+interface eth0
+ ip router isis FOO
+!
+interface eth1
+ ip router isis FOO
+!
+!
+router isis FOO
+ isis net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00
+  mpls-te on
+  mpls-te router-address 10.1.1.1
+!
+line vty
+@end group
+@end example
diff --git a/doc/kernel.texi b/doc/kernel.texi
new file mode 100644 (file)
index 0000000..67fbb5e
--- /dev/null
@@ -0,0 +1,47 @@
+@node Kernel Interface
+@chapter Kernel Interface
+
+There are several different methods for reading kernel routing table
+information, updating kernel routing tables, and for looking up
+interfaces.
+
+@table @samp
+
+@item ioctl
+The @samp{ioctl} method is a very traditional way for reading or writing
+kernel information.  @samp{ioctl} can be used for looking up interfaces
+and for modifying interface addresses, flags, mtu settings and other
+types of information.  Also, @samp{ioctl} can insert and delete kernel
+routing table entries.  It will soon be available on almost any platform
+which zebra supports, but it is a little bit ugly thus far, so if a
+better method is supported by the kernel, zebra will use that.
+
+@item sysctl
+@samp{sysctl} can lookup kernel information using MIB (Management
+Information Base) syntax.  Normally, it only provides a way of getting
+information from the kernel.  So one would usually want to change kernel
+information using another method such as @samp{ioctl}.
+
+@item proc filesystem
+@samp{proc filesystem} provides an easy way of getting kernel
+information.
+
+@item routing socket
+
+@item netlink
+On recent Linux kernels (2.0.x and 2.2.x), there is a kernel/user
+communication support called @code{netlink}.  It makes asynchronous
+communication between kernel and Quagga possible, similar to a routing
+socket on BSD systems.
+
+Before you use this feature, be sure to select (in kernel configuration) 
+the kernel/netlink support option 'Kernel/User network link driver' and 
+'Routing messages'.
+
+Today, the /dev/route special device file is obsolete.  Netlink
+communication is done by reading/writing over netlink socket.
+
+After the kernel configuration, please reconfigure and rebuild Quagga.
+You can use netlink as a dynamic routing update channel between Quagga
+and the kernel.
+@end table
diff --git a/doc/main.texi b/doc/main.texi
new file mode 100644 (file)
index 0000000..6d42c04
--- /dev/null
@@ -0,0 +1,498 @@
+@node Zebra
+@chapter Zebra
+
+@c SYNOPSIS
+@command{zebra} is an IP routing manager.  It provides kernel routing
+table updates, interface lookups, and redistribution of routes between
+different routing protocols.
+
+@menu
+* Invoking zebra::              Running the program
+* Interface Commands::          Commands for zebra interfaces
+* Static Route Commands::       Commands for adding static routes
+* Multicast RIB Commands::      Commands for controlling MRIB behavior
+* zebra Route Filtering::       Commands for zebra route filtering
+* zebra FIB push interface::    Interface to optional FPM component
+* zebra Terminal Mode Commands::  Commands for zebra's VTY
+@end menu
+
+
+@node Invoking zebra
+@section Invoking zebra
+
+Besides the common invocation options (@pxref{Common Invocation Options}), the
+@command{zebra} specific invocation options are listed below.
+
+@table @samp
+@item -b
+@itemx --batch
+Runs in batch mode.  @command{zebra} parses configuration file and terminates
+immediately.
+
+@item -k
+@itemx --keep_kernel
+When zebra starts up, don't delete old self inserted routes.
+
+@item -r
+@itemx --retain
+When program terminates, retain routes added by zebra.
+
+@end table
+
+@node Interface Commands
+@section Interface Commands
+
+@menu
+* Standard Commands::
+* Link Parameters Commands::            
+@end menu
+
+@node Standard Commands
+@subsection Standard Commands
+
+@deffn Command {interface @var{ifname}} {}
+@end deffn
+
+@deffn {Interface Command} {shutdown} {}
+@deffnx {Interface Command} {no shutdown} {}
+Up or down the current interface.
+@end deffn
+
+@deffn {Interface Command} {ip address @var{address/prefix}} {}
+@deffnx {Interface Command} {ipv6 address @var{address/prefix}} {}
+@deffnx {Interface Command} {no ip address @var{address/prefix}} {}
+@deffnx {Interface Command} {no ipv6 address @var{address/prefix}} {}
+Set the IPv4 or IPv6 address/prefix for the interface.
+@end deffn
+
+@deffn {Interface Command} {ip address @var{address/prefix} secondary} {}
+@deffnx {Interface Command} {no ip address @var{address/prefix} secondary} {}
+Set the secondary flag for this address. This causes ospfd to not treat the
+address as a distinct subnet.
+@end deffn
+
+@deffn {Interface Command} {description @var{description} ...} {}
+Set description for the interface.
+@end deffn
+
+@deffn {Interface Command} {multicast} {}
+@deffnx {Interface Command} {no multicast} {}
+Enable or disables multicast flag for the interface.
+@end deffn
+
+@deffn {Interface Command} {bandwidth <1-10000000>} {}
+@deffnx {Interface Command} {no bandwidth <1-10000000>} {}
+Set bandwidth value of the interface in kilobits/sec.  This is for
+calculating OSPF cost. This command does not affect the actual device
+configuration.
+@end deffn
+
+@deffn {Interface Command} {link-detect} {}
+@deffnx {Interface Command} {no link-detect} {}
+Enable/disable link-detect on platforms which support this. Currently
+only Linux and Solaris, and only where network interface drivers support reporting
+link-state via the IFF_RUNNING flag.
+@end deffn
+
+@node Link Parameters Commands
+@subsection Link Parameters Commands
+
+@deffn {Interface Command} {link-params} {}
+@deffnx {Interface Command} {no link-param} {}
+Enter into the link parameters sub node. At least 'enable' must be set to activate the link parameters,
+and consequently Traffic Engineering on this interface. MPLS-TE must be enable at the OSPF (@ref{OSPF Traffic Engineering})
+or ISIS (@ref{ISIS Traffic Engineering}) router level in complement to this.
+Disable link parameters for this interface.
+@end deffn
+
+Under link parameter statement, the following commands set the different TE values:
+
+@deffn link-params {enable}
+Enable link parameters for this interface.
+@end deffn
+
+@deffn link-params {metric <0-4294967295>} {}
+@deffnx link-params {max-bw @var{bandwidth}} {}
+@deffnx link-params {max-rsv-bw @var{bandwidth}} {}
+@deffnx link-params {unrsv-bw <0-7> @var{bandwidth}} {}
+@deffnx link-params {admin-grp @var{bandwidth}} {}
+These commands specifies the Traffic Engineering parameters of the interface in conformity to RFC3630 (OSPF)
+or RFC5305 (ISIS).
+There are respectively the TE Metric (different from the OSPF or ISIS metric), Maximum Bandwidth (interface speed
+by default), Maximum Reservable Bandwidth, Unreserved Bandwidth for each 0-7 priority and Admin Group (ISIS) or
+Resource Class/Color (OSPF).
+
+Note that @var{bandwidth} are specified in IEEE floating point format and express in Bytes/second.
+@end deffn
+
+@deffn  link-param {delay <0-16777215> [min <0-16777215> | max <0-16777215>]} {}
+@deffnx  link-param {delay-variation <0-16777215>} {}
+@deffnx  link-param {packet-loss @var{percentage}} {}
+@deffnx  link-param {res-bw @var{bandwidth}} {}
+@deffnx  link-param {ava-bw @var{bandwidth}} {}
+@deffnx  link-param {use-bw @var{bandwidth}} {}
+These command specifies additionnal Traffic Engineering parameters of the interface in conformity to
+draft-ietf-ospf-te-metrics-extension-05.txt and draft-ietf-isis-te-metrics-extension-03.txt. There are
+respectively the delay, jitter, loss, available bandwidth, reservable bandwidth and utilized bandwidth.
+
+Note that @var{bandwidth} are specified in IEEE floating point format and express in Bytes/second.
+Delays and delay variation are express in micro-second (µs). Loss is specified in @var{percentage} ranging
+from 0 to 50.331642% by step of 0.000003.
+@end deffn
+
+@deffn link-param {neighbor <A.B.C.D> as <0-65535>} {}
+@deffnx link-param {no neighbor} {}
+Specifies the remote ASBR IP address and Autonomous System (AS) number for InterASv2 link in OSPF (RFC5392).
+Note that this option is not yet supported for ISIS (RFC5316).
+@end deffn
+
+
+@node Static Route Commands
+@section Static Route Commands
+
+Static routing is a very fundamental feature of routing technology.  It
+defines static prefix and gateway.
+
+@deffn Command {ip route @var{network} @var{gateway}} {}
+@var{network} is destination prefix with format of A.B.C.D/M.
+@var{gateway} is gateway for the prefix.  When @var{gateway} is
+A.B.C.D format.  It is taken as a IPv4 address gateway.  Otherwise it
+is treated as an interface name. If the interface name is @var{null0} then
+zebra installs a blackhole route.
+
+@example
+ip route 10.0.0.0/8 10.0.0.2
+ip route 10.0.0.0/8 ppp0
+ip route 10.0.0.0/8 null0
+@end example
+
+First example defines 10.0.0.0/8 static route with gateway 10.0.0.2.
+Second one defines the same prefix but with gateway to interface ppp0. The
+third install a blackhole route.
+@end deffn
+
+@deffn Command {ip route @var{network} @var{netmask} @var{gateway}} {}
+This is alternate version of above command.  When @var{network} is
+A.B.C.D format, user must define @var{netmask} value with A.B.C.D
+format.  @var{gateway} is same option as above command
+
+@example
+ip route 10.0.0.0 255.0.0.0 10.0.0.2
+ip route 10.0.0.0 255.0.0.0 ppp0
+ip route 10.0.0.0 255.0.0.0 null0
+@end example
+
+These statements are equivalent to those in the previous example.
+@end deffn
+
+@deffn Command {ip route @var{network} @var{gateway} @var{distance}} {}
+Installs the route with the specified distance.
+@end deffn
+
+Multiple nexthop static route
+
+@example
+ip route 10.0.0.1/32 10.0.0.2
+ip route 10.0.0.1/32 10.0.0.3
+ip route 10.0.0.1/32 eth0
+@end example
+
+If there is no route to 10.0.0.2 and 10.0.0.3, and interface eth0
+is reachable, then the last route is installed into the kernel.
+
+If zebra has been compiled with multipath support, and both 10.0.0.2 and
+10.0.0.3 are reachable, zebra will install a multipath route via both
+nexthops, if the platform supports this.
+
+@example
+zebra> show ip route
+S>  10.0.0.1/32 [1/0] via 10.0.0.2 inactive
+                      via 10.0.0.3 inactive
+  *                   is directly connected, eth0
+@end example
+
+@example
+ip route 10.0.0.0/8 10.0.0.2
+ip route 10.0.0.0/8 10.0.0.3
+ip route 10.0.0.0/8 null0 255
+@end example
+
+This will install a multihop route via the specified next-hops if they are
+reachable, as well as a high-metric blackhole route, which can be useful to
+prevent traffic destined for a prefix to match less-specific routes (eg
+default) should the specified gateways not be reachable. Eg:
+
+@example
+zebra> show ip route 10.0.0.0/8
+Routing entry for 10.0.0.0/8
+  Known via "static", distance 1, metric 0
+    10.0.0.2 inactive
+    10.0.0.3 inactive
+
+Routing entry for 10.0.0.0/8
+  Known via "static", distance 255, metric 0
+    directly connected, Null0
+@end example
+
+@deffn Command {ipv6 route @var{network} @var{gateway}} {}
+@deffnx Command {ipv6 route @var{network} @var{gateway} @var{distance}} {}
+These behave similarly to their ipv4 counterparts.
+@end deffn
+
+
+@deffn Command {table @var{tableno}} {}
+Select the primary kernel routing table to be used.  This only works
+for kernels supporting multiple routing tables (like GNU/Linux 2.2.x
+and later).  After setting @var{tableno} with this command,
+static routes defined after this are added to the specified table.
+@end deffn
+
+@node Multicast RIB Commands
+@section Multicast RIB Commands
+
+The Multicast RIB provides a separate table of unicast destinations which
+is used for Multicast Reverse Path Forwarding decisions.  It is used with
+a multicast source's IP address, hence contains not multicast group
+addresses but unicast addresses.
+
+This table is fully separate from the default unicast table.  However,
+RPF lookup can include the unicast table.
+
+WARNING: RPF lookup results are non-responsive in this version of Quagga,
+i.e. multicast routing does not actively react to changes in underlying
+unicast topology!
+
+@deffn Command {ip multicast rpf-lookup-mode @var{mode}} {}
+@deffnx Command {no ip multicast rpf-lookup-mode [@var{mode}]} {}
+
+@var{mode} sets the method used to perform RPF lookups.  Supported modes:
+
+@table @samp
+@item urib-only
+Performs the lookup on the Unicast RIB.  The Multicast RIB is never used.
+@item mrib-only
+Performs the lookup on the Multicast RIB.  The Unicast RIB is never used.
+@item mrib-then-urib
+Tries to perform the lookup on the Multicast RIB.  If any route is found,
+that route is used.  Otherwise, the Unicast RIB is tried.
+@item lower-distance
+Performs a lookup on the Multicast RIB and Unicast RIB each.  The result
+with the lower administrative distance is used;  if they're equal, the
+Multicast RIB takes precedence.
+@item longer-prefix
+Performs a lookup on the Multicast RIB and Unicast RIB each.  The result
+with the longer prefix length is used;  if they're equal, the
+Multicast RIB takes precedence.
+@end table
+
+The @code{mrib-then-urib} setting is the default behavior if nothing is
+configured.  If this is the desired behavior, it should be explicitly
+configured to make the configuration immune against possible changes in
+what the default behavior is.
+
+WARNING: Unreachable routes do not receive special treatment and do not
+cause fallback to a second lookup.
+@end deffn
+
+@deffn Command {show ip rpf @var{addr}} {}
+
+Performs a Multicast RPF lookup, as configured with
+@command{ip multicast rpf-lookup-mode @var{mode}}.  @var{addr} specifies
+the multicast source address to look up.
+
+@example
+> show ip rpf 192.0.2.1
+Routing entry for 192.0.2.0/24 using Unicast RIB
+  Known via "kernel", distance 0, metric 0, best
+  * 198.51.100.1, via eth0
+@end example
+
+Indicates that a multicast source lookup for 192.0.2.1 would use an
+Unicast RIB entry for 192.0.2.0/24 with a gateway of 198.51.100.1.
+@end deffn
+
+@deffn Command {show ip rpf} {}
+
+Prints the entire Multicast RIB.  Note that this is independent of the
+configured RPF lookup mode, the Multicast RIB may be printed yet not
+used at all.
+@end deffn
+
+@deffn Command {ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {}
+@deffnx Command {no ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {}
+
+Adds a static route entry to the Multicast RIB.  This performs exactly as
+the @command{ip route} command, except that it inserts the route in the
+Multicast RIB instead of the Unicast RIB.
+@end deffn
+
+
+@node zebra Route Filtering
+@section zebra Route Filtering
+Zebra supports @command{prefix-list} and @command{route-map} to match
+routes received from other quagga components.  The
+@command{permit}/@command{deny} facilities provided by these commands
+can be used to filter which routes zebra will install in the kernel.
+
+@deffn Command {ip protocol @var{protocol} route-map @var{routemap}} {}
+Apply a route-map filter to routes for the specified protocol. @var{protocol}
+can be @b{any} or one of
+@b{system},
+@b{kernel},
+@b{connected},
+@b{static},
+@b{rip},
+@b{ripng},
+@b{ospf},
+@b{ospf6},
+@b{isis},
+@b{bgp},
+@b{hsls}.
+@end deffn
+
+@deffn {Route Map} {set src @var{address}}
+Within a route-map, set the preferred source address for matching routes
+when installing in the kernel.
+@end deffn
+
+The following creates a prefix-list that matches all addresses, a route-map
+that sets the preferred source address, and applies the route-map to all
+@command{rip} routes.
+
+@example
+@group
+ip prefix-list ANY permit 0.0.0.0/0 le 32
+route-map RM1 permit 10
+     match ip address prefix-list ANY
+     set src 10.0.0.1
+
+ip protocol rip route-map RM1
+@end group
+@end example
+
+@node zebra FIB push interface
+@section zebra FIB push interface
+
+Zebra supports a 'FIB push' interface that allows an external
+component to learn the forwarding information computed by the Quagga
+routing suite.
+
+In Quagga, the Routing Information Base (RIB) resides inside
+zebra. Routing protocols communicate their best routes to zebra, and
+zebra computes the best route across protocols for each prefix. This
+latter information makes up the Forwarding Information Base
+(FIB). Zebra feeds the FIB to the kernel, which allows the IP stack in
+the kernel to forward packets according to the routes computed by
+Quagga. The kernel FIB is updated in an OS-specific way. For example,
+the @code{netlink} interface is used on Linux, and route sockets are
+used on FreeBSD.
+
+The FIB push interface aims to provide a cross-platform mechanism to
+support scenarios where the router has a forwarding path that is
+distinct from the kernel, commonly a hardware-based fast path. In
+these cases, the FIB needs to be maintained reliably in the fast path
+as well. We refer to the component that programs the forwarding plane
+(directly or indirectly) as the Forwarding Plane Manager or FPM.
+
+The FIB push interface comprises of a TCP connection between zebra and
+the FPM. The connection is initiated by zebra -- that is, the FPM acts
+as the TCP server.
+
+The relevant zebra code kicks in when zebra is configured with the
+@code{--enable-fpm} flag. Zebra periodically attempts to connect to
+the well-known FPM port. Once the connection is up, zebra starts
+sending messages containing routes over the socket to the FPM. Zebra
+sends a complete copy of the forwarding table to the FPM, including
+routes that it may have picked up from the kernel. The existing
+interaction of zebra with the kernel remains unchanged -- that is, the
+kernel continues to receive FIB updates as before.
+
+The encapsulation header for the messages exchanged with the FPM is
+defined by the file @file{fpm/fpm.h} in the quagga tree. The routes
+themselves are encoded in netlink or protobuf format, with netlink
+being the default.
+
+Protobuf is one of a number of new serialization formats wherein the
+message schema is expressed in a purpose-built language. Code for
+encoding/decoding to/from the wire format is generated from the
+schema. Protobuf messages can be extended easily while maintaining
+backward-compatibility with older code. Protobuf has the following
+advantages over netlink:
+
+@itemize
+@item
+Code for serialization/deserialization is generated
+automatically. This reduces the likelihood of bugs, allows third-party
+programs to be integrated quickly, and makes it easy to add fields.
+@item
+The message format is not tied to an OS (Linux), and can be evolved
+independently.
+@end itemize
+
+As mentioned before, zebra encodes routes sent to the FPM in netlink
+format by default. The format can be controlled via the
+@code{--fpm_format} command-line option to zebra, which currently
+takes the values @code{netlink} and @code{protobuf}.
+
+The zebra FPM interface uses replace semantics. That is, if a 'route
+add' message for a prefix is followed by another 'route add' message,
+the information in the second message is complete by itself, and
+replaces the information sent in the first message.
+
+If the connection to the FPM goes down for some reason, zebra sends
+the FPM a complete copy of the forwarding table(s) when it reconnects.
+
+@node zebra Terminal Mode Commands
+@section zebra Terminal Mode Commands
+
+@deffn Command {show ip route} {}
+Display current routes which zebra holds in its database.
+
+@example
+@group
+Router# show ip route
+Codes: K - kernel route, C - connected, S - static, R - RIP,
+       B - BGP * - FIB route.
+
+K* 0.0.0.0/0              203.181.89.241
+S  0.0.0.0/0              203.181.89.1
+C* 127.0.0.0/8            lo
+C* 203.181.89.240/28      eth0
+@end group
+@end example
+@end deffn
+
+@deffn Command {show ipv6 route} {}
+@end deffn
+
+@deffn Command {show interface} {}
+@end deffn
+
+@deffn Command {show ip prefix-list [@var{name}]} {}
+@end deffn
+
+@deffn Command {show route-map [@var{name}]} {}
+@end deffn
+
+@deffn Command {show ip protocol} {}
+@end deffn
+
+@deffn Command {show ipforward} {}
+Display whether the host's IP forwarding function is enabled or not.
+Almost any UNIX kernel can be configured with IP forwarding disabled.
+If so, the box can't work as a router.
+@end deffn
+
+@deffn Command {show ipv6forward} {}
+Display whether the host's IP v6 forwarding is enabled or not.
+@end deffn
+
+@deffn Command {show zebra fpm stats} {}
+Display statistics related to the zebra code that interacts with the
+optional Forwarding Plane Manager (FPM) component.
+@end deffn
+
+@deffn Command {clear zebra fpm stats} {}
+Reset statistics related to the zebra code that interacts with the
+optional Forwarding Plane Manager (FPM) component.
+@end deffn
diff --git a/doc/mpls/.gitignore b/doc/mpls/.gitignore
new file mode 100644 (file)
index 0000000..b0a4a46
--- /dev/null
@@ -0,0 +1,5 @@
+.arch-ids
+.arch-inventory
+*~
+*.loT
+
diff --git a/doc/mpls/ChangeLog.opaque.txt b/doc/mpls/ChangeLog.opaque.txt
new file mode 100644 (file)
index 0000000..afcfaa3
--- /dev/null
@@ -0,0 +1,192 @@
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2013.07.01
+
+1. Feature enhancements
+
+  1.1 Update ospf_te.[c,h] in conformance to RFC3630 and clean the code.
+      Add new directive to enable MPLS-TE per interface instead of globally
+
+  1.2 Add support for RFC4970 "Router Information" and RFC5088 "PCE
+      Capabilities announcement".
+
+  1.3 Incorporate the mpls documentation into the main stream doc.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.12.03
+
+1. Bug fixes
+
+  1.1 Though a new member "oi" has added to "struct ospf_lsa" to control
+      flooding scope of type-9 Opaque-LSAs, the value was always NULL
+      because no one set it.
+
+  1.2 In the function "show_ip_ospf_database_summary()" and "show_lsa_
+      detail_adv_router()", VTY output for type-11 Opaque-LSAs did not
+      work properly.
+
+  1.3 URL for the opaque-type assignment reference has changed.
+
+  1.4 In the file "ospf_mpls_te.c", printf formats have changed to
+      avoid compiler warning messages; "%lu" -> "%u", "%lx" -> "%x".
+      Note that this hack depends on OS, compiler and their versions. 
+
+  1.5 One of attached documentation "opaque_lsa.txt" has changed to
+      reflect the latest coding.
+
+2. Feature enhancements
+
+  2.1 Knowing that it is an ugly hack, an "officially unallocated"
+      opaque-type value 0 has newly introduced as a "wildcard",
+      which matches to all opaque-type.
+      This value must not be flooded to the network, of course.
+
+  2.2 The Opaque-core module makes use of newly introduced hooks to
+      dispatch every LSDB change (LSA installation and deletion) to
+      preregistered opaque users.
+      Therefore, by providing appropriate callback functions as new
+      parameters of "ospf_register_opaque_functab()", an opaque user
+      can refer to every LSA instance to be installed into, or to be
+      deleted from, the LSDB.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.10.31
+
+1. Bug fixes
+
+  1.1 Since each LSA has their own lifetime, they will remain in a
+      routing domain (being stored in LSDB of each router), until their
+      age naturally reach to MaxAge or explicitly being flushed by the
+      originated router. Therefore, if a router restarted with a short
+      downtime, it is possible that previously flooded self-originated
+      LSAs might received if the NSM status is not less than Exchange.
+
+      There were some problems in the way of handling self-originated
+      Opaque-LSAs if they are contained in a received LSUpd message,
+      but not installed to the local LSDB yet.
+      Regardless of some conditions to start originating Opaque-LSAs
+      (there should be at least one opaque-capable full-state neighbor),
+      the function "ospf_flood()" will be called to flood and install
+      this brand-new looking LSA.
+      As the result, when the NSM of an opaque-capable neighbor gets
+      full, internal state inconsistency happens; a user of Opaque-LSA
+      such as MPLS-TE can refer to self-originated LSAs in the local
+      LSDB, but cannot modify their contents...
+
+      Above problems have fixed with a policy "flush it from the whole
+      routing domain and keep silent until the flushing completed".
+      By using this sweeping technique, we can be free from confusion
+      caused by self-originated LSAs received via network. 
+
+  1.2 The function "ospf_opaque_type_name()" contained massive ifdefs
+      corresponding to each "opaque-type".
+      These unnecessary ifdefs are removed completely.
+
+  1.3 In the function "ospf_delete_opaque_functab()", there was an
+      improper loop control that causes illegal memory access.
+      Original coding was "next = nextnode (node)".
+
+  1.4 The function "ospf_mpls_te_ism_change()" could not handle the
+      case when the ISM changes from Waiting to DR/BDR/Other.
+      So, there was a case that even if one of an ISM become
+      operational and MPLS-TE module has started, the corresponding
+      Opaque-LSA cannot be originated.
+
+  1.5 The function "ospf_opaque_lsa_reoriginate_schedule()" did not
+      allow to be called multiple times, simply because handling
+      module for the given "lsa-type & opaque-type" already exists.
+      But this assumption seems to be wrong.
+      Change the policy to allow this function to be called multiple
+      times and let the caller to decide what should do when the
+      corresponding callback function "(* functab->lsa_originator)()"
+      is called.
+
+2. Feature enhancements
+
+  2.1 The global bitmap "opaque" has introduced instead of former flag
+      "OpaqueCapable", to store complex conditions to handle Opaque-LSAs.
+
+  2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic
+      -06.txt", no significant changes with 05 version, though.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.08.03
+
+1. Bug fixes
+
+  1.1 Even if the ospfd started with opaque capability enabled, when
+      the ospfd receives an unknown opaque-type (unregistered by the
+      function "ospf_register_opaque_functab()" beforehand), the LSA
+      was discarded. As the result, only the opaque-LSAs that have
+      commonly registered by opaque-capable ospf routers can be
+      flooded in a routing domain.
+
+      This behavior has fixed so that arbitrary opaque-type LSAs can
+      be flooded among opaque-capable ospf routers.
+      If the ospfd has opaque-LSA capability but disabled at runtime,
+      received opaque-LSAs can be accepted and registered to LSDB as
+      is, but not be flooded to the network; those opaque LSAs will
+      remain in LSDB until explicitly flushed by incoming LSUpd
+      messages with MaxAge, or their age naturally reaches to MaxAge.
+
+  1.2 The function "ospf_register_opaque_functab()" did not check
+      if the entry corresponding to the given "lsa-type, opaque-type"
+      combination already exists or not.
+      This problem has fixed not to allow multiple registration.
+
+  1.3 Since type-11 (AS external) LSAs will be flooded beyond areas,
+      there is little relationship between "struct lsa" and "struct
+      area". More specifically, the pointer address "lsa->area" can
+      be NULL if the lsa-type is 11, thus an illegal memory access
+      will happen. This problem has fixed.
+
+  1.4 When self-originated opaque-LSAs are received via network and
+      if the corresponding opaque-type functions are not available
+      (they have already deleted) at that time, those LSAs were
+      dropped due to "unknown opaque-type" error.
+      After the problem 1.1 has fixed, those "self-originated" LSAs
+      were registered to LSDB and then flooded to the network, even
+      if the processing functions did not exist...
+
+      After all, this problem has fixed so that those LSAs should
+      explicitly be flushed from the routing domain immediately, if
+      the processing functions cannot find at that time.
+
+  1.5 Some typo have fixed.
+
+      --- EXAMPLE ---
+      static int
+      opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent)
+                                                          ^^^^^
+      --- EXAMPLE ---
+
+2. Feature enhancements
+
+  2.1 According to the description of rfc2328 in section 10.8, any
+      change in the router's optional capabilities should trigger
+      the option re-negotiation procedures with neighbors.
+
+      --- EXCERPT ---
+                              If for some reason the router's optional
+        capabilities change, the Database Exchange procedure should be
+        restarted by reverting to neighbor state ExStart.
+      --- EXCERPT ---
+
+      For the opaque-capability changes, this feature has implemented.
+      More specifically, if "ospf opaque-lsa" or "no ospf opaque-lsa"
+      VTY command is given at runtime, all self-originated LSAs will
+      be flushed immediately and then all neighbor status will be
+      forced to ExStart by generating SeqNumberMismatch events.
+
+  2.1 When we change opaque-capability dynamically (ON -> OFF -> ON),
+      there was no trigger at "OFF->ON" timing to reactivate opaque
+      LSA handling modules (such as MPLS-TE) that have once forcibly
+      stopped at "ON->OFF" timing.
+      Now this dynamic reactivation feature has added.
+
+  2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic
+      -05.txt", no significant changes with 04 version, though.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.03.28
+
+  Initial release of Opaque-LSA/MPLS-TE extensions for the zebra/ospfd.
diff --git a/doc/mpls/cli_summary.txt b/doc/mpls/cli_summary.txt
new file mode 100644 (file)
index 0000000..c60d0ae
--- /dev/null
@@ -0,0 +1,90 @@
+Summary of CLI commands, expanded for Opaque-LSA/MPLS-TE.
+---------------------------------------------------------
+
+router>
+
+  show ip ospf database (asbr-summary|external|max-age|network|router|self-originate|summary|opaque-link|opaque-area|opaque-external)
+
+  show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) (self-originate|)
+
+  show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D
+
+  show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D (self-originate|)
+
+  show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D adv-router A.B.C.D
+
+  show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) adv-router A.B.C.D
+
+  --> Add database items: opaque-link, opaque-area, opaque-external
+
+  show mpls-te interface [INTERFACE]
+
+  --> Show current MPLS-TE link-TLV parameters.
+      If [INTERFACE] is omitted, all interfaces will be displayed.
+
+  show mpls-te router
+
+  --> Show current MPLS-TE Router-TLV parameters.
+
+router> enable
+router#
+router# configure terminal
+router(config)# interface [INTERFACE]
+router(config-if)#
+
+  mpls-te link max-bw BANDWIDTH
+
+  --> Set MPLS-TE link-TLV parameter: Maximum Bandwidth (Bytes/sec).
+      In integer or floating point format (1000, or 1.0e3)
+
+  mpls-te link max-rsv-bw BANDWIDTH
+
+  --> Set MPLS-TE link-TLV parameter: Maximum Reservable Bandwidth (Bytes/sec).
+      In integer or floating point format (1000, or 1.0e3)
+
+  mpls-te link metric <0-4294967295>
+
+  --> Set MPLS-TE link-TLV parameter: MPLS-TE metric.
+
+  mpls-te link rsc-clsclr BITPATTERN
+
+  --> Set MPLS-TE link-TLV parameter: Resource Class/Color.
+      In 32-bit hexadecimal format, with leading "0x" (0x0 - 0xffffffff)
+
+  mpls-te link unrsv-bw <0-7> BANDWIDTH
+
+  --> Set MPLS-TE link-TLV parameter: Unreserved Bandwidth (Bytes/sec).
+      In integer or floating point format (1000, or 1.0e3)
+
+router(config-if)# exit
+router(config)# router ospf
+router(config-router)#
+
+  mpls-te
+
+  --> Enable MPLS-TE functionality.
+      Note that master-switch "ospf opaque-lsa" must also be specified.
+
+  mpls-te on
+
+  --> Alias of "mpls-te" command.
+
+  mpls-te router-address A.B.C.D
+
+  --> Set MPLS-TE Router-TLV parameter: Router Address.
+
+  no mpls-te
+
+  --> Disable MPLS-TE functionality.
+
+  no ospf opaque-lsa
+
+  --> Disable Opaque-LSAs capability.
+      This node behaves Opaque-incapable node.
+
+  ospf opaque-lsa
+
+  --> Enable Opaque-LSAs capability.
+      This is the master-switch to make this node Opaque-capable.
+
+router# exit
diff --git a/doc/mpls/opaque_lsa.txt b/doc/mpls/opaque_lsa.txt
new file mode 100644 (file)
index 0000000..ae89ee3
--- /dev/null
@@ -0,0 +1,365 @@
+1. List of "opaque-type dependent" callback functions per LSA-type.
+
+ <ospf_opaque_lsaN_functab>  (N = 9,10,11)
+      |
+      |    struct
+      |    list            struct                        struct
+      +-> +-------+        listnode                      listnode
+          | head  |-----> +------+                      +------
+          | tail  |       | next |--------------------> | next 
+          | count |    /--| prev |<---------------------| prev
+          +-------+       | data |----+                 |
+          |///////|       +------+    |
+          +-------+                   |
+                                      |
+           struct                     |
+           ospf_opaque_tabent         |
+          +----------------------+ <--+
+          | opaque_type          |
+          +----------------------+
+          | (Callback functions) |
+          +----------------------+
+
+
+2. Self-originated Opaque-LSAs per LSA-type.
+
+2.1 Type-11 (AS-external) Opaque-LSAs
+
+               struct
+               ospf
+        +---> +-------------------+
+        |     |///////////////////|
+        |     +-------------------+
+        |     | opaque            |
+        |     +-------------------+
+        |     |///////////////////|
+        |     +-------------------+
+        |     | opaque_lsa_self   |---+
+        |     +-------------------+   |
+        |     |///////////////////|   |
+        |     +-------------------+   |
+        |                             |
+  ......|.............................|.......................................
+  :     |                             |   Almost common for type-9,10,11 LSA :
+  :     |     +-----------------------+                                      :
+  :     |     |                                                              :
+  :     |     |    struct                                                    :
+  :     |     |    list            struct                        struct      :
+  :     |     +-> +-------+        listnode                      listnode    :
+  :     |         | head  |-----> +------+                      +------      :
+  :     |         | tail  |       | next |--------------------> | next       :
+  :     |         | count |    /--| prev |<---------------------| prev       :
+  :     |         +-------+       | data |---+                  |            :
+  :     |         |///////|       +------+   |                               :
+  :     |         +-------+                  |                               :
+  :     |                                    |                               :
+  :     |      struct                        |                               :
+  :     |      opaque_info_per_type          |                               :
+  :     |     +-------------------+ <--------+                               :
+  :     |     | opaque_type       | <------------+                           :
+  :     |     +-------------------+              |                           :
+  :     |     | status            |              |                           :
+  :     |     +-------------------+              |                           :
+  :     |     | t_opaque_lsa_self |              |                           :
+  :     |     +-------------------+              |                           :
+  :     +-----| owner             |              |       struct              :
+  :           +-------------------+              |       ospf_opaque_tabent  :
+  :           | functab           |-------------------> +----------------    :
+  :           +-------------------+              |      | opaque_type        :
+  :           | id_list           |---+          |      |(Callback Funcs)    :
+  :           +-------------------+   |          |      |                    :
+  :                                   |          |                           :
+  :           +-----------------------+          |                           :
+  :           |                                  |                           :
+  :           |    struct                        |                           :
+  :           |    list            struct        |               struct      :
+  :           +-> +-------+        listnode      |               listnode    :
+  :               | head  |-----> +------+       |              +------      :
+  :               | tail  |       | next |--------------------> | next       :
+  :               | count |    /--| prev |<---------------------| prev       :
+  :               +-------+       | data |---+   |              |            :
+  :               |///////|       +------+   |   |                           :
+  :               +-------+                  |   |                           :
+  :                                          |   |                           :
+  :            struct                        |   |                           :
+  :            opaque_info_per_id            |   |                           :
+  :           +-------------------+ <--------+   |                           :
+  :           | opaque_id         |              |                           :
+  :           +-------------------+              |                           :
+  :           | t_opaque_lsa_self |              |                           :
+  :           +-------------------+              |                           :
+  :           | opqctl_type       |--------------+                           :
+  :           +-------------------+                                          :
+  :           | lsa               |---+                                      :
+  :           +-------------------+   |                                      :
+  :                                   |                                      :
+  :            struct                 |                                      :
+  :            ospf_lsa               |                                      :
+  :           +-------------+ <-------+                                      :
+  :           |/////////////|                 struct                         :
+  :           +-------------+                 lsa_header                     :
+  :           | data        |--------------> +--------                       :
+  :           +-------------+                |                               :
+  :           |/////////////|                                                :
+  :           +-------------+                                                :
+  :  +--------| area        |                                                :
+  :  |        +-------------+                                                :
+  : ---       |/////////////|                                                :
+  :           +-------------+                                                :
+  :     +-----| oi          |                                                :
+  :     |     +-------------+                                                :
+  :    ---                                                                   :
+  :..........................................................................:
+
+2.2 Type-10 (area-local) Opaque-LSAs
+
+               struct
+               ospf
+              +---------+ <-----------+
+              |/////////|             |
+              +---------+             |
+                                      |
+               struct                 |
+               ospf_area              |
+     +--+---> +-----------------+     |
+     |  |     | top             |-----+
+     |  |     +-----------------+
+     |  |     |/////////////////|              struct
+     |  |     +-----------------+              ospf_lsa
+     |  |     | router_lsa_self |-----------> +---------
+     |  |     +-----------------+             |
+     |  |     | opaque_lsa_self |-----+       |
+     |  |     +-----------------+     |
+     |  |     |/////////////////|     |
+     |  |     +-----------------+     |
+     |  |                             |
+  ...|..|.............................|.......................................
+  :  |  |                             |   Almost common for type-9,10,11 LSA :
+  :  |  |     +-----------------------+                                      :
+  :  |  |     |                                                              :
+  :  |  |     |    struct                                                    :
+  :  |  |     |    list            struct                        struct      :
+  :  |  |     +-> +-------+        listnode                      listnode    :
+  :  |  |         | head  |-----> +------+                      +------      :
+  :  |  |         | tail  |       | next |--------------------> | next       :
+  :  |  |         | count |    /--| prev |<---------------------| prev       :
+  :  |  |         +-------+       | data |---+                  |            :
+  :  |  |         |///////|       +------+   |                               :
+  :  |  |         +-------+                  |                               :
+  :  |  |                                    |                               :
+  :  |  |      struct                        |                               :
+  :  |  |      opaque_info_per_type          |                               :
+  :  |  |     +-------------------+ <--------+                               :
+  :  |  |     | opaque_type       | <------------+                           :
+  :  |  |     +-------------------+              |                           :
+  :  |  |     | status            |              |                           :
+  :  |  |     +-------------------+              |                           :
+  :  |  |     | t_opaque_lsa_self |              |                           :
+  :  |  |     +-------------------+              |                           :
+  :  |  +-----| owner             |              |       struct              :
+  :  |        +-------------------+              |       ospf_opaque_tabent  :
+  :  |        | functab           |-------------------> +----------------    :
+  :  |        +-------------------+              |      | opaque_type        :
+  :  |        | id_list           |---+          |      |(Callback Funcs)    :
+  :  |        +-------------------+   |          |      |                    :
+  :  |                                |          |                           :
+  :  |        +-----------------------+          |                           :
+  :  |        |                                  |                           :
+  :  |        |    struct                        |                           :
+  :  |        |    list            struct        |               struct      :
+  :  |        +-> +-------+        listnode      |               listnode    :
+  :  |            | head  |-----> +------+       |              +------      :
+  :  |            | tail  |       | next |--------------------> | next       :
+  :  |            | count |    /--| prev |<---------------------| prev       :
+  :  |            +-------+       | data |---+   |              |            :
+  :  |            |///////|       +------+   |   |                           :
+  :  |            +-------+                  |   |                           :
+  :  |                                       |   |                           :
+  :  |         struct                        |   |                           :
+  :  |         opaque_info_per_id            |   |                           :
+  :  |        +-------------------+ <--------+   |                           :
+  :  |        | opaque_id         |              |                           :
+  :  |        +-------------------+              |                           :
+  :  |        | t_opaque_lsa_self |              |                           :
+  :  |        +-------------------+              |                           :
+  :  |        | opqctl_type       |--------------+                           :
+  :  |        +-------------------+                                          :
+  :  |        | lsa               |---+                                      :
+  :  |        +-------------------+   |                                      :
+  :  |                                |                                      :
+  :  |         struct                 |                                      :
+  :  |         ospf_lsa               |                                      :
+  :  |        +-------------+ <-------+                                      :
+  :  |        |/////////////|                 struct                         :
+  :  |        +-------------+                 lsa_header                     :
+  :  |        | data        |--------------> +--------                       :
+  :  |        +-------------+                |                               :
+  :  |        |/////////////|                                                :
+  :  |        +-------------+                                                :
+  :  +--------| area        |                                                :
+  :           +-------------+                                                :
+  :           |/////////////|                                                :
+  :           +-------------+                                                :
+  :     +-----| oi          |                                                :
+  :     |     +-------------+                                                :
+  :    ---                                                                   :
+  :..........................................................................:
+
+2.3 Type-9 (link-local) Opaque-LSAs
+
+               struct
+               ospf_area
+     +------> +---------+ <---------+
+     |        |/////////|           |
+     |        +---------+           |
+     |                              |
+     |         struct               |
+     |         ospf_interface       |
+     |  +-+-> +-----------------+   |
+     |  | |   |/////////////////|   |
+     |  | |   +-----------------+   |
+     |  | |   | area            |---+
+     |  | |   +-----------------+
+     |  | |   |/////////////////|              struct
+     |  | |   +-----------------+              ospf_lsa
+     |  | |   |network_lsa_self |-----------> +---------
+     |  | |   +-----------------+             |
+     |  | |   | opaque_lsa_self |-----+       |
+     |  | |   +-----------------+     |
+     |  | |   |/////////////////|     |
+     |  | |   +-----------------+     |
+     |  | |                           |
+  ...|..|.|...........................|.......................................
+  :  |  | |                           |   Almost common for type-9,10,11 LSA :
+  :  |  | |   +-----------------------+                                      :
+  :  |  | |   |                                                              :
+  :  |  | |   |    struct                                                    :
+  :  |  | |   |    list            struct                        struct      :
+  :  |  | |   +-> +-------+        listnode                      listnode    :
+  :  |  | |       | head  |-----> +------+                      +------      :
+  :  |  | |       | tail  |       | next |--------------------> | next       :
+  :  |  | |       | count |    /--| prev |<---------------------| prev       :
+  :  |  | |       +-------+       | data |---+                  |            :
+  :  |  | |       |///////|       +------+   |                               :
+  :  |  | |       +-------+                  |                               :
+  :  |  | |                                  |                               :
+  :  |  | |    struct                        |                               :
+  :  |  | |    opaque_info_per_type          |                               :
+  :  |  | |   +-------------------+ <--------+                               :
+  :  |  | |   | opaque_type       | <------------+                           :
+  :  |  | |   +-------------------+              |                           :
+  :  |  | |   | status            |              |                           :
+  :  |  | |   +-------------------+              |                           :
+  :  |  | |   | t_opaque_lsa_self |              |                           :
+  :  |  | |   +-------------------+              |                           :
+  :  |  | +---| owner             |              |       struct              :
+  :  |  |     +-------------------+              |       ospf_opaque_tabent  :
+  :  |  |     | functab           |-------------------> +----------------    :
+  :  |  |     +-------------------+              |      | opaque_type        :
+  :  |  |     | id_list           |---+          |      |(Callback Funcs)    :
+  :  |  |     +-------------------+   |          |      |                    :
+  :  |  |                             |          |                           :
+  :  |  |     +-----------------------+          |                           :
+  :  |  |     |                                  |                           :
+  :  |  |     |    struct                        |                           :
+  :  |  |     |    list            struct        |               struct      :
+  :  |  |     +-> +-------+        listnode      |               listnode    :
+  :  |  |         | head  |-----> +------+       |              +------      :
+  :  |  |         | tail  |       | next |--------------------> | next       :
+  :  |  |         | count |    /--| prev |<---------------------| prev       :
+  :  |  |         +-------+       | data |---+   |              |            :
+  :  |  |         |///////|       +------+   |   |                           :
+  :  |  |         +-------+                  |   |                           :
+  :  |  |                                    |   |                           :
+  :  |  |      struct                        |   |                           :
+  :  |  |      opaque_info_per_id            |   |                           :
+  :  |  |     +-------------------+ <--------+   |                           :
+  :  |  |     | opaque_id         |              |                           :
+  :  |  |     +-------------------+              |                           :
+  :  |  |     | t_opaque_lsa_self |              |                           :
+  :  |  |     +-------------------+              |                           :
+  :  |  |     | opqctl_type       |--------------+                           :
+  :  |  |     +-------------------+                                          :
+  :  |  |     | lsa               |---+                                      :
+  :  |  |     +-------------------+   |                                      :
+  :  |  |                             |                                      :
+  :  |  |      struct                 |                                      :
+  :  |  |      ospf_lsa               |                                      :
+  :  |  |     +-------------+ <-------+                                      :
+  :  |  |     |/////////////|                 struct                         :
+  :  |  |     +-------------+                 lsa_header                     :
+  :  |  |     | data        |--------------> +--------                       :
+  :  |  |     +-------------+                |                               :
+  :  |  |     |/////////////|                                                :
+  :  |  |     +-------------+                                                :
+  :  +--|-----| area        |                                                :
+  :     |     +-------------+                                                :
+  :     |     |/////////////|                                                :
+  :     |     +-------------+                                                :
+  :     +-----| oi          |                                                :
+  :           +-------------+                                                :
+  :..........................................................................:
+
+
+3. Internal structures for MPLS-TE parameter management.
+
+       struct                         
+       ospf_mpls_te
+      +-------------+
+      | status      |
+      +-------------+
+      | iflist      |---+
+      +-------------+   |
+      |(Router-TLV) |   |
+      +-------------+   |
+                        |
+  +---------------------+
+  |  
+  |      struct                     
+  |      list            struct                        struct
+  +---> +-------+        listnode                      listnode
+        | head  |-----> +------+                      +------
+        | tail  |       | next |--------------------> | next 
+        | count |    /--| prev |<---------------------| prev
+        +-------+       | data |---+                  |
+        |///////|       +------+   |
+        +-------+                  |
+                                   |
+  +--------------------------------+
+  |
+  |    struct
+  |    ospf_mpls_te_linkparms
+  +-> +----------------+
+      | instance       |                       struct
+      +----------------+                       interface
+      | ifp            |--------------------> +----------+
+      +----------------+               +----> |//////////|
+      | area           |----+          |      +----------+
+      +----------------+    |          |      | info     |-----+
+      | flags          |    |          |      +----------+     |
+      +----------------+    |          |      |//////////|     |
+      | (Link-TLV)     |    |          |      +----------+     |
+      +----------------+    |          |                       |
+      | (Link-SubTLVs) |    |          |       struct          |
+      +----------------+    |          |       ospf_if_info    |
+                            |          |      +----------+ <---+
+                            |          |      |//////////|
+       struct               |          |      +----------+
+       ospf_area            |          |      | oifs     |-----+
+  +-> +--------------+ <----+          |      +----------+     |
+  |   |//////////////|                 |                       |
+  |   +--------------+                 |       struct          |
+  |                                    |       route_table     |
+  |    struct                          |      +-----------+ <--+
+  |    ospf_interface                  |      | route_top | - - - - -.
+  |   +--------------+ <----+          |      +-----------+          .
+  |   |//////////////|      |          |                             .
+  |   +--------------+      |          |       struct                .
+  |   | ifp          |------|----------+       route_node            .
+  |   +--------------+      |                 +-----------+ < - - - - 
+  |   |//////////////|      |                 |///////////|
+  |   +--------------+      |                 +-----------+
+  +---| area         |      +-----------------| info      |
+      +--------------+                        +-----------+
+      |//////////////|                        |///////////|
+      +--------------+                        +-----------+
diff --git a/doc/mpls/ospfd.conf b/doc/mpls/ospfd.conf
new file mode 100644 (file)
index 0000000..2b15fa4
--- /dev/null
@@ -0,0 +1,76 @@
+!
+! Zebra configuration saved from vty
+!   2001/03/16 22:07:53
+!
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/ospfd.log
+!
+debug ospf ism
+debug ospf nsm
+debug ospf lsa
+debug ospf zebra
+debug ospf event
+debug ospf packet all detail
+!
+!
+interface fxp0
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+ mpls-te on
+ mpls-te link metric 999
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xab
+!
+interface de1
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+ mpls-te link metric 111
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xcd
+!
+interface de0
+ mpls-te link metric 0
+ mpls-te link rsc-clsclr 0x0
+!
+interface lp0
+ ip ospf network point-to-point
+!
+interface tun0
+ ip ospf network point-to-point
+!
+interface sl0
+ ip ospf network point-to-point
+!
+interface ppp0
+ ip ospf network point-to-point
+!
+interface lo0
+!
+router ospf
+ compatible rfc1583
+ network 192.168.0.0/16 area 1
+ ospf opaque-lsa
+  mpls-te
+  mpls-te router-address 1.2.3.4
+!
+line vty
+!
diff --git a/doc/next-hop-tracking.txt b/doc/next-hop-tracking.txt
new file mode 100644 (file)
index 0000000..d157866
--- /dev/null
@@ -0,0 +1,326 @@
+0. Introduction
+
+This is the design specification for next hop tracking feature in
+Quagga.
+
+1. Background
+
+Recursive routes are of the form:
+
+   p/m --> n
+  [Ex: 1.1.0.0/16 --> 2.2.2.2]
+
+where 'n' itself is resolved through another route as follows:
+
+   p2/m --> h, interface
+  [Ex: 2.2.2.0/24 --> 3.3.3.3, eth0]
+
+Usually, BGP routes are recursive in nature and BGP nexthops get
+resolved through an IGP route. IGP usually adds its routes pointing to
+an interface (these are called non-recursive routes).
+
+When BGP receives a recursive route from a peer, it needs to validate
+the nexthop. The path is marked valid or invalid based on the
+reachability status of the nexthop.  Nexthop validation is also
+important for BGP decision process as the metric to reach the nexthop
+is a parameter to best path selection process.
+
+As it goes with routing, this is a dynamic process. Route to the
+nexthop can change. The nexthop can become unreachable or
+reachable. In the current BGP implementation, the nexthop validation
+is done periodically in the scanner run. The default scanner run
+interval is one minute. Every minute, the scanner task walks the
+entire BGP table. It checks the validity of each nexthop with Zebra
+(the routing table manager) through a request and response message
+exchange between BGP and Zebra process. BGP process is blocked for
+that duration. The mechanism has two major drawbacks:
+
+(1) The scanner task runs to completion. That can potentially starve
+    the other tasks for long periods of time, based on the BGP table
+    size and number of nexthops.
+
+(2) Convergence around routing changes that affect the nexthops can be
+    long (around a minute with the default intervals). The interval
+    can be shortened to achieve faster reaction time, but it makes the
+    first problem worse, with the scanner task consuming most of the
+    CPU resources.
+
+"Next hop tracking" feature makes this process event-driven. It
+eliminates periodic nexthop validation and introduces an asynchronous
+communication path between BGP and Zebra for route change notifications
+that can then be acted upon.
+
+2. Goal
+
+Stating the obvious, the main goal is to remove the two limitations we
+discussed in the previous section. The goals, in a constructive tone,
+are the following:
+
+- fairness: the scanner run should not consume an unjustly high amount
+  of CPU time. This should give an overall good performance and
+  response time to other events (route changes, session events,
+  IO/user interface).
+
+- convergence: BGP must react to nexthop changes instantly and provide
+  sub-second convergence. This may involve diverting the routes from
+  one nexthop to another.
+
+3. Overview of the changes
+
+The changes are in both BGP and Zebra modules.  The short summary is
+the following:
+
+- Zebra implements a registration mechanism by which clients can
+   register for next hop notification. Consequently, it maintains a
+   separate table, per (VRF, AF) pair, of next hops and interested
+   client-list per next hop.
+
+- When the main routing table changes in Zebra, it evaluates the next
+   hop table: for each next hop, it checks if the route table
+   modifications have changed its state. If so, it notifies the
+   interested clients.
+
+- BGP is one such client. It registers the next hops corresponding to
+   all of its received routes/paths. It also threads the paths against
+   each nexthop structure.
+
+- When BGP receives a next hop notification from Zebra, it walks the
+   corresponding path list. It makes them valid or invalid depending
+   on the next hop notification. It then re-computes best path for the
+   corresponding destination. This may result in re-announcing those
+   destinations to peers.
+
+4. Design
+
+4.1. Modules
+
+The core design introduces an "nht" (next hop tracking) module in BGP
+and "rnh" (recursive nexthop) module in Zebra. The "nht" module
+provides the following APIs:
+
+bgp_find_or_add_nexthop() : find or add a nexthop in BGP nexthop table
+bgp_find_nexthop() : find a nexthop in BGP nexthop table
+bgp_parse_nexthop_update() : parse a nexthop update message coming
+                              from zebra
+
+The "rnh" module provides the following APIs:
+
+zebra_add_rnh() : add a recursive nexthop
+zebra_delete_rnh() : delete a recursive nexthop
+zebra_lookup_rnh() : lookup a recursive nexthop
+
+zebra_add_rnh_client() : register a client for nexthop notifications
+                         against a recursive nexthop
+
+zebra_remove_rnh_client(): remove the client registration for a
+                            recursive nexthop
+
+zebra_evaluate_rnh_table(): (re)evaluate the recursive nexthop table
+                            (most probably because the main routing
+                            table has changed).
+
+zebra_cleanup_rnh_client(): Cleanup a client from the "rnh" module
+                            data structures (most probably because the
+                            client is going away).
+
+4.2. Control flow
+
+The next hop registration control flow is the following:
+
+<====      BGP Process       ====>|<====      Zebra Process      ====>
+                                  |
+receive module     nht module     |  zserv module        rnh module
+----------------------------------------------------------------------
+              |                   |                  |
+bgp_update_   |                   |                  |
+      main()  | bgp_find_or_add_  |                  |
+              |        nexthop()  |                  |
+              |                   |                  |
+              |                   | zserv_nexthop_   |
+              |                   |       register() |
+              |                   |                  | zebra_add_rnh()
+              |                   |                  |
+
+
+The next hop notification control flow is the following:
+
+<====     Zebra Process    ====>|<====      BGP Process       ====>
+                                |
+rib module         rnh module   |     zebra module        nht module
+----------------------------------------------------------------------
+              |                 |                   |
+meta_queue_   |                 |                   |
+    process() | zebra_evaluate_ |                   |
+              |     rnh_table() |                   |
+              |                 |                   |
+              |                 | bgp_read_nexthop_ |
+              |                 |          update() |
+              |                 |                   | bgp_parse_
+              |                 |                   | nexthop_update()
+              |                 |                   |
+
+
+4.3. zclient message format
+
+ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are
+encoded in the following way:
+
+/*
+ *     0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |     AF                        |  prefix len   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .      Nexthop prefix                                           .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .                                                               .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |     AF                        |  prefix len   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .      Nexthop prefix                                           .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ZEBRA_NEXTHOP_UPDATE message is encoded as follows:
+
+/*
+ *     0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |     AF                        |  prefix len   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .      Nexthop prefix getting resolved                          .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |        metric                                                 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  #nexthops    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | nexthop type  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .      resolving Nexthop details                                .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | nexthop type  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .      resolving Nexthop details                                .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+4.4. BGP data structure
+
+Legend:
+
+/\   struct bgp_node: a BGP destination/route/prefix
+\/
+
+[ ]  struct bgp_info: a BGP path (e.g. route received from a peer)
+
+ _
+(_)  struct bgp_nexthop_cache: a BGP nexthop
+
+
+
+   /\         NULL
+   \/--+        ^
+       |        :
+       +--[ ]--[ ]--[ ]--> NULL
+   /\           :
+   \/--+        :
+       |        :
+       +--[ ]--[ ]--> NULL
+                :
+  _             :
+ (_).............
+
+
+4.5. Zebra data structure
+
+rnh table:
+
+           O
+          / \
+         O   O
+            / \
+           O   O
+
+        struct rnh
+        {
+          u_char flags;
+          struct rib *state;
+          struct list *client_list;
+          struct route_node *node;
+        };
+
+5. User interface changes
+
+quagga# show ip nht
+3.3.3.3
+ resolved via kernel
+ via 11.0.0.6, swp1
+ Client list: bgp(fd 12)
+11.0.0.10
+ resolved via connected
+ is directly connected, swp2
+ Client list: bgp(fd 12)
+11.0.0.18
+ resolved via connected
+ is directly connected, swp4
+ Client list: bgp(fd 12)
+11.11.11.11
+ resolved via kernel
+ via 10.0.1.2, eth0
+ Client list: bgp(fd 12)
+
+quagga# show ip bgp nexthop
+Current BGP nexthop cache:
+ 3.3.3.3 valid [IGP metric 0], #paths 3
+  Last update: Wed Oct 16 04:43:49 2013
+
+ 11.0.0.10 valid [IGP metric 1], #paths 1
+  Last update: Wed Oct 16 04:43:51 2013
+
+ 11.0.0.18 valid [IGP metric 1], #paths 2
+  Last update: Wed Oct 16 04:43:47 2013
+
+ 11.11.11.11 valid [IGP metric 0], #paths 1
+  Last update: Wed Oct 16 04:43:47 2013
+
+quagga# show ipv6 nht
+quagga# show ip bgp nexthop detail
+
+quagga# debug bgp nht
+quagga# debug zebra nht
+
+6. Sample test cases
+
+     r2----r3
+    /  \  /
+  r1----r4
+
+- Verify that a change in IGP cost triggers NHT
+  + shutdown the r1-r4 and r2-r4 links
+  + no shut the r1-r4 and r2-r4 links and wait for OSPF to come back
+    up
+  + We should be back to the original nexthop via r4 now
+- Verify that a NH becoming unreachable triggers NHT
+  + Shutdown all links to r4
+- Verify that a NH becoming reachable triggers NHT
+  + no shut all links to r4
+
+7. Future work
+
+- route-policy for next hop validation (e.g. ignore default route)
+- damping for rapid next hop changes
+- prioritized handling of nexthop changes ((un)reachability vs. metric
+  changes)
+- handling recursion loop, e.g.
+   11.11.11.11/32 -> 12.12.12.12
+   12.12.12.12/32 -> 11.11.11.11
+   11.0.0.0/8 -> <interface>
+- better statistics
diff --git a/doc/nhrpd.8 b/doc/nhrpd.8
new file mode 100644 (file)
index 0000000..e227c20
--- /dev/null
@@ -0,0 +1,105 @@
+.TH NHRP 8 "24 January 2017" "Quagga NHRP daemon" "Version 1.1"
+.SH NAME
+nhrpd \- a Next Hop Routing Protocol routing engine for use with Quagga routing software.
+.SH SYNOPSIS
+.B nhrpd
+[
+.B \-dhv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B nhrpd
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B nhrpd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/nhrpd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When nhrpd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart nhrpd.  The likely default is \fB\fI/var/run/nhrpd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR
+Specify the port that the nhrpd VTY will listen on. This defaults to
+2608, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the nhrpd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/nhrpd
+The default location of the
+.B nhrpd
+binary.
+.TP
+.BI /usr/local/etc/nhrpd.conf
+The default location of the
+.B nhrpd
+config file.
+.TP
+.BI $(PWD)/nhrpd.log
+If the
+.B nhrpd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBnhrpd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The nhrpd process may log to standard output, to a VTY, to a log
+file, or through syslog to the system logs. \fBnhrpd\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 vtysh (1)
+
+.B nhrpd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+Timo Teräs <timo.teras@iki.fi>
diff --git a/doc/nhrpd.texi b/doc/nhrpd.texi
new file mode 100644 (file)
index 0000000..71d1ce9
--- /dev/null
@@ -0,0 +1,144 @@
+@cindex NHRP
+@node NHRP
+@chapter NHRP
+
+@command{nhrpd} is a daemon to support Next Hop Routing Protocol (NHRP).
+NHRP is described in RFC2332.
+
+NHRP is used to improve the efficiency of routing computer network
+traffic over Non-Broadcast, Multiple Access (NBMA) Networks. NHRP provides
+an ARP-like solution that allows a system to dynamically learn the NBMA
+address of the other systems that are part of that network, allowing
+these systems to directly communicate without requiring traffic to use
+an intermediate hop.
+
+Cisco Dynamic Multipoint VPN (DMVPN) is based on NHRP, and Quagga nrhpd
+implements this scenario.
+
+@menu
+* Routing Design::
+* Configuring NHRP::
+* Hub Functionality::
+* Integration with IKE::
+* NHRP Events::
+* Configuration Example::
+@end menu
+
+@node Routing Design
+@section Routing Design
+
+nhrpd never handles routing of prefixes itself. You need to run some
+real routing protocol (e.g. BGP) to advertise routes over the tunnels.
+What nhrpd does it establishes 'shortcut routes' that optimizes the
+routing protocol to avoid going through extra nodes in NBMA GRE mesh.
+
+nhrpd does route NHRP domain addresses individually using per-host prefixes.
+This is similar to Cisco FlexVPN; but in contrast to opennhrp which uses
+a generic subnet route.
+
+To create NBMA GRE tunnel you might use the following (linux terminal
+commands):
+@example
+@group
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.255.255.2/32 dev gre1
+ ip link set gre1 up
+@end group
+@end example
+
+Note that the IP-address is assigned as host prefix to gre1. nhrpd will
+automatically create additional host routes pointing to gre1 when
+a connection with these hosts is established.
+
+The gre1 subnet prefix should be announced by routing protocol from the
+hub nodes (e.g. BGP 'network' announce). This allows the routing protocol
+to decide which is the closest hub and determine the relay hub on prefix
+basis when direct tunnel is not established.
+
+nhrpd will redistribute directly connected neighbors to zebra. Within
+hub nodes, these routes should be internally redistributed using some
+routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic.
+
+This can be achieved in hubs with the following bgp configuration (network
+command defines the GRE subnet):
+@example
+@group
+router bgp 65555
+   network 172.16.0.0/16
+   redistribute nhrp
+@end group
+@end example
+
+
+@node Configuring NHRP
+@section Configuring NHRP
+
+FIXME
+
+@node Hub Functionality
+@section Hub Functionality
+
+In addition to routing nhrp redistributed host prefixes, the hub nodes
+are also responsible to send NHRP Traffic Indication messages that
+trigger creation of the shortcut tunnels.
+
+nhrpd sends Traffic Indication messages based on network traffic captured
+using NFLOG. Typically you want to send Traffic Indications for network
+traffic that is routed from gre1 back to gre1 in rate limited manner.
+This can be achieved with the following iptables rule.
+
+@example
+@group
+iptables -A FORWARD -i gre1 -o gre1 \
+       -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \
+       --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 \
+       --hashlimit-dstmask 24 --hashlimit-name loglimit-0 \
+       -j NFLOG --nflog-group 1 --nflog-range 128
+@end group
+@end example
+
+You can fine tune the src/dstmask according to the prefix lengths you
+announce internal, add additional IP range matches, or rate limitation
+if needed. However, the above should be good in most cases.
+
+This kernel NFLOG target's nflog-group is configured in global nhrp config
+with:
+@example
+@group
+nhrp nflog-group 1
+@end group
+@end example
+
+To start sending these traffic notices out from hubs, use the nhrp
+per-interface directive:
+@example
+@group
+interface gre1
+ ip nhrp redirect
+@end group
+@end example
+
+@node Integration with IKE
+@section Integration with IKE
+
+nhrpd needs tight integration with IKE daemon for various reasons.
+Currently only strongSwan is supported as IKE daemon.
+
+nhrpd connects to strongSwan using VICI protocol based on UNIX socket
+(hardcoded now as /var/run/charon.vici).
+
+strongSwan currently needs few patches applied. Please check out the
+@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release,release}
+and
+@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras,working tree}
+git repositories for the patches.
+
+@node NHRP Events
+@section NHRP Events
+
+FIXME
+
+@node Configuration Example
+@section Configuration Example
+
+FIXME
diff --git a/doc/ospf6d.8 b/doc/ospf6d.8
new file mode 100644 (file)
index 0000000..0643226
--- /dev/null
@@ -0,0 +1,111 @@
+.TH OSPF6D 8 "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
+.B ospf6d
+[
+.B \-dhv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B ospf6d
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B ospf6d
+command:
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/ospf6d.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When ospf6d starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart ospf6d.  The likely default is \fB\fI/var/run/ospf6d.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the ospf6d VTY will listen on. This defaults to
+2606, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the ospf6d VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/ospf6d
+The default location of the 
+.B ospf6d
+binary.
+.TP
+.BI /usr/local/etc/ospf6d.conf
+The default location of the 
+.B ospf6d
+config file.
+.TP
+.BI $(PWD)/ospf6d.log 
+If the 
+.B ospf6d
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBospf6d\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The ospf6d process may log to standard output, to a VTY, to a log
+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 vtysh (1)
+.SH BUGS
+.B ospf6d
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi
new file mode 100644 (file)
index 0000000..31f4db0
--- /dev/null
@@ -0,0 +1,168 @@
+@node OSPFv3
+@chapter OSPFv3
+
+@command{ospf6d} is a daemon support OSPF version 3 for IPv6 network.
+OSPF for IPv6 is described in RFC2740.
+
+@menu
+* OSPF6 router::                
+* OSPF6 area::                  
+* OSPF6 interface::             
+* Redistribute routes to OSPF6::  
+* Showing OSPF6 information::   
+* OSPF6 Configuration Examples::
+@end menu
+
+@node OSPF6 router
+@section OSPF6 router
+
+@deffn {Command} {router ospf6} {}
+@end deffn
+
+@deffn {OSPF6 Command} {router-id @var{a.b.c.d}} {}
+Set router's Router-ID.
+@end deffn
+
+@deffn {OSPF6 Command} {interface @var{ifname} area @var{area}} {}
+Bind interface to specified area, and start sending OSPF packets.  @var{area} can
+be specified as 0.
+@end deffn
+
+@deffn {OSPF6 Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {}
+@deffnx {OSPF6 Command} {no timers throttle spf} {}
+This command sets the initial @var{delay}, the @var{initial-holdtime}
+and the @var{maximum-holdtime} between when SPF is calculated and the
+event which triggered the calculation. The times are specified in
+milliseconds and must be in the range of 0 to 600000 milliseconds.
+
+The @var{delay} specifies the minimum amount of time to delay SPF
+calculation (hence it affects how long SPF calculation is delayed after
+an event which occurs outside of the holdtime of any previous SPF
+calculation, and also serves as a minimum holdtime).
+
+Consecutive SPF calculations will always be seperated by at least
+'hold-time' milliseconds. The hold-time is adaptive and initially is
+set to the @var{initial-holdtime} configured with the above command.
+Events which occur within the holdtime of the previous SPF calculation
+will cause the holdtime to be increased by @var{initial-holdtime}, bounded
+by the @var{maximum-holdtime} configured with this command. If the adaptive
+hold-time elapses without any SPF-triggering event occuring then
+the current holdtime is reset to the @var{initial-holdtime}.
+
+@example
+@group
+router ospf6
+ timers throttle spf 200 400 10000
+@end group
+@end example
+
+In this example, the @var{delay} is set to 200ms, the @var{initial
+holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence
+there will always be at least 200ms between an event which requires SPF
+calculation and the actual SPF calculation. Further consecutive SPF
+calculations will always be seperated by between 400ms to 10s, the
+hold-time increasing by 400ms each time an SPF-triggering event occurs
+within the hold-time of the previous SPF calculation.
+
+@end deffn
+
+@deffn {OSPF6 Command} {auto-cost reference-bandwidth @var{cost}} {}
+@deffnx {OSPF6 Command} {no auto-cost reference-bandwidth} {}
+This sets the reference bandwidth for cost calculations, where this
+bandwidth is considered equivalent to an OSPF cost of 1, specified in
+Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth 100Mbit/s
+or higher will have a cost of 1. Cost of lower bandwidth links will be
+scaled with reference to this cost).
+
+This configuration setting MUST be consistent across all routers
+within the OSPF domain.
+@end deffn
+
+@node OSPF6 area
+@section OSPF6 area
+
+Area support for OSPFv3 is not yet implemented.
+
+@node OSPF6 interface
+@section OSPF6 interface
+
+@deffn {Interface Command} {ipv6 ospf6 cost COST} {}
+Sets interface's output cost.  Default value depends on the interface
+bandwidth and on the auto-cost reference bandwidth.
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 hello-interval HELLOINTERVAL} {}
+Sets interface's Hello Interval.  Default 40
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 dead-interval DEADINTERVAL} {}
+Sets interface's Router Dead Interval.  Default value is 40.
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL} {}
+Sets interface's Rxmt Interval.  Default value is 5.
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 priority PRIORITY} {}
+Sets interface's Router Priority.  Default value is 1.
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 transmit-delay TRANSMITDELAY} {}
+Sets interface's Inf-Trans-Delay.  Default value is 1.
+@end deffn
+
+@deffn {Interface Command} {ipv6 ospf6 network (broadcast|point-to-point)} {}
+Set explicitly network type for specifed interface.
+@end deffn
+
+@node Redistribute routes to OSPF6
+@section Redistribute routes to OSPF6
+
+@deffn {OSPF6 Command} {redistribute static} {}
+@deffnx {OSPF6 Command} {redistribute connected} {}
+@deffnx {OSPF6 Command} {redistribute ripng} {}
+@end deffn
+
+@node Showing OSPF6 information
+@section Showing OSPF6 information
+
+@deffn {Command} {show ipv6 ospf6 [INSTANCE_ID]} {}
+INSTANCE_ID is an optional OSPF instance ID. To see router ID and OSPF
+instance ID, simply type "show ipv6 ospf6 <cr>".
+@end deffn
+
+@deffn {Command} {show ipv6 ospf6 database} {}
+This command shows LSA database summary.  You can specify the type of LSA.
+@end deffn
+
+@deffn {Command} {show ipv6 ospf6 interface} {}
+To see OSPF interface configuration like costs.
+@end deffn
+
+@deffn {Command} {show ipv6 ospf6 neighbor} {}
+Shows state and chosen (Backup) DR of neighbor.
+@end deffn
+
+@deffn {Command} {show ipv6 ospf6 request-list A.B.C.D} {}
+Shows requestlist of neighbor.
+@end deffn
+
+@deffn {Command} {show ipv6 route ospf6} {}
+This command shows internal routing table.
+@end deffn
+
+@node OSPF6 Configuration Examples
+@section OSPF6 Configuration Examples
+
+Example of ospf6d configured on one interface and area:
+
+@example
+interface eth0
+ ipv6 ospf6 instance-id 0
+!
+router ospf6
+ router-id 212.17.55.53
+ area 0.0.0.0 range 2001:770:105:2::/64
+ interface eth0 area 0.0.0.0
+!
+@end example
diff --git a/doc/ospf_fundamentals.texi b/doc/ospf_fundamentals.texi
new file mode 100644 (file)
index 0000000..82218e6
--- /dev/null
@@ -0,0 +1,582 @@
+@c Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
+@cindex OSPF Fundamentals
+@node OSPF Fundamentals
+@section OSPF Fundamentals
+
+@cindex Link-state routing protocol
+@cindex Distance-vector routing protocol
+@acronym{OSPF} is, mostly, a link-state routing protocol. In contrast
+to @dfn{distance-vector} protocols, such as @acronym{RIP} or
+@acronym{BGP}, where routers describe available @dfn{paths} (i.e@. routes) 
+to each other, in @dfn{link-state} protocols routers instead
+describe the state of their links to their immediate neighbouring
+routers.
+
+@cindex Link State Announcement
+@cindex Link State Advertisement
+@cindex LSA flooding
+@cindex Link State DataBase
+Each router describes their link-state information in a message known
+as an @acronym{LSA,Link State Advertisement}, which is then propogated
+through to all other routers in a link-state routing domain, by a
+process called @dfn{flooding}. Each router thus builds up an
+@acronym{LSDB,Link State Database} of all the link-state messages. From
+this collection of LSAs in the LSDB, each router can then calculate the
+shortest path to any other router, based on some common metric, by
+using an algorithm such as @url{http://www.cs.utexas.edu/users/EWD/,
+Edgser Dijkstra}'s @acronym{SPF,Shortest Path First}.
+
+@cindex Link-state routing protocol advantages
+By describing connectivity of a network in this way, in terms of
+routers and links rather than in terms of the paths through a network,
+a link-state protocol can use less bandwidth and converge more quickly
+than other protocols. A link-state protocol need distribute only one
+link-state message throughout the link-state domain when a link on any
+single given router changes state, in order for all routers to
+reconverge on the best paths through the network. In contrast, distance
+vector protocols can require a progression of different path update
+messages from a series of different routers in order to converge.
+
+@cindex Link-state routing protocol disadvantages
+The disadvantage to a link-state protocol is that the process of
+computing the best paths can be relatively intensive when compared to
+distance-vector protocols, in which near to no computation need be done
+other than (potentially) select between multiple routes. This overhead
+is mostly negligible for modern embedded CPUs, even for networks with
+thousands of nodes. The primary scaling overhead lies more in coping
+with the ever greater frequency of LSA updates as the size of a
+link-state area increases, in managing the @acronym{LSDB} and required
+flooding.
+
+This section aims to give a distilled, but accurate, description of the
+more important workings of @acronym{OSPF}@ which an administrator may need
+to know to be able best configure and trouble-shoot @acronym{OSPF}@.
+
+@subsection OSPF Mechanisms
+
+@acronym{OSPF} defines a range of mechanisms, concerned with detecting,
+describing and propogating state through a network. These mechanisms
+will nearly all be covered in greater detail further on. They may be
+broadly classed as:
+
+@table @dfn
+@cindex OSPF Hello Protocol overview
+@item The Hello Protocol
+
+@cindex OSPF Hello Protocol
+The OSPF Hello protocol allows OSPF to quickly detect changes in
+two-way reachability between routers on a link. OSPF can additionally
+avail of other sources of reachability information, such as link-state
+information provided by hardware, or through dedicated reachability
+protocols such as @acronym{BFD,Bi-directional Forwarding Detection}.
+
+OSPF also uses the Hello protocol to propagate certain state between
+routers sharing a link, for example:
+
+@itemize @bullet
+@item Hello protocol configured state, such as the dead-interval.
+@item Router priority, for DR/BDR election.
+@item DR/BDR election results.
+@item Any optional capabilities supported by each router.
+@end itemize
+
+The Hello protocol is comparatively trivial and will not be explored in
+greater detail than here.
+
+@cindex OSPF LSA overview 
+@item LSAs
+
+At the heart of @acronym{OSPF} are @acronym{LSA,Link State
+Advertisement} messages. Despite the name, some @acronym{LSA}s do not,
+strictly speaking, describe link-state information. Common
+@acronym{LSA}s describe information such as:
+
+@itemize @bullet
+@item
+Routers, in terms of their links.
+@item
+Networks, in terms of attached routers.
+@item
+Routes, external to a link-state domain:
+
+@itemize @bullet
+@item External Routes
+
+Routes entirely external to @acronym{OSPF}@. Routers originating such
+routes are known as @acronym{ASBR,Autonomous-System Border Router}
+routers.
+
+@item Summary Routes
+
+Routes which summarise routing information relating to OSPF areas
+external to the OSPF link-state area at hand, originated by
+@acronym{ABR,Area Boundary Router} routers.
+@end itemize
+@end itemize
+
+@item LSA Flooding
+OSPF defines several related mechanisms, used to manage synchronisation of
+@acronym{LSDB}s between neighbours as neighbours form adjacencies and
+the propogation, or @dfn{flooding} of new or updated @acronym{LSA}s.
+
+@xref{OSPF Flooding}.
+
+@cindex OSPF Areas overview
+@item Areas
+OSPF provides for the protocol to be broken up into multiple smaller
+and independent link-state areas. Each area must be connected to a
+common backbone area by an @acronym{ABR,Area Boundary Router}. These
+@acronym{ABR} routers are responsible for summarising the link-state
+routing information of an area into @dfn{Summary LSAs}, possibly in a
+condensed (i.e. aggregated) form, and then originating these summaries
+into all other areas the @acronym{ABR} is connected to.
+
+Note that only summaries and external routes are passed between areas.
+As these describe @emph{paths}, rather than any router link-states,
+routing between areas hence is by @dfn{distance-vector}, @strong{not}
+link-state.
+
+@xref{OSPF Areas}.
+@end table
+
+@subsection OSPF LSAs
+
+@acronym{LSA}s are the core object in OSPF@. Everything else in OSPF
+revolves around detecting what to describe in LSAs, when to update
+them, how to flood them throughout a network and how to calculate
+routes from them. 
+
+There are a variety of different @acronym{LSA}s, for purposes such
+as describing actual link-state information, describing paths (i.e.
+routes), describing bandwidth usage of links for 
+@acronym{TE,Traffic Engineering} purposes, and even arbitrary data
+by way of @emph{Opaque} @acronym{LSA}s.
+
+@subsubsection LSA Header
+All LSAs share a common header with the following information:
+
+@itemize @bullet
+@item Type
+
+Different types of @acronym{LSA}s describe different things in
+@acronym{OSPF}@. Types include:
+
+@itemize @bullet
+@item Router LSA
+@item Network LSA
+@item Network Summary LSA
+@item Router Summary LSA
+@item AS-External LSA
+@end itemize
+
+The specifics of the different types of LSA are examined below.
+
+@item Advertising Router
+
+The Router ID of the router originating the LSA, see @ref{ospf router-id}.
+
+@item LSA ID
+
+The ID of the LSA, which is typically derived in some way from the
+information the LSA describes, e.g. a Router LSA uses the Router ID as
+the LSA ID, a Network LSA will have the IP address of the @acronym{DR}
+as its LSA ID@.
+
+The combination of the Type, ID and Advertising Router ID must uniquely
+identify the @acronym{LSA}@. There can however be multiple instances of
+an LSA with the same Type, LSA ID and Advertising Router ID, see
+@ref{OSPF LSA sequence number,,LSA Sequence Number}.
+
+@item Age
+
+A number to allow stale @acronym{LSA}s to, eventually, be purged by routers
+from their @acronym{LSDB}s.
+
+The value nominally is one of seconds. An age of 3600, i.e. 1 hour, is
+called the @dfn{MaxAge}. MaxAge LSAs are ignored in routing
+calculations. LSAs must be periodically refreshed by their Advertising
+Router before reaching MaxAge if they are to remain valid.
+
+Routers may deliberately flood LSAs with the age artificially set to
+3600 to indicate an LSA is no longer valid. This is called
+@dfn{flushing} of an LSA@.
+
+It is not abnormal to see stale LSAs in the LSDB, this can occur where
+a router has shutdown without flushing its LSA(s), e.g. where it has
+become disconnected from the network. Such LSAs do little harm.
+
+@anchor{OSPF LSA sequence number}
+@item Sequence Number
+
+A number used to distinguish newer instances of an LSA from older instances.
+@end itemize
+
+@subsubsection Link-State LSAs
+Of all the various kinds of @acronym{LSA}s, just two types comprise the
+actual link-state part of @acronym{OSPF}, Router @acronym{LSA}s and
+Network @acronym{LSA}s. These LSA types are absolutely core to the
+protocol. 
+
+Instances of these LSAs are specific to the link-state area in which
+they are originated. Routes calculated from these two LSA types are
+called @dfn{intra-area routes}.
+
+@itemize @bullet
+@item Router LSA
+
+Each OSPF Router must originate a router @acronym{LSA} to describe
+itself. In it, the router lists each of its @acronym{OSPF} enabled
+interfaces, for the given link-state area, in terms of:
+
+@itemize @bullet
+@item Cost
+
+The output cost of that interface, scaled inversely to some commonly known
+reference value, @xref{OSPF auto-cost reference-bandwidth,,auto-cost
+reference-bandwidth}.
+
+@item Link Type
+@itemize @bullet
+@item Transit Network
+
+A link to a multi-access network, on which the router has at least one
+Full adjacency with another router.
+
+@item @acronym{PtP,Point-to-Point}
+
+A link to a single remote router, with a Full adjacency. No
+@acronym{DR, Designated Router} is elected on such links; no network
+LSA is originated for such a link.
+
+@item Stub
+
+A link with no adjacent neighbours, or a host route.
+@end itemize
+
+@item Link ID and Data
+
+These values depend on the Link Type:
+
+@multitable @columnfractions .18 .32 .32
+@headitem Link Type @tab Link ID @tab Link Data
+
+@item Transit
+@tab Link IP address of the @acronym{DR}
+@tab Interface IP address
+
+@item Point-to-Point
+@tab Router ID of the remote router
+@tab Local interface IP address,
+or the @acronym{ifindex,MIB-II interface index} 
+for unnumbered links
+
+@item Stub
+@tab IP address
+@tab Subnet Mask
+
+@end multitable
+@end itemize
+
+Links on a router may be listed multiple times in the Router LSA, e.g.
+a @acronym{PtP} interface on which OSPF is enabled must @emph{always}
+be described by a Stub link in the Router @acronym{LSA}, in addition to
+being listed as PtP link in the Router @acronym{LSA} if the adjacency
+with the remote router is Full.
+
+Stub links may also be used as a way to describe links on which OSPF is
+@emph{not} spoken, known as @dfn{passive interfaces}, see @ref{OSPF
+passive-interface,,passive-interface}.
+
+@item Network LSA
+
+On multi-access links (e.g. ethernets, certain kinds of ATM and X@.25
+configurations), routers elect a @acronym{DR}@. The @acronym{DR} is
+responsible for originating a Network @acronym{LSA}, which helps reduce
+the information needed to describe multi-access networks with multiple
+routers attached. The @acronym{DR} also acts as a hub for the flooding of
+@acronym{LSA}s on that link, thus reducing flooding overheads.
+
+The contents of the Network LSA describes the:
+
+@itemize @bullet
+@item Subnet Mask
+
+As the @acronym{LSA} ID of a Network LSA must be the IP address of the
+@acronym{DR}, the Subnet Mask together with the @acronym{LSA} ID gives
+you the network address.
+
+@item Attached Routers
+
+Each router fully-adjacent with the @acronym{DR} is listed in the LSA,
+by their Router-ID. This allows the corresponding Router @acronym{LSA}s to be
+easily retrieved from the @acronym{LSDB}@.
+@end itemize
+@end itemize
+
+Summary of Link State LSAs:
+
+@multitable @columnfractions .18 .32 .40
+@headitem LSA Type @tab LSA ID Describes @tab LSA Data Describes
+
+@item Router LSA 
+@tab The Router ID 
+@tab The @acronym{OSPF} enabled links of the router, within
+     a specific link-state area.
+
+@item Network LSA
+@tab The IP address of the @acronym{DR} for the network
+@tab The Subnet Mask of the network, and the Router IDs of all routers
+     on the network.
+@end multitable
+
+With an LSDB composed of just these two types of @acronym{LSA}, it is
+possible to construct a directed graph of the connectivity between all
+routers and networks in a given OSPF link-state area. So, not
+surprisingly, when OSPF routers build updated routing tables, the first
+stage of @acronym{SPF} calculation concerns itself only with these two
+LSA types. 
+
+@subsubsection Link-State LSA Examples
+
+The example below (@pxref{OSPF Link-State LSA Example}) shows two
+@acronym{LSA}s, both originated by the same router (Router ID
+192.168.0.49) and with the same @acronym{LSA} ID (192.168.0.49), but of
+different LSA types.
+
+The first LSA being the router LSA describing 192.168.0.49's links: 2 links
+to multi-access networks with fully-adjacent neighbours (i.e. Transit
+links) and 1 being a Stub link (no adjacent neighbours).
+
+The second LSA being a Network LSA, for which 192.168.0.49 is the
+@acronym{DR}, listing the Router IDs of 4 routers on that network which
+are fully adjacent with 192.168.0.49.
+
+@anchor{OSPF Link-State LSA Example}
+@example
+# show ip ospf database router 192.168.0.49
+
+       OSPF Router with ID (192.168.0.53)
+
+
+                Router Link States (Area 0.0.0.0)
+
+  LS age: 38
+  Options: 0x2  : *|-|-|-|-|-|E|*
+  LS Flags: 0x6  
+  Flags: 0x2 : ASBR
+  LS Type: router-LSA
+  Link State ID: 192.168.0.49 
+  Advertising Router: 192.168.0.49
+  LS Seq Number: 80000f90
+  Checksum: 0x518b
+  Length: 60
+   Number of Links: 3
+
+    Link connected to: a Transit Network
+     (Link ID) Designated Router address: 192.168.1.3
+     (Link Data) Router Interface address: 192.168.1.3
+      Number of TOS metrics: 0
+       TOS 0 Metric: 10
+
+    Link connected to: a Transit Network
+     (Link ID) Designated Router address: 192.168.0.49
+     (Link Data) Router Interface address: 192.168.0.49
+      Number of TOS metrics: 0
+       TOS 0 Metric: 10
+
+    Link connected to: Stub Network
+     (Link ID) Net: 192.168.3.190
+     (Link Data) Network Mask: 255.255.255.255
+      Number of TOS metrics: 0
+       TOS 0 Metric: 39063
+# show ip ospf database network 192.168.0.49
+
+       OSPF Router with ID (192.168.0.53)
+
+
+                Net Link States (Area 0.0.0.0)
+
+  LS age: 285
+  Options: 0x2  : *|-|-|-|-|-|E|*
+  LS Flags: 0x6  
+  LS Type: network-LSA
+  Link State ID: 192.168.0.49 (address of Designated Router)
+  Advertising Router: 192.168.0.49
+  LS Seq Number: 80000074
+  Checksum: 0x0103
+  Length: 40
+  Network Mask: /29
+        Attached Router: 192.168.0.49
+        Attached Router: 192.168.0.52
+        Attached Router: 192.168.0.53
+        Attached Router: 192.168.0.54
+@end example
+
+Note that from one LSA, you can find the other. E.g. Given the
+Network-LSA you have a list of Router IDs on that network, from which
+you can then look up, in the local @acronym{LSDB}, the matching Router
+LSA@. From that Router-LSA you may (potentially) find links to other
+Transit networks and Routers IDs which can be used to lookup the
+corresponding Router or Network LSA@. And in that fashion, one can find
+all the Routers and Networks reachable from that starting @acronym{LSA}@.
+
+Given the Router LSA instead, you have the IP address of the
+@acronym{DR} of any attached transit links. Network LSAs will have that IP
+as their LSA ID, so you can then look up that Network LSA and from that
+find all the attached routers on that link, leading potentially to more
+links and Network and Router LSAs, etc. etc.
+
+From just the above two @acronym{LSA}s, one can already see the
+following partial topology:
+@example
+@group
+
+      
+   --------------------- Network: ......
+            |            Designated Router IP: 192.168.1.3
+            |
+      IP: 192.168.1.3
+       (transit link)
+        (cost: 10)
+   Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
+        (cost: 10)        (cost: 39063)
+       (transit link)
+      IP: 192.168.0.49
+            |
+            |
+------------------------------ Network: 192.168.0.48/29
+  |        |           |       Designated Router IP: 192.168.0.49
+  |        |           |
+  |        |     Router ID: 192.168.0.54
+  |        |
+  |   Router ID: 192.168.0.53
+  |
+Router ID: 192.168.0.52
+@end group
+@end example
+
+Note the Router IDs, though they look like IP addresses and often are
+IP addresses, are not strictly speaking IP addresses, nor need they be
+reachable addresses (though, OSPF will calculate routes to Router IDs).
+
+@subsubsection External LSAs
+
+External, or "Type 5", @acronym{LSA}s describe routing information which is
+entirely external to @acronym{OSPF}, and is "injected" into
+@acronym{OSPF}@. Such routing information may have come from another
+routing protocol, such as RIP or BGP, they may represent static routes
+or they may represent a default route.
+
+An @acronym{OSPF} router which originates External @acronym{LSA}s is known as an
+@acronym{ASBR,AS Boundary Router}. Unlike the link-state @acronym{LSA}s, and
+most other @acronym{LSA}s, which are flooded only within the area in
+which they originate, External @acronym{LSA}s are flooded through-out
+the @acronym{OSPF} network to all areas capable of carrying External
+@acronym{LSA}s (@pxref{OSPF Areas}).
+
+Routes internal to OSPF (intra-area or inter-area) are always preferred
+over external routes.
+
+The External @acronym{LSA} describes the following:
+
+@itemize @bullet
+@item IP Network number
+
+The IP Network number of the route is described by the @acronym{LSA} ID
+field.
+
+@item IP Network Mask
+
+The body of the External LSA describes the IP Network Mask of the
+route. This, together with the @acronym{LSA} ID, describes the prefix
+of the IP route concerned.
+
+@item Metric
+
+The cost of the External Route. This cost may be an OSPF cost (also
+known as a "Type 1" metric), i.e. equivalent to the normal OSPF costs,
+or an externally derived cost ("Type 2" metric) which is not comparable
+to OSPF costs and always considered larger than any OSPF cost. Where
+there are both Type 1 and 2 External routes for a route, the Type 1 is
+always preferred.
+
+@item Forwarding Address
+
+The address of the router to forward packets to for the route. This may
+be, and usually is, left as 0 to specify that the ASBR originating the
+External @acronym{LSA} should be used. There must be an internal OSPF
+route to the forwarding address, for the forwarding address to be
+useable.
+
+@item Tag
+
+An arbitrary 4-bytes of data, not interpreted by OSPF, which may
+carry whatever information about the route which OSPF speakers desire.
+@end itemize
+
+@subsubsection AS External LSA Example
+
+To illustrate, below is an example of an External @acronym{LSA} in the
+@acronym{LSDB} of an OSPF router. It describes a route to the IP prefix
+of 192.168.165.0/24, originated by the ASBR with Router-ID
+192.168.0.49. The metric of 20 is external to OSPF. The forwarding
+address is 0, so the route should forward to the originating ASBR if
+selected.
+
+@example
+@group
+# show ip ospf database external 192.168.165.0
+  LS age: 995
+  Options: 0x2  : *|-|-|-|-|-|E|*
+  LS Flags: 0x9
+  LS Type: AS-external-LSA
+  Link State ID: 192.168.165.0 (External Network Number)
+  Advertising Router: 192.168.0.49
+  LS Seq Number: 800001d8
+  Checksum: 0xea27
+  Length: 36
+  Network Mask: /24
+        Metric Type: 2 (Larger than any link state path)
+        TOS: 0
+        Metric: 20
+        Forward Address: 0.0.0.0
+        External Route Tag: 0
+@end group
+@end example
+
+We can add this to our partial topology from above, which now looks
+like:
+@example
+@group
+   --------------------- Network: ......
+            |            Designated Router IP: 192.168.1.3
+            |
+      IP: 192.168.1.3      /---- External route: 192.168.165.0/24
+       (transit link)     /                Cost: 20 (External metric)
+        (cost: 10)       /
+   Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
+        (cost: 10)        (cost: 39063)
+       (transit link)
+      IP: 192.168.0.49
+            |
+            |
+------------------------------ Network: 192.168.0.48/29
+  |        |           |       Designated Router IP: 192.168.0.49
+  |        |           |
+  |        |     Router ID: 192.168.0.54
+  |        |
+  |   Router ID: 192.168.0.53
+  |
+Router ID: 192.168.0.52
+@end group
+@end example
+
+@subsubsection Summary LSAs
+
+Summary LSAs are created by @acronym{ABR}s to summarise the destinations available within one area to other areas. These LSAs may describe IP networks, potentially in aggregated form, or @acronym{ASBR} routers. 
+
+@anchor{OSPF Flooding}
+@subsection OSPF Flooding
+
+@anchor{OSPF Areas}
+@subsection OSPF Areas
diff --git a/doc/ospfclient.8 b/doc/ospfclient.8
new file mode 100644 (file)
index 0000000..ccfad1a
--- /dev/null
@@ -0,0 +1,42 @@
+.\" This file was originally generated by help2man 1.36.
+.TH OSPFCLIENT "1" "July 2010"
+.SH NAME
+ospfclient \- an example ospf-api client
+.SH SYNOPSIS
+.B ospfclient
+.I ospfd
+.I lsatype
+.I opaquetype
+.I opaqueid
+.I ifaddr
+.I areaid
+.SH DESCRIPTION
+.B ospfclient
+is a an example ospf-api client to test the ospfd daemon.
+.SH OPTIONS
+.TP
+.I ospfd
+A router where the API\-enabled OSPF daemon is running.
+.TP
+.I lsatype
+The value has to be either "9", "10", or "11", depending on the flooding
+scope.
+.TP
+.I opaquetype
+The value has to be in the range of 0\-255 (for example, experimental
+applications use
+.I opaquetype
+larger than 128).
+.TP
+.I opaqueid
+Arbitrary application instance (24 bits).
+.TP
+.I ifaddr
+Interface IP address for type 9, otherwise it will be ignored.
+.TP
+.I areaid
+Area in the IP address format for type 10, otherwise it will be ignored.
+.SH "SEE ALSO"
+.BR ospfd (8).
+.SH AUTHORS
+See the project homepage at <http://www.quagga.net/>.
diff --git a/doc/ospfd.8 b/doc/ospfd.8
new file mode 100644 (file)
index 0000000..8c819cf
--- /dev/null
@@ -0,0 +1,113 @@
+.TH OSPFD 8 "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
+.B ospfd
+[
+.B \-dhlv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B ospfd
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B ospfd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/ospfd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When ospfd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart ospfd.  The likely default is \fB\fI/var/run/ospfd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the ospfd VTY will listen on. This defaults to
+2604, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the ospfd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-a\fR, \fB\-\-apiserver \fR
+Enable OSPF apiserver. Default is disabled.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/ospfd
+The default location of the 
+.B ospfd
+binary.
+.TP
+.BI /usr/local/etc/ospfd.conf
+The default location of the 
+.B ospfd
+config file.
+.TP
+.BI $(PWD)/ospfd.log 
+If the 
+.B ospfd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBospfd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The ospfd process may log to standard output, to a VTY, to a log
+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 vtysh (1)
+.SH BUGS
+.B ospfd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/ospfd.texi b/doc/ospfd.texi
new file mode 100644 (file)
index 0000000..d60ecf2
--- /dev/null
@@ -0,0 +1,922 @@
+
+@cindex OSPFv2
+@node OSPFv2
+@chapter OSPFv2
+
+@acronym{OSPF,Open Shortest Path First} version 2 is a routing protocol
+which is described in @cite{RFC2328, OSPF Version 2}.  OSPF is an
+@acronym{IGP,Interior Gateway Protocol}.  Compared with @acronym{RIP},
+@acronym{OSPF} can provide scalable network support and faster
+convergence times.  OSPF is widely used in large networks such as
+@acronym{ISP,Internet Service Provider} backbone and enterprise
+networks.
+
+@menu
+* OSPF Fundamentals::
+* Configuring ospfd::           
+* OSPF router::                 
+* OSPF area::                   
+* OSPF interface::              
+* Redistribute routes to OSPF::  
+* Showing OSPF information::    
+* Opaque LSA::
+* OSPF Traffic Engineering::
+* Router Information::
+* Debugging OSPF::              
+* OSPF Configuration Examples::
+@end menu
+
+@include ospf_fundamentals.texi
+
+@node Configuring ospfd
+@section Configuring ospfd
+
+There are no @command{ospfd} specific options.  Common options can be
+specified (@pxref{Common Invocation Options}) to @command{ospfd}.
+@command{ospfd} needs to acquire interface information from
+@command{zebra} in order to function. Therefore @command{zebra} must be
+running before invoking @command{ospfd}. Also, if @command{zebra} is
+restarted then @command{ospfd} must be too.
+
+Like other daemons, @command{ospfd} configuration is done in @acronym{OSPF}
+specific configuration file @file{ospfd.conf}.
+
+@node OSPF router
+@section OSPF router
+
+To start OSPF process you have to specify the OSPF router.  As of this
+writing, @command{ospfd} does not support multiple OSPF processes.
+
+@deffn Command {router ospf} {}
+@deffnx Command {no router ospf} {}
+Enable or disable the OSPF process.  @command{ospfd} does not yet
+support multiple OSPF processes.  So you can not specify an OSPF process
+number.
+@end deffn
+
+@deffn {OSPF Command} {ospf router-id @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no ospf router-id} {}
+@anchor{ospf router-id}This sets the router-ID of the OSPF process. The
+router-ID may be an IP address of the router, but need not be - it can
+be any arbitrary 32bit number. However it MUST be unique within the
+entire OSPF domain to the OSPF speaker - bad things will happen if
+multiple OSPF speakers are configured with the same router-ID! If one
+is not specified then @command{ospfd} will obtain a router-ID
+automatically from @command{zebra}.
+@end deffn
+
+@deffn {OSPF Command} {ospf abr-type @var{type}} {}
+@deffnx {OSPF Command} {no ospf abr-type @var{type}} {}
+@var{type} can be cisco|ibm|shortcut|standard. The "Cisco" and "IBM" types
+are equivalent.
+
+The OSPF standard for ABR behaviour does not allow an ABR to consider
+routes through non-backbone areas when its links to the backbone are
+down, even when there are other ABRs in attached non-backbone areas
+which still can reach the backbone - this restriction exists primarily
+to ensure routing-loops are avoided.
+
+With the "Cisco" or "IBM" ABR type, the default in this release of
+Quagga, this restriction is lifted, allowing an ABR to consider
+summaries learnt from other ABRs through non-backbone areas, and hence
+route via non-backbone areas as a last resort when, and only when,
+backbone links are down.
+
+Note that areas with fully-adjacent virtual-links are considered to be
+"transit capable" and can always be used to route backbone traffic, and
+hence are unaffected by this setting (@pxref{OSPF virtual-link}).
+
+More information regarding the behaviour controlled by this command can
+be found in @cite{RFC 3509, Alternative Implementations of OSPF Area
+Border Routers}, and @cite{draft-ietf-ospf-shortcut-abr-02.txt}.
+
+Quote: "Though the definition of the @acronym{ABR,Area Border Router}
+in the OSPF specification does not require a router with multiple
+attached areas to have a backbone connection, it is actually
+necessary to provide successful routing to the inter-area and
+external destinations. If this requirement is not met, all traffic
+destined for the areas not connected to such an ABR or out of the
+OSPF domain, is dropped.  This document describes alternative ABR
+behaviors implemented in Cisco and IBM routers."
+@end deffn
+
+@deffn {OSPF Command} {ospf rfc1583compatibility} {}
+@deffnx {OSPF Command} {no ospf rfc1583compatibility} {}
+@cite{RFC2328}, the sucessor to @cite{RFC1583}, suggests according
+to section G.2 (changes) in section 16.4 a change to the path
+preference algorithm that prevents possible routing loops that were
+possible in the old version of OSPFv2. More specifically it demands
+that inter-area paths and intra-area backbone path are now of equal preference
+but still both preferred to external paths.
+
+This command should NOT be set normally.
+@end deffn
+
+@deffn {OSPF Command} {log-adjacency-changes [detail]} {}
+@deffnx {OSPF Command} {no log-adjacency-changes [detail]} {}
+Configures ospfd to log changes in adjacency.  With the optional
+detail argument, all changes in adjacency status are shown.  Without detail,
+only changes to full or regressions are shown.
+@end deffn
+
+@deffn {OSPF Command} {passive-interface @var{interface}} {}
+@deffnx {OSPF Command} {no passive-interface @var{interface}} {}
+@anchor{OSPF passive-interface} Do not speak OSPF interface on the
+given interface, but do advertise the interface as a stub link in the
+router-@acronym{LSA,Link State Advertisement} for this router. This
+allows one to advertise addresses on such connected interfaces without
+having to originate AS-External/Type-5 LSAs (which have global flooding
+scope) - as would occur if connected addresses were redistributed into
+OSPF (@pxref{Redistribute routes to OSPF})@. This is the only way to
+advertise non-OSPF links into stub areas.
+@end deffn
+
+@deffn {OSPF Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {}
+@deffnx {OSPF Command} {no timers throttle spf} {}
+This command sets the initial @var{delay}, the @var{initial-holdtime}
+and the @var{maximum-holdtime} between when SPF is calculated and the
+event which triggered the calculation. The times are specified in
+milliseconds and must be in the range of 0 to 600000 milliseconds.
+
+The @var{delay} specifies the minimum amount of time to delay SPF
+calculation (hence it affects how long SPF calculation is delayed after
+an event which occurs outside of the holdtime of any previous SPF
+calculation, and also serves as a minimum holdtime).
+
+Consecutive SPF calculations will always be seperated by at least
+'hold-time' milliseconds. The hold-time is adaptive and initially is
+set to the @var{initial-holdtime} configured with the above command.
+Events which occur within the holdtime of the previous SPF calculation
+will cause the holdtime to be increased by @var{initial-holdtime}, bounded
+by the @var{maximum-holdtime} configured with this command. If the adaptive
+hold-time elapses without any SPF-triggering event occuring then 
+the current holdtime is reset to the @var{initial-holdtime}. The current
+holdtime can be viewed with @ref{show ip ospf}, where it is expressed as 
+a multiplier of the @var{initial-holdtime}.
+
+@example
+@group
+router ospf
+ timers throttle spf 200 400 10000
+@end group
+@end example
+
+In this example, the @var{delay} is set to 200ms, the @var{initial
+holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence
+there will always be at least 200ms between an event which requires SPF
+calculation and the actual SPF calculation. Further consecutive SPF
+calculations will always be seperated by between 400ms to 10s, the
+hold-time increasing by 400ms each time an SPF-triggering event occurs
+within the hold-time of the previous SPF calculation.
+
+This command supercedes the @command{timers spf} command in previous Quagga
+releases.
+@end deffn
+
+@deffn {OSPF Command} {max-metric router-lsa [on-startup|on-shutdown] <5-86400>} {}
+@deffnx {OSPF Command} {max-metric router-lsa administrative} {}
+@deffnx {OSPF Command} {no max-metric router-lsa [on-startup|on-shutdown|administrative]} {}
+This enables @cite{RFC3137, OSPF Stub Router Advertisement} support,
+where the OSPF process describes its transit links in its router-LSA as
+having infinite distance so that other routers will avoid calculating
+transit paths through the router while still being able to reach
+networks through the router.
+
+This support may be enabled administratively (and indefinitely) or
+conditionally. Conditional enabling of max-metric router-lsas can be
+for a period of seconds after startup and/or for a period of seconds
+prior to shutdown. 
+
+Enabling this for a period after startup allows OSPF to converge fully
+first without affecting any existing routes used by other routers,
+while still allowing any connected stub links and/or redistributed
+routes to be reachable. Enabling this for a period of time in advance
+of shutdown allows the router to gracefully excuse itself from the OSPF
+domain. 
+
+Enabling this feature administratively allows for administrative
+intervention for whatever reason, for an indefinite period of time.
+Note that if the configuration is written to file, this administrative
+form of the stub-router command will also be written to file. If
+@command{ospfd} is restarted later, the command will then take effect
+until manually deconfigured.
+
+Configured state of this feature as well as current status, such as the
+number of second remaining till on-startup or on-shutdown ends, can be
+viewed with the @ref{show ip ospf} command.
+@end deffn
+
+@deffn {OSPF Command} {auto-cost reference-bandwidth <1-4294967>} {}
+@deffnx {OSPF Command} {no auto-cost reference-bandwidth} {}
+@anchor{OSPF auto-cost reference-bandwidth}This sets the reference
+bandwidth for cost calculations, where this bandwidth is considered
+equivalent to an OSPF cost of 1, specified in Mbits/s. The default is
+100Mbit/s (i.e. a link of bandwidth 100Mbit/s or higher will have a
+cost of 1. Cost of lower bandwidth links will be scaled with reference
+to this cost).
+
+This configuration setting MUST be consistent across all routers within the
+OSPF domain.
+@end deffn
+
+@deffn {OSPF Command} {network @var{a.b.c.d/m} area @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {network @var{a.b.c.d/m} area @var{<0-4294967295>}} {}
+@deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{<0-4294967295>}} {}
+@anchor{OSPF network command}
+This command specifies the OSPF enabled interface(s).  If the interface has
+an address from range 192.168.1.0/24 then the command below enables ospf
+on this interface so router can provide network information to the other
+ospf routers via this interface.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+@end group
+@end example
+
+Prefix length in interface must be equal or bigger (ie. smaller network) than
+prefix length in network statement. For example statement above doesn't enable
+ospf on interface with address 192.168.1.1/23, but it does on interface with
+address 192.168.1.129/25.
+
+Note that the behavior when there is a peer address
+defined on an interface changed after release 0.99.7.
+Currently, if a peer prefix has been configured,
+then we test whether the prefix in the network command contains
+the destination prefix.  Otherwise, we test whether the network command prefix
+contains the local address prefix of the interface. 
+
+In some cases it may be more convenient to enable OSPF on a per
+interface/subnet basis (@pxref{OSPF ip ospf area command}).
+
+@end deffn
+
+@node OSPF area
+@section OSPF area
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {area <0-4294967295> range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {no area <0-4294967295> range @var{a.b.c.d/m}} {}
+Summarize intra area paths from specified area into one Type-3 summary-LSA
+announced to other areas. This command can be used only in ABR and ONLY
+router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
+be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
+Summarizing Type-7 AS-external-LSAs isn't supported yet by Quagga.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8
+@end group
+@end example
+
+With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
+announced into backbone area if area 0.0.0.10 contains at least one intra-area
+network (ie. described with router or network LSA) from this range.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {}
+Instead of summarizing intra area paths filter them - ie. intra area paths from this
+range are not advertised into other areas.
+This command makes sense in ABR only.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {}
+Substitute summarized prefix with another prefix.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
+@end group
+@end example
+
+One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if
+area 0.0.0.10 contains at least one intra-area network (ie. described with router-LSA or
+network-LSA) from range 10.0.0.0/8.
+This command makes sense in ABR only.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {area <0-4294967295> virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no area <0-4294967295> virtual-link @var{a.b.c.d}} {}
+@anchor{OSPF virtual-link}
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} shortcut} {}
+@deffnx {OSPF Command} {area <0-4294967295> shortcut} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} shortcut} {}
+@deffnx {OSPF Command} {no area <0-4294967295> shortcut} {}
+Configure the area as Shortcut capable. See @cite{RFC3509}. This requires
+that the 'abr-type' be set to 'shortcut'.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} stub} {}
+@deffnx {OSPF Command} {area <0-4294967295> stub} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} stub} {}
+@deffnx {OSPF Command} {no area <0-4294967295> stub} {}
+Configure the area to be a stub area. That is, an area where no router
+originates routes external to OSPF and hence an area where all external 
+routes are via the ABR(s). Hence, ABRs for such an area do not need
+to pass AS-External LSAs (type-5s) or ASBR-Summary LSAs (type-4) into the
+area. They need only pass Network-Summary (type-3) LSAs into such an area,
+along with a default-route summary.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} stub no-summary} {}
+@deffnx {OSPF Command} {area <0-4294967295> stub no-summary} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} stub no-summary} {}
+@deffnx {OSPF Command} {no area <0-4294967295> stub no-summary} {}
+Prevents an @command{ospfd} ABR from injecting inter-area 
+summaries into the specified stub area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} default-cost <0-16777215>} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} default-cost <0-16777215>} {}
+Set the cost of default-summary LSAs announced to stubby areas.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} export-list NAME} {}
+@deffnx {OSPF Command} {area <0-4294967295> export-list NAME} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} export-list NAME} {}
+@deffnx {OSPF Command} {no area <0-4294967295> export-list NAME} {}
+Filter Type-3 summary-LSAs announced to other areas originated from intra-
+area paths from specified area.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 export-list foo
+!
+access-list foo permit 10.10.0.0/16
+access-list foo deny any
+@end group
+@end example
+
+With example above any intra-area paths from area 0.0.0.10 and from range
+10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into
+other areas as Type-3 summary-LSA's, but any others (for example 10.11.0.0/16
+or 10.128.30.16/30) aren't.
+
+This command is only relevant if the router is an ABR for the specified
+area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} import-list NAME} {}
+@deffnx {OSPF Command} {area <0-4294967295> import-list NAME} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} import-list NAME} {}
+@deffnx {OSPF Command} {no area <0-4294967295> import-list NAME} {}
+Same as export-list, but it applies to paths announced into specified area as
+Type-3 summary-LSAs.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {no area <0-4294967295> filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {no area <0-4294967295> filter-list prefix NAME out} {}
+Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
+makes sense in ABR only.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} authentication} {}
+@deffnx {OSPF Command} {area <0-4294967295> authentication} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} authentication} {}
+@deffnx {OSPF Command} {no area <0-4294967295> authentication} {}
+Specify that simple password authentication should be used for the given
+area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} authentication message-digest} {}
+@deffnx {OSPF Command} {area <0-4294967295> authentication message-digest} {}
+
+@anchor{area authentication message-digest}Specify that OSPF packets
+must be authenticated with MD5 HMACs within the given area. Keying
+material must also be configured on a per-interface basis (@pxref{ip
+ospf message-digest-key}).
+
+MD5 authentication may also be configured on a per-interface basis
+(@pxref{ip ospf authentication message-digest}). Such per-interface
+settings will override any per-area authentication setting.
+@end deffn
+
+@node OSPF interface
+@section OSPF interface
+
+@deffn {Interface Command} {ip ospf area @var{AREA} [@var{ADDR}]} {} 
+@deffnx {Interface Command} {no ip ospf area [@var{ADDR}]} {}
+@anchor{OSPF ip ospf area command}
+
+Enable OSPF on the interface, optionally restricted to just the IP address
+given by @var{ADDR}, putting it in the @var{AREA} area. Per interface area
+settings take precedence to network commands (@pxref{OSPF network command}).
+
+If you have a lot of interfaces, and/or a lot of subnets, then enabling OSPF
+via this command may result in a slight performance improvement.
+
+@end deffn
+
+@deffn {Interface Command} {ip ospf authentication-key @var{AUTH_KEY}} {}
+@deffnx {Interface Command} {no ip ospf authentication-key} {}
+Set OSPF authentication key to a simple password.  After setting @var{AUTH_KEY},
+all OSPF packets are authenticated. @var{AUTH_KEY} has length up to 8 chars.
+
+Simple text password authentication is insecure and deprecated in favour of
+MD5 HMAC authentication (@pxref{ip ospf authentication message-digest}).
+@end deffn
+
+@deffn {Interface Command} {ip ospf authentication message-digest} {}
+@anchor{ip ospf authentication message-digest}Specify that MD5 HMAC
+authentication must be used on this interface. MD5 keying material must
+also be configured (@pxref{ip ospf message-digest-key}). Overrides any
+authentication enabled on a per-area basis (@pxref{area
+authentication message-digest}).
+
+Note that OSPF MD5 authentication requires that time never go backwards
+(correct time is NOT important, only that it never goes backwards), even
+across resets, if ospfd is to be able to promptly reestabish adjacencies
+with its neighbours after restarts/reboots. The host should have system
+time be set at boot from an external or non-volatile source (eg battery backed clock, NTP,
+etc.) or else the system clock should be periodically saved to non-volative
+storage and restored at boot if MD5 authentication is to be expected to work
+reliably.
+@end deffn
+
+@deffn {Interface Command} {ip ospf message-digest-key KEYID md5 KEY} {}
+@deffnx {Interface Command} {no ip ospf message-digest-key} {}
+@anchor{ip ospf message-digest-key}Set OSPF authentication key to a
+cryptographic password.  The cryptographic algorithm is MD5.  
+
+KEYID identifies secret key used to create the message digest. This ID
+is part of the protocol and must be consistent across routers on a
+link.
+
+KEY is the actual message digest key, of up to 16 chars (larger strings
+will be truncated), and is associated with the given KEYID.
+@end deffn
+
+@deffn {Interface Command} {ip ospf cost <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf cost} {}
+Set link cost for the specified interface.  The cost value is set to router-LSA's
+metric field and used for SPF calculation.
+@end deffn
+
+@deffn {Interface Command} {ip ospf dead-interval <1-65535>} {}
+@deffnx {Interface Command} {ip ospf dead-interval minimal hello-multiplier <2-20>} {}
+@deffnx {Interface Command} {no ip ospf dead-interval} {}
+@anchor{ip ospf dead-interval minimal} Set number of seconds for
+RouterDeadInterval timer value used for Wait Timer and Inactivity
+Timer.  This value must be the same for all routers attached to a
+common network.  The default value is 40 seconds.
+
+If 'minimal' is specified instead, then the dead-interval is set to 1
+second and one must specify a hello-multiplier. The hello-multiplier
+specifies how many Hellos to send per second, from 2 (every 500ms) to
+20 (every 50ms). Thus one can have 1s convergence time for OSPF. If this form
+is specified, then the hello-interval advertised in Hello packets is set to
+0 and the hello-interval on received Hello packets is not checked, thus 
+the hello-multiplier need NOT be the same across multiple routers on a common
+link.
+@end deffn
+
+@deffn {Interface Command} {ip ospf hello-interval <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf hello-interval} {}
+Set number of seconds for HelloInterval timer value.  Setting this value,
+Hello packet will be sent every timer value seconds on the specified interface.
+This value must be the same for all routers attached to a common network.
+The default value is 10 seconds.
+
+This command has no effect if @ref{ip ospf dead-interval minimal} is also 
+specified for the interface.
+@end deffn
+
+@deffn {Interface Command} {ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)} {}
+@deffnx {Interface Command} {no ip ospf network} {}
+Set explicitly network type for specifed interface.
+@end deffn
+
+@deffn {Interface Command} {ip ospf priority <0-255>} {}
+@deffnx {Interface Command} {no ip ospf priority} {}
+Set RouterPriority integer value.  The router with the highest priority
+will be more eligible to become Designated Router.  Setting the value
+to 0, makes the router ineligible to become Designated Router. The
+default value is 1.
+@end deffn
+
+@deffn {Interface Command} {ip ospf retransmit-interval <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf retransmit interval} {}
+Set number of seconds for RxmtInterval timer value.  This value is used
+when retransmitting Database Description and Link State Request packets.
+The default value is 5 seconds.
+@end deffn
+
+@deffn {Interface Command} {ip ospf transmit-delay} {}
+@deffnx {Interface Command} {no ip ospf transmit-delay} {}
+Set number of seconds for InfTransDelay value.  LSAs' age should be 
+incremented by this value when transmitting.
+The default value is 1 seconds.
+@end deffn
+
+@node Redistribute routes to OSPF
+@section Redistribute routes to OSPF
+
+@deffn {OSPF Command} {redistribute (kernel|connected|static|rip|bgp)} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) @var{route-map}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2)} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214>} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214> route-map @var{word}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214>} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214> route-map @var{word}} {}
+@deffnx {OSPF Command} {no redistribute (kernel|connected|static|rip|bgp)} {}
+@anchor{OSPF redistribute}Redistribute routes of the specified protocol
+or kind into OSPF, with the metric type and metric set if specified,
+filtering the routes using the given route-map if specified.
+Redistributed routes may also be filtered with distribute-lists, see
+@ref{ospf distribute-list}.
+
+Redistributed routes are distributed as into OSPF as Type-5 External
+LSAs into links to areas that accept external routes, Type-7 External LSAs
+for NSSA areas and are not redistributed at all into Stub areas, where
+external routes are not permitted.
+
+Note that for connected routes, one may instead use
+@dfn{passive-interface}, see @ref{OSPF passive-interface}.
+@end deffn
+
+@deffn {OSPF Command} {default-information originate} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214>} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2)} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {default-information originate always} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214>} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2)} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {no default-information originate} {}
+Originate an AS-External (type-5) LSA describing a default route into
+all external-routing capable areas, of the specified metric and metric
+type. If the 'always' keyword is given then the default is always
+advertised, even when there is no default present in the routing table.
+@end deffn
+
+@deffn {OSPF Command} {distribute-list NAME out (kernel|connected|static|rip|ospf} {}
+@deffnx {OSPF Command} {no distribute-list NAME out (kernel|connected|static|rip|ospf} {}
+@anchor{ospf distribute-list}Apply the access-list filter, NAME, to
+redistributed routes of the given type before allowing the routes to
+redistributed into OSPF (@pxref{OSPF redistribute}).
+@end deffn
+
+@deffn {OSPF Command} {default-metric <0-16777214>} {}
+@deffnx {OSPF Command} {no default-metric} {}
+@end deffn
+
+@deffn {OSPF Command} {distance <1-255>} {}
+@deffnx {OSPF Command} {no distance <1-255>} {}
+@end deffn
+
+@deffn {OSPF Command} {distance ospf (intra-area|inter-area|external) <1-255>} {}
+@deffnx {OSPF Command} {no distance ospf} {}
+@end deffn
+
+@node Showing OSPF information
+@section Showing OSPF information
+
+@deffn {Command} {show ip ospf} {}
+@anchor{show ip ospf}Show information on a variety of general OSPF and
+area state and configuration information.
+@end deffn
+
+@deffn {Command} {show ip ospf interface [INTERFACE]} {}
+Show state and configuration of OSPF the specified interface, or all
+interfaces if no interface is given.
+@end deffn
+
+@deffn {Command} {show ip ospf neighbor} {}
+@deffnx {Command} {show ip ospf neighbor INTERFACE} {}
+@deffnx {Command} {show ip ospf neighbor detail} {}
+@deffnx {Command} {show ip ospf neighbor INTERFACE detail} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database} {}
+@deffnx {Command} {show ip ospf database asbr-summary} {}
+@deffnx {Command} {show ip ospf database external} {}
+@deffnx {Command} {show ip ospf database network} {}
+@deffnx {Command} {show ip ospf database asbr-router} {}
+@deffnx {Command} {show ip ospf database summary} {}
+@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id}} {}
+@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id} adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database @dots{} adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id} self-originate} {}
+@deffnx {Command} {show ip ospf database @dots{} self-originate} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database max-age} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database self-originate} {}
+@end deffn
+
+@deffn {Command} {show ip ospf route} {}
+Show the OSPF routing table, as determined by the most recent SPF calculation.
+@end deffn
+
+@node Opaque LSA
+@section Opaque LSA
+
+@deffn {OSPF Command} {ospf opaque-lsa} {}
+@deffnx {OSPF Command} {capability opaque} {}
+@deffnx {OSPF Command} {no ospf opaque-lsa} {} 
+@deffnx {OSPF Command} {no capability opaque} {}
+@command{ospfd} support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration file. Alternate command could be "mpls-te on" (@ref{OSPF Traffic Engineering}).
+@end deffn
+
+@deffn {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external)} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} self-originate} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) self-originate} {}
+Show Opaque LSA from the database.
+@end deffn
+
+@node OSPF Traffic Engineering
+@section Traffic Engineering
+
+@deffn {OSPF Command} {mpls-te on} {}
+@deffnx {OSPF Command} {no mpls-te} {}
+Enable Traffic Engineering LSA flooding.
+@end deffn
+
+@deffn {OSPF Command} {mpls-te router-address <A.B.C.D>} {}
+@deffnx {OSPF Command} {no mpls-te} {}
+Configure stable IP address for MPLS-TE. This IP address is then advertise in Opaque LSA Type-10 TLV=1 (TE)
+option 1 (Router-Address).
+@end deffn
+
+@deffn {OSPF Command} {mpls-te inter-as area <area-id>|as} {}
+@deffnx {OSPF Command} {no mpls-te inter-as} {}
+Enable RFC5392 suuport - Inter-AS TE v2 - to flood Traffic Engineering parameters of Inter-AS link.
+2 modes are supported: AREA and AS; LSA are flood in AREA <area-id> with Opaque Type-10, 
+respectively in AS with Opaque Type-11. In all case, Opaque-LSA TLV=6.
+@end deffn
+
+@deffn {Command} {show ip ospf mpls-te interface} {}
+@deffnx {Command} {show ip ospf mpls-te interface @var{interface}} {}
+Show MPLS Traffic Engineering parameters for all or specified interface.
+@end deffn
+
+@deffn {Command} {show ip ospf mpls-te router} {}
+Show Traffic Engineering router parameters.
+@end deffn
+
+@node Router Information
+@section Router Information
+
+@deffn {OSPF Command} {router-info [as | area <A.B.C.D>]} {}
+@deffnx {OSPF Command} {no router-info} {}
+Enable Router Information (RFC4970) LSA advertisement with AS scope (default) or Area scope flooding
+when area is specified.
+@end deffn
+
+@deffn {OSPF Command} {pce address <A.B.C.D>} {}
+@deffnx {OSPF Command} {no pce address} {}
+@deffnx {OSPF Command} {pce domain as <0-65535>} {}
+@deffnx {OSPF Command} {no pce domain as <0-65535>} {}
+@deffnx {OSPF Command} {pce neighbor as <0-65535>} {}
+@deffnx {OSPF Command} {no pce neighbor as <0-65535>} {}
+@deffnx {OSPF Command} {pce flag BITPATTERN} {}
+@deffnx {OSPF Command} {no pce flag} {}
+@deffnx {OSPF Command} {pce scope BITPATTERN} {}
+@deffnx {OSPF Command} {no pce scope} {}
+The commands are conform to RFC 5088 and allow OSPF router announce Path Compuatation Elemenent (PCE) capabilities
+through the Router Information (RI) LSA. Router Information must be enable prior to this. The command set/unset
+respectively the PCE IP adress, Autonomous System (AS) numbers of controlled domains, neighbor ASs, flag and scope.
+For flag and scope, please refer to RFC5088 for the BITPATTERN recognition. Multiple 'pce neighbor' command could
+be specified in order to specify all PCE neighbours.
+@end deffn
+
+@deffn {Command} {show ip ospf router-info} {}
+Show Router Capabilities flag.
+@end deffn
+@deffn {Command} {show ip ospf router-info pce} {}
+Show Router Capabilities PCE parameters.
+@end deffn
+
+@node Debugging OSPF
+@section Debugging OSPF
+
+@deffn {Command} {debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {}
+@deffnx {Command} {no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {}
+Dump Packet for debugging
+@end deffn
+
+@deffn {Command} {debug ospf ism} {}
+@deffnx {Command} {debug ospf ism (status|events|timers)} {}
+@deffnx {Command} {no debug ospf ism} {}
+@deffnx {Command} {no debug ospf ism (status|events|timers)} {}
+Show debug information of Interface State Machine
+@end deffn
+
+@deffn {Command} {debug ospf nsm} {}
+@deffnx {Command} {debug ospf nsm (status|events|timers)} {}
+@deffnx {Command} {no debug ospf nsm} {}
+@deffnx {Command} {no debug ospf nsm (status|events|timers)} {}
+Show debug information of Network State Machine
+@end deffn
+
+@deffn {Command} {debug ospf event} {}
+@deffnx {Command} {no debug ospf event} {}
+Show debug information of OSPF event
+@end deffn
+
+@deffn {Command} {debug ospf nssa} {}
+@deffnx {Command} {no debug ospf nssa} {}
+Show debug information about Not So Stub Area
+@end deffn
+
+@deffn {Command} {debug ospf lsa} {}
+@deffnx {Command} {debug ospf lsa (generate|flooding|refresh)} {}
+@deffnx {Command} {no debug ospf lsa} {}
+@deffnx {Command} {no debug ospf lsa (generate|flooding|refresh)} {}
+Show debug detail of Link State messages
+@end deffn
+
+@deffn {Command} {debug ospf te} {}
+@deffnx {Command} {no debug ospf te} {}
+Show debug information about Traffic Engineering LSA
+@end deffn
+
+@deffn {Command} {debug ospf zebra} {}
+@deffnx {Command} {debug ospf zebra (interface|redistribute)} {}
+@deffnx {Command} {no debug ospf zebra} {}
+@deffnx {Command} {no debug ospf zebra (interface|redistribute)} {}
+Show debug information of ZEBRA API
+@end deffn
+
+@deffn {Command} {show debugging ospf} {}
+@end deffn
+
+@node OSPF Configuration Examples
+@section OSPF Configuration Examples
+A simple example, with MD5 authentication enabled:
+
+@example
+@group
+!
+interface bge0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 1 md5 ABCDEFGHIJK
+!
+router ospf
+ network 192.168.0.0/16 area 0.0.0.1
+ area 0.0.0.1 authentication message-digest
+@end group
+@end example
+
+An @acronym{ABR} router, with MD5 authentication and performing summarisation
+of networks between the areas:
+
+@example
+@group
+!
+password ABCDEF
+log file /var/log/quagga/ospfd.log
+service advanced-vty
+!
+interface eth0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 1 md5 ABCDEFGHIJK
+!
+interface ppp0
+!
+interface br0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 2 md5 XYZ12345
+!
+router ospf
+ ospf router-id 192.168.0.1
+ redistribute connected
+ passive interface ppp0
+ network 192.168.0.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ network 192.168.1.0/24 area 0.0.0.1
+ area 0.0.0.0 authentication message-digest
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 192.168.0.0/24
+ area 0.0.0.1 authentication message-digest
+ area 0.0.0.1 range 10.2.0.0/16
+!
+@end group
+@end example
+
+A Traffic Engineering configuration, with Inter-ASv2 support.
+
+ - First, the 'zebra.conf' part:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/zebra.log
+!
+interface eth0
+ ip address 198.168.1.1/24
+ mpls-te on
+ mpls-te link metric 10
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xab
+!
+interface eth1
+ ip address 192.168.2.1/24
+ mpls-te on
+ mpls-te link metric 10
+ mpls-te link max-bw 1.25e+06
+ mpls-te link max-rsv-bw 1.25e+06
+ mpls-te link unrsv-bw 0 1.25e+06
+ mpls-te link unrsv-bw 1 1.25e+06
+ mpls-te link unrsv-bw 2 1.25e+06
+ mpls-te link unrsv-bw 3 1.25e+06
+ mpls-te link unrsv-bw 4 1.25e+06
+ mpls-te link unrsv-bw 5 1.25e+06
+ mpls-te link unrsv-bw 6 1.25e+06
+ mpls-te link unrsv-bw 7 1.25e+06
+ mpls-te link rsc-clsclr 0xab
+ mpls-te neighbor 192.168.2.2 as 65000
+@end group
+@end example
+
+ - Then the 'ospfd.conf' itself:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/ospfd.log
+!
+!
+interface eth0
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+!
+interface eth1
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+!
+!
+router ospf
+ ospf router-id 192.168.1.1
+ network 192.168.0.0/16 area 1
+ ospf opaque-lsa
+  mpls-te
+  mpls-te router-address 192.168.1.1
+  mpls-te inter-as area 1
+!
+line vty
+@end group
+@end example
+
+A router information example with PCE advsertisement:
+
+@example
+@group
+!
+router ospf
+ ospf router-id 192.168.1.1
+ network 192.168.0.0/16 area 1
+ capability opaque
+  mpls-te
+  mpls-te router-address 192.168.1.1
+ router-info area 0.0.0.1
+  pce address 192.168.1.1
+  pce flag 0x80
+  pce domain as 65400
+  pce neighbor as 65500
+  pce neighbor as 65200
+  pce scope 0x80
+!
+@end group
+@end example
diff --git a/doc/overview.texi b/doc/overview.texi
new file mode 100644 (file)
index 0000000..2301c4b
--- /dev/null
@@ -0,0 +1,337 @@
+@node Overview
+@chapter Overview
+@cindex Overview
+
+  @uref{http://www.quagga.net,,Quagga} is a routing software package that
+provides TCP/IP based routing services with routing protocols support such
+as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, IS-IS, BGP-4, and BGP-4+ (@pxref{Supported
+RFCs}). Quagga also supports special BGP Route Reflector and Route Server
+behavior.  In addition to traditional IPv4 routing protocols, Quagga also
+supports IPv6 routing protocols.  With SNMP daemon which supports SMUX and AgentX
+protocol, Quagga provides routing protocol MIBs (@pxref{SNMP Support}).
+
+  Quagga uses an advanced software architecture to provide you with a high
+quality, multi server routing engine. Quagga has an interactive user
+interface for each routing protocol and supports common client commands. 
+Due to this design, you can add new protocol daemons to Quagga easily.  You
+can use Quagga library as your program's client user interface.
+
+  Quagga is distributed under the @sc{gnu} General Public License.
+
+@menu
+* About Quagga::                Basic information about Quagga
+* System Architecture::         The Quagga system architecture
+* Supported Platforms::         Supported platforms and future plans
+* Supported RFCs::               Supported RFCs
+* How to get Quagga::            
+* Mailing List::                Mailing list information
+* Bug Reports::                 Mail address for bug data
+@end menu
+
+@node About Quagga
+@comment  node-name,  next,  previous,  up
+@section About Quagga
+@cindex About Quagga
+
+  Today, TCP/IP networks are covering all of the world.  The Internet has
+been deployed in many countries, companies, and to the home.  When you
+connect to the Internet your packet will pass many routers which have TCP/IP
+routing functionality.
+
+  A system with Quagga installed acts as a dedicated router.  With Quagga,
+your machine exchanges routing information with other routers using routing
+protocols.  Quagga uses this information to update the kernel routing table
+so that the right data goes to the right place.  You can dynamically change
+the configuration and you may view routing table information from the Quagga
+terminal interface.
+
+  Adding to routing protocol support, Quagga can setup interface's flags,
+interface's address, static routes and so on.  If you have a small network,
+or a stub network, or xDSL connection, configuring the Quagga routing
+software is very easy.  The only thing you have to do is to set up the
+interfaces and put a few commands about static routes and/or default routes. 
+If the network is rather large, or if the network structure changes
+frequently, you will want to take advantage of Quagga's dynamic routing
+protocol support for protocols such as RIP, OSPF, IS-IS or BGP.
+
+  Traditionally, UNIX based router configuration is done by
+@command{ifconfig} and @command{route} commands.  Status of routing
+table is displayed by @command{netstat} utility.  Almost of these commands
+work only if the user has root privileges.  Quagga has a different system
+administration method.  There are two user modes in Quagga.  One is normal
+mode, the other is enable mode.  Normal mode user can only view system
+status, enable mode user can change system configuration.  This UNIX account
+independent feature will be great help to the router administrator.
+
+  Currently, Quagga supports common unicast routing protocols, that is BGP,
+OSPF, RIP and IS-IS.  Upcoming for MPLS support, an implementation of LDP is
+currently being prepared for merging.  Implementations of BFD and PIM-SSM
+(IPv4) also exist, but are not actively being worked on.
+
+  The ultimate goal of the Quagga project is making a productive, quality, free
+TCP/IP routing software package.
+
+@node System Architecture
+@comment  node-name,  next,  previous,  up
+@section System Architecture
+@cindex System architecture
+@cindex Software architecture
+@cindex Software internals
+
+  Traditional routing software is made as a one process program which
+provides all of the routing protocol functionalities.  Quagga takes a
+different approach.  It is made from a collection of several daemons that
+work together to build the routing table.  There may be several
+protocol-specific routing daemons and zebra the kernel routing manager.
+
+  The @command{ripd} daemon handles the RIP protocol, while
+@command{ospfd} is a daemon which supports OSPF version 2.
+@command{bgpd} supports the BGP-4 protocol.  For changing the kernel
+routing table and for redistribution of routes between different routing
+protocols, there is a kernel routing table manager @command{zebra} daemon. 
+It is easy to add a new routing protocol daemons to the entire routing
+system without affecting any other software.  You need to run only the
+protocol daemon associated with routing protocols in use.  Thus, user may
+run a specific daemon and send routing reports to a central routing console.
+
+  There is no need for these daemons to be running on the same machine. You
+can even run several same protocol daemons on the same machine.  This
+architecture creates new possibilities for the routing system.
+
+@example
+@group
++----+  +----+  +-----+  +-----+
+|bgpd|  |ripd|  |ospfd|  |zebra|
++----+  +----+  +-----+  +-----+
+                            |
++---------------------------|--+
+|                           v  |
+|  UNIX Kernel  routing table  |
+|                              |
++------------------------------+
+
+    Quagga System Architecture
+@end group
+@end example
+
+Multi-process architecture brings extensibility, modularity and
+maintainability.  At the same time it also brings many configuration files
+and terminal interfaces.  Each daemon has it's own configuration file and
+terminal interface.  When you configure a static route, it must be done in
+@command{zebra} configuration file.  When you configure BGP network it must
+be done in @command{bgpd} configuration file.  This can be a very annoying
+thing.  To resolve the problem, Quagga provides integrated user interface
+shell called @command{vtysh}.  @command{vtysh} connects to each daemon with
+UNIX domain socket and then works as a proxy for user input.
+
+Quagga was planned to use multi-threaded mechanism when it runs with a
+kernel that supports multi-threads.  But at the moment, the thread library
+which comes with @sc{gnu}/Linux or FreeBSD has some problems with running
+reliable services such as routing software, so we don't use threads at all. 
+Instead we use the @command{select(2)} system call for multiplexing the
+events.
+
+@node Supported Platforms
+@comment  node-name,  next,  previous,  up
+@section Supported Platforms
+
+@cindex Supported platforms
+@cindex Quagga on other systems
+@cindex Compatibility with other systems
+@cindex Operating systems that support Quagga
+
+Currently Quagga supports @sc{gnu}/Linux and BSD. Porting Quagga
+to other platforms is not too difficult as platform dependent code should
+most be limited to the @command{zebra} daemon.  Protocol daemons are mostly
+platform independent. Please let us know when you find out Quagga runs on a
+platform which is not listed below.
+
+The list of officially supported platforms are listed below. Note that
+Quagga may run correctly on other platforms, and may run with partial
+functionality on further platforms.
+
+@sp 1
+@itemize @bullet
+@item
+@sc{gnu}/Linux
+@item
+FreeBSD
+@item
+NetBSD
+@item
+OpenBSD
+@end itemize
+
+Versions of these platforms that are older than around 2 years from the point
+of their original release (in case of @sc{gnu}/Linux, this is since the kernel's
+release on kernel.org) may need some work.  Similarly, the following platforms
+may work with some effort:
+
+@sp 1
+@itemize @bullet
+@item
+Solaris
+@item
+Mac OSX
+@end itemize
+
+Also note that, in particular regarding proprietary platforms, compiler
+and C library choice will affect Quagga.  Only recent versions of the
+following C compilers are well-tested:
+
+@sp 1
+@itemize @bullet
+@item
+@sc{gnu}'s GCC
+@item
+LLVM's clang
+@item
+Intel's ICC
+@end itemize
+
+@node Supported RFCs
+@comment  node-name,  next,  previous,  up
+@section Supported RFCs
+
+  Below is the list of currently supported RFC's.
+
+@table @asis
+@item @asis{RFC1058}
+@cite{Routing Information Protocol. C.L. Hedrick. Jun-01-1988.}
+
+@item @asis{RF2082}
+@cite{RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.}
+
+@item @asis{RFC2453}
+@cite{RIP Version 2. G. Malkin. November 1998.}
+
+@item @asis{RFC2080}
+@cite{RIPng for IPv6. G. Malkin, R. Minnear. January 1997.}
+
+@item @asis{RFC2328}
+@cite{OSPF Version 2. J. Moy. April 1998.}
+
+@item @asis{RFC2370}
+@cite{The OSPF Opaque LSA Option R. Coltun. July 1998.}
+
+@item @asis{RFC3101}
+@cite{The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.}
+
+@item @asis{RFC2740}
+@cite{OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.}
+
+@item @asis{RFC1771} 
+@cite{A Border Gateway Protocol 4 (BGP-4). Y. Rekhter & T. Li. March 1995.}
+
+@item @asis{RFC1965}
+@cite{Autonomous System Confederations for BGP. P. Traina. June 1996.}
+
+@item @asis{RFC1997}
+@cite{BGP Communities Attribute. R. Chandra, P. Traina & T. Li. August 1996.}
+
+@item @asis{RFC2545}
+@cite{Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing. P. Marques, F. Dupont. March 1999.}
+
+@item @asis{RFC2796}
+@cite{BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. Chandrasekeran. June 1996.}
+
+@item @asis{RFC2858}
+@cite{Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. Katz. June 2000.}
+
+@item @asis{RFC2842}
+@cite{Capabilities Advertisement with BGP-4. R. Chandra, J. Scudder. May 2000.}
+
+@item @asis{RFC3137}
+@cite{OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, D. McPherson. June 2001}
+@end table
+
+  When SNMP support is enabled, below RFC is also supported.
+
+@table @asis
+
+@item @asis{RFC1227}
+@cite{SNMP MUX protocol and MIB. M.T. Rose. May-01-1991.}
+
+@item @asis{RFC1657}
+@cite{Definitions of Managed Objects for the Fourth Version of the
+Border Gateway Protocol (BGP-4) using SMIv2. S. Willis, J. Burruss,
+J. Chu, Editor. July 1994.}
+
+@item @asis{RFC1724}
+@cite{RIP Version 2 MIB Extension. G. Malkin & F. Baker. November 1994.}
+
+@item @asis{RFC1850}
+@cite{OSPF Version 2 Management Information Base. F. Baker, R. Coltun.
+November 1995.}
+
+@item @asis{RFC2741}
+@cite{Agent Extensibility (AgentX) Protocol. M. Daniele, B. Wijnen. January 2000.}
+
+@end table
+
+@node How to get Quagga
+@comment  node-name,  next,  previous,  up
+@section How to get Quagga
+
+The official Quagga web-site is located at:
+
+@uref{http://www.quagga.net/}
+
+and contains further information, as well as links to additional
+resources. 
+
+@uref{http://www.quagga.net/,Quagga} is a fork of GNU Zebra, whose
+web-site is located at:
+
+@uref{http://www.zebra.org/}.
+
+@node Mailing List
+@comment  node-name,  next,  previous,  up
+@section Mailing List
+@cindex How to get in touch with Quagga
+@cindex Mailing Quagga
+@cindex Contact information
+@cindex Mailing lists
+
+There is a mailing list for discussions about Quagga.  If you have any
+comments or suggestions to Quagga, please subscribe to:
+
+@uref{http://lists.quagga.net/mailman/listinfo/quagga-users}.
+
+The @uref{http://www.quagga.net/,,Quagga} site has further information on
+the available mailing lists, see:
+
+       @uref{http://www.quagga.net/lists.php}
+
+@node Bug Reports
+@section Bug Reports
+
+@cindex Bug Reports
+@cindex Bug hunting
+@cindex Found a bug?
+@cindex Reporting bugs
+@cindex Reporting software errors
+@cindex Errors in the software
+
+If you think you have found a bug, please send a bug report to:
+
+@uref{http://bugzilla.quagga.net}
+
+When you send a bug report, please be careful about the points below.
+
+@itemize @bullet
+@item 
+Please note what kind of OS you are using.  If you use the IPv6 stack
+please note that as well.
+@item
+Please show us the results of @code{netstat -rn} and @code{ifconfig -a}.
+Information from zebra's VTY command @code{show ip route} will also be
+helpful.
+@item
+Please send your configuration file with the report.  If you specify
+arguments to the configure script please note that too.
+@end itemize
+
+  Bug reports are very important for us to improve the quality of Quagga.
+Quagga is still in the development stage, but please don't hesitate to
+send a bug report to @uref{http://bugzilla.quagga.net}.
diff --git a/doc/pimd.8 b/doc/pimd.8
new file mode 100644 (file)
index 0000000..0dd170a
--- /dev/null
@@ -0,0 +1,125 @@
+.TH PIM 8 "10 December 2008" "Quagga PIM daemon" "Version 0.99.11"
+.SH NAME
+pimd \- a PIM routing for use with Quagga Routing Suite.
+.SH SYNOPSIS
+.B pimd
+[
+.B \-dhvZ
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-z
+.I path
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B pimd
+is a protocol-independent multicast component that works with the
+.B Quagga
+Routing Suite.
+.SH OPTIONS
+Options available for the
+.B pimd
+command:
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/pimd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When pimd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart pimd.  The likely default is \fB\fI/var/run/pimd.pid\fR.
+.TP
+\fB\-z\fR, \fB\-\-socket \fR\fIpath\fR
+Specify the socket path for contacting the zebra daemon.
+The likely default is \fB\fI/var/run/zserv.api\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the pimd VTY will listen on. This defaults to
+2611, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the pimd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.TP
+\fB\-Z\fR, \fB\-\-debug_zclient\fR
+Enable logging information for zclient debugging.
+.SH FILES
+.TP
+.BI /usr/local/sbin/pimd
+The default location of the 
+.B pimd
+binary.
+.TP
+.BI /usr/local/etc/pimd.conf
+The default location of the 
+.B pimd
+config file.
+.TP
+.BI /var/run/pimd.pid
+The default location of the 
+.B pimd
+pid file.
+.TP
+.BI /var/run/zserv.api
+The default location of the 
+.B zebra
+unix socket file.
+.TP
+.BI $(PWD)/pimd.log 
+If the 
+.B pimd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBpimd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options.
+.SH DIAGNOSTICS
+The pimd process may log to standard output, to a VTY, to a log
+file, or through syslog to the system logs.
+.SH "SEE ALSO"
+.BR zebra (8),
+.BR vtysh (1)
+.SH BUGS
+\fBpimd\fR is in early development at the moment and is not ready for
+production use.
+
+.B pimd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI https://github.com/udhos/qpimd
+.SH AUTHORS
+See
+.BI https://github.com/udhos/qpimd
+for an accurate list of authors.
+
diff --git a/doc/protocol.texi b/doc/protocol.texi
new file mode 100644 (file)
index 0000000..602768a
--- /dev/null
@@ -0,0 +1,112 @@
+@node  Zebra Protocol
+@appendix Zebra Protocol
+@appendixsection Overview of the Zebra Protocol
+
+Zebra Protocol is used by protocol daemons to communicate with the
+zebra daemon.
+
+Each protocol daemon may request and send information to and from the
+zebra daemon such as interface states, routing state,
+nexthop-validation, and so on. Protocol daemons may also install routes
+with zebra. The zebra daemon manages which route is installed into the
+forwarding table with the kernel.
+
+Zebra Protocol is a streaming protocol, with a common header. Two
+versions of the header are in use. Version 0 is implicitely versioned.
+Version 1 has an explicit version field. Version 0 can be distinguished
+from all other versions by examining the 3rd byte of the header, which
+contains a marker value for all versions bar version 0. The marker byte
+corresponds to the command field in version 0, and the marker value is
+a reserved command in version 0.
+
+We do not anticipate there will be further versions of the header for
+the foreseeable future, as the command field in version 1 is wide
+enough to allow for future extensions to done compatibly through
+seperate commands.
+
+Version 0 is used by all versions of GNU Zebra as of this writing, and
+versions of Quagga up to and including Quagga 0.98. Version 1 will be
+used as of Quagga 1.0.
+
+@appendixsection Zebra Protocol Definition
+@appendixsubsec Zebra Protocol Header (version 0)
+@example
+@group
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-------------------------------+---------------+
+|           Length (2)          |   Command (1) |
++-------------------------------+---------------+
+@end group
+@end example
+
+@appendixsubsec Zebra Protocol Common Header (version 1)
+@example
+@group
+0                   1                   2                   3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-------------------------------+---------------+-------------+
+|           Length (2)          |   Marker (1)  | Version (1) |
++-------------------------------+---------------+-------------+
+|          Command (2)          |
++-------------------------------+
+@end group
+@end example
+
+@appendixsubsec Zebra Protocol Header Field Definitions
+@table @samp
+@item Length
+Total packet length including this header. The minimum length is 3
+bytes for version 0 messages and 6 bytes for version 1 messages.
+
+@item Marker
+Static marker with a value of 255 always. This is to allow version 0
+Zserv headers (which do not include version explicitely) to be
+distinguished from versioned headers. Not present in version 0
+messages.
+
+@item Version
+Version number of the Zserv message. Clients should not continue
+processing messages past the version field for versions they do not
+recognise. Not present in version 0 messages.
+
+@item Command
+The Zebra Protocol command.
+@end table
+
+@appendixsubsec Zebra Protocol Commands
+@multitable {ZEBRA_REDISTRIBUTE_DEFAULT_DELETE_WHATEVER} {99999}
+@headitem Command @tab Value
+@item ZEBRA_INTERFACE_ADD      
+@tab 1
+@item ZEBRA_INTERFACE_DELETE
+@tab 2
+@item ZEBRA_INTERFACE_ADDRESS_ADD
+@tab 3
+@item ZEBRA_INTERFACE_ADDRESS_DELETE
+@tab 4
+@item ZEBRA_INTERFACE_UP
+@tab 5
+@item ZEBRA_INTERFACE_DOWN
+@tab 6
+@item ZEBRA_IPV4_ROUTE_ADD
+@tab 7
+@item ZEBRA_IPV4_ROUTE_DELETE
+@tab 8
+@item ZEBRA_IPV6_ROUTE_ADD
+@tab 9
+@item ZEBRA_IPV6_ROUTE_DELETE
+@tab 10
+@item ZEBRA_REDISTRIBUTE_ADD
+@tab 11
+@item ZEBRA_REDISTRIBUTE_DELETE
+@tab 12
+@item ZEBRA_REDISTRIBUTE_DEFAULT_ADD
+@tab 13
+@item ZEBRA_REDISTRIBUTE_DEFAULT_DELETE
+@tab 14
+@item ZEBRA_IPV4_NEXTHOP_LOOKUP
+@tab 15
+@item ZEBRA_IPV6_NEXTHOP_LOOKUP
+@tab 16
+@end multitable
diff --git a/doc/quagga.texi b/doc/quagga.texi
new file mode 100644 (file)
index 0000000..13e988e
--- /dev/null
@@ -0,0 +1,144 @@
+\input texinfo @c -*- texinfo -*-
+
+@c %**start of header
+@setfilename quagga.info
+@c Set variables - sourced from defines.texi
+@include defines.texi
+@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}}
+@iftex
+@afourpaper
+@end iftex
+@c %**end of header
+
+@c automake will automatically generate version.texi
+@c and set EDITION, VERSION, UPDATED and UPDATED-MONTH
+@include version.texi
+
+@copying
+@value{COPYRIGHT_STR}
+@quotation
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation
+approved by Kunihiro Ishiguro.
+@end quotation
+@end copying
+
+@c Info entry
+@dircategory Routing Software:
+@direntry
+* @value{PACKAGE_NAME}: (quagga).              The Quagga Software Routing Suite
+@end direntry
+
+@c @smallbook
+
+@ifinfo
+This file documents the Quagga Software Routing Suite which manages common
+TCP/IP routing protocols.
+
+This is Edition @value{EDITION}, last updated @value{UPDATED} of
+@cite{The Quagga Manual}, for @uref{http://www.quagga.net/,,@value{PACKAGE_NAME}}
+Version @value{VERSION}.
+
+@insertcopying
+@end ifinfo
+
+@titlepage
+@title @uref{http://www.quagga.net,,Quagga}
+@subtitle A routing software package for TCP/IP networks
+@subtitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @value{VERSION}
+@subtitle @value{UPDATED-MONTH}
+@author @value{AUTHORS}
+
+@page
+@vskip 0pt plus 1filll
+
+@insertcopying
+@end titlepage
+@page
+
+@ifnottex
+@node Top
+@top Quagga
+                    
+@uref{http://www.quagga.net,,Quagga} is an advanced routing software package
+that provides a suite of TCP/IP based routing protocols.  This is the Manual
+for Quagga @value{VERSION}. @uref{http://www.quagga.net,,Quagga} is a fork of 
+@uref{http://www.zebra.org,,GNU Zebra}.
+
+@insertcopying
+@end ifnottex
+
+@menu
+* Overview::
+* Installation::
+* Basic commands::
+* Zebra::
+* RIP::
+* RIPng::
+* OSPFv2::
+* OSPFv3::
+* ISIS::
+* NHRP::
+* BGP::
+* Configuring Quagga as a Route Server::
+* VTY shell::
+* Filtering::
+* Route Map::
+* IPv6 Support::
+* Kernel Interface::
+* SNMP Support::
+* Zebra Protocol::
+* Packet Binary Dump Format::
+* Command Index::
+* VTY Key Index::
+* Index::
+@end menu
+@contents
+
+@include overview.texi
+@include install.texi
+@include basic.texi
+@include main.texi
+@include ripd.texi
+@include ripngd.texi
+@include ospfd.texi
+@include ospf6d.texi
+@include isisd.texi
+@include nhrpd.texi
+@include bgpd.texi
+@include routeserver.texi
+@include vtysh.texi
+@include filter.texi
+@include routemap.texi
+@include ipv6.texi
+@include kernel.texi
+@include snmp.texi
+@include protocol.texi
+@include appendix.texi
+
+@node Command Index
+@unnumbered Command Index
+
+@printindex fn
+
+@node VTY Key Index
+@unnumbered VTY Key Index
+
+@printindex ky
+
+@node Index
+@unnumbered Index
+
+@printindex cp
+@bye
diff --git a/doc/ripd.8 b/doc/ripd.8
new file mode 100644 (file)
index 0000000..8fa9bf2
--- /dev/null
@@ -0,0 +1,113 @@
+.TH RIPD 8 "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
+.B ripd
+[
+.B \-dhrv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B ripd
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B ripd
+command:
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/ripd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When ripd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart ripd.  The likely default is \fB\fI/var/run/ripd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the ripd VTY will listen on. This defaults to
+2602, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the ripd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-r\fR, \fB\-\-retain\fR 
+When the program terminates, retain routes added by \fBripd\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/ripd
+The default location of the 
+.B ripd
+binary.
+.TP
+.BI /usr/local/etc/ripd.conf
+The default location of the 
+.B ripd
+config file.
+.TP
+.BI $(PWD)/ripd.log 
+If the 
+.B ripd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBripd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The ripd process may log to standard output, to a VTY, to a log
+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 vtysh (1)
+.SH BUGS
+.B ripd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
diff --git a/doc/ripd.texi b/doc/ripd.texi
new file mode 100644 (file)
index 0000000..78d63ee
--- /dev/null
@@ -0,0 +1,623 @@
+@c -*-texinfo-*-
+@c This is part of the Quagga Manual.
+@c @value{COPYRIGHT_STR}
+@c See file quagga.texi for copying conditions.
+@node RIP
+@chapter RIP
+
+RIP -- Routing Information Protocol is widely deployed interior gateway
+protocol.  RIP was developed in the 1970s at Xerox Labs as part of the
+XNS routing protocol.  RIP is a @dfn{distance-vector} protocol and is
+based on the @dfn{Bellman-Ford} algorithms.  As a distance-vector
+protocol, RIP router send updates to its neighbors periodically, thus
+allowing the convergence to a known topology.  In each update, the
+distance to any given network will be broadcasted to its neighboring
+router.
+
+@command{ripd} supports RIP version 2 as described in RFC2453 and RIP
+version 1 as described in RFC1058.
+
+@menu
+* Starting and Stopping ripd::  
+* RIP Configuration::           
+* RIP Version Control::
+* How to Announce RIP route::   
+* Filtering RIP Routes::        
+* RIP Metric Manipulation::     
+* RIP distance::                
+* RIP route-map::               
+* RIP Authentication::          
+* RIP Timers::                  
+* Show RIP Information::        
+* RIP Debug Commands::          
+@end menu
+
+@node Starting and Stopping ripd
+@section Starting and Stopping ripd
+
+The default configuration file name of @command{ripd}'s is
+@file{ripd.conf}.  When invocation @command{ripd} searches directory
+@value{INSTALL_PREFIX_ETC}.  If @file{ripd.conf} is not there next
+search current directory.
+
+RIP uses UDP port 520 to send and receive RIP packets.  So the user must have
+the capability to bind the port, generally this means that the user must
+have superuser privileges.  RIP protocol requires interface information
+maintained by @command{zebra} daemon.  So running @command{zebra}
+is mandatory to run @command{ripd}.  Thus minimum sequence for running
+RIP is like below:
+
+@example
+@group
+# zebra -d
+# ripd -d
+@end group
+@end example
+
+Please note that @command{zebra} must be invoked before @command{ripd}.
+
+To stop @command{ripd}.  Please use @command{kill `cat
+/var/run/ripd.pid`}.  Certain signals have special meaningss to @command{ripd}.
+
+@table @samp
+@item SIGHUP
+Reload configuration file @file{ripd.conf}.  All configurations are
+reseted.  All routes learned so far are cleared and removed from routing
+table.
+@item SIGUSR1
+Rotate @command{ripd} logfile.
+@item SIGINT
+@itemx SIGTERM
+@command{ripd} sweeps all installed RIP routes then terminates properly.
+@end table
+
+@command{ripd} invocation options.  Common options that can be specified
+(@pxref{Common Invocation Options}).
+
+@table @samp
+@item -r
+@itemx --retain
+When the program terminates, retain routes added by @command{ripd}.
+@end table
+
+@menu
+* RIP netmask::                 
+@end menu
+
+@node RIP netmask
+@subsection RIP netmask
+
+The netmask features of @command{ripd} support both version 1 and version 2 of
+RIP.  Version 1 of RIP originally contained no netmask information.  In
+RIP version 1, network classes were originally used to determine the
+size of the netmask.  Class A networks use 8 bits of mask, Class B
+networks use 16 bits of masks, while Class C networks use 24 bits of
+mask.  Today, the most widely used method of a network mask is assigned
+to the packet on the basis of the interface that received the packet.
+Version 2 of RIP supports a variable length subnet mask (VLSM).  By
+extending the subnet mask, the mask can be divided and reused.  Each
+subnet can be used for different purposes such as large to middle size
+LANs and WAN links.  Quagga @command{ripd} does not support the non-sequential
+netmasks that are included in RIP Version 2.
+
+In a case of similar information with the same prefix and metric, the
+old information will be suppressed.  Ripd does not currently support
+equal cost multipath routing.
+
+
+@node RIP Configuration
+@section RIP Configuration
+
+@deffn Command {router rip} {}
+The @code{router rip} command is necessary to enable RIP.  To disable
+RIP, use the @code{no router rip} command.  RIP must be enabled before
+carrying out any of the RIP commands.
+@end deffn
+
+@deffn Command {no router rip} {}
+Disable RIP.
+@end deffn
+
+@deffn {RIP Command} {network @var{network}} {}
+@deffnx {RIP Command} {no network @var{network}} {}
+Set the RIP enable interface by @var{network}.  The interfaces which
+have addresses matching with @var{network} are enabled.
+
+This group of commands either enables or disables RIP interfaces between
+certain numbers of a specified network address.  For example, if the
+network for 10.0.0.0/24 is RIP enabled, this would result in all the
+addresses from 10.0.0.0 to 10.0.0.255 being enabled for RIP.  The @code{no
+network} command will disable RIP for the specified network.
+@end deffn
+
+@deffn {RIP Command} {network @var{ifname}} {}
+@deffnx {RIP Command} {no network @var{ifname}} {}
+Set a RIP enabled interface by @var{ifname}.  Both the sending and
+receiving of RIP packets will be enabled on the port specified in the
+@code{network ifname} command.  The @code{no network ifname} command will disable
+RIP on the specified interface.
+@end deffn
+
+@deffn {RIP Command} {neighbor @var{a.b.c.d}} {}
+@deffnx {RIP Command} {no neighbor @var{a.b.c.d}} {}
+Specify RIP neighbor.  When a neighbor doesn't understand multicast,
+this command is used to specify neighbors.  In some cases, not all
+routers will be able to understand multicasting, where packets are sent
+to a network or a group of addresses.  In a situation where a neighbor
+cannot process multicast packets, it is necessary to establish a direct
+link between routers.  The neighbor command allows the network
+administrator to specify a router as a RIP neighbor.  The @code{no
+neighbor a.b.c.d} command will disable the RIP neighbor.
+@end deffn
+
+Below is very simple RIP configuration.  Interface @code{eth0} and
+interface which address match to @code{10.0.0.0/8} are RIP enabled.
+
+@example
+@group
+!
+router rip
+ network 10.0.0.0/8
+ network eth0
+!
+@end group
+@end example
+
+Passive interface
+
+@deffn {RIP command} {passive-interface (@var{IFNAME}|default)} {}
+@deffnx {RIP command} {no passive-interface @var{IFNAME}} {}
+This command sets the specified interface to passive mode.  On passive mode
+interface, all receiving packets are processed as normal and ripd does
+not send either multicast or unicast RIP packets except to RIP neighbors
+specified with @code{neighbor} command. The interface may be specified
+as @var{default} to make ripd default to passive on all interfaces. 
+
+The default is to be passive on all interfaces.
+@end deffn
+
+RIP split-horizon
+
+@deffn {Interface command} {ip split-horizon} {}
+@deffnx {Interface command} {no ip split-horizon} {}
+Control split-horizon on the interface.  Default is @code{ip
+split-horizon}.  If you don't perform split-horizon on the interface,
+please specify @code{no ip split-horizon}.
+@end deffn
+
+@node RIP Version Control
+@section RIP Version Control
+
+RIP can be configured to send either Version 1 or Version 2 packets.
+The default is to send RIPv2 while accepting both RIPv1 and RIPv2 (and
+replying with packets of the appropriate version for REQUESTS /
+triggered updates). The version to receive and send can be specified
+globally, and further overriden on a per-interface basis if needs be
+for send and receive seperately (see below).
+
+It is important to note that RIPv1 can not be authenticated. Further,
+if RIPv1 is enabled then RIP will reply to REQUEST packets, sending the
+state of its RIP routing table to any remote routers that ask on
+demand. For a more detailed discussion on the security implications of
+RIPv1 see @ref{RIP Authentication}.
+
+@deffn {RIP Command} {version @var{version}} {}
+Set RIP version to accept for reads and send.  @var{version}
+can be either `1'' or `2''. 
+
+Disabling RIPv1 by specifying version 2 is STRONGLY encouraged,
+@xref{RIP Authentication}. This may become the default in a future
+release.
+
+Default: Send Version 2, and accept either version.
+@end deffn
+
+@deffn {RIP Command} {no version} {}
+Reset the global version setting back to the default.
+@end deffn
+
+@deffn {Interface command} {ip rip send version @var{version}} {}
+@var{version} can be `1', `2' or `1 2'.
+
+This interface command overrides the global rip version setting, and
+selects which version of RIP to send packets with, for this interface
+specifically. Choice of RIP Version 1, RIP Version 2, or both versions. 
+In the latter case, where `1 2' is specified, packets will be both
+broadcast and multicast.
+
+Default: Send packets according to the global version (version 2)
+@end deffn
+
+@deffn {Interface command} {ip rip receive version @var{version}} {}
+@var{version} can be `1', `2' or `1 2'.
+
+This interface command overrides the global rip version setting, and
+selects which versions of RIP packets will be accepted on this
+interface. Choice of RIP Version 1, RIP Version 2, or both.
+
+Default: Accept packets according to the global setting (both 1 and 2).
+@end deffn
+
+@node How to Announce RIP route
+@section How to Announce RIP route
+
+@deffn {RIP command} {redistribute kernel} {}
+@deffnx {RIP command} {redistribute kernel metric <0-16>} {}
+@deffnx {RIP command} {redistribute kernel route-map @var{route-map}} {}
+@deffnx {RIP command} {no redistribute kernel} {}
+@code{redistribute kernel} redistributes routing information from
+kernel route entries into the RIP tables. @code{no redistribute kernel}
+disables the routes.
+@end deffn
+
+@deffn {RIP command} {redistribute static} {}
+@deffnx {RIP command} {redistribute static metric <0-16>} {}
+@deffnx {RIP command} {redistribute static route-map @var{route-map}} {}
+@deffnx {RIP command} {no redistribute static} {}
+@code{redistribute static} redistributes routing information from
+static route entries into the RIP tables. @code{no redistribute static}
+disables the routes.
+@end deffn
+
+@deffn {RIP command} {redistribute connected} {}
+@deffnx {RIP command} {redistribute connected metric <0-16>} {}
+@deffnx {RIP command} {redistribute connected route-map @var{route-map}} {}
+@deffnx {RIP command} {no redistribute connected} {}
+Redistribute connected routes into the RIP tables.  @code{no
+redistribute connected} disables the connected routes in the RIP tables.
+This command redistribute connected of the interface which RIP disabled.
+The connected route on RIP enabled interface is announced by default.
+@end deffn
+
+@deffn {RIP command} {redistribute ospf} {}
+@deffnx {RIP command} {redistribute ospf metric <0-16>} {}
+@deffnx {RIP command} {redistribute ospf route-map @var{route-map}} {}
+@deffnx {RIP command} {no redistribute ospf} {}
+@code{redistribute ospf} redistributes routing information from
+ospf route entries into the RIP tables. @code{no redistribute ospf}
+disables the routes.
+@end deffn
+
+@deffn {RIP command} {redistribute bgp} {}
+@deffnx {RIP command} {redistribute bgp metric <0-16>} {}
+@deffnx {RIP command} {redistribute bgp route-map @var{route-map}} {}
+@deffnx {RIP command} {no redistribute bgp} {}
+@code{redistribute bgp} redistributes routing information from
+bgp route entries into the RIP tables. @code{no redistribute bgp}
+disables the routes.
+@end deffn
+
+If you want to specify RIP only static routes:
+
+@deffn {RIP command} {default-information originate} {}
+@end deffn
+
+@deffn {RIP command} {route @var{a.b.c.d/m}} {}
+@deffnx {RIP command} {no route @var{a.b.c.d/m}} {}
+This command is specific to Quagga.  The @code{route} command makes a static
+route only inside RIP. This command should be used only by advanced
+users who are particularly knowledgeable about the RIP protocol.  In
+most cases, we recommend creating a static route in Quagga and
+redistributing it in RIP using @code{redistribute static}.
+@end deffn
+
+@node  Filtering RIP Routes
+@section Filtering RIP Routes
+
+RIP routes can be filtered by a distribute-list.
+
+@deffn Command {distribute-list @var{access_list} @var{direct} @var{ifname}} {}
+You can apply access lists to the interface with a @code{distribute-list}
+command.  @var{access_list} is the access list name.  @var{direct} is
+@samp{in} or @samp{out}.  If @var{direct} is @samp{in} the access list
+is applied to input packets.
+
+The @code{distribute-list} command can be used to filter the RIP path.
+@code{distribute-list} can apply access-lists to a chosen interface.
+First, one should specify the access-list.  Next, the name of the
+access-list is used in the distribute-list command.  For example, in the
+following configuration @samp{eth0} will permit only the paths that
+match the route 10.0.0.0/8
+
+@example
+@group
+!
+router rip
+ distribute-list private in eth0
+!
+access-list private permit 10 10.0.0.0/8
+access-list private deny any
+!
+@end group
+@end example
+@end deffn
+
+@code{distribute-list} can be applied to both incoming and outgoing data.
+
+@deffn Command {distribute-list prefix @var{prefix_list} (in|out) @var{ifname}} {}
+You can apply prefix lists to the interface with a
+@code{distribute-list} command.  @var{prefix_list} is the prefix list
+name.  Next is the direction of @samp{in} or @samp{out}.  If
+@var{direct} is @samp{in} the access list is applied to input packets.
+@end deffn
+
+@node RIP Metric Manipulation
+@section RIP Metric Manipulation
+
+RIP metric is a value for distance for the network.  Usually
+@command{ripd} increment the metric when the network information is
+received.  Redistributed routes' metric is set to 1.
+
+@deffn {RIP command} {default-metric <1-16>} {}
+@deffnx {RIP command} {no default-metric <1-16>} {}
+This command modifies the default metric value for redistributed routes.  The
+default value is 1.  This command does not affect connected route
+even if it is redistributed by @command{redistribute connected}.  To modify
+connected route's metric value, please use @command{redistribute
+connected metric} or @command{route-map}.  @command{offset-list} also
+affects connected routes.
+@end deffn
+
+@deffn {RIP command} {offset-list @var{access-list} (in|out)} {}
+@deffnx {RIP command} {offset-list @var{access-list} (in|out) @var{ifname}} {}
+@end deffn
+
+@node RIP distance
+@section RIP distance
+
+Distance value is used in zebra daemon.  Default RIP distance is 120.
+
+@deffn {RIP command} {distance <1-255>} {}
+@deffnx {RIP command} {no distance <1-255>} {}
+Set default RIP distance to specified value.
+@end deffn
+
+@deffn {RIP command} {distance <1-255> @var{A.B.C.D/M}} {}
+@deffnx {RIP command} {no distance <1-255> @var{A.B.C.D/M}} {}
+Set default RIP distance to specified value when the route's source IP
+address matches the specified prefix.
+@end deffn
+
+@deffn {RIP command} {distance <1-255> @var{A.B.C.D/M} @var{access-list}} {}
+@deffnx {RIP command} {no distance <1-255> @var{A.B.C.D/M} @var{access-list}} {}
+Set default RIP distance to specified value when the route's source IP
+address matches the specified prefix and the specified access-list.
+@end deffn
+
+@node RIP route-map
+@section RIP route-map
+
+Usage of @command{ripd}'s route-map support.
+
+Optional argument route-map MAP_NAME can be added to each @code{redistribute}
+statement.
+
+@example
+redistribute static [route-map MAP_NAME]
+redistribute connected [route-map MAP_NAME]
+.....
+@end example
+
+Cisco applies route-map _before_ routes will exported to rip route table. 
+In current Quagga's test implementation, @command{ripd} applies route-map
+after routes are listed in the route table and before routes will be
+announced to an interface (something like output filter). I think it is not
+so clear, but it is draft and it may be changed at future.
+
+Route-map statement (@pxref{Route Map}) is needed to use route-map
+functionality.
+
+@deffn {Route Map} {match interface @var{word}} {}
+This command match to incoming interface.  Notation of this match is
+different from Cisco. Cisco uses a list of interfaces - NAME1 NAME2
+... NAMEN.  Ripd allows only one name (maybe will change in the
+future).  Next - Cisco means interface which includes next-hop of
+routes (it is somewhat similar to "ip next-hop" statement).  Ripd
+means interface where this route will be sent. This difference is
+because "next-hop" of same routes which sends to different interfaces
+must be different. Maybe it'd be better to made new matches - say
+"match interface-out NAME" or something like that.
+@end deffn
+
+@deffn {Route Map} {match ip address @var{word}} {}
+@deffnx {Route Map} {match ip address prefix-list @var{word}} {}
+Match if route destination is permitted by access-list.
+@end deffn
+
+@deffn {Route Map} {match ip next-hop @var{word}} {}
+@deffnx {Route Map} {match ip next-hop prefix-list @var{word}} {}
+Match if route next-hop (meaning next-hop listed in the rip route-table
+as displayed by "show ip rip") is permitted by access-list.
+@end deffn
+
+@deffn {Route Map} {match metric <0-4294967295>} {}
+This command match to the metric value of RIP updates.  For other
+protocol compatibility metric range is shown as <0-4294967295>.  But
+for RIP protocol only the value range <0-16> make sense.
+@end deffn
+
+@deffn {Route Map} {set ip next-hop A.B.C.D} {}
+This command set next hop value in RIPv2 protocol.  This command does
+not affect RIPv1 because there is no next hop field in the packet.
+@end deffn
+
+@deffn {Route Map} {set metric <0-4294967295>} {}
+Set a metric for matched route when sending announcement.  The metric
+value range is very large for compatibility with other protocols.  For
+RIP, valid metric values are from 1 to 16.
+@end deffn
+
+@node RIP Authentication
+@section RIP Authentication
+
+RIPv2 allows packets to be authenticated via either an insecure plain
+text password, included with the packet, or via a more secure MD5 based
+@acronym{HMAC, keyed-Hashing for Message AuthentiCation},
+RIPv1 can not be authenticated at all, thus when authentication is
+configured @code{ripd} will discard routing updates received via RIPv1
+packets.
+
+However, unless RIPv1 reception is disabled entirely, 
+@xref{RIP Version Control}, RIPv1 REQUEST packets which are received,
+which query the router for routing information, will still be honoured
+by @code{ripd}, and @code{ripd} WILL reply to such packets. This allows 
+@code{ripd} to honour such REQUESTs (which sometimes is used by old
+equipment and very simple devices to bootstrap their default route),
+while still providing security for route updates which are received.
+
+In short: Enabling authentication prevents routes being updated by
+unauthenticated remote routers, but still can allow routes (I.e. the
+entire RIP routing table) to be queried remotely, potentially by anyone
+on the internet, via RIPv1.
+
+To prevent such unauthenticated querying of routes disable RIPv1,
+@xref{RIP Version Control}.
+
+@deffn {Interface command} {ip rip authentication mode md5} {}
+@deffnx {Interface command} {no ip rip authentication mode md5} {}
+Set the interface with RIPv2 MD5 authentication.
+@end deffn
+
+@deffn {Interface command} {ip rip authentication mode text} {}
+@deffnx {Interface command} {no ip rip authentication mode text} {}
+Set the interface with RIPv2 simple password authentication.
+@end deffn
+
+@deffn {Interface command} {ip rip authentication string @var{string}} {}
+@deffnx {Interface command} {no ip rip authentication string @var{string}} {}
+RIP version 2 has simple text authentication.  This command sets
+authentication string.  The string must be shorter than 16 characters.
+@end deffn
+
+@deffn {Interface command} {ip rip authentication key-chain @var{key-chain}} {}
+@deffnx {Interface command} {no ip rip authentication key-chain @var{key-chain}} {}
+Specifiy Keyed MD5 chain.
+@end deffn
+
+@example
+!
+key chain test
+ key 1
+  key-string test
+!
+interface eth1
+ ip rip authentication mode md5
+ ip rip authentication key-chain test
+!
+@end example
+
+@node RIP Timers
+@section RIP Timers
+
+@deffn {RIP command} {timers basic @var{update} @var{timeout} @var{garbage}} {}
+
+RIP protocol has several timers.  User can configure those timers' values
+by @code{timers basic} command.
+
+The default settings for the timers are as follows: 
+
+@itemize @bullet 
+@item
+The update timer is 30 seconds. Every update timer seconds, the RIP
+process is awakened to send an unsolicited Response message containing
+the complete routing table to all neighboring RIP routers.
+
+@item
+The timeout timer is 180 seconds. Upon expiration of the timeout, the
+route is no longer valid; however, it is retained in the routing table
+for a short time so that neighbors can be notified that the route has
+been dropped.
+
+@item
+The garbage collect timer is 120 seconds.  Upon expiration of the
+garbage-collection timer, the route is finally removed from the routing
+table.
+
+@end itemize
+
+The @code{timers basic} command allows the the default values of the timers
+listed above to be changed.
+@end deffn
+
+@deffn {RIP command} {no timers basic} {}
+The @code{no timers basic} command will reset the timers to the default
+settings listed above.
+@end deffn
+
+@node Show RIP Information
+@section Show RIP Information
+
+To display RIP routes.
+
+@deffn Command {show ip rip} {}
+Show RIP routes.
+@end deffn
+
+The command displays all RIP routes. For routes that are received
+through RIP, this command will display the time the packet was sent and
+the tag information.  This command will also display this information
+for routes redistributed into RIP.
+
+@c Exmaple here.
+
+@deffn Command {show ip rip status} {}
+The command displays current RIP status.  It includes RIP timer,
+filtering, version, RIP enabled interface and RIP peer inforation.
+@end deffn
+
+@example
+@group
+ripd> @b{show ip rip status}
+Routing Protocol is "rip"
+  Sending updates every 30 seconds with +/-50%, next due in 35 seconds
+  Timeout after 180 seconds, garbage collect after 120 seconds
+  Outgoing update filter list for all interface is not set
+  Incoming update filter list for all interface is not set
+  Default redistribution metric is 1
+  Redistributing: kernel connected
+  Default version control: send version 2, receive version 2 
+    Interface        Send  Recv
+  Routing for Networks:
+    eth0
+    eth1
+    1.1.1.1
+    203.181.89.241
+  Routing Information Sources:
+    Gateway          BadPackets BadRoutes  Distance Last Update
+@end group
+@end example
+
+@node RIP Debug Commands
+@section RIP Debug Commands
+
+Debug for RIP protocol.
+
+@deffn Command {debug rip events} {}
+Debug rip events.
+@end deffn
+
+@code{debug rip} will show RIP events.  Sending and receiving
+packets, timers, and changes in interfaces are events shown with @command{ripd}.
+
+@deffn Command {debug rip packet} {}
+Debug rip packet.
+@end deffn
+
+@code{debug rip packet} will display detailed information about the RIP
+packets.  The origin and port number of the packet as well as a packet
+dump is shown.
+
+@deffn Command {debug rip zebra} {}
+Debug rip between zebra communication.
+@end deffn
+
+This command will show the communication between @command{ripd} and
+@command{zebra}.  The main information will include addition and deletion of
+paths to the kernel and the sending and receiving of interface information.
+
+@deffn Command {show debugging rip} {}
+Display @command{ripd}'s debugging option.
+@end deffn
+
+@code{show debugging rip} will show all information currently set for ripd
+debug.
diff --git a/doc/ripngd.8 b/doc/ripngd.8
new file mode 100644 (file)
index 0000000..6e63dc2
--- /dev/null
@@ -0,0 +1,114 @@
+.TH RIPNGD 8 "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
+.B ripngd
+[
+.B \-dhlrv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B ripngd
+is a routing component that works with the
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B ripngd
+command:
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR 
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/ripngd.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When ripngd starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart ripngd.  The likely default is \fB\fI/var/run/ripngd.pid\fR.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the ripngd VTY will listen on. This defaults to
+2603, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the ripngd VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-r\fR, \fB\-\-retain\fR 
+When the program terminates, retain routes added by \fBripd\fR.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/ripngd
+The default location of the 
+.B ripngd
+binary.
+.TP
+.BI /usr/local/etc/ripngd.conf
+The default location of the 
+.B ripngd
+config file.
+.TP
+.BI $(PWD)/ripngd.log 
+If the 
+.B ripngd
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBripngd\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The ripngd process may log to standard output, to a VTY, to a log
+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 vtysh (1)
+.SH BUGS
+.B ripngd
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/ripngd.texi b/doc/ripngd.texi
new file mode 100644 (file)
index 0000000..0e58de6
--- /dev/null
@@ -0,0 +1,84 @@
+@c -*-texinfo-*-
+@c This is part of the Quagga Manual.
+@c @value{COPYRIGHT_STR}
+@c See file quagga.texi for copying conditions.
+@node RIPng
+@chapter RIPng
+
+@command{ripngd} supports the RIPng protocol as described in RFC2080.  It's an
+IPv6 reincarnation of the RIP protocol.
+
+@menu
+* Invoking ripngd::             
+* ripngd Configuration::        
+* ripngd Terminal Mode Commands::  
+* ripngd Filtering Commands::   
+@end menu
+
+@node Invoking ripngd
+@section Invoking ripngd
+
+There are no @code{ripngd} specific invocation options.  Common options
+can be specified (@pxref{Common Invocation Options}).
+
+@node ripngd Configuration
+@section ripngd Configuration
+
+Currently ripngd supports the following commands:
+
+@deffn Command {router ripng} {}
+Enable RIPng.
+@end deffn
+
+@deffn {RIPng Command} {flush_timer @var{time}} {}
+Set flush timer.
+@end deffn
+
+@deffn {RIPng Command} {network @var{network}} {}
+Set RIPng enabled interface by @var{network}
+@end deffn
+
+@deffn {RIPng Command} {network @var{ifname}} {}
+Set RIPng enabled interface by @var{ifname}
+@end deffn
+
+@deffn {RIPng Command} {route @var{network}} {}
+Set RIPng static routing announcement of @var{network}.
+@end deffn
+
+@deffn Command {router zebra} {}
+This command is the default and does not appear in the configuration.
+With this statement, RIPng routes go to the @command{zebra} daemon.
+@end deffn
+
+@node ripngd Terminal Mode Commands
+@section ripngd Terminal Mode Commands
+
+@deffn Command {show ip ripng} {}
+@end deffn
+
+@deffn Command {show debugging ripng} {}
+@end deffn
+
+@deffn Command {debug ripng events} {}
+@end deffn
+
+@deffn Command {debug ripng packet} {}
+@end deffn
+
+@deffn Command {debug ripng zebra} {}
+@end deffn
+
+@node ripngd Filtering Commands
+@section ripngd Filtering Commands
+
+@deffn Command {distribute-list @var{access_list} (in|out) @var{ifname}} {}
+You can apply an access-list to the interface using the
+@code{distribute-list} command.  @var{access_list} is an access-list
+name.  @var{direct} is @samp{in} or @samp{out}.  If @var{direct} is
+@samp{in}, the access-list is applied only to incoming packets.
+
+@example
+distribute-list local-only out sit1
+@end example
+@end deffn
diff --git a/doc/routemap.texi b/doc/routemap.texi
new file mode 100644 (file)
index 0000000..b3ef7ca
--- /dev/null
@@ -0,0 +1,236 @@
+@node Route Map
+@chapter Route Map
+
+Route maps provide a means to both filter and/or apply actions to
+route, hence allowing policy to be applied to routes.
+
+@menu
+* Route Map Command::           
+* Route Map Match Command::     
+* Route Map Set Command::
+* Route Map Call Command::
+* Route Map Exit Action Command::
+* Route Map Examples::
+@end menu
+
+Route-maps are an ordered list of route-map entries. Each entry may
+specify up to four distincts sets of clauses:
+
+@table @samp
+@item Matching Policy
+
+This specifies the policy implied if the @samp{Matching Conditions} are
+met or not met, and which actions of the route-map are to be taken, if
+any. The two possibilities are:
+
+@itemize @minus
+@item
+@samp{permit}: If the entry matches, then carry out the @samp{Set
+Actions}. Then finish processing the route-map, permitting the route,
+unless an @samp{Exit Action} indicates otherwise.
+
+@item
+@samp{deny}: If the entry matches, then finish processing the route-map and
+deny the route (return @samp{deny}).
+@end itemize
+
+The @samp{Matching Policy} is specified as part of the command which
+defines the ordered entry in the route-map. See below.
+
+@item Matching Conditions
+
+A route-map entry may, optionally, specify one or more conditions which
+must be matched if the entry is to be considered further, as governed
+by the Match Policy. If a route-map entry does not explicitely specify
+any matching conditions, then it always matches.
+
+@item Set Actions
+
+A route-map entry may, optionally, specify one or more @samp{Set
+Actions} to set or modify attributes of the route.
+
+@item Call Action
+
+Call to another route-map, after any @samp{Set Actions} have been
+carried out. If the route-map called returns @samp{deny} then
+processing of the route-map finishes and the route is denied,
+regardless of the @samp{Matching Policy} or the @samp{Exit Policy}. If
+the called route-map returns @samp{permit}, then @samp{Matching Policy}
+and @samp{Exit Policy} govern further behaviour, as normal.
+
+@item Exit Policy
+
+An entry may, optionally, specify an alternative @samp{Exit Policy} to
+take if the entry matched, rather than the normal policy of exiting the
+route-map and permitting the route. The two possibilities are:
+
+@itemize @minus 
+@item
+@samp{next}: Continue on with processing of the route-map entries.
+
+@item
+@samp{goto N}: Jump ahead to the first route-map entry whose order in
+the route-map is >= N. Jumping to a previous entry is not permitted.
+@end itemize
+@end table
+
+The default action of a route-map, if no entries match, is to deny.
+I.e. a route-map essentially has as its last entry an empty @samp{deny}
+entry, which matches all routes. To change this behaviour, one must
+specify an empty @samp{permit} entry as the last entry in the route-map.
+
+To summarise the above:
+
+@multitable {permit} {action} {No Match}
+@headitem           @tab Match  @tab No Match
+@item @emph{Permit} @tab action @tab cont
+@item @emph{Deny}   @tab deny   @tab cont
+@end multitable
+
+@table @samp
+
+@item action
+@itemize @minus
+@item
+Apply @emph{set} statements
+
+@item
+If @emph{call} is present, call given route-map. If that returns a @samp{deny}, finish
+processing and return @samp{deny}.
+
+@item
+If @samp{Exit Policy} is @emph{next}, goto next route-map entry
+
+@item
+If @samp{Exit Policy} is @emph{goto}, goto first entry whose order in the list
+is >= the given order.
+
+@item
+Finish processing the route-map and permit the route.
+@end itemize
+
+@item deny
+@itemize @minus
+@item
+The route is denied by the route-map (return @samp{deny}).
+@end itemize
+
+@item cont
+@itemize @minus
+@item
+goto next route-map entry
+@end itemize
+@end table
+
+@node Route Map Command
+@section Route Map Command
+
+@deffn {Command} {route-map @var{route-map-name} (permit|deny) @var{order}} {}
+
+Configure the @var{order}'th entry in @var{route-map-name} with
+@samp{Match Policy} of either @emph{permit} or @emph{deny}.
+
+@end deffn
+
+@node Route Map Match Command
+@section Route Map Match Command
+
+@deffn {Route-map Command} {match ip address @var{access_list}} {}
+Matches the specified @var{access_list}
+@end deffn
+
+@deffn {Route-map Command} {match ip next-hop @var{ipv4_addr}} {}
+Matches the specified @var{ipv4_addr}.
+@end deffn
+
+@deffn {Route-map Command} {match aspath @var{as_path}} {}
+Matches the specified @var{as_path}.
+@end deffn
+
+@deffn {Route-map Command} {match metric @var{metric}} {}
+Matches the specified @var{metric}.
+@end deffn
+
+@deffn {Route-map Command} {match local-preference @var{metric}} {}
+Matches the specified @var{local-preference}.
+@end deffn
+
+@deffn {Route-map Command} {match community @var{community_list}} {}
+Matches the specified  @var{community_list}
+@end deffn
+
+@node Route Map Set Command
+@section Route Map Set Command
+
+@deffn {Route-map Command} {set ip next-hop @var{ipv4_address}} {}
+Set the BGP nexthop address.
+@end deffn
+
+@deffn {Route-map Command} {set local-preference @var{local_pref}} {}
+Set the BGP local preference.
+@end deffn
+
+@deffn {Route-map Command} {set weight @var{weight}} {}
+Set the route's weight.
+@end deffn
+
+@deffn {Route-map Command} {set metric @var{metric}} {}
+@anchor{routemap set metric}
+Set the BGP attribute MED.
+@end deffn
+
+@deffn {Route-map Command} {set as-path prepend @var{as_path}} {}
+Set the BGP AS path to prepend.
+@end deffn
+
+@deffn {Route-map Command} {set community @var{community}} {}
+Set the BGP community attribute.
+@end deffn
+
+@deffn {Route-map Command} {set ipv6 next-hop global @var{ipv6_address}} {}
+Set the BGP-4+ global IPv6 nexthop address.
+@end deffn
+
+@deffn {Route-map Command} {set ipv6 next-hop local @var{ipv6_address}} {}
+Set the BGP-4+ link local IPv6 nexthop address.
+@end deffn
+
+@node Route Map Call Command
+@section Route Map Call Command
+
+@deffn {Route-map Command} {call @var{name}} {}
+Call route-map @var{name}. If it returns deny, deny the route and
+finish processing the route-map.
+@end deffn
+
+@node Route Map Exit Action Command
+@section Route Map Exit Action Command
+
+@deffn {Route-map Command} {on-match next} {}
+@deffnx {Route-map Command} {continue} {}
+Proceed on to the next entry in the route-map.
+@end deffn
+
+@deffn {Route-map Command} {on-match goto @var{N}} {}
+@deffnx {Route-map Command} {continue @var{N}} {}
+Proceed processing the route-map at the first entry whose order is >= N
+@end deffn
+
+@node Route Map Examples
+@section Route Map Examples
+
+A simple example of a route-map:
+
+@example
+@group
+route-map test permit 10
+ match ip address 10
+ set local-preference 200
+@end group
+@end example
+
+This means that if a route matches ip access-list number 10 it's
+local-preference value is set to 200.
+
+See @ref{BGP Configuration Examples} for examples of more sophisticated
+useage of route-maps, including of the @samp{call} action.
diff --git a/doc/routeserver.texi b/doc/routeserver.texi
new file mode 100644 (file)
index 0000000..cfe9041
--- /dev/null
@@ -0,0 +1,550 @@
+@c -*-texinfo-*-
+@c @value{COPYRIGHT_STR}
+@c See file quagga.texi for copying conditions.
+@c
+@c This file is a modified version of Jose Luis Rubio's TeX sources 
+@c of his RS-Manual document
+
+@node Configuring Quagga as a Route Server
+@chapter Configuring Quagga as a Route Server
+
+The purpose of a Route Server is to centralize the peerings between BGP
+speakers. For example if we have an exchange point scenario with four BGP
+speakers, each of which maintaining a BGP peering with the other three
+we can convert it into a centralized scenario where
+each of the four establishes a single BGP peering against the Route Server.
+
+We will first describe briefly the Route Server model implemented by Quagga.
+We will explain the commands that have been added for configuring that
+model. And finally we will show a full example of Quagga configured as Route
+Server.
+
+@menu
+* Description of the Route Server model::
+* Commands for configuring a Route Server::
+* Example of Route Server Configuration::
+@end menu
+
+@node Description of the Route Server model
+@section Description of the Route Server model
+
+First we are going to describe the normal processing that BGP announcements
+suffer inside a standard BGP speaker, as shown in @ref{fig:normal-processing},
+it consists of three steps:
+
+@itemize @bullet
+@item
+When an announcement is received from some peer, the `In' filters
+configured for that peer are applied to the announcement. These filters can
+reject the announcement, accept it unmodified, or accept it with some of its
+attributes modified.
+
+@item
+The announcements that pass the `In' filters go into the
+Best Path Selection process, where they are compared to other
+announcements referred to the same destination that have been
+received from different peers (in case such other
+announcements exist). For each different destination, the announcement
+which is selected as the best is inserted into the BGP speaker's Loc-RIB.
+
+@item
+The routes which are inserted in the Loc-RIB are
+considered for announcement to all the peers (except the one
+from which the route came). This is done by passing the routes
+in the Loc-RIB through the `Out' filters corresponding to each
+peer. These filters can reject the route,
+accept it unmodified, or accept it with some of its attributes
+modified. Those routes which are accepted by the `Out' filters
+of a peer are announced to that peer.
+@end itemize
+
+@float Figure,fig:normal-processing
+@center @image{fig-normal-processing,400pt,,Normal announcement processing}
+@caption{Announcement processing inside a ``normal'' BGP speaker}
+@end float
+
+Of course we want that the routing tables obtained in each of the routers
+are the same when using the route server than when not. But as a consequence
+of having a single BGP peering (against the route server), the BGP speakers
+can no longer distinguish from/to which peer each announce comes/goes.
+@anchor{filter-delegation}This means that the routers connected to the route
+server are not able to apply by themselves the same input/output filters
+as in the full mesh scenario, so they have to delegate those functions to
+the route server.
+
+Even more, the ``best path'' selection must be also performed inside
+the route server on behalf of its clients. The reason is that if, after
+applying the filters of the announcer and the (potential) receiver, the
+route server decides to send to some client two or more different
+announcements referred to the same destination, the client will only
+retain the last one, considering it as an implicit withdrawal of the
+previous announcements for the same destination. This is the expected
+behavior of a BGP speaker as defined in @cite{RFC1771}, and even though
+there are some proposals of mechanisms that permit multiple paths for
+the same destination to be sent through a single BGP peering, none are
+currently supported by most existing BGP implementations.
+
+As a consequence a route server must maintain additional information and
+perform additional tasks for a RS-client that those necessary for common BGP
+peerings. Essentially a route server must:
+
+@anchor{Route Server tasks}
+@itemize @bullet
+@item
+Maintain a separated Routing Information Base (Loc-RIB)
+for each peer configured as RS-client, containing the routes
+selected as a result of the ``Best Path Selection'' process
+that is performed on behalf of that RS-client.
+
+@item
+Whenever it receives an announcement from a RS-client,
+it must consider it for the Loc-RIBs of the other RS-clients.
+
+@anchor{Route-server path filter process}
+@itemize @bullet
+@item
+This means that for each of them the route server must pass the
+announcement through the appropriate `Out' filter of the
+announcer.
+
+@item
+Then through the  appropriate `In' filter of
+the potential  receiver. 
+
+@item
+Only if the announcement is accepted by both filters it will be passed
+to the ``Best Path Selection'' process.
+
+@item
+Finally, it might go into the Loc-RIB of the receiver.
+@end itemize
+@end itemize
+
+When we talk about the ``appropriate'' filter, both the announcer and the
+receiver of the route must be taken into account. Suppose that the route
+server receives an announcement from client A, and the route server is
+considering it for the Loc-RIB of client B. The filters that should be
+applied are the same that would be used in the full mesh scenario, i.e.,
+first the `Out' filter of router A for announcements going to router B, and
+then the `In' filter of router B for announcements coming from router A.
+
+We call ``Export Policy'' of a RS-client to the set of `Out' filters that
+the client would use if there was no route server. The same applies for the
+``Import Policy'' of a RS-client and the set of `In' filters of the client
+if there was no route server.
+
+It is also common to demand from a route server that it does not
+modify some BGP attributes (next-hop, as-path and MED) that are usually
+modified by standard BGP speakers before announcing a route.
+
+The announcement processing model implemented by Quagga is shown in
+@ref{fig:rs-processing}. The figure shows a mixture of RS-clients (B, C and D)
+with normal BGP peers (A). There are some details that worth additional
+comments:
+
+@itemize @bullet
+@item
+Announcements coming from a normal BGP peer are also
+considered for the Loc-RIBs of all the RS-clients. But
+logically they do not pass through any export policy.
+
+@item
+Those peers that are configured as RS-clients do not
+receive any announce from the `Main' Loc-RIB.
+
+@item
+Apart from import and export policies,
+`In' and `Out' filters can also be set for RS-clients. `In'
+filters might be useful when the route server has also normal
+BGP peers. On the other hand, `Out' filters for RS-clients are
+probably unnecessary, but we decided not to remove them as
+they do not hurt anybody (they can always be left empty).
+@end itemize
+
+@float Figure,fig:rs-processing
+@center @image{fig-rs-processing,430pt,,Route Server Processing Model}
+@caption{Announcement processing model implemented by the Route Server}
+@end float
+
+@node Commands for configuring a Route Server
+@section Commands for configuring a Route Server
+
+Now we will describe the commands that have been added to quagga
+in order to support the route server features.
+
+@deffn {Route-Server} {neighbor @var{peer-group} route-server-client} {}
+@deffnx {Route-Server} {neighbor @var{A.B.C.D} route-server-client} {}
+@deffnx {Route-Server} {neighbor @var{X:X::X:X} route-server-client} {}
+This command configures the peer given by @var{peer}, @var{A.B.C.D} or
+@var{X:X::X:X} as an RS-client.
+
+Actually this command is not new, it already existed in standard Quagga. It
+enables the transparent mode for the specified peer. This means that some
+BGP attributes (as-path, next-hop and MED) of the routes announced to that
+peer are not modified.
+
+With the route server patch, this command, apart from setting the
+transparent mode, creates a new Loc-RIB dedicated to the specified peer
+(those named `Loc-RIB for X' in @ref{fig:rs-processing}.). Starting from
+that moment, every announcement received by the route server will be also
+considered for the new Loc-RIB.
+@end deffn
+
+@deffn {Route-Server} {neigbor @{A.B.C.D|X.X::X.X|peer-group@} route-map WORD @{import|export@}} {}
+This set of commands can be used to specify the route-map that
+represents the Import or Export policy of a peer which is
+configured as a RS-client (with the previous command).
+@end deffn
+
+@deffn {Route-Server} {match peer @{A.B.C.D|X:X::X:X@}} {}
+This is a new @emph{match} statement for use in route-maps, enabling them to
+describe import/export policies. As we said before, an import/export policy
+represents a set of input/output filters of the RS-client. This statement
+makes possible that a single route-map represents the full set of filters
+that a BGP speaker would use for its different peers in a non-RS scenario.
+
+The @emph{match peer} statement has different semantics whether it is used
+inside an import or an export route-map. In the first case the statement
+matches if the address of the peer who sends the announce is the same that
+the address specified by @{A.B.C.D|X:X::X:X@}. For export route-maps it
+matches when @{A.B.C.D|X:X::X:X@} is the address of the RS-Client into whose
+Loc-RIB the announce is going to be inserted (how the same export policy is
+applied before different Loc-RIBs is shown in @ref{fig:rs-processing}.).
+@end deffn
+
+@deffn {Route-map Command} {call @var{WORD}} {}
+This command (also used inside a route-map) jumps into a different
+route-map, whose name is specified by @var{WORD}. When the called
+route-map finishes, depending on its result the original route-map
+continues or not. Apart from being useful for making import/export
+route-maps easier to write, this command can also be used inside
+any normal (in or out) route-map.
+@end deffn
+
+@node Example of Route Server Configuration
+@section Example of Route Server Configuration
+
+Finally we are going to show how to configure a Quagga daemon to act as a
+Route Server. For this purpose we are going to present a scenario without
+route server, and then we will show how to use the configurations of the BGP
+routers to generate the configuration of the route server.
+
+All the configuration files shown in this section have been taken
+from scenarios which were tested using the VNUML tool
+@uref{http://www.dit.upm.es/vnuml,VNUML}. 
+
+@menu
+* Configuration of the BGP routers without Route Server::
+* Configuration of the BGP routers with Route Server::
+* Configuration of the Route Server itself::
+* Further considerations about Import and Export route-maps::
+@end menu
+
+@node Configuration of the BGP routers without Route Server
+@subsection Configuration of the BGP routers without Route Server
+
+We will suppose that our initial scenario is an exchange point with three
+BGP capable routers, named RA, RB and RC. Each of the BGP speakers generates
+some routes (with the @var{network} command), and establishes BGP peerings
+against the other two routers. These peerings have In and Out route-maps
+configured, named like ``PEER-X-IN'' or ``PEER-X-OUT''. For example the
+configuration file for router RA could be the following:
+
+@exampleindent 0
+@example
+#Configuration for router 'RA'
+!
+hostname RA
+password ****
+!
+router bgp 65001
+  no bgp default ipv4-unicast
+  neighbor 2001:0DB8::B remote-as 65002
+  neighbor 2001:0DB8::C remote-as 65003
+!
+  address-family ipv6
+    network 2001:0DB8:AAAA:1::/64
+    network 2001:0DB8:AAAA:2::/64
+    network 2001:0DB8:0000:1::/64
+    network 2001:0DB8:0000:2::/64
+
+    neighbor 2001:0DB8::B activate
+    neighbor 2001:0DB8::B soft-reconfiguration inbound
+    neighbor 2001:0DB8::B route-map PEER-B-IN in
+    neighbor 2001:0DB8::B route-map PEER-B-OUT out
+
+    neighbor 2001:0DB8::C activate
+    neighbor 2001:0DB8::C soft-reconfiguration inbound
+    neighbor 2001:0DB8::C route-map PEER-C-IN in
+    neighbor 2001:0DB8::C route-map PEER-C-OUT out
+  exit-address-family
+!
+ipv6 prefix-list COMMON-PREFIXES seq  5 permit 2001:0DB8:0000::/48 ge 64 le 64
+ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-A-PREFIXES seq  5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
+ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-B-PREFIXES seq  5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
+ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-C-PREFIXES seq  5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
+ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
+!
+route-map PEER-B-IN permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set metric 100
+route-map PEER-B-IN permit 20
+  match ipv6 address prefix-list PEER-B-PREFIXES
+  set community 65001:11111
+!
+route-map PEER-C-IN permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set metric 200
+route-map PEER-C-IN permit 20
+  match ipv6 address prefix-list PEER-C-PREFIXES
+  set community 65001:22222
+!
+route-map PEER-B-OUT permit 10
+  match ipv6 address prefix-list PEER-A-PREFIXES
+!
+route-map PEER-C-OUT permit 10
+  match ipv6 address prefix-list PEER-A-PREFIXES
+!
+line vty
+!
+@end example
+
+@node Configuration of the BGP routers with Route Server
+@subsection Configuration of the BGP routers with Route Server
+
+To convert the initial scenario into one with route server, first we must
+modify the configuration of routers RA, RB and RC. Now they must not peer
+between them, but only with the route server. For example, RA's
+configuration would turn into:
+
+@example
+# Configuration for router 'RA'
+!
+hostname RA
+password ****
+!
+router bgp 65001
+  no bgp default ipv4-unicast
+  neighbor 2001:0DB8::FFFF remote-as 65000
+!
+  address-family ipv6
+    network 2001:0DB8:AAAA:1::/64
+    network 2001:0DB8:AAAA:2::/64
+    network 2001:0DB8:0000:1::/64
+    network 2001:0DB8:0000:2::/64
+
+    neighbor 2001:0DB8::FFFF activate
+    neighbor 2001:0DB8::FFFF soft-reconfiguration inbound
+  exit-address-family
+!
+line vty
+!
+@end example
+
+Which is logically much simpler than its initial configuration, as it now
+maintains only one BGP peering and all the filters (route-maps) have
+disappeared.
+
+@node Configuration of the Route Server itself
+@subsection Configuration of the Route Server itself
+
+As we said when we described the functions of a route server
+(@pxref{Description of the Route Server model}), it is in charge of all the
+route filtering. To achieve that, the In and Out filters from the RA, RB and
+RC configurations must be converted into Import and Export policies in the
+route server.
+
+This is a fragment of the route server configuration (we only show
+the policies for client RA):
+
+@example
+# Configuration for Route Server ('RS')
+!
+hostname RS
+password ix
+!
+bgp multiple-instance
+!
+router bgp 65000 view RS
+  no bgp default ipv4-unicast
+  neighbor 2001:0DB8::A  remote-as 65001
+  neighbor 2001:0DB8::B  remote-as 65002
+  neighbor 2001:0DB8::C  remote-as 65003
+!
+  address-family ipv6
+    neighbor 2001:0DB8::A activate
+    neighbor 2001:0DB8::A route-server-client
+    neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
+    neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export
+    neighbor 2001:0DB8::A soft-reconfiguration inbound
+
+    neighbor 2001:0DB8::B activate
+    neighbor 2001:0DB8::B route-server-client
+    neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import
+    neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export
+    neighbor 2001:0DB8::B soft-reconfiguration inbound
+
+    neighbor 2001:0DB8::C activate
+    neighbor 2001:0DB8::C route-server-client
+    neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import
+    neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export
+    neighbor 2001:0DB8::C soft-reconfiguration inbound
+  exit-address-family
+!
+ipv6 prefix-list COMMON-PREFIXES seq  5 permit 2001:0DB8:0000::/48 ge 64 le 64
+ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-A-PREFIXES seq  5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
+ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-B-PREFIXES seq  5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
+ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
+!
+ipv6 prefix-list PEER-C-PREFIXES seq  5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
+ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
+!
+route-map RSCLIENT-A-IMPORT permit 10
+  match peer 2001:0DB8::B
+  call A-IMPORT-FROM-B
+route-map RSCLIENT-A-IMPORT permit 20
+  match peer 2001:0DB8::C
+  call A-IMPORT-FROM-C
+!
+route-map A-IMPORT-FROM-B permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set metric 100
+route-map A-IMPORT-FROM-B permit 20
+  match ipv6 address prefix-list PEER-B-PREFIXES
+  set community 65001:11111
+!
+route-map A-IMPORT-FROM-C permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set metric 200
+route-map A-IMPORT-FROM-C permit 20
+  match ipv6 address prefix-list PEER-C-PREFIXES
+  set community 65001:22222
+!
+route-map RSCLIENT-A-EXPORT permit 10
+  match peer 2001:0DB8::B
+  match ipv6 address prefix-list PEER-A-PREFIXES
+route-map RSCLIENT-A-EXPORT permit 20
+  match peer 2001:0DB8::C
+  match ipv6 address prefix-list PEER-A-PREFIXES
+!
+...
+...
+...
+@end example
+
+If you compare the initial configuration of RA with the route server
+configuration above, you can see how easy it is to generate the Import and
+Export policies for RA from the In and Out route-maps of RA's original
+configuration.
+
+When there was no route server, RA maintained two peerings, one with RB and
+another with RC. Each of this peerings had an In route-map configured. To
+build the Import route-map for client RA in the route server, simply add
+route-map entries following this scheme:
+
+@example
+route-map <NAME> permit 10
+    match peer <Peer Address>
+    call <In Route-Map for this Peer>
+route-map <NAME> permit 20
+    match peer <Another Peer Address>
+    call <In Route-Map for this Peer>
+@end example
+
+This is exactly the process that has been followed to generate the route-map
+RSCLIENT-A-IMPORT. The route-maps that are called inside it (A-IMPORT-FROM-B
+and A-IMPORT-FROM-C) are exactly the same than the In route-maps from the
+original configuration of RA (PEER-B-IN and PEER-C-IN), only the name is
+different.
+
+The same could have been done to create the Export policy for RA (route-map
+RSCLIENT-A-EXPORT), but in this case the original Out route-maps where so
+simple that we decided not to use the @var{call WORD} commands, and we
+integrated all in a single route-map (RSCLIENT-A-EXPORT).
+
+The Import and Export policies for RB and RC are not shown, but
+the process would be identical.
+
+@node Further considerations about Import and Export route-maps
+@subsection Further considerations about Import and Export route-maps
+
+The current version of the route server patch only allows to specify a
+route-map for import and export policies, while in a standard BGP speaker
+apart from route-maps there are other tools for performing input and output
+filtering (access-lists, community-lists, ...). But this does not represent
+any limitation, as all kinds of filters can be included in import/export
+route-maps. For example suppose that in the non-route-server scenario peer
+RA had the following filters configured for input from peer B:
+
+@example
+    neighbor 2001:0DB8::B prefix-list LIST-1 in
+    neighbor 2001:0DB8::B filter-list LIST-2 in
+    neighbor 2001:0DB8::B route-map PEER-B-IN in
+    ...
+    ...
+route-map PEER-B-IN permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set local-preference 100
+route-map PEER-B-IN permit 20
+  match ipv6 address prefix-list PEER-B-PREFIXES
+  set community 65001:11111
+@end example
+
+It is posible to write a single route-map which is equivalent to
+the three filters (the community-list, the prefix-list and the
+route-map). That route-map can then be used inside the Import
+policy in the route server. Lets see how to do it:
+
+@example
+    neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
+    ...
+!
+...
+route-map RSCLIENT-A-IMPORT permit 10
+  match peer 2001:0DB8::B
+  call A-IMPORT-FROM-B
+...
+...
+!
+route-map A-IMPORT-FROM-B permit 1
+  match ipv6 address prefix-list LIST-1
+  match as-path LIST-2
+  on-match goto 10
+route-map A-IMPORT-FROM-B deny 2
+route-map A-IMPORT-FROM-B permit 10
+  match ipv6 address prefix-list COMMON-PREFIXES
+  set local-preference 100
+route-map A-IMPORT-FROM-B permit 20
+  match ipv6 address prefix-list PEER-B-PREFIXES
+  set community 65001:11111
+!
+...
+...
+@end example
+
+The route-map A-IMPORT-FROM-B is equivalent to the three filters
+(LIST-1, LIST-2 and PEER-B-IN). The first entry of route-map
+A-IMPORT-FROM-B (sequence number 1) matches if and only if both
+the prefix-list LIST-1 and the filter-list LIST-2 match. If that
+happens, due to the ``on-match goto 10'' statement the next
+route-map entry to be processed will be number 10, and as of that
+point route-map A-IMPORT-FROM-B is identical to PEER-B-IN. If
+the first entry does not match, `on-match goto 10'' will be
+ignored and the next processed entry will be number 2, which will
+deny the route.
+
+Thus, the result is the same that with the three original filters,
+i.e., if either LIST-1 or LIST-2 rejects the route, it does not
+reach the route-map PEER-B-IN. In case both LIST-1 and LIST-2
+accept the route, it passes to PEER-B-IN, which can reject, accept
+or modify the route.
diff --git a/doc/snmp.texi b/doc/snmp.texi
new file mode 100644 (file)
index 0000000..0918a46
--- /dev/null
@@ -0,0 +1,185 @@
+@node SNMP Support
+@chapter SNMP Support
+
+@acronym{SNMP,Simple Network Managing Protocol} is a widely implemented
+feature for collecting network information from router and/or host.
+Quagga itself does not support SNMP agent (server daemon) functionality
+but is able to connect to a SNMP agent using the SMUX protocol
+(@cite{RFC1227}) or the AgentX protocol (@cite{RFC2741}) and make the
+routing protocol MIBs available through it.
+
+@menu
+* Getting and installing an SNMP agent::
+* AgentX configuration::
+* SMUX configuration::
+* MIB and command reference::
+* Handling SNMP Traps::
+@end menu
+
+@node Getting and installing an SNMP agent
+@section Getting and installing an SNMP agent
+
+There are several SNMP agent which support SMUX or AgentX. We recommend to use the latest
+version of @code{net-snmp} which was formerly known as @code{ucd-snmp}.
+It is free and open software and available at @uref{http://www.net-snmp.org/}
+and as binary package for most Linux distributions.
+@code{net-snmp} has to be compiled with @code{--with-mib-modules=agentx} to
+be able to accept connections from Quagga using AgentX protocol or with
+@code{--with-mib-modules=smux} to use SMUX protocol.
+
+Nowadays, SMUX is a legacy protocol. The AgentX protocol should be
+preferred for any new deployment. Both protocols have the same coverage.
+
+@node AgentX configuration
+@section AgentX configuration
+
+To enable AgentX protocol support, Quagga must have been build with the
+@code{--enable-snmp} or @code{--enable-snmp=agentx} option. Both the
+master SNMP agent (snmpd) and each of the Quagga daemons must be
+configured. In @code{/etc/snmp/snmpd.conf}, @code{master agentx}
+directive should be added. In each of the Quagga daemons, @code{agentx}
+command will enable AgentX support.
+
+@example
+/etc/snmp/snmpd.conf:
+       #
+       # example access restrictions setup
+       #
+       com2sec readonly default public
+       group MyROGroup v1 readonly
+       view all included .1 80
+       access MyROGroup "" any noauth exact all none none
+       #
+       # enable master agent for AgentX subagents
+       #
+       master agentx
+
+/etc/quagga/ospfd.conf:
+       ! ... the rest of ospfd.conf has been omitted for clarity ...
+       !
+       agentx
+       !
+@end example
+
+Upon successful connection, you should get something like this in the
+log of each Quagga daemons:
+
+@example
+2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected
+@end example
+
+Then, you can use the following command to check everything works as expected:
+
+@example
+# snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
+OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
+[...]
+@end example
+
+The AgentX protocol can be transported over a Unix socket or using TCP
+or UDP. It usually defaults to a Unix socket and depends on how NetSNMP
+was built. If need to configure Quagga to use another transport, you can
+configure it through @code{/etc/snmp/quagga.conf}:
+
+@example
+/etc/snmp/quagga.conf:
+       [snmpd]
+       # Use a remote master agent
+       agentXSocket tcp:192.168.15.12:705
+@end example
+
+@node SMUX configuration
+@section SMUX configuration
+
+To enable SMUX protocol support, Quagga must have been build with the
+@code{--enable-snmp=smux} option.
+
+A separate connection has then to be established between the
+SNMP agent (snmpd) and each of the Quagga daemons. This connections
+each use different OID numbers and passwords. Be aware that this OID
+number is not the one that is used in queries by clients, it is solely
+used for the intercommunication of the daemons.
+
+In the following example the ospfd daemon will be connected to the
+snmpd daemon using the password "quagga_ospfd". For testing it is
+recommending to take exactly the below snmpd.conf as wrong access
+restrictions can be hard to debug.
+
+@example
+/etc/snmp/snmpd.conf:
+       #
+       # example access restrictions setup
+       #
+       com2sec readonly default public
+       group MyROGroup v1 readonly
+       view all included .1 80
+       access MyROGroup "" any noauth exact all none none
+       #
+       # the following line is relevant for Quagga
+       #
+       smuxpeer .1.3.6.1.4.1.3317.1.2.5 quagga_ospfd
+
+/etc/quagga/ospf:
+       ! ... the rest of ospfd.conf has been omitted for clarity ...
+       !
+       smux peer .1.3.6.1.4.1.3317.1.2.5 quagga_ospfd
+       !
+@end example
+
+After restarting snmpd and quagga, a successful connection can be verified in
+the syslog and by querying the SNMP daemon:
+
+@example
+snmpd[12300]: [smux_accept] accepted fd 12 from 127.0.0.1:36255 
+snmpd[12300]: accepted smux peer: \
+       oid GNOME-PRODUCT-ZEBRA-MIB::ospfd, quagga-0.96.5
+
+# snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
+OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
+@end example
+
+Be warned that the current version (5.1.1) of the Net-SNMP daemon writes a line
+for every SNMP connect to the syslog which can lead to enormous log file sizes.
+If that is a problem you should consider to patch snmpd and comment out the
+troublesome @code{snmp_log()} line in the function
+@code{netsnmp_agent_check_packet()} in @code{agent/snmp_agent.c}.
+
+@node MIB and command reference
+@section MIB and command reference
+
+The following OID numbers are used for the interprocess communication of snmpd and
+the Quagga daemons with SMUX only.
+@example
+            (OIDs below .iso.org.dod.internet.private.enterprises)
+zebra  .1.3.6.1.4.1.3317.1.2.1 .gnome.gnomeProducts.zebra.zserv
+bgpd   .1.3.6.1.4.1.3317.1.2.2 .gnome.gnomeProducts.zebra.bgpd
+ripd   .1.3.6.1.4.1.3317.1.2.3 .gnome.gnomeProducts.zebra.ripd
+ospfd  .1.3.6.1.4.1.3317.1.2.5 .gnome.gnomeProducts.zebra.ospfd
+ospf6d .1.3.6.1.4.1.3317.1.2.6 .gnome.gnomeProducts.zebra.ospf6d
+@end example
+
+Sadly, SNMP has not been implemented in all daemons yet. The following
+OID numbers are used for querying the SNMP daemon by a client:
+@example
+zebra  .1.3.6.1.2.1.4.24   .iso.org.dot.internet.mgmt.mib-2.ip.ipForward
+ospfd  .1.3.6.1.2.1.14     .iso.org.dot.internet.mgmt.mib-2.ospf
+bgpd   .1.3.6.1.2.1.15     .iso.org.dot.internet.mgmt.mib-2.bgp 
+ripd   .1.3.6.1.2.1.23     .iso.org.dot.internet.mgmt.mib-2.rip2
+ospf6d .1.3.6.1.3.102      .iso.org.dod.internet.experimental.ospfv3
+@end example
+
+The following syntax is understood by the Quagga daemons for configuring SNMP using SMUX:
+@deffn {Command} {smux peer @var{oid}} {}
+@deffnx {Command} {no smux peer @var{oid}} {}
+@end deffn
+
+@deffn {Command} {smux peer @var{oid} @var{password}} {}
+@deffnx {Command} {no smux peer @var{oid} @var{password}} {}
+@end deffn
+
+Here is the syntax for using AgentX:
+@deffn {Command} {agentx} {}
+@deffnx {Command} {no agentx} {}
+@end deffn
+
+@include snmptrap.texi
diff --git a/doc/snmptrap.texi b/doc/snmptrap.texi
new file mode 100644 (file)
index 0000000..6c67288
--- /dev/null
@@ -0,0 +1,211 @@
+@c Documentation on configuring Quagga and snmpd for SNMP traps
+@c contributed by Jeroen Simonetti, jsimonetti@denit.net
+
+@node Handling SNMP Traps
+@section Handling SNMP Traps
+
+To handle snmp traps make sure your snmp setup of quagga works
+correctly as described in the quagga documentation in @xref{SNMP Support}.
+
+The BGP4 mib will send traps on peer up/down events. These should be
+visible in your snmp logs with a message similar to:
+
+@samp{snmpd[13733]: Got trap from peer on fd 14}
+
+To react on these traps they should be handled by a trapsink. Configure
+your trapsink by adding the following lines to @file{/etc/snmpd/snmpd.conf}:
+
+@example
+  # send traps to the snmptrapd on localhost
+  trapsink localhost
+@end example
+
+This will send all traps to an snmptrapd running on localhost. You can
+of course also use a dedicated management station to catch traps.
+Configure the snmptrapd daemon by adding the following line to
+@file{/etc/snmpd/snmptrapd.conf}:
+
+@c Documentation contributed by Jeroen Simonetti, jsimonetti@denit.net
+
+@example
+  traphandle .1.3.6.1.4.1.3317.1.2.2 /etc/snmp/snmptrap_handle.sh
+@end example
+
+This will use the bash script @file{/etc/snmp/snmptrap_handle.sh} to handle
+the BGP4 traps. To add traps for other protocol daemons, lookup their
+appropriate OID from their mib. (For additional information about which
+traps are supported by your mib, lookup the mib on
+@uref{http://www.oidview.com/mibs/detail.html}).
+
+Make sure snmptrapd is started.
+
+The snmptrap_handle.sh script I personally use for handling BGP4 traps
+is below. You can of course do all sorts of things when handling traps,
+like sound a siren, have your display flash, etc., be creative ;).
+
+@verbatim
+  #!/bin/bash
+
+  # routers name
+  ROUTER=`hostname -s`
+
+  #email address use to sent out notification
+  EMAILADDR="john@doe.com"
+  #email address used (allongside above) where warnings should be sent
+  EMAILADDR_WARN="sms-john@doe.com"
+
+  # type of notification
+  TYPE="Notice"
+
+  # local snmp community for getting AS belonging to peer
+  COMMUNITY="<community>"
+
+  # if a peer address is in $WARN_PEERS a warning should be sent
+  WARN_PEERS="192.0.2.1"
+
+
+  # get stdin
+  INPUT=`cat -`
+
+  # get some vars from stdin
+  uptime=`echo $INPUT | cut -d' ' -f5`
+  peer=`echo $INPUT | cut -d' ' -f8 | \
+       sed -e 's/SNMPv2-SMI::mib-2.15.3.1.14.//g'`
+  peerstate=`echo $INPUT | cut -d' ' -f13`
+  errorcode=`echo $INPUT | cut -d' ' -f9 | sed -e 's/\"//g'`
+  suberrorcode=`echo $INPUT | cut -d' ' -f10 | sed -e 's/\"//g'`
+  remoteas=`snmpget -v2c -c $COMMUNITY \
+               localhost SNMPv2-SMI::mib-2.15.3.1.9.$peer \
+               | cut -d' ' -f4`
+
+  WHOISINFO=`whois -h whois.ripe.net " -r AS$remoteas" | \
+               egrep '(as-name|descr)'`
+  asname=`echo "$WHOISINFO" | grep "^as-name:" | \
+               sed -e 's/^as-name://g' -e 's/  //g' -e 's/^ //g' | uniq`
+  asdescr=`echo "$WHOISINFO" | grep "^descr:" | \
+               sed -e 's/^descr://g' -e 's/  //g' -e 's/^ //g' | uniq`
+
+  # if peer address is in $WARN_PEER, the email should also
+  # be sent to $EMAILADDR_WARN
+  for ip in $WARN_PEERS; do
+    if [ "x$ip" == "x$peer" ]; then
+      EMAILADDR="$EMAILADDR,$EMAILADDR_WARN"
+      TYPE="WARNING"
+      break
+    fi
+  done
+  
+
+  # convert peer state
+  case "$peerstate" in
+    1) peerstate="Idle" ;;
+    2) peerstate="Connect" ;;
+    3) peerstate="Active" ;;
+    4) peerstate="Opensent" ;;
+    5) peerstate="Openconfirm" ;;
+    6) peerstate="Established" ;;
+    *) peerstate="Unknown" ;;
+  esac
+
+  # get textual messages for errors
+  case "$errorcode" in
+    00)
+      error="No error"
+      suberror=""
+      ;;
+    01)
+      error="Message Header Error"
+      case "$suberrorcode" in
+        01) suberror="Connection Not Synchronized" ;;
+        02) suberror="Bad Message Length" ;;
+        03) suberror="Bad Message Type" ;;
+        *) suberror="Unknown" ;;
+      esac
+      ;;
+    02)    
+      error="OPEN Message Error"
+      case "$suberrorcode" in
+        01) suberror="Unsupported Version Number" ;;
+        02) suberror="Bad Peer AS" ;;
+        03) suberror="Bad BGP Identifier" ;;
+        04) suberror="Unsupported Optional Parameter" ;;
+        05) suberror="Authentication Failure" ;;
+        06) suberror="Unacceptable Hold Time" ;;
+        *) suberror="Unknown" ;;
+      esac
+      ;;
+    03)
+      error="UPDATE Message Error"
+      case "$suberrorcode" in
+        01) suberror="Malformed Attribute List" ;;
+        02) suberror="Unrecognized Well-known Attribute" ;;
+        03) suberror="Missing Well-known Attribute" ;;
+        04) suberror="Attribute Flags Error" ;;
+        05) suberror="Attribute Length Error" ;;
+        06) suberror="Invalid ORIGIN Attribute" ;;
+        07) suberror="AS Routing Loop" ;;
+        08) suberror="Invalid NEXT_HOP Attribute" ;;
+        09) suberror="Optional Attribute Error" ;;
+        10) suberror="Invalid Network Field" ;;
+        11) suberror="Malformed AS_PATH" ;;
+        *) suberror="Unknown" ;;
+      esac
+      ;;
+    04)
+      error="Hold Timer Expired"
+      suberror=""
+      ;;
+    05)
+      error="Finite State Machine Error"
+      suberror=""
+      ;;
+    06)
+      error="Cease"
+      case "$suberrorcode" in
+        01) suberror="Maximum Number of Prefixes Reached" ;;
+        02) suberror="Administratively Shutdown" ;;
+        03) suberror="Peer Unconfigured" ;;
+        04) suberror="Administratively Reset" ;;
+        05) suberror="Connection Rejected" ;;
+        06) suberror="Other Configuration Change" ;;
+        07) suberror="Connection collision resolution" ;;
+        08) suberror="Out of Resource" ;;
+        09) suberror="MAX" ;;
+        *) suberror="Unknown" ;;
+      esac
+      ;;
+    *)
+      error="Unknown"
+      suberror=""
+      ;;
+  esac
+
+  # create textual message from errorcodes
+  if [ "x$suberror" == "x" ]; then
+    NOTIFY="$errorcode ($error)"
+  else
+    NOTIFY="$errorcode/$suberrorcode ($error/$suberror)"
+  fi
+
+  # form a decent subject
+  SUBJECT="$TYPE: $ROUTER [bgp] $peer is $peerstate: $NOTIFY"
+  # create the email body
+  MAIL=`cat << EOF
+  BGP notification on router $ROUTER.
+  
+  Peer: $peer
+  AS: $remoteas
+  New state: $peerstate
+  Notification: $NOTIFY
+
+  Info:
+  $asname
+  $asdescr
+  Snmpd uptime: $uptime
+  EOF`
+
+  # mail the notification
+  echo "$MAIL" | mail -s "$SUBJECT" $EMAILADDR
+@end verbatim
diff --git a/doc/texinfo.css b/doc/texinfo.css
new file mode 100644 (file)
index 0000000..f5fa4f4
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+   CSS style for Texinfo documents
+   
+   Public domain 2016 sirgazil. All rights waived.
+   
+   Obtained from: 
+   
+    https://sirgazil.bitbucket.io/en/artifact
+    https://sirgazil.bitbucket.io/en/doc/texinfo-css/tip/manual/static/css/document.css
+*/
+
+
+
+/* NATIVE ELEMENTS */
+a:link,
+a:visited {
+    color: #1E90FF;
+    text-decoration: none;
+}
+
+a:active,
+a:focus,
+a:hover {
+    text-decoration: underline;
+}
+
+abbr,
+acronym {
+    cursor: help;
+}
+
+blockquote {
+    color: #555753;
+    font-style: oblique;
+    margin: 30px 0px;
+    padding-left: 3em;
+}
+
+body {
+    background-color: white;
+    box-shadow: 0 0 2px gray;
+    box-sizing: border-box;
+    color: #333;
+    font-family: sans-serif;
+    font-size: 16px;
+    margin: 50px auto;
+    max-width: 960px;
+    padding: 50px;
+}
+
+code,
+samp,
+tt,
+var {
+    color: purple;
+    font-size: 0.8em;
+}
+
+div.example,
+div.lisp {
+    margin: 0px;
+}
+
+dl {
+    margin: 3em 0em;
+}
+
+dl dl {
+    margin: 0em;
+}
+
+dt {
+    background-color: #F5F5F5;
+    padding: 0.5em;
+}
+
+h1,
+h2,
+h2.contents-heading,
+h3,
+h4 {
+    padding: 20px 0px 0px 0px;
+    font-weight: normal;
+}
+
+h1 {
+    font-size: 2.4em;
+}
+
+h2 {
+    font-size: 2.2em;
+    font-weight: bold;
+}
+
+h3 {
+    font-size: 1.8em;
+}
+
+h4 {
+    font-size: 1.4em;
+}
+
+hr {
+    background-color: silver;
+    border-style: none;
+    height: 1px;
+    margin: 0px;
+}
+
+html {
+    background-color: #F5F5F5;
+}
+
+img {
+    max-width: 100%;
+}
+
+li {
+    padding: 5px;
+}
+
+pre.display,
+pre.example,
+pre.format,
+pre.lisp,
+pre.verbatim{
+    overflow: auto;
+}
+
+pre.example,
+pre.lisp,
+pre.verbatim {
+    background-color: #2D3743;
+    border-color: #000;
+    border-style: solid;
+    border-width: thin;
+    color: #E1E1E1;
+    font-size: smaller;
+    padding: 1em;
+}
+
+table {
+    border-collapse: collapse;
+    margin: 40px 0px;
+}
+
+table.index-cp *,
+table.index-fn *,
+table.index-ky *,
+table.index-pg *,
+table.index-tp *,
+table.index-vr * {
+    background-color: inherit;
+    border-style: none;
+}
+
+td,
+th {
+    border-color: silver;
+    border-style: solid;
+    border-width: thin;
+    padding: 10px;
+}
+
+th {
+    background-color: #F5F5F5;
+}
+/* END NATIVE ELEMENTS */
+
+
+
+/* CLASSES */
+.contents {
+    margin-bottom: 4em;
+}
+
+.float {
+    margin: 3em 0em;
+}
+
+.float-caption {
+    font-size: smaller;
+    text-align: center;
+}
+
+.float > img {
+    display: block;
+    margin: auto;
+}
+
+.footnote {
+    font-size: smaller;
+    margin: 5em 0em;
+}
+
+.footnote h3 {
+    display: inline;
+    font-size: small;
+}
+
+.header {
+    background-color: #F2F2F2;
+    font-size: small;
+    padding: 0.2em 1em;
+}
+
+.key {
+    color: purple;
+    font-size: 0.8em;
+}
+
+.menu * {
+    border-style: none;
+}
+
+.menu td {
+    padding: 0.5em 0em;
+}
+
+.menu td:last-child {
+    width: 60%;
+}
+
+.menu th {
+    background-color: inherit;
+}
+/* END CLASSES */
diff --git a/doc/texinfo.tex b/doc/texinfo.tex
new file mode 100644 (file)
index 0000000..ddda670
--- /dev/null
@@ -0,0 +1,11198 @@
+% texinfo.tex -- TeX macros to handle Texinfo files.
+% 
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2016-02-05.07}
+%
+% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+% 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
+% Free Software Foundation, Inc.
+%
+% This texinfo.tex file 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 3 of the
+% License, or (at your option) any later version.
+%
+% This texinfo.tex file 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, see <http://www.gnu.org/licenses/>.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction. This Exception is an additional permission under section 7
+% of the GNU General Public License, version 3 ("GPLv3").
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+%   http://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or
+%   http://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or
+%   http://www.gnu.org/software/texinfo/ (the Texinfo home page)
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org.  Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem.  Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution.  For a simple
+% manual foo.texi, however, you can get away with this:
+%   tex foo.texi
+%   texindex foo.??
+%   tex foo.texi
+%   tex foo.texi
+%   dvips foo.dvi -o  # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent.  You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
+
+
+\message{Loading texinfo [version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+  \catcode`+=\active \catcode`\_=\active}
+
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros whose names we will redefine.
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
+\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexraggedright=\raggedright
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexsp=\sp
+\let\ptexstar=\*
+\let\ptexsup=\sup
+\let\ptext=\t
+\let\ptextop=\top
+{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+  \let\linenumber = \empty % Pre-3.0.
+\else
+  \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined  \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined   \gdef\putwordChapter{Chapter}\fi
+\ifx\putworderror\undefined     \gdef\putworderror{error}\fi
+\ifx\putwordfile\undefined      \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined        \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined       \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined   \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined      \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined  \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined   \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined        \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined        \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined      \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined   \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined   \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined       \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined       \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined  \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined       \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined    \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined   \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined    \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined    \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined   \gdef\putwordDeffunc{Function}\fi
+
+% Give the space character the catcode for a space.
+\def\spaceisspace{\catcode`\ =10\relax}
+
+\chardef\dashChar  = `\-
+\chardef\slashChar = `\/
+\chardef\underChar = `\_
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+  Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+  ap-pen-dix bit-map bit-maps
+  data-base data-bases eshell fall-ing half-way long-est man-u-script
+  man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+  par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+  spell-ing spell-ings
+  stand-alone strong-est time-stamp time-stamps which-ever white-space
+  wide-spread wrap-around
+}
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal.  We don't just call \tracingall here,
+% since that produces some useless output on the terminal.  We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+  \tracingstats2
+  \tracingpages1
+  \tracinglostchars2  % 2 gives us more in etex
+  \tracingparagraphs1
+  \tracingoutput1
+  \tracingmacros2
+  \tracingrestores1
+  \showboxbreadth\maxdimen \showboxdepth\maxdimen
+  \ifx\eTeXversion\thisisundefined\else % etex gives us more logging
+    \tracingscantokens1
+    \tracingifs1
+    \tracinggroups1
+    \tracingnesting2
+    \tracingassigns1
+  \fi
+  \tracingcommands3  % 3 gives us more in etex
+  \errorcontextlines16
+}%
+
+% @errormsg{MSG}.  Do the index-like expansions on MSG, but if things
+% aren't perfect, it's not the end of the world, being an error message,
+% after all.
+% 
+\def\errormsg{\begingroup \indexnofonts \doerrormsg}
+\def\doerrormsg#1{\errmessage{#1}}
+
+% add check for \lastpenalty to plain's definitions.  If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+  \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+  \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+  \removelastskip\penalty-200\bigskip\fi\fi}
+
+%\f Output routine
+%
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt }
+
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong  \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Output a mark which sets \thischapter, \thissection and \thiscolor.
+% We dump everything together because we only have one kind of mark.
+% This works because we only use \botmark / \topmark, not \firstmark.
+%
+% A mark contains a subexpression of the \ifcase ... \fi construct.
+% \get*marks macros below extract the needed part using \ifcase.
+%
+% Another complication is to let the user choose whether \thischapter
+% (\thissection) refers to the chapter (section) in effect at the top
+% of a page, or that at the bottom of a page.
+
+% \domark is called twice inside \chapmacro, to add one
+% mark before the section break, and one after.
+%   In the second call \prevchapterdefs is the same as \lastchapterdefs,
+% and \prevsectiondefs is the same as \lastsectiondefs.
+%   Then if the page is not broken at the mark, some of the previous
+% section appears on the page, and we can get the name of this section
+% from \firstmark for @everyheadingmarks top.
+%   @everyheadingmarks bottom uses \botmark.
+%
+% See page 260 of The TeXbook.
+\def\domark{%
+  \toks0=\expandafter{\lastchapterdefs}%
+  \toks2=\expandafter{\lastsectiondefs}%
+  \toks4=\expandafter{\prevchapterdefs}%
+  \toks6=\expandafter{\prevsectiondefs}%
+  \toks8=\expandafter{\lastcolordefs}%
+  \mark{%
+                   \the\toks0 \the\toks2  % 0: marks for @everyheadingmarks top
+      \noexpand\or \the\toks4 \the\toks6  % 1: for @everyheadingmarks bottom
+    \noexpand\else \the\toks8             % 2: color marks
+  }%
+}
+
+% \gettopheadingmarks, \getbottomheadingmarks,
+% \getcolormarks - extract needed part of mark.
+%
+% \topmark doesn't work for the very first chapter (after the title
+% page or the contents), so we use \firstmark there -- this gets us
+% the mark with the chapter defs, unless the user sneaks in, e.g.,
+% @setcolor (or @url, or @link, etc.) between @contents and the very
+% first @chapter.
+\def\gettopheadingmarks{%
+  \ifcase0\topmark\fi
+  \ifx\thischapter\empty \ifcase0\firstmark\fi \fi
+}
+\def\getbottomheadingmarks{\ifcase1\botmark\fi}
+\def\getcolormarks{\ifcase2\topmark\fi}
+
+% Avoid "undefined control sequence" errors.
+\def\lastchapterdefs{}
+\def\lastsectiondefs{}
+\def\lastsection{}
+\def\prevchapterdefs{}
+\def\prevsectiondefs{}
+\def\lastcolordefs{}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen\bindingoffset
+\newdimen\normaloffset
+\newdimen\pagewidth \newdimen\pageheight
+
+% Main output routine.
+%
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% \onepageout takes a vbox as an argument.
+% \shipout a vbox for a single page, adding an optional header, footer,
+% cropmarks, and footnote.  This also causes index entries for this page
+% to be written to the auxiliary files.
+%
+\def\onepageout#1{%
+  \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+  %
+  \ifodd\pageno  \advance\hoffset by \bindingoffset
+  \else \advance\hoffset by -\bindingoffset\fi
+  %
+  % Common context changes for both heading and footing.
+  % Do this outside of the \shipout so @code etc. will be expanded in
+  % the headline as they should be, not taken literally (outputting ''code).
+  \def\commmonheadfootline{\let\hsize=\pagewidth \texinfochars}
+  %
+  % Retrieve the information for the headings from the marks in the page,
+  % and call Plain TeX's \makeheadline and \makefootline, which use the
+  % values in \headline and \footline.
+  %
+  % This is used to check if we are on the first page of a chapter.
+  \ifcase1\topmark\fi
+  \let\prevchaptername\thischaptername
+  \ifcase0\firstmark\fi
+  \let\curchaptername\thischaptername
+  %
+  \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi
+  \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi
+  %
+  \ifx\curchaptername\prevchaptername
+    \let\thischapterheading\thischapter
+  \else
+    % \thischapterheading is the same as \thischapter except it is blank
+    % for the first page of a chapter.  This is to prevent the chapter name 
+    % being shown twice.
+    \def\thischapterheading{}%
+  \fi
+  %
+  \global\setbox\headlinebox = \vbox{\commmonheadfootline \makeheadline}%
+  \global\setbox\footlinebox = \vbox{\commmonheadfootline \makefootline}%
+  %
+  {%
+    % Set context for writing to auxiliary files like index files.
+    % Have to do this stuff outside the \shipout because we want it to
+    % take effect in \write's, yet the group defined by the \vbox ends
+    % before the \shipout runs.
+    %
+    \indexdummies         % don't expand commands in the output.
+    \normalturnoffactive  % \ in index entries must not stay \, e.g., if
+               % the page break happens to be in the middle of an example.
+               % We don't want .vr (or whatever) entries like this:
+               % \entry{{\indexbackslash }acronym}{32}{\code {\acronym}}
+               % "\acronym" won't work when it's read back in;
+               % it needs to be
+               % {\code {{\backslashcurfont }acronym}
+    \shipout\vbox{%
+      % Do this early so pdf references go to the beginning of the page.
+      \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+      %
+      \ifcropmarks \vbox to \outervsize\bgroup
+        \hsize = \outerhsize
+        \vskip-\topandbottommargin
+        \vtop to0pt{%
+          \line{\ewtop\hfil\ewtop}%
+          \nointerlineskip
+          \line{%
+            \vbox{\moveleft\cornerthick\nstop}%
+            \hfill
+            \vbox{\moveright\cornerthick\nstop}%
+          }%
+          \vss}%
+        \vskip\topandbottommargin
+        \line\bgroup
+          \hfil % center the page within the outer (page) hsize.
+          \ifodd\pageno\hskip\bindingoffset\fi
+          \vbox\bgroup
+      \fi
+      %
+      \unvbox\headlinebox
+      \pagebody{#1}%
+      \ifdim\ht\footlinebox > 0pt
+        % Only leave this space if the footline is nonempty.
+        % (We lessened \vsize for it in \oddfootingyyy.)
+        % The \baselineskip=24pt in plain's \makefootline has no effect.
+        \vskip 24pt
+        \unvbox\footlinebox
+      \fi
+      %
+      \ifcropmarks
+          \egroup % end of \vbox\bgroup
+        \hfil\egroup % end of (centering) \line\bgroup
+        \vskip\topandbottommargin plus1fill minus1fill
+        \boxmaxdepth = \cornerthick
+        \vbox to0pt{\vss
+          \line{%
+            \vbox{\moveleft\cornerthick\nsbot}%
+            \hfill
+            \vbox{\moveright\cornerthick\nsbot}%
+          }%
+          \nointerlineskip
+          \line{\ewbot\hfil\ewbot}%
+        }%
+      \egroup % \vbox from first cropmarks clause
+      \fi
+    }% end of \shipout\vbox
+  }% end of group with \indexdummies
+  \advancepageno
+  \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+% Main part of page, including any footnotes
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+  \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1\relax \unvbox#1\relax
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+% Here are the rules for the cropmarks.  Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+  {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+  {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+
+% Argument parsing
+
+% Parse an argument, then pass it to #1.  The argument is the rest of
+% the input line (except we remove a trailing comment).  #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+% For example, \def\foo{\parsearg\fooxxx}.
+%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+  \def\argtorun{#2}%
+  \begingroup
+    \obeylines
+    \spaceisspace
+    #1%
+    \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
+
+{\obeylines %
+  \gdef\parseargline#1^^M{%
+    \endgroup % End of the group started in \parsearg.
+    \argremovecomment #1\comment\ArgTerm%
+  }%
+}
+
+% First remove any @comment, then any @c comment.  Also remove a @texinfoc
+% comment (see \scanmacro for details).  Pass the result on to \argcheckspaces.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argremovetexinfoc #1\texinfoc\ArgTerm}
+\def\argremovetexinfoc#1\texinfoc#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
+
+% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+%    @end itemize  @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+  \def\temp{#3}%
+  \ifx\temp\empty
+    % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp:
+    \let\temp\finishparsearg
+  \else
+    \let\temp\argcheckspaces
+  \fi
+  % Put the space token in:
+  \temp#1 #3\ArgTerm
+}
+
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \argtorun.
+% (Similarly, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}}
+
+
+% \parseargdef - define a command taking an argument on the line
+%
+% \parseargdef\foo{...}
+%      is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+\def\parseargdef#1{%
+  \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+  \def#2{\parsearg#1}%
+  \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
+  \obeyspaces
+  \gdef\obeyedspace{ }
+
+  % Make each space character in the input produce a normal interword
+  % space in the output.  Don't allow a line break at this space, as this
+  % is used only in environments like @example, where each line of input
+  % should produce a line of output anyway.
+  %
+  \gdef\sepspaces{\obeyspaces\let =\tie}
+
+  % If an index command is used in an @example environment, any spaces
+  % therein should become regular spaces in the raw index file, not the
+  % expansion of \tie (\leavevmode \penalty \@M \ ).
+  \gdef\unsepspaces{\let =\space}
+}
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+% Define the framework for environments in texinfo.tex.  It's used like this:
+%
+%   \envdef\foo{...}
+%   \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo.  \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches.  The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as environments; they don't open a group.  (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At run-time, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+  \def\temp{#1}%
+  \ifx\thisenv\temp
+  \else
+    \badenverr
+  \fi
+}
+
+% Environment mismatch, #1 expected:
+\def\badenverr{%
+  \errhelp = \EMsimple
+  \errmessage{This command can appear only \inenvironment\temp,
+    not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+  \ifx#1\empty
+    outside of any environment%
+  \else
+    in environment \expandafter\string#1%
+  \fi
+}
+
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+  \if 1\csname iscond.#1\endcsname
+  \else
+    % The general wording of \badenverr may not be ideal.
+    \expandafter\checkenv\csname#1\endcsname
+    \csname E#1\endcsname
+    \endgroup
+  \fi
+}
+
+\newhelp\EMsimple{Press RETURN to continue.}
+
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\unskip\hfil\break\hbox{}\ignorespaces}
+
+% @/ allows a line break.
+\let\/=\allowbreak
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
+
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
+
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off  says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+  \def\temp{#1}%
+  \ifx\temp\onword \plainfrenchspacing
+  \else\ifx\temp\offword \plainnonfrenchspacing
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @frenchspacing option `\temp', must be on|off}%
+  \fi\fi
+}
+
+% @w prevents a word break.  Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox.  We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line.  According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0).  If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+% Another complication is that the group might be very large.  This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material.  In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom.  The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+  \ifnum\catcode`\^^M=\active \else
+    \errhelp = \groupinvalidhelp
+    \errmessage{@group invalid in context where filling is enabled}%
+  \fi
+  \startsavinginserts
+  %
+  \setbox\groupbox = \vtop\bgroup
+    % Do @comment since we are called inside an environment such as
+    % @example, where each end-of-line in the input causes an
+    % end-of-line in the output.  We don't want the end-of-line after
+    % the `@group' to put extra space in the output.  Since @group
+    % should appear on a line by itself (according to the Texinfo
+    % manual), we don't worry about eating any user text.
+    \comment
+}
+%
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it.  Thus, space below is not quite equal to space
+% above.  But it's pretty close.
+\def\Egroup{%
+    % To get correct interline space between the last line of the group
+    % and the first line afterwards, we have to propagate \prevdepth.
+    \endgraf % Not \par, as it may have been set to \lisppar.
+    \global\dimen1 = \prevdepth
+  \egroup           % End the \vtop.
+  \addgroupbox
+  \prevdepth = \dimen1
+  \checkinserts
+}
+
+\def\addgroupbox{
+  % \dimen0 is the vertical size of the group's box.
+  \dimen0 = \ht\groupbox  \advance\dimen0 by \dp\groupbox
+  % \dimen2 is how much space is left on the page (more or less).
+  \dimen2 = \pageheight   \advance\dimen2 by -\pagetotal
+  % if the group doesn't fit on the current page, and it's a big big
+  % group, force a page break.
+  \ifdim \dimen0 > \dimen2
+    \ifdim \pagetotal < \vfilllimit\pageheight
+      \page
+    \fi
+  \fi
+  \box\groupbox
+}
+
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil  \mil=0.001in
+
+\parseargdef\need{%
+  % Ensure vertical mode, so we don't make a big box in the middle of a
+  % paragraph.
+  \par
+  %
+  % If the @need value is less than one line space, it's useless.
+  \dimen0 = #1\mil
+  \dimen2 = \ht\strutbox
+  \advance\dimen2 by \dp\strutbox
+  \ifdim\dimen0 > \dimen2
+    %
+    % Do a \strut just to make the height of this box be normal, so the
+    % normal leading is inserted relative to the preceding line.
+    % And a page break here is fine.
+    \vtop to #1\mil{\strut\vfil}%
+    %
+    % TeX does not even consider page breaks if a penalty added to the
+    % main vertical list is 10000 or more.  But in order to see if the
+    % empty box we just added fits on the page, we must make it consider
+    % page breaks.  On the other hand, we don't want to actually break the
+    % page after the empty box.  So we use a penalty of 9999.
+    %
+    % There is an extremely small chance that TeX will actually break the
+    % page at this \penalty, if there are no other feasible breakpoints in
+    % sight.  (If the user is using lots of big @group commands, which
+    % almost-but-not-quite fill up a page, TeX will have a hard time doing
+    % good page breaking, for example.)  However, I could not construct an
+    % example where a page broke at this \penalty; if it happens in a real
+    % document, then we can reconsider our strategy.
+    \penalty9999
+    %
+    % Back up by the size of the box, whether we did a page break or not.
+    \kern -#1\mil
+    %
+    % Do not allow a page break right after this kern.
+    \nobreak
+  \fi
+}
+
+% @br   forces paragraph break (and is undocumented).
+
+\let\br = \par
+
+% @page forces the start of a new page.
+%
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
+
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+  \leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph.  For more general purposes, use the \margin insertion
+% class.  WHICH is `l' or `r'.  Not documented, written for gawk manual.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+  \nobreak
+  \kern-\strutdepth
+  \vtop to \strutdepth{%
+    \baselineskip=\strutdepth
+    \vss
+    % if you have multiple lines of stuff to put here, you'll need to
+    % make the vbox yourself of the appropriate size.
+    \ifx#1l%
+      \llap{\ignorespaces #2\hskip\inmarginspacing}%
+    \else
+      \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+    \fi
+    \null
+  }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+  \setbox0 = \hbox{\ignorespaces #2}%
+  \ifdim\wd0 > 0pt
+    \def\lefttext{#1}%  have both texts
+    \def\righttext{#2}%
+  \else
+    \def\lefttext{#1}%  have only one text
+    \def\righttext{#1}%
+  \fi
+  %
+  \ifodd\pageno
+    \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+  \else
+    \def\temp{\inleftmargin\lefttext}%
+  \fi
+  \temp
+}
+
+% @| inserts a changebar to the left of the current line.  It should
+% surround any changed text.  This approach does *not* work if the
+% change spans more than two lines of output.  To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).  This command
+% is not documented, not supported, and doesn't work.
+%
+\def\|{%
+  % \vadjust can only be used in horizontal mode.
+  \leavevmode
+  %
+  % Append this vertical mode material after the current line in the output.
+  \vadjust{%
+    % We want to insert a rule with the height and depth of the current
+    % leading; that is exactly what \strutbox is supposed to record.
+    \vskip-\baselineskip
+    %
+    % \vadjust-items are inserted at the left edge of the type.  So
+    % the \llap here moves out into the left-hand margin.
+    \llap{%
+      %
+      % For a thicker or thinner bar, change the `1pt'.
+      \vrule height\baselineskip width1pt
+      %
+      % This is the space between the bar and the text.
+      \hskip 12pt
+    }%
+  }%
+}
+
+% @include FILE -- \input text of FILE.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+  \pushthisfilestack
+  \def\thisfile{#1}%
+  {%
+    \makevalueexpandable  % we want to expand any @value in FILE.
+    \turnoffactive        % and allow special characters in the expansion
+    \indexnofonts         % Allow `@@' and other weird things in file names.
+    \wlog{texinfo.tex: doing @include of #1^^J}%
+    \edef\temp{\noexpand\input #1 }%
+    %
+    % This trickery is to read FILE outside of a group, in case it makes
+    % definitions, etc.
+    \expandafter
+  }\temp
+  \popthisfilestack
+}
+\def\filenamecatcodes{%
+  \catcode`\\=\other
+  \catcode`~=\other
+  \catcode`^=\other
+  \catcode`_=\other
+  \catcode`|=\other
+  \catcode`<=\other
+  \catcode`>=\other
+  \catcode`+=\other
+  \catcode`-=\other
+  \catcode`\`=\other
+  \catcode`\'=\other
+}
+
+\def\pushthisfilestack{%
+  \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+  \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+  \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
+
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+  the stack of filenames is empty.}}
+%
+\def\thisfile{}
+
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+  \ifhmode
+    \let\centersub\centerH
+  \else
+    \let\centersub\centerV
+  \fi
+  \centersub{\hfil \ignorespaces#1\unskip \hfil}%
+  \let\centersub\relax % don't let the definition persist, just in case
+}
+\def\centerH#1{{%
+  \hfil\break
+  \advance\hsize by -\leftskip
+  \advance\hsize by -\rightskip
+  \line{#1}%
+  \break
+}}
+%
+\newcount\centerpenalty
+\def\centerV#1{%
+  % The idea here is the same as in \startdefun, \cartouche, etc.: if
+  % @center is the first thing after a section heading, we need to wipe
+  % out the negative parskip inserted by \sectionheading, but still
+  % prevent a page break here.
+  \centerpenalty = \lastpenalty
+  \ifnum\centerpenalty>10000 \vskip\parskip \fi
+  \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi
+  \line{\kern\leftskip #1\kern\rightskip}%
+}
+
+% @sp n   outputs n lines of vertical space
+%
+\parseargdef\sp{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore  is another way to write a comment
+%
+\def\comment{\begingroup \catcode`\^^M=\active%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other\commentxxx}%
+
+{\catcode`\^^M=\active%
+\gdef\commentxxx#1^^M{\endgroup%
+\futurelet\nexttoken\commentxxxx}%
+\gdef\commentxxxx{\ifx\nexttoken\aftermacro\expandafter\comment\fi}%
+}
+
+\def\c{\begingroup \catcode`\^^M=\active%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\cxxx}
+{\catcode`\^^M=\active \gdef\cxxx#1^^M{\endgroup}}
+% See comment in \scanmacro about why the definitions of @c and @comment differ
+
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \defaultparindent = 0pt
+    \else
+      \defaultparindent = #1em
+    \fi
+  \fi
+  \parindent = \defaultparindent
+}
+
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \lispnarrowing = 0pt
+    \else
+      \lispnarrowing = #1em
+    \fi
+  \fi
+}
+
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading.  If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\noneword
+    \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+  \else\ifx\temp\insertword
+    \let\suppressfirstparagraphindent = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @firstparagraphindent option `\temp'}%
+  \fi\fi
+}
+
+% Here is how we actually suppress indentation.  Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+  \gdef\indent  {\restorefirstparagraphindent \indent}%
+  \gdef\noindent{\restorefirstparagraphindent \noindent}%
+  \global\everypar = {\kern -\parindent \restorefirstparagraphindent}%
+}
+%
+\gdef\restorefirstparagraphindent{%
+  \global\let\indent = \ptexindent
+  \global\let\noindent = \ptexnoindent
+  \global\everypar = {}%
+}
+
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% @setfilename INFO-FILENAME - ignored
+\let\setfilename=\comment
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newbox\boxB
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set).  So we test for \relax and 0 as well as being undefined.
+\ifx\pdfoutput\thisisundefined
+\else
+  \ifx\pdfoutput\relax
+  \else
+    \ifcase\pdfoutput
+    \else
+      \pdftrue
+    \fi
+  \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places.  Thus, we have to
+% double any backslashes.  Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e.  Not good.
+% 
+% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and
+% related messages.  The final outcome is that it is up to the TeX user
+% to double the backslashes and otherwise make the string valid, so
+% that's what we do.  pdftex 1.30.0 (ca.2005) introduced a primitive to
+% do this reliably, so we use it.
+
+% #1 is a control sequence in which to do the replacements,
+% which we \xdef.
+\def\txiescapepdf#1{%
+  \ifx\pdfescapestring\thisisundefined
+    % No primitive available; should we give a warning or log?
+    % Many times it won't matter.
+  \else
+    % The expandable \pdfescapestring primitive escapes parentheses,
+    % backslashes, and other special chars.
+    \xdef#1{\pdfescapestring{#1}}%
+  \fi
+}
+
+\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images
+with PDF output, and none of those formats could be found.  (.eps cannot
+be supported due to the design of the PDF format; use regular TeX (DVI
+output) for that.)}
+
+\ifpdf
+  %
+  % Color manipulation macros using ideas from pdfcolor.tex,
+  % except using rgb instead of cmyk; the latter is said to render as a
+  % very dark gray on-screen and a very dark halftone in print, instead
+  % of actual black. The dark red here is dark enough to print on paper as
+  % nearly black, but still distinguishable for online viewing.  We use
+  % black by default, though.
+  \def\rgbDarkRed{0.50 0.09 0.12}
+  \def\rgbBlack{0 0 0}
+  %
+  % rg sets the color for filling (usual text, etc.);
+  % RG sets the color for stroking (thin rules, e.g., normal _'s).
+  \def\pdfsetcolor#1{\pdfliteral{#1 rg  #1 RG}}
+  %
+  % Set color, and create a mark which defines \thiscolor accordingly,
+  % so that \makeheadline knows which color to restore.
+  \def\setcolor#1{%
+    \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}%
+    \domark
+    \pdfsetcolor{#1}%
+  }
+  %
+  \def\maincolor{\rgbBlack}
+  \pdfsetcolor{\maincolor}
+  \edef\thiscolor{\maincolor}
+  \def\lastcolordefs{}
+  %
+  \def\makefootline{%
+    \baselineskip24pt
+    \line{\pdfsetcolor{\maincolor}\the\footline}%
+  }
+  %
+  \def\makeheadline{%
+    \vbox to 0pt{%
+      \vskip-22.5pt
+      \line{%
+        \vbox to8.5pt{}%
+        % Extract \thiscolor definition from the marks.
+        \getcolormarks
+        % Typeset the headline with \maincolor, then restore the color.
+        \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}%
+      }%
+      \vss
+    }%
+    \nointerlineskip
+  }
+  %
+  %
+  \pdfcatalog{/PageMode /UseOutlines}
+  %
+  % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+  \def\dopdfimage#1#2#3{%
+    \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+    \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+    %
+    % pdftex (and the PDF format) support .pdf, .png, .jpg (among
+    % others).  Let's try in that order, PDF first since if
+    % someone has a scalable image, presumably better to use that than a
+    % bitmap.
+    \let\pdfimgext=\empty
+    \begingroup
+      \openin 1 #1.pdf \ifeof 1
+        \openin 1 #1.PDF \ifeof 1
+          \openin 1 #1.png \ifeof 1
+            \openin 1 #1.jpg \ifeof 1
+              \openin 1 #1.jpeg \ifeof 1
+                \openin 1 #1.JPG \ifeof 1
+                  \errhelp = \nopdfimagehelp
+                  \errmessage{Could not find image file #1 for pdf}%
+                \else \gdef\pdfimgext{JPG}%
+                \fi
+              \else \gdef\pdfimgext{jpeg}%
+              \fi
+            \else \gdef\pdfimgext{jpg}%
+            \fi
+          \else \gdef\pdfimgext{png}%
+          \fi
+        \else \gdef\pdfimgext{PDF}%
+        \fi
+      \else \gdef\pdfimgext{pdf}%
+      \fi
+      \closein 1
+    \endgroup
+    %
+    % without \immediate, ancient pdftex seg faults when the same image is
+    % included twice.  (Version 3.14159-pre-1.0-unofficial-20010704.)
+    \ifnum\pdftexversion < 14
+      \immediate\pdfimage
+    \else
+      \immediate\pdfximage
+    \fi
+      \ifdim \wd0 >0pt width \pdfimagewidth \fi
+      \ifdim \wd2 >0pt height \pdfimageheight \fi
+      \ifnum\pdftexversion<13
+         #1.\pdfimgext
+       \else
+         {#1.\pdfimgext}%
+       \fi
+    \ifnum\pdftexversion < 14 \else
+      \pdfrefximage \pdflastximage
+    \fi}
+  %
+  \def\pdfmkdest#1{{%
+    % We have to set dummies so commands such as @code, and characters
+    % such as \, aren't expanded when present in a section title.
+    \indexnofonts
+    \turnoffactive
+    \makevalueexpandable
+    \def\pdfdestname{#1}%
+    \txiescapepdf\pdfdestname
+    \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
+  }}
+  %
+  % used to mark target names; must be expandable.
+  \def\pdfmkpgn#1{#1}
+  %
+  % by default, use black for everything.
+  \def\urlcolor{\rgbBlack}
+  \def\linkcolor{\rgbBlack}
+  \def\endlink{\setcolor{\maincolor}\pdfendlink}
+  %
+  % Adding outlines to PDF; macros for calculating structure of outlines
+  % come from Petr Olsak
+  \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+    \else \csname#1\endcsname \fi}
+  \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+    \advance\tempnum by 1
+    \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+  %
+  % #1 is the section text, which is what will be displayed in the
+  % outline by the pdf viewer.  #2 is the pdf expression for the number
+  % of subentries (or empty, for subsubsections).  #3 is the node text,
+  % which might be empty if this toc entry had no corresponding node.
+  % #4 is the page number
+  %
+  \def\dopdfoutline#1#2#3#4{%
+    % Generate a link to the node text if that exists; else, use the
+    % page number.  We could generate a destination for the section
+    % text in the case where a section has no node, but it doesn't
+    % seem worth the trouble, since most documents are normally structured.
+    \edef\pdfoutlinedest{#3}%
+    \ifx\pdfoutlinedest\empty
+      \def\pdfoutlinedest{#4}%
+    \else
+      \txiescapepdf\pdfoutlinedest
+    \fi
+    %
+    % Also escape PDF chars in the display string.
+    \edef\pdfoutlinetext{#1}%
+    \txiescapepdf\pdfoutlinetext
+    %
+    \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+  }
+  %
+  \def\pdfmakeoutlines{%
+    \begingroup
+      % Read toc silently, to get counts of subentries for \pdfoutline.
+      \def\partentry##1##2##3##4{}% ignore parts in the outlines
+      \def\numchapentry##1##2##3##4{%
+       \def\thischapnum{##2}%
+       \def\thissecnum{0}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsecentry##1##2##3##4{%
+       \advancenumber{chap\thischapnum}%
+       \def\thissecnum{##2}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsubsecentry##1##2##3##4{%
+       \advancenumber{sec\thissecnum}%
+       \def\thissubsecnum{##2}%
+      }%
+      \def\numsubsubsecentry##1##2##3##4{%
+       \advancenumber{subsec\thissubsecnum}%
+      }%
+      \def\thischapnum{0}%
+      \def\thissecnum{0}%
+      \def\thissubsecnum{0}%
+      %
+      % use \def rather than \let here because we redefine \chapentry et
+      % al. a second time, below.
+      \def\appentry{\numchapentry}%
+      \def\appsecentry{\numsecentry}%
+      \def\appsubsecentry{\numsubsecentry}%
+      \def\appsubsubsecentry{\numsubsubsecentry}%
+      \def\unnchapentry{\numchapentry}%
+      \def\unnsecentry{\numsecentry}%
+      \def\unnsubsecentry{\numsubsecentry}%
+      \def\unnsubsubsecentry{\numsubsubsecentry}%
+      \readdatafile{toc}%
+      %
+      % Read toc second time, this time actually producing the outlines.
+      % The `-' means take the \expnumber as the absolute number of
+      % subentries, which we calculated on our first read of the .toc above.
+      %
+      % We use the node names as the destinations.
+      \def\numchapentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+      \def\numsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+      \def\numsubsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+      \def\numsubsubsecentry##1##2##3##4{% count is always zero
+        \dopdfoutline{##1}{}{##3}{##4}}%
+      %
+      % PDF outlines are displayed using system fonts, instead of
+      % document fonts.  Therefore we cannot use special characters,
+      % since the encoding is unknown.  For example, the eogonek from
+      % Latin 2 (0xea) gets translated to a | character.  Info from
+      % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+      %
+      % TODO this right, we have to translate 8-bit characters to
+      % their "best" equivalent, based on the @documentencoding.  Too
+      % much work for too little return.  Just use the ASCII equivalents
+      % we use for the index sort strings.
+      % 
+      \indexnofonts
+      \setupdatafile
+      % We can have normal brace characters in the PDF outlines, unlike
+      % Texinfo index files.  So set that up.
+      \def\{{\lbracecharliteral}%
+      \def\}{\rbracecharliteral}%
+      \catcode`\\=\active \otherbackslash
+      \input \tocreadfilename
+    \endgroup
+  }
+  {\catcode`[=1 \catcode`]=2
+   \catcode`{=\other \catcode`}=\other
+   \gdef\lbracecharliteral[{]%
+   \gdef\rbracecharliteral[}]%
+  ]
+  %
+  \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+    \ifx\PP\D\let\nextsp\relax
+    \else\let\nextsp\skipspaces
+      \addtokens{\filename}{\PP}%
+      \advance\filenamelength by 1
+    \fi
+    \nextsp}
+  \def\getfilename#1{%
+    \filenamelength=0
+    % If we don't expand the argument now, \skipspaces will get
+    % snagged on things like "@value{foo}".
+    \edef\temp{#1}%
+    \expandafter\skipspaces\temp|\relax
+  }
+  \ifnum\pdftexversion < 14
+    \let \startlink \pdfannotlink
+  \else
+    \let \startlink \pdfstartlink
+  \fi
+  % make a live url in pdf output.
+  \def\pdfurl#1{%
+    \begingroup
+      % it seems we really need yet another set of dummies; have not
+      % tried to figure out what each command should do in the context
+      % of @url.  for now, just make @/ a no-op, that's the only one
+      % people have actually reported a problem with.
+      %
+      \normalturnoffactive
+      \def\@{@}%
+      \let\/=\empty
+      \makevalueexpandable
+      % do we want to go so far as to use \indexnofonts instead of just
+      % special-casing \var here?
+      \def\var##1{##1}%
+      %
+      \leavevmode\setcolor{\urlcolor}%
+      \startlink attr{/Border [0 0 0]}%
+        user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+    \endgroup}
+  \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+  \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+  \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+  \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+  \def\maketoks{%
+    \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+    \ifx\first0\adn0
+    \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+    \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+    \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+    \else
+      \ifnum0=\countA\else\makelink\fi
+      \ifx\first.\let\next=\done\else
+        \let\next=\maketoks
+        \addtokens{\toksB}{\the\toksD}
+        \ifx\first,\addtokens{\toksB}{\space}\fi
+      \fi
+    \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+    \next}
+  \def\makelink{\addtokens{\toksB}%
+    {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+  \def\pdflink#1{%
+    \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+    \setcolor{\linkcolor}#1\endlink}
+  \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+  % non-pdf mode
+  \let\pdfmkdest = \gobble
+  \let\pdfurl = \gobble
+  \let\endlink = \relax
+  \let\setcolor = \gobble
+  \let\pdfsetcolor = \gobble
+  \let\pdfmakeoutlines = \relax
+\fi  % \ifx\pdfoutput
+
+%
+% @image support for XeTeX
+%
+\newif\ifxeteximgpdf
+\ifx\XeTeXrevision\thisisundefined
+\else
+  %
+  % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+  \def\doxeteximage#1#2#3{%
+    \def\xeteximagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+    \def\xeteximageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+    %
+    % XeTeX (and the PDF format) support .pdf, .png, .jpg (among
+    % others).  Let's try in that order, PDF first since if
+    % someone has a scalable image, presumably better to use that than a
+    % bitmap.
+    \let\xeteximgext=\empty
+    \xeteximgpdffalse
+    \begingroup
+      \openin 1 #1.pdf \ifeof 1
+        \openin 1 #1.PDF \ifeof 1
+          \openin 1 #1.png \ifeof 1
+            \openin 1 #1.jpg \ifeof 1
+              \openin 1 #1.jpeg \ifeof 1
+                \openin 1 #1.JPG \ifeof 1
+                  \errmessage{Could not find image file #1 for XeTeX}%
+                \else \gdef\xeteximgext{JPG}%
+                \fi
+              \else \gdef\xeteximgext{jpeg}%
+              \fi
+            \else \gdef\xeteximgext{jpg}%
+            \fi
+          \else \gdef\xeteximgext{png}%
+          \fi
+        \else \gdef\xeteximgext{PDF} \global\xeteximgpdftrue%
+        \fi
+      \else \gdef\xeteximgext{pdf} \global\xeteximgpdftrue%
+      \fi
+      \closein 1
+    \endgroup
+    %
+    \ifxeteximgpdf
+      \XeTeXpdffile "#1".\xeteximgext ""
+    \else
+      \XeTeXpicfile "#1".\xeteximgext ""
+    \fi
+    \ifdim \wd0 >0pt width \xeteximagewidth \fi
+    \ifdim \wd2 >0pt height \xeteximageheight \fi \relax
+  }
+\fi
+
+\message{fonts,}
+
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+  \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+  \csname ten#1\endcsname  % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Unfortunately, we have to override this for titles and the like, since
+% in those cases "rm" is bold.  Sigh.
+\def\rmisbold{\rm\def\curfontstyle{bf}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly.  There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+% can get a sort of poor man's double spacing by redefining this.
+\def\baselinefactor{1}
+%
+\newdimen\textleading
+\def\setleading#1{%
+  \dimen0 = #1\relax
+  \normalbaselineskip = \baselinefactor\dimen0
+  \normallineskip = \lineskipfactor\normalbaselineskip
+  \normalbaselines
+  \setbox\strutbox =\hbox{%
+    \vrule width0pt height\strutheightpercent\baselineskip
+                    depth \strutdepthpercent \baselineskip
+  }%
+}
+
+% PDF CMaps.  See also LaTeX's t1.cmap.
+%
+% do nothing with this by default.
+\expandafter\let\csname cmapOT1\endcsname\gobble
+\expandafter\let\csname cmapOT1IT\endcsname\gobble
+\expandafter\let\csname cmapOT1TT\endcsname\gobble
+
+% if we are producing pdf, and we have \pdffontattr, then define cmaps.
+% (\pdffontattr was introduced many years ago, but people still run
+% older pdftex's; it's easy to conditionalize, so we do.)
+\ifpdf \ifx\pdffontattr\thisisundefined \else
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1-0)
+%%Title: (TeX-OT1-0 TeX OT1 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<23> <26> <0023>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+40 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+%
+% \cmapOT1IT
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1IT-0)
+%%Title: (TeX-OT1IT-0 TeX OT1IT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1IT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1IT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<25> <26> <0025>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+42 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<23> <0023>
+<24> <00A3>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1IT\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+%
+% \cmapOT1TT
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1TT-0)
+%%Title: (TeX-OT1TT-0 TeX OT1TT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1TT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1TT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+5 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<21> <26> <0021>
+<28> <5F> <0028>
+<61> <7E> <0061>
+endbfrange
+32 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <2191>
+<0C> <2193>
+<0D> <0027>
+<0E> <00A1>
+<0F> <00BF>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<20> <2423>
+<27> <2019>
+<60> <2018>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1TT\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+\fi\fi
+
+
+% Set the font macro #1 to the font named \fontprefix#2.
+% #3 is the font's design size, #4 is a scale factor, #5 is the CMap
+% encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit).
+% Example:
+% #1 = \textrm
+% #2 = \rmshape
+% #3 = 10
+% #4 = \mainmagstep
+% #5 = OT1
+%
+\def\setfont#1#2#3#4#5{%
+  \font#1=\fontprefix#2#3 scaled #4
+  \csname cmap#5\endcsname#1%
+}
+% This is what gets called when #5 of \setfont is empty.
+\let\cmap\gobble
+%
+% (end of cmaps)
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\thisisundefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx}               % where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Definitions for a main text size of 11pt.  (The default in Texinfo.)
+%
+\def\definetextfontsizexi{%
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1095}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}{OT1}
+\setfont\deftt\ttshape{10}{\magstep1}{OT1TT}
+\setfont\defsl\slshape{10}{\magstep1}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf
+\let\tenttsl=\defttsl \let\tensl=\defsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}{OT1}
+\setfont\chapit\itbshape{10}{\magstep3}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep3}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT}
+\setfont\chapsf\sfbshape{17}{1000}{OT1}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}{OT1}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+\def\chapecsize{1728}
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}{OT1}
+\setfont\secrmnotbold\rmshape{12}{\magstep1}{OT1}
+\setfont\secit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep2}{OT1}
+\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\secsf\sfbshape{12}{\magstep1}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}{OT1}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+\def\sececsize{1440}
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1}
+\setfont\ssecit\itbshape{10}{1315}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1315}{OT1}
+\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}{OT1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+\def\ssececsize{1200}
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}{OT1}
+\setfont\reducedtt\ttshape{10}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{1000}{OT1}
+\setfont\reducedit\itshape{10}{1000}{OT1IT}
+\setfont\reducedsl\slshape{10}{1000}{OT1}
+\setfont\reducedsf\sfshape{10}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{1000}{OT1}
+\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+\def\reducedecsize{1000}
+
+\textleading = 13.2pt % line spacing for 11pt CM
+\textfonts            % reset the current fonts
+\rm
+} % end of 11pt text font size definitions, \definetextfontsizexi
+
+
+% Definitions to make the main text be 10pt Computer Modern, with
+% section, chapter, etc., sizes following suit.  This is for the GNU
+% Press printing of the Emacs 22 manual.  Maybe other manuals in the
+% future.  Used with @smallbook, which sets the leading to 12pt.
+%
+\def\definetextfontsizex{%
+% Text fonts (10pt).
+\def\textnominalsize{10pt}
+\edef\mainmagstep{1000}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1000}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstephalf}{OT1}
+\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT}
+\setfont\defsl\slshape{10}{\magstephalf}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf
+\let\tensl=\defsl \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter fonts (14.4pt).
+\def\chapnominalsize{14pt}
+\setfont\chaprm\rmbshape{12}{\magstep1}{OT1}
+\setfont\chapit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep2}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\chapsf\sfbshape{12}{\magstep1}{OT1}
+\let\chapbf\chaprm
+\setfont\chapsc\scbshape{10}{\magstep2}{OT1}
+\font\chapi=cmmi12 scaled \magstep1
+\font\chapsy=cmsy10 scaled \magstep2
+\def\chapecsize{1440}
+
+% Section fonts (12pt).
+\def\secnominalsize{12pt}
+\setfont\secrm\rmbshape{12}{1000}{OT1}
+\setfont\secit\itbshape{10}{\magstep1}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep1}{OT1}
+\setfont\sectt\ttbshape{12}{1000}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT}
+\setfont\secsf\sfbshape{12}{1000}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep1}{OT1}
+\font\seci=cmmi12
+\font\secsy=cmsy10 scaled \magstep1
+\def\sececsize{1200}
+
+% Subsection fonts (10pt).
+\def\ssecnominalsize{10pt}
+\setfont\ssecrm\rmbshape{10}{1000}{OT1}
+\setfont\ssecit\itbshape{10}{1000}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1000}{OT1}
+\setfont\ssectt\ttbshape{10}{1000}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT}
+\setfont\ssecsf\sfbshape{10}{1000}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1000}{OT1}
+\font\sseci=cmmi10
+\font\ssecsy=cmsy10
+\def\ssececsize{1000}
+
+% Reduced fonts for @acro in text (9pt).
+\def\reducednominalsize{9pt}
+\setfont\reducedrm\rmshape{9}{1000}{OT1}
+\setfont\reducedtt\ttshape{9}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{900}{OT1}
+\setfont\reducedit\itshape{9}{1000}{OT1IT}
+\setfont\reducedsl\slshape{9}{1000}{OT1}
+\setfont\reducedsf\sfshape{9}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{900}{OT1}
+\setfont\reducedttsl\ttslshape{10}{900}{OT1TT}
+\font\reducedi=cmmi9
+\font\reducedsy=cmsy9
+\def\reducedecsize{0900}
+
+\divide\parskip by 2  % reduce space between paragraphs
+\textleading = 12pt   % line spacing for 10pt CM
+\textfonts            % reset the current fonts
+\rm
+} % end of 10pt text font size definitions, \definetextfontsizex
+
+
+% We provide the user-level command
+%   @fonttextsize 10
+% (or 11) to redefine the text font size.  pt is assumed.
+%
+\def\xiword{11}
+\def\xword{10}
+\def\xwordpt{10pt}
+%
+\parseargdef\fonttextsize{%
+  \def\textsizearg{#1}%
+  %\wlog{doing @fonttextsize \textsizearg}%
+  %
+  % Set \globaldefs so that documents can use this inside @tex, since
+  % makeinfo 4.8 does not support it, but we need it nonetheless.
+  %
+ \begingroup \globaldefs=1
+  \ifx\textsizearg\xword \definetextfontsizex
+  \else \ifx\textsizearg\xiword \definetextfontsizexi
+  \else
+    \errhelp=\EMsimple
+    \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'}
+  \fi\fi
+ \endgroup
+}
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families.  We don't
+% bother to reset \scriptfont and \scriptscriptfont; awaiting user need.
+%
+\def\resetmathfonts{%
+  \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
+  \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
+  \textfont\ttfam=\tentt \textfont\sffam=\tensf
+}
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE.  We do this because \STYLE needs to also set the
+% current \fam for math mode.  Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower).  These relative commands are used
+% in, e.g., the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+\def\textfonts{%
+  \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+  \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+  \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
+  \let\tenttsl=\textttsl
+  \def\curfontsize{text}%
+  \def\lsize{reduced}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+  \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+  \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+  \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+  \let\tenttsl=\titlettsl
+  \def\curfontsize{title}%
+  \def\lsize{chap}\def\lllsize{subsec}%
+  \resetmathfonts \setleading{27pt}}
+\def\titlefont#1{{\titlefonts\rmisbold #1}}
+\def\chapfonts{%
+  \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+  \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+  \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
+  \let\tenttsl=\chapttsl
+  \def\curfontsize{chap}%
+  \def\lsize{sec}\def\lllsize{text}%
+  \resetmathfonts \setleading{19pt}}
+\def\secfonts{%
+  \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+  \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+  \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
+  \let\tenttsl=\secttsl
+  \def\curfontsize{sec}%
+  \def\lsize{subsec}\def\lllsize{reduced}%
+  \resetmathfonts \setleading{17pt}}
+\def\subsecfonts{%
+  \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+  \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+  \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
+  \let\tenttsl=\ssecttsl
+  \def\curfontsize{ssec}%
+  \def\lsize{text}\def\lllsize{small}%
+  \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+  \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+  \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+  \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+  \let\tenttsl=\reducedttsl
+  \def\curfontsize{reduced}%
+  \def\lsize{small}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+  \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+  \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+  \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+  \let\tenttsl=\smallttsl
+  \def\curfontsize{small}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+  \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+  \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+  \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+  \let\tenttsl=\smallerttsl
+  \def\curfontsize{smaller}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{9.5pt}}
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}{OT1}
+\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1}  % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}{OT1}
+\setfont\shortconttt\ttshape{12}{1000}{OT1TT}
+
+% Define these just so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts.  If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+%   8.5x11=86   smallbook=72  a4=90  a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+%   8.5x11=90+  smallbook=80  a4=90+  a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt.  So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+%   8.5x11=71  smallbook=60  a4=75  a5=58
+% --karl, 24jan03.
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\definetextfontsizexi
+
+
+\message{markup,}
+
+% Check if we are currently using a typewriter font.  Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Markup style infrastructure.  \defmarkupstylesetup\INITMACRO will
+% define and register \INITMACRO to be called on markup style changes.
+% \INITMACRO can check \currentmarkupstyle for the innermost
+% style and the set of \ifmarkupSTYLE switches for all styles
+% currently in effect.
+\newif\ifmarkupvar
+\newif\ifmarkupsamp
+\newif\ifmarkupkey
+%\newif\ifmarkupfile % @file == @samp.
+%\newif\ifmarkupoption % @option == @samp.
+\newif\ifmarkupcode
+\newif\ifmarkupkbd
+%\newif\ifmarkupenv % @env == @code.
+%\newif\ifmarkupcommand % @command == @code.
+\newif\ifmarkuptex % @tex (and part of @math, for now).
+\newif\ifmarkupexample
+\newif\ifmarkupverb
+\newif\ifmarkupverbatim
+
+\let\currentmarkupstyle\empty
+
+\def\setupmarkupstyle#1{%
+  \csname markup#1true\endcsname
+  \def\currentmarkupstyle{#1}%
+  \markupstylesetup
+}
+
+\let\markupstylesetup\empty
+
+\def\defmarkupstylesetup#1{%
+  \expandafter\def\expandafter\markupstylesetup
+    \expandafter{\markupstylesetup #1}%
+  \def#1%
+}
+
+% Markup style setup for left and right quotes.
+\defmarkupstylesetup\markupsetuplq{%
+  \expandafter\let\expandafter \temp
+    \csname markupsetuplq\currentmarkupstyle\endcsname
+  \ifx\temp\relax \markupsetuplqdefault \else \temp \fi
+}
+
+\defmarkupstylesetup\markupsetuprq{%
+  \expandafter\let\expandafter \temp
+    \csname markupsetuprq\currentmarkupstyle\endcsname
+  \ifx\temp\relax \markupsetuprqdefault \else \temp \fi
+}
+
+{
+\catcode`\'=\active
+\catcode`\`=\active
+
+\gdef\markupsetuplqdefault{\let`\lq}
+\gdef\markupsetuprqdefault{\let'\rq}
+
+\gdef\markupsetcodequoteleft{\let`\codequoteleft}
+\gdef\markupsetcodequoteright{\let'\codequoteright}
+}
+
+\let\markupsetuplqcode \markupsetcodequoteleft
+\let\markupsetuprqcode \markupsetcodequoteright
+%
+\let\markupsetuplqexample \markupsetcodequoteleft
+\let\markupsetuprqexample \markupsetcodequoteright
+%
+\let\markupsetuplqkbd     \markupsetcodequoteleft
+\let\markupsetuprqkbd     \markupsetcodequoteright
+%
+\let\markupsetuplqsamp \markupsetcodequoteleft
+\let\markupsetuprqsamp \markupsetcodequoteright
+%
+\let\markupsetuplqverb \markupsetcodequoteleft
+\let\markupsetuprqverb \markupsetcodequoteright
+%
+\let\markupsetuplqverbatim \markupsetcodequoteleft
+\let\markupsetuprqverbatim \markupsetcodequoteright
+
+% Allow an option to not use regular directed right quote/apostrophe
+% (char 0x27), but instead the undirected quote from cmtt (char 0x0d).
+% The undirected quote is ugly, so don't make it the default, but it
+% works for pasting with more pdf viewers (at least evince), the
+% lilypond developers report.  xpdf does work with the regular 0x27.
+%
+\def\codequoteright{%
+  \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax
+    \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax
+      '%
+    \else \char'15 \fi
+  \else \char'15 \fi
+}
+%
+% and a similar option for the left quote char vs. a grave accent.
+% Modern fonts display ASCII 0x60 as a grave accent, so some people like
+% the code environments to do likewise.
+%
+\def\codequoteleft{%
+  \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax
+    \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax
+      % [Knuth] pp. 380,381,391
+      % \relax disables Spanish ligatures ?` and !` of \tt font.
+      \relax`%
+    \else \char'22 \fi
+  \else \char'22 \fi
+}
+
+% Commands to set the quote options.
+% 
+\parseargdef\codequoteundirected{%
+  \def\temp{#1}%
+  \ifx\temp\onword
+    \expandafter\let\csname SETtxicodequoteundirected\endcsname
+      = t%
+  \else\ifx\temp\offword
+    \expandafter\let\csname SETtxicodequoteundirected\endcsname
+      = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}%
+  \fi\fi
+}
+%
+\parseargdef\codequotebacktick{%
+  \def\temp{#1}%
+  \ifx\temp\onword
+    \expandafter\let\csname SETtxicodequotebacktick\endcsname
+      = t%
+  \else\ifx\temp\offword
+    \expandafter\let\csname SETtxicodequotebacktick\endcsname
+      = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}%
+  \fi\fi
+}
+
+% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font.
+\def\noligaturesquoteleft{\relax\lq}
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Font commands.
+
+% #1 is the font command (\sl or \it), #2 is the text to slant.
+% If we are in a monospaced environment, however, 1) always use \ttsl,
+% and 2) do not add an italic correction.
+\def\dosmartslant#1#2{%
+  \ifusingtt 
+    {{\ttsl #2}\let\next=\relax}%
+    {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}%
+  \next
+}
+\def\smartslanted{\dosmartslant\sl}
+\def\smartitalic{\dosmartslant\it}
+
+% Output an italic correction unless \next (presumed to be the following
+% character) is such as not to need one.
+\def\smartitaliccorrection{%
+  \ifx\next,%
+  \else\ifx\next-%
+  \else\ifx\next.%
+  \else\ifx\next\.%
+  \else\ifx\next\comma%
+  \else\ptexslash
+  \fi\fi\fi\fi\fi
+  \aftersmartic
+}
+
+% Unconditional use \ttsl, and no ic.  @var is set to this for defuns.
+\def\ttslanted#1{{\ttsl #1}}
+
+% @cite is like \smartslanted except unconditionally use \sl.  We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection}
+
+\def\aftersmartic{}
+\def\var#1{%
+  \let\saveaftersmartic = \aftersmartic
+  \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}%
+  \smartslanted{#1}%
+}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}}              % roman font
+\def\sc#1{{\smallcaps#1}}       % smallcaps font
+\def\ii#1{{\it #1}}             % italic font
+
+% @b, explicit bold.  Also @strong.
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph.  Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1  \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\catcode`@=11
+  \def\plainfrenchspacing{%
+    \sfcode`\.=\@m \sfcode`\?=\@m \sfcode`\!=\@m
+    \sfcode`\:=\@m \sfcode`\;=\@m \sfcode`\,=\@m
+    \def\endofsentencespacefactor{1000}% for @. and friends
+  }
+  \def\plainnonfrenchspacing{%
+    \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+    \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+    \def\endofsentencespacefactor{3000}% for @. and friends
+  }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+% @t, explicit typewriter.
+\def\t#1{%
+  {\tt \rawbackslash \plainfrenchspacing #1}%
+  \null
+}
+
+% @samp.
+\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}}
+
+% @indicateurl is \samp, that is, with quotes.
+\let\indicateurl=\samp
+
+% @code (and similar) prints in typewriter, but with spaces the same
+% size as normal in the surrounding text, without hyphenation, etc.
+% This is a subroutine for that.
+\def\tclose#1{%
+  {%
+    % Change normal interword space to be same as for the current font.
+    \spaceskip = \fontdimen2\font
+    %
+    % Switch to typewriter.
+    \tt
+    %
+    % But `\ ' produces the large typewriter interword space.
+    \def\ {{\spaceskip = 0pt{} }}%
+    %
+    % Turn off hyphenation.
+    \nohyphenation
+    %
+    \rawbackslash
+    \plainfrenchspacing
+    #1%
+  }%
+  \null % reset spacefactor to 1000
+}
+
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% (But see \codedashfinish below.)
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+%
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash. -- rms.
+{
+  \catcode`\-=\active \catcode`\_=\active
+  \catcode`\'=\active \catcode`\`=\active
+  \global\let'=\rq \global\let`=\lq  % default definitions
+  %
+  \global\def\code{\begingroup
+    \setupmarkupstyle{code}%
+    % The following should really be moved into \setupmarkupstyle handlers.
+    \catcode\dashChar=\active  \catcode\underChar=\active
+    \ifallowcodebreaks
+     \let-\codedash
+     \let_\codeunder
+    \else
+     \let-\normaldash
+     \let_\realunder
+    \fi
+    % Given -foo (with a single dash), we do not want to allow a break
+    % after the hyphen.
+    \global\let\codedashprev=\codedash
+    %
+    \codex
+  }
+  %
+  \gdef\codedash{\futurelet\next\codedashfinish}
+  \gdef\codedashfinish{%
+    \normaldash % always output the dash character itself.
+    % 
+    % Now, output a discretionary to allow a line break, unless
+    % (a) the next character is a -, or
+    % (b) the preceding character is a -.
+    % E.g., given --posix, we do not want to allow a break after either -.
+    % Given --foo-bar, we do want to allow a break between the - and the b.
+    \ifx\next\codedash \else
+      \ifx\codedashprev\codedash 
+      \else \discretionary{}{}{}\fi
+    \fi
+    % we need the space after the = for the case when \next itself is a
+    % space token; it would get swallowed otherwise.  As in @code{- a}.
+    \global\let\codedashprev= \next
+  }
+}
+\def\normaldash{-}
+%
+\def\codex #1{\tclose{#1}\endgroup}
+
+\def\codeunder{%
+  % this is all so @math{@code{var_name}+1} can work.  In math mode, _
+  % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+  % will therefore expand the active definition of _, which is us
+  % (inside @code that is), therefore an endless loop.
+  \ifusingtt{\ifmmode
+               \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+             \else\normalunderscore \fi
+             \discretionary{}{}{}}%
+            {\_}%
+}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__.  This is bad.
+% @allowcodebreaks provides a document-level way to turn breaking at -
+% and _ on and off.
+%
+\newif\ifallowcodebreaks  \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\keywordtrue
+    \allowcodebreakstrue
+  \else\ifx\txiarg\keywordfalse
+    \allowcodebreaksfalse
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}%
+  \fi\fi
+}
+
+% For @command, @env, @file, @option quotes seem unnecessary,
+% so use \code rather than \samp.
+\let\command=\code
+\let\env=\code
+\let\file=\code
+\let\option=\code
+
+% @uref (abbreviation for `urlref') aka @url takes an optional
+% (comma-separated) second argument specifying the text to display and
+% an optional third arg as text to display instead of (rather than in
+% addition to) the url itself.  First (mandatory) arg is the url.
+
+% TeX-only option to allow changing PDF output to show only the second
+% arg (if given), and not the url (which is then just the link target).
+\newif\ifurefurlonlylink
+
+% The main macro is \urefbreak, which allows breaking at expected
+% places within the url.  (There used to be another version, which
+% didn't support automatic breaking.)
+\def\urefbreak{\begingroup \urefcatcodes \dourefbreak}
+\let\uref=\urefbreak
+%
+\def\dourefbreak#1{\urefbreakfinish #1,,,\finish}
+\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example
+  \unsepspaces
+  \pdfurl{#1}%
+  \setbox0 = \hbox{\ignorespaces #3}%
+  \ifdim\wd0 > 0pt
+    \unhbox0 % third arg given, show only that
+  \else
+    \setbox0 = \hbox{\ignorespaces #2}% look for second arg
+    \ifdim\wd0 > 0pt
+      \ifpdf
+        \ifurefurlonlylink
+          % PDF plus option to not display url, show just arg
+          \unhbox0             
+        \else
+          % PDF, normally display both arg and url for consistency,
+          % visibility, if the pdf is eventually used to print, etc.
+          \unhbox0\ (\urefcode{#1})%
+        \fi
+      \else
+        \unhbox0\ (\urefcode{#1})% DVI, always show arg and url
+      \fi
+    \else
+      \urefcode{#1}% only url given, so show it
+    \fi
+  \fi
+  \endlink
+\endgroup}
+
+% Allow line breaks around only a few characters (only).
+\def\urefcatcodes{%
+  \catcode`\&=\active \catcode`\.=\active
+  \catcode`\#=\active \catcode`\?=\active
+  \catcode`\/=\active
+}
+{
+  \urefcatcodes
+  %
+  \global\def\urefcode{\begingroup
+    \setupmarkupstyle{code}%
+    \urefcatcodes
+    \let&\urefcodeamp
+    \let.\urefcodedot
+    \let#\urefcodehash
+    \let?\urefcodequest
+    \let/\urefcodeslash
+    \codex
+  }
+  %
+  % By default, they are just regular characters.
+  \global\def&{\normalamp}
+  \global\def.{\normaldot}
+  \global\def#{\normalhash}
+  \global\def?{\normalquest}
+  \global\def/{\normalslash}
+}
+
+% we put a little stretch before and after the breakable chars, to help
+% line breaking of long url's.  The unequal skips make look better in
+% cmtt at least, especially for dots.
+\def\urefprestretchamount{.13em}
+\def\urefpoststretchamount{.1em}
+\def\urefprestretch{\urefprebreak \hskip0pt plus\urefprestretchamount\relax}
+\def\urefpoststretch{\urefpostbreak \hskip0pt plus\urefprestretchamount\relax}
+%
+\def\urefcodeamp{\urefprestretch \&\urefpoststretch}
+\def\urefcodedot{\urefprestretch .\urefpoststretch}
+\def\urefcodehash{\urefprestretch \#\urefpoststretch}
+\def\urefcodequest{\urefprestretch ?\urefpoststretch}
+\def\urefcodeslash{\futurelet\next\urefcodeslashfinish}
+{
+  \catcode`\/=\active
+  \global\def\urefcodeslashfinish{%
+    \urefprestretch \slashChar
+    % Allow line break only after the final / in a sequence of
+    % slashes, to avoid line break between the slashes in http://.
+    \ifx\next/\else \urefpoststretch \fi
+  }
+}
+
+% One more complication: by default we'll break after the special
+% characters, but some people like to break before the special chars, so
+% allow that.  Also allow no breaking at all, for manual control.
+% 
+\parseargdef\urefbreakstyle{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\wordnone
+    \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak}
+  \else\ifx\txiarg\wordbefore
+    \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak}
+  \else\ifx\txiarg\wordafter
+    \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak}
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @urefbreakstyle setting `\txiarg'}%
+  \fi\fi\fi
+}
+\def\wordafter{after}
+\def\wordbefore{before}
+\def\wordnone{none}
+
+\urefbreakstyle after
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+  \def\email#1{\doemail#1,,\finish}
+  \def\doemail#1,#2,#3\finish{\begingroup
+    \unsepspaces
+    \pdfurl{mailto:#1}%
+    \setbox0 = \hbox{\ignorespaces #2}%
+    \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+    \endlink
+  \endgroup}
+\else
+  \let\email=\uref
+\fi
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+%   `example' (@kbd uses ttsl only inside of @example and friends),
+%   or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\worddistinct
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+  \else\ifx\txiarg\wordexample
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+  \else\ifx\txiarg\wordcode
+    \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @kbdinputstyle setting `\txiarg'}%
+  \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct'.
+\kbdinputstyle distinct
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+\def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}}
+
+\def\xkey{\key}
+\def\kbdsub#1#2#3\par{%
+  \def\one{#1}\def\three{#3}\def\threex{??}%
+  \ifx\one\xkey\ifx\threex\three \key{#2}%
+  \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+  \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+}
+
+% definition of @key that produces a lozenge.  Doesn't adjust to text size.
+%\setfont\keyrm\rmshape{8}{1000}{OT1}
+%\font\keysy=cmsy9
+%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+%  \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+%    \vbox{\hrule\kern-0.4pt
+%     \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+%    \kern-0.4pt\hrule}%
+%  \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+
+% definition of @key with no lozenge.  If the current font is already
+% monospace, don't change it; that way, we respect @kbdinputstyle.  But
+% if it isn't monospace, then use \tt.
+%
+\def\key#1{{\setupmarkupstyle{key}%
+  \nohyphenation
+  \ifmonospace\else\tt\fi
+  #1}\null}
+
+% @clicksequence{File @click{} Open ...}
+\def\clicksequence#1{\begingroup #1\endgroup}
+
+% @clickstyle @arrow   (by default)
+\parseargdef\clickstyle{\def\click{#1}}
+\def\click{\arrow}
+
+% Typeset a dimension, e.g., `in' or `pt'.  The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+%
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+  {\selectfonts\lsize #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+  \null % reset \spacefactor=1000
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+%
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+  {\plainfrenchspacing #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+  \null % reset \spacefactor=1000
+}
+
+% @asis just yields its argument.  Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}.  So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+  \catcode`\_ = \active
+  \gdef\mathunderscore{%
+    \catcode`\_=\active
+    \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+  }
+}
+% Another complication: we want \\ (and @\) to output a math (or tt) \.
+% FYI, plain.tex uses \\ as a temporary control sequence (for no
+% particular reason), but this is not advertised and we don't care.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+  \ifmmode\else % only go into math if not in math mode already
+    \tex
+    \mathunderscore
+    \let\\ = \mathbackslash
+    \mathactive
+    % make the texinfo accent commands work in math mode
+    \let\"=\ddot
+    \let\'=\acute
+    \let\==\bar
+    \let\^=\hat
+    \let\`=\grave
+    \let\u=\breve
+    \let\v=\check
+    \let\~=\tilde
+    \let\dotaccent=\dot
+    % have to provide another name for sup operator
+    \let\mathopsup=\sup
+  $\expandafter\finishmath\fi
+}
+\def\finishmath#1{#1$\endgroup}  % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+  \catcode`^ = \active
+  \catcode`< = \active
+  \catcode`> = \active
+  \catcode`+ = \active
+  \catcode`' = \active
+  \gdef\mathactive{%
+    \let^ = \ptexhat
+    \let< = \ptexless
+    \let> = \ptexgtr
+    \let+ = \ptexplus
+    \let' = \ptexquoteright
+  }
+}
+
+% for @sub and @sup, if in math mode, just do a normal sub/superscript.
+% If in text, use math to place as sub/superscript, but switch
+% into text mode, with smaller fonts.  This is a different font than the
+% one used for real math sub/superscripts (8pt vs. 7pt), but let's not
+% fix it (significant additions to font machinery) until someone notices.
+%
+\def\sub{\ifmmode \expandafter\sb \else \expandafter\finishsub\fi}
+\def\finishsub#1{$\sb{\hbox{\selectfonts\lllsize #1}}$}%
+%
+\def\sup{\ifmmode \expandafter\ptexsp \else \expandafter\finishsup\fi}
+\def\finishsup#1{$\ptexsp{\hbox{\selectfonts\lllsize #1}}$}%
+
+% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}.
+% Ignore unless FMTNAME == tex; then it is like @iftex and @tex,
+% except specified as a normal braced arg, so no newlines to worry about.
+% 
+\def\outfmtnametex{tex}
+%
+\long\def\inlinefmt#1{\doinlinefmt #1,\finish}
+\long\def\doinlinefmt#1,#2,\finish{%
+  \def\inlinefmtname{#1}%
+  \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi
+}
+% 
+% @inlinefmtifelse{FMTNAME,THEN-TEXT,ELSE-TEXT} expands THEN-TEXT if
+% FMTNAME is tex, else ELSE-TEXT.
+\long\def\inlinefmtifelse#1{\doinlinefmtifelse #1,,,\finish}
+\long\def\doinlinefmtifelse#1,#2,#3,#4,\finish{%
+  \def\inlinefmtname{#1}%
+  \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\else \ignorespaces #3\fi
+}
+%
+% For raw, must switch into @tex before parsing the argument, to avoid
+% setting catcodes prematurely.  Doing it this way means that, for
+% example, @inlineraw{html, foo{bar} gets a parse error instead of being
+% ignored.  But this isn't important because if people want a literal
+% *right* brace they would have to use a command anyway, so they may as
+% well use a command to get a left brace too.  We could re-use the
+% delimiter character idea from \verb, but it seems like overkill.
+% 
+\long\def\inlineraw{\tex \doinlineraw}
+\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish}
+\def\doinlinerawtwo#1,#2,\finish{%
+  \def\inlinerawname{#1}%
+  \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi
+  \endgroup % close group opened by \tex.
+}
+
+% @inlineifset{VAR, TEXT} expands TEXT if VAR is @set.
+%
+\long\def\inlineifset#1{\doinlineifset #1,\finish}
+\long\def\doinlineifset#1,#2,\finish{%
+  \def\inlinevarname{#1}%
+  \expandafter\ifx\csname SET\inlinevarname\endcsname\relax
+  \else\ignorespaces#2\fi
+}
+
+% @inlineifclear{VAR, TEXT} expands TEXT if VAR is not @set.
+%
+\long\def\inlineifclear#1{\doinlineifclear #1,\finish}
+\long\def\doinlineifclear#1,#2,\finish{%
+  \def\inlinevarname{#1}%
+  \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \ignorespaces#2\fi
+}
+
+
+\message{glyphs,}
+% and logos.
+
+% @@ prints an @, as does @atchar{}.
+\def\@{\char64 }
+\let\atchar=\@
+
+% @{ @} @lbracechar{} @rbracechar{} all generate brace characters.
+% Unless we're in typewriter, use \ecfont because the CM text fonts do
+% not have braces, and we don't want to switch into math.
+\def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}}
+\def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}}
+\let\{=\mylbrace \let\lbracechar=\{
+\let\}=\myrbrace \let\rbracechar=\}
+\begingroup
+  % Definitions to produce \{ and \} commands for indices,
+  % and @{ and @} for the aux/toc files.
+  \catcode`\{ = \other \catcode`\} = \other
+  \catcode`\[ = 1 \catcode`\] = 2
+  \catcode`\! = 0 \catcode`\\ = \other
+  !gdef!lbracecmd[\{]%
+  !gdef!rbracecmd[\}]%
+  !gdef!lbraceatcmd[@{]%
+  !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \ptexc
+\let\dotaccent = \ptexdot
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \ptext
+\let\ubaraccent = \ptexb
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+  \def\temp{#1}%
+  \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi
+  \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi
+  \else \errmessage{@dotless can be used only with i or j}%
+  \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence.  (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo.  Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+  L\kern-.36em
+  {\setbox0=\hbox{T}%
+   \vbox to \ht0{\hbox{%
+     \ifx\textnominalsize\xwordpt
+       % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX.
+       % Revert to plain's \scriptsize, which is 7pt.
+       \count255=\the\fam $\fam\count255 \scriptstyle A$%
+     \else
+       % For 11pt, we can use our lllsize.
+       \selectfonts\lllsize A%
+     \fi
+     }%
+     \vss
+  }}%
+  \kern-.15em
+  \TeX
+}
+
+% Some math mode symbols.  Define \ensuremath to switch into math mode
+% unless we are already there.  Expansion tricks may not be needed here,
+% but safer, and can't hurt.
+\def\ensuremath{\ifmmode \expandafter\asis \else\expandafter\ensuredmath \fi}
+\def\ensuredmath#1{$\relax#1$}
+%
+\def\bullet{\ensuremath\ptexbullet}
+\def\geq{\ensuremath\ge}
+\def\leq{\ensuremath\le}
+\def\minus{\ensuremath-}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in the cm
+% typewriter fonts as three actual period characters; on the other hand,
+% in other typewriter fonts three periods are wider than 1.5em.  So do
+% whichever is larger.
+%
+\def\dots{%
+  \leavevmode
+  \setbox0=\hbox{...}% get width of three periods
+  \ifdim\wd0 > 1.5em
+    \dimen0 = \wd0
+  \else
+    \dimen0 = 1.5em
+  \fi
+  \hbox to \dimen0{%
+    \hskip 0pt plus.25fil
+    .\hskip 0pt plus1fil
+    .\hskip 0pt plus1fil
+    .\hskip 0pt plus.5fil
+  }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+  \dots
+  \spacefactor=\endofsentencespacefactor
+}
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% Since these characters are used in examples, they should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+%
+\def\point{$\star$}
+\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}}
+\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt}
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
+   \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+   \advance\hsize by -2\dimen2 % Rules.
+   \vbox{%
+      \hrule height\dimen2
+      \hbox{\vrule width\dimen2 \kern3pt          % Space to left of text.
+         \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+         \kern3pt\vrule width\dimen2}% Space to right.
+      \hrule height\dimen2}
+    \hfil}
+%
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+%
+% Although only regular is the truly official Euro symbol, we ignore
+% that.  The Euro is designed to be slightly taller than the regular
+% font height.
+%
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+%
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+%
+% Also doesn't work in math.  Do we need to do math with euro symbols?
+% Hope not.
+%
+%
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+  % We set the font at each command, rather than predefining it in
+  % \textfonts and the other font-switching commands, so that
+  % installations which never need the symbol don't have to have the
+  % font installed.
+  %
+  % There is only one designed size (nominal 10pt), so we always scale
+  % that to the current nominal size.
+  %
+  % By the way, simply using "at 1em" works for cmr10 and the like, but
+  % does not work for cmbx10 and other extended/shrunken fonts.
+  %
+  \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+  %
+  \ifx\curfontstyle\bfstylename
+    % bold:
+    \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+  \else
+    % regular:
+    \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+  \fi
+  \thiseurofont
+}
+
+% Glyphs from the EC fonts.  We don't use \let for the aliases, because
+% sometimes we redefine the original macro, and the alias should reflect
+% the redefinition.
+%
+% Use LaTeX names for the Icelandic letters.
+\def\DH{{\ecfont \char"D0}} % Eth
+\def\dh{{\ecfont \char"F0}} % eth
+\def\TH{{\ecfont \char"DE}} % Thorn
+\def\th{{\ecfont \char"FE}} % thorn
+%
+\def\guillemetleft{{\ecfont \char"13}}
+\def\guillemotleft{\guillemetleft}
+\def\guillemetright{{\ecfont \char"14}}
+\def\guillemotright{\guillemetright}
+\def\guilsinglleft{{\ecfont \char"0E}}
+\def\guilsinglright{{\ecfont \char"0F}}
+\def\quotedblbase{{\ecfont \char"12}}
+\def\quotesinglbase{{\ecfont \char"0D}}
+%
+% This positioning is not perfect (see the ogonek LaTeX package), but
+% we have the precomposed glyphs for the most common cases.  We put the
+% tests to use those glyphs in the single \ogonek macro so we have fewer
+% dummy definitions to worry about for index entries, etc.
+%
+% ogonek is also used with other letters in Lithuanian (IOU), but using
+% the precomposed glyphs for those is not so easy since they aren't in
+% the same EC font.
+\def\ogonek#1{{%
+  \def\temp{#1}%
+  \ifx\temp\macrocharA\Aogonek
+  \else\ifx\temp\macrochara\aogonek
+  \else\ifx\temp\macrocharE\Eogonek
+  \else\ifx\temp\macrochare\eogonek
+  \else
+    \ecfont \setbox0=\hbox{#1}%
+    \ifdim\ht0=1ex\accent"0C #1%
+    \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}%
+    \fi
+  \fi\fi\fi\fi
+  }%
+}
+\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A}
+\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a}
+\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E}
+\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e}
+%
+% Use the European Computer Modern fonts (cm-super in outline format)
+% for non-CM glyphs.  That is ec* for regular text and tc* for the text
+% companion symbols (LaTeX TS1 encoding).  Both are part of the ec
+% package and follow the same conventions.
+% 
+\def\ecfont{\etcfont{e}}
+\def\tcfont{\etcfont{t}}
+%
+\def\etcfont#1{%
+  % We can't distinguish serif/sans and italic/slanted, but this
+  % is used for crude hacks anyway (like adding French and German
+  % quotes to documents typeset with CM, where we lose kerning), so
+  % hopefully nobody will notice/care.
+  \edef\ecsize{\csname\curfontsize ecsize\endcsname}%
+  \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}%
+  \ifmonospace
+    % typewriter:
+    \font\thisecfont = #1ctt\ecsize \space at \nominalsize
+  \else
+    \ifx\curfontstyle\bfstylename
+      % bold:
+      \font\thisecfont = #1cb\ifusingit{i}{x}\ecsize \space at \nominalsize
+    \else
+      % regular:
+      \font\thisecfont = #1c\ifusingit{ti}{rm}\ecsize \space at \nominalsize
+    \fi
+  \fi
+  \thisecfont
+}
+
+% @registeredsymbol - R in a circle.  The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+  $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+               \hfil\crcr\Orb}}%
+    }$%
+}
+
+% @textdegree - the normal degrees sign.
+%
+\def\textdegree{$^\circ$}
+
+% Laurent Siebenmann reports \Orb undefined with:
+%  Textures 1.7.7 (preloaded format=plain 93.10.14)  (68K)  16 APR 2004 02:38
+% so we'll define it if necessary.
+%
+\ifx\Orb\thisisundefined
+\def\Orb{\mathhexbox20D}
+\fi
+
+% Quotes.
+\chardef\quotedblleft="5C
+\chardef\quotedblright=`\"
+\chardef\quoteleft=`\`
+\chardef\quoteright=`\'
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page.  Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{%
+  \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+  \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+  % Open one extra group, as we want to close it in the middle of \Etitlepage.
+  \begingroup
+    \parindent=0pt \textfonts
+    % Leave some space at the very top of the page.
+    \vglue\titlepagetopglue
+    % No rule at page bottom unless we print one at the top with @title.
+    \finishedtitlepagetrue
+    %
+    % Most title ``pages'' are actually two pages long, with space
+    % at the top of the second.  We don't want the ragged left on the second.
+    \let\oldpage = \page
+    \def\page{%
+      \iffinishedtitlepage\else
+        \finishtitlepage
+      \fi
+      \let\page = \oldpage
+      \page
+      \null
+    }%
+}
+
+\def\Etitlepage{%
+    \iffinishedtitlepage\else
+       \finishtitlepage
+    \fi
+    % It is important to do the page break before ending the group,
+    % because the headline and footline are only empty inside the group.
+    % If we use the new definition of \page, we always get a blank page
+    % after the title page, which we certainly don't want.
+    \oldpage
+  \endgroup
+  %
+  % Need this before the \...aftertitlepage checks so that if they are
+  % in effect the toc pages will come out with page numbers.
+  \HEADINGSon
+  %
+  % If they want short, they certainly want long too.
+  \ifsetshortcontentsaftertitlepage
+    \shortcontents
+    \contents
+    \global\let\shortcontents = \relax
+    \global\let\contents = \relax
+  \fi
+  %
+  \ifsetcontentsaftertitlepage
+    \contents
+    \global\let\contents = \relax
+    \global\let\shortcontents = \relax
+  \fi
+}
+
+\def\finishtitlepage{%
+  \vskip4pt \hrule height 2pt width \hsize
+  \vskip\titlepagebottomglue
+  \finishedtitlepagetrue
+}
+
+% Settings used for typesetting titles: no hyphenation, no indentation,
+% don't worry much about spacing, ragged right.  This should be used
+% inside a \vbox, and fonts need to be set appropriately first.  Because
+% it is always used for titles, nothing else, we call \rmisbold.  \par
+% should be specified before the end of the \vbox, since a vbox is a group.
+% 
+\def\raggedtitlesettings{%
+  \rmisbold
+  \hyphenpenalty=10000
+  \parindent=0pt
+  \tolerance=5000
+  \ptexraggedright
+}
+
+% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\parseargdef\title{%
+  \checkenv\titlepage
+  \vbox{\titlefonts \raggedtitlesettings #1\par}%
+  % print a rule at the page bottom also.
+  \finishedtitlepagefalse
+  \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+  \checkenv\titlepage
+  {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+  \def\temp{\quotation}%
+  \ifx\thisenv\temp
+    \def\quotationauthor{#1}% printed in \Equotation.
+  \else
+    \checkenv\titlepage
+    \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+    {\secfonts\rmisbold \leftline{#1}}%
+  \fi
+}
+
+
+% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks\evenheadline    % headline on even pages
+\newtoks\oddheadline     % headline on odd pages
+\newtoks\evenfootline    % footline on even pages
+\newtoks\oddfootline     % footline on odd pages
+
+% Now make \makeheadline and \makefootline in Plain TeX use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+                            \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+                            \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what  @headings on  does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+  \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+  %
+  % Leave some space for the footline.  Hopefully ok to assume
+  % @evenfooting will not be used by itself.
+  \global\advance\pageheight by -12pt
+  \global\advance\vsize by -12pt
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+
+% @evenheadingmarks top     \thischapter <- chapter at the top of a page
+% @evenheadingmarks bottom  \thischapter <- chapter at the bottom of a page
+%
+% The same set of arguments for:
+%
+% @oddheadingmarks
+% @evenfootingmarks
+% @oddfootingmarks
+% @everyheadingmarks
+% @everyfootingmarks
+
+% These define \getoddheadingmarks, \getevenheadingmarks,
+% \getoddfootingmarks, and \getevenfootingmarks, each to one of
+% \gettopheadingmarks, \getbottomheadingmarks.
+%
+\def\evenheadingmarks{\headingmarks{even}{heading}}
+\def\oddheadingmarks{\headingmarks{odd}{heading}}
+\def\evenfootingmarks{\headingmarks{even}{footing}}
+\def\oddfootingmarks{\headingmarks{odd}{footing}}
+\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1}
+                          \headingmarks{odd}{heading}{#1} }
+\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1}
+                          \headingmarks{odd}{footing}{#1} }
+% #1 = even/odd, #2 = heading/footing, #3 = top/bottom.
+\def\headingmarks#1#2#3 {%
+  \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname
+  \global\expandafter\let\csname get#1#2marks\endcsname \temp
+}
+
+\everyheadingmarks bottom
+\everyfootingmarks bottom
+
+% @headings double      turns headings on for double-sided printing.
+% @headings single      turns headings on for single-sided printing.
+% @headings off         turns them off.
+% @headings on          same as @headings double, retained for compatibility.
+% @headings after       turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\headingsoff{% non-global headings elimination
+  \evenheadline={\hfil}\evenfootline={\hfil}%
+   \oddheadline={\hfil}\oddfootline={\hfil}%
+}
+
+\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting
+\HEADINGSoff  % it's the default
+
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapterheading\hfil\folio}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapterheading\hfil\folio}}
+\global\oddheadline={\line{\thischapterheading\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\thisisundefined
+\def\today{%
+  \number\day\space
+  \ifcase\month
+  \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+  \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+  \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+  \fi
+  \space\number\year}
+\fi
+
+% @settitle line...  specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
+
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent  \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin  \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\itemzzz #1{\begingroup %
+  \advance\hsize by -\rightskip
+  \advance\hsize by -\tableindent
+  \setbox0=\hbox{\itemindicate{#1}}%
+  \itemindex{#1}%
+  \nobreak % This prevents a break before @itemx.
+  %
+  % If the item text does not fit in the space we have, put it on a line
+  % by itself, and do not allow a page break either before or after that
+  % line.  We do not start a paragraph here because then if the next
+  % command is, e.g., @kindex, the whatsit would get put into the
+  % horizontal list on a line by itself, resulting in extra blank space.
+  \ifdim \wd0>\itemmax
+    %
+    % Make this a paragraph so we get the \parskip glue and wrapping,
+    % but leave it ragged-right.
+    \begingroup
+      \advance\leftskip by-\tableindent
+      \advance\hsize by\tableindent
+      \advance\rightskip by0pt plus1fil\relax
+      \leavevmode\unhbox0\par
+    \endgroup
+    %
+    % We're going to be starting a paragraph, but we don't want the
+    % \parskip glue -- logically it's part of the @item we just started.
+    \nobreak \vskip-\parskip
+    %
+    % Stop a page break at the \parskip glue coming up.  However, if
+    % what follows is an environment such as @example, there will be no
+    % \parskip glue; then the negative vskip we just inserted would
+    % cause the example and the item to crash together.  So we use this
+    % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+    % \parskip glue after all.  Section titles are handled this way also.
+    %
+    \penalty 10001
+    \endgroup
+    \itemxneedsnegativevskipfalse
+  \else
+    % The item text fits into the space.  Start a paragraph, so that the
+    % following text (if any) will end up on the same line.
+    \noindent
+    % Do this with kerns and \unhbox so that if there is a footnote in
+    % the item text, it can migrate to the main vertical list and
+    % eventually be printed.
+    \nobreak\kern-\tableindent
+    \dimen0 = \itemmax  \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+    \unhbox0
+    \nobreak\kern\dimen0
+    \endgroup
+    \itemxneedsnegativevskiptrue
+  \fi
+}
+
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+  \let\itemindex\gobble
+  \tablecheck{table}%
+}
+\envdef\ftable{%
+  \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+  \tablecheck{ftable}%
+}
+\envdef\vtable{%
+  \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+  \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+  \ifnum \the\catcode`\^^M=\active
+    \endgroup
+    \errmessage{This command won't work in this context; perhaps the problem is
+      that we are \inenvironment\thisenv}%
+    \def\next{\doignore{#1}}%
+  \else
+    \let\next\tablex
+  \fi
+  \next
+}
+\def\tablex#1{%
+  \def\itemindicate{#1}%
+  \parsearg\tabley
+}
+\def\tabley#1{%
+  {%
+    \makevalueexpandable
+    \edef\temp{\noexpand\tablez #1\space\space\space}%
+    \expandafter
+  }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+  \aboveenvbreak
+  \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+  \ifnum 0#2>0 \tableindent=#2\mil \fi
+  \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+  \itemmax=\tableindent
+  \advance \itemmax by -\itemmargin
+  \advance \leftskip by \tableindent
+  \exdentamount=\tableindent
+  \parindent = 0pt
+  \parskip = \smallskipamount
+  \ifdim \parskip=0pt \parskip=2pt \fi
+  \let\item = \internalBitem
+  \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+  \aboveenvbreak
+  \itemmax=\itemindent
+  \advance\itemmax by -\itemmargin
+  \advance\leftskip by \itemindent
+  \exdentamount=\itemindent
+  \parindent=0pt
+  \parskip=\smallskipamount
+  \ifdim\parskip=0pt \parskip=2pt \fi
+  %
+  % Try typesetting the item mark so that if the document erroneously says
+  % something like @itemize @samp (intending @table), there's an error
+  % right away at the @itemize.  It's not the best error message in the
+  % world, but it's better than leaving it to the @item.  This means if
+  % the user wants an empty mark, they have to say @w{} not just @w.
+  \def\itemcontents{#1}%
+  \setbox0 = \hbox{\itemcontents}%
+  %
+  % @itemize with no arg is equivalent to @itemize @bullet.
+  \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+  %
+  \let\item=\itemizeitem
+}
+
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+  \advance\itemno by 1  % for enumerations
+  {\let\par=\endgraf \smallbreak}% reasonable place to break
+  {%
+   % If the document has an @itemize directly after a section title, a
+   % \nobreak will be last on the list, and \sectionheading will have
+   % done a \vskip-\parskip.  In that case, we don't want to zero
+   % parskip, or the item text will crash with the heading.  On the
+   % other hand, when there is normal text preceding the item (as there
+   % usually is), we do want to zero parskip, or there would be too much
+   % space.  In that case, we won't have a \nobreak before.  At least
+   % that's the theory.
+   \ifnum\lastpenalty<10000 \parskip=0in \fi
+   \noindent
+   \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+   %
+   \ifinner\else
+     \vadjust{\penalty 1200}% not good to break after first line of item.
+   \fi
+   % We can be in inner vertical mode in a footnote, although an
+   % @itemize looks awful there.
+  }%
+  \flushcr
+}
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list.  No
+% argument is the same as `1'.
+%
+\envparseargdef\enumerate{\enumeratey #1  \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+  % If we were given no argument, pretend we were given `1'.
+  \def\thearg{#1}%
+  \ifx\thearg\empty \def\thearg{1}\fi
+  %
+  % Detect if the argument is a single token.  If so, it might be a
+  % letter.  Otherwise, the only valid thing it can be is a number.
+  % (We will always have one token, because of the test we just made.
+  % This is a good thing, since \splitoff doesn't work given nothing at
+  % all -- the first parameter is undelimited.)
+  \expandafter\splitoff\thearg\endmark
+  \ifx\rest\empty
+    % Only one token in the argument.  It could still be anything.
+    % A ``lowercase letter'' is one whose \lccode is nonzero.
+    % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+    %   not equal to itself.
+    % Otherwise, we assume it's a number.
+    %
+    % We need the \relax at the end of the \ifnum lines to stop TeX from
+    % continuing to look for a <number>.
+    %
+    \ifnum\lccode\expandafter`\thearg=0\relax
+      \numericenumerate % a number (we hope)
+    \else
+      % It's a letter.
+      \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+        \lowercaseenumerate % lowercase letter
+      \else
+        \uppercaseenumerate % uppercase letter
+      \fi
+    \fi
+  \else
+    % Multiple tokens in the argument.  We hope it's a number.
+    \numericenumerate
+  \fi
+}
+
+% An @enumerate whose labels are integers.  The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+  \itemno = \thearg
+  \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+  \itemno = \expandafter`\thearg
+  \startenumeration{%
+    % Be sure we're not beyond the end of the alphabet.
+    \ifnum\itemno=0
+      \errmessage{No more lowercase letters in @enumerate; get a bigger
+                  alphabet}%
+    \fi
+    \char\lccode\itemno
+  }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+  \itemno = \expandafter`\thearg
+  \startenumeration{%
+    % Be sure we're not beyond the end of the alphabet.
+    \ifnum\itemno=0
+      \errmessage{No more uppercase letters in @enumerate; get a bigger
+                  alphabet}
+    \fi
+    \char\uccode\itemno
+  }%
+}
+
+% Call \doitemize, adding a period to the first argument and supplying the
+% common last two arguments.  Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+  \advance\itemno by -1
+  \doitemize{#1.}\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble.  Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+%   @multitable @columnfractions .25 .3 .45
+%   @item ...
+%
+%   Numbers following @columnfractions are the percent of the total
+%   current hsize to be used for each column. You may use as many
+%   columns as desired.
+
+
+% Or use a template:
+%   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+%   @item ...
+%   using the widest term desired in each column.
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
+
+% Sample multitable:
+
+%   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+%   @item first col stuff @tab second col stuff @tab third col
+%   @item
+%   first col stuff
+%   @tab
+%   second col stuff
+%   @tab
+%   third col
+%   @item first col stuff @tab second col stuff
+%   @tab Many paragraphs of text may be used in any column.
+%
+%         They will wrap at the width determined by the template.
+%   @item@tab@tab This will be in third column.
+%   @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+%                                                            to baseline.
+%   0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+% Macros used to set up halign preamble:
+%
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1.  We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+  \global\advance\colcount by 1
+  \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+  \setuptable
+}
+
+\newcount\colcount
+\def\setuptable#1{%
+  \def\firstarg{#1}%
+  \ifx\firstarg\xendsetuptable
+    \let\go = \relax
+  \else
+    \ifx\firstarg\xcolumnfractions
+      \global\setpercenttrue
+    \else
+      \ifsetpercent
+         \let\go\pickupwholefraction
+      \else
+         \global\advance\colcount by 1
+         \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+                   % separator; typically that is always in the input, anyway.
+         \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+      \fi
+    \fi
+    \ifx\go\pickupwholefraction
+      % Put the argument back for the \pickupwholefraction call, so
+      % we'll always have a period there to be parsed.
+      \def\go{\pickupwholefraction#1}%
+    \else
+      \let\go = \setuptable
+    \fi%
+  \fi
+  \go
+}
+
+% multitable-only commands.
+% 
+% @headitem starts a heading row, which we typeset in bold.  Assignments
+% have to be global since we are inside the implicit group of an
+% alignment entry.  \everycr below resets \everytab so we don't have to
+% undo it ourselves.
+\def\headitemfont{\b}% for people to use in the template row; not changeable
+\def\headitem{%
+  \checkenv\multitable
+  \crcr
+  \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings
+  \global\everytab={\bf}% can't use \headitemfont since the parsing differs
+  \the\everytab % for the first item
+}%
+%
+% default for tables with no headings.
+\let\headitemcrhook=\relax
+%
+% A \tab used to include \hskip1sp.  But then the space in a template
+% line is not enough.  That is bad.  So let's go back to just `&' until
+% we again encounter the problem the 1sp was intended to solve.
+%                                      --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab  % insert after every tab.
+%
+\envdef\multitable{%
+  \vskip\parskip
+  \startsavinginserts
+  %
+  % @item within a multitable starts a normal row.
+  % We use \def instead of \let so that if one of the multitable entries
+  % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+  % \endtemplate) expanding \doitemize.
+  \def\item{\crcr}%
+  %
+  \tolerance=9500
+  \hbadness=9500
+  \setmultitablespacing
+  \parskip=\multitableparskip
+  \parindent=\multitableparindent
+  \overfullrule=0pt
+  \global\colcount=0
+  %
+  \everycr = {%
+    \noalign{%
+      \global\everytab={}% Reset from possible headitem.
+      \global\colcount=0 % Reset the column counter.
+      %
+      % Check for saved footnotes, etc.:
+      \checkinserts
+      %
+      % Perhaps a \nobreak, then reset:
+      \headitemcrhook
+      \global\let\headitemcrhook=\relax
+    }%
+  }%
+  %
+  \parsearg\domultitable
+}
+\def\domultitable#1{%
+  % To parse everything between @multitable and @item:
+  \setuptable#1 \endsetuptable
+  %
+  % This preamble sets up a generic column definition, which will
+  % be used as many times as user calls for columns.
+  % \vtop will set a single line and will also let text wrap and
+  % continue for many paragraphs if desired.
+  \halign\bgroup &%
+    \global\advance\colcount by 1
+    \multistrut
+    \vtop{%
+      % Use the current \colcount to find the correct column width:
+      \hsize=\expandafter\csname col\the\colcount\endcsname
+      %
+      % In order to keep entries from bumping into each other
+      % we will add a \leftskip of \multitablecolspace to all columns after
+      % the first one.
+      %
+      % If a template has been used, we will add \multitablecolspace
+      % to the width of each template entry.
+      %
+      % If the user has set preamble in terms of percent of \hsize we will
+      % use that dimension as the width of the column, and the \leftskip
+      % will keep entries from bumping into each other.  Table will start at
+      % left margin and final column will justify at right margin.
+      %
+      % Make sure we don't inherit \rightskip from the outer environment.
+      \rightskip=0pt
+      \ifnum\colcount=1
+       % The first column will be indented with the surrounding text.
+       \advance\hsize by\leftskip
+      \else
+       \ifsetpercent \else
+         % If user has not set preamble in terms of percent of \hsize
+         % we will advance \hsize by \multitablecolspace.
+         \advance\hsize by \multitablecolspace
+       \fi
+       % In either case we will make \leftskip=\multitablecolspace:
+      \leftskip=\multitablecolspace
+      \fi
+      % Ignoring space at the beginning and end avoids an occasional spurious
+      % blank line, when TeX decides to break the line at the space before the
+      % box from the multistrut, so the strut ends up on a line by itself.
+      % For example:
+      % @multitable @columnfractions .11 .89
+      % @item @code{#}
+      % @tab Legal holiday which is valid in major parts of the whole country.
+      % Is automatically provided with highlighting sequences respectively
+      % marking characters.
+      \noindent\ignorespaces##\unskip\multistrut
+    }\cr
+}
+\def\Emultitable{%
+  \crcr
+  \egroup % end the \halign
+  \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+  \def\multistrut{\strut}% just use the standard line spacing
+  %
+  % Compute \multitablelinespace (if not defined by user) for use in
+  % \multitableparskip calculation.  We used define \multistrut based on
+  % this, but (ironically) that caused the spacing to be off.
+  % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+% Test to see if parskip is larger than space between lines of
+% table. If not, do nothing.
+%        If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller
+                                      % than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller
+                                      % than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed.  They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested.  But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+  \expandafter\let\csname #1\endcsname = \relax
+  \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+  % Scan in ``verbatim'' mode:
+  \obeylines
+  \catcode`\@ = \other
+  \catcode`\{ = \other
+  \catcode`\} = \other
+  %
+  % Make sure that spaces turn into tokens that match what \doignoretext wants.
+  \spaceisspace
+  %
+  % Count number of #1's that we've seen.
+  \doignorecount = 0
+  %
+  % Swallow text until we reach the matching `@end #1'.
+  \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+  \obeylines %
+  %
+  \gdef\dodoignore#1{%
+    % #1 contains the command name as a string, e.g., `ifinfo'.
+    %
+    % Define a command to find the next `@end #1'.
+    \long\def\doignoretext##1^^M@end #1{%
+      \doignoretextyyy##1^^M@#1\_STOP_}%
+    %
+    % And this command to find another #1 command, at the beginning of a
+    % line.  (Otherwise, we would consider a line `@c @ifset', for
+    % example, to count as an @ifset for nesting.)
+    \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+    %
+    % And now expand that command.
+    \doignoretext ^^M%
+  }%
+}
+
+\def\doignoreyyy#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty                      % Nothing found.
+    \let\next\doignoretextzzz
+  \else                                        % Found a nested condition, ...
+    \advance\doignorecount by 1
+    \let\next\doignoretextyyy          % ..., look for another.
+    % If we're here, #1 ends with ^^M\ifinfo (for example).
+  \fi
+  \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+  \ifnum\doignorecount = 0     % We have just found the outermost @end.
+    \let\next\enddoignore
+  \else                                % Still inside a nested condition.
+    \advance\doignorecount by -1
+    \let\next\doignoretext      % Look for the next @end.
+  \fi
+  \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+  % Ignore anything after the last `@end #1'; this matters in verbatim
+  % environments, where otherwise the newline after an ignored conditional
+  % would result in a blank line in the output.
+  \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+  {%
+    \makevalueexpandable
+    \def\temp{#2}%
+    \edef\next{\gdef\makecsname{SET#1}}%
+    \ifx\temp\empty
+      \next{}%
+    \else
+      \setzzz#2\endsetzzz
+    \fi
+  }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+  {%
+    \makevalueexpandable
+    \global\expandafter\let\csname SET#1\endcsname=\relax
+  }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+  \catcode`\-=\active \catcode`\_=\active
+  %
+  \gdef\makevalueexpandable{%
+    \let\value = \expandablevalue
+    % We don't want these characters active, ...
+    \catcode`\-=\other \catcode`\_=\other
+    % ..., but we might end up with active ones in the argument if
+    % we're called from @code, as @code{@value{foo-bar_}}, though.
+    % So \let them to their normal equivalents.
+    \let-\normaldash \let_\normalunderscore
+  }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file.  This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+% 
+% Unfortunately, this has the consequence that when _ is in the *value*
+% of an @set, it does not print properly in the roman fonts (get the cmr
+% dot accent at position 126 instead).  No fix comes to mind, and it's
+% been this way since 2003 or earlier, so just ignore it.
+% 
+\def\expandablevalue#1{%
+  \expandafter\ifx\csname SET#1\endcsname\relax
+    {[No value for ``#1'']}%
+    \message{Variable `#1', used in @value, is not set.}%
+  \else
+    \csname SET#1\endcsname
+  \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+% 
+% To get the special treatment we need for `@end ifset,' we call
+% \makecond and then redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+  {%
+    \makevalueexpandable
+    \let\next=\empty
+    \expandafter\ifx\csname SET#2\endcsname\relax
+      #1% If not set, redefine \next.
+    \fi
+    \expandafter
+  }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end executes the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @ifcommandisdefined CMD ... @end executes the `...' if CMD (written
+% without the @) is in fact defined.  We can only feasibly check at the
+% TeX level, so something like `mathcode' is going to considered
+% defined even though it is not a Texinfo command.
+% 
+\makecond{ifcommanddefined}
+\def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}}
+%
+\def\doifcmddefined#1#2{{%
+    \makevalueexpandable
+    \let\next=\empty
+    \expandafter\ifx\csname #2\endcsname\relax
+      #1% If not defined, \let\next as above.
+    \fi
+    \expandafter
+  }\next
+}
+\def\ifcmddefinedfail{\doignore{ifcommanddefined}}
+
+% @ifcommandnotdefined CMD ... handled similar to @ifclear above.
+\makecond{ifcommandnotdefined}
+\def\ifcommandnotdefined{%
+  \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}}
+\def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}}
+
+% Set the `txicommandconditionals' variable, so documents have a way to
+% test if the @ifcommand...defined conditionals are available.
+\set txicommandconditionals
+
+% @dircategory CATEGORY  -- specify a category of the dir file
+% which this file should belong to.  Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
+
+% \newindex {foo} defines an index named IX.
+% It automatically defines \IXindex such that
+% \IXindex ...rest of line... puts an entry in the index IX.
+% It also defines \IXindfile to be the number of the output channel for
+% the file that accumulates this index.  The file's extension is IX.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+%
+\def\newindex#1{%
+  \expandafter\chardef\csname#1indfile\endcsname=0
+  \expandafter\xdef\csname#1index\endcsname{%     % Define @#1index
+    \noexpand\doindex{#1}}
+}
+
+% @defindex foo  ==  \newindex{foo}
+%
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+  \expandafter\chardef\csname#1indfile\endcsname=0
+  \expandafter\xdef\csname#1index\endcsname{%
+    \noexpand\docodeindex{#1}}%
+}
+
+% The default indices:
+\newindex{cp}%      concepts,
+\newcodeindex{fn}%  functions,
+\newcodeindex{vr}%  variables,
+\newcodeindex{tp}%  types,
+\newcodeindex{ky}%  keys
+\newcodeindex{pg}%  and programs.
+
+
+% @synindex foo bar    makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+%
+% @syncodeindex foo bar   similar, but put all entries made for index foo
+% inside @code.
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+  % Only do \closeout if we haven't already done it, else we'll end up
+  % closing the target index.
+  \expandafter \ifx\csname donesynindex#2\endcsname \relax
+    % The \closeout helps reduce unnecessary open files; the limit on the
+    % Acorn RISC OS is a mere 16 files.
+    \expandafter\closeout\csname#2indfile\endcsname
+    \expandafter\let\csname donesynindex#2\endcsname = 1
+  \fi
+  % redefine \fooindfile:
+  \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+  \expandafter\let\csname#2indfile\endcsname=\temp
+  % redefine \fooindex:
+  \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
+}
+
+% Define \doindex, the driver for all index macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it the two-letter name of the index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx}
+\def\doindexxxx #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx}
+\def\docodeindexxxx #1{\doind{\indexname}{\code{#1}}}
+
+% Used when writing an index entry out to an index file, to prevent
+% expansion of Texinfo commands that can appear in an index entry.
+%
+\def\indexdummies{%
+  \escapechar = `\\     % use backslash in output files.
+  \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+  \def\ {\realbackslash\space }%
+  %
+  % Need these unexpandable (because we define \tt as a dummy)
+  % definitions when @{ or @} appear in index entry text.  Also, more
+  % complicated, when \tex is in effect and \{ is a \delimiter again.
+  % We can't use \lbracecmd and \rbracecmd because texindex assumes
+  % braces and backslashes are used only as delimiters.  Perhaps we
+  % should use @lbracechar and @rbracechar?
+  \def\{{{\tt\char123}}%
+  \def\}{{\tt\char125}}%
+  %
+  % Do the redefinitions.
+  \commondummies
+}
+
+% For the aux and toc files, @ is the escape character.  So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files).  When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+  \def\@{@@}%
+  \def\ {@ }%
+  \let\{ = \lbraceatcmd
+  \let\} = \rbraceatcmd
+  %
+  % Do the redefinitions.
+  \commondummies
+  \otherbackslash
+}
+
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+  % \definedummyword defines \#1 as \string\#1\space, thus effectively
+  % preventing its expansion.  This is used only for control words,
+  % not control letters, because the \space would be incorrect for
+  % control characters, but is needed to separate the control word
+  % from whatever follows.
+  %
+  % For control letters, we have \definedummyletter, which omits the
+  % space.
+  %
+  % These can be used both for control words that take an argument and
+  % those that do not.  If it is followed by {arg} in the input, then
+  % that will dutifully get written to the index (or wherever).
+  %
+  \def\definedummyword  ##1{\def##1{\string##1\space}}%
+  \def\definedummyletter##1{\def##1{\string##1}}%
+  \let\definedummyaccent\definedummyletter
+  %
+  \commondummiesnofonts
+  %
+  \definedummyletter\_%
+  \definedummyletter\-%
+  %
+  % Non-English letters.
+  \definedummyword\AA
+  \definedummyword\AE
+  \definedummyword\DH
+  \definedummyword\L
+  \definedummyword\O
+  \definedummyword\OE
+  \definedummyword\TH
+  \definedummyword\aa
+  \definedummyword\ae
+  \definedummyword\dh
+  \definedummyword\exclamdown
+  \definedummyword\l
+  \definedummyword\o
+  \definedummyword\oe
+  \definedummyword\ordf
+  \definedummyword\ordm
+  \definedummyword\questiondown
+  \definedummyword\ss
+  \definedummyword\th
+  %
+  % Although these internal commands shouldn't show up, sometimes they do.
+  \definedummyword\bf
+  \definedummyword\gtr
+  \definedummyword\hat
+  \definedummyword\less
+  \definedummyword\sf
+  \definedummyword\sl
+  \definedummyword\tclose
+  \definedummyword\tt
+  %
+  \definedummyword\LaTeX
+  \definedummyword\TeX
+  %
+  % Assorted special characters.
+  \definedummyword\arrow
+  \definedummyword\bullet
+  \definedummyword\comma
+  \definedummyword\copyright
+  \definedummyword\registeredsymbol
+  \definedummyword\dots
+  \definedummyword\enddots
+  \definedummyword\entrybreak
+  \definedummyword\equiv
+  \definedummyword\error
+  \definedummyword\euro
+  \definedummyword\expansion
+  \definedummyword\geq
+  \definedummyword\guillemetleft
+  \definedummyword\guillemetright
+  \definedummyword\guilsinglleft
+  \definedummyword\guilsinglright
+  \definedummyword\lbracechar
+  \definedummyword\leq
+  \definedummyword\mathopsup
+  \definedummyword\minus
+  \definedummyword\ogonek
+  \definedummyword\pounds
+  \definedummyword\point
+  \definedummyword\print
+  \definedummyword\quotedblbase
+  \definedummyword\quotedblleft
+  \definedummyword\quotedblright
+  \definedummyword\quoteleft
+  \definedummyword\quoteright
+  \definedummyword\quotesinglbase
+  \definedummyword\rbracechar
+  \definedummyword\result
+  \definedummyword\sub
+  \definedummyword\sup
+  \definedummyword\textdegree
+  %
+  % We want to disable all macros so that they are not expanded by \write.
+  \macrolist
+  %
+  \normalturnoffactive
+  %
+  % Handle some cases of @value -- where it does not contain any
+  % (non-fully-expandable) commands.
+  \makevalueexpandable
+}
+
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+% Define \definedumyletter, \definedummyaccent and \definedummyword before
+% using.
+%
+\def\commondummiesnofonts{%
+  % Control letters and accents.
+  \definedummyletter\!%
+  \definedummyaccent\"%
+  \definedummyaccent\'%
+  \definedummyletter\*%
+  \definedummyaccent\,%
+  \definedummyletter\.%
+  \definedummyletter\/%
+  \definedummyletter\:%
+  \definedummyaccent\=%
+  \definedummyletter\?%
+  \definedummyaccent\^%
+  \definedummyaccent\`%
+  \definedummyaccent\~%
+  \definedummyword\u
+  \definedummyword\v
+  \definedummyword\H
+  \definedummyword\dotaccent
+  \definedummyword\ogonek
+  \definedummyword\ringaccent
+  \definedummyword\tieaccent
+  \definedummyword\ubaraccent
+  \definedummyword\udotaccent
+  \definedummyword\dotless
+  %
+  % Texinfo font commands.
+  \definedummyword\b
+  \definedummyword\i
+  \definedummyword\r
+  \definedummyword\sansserif
+  \definedummyword\sc
+  \definedummyword\slanted
+  \definedummyword\t
+  %
+  % Commands that take arguments.
+  \definedummyword\abbr
+  \definedummyword\acronym
+  \definedummyword\anchor
+  \definedummyword\cite
+  \definedummyword\code
+  \definedummyword\command
+  \definedummyword\dfn
+  \definedummyword\dmn
+  \definedummyword\email
+  \definedummyword\emph
+  \definedummyword\env
+  \definedummyword\file
+  \definedummyword\image
+  \definedummyword\indicateurl
+  \definedummyword\inforef
+  \definedummyword\kbd
+  \definedummyword\key
+  \definedummyword\math
+  \definedummyword\option
+  \definedummyword\pxref
+  \definedummyword\ref
+  \definedummyword\samp
+  \definedummyword\strong
+  \definedummyword\tie
+  \definedummyword\U
+  \definedummyword\uref
+  \definedummyword\url
+  \definedummyword\var
+  \definedummyword\verb
+  \definedummyword\w
+  \definedummyword\xref
+}
+
+% For testing: output @{ and @} in index sort strings as \{ and \}.
+\newif\ifusebracesinindexes
+
+\let\indexlbrace\relax
+\let\indexrbrace\relax
+
+{\catcode`\@=0
+\catcode`\\=13
+  @gdef@backslashdisappear{@def\{}}
+}
+
+{
+\catcode`\<=13
+\catcode`\-=13
+\catcode`\`=13
+  \gdef\indexnonalnumdisappear{%
+    \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax\else
+      % @set txiindexlquoteignore makes us ignore left quotes in the sort term.
+      % (Introduced for FSFS 2nd ed.)
+      \let`=\empty
+    \fi
+    %
+    \expandafter\ifx\csname SETtxiindexbackslashignore\endcsname\relax\else
+      \backslashdisappear
+    \fi
+    %
+    \expandafter\ifx\csname SETtxiindexhyphenignore\endcsname\relax\else
+      \def-{}%
+    \fi
+    \expandafter\ifx\csname SETtxiindexlessthanignore\endcsname\relax\else
+      \def<{}%
+    \fi
+    \expandafter\ifx\csname SETtxiindexatsignignore\endcsname\relax\else
+      \def\@{}%
+    \fi
+  }
+
+  \gdef\indexnonalnumreappear{%
+    \useindexbackslash
+    \let-\normaldash
+    \let<\normalless
+    \def\@{@}%
+  }
+}
+
+
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names.  It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+  % Accent commands should become @asis.
+  \def\definedummyaccent##1{\let##1\asis}%
+  % We can just ignore other control letters.
+  \def\definedummyletter##1{\let##1\empty}%
+  % All control words become @asis by default; overrides below.
+  \let\definedummyword\definedummyaccent
+  \commondummiesnofonts
+  %
+  % Don't no-op \tt, since it isn't a user-level command
+  % and is used in the definitions of the active chars like <, >, |, etc.
+  % Likewise with the other plain tex font commands.
+  %\let\tt=\asis
+  %
+  \def\ { }%
+  \def\@{@}%
+  \def\_{\normalunderscore}%
+  \def\-{}% @- shouldn't affect sorting
+  %
+  \uccode`\1=`\{ \uppercase{\def\{{1}}%
+  \uccode`\1=`\} \uppercase{\def\}{1}}%
+  \let\lbracechar\{%
+  \let\rbracechar\}%
+  %
+  % Non-English letters.
+  \def\AA{AA}%
+  \def\AE{AE}%
+  \def\DH{DZZ}%
+  \def\L{L}%
+  \def\OE{OE}%
+  \def\O{O}%
+  \def\TH{TH}%
+  \def\aa{aa}%
+  \def\ae{ae}%
+  \def\dh{dzz}%
+  \def\exclamdown{!}%
+  \def\l{l}%
+  \def\oe{oe}%
+  \def\ordf{a}%
+  \def\ordm{o}%
+  \def\o{o}%
+  \def\questiondown{?}%
+  \def\ss{ss}%
+  \def\th{th}%
+  %
+  \def\LaTeX{LaTeX}%
+  \def\TeX{TeX}%
+  %
+  % Assorted special characters.
+  % (The following {} will end up in the sort string, but that's ok.)
+  \def\arrow{->}%
+  \def\bullet{bullet}%
+  \def\comma{,}%
+  \def\copyright{copyright}%
+  \def\dots{...}%
+  \def\enddots{...}%
+  \def\equiv{==}%
+  \def\error{error}%
+  \def\euro{euro}%
+  \def\expansion{==>}%
+  \def\geq{>=}%
+  \def\guillemetleft{<<}%
+  \def\guillemetright{>>}%
+  \def\guilsinglleft{<}%
+  \def\guilsinglright{>}%
+  \def\leq{<=}%
+  \def\minus{-}%
+  \def\point{.}%
+  \def\pounds{pounds}%
+  \def\print{-|}%
+  \def\quotedblbase{"}%
+  \def\quotedblleft{"}%
+  \def\quotedblright{"}%
+  \def\quoteleft{`}%
+  \def\quoteright{'}%
+  \def\quotesinglbase{,}%
+  \def\registeredsymbol{R}%
+  \def\result{=>}%
+  \def\textdegree{o}%
+  %
+  % We need to get rid of all macros, leaving only the arguments (if present).
+  % Of course this is not nearly correct, but it is the best we can do for now.
+  % makeinfo does not expand macros in the argument to @deffn, which ends up
+  % writing an index entry, and texindex isn't prepared for an index sort entry
+  % that starts with \.
+  %
+  % Since macro invocations are followed by braces, we can just redefine them
+  % to take a single TeX argument.  The case of a macro invocation that
+  % goes to end-of-line is not handled.
+  %
+  \macrolist
+}
+
+
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
+
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+% TODO: Two-level index?  Operation index?
+
+% Workhorse for all indexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+  \iflinks
+  {%
+    \requireopenindexfile{#1}%
+    % Store the main index entry text (including the third arg).
+    \toks0 = {#2}%
+    % If third arg is present, precede it with a space.
+    \def\thirdarg{#3}%
+    \ifx\thirdarg\empty \else
+      \toks0 = \expandafter{\the\toks0 \space #3}%
+    \fi
+    %
+    \edef\writeto{\csname#1indfile\endcsname}%
+    %
+    \safewhatsit\dosubindwrite
+  }%
+  \fi
+}
+
+% Check if an index file has been opened, and if not, open it.
+\def\requireopenindexfile#1{%
+\ifnum\csname #1indfile\endcsname=0
+  \expandafter\newwrite \csname#1indfile\endcsname
+  \edef\suffix{#1}%
+  % A .fls suffix would conflict with the file extension for the output
+  % of -recorder, so use .f1s instead.
+  \ifx\suffix\indexisfl\def\suffix{f1}\fi
+  % Open the file
+  \immediate\openout\csname#1indfile\endcsname \jobname.\suffix
+  % Using \immediate here prevents an object entering into the current box,
+  % which could confound checks such as those in \safewhatsit for preceding
+  % skips.
+\fi}
+\def\indexisfl{fl}
+
+% Output \ as {\indexbackslash}, because \ is an escape character in
+% the index files.
+\let\indexbackslash=\relax
+{\catcode`\@=0 \catcode`\\=\active
+  @gdef@useindexbackslash{@def\{{@indexbackslash}}}
+}
+
+% Definition for writing index entry text.
+\def\sortas#1{\ignorespaces}%
+
+% Definition for writing index entry sort key.  Should occur at the at
+% the beginning of the index entry, like
+%     @cindex @sortas{september} \september
+% The \ignorespaces takes care of following space, but there's no way
+% to remove space before it.
+{
+\catcode`\-=13
+\gdef\indexwritesortas{%
+  \begingroup
+  \indexnonalnumreappear
+  \indexwritesortasxxx}
+\gdef\indexwritesortasxxx#1{%
+  \xdef\indexsortkey{#1}\endgroup}
+}
+
+
+% Write the entry in \toks0 to the index file.
+%
+\def\dosubindwrite{%
+  % Put the index entry in the margin if desired.
+  \ifx\SETmarginindex\relax\else
+    \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+  \fi
+  %
+  % Remember, we are within a group.
+  \indexdummies % Must do this here, since \bf, etc expand at this stage
+  \useindexbackslash % \indexbackslash isn't defined now so it will be output 
+                     % as is; and it will print as backslash.
+  % The braces around \indexbrace are recognized by texindex.
+  %
+  % Get the string to sort by, by processing the index entry with all
+  % font commands turned off.
+  {\indexnofonts
+   \def\lbracechar{{\indexlbrace}}%
+   \def\rbracechar{{\indexrbrace}}%
+   \let\{=\lbracechar
+   \let\}=\rbracechar
+   \indexnonalnumdisappear
+   \xdef\indexsortkey{}%
+   \let\sortas=\indexwritesortas
+   \edef\temp{\the\toks0}%
+   \setbox\dummybox = \hbox{\temp}% Make sure to execute any \sortas
+   \ifx\indexsortkey\empty
+     \xdef\indexsortkey{\temp}%
+     \ifx\indexsortkey\empty\xdef\indexsortkey{ }\fi
+   \fi
+  }%
+  %
+  % Set up the complete index entry, with both the sort key and
+  % the original text, including any font commands.  We write
+  % three arguments to \entry to the .?? file (four in the
+  % subentry case), texindex reduces to two when writing the .??s
+  % sorted result.
+  \edef\temp{%
+    \write\writeto{%
+      \string\entry{\indexsortkey}{\noexpand\folio}{\the\toks0}}%
+  }%
+  \temp
+}
+\newbox\dummybox % used above
+
+% Take care of unwanted page breaks/skips around a whatsit:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again.  Otherwise, the whatsit generated by the
+% \write or \pdfdest will make \lastskip zero.  The result is that
+% sequences like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode.  We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip.  \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip.  The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+\newskip\whatsitskip
+\newcount\whatsitpenalty
+%
+% ..., ready, GO:
+%
+\def\safewhatsit#1{\ifhmode
+  #1%
+ \else
+  % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+  \whatsitskip = \lastskip
+  \edef\lastskipmacro{\the\lastskip}%
+  \whatsitpenalty = \lastpenalty
+  %
+  % If \lastskip is nonzero, that means the last item was a
+  % skip.  And since a skip is discardable, that means this
+  % -\whatsitskip glue we're inserting is preceded by a
+  % non-discardable item, therefore it is not a potential
+  % breakpoint, therefore no \nobreak needed.
+  \ifx\lastskipmacro\zeroskipmacro
+  \else
+    \vskip-\whatsitskip
+  \fi
+  %
+  #1%
+  %
+  \ifx\lastskipmacro\zeroskipmacro
+    % If \lastskip was zero, perhaps the last item was a penalty, and
+    % perhaps it was >=10000, e.g., a \nobreak.  In that case, we want
+    % to re-insert the same penalty (values >10000 are used for various
+    % signals); since we just inserted a non-discardable item, any
+    % following glue (such as a \parskip) would be a breakpoint.  For example:
+    %   @deffn deffn-whatever
+    %   @vindex index-whatever
+    %   Description.
+    % would allow a break between the index-whatever whatsit
+    % and the "Description." paragraph.
+    \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi
+  \else
+    % On the other hand, if we had a nonzero \lastskip,
+    % this make-up glue would be preceded by a non-discardable item
+    % (the whatsit from the \write), so we must insert a \nobreak.
+    \nobreak\vskip\whatsitskip
+  \fi
+\fi}
+
+% The index entry written in the file actually looks like
+%  \entry {sortstring}{page}{topic}
+% or
+%  \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+%  \initial {c}
+%     before the first topic whose initial is c
+%  \entry {topic}{pagelist}
+%     for a topic that is used without subtopics
+%  \primary {topic}
+%     for the beginning of a topic that is used with subtopics
+%  \secondary {subtopic}{pagelist}
+%     for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+  \dobreak \chapheadingskip{10000}%
+  %
+  \smallfonts \rm
+  \tolerance = 9500
+  \plainfrenchspacing
+  \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+  %
+  % See if the index file exists and is nonempty.
+  % Change catcode of @ here so that if the index file contains
+  % \initial {@}
+  % as its first line, TeX doesn't complain about mismatched braces
+  % (because it thinks @} is a control sequence).
+  \catcode`\@ = 11
+  % See comment in \requireopenindexfile.
+  \def\indexname{#1}\ifx\indexname\indexisfl\def\indexname{f1}\fi
+  \openin 1 \jobname.\indexname s
+  \ifeof 1
+    % \enddoublecolumns gets confused if there is no text in the index,
+    % and it loses the chapter title and the aux file entries for the
+    % index.  The easiest way to prevent this problem is to make sure
+    % there is some text.
+    \putwordIndexNonexistent
+  \else
+    \catcode`\\ = 0
+    \escapechar = `\\
+    %
+    % If the index file exists but is empty, then \openin leaves \ifeof
+    % false.  We have to make TeX try to read something from the file, so
+    % it can discover if there is anything in it.
+    \read 1 to \thisline
+    \ifeof 1
+      \putwordIndexIsEmpty
+    \else
+      % Index files are almost Texinfo source, but we use \ as the escape
+      % character.  It would be better to use @, but that's too big a change
+      % to make right now.
+      \def\indexbackslash{\ttbackslash}%
+      \let\indexlbrace\{   % Likewise, set these sequences for braces
+      \let\indexrbrace\}   % used in the sort key.
+      \begindoublecolumns
+      \let\entryorphanpenalty=\indexorphanpenalty
+      %
+      % Read input from the index file line by line.
+      \loopdo
+        \ifeof1
+          \let\firsttoken\relax
+        \else
+          \read 1 to \nextline
+          \edef\act{\gdef\noexpand\firsttoken{\getfirsttoken\nextline}}%
+          \act
+        \fi
+        \thisline
+        %
+        \ifeof1\else
+        \let\thisline\nextline
+      \repeat
+      %%
+      \enddoublecolumns
+    \fi
+  \fi
+  \closein 1
+\endgroup}
+
+\def\getfirsttoken#1{\expandafter\getfirsttokenx#1\endfirsttoken}
+\long\def\getfirsttokenx#1#2\endfirsttoken{\noexpand#1}
+
+\def\loopdo#1\repeat{\def\body{#1}\loopdoxxx}
+\def\loopdoxxx{\let\next=\relax\body\let\next=\loopdoxxx\fi\next}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+{\catcode`\/=13 \catcode`\-=13 \catcode`\^=13 \catcode`\~=13 \catcode`\_=13
+\catcode`\|=13 \catcode`\<=13 \catcode`\>=13 \catcode`\+=13 \catcode`\"=13
+\catcode`\$=3
+\gdef\initialglyphs{%
+  % Some changes for non-alphabetic characters.  Using the glyphs from the
+  % math fonts looks more consistent than the typewriter font used elsewhere
+  % for these characters.
+  \def\indexbackslash{\math{\backslash}}%
+  \let\\=\indexbackslash
+  %
+  % Can't get bold backslash so don't use bold forward slash
+  \catcode`\/=13
+  \def/{{\secrmnotbold \normalslash}}%
+  \def-{{\normaldash\normaldash}}% en dash `--'
+  \def^{{\chapbf \normalcaret}}%
+  \def~{{\chapbf \normaltilde}}%
+  \def\_{%
+     \leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }%
+  \def|{$\vert$}%
+  \def<{$\less$}%
+  \def>{$\gtr$}%
+  \def+{$\normalplus$}%
+}}
+
+\def\initial{%
+  \bgroup
+  \initialglyphs
+  \initialx
+}
+
+\def\initialx#1{%
+  % Remove any glue we may have, we'll be inserting our own.
+  \removelastskip
+  %
+  % We like breaks before the index initials, so insert a bonus.
+  % The glue before the bonus allows a little bit of space at the
+  % bottom of a column to reduce an increase in inter-line spacing.
+  \nobreak
+  \vskip 0pt plus 5\baselineskip
+  \penalty -300 
+  \vskip 0pt plus -5\baselineskip
+  %
+  % Typeset the initial.  Making this add up to a whole number of
+  % baselineskips increases the chance of the dots lining up from column
+  % to column.  It still won't often be perfect, because of the stretch
+  % we need before each entry, but it's better.
+  %
+  % No shrink because it confuses \balancecolumns.
+  \vskip 1.67\baselineskip plus 1\baselineskip
+  \leftline{\secfonts \kern-0.05em \secbf #1}%
+  % \secfonts is inside the argument of \leftline so that the change of
+  % \baselineskip will not affect any glue inserted before the vbox that
+  % \leftline creates.
+  % Do our best not to break after the initial.
+  \nobreak
+  \vskip .33\baselineskip plus .1\baselineskip
+  \egroup % \initialglyphs
+}
+
+\newdimen\entryrightmargin
+\entryrightmargin=0pt
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin.  It is used for index
+% and table of contents entries.  The paragraph is indented by \leftskip.
+%
+\def\entry{%
+  \begingroup
+    %
+    % Start a new paragraph if necessary, so our assignments below can't
+    % affect previous text.
+    \par
+    %
+    % No extra space above this paragraph.
+    \parskip = 0in
+    %
+    % When reading the text of entry, convert explicit line breaks
+    % from @* into spaces.  The user might give these in long section
+    % titles, for instance.
+    \def\*{\unskip\space\ignorespaces}%
+    \def\entrybreak{\hfil\break}% An undocumented command
+    %
+    % A bit of stretch before each entry for the benefit of balancing
+    % columns.
+    \vskip 0pt plus0.5pt
+    %
+    % Swallow the left brace of the text (first parameter):
+    \afterassignment\doentry
+    \let\temp =
+}
+\def\entrybreak{\unskip\space\ignorespaces}%
+\def\doentry{%
+    % Save the text of the entry
+    \global\setbox\boxA=\hbox\bgroup
+    \bgroup % Instead of the swallowed brace.
+      \noindent
+      \aftergroup\finishentry
+      % And now comes the text of the entry.
+      % Not absorbing as a macro argument reduces the chance of problems
+      % with catcodes occurring.
+}
+{\catcode`\@=11
+\gdef\finishentry#1{%
+    \egroup % end box A
+    \dimen@ = \wd\boxA % Length of text of entry
+    \global\setbox\boxA=\hbox\bgroup\unhbox\boxA
+    % #1 is the page number.
+    %
+    % Get the width of the page numbers, and only use
+    % leaders if they are present.
+    \global\setbox\boxB = \hbox{#1}%
+    \ifdim\wd\boxB = 0pt
+      \null\nobreak\hfill\ %
+    \else
+      %
+      \null\nobreak\indexdotfill % Have leaders before the page number.
+      %
+      \ifpdf
+        \pdfgettoks#1.%
+        \bgroup\let\domark\relax
+          \hskip\skip\thinshrinkable\the\toksA
+        \egroup
+        % The redefinion of \domark stops marks being added in \pdflink to 
+        % preserve coloured links across page boundaries.  Otherwise the marks
+        % would get in the way of \lastbox in \insertindexentrybox.
+      \else
+        \hskip\skip\thinshrinkable #1%
+      \fi
+    \fi
+    \egroup % end \boxA
+    \ifdim\wd\boxB = 0pt
+      \global\setbox\entryindexbox=\vbox{\unhbox\boxA}%
+    \else
+    \global\setbox\entryindexbox=\vbox\bgroup
+      \prevdepth=\entrylinedepth
+      \noindent
+      % We want the text of the entries to be aligned to the left, and the
+      % page numbers to be aligned to the right.
+      %
+      \advance\leftskip by 0pt plus 1fil
+      \advance\leftskip by 0pt plus -1fill
+      \rightskip = 0pt plus -1fil
+      \advance\rightskip by 0pt plus 1fill
+      % Cause last line, which could consist of page numbers on their own
+      % if the list of page numbers is long, to be aligned to the right.
+      \parfillskip=0pt plus -1fill
+      %
+      \hangindent=1em
+      %
+      \advance\rightskip by \entryrightmargin
+      % Determine how far we can stretch into the margin.
+      % This allows, e.g., "Appendix H  GNU Free Documentation License" to
+      % fit on one line in @letterpaper format.
+      \ifdim\entryrightmargin>2.1em
+        \dimen@i=2.1em
+      \else
+        \dimen@i=0em
+      \fi
+      \advance \parfillskip by 0pt minus 1\dimen@i
+      %
+      \dimen@ii = \hsize
+      \advance\dimen@ii by -1\leftskip
+      \advance\dimen@ii by -1\entryrightmargin
+      \advance\dimen@ii by 1\dimen@i
+      \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line
+      \ifdim\dimen@ > 0.8\dimen@ii   % due to long index text
+        \dimen@ = 0.7\dimen@ % Try to split the text roughly evenly
+        \dimen@ii = \hsize
+        \advance \dimen@ii by -1em
+        \ifnum\dimen@>\dimen@ii
+          % If the entry is too long, use the whole line
+          \dimen@ = \dimen@ii
+        \fi
+        \advance\leftskip by 0pt plus 1fill % ragged right
+        \advance \dimen@ by 1\rightskip
+        \parshape = 2 0pt \dimen@ 1em \dimen@ii
+        % Ideally we'd add a finite glue at the end of the first line only, but
+        % TeX doesn't seem to provide a way to do such a thing.
+      \fi\fi
+      \unhbox\boxA
+      %
+      % Do not prefer a separate line ending with a hyphen to fewer lines.
+      \finalhyphendemerits = 0
+      %
+      % Word spacing - no stretch
+      \spaceskip=\fontdimen2\font minus \fontdimen4\font
+      %
+      \linepenalty=1000  % Discourage line breaks.
+      \hyphenpenalty=5000  % Discourage hyphenation.
+      %
+      \par % format the paragraph
+    \egroup % The \vbox
+    \fi
+  \endgroup
+  % delay text of entry until after penalty
+  \bgroup\aftergroup\insertindexentrybox
+  \entryorphanpenalty
+}}
+
+\newskip\thinshrinkable
+\skip\thinshrinkable=.15em minus .15em
+
+\newbox\entryindexbox
+\def\insertindexentrybox{%
+  \copy\entryindexbox
+  % The following gets the depth of the last box.  This is for even
+  % line spacing when entries span several lines.
+  \setbox\dummybox\vbox{%
+    \unvbox\entryindexbox
+    \nointerlineskip
+    \lastbox
+    \global\entrylinedepth=\prevdepth
+  }%
+  % Note that we couldn't simply \unvbox\entryindexbox followed by 
+  % \nointerlineskip\lastbox to remove the last box and then reinstate it, 
+  % because this resets how far the box has been \moveleft'ed to 0.  \unvbox
+  % doesn't affect \prevdepth either.
+}
+\newdimen\entrylinedepth
+
+% Default is no penalty
+\let\entryorphanpenalty\egroup
+
+% Used from \printindex.  \firsttoken should be the first token
+% after the \entry.  If it's not another \entry, we are at the last
+% line of a group of index entries, so insert a penalty to discourage
+% orphaned index entries.
+\long\def\indexorphanpenalty{%
+  \def\isentry{\entry}%
+  \ifx\firsttoken\isentry
+  \else
+    \unskip\penalty 9000
+    % The \unskip here stops breaking before the glue.  It relies on the
+    % \vskip above being there, otherwise there is an error
+    % "You can't use `\unskip' in vertical mode".  There has to be glue
+    % in the current vertical list that hasn't been added to the
+    % "current page".  See Chapter 24 of the TeXbook.  This contradicts
+    % Section 8.3.7 in "TeX by Topic," though.
+  \fi
+  \egroup % now comes the box added with \aftergroup
+}
+
+% Like plain.tex's \dotfill, except uses up at least 1 em.
+% The filll stretch here overpowers both the fil and fill stretch to push
+% the page number to the right.
+\def\indexdotfill{\cleaders
+  \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1filll}
+
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+\def\secondary#1#2{{%
+  \parfillskip=0in
+  \parskip=0in
+  \hangindent=1in
+  \hangafter=1
+  \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+  \ifpdf
+    \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+  \else
+    #2
+  \fi
+  \par
+}}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11  % private names
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+\newdimen\doublecolumntopgap
+\doublecolumntopgap = 0pt
+
+% Use inside an output routine to save \topmark and \firstmark
+\def\savemarks{%
+  \global\savedtopmark=\expandafter{\topmark }%
+  \global\savedfirstmark=\expandafter{\firstmark }%
+}
+\newtoks\savedtopmark
+\newtoks\savedfirstmark
+
+% Set \topmark and \firstmark for next time \output runs.
+% Can't be run from withinside \output (because any material
+% added while an output routine is active, including 
+% penalties, is saved for after it finishes).  The page so far
+% should be empty, otherwise what's on it will be thrown away.
+\def\restoremarks{%
+  \mark{\the\savedtopmark}%
+  \bgroup\output = {%
+    \setbox\dummybox=\box\PAGE
+  }abc\eject\egroup
+  % "abc" because output routine doesn't fire for a completely empty page.
+  \mark{\the\savedfirstmark}%
+}
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+  % If not much space left on page, start a new page.
+  \ifdim\pagetotal>0.8\vsize\vfill\eject\fi
+  %
+  % Grab any single-column material above us.
+  \output = {%
+    %
+    % Here is a possibility not foreseen in manmac: if we accumulate a
+    % whole lot of material, we might end up calling this \output
+    % routine twice in a row (see the doublecol-lose test, which is
+    % essentially a couple of indexes with @setchapternewpage off).  In
+    % that case we just ship out what is in \partialpage with the normal
+    % output routine.  Generally, \partialpage will be empty when this
+    % runs and this will be a no-op.  See the indexspread.tex test case.
+    \ifvoid\partialpage \else
+      \onepageout{\pagecontents\partialpage}%
+    \fi
+    %
+    \global\setbox\partialpage = \vbox{%
+      % Unvbox the main output page.
+      \unvbox\PAGE
+      \kern-\topskip \kern\baselineskip
+    }%
+    \savemarks
+  }%
+  \eject % run that output routine to set \partialpage
+  \restoremarks
+  %
+  % We recover the two marks that the last output routine saved in order
+  % to propagate the information in marks added around a chapter heading,
+  % which could be otherwise be lost by the time the final page is output.
+  %
+  %
+  % Use the double-column output routine for subsequent pages.
+  \output = {\doublecolumnout}%
+  %
+  % Change the page size parameters.  We could do this once outside this
+  % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+  % format, but then we repeat the same computation.  Repeating a couple
+  % of assignments once per index is clearly meaningless for the
+  % execution time, so we may as well do it in one place.
+  %
+  % First we halve the line length, less a little for the gutter between
+  % the columns.  We compute the gutter based on the line length, so it
+  % changes automatically with the paper format.  The magic constant
+  % below is chosen so that the gutter has the same value (well, +-<1pt)
+  % as it did when we hard-coded it.
+  %
+  % We put the result in a separate register, \doublecolumhsize, so we
+  % can restore it in \pagesofar, after \hsize itself has (potentially)
+  % been clobbered.
+  %
+  \doublecolumnhsize = \hsize
+    \advance\doublecolumnhsize by -.04154\hsize
+    \divide\doublecolumnhsize by 2
+  \hsize = \doublecolumnhsize
+  %
+  % Double the \vsize as well.  (We don't need a separate register here,
+  % since nobody clobbers \vsize.)
+  \global\doublecolumntopgap = \topskip
+  \global\advance\doublecolumntopgap by -1\baselineskip
+  \advance\vsize by -1\doublecolumntopgap
+  \vsize = 2\vsize
+  \topskip=0pt
+  \global\entrylinedepth=0pt\relax
+}
+
+% The double-column output routine for all double-column pages except
+% the last, which is done by \balancecolumns.
+%
+\def\doublecolumnout{%
+  %
+  \splittopskip=\topskip \splitmaxdepth=\maxdepth
+  % Get the available space for the double columns -- the normal
+  % (undoubled) page height minus any material left over from the
+  % previous page.
+  \dimen@ = \vsize
+  \divide\dimen@ by 2
+  \advance\dimen@ by -\ht\partialpage
+  %
+  % box0 will be the left-hand column, box2 the right.
+  \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+  \onepageout\pagesofar
+  \unvbox255
+  \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+  \unvbox\partialpage
+  %
+  \hsize = \doublecolumnhsize
+  \wd0=\hsize \wd2=\hsize
+  \vbox{%
+    \vskip\doublecolumntopgap
+    \hbox to\pagewidth{\box0\hfil\box2}}%
+}
+
+
+% Finished with with double columns.
+\def\enddoublecolumns{%
+  % The following penalty ensures that the page builder is exercised
+  % _before_ we change the output routine.  This is necessary in the
+  % following situation:
+  %
+  % The last section of the index consists only of a single entry.
+  % Before this section, \pagetotal is less than \pagegoal, so no
+  % break occurs before the last section starts.  However, the last
+  % section, consisting of \initial and the single \entry, does not
+  % fit on the page and has to be broken off.  Without the following
+  % penalty the page builder will not be exercised until \eject
+  % below, and by that time we'll already have changed the output
+  % routine to the \balancecolumns version, so the next-to-last
+  % double-column page will be processed with \balancecolumns, which
+  % is wrong:  The two columns will go to the main vertical list, with
+  % the broken-off section in the recent contributions.  As soon as
+  % the output routine finishes, TeX starts reconsidering the page
+  % break.  The two columns and the broken-off section both fit on the
+  % page, because the two columns now take up only half of the page
+  % goal.  When TeX sees \eject from below which follows the final
+  % section, it invokes the new output routine that we've set after
+  % \balancecolumns below; \onepageout will try to fit the two columns
+  % and the final section into the vbox of \pageheight (see
+  % \pagebody), causing an overfull box.
+  %
+  % Note that glue won't work here, because glue does not exercise the
+  % page builder, unlike penalties (see The TeXbook, pp. 280-281).
+  \penalty0
+  %
+  \output = {%
+    % Split the last of the double-column material.
+    \savemarks
+    \balancecolumns
+    %
+    % Having called \balancecolumns once, we do not
+    % want to call it again.  Therefore, reset \output to its normal
+    % definition right away.
+    \global\output = {\onepageout{\pagecontents\PAGE}}%
+  }%
+  \eject
+  \endgroup % started in \begindoublecolumns
+  \restoremarks
+  % Leave the double-column material on the current page, no automatic
+  % page break.
+  \box\balancedcolumns
+  %
+  % \pagegoal was set to the doubled \vsize above, since we restarted
+  % the current page.  We're now back to normal single-column
+  % typesetting, so reset \pagegoal to the normal \vsize (after the
+  % \endgroup where \vsize got restored).
+  \pagegoal = \vsize
+}
+\newbox\balancedcolumns
+\setbox\balancedcolumns=\vbox{shouldnt see this}%
+%
+% Only called for the last of the double column material.  \doublecolumnout 
+% does the others.
+\def\balancecolumns{%
+  \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+  \dimen@ = \ht0
+  \advance\dimen@ by \topskip
+  \advance\dimen@ by-\baselineskip
+  \ifdim\dimen@<14\baselineskip
+    % Don't split a short final column in two.
+    \setbox2=\vbox{}%
+  \else
+    \divide\dimen@ by 2 % target to split to
+    \dimen@ii = \dimen@
+    \splittopskip = \topskip
+    % Loop until the second column is no higher than the first
+    {%
+      \vbadness = 10000
+      \loop
+        \global\setbox3 = \copy0
+        \global\setbox1 = \vsplit3 to \dimen@
+        % Remove glue from bottom of first column to
+        % make sure it is higher than the second.
+        \global\setbox1 = \vbox{\unvbox1\unpenalty\unskip}%
+      \ifdim\ht3>\ht1
+        \global\advance\dimen@ by 1pt
+      \repeat
+    }%
+    \multiply\dimen@ii by 4
+    \divide\dimen@ii by 5
+    \ifdim\ht3<\dimen@ii
+      % Column heights are too different, so don't make their bottoms
+      % flush with each other.  The glue at the end of the second column
+      % allows a second column to stretch, reducing the difference in
+      % height between the two.
+      \setbox0=\vbox to\dimen@{\unvbox1\vfill}%
+      \setbox2=\vbox to\dimen@{\unvbox3\vskip 0pt plus 0.3\ht0}%
+    \else
+      \setbox0=\vbox to\dimen@{\unvbox1}%
+      \setbox2=\vbox to\dimen@{\unvbox3}%
+    \fi
+  \fi
+  %
+  \global\setbox\balancedcolumns=\vbox{\pagesofar}%
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% Let's start with @part.
+\outer\parseargdef\part{\partzzz{#1}}
+\def\partzzz#1{%
+  \chapoddpage
+  \null
+  \vskip.3\vsize  % move it down on the page a bit
+  \begingroup
+    \noindent \titlefonts\rmisbold #1\par % the text
+    \let\lastnode=\empty      % no node to associate with
+    \writetocentry{part}{#1}{}% but put it in the toc
+    \headingsoff              % no headline or footline on the part page
+    % This outputs a mark at the end of the page that clears \thischapter
+    % and \thissection, as is done in \startcontents.
+    \let\pchapsepmacro\relax
+    \chapmacro{}{Yomitfromtoc}{}%
+    \chapoddpage
+  \endgroup
+}
+
+% \unnumberedno is an oxymoron.  But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number".  We avoid collisions with chapter
+% numbers by starting them at 10000.  (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno        \secno=0
+\newcount\subsecno     \subsecno=0
+\newcount\subsubsecno  \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno  \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+  \ifnum\appendixno=`A A%
+  \else\ifnum\appendixno=`B B%
+  \else\ifnum\appendixno=`C C%
+  \else\ifnum\appendixno=`D D%
+  \else\ifnum\appendixno=`E E%
+  \else\ifnum\appendixno=`F F%
+  \else\ifnum\appendixno=`G G%
+  \else\ifnum\appendixno=`H H%
+  \else\ifnum\appendixno=`I I%
+  \else\ifnum\appendixno=`J J%
+  \else\ifnum\appendixno=`K K%
+  \else\ifnum\appendixno=`L L%
+  \else\ifnum\appendixno=`M M%
+  \else\ifnum\appendixno=`N N%
+  \else\ifnum\appendixno=`O O%
+  \else\ifnum\appendixno=`P P%
+  \else\ifnum\appendixno=`Q Q%
+  \else\ifnum\appendixno=`R R%
+  \else\ifnum\appendixno=`S S%
+  \else\ifnum\appendixno=`T T%
+  \else\ifnum\appendixno=`U U%
+  \else\ifnum\appendixno=`V V%
+  \else\ifnum\appendixno=`W W%
+  \else\ifnum\appendixno=`X X%
+  \else\ifnum\appendixno=`Y Y%
+  \else\ifnum\appendixno=`Z Z%
+  % The \the is necessary, despite appearances, because \appendixletter is
+  % expanded while writing the .toc file.  \char\appendixno is not
+  % expandable, thus it is written literally, thus all appendixes come out
+  % with the same letter (or @) in the toc without it.
+  \else\char\the\appendixno
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+% Each @chapter defines these (using marks) as the number+name, number
+% and name of the chapter.  Page headings and footings can use
+% these.  @section does likewise.
+\def\thischapter{}
+\def\thischapternum{}
+\def\thischaptername{}
+\def\thissection{}
+\def\thissectionnum{}
+\def\thissectionname{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achieve this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unnlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+  % Compute the abs. sec. level:
+  \absseclevel=#2
+  \advance\absseclevel by \secbase
+  % Make sure \absseclevel doesn't fall outside the range:
+  \ifnum \absseclevel < 0
+    \absseclevel = 0
+  \else
+    \ifnum \absseclevel > 3
+      \absseclevel = 3
+    \fi
+  \fi
+  % The heading type:
+  \def\headtype{#1}%
+  \if \headtype U%
+    \ifnum \absseclevel < \unnlevel
+      \chardef\unnlevel = \absseclevel
+    \fi
+  \else
+    % Check for appendix sections:
+    \ifnum \absseclevel = 0
+      \edef\chapheadtype{\headtype}%
+    \else
+      \if \headtype A\if \chapheadtype N%
+       \errmessage{@appendix... within a non-appendix chapter}%
+      \fi\fi
+    \fi
+    % Check for numbered within unnumbered:
+    \ifnum \absseclevel > \unnlevel
+      \def\headtype{U}%
+    \else
+      \chardef\unnlevel = 3
+    \fi
+  \fi
+  % Now print the heading:
+  \if \headtype U%
+    \ifcase\absseclevel
+       \unnumberedzzz{#3}%
+    \or \unnumberedseczzz{#3}%
+    \or \unnumberedsubseczzz{#3}%
+    \or \unnumberedsubsubseczzz{#3}%
+    \fi
+  \else
+    \if \headtype A%
+      \ifcase\absseclevel
+         \appendixzzz{#3}%
+      \or \appendixsectionzzz{#3}%
+      \or \appendixsubseczzz{#3}%
+      \or \appendixsubsubseczzz{#3}%
+      \fi
+    \else
+      \ifcase\absseclevel
+         \chapterzzz{#3}%
+      \or \seczzz{#3}%
+      \or \numberedsubseczzz{#3}%
+      \or \numberedsubsubseczzz{#3}%
+      \fi
+    \fi
+  \fi
+  \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered.  Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v.  By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+  % section resetting is \global in case the chapter is in a group, such
+  % as an @include file.
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\chapno by 1
+  %
+  % Used for \float.
+  \gdef\chaplevelprefix{\the\chapno.}%
+  \resetallfloatnos
+  %
+  % \putwordChapter can contain complex things in translations.
+  \toks0=\expandafter{\putwordChapter}%
+  \message{\the\toks0 \space \the\chapno}%
+  %
+  % Write the actual heading.
+  \chapmacro{#1}{Ynumbered}{\the\chapno}%
+  %
+  % So @section and the like are numbered underneath this chapter.
+  \global\let\section = \numberedsec
+  \global\let\subsection = \numberedsubsec
+  \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz
+%
+\def\appendixzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\appendixno by 1
+  \gdef\chaplevelprefix{\appendixletter.}%
+  \resetallfloatnos
+  %
+  % \putwordAppendix can contain complex things in translations.
+  \toks0=\expandafter{\putwordAppendix}%
+  \message{\the\toks0 \space \appendixletter}%
+  %
+  \chapmacro{#1}{Yappendix}{\appendixletter}%
+  %
+  \global\let\section = \appendixsec
+  \global\let\subsection = \appendixsubsec
+  \global\let\subsubsection = \appendixsubsubsec
+}
+
+% normally unnmhead0 calls unnumberedzzz:
+\outer\parseargdef\unnumbered{\unnmhead0{#1}}
+\def\unnumberedzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\unnumberedno by 1
+  %
+  % Since an unnumbered has no number, no prefix for figures.
+  \global\let\chaplevelprefix = \empty
+  \resetallfloatnos
+  %
+  % This used to be simply \message{#1}, but TeX fully expands the
+  % argument to \message.  Therefore, if #1 contained @-commands, TeX
+  % expanded them.  For example, in `@unnumbered The @cite{Book}', TeX
+  % expanded @cite (which turns out to cause errors because \cite is meant
+  % to be executed, not expanded).
+  %
+  % Anyway, we don't want the fully-expanded definition of @cite to appear
+  % as a result of the \message, we just want `@cite' itself.  We use
+  % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+  % simply yielding the contents of <toks register>.  (We also do this for
+  % the toc entries.)
+  \toks0 = {#1}%
+  \message{(\the\toks0)}%
+  %
+  \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+  %
+  \global\let\section = \unnumberedsec
+  \global\let\subsection = \unnumberedsubsec
+  \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+  \let\centerparametersmaybe = \centerparameters
+  \unnmhead0{#1}%
+  \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+% 
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+% normally calls appendixsectionzzz:
+\outer\parseargdef\appendixsection{\apphead1{#1}}
+\def\appendixsectionzzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+% normally calls unnumberedseczzz:
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}}
+\def\unnumberedseczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+% 
+% normally calls numberedsubseczzz:
+\outer\parseargdef\numberedsubsec{\numhead2{#1}}
+\def\numberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+% normally calls appendixsubseczzz:
+\outer\parseargdef\appendixsubsec{\apphead2{#1}}
+\def\appendixsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno}%
+}
+
+% normally calls unnumberedsubseczzz:
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}}
+\def\unnumberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+% 
+% normally numberedsubsubseczzz:
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}}
+\def\numberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynumbered}%
+                 {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% normally appendixsubsubseczzz:
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}}
+\def\appendixsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% normally unnumberedsubsubseczzz:
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}}
+\def\unnumberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+\def\majorheading{%
+  {\advance\chapheadingskip by 10pt \chapbreak }%
+  \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+  \vbox{\chapfonts \raggedtitlesettings #1\par}%
+  \nobreak\bigskip \nobreak
+  \suppressfirstparagraphindent
+}
+
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+% Parameter controlling skip before chapter headings (if needed)
+\newskip\chapheadingskip
+
+% Define plain chapter starts, and page on/off switching for it.
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+
+% Start a new page
+\def\chappager{\par\vfill\supereject}
+
+% \chapoddpage - start on an odd page for a new chapter
+% Because \domark is called before \chapoddpage, the filler page will
+% get the headings for the next chapter, which is wrong.  But we don't
+% care -- we just disable all headings on the filler page.
+\def\chapoddpage{%
+  \chappager
+  \ifodd\pageno \else
+    \begingroup
+      \headingsoff
+      \null
+      \chappager
+    \endgroup
+  \fi
+}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+% \chapmacro - Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+% Not used for @heading series.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yappendixkeyword{Yappendix}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+%
+\def\chapmacro#1#2#3{%
+  \expandafter\ifx\thisenv\titlepage\else
+    \checkenv{}% chapters, etc., should not start inside an environment.
+  \fi
+  % FIXME: \chapmacro is currently called from inside \titlepage when
+  % \setcontentsaftertitlepage to print the "Table of Contents" heading, but
+  % this should probably be done by \sectionheading with an option to print
+  % in chapter size.
+  %
+  % Insert the first mark before the heading break (see notes for \domark).
+  \let\prevchapterdefs=\lastchapterdefs
+  \let\prevsectiondefs=\lastsectiondefs
+  \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}%
+                        \gdef\thissection{}}%
+  %
+  \def\temptype{#2}%
+  \ifx\temptype\Ynothingkeyword
+    \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+                          \gdef\thischapter{\thischaptername}}%
+  \else\ifx\temptype\Yomitfromtockeyword
+    \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+                          \gdef\thischapter{}}%
+  \else\ifx\temptype\Yappendixkeyword
+    \toks0={#1}%
+    \xdef\lastchapterdefs{%
+      \gdef\noexpand\thischaptername{\the\toks0}%
+      \gdef\noexpand\thischapternum{\appendixletter}%
+      % \noexpand\putwordAppendix avoids expanding indigestible
+      % commands in some of the translations.
+      \gdef\noexpand\thischapter{\noexpand\putwordAppendix{}
+                                 \noexpand\thischapternum:
+                                 \noexpand\thischaptername}%
+    }%
+  \else
+    \toks0={#1}%
+    \xdef\lastchapterdefs{%
+      \gdef\noexpand\thischaptername{\the\toks0}%
+      \gdef\noexpand\thischapternum{\the\chapno}%
+      % \noexpand\putwordChapter avoids expanding indigestible
+      % commands in some of the translations.
+      \gdef\noexpand\thischapter{\noexpand\putwordChapter{}
+                                 \noexpand\thischapternum:
+                                 \noexpand\thischaptername}%
+    }%
+  \fi\fi\fi
+  %
+  % Output the mark.  Pass it through \safewhatsit, to take care of
+  % the preceding space.
+  \safewhatsit\domark
+  %
+  % Insert the chapter heading break.
+  \pchapsepmacro
+  %
+  % Now the second mark, after the heading break.  No break points
+  % between here and the heading.
+  \let\prevchapterdefs=\lastchapterdefs
+  \let\prevsectiondefs=\lastsectiondefs
+  \domark
+  %
+  {%
+    \chapfonts \rmisbold
+    \let\footnote=\errfootnoteheading % give better error message
+    %
+    % Have to define \lastsection before calling \donoderef, because the
+    % xref code eventually uses it.  On the other hand, it has to be called
+    % after \pchapsepmacro, or the headline will change too soon.
+    \gdef\lastsection{#1}%
+    %
+    % Only insert the separating space if we have a chapter/appendix
+    % number, and don't print the unnumbered ``number''.
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unnchap}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+      \def\toctype{omit}%
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+      \def\toctype{app}%
+    \else
+      \setbox0 = \hbox{#3\enspace}%
+      \def\toctype{numchap}%
+    \fi\fi\fi
+    %
+    % Write the toc entry for this chapter.  Must come before the
+    % \donoderef, because we include the current node name in the toc
+    % entry, and \donoderef resets it to empty.
+    \writetocentry{\toctype}{#1}{#3}%
+    %
+    % For pdftex, we have to write out the node definition (aka, make
+    % the pdfdest) after any page break, but before the actual text has
+    % been typeset.  If the destination for the pdf outline is after the
+    % text, then jumping from the outline may wind up with the text not
+    % being visible, for instance under high magnification.
+    \donoderef{#2}%
+    %
+    % Typeset the actual heading.
+    \nobreak % Avoid page breaks at the interline glue.
+    \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe
+          \unhbox0 #1\par}%
+  }%
+  \nobreak\bigskip % no page break after a chapter title
+  \nobreak
+}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+  \advance\rightskip by 3\rightskip
+  \leftskip = \rightskip
+  \parfillskip = 0pt
+}
+
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff.  We'll see.  --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
+\def\unnchfopen #1{%
+  \chapoddpage
+  \vbox{\chapfonts \raggedtitlesettings #1\par}%
+  \nobreak\bigskip\nobreak
+}
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+\def\centerchfopen #1{%
+  \chapoddpage
+  \vbox{\chapfonts \raggedtitlesettings \hfill #1\hfill}%
+  \nobreak\bigskip \nobreak
+}
+\def\CHAPFopen{%
+  \global\let\chapmacro=\chfopen
+  \global\let\centerchapmacro=\centerchfopen}
+
+
+% Section titles.  These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
+
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
+
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
+
+
+% Print any size, any type, section title.
+%
+% #1 is the text of the title,
+% #2 is the section level (sec/subsec/subsubsec),
+% #3 is the section type (Ynumbered, Ynothing, Yappendix, Yomitfromtoc),
+% #4 is the section number.
+%
+\def\seckeyword{sec}
+%
+\def\sectionheading#1#2#3#4{%
+  {%
+    \def\sectionlevel{#2}%
+    \def\temptype{#3}%
+    %
+    % It is ok for the @heading series commands to appear inside an
+    % environment (it's been historically allowed, though the logic is
+    % dubious), but not the others.
+    \ifx\temptype\Yomitfromtockeyword\else
+      \checkenv{}% non-@*heading should not be in an environment.
+    \fi
+    \let\footnote=\errfootnoteheading
+    %
+    % Switch to the right set of fonts.
+    \csname #2fonts\endcsname \rmisbold
+    %
+    % Insert first mark before the heading break (see notes for \domark).
+    \let\prevsectiondefs=\lastsectiondefs
+    \ifx\temptype\Ynothingkeyword
+      \ifx\sectionlevel\seckeyword
+        \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}%
+                              \gdef\thissection{\thissectionname}}%
+      \fi
+    \else\ifx\temptype\Yomitfromtockeyword
+      % Don't redefine \thissection.
+    \else\ifx\temptype\Yappendixkeyword
+      \ifx\sectionlevel\seckeyword
+        \toks0={#1}%
+        \xdef\lastsectiondefs{%
+          \gdef\noexpand\thissectionname{\the\toks0}%
+          \gdef\noexpand\thissectionnum{#4}%
+          % \noexpand\putwordSection avoids expanding indigestible
+          % commands in some of the translations.
+          \gdef\noexpand\thissection{\noexpand\putwordSection{}
+                                     \noexpand\thissectionnum:
+                                     \noexpand\thissectionname}%
+        }%
+      \fi
+    \else
+      \ifx\sectionlevel\seckeyword
+        \toks0={#1}%
+        \xdef\lastsectiondefs{%
+          \gdef\noexpand\thissectionname{\the\toks0}%
+          \gdef\noexpand\thissectionnum{#4}%
+          % \noexpand\putwordSection avoids expanding indigestible
+          % commands in some of the translations.
+          \gdef\noexpand\thissection{\noexpand\putwordSection{}
+                                     \noexpand\thissectionnum:
+                                     \noexpand\thissectionname}%
+        }%
+      \fi
+    \fi\fi\fi
+    %
+    % Go into vertical mode.  Usually we'll already be there, but we
+    % don't want the following whatsit to end up in a preceding paragraph
+    % if the document didn't happen to have a blank line.
+    \par
+    %
+    % Output the mark.  Pass it through \safewhatsit, to take care of
+    % the preceding space.
+    \safewhatsit\domark
+    %
+    % Insert space above the heading.
+    \csname #2headingbreak\endcsname
+    %
+    % Now the second mark, after the heading break.  No break points
+    % between here and the heading.
+    \global\let\prevsectiondefs=\lastsectiondefs
+    \domark
+    %
+    % Only insert the space after the number if we have a section number.
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unn}%
+      \gdef\lastsection{#1}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      % for @headings -- no section number, don't include in toc,
+      % and don't redefine \lastsection.
+      \setbox0 = \hbox{}%
+      \def\toctype{omit}%
+      \let\sectionlevel=\empty
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{app}%
+      \gdef\lastsection{#1}%
+    \else
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{num}%
+      \gdef\lastsection{#1}%
+    \fi\fi\fi
+    %
+    % Write the toc entry (before \donoderef).  See comments in \chapmacro.
+    \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+    %
+    % Write the node reference (= pdf destination for pdftex).
+    % Again, see comments in \chapmacro.
+    \donoderef{#3}%
+    %
+    % Interline glue will be inserted when the vbox is completed.
+    % That glue will be a valid breakpoint for the page, since it'll be
+    % preceded by a whatsit (usually from the \donoderef, or from the
+    % \writetocentry if there was no node).  We don't want to allow that
+    % break, since then the whatsits could end up on page n while the
+    % section is on page n+1, thus toc/etc. are wrong.  Debian bug 276000.
+    \nobreak
+    %
+    % Output the actual section heading.
+    \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright
+          \hangindent=\wd0  % zero if no section number
+          \unhbox0 #1}%
+  }%
+  % Add extra space after the heading -- half of whatever came above it.
+  % Don't allow stretch, though.
+  \kern .5 \csname #2headingskip\endcsname
+  %
+  % Do not let the kern be a potential breakpoint, as it would be if it
+  % was followed by glue.
+  \nobreak
+  %
+  % We'll almost certainly start a paragraph next, so don't let that
+  % glue accumulate.  (Not a breakpoint because it's preceded by a
+  % discardable item.)  However, when a paragraph is not started next
+  % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out
+  % or the negative glue will cause weirdly wrong output, typically
+  % obscuring the section heading with something else.
+  \vskip-\parskip
+  %
+  % This is so the last item on the main vertical list is a known
+  % \penalty > 10000, so \startdefun, etc., can recognize the situation
+  % and do the needful.
+  \penalty 10001
+}
+
+
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
+
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this.  The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything.  This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+  \edef\writetoctype{#1}%
+  \ifx\writetoctype\omitkeyword \else
+    \iftocfileopened\else
+      \immediate\openout\tocfile = \jobname.toc
+      \global\tocfileopenedtrue
+    \fi
+    %
+    \iflinks
+      {\atdummies
+       \edef\temp{%
+         \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+       \temp
+      }%
+    \fi
+  \fi
+  %
+  % Tell \shipout to create a pdf destination on each page, if we're
+  % writing pdf.  These are used in the table of contents.  We can't
+  % just write one on every page because the title pages are numbered
+  % 1 and 2 (the page numbers aren't printed), and so are the first
+  % two pages of the document.  Thus, we'd have two destinations named
+  % `1', and two named `2'.
+  \ifpdf \global\pdfmakepagedesttrue \fi
+}
+
+
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care.  This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+%
+\def\activecatcodes{%
+  \catcode`\"=\active
+  \catcode`\$=\active
+  \catcode`\<=\active
+  \catcode`\>=\active
+  \catcode`\\=\active
+  \catcode`\^=\active
+  \catcode`\_=\active
+  \catcode`\|=\active
+  \catcode`\~=\active
+}
+
+
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+  \setupdatafile
+  \activecatcodes
+  \input \tocreadfilename
+}
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
+\def\startcontents#1{%
+  % If @setchapternewpage on, and @headings double, the contents should
+  % start on an odd page, unlike chapters.  Thus, we maintain
+  % \contentsalignmacro in parallel with \pagealignmacro.
+  % From: Torbjorn Granlund <tege@matematik.su.se>
+  \contentsalignmacro
+  \immediate\closeout\tocfile
+  %
+  % Don't need to put `Contents' or `Short Contents' in the headline.
+  % It is abundantly clear what they are.
+  \chapmacro{#1}{Yomitfromtoc}{}%
+  %
+  \savepageno = \pageno
+  \begingroup                  % Set up to handle contents files properly.
+    \raggedbottom              % Worry more about breakpoints than the bottom.
+    \entryrightmargin=\contentsrightmargin % Don't use the full line length.
+    %
+    % Roman numerals for page numbers.
+    \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
+}
+
+% redefined for the two-volume lispref.  We always output on
+% \jobname.toc even if this is redefined.
+%
+\def\tocreadfilename{\jobname.toc}
+
+% Normal (long) toc.
+%
+\def\contents{%
+  \startcontents{\putwordTOC}%
+    \openin 1 \tocreadfilename\space
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+    \ifeof 1 \else
+      \pdfmakeoutlines
+    \fi
+    \closein 1
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
+}
+
+% And just the chapters.
+\def\summarycontents{%
+  \startcontents{\putwordShortTOC}%
+    %
+    \let\partentry = \shortpartentry
+    \let\numchapentry = \shortchapentry
+    \let\appentry = \shortchapentry
+    \let\unnchapentry = \shortunnchapentry
+    % We want a true roman here for the page numbers.
+    \secfonts
+    \let\rm=\shortcontrm \let\bf=\shortcontbf
+    \let\sl=\shortcontsl \let\tt=\shortconttt
+    \rm
+    \hyphenpenalty = 10000
+    \advance\baselineskip by 1pt % Open it up a little.
+    \def\numsecentry##1##2##3##4{}
+    \let\appsecentry = \numsecentry
+    \let\unnsecentry = \numsecentry
+    \let\numsubsecentry = \numsecentry
+    \let\appsubsecentry = \numsecentry
+    \let\unnsubsecentry = \numsecentry
+    \let\numsubsubsecentry = \numsecentry
+    \let\appsubsubsecentry = \numsecentry
+    \let\unnsubsubsecentry = \numsecentry
+    \openin 1 \tocreadfilename\space
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \closein 1
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
+}
+\let\shortcontents = \summarycontents
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+  % This space should be enough, since a single number is .5em, and the
+  % widest letter (M) is 1em, at least in the Computer Modern fonts.
+  % But use \hss just in case.
+  % (This space doesn't include the extra space that gets added after
+  % the label; that gets put in by \shortchapentry above.)
+  %
+  % We'd like to right-justify chapter numbers, but that looks strange
+  % with appendix letters.  And right-justifying numbers and
+  % left-justifying letters looks strange when there is less than 10
+  % chapters.  Have to read the whole toc once to know how many chapters
+  % there are before deciding ...
+  \hbox to 1em{#1\hss}%
+}
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Parts, in the main contents.  Replace the part number, which doesn't
+% exist, with an empty box.  Let's hope all the numbers have the same width.
+% Also ignore the page number, which is conventionally not printed.
+\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}}
+\def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}}
+%
+% Parts, in the short toc.
+\def\shortpartentry#1#2#3#4{%
+  \penalty-300
+  \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip
+  \shortchapentry{{\bf #1}}{\numeralbox}{}{}%
+}
+
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+  \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
+}
+
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+  % We use M since it's probably the widest letter.
+  \setbox0 = \hbox{\putwordAppendix{} M}%
+  \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+   \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+   \begingroup
+     % Move the page numbers slightly to the right
+     \advance\entryrightmargin by -0.05em
+     \chapentryfonts
+     \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+   \endgroup
+   \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+  \secentryfonts \leftskip=\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+  \subsecentryfonts \leftskip=2\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+  \subsubsecentryfonts \leftskip=3\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
+\message{environments,}
+% @foo ... @end foo.
+
+% @tex ... @end tex    escapes into raw TeX temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain @ character.
+
+\envdef\tex{%
+  \setupmarkupstyle{tex}%
+  \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+  \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+  \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+  \catcode `\%=14
+  \catcode `\+=\other
+  \catcode `\"=\other
+  \catcode `\|=\other
+  \catcode `\<=\other
+  \catcode `\>=\other
+  \catcode `\`=\other
+  \catcode `\'=\other
+  \escapechar=`\\
+  %
+  % ' is active in math mode (mathcode"8000).  So reset it, and all our
+  % other math active characters (just in case), to plain's definitions.
+  \mathactive
+  %
+  % Inverse of the list at the beginning of the file.
+  \let\b=\ptexb
+  \let\bullet=\ptexbullet
+  \let\c=\ptexc
+  \let\,=\ptexcomma
+  \let\.=\ptexdot
+  \let\dots=\ptexdots
+  \let\equiv=\ptexequiv
+  \let\!=\ptexexclam
+  \let\i=\ptexi
+  \let\indent=\ptexindent
+  \let\noindent=\ptexnoindent
+  \let\{=\ptexlbrace
+  \let\+=\tabalign
+  \let\}=\ptexrbrace
+  \let\/=\ptexslash
+  \let\sp=\ptexsp
+  \let\*=\ptexstar
+  %\let\sup=\ptexsup % do not redefine, we want @sup to work in math mode
+  \let\t=\ptext
+  \expandafter \let\csname top\endcsname=\ptextop  % we've made it outer
+  \let\frenchspacing=\plainfrenchspacing
+  %
+  \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+  \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+  \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments.  \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical.  We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip.
+%
+\def\aboveenvbreak{{%
+  % =10000 instead of <10000 because of a special case in \itemzzz and
+  % \sectionheading, q.v.
+  \ifnum \lastpenalty=10000 \else
+    \advance\envskipamount by \parskip
+    \endgraf
+    \ifdim\lastskip<\envskipamount
+      \removelastskip
+      \ifnum\lastpenalty<10000
+        % Penalize breaking before the environment, because preceding text
+        % often leads into it.
+        \penalty100
+      \fi
+      \vskip\envskipamount
+    \fi
+  \fi
+}}
+
+\def\afterenvbreak{{%
+  % =10000 instead of <10000 because of a special case in \itemzzz and
+  % \sectionheading, q.v.
+  \ifnum \lastpenalty=10000 \else
+    \advance\envskipamount by \parskip
+    \endgraf
+    \ifdim\lastskip<\envskipamount
+      \removelastskip
+      % it's not a good place to break if the last penalty was \nobreak
+      % or better ...
+      \ifnum\lastpenalty<10000 \penalty-50 \fi
+      \vskip\envskipamount
+    \fi
+  \fi
+}}
+
+% \nonarrowing is a flag.  If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+        \ctl\leaders\hrule height\circthick\hfil\ctr
+        \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+        \cbl\leaders\hrule height\circthick\hfil\cbr
+        \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\envdef\cartouche{%
+  \ifhmode\par\fi  % can't be in the midst of a paragraph.
+  \startsavinginserts
+  \lskip=\leftskip \rskip=\rightskip
+  \leftskip=0pt\rightskip=0pt % we want these *outside*.
+  \cartinner=\hsize \advance\cartinner by-\lskip
+  \advance\cartinner by-\rskip
+  \cartouter=\hsize
+  \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+                               % side, and for 6pt waste from
+                               % each corner char, and rule thickness
+  \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+  %
+  % If this cartouche directly follows a sectioning command, we need the
+  % \parskip glue (backspaced over by default) or the cartouche can
+  % collide with the section heading.
+  \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi
+  %
+  \setbox\groupbox=\vbox\bgroup
+      \baselineskip=0pt\parskip=0pt\lineskip=0pt
+      \carttop
+      \hbox\bgroup
+         \hskip\lskip
+         \vrule\kern3pt
+         \vbox\bgroup
+             \kern3pt
+             \hsize=\cartinner
+             \baselineskip=\normbskip
+             \lineskip=\normlskip
+             \parskip=\normpskip
+             \vskip -\parskip
+             \comment % For explanation, see the end of def\group.
+}
+\def\Ecartouche{%
+              \ifhmode\par\fi
+             \kern3pt
+         \egroup
+         \kern3pt\vrule
+         \hskip\rskip
+      \egroup
+      \cartbot
+  \egroup
+  \addgroupbox
+  \checkinserts
+}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\newdimen\nonfillparindent
+\def\nonfillstart{%
+  \aboveenvbreak
+  \ifdim\hfuzz < 12pt \hfuzz = 12pt \fi % Don't be fussy
+  \sepspaces % Make spaces be word-separators rather than space tokens.
+  \let\par = \lisppar % don't ignore blank lines
+  \obeylines % each line of input is a line of output
+  \parskip = 0pt
+  % Turn off paragraph indentation but redefine \indent to emulate
+  % the normal \indent.
+  \nonfillparindent=\parindent
+  \parindent = 0pt
+  \let\indent\nonfillindent
+  %
+  \emergencystretch = 0pt % don't try to avoid overfull boxes
+  \ifx\nonarrowing\relax
+    \advance \leftskip by \lispnarrowing
+    \exdentamount=\lispnarrowing
+  \else
+    \let\nonarrowing = \relax
+  \fi
+  \let\exdent=\nofillexdent
+}
+
+\begingroup
+\obeyspaces
+% We want to swallow spaces (but not other tokens) after the fake
+% @indent in our nonfill-environments, where spaces are normally
+% active and set to @tie, resulting in them not being ignored after
+% @indent.
+\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}%
+\gdef\nonfillindentcheck{%
+\ifx\temp %
+\expandafter\nonfillindentgobble%
+\else%
+\leavevmode\nonfillindentbox%
+\fi%
+}%
+\endgroup
+\def\nonfillindentgobble#1{\nonfillindent}
+\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}}
+
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+%    @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+  \ifx\SETdispenvsize\smallword
+    % end paragraph for sake of leading, in case document has no blank
+    % line.  This is redundant with what happens in \aboveenvbreak, but
+    % we need to do it before changing the fonts, and it's inconvenient
+    % to change the fonts afterward.
+    \ifnum \lastpenalty=10000 \else \endgraf \fi
+    \smallexamplefonts \rm
+  \fi
+}
+\def\setsmalldispenv{%
+  \ifx\SETdispenvsize\nosmallword
+  \else
+    \ifnum \lastpenalty=10000 \else \endgraf \fi
+    \smallexamplefonts \rm
+  \fi
+}
+
+% We often define two environments, @foo and @smallfoo.
+% Let's do it in one command.  #1 is the env name, #2 the definition.
+\def\makedispenvdef#1#2{%
+  \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}%
+  \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}%
+  \expandafter\let\csname E#1\endcsname \afterenvbreak
+  \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two environment synonyms (#1 and #2) for an environment.
+\def\maketwodispenvdef#1#2#3{%
+  \makedispenvdef{#1}{#3}%
+  \makedispenvdef{#2}{#3}%
+}
+%
+% @lisp: indented, narrowed, typewriter font;
+% @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvdef{lisp}{example}{%
+  \nonfillstart
+  \tt\setupmarkupstyle{example}%
+  \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+  \gobble % eat return
+}
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenvdef{display}{%
+  \nonfillstart
+  \gobble
+}
+
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenvdef{format}{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \gobble
+}
+
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \gobble
+}
+\let\Eflushleft = \afterenvbreak
+
+% @flushright.
+%
+\envdef\flushright{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \advance\leftskip by 0pt plus 1fill\relax
+  \gobble
+}
+\let\Eflushright = \afterenvbreak
+
+
+% @raggedright does more-or-less normal line breaking but no right
+% justification.  From plain.tex.  Don't stretch around special
+% characters in urls in this environment, since the stretch at the right
+% should be enough.
+\envdef\raggedright{%
+  \rightskip0pt plus2.4em \spaceskip.3333em \xspaceskip.5em\relax
+  \def\urefprestretchamount{0pt}%
+  \def\urefpoststretchamount{0pt}%
+}
+\let\Eraggedright\par
+
+\envdef\raggedleft{%
+  \parindent=0pt \leftskip0pt plus2em
+  \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+  \hbadness=10000 % Last line will usually be underfull, so turn off
+                  % badness reporting.
+}
+\let\Eraggedleft\par
+
+\envdef\raggedcenter{%
+  \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em
+  \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+  \hbadness=10000 % Last line will usually be underfull, so turn off
+                  % badness reporting.
+}
+\let\Eraggedcenter\par
+
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins.  We keep \parskip nonzero in general, since
+% we're doing normal filling.  So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\makedispenvdef{quotation}{\quotationstart}
+%
+\def\quotationstart{%
+  \indentedblockstart % same as \indentedblock, but increase right margin too.
+  \ifx\nonarrowing\relax
+    \advance\rightskip by \lispnarrowing
+  \fi
+  \parsearg\quotationlabel
+}
+
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+  \par
+  \ifx\quotationauthor\thisisundefined\else
+    % indent a bit.
+    \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+  \fi
+  {\parskip=0pt \afterenvbreak}%
+}
+\def\Esmallquotation{\Equotation}
+
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty \else
+    {\bf #1: }%
+  \fi
+}
+
+% @indentedblock is like @quotation, but indents only on the left and
+% has no optional argument.
+% 
+\makedispenvdef{indentedblock}{\indentedblockstart}
+%
+\def\indentedblockstart{%
+  {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+  \parindent=0pt
+  %
+  % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+  \ifx\nonarrowing\relax
+    \advance\leftskip by \lispnarrowing
+    \exdentamount = \lispnarrowing
+  \else
+    \let\nonarrowing = \relax
+  \fi
+}
+
+% Keep a nonzero parskip for the environment, since we're doing normal filling.
+%
+\def\Eindentedblock{%
+  \par
+  {\parskip=0pt \afterenvbreak}%
+}
+\def\Esmallindentedblock{\Eindentedblock}
+
+
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command.  --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996.  The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too.  Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+  \do\ \do\\\do\{\do\}\do\$\do\&%
+  \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+  \do\<\do\>\do\|\do\@\do+\do\"%
+  % Don't do the quotes -- if we do, @set txicodequoteundirected and
+  % @set txicodequotebacktick will not have effect on @verb and
+  % @verbatim, and ?` and !` ligatures won't get disabled.
+  %\do\`\do\'%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+  \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+  \tt  % easiest (and conventionally used) font for verbatim
+  \def\par{\leavevmode\endgraf}%
+  \setupmarkupstyle{verb}%
+  \tabeightspaces
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count
+  % must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
+}
+
+% Setup for the @verbatim environment
+%
+% Real tab expansion.
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+% We typeset each line of the verbatim in an \hbox, so we can handle
+% tabs.  The \global is in case the verbatim line starts with an accent,
+% or some other command that starts with a begin-group.  Otherwise, the
+% entire \verbbox would disappear at the corresponding end-group, before
+% it is typeset.  Meanwhile, we can't have nested verbatim commands
+% (can we?), so the \global won't be overwriting itself.
+\newbox\verbbox
+\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup}
+%
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabexpand{%
+    \catcode`\^^I=\active
+    \def^^I{\leavevmode\egroup
+      \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab
+      \divide\dimen\verbbox by\tabw
+      \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw
+      \advance\dimen\verbbox by\tabw  % advance to next multiple of \tabw
+      \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox
+    }%
+  }
+\endgroup
+
+% start the verbatim environment.
+\def\setupverbatim{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \tt % easiest (and conventionally used) font for verbatim
+  % The \leavevmode here is for blank lines.  Otherwise, we would
+  % never \starttabox and the \egroup would end verbatim mode.
+  \def\par{\leavevmode\egroup\box\verbbox\endgraf}%
+  \tabexpand
+  \setupmarkupstyle{verbatim}%
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count.
+  % Must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
+  \everypar{\starttabbox}%
+}
+
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters.  Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+%    \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+  \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+  \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+%     \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+  \catcode`\ =\active
+  \obeylines %
+  % ignore everything up to the first ^^M, that's the newline at the end
+  % of the @verbatim input line itself.  Otherwise we get an extra blank
+  % line in the output.
+  \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+  % We really want {...\end verbatim} in the body of the macro, but
+  % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+    \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
+
+
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+  {%
+    \makevalueexpandable
+    \setupverbatim
+    \indexnofonts       % Allow `@@' and other weird things in file names.
+    \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}%
+    \input #1
+    \afterenvbreak
+  }%
+}
+
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+  \begingroup
+    \parindent = 0pt  % paragraph indentation looks wrong on title page
+    \scanexp\copyingtext
+  \endgroup
+}
+
+
+\message{defuns,}
+% @defun etc.
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+\newcount\defunpenalty
+
+% Start the processing of @deffn:
+\def\startdefun{%
+  \ifnum\lastpenalty<10000
+    \medbreak
+    \defunpenalty=10003 % Will keep this @deffn together with the
+                        % following @def command, see below.
+  \else
+    % If there are two @def commands in a row, we'll have a \nobreak,
+    % which is there to keep the function description together with its
+    % header.  But if there's nothing but headers, we need to allow a
+    % break somewhere.  Check specifically for penalty 10002, inserted
+    % by \printdefunline, instead of 10000, since the sectioning
+    % commands also insert a nobreak penalty, and we don't want to allow
+    % a break between a section heading and a defun.
+    %
+    % As a further refinement, we avoid "club" headers by signalling
+    % with penalty of 10003 after the very first @deffn in the
+    % sequence (see above), and penalty of 10002 after any following
+    % @def command.
+    \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi
+    %
+    % Similarly, after a section heading, do not allow a break.
+    % But do insert the glue.
+    \medskip  % preceded by discardable penalty, so not a breakpoint
+  \fi
+  %
+  \parindent=0in
+  \advance\leftskip by \defbodyindent
+  \exdentamount=\defbodyindent
+}
+
+\def\dodefunx#1{%
+  % First, check whether we are in the right environment:
+  \checkenv#1%
+  %
+  % As above, allow line break if we have multiple x headers in a row.
+  % It's not a great place, though.
+  \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi
+  %
+  % And now, it's time to reuse the body of the original defun:
+  \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
+
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+  \begingroup
+    % call \deffnheader:
+    #1#2 \endheader
+    % common ending:
+    \interlinepenalty = 10000
+    \advance\rightskip by 0pt plus 1fil\relax
+    \endgraf
+    \nobreak\vskip -\parskip
+    \penalty\defunpenalty  % signal to \startdefun and \dodefunx
+    % Some of the @defun-type tags do not enable magic parentheses,
+    % rendering the following check redundant.  But we don't optimize.
+    \checkparencounts
+  \endgroup
+}
+
+\def\Edefun{\endgraf\medbreak}
+
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remaining is to define \deffnheader.
+%
+\def\makedefun#1{%
+  \expandafter\let\csname E#1\endcsname = \Edefun
+  \edef\temp{\noexpand\domakedefun
+    \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+  \temp
+}
+
+% \domakedefun \deffn \deffnx \deffnheader { (defn. of \deffnheader) }
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+  \envdef#1{%
+    \startdefun
+    \doingtypefnfalse    % distinguish typed functions from all else
+    \parseargusing\activeparens{\printdefunline#3}%
+  }%
+  \def#2{\dodefunx#1}%
+  \def#3%
+}
+
+\newif\ifdoingtypefn       % doing typed function?
+\newif\ifrettypeownline    % typeset return type on its own line?
+
+% @deftypefnnewline on|off says whether the return type of typed functions
+% are printed on their own line.  This affects @deftypefn, @deftypefun,
+% @deftypeop, and @deftypemethod.
+% 
+\parseargdef\deftypefnnewline{%
+  \def\temp{#1}%
+  \ifx\temp\onword
+    \expandafter\let\csname SETtxideftypefnnl\endcsname
+      = \empty
+  \else\ifx\temp\offword
+    \expandafter\let\csname SETtxideftypefnnl\endcsname
+      = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @txideftypefnnl value `\temp',
+                must be on|off}%
+  \fi\fi
+}
+
+% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
+
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
+
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+  % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+  \dosubind{fn}{\code{#3}}{#1}%
+  \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
+}
+
+% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
+
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{fn}{\code{#4}}{#1}%
+  \doingtypefntrue
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
+
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{vr}{\code{#4}}{#1}%
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+% Untyped variables:
+
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
+
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
+
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
+
+% Types:
+
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+  \doind{tp}{\code{#2}}%
+  \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
+
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+  \par
+  % Get the values of \leftskip and \rightskip as they were outside the @def...
+  \advance\leftskip by -\defbodyindent
+  %
+  % Determine if we are typesetting the return type of a typed function
+  % on a line by itself.
+  \rettypeownlinefalse
+  \ifdoingtypefn  % doing a typed function specifically?
+    % then check user option for putting return type on its own line:
+    \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else
+      \rettypeownlinetrue
+    \fi
+  \fi
+  %
+  % How we'll format the category name.  Putting it in brackets helps
+  % distinguish it from the body text that may end up on the next line
+  % just below it.
+  \def\temp{#1}%
+  \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+  %
+  % Figure out line sizes for the paragraph shape.  We'll always have at
+  % least two.
+  \tempnum = 2
+  %
+  % The first line needs space for \box0; but if \rightskip is nonzero,
+  % we need only space for the part of \box0 which exceeds it:
+  \dimen0=\hsize  \advance\dimen0 by -\wd0  \advance\dimen0 by \rightskip
+  %
+  % If doing a return type on its own line, we'll have another line.
+  \ifrettypeownline
+    \advance\tempnum by 1
+    \def\maybeshapeline{0in \hsize}%
+  \else
+    \def\maybeshapeline{}%
+  \fi
+  %
+  % The continuations:
+  \dimen2=\hsize  \advance\dimen2 by -\defargsindent
+  %
+  % The final paragraph shape:
+  \parshape \tempnum  0in \dimen0  \maybeshapeline  \defargsindent \dimen2
+  %
+  % Put the category name at the right margin.
+  \noindent
+  \hbox to 0pt{%
+    \hfil\box0 \kern-\hsize
+    % \hsize has to be shortened this way:
+    \kern\leftskip
+    % Intentionally do not respect \rightskip, since we need the space.
+  }%
+  %
+  % Allow all lines to be underfull without complaint:
+  \tolerance=10000 \hbadness=10000
+  \exdentamount=\defbodyindent
+  {%
+    % defun fonts. We use typewriter by default (used to be bold) because:
+    % . we're printing identifiers, they should be in tt in principle.
+    % . in languages with many accents, such as Czech or French, it's
+    %   common to leave accents off identifiers.  The result looks ok in
+    %   tt, but exceedingly strange in rm.
+    % . we don't want -- and --- to be treated as ligatures.
+    % . this still does not fix the ?` and !` ligatures, but so far no
+    %   one has made identifiers using them :).
+    \df \tt
+    \def\temp{#2}% text of the return type
+    \ifx\temp\empty\else
+      \tclose{\temp}% typeset the return type
+      \ifrettypeownline
+        % put return type on its own line; prohibit line break following:
+        \hfil\vadjust{\nobreak}\break  
+      \else
+        \space  % type on same line, so just followed by a space
+      \fi
+    \fi           % no return type
+    #3% output function name
+  }%
+  {\rm\enskip}% hskip 0.5 em of \tenrm
+  %
+  \boldbrax
+  % arguments will be output next, if any.
+}
+
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name.  This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable.  Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+  % use sl by default (not ttsl),
+  % tt for the names.
+  \df \sl \hyphenchar\font=0
+  %
+  % On the other hand, if an argument has two dashes (for instance), we
+  % want a way to get ttsl.  We used to recommend @var for that, so
+  % leave the code in, but it's strange for @var to lead to typewriter.
+  % Nowadays we recommend @code, since the difference between a ttsl hyphen
+  % and a tt hyphen is pretty tiny.  @code also disables ?` !`.
+  \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}%
+  #1%
+  \sl\hyphenchar\font=45
+}
+
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+  \catcode`\(=\active \catcode`\)=\active
+  \catcode`\[=\active \catcode`\]=\active
+  \catcode`\&=\active
+}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+% Be sure that we always have a definition for `(', etc.  For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+  \activeparens
+  \global\let(=\lparen \global\let)=\rparen
+  \global\let[=\lbrack \global\let]=\rbrack
+  \global\let& = \&
+
+  \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+  \gdef\magicamp{\let&=\amprm}
+}
+
+\newcount\parencount
+
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+  \ifampseen
+    % At the first level, print parens in roman,
+    % otherwise use the default font.
+    \ifnum \parencount=1 \rm \fi
+  \else
+    % The \sf parens (in \boldbrax) actually are a little bolder than
+    % the contained text.  This is especially needed for [ and ] .
+    \sf
+  \fi
+}
+\def\infirstlevel#1{%
+  \ifampseen
+    \ifnum\parencount=1
+      #1%
+    \fi
+  \fi
+}
+\def\bfafterword#1 {#1 \bf}
+
+\def\opnr{%
+  \global\advance\parencount by 1
+  {\parenfont(}%
+  \infirstlevel \bfafterword
+}
+\def\clnr{%
+  {\parenfont)}%
+  \infirstlevel \sl
+  \global\advance\parencount by -1
+}
+
+\newcount\brackcount
+\def\lbrb{%
+  \global\advance\brackcount by 1
+  {\bf[}%
+}
+\def\rbrb{%
+  {\bf]}%
+  \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+  \ifnum\parencount=0 \else \badparencount \fi
+  \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+% these should not use \errmessage; the glibc manual, at least, actually
+% has such constructs (when documenting function pointers).
+\def\badparencount{%
+  \message{Warning: unbalanced parentheses in @def...}%
+  \global\parencount=0
+}
+\def\badbrackcount{%
+  \message{Warning: unbalanced square brackets in @def...}%
+  \global\brackcount=0
+}
+
+
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\thisisundefined
+  \newwrite\macscribble
+  \def\scantokens#1{%
+    \toks0={#1}%
+    \immediate\openout\macscribble=\jobname.tmp
+    \immediate\write\macscribble{\the\toks0}%
+    \immediate\closeout\macscribble
+    \input \jobname.tmp
+  }
+\fi
+
+\let\aftermacroxxx\relax
+\def\aftermacro{\aftermacroxxx}
+
+% alias because \c means cedilla in @tex or @math
+\let\texinfoc=\c
+
+% Used at the time of macro expansion.
+% Argument is macro body with arguments substituted
+\def\scanmacro#1{%
+  \newlinechar`\^^M
+  \def\xprocessmacroarg{\eatspaces}%
+  %
+  % Process the macro body under the current catcode regime.
+  \scantokens{#1\texinfoc}\aftermacro%
+  %
+  % The \c is to remove the \newlinechar added by \scantokens, and
+  % can be noticed by \parsearg.
+  %   The \aftermacro allows a \comment at the end of the macro definition
+  % to duplicate itself past the final \newlinechar added by \scantokens:
+  % this is used in the definition of \group to comment out a newline.  We
+  % don't do the same for \c to support Texinfo files with macros that ended
+  % with a @c, which should no longer be necessary.
+  %   We avoid surrounding the call to \scantokens with \bgroup and \egroup
+  % to allow macros to open or close groups themselves.
+}
+
+% Used for copying and captions
+\def\scanexp#1{%
+  \bgroup
+  % Undo catcode changes of \startcontents and \printindex
+  % When called from @insertcopying or (short)caption, we need active
+  % backslash to get it printed correctly.
+  % FIXME: This may not be needed.
+  %\catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+  \edef\temp{\noexpand\scanmacro{#1}}%
+  \temp
+  \egroup
+}
+
+\newcount\paramno   % Count of parameters
+\newtoks\macname    % Macro name
+\newif\ifrecursive  % Is it recursive?
+
+% List of all defined macros in the form
+%    \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+     \toks0 = \expandafter{\macrolist\definedummyword#1}%
+     \xdef\macrolist{\the\toks0}%
+}
+
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+%   \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+%
+\def\cslet#1#2{%
+  \expandafter\let
+  \csname#1\expandafter\endcsname
+  \csname#2\endcsname
+}
+
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
+
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
+
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \
+% to recognize macro arguments; this is the job of \mbodybackslash.
+%
+% Non-ASCII encodings make 8-bit characters active, so un-activate
+% them to avoid their expansion.  Must do this non-globally, to
+% confine the change to the current group.
+%
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+%
+\def\scanctxt{% used as subroutine
+  \catcode`\"=\other
+  \catcode`\+=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\^=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\~=\other
+  \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi
+}
+
+\def\scanargctxt{% used for copying and captions, not macros.
+  \scanctxt
+  \catcode`\@=\other
+  \catcode`\\=\other
+  \catcode`\^^M=\other
+}
+
+\def\macrobodyctxt{% used for @macro definitions
+  \scanctxt
+  \catcode`\ =\other
+  \catcode`\@=\other
+  \catcode`\{=\other
+  \catcode`\}=\other
+  \catcode`\^^M=\other
+  \usembodybackslash
+}
+
+% Used when scanning braced macro arguments.  Note, however, that catcode
+% changes here are ineffectual if the macro invocation was nested inside
+% an argument to another Texinfo command.
+\def\macroargctxt{%
+  \scanctxt
+  \catcode`\ =\active
+  \catcode`\^^M=\other
+  \catcode`\\=\active
+}
+
+\def\macrolineargctxt{% used for whole-line arguments without braces
+  \scanctxt
+  \catcode`\{=\other
+  \catcode`\}=\other
+}
+
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
+%
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\margbackslash#1{\char`\#1 }
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+  \getargs{#1}% now \macname is the macname and \argl the arglist
+  \ifx\argl\empty       % no arguments
+     \paramno=0\relax
+  \else
+     \expandafter\parsemargdef \argl;%
+     \if\paramno>256\relax
+       \ifx\eTeXversion\thisisundefined
+         \errhelp = \EMsimple
+         \errmessage{You need eTeX to compile a file with macros with more than 256 arguments}
+       \fi
+     \fi
+  \fi
+  \if1\csname ismacro.\the\macname\endcsname
+     \message{Warning: redefining \the\macname}%
+  \else
+     \expandafter\ifx\csname \the\macname\endcsname \relax
+     \else \errmessage{Macro name \the\macname\space already defined}\fi
+     \global\cslet{macsave.\the\macname}{\the\macname}%
+     \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+     \addtomacrolist{\the\macname}%
+  \fi
+  \begingroup \macrobodyctxt
+  \ifrecursive \expandafter\parsermacbody
+  \else \expandafter\parsemacbody
+  \fi}
+
+\parseargdef\unmacro{%
+  \if1\csname ismacro.#1\endcsname
+    \global\cslet{#1}{macsave.#1}%
+    \global\expandafter\let \csname ismacro.#1\endcsname=0%
+    % Remove the macro name from \macrolist:
+    \begingroup
+      \expandafter\let\csname#1\endcsname \relax
+      \let\definedummyword\unmacrodo
+      \xdef\macrolist{\macrolist}%
+    \endgroup
+  \else
+    \errmessage{Macro #1 not defined}%
+  \fi
+}
+
+% Called by \do from \dounmacro on each macro.  The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+  \ifx #1\relax
+    % remove this
+  \else
+    \noexpand\definedummyword \noexpand#1%
+  \fi
+}
+
+% \getargs -- Parse the arguments to a @macro line.  Set \macname to
+% the name of the macro, and \argl to the braced argument list.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname#1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+% This made use of the feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+
+% Parse the optional {params} list to @macro or @rmacro.
+% Set \paramno to the number of arguments,
+% and \paramlist to a parameter text for the macro (e.g. #1,#2,#3 for a
+% three-param macro.)  Define \macarg.BLAH for each BLAH in the params
+% list to some hook where the argument is to be expanded.  If there are
+% less than 10 arguments that hook is to be replaced by ##N where N
+% is the position in that list, that is to say the macro arguments are to be
+% defined `a la TeX in the macro body.  
+%
+% That gets used by \mbodybackslash (above).
+%
+% If there are 10 or more arguments, a different technique is used: see
+% \parsemmanyargdef.
+%
+\def\parsemargdef#1;{%
+  \paramno=0\def\paramlist{}%
+  \let\hash\relax
+  % \hash is redefined to `#' later to get it into definitions
+  \let\processmacroarg\relax
+  \parsemargdefxxx#1,;,%
+  \ifnum\paramno<10\relax\else
+    \paramno0\relax
+    \parsemmanyargdef@@#1,;,% 10 or more arguments
+  \fi
+}
+\def\parsemargdefxxx#1,{%
+  \if#1;\let\next=\relax
+  \else \let\next=\parsemargdefxxx
+    \advance\paramno by 1
+    \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+        {\processmacroarg{\hash\the\paramno}}%
+    \edef\paramlist{\paramlist\hash\the\paramno,}%
+  \fi\next}
+
+% \parsemacbody, \parsermacbody
+%
+% Read recursive and nonrecursive macro bodies. (They're different since
+% rec and nonrec macros end differently.)
+% 
+% We are in \macrobodyctxt, and the \xdef causes backslashshes in the macro 
+% body to be transformed.
+% Set \macrobody to the body of the macro, and call \defmacro.
+%
+{\catcode`\ =\other\long\gdef\parsemacbody#1@end macro{%
+\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}%
+{\catcode`\ =\other\long\gdef\parsermacbody#1@end rmacro{%
+\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}%
+
+% Make @ a letter, so that we can make private-to-Texinfo macro names.
+\edef\texiatcatcode{\the\catcode`\@}
+\catcode `@=11\relax
+
+%%%%%%%%%%%%%% Code for > 10 arguments only   %%%%%%%%%%%%%%%%%%
+
+% If there are 10 or more arguments, a different technique is used, where the
+% hook remains in the body, and when macro is to be expanded the body is
+% processed again to replace the arguments.
+%
+% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the
+% argument N value and then \edef the body (nothing else will expand because of
+% the catcode regime under which the body was input).
+%
+% If you compile with TeX (not eTeX), and you have macros with 10 or more
+% arguments, no macro can have more than 256 arguments (else error).
+%
+% In case that there are 10 or more arguments we parse again the arguments
+% list to set new definitions for the \macarg.BLAH macros corresponding to
+% each BLAH argument. It was anyhow needed to parse already once this list
+% in order to count the arguments, and as macros with at most 9 arguments
+% are by far more frequent than macro with 10 or more arguments, defining
+% twice the \macarg.BLAH macros does not cost too much processing power.
+\def\parsemmanyargdef@@#1,{%
+  \if#1;\let\next=\relax
+  \else 
+    \let\next=\parsemmanyargdef@@
+    \edef\tempb{\eatspaces{#1}}%
+    \expandafter\def\expandafter\tempa
+       \expandafter{\csname macarg.\tempb\endcsname}%
+    % Note that we need some extra \noexpand\noexpand, this is because we
+    % don't want \the  to be expanded in the \parsermacbody  as it uses an
+    % \xdef .
+    \expandafter\edef\tempa
+      {\noexpand\noexpand\noexpand\the\toks\the\paramno}%
+    \advance\paramno by 1\relax
+  \fi\next}
+
+
+\let\endargs@\relax
+\let\nil@\relax
+\def\nilm@{\nil@}%
+\long\def\nillm@{\nil@}%
+
+% This macro is expanded during the Texinfo macro expansion, not during its
+% definition.  It gets all the arguments' values and assigns them to macros
+% macarg.ARGNAME
+%
+% #1 is the macro name
+% #2 is the list of argument names
+% #3 is the list of argument values
+\def\getargvals@#1#2#3{%
+  \def\macargdeflist@{}%
+  \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion.
+  \def\paramlist{#2,\nil@}%
+  \def\macroname{#1}%
+  \begingroup
+  \macroargctxt
+  \def\argvaluelist{#3,\nil@}%
+  \def\@tempa{#3}%
+  \ifx\@tempa\empty
+    \setemptyargvalues@
+  \else
+    \getargvals@@
+  \fi
+}
+\def\getargvals@@{%
+  \ifx\paramlist\nilm@
+      % Some sanity check needed here that \argvaluelist is also empty.
+      \ifx\argvaluelist\nillm@
+      \else
+        \errhelp = \EMsimple
+        \errmessage{Too many arguments in macro `\macroname'!}%
+      \fi
+      \let\next\macargexpandinbody@
+  \else
+    \ifx\argvaluelist\nillm@
+       % No more arguments values passed to macro.  Set remaining named-arg
+       % macros to empty.
+       \let\next\setemptyargvalues@
+    \else
+      % pop current arg name into \@tempb
+      \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}%
+      \expandafter\@tempa\expandafter{\paramlist}%
+       % pop current argument value into \@tempc
+      \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}%
+      \expandafter\@tempa\expandafter{\argvaluelist}%
+       % Here \@tempb is the current arg name and \@tempc is the current arg value.
+       % First place the new argument macro definition into \@tempd
+       \expandafter\macname\expandafter{\@tempc}%
+       \expandafter\let\csname macarg.\@tempb\endcsname\relax
+       \expandafter\def\expandafter\@tempe\expandafter{%
+         \csname macarg.\@tempb\endcsname}%
+       \edef\@tempd{\long\def\@tempe{\the\macname}}%
+       \push@\@tempd\macargdeflist@
+       \let\next\getargvals@@
+    \fi
+  \fi
+  \next
+}
+
+\def\push@#1#2{%
+  \expandafter\expandafter\expandafter\def
+  \expandafter\expandafter\expandafter#2%
+  \expandafter\expandafter\expandafter{%
+  \expandafter#1#2}%
+}
+
+% Replace arguments by their values in the macro body, and place the result
+% in macro \@tempa.
+% 
+\def\macvalstoargs@{%
+  %  To do this we use the property that token registers that are \the'ed
+  % within an \edef  expand only once. So we are going to place all argument
+  % values into respective token registers.
+  %
+  % First we save the token context, and initialize argument numbering.
+  \begingroup
+    \paramno0\relax
+    % Then, for each argument number #N, we place the corresponding argument
+    % value into a new token list register \toks#N
+    \expandafter\putargsintokens@\saveparamlist@,;,%
+    % Then, we expand the body so that argument are replaced by their
+    % values. The trick for values not to be expanded themselves is that they
+    % are within tokens and that tokens expand only once in an \edef .
+    \edef\@tempc{\csname mac.\macroname .body\endcsname}%
+    % Now we restore the token stack pointer to free the token list registers
+    % which we have used, but we make sure that expanded body is saved after
+    % group.
+    \expandafter
+  \endgroup
+  \expandafter\def\expandafter\@tempa\expandafter{\@tempc}%
+  }
+
+% Define the named-macro outside of this group and then close this group. 
+% 
+\def\macargexpandinbody@{% 
+  \expandafter
+  \endgroup
+  \macargdeflist@
+  % First the replace in body the macro arguments by their values, the result
+  % is in \@tempa .
+  \macvalstoargs@
+  % Then we point at the \norecurse or \gobble (for recursive) macro value
+  % with \@tempb .
+  \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname
+  % Depending on whether it is recursive or not, we need some tailing
+  % \egroup .
+  \ifx\@tempb\gobble
+     \let\@tempc\relax
+  \else
+     \let\@tempc\egroup
+  \fi
+  % And now we do the real job:
+  \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}%
+  \@tempd
+}
+
+\def\putargsintokens@#1,{%
+  \if#1;\let\next\relax
+  \else
+    \let\next\putargsintokens@
+    % First we allocate the new token list register, and give it a temporary
+    % alias \@tempb .
+    \toksdef\@tempb\the\paramno
+    % Then we place the argument value into that token list register.
+    \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname
+    \expandafter\@tempb\expandafter{\@tempa}%
+    \advance\paramno by 1\relax
+  \fi
+  \next
+}
+
+% Trailing missing arguments are set to empty.
+% 
+\def\setemptyargvalues@{%
+  \ifx\paramlist\nilm@
+    \let\next\macargexpandinbody@
+  \else
+    \expandafter\setemptyargvaluesparser@\paramlist\endargs@
+    \let\next\setemptyargvalues@
+  \fi
+  \next
+}
+
+\def\setemptyargvaluesparser@#1,#2\endargs@{%
+  \expandafter\def\expandafter\@tempa\expandafter{%
+    \expandafter\def\csname macarg.#1\endcsname{}}%
+  \push@\@tempa\macargdeflist@
+  \def\paramlist{#2}%
+}
+
+% #1 is the element target macro
+% #2 is the list macro
+% #3,#4\endargs@ is the list value
+\def\pop@#1#2#3,#4\endargs@{%
+   \def#1{#3}%
+   \def#2{#4}%
+}
+\long\def\longpop@#1#2#3,#4\endargs@{%
+   \long\def#1{#3}%
+   \long\def#2{#4}%
+}
+
+
+%%%%%%%%%%%%%% End of code for > 10 arguments %%%%%%%%%%%%%%%%%%
+
+
+
+% Remove following spaces at the expansion stage.
+% This works because spaces are discarded before each argument when TeX is 
+% getting the arguments for a macro.
+% This must not be immediately followed by a }.
+\long\def\gobblespaces#1{#1}
+
+% This defines a Texinfo @macro or @rmacro, called by \parsemacbody.
+%    \macrobody has the body of the macro in it, with placeholders for
+% its parameters, looking like "\processmacroarg{\hash 1}".
+%    \paramno is the number of parameters
+%    \paramlist is a TeX parameter text, e.g. "#1,#2,#3,"
+% There are eight cases: recursive and nonrecursive macros of zero, one,
+% up to nine, and many arguments.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in: @include reads the file inside a group.
+%
+\def\defmacro{%
+  \let\hash=##% convert placeholders to macro parameter chars
+  \ifnum\paramno=1
+    \def\processmacroarg{\gobblespaces}%
+    % This removes the pair of braces around the argument.  We don't
+    % use \eatspaces, because this can cause ends of lines to be lost
+    % when the argument to \eatspaces is read, leading to line-based
+    % commands like "@itemize" not being read correctly.
+  \else
+    \def\processmacroarg{\xprocessmacroarg}%
+    \let\xprocessmacroarg\relax
+  \fi
+  \ifrecursive   %%%%%%%%%%%%%% Recursive %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\scanmacro{\macrobody}}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+      \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+         \expandafter\noexpand\csname\the\macname @@@@\endcsname{%
+           \noexpand\gobblespaces##1\empty}%
+           % The \empty is for \gobblespaces in case #1 is empty
+         }%
+      \expandafter\xdef\csname\the\macname @@@@\endcsname##1{%
+         \egroup\noexpand\scanmacro{\macrobody}}%
+    \else
+      \ifnum\paramno<10\relax % at most 9
+        % See non-recursive section below for comments
+        \expandafter\xdef\csname\the\macname\endcsname{%
+          \bgroup
+          \noexpand\expandafter
+          \noexpand\macroargctxt
+          \noexpand\expandafter
+          \expandafter\noexpand\csname\the\macname @@\endcsname}%
+        \expandafter\xdef\csname\the\macname @@\endcsname##1{%
+            \noexpand\passargtomacro
+            \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
+        \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+            \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
+        \expandafter\expandafter
+        \expandafter\xdef
+        \expandafter\expandafter
+          \csname\the\macname @@@@\endcsname\paramlist{%
+            \egroup\noexpand\scanmacro{\macrobody}}%
+      \else % 10 or more
+        \expandafter\xdef\csname\the\macname\endcsname{%
+          \noexpand\getargvals@{\the\macname}{\argl}%
+        }%    
+        \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
+        \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble
+      \fi
+    \fi
+  \else  %%%%%%%%%%%%%%%%%%%%%% Non-recursive %%%%%%%%%%%%%%%%%%%%%%%%%%
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\scanmacro{\macrobody}}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+      \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+         \expandafter\noexpand\csname\the\macname @@@@\endcsname{%
+           \noexpand\gobblespaces##1\empty}%
+           % The \empty is for \gobblespaces in case #1 is empty
+         }%
+      \expandafter\xdef\csname\the\macname @@@@\endcsname##1{%
+        \egroup
+        \noexpand\scanmacro{\macrobody}%
+        }%
+    \else % at most 9
+      \ifnum\paramno<10\relax
+        % @MACNAME sets the context for reading the macro argument
+        % @MACNAME@@ gets the argument, processes backslashes and appends a 
+        % comma.
+        % @MACNAME@@@ removes braces surrounding the argument list.
+        % @MACNAME@@@@ scans the macro body with arguments substituted.
+        \expandafter\xdef\csname\the\macname\endcsname{%
+          \bgroup
+          \noexpand\expandafter  % This \expandafter skip any spaces after the
+          \noexpand\macroargctxt % macro before we change the catcode of space.
+          \noexpand\expandafter
+          \expandafter\noexpand\csname\the\macname @@\endcsname}%
+        \expandafter\xdef\csname\the\macname @@\endcsname##1{%
+            \noexpand\passargtomacro
+            \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
+        \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+            \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
+        \expandafter\expandafter
+        \expandafter\xdef
+        \expandafter\expandafter
+          \csname\the\macname @@@@\endcsname\paramlist{%
+            \egroup\noexpand\scanmacro{\macrobody}}%
+      \else % 10 or more:
+        \expandafter\xdef\csname\the\macname\endcsname{%
+          \noexpand\getargvals@{\the\macname}{\argl}%
+        }%
+        \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
+        \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse
+      \fi
+    \fi
+  \fi}
+
+\catcode `\@\texiatcatcode\relax % end private-to-Texinfo catcodes
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+{\catcode`\@=0 \catcode`\\=13  % We need to manipulate \ so use @ as escape
+@catcode`@_=11  % private names
+@catcode`@!=11  % used as argument separator
+
+% \passargtomacro#1#2 -
+% Call #1 with a list of tokens #2, with any doubled backslashes in #2
+% compressed to one.
+%
+% This implementation works by expansion, and not execution (so we cannot use 
+% \def or similar).  This reduces the risk of this failing in contexts where 
+% complete expansion is done with no execution (for example, in writing out to 
+% an auxiliary file for an index entry).
+% 
+% State is kept in the input stream: the argument passed to
+% @look_ahead, @gobble_and_check_finish and @add_segment is
+%
+% THE_MACRO ARG_RESULT ! {PENDING_BS} NEXT_TOKEN  (... rest of input)
+%
+% where:
+% THE_MACRO - name of the macro we want to call
+% ARG_RESULT - argument list we build to pass to that macro
+% PENDING_BS - either a backslash or nothing
+% NEXT_TOKEN - used to look ahead in the input stream to see what's coming next
+
+@gdef@passargtomacro#1#2{%
+  @add_segment #1!{}@relax#2\@_finish\%
+}
+@gdef@_finish{@_finishx} @global@let@_finishx@relax
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 used to look ahead
+%
+% If the next token is not a backslash, process the rest of the argument; 
+% otherwise, remove the next token.
+@gdef@look_ahead#1!#2#3#4{%
+  @ifx#4\%
+   @expandafter@gobble_and_check_finish 
+  @else
+   @expandafter@add_segment
+  @fi#1!{#2}#4#4%
+}
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 should be a backslash, which is gobbled.
+% #5 looks ahead
+%
+% Double backslash found.  Add a single backslash, and look ahead.
+@gdef@gobble_and_check_finish#1!#2#3#4#5{%
+  @add_segment#1\!{}#5#5%
+}
+
+@gdef@is_fi{@fi}
+
+% #1 - THE_MACRO ARG_RESULT
+% #2 - PENDING_BS
+% #3 - NEXT_TOKEN
+% #4 is input stream until next backslash
+%
+% Input stream is either at the start of the argument, or just after a 
+% backslash sequence, either a lone backslash, or a doubled backslash.  
+% NEXT_TOKEN contains the first token in the input stream: if it is \finish, 
+% finish; otherwise, append to ARG_RESULT the segment of the argument up until
+% the next backslash.  PENDING_BACKSLASH contains a backslash to represent
+% a backslash just before the start of the input stream that has not been
+% added to ARG_RESULT.
+@gdef@add_segment#1!#2#3#4\{%
+@ifx#3@_finish
+  @call_the_macro#1!%
+@else
+  % append the pending backslash to the result, followed by the next segment
+  @expandafter@is_fi@look_ahead#1#2#4!{\}@fi
+  % this @fi is discarded by @look_ahead.
+  % we can't get rid of it with \expandafter because we don't know how 
+  % long #4 is.
+}
+
+% #1 - THE_MACRO
+% #2 - ARG_RESULT
+% #3 discards the res of the conditional in @add_segment, and @is_fi ends the 
+% conditional.
+@gdef@call_the_macro#1#2!#3@fi{@is_fi #1{#2}}
+
+}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% \braceorline MAC is used for a one-argument macro MAC.  It checks
+% whether the next non-whitespace character is a {.  It sets the context
+% for reading the argument (slightly different in the two cases).  Then,
+% to read the argument, in the whole-line case, it then calls the regular
+% \parsearg MAC; in the lbrace case, it calls \passargtomacro MAC.
+% 
+\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+  \ifx\nchar\bgroup
+    \macroargctxt
+    \expandafter\passargtomacro
+  \else
+    \macrolineargctxt\expandafter\parsearg
+  \fi \macnamexxx}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign.  Make them active and then expand them all to nothing.
+%
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+  {%
+    \expandafter\let\obeyedspace=\empty
+    \addtomacrolist{#1}%
+    \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+  }%
+  \next
+}
+
+
+\message{cross references,}
+
+\newwrite\auxfile
+\newif\ifhavexrefs    % True if xref values are known.
+\newif\ifwarnedxrefs  % True if we warned once that they aren't known.
+
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{%
+  \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+  node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references.  The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross,  ,  , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
+
+\let\nwnode=\node
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node.  #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+  \ifx\lastnode\empty\else
+    \setref{\lastnode}{#1}%
+    \global\let\lastnode=\empty
+  \fi
+}
+
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \lastsection,
+%                 or the anchor name.
+% 2) NAME-snt   - section number and type, passed as the SNT arg, or
+%                 empty for anchors.
+% 3) NAME-pg    - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat.  In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof   - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+  \pdfmkdest{#1}%
+  \iflinks
+    {%
+      \requireauxfile
+      \atdummies  % preserve commands, but don't expand them
+      \edef\writexrdef##1##2{%
+       \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+         ##1}{##2}}% these are parameters of \writexrdef
+      }%
+      \toks0 = \expandafter{\lastsection}%
+      \immediate \writexrdef{title}{\the\toks0 }%
+      \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+      \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout
+    }%
+  \fi
+}
+
+% @xrefautosectiontitle on|off says whether @section(ing) names are used
+% automatically in xrefs, if the third arg is not explicitly specified.
+% This was provided as a "secret" @set xref-automatic-section-title
+% variable, now it's official.
+% 
+\parseargdef\xrefautomaticsectiontitle{%
+  \def\temp{#1}%
+  \ifx\temp\onword
+    \expandafter\let\csname SETxref-automatic-section-title\endcsname
+      = \empty
+  \else\ifx\temp\offword
+    \expandafter\let\csname SETxref-automatic-section-title\endcsname
+      = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @xrefautomaticsectiontitle value `\temp',
+                must be on|off}%
+  \fi\fi
+}
+
+% \f
+% @xref, @pxref, and @ref generate cross-references.  For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual.  All but the node name can be omitted.
+%
+\def\pxref{\putwordsee{} \xrefXX}
+\def\xref{\putwordSee{} \xrefXX}
+\def\ref{\xrefXX}
+
+\def\xrefXX#1{\def\xrefXXarg{#1}\futurelet\tokenafterxref\xrefXXX}
+\def\xrefXXX{\expandafter\xrefX\expandafter[\xrefXXarg,,,,,,,]}
+%
+\newbox\toprefbox
+\newbox\printedrefnamebox
+\newbox\infofilenamebox
+\newbox\printedmanualbox
+%
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+  \unsepspaces
+  %
+  % Get args without leading/trailing spaces.
+  \def\printedrefname{\ignorespaces #3}%
+  \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}%
+  %
+  \def\infofilename{\ignorespaces #4}%
+  \setbox\infofilenamebox = \hbox{\infofilename\unskip}%
+  %
+  \def\printedmanual{\ignorespaces #5}%
+  \setbox\printedmanualbox  = \hbox{\printedmanual\unskip}%
+  %
+  % If the printed reference name (arg #3) was not explicitly given in
+  % the @xref, figure out what we want to use.
+  \ifdim \wd\printedrefnamebox = 0pt
+    % No printed node name was explicitly given.
+    \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax
+      % Not auto section-title: use node name inside the square brackets.
+      \def\printedrefname{\ignorespaces #1}%
+    \else
+      % Auto section-title: use chapter/section title inside
+      % the square brackets if we have it.
+      \ifdim \wd\printedmanualbox > 0pt
+        % It is in another manual, so we don't have it; use node name.
+        \def\printedrefname{\ignorespaces #1}%
+      \else
+        \ifhavexrefs
+          % We (should) know the real title if we have the xref values.
+          \def\printedrefname{\refx{#1-title}{}}%
+        \else
+          % Otherwise just copy the Info node name.
+          \def\printedrefname{\ignorespaces #1}%
+        \fi%
+      \fi
+    \fi
+  \fi
+  %
+  % Make link in pdf output.
+  \ifpdf
+    {\indexnofonts
+     \turnoffactive
+     \makevalueexpandable
+     % This expands tokens, so do it after making catcode changes, so _
+     % etc. don't get their TeX definitions.  This ignores all spaces in
+     % #4, including (wrongly) those in the middle of the filename.
+     \getfilename{#4}%
+     %
+     % This (wrongly) does not take account of leading or trailing
+     % spaces in #1, which should be ignored.
+     \edef\pdfxrefdest{#1}%
+     \ifx\pdfxrefdest\empty
+       \def\pdfxrefdest{Top}% no empty targets
+     \else
+       \txiescapepdf\pdfxrefdest  % escape PDF special chars
+     \fi
+     %
+     \leavevmode
+     \startlink attr{/Border [0 0 0]}%
+     \ifnum\filenamelength>0
+       goto file{\the\filename.pdf} name{\pdfxrefdest}%
+     \else
+       goto name{\pdfmkpgn{\pdfxrefdest}}%
+     \fi
+    }%
+    \setcolor{\linkcolor}%
+  \fi
+  {%
+    % Have to otherify everything special to allow the \csname to
+    % include an _ in the xref name, etc.
+    \indexnofonts
+    \turnoffactive
+    \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+      \csname XR#1-title\endcsname
+  }%
+  %
+  % Float references are printed completely differently: "Figure 1.2"
+  % instead of "[somenode], p.3".  \iffloat distinguishes them by
+  % \Xthisreftitle being set to a magic string.
+  \iffloat\Xthisreftitle
+    % If the user specified the print name (third arg) to the ref,
+    % print it instead of our usual "Figure 1.2".
+    \ifdim\wd\printedrefnamebox = 0pt
+      \refx{#1-snt}{}%
+    \else
+      \printedrefname
+    \fi
+    %
+    % If the user also gave the printed manual name (fifth arg), append
+    % "in MANUALNAME".
+    \ifdim \wd\printedmanualbox > 0pt
+      \space \putwordin{} \cite{\printedmanual}%
+    \fi
+  \else
+    % node/anchor (non-float) references.
+    % 
+    % If we use \unhbox to print the node names, TeX does not insert
+    % empty discretionaries after hyphens, which means that it will not
+    % find a line break at a hyphen in a node names.  Since some manuals
+    % are best written with fairly long node names, containing hyphens,
+    % this is a loss.  Therefore, we give the text of the node name
+    % again, so it is as if TeX is seeing it for the first time.
+    % 
+    \ifdim \wd\printedmanualbox > 0pt
+      % Cross-manual reference with a printed manual name.
+      % 
+      \crossmanualxref{\cite{\printedmanual\unskip}}%
+    %
+    \else\ifdim \wd\infofilenamebox > 0pt
+      % Cross-manual reference with only an info filename (arg 4), no
+      % printed manual name (arg 5).  This is essentially the same as
+      % the case above; we output the filename, since we have nothing else.
+      % 
+      \crossmanualxref{\code{\infofilename\unskip}}%
+    %
+    \else
+      % Reference within this manual.
+      %
+      % _ (for example) has to be the character _ for the purposes of the
+      % control sequence corresponding to the node, but it has to expand
+      % into the usual \leavevmode...\vrule stuff for purposes of
+      % printing. So we \turnoffactive for the \refx-snt, back on for the
+      % printing, back off for the \refx-pg.
+      {\turnoffactive
+       % Only output a following space if the -snt ref is nonempty; for
+       % @unnumbered and @anchor, it won't be.
+       \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+       \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+      }%
+      % output the `[mynode]' via the macro below so it can be overridden.
+      \xrefprintnodename\printedrefname
+      %
+      % But we always want a comma and a space:
+      ,\space
+      %
+      % output the `page 3'.
+      \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+      % Add a , if xref followed by a space
+      \if\space\noexpand\tokenafterxref ,%
+      \else\ifx\       \tokenafterxref ,% @TAB
+      \else\ifx\*\tokenafterxref ,%   @*
+      \else\ifx\ \tokenafterxref ,%   @SPACE
+      \else\ifx\
+                \tokenafterxref ,%    @NL
+      \else\ifx\tie\tokenafterxref ,% @tie
+      \fi\fi\fi\fi\fi\fi
+    \fi\fi
+  \fi
+  \endlink
+\endgroup}
+
+% Output a cross-manual xref to #1.  Used just above (twice).
+% 
+% Only include the text "Section ``foo'' in" if the foo is neither
+% missing or Top.  Thus, @xref{,,,foo,The Foo Manual} outputs simply
+% "see The Foo Manual", the idea being to refer to the whole manual.
+% 
+% But, this being TeX, we can't easily compare our node name against the
+% string "Top" while ignoring the possible spaces before and after in
+% the input.  By adding the arbitrary 7sp below, we make it much less
+% likely that a real node name would have the same width as "Top" (e.g.,
+% in a monospaced font).  Hopefully it will never happen in practice.
+% 
+% For the same basic reason, we retypeset the "Top" at every
+% reference, since the current font is indeterminate.
+% 
+\def\crossmanualxref#1{%
+  \setbox\toprefbox = \hbox{Top\kern7sp}%
+  \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}%
+  \ifdim \wd2 > 7sp  % nonempty?
+    \ifdim \wd2 = \wd\toprefbox \else  % same as Top?
+      \putwordSection{} ``\printedrefname'' \putwordin{}\space
+    \fi
+  \fi
+  #1%
+}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output.  It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents.  Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
+
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+  \ifnum\secno=0
+    \putwordChapter@tie \the\chapno
+  \else \ifnum\subsecno=0
+    \putwordSection@tie \the\chapno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
+\def\Yappendix{%
+  \ifnum\secno=0
+     \putwordAppendix@tie @char\the\appendixno{}%
+  \else \ifnum\subsecno=0
+     \putwordSection@tie @char\the\appendixno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie
+      @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+%
+\def\refx#1#2{%
+  \requireauxfile
+  {%
+    \indexnofonts
+    \otherbackslash
+    \expandafter\global\expandafter\let\expandafter\thisrefX
+      \csname XR#1\endcsname
+  }%
+  \ifx\thisrefX\relax
+    % If not defined, say something at least.
+    \angleleft un\-de\-fined\angleright
+    \iflinks
+      \ifhavexrefs
+        {\toks0 = {#1}% avoid expansion of possibly-complex value
+         \message{\linenumber Undefined cross reference `\the\toks0'.}}%
+      \else
+        \ifwarnedxrefs\else
+          \global\warnedxrefstrue
+          \message{Cross reference values unknown; you must run TeX again.}%
+        \fi
+      \fi
+    \fi
+  \else
+    % It's defined, so just use it.
+    \thisrefX
+  \fi
+  #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file.  Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions).  But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+  {% The node name might contain 8-bit characters, which in our current
+   % implementation are changed to commands like @'e.  Don't let these
+   % mess up the control sequence name.
+    \indexnofonts
+    \turnoffactive
+    \xdef\safexrefname{#1}%
+  }%
+  %
+  \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref
+  %
+  % Was that xref control sequence that we just defined for a float?
+  \expandafter\iffloat\csname XR\safexrefname\endcsname
+    % it was a float, and we have the (safe) float type in \iffloattype.
+    \expandafter\let\expandafter\floatlist
+      \csname floatlist\iffloattype\endcsname
+    %
+    % Is this the first time we've seen this float type?
+    \expandafter\ifx\floatlist\relax
+      \toks0 = {\do}% yes, so just \do
+    \else
+      % had it before, so preserve previous elements in list.
+      \toks0 = \expandafter{\floatlist\do}%
+    \fi
+    %
+    % Remember this xref in the control sequence \floatlistFLOATTYPE,
+    % for later use in \listoffloats.
+    \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0
+      {\safexrefname}}%
+  \fi
+}
+
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate at the beginning of the file.
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% Used when writing to the aux file, or when using data from it.
+\def\requireauxfile{%
+  \iflinks
+    \tryauxfile
+    % Open the new aux file.  TeX will close it automatically at exit.
+    \immediate\openout\auxfile=\jobname.aux
+  \fi
+  \global\let\requireauxfile=\relax   % Only do this once.
+}
+
+% Read the last existing aux file, if any.  No error if none exists.
+%
+\def\tryauxfile{%
+  \openin 1 \jobname.aux
+  \ifeof 1 \else
+    \readdatafile{aux}%
+    \global\havexrefstrue
+  \fi
+  \closein 1
+}
+
+\def\setupdatafile{%
+  \catcode`\^^@=\other
+  \catcode`\^^A=\other
+  \catcode`\^^B=\other
+  \catcode`\^^C=\other
+  \catcode`\^^D=\other
+  \catcode`\^^E=\other
+  \catcode`\^^F=\other
+  \catcode`\^^G=\other
+  \catcode`\^^H=\other
+  \catcode`\^^K=\other
+  \catcode`\^^L=\other
+  \catcode`\^^N=\other
+  \catcode`\^^P=\other
+  \catcode`\^^Q=\other
+  \catcode`\^^R=\other
+  \catcode`\^^S=\other
+  \catcode`\^^T=\other
+  \catcode`\^^U=\other
+  \catcode`\^^V=\other
+  \catcode`\^^W=\other
+  \catcode`\^^X=\other
+  \catcode`\^^Z=\other
+  \catcode`\^^[=\other
+  \catcode`\^^\=\other
+  \catcode`\^^]=\other
+  \catcode`\^^^=\other
+  \catcode`\^^_=\other
+  % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
+  % in xref tags, i.e., node names.  But since ^^e4 notation isn't
+  % supported in the main text, it doesn't seem desirable.  Furthermore,
+  % that is not enough: for node names that actually contain a ^
+  % character, we would end up writing a line like this: 'xrdef {'hat
+  % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+  % argument, and \hat is not an expandable control sequence.  It could
+  % all be worked out, but why?  Either we support ^^ or we don't.
+  %
+  % The other change necessary for this was to define \auxhat:
+  % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+  % and then to call \auxhat in \setq.
+  %
+  \catcode`\^=\other
+  %
+  % Special characters.  Should be turned off anyway, but...
+  \catcode`\~=\other
+  \catcode`\[=\other
+  \catcode`\]=\other
+  \catcode`\"=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\$=\other
+  \catcode`\#=\other
+  \catcode`\&=\other
+  \catcode`\%=\other
+  \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+  %
+  % This is to support \ in node names and titles, since the \
+  % characters end up in a \csname.  It's easier than
+  % leaving it active and making its active definition an actual \
+  % character.  What I don't understand is why it works in the *value*
+  % of the xrdef.  Seems like it should be a catcode12 \, and that
+  % should not typeset properly.  But it works, so I'm moving on for
+  % now.  --karl, 15jan04.
+  \catcode`\\=\other
+  %
+  % Make the characters 128-255 be printing characters.
+  {\setnonasciicharscatcodenonglobal\other}%
+  %
+  % @ is our escape character in .aux files, and we need braces.
+  \catcode`\{=1
+  \catcode`\}=2
+  \catcode`\@=0
+}
+
+\def\readdatafile#1{%
+\begingroup
+  \setupdatafile
+  \input\jobname.#1
+\endgroup}
+
+
+\message{insertions,}
+% including footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for Info output only.
+\let\footnotestyle=\comment
+
+{\catcode `\@=11
+%
+% Auto-number footnotes.  Otherwise like plain.
+\gdef\footnote{%
+  \global\advance\footnoteno by \@ne
+  \edef\thisfootno{$^{\the\footnoteno}$}%
+  %
+  % In case the footnote comes at the end of a sentence, preserve the
+  % extra spacing after we do the footnote number.
+  \let\@sf\empty
+  \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
+  %
+  % Remove inadvertent blank space before typesetting the footnote number.
+  \unskip
+  \thisfootno\@sf
+  \dofootnote
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter.  Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read.  --karl, 16nov96.
+%
+\gdef\dofootnote{%
+  \insert\footins\bgroup
+  %
+  % Nested footnotes are not supported in TeX, that would take a lot
+  % more work.  (\startsavinginserts does not suffice.)
+  \let\footnote=\errfootnotenest
+  %
+  % We want to typeset this text as a normal paragraph, even if the
+  % footnote reference occurs in (for example) a display environment.
+  % So reset some parameters.
+  \hsize=\pagewidth
+  \interlinepenalty\interfootnotelinepenalty
+  \splittopskip\ht\strutbox % top baseline for broken footnotes
+  \splitmaxdepth\dp\strutbox
+  \floatingpenalty\@MM
+  \leftskip\z@skip
+  \rightskip\z@skip
+  \spaceskip\z@skip
+  \xspaceskip\z@skip
+  \parindent\defaultparindent
+  %
+  \smallfonts \rm
+  %
+  % Because we use hanging indentation in footnotes, a @noindent appears
+  % to exdent this text, so make it be a no-op.  makeinfo does not use
+  % hanging indentation so @noindent can still be needed within footnote
+  % text after an @example or the like (not that this is good style).
+  \let\noindent = \relax
+  %
+  % Hang the footnote text off the number.  Use \everypar in case the
+  % footnote extends for more than one paragraph.
+  \everypar = {\hang}%
+  \textindent{\thisfootno}%
+  %
+  % Don't crash into the line above the footnote text.  Since this
+  % expands into a box, it must come within the paragraph, lest it
+  % provide a place where TeX can split the footnote.
+  \footstrut
+  %
+  % Invoke rest of plain TeX footnote routine.
+  \futurelet\next\fo@t
+}
+}%end \catcode `\@=11
+
+\def\errfootnotenest{%
+  \errhelp=\EMsimple
+  \errmessage{Nested footnotes not supported in texinfo.tex,
+    even though they work in makeinfo; sorry}
+}
+
+\def\errfootnoteheading{%
+  \errhelp=\EMsimple
+  \errmessage{Footnotes in chapters, sections, etc., are not supported}
+}
+
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished.  Otherwise, the insertion
+% would be lost.
+% Similarly, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes.  --kasal, 16nov03.
+%
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+  \ifx \insert\ptexinsert
+    \let\insert\saveinsert
+  \else
+    \let\checkinserts\relax
+  \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+  \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+  \afterassignment\next
+  % swallow the left brace
+  \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+  \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+    {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+  \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials  %  ;-)
+  \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+  \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+  \next
+}
+\def\newsaveinsX #1{%
+  \csname newbox\endcsname #1%
+  \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+    \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image.  We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front.  If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+  % Do not bother showing banner with epsf.tex v2.7k (available in
+  % doc/epsf.tex and on ctan).
+  \def\epsfannounce{\toks0 = }%
+  \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+  work.  It is also included in the Texinfo distribution, or you can get
+  it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+  \ifx\epsfbox\thisisundefined
+    \ifwarnednoepsf \else
+      \errhelp = \noepsfhelp
+      \errmessage{epsf.tex not found, images will be ignored}%
+      \global\warnednoepsftrue
+    \fi
+  \else
+    \imagexxx #1,,,,,\finish
+  \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+  \catcode`\^^M = 5     % in case we're inside an example
+  \normalturnoffactive  % allow _ et al. in names
+  \def\xprocessmacroarg{\eatspaces}% in case we are being used via a macro
+  % If the image is by itself, center it.
+  \ifvmode
+    \imagevmodetrue
+  \else \ifx\centersub\centerV
+    % for @center @image, we need a vbox so we can have our vertical space
+    \imagevmodetrue
+    \vbox\bgroup % vbox has better behavior than vtop herev
+  \fi\fi
+  %
+  \ifimagevmode
+    \nobreak\medskip
+    % Usually we'll have text after the image which will insert
+    % \parskip glue, so insert it here too to equalize the space
+    % above and below.
+    \nobreak\vskip\parskip
+    \nobreak
+  \fi
+  %
+  % Leave vertical mode so that indentation from an enclosing
+  %  environment such as @quotation is respected.
+  % However, if we're at the top level, we don't want the
+  %  normal paragraph indentation.
+  % On the other hand, if we are in the case of @center @image, we don't
+  %  want to start a paragraph, which will create a hsize-width box and
+  %  eradicate the centering.
+  \ifx\centersub\centerV\else \noindent \fi
+  %
+  % Output the image.
+  \ifpdf
+    % For pdfTeX and LuaTeX <= 0.80
+    \dopdfimage{#1}{#2}{#3}%
+  \else
+    \ifx\XeTeXrevision\thisisundefined
+      % For epsf.tex
+      % \epsfbox itself resets \epsf?size at each figure.
+      \setbox0 = \hbox{\ignorespaces #2}%
+        \ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+      \setbox0 = \hbox{\ignorespaces #3}%
+        \ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+      \epsfbox{#1.eps}%
+    \else
+      % For XeTeX
+      \doxeteximage{#1}{#2}{#3}%
+    \fi
+  \fi
+  %
+  \ifimagevmode
+    \medskip  % space after a standalone image
+  \fi  
+  \ifx\centersub\centerV \egroup \fi
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc.  We don't actually implement floating yet, we always include the
+% float "here".  But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc.  Can't contain commas.  If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label.  Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored.  It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+  \let\thiscaption=\empty
+  \let\thisshortcaption=\empty
+  %
+  % don't lose footnotes inside @float.
+  %
+  % BEWARE: when the floats start float, we have to issue warning whenever an
+  % insert appears inside a float which could possibly float. --kasal, 26may04
+  %
+  \startsavinginserts
+  %
+  % We can't be used inside a paragraph.
+  \par
+  %
+  \vtop\bgroup
+    \def\floattype{#1}%
+    \def\floatlabel{#2}%
+    \def\floatloc{#3}% we do nothing with this yet.
+    %
+    \ifx\floattype\empty
+      \let\safefloattype=\empty
+    \else
+      {%
+        % the floattype might have accents or other special characters,
+        % but we need to use it in a control sequence name.
+        \indexnofonts
+        \turnoffactive
+        \xdef\safefloattype{\floattype}%
+      }%
+    \fi
+    %
+    % If label is given but no type, we handle that as the empty type.
+    \ifx\floatlabel\empty \else
+      % We want each FLOATTYPE to be numbered separately (Figure 1,
+      % Table 1, Figure 2, ...).  (And if no label, no number.)
+      %
+      \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+      \global\advance\floatno by 1
+      %
+      {%
+        % This magic value for \lastsection is output by \setref as the
+        % XREFLABEL-title value.  \xrefX uses it to distinguish float
+        % labels (which have a completely different output format) from
+        % node and anchor labels.  And \xrdef uses it to construct the
+        % lists of floats.
+        %
+        \edef\lastsection{\floatmagic=\safefloattype}%
+        \setref{\floatlabel}{Yfloat}%
+      }%
+    \fi
+    %
+    % start with \parskip glue, I guess.
+    \vskip\parskip
+    %
+    % Don't suppress indentation if a float happens to start a section.
+    \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption:    Foo 1.1
+% @float Foo & @caption{Cap}:     Foo: Cap
+% @float Foo & no caption:        Foo
+% @float ,lbl & Caption{Cap}:     1.1: Cap
+% @float ,lbl & no caption:       1.1
+% @float & @caption{Cap}:         Cap
+% @float & no caption:
+%
+\def\Efloat{%
+    \let\floatident = \empty
+    %
+    % In all cases, if we have a float type, it comes first.
+    \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+    %
+    % If we have an xref label, the number comes next.
+    \ifx\floatlabel\empty \else
+      \ifx\floattype\empty \else % if also had float type, need tie first.
+        \appendtomacro\floatident{\tie}%
+      \fi
+      % the number.
+      \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+    \fi
+    %
+    % Start the printed caption with what we've constructed in
+    % \floatident, but keep it separate; we need \floatident again.
+    \let\captionline = \floatident
+    %
+    \ifx\thiscaption\empty \else
+      \ifx\floatident\empty \else
+       \appendtomacro\captionline{: }% had ident, so need a colon between
+      \fi
+      %
+      % caption text.
+      \appendtomacro\captionline{\scanexp\thiscaption}%
+    \fi
+    %
+    % If we have anything to print, print it, with space before.
+    % Eventually this needs to become an \insert.
+    \ifx\captionline\empty \else
+      \vskip.5\parskip
+      \captionline
+      %
+      % Space below caption.
+      \vskip\parskip
+    \fi
+    %
+    % If have an xref label, write the list of floats info.  Do this
+    % after the caption, to avoid chance of it being a breakpoint.
+    \ifx\floatlabel\empty \else
+      % Write the text that goes in the lof to the aux file as
+      % \floatlabel-lof.  Besides \floatident, we include the short
+      % caption if specified, else the full caption if specified, else nothing.
+      {%
+        \requireauxfile
+        \atdummies
+        %
+        % since we read the caption text in the macro world, where ^^M
+        % is turned into a normal character, we have to scan it back, so
+        % we don't write the literal three characters "^^M" into the aux file.
+       \scanexp{%
+         \xdef\noexpand\gtemp{%
+           \ifx\thisshortcaption\empty
+             \thiscaption
+           \else
+             \thisshortcaption
+           \fi
+         }%
+       }%
+        \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+         \ifx\gtemp\empty \else : \gtemp \fi}}%
+      }%
+    \fi
+  \egroup  % end of \vtop
+  %
+  % place the captured inserts
+  %
+  % BEWARE: when the floats start floating, we have to issue warning
+  % whenever an insert appears inside a float which could possibly
+  % float. --kasal, 26may04
+  %
+  \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+  \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use.  Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+  \ifx#1\relax
+      % Haven't seen this figure type before.
+      \csname newcount\endcsname #1%
+      %
+      % Remember to reset this floatno at the next chap.
+      \expandafter\gdef\expandafter\resetallfloatnos
+        \expandafter{\resetallfloatnos #1=0 }%
+  \fi
+  \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value.  We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1".  We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref.  That is, the magic
+% \lastsection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string.  If so, #2 will be the
+% (safe) float type for this float.  We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+  \def\temp{#1}%
+  \def\iffloattype{#2}%
+  \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+  \def\floattype{#1}% floattype
+  {%
+    % the floattype might have accents or other special characters,
+    % but we need to use it in a control sequence name.
+    \indexnofonts
+    \turnoffactive
+    \xdef\safefloattype{\floattype}%
+  }%
+  %
+  % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+  \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+    \ifhavexrefs
+      % if the user said @listoffloats foo but never @float foo.
+      \message{\linenumber No `\safefloattype' floats to list.}%
+    \fi
+  \else
+    \begingroup
+      \leftskip=\tocindent  % indent these entries like a toc
+      \let\do=\listoffloatsdo
+      \csname floatlist\safefloattype\endcsname
+    \endgroup
+  \fi
+}
+
+% This is called on each entry in a list of floats.  We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file.  We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+  % Can't fully expand XR#1-lof because it can contain anything.  Just
+  % pass the control sequence.  On the other hand, XR#1-pg is just the
+  % page number, and we want to fully expand that so we can get a link
+  % in pdf output.
+  \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+  %
+  % use the same \entry macro we use to generate the TOC and index.
+  \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+  \writeentry
+}}
+
+
+\message{localization,}
+
+% For single-language documents, @documentlanguage is usually given very
+% early, just after @documentencoding.  Single argument is the language
+% (de) or locale (de_DE) abbreviation.
+%
+{
+  \catcode`\_ = \active
+  \globaldefs=1
+\parseargdef\documentlanguage{%
+  \tex % read txi-??.tex file in plain TeX.
+    % Read the file by the name they passed if it exists.
+    \let_ = \normalunderscore  % normal _ character for filename test
+    \openin 1 txi-#1.tex
+    \ifeof 1
+      \documentlanguagetrywithoutunderscore #1_\finish
+    \else
+      \globaldefs = 1  % everything in the txi-LL files needs to persist
+      \input txi-#1.tex
+    \fi
+    \closein 1
+  \endgroup % end raw TeX
+}
+%
+% If they passed de_DE, and txi-de_DE.tex doesn't exist,
+% try txi-de.tex.
+%
+\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{%
+  \openin 1 txi-#1.tex
+  \ifeof 1
+    \errhelp = \nolanghelp
+    \errmessage{Cannot read language file txi-#1.tex}%
+  \else
+    \globaldefs = 1  % everything in the txi-LL files needs to persist
+    \input txi-#1.tex
+  \fi
+  \closein 1
+}
+}% end of special _ catcode
+%
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty.  Maybe you need to install it?  Putting it in the current
+directory should work if nowhere else does.}
+
+% This macro is called from txi-??.tex files; the first argument is the
+% \language name to set (without the "\lang@" prefix), the second and
+% third args are \{left,right}hyphenmin.
+%
+% The language names to pass are determined when the format is built.
+% See the etex.log file created at that time, e.g.,
+% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log.
+%
+% With TeX Live 2008, etex now includes hyphenation patterns for all
+% available languages.  This means we can support hyphenation in
+% Texinfo, at least to some extent.  (This still doesn't solve the
+% accented characters problem.)
+%
+\catcode`@=11
+\def\txisetlanguage#1#2#3{%
+  % do not set the language if the name is undefined in the current TeX.
+  \expandafter\ifx\csname lang@#1\endcsname \relax
+    \message{no patterns for #1}%
+  \else
+    \global\language = \csname lang@#1\endcsname
+  \fi
+  % but there is no harm in adjusting the hyphenmin values regardless.
+  \global\lefthyphenmin = #2\relax
+  \global\righthyphenmin = #3\relax
+}
+
+% Get input by bytes instead of by UTF-8 codepoints for XeTeX and LuaTeX, 
+% otherwise the encoding support is completely broken.
+\ifx\XeTeXrevision\thisisundefined
+\else
+\XeTeXdefaultencoding "bytes"  % For subsequent files to be read
+\XeTeXinputencoding "bytes"  % Effective in texinfo.tex only
+% Unfortunately, there seems to be no corresponding XeTeX command for
+% output encoding.  This is a problem for auxiliary index and TOC files.
+% The only solution would be perhaps to write out @U{...} sequences in
+% place of UTF-8 characters.
+\fi
+
+\ifx\luatexversion\thisisundefined
+\else
+\directlua{
+local utf8_char, byte, gsub = unicode.utf8.char, string.byte, string.gsub
+local function convert_char (char)
+  return utf8_char(byte(char))
+end
+
+local function convert_line (line)
+  return gsub(line, ".", convert_char)
+end
+
+callback.register("process_input_buffer", convert_line)
+
+local function convert_line_out (line)
+  local line_out = ""
+  for c in string.utfvalues(line) do
+     line_out = line_out .. string.char(c)
+  end
+  return line_out
+end
+
+callback.register("process_output_buffer", convert_line_out)
+}
+\fi
+
+
+% Helpers for encodings.
+% Set the catcode of characters 128 through 255 to the specified number.
+%
+\def\setnonasciicharscatcode#1{%
+   \count255=128
+   \loop\ifnum\count255<256
+      \global\catcode\count255=#1\relax
+      \advance\count255 by 1
+   \repeat
+}
+
+\def\setnonasciicharscatcodenonglobal#1{%
+   \count255=128
+   \loop\ifnum\count255<256
+      \catcode\count255=#1\relax
+      \advance\count255 by 1
+   \repeat
+}
+
+% @documentencoding sets the definition of non-ASCII characters
+% according to the specified encoding.
+%
+\def\documentencoding{\parseargusing\filenamecatcodes\documentencodingzzz}
+\def\documentencodingzzz#1{%
+  % Get input by bytes instead of by UTF-8 codepoints for XeTeX,
+  % otherwise the encoding support is completely broken.
+  % This settings is for the document root file.
+  \ifx\XeTeXrevision\thisisundefined
+  \else
+    \XeTeXinputencoding "bytes"
+  \fi
+  %
+  % Encoding being declared for the document.
+  \def\declaredencoding{\csname #1.enc\endcsname}%
+  %
+  % Supported encodings: names converted to tokens in order to be able
+  % to compare them with \ifx.
+  \def\ascii{\csname US-ASCII.enc\endcsname}%
+  \def\latnine{\csname ISO-8859-15.enc\endcsname}%
+  \def\latone{\csname ISO-8859-1.enc\endcsname}%
+  \def\lattwo{\csname ISO-8859-2.enc\endcsname}%
+  \def\utfeight{\csname UTF-8.enc\endcsname}%
+  %
+  \ifx \declaredencoding \ascii
+     \asciichardefs
+  %
+  \else \ifx \declaredencoding \lattwo
+     \setnonasciicharscatcode\active
+     \lattwochardefs
+  %
+  \else \ifx \declaredencoding \latone
+     \setnonasciicharscatcode\active
+     \latonechardefs
+  %
+  \else \ifx \declaredencoding \latnine
+     \setnonasciicharscatcode\active
+     \latninechardefs
+  %
+  \else \ifx \declaredencoding \utfeight
+     \setnonasciicharscatcode\active
+     % since we already invoked \utfeightchardefs at the top level
+     % (below), do not re-invoke it, then our check for duplicated
+     % definitions triggers.  Making non-ascii chars active is enough.
+  %
+  \else
+    \message{Ignoring unknown document encoding: #1.}%
+  %
+  \fi % utfeight
+  \fi % latnine
+  \fi % latone
+  \fi % lattwo
+  \fi % ascii
+}
+
+% emacs-page
+% A message to be logged when using a character that isn't available
+% the default font encoding (OT1).
+%
+\def\missingcharmsg#1{\message{Character missing, sorry: #1.}}
+
+% Take account of \c (plain) vs. \, (Texinfo) difference.
+\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi}
+
+% First, make active non-ASCII characters in order for them to be
+% correctly categorized when TeX reads the replacement text of
+% macros containing the character definitions.
+\setnonasciicharscatcode\active
+%
+% Latin1 (ISO-8859-1) character definitions.
+\def\latonechardefs{%
+  \gdef^^a0{\tie}
+  \gdef^^a1{\exclamdown}
+  \gdef^^a2{{\tcfont \char162}} % cent
+  \gdef^^a3{\pounds}
+  \gdef^^a4{{\tcfont \char164}} % currency
+  \gdef^^a5{{\tcfont \char165}} % yen
+  \gdef^^a6{{\tcfont \char166}} % broken bar
+  \gdef^^a7{\S}
+  \gdef^^a8{\"{}}
+  \gdef^^a9{\copyright}
+  \gdef^^aa{\ordf}
+  \gdef^^ab{\guillemetleft}
+  \gdef^^ac{\ensuremath\lnot}
+  \gdef^^ad{\-}
+  \gdef^^ae{\registeredsymbol}
+  \gdef^^af{\={}}
+  %
+  \gdef^^b0{\textdegree}
+  \gdef^^b1{$\pm$}
+  \gdef^^b2{$^2$}
+  \gdef^^b3{$^3$}
+  \gdef^^b4{\'{}}
+  \gdef^^b5{$\mu$}
+  \gdef^^b6{\P}
+  \gdef^^b7{\ensuremath\cdot}
+  \gdef^^b8{\cedilla\ }
+  \gdef^^b9{$^1$}
+  \gdef^^ba{\ordm}
+  \gdef^^bb{\guillemetright}
+  \gdef^^bc{$1\over4$}
+  \gdef^^bd{$1\over2$}
+  \gdef^^be{$3\over4$}
+  \gdef^^bf{\questiondown}
+  %
+  \gdef^^c0{\`A}
+  \gdef^^c1{\'A}
+  \gdef^^c2{\^A}
+  \gdef^^c3{\~A}
+  \gdef^^c4{\"A}
+  \gdef^^c5{\ringaccent A}
+  \gdef^^c6{\AE}
+  \gdef^^c7{\cedilla C}
+  \gdef^^c8{\`E}
+  \gdef^^c9{\'E}
+  \gdef^^ca{\^E}
+  \gdef^^cb{\"E}
+  \gdef^^cc{\`I}
+  \gdef^^cd{\'I}
+  \gdef^^ce{\^I}
+  \gdef^^cf{\"I}
+  %
+  \gdef^^d0{\DH}
+  \gdef^^d1{\~N}
+  \gdef^^d2{\`O}
+  \gdef^^d3{\'O}
+  \gdef^^d4{\^O}
+  \gdef^^d5{\~O}
+  \gdef^^d6{\"O}
+  \gdef^^d7{$\times$}
+  \gdef^^d8{\O}
+  \gdef^^d9{\`U}
+  \gdef^^da{\'U}
+  \gdef^^db{\^U}
+  \gdef^^dc{\"U}
+  \gdef^^dd{\'Y}
+  \gdef^^de{\TH}
+  \gdef^^df{\ss}
+  %
+  \gdef^^e0{\`a}
+  \gdef^^e1{\'a}
+  \gdef^^e2{\^a}
+  \gdef^^e3{\~a}
+  \gdef^^e4{\"a}
+  \gdef^^e5{\ringaccent a}
+  \gdef^^e6{\ae}
+  \gdef^^e7{\cedilla c}
+  \gdef^^e8{\`e}
+  \gdef^^e9{\'e}
+  \gdef^^ea{\^e}
+  \gdef^^eb{\"e}
+  \gdef^^ec{\`{\dotless i}}
+  \gdef^^ed{\'{\dotless i}}
+  \gdef^^ee{\^{\dotless i}}
+  \gdef^^ef{\"{\dotless i}}
+  %
+  \gdef^^f0{\dh}
+  \gdef^^f1{\~n}
+  \gdef^^f2{\`o}
+  \gdef^^f3{\'o}
+  \gdef^^f4{\^o}
+  \gdef^^f5{\~o}
+  \gdef^^f6{\"o}
+  \gdef^^f7{$\div$}
+  \gdef^^f8{\o}
+  \gdef^^f9{\`u}
+  \gdef^^fa{\'u}
+  \gdef^^fb{\^u}
+  \gdef^^fc{\"u}
+  \gdef^^fd{\'y}
+  \gdef^^fe{\th}
+  \gdef^^ff{\"y}
+}
+
+% Latin9 (ISO-8859-15) encoding character definitions.
+\def\latninechardefs{%
+  % Encoding is almost identical to Latin1.
+  \latonechardefs
+  %
+  \gdef^^a4{\euro}
+  \gdef^^a6{\v S}
+  \gdef^^a8{\v s}
+  \gdef^^b4{\v Z}
+  \gdef^^b8{\v z}
+  \gdef^^bc{\OE}
+  \gdef^^bd{\oe}
+  \gdef^^be{\"Y}
+}
+
+% Latin2 (ISO-8859-2) character definitions.
+\def\lattwochardefs{%
+  \gdef^^a0{\tie}
+  \gdef^^a1{\ogonek{A}}
+  \gdef^^a2{\u{}}
+  \gdef^^a3{\L}
+  \gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
+  \gdef^^a5{\v L}
+  \gdef^^a6{\'S}
+  \gdef^^a7{\S}
+  \gdef^^a8{\"{}}
+  \gdef^^a9{\v S}
+  \gdef^^aa{\cedilla S}
+  \gdef^^ab{\v T}
+  \gdef^^ac{\'Z}
+  \gdef^^ad{\-}
+  \gdef^^ae{\v Z}
+  \gdef^^af{\dotaccent Z}
+  %
+  \gdef^^b0{\textdegree}
+  \gdef^^b1{\ogonek{a}}
+  \gdef^^b2{\ogonek{ }}
+  \gdef^^b3{\l}
+  \gdef^^b4{\'{}}
+  \gdef^^b5{\v l}
+  \gdef^^b6{\'s}
+  \gdef^^b7{\v{}}
+  \gdef^^b8{\cedilla\ }
+  \gdef^^b9{\v s}
+  \gdef^^ba{\cedilla s}
+  \gdef^^bb{\v t}
+  \gdef^^bc{\'z}
+  \gdef^^bd{\H{}}
+  \gdef^^be{\v z}
+  \gdef^^bf{\dotaccent z}
+  %
+  \gdef^^c0{\'R}
+  \gdef^^c1{\'A}
+  \gdef^^c2{\^A}
+  \gdef^^c3{\u A}
+  \gdef^^c4{\"A}
+  \gdef^^c5{\'L}
+  \gdef^^c6{\'C}
+  \gdef^^c7{\cedilla C}
+  \gdef^^c8{\v C}
+  \gdef^^c9{\'E}
+  \gdef^^ca{\ogonek{E}}
+  \gdef^^cb{\"E}
+  \gdef^^cc{\v E}
+  \gdef^^cd{\'I}
+  \gdef^^ce{\^I}
+  \gdef^^cf{\v D}
+  %
+  \gdef^^d0{\DH}
+  \gdef^^d1{\'N}
+  \gdef^^d2{\v N}
+  \gdef^^d3{\'O}
+  \gdef^^d4{\^O}
+  \gdef^^d5{\H O}
+  \gdef^^d6{\"O}
+  \gdef^^d7{$\times$}
+  \gdef^^d8{\v R}
+  \gdef^^d9{\ringaccent U}
+  \gdef^^da{\'U}
+  \gdef^^db{\H U}
+  \gdef^^dc{\"U}
+  \gdef^^dd{\'Y}
+  \gdef^^de{\cedilla T}
+  \gdef^^df{\ss}
+  %
+  \gdef^^e0{\'r}
+  \gdef^^e1{\'a}
+  \gdef^^e2{\^a}
+  \gdef^^e3{\u a}
+  \gdef^^e4{\"a}
+  \gdef^^e5{\'l}
+  \gdef^^e6{\'c}
+  \gdef^^e7{\cedilla c}
+  \gdef^^e8{\v c}
+  \gdef^^e9{\'e}
+  \gdef^^ea{\ogonek{e}}
+  \gdef^^eb{\"e}
+  \gdef^^ec{\v e}
+  \gdef^^ed{\'{\dotless{i}}}
+  \gdef^^ee{\^{\dotless{i}}}
+  \gdef^^ef{\v d}
+  %
+  \gdef^^f0{\dh}
+  \gdef^^f1{\'n}
+  \gdef^^f2{\v n}
+  \gdef^^f3{\'o}
+  \gdef^^f4{\^o}
+  \gdef^^f5{\H o}
+  \gdef^^f6{\"o}
+  \gdef^^f7{$\div$}
+  \gdef^^f8{\v r}
+  \gdef^^f9{\ringaccent u}
+  \gdef^^fa{\'u}
+  \gdef^^fb{\H u}
+  \gdef^^fc{\"u}
+  \gdef^^fd{\'y}
+  \gdef^^fe{\cedilla t}
+  \gdef^^ff{\dotaccent{}}
+}
+
+% UTF-8 character definitions.
+%
+% This code to support UTF-8 is based on LaTeX's utf8.def, with some
+% changes for Texinfo conventions.  It is included here under the GPL by
+% permission from Frank Mittelbach and the LaTeX team.
+%
+\newcount\countUTFx
+\newcount\countUTFy
+\newcount\countUTFz
+
+\gdef\UTFviiiTwoOctets#1#2{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\endcsname}
+%
+\gdef\UTFviiiThreeOctets#1#2#3{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname}
+%
+\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname}
+
+\gdef\UTFviiiDefined#1{%
+  \ifx #1\relax
+    \message{\linenumber Unicode char \string #1 not defined for Texinfo}%
+  \else
+    \expandafter #1%
+  \fi
+}
+
+\begingroup
+  \catcode`\~13
+  \catcode`\"12
+
+  \def\UTFviiiLoop{%
+    \global\catcode\countUTFx\active
+    \uccode`\~\countUTFx
+    \uppercase\expandafter{\UTFviiiTmp}%
+    \advance\countUTFx by 1
+    \ifnum\countUTFx < \countUTFy
+      \expandafter\UTFviiiLoop
+    \fi}
+
+  \countUTFx = "C2
+  \countUTFy = "E0
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiTwoOctets\string~}}
+  \UTFviiiLoop
+
+  \countUTFx = "E0
+  \countUTFy = "F0
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiThreeOctets\string~}}
+  \UTFviiiLoop
+
+  \countUTFx = "F0
+  \countUTFy = "F4
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiFourOctets\string~}}
+  \UTFviiiLoop
+\endgroup
+
+\def\globallet{\global\let} % save some \expandafter's below
+
+% @U{xxxx} to produce U+xxxx, if we support it.
+\def\U#1{%
+  \expandafter\ifx\csname uni:#1\endcsname \relax
+    \errhelp = \EMsimple       
+    \errmessage{Unicode character U+#1 not supported, sorry}%
+  \else
+    \csname uni:#1\endcsname
+  \fi
+}
+
+\begingroup
+  \catcode`\"=12
+  \catcode`\<=12
+  \catcode`\.=12
+  \catcode`\,=12
+  \catcode`\;=12
+  \catcode`\!=12
+  \catcode`\~=13
+  \gdef\DeclareUnicodeCharacter#1#2{%
+    \countUTFz = "#1\relax
+    %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}%
+    \begingroup
+      \parseXMLCharref
+      \def\UTFviiiTwoOctets##1##2{%
+        \csname u8:##1\string ##2\endcsname}%
+      \def\UTFviiiThreeOctets##1##2##3{%
+        \csname u8:##1\string ##2\string ##3\endcsname}%
+      \def\UTFviiiFourOctets##1##2##3##4{%
+        \csname u8:##1\string ##2\string ##3\string ##4\endcsname}%
+      \expandafter\expandafter\expandafter\expandafter
+       \expandafter\expandafter\expandafter
+       \gdef\UTFviiiTmp{#2}%
+      % 
+      \expandafter\ifx\csname uni:#1\endcsname \relax \else
+       \message{Internal error, already defined: #1}%
+      \fi
+      %
+      % define an additional control sequence for this code point.
+      \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp
+    \endgroup}
+
+  \gdef\parseXMLCharref{%
+    \ifnum\countUTFz < "A0\relax
+      \errhelp = \EMsimple
+      \errmessage{Cannot define Unicode char value < 00A0}%
+    \else\ifnum\countUTFz < "800\relax
+      \parseUTFviiiA,%
+      \parseUTFviiiB C\UTFviiiTwoOctets.,%
+    \else\ifnum\countUTFz < "10000\relax
+      \parseUTFviiiA;%
+      \parseUTFviiiA,%
+      \parseUTFviiiB E\UTFviiiThreeOctets.{,;}%
+    \else
+      \parseUTFviiiA;%
+      \parseUTFviiiA,%
+      \parseUTFviiiA!%
+      \parseUTFviiiB F\UTFviiiFourOctets.{!,;}%
+    \fi\fi\fi
+  }
+
+  \gdef\parseUTFviiiA#1{%
+    \countUTFx = \countUTFz
+    \divide\countUTFz by 64
+    \countUTFy = \countUTFz
+    \multiply\countUTFz by 64
+    \advance\countUTFx by -\countUTFz
+    \advance\countUTFx by 128
+    \uccode `#1\countUTFx
+    \countUTFz = \countUTFy}
+
+  \gdef\parseUTFviiiB#1#2#3#4{%
+    \advance\countUTFz by "#10\relax
+    \uccode `#3\countUTFz
+    \uppercase{\gdef\UTFviiiTmp{#2#3#4}}}
+\endgroup
+
+% https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_M
+% U+0000..U+007F = https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)
+% U+0080..U+00FF = https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)
+% U+0100..U+017F = https://en.wikipedia.org/wiki/Latin_Extended-A
+% U+0180..U+024F = https://en.wikipedia.org/wiki/Latin_Extended-B
+% 
+% Many of our renditions are less than wonderful, and all the missing
+% characters are available somewhere.  Loading the necessary fonts
+% awaits user request.  We can't truly support Unicode without
+% reimplementing everything that's been done in LaTeX for many years,
+% plus probably using luatex or xetex, and who knows what else.
+% We won't be doing that here in this simple file.  But we can try to at
+% least make most of the characters not bomb out.
+%
+\def\utfeightchardefs{%
+  \DeclareUnicodeCharacter{00A0}{\tie}
+  \DeclareUnicodeCharacter{00A1}{\exclamdown}
+  \DeclareUnicodeCharacter{00A2}{{\tcfont \char162}}% 0242=cent
+  \DeclareUnicodeCharacter{00A3}{\pounds}
+  \DeclareUnicodeCharacter{00A4}{{\tcfont \char164}}% 0244=currency
+  \DeclareUnicodeCharacter{00A5}{{\tcfont \char165}}% 0245=yen
+  \DeclareUnicodeCharacter{00A6}{{\tcfont \char166}}% 0246=brokenbar
+  \DeclareUnicodeCharacter{00A7}{\S}
+  \DeclareUnicodeCharacter{00A8}{\"{ }}
+  \DeclareUnicodeCharacter{00A9}{\copyright}
+  \DeclareUnicodeCharacter{00AA}{\ordf}
+  \DeclareUnicodeCharacter{00AB}{\guillemetleft}
+  \DeclareUnicodeCharacter{00AC}{\ensuremath\lnot}
+  \DeclareUnicodeCharacter{00AD}{\-}
+  \DeclareUnicodeCharacter{00AE}{\registeredsymbol}
+  \DeclareUnicodeCharacter{00AF}{\={ }}
+  %
+  \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}
+  \DeclareUnicodeCharacter{00B1}{\ensuremath\pm}
+  \DeclareUnicodeCharacter{00B2}{$^2$}
+  \DeclareUnicodeCharacter{00B3}{$^3$}
+  \DeclareUnicodeCharacter{00B4}{\'{ }}
+  \DeclareUnicodeCharacter{00B5}{$\mu$}
+  \DeclareUnicodeCharacter{00B6}{\P}
+  \DeclareUnicodeCharacter{00B7}{\ensuremath\cdot}
+  \DeclareUnicodeCharacter{00B8}{\cedilla{ }}
+  \DeclareUnicodeCharacter{00B9}{$^1$}
+  \DeclareUnicodeCharacter{00BA}{\ordm}
+  \DeclareUnicodeCharacter{00BB}{\guillemetright}
+  \DeclareUnicodeCharacter{00BC}{$1\over4$}
+  \DeclareUnicodeCharacter{00BD}{$1\over2$}
+  \DeclareUnicodeCharacter{00BE}{$3\over4$}
+  \DeclareUnicodeCharacter{00BF}{\questiondown}
+  %
+  \DeclareUnicodeCharacter{00C0}{\`A}
+  \DeclareUnicodeCharacter{00C1}{\'A}
+  \DeclareUnicodeCharacter{00C2}{\^A}
+  \DeclareUnicodeCharacter{00C3}{\~A}
+  \DeclareUnicodeCharacter{00C4}{\"A}
+  \DeclareUnicodeCharacter{00C5}{\AA}
+  \DeclareUnicodeCharacter{00C6}{\AE}
+  \DeclareUnicodeCharacter{00C7}{\cedilla{C}}
+  \DeclareUnicodeCharacter{00C8}{\`E}
+  \DeclareUnicodeCharacter{00C9}{\'E}
+  \DeclareUnicodeCharacter{00CA}{\^E}
+  \DeclareUnicodeCharacter{00CB}{\"E}
+  \DeclareUnicodeCharacter{00CC}{\`I}
+  \DeclareUnicodeCharacter{00CD}{\'I}
+  \DeclareUnicodeCharacter{00CE}{\^I}
+  \DeclareUnicodeCharacter{00CF}{\"I}
+  %
+  \DeclareUnicodeCharacter{00D0}{\DH}
+  \DeclareUnicodeCharacter{00D1}{\~N}
+  \DeclareUnicodeCharacter{00D2}{\`O}
+  \DeclareUnicodeCharacter{00D3}{\'O}
+  \DeclareUnicodeCharacter{00D4}{\^O}
+  \DeclareUnicodeCharacter{00D5}{\~O}
+  \DeclareUnicodeCharacter{00D6}{\"O}
+  \DeclareUnicodeCharacter{00D7}{\ensuremath\times}
+  \DeclareUnicodeCharacter{00D8}{\O}
+  \DeclareUnicodeCharacter{00D9}{\`U}
+  \DeclareUnicodeCharacter{00DA}{\'U}
+  \DeclareUnicodeCharacter{00DB}{\^U}
+  \DeclareUnicodeCharacter{00DC}{\"U}
+  \DeclareUnicodeCharacter{00DD}{\'Y}
+  \DeclareUnicodeCharacter{00DE}{\TH}
+  \DeclareUnicodeCharacter{00DF}{\ss}
+  %
+  \DeclareUnicodeCharacter{00E0}{\`a}
+  \DeclareUnicodeCharacter{00E1}{\'a}
+  \DeclareUnicodeCharacter{00E2}{\^a}
+  \DeclareUnicodeCharacter{00E3}{\~a}
+  \DeclareUnicodeCharacter{00E4}{\"a}
+  \DeclareUnicodeCharacter{00E5}{\aa}
+  \DeclareUnicodeCharacter{00E6}{\ae}
+  \DeclareUnicodeCharacter{00E7}{\cedilla{c}}
+  \DeclareUnicodeCharacter{00E8}{\`e}
+  \DeclareUnicodeCharacter{00E9}{\'e}
+  \DeclareUnicodeCharacter{00EA}{\^e}
+  \DeclareUnicodeCharacter{00EB}{\"e}
+  \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}
+  \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}
+  \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}
+  \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}
+  %
+  \DeclareUnicodeCharacter{00F0}{\dh}
+  \DeclareUnicodeCharacter{00F1}{\~n}
+  \DeclareUnicodeCharacter{00F2}{\`o}
+  \DeclareUnicodeCharacter{00F3}{\'o}
+  \DeclareUnicodeCharacter{00F4}{\^o}
+  \DeclareUnicodeCharacter{00F5}{\~o}
+  \DeclareUnicodeCharacter{00F6}{\"o}
+  \DeclareUnicodeCharacter{00F7}{\ensuremath\div}
+  \DeclareUnicodeCharacter{00F8}{\o}
+  \DeclareUnicodeCharacter{00F9}{\`u}
+  \DeclareUnicodeCharacter{00FA}{\'u}
+  \DeclareUnicodeCharacter{00FB}{\^u}
+  \DeclareUnicodeCharacter{00FC}{\"u}
+  \DeclareUnicodeCharacter{00FD}{\'y}
+  \DeclareUnicodeCharacter{00FE}{\th}
+  \DeclareUnicodeCharacter{00FF}{\"y}
+  %
+  \DeclareUnicodeCharacter{0100}{\=A}
+  \DeclareUnicodeCharacter{0101}{\=a}
+  \DeclareUnicodeCharacter{0102}{\u{A}}
+  \DeclareUnicodeCharacter{0103}{\u{a}}
+  \DeclareUnicodeCharacter{0104}{\ogonek{A}}
+  \DeclareUnicodeCharacter{0105}{\ogonek{a}}
+  \DeclareUnicodeCharacter{0106}{\'C}
+  \DeclareUnicodeCharacter{0107}{\'c}
+  \DeclareUnicodeCharacter{0108}{\^C}
+  \DeclareUnicodeCharacter{0109}{\^c}
+  \DeclareUnicodeCharacter{010A}{\dotaccent{C}}
+  \DeclareUnicodeCharacter{010B}{\dotaccent{c}}
+  \DeclareUnicodeCharacter{010C}{\v{C}}
+  \DeclareUnicodeCharacter{010D}{\v{c}}
+  \DeclareUnicodeCharacter{010E}{\v{D}}
+  \DeclareUnicodeCharacter{010F}{d'}
+  %
+  \DeclareUnicodeCharacter{0110}{\DH}
+  \DeclareUnicodeCharacter{0111}{\dh}
+  \DeclareUnicodeCharacter{0112}{\=E}
+  \DeclareUnicodeCharacter{0113}{\=e}
+  \DeclareUnicodeCharacter{0114}{\u{E}}
+  \DeclareUnicodeCharacter{0115}{\u{e}}
+  \DeclareUnicodeCharacter{0116}{\dotaccent{E}}
+  \DeclareUnicodeCharacter{0117}{\dotaccent{e}}
+  \DeclareUnicodeCharacter{0118}{\ogonek{E}}
+  \DeclareUnicodeCharacter{0119}{\ogonek{e}}
+  \DeclareUnicodeCharacter{011A}{\v{E}}
+  \DeclareUnicodeCharacter{011B}{\v{e}}
+  \DeclareUnicodeCharacter{011C}{\^G}
+  \DeclareUnicodeCharacter{011D}{\^g}
+  \DeclareUnicodeCharacter{011E}{\u{G}}
+  \DeclareUnicodeCharacter{011F}{\u{g}}
+  %
+  \DeclareUnicodeCharacter{0120}{\dotaccent{G}}
+  \DeclareUnicodeCharacter{0121}{\dotaccent{g}}
+  \DeclareUnicodeCharacter{0122}{\cedilla{G}}
+  \DeclareUnicodeCharacter{0123}{\cedilla{g}}
+  \DeclareUnicodeCharacter{0124}{\^H}
+  \DeclareUnicodeCharacter{0125}{\^h}
+  \DeclareUnicodeCharacter{0126}{\missingcharmsg{H WITH STROKE}}
+  \DeclareUnicodeCharacter{0127}{\missingcharmsg{h WITH STROKE}}
+  \DeclareUnicodeCharacter{0128}{\~I}
+  \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}
+  \DeclareUnicodeCharacter{012A}{\=I}
+  \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}
+  \DeclareUnicodeCharacter{012C}{\u{I}}
+  \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}
+  \DeclareUnicodeCharacter{012E}{\ogonek{I}}
+  \DeclareUnicodeCharacter{012F}{\ogonek{i}}
+  %
+  \DeclareUnicodeCharacter{0130}{\dotaccent{I}}
+  \DeclareUnicodeCharacter{0131}{\dotless{i}}
+  \DeclareUnicodeCharacter{0132}{IJ}
+  \DeclareUnicodeCharacter{0133}{ij}
+  \DeclareUnicodeCharacter{0134}{\^J}
+  \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}
+  \DeclareUnicodeCharacter{0136}{\cedilla{K}}
+  \DeclareUnicodeCharacter{0137}{\cedilla{k}}
+  \DeclareUnicodeCharacter{0138}{\ensuremath\kappa}  
+  \DeclareUnicodeCharacter{0139}{\'L}
+  \DeclareUnicodeCharacter{013A}{\'l}
+  \DeclareUnicodeCharacter{013B}{\cedilla{L}}
+  \DeclareUnicodeCharacter{013C}{\cedilla{l}}
+  \DeclareUnicodeCharacter{013D}{L'}% should kern
+  \DeclareUnicodeCharacter{013E}{l'}% should kern
+  \DeclareUnicodeCharacter{013F}{L\U{00B7}}
+  %
+  \DeclareUnicodeCharacter{0140}{l\U{00B7}}
+  \DeclareUnicodeCharacter{0141}{\L}
+  \DeclareUnicodeCharacter{0142}{\l}
+  \DeclareUnicodeCharacter{0143}{\'N}
+  \DeclareUnicodeCharacter{0144}{\'n}
+  \DeclareUnicodeCharacter{0145}{\cedilla{N}}
+  \DeclareUnicodeCharacter{0146}{\cedilla{n}}
+  \DeclareUnicodeCharacter{0147}{\v{N}}
+  \DeclareUnicodeCharacter{0148}{\v{n}}
+  \DeclareUnicodeCharacter{0149}{'n}
+  \DeclareUnicodeCharacter{014A}{\missingcharmsg{ENG}}
+  \DeclareUnicodeCharacter{014B}{\missingcharmsg{eng}}
+  \DeclareUnicodeCharacter{014C}{\=O}
+  \DeclareUnicodeCharacter{014D}{\=o}
+  \DeclareUnicodeCharacter{014E}{\u{O}}
+  \DeclareUnicodeCharacter{014F}{\u{o}}
+  %
+  \DeclareUnicodeCharacter{0150}{\H{O}}
+  \DeclareUnicodeCharacter{0151}{\H{o}}
+  \DeclareUnicodeCharacter{0152}{\OE}
+  \DeclareUnicodeCharacter{0153}{\oe}
+  \DeclareUnicodeCharacter{0154}{\'R}
+  \DeclareUnicodeCharacter{0155}{\'r}
+  \DeclareUnicodeCharacter{0156}{\cedilla{R}}
+  \DeclareUnicodeCharacter{0157}{\cedilla{r}}
+  \DeclareUnicodeCharacter{0158}{\v{R}}
+  \DeclareUnicodeCharacter{0159}{\v{r}}
+  \DeclareUnicodeCharacter{015A}{\'S}
+  \DeclareUnicodeCharacter{015B}{\'s}
+  \DeclareUnicodeCharacter{015C}{\^S}
+  \DeclareUnicodeCharacter{015D}{\^s}
+  \DeclareUnicodeCharacter{015E}{\cedilla{S}}
+  \DeclareUnicodeCharacter{015F}{\cedilla{s}}
+  %
+  \DeclareUnicodeCharacter{0160}{\v{S}}
+  \DeclareUnicodeCharacter{0161}{\v{s}}
+  \DeclareUnicodeCharacter{0162}{\cedilla{T}}
+  \DeclareUnicodeCharacter{0163}{\cedilla{t}}
+  \DeclareUnicodeCharacter{0164}{\v{T}}
+  \DeclareUnicodeCharacter{0165}{\v{t}}
+  \DeclareUnicodeCharacter{0166}{\missingcharmsg{H WITH STROKE}}
+  \DeclareUnicodeCharacter{0167}{\missingcharmsg{h WITH STROKE}}
+  \DeclareUnicodeCharacter{0168}{\~U}
+  \DeclareUnicodeCharacter{0169}{\~u}
+  \DeclareUnicodeCharacter{016A}{\=U}
+  \DeclareUnicodeCharacter{016B}{\=u}
+  \DeclareUnicodeCharacter{016C}{\u{U}}
+  \DeclareUnicodeCharacter{016D}{\u{u}}
+  \DeclareUnicodeCharacter{016E}{\ringaccent{U}}
+  \DeclareUnicodeCharacter{016F}{\ringaccent{u}}
+  %
+  \DeclareUnicodeCharacter{0170}{\H{U}}
+  \DeclareUnicodeCharacter{0171}{\H{u}}
+  \DeclareUnicodeCharacter{0172}{\ogonek{U}}
+  \DeclareUnicodeCharacter{0173}{\ogonek{u}}
+  \DeclareUnicodeCharacter{0174}{\^W}
+  \DeclareUnicodeCharacter{0175}{\^w}
+  \DeclareUnicodeCharacter{0176}{\^Y}
+  \DeclareUnicodeCharacter{0177}{\^y}
+  \DeclareUnicodeCharacter{0178}{\"Y}
+  \DeclareUnicodeCharacter{0179}{\'Z}
+  \DeclareUnicodeCharacter{017A}{\'z}
+  \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}
+  \DeclareUnicodeCharacter{017C}{\dotaccent{z}}
+  \DeclareUnicodeCharacter{017D}{\v{Z}}
+  \DeclareUnicodeCharacter{017E}{\v{z}}
+  \DeclareUnicodeCharacter{017F}{\missingcharmsg{LONG S}}
+  %
+  \DeclareUnicodeCharacter{01C4}{D\v{Z}}
+  \DeclareUnicodeCharacter{01C5}{D\v{z}}
+  \DeclareUnicodeCharacter{01C6}{d\v{z}}
+  \DeclareUnicodeCharacter{01C7}{LJ}
+  \DeclareUnicodeCharacter{01C8}{Lj}
+  \DeclareUnicodeCharacter{01C9}{lj}
+  \DeclareUnicodeCharacter{01CA}{NJ}
+  \DeclareUnicodeCharacter{01CB}{Nj}
+  \DeclareUnicodeCharacter{01CC}{nj}
+  \DeclareUnicodeCharacter{01CD}{\v{A}}
+  \DeclareUnicodeCharacter{01CE}{\v{a}}
+  \DeclareUnicodeCharacter{01CF}{\v{I}}
+  %
+  \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}
+  \DeclareUnicodeCharacter{01D1}{\v{O}}
+  \DeclareUnicodeCharacter{01D2}{\v{o}}
+  \DeclareUnicodeCharacter{01D3}{\v{U}}
+  \DeclareUnicodeCharacter{01D4}{\v{u}}
+  %
+  \DeclareUnicodeCharacter{01E2}{\={\AE}}
+  \DeclareUnicodeCharacter{01E3}{\={\ae}}
+  \DeclareUnicodeCharacter{01E6}{\v{G}}
+  \DeclareUnicodeCharacter{01E7}{\v{g}}
+  \DeclareUnicodeCharacter{01E8}{\v{K}}
+  \DeclareUnicodeCharacter{01E9}{\v{k}}
+  %
+  \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}
+  \DeclareUnicodeCharacter{01F1}{DZ}
+  \DeclareUnicodeCharacter{01F2}{Dz}
+  \DeclareUnicodeCharacter{01F3}{dz}
+  \DeclareUnicodeCharacter{01F4}{\'G}
+  \DeclareUnicodeCharacter{01F5}{\'g}
+  \DeclareUnicodeCharacter{01F8}{\`N}
+  \DeclareUnicodeCharacter{01F9}{\`n}
+  \DeclareUnicodeCharacter{01FC}{\'{\AE}}
+  \DeclareUnicodeCharacter{01FD}{\'{\ae}}
+  \DeclareUnicodeCharacter{01FE}{\'{\O}}
+  \DeclareUnicodeCharacter{01FF}{\'{\o}}
+  %
+  \DeclareUnicodeCharacter{021E}{\v{H}}
+  \DeclareUnicodeCharacter{021F}{\v{h}}
+  %
+  \DeclareUnicodeCharacter{0226}{\dotaccent{A}}
+  \DeclareUnicodeCharacter{0227}{\dotaccent{a}}
+  \DeclareUnicodeCharacter{0228}{\cedilla{E}}
+  \DeclareUnicodeCharacter{0229}{\cedilla{e}}
+  \DeclareUnicodeCharacter{022E}{\dotaccent{O}}
+  \DeclareUnicodeCharacter{022F}{\dotaccent{o}}
+  %
+  \DeclareUnicodeCharacter{0232}{\=Y}
+  \DeclareUnicodeCharacter{0233}{\=y}
+  \DeclareUnicodeCharacter{0237}{\dotless{j}}
+  %
+  \DeclareUnicodeCharacter{02DB}{\ogonek{ }}
+  %
+  % Greek letters upper case
+  \DeclareUnicodeCharacter{0391}{{\it A}}
+  \DeclareUnicodeCharacter{0392}{{\it B}}
+  \DeclareUnicodeCharacter{0393}{\ensuremath{\mit\Gamma}}
+  \DeclareUnicodeCharacter{0394}{\ensuremath{\mit\Delta}}
+  \DeclareUnicodeCharacter{0395}{{\it E}}
+  \DeclareUnicodeCharacter{0396}{{\it Z}}
+  \DeclareUnicodeCharacter{0397}{{\it H}}
+  \DeclareUnicodeCharacter{0398}{\ensuremath{\mit\Theta}}
+  \DeclareUnicodeCharacter{0399}{{\it I}}
+  \DeclareUnicodeCharacter{039A}{{\it K}}
+  \DeclareUnicodeCharacter{039B}{\ensuremath{\mit\Lambda}}
+  \DeclareUnicodeCharacter{039C}{{\it M}}
+  \DeclareUnicodeCharacter{039D}{{\it N}}
+  \DeclareUnicodeCharacter{039E}{\ensuremath{\mit\Xi}}
+  \DeclareUnicodeCharacter{039F}{{\it O}}
+  \DeclareUnicodeCharacter{03A0}{\ensuremath{\mit\Pi}}
+  \DeclareUnicodeCharacter{03A1}{{\it P}}
+  %\DeclareUnicodeCharacter{03A2}{} % none - corresponds to final sigma
+  \DeclareUnicodeCharacter{03A3}{\ensuremath{\mit\Sigma}}
+  \DeclareUnicodeCharacter{03A4}{{\it T}}
+  \DeclareUnicodeCharacter{03A5}{\ensuremath{\mit\Upsilon}}
+  \DeclareUnicodeCharacter{03A6}{\ensuremath{\mit\Phi}}
+  \DeclareUnicodeCharacter{03A7}{{\it X}}
+  \DeclareUnicodeCharacter{03A8}{\ensuremath{\mit\Psi}}
+  \DeclareUnicodeCharacter{03A9}{\ensuremath{\mit\Omega}}
+  %
+  % Vowels with accents
+  \DeclareUnicodeCharacter{0390}{\ensuremath{\ddot{\acute\iota}}}
+  \DeclareUnicodeCharacter{03AC}{\ensuremath{\acute\alpha}}
+  \DeclareUnicodeCharacter{03AD}{\ensuremath{\acute\epsilon}}
+  \DeclareUnicodeCharacter{03AE}{\ensuremath{\acute\eta}}
+  \DeclareUnicodeCharacter{03AF}{\ensuremath{\acute\iota}}
+  \DeclareUnicodeCharacter{03B0}{\ensuremath{\acute{\ddot\upsilon}}}
+  %
+  % Standalone accent
+  \DeclareUnicodeCharacter{0384}{\ensuremath{\acute{\ }}}
+  %
+  % Greek letters lower case
+  \DeclareUnicodeCharacter{03B1}{\ensuremath\alpha}
+  \DeclareUnicodeCharacter{03B2}{\ensuremath\beta}
+  \DeclareUnicodeCharacter{03B3}{\ensuremath\gamma}
+  \DeclareUnicodeCharacter{03B4}{\ensuremath\delta}
+  \DeclareUnicodeCharacter{03B5}{\ensuremath\epsilon}
+  \DeclareUnicodeCharacter{03B6}{\ensuremath\zeta}
+  \DeclareUnicodeCharacter{03B7}{\ensuremath\eta}
+  \DeclareUnicodeCharacter{03B8}{\ensuremath\theta}
+  \DeclareUnicodeCharacter{03B9}{\ensuremath\iota}
+  \DeclareUnicodeCharacter{03BA}{\ensuremath\kappa}
+  \DeclareUnicodeCharacter{03BB}{\ensuremath\lambda}
+  \DeclareUnicodeCharacter{03BC}{\ensuremath\mu}
+  \DeclareUnicodeCharacter{03BD}{\ensuremath\nu}
+  \DeclareUnicodeCharacter{03BE}{\ensuremath\xi}
+  \DeclareUnicodeCharacter{03BF}{{\it o}} % omicron
+  \DeclareUnicodeCharacter{03C0}{\ensuremath\pi}
+  \DeclareUnicodeCharacter{03C1}{\ensuremath\rho}
+  \DeclareUnicodeCharacter{03C2}{\ensuremath\varsigma}
+  \DeclareUnicodeCharacter{03C3}{\ensuremath\sigma}
+  \DeclareUnicodeCharacter{03C4}{\ensuremath\tau}
+  \DeclareUnicodeCharacter{03C5}{\ensuremath\upsilon}
+  \DeclareUnicodeCharacter{03C6}{\ensuremath\phi}
+  \DeclareUnicodeCharacter{03C7}{\ensuremath\chi}
+  \DeclareUnicodeCharacter{03C8}{\ensuremath\psi}
+  \DeclareUnicodeCharacter{03C9}{\ensuremath\omega}
+  %
+  % More Greek vowels with accents
+  \DeclareUnicodeCharacter{03CA}{\ensuremath{\ddot\iota}}
+  \DeclareUnicodeCharacter{03CB}{\ensuremath{\ddot\upsilon}}
+  \DeclareUnicodeCharacter{03CC}{\ensuremath{\acute o}}
+  \DeclareUnicodeCharacter{03CD}{\ensuremath{\acute\upsilon}}
+  \DeclareUnicodeCharacter{03CE}{\ensuremath{\acute\omega}}
+  %
+  % Variant Greek letters
+  \DeclareUnicodeCharacter{03D1}{\ensuremath\vartheta}
+  \DeclareUnicodeCharacter{03D6}{\ensuremath\varpi}
+  \DeclareUnicodeCharacter{03F1}{\ensuremath\varrho}
+  %
+  \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}
+  \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}
+  \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}
+  \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}
+  \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}
+  \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}
+  \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}
+  \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}
+  \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}
+  \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}
+  \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}
+  \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}
+  %
+  \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}
+  \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}
+  %
+  \DeclareUnicodeCharacter{1E20}{\=G}
+  \DeclareUnicodeCharacter{1E21}{\=g}
+  \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}
+  \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}
+  \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}
+  \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}
+  \DeclareUnicodeCharacter{1E26}{\"H}
+  \DeclareUnicodeCharacter{1E27}{\"h}
+  %
+  \DeclareUnicodeCharacter{1E30}{\'K}
+  \DeclareUnicodeCharacter{1E31}{\'k}
+  \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}
+  \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}
+  \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}
+  \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}
+  \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}
+  \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}
+  \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}
+  \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}
+  \DeclareUnicodeCharacter{1E3E}{\'M}
+  \DeclareUnicodeCharacter{1E3F}{\'m}
+  %
+  \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}
+  \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}
+  \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}
+  \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}
+  \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}
+  \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}
+  \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}
+  \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}
+  \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}
+  \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}
+  %
+  \DeclareUnicodeCharacter{1E54}{\'P}
+  \DeclareUnicodeCharacter{1E55}{\'p}
+  \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}
+  \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}
+  \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}
+  \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}
+  \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}
+  \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}
+  \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}
+  \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}
+  %
+  \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}
+  \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}
+  \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}
+  \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}
+  \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}
+  \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}
+  \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}
+  \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}
+  \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}
+  \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}
+  %
+  \DeclareUnicodeCharacter{1E7C}{\~V}
+  \DeclareUnicodeCharacter{1E7D}{\~v}
+  \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}
+  \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}
+  %
+  \DeclareUnicodeCharacter{1E80}{\`W}
+  \DeclareUnicodeCharacter{1E81}{\`w}
+  \DeclareUnicodeCharacter{1E82}{\'W}
+  \DeclareUnicodeCharacter{1E83}{\'w}
+  \DeclareUnicodeCharacter{1E84}{\"W}
+  \DeclareUnicodeCharacter{1E85}{\"w}
+  \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}
+  \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}
+  \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}
+  \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}
+  \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}
+  \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}
+  \DeclareUnicodeCharacter{1E8C}{\"X}
+  \DeclareUnicodeCharacter{1E8D}{\"x}
+  \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}
+  \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}
+  %
+  \DeclareUnicodeCharacter{1E90}{\^Z}
+  \DeclareUnicodeCharacter{1E91}{\^z}
+  \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}
+  \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}
+  \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}
+  \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}
+  \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}
+  \DeclareUnicodeCharacter{1E97}{\"t}
+  \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}
+  \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}
+  %
+  \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}
+  \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}
+  %
+  \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}
+  \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}
+  \DeclareUnicodeCharacter{1EBC}{\~E}
+  \DeclareUnicodeCharacter{1EBD}{\~e}
+  %
+  \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}
+  \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}
+  \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}
+  \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}
+  %
+  \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}
+  \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}
+  %
+  \DeclareUnicodeCharacter{1EF2}{\`Y}
+  \DeclareUnicodeCharacter{1EF3}{\`y}
+  \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}
+  %
+  \DeclareUnicodeCharacter{1EF8}{\~Y}
+  \DeclareUnicodeCharacter{1EF9}{\~y}
+  %
+  % Punctuation
+  \DeclareUnicodeCharacter{2013}{--}
+  \DeclareUnicodeCharacter{2014}{---}
+  \DeclareUnicodeCharacter{2018}{\quoteleft}
+  \DeclareUnicodeCharacter{2019}{\quoteright}
+  \DeclareUnicodeCharacter{201A}{\quotesinglbase}
+  \DeclareUnicodeCharacter{201C}{\quotedblleft}
+  \DeclareUnicodeCharacter{201D}{\quotedblright}
+  \DeclareUnicodeCharacter{201E}{\quotedblbase}
+  \DeclareUnicodeCharacter{2020}{\ensuremath\dagger}
+  \DeclareUnicodeCharacter{2021}{\ensuremath\ddagger}
+  \DeclareUnicodeCharacter{2022}{\bullet}
+  \DeclareUnicodeCharacter{202F}{\thinspace}
+  \DeclareUnicodeCharacter{2026}{\dots}
+  \DeclareUnicodeCharacter{2039}{\guilsinglleft}
+  \DeclareUnicodeCharacter{203A}{\guilsinglright}
+  %
+  \DeclareUnicodeCharacter{20AC}{\euro}
+  %
+  \DeclareUnicodeCharacter{2192}{\expansion}
+  \DeclareUnicodeCharacter{21D2}{\result}
+  %
+  % Mathematical symbols
+  \DeclareUnicodeCharacter{2200}{\ensuremath\forall}
+  \DeclareUnicodeCharacter{2203}{\ensuremath\exists}
+  \DeclareUnicodeCharacter{2208}{\ensuremath\in}
+  \DeclareUnicodeCharacter{2212}{\minus}
+  \DeclareUnicodeCharacter{2217}{\ast}
+  \DeclareUnicodeCharacter{221E}{\ensuremath\infty}
+  \DeclareUnicodeCharacter{2225}{\ensuremath\parallel}
+  \DeclareUnicodeCharacter{2227}{\ensuremath\wedge}
+  \DeclareUnicodeCharacter{2229}{\ensuremath\cap}
+  \DeclareUnicodeCharacter{2261}{\equiv}
+  \DeclareUnicodeCharacter{2264}{\ensuremath\leq}
+  \DeclareUnicodeCharacter{2265}{\ensuremath\geq}
+  \DeclareUnicodeCharacter{2282}{\ensuremath\subset}
+  \DeclareUnicodeCharacter{2287}{\ensuremath\supseteq}
+  %
+  \DeclareUnicodeCharacter{2016}{\ensuremath\Vert}
+  \DeclareUnicodeCharacter{2032}{\ensuremath\prime}
+  \DeclareUnicodeCharacter{210F}{\ensuremath\hbar}
+  \DeclareUnicodeCharacter{2111}{\ensuremath\Im}
+  \DeclareUnicodeCharacter{2113}{\ensuremath\ell}
+  \DeclareUnicodeCharacter{2118}{\ensuremath\wp}
+  \DeclareUnicodeCharacter{211C}{\ensuremath\Re}
+  \DeclareUnicodeCharacter{2127}{\ensuremath\mho}
+  \DeclareUnicodeCharacter{2135}{\ensuremath\aleph}
+  \DeclareUnicodeCharacter{2190}{\ensuremath\leftarrow}
+  \DeclareUnicodeCharacter{2191}{\ensuremath\uparrow}
+  \DeclareUnicodeCharacter{2193}{\ensuremath\downarrow}
+  \DeclareUnicodeCharacter{2194}{\ensuremath\leftrightarrow}
+  \DeclareUnicodeCharacter{2195}{\ensuremath\updownarrow}
+  \DeclareUnicodeCharacter{2196}{\ensuremath\nwarrow}
+  \DeclareUnicodeCharacter{2197}{\ensuremath\nearrow}
+  \DeclareUnicodeCharacter{2198}{\ensuremath\searrow}
+  \DeclareUnicodeCharacter{2199}{\ensuremath\swarrow}
+  \DeclareUnicodeCharacter{21A6}{\ensuremath\mapsto}
+  \DeclareUnicodeCharacter{21A9}{\ensuremath\hookleftarrow}
+  \DeclareUnicodeCharacter{21AA}{\ensuremath\hookrightarrow}
+  \DeclareUnicodeCharacter{21BC}{\ensuremath\leftharpoonup}
+  \DeclareUnicodeCharacter{21BD}{\ensuremath\leftharpoondown}
+  \DeclareUnicodeCharacter{21BE}{\ensuremath\upharpoonright}
+  \DeclareUnicodeCharacter{21C0}{\ensuremath\rightharpoonup}
+  \DeclareUnicodeCharacter{21C1}{\ensuremath\rightharpoondown}
+  \DeclareUnicodeCharacter{21CC}{\ensuremath\rightleftharpoons}
+  \DeclareUnicodeCharacter{21D0}{\ensuremath\Leftarrow}
+  \DeclareUnicodeCharacter{21D1}{\ensuremath\Uparrow}
+  \DeclareUnicodeCharacter{21D3}{\ensuremath\Downarrow}
+  \DeclareUnicodeCharacter{21D4}{\ensuremath\Leftrightarrow}
+  \DeclareUnicodeCharacter{21D5}{\ensuremath\Updownarrow}
+  \DeclareUnicodeCharacter{21DD}{\ensuremath\leadsto}
+  \DeclareUnicodeCharacter{2201}{\ensuremath\complement}
+  \DeclareUnicodeCharacter{2202}{\ensuremath\partial}
+  \DeclareUnicodeCharacter{2205}{\ensuremath\emptyset}
+  \DeclareUnicodeCharacter{2207}{\ensuremath\nabla}
+  \DeclareUnicodeCharacter{2209}{\ensuremath\notin}
+  \DeclareUnicodeCharacter{220B}{\ensuremath\owns}
+  \DeclareUnicodeCharacter{220F}{\ensuremath\prod}
+  \DeclareUnicodeCharacter{2210}{\ensuremath\coprod}
+  \DeclareUnicodeCharacter{2211}{\ensuremath\sum}
+  \DeclareUnicodeCharacter{2213}{\ensuremath\mp}
+  \DeclareUnicodeCharacter{2218}{\ensuremath\circ}
+  \DeclareUnicodeCharacter{221A}{\ensuremath\surd}
+  \DeclareUnicodeCharacter{221D}{\ensuremath\propto}
+  \DeclareUnicodeCharacter{2220}{\ensuremath\angle}
+  \DeclareUnicodeCharacter{2223}{\ensuremath\mid}
+  \DeclareUnicodeCharacter{2228}{\ensuremath\vee}
+  \DeclareUnicodeCharacter{222A}{\ensuremath\cup}
+  \DeclareUnicodeCharacter{222B}{\ensuremath\smallint}
+  \DeclareUnicodeCharacter{222E}{\ensuremath\oint}
+  \DeclareUnicodeCharacter{223C}{\ensuremath\sim}
+  \DeclareUnicodeCharacter{2240}{\ensuremath\wr}
+  \DeclareUnicodeCharacter{2243}{\ensuremath\simeq}
+  \DeclareUnicodeCharacter{2245}{\ensuremath\cong}
+  \DeclareUnicodeCharacter{2248}{\ensuremath\approx}
+  \DeclareUnicodeCharacter{224D}{\ensuremath\asymp}
+  \DeclareUnicodeCharacter{2250}{\ensuremath\doteq}
+  \DeclareUnicodeCharacter{2260}{\ensuremath\neq}
+  \DeclareUnicodeCharacter{226A}{\ensuremath\ll}
+  \DeclareUnicodeCharacter{226B}{\ensuremath\gg}
+  \DeclareUnicodeCharacter{227A}{\ensuremath\prec}
+  \DeclareUnicodeCharacter{227B}{\ensuremath\succ}
+  \DeclareUnicodeCharacter{2283}{\ensuremath\supset}
+  \DeclareUnicodeCharacter{2286}{\ensuremath\subseteq}
+  \DeclareUnicodeCharacter{228E}{\ensuremath\uplus}
+  \DeclareUnicodeCharacter{228F}{\ensuremath\sqsubset}
+  \DeclareUnicodeCharacter{2290}{\ensuremath\sqsupset}
+  \DeclareUnicodeCharacter{2291}{\ensuremath\sqsubseteq}
+  \DeclareUnicodeCharacter{2292}{\ensuremath\sqsupseteq}
+  \DeclareUnicodeCharacter{2293}{\ensuremath\sqcap}
+  \DeclareUnicodeCharacter{2294}{\ensuremath\sqcup}
+  \DeclareUnicodeCharacter{2295}{\ensuremath\oplus}
+  \DeclareUnicodeCharacter{2296}{\ensuremath\ominus}
+  \DeclareUnicodeCharacter{2297}{\ensuremath\otimes}
+  \DeclareUnicodeCharacter{2298}{\ensuremath\oslash}
+  \DeclareUnicodeCharacter{2299}{\ensuremath\odot}
+  \DeclareUnicodeCharacter{22A2}{\ensuremath\vdash}
+  \DeclareUnicodeCharacter{22A3}{\ensuremath\dashv}
+  \DeclareUnicodeCharacter{22A4}{\ensuremath\ptextop}
+  \DeclareUnicodeCharacter{22A5}{\ensuremath\bot}
+  \DeclareUnicodeCharacter{22A8}{\ensuremath\models}
+  \DeclareUnicodeCharacter{22B4}{\ensuremath\unlhd}
+  \DeclareUnicodeCharacter{22B5}{\ensuremath\unrhd}
+  \DeclareUnicodeCharacter{22C0}{\ensuremath\bigwedge}
+  \DeclareUnicodeCharacter{22C1}{\ensuremath\bigvee}
+  \DeclareUnicodeCharacter{22C2}{\ensuremath\bigcap}
+  \DeclareUnicodeCharacter{22C3}{\ensuremath\bigcup}
+  \DeclareUnicodeCharacter{22C4}{\ensuremath\diamond}
+  \DeclareUnicodeCharacter{22C5}{\ensuremath\cdot}
+  \DeclareUnicodeCharacter{22C6}{\ensuremath\star}
+  \DeclareUnicodeCharacter{22C8}{\ensuremath\bowtie}
+  \DeclareUnicodeCharacter{2308}{\ensuremath\lceil}
+  \DeclareUnicodeCharacter{2309}{\ensuremath\rceil}
+  \DeclareUnicodeCharacter{230A}{\ensuremath\lfloor}
+  \DeclareUnicodeCharacter{230B}{\ensuremath\rfloor}
+  \DeclareUnicodeCharacter{2322}{\ensuremath\frown}
+  \DeclareUnicodeCharacter{2323}{\ensuremath\smile}
+  %
+  \DeclareUnicodeCharacter{25A1}{\ensuremath\Box}
+  \DeclareUnicodeCharacter{25B3}{\ensuremath\triangle}
+  \DeclareUnicodeCharacter{25B7}{\ensuremath\triangleright}
+  \DeclareUnicodeCharacter{25BD}{\ensuremath\bigtriangledown}
+  \DeclareUnicodeCharacter{25C1}{\ensuremath\triangleleft}
+  \DeclareUnicodeCharacter{25C7}{\ensuremath\Diamond}
+  \DeclareUnicodeCharacter{2660}{\ensuremath\spadesuit}
+  \DeclareUnicodeCharacter{2661}{\ensuremath\heartsuit}
+  \DeclareUnicodeCharacter{2662}{\ensuremath\diamondsuit}
+  \DeclareUnicodeCharacter{2663}{\ensuremath\clubsuit}
+  \DeclareUnicodeCharacter{266D}{\ensuremath\flat}
+  \DeclareUnicodeCharacter{266E}{\ensuremath\natural}
+  \DeclareUnicodeCharacter{266F}{\ensuremath\sharp}
+  \DeclareUnicodeCharacter{26AA}{\ensuremath\bigcirc}
+  \DeclareUnicodeCharacter{27B9}{\ensuremath\rangle}
+  \DeclareUnicodeCharacter{27C2}{\ensuremath\perp}
+  \DeclareUnicodeCharacter{27E8}{\ensuremath\langle}
+  \DeclareUnicodeCharacter{27F5}{\ensuremath\longleftarrow}
+  \DeclareUnicodeCharacter{27F6}{\ensuremath\longrightarrow}
+  \DeclareUnicodeCharacter{27F7}{\ensuremath\longleftrightarrow}
+  \DeclareUnicodeCharacter{27FC}{\ensuremath\longmapsto}
+  \DeclareUnicodeCharacter{29F5}{\ensuremath\setminus}
+  \DeclareUnicodeCharacter{2A00}{\ensuremath\bigodot}
+  \DeclareUnicodeCharacter{2A01}{\ensuremath\bigoplus}
+  \DeclareUnicodeCharacter{2A02}{\ensuremath\bigotimes}
+  \DeclareUnicodeCharacter{2A04}{\ensuremath\biguplus}
+  \DeclareUnicodeCharacter{2A06}{\ensuremath\bigsqcup}
+  \DeclareUnicodeCharacter{2A1D}{\ensuremath\Join}
+  \DeclareUnicodeCharacter{2A3F}{\ensuremath\amalg}
+  \DeclareUnicodeCharacter{2AAF}{\ensuremath\preceq}
+  \DeclareUnicodeCharacter{2AB0}{\ensuremath\succeq}
+  %
+  \global\mathchardef\checkmark="1370 % actually the square root sign
+  \DeclareUnicodeCharacter{2713}{\ensuremath\checkmark}
+}% end of \utfeightchardefs
+
+% US-ASCII character definitions.
+\def\asciichardefs{% nothing need be done
+   \relax
+}
+
+% Latin1 (ISO-8859-1) character definitions.
+\def\nonasciistringdefs{%
+  \setnonasciicharscatcode\active
+  \def\defstringchar##1{\def##1{\string##1}}%
+  %
+  \defstringchar^^80\defstringchar^^81\defstringchar^^82\defstringchar^^83%
+  \defstringchar^^84\defstringchar^^85\defstringchar^^86\defstringchar^^87%
+  \defstringchar^^88\defstringchar^^89\defstringchar^^8a\defstringchar^^8b%
+  \defstringchar^^8c\defstringchar^^8d\defstringchar^^8e\defstringchar^^8f%
+  %
+  \defstringchar^^90\defstringchar^^91\defstringchar^^92\defstringchar^^93%
+  \defstringchar^^94\defstringchar^^95\defstringchar^^96\defstringchar^^97%
+  \defstringchar^^98\defstringchar^^99\defstringchar^^9a\defstringchar^^9b%
+  \defstringchar^^9c\defstringchar^^9d\defstringchar^^9e\defstringchar^^9f%
+  %
+  \defstringchar^^a0\defstringchar^^a1\defstringchar^^a2\defstringchar^^a3%
+  \defstringchar^^a4\defstringchar^^a5\defstringchar^^a6\defstringchar^^a7%
+  \defstringchar^^a8\defstringchar^^a9\defstringchar^^aa\defstringchar^^ab%
+  \defstringchar^^ac\defstringchar^^ad\defstringchar^^ae\defstringchar^^af%
+  %
+  \defstringchar^^b0\defstringchar^^b1\defstringchar^^b2\defstringchar^^b3%
+  \defstringchar^^b4\defstringchar^^b5\defstringchar^^b6\defstringchar^^b7%
+  \defstringchar^^b8\defstringchar^^b9\defstringchar^^ba\defstringchar^^bb%
+  \defstringchar^^bc\defstringchar^^bd\defstringchar^^be\defstringchar^^bf%
+  %
+  \defstringchar^^c0\defstringchar^^c1\defstringchar^^c2\defstringchar^^c3%
+  \defstringchar^^c4\defstringchar^^c5\defstringchar^^c6\defstringchar^^c7%
+  \defstringchar^^c8\defstringchar^^c9\defstringchar^^ca\defstringchar^^cb%
+  \defstringchar^^cc\defstringchar^^cd\defstringchar^^ce\defstringchar^^cf%
+  %
+  \defstringchar^^d0\defstringchar^^d1\defstringchar^^d2\defstringchar^^d3%
+  \defstringchar^^d4\defstringchar^^d5\defstringchar^^d6\defstringchar^^d7%
+  \defstringchar^^d8\defstringchar^^d9\defstringchar^^da\defstringchar^^db%
+  \defstringchar^^dc\defstringchar^^dd\defstringchar^^de\defstringchar^^df%
+  %
+  \defstringchar^^e0\defstringchar^^e1\defstringchar^^e2\defstringchar^^e3%
+  \defstringchar^^e4\defstringchar^^e5\defstringchar^^e6\defstringchar^^e7%
+  \defstringchar^^e8\defstringchar^^e9\defstringchar^^ea\defstringchar^^eb%
+  \defstringchar^^ec\defstringchar^^ed\defstringchar^^ee\defstringchar^^ef%
+  %
+  \defstringchar^^f0\defstringchar^^f1\defstringchar^^f2\defstringchar^^f3%
+  \defstringchar^^f4\defstringchar^^f5\defstringchar^^f6\defstringchar^^f7%
+  \defstringchar^^f8\defstringchar^^f9\defstringchar^^fa\defstringchar^^fb%
+  \defstringchar^^fc\defstringchar^^fd\defstringchar^^fe\defstringchar^^ff%
+}
+
+
+% define all the unicode characters we know about, for the sake of @U.
+\utfeightchardefs
+
+
+% Make non-ASCII characters printable again for compatibility with
+% existing Texinfo documents that may use them, even without declaring a
+% document encoding.
+%
+\setnonasciicharscatcode \other
+
+
+\message{formatting,}
+
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness = 10000
+
+% Don't be very finicky about underfull hboxes, either.
+\hbadness = 6666
+
+% Following George Bush, get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything.  We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize.  We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+  \ifx\emergencystretch\thisisundefined
+    % Allow us to assign to \emergencystretch anyway.
+    \def\emergencystretch{\dimen0}%
+  \else
+    \emergencystretch = .15\hsize
+  \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading.  The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+  \voffset = #3\relax
+  \topskip = #6\relax
+  \splittopskip = \topskip
+  %
+  \vsize = #1\relax
+  \advance\vsize by \topskip
+  \outervsize = \vsize
+  \advance\outervsize by 2\topandbottommargin
+  \pageheight = \vsize
+  %
+  \hsize = #2\relax
+  \outerhsize = \hsize
+  \advance\outerhsize by 0.5in
+  \pagewidth = \hsize
+  %
+  \normaloffset = #4\relax
+  \bindingoffset = #5\relax
+  %
+  \ifpdf
+    \pdfpageheight #7\relax
+    \pdfpagewidth #8\relax
+    % if we don't reset these, they will remain at "1 true in" of
+    % whatever layout pdftex was dumped with.
+    \pdfhorigin = 1 true in
+    \pdfvorigin = 1 true in
+  \fi
+  %
+  \setleading{\textleading}
+  %
+  \parindent = \defaultparindent
+  \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % If page is nothing but text, make it come out even.
+  \internalpagesizes{607.2pt}{6in}% that's 46 lines
+                    {\voffset}{.25in}%
+                    {\bindingoffset}{36pt}%
+                    {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+  \parskip = 2pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.5in}{5in}%
+                    {-.2in}{0in}%
+                    {\bindingoffset}{16pt}%
+                    {9.25in}{7in}%
+  %
+  \lispnarrowing = 0.3in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+  \parskip = 1.5pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.4in}{4.8in}%
+                    {-.2in}{-.4in}%
+                    {0pt}{14pt}%
+                    {9in}{6in}%
+  %
+  \lispnarrowing = 0.25in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % Double-side printing via postscript on Laserjet 4050
+  % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+  % To change the settings for a different printer or situation, adjust
+  % \normaloffset until the front-side and back-side texts align.  Then
+  % do the same for \bindingoffset.  You can set these for testing in
+  % your texinfo source file like this:
+  % @tex
+  % \global\normaloffset = -6mm
+  % \global\bindingoffset = 10mm
+  % @end tex
+  \internalpagesizes{673.2pt}{160mm}% that's 51 lines
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{44pt}%
+                    {297mm}{210mm}%
+  %
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+  \parskip = 2pt plus 1pt minus 0.1pt
+  \textleading = 12.5pt
+  %
+  \internalpagesizes{160mm}{120mm}%
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{8pt}%
+                    {210mm}{148mm}%
+  %
+  \lispnarrowing = 0.2in
+  \tolerance = 800
+  \hfuzz = 1.2pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 2mm
+  \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{237mm}{150mm}%
+                    {\voffset}{4.6mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  %
+  % Must explicitly reset to 0 because we call \afourpaper.
+  \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{241mm}{165mm}%
+                    {\voffset}{-2.95mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+  \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+  \globaldefs = 1
+  %
+  \parskip = 3pt plus 2pt minus 1pt
+  \setleading{\textleading}%
+  %
+  \dimen0 = #1\relax
+  \advance\dimen0 by \voffset
+  %
+  \dimen2 = \hsize
+  \advance\dimen2 by \normaloffset
+  %
+  \internalpagesizes{#1}{\hsize}%
+                    {\voffset}{\normaloffset}%
+                    {\bindingoffset}{44pt}%
+                    {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
+
+\def^^L{\par} % remove \outer, so ^L can appear in an @comment
+
+% DEL is a comment character, in case @c does not suffice.
+\catcode`\^^? = 14
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other \def\normaldoublequote{"}
+\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix
+\catcode`\+=\other \def\normalplus{+}
+\catcode`\<=\other \def\normalless{<}
+\catcode`\>=\other \def\normalgreater{>}
+\catcode`\^=\other \def\normalcaret{^}
+\catcode`\_=\other \def\normalunderscore{_}
+\catcode`\|=\other \def\normalverticalbar{|}
+\catcode`\~=\other \def\normaltilde{~}
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise.  Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font.  Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts.  But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
+
+% Set catcodes for Texinfo file
+
+% Active characters for printing the wanted glyph.
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+%
+\catcode`\"=\active
+\def\activedoublequote{{\tt\char34}}
+\let"=\activedoublequote
+\catcode`\~=\active \def\activetilde{{\tt\char126}} \let~ = \activetilde
+\chardef\hatchar=`\^
+\catcode`\^=\active \def\activehat{{\tt \hatchar}} \let^ = \activehat
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+\let\realunder=_
+
+\catcode`\|=\active \def|{{\tt\char124}}
+
+\chardef \less=`\<
+\catcode`\<=\active \def\activeless{{\tt \less}}\let< = \activeless
+\chardef \gtr=`\>
+\catcode`\>=\active \def\activegtr{{\tt \gtr}}\let> = \activegtr
+\catcode`\+=\active \def+{{\tt \char 43}}
+\catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+\catcode`\-=\active \let-=\normaldash
+
+
+% used for headline/footline in the output routine, in case the page
+% breaks in the middle of an @tex block.
+\def\texinfochars{%
+  \let< = \activeless
+  \let> = \activegtr
+  \let~ = \activetilde 
+  \let^ = \activehat
+  \markupsetuplqdefault \markupsetuprqdefault 
+  \let\b = \strong
+  \let\i = \smartitalic
+  % in principle, all other definitions in \tex have to be undone too.
+}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+  \normalturnoffactive
+  \otherbackslash
+}
+
+\catcode`\@=0
+
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont  % let existing .??s files work
+
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
+
+% In Texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active  % @ for escape char from now on.
+
+% Print a typewriter backslash.  For math mode, we can't simply use
+% \backslashcurfont: the story here is that in math mode, the \char
+% of \backslashcurfont ends up printing the roman \ from the math symbol
+% font (because \char in math mode uses the \mathcode, and plain.tex
+% sets \mathcode`\\="026E).  Hence we use an explicit \mathchar,
+% which is the decimal equivalent of "715c (class 7, e.g., use \fam;
+% ignored family value; char position "5C).  We can't use " for the
+% usual hex value because it has already been made active.
+
+@def@ttbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}}
+@let@backslashchar = @ttbackslash % @backslashchar{} is for user documents.
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.  We switch back and forth between these.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+%
+{@catcode`- = @active
+ @gdef@normalturnoffactive{%
+   @nonasciistringdefs
+   @let-=@normaldash
+   @let"=@normaldoublequote
+   @let$=@normaldollar %$ font-lock fix
+   @let+=@normalplus
+   @let<=@normalless
+   @let>=@normalgreater
+   @let^=@normalcaret
+   @let_=@normalunderscore
+   @let|=@normalverticalbar
+   @let~=@normaltilde
+   @let\=@ttbackslash
+   @markupsetuplqdefault
+   @markupsetuprqdefault
+   @unsepspaces
+ }
+}
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have @fixbackslash turn them back on.
+@catcode`+=@other @catcode`@_=@other
+
+% \enablebackslashhack - allow file to begin `\input texinfo'
+%
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+% If the file did not have a `\input texinfo', then it is turned off after
+% the first line; otherwise the first `\' in the file would cause an error.
+% This is used on the very last line of this file, texinfo.tex.
+% We also use @c to call @fixbackslash, in case ends of lines are hidden.
+{
+@catcode`@^=7
+@catcode`@^^M=13@gdef@enablebackslashhack{%
+  @global@let\ = @eatinput%
+  @catcode`@^^M=13%
+  @def@c{@fixbackslash@c}%
+  @def ^^M{@let^^M@secondlinenl}%
+  @gdef @secondlinenl{@let^^M@thirdlinenl}%
+  @gdef @thirdlinenl{@fixbackslash}%
+}}
+
+{@catcode`@^=7 @catcode`@^^M=13%
+@gdef@eatinput input texinfo#1^^M{@fixbackslash}}
+
+% Emergency active definition of newline, in case an active newline token
+% appears by mistake.
+{@catcode`@^=7 @catcode13=13%
+@gdef@enableemergencynewline{%
+  @gdef^^M{%
+    @par%
+    %<warning: active newline>@par%
+}}}
+
+
+@gdef@fixbackslash{%
+  @ifx\@eatinput @let\ = @ttbackslash @fi
+  @catcode13=5 % regular end of line
+  @enableemergencynewline
+  @let@c=@texinfoc
+  % Also turn back on active characters that might appear in the input
+  % file name, in case not using a pre-dumped format.
+  @catcode`+=@active
+  @catcode`@_=@active
+  %
+  % If texinfo.cnf is present on the system, read it.
+  % Useful for site-wide @afourpaper, etc.  This macro, @fixbackslash, gets
+  % called at the beginning of every Texinfo file.  Not opening texinfo.cnf
+  % directly in this file, texinfo.tex, makes it possible to make a format
+  % file for Texinfo.
+  %
+  @openin 1 texinfo.cnf
+  @ifeof 1 @else @input texinfo.cnf @fi
+  @closein 1
+}
+
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
+
+% These (along with & and #) are made active for url-breaking, so need
+% active definitions as the normal characters.
+@def@normaldot{.}
+@def@normalquest{?}
+@def@normalslash{/}
+
+% These look ok in all fonts, so just make them not special.
+% @hashchar{} gets its own user-level command, because of #line.
+@catcode`@& = @other @def@normalamp{&}
+@catcode`@# = @other @def@normalhash{#}
+@catcode`@% = @other @def@normalpercent{%}
+
+@let @hashchar = @normalhash
+
+@c Finally, make ` and ' active, so that txicodequoteundirected and
+@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}.  If we
+@c don't make ` and ' active, @code will not get them as active chars.
+@c Do this last of all since we use ` in the previous @catcode assignments.
+@catcode`@'=@active
+@catcode`@`=@active
+@markupsetuplqdefault
+@markupsetuprqdefault
+
+@c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c page-delimiter: "^\\\\message\\|emacs-page"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@ignore
+   arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore
+@enablebackslashhack
diff --git a/doc/vtysh.1 b/doc/vtysh.1
new file mode 100644 (file)
index 0000000..a2afa9f
--- /dev/null
@@ -0,0 +1,103 @@
+.TH VTYSH 1 "27 July 2006" "Quagga VTY shell" "Version 0.96.5"
+.SH NAME
+vtysh \- a integrated shell for Quagga routing software
+.SH SYNOPSIS
+.B vtysh
+[
+.B \-b
+]
+.br
+.B vtysh
+[
+.B \-E
+] [
+.B \-d
+.I daemon
+]
+] [
+.B \-c
+.I command
+]
+.SH DESCRIPTION
+.B vtysh
+is a integrated shell for
+.B Quagga
+routing engine.
+.SH OPTIONS
+Options available for the
+.B vtysh
+command:
+.IP "\fB\-b, \-\-boot\fP"
+Execute boot startup configuration. It makes sense only if integrated config
+file is in use (not default in Quagga). See Info file \fBQuagga\fR for more
+info.
+.IP "\fB\-c, \-\-command \fIcommand\fP"
+Specify command to be executed under batch mode. It behaves like -c option in
+any other shell -
+.I command
+is executed and
+.B vtysh
+exits.
+
+It's useful for gathering info from Quagga routing software or reconfiguring
+daemons from inside shell scripts, etc.
+Note that multiple commands may be executed by using more than one
+-c option and/or embedding linefeed characters inside the
+.I command
+string.
+.IP "\fB\-d, \-\-daemon \fIdaemon_name\fP"
+Specify which daemon to connect to.  By default,
+.B vtysh
+attempts to connect to all Quagga daemons running on the system.  With this
+flag, one can specify a single daemon to connect to instead.  For example,
+specifying '-d ospfd' will connect only to ospfd.  This can be particularly
+useful inside scripts with -c where the command is targeted for a single daemon.
+.IP "\fB\-e, \-\-execute \fIcommand\fP"
+Alias for -c. It's here only for compatibility with Zebra routing software and
+older Quagga versions. This will be removed in future.
+.IP "\fB\-E, \-\-echo\fP"
+When the -c option is being used, this flag will cause the standard
+.B vtysh
+prompt and command to be echoed prior to displaying the results.
+This is particularly useful to separate the results
+when executing multiple commands.
+.IP "\fB\-h, \-\-help\fP"
+Display a usage message on standard output and exit.
+.SH ENVIRONMENT VARIABLES
+.IP "\fBVTYSH_PAGER\fR"
+This should be the name of the pager to use. Default is \fBmore\fR.
+.SH FILES
+.TP
+.BI /usr/local/etc/vtysh.conf
+The default location of the 
+.B vtysh
+config file.
+.TP
+.BI /usr/local/etc/Quagga.conf
+The default location of the integrated Quagga routing engine config file
+if integrated config file is in use (not default).
+.TP
+.BI ${HOME}/.history_quagga
+Location of history of commands entered via cli
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH "SEE ALSO"
+.BR bgpd (8),
+.BR ripd (8),
+.BR ripngd (8),
+.BR ospfd (8),
+.BR ospf6d (8),
+.BR isisd (8),
+.BR zebra (8)
+.SH BUGS
+.B vtysh
+eats bugs for breakfast. If you have food for the maintainers try 
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/doc/vtysh.texi b/doc/vtysh.texi
new file mode 100644 (file)
index 0000000..66562a9
--- /dev/null
@@ -0,0 +1,61 @@
+@node VTY shell
+@chapter VTY shell
+
+@command{vtysh} is integrated shell of Quagga software.
+
+To use vtysh please specify ---enable-vtysh to configure script.  To use
+PAM for authentication use ---with-libpam option to configure script.
+
+vtysh only searches @value{INSTALL_PREFIX_ETC} path for vtysh.conf which
+is the vtysh configuration file.  Vtysh does not search current
+directory for configuration file because the file includes user
+authentication settings.
+
+Currently, vtysh.conf has only two commands.
+
+@menu
+* VTY shell username::
+* VTY shell integrated configuration::
+@end menu
+
+@node VTY shell username
+@section VTY shell username
+
+@deffn {Command} {username @var{username} nopassword} {}
+
+With this set, user foo does not need password authentication for user vtysh.
+With PAM vtysh uses PAM authentication mechanism.
+
+If vtysh is compiled without PAM authentication, every user can use vtysh
+without authentication. vtysh requires read/write permission
+to the various daemons vty sockets, this can be accomplished through use
+of unix groups and the --enable-vty-group configure option.
+
+@end deffn
+
+@node VTY shell integrated configuration
+@section VTY shell integrated configuration
+
+@deffn {Command} {service integrated-vtysh-config} {}
+Write out integrated Quagga.conf file when 'write file' is issued.
+
+This command controls the behaviour of vtysh when it is told to write out
+the configuration.  Per default, vtysh will instruct each daemon to write
+out their own config files when @command{write file} is issued.  However, if
+@command{service integrated-vtysh-config} is set, when @command{write file}
+is issued, vtysh will instruct the daemons will write out a Quagga.conf with
+all daemons' commands integrated into it. 
+
+Vtysh per default behaves as if @command{write-conf daemon} is set. Note
+that both may be set at same time if one wishes to have both Quagga.conf and
+daemon specific files written out. Further, note that the daemons are
+hard-coded to first look for the integrated Quagga.conf file before looking
+for their own file.
+
+We recommend you do not mix the use of the two types of files. Further, it
+is better not to use the integrated Quagga.conf file, as any syntax error in
+it can lead to /all/ of your daemons being unable to start up. Per daemon
+files are more robust as impact of errors in configuration are limited to
+the daemon in whose file the error is made.
+
+@end deffn
diff --git a/doc/watchquagga.8 b/doc/watchquagga.8
new file mode 100644 (file)
index 0000000..ca99164
--- /dev/null
@@ -0,0 +1,231 @@
+.\" This file was originally generated by help2man 1.36.
+.TH WATCHQUAGGA 8 "July 2010"
+.SH NAME
+watchquagga \- a program to monitor the status of quagga daemons
+.SH SYNOPSIS
+.B watchquagga
+.RI [ option ...]
+.IR daemon ...
+.br
+.B watchquagga
+.BR \-h " | " \-v
+.SH DESCRIPTION
+.B watchquagga
+is a watchdog program that monitors the status of supplied quagga
+.IR daemon s
+and tries to restart them in case they become unresponsive or shut down.
+.PP
+To determine whether a daemon is running, it tries to connect to the
+daemon's VTY UNIX stream socket, and send echo commands to ensure the
+daemon responds. When the daemon crashes, EOF is received from the socket,
+so that watchquagga can react immediately.
+.PP
+This program can run in one of the following 5 modes:
+.TP
+.B Mode 0: monitor
+In this mode, the program serves as a monitor and reports status changes.
+.IP
+Example usage: watchquagga \-d zebra ospfd bgpd
+.TP
+.B Mode 1: global restart
+In this mode, whenever a daemon hangs or crashes, the given command is used
+to restart all watched daemons.
+.IP
+Example usage: watchquagga \-dz \e
+.br
+-R '/sbin/service zebra restart; /sbin/service ospfd restart' \e
+.br
+zebra ospfd
+.TP
+.B Mode 2: individual daemon restart
+In this mode, whenever a single daemon hangs or crashes, the given command
+is used to restart this daemon only.
+.IP
+Example usage: watchquagga \-dz \-r '/sbin/service %s restart' \e
+.br
+zebra ospfd bgpd
+.TP
+.B Mode 3: phased zebra restart
+In this mode, whenever a single daemon hangs or crashes, the given command
+is used to restart this daemon only. The only exception is the zebra
+daemon; in this case, the following steps are taken: (1) all other daemons
+are stopped, (2) zebra is restarted, and (3) other daemons are started
+again.
+.IP
+Example usage: watchquagga \-adz \-r '/sbin/service %s restart' \e
+.br
+\-s '/sbin/service %s start' \e
+.br
+\-k '/sbin/service %s stop' zebra ospfd bgpd
+.TP
+.B Mode 4: phased global restart for any failure
+In this mode, whenever a single daemon hangs or crashes, the following
+steps are taken: (1) all other daemons are stopped, (2) zebra is restarted,
+and (3) other daemons are started again.
+.IP
+Example usage: watchquagga \-Adz \-r '/sbin/service %s restart' \e
+.br
+\-s '/sbin/service %s start' \e
+.br
+\-k '/sbin/service %s stop' zebra ospfd bgpd
+.PP
+Important: It is believed that mode 2 (individual daemon restart) is not
+safe, and mode 3 (phased zebra restart) may not be safe with certain
+routing daemons.
+.PP
+In order to avoid restarting the daemons in quick succession, you can
+supply the
+.B \-m
+and
+.B \-M
+options to set the minimum and maximum delay between the restart commands.
+The minimum restart delay is recalculated each time a restart is attempted.
+If the time since the last restart attempt exceeds twice the value of
+.BR  \-M ,
+the restart delay is set to the value of
+.BR \-m ,
+otherwise the interval is doubled (but capped at the value of
+.BR \-M ).
+.SH OPTIONS
+.TP
+.BR \-d ", " \-\-daemon
+Run in daemon mode. When supplied, error messages are sent to Syslog
+instead of standard output (stdout).
+.TP
+.BI \-S " directory" "\fR, \fB\-\-statedir " directory
+Set the VTY socket
+.I directory
+(the default value is "/var/run/quagga").
+.TP
+.BR \-e ", " \-\-no\-echo
+Do not ping the daemons to test whether they respond. This option is
+necessary if one or more daemons do not support the echo command.
+.TP
+.BI \-l " level" "\fR, \fB\-\-loglevel " level
+Set the logging
+.I level
+(the default value is "6"). The value should range from 0 (LOG_EMERG) to 7
+(LOG_DEBUG), but higher number can be supplied if extra debugging messages
+are required.
+.TP
+.BI \-m " number" "\fR, \fB\-\-min\-restart\-interval " number
+Set the minimum
+.I number
+of seconds to wait between invocations of the daemon restart commands (the
+default value is "60").
+.TP
+.BI \-M " number" "\fR, \fB\-\-max\-restart\-interval " number
+Set the maximum
+.I number
+of seconds to wait between invocations of the daemon restart commands (the
+default value is "600").
+.TP
+.BI \-i " number" "\fR, \fB\-\-interval " number
+Set the status polling interval in seconds (the default value is "5").
+.TP
+.BI \-t " number" "\fR, \fB\-\-timeout " number
+Set the unresponsiveness timeout in seconds (the default value is "10").
+.TP
+.BI \-T " number" "\fR, \fB\-\-restart\-timeout " number
+Set the restart (kill) timeout in seconds (the default value is "20"). If
+any background jobs are still running after this period has elapsed, they
+will be killed.
+.TP
+.BI \-r " command" "\fR, \fB\-\-restart " command
+Supply a Bourne shell
+.I command
+to restart a single daemon. The command string should contain the '%s'
+placeholder to be substituted with the daemon name.
+.IP
+Note that
+.B \-r
+and
+.B \-R
+options are not compatible.
+.TP
+.BI \-s " command" "\fR, \fB\-\-start\-command " command
+Supply a Bourne shell
+.I command
+to start a single daemon. The command string should contain the '%s'
+placeholder to be substituted with the daemon name.
+.TP
+.BI \-k " command" "\fR, \fB\-\-kill\-command " command
+Supply a Bourne shell
+.I command
+to stop a single daemon. The command string should contain the '%s'
+placeholder to be substituted with the daemon name.
+.TP
+.BR \-R ", " \-\-restart\-all
+When one or more daemons are shut down, try to restart them using the
+Bourne shell command supplied on the command line.
+.IP
+Note that
+.B \-r
+and
+.B \-R
+options are not compatible.
+.TP
+.BR \-z ", " \-\-unresponsive\-restart
+When a daemon is in an unresponsive state, treat it as being shut down for
+the restart purposes.
+.TP
+.BR \-a ", " \-\-all\-restart
+When zebra hangs or crashes, restart all daemons taking the following
+steps: (1) stop all other daemons, (2) restart zebra, and (3) start other
+daemons again.
+.IP
+Note that this option also requires
+.BR \-r ,
+.BR \-s ,
+and
+.B \-k
+options to be specified.
+.TP
+.BR \-A ", " \-\-always\-all\-restart
+When any daemon (i.e., not just zebra) hangs or crashes, restart all
+daemons taking the following steps: (1) stop all other daemons, (2) restart
+zebra, and (3) start other daemons again.
+.IP
+Note that this option also requires
+.BR \-r ,
+.BR \-s ,
+and
+.B \-k
+options to be specified.
+.TP
+.BI \-p " filename" "\fR, \fB\-\-pid\-file " filename
+Set the process identifier
+.I filename
+(the default value is "/var/run/quagga/watchquagga.pid").
+.TP
+.BI \-b " string" "\fR, \fB\-\-blank\-string " string
+When the supplied
+.I string
+is found in any of the command line option arguments (i.e.,
+.BR \-r ,
+.BR \-s ,
+.BR \-k ,
+or
+.BR \-R ),
+replace it with a space.
+.IP
+This is an ugly hack to circumvent problems with passing the command line
+arguments containing embedded spaces.
+.TP
+.BR \-v ", " \-\-version
+Display the version information and exit.
+.TP
+.BR \-h ", " \-\-help
+Display the usage information and exit.
+.SH SEE ALSO
+.BR zebra (8),
+.BR bgpd (8),
+.BR isisd (8),
+.BR ospfd (8),
+.BR ospf6d (8),
+.BR ripd (8),
+.BR ripngd (8)
+.PP
+See the project homepage at <http://www.quagga.net/>.
+.SH AUTHORS
+Copyright 2004 Andrew J. Schorr
diff --git a/doc/zebra.8 b/doc/zebra.8
new file mode 100644 (file)
index 0000000..6f70389
--- /dev/null
@@ -0,0 +1,134 @@
+.TH ZEBRA 8 "25 November 2004" "Zebra daemon" "Version 0.97.3"
+.SH NAME
+zebra \- a routing manager for use with associated Quagga components.
+.SH SYNOPSIS
+.B zebra
+[
+.B \-bdhklrv
+] [
+.B \-f
+.I config-file
+] [
+.B \-i
+.I pid-file
+] [
+.B \-P
+.I port-number
+] [
+.B \-A
+.I vty-address
+] [
+.B \-u
+.I user
+] [
+.B \-g
+.I group
+]
+.SH DESCRIPTION
+.B zebra 
+is a routing manager that implements the 
+.B zebra
+route engine.
+.B zebra 
+supports RIPv1, RIPv2, RIPng, OSPF, OSPF6, IS-IS, BGP4+, and BGP4-.
+.SH OPTIONS
+Options available for the
+.B zebra
+command:
+.TP
+\fB\-b\fR, \fB\-\-batch\fR
+Runs in batch mode, \fBzebra\fR parses its config and exits.
+.TP
+\fB\-d\fR, \fB\-\-daemon\fR
+Runs in daemon mode, forking and exiting from tty.
+.TP
+\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR
+Specifies the config file to use for startup. If not specified this
+option will likely default to \fB\fI/usr/local/etc/zebra.conf\fR.
+.TP
+\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR
+Specify the group to run as. Default is \fIquagga\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+A brief message.
+.TP
+\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR
+When zebra starts its process identifier is written to
+\fB\fIpid-file\fR.  The init system uses the recorded PID to stop or
+restart zebra.  The likely default is \fB\fI/var/run/zebra.pid\fR.
+.TP
+\fB\-k\fR, \fB\-\-keep_kernel\fR
+On startup, don't delete self inserted routes.
+.TP
+\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR 
+Specify the port that the zebra VTY will listen on. This defaults to
+2601, as specified in \fB\fI/etc/services\fR.
+.TP
+\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR
+Specify the address that the zebra VTY will listen on. Default is all
+interfaces.
+.TP
+\fB\-u\fR, \fB\-\-user \fR\fIuser\fR
+Specify the user to run as. Default is \fIquagga\fR.
+.TP
+\fB\-r\fR, \fB\-\-retain\fR 
+When the program terminates, retain routes added by \fBzebra\fR.
+.TP
+\fB\-s\fR, \fB\-\-nl-bufsize \fR\fInetlink-buffer-size\fR
+Set netlink receive buffer size. There are cases where zebra daemon can't
+handle flood of netlink messages from kernel. If you ever see "recvmsg overrun"
+messages in zebra log, you are in trouble.
+
+Solution is to increase receive buffer of netlink socket. Note that kernel
+< 2.6.14 doesn't allow to increase it over maximum value defined in
+\fI/proc/sys/net/core/rmem_max\fR. If you want to do it, you have to increase
+maximum before starting zebra.
+
+Note that this affects Linux only.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print the version and exit.
+.SH FILES
+.TP
+.BI /usr/local/sbin/zebra
+The default location of the 
+.B zebra
+binary.
+.TP
+.BI /usr/local/etc/zebra.conf
+The default location of the 
+.B zebra
+config file.
+.TP
+.BI $(PWD)/zebra.log 
+If the 
+.B zebra
+process is config'd to output logs to a file, then you will find this
+file in the directory where you started \fBzebra\fR.
+.SH WARNING
+This man page is intended to be a quick reference for command line
+options. The definitive document is the Info file \fBQuagga\fR.
+.SH DIAGNOSTICS
+The zebra process may log to standard output, to a VTY, to a log
+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 nhrpd (8),
+.BR vtysh (1)
+.SH BUGS
+.B zebra
+eats bugs for breakfast. If you have food for the maintainers try
+.BI http://bugzilla.quagga.net
+.SH AUTHORS
+See
+.BI http://www.zebra.org
+and
+.BI http://www.quagga.net
+or the Info file for an accurate list of authors.
+
diff --git a/fpm/.gitignore b/fpm/.gitignore
new file mode 100644 (file)
index 0000000..b133c52
--- /dev/null
@@ -0,0 +1,15 @@
+Makefile
+Makefile.in
+*.o
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.a
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/fpm/Makefile.am b/fpm/Makefile.am
new file mode 100644 (file)
index 0000000..83ab31c
--- /dev/null
@@ -0,0 +1,29 @@
+include ../common.am
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES)
+
+PROTOBUF_INCLUDES=-I$(top_srcdir)
+PROTOBUF_PACKAGE = fpm
+
+lib_LTLIBRARIES = libfpm_pb.la
+libfpm_pb_la_LDFLAGS = -version-info 0:0:0
+
+if HAVE_PROTOBUF
+protobuf_srcs =
+
+protobuf_srcs_nodist =                         \
+       fpm.pb-c.c
+endif
+
+libfpm_pb_la_SOURCES =                         \
+       fpm.h                                   \
+       fpm_pb.h                                \
+       fpm_pb.c                                \
+       $(protobuf_srcs)
+
+nodist_libfpm_pb_la_SOURCES = $(protobuf_srcs_nodist)
+
+CLEANFILES = $(Q_CLEANFILES)
+
+BUILT_SOURCES = $(Q_PROTOBUF_SRCS)
+EXTRA_DIST = fpm.proto
diff --git a/fpm/fpm.h b/fpm/fpm.h
new file mode 100644 (file)
index 0000000..8528599
--- /dev/null
+++ b/fpm/fpm.h
@@ -0,0 +1,309 @@
+/*
+ * Public definitions pertaining to the Forwarding Plane Manager component.
+ *
+ * Permission is granted to use, copy, modify and/or distribute this
+ * software under either one of the licenses below.
+ *
+ * Note that if you use other files from the Quagga tree directly or
+ * indirectly, then the licenses in those files still apply.
+ *
+ * Please retain both licenses below when modifying this code in the
+ * Quagga tree.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ */
+
+/*
+ * License Option 1: GPL
+ *
+ * This program 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 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.
+ */
+
+/*
+ * License Option 2: ISC License
+ *
+ * Permission to use, copy, modify, and/or distribute this software
+ * for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear
+ * in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FPM_H
+#define _FPM_H
+
+/*
+ * The Forwarding Plane Manager (FPM) is an optional component that
+ * may be used in scenarios where the router has a forwarding path
+ * that is distinct from the kernel, commonly a hardware-based fast
+ * path. It is responsible for programming forwarding information
+ * (such as routes and nexthops) in the fast path.
+ *
+ * In Quagga, the Routing Information Base is maintained in the
+ * 'zebra' infrastructure daemon. Routing protocols communicate their
+ * best routes to zebra, and zebra computes the best route across
+ * protocols for each prefix. This latter information comprises the
+ * bulk of the Forwarding Information Base.
+ *
+ * This header file defines a point-to-point interface using which
+ * zebra can update the FPM about changes in routes. The communication
+ * takes place over a stream socket. The FPM listens on a well-known
+ * TCP port, and zebra initiates the connection.
+ *
+ * All messages sent over the connection start with a short FPM
+ * header, fpm_msg_hdr_t. In the case of route add/delete messages,
+ * the header is followed by a netlink message. Zebra should send a
+ * complete copy of the forwarding table(s) to the FPM, including
+ * routes that it may have picked up from the kernel.
+ *
+ * The FPM interface uses replace semantics. That is, if a 'route add'
+ * message for a prefix is followed by another 'route add' message, the
+ * information in the second message is complete by itself, and replaces
+ * the information sent in the first message.
+ *
+ * If the connection to the FPM goes down for some reason, the client
+ * (zebra) should send the FPM a complete copy of the forwarding
+ * table(s) when it reconnects.
+ */
+
+/*
+ * Local host as a default server for fpm connection 
+ */
+#define FPM_DEFAULT_IP              (htonl (INADDR_LOOPBACK))
+
+/*
+ * default port for fpm connections
+ */
+#define FPM_DEFAULT_PORT 2620
+
+/*
+ * Largest message that can be sent to or received from the FPM.
+ */
+#define FPM_MAX_MSG_LEN 4096
+
+#ifdef __SUNPRO_C
+#pragma pack(1)
+#endif
+
+/*
+ * Header that precedes each fpm message to/from the FPM.
+ */
+typedef struct fpm_msg_hdr_t_
+{
+  /*
+   * Protocol version.
+   */
+  uint8_t version;
+
+  /*
+   * Type of message, see below.
+   */
+  uint8_t msg_type;
+
+  /*
+   * Length of entire message, including the header, in network byte
+   * order.
+   */
+  uint16_t msg_len;
+} __attribute__ ((packed)) fpm_msg_hdr_t;
+
+#ifdef __SUNPRO_C
+#pragma pack()
+#endif
+
+/*
+ * The current version of the FPM protocol is 1.
+ */
+#define FPM_PROTO_VERSION 1
+
+typedef enum fpm_msg_type_e_ {
+  FPM_MSG_TYPE_NONE = 0,
+
+  /*
+   * Indicates that the payload is a completely formed netlink
+   * message.
+   *
+   * XXX Netlink cares about the alignment of messages. When any
+   * FPM_MSG_TYPE_NETLINK messages are sent over a channel, then all
+   * messages should be sized such that netlink alignment is
+   * maintained.
+   */
+  FPM_MSG_TYPE_NETLINK = 1,
+  FPM_MSG_TYPE_PROTOBUF = 2,
+} fpm_msg_type_e;
+
+/*
+ * The FPM message header is aligned to the same boundary as netlink
+ * messages (4). This means that a netlink message does not need
+ * padding when encapsulated in an FPM message.
+ */
+#define FPM_MSG_ALIGNTO 4
+
+/*
+ * fpm_msg_align
+ *
+ * Round up the given length to the desired alignment.
+ *
+ * **NB**: Alignment is required only when netlink messages are used.
+ */
+static inline size_t
+fpm_msg_align (size_t len)
+{
+  return (len + FPM_MSG_ALIGNTO - 1) & ~(FPM_MSG_ALIGNTO - 1);
+}
+
+/*
+ * The (rounded up) size of the FPM message header. This ensures that
+ * the message payload always starts at an aligned address.
+ */
+#define FPM_MSG_HDR_LEN (sizeof (fpm_msg_hdr_t))
+
+#ifndef COMPILE_ASSERT
+#define COMPILE_ASSERT(x) extern int __dummy[2 * !!(x) - 1]
+#endif
+
+COMPILE_ASSERT(FPM_MSG_ALIGNTO == FPM_MSG_HDR_LEN);
+
+/*
+ * fpm_data_len_to_msg_len
+ *
+ * The length value that should be placed in the msg_len field of the
+ * header for a *payload* of size 'data_len'.
+ */
+static inline size_t
+fpm_data_len_to_msg_len (size_t data_len)
+{
+  return data_len + FPM_MSG_HDR_LEN;
+}
+
+/*
+ * fpm_msg_data
+ *
+ * Pointer to the payload of the given fpm header.
+ */
+static inline void *
+fpm_msg_data (fpm_msg_hdr_t *hdr)
+{
+  return ((char*) hdr) + FPM_MSG_HDR_LEN;
+}
+
+/*
+ * fpm_msg_len
+ */
+static inline size_t
+fpm_msg_len (const fpm_msg_hdr_t *hdr)
+{
+  return ntohs (hdr->msg_len);
+}
+
+/*
+ * fpm_msg_data_len
+ */
+static inline size_t
+fpm_msg_data_len (const fpm_msg_hdr_t *hdr)
+{
+  return (fpm_msg_len (hdr) - FPM_MSG_HDR_LEN);
+}
+
+/*
+ * fpm_msg_next
+ *
+ * Move to the next message in a buffer.
+ */
+static inline fpm_msg_hdr_t *
+fpm_msg_next (fpm_msg_hdr_t *hdr, size_t *len)
+{
+  size_t msg_len;
+
+  msg_len = fpm_msg_len (hdr);
+
+  if (len) {
+    if (*len < msg_len)
+      {
+       assert(0);
+       return NULL;
+      }
+    *len -= msg_len;
+  }
+
+  return (fpm_msg_hdr_t *) (((char*) hdr) + msg_len);
+}
+
+/*
+ * fpm_msg_hdr_ok
+ *
+ * Returns TRUE if a message header looks well-formed.
+ */
+static inline int
+fpm_msg_hdr_ok (const fpm_msg_hdr_t *hdr)
+{
+  size_t msg_len;
+
+  if (hdr->msg_type == FPM_MSG_TYPE_NONE)
+    return 0;
+
+  msg_len = fpm_msg_len (hdr);
+
+  if (msg_len < FPM_MSG_HDR_LEN || msg_len > FPM_MAX_MSG_LEN)
+    return 0;
+
+  /*
+   * Netlink messages must be aligned properly.
+   */
+  if (hdr->msg_type == FPM_MSG_TYPE_NETLINK &&
+      fpm_msg_align (msg_len) != msg_len)
+    return 0;
+
+  return 1;
+}
+
+/*
+ * fpm_msg_ok
+ *
+ * Returns TRUE if a message looks well-formed.
+ *
+ * @param len The length in bytes from 'hdr' to the end of the buffer.
+ */
+static inline int
+fpm_msg_ok (const fpm_msg_hdr_t *hdr, size_t len)
+{
+  if (len < FPM_MSG_HDR_LEN)
+    return 0;
+
+  if (!fpm_msg_hdr_ok (hdr))
+    return 0;
+
+  if (fpm_msg_len (hdr) > len)
+    return 0;
+
+  return 1;
+}
+
+// tcp maximum range
+#define TCP_MAX_PORT   65535
+
+// tcp minimum range 
+#define TCP_MIN_PORT   1
+
+#endif /* _FPM_H */
diff --git a/fpm/fpm.proto b/fpm/fpm.proto
new file mode 100644 (file)
index 0000000..318e80a
--- /dev/null
@@ -0,0 +1,88 @@
+//
+// fpm.proto
+//
+// @copyright Copyright (C) 2016 Sproute Networks, Inc.
+//
+// @author Avneesh Sachdev <avneesh@sproute.com>
+//
+// Permission to use, copy, modify, and/or distribute this software
+// for any purpose with or without fee is hereby granted, provided
+// that the above copyright notice and this permission notice appear
+// in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+//
+// Protobuf definitions pertaining to the Forwarding Plane Manager component.
+//
+
+package fpm;
+
+import "qpb/qpb.proto";
+
+//
+// A Nexthop for a route. It indicates how packets to a given prefix
+// should be forwarded (for instance, send them out of a specified
+// interface to a specified address).
+//
+message Nexthop {
+  optional qpb.IfIdentifier if_id = 2;
+  optional qpb.L3Address address = 3;
+}
+
+message RouteKey {
+  optional qpb.L3Prefix prefix = 1;
+}
+
+message DeleteRoute {
+  required uint32 vrf_id = 1;
+  required qpb.AddressFamily address_family = 2;
+  required qpb.SubAddressFamily sub_address_family = 3;
+  required RouteKey key = 4;
+}
+
+enum RouteType {
+  UNKNOWN = 0;
+  NORMAL = 1;
+  UNREACHABLE = 2;
+  BLACKHOLE = 3;
+}
+
+message AddRoute {
+  required uint32 vrf_id = 1;
+  required qpb.AddressFamily address_family = 2;
+  required qpb.SubAddressFamily sub_address_family = 3;
+  required RouteKey key = 4;
+
+  optional RouteType route_type = 5;
+
+  required qpb.Protocol protocol = 6;
+
+  required int32 metric = 8;
+
+  repeated Nexthop nexthops = 9;
+}
+
+//
+// Any message from the FPM.
+//
+message Message {
+  enum Type {
+    UNKNOWN_MSG = 0;
+    ADD_ROUTE = 1;
+    DELETE_ROUTE = 2;
+  };
+
+  optional Type type = 1;
+
+  optional AddRoute add_route = 2;
+  optional DeleteRoute delete_route = 3;
+}
diff --git a/fpm/fpm_pb.c b/fpm/fpm_pb.c
new file mode 100644 (file)
index 0000000..ba18627
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * fpm_pb.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Main file for the fpm_pb library.
+ */
diff --git a/fpm/fpm_pb.h b/fpm/fpm_pb.h
new file mode 100644 (file)
index 0000000..8f74ac0
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * fpm_pb.h
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Public header file for fpm protobuf definitions.
+ */
+
+#ifndef _FPM_PB_H
+#define _FPM_PB_H
+
+#include "route_types.h"
+#include "qpb/qpb.h"
+
+#include "fpm/fpm.pb-c.h"
+
+/*
+ * fpm__route_key__create
+ */
+#define fpm_route_key_create fpm__route_key__create
+static inline Fpm__RouteKey *
+fpm__route_key__create (qpb_allocator_t *allocator, struct prefix *prefix)
+{
+  Fpm__RouteKey *key;
+
+  key = QPB_ALLOC (allocator, typeof (*key));
+  if (!key)
+    {
+      return NULL;
+    }
+  fpm__route_key__init (key);
+
+  key->prefix = qpb__l3_prefix__create (allocator, prefix);
+  if (!key->prefix)
+    {
+      return NULL;
+    }
+
+  return key;
+}
+
+#endif
diff --git a/gdb/lib.txt b/gdb/lib.txt
new file mode 100644 (file)
index 0000000..b703808
--- /dev/null
@@ -0,0 +1,295 @@
+# GDB macros for use with Quagga.
+#
+# Macros in this file are not daemon specific. E.g., OS or Quagga library
+# APIs.
+#
+# The macro file can be loaded with 'source <filename>'. They can then be
+# called by the user. Macros that explore more complicated structs generally
+# take pointer arguments.
+#
+# E.g.:
+# 
+# (gdb) source ~paul/code/quagga/gdb/lib.txt
+# (gdb) break bgp_packet.c:613
+# Breakpoint 3 at 0x7fa883033a32: file bgp_packet.c, line 613.
+# (gdb) cont
+# ...
+# (gdb) cont
+# Breakpoint 3, bgp_write_packet (peer=0x7fa885199080) at bgp_packet.c:614
+# 614                     if (CHECK_FLAG (adv->binfo->peer->cap,PEER_CAP_RESTART_RCV)
+# (gdb) dump_prefix4  &adv->rn->p
+# IPv4:10.1.1.0/24
+# (gdb) dump_prefix  &adv->rn->p
+# IPv4:10.1.1.0/24
+#
+
+
+define def_ntohs
+ set $data = (char *)$arg0
+ set $i = 0
+ set $_  = $data[$i++] << 8
+ set $_ += $data[$i++]
+end
+document def_ntohs
+Read a 2-byte short at the given pointed to area as big-endian and 
+return it in $_
+
+Argument: Pointer to a 2-byte, big-endian short word.
+Returns: Integer value of that word in $_
+end
+
+define def_ntohl
+ set $data = (char *)$arg0
+ set $i = 0
+ set $_  = $data[$i++] << 24
+ set $_ += $data[$i++] << 16
+ set $_ += $data[$i++] << 8
+ set $_ += $data[$i++]
+end
+document def_ntohl
+Read a 4-byte integer at the given pointed to area as big-endian and 
+return it in $_
+
+Argument: Pointer to a big-endian 4-byte word.
+Returns: Integer value of that word in $_
+end
+
+# NB: This is in more complicated iterative form, rather than more
+# conventional and simpler recursive form, because GDB has a recursion limit
+# on macro calls (I think).
+define walk_route_table_next
+  # callee saves
+  set $_top = $top
+  set $_node = $node
+  set $_prevl = $prevl
+  
+  set $top = (struct route_node *)$arg0
+  set $node = (struct route_node *)$arg1
+  set $prevl = $node
+  
+  # first try left
+  #echo try left\n
+  set $node = $prevl->link[0]
+  
+  # otherwise try right
+  if ($node == 0)
+    #echo left null, try right\n
+    set $node = $prevl->link[1]
+  end
+  
+  # otherwise go up, till we find the first right that
+  # we havn't been to yet
+  if ($node == 0)
+    set $node = $prevl
+    while ($node != $top)
+       #echo right null, try up and right\n
+       
+       set $prevl = $node
+       set $parent = $node->parent
+       set $node = $parent->link[1]
+       
+       if ($node != 0 && $node != $prevl)
+         #echo found node \n
+         loop_break
+       end
+       
+       #echo go up\n
+       set $node = $parent       
+    end
+  end
+  
+  #printf "next node: 0x%x\n", $node
+  
+  set $_ = $node
+  
+  set $top = $_top
+  set $node = $_node
+  set $prevl = $_prevl
+end
+document walk_route_table_next
+Return the next node to visit in the given route_table (or subset of) and
+the given current node.
+
+Arguments:
+1st: (struct route_node *) to the top of the route_table to walk
+2nd: (struct route_node *) to the current node
+
+Returns: The (struct route_node *) for the next to visit in $_
+end
+
+define walk_route_table
+  set $_visited = $visited
+  set $_node = $node
+  set $top = $_top
+  
+  set $node = (struct route_node *)$arg0
+  set $top = (struct route_node *)$arg0
+  set $visited = 0
+  
+  while ($node != 0)
+    printf "Node: 0x%x", $node
+
+    if ($node->info != 0)
+      printf "\tinfo: 0x%x", $node->info
+      set $visited = $visited + 1
+    end
+    
+    printf "\n"
+    
+    walk_route_table_next $top $node
+    set $node = $_
+    
+    # we've gotten back to the top, finish
+    if ($node == $top)
+      set $node = 0
+    end
+  end
+  printf "Visited: %u\n", $visited
+  
+  set $top = $_top
+  set $visited = $_visited
+  set $node = $_node
+end
+
+document walk_route_table
+Walk through a routing table (or subset thereof) and dump all the non-null
+(struct route_node *)->info pointers.
+
+Argument: A lib/thread.h::(struct route_node *) pointing to the route_node
+under which all data should be dumped
+end
+
+define dump_timeval 
+  set $tv = (struct timeval *)$arg0
+  set $day = 3600*24
+  
+  if $tv->tv_sec > $day
+    printf "%d days, ", $tv->tv_sec / $day
+  end
+  if $tv->tv_sec > 3600
+    printf "%dh", $tv->tv_sec / 3600
+  end
+  if ($tv->tv_sec % 3600) > 60
+    printf "%dm", ($tv->tv_sec % 3600) / 60
+  end
+  printf "%d", $tv->tv_sec % 3600 % 60
+  if $tv->tv_usec != 0
+    printf ".%06d", $tv->tv_usec
+  end
+  printf "s"
+end
+document dump_timeval
+Human readable dump of a (struct timeval *) argument
+end
+
+define dump_s_addr
+  set $addr = (char *)$arg0
+  
+  printf "%d.%d.%d.%d", $addr[0], $addr[1], $addr[2], $addr[3]
+end
+
+define dump_s6_addr
+  set $a6 = (char *)$arg0
+  set $field = 0
+  
+  while ($field < 16)
+    set $i1 = $field++
+    set $i2 = $field++
+    
+    printf "%x%x", $a6[$i1], $a6[$i2]
+    
+    if ($field > 2 && ($field % 4 == 0))
+      printf ":"
+    end
+  end
+end
+document dump_s6_addr
+Interpret the memory starting at given address as an IPv6 s6_addr and
+print in human readable form.
+end
+
+define dump_prefix4
+  set $p = (struct prefix *) $arg0
+  echo IPv4:
+  dump_s_addr &($p->u.prefix4)
+  printf "/%d\n", $p->prefixlen
+end
+document dump_prefix4
+Textual dump of a (struct prefix4 *) argument.
+end
+
+define dump_prefix6
+  set $p = (struct prefix *) $arg0
+  echo IPv6:
+  dump_s6_addr &($p->u.prefix6)
+  printf "/%d\n", $p->prefixlen
+end
+document dump_prefix6
+Textual dump of a (struct prefix6 *) argument.
+end
+
+define dump_prefix
+  set $p = $arg0
+  
+  if ($p->family == 2)
+    dump_prefix4 $p
+  end
+  if ($p->family == 10)
+    dump_prefix6 $p
+  end
+end
+document dump_prefix
+Human readable dump of a (struct prefix *) argument.
+end
+
+define rn_next_down
+  set $node = $arg0
+  while ($node != 0)
+    print/x $node
+    if ($node->link[0] != 0)
+      set $node = $node->link[0]
+    else
+      set $node = $node->link[1]
+    end
+  end
+end
+
+document rn_next_down
+Walk left-down a given route table, dumping locations of route_nodes
+
+Argument: A single (struct route_node *).
+end
+
+define rn_next_up
+  set $top = (struct route_node *)$arg0
+  set $node = (struct route_node *)$arg1
+  
+  while ($node != $top)
+    echo walk up\n
+    
+    set $prevl = $node
+    set $parent = $node->parent
+    set $node = $parent->link[1]
+    
+    if ($node != 0 && $node != $prevl)
+      echo found a node\n
+      loop_break
+    end
+    
+    echo going up\n
+    set $node = $parent
+  end
+  output/x $node
+  echo \n
+end
+
+document rn_next_up
+Walk up-and-right from the given route_node to the next valid route_node
+which is not the given "top" route_node
+
+Arguments:
+1st: A (struct route_node *) to the top of the route table.
+2nd: The (struct route_node *) to walk up from
+end
diff --git a/gdb/ospf.txt b/gdb/ospf.txt
new file mode 100644 (file)
index 0000000..984104b
--- /dev/null
@@ -0,0 +1,137 @@
+# GDB macros for use with Quagga.
+#
+# Macros in this file are specific to ospfd/. Definitions here depend on the
+# lib.txt macros file, which must also be loaed.
+#
+# The macro file can be loaded with 'source <filename>'. They can then be   
+# called by the user. Macros that explore more complicated structs generally
+# take pointer arguments. 
+
+define dump_ospf_lsa_flags
+  set $flags = $arg0
+  
+  printf "%u: ", $flags
+  
+  if $flags & 0x1
+    echo Self,
+  end
+  if $flags & 0x2
+    echo Self-checked,
+  end
+  if $flags & 0x4
+    echo Recvd,
+  end
+  if $flags & 0x8
+    echo Apprvd,
+  end
+  if $flags & 0x10
+    echo Discard,
+  end
+  if $flags & 0x20
+    echo Local-Xlt,
+  end
+  if $flags & 0x40
+    echo Premature-Aged,
+  end
+  if $flags & 0x40
+    echo In-Maxage,
+  end
+  echo \n
+end
+
+define dump_ospf_lsa_data
+  set $lsad = (struct lsa_header *)$arg0
+  
+  echo ID / AdvRtr:  \t\t
+  dump_s_addr &$lsad->id.s_addr
+  echo \ : \ 
+  dump_s_addr &$lsad->adv_router.s_addr
+  echo \n
+  
+  def_ntohs &$lsad->ls_age
+  printf "Type: %2u Age: %4u,", $lsad->type, $_
+  
+  def_ntohs &$lsad->length
+  printf " length: %2u", $_
+  
+  def_ntohl &$lsad->ls_seqnum
+  printf " Seqnum: 0x%08x", $_
+  
+  def_ntohs &$lsad->checksum
+  printf " csum: 0x%04x\n", $_
+  
+  # return the age
+  def_ntohs &$lsad->ls_age
+end
+
+define dump_ospf_lsa
+  set $lsa = (struct ospf_lsa *)$arg0
+  
+  #print/x *$lsa
+  
+  dump_ospf_lsa_data $lsa->data
+  
+  set $relage = $_ + (relative_time.tv_sec - $lsa->tv_recv.tv_sec)
+  printf "Relative age: %4u\n", $relage
+  
+  dump_ospf_lsa_flags $lsa->flags
+  
+  echo tv_recv: \ 
+  dump_timeval &$lsa->tv_recv
+  echo \ tv_orig: \ 
+  dump_timeval &$lsa->tv_orig
+  echo \n
+  
+  printf "lock %2u", $lsa->lock
+  printf " stat %2d", $lsa->stat
+  printf " rtx count: %u", $lsa->retransmit_counter
+  printf " rfsh list: %d", $lsa->refresh_list
+  printf "\n\n"
+end
+
+define walk_ospf_lsdb
+  set $node = (struct route_node *)$arg0
+  set $top = (struct route_node *)$arg0
+  set $visited = 0
+  
+  while ($node != 0)
+    set $prevl = $node
+    
+    if ($node->info != 0)
+      dump_ospf_lsa $node->info
+      set $visited = $visited + 1
+    end
+    
+    walk_route_table_next $top $node
+    set $node = $_
+    
+    # we've gotten back to the top, finish
+    if ($node == $top)
+      set $node = 0
+    end
+  end
+  printf "Visited: %u\n", $visited
+end
+
+document walk_ospf_lsdb
+Walk through an OSPF LSDB (or subset thereof) and dump all the LSAs
+contained there-in.
+
+Argument: A (struct route_node *) pointing to the top of the
+LSDB route-table which should be dumped.
+end
+
+define ospf_backbone_lsdb_top
+  set $type = $arg0
+  
+  set $ospf = ospf_master->ospf->head->data
+  
+  output/x ((struct ospf *)$ospf)->backbone->lsdb->type[$type]->db->top
+  echo \n
+end
+document ospf_backbone_lsdb_top
+Dump location of the LSDB in the backbone area for the given LSA type
+
+Argument: Integer LSA type
+end
+
diff --git a/infra/buildbot/master/master.cfg b/infra/buildbot/master/master.cfg
new file mode 100644 (file)
index 0000000..00e56dd
--- /dev/null
@@ -0,0 +1,619 @@
+# -*- python -*-
+# ex: set syntax=python:
+
+from buildbot.plugins import *
+from buildbot.plugins import buildslave, util
+
+# This is a sample buildmaster config file. It must be installed as
+# 'master.cfg' in your buildmaster's base directory.
+
+# This is the dictionary that the buildmaster pays attention to. We also use
+# a shorter alias to save typing.
+c = BuildmasterConfig = {}
+
+quaggagit = 'git://git.sv.gnu.org/quagga.git'
+
+# password defs
+execfile("pass.cfg")
+
+# filter a given 'workers' entry into a property list
+# suitable for public display
+def workers2publicprops (worker):
+       publicprops = [ "os", "version", "vm", "pkg", "texi", "cc",
+                       "latent", ]
+       return  { k:worker[k] for k in worker if k in publicprops }
+
+# vm: non-VM are assumed faster and used for initial build
+# pkg: rpm, sysv, dpkg - only do test rpm builds at moment
+# texi: True or "true" if we can use for doc building
+# cc: List of tuples of installed compilers, with:
+#     (<tag>, <version>, <command>)
+#     tag: gcc, clang, sunpro
+# latent: VM spun up on demand via LatentSlave, uses "session" for
+#         the libvirt URI.
+# session: libvirt URI to use for latent workers. Default will be set on
+#          latent VMs if not specified.
+# hd_image: libvirt image to use
+workers = {
+       "fedora-24": { 
+         "os": "Fedora",
+         "version": "24",
+         "vm": False,
+         "pkg": "rpm",
+         "texi": True,
+         "cc": [ ("gcc", "6.3.1"),
+                 ("clang", "3.8.1"),
+                 ("gcc",  "3.4.6", "gcc34"),
+               ],
+       }, 
+       "fedora-26": { 
+         "os": "Fedora",
+         "version": "26",
+         "vm": False,
+         "pkg": "rpm",
+         "cc": [ ("gcc", "7.0.1"),
+                 ("clang", "3.9.0"),
+                 ("gcc",  "3.4.6", "gcc34"),
+               ],
+       }, 
+       "centos-7": {
+         "os": "CentOS",
+         "version": "7",
+         "vm": False,
+         "pkg": "rpm",
+         "cc": [ ("gcc", "4.8.5") ],
+       },
+       "debian-8": {
+         "os": "Debian",
+         "version": "8",
+         "vm": True,   
+         "pkg": "dpkg",
+         "latent": True,
+         "cc": [ ("gcc", "4.9.2") ],
+         "hd_image": "/var/lib/libvirt/images/debian8.qcow2",
+       },
+       "debian-9": { 
+         "os": "Debian",
+         "version": "9",
+         "vm": True,   
+         "pkg": "dpkg",
+         "cc": [ ("gcc", "6.3.0") ],
+         "latent": True,
+         "hd_image": "/var/lib/libvirt/images/debian9.qcow2",
+       },
+       "freebsd-10": { 
+         "os": "FreeBSD",
+         "version": "10",
+         "vm": True,   
+         "pkg": "",
+         "latent": True,
+         "cc": [ ("clang", "3.4.1") ],
+         "hd_image": "/var/lib/libvirt/images/freebsd103.qcow2",
+       }, 
+       "freebsd-11": { 
+         "os": "FreeBSD",
+         "version": "11",
+         "vm": True,   
+         "pkg": "",
+         "cc": [ ("gcc", "4.9.4"), ("clang", "3.8.0"), ],
+         "latent": True,
+         "hd_image": "/var/lib/libvirt/images/freebsd110.qcow2",
+       },
+       "oi-hipster": {
+         "os": "OpenIndiana",
+         "version": "hipster",
+         "vm": True,
+         "pkg": "sysv",
+         "latent": True,
+         "cc": [ ("gcc", "6.3.0"), ("sunpro", "12.0"), 
+                 ("gcc", "4.4.4") 
+               ],
+         "hd_image": "/var/lib/libvirt/images/buildbot-oi-hipster.qcow2",
+       },
+}
+
+# ensure "latent" is set to false, where not set.
+# add in the passwords
+for kw in workers:
+       w = workers[kw]
+       w["bot"] = "buildbot-" + kw
+       if "latent" not in w:
+               w["latent"] = False
+       w["pass"] = workers_pass[kw]
+
+analyses_builders = [ "clang-analyzer" ]
+
+# default Libvirt session
+for w in (w for w in workers.values () if ("latent" in w and w["latent"])
+                                       and ("session" not in w)):
+       w["session"] = 'qemu+ssh://buildbot@sagan.jakma.org/system'
+
+osbuilders = ["build-" + kw for kw in workers]
+osfastbuilders = ["build-" + kw for kw in workers if workers[kw]["vm"] == False]
+osslowbuilders = ["build-" + kw for kw in workers if workers[kw]["vm"] == True]
+
+rpmbuilders = ["rpm-" + kw for kw in workers if workers[kw]["pkg"] == "rpm"]
+
+# compilers
+# not using yet
+# [kw for kw in workers if len([v for (c,v) in workers[kw]["cc"] if c == "gcc"]) > 0 ]
+
+allbuilders =  []
+allbuilders += osbuilders
+allbuilders += rpmbuilders
+allbuilders += analyses_builders
+allbuilders += ["commit-builder"]
+allbuilders += ["build-distcheck"]
+allbuilders += ["build-docs" ]
+
+# Force merging of requests.
+# c['mergeRequests'] = lambda *args, **kwargs: True
+
+####### BUILDSLAVES
+c['slaves'] = []
+
+# The 'slaves' list defines the set of recognized buildslaves. Each element is
+# a BuildSlave object, specifying a unique slave name and password.  The same
+# slave name and password must be configured on the slave.
+
+for w in (w for w in workers.values() if ("latent" not in w)
+                                     or (w["latent"] == False)):
+       c['slaves'].append(buildslave.BuildSlave(w["bot"], w["pass"],
+                               properties=workers2publicprops (w),
+       ))
+
+for w in (w for w in workers.values()
+                 if ("latent" in w) 
+                    and w["latent"]
+                    and "hd_image" in w):
+       c['slaves'].append(buildslave.LibVirtSlave(
+                                       w["bot"],
+                                       w["pass"],
+                                       util.Connection(w["session"]),
+                                       w["hd_image"],
+                                       properties=workers2publicprops (w),
+       ))
+
+# 'protocols' contains information about protocols which master will use for
+# communicating with slaves.
+# You must define at least 'port' option that slaves could connect to your master
+# with this protocol.
+# 'port' must match the value configured into the buildslaves (with their
+# --master option)
+c['protocols'] = {'pb': {'port': 9989}}
+
+####### CHANGESOURCES
+
+# the 'change_source' setting tells the buildmaster how it should find out
+# about source code changes.  Here we point to the buildbot clone of pyflakes.
+
+c['change_source'] = []
+c['change_source'].append(changes.GitPoller(
+       quaggagit,
+        workdir='gitpoller-workdir', 
+       branches=['master','volatile/next'],
+        pollinterval=300))
+
+####### REVISION LINKS
+# associate changesouce repositories to URI templates for code view
+#
+c['revlink'] = util.RevlinkMatch([quaggagit + r"(.*)"],
+       r"http://git.savannah.gnu.org/cgit/quagga.git/commit/?id=%s")
+
+####### SCHEDULERS
+
+# Configure the Schedulers, which decide how to react to incoming changes. 
+
+# We want a first line of 'quick' builds, which then trigger further builds.
+#
+# A control-flow builder, "commit-builder", used to sequence the 'real'
+# sets of builders, via Triggers.
+
+c['schedulers'] = []
+c['schedulers'].append(schedulers.SingleBranchScheduler(
+                            name="master-change",
+                            change_filter=util.ChangeFilter(branch='master'),
+                            treeStableTimer=10,
+                            builderNames=[ "commit-builder" ]))
+
+c['schedulers'].append(schedulers.SingleBranchScheduler(
+                            name="next-change",
+                            change_filter=util.ChangeFilter(
+                               branch='volatile/next'),
+                            treeStableTimer=10,
+                            builderNames=[ "commit-builder" ] ))
+
+# Initial build checks on faster, non-VM
+c['schedulers'].append(schedulers.Triggerable(
+       name="trigger-build-first",
+       builderNames=osfastbuilders))
+
+# Build using remaining builders, after firstbuilders.
+c['schedulers'].append(schedulers.Triggerable(
+       name="trigger-build-rest",
+       builderNames=osslowbuilders))
+
+# Analyses tools, e.g. CLang Analyzer scan-build
+c['schedulers'].append(schedulers.Triggerable(
+                      name="trigger-build-analyses",   
+                      builderNames=analyses_builders))
+# Dist check
+c['schedulers'].append(schedulers.Triggerable(
+                      name="trigger-distcheck",        
+                      builderNames=["build-distcheck"]))
+# RPM check and build
+c['schedulers'].append(schedulers.Triggerable(
+                      name="trigger-rpm",      
+                      builderNames=rpmbuilders))
+
+# Doc build check (non-nightly, so no upload)
+c['schedulers'].append(schedulers.Triggerable(
+                      name="trigger-build-docs",       
+                      builderNames=["build-docs"]))
+
+# Try and force schedulers
+c['schedulers'].append(schedulers.ForceScheduler(
+                       name="force",
+                       builderNames=allbuilders))
+
+c['schedulers'].append(schedulers.Try_Userpass(
+                      name="try",
+                      builderNames=osbuilders
+                                   + rpmbuilders
+                                   + ["build-distcheck", 
+                                      "clang-analyzer",
+                                      "build-docs" ],
+                       userpass=users,
+                       port=8031))
+
+## nightly docs build
+c['schedulers'].append(schedulers.Nightly(
+       name="nightly-docs",
+       branch="master",
+       builderNames=[ "build-docs" ],
+       hour=3,
+       minute=0,
+       onlyIfChanged=True,
+       properties = { "nightly": True },
+))
+
+
+####### BUILDERS
+c['builders'] = []
+
+# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
+# what steps, and which slaves can execute them.  Note that any particular build will
+# only take place on one slave.
+
+common_setup = [
+       steps.Git(repourl=quaggagit, mode='incremental'),
+       steps.ShellCommand(command=["./update-autotools"],
+                                   description="generating autoconf",
+                                   descriptionDone="autoconf"),
+       steps.Configure(command="../build/configure"),
+       steps.ShellCommand(command=["make", "clean"],
+                                   description="cleaning",
+                                   descriptionDone="make clean"),
+]
+
+### Default 'check' build, builder instantiated for each OS
+
+factory = util.BuildFactory()
+# check out the source
+factory.addStep(steps.Git(repourl=quaggagit, mode='incremental'))
+factory.addStep(steps.ShellCommand(command=["./update-autotools"],
+                                  description="generating autoconf",
+                                  descriptionDone="autoconf"))
+factory.addStep(steps.Configure())
+factory.addStep(steps.ShellCommand(command=["make", "clean"],
+                                  description="cleaning",
+                                  descriptionDone="clean"))
+
+#factory.addSteps(common_setup)
+factory.addStep(steps.Compile(command=["make", "-j", "2", "all"]))
+factory.addStep(steps.ShellCommand(command=["make", "check"],
+                                  description="checking",
+                                  descriptionDone="make check"))
+
+# create builder for every OS, for every buildbot
+# XXX: at moment this assumes 1:1 OS<->bot
+for kw in workers:
+   c['builders'].append(util.BuilderConfig(
+       name="build-" + kw,
+       slavenames=workers[kw]["bot"],
+       factory=factory))
+
+### distcheck Builder, executed on any available bot
+factory = util.BuildFactory()
+# check out the source
+factory.addStep(steps.Git(repourl=quaggagit, mode='incremental'))
+factory.addStep(steps.ShellCommand(command=["./update-autotools"],
+                                  description="generating autoconf",
+                                  descriptionDone="autoconf"))
+factory.addStep(steps.Configure())
+factory.addStep(steps.ShellCommand(command=["make", "clean"],
+                                  description="cleaning",
+                                  descriptionDone="make clean"))
+factory.addStep(steps.ShellCommand(command=["make", "distcheck"],
+                                  description="run make distcheck",
+                                  descriptionDone="make distcheck"))
+c['builders'].append(
+       util.BuilderConfig(name="build-distcheck",
+       slavenames=list(w["bot"] for w in workers.values()),
+       factory=factory,
+))
+
+### LLVM clang-analyzer build, executed on any available non-VM bot
+
+f = util.BuildFactory()
+# check out the source
+f.addStep(steps.Git(repourl=quaggagit, mode='incremental',
+                   getDescription=True))
+f.addStep(steps.ShellCommand(command=["./update-autotools"],
+                            description="run autotools",
+                            descriptionDone="autoconf"))
+f.addStep(steps.Configure())
+f.addStep(steps.ShellCommand(command=["make", "clean"],
+                            description="cleaning",
+                            descriptionDone="make clean"))
+
+f.addStep(steps.SetProperty(property="clang-id",
+       value=util.Interpolate("%(prop:commit-description)s-%(prop:buildnumber)s")))
+
+f.addStep(steps.SetProperty(property="clang-output-dir",
+       value=util.Interpolate("../CLANG-%(prop:clang-id)s")))
+f.addStep(steps.SetProperty(property="clang-uri",
+       value=util.Interpolate("/clang-analyzer/%(prop:clang-id)s")))
+# relative to buildbot master working directory
+f.addStep(steps.SetProperty(property="clang-upload-dir",
+       value=util.Interpolate("public_html/clang-analyzer/%(prop:clang-id)s")))
+
+f.addStep(steps.Compile(command=["scan-build",
+                                "-analyze-headers",
+                                "-o",
+                                util.Interpolate("%(prop:clang-output-dir)s"),
+                                "make", "-j", "all"]))
+f.addStep(steps.DirectoryUpload(
+         slavesrc=util.Interpolate("%(prop:clang-output-dir)s"),
+         masterdest = util.Interpolate("%(prop:clang-upload-dir)s"),
+         compress = 'bz2',
+         name = "clang report",
+         url = util.Interpolate("%(prop:clang-uri)s"),
+))
+f.addStep(steps.RemoveDirectory(
+       dir=util.Interpolate("%(prop:clang-output-dir)s")
+))
+
+
+c['builders'].append(
+    util.BuilderConfig(name="clang-analyzer",
+      slavenames=list(w["bot"] for w in workers.values() if not w["vm"]),
+      factory=f))
+
+
+### RPM: check and build
+f = util.BuildFactory ()
+
+# check out the source
+f.addStep(steps.Git(repourl=quaggagit, mode='full'))
+f.addStep(steps.ShellCommand(command=["./update-autotools"],
+                                  description="run autotools",
+                                  descriptionDone="autotools"))
+f.addStep(steps.Configure())
+f.addStep(steps.ShellCommand(command=["make", "dist"],
+                                    description="run make dist",
+                                    descriptionDone="make dist"))
+# not imported somehow
+#f.addStep(steps.RpmLint(fileloc="redhat/quagga.spec"))
+f.addStep(steps.ShellCommand(command=["rpmlint", "-i", "redhat/quagga.spec"],
+                               description="run rpmlint",
+                               descriptionDone="rpmlint"))
+f.addStep(steps.RpmBuild(specfile="redhat/quagga.spec"))
+#                       rpmdir=util.Interpolate("%(prop:builddir)s/rpm")))
+
+# XXX: assuming 1:1 OS:buildbot mapping
+for kw in (kw for kw in workers if workers[kw]["pkg"] == "rpm"):
+       c['builders'].append(
+               util.BuilderConfig(name="rpm-" + kw,
+                                  slavenames="buildbot-" + kw,
+                                  factory=f
+               )
+       )
+
+### Build documentation
+
+def build_is_nightly (step):
+    n = step.getProperty("nightly")
+    if n == True or n == "True" or n == "true":
+       return True
+    return False
+
+f = util.BuildFactory ()
+f.addStep(steps.Git(repourl=quaggagit, mode='full'))
+f.addStep(steps.ShellCommand(command=["./update-autotools"],
+                                  description="run autotools",
+                                  descriptionDone="autotools"))
+f.addStep(steps.Configure(command=["../build/configure"],
+                         workdir="docs"))
+f.addStep(steps.ShellCommand(command=["make", "V=99", "quagga.html"],
+                            description="making split HTML doc",
+                            descriptionDone="docs: split HTML",
+                            workdir="docs/doc",
+                            haltOnFailure=True,
+))
+#f.addStep(steps.FileUpload(
+#        slavesrc="build/doc/fig-normal-processing.png",
+#        masterdest = "public_html/docs/nightly/quagga/",
+#        name = "Upload Fig 1",
+#        doStepIf=build_is_nightly,
+#))
+#f.addStep(steps.FileUpload(
+#        slavesrc="build/doc/fig-rs-processing.png",
+#        masterdest = "public_html/docs/nightly/quagga/",
+#        name = "Upload Fig 2",
+#        doStepIf=build_is_nightly,
+#))
+f.addStep(steps.MultipleFileUpload(
+         slavesrcs=[ "doc/fig-rs-processing.png",
+                     "doc/fig-normal-processing.png" ],
+         masterdest = "public_html/docs/nightly/quagga/",
+         name = "Upload Figures",
+         doStepIf=build_is_nightly,
+))
+f.addStep(steps.DirectoryUpload(
+         slavesrc="quagga.html",
+         masterdest = "public_html/docs/nightly/quagga",
+         compress = 'bz2',
+         name = "Upload split HTML",
+         url = "/docs/nightly/quagga/index.html",
+         workdir="docs/doc",
+         doStepIf=build_is_nightly,
+))
+f.addStep(steps.RemoveDirectory(
+         dir="docs/doc/quagga.html",
+))
+f.addStep(steps.ShellCommand(command=["make", "V=99",
+                                       "MAKEINFOFLAGS=--no-split",
+                                       "quagga.html"],
+                            description="making one-page HTML doc",
+                            descriptionDone="docs: one-page HTML",
+                            workdir="docs/doc",
+                            haltOnFailure=True
+))
+f.addStep(steps.FileUpload(
+         slavesrc="quagga.html",
+         masterdest = "public_html/docs/nightly/quagga/quagga.html",
+         name = "Upload single HTML",
+         url = "/docs/nightly/quagga/quagga.html",
+         workdir="docs/doc",
+         doStepIf=build_is_nightly,
+))
+f.addStep(steps.ShellCommand(command=["make", "V=99", "quagga.pdf"],
+                            description="making PDF docs",
+                            descriptionDone="docs: PDF",
+                            workdir="docs/doc"
+))
+f.addStep(steps.FileUpload(
+         slavesrc="quagga.pdf",
+         masterdest = "public_html/docs/nightly/quagga/quagga.pdf",
+         name = "Upload PDF",
+         url = "/docs/nightly/quagga/quagga.pdf",
+         workdir="docs/doc",
+         doStepIf=build_is_nightly,
+))
+
+c['builders'].append(
+    util.BuilderConfig(name="build-docs",
+      slavenames=[w["bot"] for w in workers.values() 
+                       if "texi" in w and w["texi"] == True ],
+      factory=f
+))
+
+### Co-ordination builds used to sequence parallel builds via Triggerable
+
+# to understand this you have to read this list and the Triggered schedulers
+# to see what sets of builds are being sequenced. Bit clunky, but Buildbot
+# doesn't have a way to just specify a pipeline of groups of builders more
+# cleanly.
+
+f = util.BuildFactory()
+f.addStep(steps.Trigger (
+       schedulerNames = [ "trigger-build-first" ],
+       waitForFinish=True,
+       updateSourceStamp=True
+))
+f.addStep(steps.Trigger (
+       schedulerNames = [ "trigger-build-rest" ],
+       waitForFinish=True,
+       updateSourceStamp=True
+))
+f.addStep(steps.Trigger (
+       schedulerNames = [ "trigger-build-analyses", "trigger-distcheck",
+                          "trigger-build-docs" ],
+       waitForFinish=True,
+       updateSourceStamp=True
+))
+f.addStep(steps.Trigger (
+       schedulerNames = [ "trigger-rpm" ],
+       waitForFinish=True,
+       updateSourceStamp=True
+))
+
+c['builders'].append(
+    util.BuilderConfig(name="commit-builder",
+      slavenames=[w["bot"] for w in workers.values() if not w["vm"]],
+      factory=f)
+)
+
+####### STATUS TARGETS
+
+# 'status' is a list of Status Targets. The results of each build will be
+# pushed to these targets. buildbot/status/*.py has a variety to choose from,
+# including web pages, email senders, and IRC bots.
+
+c['status'] = []
+
+from buildbot.status import html
+from buildbot.status.web import authz, auth
+
+authz_cfg=authz.Authz(
+    # change any of these to True to enable; see the manual for more
+    # options
+    #auth=auth.BasicAuth([("pyflakes","pyflakes")]),
+    auth=util.BasicAuth(users),
+    gracefulShutdown = False,
+    forceBuild = 'auth', # use this to test your slave once it is set up
+    forceAllBuilds = 'auth',  # ..or this
+    pingBuilder = 'auth',
+    stopBuild = 'auth',
+    stopAllBuilds = 'auth',
+    cancelPendingBuild = 'auth',
+    cancelAllPendingBuilds = 'auth',
+    pauseSlave = 'auth',    
+)
+c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
+
+c['status'].append(status.MailNotifier(
+       fromaddr="buildbot@quagga.net",
+       extraRecipients=["paul@jakma.org"],
+       sendToInterestedUsers=False,
+))
+
+c['status'].append (status.IRC(
+       "irc.freenode.net", "bb-quagga",
+       useColors=True,
+       channels=[{"channel": "#quagga"}],
+       notify_events={
+               'exception': 1,
+               'successToFailure': 1,
+               'failureToSuccess': 1,
+       },
+))
+
+####### PROJECT IDENTITY
+
+# the 'title' string will appear at the top of this buildbot
+# installation's html.WebStatus home page (linked to the
+# 'titleURL') and is embedded in the title of the waterfall HTML page.
+
+c['title'] = "Quagga"
+c['titleURL'] = "https://www.quagga.net/"
+
+# the 'buildbotURL' string should point to the location where the buildbot's
+# internal web server (usually the html.WebStatus page) is visible. This
+# typically uses the port number set in the Waterfall 'status' entry, but
+# with an externally-visible host name which the buildbot cannot figure out
+# without some help.
+
+c['buildbotURL'] = "http://buildbot.quagga.net/"
+
+####### DB URL
+
+c['db'] = {
+    # This specifies what database buildbot uses to store its state.  You can leave
+    # this at its default for all but the largest installations.
+    'db_url' : "sqlite:///state.sqlite",
+}
+
+#### debug
+c['debugPassword'] = debugPassword
diff --git a/infra/buildbot/master/pass.cfg b/infra/buildbot/master/pass.cfg
new file mode 100644 (file)
index 0000000..34a9340
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- python -*-
+# ex: set syntax=python:
+
+# example pass.cfg
+
+# privileged users for webui and try building
+#users = [
+#    ('foo', 'password123'),
+#]
+
+workers_pass = {
+          "fedora-24":  "aaaaaaa",
+          "centos-7":   "bbbbbbb",
+          "debian-8":   "ccccccc",
+          "debian-9":   "ddddddd",
+          "freebsd-10": "eeeeeee", 
+          "freebsd-11": "fffffff",
+}
+
+#### debug
+#debugPassword = "abcdefghijklmnopqrstuvwxyz"
diff --git a/infra/buildbot/worker/buildbot-slave.service b/infra/buildbot/worker/buildbot-slave.service
new file mode 100644 (file)
index 0000000..dcb136f
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Buildbot slave Daemon
+
+[Service]
+WorkingDirectory=/home/buildbot
+User=buildbot
+Group=buildbot
+ExecStart=/usr/bin/buildslave start --nodaemon
+ExecStop=/usr/bin/buildslave stop
+ExecReload=/usr/bin/buildslave reconfig
+
+[Install]
+WantedBy=multi-user.target
diff --git a/infra/buildbot/worker/buildbot-slave.xml b/infra/buildbot/worker/buildbot-slave.xml
new file mode 100644 (file)
index 0000000..d4177a7
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+        This file is part of Quagga.
+        The contents of this file are released to the public domain.
+-->
+
+<!--
+       Solaris/Illumos SMF manifest file for buildbot slave/worker.
+       
+       Usage:
+       
+       - edit occurances of 'slave' to 'worker', if needed. 
+       - install buildbot-slave or buildbot-worker:
+       
+               pip install buildbot-slave
+       
+       - create buildbot user and homedir, then:
+       
+               cd ~buildbot && buildslave . <buildbot-master> <name> <passw>
+       
+       - enable: svcadm enable buildbot-slave  
+-->
+<service_bundle type='manifest' name='buildbot-slave'>
+
+<service
+        name='application/buildbot-slave'
+        type='service'
+        version='1'>
+        
+        <create_default_instance enabled='true' />
+        
+        <single_instance />
+        
+        <dependency name='fslocal'
+               type='service'
+               grouping='require_all'
+               restart_on='none'>
+               <service_fmri value='svc:/system/filesystem/local:default' />
+        </dependency>
+        <dependency name='home'
+               type='service'
+               grouping='require_all'
+               restart_on='none'>
+               <service_fmri value='svc:/system/filesystem/autofs:default' />
+        </dependency>
+        
+        <dependency name='net'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri value='svc:/milestone/network:default' />
+       </dependency>
+       
+       <exec_method
+                       type='method'
+                       name='start'
+                       exec='/usr/bin/buildslave start'
+                       timeout_seconds='30'>
+                       <method_context>
+                               <method_credential user='buildbot' group='staff' />
+                       </method_context>
+               </exec_method>
+               
+               <exec_method
+                       type='method'
+                       name='stop'
+                       exec='/usr/bin/buildslave stop'
+                       timeout_seconds='60'>
+                       <method_context>
+                               <method_credential user='buildbot' group='staff' />
+                       </method_context>
+               </exec_method>
+
+               <exec_method
+                       type='method'
+                       name='refresh'
+                       exec='/usr/bin/buildslave restart'
+                       timeout_seconds='60'>
+                       <method_context>
+                               <method_credential user='buildbot' group='buildbot' />
+                       </method_context>
+               </exec_method>
+       <stability value='Unstable' />   
+</service>
+</service_bundle>
diff --git a/infra/patchwork/pass.py b/infra/patchwork/pass.py
new file mode 100644 (file)
index 0000000..6514232
--- /dev/null
@@ -0,0 +1,4 @@
+# example pass file
+SECRET_KEY = "aaaaaaaaaaaaaaaaaa"
+EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
+DATABASES['default']['PASSWORD'] = os.environ.get('DATABASE_PASSWORD', 'bbbbbbbbbbbbbbbbbbbbb');
diff --git a/infra/patchwork/production.py b/infra/patchwork/production.py
new file mode 100644 (file)
index 0000000..1683c0c
--- /dev/null
@@ -0,0 +1,89 @@
+"""
+Sample production-ready settings for patchwork project.
+
+Most of these are commented out as they will be installation dependent.
+
+Design based on:
+    http://www.revsys.com/blog/2014/nov/21/recommended-django-project-layout/
+"""
+
+from __future__ import absolute_import
+
+import os
+
+import django
+
+from .base import *  # noqa
+
+DEBUG = True
+#
+# Core settings
+# https://docs.djangoproject.com/en/1.8/ref/settings/#core-settings
+#
+
+# Security
+#
+# You'll need to replace this to a random string. The following python code can
+# be used to generate a secret key:
+#
+#      import string, random
+#      chars = string.letters + string.digits + string.punctuation
+#      print repr("".join([random.choice(chars) for i in range(0,50)]))
+
+# Email
+#
+# Replace this with your own details
+
+EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost')
+EMAIL_PORT = os.getenv('EMAIL_PORT', 25)
+EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '')
+# password goes in pass.py
+EMAIL_USE_TLS = True
+
+DEFAULT_FROM_EMAIL = 'Patchwork <patchwork@patchwork.quagga.net>'
+SERVER_EMAIL = DEFAULT_FROM_EMAIL
+NOTIFICATION_FROM_EMAIL = DEFAULT_FROM_EMAIL
+
+ADMINS = (
+    ('Paul Jakma', 'paul@quagga.net'),
+)
+
+# Database
+#
+# If you're using a postgres database, connecting over a local unix-domain
+# socket, then the following setting should work for you. Otherwise,
+# see https://docs.djangoproject.com/en/1.8/ref/settings/#databases
+
+# password goes in pass.py
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': os.environ.get('DATABASE_NAME', 'patchwork'),
+        'USER': os.environ.get('DATABASE_USER', 'patchwork'),
+         'HOST': '127.0.0.1',
+    },
+}
+
+
+#
+# Static files settings
+# https://docs.djangoproject.com/en/1.8/ref/settings/#static-files
+# https://docs.djangoproject.com/en/1.8/ref/contrib/staticfiles/#manifeststaticfilesstorage
+#
+
+STATIC_ROOT = os.environ.get('STATIC_ROOT', '/home/patchwork/htdocs/static')
+
+
+if django.VERSION >= (1, 7):
+    STATICFILES_STORAGE = \
+        'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
+
+ENABLE_XMLRPC = True
+
+LANGUAGE_CODE='en-gb'
+TIME_ZONE='GMT'
+ALLOWED_HOSTS=['patchwork.quagga.net','testpw.quagga.net', 'http', 'http.quagga.net', '*.quagga.net']
+
+with open("patchwork/settings/pass.py") as f:
+    code = compile(f.read(), "patchwork/settings/pass.py", 'exec')
+    exec(code)
diff --git a/infra/patchwork/systemd/patchwork-delivery.socket b/infra/patchwork/systemd/patchwork-delivery.socket
new file mode 100644 (file)
index 0000000..bfa6752
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Patchwork unix pipe to accept list mail delivery on
+
+[Socket]
+#ListenStream=/tmp/patchwork.sock
+ListenStream=127.0.0.1:8001
+SocketUser=patchwork
+SocketGroup=patchwork
+Accept=yes
+
+#ListenFIFO=/tmp/patchwork-fifo.sock
+
+
+[Install]
+WantedBy=sockets.target
diff --git a/infra/patchwork/systemd/patchwork-delivery@.service b/infra/patchwork/systemd/patchwork-delivery@.service
new file mode 100644 (file)
index 0000000..d152b2d
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Patchwork list mail socket processing script
+
+[Service]
+EnvironmentFile=/home/patchwork/patchwork.env 
+ExecStart=-/home/patchwork/patchwork/patchwork/bin/parsemail.sh
+StandardInput=socket
+StandardOutput=inherit
+StandardError=journal
+User=patchwork
+Group=patchwork
+
+[Install]
+WantedBy=multi-user.target
+Also=patchwork-delivery.socket
diff --git a/infra/patchwork/systemd/patchwork.service b/infra/patchwork/systemd/patchwork.service
new file mode 100644 (file)
index 0000000..90ec4bb
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Patchwork Daemon
+
+[Service]
+WorkingDirectory=/home/patchwork/patchwork
+User=patchwork
+Group=patchwork
+EnvironmentFile=/home/patchwork/patchwork.env 
+ExecStart=/usr/bin/python3 manage.py runserver 0.0.0.0:8000
+
+[Install]
+WantedBy=multi-user.target
diff --git a/init/.gitignore b/init/.gitignore
new file mode 100644 (file)
index 0000000..30b4bc9
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.nfs*
+*~
+*.loT
+
diff --git a/isisd/.gitignore b/isisd/.gitignore
new file mode 100644 (file)
index 0000000..5e8028c
--- /dev/null
@@ -0,0 +1,15 @@
+Makefile
+Makefile.in
+*.o
+isisd
+.deps
+isisd.conf
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+*.a
diff --git a/isisd/AUTHORS b/isisd/AUTHORS
new file mode 100644 (file)
index 0000000..80b3a28
--- /dev/null
@@ -0,0 +1,5 @@
+Sampo Saaristo   <sambo@cs.tut.fi>
+Ofer Wald        <ofersf@islands.co.il>
+Hannes Gredler   <hannes@gredler.at>
+Subbaiah Venkata <svenkata@google.com>
+Olivier Dugeon   <olivier.dugeon@orange.com>
diff --git a/isisd/Makefile.am b/isisd/Makefile.am
new file mode 100644 (file)
index 0000000..bfe2e94
--- /dev/null
@@ -0,0 +1,38 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \
+          @ISIS_TOPOLOGY_INCLUDES@
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+LIBS = @LIBS@ 
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = libisis.a
+sbin_PROGRAMS = isisd 
+SUBDIRS = topology
+
+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_zebra.c isis_dr.c \
+       isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \
+       isis_spf.c isis_redist.c isis_route.c isis_routemap.c isis_te.c \
+       isis_vty.c
+
+
+noinst_HEADERS = \
+       isisd.h isis_pdu.h isis_tlv.h isis_adjacency.h isis_constants.h \
+       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_redist.h \
+       isis_route.h isis_routemap.h isis_te.h \
+       include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h
+
+isisd_SOURCES = \
+       isis_main.c $(libisis_a_SOURCES) \
+       isis_bpf.c isis_dlpi.c isis_pfpacket.c
+
+isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = isisd.conf.sample
diff --git a/isisd/README b/isisd/README
new file mode 100644 (file)
index 0000000..4f13ff6
--- /dev/null
@@ -0,0 +1,3 @@
+Constraints
+
+  o Maximum number of interfaces 255
diff --git a/isisd/dict.c b/isisd/dict.c
new file mode 100644 (file)
index 0000000..bbcb421
--- /dev/null
@@ -0,0 +1,1485 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ */
+
+#include "zebra.h"
+#include "zassert.h"
+#include "memory.h"
+#include "dict.h"
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a 
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define nodecount dict_nodecount
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define allocnode dict_allocnode
+#define freenode dict_freenode
+#define context dict_context
+#define dupes dict_dupes
+
+#define dictptr dict_dictptr
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+#define DICT_DEPTH_MAX 64
+
+static dnode_t *dnode_alloc(void *context);
+static void dnode_free(dnode_t *node, void *context);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree.  The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C.   The left subtree of C is inherited as the new right subtree
+ * for P.  The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+    dnode_t *lower, *lowleft, *upparent;
+
+    lower = upper->right;
+    upper->right = lowleft = lower->left;
+    lowleft->parent = upper;
+
+    lower->parent = upparent = upper->parent;
+
+    /* don't need to check for root node here because root->parent is
+       the sentinel nil node, and root->parent->left points back to root */
+
+    if (upper == upparent->left) {
+       upparent->left = lower;
+    } else {
+       assert (upper == upparent->right);
+       upparent->right = lower;
+    }
+
+    lower->left = upper;
+    upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+    dnode_t *lower, *lowright, *upparent;
+
+    lower = upper->left;
+    upper->left = lowright = lower->right;
+    lowright->parent = upper;
+
+    lower->parent = upparent = upper->parent;
+
+    if (upper == upparent->right) {
+       upparent->right = lower;
+    } else {
+       assert (upper == upparent->left);
+       upparent->left = lower;
+    }
+
+    lower->right = upper;
+    upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it.  Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+    if (node == nil)
+       return;
+    free_nodes(dict, node->left, nil);
+    free_nodes(dict, node->right, nil);
+    dict->freenode(node, dict->context);
+}
+
+/*
+ * This procedure performs a verification that the given subtree is a binary
+ * search tree. It performs an inorder traversal of the tree using the
+ * dict_next() successor function, verifying that the key of each node is
+ * strictly lower than that of its successor, if duplicates are not allowed,
+ * or lower or equal if duplicates are allowed.  This function is used for
+ * debugging purposes. 
+ */
+
+static int verify_bintree(dict_t *dict)
+{
+    dnode_t *first, *next;
+
+    first = dict_first(dict);
+
+    if (dict->dupes) {
+       while (first && (next = dict_next(dict, first))) {
+           if (dict->compare(first->key, next->key) > 0)
+               return 0;
+           first = next;
+       }
+    } else {
+       while (first && (next = dict_next(dict, first))) {
+           if (dict->compare(first->key, next->key) >= 0)
+               return 0;
+           first = next;
+       }
+    }
+    return 1;
+}
+
+
+/*
+ * This function recursively verifies that the given binary subtree satisfies
+ * three of the red black properties. It checks that every red node has only
+ * black children. It makes sure that each node is either red or black. And it
+ * checks that every path has the same count of black nodes from root to leaf.
+ * It returns the blackheight of the given subtree; this allows blackheights to
+ * be computed recursively and compared for left and right siblings for
+ * mismatches. It does not check for every nil node being black, because there
+ * is only one sentinel nil node. The return value of this function is the
+ * black height of the subtree rooted at the node ``root'', or zero if the
+ * subtree is not red-black.
+ */
+
+static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
+{
+    unsigned height_left, height_right;
+
+    if (root != nil) {
+       height_left = verify_redblack(nil, root->left);
+       height_right = verify_redblack(nil, root->right);
+       if (height_left == 0 || height_right == 0)
+           return 0;
+       if (height_left != height_right)
+           return 0;
+       if (root->color == dnode_red) {
+           if (root->left->color != dnode_black)
+               return 0;
+           if (root->right->color != dnode_black)
+               return 0;
+           return height_left;
+       }
+       if (root->color != dnode_black)
+           return 0;
+       return height_left + 1;
+    } 
+    return 1;
+}
+
+/*
+ * Compute the actual count of nodes by traversing the tree and
+ * return it. This could be compared against the stored count to
+ * detect a mismatch.
+ */
+
+static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
+{
+    if (root == nil)
+       return 0;
+    else
+       return 1 + verify_node_count(nil, root->left)
+           + verify_node_count(nil, root->right);
+}
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+    if (root != nil) {
+       return root == node
+               || verify_dict_has_node(nil, root->left, node)
+               || verify_dict_has_node(nil, root->right, node);
+    }
+    return 0;
+}
+
+
+/*
+ * Dynamically allocate and initialize a dictionary object.
+ */
+
+dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
+{
+    dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t));
+
+    if (new) {
+       new->compare = comp;
+       new->allocnode = dnode_alloc;
+       new->freenode = dnode_free;
+       new->context = NULL;
+       new->nodecount = 0;
+       new->maxcount = maxcount;
+       new->nilnode.left = &new->nilnode;
+       new->nilnode.right = &new->nilnode;
+       new->nilnode.parent = &new->nilnode;
+       new->nilnode.color = dnode_black;
+       new->dupes = 0;
+    }
+    return new;
+}
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
+       dnode_free_t fr, void *context)
+{
+    assert (dict_count(dict) == 0);
+    assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
+
+    dict->allocnode = al ? al : dnode_alloc;
+    dict->freenode = fr ? fr : dnode_free;
+    dict->context = context;
+}
+
+/*
+ * Free a dynamically allocated dictionary object. Removing the nodes
+ * from the tree before deleting it is required.
+ */
+
+void dict_destroy(dict_t *dict)
+{
+    assert (dict_isempty(dict));
+    XFREE(MTYPE_ISIS_DICT, dict);
+}
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+void dict_free_nodes(dict_t *dict)
+{
+    dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+    free_nodes(dict, root, nil);
+    dict->nodecount = 0;
+    dict->nilnode.left = &dict->nilnode;
+    dict->nilnode.right = &dict->nilnode;
+}
+
+/*
+ * Obsolescent function, equivalent to dict_free_nodes
+ */
+
+void dict_free(dict_t *dict)
+{
+    dict_free_nodes(dict);
+}
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+    dict->compare = comp;
+    dict->allocnode = dnode_alloc;
+    dict->freenode = dnode_free;
+    dict->context = NULL;
+    dict->nodecount = 0;
+    dict->maxcount = maxcount;
+    dict->nilnode.left = &dict->nilnode;
+    dict->nilnode.right = &dict->nilnode;
+    dict->nilnode.parent = &dict->nilnode;
+    dict->nilnode.color = dnode_black;
+    dict->dupes = 0;
+    return dict;
+}
+
+/* 
+ * Initialize a dictionary in the likeness of another dictionary
+ */
+
+void dict_init_like(dict_t *dict, const dict_t *template)
+{
+    dict->compare = template->compare;
+    dict->allocnode = template->allocnode;
+    dict->freenode = template->freenode;
+    dict->context = template->context;
+    dict->nodecount = 0;
+    dict->maxcount = template->maxcount;
+    dict->nilnode.left = &dict->nilnode;
+    dict->nilnode.right = &dict->nilnode;
+    dict->nilnode.parent = &dict->nilnode;
+    dict->nilnode.color = dnode_black;
+    dict->dupes = template->dupes;
+
+    assert (dict_similar(dict, template));
+}
+
+/*
+ * Remove all nodes from the dictionary (without freeing them in any way).
+ */
+
+static void dict_clear(dict_t *dict)
+{
+    dict->nodecount = 0;
+    dict->nilnode.left = &dict->nilnode;
+    dict->nilnode.right = &dict->nilnode;
+    dict->nilnode.parent = &dict->nilnode;
+    assert (dict->nilnode.color == dnode_black);
+}
+
+
+/*
+ * Verify the integrity of the dictionary structure.  This is provided for
+ * debugging purposes, and should be placed in assert statements.   Just because
+ * this function succeeds doesn't mean that the tree is not corrupt. Certain
+ * corruptions in the tree may simply cause undefined behavior.
+ */ 
+
+int dict_verify(dict_t *dict)
+{
+    dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+
+    /* check that the sentinel node and root node are black */
+    if (root->color != dnode_black)
+       return 0;
+    if (nil->color != dnode_black)
+       return 0;
+    if (nil->right != nil)
+       return 0;
+    /* nil->left is the root node; check that its parent pointer is nil */
+    if (nil->left->parent != nil)
+       return 0;
+    /* perform a weak test that the tree is a binary search tree */
+    if (!verify_bintree(dict))
+       return 0;
+    /* verify that the tree is a red-black tree */
+    if (!verify_redblack(nil, root))
+       return 0;
+    if (verify_node_count(nil, root) != dict_count(dict))
+       return 0;
+    return 1;
+}
+
+/*
+ * Determine whether two dictionaries are similar: have the same comparison and
+ * allocator functions, and same status as to whether duplicates are allowed.
+ */
+
+int dict_similar(const dict_t *left, const dict_t *right)
+{
+    if (left->compare != right->compare)
+       return 0;
+
+    if (left->allocnode != right->allocnode)
+       return 0;
+
+    if (left->freenode != right->freenode)
+       return 0;
+
+    if (left->context != right->context)
+       return 0;
+
+    if (left->dupes != right->dupes)
+       return 0;
+
+    return 1;
+}
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than 
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+    dnode_t *root = dict_root(dict);
+    dnode_t *nil = dict_nil(dict);
+    dnode_t *saved;
+    int result;
+
+    /* simple binary search adapted for trees that contain duplicate keys */
+
+    while (root != nil) {
+       result = dict->compare(key, root->key);
+       if (result < 0)
+           root = root->left;
+       else if (result > 0)
+           root = root->right;
+       else {
+           if (!dict->dupes) { /* no duplicates, return match          */
+               return root;
+           } else {            /* could be dupes, find leftmost one    */
+               do {
+                   saved = root;
+                   root = root->left;
+                   while (root != nil && dict->compare(key, root->key))
+                       root = root->right;
+               } while (root != nil);
+               return saved;
+           }
+       }
+    }
+
+    return NULL;
+}
+
+/*
+ * Look for the node corresponding to the lowest key that is equal to or
+ * greater than the given key.  If there is no such node, return null.
+ */
+
+dnode_t *dict_lower_bound(dict_t *dict, const void *key)
+{
+    dnode_t *root = dict_root(dict);
+    dnode_t *nil = dict_nil(dict);
+    dnode_t *tentative = 0;
+
+    while (root != nil) {
+       int result = dict->compare(key, root->key);
+
+       if (result > 0) {
+           root = root->right;
+       } else if (result < 0) {
+           tentative = root;
+           root = root->left;
+       } else {
+           if (!dict->dupes) {
+               return root;
+           } else {
+               tentative = root;
+               root = root->left;
+           }
+       } 
+    }
+    
+    return tentative;
+}
+
+/*
+ * Look for the node corresponding to the greatest key that is equal to or
+ * lower than the given key.  If there is no such node, return null.
+ */
+
+dnode_t *dict_upper_bound(dict_t *dict, const void *key)
+{
+    dnode_t *root = dict_root(dict);
+    dnode_t *nil = dict_nil(dict);
+    dnode_t *tentative = 0;
+
+    while (root != nil) {
+       int result = dict->compare(key, root->key);
+
+       if (result < 0) {
+           root = root->left;
+       } else if (result > 0) {
+           tentative = root;
+           root = root->right;
+       } else {
+           if (!dict->dupes) {
+               return root;
+           } else {
+               tentative = root;
+               root = root->right;
+           }
+       } 
+    }
+    
+    return tentative;
+}
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+    dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+    dnode_t *parent = nil, *uncle, *grandpa;
+    int result = -1;
+
+    node->key = key;
+
+    assert (!dict_isfull(dict));
+    assert (!dict_contains(dict, node));
+    assert (!dnode_is_in_a_dict(node));
+
+    /* basic binary tree insert */
+
+    while (where != nil) {
+       parent = where;
+       result = dict->compare(key, where->key);
+       /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+       assert (dict->dupes || result != 0);
+       if (result < 0)
+           where = where->left;
+       else
+           where = where->right;
+    }
+
+    assert (where == nil);
+
+    if (result < 0)
+       parent->left = node;
+    else
+       parent->right = node;
+
+    node->parent = parent;
+    node->left = nil;
+    node->right = nil;
+
+    dict->nodecount++;
+
+    /* red black adjustments */
+
+    node->color = dnode_red;
+
+    while (parent->color == dnode_red) {
+       grandpa = parent->parent;
+       if (parent == grandpa->left) {
+           uncle = grandpa->right;
+           if (uncle->color == dnode_red) {    /* red parent, red uncle */
+               parent->color = dnode_black;
+               uncle->color = dnode_black;
+               grandpa->color = dnode_red;
+               node = grandpa;
+               parent = grandpa->parent;
+           } else {                            /* red parent, black uncle */
+               if (node == parent->right) {
+                   rotate_left(parent);
+                   parent = node;
+                   assert (grandpa == parent->parent);
+                   /* rotation between parent and child preserves grandpa */
+               }
+               parent->color = dnode_black;
+               grandpa->color = dnode_red;
+               rotate_right(grandpa);
+               break;
+           }
+       } else {        /* symmetric cases: parent == parent->parent->right */
+           uncle = grandpa->left;
+           if (uncle->color == dnode_red) {
+               parent->color = dnode_black;
+               uncle->color = dnode_black;
+               grandpa->color = dnode_red;
+               node = grandpa;
+               parent = grandpa->parent;
+           } else {
+               if (node == parent->left) {
+                   rotate_right(parent);
+                   parent = node;
+                   assert (grandpa == parent->parent);
+               }
+               parent->color = dnode_black;
+               grandpa->color = dnode_red;
+               rotate_left(grandpa);
+               break;
+           }
+       }
+    }
+
+    dict_root(dict)->color = dnode_black;
+
+    assert (dict_verify(dict));
+}
+
+/*
+ * Delete the given node from the dictionary. If the given node does not belong
+ * to the given dictionary, undefined behavior results.  A pointer to the
+ * deleted node is returned.
+ */
+
+dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
+{
+    dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
+
+    /* basic deletion */
+
+    assert (!dict_isempty(dict));
+    assert (dict_contains(dict, delete));
+
+    /*
+     * If the node being deleted has two children, then we replace it with its
+     * successor (i.e. the leftmost node in the right subtree.) By doing this,
+     * we avoid the traditional algorithm under which the successor's key and
+     * value *only* move to the deleted node and the successor is spliced out
+     * from the tree. We cannot use this approach because the user may hold
+     * pointers to the successor, or nodes may be inextricably tied to some
+     * other structures by way of embedding, etc. So we must splice out the
+     * node we are given, not some other node, and must not move contents from
+     * one node to another behind the user's back.
+     */
+
+    if (delete->left != nil && delete->right != nil) {
+       dnode_t *next = dict_next(dict, delete);
+       dnode_t *nextparent = next->parent;
+       dnode_color_t nextcolor = next->color;
+
+       assert (next != nil);
+       assert (next->parent != nil);
+       assert (next->left == nil);
+
+       /*
+        * First, splice out the successor from the tree completely, by
+        * moving up its right child into its place.
+        */
+
+       child = next->right;
+       child->parent = nextparent;
+
+       if (nextparent->left == next) {
+           nextparent->left = child;
+       } else {
+           assert (nextparent->right == next);
+           nextparent->right = child;
+       }
+
+       /*
+        * Now that the successor has been extricated from the tree, install it
+        * in place of the node that we want deleted.
+        */
+
+       next->parent = delparent;
+       next->left = delete->left;
+       next->right = delete->right;
+       next->left->parent = next;
+       next->right->parent = next;
+       next->color = delete->color;
+       delete->color = nextcolor;
+
+       if (delparent->left == delete) {
+           delparent->left = next;
+       } else {
+           assert (delparent->right == delete);
+           delparent->right = next;
+       }
+
+    } else {
+       assert (delete != nil);
+       assert (delete->left == nil || delete->right == nil);
+
+       child = (delete->left != nil) ? delete->left : delete->right;
+
+       child->parent = delparent = delete->parent;         
+
+       if (delete == delparent->left) {
+           delparent->left = child;    
+       } else {
+           assert (delete == delparent->right);
+           delparent->right = child;
+       }
+    }
+
+    delete->parent = NULL;
+    delete->right = NULL;
+    delete->left = NULL;
+
+    dict->nodecount--;
+
+    assert (verify_bintree(dict));
+
+    /* red-black adjustments */
+
+    if (delete->color == dnode_black) {
+       dnode_t *parent, *sister;
+
+       dict_root(dict)->color = dnode_red;
+
+       while (child->color == dnode_black) {
+           parent = child->parent;
+           if (child == parent->left) {
+               sister = parent->right;
+               assert (sister != nil);
+               if (sister->color == dnode_red) {
+                   sister->color = dnode_black;
+                   parent->color = dnode_red;
+                   rotate_left(parent);
+                   sister = parent->right;
+                   assert (sister != nil);
+               }
+               if (sister->left->color == dnode_black
+                       && sister->right->color == dnode_black) {
+                   sister->color = dnode_red;
+                   child = parent;
+               } else {
+                   if (sister->right->color == dnode_black) {
+                       assert (sister->left->color == dnode_red);
+                       sister->left->color = dnode_black;
+                       sister->color = dnode_red;
+                       rotate_right(sister);
+                       sister = parent->right;
+                       assert (sister != nil);
+                   }
+                   sister->color = parent->color;
+                   sister->right->color = dnode_black;
+                   parent->color = dnode_black;
+                   rotate_left(parent);
+                   break;
+               }
+           } else {    /* symmetric case: child == child->parent->right */
+               assert (child == parent->right);
+               sister = parent->left;
+               assert (sister != nil);
+               if (sister->color == dnode_red) {
+                   sister->color = dnode_black;
+                   parent->color = dnode_red;
+                   rotate_right(parent);
+                   sister = parent->left;
+                   assert (sister != nil);
+               }
+               if (sister->right->color == dnode_black
+                       && sister->left->color == dnode_black) {
+                   sister->color = dnode_red;
+                   child = parent;
+               } else {
+                   if (sister->left->color == dnode_black) {
+                       assert (sister->right->color == dnode_red);
+                       sister->right->color = dnode_black;
+                       sister->color = dnode_red;
+                       rotate_left(sister);
+                       sister = parent->left;
+                       assert (sister != nil);
+                   }
+                   sister->color = parent->color;
+                   sister->left->color = dnode_black;
+                   parent->color = dnode_black;
+                   rotate_right(parent);
+                   break;
+               }
+           }
+       }
+
+       child->color = dnode_black;
+       dict_root(dict)->color = dnode_black;
+    }
+
+    assert (dict_verify(dict));
+
+    return delete;
+}
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+    dnode_t *node = dict->allocnode (dict->context);
+
+    if (node) {
+       dnode_init(node, data);
+       dict_insert(dict, node, key);
+       return 1;
+    }
+    return 0;
+}
+
+void dict_delete_free(dict_t *dict, dnode_t *node)
+{
+    dict_delete(dict, node);
+    dict->freenode(node, dict->context);
+}
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_first(dict_t *dict)
+{
+    dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+    if (root != nil)
+       while ((left = root->left) != nil)
+           root = left;
+
+    return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the node with the highest (rightmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_last(dict_t *dict)
+{
+    dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
+
+    if (root != nil)
+       while ((right = root->right) != nil)
+           root = right;
+
+    return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+
+dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+    dnode_t *nil = dict_nil(dict), *parent, *left;
+
+    if (curr->right != nil) {
+       curr = curr->right;
+       while ((left = curr->left) != nil)
+           curr = left;
+       return curr;
+    }
+
+    parent = curr->parent;
+
+    while (parent != nil && curr == parent->right) {
+       curr = parent;
+       parent = curr->parent;
+    }
+
+    return (parent == nil) ? NULL : parent;
+}
+
+/*
+ * Return the given node's predecessor, in the key order.
+ * The nil sentinel node is returned if there is no predecessor.
+ */
+
+dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
+{
+    dnode_t *nil = dict_nil(dict), *parent, *right;
+
+    if (curr->left != nil) {
+       curr = curr->left;
+       while ((right = curr->right) != nil)
+           curr = right;
+       return curr;
+    }
+
+    parent = curr->parent;
+
+    while (parent != nil && curr == parent->left) {
+       curr = parent;
+       parent = curr->parent;
+    }
+
+    return (parent == nil) ? NULL : parent;
+}
+
+void dict_allow_dupes(dict_t *dict)
+{
+    dict->dupes = 1;
+}
+
+#undef dict_count
+#undef dict_isempty
+#undef dict_isfull
+#undef dnode_get
+#undef dnode_put
+#undef dnode_getkey
+
+dictcount_t dict_count(dict_t *dict)
+{
+    return dict->nodecount;
+}
+
+int dict_isempty(dict_t *dict)
+{
+    return dict->nodecount == 0;
+}
+
+int dict_isfull(dict_t *dict)
+{
+    return dict->nodecount == dict->maxcount;
+}
+
+int dict_contains(dict_t *dict, dnode_t *node)
+{
+    return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
+}
+
+static dnode_t *dnode_alloc(void *context)
+{
+    return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t));
+}
+
+static void dnode_free(dnode_t *node, void *context)
+{
+    XFREE(MTYPE_ISIS_DICT_NODE, node);
+}
+
+dnode_t *dnode_create(void *data)
+{
+    dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t));
+    if (new) {
+       new->data = data;
+       new->parent = NULL;
+       new->left = NULL;
+       new->right = NULL;
+    }
+    return new;
+}
+
+dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+    dnode->data = data;
+    dnode->parent = NULL;
+    dnode->left = NULL;
+    dnode->right = NULL;
+    return dnode;
+}
+
+void dnode_destroy(dnode_t *dnode)
+{
+    assert (!dnode_is_in_a_dict(dnode));
+    XFREE(MTYPE_ISIS_DICT_NODE, dnode);
+}
+
+void *dnode_get(dnode_t *dnode)
+{
+    return dnode->data;
+}
+
+const void *dnode_getkey(dnode_t *dnode)
+{
+    return dnode->key;
+}
+
+void dnode_put(dnode_t *dnode, void *data)
+{
+    dnode->data = data;
+}
+
+int dnode_is_in_a_dict(dnode_t *dnode)
+{
+    return (dnode->parent && dnode->left && dnode->right);
+}
+
+void dict_process(dict_t *dict, void *context, dnode_process_t function)
+{
+    dnode_t *node = dict_first(dict), *next;
+
+    while (node != NULL) {
+       /* check for callback function deleting */
+       /* the next node from under us          */
+       assert (dict_contains(dict, node));
+       next = dict_next(dict, node);
+       function(dict, node, context);
+       node = next;
+    }
+}
+
+static void load_begin_internal(dict_load_t *load, dict_t *dict)
+{
+    load->dictptr = dict;
+    load->nilnode.left = &load->nilnode;
+    load->nilnode.right = &load->nilnode;
+}
+
+void dict_load_begin(dict_load_t *load, dict_t *dict)
+{
+    assert (dict_isempty(dict));
+    load_begin_internal(load, dict);
+}
+
+void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
+{
+    dict_t *dict = load->dictptr;
+    dnode_t *nil = &load->nilnode;
+   
+    assert (!dnode_is_in_a_dict(newnode));
+    assert (dict->nodecount < dict->maxcount);
+
+    #ifndef NDEBUG
+    if (dict->nodecount > 0) {
+       if (dict->dupes)
+           assert (dict->compare(nil->left->key, key) <= 0);
+       else
+           assert (dict->compare(nil->left->key, key) < 0);
+    }
+    #endif
+
+    newnode->key = key;
+    nil->right->left = newnode;
+    nil->right = newnode;
+    newnode->left = nil;
+    dict->nodecount++;
+}
+
+void dict_load_end(dict_load_t *load)
+{
+    dict_t *dict = load->dictptr;
+    dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
+    dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
+    dnode_t *complete = 0;
+    dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
+    dictcount_t botrowcount;
+    unsigned baselevel = 0, level = 0, i;
+
+    assert (dnode_red == 0 && dnode_black == 1);
+
+    while (fullcount >= nodecount && fullcount)
+       fullcount >>= 1;
+
+    botrowcount = nodecount - fullcount;
+
+    for (curr = loadnil->left; curr != loadnil; curr = next) {
+       next = curr->left;
+
+       if (complete == NULL && botrowcount-- == 0) {
+           assert (baselevel == 0);
+           assert (level == 0);
+           baselevel = level = 1;
+           complete = tree[0];
+
+           if (complete != 0) {
+               tree[0] = 0;
+               complete->right = dictnil;
+               while (tree[level] != 0) {
+                   tree[level]->right = complete;
+                   complete->parent = tree[level];
+                   complete = tree[level];
+                   tree[level++] = 0;
+               }
+           }
+       }
+
+       if (complete == NULL) {
+           curr->left = dictnil;
+           curr->right = dictnil;
+           curr->color = level % 2;
+           complete = curr;
+
+           assert (level == baselevel);
+           while (tree[level] != 0) {
+               tree[level]->right = complete;
+               complete->parent = tree[level];
+               complete = tree[level];
+               tree[level++] = 0;
+           }
+       } else {
+           curr->left = complete;
+           curr->color = (level + 1) % 2;
+           complete->parent = curr;
+           tree[level] = curr;
+           complete = 0;
+           level = baselevel;
+       }
+    }
+
+    if (complete == NULL)
+       complete = dictnil;
+
+    for (i = 0; i < DICT_DEPTH_MAX; i++) {
+       if (tree[i] != 0) {
+           tree[i]->right = complete;
+           complete->parent = tree[i];
+           complete = tree[i];
+       }
+    }
+
+    dictnil->color = dnode_black;
+    dictnil->right = dictnil;
+    complete->parent = dictnil;
+    complete->color = dnode_black;
+    dict_root(dict) = complete;
+
+    assert (dict_verify(dict));
+}
+
+void dict_merge(dict_t *dest, dict_t *source)
+{
+    dict_load_t load;
+    dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
+
+    assert (dict_similar(dest, source));       
+
+    if (source == dest)
+       return;
+
+    dest->nodecount = 0;
+    load_begin_internal(&load, dest);
+
+    for (;;) {
+       if (leftnode != NULL && rightnode != NULL) {
+           if (dest->compare(leftnode->key, rightnode->key) < 0)
+               goto copyleft;
+           else
+               goto copyright;
+       } else if (leftnode != NULL) {
+           goto copyleft;
+       } else if (rightnode != NULL) {
+           goto copyright;
+       } else {
+           assert (leftnode == NULL && rightnode == NULL);
+           break;
+       }
+
+    copyleft:
+       {
+           dnode_t *next = dict_next(dest, leftnode);
+           #ifndef NDEBUG
+           leftnode->left = NULL;      /* suppress assertion in dict_load_next */
+           #endif
+           dict_load_next(&load, leftnode, leftnode->key);
+           leftnode = next;
+           continue;
+       }
+       
+    copyright:
+       {
+           dnode_t *next = dict_next(source, rightnode);
+           #ifndef NDEBUG
+           rightnode->left = NULL;
+           #endif
+           dict_load_next(&load, rightnode, rightnode->key);
+           rightnode = next;
+           continue;
+       }
+    }
+
+    dict_clear(source);
+    dict_load_end(&load);
+}
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+    char **tokptr; 
+    va_list arglist;
+    int tokcount = 0;
+
+    va_start(arglist, string);
+    tokptr = va_arg(arglist, char **);
+    while (tokptr) {
+       while (*string && isspace((unsigned char) *string))
+           string++;
+       if (!*string)
+           break;
+       *tokptr = string;
+       while (*string && !isspace((unsigned char) *string))
+           string++;
+       tokptr = va_arg(arglist, char **);
+       tokcount++;
+       if (!*string)
+           break;
+       *string++ = 0;
+    }
+    va_end(arglist);
+
+    return tokcount;
+}
+
+static int comparef(const void *key1, const void *key2)
+{
+    return strcmp(key1, key2);
+}
+
+static char *dupstring(char *str)
+{
+    int sz = strlen(str) + 1;
+    char *new = XCALLOC(MTYPE_ISIS_TMP, sz);
+    if (new)
+       memcpy(new, str, sz);
+    return new;
+}
+
+static dnode_t *new_node(void *c)
+{
+    static dnode_t few[5];
+    static int count;
+
+    if (count < 5)
+       return few + count++;
+
+    return NULL;
+}
+
+static void del_node(dnode_t *n, void *c)
+{
+}
+
+static int prompt = 0;
+
+static void construct(dict_t *d)
+{
+    input_t in;
+    int done = 0;
+    dict_load_t dl;
+    dnode_t *dn;
+    char *tok1, *tok2, *val;
+    const char *key;
+    char *help = 
+       "p                      turn prompt on\n"
+       "q                      finish construction\n"
+       "a <key> <val>          add new entry\n";
+
+    if (!dict_isempty(d))
+       puts("warning: dictionary not empty!");
+
+    dict_load_begin(&dl, d);
+
+    while (!done) {
+       if (prompt)
+           putchar('>');
+       fflush(stdout);
+
+       if (!fgets(in, sizeof(input_t), stdin))
+           break;
+
+       switch (in[0]) {
+           case '?':
+               puts(help);
+               break;
+           case 'p':
+               prompt = 1;
+               break;
+           case 'q':
+               done = 1;
+               break;
+           case 'a':
+               if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+                   puts("what?");
+                   break;
+               }
+               key = dupstring(tok1);
+               val = dupstring(tok2);
+               dn = dnode_create(val);
+
+               if (!key || !val || !dn) {
+                   puts("out of memory");
+                   free((void *) key);
+                   free(val);
+                   if (dn)
+                       dnode_destroy(dn);
+               }
+
+               dict_load_next(&dl, dn, key);
+               break;
+           default:
+               putchar('?');
+               putchar('\n');
+               break;
+       }
+    }
+
+    dict_load_end(&dl);
+}
+
+int main(void)
+{
+    input_t in;
+    dict_t darray[10];
+    dict_t *d = &darray[0];
+    dnode_t *dn;
+    int i;
+    char *tok1, *tok2, *val;
+    const char *key;
+
+    char *help =
+       "a <key> <val>          add value to dictionary\n"
+       "d <key>                delete value from dictionary\n"
+       "l <key>                lookup value in dictionary\n"
+       "( <key>                lookup lower bound\n"
+       ") <key>                lookup upper bound\n"
+       "# <num>                switch to alternate dictionary (0-9)\n"
+       "j <num> <num>          merge two dictionaries\n"
+       "f                      free the whole dictionary\n"
+       "k                      allow duplicate keys\n"
+       "c                      show number of entries\n"
+       "t                      dump whole dictionary in sort order\n"
+       "m                      make dictionary out of sorted items\n"
+       "p                      turn prompt on\n"
+       "s                      switch to non-functioning allocator\n"
+       "q                      quit";
+
+    for (i = 0; i < 10; i++)
+       dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
+
+    for (;;) {
+       if (prompt)
+           putchar('>');
+       fflush(stdout);
+
+       if (!fgets(in, sizeof(input_t), stdin))
+           break;
+
+       switch(in[0]) {
+           case '?':
+               puts(help);
+               break;
+           case 'a':
+               if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+                   puts("what?");
+                   break;
+               }
+               key = dupstring(tok1);
+               val = dupstring(tok2);
+
+               if (!key || !val) {
+                   puts("out of memory");
+                   free((void *) key);
+                   free(val);
+               }
+
+               if (!dict_alloc_insert(d, key, val)) {
+                   puts("dict_alloc_insert failed");
+                   free((void *) key);
+                   free(val);
+                   break;
+               }
+               break;
+           case 'd':
+               if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+                   puts("what?");
+                   break;
+               }
+               dn = dict_lookup(d, tok1);
+               if (!dn) {
+                   puts("dict_lookup failed");
+                   break;
+               }
+               val = dnode_get(dn);
+               key = dnode_getkey(dn);
+               dict_delete_free(d, dn);
+
+               free(val);
+               free((void *) key);
+               break;
+           case 'f':
+               dict_free(d);
+               break;
+           case 'l':
+           case '(':
+           case ')':
+               if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+                   puts("what?");
+                   break;
+               }
+               dn = 0;
+               switch (in[0]) {
+               case 'l':
+                   dn = dict_lookup(d, tok1);
+                   break;
+               case '(':
+                   dn = dict_lower_bound(d, tok1);
+                   break;
+               case ')':
+                   dn = dict_upper_bound(d, tok1);
+                   break;
+               }
+               if (!dn) {
+                   puts("lookup failed");
+                   break;
+               }
+               val = dnode_get(dn);
+               puts(val);
+               break;
+           case 'm':
+               construct(d);
+               break;
+           case 'k':
+               dict_allow_dupes(d);
+               break;
+           case 'c':
+               printf("%lu\n", (unsigned long) dict_count(d));
+               break;
+           case 't':
+               for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
+                   printf("%s\t%s\n", (char *) dnode_getkey(dn),
+                           (char *) dnode_get(dn));
+               }
+               break;
+           case 'q':
+               exit(0);
+               break;
+           case '\0':
+               break;
+           case 'p':
+               prompt = 1;
+               break;
+           case 's':
+               dict_set_allocator(d, new_node, del_node, NULL);
+               break;
+           case '#':
+               if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+                   puts("what?");
+                   break;
+               } else {
+                   int dictnum = atoi(tok1);
+                   if (dictnum < 0 || dictnum > 9) {
+                       puts("invalid number");
+                       break;
+                   }
+                   d = &darray[dictnum];
+               }
+               break;
+           case 'j':
+               if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+                   puts("what?");
+                   break;
+               } else {
+                   int dict1 = atoi(tok1), dict2 = atoi(tok2);
+                   if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
+                       puts("invalid number");
+                       break;
+                   }
+                   dict_merge(&darray[dict1], &darray[dict2]);
+               }
+               break;
+           default:
+               putchar('?');
+               putchar('\n');
+               break;
+       }
+    }
+
+    return 0;
+}
+
+#endif
diff --git a/isisd/dict.h b/isisd/dict.h
new file mode 100644 (file)
index 0000000..93edb7d
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.h,v 1.3 2005/09/25 12:04:25 hasso Exp $
+ * $Name:  $
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <limits.h>
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+    struct dnode_t *dict_left;
+    struct dnode_t *dict_right;
+    struct dnode_t *dict_parent;
+    dnode_color_t dict_color;
+    const void *dict_key;
+    void *dict_data;
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *);
+typedef dnode_t *(*dnode_alloc_t)(void *);
+typedef void (*dnode_free_t)(dnode_t *, void *);
+
+typedef struct dict_t {
+    dnode_t dict_nilnode;
+    dictcount_t dict_nodecount;
+    dictcount_t dict_maxcount;
+    dict_comp_t dict_compare;
+    dnode_alloc_t dict_allocnode;
+    dnode_free_t dict_freenode;
+    void *dict_context;
+    int dict_dupes;
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+    dict_t *dict_dictptr;
+    dnode_t dict_nilnode;
+} dict_load_t;
+
+extern dict_t *dict_create(dictcount_t, dict_comp_t);
+extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
+extern void dict_destroy(dict_t *);
+extern void dict_free_nodes(dict_t *);
+extern void dict_free(dict_t *);
+extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
+extern void dict_init_like(dict_t *, const dict_t *);
+extern int dict_verify(dict_t *);
+extern int dict_similar(const dict_t *, const dict_t *);
+extern dnode_t *dict_lookup(dict_t *, const void *);
+extern dnode_t *dict_lower_bound(dict_t *, const void *);
+extern dnode_t *dict_upper_bound(dict_t *, const void *);
+extern void dict_insert(dict_t *, dnode_t *, const void *);
+extern dnode_t *dict_delete(dict_t *, dnode_t *);
+extern int dict_alloc_insert(dict_t *, const void *, void *);
+extern void dict_delete_free(dict_t *, dnode_t *);
+extern dnode_t *dict_first(dict_t *);
+extern dnode_t *dict_last(dict_t *);
+extern dnode_t *dict_next(dict_t *, dnode_t *);
+extern dnode_t *dict_prev(dict_t *, dnode_t *);
+extern dictcount_t dict_count(dict_t *);
+extern int dict_isempty(dict_t *);
+extern int dict_isfull(dict_t *);
+extern int dict_contains(dict_t *, dnode_t *);
+extern void dict_allow_dupes(dict_t *);
+extern int dnode_is_in_a_dict(dnode_t *);
+extern dnode_t *dnode_create(void *);
+extern dnode_t *dnode_init(dnode_t *, void *);
+extern void dnode_destroy(dnode_t *);
+extern void *dnode_get(dnode_t *);
+extern const void *dnode_getkey(dnode_t *);
+extern void dnode_put(dnode_t *, void *);
+extern void dict_process(dict_t *, void *, dnode_process_t);
+extern void dict_load_begin(dict_load_t *, dict_t *);
+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 dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
+#define dict_count(D) ((D)->dict_nodecount)
+#define dict_isempty(D) ((D)->dict_nodecount == 0)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+#define dnode_put(N, X) ((N)->dict_data = (X))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/isisd/include-netbsd/.gitignore b/isisd/include-netbsd/.gitignore
new file mode 100644 (file)
index 0000000..dd5bf7c
--- /dev/null
@@ -0,0 +1,6 @@
+.arch-inventory
+.arch-ids
+
+*~
+*.loT
+
diff --git a/isisd/include-netbsd/clnp.h b/isisd/include-netbsd/clnp.h
new file mode 100644 (file)
index 0000000..6bc3d25
--- /dev/null
@@ -0,0 +1,547 @@
+/*     $NetBSD: clnp.h,v 1.13 2001/08/20 12:00:54 wiz Exp $    */\r
+\r
+/*-\r
+ * Copyright (c) 1991, 1993, 1994\r
+ *     The Regents of the University of California.  All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions\r
+ * are met:\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ *    notice, this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ *    notice, this list of conditions and the following disclaimer in the\r
+ *    documentation and/or other materials provided with the distribution.\r
+ * 3. All advertising materials mentioning features or use of this software\r
+ *    must display the following acknowledgement:\r
+ *     This product includes software developed by the University of\r
+ *     California, Berkeley and its contributors.\r
+ * 4. Neither the name of the University nor the names of its contributors\r
+ *    may be used to endorse or promote products derived from this software\r
+ *    without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\r
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\r
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+ * SUCH DAMAGE.\r
+ *\r
+ *     @(#)clnp.h      8.2 (Berkeley) 4/16/94\r
+ */\r
+\r
+/***********************************************************\r
+               Copyright IBM Corporation 1987\r
+\r
+                      All Rights Reserved\r
+\r
+Permission to use, copy, modify, and distribute this software and its\r
+documentation for any purpose and without fee is hereby granted,\r
+provided that the above copyright notice appear in all copies and that\r
+both that copyright notice and this permission notice appear in\r
+supporting documentation, and that the name of IBM not be\r
+used in advertising or publicity pertaining to distribution of the\r
+software without specific, written prior permission.\r
+\r
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
+SOFTWARE.\r
+\r
+******************************************************************/\r
+\r
+/*\r
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison\r
+ */\r
+\r
+/* should be config option but cpp breaks with too many #defines */\r
+#define        DECBIT\r
+\r
+/*\r
+ *     Return true if the mbuf is a cluster mbuf\r
+ */\r
+#define        IS_CLUSTER(m)   ((m)->m_flags & M_EXT)\r
+\r
+/*\r
+ *     Move the halfword into the two characters\r
+ */\r
+#define        HTOC(msb, lsb, hword)\\r
+       (msb) = (u_char)((hword) >> 8);\\r
+       (lsb) = (u_char)((hword) & 0xff)\r
+/*\r
+ *     Move the two charcters into the halfword\r
+ */\r
+#define        CTOH(msb, lsb, hword)\\r
+       (hword) = ((msb) << 8) | (lsb)\r
+\r
+/*\r
+ *     Return true if the checksum has been set - ie. the checksum is\r
+ *     not zero\r
+ */\r
+#define        CKSUM_REQUIRED(clnp)\\r
+       (((clnp)->cnf_cksum_msb != 0) || ((clnp)->cnf_cksum_lsb != 0))\r
+\r
+/*\r
+ *     Fixed part of clnp header\r
+ */\r
+struct clnp_fixed {\r
+       u_char          cnf_proto_id;   /* network layer protocol identifier */\r
+       u_char          cnf_hdr_len;    /* length indicator (octets) */\r
+       u_char          cnf_vers;       /* version/protocol identifier\r
+                                        * extension */\r
+       u_char          cnf_ttl;/* lifetime (500 milliseconds) */\r
+       u_char          cnf_type;       /* type code */\r
+       /* Includes err_ok, more_segs, and seg_ok */\r
+       u_char          cnf_seglen_msb; /* pdu segment length (octets) high\r
+                                        * byte */\r
+       u_char          cnf_seglen_lsb; /* pdu segment length (octets) low\r
+                                        * byte */\r
+       u_char          cnf_cksum_msb;  /* checksum high byte */\r
+       u_char          cnf_cksum_lsb;  /* checksum low byte */\r
+} __attribute__((packed));\r
+#define CNF_TYPE       0x1f\r
+#define CNF_ERR_OK     0x20\r
+#define CNF_MORE_SEGS  0x40\r
+#define CNF_SEG_OK     0x80\r
+\r
+#define CLNP_CKSUM_OFF 0x07    /* offset of checksum */\r
+\r
+#define        clnl_fixed      clnp_fixed\r
+\r
+/*\r
+ *     Segmentation part of clnp header\r
+ */\r
+struct clnp_segment {\r
+       u_short         cng_id; /* data unit identifier */\r
+       u_short         cng_off;/* segment offset */\r
+       u_short         cng_tot_len;    /* total length */\r
+};\r
+\r
+/*\r
+ *     Clnp fragment reassembly structures:\r
+ *\r
+ *     All packets undergoing reassembly are linked together in\r
+ *     clnp_fragl structures. Each clnp_fragl structure contains a\r
+ *     pointer to the original clnp packet header, as well as a\r
+ *     list of packet fragments. Each packet fragment\r
+ *     is headed by a clnp_frag structure. This structure contains the\r
+ *     offset of the first and last byte of the fragment, as well as\r
+ *     a pointer to the data (an mbuf chain) of the fragment.\r
+ */\r
+\r
+/*\r
+ *     NOTE:\r
+ *             The clnp_frag structure is stored in an mbuf immedately\r
+ *             preceding the fragment data. Since there are words in\r
+ *             this struct, it must be word aligned.\r
+ *\r
+ *     NOTE:\r
+ *             All the fragment code assumes that the entire clnp header is\r
+ *             contained in the first mbuf.\r
+ */\r
+struct clnp_frag {\r
+       u_int           cfr_first;      /* offset of first byte of this frag */\r
+       u_int           cfr_last;       /* offset of last byte of this frag */\r
+       u_int           cfr_bytes;      /* bytes to shave to get to data */\r
+       struct mbuf    *cfr_data;       /* ptr to data for this frag */\r
+       struct clnp_frag *cfr_next;     /* next fragment in list */\r
+};\r
+\r
+struct clnp_fragl {\r
+       struct iso_addr cfl_src;/* source of the pkt */\r
+       struct iso_addr cfl_dst;/* destination of the pkt */\r
+       u_short         cfl_id; /* id of the pkt */\r
+       u_char          cfl_ttl;/* current ttl of pkt */\r
+       u_short         cfl_last;       /* offset of last byte of packet */\r
+       struct mbuf    *cfl_orighdr;    /* ptr to original header */\r
+       struct clnp_frag *cfl_frags;    /* linked list of fragments for pkt */\r
+       struct clnp_fragl *cfl_next;    /* next pkt being reassembled */\r
+};\r
+\r
+/*\r
+ *     The following structure is used to index into an options section\r
+ *     of a clnp datagram. These values can be used without worry that\r
+ *     offset or length fields are invalid or too big, etc. That is,\r
+ *     the consistancy of the options will be guaranteed before this\r
+ *     structure is filled in. Any pointer (field ending in p) is\r
+ *     actually the offset from the beginning of the mbuf the option\r
+ *     is contained in.  A value of NULL for any pointer\r
+ *     means that the option is not present. The length any option\r
+ *     does not include the option code or option length fields.\r
+ */\r
+struct clnp_optidx {\r
+       u_short         cni_securep;    /* ptr to start of security option */\r
+       char            cni_secure_len; /* length of entire security option */\r
+\r
+       u_short         cni_srcrt_s;    /* offset of start of src rt option */\r
+       u_short         cni_srcrt_len;  /* length of entire src rt option */\r
+\r
+       u_short         cni_recrtp;     /* ptr to beginning of recrt option */\r
+       char            cni_recrt_len;  /* length of entire recrt option */\r
+\r
+       char            cni_priorp;     /* ptr to priority option */\r
+\r
+       u_short         cni_qos_formatp;        /* ptr to format of qos\r
+                                                * option */\r
+       char            cni_qos_len;    /* length of entire qos option */\r
+\r
+       u_char          cni_er_reason;  /* reason from ER pdu option */\r
+\r
+       /* ESIS options */\r
+\r
+       u_short         cni_esct;       /* value from ISH ESCT option */\r
+\r
+       u_short         cni_netmaskp;   /* ptr to beginning of netmask option */\r
+       char            cni_netmask_len;        /* length of entire netmask\r
+                                                * option */\r
+\r
+       u_short         cni_snpamaskp;  /* ptr to start of snpamask option */\r
+       char            cni_snpamask_len;       /* length of entire snpamask\r
+                                                * option */\r
+\r
+};\r
+\r
+#define        ER_INVALREAS    0xff    /* code for invalid ER pdu discard reason */\r
+\r
+/* given an mbuf and addr of option, return offset from data of mbuf */\r
+#define CLNP_OPTTOOFF(m, opt) ((u_short) (opt - mtod(m, caddr_t)))\r
+\r
+/* given an mbuf and offset of option, return address of option */\r
+#define CLNP_OFFTOOPT(m, off) ((caddr_t) (mtod(m, caddr_t) + off))\r
+\r
+/* return true iff src route is valid */\r
+#define        CLNPSRCRT_VALID(oidx) ((oidx) && (oidx->cni_srcrt_s))\r
+\r
+/* return the offset field of the src rt */\r
+#define CLNPSRCRT_OFF(oidx, options)\\r
+       (*((u_char *)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + 1)))\r
+\r
+/* return the type field of the src rt */\r
+#define CLNPSRCRT_TYPE(oidx, options)\\r
+       ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s))))\r
+\r
+/* return the length of the current address */\r
+#define CLNPSRCRT_CLEN(oidx, options)\\r
+       ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options) - 1)))\r
+\r
+/* return the address of the current address */\r
+#define CLNPSRCRT_CADDR(oidx, options)\\r
+       ((caddr_t)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options)))\r
+\r
+/*\r
+ * return true if the src route has run out of routes this is true if the\r
+ * offset of next route is greater than the end of the rt\r
+ */\r
+#define        CLNPSRCRT_TERM(oidx, options)\\r
+       (CLNPSRCRT_OFF(oidx, options) > oidx->cni_srcrt_len)\r
+\r
+/*\r
+ *     Options a user can set/get\r
+ */\r
+#define        CLNPOPT_FLAGS   0x01    /* flags: seg permitted, no er xmit, etc  */\r
+#define        CLNPOPT_OPTS    0x02    /* datagram options */\r
+\r
+/*\r
+ *     Values for particular datagram options\r
+ */\r
+#define        CLNPOVAL_PAD            0xcc    /* padding */\r
+#define        CLNPOVAL_SECURE         0xc5    /* security */\r
+#define        CLNPOVAL_SRCRT          0xc8    /* source routing */\r
+#define        CLNPOVAL_RECRT          0xcb    /* record route */\r
+#define        CLNPOVAL_QOS            0xc3    /* quality of service */\r
+#define        CLNPOVAL_PRIOR          0xcd    /* priority */\r
+#define CLNPOVAL_ERREAS                0xc1    /* ER PDU ONLY: reason for discard */\r
+\r
+#define        CLNPOVAL_SRCSPEC        0x40    /* source address specific */\r
+#define        CLNPOVAL_DSTSPEC        0x80    /* destination address specific */\r
+#define        CLNPOVAL_GLOBAL         0xc0    /* globally unique */\r
+\r
+/* Globally Unique QOS */\r
+#define        CLNPOVAL_SEQUENCING     0x10    /* sequencing preferred */\r
+#define CLNPOVAL_CONGESTED     0x08    /* congestion experienced */\r
+#define CLNPOVAL_LOWDELAY      0x04    /* low transit delay */\r
+\r
+#define        CLNPOVAL_PARTRT         0x00    /* partial source routing */\r
+#define CLNPOVAL_COMPRT                0x01    /* complete source routing */\r
+\r
+/*\r
+ *     Clnp flags used in a control block flags field.\r
+ *     NOTE: these must be out of the range of bits defined in ../net/raw_cb.h\r
+ */\r
+#define        CLNP_NO_SEG             0x010   /* segmentation not permitted */\r
+#define        CLNP_NO_ER              0x020   /* do not generate ERs */\r
+#define CLNP_SEND_RAW          0x080   /* send pkt as RAW DT not TP DT */\r
+#define        CLNP_NO_CKSUM           0x100   /* don't use clnp checksum */\r
+#define CLNP_ECHO              0x200   /* send echo request */\r
+#define        CLNP_NOCACHE            0x400   /* don't store cache information */\r
+#define CLNP_ECHOR             0x800   /* send echo reply */\r
+\r
+/* valid clnp flags */\r
+#define CLNP_VFLAGS \\r
+       (CLNP_SEND_RAW|CLNP_NO_SEG|CLNP_NO_ER|CLNP_NO_CKSUM|\\r
+        CLNP_ECHO|CLNP_NOCACHE|CLNP_ECHOR)\r
+\r
+/*\r
+ * Constants used by clnp\r
+ */\r
+#define        CLNP_HDR_MIN    (sizeof (struct clnp_fixed))\r
+#define        CLNP_HDR_MAX    (254)\r
+#define        CLNP_TTL_UNITS  2       /* 500 milliseconds */\r
+#define CLNP_TTL       15*CLNP_TTL_UNITS       /* time to live (seconds) */\r
+#define        ISO8473_V1      0x01\r
+\r
+/*\r
+ *     Clnp packet types\r
+ *     In order to test raw clnp and tp/clnp simultaneously, a third type of\r
+ *     packet has been defined: CLNP_RAW. This is done so that the input\r
+ *     routine can switch to the correct input routine (rclnp_input or\r
+ *     tpclnp_input) based on the type field. If clnp had a higher level\r
+ *     protocol field, this would not be necessary.\r
+ */\r
+#define        CLNP_DT                 0x1C    /* normal data */\r
+#define        CLNP_ER                 0x01    /* error report */\r
+#define        CLNP_RAW                0x1D    /* debug only */\r
+#define CLNP_EC                        0x1E    /* echo packet */\r
+#define CLNP_ECR               0x1F    /* echo reply */\r
+\r
+/*\r
+ *     ER pdu error codes\r
+ */\r
+#define GEN_NOREAS             0x00    /* reason not specified */\r
+#define GEN_PROTOERR           0x01    /* protocol procedure error */\r
+#define GEN_BADCSUM            0x02    /* incorrect checksum */\r
+#define GEN_CONGEST            0x03    /* pdu discarded due to congestion */\r
+#define GEN_HDRSYNTAX          0x04    /* header syntax error */\r
+#define GEN_SEGNEEDED          0x05    /* need segmentation but not allowed */\r
+#define GEN_INCOMPLETE         0x06    /* incomplete pdu received */\r
+#define GEN_DUPOPT             0x07    /* duplicate option */\r
+\r
+/* address errors */\r
+#define ADDR_DESTUNREACH       0x80    /* destination address unreachable */\r
+#define ADDR_DESTUNKNOWN       0x81    /* destination address unknown */\r
+\r
+/* source routing */\r
+#define SRCRT_UNSPECERR                0x90    /* unspecified src rt error */\r
+#define SRCRT_SYNTAX           0x91    /* syntax error in src rt field */\r
+#define SRCRT_UNKNOWNADDR      0x92    /* unknown addr in src rt field */\r
+#define SRCRT_BADPATH          0x93    /* path not acceptable */\r
+\r
+/* lifetime */\r
+#define TTL_EXPTRANSIT         0xa0    /* lifetime expired during transit */\r
+#define TTL_EXPREASS           0xa1    /* lifetime expired during reassembly */\r
+\r
+/* pdu discarded */\r
+#define DISC_UNSUPPOPT         0xb0    /* unsupported option not specified? */\r
+#define DISC_UNSUPPVERS                0xb1    /* unsupported protocol version */\r
+#define DISC_UNSUPPSECURE      0xb2    /* unsupported security option */\r
+#define DISC_UNSUPPSRCRT       0xb3    /* unsupported src rt option */\r
+#define DISC_UNSUPPRECRT       0xb4    /* unsupported rec rt option */\r
+\r
+/* reassembly */\r
+#define REASS_INTERFERE                0xc0    /* reassembly interference */\r
+#define CLNP_ERRORS            22\r
+\r
+\r
+#ifdef CLNP_ER_CODES\r
+u_char          clnp_er_codes[CLNP_ERRORS] = {\r
+       GEN_NOREAS, GEN_PROTOERR, GEN_BADCSUM, GEN_CONGEST,\r
+       GEN_HDRSYNTAX, GEN_SEGNEEDED, GEN_INCOMPLETE, GEN_DUPOPT,\r
+       ADDR_DESTUNREACH, ADDR_DESTUNKNOWN,\r
+       SRCRT_UNSPECERR, SRCRT_SYNTAX, SRCRT_UNKNOWNADDR, SRCRT_BADPATH,\r
+       TTL_EXPTRANSIT, TTL_EXPREASS,\r
+       DISC_UNSUPPOPT, DISC_UNSUPPVERS, DISC_UNSUPPSECURE,\r
+       DISC_UNSUPPSRCRT, DISC_UNSUPPRECRT, REASS_INTERFERE\r
+};\r
+#endif\r
+\r
+#ifdef TROLL\r
+\r
+#define        TR_DUPEND               0x01    /* duplicate end of fragment */\r
+#define TR_DUPPKT              0x02    /* duplicate entire packet */\r
+#define        TR_DROPPKT              0x04    /* drop packet on output */\r
+#define TR_TRIM                        0x08    /* trim bytes from packet */\r
+#define TR_CHANGE              0x10    /* change bytes in packet */\r
+#define TR_MTU                 0x20    /* delta to change device mtu */\r
+#define        TR_CHUCK                0x40    /* drop packet in rclnp_input */\r
+#define        TR_BLAST                0x80    /* force rclnp_output to blast many\r
+                                        * packet */\r
+#define        TR_RAWLOOP              0x100   /* make if_loop call clnpintr\r
+                                        * directly */\r
+struct troll {\r
+       int             tr_ops; /* operations to perform */\r
+       float           tr_dup_size;    /* % to duplicate */\r
+       float           tr_dup_freq;    /* frequency to duplicate packets */\r
+       float           tr_drop_freq;   /* frequence to drop packets */\r
+       int             tr_mtu_adj;     /* delta to adjust if mtu */\r
+       int             tr_blast_cnt;   /* # of pkts to blast out */\r
+};\r
+\r
+#define        SN_OUTPUT(clcp, m)\\r
+       troll_output(clcp->clc_ifp, m, clcp->clc_firsthop, clcp->clc_rt)\r
+\r
+#define        SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\\r
+       rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))\\r
+               - trollctl.tr_mtu_adj)\r
+\r
+#ifdef _KERNEL\r
+extern float    troll_random;\r
+#endif\r
+\r
+#else                          /* NO TROLL */\r
+\r
+#define        SN_OUTPUT(clcp, m)\\r
+       (*clcp->clc_ifp->if_output)(clcp->clc_ifp, m, clcp->clc_firsthop, \\r
+                                   clcp->clc_rt)\r
+\r
+#define        SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\\r
+       rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__)))\r
+\r
+#endif                         /* TROLL */\r
+\r
+/*\r
+ *     Macro to remove an address from a clnp header\r
+ */\r
+#define CLNP_EXTRACT_ADDR(isoa, hoff, hend)\\r
+       {\\r
+               isoa.isoa_len = (u_char)*hoff;\\r
+               if ((((++hoff) + isoa.isoa_len) > hend) ||\\r
+                       (isoa.isoa_len > 20) || (isoa.isoa_len == 0)) {\\r
+                       hoff = (caddr_t)0;\\r
+               } else {\\r
+                       (void) bcopy(hoff, (caddr_t)isoa.isoa_genaddr, \\r
+                                    isoa.isoa_len);\\r
+                       hoff += isoa.isoa_len;\\r
+               }\\r
+       }\r
+\r
+/*\r
+ *     Macro to insert an address into a clnp header\r
+ */\r
+#define CLNP_INSERT_ADDR(hoff, isoa)\\r
+       *hoff++ = (isoa).isoa_len;\\r
+       (void) bcopy((caddr_t)((isoa).isoa_genaddr), hoff, (isoa).isoa_len);\\r
+       hoff += (isoa).isoa_len;\r
+\r
+/*\r
+ *     Clnp hdr cache. Whenever a clnp packet is sent, a copy of the\r
+ *     header is made and kept in this cache. In addition to a copy of\r
+ *     the cached clnp hdr, the cache contains\r
+ *     information necessary to determine whether the new packet\r
+ *     to send requires a new header to be built.\r
+ */\r
+struct clnp_cache {\r
+       /* these fields are used to check the validity of the cache */\r
+       struct iso_addr clc_dst;/* destination of packet */\r
+       struct mbuf    *clc_options;    /* ptr to options mbuf */\r
+       int             clc_flags;      /* flags passed to clnp_output */\r
+\r
+       /* these fields are state that clnp_output requires to finish the pkt */\r
+       int             clc_segoff;     /* offset of seg part of header */\r
+       struct rtentry *clc_rt; /* ptr to rtentry (points into the route\r
+                                * structure) */\r
+       struct sockaddr *clc_firsthop;  /* first hop of packet */\r
+       struct ifnet   *clc_ifp;/* ptr to interface structure */\r
+       struct iso_ifaddr\r
+                      *clc_ifa;/* ptr to interface address */\r
+       struct mbuf    *clc_hdr;/* cached pkt hdr (finally)! */\r
+};\r
+\r
+#ifdef _KERNEL\r
+struct iso_addr;\r
+struct sockaddr_iso;\r
+struct mbuf;\r
+struct clnp_segment;\r
+struct sockaddr;\r
+struct rt_entry;\r
+struct clnp_fragl;\r
+struct clnp_optidx;\r
+struct isopcb;\r
+struct snpa_hdr;\r
+struct iso_ifaddr;\r
+struct route_iso;\r
+\r
+/* clnp_debug.c */\r
+char *clnp_hexp __P((char *, int, char *));\r
+char *clnp_iso_addrp __P((struct iso_addr *));\r
+char *clnp_saddr_isop __P((struct sockaddr_iso *));\r
+\r
+/* clnp_er.c */\r
+void clnp_er_input __P((struct mbuf *, struct iso_addr *, u_int));\r
+void clnp_discard __P((struct mbuf *, u_int));\r
+void clnp_emit_er __P((struct mbuf *, u_int));\r
+int clnp_er_index __P((u_int));\r
+\r
+int clnp_fragment __P((struct ifnet *, struct mbuf *, struct sockaddr *,\r
+                      int, int, int, struct rtentry *));\r
+struct mbuf *clnp_reass __P((struct mbuf *, struct iso_addr *,\r
+                            struct iso_addr *, struct clnp_segment *));\r
+int clnp_newpkt __P((struct mbuf *, struct iso_addr *, struct iso_addr *,\r
+                    struct clnp_segment *));\r
+void clnp_insert_frag __P((struct clnp_fragl *, struct mbuf *,\r
+                          struct clnp_segment *));\r
+struct mbuf    *clnp_comp_pdu __P((struct clnp_fragl *));\r
+#ifdef TROLL\r
+float troll_random __P((void));\r
+int troll_output __P((struct ifnet *, struct mbuf *, struct sockaddr *,\r
+                     struct rtentry *));\r
+#endif\r
+\r
+/* clnp_input.c */\r
+void clnp_init  __P((void));\r
+void clnlintr    __P((void));\r
+void clnp_input __P((struct mbuf *, ...));\r
+\r
+/* clnp_options.c */\r
+void clnp_update_srcrt __P((struct mbuf *, struct clnp_optidx *));\r
+void clnp_dooptions __P((struct mbuf *, struct clnp_optidx *, struct ifnet *,\r
+                        struct iso_addr *));\r
+int clnp_set_opts __P((struct mbuf **, struct mbuf **));\r
+int clnp_opt_sanity __P((struct mbuf *, caddr_t, int, struct clnp_optidx *));\r
+\r
+/* clnp_output.c */\r
+int clnp_output __P((struct mbuf *, ...));\r
+void clnp_ctloutput __P((void));\r
+\r
+/* clnp_raw.c */\r
+void rclnp_input __P((struct mbuf *, ...));\r
+int rclnp_output __P((struct mbuf *, ...));\r
+int rclnp_ctloutput __P((int, struct socket *, int, int, struct mbuf **));\r
+int clnp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,\r
+                    struct mbuf *, struct proc *));\r
+\r
+/* clnp_subr.c */\r
+struct mbuf    *clnp_data_ck __P((struct mbuf *, int));\r
+caddr_t clnp_extract_addr __P((caddr_t, int, struct iso_addr *,\r
+                              struct iso_addr *));\r
+int clnp_ours   __P((struct iso_addr *));\r
+void clnp_forward __P((struct mbuf *, int, struct iso_addr *,\r
+                      struct clnp_optidx *, int, struct snpa_hdr *));\r
+caddr_t clnp_insert_addr __P((caddr_t, struct iso_addr *, struct iso_addr *));\r
+int clnp_route  __P((struct iso_addr *, struct route_iso *, int,\r
+                    struct sockaddr **, struct iso_ifaddr **));\r
+int clnp_srcroute __P((struct mbuf *, struct clnp_optidx *, struct route_iso *,\r
+                      struct sockaddr **, struct iso_ifaddr **,\r
+                      struct iso_addr *));\r
+int clnp_echoreply __P((struct mbuf *, int, struct sockaddr_iso *,\r
+                       struct sockaddr_iso *, struct clnp_optidx *));\r
+int clnp_badmtu __P((struct ifnet *, struct rtentry *, int, char *));\r
+void clnp_ypocb  __P((caddr_t, caddr_t, u_int));\r
+\r
+/* clnp_timer.c */\r
+struct clnp_fragl *clnp_freefrags __P((struct clnp_fragl *));\r
+void clnp_slowtimo __P((void));\r
+void clnp_drain __P((void));\r
+\r
+#ifdef TROLL\r
+struct troll    trollctl;\r
+#endif /* TROLL */\r
+\r
+#endif /* _KERNEL */\r
diff --git a/isisd/include-netbsd/esis.h b/isisd/include-netbsd/esis.h
new file mode 100644 (file)
index 0000000..ded864e
--- /dev/null
@@ -0,0 +1,146 @@
+/*     $NetBSD: esis.h,v 1.11 1997/11/03 15:01:19 is Exp $     */\r
+\r
+/*-\r
+ * Copyright (c) 1991, 1993\r
+ *     The Regents of the University of California.  All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions\r
+ * are met:\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ *    notice, this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ *    notice, this list of conditions and the following disclaimer in the\r
+ *    documentation and/or other materials provided with the distribution.\r
+ * 3. All advertising materials mentioning features or use of this software\r
+ *    must display the following acknowledgement:\r
+ *     This product includes software developed by the University of\r
+ *     California, Berkeley and its contributors.\r
+ * 4. Neither the name of the University nor the names of its contributors\r
+ *    may be used to endorse or promote products derived from this software\r
+ *    without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\r
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\r
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+ * SUCH DAMAGE.\r
+ *\r
+ *     @(#)esis.h      8.1 (Berkeley) 6/10/93\r
+ */\r
+\r
+/***********************************************************\r
+               Copyright IBM Corporation 1987\r
+\r
+                      All Rights Reserved\r
+\r
+Permission to use, copy, modify, and distribute this software and its\r
+documentation for any purpose and without fee is hereby granted,\r
+provided that the above copyright notice appear in all copies and that\r
+both that copyright notice and this permission notice appear in\r
+supporting documentation, and that the name of IBM not be\r
+used in advertising or publicity pertaining to distribution of the\r
+software without specific, written prior permission.\r
+\r
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
+SOFTWARE.\r
+\r
+******************************************************************/\r
+\r
+/*\r
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison\r
+ */\r
+\r
+#include <machine/endian.h>\r
+\r
+#define        SNPAC_AGE               60      /* seconds */\r
+#define        ESIS_CONFIG             60      /* seconds */\r
+#define        ESIS_HT                 (ESIS_CONFIG * 2)\r
+\r
+/*\r
+ *     Fixed part of an ESIS header\r
+ */\r
+struct esis_fixed {\r
+       u_char          esis_proto_id;  /* network layer protocol identifier */\r
+       u_char          esis_hdr_len;   /* length indicator (octets) */\r
+       u_char          esis_vers;      /* version/protocol identifier\r
+                                        * extension */\r
+       u_char          esis_res1;      /* reserved */\r
+       u_char          esis_type;      /* type code */\r
+       /* technically, type should be &='d 0x1f */\r
+#define ESIS_ESH       0x02    /* End System Hello */\r
+#define ESIS_ISH       0x04    /* Intermediate System Hello */\r
+#define ESIS_RD                0x06    /* Redirect */\r
+       u_char          esis_ht_msb;    /* holding time (seconds) high byte */\r
+       u_char          esis_ht_lsb;    /* holding time (seconds) low byte */\r
+       u_char          esis_cksum_msb; /* checksum high byte */\r
+       u_char          esis_cksum_lsb; /* checksum low byte */\r
+} __attribute__((packed));\r
+/*\r
+ * Values for ESIS datagram options\r
+ */\r
+#define ESISOVAL_NETMASK       0xe1    /* address mask option, RD PDU only */\r
+#define ESISOVAL_SNPAMASK      0xe2    /* snpa mask option, RD PDU only */\r
+#define ESISOVAL_ESCT          0xc6    /* end system conf. timer, ISH PDU\r
+                                        * only */\r
+\r
+\r
+#define        ESIS_CKSUM_OFF          0x07\r
+#define ESIS_CKSUM_REQUIRED(pdu)\\r
+       ((pdu->esis_cksum_msb != 0) || (pdu->esis_cksum_lsb != 0))\r
+\r
+#define        ESIS_VERSION    1\r
+\r
+struct esis_stat {\r
+       u_short         es_nomem;       /* insufficient memory to send hello */\r
+       u_short         es_badcsum;     /* incorrect checksum */\r
+       u_short         es_badvers;     /* incorrect version number */\r
+       u_short         es_badtype;     /* unknown pdu type field */\r
+       u_short         es_toosmall;    /* packet too small */\r
+       u_short         es_eshsent;     /* ESH sent */\r
+       u_short         es_eshrcvd;     /* ESH rcvd */\r
+       u_short         es_ishsent;     /* ISH sent */\r
+       u_short         es_ishrcvd;     /* ISH rcvd */\r
+       u_short         es_rdsent;      /* RD sent */\r
+       u_short         es_rdrcvd;      /* RD rcvd */\r
+};\r
+\r
+#ifdef _KERNEL\r
+struct esis_stat esis_stat;\r
+struct socket;\r
+struct mbuf;\r
+struct snpa_hdr;\r
+struct clnp_optidx;\r
+struct iso_addr;\r
+struct rtentry;\r
+struct sockaddr_dl;\r
+\r
+void esis_init __P((void));\r
+int esis_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,\r
+                    struct mbuf *, struct proc *));\r
+void esis_input __P((struct mbuf *, ...));\r
+void esis_rdoutput __P((struct snpa_hdr *, struct mbuf *, struct clnp_optidx *,\r
+                       struct iso_addr *, struct rtentry *));\r
+int esis_insert_addr __P((caddr_t *, int *, struct iso_addr *, struct mbuf *,\r
+                         int));\r
+void esis_eshinput __P((struct mbuf *, struct snpa_hdr *));\r
+void esis_ishinput __P((struct mbuf *, struct snpa_hdr *));\r
+void esis_rdinput __P((struct mbuf *, struct snpa_hdr *));\r
+void esis_config __P((void *));\r
+void esis_shoutput __P((struct ifnet *, int, int, caddr_t, int,\r
+                      struct iso_addr *));\r
+void isis_input __P((struct mbuf *, ...));\r
+int isis_output __P((struct mbuf *, ...));\r
+void *esis_ctlinput __P((int, struct sockaddr *, void *));\r
+#endif /* _KERNEL */\r
diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h
new file mode 100644 (file)
index 0000000..42b9bc8
--- /dev/null
@@ -0,0 +1,214 @@
+/*     $NetBSD: iso.h,v 1.13 2000/07/28 12:13:34 kleink Exp $  */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)iso.h       8.1 (Berkeley) 6/10/93
+ */
+
+/***********************************************************
+               Copyright IBM Corporation 1987
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#ifndef _NETISO_ISO_H_
+#define _NETISO_ISO_H_
+
+#if 0
+#include <sys/ansi.h>
+#endif
+
+#if 0
+#ifndef sa_family_t
+typedef __sa_family_t  sa_family_t;
+#define sa_family_t    __sa_family_t
+#endif
+#endif
+/*
+ *     Return true if this is a multicast address
+ *     This assumes that the bit transmission is lsb first. This
+ *     assumption is valid for 802.3 but not 802.5. There is a
+ *     kludge to get around this for 802.5 -- see if_lan.c
+ *     where subnetwork header is setup.
+ */
+#define        IS_MULTICAST(snpa)\
+       ((snpa)[0] & 0x01)
+
+/*
+ * Protocols
+ */
+#define        ISOPROTO_TCP    6       /* IETF experiment */
+#define        ISOPROTO_UDP    17      /* IETF experiment */
+#define        ISOPROTO_TP0    25      /* connection oriented transport protocol */
+#define        ISOPROTO_TP1    26      /* not implemented */
+#define        ISOPROTO_TP2    27      /* not implemented */
+#define        ISOPROTO_TP3    28      /* not implemented */
+#define        ISOPROTO_TP4    29      /* connection oriented transport protocol */
+#define        ISOPROTO_TP             ISOPROTO_TP4    /* tp-4 with negotiation */
+#define        ISOPROTO_CLTP   30      /* connectionless transport (not yet impl.) */
+#define        ISOPROTO_CLNP   31      /* connectionless internetworking protocol */
+#define        ISOPROTO_X25    32      /* cons */
+#define        ISOPROTO_INACT_NL       33      /* inactive network layer! */
+#define        ISOPROTO_ESIS   34      /* ES-IS protocol */
+#define        ISOPROTO_INTRAISIS      35      /* IS-IS protocol */
+#define        ISOPROTO_IDRP   36      /* Interdomain Routing Protocol */
+
+#define        ISOPROTO_RAW    255     /* raw clnp */
+#define        ISOPROTO_MAX    256
+
+#define        ISO_PORT_RESERVED               1024
+#define        ISO_PORT_USERRESERVED   5000
+/*
+ * Port/socket numbers: standard network functions
+ * NOT PRESENTLY USED
+ */
+#define        ISO_PORT_MAINT          501
+#define        ISO_PORT_ECHO           507
+#define        ISO_PORT_DISCARD        509
+#define        ISO_PORT_SYSTAT         511
+#define        ISO_PORT_NETSTAT        515
+/*
+ * Port/socket numbers: non-standard application functions
+ */
+#define ISO_PORT_LOGIN         513
+/*
+ * Port/socket numbers: public use
+ */
+#define ISO_PORT_PUBLIC                1024    /* high bit set --> public */
+
+/*
+ *     Network layer protocol identifiers
+ */
+#define ISO8473_CLNP   0x81
+#define        ISO9542_ESIS    0x82
+#define ISO9542X25_ESIS        0x8a
+#define ISO10589_ISIS  0x83
+#define ISO8878A_CONS  0x84
+#define ISO10747_IDRP  0x85
+
+
+#ifndef IN_CLASSA_NET
+#include <netinet/in.h>
+#endif                         /* IN_CLASSA_NET */
+
+
+
+/*
+ * The following looks like a sockaddr to facilitate using tree lookup
+ * routines
+ */
+struct iso_addr {
+       u_char          isoa_len;       /* length (in bytes) */
+       char            isoa_genaddr[20];       /* general opaque address */
+};
+
+struct sockaddr_iso {
+       u_char          siso_len;       /* length */
+       sa_family_t     siso_family;    /* family */
+       u_char          siso_plen;      /* presentation selector length */
+       u_char          siso_slen;      /* session selector length */
+       u_char          siso_tlen;      /* transport selector length */
+       struct iso_addr siso_addr;      /* network address */
+       u_char          siso_pad[6];    /* space for gosip v2 sels */
+       /* makes struct 32 bytes long */
+};
+#define siso_nlen siso_addr.isoa_len
+#define siso_data siso_addr.isoa_genaddr
+
+#define TSEL(s) ((caddr_t)((s)->siso_data + (s)->siso_nlen))
+
+#define SAME_ISOADDR(a, b) \
+       (bcmp((a)->siso_data, (b)->siso_data, (unsigned)(a)->siso_nlen)==0)
+#define SAME_ISOIFADDR(a, b) (bcmp((a)->siso_data, (b)->siso_data, \
+       (unsigned)((b)->siso_nlen - (b)->siso_tlen)) == 0)
+/*
+ * The following are specific values for siso->siso_data[0],
+ * otherwise known as the AFI:
+ */
+#define        AFI_37          0x37    /* bcd of "37" */
+#define AFI_OSINET     0x47    /* bcd of "47" */
+#define AFI_RFC986     0x47    /* bcd of "47" */
+#define        AFI_SNA         0x00    /* SubNetwork Address; invalid really... */
+
+#ifdef _KERNEL
+
+extern struct domain isodomain;
+extern struct protosw isosw[];
+
+#define        satosiso(sa)    ((struct sockaddr_iso *)(sa))
+#define        sisotosa(siso)  ((struct sockaddr *)(siso))
+
+#else
+/* user utilities definitions from the iso library */
+
+#ifndef HAVE_SYS_CDEFS_H
+#define        __P(x)  x
+#define        __BEGIN_DECLS
+#define        __END_DECLS
+#else
+#include <sys/cdefs.h>
+#endif
+
+__BEGIN_DECLS
+struct iso_addr *iso_addr __P((const char *));
+char           *iso_ntoa __P((const struct iso_addr *));
+
+/* THESE DON'T EXIST YET */
+struct hostent *iso_gethostbyname __P((const char *));
+struct hostent *iso_gethostbyaddr __P((const char *, int, int));
+__END_DECLS
+
+#endif /* _KERNEL */
+
+#endif /* _NETISO_ISO_H_ */
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
new file mode 100644 (file)
index 0000000..8afabed
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_adjacency.c   
+ *                             handling of IS-IS adjacencies
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "hash.h"
+#include "vty.h"
+#include "linklist.h"
+#include "thread.h"
+#include "if.h"
+#include "stream.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_misc.h"
+#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_spf.h"
+#include "isisd/isis_events.h"
+
+extern struct isis *isis;
+
+static struct isis_adjacency *
+adj_alloc (const u_char *id)
+{
+  struct isis_adjacency *adj;
+
+  adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
+  memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
+
+  return adj;
+}
+
+struct isis_adjacency *
+isis_new_adj (const u_char * id, const u_char * snpa, int level,
+             struct isis_circuit *circuit)
+{
+  struct isis_adjacency *adj;
+  int i;
+
+  adj = adj_alloc (id);                /* P2P kludge */
+
+  if (adj == NULL)
+    {
+      zlog_err ("Out of memory!");
+      return NULL;
+    }
+
+  if (snpa) {
+    memcpy (adj->snpa, snpa, ETH_ALEN);
+  } else {
+    memset (adj->snpa, ' ', ETH_ALEN);
+  }
+
+  adj->circuit = circuit;
+  adj->level = level;
+  adj->flaps = 0;
+  adj->last_flap = time (NULL);
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      listnode_add (circuit->u.bc.adjdb[level - 1], adj);
+      adj->dischanges[level - 1] = 0;
+      for (i = 0; i < DIS_RECORDS; i++)        /* clear N DIS state change records */
+       {
+         adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis
+           = ISIS_UNKNOWN_DIS;
+         adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change
+           = time (NULL);
+       }
+    }
+
+  return adj;
+}
+
+struct isis_adjacency *
+isis_adj_lookup (const u_char * sysid, struct list *adjdb)
+{
+  struct isis_adjacency *adj;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+    if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
+      return adj;
+
+  return NULL;
+}
+
+struct isis_adjacency *
+isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb)
+{
+  struct listnode *node;
+  struct isis_adjacency *adj;
+
+  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+    if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0)
+      return adj;
+
+  return NULL;
+}
+
+void
+isis_delete_adj (void *arg)
+{
+  struct isis_adjacency *adj = arg;
+
+  if (!adj)
+    return;
+
+  THREAD_TIMER_OFF (adj->t_expire);
+
+  /* remove from SPF trees */
+  spftree_area_adj_del (adj->circuit->area, adj);
+
+  if (adj->area_addrs)
+    list_delete (adj->area_addrs);
+  if (adj->ipv4_addrs)
+    list_delete (adj->ipv4_addrs);
+#ifdef HAVE_IPV6
+  if (adj->ipv6_addrs)
+    list_delete (adj->ipv6_addrs);
+#endif
+
+  XFREE (MTYPE_ISIS_ADJACENCY, adj);
+  return;
+}
+
+static const char *
+adj_state2string (int state)
+{
+
+  switch (state)
+    {
+    case ISIS_ADJ_INITIALIZING:
+      return "Initializing";
+    case ISIS_ADJ_UP:
+      return "Up";
+    case ISIS_ADJ_DOWN:
+      return "Down";
+    default:
+      return "Unknown";
+    }
+
+  return NULL;                 /* not reached */
+}
+
+void
+isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state,
+                      const char *reason)
+{
+  int old_state;
+  int level;
+  struct isis_circuit *circuit;
+
+  old_state = adj->adj_state;
+  adj->adj_state = new_state;
+
+  circuit = adj->circuit;
+
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      zlog_debug ("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
+                circuit->area->area_tag,
+                old_state, new_state, reason ? reason : "unspecified");
+    }
+
+  if (circuit->area->log_adj_changes)
+    {
+      const char *adj_name;
+      struct isis_dynhn *dyn;
+
+      dyn = dynhn_find_by_id (adj->sysid);
+      if (dyn)
+       adj_name = (const char *)dyn->name.name;
+      else
+       adj_name = sysid_print (adj->sysid);
+
+      zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
+                adj_name,
+                adj->circuit->interface->name,
+                adj_state2string (old_state),
+                adj_state2string (new_state),
+                reason ? reason : "unspecified");
+    }
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
+      {
+        if ((adj->level & level) == 0)
+          continue;
+        if (new_state == ISIS_ADJ_UP)
+        {
+          circuit->upadjcount[level - 1]++;
+          isis_event_adjacency_state_change (adj, new_state);
+          /* update counter & timers for debugging purposes */
+          adj->last_flap = time (NULL);
+          adj->flaps++;
+        }
+        else if (new_state == ISIS_ADJ_DOWN)
+        {
+          listnode_delete (circuit->u.bc.adjdb[level - 1], adj);
+          circuit->upadjcount[level - 1]--;
+          if (circuit->upadjcount[level - 1] == 0)
+            {
+              /* Clean lsp_queue when no adj is up. */
+              if (circuit->lsp_queue)
+                list_delete_all_node (circuit->lsp_queue);
+            }
+          isis_event_adjacency_state_change (adj, new_state);
+          isis_delete_adj (adj);
+        }
+
+        if (circuit->u.bc.lan_neighs[level - 1])
+          {
+            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_regenerate_schedule_pseudo (circuit, level);
+      }
+    }
+  else if (circuit->circ_type == CIRCUIT_T_P2P)
+    {
+      for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
+      {
+        if ((adj->level & level) == 0)
+          continue;
+        if (new_state == ISIS_ADJ_UP)
+        {
+          circuit->upadjcount[level - 1]++;
+          isis_event_adjacency_state_change (adj, new_state);
+
+          if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN)
+            send_hello (circuit, level);
+
+          /* update counter & timers for debugging purposes */
+          adj->last_flap = time (NULL);
+          adj->flaps++;
+
+          /* 7.3.17 - going up on P2P -> send CSNP */
+          /* FIXME: yup, I know its wrong... but i will do it! (for now) */
+          send_csnp (circuit, level);
+        }
+        else if (new_state == ISIS_ADJ_DOWN)
+        {
+          if (adj->circuit->u.p2p.neighbor == adj)
+            adj->circuit->u.p2p.neighbor = NULL;
+          circuit->upadjcount[level - 1]--;
+          if (circuit->upadjcount[level - 1] == 0)
+            {
+              /* Clean lsp_queue when no adj is up. */
+              if (circuit->lsp_queue)
+                list_delete_all_node (circuit->lsp_queue);
+            }
+          isis_event_adjacency_state_change (adj, new_state);
+          isis_delete_adj (adj);
+        }
+      }
+    }
+
+  return;
+}
+
+
+void
+isis_adj_print (struct isis_adjacency *adj)
+{
+  struct isis_dynhn *dyn;
+  struct listnode *node;
+  struct in_addr *ipv4_addr;
+#ifdef HAVE_IPV6
+  struct in6_addr *ipv6_addr;
+  u_char ip6[INET6_ADDRSTRLEN];
+#endif /* HAVE_IPV6 */
+
+  if (!adj)
+    return;
+  dyn = dynhn_find_by_id (adj->sysid);
+  if (dyn)
+    zlog_debug ("%s", dyn->name.name);
+
+  zlog_debug ("SystemId %20s SNPA %s, level %d\nHolding Time %d",
+              sysid_print (adj->sysid), snpa_print (adj->snpa),
+              adj->level, adj->hold_time);
+  if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
+    {
+      zlog_debug ("IPv4 Address(es):");
+
+      for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr))
+        zlog_debug ("%s", inet_ntoa (*ipv4_addr));
+    }
+
+#ifdef HAVE_IPV6
+  if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
+    {
+      zlog_debug ("IPv6 Address(es):");
+      for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
+       {
+         inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
+         zlog_debug ("%s", ip6);
+       }
+    }
+#endif /* HAVE_IPV6 */
+  zlog_debug ("Speaks: %s", nlpid2string (&adj->nlpids));
+
+  return;
+}
+
+int
+isis_adj_expire (struct thread *thread)
+{
+  struct isis_adjacency *adj;
+
+  /*
+   * Get the adjacency
+   */
+  adj = THREAD_ARG (thread);
+  assert (adj);
+  adj->t_expire = NULL;
+
+  /* trigger the adj expire event */
+  isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired");
+
+  return 0;
+}
+
+/*
+ * show isis neighbor [detail]
+ */
+void
+isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail)
+{
+#ifdef HAVE_IPV6
+  struct in6_addr *ipv6_addr;
+  u_char ip6[INET6_ADDRSTRLEN];
+#endif /* HAVE_IPV6 */
+  struct in_addr *ip_addr;
+  time_t now;
+  struct isis_dynhn *dyn;
+  int level;
+  struct listnode *node;
+
+  dyn = dynhn_find_by_id (adj->sysid);
+  if (dyn)
+    vty_out (vty, "  %-20s", dyn->name.name);
+  else
+    vty_out (vty, "  %-20s", sysid_print (adj->sysid));
+
+  if (detail == ISIS_UI_LEVEL_BRIEF)
+    {
+      if (adj->circuit)
+       vty_out (vty, "%-12s", adj->circuit->interface->name);
+      else
+       vty_out (vty, "NULL circuit!");
+      vty_out (vty, "%-3u", adj->level);       /* level */
+      vty_out (vty, "%-13s", adj_state2string (adj->adj_state));
+      now = time (NULL);
+      if (adj->last_upd)
+        vty_out (vty, "%-9llu",
+                 (unsigned long long)adj->last_upd + adj->hold_time - now);
+      else
+       vty_out (vty, "-        ");
+      vty_out (vty, "%-10s", snpa_print (adj->snpa));
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  if (detail == ISIS_UI_LEVEL_DETAIL)
+    {
+      level = adj->level;
+      vty_out (vty, "%s", VTY_NEWLINE);
+      if (adj->circuit)
+       vty_out (vty, "    Interface: %s", adj->circuit->interface->name);
+      else
+       vty_out (vty, "    Interface: NULL circuit");
+      vty_out (vty, ", Level: %u", adj->level);        /* level */
+      vty_out (vty, ", State: %s", adj_state2string (adj->adj_state));
+      now = time (NULL);
+      if (adj->last_upd)
+       vty_out (vty, ", Expires in %s",
+                time2string (adj->last_upd + adj->hold_time - now));
+      else
+       vty_out (vty, ", Expires in %s", time2string (adj->hold_time));
+      vty_out (vty, "%s", VTY_NEWLINE);
+      vty_out (vty, "    Adjacency flaps: %u", adj->flaps);
+      vty_out (vty, ", Last: %s ago", time2string (now - adj->last_flap));
+      vty_out (vty, "%s", VTY_NEWLINE);
+      vty_out (vty, "    Circuit type: %s", circuit_t2string (adj->circuit_t));
+      vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids));
+      vty_out (vty, "%s", VTY_NEWLINE);
+      vty_out (vty, "    SNPA: %s", snpa_print (adj->snpa));
+      if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST))
+      {
+        dyn = dynhn_find_by_id (adj->lanid);
+        if (dyn)
+          vty_out (vty, ", LAN id: %s.%02x",
+              dyn->name.name, adj->lanid[ISIS_SYS_ID_LEN]);
+        else
+          vty_out (vty, ", LAN id: %s.%02x",
+              sysid_print (adj->lanid), adj->lanid[ISIS_SYS_ID_LEN]);
+
+        vty_out (vty, "%s", VTY_NEWLINE);
+        vty_out (vty, "    LAN Priority: %u", adj->prio[adj->level - 1]);
+
+        vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago",
+            isis_disflag2string (adj->dis_record[ISIS_LEVELS + level - 1].
+              dis), adj->dischanges[level - 1],
+            time2string (now -
+              (adj->dis_record[ISIS_LEVELS + level - 1].
+               last_dis_change)));
+      }
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      if (adj->area_addrs && listcount (adj->area_addrs) > 0)
+        {
+          struct area_addr *area_addr;
+          vty_out (vty, "    Area Address(es):%s", VTY_NEWLINE);
+          for (ALL_LIST_ELEMENTS_RO (adj->area_addrs, node, area_addr))
+            vty_out (vty, "      %s%s", isonet_print (area_addr->area_addr,
+                     area_addr->addr_len), VTY_NEWLINE);
+        }
+      if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
+       {
+         vty_out (vty, "    IPv4 Address(es):%s", VTY_NEWLINE);
+         for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ip_addr))
+            vty_out (vty, "      %s%s", inet_ntoa (*ip_addr), VTY_NEWLINE);
+       }
+#ifdef HAVE_IPV6
+      if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
+       {
+         vty_out (vty, "    IPv6 Address(es):%s", VTY_NEWLINE);
+         for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
+           {
+             inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
+             vty_out (vty, "      %s%s", ip6, VTY_NEWLINE);
+           }
+       }
+#endif /* HAVE_IPV6 */
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return;
+}
+
+void
+isis_adj_build_neigh_list (struct list *adjdb, struct list *list)
+{
+  struct isis_adjacency *adj;
+  struct listnode *node;
+
+  if (!list)
+    {
+      zlog_warn ("isis_adj_build_neigh_list(): NULL list");
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+    {
+      if (!adj)
+       {
+         zlog_warn ("isis_adj_build_neigh_list(): NULL adj");
+         return;
+       }
+
+      if ((adj->adj_state == ISIS_ADJ_UP ||
+          adj->adj_state == ISIS_ADJ_INITIALIZING))
+       listnode_add (list, adj->snpa);
+    }
+  return;
+}
+
+void
+isis_adj_build_up_list (struct list *adjdb, struct list *list)
+{
+  struct isis_adjacency *adj;
+  struct listnode *node;
+
+  if (adjdb == NULL) {
+    zlog_warn ("isis_adj_build_up_list(): adjacency DB is empty");
+    return;
+  }
+
+  if (!list)
+    {
+      zlog_warn ("isis_adj_build_up_list(): NULL list");
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+    {
+      if (!adj)
+       {
+         zlog_warn ("isis_adj_build_up_list(): NULL adj");
+         return;
+       }
+
+      if (adj->adj_state == ISIS_ADJ_UP)
+       listnode_add (list, adj);
+    }
+
+  return;
+}
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h
new file mode 100644 (file)
index 0000000..99d0c49
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_adjacency.h   
+ *                             IS-IS adjacency handling
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_ADJACENCY_H
+#define _ZEBRA_ISIS_ADJACENCY_H
+
+enum isis_adj_usage
+{
+  ISIS_ADJ_NONE,
+  ISIS_ADJ_LEVEL1,
+  ISIS_ADJ_LEVEL2,
+  ISIS_ADJ_LEVEL1AND2
+};
+
+enum isis_system_type
+{
+  ISIS_SYSTYPE_UNKNOWN,
+  ISIS_SYSTYPE_ES,
+  ISIS_SYSTYPE_IS,
+  ISIS_SYSTYPE_L1_IS,
+  ISIS_SYSTYPE_L2_IS
+};
+
+enum isis_adj_state
+{
+  ISIS_ADJ_UNKNOWN,
+  ISIS_ADJ_INITIALIZING,
+  ISIS_ADJ_UP,
+  ISIS_ADJ_DOWN
+};
+
+/*
+ * we use the following codes to give an indication _why_
+ * a specific adjacency is up or down
+ */
+enum isis_adj_updown_reason
+{
+  ISIS_ADJ_REASON_SEENSELF,
+  ISIS_ADJ_REASON_AREA_MISMATCH,
+  ISIS_ADJ_REASON_HOLDTIMER_EXPIRED,
+  ISIS_ADJ_REASON_AUTH_FAILED,
+  ISIS_ADJ_REASON_CHECKSUM_FAILED
+};
+
+#define DIS_RECORDS 8  /* keep the last 8 DIS state changes on record */
+
+struct isis_dis_record
+{
+  int dis;                     /* is our neighbor the DIS ? */
+  time_t last_dis_change;      /* timestamp for last dis change */
+};
+
+struct isis_adjacency
+{
+  u_char snpa[ETH_ALEN];               /* NeighbourSNPAAddress */
+  u_char sysid[ISIS_SYS_ID_LEN];       /* neighbourSystemIdentifier */
+  u_char lanid[ISIS_SYS_ID_LEN + 1];   /* LAN id on bcast circuits */
+  int dischanges[ISIS_LEVELS];         /* how many DIS changes ? */
+  /* an array of N levels for M records */
+  struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS];
+  enum isis_adj_state adj_state;       /* adjacencyState */
+  enum isis_adj_usage adj_usage;       /* adjacencyUsage */
+  struct list *area_addrs;             /* areaAdressesOfNeighbour */
+  struct nlpids nlpids;                        /* protocols spoken ... */
+  struct list *ipv4_addrs;
+  struct in_addr router_address;
+#ifdef HAVE_IPV6
+  struct list *ipv6_addrs;
+  struct in6_addr router_address6;
+#endif                         /* HAVE_IPV6 */
+  u_char prio[ISIS_LEVELS];    /* priorityOfNeighbour for DIS */
+  int circuit_t;               /* from hello PDU hdr */
+  int level;                   /* level (1 or 2) */
+  enum isis_system_type sys_type;      /* neighbourSystemType */
+  u_int16_t hold_time;         /* entryRemainingTime */
+  u_int32_t last_upd;
+  u_int32_t last_flap;         /* last time the adj flapped */
+  int flaps;                   /* number of adjacency flaps  */
+  struct thread *t_expire;     /* expire after hold_time  */
+  struct isis_circuit *circuit;        /* back pointer */
+};
+
+struct isis_adjacency *isis_adj_lookup (const u_char * sysid, struct list *adjdb);
+struct isis_adjacency *isis_adj_lookup_snpa (const u_char * ssnpa,
+                                            struct list *adjdb);
+struct isis_adjacency *isis_new_adj (const u_char * id, const u_char * snpa, int level,
+                                    struct isis_circuit *circuit);
+void isis_delete_adj (void *adj);
+void isis_adj_state_change (struct isis_adjacency *adj,
+                           enum isis_adj_state state, const char *reason);
+void isis_adj_print (struct isis_adjacency *adj);
+int isis_adj_expire (struct thread *thread);
+void isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail);
+void isis_adj_build_neigh_list (struct list *adjdb, struct list *list);
+void isis_adj_build_up_list (struct list *adjdb, struct list *list);
+
+#endif /* ISIS_ADJACENCY_H */
diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
new file mode 100644 (file)
index 0000000..889b4c3
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_bpf.c
+ *
+ * Copyright (C) 2001,2002    Sampo Saaristo
+ *                            Tampere University of Technology      
+ *                            Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_BPF
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <net/bpf.h>
+
+#include "log.h"
+#include "network.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)
+};
+u_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_int blen, immediate, seesent;
+  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. 
+   */
+  immediate = 1;
+  if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & immediate) < 0)
+    {
+      zlog_warn ("failed to set BPF dev to immediate mode");
+    }
+
+#ifdef BIOCSSEESENT
+  /*
+   * We want to see only incoming packets
+   */
+  seesent = 0;
+  if (ioctl (fd, BIOCSSEESENT, (caddr_t) & seesent) < 0)
+    {
+      zlog_warn ("failed to set BPF dev to incoming only mode");
+    }
+#endif
+
+  /*
+   * ...but all of them
+   */
+  if (ioctl (fd, BIOCPROMISC) < 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;
+  ssize_t written;
+  size_t buflen;
+
+  buflen = stream_get_endp (circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN;
+  if (buflen > sizeof (sock_buff))
+    {
+      zlog_warn ("isis_send_pdu_bcast: sock_buff size %zu is less than "
+                "output pdu size %zu on circuit %s",
+                sizeof (sock_buff), buflen, circuit->interface->name);
+      return ISIS_WARNING;
+    }
+
+  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, buflen);
+  if (written < 0)
+    {
+      zlog_warn("IS-IS bpf: could not transmit packet on %s: %s",
+                circuit->interface->name, safe_strerror(errno));
+      if (ERRNO_IO_RETRY(errno))
+        return ISIS_WARNING;
+      return ISIS_ERROR;
+    }
+
+  return ISIS_OK;
+}
+
+int
+isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+{
+  return ISIS_OK;
+}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_BPF */
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
new file mode 100644 (file)
index 0000000..1d1a59e
--- /dev/null
@@ -0,0 +1,1423 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_circuit.h
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <zebra.h>
+#ifdef GNU_LINUX
+#include <net/ethernet.h>
+#else
+#include <netinet/if_ether.h>
+#endif
+
+#ifndef ETHER_ADDR_LEN
+#define        ETHER_ADDR_LEN  ETHERADDRL
+#endif
+
+#include "log.h"
+#include "memory.h"
+#include "if.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "vty.h"
+#include "hash.h"
+#include "prefix.h"
+#include "stream.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_events.h"
+#include "isisd/isis_te.h"
+
+/*
+ * Prototypes.
+ */
+int isis_interface_config_write(struct vty *);
+int isis_if_new_hook(struct interface *);
+int isis_if_delete_hook(struct interface *);
+
+struct isis_circuit *
+isis_circuit_new ()
+{
+  struct isis_circuit *circuit;
+  int i;
+
+  circuit = XCALLOC (MTYPE_ISIS_CIRCUIT, sizeof (struct isis_circuit));
+  if (circuit == NULL)
+    {
+      zlog_err ("Can't malloc isis circuit");
+      return NULL;
+    }
+
+  /*
+   * Default values
+   */
+  circuit->is_type = IS_LEVEL_1_AND_2;
+  circuit->flags = 0;
+  circuit->pad_hellos = 1;
+  for (i = 0; i < 2; i++)
+    {
+      circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL;
+      circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER;
+      circuit->csnp_interval[i] = DEFAULT_CSNP_INTERVAL;
+      circuit->psnp_interval[i] = DEFAULT_PSNP_INTERVAL;
+      circuit->priority[i] = DEFAULT_PRIORITY;
+      circuit->metric[i] = DEFAULT_CIRCUIT_METRIC;
+      circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC;
+    }
+
+  circuit->mtc = mpls_te_circuit_new();
+
+  return circuit;
+}
+
+void
+isis_circuit_del (struct isis_circuit *circuit)
+{
+  if (!circuit)
+    return;
+
+  isis_circuit_if_unbind (circuit, circuit->interface);
+
+  /* and lastly the circuit itself */
+  XFREE (MTYPE_ISIS_CIRCUIT, circuit);
+
+  return;
+}
+
+void
+isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area)
+{
+  assert (area);
+  circuit->area = area;
+
+  /*
+   * Whenever the is-type of an area is changed, the is-type of each circuit
+   * in that area is updated to a non-empty subset of the area is-type.
+   * Inversely, when configuring a new circuit, this property should be
+   * ensured as well.
+   */
+  if (area->is_type != IS_LEVEL_1_AND_2)
+    circuit->is_type = area->is_type;
+
+  /*
+   * Add the circuit into area
+   */
+  listnode_add (area->circuit_list, circuit);
+
+  circuit->idx = flags_get_index (&area->flags);
+
+  return;
+}
+
+void
+isis_circuit_deconfigure (struct isis_circuit *circuit, struct isis_area *area)
+{
+  /* Free the index of SRM and SSN flags */
+  flags_free_index (&area->flags, circuit->idx);
+  circuit->idx = 0;
+  /* Remove circuit from area */
+  assert (circuit->area == area);
+  listnode_delete (area->circuit_list, circuit);
+  circuit->area = NULL;
+
+  return;
+}
+
+struct isis_circuit *
+circuit_lookup_by_ifp (struct interface *ifp, struct list *list)
+{
+  struct isis_circuit *circuit = NULL;
+  struct listnode *node;
+
+  if (!list)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, circuit))
+    if (circuit->interface == ifp)
+      {
+        assert (ifp->info == circuit);
+        return circuit;
+      }
+
+  return NULL;
+}
+
+struct isis_circuit *
+circuit_scan_by_ifp (struct interface *ifp)
+{
+  struct isis_area *area;
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (ifp->info)
+    return (struct isis_circuit *)ifp->info;
+
+  if (isis->area_list)
+    {
+      for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+        {
+          circuit = circuit_lookup_by_ifp (ifp, area->circuit_list);
+          if (circuit)
+            return circuit;
+        }
+    }
+  return circuit_lookup_by_ifp (ifp, isis->init_circ_list);
+}
+
+void
+isis_circuit_add_addr (struct isis_circuit *circuit,
+                      struct connected *connected)
+{
+  struct listnode *node;
+  struct prefix_ipv4 *ipv4;
+  u_char buf[BUFSIZ];
+#ifdef HAVE_IPV6
+  struct prefix_ipv6 *ipv6;
+#endif /* HAVE_IPV6 */
+
+  memset (&buf, 0, BUFSIZ);
+  if (connected->address->family == AF_INET)
+    {
+      u_int32_t addr = connected->address->u.prefix4.s_addr;
+      addr = ntohl (addr);
+      if (IPV4_NET0(addr) ||
+          IPV4_NET127(addr) ||
+          IN_CLASSD(addr) ||
+          IPV4_LINKLOCAL(addr))
+        return;
+
+      for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ipv4))
+        if (prefix_same ((struct prefix *) ipv4, connected->address))
+          return;
+
+      ipv4 = prefix_ipv4_new ();
+      ipv4->prefixlen = connected->address->prefixlen;
+      ipv4->prefix = connected->address->u.prefix4;
+      listnode_add (circuit->ip_addrs, ipv4);
+
+      /* Update MPLS TE Local IP address parameter */
+      set_circuitparams_local_ipaddr (circuit->mtc, ipv4->prefix);
+
+      if (circuit->area)
+        lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+
+#ifdef EXTREME_DEBUG
+      prefix2str (connected->address, buf, BUFSIZ);
+      zlog_debug ("Added IP address %s to circuit %d", buf,
+                circuit->circuit_id);
+#endif /* EXTREME_DEBUG */
+    }
+#ifdef HAVE_IPV6
+  if (connected->address->family == AF_INET6)
+    {
+      if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6))
+        return;
+
+      for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_link, node, ipv6))
+        if (prefix_same ((struct prefix *) ipv6, connected->address))
+          return;
+      for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, node, ipv6))
+        if (prefix_same ((struct prefix *) ipv6, connected->address))
+          return;
+
+      ipv6 = prefix_ipv6_new ();
+      ipv6->prefixlen = connected->address->prefixlen;
+      ipv6->prefix = connected->address->u.prefix6;
+
+      if (IN6_IS_ADDR_LINKLOCAL (&ipv6->prefix))
+       listnode_add (circuit->ipv6_link, ipv6);
+      else
+       listnode_add (circuit->ipv6_non_link, ipv6);
+      if (circuit->area)
+        lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+
+#ifdef EXTREME_DEBUG
+      prefix2str (connected->address, buf, BUFSIZ);
+      zlog_debug ("Added IPv6 address %s to circuit %d", buf,
+                circuit->circuit_id);
+#endif /* EXTREME_DEBUG */
+    }
+#endif /* HAVE_IPV6 */
+  return;
+}
+
+void
+isis_circuit_del_addr (struct isis_circuit *circuit,
+                      struct connected *connected)
+{
+  struct prefix_ipv4 *ipv4, *ip = NULL;
+  struct listnode *node;
+  u_char buf[BUFSIZ];
+#ifdef HAVE_IPV6
+  struct prefix_ipv6 *ipv6, *ip6 = NULL;
+  int found = 0;
+#endif /* HAVE_IPV6 */
+
+  memset (&buf, 0, BUFSIZ);
+  if (connected->address->family == AF_INET)
+    {
+      ipv4 = prefix_ipv4_new ();
+      ipv4->prefixlen = connected->address->prefixlen;
+      ipv4->prefix = connected->address->u.prefix4;
+
+      for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ip))
+        if (prefix_same ((struct prefix *) ip, (struct prefix *) ipv4))
+          break;
+
+      if (ip)
+       {
+         listnode_delete (circuit->ip_addrs, ip);
+          if (circuit->area)
+            lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+       }
+      else
+       {
+         prefix2str (connected->address, (char *)buf, BUFSIZ);
+         zlog_warn ("Nonexitant ip address %s removal attempt from \
+                      circuit %d", buf, circuit->circuit_id);
+         zlog_warn ("Current ip addresses on %s:", circuit->interface->name);
+         for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ip))
+           {
+             prefix2str((struct prefix*)ip, (char *)buf, BUFSIZ);
+             zlog_warn("  %s", buf);
+           }
+         zlog_warn("End of addresses");
+       }
+
+      prefix_ipv4_free (ipv4);
+    }
+#ifdef HAVE_IPV6
+  if (connected->address->family == AF_INET6)
+    {
+      ipv6 = prefix_ipv6_new ();
+      ipv6->prefixlen = connected->address->prefixlen;
+      ipv6->prefix = connected->address->u.prefix6;
+
+      if (IN6_IS_ADDR_LINKLOCAL (&ipv6->prefix))
+       {
+         for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_link, node, ip6))
+           {
+             if (prefix_same ((struct prefix *) ip6, (struct prefix *) ipv6))
+               break;
+           }
+         if (ip6)
+           {
+             listnode_delete (circuit->ipv6_link, ip6);
+             found = 1;
+           }
+       }
+      else
+       {
+         for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, node, ip6))
+           {
+             if (prefix_same ((struct prefix *) ip6, (struct prefix *) ipv6))
+               break;
+           }
+         if (ip6)
+           {
+             listnode_delete (circuit->ipv6_non_link, ip6);
+             found = 1;
+           }
+       }
+
+      if (!found)
+       {
+         prefix2str (connected->address, (char *)buf, BUFSIZ);
+         zlog_warn ("Nonexitant ip address %s removal attempt from \
+                     circuit %d", buf, circuit->circuit_id);
+         zlog_warn ("Current ip addresses on %s:", circuit->interface->name);
+         for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ip6))
+           {
+             prefix2str((struct prefix*)ip6, (char *)buf, BUFSIZ);
+             zlog_warn("  %s", buf);
+           }
+         zlog_warn(" -----");
+         for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ip6))
+           {
+             prefix2str((struct prefix*)ip6, (char *)buf, BUFSIZ);
+             zlog_warn("  %s", buf);
+           }
+         zlog_warn("End of addresses");
+       }
+      else if (circuit->area)
+         lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+
+      prefix_ipv6_free (ipv6);
+    }
+#endif /* HAVE_IPV6 */
+  return;
+}
+
+static u_char
+isis_circuit_id_gen (struct interface *ifp)
+{
+  u_char id = 0;
+  char ifname[16];
+  unsigned int i;
+  int start = -1, end = -1;
+
+  /*
+   * Get a stable circuit id from ifname. This makes
+   * the ifindex from flapping when netdevs are created
+   * and deleted on the fly. Note that this circuit id
+   * is used in pseudo lsps so it is better to be stable.
+   * The following code works on any reasonanle ifname
+   * like: eth1 or trk-1.1 etc.
+   */
+  for (i = 0; i < strlen (ifp->name); i++)
+    {
+      if (isdigit((unsigned char)ifp->name[i]))
+        {
+          if (start < 0)
+            {
+              start = i;
+              end = i + 1;
+            }
+          else
+            {
+              end = i + 1;
+            }
+        }
+      else if (start >= 0)
+        break;
+    }
+
+  if ((start >= 0) && (end >= start) && (end - start) < 16)
+    {
+      memset (ifname, 0, 16);
+      strncpy (ifname, &ifp->name[start], end - start);
+      id = (u_char)atoi(ifname);
+    }
+
+  /* Try to be unique. */
+  if (!id)
+    id = (u_char)((ifp->ifindex & 0xff) | 0x80);
+
+  return id;
+}
+
+void
+isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct connected *conn;
+
+  circuit->circuit_id = isis_circuit_id_gen (ifp);
+
+  isis_circuit_if_bind (circuit, ifp);
+  /*  isis_circuit_update_addrs (circuit, ifp); */
+
+  if (if_is_broadcast (ifp))
+    {
+      if (circuit->circ_type_config == CIRCUIT_T_P2P)
+        circuit->circ_type = CIRCUIT_T_P2P;
+      else
+        circuit->circ_type = CIRCUIT_T_BROADCAST;
+    }
+  else if (if_is_pointopoint (ifp))
+    {
+      circuit->circ_type = CIRCUIT_T_P2P;
+    }
+  else if (if_is_loopback (ifp))
+    {
+      circuit->circ_type = CIRCUIT_T_LOOPBACK;
+      circuit->is_passive = 1;
+    }
+  else
+    {
+      /* It's normal in case of loopback etc. */
+      if (isis->debugs & DEBUG_EVENTS)
+        zlog_debug ("isis_circuit_if_add: unsupported media");
+      circuit->circ_type = CIRCUIT_T_UNKNOWN;
+    }
+
+  circuit->ip_addrs = list_new ();
+#ifdef HAVE_IPV6
+  circuit->ipv6_link = list_new ();
+  circuit->ipv6_non_link = list_new ();
+#endif /* HAVE_IPV6 */
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, conn))
+    isis_circuit_add_addr (circuit, conn);
+
+  return;
+}
+
+void
+isis_circuit_if_del (struct isis_circuit *circuit, struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct connected *conn;
+
+  assert (circuit->interface == ifp);
+
+  /* destroy addresses */
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, conn))
+    isis_circuit_del_addr (circuit, conn);
+
+  if (circuit->ip_addrs)
+    {
+      assert (listcount(circuit->ip_addrs) == 0);
+      list_delete (circuit->ip_addrs);
+      circuit->ip_addrs = NULL;
+    }
+
+#ifdef HAVE_IPV6
+  if (circuit->ipv6_link)
+    {
+      assert (listcount(circuit->ipv6_link) == 0);
+      list_delete (circuit->ipv6_link);
+      circuit->ipv6_link = NULL;
+    }
+
+  if (circuit->ipv6_non_link)
+    {
+      assert (listcount(circuit->ipv6_non_link) == 0);
+      list_delete (circuit->ipv6_non_link);
+      circuit->ipv6_non_link = NULL;
+    }
+#endif /* HAVE_IPV6 */
+
+  circuit->circ_type = CIRCUIT_T_UNKNOWN;
+  circuit->circuit_id = 0;
+
+  return;
+}
+
+void
+isis_circuit_if_bind (struct isis_circuit *circuit, struct interface *ifp)
+{
+  assert (circuit != NULL);
+  assert (ifp != NULL);
+  if (circuit->interface)
+    assert (circuit->interface == ifp);
+  else
+    circuit->interface = ifp;
+  if (ifp->info)
+    assert (ifp->info == circuit);
+  else
+    ifp->info = circuit;
+  isis_link_params_update (circuit, ifp);
+}
+
+void
+isis_circuit_if_unbind (struct isis_circuit *circuit, struct interface *ifp)
+{
+  assert (circuit != NULL);
+  assert (ifp != NULL);
+  assert (circuit->interface == ifp);
+  assert (ifp->info == circuit);
+  circuit->interface = NULL;
+  ifp->info = NULL;
+}
+
+static void
+isis_circuit_update_all_srmflags (struct isis_circuit *circuit, int is_set)
+{
+  struct isis_area *area;
+  struct isis_lsp *lsp;
+  dnode_t *dnode, *dnode_next;
+  int level;
+
+  assert (circuit);
+  area = circuit->area;
+  assert (area);
+  for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++)
+    {
+      if (level & circuit->is_type)
+        {
+          if (area->lspdb[level - 1] &&
+              dict_count (area->lspdb[level - 1]) > 0)
+            {
+              for (dnode = dict_first (area->lspdb[level - 1]);
+                   dnode != NULL; dnode = dnode_next)
+                {
+                  dnode_next = dict_next (area->lspdb[level - 1], dnode);
+                  lsp = dnode_get (dnode);
+                  if (is_set)
+                    {
+                      ISIS_SET_FLAG (lsp->SRMflags, circuit);
+                    }
+                  else
+                    {
+                      ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+                    }
+                }
+            }
+        }
+    }
+}
+
+size_t
+isis_circuit_pdu_size(struct isis_circuit *circuit)
+{
+  return ISO_MTU(circuit);
+}
+
+void
+isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream)
+{
+  size_t stream_size = isis_circuit_pdu_size(circuit);
+
+  if (!*stream)
+    {
+      *stream = stream_new(stream_size);
+    }
+  else
+    {
+      if (STREAM_SIZE(*stream) != stream_size)
+        stream_resize(*stream, stream_size);
+      stream_reset(*stream);
+    }
+}
+
+int
+isis_circuit_up (struct isis_circuit *circuit)
+{
+  int retv;
+
+  /* Set the flags for all the lsps of the circuit. */
+  isis_circuit_update_all_srmflags (circuit, 1);
+
+  if (circuit->state == C_STATE_UP)
+    return ISIS_OK;
+
+  if (circuit->is_passive)
+    return ISIS_OK;
+
+  if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit))
+    {
+      zlog_err("Interface MTU %zu on %s is too low to support area lsp mtu %u!",
+               isis_circuit_pdu_size(circuit), circuit->interface->name,
+               circuit->area->lsp_mtu);
+      isis_circuit_update_all_srmflags(circuit, 0);
+      return ISIS_ERROR;
+    }
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      /*
+       * Get the Hardware Address
+       */
+      if (circuit->interface->hw_addr_len != ETH_ALEN)
+        {
+          zlog_warn ("unsupported link layer");
+        }
+      else
+        {
+          memcpy (circuit->u.bc.snpa, circuit->interface->hw_addr, ETH_ALEN);
+        }
+#ifdef EXTREME_DEGUG
+      zlog_debug ("isis_circuit_if_add: if_id %d, isomtu %d snpa %s",
+                  circuit->interface->ifindex, ISO_MTU (circuit),
+                  snpa_print (circuit->u.bc.snpa));
+#endif /* EXTREME_DEBUG */
+
+      circuit->u.bc.adjdb[0] = list_new ();
+      circuit->u.bc.adjdb[1] = list_new ();
+
+      /*
+       * ISO 10589 - 8.4.1 Enabling of broadcast circuits
+       */
+
+      /* initilizing the hello sending threads
+       * for a broadcast IF
+       */
+
+      /* 8.4.1 a) commence sending of IIH PDUs */
+
+      if (circuit->is_type & IS_LEVEL_1)
+        {
+          thread_add_event (master, send_lan_l1_hello, circuit, 0);
+          circuit->u.bc.lan_neighs[0] = list_new ();
+        }
+
+      if (circuit->is_type & IS_LEVEL_2)
+        {
+          thread_add_event (master, send_lan_l2_hello, circuit, 0);
+          circuit->u.bc.lan_neighs[1] = list_new ();
+        }
+
+      /* 8.4.1 b) FIXME: solicit ES - 8.4.6 */
+      /* 8.4.1 c) FIXME: listen for ESH PDUs */
+
+      /* 8.4.1 d) */
+      /* dr election will commence in... */
+      if (circuit->is_type & IS_LEVEL_1)
+        THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
+            circuit, 2 * circuit->hello_interval[0]);
+      if (circuit->is_type & IS_LEVEL_2)
+        THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
+            circuit, 2 * circuit->hello_interval[1]);
+    }
+  else
+    {
+      /* initializing the hello send threads
+       * for a ptp IF
+       */
+      circuit->u.p2p.neighbor = NULL;
+      thread_add_event (master, send_p2p_hello, circuit, 0);
+    }
+
+  /* initializing PSNP timers */
+  if (circuit->is_type & IS_LEVEL_1)
+    THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit,
+                     isis_jitter (circuit->psnp_interval[0], PSNP_JITTER));
+
+  if (circuit->is_type & IS_LEVEL_2)
+    THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit,
+                     isis_jitter (circuit->psnp_interval[1], PSNP_JITTER));
+
+  /* unified init for circuits; ignore warnings below this level */
+  retv = isis_sock_init (circuit);
+  if (retv != ISIS_OK)
+    {
+      isis_circuit_down (circuit);
+      return retv;
+    }
+
+  /* initialize the circuit streams after opening connection */
+  isis_circuit_stream(circuit, &circuit->rcv_stream);
+  isis_circuit_stream(circuit, &circuit->snd_stream);
+
+#ifdef GNU_LINUX
+  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
+
+  circuit->lsp_queue = list_new ();
+  circuit->lsp_queue_last_cleared = time (NULL);
+
+  return ISIS_OK;
+}
+
+void
+isis_circuit_down (struct isis_circuit *circuit)
+{
+  if (circuit->state != C_STATE_UP)
+    return;
+
+  /* Clear the flags for all the lsps of the circuit. */
+  isis_circuit_update_all_srmflags (circuit, 0);
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      /* destroy neighbour lists */
+      if (circuit->u.bc.lan_neighs[0])
+        {
+          list_delete (circuit->u.bc.lan_neighs[0]);
+          circuit->u.bc.lan_neighs[0] = NULL;
+        }
+      if (circuit->u.bc.lan_neighs[1])
+        {
+          list_delete (circuit->u.bc.lan_neighs[1]);
+          circuit->u.bc.lan_neighs[1] = NULL;
+        }
+      /* destroy adjacency databases */
+      if (circuit->u.bc.adjdb[0])
+        {
+          circuit->u.bc.adjdb[0]->del = isis_delete_adj;
+          list_delete (circuit->u.bc.adjdb[0]);
+          circuit->u.bc.adjdb[0] = NULL;
+        }
+      if (circuit->u.bc.adjdb[1])
+        {
+          circuit->u.bc.adjdb[1]->del = isis_delete_adj;
+          list_delete (circuit->u.bc.adjdb[1]);
+          circuit->u.bc.adjdb[1] = NULL;
+        }
+      if (circuit->u.bc.is_dr[0])
+        {
+          isis_dr_resign (circuit, 1);
+          circuit->u.bc.is_dr[0] = 0;
+        }
+      memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
+      if (circuit->u.bc.is_dr[1])
+        {
+          isis_dr_resign (circuit, 2);
+          circuit->u.bc.is_dr[1] = 0;
+        }
+      memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
+      memset (circuit->u.bc.snpa, 0, ETH_ALEN);
+
+      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]);
+      circuit->lsp_regenerate_pending[0] = 0;
+      circuit->lsp_regenerate_pending[1] = 0;
+    }
+  else if (circuit->circ_type == CIRCUIT_T_P2P)
+    {
+      isis_delete_adj (circuit->u.p2p.neighbor);
+      circuit->u.p2p.neighbor = NULL;
+      THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello);
+    }
+
+  /* Cancel all active threads */
+  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]);
+  THREAD_OFF (circuit->t_read);
+
+  if (circuit->lsp_queue)
+    {
+      circuit->lsp_queue->del = NULL;
+      list_delete (circuit->lsp_queue);
+      circuit->lsp_queue = NULL;
+    }
+
+  /* send one gratuitous hello to spead up convergence */
+  if (circuit->is_type & IS_LEVEL_1)
+    send_hello (circuit, IS_LEVEL_1);
+  if (circuit->is_type & IS_LEVEL_2)
+    send_hello (circuit, IS_LEVEL_2);
+
+  circuit->upadjcount[0] = 0;
+  circuit->upadjcount[1] = 0;
+
+  /* close the socket */
+  if (circuit->fd)
+    {
+      close (circuit->fd);
+      circuit->fd = 0;
+    }
+
+  if (circuit->rcv_stream != NULL)
+    {
+      stream_free (circuit->rcv_stream);
+      circuit->rcv_stream = NULL;
+    }
+
+  if (circuit->snd_stream != NULL)
+    {
+      stream_free (circuit->snd_stream);
+      circuit->snd_stream = NULL;
+    }
+
+  thread_cancel_event (master, circuit);
+
+  return;
+}
+
+void
+circuit_update_nlpids (struct isis_circuit *circuit)
+{
+  circuit->nlpids.count = 0;
+
+  if (circuit->ip_router)
+    {
+      circuit->nlpids.nlpids[0] = NLPID_IP;
+      circuit->nlpids.count++;
+    }
+#ifdef HAVE_IPV6
+  if (circuit->ipv6_router)
+    {
+      circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6;
+      circuit->nlpids.count++;
+    }
+#endif /* HAVE_IPV6 */
+  return;
+}
+
+void
+isis_circuit_print_vty (struct isis_circuit *circuit, struct vty *vty,
+                        char detail)
+{
+  if (detail == ISIS_UI_LEVEL_BRIEF)
+    {
+      vty_out (vty, "  %-12s", circuit->interface->name);
+      vty_out (vty, "0x%-7x", circuit->circuit_id);
+      vty_out (vty, "%-9s", circuit_state2string (circuit->state));
+      vty_out (vty, "%-9s", circuit_type2string (circuit->circ_type));
+      vty_out (vty, "%-9s", circuit_t2string (circuit->is_type));
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  if (detail == ISIS_UI_LEVEL_DETAIL)
+    {
+      struct listnode *node;
+      struct prefix *ip_addr;
+      u_char buf[BUFSIZ];
+
+      vty_out (vty, "  Interface: %s", circuit->interface->name);
+      vty_out (vty, ", State: %s", circuit_state2string (circuit->state));
+      if (circuit->is_passive)
+        vty_out (vty, ", Passive");
+      else
+        vty_out (vty, ", Active");
+      vty_out (vty, ", Circuit Id: 0x%x", circuit->circuit_id);
+      vty_out (vty, "%s", VTY_NEWLINE);
+      vty_out (vty, "    Type: %s", circuit_type2string (circuit->circ_type));
+      vty_out (vty, ", Level: %s", circuit_t2string (circuit->is_type));
+      if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+        vty_out (vty, ", SNPA: %-10s", snpa_print (circuit->u.bc.snpa));
+      vty_out (vty, "%s", VTY_NEWLINE);
+      if (circuit->is_type & IS_LEVEL_1)
+        {
+          vty_out (vty, "    Level-1 Information:%s", VTY_NEWLINE);
+          if (circuit->area->newmetric)
+            vty_out (vty, "      Metric: %d", circuit->te_metric[0]);
+          else
+            vty_out (vty, "      Metric: %d",
+                     circuit->metric[0]);
+          if (!circuit->is_passive)
+            {
+              vty_out (vty, ", Active neighbors: %u%s",
+                       circuit->upadjcount[0], VTY_NEWLINE);
+              vty_out (vty, "      Hello interval: %u, "
+                            "Holddown count: %u %s%s",
+                       circuit->hello_interval[0],
+                       circuit->hello_multiplier[0],
+                       (circuit->pad_hellos ? "(pad)" : "(no-pad)"),
+                       VTY_NEWLINE);
+              vty_out (vty, "      CNSP interval: %u, "
+                            "PSNP interval: %u%s",
+                       circuit->csnp_interval[0],
+                       circuit->psnp_interval[0], VTY_NEWLINE);
+              if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+                vty_out (vty, "      LAN Priority: %u, %s%s",
+                         circuit->priority[0],
+                         (circuit->u.bc.is_dr[0] ? \
+                          "is DIS" : "is not DIS"), VTY_NEWLINE);
+            }
+          else
+            {
+              vty_out (vty, "%s", VTY_NEWLINE);
+            }
+        }
+      if (circuit->is_type & IS_LEVEL_2)
+        {
+          vty_out (vty, "    Level-2 Information:%s", VTY_NEWLINE);
+          if (circuit->area->newmetric)
+            vty_out (vty, "      Metric: %d", circuit->te_metric[1]);
+          else
+            vty_out (vty, "      Metric: %d",
+                     circuit->metric[1]);
+          if (!circuit->is_passive)
+            {
+              vty_out (vty, ", Active neighbors: %u%s",
+                       circuit->upadjcount[1], VTY_NEWLINE);
+              vty_out (vty, "      Hello interval: %u, "
+                            "Holddown count: %u %s%s",
+                       circuit->hello_interval[1],
+                       circuit->hello_multiplier[1],
+                       (circuit->pad_hellos ? "(pad)" : "(no-pad)"),
+                       VTY_NEWLINE);
+              vty_out (vty, "      CNSP interval: %u, "
+                            "PSNP interval: %u%s",
+                       circuit->csnp_interval[1],
+                       circuit->psnp_interval[1], VTY_NEWLINE);
+              if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+                vty_out (vty, "      LAN Priority: %u, %s%s",
+                         circuit->priority[1],
+                         (circuit->u.bc.is_dr[1] ? \
+                          "is DIS" : "is not DIS"), VTY_NEWLINE);
+            }
+          else
+            {
+              vty_out (vty, "%s", VTY_NEWLINE);
+            }
+        }
+      if (circuit->ip_addrs && listcount (circuit->ip_addrs) > 0)
+        {
+          vty_out (vty, "    IP Prefix(es):%s", VTY_NEWLINE);
+          for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ip_addr))
+            {
+              prefix2str (ip_addr, (char*)buf, BUFSIZ),
+              vty_out (vty, "      %s%s", buf, VTY_NEWLINE);
+            }
+        }
+      if (circuit->ipv6_link && listcount(circuit->ipv6_link) > 0)
+        {
+          vty_out(vty, "    IPv6 Link-Locals:%s", VTY_NEWLINE);
+          for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ip_addr))
+            {
+              prefix2str(ip_addr, (char*)buf, BUFSIZ),
+              vty_out(vty, "      %s%s", buf, VTY_NEWLINE);
+            }
+        }
+      if (circuit->ipv6_non_link && listcount(circuit->ipv6_non_link) > 0)
+        {
+          vty_out(vty, "    IPv6 Prefixes:%s", VTY_NEWLINE);
+          for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ip_addr))
+            {
+              prefix2str(ip_addr, (char*)buf, BUFSIZ),
+              vty_out(vty, "      %s%s", buf, VTY_NEWLINE);
+            }
+        }
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return;
+}
+
+int
+isis_interface_config_write (struct vty *vty)
+{
+  int write = 0;
+  struct listnode *node, *node2;
+  struct interface *ifp;
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+  int i;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      /* IF name */
+      vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+      write++;
+      /* IF desc */
+      if (ifp->desc)
+        {
+          vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE);
+          write++;
+        }
+      /* ISIS Circuit */
+      for (ALL_LIST_ELEMENTS_RO (isis->area_list, node2, area))
+        {
+          circuit = circuit_lookup_by_ifp (ifp, area->circuit_list);
+          if (circuit == NULL)
+            continue;
+          if (circuit->ip_router)
+            {
+              vty_out (vty, " ip router isis %s%s", area->area_tag,
+                       VTY_NEWLINE);
+              write++;
+            }
+          if (circuit->is_passive)
+            {
+              vty_out (vty, " isis passive%s", VTY_NEWLINE);
+              write++;
+            }
+          if (circuit->circ_type_config == CIRCUIT_T_P2P)
+            {
+              vty_out (vty, " isis network point-to-point%s", VTY_NEWLINE);
+              write++;
+            }
+#ifdef HAVE_IPV6
+          if (circuit->ipv6_router)
+            {
+              vty_out (vty, " ipv6 router isis %s%s", area->area_tag,
+                  VTY_NEWLINE);
+              write++;
+            }
+#endif /* HAVE_IPV6 */
+
+          /* ISIS - circuit type */
+          if (circuit->is_type == IS_LEVEL_1)
+            {
+              vty_out (vty, " isis circuit-type level-1%s", VTY_NEWLINE);
+              write++;
+            }
+          else
+            {
+              if (circuit->is_type == IS_LEVEL_2)
+                {
+                  vty_out (vty, " isis circuit-type level-2-only%s",
+                           VTY_NEWLINE);
+                  write++;
+                }
+            }
+
+          /* ISIS - CSNP interval */
+          if (circuit->csnp_interval[0] == circuit->csnp_interval[1])
+            {
+              if (circuit->csnp_interval[0] != DEFAULT_CSNP_INTERVAL)
+                {
+                  vty_out (vty, " isis csnp-interval %d%s",
+                           circuit->csnp_interval[0], VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+          {
+            for (i = 0; i < 2; i++)
+              {
+                if (circuit->csnp_interval[i] != DEFAULT_CSNP_INTERVAL)
+                  {
+                    vty_out (vty, " isis csnp-interval %d level-%d%s",
+                             circuit->csnp_interval[i], i + 1, VTY_NEWLINE);
+                    write++;
+                  }
+              }
+          }
+
+          /* ISIS - PSNP interval */
+          if (circuit->psnp_interval[0] == circuit->psnp_interval[1])
+            {
+              if (circuit->psnp_interval[0] != DEFAULT_PSNP_INTERVAL)
+                {
+                  vty_out (vty, " isis psnp-interval %d%s",
+                           circuit->psnp_interval[0], VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  if (circuit->psnp_interval[i] != DEFAULT_PSNP_INTERVAL)
+                  {
+                    vty_out (vty, " isis psnp-interval %d level-%d%s",
+                             circuit->psnp_interval[i], i + 1, VTY_NEWLINE);
+                    write++;
+                  }
+                }
+            }
+
+          /* ISIS - Hello padding - Defaults to true so only display if false */
+          if (circuit->pad_hellos == 0)
+            {
+              vty_out (vty, " no isis hello padding%s", VTY_NEWLINE);
+              write++;
+            }
+
+          /* ISIS - Hello interval */
+          if (circuit->hello_interval[0] == circuit->hello_interval[1])
+            {
+              if (circuit->hello_interval[0] != DEFAULT_HELLO_INTERVAL)
+                {
+                  vty_out (vty, " isis hello-interval %d%s",
+                           circuit->hello_interval[0], VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  if (circuit->hello_interval[i] != DEFAULT_HELLO_INTERVAL)
+                    {
+                      vty_out (vty, " isis hello-interval %d level-%d%s",
+                               circuit->hello_interval[i], i + 1, VTY_NEWLINE);
+                      write++;
+                    }
+                }
+            }
+
+          /* ISIS - Hello Multiplier */
+          if (circuit->hello_multiplier[0] == circuit->hello_multiplier[1])
+            {
+              if (circuit->hello_multiplier[0] != DEFAULT_HELLO_MULTIPLIER)
+                {
+                  vty_out (vty, " isis hello-multiplier %d%s",
+                           circuit->hello_multiplier[0], VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  if (circuit->hello_multiplier[i] != DEFAULT_HELLO_MULTIPLIER)
+                    {
+                      vty_out (vty, " isis hello-multiplier %d level-%d%s",
+                               circuit->hello_multiplier[i], i + 1,
+                               VTY_NEWLINE);
+                      write++;
+                    }
+                }
+            }
+
+          /* ISIS - Priority */
+          if (circuit->priority[0] == circuit->priority[1])
+            {
+              if (circuit->priority[0] != DEFAULT_PRIORITY)
+                {
+                  vty_out (vty, " isis priority %d%s",
+                           circuit->priority[0], VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  if (circuit->priority[i] != DEFAULT_PRIORITY)
+                    {
+                      vty_out (vty, " isis priority %d level-%d%s",
+                               circuit->priority[i], i + 1, VTY_NEWLINE);
+                      write++;
+                    }
+                }
+            }
+
+          /* ISIS - Metric */
+          if (circuit->te_metric[0] == circuit->te_metric[1])
+            {
+              if (circuit->te_metric[0] != DEFAULT_CIRCUIT_METRIC)
+                {
+                  vty_out (vty, " isis metric %d%s", circuit->te_metric[0],
+                           VTY_NEWLINE);
+                  write++;
+                }
+            }
+          else
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  if (circuit->te_metric[i] != DEFAULT_CIRCUIT_METRIC)
+                    {
+                      vty_out (vty, " isis metric %d level-%d%s",
+                               circuit->te_metric[i], i + 1, VTY_NEWLINE);
+                      write++;
+                    }
+                }
+            }
+          if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+            {
+              vty_out (vty, " isis password md5 %s%s", circuit->passwd.passwd,
+                       VTY_NEWLINE);
+              write++;
+            }
+          else if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT)
+            {
+              vty_out (vty, " isis password clear %s%s", circuit->passwd.passwd,
+                       VTY_NEWLINE);
+              write++;
+            }
+        }
+      vty_out (vty, "!%s", VTY_NEWLINE);
+    }
+
+  return write;
+}
+
+struct isis_circuit *
+isis_circuit_create (struct isis_area *area, struct interface *ifp)
+{
+  struct isis_circuit *circuit = circuit_scan_by_ifp (ifp);
+  if (circuit && circuit->area)
+    return NULL;
+  circuit = isis_csm_state_change (ISIS_ENABLE, circuit, area);
+  if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP)
+    return circuit;
+  isis_circuit_if_bind (circuit, ifp);
+  return circuit;
+}
+
+void
+isis_circuit_af_set (struct isis_circuit *circuit, bool ip_router, bool ipv6_router)
+{
+  struct isis_area *area = circuit->area;
+  bool change = circuit->ip_router != ip_router || circuit->ipv6_router != ipv6_router;
+  bool was_enabled = !!circuit->area;
+
+  area->ip_circuits   += ip_router   - circuit->ip_router;
+  area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
+  circuit->ip_router   = ip_router;
+  circuit->ipv6_router = ipv6_router;
+
+  if (!change)
+    return;
+
+  circuit_update_nlpids (circuit);
+
+  if (!ip_router && !ipv6_router)
+    isis_csm_state_change (ISIS_DISABLE, circuit, area);
+  else if (!was_enabled)
+    isis_csm_state_change (ISIS_ENABLE, circuit, area);
+  else
+    lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
+}
+
+int
+isis_circuit_passive_set (struct isis_circuit *circuit, bool passive)
+{
+  if (circuit->is_passive == passive)
+    return 0;
+
+  if (if_is_loopback (circuit->interface) && !passive)
+    return -1;
+
+  if (circuit->state != C_STATE_UP)
+    {
+      circuit->is_passive = passive;
+    }
+  else
+    {
+      struct isis_area *area = circuit->area;
+      isis_csm_state_change (ISIS_DISABLE, circuit, area);
+      circuit->is_passive = passive;
+      isis_csm_state_change (ISIS_ENABLE, circuit, area);
+    }
+
+  return 0;
+}
+
+int
+isis_circuit_metric_set (struct isis_circuit *circuit, int level, int metric)
+{
+  assert (level == IS_LEVEL_1 || level == IS_LEVEL_2);
+  if (metric > MAX_WIDE_LINK_METRIC)
+    return -1;
+  if (circuit->area && circuit->area->oldmetric
+      && metric > MAX_NARROW_LINK_METRIC)
+    return -1;
+
+  circuit->te_metric[level - 1] = metric;
+  circuit->metric[level - 1] = metric;
+
+  if (circuit->area)
+    lsp_regenerate_schedule (circuit->area, level, 0);
+  return 0;
+}
+
+int
+isis_circuit_passwd_unset (struct isis_circuit *circuit)
+{
+  memset(&circuit->passwd, 0, sizeof(circuit->passwd));
+  return 0;
+}
+
+static int
+isis_circuit_passwd_set (struct isis_circuit *circuit, u_char passwd_type, const char *passwd)
+{
+  int len;
+
+  if (!passwd)
+    return -1;
+
+  len = strlen(passwd);
+  if (len > 254)
+    return -1;
+
+  circuit->passwd.len = len;
+  strncpy((char *)circuit->passwd.passwd, passwd, 255);
+  circuit->passwd.type = passwd_type;
+  return 0;
+}
+
+int
+isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd)
+{
+  return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_CLEARTXT, passwd);
+}
+
+int
+isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd)
+{
+  return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_HMAC_MD5, passwd);
+}
+struct cmd_node interface_node = {
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1,
+};
+
+int
+isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
+{
+  /* Changing the network type to/of loopback or unknown interfaces
+   * is not supported. */
+  if (circ_type == CIRCUIT_T_UNKNOWN
+      || circ_type == CIRCUIT_T_LOOPBACK
+      || circuit->circ_type == CIRCUIT_T_LOOPBACK)
+    {
+      if (circuit->circ_type != circ_type)
+        return -1;
+      else
+        return 0;
+    }
+
+  if (circuit->circ_type == circ_type)
+    return 0;
+
+  if (circuit->state != C_STATE_UP)
+    {
+      circuit->circ_type = circ_type;
+      circuit->circ_type_config = circ_type;
+    }
+  else
+    {
+      struct isis_area *area = circuit->area;
+      if (circ_type == CIRCUIT_T_BROADCAST
+          && !if_is_broadcast(circuit->interface))
+        return -1;
+
+      isis_csm_state_change(ISIS_DISABLE, circuit, area);
+      circuit->circ_type = circ_type;
+      circuit->circ_type_config = circ_type;
+      isis_csm_state_change(ISIS_ENABLE, circuit, area);
+    }
+  return 0;
+}
+
+int
+isis_if_new_hook (struct interface *ifp)
+{
+  return 0;
+}
+
+int
+isis_if_delete_hook (struct interface *ifp)
+{
+  struct isis_circuit *circuit;
+  /* Clean up the circuit data */
+  if (ifp && ifp->info)
+    {
+      circuit = ifp->info;
+      isis_csm_state_change (IF_DOWN_FROM_Z, circuit, circuit->area);
+      isis_csm_state_change (ISIS_DISABLE, circuit, circuit->area);
+    }
+
+  return 0;
+}
+
+void
+isis_circuit_init ()
+{
+  /* Initialize Zebra interface data structure */
+  if_add_hook (IF_NEW_HOOK, isis_if_new_hook);
+  if_add_hook (IF_DELETE_HOOK, isis_if_delete_hook);
+
+  /* Install interface node */
+  install_node (&interface_node, isis_interface_config_write);
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_element (CONFIG_NODE, &no_interface_cmd);
+
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+
+  isis_vty_init ();
+}
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
new file mode 100644 (file)
index 0000000..9ada1e2
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_circuit.h
+ *
+ * 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.
+ */
+
+#ifndef ISIS_CIRCUIT_H
+#define ISIS_CIRCUIT_H
+
+#include "vty.h"
+#include "if.h"
+
+#include "isis_constants.h"
+#include "isis_common.h"
+
+#define CIRCUIT_MAX 255
+
+struct password
+{
+  struct password *next;
+  int len;
+  u_char *pass;
+};
+
+struct metric
+{
+  u_char metric_default;
+  u_char metric_error;
+  u_char metric_expense;
+  u_char metric_delay;
+};
+
+struct isis_bcast_info
+{
+  u_char snpa[ETH_ALEN];       /* SNPA of this circuit */
+  char run_dr_elect[2];                /* Should we run dr election ? */
+  struct thread *t_run_dr[2];  /* DR election thread */
+  struct thread *t_send_lan_hello[2];  /* send LAN IIHs in this thread */
+  struct list *adjdb[2];       /* adjacency dbs */
+  struct list *lan_neighs[2];  /* list of lx neigh snpa */
+  char is_dr[2];               /* Are we level x DR ? */
+  u_char l1_desig_is[ISIS_SYS_ID_LEN + 1];     /* level-1 DR */
+  u_char l2_desig_is[ISIS_SYS_ID_LEN + 1];     /* level-2 DR */
+  struct thread *t_refresh_pseudo_lsp[2];      /* refresh pseudo-node LSPs */
+};
+
+struct isis_p2p_info
+{
+  struct isis_adjacency *neighbor;
+  struct thread *t_send_p2p_hello;     /* send P2P IIHs in this thread  */
+};
+
+struct isis_circuit
+{
+  int state;
+  u_char circuit_id;           /* l1/l2 p2p/bcast CircuitID */
+  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
+   */
+  struct thread *t_read;
+  struct thread *t_send_csnp[2];
+  struct thread *t_send_psnp[2];
+  struct list *lsp_queue;      /* LSPs to be txed (both levels) */
+  time_t lsp_queue_last_cleared;/* timestamp used to enforce transmit interval;
+                                 * for scalability, use one timestamp per 
+                                 * circuit, instead of one per lsp per circuit
+                                 */
+  /* there is no real point in two streams, just for programming kicker */
+  int (*rx) (struct isis_circuit * circuit, u_char * ssnpa);
+  struct stream *rcv_stream;   /* Stream for receiving */
+  int (*tx) (struct isis_circuit * circuit, int level);
+  struct stream *snd_stream;   /* Stream for sending */
+  int idx;                     /* idx in S[RM|SN] flags */
+#define CIRCUIT_T_UNKNOWN    0
+#define CIRCUIT_T_BROADCAST  1
+#define CIRCUIT_T_P2P        2
+#define CIRCUIT_T_LOOPBACK   3
+  int circ_type;               /* type of the physical interface */
+  int circ_type_config;                /* config type of the physical interface */
+  union
+  {
+    struct isis_bcast_info bc;
+    struct isis_p2p_info p2p;
+  } u;
+  u_char priority[2];          /* l1/2 IS configured priority */
+  int pad_hellos;              /* add padding to Hello PDUs ? */
+  char ext_domain;             /* externalDomain   (boolean) */
+  int lsp_regenerate_pending[ISIS_LEVELS];
+  /* 
+   * Configurables 
+   */
+  struct isis_passwd passwd;   /* Circuit rx/tx password */
+  int is_type;                 /* circuit is type == level of circuit
+                                * differentiated from circuit type (media) */
+  u_int32_t hello_interval[2]; /* l1HelloInterval in msecs */
+  u_int16_t hello_multiplier[2];       /* l1HelloMultiplier */
+  u_int16_t csnp_interval[2];  /* level-1 csnp-interval in seconds */
+  u_int16_t psnp_interval[2];  /* level-1 psnp-interval in seconds */
+  u_int8_t  metric[2];
+  u_int32_t te_metric[2];
+  struct mpls_te_circuit *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */
+  int ip_router;               /* Route IP ? */
+  int is_passive;              /* Is Passive ? */
+  struct list *ip_addrs;       /* our IP addresses */
+#ifdef HAVE_IPV6
+  int ipv6_router;             /* Route IPv6 ? */
+  struct list *ipv6_link;      /* our link local IPv6 addresses */
+  struct list *ipv6_non_link;  /* our non-link local IPv6 addresses */
+#endif                         /* HAVE_IPV6 */
+  u_int16_t upadjcount[2];
+#define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01
+  u_char flags;
+  /*
+   * Counters as in 10589--11.2.5.9
+   */
+  u_int32_t adj_state_changes; /* changesInAdjacencyState */
+  u_int32_t init_failures;     /* intialisationFailures */
+  u_int32_t ctrl_pdus_rxed;    /* controlPDUsReceived */
+  u_int32_t ctrl_pdus_txed;    /* controlPDUsSent */
+  u_int32_t desig_changes[2];  /* lanLxDesignatedIntermediateSystemChanges */
+  u_int32_t rej_adjacencies;   /* rejectedAdjacencies */
+};
+
+void isis_circuit_init (void);
+struct isis_circuit *isis_circuit_new (void);
+void isis_circuit_del (struct isis_circuit *circuit);
+struct isis_circuit *circuit_lookup_by_ifp (struct interface *ifp,
+                                           struct list *list);
+struct isis_circuit *circuit_scan_by_ifp (struct interface *ifp);
+void isis_circuit_configure (struct isis_circuit *circuit,
+                            struct isis_area *area);
+void isis_circuit_deconfigure (struct isis_circuit *circuit,
+                              struct isis_area *area);
+void isis_circuit_if_add (struct isis_circuit *circuit,
+                         struct interface *ifp);
+void isis_circuit_if_del (struct isis_circuit *circuit,
+                         struct interface *ifp);
+void isis_circuit_if_bind (struct isis_circuit *circuit,
+                           struct interface *ifp);
+void isis_circuit_if_unbind (struct isis_circuit *circuit,
+                             struct interface *ifp);
+void isis_circuit_add_addr (struct isis_circuit *circuit,
+                           struct connected *conn);
+void isis_circuit_del_addr (struct isis_circuit *circuit,
+                           struct connected *conn);
+int isis_circuit_up (struct isis_circuit *circuit);
+void isis_circuit_down (struct isis_circuit *);
+void circuit_update_nlpids (struct isis_circuit *circuit);
+void isis_circuit_print_vty (struct isis_circuit *circuit, struct vty *vty,
+                             char detail);
+size_t isis_circuit_pdu_size(struct isis_circuit *circuit);
+void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream);
+
+struct isis_circuit *isis_circuit_create (struct isis_area *area, struct interface *ifp);
+void isis_circuit_af_set (struct isis_circuit *circuit, bool ip_router, bool ipv6_router);
+int  isis_circuit_passive_set (struct isis_circuit *circuit, bool passive);
+void isis_circuit_is_type_set (struct isis_circuit *circuit, int is_type);
+int  isis_circuit_circ_type_set (struct isis_circuit *circuit, int circ_type);
+
+int  isis_circuit_metric_set (struct isis_circuit *circuit, int level, int metric);
+
+int  isis_circuit_passwd_unset (struct isis_circuit *circuit);
+int  isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd);
+int  isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd);
+
+#endif /* _ZEBRA_ISIS_CIRCUIT_H */
diff --git a/isisd/isis_common.h b/isisd/isis_common.h
new file mode 100644 (file)
index 0000000..d158961
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_common.h  
+ *                             some common data structures
+ *
+ * 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.
+ */
+
+#ifndef ISIS_COMMON_H
+#define ISIS_COMMON_H
+
+/*
+ * Area Address
+ */
+struct area_addr
+{
+  u_char addr_len;
+  u_char area_addr[20];
+};
+
+struct isis_passwd
+{
+  u_char len;
+#define ISIS_PASSWD_TYPE_UNUSED   0
+#define ISIS_PASSWD_TYPE_CLEARTXT 1
+#define ISIS_PASSWD_TYPE_HMAC_MD5 54
+#define ISIS_PASSWD_TYPE_PRIVATE  255
+  u_char type;
+  /* Authenticate SNPs? */
+#define SNP_AUTH_SEND   0x01
+#define SNP_AUTH_RECV   0x02
+  u_char snp_auth;
+  u_char passwd[255];
+};
+
+/*
+ * (Dynamic) Hostname
+ * one struct for cache list
+ * one struct for LSP TLV
+ */
+struct hostname
+{
+  u_char namelen;
+  u_char name[255];
+};
+
+/*
+ * Supported Protocol IDs
+ */
+struct nlpids
+{
+  u_char count;
+  u_char nlpids[4];            /* FIXME: enough ? */
+};
+
+#endif
diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h
new file mode 100644 (file)
index 0000000..8b21894
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_constants.h   
+ *
+ * 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.
+ */
+
+#ifndef ISIS_CONSTANTS_H
+#define ISIS_CONSTANTS_H
+
+/*
+ * Architectural constant values from p. 35 of ISO/IEC 10589
+ */
+
+#define MAX_NARROW_LINK_METRIC        63
+#define MAX_NARROW_PATH_METRIC        1023
+#define MAX_WIDE_LINK_METRIC          0x00FFFFFF  /* RFC4444 */
+#define MAX_WIDE_PATH_METRIC          0xFE000000  /* RFC3787 */
+#define ISO_SAP                       0xFE
+#define INTRADOMAIN_ROUTEING_SELECTOR 0
+#define SEQUENCE_MODULUS              4294967296
+
+/*
+ * implementation specific jitter values
+ */
+
+#define IIH_JITTER                    10       /* % */
+#define MAX_AGE_JITTER                 5       /* % */
+#define MAX_LSP_GEN_JITTER             5       /* % */
+#define CSNP_JITTER                   10       /* % */
+#define PSNP_JITTER                   10       /* % */
+
+#define RANDOM_SPREAD           100000.0
+
+#define ISIS_LEVELS                   2
+#define ISIS_LEVEL1                   1
+#define ISIS_LEVEL2                   2
+
+/*
+ * Default values
+ * ISO - 10589 Section 7.3.21 - Parameters
+ * RFC 4444
+ */
+#define MAX_AGE                       1200
+#define ZERO_AGE_LIFETIME             60
+#define MIN_LSP_LIFETIME              350
+#define MAX_LSP_LIFETIME              65535
+#define DEFAULT_LSP_LIFETIME          1200
+
+#define MIN_MAX_LSP_GEN_INTERVAL      1
+#define MAX_MAX_LSP_GEN_INTERVAL      65235
+#define DEFAULT_MAX_LSP_GEN_INTERVAL  900
+
+#define MIN_MIN_LSP_GEN_INTERVAL      1
+#define MAX_MIN_LSP_GEN_INTERVAL      120  /* RFC 4444 says 65535 */
+#define DEFAULT_MIN_LSP_GEN_INTERVAL  30
+
+#define MIN_LSP_TRANS_INTERVAL        5
+
+#define MIN_CSNP_INTERVAL             1
+#define MAX_CSNP_INTERVAL             600
+#define DEFAULT_CSNP_INTERVAL         10
+
+#define MIN_PSNP_INTERVAL             1
+#define MAX_PSNP_INTERVAL             120
+#define DEFAULT_PSNP_INTERVAL         2
+
+#define MIN_HELLO_INTERVAL            1
+#define MAX_HELLO_INTERVAL            600
+#define DEFAULT_HELLO_INTERVAL        3
+
+#define MIN_HELLO_MULTIPLIER          2
+#define MAX_HELLO_MULTIPLIER          100
+#define DEFAULT_HELLO_MULTIPLIER      10
+
+#define MIN_PRIORITY                  0
+#define MAX_PRIORITY                  127
+#define DEFAULT_PRIORITY              64
+
+/* min and max metric varies by new vs old metric types */
+#define DEFAULT_CIRCUIT_METRIC        10
+
+#define METRICS_UNSUPPORTED           0x80
+
+#define MINIMUM_SPF_INTERVAL          1
+
+#define ISIS_MAX_PATH_SPLITS          64
+
+/*
+ * NLPID values
+ */
+#define NLPID_IP   204
+#define NLPID_IPV6 142
+#define NLPID_SNAP 128
+#define NLPID_CLNP 129
+#define NLPID_ESIS 130
+
+/*
+ * Return values for functions
+ */
+#define ISIS_OK       0
+#define ISIS_WARNING  1
+#define ISIS_ERROR    2
+#define ISIS_CRITICAL 3
+
+/*
+ * IS-IS Circuit Types
+ */
+
+#define IS_LEVEL_1       1
+#define IS_LEVEL_2       2
+#define IS_LEVEL_1_AND_2 3
+
+#define SNPA_ADDRSTRLEN 18
+#define ISIS_SYS_ID_LEN  6
+#define ISIS_NSEL_LEN    1
+#define SYSID_STRLEN    24
+
+/*
+ * LSP bit masks
+ */
+#define LSPBIT_P   0x80
+#define LSPBIT_ATT 0x78
+#define LSPBIT_OL  0x04
+#define LSPBIT_IST 0x03
+
+/*
+ * LSP bit masking macros
+ * taken from tcpdumps
+ * print-isoclns.c
+ */
+
+#define ISIS_MASK_LSP_OL_BIT(x)            ((x)&0x4)
+#define ISIS_MASK_LSP_IS_L1_BIT(x)         ((x)&0x1)
+#define ISIS_MASK_LSP_IS_L2_BIT(x)         ((x)&0x2)
+#define ISIS_MASK_LSP_PARTITION_BIT(x)     ((x)&0x80)
+#define ISIS_MASK_LSP_ATT_BITS(x)          ((x)&0x78)
+#define ISIS_MASK_LSP_ATT_ERROR_BIT(x)     ((x)&0x40)
+#define ISIS_MASK_LSP_ATT_EXPENSE_BIT(x)   ((x)&0x20)
+#define ISIS_MASK_LSP_ATT_DELAY_BIT(x)     ((x)&0x10)
+#define ISIS_MASK_LSP_ATT_DEFAULT_BIT(x)   ((x)&0x8)
+
+#define LLC_LEN 3
+
+/* we need to be aware of the fact we are using ISO sized
+ * packets, using isomtu = mtu - LLC_LEN
+ */
+#define ISO_MTU(C) \
+          ((if_is_broadcast ((C)->interface)) ? \
+           (C->interface->mtu - LLC_LEN) : (C->interface->mtu))
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#endif /* ISIS_CONSTANTS_H */
diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c
new file mode 100644 (file)
index 0000000..0f642a7
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_csm.c
+ *                             IS-IS circuit state machine
+ * Copyright (C) 2001,2002    Sampo Saaristo
+ *                            Tampere University of Technology      
+ *                            Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "if.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "hash.h"
+#include "prefix.h"
+#include "stream.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_events.h"
+
+extern struct isis *isis;
+
+static const char *csm_statestr[] = {
+  "C_STATE_NA",
+  "C_STATE_INIT",
+  "C_STATE_CONF",
+  "C_STATE_UP"
+};
+
+#define STATE2STR(S) csm_statestr[S]
+
+static const char *csm_eventstr[] = {
+  "NO_STATE",
+  "ISIS_ENABLE",
+  "IF_UP_FROM_Z",
+  "ISIS_DISABLE",
+  "IF_DOWN_FROM_Z",
+};
+
+#define EVENT2STR(E) csm_eventstr[E]
+
+struct isis_circuit *
+isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
+{
+  int old_state;
+
+  old_state = circuit ? circuit->state : C_STATE_NA;
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("CSM_EVENT: %s", EVENT2STR (event));
+
+  switch (old_state)
+    {
+    case C_STATE_NA:
+      if (circuit)
+       zlog_warn ("Non-null circuit while state C_STATE_NA");
+      assert (circuit == NULL);
+      switch (event)
+       {
+       case ISIS_ENABLE:
+         circuit = isis_circuit_new ();
+         isis_circuit_configure (circuit, (struct isis_area *) arg);
+         circuit->state = C_STATE_CONF;
+         break;
+       case IF_UP_FROM_Z:
+         circuit = isis_circuit_new ();
+         isis_circuit_if_add (circuit, (struct interface *) arg);
+         listnode_add (isis->init_circ_list, circuit);
+         circuit->state = C_STATE_INIT;
+         break;
+       case ISIS_DISABLE:
+         zlog_warn ("circuit already disabled");
+         break;
+       case IF_DOWN_FROM_Z:
+         zlog_warn ("circuit already disconnected");
+         break;
+       }
+      break;
+    case C_STATE_INIT:
+      assert (circuit);
+      switch (event)
+       {
+       case ISIS_ENABLE:
+         isis_circuit_configure (circuit, (struct isis_area *) arg);
+         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, circuit->area, 1);
+         listnode_delete (isis->init_circ_list, circuit);
+         break;
+       case IF_UP_FROM_Z:
+          assert (circuit);
+         zlog_warn ("circuit already connected");
+         break;
+       case ISIS_DISABLE:
+         zlog_warn ("circuit already disabled");
+         break;
+       case IF_DOWN_FROM_Z:
+         isis_circuit_if_del (circuit, (struct interface *) arg);
+         listnode_delete (isis->init_circ_list, circuit);
+         isis_circuit_del (circuit);
+         circuit = NULL;
+         break;
+       }
+      break;
+    case C_STATE_CONF:
+      assert (circuit);
+      switch (event)
+       {
+       case ISIS_ENABLE:
+         zlog_warn ("circuit already enabled");
+         break;
+       case IF_UP_FROM_Z:
+         isis_circuit_if_add (circuit, (struct interface *) arg);
+         if (isis_circuit_up (circuit) != ISIS_OK)
+           {
+             zlog_err("Could not bring up %s because of invalid config.",
+                      circuit->interface->name);
+             zlog_err("Clearing config for %s. Please re-examine it.",
+                      circuit->interface->name);
+             if (circuit->ip_router)
+               {
+                 circuit->ip_router = 0;
+                 circuit->area->ip_circuits--;
+               }
+             if (circuit->ipv6_router)
+               {
+                 circuit->ipv6_router = 0;
+                 circuit->area->ipv6_circuits--;
+               }
+             circuit_update_nlpids(circuit);
+             isis_circuit_deconfigure(circuit, circuit->area);
+             listnode_add (isis->init_circ_list, circuit);
+             circuit->state = C_STATE_INIT;
+             break;
+           }
+         circuit->state = C_STATE_UP;
+         isis_event_circuit_state_change (circuit, circuit->area, 1);
+         break;
+       case ISIS_DISABLE:
+         isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
+         isis_circuit_del (circuit);
+         circuit = NULL;
+         break;
+       case IF_DOWN_FROM_Z:
+         zlog_warn ("circuit already disconnected");
+         break;
+       }
+      break;
+    case C_STATE_UP:
+      assert (circuit);
+      switch (event)
+       {
+       case ISIS_ENABLE:
+         zlog_warn ("circuit already configured");
+         break;
+       case IF_UP_FROM_Z:
+         zlog_warn ("circuit already connected");
+         break;
+       case ISIS_DISABLE:
+         isis_circuit_down (circuit);
+         isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
+         circuit->state = C_STATE_INIT;
+         isis_event_circuit_state_change (circuit,
+                                           (struct isis_area *)arg, 0);
+         listnode_add (isis->init_circ_list, circuit);
+         break;
+       case IF_DOWN_FROM_Z:
+         isis_circuit_down (circuit);
+          isis_circuit_if_del (circuit, (struct interface *) arg);
+         circuit->state = C_STATE_CONF;
+         isis_event_circuit_state_change (circuit, circuit->area, 0);
+         break;
+       }
+      break;
+
+    default:
+      zlog_warn ("Invalid circuit state %d", old_state);
+    }
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("CSM_STATE_CHANGE: %s -> %s ", STATE2STR (old_state),
+               circuit ? STATE2STR (circuit->state) : STATE2STR (C_STATE_NA));
+
+  return circuit;
+}
diff --git a/isisd/isis_csm.h b/isisd/isis_csm.h
new file mode 100644 (file)
index 0000000..d6b13ac
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_csm.h
+ *                             IS-IS circuit state machine
+ *
+ * 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.
+ */
+#ifndef _ZEBRA_ISIS_CSM_H
+#define _ZEBRA_ISIS_CSM_H
+
+/*
+ * Circuit states
+ */
+#define C_STATE_NA   0
+#define C_STATE_INIT 1         /* Connected to interface */
+#define C_STATE_CONF 2         /* Configured for ISIS    */
+#define C_STATE_UP   3         /* CONN | CONF            */
+
+/*
+ * Circuit events
+ */
+#define ISIS_ENABLE    1
+#define IF_UP_FROM_Z   2
+#define ISIS_DISABLE   3
+#define IF_DOWN_FROM_Z 4
+
+struct isis_circuit *isis_csm_state_change (int event,
+                                           struct isis_circuit *circuit,
+                                           void *arg);
+
+#endif /* _ZEBRA_ISIS_CSM_H */
diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
new file mode 100644 (file)
index 0000000..7c7e090
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_dlpi.c
+ *
+ * Copyright (C) 2001,2002    Sampo Saaristo
+ *                            Tampere University of Technology      
+ *                            Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_DLPI
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <poll.h>
+#include <sys/dlpi.h>
+#include <sys/pfmod.h>
+
+#include "log.h"
+#include "network.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_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 int
+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;
+  int rv;
+
+  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 */
+  rv = putmsg(fd, ctlptr, dataptr, flags);
+  if (rv == -1 && dbuf == NULL)
+    {
+      /*
+       * For actual PDU transmission - recognizable buf dbuf != NULL,
+       * the error is passed upwards and should not be printed here.
+       */
+      zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno));
+    }
+  return rv;
+}
+
+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 < (ssize_t)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 < (ssize_t)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 < (ssize_t)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 < (ssize_t)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 > (size_t)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 = -1, 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 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);
+    }
+
+  /* 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: %lld", __func__,
+       circuit->interface->name, (long long)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;
+  retval |= dlpimcast (fd, ALL_L1_ISS);
+  retval |= dlpimcast (fd, ALL_ISS);
+  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;
+      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));
+      /* 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;
+
+  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 < (ssize_t)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 > (size_t)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;
+  int buflen;
+  int rv;
+
+  buflen = stream_get_endp (circuit->snd_stream) + LLC_LEN;
+  if ((size_t)buflen > sizeof (sock_buff))
+    {
+      zlog_warn ("isis_send_pdu_bcast: sock_buff size %zu is less than "
+                "output pdu size %d on circuit %s",
+                sizeof (sock_buff), buflen, circuit->interface->name);
+      return ISIS_WARNING;
+    }
+
+  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 = buflen;
+
+  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));
+  rv = dlpisend(circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length,
+                sock_buff, buflen, 0);
+  if (rv < 0)
+    {
+      zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s",
+                circuit->interface->name, safe_strerror(errno));
+      if (ERRNO_IO_RETRY(errno))
+        return ISIS_WARNING;
+      return ISIS_ERROR;
+    }
+
+  return ISIS_OK;
+}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_DLPI */
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c
new file mode 100644 (file)
index 0000000..bc6ec11
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_dr.c
+ *                             IS-IS designated router related routines   
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+
+#include <zebra.h>
+
+#include "log.h"
+#include "hash.h"
+#include "thread.h"
+#include "linklist.h"
+#include "vty.h"
+#include "stream.h"
+#include "if.h"
+
+#include "isisd/dict.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isis_events.h"
+
+const char *
+isis_disflag2string (int disflag)
+{
+
+  switch (disflag)
+    {
+    case ISIS_IS_NOT_DIS:
+      return "is not DIS";
+    case ISIS_IS_DIS:
+      return "is DIS";
+    case ISIS_WAS_DIS:
+      return "was DIS";
+    default:
+      return "unknown DIS state";
+    }
+  return NULL;                 /* not reached */
+}
+
+int
+isis_run_dr_l1 (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  if (circuit->u.bc.run_dr_elect[0])
+    zlog_warn ("isis_run_dr(): run_dr_elect already set for l1");
+
+  circuit->u.bc.t_run_dr[0] = NULL;
+  circuit->u.bc.run_dr_elect[0] = 1;
+
+  return ISIS_OK;
+}
+
+int
+isis_run_dr_l2 (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  if (circuit->u.bc.run_dr_elect[1])
+    zlog_warn ("isis_run_dr(): run_dr_elect already set for l2");
+
+
+  circuit->u.bc.t_run_dr[1] = NULL;
+  circuit->u.bc.run_dr_elect[1] = 1;
+
+  return ISIS_OK;
+}
+
+static int
+isis_check_dr_change (struct isis_adjacency *adj, int level)
+{
+  int i;
+
+  if (adj->dis_record[level - 1].dis !=
+      adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis)
+    /* was there a DIS state transition ? */
+    {
+      adj->dischanges[level - 1]++;
+      /* ok rotate the history list through */
+      for (i = DIS_RECORDS - 1; i > 0; i--)
+       {
+         adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
+           adj->dis_record[((i - 1) * ISIS_LEVELS) + level - 1].dis;
+         adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change =
+           adj->dis_record[((i - 1) * ISIS_LEVELS) + level -
+                           1].last_dis_change;
+       }
+    }
+  return ISIS_OK;
+}
+
+int
+isis_dr_elect (struct isis_circuit *circuit, int level)
+{
+  struct list *adjdb;
+  struct listnode *node;
+  struct isis_adjacency *adj, *adj_dr = NULL;
+  struct list *list = list_new ();
+  u_char own_prio;
+  int biggest_prio = -1;
+  int cmp_res, retval = ISIS_OK;
+
+  own_prio = circuit->priority[level - 1];
+  adjdb = circuit->u.bc.adjdb[level - 1];
+
+  if (!adjdb)
+    {
+      zlog_warn ("isis_dr_elect() adjdb == NULL");
+      list_delete (list);
+      return ISIS_WARNING;
+    }
+  isis_adj_build_up_list (adjdb, list);
+
+  /*
+   * Loop the adjacencies and find the one with the biggest priority
+   */
+  for (ALL_LIST_ELEMENTS_RO (list, node, adj))
+    {
+      /* clear flag for show output */
+      adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
+      adj->dis_record[level - 1].last_dis_change = time (NULL);
+
+      if (adj->prio[level - 1] > biggest_prio)
+       {
+         biggest_prio = adj->prio[level - 1];
+         adj_dr = adj;
+       }
+      else if (adj->prio[level - 1] == biggest_prio)
+       {
+         /*
+          * Comparison of MACs breaks a tie
+          */
+         if (adj_dr)
+           {
+             cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN);
+             if (cmp_res < 0)
+               {
+                 adj_dr = adj;
+               }
+             if (cmp_res == 0)
+               zlog_warn
+                 ("isis_dr_elect(): multiple adjacencies with same SNPA");
+           }
+         else
+           {
+             adj_dr = adj;
+           }
+       }
+    }
+
+  if (!adj_dr)
+    {
+      /*
+       * Could not find the DR - means we are alone. Resign if we were DR.
+       */
+      if (circuit->u.bc.is_dr[level - 1])
+        retval = isis_dr_resign (circuit, level);
+      list_delete (list);
+      return retval;
+    }
+
+  /*
+   * Now we have the DR adjacency, compare it to self
+   */
+  if (adj_dr->prio[level - 1] < own_prio ||
+      (adj_dr->prio[level - 1] == own_prio &&
+       memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0))
+    {
+      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);
+
+      /* We are the DR, commence DR */
+      if (circuit->u.bc.is_dr[level - 1] == 0 && listcount (list) > 0)
+        retval = isis_dr_commence (circuit, level);
+    }
+  else
+    {
+      /* ok we have found the DIS - lets mark the adjacency */
+      /* set flag for show output */
+      adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
+      adj_dr->dis_record[level - 1].last_dis_change = time (NULL);
+
+      /* now loop through a second time to check if there has been a DIS change
+       * if yes rotate the history log
+       */
+
+      for (ALL_LIST_ELEMENTS_RO (list, node, adj))
+        isis_check_dr_change (adj, level);
+
+      /*
+       * We are not DR - if we were -> resign
+       */
+      if (circuit->u.bc.is_dr[level - 1])
+        retval = isis_dr_resign (circuit, level);
+    }
+  list_delete (list);
+  return retval;
+}
+
+int
+isis_dr_resign (struct isis_circuit *circuit, int level)
+{
+  u_char id[ISIS_SYS_ID_LEN + 2];
+
+  zlog_debug ("isis_dr_resign l%d", level);
+
+  circuit->u.bc.is_dr[level - 1] = 0;
+  circuit->u.bc.run_dr_elect[level - 1] = 0;
+  THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[level - 1]);
+  THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
+  circuit->lsp_regenerate_pending[level - 1] = 0;
+
+  memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (id) = circuit->circuit_id;
+  LSP_FRAGMENT (id) = 0;
+  lsp_purge_pseudo (id, circuit, level);
+
+  if (level == 1)
+    {
+      memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
+
+      THREAD_TIMER_OFF (circuit->t_send_csnp[0]);
+
+      THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
+                      circuit, 2 * circuit->hello_interval[0]);
+
+      THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit,
+                      isis_jitter (circuit->psnp_interval[level - 1],
+                                   PSNP_JITTER));
+    }
+  else
+    {
+      memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
+
+      THREAD_TIMER_OFF (circuit->t_send_csnp[1]);
+
+      THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
+                      circuit, 2 * circuit->hello_interval[1]);
+
+      THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit,
+                      isis_jitter (circuit->psnp_interval[level - 1],
+                                   PSNP_JITTER));
+    }
+
+  thread_add_event (master, isis_event_dis_status_change, circuit, 0);
+
+  return ISIS_OK;
+}
+
+int
+isis_dr_commence (struct isis_circuit *circuit, int level)
+{
+  u_char old_dr[ISIS_SYS_ID_LEN + 2];
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("isis_dr_commence l%d", level);
+
+  /* Lets keep a pause in DR election */
+  circuit->u.bc.run_dr_elect[level - 1] = 0;
+  if (level == 1)
+    THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
+                    circuit, 2 * circuit->hello_interval[0]);
+  else
+    THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
+                    circuit, 2 * circuit->hello_interval[1]);
+  circuit->u.bc.is_dr[level - 1] = 1;
+
+  if (level == 1)
+    {
+      memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+      LSP_FRAGMENT (old_dr) = 0;
+      if (LSP_PSEUDO_ID (old_dr))
+       {
+         /* there was a dr elected, purge its LSPs from the db */
+         lsp_purge_pseudo (old_dr, circuit, level);
+       }
+      memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
+      *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id;
+
+      assert (circuit->circuit_id);    /* must be non-zero */
+      /*    if (circuit->t_send_l1_psnp)
+         thread_cancel (circuit->t_send_l1_psnp); */
+      lsp_generate_pseudo (circuit, 1);
+
+      THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]);
+      THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
+                      circuit, 2 * circuit->hello_interval[0]);
+
+      THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit,
+                      isis_jitter (circuit->csnp_interval[level - 1],
+                                   CSNP_JITTER));
+
+    }
+  else
+    {
+      memcpy (old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+      LSP_FRAGMENT (old_dr) = 0;
+      if (LSP_PSEUDO_ID (old_dr))
+       {
+         /* there was a dr elected, purge its LSPs from the db */
+         lsp_purge_pseudo (old_dr, circuit, level);
+       }
+      memcpy (circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
+      *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id;
+
+      assert (circuit->circuit_id);    /* must be non-zero */
+      /*    if (circuit->t_send_l1_psnp)
+         thread_cancel (circuit->t_send_l1_psnp); */
+      lsp_generate_pseudo (circuit, 2);
+
+      THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]);
+      THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
+                      circuit, 2 * circuit->hello_interval[1]);
+
+      THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit,
+                      isis_jitter (circuit->csnp_interval[level - 1],
+                                   CSNP_JITTER));
+    }
+
+  thread_add_event (master, isis_event_dis_status_change, circuit, 0);
+
+  return ISIS_OK;
+}
diff --git a/isisd/isis_dr.h b/isisd/isis_dr.h
new file mode 100644 (file)
index 0000000..bad6836
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_dr.h
+ *                             IS-IS designated router related routines   
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_DR_H
+#define _ZEBRA_ISIS_DR_H
+
+int isis_run_dr_l1 (struct thread *thread);
+int isis_run_dr_l2 (struct thread *thread);
+int isis_dr_elect (struct isis_circuit *circuit, int level);
+int isis_dr_resign (struct isis_circuit *circuit, int level);
+int isis_dr_commence (struct isis_circuit *circuit, int level);
+const char *isis_disflag2string (int disflag);
+
+enum isis_dis_state
+{
+  ISIS_IS_NOT_DIS,
+  ISIS_IS_DIS,
+  ISIS_WAS_DIS,
+  ISIS_UNKNOWN_DIS
+};
+
+#endif /* _ZEBRA_ISIS_DR_H */
diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c
new file mode 100644 (file)
index 0000000..412f098
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_dynhn.c
+ *                             Dynamic hostname cache
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "linklist.h"
+#include "memory.h"
+#include "log.h"
+#include "stream.h"
+#include "command.h"
+#include "if.h"
+#include "thread.h"
+
+#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_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+
+extern struct host host;
+
+struct list *dyn_cache = NULL;
+static int dyn_cache_cleanup (struct thread *);
+
+void
+dyn_cache_init (void)
+{
+  if (dyn_cache == NULL)
+    dyn_cache = list_new ();
+  THREAD_TIMER_ON (master, isis->t_dync_clean, dyn_cache_cleanup, NULL, 120);
+  return;
+}
+
+static int
+dyn_cache_cleanup (struct thread *thread)
+{
+  struct listnode *node, *nnode;
+  struct isis_dynhn *dyn;
+  time_t now = time (NULL);
+
+  isis->t_dync_clean = NULL;
+
+  for (ALL_LIST_ELEMENTS (dyn_cache, node, nnode, dyn))
+    {
+      if ((now - dyn->refresh) < MAX_LSP_LIFETIME)
+        continue;
+
+      list_delete_node (dyn_cache, node);
+      XFREE (MTYPE_ISIS_DYNHN, dyn);
+    }
+
+  THREAD_TIMER_ON (master, isis->t_dync_clean, dyn_cache_cleanup, NULL, 120);
+  return ISIS_OK;
+}
+
+struct isis_dynhn *
+dynhn_find_by_id (const u_char * id)
+{
+  struct listnode *node = NULL;
+  struct isis_dynhn *dyn = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn))
+    if (memcmp (dyn->id, id, ISIS_SYS_ID_LEN) == 0)
+      return dyn;
+
+  return NULL;
+}
+
+struct isis_dynhn *
+dynhn_find_by_name (const char *hostname)
+{
+  struct listnode *node = NULL;
+  struct isis_dynhn *dyn = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn))
+    if (strncmp ((char *)dyn->name.name, hostname, 255) == 0)
+      return dyn;
+
+  return NULL;
+}
+
+void
+isis_dynhn_insert (const u_char * id, struct hostname *hostname, int level)
+{
+  struct isis_dynhn *dyn;
+
+  dyn = dynhn_find_by_id (id);
+  if (dyn)
+    {
+      memcpy (&dyn->name, hostname, hostname->namelen + 1);
+      memcpy (dyn->id, id, ISIS_SYS_ID_LEN);
+      dyn->refresh = time (NULL);
+      return;
+    }
+  dyn = XCALLOC (MTYPE_ISIS_DYNHN, sizeof (struct isis_dynhn));
+  if (!dyn)
+    {
+      zlog_warn ("isis_dynhn_insert(): out of memory!");
+      return;
+    }
+
+  /* we also copy the length */
+  memcpy (&dyn->name, hostname, hostname->namelen + 1);
+  memcpy (dyn->id, id, ISIS_SYS_ID_LEN);
+  dyn->refresh = time (NULL);
+  dyn->level = level;
+
+  listnode_add (dyn_cache, dyn);
+
+  return;
+}
+
+void
+isis_dynhn_remove (const u_char * id)
+{
+  struct isis_dynhn *dyn;
+
+  dyn = dynhn_find_by_id (id);
+  if (!dyn)
+    return;
+  listnode_delete (dyn_cache, dyn);
+  XFREE (MTYPE_ISIS_DYNHN, dyn);
+  return;
+}
+
+/*
+ * Level  System ID      Dynamic Hostname  (notag)
+ *  2     0000.0000.0001 foo-gw
+ *  2     0000.0000.0002 bar-gw
+ *      * 0000.0000.0004 this-gw
+ */
+void
+dynhn_print_all (struct vty *vty)
+{
+  struct listnode *node;
+  struct isis_dynhn *dyn;
+
+  vty_out (vty, "Level  System ID      Dynamic Hostname%s", VTY_NEWLINE);
+  for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn))
+    {
+      vty_out (vty, "%-7d", dyn->level);
+      vty_out (vty, "%-15s%-15s%s", sysid_print (dyn->id), dyn->name.name,
+              VTY_NEWLINE);
+    }
+
+  vty_out (vty, "     * %s %s%s", sysid_print (isis->sysid), unix_hostname (),
+          VTY_NEWLINE);
+  return;
+}
diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h
new file mode 100644 (file)
index 0000000..f06a067
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_dynhn.h
+ *                             Dynamic hostname cache
+ *
+ * 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.
+ */
+#ifndef _ZEBRA_ISIS_DYNHN_H
+#define _ZEBRA_ISIS_DYNHN_H
+
+struct isis_dynhn
+{
+  u_char id[ISIS_SYS_ID_LEN];
+  struct hostname name;
+  time_t refresh;
+  int level;
+};
+
+void dyn_cache_init (void);
+void isis_dynhn_insert (const u_char * id, struct hostname *hostname, int level);
+void isis_dynhn_remove (const u_char * id);
+struct isis_dynhn *dynhn_find_by_id (const u_char * id);
+struct isis_dynhn *dynhn_find_by_name (const char *hostname);
+void dynhn_print_all (struct vty *vty);
+
+#endif /* _ZEBRA_ISIS_DYNHN_H */
diff --git a/isisd/isis_events.c b/isisd/isis_events.c
new file mode 100644 (file)
index 0000000..abc4471
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_events.h   
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "if.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "hash.h"
+#include "prefix.h"
+#include "stream.h"
+#include "table.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_events.h"
+#include "isisd/isis_spf.h"
+
+/* debug isis-spf spf-events 
+ 4w4d: ISIS-Spf (tlt): L2 SPF needed, new adjacency, from 0x609229F4
+ 4w4d: ISIS-Spf (tlt): L2, 0000.0000.0042.01-00 TLV contents changed, code 0x2
+ 4w4d: ISIS-Spf (tlt): L2, new LSP 0 DEAD.BEEF.0043.00-00
+ 4w5d: ISIS-Spf (tlt): L1 SPF needed, periodic SPF, from 0x6091C844
+ 4w5d: ISIS-Spf (tlt): L2 SPF needed, periodic SPF, from 0x6091C844
+*/
+
+void
+isis_event_circuit_state_change (struct isis_circuit *circuit,
+                                 struct isis_area *area, int up)
+{
+  area->circuit_state_changes++;
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) circuit %s", area->area_tag,
+                up ? "up" : "down");
+
+  /*
+   * Regenerate LSPs this affects
+   */
+  lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+  return;
+}
+
+static void
+circuit_commence_level (struct isis_circuit *circuit, int level)
+{
+  if (level == 1)
+    {
+      if (! circuit->is_passive)
+        THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit,
+                        isis_jitter (circuit->psnp_interval[0], PSNP_JITTER));
+
+      if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+       {
+         THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
+                          circuit, 2 * circuit->hello_interval[0]);
+
+         THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0],
+                          send_lan_l1_hello, circuit,
+                          isis_jitter (circuit->hello_interval[0],
+                                       IIH_JITTER));
+
+         circuit->u.bc.lan_neighs[0] = list_new ();
+       }
+    }
+  else
+    {
+      if (! circuit->is_passive)
+        THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit,
+                        isis_jitter (circuit->psnp_interval[1], PSNP_JITTER));
+
+      if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+       {
+         THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
+                          circuit, 2 * circuit->hello_interval[1]);
+
+         THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1],
+                          send_lan_l2_hello, circuit,
+                          isis_jitter (circuit->hello_interval[1],
+                                       IIH_JITTER));
+
+         circuit->u.bc.lan_neighs[1] = list_new ();
+       }
+    }
+
+  return;
+}
+
+static void
+circuit_resign_level (struct isis_circuit *circuit, int level)
+{
+  int idx = level - 1;
+
+  THREAD_TIMER_OFF (circuit->t_send_csnp[idx]);
+  THREAD_TIMER_OFF (circuit->t_send_psnp[idx]);
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[idx]);
+      THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[idx]);
+      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[idx]);
+      circuit->lsp_regenerate_pending[idx] = 0;
+      circuit->u.bc.run_dr_elect[idx] = 0;
+      if (circuit->u.bc.lan_neighs[idx] != NULL) {
+        list_delete (circuit->u.bc.lan_neighs[idx]);
+        circuit->u.bc.lan_neighs[idx] = NULL;
+      }
+    }
+
+  return;
+}
+
+void
+isis_circuit_is_type_set (struct isis_circuit *circuit, int newtype)
+{
+  if (circuit->state != C_STATE_UP)
+  {
+    circuit->is_type = newtype;
+    return;
+  }
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) circuit type change %s -> %s",
+              circuit->area->area_tag,
+              circuit_t2string (circuit->is_type),
+              circuit_t2string (newtype));
+
+  if (circuit->is_type == newtype)
+    return;                    /* No change */
+
+  if (!(newtype & circuit->area->is_type))
+    {
+      zlog_err ("ISIS-Evt (%s) circuit type change - invalid level %s because"
+               " area is %s", circuit->area->area_tag,
+               circuit_t2string (newtype),
+               circuit_t2string (circuit->area->is_type));
+      return;
+    }
+
+  if (! circuit->is_passive)
+    {
+      switch (circuit->is_type)
+        {
+        case IS_LEVEL_1:
+          if (newtype == IS_LEVEL_2)
+            circuit_resign_level (circuit, 1);
+          circuit_commence_level (circuit, 2);
+          break;
+        case IS_LEVEL_1_AND_2:
+          if (newtype == IS_LEVEL_1)
+            circuit_resign_level (circuit, 2);
+          else
+            circuit_resign_level (circuit, 1);
+          break;
+        case IS_LEVEL_2:
+          if (newtype == IS_LEVEL_1)
+            circuit_resign_level (circuit, 2);
+          circuit_commence_level (circuit, 1);
+          break;
+        default:
+          break;
+        }
+    }
+
+  circuit->is_type = newtype;
+  lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+  return;
+}
+
+ /* 04/18/2002 by Gwak. */
+ /**************************************************************************
+  *
+  * EVENTS for LSP generation
+  *
+  * 1) an Adajacency or Circuit Up/Down event
+  * 2) a chnage in Circuit metric
+  * 3) a change in Reachable Address metric
+  * 4) a change in manualAreaAddresses
+  * 5) a change in systemID
+  * 6) a change in DIS status
+  * 7) a chnage in the waiting status
+  *
+  * ***********************************************************************
+  *
+  * current support event
+  *
+  * 1) Adjacency Up/Down event
+  * 6) a change in DIS status
+  *
+  * ***********************************************************************/
+
+void
+isis_event_adjacency_state_change (struct isis_adjacency *adj, int newstate)
+{
+  /* adjacency state change event. 
+   * - the only proto-type was supported */
+
+  /* invalid arguments */
+  if (!adj || !adj->circuit || !adj->circuit->area)
+    return;
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) Adjacency State change",
+               adj->circuit->area->area_tag);
+
+  /* LSP generation again */
+  lsp_regenerate_schedule (adj->circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+  return;
+}
+
+/* events supporting code */
+
+int
+isis_event_dis_status_change (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+
+  circuit = THREAD_ARG (thread);
+
+  /* invalid arguments */
+  if (!circuit || !circuit->area)
+    return 0;
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) DIS status change", circuit->area->area_tag);
+
+  /* LSP generation again */
+  lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+  return 0;
+}
+
+void
+isis_event_auth_failure (char *area_tag, const char *error_string, u_char *sysid)
+{
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) Authentication failure %s from %s",
+               area_tag, error_string, sysid_print (sysid));
+
+  return;
+}
diff --git a/isisd/isis_events.h b/isisd/isis_events.h
new file mode 100644 (file)
index 0000000..e7cfa35
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_events.h   
+ *
+ * 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.
+ */
+#ifndef _ZEBRA_ISIS_EVENTS_H
+#define _ZEBRA_ISIS_EVENTS_H
+
+/*
+ * Events related to circuit
+ */
+void isis_event_circuit_state_change (struct isis_circuit *circuit,
+                                     struct isis_area *area, int state);
+void isis_event_circuit_type_change (struct isis_circuit *circuit,
+                                    int newtype);
+/*
+ * Events related to adjacencies
+ */
+void isis_event_adjacency_state_change (struct isis_adjacency *adj,
+                                       int newstate);
+
+int isis_event_dis_status_change (struct thread *thread);
+
+/*
+ * Error events
+ */
+#define AUTH_ERROR_TYPE_LSP   3
+#define AUTH_ERROR_TYPE_SNP   2
+#define AUTH_ERROR_TYPE_HELLO 1
+void isis_event_auth_failure (char *area_tag, const char *error_string,
+                             u_char *sysid);
+
+#endif /* _ZEBRA_ISIS_EVENTS_H */
diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c
new file mode 100644 (file)
index 0000000..ec0eaa4
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_flags.c
+ *                             Routines for manipulation of SSN and SRM flags
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "linklist.h"
+
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+
+void
+flags_initialize (struct flags *flags)
+{
+  flags->maxindex = 0;
+  flags->free_idcs = NULL;
+}
+
+long int
+flags_get_index (struct flags *flags)
+{
+  struct listnode *node;
+  long int index;
+
+  if (flags->free_idcs == NULL || flags->free_idcs->count == 0)
+    {
+      index = flags->maxindex++;
+    }
+  else
+    {
+      node = listhead (flags->free_idcs);
+      index = (long int) listgetdata (node);
+      listnode_delete (flags->free_idcs, (void *) index);
+      index--;
+    }
+
+  return index;
+}
+
+void
+flags_free_index (struct flags *flags, long 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 + 1));
+
+  return;
+}
+
+int
+flags_any_set (u_int32_t * flags)
+{
+  u_int32_t zero[ISIS_MAX_CIRCUITS];
+  memset (zero, 0x00, ISIS_MAX_CIRCUITS * 4);
+
+  return bcmp (flags, zero, ISIS_MAX_CIRCUITS * 4);
+}
diff --git a/isisd/isis_flags.h b/isisd/isis_flags.h
new file mode 100644 (file)
index 0000000..e2e42ad
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_flags.h
+ *                             Routines for manipulation of SSN and SRM flags
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_FLAGS_H
+#define _ZEBRA_ISIS_FLAGS_H
+
+/* 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 */
+
+/*
+ * Flags structure for SSN and SRM flags
+ */
+struct flags
+{
+  int maxindex;
+  struct list *free_idcs;
+};
+
+void flags_initialize (struct flags *flags);
+long int flags_get_index (struct flags *flags);
+void flags_free_index (struct flags *flags, long int index);
+int flags_any_set (u_int32_t * flags);
+
+#define ISIS_SET_FLAG(F,C) \
+        { \
+          F[C->idx>>5] |= (1<<(C->idx & 0x1F)); \
+        }
+
+#define ISIS_CLEAR_FLAG(F,C) \
+        { \
+          F[C->idx>>5] &= ~(1<<(C->idx & 0x1F)); \
+        }
+
+#define ISIS_CHECK_FLAG(F, C)  (F[(C)->idx>>5] & (1<<(C->idx & 0x1F)))
+
+/* sets all u_32int_t flags to 1 */
+#define ISIS_FLAGS_SET_ALL(FLAGS) \
+        { \
+          memset(FLAGS,0xFF,ISIS_MAX_CIRCUITS*4); \
+        }
+
+#define ISIS_FLAGS_CLEAR_ALL(FLAGS) \
+        { \
+          memset(FLAGS,0x00,ISIS_MAX_CIRCUITS*4); \
+        }
+
+#endif /* _ZEBRA_ISIS_FLAGS_H */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
new file mode 100644 (file)
index 0000000..aac8451
--- /dev/null
@@ -0,0 +1,3073 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_lsp.c
+ *                             LSP processing
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ * Copyright (C) 2013-2015   Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program 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 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "vty.h"
+#include "stream.h"
+#include "memory.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "checksum.h"
+#include "md5.h"
+#include "table.h"
+
+#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"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_te.h"
+
+#ifdef TOPOLOGY_GENERATE
+#include "spgrid.h"
+#endif
+
+/* staticly assigned vars for printing purposes */
+char lsp_bits_string[200];     /* FIXME: enough ? */
+
+static int lsp_l1_refresh (struct thread *thread);
+static int lsp_l2_refresh (struct thread *thread);
+static int lsp_l1_refresh_pseudo (struct thread *thread);
+static int lsp_l2_refresh_pseudo (struct thread *thread);
+
+int
+lsp_id_cmp (u_char * id1, u_char * id2)
+{
+  return memcmp (id1, id2, ISIS_SYS_ID_LEN + 2);
+}
+
+dict_t *
+lsp_db_init (void)
+{
+  dict_t *dict;
+
+  dict = dict_create (DICTCOUNT_T_MAX, (dict_comp_t) lsp_id_cmp);
+
+  return dict;
+}
+
+struct isis_lsp *
+lsp_search (u_char * id, dict_t * lspdb)
+{
+  dnode_t *node;
+
+#ifdef EXTREME_DEBUG
+  dnode_t *dn;
+
+  zlog_debug ("searching db");
+  for (dn = dict_first (lspdb); dn; dn = dict_next (lspdb, dn))
+    {
+      zlog_debug ("%s\t%pX", rawlspid_print ((u_char *) dnode_getkey (dn)),
+                 dnode_get (dn));
+    }
+#endif /* EXTREME DEBUG */
+
+  node = dict_lookup (lspdb, id);
+
+  if (node)
+    return (struct isis_lsp *) dnode_get (node);
+
+  return NULL;
+}
+
+static void
+lsp_clear_data (struct isis_lsp *lsp)
+{
+  if (!lsp)
+    return;
+
+  if (lsp->tlv_data.hostname)
+    isis_dynhn_remove (lsp->lsp_header->lsp_id);
+
+  if (lsp->own_lsp)
+    {
+      if (lsp->tlv_data.nlpids)
+        XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.nlpids);
+      if (lsp->tlv_data.hostname)
+        XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.hostname);
+      if (lsp->tlv_data.router_id)
+        XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.router_id);
+    }
+
+  free_tlvs (&lsp->tlv_data);
+}
+
+static void
+lsp_destroy (struct isis_lsp *lsp)
+{
+  struct listnode *cnode, *lnode, *lnnode;
+  struct isis_lsp *lsp_in_list;
+  struct isis_circuit *circuit;
+
+  if (!lsp)
+    return;
+
+  if (lsp->area->circuit_list) {
+    for (ALL_LIST_ELEMENTS_RO (lsp->area->circuit_list, cnode, circuit))
+      {
+        if (circuit->lsp_queue == NULL)
+          continue;
+        for (ALL_LIST_ELEMENTS (circuit->lsp_queue, lnode, lnnode, lsp_in_list))
+          if (lsp_in_list == lsp)
+            list_delete_node(circuit->lsp_queue, lnode);
+      }
+  }
+  ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags);
+  ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags);
+
+  lsp_clear_data (lsp);
+
+  if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags)
+    {
+      list_delete (lsp->lspu.frags);
+      lsp->lspu.frags = NULL;
+    }
+
+  isis_spf_schedule (lsp->area, lsp->level);
+#ifdef HAVE_IPV6
+  isis_spf_schedule6 (lsp->area, lsp->level);
+#endif
+
+  if (lsp->pdu)
+    stream_free (lsp->pdu);
+  XFREE (MTYPE_ISIS_LSP, lsp);
+}
+
+void
+lsp_db_destroy (dict_t * lspdb)
+{
+  dnode_t *dnode, *next;
+  struct isis_lsp *lsp;
+
+  dnode = dict_first (lspdb);
+  while (dnode)
+    {
+      next = dict_next (lspdb, dnode);
+      lsp = dnode_get (dnode);
+      lsp_destroy (lsp);
+      dict_delete_free (lspdb, dnode);
+      dnode = next;
+    }
+
+  dict_free (lspdb);
+
+  return;
+}
+
+/*
+ * Remove all the frags belonging to the given lsp
+ */
+static void
+lsp_remove_frags (struct list *frags, dict_t * lspdb)
+{
+  dnode_t *dnode;
+  struct listnode *lnode, *lnnode;
+  struct isis_lsp *lsp;
+
+  for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp))
+    {
+      dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id);
+      lsp_destroy (lsp);
+      dnode_destroy (dict_delete (lspdb, dnode));
+    }
+
+  list_delete_all_node (frags);
+
+  return;
+}
+
+void
+lsp_search_and_destroy (u_char * id, dict_t * lspdb)
+{
+  dnode_t *node;
+  struct isis_lsp *lsp;
+
+  node = dict_lookup (lspdb, id);
+  if (node)
+    {
+      node = dict_delete (lspdb, node);
+      lsp = dnode_get (node);
+      /*
+       * If this is a zero lsp, remove all the frags now 
+       */
+      if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0)
+       {
+         if (lsp->lspu.frags)
+           lsp_remove_frags (lsp->lspu.frags, lspdb);
+       }
+      else
+       {
+         /* 
+          * else just remove this frag, from the zero lsps' frag list
+          */
+         if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags)
+           listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp);
+       }
+      lsp_destroy (lsp);
+      dnode_destroy (node);
+    }
+}
+
+/*
+ * Compares a LSP to given values
+ * Params are given in net order
+ */
+int
+lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num,
+            u_int16_t checksum, u_int16_t rem_lifetime)
+{
+  /* no point in double ntohl on seqnum */
+  if (lsp->lsp_header->seq_num == seq_num &&
+      lsp->lsp_header->checksum == checksum &&
+      /*comparing with 0, no need to do ntohl */
+      ((lsp->lsp_header->rem_lifetime == 0 && rem_lifetime == 0) ||
+       (lsp->lsp_header->rem_lifetime != 0 && rem_lifetime != 0)))
+    {
+      if (isis->debugs & DEBUG_SNP_PACKETS)
+       {
+         zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x,"
+                     " lifetime %us",
+                     areatag,
+                     rawlspid_print (lsp->lsp_header->lsp_id),
+                     ntohl (lsp->lsp_header->seq_num),
+                     ntohs (lsp->lsp_header->checksum),
+                     ntohs (lsp->lsp_header->rem_lifetime));
+         zlog_debug ("ISIS-Snp (%s):         is equal to ours seq 0x%08x,"
+                     " cksum 0x%04x, lifetime %us",
+                     areatag,
+                     ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime));
+       }
+      return LSP_EQUAL;
+    }
+
+  /*
+   * LSPs with identical checksums should only be treated as newer if:
+   * a) The current LSP has a remaining lifetime != 0 and the other LSP has a
+   *    remaining lifetime == 0. In this case, we should participate in the purge
+   *    and should not treat the current LSP with remaining lifetime == 0 as older.
+   * b) The LSP has an incorrect checksum. In this case, we need to react as given
+   *    in 7.3.16.2.
+   */
+   if (ntohl (seq_num) > ntohl (lsp->lsp_header->seq_num)
+      || (ntohl(seq_num) == ntohl(lsp->lsp_header->seq_num)
+          && (  (lsp->lsp_header->rem_lifetime != 0
+                 && rem_lifetime == 0)
+              || lsp->lsp_header->checksum != checksum)))
+    {
+      if (isis->debugs & DEBUG_SNP_PACKETS)
+       {
+         zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x,"
+                     " lifetime %us",
+                     areatag,
+                     rawlspid_print (lsp->lsp_header->lsp_id),
+                     ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime));
+         zlog_debug ("ISIS-Snp (%s):       is newer than ours seq 0x%08x, "
+                     "cksum 0x%04x, lifetime %us",
+                     areatag,
+                     ntohl (lsp->lsp_header->seq_num),
+                     ntohs (lsp->lsp_header->checksum),
+                     ntohs (lsp->lsp_header->rem_lifetime));
+       }
+      return LSP_NEWER;
+    }
+  if (isis->debugs & DEBUG_SNP_PACKETS)
+    {
+      zlog_debug
+       ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us",
+        areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (seq_num),
+        ntohs (checksum), ntohs (rem_lifetime));
+      zlog_debug ("ISIS-Snp (%s):       is older than ours seq 0x%08x,"
+                 " cksum 0x%04x, lifetime %us", areatag,
+                 ntohl (lsp->lsp_header->seq_num),
+                 ntohs (lsp->lsp_header->checksum),
+                 ntohs (lsp->lsp_header->rem_lifetime));
+    }
+
+  return LSP_OLDER;
+}
+
+static void
+lsp_auth_add (struct isis_lsp *lsp)
+{
+  struct isis_passwd *passwd;
+  unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
+
+  /*
+   * Add the authentication info if its present
+   */
+  (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) :
+                               (passwd = &lsp->area->domain_passwd);
+  switch (passwd->type)
+    {
+      /* Cleartext */
+      case ISIS_PASSWD_TYPE_CLEARTXT:
+        memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd));
+        tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu);
+        break;
+
+      /* HMAC MD5 */
+      case ISIS_PASSWD_TYPE_HMAC_MD5:
+        /* Remember where TLV is written so we can later
+         * overwrite the MD5 hash */
+        lsp->auth_tlv_offset = stream_get_endp (lsp->pdu);
+        memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+        lsp->tlv_data.auth_info.type = ISIS_PASSWD_TYPE_HMAC_MD5;
+        lsp->tlv_data.auth_info.len = ISIS_AUTH_MD5_SIZE;
+        memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash,
+                ISIS_AUTH_MD5_SIZE);
+        tlv_add_authinfo (passwd->type, ISIS_AUTH_MD5_SIZE, hmac_md5_hash,
+                          lsp->pdu);
+        break;
+
+      default:
+        break;
+    }
+}
+
+static void
+lsp_auth_update (struct isis_lsp *lsp)
+{
+  struct isis_passwd *passwd;
+  unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
+  uint16_t checksum, rem_lifetime;
+
+  /* For HMAC MD5 we need to recompute the md5 hash and store it */
+  (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) :
+                               (passwd = &lsp->area->domain_passwd);
+  if (passwd->type != ISIS_PASSWD_TYPE_HMAC_MD5)
+    return;
+
+  /*
+   * In transient conditions (when net is configured where authentication
+   * config and lsp regenerate schedule is not yet run), there could be
+   * an own_lsp with auth_tlv_offset set to 0. In such a case, simply
+   * return, when lsp_regenerate is run, lsp will have auth tlv.
+   */
+  if (lsp->auth_tlv_offset == 0)
+    return;
+
+  /*
+   * RFC 5304 set auth value, checksum and remaining lifetime to zero
+   * before computation and reset to old values after computation.
+   */
+  checksum = lsp->lsp_header->checksum;
+  rem_lifetime = lsp->lsp_header->rem_lifetime;
+  lsp->lsp_header->checksum = 0;
+  lsp->lsp_header->rem_lifetime = 0;
+  /* Set the authentication value as well to zero */
+  memset (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3,
+          0, ISIS_AUTH_MD5_SIZE);
+  /* Compute autentication value */
+  hmac_md5 (STREAM_DATA (lsp->pdu), stream_get_endp(lsp->pdu),
+            (unsigned char *) &passwd->passwd, passwd->len,
+            (unsigned char *) &hmac_md5_hash);
+  /* Copy the hash into the stream */
+  memcpy (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3,
+          hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
+  memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash,
+          ISIS_AUTH_MD5_SIZE);
+  /* Copy back the checksum and remaining lifetime */
+  lsp->lsp_header->checksum = checksum;
+  lsp->lsp_header->rem_lifetime = rem_lifetime;
+}
+
+void
+lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num)
+{
+  u_int32_t newseq;
+
+  if (seq_num == 0 || ntohl (lsp->lsp_header->seq_num) > seq_num)
+    newseq = ntohl (lsp->lsp_header->seq_num) + 1;
+  else
+    newseq = seq_num + 1;
+
+  lsp->lsp_header->seq_num = htonl (newseq);
+
+  /* Recompute authentication and checksum information */
+  lsp_auth_update (lsp);
+  /* ISO 10589 - 7.3.11 Generation of the checksum
+   * The checksum shall be computed over all fields in the LSP which appear
+   * after the Remaining Lifetime field. This field (and those appearing
+   * before it) are excluded so that the LSP may be aged by systems without
+   * requiring recomputation.
+   */
+  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+                    ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+
+  isis_spf_schedule (lsp->area, lsp->level);
+#ifdef HAVE_IPV6
+  isis_spf_schedule6 (lsp->area, lsp->level);
+#endif
+
+  return;
+}
+
+/*
+ * Genetates checksum for LSP and its frags
+ */
+static void
+lsp_seqnum_update (struct isis_lsp *lsp0)
+{
+  struct isis_lsp *lsp;
+  struct listnode *node;
+
+  lsp_inc_seqnum (lsp0, 0);
+
+  if (!lsp0->lspu.frags)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (lsp0->lspu.frags, node, lsp))
+    lsp_inc_seqnum (lsp, 0);
+
+  return;
+}
+
+static u_int8_t
+lsp_bits_generate (int level, int overload_bit, int attached_bit)
+{
+  u_int8_t lsp_bits = 0;
+  if (level == IS_LEVEL_1)
+    lsp_bits = IS_LEVEL_1;
+  else
+    lsp_bits = IS_LEVEL_1_AND_2;
+  if (overload_bit)
+    lsp_bits |= overload_bit;
+  if (attached_bit)
+    lsp_bits |= attached_bit;
+  return lsp_bits;
+}
+
+static void
+lsp_update_data (struct isis_lsp *lsp, struct stream *stream,
+                 struct isis_area *area, int level)
+{
+  uint32_t expected = 0, found;
+  int retval;
+
+  /* free the old lsp data */
+  lsp_clear_data (lsp);
+
+  /* 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 */
+  lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu));
+  lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) +
+                                                   ISIS_FIXED_HDR_LEN);
+  lsp->area = area;
+  lsp->level = level;
+  lsp->age_out = ZERO_AGE_LIFETIME;
+  lsp->installed = time (NULL);
+  /*
+   * Get LSP data i.e. TLVs
+   */
+  expected |= TLVFLAG_AUTH_INFO;
+  expected |= TLVFLAG_AREA_ADDRS;
+  expected |= TLVFLAG_IS_NEIGHS;
+  expected |= TLVFLAG_NLPID;
+  if (area->dynhostname)
+    expected |= TLVFLAG_DYN_HOSTNAME;
+  if (area->newmetric)
+    {
+      expected |= TLVFLAG_TE_IS_NEIGHS;
+      expected |= TLVFLAG_TE_IPV4_REACHABILITY;
+      expected |= TLVFLAG_TE_ROUTER_ID;
+    }
+  expected |= TLVFLAG_IPV4_ADDR;
+  expected |= TLVFLAG_IPV4_INT_REACHABILITY;
+  expected |= TLVFLAG_IPV4_EXT_REACHABILITY;
+#ifdef HAVE_IPV6
+  expected |= TLVFLAG_IPV6_ADDR;
+  expected |= TLVFLAG_IPV6_REACHABILITY;
+#endif /* HAVE_IPV6 */
+
+  retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) +
+                       ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN,
+                       ntohs (lsp->lsp_header->pdu_len) -
+                       ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN,
+                       &expected, &found, &lsp->tlv_data,
+                       NULL);
+  if (retval != ISIS_OK)
+    {
+      zlog_warn ("Could not parse LSP");
+      return;
+    }
+
+  if ((found & TLVFLAG_DYN_HOSTNAME) && (area->dynhostname))
+    {
+      isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname,
+                         (lsp->lsp_header->lsp_bits & LSPBIT_IST) ==
+                          IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : IS_LEVEL_1);
+    }
+
+  return;
+}
+
+void
+lsp_update (struct isis_lsp *lsp, struct stream *stream,
+            struct isis_area *area, int level)
+{
+  dnode_t *dnode = NULL;
+
+  /* Remove old LSP from database. This is required since the
+   * lsp_update_data will free the lsp->pdu (which has the key, lsp_id)
+   * and will update it with the new data in the stream. */
+  dnode = dict_lookup (area->lspdb[level - 1], lsp->lsp_header->lsp_id);
+  if (dnode)
+    dnode_destroy (dict_delete (area->lspdb[level - 1], dnode));
+
+  /* rebuild the lsp data */
+  lsp_update_data (lsp, stream, area, level);
+
+  /* insert the lsp back into the database */
+  lsp_insert (lsp, area->lspdb[level - 1]);
+}
+
+/* creation of LSP directly from what we received */
+struct isis_lsp *
+lsp_new_from_stream_ptr (struct stream *stream,
+                        u_int16_t pdu_len, struct isis_lsp *lsp0,
+                        struct isis_area *area, int level)
+{
+  struct isis_lsp *lsp;
+
+  lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp));
+  lsp_update_data (lsp, stream, area, level);
+
+  if (lsp0 == NULL)
+    {
+      /*
+       * zero lsp -> create the list for fragments
+       */
+      lsp->lspu.frags = list_new ();
+    }
+  else
+    {
+      /*
+       * a fragment -> set the backpointer and add this to zero lsps frag list
+       */
+      lsp->lspu.zero_lsp = lsp0;
+      listnode_add (lsp0->lspu.frags, lsp);
+    }
+
+  return lsp;
+}
+
+struct isis_lsp *
+lsp_new(struct isis_area *area, u_char * lsp_id,
+       u_int16_t rem_lifetime, u_int32_t seq_num,
+       u_int8_t lsp_bits, u_int16_t checksum, int level)
+{
+  struct isis_lsp *lsp;
+
+  lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp));
+  lsp->area = area;
+
+  lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu);
+  if (LSP_FRAGMENT (lsp_id) == 0)
+    lsp->lspu.frags = list_new ();
+  lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu));
+  lsp->lsp_header = (struct isis_link_state_hdr *)
+    (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN);
+
+  /* at first we fill the FIXED HEADER */
+  (level == IS_LEVEL_1) ? fill_fixed_hdr (lsp->isis_header, L1_LINK_STATE) :
+    fill_fixed_hdr (lsp->isis_header, L2_LINK_STATE);
+
+  /* now for the LSP HEADER */
+  /* Minimal LSP PDU size */
+  lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+  memcpy (lsp->lsp_header->lsp_id, lsp_id, ISIS_SYS_ID_LEN + 2);
+  lsp->lsp_header->checksum = checksum;        /* Provided in network order */
+  lsp->lsp_header->seq_num = htonl (seq_num);
+  lsp->lsp_header->rem_lifetime = htons (rem_lifetime);
+  lsp->lsp_header->lsp_bits = lsp_bits;
+  lsp->level = level;
+  lsp->age_out = ZERO_AGE_LIFETIME;
+
+  stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("New LSP with ID %s-%02x-%02x len %d seqnum %08x",
+               sysid_print (lsp_id), LSP_PSEUDO_ID (lsp->lsp_header->lsp_id),
+               LSP_FRAGMENT (lsp->lsp_header->lsp_id),
+               ntohl (lsp->lsp_header->pdu_len),
+               ntohl (lsp->lsp_header->seq_num));
+
+  return lsp;
+}
+
+void
+lsp_insert (struct isis_lsp *lsp, dict_t * lspdb)
+{
+  dict_alloc_insert (lspdb, lsp->lsp_header->lsp_id, lsp);
+  if (lsp->lsp_header->seq_num != 0)
+    {
+      isis_spf_schedule (lsp->area, lsp->level);
+#ifdef HAVE_IPV6
+      isis_spf_schedule6 (lsp->area, lsp->level);
+#endif
+    }
+}
+
+/*
+ * Build a list of LSPs with non-zero ht bounded by start and stop ids
+ */
+void
+lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id,
+                          struct list *list, dict_t * lspdb)
+{
+  dnode_t *first, *last, *curr;
+
+  first = dict_lower_bound (lspdb, start_id);
+  if (!first)
+    return;
+
+  last = dict_upper_bound (lspdb, stop_id);
+
+  curr = first;
+
+  if (((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime)
+    listnode_add (list, first->dict_data);
+
+  while (curr)
+    {
+      curr = dict_next (lspdb, curr);
+      if (curr &&
+         ((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime)
+       listnode_add (list, curr->dict_data);
+      if (curr == last)
+       break;
+    }
+
+  return;
+}
+
+/*
+ * Build a list of num_lsps LSPs bounded by start_id and stop_id.
+ */
+void
+lsp_build_list (u_char * start_id, u_char * stop_id, u_char num_lsps,
+               struct list *list, dict_t * lspdb)
+{
+  u_char count;
+  dnode_t *first, *last, *curr;
+
+  first = dict_lower_bound (lspdb, start_id);
+  if (!first)
+    return;
+
+  last = dict_upper_bound (lspdb, stop_id);
+
+  curr = first;
+
+  listnode_add (list, first->dict_data);
+  count = 1;
+
+  while (curr)
+    {
+      curr = dict_next (lspdb, curr);
+      if (curr)
+        {
+          listnode_add (list, curr->dict_data);
+          count++;
+        }
+      if (count == num_lsps || curr == last)
+        break;
+    }
+
+  return;
+}
+
+/*
+ * Build a list of LSPs with SSN flag set for the given circuit
+ */
+void
+lsp_build_list_ssn (struct isis_circuit *circuit, u_char num_lsps,
+                    struct list *list, dict_t * lspdb)
+{
+  dnode_t *dnode, *next;
+  struct isis_lsp *lsp;
+  u_char count = 0;
+
+  dnode = dict_first (lspdb);
+  while (dnode != NULL)
+    {
+      next = dict_next (lspdb, dnode);
+      lsp = dnode_get (dnode);
+      if (ISIS_CHECK_FLAG (lsp->SSNflags, circuit))
+        {
+          listnode_add (list, lsp);
+          ++count;
+        }
+      if (count == num_lsps)
+        break;
+      dnode = next;
+    }
+
+  return;
+}
+
+static void
+lsp_set_time (struct isis_lsp *lsp)
+{
+  assert (lsp);
+
+  if (lsp->lsp_header->rem_lifetime == 0)
+    {
+      if (lsp->age_out > 0)
+        lsp->age_out--;
+      return;
+    }
+
+  lsp->lsp_header->rem_lifetime =
+    htons (ntohs (lsp->lsp_header->rem_lifetime) - 1);
+}
+
+static void
+lspid_print (u_char * lsp_id, u_char * trg, char dynhost, char frag)
+{
+  struct isis_dynhn *dyn = NULL;
+  u_char id[SYSID_STRLEN];
+
+  if (dynhost)
+    dyn = dynhn_find_by_id (lsp_id);
+  else
+    dyn = NULL;
+
+  if (dyn)
+      sprintf ((char *)id, "%.14s", dyn->name.name);
+  else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost)
+      sprintf ((char *)id, "%.14s", unix_hostname ());
+  else
+      memcpy (id, sysid_print (lsp_id), 15);
+  if (frag)
+    sprintf ((char *)trg, "%s.%02x-%02x", id, LSP_PSEUDO_ID (lsp_id),
+            LSP_FRAGMENT (lsp_id));
+  else
+    sprintf ((char *)trg, "%s.%02x", id, LSP_PSEUDO_ID (lsp_id));
+}
+
+/* Convert the lsp attribute bits to attribute string */
+const char *
+lsp_bits2string (u_char * lsp_bits)
+{
+  char *pos = lsp_bits_string;
+
+  if (!*lsp_bits)
+    return " none";
+
+  /* we only focus on the default metric */
+  pos += sprintf (pos, "%d/",
+                 ISIS_MASK_LSP_ATT_DEFAULT_BIT (*lsp_bits) ? 1 : 0);
+
+  pos += sprintf (pos, "%d/",
+                 ISIS_MASK_LSP_PARTITION_BIT (*lsp_bits) ? 1 : 0);
+
+  pos += sprintf (pos, "%d", ISIS_MASK_LSP_OL_BIT (*lsp_bits) ? 1 : 0);
+
+  *(pos) = '\0';
+
+  return lsp_bits_string;
+}
+
+/* this function prints the lsp on show isis database */
+void
+lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost)
+{
+  u_char LSPid[255];
+  char age_out[8];
+
+  lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1);
+  vty_out (vty, "%-21s%c  ", LSPid, lsp->own_lsp ? '*' : ' ');
+  vty_out (vty, "%5u   ", ntohs (lsp->lsp_header->pdu_len));
+  vty_out (vty, "0x%08x  ", ntohl (lsp->lsp_header->seq_num));
+  vty_out (vty, "0x%04x  ", ntohs (lsp->lsp_header->checksum));
+  if (ntohs (lsp->lsp_header->rem_lifetime) == 0)
+    {
+      snprintf (age_out, 8, "(%u)", lsp->age_out);
+      age_out[7] = '\0';
+      vty_out (vty, "%7s   ", age_out);
+    }
+  else
+    vty_out (vty, " %5u    ", ntohs (lsp->lsp_header->rem_lifetime));
+  vty_out (vty, "%s%s",
+           lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE);
+}
+
+void
+lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
+{
+  struct area_addr *area_addr;
+  int i;
+  struct listnode *lnode;
+  struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
+  struct ipv4_reachability *ipv4_reach;
+  struct in_addr *ipv4_addr;
+  struct te_ipv4_reachability *te_ipv4_reach;
+#ifdef HAVE_IPV6
+  struct ipv6_reachability *ipv6_reach;
+  struct in6_addr in6;
+  u_char buff[BUFSIZ];
+#endif
+  u_char LSPid[255];
+  u_char hostname[255];
+  u_char ipv4_reach_prefix[20];
+  u_char ipv4_reach_mask[20];
+  u_char ipv4_address[20];
+
+  lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1);
+  lsp_print (lsp, vty, dynhost);
+
+  /* for all area address */
+  if (lsp->tlv_data.area_addrs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.area_addrs, lnode, area_addr))
+      {
+       vty_out (vty, "  Area Address: %s%s",
+                isonet_print (area_addr->area_addr, area_addr->addr_len),
+                VTY_NEWLINE);
+      }
+  
+  /* for the nlpid tlv */
+  if (lsp->tlv_data.nlpids)
+    {
+      for (i = 0; i < lsp->tlv_data.nlpids->count; i++)
+       {
+         switch (lsp->tlv_data.nlpids->nlpids[i])
+           {
+           case NLPID_IP:
+           case NLPID_IPV6:
+             vty_out (vty, "  NLPID       : 0x%X%s",
+                      lsp->tlv_data.nlpids->nlpids[i], VTY_NEWLINE);
+             break;
+           default:
+             vty_out (vty, "  NLPID       : %s%s", "unknown", VTY_NEWLINE);
+             break;
+           }
+       }
+    }
+
+  /* for the hostname tlv */
+  if (lsp->tlv_data.hostname)
+    {
+      bzero (hostname, sizeof (hostname));
+      memcpy (hostname, lsp->tlv_data.hostname->name,
+             lsp->tlv_data.hostname->namelen);
+      vty_out (vty, "  Hostname    : %s%s", hostname, VTY_NEWLINE);
+    }
+
+  /* authentication tlv */
+  if (lsp->tlv_data.auth_info.type != ISIS_PASSWD_TYPE_UNUSED)
+    {
+      if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+        vty_out (vty, "  Auth type   : md5%s", VTY_NEWLINE);
+      else if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_CLEARTXT)
+        vty_out (vty, "  Auth type   : clear text%s", VTY_NEWLINE);
+    }
+
+  /* TE router id */
+  if (lsp->tlv_data.router_id)
+    {
+      memcpy (ipv4_address, inet_ntoa (lsp->tlv_data.router_id->id),
+             sizeof (ipv4_address));
+      vty_out (vty, "  Router ID   : %s%s", ipv4_address, VTY_NEWLINE);
+    }
+
+  if (lsp->tlv_data.ipv4_addrs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_addrs, lnode, ipv4_addr))
+      {
+        memcpy (ipv4_address, inet_ntoa (*ipv4_addr), sizeof (ipv4_address));
+        vty_out (vty, "  IPv4 Address: %s%s", ipv4_address, VTY_NEWLINE);
+      }
+
+  /* for the IS neighbor tlv */
+  if (lsp->tlv_data.is_neighs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, lnode, is_neigh))
+      {
+       lspid_print (is_neigh->neigh_id, LSPid, dynhost, 0);
+       vty_out (vty, "  Metric      : %-8d IS            : %s%s",
+                is_neigh->metrics.metric_default, LSPid, VTY_NEWLINE);
+      }
+  
+  /* for the internal reachable tlv */
+  if (lsp->tlv_data.ipv4_int_reachs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, lnode,
+                              ipv4_reach))
+    {
+      memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix),
+             sizeof (ipv4_reach_prefix));
+      memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask),
+             sizeof (ipv4_reach_mask));
+      vty_out (vty, "  Metric      : %-8d IPv4-Internal : %s %s%s",
+              ipv4_reach->metrics.metric_default, ipv4_reach_prefix,
+              ipv4_reach_mask, VTY_NEWLINE);
+    }
+
+  /* for the external reachable tlv */
+  if (lsp->tlv_data.ipv4_ext_reachs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, lnode, 
+                              ipv4_reach))
+    {
+      memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix),
+             sizeof (ipv4_reach_prefix));
+      memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask),
+             sizeof (ipv4_reach_mask));
+      vty_out (vty, "  Metric      : %-8d IPv4-External : %s %s%s",
+              ipv4_reach->metrics.metric_default, ipv4_reach_prefix,
+              ipv4_reach_mask, VTY_NEWLINE);
+    }
+  
+  /* IPv6 tlv */
+#ifdef HAVE_IPV6
+  if (lsp->tlv_data.ipv6_reachs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, lnode, ipv6_reach))
+    {
+      memset (&in6, 0, sizeof (in6));
+      memcpy (in6.s6_addr, ipv6_reach->prefix,
+             PSIZE (ipv6_reach->prefix_len));
+      inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ);
+      if ((ipv6_reach->control_info &
+          CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL)
+       vty_out (vty, "  Metric      : %-8d IPv6-Internal : %s/%d%s",
+                ntohl (ipv6_reach->metric),
+                buff, ipv6_reach->prefix_len, VTY_NEWLINE);
+      else
+       vty_out (vty, "  Metric      : %-8d IPv6-External : %s/%d%s",
+                ntohl (ipv6_reach->metric),
+                buff, ipv6_reach->prefix_len, VTY_NEWLINE);
+    }
+#endif
+
+  /* TE IS neighbor tlv */
+  if (lsp->tlv_data.te_is_neighs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh))
+    {
+      lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0);
+      vty_out (vty, "  Metric      : %-8d IS-Extended   : %s%s",
+              GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE);
+      if (IS_MPLS_TE(isisMplsTE))
+        mpls_te_print_detail(vty, te_is_neigh);
+    }
+
+  /* TE IPv4 tlv */
+  if (lsp->tlv_data.te_ipv4_reachs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, lnode,
+                              te_ipv4_reach))
+    {
+      /* FIXME: There should be better way to output this stuff. */
+      vty_out (vty, "  Metric      : %-8d IPv4-Extended : %s/%d%s",
+              ntohl (te_ipv4_reach->te_metric),
+              inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start,
+                                           te_ipv4_reach->control)),
+              te_ipv4_reach->control & 0x3F, VTY_NEWLINE);
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  return;
+}
+
+/* print all the lsps info in the local lspdb */
+int
+lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost)
+{
+
+  dnode_t *node = dict_first (lspdb), *next;
+  int lsp_count = 0;
+
+  if (detail == ISIS_UI_LEVEL_BRIEF)
+    {
+      while (node != NULL)
+       {
+         /* I think it is unnecessary, so I comment it out */
+         /* dict_contains (lspdb, node); */
+         next = dict_next (lspdb, node);
+         lsp_print (dnode_get (node), vty, dynhost);
+         node = next;
+         lsp_count++;
+       }
+    }
+  else if (detail == ISIS_UI_LEVEL_DETAIL)
+    {
+      while (node != NULL)
+       {
+         next = dict_next (lspdb, node);
+         lsp_print_detail (dnode_get (node), vty, dynhost);
+         node = next;
+         lsp_count++;
+       }
+    }
+
+  return lsp_count;
+}
+
+#define FRAG_THOLD(S,T) \
+  ((STREAM_SIZE(S)*T)/100)
+
+/* stream*, area->lsp_frag_threshold, increment */
+#define FRAG_NEEDED(S,T,I) \
+  (STREAM_SIZE(S)-STREAM_REMAIN(S)+(I) > FRAG_THOLD(S,T))
+
+/* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have
+ * variable length (TE TLVs, sub TLVs). */
+static void
+lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to,
+            int tlvsize, int frag_thold,
+            int tlv_build_func (struct list *, struct stream *))
+{
+  int count, i;
+
+  /* can we fit all ? */
+  if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2))
+    {
+      tlv_build_func (*from, lsp->pdu);
+      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));
+      count = count / tlvsize;
+      if (count > (int)listcount (*from))
+       count = listcount (*from);
+      for (i = 0; i < count; i++)
+       {
+         listnode_add (*to, listgetdata (listhead (*from)));
+         listnode_delete (*from, listgetdata (listhead (*from)));
+       }
+      tlv_build_func (*to, lsp->pdu);
+    }
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+  return;
+}
+
+/* Process IS_NEIGHBOURS TLV with TE subTLVs */
+static void
+lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, int frag_thold)
+{
+  int count, size = 0;
+  struct listnode *node, *nextnode;
+  struct te_is_neigh *elem;
+
+  /* Start computing real size of TLVs */
+  for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem))
+    size = size + elem->sub_tlvs_length + IS_NEIGHBOURS_LEN;
+
+  /* can we fit all ? */
+  if (!FRAG_NEEDED (lsp->pdu, frag_thold, size))
+    {
+      tlv_add_te_is_neighs (*from, lsp->pdu);
+      if (listcount (*to) != 0)
+        {
+          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
+    {
+      /* fit all we can */
+      /* Compute remaining place in LSP PDU */
+      count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 -
+        (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu));
+      /* Determine size of TE SubTLVs */
+      elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from));
+      count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN;
+      if (count > 0)
+        {
+          while (count > 0)
+            {
+              listnode_add (*to, listgetdata ((struct listnode *)listhead (*from)));
+              listnode_delete (*from, listgetdata ((struct listnode *)listhead (*from)));
+
+              elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from));
+              count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN;
+            }
+
+          tlv_add_te_is_neighs (*to, lsp->pdu);
+        }
+    }
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+  return;
+}
+
+static u_int16_t
+lsp_rem_lifetime (struct isis_area *area, int level)
+{
+  u_int16_t rem_lifetime;
+
+  /* Add jitter to configured LSP lifetime */
+  rem_lifetime = isis_jitter (area->max_lsp_lifetime[level - 1],
+                              MAX_AGE_JITTER);
+
+  /* No jitter if the max refresh will be less than configure gen interval */
+  /* N.B. this calucation is acceptable since rem_lifetime is in [332,65535] at
+   * this point */
+  if (area->lsp_gen_interval[level - 1] > (rem_lifetime - 300))
+    rem_lifetime = area->max_lsp_lifetime[level - 1];
+
+  return rem_lifetime;
+}
+
+static u_int16_t
+lsp_refresh_time (struct isis_lsp *lsp, u_int16_t rem_lifetime)
+{
+  struct isis_area *area = lsp->area;
+  int level = lsp->level;
+  u_int16_t refresh_time;
+
+  /* Add jitter to LSP refresh time */
+  refresh_time = isis_jitter (area->lsp_refresh[level - 1],
+                              MAX_LSP_GEN_JITTER);
+
+  /* RFC 4444 : make sure the refresh time is at least less than 300
+   * of the remaining lifetime and more than gen interval */
+  if (refresh_time <= area->lsp_gen_interval[level - 1] ||
+      refresh_time > (rem_lifetime - 300))
+    refresh_time = rem_lifetime - 300;
+
+  /* In cornercases, refresh_time might be <= lsp_gen_interval, however
+   * we accept this violation to satisfy refresh_time <= rem_lifetime - 300 */
+
+  return refresh_time;
+}
+
+static struct isis_lsp *
+lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area,
+              int level)
+{
+  struct isis_lsp *lsp;
+  u_char frag_id[ISIS_SYS_ID_LEN + 2];
+
+  memcpy (frag_id, lsp0->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 1);
+  LSP_FRAGMENT (frag_id) = frag_num;
+  /* FIXME add authentication TLV for fragment LSPs */
+  lsp = lsp_search (frag_id, area->lspdb[level - 1]);
+  if (lsp)
+    {
+      /* Clear the TLVs */
+      lsp_clear_data (lsp);
+      return lsp;
+    }
+  lsp = lsp_new (area, frag_id, ntohs(lsp0->lsp_header->rem_lifetime), 0,
+                 lsp_bits_generate (level, area->overload_bit,
+                 area->attached_bit), 0, level);
+  lsp->area = area;
+  lsp->own_lsp = 1;
+  lsp_insert (lsp, area->lspdb[level - 1]);
+  listnode_add (lsp0->lspu.frags, lsp);
+  lsp->lspu.zero_lsp = lsp0;
+  return lsp;
+}
+
+static void
+lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area,
+                         struct tlvs *tlv_data)
+{
+  struct route_table *er_table;
+  struct route_node *rn;
+  struct prefix_ipv4 *ipv4;
+  struct isis_ext_info *info;
+  struct ipv4_reachability *ipreach;
+  struct te_ipv4_reachability *te_ipreach;
+
+  er_table = get_ext_reach(area, AF_INET, lsp->level);
+  if (!er_table)
+    return;
+
+  for (rn = route_top(er_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+
+      ipv4 = (struct prefix_ipv4*)&rn->p;
+      info = rn->info;
+      if (area->oldmetric)
+        {
+          if (tlv_data->ipv4_ext_reachs == NULL)
+            {
+              tlv_data->ipv4_ext_reachs = list_new();
+              tlv_data->ipv4_ext_reachs->del = free_tlv;
+            }
+          ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach));
+
+          ipreach->prefix.s_addr = ipv4->prefix.s_addr;
+          masklen2ip(ipv4->prefixlen, &ipreach->mask);
+          ipreach->prefix.s_addr &= ipreach->mask.s_addr;
+
+          if ((info->metric & 0x3f) != info->metric)
+            ipreach->metrics.metric_default = 0x3f;
+          else
+            ipreach->metrics.metric_default = info->metric;
+          ipreach->metrics.metric_expense = METRICS_UNSUPPORTED;
+          ipreach->metrics.metric_error = METRICS_UNSUPPORTED;
+          ipreach->metrics.metric_delay = METRICS_UNSUPPORTED;
+          listnode_add(tlv_data->ipv4_ext_reachs, ipreach);
+        }
+      if (area->newmetric)
+        {
+          if (tlv_data->te_ipv4_reachs == NULL)
+            {
+              tlv_data->te_ipv4_reachs = list_new();
+              tlv_data->te_ipv4_reachs->del = free_tlv;
+            }
+          te_ipreach =
+              XCALLOC(MTYPE_ISIS_TLV,
+                      sizeof(*te_ipreach) - 1 + PSIZE(ipv4->prefixlen));
+          if (info->metric > MAX_WIDE_PATH_METRIC)
+            te_ipreach->te_metric = htonl(MAX_WIDE_PATH_METRIC);
+          else
+            te_ipreach->te_metric = htonl(info->metric);
+          te_ipreach->control = ipv4->prefixlen & 0x3f;
+          memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr,
+                 PSIZE(ipv4->prefixlen));
+          listnode_add(tlv_data->te_ipv4_reachs, te_ipreach);
+        }
+    }
+}
+
+static void
+lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
+                         struct tlvs *tlv_data)
+{
+  struct route_table *er_table;
+  struct route_node *rn;
+  struct prefix_ipv6 *ipv6;
+  struct isis_ext_info *info;
+  struct ipv6_reachability *ip6reach;
+
+  er_table = get_ext_reach(area, AF_INET6, lsp->level);
+  if (!er_table)
+    return;
+
+  for (rn = route_top(er_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+
+      ipv6 = (struct prefix_ipv6*)&rn->p;
+      info = rn->info;
+
+      if (tlv_data->ipv6_reachs == NULL)
+        {
+          tlv_data->ipv6_reachs = list_new();
+          tlv_data->ipv6_reachs->del = free_tlv;
+        }
+      ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach));
+      if (info->metric > MAX_WIDE_PATH_METRIC)
+        ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC);
+      else
+        ip6reach->metric = htonl(info->metric);
+      ip6reach->control_info = DISTRIBUTION_EXTERNAL;
+      ip6reach->prefix_len = ipv6->prefixlen;
+      memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix));
+      listnode_add(tlv_data->ipv6_reachs, ip6reach);
+    }
+}
+
+static void
+lsp_build_ext_reach (struct isis_lsp *lsp, struct isis_area *area,
+                     struct tlvs *tlv_data)
+{
+  lsp_build_ext_reach_ipv4(lsp, area, tlv_data);
+  lsp_build_ext_reach_ipv6(lsp, area, tlv_data);
+}
+
+/*
+ * Builds the LSP data part. This func creates a new frag whenever 
+ * area->lsp_frag_threshold is exceeded.
+ */
+static void
+lsp_build (struct isis_lsp *lsp, struct isis_area *area)
+{
+  struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
+  struct listnode *node, *ipnode;
+  int level = lsp->level;
+  struct isis_circuit *circuit;
+  struct prefix_ipv4 *ipv4;
+  struct ipv4_reachability *ipreach;
+  struct te_ipv4_reachability *te_ipreach;
+  struct isis_adjacency *nei;
+#ifdef HAVE_IPV6
+  struct prefix_ipv6 *ipv6, ip6prefix;
+  struct ipv6_reachability *ip6reach;
+#endif /* HAVE_IPV6 */
+  struct tlvs tlv_data;
+  struct isis_lsp *lsp0 = lsp;
+  struct in_addr *routerid;
+  uint32_t expected = 0, found = 0;
+  uint32_t metric;
+  u_char zero_id[ISIS_SYS_ID_LEN + 1];
+  int retval = ISIS_OK;
+  char buf[BUFSIZ];
+
+  lsp_debug("ISIS (%s): Constructing local system LSP for level %d", area->area_tag, level);
+
+  /*
+   * Building the zero lsp
+   */
+  memset (zero_id, 0, ISIS_SYS_ID_LEN + 1);
+
+  /* Reset stream endp. Stream is always there and on every LSP refresh only
+   * TLV part of it is overwritten. So we must seek past header we will not
+   * touch. */
+  stream_reset (lsp->pdu);
+  stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+
+  /*
+   * Add the authentication info if its present
+   */
+  lsp_auth_add (lsp);
+
+  /*
+   * First add the tlvs related to area
+   */
+
+  /* Area addresses */
+  if (lsp->tlv_data.area_addrs == NULL)
+    lsp->tlv_data.area_addrs = list_new ();
+  list_add_list (lsp->tlv_data.area_addrs, area->area_addrs);
+  if (listcount (lsp->tlv_data.area_addrs) > 0)
+    tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu);
+
+  /* Protocols Supported */
+  if (area->ip_circuits > 0
+#ifdef HAVE_IPV6
+      || area->ipv6_circuits > 0
+#endif /* HAVE_IPV6 */
+    )
+    {
+      lsp->tlv_data.nlpids = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct nlpids));
+      lsp->tlv_data.nlpids->count = 0;
+      if (area->ip_circuits > 0)
+       {
+         lsp_debug("ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs", area->area_tag);
+         lsp->tlv_data.nlpids->count++;
+         lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP;
+       }
+#ifdef HAVE_IPV6
+      if (area->ipv6_circuits > 0)
+       {
+         lsp_debug("ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs", area->area_tag);
+         lsp->tlv_data.nlpids->count++;
+         lsp->tlv_data.nlpids->nlpids[lsp->tlv_data.nlpids->count - 1] =
+           NLPID_IPV6;
+       }
+#endif /* HAVE_IPV6 */
+      tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu);
+    }
+
+  /* Dynamic Hostname */
+  if (area->dynhostname)
+    {
+      const char *hostname = unix_hostname();
+      size_t hostname_len = strlen(hostname);
+
+      lsp->tlv_data.hostname = XMALLOC (MTYPE_ISIS_TLV,
+                                       sizeof (struct hostname));
+
+      strncpy((char *)lsp->tlv_data.hostname->name, hostname,
+              sizeof(lsp->tlv_data.hostname->name));
+      if (hostname_len <= MAX_TLV_LEN)
+        lsp->tlv_data.hostname->namelen = hostname_len;
+      else
+        lsp->tlv_data.hostname->namelen = MAX_TLV_LEN;
+
+      lsp_debug("ISIS (%s): Adding dynamic hostname '%.*s'", area->area_tag,
+                lsp->tlv_data.hostname->namelen, lsp->tlv_data.hostname->name);
+      tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu);
+    }
+  else
+    {
+      lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag);
+    }
+
+  /* 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. */
+  if (isis->router_id != 0)
+    {
+      inet_ntop(AF_INET, &isis->router_id, buf, sizeof(buf));
+      lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf);
+      if (lsp->tlv_data.ipv4_addrs == NULL)
+       {
+         lsp->tlv_data.ipv4_addrs = list_new ();
+         lsp->tlv_data.ipv4_addrs->del = free_tlv;
+       }
+
+      routerid = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct in_addr));
+      routerid->s_addr = isis->router_id;
+      listnode_add (lsp->tlv_data.ipv4_addrs, routerid);
+      tlv_add_in_addr (routerid, lsp->pdu, IPV4_ADDR);
+
+      /* Exactly same data is put into TE router ID TLV, but only if new style
+       * TLV's are in use. */
+      if (area->newmetric)
+       {
+          lsp_debug("ISIS (%s): Adding router ID also as TE router ID tlv.", area->area_tag);
+         lsp->tlv_data.router_id = XMALLOC (MTYPE_ISIS_TLV,
+                                            sizeof (struct in_addr));
+         lsp->tlv_data.router_id->id.s_addr = isis->router_id;
+         tlv_add_in_addr (&lsp->tlv_data.router_id->id, lsp->pdu,
+                           TE_ROUTER_ID);
+       }
+    }
+  else
+    {
+      lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.", area->area_tag);
+    }
+
+  memset (&tlv_data, 0, sizeof (struct tlvs));
+
+#ifdef TOPOLOGY_GENERATE
+  /* If topology exists (and we create topology for level 1 only), create
+   * (hardcoded) link to topology. */
+  if (area->topology && level == IS_LEVEL_1)
+    {
+      if (tlv_data.is_neighs == NULL)
+       {
+         tlv_data.is_neighs = list_new ();
+         tlv_data.is_neighs->del = free_tlv;
+       }
+      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+
+      memcpy (&is_neigh->neigh_id, area->topology_baseis, ISIS_SYS_ID_LEN);
+      is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (1 & 0xFF);
+      is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((1 >> 8) & 0xFF);
+      is_neigh->metrics.metric_default = 0x01;
+      is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED;
+      is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED;
+      is_neigh->metrics.metric_error = METRICS_UNSUPPORTED;
+      listnode_add (tlv_data.is_neighs, is_neigh);
+    }
+#endif /* TOPOLOGY_GENERATE */
+
+  lsp_debug("ISIS (%s): Adding circuit specific information.", area->area_tag);
+
+  /*
+   * Then build lists of tlvs related to circuits
+   */
+  for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit))
+    {
+      if (!circuit->interface)
+        lsp_debug("ISIS (%s): Processing %s circuit %p with unknown interface",
+                  area->area_tag, circuit_type2string(circuit->circ_type), circuit);
+      else
+        lsp_debug("ISIS (%s): Processing %s circuit %s",
+                  area->area_tag, circuit_type2string(circuit->circ_type), circuit->interface->name);
+
+      if (circuit->state != C_STATE_UP)
+        {
+          lsp_debug("ISIS (%s): Circuit is not up, ignoring.", area->area_tag);
+          continue;
+        }
+
+      /*
+       * Add IPv4 internal reachability of this circuit
+       */
+      if (circuit->ip_router && circuit->ip_addrs &&
+         circuit->ip_addrs->count > 0)
+       {
+         lsp_debug("ISIS (%s): Circuit has IPv4 active, adding respective TLVs.", area->area_tag);
+         if (area->oldmetric)
+           {
+             if (tlv_data.ipv4_int_reachs == NULL)
+               {
+                 tlv_data.ipv4_int_reachs = list_new ();
+                 tlv_data.ipv4_int_reachs->del = free_tlv;
+               }
+             for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4))
+               {
+                 ipreach =
+                   XMALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability));
+                 ipreach->metrics.metric_default = circuit->metric[level - 1];
+                 ipreach->metrics.metric_expense = METRICS_UNSUPPORTED;
+                 ipreach->metrics.metric_error = METRICS_UNSUPPORTED;
+                 ipreach->metrics.metric_delay = METRICS_UNSUPPORTED;
+                 masklen2ip (ipv4->prefixlen, &ipreach->mask);
+                 ipreach->prefix.s_addr = ((ipreach->mask.s_addr) &
+                                           (ipv4->prefix.s_addr));
+                 inet_ntop(AF_INET, &ipreach->prefix.s_addr, buf, sizeof(buf));
+                 lsp_debug("ISIS (%s): Adding old-style IP reachability for %s/%d",
+                           area->area_tag, buf, ipv4->prefixlen);
+                 listnode_add (tlv_data.ipv4_int_reachs, ipreach);
+               }
+           }
+         if (area->newmetric)
+           {
+             if (tlv_data.te_ipv4_reachs == NULL)
+               {
+                 tlv_data.te_ipv4_reachs = list_new ();
+                 tlv_data.te_ipv4_reachs->del = free_tlv;
+               }
+             for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4))
+               {
+                 /* FIXME All this assumes that we have no sub TLVs. */
+                 te_ipreach = XCALLOC (MTYPE_ISIS_TLV,
+                                       sizeof (struct te_ipv4_reachability) +
+                                       ((ipv4->prefixlen + 7)/8) - 1);
+
+                 if (area->oldmetric)
+                   te_ipreach->te_metric = htonl (circuit->metric[level - 1]);
+                 else
+                   te_ipreach->te_metric = htonl (circuit->te_metric[level - 1]);
+
+                 te_ipreach->control = (ipv4->prefixlen & 0x3F);
+                 memcpy (&te_ipreach->prefix_start, &ipv4->prefix.s_addr,
+                         (ipv4->prefixlen + 7)/8);
+                 inet_ntop(AF_INET, &ipv4->prefix.s_addr, buf, sizeof(buf));
+                 lsp_debug("ISIS (%s): Adding te-style IP reachability for %s/%d",
+                           area->area_tag, buf, ipv4->prefixlen);
+                 listnode_add (tlv_data.te_ipv4_reachs, te_ipreach);
+               }
+           }
+       }
+
+#ifdef HAVE_IPV6
+      /*
+       * Add IPv6 reachability of this circuit
+       */
+      if (circuit->ipv6_router && circuit->ipv6_non_link &&
+         circuit->ipv6_non_link->count > 0)
+       {
+
+         if (tlv_data.ipv6_reachs == NULL)
+           {
+             tlv_data.ipv6_reachs = list_new ();
+             tlv_data.ipv6_reachs->del = free_tlv;
+           }
+          for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6))
+           {
+             ip6reach =
+               XCALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv6_reachability));
+
+             if (area->oldmetric)
+               ip6reach->metric =
+                         htonl (circuit->metric[level - 1]);
+             else
+                 ip6reach->metric = htonl (circuit->te_metric[level - 1]);
+
+             ip6reach->control_info = 0;
+             ip6reach->prefix_len = ipv6->prefixlen;
+             memcpy(&ip6prefix, ipv6, sizeof(ip6prefix));
+             apply_mask_ipv6(&ip6prefix);
+
+             inet_ntop(AF_INET6, &ip6prefix.prefix.s6_addr, buf, sizeof(buf));
+             lsp_debug("ISIS (%s): Adding IPv6 reachability for %s/%d",
+                       area->area_tag, buf, ipv6->prefixlen);
+
+             memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr,
+                     sizeof (ip6reach->prefix));
+             listnode_add (tlv_data.ipv6_reachs, ip6reach);
+           }
+       }
+#endif /* HAVE_IPV6 */
+
+      switch (circuit->circ_type)
+       {
+       case CIRCUIT_T_BROADCAST:
+         if (level & circuit->is_type)
+           {
+             if (area->oldmetric)
+               {
+                 if (tlv_data.is_neighs == NULL)
+                   {
+                     tlv_data.is_neighs = list_new ();
+                     tlv_data.is_neighs->del = free_tlv;
+                   }
+                 is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+                 if (level == IS_LEVEL_1)
+                   memcpy (is_neigh->neigh_id,
+                           circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+                 else
+                   memcpy (is_neigh->neigh_id,
+                           circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+                 is_neigh->metrics.metric_default = circuit->metric[level - 1];
+                 is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED;
+                 is_neigh->metrics.metric_error = METRICS_UNSUPPORTED;
+                 is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED;
+                  if (!memcmp (is_neigh->neigh_id, zero_id,
+                               ISIS_SYS_ID_LEN + 1))
+                    {
+                      XFREE (MTYPE_ISIS_TLV, is_neigh);
+                      lsp_debug("ISIS (%s): No DIS for circuit, not adding old-style IS neighbor.",
+                                area->area_tag);
+                    }
+                  else
+                    {
+                      listnode_add (tlv_data.is_neighs, is_neigh);
+                      lsp_debug("ISIS (%s): Adding DIS %s.%02x as old-style neighbor",
+                                area->area_tag, sysid_print(is_neigh->neigh_id),
+                                LSP_PSEUDO_ID(is_neigh->neigh_id));
+                    }
+               }
+             if (area->newmetric)
+               {
+                 if (tlv_data.te_is_neighs == NULL)
+                   {
+                     tlv_data.te_is_neighs = list_new ();
+                     tlv_data.te_is_neighs->del = free_tlv;
+                   }
+                 te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+                                        sizeof (struct te_is_neigh));
+                 if (level == IS_LEVEL_1)
+                   memcpy (te_is_neigh->neigh_id,
+                           circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+                 else
+                   memcpy (te_is_neigh->neigh_id,
+                           circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+                 if (area->oldmetric)
+                   metric = circuit->metric[level - 1];
+                 else
+                   metric = circuit->te_metric[level - 1];
+                 SET_TE_METRIC(te_is_neigh, metric);
+                  if (!memcmp (te_is_neigh->neigh_id, zero_id,
+                               ISIS_SYS_ID_LEN + 1))
+                    {
+                      XFREE (MTYPE_ISIS_TLV, te_is_neigh);
+                      lsp_debug("ISIS (%s): No DIS for circuit, not adding te-style IS neighbor.",
+                                area->area_tag);
+                    }
+                  else
+                    {
+                      /* Check if MPLS_TE is activate */
+                      if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface))
+                        /* Add SubTLVs & Adjust real size of SubTLVs */
+                        te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc);
+                      else
+                        /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */
+                        te_is_neigh->sub_tlvs_length = 0;
+
+                      listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+                      lsp_debug("ISIS (%s): Adding DIS %s.%02x as te-style neighbor",
+                                area->area_tag, sysid_print(te_is_neigh->neigh_id),
+                                LSP_PSEUDO_ID(te_is_neigh->neigh_id));
+                    }
+               }
+           }
+         else
+           {
+             lsp_debug("ISIS (%s): Circuit is not active for current level. Not adding IS neighbors",
+                       area->area_tag);
+           }
+         break;
+       case CIRCUIT_T_P2P:
+         nei = circuit->u.p2p.neighbor;
+         if (nei && (level & nei->circuit_t))
+           {
+             if (area->oldmetric)
+               {
+                 if (tlv_data.is_neighs == NULL)
+                   {
+                     tlv_data.is_neighs = list_new ();
+                     tlv_data.is_neighs->del = free_tlv;
+                   }
+                 is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+                 memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN);
+                 is_neigh->metrics.metric_default = circuit->metric[level - 1];
+                 is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED;
+                 is_neigh->metrics.metric_error = METRICS_UNSUPPORTED;
+                 is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED;
+                 listnode_add (tlv_data.is_neighs, is_neigh);
+                 lsp_debug("ISIS (%s): Adding old-style is reach for %s", area->area_tag,
+                            sysid_print(is_neigh->neigh_id));
+               }
+             if (area->newmetric)
+               {
+                 uint32_t metric;
+
+                 if (tlv_data.te_is_neighs == NULL)
+                   {
+                     tlv_data.te_is_neighs = list_new ();
+                     tlv_data.te_is_neighs->del = free_tlv;
+                   }
+                 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 = circuit->te_metric[level - 1];
+                 SET_TE_METRIC(te_is_neigh, metric);
+                 /* Check if MPLS_TE is activate */
+                  if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface))
+                    /* Update Local and Remote IP address for MPLS TE circuit parameters */
+                    /* NOTE sure that it is the pertinent place for that updates */
+                    /* Local IP address could be updated in isis_circuit.c - isis_circuit_add_addr() */
+                    /* But, where update remote IP address ? in isis_pdu.c - process_p2p_hello() ? */
+
+                    /* Add SubTLVs & Adjust real size of SubTLVs */
+                    te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc);
+                  else
+                    /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */
+                    te_is_neigh->sub_tlvs_length = 0;
+                 listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+                 lsp_debug("ISIS (%s): Adding te-style is reach for %s", area->area_tag,
+                            sysid_print(te_is_neigh->neigh_id));
+               }
+           }
+          else
+            {
+              lsp_debug("ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors",
+              area->area_tag);
+            }
+         break;
+       case CIRCUIT_T_LOOPBACK:
+          break;
+       default:
+         zlog_warn ("lsp_area_create: unknown circuit type");
+       }
+    }
+
+  lsp_build_ext_reach(lsp, area, &tlv_data);
+
+  lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag);
+
+  while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs))
+    {
+      if (lsp->tlv_data.ipv4_int_reachs == NULL)
+       lsp->tlv_data.ipv4_int_reachs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs,
+                  &lsp->tlv_data.ipv4_int_reachs,
+                  IPV4_REACH_LEN, area->lsp_frag_threshold,
+                  tlv_add_ipv4_int_reachs);
+      if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+
+  while (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs))
+    {
+      if (lsp->tlv_data.ipv4_ext_reachs == NULL)
+       lsp->tlv_data.ipv4_ext_reachs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.ipv4_ext_reachs,
+                  &lsp->tlv_data.ipv4_ext_reachs,
+                  IPV4_REACH_LEN, area->lsp_frag_threshold,
+                  tlv_add_ipv4_ext_reachs);
+      if (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+
+  /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit()
+   * for now. lsp_tlv_fit() needs to be fixed to deal with variable length
+   * TLVs (sub TLVs!). */
+  while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs))
+    {
+      if (lsp->tlv_data.te_ipv4_reachs == NULL)
+       lsp->tlv_data.te_ipv4_reachs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs,
+                  &lsp->tlv_data.te_ipv4_reachs,
+                  TE_IPV4_REACH_LEN, area->lsp_frag_threshold,
+                  tlv_add_te_ipv4_reachs);
+      if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+
+#ifdef  HAVE_IPV6
+  while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs))
+    {
+      if (lsp->tlv_data.ipv6_reachs == NULL)
+       lsp->tlv_data.ipv6_reachs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs,
+                  &lsp->tlv_data.ipv6_reachs,
+                  IPV6_REACH_LEN, area->lsp_frag_threshold,
+                  tlv_add_ipv6_reachs);
+      if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+#endif /* HAVE_IPV6 */
+
+  while (tlv_data.is_neighs && listcount (tlv_data.is_neighs))
+    {
+      if (lsp->tlv_data.is_neighs == NULL)
+       lsp->tlv_data.is_neighs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.is_neighs,
+                  &lsp->tlv_data.is_neighs,
+                  IS_NEIGHBOURS_LEN, area->lsp_frag_threshold,
+                  tlv_add_is_neighs);
+      if (tlv_data.is_neighs && listcount (tlv_data.is_neighs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+
+  while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
+    {
+      if (lsp->tlv_data.te_is_neighs == NULL)
+       lsp->tlv_data.te_is_neighs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs,
+                  IS_NEIGHBOURS_LEN, area->lsp_frag_threshold,
+                  tlv_add_te_is_neighs);
+      if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, level);
+    }
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+
+  free_tlvs (&tlv_data);
+
+  /* Validate the LSP */
+  retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) +
+                       ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN,
+                       stream_get_endp (lsp->pdu) -
+                       ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN,
+                       &expected, &found, &tlv_data, NULL);
+  assert (retval == ISIS_OK);
+
+  return;
+}
+
+/*
+ * 7.3.7 and 7.3.9 Generation on non-pseudonode LSPs
+ */
+int
+lsp_generate (struct isis_area *area, int level)
+{
+  struct isis_lsp *oldlsp, *newlsp;
+  u_int32_t seq_num = 0;
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+  u_int16_t rem_lifetime, refresh_time;
+
+  if ((area == NULL) || (area->is_type & level) != level)
+    return ISIS_ERROR;
+
+  memset (&lspid, 0, ISIS_SYS_ID_LEN + 2);
+  memcpy (&lspid, isis->sysid, ISIS_SYS_ID_LEN);
+
+  /* only builds the lsp if the area shares the level */
+  oldlsp = lsp_search (lspid, area->lspdb[level - 1]);
+  if (oldlsp)
+    {
+      /* FIXME: we should actually initiate a purge */
+      seq_num = ntohl (oldlsp->lsp_header->seq_num);
+      lsp_search_and_destroy (oldlsp->lsp_header->lsp_id,
+                              area->lspdb[level - 1]);
+    }
+  rem_lifetime = lsp_rem_lifetime (area, level);
+  newlsp = lsp_new (area, lspid, rem_lifetime, seq_num,
+                    area->is_type | area->overload_bit | area->attached_bit,
+                    0, level);
+  newlsp->area = area;
+  newlsp->own_lsp = 1;
+
+  lsp_insert (newlsp, area->lspdb[level - 1]);
+  /* build_lsp_data (newlsp, area); */
+  lsp_build (newlsp, area);
+  /* time to calculate our checksum */
+  lsp_seqnum_update (newlsp);
+  newlsp->last_generated = time(NULL);
+  lsp_set_all_srmflags (newlsp);
+
+  refresh_time = lsp_refresh_time (newlsp, rem_lifetime);
+
+  THREAD_TIMER_OFF (area->t_lsp_refresh[level - 1]);
+  area->lsp_regenerate_pending[level - 1] = 0;
+  if (level == IS_LEVEL_1)
+    THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1],
+                     lsp_l1_refresh, area, refresh_time);
+  else if (level == IS_LEVEL_2)
+    THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1],
+                     lsp_l2_refresh, area, refresh_time);
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Building L%d LSP %s, len %d, "
+                  "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us",
+                  area->area_tag, level,
+                  rawlspid_print (newlsp->lsp_header->lsp_id),
+                  ntohl (newlsp->lsp_header->pdu_len),
+                  ntohl (newlsp->lsp_header->seq_num),
+                  ntohs (newlsp->lsp_header->checksum),
+                  ntohs (newlsp->lsp_header->rem_lifetime),
+                  refresh_time);
+    }
+  sched_debug("ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.",
+              area->area_tag, level);
+
+  return ISIS_OK;
+}
+
+/*
+ * Search own LSPs, update holding time and set SRM
+ */
+static int
+lsp_regenerate (struct isis_area *area, int level)
+{
+  dict_t *lspdb;
+  struct isis_lsp *lsp, *frag;
+  struct listnode *node;
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+  u_int16_t rem_lifetime, refresh_time;
+
+  if ((area == NULL) || (area->is_type & level) != level)
+    return ISIS_ERROR;
+
+  lspdb = area->lspdb[level - 1];
+
+  memset (lspid, 0, ISIS_SYS_ID_LEN + 2);
+  memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
+
+  lsp = lsp_search (lspid, lspdb);
+
+  if (!lsp)
+    {
+      zlog_err ("ISIS-Upd (%s): lsp_regenerate: no L%d LSP found!",
+                area->area_tag, level);
+      return ISIS_ERROR;
+    }
+
+  lsp_clear_data (lsp);
+  lsp_build (lsp, area);
+  lsp->lsp_header->lsp_bits = lsp_bits_generate (level, area->overload_bit,
+                                                 area->attached_bit);
+  rem_lifetime = lsp_rem_lifetime (area, level);
+  lsp->lsp_header->rem_lifetime = htons (rem_lifetime);
+  lsp_seqnum_update (lsp);
+
+  lsp->last_generated = time (NULL);
+  lsp_set_all_srmflags (lsp);
+  for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag))
+    {
+      frag->lsp_header->lsp_bits = lsp_bits_generate (level,
+                                                      area->overload_bit,
+                                                      area->attached_bit);
+      /* Set the lifetime values of all the fragments to the same value,
+       * so that no fragment expires before the lsp is refreshed.
+       */
+      frag->lsp_header->rem_lifetime = htons (rem_lifetime);
+      lsp_set_all_srmflags (frag);
+    }
+
+  refresh_time = lsp_refresh_time (lsp, rem_lifetime);
+  if (level == IS_LEVEL_1)
+    THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1],
+                     lsp_l1_refresh, area, refresh_time);
+  else if (level == IS_LEVEL_2)
+    THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1],
+                     lsp_l2_refresh, area, refresh_time);
+  area->lsp_regenerate_pending[level - 1] = 0;
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Refreshing our L%d LSP %s, len %d, "
+                  "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us",
+                  area->area_tag, level,
+                  rawlspid_print (lsp->lsp_header->lsp_id),
+                  ntohl (lsp->lsp_header->pdu_len),
+                  ntohl (lsp->lsp_header->seq_num),
+                  ntohs (lsp->lsp_header->checksum),
+                  ntohs (lsp->lsp_header->rem_lifetime),
+                  refresh_time);
+    }
+  sched_debug("ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.",
+              area->area_tag, level);
+
+  return ISIS_OK;
+}
+
+/*
+ * Something has changed or periodic refresh -> regenerate LSP
+ */
+static int
+lsp_l1_refresh (struct thread *thread)
+{
+  struct isis_area *area;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->t_lsp_refresh[0] = NULL;
+  area->lsp_regenerate_pending[0] = 0;
+
+  if ((area->is_type & IS_LEVEL_1) == 0)
+    return ISIS_ERROR;
+
+  sched_debug("ISIS (%s): LSP L1 refresh timer expired. Refreshing LSP...", area->area_tag);
+  return lsp_regenerate (area, IS_LEVEL_1);
+}
+
+static int
+lsp_l2_refresh (struct thread *thread)
+{
+  struct isis_area *area;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->t_lsp_refresh[1] = NULL;
+  area->lsp_regenerate_pending[1] = 0;
+
+  if ((area->is_type & IS_LEVEL_2) == 0)
+    return ISIS_ERROR;
+
+  sched_debug("ISIS (%s): LSP L2 refresh timer expired. Refreshing LSP...", area->area_tag);
+  return lsp_regenerate (area, IS_LEVEL_2);
+}
+
+int
+lsp_regenerate_schedule (struct isis_area *area, int level, int all_pseudo)
+{
+  struct isis_lsp *lsp;
+  u_char id[ISIS_SYS_ID_LEN + 2];
+  time_t now, diff;
+  long timeout;
+  struct listnode *cnode;
+  struct isis_circuit *circuit;
+  int lvl;
+
+  if (area == NULL)
+    return ISIS_ERROR;
+
+  sched_debug("ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs",
+            area->area_tag, circuit_t2string(level), all_pseudo ? "" : "not ");
+
+  memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (id) = LSP_FRAGMENT (id) = 0;
+  now = time (NULL);
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++)
+    {
+      if (!((level & lvl) && (area->is_type & lvl)))
+        continue;
+
+      sched_debug("ISIS (%s): Checking whether L%d needs to be scheduled",
+                  area->area_tag, lvl);
+
+      if (area->lsp_regenerate_pending[lvl - 1])
+        {
+          struct timeval remain = thread_timer_remain(area->t_lsp_refresh[lvl - 1]);
+          sched_debug("ISIS (%s): Regeneration is already pending, nothing todo."
+                      " (Due in %lld.%03lld seconds)", area->area_tag,
+                      (long long)remain.tv_sec, (long long)remain.tv_usec / 1000);
+          continue;
+        }
+
+      lsp = lsp_search (id, area->lspdb[lvl - 1]);
+      if (!lsp)
+        {
+          sched_debug("ISIS (%s): We do not have any LSPs to regenerate, nothing todo.",
+                      area->area_tag);
+          continue;
+        }
+
+      /*
+       * Throttle avoidance
+       */
+      sched_debug("ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld",
+                  area->area_tag, (long long)lsp->last_generated, (long long)now);
+      THREAD_TIMER_OFF (area->t_lsp_refresh[lvl - 1]);
+      diff = now - lsp->last_generated;
+      if (diff < area->lsp_gen_interval[lvl - 1])
+        {
+          timeout = 1000 * (area->lsp_gen_interval[lvl - 1] - diff);
+          sched_debug("ISIS (%s): Scheduling in %ld ms to match configured lsp_gen_interval",
+                      area->area_tag, timeout);
+        }
+      else
+        {
+          /*
+           * lsps are not regenerated if lsp_regenerate function is called
+           * directly. However if the lsp_regenerate call is queued for
+           * later execution it works.
+           */
+          timeout = 100;
+          sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago."
+                      " Scheduling for execution in %ld ms.", area->area_tag, timeout);
+        }
+
+      area->lsp_regenerate_pending[lvl - 1] = 1;
+      if (lvl == IS_LEVEL_1)
+        {
+          THREAD_TIMER_MSEC_ON(master, area->t_lsp_refresh[lvl - 1],
+                               lsp_l1_refresh, area, timeout);
+        }
+      else if (lvl == IS_LEVEL_2)
+        {
+          THREAD_TIMER_MSEC_ON(master, area->t_lsp_refresh[lvl - 1],
+                               lsp_l2_refresh, area, timeout);
+        }
+    }
+
+  if (all_pseudo)
+    {
+      for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+        lsp_regenerate_schedule_pseudo (circuit, level);
+    }
+
+  return ISIS_OK;
+}
+
+/*
+ * Funcs for pseudonode LSPs
+ */
+
+/*
+ * 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs 
+ */
+static void
+lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
+                 int level)
+{
+  struct isis_adjacency *adj;
+  struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
+  struct es_neigh *es_neigh;
+  struct list *adj_list;
+  struct listnode *node;
+  struct isis_area *area = circuit->area;
+
+  lsp_debug("ISIS (%s): Constructing pseudo LSP %s for interface %s level %d",
+            area->area_tag, rawlspid_print(lsp->lsp_header->lsp_id),
+            circuit->interface->name, level);
+
+  lsp->level = level;
+  /* RFC3787  section 4 SHOULD not set overload bit in pseudo LSPs */
+  lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0,
+                                                 circuit->area->attached_bit);
+
+  /*
+   * add self to IS neighbours 
+   */
+  if (circuit->area->oldmetric)
+    {
+      if (lsp->tlv_data.is_neighs == NULL)
+       {
+         lsp->tlv_data.is_neighs = list_new ();
+         lsp->tlv_data.is_neighs->del = free_tlv;
+       }
+      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+
+      memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
+      listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+      lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (self)",
+                area->area_tag, sysid_print(is_neigh->neigh_id),
+                LSP_PSEUDO_ID(is_neigh->neigh_id));
+    }
+  if (circuit->area->newmetric)
+    {
+      if (lsp->tlv_data.te_is_neighs == NULL)
+       {
+         lsp->tlv_data.te_is_neighs = list_new ();
+         lsp->tlv_data.te_is_neighs->del = free_tlv;
+       }
+      te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh));
+
+      memcpy (&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
+      listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh);
+      lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (self)",
+                area->area_tag, sysid_print(te_is_neigh->neigh_id),
+                LSP_PSEUDO_ID(te_is_neigh->neigh_id));
+    }
+
+  adj_list = list_new ();
+  isis_adj_build_up_list (circuit->u.bc.adjdb[level - 1], adj_list);
+
+  for (ALL_LIST_ELEMENTS_RO (adj_list, node, adj))
+    {
+      if (adj->level & level)
+       {
+         if ((level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) ||
+             (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L2_IS &&
+             adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
+             (level == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS))
+           {
+             /* an IS neighbour -> add it */
+             if (circuit->area->oldmetric)
+               {
+                 is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+
+                 memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN);
+                 listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+                 lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (peer)",
+                           area->area_tag, sysid_print(is_neigh->neigh_id),
+                           LSP_PSEUDO_ID(is_neigh->neigh_id));
+               }
+             if (circuit->area->newmetric)
+               {
+                 te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+                                        sizeof (struct te_is_neigh));
+                 memcpy (&te_is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN);
+                 listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh);
+                 lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (peer)",
+                           area->area_tag, sysid_print(te_is_neigh->neigh_id),
+                           LSP_PSEUDO_ID(te_is_neigh->neigh_id));
+               }
+           }
+         else if (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_ES)
+           {
+             /* an ES neigbour add it, if we are building level 1 LSP */
+             /* FIXME: the tlv-format is hard to use here */
+             if (lsp->tlv_data.es_neighs == NULL)
+               {
+                 lsp->tlv_data.es_neighs = list_new ();
+                 lsp->tlv_data.es_neighs->del = free_tlv;
+               }
+             es_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct es_neigh));
+             
+             memcpy (&es_neigh->first_es_neigh, adj->sysid, ISIS_SYS_ID_LEN);
+             listnode_add (lsp->tlv_data.es_neighs, es_neigh);
+             lsp_debug("ISIS (%s): Adding %s as ES neighbor (peer)",
+                       area->area_tag, sysid_print(es_neigh->first_es_neigh));
+           }
+         else
+           {
+             lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not match",
+                       area->area_tag, sysid_print(adj->sysid));
+           }
+       }
+      else
+        {
+         lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not intersect",
+                   area->area_tag, sysid_print(adj->sysid));
+       }
+    }
+  list_delete (adj_list);
+
+  lsp_debug("ISIS (%s): Pseudo LSP construction is complete.", area->area_tag);
+
+  /* Reset endp of stream to overwrite only TLV part of it. */
+  stream_reset (lsp->pdu);
+  stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+
+  /*
+   * Add the authentication info if it's present
+   */
+  lsp_auth_add (lsp);
+
+  if (lsp->tlv_data.is_neighs && listcount (lsp->tlv_data.is_neighs) > 0)
+    tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu);
+
+  if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0)
+    tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu);
+
+  if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0)
+    tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);
+
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+
+  /* Recompute authentication and checksum information */
+  lsp_auth_update (lsp);
+  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+                    ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+
+  return;
+}
+
+int
+lsp_generate_pseudo (struct isis_circuit *circuit, int level)
+{
+  dict_t *lspdb = circuit->area->lspdb[level - 1];
+  struct isis_lsp *lsp;
+  u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  u_int16_t rem_lifetime, refresh_time;
+
+  if ((circuit->is_type & level) != level ||
+      (circuit->state != C_STATE_UP) ||
+      (circuit->circ_type != CIRCUIT_T_BROADCAST) ||
+      (circuit->u.bc.is_dr[level - 1] == 0))
+    return ISIS_ERROR;
+
+  memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+  LSP_FRAGMENT (lsp_id) = 0;
+  LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id;
+
+  /*
+   * If for some reason have a pseudo LSP in the db already -> regenerate
+   */
+  if (lsp_search (lsp_id, lspdb))
+    return lsp_regenerate_schedule_pseudo (circuit, level);
+
+  rem_lifetime = lsp_rem_lifetime (circuit->area, level);
+  /* RFC3787  section 4 SHOULD not set overload bit in pseudo LSPs */
+  lsp = lsp_new (circuit->area, lsp_id, rem_lifetime, 1,
+                 circuit->area->is_type | circuit->area->attached_bit,
+                 0, level);
+  lsp->area = circuit->area;
+
+  lsp_build_pseudo (lsp, circuit, level);
+
+  lsp->own_lsp = 1;
+  lsp_insert (lsp, lspdb);
+  lsp_set_all_srmflags (lsp);
+
+  refresh_time = lsp_refresh_time (lsp, rem_lifetime);
+  THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
+  circuit->lsp_regenerate_pending[level - 1] = 0;
+  if (level == IS_LEVEL_1)
+    THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1],
+                     lsp_l1_refresh_pseudo, circuit, refresh_time);
+  else if (level == IS_LEVEL_2)
+    THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1],
+                     lsp_l2_refresh_pseudo, circuit, refresh_time);
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Building L%d Pseudo LSP %s, len %d, "
+                  "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us",
+                  circuit->area->area_tag, level,
+                  rawlspid_print (lsp->lsp_header->lsp_id),
+                  ntohl (lsp->lsp_header->pdu_len),
+                  ntohl (lsp->lsp_header->seq_num),
+                  ntohs (lsp->lsp_header->checksum),
+                  ntohs (lsp->lsp_header->rem_lifetime),
+                  refresh_time);
+    }
+
+  return ISIS_OK;
+}
+
+static int
+lsp_regenerate_pseudo (struct isis_circuit *circuit, int level)
+{
+  dict_t *lspdb = circuit->area->lspdb[level - 1];
+  struct isis_lsp *lsp;
+  u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  u_int16_t rem_lifetime, refresh_time;
+
+  if ((circuit->is_type & level) != level ||
+      (circuit->state != C_STATE_UP) ||
+      (circuit->circ_type != CIRCUIT_T_BROADCAST) ||
+      (circuit->u.bc.is_dr[level - 1] == 0))
+    return ISIS_ERROR;
+
+  memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id;
+  LSP_FRAGMENT (lsp_id) = 0;
+
+  lsp = lsp_search (lsp_id, lspdb);
+
+  if (!lsp)
+    {
+      zlog_err ("lsp_regenerate_pseudo: no l%d LSP %s found!",
+                level, rawlspid_print (lsp_id));
+      return ISIS_ERROR;
+    }
+  lsp_clear_data (lsp);
+
+  lsp_build_pseudo (lsp, circuit, level);
+
+  /* RFC3787  section 4 SHOULD not set overload bit in pseudo LSPs */
+  lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0,
+                                                 circuit->area->attached_bit);
+  rem_lifetime = lsp_rem_lifetime (circuit->area, level);
+  lsp->lsp_header->rem_lifetime = htons (rem_lifetime);
+  lsp_inc_seqnum (lsp, 0);
+  lsp->last_generated = time (NULL);
+  lsp_set_all_srmflags (lsp);
+
+  refresh_time = lsp_refresh_time (lsp, rem_lifetime);
+  if (level == IS_LEVEL_1)
+    THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1],
+                     lsp_l1_refresh_pseudo, circuit, refresh_time);
+  else if (level == IS_LEVEL_2)
+    THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1],
+                     lsp_l2_refresh_pseudo, circuit, refresh_time);
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Refreshing L%d Pseudo LSP %s, len %d, "
+                  "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us",
+                  circuit->area->area_tag, level,
+                  rawlspid_print (lsp->lsp_header->lsp_id),
+                  ntohl (lsp->lsp_header->pdu_len),
+                  ntohl (lsp->lsp_header->seq_num),
+                  ntohs (lsp->lsp_header->checksum),
+                  ntohs (lsp->lsp_header->rem_lifetime),
+                  refresh_time);
+    }
+
+  return ISIS_OK;
+}
+
+/*
+ * Something has changed or periodic refresh -> regenerate pseudo LSP
+ */
+static int
+lsp_l1_refresh_pseudo (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  u_char id[ISIS_SYS_ID_LEN + 2];
+
+  circuit = THREAD_ARG (thread);
+
+  circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL;
+  circuit->lsp_regenerate_pending[0] = 0;
+
+  if ((circuit->u.bc.is_dr[0] == 0) ||
+      (circuit->is_type & IS_LEVEL_1) == 0)
+    {
+      memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
+      LSP_PSEUDO_ID (id) = circuit->circuit_id;
+      LSP_FRAGMENT (id) = 0;
+      lsp_purge_pseudo (id, circuit, IS_LEVEL_1);
+      return ISIS_ERROR;
+    }
+
+  return lsp_regenerate_pseudo (circuit, IS_LEVEL_1);
+}
+
+static int
+lsp_l2_refresh_pseudo (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  u_char id[ISIS_SYS_ID_LEN + 2];
+
+  circuit = THREAD_ARG (thread);
+
+  circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL;
+  circuit->lsp_regenerate_pending[1] = 0;
+
+  if ((circuit->u.bc.is_dr[1] == 0) ||
+      (circuit->is_type & IS_LEVEL_2) == 0)
+    {
+      memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
+      LSP_PSEUDO_ID (id) = circuit->circuit_id;
+      LSP_FRAGMENT (id) = 0;
+      lsp_purge_pseudo (id, circuit, IS_LEVEL_2);
+      return ISIS_ERROR;
+    }
+
+  return lsp_regenerate_pseudo (circuit, IS_LEVEL_2);
+}
+
+int
+lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level)
+{
+  struct isis_lsp *lsp;
+  u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  time_t now, diff;
+  long timeout;
+  int lvl;
+  struct isis_area *area = circuit->area;
+
+  if (circuit == NULL ||
+      circuit->circ_type != CIRCUIT_T_BROADCAST ||
+      circuit->state != C_STATE_UP)
+    return ISIS_OK;
+
+  sched_debug("ISIS (%s): Scheduling regeneration of %s pseudo LSP for interface %s",
+              area->area_tag, circuit_t2string(level), circuit->interface->name);
+
+  memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id;
+  LSP_FRAGMENT (lsp_id) = 0;
+  now = time (NULL);
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++)
+    {
+      sched_debug("ISIS (%s): Checking whether L%d pseudo LSP needs to be scheduled",
+                  area->area_tag, lvl);
+
+      if (!((level & lvl) && (circuit->is_type & lvl)))
+        {
+          sched_debug("ISIS (%s): Level is not active on circuit",
+                      area->area_tag);
+          continue;
+        }
+
+      if (circuit->u.bc.is_dr[lvl - 1] == 0)
+        {
+          sched_debug("ISIS (%s): This IS is not DR, nothing to do.",
+                      area->area_tag);
+          continue;
+        }
+
+      if (circuit->lsp_regenerate_pending[lvl - 1])
+        {
+          struct timeval remain =
+                  thread_timer_remain(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
+          sched_debug("ISIS (%s): Regenerate is already pending, nothing todo."
+                      " (Due in %lld.%03lld seconds)", area->area_tag,
+                      (long long)remain.tv_sec, (long long)remain.tv_usec/1000);
+          continue;
+        }
+
+      lsp = lsp_search (lsp_id, circuit->area->lspdb[lvl - 1]);
+      if (!lsp)
+        {
+          sched_debug("ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.",
+                      area->area_tag);
+          continue;
+        }
+
+      /*
+       * Throttle avoidance
+       */
+      sched_debug("ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld",
+                  area->area_tag, (long long)lsp->last_generated, (long long) now);
+      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
+      diff = now - lsp->last_generated;
+      if (diff < circuit->area->lsp_gen_interval[lvl - 1])
+        {
+          timeout = 1000 * (circuit->area->lsp_gen_interval[lvl - 1] - diff);
+          sched_debug("ISIS (%s): Sechduling in %ld ms to match configured lsp_gen_interval",
+                      area->area_tag, timeout);
+        }
+      else
+        {
+          timeout = 100;
+          sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago."
+                      " Scheduling for execution in %ld ms.", area->area_tag, timeout);
+        }
+
+      circuit->lsp_regenerate_pending[lvl - 1] = 1;
+
+      if (lvl == IS_LEVEL_1)
+        {
+          THREAD_TIMER_MSEC_ON(master,
+                               circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1],
+                               lsp_l1_refresh_pseudo, circuit, timeout);
+        }
+      else if (lvl == IS_LEVEL_2)
+        {
+          THREAD_TIMER_MSEC_ON(master,
+                               circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1],
+                               lsp_l2_refresh_pseudo, circuit, timeout);
+        }
+    }
+
+  return ISIS_OK;
+}
+
+/*
+ * Walk through LSPs for an area
+ *  - set remaining lifetime
+ *  - set LSPs with SRMflag set for sending
+ */
+int
+lsp_tick (struct thread *thread)
+{
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+  struct isis_lsp *lsp;
+  struct list *lsp_list;
+  struct listnode *lspnode, *cnode;
+  dnode_t *dnode, *dnode_next;
+  int level;
+  u_int16_t rem_lifetime;
+
+  lsp_list = list_new ();
+
+  area = THREAD_ARG (thread);
+  assert (area);
+  area->t_tick = NULL;
+  THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
+
+  /*
+   * Build a list of LSPs with (any) SRMflag set
+   * and removed the ones that have aged out
+   */
+  for (level = 0; level < ISIS_LEVELS; level++)
+    {
+      if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0)
+        {
+          for (dnode = dict_first (area->lspdb[level]);
+               dnode != NULL; dnode = dnode_next)
+            {
+              dnode_next = dict_next (area->lspdb[level], dnode);
+              lsp = dnode_get (dnode);
+
+              /*
+               * The lsp rem_lifetime is kept at 0 for MaxAge or
+               * ZeroAgeLifetime depending on explicit purge or
+               * natural age out. So schedule spf only once when
+               * the first time rem_lifetime becomes 0.
+               */
+              rem_lifetime = ntohs(lsp->lsp_header->rem_lifetime);
+              lsp_set_time (lsp);
+
+              /*
+               * Schedule may run spf which should be done only after
+               * the lsp rem_lifetime becomes 0 for the first time.
+               * ISO 10589 - 7.3.16.4 first paragraph.
+               */
+              if (rem_lifetime == 1 && lsp->lsp_header->seq_num != 0)
+                {
+                  /* 7.3.16.4 a) set SRM flags on all */
+                  lsp_set_all_srmflags (lsp);
+                  /* 7.3.16.4 b) retain only the header FIXME  */
+                  /* 7.3.16.4 c) record the time to purge FIXME */
+                  /* run/schedule spf */
+                  /* isis_spf_schedule is called inside lsp_destroy() below;
+                   * so it is not needed here. */
+                  /* isis_spf_schedule (lsp->area, lsp->level); */
+                }
+
+              if (lsp->age_out == 0)
+                {
+                  zlog_debug ("ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out",
+                              area->area_tag,
+                              lsp->level,
+                              rawlspid_print (lsp->lsp_header->lsp_id),
+                              ntohl (lsp->lsp_header->seq_num));
+#ifdef TOPOLOGY_GENERATE
+                  if (lsp->from_topology)
+                    THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
+#endif /* TOPOLOGY_GENERATE */
+                  lsp_destroy (lsp);
+                  lsp = NULL;
+                  dict_delete_free (area->lspdb[level], dnode);
+                }
+              else if (flags_any_set (lsp->SRMflags))
+                listnode_add (lsp_list, lsp);
+            }
+
+          /*
+           * Send LSPs on circuits indicated by the SRMflags
+           */
+          if (listcount (lsp_list) > 0)
+            {
+              for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+                {
+                  int diff = time (NULL) - circuit->lsp_queue_last_cleared;
+                  if (circuit->lsp_queue == NULL ||
+                      diff < MIN_LSP_TRANS_INTERVAL)
+                    continue;
+                  for (ALL_LIST_ELEMENTS_RO (lsp_list, lspnode, lsp))
+                    {
+                      if (circuit->upadjcount[lsp->level - 1] &&
+                          ISIS_CHECK_FLAG (lsp->SRMflags, circuit))
+                        {
+                          /* Add the lsp only if it is not already in lsp
+                           * queue */
+                          if (! listnode_lookup (circuit->lsp_queue, lsp))
+                            {
+                              listnode_add (circuit->lsp_queue, lsp);
+                              thread_add_event (master, send_lsp, circuit, 0);
+                            }
+                        }
+                    }
+                }
+              list_delete_all_node (lsp_list);
+            }
+        }
+    }
+
+  list_delete (lsp_list);
+
+  return ISIS_OK;
+}
+
+void
+lsp_purge_pseudo (u_char * id, struct isis_circuit *circuit, int level)
+{
+  struct isis_lsp *lsp;
+  u_int16_t seq_num;
+  u_int8_t lsp_bits;
+
+  lsp = lsp_search (id, circuit->area->lspdb[level - 1]);
+  if (!lsp)
+    return;
+
+  /* store old values */
+  seq_num = lsp->lsp_header->seq_num;
+  lsp_bits = lsp->lsp_header->lsp_bits;
+
+  /* reset stream */
+  lsp_clear_data (lsp);
+  stream_reset (lsp->pdu);
+
+  /* update header */
+  lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+  memcpy (lsp->lsp_header->lsp_id, id, ISIS_SYS_ID_LEN + 2);
+  lsp->lsp_header->checksum = 0;
+  lsp->lsp_header->seq_num = seq_num;
+  lsp->lsp_header->rem_lifetime = 0;
+  lsp->lsp_header->lsp_bits = lsp_bits;
+  lsp->level = level;
+  lsp->age_out = lsp->area->max_lsp_lifetime[level-1];
+  stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+
+  /*
+   * Add and update the authentication info if its present
+   */
+  lsp_auth_add (lsp);
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+  lsp_auth_update (lsp);
+  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+                    ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+
+  lsp_set_all_srmflags (lsp);
+
+  return;
+}
+
+/*
+ * Purge own LSP that is received and we don't have. 
+ * -> Do as in 7.3.16.4
+ */
+void
+lsp_purge_non_exist (int level,
+                    struct isis_link_state_hdr *lsp_hdr,
+                    struct isis_area *area)
+{
+  struct isis_lsp *lsp;
+
+  /*
+   * We need to create the LSP to be purged 
+   */
+  lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp));
+  lsp->area = area;
+  lsp->level = level;
+  lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu);
+  lsp->isis_header = (struct isis_fixed_hdr *) STREAM_DATA (lsp->pdu);
+  fill_fixed_hdr (lsp->isis_header, (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE
+                 : L2_LINK_STATE);
+  lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) +
+                                                   ISIS_FIXED_HDR_LEN);
+  memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN);
+  stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+
+  /*
+   * Set the remaining lifetime to 0
+   */
+  lsp->lsp_header->rem_lifetime = 0;
+
+  /*
+   * Add and update the authentication info if its present
+   */
+  lsp_auth_add (lsp);
+  lsp_auth_update (lsp);
+
+  /*
+   * Update the PDU length to header plus any authentication TLV.
+   */
+  lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+
+  /*
+   * Put the lsp into LSPdb
+   */
+  lsp_insert (lsp, area->lspdb[lsp->level - 1]);
+
+  /*
+   * Send in to whole area
+   */
+  lsp_set_all_srmflags (lsp);
+
+  return;
+}
+
+void lsp_set_all_srmflags (struct isis_lsp *lsp)
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  assert (lsp);
+
+  ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags);
+
+  if (lsp->area)
+    {
+      struct list *circuit_list = lsp->area->circuit_list;
+      for (ALL_LIST_ELEMENTS_RO (circuit_list, node, circuit))
+        {
+          ISIS_SET_FLAG(lsp->SRMflags, circuit);
+        }
+    }
+}
+
+#ifdef TOPOLOGY_GENERATE
+static int
+top_lsp_refresh (struct thread *thread)
+{
+  struct isis_lsp *lsp;
+  u_int16_t rem_lifetime;
+
+  lsp = THREAD_ARG (thread);
+  assert (lsp);
+
+  lsp->t_lsp_top_ref = NULL;
+
+  lsp_seqnum_update (lsp);
+
+  lsp_set_all_srmflags (lsp);
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (): refreshing Topology L1 %s",
+                 rawlspid_print (lsp->lsp_header->lsp_id));
+    }
+  /* Refresh dynamic hostname in the cache. */
+  isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname,
+                    IS_LEVEL_1);
+
+  lsp->lsp_header->lsp_bits = lsp_bits_generate (lsp->level,
+                                                 lsp->area->overload_bit,
+                                                 lsp->area->attached_bit);
+  rem_lifetime = lsp_rem_lifetime (lsp->area, IS_LEVEL_1);
+  lsp->lsp_header->rem_lifetime = htons (rem_lifetime);
+
+  /* refresh_time = lsp_refresh_time (lsp, rem_lifetime); */
+  THREAD_TIMER_ON (master, lsp->t_lsp_top_ref, top_lsp_refresh, lsp,
+                  lsp->area->lsp_refresh[0]);
+
+  return ISIS_OK;
+}
+
+void
+generate_topology_lsps (struct isis_area *area)
+{
+  struct listnode *node;
+  int i, max = 0;
+  struct arc *arc;
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+  struct isis_lsp *lsp;
+  u_int16_t rem_lifetime, refresh_time;
+
+  /* first we find the maximal node */
+  for (ALL_LIST_ELEMENTS_RO (area->topology, node, arc))
+    {
+      if (arc->from_node > max)
+        max = arc->from_node;
+      if (arc->to_node > max)
+        max = arc->to_node;
+    }
+
+  for (i = 1; i < (max + 1); i++)
+    {
+      memcpy (lspid, area->topology_baseis, ISIS_SYS_ID_LEN);
+      LSP_PSEUDO_ID (lspid) = 0x00;
+      LSP_FRAGMENT (lspid) = 0x00;
+      lspid[ISIS_SYS_ID_LEN - 1] = (i & 0xFF);
+      lspid[ISIS_SYS_ID_LEN - 2] = ((i >> 8) & 0xFF);
+
+      rem_lifetime = lsp_rem_lifetime (area, IS_LEVEL_1);
+      lsp = lsp_new (area, lspid, rem_lifetime, 1,
+                     IS_LEVEL_1 | area->overload_bit | area->attached_bit,
+                     0, 1);
+      if (!lsp)
+       return;
+      lsp->from_topology = 1;
+
+      /* Creating LSP data based on topology info. */
+      build_topology_lsp_data (lsp, area, i);
+      /* Checksum is also calculated here. */
+      lsp_seqnum_update (lsp);
+      /* Take care of inserting dynamic hostname into cache. */
+      isis_dynhn_insert (lspid, lsp->tlv_data.hostname, IS_LEVEL_1);
+
+      refresh_time = lsp_refresh_time (lsp, rem_lifetime);
+      THREAD_TIMER_ON (master, lsp->t_lsp_top_ref, top_lsp_refresh, lsp,
+                      refresh_time);
+      lsp_set_all_srmflags (lsp);
+      lsp_insert (lsp, area->lspdb[0]);
+    }
+}
+
+void
+remove_topology_lsps (struct isis_area *area)
+{
+  struct isis_lsp *lsp;
+  dnode_t *dnode, *dnode_next;
+
+  dnode = dict_first (area->lspdb[0]);
+  while (dnode != NULL)
+    {
+      dnode_next = dict_next (area->lspdb[0], dnode);
+      lsp = dnode_get (dnode);
+      if (lsp->from_topology)
+       {
+         THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
+         lsp_destroy (lsp);
+         dict_delete (area->lspdb[0], dnode);
+       }
+      dnode = dnode_next;
+    }
+}
+
+void
+build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area,
+                        int lsp_top_num)
+{
+  struct listnode *node;
+  struct arc *arc;
+  struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
+  char buff[200];
+  struct tlvs tlv_data;
+  struct isis_lsp *lsp0 = lsp;
+
+  /* Add area addresses. FIXME: Is it needed at all? */
+  if (lsp->tlv_data.area_addrs == NULL)
+    lsp->tlv_data.area_addrs = list_new ();
+  list_add_list (lsp->tlv_data.area_addrs, area->area_addrs);
+
+  if (lsp->tlv_data.nlpids == NULL)
+    lsp->tlv_data.nlpids = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct nlpids));
+  lsp->tlv_data.nlpids->count = 1;
+  lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP;
+
+  if (area->dynhostname)
+    {
+      lsp->tlv_data.hostname = XMALLOC (MTYPE_ISIS_TLV,
+                                       sizeof (struct hostname));
+      memset (buff, 0x00, 200);
+      sprintf (buff, "%s%d", area->topology_basedynh ? area->topology_basedynh :
+              "feedme", lsp_top_num);
+      memcpy (lsp->tlv_data.hostname->name, buff, strlen (buff));
+      lsp->tlv_data.hostname->namelen = strlen (buff);
+    }
+
+  if (lsp->tlv_data.nlpids)
+    tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu);
+  if (lsp->tlv_data.hostname)
+    tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu);
+  if (lsp->tlv_data.area_addrs && listcount (lsp->tlv_data.area_addrs) > 0)
+    tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu);
+
+  memset (&tlv_data, 0, sizeof (struct tlvs));
+  if (tlv_data.is_neighs == NULL)
+    {
+      tlv_data.is_neighs = list_new ();
+      tlv_data.is_neighs->del = free_tlv;
+    }
+
+  /* Add reachability for this IS for simulated 1. */
+  if (lsp_top_num == 1)
+    {
+      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+
+      memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
+      LSP_PSEUDO_ID (is_neigh->neigh_id) = 0x00;
+      /* Metric MUST NOT be 0, unless it's not alias TLV. */
+      is_neigh->metrics.metric_default = 0x01;
+      is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED;
+      is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED;
+      is_neigh->metrics.metric_error = METRICS_UNSUPPORTED;
+      listnode_add (tlv_data.is_neighs, is_neigh);
+    }
+
+  /* Add IS reachabilities. */
+  for (ALL_LIST_ELEMENTS_RO (area->topology, node, arc))
+    {
+      int to_lsp = 0;
+      
+      if ((lsp_top_num != arc->from_node) && (lsp_top_num != arc->to_node))
+       continue;
+
+      if (lsp_top_num == arc->from_node)
+       to_lsp = arc->to_node;
+      else
+       to_lsp = arc->from_node;
+
+      if (area->oldmetric)
+       {
+         is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+
+         memcpy (&is_neigh->neigh_id, area->topology_baseis, ISIS_SYS_ID_LEN);
+         is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (to_lsp & 0xFF);
+         is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((to_lsp >> 8) & 0xFF);
+         is_neigh->metrics.metric_default = arc->distance;
+         is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED;
+         is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED;
+         is_neigh->metrics.metric_error = METRICS_UNSUPPORTED;
+         listnode_add (tlv_data.is_neighs, is_neigh);
+       }
+
+      if (area->newmetric)
+       {
+         if (tlv_data.te_is_neighs == NULL)
+           {
+             tlv_data.te_is_neighs = list_new ();
+             tlv_data.te_is_neighs->del = free_tlv;
+           }
+         te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh));
+         memcpy (&te_is_neigh->neigh_id, area->topology_baseis,
+                 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);
+         SET_TE_METRIC(te_is_neigh, arc->distance);
+         listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+       }
+    }
+
+  while (tlv_data.is_neighs && listcount (tlv_data.is_neighs))
+    {
+      if (lsp->tlv_data.is_neighs == NULL)
+       lsp->tlv_data.is_neighs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.is_neighs, &lsp->tlv_data.is_neighs,
+                  IS_NEIGHBOURS_LEN, area->lsp_frag_threshold,
+                  tlv_add_is_neighs);
+      if (tlv_data.is_neighs && listcount (tlv_data.is_neighs))
+        lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, IS_LEVEL_1);
+    }
+
+  while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
+    {
+      if (lsp->tlv_data.te_is_neighs == NULL)
+       lsp->tlv_data.te_is_neighs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs,
+                  IS_NEIGHBOURS_LEN, area->lsp_frag_threshold,
+                  tlv_add_te_is_neighs);
+      if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
+       lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                            lsp0, area, IS_LEVEL_1);
+    }
+
+  free_tlvs (&tlv_data);
+  return;
+}
+#endif /* TOPOLOGY_GENERATE */
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
new file mode 100644 (file)
index 0000000..a35bfa7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_lsp.h   
+ *                             LSP processing
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_LSP_H
+#define _ZEBRA_ISIS_LSP_H
+
+/* Structure for isis_lsp, this structure will only support the fixed
+ * System ID (Currently 6) (atleast for now). In order to support more
+ * We will have to split the header into two parts, and for readability
+ * sake it should better be avoided */
+struct isis_lsp
+{
+  struct isis_fixed_hdr *isis_header;          /* normally equals pdu */
+  struct isis_link_state_hdr *lsp_header;      /* pdu + isis_header_len */
+  struct stream *pdu;                          /* full pdu lsp */
+  union
+  {
+    struct list *frags;
+    struct isis_lsp *zero_lsp;
+  } lspu;
+  u_int32_t auth_tlv_offset;    /* authentication TLV position in the pdu */
+  u_int32_t SRMflags[ISIS_MAX_CIRCUITS];
+  u_int32_t SSNflags[ISIS_MAX_CIRCUITS];
+  int level;                   /* L1 or L2? */
+  int scheduled;               /* scheduled for sending */
+  time_t installed;
+  time_t last_generated;
+  int own_lsp;
+#ifdef TOPOLOGY_GENERATE
+  int from_topology;
+  struct thread *t_lsp_top_ref;
+#endif
+  /* used for 60 second counting when rem_lifetime is zero */
+  int age_out;
+  struct isis_area *area;
+  struct tlvs tlv_data;                /* Simplifies TLV access */
+};
+
+dict_t *lsp_db_init (void);
+void lsp_db_destroy (dict_t * lspdb);
+int lsp_tick (struct thread *thread);
+
+int lsp_generate (struct isis_area *area, int level);
+int lsp_regenerate_schedule (struct isis_area *area, int level,
+                             int all_pseudo);
+int lsp_generate_pseudo (struct isis_circuit *circuit, int level);
+int lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level);
+
+struct isis_lsp *lsp_new (struct isis_area *area, u_char * lsp_id,
+                         u_int16_t rem_lifetime,
+                         u_int32_t seq_num, u_int8_t lsp_bits,
+                         u_int16_t checksum, int level);
+struct isis_lsp *lsp_new_from_stream_ptr (struct stream *stream,
+                                         u_int16_t pdu_len,
+                                         struct isis_lsp *lsp0,
+                                         struct isis_area *area,
+                                          int level);
+void lsp_insert (struct isis_lsp *lsp, dict_t * lspdb);
+struct isis_lsp *lsp_search (u_char * id, dict_t * lspdb);
+
+void lsp_build_list (u_char * start_id, u_char * stop_id, u_char num_lsps,
+                    struct list *list, dict_t * lspdb);
+void lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id,
+                               struct list *list, dict_t * lspdb);
+void lsp_build_list_ssn (struct isis_circuit *circuit, u_char num_lsps,
+                         struct list *list, dict_t * lspdb);
+
+void lsp_search_and_destroy (u_char * id, dict_t * lspdb);
+void lsp_purge_pseudo (u_char * id, struct isis_circuit *circuit, int level);
+void lsp_purge_non_exist (int level,
+                         struct isis_link_state_hdr *lsp_hdr,
+                         struct isis_area *area);
+
+#define LSP_EQUAL 1
+#define LSP_NEWER 2
+#define LSP_OLDER 3
+
+#define LSP_PSEUDO_ID(I) ((I)[ISIS_SYS_ID_LEN])
+#define LSP_FRAGMENT(I) ((I)[ISIS_SYS_ID_LEN + 1])
+#define OWNLSPID(I) \
+        memcpy ((I), isis->sysid, ISIS_SYS_ID_LEN);\
+        (I)[ISIS_SYS_ID_LEN] = 0;\
+        (I)[ISIS_SYS_ID_LEN + 1] = 0
+int lsp_id_cmp (u_char * id1, u_char * id2);
+int lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num,
+                u_int16_t checksum, u_int16_t rem_lifetime);
+void lsp_update (struct isis_lsp *lsp, struct stream *stream,
+                 struct isis_area *area, int level);
+void lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num);
+void lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost);
+void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost);
+int lsp_print_all (struct vty *vty, dict_t * lspdb, char detail,
+                  char dynhost);
+const char *lsp_bits2string (u_char *);
+
+/* sets SRMflags for all active circuits of an lsp */
+void lsp_set_all_srmflags (struct isis_lsp *lsp);
+
+#ifdef TOPOLOGY_GENERATE
+void generate_topology_lsps (struct isis_area *area);
+void remove_topology_lsps (struct isis_area *area);
+void build_topology_lsp_data (struct isis_lsp *lsp,
+                             struct isis_area *area, int lsp_top_num);
+#endif /* TOPOLOGY_GENERATE */
+
+#endif /* ISIS_LSP */
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
new file mode 100644 (file)
index 0000000..bbb1fb2
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_main.c
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "getopt.h"
+#include "thread.h"
+#include "log.h"
+#include <lib/version.h>
+#include "command.h"
+#include "vty.h"
+#include "memory.h"
+#include "stream.h"
+#include "if.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "filter.h"
+#include "plist.h"
+#include "zclient.h"
+#include "vrf.h"
+
+#include "isisd/dict.h"
+#include "include-netbsd/iso.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_dynhn.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_routemap.h"
+#include "isisd/isis_zebra.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_te.h"
+
+/* Default configuration file name */
+#define ISISD_DEFAULT_CONFIG "isisd.conf"
+/* Default vty port */
+#define ISISD_VTY_PORT       2608
+
+/* isisd privileges */
+zebra_capabilities_t _caps_p[] = {
+  ZCAP_NET_RAW,
+  ZCAP_BIND
+};
+
+struct zebra_privs_t isisd_privs = {
+#if defined(QUAGGA_USER)
+  .user = QUAGGA_USER,
+#endif
+#if defined QUAGGA_GROUP
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = sizeof (_caps_p) / sizeof (*_caps_p),
+  .cap_num_i = 0
+};
+
+/* isisd options */
+struct option longopts[] = {
+  {"daemon",      no_argument,       NULL, 'd'},
+  {"config_file", required_argument, NULL, 'f'},
+  {"pid_file",    required_argument, NULL, 'i'},
+  {"socket",      required_argument, NULL, 'z'},
+  {"vty_addr",    required_argument, NULL, 'A'},
+  {"vty_port",    required_argument, NULL, 'P'},
+  {"user",        required_argument, NULL, 'u'},
+  {"group",       required_argument, NULL, 'g'},
+  {"version",     no_argument,       NULL, 'v'},
+  {"dryrun",      no_argument,       NULL, 'C'},
+  {"help",        no_argument,       NULL, 'h'},
+  {0}
+};
+
+/* Configuration file and directory. */
+char config_default[] = SYSCONFDIR ISISD_DEFAULT_CONFIG;
+char *config_file = NULL;
+
+/* isisd program name. */
+char *progname;
+
+int daemon_mode = 0;
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_ISISD_PID;
+
+/* for reload */
+char _cwd[MAXPATHLEN];
+char _progpath[MAXPATHLEN];
+int _argc;
+char **_argv;
+char **_envp;
+
+/*
+ * Prototypes.
+ */
+void reload(void);
+void sighup(void);
+void sigint(void);
+void sigterm(void);
+void sigusr1(void);
+
+
+/* Help information display. */
+static void
+usage (int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {
+      printf ("Usage : %s [OPTION...]\n\n\
+Daemon which manages IS-IS routing\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-v, --version      Print program version\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+
+void
+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
+terminate (int i)
+{
+  exit (i);
+}
+
+/*
+ * Signal handlers
+ */
+
+void
+sighup (void)
+{
+  zlog_debug ("SIGHUP received");
+  reload ();
+
+  return;
+}
+
+void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal SIGINT");
+  terminate (0);
+}
+
+void
+sigterm (void)
+{
+  zlog_notice ("Terminating on signal SIGTERM");
+  terminate (0);
+}
+
+void
+sigusr1 (void)
+{
+  zlog_debug ("SIGUSR1 received");
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t isisd_signals[] =
+{
+  {
+   .signal = SIGHUP,
+   .handler = &sighup,
+   },
+  {
+   .signal = SIGUSR1,
+   .handler = &sigusr1,
+   },
+  {
+   .signal = SIGINT,
+   .handler = &sigint,
+   },
+  {
+   .signal = SIGTERM,
+   .handler = &sigterm,
+   },
+};
+
+/*
+ * Main routine of isisd. Parse arguments and handle IS-IS state machine.
+ */
+int
+main (int argc, char **argv, char **envp)
+{
+  char *p;
+  int opt, vty_port = ISISD_VTY_PORT;
+  struct thread thread;
+  char *config_file = NULL;
+  char *vty_addr = NULL;
+  int dryrun = 0;
+
+  /* Get the programname without the preceding path. */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  zlog_default = openzlog (progname, ZLOG_ISIS,
+                          LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
+
+  /* for reload */
+  _argc = argc;
+  _argv = argv;
+  _envp = envp;
+  getcwd (_cwd, sizeof (_cwd));
+  if (*argv[0] == '.')
+    snprintf (_progpath, sizeof (_progpath), "%s/%s", _cwd, _argv[0]);
+  else
+    snprintf (_progpath, sizeof (_progpath), "%s", argv[0]);
+
+  /* Command line argument treatment. */
+  while (1)
+    {
+      opt = getopt_long (argc, argv, "df:i:z:hA:p:P:u:g:vC", longopts, 0);
+
+      if (opt == EOF)
+       break;
+
+      switch (opt)
+       {
+       case 0:
+         break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'i':
+         pid_file = optarg;
+         break;
+       case 'z':
+         zclient_serv_path_set (optarg);
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+       case 'P':
+         /* Deal with atoi() returning 0 on failure, and isisd not
+            listening on isisd port... */
+         if (strcmp (optarg, "0") == 0)
+           {
+             vty_port = 0;
+             break;
+           }
+         vty_port = atoi (optarg);
+         vty_port = (vty_port ? vty_port : ISISD_VTY_PORT);
+         break;
+       case 'u':
+         isisd_privs.user = optarg;
+         break;
+       case 'g':
+         isisd_privs.group = optarg;
+         break;
+       case 'v':
+         printf ("ISISd version %s\n", ISISD_VERSION);
+         printf ("Copyright (c) 2001-2002 Sampo Saaristo,"
+                 " Ofer Wald and Hannes Gredler\n");
+         print_version ("Zebra");
+         exit (0);
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'h':
+         usage (0);
+         break;
+       default:
+         usage (1);
+         break;
+       }
+    }
+
+  /* thread master */
+  master = thread_master_create ();
+
+  /* random seed from time */
+  srandom (time (NULL));
+
+  /*
+   *  initializations
+   */
+  zprivs_init (&isisd_privs);
+  signal_init (master, array_size (isisd_signals), isisd_signals);
+  cmd_init (1);
+  vty_init (master);
+  memory_init ();
+  access_list_init();
+  vrf_init ();
+  prefix_list_init();
+  isis_init ();
+  isis_circuit_init ();
+  isis_spf_cmds_init ();
+  isis_redist_init ();
+  isis_route_map_init();
+  isis_mpls_te_init();
+
+  /* create the global 'isis' instance */
+  isis_new (1);
+
+  isis_zebra_init (master);
+
+  /* parse config file */
+  /* this is needed three times! because we have interfaces before the areas */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if (dryrun)
+    return(0);
+  
+  /* demonize */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("IS-IS daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Process ID file creation. */
+  if (pid_file[0] != '\0')
+    pid_output (pid_file);
+
+  /* Make isis vty socket. */
+  vty_serv_sock (vty_addr, vty_port, ISIS_VTYSH_PATH);
+
+  /* Print banner. */
+  zlog_notice ("Quagga-ISISd %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  /* Start finite state machine. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  exit (0);
+}
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c
new file mode 100644 (file)
index 0000000..f19b441
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_misc.h
+ *                             Miscellanous routines
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "stream.h"
+#include "vty.h"
+#include "hash.h"
+#include "if.h"
+#include "command.h"
+
+#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/isis_csm.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_misc.h"
+
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.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;
+/* len of xxxx.xxxx.xxxx + place for #0 termination */
+char sysid[15];
+/* len of xxxx.xxxx.xxxx + place for #0 termination */
+char snpa[15];
+/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */
+char isonet[51];
+/* + place for #0 termination */
+/* len of xxxx.xxxx.xxxx.xx.xx + place for #0 termination */
+char lspid[21];
+/* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */
+char datestring[20];
+char nlpidstring[30];
+
+/*
+ * This converts the isonet to its printable format
+ */
+const char *
+isonet_print (const u_char * from, int len)
+{
+  int i = 0;
+  char *pos = isonet;
+
+  if (!from)
+    return "unknown";
+
+  while (i < len)
+    {
+      if (i & 1)
+       {
+         sprintf (pos, "%02x", *(from + i));
+         pos += 2;
+       }
+      else
+       {
+         if (i == (len - 1))
+           {                   /* No dot at the end of address */
+             sprintf (pos, "%02x", *(from + i));
+             pos += 2;
+           }
+         else
+           {
+             sprintf (pos, "%02x.", *(from + i));
+             pos += 3;
+           }
+       }
+      i++;
+    }
+  *(pos) = '\0';
+  return isonet;
+}
+
+/*
+ * Returns 0 on error, length of buff on ok
+ * extract dot from the dotted str, and insert all the number in a buff 
+ */
+int
+dotformat2buff (u_char * buff, const char * dotted)
+{
+  int dotlen, len = 0;
+  const char *pos = dotted;
+  u_char number[3];
+  int nextdotpos = 2;
+
+  number[2] = '\0';
+  dotlen = strlen(dotted);
+  if (dotlen > 50)
+    {
+      /* this can't be an iso net, its too long */
+      return 0;
+    }
+
+  while ((pos - dotted) < dotlen && len < 20)
+    {
+      if (*pos == '.')
+       {
+         /* we expect the . at 2, and than every 5 */
+         if ((pos - dotted) != nextdotpos)
+           {
+             len = 0;
+             break;
+           }
+         nextdotpos += 5;
+         pos++;
+         continue;
+       }
+      /* we must have at least two chars left here */
+      if (dotlen - (pos - dotted) < 2)
+       {
+         len = 0;
+         break;
+       }
+
+      if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1))))
+       {
+         memcpy (number, pos, 2);
+         pos += 2;
+       }
+      else
+       {
+         len = 0;
+         break;
+       }
+
+      *(buff + len) = (char) strtol ((char *)number, NULL, 16);
+      len++;
+    }
+
+  return len;
+}
+
+/*
+ * conversion of XXXX.XXXX.XXXX to memory
+ */
+int
+sysid2buff (u_char * buff, const char * dotted)
+{
+  int len = 0;
+  const char *pos = dotted;
+  u_char number[3];
+
+  number[2] = '\0';
+  // surely not a sysid_string if not 14 length
+  if (strlen (dotted) != 14)
+    {
+      return 0;
+    }
+
+  while (len < ISIS_SYS_ID_LEN)
+    {
+      if (*pos == '.')
+       {
+         /* the . is not positioned correctly */
+         if (((pos - dotted) != 4) && ((pos - dotted) != 9))
+           {
+             len = 0;
+             break;
+           }
+         pos++;
+         continue;
+       }
+      if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1))))
+       {
+         memcpy (number, pos, 2);
+         pos += 2;
+       }
+      else
+       {
+         len = 0;
+         break;
+       }
+
+      *(buff + len) = (char) strtol ((char *)number, NULL, 16);
+      len++;
+    }
+
+  return len;
+
+}
+
+/*
+ * converts the nlpids struct (filled by TLV #129)
+ * into a string
+ */
+
+char *
+nlpid2string (struct nlpids *nlpids)
+{
+  char *pos = nlpidstring;
+  int i;
+
+  for (i = 0; i < nlpids->count; i++)
+    {
+      switch (nlpids->nlpids[i])
+       {
+       case NLPID_IP:
+         pos += sprintf (pos, "IPv4");
+         break;
+       case NLPID_IPV6:
+         pos += sprintf (pos, "IPv6");
+         break;
+       case NLPID_SNAP:
+         pos += sprintf (pos, "SNAP");
+         break;
+       case NLPID_CLNP:
+         pos += sprintf (pos, "CLNP");
+         break;
+       case NLPID_ESIS:
+         pos += sprintf (pos, "ES-IS");
+         break;
+       default:
+         pos += sprintf (pos, "unknown");
+         break;
+       }
+      if (nlpids->count - i > 1)
+       pos += sprintf (pos, ", ");
+
+    }
+
+  *(pos) = '\0';
+
+  return nlpidstring;
+}
+
+/*
+ *  supports the given af ?
+ */
+int
+speaks (struct nlpids *nlpids, int family)
+{
+  int i, speaks = 0;
+
+  if (nlpids == (struct nlpids *) NULL)
+    return speaks;
+  for (i = 0; i < nlpids->count; i++)
+    {
+      if (family == AF_INET && nlpids->nlpids[i] == NLPID_IP)
+       speaks = 1;
+      if (family == AF_INET6 && nlpids->nlpids[i] == NLPID_IPV6)
+       speaks = 1;
+    }
+
+  return speaks;
+}
+
+/*
+ * Returns 0 on error, IS-IS Circuit Type on ok
+ */
+int
+string2circuit_t (const char * str)
+{
+
+  if (!str)
+    return 0;
+
+  if (!strcmp (str, "level-1"))
+    return IS_LEVEL_1;
+
+  if (!strcmp (str, "level-2-only") || !strcmp (str, "level-2"))
+    return IS_LEVEL_2;
+
+  if (!strcmp (str, "level-1-2"))
+    return IS_LEVEL_1_AND_2;
+
+  return 0;
+}
+
+const char *
+circuit_state2string (int state)
+{
+
+  switch (state)
+    {
+    case C_STATE_INIT:
+      return "Init";
+    case C_STATE_CONF:
+      return "Config";
+    case C_STATE_UP:
+      return "Up";
+    default:
+      return "Unknown";
+    }
+  return NULL;
+}
+
+const char *
+circuit_type2string (int type)
+{
+
+  switch (type)
+    {
+    case CIRCUIT_T_P2P:
+      return "p2p";
+    case CIRCUIT_T_BROADCAST:
+      return "lan";
+    case CIRCUIT_T_LOOPBACK:
+      return "loopback";
+    default:
+      return "Unknown";
+    }
+  return NULL;
+}
+
+const char *
+circuit_t2string (int circuit_t)
+{
+  switch (circuit_t)
+    {
+    case IS_LEVEL_1:
+      return "L1";
+    case IS_LEVEL_2:
+      return "L2";
+    case IS_LEVEL_1_AND_2:
+      return "L1L2";
+    default:
+      return "??";
+    }
+
+  return NULL;                 /* not reached */
+}
+
+const char *
+syst2string (int type)
+{
+  switch (type)
+    {
+    case ISIS_SYSTYPE_ES:
+      return "ES";
+    case ISIS_SYSTYPE_IS:
+      return "IS";
+    case ISIS_SYSTYPE_L1_IS:
+      return "1";
+    case ISIS_SYSTYPE_L2_IS:
+      return "2";
+    default:
+      return "??";
+    }
+
+  return NULL;                 /* not reached */
+}
+
+/*
+ * Print functions - we print to static vars
+ */
+const char *
+snpa_print (const u_char * from)
+{
+  int i = 0;
+  u_char *pos = (u_char *)snpa;
+
+  if (!from)
+    return "unknown";
+
+  while (i < ETH_ALEN - 1)
+    {
+      if (i & 1)
+       {
+         sprintf ((char *)pos, "%02x.", *(from + i));
+         pos += 3;
+       }
+      else
+       {
+         sprintf ((char *)pos, "%02x", *(from + i));
+         pos += 2;
+
+       }
+      i++;
+    }
+
+  sprintf ((char *)pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1)));
+  pos += 2;
+  *(pos) = '\0';
+
+  return snpa;
+}
+
+const char *
+sysid_print (const u_char * from)
+{
+  int i = 0;
+  char *pos = sysid;
+
+  if (!from)
+    return "unknown";
+
+  while (i < ISIS_SYS_ID_LEN - 1)
+    {
+      if (i & 1)
+       {
+         sprintf (pos, "%02x.", *(from + i));
+         pos += 3;
+       }
+      else
+       {
+         sprintf (pos, "%02x", *(from + i));
+         pos += 2;
+
+       }
+      i++;
+    }
+
+  sprintf (pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1)));
+  pos += 2;
+  *(pos) = '\0';
+
+  return sysid;
+}
+
+const char *
+rawlspid_print (const u_char * from)
+{
+  char *pos = lspid;
+  if (!from)
+    return "unknown";
+  memcpy (pos, sysid_print (from), 15);
+  pos += 14;
+  sprintf (pos, ".%02x", LSP_PSEUDO_ID (from));
+  pos += 3;
+  sprintf (pos, "-%02x", LSP_FRAGMENT (from));
+  pos += 3;
+
+  *(pos) = '\0';
+
+  return lspid;
+}
+
+const char *
+time2string (u_int32_t time)
+{
+  char *pos = datestring;
+  u_int32_t rest;
+
+  if (time == 0)
+    return "-";
+
+  if (time / SECS_PER_YEAR)
+    pos += sprintf (pos, "%uY", time / SECS_PER_YEAR);
+  rest = time % SECS_PER_YEAR;
+  if (rest / SECS_PER_MONTH)
+    pos += sprintf (pos, "%uM", rest / SECS_PER_MONTH);
+  rest = rest % SECS_PER_MONTH;
+  if (rest / SECS_PER_WEEK)
+    pos += sprintf (pos, "%uw", rest / SECS_PER_WEEK);
+  rest = rest % SECS_PER_WEEK;
+  if (rest / SECS_PER_DAY)
+    pos += sprintf (pos, "%ud", rest / SECS_PER_DAY);
+  rest = rest % SECS_PER_DAY;
+  if (rest / SECS_PER_HOUR)
+    pos += sprintf (pos, "%uh", rest / SECS_PER_HOUR);
+  rest = rest % SECS_PER_HOUR;
+  if (rest / SECS_PER_MINUTE)
+    pos += sprintf (pos, "%um", rest / SECS_PER_MINUTE);
+  rest = rest % SECS_PER_MINUTE;
+  if (rest)
+    pos += sprintf (pos, "%us", rest);
+
+  *(pos) = 0;
+
+  return datestring;
+}
+
+/*
+ * routine to decrement a timer by a random
+ * number
+ *
+ * first argument is the timer and the second is
+ * the jitter
+ */
+unsigned long
+isis_jitter (unsigned long timer, unsigned long jitter)
+{
+  int j, k;
+
+  if (jitter >= 100)
+    return timer;
+
+  if (timer == 1)
+    return timer;
+  /* 
+   * randomizing just the percent value provides
+   * no good random numbers - hence the spread
+   * to RANDOM_SPREAD (100000), which is ok as
+   * most IS-IS timers are no longer than 16 bit
+   */
+
+  j = 1 + (int) ((RANDOM_SPREAD * random ()) / (RAND_MAX + 1.0));
+
+  k = timer - (timer * (100 - jitter)) / 100;
+
+  timer = timer - (k * j / RANDOM_SPREAD);
+
+  return timer;
+}
+
+struct in_addr
+newprefix2inaddr (u_char * prefix_start, u_char prefix_masklen)
+{
+  memset (&new_prefix, 0, sizeof (new_prefix));
+  memcpy (&new_prefix, prefix_start, (prefix_masklen & 0x3F) ?
+         ((((prefix_masklen & 0x3F) - 1) >> 3) + 1) : 0);
+  return new_prefix;
+}
+
+/*
+ * Returns host.name if any, otherwise
+ * it returns the system hostname.
+ */
+const char *
+unix_hostname (void)
+{
+  static struct utsname names;
+  const char *hostname;
+
+  hostname = host.name;
+  if (!hostname)
+    {
+      uname (&names);
+      hostname = names.nodename;
+    }
+
+  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 (const 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);
+}
+
+/*
+ * This function is a generic utility that logs data of given length.
+ * Move this to a shared lib so that any protocol can use it.
+ */
+void
+zlog_dump_data (void *data, int len)
+{
+  int i;
+  unsigned char *p;
+  unsigned char c;
+  char bytestr[4];
+  char addrstr[10];
+  char hexstr[ 16*3 + 5];
+  char charstr[16*1 + 5];
+
+  p = data;
+  memset (bytestr, 0, sizeof(bytestr));
+  memset (addrstr, 0, sizeof(addrstr));
+  memset (hexstr, 0, sizeof(hexstr));
+  memset (charstr, 0, sizeof(charstr));
+
+  for (i = 1; i <= len; i++)
+  {
+    c = *p;
+    if (isalnum (c) == 0)
+      c = '.';
+
+    /* store address for this line */
+    if ((i % 16) == 1)
+      snprintf (addrstr, sizeof(addrstr), "%p", p);
+
+    /* store hex str (for left side) */
+    snprintf (bytestr, sizeof (bytestr), "%02X ", *p);
+    strncat (hexstr, bytestr, sizeof (hexstr) - strlen (hexstr) - 1);
+
+    /* store char str (for right side) */
+    snprintf (bytestr, sizeof (bytestr), "%c", c);
+    strncat (charstr, bytestr, sizeof (charstr) - strlen (charstr) - 1);
+
+    if ((i % 16) == 0)
+    {
+      /* line completed */
+      zlog_debug ("[%8.8s]   %-50.50s  %s", addrstr, hexstr, charstr);
+      hexstr[0] = 0;
+      charstr[0] = 0;
+    }
+    else if ((i % 8) == 0)
+    {
+      /* half line: add whitespaces */
+      strncat (hexstr, "  ", sizeof (hexstr) - strlen (hexstr) - 1);
+      strncat (charstr, " ", sizeof (charstr) - strlen (charstr) - 1);
+    }
+    p++; /* next byte */
+  }
+
+  /* print rest of buffer if not empty */
+  if (strlen (hexstr) > 0)
+    zlog_debug ("[%8.8s]   %-50.50s  %s", addrstr, hexstr, charstr);
+  return;
+}
diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h
new file mode 100644 (file)
index 0000000..37eaea1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_misc.h
+ *                             Miscellanous routines
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_MISC_H
+#define _ZEBRA_ISIS_MISC_H
+
+int string2circuit_t (const char *);
+const char *circuit_t2string (int);
+const char *circuit_state2string (int state);
+const char *circuit_type2string (int type);
+const char *syst2string (int);
+struct in_addr newprefix2inaddr (u_char * prefix_start,
+                                u_char prefix_masklen);
+/*
+ * Converting input to memory stored format
+ * return value of 0 indicates wrong input
+ */
+int dotformat2buff (u_char *, const char *);
+int sysid2buff (u_char *, const char *);
+
+/*
+ * Printing functions
+ */
+const char *isonet_print (const u_char *, int len);
+const char *sysid_print (const u_char *);
+const char *snpa_print (const u_char *);
+const char *rawlspid_print (const u_char *);
+const char *time2string (u_int32_t);
+/* typedef struct nlpids nlpids; */
+char *nlpid2string (struct nlpids *);
+const char *print_sys_hostname (const u_char *sysid);
+void zlog_dump_data (void *data, int len);
+
+/*
+ * misc functions
+ */
+int speaks (struct nlpids *nlpids, int family);
+unsigned long isis_jitter (unsigned long timer, unsigned long jitter);
+const char *unix_hostname (void);
+
+/*
+ * macros
+ */
+#define GETSYSID(A) (A->area_addr + (A->addr_len - \
+                                     (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN)))
+
+/* used for calculating nice string representation instead of plain seconds */
+
+#define SECS_PER_MINUTE 60
+#define SECS_PER_HOUR   3600
+#define SECS_PER_DAY    86400
+#define SECS_PER_WEEK   604800
+#define SECS_PER_MONTH  2628000
+#define SECS_PER_YEAR   31536000
+
+enum
+{
+  ISIS_UI_LEVEL_BRIEF,
+  ISIS_UI_LEVEL_DETAIL,
+  ISIS_UI_LEVEL_EXTENSIVE,
+};
+
+#endif
diff --git a/isisd/isis_network.h b/isisd/isis_network.h
new file mode 100644 (file)
index 0000000..e1e10df
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_network.h   
+ *
+ * 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.
+ */
+
+
+#ifndef _ZEBRA_ISIS_NETWORK_H
+#define _ZEBRA_ISIS_NETWORK_H
+
+extern u_char ALL_L1_ISYSTEMS[];
+extern u_char ALL_L2_ISYSTEMS[];
+
+int isis_sock_init (struct isis_circuit *circuit);
+
+int isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa);
+int isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa);
+int isis_send_pdu_bcast (struct isis_circuit *circuit, int level);
+int isis_send_pdu_p2p (struct isis_circuit *circuit, int level);
+
+#endif /* _ZEBRA_ISIS_NETWORK_H */
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
new file mode 100644 (file)
index 0000000..1dfb462
--- /dev/null
@@ -0,0 +1,3257 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_pdu.c   
+ *                             PDU processing
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "thread.h"
+#include "linklist.h"
+#include "log.h"
+#include "stream.h"
+#include "vty.h"
+#include "hash.h"
+#include "prefix.h"
+#include "if.h"
+#include "checksum.h"
+#include "md5.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/iso_checksum.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_events.h"
+#include "isisd/isis_te.h"
+
+#define ISIS_MINIMUM_FIXED_HDR_LEN 15
+#define ISIS_MIN_PDU_LEN           13  /* partial seqnum pdu with id_len=2 */
+
+#ifndef PNBBY
+#define PNBBY 8
+#endif /* PNBBY */
+
+/* Utility mask array. */
+static const u_char maskbit[] = {
+  0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+/*
+ * HELPER FUNCS
+ */
+
+/*
+ * Compares two sets of area addresses
+ */
+static int
+area_match (struct list *left, struct list *right)
+{
+  struct area_addr *addr1, *addr2;
+  struct listnode *node1, *node2;
+
+  for (ALL_LIST_ELEMENTS_RO (left, node1, addr1))
+  {
+    for (ALL_LIST_ELEMENTS_RO (right, node2, addr2))
+    {
+      if (addr1->addr_len == addr2->addr_len &&
+         !memcmp (addr1->area_addr, addr2->area_addr, (int) addr1->addr_len))
+       return 1;               /* match */
+    }
+  }
+
+  return 0;                    /* mismatch */
+}
+
+/*
+ * Check if ip2 is in the ip1's network (function like Prefix.h:prefix_match() )
+ * param ip1            the IS interface ip address structure
+ * param ip2            the IIH's ip address
+ * return  0            the IIH's IP is not in the IS's subnetwork
+ *         1            the IIH's IP is in the IS's subnetwork
+ */
+static int
+ip_same_subnet (struct prefix_ipv4 *ip1, struct in_addr *ip2)
+{
+  u_char *addr1, *addr2;
+  int shift, offset, offsetloop;
+  int len;
+
+  addr1 = (u_char *) & ip1->prefix.s_addr;
+  addr2 = (u_char *) & ip2->s_addr;
+  len = ip1->prefixlen;
+
+  shift = len % PNBBY;
+  offsetloop = offset = len / PNBBY;
+
+  while (offsetloop--)
+    if (addr1[offsetloop] != addr2[offsetloop])
+      return 0;
+
+  if (shift)
+    if (maskbit[shift] & (addr1[offset] ^ addr2[offset]))
+      return 0;
+
+  return 1;                    /* match  */
+}
+
+/*
+ * Compares two set of ip addresses
+ * param left     the local interface's ip addresses
+ * param right    the iih interface's ip address
+ * return         0   no match;
+ *                1   match;
+ */
+static int
+ip_match (struct list *left, struct list *right)
+{
+  struct prefix_ipv4 *ip1;
+  struct in_addr *ip2;
+  struct listnode *node1, *node2;
+
+  if ((left == NULL) || (right == NULL))
+    return 0;
+  
+  for (ALL_LIST_ELEMENTS_RO (left, node1, ip1))
+  {
+    for (ALL_LIST_ELEMENTS_RO (right, node2, ip2))
+    {
+      if (ip_same_subnet (ip1, ip2))
+       {
+         return 1;             /* match */
+       }
+    }
+
+  }
+  return 0;
+}
+
+/*
+ * Checks whether we should accept a PDU of given level 
+ */
+static int
+accept_level (int level, int circuit_t)
+{
+  int retval = ((circuit_t & level) == level); /* simple approach */
+
+  return retval;
+}
+
+/*
+ * Verify authentication information
+ * Support cleartext and HMAC MD5 authentication
+ */
+static int
+authentication_check (struct isis_passwd *remote, struct isis_passwd *local,
+                      struct stream *stream, uint32_t auth_tlv_offset)
+{
+  unsigned char digest[ISIS_AUTH_MD5_SIZE];
+
+  /* Auth fail () - passwd type mismatch */
+  if (local->type != remote->type)
+    return ISIS_ERROR;
+
+  switch (local->type)
+  {
+    /* No authentication required */
+    case ISIS_PASSWD_TYPE_UNUSED:
+      break;
+
+    /* Cleartext (ISO 10589) */
+    case ISIS_PASSWD_TYPE_CLEARTXT:
+      /* Auth fail () - passwd len mismatch */
+      if (remote->len != local->len)
+        return ISIS_ERROR;
+      return memcmp (local->passwd, remote->passwd, local->len);
+
+    /* HMAC MD5 (RFC 3567) */
+    case ISIS_PASSWD_TYPE_HMAC_MD5:
+      /* Auth fail () - passwd len mismatch */
+      if (remote->len != ISIS_AUTH_MD5_SIZE)
+        return ISIS_ERROR;
+      /* Set the authentication value to 0 before the check */
+      memset (STREAM_DATA (stream) + auth_tlv_offset + 3, 0,
+              ISIS_AUTH_MD5_SIZE);
+      /* Compute the digest */
+      hmac_md5 (STREAM_DATA (stream), stream_get_endp (stream),
+                (unsigned char *) &(local->passwd), local->len,
+                (unsigned char *) &digest);
+      /* Copy back the authentication value after the check */
+      memcpy (STREAM_DATA (stream) + auth_tlv_offset + 3,
+              remote->passwd, ISIS_AUTH_MD5_SIZE);
+      return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE);
+
+    default:
+      zlog_err ("Unsupported authentication type");
+      return ISIS_ERROR;
+  }
+
+  /* Authentication pass when no authentication is configured */
+  return ISIS_OK;
+}
+
+static int
+lsp_authentication_check (struct stream *stream, struct isis_area *area,
+                          int level, struct isis_passwd *passwd)
+{
+  struct isis_link_state_hdr *hdr;
+  uint32_t expected = 0, found = 0, auth_tlv_offset = 0;
+  uint16_t checksum, rem_lifetime, pdu_len;
+  struct tlvs tlvs;
+  int retval = ISIS_OK;
+
+  hdr = (struct isis_link_state_hdr *) (STREAM_PNT (stream));
+  pdu_len = ntohs (hdr->pdu_len);
+  expected |= TLVFLAG_AUTH_INFO;
+  auth_tlv_offset = stream_get_getp (stream) + ISIS_LSP_HDR_LEN;
+  retval = parse_tlvs (area->area_tag, STREAM_PNT (stream) + ISIS_LSP_HDR_LEN,
+                       pdu_len - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN,
+                       &expected, &found, &tlvs, &auth_tlv_offset);
+
+  if (retval != ISIS_OK)
+    {
+      zlog_err ("ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, "
+                "cksum 0x%04x, lifetime %us, len %u",
+                area->area_tag, level, rawlspid_print (hdr->lsp_id),
+                ntohl (hdr->seq_num), ntohs (hdr->checksum),
+                ntohs (hdr->rem_lifetime), pdu_len);
+      if ((isis->debugs & DEBUG_UPDATE_PACKETS) &&
+          (isis->debugs & DEBUG_PACKET_DUMP))
+        zlog_dump_data (STREAM_DATA (stream), stream_get_endp (stream));
+      return retval;
+    }
+
+  if (!(found & TLVFLAG_AUTH_INFO))
+    {
+      zlog_err ("No authentication tlv in LSP");
+      return ISIS_ERROR;
+    }
+
+  if (tlvs.auth_info.type != ISIS_PASSWD_TYPE_CLEARTXT &&
+      tlvs.auth_info.type != ISIS_PASSWD_TYPE_HMAC_MD5)
+    {
+      zlog_err ("Unknown authentication type in LSP");
+      return ISIS_ERROR;
+    }
+
+  /*
+   * RFC 5304 set checksum and remaining lifetime to zero before
+   * verification and reset to old values after verification.
+   */
+  checksum = hdr->checksum;
+  rem_lifetime = hdr->rem_lifetime;
+  hdr->checksum = 0;
+  hdr->rem_lifetime = 0;
+  retval = authentication_check (&tlvs.auth_info, passwd, stream,
+                                 auth_tlv_offset);
+  hdr->checksum = checksum;
+  hdr->rem_lifetime = rem_lifetime;
+
+  return retval;
+}
+
+/*
+ * Processing helper functions
+ */
+static void
+del_addr (void *val)
+{
+  XFREE (MTYPE_ISIS_TMP, val);
+}
+
+static void
+tlvs_to_adj_area_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
+{
+  struct listnode *node;
+  struct area_addr *area_addr, *malloced;
+
+  if (adj->area_addrs)
+    {
+      adj->area_addrs->del = del_addr;
+      list_delete (adj->area_addrs);
+    }
+  adj->area_addrs = list_new ();
+  if (tlvs->area_addrs)
+    {
+      for (ALL_LIST_ELEMENTS_RO (tlvs->area_addrs, node, area_addr))
+      {
+       malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct area_addr));
+       memcpy (malloced, area_addr, sizeof (struct area_addr));
+       listnode_add (adj->area_addrs, malloced);
+      }
+    }
+}
+
+static int
+tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj)
+{
+  int i;
+  struct nlpids *tlv_nlpids;
+
+  if (tlvs->nlpids)
+    {
+
+      tlv_nlpids = tlvs->nlpids;
+      if (tlv_nlpids->count > array_size (adj->nlpids.nlpids))
+        return 1;
+
+      adj->nlpids.count = tlv_nlpids->count;
+
+      for (i = 0; i < tlv_nlpids->count; i++)
+       {
+         adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i];
+       }
+    }
+  return 0;
+}
+
+static void
+tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
+{
+  struct listnode *node;
+  struct in_addr *ipv4_addr, *malloced;
+
+  if (adj->ipv4_addrs)
+    {
+      adj->ipv4_addrs->del = del_addr;
+      list_delete (adj->ipv4_addrs);
+    }
+  adj->ipv4_addrs = list_new ();
+  if (tlvs->ipv4_addrs)
+    {
+      for (ALL_LIST_ELEMENTS_RO (tlvs->ipv4_addrs, node, ipv4_addr))
+      {
+       malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in_addr));
+       memcpy (malloced, ipv4_addr, sizeof (struct in_addr));
+       listnode_add (adj->ipv4_addrs, malloced);
+      }
+    }
+}
+
+#ifdef HAVE_IPV6
+static void
+tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
+{
+  struct listnode *node;
+  struct in6_addr *ipv6_addr, *malloced;
+
+  if (adj->ipv6_addrs)
+    {
+      adj->ipv6_addrs->del = del_addr;
+      list_delete (adj->ipv6_addrs);
+    }
+  adj->ipv6_addrs = list_new ();
+  if (tlvs->ipv6_addrs)
+    {
+      for (ALL_LIST_ELEMENTS_RO (tlvs->ipv6_addrs, node, ipv6_addr))
+      {
+       malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in6_addr));
+       memcpy (malloced, ipv6_addr, sizeof (struct in6_addr));
+       listnode_add (adj->ipv6_addrs, malloced);
+      }
+    }
+
+}
+#endif /* HAVE_IPV6 */
+
+/*
+ *  RECEIVE SIDE                           
+ */
+
+/*
+ * Process P2P IIH
+ * ISO - 10589
+ * Section 8.2.5 - Receiving point-to-point IIH PDUs
+ *
+ */
+static int
+process_p2p_hello (struct isis_circuit *circuit)
+{
+  int retval = ISIS_OK;
+  struct isis_p2p_hello_hdr *hdr;
+  struct isis_adjacency *adj;
+  u_int32_t expected = 0, found = 0, auth_tlv_offset = 0;
+  uint16_t pdu_len;
+  struct tlvs tlvs;
+  int v4_usable = 0, v6_usable = 0;
+
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u",
+                  circuit->area->area_tag, circuit->interface->name,
+                  circuit_t2string (circuit->is_type), circuit->circuit_id);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
+                        stream_get_endp (circuit->rcv_stream));
+    }
+
+  if (circuit->circ_type != CIRCUIT_T_P2P)
+    {
+      zlog_warn ("p2p hello on non p2p circuit");
+      return ISIS_WARNING;
+    }
+
+  if ((stream_get_endp (circuit->rcv_stream) -
+       stream_get_getp (circuit->rcv_stream)) < ISIS_P2PHELLO_HDRLEN)
+    {
+      zlog_warn ("Packet too short");
+      return ISIS_WARNING;
+    }
+
+  /* 8.2.5.1 PDU acceptance tests */
+
+  /* 8.2.5.1 a) external domain untrue */
+  /* FIXME: not useful at all?         */
+
+  /* 8.2.5.1 b) ID Length mismatch */
+  /* checked at the handle_pdu     */
+
+  /* 8.2.5.2 IIH PDU Processing */
+
+  /* 8.2.5.2 a) 1) Maximum Area Addresses */
+  /* Already checked, and can also be ommited */
+
+  /*
+   * Get the header
+   */
+  hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream);
+  pdu_len = ntohs (hdr->pdu_len);
+
+  if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_P2PHELLO_HDRLEN) ||
+      pdu_len > ISO_MTU(circuit) ||
+      pdu_len > stream_get_endp (circuit->rcv_stream))
+    {
+      zlog_warn ("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with "
+                 "invalid pdu length %d",
+                 circuit->area->area_tag, circuit->interface->name, pdu_len);
+      return ISIS_WARNING;
+    }
+
+  /*
+   * Set the stream endp to PDU length, ignoring additional padding
+   * introduced by transport chips.
+   */
+  if (pdu_len < stream_get_endp (circuit->rcv_stream))
+    stream_set_endp (circuit->rcv_stream, pdu_len);
+
+  stream_forward_getp (circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN);
+
+  /*
+   * Lets get the TLVS now
+   */
+  expected |= TLVFLAG_AREA_ADDRS;
+  expected |= TLVFLAG_AUTH_INFO;
+  expected |= TLVFLAG_NLPID;
+  expected |= TLVFLAG_IPV4_ADDR;
+  expected |= TLVFLAG_IPV6_ADDR;
+
+  auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
+  retval = parse_tlvs (circuit->area->area_tag,
+                      STREAM_PNT (circuit->rcv_stream),
+                      pdu_len - ISIS_P2PHELLO_HDRLEN - ISIS_FIXED_HDR_LEN,
+                       &expected, &found, &tlvs, &auth_tlv_offset);
+
+  if (retval > ISIS_WARNING)
+    {
+      zlog_warn ("parse_tlvs() failed");
+      free_tlvs (&tlvs);
+      return retval;
+    };
+
+  if (!(found & TLVFLAG_AREA_ADDRS))
+    {
+      zlog_warn ("No Area addresses TLV in P2P IS to IS hello");
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+  if (!(found & TLVFLAG_NLPID))
+    {
+      zlog_warn ("No supported protocols TLV in P2P IS to IS hello");
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+  /* 8.2.5.1 c) Authentication */
+  if (circuit->passwd.type)
+    {
+      if (!(found & TLVFLAG_AUTH_INFO) ||
+          authentication_check (&tlvs.auth_info, &circuit->passwd,
+                                circuit->rcv_stream, auth_tlv_offset))
+        {
+          isis_event_auth_failure (circuit->area->area_tag,
+                                   "P2P hello authentication failure",
+                                   hdr->source_id);
+          free_tlvs (&tlvs);
+          return ISIS_OK;
+        }
+    }
+
+  /*
+   * check if it's own interface ip match iih ip addrs
+   */
+  if (found & TLVFLAG_IPV4_ADDR)
+    {
+      if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs))
+       v4_usable = 1;
+      else
+       zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap "
+                  "in P2P IIH from %s\n", circuit->interface->name);
+    }
+#ifndef HAVE_IPV6
+  else /* !(found & TLVFLAG_IPV4_ADDR) */
+    zlog_warn ("ISIS-Adj: no IPv4 in P2P IIH from %s "
+              "(this isisd has no IPv6)\n", circuit->interface->name);
+
+#else
+  if (found & TLVFLAG_IPV6_ADDR)
+    {
+      /* TBA: check that we have a linklocal ourselves? */
+      struct listnode *node;
+      struct in6_addr *ip;
+      for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip))
+       if (IN6_IS_ADDR_LINKLOCAL (ip))
+         {
+           v6_usable = 1;
+           break;
+         }
+
+      if (!v6_usable)
+       zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local "
+                  "in P2P IIH from %s\n", circuit->interface->name);
+    }
+
+  if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
+    zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n",
+              circuit->interface->name);
+#endif
+
+  if (!v6_usable && !v4_usable)
+    {
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+  /*
+   * it's own p2p IIH PDU - discard
+   */
+  if (!memcmp (hdr->source_id, isis->sysid, ISIS_SYS_ID_LEN))
+    {
+      zlog_warn ("ISIS-Adj (%s): it's own IIH PDU - discarded",
+                  circuit->area->area_tag);
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+  /*
+   * My interpertation of the ISO, if no adj exists we will create one for
+   * the circuit
+   */
+  adj = circuit->u.p2p.neighbor;
+  /* If an adjacency exists, check it is with the source of the hello
+   * packets */
+  if (adj)
+    {
+      if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN))
+       {
+          zlog_debug("hello source and adjacency do not match, set adj down\n");
+          isis_adj_state_change (adj, ISIS_ADJ_DOWN, "adj do not exist");
+          return 0;
+        }
+    }
+  if (!adj || adj->level != hdr->circuit_t)
+    {
+      if (!adj)
+        {
+          adj = isis_new_adj (hdr->source_id, NULL, hdr->circuit_t, circuit);
+          if (adj == NULL)
+            return ISIS_ERROR;
+        }
+      else
+        {
+          adj->level = hdr->circuit_t;
+        }
+      circuit->u.p2p.neighbor = adj;
+      /* Build lsp with the new neighbor entry when a new
+       * adjacency is formed. Set adjacency circuit type to
+       * IIH PDU header circuit type before lsp is regenerated
+       * when an adjacency is up. This will result in the new
+       * adjacency entry getting added to the lsp tlv neighbor list.
+       */
+      adj->circuit_t = hdr->circuit_t;
+      isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL);
+      adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
+    }
+
+  /* 8.2.6 Monitoring point-to-point adjacencies */
+  adj->hold_time = ntohs (hdr->hold_time);
+  adj->last_upd = time (NULL);
+
+  /* we do this now because the adj may not survive till the end... */
+  tlvs_to_adj_area_addrs (&tlvs, adj);
+
+  /* which protocol are spoken ??? */
+  if (tlvs_to_adj_nlpids (&tlvs, adj))
+    {
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+  /* we need to copy addresses to the adj */
+  if (found & TLVFLAG_IPV4_ADDR)
+    tlvs_to_adj_ipv4_addrs (&tlvs, adj);
+
+  /* Update MPLS TE Remote IP address parameter if possible */
+  if (IS_MPLS_TE(isisMplsTE) && circuit->mtc && IS_CIRCUIT_TE(circuit->mtc))
+    if (adj->ipv4_addrs != NULL && listcount(adj->ipv4_addrs) != 0)
+      {
+        struct in_addr *ip_addr;
+        ip_addr = (struct in_addr *)listgetdata ((struct listnode *)listhead (adj->ipv4_addrs));
+        set_circuitparams_rmt_ipaddr (circuit->mtc, *ip_addr);
+      }
+
+#ifdef HAVE_IPV6
+  if (found & TLVFLAG_IPV6_ADDR)
+    tlvs_to_adj_ipv6_addrs (&tlvs, adj);
+#endif /* HAVE_IPV6 */
+
+  /* lets take care of the expiry */
+  THREAD_TIMER_OFF (adj->t_expire);
+  THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj,
+                  (long) adj->hold_time);
+
+  /* 8.2.5.2 a) a match was detected */
+  if (area_match (circuit->area->area_addrs, tlvs.area_addrs))
+    {
+      /* 8.2.5.2 a) 2) If the system is L1 - table 5 */
+      if (circuit->area->is_type == IS_LEVEL_1)
+       {
+         switch (hdr->circuit_t)
+           {
+           case IS_LEVEL_1:
+           case IS_LEVEL_1_AND_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (4) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (5) adj usage level 1 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL1;
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
+               {
+                 ;             /* accept */
+               }
+             break;
+           case IS_LEVEL_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (7) reject - wrong system type event */
+                 zlog_warn ("wrongSystemType");
+                  free_tlvs (&tlvs);
+                 return ISIS_WARNING;  /* Reject */
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
+               {
+                 /* (6) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             break;
+           }
+       }
+
+      /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */
+      if (circuit->area->is_type == IS_LEVEL_1_AND_2)
+       {
+         switch (hdr->circuit_t)
+           {
+           case IS_LEVEL_1:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (6) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (7) adj usage level 1 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL1;
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
+               {
+                 ;             /* accept */
+               }
+             else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
+                      (adj->adj_usage == ISIS_ADJ_LEVEL2))
+               {
+                 /* (8) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             break;
+           case IS_LEVEL_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (6) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (9) adj usage level 2 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL2;
+               }
+             else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) ||
+                      (adj->adj_usage == ISIS_ADJ_LEVEL1AND2))
+               {
+                 /* (8) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
+               {
+                 ;             /* Accept */
+               }
+             break;
+           case IS_LEVEL_1_AND_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (6) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (10) adj usage level 1 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL1AND2;
+               }
+             else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) ||
+                      (adj->adj_usage == ISIS_ADJ_LEVEL2))
+               {
+                 /* (8) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
+               {
+                 ;             /* Accept */
+               }
+             break;
+           }
+       }
+
+      /* 8.2.5.2 a) 4) If the system is L2 - table 7 */
+      if (circuit->area->is_type == IS_LEVEL_2)
+       {
+         switch (hdr->circuit_t)
+           {
+           case IS_LEVEL_1:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (5) reject - wrong system type event */
+                 zlog_warn ("wrongSystemType");
+                  free_tlvs (&tlvs);
+                 return ISIS_WARNING;  /* Reject */
+               }
+             else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
+                      (adj->adj_usage == ISIS_ADJ_LEVEL2))
+               {
+                 /* (6) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             break;
+           case IS_LEVEL_1_AND_2:
+           case IS_LEVEL_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (7) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (8) adj usage level 2 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL2;
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
+               {
+                 /* (6) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
+               {
+                 ;             /* Accept */
+               }
+             break;
+           }
+       }
+    }
+  /* 8.2.5.2 b) if no match was detected */
+  else if (listcount (circuit->area->area_addrs) > 0)
+    {
+      if (circuit->area->is_type == IS_LEVEL_1)
+       {
+         /* 8.2.5.2 b) 1) is_type L1 and adj is not up */
+         if (adj->adj_state != ISIS_ADJ_UP)
+           {
+             isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
+             /* 8.2.5.2 b) 2)is_type L1 and adj is up */
+           }
+         else
+           {
+             isis_adj_state_change (adj, ISIS_ADJ_DOWN,
+                                    "Down - Area Mismatch");
+           }
+       }
+      /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
+      else
+       {
+         switch (hdr->circuit_t)
+           {
+           case IS_LEVEL_1:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (6) reject - Area Mismatch event */
+                 zlog_warn ("AreaMismatch");
+                  free_tlvs (&tlvs);
+                 return ISIS_WARNING;  /* Reject */
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
+               {
+                 /* (7) down - area mismatch */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
+
+               }
+             else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
+                      (adj->adj_usage == ISIS_ADJ_LEVEL2))
+               {
+                 /* (7) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             break;
+           case IS_LEVEL_1_AND_2:
+           case IS_LEVEL_2:
+             if (adj->adj_state != ISIS_ADJ_UP)
+               {
+                 /* (8) adj state up */
+                 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
+                 /* (9) adj usage level 2 */
+                 adj->adj_usage = ISIS_ADJ_LEVEL2;
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
+               {
+                 /* (7) down - wrong system */
+                 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
+               {
+                 if (hdr->circuit_t == IS_LEVEL_2)
+                   {
+                     /* (7) down - wrong system */
+                     isis_adj_state_change (adj, ISIS_ADJ_DOWN,
+                                            "Wrong System");
+                   }
+                 else
+                   {
+                     /* (7) down - area mismatch */
+                     isis_adj_state_change (adj, ISIS_ADJ_DOWN,
+                                            "Area Mismatch");
+                   }
+               }
+             else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
+               {
+                 ;             /* Accept */
+               }
+             break;
+           }
+       }
+    }
+  else
+    {
+      /* down - area mismatch */
+      isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
+    }
+  /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
+  /* FIXME - Missing parts */
+
+  /* some of my own understanding of the ISO, why the heck does
+   * it not say what should I change the system_type to...
+   */
+  switch (adj->adj_usage)
+    {
+    case ISIS_ADJ_LEVEL1:
+      adj->sys_type = ISIS_SYSTYPE_L1_IS;
+      break;
+    case ISIS_ADJ_LEVEL2:
+      adj->sys_type = ISIS_SYSTYPE_L2_IS;
+      break;
+    case ISIS_ADJ_LEVEL1AND2:
+      adj->sys_type = ISIS_SYSTYPE_L2_IS;
+      break;
+    case ISIS_ADJ_NONE:
+      adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
+      break;
+    }
+
+
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s,"
+                 " cir id %02d, length %d",
+                 circuit->area->area_tag, circuit->interface->name,
+                 circuit_t2string (circuit->is_type),
+                 circuit->circuit_id, pdu_len);
+    }
+
+  free_tlvs (&tlvs);
+
+  return retval;
+}
+
+/*
+ * Process IS-IS LAN Level 1/2 Hello PDU
+ */
+static int
+process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa)
+{
+  int retval = ISIS_OK;
+  struct isis_lan_hello_hdr hdr;
+  struct isis_adjacency *adj;
+  u_int32_t expected = 0, found = 0, auth_tlv_offset = 0;
+  struct tlvs tlvs;
+  u_char *snpa;
+  struct listnode *node;
+  int v4_usable = 0, v6_usable = 0;
+
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, "
+                  "cirID %u",
+                  circuit->area->area_tag, level, circuit->interface->name,
+                  circuit_t2string (circuit->is_type), circuit->circuit_id);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
+                        stream_get_endp (circuit->rcv_stream));
+    }
+
+  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    {
+      zlog_warn ("lan hello on non broadcast circuit");
+      return ISIS_WARNING;
+    }
+
+  if ((stream_get_endp (circuit->rcv_stream) -
+       stream_get_getp (circuit->rcv_stream)) < ISIS_LANHELLO_HDRLEN)
+    {
+      zlog_warn ("Packet too short");
+      return ISIS_WARNING;
+    }
+
+  if (circuit->ext_domain)
+    {
+      zlog_debug ("level %d LAN Hello received over circuit with "
+                 "externalDomain = true", level);
+      return ISIS_WARNING;
+    }
+
+  if (!accept_level (level, circuit->is_type))
+    {
+      if (isis->debugs & DEBUG_ADJ_PACKETS)
+       {
+         zlog_debug ("ISIS-Adj (%s): Interface level mismatch, %s",
+                     circuit->area->area_tag, circuit->interface->name);
+       }
+      return ISIS_WARNING;
+    }
+
+#if 0
+  /* Cisco's debug message compatability */
+  if (!accept_level (level, circuit->area->is_type))
+    {
+      if (isis->debugs & DEBUG_ADJ_PACKETS)
+       {
+         zlog_debug ("ISIS-Adj (%s): is type mismatch",
+                     circuit->area->area_tag);
+       }
+      return ISIS_WARNING;
+    }
+#endif
+  /*
+   * Fill the header
+   */
+  hdr.circuit_t = stream_getc (circuit->rcv_stream);
+  stream_get (hdr.source_id, circuit->rcv_stream, ISIS_SYS_ID_LEN);
+  hdr.hold_time = stream_getw (circuit->rcv_stream);
+  hdr.pdu_len = stream_getw (circuit->rcv_stream);
+  hdr.prio = stream_getc (circuit->rcv_stream);
+  stream_get (hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1);
+
+  if (hdr.pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LANHELLO_HDRLEN) ||
+      hdr.pdu_len > ISO_MTU(circuit) ||
+      hdr.pdu_len > stream_get_endp (circuit->rcv_stream))
+    {
+      zlog_warn ("ISIS-Adj (%s): Rcvd LAN IIH from (%s) with "
+                 "invalid pdu length %d",
+                 circuit->area->area_tag, circuit->interface->name,
+                 hdr.pdu_len);
+      return ISIS_WARNING;
+    }
+
+  /*
+   * Set the stream endp to PDU length, ignoring additional padding
+   * introduced by transport chips.
+   */
+  if (hdr.pdu_len < stream_get_endp (circuit->rcv_stream))
+    stream_set_endp (circuit->rcv_stream, hdr.pdu_len);
+
+  if (hdr.circuit_t != IS_LEVEL_1 &&
+      hdr.circuit_t != IS_LEVEL_2 &&
+      hdr.circuit_t != IS_LEVEL_1_AND_2 &&
+      (level & hdr.circuit_t) == 0)
+    {
+      zlog_err ("Level %d LAN Hello with Circuit Type %d", level,
+                hdr.circuit_t);
+      return ISIS_ERROR;
+    }
+
+  /*
+   * Then get the tlvs
+   */
+  expected |= TLVFLAG_AUTH_INFO;
+  expected |= TLVFLAG_AREA_ADDRS;
+  expected |= TLVFLAG_LAN_NEIGHS;
+  expected |= TLVFLAG_NLPID;
+  expected |= TLVFLAG_IPV4_ADDR;
+  expected |= TLVFLAG_IPV6_ADDR;
+
+  auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
+  retval = parse_tlvs (circuit->area->area_tag,
+                       STREAM_PNT (circuit->rcv_stream),
+                       hdr.pdu_len - ISIS_LANHELLO_HDRLEN - ISIS_FIXED_HDR_LEN,
+                       &expected, &found, &tlvs,
+                       &auth_tlv_offset);
+
+  if (retval > ISIS_WARNING)
+    {
+      zlog_warn ("parse_tlvs() failed");
+      goto out;
+    }
+
+  if (!(found & TLVFLAG_AREA_ADDRS))
+    {
+      zlog_warn ("No Area addresses TLV in Level %d LAN IS to IS hello",
+                level);
+      retval = ISIS_WARNING;
+      goto out;
+    }
+
+  if (!(found & TLVFLAG_NLPID))
+    {
+      zlog_warn ("No supported protocols TLV in Level %d LAN IS to IS hello",
+                level);
+      retval = ISIS_WARNING;
+      goto out;
+    }
+
+  /* Verify authentication, either cleartext of HMAC MD5 */
+  if (circuit->passwd.type)
+    {
+      if (!(found & TLVFLAG_AUTH_INFO) ||
+          authentication_check (&tlvs.auth_info, &circuit->passwd,
+                                circuit->rcv_stream, auth_tlv_offset))
+        {
+          isis_event_auth_failure (circuit->area->area_tag,
+                                   "LAN hello authentication failure",
+                                   hdr.source_id);
+          retval = ISIS_WARNING;
+          goto out;
+        }
+    }
+
+  if (!memcmp (hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN))
+    {
+      zlog_warn ("ISIS-Adj (%s): duplicate system ID on interface %s",
+                circuit->area->area_tag, circuit->interface->name);
+      return ISIS_WARNING;
+    }
+
+  /*
+   * Accept the level 1 adjacency only if a match between local and
+   * remote area addresses is found
+   */
+  if (listcount (circuit->area->area_addrs) == 0 ||
+      (level == IS_LEVEL_1 &&
+       area_match (circuit->area->area_addrs, tlvs.area_addrs) == 0))
+    {
+      if (isis->debugs & DEBUG_ADJ_PACKETS)
+       {
+         zlog_debug ("ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
+                     circuit->area->area_tag, level,
+                     circuit->interface->name);
+       }
+      retval = ISIS_OK;
+      goto out;
+    }
+
+  /* 
+   * it's own IIH PDU - discard silently 
+   */
+  if (!memcmp (circuit->u.bc.snpa, ssnpa, ETH_ALEN))
+    {
+      zlog_debug ("ISIS-Adj (%s): it's own IIH PDU - discarded",
+                 circuit->area->area_tag);
+
+      retval = ISIS_OK;
+      goto out;
+    }
+
+  /*
+   * check if it's own interface ip match iih ip addrs
+   */
+  if (found & TLVFLAG_IPV4_ADDR)
+    {
+      if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs))
+       v4_usable = 1;
+      else
+       zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap "
+                  "in LAN IIH from %s\n", circuit->interface->name);
+    }
+#ifndef HAVE_IPV6
+  else /* !(found & TLVFLAG_IPV4_ADDR) */
+    zlog_warn ("ISIS-Adj: no IPv4 in LAN IIH from %s "
+              "(this isisd has no IPv6)\n", circuit->interface->name);
+
+#else
+  if (found & TLVFLAG_IPV6_ADDR)
+    {
+      /* TBA: check that we have a linklocal ourselves? */
+      struct listnode *node;
+      struct in6_addr *ip;
+      for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip))
+       if (IN6_IS_ADDR_LINKLOCAL (ip))
+         {
+           v6_usable = 1;
+           break;
+         }
+
+      if (!v6_usable)
+       zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local "
+                  "in LAN IIH from %s\n", circuit->interface->name);
+    }
+
+  if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
+    zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n",
+              circuit->interface->name);
+#endif
+
+  if (!v6_usable && !v4_usable)
+    {
+      free_tlvs (&tlvs);
+      return ISIS_WARNING;
+    }
+
+
+  adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]);
+  if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) ||
+      (adj->level != level))
+    {
+      if (!adj)
+        {
+          /*
+           * Do as in 8.4.2.5
+           */
+          adj = isis_new_adj (hdr.source_id, ssnpa, level, circuit);
+          if (adj == NULL)
+            {
+              retval = ISIS_ERROR;
+              goto out;
+            }
+        }
+      else
+        {
+          if (ssnpa) {
+            memcpy (adj->snpa, ssnpa, 6);
+          } else {
+            memset (adj->snpa, ' ', 6);
+          }
+          adj->level = level;
+        }
+      isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL);
+
+      if (level == IS_LEVEL_1)
+          adj->sys_type = ISIS_SYSTYPE_L1_IS;
+      else
+          adj->sys_type = ISIS_SYSTYPE_L2_IS;
+      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]);
+    }
+
+  if(adj->dis_record[level-1].dis==ISIS_IS_DIS)
+    switch (level)
+      {
+      case 1:
+       if (memcmp (circuit->u.bc.l1_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1))
+         {
+            thread_add_event (master, isis_event_dis_status_change, circuit, 0);
+           memcpy (&circuit->u.bc.l1_desig_is, hdr.lan_id,
+                   ISIS_SYS_ID_LEN + 1);
+         }
+       break;
+      case 2:
+       if (memcmp (circuit->u.bc.l2_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1))
+         {
+            thread_add_event (master, isis_event_dis_status_change, circuit, 0);
+           memcpy (&circuit->u.bc.l2_desig_is, hdr.lan_id,
+                   ISIS_SYS_ID_LEN + 1);
+         }
+       break;
+      }
+
+  adj->hold_time = hdr.hold_time;
+  adj->last_upd = time (NULL);
+  adj->prio[level - 1] = hdr.prio;
+
+  memcpy (adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1);
+
+  tlvs_to_adj_area_addrs (&tlvs, adj);
+
+  /* which protocol are spoken ??? */
+  if (tlvs_to_adj_nlpids (&tlvs, adj))
+    {
+      retval = ISIS_WARNING;
+      goto out;
+    }
+
+  /* we need to copy addresses to the adj */
+  if (found & TLVFLAG_IPV4_ADDR)
+    tlvs_to_adj_ipv4_addrs (&tlvs, adj);
+
+#ifdef HAVE_IPV6
+  if (found & TLVFLAG_IPV6_ADDR)
+    tlvs_to_adj_ipv6_addrs (&tlvs, adj);
+#endif /* HAVE_IPV6 */
+
+  adj->circuit_t = hdr.circuit_t;
+
+  /* lets take care of the expiry */
+  THREAD_TIMER_OFF (adj->t_expire);
+  THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj,
+                   (long) adj->hold_time);
+
+  /*
+   * If the snpa for this circuit is found from LAN Neighbours TLV
+   * we have two-way communication -> adjacency can be put to state "up"
+   */
+
+  if (found & TLVFLAG_LAN_NEIGHS)
+  {
+    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))
+        {
+          isis_adj_state_change (adj, ISIS_ADJ_UP,
+                                 "own SNPA found in LAN Neighbours TLV");
+        }
+      }
+    }
+    else
+    {
+      int found = 0;
+      for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa))
+        if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
+        {
+          found = 1;
+          break;
+        }
+      if (found == 0)
+        isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING,
+                               "own SNPA not found in LAN Neighbours TLV");
+    }
+  }
+  else if (adj->adj_state == ISIS_ADJ_UP)
+  {
+    isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING,
+                           "no LAN Neighbours TLV found");
+  }
+
+out:
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, "
+                 "cirID %u, length %zd",
+                 circuit->area->area_tag,
+                 level, snpa_print (ssnpa), circuit->interface->name,
+                 circuit_t2string (circuit->is_type),
+                 circuit->circuit_id,
+                 stream_get_endp (circuit->rcv_stream));
+    }
+
+  free_tlvs (&tlvs);
+
+  return retval;
+}
+
+/*
+ * Process Level 1/2 Link State
+ * ISO - 10589
+ * Section 7.3.15.1 - Action on receipt of a link state PDU
+ */
+static int
+process_lsp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
+{
+  struct isis_link_state_hdr *hdr;
+  struct isis_adjacency *adj = NULL;
+  struct isis_lsp *lsp, *lsp0 = NULL;
+  int retval = ISIS_OK, comp = 0;
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+  struct isis_passwd *passwd;
+  uint16_t pdu_len;
+  int lsp_confusion;
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u",
+                  circuit->area->area_tag, level, circuit->interface->name,
+                  circuit_t2string (circuit->is_type), circuit->circuit_id);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
+                        stream_get_endp (circuit->rcv_stream));
+    }
+
+  if ((stream_get_endp (circuit->rcv_stream) -
+       stream_get_getp (circuit->rcv_stream)) < ISIS_LSP_HDR_LEN)
+    {
+      zlog_warn ("Packet too short");
+      return ISIS_WARNING;
+    }
+
+  /* Reference the header   */
+  hdr = (struct isis_link_state_hdr *) STREAM_PNT (circuit->rcv_stream);
+  pdu_len = ntohs (hdr->pdu_len);
+
+  /* lsp length check */
+  if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN) ||
+      pdu_len > ISO_MTU(circuit) ||
+      pdu_len > stream_get_endp (circuit->rcv_stream))
+    {
+      zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP length %d",
+                 circuit->area->area_tag,
+                 rawlspid_print (hdr->lsp_id), pdu_len);
+
+      return ISIS_WARNING;
+    }
+
+  /*
+   * Set the stream endp to PDU length, ignoring additional padding
+   * introduced by transport chips.
+   */
+  if (pdu_len < stream_get_endp (circuit->rcv_stream))
+    stream_set_endp (circuit->rcv_stream, pdu_len);
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, "
+                 "lifetime %us, len %u, on %s",
+                 circuit->area->area_tag,
+                 level,
+                 rawlspid_print (hdr->lsp_id),
+                 ntohl (hdr->seq_num),
+                 ntohs (hdr->checksum),
+                 ntohs (hdr->rem_lifetime),
+                 pdu_len,
+                 circuit->interface->name);
+    }
+
+  /* lsp is_type check */
+  if ((hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1 &&
+      (hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1_AND_2)
+    {
+      zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP is type %x",
+                 circuit->area->area_tag,
+                 rawlspid_print (hdr->lsp_id), hdr->lsp_bits);
+      /* continue as per RFC1122 Be liberal in what you accept, and
+       * conservative in what you send */
+    }
+
+  /* Checksum sanity check - FIXME: move to correct place */
+  /* 12 = sysid+pdu+remtime */
+  if (iso_csum_verify (STREAM_PNT (circuit->rcv_stream) + 4,
+                      pdu_len - 12, &hdr->checksum))
+    {
+      zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x",
+                 circuit->area->area_tag,
+                 rawlspid_print (hdr->lsp_id), ntohs (hdr->checksum));
+
+      return ISIS_WARNING;
+    }
+
+  /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
+  if (circuit->ext_domain)
+    {
+      zlog_debug
+       ("ISIS-Upd (%s): LSP %s received at level %d over circuit with "
+        "externalDomain = true", circuit->area->area_tag,
+        rawlspid_print (hdr->lsp_id), level);
+
+      return ISIS_WARNING;
+    }
+
+  /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
+  if (!accept_level (level, circuit->is_type))
+    {
+      zlog_debug ("ISIS-Upd (%s): LSP %s received at level %d over circuit of"
+                 " type %s",
+                 circuit->area->area_tag,
+                 rawlspid_print (hdr->lsp_id),
+                 level, circuit_t2string (circuit->is_type));
+
+      return ISIS_WARNING;
+    }
+
+  /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
+
+  /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use 3 */
+
+  /* 7.3.15.1 a) 7 - password check */
+  (level == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) :
+                          (passwd = &circuit->area->domain_passwd);
+  if (passwd->type)
+    {
+      if (lsp_authentication_check (circuit->rcv_stream, circuit->area,
+                                    level, passwd))
+       {
+         isis_event_auth_failure (circuit->area->area_tag,
+                                  "LSP authentication failure", hdr->lsp_id);
+         return ISIS_WARNING;
+       }
+    }
+  /* Find the LSP in our database and compare it to this Link State header */
+  lsp = lsp_search (hdr->lsp_id, circuit->area->lspdb[level - 1]);
+  if (lsp)
+    comp = lsp_compare (circuit->area->area_tag, lsp, hdr->seq_num,
+                       hdr->checksum, hdr->rem_lifetime);
+  if (lsp && (lsp->own_lsp
+#ifdef TOPOLOGY_GENERATE
+             || lsp->from_topology
+#endif /* TOPOLOGY_GENERATE */
+      ))
+    goto dontcheckadj;
+
+  /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level  */
+  /* for broadcast circuits, snpa should be compared */
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      adj = isis_adj_lookup_snpa (ssnpa, circuit->u.bc.adjdb[level - 1]);
+      if (!adj)
+       {
+         zlog_debug ("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, "
+                     "lifetime %us on %s",
+                     circuit->area->area_tag,
+                     rawlspid_print (hdr->lsp_id),
+                     ntohl (hdr->seq_num),
+                     ntohs (hdr->checksum),
+                     ntohs (hdr->rem_lifetime), circuit->interface->name);
+         return ISIS_WARNING;  /* Silently discard */
+       }
+    }
+  /* for non broadcast, we just need to find same level adj */
+  else
+    {
+      /* If no adj, or no sharing of level */
+      if (!circuit->u.p2p.neighbor)
+       {
+         return ISIS_OK;       /* Silently discard */
+       }
+      else
+       {
+         if (((level == IS_LEVEL_1) &&
+              (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) ||
+             ((level == IS_LEVEL_2) &&
+              (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1)))
+           return ISIS_WARNING;        /* Silently discard */
+         adj = circuit->u.p2p.neighbor;
+       }
+    }
+
+dontcheckadj:
+  /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented  */
+
+  /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented  */
+
+  /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented  FIXME: do it */
+
+  /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num but
+   *            wrong checksum, initiate a purge. */
+  if (lsp
+      && (lsp->lsp_header->seq_num == hdr->seq_num)
+      && (lsp->lsp_header->checksum != hdr->checksum))
+    {
+      zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
+                circuit->area->area_tag, rawlspid_print(hdr->lsp_id),
+                ntohl(hdr->seq_num));
+      hdr->rem_lifetime = 0;
+      lsp_confusion = 1;
+    }
+  else
+    lsp_confusion = 0;
+
+  /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
+  if (hdr->rem_lifetime == 0)
+    {
+      if (!lsp)
+       {
+         /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't save */
+         /* only needed on explicit update, eg - p2p */
+         if (circuit->circ_type == CIRCUIT_T_P2P)
+           ack_lsp (hdr, circuit, level);
+         return retval;        /* FIXME: do we need a purge? */
+       }
+      else
+       {
+         if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN))
+           {
+             /* LSP by some other system -> do 7.3.16.4 b) */
+             /* 7.3.16.4 b) 1)  */
+             if (comp == LSP_NEWER)
+               {
+                  lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
+                 /* ii */
+                  lsp_set_all_srmflags (lsp);
+                 /* v */
+                 ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */
+
+                 /* For the case of lsp confusion, flood the purge back to its
+                  * originator so that it can react. Otherwise, don't reflood
+                  * through incoming circuit as usual */
+                 if (!lsp_confusion)
+                   {
+                     /* iii */
+                     ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+                     /* iv */
+                     if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+                       ISIS_SET_FLAG (lsp->SSNflags, circuit);
+                   }
+               }               /* 7.3.16.4 b) 2) */
+             else if (comp == LSP_EQUAL)
+               {
+                 /* i */
+                 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+                 /* ii */
+                 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+                   ISIS_SET_FLAG (lsp->SSNflags, circuit);
+               }               /* 7.3.16.4 b) 3) */
+             else
+               {
+                 ISIS_SET_FLAG (lsp->SRMflags, circuit);
+                 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
+               }
+           }
+          else if (lsp->lsp_header->rem_lifetime != 0)
+            {
+              /* our own LSP -> 7.3.16.4 c) */
+              if (comp == LSP_NEWER)
+                {
+                  lsp_inc_seqnum (lsp, ntohl (hdr->seq_num));
+                  lsp_set_all_srmflags (lsp);
+                }
+              else
+                {
+                  ISIS_SET_FLAG (lsp->SRMflags, circuit);
+                  ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
+                }
+              if (isis->debugs & DEBUG_UPDATE_PACKETS)
+                zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new "
+                            "seq 0x%08x", circuit->area->area_tag,
+                            rawlspid_print (hdr->lsp_id),
+                            ntohl (lsp->lsp_header->seq_num));
+            }
+       }
+      return retval;
+    }
+  /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a 
+   * purge */
+  if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+    {
+      if (!lsp)
+       {
+         /* 7.3.16.4: initiate a purge */
+         lsp_purge_non_exist(level, hdr, circuit->area);
+         return ISIS_OK;
+       }
+      /* 7.3.15.1 d) - If this is our own lsp and we have it */
+
+      /* In 7.3.16.1, If an Intermediate system R somewhere in the domain
+       * has information that the current sequence number for source S is
+       * "greater" than that held by S, ... */
+
+      if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num))
+       {
+         /* 7.3.16.1  */
+          lsp_inc_seqnum (lsp, ntohl (hdr->seq_num));
+         if (isis->debugs & DEBUG_UPDATE_PACKETS)
+           zlog_debug ("ISIS-Upd (%s): (2) re-originating LSP %s new seq "
+                       "0x%08x", circuit->area->area_tag,
+                       rawlspid_print (hdr->lsp_id),
+                       ntohl (lsp->lsp_header->seq_num));
+       }
+      /* If the received LSP is older or equal,
+       * resend the LSP which will act as ACK */
+      lsp_set_all_srmflags (lsp);
+    }
+  else
+    {
+      /* 7.3.15.1 e) - This lsp originated on another system */
+
+      /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */
+      if ((!lsp || comp == LSP_NEWER))
+       {
+         /*
+          * If this lsp is a frag, need to see if we have zero lsp present
+          */
+         if (LSP_FRAGMENT (hdr->lsp_id) != 0)
+           {
+             memcpy (lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1);
+             LSP_FRAGMENT (lspid) = 0;
+             lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]);
+             if (!lsp0)
+               {
+                 zlog_debug ("Got lsp frag, while zero lsp not in database");
+                 return ISIS_OK;
+               }
+           }
+         /* i */
+         if (!lsp)
+            {
+             lsp = lsp_new_from_stream_ptr (circuit->rcv_stream,
+                                             pdu_len, lsp0,
+                                             circuit->area, level);
+              lsp_insert (lsp, circuit->area->lspdb[level - 1]);
+            }
+          else /* exists, so we overwrite */
+            {
+              lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
+            }
+         /* ii */
+          lsp_set_all_srmflags (lsp);
+         /* iii */
+         ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+
+         /* iv */
+         if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+           ISIS_SET_FLAG (lsp->SSNflags, circuit);
+         /* FIXME: v) */
+       }
+      /* 7.3.15.1 e) 2) LSP equal to the one in db */
+      else if (comp == LSP_EQUAL)
+       {
+         ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+         lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
+         if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+           ISIS_SET_FLAG (lsp->SSNflags, circuit);
+       }
+      /* 7.3.15.1 e) 3) LSP older than the one in db */
+      else
+       {
+         ISIS_SET_FLAG (lsp->SRMflags, circuit);
+         ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
+       }
+    }
+  return retval;
+}
+
+/*
+ * Process Sequence Numbers
+ * ISO - 10589
+ * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
+ */
+
+static int
+process_snp (int snp_type, int level, struct isis_circuit *circuit,
+            const u_char *ssnpa)
+{
+  int retval = ISIS_OK;
+  int cmp, own_lsp;
+  char typechar = ' ';
+  uint16_t pdu_len;
+  struct isis_adjacency *adj;
+  struct isis_complete_seqnum_hdr *chdr = NULL;
+  struct isis_partial_seqnum_hdr *phdr = NULL;
+  uint32_t found = 0, expected = 0, auth_tlv_offset = 0;
+  struct isis_lsp *lsp;
+  struct lsp_entry *entry;
+  struct listnode *node, *nnode;
+  struct listnode *node2, *nnode2;
+  struct tlvs tlvs;
+  struct list *lsp_list = NULL;
+  struct isis_passwd *passwd;
+
+  if (snp_type == ISIS_SNP_CSNP_FLAG)
+    {
+      /* getting the header info */
+      typechar = 'C';
+      chdr =
+       (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream);
+      stream_forward_getp (circuit->rcv_stream, ISIS_CSNP_HDRLEN);
+      pdu_len = ntohs (chdr->pdu_len);
+      if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_CSNP_HDRLEN) ||
+          pdu_len > ISO_MTU(circuit) ||
+          pdu_len > stream_get_endp (circuit->rcv_stream))
+       {
+         zlog_warn ("Received a CSNP with bogus length %d", pdu_len);
+         return ISIS_WARNING;
+       }
+    }
+  else
+    {
+      typechar = 'P';
+      phdr =
+       (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream);
+      stream_forward_getp (circuit->rcv_stream, ISIS_PSNP_HDRLEN);
+      pdu_len = ntohs (phdr->pdu_len);
+      if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_PSNP_HDRLEN) ||
+          pdu_len > ISO_MTU(circuit) ||
+          pdu_len > stream_get_endp (circuit->rcv_stream))
+       {
+         zlog_warn ("Received a PSNP with bogus length %d", pdu_len);
+         return ISIS_WARNING;
+       }
+    }
+
+  /*
+   * Set the stream endp to PDU length, ignoring additional padding
+   * introduced by transport chips.
+   */
+  if (pdu_len < stream_get_endp (circuit->rcv_stream))
+    stream_set_endp (circuit->rcv_stream, pdu_len);
+
+  /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
+  if (circuit->ext_domain)
+    {
+
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
+                 "skipping: circuit externalDomain = true",
+                 circuit->area->area_tag,
+                 level, typechar, circuit->interface->name);
+
+      return ISIS_OK;
+    }
+
+  /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
+  if (!accept_level (level, circuit->is_type))
+    {
+
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
+                 "skipping: circuit type %s does not match level %d",
+                 circuit->area->area_tag,
+                 level,
+                 typechar,
+                 circuit->interface->name,
+                 circuit_t2string (circuit->is_type), level);
+
+      return ISIS_OK;
+    }
+
+  /* 7.3.15.2 a) 4 - not applicable for CSNP  only PSNPs on broadcast */
+  if ((snp_type == ISIS_SNP_PSNP_FLAG) &&
+      (circuit->circ_type == CIRCUIT_T_BROADCAST) &&
+      (!circuit->u.bc.is_dr[level - 1]))
+    {
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, "
+                  "skipping: we are not the DIS",
+                  circuit->area->area_tag,
+                  level,
+                  typechar, snpa_print (ssnpa), circuit->interface->name);
+
+      return ISIS_OK;
+    }
+
+  /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */
+
+  /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use 3
+   * - already checked */
+
+  /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same level  */
+  /* for broadcast circuits, snpa should be compared */
+  /* FIXME : Do we need to check SNPA? */
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      if (snp_type == ISIS_SNP_CSNP_FLAG)
+       {
+         adj =
+           isis_adj_lookup (chdr->source_id, circuit->u.bc.adjdb[level - 1]);
+       }
+      else
+       {
+         /* a psnp on a broadcast, how lovely of Juniper :) */
+         adj =
+           isis_adj_lookup (phdr->source_id, circuit->u.bc.adjdb[level - 1]);
+       }
+      if (!adj)
+       return ISIS_OK;         /* Silently discard */
+    }
+  else
+    {
+      if (!circuit->u.p2p.neighbor)
+      {
+        zlog_warn ("no p2p neighbor on circuit %s", circuit->interface->name);
+        return ISIS_OK;                /* Silently discard */
+      }
+    }
+
+  /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented  */
+
+  /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented  */
+
+  memset (&tlvs, 0, sizeof (struct tlvs));
+
+  /* parse the SNP */
+  expected |= TLVFLAG_LSP_ENTRIES;
+  expected |= TLVFLAG_AUTH_INFO;
+
+  auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
+  retval = parse_tlvs (circuit->area->area_tag,
+                      STREAM_PNT (circuit->rcv_stream),
+                      pdu_len - stream_get_getp (circuit->rcv_stream),
+                      &expected, &found, &tlvs, &auth_tlv_offset);
+
+  if (retval > ISIS_WARNING)
+    {
+      zlog_warn ("something went very wrong processing SNP");
+      free_tlvs (&tlvs);
+      return retval;
+    }
+
+  if (level == IS_LEVEL_1)
+    passwd = &circuit->area->area_passwd;
+  else
+    passwd = &circuit->area->domain_passwd;
+
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV))
+    {
+      if (passwd->type)
+        {
+          if (!(found & TLVFLAG_AUTH_INFO) ||
+              authentication_check (&tlvs.auth_info, passwd,
+                                    circuit->rcv_stream, auth_tlv_offset))
+            {
+              isis_event_auth_failure (circuit->area->area_tag,
+                                       "SNP authentication" " failure",
+                                       phdr ? phdr->source_id :
+                                       chdr->source_id);
+              free_tlvs (&tlvs);
+              return ISIS_OK;
+            }
+        }
+    }
+
+  /* debug isis snp-packets */
+  if (isis->debugs & DEBUG_SNP_PACKETS)
+    {
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
+                 circuit->area->area_tag,
+                 level,
+                 typechar, snpa_print (ssnpa), circuit->interface->name);
+      if (tlvs.lsp_entries)
+       {
+         for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry))
+         {
+           zlog_debug ("ISIS-Snp (%s):         %cSNP entry %s, seq 0x%08x,"
+                       " cksum 0x%04x, lifetime %us",
+                       circuit->area->area_tag,
+                       typechar,
+                       rawlspid_print (entry->lsp_id),
+                       ntohl (entry->seq_num),
+                       ntohs (entry->checksum), ntohs (entry->rem_lifetime));
+         }
+       }
+    }
+
+  /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
+  if (tlvs.lsp_entries)
+    {
+      for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry))
+      {
+       lsp = lsp_search (entry->lsp_id, circuit->area->lspdb[level - 1]);
+       own_lsp = !memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+       if (lsp)
+         {
+           /* 7.3.15.2 b) 1) is this LSP newer */
+           cmp = lsp_compare (circuit->area->area_tag, lsp, entry->seq_num,
+                              entry->checksum, entry->rem_lifetime);
+           /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */
+           if (cmp == LSP_EQUAL)
+             {
+               /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */
+               ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+             }
+           /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */
+           else if (cmp == LSP_OLDER)
+             {
+               ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
+               ISIS_SET_FLAG (lsp->SRMflags, circuit);
+             }
+           /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM on p2p */
+           else
+             {
+               if (own_lsp)
+                 {
+                   lsp_inc_seqnum (lsp, ntohl (entry->seq_num));
+                   ISIS_SET_FLAG (lsp->SRMflags, circuit);
+                 }
+               else
+                 {
+                   ISIS_SET_FLAG (lsp->SSNflags, circuit);
+                   /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */
+                   ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+                 }
+             }
+         }
+       else
+         {
+           /* 7.3.15.2 b) 5) if it was not found, and all of those are not 0, 
+            * insert it and set SSN on it */
+           if (entry->rem_lifetime && entry->checksum && entry->seq_num &&
+               memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN))
+             {
+               lsp = lsp_new(circuit->area, entry->lsp_id,
+                             ntohs(entry->rem_lifetime),
+                             0, 0, entry->checksum, level);
+               lsp_insert (lsp, circuit->area->lspdb[level - 1]);
+               ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags);
+               ISIS_SET_FLAG (lsp->SSNflags, circuit);
+             }
+         }
+      }
+    }
+
+  /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported */
+  if (snp_type == ISIS_SNP_CSNP_FLAG)
+    {
+      /*
+       * Build a list from our own LSP db bounded with
+       * start_lsp_id and stop_lsp_id
+       */
+      lsp_list = list_new ();
+      lsp_build_list_nonzero_ht (chdr->start_lsp_id, chdr->stop_lsp_id,
+                                lsp_list, circuit->area->lspdb[level - 1]);
+
+      /* Fixme: Find a better solution */
+      if (tlvs.lsp_entries)
+       {
+         for (ALL_LIST_ELEMENTS (tlvs.lsp_entries, node, nnode, entry))
+         {
+           for (ALL_LIST_ELEMENTS (lsp_list, node2, nnode2, lsp))
+           {
+             if (lsp_id_cmp (lsp->lsp_header->lsp_id, entry->lsp_id) == 0)
+               {
+                 list_delete_node (lsp_list, node2);
+                 break;
+               }
+           }
+         }
+       }
+      /* on remaining LSPs we set SRM (neighbor knew not of) */
+      for (ALL_LIST_ELEMENTS_RO (lsp_list, node, lsp))
+       ISIS_SET_FLAG (lsp->SRMflags, circuit);
+      /* lets free it */
+      list_delete (lsp_list);
+
+    }
+
+  free_tlvs (&tlvs);
+  return retval;
+}
+
+static int
+process_csnp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
+{
+  if (isis->debugs & DEBUG_SNP_PACKETS)
+    {
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u",
+                  circuit->area->area_tag, level, circuit->interface->name,
+                  circuit_t2string (circuit->is_type), circuit->circuit_id);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
+                        stream_get_endp (circuit->rcv_stream));
+    }
+
+  /* Sanity check - FIXME: move to correct place */
+  if ((stream_get_endp (circuit->rcv_stream) -
+       stream_get_getp (circuit->rcv_stream)) < ISIS_CSNP_HDRLEN)
+    {
+      zlog_warn ("Packet too short ( < %d)", ISIS_CSNP_HDRLEN);
+      return ISIS_WARNING;
+    }
+
+  return process_snp (ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa);
+}
+
+static int
+process_psnp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
+{
+  if (isis->debugs & DEBUG_SNP_PACKETS)
+    {
+      zlog_debug ("ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u",
+                  circuit->area->area_tag, level, circuit->interface->name,
+                  circuit_t2string (circuit->is_type), circuit->circuit_id);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
+                        stream_get_endp (circuit->rcv_stream));
+    }
+
+  if ((stream_get_endp (circuit->rcv_stream) -
+       stream_get_getp (circuit->rcv_stream)) < ISIS_PSNP_HDRLEN)
+    {
+      zlog_warn ("Packet too short ( < %d)", ISIS_PSNP_HDRLEN);
+      return ISIS_WARNING;
+    }
+
+  return process_snp (ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa);
+}
+
+/*
+ * PDU Dispatcher
+ */
+
+static int
+isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
+{
+  struct isis_fixed_hdr *hdr;
+
+  int retval = ISIS_OK;
+
+  /*
+   * Let's first read data from stream to the header
+   */
+  hdr = (struct isis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream);
+
+  if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS))
+    {
+      zlog_err ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp);
+      return ISIS_ERROR;
+    }
+
+  /* now we need to know if this is an ISO 9542 packet and
+   * take real good care of it, waaa!
+   */
+  if (hdr->idrp == ISO9542_ESIS)
+    {
+      zlog_err ("No support for ES-IS packet IDRP=%02x", hdr->idrp);
+      return ISIS_ERROR;
+    }
+  stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN);
+
+  /*
+   * and then process it
+   */
+
+  if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN)
+    {
+      zlog_err ("Fixed header length = %d", hdr->length);
+      return ISIS_ERROR;
+    }
+
+  if (hdr->version1 != 1)
+    {
+      zlog_warn ("Unsupported ISIS version %u", hdr->version1);
+      return ISIS_WARNING;
+    }
+  /* either 6 or 0 */
+  if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN))
+    {
+      zlog_err
+       ("IDFieldLengthMismatch: ID Length field in a received PDU  %u, "
+        "while the parameter for this IS is %u", hdr->id_len,
+        ISIS_SYS_ID_LEN);
+      return ISIS_ERROR;
+    }
+
+  if (hdr->version2 != 1)
+    {
+      zlog_warn ("Unsupported ISIS version %u", hdr->version2);
+      return ISIS_WARNING;
+    }
+
+  if (circuit->is_passive)
+    {
+      zlog_warn ("Received ISIS PDU on passive circuit %s",
+                circuit->interface->name);
+      return ISIS_WARNING;
+    }
+
+  /* either 3 or 0 */
+  if ((hdr->max_area_addrs != 0)
+      && (hdr->max_area_addrs != isis->max_area_addrs))
+    {
+      zlog_err ("maximumAreaAddressesMismatch: maximumAreaAdresses in a "
+               "received PDU %u while the parameter for this IS is %u",
+               hdr->max_area_addrs, isis->max_area_addrs);
+      return ISIS_ERROR;
+    }
+
+  switch (hdr->pdu_type)
+    {
+    case L1_LAN_HELLO:
+      retval = process_lan_hello (ISIS_LEVEL1, circuit, ssnpa);
+      break;
+    case L2_LAN_HELLO:
+      retval = process_lan_hello (ISIS_LEVEL2, circuit, ssnpa);
+      break;
+    case P2P_HELLO:
+      retval = process_p2p_hello (circuit);
+      break;
+    case L1_LINK_STATE:
+      retval = process_lsp (ISIS_LEVEL1, circuit, ssnpa);
+      break;
+    case L2_LINK_STATE:
+      retval = process_lsp (ISIS_LEVEL2, circuit, ssnpa);
+      break;
+    case L1_COMPLETE_SEQ_NUM:
+      retval = process_csnp (ISIS_LEVEL1, circuit, ssnpa);
+      break;
+    case L2_COMPLETE_SEQ_NUM:
+      retval = process_csnp (ISIS_LEVEL2, circuit, ssnpa);
+      break;
+    case L1_PARTIAL_SEQ_NUM:
+      retval = process_psnp (ISIS_LEVEL1, circuit, ssnpa);
+      break;
+    case L2_PARTIAL_SEQ_NUM:
+      retval = process_psnp (ISIS_LEVEL2, circuit, ssnpa);
+      break;
+    default:
+      return ISIS_ERROR;
+    }
+
+  return retval;
+}
+
+#ifdef GNU_LINUX
+int
+isis_receive (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  u_char ssnpa[ETH_ALEN];
+  int retval;
+
+  /*
+   * Get the circuit 
+   */
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  isis_circuit_stream(circuit, &circuit->rcv_stream);
+
+  retval = circuit->rx (circuit, ssnpa);
+  circuit->t_read = NULL;
+
+  if (retval == ISIS_OK)
+    retval = isis_handle_pdu (circuit, ssnpa);
+
+  /* 
+   * prepare for next packet. 
+   */
+  if (!circuit->is_passive)
+  {
+    THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit,
+                    circuit->fd);
+  }
+
+  return retval;
+}
+
+#else
+int
+isis_receive (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  u_char ssnpa[ETH_ALEN];
+  int retval;
+
+  /*
+   * Get the circuit 
+   */
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  circuit->t_read = NULL;
+
+  isis_circuit_stream(circuit, &circuit->rcv_stream);
+
+  retval = circuit->rx (circuit, ssnpa);
+
+  if (retval == ISIS_OK)
+    retval = isis_handle_pdu (circuit, ssnpa);
+
+  /* 
+   * prepare for next packet. 
+   */
+  if (!circuit->is_passive)
+  {
+    circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit,
+                                            listcount
+                                            (circuit->area->circuit_list) *
+                                            100);
+  }
+
+  return retval;
+}
+
+#endif
+
+ /* filling of the fixed isis header */
+void
+fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type)
+{
+  memset (hdr, 0, sizeof (struct isis_fixed_hdr));
+
+  hdr->idrp = ISO10589_ISIS;
+
+  switch (pdu_type)
+    {
+    case L1_LAN_HELLO:
+    case L2_LAN_HELLO:
+      hdr->length = ISIS_LANHELLO_HDRLEN;
+      break;
+    case P2P_HELLO:
+      hdr->length = ISIS_P2PHELLO_HDRLEN;
+      break;
+    case L1_LINK_STATE:
+    case L2_LINK_STATE:
+      hdr->length = ISIS_LSP_HDR_LEN;
+      break;
+    case L1_COMPLETE_SEQ_NUM:
+    case L2_COMPLETE_SEQ_NUM:
+      hdr->length = ISIS_CSNP_HDRLEN;
+      break;
+    case L1_PARTIAL_SEQ_NUM:
+    case L2_PARTIAL_SEQ_NUM:
+      hdr->length = ISIS_PSNP_HDRLEN;
+      break;
+    default:
+      zlog_warn ("fill_fixed_hdr(): unknown pdu type %d", pdu_type);
+      return;
+    }
+  hdr->length += ISIS_FIXED_HDR_LEN;
+  hdr->pdu_type = pdu_type;
+  hdr->version1 = 1;
+  hdr->id_len = 0;             /* ISIS_SYS_ID_LEN -  0==6 */
+  hdr->version2 = 1;
+  hdr->max_area_addrs = 0;     /* isis->max_area_addrs -  0==3 */
+}
+
+/*
+ * SEND SIDE                             
+ */
+static void
+fill_fixed_hdr_andstream (struct isis_fixed_hdr *hdr, u_char pdu_type,
+                         struct stream *stream)
+{
+  fill_fixed_hdr (hdr, pdu_type);
+
+  stream_putc (stream, hdr->idrp);
+  stream_putc (stream, hdr->length);
+  stream_putc (stream, hdr->version1);
+  stream_putc (stream, hdr->id_len);
+  stream_putc (stream, hdr->pdu_type);
+  stream_putc (stream, hdr->version2);
+  stream_putc (stream, hdr->reserved);
+  stream_putc (stream, hdr->max_area_addrs);
+
+  return;
+}
+
+int
+send_hello (struct isis_circuit *circuit, int level)
+{
+  struct isis_fixed_hdr fixed_hdr;
+  struct isis_lan_hello_hdr hello_hdr;
+  struct isis_p2p_hello_hdr p2p_hello_hdr;
+  unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
+  size_t len_pointer, length, auth_tlv_offset = 0;
+  u_int32_t interval;
+  int retval;
+
+  if (circuit->is_passive)
+    return ISIS_OK;
+
+  if (circuit->interface->mtu == 0)
+    {
+      zlog_warn ("circuit has zero MTU");
+      return ISIS_WARNING;
+    }
+
+  isis_circuit_stream(circuit, &circuit->snd_stream);
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    if (level == IS_LEVEL_1)
+      fill_fixed_hdr_andstream (&fixed_hdr, L1_LAN_HELLO,
+                               circuit->snd_stream);
+    else
+      fill_fixed_hdr_andstream (&fixed_hdr, L2_LAN_HELLO,
+                               circuit->snd_stream);
+  else
+    fill_fixed_hdr_andstream (&fixed_hdr, P2P_HELLO, circuit->snd_stream);
+
+  /*
+   * Fill LAN Level 1 or 2 Hello PDU header
+   */
+  memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr));
+  interval = circuit->hello_multiplier[level - 1] *
+    circuit->hello_interval[level - 1];
+  if (interval > USHRT_MAX)
+    interval = USHRT_MAX;
+  hello_hdr.circuit_t = circuit->is_type;
+  memcpy (hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN);
+  hello_hdr.hold_time = htons ((u_int16_t) interval);
+
+  hello_hdr.pdu_len = 0;       /* Update the PDU Length later */
+  len_pointer = stream_get_endp (circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN;
+
+  /* copy the shared part of the hello to the p2p hello if needed */
+  if (circuit->circ_type == CIRCUIT_T_P2P)
+    {
+      memcpy (&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN);
+      p2p_hello_hdr.local_id = circuit->circuit_id;
+      /* FIXME: need better understanding */
+      stream_put (circuit->snd_stream, &p2p_hello_hdr, ISIS_P2PHELLO_HDRLEN);
+    }
+  else
+    {
+      hello_hdr.prio = circuit->priority[level - 1];
+      if (level == IS_LEVEL_1)
+       {
+         memcpy (hello_hdr.lan_id, circuit->u.bc.l1_desig_is,
+                 ISIS_SYS_ID_LEN + 1);
+       }
+      else if (level == IS_LEVEL_2)
+       {
+         memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is,
+                 ISIS_SYS_ID_LEN + 1);
+       }
+      stream_put (circuit->snd_stream, &hello_hdr, ISIS_LANHELLO_HDRLEN);
+    }
+
+  /*
+   * Then the variable length part.
+   */
+
+  /* add circuit password */
+  switch (circuit->passwd.type)
+  {
+    /* Cleartext */
+    case ISIS_PASSWD_TYPE_CLEARTXT:
+      if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len,
+                            circuit->passwd.passwd, circuit->snd_stream))
+        return ISIS_WARNING;
+      break;
+
+    /* HMAC MD5 */
+    case ISIS_PASSWD_TYPE_HMAC_MD5:
+      /* Remember where TLV is written so we can later overwrite the MD5 hash */
+      auth_tlv_offset = stream_get_endp (circuit->snd_stream);
+      memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+      if (tlv_add_authinfo (circuit->passwd.type, ISIS_AUTH_MD5_SIZE,
+                            hmac_md5_hash, circuit->snd_stream))
+        return ISIS_WARNING;
+      break;
+
+    default:
+      break;
+  }
+
+  /*  Area Addresses TLV */
+  if (listcount (circuit->area->area_addrs) == 0)
+    return ISIS_WARNING;
+  if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream))
+    return ISIS_WARNING;
+
+  /*  LAN Neighbors TLV */
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+    {
+      if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0] &&
+          listcount (circuit->u.bc.lan_neighs[0]) > 0)
+       if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0],
+                               circuit->snd_stream))
+         return ISIS_WARNING;
+      if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1] &&
+          listcount (circuit->u.bc.lan_neighs[1]) > 0)
+       if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1],
+                               circuit->snd_stream))
+         return ISIS_WARNING;
+    }
+
+  /* Protocols Supported TLV */
+  if (circuit->nlpids.count > 0)
+    if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream))
+      return ISIS_WARNING;
+  /* IP interface Address TLV */
+  if (circuit->ip_router && circuit->ip_addrs &&
+      listcount (circuit->ip_addrs) > 0)
+    if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream))
+      return ISIS_WARNING;
+
+#ifdef HAVE_IPV6
+  /* IPv6 Interface Address TLV */
+  if (circuit->ipv6_router && circuit->ipv6_link &&
+      listcount (circuit->ipv6_link) > 0)
+    if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream))
+      return ISIS_WARNING;
+#endif /* HAVE_IPV6 */
+
+  if (circuit->pad_hellos)
+    if (tlv_add_padding (circuit->snd_stream))
+      return ISIS_WARNING;
+
+  length = stream_get_endp (circuit->snd_stream);
+  /* Update PDU length */
+  stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length);
+
+  /* For HMAC MD5 we need to compute the md5 hash and store it */
+  if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+    {
+      hmac_md5 (STREAM_DATA (circuit->snd_stream),
+                stream_get_endp (circuit->snd_stream),
+                (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len,
+                (unsigned char *) &hmac_md5_hash);
+      /* Copy the hash into the stream */
+      memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
+              hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
+    }
+
+  if (isis->debugs & DEBUG_ADJ_PACKETS)
+    {
+      if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+       {
+         zlog_debug ("ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
+                     circuit->area->area_tag, level, circuit->interface->name,
+                     length);
+       }
+      else
+       {
+         zlog_debug ("ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
+                     circuit->area->area_tag, circuit->interface->name,
+                     length);
+       }
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->snd_stream),
+                        stream_get_endp (circuit->snd_stream));
+    }
+
+  retval = circuit->tx (circuit, level);
+  if (retval != ISIS_OK)
+    zlog_err ("ISIS-Adj (%s): Send L%d IIH on %s failed",
+              circuit->area->area_tag, level, circuit->interface->name);
+
+  return retval;
+}
+
+int
+send_lan_l1_hello (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  int retval;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+  circuit->u.bc.t_send_lan_hello[0] = NULL;
+
+  if (!(circuit->area->is_type & IS_LEVEL_1))
+    {
+      zlog_warn ("ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
+                circuit->area->area_tag);
+      return 1;
+    }
+
+  if (circuit->u.bc.run_dr_elect[0])
+    retval = isis_dr_elect (circuit, 1);
+
+  retval = send_hello (circuit, 1);
+
+  /* set next timer thread */
+  THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0],
+                  send_lan_l1_hello, circuit,
+                  isis_jitter (circuit->hello_interval[0], IIH_JITTER));
+
+  return retval;
+}
+
+int
+send_lan_l2_hello (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  int retval;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+  circuit->u.bc.t_send_lan_hello[1] = NULL;
+
+  if (!(circuit->area->is_type & IS_LEVEL_2))
+    {
+      zlog_warn ("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
+                circuit->area->area_tag);
+      return 1;
+    }
+
+  if (circuit->u.bc.run_dr_elect[1])
+    retval = isis_dr_elect (circuit, 2);
+
+  retval = send_hello (circuit, 2);
+
+  /* set next timer thread */
+  THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1],
+                  send_lan_l2_hello, circuit,
+                  isis_jitter (circuit->hello_interval[1], IIH_JITTER));
+
+  return retval;
+}
+
+int
+send_p2p_hello (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+  circuit->u.p2p.t_send_p2p_hello = NULL;
+
+  send_hello (circuit, 1);
+
+  /* set next timer thread */
+  THREAD_TIMER_ON (master, circuit->u.p2p.t_send_p2p_hello, send_p2p_hello,
+                  circuit, isis_jitter (circuit->hello_interval[1],
+                                        IIH_JITTER));
+
+  return ISIS_OK;
+}
+
+static int
+build_csnp (int level, u_char * start, u_char * stop, struct list *lsps,
+           struct isis_circuit *circuit)
+{
+  struct isis_fixed_hdr fixed_hdr;
+  struct isis_passwd *passwd;
+  unsigned long lenp;
+  u_int16_t length;
+  unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
+  unsigned long auth_tlv_offset = 0;
+  int retval = ISIS_OK;
+
+  isis_circuit_stream(circuit, &circuit->snd_stream);
+
+  if (level == IS_LEVEL_1)
+    fill_fixed_hdr_andstream (&fixed_hdr, L1_COMPLETE_SEQ_NUM,
+                             circuit->snd_stream);
+  else
+    fill_fixed_hdr_andstream (&fixed_hdr, L2_COMPLETE_SEQ_NUM,
+                             circuit->snd_stream);
+
+  /*
+   * Fill Level 1 or 2 Complete Sequence Numbers header
+   */
+
+  lenp = stream_get_endp (circuit->snd_stream);
+  stream_putw (circuit->snd_stream, 0);        /* PDU length - when we know it */
+  /* no need to send the source here, it is always us if we csnp */
+  stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
+  /* with zero circuit id - ref 9.10, 9.11 */
+  stream_putc (circuit->snd_stream, 0x00);
+
+  stream_put (circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2);
+  stream_put (circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2);
+
+  /*
+   * And TLVs
+   */
+  if (level == IS_LEVEL_1)
+    passwd = &circuit->area->area_passwd;
+  else
+    passwd = &circuit->area->domain_passwd;
+
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
+  {
+    switch (passwd->type)
+    {
+      /* Cleartext */
+      case ISIS_PASSWD_TYPE_CLEARTXT:
+        if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len,
+                              passwd->passwd, circuit->snd_stream))
+          return ISIS_WARNING;
+        break;
+
+        /* HMAC MD5 */
+      case ISIS_PASSWD_TYPE_HMAC_MD5:
+        /* Remember where TLV is written so we can later overwrite the MD5 hash */
+        auth_tlv_offset = stream_get_endp (circuit->snd_stream);
+        memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+        if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
+                              hmac_md5_hash, circuit->snd_stream))
+          return ISIS_WARNING;
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  retval = tlv_add_lsp_entries (lsps, circuit->snd_stream);
+  if (retval != ISIS_OK)
+    return retval;
+
+  length = (u_int16_t) stream_get_endp (circuit->snd_stream);
+  /* Update PU length */
+  stream_putw_at (circuit->snd_stream, lenp, length);
+
+  /* For HMAC MD5 we need to compute the md5 hash and store it */
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) &&
+      passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5)
+    {
+      hmac_md5 (STREAM_DATA (circuit->snd_stream),
+                stream_get_endp(circuit->snd_stream),
+                (unsigned char *) &passwd->passwd, passwd->len,
+                (unsigned char *) &hmac_md5_hash);
+      /* Copy the hash into the stream */
+      memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
+              hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
+    }
+
+  return retval;
+}
+
+/*
+ * Count the maximum number of lsps that can be accomodated by a given size.
+ */
+static uint16_t
+get_max_lsp_count (uint16_t size)
+{
+  uint16_t tlv_count;
+  uint16_t lsp_count;
+  uint16_t remaining_size;
+
+  /* First count the full size TLVs */
+  tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE;
+  lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN);
+
+  /* The last TLV, if any */
+  remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE;
+  if (remaining_size - 2 >= LSP_ENTRIES_LEN)
+    lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN;
+
+  return lsp_count;
+}
+
+/*
+ * Calculate the length of Authentication Info. TLV.
+ */
+static uint16_t
+auth_tlv_length (int level, struct isis_circuit *circuit)
+{
+  struct isis_passwd *passwd;
+  uint16_t length;
+
+  if (level == IS_LEVEL_1)
+    passwd = &circuit->area->area_passwd;
+  else
+    passwd = &circuit->area->domain_passwd;
+
+  /* Also include the length of TLV header */
+  length = AUTH_INFO_HDRLEN;
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
+  {
+    switch (passwd->type)
+    {
+      /* Cleartext */
+      case ISIS_PASSWD_TYPE_CLEARTXT:
+        length += passwd->len;
+        break;
+
+        /* HMAC MD5 */
+      case ISIS_PASSWD_TYPE_HMAC_MD5:
+        length += ISIS_AUTH_MD5_SIZE;
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  return length;
+}
+
+/*
+ * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
+ */
+static uint16_t
+max_lsps_per_snp (int snp_type, int level, struct isis_circuit *circuit)
+{
+  int snp_hdr_len;
+  int auth_tlv_len;
+  uint16_t lsp_count;
+
+  snp_hdr_len = ISIS_FIXED_HDR_LEN;
+  if (snp_type == ISIS_SNP_CSNP_FLAG)
+    snp_hdr_len += ISIS_CSNP_HDRLEN;
+  else
+    snp_hdr_len += ISIS_PSNP_HDRLEN;
+
+  auth_tlv_len = auth_tlv_length (level, circuit);
+  lsp_count = get_max_lsp_count (
+      stream_get_size (circuit->snd_stream) - snp_hdr_len - auth_tlv_len);
+  return lsp_count;
+}
+
+/*
+ * FIXME: support multiple CSNPs
+ */
+
+int
+send_csnp (struct isis_circuit *circuit, int level)
+{
+  u_char start[ISIS_SYS_ID_LEN + 2];
+  u_char stop[ISIS_SYS_ID_LEN + 2];
+  struct list *list = NULL;
+  struct listnode *node;
+  struct isis_lsp *lsp;
+  u_char num_lsps, loop = 1;
+  int i, retval = ISIS_OK;
+
+  if (circuit->area->lspdb[level - 1] == NULL ||
+      dict_count (circuit->area->lspdb[level - 1]) == 0)
+    return retval;
+
+  memset (start, 0x00, ISIS_SYS_ID_LEN + 2);
+  memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
+
+  num_lsps = max_lsps_per_snp (ISIS_SNP_CSNP_FLAG, level, circuit);
+
+  while (loop)
+    {
+      list = list_new ();
+      lsp_build_list (start, stop, num_lsps, list,
+                      circuit->area->lspdb[level - 1]);
+      /*
+       * Update the stop lsp_id before encoding this CSNP.
+       */
+      if (listcount (list) < num_lsps)
+        {
+          memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
+        }
+      else
+        {
+          node = listtail (list);
+          lsp = listgetdata (node);
+          memcpy (stop, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2);
+        }
+
+      retval = build_csnp (level, start, stop, list, circuit);
+      if (retval != ISIS_OK)
+        {
+          zlog_err ("ISIS-Snp (%s): Build L%d CSNP on %s failed",
+                    circuit->area->area_tag, level, circuit->interface->name);
+          list_delete (list);
+          return retval;
+        }
+
+      if (isis->debugs & DEBUG_SNP_PACKETS)
+        {
+          zlog_debug ("ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
+                      circuit->area->area_tag, level, circuit->interface->name,
+                      stream_get_endp (circuit->snd_stream));
+          for (ALL_LIST_ELEMENTS_RO (list, node, lsp))
+            {
+              zlog_debug ("ISIS-Snp (%s):         CSNP entry %s, seq 0x%08x,"
+                          " cksum 0x%04x, lifetime %us",
+                          circuit->area->area_tag,
+                          rawlspid_print (lsp->lsp_header->lsp_id),
+                          ntohl (lsp->lsp_header->seq_num),
+                          ntohs (lsp->lsp_header->checksum),
+                          ntohs (lsp->lsp_header->rem_lifetime));
+            }
+          if (isis->debugs & DEBUG_PACKET_DUMP)
+            zlog_dump_data (STREAM_DATA (circuit->snd_stream),
+                            stream_get_endp (circuit->snd_stream));
+        }
+
+      retval = circuit->tx (circuit, level);
+      if (retval != ISIS_OK)
+        {
+          zlog_err ("ISIS-Snp (%s): Send L%d CSNP on %s failed",
+                    circuit->area->area_tag, level,
+                    circuit->interface->name);
+          list_delete (list);
+          return retval;
+        }
+
+      /*
+       * Start lsp_id of the next CSNP should be one plus the
+       * stop lsp_id in this current CSNP.
+       */
+      memcpy (start, stop, ISIS_SYS_ID_LEN + 2);
+      loop = 0;
+      for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i)
+        {
+          if (start[i] < (u_char)0xff)
+            {
+              start[i] += 1;
+              loop = 1;
+              break;
+            }
+        }
+      memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
+      list_delete (list);
+    }
+
+  return retval;
+}
+
+int
+send_l1_csnp (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  int retval = ISIS_OK;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  circuit->t_send_csnp[0] = NULL;
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[0])
+    {
+      send_csnp (circuit, 1);
+    }
+  /* 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));
+
+  return retval;
+}
+
+int
+send_l2_csnp (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  int retval = ISIS_OK;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  circuit->t_send_csnp[1] = NULL;
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[1])
+    {
+      send_csnp (circuit, 2);
+    }
+  /* 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));
+
+  return retval;
+}
+
+static int
+build_psnp (int level, struct isis_circuit *circuit, struct list *lsps)
+{
+  struct isis_fixed_hdr fixed_hdr;
+  unsigned long lenp;
+  u_int16_t length;
+  struct isis_lsp *lsp;
+  struct isis_passwd *passwd;
+  struct listnode *node;
+  unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
+  unsigned long auth_tlv_offset = 0;
+  int retval = ISIS_OK;
+
+  isis_circuit_stream(circuit, &circuit->snd_stream);
+
+  if (level == IS_LEVEL_1)
+    fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM,
+                             circuit->snd_stream);
+  else
+    fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM,
+                             circuit->snd_stream);
+
+  /*
+   * Fill Level 1 or 2 Partial Sequence Numbers header
+   */
+  lenp = stream_get_endp (circuit->snd_stream);
+  stream_putw (circuit->snd_stream, 0);        /* PDU length - when we know it */
+  stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
+  stream_putc (circuit->snd_stream, circuit->idx);
+
+  /*
+   * And TLVs
+   */
+
+  if (level == IS_LEVEL_1)
+    passwd = &circuit->area->area_passwd;
+  else
+    passwd = &circuit->area->domain_passwd;
+
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
+  {
+    switch (passwd->type)
+    {
+      /* Cleartext */
+      case ISIS_PASSWD_TYPE_CLEARTXT:
+        if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len,
+                              passwd->passwd, circuit->snd_stream))
+          return ISIS_WARNING;
+        break;
+
+        /* HMAC MD5 */
+      case ISIS_PASSWD_TYPE_HMAC_MD5:
+        /* Remember where TLV is written so we can later overwrite the MD5 hash */
+        auth_tlv_offset = stream_get_endp (circuit->snd_stream);
+        memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+        if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
+                              hmac_md5_hash, circuit->snd_stream))
+          return ISIS_WARNING;
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  retval = tlv_add_lsp_entries (lsps, circuit->snd_stream);
+  if (retval != ISIS_OK)
+    return retval;
+
+  if (isis->debugs & DEBUG_SNP_PACKETS)
+    {
+      for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp))
+      {
+       zlog_debug ("ISIS-Snp (%s):         PSNP entry %s, seq 0x%08x,"
+                   " cksum 0x%04x, lifetime %us",
+                   circuit->area->area_tag,
+                   rawlspid_print (lsp->lsp_header->lsp_id),
+                   ntohl (lsp->lsp_header->seq_num),
+                   ntohs (lsp->lsp_header->checksum),
+                   ntohs (lsp->lsp_header->rem_lifetime));
+      }
+    }
+
+  length = (u_int16_t) stream_get_endp (circuit->snd_stream);
+  /* Update PDU length */
+  stream_putw_at (circuit->snd_stream, lenp, length);
+
+  /* For HMAC MD5 we need to compute the md5 hash and store it */
+  if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) &&
+      passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5)
+    {
+      hmac_md5 (STREAM_DATA (circuit->snd_stream),
+                stream_get_endp(circuit->snd_stream),
+                (unsigned char *) &passwd->passwd, passwd->len,
+                (unsigned char *) &hmac_md5_hash);
+      /* Copy the hash into the stream */
+      memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
+              hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
+    }
+
+  return ISIS_OK;
+}
+
+/*
+ *  7.3.15.4 action on expiration of partial SNP interval
+ *  level 1
+ */
+static int
+send_psnp (int level, struct isis_circuit *circuit)
+{
+  struct isis_lsp *lsp;
+  struct list *list = NULL;
+  struct listnode *node;
+  u_char num_lsps;
+  int retval = ISIS_OK;
+
+  if (circuit->circ_type == CIRCUIT_T_BROADCAST &&
+      circuit->u.bc.is_dr[level - 1])
+    return ISIS_OK;
+
+  if (circuit->area->lspdb[level - 1] == NULL ||
+      dict_count (circuit->area->lspdb[level - 1]) == 0)
+    return ISIS_OK;
+
+  if (! circuit->snd_stream)
+    return ISIS_ERROR;
+
+  num_lsps = max_lsps_per_snp (ISIS_SNP_PSNP_FLAG, level, circuit);
+
+  while (1)
+    {
+      list = list_new ();
+      lsp_build_list_ssn (circuit, num_lsps, list,
+                          circuit->area->lspdb[level - 1]);
+
+      if (listcount (list) == 0)
+        {
+          list_delete (list);
+          return ISIS_OK;
+        }
+
+      retval = build_psnp (level, circuit, list);
+      if (retval != ISIS_OK)
+        {
+          zlog_err ("ISIS-Snp (%s): Build L%d PSNP on %s failed",
+                    circuit->area->area_tag, level, circuit->interface->name);
+          list_delete (list);
+          return retval;
+        }
+
+      if (isis->debugs & DEBUG_SNP_PACKETS)
+        {
+          zlog_debug ("ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
+                      circuit->area->area_tag, level,
+                      circuit->interface->name,
+                      stream_get_endp (circuit->snd_stream));
+          if (isis->debugs & DEBUG_PACKET_DUMP)
+            zlog_dump_data (STREAM_DATA (circuit->snd_stream),
+                            stream_get_endp (circuit->snd_stream));
+        }
+
+      retval = circuit->tx (circuit, level);
+      if (retval != ISIS_OK)
+        {
+          zlog_err ("ISIS-Snp (%s): Send L%d PSNP on %s failed",
+                    circuit->area->area_tag, level,
+                    circuit->interface->name);
+          list_delete (list);
+          return retval;
+        }
+
+      /*
+       * sending succeeded, we can clear SSN flags of this circuit
+       * for the LSPs in list
+       */
+      for (ALL_LIST_ELEMENTS_RO (list, node, lsp))
+        ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
+      list_delete (list);
+    }
+
+  return retval;
+}
+
+int
+send_l1_psnp (struct thread *thread)
+{
+
+  struct isis_circuit *circuit;
+  int retval = ISIS_OK;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  circuit->t_send_psnp[0] = NULL;
+
+  send_psnp (1, circuit);
+  /* set next timer thread */
+  THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit,
+                  isis_jitter (circuit->psnp_interval[0], PSNP_JITTER));
+
+  return retval;
+}
+
+/*
+ *  7.3.15.4 action on expiration of partial SNP interval
+ *  level 2
+ */
+int
+send_l2_psnp (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  int retval = ISIS_OK;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  circuit->t_send_psnp[1] = NULL;
+
+  send_psnp (2, circuit);
+
+  /* set next timer thread */
+  THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit,
+                  isis_jitter (circuit->psnp_interval[1], PSNP_JITTER));
+
+  return retval;
+}
+
+/*
+ * ISO 10589 - 7.3.14.3
+ */
+int
+send_lsp (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  struct isis_lsp *lsp;
+  struct listnode *node;
+  int clear_srm = 1;
+  int retval = ISIS_OK;
+
+  circuit = THREAD_ARG (thread);
+  assert (circuit);
+
+  if (!circuit->lsp_queue)
+    return ISIS_OK;
+
+  node = listhead (circuit->lsp_queue);
+
+  /*
+   * Handle case where there are no LSPs on the queue. This can
+   * happen, for instance, if an adjacency goes down before this
+   * thread gets a chance to run.
+   */
+  if (!node)
+    return ISIS_OK;
+
+  /*
+   * Delete LSP from lsp_queue. If it's still in queue, it is assumed
+   * as 'transmit pending', but send_lsp may never be called again.
+   * Retry will happen because SRM flag will not be cleared.
+   */
+  lsp = listgetdata(node);
+  list_delete_node (circuit->lsp_queue, node);
+
+  /* Set the last-cleared time if the queue is empty. */
+  /* TODO: Is is possible that new lsps keep being added to the queue
+   * that the queue is never empty? */
+  if (list_isempty (circuit->lsp_queue))
+    circuit->lsp_queue_last_cleared = time (NULL);
+
+  if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
+    goto out;
+
+  /*
+   * Do not send if levels do not match
+   */
+  if (!(lsp->level & circuit->is_type))
+    goto out;
+
+  /*
+   * Do not send if we do not have adjacencies in state up on the circuit
+   */
+  if (circuit->upadjcount[lsp->level - 1] == 0)
+    goto out;
+
+  /* stream_copy will assert and stop program execution if LSP is larger than
+   * the circuit's MTU. So handle and log this case here. */
+  if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream))
+    {
+      zlog_err("ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
+               " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
+               " while interface stream size is %zu.",
+               circuit->area->area_tag, lsp->level,
+               rawlspid_print(lsp->lsp_header->lsp_id),
+               ntohl(lsp->lsp_header->seq_num),
+               ntohs(lsp->lsp_header->checksum),
+               ntohs(lsp->lsp_header->rem_lifetime),
+               circuit->interface->name,
+               stream_get_endp(lsp->pdu),
+               stream_get_size(circuit->snd_stream));
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu));
+      retval = ISIS_ERROR;
+      goto out;
+    }
+
+  /* copy our lsp to the send buffer */
+  stream_copy (circuit->snd_stream, lsp->pdu);
+
+  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+    {
+      zlog_debug
+        ("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
+         " lifetime %us on %s", circuit->area->area_tag, lsp->level,
+         rawlspid_print (lsp->lsp_header->lsp_id),
+         ntohl (lsp->lsp_header->seq_num),
+         ntohs (lsp->lsp_header->checksum),
+         ntohs (lsp->lsp_header->rem_lifetime),
+         circuit->interface->name);
+      if (isis->debugs & DEBUG_PACKET_DUMP)
+        zlog_dump_data (STREAM_DATA (circuit->snd_stream),
+                        stream_get_endp (circuit->snd_stream));
+    }
+
+  clear_srm = 0;
+  retval = circuit->tx (circuit, lsp->level);
+  if (retval != ISIS_OK)
+    {
+      zlog_err ("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
+                circuit->area->area_tag, lsp->level,
+                circuit->interface->name,
+                (retval == ISIS_WARNING) ? "temporarily" : "permanently");
+    }
+
+out:
+  if (clear_srm
+      || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST)
+      || (retval != ISIS_OK && retval != ISIS_WARNING))
+    {
+      /* SRM flag will trigger retransmission. We will not retransmit if we
+       * encountered a fatal error.
+       * On success, they should only be cleared if it's a broadcast circuit.
+       * On a P2P circuit, we will wait for the ack from the neighbor to clear
+       * the fag.
+       */
+      ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+    }
+
+  return retval;
+}
+
+int
+ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit,
+        int level)
+{
+  unsigned long lenp;
+  int retval;
+  u_int16_t length;
+  struct isis_fixed_hdr fixed_hdr;
+
+  isis_circuit_stream(circuit, &circuit->snd_stream);
+
+  //  fill_llc_hdr (stream);
+  if (level == IS_LEVEL_1)
+    fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM,
+                             circuit->snd_stream);
+  else
+    fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM,
+                             circuit->snd_stream);
+
+
+  lenp = stream_get_endp (circuit->snd_stream);
+  stream_putw (circuit->snd_stream, 0);        /* PDU length  */
+  stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
+  stream_putc (circuit->snd_stream, circuit->idx);
+  stream_putc (circuit->snd_stream, 9);        /* code */
+  stream_putc (circuit->snd_stream, 16);       /* len */
+
+  stream_putw (circuit->snd_stream, ntohs (hdr->rem_lifetime));
+  stream_put (circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2);
+  stream_putl (circuit->snd_stream, ntohl (hdr->seq_num));
+  stream_putw (circuit->snd_stream, ntohs (hdr->checksum));
+
+  length = (u_int16_t) stream_get_endp (circuit->snd_stream);
+  /* Update PDU length */
+  stream_putw_at (circuit->snd_stream, lenp, length);
+
+  retval = circuit->tx (circuit, level);
+  if (retval != ISIS_OK)
+    zlog_err ("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
+              circuit->area->area_tag, level,
+              circuit->interface->name);
+
+  return retval;
+}
diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h
new file mode 100644 (file)
index 0000000..3eca731
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_pdu.h
+ *                             PDU processing
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_PDU_H
+#define _ZEBRA_ISIS_PDU_H
+
+#ifdef __SUNPRO_C
+#pragma pack(1)
+#endif
+
+/*
+ *                    ISO 9542 - 7.5,7.6
+ *
+ *                       ES to IS Fixed Header
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |         Intradomain Routeing Protocol Discriminator           |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                       Length Indicator                        |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                  Version/Protocol ID extension                |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                         Reserved = 0                          |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |   0   |   0   |   0   |              PDU Type                 |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                         Holding Time                          | 2
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                          Checksum                             | 2
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+
+struct esis_fixed_hdr
+{
+  u_char idrp;
+  u_char length;
+  u_char version;
+  u_char id_len;
+  u_char pdu_type;
+  u_int16_t holdtime;
+  u_int16_t checksum;
+} __attribute__ ((packed));
+
+#define ESIS_FIXED_HDR_LEN   9
+
+#define ESH_PDU              2
+#define ISH_PDU              4
+#define RD_PDU               5
+
+/*
+ *                       IS to IS Fixed Header
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |         Intradomain Routeing Protocol Discriminator           | 
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                       Length Indicator                        |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                  Version/Protocol ID extension                |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |   R   |   R   |   R   |              PDU Type                 |      
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                            Version                            |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                            Reserved                           |
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ *  |                       Maximum Area Addresses                  |      
+ *  +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+
+struct isis_fixed_hdr
+{
+  u_char idrp;
+  u_char length;
+  u_char version1;
+  u_char id_len;
+  u_char pdu_type;
+  u_char version2;
+  u_char reserved;
+  u_char max_area_addrs;
+} __attribute__ ((packed));
+
+#define ISIS_FIXED_HDR_LEN 8
+
+/*
+ * IS-IS PDU types.
+ */
+
+#define L1_LAN_HELLO         15
+#define L2_LAN_HELLO         16
+/*
+ *              L1 and L2 LAN IS to IS Hello PDU header
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                       Reserved                | Circuit Type  | 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Source ID                              + id_len   
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        Holding  Time                          | 2     
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        PDU Length                             | 2    
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |   R   |                Priority                               | 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        LAN ID                                 | id_len + 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+struct isis_lan_hello_hdr
+{
+  u_char circuit_t;
+  u_char source_id[ISIS_SYS_ID_LEN];
+  u_int16_t hold_time;
+  u_int16_t pdu_len;
+  u_char prio;
+  u_char lan_id[ISIS_SYS_ID_LEN + 1];
+} __attribute__ ((packed));
+#define ISIS_LANHELLO_HDRLEN  19
+
+#define P2P_HELLO            17
+/*
+ *           Point-to-point IS to IS hello PDU header
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        Reserved               | Circuit Type  | 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Source ID                              + id_len   
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Holding  Time                          + 2     
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        PDU Length                             + 2    
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        Local Circuit ID                       | 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+struct isis_p2p_hello_hdr
+{
+  u_char circuit_t;
+  u_char source_id[ISIS_SYS_ID_LEN];
+  u_int16_t hold_time;
+  u_int16_t pdu_len;
+  u_char local_id;
+} __attribute__ ((packed));
+#define ISIS_P2PHELLO_HDRLEN 12
+
+#define L1_LINK_STATE        18
+#define L2_LINK_STATE        20
+/*
+ *              L1 and L2 IS to IS link state PDU header
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        PDU Length                             + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Remaining Lifetime                     + 2 
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |                        LSP ID                                 | id_len + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Sequence Number                        + 4
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Checksum                               + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * |   P   |              ATT              |LSPDBOL|    ISTYPE     |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+struct isis_link_state_hdr
+{
+  u_int16_t pdu_len;
+  u_int16_t rem_lifetime;
+  u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  u_int32_t seq_num;
+  u_int16_t checksum;
+  u_int8_t lsp_bits;
+} __attribute__ ((packed));
+#define ISIS_LSP_HDR_LEN 19
+
+/*
+ * Since the length field of LSP Entries TLV is one byte long, and each LSP
+ * entry is LSP_ENTRIES_LEN (16) bytes long, the maximum number of LSP entries
+ * can be accomodated in a TLV is
+ * 255 / 16 = 15.
+ * 
+ * Therefore, the maximum length of the LSP Entries TLV is
+ * 16 * 15 + 2 (header) = 242 bytes.
+ */
+#define MAX_LSP_ENTRIES_TLV_SIZE 242
+
+#define L1_COMPLETE_SEQ_NUM  24
+#define L2_COMPLETE_SEQ_NUM  25
+/*
+ *      L1 and L2 IS to IS complete sequence numbers PDU header
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        PDU Length                             + 2    
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Source ID                              + id_len + 1
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Start LSP ID                           + id_len + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        End LSP ID                             + id_len + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+struct isis_complete_seqnum_hdr
+{
+  u_int16_t pdu_len;
+  u_char source_id[ISIS_SYS_ID_LEN + 1];
+  u_char start_lsp_id[ISIS_SYS_ID_LEN + 2];
+  u_char stop_lsp_id[ISIS_SYS_ID_LEN + 2];
+};
+#define ISIS_CSNP_HDRLEN 25
+
+#define L1_PARTIAL_SEQ_NUM   26
+#define L2_PARTIAL_SEQ_NUM   27
+/*
+ *      L1 and L2 IS to IS partial sequence numbers PDU header
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        PDU Length                             + 2
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * +                        Source ID                              + id_len + 1
+ * +---------------------------------------------------------------+
+ */
+struct isis_partial_seqnum_hdr
+{
+  u_int16_t pdu_len;
+  u_char source_id[ISIS_SYS_ID_LEN + 1];
+};
+#define ISIS_PSNP_HDRLEN 9
+
+#ifdef __SUNPRO_C
+#pragma pack()
+#endif
+
+/*
+ * Function for receiving IS-IS PDUs
+ */
+int isis_receive (struct thread *thread);
+
+/*
+ * calling arguments for snp_process ()
+ */
+#define ISIS_SNP_PSNP_FLAG 0
+#define ISIS_SNP_CSNP_FLAG 1
+
+#define ISIS_AUTH_MD5_SIZE       16U
+
+/*
+ * Sending functions
+ */
+int send_lan_l1_hello (struct thread *thread);
+int send_lan_l2_hello (struct thread *thread);
+int send_p2p_hello (struct thread *thread);
+int send_csnp (struct isis_circuit *circuit, int level);
+int send_l1_csnp (struct thread *thread);
+int send_l2_csnp (struct thread *thread);
+int send_l1_psnp (struct thread *thread);
+int send_l2_psnp (struct thread *thread);
+int send_lsp (struct thread *thread);
+int ack_lsp (struct isis_link_state_hdr *hdr,
+            struct isis_circuit *circuit, int level);
+void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type);
+int send_hello (struct isis_circuit *circuit, int level);
+
+#endif /* _ZEBRA_ISIS_PDU_H */
diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
new file mode 100644 (file)
index 0000000..2427047
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_pfpacket.c
+ *
+ * Copyright (C) 2001,2002    Sampo Saaristo
+ *                            Tampere University of Technology      
+ *                            Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#if ISIS_METHOD == ISIS_METHOD_PFPACKET
+#include <net/ethernet.h>      /* the L2 protocols */
+#include <netpacket/packet.h>
+
+#include "log.h"
+#include "network.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 uint8_t discard_buff[8192];
+static uint8_t 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));
+      close (fd);
+      return ISIS_WARNING;
+    }
+
+  circuit->fd = fd;
+
+  if (if_is_broadcast (circuit->interface))
+    {
+      /*
+       * Join to multicast groups
+       * according to
+       * 8.4.2 - Broadcast subnetwork IIH PDUs
+       * FIXME: is there a case only one will fail??
+       */
+      /* joining ALL_L1_ISS */
+      retval |= isis_multicast_join (circuit->fd, 1,
+                                      circuit->interface->ifindex);
+      /* joining ALL_L2_ISS */
+      retval |= isis_multicast_join (circuit->fd, 2,
+                                      circuit->interface->ifindex);
+      /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */
+      retval |= isis_multicast_join (circuit->fd, 3,
+                                    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;
+    }
+
+  /* Assign Rx and Tx callbacks are based on real if type */
+  if (if_is_broadcast (circuit->interface))
+    {
+      circuit->tx = isis_send_pdu_bcast;
+      circuit->rx = isis_recv_pdu_bcast;
+    }
+  else if (if_is_pointopoint (circuit->interface))
+    {
+      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) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex))
+    {
+      if (bytesread < 0)
+        {
+          zlog_warn ("isis_recv_packet_bcast(): ifname %s, fd %d, "
+                     "bytesread %d, recvfrom(): %s",
+                     circuit->interface->name, circuit->fd, bytesread,
+                     safe_strerror (errno));
+        }
+      if (s_addr.sll_ifindex != (int)circuit->interface->ifindex)
+        {
+          zlog_warn("packet is received on multiple interfaces: "
+                    "socket interface %d, circuit interface %d, "
+                    "packet type %u",
+                    s_addr.sll_ifindex, circuit->interface->ifindex,
+                    s_addr.sll_pkttype);
+        }
+
+      /* get rid of the packet */
+      bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
+                            MSG_DONTWAIT, (struct sockaddr *) &s_addr,
+                            (socklen_t *) &addr_len);
+      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 = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
+                            MSG_DONTWAIT, (struct sockaddr *) &s_addr,
+                            (socklen_t *) &addr_len);
+      if (bytesread < 0)
+       zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
+      return ISIS_WARNING;
+    }
+
+  /* on lan we have to read to the static buff first */
+  bytesread = recvfrom (circuit->fd, sock_buff, sizeof (sock_buff), MSG_DONTWAIT,
+                       (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+  if (bytesread < 0)
+    {
+      zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
+      return ISIS_WARNING;
+    }
+
+  /* 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 = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
+                            MSG_DONTWAIT, (struct sockaddr *) &s_addr,
+                            (socklen_t *) &addr_len);
+      if (bytesread < 0)
+       zlog_warn ("isis_recv_pdu_p2p(): recvfrom() 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)
+{
+  struct msghdr msg;
+  struct iovec iov[2];
+
+  /* we need to do the LLC in here because of P2P circuits, which will
+   * not need it
+   */
+  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;
+  /* RFC5309 section 4.1 recommends ALL_ISS */
+  if (circuit->circ_type == CIRCUIT_T_P2P)
+    memcpy (&sa.sll_addr, ALL_ISS, ETH_ALEN);
+  else 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;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_name = &sa;
+  msg.msg_namelen = sizeof (struct sockaddr_ll);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = 2;
+  iov[0].iov_base = sock_buff;
+  iov[0].iov_len = LLC_LEN;
+  iov[1].iov_base = circuit->snd_stream->data;
+  iov[1].iov_len = stream_get_endp (circuit->snd_stream);
+
+  if (sendmsg(circuit->fd, &msg, 0) < 0)
+    {
+      zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
+                circuit->interface->name, safe_strerror(errno));
+      if (ERRNO_IO_RETRY(errno))
+        return ISIS_WARNING;
+      return ISIS_ERROR;
+    }
+  return ISIS_OK;
+}
+
+int
+isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+{
+  struct sockaddr_ll sa;
+  ssize_t rv;
+
+  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);
+  rv = sendto(circuit->fd, circuit->snd_stream->data,
+             stream_get_endp (circuit->snd_stream), 0,
+             (struct sockaddr *) &sa,
+             sizeof (struct sockaddr_ll));
+  if (rv < 0)
+    {
+      zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
+                circuit->interface->name, safe_strerror(errno));
+      if (ERRNO_IO_RETRY(errno))
+        return ISIS_WARNING;
+      return ISIS_ERROR;
+    }
+  return ISIS_OK;
+}
+
+#endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */
diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c
new file mode 100644 (file)
index 0000000..ad0a31b
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_redist.c
+ *
+ * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program 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 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "if.h"
+#include "linklist.h"
+#include "memory.h"
+#include "memtypes.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "stream.h"
+#include "table.h"
+#include "vty.h"
+
+#include "isisd/dict.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_zebra.h"
+
+static int
+redist_protocol(int family)
+{
+  if (family == AF_INET)
+    return 0;
+  if (family == AF_INET6)
+    return 1;
+
+  assert(!"Unsupported address family!");
+  return 0;
+}
+
+static int
+is_default(struct prefix *p)
+{
+  if (p->family == AF_INET)
+    if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0)
+      return 1;
+  if (p->family == AF_INET6)
+    if (IN6_IS_ADDR_UNSPECIFIED(&p->u.prefix6) && p->prefixlen == 0)
+      return 1;
+  return 0;
+}
+
+static struct route_table*
+get_ext_info(struct isis *i, int family)
+{
+  int protocol = redist_protocol(family);
+
+  return i->ext_info[protocol];
+}
+
+static struct isis_redist*
+get_redist_settings(struct isis_area *area, int family, int type, int level)
+{
+  int protocol = redist_protocol(family);
+
+  return &area->redist_settings[protocol][type][level-1];
+}
+
+struct route_table*
+get_ext_reach(struct isis_area *area, int family, int level)
+{
+  int protocol = redist_protocol(family);
+
+  return area->ext_reach[protocol][level-1];
+}
+
+static struct route_node *
+isis_redist_route_node_create(route_table_delegate_t *delegate,
+                              struct route_table *table)
+{
+  struct route_node *node;
+  node = XCALLOC(MTYPE_ROUTE_NODE, sizeof(*node));
+  return node;
+}
+
+static void
+isis_redist_route_node_destroy(route_table_delegate_t *delegate,
+                               struct route_table *table,
+                               struct route_node *node)
+{
+  if (node->info)
+    XFREE(MTYPE_ISIS, node->info);
+  XFREE (MTYPE_ROUTE_NODE, node);
+}
+
+static route_table_delegate_t isis_redist_rt_delegate = {
+  .create_node = isis_redist_route_node_create,
+  .destroy_node = isis_redist_route_node_destroy
+};
+
+/* Install external reachability information into a
+ * specific area for a specific level.
+ * Schedule an lsp regenerate if necessary */
+static void
+isis_redist_install(struct isis_area *area, int level,
+                    struct prefix *p, struct isis_ext_info *info)
+{
+  int family = p->family;
+  struct route_table *er_table = get_ext_reach(area, family, level);
+  struct route_node *er_node;
+
+  if (!er_table)
+    {
+      zlog_warn("%s: External reachability table of area %s"
+                " is not initialized.", __func__, area->area_tag);
+      return;
+    }
+
+  er_node = route_node_get(er_table, p);
+  if (er_node->info)
+    {
+      route_unlock_node(er_node);
+
+      /* Don't update/reschedule lsp generation if nothing changed. */
+      if (!memcmp(er_node->info, info, sizeof(*info)))
+        return;
+    }
+  else
+    {
+      er_node->info = XMALLOC(MTYPE_ISIS, sizeof(*info));
+    }
+
+  memcpy(er_node->info, info, sizeof(*info));
+  lsp_regenerate_schedule(area, level, 0);
+}
+
+/* Remove external reachability information from a
+ * specific area for a specific level.
+ * Schedule an lsp regenerate if necessary. */
+static void
+isis_redist_uninstall(struct isis_area *area, int level, struct prefix *p)
+{
+  int family = p->family;
+  struct route_table *er_table = get_ext_reach(area, family, level);
+  struct route_node *er_node;
+
+  if (!er_table)
+    {
+      zlog_warn("%s: External reachability table of area %s"
+                " is not initialized.", __func__, area->area_tag);
+      return;
+    }
+
+  er_node = route_node_lookup(er_table, p);
+  if (!er_node)
+    return;
+  else
+    route_unlock_node(er_node);
+
+  if (!er_node->info)
+    return;
+
+  XFREE(MTYPE_ISIS, er_node->info);
+  route_unlock_node(er_node);
+  lsp_regenerate_schedule(area, level, 0);
+}
+
+/* Update external reachability info of area for a given level
+ * and prefix, using the given redistribution settings. */
+static void
+isis_redist_update_ext_reach(struct isis_area *area, int level,
+                             struct isis_redist *redist, struct prefix *p,
+                             struct isis_ext_info *info)
+{
+  struct isis_ext_info area_info;
+  route_map_result_t map_ret;
+
+  memcpy(&area_info, info, sizeof(area_info));
+  if (redist->metric != 0xffffffff)
+    area_info.metric = redist->metric;
+
+  if (redist->map_name)
+    {
+      map_ret = route_map_apply(redist->map, p, RMAP_ISIS, &area_info);
+      if (map_ret == RMAP_DENYMATCH)
+        area_info.distance = 255;
+    }
+
+  /* Allow synthesized default routes only on always orignate */
+  if (area_info.origin == DEFAULT_ROUTE
+      && redist->redist != DEFAULT_ORIGINATE_ALWAYS)
+    area_info.distance = 255;
+
+  if (area_info.distance < 255)
+    isis_redist_install(area, level, p, &area_info);
+  else
+    isis_redist_uninstall(area, level, p);
+}
+
+static void
+isis_redist_ensure_default(struct isis *isis, int family)
+{
+  struct prefix p;
+  struct route_table *ei_table = get_ext_info(isis, family);
+  struct route_node *ei_node;
+  struct isis_ext_info *info;
+
+  if (family == AF_INET)
+    {
+      p.family = AF_INET;
+      p.prefixlen = 0;
+      memset(&p.u.prefix4, 0, sizeof(p.u.prefix4));
+    }
+  else if (family == AF_INET6)
+    {
+      p.family = AF_INET6;
+      p.prefixlen = 0;
+      memset(&p.u.prefix6, 0, sizeof(p.u.prefix6));
+    }
+  else
+    assert(!"Unknown family!");
+
+  ei_node = route_node_get(ei_table, &p);
+  if (ei_node->info)
+    {
+      route_unlock_node(ei_node);
+      return;
+    }
+
+  ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info));
+
+  info = ei_node->info;
+  info->origin = DEFAULT_ROUTE;
+  info->distance = 254;
+  info->metric = MAX_WIDE_PATH_METRIC;
+}
+
+/* Handle notification about route being added */
+void
+isis_redist_add(int type, struct prefix *p, u_char distance, uint32_t metric)
+{
+  int family = p->family;
+  struct route_table *ei_table = get_ext_info(isis, family);
+  struct route_node *ei_node;
+  struct isis_ext_info *info;
+  struct listnode *node;
+  struct isis_area *area;
+  int level;
+  struct isis_redist *redist;
+
+  char debug_buf[BUFSIZ];
+  prefix2str(p, debug_buf, sizeof(debug_buf));
+
+  zlog_debug("%s: New route %s from %s.", __func__, debug_buf,
+             zebra_route_string(type));
+
+  if (!ei_table)
+    {
+      zlog_warn("%s: External information table not initialized.",
+                __func__);
+      return;
+    }
+
+  ei_node = route_node_get(ei_table, p);
+  if (ei_node->info)
+    route_unlock_node(ei_node);
+  else
+    ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info));
+
+  info = ei_node->info;
+  info->origin = type;
+  info->distance = distance;
+  info->metric = metric;
+
+  if (is_default(p))
+    type = DEFAULT_ROUTE;
+
+  for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
+    for (level = 1; level <= ISIS_LEVELS; level++)
+      {
+        redist = get_redist_settings(area, family, type, level);
+        if (!redist->redist)
+          continue;
+
+        isis_redist_update_ext_reach(area, level, redist, p, info);
+      }
+}
+
+void
+isis_redist_delete(int type, struct prefix *p)
+{
+  int family = p->family;
+  struct route_table *ei_table = get_ext_info(isis, family);
+  struct route_node *ei_node;
+  struct listnode *node;
+  struct isis_area *area;
+  int level;
+  struct isis_redist *redist;
+
+  char debug_buf[BUFSIZ];
+  prefix2str(p, debug_buf, sizeof(debug_buf));
+
+  zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf,
+             zebra_route_string(type));
+
+  if (is_default(p))
+    {
+      /* Don't remove default route but add synthetic route for use
+       * by "default-information originate always". Areas without the
+       * "always" setting will ignore routes with origin DEFAULT_ROUTE. */
+      isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC);
+      return;
+    }
+
+  if (!ei_table)
+    {
+      zlog_warn("%s: External information table not initialized.",
+                __func__);
+      return;
+    }
+
+  ei_node = route_node_lookup(ei_table, p);
+  if (!ei_node || !ei_node->info)
+    {
+      char buf[BUFSIZ];
+      prefix2str(p, buf, sizeof(buf));
+      zlog_warn("%s: Got a delete for %s route %s, but that route"
+                " was never added.", __func__, zebra_route_string(type),
+                buf);
+      if (ei_node)
+        route_unlock_node(ei_node);
+      return;
+    }
+  route_unlock_node(ei_node);
+
+  for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
+    for (level = 1; level < ISIS_LEVELS; level++)
+      {
+        redist = get_redist_settings(area, family, type, level);
+        if (!redist->redist)
+          continue;
+
+        isis_redist_uninstall(area, level, p);
+      }
+
+  XFREE(MTYPE_ISIS, ei_node->info);
+  route_unlock_node(ei_node);
+}
+
+static void
+isis_redist_routemap_set(struct isis_redist *redist, const char *routemap)
+{
+  if (redist->map_name) {
+    XFREE(MTYPE_ISIS, redist->map_name);
+    redist->map = NULL;
+  }
+
+  if (routemap && strlen(routemap)) {
+    redist->map_name = XSTRDUP(MTYPE_ISIS, routemap);
+    redist->map = route_map_lookup_by_name(routemap);
+  }
+}
+
+static void
+isis_redist_update_zebra_subscriptions(struct isis *isis)
+{
+  struct listnode *node;
+  struct isis_area *area;
+  int type;
+  int level;
+  int protocol;
+
+  char do_subscribe[ZEBRA_ROUTE_MAX + 1];
+
+  memset(do_subscribe, 0, sizeof(do_subscribe));
+
+  for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
+    for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++)
+      for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++)
+        for (level = 0; level < ISIS_LEVELS; level++)
+          if (area->redist_settings[protocol][type][level].redist)
+            do_subscribe[type] = 1;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++)
+    {
+      /* This field is actually controlling transmission of the IS-IS
+       * routes to Zebra and has nothing to do with redistribution,
+       * so skip it. */
+      if (type == ZEBRA_ROUTE_ISIS)
+        continue;
+
+      if (do_subscribe[type])
+        isis_zebra_redistribute_set(type);
+      else
+        isis_zebra_redistribute_unset(type);
+    }
+}
+
+static void
+isis_redist_set(struct isis_area *area, int level,
+                int family, int type, uint32_t metric,
+                const char *routemap, int originate_type)
+{
+  int protocol = redist_protocol(family);
+  struct isis_redist *redist = get_redist_settings(area, family, type, level);
+  int i;
+  struct route_table *ei_table;
+  struct route_node *rn;
+  struct isis_ext_info *info;
+
+  redist->redist = (type == DEFAULT_ROUTE) ? originate_type : 1;
+  redist->metric = metric;
+  isis_redist_routemap_set(redist, routemap);
+
+  if (!area->ext_reach[protocol][level-1])
+    {
+      area->ext_reach[protocol][level-1] =
+          route_table_init_with_delegate(&isis_redist_rt_delegate);
+    }
+
+  for (i = 0; i < REDIST_PROTOCOL_COUNT; i++)
+    if (!area->isis->ext_info[i])
+      {
+        area->isis->ext_info[i] =
+            route_table_init_with_delegate(&isis_redist_rt_delegate);
+      }
+
+  isis_redist_update_zebra_subscriptions(area->isis);
+
+  if (type == DEFAULT_ROUTE && originate_type == DEFAULT_ORIGINATE_ALWAYS)
+    isis_redist_ensure_default(area->isis, family);
+
+  ei_table = get_ext_info(area->isis, family);
+  for (rn = route_top(ei_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+      info = rn->info;
+
+      if (type == DEFAULT_ROUTE)
+        {
+          if (!is_default(&rn->p))
+            continue;
+        }
+      else
+        {
+          if (info->origin != type)
+            continue;
+        }
+
+      isis_redist_update_ext_reach(area, level, redist, &rn->p, info);
+    }
+}
+
+static void
+isis_redist_unset(struct isis_area *area, int level,
+                  int family, int type)
+{
+  struct isis_redist *redist = get_redist_settings(area, family, type, level);
+  struct route_table *er_table = get_ext_reach(area, family, level);
+  struct route_node *rn;
+  struct isis_ext_info *info;
+
+  if (!redist->redist)
+    return;
+
+  redist->redist = 0;
+  if (!er_table)
+    {
+      zlog_warn("%s: External reachability table uninitialized.", __func__);
+      return;
+    }
+
+  for (rn = route_top(er_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+      info = rn->info;
+
+      if (type == DEFAULT_ROUTE)
+        {
+          if (!is_default(&rn->p))
+            continue;
+        }
+      else
+        {
+          if (info->origin != type)
+            continue;
+        }
+
+      XFREE(MTYPE_ISIS, rn->info);
+      route_unlock_node(rn);
+    }
+
+  lsp_regenerate_schedule(area, level, 0);
+  isis_redist_update_zebra_subscriptions(area->isis);
+}
+
+void
+isis_redist_area_finish(struct isis_area *area)
+{
+  int protocol;
+  int level;
+  int type;
+
+  for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++)
+    for (level = 0; level < ISIS_LEVELS; level++)
+      {
+        for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++)
+          {
+            struct isis_redist *redist;
+
+            redist = &area->redist_settings[protocol][type][level];
+            redist->redist = 0;
+            if (redist->map_name)
+              XFREE(MTYPE_ISIS, redist->map_name);
+          }
+        route_table_finish(area->ext_reach[protocol][level]);
+      }
+
+  isis_redist_update_zebra_subscriptions(area->isis);
+}
+
+DEFUN(isis_redistribute,
+      isis_redistribute_cmd,
+      "redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD
+      " (level-1|level-2) {metric <0-16777215>|route-map WORD}",
+      REDIST_STR
+      "Redistribute IPv4 routes\n"
+      "Redistribute IPv6 routes\n"
+      QUAGGA_REDIST_HELP_STR_ISISD
+      "Redistribute into level-1\n"
+      "Redistribute into level-2\n"
+      "Metric for redistributed routes\n"
+      "ISIS default metric\n"
+      "Route map reference\n"
+      "Pointer to route-map entries\n")
+{
+  struct isis_area *area = vty->index;
+  int family;
+  int afi;
+  int type;
+  int level;
+  unsigned long metric;
+  const char *routemap;
+
+  if (argc < 5)
+    return CMD_WARNING;
+
+  family = str2family(argv[0]);
+  if (family < 0)
+    return CMD_WARNING;
+
+  afi = family2afi(family);
+  if (!afi)
+    return CMD_WARNING;
+
+  type = proto_redistnum(afi, argv[1]);
+  if (type < 0 || type == ZEBRA_ROUTE_ISIS)
+    return CMD_WARNING;
+
+  if (!strcmp("level-1", argv[2]))
+    level = 1;
+  else if (!strcmp("level-2", argv[2]))
+    level = 2;
+  else
+    return CMD_WARNING;
+
+  if ((area->is_type & level) != level)
+    {
+      vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argv[3])
+    {
+      char *endp;
+      metric = strtoul(argv[3], &endp, 10);
+      if (argv[3][0] == '\0' || *endp != '\0')
+        return CMD_WARNING;
+    }
+  else
+    {
+      metric = 0xffffffff;
+    }
+
+  routemap = argv[4];
+
+  isis_redist_set(area, level, family, type, metric, routemap, 0);
+  return 0;
+}
+
+DEFUN(no_isis_redistribute,
+      no_isis_redistribute_cmd,
+      "no redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD
+      " (level-1|level-2)",
+      NO_STR
+      REDIST_STR
+      "Redistribute IPv4 routes\n"
+      "Redistribute IPv6 routes\n"
+      QUAGGA_REDIST_HELP_STR_ISISD
+      "Redistribute into level-1\n"
+      "Redistribute into level-2\n")
+{
+  struct isis_area *area = vty->index;
+  int type;
+  int level;
+  int family;
+  int afi;
+
+  if (argc < 3)
+    return CMD_WARNING;
+
+  family = str2family(argv[0]);
+  if (family < 0)
+    return CMD_WARNING;
+
+  afi = family2afi(family);
+  if (!afi)
+    return CMD_WARNING;
+
+  type = proto_redistnum(afi, argv[1]);
+  if (type < 0 || type == ZEBRA_ROUTE_ISIS)
+    return CMD_WARNING;
+
+  if (!strcmp("level-1", argv[2]))
+    level = 1;
+  else if (!strcmp("level-2", argv[2]))
+    level = 2;
+  else
+    return CMD_WARNING;
+
+  isis_redist_unset(area, level, family, type);
+  return 0;
+}
+
+DEFUN(isis_default_originate,
+      isis_default_originate_cmd,
+      "default-information originate (ipv4|ipv6) (level-1|level-2) "
+        "{always|metric <0-16777215>|route-map WORD}",
+      "Control distribution of default information\n"
+      "Distribute a default route\n"
+      "Distribute default route for IPv4\n"
+      "Distribute default route for IPv6\n"
+      "Distribute default route into level-1\n"
+      "Distribute default route into level-2\n"
+      "Always advertise default route\n"
+      "Metric for default route\n"
+      "ISIS default metric\n"
+      "Route map reference\n"
+      "Pointer to route-map entries\n")
+{
+  struct isis_area *area = vty->index;
+  int family;
+  int originate_type;
+  int level;
+  unsigned long metric;
+  const char *routemap;
+
+  if (argc < 5)
+    return CMD_WARNING;
+
+  family = str2family(argv[0]);
+  if (family < 0)
+    return CMD_WARNING;
+
+  if (!strcmp("level-1", argv[1]))
+    level = 1;
+  else if (!strcmp("level-2", argv[1]))
+    level = 2;
+  else
+    return CMD_WARNING;
+
+  if ((area->is_type & level) != level)
+    {
+      vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argv[2] && *argv[2] != '\0')
+    originate_type = DEFAULT_ORIGINATE_ALWAYS;
+  else
+    originate_type = DEFAULT_ORIGINATE;
+
+  if (family == AF_INET6 && originate_type != DEFAULT_ORIGINATE_ALWAYS)
+    {
+      vty_out(vty, "Zebra doesn't implement default-originate for IPv6 yet%s", VTY_NEWLINE);
+      vty_out(vty, "so use with care or use default-originate always.%s", VTY_NEWLINE);
+    }
+
+  if (argv[3])
+    {
+      char *endp;
+      metric = strtoul(argv[3], &endp, 10);
+      if (argv[3][0] == '\0' || *endp != '\0')
+        return CMD_WARNING;
+    }
+  else
+    {
+      metric = 0xffffffff;
+    }
+
+  routemap = argv[4];
+
+  isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, originate_type);
+  return 0;
+}
+
+DEFUN(no_isis_default_originate,
+      no_isis_default_originate_cmd,
+      "no default-information originate (ipv4|ipv6) (level-1|level-2)",
+      NO_STR
+      "Control distribution of default information\n"
+      "Distribute a default route\n"
+      "Distribute default route for IPv4\n"
+      "Distribute default route for IPv6\n"
+      "Distribute default route into level-1\n"
+      "Distribute default route into level-2\n")
+{
+  struct isis_area *area = vty->index;
+
+  int family;
+  int level;
+
+  if (argc < 2)
+    return CMD_WARNING;
+
+  family = str2family(argv[0]);
+  if (family < 0)
+    return CMD_WARNING;
+
+  if (!strcmp("level-1", argv[1]))
+    level = 1;
+  else if (!strcmp("level-2", argv[1]))
+    level = 2;
+  else
+    return CMD_WARNING;
+
+  isis_redist_unset(area, level, family, DEFAULT_ROUTE);
+  return 0;
+}
+
+int
+isis_redist_config_write(struct vty *vty, struct isis_area *area,
+                         int family)
+{
+  int type;
+  int level;
+  int write = 0;
+  struct isis_redist *redist;
+  const char *family_str;
+
+  if (family == AF_INET)
+    family_str = "ipv4";
+  else if (family == AF_INET6)
+    family_str = "ipv6";
+  else
+    return 0;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (type == ZEBRA_ROUTE_ISIS)
+        continue;
+
+      for (level = 1; level <= ISIS_LEVELS; level++)
+        {
+          redist = get_redist_settings(area, family, type, level);
+          if (!redist->redist)
+            continue;
+          vty_out(vty, " redistribute %s %s level-%d",
+                  family_str, zebra_route_string(type), level);
+          if (redist->metric != 0xffffffff)
+            vty_out(vty, " metric %u", redist->metric);
+          if (redist->map_name)
+            vty_out(vty, " route-map %s", redist->map_name);
+          vty_out(vty, "%s", VTY_NEWLINE);
+          write++;
+        }
+    }
+
+  for (level = 1; level <= ISIS_LEVELS; level++)
+    {
+      redist = get_redist_settings(area, family, DEFAULT_ROUTE, level);
+      if (!redist->redist)
+        continue;
+      vty_out(vty, " default-information originate %s level-%d",
+              family_str, level);
+      if (redist->redist == DEFAULT_ORIGINATE_ALWAYS)
+        vty_out(vty, " always");
+      if (redist->metric != 0xffffffff)
+        vty_out(vty, " metric %u", redist->metric);
+      if (redist->map_name)
+        vty_out(vty, " route-map %s", redist->map_name);
+      vty_out(vty, "%s", VTY_NEWLINE);
+      write++;
+    }
+
+  return write;
+}
+
+void
+isis_redist_init(void)
+{
+  install_element(ISIS_NODE, &isis_redistribute_cmd);
+  install_element(ISIS_NODE, &no_isis_redistribute_cmd);
+  install_element(ISIS_NODE, &isis_default_originate_cmd);
+  install_element(ISIS_NODE, &no_isis_default_originate_cmd);
+}
diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h
new file mode 100644 (file)
index 0000000..cc9c2e6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_redist.h
+ *
+ * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program 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 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_REDIST_H
+#define ISIS_REDIST_H
+
+#define REDIST_PROTOCOL_COUNT 2
+
+#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX
+#define DEFAULT_ORIGINATE 1
+#define DEFAULT_ORIGINATE_ALWAYS 2
+
+struct isis_ext_info
+{
+  int origin;
+  uint32_t metric;
+  u_char distance;
+};
+
+struct isis_redist
+{
+  int redist;
+  uint32_t metric;
+  char *map_name;
+  struct route_map *map;
+};
+
+struct isis_area;
+struct prefix;
+struct vty;
+
+struct route_table *get_ext_reach(struct isis_area *area,
+                                  int family, int level);
+void isis_redist_add(int type, struct prefix *p,
+                     u_char distance, uint32_t metric);
+void isis_redist_delete(int type, struct prefix *p);
+int isis_redist_config_write(struct vty *vty, struct isis_area *area,
+                             int family);
+void isis_redist_init(void);
+void isis_redist_area_finish(struct isis_area *area);
+
+#endif
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
new file mode 100644 (file)
index 0000000..c0ec01e
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * IS-IS Rout(e)ing protocol               - isis_route.c
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ *                                         based on ../ospf6d/ospf6_route.[ch]
+ *                                         by Yasuhiro Ohara
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "vty.h"
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "hash.h"
+#include "if.h"
+#include "table.h"
+
+#include "isis_constants.h"
+#include "isis_common.h"
+#include "isis_flags.h"
+#include "dict.h"
+#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_spf.h"
+#include "isis_route.h"
+#include "isis_zebra.h"
+
+static struct isis_nexthop *
+isis_nexthop_create (struct in_addr *ip, ifindex_t ifindex)
+{
+  struct listnode *node;
+  struct isis_nexthop *nexthop;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->nexthops, node, nexthop))
+    {
+      if (nexthop->ifindex != ifindex)
+       continue;
+      if (ip && memcmp (&nexthop->ip, ip, sizeof (struct in_addr)) != 0)
+       continue;
+
+      nexthop->lock++;
+      return nexthop;
+    }
+
+  nexthop = XCALLOC (MTYPE_ISIS_NEXTHOP, sizeof (struct isis_nexthop));
+
+  nexthop->ifindex = ifindex;
+  memcpy (&nexthop->ip, ip, sizeof (struct in_addr));
+  listnode_add (isis->nexthops, nexthop);
+  nexthop->lock++;
+
+  return nexthop;
+}
+
+static void
+isis_nexthop_delete (struct isis_nexthop *nexthop)
+{
+  nexthop->lock--;
+  if (nexthop->lock == 0)
+    {
+      listnode_delete (isis->nexthops, nexthop);
+      XFREE (MTYPE_ISIS_NEXTHOP, nexthop);
+    }
+
+  return;
+}
+
+static int
+nexthoplookup (struct list *nexthops, struct in_addr *ip,
+              ifindex_t ifindex)
+{
+  struct listnode *node;
+  struct isis_nexthop *nh;
+
+  for (ALL_LIST_ELEMENTS_RO (nexthops, node, nh))
+    {
+      if (!(memcmp (ip, &nh->ip, sizeof (struct in_addr))) &&
+         ifindex == nh->ifindex)
+       return 1;
+    }
+
+  return 0;
+}
+
+#ifdef EXTREME_DEBUG
+static void
+nexthop_print (struct isis_nexthop *nh)
+{
+  u_char buf[BUFSIZ];
+
+  inet_ntop (AF_INET, &nh->ip, (char *) buf, BUFSIZ);
+
+  zlog_debug ("      %s %u", buf, nh->ifindex);
+}
+
+static void
+nexthops_print (struct list *nhs)
+{
+  struct listnode *node;
+  struct isis_nexthop *nh;
+
+  for (ALL_LIST_ELEMENTS_RO (nhs, node, nh))
+    nexthop_print (nh);
+}
+#endif /* EXTREME_DEBUG */
+
+#ifdef HAVE_IPV6
+static struct isis_nexthop6 *
+isis_nexthop6_new (struct in6_addr *ip6, ifindex_t ifindex)
+{
+  struct isis_nexthop6 *nexthop6;
+
+  nexthop6 = XCALLOC (MTYPE_ISIS_NEXTHOP6, sizeof (struct isis_nexthop6));
+
+  nexthop6->ifindex = ifindex;
+  memcpy (&nexthop6->ip6, ip6, sizeof (struct in6_addr));
+  nexthop6->lock++;
+
+  return nexthop6;
+}
+
+static struct isis_nexthop6 *
+isis_nexthop6_create (struct in6_addr *ip6, ifindex_t ifindex)
+{
+  struct listnode *node;
+  struct isis_nexthop6 *nexthop6;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->nexthops6, node, nexthop6))
+    {
+      if (nexthop6->ifindex != ifindex)
+       continue;
+      if (ip6 && memcmp (&nexthop6->ip6, ip6, sizeof (struct in6_addr)) != 0)
+       continue;
+
+      nexthop6->lock++;
+      return nexthop6;
+    }
+
+  nexthop6 = isis_nexthop6_new (ip6, ifindex);
+
+  return nexthop6;
+}
+
+static void
+isis_nexthop6_delete (struct isis_nexthop6 *nexthop6)
+{
+
+  nexthop6->lock--;
+  if (nexthop6->lock == 0)
+    {
+      listnode_delete (isis->nexthops6, nexthop6);
+      XFREE (MTYPE_ISIS_NEXTHOP6, nexthop6);
+    }
+
+  return;
+}
+
+static int
+nexthop6lookup (struct list *nexthops6, struct in6_addr *ip6,
+               ifindex_t ifindex)
+{
+  struct listnode *node;
+  struct isis_nexthop6 *nh6;
+
+  for (ALL_LIST_ELEMENTS_RO (nexthops6, node, nh6))
+    {
+      if (!(memcmp (ip6, &nh6->ip6, sizeof (struct in6_addr))) &&
+         ifindex == nh6->ifindex)
+       return 1;
+    }
+
+  return 0;
+}
+
+#ifdef EXTREME_DEBUG
+static void
+nexthop6_print (struct isis_nexthop6 *nh6)
+{
+  u_char buf[BUFSIZ];
+
+  inet_ntop (AF_INET6, &nh6->ip6, (char *) buf, BUFSIZ);
+
+  zlog_debug ("      %s %u", buf, nh6->ifindex);
+}
+
+static void
+nexthops6_print (struct list *nhs6)
+{
+  struct listnode *node;
+  struct isis_nexthop6 *nh6;
+
+  for (ALL_LIST_ELEMENTS_RO (nhs6, node, nh6))
+    nexthop6_print (nh6);
+}
+#endif /* EXTREME_DEBUG */
+#endif /* HAVE_IPV6 */
+
+static void
+adjinfo2nexthop (struct list *nexthops, struct isis_adjacency *adj)
+{
+  struct isis_nexthop *nh;
+  struct listnode *node;
+  struct in_addr *ipv4_addr;
+
+  if (adj->ipv4_addrs == NULL)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr))
+    {
+      if (!nexthoplookup (nexthops, ipv4_addr,
+                         adj->circuit->interface->ifindex))
+       {
+         nh = isis_nexthop_create (ipv4_addr,
+                                   adj->circuit->interface->ifindex);
+          nh->router_address = adj->router_address;
+         listnode_add (nexthops, nh);
+       }
+    }
+}
+
+#ifdef HAVE_IPV6
+static void
+adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj)
+{
+  struct listnode *node;
+  struct in6_addr *ipv6_addr;
+  struct isis_nexthop6 *nh6;
+
+  if (!adj->ipv6_addrs)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
+    {
+      if (!nexthop6lookup (nexthops6, ipv6_addr,
+                          adj->circuit->interface->ifindex))
+       {
+         nh6 = isis_nexthop6_create (ipv6_addr,
+                                     adj->circuit->interface->ifindex);
+          nh6->router_address6 = adj->router_address6;
+         listnode_add (nexthops6, nh6);
+       }
+    }
+}
+#endif /* HAVE_IPV6 */
+
+static struct isis_route_info *
+isis_route_info_new (struct prefix *prefix, uint32_t cost, uint32_t depth,
+                     struct list *adjacencies)
+{
+  struct isis_route_info *rinfo;
+  struct isis_adjacency *adj;
+  struct listnode *node;
+
+  rinfo = XCALLOC (MTYPE_ISIS_ROUTE_INFO, sizeof (struct isis_route_info));
+
+  if (prefix->family == AF_INET)
+    {
+      rinfo->nexthops = list_new ();
+      for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj))
+        {
+          /* check for force resync this route */
+          if (CHECK_FLAG (adj->circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF))
+            SET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
+          /* update neighbor router address */
+          if (depth == 2 && prefix->prefixlen == 32)
+            adj->router_address = prefix->u.prefix4;
+          adjinfo2nexthop (rinfo->nexthops, adj);
+        }
+    }
+#ifdef HAVE_IPV6
+  if (prefix->family == AF_INET6)
+    {
+      rinfo->nexthops6 = list_new ();
+      for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj))
+        {
+          /* check for force resync this route */
+          if (CHECK_FLAG (adj->circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF))
+            SET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
+          /* update neighbor router address */
+          if (depth == 2 && prefix->prefixlen == 128)
+            adj->router_address6 = prefix->u.prefix6;
+          adjinfo2nexthop6 (rinfo->nexthops6, adj);
+        }
+    }
+
+#endif /* HAVE_IPV6 */
+
+  rinfo->cost = cost;
+  rinfo->depth = depth;
+
+  return rinfo;
+}
+
+static void
+isis_route_info_delete (struct isis_route_info *route_info)
+{
+  if (route_info->nexthops)
+    {
+      route_info->nexthops->del = (void (*)(void *)) isis_nexthop_delete;
+      list_delete (route_info->nexthops);
+    }
+
+#ifdef HAVE_IPV6
+  if (route_info->nexthops6)
+    {
+      route_info->nexthops6->del = (void (*)(void *)) isis_nexthop6_delete;
+      list_delete (route_info->nexthops6);
+    }
+#endif /* HAVE_IPV6 */
+
+  XFREE (MTYPE_ISIS_ROUTE_INFO, route_info);
+}
+
+static int
+isis_route_info_same_attrib (struct isis_route_info *new,
+                            struct isis_route_info *old)
+{
+  if (new->cost != old->cost)
+    return 0;
+  if (new->depth != old->depth)
+    return 0;
+
+  return 1;
+}
+
+static int
+isis_route_info_same (struct isis_route_info *new,
+                     struct isis_route_info *old, u_char family)
+{
+  struct listnode *node;
+  struct isis_nexthop *nexthop;
+#ifdef HAVE_IPV6
+  struct isis_nexthop6 *nexthop6;
+#endif /* HAVE_IPV6 */
+
+  if (!CHECK_FLAG (old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
+    return 0;
+
+  if (CHECK_FLAG (new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC))
+    return 0;
+
+  if (!isis_route_info_same_attrib (new, old))
+    return 0;
+
+  if (family == AF_INET)
+    {
+      for (ALL_LIST_ELEMENTS_RO (new->nexthops, node, nexthop))
+        if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex)
+              == 0)
+          return 0;
+
+      for (ALL_LIST_ELEMENTS_RO (old->nexthops, node, nexthop))
+        if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex)
+             == 0)
+          return 0;
+    }
+#ifdef HAVE_IPV6
+  else if (family == AF_INET6)
+    {
+      for (ALL_LIST_ELEMENTS_RO (new->nexthops6, node, nexthop6))
+        if (nexthop6lookup (old->nexthops6, &nexthop6->ip6,
+                            nexthop6->ifindex) == 0)
+          return 0;
+
+      for (ALL_LIST_ELEMENTS_RO (old->nexthops6, node, nexthop6))
+        if (nexthop6lookup (new->nexthops6, &nexthop6->ip6,
+                            nexthop6->ifindex) == 0)
+          return 0;
+    }
+#endif /* HAVE_IPV6 */
+
+  return 1;
+}
+
+struct isis_route_info *
+isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth,
+                  struct list *adjacencies, struct isis_area *area,
+                  int level)
+{
+  struct route_node *route_node;
+  struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
+  u_char buff[BUFSIZ];
+  u_char family;
+
+  family = prefix->family;
+  /* for debugs */
+  prefix2str (prefix, (char *) buff, BUFSIZ);
+
+  rinfo_new = isis_route_info_new (prefix, cost, depth, adjacencies);
+
+  if (family == AF_INET)
+    route_node = route_node_get (area->route_table[level - 1], prefix);
+#ifdef HAVE_IPV6
+  else if (family == AF_INET6)
+    route_node = route_node_get (area->route_table6[level - 1], prefix);
+#endif /* HAVE_IPV6 */
+  else
+    {
+      isis_route_info_delete (rinfo_new);
+      return NULL;
+    }
+
+  rinfo_old = route_node->info;
+  if (!rinfo_old)
+    {
+      if (isis->debugs & DEBUG_RTE_EVENTS)
+        zlog_debug ("ISIS-Rte (%s) route created: %s", area->area_tag, buff);
+      route_info = rinfo_new;
+      UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+    }
+  else
+    {
+      if (isis->debugs & DEBUG_RTE_EVENTS)
+        zlog_debug ("ISIS-Rte (%s) route already exists: %s", area->area_tag,
+                   buff);
+      if (isis_route_info_same (rinfo_new, rinfo_old, family))
+        {
+          if (isis->debugs & DEBUG_RTE_EVENTS)
+            zlog_debug ("ISIS-Rte (%s) route unchanged: %s", area->area_tag,
+                        buff);
+          isis_route_info_delete (rinfo_new);
+          route_info = rinfo_old;
+        }
+      else
+        {
+          if (isis->debugs & DEBUG_RTE_EVENTS)
+            zlog_debug ("ISIS-Rte (%s) route changed: %s", area->area_tag,
+                        buff);
+          isis_route_info_delete (rinfo_old);
+          route_info = rinfo_new;
+          UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+        }
+    }
+
+  SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE);
+  route_node->info = route_info;
+
+  return route_info;
+}
+
+static void
+isis_route_delete (struct prefix *prefix, struct route_table *table)
+{
+  struct route_node *rode;
+  struct isis_route_info *rinfo;
+  char buff[BUFSIZ];
+
+  /* for log */
+  prefix2str (prefix, buff, BUFSIZ);
+
+
+  rode = route_node_get (table, prefix);
+  rinfo = rode->info;
+
+  if (rinfo == NULL)
+    {
+      if (isis->debugs & DEBUG_RTE_EVENTS)
+       zlog_debug ("ISIS-Rte: tried to delete non-existant route %s", buff);
+      return;
+    }
+
+  if (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
+    {
+      UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
+      if (isis->debugs & DEBUG_RTE_EVENTS)
+       zlog_debug ("ISIS-Rte: route delete  %s", buff);
+      isis_zebra_route_update (prefix, rinfo);
+    }
+  isis_route_info_delete (rinfo);
+  rode->info = NULL;
+
+  return;
+}
+
+/* Validating routes in particular table. */
+static void
+isis_route_validate_table (struct isis_area *area, struct route_table *table)
+{
+  struct route_node *rnode, *drnode;
+  struct isis_route_info *rinfo;
+  u_char buff[BUFSIZ];
+
+  for (rnode = route_top (table); rnode; rnode = route_next (rnode))
+    {
+      if (rnode->info == NULL)
+       continue;
+      rinfo = rnode->info;
+
+      if (isis->debugs & DEBUG_RTE_EVENTS)
+       {
+         prefix2str (&rnode->p, (char *) buff, BUFSIZ);
+         zlog_debug ("ISIS-Rte (%s): route validate: %s %s %s %s",
+                     area->area_tag,
+                     (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED) ?
+                     "synced" : "not-synced"),
+                     (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC) ?
+                     "resync" : "not-resync"),
+                     (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE) ?
+                     "active" : "inactive"), buff);
+       }
+
+      isis_zebra_route_update (&rnode->p, rinfo);
+      if (!CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE))
+       {
+         /* Area is either L1 or L2 => we use level route tables directly for
+          * validating => no problems with deleting routes. */
+         if (area->is_type != IS_LEVEL_1_AND_2)
+           {
+             isis_route_delete (&rnode->p, table);
+             continue;
+           }
+         /* If area is L1L2, we work with merge table and therefore must
+          * delete node from level tables as well before deleting route info.
+          * FIXME: Is it performance problem? There has to be the better way.
+          * Like not to deal with it here at all (see the next comment)? */
+         if (rnode->p.family == AF_INET)
+           {
+             drnode = route_node_get (area->route_table[0], &rnode->p);
+             if (drnode->info == rnode->info)
+               drnode->info = NULL;
+             drnode = route_node_get (area->route_table[1], &rnode->p);
+             if (drnode->info == rnode->info)
+               drnode->info = NULL;
+           }
+
+#ifdef HAVE_IPV6
+         if (rnode->p.family == AF_INET6)
+           {
+             drnode = route_node_get (area->route_table6[0], &rnode->p);
+             if (drnode->info == rnode->info)
+               drnode->info = NULL;
+             drnode = route_node_get (area->route_table6[1], &rnode->p);
+             if (drnode->info == rnode->info)
+               drnode->info = NULL;
+           }
+#endif
+             
+         isis_route_delete (&rnode->p, table);
+       }
+    }
+}
+
+/* Function to validate route tables for L1L2 areas. In this case we can't use
+ * level route tables directly, we have to merge them at first. L1 routes are
+ * preferred over the L2 ones.
+ *
+ * Merge algorithm is trivial (at least for now). All L1 paths are copied into
+ * merge table at first, then L2 paths are added if L1 path for same prefix
+ * doesn't already exists there.
+ *
+ * FIXME: Is it right place to do it at all? Maybe we should push both levels
+ * to the RIB with different zebra route types and let RIB handle this? */
+static void
+isis_route_validate_merge (struct isis_area *area, int family)
+{
+  struct route_table *table = NULL;
+  struct route_table *merge;
+  struct route_node *rnode, *mrnode;
+
+  merge = route_table_init ();
+
+  if (family == AF_INET)
+    table = area->route_table[0];
+#ifdef HAVE_IPV6
+  else if (family == AF_INET6)
+    table = area->route_table6[0];
+#endif
+
+  for (rnode = route_top (table); rnode; rnode = route_next (rnode))
+    {
+      if (rnode->info == NULL)
+        continue;
+      mrnode = route_node_get (merge, &rnode->p);
+      mrnode->info = rnode->info;
+    }
+
+  if (family == AF_INET)
+    table = area->route_table[1];
+#ifdef HAVE_IPV6
+  else if (family == AF_INET6)
+    table = area->route_table6[1];
+#endif
+
+  for (rnode = route_top (table); rnode; rnode = route_next (rnode))
+    {
+      if (rnode->info == NULL)
+        continue;
+      mrnode = route_node_get (merge, &rnode->p);
+      if (mrnode->info != NULL)
+        continue;
+      mrnode->info = rnode->info;
+    }
+
+  isis_route_validate_table (area, merge);
+  route_table_finish (merge);
+}
+
+/* Walk through route tables and propagate necessary changes into RIB. In case
+ * of L1L2 area, level tables have to be merged at first. */
+void
+isis_route_validate (struct isis_area *area)
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (area->is_type == IS_LEVEL_1)
+    isis_route_validate_table (area, area->route_table[0]);
+  else if (area->is_type == IS_LEVEL_2)
+    isis_route_validate_table (area, area->route_table[1]);
+  else
+    isis_route_validate_merge (area, AF_INET);
+
+#ifdef HAVE_IPV6
+  if (area->is_type == IS_LEVEL_1)
+    isis_route_validate_table (area, area->route_table6[0]);
+  else if (area->is_type == IS_LEVEL_2)
+    isis_route_validate_table (area, area->route_table6[1]);
+  else
+    isis_route_validate_merge (area, AF_INET6);
+#endif
+
+  if (!area->circuit_list) {
+    return;
+  }
+  /* walk all circuits and reset any spf specific flags */
+  for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit))
+    UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
+
+  return;
+}
+
+void
+isis_route_invalidate_table (struct isis_area *area, struct route_table *table)
+{
+  struct route_node *rode;
+  struct isis_route_info *rinfo;
+  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);
+    }
+}
+
+void
+isis_route_invalidate (struct isis_area *area)
+{
+  if (area->is_type & IS_LEVEL_1)
+    isis_route_invalidate_table (area, area->route_table[0]);
+  if (area->is_type & IS_LEVEL_2)
+    isis_route_invalidate_table (area, area->route_table[1]);
+}
diff --git a/isisd/isis_route.h b/isisd/isis_route.h
new file mode 100644 (file)
index 0000000..0d2379c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * IS-IS Rout(e)ing protocol               - isis_route.h
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ *                                         based on ../ospf6d/ospf6_route.[ch]
+ *                                         by Yasuhiro Ohara
+ *
+ * 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_ROUTE_H
+#define _ZEBRA_ISIS_ROUTE_H
+
+#ifdef HAVE_IPV6
+struct isis_nexthop6
+{
+  ifindex_t ifindex;
+  struct in6_addr ip6;
+  struct in6_addr router_address6;
+  unsigned int lock;
+};
+#endif /* HAVE_IPV6 */
+
+struct isis_nexthop
+{
+  ifindex_t ifindex;
+  struct in_addr ip;
+  struct in_addr router_address;
+  unsigned int lock;
+};
+
+struct isis_route_info
+{
+#define ISIS_ROUTE_FLAG_ACTIVE       0x01  /* active route for the prefix */
+#define ISIS_ROUTE_FLAG_ZEBRA_SYNCED 0x02  /* set when route synced to zebra */
+#define ISIS_ROUTE_FLAG_ZEBRA_RESYNC 0x04  /* set when route needs to sync */
+  u_char flag;
+  u_int32_t cost;
+  u_int32_t depth;
+  struct list *nexthops;
+#ifdef HAVE_IPV6
+  struct list *nexthops6;
+#endif                         /* HAVE_IPV6 */
+};
+
+struct isis_route_info *isis_route_create (struct prefix *prefix,
+                                          u_int32_t cost, u_int32_t depth,
+                                          struct list *adjacencies,
+                                          struct isis_area *area, int level);
+
+void isis_route_validate (struct isis_area *area);
+void isis_route_invalidate_table (struct isis_area *area,
+                                  struct route_table *table);
+void isis_route_invalidate (struct isis_area *area);
+
+#endif /* _ZEBRA_ISIS_ROUTE_H */
diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c
new file mode 100644 (file)
index 0000000..3443a0a
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_routemap.c
+ *
+ * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program 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 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "filter.h"
+#include "hash.h"
+#include "if.h"
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "plist.h"
+#include "routemap.h"
+#include "table.h"
+#include "thread.h"
+#include "vty.h"
+
+#include "isis_constants.h"
+#include "isis_common.h"
+#include "isis_flags.h"
+#include "dict.h"
+#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_spf.h"
+#include "isis_route.h"
+#include "isis_zebra.h"
+#include "isis_routemap.h"
+
+static route_map_result_t
+route_match_ip_address(void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+
+  if (type != RMAP_ISIS)
+    return RMAP_NOMATCH;
+
+  alist = access_list_lookup(AFI_IP, (char*)rule);
+  if (access_list_apply(alist, prefix) != FILTER_DENY)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_compile(const char *arg)
+{
+  return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_free(void *rule)
+{
+  XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ip_address_cmd =
+{
+  "ip address",
+  route_match_ip_address,
+  route_match_ip_address_compile,
+  route_match_ip_address_free
+};
+
+/* ------------------------------------------------------------*/
+
+static route_map_result_t
+route_match_ip_address_prefix_list(void *rule, struct prefix *prefix,
+                                   route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type != RMAP_ISIS)
+    return RMAP_NOMATCH;
+
+  plist = prefix_list_lookup(AFI_IP, (char*)rule);
+  if (prefix_list_apply(plist, prefix) != PREFIX_DENY)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_prefix_list_compile(const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
+{
+  "ip address prefix-list",
+  route_match_ip_address_prefix_list,
+  route_match_ip_address_prefix_list_compile,
+  route_match_ip_address_prefix_list_free
+};
+
+/* ------------------------------------------------------------*/
+
+static route_map_result_t
+route_match_ipv6_address(void *rule, struct prefix *prefix,
+                         route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+
+  if (type != RMAP_ISIS)
+    return RMAP_NOMATCH;
+
+  alist = access_list_lookup(AFI_IP6, (char*)rule);
+  if (access_list_apply(alist, prefix) != FILTER_DENY)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ipv6_address_compile(const char *arg)
+{
+  return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ipv6_address_free(void *rule)
+{
+  XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ipv6_address_cmd =
+{
+  "ipv6 address",
+  route_match_ipv6_address,
+  route_match_ipv6_address_compile,
+  route_match_ipv6_address_free
+};
+
+/* ------------------------------------------------------------*/
+
+static route_map_result_t
+route_match_ipv6_address_prefix_list(void *rule, struct prefix *prefix,
+                                     route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type != RMAP_ISIS)
+    return RMAP_NOMATCH;
+
+  plist = prefix_list_lookup(AFI_IP6, (char*)rule);
+  if (prefix_list_apply(plist, prefix) != PREFIX_DENY)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ipv6_address_prefix_list_compile(const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ipv6_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd =
+{
+  "ipv6 address prefix-list",
+  route_match_ipv6_address_prefix_list,
+  route_match_ipv6_address_prefix_list_compile,
+  route_match_ipv6_address_prefix_list_free
+};
+
+/* ------------------------------------------------------------*/
+
+static route_map_result_t
+route_set_metric(void *rule, struct prefix *prefix,
+                 route_map_object_t type, void *object)
+{
+  uint32_t *metric;
+  struct isis_ext_info *info;
+
+  if (type == RMAP_ISIS)
+    {
+      metric = rule;
+      info = object;
+
+      info->metric = *metric;
+    }
+  return RMAP_OKAY;
+}
+
+static void *
+route_set_metric_compile(const char *arg)
+{
+  unsigned long metric;
+  char *endp;
+  uint32_t *ret;
+
+  metric = strtoul(arg, &endp, 10);
+  if (arg[0] == '\0' || *endp != '\0' || metric > MAX_WIDE_PATH_METRIC)
+    return NULL;
+
+  ret = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*ret));
+  *ret = metric;
+
+  return ret;
+}
+
+static void
+route_set_metric_free(void *rule)
+{
+  XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_set_metric_cmd =
+{
+  "metric",
+  route_set_metric,
+  route_set_metric_compile,
+  route_set_metric_free
+};
+
+/* ------------------------------------------------------------*/
+
+static int
+isis_route_match_add(struct vty *vty, struct route_map_index *index,
+                      const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+isis_route_match_delete(struct vty *vty, struct route_map_index *index,
+                        const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+isis_route_set_add(struct vty *vty, struct route_map_index *index,
+                   const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set(index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+isis_route_set_delete (struct vty *vty, struct route_map_index *index,
+                      const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* ------------------------------------------------------------*/
+
+DEFUN(match_ip_address,
+      match_ip_address_cmd,
+      "match ip address (<1-199>|<1300-2699>|WORD)",
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+      "IP access-list number\n"
+      "IP access-list number (expanded range)\n"
+      "IP Access-list name\n")
+{
+  return isis_route_match_add(vty, vty->index, "ip address", argv[0]);
+}
+
+DEFUN(no_match_ip_address,
+      no_match_ip_address_val_cmd,
+      "no match ip address (<1-199>|<1300-2699>|WORD)",
+      NO_STR
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+      "IP access-list number\n"
+      "IP access-list number (expanded range)\n"
+      "IP Access-list name\n")
+{
+  if (argc == 0)
+    return isis_route_match_delete(vty, vty->index, "ip address", NULL);
+  return isis_route_match_delete(vty, vty->index, "ip address", argv[0]);
+}
+
+ALIAS(no_match_ip_address,
+      no_match_ip_address_cmd,
+      "no match ip address",
+      NO_STR
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+);
+
+/* ------------------------------------------------------------*/
+
+DEFUN(match_ip_address_prefix_list,
+      match_ip_address_prefix_list_cmd,
+      "match ip address prefix-list WORD",
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n"
+      "IP prefix-list name\n")
+{
+  return isis_route_match_add(vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+DEFUN(no_match_ip_address_prefix_list,
+      no_match_ip_address_prefix_list_cmd,
+      "no match ip address prefix-list",
+      NO_STR
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return isis_route_match_delete (vty, vty->index, "ip address prefix-list", NULL);
+  return isis_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+ALIAS(no_match_ip_address_prefix_list,
+      no_match_ip_address_prefix_list_val_cmd,
+      "no match ip address prefix-list WORD",
+      NO_STR
+      MATCH_STR
+      IP_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n"
+      "IP prefix-list name\n"
+);
+
+/* ------------------------------------------------------------*/
+
+DEFUN(match_ipv6_address,
+      match_ipv6_address_cmd,
+      "match ipv6 address WORD",
+      MATCH_STR
+      IPV6_STR
+      "Match IPv6 address of route\n"
+      "IPv6 access-list name\n")
+{
+  return isis_route_match_add(vty, vty->index, "ipv6 address", argv[0]);
+}
+
+DEFUN(no_match_ipv6_address,
+      no_match_ipv6_address_val_cmd,
+      "no match ipv6 address WORD",
+      NO_STR
+      MATCH_STR
+      IPV6_STR
+      "Match IPv6 address of route\n"
+      "IPv6 access-list name\n")
+{
+  if (argc == 0)
+    return isis_route_match_delete(vty, vty->index, "ipv6 address", NULL);
+  return isis_route_match_delete(vty, vty->index, "ipv6 address", argv[0]);
+}
+
+ALIAS(no_match_ipv6_address,
+      no_match_ipv6_address_cmd,
+      "no match ipv6 address",
+      NO_STR
+      MATCH_STR
+      IPV6_STR
+      "Match IPv6 address of route\n"
+);
+
+/* ------------------------------------------------------------*/
+
+DEFUN(match_ipv6_address_prefix_list,
+      match_ipv6_address_prefix_list_cmd,
+      "match ipv6 address prefix-list WORD",
+      MATCH_STR
+      IPV6_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n"
+      "IP prefix-list name\n")
+{
+  return isis_route_match_add(vty, vty->index, "ipv6 address prefix-list", argv[0]);
+}
+
+DEFUN(no_match_ipv6_address_prefix_list,
+      no_match_ipv6_address_prefix_list_cmd,
+      "no match ipv6 address prefix-list",
+      NO_STR
+      MATCH_STR
+      IPV6_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", NULL);
+  return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]);
+}
+
+ALIAS(no_match_ipv6_address_prefix_list,
+      no_match_ipv6_address_prefix_list_val_cmd,
+      "no match ipv6 address prefix-list WORD",
+      NO_STR
+      MATCH_STR
+      IPV6_STR
+      "Match address of route\n"
+      "Match entries of prefix-lists\n"
+      "IP prefix-list name\n"
+);
+
+/* ------------------------------------------------------------*/
+
+/* set metric already exists e.g. in the ospf routemap. vtysh doesn't cope well with different
+ * commands at the same node, therefore add set metric with the same 32-bit range as ospf and
+ * verify that the input is a valid isis metric */
+DEFUN(set_metric,
+      set_metric_cmd,
+      "set metric <0-4294967295>",
+      SET_STR
+      "Metric vale for destination routing protocol\n"
+      "Metric value\n")
+{
+  return isis_route_set_add(vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN(no_set_metric,
+      no_set_metric_val_cmd,
+      "no set metric <0-4294967295>",
+      NO_STR
+      SET_STR
+      "Metric value for destination routing protocol\n"
+      "Metric value\n")
+{
+  if (argc == 0)
+    return isis_route_set_delete(vty, vty->index, "metric", NULL);
+  return isis_route_set_delete(vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS(no_set_metric,
+      no_set_metric_cmd,
+      "no set metric",
+      NO_STR
+      SET_STR
+      "Metric vale for destination routing protocol\n"
+);
+
+void
+isis_route_map_init(void)
+{
+  route_map_init();
+  route_map_init_vty();
+
+  route_map_install_match(&route_match_ip_address_cmd);
+  install_element(RMAP_NODE, &match_ip_address_cmd);
+  install_element(RMAP_NODE, &no_match_ip_address_val_cmd);
+  install_element(RMAP_NODE, &no_match_ip_address_cmd);
+
+  route_map_install_match(&route_match_ip_address_prefix_list_cmd);
+  install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd);
+  install_element(RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd);
+  install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
+
+  route_map_install_match(&route_match_ipv6_address_cmd);
+  install_element(RMAP_NODE, &match_ipv6_address_cmd);
+  install_element(RMAP_NODE, &no_match_ipv6_address_val_cmd);
+  install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
+
+  route_map_install_match(&route_match_ipv6_address_prefix_list_cmd);
+  install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
+  install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_val_cmd);
+  install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
+
+  route_map_install_set(&route_set_metric_cmd);
+  install_element(RMAP_NODE, &set_metric_cmd);
+  install_element(RMAP_NODE, &no_set_metric_val_cmd);
+  install_element(RMAP_NODE, &no_set_metric_cmd);
+}
diff --git a/isisd/isis_routemap.h b/isisd/isis_routemap.h
new file mode 100644 (file)
index 0000000..1cb063f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_routemap.h
+ *
+ * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program 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 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_ROUTEMAP_H
+#define ISIS_ROUTEMAP_H
+
+void isis_route_map_init(void);
+
+#endif
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
new file mode 100644 (file)
index 0000000..6b2456f
--- /dev/null
@@ -0,0 +1,1695 @@
+/*
+ * IS-IS Rout(e)ing protocol                  - isis_spf.c
+ *                                              The SPT algorithm
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.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 "isis_constants.h"
+#include "isis_common.h"
+#include "isis_flags.h"
+#include "dict.h"
+#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"
+
+int isis_run_spf_l1 (struct thread *thread);
+int isis_run_spf_l2 (struct thread *thread);
+
+/* 7.2.7 */
+static void
+remove_excess_adjs (struct list *adjs)
+{
+  struct listnode *node, *excess = NULL;
+  struct isis_adjacency *adj, *candidate = NULL;
+  int comp;
+
+  for (ALL_LIST_ELEMENTS_RO (adjs, node, adj)) 
+    {
+      if (excess == NULL)
+       excess = node;
+      candidate = listgetdata (excess);
+
+      if (candidate->sys_type < adj->sys_type)
+       {
+         excess = node;
+         candidate = adj;
+         continue;
+       }
+      if (candidate->sys_type > adj->sys_type)
+       continue;
+
+      comp = memcmp (candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN);
+      if (comp > 0)
+       {
+         excess = node;
+         candidate = adj;
+         continue;
+       }
+      if (comp < 0)
+       continue;
+
+      if (candidate->circuit->circuit_id > adj->circuit->circuit_id)
+       {
+         excess = node;
+         candidate = adj;
+         continue;
+       }
+
+      if (candidate->circuit->circuit_id < adj->circuit->circuit_id)
+       continue;
+
+      comp = memcmp (candidate->snpa, adj->snpa, ETH_ALEN);
+      if (comp > 0)
+       {
+         excess = node;
+         candidate = adj;
+         continue;
+       }
+    }
+
+  list_delete_node (adjs, excess);
+
+  return;
+}
+
+static const char *
+vtype2string (enum vertextype vtype)
+{
+  switch (vtype)
+    {
+    case VTYPE_PSEUDO_IS:
+      return "pseudo_IS";
+      break;
+    case VTYPE_PSEUDO_TE_IS:
+      return "pseudo_TE-IS";
+      break;
+    case VTYPE_NONPSEUDO_IS:
+      return "IS";
+      break;
+    case VTYPE_NONPSEUDO_TE_IS:
+      return "TE-IS";
+      break;
+    case VTYPE_ES:
+      return "ES";
+      break;
+    case VTYPE_IPREACH_INTERNAL:
+      return "IP internal";
+      break;
+    case VTYPE_IPREACH_EXTERNAL:
+      return "IP external";
+      break;
+    case VTYPE_IPREACH_TE:
+      return "IP TE";
+      break;
+#ifdef HAVE_IPV6
+    case VTYPE_IP6REACH_INTERNAL:
+      return "IP6 internal";
+      break;
+    case VTYPE_IP6REACH_EXTERNAL:
+      return "IP6 external";
+      break;
+#endif /* HAVE_IPV6 */
+    default:
+      return "UNKNOWN";
+    }
+  return NULL;                 /* Not reached */
+}
+
+static const char *
+vid2string (struct isis_vertex *vertex, u_char * buff)
+{
+  switch (vertex->type)
+    {
+    case VTYPE_PSEUDO_IS:
+    case VTYPE_PSEUDO_TE_IS:
+      return print_sys_hostname (vertex->N.id);
+      break;
+    case VTYPE_NONPSEUDO_IS:
+    case VTYPE_NONPSEUDO_TE_IS:
+    case VTYPE_ES:
+      return print_sys_hostname (vertex->N.id);
+      break;
+    case VTYPE_IPREACH_INTERNAL:
+    case VTYPE_IPREACH_EXTERNAL:
+    case VTYPE_IPREACH_TE:
+#ifdef HAVE_IPV6
+    case VTYPE_IP6REACH_INTERNAL:
+    case VTYPE_IP6REACH_EXTERNAL:
+#endif /* HAVE_IPV6 */
+      prefix2str ((struct prefix *) &vertex->N.prefix, (char *) buff, BUFSIZ);
+      break;
+    default:
+      return "UNKNOWN";
+    }
+
+  return (char *) buff;
+}
+
+static struct isis_vertex *
+isis_vertex_new (void *id, enum vertextype vtype)
+{
+  struct isis_vertex *vertex;
+
+  vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex));
+
+  vertex->type = vtype;
+  switch (vtype)
+    {
+    case VTYPE_ES:
+    case VTYPE_NONPSEUDO_IS:
+    case VTYPE_NONPSEUDO_TE_IS:
+      memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN);
+      break;
+    case VTYPE_PSEUDO_IS:
+    case VTYPE_PSEUDO_TE_IS:
+      memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1);
+      break;
+    case VTYPE_IPREACH_INTERNAL:
+    case VTYPE_IPREACH_EXTERNAL:
+    case VTYPE_IPREACH_TE:
+#ifdef HAVE_IPV6
+    case VTYPE_IP6REACH_INTERNAL:
+    case VTYPE_IP6REACH_EXTERNAL:
+#endif /* HAVE_IPV6 */
+      memcpy (&vertex->N.prefix, (struct prefix *) id,
+             sizeof (struct prefix));
+      break;
+    default:
+      zlog_err ("WTF!");
+    }
+
+  vertex->Adj_N = list_new ();
+  vertex->parents = list_new ();
+  vertex->children = list_new ();
+
+  return vertex;
+}
+
+static void
+isis_vertex_del (struct isis_vertex *vertex)
+{
+  list_delete (vertex->Adj_N);
+  vertex->Adj_N = NULL;
+  list_delete (vertex->parents);
+  vertex->parents = NULL;
+  list_delete (vertex->children);
+  vertex->children = NULL;
+
+  memset(vertex, 0, sizeof(struct isis_vertex));
+  XFREE (MTYPE_ISIS_VERTEX, vertex);
+
+  return;
+}
+
+static void
+isis_vertex_adj_del (struct isis_vertex *vertex, struct isis_adjacency *adj)
+{
+  struct listnode *node, *nextnode;
+  if (!vertex)
+    return;
+  for (node = listhead (vertex->Adj_N); node; node = nextnode)
+  {
+    nextnode = listnextnode(node);
+    if (listgetdata(node) == adj)
+      list_delete_node(vertex->Adj_N, node);
+  }
+  return;
+}
+
+struct isis_spftree *
+isis_spftree_new (struct isis_area *area)
+{
+  struct isis_spftree *tree;
+
+  tree = XCALLOC (MTYPE_ISIS_SPFTREE, sizeof (struct isis_spftree));
+  if (tree == NULL)
+    {
+      zlog_err ("ISIS-Spf: isis_spftree_new Out of memory!");
+      return NULL;
+    }
+
+  tree->tents = list_new ();
+  tree->paths = list_new ();
+  tree->area = area;
+  tree->last_run_timestamp = 0;
+  tree->last_run_duration = 0;
+  tree->runcount = 0;
+  tree->pending = 0;
+  return tree;
+}
+
+void
+isis_spftree_del (struct isis_spftree *spftree)
+{
+  THREAD_TIMER_OFF (spftree->t_spf);
+
+  spftree->tents->del = (void (*)(void *)) isis_vertex_del;
+  list_delete (spftree->tents);
+  spftree->tents = NULL;
+
+  spftree->paths->del = (void (*)(void *)) isis_vertex_del;
+  list_delete (spftree->paths);
+  spftree->paths = NULL;
+
+  XFREE (MTYPE_ISIS_SPFTREE, spftree);
+
+  return;
+}
+
+void
+isis_spftree_adj_del (struct isis_spftree *spftree, struct isis_adjacency *adj)
+{
+  struct listnode *node;
+  if (!adj)
+    return;
+  for (node = listhead (spftree->tents); node; node = listnextnode (node))
+    isis_vertex_adj_del (listgetdata (node), adj);
+  for (node = listhead (spftree->paths); node; node = listnextnode (node))
+    isis_vertex_adj_del (listgetdata (node), adj);
+  return;
+}
+
+void
+spftree_area_init (struct isis_area *area)
+{
+  if (area->is_type & IS_LEVEL_1)
+  {
+    if (area->spftree[0] == NULL)
+      area->spftree[0] = isis_spftree_new (area);
+#ifdef HAVE_IPV6
+    if (area->spftree6[0] == NULL)
+      area->spftree6[0] = isis_spftree_new (area);
+#endif
+  }
+
+  if (area->is_type & IS_LEVEL_2)
+  {
+    if (area->spftree[1] == NULL)
+      area->spftree[1] = isis_spftree_new (area);
+#ifdef HAVE_IPV6
+    if (area->spftree6[1] == NULL)
+      area->spftree6[1] = isis_spftree_new (area);
+#endif
+  }
+
+  return;
+}
+
+void
+spftree_area_del (struct isis_area *area)
+{
+  if (area->is_type & IS_LEVEL_1)
+  {
+    if (area->spftree[0] != NULL)
+    {
+      isis_spftree_del (area->spftree[0]);
+      area->spftree[0] = NULL;
+    }
+#ifdef HAVE_IPV6
+    if (area->spftree6[0])
+    {
+      isis_spftree_del (area->spftree6[0]);
+      area->spftree6[0] = NULL;
+    }
+#endif
+  }
+
+  if (area->is_type & IS_LEVEL_2)
+  {
+    if (area->spftree[1] != NULL)
+    {
+      isis_spftree_del (area->spftree[1]);
+      area->spftree[1] = NULL;
+    }
+#ifdef HAVE_IPV6
+    if (area->spftree6[1] != NULL)
+    {
+      isis_spftree_del (area->spftree6[1]);
+      area->spftree6[1] = NULL;
+    }
+#endif
+  }
+
+  return;
+}
+
+void
+spftree_area_adj_del (struct isis_area *area, struct isis_adjacency *adj)
+{
+  if (area->is_type & IS_LEVEL_1)
+  {
+    if (area->spftree[0] != NULL)
+      isis_spftree_adj_del (area->spftree[0], adj);
+#ifdef HAVE_IPV6
+    if (area->spftree6[0] != NULL)
+      isis_spftree_adj_del (area->spftree6[0], adj);
+#endif
+  }
+
+  if (area->is_type & IS_LEVEL_2)
+  {
+    if (area->spftree[1] != NULL)
+      isis_spftree_adj_del (area->spftree[1], adj);
+#ifdef HAVE_IPV6
+    if (area->spftree6[1] != NULL)
+      isis_spftree_adj_del (area->spftree6[1], adj);
+#endif
+  }
+
+  return;
+}
+
+/* 
+ * 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)
+{
+  struct isis_lsp *lsp;
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+
+  memcpy (lspid, sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (lspid) = 0;
+  LSP_FRAGMENT (lspid) = 0;
+  lsp = lsp_search (lspid, area->lspdb[level - 1]);
+  if (lsp && lsp->lsp_header->rem_lifetime != 0)
+    return lsp;
+  return NULL;
+}
+
+/*
+ * Add this IS to the root of SPT
+ */
+static struct isis_vertex *
+isis_spf_add_root (struct isis_spftree *spftree, int level, u_char *sysid)
+{
+  struct isis_vertex *vertex;
+  struct isis_lsp *lsp;
+#ifdef EXTREME_DEBUG
+  u_char buff[BUFSIZ];
+#endif /* EXTREME_DEBUG */
+
+  lsp = isis_root_system_lsp (spftree->area, level, sysid);
+  if (lsp == NULL)
+    zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
+
+  if (!spftree->area->oldmetric)
+    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS);
+  else
+    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS);
+
+  listnode_add (spftree->paths, vertex);
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("ISIS-Spf: added this IS  %s %s depth %d dist %d to PATHS",
+             vtype2string (vertex->type), vid2string (vertex, buff),
+             vertex->depth, vertex->d_N);
+#endif /* EXTREME_DEBUG */
+
+  return vertex;
+}
+
+static struct isis_vertex *
+isis_find_vertex (struct list *list, void *id, enum vertextype vtype)
+{
+  struct listnode *node;
+  struct isis_vertex *vertex;
+  struct prefix *p1, *p2;
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, vertex))
+    {
+      if (vertex->type != vtype)
+       continue;
+      switch (vtype)
+       {
+       case VTYPE_ES:
+       case VTYPE_NONPSEUDO_IS:
+       case VTYPE_NONPSEUDO_TE_IS:
+         if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0)
+           return vertex;
+         break;
+       case VTYPE_PSEUDO_IS:
+       case VTYPE_PSEUDO_TE_IS:
+         if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0)
+           return vertex;
+         break;
+       case VTYPE_IPREACH_INTERNAL:
+       case VTYPE_IPREACH_EXTERNAL:
+       case VTYPE_IPREACH_TE:
+#ifdef HAVE_IPV6
+       case VTYPE_IP6REACH_INTERNAL:
+       case VTYPE_IP6REACH_EXTERNAL:
+#endif /* HAVE_IPV6 */
+         p1 = (struct prefix *) id;
+         p2 = (struct prefix *) &vertex->N.id;
+         if (p1->family == p2->family && p1->prefixlen == p2->prefixlen &&
+             memcmp (&p1->u.prefix, &p2->u.prefix,
+                     PSIZE (p1->prefixlen)) == 0)
+           return vertex;
+         break;
+       }
+    }
+
+  return NULL;
+}
+
+/*
+ * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
+ */
+static struct isis_vertex *
+isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
+                  void *id, uint32_t cost, int depth, int family,
+                  struct isis_adjacency *adj, struct isis_vertex *parent)
+{
+  struct isis_vertex *vertex, *v;
+  struct listnode *node;
+  struct isis_adjacency *parent_adj;
+#ifdef EXTREME_DEBUG
+  u_char buff[BUFSIZ];
+#endif
+
+  assert (isis_find_vertex (spftree->paths, id, vtype) == NULL);
+  assert (isis_find_vertex (spftree->tents, id, vtype) == NULL);
+  vertex = isis_vertex_new (id, vtype);
+  vertex->d_N = cost;
+  vertex->depth = depth;
+
+  if (parent) {
+    listnode_add (vertex->parents, parent);
+    if (listnode_lookup (parent->children, vertex) == NULL)
+      listnode_add (parent->children, vertex);
+  }
+
+  if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
+    for (ALL_LIST_ELEMENTS_RO (parent->Adj_N, node, parent_adj))
+      listnode_add (vertex->Adj_N, parent_adj);
+  } else if (adj) {
+    listnode_add (vertex->Adj_N, adj);
+  }
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d",
+              print_sys_hostname (vertex->N.id),
+             vtype2string (vertex->type), vid2string (vertex, buff),
+             vertex->depth, vertex->d_N, listcount(vertex->Adj_N));
+#endif /* EXTREME_DEBUG */
+
+  if (list_isempty (spftree->tents))
+    {
+      listnode_add (spftree->tents, vertex);
+      return vertex;
+    }
+
+  /* XXX: This cant use the standard ALL_LIST_ELEMENTS macro */
+  for (node = listhead (spftree->tents); node; node = listnextnode (node))
+    {
+      v = listgetdata (node);
+      if (v->d_N > vertex->d_N)
+       {
+         list_add_node_prev (spftree->tents, node, vertex);
+         break;
+       }
+      else if (v->d_N == vertex->d_N && v->type > vertex->type)
+       {
+         /*  Tie break, add according to type */
+          list_add_node_prev (spftree->tents, node, vertex);
+         break;
+       }
+    }
+
+  if (node == NULL)
+      listnode_add (spftree->tents, vertex);
+
+  return vertex;
+}
+
+static void
+isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
+                   void *id, struct isis_adjacency *adj, uint32_t cost,
+                   int family, struct isis_vertex *parent)
+{
+  struct isis_vertex *vertex;
+
+  vertex = isis_find_vertex (spftree->tents, id, vtype);
+
+  if (vertex)
+    {
+      /* C.2.5   c) */
+      if (vertex->d_N == cost)
+       {
+         if (adj)
+           listnode_add (vertex->Adj_N, adj);
+         /*       d) */
+         if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
+           remove_excess_adjs (vertex->Adj_N);
+         if (parent && (listnode_lookup (vertex->parents, parent) == NULL))
+           listnode_add (vertex->parents, parent);
+         if (parent && (listnode_lookup (parent->children, vertex) == NULL))
+           listnode_add (parent->children, vertex);
+         return;
+       }
+      else if (vertex->d_N < cost)
+       {
+         /*       e) do nothing */
+         return;
+       }
+      else {  /* vertex->d_N > cost */
+         /*         f) */
+         struct listnode *pnode, *pnextnode;
+         struct isis_vertex *pvertex;
+         listnode_delete (spftree->tents, vertex);
+         assert (listcount (vertex->children) == 0);
+         for (ALL_LIST_ELEMENTS (vertex->parents, pnode, pnextnode, pvertex))
+           listnode_delete(pvertex->children, vertex);
+         isis_vertex_del (vertex);
+      }
+    }
+
+  isis_spf_add2tent (spftree, vtype, id, cost, 1, family, adj, parent);
+  return;
+}
+
+static void
+process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+          uint32_t dist, uint16_t depth, int family,
+          struct isis_vertex *parent)
+{
+  struct isis_vertex *vertex;
+#ifdef EXTREME_DEBUG
+  u_char buff[255];
+#endif
+
+  assert (spftree && parent);
+
+  /* RFC3787 section 5.1 */
+  if (spftree->area->newmetric == 1)
+    {
+      if (dist > MAX_WIDE_PATH_METRIC)
+        return;
+    }
+  /* C.2.6 b)    */
+  else if (spftree->area->oldmetric == 1)
+    {
+      if (dist > MAX_NARROW_PATH_METRIC)
+        return;
+    }
+
+  /*       c)    */
+  vertex = isis_find_vertex (spftree->paths, id, vtype);
+  if (vertex)
+    {
+#ifdef EXTREME_DEBUG
+      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);
+      return;
+    }
+
+  vertex = isis_find_vertex (spftree->tents, id, vtype);
+  /*       d)    */
+  if (vertex)
+    {
+      /*        1) */
+#ifdef EXTREME_DEBUG
+      zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d",
+                 print_sys_hostname (vertex->N.id),
+                  vtype2string (vtype), vid2string (vertex, buff), dist,
+                  (parent ? print_sys_hostname (parent->N.id) : "null"),
+                  (parent ? listcount (parent->Adj_N) : 0));
+#endif /* EXTREME_DEBUG */
+      if (vertex->d_N == dist)
+       {
+         struct listnode *node;
+         struct isis_adjacency *parent_adj;
+         for (ALL_LIST_ELEMENTS_RO (parent->Adj_N, node, parent_adj))
+           if (listnode_lookup(vertex->Adj_N, parent_adj) == NULL)
+             listnode_add (vertex->Adj_N, parent_adj);
+         /*      2) */
+         if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
+           remove_excess_adjs (vertex->Adj_N);
+         if (listnode_lookup (vertex->parents, parent) == NULL)
+           listnode_add (vertex->parents, parent);
+         if (listnode_lookup (parent->children, vertex) == NULL)
+           listnode_add (parent->children, vertex);
+         /*      3) */
+         return;
+       }
+      else if (vertex->d_N < dist)
+       {
+         return;
+         /*      4) */
+       }
+      else
+       {
+         struct listnode *pnode, *pnextnode;
+         struct isis_vertex *pvertex;
+         listnode_delete (spftree->tents, vertex);
+         assert (listcount (vertex->children) == 0);
+         for (ALL_LIST_ELEMENTS (vertex->parents, pnode, pnextnode, pvertex))
+           listnode_delete(pvertex->children, vertex);
+         isis_vertex_del (vertex);
+       }
+    }
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("ISIS-Spf: process_N add2tent %s %s dist %d parent %s",
+              print_sys_hostname(id), vtype2string (vtype), dist,
+              (parent ? print_sys_hostname (parent->N.id) : "null"));
+#endif /* EXTREME_DEBUG */
+
+  isis_spf_add2tent (spftree, vtype, id, dist, depth, family, NULL, parent);
+  return;
+}
+
+/*
+ * C.2.6 Step 1
+ */
+static int
+isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
+                     uint32_t cost, uint16_t depth, int family,
+                     u_char *root_sysid, struct isis_vertex *parent)
+{
+  struct listnode *node, *fragnode = NULL;
+  uint32_t dist;
+  struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
+  struct ipv4_reachability *ipreach;
+  struct te_ipv4_reachability *te_ipv4_reach;
+  enum vertextype vtype;
+  struct prefix prefix;
+#ifdef HAVE_IPV6
+  struct ipv6_reachability *ip6reach;
+#endif /* HAVE_IPV6 */
+  static const u_char null_sysid[ISIS_SYS_ID_LEN];
+
+  if (!speaks (lsp->tlv_data.nlpids, family))
+    return ISIS_OK;
+
+lspfragloop:
+  if (lsp->lsp_header->seq_num == 0)
+    {
+      zlog_warn ("isis_spf_process_lsp(): lsp with 0 seq_num - ignore");
+      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)
+    {
+      for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
+      {
+        /* C.2.6 a) */
+        /* Two way connectivity */
+        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, family, parent);
+      }
+    }
+    if (lsp->tlv_data.te_is_neighs)
+    {
+      for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node,
+            te_is_neigh))
+      {
+        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;
+        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, family, parent);
+      }
+    }
+  }
+
+  if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
+  {
+    prefix.family = AF_INET;
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, node, ipreach))
+    {
+      dist = cost + ipreach->metrics.metric_default;
+      vtype = VTYPE_IPREACH_INTERNAL;
+      prefix.u.prefix4 = ipreach->prefix;
+      prefix.prefixlen = ip_masklen (ipreach->mask);
+      apply_mask (&prefix);
+      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+                 family, parent);
+    }
+  }
+  if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs)
+  {
+    prefix.family = AF_INET;
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, node, ipreach))
+    {
+      dist = cost + ipreach->metrics.metric_default;
+      vtype = VTYPE_IPREACH_EXTERNAL;
+      prefix.u.prefix4 = ipreach->prefix;
+      prefix.prefixlen = ip_masklen (ipreach->mask);
+      apply_mask (&prefix);
+      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+                 family, parent);
+    }
+  }
+  if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs)
+  {
+    prefix.family = AF_INET;
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs,
+                               node, te_ipv4_reach))
+    {
+      assert ((te_ipv4_reach->control & 0x3F) <= IPV4_MAX_BITLEN);
+
+      dist = cost + ntohl (te_ipv4_reach->te_metric);
+      vtype = VTYPE_IPREACH_TE;
+      prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start,
+                                           te_ipv4_reach->control);
+      prefix.prefixlen = (te_ipv4_reach->control & 0x3F);
+      apply_mask (&prefix);
+      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+                 family, parent);
+    }
+  }
+#ifdef HAVE_IPV6
+  if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs)
+  {
+    prefix.family = AF_INET6;
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, node, ip6reach))
+    {
+      assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN);
+
+      dist = cost + ntohl(ip6reach->metric);
+      vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ?
+        VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL;
+      prefix.prefixlen = ip6reach->prefix_len;
+      memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
+              PSIZE (ip6reach->prefix_len));
+      apply_mask (&prefix);
+      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+                 family, parent);
+    }
+  }
+#endif /* HAVE_IPV6 */
+
+  if (fragnode == NULL)
+    fragnode = listhead (lsp->lspu.frags);
+  else
+    fragnode = listnextnode (fragnode);
+
+  if (fragnode)
+    {
+      lsp = listgetdata (fragnode);
+      goto lspfragloop;
+    }
+
+  return ISIS_OK;
+}
+
+static int
+isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
+                            struct isis_lsp *lsp, uint32_t cost,
+                            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;
+  uint32_t dist;
+
+pseudofragloop:
+
+  if (lsp->lsp_header->seq_num == 0)
+    {
+      zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num"
+                " - do not process");
+      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 */
+
+  /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
+
+  if (lsp->tlv_data.is_neighs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
+      {
+       /* Two way connectivity */
+       if (!memcmp (is_neigh->neigh_id, root_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, family, parent);
+      }
+  if (lsp->tlv_data.te_is_neighs)
+    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh))
+      {
+       /* Two way connectivity */
+       if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
+         continue;
+        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, family, parent);
+      }
+
+  if (fragnode == NULL)
+    fragnode = listhead (lsp->lspu.frags);
+  else
+    fragnode = listnextnode (fragnode);
+
+  if (fragnode)
+    {
+      lsp = listgetdata (fragnode);
+      goto pseudofragloop;
+    }
+
+  return ISIS_OK;
+}
+
+static int
+isis_spf_preload_tent (struct isis_spftree *spftree, int level,
+                      int family, u_char *root_sysid,
+                      struct isis_vertex *parent)
+{
+  struct isis_circuit *circuit;
+  struct listnode *cnode, *anode, *ipnode;
+  struct isis_adjacency *adj;
+  struct isis_lsp *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 */
+
+  for (ALL_LIST_ELEMENTS_RO (spftree->area->circuit_list, cnode, circuit))
+    {
+      if (circuit->state != C_STATE_UP)
+       continue;
+      if (!(circuit->is_type & level))
+       continue;
+      if (family == AF_INET && !circuit->ip_router)
+       continue;
+#ifdef HAVE_IPV6
+      if (family == AF_INET6 && !circuit->ipv6_router)
+       continue;
+#endif /* HAVE_IPV6 */
+      /* 
+       * Add IP(v6) addresses of this circuit
+       */
+      if (family == AF_INET)
+       {
+         prefix.family = AF_INET;
+          for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4))
+           {
+             prefix.u.prefix4 = ipv4->prefix;
+             prefix.prefixlen = ipv4->prefixlen;
+              apply_mask (&prefix);
+             isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix,
+                                 NULL, 0, family, parent);
+           }
+       }
+#ifdef HAVE_IPV6
+      if (family == AF_INET6)
+       {
+         prefix.family = AF_INET6;
+         for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6))
+           {
+             prefix.prefixlen = ipv6->prefixlen;
+             prefix.u.prefix6 = ipv6->prefix;
+              apply_mask (&prefix);
+             isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL,
+                                 &prefix, NULL, 0, family, parent);
+           }
+       }
+#endif /* HAVE_IPV6 */
+      if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+       {
+         /*
+          * Add the adjacencies
+          */
+         adj_list = list_new ();
+         adjdb = circuit->u.bc.adjdb[level - 1];
+         isis_adj_build_up_list (adjdb, adj_list);
+         if (listcount (adj_list) == 0)
+           {
+             list_delete (adj_list);
+             if (isis->debugs & DEBUG_SPF_EVENTS)
+               zlog_debug ("ISIS-Spf: no L%d adjacencies on circuit %s",
+                           level, circuit->interface->name);
+             continue;
+           }
+          for (ALL_LIST_ELEMENTS_RO (adj_list, anode, adj))
+           {
+             if (!speaks (&adj->nlpids, family))
+                 continue;
+             switch (adj->sys_type)
+               {
+               case ISIS_SYSTYPE_ES:
+                 isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
+                                     circuit->te_metric[level - 1],
+                                     family, parent);
+                 break;
+               case ISIS_SYSTYPE_IS:
+               case ISIS_SYSTYPE_L1_IS:
+               case ISIS_SYSTYPE_L2_IS:
+                 isis_spf_add_local (spftree,
+                                      spftree->area->oldmetric ?
+                                      VTYPE_NONPSEUDO_IS :
+                                      VTYPE_NONPSEUDO_TE_IS,
+                                      adj->sysid, adj,
+                                      circuit->te_metric[level - 1],
+                                      family, parent);
+                 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, spftree->area->lspdb[level - 1]);
+                  if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
+                    zlog_warn ("ISIS-Spf: No LSP %s found for IS adjacency "
+                        "L%d on %s (ID %u)",
+                       rawlspid_print (lsp_id), level,
+                       circuit->interface->name, circuit->circuit_id);
+                 break;
+               case ISIS_SYSTYPE_UNKNOWN:
+               default:
+                 zlog_warn ("isis_spf_preload_tent unknow adj type");
+               }
+           }
+         list_delete (adj_list);
+         /*
+          * Add the pseudonode 
+          */
+         if (level == 1)
+           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;
+           }
+         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 from root "
+                  "to L%d DR %s on %s (ID %d)",
+                 level, rawlspid_print (lsp_id),
+                 circuit->interface->name, circuit->circuit_id);
+              continue;
+           }
+         lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]);
+         if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
+           {
+             zlog_warn ("ISIS-Spf: No lsp (%p) found from root "
+                  "to L%d DR %s on %s (ID %d)",
+                  (void *)lsp, level, rawlspid_print (lsp_id),
+                  circuit->interface->name, circuit->circuit_id);
+              continue;
+           }
+         isis_spf_process_pseudo_lsp (spftree, lsp,
+                                       circuit->te_metric[level - 1], 0,
+                                       family, root_sysid, parent);
+       }
+      else if (circuit->circ_type == CIRCUIT_T_P2P)
+       {
+         adj = circuit->u.p2p.neighbor;
+         if (!adj)
+           continue;
+         switch (adj->sys_type)
+           {
+           case ISIS_SYSTYPE_ES:
+             isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
+                                 circuit->te_metric[level - 1], family,
+                                 parent);
+             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,
+                                   spftree->area->oldmetric ?
+                                    VTYPE_NONPSEUDO_IS :
+                                   VTYPE_NONPSEUDO_TE_IS,
+                                    adj->sysid,
+                                   adj, circuit->te_metric[level - 1],
+                                   family, parent);
+             break;
+           case ISIS_SYSTYPE_UNKNOWN:
+           default:
+             zlog_warn ("isis_spf_preload_tent unknown adj type");
+             break;
+           }
+       }
+      else if (circuit->circ_type == CIRCUIT_T_LOOPBACK)
+       {
+          continue;
+        }
+      else
+       {
+         zlog_warn ("isis_spf_preload_tent unsupported media");
+         retval = ISIS_WARNING;
+       }
+    }
+
+  return retval;
+}
+
+/*
+ * The parent(s) for vertex is set when added to TENT list
+ * now we just put the child pointer(s) in place
+ */
+static void
+add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex,
+             int level)
+{
+  u_char buff[BUFSIZ];
+
+  if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
+    return;
+  listnode_add (spftree->paths, vertex);
+
+#ifdef EXTREME_DEBUG
+  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 */
+
+  if (vertex->type > VTYPE_ES)
+    {
+      if (listcount (vertex->Adj_N) > 0)
+       isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N,
+                          vertex->depth, vertex->Adj_N, spftree->area, level);
+      else if (isis->debugs & DEBUG_SPF_EVENTS)
+       zlog_debug ("ISIS-Spf: no adjacencies do not install route for "
+                    "%s depth %d dist %d", vid2string (vertex, buff),
+                    vertex->depth, vertex->d_N);
+    }
+
+  return;
+}
+
+static void
+init_spt (struct isis_spftree *spftree)
+{
+  spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del;
+  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, u_char *sysid)
+{
+  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;
+  struct route_table *table = NULL;
+  struct timeval time_now;
+  unsigned long long start_time, end_time;
+
+  /* Get time that can't roll backwards. */
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now);
+  start_time = time_now.tv_sec;
+  start_time = (start_time * 1000000) + time_now.tv_usec;
+
+  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_IPV6
+  else if (family == AF_INET6)
+    table = area->route_table6[level - 1];
+#endif
+
+  isis_route_invalidate_table (area, table);
+
+  /*
+   * C.2.5 Step 0
+   */
+  init_spt (spftree);
+  /*              a) */
+  root_vertex = isis_spf_add_root (spftree, level, sysid);
+  /*              b) */
+  retval = isis_spf_preload_tent (spftree, 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 SPF-root:%s", print_sys_hostname(sysid));
+      goto out;
+    }
+
+  while (listcount (spftree->tents) > 0)
+    {
+      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 and add to paths list */
+      list_delete_node (spftree->tents, node);
+      add_to_paths (spftree, vertex, level);
+      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]);
+         if (lsp && lsp->lsp_header->rem_lifetime != 0)
+           {
+             if (LSP_PSEUDO_ID (lsp_id))
+               {
+                 isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
+                                              vertex->depth, family, sysid,
+                                              vertex);
+               }
+             else
+               {
+                 isis_spf_process_lsp (spftree, lsp, vertex->d_N,
+                                       vertex->depth, family, sysid, vertex);
+               }
+           }
+         else
+           {
+             zlog_warn ("ISIS-Spf: No LSP found for %s",
+                        rawlspid_print (lsp_id));
+           }
+         break;
+       default:;
+       }
+    }
+
+out:
+  isis_route_validate (area);
+  spftree->pending = 0;
+  spftree->runcount++;
+  spftree->last_run_timestamp = time (NULL);
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now);
+  end_time = time_now.tv_sec;
+  end_time = (end_time * 1000000) + time_now.tv_usec;
+  spftree->last_run_duration = end_time - start_time;
+
+
+  return retval;
+}
+
+int
+isis_run_spf_l1 (struct thread *thread)
+{
+  struct isis_area *area;
+  int retval = ISIS_OK;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->spftree[0]->t_spf = NULL;
+  area->spftree[0]->pending = 0;
+
+  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);
+
+  if (area->ip_circuits)
+    retval = isis_run_spf (area, 1, AF_INET, isis->sysid);
+
+  return retval;
+}
+
+int
+isis_run_spf_l2 (struct thread *thread)
+{
+  struct isis_area *area;
+  int retval = ISIS_OK;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->spftree[1]->t_spf = NULL;
+  area->spftree[1]->pending = 0;
+
+  if (!(area->is_type & IS_LEVEL_2))
+    {
+      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) L2 SPF needed, periodic SPF", area->area_tag);
+
+  if (area->ip_circuits)
+    retval = isis_run_spf (area, 2, AF_INET, isis->sysid);
+
+  return retval;
+}
+
+int
+isis_spf_schedule (struct isis_area *area, int level)
+{
+  struct isis_spftree *spftree = area->spftree[level - 1];
+  time_t now = time (NULL);
+  int diff = now - spftree->last_run_timestamp;
+
+  assert (diff >= 0);
+  assert (area->is_type & level);
+
+  if (isis->debugs & DEBUG_SPF_EVENTS)
+    zlog_debug ("ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago",
+                area->area_tag, level, diff);
+
+  if (spftree->pending)
+    return ISIS_OK;
+
+  THREAD_TIMER_OFF (spftree->t_spf);
+
+  /* wait configured min_spf_interval before doing the SPF */
+  if (diff >= area->min_spf_interval[level-1])
+      return isis_run_spf (area, level, AF_INET, isis->sysid);
+
+  if (level == 1)
+    THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
+                     area->min_spf_interval[0] - diff);
+  else
+    THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area,
+                     area->min_spf_interval[1] - diff);
+
+  if (isis->debugs & DEBUG_SPF_EVENTS)
+    zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %d sec from now",
+                area->area_tag, level, area->min_spf_interval[level-1] - diff);
+
+  spftree->pending = 1;
+
+  return ISIS_OK;
+}
+
+#ifdef HAVE_IPV6
+static int
+isis_run_spf6_l1 (struct thread *thread)
+{
+  struct isis_area *area;
+  int retval = ISIS_OK;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->spftree6[0]->t_spf = NULL;
+  area->spftree6[0]->pending = 0;
+
+  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);
+
+  if (area->ipv6_circuits)
+    retval = isis_run_spf (area, 1, AF_INET6, isis->sysid);
+
+  return retval;
+}
+
+static int
+isis_run_spf6_l2 (struct thread *thread)
+{
+  struct isis_area *area;
+  int retval = ISIS_OK;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->spftree6[1]->t_spf = NULL;
+  area->spftree6[1]->pending = 0;
+
+  if (!(area->is_type & IS_LEVEL_2))
+    {
+      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) L2 SPF needed, periodic SPF.", area->area_tag);
+
+  if (area->ipv6_circuits)
+    retval = isis_run_spf (area, 2, AF_INET6, isis->sysid);
+
+  return retval;
+}
+
+int
+isis_spf_schedule6 (struct isis_area *area, int level)
+{
+  int retval = ISIS_OK;
+  struct isis_spftree *spftree = area->spftree6[level - 1];
+  time_t now = time (NULL);
+  time_t diff = now - spftree->last_run_timestamp;
+
+  assert (diff >= 0);
+  assert (area->is_type & level);
+
+  if (isis->debugs & DEBUG_SPF_EVENTS)
+    zlog_debug ("ISIS-Spf (%s) L%d SPF schedule called, lastrun %lld sec ago",
+                area->area_tag, level, (long long)diff);
+
+  if (spftree->pending)
+    return ISIS_OK;
+
+  THREAD_TIMER_OFF (spftree->t_spf);
+
+  /* wait configured min_spf_interval before doing the SPF */
+  if (diff >= area->min_spf_interval[level-1])
+      return isis_run_spf (area, level, AF_INET6, isis->sysid);
+
+  if (level == 1)
+    THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
+                     area->min_spf_interval[0] - diff);
+  else
+    THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area,
+                     area->min_spf_interval[1] - diff);
+
+  if (isis->debugs & DEBUG_SPF_EVENTS)
+    zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %lld sec from now",
+                area->area_tag, level,
+                (long long)(area->min_spf_interval[level-1] - diff));
+
+  spftree->pending = 1;
+
+  return retval;
+}
+#endif
+
+static void
+isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid)
+{
+  struct listnode *node;
+  struct listnode *anode;
+  struct isis_vertex *vertex;
+  struct isis_adjacency *adj;
+  u_char buff[BUFSIZ];
+
+  vty_out (vty, "Vertex               Type         Metric "
+                "Next-Hop             Interface Parent%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (paths, node, vertex)) {
+      if (memcmp (vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) {
+       vty_out (vty, "%-20s %-12s %-6s", print_sys_hostname (root_sysid),
+                "", "");
+       vty_out (vty, "%-30s", "");
+      } else {
+       int rows = 0;
+       vty_out (vty, "%-20s %-12s %-6u ", vid2string (vertex, buff),
+                vtype2string (vertex->type), vertex->d_N);
+       for (ALL_LIST_ELEMENTS_RO (vertex->Adj_N, anode, adj)) {
+         if (adj) {
+           if (rows) {
+               vty_out (vty, "%s", VTY_NEWLINE);
+               vty_out (vty, "%-20s %-12s %-6s ", "", "", "");
+           }
+           vty_out (vty, "%-20s %-9s ",
+                    print_sys_hostname (adj->sysid),
+                    adj->circuit->interface->name);
+           ++rows;
+         }
+       }
+       if (rows == 0)
+         vty_out (vty, "%-30s ", "");
+      }
+
+      /* Print list of parents for the ECMP DAG */
+      if (listcount (vertex->parents) > 0) {
+       struct listnode *pnode;
+       struct isis_vertex *pvertex;
+       int rows = 0;
+       for (ALL_LIST_ELEMENTS_RO (vertex->parents, pnode, pvertex)) {
+         if (rows) {
+           vty_out (vty, "%s", VTY_NEWLINE);
+           vty_out (vty, "%-72s", "");
+         }
+         vty_out (vty, "%s(%d)",
+                  vid2string (pvertex, buff), pvertex->type);
+         ++rows;
+       }
+      } else {
+       vty_out (vty, "  NULL ");
+      }
+
+#if 0
+      if (listcount (vertex->children) > 0) {
+         struct listnode *cnode;
+         struct isis_vertex *cvertex;
+         for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex)) {
+             vty_out (vty, "%s", VTY_NEWLINE);
+             vty_out (vty, "%-72s", "");
+             vty_out (vty, "%s(%d) ", 
+                      vid2string (cvertex, buff), cvertex->type);
+           }
+       }
+#endif
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_isis_topology,
+       show_isis_topology_cmd,
+       "show isis topology",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS paths to Intermediate Systems\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+  int level;
+
+  if (!isis->area_list || isis->area_list->count == 0)
+    return CMD_SUCCESS;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
+              VTY_NEWLINE);
+
+      for (level = 0; level < ISIS_LEVELS; level++)
+       {
+         if (area->ip_circuits > 0 && area->spftree[level]
+             && area->spftree[level]->paths->count > 0)
+           {
+             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->sysid);
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+#ifdef HAVE_IPV6
+         if (area->ipv6_circuits > 0 && area->spftree6[level]
+             && area->spftree6[level]->paths->count > 0)
+           {
+             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->sysid);
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+#endif /* HAVE_IPV6 */
+       }
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_topology_l1,
+       show_isis_topology_l1_cmd,
+       "show isis topology level-1",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS paths to Intermediate Systems\n"
+       "Paths to all level-1 routers in the area\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->area_list || isis->area_list->count == 0)
+    return CMD_SUCCESS;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
+              VTY_NEWLINE);
+
+      if (area->ip_circuits > 0 && area->spftree[0]
+         && area->spftree[0]->paths->count > 0)
+       {
+         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->sysid);
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+#ifdef HAVE_IPV6
+      if (area->ipv6_circuits > 0 && area->spftree6[0]
+         && area->spftree6[0]->paths->count > 0)
+       {
+         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->sysid);
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+#endif /* HAVE_IPV6 */
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_topology_l2,
+       show_isis_topology_l2_cmd,
+       "show isis topology level-2",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS paths to Intermediate Systems\n"
+       "Paths to all level-2 routers in the domain\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->area_list || isis->area_list->count == 0)
+    return CMD_SUCCESS;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
+              VTY_NEWLINE);
+
+      if (area->ip_circuits > 0 && area->spftree[1]
+         && area->spftree[1]->paths->count > 0)
+       {
+         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->sysid);
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+#ifdef HAVE_IPV6
+      if (area->ipv6_circuits > 0 && area->spftree6[1]
+         && area->spftree6[1]->paths->count > 0)
+       {
+         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->sysid);
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+#endif /* HAVE_IPV6 */
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+void
+isis_spf_cmds_init ()
+{
+  install_element (VIEW_NODE, &show_isis_topology_cmd);
+  install_element (VIEW_NODE, &show_isis_topology_l1_cmd);
+  install_element (VIEW_NODE, &show_isis_topology_l2_cmd);
+}
diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h
new file mode 100644 (file)
index 0000000..aa543b7
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_spf.h
+ *                             IS-IS Shortest Path First algorithm  
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_SPF_H
+#define _ZEBRA_ISIS_SPF_H
+
+enum vertextype
+{
+  VTYPE_PSEUDO_IS = 1,
+  VTYPE_PSEUDO_TE_IS,
+  VTYPE_NONPSEUDO_IS,
+  VTYPE_NONPSEUDO_TE_IS,
+  VTYPE_ES,
+  VTYPE_IPREACH_INTERNAL,
+  VTYPE_IPREACH_EXTERNAL,
+  VTYPE_IPREACH_TE
+#ifdef HAVE_IPV6
+    ,
+  VTYPE_IP6REACH_INTERNAL,
+  VTYPE_IP6REACH_EXTERNAL
+#endif /* HAVE_IPV6 */
+};
+
+/*
+ * Triple <N, d(N), {Adj(N)}> 
+ */
+struct isis_vertex
+{
+  enum vertextype type;
+
+  union
+  {
+    u_char id[ISIS_SYS_ID_LEN + 1];
+    struct prefix prefix;
+  } N;
+
+  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)} next hop or neighbor list */
+  struct list *parents;         /* list of parents for ECMP */
+  struct list *children;        /* list of children used for tree dump */
+};
+
+struct isis_spftree
+{
+  struct thread *t_spf;                /* spf threads */
+  struct list *paths;          /* the SPT */
+  struct list *tents;          /* TENT */
+  struct isis_area *area;       /* back pointer to area */
+  int pending;                 /* already scheduled */
+  unsigned int runcount;        /* number of runs since uptime */
+  time_t last_run_timestamp;    /* last run timestamp for scheduling */
+  time_t last_run_duration;     /* last run duration in msec */
+};
+
+struct isis_spftree * isis_spftree_new (struct isis_area *area);
+void isis_spftree_del (struct isis_spftree *spftree);
+void isis_spftree_adj_del (struct isis_spftree *spftree,
+                           struct isis_adjacency *adj);
+void spftree_area_init (struct isis_area *area);
+void spftree_area_del (struct isis_area *area);
+void spftree_area_adj_del (struct isis_area *area,
+                           struct isis_adjacency *adj);
+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
+#endif /* _ZEBRA_ISIS_SPF_H */
diff --git a/isisd/isis_te.c b/isisd/isis_te.c
new file mode 100644 (file)
index 0000000..6cb8551
--- /dev/null
@@ -0,0 +1,1370 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_te.c
+ *
+ * This is an implementation of RFC5305
+ *
+ *      Copyright (C) 2014 Orange Labs
+ *      http://www.orange.com
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "vty.h"
+#include "stream.h"
+#include "memory.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "checksum.h"
+#include "md5.h"
+#include "sockunion.h"
+#include "network.h"
+
+#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"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_te.h"
+
+/* Global varial for MPLS TE management */
+struct isis_mpls_te isisMplsTE;
+
+const char *mode2text[] = { "Disable", "Area", "AS", "Emulate" };
+
+/*------------------------------------------------------------------------*
+ * Followings are control functions for MPLS-TE parameters management.
+ *------------------------------------------------------------------------*/
+
+/* Search MPLS TE Circuit context from Interface */
+static struct mpls_te_circuit *
+lookup_mpls_params_by_ifp (struct interface *ifp)
+{
+  struct isis_circuit *circuit;
+
+  if ((circuit = circuit_scan_by_ifp (ifp)) == NULL)
+      return NULL;
+
+  return circuit->mtc;
+}
+
+/* Create new MPLS TE Circuit context */
+struct mpls_te_circuit *
+mpls_te_circuit_new()
+{
+  struct mpls_te_circuit *mtc;
+
+  zlog_debug ("ISIS MPLS-TE: Create new MPLS TE Circuit context");
+
+  mtc = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof (struct mpls_te_circuit));
+
+  if (mtc == NULL)
+    return NULL;
+
+  mtc->status = disable;
+  mtc->type = STD_TE;
+  mtc->length = 0;
+
+  return mtc;
+
+}
+
+/* Copy SUB TLVs parameters into a buffer - No space verification are performed */
+/* Caller must verify before that there is enough free space in the buffer */
+u_char
+add_te_subtlvs(u_char *buf, struct mpls_te_circuit *mtc)
+{
+  u_char size, *tlvs = buf;
+
+  zlog_debug ("ISIS MPLS-TE: Add TE Sub TLVs to buffer");
+
+  if (mtc == NULL)
+    {
+      zlog_debug("ISIS MPLS-TE: Abort! No MPLS TE Circuit available has been specified");
+      return 0;
+    }
+
+  /* Create buffer if not provided */
+  if (buf == NULL)
+    {
+      zlog_debug("ISIS MPLS-TE: Abort! No Buffer has been specified");
+      return 0;
+    }
+
+  /* TE_SUBTLV_ADMIN_GRP */
+  if (SUBTLV_TYPE(mtc->admin_grp) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->admin_grp.header));
+      memcpy(tlvs, &(mtc->admin_grp), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_LLRI */
+  if (SUBTLV_TYPE(mtc->llri) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->llri.header));
+      memcpy(tlvs, &(mtc->llri), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_LCLIF_IPADDR */
+  if (SUBTLV_TYPE(mtc->local_ipaddr) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->local_ipaddr.header));
+      memcpy(tlvs, &(mtc->local_ipaddr), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_RMTIF_IPADDR */
+  if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->rmt_ipaddr.header));
+      memcpy(tlvs, &(mtc->rmt_ipaddr), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_MAX_BW */
+  if (SUBTLV_TYPE(mtc->max_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->max_bw.header));
+      memcpy(tlvs, &(mtc->max_bw), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_MAX_RSV_BW */
+  if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->max_rsv_bw.header));
+      memcpy(tlvs, &(mtc->max_rsv_bw), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_UNRSV_BW */
+  if (SUBTLV_TYPE(mtc->unrsv_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->unrsv_bw.header));
+      memcpy(tlvs, &(mtc->unrsv_bw), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_TE_METRIC */
+  if (SUBTLV_TYPE(mtc->te_metric) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->te_metric.header));
+      memcpy(tlvs, &(mtc->te_metric), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_AV_DELAY */
+  if (SUBTLV_TYPE(mtc->av_delay) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->av_delay.header));
+      memcpy(tlvs, &(mtc->av_delay), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_MM_DELAY */
+  if (SUBTLV_TYPE(mtc->mm_delay) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->mm_delay.header));
+      memcpy(tlvs, &(mtc->mm_delay), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_DELAY_VAR */
+  if (SUBTLV_TYPE(mtc->delay_var) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->delay_var.header));
+      memcpy(tlvs, &(mtc->delay_var), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_PKT_LOSS */
+  if (SUBTLV_TYPE(mtc->pkt_loss) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->pkt_loss.header));
+      memcpy(tlvs, &(mtc->pkt_loss), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_RES_BW */
+  if (SUBTLV_TYPE(mtc->res_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->res_bw.header));
+      memcpy(tlvs, &(mtc->res_bw), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_AVA_BW */
+  if (SUBTLV_TYPE(mtc->ava_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->ava_bw.header));
+      memcpy(tlvs, &(mtc->ava_bw), size);
+      tlvs += size;
+    }
+
+  /* TE_SUBTLV_USE_BW */
+  if (SUBTLV_TYPE(mtc->use_bw) != 0)
+    {
+      size = SUBTLV_SIZE (&(mtc->use_bw.header));
+      memcpy(tlvs, &(mtc->use_bw), size);
+      tlvs += size;
+    }
+
+  /* Update SubTLVs length */
+  mtc->length = subtlvs_len(mtc);
+
+  zlog_debug("ISIS MPLS-TE: Add %d bytes length SubTLVs", mtc->length);
+
+  return mtc->length;
+}
+
+/* Compute total Sub-TLVs size */
+u_char
+subtlvs_len (struct mpls_te_circuit *mtc)
+{
+  int length = 0;
+
+  /* Sanity Check */
+  if (mtc == NULL)
+    return 0;
+
+  /* TE_SUBTLV_ADMIN_GRP */
+  if (SUBTLV_TYPE(mtc->admin_grp) != 0)
+    length += SUBTLV_SIZE (&(mtc->admin_grp.header));
+
+  /* TE_SUBTLV_LLRI */
+  if (SUBTLV_TYPE(mtc->llri) != 0)
+    length += SUBTLV_SIZE (&mtc->llri.header);
+
+  /* TE_SUBTLV_LCLIF_IPADDR */
+  if (SUBTLV_TYPE(mtc->local_ipaddr) != 0)
+    length += SUBTLV_SIZE (&mtc->local_ipaddr.header);
+
+  /* TE_SUBTLV_RMTIF_IPADDR */
+  if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0)
+    length += SUBTLV_SIZE (&mtc->rmt_ipaddr.header);
+
+  /* TE_SUBTLV_MAX_BW */
+  if (SUBTLV_TYPE(mtc->max_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->max_bw.header);
+
+  /* TE_SUBTLV_MAX_RSV_BW */
+  if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->max_rsv_bw.header);
+
+  /* TE_SUBTLV_UNRSV_BW */
+  if (SUBTLV_TYPE(mtc->unrsv_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->unrsv_bw.header);
+
+  /* TE_SUBTLV_TE_METRIC */
+  if (SUBTLV_TYPE(mtc->te_metric) != 0)
+    length += SUBTLV_SIZE (&mtc->te_metric.header);
+
+  /* TE_SUBTLV_AV_DELAY */
+  if (SUBTLV_TYPE(mtc->av_delay) != 0)
+    length += SUBTLV_SIZE (&mtc->av_delay.header);
+
+  /* TE_SUBTLV_MM_DELAY */
+  if (SUBTLV_TYPE(mtc->mm_delay) != 0)
+    length += SUBTLV_SIZE (&mtc->mm_delay.header);
+
+  /* TE_SUBTLV_DELAY_VAR */
+  if (SUBTLV_TYPE(mtc->delay_var) != 0)
+    length += SUBTLV_SIZE (&mtc->delay_var.header);
+
+  /* TE_SUBTLV_PKT_LOSS */
+  if (SUBTLV_TYPE(mtc->pkt_loss) != 0)
+    length += SUBTLV_SIZE (&mtc->pkt_loss.header);
+
+  /* TE_SUBTLV_RES_BW */
+  if (SUBTLV_TYPE(mtc->res_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->res_bw.header);
+
+  /* TE_SUBTLV_AVA_BW */
+  if (SUBTLV_TYPE(mtc->ava_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->ava_bw.header);
+
+  /* TE_SUBTLV_USE_BW */
+  if (SUBTLV_TYPE(mtc->use_bw) != 0)
+    length += SUBTLV_SIZE (&mtc->use_bw.header);
+
+  /* Check that length is lower than the MAXIMUM SUBTLV size i.e. 256 */
+  if (length > MAX_SUBTLV_SIZE)
+    {
+      mtc->length = 0;
+      return 0;
+    }
+
+  mtc->length = (u_char)length;
+
+  return mtc->length;
+}
+
+/* Following are various functions to set MPLS TE parameters */
+static void
+set_circuitparams_admin_grp (struct mpls_te_circuit *mtc, u_int32_t admingrp)
+{
+  SUBTLV_TYPE(mtc->admin_grp) = TE_SUBTLV_ADMIN_GRP;
+  SUBTLV_LEN(mtc->admin_grp)  = SUBTLV_DEF_SIZE;
+  mtc->admin_grp.value        = htonl(admingrp);
+  return;
+}
+
+static void  __attribute__ ((unused))
+set_circuitparams_llri (struct mpls_te_circuit *mtc, u_int32_t local, u_int32_t remote)
+{
+  SUBTLV_TYPE(mtc->llri) = TE_SUBTLV_LLRI;
+  SUBTLV_LEN(mtc->llri)  = TE_SUBTLV_LLRI_SIZE;
+  mtc->llri.local        = htonl(local);
+  mtc->llri.remote       = htonl(remote);
+}
+
+void
+set_circuitparams_local_ipaddr (struct mpls_te_circuit *mtc, struct in_addr addr)
+{
+
+  SUBTLV_TYPE(mtc->local_ipaddr) = TE_SUBTLV_LOCAL_IPADDR;
+  SUBTLV_LEN(mtc->local_ipaddr)  = SUBTLV_DEF_SIZE;
+  mtc->local_ipaddr.value.s_addr = addr.s_addr;
+  return;
+}
+
+void
+set_circuitparams_rmt_ipaddr (struct mpls_te_circuit *mtc, struct in_addr addr)
+{
+
+  SUBTLV_TYPE(mtc->rmt_ipaddr) = TE_SUBTLV_RMT_IPADDR;
+  SUBTLV_LEN(mtc->rmt_ipaddr)  = SUBTLV_DEF_SIZE;
+  mtc->rmt_ipaddr.value.s_addr = addr.s_addr;
+  return;
+}
+
+static void
+set_circuitparams_max_bw (struct mpls_te_circuit *mtc, float fp)
+{
+  SUBTLV_TYPE(mtc->max_bw) = TE_SUBTLV_MAX_BW;
+  SUBTLV_LEN(mtc->max_bw)  = SUBTLV_DEF_SIZE;
+  mtc->max_bw.value = htonf(fp);
+  return;
+}
+
+static void
+set_circuitparams_max_rsv_bw (struct mpls_te_circuit *mtc, float fp)
+{
+  SUBTLV_TYPE(mtc->max_rsv_bw) = TE_SUBTLV_MAX_RSV_BW;
+  SUBTLV_LEN(mtc->max_rsv_bw)  = SUBTLV_DEF_SIZE;
+  mtc->max_rsv_bw.value = htonf(fp);
+  return;
+}
+
+static void
+set_circuitparams_unrsv_bw (struct mpls_te_circuit *mtc, int priority, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->unrsv_bw) = TE_SUBTLV_UNRSV_BW;
+  SUBTLV_LEN(mtc->unrsv_bw)  = TE_SUBTLV_UNRSV_SIZE;
+  mtc->unrsv_bw.value[priority] = htonf(fp);
+  return;
+}
+
+static void
+set_circuitparams_te_metric (struct mpls_te_circuit *mtc, u_int32_t te_metric)
+{
+  SUBTLV_TYPE(mtc->te_metric) = TE_SUBTLV_TE_METRIC;
+  SUBTLV_LEN(mtc->te_metric)  = TE_SUBTLV_TE_METRIC_SIZE;
+  mtc->te_metric.value[0] = (te_metric >> 16) & 0xFF;
+  mtc->te_metric.value[1] = (te_metric  >> 8) & 0xFF;
+  mtc->te_metric.value[2] = te_metric & 0xFF;
+  return;
+}
+
+static void
+set_circuitparams_inter_as (struct mpls_te_circuit *mtc, struct in_addr addr, u_int32_t as)
+{
+
+  /* Set the Remote ASBR IP address and then the associated AS number */
+  SUBTLV_TYPE(mtc->rip) = TE_SUBTLV_RIP;
+  SUBTLV_LEN(mtc->rip)  = SUBTLV_DEF_SIZE;
+  mtc->rip.value.s_addr = addr.s_addr;
+
+  SUBTLV_TYPE(mtc->ras) = TE_SUBTLV_RAS;
+  SUBTLV_LEN(mtc->ras)  = SUBTLV_DEF_SIZE;
+  mtc->ras.value        = htonl(as);
+}
+
+static void
+unset_circuitparams_inter_as (struct mpls_te_circuit *mtc)
+{
+
+  /* Reset the Remote ASBR IP address and then the associated AS number */
+  SUBTLV_TYPE(mtc->rip) = 0;
+  SUBTLV_LEN(mtc->rip)  = 0;
+  mtc->rip.value.s_addr = 0;
+
+  SUBTLV_TYPE(mtc->ras) = 0;
+  SUBTLV_LEN(mtc->ras)  = 0;
+  mtc->ras.value        = 0;
+}
+
+static void
+set_circuitparams_av_delay (struct mpls_te_circuit *mtc, u_int32_t delay, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->av_delay) = TE_SUBTLV_AV_DELAY;
+  SUBTLV_LEN(mtc->av_delay)  = SUBTLV_DEF_SIZE;
+  tmp = delay & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  mtc->av_delay.value = htonl(tmp);
+  return;
+}
+
+static void
+set_circuitparams_mm_delay (struct mpls_te_circuit *mtc, u_int32_t low, u_int32_t high, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->mm_delay) = TE_SUBTLV_MM_DELAY;
+  SUBTLV_LEN(mtc->mm_delay)  = TE_SUBTLV_MM_DELAY_SIZE;
+  tmp = low & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  mtc->mm_delay.low = htonl(tmp);
+  mtc->mm_delay.high = htonl(high);
+  return;
+}
+
+static void
+set_circuitparams_delay_var (struct mpls_te_circuit *mtc, u_int32_t jitter)
+{
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->delay_var) = TE_SUBTLV_DELAY_VAR;
+  SUBTLV_LEN(mtc->delay_var)  = SUBTLV_DEF_SIZE;
+  mtc->delay_var.value        = htonl(jitter & TE_EXT_MASK);
+  return;
+}
+
+static void
+set_circuitparams_pkt_loss (struct mpls_te_circuit *mtc, u_int32_t loss, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->pkt_loss) = TE_SUBTLV_PKT_LOSS;
+  SUBTLV_LEN(mtc->pkt_loss)  = SUBTLV_DEF_SIZE;
+  tmp = loss & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  mtc->pkt_loss.value = htonl(tmp);
+  return;
+}
+
+static void
+set_circuitparams_res_bw (struct mpls_te_circuit *mtc, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->res_bw) = TE_SUBTLV_RES_BW;
+  SUBTLV_LEN(mtc->res_bw)  = SUBTLV_DEF_SIZE;
+  mtc->res_bw.value = htonf(fp);
+  return;
+}
+
+static void
+set_circuitparams_ava_bw (struct mpls_te_circuit *mtc, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->ava_bw) = TE_SUBTLV_AVA_BW;
+  SUBTLV_LEN(mtc->ava_bw)  = SUBTLV_DEF_SIZE;
+  mtc->ava_bw.value = htonf(fp);
+  return;
+}
+
+static void
+set_circuitparams_use_bw (struct mpls_te_circuit *mtc, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  SUBTLV_TYPE(mtc->use_bw) = TE_SUBTLV_USE_BW;
+  SUBTLV_LEN(mtc->use_bw)  = SUBTLV_DEF_SIZE;
+  mtc->use_bw.value = htonf(fp);
+  return;
+}
+
+/* Main initialization / update function of the MPLS TE Circuit context */
+/* Call when interface TE Link parameters are modified */
+void
+isis_link_params_update (struct isis_circuit *circuit, struct interface *ifp)
+{
+  int i;
+  struct prefix_ipv4 *addr;
+  struct mpls_te_circuit *mtc;
+
+  /* Sanity Check */
+  if ((circuit == NULL) || (ifp == NULL))
+      return;
+
+  zlog_info ("MPLS-TE: Initialize circuit parameters for interface %s", ifp->name);
+  
+  /* Check if MPLS TE Circuit context has not been already created */
+  if (circuit->mtc == NULL)
+      circuit->mtc = mpls_te_circuit_new();
+
+  mtc = circuit->mtc;
+
+  /* Fulfil MTC TLV from ifp TE Link parameters */
+  if (HAS_LINK_PARAMS(ifp))
+    {
+      mtc->status = enable;
+      /* STD_TE metrics */
+      if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP))
+        set_circuitparams_admin_grp (mtc, ifp->link_params->admin_grp);
+      else
+        SUBTLV_TYPE(mtc->admin_grp) = 0;
+
+      /* If not already set, register local IP addr from ip_addr list if it exists */
+      if (SUBTLV_TYPE(mtc->local_ipaddr) == 0)
+        {
+          if (circuit->ip_addrs != NULL && listcount(circuit->ip_addrs) != 0)
+            {
+              addr = (struct prefix_ipv4 *)listgetdata ((struct listnode *)listhead (circuit->ip_addrs));
+              set_circuitparams_local_ipaddr (mtc, addr->prefix);
+            }
+        }
+
+      /* If not already set, try to determine Remote IP addr if circuit is P2P */
+      if ((SUBTLV_TYPE(mtc->rmt_ipaddr) == 0) && (circuit->circ_type == CIRCUIT_T_P2P))
+        {
+          struct isis_adjacency *adj = circuit->u.p2p.neighbor;
+          if (adj->ipv4_addrs != NULL && listcount(adj->ipv4_addrs) != 0)
+            {
+              struct in_addr *ip_addr;
+              ip_addr = (struct in_addr *)listgetdata ((struct listnode *)listhead (adj->ipv4_addrs));
+              set_circuitparams_rmt_ipaddr (mtc, *ip_addr);
+            }
+        }
+
+      if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW))
+        set_circuitparams_max_bw (mtc, ifp->link_params->max_bw);
+      else
+        SUBTLV_TYPE(mtc->max_bw) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW))
+        set_circuitparams_max_rsv_bw (mtc, ifp->link_params->max_rsv_bw);
+      else
+        SUBTLV_TYPE(mtc->max_rsv_bw) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW))
+        for (i = 0; i < MAX_CLASS_TYPE; i++)
+          set_circuitparams_unrsv_bw (mtc, i, ifp->link_params->unrsv_bw[i]);
+      else
+        SUBTLV_TYPE(mtc->unrsv_bw) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_TE))
+        set_circuitparams_te_metric(mtc, ifp->link_params->te_metric);
+      else
+        SUBTLV_TYPE(mtc->te_metric) = 0;
+
+      /* TE metric Extensions */
+      if (IS_PARAM_SET(ifp->link_params, LP_DELAY))
+        set_circuitparams_av_delay(mtc, ifp->link_params->av_delay, 0);
+      else
+        SUBTLV_TYPE(mtc->av_delay) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY))
+        set_circuitparams_mm_delay(mtc, ifp->link_params->min_delay, ifp->link_params->max_delay, 0);
+      else
+        SUBTLV_TYPE(mtc->mm_delay) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR))
+        set_circuitparams_delay_var(mtc, ifp->link_params->delay_var);
+      else
+        SUBTLV_TYPE(mtc->delay_var) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS))
+        set_circuitparams_pkt_loss(mtc, ifp->link_params->pkt_loss, 0);
+      else
+        SUBTLV_TYPE(mtc->pkt_loss) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_RES_BW))
+        set_circuitparams_res_bw(mtc, ifp->link_params->res_bw);
+      else
+        SUBTLV_TYPE(mtc->res_bw) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW))
+        set_circuitparams_ava_bw(mtc, ifp->link_params->ava_bw);
+      else
+        SUBTLV_TYPE(mtc->ava_bw) = 0;
+
+      if (IS_PARAM_SET(ifp->link_params, LP_USE_BW))
+        set_circuitparams_use_bw(mtc, ifp->link_params->use_bw);
+      else
+        SUBTLV_TYPE(mtc->use_bw) = 0;
+
+      /* INTER_AS */
+      if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS))
+        set_circuitparams_inter_as(mtc, ifp->link_params->rmt_ip, ifp->link_params->rmt_as);
+      else
+        /* reset inter-as TE params */
+        unset_circuitparams_inter_as (mtc);
+
+      /* Compute total length of SUB TLVs */
+      mtc->length = subtlvs_len(mtc);
+
+    }
+  else
+    mtc->status = disable;
+
+  /* Finally Update LSP */
+#if 0
+  if (IS_MPLS_TE(isisMplsTE) && circuit->area)
+       lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+#endif
+  return;
+}
+
+void
+isis_mpls_te_update (struct interface *ifp)
+{
+  struct isis_circuit *circuit;
+
+  /* Sanity Check */
+  if (ifp == NULL)
+    return;
+
+  /* Get circuit context from interface */
+  if ((circuit = circuit_scan_by_ifp(ifp)) == NULL)
+    return;
+
+  /* Update TE TLVs ... */
+  isis_link_params_update(circuit, ifp);
+
+  /* ... and LSP */
+  if (IS_MPLS_TE(isisMplsTE) && circuit->area)
+     lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty session control functions.
+ *------------------------------------------------------------------------*/
+
+static u_char
+show_vty_subtlv_admin_grp (struct vty *vty, struct te_subtlv_admin_grp *tlv)
+{
+
+  if (vty != NULL)
+    vty_out (vty, "    Administrative Group: 0x%x%s",
+             (u_int32_t) ntohl (tlv->value), VTY_NEWLINE);
+  else
+    zlog_debug ("      Administrative Group: 0x%x",
+                (u_int32_t) ntohl (tlv->value));
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_llri (struct vty *vty, struct te_subtlv_llri *tlv)
+{
+  if (vty != NULL)
+    {
+      vty_out (vty, "    Link Local  ID: %d%s", (u_int32_t) ntohl (tlv->local),
+               VTY_NEWLINE);
+      vty_out (vty, "    Link Remote ID: %d%s", (u_int32_t) ntohl (tlv->remote),
+               VTY_NEWLINE);
+    }
+  else
+    {
+      zlog_debug ("      Link Local  ID: %d", (u_int32_t) ntohl (tlv->local));
+      zlog_debug ("      Link Remote ID: %d", (u_int32_t) ntohl (tlv->remote));
+    }
+
+  return (SUBTLV_HDR_SIZE + TE_SUBTLV_LLRI_SIZE);
+}
+
+static u_char
+show_vty_subtlv_local_ipaddr (struct vty *vty, struct te_subtlv_local_ipaddr *tlv)
+{
+  if (vty != NULL)
+    vty_out (vty, "    Local Interface IP Address(es): %s%s", inet_ntoa (tlv->value), VTY_NEWLINE);
+  else
+    zlog_debug ("      Local Interface IP Address(es): %s", inet_ntoa (tlv->value));
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_rmt_ipaddr (struct vty *vty, struct te_subtlv_rmt_ipaddr *tlv)
+{
+  if (vty != NULL)
+    vty_out (vty, "    Remote Interface IP Address(es): %s%s", inet_ntoa (tlv->value), VTY_NEWLINE);
+  else
+    zlog_debug ("      Remote Interface IP Address(es): %s", inet_ntoa (tlv->value));
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_max_bw (struct vty *vty, struct te_subtlv_max_bw *tlv)
+{
+  float fval;
+
+  fval = ntohf (tlv->value);
+
+  if (vty != NULL)
+    vty_out (vty, "    Maximum Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("      Maximum Bandwidth: %g (Bytes/sec)", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_max_rsv_bw (struct vty *vty, struct te_subtlv_max_rsv_bw *tlv)
+{
+  float fval;
+
+  fval = ntohf (tlv->value);
+
+  if (vty != NULL)
+    vty_out (vty, "    Maximum Reservable Bandwidth: %g (Bytes/sec)%s", fval,
+             VTY_NEWLINE);
+  else
+    zlog_debug ("      Maximum Reservable Bandwidth: %g (Bytes/sec)", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_unrsv_bw (struct vty *vty, struct te_subtlv_unrsv_bw *tlv)
+{
+  float fval1, fval2;
+  int i;
+
+  if (vty != NULL)
+    vty_out (vty, "    Unreserved Bandwidth:%s",VTY_NEWLINE);
+  else
+    zlog_debug ("      Unreserved Bandwidth:");
+
+  for (i = 0; i < MAX_CLASS_TYPE; i+=2)
+    {
+      fval1 = ntohf (tlv->value[i]);
+      fval2 = ntohf (tlv->value[i+1]);
+      if (vty != NULL)
+        vty_out (vty, "      [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s", i, fval1, i+1, fval2, VTY_NEWLINE);
+      else
+        zlog_debug ("        [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)", i, fval1, i+1, fval2);
+    }
+
+  return (SUBTLV_HDR_SIZE + TE_SUBTLV_UNRSV_SIZE);
+}
+
+static u_char
+show_vty_subtlv_te_metric (struct vty *vty, struct te_subtlv_te_metric *tlv)
+{
+  u_int32_t te_metric;
+
+  te_metric = tlv->value[2] | tlv->value[1] << 8 | tlv->value[0] << 16;
+  if (vty != NULL)
+    vty_out (vty, "    Traffic Engineering Metric: %u%s", te_metric, VTY_NEWLINE);
+  else
+    zlog_debug ("      Traffic Engineering Metric: %u", te_metric);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_ras (struct vty *vty, struct te_subtlv_ras *tlv)
+{
+  if (vty != NULL)
+    vty_out (vty, "    Inter-AS TE Remote AS number: %u%s", ntohl (tlv->value), VTY_NEWLINE);
+  else
+    zlog_debug ("      Inter-AS TE Remote AS number: %u", ntohl (tlv->value));
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_rip (struct vty *vty, struct te_subtlv_rip *tlv)
+{
+  if (vty != NULL)
+    vty_out (vty, "    Inter-AS TE Remote ASBR IP address: %s%s", inet_ntoa (tlv->value), VTY_NEWLINE);
+  else
+    zlog_debug ("      Inter-AS TE Remote ASBR IP address: %s", inet_ntoa (tlv->value));
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_av_delay (struct vty *vty, struct te_subtlv_av_delay *tlv)
+{
+  u_int32_t delay;
+  u_int32_t A;
+
+  delay = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK;
+  A = (u_int32_t) ntohl (tlv->value) & TE_EXT_ANORMAL;
+
+  if (vty != NULL)
+    vty_out (vty, "    %s Average Link Delay: %d (micro-sec)%s", A ? "Anomalous" : "Normal", delay, VTY_NEWLINE);
+  else
+    zlog_debug ("      %s Average Link Delay: %d (micro-sec)", A ? "Anomalous" : "Normal", delay);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_mm_delay (struct vty *vty, struct te_subtlv_mm_delay *tlv)
+{
+  u_int32_t low, high;
+  u_int32_t A;
+
+  low = (u_int32_t) ntohl (tlv->low) & TE_EXT_MASK;
+  A = (u_int32_t) ntohl (tlv->low) & TE_EXT_ANORMAL;
+  high = (u_int32_t) ntohl (tlv->high) & TE_EXT_MASK;
+
+  if (vty != NULL)
+    vty_out (vty, "    %s Min/Max Link Delay: %d / %d (micro-sec)%s", A ? "Anomalous" : "Normal", low, high, VTY_NEWLINE);
+  else
+    zlog_debug ("      %s Min/Max Link Delay: %d / %d (micro-sec)", A ? "Anomalous" : "Normal", low, high);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_delay_var (struct vty *vty, struct te_subtlv_delay_var *tlv)
+{
+  u_int32_t jitter;
+
+  jitter = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK;
+
+  if (vty != NULL)
+    vty_out (vty, "    Delay Variation: %d (micro-sec)%s", jitter, VTY_NEWLINE);
+  else
+    zlog_debug ("      Delay Variation: %d (micro-sec)", jitter);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_pkt_loss (struct vty *vty, struct te_subtlv_pkt_loss *tlv)
+{
+  u_int32_t loss;
+  u_int32_t A;
+  float fval;
+
+  loss = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK;
+  fval = (float) (loss * LOSS_PRECISION);
+  A = (u_int32_t) ntohl (tlv->value) & TE_EXT_ANORMAL;
+
+  if (vty != NULL)
+    vty_out (vty, "    %s Link Packet Loss: %g (%%)%s", A ? "Anomalous" : "Normal", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("      %s Link Packet Loss: %g (%%)", A ? "Anomalous" : "Normal", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_res_bw (struct vty *vty, struct te_subtlv_res_bw *tlv)
+{
+  float fval;
+
+  fval = ntohf(tlv->value);
+
+  if (vty != NULL)
+    vty_out (vty, "    Unidirectional Residual Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("      Unidirectional Residual Bandwidth: %g (Bytes/sec)", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_ava_bw (struct vty *vty, struct te_subtlv_ava_bw *tlv)
+{
+  float fval;
+
+  fval = ntohf (tlv->value);
+
+  if (vty != NULL)
+    vty_out (vty, "    Unidirectional Available Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("      Unidirectional Available Bandwidth: %g (Bytes/sec)", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_subtlv_use_bw (struct vty *vty, struct te_subtlv_use_bw *tlv)
+{
+  float fval;
+
+  fval = ntohf (tlv->value);
+
+  if (vty != NULL)
+    vty_out (vty, "    Unidirectional Utilized Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("      Unidirectional Utilized Bandwidth: %g (Bytes/sec)", fval);
+
+  return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE);
+}
+
+static u_char
+show_vty_unknown_tlv (struct vty *vty, struct subtlv_header *tlvh)
+{
+  int i, rtn = 1;
+  u_char *v = (u_char *)tlvh;
+
+  if (vty != NULL)
+    {
+      if (tlvh->length != 0)
+        {
+          vty_out (vty, "    Unknown TLV: [type(%#.2x), length(%#.2x)]%s",
+              tlvh->type, tlvh->length, VTY_NEWLINE);
+          vty_out(vty, "       Dump: [00]");
+          rtn = 1;          /* initialize end of line counter */
+          for (i = 0; i < tlvh->length; i++)
+            {
+              vty_out (vty, " %#.2x", v[i]);
+              if (rtn == 8)
+                {
+                  vty_out (vty, "%s             [%.2x]", VTY_NEWLINE, i + 1);
+                  rtn = 1;
+                }
+              else
+                rtn++;
+            }
+          vty_out (vty, "%s", VTY_NEWLINE);
+        }
+      else
+        vty_out (vty, "    Unknown TLV: [type(%#.2x), length(%#.2x)]%s",
+            tlvh->type, tlvh->length, VTY_NEWLINE);
+    }
+  else
+    {
+      zlog_debug ("      Unknown TLV: [type(%#.2x), length(%#.2x)]",
+          tlvh->type, tlvh->length);
+    }
+
+  return SUBTLV_SIZE(tlvh);
+}
+
+/* Main Show function */
+void
+mpls_te_print_detail(struct vty *vty, struct te_is_neigh *te)
+{
+  struct subtlv_header *tlvh, *next;
+  u_int16_t sum = 0;
+
+  zlog_debug ("ISIS MPLS-TE: Show database TE detail");
+
+  if (te->sub_tlvs == NULL)
+    return;
+
+  tlvh = (struct subtlv_header *)te->sub_tlvs;
+
+  for (; sum < te->sub_tlvs_length; tlvh = (next ? next : SUBTLV_HDR_NEXT (tlvh)))
+    {
+      next = NULL;
+
+      switch (tlvh->type)
+      {
+      case TE_SUBTLV_ADMIN_GRP:
+        sum += show_vty_subtlv_admin_grp (vty, (struct te_subtlv_admin_grp *)tlvh);
+        break;
+      case TE_SUBTLV_LLRI:
+        sum += show_vty_subtlv_llri (vty, (struct te_subtlv_llri *)tlvh);
+        break;
+      case TE_SUBTLV_LOCAL_IPADDR:
+        sum += show_vty_subtlv_local_ipaddr (vty, (struct te_subtlv_local_ipaddr *)tlvh);
+        break;
+      case TE_SUBTLV_RMT_IPADDR:
+        sum += show_vty_subtlv_rmt_ipaddr (vty, (struct te_subtlv_rmt_ipaddr *)tlvh);
+        break;
+      case TE_SUBTLV_MAX_BW:
+        sum += show_vty_subtlv_max_bw (vty, (struct te_subtlv_max_bw *)tlvh);
+        break;
+      case TE_SUBTLV_MAX_RSV_BW:
+        sum += show_vty_subtlv_max_rsv_bw (vty, (struct te_subtlv_max_rsv_bw *)tlvh);
+        break;
+      case TE_SUBTLV_UNRSV_BW:
+        sum += show_vty_subtlv_unrsv_bw (vty, (struct te_subtlv_unrsv_bw *)tlvh);
+        break;
+      case TE_SUBTLV_TE_METRIC:
+        sum += show_vty_subtlv_te_metric (vty, (struct te_subtlv_te_metric *)tlvh);
+        break;
+      case TE_SUBTLV_RAS:
+        sum += show_vty_subtlv_ras (vty, (struct te_subtlv_ras *)tlvh);
+        break;
+      case TE_SUBTLV_RIP:
+        sum += show_vty_subtlv_rip (vty, (struct te_subtlv_rip *)tlvh);
+        break;
+      case TE_SUBTLV_AV_DELAY:
+        sum += show_vty_subtlv_av_delay (vty, (struct te_subtlv_av_delay *)tlvh);
+        break;
+      case TE_SUBTLV_MM_DELAY:
+        sum += show_vty_subtlv_mm_delay (vty, (struct te_subtlv_mm_delay *)tlvh);
+        break;
+      case TE_SUBTLV_DELAY_VAR:
+        sum += show_vty_subtlv_delay_var (vty, (struct te_subtlv_delay_var *)tlvh);
+        break;
+      case TE_SUBTLV_PKT_LOSS:
+        sum += show_vty_subtlv_pkt_loss (vty, (struct te_subtlv_pkt_loss *)tlvh);
+        break;
+      case TE_SUBTLV_RES_BW:
+        sum += show_vty_subtlv_res_bw (vty, (struct te_subtlv_res_bw *)tlvh);
+        break;
+      case TE_SUBTLV_AVA_BW:
+        sum += show_vty_subtlv_ava_bw (vty, (struct te_subtlv_ava_bw *)tlvh);
+        break;
+      case TE_SUBTLV_USE_BW:
+        sum += show_vty_subtlv_use_bw (vty, (struct te_subtlv_use_bw *)tlvh);
+        break;
+      default:
+        sum += show_vty_unknown_tlv (vty, tlvh);
+        break;
+      }
+    }
+  return;
+}
+
+/* Specific MPLS TE router parameters write function */
+void
+isis_mpls_te_config_write_router (struct vty *vty)
+{
+
+  zlog_debug ("ISIS MPLS-TE: Write ISIS router configuration");
+
+  if (IS_MPLS_TE(isisMplsTE))
+    {
+      vty_out (vty, "  mpls-te on%s", VTY_NEWLINE);
+      vty_out (vty, "  mpls-te router-address %s%s",
+               inet_ntoa (isisMplsTE.router_id), VTY_NEWLINE);
+    }
+
+  return;
+}
+
+
+/*------------------------------------------------------------------------*
+ * Followings are vty command functions.
+ *------------------------------------------------------------------------*/
+
+DEFUN (isis_mpls_te_on,
+       isis_mpls_te_on_cmd,
+       "mpls-te on",
+       MPLS_TE_STR
+       "Enable MPLS-TE functionality\n")
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (IS_MPLS_TE(isisMplsTE))
+    return CMD_SUCCESS;
+
+  if (IS_DEBUG_ISIS(DEBUG_TE))
+    zlog_debug ("ISIS MPLS-TE: OFF -> ON");
+
+  isisMplsTE.status = enable;
+
+  /*
+   * Following code is intended to handle two cases;
+   *
+   * 1) MPLS-TE was disabled at startup time, but now become enabled.
+   * In this case, we must enable MPLS-TE Circuit regarding interface MPLS_TE flag
+   * 2) MPLS-TE was once enabled then disabled, and now enabled again.
+   */
+  for (ALL_LIST_ELEMENTS_RO (isisMplsTE.cir_list, node, circuit))
+    {
+      if (circuit->mtc == NULL || IS_FLOOD_AS (circuit->mtc->type))
+        continue;
+
+      if ((circuit->mtc->status == disable)
+          && HAS_LINK_PARAMS(circuit->interface))
+        circuit->mtc->status = enable;
+      else
+        continue;
+
+      /* Reoriginate STD_TE & GMPLS circuits */
+      if (circuit->area)
+        lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_mpls_te_on,
+       no_isis_mpls_te_on_cmd,
+       "no mpls-te",
+       NO_STR
+       "Disable the MPLS-TE functionality\n")
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (isisMplsTE.status == disable)
+    return CMD_SUCCESS;
+
+  if (IS_DEBUG_ISIS(DEBUG_TE))
+    zlog_debug ("ISIS MPLS-TE: ON -> OFF");
+
+  isisMplsTE.status = disable;
+
+  /* Flush LSP if circuit engage */
+  for (ALL_LIST_ELEMENTS_RO (isisMplsTE.cir_list, node, circuit))
+    {
+      if (circuit->mtc == NULL || (circuit->mtc->status == disable))
+        continue;
+
+      /* disable MPLS_TE Circuit */
+      circuit->mtc->status = disable;
+
+      /* Re-originate circuit without STD_TE & GMPLS parameters */
+      if (circuit->area)
+        lsp_regenerate_schedule (circuit->area, circuit->is_type, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_mpls_te_router_addr,
+       isis_mpls_te_router_addr_cmd,
+       "mpls-te router-address A.B.C.D",
+       MPLS_TE_STR
+       "Stable IP address of the advertising router\n"
+       "MPLS-TE router address in IPv4 address format\n")
+{
+  struct in_addr value;
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (! inet_aton (argv[0], &value))
+    {
+      vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  isisMplsTE.router_id.s_addr = value.s_addr;
+
+  if (isisMplsTE.status == disable)
+    return CMD_SUCCESS;
+
+  /* Update main Router ID in isis global structure */
+  isis->router_id = value.s_addr;
+  /* And re-schedule LSP update */
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    if (listcount (area->area_addrs) > 0)
+      lsp_regenerate_schedule (area, area->is_type, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_mpls_te_inter_as,
+       isis_mpls_te_inter_as_cmd,
+       "mpls-te inter-as (level-1|level-1-2|level-2-only)",
+       MPLS_TE_STR
+       "Configure MPLS-TE Inter-AS support\n"
+       "AREA native mode self originate INTER-AS LSP with L1 only flooding scope)\n"
+       "AREA native mode self originate INTER-AS LSP with L1 and L2 flooding scope)\n"
+       "AS native mode self originate INTER-AS LSP with L2 only flooding scope\n")
+{
+  vty_out (vty, "Not yet supported%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_mpls_te_inter_as,
+       no_isis_mpls_te_inter_as_cmd,
+       "no mpls-te inter-as",
+       NO_STR
+       "Disable the MPLS-TE functionality\n"
+       "Disable MPLS-TE Inter-AS support\n")
+{
+
+  vty_out (vty, "Not yet supported%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_mpls_te_router,
+       show_isis_mpls_te_router_cmd,
+       "show isis mpls-te router",
+       SHOW_STR
+       ISIS_STR
+       MPLS_TE_STR
+       "Router information\n")
+{
+  if (IS_MPLS_TE(isisMplsTE))
+    {
+      vty_out (vty, "--- MPLS-TE router parameters ---%s", VTY_NEWLINE);
+
+      if (vty != NULL)
+        {
+          if (ntohs (isisMplsTE.router_id.s_addr) != 0)
+            vty_out (vty, "  Router-Address: %s%s", inet_ntoa (isisMplsTE.router_id), VTY_NEWLINE);
+          else
+            vty_out (vty, "  N/A%s", VTY_NEWLINE);
+        }
+    }
+  else
+    vty_out (vty, "  MPLS-TE is disable on this router%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+static void
+show_mpls_te_sub (struct vty *vty, struct interface *ifp)
+{
+  struct mpls_te_circuit *mtc;
+
+  if ((IS_MPLS_TE(isisMplsTE))
+      &&  ((mtc = lookup_mpls_params_by_ifp (ifp)) != NULL))
+    {
+      /* Continue only if interface is not passive or support Inter-AS TEv2 */
+      if (mtc->status != enable)
+        {
+          if (IS_INTER_AS(mtc->type))
+            {
+              vty_out (vty, "-- Inter-AS TEv2 link parameters for %s --%s",
+                       ifp->name, VTY_NEWLINE);
+            }
+          else
+            {
+              /* MPLS-TE is not activate on this interface */
+              /* or this interface is passive and Inter-AS TEv2 is not activate */
+              vty_out (vty, "  %s: MPLS-TE is disabled on this interface%s",
+                       ifp->name, VTY_NEWLINE);
+              return;
+            }
+        }
+      else
+        {
+          vty_out (vty, "-- MPLS-TE link parameters for %s --%s",
+                   ifp->name, VTY_NEWLINE);
+        }
+
+      show_vty_subtlv_admin_grp (vty, &mtc->admin_grp);
+
+      if (SUBTLV_TYPE(mtc->local_ipaddr) != 0)
+        show_vty_subtlv_local_ipaddr (vty, &mtc->local_ipaddr);
+      if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0)
+        show_vty_subtlv_rmt_ipaddr (vty, &mtc->rmt_ipaddr);
+
+      show_vty_subtlv_max_bw (vty, &mtc->max_bw);
+      show_vty_subtlv_max_rsv_bw (vty, &mtc->max_rsv_bw);
+      show_vty_subtlv_unrsv_bw (vty, &mtc->unrsv_bw);
+      show_vty_subtlv_te_metric (vty, &mtc->te_metric);
+
+      if (IS_INTER_AS(mtc->type))
+        {
+          if (SUBTLV_TYPE(mtc->ras) != 0)
+            show_vty_subtlv_ras (vty, &mtc->ras);
+          if (SUBTLV_TYPE(mtc->rip) != 0)
+            show_vty_subtlv_rip (vty, &mtc->rip);
+        }
+
+      show_vty_subtlv_av_delay (vty, &mtc->av_delay);
+      show_vty_subtlv_mm_delay (vty, &mtc->mm_delay);
+      show_vty_subtlv_delay_var (vty, &mtc->delay_var);
+      show_vty_subtlv_pkt_loss (vty, &mtc->pkt_loss);
+      show_vty_subtlv_res_bw (vty, &mtc->res_bw);
+      show_vty_subtlv_ava_bw (vty, &mtc->ava_bw);
+      show_vty_subtlv_use_bw (vty, &mtc->use_bw);
+      vty_out (vty, "---------------%s%s", VTY_NEWLINE, VTY_NEWLINE);
+    }
+  else
+    {
+      vty_out (vty, "  %s: MPLS-TE is disabled on this interface%s",
+               ifp->name, VTY_NEWLINE);
+    }
+
+  return;
+}
+
+DEFUN (show_isis_mpls_te_interface,
+       show_isis_mpls_te_interface_cmd,
+       "show isis mpls-te interface [INTERFACE]",
+       SHOW_STR
+       ISIS_STR
+       MPLS_TE_STR
+       "Interface information\n"
+       "Interface name\n")
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  /* Show All Interfaces. */
+  if (argc == 0)
+    {
+      for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+        show_mpls_te_sub (vty, ifp);
+    }
+  /* Interface name is specified. */
+  else
+    {
+      if ((ifp = if_lookup_by_name (argv[0])) == NULL)
+        vty_out (vty, "No such interface name%s", VTY_NEWLINE);
+      else
+        show_mpls_te_sub (vty, ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Initialize MPLS_TE */
+void
+isis_mpls_te_init (void)
+{
+
+  zlog_debug("ISIS MPLS-TE: Initialize");
+
+  /* Initialize MPLS_TE structure */
+  isisMplsTE.status = disable;
+  isisMplsTE.level = 0;
+  isisMplsTE.inter_as = off;
+  isisMplsTE.interas_areaid.s_addr = 0;
+  isisMplsTE.cir_list = list_new();
+  isisMplsTE.router_id.s_addr = 0;
+  
+  /* Register new VTY commands */
+  install_element (VIEW_NODE, &show_isis_mpls_te_router_cmd);
+  install_element (VIEW_NODE, &show_isis_mpls_te_interface_cmd);
+
+  install_element (ISIS_NODE, &isis_mpls_te_on_cmd);
+  install_element (ISIS_NODE, &no_isis_mpls_te_on_cmd);
+  install_element (ISIS_NODE, &isis_mpls_te_router_addr_cmd);
+  install_element (ISIS_NODE, &isis_mpls_te_inter_as_cmd);
+  install_element (ISIS_NODE, &no_isis_mpls_te_inter_as_cmd);
+
+  return;
+}
+
diff --git a/isisd/isis_te.h b/isisd/isis_te.h
new file mode 100644 (file)
index 0000000..4f29fdb
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_te.c
+ *
+ * This is an implementation of RFC5305, RFC 5307 and draft-ietf-isis-te-metric-extensions-11
+ *
+ *      Copyright (C) 2014 Orange Labs
+ *      http://www.orange.com
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_ISIS_MPLS_TE_H
+#define _ZEBRA_ISIS_MPLS_TE_H
+
+/*
+ * Traffic Engineering information are transport through LSP:
+ *  - Extended IS Reachability          TLV = 22
+ *  - Traffic Engineering Router ID     TLV = 134
+ *  - Extended IP Reachability          TLV = 135
+ *  - Inter-AS Reachability Information TLV = 141
+ *
+ *  and support following sub-TLV:
+ *
+ * Name                           Value   Status
+ * _________________________________________________
+ * Administartive group (color)       3   RFC5305
+ * Link Local/Remote Identifiers      4   RFC5307
+ * IPv4 interface address             6   RFC5305
+ * IPv4 neighbor address              8   RFC5305
+ * Maximum link bandwidth             9   RFC5305
+ * Reservable link bandwidth         10   RFC5305
+ * Unreserved bandwidth              11   RFC5305
+ * TE Default metric                 18   RFC5305
+ * Link Protection Type              20   RFC5307
+ * Interface Switching Capability    21   RFC5307
+ * Remote AS number                  24   RFC5316
+ * IPv4 Remote ASBR identifier       25   RFC5316
+ *
+ */
+
+/* NOTE: RFC5316 is not yet supported in this version */
+
+/* Following define the type of TE link regarding the various RFC */
+#define STD_TE                 0x01
+#define GMPLS                  0x02
+#define INTER_AS               0x04
+#define FLOOD_L1               0x10
+#define FLOOD_L2               0x20
+#define FLOOD_AS                0x40
+#define EMULATED               0x80
+
+#define IS_STD_TE(x)           (x & STD_TE)
+#define IS_INTER_AS(x)                 (x & INTER_AS)
+#define IS_EMULATED(x)         (x & EMULATED)
+#define IS_FLOOD_L1(x)         (x & FLOOD_L1)
+#define IS_FLOOD_L2(x)         (x & FLOOD_L2)
+#define IS_FLOOD_AS(x)          (x & FLOOD_AS)
+#define IS_INTER_AS_EMU(x)     (x & INTER_AS & EMULATED)
+#define IS_INTER_AS_AS(x)      (x & INTER_AS & FLOOD_AS)
+
+/*
+ * Following section defines subTLV (tag, length, value) structures,
+ * used for Traffic Engineering.
+ */
+struct subtlv_header
+{
+  u_char       type;           /* sub_TLV_XXX type (see above) */
+  u_char       length;         /* Value portion only, in byte */
+};
+
+#define SUBTLV_HDR_SIZE        2  /* (sizeof (struct sub_tlv_header)) */
+
+#define SUBTLV_SIZE(stlvh)     (SUBTLV_HDR_SIZE + (stlvh)->length)
+
+#define SUBTLV_HDR_TOP(lsph)   (struct subtlv_header *)((char *)(lsph) + ISIS_LSP_HEADER_SIZE)
+
+#define SUBTLV_HDR_NEXT(stlvh)         (struct subtlv_header *)((char *)(stlvh) + SUBTLV_SIZE(stlvh))
+
+#define SUBTLV_TYPE(stlvh)     stlvh.header.type
+#define SUBTLV_LEN(stlvh)      stlvh.header.length
+#define SUBTLV_VAL(stlvh)      stlvh.value
+#define SUBTLV_DATA(stlvh)     stlvh + SUBTLV_HDR_SIZE
+
+#define SUBTLV_DEF_SIZE                4
+
+/* Link Sub-TLV: Resource Class/Color - RFC 5305 */
+#define TE_SUBTLV_ADMIN_GRP    3
+struct te_subtlv_admin_grp
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  u_int32_t            value;          /* Admin. group membership. */
+} __attribute__((__packed__));
+
+/* Link Local/Remote Identifiers - RFC 5307 */
+#define TE_SUBTLV_LLRI         4
+#define TE_SUBTLV_LLRI_SIZE    8
+struct te_subtlv_llri
+{
+  struct subtlv_header header;         /* Value length is 8 octets. */
+  u_int32_t            local;          /* Link Local Identifier */
+  u_int32_t            remote; /* Link Remote Identifier */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Local Interface IP Address - RFC 5305 */
+#define TE_SUBTLV_LOCAL_IPADDR 6
+struct te_subtlv_local_ipaddr
+{
+  struct subtlv_header header;         /* Value length is 4 x N octets. */
+  struct in_addr       value;          /* Local IP address(es). */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Neighbor Interface IP Address - RFC 5305 */
+#define TE_SUBTLV_RMT_IPADDR   8
+struct te_subtlv_rmt_ipaddr
+{
+  struct subtlv_header header;         /* Value length is 4 x N octets. */
+  struct in_addr       value;          /* Neighbor's IP address(es). */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Maximum Bandwidth - RFC 5305 */
+#define TE_SUBTLV_MAX_BW       9
+struct te_subtlv_max_bw
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  float                        value;          /* bytes/sec */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Maximum Reservable Bandwidth - RFC 5305 */
+#define TE_SUBTLV_MAX_RSV_BW   10
+struct te_subtlv_max_rsv_bw
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  float                        value;          /* bytes/sec */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Unreserved Bandwidth - RFC 5305 */
+#define TE_SUBTLV_UNRSV_BW     11
+#define TE_SUBTLV_UNRSV_SIZE   32
+struct te_subtlv_unrsv_bw
+{
+  struct subtlv_header header;         /* Value length is 32 octets. */
+  float                        value[8];       /* One for each priority level. */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Traffic Engineering Metric - RFC 5305 */
+#define TE_SUBTLV_TE_METRIC    18
+#define TE_SUBTLV_TE_METRIC_SIZE    3
+struct te_subtlv_te_metric
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  u_char               value[3];       /* Link metric for TE purpose. */
+} __attribute__((__packed__));
+
+/* Remote AS Number sub-TLV - RFC5316 */
+#define TE_SUBTLV_RAS          24
+struct te_subtlv_ras
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  u_int32_t           value;           /* Remote AS number */
+} __attribute__((__packed__));
+
+/* IPv4 Remote ASBR ID Sub-TLV - RFC5316 */
+#define TE_SUBTLV_RIP          25
+struct te_subtlv_rip
+{
+  struct subtlv_header header;         /* Value length is 4 octets. */
+  struct in_addr       value;          /* Remote ASBR IP address */
+} __attribute__((__packed__));
+
+
+/* draft-ietf-isis-te-metric-extensions-11.txt */
+/* Link Sub-TLV: Average Link Delay */
+#define TE_SUBTLV_AV_DELAY     33
+struct te_subtlv_av_delay
+{
+  struct subtlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* Average delay in micro-seconds only 24 bits => 0 ... 16777215
+                                   with Anomalous Bit (A) as Upper most bit */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Low/High Link Delay */
+#define TE_SUBTLV_MM_DELAY      34
+#define TE_SUBTLV_MM_DELAY_SIZE    8
+struct te_subtlv_mm_delay
+{
+  struct subtlv_header header;  /* Value length is 8 bytes. */
+  u_int32_t            low;     /* low delay in micro-seconds only 24 bits => 0 ... 16777215
+                                   with Anomalous Bit (A) as Upper most bit */
+  u_int32_t            high;    /* high delay in micro-seconds only 24 bits => 0 ... 16777215 */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Link Delay Variation i.e. Jitter */
+#define TE_SUBTLV_DELAY_VAR     35
+struct te_subtlv_delay_var
+{
+  struct subtlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* interval in micro-seconds only 24 bits => 0 ... 16777215 */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */
+#define TE_SUBTLV_PKT_LOSS     36
+struct te_subtlv_pkt_loss
+{
+  struct subtlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* in percentage of total traffic only 24 bits (2^24 - 2)
+                                   with Anomalous Bit (A) as Upper most bit */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */
+#define TE_SUBTLV_RES_BW       37
+struct te_subtlv_res_bw
+{
+  struct subtlv_header header;  /* Value length is 4 bytes. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */
+#define TE_SUBTLV_AVA_BW       38
+struct te_subtlv_ava_bw
+{
+  struct subtlv_header header;  /* Value length is 4 octets. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+} __attribute__((__packed__));
+
+/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */
+#define TE_SUBTLV_USE_BW        39
+struct te_subtlv_use_bw
+{
+  struct subtlv_header header;  /* Value length is 4 octets. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+} __attribute__((__packed__));
+
+#define TE_SUBTLV_MAX          40      /* Last SUBTLV + 1 */
+
+/* Following declaration concerns the MPLS-TE and LINk-TE management */
+typedef enum _status_t { disable, enable, learn } status_t;
+
+/* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */
+typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t;
+
+#define IS_MPLS_TE(m)    (m.status == enable)
+#define IS_CIRCUIT_TE(c) (c->status == enable)
+
+/* Following structure are internal use only. */
+struct isis_mpls_te
+{
+  /* Status of MPLS-TE: enable or disable */
+  status_t status;
+
+  /* L1, L1-L2, L2-Only */
+  u_int8_t level;
+
+  /* RFC5316 */
+  interas_mode_t inter_as;
+  struct in_addr interas_areaid;
+
+  /* Circuit list on which TE are enable */
+  struct list *cir_list;
+
+  /* MPLS_TE router ID */
+  struct in_addr router_id;
+};
+
+extern struct isis_mpls_te isisMplsTE;
+
+struct mpls_te_circuit
+{
+
+  /* Status of MPLS-TE on this interface */
+  status_t status;
+
+  /* Type of MPLS-TE circuit: STD_TE(RFC5305), INTER_AS(RFC5316), INTER_AS_EMU(RFC5316 emulated) */
+  u_int8_t type;
+
+  /* Total size of sub_tlvs */
+  u_char length;
+
+  /* Store subTLV in network byte order. */
+  /* RFC5305 */
+  struct te_subtlv_admin_grp admin_grp;
+  /* RFC5307 */
+  struct te_subtlv_llri llri;
+  /* RFC5305 */
+  struct te_subtlv_local_ipaddr local_ipaddr;
+  struct te_subtlv_rmt_ipaddr rmt_ipaddr;
+  struct te_subtlv_max_bw max_bw;
+  struct te_subtlv_max_rsv_bw max_rsv_bw;
+  struct te_subtlv_unrsv_bw unrsv_bw;
+  struct te_subtlv_te_metric te_metric;
+  /* RFC5316 */
+  struct te_subtlv_ras ras;
+  struct te_subtlv_rip rip;
+  /* draft-ietf-isis-te-metric-extension */
+  struct te_subtlv_av_delay av_delay;
+  struct te_subtlv_mm_delay mm_delay;
+  struct te_subtlv_delay_var delay_var;
+  struct te_subtlv_pkt_loss pkt_loss;
+  struct te_subtlv_res_bw res_bw;
+  struct te_subtlv_ava_bw ava_bw;
+  struct te_subtlv_use_bw use_bw;
+};
+
+/* Prototypes. */
+void isis_mpls_te_init (void);
+struct mpls_te_circuit *mpls_te_circuit_new(void);
+void mpls_te_print_detail(struct vty *, struct te_is_neigh *);
+void set_circuitparams_local_ipaddr (struct mpls_te_circuit *, struct in_addr);
+void set_circuitparams_rmt_ipaddr (struct mpls_te_circuit *, struct in_addr);
+u_char subtlvs_len (struct mpls_te_circuit *);
+u_char add_te_subtlvs(u_char *, struct mpls_te_circuit *);
+u_char build_te_subtlvs(u_char *, struct isis_circuit *);
+void isis_link_params_update(struct isis_circuit *, struct interface *);
+void isis_mpls_te_update(struct interface *);
+void isis_mpls_te_config_write_router (struct vty *);
+
+#endif /* _ZEBRA_ISIS_MPLS_TE_H */
diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c
new file mode 100644 (file)
index 0000000..1d29d78
--- /dev/null
@@ -0,0 +1,1196 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_tlv.c
+ *                             IS-IS TLV related routines
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "linklist.h"
+#include "stream.h"
+#include "memory.h"
+#include "prefix.h"
+#include "vty.h"
+#include "if.h"
+
+#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/isis_tlv.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_te.h"
+
+void
+free_tlv (void *val)
+{
+  XFREE (MTYPE_ISIS_TLV, val);
+
+  return;
+}
+
+/*
+ * Called after parsing of a PDU. There shouldn't be any tlv's left, so this
+ * is only a caution to avoid memory leaks
+ */
+void
+free_tlvs (struct tlvs *tlvs)
+{
+  if (tlvs->area_addrs)
+    list_delete (tlvs->area_addrs);
+  if (tlvs->is_neighs)
+    list_delete (tlvs->is_neighs);
+  if (tlvs->te_is_neighs)
+    list_delete (tlvs->te_is_neighs);
+  if (tlvs->es_neighs)
+    list_delete (tlvs->es_neighs);
+  if (tlvs->lsp_entries)
+    list_delete (tlvs->lsp_entries);
+  if (tlvs->prefix_neighs)
+    list_delete (tlvs->prefix_neighs);
+  if (tlvs->lan_neighs)
+    list_delete (tlvs->lan_neighs);
+  if (tlvs->ipv4_addrs)
+    list_delete (tlvs->ipv4_addrs);
+  if (tlvs->ipv4_int_reachs)
+    list_delete (tlvs->ipv4_int_reachs);
+  if (tlvs->ipv4_ext_reachs)
+    list_delete (tlvs->ipv4_ext_reachs);
+  if (tlvs->te_ipv4_reachs)
+    list_delete (tlvs->te_ipv4_reachs);
+#ifdef HAVE_IPV6
+  if (tlvs->ipv6_addrs)
+    list_delete (tlvs->ipv6_addrs);
+  if (tlvs->ipv6_reachs)
+    list_delete (tlvs->ipv6_reachs);
+#endif /* HAVE_IPV6 */
+
+  memset (tlvs, 0, sizeof (struct tlvs));
+
+  return;
+}
+
+/*
+ * Parses the tlvs found in the variant length part of the PDU.
+ * Caller tells with flags in "expected" which TLV's it is interested in.
+ */
+int
+parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
+           u_int32_t * found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset)
+{
+  u_char type, length;
+  struct lan_neigh *lan_nei;
+  struct area_addr *area_addr;
+  struct is_neigh *is_nei;
+  struct te_is_neigh *te_is_nei;
+  struct es_neigh *es_nei;
+  struct lsp_entry *lsp_entry;
+  struct in_addr *ipv4_addr;
+  struct ipv4_reachability *ipv4_reach;
+  struct te_ipv4_reachability *te_ipv4_reach;
+#ifdef HAVE_IPV6
+  struct in6_addr *ipv6_addr;
+  struct ipv6_reachability *ipv6_reach;
+  int prefix_octets;
+#endif /* HAVE_IPV6 */
+  int value_len, retval = ISIS_OK;
+  u_char *start = stream, *pnt = stream, *endpnt;
+
+  *found = 0;
+  memset (tlvs, 0, sizeof (struct tlvs));
+
+  while (pnt < stream + size - 2)
+    {
+      type = *pnt;
+      length = *(pnt + 1);
+      pnt += 2;
+      value_len = 0;
+      if (pnt + length > stream + size)
+       {
+         zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) exceeds packet "
+                    "boundaries", areatag, type, length);
+         retval = ISIS_WARNING;
+         break;
+       }
+      switch (type)
+       {
+       case AREA_ADDRESSES:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        Address Length                         | 
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                         Area Address                          | 
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_AREA_ADDRS;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("TLV Area Adresses len %d", length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_AREA_ADDRS)
+           {
+             while (length > value_len)
+               {
+                 area_addr = (struct area_addr *) pnt;
+                 value_len += area_addr->addr_len + 1;
+                 pnt += area_addr->addr_len + 1;
+                 if (!tlvs->area_addrs)
+                   tlvs->area_addrs = list_new ();
+                 listnode_add (tlvs->area_addrs, area_addr);
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case IS_NEIGHBOURS:
+         *found |= TLVFLAG_IS_NEIGHS;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IS Neighbours length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (TLVFLAG_IS_NEIGHS & *expected)
+           {
+             /* +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |                        Virtual Flag                           | 
+              * +-------+-------+-------+-------+-------+-------+-------+-------+
+              */
+             pnt++;
+             value_len++;
+             /* +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |   0   |  I/E  |               Default Metric                  | 
+              * +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |   S   |  I/E  |               Delay Metric                    |
+              * +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |   S   |  I/E  |               Expense Metric                  |
+              * +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |   S   |  I/E  |               Error Metric                    |
+              * +-------+-------+-------+-------+-------+-------+-------+-------+
+              * |                        Neighbour ID                           |
+              * +---------------------------------------------------------------+
+              * :                                                               :
+              */
+             while (length > value_len)
+               {
+                 is_nei = (struct is_neigh *) pnt;
+                 value_len += 4 + ISIS_SYS_ID_LEN + 1;
+                 pnt += 4 + ISIS_SYS_ID_LEN + 1;
+                 if (!tlvs->is_neighs)
+                   tlvs->is_neighs = list_new ();
+                 listnode_add (tlvs->is_neighs, is_nei);
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case TE_IS_NEIGHBOURS:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        Neighbour ID                           | 7
+          * +---------------------------------------------------------------+
+          * |                        TE Metric                              | 3
+          * +---------------------------------------------------------------+
+          * |                        SubTLVs Length                         | 1
+          * +---------------------------------------------------------------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_TE_IS_NEIGHS;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d",
+                    areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (TLVFLAG_TE_IS_NEIGHS & *expected)
+           {
+             while (length > value_len)
+               {
+                 te_is_nei = (struct te_is_neigh *) pnt;
+                 value_len += IS_NEIGHBOURS_LEN;
+                 pnt += IS_NEIGHBOURS_LEN;
+                  /* FIXME - subtlvs are handled here, for now we skip */
+                 /* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */
+                 /* So, it must be copied in a new te_is_neigh structure        */
+                 /* rather than just initialize pointer to the original LSP PDU */
+                 /* to avoid consider the rest of lspdu as subTLVs or buffer overflow */
+                 if (IS_MPLS_TE(isisMplsTE))
+                   {
+                     struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh));
+                     memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1);
+                     memcpy(new->te_metric, te_is_nei->te_metric, 3);
+                     new->sub_tlvs_length = te_is_nei->sub_tlvs_length;
+                     memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length);
+                      te_is_nei = new;
+                    }
+                 /* Skip SUB TLVs payload */
+                 value_len += te_is_nei->sub_tlvs_length;
+                 pnt += te_is_nei->sub_tlvs_length;
+
+                 if (!tlvs->te_is_neighs)
+                   tlvs->te_is_neighs = list_new ();
+                 listnode_add (tlvs->te_is_neighs, te_is_nei);
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case ES_NEIGHBOURS:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   0   |  I/E  |               Default Metric                  | 
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Delay Metric                    |
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Expense Metric                  |
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Error Metric                    |
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        Neighbour ID                           |
+          * +---------------------------------------------------------------+
+          * |                        Neighbour ID                           |
+          * +---------------------------------------------------------------+
+          * :                                                               :
+          */
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): ES Neighbours length %d",
+                    areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         *found |= TLVFLAG_ES_NEIGHS;
+         if (*expected & TLVFLAG_ES_NEIGHS)
+           {
+             es_nei = (struct es_neigh *) pnt;
+             value_len += 4;
+             pnt += 4;
+             while (length > value_len)
+               {
+                 /* FIXME FIXME FIXME - add to the list */
+                 /*          sys_id->id = pnt; */
+                 value_len += ISIS_SYS_ID_LEN;
+                 pnt += ISIS_SYS_ID_LEN;
+                 /*  if (!es_nei->neigh_ids) es_nei->neigh_ids = sysid; */
+               }
+             if (!tlvs->es_neighs)
+               tlvs->es_neighs = list_new ();
+             listnode_add (tlvs->es_neighs, es_nei);
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case LAN_NEIGHBOURS:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        LAN Address                            | 
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_LAN_NEIGHS;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): LAN Neigbours length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (TLVFLAG_LAN_NEIGHS & *expected)
+           {
+             while (length > value_len)
+               {
+                 lan_nei = (struct lan_neigh *) pnt;
+                 if (!tlvs->lan_neighs)
+                   tlvs->lan_neighs = list_new ();
+                 listnode_add (tlvs->lan_neighs, lan_nei);
+                 value_len += ETH_ALEN;
+                 pnt += ETH_ALEN;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case PADDING:
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("TLV padding %d", length);
+#endif /* EXTREME_TLV_DEBUG */
+         pnt += length;
+         break;
+
+       case LSP_ENTRIES:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                     Remaining Lifetime                        | 2
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                         LSP ID                                | id+2
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                   LSP Sequence Number                         | 4
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        Checksum                               | 2
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          */
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): LSP Entries length %d", areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         *found |= TLVFLAG_LSP_ENTRIES;
+         if (TLVFLAG_LSP_ENTRIES & *expected)
+           {
+             while (length > value_len)
+               {
+                 lsp_entry = (struct lsp_entry *) pnt;
+                 value_len += 10 + ISIS_SYS_ID_LEN;
+                 pnt += 10 + ISIS_SYS_ID_LEN;
+                 if (!tlvs->lsp_entries)
+                   tlvs->lsp_entries = list_new ();
+                 listnode_add (tlvs->lsp_entries, lsp_entry);
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case CHECKSUM:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                   16 bit fletcher CHECKSUM                    |
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_CHECKSUM;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): Checksum length %d", areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_CHECKSUM)
+           {
+             tlvs->checksum = (struct checksum *) pnt;
+           }
+         pnt += length;
+         break;
+
+       case PROTOCOLS_SUPPORTED:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                       NLPID                                   |
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_NLPID;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): Protocols Supported length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_NLPID)
+           {
+             tlvs->nlpids = (struct nlpids *) (pnt - 1);
+           }
+         pnt += length;
+         break;
+
+       case IPV4_ADDR:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * +                 IP version 4 address                          + 4
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_IPV4_ADDR;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv4 Address length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_IPV4_ADDR)
+           {
+             while (length > value_len)
+               {
+                 ipv4_addr = (struct in_addr *) pnt;
+#ifdef EXTREME_TLV_DEBUG
+                 zlog_debug ("ISIS-TLV (%s) : IP ADDR %s, pnt %p", areatag,
+                             inet_ntoa (*ipv4_addr), pnt);
+#endif /* EXTREME_TLV_DEBUG */
+                 if (!tlvs->ipv4_addrs)
+                   tlvs->ipv4_addrs = list_new ();
+                 listnode_add (tlvs->ipv4_addrs, ipv4_addr);
+                 value_len += 4;
+                 pnt += 4;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case AUTH_INFO:
+         *found |= TLVFLAG_AUTH_INFO;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IS-IS Authentication Information",
+                     areatag);
+#endif
+         if (*expected & TLVFLAG_AUTH_INFO)
+           {
+             tlvs->auth_info.type = *pnt;
+              if (length == 0)
+                {
+                  zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) "
+                             "incorrect.", areatag, type, length);
+                  return ISIS_WARNING;
+                }
+              --length;
+             tlvs->auth_info.len = length;
+             pnt++;
+             memcpy (tlvs->auth_info.passwd, pnt, length);
+              /* Return the authentication tlv pos for later computation
+               * of MD5 (RFC 5304, 2)
+               */
+              if (auth_tlv_offset)
+                *auth_tlv_offset += (pnt - start - 3);
+              pnt += length;
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case DYNAMIC_HOSTNAME:
+         *found |= TLVFLAG_DYN_HOSTNAME;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): Dynamic Hostname length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_DYN_HOSTNAME)
+           {
+             /* the length is also included in the pointed struct */
+             tlvs->hostname = (struct hostname *) (pnt - 1);
+           }
+         pnt += length;
+         break;
+
+       case TE_ROUTER_ID:
+         /* +---------------------------------------------------------------+
+          * +                         Router ID                             + 4
+          * +---------------------------------------------------------------+
+          */
+         *found |= TLVFLAG_TE_ROUTER_ID;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): TE Router ID %d", areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_TE_ROUTER_ID)
+           tlvs->router_id = (struct te_router_id *) (pnt);
+         pnt += length;
+         break;
+
+       case IPV4_INT_REACHABILITY:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   0   |  I/E  |               Default Metric                  | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Delay Metric                    | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Expense Metric                  | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Error Metric                    | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        ip address                             | 4
+          * +---------------------------------------------------------------+
+          * |                        address mask                           | 4
+          * +---------------------------------------------------------------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_IPV4_INT_REACHABILITY;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv4 internal Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_IPV4_INT_REACHABILITY)
+           {
+             while (length > value_len)
+               {
+                 ipv4_reach = (struct ipv4_reachability *) pnt;
+                 if (!tlvs->ipv4_int_reachs)
+                   tlvs->ipv4_int_reachs = list_new ();
+                 listnode_add (tlvs->ipv4_int_reachs, ipv4_reach);
+                 value_len += 12;
+                 pnt += 12;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case IPV4_EXT_REACHABILITY:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   0   |  I/E  |               Default Metric                  | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Delay Metric                    | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Expense Metric                  | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |   S   |  I/E  |               Error Metric                    | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        ip address                             | 4
+          * +---------------------------------------------------------------+
+          * |                        address mask                           | 4
+          * +---------------------------------------------------------------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_IPV4_EXT_REACHABILITY;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv4 external Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_IPV4_EXT_REACHABILITY)
+           {
+             while (length > value_len)
+               {
+                 ipv4_reach = (struct ipv4_reachability *) pnt;
+                 if (!tlvs->ipv4_ext_reachs)
+                   tlvs->ipv4_ext_reachs = list_new ();
+                 listnode_add (tlvs->ipv4_ext_reachs, ipv4_reach);
+                 value_len += 12;
+                 pnt += 12;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case TE_IPV4_REACHABILITY:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        TE Metric                              | 4
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |  U/D  | sTLV? |               Prefix Mask Len                 | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                           Prefix                              | 0-4
+          * +---------------------------------------------------------------+
+          * |                         sub tlvs                              |
+          * +---------------------------------------------------------------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_TE_IPV4_REACHABILITY;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         endpnt = pnt + length;
+         if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
+           {
+             while (length > value_len)
+               {
+                 te_ipv4_reach = (struct te_ipv4_reachability *) pnt;
+                 if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN)
+                   {
+                     zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach"
+                                "ability prefix length %d", areatag,
+                                te_ipv4_reach->control & 0x3F);
+                     retval = ISIS_WARNING;
+                     break;
+                   }
+                 if (!tlvs->te_ipv4_reachs)
+                   tlvs->te_ipv4_reachs = list_new ();
+                 listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach);
+                 /* this trickery is permitable since no subtlvs are defined */
+                 value_len += 5 + ((te_ipv4_reach->control & 0x3F) ?
+                                   ((((te_ipv4_reach->control & 0x3F) -
+                                      1) >> 3) + 1) : 0);
+                 pnt += 5 + ((te_ipv4_reach->control & 0x3F) ?
+                             ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0);
+               }
+           }
+
+         pnt = endpnt;
+         break;
+
+#ifdef  HAVE_IPV6
+       case IPV6_ADDR:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * +                 IP version 6 address                          + 16
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * :                                                               :
+          */
+         *found |= TLVFLAG_IPV6_ADDR;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv6 Address length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_IPV6_ADDR)
+           {
+             while (length > value_len)
+               {
+                 ipv6_addr = (struct in6_addr *) pnt;
+                 if (!tlvs->ipv6_addrs)
+                   tlvs->ipv6_addrs = list_new ();
+                 listnode_add (tlvs->ipv6_addrs, ipv6_addr);
+                 value_len += 16;
+                 pnt += 16;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+         break;
+
+       case IPV6_REACHABILITY:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                 Default Metric                                | 4 
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                        Control Informantion                   |
+          * +---------------------------------------------------------------+
+          * |                        IPv6 Prefix Length                     |--+
+          * +---------------------------------------------------------------+  |
+          * |                        IPv6 Prefix                            |<-+
+          * +---------------------------------------------------------------+
+          */
+         *found |= TLVFLAG_IPV6_REACHABILITY;
+         endpnt = pnt + length;
+
+         if (*expected & TLVFLAG_IPV6_REACHABILITY)
+           {
+             while (length > value_len)
+               {
+                 ipv6_reach = (struct ipv6_reachability *) pnt;
+                 if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN)
+                   {
+                     zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach"
+                                "ability prefix length %d", areatag,
+                                ipv6_reach->prefix_len);
+                     retval = ISIS_WARNING;
+                     break;
+                   }
+
+                 prefix_octets = ((ipv6_reach->prefix_len + 7) / 8);
+                 value_len += prefix_octets + 6;
+                 pnt += prefix_octets + 6;
+                 /* FIXME: sub-tlvs */
+                 if (!tlvs->ipv6_reachs)
+                   tlvs->ipv6_reachs = list_new ();
+                 listnode_add (tlvs->ipv6_reachs, ipv6_reach);
+               }
+           }
+
+         pnt = endpnt;
+         break;
+#endif /* HAVE_IPV6 */
+
+       case WAY3_HELLO:
+         /* +---------------------------------------------------------------+
+          * |                  Adjacency state                              | 1
+          * +---------------------------------------------------------------+
+          * |                  Extended Local Circuit ID                    | 4
+          * +---------------------------------------------------------------+
+          * |                  Neighbor System ID (If known)                | 0-8
+          *                                      (probably 6)
+          * +---------------------------------------------------------------+
+          * |                  Neighbor Local Circuit ID (If known)         | 4
+          * +---------------------------------------------------------------+
+          */
+         *found |= TLVFLAG_3WAY_HELLO;
+         if (*expected & TLVFLAG_3WAY_HELLO)
+           {
+             while (length > value_len)
+               {
+                 /* FIXME: make this work */
+/*           Adjacency State (one octet):
+              0 = Up
+              1 = Initializing
+              2 = Down
+            Extended Local Circuit ID (four octets)
+            Neighbor System ID if known (zero to eight octets)
+            Neighbor Extended Local Circuit ID (four octets, if Neighbor
+              System ID is present) */
+                 pnt += length;
+                 value_len += length;
+               }
+           }
+         else
+           {
+             pnt += length;
+           }
+
+         break;
+       case GRACEFUL_RESTART:
+         /* +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |         Reserved                      |  SA   |  RA   |  RR   | 1
+          * +-------+-------+-------+-------+-------+-------+-------+-------+
+          * |                          Remaining Time                       | 2
+          * +---------------------------------------------------------------+
+          * |                Restarting Neighbor ID (If known)              | 0-8
+          * +---------------------------------------------------------------+
+          */
+         *found |= TLVFLAG_GRACEFUL_RESTART;
+         if (*expected & TLVFLAG_GRACEFUL_RESTART)
+           {
+             /* FIXME: make this work */
+           }
+         pnt += length;
+         break;
+
+       default:
+         zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d",
+                    areatag, type, length);
+
+         pnt += length;
+         break;
+       }
+    }
+
+  return retval;
+}
+
+int
+add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
+{
+  if ((stream_get_size (stream) - stream_get_endp (stream)) <
+      (((unsigned)len) + 2))
+    {
+      zlog_warn ("No room for TLV of type %d "
+                 "(total size %d available %d required %d)",
+                 tag, (int)stream_get_size (stream),
+                 (int)(stream_get_size (stream) - stream_get_endp (stream)),
+                 len+2);
+      return ISIS_WARNING;
+    }
+
+  stream_putc (stream, tag);   /* TAG */
+  stream_putc (stream, len);   /* LENGTH */
+  stream_put (stream, value, (int) len);       /* VALUE */
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("Added TLV %d len %d", tag, len);
+#endif /* EXTREME DEBUG */
+  return ISIS_OK;
+}
+
+int
+tlv_add_area_addrs (struct list *area_addrs, struct stream *stream)
+{
+  struct listnode *node;
+  struct area_addr *area_addr;
+
+  u_char value[255];
+  u_char *pos = value;
+
+  for (ALL_LIST_ELEMENTS_RO (area_addrs, node, area_addr))
+    {
+      if (pos - value + area_addr->addr_len > 255)
+       goto err;
+      *pos = area_addr->addr_len;
+      pos++;
+      memcpy (pos, area_addr->area_addr, (int) area_addr->addr_len);
+      pos += area_addr->addr_len;
+    }
+
+  return add_tlv (AREA_ADDRESSES, pos - value, value, stream);
+
+err:
+  zlog_warn ("tlv_add_area_addrs(): TLV longer than 255");
+  return ISIS_WARNING;
+}
+
+int
+tlv_add_is_neighs (struct list *is_neighs, struct stream *stream)
+{
+  struct listnode *node;
+  struct is_neigh *is_neigh;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  *pos = 0;                    /*is_neigh->virtual; */
+  pos++;
+
+  for (ALL_LIST_ELEMENTS_RO (is_neighs, node, is_neigh))
+    {
+      if (pos - value + IS_NEIGHBOURS_LEN > 255)
+       {
+         retval = add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      *pos = is_neigh->metrics.metric_default;
+      pos++;
+      *pos = is_neigh->metrics.metric_delay;
+      pos++;
+      *pos = is_neigh->metrics.metric_expense;
+      pos++;
+      *pos = is_neigh->metrics.metric_error;
+      pos++;
+      memcpy (pos, is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
+      pos += ISIS_SYS_ID_LEN + 1;
+    }
+
+  return add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
+}
+
+int
+tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
+{
+  struct listnode *node;
+  struct te_is_neigh *te_is_neigh;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh))
+    {
+      /* FIXME: Check if Total SubTLVs size doesn't exceed 255 */
+      if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255)
+        {
+          retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
+          if (retval != ISIS_OK)
+            return retval;
+          pos = value;
+        }
+      
+      memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
+      pos += ISIS_SYS_ID_LEN + 1;
+      memcpy (pos, te_is_neigh->te_metric, 3);
+      pos += 3;
+      /* Set the total size of Sub TLVs */
+      *pos = te_is_neigh->sub_tlvs_length;
+      pos++;
+      /* Copy Sub TLVs if any */
+      if (te_is_neigh->sub_tlvs_length > 0)
+        {
+          memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length);
+          pos += te_is_neigh->sub_tlvs_length;
+        }
+    }
+
+  return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
+}
+
+int
+tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream)
+{
+  struct listnode *node;
+  u_char *snpa;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (lan_neighs, node, snpa))
+    {
+      if (pos - value + ETH_ALEN > 255)
+       {
+         retval = add_tlv (LAN_NEIGHBOURS, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      memcpy (pos, snpa, ETH_ALEN);
+      pos += ETH_ALEN;
+    }
+
+  return add_tlv (LAN_NEIGHBOURS, pos - value, value, stream);
+}
+
+int
+tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream)
+{
+  return add_tlv (PROTOCOLS_SUPPORTED, nlpids->count, nlpids->nlpids, stream);
+}
+
+int
+tlv_add_authinfo (u_char auth_type, u_char auth_len, u_char *auth_value,
+                 struct stream *stream)
+{
+  u_char value[255];
+  u_char *pos = value;
+  *pos++ = auth_type;
+  memcpy (pos, auth_value, auth_len);
+
+  return add_tlv (AUTH_INFO, auth_len + 1, value, stream);
+}
+
+int
+tlv_add_checksum (struct checksum *checksum, struct stream *stream)
+{
+  u_char value[255];
+  u_char *pos = value;
+  return add_tlv (CHECKSUM, pos - value, value, stream);
+}
+
+int
+tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream)
+{
+  struct listnode *node;
+  struct prefix_ipv4 *ipv4;
+  u_char value[255];
+  u_char *pos = value;
+
+  for (ALL_LIST_ELEMENTS_RO (ip_addrs, node, ipv4))
+    {
+      if (pos - value + IPV4_MAX_BYTELEN > 255)
+       {
+         /* RFC 1195 s4.2: only one tuple of 63 allowed. */
+         zlog_warn ("tlv_add_ip_addrs(): cutting off at 63 IP addresses");
+         break;
+       }
+      *(u_int32_t *) pos = ipv4->prefix.s_addr;
+      pos += IPV4_MAX_BYTELEN;
+    }
+
+  return add_tlv (IPV4_ADDR, pos - value, value, stream);
+}
+
+/* Used to add TLV containing just one IPv4 address - either IPv4 address TLV
+ * (in case of LSP) or TE router ID TLV. */
+int
+tlv_add_in_addr (struct in_addr *addr, struct stream *stream, u_char tag)
+{
+  u_char value[255];
+  u_char *pos = value;
+  
+  memcpy (pos, addr, IPV4_MAX_BYTELEN);
+  pos += IPV4_MAX_BYTELEN;
+
+  return add_tlv (tag, pos - value, value, stream);
+}
+
+int
+tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream)
+{
+  return add_tlv (DYNAMIC_HOSTNAME, hostname->namelen, hostname->name,
+                 stream);
+}
+
+int
+tlv_add_lsp_entries (struct list *lsps, struct stream *stream)
+{
+  struct listnode *node;
+  struct isis_lsp *lsp;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp))
+    {
+      if (pos - value + LSP_ENTRIES_LEN > 255)
+       {
+         retval = add_tlv (LSP_ENTRIES, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      *((u_int16_t *) pos) = lsp->lsp_header->rem_lifetime;
+      pos += 2;
+      memcpy (pos, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2);
+      pos += ISIS_SYS_ID_LEN + 2;
+      *((u_int32_t *) pos) = lsp->lsp_header->seq_num;
+      pos += 4;
+      *((u_int16_t *) pos) = lsp->lsp_header->checksum;
+      pos += 2;
+    }
+
+  return add_tlv (LSP_ENTRIES, pos - value, value, stream);
+}
+
+static int
+tlv_add_ipv4_reachs (u_char tag, struct list *ipv4_reachs, struct stream *stream)
+{
+  struct listnode *node;
+  struct ipv4_reachability *reach;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (ipv4_reachs, node, reach))
+    {
+      if (pos - value + IPV4_REACH_LEN > 255)
+       {
+         retval =
+           add_tlv (tag, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      *pos = reach->metrics.metric_default;
+      pos++;
+      *pos = reach->metrics.metric_delay;
+      pos++;
+      *pos = reach->metrics.metric_expense;
+      pos++;
+      *pos = reach->metrics.metric_error;
+      pos++;
+      *(u_int32_t *) pos = reach->prefix.s_addr;
+      pos += IPV4_MAX_BYTELEN;
+      *(u_int32_t *) pos = reach->mask.s_addr;
+      pos += IPV4_MAX_BYTELEN;
+    }
+
+  return add_tlv (tag, pos - value, value, stream);
+}
+
+int
+tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream)
+{
+  return tlv_add_ipv4_reachs(IPV4_INT_REACHABILITY, ipv4_reachs, stream);
+}
+
+int
+tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream)
+{
+  return tlv_add_ipv4_reachs(IPV4_EXT_REACHABILITY, ipv4_reachs, stream);
+}
+
+
+int
+tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream)
+{
+  struct listnode *node;
+  struct te_ipv4_reachability *te_reach;
+  u_char value[255];
+  u_char *pos = value;
+  u_char prefix_size;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach))
+    {
+      prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1);
+
+      if (pos - value + (5 + prefix_size) > 255)
+       {
+         retval =
+           add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      *(u_int32_t *) pos = te_reach->te_metric;
+      pos += 4;
+      *pos = te_reach->control;
+      pos++;
+      memcpy (pos, &te_reach->prefix_start, prefix_size);
+      pos += prefix_size;
+    }
+
+  return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
+}
+
+#ifdef HAVE_IPV6
+int
+tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream)
+{
+  struct listnode *node;
+  struct prefix_ipv6 *ipv6;
+  u_char value[255];
+  u_char *pos = value;
+  int retval;
+
+  for (ALL_LIST_ELEMENTS_RO (ipv6_addrs, node, ipv6))
+    {
+      if (pos - value + IPV6_MAX_BYTELEN > 255)
+       {
+         retval = add_tlv (IPV6_ADDR, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      memcpy (pos, ipv6->prefix.s6_addr, IPV6_MAX_BYTELEN);
+      pos += IPV6_MAX_BYTELEN;
+    }
+
+  return add_tlv (IPV6_ADDR, pos - value, value, stream);
+}
+
+int
+tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream)
+{
+  struct listnode *node;
+  struct ipv6_reachability *ip6reach;
+  u_char value[255];
+  u_char *pos = value;
+  int retval, prefix_octets;
+
+  for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach))
+    {
+      if (pos - value + IPV6_MAX_BYTELEN + 6 > 255)
+       {
+         retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
+         if (retval != ISIS_OK)
+           return retval;
+         pos = value;
+       }
+      *(uint32_t *) pos = ip6reach->metric;
+      pos += 4;
+      *pos = ip6reach->control_info;
+      pos++;
+      prefix_octets = ((ip6reach->prefix_len + 7) / 8);
+      *pos = ip6reach->prefix_len;
+      pos++;
+      memcpy (pos, ip6reach->prefix, prefix_octets);
+      pos += prefix_octets;
+    }
+
+  return add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
+}
+#endif /* HAVE_IPV6 */
+
+int
+tlv_add_padding (struct stream *stream)
+{
+  int fullpads, i, left;
+
+  /*
+   * How many times can we add full padding ?
+   */
+  fullpads = (stream_get_size (stream) - stream_get_endp (stream)) / 257;
+  for (i = 0; i < fullpads; i++)
+    {
+      if (!stream_putc (stream, (u_char) PADDING))     /* TAG */
+       goto err;
+      if (!stream_putc (stream, (u_char) 255)) /* LENGHT */
+       goto err;
+      stream_put (stream, NULL, 255);          /* zero padding */
+    }
+
+  left = stream_get_size (stream) - stream_get_endp (stream);
+
+  if (left < 2)
+    return ISIS_OK;
+
+  if (left == 2)
+    {
+      stream_putc (stream, PADDING);
+      stream_putc (stream, 0);
+      return ISIS_OK;
+    }
+
+  stream_putc (stream, PADDING);
+  stream_putc (stream, left - 2);
+  stream_put (stream, NULL, left-2);
+
+  return ISIS_OK;
+
+err:
+  zlog_warn ("tlv_add_padding(): no room for tlv");
+  return ISIS_WARNING;
+}
diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h
new file mode 100644 (file)
index 0000000..5a39d56
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_tlv.h
+ *                             IS-IS TLV related routines
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_ISIS_TLV_H
+#define _ZEBRA_ISIS_TLV_H
+
+/*
+ * The list of TLVs we (should) support.
+ * ____________________________________________________________________________
+ * Name                   Value  IIH LSP SNP Status
+ *                               LAN
+ * ____________________________________________________________________________
+ *
+ * Area Addresses             1   y   y   n  ISO10589
+ * IIS Neighbors              2   n   y   n  ISO10589
+ * ES Neighbors               3   n   y   n  ISO10589
+ * IIS Neighbors              6   y   n   n  ISO10589
+ * Padding                    8   y   n   n  ISO10589
+ * LSP Entries                9   n   n   y  ISO10589
+ * Authentication            10   y   y   y  ISO10589, RFC3567
+ * Checksum                  12   y   n   y  RFC3358
+ * Extended IS Reachability  22   n   y   n  RFC5305
+ * IS Alias                  24   n   y   n  RFC3786
+ * IP Int. Reachability     128   n   y   n  RFC1195
+ * Protocols Supported      129   y   y   n  RFC1195
+ * IP Ext. Reachability     130   n   y   n  RFC1195
+ * IDRPI                    131   n   y   y  RFC1195
+ * IP Interface Address     132   y   y   n  RFC1195
+ * TE Router ID             134   n   y   n  RFC5305
+ * Extended IP Reachability 135   n   y   n  RFC5305
+ * Dynamic Hostname         137   n   y   n  RFC2763
+ * Shared Risk Link Group   138   n   y   y  RFC5307
+ * Inter-AS Reachability    141   n   y   n  RFC5316
+ * Restart TLV              211   y   n   n  RFC3847
+ * MT IS Reachability       222   n   y   n  RFC5120
+ * MT Supported             229   y   y   n  RFC5120
+ * IPv6 Interface Address   232   y   y   n  RFC5308
+ * MT IP Reachability       235   n   y   n  RFC5120
+ * IPv6 IP Reachability     236   n   y   n  RFC5308
+ * MT IPv6 IP Reachability  237   n   y   n  RFC5120
+ * P2P Adjacency State      240   y   n   n  RFC3373
+ * IIH Sequence Number      241   y   n   n  draft-shen-isis-iih-sequence
+ * Router Capability        242   n   y   n  RFC4971
+ *
+ *
+ * IS Reachability sub-TLVs we support (See isis_te.[c,h])
+ * ____________________________________________________________________________
+ * Name                           Value   Status
+ * ____________________________________________________________________________
+ * Administartive group (color)       3   RFC5305
+ * Link Local/Remote Identifiers      4   RFC5307
+ * IPv4 interface address             6   RFC5305
+ * IPv4 neighbor address              8   RFC5305
+ * Maximum link bandwidth             9   RFC5305
+ * Reservable link bandwidth         10   RFC5305
+ * Unreserved bandwidth              11   RFC5305
+ * TE Default metric                 18   RFC5305
+ * Link Protection Type              20   RFC5307
+ * Interface Switching Capability    21   RFC5307
+ * Remote AS number                  24   RFC5316
+ * IPv4 Remote ASBR identifier       25   RFC5316
+ *
+ *
+ * IP Reachability sub-TLVs we (should) support.
+ * ____________________________________________________________________________
+ * Name                           Value   Status
+ * ____________________________________________________________________________
+ * 32bit administrative tag           1   RFC5130
+ * 64bit administrative tag           2   RFC5130
+ * Management prefix color          117   RFC5120
+ */
+
+#define AREA_ADDRESSES            1
+#define IS_NEIGHBOURS             2
+#define ES_NEIGHBOURS             3
+#define LAN_NEIGHBOURS            6
+#define PADDING                   8
+#define LSP_ENTRIES               9
+#define AUTH_INFO                 10
+#define CHECKSUM                  12
+#define TE_IS_NEIGHBOURS          22
+#define IS_ALIAS                  24
+#define IPV4_INT_REACHABILITY     128
+#define PROTOCOLS_SUPPORTED       129
+#define IPV4_EXT_REACHABILITY     130
+#define IDRP_INFO                 131
+#define IPV4_ADDR                 132
+#define TE_ROUTER_ID              134
+#define TE_IPV4_REACHABILITY      135
+#define DYNAMIC_HOSTNAME          137
+#define GRACEFUL_RESTART          211
+#define IPV6_ADDR                 232
+#define IPV6_REACHABILITY         236
+#define WAY3_HELLO                240
+#define ROUTER_INFORMATION        242
+
+#define AUTH_INFO_HDRLEN          3
+
+#define MAX_TLV_LEN 255
+
+#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 TE_IPV4_REACH_LEN 9
+
+#define MAX_SUBTLV_SIZE         256
+
+/* struct for neighbor */
+struct is_neigh
+{
+  struct metric metrics;
+  u_char neigh_id[ISIS_SYS_ID_LEN + 1];
+};
+
+/* struct for te metric */
+struct te_is_neigh
+{
+  u_char neigh_id[ISIS_SYS_ID_LEN + 1];
+  u_char te_metric[3];
+  u_char sub_tlvs_length;
+  /* Theorical Maximum SubTLVs is 256 because the sub_tlvs_length is 8 bits */
+  /* Practically, 118 bytes are necessary to store all supported TE parameters */
+  /* FIXME: A pointer will use less memory, but need to be free */
+  /* which is hard to fix, especially within free_tlvs() function */
+  /* and malloc() / free() as a CPU cost compared to the memory usage */
+  u_char sub_tlvs[MAX_SUBTLV_SIZE];      /* SUB TLVs storage */
+};
+
+/* 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
+{
+  struct metric metrics;
+  /* approximate position of first, we use the
+   * length ((uchar*)metric-1) to know all     */
+  u_char first_es_neigh[ISIS_SYS_ID_LEN];
+
+};
+
+struct partition_desig_level2_is
+{
+  struct list *isis_system_ids;
+};
+
+/* struct for lan neighbors */
+struct lan_neigh
+{
+  u_char LAN_addr[6];
+};
+
+#ifdef __SUNPRO_C
+#pragma pack(1)
+#endif
+
+/* struct for LSP entry */
+struct lsp_entry
+{
+  u_int16_t rem_lifetime;
+  u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  u_int32_t seq_num;
+  u_int16_t checksum;
+} __attribute__ ((packed));
+
+#ifdef __SUNPRO_C
+#pragma pack()
+#endif
+
+/* struct for checksum */
+struct checksum
+{
+  u_int16_t checksum;
+};
+
+/* ipv4 reachability */
+struct ipv4_reachability
+{
+  struct metric metrics;
+  struct in_addr prefix;
+  struct in_addr mask;
+};
+
+/* te router id */
+struct te_router_id
+{
+  struct in_addr id;
+};
+
+/* te ipv4 reachability */
+struct te_ipv4_reachability
+{
+  u_int32_t te_metric;
+  u_char control;
+  u_char prefix_start;         /* since this is variable length by nature it only */
+};                             /* points to an approximate location */
+
+
+
+struct idrp_info
+{
+  u_char len;
+  u_char *value;
+};
+
+#ifdef HAVE_IPV6
+struct ipv6_reachability
+{
+  u_int32_t metric;
+  u_char control_info;
+  u_char prefix_len;
+  u_char prefix[16];
+};
+
+/* bits in control_info */
+#define CTRL_INFO_DIRECTION    0x80
+#define DIRECTION_UP           0x00
+#define DIRECTION_DOWN         0x80
+
+#define CTRL_INFO_DISTRIBUTION 0x40
+#define DISTRIBUTION_INTERNAL  0x00
+#define DISTRIBUTION_EXTERNAL  0x40
+
+#define CTRL_INFO_SUBTLVS      0x20
+#endif /* HAVE_IPV6 */
+
+/*
+ * Pointer to each tlv type, filled by parse_tlvs()
+ */
+struct tlvs
+{
+  struct checksum *checksum;
+  struct hostname *hostname;
+  struct nlpids *nlpids;
+  struct te_router_id *router_id;
+  struct list *area_addrs;
+  struct list *is_neighs;
+  struct list *te_is_neighs;
+  struct list *es_neighs;
+  struct list *lsp_entries;
+  struct list *prefix_neighs;
+  struct list *lan_neighs;
+  struct list *ipv4_addrs;
+  struct list *ipv4_int_reachs;
+  struct list *ipv4_ext_reachs;
+  struct list *te_ipv4_reachs;
+#ifdef HAVE_IPV6
+  struct list *ipv6_addrs;
+  struct list *ipv6_reachs;
+#endif
+  struct isis_passwd auth_info;
+};
+
+/*
+ * Own definitions - used to bitmask found and expected
+ */
+
+#define TLVFLAG_AREA_ADDRS                (1<<0)
+#define TLVFLAG_IS_NEIGHS                 (1<<1)
+#define TLVFLAG_ES_NEIGHS                 (1<<2)
+#define TLVFLAG_PARTITION_DESIG_LEVEL2_IS (1<<3)
+#define TLVFLAG_PREFIX_NEIGHS             (1<<4)
+#define TLVFLAG_LAN_NEIGHS                (1<<5)
+#define TLVFLAG_LSP_ENTRIES               (1<<6)
+#define TLVFLAG_PADDING                   (1<<7)
+#define TLVFLAG_AUTH_INFO                 (1<<8)
+#define TLVFLAG_IPV4_INT_REACHABILITY     (1<<9)
+#define TLVFLAG_NLPID                     (1<<10)
+#define TLVFLAG_IPV4_EXT_REACHABILITY     (1<<11)
+#define TLVFLAG_IPV4_ADDR                 (1<<12)
+#define TLVFLAG_DYN_HOSTNAME              (1<<13)
+#define TLVFLAG_IPV6_ADDR                 (1<<14)
+#define TLVFLAG_IPV6_REACHABILITY         (1<<15)
+#define TLVFLAG_TE_IS_NEIGHS              (1<<16)
+#define TLVFLAG_TE_IPV4_REACHABILITY      (1<<17)
+#define TLVFLAG_3WAY_HELLO                (1<<18)
+#define TLVFLAG_TE_ROUTER_ID              (1<<19)
+#define TLVFLAG_CHECKSUM                  (1<<20)
+#define TLVFLAG_GRACEFUL_RESTART          (1<<21)
+
+void init_tlvs (struct tlvs *tlvs, uint32_t expected);
+void free_tlvs (struct tlvs *tlvs);
+int parse_tlvs (char *areatag, u_char * stream, int size,
+               u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs,
+                u_int32_t * auth_tlv_offset);
+int add_tlv (u_char, u_char, u_char *, struct stream *);
+void free_tlv (void *val);
+
+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);
+int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream);
+int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream);
+int tlv_add_checksum (struct checksum *checksum, struct stream *stream);
+int tlv_add_authinfo (u_char auth_type, u_char authlen, u_char *auth_value,
+                     struct stream *stream);
+int tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream);
+int tlv_add_in_addr (struct in_addr *, struct stream *stream, u_char tag);
+int tlv_add_dynamic_hostname (struct hostname *hostname,
+                             struct stream *stream);
+int tlv_add_lsp_entries (struct list *lsps, struct stream *stream);
+int tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream);
+int tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream);
+int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream);
+#ifdef HAVE_IPV6
+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_padding (struct stream *stream);
+
+#endif /* _ZEBRA_ISIS_TLV_H */
diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c
new file mode 100644 (file)
index 0000000..4148eb5
--- /dev/null
@@ -0,0 +1,2428 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_circuit.h
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ * Copyright (C) 2016        David Lamparter, for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <command.h>
+
+#include "isis_circuit.h"
+#include "isis_csm.h"
+#include "isis_misc.h"
+#include "isisd.h"
+
+static struct isis_circuit *
+isis_circuit_lookup (struct vty *vty)
+{
+  struct interface *ifp;
+  struct isis_circuit *circuit;
+
+  ifp = (struct interface *) vty->index;
+  if (!ifp)
+    {
+      vty_out (vty, "Invalid interface %s", VTY_NEWLINE);
+      return NULL;
+    }
+
+  circuit = circuit_scan_by_ifp (ifp);
+  if (!circuit)
+    {
+      vty_out (vty, "ISIS is not enabled on circuit %s%s",
+               ifp->name, VTY_NEWLINE);
+      return NULL;
+    }
+
+  return circuit;
+}
+
+DEFUN (ip_router_isis,
+       ip_router_isis_cmd,
+       "(ip|ipv6) router isis WORD",
+       "Interface Internet Protocol config commands\n"
+       "IP router interface commands\n"
+       "IS-IS Routing for IP\n"
+       "Routing process tag\n")
+{
+  struct interface *ifp;
+  struct isis_circuit *circuit;
+  struct isis_area *area;
+  const char *af = argv[0];
+  const char *area_tag = argv[1];
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  /* Prevent more than one area per circuit */
+  circuit = circuit_scan_by_ifp (ifp);
+  if (circuit && circuit->area)
+    {
+      if (strcmp (circuit->area->area_tag, area_tag))
+        {
+          vty_out (vty, "ISIS circuit is already defined on %s%s",
+                   circuit->area->area_tag, VTY_NEWLINE);
+          return CMD_ERR_NOTHING_TODO;
+        }
+    }
+
+  area = isis_area_lookup (area_tag);
+  if (!area)
+    area = isis_area_create (area_tag);
+
+  if (!circuit || !circuit->area) {
+    circuit = isis_circuit_create (area, ifp);
+
+    if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP)
+      {
+        vty_out(vty, "Couldn't bring up interface, please check log.%s", VTY_NEWLINE);
+        return CMD_WARNING;
+      }
+  }
+
+  bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router;
+  if (af[2] != '\0')
+    ipv6 = true;
+  else
+    ip = true;
+
+  isis_circuit_af_set (circuit, ip, ipv6);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_router_isis,
+       no_ip_router_isis_cmd,
+       "no (ip|ipv6) router isis WORD",
+       NO_STR
+       "Interface Internet Protocol config commands\n"
+       "IP router interface commands\n"
+       "IS-IS Routing for IP\n"
+       "Routing process tag\n")
+{
+  struct interface *ifp;
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+  const char *af = argv[0];
+  const char *area_tag = argv[1];
+
+  ifp = (struct interface *) vty->index;
+  if (!ifp)
+    {
+      vty_out (vty, "Invalid interface %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  area = isis_area_lookup (area_tag);
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s%s",
+               argv[0], VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  circuit = circuit_lookup_by_ifp (ifp, area->circuit_list);
+  if (!circuit)
+    {
+      vty_out (vty, "ISIS is not enabled on circuit %s%s",
+               ifp->name, VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router;
+  if (af[2] != '\0')
+    ipv6 = false;
+  else
+    ip = false;
+
+  isis_circuit_af_set (circuit, ip, ipv6);
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_passive,
+       isis_passive_cmd,
+       "isis passive",
+       "IS-IS commands\n"
+       "Configure the passive mode for interface\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  isis_circuit_passive_set (circuit, 1);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_passive,
+       no_isis_passive_cmd,
+       "no isis passive",
+       NO_STR
+       "IS-IS commands\n"
+       "Configure the passive mode for interface\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  if (if_is_loopback (circuit->interface))
+    {
+      vty_out (vty, "Can't set no passive for loopback interface%s",
+               VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  isis_circuit_passive_set (circuit, 0);
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_circuit_type,
+       isis_circuit_type_cmd,
+       "isis circuit-type (level-1|level-1-2|level-2-only)",
+       "IS-IS commands\n"
+       "Configure circuit type for interface\n"
+       "Level-1 only adjacencies are formed\n"
+       "Level-1-2 adjacencies are formed\n"
+       "Level-2 only adjacencies are formed\n")
+{
+  int is_type;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  is_type = string2circuit_t (argv[0]);
+  if (!is_type)
+    {
+      vty_out (vty, "Unknown circuit-type %s", VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  if (circuit->state == C_STATE_UP &&
+      circuit->area->is_type != IS_LEVEL_1_AND_2 &&
+      circuit->area->is_type != is_type)
+    {
+      vty_out (vty, "Invalid circuit level for area %s.%s",
+               circuit->area->area_tag, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+  isis_circuit_is_type_set (circuit, is_type);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_circuit_type,
+       no_isis_circuit_type_cmd,
+       "no isis circuit-type (level-1|level-1-2|level-2-only)",
+       NO_STR
+       "IS-IS commands\n"
+       "Configure circuit type for interface\n"
+       "Level-1 only adjacencies are formed\n"
+       "Level-1-2 adjacencies are formed\n"
+       "Level-2 only adjacencies are formed\n")
+{
+  int is_type;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  /*
+   * Set the circuits level to its default value
+   */
+  if (circuit->state == C_STATE_UP)
+    is_type = circuit->area->is_type;
+  else
+    is_type = IS_LEVEL_1_AND_2;
+  isis_circuit_is_type_set (circuit, is_type);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_network,
+       isis_network_cmd,
+       "isis network point-to-point",
+       "IS-IS commands\n"
+       "Set network type\n"
+       "point-to-point network type\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_P2P))
+    {
+      vty_out (vty, "isis network point-to-point "
+               "is valid only on broadcast interfaces%s",
+               VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_network,
+       no_isis_network_cmd,
+       "no isis network point-to-point",
+       NO_STR
+       "IS-IS commands\n"
+       "Set network type for circuit\n"
+       "point-to-point network type\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_BROADCAST))
+    {
+      vty_out (vty, "isis network point-to-point "
+               "is valid only on broadcast interfaces%s",
+               VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (isis_passwd,
+       isis_passwd_cmd,
+       "isis password (md5|clear) WORD",
+       "IS-IS commands\n"
+       "Configure the authentication password for a circuit\n"
+       "HMAC-MD5 authentication\n"
+       "Cleartext password\n"
+       "Circuit password\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  int rv;
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  if (argv[0][0] == 'm')
+    rv = isis_circuit_passwd_hmac_md5_set(circuit, argv[1]);
+  else
+    rv = isis_circuit_passwd_cleartext_set(circuit, argv[1]);
+  if (rv)
+    {
+      vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_passwd,
+       no_isis_passwd_cmd,
+       "no isis password",
+       NO_STR
+       "IS-IS commands\n"
+       "Configure the authentication password for a circuit\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  isis_circuit_passwd_unset(circuit);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_passwd,
+       no_isis_passwd_arg_cmd,
+       "no isis password (md5|clear) WORD",
+       NO_STR
+       "IS-IS commands\n"
+       "Configure the authentication password for a circuit\n"
+       "HMAC-MD5 authentication\n"
+       "Cleartext password\n"
+       "Circuit password\n")
+
+DEFUN (isis_priority,
+       isis_priority_cmd,
+       "isis priority <0-127>",
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n")
+{
+  int prio;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  prio = atoi (argv[0]);
+  if (prio < MIN_PRIORITY || prio > MAX_PRIORITY)
+    {
+      vty_out (vty, "Invalid priority %d - should be <0-127>%s",
+               prio, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->priority[0] = prio;
+  circuit->priority[1] = prio;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_priority,
+       no_isis_priority_cmd,
+       "no isis priority",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->priority[0] = DEFAULT_PRIORITY;
+  circuit->priority[1] = DEFAULT_PRIORITY;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_priority,
+       no_isis_priority_arg_cmd,
+       "no isis priority <0-127>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n")
+
+DEFUN (isis_priority_l1,
+       isis_priority_l1_cmd,
+       "isis priority <0-127> level-1",
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n"
+       "Specify priority for level-1 routing\n")
+{
+  int prio;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  prio = atoi (argv[0]);
+  if (prio < MIN_PRIORITY || prio > MAX_PRIORITY)
+    {
+      vty_out (vty, "Invalid priority %d - should be <0-127>%s",
+               prio, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->priority[0] = prio;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_priority_l1,
+       no_isis_priority_l1_cmd,
+       "no isis priority level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Specify priority for level-1 routing\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->priority[0] = DEFAULT_PRIORITY;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_priority_l1,
+       no_isis_priority_l1_arg_cmd,
+       "no isis priority <0-127> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n"
+       "Specify priority for level-1 routing\n")
+
+DEFUN (isis_priority_l2,
+       isis_priority_l2_cmd,
+       "isis priority <0-127> level-2",
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n"
+       "Specify priority for level-2 routing\n")
+{
+  int prio;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  prio = atoi (argv[0]);
+  if (prio < MIN_PRIORITY || prio > MAX_PRIORITY)
+    {
+      vty_out (vty, "Invalid priority %d - should be <0-127>%s",
+               prio, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->priority[1] = prio;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_priority_l2,
+       no_isis_priority_l2_cmd,
+       "no isis priority level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Specify priority for level-2 routing\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->priority[1] = DEFAULT_PRIORITY;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_priority_l2,
+       no_isis_priority_l2_arg_cmd,
+       "no isis priority <0-127> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set priority for Designated Router election\n"
+       "Priority value\n"
+       "Specify priority for level-2 routing\n")
+
+/* Metric command */
+DEFUN (isis_metric,
+       isis_metric_cmd,
+       "isis metric <0-16777215>",
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n")
+{
+  int met;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  met = atoi (argv[0]);
+
+  /* RFC3787 section 5.1 */
+  if (circuit->area && circuit->area->oldmetric == 1 &&
+      met > MAX_NARROW_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-63> "
+               "when narrow metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  /* RFC4444 */
+  if (circuit->area && circuit->area->newmetric == 1 &&
+      met > MAX_WIDE_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-16777215> "
+               "when wide metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_1, met);
+  isis_circuit_metric_set (circuit, IS_LEVEL_2, met);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_metric,
+       no_isis_metric_cmd,
+       "no isis metric",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC);
+  isis_circuit_metric_set (circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_metric,
+       no_isis_metric_arg_cmd,
+       "no isis metric <0-16777215>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n")
+
+DEFUN (isis_metric_l1,
+       isis_metric_l1_cmd,
+       "isis metric <0-16777215> level-1",
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n"
+       "Specify metric for level-1 routing\n")
+{
+  int met;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  met = atoi (argv[0]);
+
+  /* RFC3787 section 5.1 */
+  if (circuit->area && circuit->area->oldmetric == 1 &&
+      met > MAX_NARROW_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-63> "
+               "when narrow metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  /* RFC4444 */
+  if (circuit->area && circuit->area->newmetric == 1 &&
+      met > MAX_WIDE_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-16777215> "
+               "when wide metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_1, met);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_metric_l1,
+       no_isis_metric_l1_cmd,
+       "no isis metric level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Specify metric for level-1 routing\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_metric_l1,
+       no_isis_metric_l1_arg_cmd,
+       "no isis metric <0-16777215> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n"
+       "Specify metric for level-1 routing\n")
+
+DEFUN (isis_metric_l2,
+       isis_metric_l2_cmd,
+       "isis metric <0-16777215> level-2",
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n"
+       "Specify metric for level-2 routing\n")
+{
+  int met;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  met = atoi (argv[0]);
+
+  /* RFC3787 section 5.1 */
+  if (circuit->area && circuit->area->oldmetric == 1 &&
+      met > MAX_NARROW_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-63> "
+               "when narrow metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  /* RFC4444 */
+  if (circuit->area && circuit->area->newmetric == 1 &&
+      met > MAX_WIDE_LINK_METRIC)
+    {
+      vty_out (vty, "Invalid metric %d - should be <0-16777215> "
+               "when wide metric type enabled%s",
+               met, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_2, met);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_metric_l2,
+       no_isis_metric_l2_cmd,
+       "no isis metric level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Specify metric for level-2 routing\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  isis_circuit_metric_set (circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_metric_l2,
+       no_isis_metric_l2_arg_cmd,
+       "no isis metric <0-16777215> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set default metric for circuit\n"
+       "Default metric value\n"
+       "Specify metric for level-2 routing\n")
+/* end of metrics */
+
+DEFUN (isis_hello_interval,
+       isis_hello_interval_cmd,
+       "isis hello-interval <1-600>",
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 seconds, interval depends on multiplier\n")
+{
+  int interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atoi (argv[0]);
+  if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL)
+    {
+      vty_out (vty, "Invalid hello-interval %d - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_interval[0] = (u_int16_t) interval;
+  circuit->hello_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_interval,
+       no_isis_hello_interval_cmd,
+       "no isis hello-interval",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL;
+  circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_interval,
+       no_isis_hello_interval_arg_cmd,
+       "no isis hello-interval <1-600>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 second, interval depends on multiplier\n")
+
+DEFUN (isis_hello_interval_l1,
+       isis_hello_interval_l1_cmd,
+       "isis hello-interval <1-600> level-1",
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 second, interval depends on multiplier\n"
+       "Specify hello-interval for level-1 IIHs\n")
+{
+  long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atoi (argv[0]);
+  if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL)
+    {
+      vty_out (vty, "Invalid hello-interval %ld - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_interval[0] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_interval_l1,
+       no_isis_hello_interval_l1_cmd,
+       "no isis hello-interval level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Specify hello-interval for level-1 IIHs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_interval_l1,
+       no_isis_hello_interval_l1_arg_cmd,
+       "no isis hello-interval <1-600> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 second, interval depends on multiplier\n"
+       "Specify hello-interval for level-1 IIHs\n")
+
+DEFUN (isis_hello_interval_l2,
+       isis_hello_interval_l2_cmd,
+       "isis hello-interval <1-600> level-2",
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 second, interval depends on multiplier\n"
+       "Specify hello-interval for level-2 IIHs\n")
+{
+  long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atoi (argv[0]);
+  if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL)
+    {
+      vty_out (vty, "Invalid hello-interval %ld - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_interval_l2,
+       no_isis_hello_interval_l2_cmd,
+       "no isis hello-interval level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Specify hello-interval for level-2 IIHs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_interval_l2,
+       no_isis_hello_interval_l2_arg_cmd,
+       "no isis hello-interval <1-600> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set Hello interval\n"
+       "Hello interval value\n"
+       "Holdtime 1 second, interval depends on multiplier\n"
+       "Specify hello-interval for level-2 IIHs\n")
+
+DEFUN (isis_hello_multiplier,
+       isis_hello_multiplier_cmd,
+       "isis hello-multiplier <2-100>",
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n")
+{
+  int mult;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  mult = atoi (argv[0]);
+  if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER)
+    {
+      vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s",
+               mult, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_multiplier[0] = (u_int16_t) mult;
+  circuit->hello_multiplier[1] = (u_int16_t) mult;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_multiplier,
+       no_isis_hello_multiplier_cmd,
+       "no isis hello-multiplier",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER;
+  circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_multiplier,
+       no_isis_hello_multiplier_arg_cmd,
+       "no isis hello-multiplier <2-100>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n")
+
+DEFUN (isis_hello_multiplier_l1,
+       isis_hello_multiplier_l1_cmd,
+       "isis hello-multiplier <2-100> level-1",
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n"
+       "Specify hello multiplier for level-1 IIHs\n")
+{
+  int mult;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  mult = atoi (argv[0]);
+  if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER)
+    {
+      vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s",
+               mult, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_multiplier[0] = (u_int16_t) mult;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_multiplier_l1,
+       no_isis_hello_multiplier_l1_cmd,
+       "no isis hello-multiplier level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Specify hello multiplier for level-1 IIHs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_multiplier_l1,
+       no_isis_hello_multiplier_l1_arg_cmd,
+       "no isis hello-multiplier <2-100> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n"
+       "Specify hello multiplier for level-1 IIHs\n")
+
+DEFUN (isis_hello_multiplier_l2,
+       isis_hello_multiplier_l2_cmd,
+       "isis hello-multiplier <2-100> level-2",
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n"
+       "Specify hello multiplier for level-2 IIHs\n")
+{
+  int mult;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  mult = atoi (argv[0]);
+  if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER)
+    {
+      vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s",
+               mult, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->hello_multiplier[1] = (u_int16_t) mult;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_multiplier_l2,
+       no_isis_hello_multiplier_l2_cmd,
+       "no isis hello-multiplier level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Specify hello multiplier for level-2 IIHs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_isis_hello_multiplier_l2,
+       no_isis_hello_multiplier_l2_arg_cmd,
+       "no isis hello-multiplier <2-100> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set multiplier for Hello holding time\n"
+       "Hello multiplier value\n"
+       "Specify hello multiplier for level-2 IIHs\n")
+
+DEFUN (isis_hello_padding,
+       isis_hello_padding_cmd,
+       "isis hello padding",
+       "IS-IS commands\n"
+       "Add padding to IS-IS hello packets\n"
+       "Pad hello packets\n"
+       "<cr>\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->pad_hellos = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_isis_hello_padding,
+       no_isis_hello_padding_cmd,
+       "no isis hello padding",
+       NO_STR
+       "IS-IS commands\n"
+       "Add padding to IS-IS hello packets\n"
+       "Pad hello packets\n"
+       "<cr>\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->pad_hellos = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (csnp_interval,
+       csnp_interval_cmd,
+       "isis csnp-interval <1-600>",
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->csnp_interval[0] = (u_int16_t) interval;
+  circuit->csnp_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_csnp_interval,
+       no_csnp_interval_cmd,
+       "no isis csnp-interval",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL;
+  circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_csnp_interval,
+       no_csnp_interval_arg_cmd,
+       "no isis csnp-interval <1-600>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n")
+
+DEFUN (csnp_interval_l1,
+       csnp_interval_l1_cmd,
+       "isis csnp-interval <1-600> level-1",
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n"
+       "Specify interval for level-1 CSNPs\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->csnp_interval[0] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_csnp_interval_l1,
+       no_csnp_interval_l1_cmd,
+       "no isis csnp-interval level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "Specify interval for level-1 CSNPs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_csnp_interval_l1,
+       no_csnp_interval_l1_arg_cmd,
+       "no isis csnp-interval <1-600> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n"
+       "Specify interval for level-1 CSNPs\n")
+
+DEFUN (csnp_interval_l2,
+       csnp_interval_l2_cmd,
+       "isis csnp-interval <1-600> level-2",
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n"
+       "Specify interval for level-2 CSNPs\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->csnp_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_csnp_interval_l2,
+       no_csnp_interval_l2_cmd,
+       "no isis csnp-interval level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "Specify interval for level-2 CSNPs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_csnp_interval_l2,
+       no_csnp_interval_l2_arg_cmd,
+       "no isis csnp-interval <1-600> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set CSNP interval in seconds\n"
+       "CSNP interval value\n"
+       "Specify interval for level-2 CSNPs\n")
+
+DEFUN (psnp_interval,
+       psnp_interval_cmd,
+       "isis psnp-interval <1-120>",
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->psnp_interval[0] = (u_int16_t) interval;
+  circuit->psnp_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_psnp_interval,
+       no_psnp_interval_cmd,
+       "no isis psnp-interval",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL;
+  circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_psnp_interval,
+       no_psnp_interval_arg_cmd,
+       "no isis psnp-interval <1-120>",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n")
+
+DEFUN (psnp_interval_l1,
+       psnp_interval_l1_cmd,
+       "isis psnp-interval <1-120> level-1",
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n"
+       "Specify interval for level-1 PSNPs\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->psnp_interval[0] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_psnp_interval_l1,
+       no_psnp_interval_l1_cmd,
+       "no isis psnp-interval level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "Specify interval for level-1 PSNPs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_psnp_interval_l1,
+       no_psnp_interval_l1_arg_cmd,
+       "no isis psnp-interval <1-120> level-1",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n"
+       "Specify interval for level-1 PSNPs\n")
+
+DEFUN (psnp_interval_l2,
+       psnp_interval_l2_cmd,
+       "isis psnp-interval <1-120> level-2",
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n"
+       "Specify interval for level-2 PSNPs\n")
+{
+  unsigned long interval;
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  interval = atol (argv[0]);
+  if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL)
+    {
+      vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s",
+               interval, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  circuit->psnp_interval[1] = (u_int16_t) interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_psnp_interval_l2,
+       no_psnp_interval_l2_cmd,
+       "no isis psnp-interval level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "Specify interval for level-2 PSNPs\n")
+{
+  struct isis_circuit *circuit = isis_circuit_lookup (vty);
+  if (!circuit)
+    return CMD_ERR_NO_MATCH;
+
+  circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_psnp_interval_l2,
+       no_psnp_interval_l2_arg_cmd,
+       "no isis psnp-interval <1-120> level-2",
+       NO_STR
+       "IS-IS commands\n"
+       "Set PSNP interval in seconds\n"
+       "PSNP interval value\n"
+       "Specify interval for level-2 PSNPs\n")
+
+static int
+validate_metric_style_narrow (struct vty *vty, struct isis_area *area)
+{
+  struct isis_circuit *circuit;
+  struct listnode *node;
+
+  if (! vty)
+    return CMD_ERR_AMBIGUOUS;
+
+  if (! area)
+    {
+      vty_out (vty, "ISIS area is invalid%s", VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit))
+    {
+      if ((area->is_type & IS_LEVEL_1) &&
+          (circuit->is_type & IS_LEVEL_1) &&
+          (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC))
+        {
+          vty_out (vty, "ISIS circuit %s metric is invalid%s",
+                   circuit->interface->name, VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+      if ((area->is_type & IS_LEVEL_2) &&
+          (circuit->is_type & IS_LEVEL_2) &&
+          (circuit->te_metric[1] > MAX_NARROW_LINK_METRIC))
+        {
+          vty_out (vty, "ISIS circuit %s metric is invalid%s",
+                   circuit->interface->name, VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (metric_style,
+       metric_style_cmd,
+       "metric-style (narrow|transition|wide)",
+       "Use old-style (ISO 10589) or new-style packet formats\n"
+       "Use old style of TLVs with narrow metric\n"
+       "Send and accept both styles of TLVs during transition\n"
+       "Use new style of TLVs to carry wider metric\n")
+{
+  struct isis_area *area = vty->index;
+  int ret;
+
+  assert(area);
+
+  if (strncmp (argv[0], "w", 1) == 0)
+    {
+      isis_area_metricstyle_set(area, false, true);
+      return CMD_SUCCESS;
+    }
+
+  ret = validate_metric_style_narrow (vty, area);
+  if (ret != CMD_SUCCESS)
+    return ret;
+
+  if (strncmp (argv[0], "t", 1) == 0)
+    isis_area_metricstyle_set(area, true, true);
+  else if (strncmp (argv[0], "n", 1) == 0)
+    isis_area_metricstyle_set(area, true, false);
+      return CMD_SUCCESS;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_metric_style,
+       no_metric_style_cmd,
+       "no metric-style",
+       NO_STR
+       "Use old-style (ISO 10589) or new-style packet formats\n")
+{
+  struct isis_area *area = vty->index;
+  int ret;
+
+  assert (area);
+  ret = validate_metric_style_narrow (vty, area);
+  if (ret != CMD_SUCCESS)
+    return ret;
+
+  isis_area_metricstyle_set(area, true, false);
+  return CMD_SUCCESS;
+}
+
+DEFUN (set_overload_bit,
+       set_overload_bit_cmd,
+       "set-overload-bit",
+       "Set overload bit to avoid any transit traffic\n"
+       "Set overload bit\n")
+{
+  struct isis_area *area = vty->index;
+  assert (area);
+
+  isis_area_overload_bit_set(area, true);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_set_overload_bit,
+       no_set_overload_bit_cmd,
+       "no set-overload-bit",
+       "Reset overload bit to accept transit traffic\n"
+       "Reset overload bit\n")
+{
+  struct isis_area *area = vty->index;
+  assert (area);
+
+  isis_area_overload_bit_set(area, false);
+  return CMD_SUCCESS;
+}
+
+DEFUN (set_attached_bit,
+       set_attached_bit_cmd,
+       "set-attached-bit",
+       "Set attached bit to identify as L1/L2 router for inter-area traffic\n"
+       "Set attached bit\n")
+{
+  struct isis_area *area = vty->index;
+  assert (area);
+
+  isis_area_attached_bit_set(area, true);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_set_attached_bit,
+       no_set_attached_bit_cmd,
+       "no set-attached-bit",
+       "Reset attached bit\n")
+{
+  struct isis_area *area = vty->index;
+  assert (area);
+
+  isis_area_attached_bit_set(area, false);
+  return CMD_SUCCESS;
+}
+
+DEFUN (dynamic_hostname,
+       dynamic_hostname_cmd,
+       "hostname dynamic",
+       "Dynamic hostname for IS-IS\n"
+       "Dynamic hostname\n")
+{
+  struct isis_area *area = vty->index;
+  assert(area);
+
+  isis_area_dynhostname_set(area, true);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_dynamic_hostname,
+       no_dynamic_hostname_cmd,
+       "no hostname dynamic",
+       NO_STR
+       "Dynamic hostname for IS-IS\n"
+       "Dynamic hostname\n")
+{
+  struct isis_area *area = vty->index;
+  assert(area);
+
+  isis_area_dynhostname_set(area, false);
+  return CMD_SUCCESS;
+}
+
+static int area_lsp_mtu_set(struct vty *vty, unsigned int lsp_mtu)
+{
+  struct isis_area *area = vty->index;
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+    {
+      if(circuit->state != C_STATE_INIT && circuit->state != C_STATE_UP)
+        continue;
+      if(lsp_mtu > isis_circuit_pdu_size(circuit))
+        {
+          vty_out(vty, "ISIS area contains circuit %s, which has a maximum PDU size of %zu.%s",
+                  circuit->interface->name, isis_circuit_pdu_size(circuit),
+                  VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+    }
+
+  isis_area_lsp_mtu_set(area, lsp_mtu);
+  return CMD_SUCCESS;
+}
+
+DEFUN (area_lsp_mtu,
+       area_lsp_mtu_cmd,
+       "lsp-mtu <128-4352>",
+       "Configure the maximum size of generated LSPs\n"
+       "Maximum size of generated LSPs\n")
+{
+  unsigned int lsp_mtu;
+
+  VTY_GET_INTEGER_RANGE("lsp-mtu", lsp_mtu, argv[0], 128, 4352);
+
+  return area_lsp_mtu_set(vty, lsp_mtu);
+}
+
+DEFUN(no_area_lsp_mtu,
+      no_area_lsp_mtu_cmd,
+      "no lsp-mtu",
+      NO_STR
+      "Configure the maximum size of generated LSPs\n")
+{
+  return area_lsp_mtu_set(vty, DEFAULT_LSP_MTU);
+}
+
+ALIAS(no_area_lsp_mtu,
+      no_area_lsp_mtu_arg_cmd,
+      "no lsp-mtu <128-4352>",
+      NO_STR
+      "Configure the maximum size of generated LSPs\n"
+      "Maximum size of generated LSPs\n");
+
+DEFUN (is_type,
+       is_type_cmd,
+       "is-type (level-1|level-1-2|level-2-only)",
+       "IS Level for this routing process (OSI only)\n"
+       "Act as a station router only\n"
+       "Act as both a station router and an area router\n"
+       "Act as an area router only\n")
+{
+  struct isis_area *area;
+  int type;
+
+  area = vty->index;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  type = string2circuit_t (argv[0]);
+  if (!type)
+    {
+      vty_out (vty, "Unknown IS level %s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  isis_area_is_type_set(area, type);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_is_type,
+       no_is_type_cmd,
+       "no is-type (level-1|level-1-2|level-2-only)",
+       NO_STR
+       "IS Level for this routing process (OSI only)\n"
+       "Act as a station router only\n"
+       "Act as both a station router and an area router\n"
+       "Act as an area router only\n")
+{
+  struct isis_area *area;
+  int type;
+
+  area = vty->index;
+  assert (area);
+
+  /*
+   * Put the is-type back to defaults:
+   * - level-1-2 on first area
+   * - level-1 for the rest
+   */
+  if (listgetdata (listhead (isis->area_list)) == area)
+    type = IS_LEVEL_1_AND_2;
+  else
+    type = IS_LEVEL_1;
+
+  isis_area_is_type_set(area, type);
+
+  return CMD_SUCCESS;
+}
+
+static int
+set_lsp_gen_interval (struct vty *vty, struct isis_area *area,
+                      uint16_t interval, int level)
+{
+  int lvl;
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl)
+    {
+      if (!(lvl & level))
+        continue;
+
+      if (interval >= area->lsp_refresh[lvl-1])
+        {
+          vty_out (vty, "LSP gen interval %us must be less than "
+                   "the LSP refresh interval %us%s",
+                   interval, area->lsp_refresh[lvl-1], VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+    }
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl)
+    {
+      if (!(lvl & level))
+        continue;
+      area->lsp_gen_interval[lvl-1] = interval;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (lsp_gen_interval,
+       lsp_gen_interval_cmd,
+       "lsp-gen-interval <1-120>",
+       "Minimum interval between regenerating same LSP\n"
+       "Minimum interval in seconds\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  level = IS_LEVEL_1 | IS_LEVEL_2;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+DEFUN (no_lsp_gen_interval,
+       no_lsp_gen_interval_cmd,
+       "no lsp-gen-interval",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = DEFAULT_MIN_LSP_GEN_INTERVAL;
+  level = IS_LEVEL_1 | IS_LEVEL_2;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+ALIAS (no_lsp_gen_interval,
+       no_lsp_gen_interval_arg_cmd,
+       "no lsp-gen-interval <1-120>",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n"
+       "Minimum interval in seconds\n")
+
+DEFUN (lsp_gen_interval_l1,
+       lsp_gen_interval_l1_cmd,
+       "lsp-gen-interval level-1 <1-120>",
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 1 only\n"
+       "Minimum interval in seconds\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  level = IS_LEVEL_1;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+DEFUN (no_lsp_gen_interval_l1,
+       no_lsp_gen_interval_l1_cmd,
+       "no lsp-gen-interval level-1",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 1 only\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = DEFAULT_MIN_LSP_GEN_INTERVAL;
+  level = IS_LEVEL_1;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+ALIAS (no_lsp_gen_interval_l1,
+       no_lsp_gen_interval_l1_arg_cmd,
+       "no lsp-gen-interval level-1 <1-120>",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 1 only\n"
+       "Minimum interval in seconds\n")
+
+DEFUN (lsp_gen_interval_l2,
+       lsp_gen_interval_l2_cmd,
+       "lsp-gen-interval level-2 <1-120>",
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 2 only\n"
+       "Minimum interval in seconds\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  level = IS_LEVEL_2;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+DEFUN (no_lsp_gen_interval_l2,
+       no_lsp_gen_interval_l2_cmd,
+       "no lsp-gen-interval level-2",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 2 only\n")
+{
+  struct isis_area *area;
+  uint16_t interval;
+  int level;
+
+  area = vty->index;
+  interval = DEFAULT_MIN_LSP_GEN_INTERVAL;
+  level = IS_LEVEL_2;
+  return set_lsp_gen_interval (vty, area, interval, level);
+}
+
+ALIAS (no_lsp_gen_interval_l2,
+       no_lsp_gen_interval_l2_arg_cmd,
+       "no lsp-gen-interval level-2 <1-120>",
+       NO_STR
+       "Minimum interval between regenerating same LSP\n"
+       "Set interval for level 2 only\n"
+       "Minimum interval in seconds\n")
+
+DEFUN (spf_interval,
+       spf_interval_cmd,
+       "spf-interval <1-120>",
+       "Minimum interval between SPF calculations\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+{
+  struct isis_area *area;
+  u_int16_t interval;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  area->min_spf_interval[0] = interval;
+  area->min_spf_interval[1] = interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_spf_interval,
+       no_spf_interval_cmd,
+       "no spf-interval",
+       NO_STR
+       "Minimum interval between SPF calculations\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+
+  area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL;
+  area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_spf_interval,
+       no_spf_interval_arg_cmd,
+       "no spf-interval <1-120>",
+       NO_STR
+       "Minimum interval between SPF calculations\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+
+DEFUN (spf_interval_l1,
+       spf_interval_l1_cmd,
+       "spf-interval level-1 <1-120>",
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 1 only\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+{
+  struct isis_area *area;
+  u_int16_t interval;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  area->min_spf_interval[0] = interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_spf_interval_l1,
+       no_spf_interval_l1_cmd,
+       "no spf-interval level-1",
+       NO_STR
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 1 only\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+
+  area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_spf_interval,
+       no_spf_interval_l1_arg_cmd,
+       "no spf-interval level-1 <1-120>",
+       NO_STR
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 1 only\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+
+DEFUN (spf_interval_l2,
+       spf_interval_l2_cmd,
+       "spf-interval level-2 <1-120>",
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 2 only\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+{
+  struct isis_area *area;
+  u_int16_t interval;
+
+  area = vty->index;
+  interval = atoi (argv[0]);
+  area->min_spf_interval[1] = interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_spf_interval_l2,
+       no_spf_interval_l2_cmd,
+       "no spf-interval level-2",
+       NO_STR
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 2 only\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+
+  area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_spf_interval,
+       no_spf_interval_l2_arg_cmd,
+       "no spf-interval level-2 <1-120>",
+       NO_STR
+       "Minimum interval between SPF calculations\n"
+       "Set interval for level 2 only\n"
+       "Minimum interval between consecutive SPFs in seconds\n")
+
+static int
+area_max_lsp_lifetime_set(struct vty *vty, int level,
+                         uint16_t interval)
+{
+  struct isis_area *area = vty->index;
+  int lvl;
+  uint16_t refresh_interval = interval - 300;
+  int set_refresh_interval[ISIS_LEVELS] = {0, 0};
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++)
+    {
+      if (!(lvl & level))
+        continue;
+
+      if (refresh_interval < area->lsp_refresh[lvl-1])
+        {
+          vty_out (vty, "Level %d Max LSP lifetime %us must be 300s greater than "
+                   "the configured LSP refresh interval %us%s",
+                   lvl, interval, area->lsp_refresh[lvl-1], VTY_NEWLINE);
+          vty_out (vty, "Automatically reducing level %d LSP refresh interval "
+                   "to %us%s", lvl, refresh_interval, VTY_NEWLINE);
+          set_refresh_interval[lvl-1] = 1;
+
+          if (refresh_interval <= area->lsp_gen_interval[lvl-1])
+            {
+              vty_out (vty, "LSP refresh interval %us must be greater than "
+                       "the configured LSP gen interval %us%s",
+                       refresh_interval, area->lsp_gen_interval[lvl-1],
+                       VTY_NEWLINE);
+              return CMD_ERR_AMBIGUOUS;
+            }
+        }
+    }
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++)
+    {
+      if (!(lvl & level))
+        continue;
+      isis_area_max_lsp_lifetime_set(area, lvl, interval);
+      if (set_refresh_interval[lvl-1])
+        isis_area_lsp_refresh_set(area, lvl, refresh_interval);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (max_lsp_lifetime,
+       max_lsp_lifetime_cmd,
+       "max-lsp-lifetime <350-65535>",
+       "Maximum LSP lifetime\n"
+       "LSP lifetime in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2, atoi(argv[0]));
+}
+
+DEFUN (no_max_lsp_lifetime,
+       no_max_lsp_lifetime_cmd,
+       "no max-lsp-lifetime",
+       NO_STR
+       "LSP lifetime in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2,
+                                  DEFAULT_LSP_LIFETIME);
+}
+
+ALIAS (no_max_lsp_lifetime,
+       no_max_lsp_lifetime_arg_cmd,
+       "no max-lsp-lifetime <350-65535>",
+       NO_STR
+       "Maximum LSP lifetime\n"
+       "LSP lifetime in seconds\n")
+
+DEFUN (max_lsp_lifetime_l1,
+       max_lsp_lifetime_l1_cmd,
+       "max-lsp-lifetime level-1 <350-65535>",
+       "Maximum LSP lifetime for Level 1 only\n"
+       "LSP lifetime for Level 1 only in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_1, atoi(argv[0]));
+}
+
+DEFUN (no_max_lsp_lifetime_l1,
+       no_max_lsp_lifetime_l1_cmd,
+       "no max-lsp-lifetime level-1",
+       NO_STR
+       "LSP lifetime for Level 1 only in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_1, DEFAULT_LSP_LIFETIME);
+}
+
+ALIAS (no_max_lsp_lifetime_l1,
+       no_max_lsp_lifetime_l1_arg_cmd,
+       "no max-lsp-lifetime level-1 <350-65535>",
+       NO_STR
+       "Maximum LSP lifetime for Level 1 only\n"
+       "LSP lifetime for Level 1 only in seconds\n")
+
+DEFUN (max_lsp_lifetime_l2,
+       max_lsp_lifetime_l2_cmd,
+       "max-lsp-lifetime level-2 <350-65535>",
+       "Maximum LSP lifetime for Level 2 only\n"
+       "LSP lifetime for Level 2 only in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_2, atoi(argv[0]));
+}
+
+DEFUN (no_max_lsp_lifetime_l2,
+       no_max_lsp_lifetime_l2_cmd,
+       "no max-lsp-lifetime level-2",
+       NO_STR
+       "LSP lifetime for Level 2 only in seconds\n")
+{
+  return area_max_lsp_lifetime_set(vty, IS_LEVEL_2, DEFAULT_LSP_LIFETIME);
+}
+
+ALIAS (no_max_lsp_lifetime_l2,
+       no_max_lsp_lifetime_l2_arg_cmd,
+       "no max-lsp-lifetime level-2 <350-65535>",
+       NO_STR
+       "Maximum LSP lifetime for Level 2 only\n"
+       "LSP lifetime for Level 2 only in seconds\n")
+
+static int
+area_lsp_refresh_interval_set(struct vty *vty, int level, uint16_t interval)
+{
+  struct isis_area *area = vty->index;
+  int lvl;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl)
+    {
+      if (!(lvl & level))
+        continue;
+      if (interval <= area->lsp_gen_interval[lvl-1])
+        {
+          vty_out (vty, "LSP refresh interval %us must be greater than "
+                   "the configured LSP gen interval %us%s",
+                   interval, area->lsp_gen_interval[lvl-1],
+                   VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+      if (interval > (area->max_lsp_lifetime[lvl-1] - 300))
+        {
+          vty_out (vty, "LSP refresh interval %us must be less than "
+                   "the configured LSP lifetime %us less 300%s",
+                   interval, area->max_lsp_lifetime[lvl-1],
+                   VTY_NEWLINE);
+          return CMD_ERR_AMBIGUOUS;
+        }
+    }
+
+  for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl)
+    {
+      if (!(lvl & level))
+        continue;
+      isis_area_lsp_refresh_set(area, lvl, interval);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (lsp_refresh_interval,
+       lsp_refresh_interval_cmd,
+       "lsp-refresh-interval <1-65235>",
+       "LSP refresh interval\n"
+       "LSP refresh interval in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_1_AND_2, atoi(argv[0]));
+}
+
+DEFUN (no_lsp_refresh_interval,
+       no_lsp_refresh_interval_cmd,
+       "no lsp-refresh-interval",
+       NO_STR
+       "LSP refresh interval in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_1_AND_2,
+                                      DEFAULT_MAX_LSP_GEN_INTERVAL);
+}
+
+ALIAS (no_lsp_refresh_interval,
+       no_lsp_refresh_interval_arg_cmd,
+       "no lsp-refresh-interval <1-65235>",
+       NO_STR
+       "LSP refresh interval\n"
+       "LSP refresh interval in seconds\n")
+
+DEFUN (lsp_refresh_interval_l1,
+       lsp_refresh_interval_l1_cmd,
+       "lsp-refresh-interval level-1 <1-65235>",
+       "LSP refresh interval for Level 1 only\n"
+       "LSP refresh interval for Level 1 only in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_1, atoi(argv[0]));
+}
+
+DEFUN (no_lsp_refresh_interval_l1,
+       no_lsp_refresh_interval_l1_cmd,
+       "no lsp-refresh-interval level-1",
+       NO_STR
+       "LSP refresh interval for Level 1 only in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_1,
+                                      DEFAULT_MAX_LSP_GEN_INTERVAL);
+}
+
+ALIAS (no_lsp_refresh_interval_l1,
+       no_lsp_refresh_interval_l1_arg_cmd,
+       "no lsp-refresh-interval level-1 <1-65235>",
+       NO_STR
+       "LSP refresh interval for Level 1 only\n"
+       "LSP refresh interval for Level 1 only in seconds\n")
+
+DEFUN (lsp_refresh_interval_l2,
+       lsp_refresh_interval_l2_cmd,
+       "lsp-refresh-interval level-2 <1-65235>",
+       "LSP refresh interval for Level 2 only\n"
+       "LSP refresh interval for Level 2 only in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_2, atoi(argv[0]));
+}
+
+DEFUN (no_lsp_refresh_interval_l2,
+       no_lsp_refresh_interval_l2_cmd,
+       "no lsp-refresh-interval level-2",
+       NO_STR
+       "LSP refresh interval for Level 2 only in seconds\n")
+{
+  return area_lsp_refresh_interval_set(vty, IS_LEVEL_2,
+                                      DEFAULT_MAX_LSP_GEN_INTERVAL);
+}
+
+ALIAS (no_lsp_refresh_interval_l2,
+       no_lsp_refresh_interval_l2_arg_cmd,
+       "no lsp-refresh-interval level-2 <1-65235>",
+       NO_STR
+       "LSP refresh interval for Level 2 only\n"
+       "LSP refresh interval for Level 2 only in seconds\n")
+
+static int
+area_passwd_set(struct vty *vty, int level,
+                int (*type_set)(struct isis_area *area, int level,
+                                const char *passwd, u_char snp_auth),
+                const char *passwd, u_char snp_auth)
+{
+  struct isis_area *area = vty->index;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  if (passwd && strlen(passwd) > 254)
+    {
+      vty_out (vty, "Too long area password (>254)%s", VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  type_set(area, level, passwd, snp_auth);
+  return CMD_SUCCESS;
+}
+
+DEFUN (area_passwd_md5,
+       area_passwd_md5_cmd,
+       "(area-password|domain-password) md5 WORD",
+       "Configure the authentication password for an area\n"
+       "Set the authentication password for a routing domain\n"
+       "Authentication type\n"
+       "Level-wide password\n")
+{
+  u_char snp_auth = 0;
+  int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1;
+
+  if (argc > 2)
+    {
+      snp_auth = SNP_AUTH_SEND;
+      if (strncmp(argv[2], "v", 1) == 0)
+        snp_auth |= SNP_AUTH_RECV;
+    }
+
+  return area_passwd_set(vty, level, isis_area_passwd_hmac_md5_set,
+                         argv[1], snp_auth);
+}
+
+ALIAS (area_passwd_md5,
+       area_passwd_md5_snpauth_cmd,
+       "(area-password|domain-password) md5 WORD authenticate snp (send-only|validate)",
+       "Configure the authentication password for an area\n"
+       "Set the authentication password for a routing domain\n"
+       "Authentication type\n"
+       "Level-wide password\n"
+       "Authentication\n"
+       "SNP PDUs\n"
+       "Send but do not check PDUs on receiving\n"
+       "Send and check PDUs on receiving\n")
+
+DEFUN (area_passwd_clear,
+       area_passwd_clear_cmd,
+       "(area-password|domain-password) clear WORD",
+       "Configure the authentication password for an area\n"
+       "Set the authentication password for a routing domain\n"
+       "Authentication type\n"
+       "Area password\n")
+{
+  u_char snp_auth = 0;
+  int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1;
+
+  if (argc > 2)
+    {
+      snp_auth = SNP_AUTH_SEND;
+      if (strncmp(argv[2], "v", 1) == 0)
+        snp_auth |= SNP_AUTH_RECV;
+    }
+
+  return area_passwd_set(vty, level, isis_area_passwd_cleartext_set,
+                         argv[1], snp_auth);
+}
+
+ALIAS (area_passwd_clear,
+       area_passwd_clear_snpauth_cmd,
+       "(area-password|domain-password) clear WORD authenticate snp (send-only|validate)",
+       "Configure the authentication password for an area\n"
+       "Set the authentication password for a routing domain\n"
+       "Authentication type\n"
+       "Area password\n"
+       "Authentication\n"
+       "SNP PDUs\n"
+       "Send but do not check PDUs on receiving\n"
+       "Send and check PDUs on receiving\n")
+
+DEFUN (no_area_passwd,
+       no_area_passwd_cmd,
+       "no (area-password|domain-password)",
+       NO_STR
+       "Configure the authentication password for an area\n"
+       "Set the authentication password for a routing domain\n")
+{
+  int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1;
+  struct isis_area *area = vty->index;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  return isis_area_passwd_unset (area, level);
+}
+
+void
+isis_vty_init (void)
+{
+  install_element (INTERFACE_NODE, &ip_router_isis_cmd);
+  install_element (INTERFACE_NODE, &no_ip_router_isis_cmd);
+
+  install_element (INTERFACE_NODE, &isis_passive_cmd);
+  install_element (INTERFACE_NODE, &no_isis_passive_cmd);
+
+  install_element (INTERFACE_NODE, &isis_circuit_type_cmd);
+  install_element (INTERFACE_NODE, &no_isis_circuit_type_cmd);
+
+  install_element (INTERFACE_NODE, &isis_network_cmd);
+  install_element (INTERFACE_NODE, &no_isis_network_cmd);
+
+  install_element (INTERFACE_NODE, &isis_passwd_cmd);
+  install_element (INTERFACE_NODE, &no_isis_passwd_cmd);
+  install_element (INTERFACE_NODE, &no_isis_passwd_arg_cmd);
+
+  install_element (INTERFACE_NODE, &isis_priority_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_priority_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_priority_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_priority_l2_arg_cmd);
+
+  install_element (INTERFACE_NODE, &isis_metric_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_metric_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_metric_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_metric_l2_arg_cmd);
+
+  install_element (INTERFACE_NODE, &isis_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_hello_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_hello_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_arg_cmd);
+
+  install_element (INTERFACE_NODE, &isis_hello_multiplier_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_hello_multiplier_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &isis_hello_multiplier_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_arg_cmd);
+
+  install_element (INTERFACE_NODE, &isis_hello_padding_cmd);
+  install_element (INTERFACE_NODE, &no_isis_hello_padding_cmd);
+
+  install_element (INTERFACE_NODE, &csnp_interval_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_arg_cmd);
+  install_element (INTERFACE_NODE, &csnp_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &csnp_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_csnp_interval_l2_arg_cmd);
+
+  install_element (INTERFACE_NODE, &psnp_interval_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_arg_cmd);
+  install_element (INTERFACE_NODE, &psnp_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_l1_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_l1_arg_cmd);
+  install_element (INTERFACE_NODE, &psnp_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_l2_cmd);
+  install_element (INTERFACE_NODE, &no_psnp_interval_l2_arg_cmd);
+
+  install_element (ISIS_NODE, &metric_style_cmd);
+  install_element (ISIS_NODE, &no_metric_style_cmd);
+
+  install_element (ISIS_NODE, &set_overload_bit_cmd);
+  install_element (ISIS_NODE, &no_set_overload_bit_cmd);
+
+  install_element (ISIS_NODE, &set_attached_bit_cmd);
+  install_element (ISIS_NODE, &no_set_attached_bit_cmd);
+
+  install_element (ISIS_NODE, &dynamic_hostname_cmd);
+  install_element (ISIS_NODE, &no_dynamic_hostname_cmd);
+
+  install_element (ISIS_NODE, &area_lsp_mtu_cmd);
+  install_element (ISIS_NODE, &no_area_lsp_mtu_cmd);
+  install_element (ISIS_NODE, &no_area_lsp_mtu_arg_cmd);
+
+  install_element (ISIS_NODE, &is_type_cmd);
+  install_element (ISIS_NODE, &no_is_type_cmd);
+
+  install_element (ISIS_NODE, &lsp_gen_interval_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_arg_cmd);
+  install_element (ISIS_NODE, &lsp_gen_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_l1_arg_cmd);
+  install_element (ISIS_NODE, &lsp_gen_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_lsp_gen_interval_l2_arg_cmd);
+
+  install_element (ISIS_NODE, &spf_interval_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_arg_cmd);
+  install_element (ISIS_NODE, &spf_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_l1_arg_cmd);
+  install_element (ISIS_NODE, &spf_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_spf_interval_l2_arg_cmd);
+
+  install_element (ISIS_NODE, &max_lsp_lifetime_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_arg_cmd);
+  install_element (ISIS_NODE, &max_lsp_lifetime_l1_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_l1_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_l1_arg_cmd);
+  install_element (ISIS_NODE, &max_lsp_lifetime_l2_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_l2_cmd);
+  install_element (ISIS_NODE, &no_max_lsp_lifetime_l2_arg_cmd);
+
+  install_element (ISIS_NODE, &lsp_refresh_interval_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_arg_cmd);
+  install_element (ISIS_NODE, &lsp_refresh_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_l1_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_l1_arg_cmd);
+  install_element (ISIS_NODE, &lsp_refresh_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_l2_cmd);
+  install_element (ISIS_NODE, &no_lsp_refresh_interval_l2_arg_cmd);
+
+  install_element (ISIS_NODE, &area_passwd_md5_cmd);
+  install_element (ISIS_NODE, &area_passwd_md5_snpauth_cmd);
+  install_element (ISIS_NODE, &area_passwd_clear_cmd);
+  install_element (ISIS_NODE, &area_passwd_clear_snpauth_cmd);
+  install_element (ISIS_NODE, &no_area_passwd_cmd);
+}
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
new file mode 100644 (file)
index 0000000..40157b5
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_zebra.c   
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ * Copyright (C) 2013-2015   Christian Franke <chris@opensourcerouting.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+#include "if.h"
+#include "network.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "linklist.h"
+#include "vrf.h"
+
+#include "isisd/dict.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_zebra.h"
+#include "isisd/isis_te.h"
+
+struct zclient *zclient = NULL;
+
+/* Router-id update message from zebra. */
+static int
+isis_router_id_update_zebra (int command, struct zclient *zclient,
+                            zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct isis_area *area;
+  struct listnode *node;
+  struct prefix router_id;
+
+  /*
+   * If ISIS TE is enable, TE Router ID is set through specific command.
+   * See mpls_te_router_addr() command in isis_te.c
+   */
+  if (IS_MPLS_TE(isisMplsTE))
+    return 0;
+
+  zebra_router_id_update_read (zclient->ibuf, &router_id);
+  if (isis->router_id == router_id.u.prefix4.s_addr)
+    return 0;
+
+  isis->router_id = router_id.u.prefix4.s_addr;
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    if (listcount (area->area_addrs) > 0)
+      lsp_regenerate_schedule (area, area->is_type, 0);
+
+  return 0;
+}
+
+static int
+isis_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+
+  if (isis->debugs & DEBUG_ZEBRA)
+    zlog_debug ("Zebra I/F add: %s index %d flags %ld metric %d mtu %d",
+               ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, ifp->mtu);
+
+  if (if_is_operative (ifp))
+    isis_csm_state_change (IF_UP_FROM_Z, circuit_scan_by_ifp (ifp), ifp);
+
+  return 0;
+}
+
+static int
+isis_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct stream *s;
+
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (!ifp)
+    return 0;
+
+  if (if_is_operative (ifp))
+    zlog_warn ("Zebra: got delete of %s, but interface is still up",
+              ifp->name);
+
+  if (isis->debugs & DEBUG_ZEBRA)
+    zlog_debug ("Zebra I/F delete: %s index %d flags %ld metric %d mtu %d",
+               ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, ifp->mtu);
+
+  isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp), ifp);
+
+  /* Cannot call if_delete because we should retain the pseudo interface
+     in case there is configuration info attached to it. */
+  if_delete_retain(ifp);
+
+  ifp->ifindex = IFINDEX_INTERNAL;
+
+  return 0;
+}
+
+static int
+isis_zebra_if_state_up (int command, struct zclient *zclient,
+                       zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_state_read (zclient->ibuf, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  isis_csm_state_change (IF_UP_FROM_Z, circuit_scan_by_ifp (ifp), ifp);
+
+  return 0;
+}
+
+static int
+isis_zebra_if_state_down (int command, struct zclient *zclient,
+                         zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct isis_circuit *circuit;
+
+  ifp = zebra_interface_state_read (zclient->ibuf, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  circuit = isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp),
+                                   ifp);
+  if (circuit)
+    SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
+
+  return 0;
+}
+
+static int
+isis_zebra_if_address_add (int command, struct zclient *zclient,
+                          zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct prefix *p;
+  char buf[BUFSIZ];
+
+  c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD,
+                                   zclient->ibuf, vrf_id);
+
+  if (c == NULL)
+    return 0;
+
+  p = c->address;
+
+  prefix2str (p, buf, BUFSIZ);
+#ifdef EXTREME_DEBUG
+  if (p->family == AF_INET)
+    zlog_debug ("connected IP address %s", buf);
+#ifdef HAVE_IPV6
+  if (p->family == AF_INET6)
+    zlog_debug ("connected IPv6 address %s", buf);
+#endif /* HAVE_IPV6 */
+#endif /* EXTREME_DEBUG */
+  if (if_is_operative (c->ifp))
+    isis_circuit_add_addr (circuit_scan_by_ifp (c->ifp), c);
+
+  return 0;
+}
+
+static int
+isis_zebra_if_address_del (int command, struct zclient *client,
+                          zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct interface *ifp;
+#ifdef EXTREME_DEBUG
+  struct prefix *p;
+  u_char buf[BUFSIZ];
+#endif /* EXTREME_DEBUG */
+
+  c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE,
+                                   zclient->ibuf, vrf_id);
+
+  if (c == NULL)
+    return 0;
+
+  ifp = c->ifp;
+
+#ifdef EXTREME_DEBUG
+  p = c->address;
+  prefix2str (p, buf, BUFSIZ);
+
+  if (p->family == AF_INET)
+    zlog_debug ("disconnected IP address %s", buf);
+#ifdef HAVE_IPV6
+  if (p->family == AF_INET6)
+    zlog_debug ("disconnected IPv6 address %s", buf);
+#endif /* HAVE_IPV6 */
+#endif /* EXTREME_DEBUG */
+
+  if (if_is_operative (ifp))
+    isis_circuit_del_addr (circuit_scan_by_ifp (ifp), c);
+  connected_free (c);
+
+  return 0;
+}
+
+static int
+isis_zebra_link_params (int command, struct zclient *zclient,
+                        zebra_size_t length)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_link_params_read (zclient->ibuf);
+
+  if (ifp == NULL)
+    return 0;
+
+  /* Update TE TLV */
+  isis_mpls_te_update(ifp);
+
+  return 0;
+}
+
+static void
+isis_zebra_route_add_ipv4 (struct prefix *prefix,
+                          struct isis_route_info *route_info)
+{
+  u_char message, flags;
+  int psize;
+  struct stream *stream;
+  struct isis_nexthop *nexthop;
+  struct listnode *node;
+
+  if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
+    return;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT))
+    {
+      message = 0;
+      flags = 0;
+
+      SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP);
+      SET_FLAG (message, ZAPI_MESSAGE_METRIC);
+#if 0
+      SET_FLAG (message, ZAPI_MESSAGE_DISTANCE);
+#endif
+
+      stream = zclient->obuf;
+      stream_reset (stream);
+      zclient_create_header (stream, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT);
+      /* type */
+      stream_putc (stream, ZEBRA_ROUTE_ISIS);
+      /* flags */
+      stream_putc (stream, flags);
+      /* message */
+      stream_putc (stream, message);
+      /* SAFI */
+      stream_putw (stream, SAFI_UNICAST);
+      /* prefix information */
+      psize = PSIZE (prefix->prefixlen);
+      stream_putc (stream, prefix->prefixlen);
+      stream_write (stream, (u_char *) & prefix->u.prefix4, psize);
+
+      stream_putc (stream, listcount (route_info->nexthops));
+
+      /* Nexthop, ifindex, distance and metric information */
+      for (ALL_LIST_ELEMENTS_RO (route_info->nexthops, node, nexthop))
+       {
+         /* FIXME: can it be ? */
+         if (nexthop->ip.s_addr != INADDR_ANY)
+           {
+             stream_putc (stream, ZEBRA_NEXTHOP_IPV4);
+             stream_put_in_addr (stream, &nexthop->ip);
+           }
+         else
+           {
+             stream_putc (stream, ZEBRA_NEXTHOP_IFINDEX);
+             stream_putl (stream, nexthop->ifindex);
+           }
+       }
+#if 0
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+       stream_putc (stream, route_info->depth);
+#endif
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+       stream_putl (stream, route_info->cost);
+
+      stream_putw_at (stream, 0, stream_get_endp (stream));
+      zclient_send_message(zclient);
+      SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+      UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
+    }
+}
+
+static void
+isis_zebra_route_del_ipv4 (struct prefix *prefix,
+                          struct isis_route_info *route_info)
+{
+  struct zapi_ipv4 api;
+  struct prefix_ipv4 prefix4;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT))
+    {
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_ISIS;
+      api.flags = 0;
+      api.message = 0;
+      api.safi = SAFI_UNICAST;
+      prefix4.family = AF_INET;
+      prefix4.prefixlen = prefix->prefixlen;
+      prefix4.prefix = prefix->u.prefix4;
+      zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, &prefix4, &api);
+    }
+  UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+
+  return;
+}
+
+#ifdef HAVE_IPV6
+static void
+isis_zebra_route_add_ipv6 (struct prefix *prefix,
+                          struct isis_route_info *route_info)
+{
+  struct zapi_ipv6 api;
+  struct in6_addr **nexthop_list;
+  ifindex_t *ifindex_list;
+  struct isis_nexthop6 *nexthop6;
+  int i, size;
+  struct listnode *node;
+  struct prefix_ipv6 prefix6;
+
+  if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
+    return;
+
+  api.vrf_id = VRF_DEFAULT;
+  api.type = ZEBRA_ROUTE_ISIS;
+  api.flags = 0;
+  api.message = 0;
+  api.safi = SAFI_UNICAST;
+  SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+  SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+  SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+  api.metric = route_info->cost;
+#if 0
+  SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+  api.distance = route_info->depth;
+#endif
+  api.nexthop_num = listcount (route_info->nexthops6);
+  api.ifindex_num = listcount (route_info->nexthops6);
+
+  /* allocate memory for nexthop_list */
+  size = sizeof (struct isis_nexthop6 *) * listcount (route_info->nexthops6);
+  nexthop_list = (struct in6_addr **) XMALLOC (MTYPE_ISIS_TMP, size);
+  if (!nexthop_list)
+    {
+      zlog_err ("isis_zebra_add_route_ipv6: out of memory!");
+      return;
+    }
+
+  /* allocate memory for ifindex_list */
+  size = sizeof (unsigned int) * listcount (route_info->nexthops6);
+  ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size);
+  if (!ifindex_list)
+    {
+      zlog_err ("isis_zebra_add_route_ipv6: out of memory!");
+      XFREE (MTYPE_ISIS_TMP, nexthop_list);
+      return;
+    }
+
+  /* for each nexthop */
+  i = 0;
+  for (ALL_LIST_ELEMENTS_RO (route_info->nexthops6, node, nexthop6))
+    {
+      if (!IN6_IS_ADDR_LINKLOCAL (&nexthop6->ip6) &&
+         !IN6_IS_ADDR_UNSPECIFIED (&nexthop6->ip6))
+       {
+         api.nexthop_num--;
+         api.ifindex_num--;
+         continue;
+       }
+
+      nexthop_list[i] = &nexthop6->ip6;
+      ifindex_list[i] = nexthop6->ifindex;
+      i++;
+    }
+
+  api.nexthop = nexthop_list;
+  api.ifindex = ifindex_list;
+
+  if (api.nexthop_num && api.ifindex_num)
+    {
+      prefix6.family = AF_INET6;
+      prefix6.prefixlen = prefix->prefixlen;
+      memcpy (&prefix6.prefix, &prefix->u.prefix6, sizeof (struct in6_addr));
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, &prefix6, &api);
+      SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+      UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
+    }
+
+  XFREE (MTYPE_ISIS_TMP, nexthop_list);
+  XFREE (MTYPE_ISIS_TMP, ifindex_list);
+
+  return;
+}
+
+static void
+isis_zebra_route_del_ipv6 (struct prefix *prefix,
+                          struct isis_route_info *route_info)
+{
+  struct zapi_ipv6 api;
+  struct in6_addr **nexthop_list;
+  ifindex_t *ifindex_list;
+  struct isis_nexthop6 *nexthop6;
+  int i, size;
+  struct listnode *node;
+  struct prefix_ipv6 prefix6;
+
+  if (!CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
+    return;
+
+  api.vrf_id = VRF_DEFAULT;
+  api.type = ZEBRA_ROUTE_ISIS;
+  api.flags = 0;
+  api.message = 0;
+  api.safi = SAFI_UNICAST;
+  SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+  SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+  api.nexthop_num = listcount (route_info->nexthops6);
+  api.ifindex_num = listcount (route_info->nexthops6);
+
+  /* allocate memory for nexthop_list */
+  size = sizeof (struct isis_nexthop6 *) * listcount (route_info->nexthops6);
+  nexthop_list = (struct in6_addr **) XMALLOC (MTYPE_ISIS_TMP, size);
+  if (!nexthop_list)
+    {
+      zlog_err ("isis_zebra_route_del_ipv6: out of memory!");
+      return;
+    }
+
+  /* allocate memory for ifindex_list */
+  size = sizeof (unsigned int) * listcount (route_info->nexthops6);
+  ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size);
+  if (!ifindex_list)
+    {
+      zlog_err ("isis_zebra_route_del_ipv6: out of memory!");
+      XFREE (MTYPE_ISIS_TMP, nexthop_list);
+      return;
+    }
+
+  /* for each nexthop */
+  i = 0;
+  for (ALL_LIST_ELEMENTS_RO (route_info->nexthops6, node, nexthop6))
+    {
+      if (!IN6_IS_ADDR_LINKLOCAL (&nexthop6->ip6) &&
+         !IN6_IS_ADDR_UNSPECIFIED (&nexthop6->ip6))
+       {
+         api.nexthop_num--;
+         api.ifindex_num--;
+         continue;
+       }
+
+      nexthop_list[i] = &nexthop6->ip6;
+      ifindex_list[i] = nexthop6->ifindex;
+      i++;
+    }
+
+  api.nexthop = nexthop_list;
+  api.ifindex = ifindex_list;
+
+  if (api.nexthop_num && api.ifindex_num)
+    {
+      prefix6.family = AF_INET6;
+      prefix6.prefixlen = prefix->prefixlen;
+      memcpy (&prefix6.prefix, &prefix->u.prefix6, sizeof (struct in6_addr));
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, &prefix6, &api);
+      UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+    }
+
+  XFREE (MTYPE_ISIS_TMP, nexthop_list);
+  XFREE (MTYPE_ISIS_TMP, ifindex_list);
+}
+
+#endif /* HAVE_IPV6 */
+
+void
+isis_zebra_route_update (struct prefix *prefix,
+                        struct isis_route_info *route_info)
+{
+  if (zclient->sock < 0)
+    return;
+
+  if (!vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT))
+    return;
+
+  if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE))
+    {
+      if (prefix->family == AF_INET)
+       isis_zebra_route_add_ipv4 (prefix, route_info);
+#ifdef HAVE_IPV6
+      else if (prefix->family == AF_INET6)
+       isis_zebra_route_add_ipv6 (prefix, route_info);
+#endif /* HAVE_IPV6 */
+    }
+  else
+    {
+      if (prefix->family == AF_INET)
+       isis_zebra_route_del_ipv4 (prefix, route_info);
+#ifdef HAVE_IPV6
+      else if (prefix->family == AF_INET6)
+       isis_zebra_route_del_ipv6 (prefix, route_info);
+#endif /* HAVE_IPV6 */
+    }
+  return;
+}
+
+static int
+isis_zebra_read_ipv4 (int command, struct zclient *zclient,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *stream;
+  struct zapi_ipv4 api;
+  struct prefix_ipv4 p;
+  struct prefix *p_generic = (struct prefix*)&p;
+  unsigned long ifindex __attribute__ ((unused));
+  struct in_addr nexthop __attribute__ ((unused));
+  unsigned char plength = 0;
+
+  stream = zclient->ibuf;
+  memset(&api, 0, sizeof(api));
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  memset(&nexthop, 0, sizeof(nexthop));
+  ifindex = 0;
+
+  api.type = stream_getc (stream);
+  api.flags = stream_getc (stream);
+  api.message = stream_getc (stream);
+
+  p.family = AF_INET;
+  plength = stream_getc (stream);
+  p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, stream, PSIZE (p.prefixlen));
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (stream);
+      nexthop.s_addr = stream_get_ipv4 (stream);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (stream);
+      ifindex = stream_getl (stream);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (stream);
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (stream);
+
+  /*
+   * Avoid advertising a false default reachability. (A default
+   * route installed by IS-IS gets redistributed from zebra back
+   * into IS-IS causing us to start advertising default reachabity
+   * without this check)
+   */
+  if (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS)
+    command = ZEBRA_IPV4_ROUTE_DELETE;
+
+  if (command == ZEBRA_IPV4_ROUTE_ADD)
+    isis_redist_add(api.type, p_generic, api.distance, api.metric);
+  else
+    isis_redist_delete(api.type, p_generic);
+
+  return 0;
+}
+
+static int
+isis_zebra_read_ipv6 (int command, struct zclient *zclient,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *stream;
+  struct zapi_ipv6 api;
+  struct prefix_ipv6 p;
+  struct prefix *p_generic = (struct prefix*)&p;
+  struct in6_addr nexthop;
+  unsigned long ifindex __attribute__((unused));
+
+  stream = zclient->ibuf;
+  memset(&api, 0, sizeof(api));
+  memset(&p, 0, sizeof(struct prefix_ipv6));
+  memset(&nexthop, 0, sizeof(nexthop));
+  ifindex = 0;
+
+  api.type = stream_getc(stream);
+  api.flags = stream_getc(stream);
+  api.message = stream_getc(stream);
+
+  p.family = AF_INET6;
+  p.prefixlen = stream_getc(stream);
+  stream_get(&p.prefix, stream, PSIZE(p.prefixlen));
+
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc(stream); /* this is always 1 */
+      stream_get(&nexthop, stream, sizeof(nexthop));
+    }
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc(stream);
+      ifindex = stream_getl(stream);
+    }
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc(stream);
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl(stream);
+
+  /*
+   * Avoid advertising a false default reachability. (A default
+   * route installed by IS-IS gets redistributed from zebra back
+   * into IS-IS causing us to start advertising default reachabity
+   * without this check)
+   */
+  if (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS)
+    command = ZEBRA_IPV6_ROUTE_DELETE;
+
+  if (command == ZEBRA_IPV6_ROUTE_ADD)
+    isis_redist_add(api.type, p_generic, api.distance, api.metric);
+  else
+    isis_redist_delete(api.type, p_generic);
+
+  return 0;
+}
+
+int
+isis_distribute_list_update (int routetype)
+{
+  return 0;
+}
+
+void
+isis_zebra_redistribute_set(int type)
+{
+  if (type == DEFAULT_ROUTE)
+    zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, VRF_DEFAULT);
+  else
+    zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+}
+
+void
+isis_zebra_redistribute_unset(int type)
+{
+  if (type == DEFAULT_ROUTE)
+    zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, VRF_DEFAULT);
+  else
+    zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, type, VRF_DEFAULT);
+}
+
+static void
+isis_zebra_connected (struct zclient *zclient)
+{
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+void
+isis_zebra_init (struct thread_master *master)
+{
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_ISIS);
+  zclient->zebra_connected = isis_zebra_connected;
+  zclient->router_id_update = isis_router_id_update_zebra;
+  zclient->interface_add = isis_zebra_if_add;
+  zclient->interface_delete = isis_zebra_if_del;
+  zclient->interface_up = isis_zebra_if_state_up;
+  zclient->interface_down = isis_zebra_if_state_down;
+  zclient->interface_address_add = isis_zebra_if_address_add;
+  zclient->interface_address_delete = isis_zebra_if_address_del;
+  zclient->interface_link_params = isis_zebra_link_params;
+  zclient->ipv4_route_add = isis_zebra_read_ipv4;
+  zclient->ipv4_route_delete = isis_zebra_read_ipv4;
+#ifdef HAVE_IPV6
+  zclient->ipv6_route_add = isis_zebra_read_ipv6;
+  zclient->ipv6_route_delete = isis_zebra_read_ipv6;
+#endif /* HAVE_IPV6 */
+
+  return;
+}
diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h
new file mode 100644 (file)
index 0000000..dd36d7d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_zebra.h   
+ *
+ * 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.
+ */
+#ifndef _ZEBRA_ISIS_ZEBRA_H
+#define _ZEBRA_ISIS_ZEBRA_H
+
+extern struct zclient *zclient;
+
+void isis_zebra_init (struct thread_master *);
+void isis_zebra_route_update (struct prefix *prefix,
+                             struct isis_route_info *route_info);
+int isis_distribute_list_update (int routetype);
+void isis_zebra_redistribute_set(int type);
+void isis_zebra_redistribute_unset(int type);
+
+#endif /* _ZEBRA_ISIS_ZEBRA_H */
diff --git a/isisd/isisd.c b/isisd/isisd.c
new file mode 100644 (file)
index 0000000..9844ae5
--- /dev/null
@@ -0,0 +1,2406 @@
+/*
+ * IS-IS Rout(e)ing protocol - isisd.c
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "time.h"
+#include "linklist.h"
+#include "if.h"
+#include "hash.h"
+#include "stream.h"
+#include "prefix.h"
+#include "table.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_csm.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_zebra.h"
+#include "isisd/isis_events.h"
+#include "isisd/isis_te.h"
+
+#ifdef TOPOLOGY_GENERATE
+#include "spgrid.h"
+u_char DEFAULT_TOPOLOGY_BASEIS[6] = { 0xFE, 0xED, 0xFE, 0xED, 0x00, 0x00 };
+#endif /* TOPOLOGY_GENERATE */
+
+struct isis *isis = NULL;
+
+/*
+ * Prototypes.
+ */
+int isis_area_get(struct vty *, const char *);
+int isis_area_destroy(struct vty *, const char *);
+int area_net_title(struct vty *, const char *);
+int area_clear_net_title(struct vty *, const char *);
+int show_isis_interface_common(struct vty *, const char *ifname, char);
+int show_isis_neighbor_common(struct vty *, const char *id, char);
+int clear_isis_neighbor_common(struct vty *, const char *id);
+int isis_config_write(struct vty *);
+
+
+
+void
+isis_new (unsigned long process_id)
+{
+  isis = XCALLOC (MTYPE_ISIS, sizeof (struct isis));
+  /*
+   * Default values
+   */
+  isis->max_area_addrs = 3;
+  isis->process_id = process_id;
+  isis->router_id = 0;
+  isis->area_list = list_new ();
+  isis->init_circ_list = list_new ();
+  isis->uptime = time (NULL);
+  isis->nexthops = list_new ();
+#ifdef HAVE_IPV6
+  isis->nexthops6 = list_new ();
+#endif /* HAVE_IPV6 */
+  dyn_cache_init ();
+  /*
+   * uncomment the next line for full debugs
+   */
+  /* isis->debugs = 0xFFFF; */
+  isisMplsTE.status = disable;            /* Only support TE metric */
+}
+
+struct isis_area *
+isis_area_create (const char *area_tag)
+{
+  struct isis_area *area;
+
+  area = XCALLOC (MTYPE_ISIS_AREA, sizeof (struct isis_area));
+
+  /*
+   * The first instance is level-1-2 rest are level-1, unless otherwise
+   * configured
+   */
+  if (listcount (isis->area_list) > 0)
+    area->is_type = IS_LEVEL_1;
+  else
+    area->is_type = IS_LEVEL_1_AND_2;
+
+  /*
+   * intialize the databases
+   */
+  if (area->is_type & IS_LEVEL_1)
+    {
+      area->lspdb[0] = lsp_db_init ();
+      area->route_table[0] = route_table_init ();
+#ifdef HAVE_IPV6
+      area->route_table6[0] = route_table_init ();
+#endif /* HAVE_IPV6 */
+    }
+  if (area->is_type & IS_LEVEL_2)
+    {
+      area->lspdb[1] = lsp_db_init ();
+      area->route_table[1] = route_table_init ();
+#ifdef HAVE_IPV6
+      area->route_table6[1] = route_table_init ();
+#endif /* HAVE_IPV6 */
+    }
+
+  spftree_area_init (area);
+
+  area->circuit_list = list_new ();
+  area->area_addrs = list_new ();
+  THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
+  flags_initialize (&area->flags);
+
+  /*
+   * Default values
+   */
+  area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME;    /* 1200 */
+  area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME;    /* 1200 */
+  area->lsp_refresh[0] = DEFAULT_MAX_LSP_GEN_INTERVAL; /* 900 */
+  area->lsp_refresh[1] = DEFAULT_MAX_LSP_GEN_INTERVAL; /* 900 */
+  area->lsp_gen_interval[0] = DEFAULT_MIN_LSP_GEN_INTERVAL;
+  area->lsp_gen_interval[1] = DEFAULT_MIN_LSP_GEN_INTERVAL;
+  area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL;
+  area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL;
+  area->dynhostname = 1;
+  area->oldmetric = 0;
+  area->newmetric = 1;
+  area->lsp_frag_threshold = 90;
+  area->lsp_mtu = DEFAULT_LSP_MTU;
+#ifdef TOPOLOGY_GENERATE
+  memcpy (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN);
+#endif /* TOPOLOGY_GENERATE */
+
+  area->area_tag = strdup (area_tag);
+  listnode_add (isis->area_list, area);
+  area->isis = isis;
+
+  return area;
+}
+
+struct isis_area *
+isis_area_lookup (const char *area_tag)
+{
+  struct isis_area *area;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    if ((area->area_tag == NULL && area_tag == NULL) ||
+       (area->area_tag && area_tag
+        && strcmp (area->area_tag, area_tag) == 0))
+    return area;
+
+  return NULL;
+}
+
+int
+isis_area_get (struct vty *vty, const char *area_tag)
+{
+  struct isis_area *area;
+
+  area = isis_area_lookup (area_tag);
+
+  if (area)
+    {
+      vty->node = ISIS_NODE;
+      vty->index = area;
+      return CMD_SUCCESS;
+    }
+
+  area = isis_area_create (area_tag);
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("New IS-IS area instance %s", area->area_tag);
+
+  vty->node = ISIS_NODE;
+  vty->index = area;
+
+  return CMD_SUCCESS;
+}
+
+int
+isis_area_destroy (struct vty *vty, const char *area_tag)
+{
+  struct isis_area *area;
+  struct listnode *node, *nnode;
+  struct isis_circuit *circuit;
+  struct area_addr *addr;
+
+  area = isis_area_lookup (area_tag);
+
+  if (area == NULL)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  if (area->circuit_list)
+    {
+      for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
+        {
+          circuit->ip_router = 0;
+#ifdef HAVE_IPV6
+          circuit->ipv6_router = 0;
+#endif
+          isis_csm_state_change (ISIS_DISABLE, circuit, area);
+        }
+      list_delete (area->circuit_list);
+      area->circuit_list = NULL;
+    }
+
+  if (area->lspdb[0] != NULL)
+    {
+      lsp_db_destroy (area->lspdb[0]);
+      area->lspdb[0] = NULL;
+    }
+  if (area->lspdb[1] != NULL)
+    {
+      lsp_db_destroy (area->lspdb[1]);
+      area->lspdb[1] = NULL;
+    }
+
+  spftree_area_del (area);
+
+  /* invalidate and validate would delete all routes from zebra */
+  isis_route_invalidate (area);
+  isis_route_validate (area);
+
+  if (area->route_table[0])
+    {
+      route_table_finish (area->route_table[0]);
+      area->route_table[0] = NULL;
+    }
+  if (area->route_table[1])
+    {
+      route_table_finish (area->route_table[1]);
+      area->route_table[1] = NULL;
+    }
+#ifdef HAVE_IPV6
+  if (area->route_table6[0])
+    {
+      route_table_finish (area->route_table6[0]);
+      area->route_table6[0] = NULL;
+    }
+  if (area->route_table6[1])
+    {
+      route_table_finish (area->route_table6[1]);
+      area->route_table6[1] = NULL;
+    }
+#endif /* HAVE_IPV6 */
+
+  isis_redist_area_finish(area);
+
+  for (ALL_LIST_ELEMENTS (area->area_addrs, node, nnode, addr))
+    {
+      list_delete_node (area->area_addrs, node);
+      XFREE (MTYPE_ISIS_AREA_ADDR, addr);
+    }
+  area->area_addrs = NULL;
+
+  THREAD_TIMER_OFF (area->t_tick);
+  THREAD_TIMER_OFF (area->t_lsp_refresh[0]);
+  THREAD_TIMER_OFF (area->t_lsp_refresh[1]);
+
+  thread_cancel_event (master, area);
+
+  listnode_delete (isis->area_list, area);
+
+  free (area->area_tag);
+
+  XFREE (MTYPE_ISIS_AREA, area);
+
+  if (listcount (isis->area_list) == 0)
+    {
+      memset (isis->sysid, 0, ISIS_SYS_ID_LEN);
+      isis->sysid_set = 0;
+    }
+
+  return CMD_SUCCESS;
+}
+
+int
+area_net_title (struct vty *vty, const char *net_title)
+{
+  struct isis_area *area;
+  struct area_addr *addr;
+  struct area_addr *addrp;
+  struct listnode *node;
+
+  u_char buff[255];
+  area = vty->index;
+
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  /* We check that we are not over the maximal number of addresses */
+  if (listcount (area->area_addrs) >= isis->max_area_addrs)
+    {
+      vty_out (vty, "Maximum of area addresses (%d) already reached %s",
+              isis->max_area_addrs, VTY_NEWLINE);
+      return CMD_ERR_NOTHING_TODO;
+    }
+
+  addr = XMALLOC (MTYPE_ISIS_AREA_ADDR, sizeof (struct area_addr));
+  addr->addr_len = dotformat2buff (buff, net_title);
+  memcpy (addr->area_addr, buff, addr->addr_len);
+#ifdef EXTREME_DEBUG
+  zlog_debug ("added area address %s for area %s (address length %d)",
+            net_title, area->area_tag, addr->addr_len);
+#endif /* EXTREME_DEBUG */
+  if (addr->addr_len < 8 || addr->addr_len > 20)
+    {
+      vty_out (vty, "area address must be at least 8..20 octets long (%d)%s",
+               addr->addr_len, VTY_NEWLINE);
+      XFREE (MTYPE_ISIS_AREA_ADDR, addr);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  if (addr->area_addr[addr->addr_len-1] != 0)
+    {
+      vty_out (vty, "nsel byte (last byte) in area address must be 0%s",
+               VTY_NEWLINE);
+      XFREE (MTYPE_ISIS_AREA_ADDR, addr);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  if (isis->sysid_set == 0)
+    {
+      /*
+       * First area address - get the SystemID for this router
+       */
+      memcpy (isis->sysid, GETSYSID (addr), ISIS_SYS_ID_LEN);
+      isis->sysid_set = 1;
+      if (isis->debugs & DEBUG_EVENTS)
+       zlog_debug ("Router has SystemID %s", sysid_print (isis->sysid));
+    }
+  else
+    {
+      /*
+       * Check that the SystemID portions match
+       */
+      if (memcmp (isis->sysid, GETSYSID (addr), ISIS_SYS_ID_LEN))
+       {
+         vty_out (vty,
+                  "System ID must not change when defining additional area"
+                  " addresses%s", VTY_NEWLINE);
+         XFREE (MTYPE_ISIS_AREA_ADDR, addr);
+         return CMD_ERR_AMBIGUOUS;
+       }
+
+      /* now we see that we don't already have this address */
+      for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp))
+       {
+         if ((addrp->addr_len + ISIS_SYS_ID_LEN + ISIS_NSEL_LEN) != (addr->addr_len))
+           continue;
+         if (!memcmp (addrp->area_addr, addr->area_addr, addr->addr_len))
+           {
+             XFREE (MTYPE_ISIS_AREA_ADDR, addr);
+             return CMD_SUCCESS;       /* silent fail */
+           }
+       }
+    }
+
+  /*
+   * Forget the systemID part of the address
+   */
+  addr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN);
+  listnode_add (area->area_addrs, addr);
+
+  /* only now we can safely generate our LSPs for this area */
+  if (listcount (area->area_addrs) > 0)
+    {
+      if (area->is_type & IS_LEVEL_1)
+        lsp_generate (area, IS_LEVEL_1);
+      if (area->is_type & IS_LEVEL_2)
+        lsp_generate (area, IS_LEVEL_2);
+    }
+
+  return CMD_SUCCESS;
+}
+
+int
+area_clear_net_title (struct vty *vty, const char *net_title)
+{
+  struct isis_area *area;
+  struct area_addr addr, *addrp = NULL;
+  struct listnode *node;
+  u_char buff[255];
+
+  area = vty->index;
+  if (!area)
+    {
+      vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  addr.addr_len = dotformat2buff (buff, net_title);
+  if (addr.addr_len < 8 || addr.addr_len > 20)
+    {
+      vty_out (vty, "Unsupported area address length %d, should be 8...20 %s",
+              addr.addr_len, VTY_NEWLINE);
+      return CMD_ERR_AMBIGUOUS;
+    }
+
+  memcpy (addr.area_addr, buff, (int) addr.addr_len);
+
+  for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp))
+    if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len &&
+       !memcmp (addrp->area_addr, addr.area_addr, addr.addr_len))
+    break;
+
+  if (!addrp)
+    {
+      vty_out (vty, "No area address %s for area %s %s", net_title,
+              area->area_tag, VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+
+  listnode_delete (area->area_addrs, addrp);
+  XFREE (MTYPE_ISIS_AREA_ADDR, addrp);
+
+  /*
+   * Last area address - reset the SystemID for this router
+   */
+  if (listcount (area->area_addrs) == 0)
+    {
+      memset (isis->sysid, 0, ISIS_SYS_ID_LEN);
+      isis->sysid_set = 0;
+      if (isis->debugs & DEBUG_EVENTS)
+        zlog_debug ("Router has no SystemID");
+    }
+
+  return CMD_SUCCESS;
+}
+
+/*
+ * 'show isis interface' command
+ */
+
+int
+show_isis_interface_common (struct vty *vty, const char *ifname, char detail)
+{
+  struct listnode *anode, *cnode;
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+
+  if (!isis)
+    {
+      vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, anode, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag, VTY_NEWLINE);
+
+      if (detail == ISIS_UI_LEVEL_BRIEF)
+        vty_out (vty, "  Interface   CircId   State    Type     Level%s",
+                 VTY_NEWLINE);
+
+      for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+        if (!ifname)
+          isis_circuit_print_vty (circuit, vty, detail);
+        else if (strcmp(circuit->interface->name, ifname) == 0)
+          isis_circuit_print_vty (circuit, vty, detail);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_interface,
+       show_isis_interface_cmd,
+       "show isis interface",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS interface\n")
+{
+  return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_BRIEF);
+}
+
+DEFUN (show_isis_interface_detail,
+       show_isis_interface_detail_cmd,
+       "show isis interface detail",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS interface\n"
+       "show detailed information\n")
+{
+  return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_DETAIL);
+}
+
+DEFUN (show_isis_interface_arg,
+       show_isis_interface_arg_cmd,
+       "show isis interface WORD",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS interface\n"
+       "ISIS interface name\n")
+{
+  return show_isis_interface_common (vty, argv[0], ISIS_UI_LEVEL_DETAIL);
+}
+
+/*
+ * 'show isis neighbor' command
+ */
+
+int
+show_isis_neighbor_common (struct vty *vty, const char *id, char detail)
+{
+  struct listnode *anode, *cnode, *node;
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+  struct list *adjdb;
+  struct isis_adjacency *adj;
+  struct isis_dynhn *dynhn;
+  u_char sysid[ISIS_SYS_ID_LEN];
+  int i;
+
+  if (!isis)
+    {
+      vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  memset (sysid, 0, ISIS_SYS_ID_LEN);
+  if (id)
+    {
+      if (sysid2buff (sysid, id) == 0)
+        {
+          dynhn = dynhn_find_by_name (id);
+          if (dynhn == NULL)
+            {
+              vty_out (vty, "Invalid system id %s%s", id, VTY_NEWLINE);
+              return CMD_SUCCESS;
+            }
+          memcpy (sysid, dynhn->id, ISIS_SYS_ID_LEN);
+        }
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, anode, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag, VTY_NEWLINE);
+
+      if (detail == ISIS_UI_LEVEL_BRIEF)
+        vty_out (vty, "  System Id           Interface   L  State"
+                      "        Holdtime SNPA%s", VTY_NEWLINE);
+
+      for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+        {
+          if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  adjdb = circuit->u.bc.adjdb[i];
+                  if (adjdb && adjdb->count)
+                    {
+                      for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+                        if (!id || !memcmp (adj->sysid, sysid,
+                                            ISIS_SYS_ID_LEN))
+                          isis_adj_print_vty (adj, vty, detail);
+                    }
+                }
+            }
+          else if (circuit->circ_type == CIRCUIT_T_P2P &&
+                   circuit->u.p2p.neighbor)
+            {
+              adj = circuit->u.p2p.neighbor;
+              if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN))
+                isis_adj_print_vty (adj, vty, detail);
+            }
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+/*
+ * 'clear isis neighbor' command
+ */
+int
+clear_isis_neighbor_common (struct vty *vty, const char *id)
+{
+  struct listnode *anode, *cnode, *cnextnode, *node, *nnode;
+  struct isis_area *area;
+  struct isis_circuit *circuit;
+  struct list *adjdb;
+  struct isis_adjacency *adj;
+  struct isis_dynhn *dynhn;
+  u_char sysid[ISIS_SYS_ID_LEN];
+  int i;
+
+  if (!isis)
+    {
+      vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  memset (sysid, 0, ISIS_SYS_ID_LEN);
+  if (id)
+    {
+      if (sysid2buff (sysid, id) == 0)
+        {
+          dynhn = dynhn_find_by_name (id);
+          if (dynhn == NULL)
+            {
+              vty_out (vty, "Invalid system id %s%s", id, VTY_NEWLINE);
+              return CMD_SUCCESS;
+            }
+          memcpy (sysid, dynhn->id, ISIS_SYS_ID_LEN);
+        }
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, anode, area))
+    {
+      for (ALL_LIST_ELEMENTS (area->circuit_list, cnode, cnextnode, circuit))
+        {
+          if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+            {
+              for (i = 0; i < 2; i++)
+                {
+                  adjdb = circuit->u.bc.adjdb[i];
+                  if (adjdb && adjdb->count)
+                    {
+                      for (ALL_LIST_ELEMENTS (adjdb, node, nnode, adj))
+                        if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN))
+                          isis_adj_state_change (adj, ISIS_ADJ_DOWN,
+                                                 "clear user request");
+                    }
+                }
+            }
+          else if (circuit->circ_type == CIRCUIT_T_P2P &&
+                   circuit->u.p2p.neighbor)
+            {
+              adj = circuit->u.p2p.neighbor;
+              if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN))
+                isis_adj_state_change (adj, ISIS_ADJ_DOWN,
+                                       "clear user request");
+            }
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_neighbor,
+       show_isis_neighbor_cmd,
+       "show isis neighbor",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS neighbor adjacencies\n")
+{
+  return show_isis_neighbor_common (vty, NULL, ISIS_UI_LEVEL_BRIEF);
+}
+
+DEFUN (show_isis_neighbor_detail,
+       show_isis_neighbor_detail_cmd,
+       "show isis neighbor detail",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS neighbor adjacencies\n"
+       "show detailed information\n")
+{
+  return show_isis_neighbor_common (vty, NULL, ISIS_UI_LEVEL_DETAIL);
+}
+
+DEFUN (show_isis_neighbor_arg,
+       show_isis_neighbor_arg_cmd,
+       "show isis neighbor WORD",
+       SHOW_STR
+       "ISIS network information\n"
+       "ISIS neighbor adjacencies\n"
+       "System id\n")
+{
+  return show_isis_neighbor_common (vty, argv[0], ISIS_UI_LEVEL_DETAIL);
+}
+
+DEFUN (clear_isis_neighbor,
+       clear_isis_neighbor_cmd,
+       "clear isis neighbor",
+       CLEAR_STR
+       "Reset ISIS network information\n"
+       "Reset ISIS neighbor adjacencies\n")
+{
+  return clear_isis_neighbor_common (vty, NULL);
+}
+
+DEFUN (clear_isis_neighbor_arg,
+       clear_isis_neighbor_arg_cmd,
+       "clear isis neighbor WORD",
+       CLEAR_STR
+       "ISIS network information\n"
+       "ISIS neighbor adjacencies\n"
+       "System id\n")
+{
+  return clear_isis_neighbor_common (vty, argv[0]);
+}
+
+/*
+ * 'isis debug', 'show debugging'
+ */
+void
+print_debug (struct vty *vty, int flags, int onoff)
+{
+  char onoffs[4];
+  if (onoff)
+    strcpy (onoffs, "on");
+  else
+    strcpy (onoffs, "off");
+
+  if (flags & DEBUG_ADJ_PACKETS)
+    vty_out (vty, "IS-IS Adjacency related packets debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_CHECKSUM_ERRORS)
+    vty_out (vty, "IS-IS checksum errors debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_LOCAL_UPDATES)
+    vty_out (vty, "IS-IS local updates debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_PROTOCOL_ERRORS)
+    vty_out (vty, "IS-IS protocol errors debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_SNP_PACKETS)
+    vty_out (vty, "IS-IS CSNP/PSNP packets debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_SPF_EVENTS)
+    vty_out (vty, "IS-IS SPF events debugging is %s%s", onoffs, VTY_NEWLINE);
+  if (flags & DEBUG_SPF_STATS)
+    vty_out (vty, "IS-IS SPF Timing and Statistics Data debugging is %s%s",
+            onoffs, VTY_NEWLINE);
+  if (flags & DEBUG_SPF_TRIGGERS)
+    vty_out (vty, "IS-IS SPF triggering events debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_UPDATE_PACKETS)
+    vty_out (vty, "IS-IS Update related packet debugging is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_RTE_EVENTS)
+    vty_out (vty, "IS-IS Route related debuggin is %s%s", onoffs,
+            VTY_NEWLINE);
+  if (flags & DEBUG_EVENTS)
+    vty_out (vty, "IS-IS Event debugging is %s%s", onoffs, VTY_NEWLINE);
+  if (flags & DEBUG_PACKET_DUMP)
+    vty_out (vty, "IS-IS Packet dump debugging is %s%s", onoffs, VTY_NEWLINE);
+  if (flags & DEBUG_LSP_GEN)
+    vty_out (vty, "IS-IS LSP generation debugging is %s%s", onoffs, VTY_NEWLINE);
+  if (flags & DEBUG_LSP_SCHED)
+    vty_out (vty, "IS-IS LSP scheduling debugging is %s%s", onoffs, VTY_NEWLINE);
+}
+
+DEFUN (show_debugging,
+       show_debugging_isis_cmd,
+       "show debugging isis",
+       SHOW_STR
+       "State of each debugging option\n")
+{
+  if (isis->debugs) {
+      vty_out (vty, "IS-IS:%s", VTY_NEWLINE);
+      print_debug (vty, isis->debugs, 1);
+  }
+  return CMD_SUCCESS;
+}
+
+/* Debug node. */
+static struct cmd_node debug_node = {
+  DEBUG_NODE,
+  "",
+  1
+};
+
+static int
+config_write_debug (struct vty *vty)
+{
+  int write = 0;
+  int flags = isis->debugs;
+
+  if (flags & DEBUG_ADJ_PACKETS)
+    {
+      vty_out (vty, "debug isis adj-packets%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_CHECKSUM_ERRORS)
+    {
+      vty_out (vty, "debug isis checksum-errors%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_LOCAL_UPDATES)
+    {
+      vty_out (vty, "debug isis local-updates%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_PROTOCOL_ERRORS)
+    {
+      vty_out (vty, "debug isis protocol-errors%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_SNP_PACKETS)
+    {
+      vty_out (vty, "debug isis snp-packets%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_SPF_EVENTS)
+    {
+      vty_out (vty, "debug isis spf-events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_SPF_STATS)
+    {
+      vty_out (vty, "debug isis spf-statistics%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_SPF_TRIGGERS)
+    {
+      vty_out (vty, "debug isis spf-triggers%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_UPDATE_PACKETS)
+    {
+      vty_out (vty, "debug isis update-packets%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_RTE_EVENTS)
+    {
+      vty_out (vty, "debug isis route-events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_EVENTS)
+    {
+      vty_out (vty, "debug isis events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_PACKET_DUMP)
+    {
+      vty_out (vty, "debug isis packet-dump%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_LSP_GEN)
+    {
+      vty_out (vty, "debug isis lsp-gen%s", VTY_NEWLINE);
+      write++;
+    }
+  if (flags & DEBUG_LSP_SCHED)
+    {
+      vty_out (vty, "debug isis lsp-sched%s", VTY_NEWLINE);
+      write++;
+    }
+
+  return write;
+}
+
+DEFUN (debug_isis_adj,
+       debug_isis_adj_cmd,
+       "debug isis adj-packets",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Adjacency related packets\n")
+{
+  isis->debugs |= DEBUG_ADJ_PACKETS;
+  print_debug (vty, DEBUG_ADJ_PACKETS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_adj,
+       no_debug_isis_adj_cmd,
+       "no debug isis adj-packets",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Adjacency related packets\n")
+{
+  isis->debugs &= ~DEBUG_ADJ_PACKETS;
+  print_debug (vty, DEBUG_ADJ_PACKETS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_csum,
+       debug_isis_csum_cmd,
+       "debug isis checksum-errors",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS LSP checksum errors\n")
+{
+  isis->debugs |= DEBUG_CHECKSUM_ERRORS;
+  print_debug (vty, DEBUG_CHECKSUM_ERRORS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_csum,
+       no_debug_isis_csum_cmd,
+       "no debug isis checksum-errors",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS LSP checksum errors\n")
+{
+  isis->debugs &= ~DEBUG_CHECKSUM_ERRORS;
+  print_debug (vty, DEBUG_CHECKSUM_ERRORS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_lupd,
+       debug_isis_lupd_cmd,
+       "debug isis local-updates",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS local update packets\n")
+{
+  isis->debugs |= DEBUG_LOCAL_UPDATES;
+  print_debug (vty, DEBUG_LOCAL_UPDATES, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_lupd,
+       no_debug_isis_lupd_cmd,
+       "no debug isis local-updates",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS local update packets\n")
+{
+  isis->debugs &= ~DEBUG_LOCAL_UPDATES;
+  print_debug (vty, DEBUG_LOCAL_UPDATES, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_err,
+       debug_isis_err_cmd,
+       "debug isis protocol-errors",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS LSP protocol errors\n")
+{
+  isis->debugs |= DEBUG_PROTOCOL_ERRORS;
+  print_debug (vty, DEBUG_PROTOCOL_ERRORS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_err,
+       no_debug_isis_err_cmd,
+       "no debug isis protocol-errors",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS LSP protocol errors\n")
+{
+  isis->debugs &= ~DEBUG_PROTOCOL_ERRORS;
+  print_debug (vty, DEBUG_PROTOCOL_ERRORS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_snp,
+       debug_isis_snp_cmd,
+       "debug isis snp-packets",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS CSNP/PSNP packets\n")
+{
+  isis->debugs |= DEBUG_SNP_PACKETS;
+  print_debug (vty, DEBUG_SNP_PACKETS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_snp,
+       no_debug_isis_snp_cmd,
+       "no debug isis snp-packets",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS CSNP/PSNP packets\n")
+{
+  isis->debugs &= ~DEBUG_SNP_PACKETS;
+  print_debug (vty, DEBUG_SNP_PACKETS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_upd,
+       debug_isis_upd_cmd,
+       "debug isis update-packets",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Update related packets\n")
+{
+  isis->debugs |= DEBUG_UPDATE_PACKETS;
+  print_debug (vty, DEBUG_UPDATE_PACKETS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_upd,
+       no_debug_isis_upd_cmd,
+       "no debug isis update-packets",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Update related packets\n")
+{
+  isis->debugs &= ~DEBUG_UPDATE_PACKETS;
+  print_debug (vty, DEBUG_UPDATE_PACKETS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_spfevents,
+       debug_isis_spfevents_cmd,
+       "debug isis spf-events",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Shortest Path First Events\n")
+{
+  isis->debugs |= DEBUG_SPF_EVENTS;
+  print_debug (vty, DEBUG_SPF_EVENTS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_spfevents,
+       no_debug_isis_spfevents_cmd,
+       "no debug isis spf-events",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Shortest Path First Events\n")
+{
+  isis->debugs &= ~DEBUG_SPF_EVENTS;
+  print_debug (vty, DEBUG_SPF_EVENTS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_spfstats,
+       debug_isis_spfstats_cmd,
+       "debug isis spf-statistics ",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS SPF Timing and Statistic Data\n")
+{
+  isis->debugs |= DEBUG_SPF_STATS;
+  print_debug (vty, DEBUG_SPF_STATS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_spfstats,
+       no_debug_isis_spfstats_cmd,
+       "no debug isis spf-statistics",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS SPF Timing and Statistic Data\n")
+{
+  isis->debugs &= ~DEBUG_SPF_STATS;
+  print_debug (vty, DEBUG_SPF_STATS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_spftrigg,
+       debug_isis_spftrigg_cmd,
+       "debug isis spf-triggers",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS SPF triggering events\n")
+{
+  isis->debugs |= DEBUG_SPF_TRIGGERS;
+  print_debug (vty, DEBUG_SPF_TRIGGERS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_spftrigg,
+       no_debug_isis_spftrigg_cmd,
+       "no debug isis spf-triggers",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS SPF triggering events\n")
+{
+  isis->debugs &= ~DEBUG_SPF_TRIGGERS;
+  print_debug (vty, DEBUG_SPF_TRIGGERS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_rtevents,
+       debug_isis_rtevents_cmd,
+       "debug isis route-events",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Route related events\n")
+{
+  isis->debugs |= DEBUG_RTE_EVENTS;
+  print_debug (vty, DEBUG_RTE_EVENTS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_rtevents,
+       no_debug_isis_rtevents_cmd,
+       "no debug isis route-events",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Route related events\n")
+{
+  isis->debugs &= ~DEBUG_RTE_EVENTS;
+  print_debug (vty, DEBUG_RTE_EVENTS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_events,
+       debug_isis_events_cmd,
+       "debug isis events",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Events\n")
+{
+  isis->debugs |= DEBUG_EVENTS;
+  print_debug (vty, DEBUG_EVENTS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_events,
+       no_debug_isis_events_cmd,
+       "no debug isis events",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS Events\n")
+{
+  isis->debugs &= ~DEBUG_EVENTS;
+  print_debug (vty, DEBUG_EVENTS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_packet_dump,
+       debug_isis_packet_dump_cmd,
+       "debug isis packet-dump",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS packet dump\n")
+{
+  isis->debugs |= DEBUG_PACKET_DUMP;
+  print_debug (vty, DEBUG_PACKET_DUMP, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_packet_dump,
+       no_debug_isis_packet_dump_cmd,
+       "no debug isis packet-dump",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS packet dump\n")
+{
+  isis->debugs &= ~DEBUG_PACKET_DUMP;
+  print_debug (vty, DEBUG_PACKET_DUMP, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_lsp_gen,
+       debug_isis_lsp_gen_cmd,
+       "debug isis lsp-gen",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS generation of own LSPs\n")
+{
+  isis->debugs |= DEBUG_LSP_GEN;
+  print_debug (vty, DEBUG_LSP_GEN, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_lsp_gen,
+       no_debug_isis_lsp_gen_cmd,
+       "no debug isis lsp-gen",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS generation of own LSPs\n")
+{
+  isis->debugs &= ~DEBUG_LSP_GEN;
+  print_debug (vty, DEBUG_LSP_GEN, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_isis_lsp_sched,
+       debug_isis_lsp_sched_cmd,
+       "debug isis lsp-sched",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS scheduling of LSP generation\n")
+{
+  isis->debugs |= DEBUG_LSP_SCHED;
+  print_debug (vty, DEBUG_LSP_SCHED, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_isis_lsp_sched,
+       no_debug_isis_lsp_sched_cmd,
+       "no debug isis lsp-gen",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS scheduling of LSP generation\n")
+{
+  isis->debugs &= ~DEBUG_LSP_SCHED;
+  print_debug (vty, DEBUG_LSP_SCHED, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_hostname,
+       show_hostname_cmd,
+       "show isis hostname",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS Dynamic hostname mapping\n")
+{
+  dynhn_print_all (vty);
+
+  return CMD_SUCCESS;
+}
+
+static void
+vty_out_timestr(struct vty *vty, time_t uptime)
+{
+  struct tm *tm;
+  time_t difftime = time (NULL);
+  difftime -= uptime;
+  tm = gmtime (&difftime);
+
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+  if (difftime < ONE_DAY_SECOND)
+    vty_out (vty,  "%02d:%02d:%02d", 
+        tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (difftime < ONE_WEEK_SECOND)
+    vty_out (vty, "%dd%02dh%02dm", 
+        tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    vty_out (vty, "%02dw%dd%02dh", 
+        tm->tm_yday/7,
+        tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  vty_out (vty, " ago");
+}
+
+DEFUN (show_isis_summary,
+       show_isis_summary_cmd,
+       "show isis summary",
+       SHOW_STR "IS-IS information\n" "IS-IS summary\n")
+{
+  struct listnode *node, *node2;
+  struct isis_area *area;
+  struct isis_spftree *spftree;
+  int level;
+
+  if (isis == NULL)
+  {
+    vty_out (vty, "ISIS is not running%s", VTY_NEWLINE);
+    return CMD_SUCCESS;
+  }
+
+  vty_out (vty, "Process Id      : %ld%s", isis->process_id,
+      VTY_NEWLINE);
+  if (isis->sysid_set)
+    vty_out (vty, "System Id       : %s%s", sysid_print (isis->sysid),
+        VTY_NEWLINE);
+
+  vty_out (vty, "Up time         : ");
+  vty_out_timestr(vty, isis->uptime);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  if (isis->area_list)
+    vty_out (vty, "Number of areas : %d%s", isis->area_list->count,
+        VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+  {
+    vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
+        VTY_NEWLINE);
+
+    if (listcount (area->area_addrs) > 0)
+    {
+      struct area_addr *area_addr;
+      for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node2, area_addr))
+      {
+        vty_out (vty, "  Net: %s%s",
+            isonet_print (area_addr->area_addr,
+              area_addr->addr_len + ISIS_SYS_ID_LEN +
+              1), VTY_NEWLINE);
+      }
+    }
+
+    for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
+    {
+      if ((area->is_type & level) == 0)
+        continue;
+
+      vty_out (vty, "  Level-%d:%s", level, VTY_NEWLINE);
+      spftree = area->spftree[level - 1];
+      if (spftree->pending)
+        vty_out (vty, "    IPv4 SPF: (pending)%s", VTY_NEWLINE);
+      else
+        vty_out (vty, "    IPv4 SPF:%s", VTY_NEWLINE);
+
+      vty_out (vty, "      minimum interval  : %d%s",
+          area->min_spf_interval[level - 1], VTY_NEWLINE);
+
+      vty_out (vty, "      last run elapsed  : ");
+      vty_out_timestr(vty, spftree->last_run_timestamp);
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      vty_out (vty, "      last run duration : %u usec%s",
+               (u_int32_t)spftree->last_run_duration, VTY_NEWLINE);
+
+      vty_out (vty, "      run count         : %d%s",
+          spftree->runcount, VTY_NEWLINE);
+
+#ifdef HAVE_IPV6
+      spftree = area->spftree6[level - 1];
+      if (spftree->pending)
+        vty_out (vty, "    IPv6 SPF: (pending)%s", VTY_NEWLINE);
+      else
+        vty_out (vty, "    IPv6 SPF:%s", VTY_NEWLINE);
+
+      vty_out (vty, "      minimum interval  : %d%s",
+          area->min_spf_interval[level - 1], VTY_NEWLINE);
+
+      vty_out (vty, "      last run elapsed  : ");
+      vty_out_timestr(vty, spftree->last_run_timestamp);
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      vty_out (vty, "      last run duration : %llu msec%s",
+               (unsigned long long)spftree->last_run_duration, VTY_NEWLINE);
+
+      vty_out (vty, "      run count         : %d%s",
+          spftree->runcount, VTY_NEWLINE);
+#endif
+    }
+  }
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+/*
+ * This function supports following display options:
+ * [ show isis database [detail] ]
+ * [ show isis database <sysid> [detail] ]
+ * [ show isis database <hostname> [detail] ]
+ * [ show isis database <sysid>.<pseudo-id> [detail] ]
+ * [ show isis database <hostname>.<pseudo-id> [detail] ]
+ * [ show isis database <sysid>.<pseudo-id>-<fragment-number> [detail] ]
+ * [ show isis database <hostname>.<pseudo-id>-<fragment-number> [detail] ]
+ * [ show isis database detail <sysid> ]
+ * [ show isis database detail <hostname> ]
+ * [ show isis database detail <sysid>.<pseudo-id> ]
+ * [ show isis database detail <hostname>.<pseudo-id> ]
+ * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ]
+ * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ]
+ */
+static int
+show_isis_database (struct vty *vty, const char *argv, int ui_level)
+{
+  struct listnode *node;
+  struct isis_area *area;
+  struct isis_lsp *lsp;
+  struct isis_dynhn *dynhn;
+  const char *pos = argv;
+  u_char lspid[ISIS_SYS_ID_LEN+2];
+  char sysid[255];
+  u_char number[3];
+  int level, lsp_count;
+
+  if (isis->area_list->count == 0)
+    return CMD_SUCCESS;
+
+  memset (&lspid, 0, ISIS_SYS_ID_LEN);
+  memset (&sysid, 0, 255);
+
+  /*
+   * extract fragment and pseudo id from the string argv
+   * in the forms:
+   * (a) <systemid/hostname>.<pseudo-id>-<framenent> or
+   * (b) <systemid/hostname>.<pseudo-id> or
+   * (c) <systemid/hostname> or
+   * Where systemid is in the form:
+   * xxxx.xxxx.xxxx
+   */
+  if (argv)
+     strncpy (sysid, argv, 254);
+  if (argv && strlen (argv) > 3)
+    {
+      pos = argv + strlen (argv) - 3;
+      if (strncmp (pos, "-", 1) == 0)
+      {
+        memcpy (number, ++pos, 2);
+        lspid[ISIS_SYS_ID_LEN+1] = (u_char) strtol ((char *)number, NULL, 16);
+        pos -= 4;
+        if (strncmp (pos, ".", 1) != 0)
+          return CMD_ERR_AMBIGUOUS;
+      }
+      if (strncmp (pos, ".", 1) == 0)
+      {
+        memcpy (number, ++pos, 2);
+        lspid[ISIS_SYS_ID_LEN] = (u_char) strtol ((char *)number, NULL, 16);
+        sysid[pos - argv - 1] = '\0';
+      }
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
+               VTY_NEWLINE);
+
+      for (level = 0; level < ISIS_LEVELS; level++)
+        {
+          if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0)
+            {
+              lsp = NULL;
+              if (argv != NULL)
+                {
+                  /*
+                   * Try to find the lsp-id if the argv string is in
+                   * the form hostname.<pseudo-id>-<fragment>
+                   */
+                  if (sysid2buff (lspid, sysid))
+                    {
+                      lsp = lsp_search (lspid, area->lspdb[level]);
+                    }
+                  else if ((dynhn = dynhn_find_by_name (sysid)))
+                    {
+                      memcpy (lspid, dynhn->id, ISIS_SYS_ID_LEN);
+                      lsp = lsp_search (lspid, area->lspdb[level]);
+                    }
+                  else if (strncmp(unix_hostname (), sysid, 15) == 0)
+                    {
+                      memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
+                      lsp = lsp_search (lspid, area->lspdb[level]);
+                    }
+                }
+
+              if (lsp != NULL || argv == NULL)
+                {
+                  vty_out (vty, "IS-IS Level-%d link-state database:%s",
+                           level + 1, VTY_NEWLINE);
+
+                  /* print the title in all cases */
+                  vty_out (vty, "LSP ID                  PduLen  "
+                           "SeqNumber   Chksum  Holdtime  ATT/P/OL%s",
+                           VTY_NEWLINE);
+                }
+
+              if (lsp)
+                {
+                  if (ui_level == ISIS_UI_LEVEL_DETAIL)
+                    lsp_print_detail (lsp, vty, area->dynhostname);
+                  else
+                    lsp_print (lsp, vty, area->dynhostname);
+                }
+              else if (argv == NULL)
+                {
+                  lsp_count = lsp_print_all (vty, area->lspdb[level],
+                                             ui_level,
+                                             area->dynhostname);
+
+                  vty_out (vty, "    %u LSPs%s%s",
+                           lsp_count, VTY_NEWLINE, VTY_NEWLINE);
+                }
+            }
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_database_brief,
+       show_database_cmd,
+       "show isis database",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS link state database\n")
+{
+  return show_isis_database (vty, NULL, ISIS_UI_LEVEL_BRIEF);
+}
+
+DEFUN (show_database_lsp_brief,
+       show_database_arg_cmd,
+       "show isis database WORD",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS link state database\n"
+       "LSP ID\n")
+{
+  return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_BRIEF);
+}
+
+DEFUN (show_database_lsp_detail,
+       show_database_arg_detail_cmd,
+       "show isis database WORD detail",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS link state database\n"
+       "LSP ID\n"
+       "Detailed information\n")
+{
+  return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_DETAIL);
+}
+
+DEFUN (show_database_detail,
+       show_database_detail_cmd,
+       "show isis database detail",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS link state database\n")
+{
+  return show_isis_database (vty, NULL, ISIS_UI_LEVEL_DETAIL);
+}
+
+DEFUN (show_database_detail_lsp,
+       show_database_detail_arg_cmd,
+       "show isis database detail WORD",
+       SHOW_STR
+       "IS-IS information\n"
+       "IS-IS link state database\n"
+       "Detailed information\n"
+       "LSP ID\n")
+{
+  return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_DETAIL);
+}
+
+/* 
+ * 'router isis' command 
+ */
+DEFUN (router_isis,
+       router_isis_cmd,
+       "router isis WORD",
+       ROUTER_STR
+       "ISO IS-IS\n"
+       "ISO Routing area tag")
+{
+  return isis_area_get (vty, argv[0]);
+}
+
+/* 
+ *'no router isis' command 
+ */
+DEFUN (no_router_isis,
+       no_router_isis_cmd,
+       "no router isis WORD",
+       "no\n" ROUTER_STR "ISO IS-IS\n" "ISO Routing area tag")
+{
+  return isis_area_destroy (vty, argv[0]);
+}
+
+/*
+ * 'net' command
+ */
+DEFUN (net,
+       net_cmd,
+       "net WORD",
+       "A Network Entity Title for this process (OSI only)\n"
+       "XX.XXXX. ... .XXX.XX  Network entity title (NET)\n")
+{
+  return area_net_title (vty, argv[0]);
+}
+
+/*
+ * 'no net' command
+ */
+DEFUN (no_net,
+       no_net_cmd,
+       "no net WORD",
+       NO_STR
+       "A Network Entity Title for this process (OSI only)\n"
+       "XX.XXXX. ... .XXX.XX  Network entity title (NET)\n")
+{
+  return area_clear_net_title (vty, argv[0]);
+}
+
+void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu)
+{
+  area->lsp_mtu = lsp_mtu;
+  lsp_regenerate_schedule(area, IS_LEVEL_1_AND_2, 1);
+}
+
+static int
+isis_area_passwd_set(struct isis_area *area, int level, u_char passwd_type,
+                    const char *passwd, u_char snp_auth)
+{
+  struct isis_passwd *dest;
+  struct isis_passwd modified;
+  int len;
+
+  assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2));
+  dest = (level == IS_LEVEL_1) ? &area->area_passwd : &area->domain_passwd;
+  memset(&modified, 0, sizeof(modified));
+
+  if (passwd_type != ISIS_PASSWD_TYPE_UNUSED)
+    {
+      if (!passwd)
+        return -1;
+
+      len = strlen(passwd);
+      if (len > 254)
+        return -1;
+
+      modified.len = len;
+      strncpy((char*)modified.passwd, passwd, 255);
+      modified.type = passwd_type;
+      modified.snp_auth = snp_auth;
+    }
+
+  if (memcmp(&modified, dest, sizeof(modified)))
+    {
+      memcpy(dest, &modified, sizeof(modified));
+      lsp_regenerate_schedule(area, IS_LEVEL_1|IS_LEVEL_2, 1);
+    }
+
+  return 0;
+}
+
+int
+isis_area_passwd_unset (struct isis_area *area, int level)
+{
+  return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_UNUSED, NULL, 0);
+}
+
+int
+isis_area_passwd_cleartext_set (struct isis_area *area, int level,
+                                const char *passwd, u_char snp_auth)
+{
+  return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_CLEARTXT,
+                               passwd, snp_auth);
+}
+
+int
+isis_area_passwd_hmac_md5_set (struct isis_area *area, int level,
+                               const char *passwd, u_char snp_auth)
+{
+  return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_HMAC_MD5,
+                               passwd, snp_auth);
+}
+
+static void
+area_resign_level (struct isis_area *area, int level)
+{
+  if (area->lspdb[level - 1])
+    {
+      lsp_db_destroy (area->lspdb[level - 1]);
+      area->lspdb[level - 1] = NULL;
+    }
+  if (area->spftree[level - 1])
+    {
+      isis_spftree_del (area->spftree[level - 1]);
+      area->spftree[level - 1] = NULL;
+    }
+#ifdef HAVE_IPV6
+  if (area->spftree6[level - 1])
+    {
+      isis_spftree_del (area->spftree6[level - 1]);
+      area->spftree6[level - 1] = NULL;
+    }
+#endif
+  if (area->route_table[level - 1])
+    {
+      route_table_finish (area->route_table[level - 1]);
+      area->route_table[level - 1] = NULL;
+    }
+#ifdef HAVE_IPV6
+  if (area->route_table6[level - 1])
+    {
+      route_table_finish (area->route_table6[level - 1]);
+      area->route_table6[level - 1] = NULL;
+    }
+#endif /* HAVE_IPV6 */
+
+  sched_debug("ISIS (%s): Resigned from L%d - canceling LSP regeneration timer.",
+              area->area_tag, level);
+  THREAD_TIMER_OFF (area->t_lsp_refresh[level - 1]);
+  area->lsp_regenerate_pending[level - 1] = 0;
+}
+
+void
+isis_area_is_type_set(struct isis_area *area, int is_type)
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+
+  if (isis->debugs & DEBUG_EVENTS)
+    zlog_debug ("ISIS-Evt (%s) system type change %s -> %s", area->area_tag,
+              circuit_t2string (area->is_type), circuit_t2string (is_type));
+
+  if (area->is_type == is_type)
+    return;                    /* No change */
+
+  switch (area->is_type)
+  {
+    case IS_LEVEL_1:
+      if (is_type == IS_LEVEL_2)
+        area_resign_level (area, IS_LEVEL_1);
+
+      if (area->lspdb[1] == NULL)
+        area->lspdb[1] = lsp_db_init ();
+      if (area->route_table[1] == NULL)
+        area->route_table[1] = route_table_init ();
+#ifdef HAVE_IPV6
+      if (area->route_table6[1] == NULL)
+        area->route_table6[1] = route_table_init ();
+#endif /* HAVE_IPV6 */
+      break;
+
+    case IS_LEVEL_1_AND_2:
+      if (is_type == IS_LEVEL_1)
+        area_resign_level (area, IS_LEVEL_2);
+      else
+        area_resign_level (area, IS_LEVEL_1);
+      break;
+
+    case IS_LEVEL_2:
+      if (is_type == IS_LEVEL_1)
+        area_resign_level (area, IS_LEVEL_2);
+
+      if (area->lspdb[0] == NULL)
+        area->lspdb[0] = lsp_db_init ();
+      if (area->route_table[0] == NULL)
+        area->route_table[0] = route_table_init ();
+#ifdef HAVE_IPV6
+      if (area->route_table6[0] == NULL)
+        area->route_table6[0] = route_table_init ();
+#endif /* HAVE_IPV6 */
+      break;
+
+    default:
+      break;
+  }
+
+  area->is_type = is_type;
+
+  /* override circuit's is_type */
+  if (area->is_type != IS_LEVEL_1_AND_2)
+  {
+    for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit))
+      isis_circuit_is_type_set (circuit, is_type);
+  }
+
+  spftree_area_init (area);
+
+  if (is_type & IS_LEVEL_1)
+    lsp_generate (area, IS_LEVEL_1);
+  if (is_type & IS_LEVEL_2)
+    lsp_generate (area, IS_LEVEL_2);
+  lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 1);
+
+  return;
+}
+
+void isis_area_metricstyle_set(struct isis_area *area, bool old_metric,
+                              bool new_metric)
+{
+  if (area->oldmetric != old_metric
+      || area->newmetric != new_metric)
+    {
+      area->oldmetric = old_metric;
+      area->newmetric = new_metric;
+      lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
+    }
+}
+
+void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit)
+{
+  char new_overload_bit = overload_bit ? LSPBIT_OL : 0;
+
+  if (new_overload_bit != area->overload_bit)
+    {
+      area->overload_bit = new_overload_bit;
+      lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
+    }
+}
+
+void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit)
+{
+  char new_attached_bit = attached_bit ? LSPBIT_ATT : 0;
+
+  if (new_attached_bit != area->attached_bit)
+    {
+      area->attached_bit = new_attached_bit;
+      lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
+    }
+}
+
+void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname)
+{
+  if (area->dynhostname != dynhostname)
+    {
+      area->dynhostname = dynhostname;
+      lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+    }
+}
+
+void
+isis_area_max_lsp_lifetime_set(struct isis_area *area, int level,
+                              uint16_t max_lsp_lifetime)
+{
+  assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2));
+
+  if (area->max_lsp_lifetime[level-1] == max_lsp_lifetime)
+    return;
+
+  area->max_lsp_lifetime[level-1] = max_lsp_lifetime;
+  lsp_regenerate_schedule(area, level, 1);
+}
+
+void
+isis_area_lsp_refresh_set(struct isis_area *area, int level,
+                         uint16_t lsp_refresh)
+{
+  assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2));
+
+  if (area->lsp_refresh[level-1] == lsp_refresh)
+    return;
+
+  area->lsp_refresh[level-1] = lsp_refresh;
+  lsp_regenerate_schedule(area, level, 1);
+}
+
+DEFUN (log_adj_changes,
+       log_adj_changes_cmd,
+       "log-adjacency-changes",
+       "Log changes in adjacency state\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+
+  area->log_adj_changes = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_log_adj_changes,
+       no_log_adj_changes_cmd,
+       "no log-adjacency-changes",
+       "Stop logging changes in adjacency state\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+
+  area->log_adj_changes = 0;
+
+  return CMD_SUCCESS;
+}
+
+#ifdef TOPOLOGY_GENERATE
+
+DEFUN (topology_generate_grid,
+       topology_generate_grid_cmd,
+       "topology generate grid <1-100> <1-100> <1-65000> [param] [param] "
+       "[param]",
+       "Topology generation for IS-IS\n"
+       "Topology generation\n"
+       "Grid topology\n"
+       "X parameter of the grid\n"
+       "Y parameter of the grid\n"
+       "Random seed\n"
+       "Optional param 1\n"
+       "Optional param 2\n"
+       "Optional param 3\n"
+       "Topology\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+
+  if (!spgrid_check_params (vty, argc, argv))
+    {
+      if (area->topology)
+       list_delete (area->topology);
+      area->topology = list_new ();
+      memcpy (area->top_params, vty->buf, 200);
+      gen_spgrid_topology (vty, area->topology);
+      remove_topology_lsps (area);
+      generate_topology_lsps (area);
+      /* Regenerate L1 LSP to get two way connection to the generated
+       * topology. */
+      lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 1);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_isis_generated_topology,
+       show_isis_generated_topology_cmd,
+       "show isis generated-topologies",
+       SHOW_STR
+       "ISIS network information\n"
+       "Show generated topologies\n")
+{
+  struct isis_area *area;
+  struct listnode *node;
+  struct listnode *node2;
+  struct arc *arc;
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      if (!area->topology)
+       continue;
+
+      vty_out (vty, "Topology for isis area: %s%s", area->area_tag,
+              VTY_NEWLINE);
+      vty_out (vty, "From node     To node     Distance%s", VTY_NEWLINE);
+
+      for (ALL_LIST_ELEMENTS_RO (area->topology, node2, arc))
+       vty_out (vty, "%9ld %11ld %12ld%s", arc->from_node, arc->to_node,
+                arc->distance, VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+/* Base IS for topology generation. */
+DEFUN (topology_baseis,
+       topology_baseis_cmd,
+       "topology base-is WORD",
+       "Topology generation for IS-IS\n"
+       "A Network IS Base for this topology\n"
+       "XXXX.XXXX.XXXX Network entity title (NET)\n")
+{
+  struct isis_area *area;
+  u_char buff[ISIS_SYS_ID_LEN];
+
+  area = vty->index;
+  assert (area);
+
+  if (sysid2buff (buff, argv[0]))
+    sysid2buff (area->topology_baseis, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_topology_baseis,
+       no_topology_baseis_cmd,
+       "no topology base-is WORD",
+       NO_STR
+       "Topology generation for IS-IS\n"
+       "A Network IS Base for this topology\n"
+       "XXXX.XXXX.XXXX Network entity title (NET)\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+
+  memcpy (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_topology_baseis,
+       no_topology_baseis_noid_cmd,
+       "no topology base-is",
+       NO_STR
+       "Topology generation for IS-IS\n"
+       "A Network IS Base for this topology\n")
+
+DEFUN (topology_basedynh,
+       topology_basedynh_cmd,
+       "topology base-dynh WORD",
+       "Topology generation for IS-IS\n"
+       "Dynamic hostname base for this topology\n"
+       "Dynamic hostname base\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+
+  /* I hope that it's enough. */
+  area->topology_basedynh = strndup (argv[0], 16); 
+  return CMD_SUCCESS;
+}
+
+#endif /* TOPOLOGY_GENERATE */
+
+/* IS-IS configuration write function */
+int
+isis_config_write (struct vty *vty)
+{
+  int write = 0;
+
+  if (isis != NULL)
+    {
+      struct isis_area *area;
+      struct listnode *node, *node2;
+
+      for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+      {
+       /* ISIS - Area name */
+       vty_out (vty, "router isis %s%s", area->area_tag, VTY_NEWLINE);
+       write++;
+       /* ISIS - Net */
+       if (listcount (area->area_addrs) > 0)
+         {
+           struct area_addr *area_addr;
+           for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node2, area_addr))
+             {
+               vty_out (vty, " net %s%s",
+                        isonet_print (area_addr->area_addr,
+                                      area_addr->addr_len + ISIS_SYS_ID_LEN +
+                                      1), VTY_NEWLINE);
+               write++;
+             }
+         }
+       /* ISIS - Dynamic hostname - Defaults to true so only display if
+        * false. */
+       if (!area->dynhostname)
+         {
+           vty_out (vty, " no hostname dynamic%s", VTY_NEWLINE);
+           write++;
+         }
+       /* ISIS - Metric-Style - when true displays wide */
+       if (area->newmetric)
+         {
+           if (!area->oldmetric)
+             vty_out (vty, " metric-style wide%s", VTY_NEWLINE);
+           else
+             vty_out (vty, " metric-style transition%s", VTY_NEWLINE);
+           write++;
+         }
+       else
+         {
+           vty_out (vty, " metric-style narrow%s", VTY_NEWLINE);
+           write++;
+         }
+       /* ISIS - overload-bit */
+       if (area->overload_bit)
+         {
+           vty_out (vty, " set-overload-bit%s", VTY_NEWLINE);
+           write++;
+         }
+       /* ISIS - Area is-type (level-1-2 is default) */
+       if (area->is_type == IS_LEVEL_1)
+         {
+           vty_out (vty, " is-type level-1%s", VTY_NEWLINE);
+           write++;
+         }
+       else if (area->is_type == IS_LEVEL_2)
+         {
+           vty_out (vty, " is-type level-2-only%s", VTY_NEWLINE);
+           write++;
+         }
+       write += isis_redist_config_write(vty, area, AF_INET);
+       write += isis_redist_config_write(vty, area, AF_INET6);
+       /* ISIS - Lsp generation interval */
+       if (area->lsp_gen_interval[0] == area->lsp_gen_interval[1])
+         {
+           if (area->lsp_gen_interval[0] != DEFAULT_MIN_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-gen-interval %d%s",
+                        area->lsp_gen_interval[0], VTY_NEWLINE);
+               write++;
+             }
+         }
+       else
+         {
+           if (area->lsp_gen_interval[0] != DEFAULT_MIN_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-gen-interval level-1 %d%s",
+                        area->lsp_gen_interval[0], VTY_NEWLINE);
+               write++;
+             }
+           if (area->lsp_gen_interval[1] != DEFAULT_MIN_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-gen-interval level-2 %d%s",
+                        area->lsp_gen_interval[1], VTY_NEWLINE);
+               write++;
+             }
+         }
+       /* ISIS - LSP lifetime */
+       if (area->max_lsp_lifetime[0] == area->max_lsp_lifetime[1])
+         {
+           if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME)
+             {
+               vty_out (vty, " max-lsp-lifetime %u%s", area->max_lsp_lifetime[0],
+                        VTY_NEWLINE);
+               write++;
+             }
+         }
+       else
+         {
+           if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME)
+             {
+               vty_out (vty, " max-lsp-lifetime level-1 %u%s",
+                        area->max_lsp_lifetime[0], VTY_NEWLINE);
+               write++;
+             }
+           if (area->max_lsp_lifetime[1] != DEFAULT_LSP_LIFETIME)
+             {
+               vty_out (vty, " max-lsp-lifetime level-2 %u%s",
+                        area->max_lsp_lifetime[1], VTY_NEWLINE);
+               write++;
+             }
+         }
+       /* ISIS - LSP refresh interval */
+       if (area->lsp_refresh[0] == area->lsp_refresh[1])
+         {
+           if (area->lsp_refresh[0] != DEFAULT_MAX_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-refresh-interval %u%s", area->lsp_refresh[0],
+                        VTY_NEWLINE);
+               write++;
+             }
+         }
+       else
+         {
+           if (area->lsp_refresh[0] != DEFAULT_MAX_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-refresh-interval level-1 %u%s",
+                        area->lsp_refresh[0], VTY_NEWLINE);
+               write++;
+             }
+           if (area->lsp_refresh[1] != DEFAULT_MAX_LSP_GEN_INTERVAL)
+             {
+               vty_out (vty, " lsp-refresh-interval level-2 %u%s",
+                        area->lsp_refresh[1], VTY_NEWLINE);
+               write++;
+             }
+         }
+       if (area->lsp_mtu != DEFAULT_LSP_MTU)
+         {
+           vty_out(vty, " lsp-mtu %u%s", area->lsp_mtu, VTY_NEWLINE);
+           write++;
+         }
+
+       /* Minimum SPF interval. */
+       if (area->min_spf_interval[0] == area->min_spf_interval[1])
+         {
+           if (area->min_spf_interval[0] != MINIMUM_SPF_INTERVAL)
+             {
+               vty_out (vty, " spf-interval %d%s",
+                        area->min_spf_interval[0], VTY_NEWLINE);
+               write++;
+             }
+         }
+       else
+         {
+           if (area->min_spf_interval[0] != MINIMUM_SPF_INTERVAL)
+             {
+               vty_out (vty, " spf-interval level-1 %d%s",
+                        area->min_spf_interval[0], VTY_NEWLINE);
+               write++;
+             }
+           if (area->min_spf_interval[1] != MINIMUM_SPF_INTERVAL)
+             {
+               vty_out (vty, " spf-interval level-2 %d%s",
+                        area->min_spf_interval[1], VTY_NEWLINE);
+               write++;
+             }
+         }
+       /* Authentication passwords. */
+       if (area->area_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+         {
+           vty_out(vty, " area-password md5 %s", area->area_passwd.passwd);
+           if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND))
+             {
+               vty_out(vty, " authenticate snp ");
+               if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV))
+                 vty_out(vty, "validate");
+               else
+                 vty_out(vty, "send-only");
+             }
+           vty_out(vty, "%s", VTY_NEWLINE);
+           write++; 
+         }
+        else if (area->area_passwd.type == ISIS_PASSWD_TYPE_CLEARTXT)
+          {
+           vty_out(vty, " area-password clear %s", area->area_passwd.passwd);
+           if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND))
+             {
+               vty_out(vty, " authenticate snp ");
+               if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV))
+                 vty_out(vty, "validate");
+               else
+                 vty_out(vty, "send-only");
+             }
+           vty_out(vty, "%s", VTY_NEWLINE);
+           write++; 
+          }
+       if (area->domain_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+         {
+            vty_out(vty, " domain-password md5 %s",
+                    area->domain_passwd.passwd);
+           if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND))
+             {
+               vty_out(vty, " authenticate snp ");
+               if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV))
+                 vty_out(vty, "validate");
+               else
+                 vty_out(vty, "send-only");
+             }
+           vty_out(vty, "%s", VTY_NEWLINE);
+           write++;
+         }
+        else if (area->domain_passwd.type == ISIS_PASSWD_TYPE_CLEARTXT)
+         {
+           vty_out(vty, " domain-password clear %s",
+                    area->domain_passwd.passwd); 
+           if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND))
+             {
+               vty_out(vty, " authenticate snp ");
+               if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV))
+                 vty_out(vty, "validate");
+               else
+                 vty_out(vty, "send-only");
+             }
+           vty_out(vty, "%s", VTY_NEWLINE);
+           write++;
+         }
+
+       if (area->log_adj_changes)
+         {
+           vty_out (vty, " log-adjacency-changes%s", VTY_NEWLINE);
+           write++;
+         }
+
+#ifdef TOPOLOGY_GENERATE
+       if (memcmp (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS,
+                   ISIS_SYS_ID_LEN))
+         {
+           vty_out (vty, " topology base-is %s%s",
+                    sysid_print ((u_char *)area->topology_baseis), VTY_NEWLINE);
+           write++;
+         }
+       if (area->topology_basedynh)
+         {
+           vty_out (vty, " topology base-dynh %s%s",
+                    area->topology_basedynh, VTY_NEWLINE);
+           write++;
+         }
+       /* We save the whole command line here. */
+       if (strlen(area->top_params))
+         {
+           vty_out (vty, " %s%s", area->top_params, VTY_NEWLINE);
+           write++;
+         }
+#endif /* TOPOLOGY_GENERATE */
+
+      }
+    isis_mpls_te_config_write_router(vty);
+    }
+
+  return write;
+}
+
+struct cmd_node isis_node = {
+  ISIS_NODE,
+  "%s(config-router)# ",
+  1
+};
+
+void
+isis_init ()
+{
+  /* Install IS-IS top node */
+  install_node (&isis_node, isis_config_write);
+
+  install_element (VIEW_NODE, &show_isis_summary_cmd);
+
+  install_element (VIEW_NODE, &show_isis_interface_cmd);
+  install_element (VIEW_NODE, &show_isis_interface_detail_cmd);
+  install_element (VIEW_NODE, &show_isis_interface_arg_cmd);
+
+  install_element (VIEW_NODE, &show_isis_neighbor_cmd);
+  install_element (VIEW_NODE, &show_isis_neighbor_detail_cmd);
+  install_element (VIEW_NODE, &show_isis_neighbor_arg_cmd);
+  install_element (VIEW_NODE, &clear_isis_neighbor_cmd);
+  install_element (VIEW_NODE, &clear_isis_neighbor_arg_cmd);
+
+  install_element (VIEW_NODE, &show_hostname_cmd);
+  install_element (VIEW_NODE, &show_database_cmd);
+  install_element (VIEW_NODE, &show_database_arg_cmd);
+  install_element (VIEW_NODE, &show_database_arg_detail_cmd);
+  install_element (VIEW_NODE, &show_database_detail_cmd);
+  install_element (VIEW_NODE, &show_database_detail_arg_cmd);
+
+  install_element (ENABLE_NODE, &show_debugging_isis_cmd);
+
+  install_node (&debug_node, config_write_debug);
+
+  install_element (ENABLE_NODE, &debug_isis_adj_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_adj_cmd);
+  install_element (ENABLE_NODE, &debug_isis_csum_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_csum_cmd);
+  install_element (ENABLE_NODE, &debug_isis_lupd_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_lupd_cmd);
+  install_element (ENABLE_NODE, &debug_isis_err_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_err_cmd);
+  install_element (ENABLE_NODE, &debug_isis_snp_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_snp_cmd);
+  install_element (ENABLE_NODE, &debug_isis_upd_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_upd_cmd);
+  install_element (ENABLE_NODE, &debug_isis_spfevents_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_spfevents_cmd);
+  install_element (ENABLE_NODE, &debug_isis_spfstats_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_spfstats_cmd);
+  install_element (ENABLE_NODE, &debug_isis_spftrigg_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_spftrigg_cmd);
+  install_element (ENABLE_NODE, &debug_isis_rtevents_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_rtevents_cmd);
+  install_element (ENABLE_NODE, &debug_isis_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_events_cmd);
+  install_element (ENABLE_NODE, &debug_isis_packet_dump_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_packet_dump_cmd);
+  install_element (ENABLE_NODE, &debug_isis_lsp_gen_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_lsp_gen_cmd);
+  install_element (ENABLE_NODE, &debug_isis_lsp_sched_cmd);
+  install_element (ENABLE_NODE, &no_debug_isis_lsp_sched_cmd);
+
+  install_element (CONFIG_NODE, &debug_isis_adj_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_adj_cmd);
+  install_element (CONFIG_NODE, &debug_isis_csum_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_csum_cmd);
+  install_element (CONFIG_NODE, &debug_isis_lupd_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_lupd_cmd);
+  install_element (CONFIG_NODE, &debug_isis_err_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_err_cmd);
+  install_element (CONFIG_NODE, &debug_isis_snp_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_snp_cmd);
+  install_element (CONFIG_NODE, &debug_isis_upd_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_upd_cmd);
+  install_element (CONFIG_NODE, &debug_isis_spfevents_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_spfevents_cmd);
+  install_element (CONFIG_NODE, &debug_isis_spfstats_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_spfstats_cmd);
+  install_element (CONFIG_NODE, &debug_isis_spftrigg_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_spftrigg_cmd);
+  install_element (CONFIG_NODE, &debug_isis_rtevents_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_rtevents_cmd);
+  install_element (CONFIG_NODE, &debug_isis_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_events_cmd);
+  install_element (CONFIG_NODE, &debug_isis_packet_dump_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_packet_dump_cmd);
+  install_element (CONFIG_NODE, &debug_isis_lsp_gen_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_lsp_gen_cmd);
+  install_element (CONFIG_NODE, &debug_isis_lsp_sched_cmd);
+  install_element (CONFIG_NODE, &no_debug_isis_lsp_sched_cmd);
+
+  install_element (CONFIG_NODE, &router_isis_cmd);
+  install_element (CONFIG_NODE, &no_router_isis_cmd);
+
+  install_default (ISIS_NODE);
+
+  install_element (ISIS_NODE, &net_cmd);
+  install_element (ISIS_NODE, &no_net_cmd);
+
+  install_element (ISIS_NODE, &log_adj_changes_cmd);
+  install_element (ISIS_NODE, &no_log_adj_changes_cmd);
+
+#ifdef TOPOLOGY_GENERATE
+  install_element (ISIS_NODE, &topology_generate_grid_cmd);
+  install_element (ISIS_NODE, &topology_baseis_cmd);
+  install_element (ISIS_NODE, &topology_basedynh_cmd);
+  install_element (ISIS_NODE, &no_topology_baseis_cmd);
+  install_element (ISIS_NODE, &no_topology_baseis_noid_cmd);
+  install_element (VIEW_NODE, &show_isis_generated_topology_cmd);
+#endif /* TOPOLOGY_GENERATE */
+}
diff --git a/isisd/isisd.conf.sample b/isisd/isisd.conf.sample
new file mode 100644 (file)
index 0000000..47b1595
--- /dev/null
@@ -0,0 +1,39 @@
+! -*- isis -*-
+!
+! ISISd sample configuration file
+!
+hostname isisd 
+password foo
+enable password foo
+log stdout 
+!log file /tmp/isisd.log
+! 
+! 
+router isis DEAD
+  net 47.0023.0000.0003.0300.0100.0102.0304.0506.00
+!  is-type level-1
+
+!  -- set the lifetime either for level-1, level-2 or both
+!  lsp-lifetime level-1 65535
+!  lsp-lifetime level-2 65535
+!  lsp-lifetime 65535
+
+!  hostname isisd-router
+!  area-password foobar
+!  domain-password foobar
+
+interface eth0
+ ip router isis DEAD
+! isis hello-interval 5
+! isis lsp-interval 1000
+
+! -- optional
+! isis circuit-type level-1
+! isis password lallaa level-1 
+! isis metric 1 level-1
+! isis csnp-interval 5 level-1
+! isis retransmit-interval 10
+! isis retransmit-throttle-interval
+! isis hello-multiplier 2 level-1
+! isis priority 64
+! 
diff --git a/isisd/isisd.h b/isisd/isisd.h
new file mode 100644 (file)
index 0000000..eedb451
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * IS-IS Rout(e)ing protocol - isisd.h   
+ *
+ * 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.
+ */
+
+#ifndef ISISD_H
+#define ISISD_H
+
+#define ISISD_VERSION "0.0.7"
+
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_redist.h"
+#include "isis_flags.h"
+#include "dict.h"
+
+/* uncomment if you are a developer in bug hunt */
+/* #define EXTREME_DEBUG  */
+/* #define EXTREME_TLV_DEBUG */
+
+struct isis
+{
+  u_long process_id;
+  int sysid_set;
+  u_char sysid[ISIS_SYS_ID_LEN];       /* SystemID for this IS */
+  u_int32_t router_id;          /* Router ID from zebra */
+  struct list *area_list;      /* list of IS-IS areas */
+  struct list *init_circ_list;
+  struct list *nexthops;       /* IPv4 next hops from this IS */
+#ifdef HAVE_IPV6
+  struct list *nexthops6;      /* IPv6 next hops from this IS */
+#endif                         /* HAVE_IPV6 */
+  u_char max_area_addrs;       /* maximumAreaAdresses */
+  struct area_addr *man_area_addrs;    /* manualAreaAddresses */
+  u_int32_t debugs;            /* bitmap for debug */
+  time_t uptime;               /* when did we start */
+  struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */
+
+  struct route_table *ext_info[REDIST_PROTOCOL_COUNT];
+};
+
+extern struct isis *isis;
+
+struct isis_area
+{
+  struct isis *isis;                             /* back pointer */
+  dict_t *lspdb[ISIS_LEVELS];                    /* link-state dbs */
+  struct isis_spftree *spftree[ISIS_LEVELS];     /* The v4 SPTs */
+  struct route_table *route_table[ISIS_LEVELS];          /* IPv4 routes */
+#ifdef HAVE_IPV6
+  struct isis_spftree *spftree6[ISIS_LEVELS];    /* The v6 SPTs */
+  struct route_table *route_table6[ISIS_LEVELS];  /* IPv6 routes */
+#endif
+#define DEFAULT_LSP_MTU 1497
+  unsigned int lsp_mtu;                                  /* Size of LSPs to generate */
+  struct list *circuit_list;   /* IS-IS circuits */
+  struct flags flags;
+  struct thread *t_tick;       /* LSP walker */
+  struct thread *t_lsp_refresh[ISIS_LEVELS];
+  /* t_lsp_refresh is used in two ways:
+   * a) regular refresh of LSPs
+   * b) (possibly throttled) updates to LSPs
+   *
+   * The lsp_regenerate_pending flag tracks whether the timer is active
+   * for the a) or the b) case.
+   *
+   * It is of utmost importance to clear this flag when the timer is
+   * rescheduled for normal refresh, because otherwise, updates will
+   * be delayed until the next regular refresh.
+   */
+  int lsp_regenerate_pending[ISIS_LEVELS];
+
+  /*
+   * Configurables 
+   */
+  struct isis_passwd area_passwd;
+  struct isis_passwd domain_passwd;
+  /* do we support dynamic hostnames?  */
+  char dynhostname;
+  /* do we support new style metrics?  */
+  char newmetric;
+  char oldmetric;
+  /* identifies the routing instance   */
+  char *area_tag;
+  /* area addresses for this area      */
+  struct list *area_addrs;
+  u_int16_t max_lsp_lifetime[ISIS_LEVELS];
+  char is_type;                        /* level-1 level-1-2 or level-2-only */
+  /* are we overloaded? */
+  char overload_bit;
+  /* L1/L2 router identifier for inter-area traffic */
+  char attached_bit;
+  u_int16_t lsp_refresh[ISIS_LEVELS];
+  /* minimum time allowed before lsp retransmission */
+  u_int16_t lsp_gen_interval[ISIS_LEVELS];
+  /* min interval between between consequtive SPFs */
+  u_int16_t min_spf_interval[ISIS_LEVELS];
+  /* the percentage of LSP mtu size used, before generating a new frag */
+  int lsp_frag_threshold;
+  int ip_circuits;
+  /* logging adjacency changes? */
+  u_char log_adj_changes;
+#ifdef HAVE_IPV6
+  int ipv6_circuits;
+#endif                         /* HAVE_IPV6 */
+  /* Counters */
+  u_int32_t circuit_state_changes;
+  struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT]
+                                    [ZEBRA_ROUTE_MAX + 1][ISIS_LEVELS];
+  struct route_table *ext_reach[REDIST_PROTOCOL_COUNT][ISIS_LEVELS];
+
+#ifdef TOPOLOGY_GENERATE
+  struct list *topology;
+  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 *);
+int isis_area_get (struct vty *vty, const char *area_tag);
+void print_debug(struct vty *, int, int);
+
+void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit);
+void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit);
+void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname);
+void isis_area_metricstyle_set(struct isis_area *area, bool old_metric,
+                              bool new_metric);
+void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu);
+void isis_area_is_type_set(struct isis_area *area, int is_type);
+void isis_area_max_lsp_lifetime_set(struct isis_area *area, int level,
+                                   uint16_t max_lsp_lifetime);
+void isis_area_lsp_refresh_set(struct isis_area *area, int level,
+                              uint16_t lsp_refresh);
+/* IS_LEVEL_1 sets area_passwd, IS_LEVEL_2 domain_passwd */
+int isis_area_passwd_unset (struct isis_area *area, int level);
+int isis_area_passwd_cleartext_set (struct isis_area *area, int level,
+                                    const char *passwd, u_char snp_auth);
+int isis_area_passwd_hmac_md5_set (struct isis_area *area, int level,
+                                   const char *passwd, u_char snp_auth);
+void isis_vty_init (void);
+
+/* Master of threads. */
+extern struct thread_master *master;
+
+#define DEBUG_ADJ_PACKETS                (1<<0)
+#define DEBUG_CHECKSUM_ERRORS            (1<<1)
+#define DEBUG_LOCAL_UPDATES              (1<<2)
+#define DEBUG_PROTOCOL_ERRORS            (1<<3)
+#define DEBUG_SNP_PACKETS                (1<<4)
+#define DEBUG_UPDATE_PACKETS             (1<<5)
+#define DEBUG_SPF_EVENTS                 (1<<6)
+#define DEBUG_SPF_STATS                  (1<<7)
+#define DEBUG_SPF_TRIGGERS               (1<<8)
+#define DEBUG_RTE_EVENTS                 (1<<9)
+#define DEBUG_EVENTS                     (1<<10)
+#define DEBUG_ZEBRA                      (1<<11)
+#define DEBUG_PACKET_DUMP                (1<<12)
+#define DEBUG_LSP_GEN                    (1<<13)
+#define DEBUG_LSP_SCHED                  (1<<14)
+
+#define lsp_debug(...) \
+  do \
+    { \
+      if (isis->debugs & DEBUG_LSP_GEN) \
+        zlog_debug(__VA_ARGS__); \
+    } \
+  while (0)
+
+#define sched_debug(...) \
+  do \
+    { \
+      if (isis->debugs & DEBUG_LSP_SCHED) \
+        zlog_debug(__VA_ARGS__); \
+    } \
+  while (0)
+
+#define DEBUG_TE                         (1<<13)
+
+#define IS_DEBUG_ISIS(x)                 (isis->debugs & x)
+
+#endif /* ISISD_H */
diff --git a/isisd/iso_checksum.c b/isisd/iso_checksum.c
new file mode 100644 (file)
index 0000000..294fe99
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * IS-IS Rout(e)ing protocol - iso_checksum.c
+ *                             ISO checksum related routines
+ *
+ * Copyright (C) 2001,2002   Sampo Saaristo
+ *                           Tampere University of Technology      
+ *                           Institute of Communications Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public Licenseas published by the Free 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "iso_checksum.h"
+#include "checksum.h"
+
+/*
+ * Calculations of the OSI checksum.
+ * ISO/IEC 8473 defines the sum as
+ *
+ *     L
+ *  sum  a (mod 255) = 0
+ *     1  i
+ *
+ *     L 
+ *  sum (L-i+1)a (mod 255) = 0
+ *     1        i
+ *
+ */
+
+/*
+ * Verifies that the checksum is correct.
+ * Return 0 on correct and 1 on invalid checksum.
+ * Based on Annex C.4 of ISO/IEC 8473
+ */
+
+int
+iso_csum_verify (u_char * buffer, int len, uint16_t * csum)
+{
+  u_int16_t checksum;
+  u_int32_t c0;
+  u_int32_t c1;
+
+  c0 = *csum & 0xff00;
+  c1 = *csum & 0x00ff;
+
+  /*
+   * If both are zero return correct
+   */
+  if (c0 == 0 && c1 == 0)
+    return 0;
+
+  /*
+   * If either, but not both are zero return incorrect
+   */
+  if (c0 == 0 || c1 == 0)
+    return 1;
+
+  /* Offset of checksum from the start of the buffer */
+  int offset = (u_char *) csum - buffer;
+
+  checksum = fletcher_checksum(buffer, len, offset);
+  if (checksum == *csum)
+    return 0;
+  return 1;
+}
diff --git a/isisd/iso_checksum.h b/isisd/iso_checksum.h
new file mode 100644 (file)
index 0000000..5f8d41f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * IS-IS Rout(e)ing protocol - iso_checksum.c
+ *                             ISO checksum related routines
+ *
+ * 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.
+ */
+#ifndef _ZEBRA_ISO_CSUM_H
+#define _ZEBRA_ISO_CSUM_H
+
+int iso_csum_verify (u_char * buffer, int len, uint16_t * csum);
+
+#endif /* _ZEBRA_ISO_CSUM_H */
diff --git a/isisd/topology/.gitignore b/isisd/topology/.gitignore
new file mode 100644 (file)
index 0000000..dd75c27
--- /dev/null
@@ -0,0 +1,12 @@
+Makefile
+Makefile.in
+*.o
+tags
+TAGS
+.deps
+.nfs*
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/isisd/topology/Makefile.am b/isisd/topology/Makefile.am
new file mode 100644 (file)
index 0000000..fe73ae5
--- /dev/null
@@ -0,0 +1,21 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+
+AM_CFLAGS = $(PICFLAGS)
+AM_LDFLAGS = $(PILDFLAGS)
+
+noinst_LIBRARIES = libtopology.a
+
+libtopology_a_SOURCES = \
+       spgrid.c random.c
+
+libtopology_a_DEPENDENCIES = @LIB_REGEX@
+
+libtopology_a_LIBADD = @LIB_REGEX@ ../../lib/libzebra.la
+
+noinst_HEADERS = \
+       spgrid.h
+
+## File dependency.
diff --git a/isisd/topology/random.c b/isisd/topology/random.c
new file mode 100644 (file)
index 0000000..2c457c5
--- /dev/null
@@ -0,0 +1,166 @@
+/*********************************************************************/
+/*                                                                   */
+/* current processor time in seconds                                 */
+/* difference between two calls is processor time spent by your code */
+/* needs: <sys/types.h>, <sys/times.h>                               */
+/* depends on compiler and OS                                        */
+/*                                                                   */
+/*********************************************************************/
+
+#include <zebra.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+/*
+ * Prototypes.
+ */
+unsigned long timer(void);
+void init_rand(long);
+double rand01(void);
+double randg01(void);
+long nrand(long);
+void free_arc(void *);
+
+unsigned long timer ()
+   { struct tms hold;
+
+        times(&hold);
+        return (unsigned long) ((float) (hold.tms_utime) / 60.0);
+   }
+
+
+/*********************************************************************/
+/*                                                                   */
+/*            Family of random number generators                     */
+/*                                                                   */
+/*  Initialisation:                                                  */
+/*            void init_rand ( seed );                               */
+/*                 long seed     - any positive number               */
+/*                                 if seed<=0 init_rand takes time   */
+/*                                 from timer instead of seed        */
+/*                                                                   */
+/*  Whole number uniformly distributed on [0,n):                     */
+/*            long nrand (n);                                        */
+/*                 long n                                            */
+/*                                                                   */
+/*  Real number uniformly distributed on [0,1]                       */
+/*            double rand01();                                       */
+/*                                                                   */
+/*  Real number with Gauss(0,1) disitribution:                       */
+/*            double randg01();                                      */
+/*                                                                   */
+/*  Algorithm:                                                       */
+/*            x(n+1) = (x(n) * 5^13) mod 2^31                        */
+/*                                                                   */
+/*********************************************************************/
+
+unsigned long internal_seed;
+
+void init_rand ( init_seed )
+
+long init_seed;
+
+{ internal_seed = ( init_seed > 0 )
+                 ? (unsigned long) init_seed
+                 : (unsigned long) timer(); 
+                
+
+  /* only odd numbers are acceptable */
+  if ( internal_seed % 2 == 0 ) internal_seed --;
+}
+
+/*********************************************************************/
+/*                                                                   */
+/* Internal function  irand  may depend on OS and compiler           */
+/*                                                                   */
+/* irand assumption:                                                 */
+/* unsigned long i,j;                                                */
+/*   if i*j > max(unsigned long)                                     */
+/*           1. No overflow interruption                             */
+/*           2. i*j = i*j mod max(unsigned long)                     */
+/*                                                                   */
+/* This assumption is true for a lot of computers.                   */
+/* If your computer fails:                                           */
+/*   rename: irand <---> xrand                                       */
+/*                                                                   */
+/*********************************************************************/
+#define  A   1220703125
+#define  B   2147483647
+#define  BF  2147483647.
+
+static long irand ()
+
+{ internal_seed = ( internal_seed * A ) & B;
+  return (long) internal_seed ;
+}
+
+#if 0 /* Not used. */
+/*********************************************************************/
+/*                                                                   */
+/*              computer independent variant of  irand               */
+/*                                                                   */
+/*********************************************************************/
+
+
+#define T15  32768 
+#define T16  65536
+#define A1   37252
+#define A2   29589
+
+static long xrand()
+
+{ unsigned long is1, is2;
+
+  is1 = internal_seed / T15;
+  is2 = internal_seed % T15;
+
+  internal_seed = ( (((is2 * A1) + (is1 * A2))% T16 )* T15 + (is2 * A2) ) & B;
+  return (long) ( internal_seed ) ;
+}
+#endif
+
+/*********************************************************************/
+
+
+double rand01()
+
+{ return  (double) (irand() / BF) ;
+}
+  
+/*********************************************************************/
+
+#define NK  12
+
+double randg01()
+
+{ int i;
+  double sum = 0;
+
+  for ( i = 0; i < NK; i++ ) sum += rand01();
+  return sum - 6.;
+
+  /* if   NK != 12  then you must return (12/NK)*sum - (NK/2) */
+}
+
+#undef NK
+     
+  
+/*********************************************************************/
+
+long nrand ( n )
+
+long n;
+
+{ return (long) ( rand01() * (double) n );
+}
+
+/*********************************************************************/
+
+#undef A
+#undef A1
+#undef A2
+#undef B
+#undef BF
+#undef T15
+#undef T16
diff --git a/isisd/topology/spacyc.c b/isisd/topology/spacyc.c
new file mode 100644 (file)
index 0000000..91a4799
--- /dev/null
@@ -0,0 +1,484 @@
+#include <zebra.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <values.h>
+
+#include "random.c"
+
+#define DASH '-'
+#define VERY_FAR 100000000
+
+
+/* generator of acyclic random networks for the shortest paths problem;
+   extended DIMACS format for output */
+
+main ( argc, argv )
+
+int argc;
+char* argv[];
+
+{
+
+char   args[30];
+
+long   n,
+       n0,
+       source,
+       i,
+       i0,
+       j,
+       dij;
+
+long   m,
+       m0,
+       mc,
+       k;
+
+long   *p,
+       p_t,
+       l,
+       lx;
+
+long   seed,
+       seed1,
+       seed2;
+
+int    ext=0;
+
+FILE   *fout;
+
+/* variables for lengths generating */
+/* initialized by default values */
+int    l_f = 0, ll_f = 0, lm_f = 0, ln_f = 0, ls_f = 0;
+long   ll = 10000,    /* upper bound of the interval */
+       lm = 0;        /* lower bound of the interval */
+double ln = 0,        /* l += ln * |i-j| */ 
+       ls = 0;        /* l += ls * |i-j|^2 */
+
+/* variables for connecting path(s) */
+int    c_f = 0, cl_f = 0, ch_f = 0, c_rand = 1;
+long   cl = 1;        /* length of path arc */
+long   ch;            /* number of arcs in the path
+                         n - by default */
+
+/* variables for artifical source */
+int    s_f = 0, sl_f = 0, sm_f = 0;
+long   sl   = VERY_FAR, /* upper bound of artifical arc */
+       sm,              /* lower bound of artifical arc */
+       s;  
+
+/* variables for potentials */
+int    p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0,
+       pa_f = 0, pap_f = 0, pac_f = 0;
+long   pl,            /* upper bound of the interval */
+       pm;            /* lower bound of the interval */
+double pn = 0,        /* l += ln * |i-j| */ 
+       ps = 0,        /* l += ls * |i-j|^2 */
+       pap = 0,       /* part of nodes with alternative dustribution */
+       pac = -1;      /* multiplier for alternative distribution */
+
+int np;               /* number of parameter parsing now */
+
+#define PRINT_ARC( i, j, length )\
+{\
+l = length;\
+if ( p_f ) l += ( p[i] - p[j] );\
+printf ("a %8ld %8ld %12ld\n", i, j, l );\
+}
+
+  /* parsing  parameters */
+
+if ( argc < 2 ) goto usage;
+
+np = 0;
+
+strcpy ( args, argv[1] );
+
+  if ( ( args[0] == DASH ) && ( args[1] == 'h')
+     )
+      goto help;
+
+if ( argc < 4 ) goto usage;
+
+/* first parameter - number of nodes */
+np = 1;
+if ( ( n = atoi ( argv[1] ) )  <  2  )  goto usage;
+
+/* second parameter - number of arcs */
+np = 2;
+if ( ( m = atoi ( argv[2] ) )  <  n  )  goto usage;
+
+/* third parameter - seed */
+np=3;
+if ( ( seed = atoi ( argv[3] ) )  <=  0  )  goto usage;
+
+/* other parameters */
+
+for ( np = 4; np < argc; np ++ )
+  {
+    strcpy ( args, argv[np] );
+    if ( args[0] != DASH ) goto usage;
+
+    switch ( args[1] )
+      {
+
+      case 'l' : /* an interval for arc length */
+       l_f = 1;
+       switch ( args[2] )
+         { 
+         case 'l': /* length of the interval */
+           ll_f = 1;
+           ll  =  (long) atof ( &args[3] );
+           break;
+         case 'm': /* minimal bound */
+           lm_f = 1;
+           lm  = (long ) atof ( &args[3] );
+           break;
+         case 'n': /* additional length: l*|i-j| */
+           ln_f = 1;
+           ln  = atof ( &args[3] );
+           break;
+         case 's': /* additional length: l*|i-j|^2 */
+           ls_f = 1;
+           ls  = atof ( &args[3] );
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+         }
+       break;
+
+      case 'c' : /* connecting path(s) */
+        c_f = 1;
+       switch ( args[2] )
+         { 
+         case 'l': /* length of path arc */
+            c_rand = 0; /* fixed arc length */
+           cl_f = 1;
+           cl  =  (long) atof ( &args[3] );
+            break;
+         case 'h': /* number of arcs in connecting path */
+           ch_f = 1;
+           ch  =  (long) atof ( &args[3] );
+            if ( ch < 1 || ch > n ) goto usage;
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+          }
+       break;
+
+      case 's' : /* additional source */
+        s_f = 1;
+       if ( strlen ( args ) > 2 )
+       {  
+       switch ( args[2] )
+         { 
+         case 'l': /* upper bound of art. arc */
+           sl_f = 1;
+           sl  =  (long) atof ( &args[3] );
+            break;
+         case 'm': /* lower bound of art. arc */
+           sm_f = 1;
+           sm  =  (long) atof ( &args[3] );
+            break;
+         default:  /* unknown switch  value */
+           goto usage;
+          }
+         }
+       break;
+
+      case 'p' : /* potentials */
+       p_f = 1;
+       if ( strlen ( args ) > 2 )
+       {  
+       switch ( args[2] )
+         { 
+         case 'l': /* length of the interval */
+           pl_f = 1;
+           pl  =  (long) atof ( &args[3] );
+           break;
+         case 'm': /* minimal bound */
+           pm_f = 1;
+           pm  = (long ) atof ( &args[3] );
+           break;
+         case 'n': /* additional length: l*|i-j| */
+           pn_f = 1;
+           pn  = atof ( &args[3] );
+           break;
+         case 's': /* additional length: l*|i-j|^2 */
+           ps_f = 1;
+           ps  = atof ( &args[3] );
+           break;
+         case 'a': /* bipolar distribution */
+           pa_f = 1;
+           switch ( args[3] )
+             {
+             case 'p': /* % of alternative potentials */
+                pap_f = 1;
+                pap  = atof ( &args[4] );
+               if ( pap < 0   ) pap = 0;
+               if ( pap > 100 ) pap = 100;
+                pap /= 100;
+               break;
+             case 'c': /* multiplier */
+               pac_f = 1;
+               pac = atof ( &args[4] );
+               break;
+             default: /* unknown switch value */
+               goto usage;
+             }
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+         }
+      }
+       break;
+
+      default  : /* unknoun case */
+       goto usage;
+      }
+  }
+   
+/* ----- ajusting parameters ----- */
+
+n0 = n; m0 = m;
+
+/* length parameters */
+if ( ll < lm ) { lx = ll; ll = lm; lm = lx; }
+
+/* potential parameters */
+if ( p_f )
+  {
+   if ( ! pl_f ) pl = ll;
+   if ( ! pm_f ) pm = lm;
+   if ( pl < pm ) { lx = pl; pl = pm; pm = lx; }
+  }
+
+/* path(s) parameters */
+if ( ! ch_f ) ch = n - 1;
+mc = n - 1;
+
+ /* artifical source parameters */
+if ( s_f )          
+   { m0 += n; n0 ++ ; 
+     if ( ! sm_f ) sm = sl;
+     if ( sl < sm ) { lx = sl; sl = sm; sm = lx; }
+   }
+
+/*----- printing title -----*/
+
+printf ("c acyclic network for shortest paths problem\n");
+printf ("c extended DIMACS format\nc\n" );
+
+
+/* name of the problem */
+printf ("t ac_%ld_%ld_%ld_", n, m, seed );
+if ( l_f )
+  printf ("%c", 'l');
+if ( c_f )
+  printf ("%c", 'c');
+if ( s_f )
+  printf ("%c", 's');
+if ( p_f )
+  printf ("%c", 'p');
+printf ("\nc\n");
+
+/* printing additional information  */
+if ( l_f )
+  printf ("c length -> min: %ld max: %ld k1: %.2f k2: %.2f\n",
+           lm, ll, ln, ls );
+if ( c_f )
+  printf ("c path(s) -> number of arcs: %ld arc length: %ld\n",
+           ch, cl );
+if ( s_f )
+  printf ("c length of arcs from artifical source -> min: %ld max: %ld\n",
+           sm, sl );
+if ( p_f )
+  {
+  printf ("c potentials -> min: %ld max: %ld k1: %.2f k2: %.2f\n",
+         pm, pl, pn, ps );
+  if ( pa_f )
+  printf ("c potentials -> part of alternative distribution: %.2f k: %.2f\n",
+          pap, pac );
+  }
+printf ("c\n" );
+
+printf ("p sp %8ld %8ld\nc\n", n0, m0 );
+
+source = ( s_f ) ? n0 : 1;
+printf ("n %8ld\nc\n", source );
+
+
+if ( p_f ) /* generating potentials */
+  {
+    seed1 = 2*seed + 1;
+    p = (long*) calloc ( n+2, sizeof (long) );
+    init_rand ( seed1);
+    pl = pl - pm + 1;
+
+    for ( i = 0; i <= n; i ++ )
+      {
+       p_t = pm + nrand ( pl );
+       if ( pn_f ) p_t += (long) ( i * pn );
+       if ( ps_f ) p_t += (long) ( i * ( i * ps ));
+       if ( pap_f )
+           if ( rand01() < pap )
+               p_t = (long) ( p_t * pac );
+        p[i] = p_t;
+      }
+    p[n+1] = 0;
+  }
+
+
+if ( s_f ) /* additional arcs from artifical source */
+  {
+    seed2 = 3*seed + 1;
+    init_rand ( seed2 );
+    sl = sl - sm + 1;
+
+    for ( i = n; i > 1; i -- )
+      {
+       s = sm + nrand ( sl );
+       PRINT_ARC ( n0, i, s ) 
+      }
+
+    PRINT_ARC ( n0, 1, 0 )
+  }
+
+/* initialize random number generator */
+init_rand ( seed );
+ll = ll - lm + 1;
+
+/* generating connecting path(s) */
+for ( i = 1; i < n; i ++ )
+  {
+    if ( ( (i-1) % ch ) != 0 )
+      i0 = i;
+    else
+      i0 = 1;
+
+      if (c_rand)
+        cl = lm + nrand(ll);
+      PRINT_ARC ( i0, i+1, cl )
+  }
+
+/* generating random arcs */
+
+
+for ( k = 1; k <= m - mc; k ++ )
+  {
+    i = 1 + nrand ( n );
+
+    do
+    j = 1 + nrand ( n );
+    while ( j == i );
+
+    if ( i > j )
+      { i0 = i; i = j; j = i0; }
+
+    dij = j - i;
+    l = lm + nrand ( ll );
+    if ( ln_f ) l += (long) ( dij * ln );
+    if ( ls_f ) l += (long) ( dij * ( dij * ls ) );
+    PRINT_ARC ( i, j, l );
+  }
+
+/* all is done */
+exit (ext);
+
+/* ----- wrong usage ----- */
+
+ usage:
+fprintf ( stderr,
+"\nusage: %s  n  m  seed  [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\
+help:  %s -h\n\n", argv[0], argv[0] );
+
+if ( np > 0 )
+  fprintf ( stderr, "error in parameter # %d\n\n", np );   
+exit (4);
+
+/* ---- help ---- */
+
+ help:
+
+if ( args[2] == 'h') goto hhelp;
+
+fprintf ( stderr, 
+"\n'%s' - acyclic network generator for shortest paths problem.\n\
+Generates problems in extended DIMACS format.\n\
+\n\
+   %s  n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\
+   %s -hh\n\
+\n\
+                        #i - integer number   #f - real number\n\
+\n\
+-ll#i  - #i is the upper bound on arc lengths          (default 10000)\n\
+-lm#i  - #i is the lower bound on arc lengths          (default 0)\n\
+-cl#i  - #i is length of arcs in connecting path(s)    (default random)\n\
+-p     - generate potentials \n\
+-pl#i  - #i is the upper bound on potentials           (default ll)\n\
+-pm#i  - #i is the lower bound on potentials           (default lm)\n\
+\n\
+-hh    - extended help \n\n",
+argv[0], argv[0], argv[0] );
+
+exit (0);
+
+/* --------- sophisticated help ------------ */
+ hhelp:
+
+if ( argc < 3 )
+     fout = stderr;
+else
+     fout = fopen ( argv[2], "w" );
+
+if ( fout == NULL )
+{ fprintf ( stderr, "\nCan't open file  '%s' for writing help\n\n", argv[2] );
+  exit ( 2 );
+}
+
+fprintf (fout, 
+"\n'%s' - acyclic network generator for shortest paths problem.\n\
+Generates problems in extended DIMACS format.\n\
+\n\
+   %s  n m seed [ -ll#i -lm#i -ln#f -ls#f\n\
+                      -p  -pl#i -pm#i -pn#f -ps#f -pap#i -pac#f\n\
+                      -cl#i -ch#i\n\
+                      -s  -sl#i -sm#i\n\
+                    ]\n\
+   %s -hh file_name\n\
+\n\
+                        #i - integer number   #f - real number\n\
+\n\
+      Arc length parameters:\n\
+-ll#i  - #i is the upper bound on arc lengths          (default 10000)\n\
+-lm#i  - #i is the lower bound on arc lengths          (default 0)\n\
+-ln#f  - multipliy l(i, j) by #f * |i-j|               (default 0)\n\
+-ls#f  - multipliy l(i, j) by #f * |i-j|^2             (default 0)\n\
+\n\
+      Potential parameters:\n\
+-p     - generate potentials \n\
+-pl#i  - #i is the upper bound on potentials           (default ll)\n\
+-pm#i  - #i is the lower bound on potentials           (default lm)\n\
+-pn#f  - multiply p(i) by #f * i                       (default 0)\n\
+-ps#f  - multiply p(i) by #f * i^2                     (default 0)\n\
+-pap#i - percentage of alternative potential nodes     (default 0)\n\
+-pac#f - if i is alternative, multiply  p(i) by #f     (default -1)\n\
+\n\
+      Connecting path(s) parameters:\n\
+-cl#i  - #i is length of arcs in connecting path(s)   (default random)\n\
+-ch#i  - #i is length of connecting path(s)           (default n-1)\n\
+\n\
+      Artificial source parameters:\n\
+-s     - generate artificial source with default connecting arc lengths\n\
+-sl#i  - #i is the upper bound on art. arc lengths    (default 100000000)\n\
+-sm#i  - #i is the lower bound on art. arc lengths    (default sl)\n\
+\n\
+-hh file_name  - save this help in the file 'file_name'\n\n",
+argv[0], argv[0], argv[0] );
+
+exit (0);
+}
+
+
+
diff --git a/isisd/topology/spgrid.c b/isisd/topology/spgrid.c
new file mode 100644 (file)
index 0000000..e1c87ab
--- /dev/null
@@ -0,0 +1,738 @@
+#include <zebra.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "random.c"
+
+#include "thread.h"
+#include "vty.h"
+#include "log.h"
+#include "linklist.h"
+
+#include "spgrid.h"
+
+
+#define DASH '-'
+#define VERY_FAR 100000000
+
+#define DOUBLE_CYCLE   0
+#define CYCLE          1
+#define PATH           2
+
+#define NO             0
+#define YES            1
+
+#define NODE( x, y ) (x*Y + y + 1)
+
+/*
+ * Prototypes.
+ */
+void free_arc(void *);
+void help(struct vty *);
+void print_arc(struct vty *, struct list *, long, long, long);
+void hhelp(struct vty *);
+void usage(struct vty *);
+
+const char   *graph_type[] =  {
+  "double cycle",
+  "cycle",
+  "path"
+};
+
+struct arc *arc;
+
+char   args[30];
+
+long   X,   /* horizontal size of grid */
+       Y;   /* vertical size of grid */
+
+long   x,
+       y,
+       yy1, yy2, yyp,
+       dl, dx, xn, yyn, count,
+       *mess;
+
+double n;
+long   n0,
+       source,
+       i,
+       i0,
+       j,
+       dij;
+
+double m;
+long   m0,
+       mc,
+       k;
+
+long   *p,
+       p_t,
+       l,
+       lx;
+
+long   seed,
+       seed1,
+       seed2;
+
+int    ext=0;
+
+/* initialized by default values */
+
+/* variables for generating one layer */
+
+/* variables for generating spanning graph */
+int    c_f = 0, cw_f = 0, cm_f = 0, cl_f = 0;
+
+int    cw = DOUBLE_CYCLE;  /* type of spanning graph */
+long   cm = 0,             /* lower bound of the interval */
+       cl = 100;           /* upper bound of the interval */
+
+/* variables for generating additional arcs */
+int    a_f = 0, ax_f = 0, am_f = 0, al_f = 0;
+
+long   ax = 0,             /* number of additional arcs */
+       am = 0,             /* lower bound of the interval */
+       al = 100;           /* upper bound of the interval */
+
+/* variables for inter-layer arcs */
+int    i_f = 0, ip_f = 0, ix_f = 0, ih_f = 0,
+       im_f = 0, il_f = 0, in_f = 0, is_f = 0;
+
+int    ip = NO;       /* to mess or not to mess */
+long   ix = 1,        /* number of interlayered arcs in a NODE */
+       ih = 1,        /* step between two layeres */
+       il = 10000,    /* upper bound of the interval */
+       im = 1000;     /* lower bound of the interval */
+double in = 1,        /* l *=  in * |x1-x2| */
+       is = 0;        /* l *=  is * |x1-x2|^2 */
+
+/* variables for artifical source */
+int    s_f = 0, sl_f = 0, sm_f = 0;
+long   sl   = VERY_FAR, /* upper bound of artifical arc */
+       sm,              /* lower bound of artifical arc */
+       s;
+
+/* variables for potentials */
+int    p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0;
+
+long   pl,            /* upper bound of the interval */
+       pm;            /* lower bound of the interval */
+double pn = 0,        /* p +=  ln * (x+1) */
+       ps = 0;        /* p +=  ls * (x+1)^2 */
+
+int np;               /* number of parameter parsing now */
+
+
+void
+free_arc   (void *val) {
+  free(val);
+}
+
+void
+print_arc (struct vty *vty, struct list *topology, long i, long j, long length)
+{
+  struct arc *myarc;
+
+  l = length;
+  if ( p_f ) l += ( p[i] - p[j] );
+//  vty_out (vty,"a %8ld %8ld %12ld%s", i, j, l ,VTY_NEWLINE);
+  myarc = malloc (sizeof(struct arc));
+  myarc->from_node = i;
+  myarc->to_node = j;
+  myarc->distance = l;
+  topology->del = free_arc;
+  listnode_add (topology, myarc);
+}
+
+/* ---- help ---- */
+void
+help (struct vty *vty) {
+//  if ( args[2] == 'h') hhelp (vty);
+  vty_out (vty,"grid network generator for shortest paths problem.%s",VTY_NEWLINE);
+  vty_out (vty,"Generates problems in extended DIMACS format.%s",VTY_NEWLINE);
+  vty_out (vty,"X Y seed [ -cl#i -cm#i -c{c|d|p} -ip -il#i -im#i -p -pl#i -pm#i... ]%s",VTY_NEWLINE);
+  vty_out (vty,"#i - integer number%s",VTY_NEWLINE);
+  vty_out (vty,"-cl#i - #i is the upper bound on layer arc lengths    (default 100)%s",VTY_NEWLINE);
+  vty_out (vty,"-cm#i - #i is the lower bound on layer arc lengths    (default 0)%s",VTY_NEWLINE);
+  vty_out (vty,"-c#t  - #t is the type of connecting graph: { c | d | p }%s",VTY_NEWLINE);
+  vty_out (vty,"           c - cycle, d - double cycle, p - path      (default d)%s",VTY_NEWLINE);
+  vty_out (vty,"-ip   - shuffle inter-layer arcs                     (default NO)%s",VTY_NEWLINE);
+  vty_out (vty,"-il#i - #i is the upper bound on inter-layer arc lengths (default 10000)%s",VTY_NEWLINE);
+  vty_out (vty,"-im#i - #i is the lower bound on inter-layer arc lengths (default 1000)%s",VTY_NEWLINE);
+  vty_out (vty,"-p    - generate potentials%s",VTY_NEWLINE);
+  vty_out (vty,"-pl#i - #i is the upper bound on potentials           (default il)%s",VTY_NEWLINE);
+  vty_out (vty,"-pm#i - #i is the lower bound on potentials           (default im)%s",VTY_NEWLINE);
+  vty_out (vty,"%s",VTY_NEWLINE);
+  vty_out (vty,"-hh    - extended help%s",VTY_NEWLINE);
+}
+
+/* --------- sophisticated help ------------ */
+void
+hhelp (struct vty *vty) {
+/*
+zlog_info (
+"\n'%s' - grid network generator for shortest paths problem.\n\
+Generates problems in extended DIMACS format.\n\
+\n\
+   %s  X Y seed [ -cl#i -cm#i -c{c|d|p}\n\
+                      -ax#i -al#i -am#i\n\
+                      -ip   -il#i -im#i -in#i -is#i -ix#i -ih#i\n\
+                      -p    -pl#i -pm#i -pn#f -ps#f\n\
+                      -s    -sl#i -sm#i\n\
+                    ]\n\
+   %s -hh file_name\n\
+\n\
+                        #i - integer number   #f - real number\n\
+\n\
+      Parameters of connecting arcs within one layer:\n\
+-cl#i - #i is the upper bound on arc lengths          (default 100)\n\
+-cm#i - #i is the lower bound on arc lengths          (default 0)\n\
+-c#t  - #t is the type of connecting graph: { c | d | p }\n\
+           c - cycle, d - double cycle, p - path      (default d)\n\
+\n\
+      Parameters of additional arcs within one layer:\n\
+-ax#i - #i is the number of additional arcs           (default 0)\n\
+-al#i - #i is the upper bound on arc lengths          (default 100)\n\
+-am#i - #i is the lower bound on arc lengths          (default 0)\n\
+\n\
+      Interlayerd arc parameters:\n\
+-ip    - shuffle inter-layer arcs                         (default NO)\n\
+-il#i  - #i is the upper bound on arc lengths          (default 10000)\n\
+-im#i  - #i is the lower bound on arc lengths          (default 1000)\n\
+-in#f  - multiply l(i, j) by #f * x(j)-x(i)           (default 1)\n\
+         if #f=0 - don't multiply\n\
+-is#f  - multiply l(i, j) by #f * (x(j)-x(i))^2       (default NO)\n\
+-ix#i  - #i - is the number of arcs from a node        (default 1)\n\
+-ih#i  - #i - is the step between connected layers     (default 1)\n\
+\n\
+      Potential parameters:\n\
+-p     - generate potentials \n\
+-pl#i  - #i is the upper bound on potentials           (default ll)\n\
+-pm#i  - #i is the lower bound on potentials           (default lm)\n\
+-pn#f  - multiply p(i) by #f * x(i)                    (default NO)\n\
+-ps#f  - multiply p(i) by #f * x(i)^2                  (default NO)\n\
+\n");
+zlog_info (
+"     Artificial source parameters:\n\
+-s     - generate artificial source with default connecting arc lengths\n\
+-sl#i  - #i is the upper bound on art. arc lengths    (default 100000000)\n\
+-sm#i  - #i is the lower bound on art. arc lengths    (default sl)\n\"
+);*/
+}
+
+/* ----- wrong usage ----- */
+void
+usage (struct vty *vty) {
+  vty_out (vty,"usage: X Y seed [-ll#i -lm#i -cl#i -p -pl#i -pm#i ...]%s",VTY_NEWLINE);
+  vty_out (vty,"help: -h or -hh%s",VTY_NEWLINE);
+
+  if ( np > 0 )
+    zlog_err ("error in parameter # %d\n\n", np );
+}
+
+
+/* parsing  parameters */
+/* checks the validity of incoming parameters */
+int
+spgrid_check_params ( struct vty *vty, int argc, const char **argv)
+{
+/* initialized by default values */
+  ext=0;
+
+/* variables for generating one layer */
+
+/* variables for generating spanning graph */
+  c_f = 0;
+  cw_f = 0;
+  cm_f = 0;
+  cl_f = 0;
+
+  cw = PATH;  /* type of spanning graph */
+  cm = 0;             /* lower bound of the interval */
+  cl = 63;           /* upper bound of the interval */
+
+/* variables for generating additional arcs */
+  a_f = 0;
+  ax_f = 0;
+  am_f = 0;
+  al_f = 0;
+
+  ax = 0;             /* number of additional arcs */
+  am = 0;             /* lower bound of the interval */
+  al = 63;           /* upper bound of the interval */
+
+/* variables for inter-layer arcs */
+  i_f = 0;
+  ip_f = 0;
+  ix_f = 0;
+  ih_f = 0;
+  im_f = 0;
+  il_f = 0;
+  in_f = 0;
+  is_f = 0;
+
+  ip = NO;       /* to mess or not to mess */
+  ix = 1;        /* number of interlayered arcs in a NODE */
+  ih = 1;        /* step between two layeres */
+  il = 63; //was 10000;    /* upper bound of the interval */
+  im = 0;  //was 1000;     /* lower bound of the interval */
+  in = 1;        /* l *=  in * |x1-x2| */
+  is = 0;        /* l *=  is * |x1-x2|^2 */
+
+/* variables for artifical source */
+  s_f = 0;
+  sl_f = 0;
+  sm_f = 0;
+  sl   = VERY_FAR; /* upper bound of artifical arc */
+
+/* variables for potentials */
+  p_f = 0;
+  pl_f = 0;
+  pm_f = 0;
+  pn_f = 0;
+  ps_f = 0;
+
+  pn = 0;        /* p +=  ln * (x+1) */
+  ps = 0;        /* p +=  ls * (x+1)^2 */
+
+
+  if ( argc < 1 ) {
+    usage (vty);
+    return 1;
+  }
+
+  np = 0;
+
+  strcpy ( args, argv[0] );
+
+  if ((args[0] == DASH) && (args[1] == 'h'))
+    help (vty);
+
+  if ( argc < 3 ) {
+    usage (vty);
+    return 1;
+  }
+
+  /* first parameter - horizontal size */
+  np = 1;
+  if ( ( X = atoi ( argv[0] ) )  <  1  ) {
+    usage (vty);
+    return 1;
+  }
+
+  /* second parameter - vertical size */
+  np = 2;
+  if ( ( Y = atoi ( argv[1] ) )  <  1  ) {
+    usage (vty);
+    return 1;
+  }
+
+  /* third parameter - seed */
+  np=3;
+  if ( ( seed = atoi ( argv[2] ) )  <=  0  ) {
+    usage (vty);
+    return 1;
+  }
+
+  /* other parameters */
+  for ( np = 3; np < argc; np ++ ) {
+    strcpy ( args, argv[np] );
+    if ( args[0] != DASH )  {
+      usage (vty);
+      return 1;
+    }
+
+    switch ( args[1] ) {
+      case 'c' : /* spanning graph in one layer */
+        c_f = 1;
+        switch ( args[2] ) {
+          case 'l': /* upper bound of the interval */
+            cl_f = 1;
+            cl  =  atol ( &args[3] );
+            break;
+          case 'm': /* lower bound */
+            cm_f = 1;
+            cm  = atol ( &args[3] );
+            break;
+          case 'c': /* type - cycle */
+            cw_f = 1;
+            cw   = CYCLE;
+            break;
+          case 'd': /* type - double cycle */
+            cw_f = 1;
+            cw   = DOUBLE_CYCLE;
+            break;
+          case 'p': /* type - path */
+            cw_f = 1;
+            cw   = PATH;
+            break;
+
+          default:  /* unknown switch  value */
+            usage (vty);
+            return 1;
+          }
+        break;
+
+      case 'a' : /* additional arcs in one layer */
+         a_f = 1;
+        switch ( args[2] )
+          {
+          case 'l': /* upper bound of the interval */
+            al_f = 1;
+            al  =  atol ( &args[3] );
+            break;
+          case 'm': /* lower bound */
+            am_f = 1;
+            am  = atol ( &args[3] );
+            break;
+          case 'x': /* number of additional arcs */
+            ax_f = 1;
+            ax   = atol ( &args[3] );
+            if ( ax < 0 )
+             {
+               usage (vty);
+               return 1;
+             }
+            break;
+
+          default:  /* unknown switch  value */
+            {
+              usage (vty);
+              return 1;
+            }
+          }
+        break;
+
+
+      case 'i' : /* interlayered arcs */
+        i_f = 1;
+
+        switch ( args[2] )
+          {
+          case 'l': /* upper bound */
+            il_f = 1;
+            il  =  atol ( &args[3] );
+            break;
+          case 'm': /* lower bound */
+            im_f = 1;
+            im  = atol ( &args[3] );
+            break;
+          case 'n': /* additional length: l *= in*|i1-i2| */
+            in_f = 1;
+            in  = atof ( &args[3] );
+            break;
+          case 's': /* additional length: l *= is*|i1-i2|^2 */
+            is_f = 1;
+            is  = atof ( &args[3] );
+            break;
+          case 'p': /* mess interlayered arcs */
+            ip_f = 1;
+            ip = YES;
+            break;
+          case 'x': /* number of interlayered arcs */
+            ix_f = 1;
+            ix  = atof ( &args[3] );
+            if ( ix < 1 ) {
+              usage (vty);
+              return 1;
+            }
+            break;
+          case 'h': /* step between two layeres */
+            ih_f = 1;
+            ih  = atof ( &args[3] );
+            if ( ih < 1 ) {
+               usage (vty);
+               return 1;
+             }
+            break;
+          default:  /* unknown switch  value */
+            usage (vty);
+            return 1;
+          }
+        break;
+
+      case 's' : /* additional source */
+        s_f = 1;
+        if ( strlen ( args ) > 2 )
+        {
+        switch ( args[2] )
+          {
+          case 'l': /* upper bound of art. arc */
+            sl_f = 1;
+            sl  =  atol ( &args[3] );
+            break;
+          case 'm': /* lower bound of art. arc */
+            sm_f = 1;
+            sm  =  atol ( &args[3] );
+            break;
+          default:  /* unknown switch  value */
+            usage (vty);
+            return 1;
+          }
+         }
+        break;
+
+      case 'p' : /* potentials */
+        p_f = 1;
+        if ( strlen ( args ) > 2 )
+        {
+        switch ( args[2] )
+          {
+          case 'l': /* upper bound */
+            pl_f = 1;
+            pl  =  atol ( &args[3] );
+            break;
+          case 'm': /* lower bound */
+            pm_f = 1;
+            pm  = atol ( &args[3] );
+            break;
+          case 'n': /* additional: p *= pn*(x+1) */
+            pn_f = 1;
+            pn  = atof ( &args[3] );
+            break;
+          case 's': /* additional: p = ps* (x+1)^2 */
+            ps_f = 1;
+            ps  = atof ( &args[3] );
+            break;
+          default:  /* unknown switch  value */
+            usage (vty);
+            return 1;
+          }
+        }
+        break;
+
+      default: /* unknoun case */
+        usage (vty);
+        return 1;
+      }
+  }
+
+
+  return 0;
+}
+
+
+/* generator of layered networks for the shortest paths problem;
+   extended DIMACS format for output */
+int
+gen_spgrid_topology (struct vty *vty, struct list *topology)
+{
+  /* ----- ajusting parameters ----- */
+
+  /* spanning */
+  if ( cl < cm ) { lx = cl; cl = cm; cm = lx; }
+
+  /* additional arcs */
+  if ( al < am ) { lx = al; al = am; am = lx; }
+
+  /* interlayered arcs */
+  if ( il < im ) { lx = il; il = im; im = lx; }
+
+  /* potential parameters */
+  if ( p_f )
+    {
+     if ( ! pl_f ) pl = il;
+     if ( ! pm_f ) pm = im;
+     if ( pl < pm ) { lx = pl; pl = pm; pm = lx; }
+    }
+
+  /* number of nodes and arcs */
+
+  n = (double)X *(double)Y + 1;
+
+  m  = (double)Y; /* arcs from source */
+
+  switch ( cw )
+  {
+   case PATH:
+    mc = (double)Y - 1;
+    break;
+   case CYCLE:
+    mc = (double)Y;
+    break;
+   case DOUBLE_CYCLE:
+    mc = 2*(double)Y;
+  }
+
+  m += (double)X * (double)mc;  /* spanning arcs */
+  m += (double)X * (double)ax;  /* additional arcs */
+
+  /* interlayered arcs */
+  for ( x = 0; x < X; x ++ )
+  {
+    dl = ( ( X - x - 1 ) + ( ih - 1 ) ) / ih;
+    if ( dl > ix ) dl = ix;
+    m += (double)Y * (double)dl;
+  }
+
+   /* artifical source parameters */
+  if ( s_f ) {
+    m += n; n ++ ;
+    if ( ! sm_f ) sm = sl;
+    if ( sl < sm ) { lx = sl; sl = sm; sm = lx; }
+  }
+
+  if ( n >= (double)LONG_MAX || m >= (double)LONG_MAX )
+  {
+    zlog_err ("Too large problem. It can't be generated\n");
+    exit (4);
+  }
+   else
+  {
+    n0 = (long)n; m0 = (long)m;
+  }
+
+  if ( ip_f )
+     mess = (long*) calloc ( Y, sizeof ( long ) );
+
+  /* printing title */
+  zlog_info ("Generating topology for ISIS");
+
+  source = ( s_f ) ? n0-1 : n0;
+
+  if ( p_f ) /* generating potentials */ {
+    p = (long*) calloc ( n0+1, sizeof (long) );
+    seed1 = 2*seed + 1;
+    init_rand ( seed1);
+    pl = pl - pm + 1;
+
+    for ( x = 0; x < X; x ++ ) {
+      for ( y = 0; y < Y; y ++ ) {
+        p_t = pm + nrand ( pl );
+        if ( pn_f ) p_t *= (long) ( (1 + x) * pn );
+        if ( ps_f ) p_t *= (long) ( (1 + x) * ( (1 + x) * ps ));
+
+        p[ NODE ( x, y ) ] = p_t;
+      }
+    }
+    p[n0] = 0;
+    if ( s_f ) p[n0-1] = 0;
+  }
+
+  if ( s_f ) /* additional arcs from artifical source */
+    {
+      seed2 = 3*seed + 1;
+      init_rand ( seed2 );
+      sl = sl - sm + 1;
+
+      for ( x = X - 1; x >= 0; x -- )
+        for ( y = Y - 1; y >= 0; y -- )
+        {
+          i = NODE ( x, y );
+          s = sm + nrand ( sl );
+          print_arc (vty, topology,  n0, i, s );
+        }
+
+      print_arc (vty, topology,  n0, n0-1, 0 );
+    }
+
+
+  /* ----- generating arcs within layers ----- */
+
+  init_rand ( seed );
+  cl = cl - cm + 1;
+  al = al - am + 1;
+
+  for ( x = 0; x < X; x ++ )
+   {
+  /* generating arcs within one layer */
+    for ( y = 0; y < Y-1; y ++ )
+    {
+       /* generating spanning graph */
+       i = NODE ( x, y );
+       j = NODE ( x, y+1 );
+       l = cm + nrand ( cl );
+       print_arc (vty, topology,  i, j, l );
+
+       if ( cw == DOUBLE_CYCLE )
+         {
+           l = cm + nrand ( cl );
+           print_arc (vty, topology,  j, i, l );
+         }
+     }
+
+    if ( cw <= CYCLE )
+      {
+        i = NODE ( x, Y-1 );
+        j = NODE ( x, 0 );
+        l = cm + nrand ( cl );
+        print_arc (vty, topology,  i, j, l );
+
+        if ( cw == DOUBLE_CYCLE )
+          {
+         l = cm + nrand ( cl );
+            print_arc (vty, topology,  j, i, l );
+          }
+       }
+
+  /* generating additional arcs */
+
+    for ( k = ax; k > 0; k -- )
+       {
+         yy1 = nrand ( Y );
+         do
+            yy2 = nrand ( Y );
+         while ( yy2 == yy1 );
+         i  = NODE ( x, yy1 );
+         j  = NODE ( x, yy2 );
+         l = am + nrand ( al );
+         print_arc (vty, topology,  i, j, l );
+       }
+   }
+
+  /* ----- generating interlayered arcs ------ */
+
+  il = il - im + 1;
+
+  /* arcs from the source */
+
+    for ( y = 0; y < Y; y ++ )
+      {
+        l = im + nrand ( il );
+        i = NODE ( 0, y );
+        print_arc (vty, topology,  source, i, l );
+      }
+
+  for ( x = 0; x < X-1; x ++ )
+   {
+  /* generating arcs from one layer */
+     for ( count = 0, xn = x + 1;
+           count < ix && xn < X;
+           count ++, xn += ih )
+      {
+        if ( ip_f )
+        for ( y = 0; y < Y; y ++ )
+       mess[y] = y;
+
+        for ( y = 0; y < Y; y ++ )
+         {
+            i = NODE ( x, y );
+         dx = xn - x;
+         if ( ip_f )
+           {
+             yyp = nrand(Y-y);
+             yyn = mess[ yyp ];
+                mess[ yyp ] = mess[ Y - y - 1 ];
+           }
+         else
+               yyn =  y;
+         j = NODE ( xn, yyn );
+         l = im + nrand ( il );
+         if ( in != 0 )
+              l *= (long) ( in * dx );
+            if ( is_f )
+              l *= (long) ( ( is * dx ) * dx );
+            print_arc (vty, topology,  i, j, l );
+       }
+      }
+   }
+  /* all is done */
+  return ext;
+
+return 0;
+}
+
+
+
diff --git a/isisd/topology/spgrid.h b/isisd/topology/spgrid.h
new file mode 100644 (file)
index 0000000..1c1ceea
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * IS-IS Rout(e)ing protocol - topology/spgrid.h
+                               Routines for manipulation of SSN and SRM flags
+ * Copyright (C) 2001 Sampo Saaristo, Ofer Wald
+ *
+ * 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.
+ */
+
+/*
+ * Based on:
+ * SPLIB Copyright C 1994 by Cherkassky, Goldberg, and Radzik
+ *
+ */
+#ifndef _ZEBRA_ISIS_TOPOLOGY_SPGRID_H
+#define _ZEBRA_ISIS_TOPOLOGY_SPGRID_H
+
+struct arc {
+  long from_node;
+  long to_node;
+  long distance;
+};
+
+int           gen_spgrid_topology (struct vty *vty, struct list *topology);
+int           spgrid_check_params (struct vty *vty, int argc, const char **argv);
+
+
+#endif /* _ZEBRA_ISIS_TOPOLOGY_SPGRID_H */
+
+
+
+
+
+
diff --git a/isisd/topology/sprand.c b/isisd/topology/sprand.c
new file mode 100644 (file)
index 0000000..1c1eb19
--- /dev/null
@@ -0,0 +1,501 @@
+#include <zebra.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <values.h>
+
+#include "random.c"
+
+#define DASH '-'
+#define VERY_FAR 100000000
+
+/* generator of random networks for the shortest paths problem;
+   extended DIMACS format for output */
+
+main ( argc, argv )
+
+int argc;
+char* argv[];
+
+{
+
+char   args[30];
+
+long   n,
+       n0,
+       source,
+       i,
+       i0,
+       j,
+       dij;
+
+long   m,
+       m0,
+       mc,
+       k;
+
+long   *p,
+       p_t,
+       l,
+       lx;
+
+long   seed,
+       seed1,
+       seed2;
+
+int    ext=0;
+
+FILE   *fout;
+
+/* variables for lengths generating */
+/* initialized by default values */
+int    l_f = 0, ll_f = 0, lm_f = 0, ln_f = 0, ls_f = 0;
+long   ll = 10000,    /* length of the interval */
+       lm = 0;        /* minimal bound of the interval */
+double ln = 0,        /* l += ln * |i-j| */ 
+       ls = 0;        /* l += ls * |i-j|^2 */
+
+/* variables for connecting cycle(s) */
+int    c_f = 0, cl_f = 0, ch_f = 0, c_random = 1;
+long   cl = 1;        /* length of cycle arc */
+long   ch;            /* number of arcs in the cycle 
+                         n - by default */
+
+/* variables for artifical source */
+int    s_f = 0, sl_f = 0, sm_f = 0;
+long   sl   = VERY_FAR, /* upper bound of artifical arc */
+       sm,              /* lower bound of artifical arc */
+       s;  
+
+/* variables for potentials */
+int    p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0,
+       pa_f = 0, pap_f = 0, pac_f = 0;
+long   pl,            /* length of the interval */
+       pm;            /* minimal bound of the interval */
+double pn = 0,        /* l += ln * |i-j| */ 
+       ps = 0,        /* l += ls * |i-j|^2 */
+       pap = 0,       /* part of nodes with alternative dustribution */
+       pac = -1;      /* multiplier for alternative distribution */
+
+int np;               /* number of parameter parsing now */
+
+#define PRINT_ARC( i, j, length )\
+{\
+l = length;\
+if ( p_f ) l += ( p[i] - p[j] );\
+printf ("a %8ld %8ld %12ld\n", i, j, l );\
+}
+
+  /* parsing  parameters */
+
+if ( argc < 2 ) goto usage;
+
+np = 0;
+
+strcpy ( args, argv[1] );
+
+  if ( ( args[0] == DASH ) && ( args[1] == 'h')
+     )
+      goto help;
+
+if ( argc < 4 ) goto usage;
+
+/* first parameter - number of nodes */
+np = 1;
+if ( ( n = atoi ( argv[1] ) )  <  2  )  goto usage;
+
+/* second parameter - number of arcs */
+np = 2;
+if ( ( m = atoi ( argv[2] ) )  <  n  )  goto usage;
+
+/* third parameter - seed */
+np=3;
+if ( ( seed = atoi ( argv[3] ) )  <=  0  )  goto usage;
+
+/* other parameters */
+
+for ( np = 4; np < argc; np ++ )
+  {
+    strcpy ( args, argv[np] );
+    if ( args[0] != DASH ) goto usage;
+
+    switch ( args[1] )
+      {
+
+      case 'l' : /* an interval for arc length */
+       l_f = 1;
+       switch ( args[2] )
+         { 
+         case 'l': /* length of the interval */
+           ll_f = 1;
+           ll  =  (long) atof ( &args[3] );
+           break;
+         case 'm': /* minimal bound */
+           lm_f = 1;
+           lm  = (long ) atof ( &args[3] );
+           break;
+         case 'n': /* additional length: l*|i-j| */
+           ln_f = 1;
+           ln  = atof ( &args[3] );
+           break;
+         case 's': /* additional length: l*|i-j|^2 */
+           ls_f = 1;
+           ls  = atof ( &args[3] );
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+         }
+       break;
+
+      case 'c' : /* connecting cycle(s) */
+        c_f = 1;
+       switch ( args[2] )
+         { 
+         case 'l':
+            c_random = 0;
+           cl_f = 1;
+           cl  =  (long) atof ( &args[3] );
+            if ( cl < 0 ) goto usage;
+           break;
+         case 'h':
+           ch_f = 1;
+           ch  =  (long) atof ( &args[3] );
+            if ( ch < 2 || ch > n ) goto usage;
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+          }
+       break;
+
+      case 's' : /* additional source */
+        s_f = 1;
+       if ( strlen ( args ) > 2 )
+       {  
+       switch ( args[2] )
+         { 
+         case 'l': /* upper bound of art. arc */
+           sl_f = 1;
+           sl  =  (long) atof ( &args[3] );
+            break;
+         case 'm': /* lower bound of art. arc */
+           sm_f = 1;
+           sm  =  (long) atof ( &args[3] );
+            break;
+         default:  /* unknown switch  value */
+           goto usage;
+          }
+         }
+       break;
+
+      case 'p' : /* potentials */
+       p_f = 1;
+       if ( strlen ( args ) > 2 )
+       {  
+       switch ( args[2] )
+         { 
+         case 'l': /* length of the interval */
+           pl_f = 1;
+           pl  =  (long) atof ( &args[3] );
+           break;
+         case 'm': /* minimal bound */
+           pm_f = 1;
+           pm  = (long ) atof ( &args[3] );
+           break;
+         case 'n': /* additional length: l*|i-j| */
+           pn_f = 1;
+           pn  = atof ( &args[3] );
+           break;
+         case 's': /* additional length: l*|i-j|^2 */
+           ps_f = 1;
+           ps  = atof ( &args[3] );
+           break;
+         case 'a': /* bipolar distribution */
+           pa_f = 1;
+           switch ( args[3] )
+             {
+             case 'p': /* % of alternative potentials */
+                pap_f = 1;
+                pap  = atof ( &args[4] );
+               if ( pap < 0   ) pap = 0;
+               if ( pap > 100 ) pap = 100;
+                pap /= 100;
+               break;
+             case 'c': /* multiplier */
+               pac_f = 1;
+               pac = atof ( &args[4] );
+               break;
+             default: /* unknown switch value */
+               goto usage;
+             }
+           break;
+         default:  /* unknown switch  value */
+           goto usage;
+         }
+      }
+       break;
+
+      default  : /* unknoun case */
+       goto usage;
+      }
+  }
+   
+
+/* ----- ajusting parameters ----- */
+
+n0 = n; m0 = m;
+
+/* length parameters */
+if ( ll < lm ) { lx = ll; ll = lm; lm = lx; }
+
+/* potential parameters */
+if ( p_f )
+  {
+   if ( ! pl_f ) pl = ll;
+   if ( ! pm_f ) pm = lm;
+   if ( pl < pm ) { lx = pl; pl = pm; pm = lx; }
+  }
+
+/* path(s) parameters */
+if ( ! ch_f ) ch = n;
+
+mc = n + (n-2) / (ch-1);
+if ( mc > m ) 
+  { fprintf ( stderr,
+              "Error: not enough arcs for generating connecting cycle(s)\n" );
+    exit (4);
+  }
+
+ /* artifical source parameters */
+if ( s_f )          
+   { m0 += n; n0 ++ ; 
+     if ( ! sm_f ) sm = sl;
+     if ( sl < sm ) { lx = sl; sl = sm; sm = lx; }
+   }
+
+/* printing title */
+printf ("c random network for shortest paths problem\n");
+printf ("c extended DIMACS format\nc\n" );
+
+/* name of the problem */
+printf ("t rd_%ld_%ld_%ld_", n, m, seed );
+if ( l_f )
+  printf ("%c", 'l');
+if ( c_f )
+  printf ("%c", 'c');
+if ( s_f )
+  printf ("%c", 's');
+if ( p_f )
+  printf ("%c", 'p');
+printf ("\nc\n");
+
+/* printing additional information  */
+if ( l_f )
+  printf ("c length -> min: %ld max: %ld k1: %.2f k2: %.2f\n",
+           lm, ll, ln, ls );
+if ( c_f )
+  {
+    if ( c_random )
+      printf ("c cycle -> number of arcs: %ld  arc length: random\n", ch);
+    else
+      printf ("c cycle -> number of arcs: %ld  arc length: %ld\n",
+              ch, cl );
+  }
+if ( s_f )
+  printf ("c length of arcs from artifical source -> min: %ld max: %ld\n",
+           sm, sl );
+if ( p_f )
+  {
+  printf ("c potentials -> min: %ld max: %ld k1: %.2f k2: %.2f\n",
+         pm, pl, pn, ps );
+  if ( pa_f )
+  printf ("c potentials -> part of alternative distribution: %.2f k: %.2f\n",
+          pap, pac );
+  }
+printf ("c\n" );
+
+
+printf ("p sp %8ld %8ld\nc\n", n0, m0 );
+
+source = ( s_f ) ? n0 : 1;
+printf ("n %8ld\nc\n", source );
+
+if ( p_f ) /* generating potentials */
+  {
+    p = (long*) calloc ( n+2, sizeof (long) );
+    seed1 = 2*seed + 1;
+    init_rand ( seed1);
+    pl = pl - pm + 1;
+
+    for ( i = 0; i <= n; i ++ )
+      {
+       p_t = pm + nrand ( pl );
+       if ( pn_f ) p_t += (long) ( i * pn );
+       if ( ps_f ) p_t += (long) ( i * ( i * ps ));
+       if ( pap_f )
+           if ( rand01() < pap )
+               p_t = (long) ( p_t * pac );
+        p[i] = p_t;
+      }
+    p[n+1] = 0;
+  }
+
+
+if ( s_f ) /* additional arcs from artifical source */
+  {
+    seed2 = 3*seed + 1;
+    init_rand ( seed2 );
+    sl = sl - sm + 1;
+
+    for ( i = n; i > 1; i -- )
+      {
+       s = sm + nrand ( sl );
+       PRINT_ARC ( n0, i, s ) 
+      }
+
+    PRINT_ARC ( n0, 1, 0 )
+  }
+
+/* initialize random number generator */
+init_rand ( seed );
+ll = ll - lm + 1;
+
+/* generating connecting cycle(s) */
+if (c_random)
+  cl = lm + nrand ( ll );
+PRINT_ARC ( 1, 2, cl )
+if (c_random)
+  cl = lm + nrand ( ll );
+PRINT_ARC ( n, 1, cl )
+
+for ( i = 2; i < n; i ++ )
+  {
+    if (c_random)
+      cl = lm + nrand ( ll );
+
+    if ( ( (i-1) % (ch-1) ) != 0 )
+        PRINT_ARC ( i, i+1, cl )
+    else
+      { PRINT_ARC ( i,   1, cl )
+        if (c_random)
+          cl = lm + nrand ( ll );
+       PRINT_ARC ( 1, i+1, cl )
+      }
+  }
+
+/* generating random arcs */
+
+for ( k = 1; k <= m - mc; k ++ )
+  {
+    i = 1 + nrand ( n );
+
+    do
+    j = 1 + nrand ( n );
+    while ( j == i );
+
+    dij = ( i > j ) ? ( i - j ) : ( j - i );
+    l = lm + nrand ( ll );
+    if ( ln_f ) l += (long) ( dij * ln );
+    if ( ls_f ) l += (long) ( dij * ( dij * ls ) );
+    PRINT_ARC ( i, j, l );
+  }
+
+/* all is done */
+exit (ext);
+
+/* ----- wrong usage ----- */
+
+ usage:
+fprintf ( stderr,
+"\nusage: %s  n  m  seed  [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\
+help:  %s -h\n\n", argv[0], argv[0] );
+
+if ( np > 0 )
+  fprintf ( stderr, "error in parameter # %d\n\n", np );   
+exit (4);
+
+/* ---- help ---- */
+
+ help:
+
+if ( args[2] == 'h') goto hhelp;
+
+fprintf ( stderr, 
+"\n'%s' - random network generator for shortest paths problem.\n\
+Generates problems in extended DIMACS format.\n\
+\n\
+   %s  n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\
+   %s -hh\n\
+\n\
+                        #i - integer number   #f - real number\n\
+\n\
+-ll#i  - #i is the upper bound on arc lengths          (default 10000)\n\
+-lm#i  - #i is the lower bound on arc lengths          (default 0)\n\
+-cl#i  - #i is length of arcs in connecting cycle(s)   (default random)\n\
+-p     - generate potentials \n\
+-pl#i  - #i is the upper bound on potentials           (default ll)\n\
+-pm#i  - #i is the lower bound on potentials           (default lm)\n\
+\n\
+-hh    - extended help \n\n",
+argv[0], argv[0], argv[0] );
+
+exit (0);
+
+/* --------- sophisticated help ------------ */
+ hhelp:
+
+if ( argc < 3 )
+     fout = stderr;
+else
+     fout = fopen ( argv[2], "w" );
+
+if ( fout == NULL )
+{ fprintf ( stderr, "\nCan't open file  '%s' for writing help\n\n", argv[2] );
+  exit ( 2 );
+}
+
+fprintf (fout, 
+"\n'%s' - random network generator for shortest paths problem.\n\
+Generates problems in extended DIMACS format.\n\
+\n\
+   %s  n m seed [ -ll#i -lm#i -ln#f -ls#f\n\
+                      -p  -pl#i -pm#i -pn#f -ps#f -pap#i -pac#f\n\
+                      -cl#i -ch#i\n\
+                      -s -sl#i -sm#i\n\
+                    ]\n\
+   %s -hh file_name\n\
+\n\
+                        #i - integer number   #f - real number\n\
+\n\
+      Arc length parameters:\n\
+-ll#i  - #i is the upper bound on arc lengths          (default 10000)\n\
+-lm#i  - #i is the lower bound on arc lengths          (default 0)\n\
+-ln#f  - multipliy l(i, j) by #f * |i-j|               (default 0)\n\
+-ls#f  - multipliy l(i, j) by #f * |i-j|^2             (default 0)\n\
+\n\
+      Potential parameters:\n\
+-p     - generate potentials \n\
+-pl#i  - #i is the upper bound on potentials           (default ll)\n\
+-pm#i  - #i is the lower bound on potentials           (default lm)\n\
+-pn#f  - multiply p(i) by #f * i                       (default 0)\n\
+-ps#f  - multiply p(i) by #f * i^2                     (default 0)\n\
+-pap#i - percentage of alternative potential nodes     (default 0)\n\
+-pac#f - if i is alternative, multiply  p(i) by #f     (default -1)\n\
+\n\
+      Connecting cycle(s) parameters:\n\
+-cl#i  - #i is length of arcs in connecting cycle(s)   (default random)\n\
+-ch#i  - #i is length of connecting cycles             (default n)\n\
+\n\
+      Artificial source parameters:\n\
+-s     - generate artificial source with default connecting arc lengths\n\
+-sl#i  - #i is the upper bound on art. arc lengths    (default 100000000)\n\
+-sm#i  - #i is the lower bound on art. arc lengths    (default sl)\n\
+\n\
+-hh file_name  - save this help in the file 'file_name'\n\n",
+argv[0], argv[0], argv[0] );
+
+exit (0);
+}
+
+
+
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644 (file)
index 0000000..02aa432
--- /dev/null
@@ -0,0 +1,18 @@
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+version.c
+version.h
+gitversion.h
+gitversion.h.tmp
+.deps
+.nfs*
+.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+memtypes.h
+route_types.h
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644 (file)
index 0000000..cb9e1fb
--- /dev/null
@@ -0,0 +1,71 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+AM_CFLAGS = $(WERROR)
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+
+lib_LTLIBRARIES = libzebra.la
+libzebra_la_LDFLAGS = -version-info 1:0:0 
+
+libzebra_la_SOURCES = \
+       network.c pid_output.c getopt.c getopt1.c daemon.c \
+       checksum.c vector.c linklist.c vty.c command.c \
+       sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
+       filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
+       zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \
+       sigevent.c pqueue.c jhash.c memtypes.c workqueue.c vrf.c \
+       event_counter.c nexthop.c
+
+BUILT_SOURCES = memtypes.h route_types.h gitversion.h
+
+libzebra_la_DEPENDENCIES = @LIB_REGEX@
+
+libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@
+
+pkginclude_HEADERS = \
+       buffer.h checksum.h command.h filter.h getopt.h hash.h \
+       if.h linklist.h log.h \
+       memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
+       str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
+       plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
+       privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
+       workqueue.h route_types.h libospf.h vrf.h fifo.h event_counter.h \
+       nexthop.h
+
+noinst_HEADERS = \
+       plist_int.h
+
+EXTRA_DIST = \
+       regex.c regex-gnu.h \
+       queue.h \
+       memtypes.awk \
+       route_types.pl route_types.txt \
+       gitversion.pl
+
+memtypes.h: $(srcdir)/memtypes.c $(srcdir)/memtypes.awk
+       ($(GAWK) -f $(srcdir)/memtypes.awk $(srcdir)/memtypes.c > $@)
+
+route_types.h: $(srcdir)/route_types.txt $(srcdir)/route_types.pl
+       @PERL@ $(srcdir)/route_types.pl < $(srcdir)/route_types.txt > $@
+
+if GIT_VERSION
+
+# bit of a trick here to always have up-to-date git stamps without triggering
+# unneccessary rebuilds.  .PHONY causes the .tmp file to be rebuilt always,
+# but if we use that on gitversion.h it'll ripple through the .c file deps.
+# (even if gitversion.h's file timestamp doesn't change, make will think it
+# did, because of .PHONY...)
+
+.PHONY: gitversion.h.tmp
+.SILENT: gitversion.h gitversion.h.tmp
+GITH=gitversion.h
+gitversion.h.tmp: $(srcdir)/../.git
+       @PERL@ $(srcdir)/gitversion.pl $(srcdir) > ${GITH}.tmp
+gitversion.h: gitversion.h.tmp
+       { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH}
+
+else
+.PHONY: gitversion.h
+gitversion.h:
+       true
+endif
diff --git a/lib/agentx.c b/lib/agentx.c
new file mode 100644 (file)
index 0000000..5d7d057
--- /dev/null
@@ -0,0 +1,315 @@
+/* SNMP support
+ * Copyright (C) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#if defined HAVE_SNMP && defined SNMP_AGENTX
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+
+#include "command.h"
+#include "smux.h"
+
+static int agentx_enabled = 0;
+
+static struct thread_master *agentx_tm;
+static struct thread *timeout_thr = NULL;
+static struct list *events = NULL;
+
+static void agentx_events_update(void);
+
+static int
+agentx_timeout(struct thread *t)
+{
+  timeout_thr = NULL;
+
+  snmp_timeout ();
+  run_alarms ();
+  netsnmp_check_outstanding_agent_requests ();
+  agentx_events_update ();
+  return 0;
+}
+
+static int
+agentx_read(struct thread *t)
+{
+  fd_set fds;
+  struct listnode *ln = THREAD_ARG (t);
+  list_delete_node (events, ln);
+
+  FD_ZERO (&fds);
+  FD_SET (THREAD_FD (t), &fds);
+  snmp_read (&fds);
+
+  netsnmp_check_outstanding_agent_requests ();
+  agentx_events_update ();
+  return 0;
+}
+
+static void
+agentx_events_update(void)
+{
+  int maxfd = 0;
+  int block = 1;
+  struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
+  fd_set fds;
+  struct listnode *ln;
+  struct thread *thr;
+  int fd, thr_fd;
+
+  THREAD_OFF (timeout_thr);
+
+  FD_ZERO (&fds);
+  snmp_select_info (&maxfd, &fds, &timeout, &block);
+
+  if (!block)
+    timeout_thr = thread_add_timer_tv (agentx_tm, agentx_timeout, NULL, &timeout);
+
+  ln = listhead (events);
+  thr = ln ? listgetdata (ln) : NULL;
+  thr_fd = thr ? THREAD_FD (thr) : -1;
+
+  /* "two-pointer" / two-list simultaneous iteration
+   * ln/thr/thr_fd point to the next existing event listener to hit while
+   * fd counts to catch up */
+  for (fd = 0; fd < maxfd; fd++)
+    {
+      /* caught up */
+      if (thr_fd == fd)
+        {
+          struct listnode *nextln = listnextnode (ln);
+          if (!FD_ISSET (fd, &fds))
+            {
+              thread_cancel (thr);
+              list_delete_node (events, ln);
+            }
+          ln = nextln;
+          thr = ln ? listgetdata (ln) : NULL;
+          thr_fd = thr ? THREAD_FD (thr) : -1;
+        }
+      /* need listener, but haven't hit one where it would be */
+      else if (FD_ISSET (fd, &fds))
+        {
+          struct listnode *newln;
+          thr = thread_add_read (agentx_tm, agentx_read, NULL, fd);
+          newln = listnode_add_before (events, ln, thr);
+          thr->arg = newln;
+        }
+    }
+
+  /* leftover event listeners at this point have fd > maxfd, delete them */
+  while (ln)
+    {
+      struct listnode *nextln = listnextnode (ln);
+      thread_cancel (listgetdata (ln));
+      list_delete_node (events, ln);
+      ln = nextln;
+    }
+}
+
+/* AgentX node. */
+static struct cmd_node agentx_node =
+{
+  SMUX_NODE,
+  ""                            /* AgentX has no interface. */
+};
+
+/* Logging NetSNMP messages */
+static int
+agentx_log_callback(int major, int minor,
+                   void *serverarg, void *clientarg)
+{
+  struct snmp_log_message *slm = (struct snmp_log_message *)serverarg;
+  char *msg = strdup (slm->msg);
+  if (msg) msg[strlen(msg)-1] = '\0';
+  switch (slm->priority)
+    {
+    case LOG_EMERG:   zlog_err   ("snmp[emerg]: %s",   msg?msg:slm->msg); break;
+    case LOG_ALERT:   zlog_err   ("snmp[alert]: %s",   msg?msg:slm->msg); break;
+    case LOG_CRIT:    zlog_err   ("snmp[crit]: %s",    msg?msg:slm->msg); break;
+    case LOG_ERR:     zlog_err   ("snmp[err]: %s",     msg?msg:slm->msg); break;
+    case LOG_WARNING: zlog_warn  ("snmp[warning]: %s", msg?msg:slm->msg); break;
+    case LOG_NOTICE:  zlog_notice("snmp[notice]: %s",  msg?msg:slm->msg); break;
+    case LOG_INFO:    zlog_info  ("snmp[info]: %s",    msg?msg:slm->msg); break;
+    case LOG_DEBUG:   zlog_debug ("snmp[debug]: %s",   msg?msg:slm->msg); break;
+    }
+  free(msg);
+  return SNMP_ERR_NOERROR;
+}
+
+static int
+config_write_agentx (struct vty *vty)
+{
+  if (agentx_enabled)
+      vty_out (vty, "agentx%s", VTY_NEWLINE);
+  return 0;
+}
+
+DEFUN (agentx_enable,
+       agentx_enable_cmd,
+       "agentx",
+       "SNMP AgentX protocol settings\n"
+       "SNMP AgentX settings\n")
+{
+  if (!agentx_enabled)
+    {
+      init_snmp("quagga");
+      events = list_new();
+      agentx_events_update ();
+      agentx_enabled = 1;
+      return CMD_SUCCESS;
+    }
+  vty_out (vty, "SNMP AgentX already enabled%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+DEFUN (no_agentx,
+       no_agentx_cmd,
+       "no agentx",
+       NO_STR
+       "SNMP AgentX protocol settings\n"
+       "SNMP AgentX settings\n")
+{
+  if (!agentx_enabled) return CMD_SUCCESS;
+  vty_out (vty, "SNMP AgentX support cannot be disabled once enabled%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+void
+smux_init (struct thread_master *tm)
+{
+  agentx_tm = tm;
+
+  netsnmp_enable_subagent ();
+  snmp_disable_log ();
+  snmp_enable_calllog ();
+  snmp_register_callback (SNMP_CALLBACK_LIBRARY,
+                         SNMP_CALLBACK_LOGGING,
+                         agentx_log_callback,
+                         NULL);
+  init_agent ("quagga");
+
+  install_node (&agentx_node, config_write_agentx);
+  install_element (CONFIG_NODE, &agentx_enable_cmd);
+  install_element (CONFIG_NODE, &no_agentx_cmd);
+}
+
+void
+smux_register_mib (const char *descr, struct variable *var, 
+                  size_t width, int num,
+                  oid name[], size_t namelen)
+{
+  register_mib (descr, var, width, num, name, namelen);
+}
+
+int
+smux_trap (struct variable *vp, size_t vp_len,
+          const oid *ename, size_t enamelen,
+          const oid *name, size_t namelen,
+          const oid *iname, size_t inamelen,
+          const struct trap_object *trapobj, size_t trapobjlen,
+          u_char sptrap)
+{
+  oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
+  size_t objid_snmptrap_len = sizeof objid_snmptrap / sizeof (oid);
+  oid notification_oid[MAX_OID_LEN];
+  size_t notification_oid_len;
+  unsigned int i;
+
+  netsnmp_variable_list *notification_vars = NULL;
+  if (!agentx_enabled) return 0;
+
+  /* snmpTrapOID */
+  oid_copy (notification_oid, ename, enamelen);
+  notification_oid[enamelen] = sptrap;
+  notification_oid_len = enamelen + 1;
+  snmp_varlist_add_variable (&notification_vars,
+                            objid_snmptrap, objid_snmptrap_len,
+                            ASN_OBJECT_ID,
+                            (u_char *) notification_oid,
+                            notification_oid_len * sizeof(oid));
+
+  /* Provided bindings */
+  for (i = 0; i < trapobjlen; i++)
+    {
+      unsigned int j;
+      oid oid[MAX_OID_LEN];
+      size_t oid_len, onamelen;
+      u_char *val;
+      size_t val_len;
+      WriteMethod *wm = NULL;
+      struct variable cvp;
+
+      /* Make OID. */
+      if (trapobj[i].namelen > 0)
+        {
+         /* Columnar object */
+         onamelen = trapobj[i].namelen;
+         oid_copy (oid, name, namelen);
+         oid_copy (oid + namelen, trapobj[i].name, onamelen);
+         oid_copy (oid + namelen + onamelen, iname, inamelen);
+         oid_len = namelen + onamelen + inamelen;
+        }
+      else
+        {
+         /* Scalar object */
+         onamelen = trapobj[i].namelen * (-1);
+         oid_copy (oid, name, namelen);
+         oid_copy (oid + namelen, trapobj[i].name, onamelen);
+         oid[onamelen + namelen] = 0;
+         oid_len = namelen + onamelen + 1;
+        }
+
+      /* Locate the appropriate function and type in the MIB registry. */
+      for (j = 0; j < vp_len; j++)
+       {
+         if (oid_compare (trapobj[i].name, onamelen, vp[j].name, vp[j].namelen) != 0)
+           continue;
+         /* We found the appropriate variable in the MIB registry. */
+         oid_copy(cvp.name, name, namelen);
+         oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen);
+         cvp.namelen = namelen + vp[j].namelen;
+         cvp.type = vp[j].type;
+         cvp.magic = vp[j].magic;
+         cvp.acl = vp[j].acl;
+         cvp.findVar = vp[j].findVar;
+         /* Grab the result. */
+         val = cvp.findVar (&cvp, oid, &oid_len, 1, &val_len, &wm);
+         if (!val) break;
+         snmp_varlist_add_variable (&notification_vars,
+                                    oid, oid_len,
+                                    vp[j].type,
+                                    val,
+                                    val_len);
+         break;
+       }
+    }
+
+
+  send_v2trap (notification_vars);
+  snmp_free_varbind (notification_vars);
+  agentx_events_update ();
+  return 1;
+}
+
+#endif /* HAVE_SNMP */
diff --git a/lib/buffer.c b/lib/buffer.c
new file mode 100644 (file)
index 0000000..ee93101
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Buffering of output and input. 
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "buffer.h"
+#include "log.h"
+#include "network.h"
+#include <stddef.h>
+
+
+
+/* Buffer master. */
+struct buffer
+{
+  /* Data list. */
+  struct buffer_data *head;
+  struct buffer_data *tail;
+  
+  /* Size of each buffer_data chunk. */
+  size_t size;
+};
+
+/* Data container. */
+struct buffer_data
+{
+  struct buffer_data *next;
+
+  /* Location to add new data. */
+  size_t cp;
+
+  /* Pointer to data not yet flushed. */
+  size_t sp;
+
+  /* Actual data stream (variable length). */
+  unsigned char data[];  /* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified).  It is rounded up to the
+   next page boundery. */
+#define BUFFER_SIZE_DEFAULT            4096
+
+
+#define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
+
+/* Make new buffer. */
+struct buffer *
+buffer_new (size_t size)
+{
+  struct buffer *b;
+
+  b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
+
+  if (size)
+    b->size = size;
+  else
+    {
+      static size_t default_size;
+      if (!default_size)
+        {
+         long pgsz = sysconf(_SC_PAGESIZE);
+         default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz);
+       }
+      b->size = default_size;
+    }
+
+  return b;
+}
+
+/* Free buffer. */
+void
+buffer_free (struct buffer *b)
+{
+  buffer_reset(b);
+  XFREE (MTYPE_BUFFER, b);
+}
+
+/* Make string clone. */
+char *
+buffer_getstr (struct buffer *b)
+{
+  size_t totlen = 0;
+  struct buffer_data *data;
+  char *s;
+  char *p;
+
+  for (data = b->head; data; data = data->next)
+    totlen += data->cp - data->sp;
+  if (!(s = XMALLOC(MTYPE_TMP, totlen+1)))
+    return NULL;
+  p = s;
+  for (data = b->head; data; data = data->next)
+    {
+      memcpy(p, data->data + data->sp, data->cp - data->sp);
+      p += data->cp - data->sp;
+    }
+  *p = '\0';
+  return s;
+}
+
+/* Return 1 if buffer is empty. */
+int
+buffer_empty (struct buffer *b)
+{
+  return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void
+buffer_reset (struct buffer *b)
+{
+  struct buffer_data *data;
+  struct buffer_data *next;
+  
+  for (data = b->head; data; data = next)
+    {
+      next = data->next;
+      BUFFER_DATA_FREE(data);
+    }
+  b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *
+buffer_add (struct buffer *b)
+{
+  struct buffer_data *d;
+
+  d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data) + b->size);
+  d->cp = d->sp = 0;
+  d->next = NULL;
+
+  if (b->tail)
+    b->tail->next = d;
+  else
+    b->head = d;
+  b->tail = d;
+
+  return d;
+}
+
+/* Write data to buffer. */
+void
+buffer_put(struct buffer *b, const void *p, size_t size)
+{
+  struct buffer_data *data = b->tail;
+  const char *ptr = p;
+
+  /* We use even last one byte of data buffer. */
+  while (size)    
+    {
+      size_t chunk;
+
+      /* If there is no data buffer add it. */
+      if (data == NULL || data->cp == b->size)
+       data = buffer_add (b);
+
+      chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
+      memcpy ((data->data + data->cp), ptr, chunk);
+      size -= chunk;
+      ptr += chunk;
+      data->cp += chunk;
+    }
+}
+
+/* Insert character into the buffer. */
+void
+buffer_putc (struct buffer *b, u_char c)
+{
+  buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void
+buffer_putstr (struct buffer *b, const char *c)
+{
+  buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+   encountered or the operation would block. */
+buffer_status_t
+buffer_flush_all (struct buffer *b, int fd)
+{
+  buffer_status_t ret;
+  struct buffer_data *head;
+  size_t head_sp;
+
+  if (!b->head)
+    return BUFFER_EMPTY;
+  head_sp = (head = b->head)->sp;
+  /* Flush all data. */
+  while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
+    {
+      if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
+        /* No data was flushed, so kernel buffer must be full. */
+       return ret;
+      head_sp = (head = b->head)->sp;
+    }
+
+  return ret;
+}
+
+/* Flush enough data to fill a terminal window of the given scene (used only
+   by vty telnet interface). */
+buffer_status_t
+buffer_flush_window (struct buffer *b, int fd, int width, int height, 
+                    int erase_flag, int no_more_flag)
+{
+  int nbytes;
+  int iov_alloc;
+  int iov_index;
+  struct iovec *iov;
+  struct iovec small_iov[3];
+  char more[] = " --More-- ";
+  char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+                  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+                  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
+  struct buffer_data *data;
+  int column;
+
+  if (!b->head)
+    return BUFFER_EMPTY;
+
+  if (height < 1)
+    {
+      zlog_warn("%s called with non-positive window height %d, forcing to 1",
+               __func__, height);
+      height = 1;
+    }
+  else if (height >= 2)
+    height--;
+  if (width < 1)
+    {
+      zlog_warn("%s called with non-positive window width %d, forcing to 1",
+               __func__, width);
+      width = 1;
+    }
+
+  /* For erase and more data add two to b's buffer_data count.*/
+  if (b->head->next == NULL)
+    {
+      iov_alloc = array_size(small_iov);
+      iov = small_iov;
+    }
+  else
+    {
+      iov_alloc = ((height*(width+2))/b->size)+10;
+      iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
+    }
+  iov_index = 0;
+
+  /* Previously print out is performed. */
+  if (erase_flag)
+    {
+      iov[iov_index].iov_base = erase;
+      iov[iov_index].iov_len = sizeof erase;
+      iov_index++;
+    }
+
+  /* Output data. */
+  column = 1;  /* Column position of next character displayed. */
+  for (data = b->head; data && (height > 0); data = data->next)
+    {
+      size_t cp;
+
+      cp = data->sp;
+      while ((cp < data->cp) && (height > 0))
+        {
+         /* Calculate lines remaining and column position after displaying
+            this character. */
+         if (data->data[cp] == '\r')
+           column = 1;
+         else if ((data->data[cp] == '\n') || (column == width))
+           {
+             column = 1;
+             height--;
+           }
+         else
+           column++;
+         cp++;
+        }
+      iov[iov_index].iov_base = (char *)(data->data + data->sp);
+      iov[iov_index++].iov_len = cp-data->sp;
+      data->sp = cp;
+
+      if (iov_index == iov_alloc)
+       /* This should not ordinarily happen. */
+        {
+         iov_alloc *= 2;
+         if (iov != small_iov)
+           {
+             zlog_warn("%s: growing iov array to %d; "
+                       "width %d, height %d, size %lu",
+                       __func__, iov_alloc, width, height, (u_long)b->size);
+             iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov));
+           }
+         else
+           {
+             /* This should absolutely never occur. */
+             zlog_err("%s: corruption detected: iov_small overflowed; "
+                      "head %p, tail %p, head->next %p",
+                      __func__, (void *)b->head, (void *)b->tail,
+                      (void *)b->head->next);
+             iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
+             memcpy(iov, small_iov, sizeof(small_iov));
+           }
+       }
+    }
+
+  /* In case of `more' display need. */
+  if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
+    {
+      iov[iov_index].iov_base = more;
+      iov[iov_index].iov_len = sizeof more;
+      iov_index++;
+    }
+
+
+#ifdef IOV_MAX
+  /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+     example: Solaris2.6 are defined IOV_MAX size at 16.     */
+  {
+    struct iovec *c_iov = iov;
+    nbytes = 0; /* Make sure it's initialized. */
+
+    while (iov_index > 0)
+      {
+        int iov_size;
+
+        iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+        if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
+          {
+            zlog_warn("%s: writev to fd %d failed: %s",
+                      __func__, fd, safe_strerror(errno));
+            break;
+          }
+
+        /* move pointer io-vector */
+        c_iov += iov_size;
+        iov_index -= iov_size;
+      }
+  }
+#else  /* IOV_MAX */
+   if ((nbytes = writev (fd, iov, iov_index)) < 0)
+     zlog_warn("%s: writev to fd %d failed: %s",
+              __func__, fd, safe_strerror(errno));
+#endif /* IOV_MAX */
+
+  /* Free printed buffer data. */
+  while (b->head && (b->head->sp == b->head->cp))
+    {
+      struct buffer_data *del;
+      if (!(b->head = (del = b->head)->next))
+        b->tail = NULL;
+      BUFFER_DATA_FREE(del);
+    }
+
+  if (iov != small_iov)
+    XFREE (MTYPE_TMP, iov);
+
+  return (nbytes < 0) ? BUFFER_ERROR :
+                       (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets.  It does not attempt to write out
+all of the queued data, just a "big" chunk.  It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t
+buffer_flush_available(struct buffer *b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written.  There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+  struct buffer_data *d;
+  size_t written;
+  struct iovec iov[MAX_CHUNKS];
+  size_t iovcnt = 0;
+  size_t nbyte = 0;
+
+  for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+       d = d->next, iovcnt++)
+    {
+      iov[iovcnt].iov_base = d->data+d->sp;
+      nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
+    }
+
+  if (!nbyte)
+    /* No data to flush: should we issue a warning message? */
+    return BUFFER_EMPTY;
+
+  /* only place where written should be sign compared */
+  if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
+    {
+      if (ERRNO_IO_RETRY(errno))
+       /* Calling code should try again later. */
+        return BUFFER_PENDING;
+      zlog_warn("%s: write error on fd %d: %s",
+               __func__, fd, safe_strerror(errno));
+      return BUFFER_ERROR;
+    }
+
+  /* Free printed buffer data. */
+  while (written > 0)
+    {
+      struct buffer_data *d;
+      if (!(d = b->head))
+        {
+          zlog_err("%s: corruption detected: buffer queue empty, "
+                  "but written is %lu", __func__, (u_long)written);
+         break;
+        }
+      if (written < d->cp-d->sp)
+        {
+         d->sp += written;
+         return BUFFER_PENDING;
+       }
+
+      written -= (d->cp-d->sp);
+      if (!(b->head = d->next))
+        b->tail = NULL;
+      BUFFER_DATA_FREE(d);
+    }
+
+  return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer *b, int fd, const void *p, size_t size)
+{
+  ssize_t nbytes;
+
+#if 0
+  /* Should we attempt to drain any previously buffered data?  This could help
+     reduce latency in pushing out the data if we are stuck in a long-running
+     thread that is preventing the main select loop from calling the flush
+     thread... */
+  if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+    return BUFFER_ERROR;
+#endif
+  if (b->head)
+    /* Buffer is not empty, so do not attempt to write the new data. */
+    nbytes = 0;
+  else if ((nbytes = write(fd, p, size)) < 0)
+    {
+      if (ERRNO_IO_RETRY(errno))
+        nbytes = 0;
+      else
+        {
+         zlog_warn("%s: write error on fd %d: %s",
+                   __func__, fd, safe_strerror(errno));
+         return BUFFER_ERROR;
+       }
+    }
+  /* Add any remaining data to the buffer. */
+  {
+    size_t written = nbytes;
+    if (written < size)
+      buffer_put(b, ((const char *)p)+written, size-written);
+  }
+  return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}
diff --git a/lib/buffer.h b/lib/buffer.h
new file mode 100644 (file)
index 0000000..6c3dc76
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Buffering to output and input. 
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_BUFFER_H
+#define _ZEBRA_BUFFER_H
+
+
+/* Create a new buffer.  Memory will be allocated in chunks of the given
+   size.  If the argument is 0, the library will supply a reasonable
+   default size suitable for buffering socket I/O. */
+extern struct buffer *buffer_new (size_t);
+
+/* Free all data in the buffer. */
+extern void buffer_reset (struct buffer *);
+
+/* This function first calls buffer_reset to release all buffered data.
+   Then it frees the struct buffer itself. */
+extern void buffer_free (struct buffer *);
+
+/* Add the given data to the end of the buffer. */
+extern void buffer_put (struct buffer *, const void *, size_t);
+/* Add a single character to the end of the buffer. */
+extern void buffer_putc (struct buffer *, u_char);
+/* Add a NUL-terminated string to the end of the buffer. */
+extern void buffer_putstr (struct buffer *, const char *);
+
+/* Combine all accumulated (and unflushed) data inside the buffer into a
+   single NUL-terminated string allocated using XMALLOC(MTYPE_TMP).  Note
+   that this function does not alter the state of the buffer, so the data
+   is still inside waiting to be flushed. */
+char *buffer_getstr (struct buffer *);
+
+/* Returns 1 if there is no pending data in the buffer.  Otherwise returns 0. */
+int buffer_empty (struct buffer *);
+
+typedef enum
+  {
+    /* An I/O error occurred.  The buffer should be destroyed and the
+       file descriptor should be closed. */
+    BUFFER_ERROR = -1,
+
+    /* The data was written successfully, and the buffer is now empty
+       (there is no pending data waiting to be flushed). */
+    BUFFER_EMPTY = 0,
+
+    /* There is pending data in the buffer waiting to be flushed.  Please
+       try flushing the buffer when select indicates that the file descriptor
+       is writeable. */
+    BUFFER_PENDING = 1
+  } buffer_status_t;
+
+/* Try to write this data to the file descriptor.  Any data that cannot
+   be written immediately is added to the buffer queue. */
+extern buffer_status_t buffer_write(struct buffer *, int fd,
+                                   const void *, size_t);
+
+/* This function attempts to flush some (but perhaps not all) of 
+   the queued data to the given file descriptor. */
+extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+
+/* The following 2 functions (buffer_flush_all and buffer_flush_window)
+   are for use in lib/vty.c only.  They should not be used elsewhere. */
+
+/* Call buffer_flush_available repeatedly until either all data has been
+   flushed, or an I/O error has been encountered, or the operation would
+   block. */
+extern buffer_status_t buffer_flush_all (struct buffer *, int fd);
+
+/* Attempt to write enough data to the given fd to fill a window of the
+   given width and height (and remove the data written from the buffer).
+
+   If !no_more, then a message saying " --More-- " is appended. 
+   If erase is true, then first overwrite the previous " --More-- " message
+   with spaces.
+
+   Any write error (including EAGAIN or EINTR) will cause this function
+   to return -1 (because the logic for handling the erase and more features
+   is too complicated to retry the write later).
+*/
+extern buffer_status_t buffer_flush_window (struct buffer *, int fd, int width,
+                                           int height, int erase, int no_more);
+
+#endif /* _ZEBRA_BUFFER_H */
diff --git a/lib/checksum.c b/lib/checksum.c
new file mode 100644 (file)
index 0000000..43940b7
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Checksum routine for Internet Protocol family headers (C Version).
+ *
+ * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and
+ * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989,
+ * pp. 86-101, for additional details on computing this checksum.
+ */
+
+#include <zebra.h>
+#include "checksum.h"
+
+int                    /* return checksum in low-order 16 bits */
+in_cksum(void *parg, int nbytes)
+{
+       u_short *ptr = parg;
+       register long           sum;            /* assumes long == 32 bits */
+       u_short                 oddbyte;
+       register u_short        answer;         /* assumes u_short == 16 bits */
+
+       /*
+        * Our algorithm is simple, using a 32-bit accumulator (sum),
+        * we add sequential 16-bit words to it, and at the end, fold back
+        * all the carry bits from the top 16 bits into the lower 16 bits.
+        */
+
+       sum = 0;
+       while (nbytes > 1)  {
+               sum += *ptr++;
+               nbytes -= 2;
+       }
+
+                               /* mop up an odd byte, if necessary */
+       if (nbytes == 1) {
+               oddbyte = 0;            /* make sure top half is zero */
+               *((u_char *) &oddbyte) = *(u_char *)ptr;   /* one byte only */
+               sum += oddbyte;
+       }
+
+       /*
+        * Add back carry outs from top 16 bits to low 16 bits.
+        */
+
+       sum  = (sum >> 16) + (sum & 0xffff);    /* add high-16 to low-16 */
+       sum += (sum >> 16);                     /* add carry */
+       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 */
+/* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum
+   without modifying the buffer; a valid checksum returns 0 */
+u_int16_t
+fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset)
+{
+  u_int8_t *p;
+  int x, y, c0, c1;
+  u_int16_t checksum;
+  u_int16_t *csum;
+  size_t partial_len, i, left = len;
+  
+  checksum = 0;
+
+
+  if (offset != FLETCHER_CHECKSUM_VALIDATE)
+    /* Zero the csum in the packet. */
+    {
+      assert (offset < (len - 1)); /* account for two bytes of checksum */
+      csum = (u_int16_t *) (buffer + offset);
+      *(csum) = 0;
+    }
+
+  p = buffer;
+  c0 = 0;
+  c1 = 0;
+
+  while (left != 0)
+    {
+      partial_len = MIN(left, MODX);
+
+      for (i = 0; i < partial_len; i++)
+       {
+         c0 = c0 + *(p++);
+         c1 += c0;
+       }
+
+      c0 = c0 % 255;
+      c1 = c1 % 255;
+
+      left -= partial_len;
+    }
+
+  /* The cast is important, to ensure the mod is taken as a signed value. */
+  x = (int)((len - offset - 1) * c0 - c1) % 255;
+
+  if (x <= 0)
+    x += 255;
+  y = 510 - c0 - x;
+  if (y > 255)  
+    y -= 255;
+
+  if (offset == FLETCHER_CHECKSUM_VALIDATE)
+    {
+      checksum = (c1 << 8) + c0;
+    }
+  else
+    {
+      /*
+       * 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 --git a/lib/checksum.h b/lib/checksum.h
new file mode 100644 (file)
index 0000000..b310f74
--- /dev/null
@@ -0,0 +1,3 @@
+extern int in_cksum(void *, int);
+#define FLETCHER_CHECKSUM_VALIDATE 0xffff
+extern u_int16_t fletcher_checksum(u_char *, const size_t len, const uint16_t offset);
diff --git a/lib/command.c b/lib/command.c
new file mode 100644 (file)
index 0000000..662f8a3
--- /dev/null
@@ -0,0 +1,4334 @@
+/*
+   Command interpreter routine for virtual terminal [aka TeletYpe]
+   Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+   Copyright (C) 2013 by Open Source Routing.
+   Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+
+This file is part of GNU Zebra.
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <zebra.h>
+
+
+#include "memory.h"
+#include "log.h"
+#include <lib/version.h>
+#include "thread.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "workqueue.h"
+
+/* Command vector which includes some level of command lists. Normally
+   each daemon maintains each own cmdvec. */
+vector cmdvec = NULL;
+
+struct cmd_token token_cr;
+char *command_cr = NULL;
+
+enum filter_type
+{
+  FILTER_RELAXED,
+  FILTER_STRICT
+};
+
+enum matcher_rv
+{
+  MATCHER_OK,
+  MATCHER_COMPLETE,
+  MATCHER_INCOMPLETE,
+  MATCHER_NO_MATCH,
+  MATCHER_AMBIGUOUS,
+  MATCHER_EXCEED_ARGC_MAX
+};
+
+#define MATCHER_ERROR(matcher_rv) \
+  (   (matcher_rv) == MATCHER_INCOMPLETE \
+   || (matcher_rv) == MATCHER_NO_MATCH \
+   || (matcher_rv) == MATCHER_AMBIGUOUS \
+   || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
+  )
+
+/* Host information structure. */
+struct host host;
+
+/* Standard command node structures. */
+static struct cmd_node auth_node =
+{
+  AUTH_NODE,
+  "Password: ",
+};
+
+static struct cmd_node view_node =
+{
+  VIEW_NODE,
+  "%s> ",
+};
+
+static struct cmd_node restricted_node =
+{
+  RESTRICTED_NODE,
+  "%s$ ",
+};
+
+static struct cmd_node auth_enable_node =
+{
+  AUTH_ENABLE_NODE,
+  "Password: ",
+};
+
+static struct cmd_node enable_node =
+{
+  ENABLE_NODE,
+  "%s# ",
+};
+
+static struct cmd_node config_node =
+{
+  CONFIG_NODE,
+  "%s(config)# ",
+  1
+};
+
+/* Default motd string. */
+static const char *default_motd =
+"\r\n\
+Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
+" QUAGGA_COPYRIGHT "\r\n\
+" GIT_INFO "\r\n";
+
+
+static const struct facility_map {
+  int facility;
+  const char *name;
+  size_t match;
+} syslog_facilities[] = 
+  {
+    { LOG_KERN, "kern", 1 },
+    { LOG_USER, "user", 2 },
+    { LOG_MAIL, "mail", 1 },
+    { LOG_DAEMON, "daemon", 1 },
+    { LOG_AUTH, "auth", 1 },
+    { LOG_SYSLOG, "syslog", 1 },
+    { LOG_LPR, "lpr", 2 },
+    { LOG_NEWS, "news", 1 },
+    { LOG_UUCP, "uucp", 2 },
+    { LOG_CRON, "cron", 1 },
+#ifdef LOG_FTP
+    { LOG_FTP, "ftp", 1 },
+#endif
+    { LOG_LOCAL0, "local0", 6 },
+    { LOG_LOCAL1, "local1", 6 },
+    { LOG_LOCAL2, "local2", 6 },
+    { LOG_LOCAL3, "local3", 6 },
+    { LOG_LOCAL4, "local4", 6 },
+    { LOG_LOCAL5, "local5", 6 },
+    { LOG_LOCAL6, "local6", 6 },
+    { LOG_LOCAL7, "local7", 6 },
+    { 0, NULL, 0 },
+  };
+
+static const char *
+facility_name(int facility)
+{
+  const struct facility_map *fm;
+
+  for (fm = syslog_facilities; fm->name; fm++)
+    if (fm->facility == facility)
+      return fm->name;
+  return "";
+}
+
+static int
+facility_match(const char *str)
+{
+  const struct facility_map *fm;
+
+  for (fm = syslog_facilities; fm->name; fm++)
+    if (!strncmp(str,fm->name,fm->match))
+      return fm->facility;
+  return -1;
+}
+
+static int
+level_match(const char *s)
+{
+  int level ;
+  
+  for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
+    if (!strncmp (s, zlog_priority[level], 2))
+      return level;
+  return ZLOG_DISABLED;
+}
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void
+print_version (const char *progname)
+{
+  printf ("%s version %s\n", progname, QUAGGA_VERSION);
+  printf ("%s\n", QUAGGA_COPYRIGHT);
+  printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS);
+}
+
+
+/* Utility function to concatenate argv argument into a single string
+   with inserting ' ' character between each argument.  */
+char *
+argv_concat (const char **argv, int argc, int shift)
+{
+  int i;
+  size_t len;
+  char *str;
+  char *p;
+
+  len = 0;
+  for (i = shift; i < argc; i++)
+    len += strlen(argv[i])+1;
+  if (!len)
+    return NULL;
+  p = str = XMALLOC(MTYPE_TMP, len);
+  for (i = shift; i < argc; i++)
+    {
+      size_t arglen;
+      memcpy(p, argv[i], (arglen = strlen(argv[i])));
+      p += arglen;
+      *p++ = ' ';
+    }
+  *(p-1) = '\0';
+  return str;
+}
+
+static unsigned int
+cmd_hash_key (void *p)
+{
+  return (uintptr_t) p;
+}
+
+static int
+cmd_hash_cmp (const void *a, const void *b)
+{
+  return a == b;
+}
+
+/* Install top node of command vector. */
+void
+install_node (struct cmd_node *node, 
+             int (*func) (struct vty *))
+{
+  vector_set_index (cmdvec, node->node, node);
+  node->func = func;
+  node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
+  node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp);
+}
+
+/* Breaking up string into each command piece. I assume given
+   character is separated by a space character. Return value is a
+   vector which includes char ** data element. */
+vector
+cmd_make_strvec (const char *string)
+{
+  const char *cp, *start;
+  char *token;
+  int strlen;
+  vector strvec;
+  
+  if (string == NULL)
+    return NULL;
+  
+  cp = string;
+
+  /* Skip white spaces. */
+  while (isspace ((int) *cp) && *cp != '\0')
+    cp++;
+
+  /* Return if there is only white spaces */
+  if (*cp == '\0')
+    return NULL;
+
+  if (*cp == '!' || *cp == '#')
+    return NULL;
+
+  /* Prepare return vector. */
+  strvec = vector_init (VECTOR_MIN_SIZE);
+
+  /* Copy each command piece and set into vector. */
+  while (1) 
+    {
+      start = cp;
+      while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
+            *cp != '\0')
+       cp++;
+      strlen = cp - start;
+      token = XMALLOC (MTYPE_STRVEC, strlen + 1);
+      memcpy (token, start, strlen);
+      *(token + strlen) = '\0';
+      vector_set (strvec, token);
+
+      while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
+            *cp != '\0')
+       cp++;
+
+      if (*cp == '\0')
+       return strvec;
+    }
+}
+
+/* Free allocated string vector. */
+void
+cmd_free_strvec (vector v)
+{
+  unsigned int i;
+  char *cp;
+
+  if (!v)
+    return;
+
+  for (i = 0; i < vector_active (v); i++)
+    if ((cp = vector_slot (v, i)) != NULL)
+      XFREE (MTYPE_STRVEC, cp);
+
+  vector_free (v);
+}
+
+struct format_parser_state
+{
+  vector topvect; /* Top level vector */
+  vector intvect; /* Intermediate level vector, used when there's
+                   * a multiple in a keyword. */
+  vector curvect; /* current vector where read tokens should be
+                     appended. */
+
+  const char *string; /* pointer to command string, not modified */
+  const char *cp; /* pointer in command string, moved along while
+                     parsing */
+  const char *dp;  /* pointer in description string, moved along while
+                     parsing */
+
+  int in_keyword; /* flag to remember if we are in a keyword group */
+  int in_multiple; /* flag to remember if we are in a multiple group */
+  int just_read_word; /* flag to remember if the last thing we red was a
+                       * real word and not some abstract token */
+};
+
+static void
+format_parser_error(struct format_parser_state *state, const char *message)
+{
+  int offset = state->cp - state->string + 1;
+
+  fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
+  fprintf(stderr, "                        %*c\n", offset, '^');
+  fprintf(stderr, "%s at offset %d.\n", message, offset);
+  fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
+  exit(1);
+}
+
+static char *
+format_parser_desc_str(struct format_parser_state *state)
+{
+  const char *cp, *start;
+  char *token;
+  int strlen;
+
+  cp = state->dp;
+
+  if (cp == NULL)
+    return NULL;
+
+  /* Skip white spaces. */
+  while (isspace ((int) *cp) && *cp != '\0')
+    cp++;
+
+  /* Return if there is only white spaces */
+  if (*cp == '\0')
+    return NULL;
+
+  start = cp;
+
+  while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
+    cp++;
+
+  strlen = cp - start;
+  token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
+  memcpy (token, start, strlen);
+  *(token + strlen) = '\0';
+
+  state->dp = cp;
+
+  return token;
+}
+
+static void
+format_parser_begin_keyword(struct format_parser_state *state)
+{
+  struct cmd_token *token;
+  vector keyword_vect;
+
+  if (state->in_keyword
+      || state->in_multiple)
+    format_parser_error(state, "Unexpected '{'");
+
+  state->cp++;
+  state->in_keyword = 1;
+
+  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
+  token->type = TOKEN_KEYWORD;
+  token->keyword = vector_init(VECTOR_MIN_SIZE);
+
+  keyword_vect = vector_init(VECTOR_MIN_SIZE);
+  vector_set(token->keyword, keyword_vect);
+
+  vector_set(state->curvect, token);
+  state->curvect = keyword_vect;
+}
+
+static void
+format_parser_begin_multiple(struct format_parser_state *state)
+{
+  struct cmd_token *token;
+
+  if (state->in_keyword == 1)
+    format_parser_error(state, "Keyword starting with '('");
+
+  if (state->in_multiple)
+    format_parser_error(state, "Nested group");
+
+  state->cp++;
+  state->in_multiple = 1;
+  state->just_read_word = 0;
+
+  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
+  token->type = TOKEN_MULTIPLE;
+  token->multiple = vector_init(VECTOR_MIN_SIZE);
+
+  vector_set(state->curvect, token);
+  if (state->curvect != state->topvect)
+    state->intvect = state->curvect;
+  state->curvect = token->multiple;
+}
+
+static void
+format_parser_end_keyword(struct format_parser_state *state)
+{
+  if (state->in_multiple
+      || !state->in_keyword)
+    format_parser_error(state, "Unexpected '}'");
+
+  if (state->in_keyword == 1)
+    format_parser_error(state, "Empty keyword group");
+
+  state->cp++;
+  state->in_keyword = 0;
+  state->curvect = state->topvect;
+}
+
+static void
+format_parser_end_multiple(struct format_parser_state *state)
+{
+  char *dummy;
+
+  if (!state->in_multiple)
+    format_parser_error(state, "Unexpected ')'");
+
+  if (vector_active(state->curvect) == 0)
+    format_parser_error(state, "Empty multiple section");
+
+  if (!state->just_read_word)
+    {
+      /* There are constructions like
+       * 'show ip ospf database ... (self-originate|)'
+       * in use.
+       * The old parser reads a description string for the
+       * word '' between |) which will never match.
+       * Simulate this behvaior by dropping the next desc
+       * string in such a case. */
+
+      dummy = format_parser_desc_str(state);
+      XFREE(MTYPE_CMD_TOKENS, dummy);
+    }
+
+  state->cp++;
+  state->in_multiple = 0;
+
+  if (state->intvect)
+    state->curvect = state->intvect;
+  else
+    state->curvect = state->topvect;
+}
+
+static void
+format_parser_handle_pipe(struct format_parser_state *state)
+{
+  struct cmd_token *keyword_token;
+  vector keyword_vect;
+
+  if (state->in_multiple)
+    {
+      state->just_read_word = 0;
+      state->cp++;
+    }
+  else if (state->in_keyword)
+    {
+      state->in_keyword = 1;
+      state->cp++;
+
+      keyword_token = vector_slot(state->topvect,
+                                  vector_active(state->topvect) - 1);
+      keyword_vect = vector_init(VECTOR_MIN_SIZE);
+      vector_set(keyword_token->keyword, keyword_vect);
+      state->curvect = keyword_vect;
+    }
+  else
+    {
+      format_parser_error(state, "Unexpected '|'");
+    }
+}
+
+static void
+format_parser_read_word(struct format_parser_state *state)
+{
+  const char *start;
+  int len;
+  char *cmd;
+  struct cmd_token *token;
+
+  start = state->cp;
+
+  while (state->cp[0] != '\0'
+         && !strchr("\r\n(){}|", state->cp[0])
+         && !isspace((int)state->cp[0]))
+    state->cp++;
+
+  len = state->cp - start;
+  cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
+  memcpy(cmd, start, len);
+  cmd[len] = '\0';
+
+  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
+  token->type = TOKEN_TERMINAL;
+  if (strcmp (cmd, "A.B.C.D") == 0)
+    token->terminal = TERMINAL_IPV4;
+  else if (strcmp (cmd, "A.B.C.D/M") == 0)
+    token->terminal = TERMINAL_IPV4_PREFIX;
+  else if (strcmp (cmd, "X:X::X:X") == 0)
+    token->terminal = TERMINAL_IPV6;
+  else if (strcmp (cmd, "X:X::X:X/M") == 0)
+    token->terminal = TERMINAL_IPV6_PREFIX;
+  else if (cmd[0] == '[')
+    token->terminal = TERMINAL_OPTION;
+  else if (cmd[0] == '.')
+    token->terminal = TERMINAL_VARARG;
+  else if (cmd[0] == '<')
+    token->terminal = TERMINAL_RANGE;
+  else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
+    token->terminal = TERMINAL_VARIABLE;
+  else
+    token->terminal = TERMINAL_LITERAL;
+
+  token->cmd = cmd;
+  token->desc = format_parser_desc_str(state);
+  vector_set(state->curvect, token);
+
+  if (state->in_keyword == 1)
+    state->in_keyword = 2;
+
+  state->just_read_word = 1;
+}
+
+/**
+ * Parse a given command format string and build a tree of tokens from
+ * it that is suitable to be used by the command subsystem.
+ *
+ * @param string Command format string.
+ * @param descstr Description string.
+ * @return A vector of struct cmd_token representing the given command,
+ *         or NULL on error.
+ */
+static vector
+cmd_parse_format(const char *string, const char *descstr)
+{
+  struct format_parser_state state;
+
+  if (string == NULL)
+    return NULL;
+
+  memset(&state, 0, sizeof(state));
+  state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
+  state.cp = state.string = string;
+  state.dp = descstr;
+
+  while (1)
+    {
+      while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
+        state.cp++;
+
+      switch (state.cp[0])
+        {
+        case '\0':
+          if (state.in_keyword
+              || state.in_multiple)
+            format_parser_error(&state, "Unclosed group/keyword");
+          return state.topvect;
+        case '{':
+          format_parser_begin_keyword(&state);
+          break;
+        case '(':
+          format_parser_begin_multiple(&state);
+          break;
+        case '}':
+          format_parser_end_keyword(&state);
+          break;
+        case ')':
+          format_parser_end_multiple(&state);
+          break;
+        case '|':
+          format_parser_handle_pipe(&state);
+          break;
+        default:
+          format_parser_read_word(&state);
+        }
+    }
+}
+
+/* Return prompt character of specified node. */
+const char *
+cmd_prompt (enum node_type node)
+{
+  struct cmd_node *cnode;
+
+  cnode = vector_slot (cmdvec, node);
+  return cnode->prompt;
+}
+
+/* Install a command into a node. */
+void
+install_element (enum node_type ntype, struct cmd_element *cmd)
+{
+  struct cmd_node *cnode;
+  
+  /* cmd_init hasn't been called */
+  if (!cmdvec)
+    {
+      fprintf (stderr, "%s called before cmd_init, breakage likely\n",
+               __func__);
+      return;
+    }
+  
+  cnode = vector_slot (cmdvec, ntype);
+
+  if (cnode == NULL) 
+    {
+      fprintf (stderr, "Command node %d doesn't exist, please check it\n",
+              ntype);
+      exit (1);
+    }
+  
+  if (hash_lookup (cnode->cmd_hash, cmd) != NULL)
+    {
+#ifdef DEV_BUILD
+      fprintf (stderr, 
+               "Multiple command installs to node %d of command:\n%s\n",
+               ntype, cmd->string);
+#endif
+      return;
+    }
+  
+  assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern));
+  
+  vector_set (cnode->cmd_vector, cmd);
+  if (cmd->tokens == NULL)
+    cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
+
+  if (ntype == VIEW_NODE)
+    install_element (ENABLE_NODE, cmd);
+}
+
+static const unsigned char itoa64[] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void
+to64(char *s, long v, int n)
+{
+  while (--n >= 0) 
+    {
+      *s++ = itoa64[v&0x3f];
+      v >>= 6;
+    }
+}
+
+static char *
+zencrypt (const char *passwd)
+{
+  char salt[6];
+  struct timeval tv;
+  char *crypt (const char *, const char *);
+
+  gettimeofday(&tv,0);
+  
+  to64(&salt[0], random(), 3);
+  to64(&salt[3], tv.tv_usec, 3);
+  salt[5] = '\0';
+
+  return crypt (passwd, salt);
+}
+
+/* This function write configuration of this host. */
+static int
+config_write_host (struct vty *vty)
+{
+  if (host.name)
+    vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
+
+  if (host.encrypt)
+    {
+      if (host.password_encrypt)
+        vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); 
+      if (host.enable_encrypt)
+        vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); 
+    }
+  else
+    {
+      if (host.password)
+        vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
+      if (host.enable)
+        vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
+    }
+
+  if (zlog_default->default_lvl != LOG_DEBUG)
+    {
+      vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
+              VTY_NEWLINE);
+      vty_out (vty, "log trap %s%s",
+              zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+    }
+
+  if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
+    {
+      vty_out (vty, "log file %s", host.logfile);
+      if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
+       vty_out (vty, " %s",
+                zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
+    {
+      vty_out (vty, "log stdout");
+      if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
+       vty_out (vty, " %s",
+                zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+    vty_out(vty,"no log monitor%s",VTY_NEWLINE);
+  else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
+    vty_out(vty,"log monitor %s%s",
+           zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
+
+  if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
+    {
+      vty_out (vty, "log syslog");
+      if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
+       vty_out (vty, " %s",
+                zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  if (zlog_default->facility != LOG_DAEMON)
+    vty_out (vty, "log facility %s%s",
+            facility_name(zlog_default->facility), VTY_NEWLINE);
+
+  if (zlog_default->record_priority == 1)
+    vty_out (vty, "log record-priority%s", VTY_NEWLINE);
+
+  if (zlog_default->timestamp_precision > 0)
+    vty_out (vty, "log timestamp precision %d%s",
+            zlog_default->timestamp_precision, VTY_NEWLINE);
+
+  if (host.advanced)
+    vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
+
+  if (host.encrypt)
+    vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
+
+  if (host.lines >= 0)
+    vty_out (vty, "service terminal-length %d%s", host.lines,
+            VTY_NEWLINE);
+
+  if (host.motdfile)
+    vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
+  else if (! host.motd)
+    vty_out (vty, "no banner motd%s", VTY_NEWLINE);
+
+  return 1;
+}
+
+/* Utility function for getting command vector. */
+static vector
+cmd_node_vector (vector v, enum node_type ntype)
+{
+  struct cmd_node *cnode = vector_slot (v, ntype);
+  return cnode->cmd_vector;
+}
+
+/* Completion match types. */
+enum match_type 
+{
+  no_match,
+  extend_match,
+  ipv4_prefix_match,
+  ipv4_match,
+  ipv6_prefix_match,
+  ipv6_match,
+  range_match,
+  vararg_match,
+  partly_match,
+  exact_match 
+};
+
+static enum match_type
+cmd_ipv4_match (const char *str)
+{
+  const char *sp;
+  int dots = 0, nums = 0;
+  char buf[4];
+
+  if (str == NULL)
+    return partly_match;
+
+  for (;;)
+    {
+      memset (buf, 0, sizeof (buf));
+      sp = str;
+      while (*str != '\0')
+       {
+         if (*str == '.')
+           {
+             if (dots >= 3)
+               return no_match;
+
+             if (*(str + 1) == '.')
+               return no_match;
+
+             if (*(str + 1) == '\0')
+               return partly_match;
+
+             dots++;
+             break;
+           }
+         if (!isdigit ((int) *str))
+           return no_match;
+
+         str++;
+       }
+
+      if (str - sp > 3)
+       return no_match;
+
+      strncpy (buf, sp, str - sp);
+      if (atoi (buf) > 255)
+       return no_match;
+
+      nums++;
+
+      if (*str == '\0')
+       break;
+
+      str++;
+    }
+
+  if (nums < 4)
+    return partly_match;
+
+  return exact_match;
+}
+
+static enum match_type
+cmd_ipv4_prefix_match (const char *str)
+{
+  const char *sp;
+  int dots = 0;
+  char buf[4];
+
+  if (str == NULL)
+    return partly_match;
+
+  for (;;)
+    {
+      memset (buf, 0, sizeof (buf));
+      sp = str;
+      while (*str != '\0' && *str != '/')
+       {
+         if (*str == '.')
+           {
+             if (dots == 3)
+               return no_match;
+
+             if (*(str + 1) == '.' || *(str + 1) == '/')
+               return no_match;
+
+             if (*(str + 1) == '\0')
+               return partly_match;
+
+             dots++;
+             break;
+           }
+
+         if (!isdigit ((int) *str))
+           return no_match;
+
+         str++;
+       }
+
+      if (str - sp > 3)
+       return no_match;
+
+      strncpy (buf, sp, str - sp);
+      if (atoi (buf) > 255)
+       return no_match;
+
+      if (dots == 3)
+       {
+         if (*str == '/')
+           {
+             if (*(str + 1) == '\0')
+               return partly_match;
+
+             str++;
+             break;
+           }
+         else if (*str == '\0')
+           return partly_match;
+       }
+
+      if (*str == '\0')
+       return partly_match;
+
+      str++;
+    }
+
+  sp = str;
+  while (*str != '\0')
+    {
+      if (!isdigit ((int) *str))
+       return no_match;
+
+      str++;
+    }
+
+  if (atoi (sp) > 32)
+    return no_match;
+
+  return exact_match;
+}
+
+#define IPV6_ADDR_STR          "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR                "0123456789abcdefABCDEF:.%/"
+#define STATE_START            1
+#define STATE_COLON            2
+#define STATE_DOUBLE           3
+#define STATE_ADDR             4
+#define STATE_DOT               5
+#define STATE_SLASH            6
+#define STATE_MASK             7
+
+#ifdef HAVE_IPV6
+
+static enum match_type
+cmd_ipv6_match (const char *str)
+{
+  struct sockaddr_in6 sin6_dummy;
+  int ret;
+
+  if (str == NULL)
+    return partly_match;
+
+  if (strspn (str, IPV6_ADDR_STR) != strlen (str))
+    return no_match;
+
+  /* use inet_pton that has a better support,
+   * for example inet_pton can support the automatic addresses:
+   *  ::1.2.3.4
+   */
+  ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+   
+  if (ret == 1)
+    return exact_match;
+
+  return no_match;
+}
+
+static enum match_type
+cmd_ipv6_prefix_match (const char *str)
+{
+  int state = STATE_START;
+  int colons = 0, nums = 0, double_colon = 0;
+  int mask;
+  const char *sp = NULL;
+  char *endptr = NULL;
+
+  if (str == NULL)
+    return partly_match;
+
+  if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
+    return no_match;
+
+  while (*str != '\0' && state != STATE_MASK)
+    {
+      switch (state)
+       {
+       case STATE_START:
+         if (*str == ':')
+           {
+             if (*(str + 1) != ':' && *(str + 1) != '\0')
+               return no_match;
+             colons--;
+             state = STATE_COLON;
+           }
+         else
+           {
+             sp = str;
+             state = STATE_ADDR;
+           }
+
+         continue;
+       case STATE_COLON:
+         colons++;
+         if (*(str + 1) == '/')
+           return no_match;
+         else if (*(str + 1) == ':')
+           state = STATE_DOUBLE;
+         else
+           {
+             sp = str + 1;
+             state = STATE_ADDR;
+           }
+         break;
+       case STATE_DOUBLE:
+         if (double_colon)
+           return no_match;
+
+         if (*(str + 1) == ':')
+           return no_match;
+         else
+           {
+             if (*(str + 1) != '\0' && *(str + 1) != '/')
+               colons++;
+             sp = str + 1;
+
+             if (*(str + 1) == '/')
+               state = STATE_SLASH;
+             else
+               state = STATE_ADDR;
+           }
+
+         double_colon++;
+         nums += 1;
+         break;
+       case STATE_ADDR:
+         if (*(str + 1) == ':' || *(str + 1) == '.'
+             || *(str + 1) == '\0' || *(str + 1) == '/')
+           {
+             if (str - sp > 3)
+               return no_match;
+
+             for (; sp <= str; sp++)
+               if (*sp == '/')
+                 return no_match;
+
+             nums++;
+
+             if (*(str + 1) == ':')
+               state = STATE_COLON;
+             else if (*(str + 1) == '.')
+               {
+                 if (colons || double_colon)
+                   state = STATE_DOT;
+                 else
+                   return no_match;
+               }
+             else if (*(str + 1) == '/')
+               state = STATE_SLASH;
+           }
+         break;
+       case STATE_DOT:
+         state = STATE_ADDR;
+         break;
+       case STATE_SLASH:
+         if (*(str + 1) == '\0')
+           return partly_match;
+
+         state = STATE_MASK;
+         break;
+       default:
+         break;
+       }
+
+      if (nums > 11)
+       return no_match;
+
+      if (colons > 7)
+       return no_match;
+
+      str++;
+    }
+
+  if (state < STATE_MASK)
+    return partly_match;
+
+  mask = strtol (str, &endptr, 10);
+  if (*endptr != '\0')
+    return no_match;
+
+  if (mask < 0 || mask > 128)
+    return no_match;
+  
+  return exact_match;
+}
+
+#endif /* HAVE_IPV6  */
+
+#define DECIMAL_STRLEN_MAX 10
+
+static int
+cmd_range_match (const char *range, const char *str)
+{
+  char *p;
+  char buf[DECIMAL_STRLEN_MAX + 1];
+  char *endptr = NULL;
+  unsigned long min, max, val;
+
+  if (str == NULL)
+    return 1;
+
+  val = strtoul (str, &endptr, 10);
+  if (*endptr != '\0')
+    return 0;
+
+  range++;
+  p = strchr (range, '-');
+  if (p == NULL)
+    return 0;
+  if (p - range > DECIMAL_STRLEN_MAX)
+    return 0;
+  strncpy (buf, range, p - range);
+  buf[p - range] = '\0';
+  min = strtoul (buf, &endptr, 10);
+  if (*endptr != '\0')
+    return 0;
+
+  range = p + 1;
+  p = strchr (range, '>');
+  if (p == NULL)
+    return 0;
+  if (p - range > DECIMAL_STRLEN_MAX)
+    return 0;
+  strncpy (buf, range, p - range);
+  buf[p - range] = '\0';
+  max = strtoul (buf, &endptr, 10);
+  if (*endptr != '\0')
+    return 0;
+
+  if (val < min || val > max)
+    return 0;
+
+  return 1;
+}
+
+static enum match_type
+cmd_word_match(struct cmd_token *token,
+               enum filter_type filter,
+               const char *word)
+{
+  const char *str;
+  enum match_type match_type;
+
+  str = token->cmd;
+
+  if (filter == FILTER_RELAXED)
+    if (!word || !strlen(word))
+      return partly_match;
+
+  if (!word)
+    return no_match;
+
+  switch (token->terminal)
+    {
+      case TERMINAL_VARARG:
+        return vararg_match;
+
+      case TERMINAL_RANGE:
+        if (cmd_range_match(str, word))
+          return range_match;
+        break;
+
+      case TERMINAL_IPV6:
+        match_type = cmd_ipv6_match(word);
+        if ((filter == FILTER_RELAXED && match_type != no_match)
+          || (filter == FILTER_STRICT && match_type == exact_match))
+          return ipv6_match;
+        break;
+
+      case TERMINAL_IPV6_PREFIX:
+        match_type = cmd_ipv6_prefix_match(word);
+        if ((filter == FILTER_RELAXED && match_type != no_match)
+            || (filter == FILTER_STRICT && match_type == exact_match))
+          return ipv6_prefix_match;
+        break;
+
+      case TERMINAL_IPV4:
+        match_type = cmd_ipv4_match(word);
+        if ((filter == FILTER_RELAXED && match_type != no_match)
+            || (filter == FILTER_STRICT && match_type == exact_match))
+          return ipv4_match;
+        break;
+
+      case TERMINAL_IPV4_PREFIX:
+        match_type = cmd_ipv4_prefix_match(word);
+        if ((filter == FILTER_RELAXED && match_type != no_match)
+            || (filter == FILTER_STRICT && match_type == exact_match))
+          return ipv4_prefix_match;
+        break;
+
+      case TERMINAL_OPTION:
+      case TERMINAL_VARIABLE:
+        return extend_match;
+
+      case TERMINAL_LITERAL:
+        if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
+          {
+            if (!strcmp(str, word))
+              return exact_match;
+            return partly_match;
+          }
+        if (filter == FILTER_STRICT && !strcmp(str, word))
+          return exact_match;
+        break;
+
+      default:
+        assert (0);
+    }
+
+  return no_match;
+}
+
+struct cmd_matcher
+{
+  struct cmd_element *cmd; /* The command element the matcher is using */
+  enum filter_type filter; /* Whether to use strict or relaxed matching */
+  vector vline; /* The tokenized commandline which is to be matched */
+  unsigned int index; /* The index up to which matching should be done */
+
+  /* If set, construct a list of matches at the position given by index */
+  enum match_type *match_type;
+  vector *match;
+
+  unsigned int word_index; /* iterating over vline */
+};
+
+static int
+push_argument(int *argc, const char **argv, const char *arg)
+{
+  if (!arg || !strlen(arg))
+    arg = NULL;
+
+  if (!argc || !argv)
+    return 0;
+
+  if (*argc >= CMD_ARGC_MAX)
+    return -1;
+
+  argv[(*argc)++] = arg;
+  return 0;
+}
+
+static void
+cmd_matcher_record_match(struct cmd_matcher *matcher,
+                         enum match_type match_type,
+                         struct cmd_token *token)
+{
+  if (matcher->word_index != matcher->index)
+    return;
+
+  if (matcher->match)
+    {
+      if (!*matcher->match)
+        *matcher->match = vector_init(VECTOR_MIN_SIZE);
+      vector_set(*matcher->match, token);
+    }
+
+  if (matcher->match_type)
+    {
+      if (match_type > *matcher->match_type)
+        *matcher->match_type = match_type;
+    }
+}
+
+static int
+cmd_matcher_words_left(struct cmd_matcher *matcher)
+{
+  return matcher->word_index < vector_active(matcher->vline);
+}
+
+static const char*
+cmd_matcher_get_word(struct cmd_matcher *matcher)
+{
+  assert(cmd_matcher_words_left(matcher));
+
+  return vector_slot(matcher->vline, matcher->word_index);
+}
+
+static enum matcher_rv
+cmd_matcher_match_terminal(struct cmd_matcher *matcher,
+                           struct cmd_token *token,
+                           int *argc, const char **argv)
+{
+  const char *word;
+  enum match_type word_match;
+
+  assert(token->type == TOKEN_TERMINAL);
+
+  if (!cmd_matcher_words_left(matcher))
+    {
+      if (token->terminal == TERMINAL_OPTION)
+        return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
+      else
+        return MATCHER_INCOMPLETE;
+    }
+
+  word = cmd_matcher_get_word(matcher);
+  word_match = cmd_word_match(token, matcher->filter, word);
+  if (word_match == no_match)
+    return MATCHER_NO_MATCH;
+
+  /* We have to record the input word as argument if it matched
+   * against a variable. */
+  if (TERMINAL_RECORD (token->terminal))
+    {
+      if (push_argument(argc, argv, word))
+        return MATCHER_EXCEED_ARGC_MAX;
+    }
+
+  cmd_matcher_record_match(matcher, word_match, token);
+
+  matcher->word_index++;
+
+  /* A vararg token should consume all left over words as arguments */
+  if (token->terminal == TERMINAL_VARARG)
+    while (cmd_matcher_words_left(matcher))
+      {
+        word = cmd_matcher_get_word(matcher);
+        if (word && strlen(word))
+          push_argument(argc, argv, word);
+        matcher->word_index++;
+      }
+
+  return MATCHER_OK;
+}
+
+static enum matcher_rv
+cmd_matcher_match_multiple(struct cmd_matcher *matcher,
+                           struct cmd_token *token,
+                           int *argc, const char **argv)
+{
+  enum match_type multiple_match;
+  unsigned int multiple_index;
+  const char *word;
+  const char *arg = NULL;
+  struct cmd_token *word_token;
+  enum match_type word_match;
+
+  assert(token->type == TOKEN_MULTIPLE);
+
+  multiple_match = no_match;
+
+  if (!cmd_matcher_words_left(matcher))
+    return MATCHER_INCOMPLETE;
+
+  word = cmd_matcher_get_word(matcher);
+  for (multiple_index = 0;
+       multiple_index < vector_active(token->multiple);
+       multiple_index++)
+    {
+      word_token = vector_slot(token->multiple, multiple_index);
+
+      word_match = cmd_word_match(word_token, matcher->filter, word);
+      if (word_match == no_match)
+        continue;
+
+      cmd_matcher_record_match(matcher, word_match, word_token);
+
+      if (word_match > multiple_match)
+        {
+          multiple_match = word_match;
+          arg = word;
+        }
+      /* To mimic the behavior of the old command implementation, we
+       * tolerate any ambiguities here :/ */
+    }
+
+  matcher->word_index++;
+
+  if (multiple_match == no_match)
+    return MATCHER_NO_MATCH;
+
+  if (push_argument(argc, argv, arg))
+    return MATCHER_EXCEED_ARGC_MAX;
+
+  return MATCHER_OK;
+}
+
+static enum matcher_rv
+cmd_matcher_read_keywords(struct cmd_matcher *matcher,
+                          struct cmd_token *token,
+                          vector args_vector)
+{
+  unsigned int i;
+  unsigned long keyword_mask;
+  unsigned int keyword_found;
+  enum match_type keyword_match;
+  enum match_type word_match;
+  vector keyword_vector;
+  struct cmd_token *word_token;
+  const char *word;
+  int keyword_argc;
+  const char **keyword_argv;
+  enum matcher_rv rv = MATCHER_NO_MATCH;
+
+  keyword_mask = 0;
+  while (1)
+    {
+      if (!cmd_matcher_words_left(matcher))
+        return MATCHER_OK;
+
+      word = cmd_matcher_get_word(matcher);
+
+      keyword_found = -1;
+      keyword_match = no_match;
+      for (i = 0; i < vector_active(token->keyword); i++)
+        {
+          if (keyword_mask & (1 << i))
+            continue;
+
+          keyword_vector = vector_slot(token->keyword, i);
+          word_token = vector_slot(keyword_vector, 0);
+
+          word_match = cmd_word_match(word_token, matcher->filter, word);
+          if (word_match == no_match)
+            continue;
+
+          cmd_matcher_record_match(matcher, word_match, word_token);
+
+          if (word_match > keyword_match)
+            {
+              keyword_match = word_match;
+              keyword_found = i;
+            }
+          else if (word_match == keyword_match)
+            {
+              if (matcher->word_index != matcher->index || args_vector)
+                return MATCHER_AMBIGUOUS;
+            }
+        }
+
+      if (keyword_found == (unsigned int)-1)
+        return MATCHER_NO_MATCH;
+
+      matcher->word_index++;
+
+      if (matcher->word_index > matcher->index)
+        return MATCHER_OK;
+
+      keyword_mask |= (1 << keyword_found);
+
+      if (args_vector)
+        {
+          keyword_argc = 0;
+          keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
+          /* We use -1 as a marker for unused fields as NULL might be a valid value */
+          for (i = 0; i < CMD_ARGC_MAX + 1; i++)
+            keyword_argv[i] = (void*)-1;
+          vector_set_index(args_vector, keyword_found, keyword_argv);
+        }
+      else
+        {
+          keyword_argv = NULL;
+        }
+
+      keyword_vector = vector_slot(token->keyword, keyword_found);
+      /* the keyword itself is at 0. We are only interested in the arguments,
+       * so start counting at 1. */
+      for (i = 1; i < vector_active(keyword_vector); i++)
+        {
+          word_token = vector_slot(keyword_vector, i);
+
+          switch (word_token->type)
+            {
+            case TOKEN_TERMINAL:
+              rv = cmd_matcher_match_terminal(matcher, word_token,
+                                              &keyword_argc, keyword_argv);
+              break;
+            case TOKEN_MULTIPLE:
+              rv = cmd_matcher_match_multiple(matcher, word_token,
+                                              &keyword_argc, keyword_argv);
+              break;
+            case TOKEN_KEYWORD:
+              assert(!"Keywords should never be nested.");
+              break;
+            }
+
+          if (MATCHER_ERROR(rv))
+            return rv;
+
+          if (matcher->word_index > matcher->index)
+            return MATCHER_OK;
+        }
+    }
+  /* not reached */
+}
+
+static enum matcher_rv
+cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
+                               struct cmd_token *token,
+                               int *argc, const char **argv,
+                               vector keyword_args_vector)
+{
+  unsigned int i, j;
+  const char **keyword_args;
+  vector keyword_vector;
+  struct cmd_token *word_token;
+  const char *arg;
+  enum matcher_rv rv;
+
+  rv = MATCHER_OK;
+
+  if (keyword_args_vector == NULL)
+    return rv;
+
+  for (i = 0; i < vector_active(token->keyword); i++)
+    {
+      keyword_vector = vector_slot(token->keyword, i);
+      keyword_args = vector_lookup(keyword_args_vector, i);
+
+      if (vector_active(keyword_vector) == 1)
+        {
+          /* this is a keyword without arguments */
+          if (keyword_args)
+            {
+              word_token = vector_slot(keyword_vector, 0);
+              arg = word_token->cmd;
+            }
+          else
+            {
+              arg = NULL;
+            }
+
+          if (push_argument(argc, argv, arg))
+            rv = MATCHER_EXCEED_ARGC_MAX;
+        }
+      else
+        {
+          /* this is a keyword with arguments */
+          if (keyword_args)
+            {
+              /* the keyword was present, so just fill in the arguments */
+              for (j = 0; keyword_args[j] != (void*)-1; j++)
+                if (push_argument(argc, argv, keyword_args[j]))
+                  rv = MATCHER_EXCEED_ARGC_MAX;
+              XFREE(MTYPE_TMP, keyword_args);
+            }
+          else
+            {
+              /* the keyword was not present, insert NULL for the arguments
+               * the keyword would have taken. */
+              for (j = 1; j < vector_active(keyword_vector); j++)
+                {
+                  word_token = vector_slot(keyword_vector, j);
+                  if ((word_token->type == TOKEN_TERMINAL
+                       && TERMINAL_RECORD (word_token->terminal))
+                      || word_token->type == TOKEN_MULTIPLE)
+                    {
+                      if (push_argument(argc, argv, NULL))
+                        rv = MATCHER_EXCEED_ARGC_MAX;
+                    }
+                }
+            }
+        }
+    }
+  vector_free(keyword_args_vector);
+  return rv;
+}
+
+static enum matcher_rv
+cmd_matcher_match_keyword(struct cmd_matcher *matcher,
+                          struct cmd_token *token,
+                          int *argc, const char **argv)
+{
+  vector keyword_args_vector;
+  enum matcher_rv reader_rv;
+  enum matcher_rv builder_rv;
+
+  assert(token->type == TOKEN_KEYWORD);
+
+  if (argc && argv)
+    keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
+  else
+    keyword_args_vector = NULL;
+
+  reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
+  builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
+                                              argv, keyword_args_vector);
+  /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
+
+  if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
+    return builder_rv;
+
+  return reader_rv;
+}
+
+static void
+cmd_matcher_init(struct cmd_matcher *matcher,
+                 struct cmd_element *cmd,
+                 enum filter_type filter,
+                 vector vline,
+                 unsigned int index,
+                 enum match_type *match_type,
+                 vector *match)
+{
+  memset(matcher, 0, sizeof(*matcher));
+
+  matcher->cmd = cmd;
+  matcher->filter = filter;
+  matcher->vline = vline;
+  matcher->index = index;
+
+  matcher->match_type = match_type;
+  if (matcher->match_type)
+    *matcher->match_type = no_match;
+  matcher->match = match;
+
+  matcher->word_index = 0;
+}
+
+static enum matcher_rv
+cmd_element_match(struct cmd_element *cmd_element,
+                  enum filter_type filter,
+                  vector vline,
+                  unsigned int index,
+                  enum match_type *match_type,
+                  vector *match,
+                  int *argc,
+                  const char **argv)
+{
+  struct cmd_matcher matcher;
+  unsigned int token_index;
+  enum matcher_rv rv = MATCHER_NO_MATCH;
+
+  cmd_matcher_init(&matcher, cmd_element, filter,
+                   vline, index, match_type, match);
+
+  if (argc != NULL)
+    *argc = 0;
+
+  for (token_index = 0;
+       token_index < vector_active(cmd_element->tokens);
+       token_index++)
+    {
+      struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
+
+      switch (token->type)
+        {
+        case TOKEN_TERMINAL:
+          rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
+          break;
+        case TOKEN_MULTIPLE:
+          rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
+          break;
+        case TOKEN_KEYWORD:
+          rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
+        }
+
+      if (MATCHER_ERROR(rv))
+        return rv;
+
+      if (matcher.word_index > index)
+        return MATCHER_OK;
+    }
+
+  /* return MATCHER_COMPLETE if all words were consumed */
+  if (matcher.word_index >= vector_active(vline))
+    return MATCHER_COMPLETE;
+
+  /* return MATCHER_COMPLETE also if only an empty word is left. */
+  if (matcher.word_index == vector_active(vline) - 1
+      && (!vector_slot(vline, matcher.word_index)
+          || !strlen((char*)vector_slot(vline, matcher.word_index))))
+    return MATCHER_COMPLETE;
+
+  return MATCHER_NO_MATCH; /* command is too long to match */
+}
+
+/**
+ * Filter a given vector of commands against a given commandline and
+ * calculate possible completions.
+ *
+ * @param commands A vector of struct cmd_element*. Commands that don't
+ *                 match against the given command line will be overwritten
+ *                 with NULL in that vector.
+ * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
+ *               determines how incomplete commands are handled, compare with
+ *               cmd_word_match for details.
+ * @param vline A vector of char* containing the tokenized commandline.
+ * @param index Only match up to the given token of the commandline.
+ * @param match_type Record the type of the best match here.
+ * @param matches Record the matches here. For each cmd_element in the commands
+ *                vector, a match vector will be created in the matches vector.
+ *                That vector will contain all struct command_token* of the
+ *                cmd_element which matched against the given vline at the given
+ *                index.
+ * @return A code specifying if an error occured. If all went right, it's
+ *         CMD_SUCCESS.
+ */
+static int
+cmd_vector_filter(vector commands,
+                  enum filter_type filter,
+                  vector vline,
+                  unsigned int index,
+                  enum match_type *match_type,
+                  vector *matches)
+{
+  unsigned int i;
+  struct cmd_element *cmd_element;
+  enum match_type best_match;
+  enum match_type element_match;
+  enum matcher_rv matcher_rv;
+
+  best_match = no_match;
+  *matches = vector_init(VECTOR_MIN_SIZE);
+
+  for (i = 0; i < vector_active (commands); i++)
+    if ((cmd_element = vector_slot (commands, i)) != NULL)
+      {
+        vector_set_index(*matches, i, NULL);
+        matcher_rv = cmd_element_match(cmd_element, filter,
+                                       vline, index,
+                                       &element_match,
+                                       (vector*)&vector_slot(*matches, i),
+                                       NULL, NULL);
+        if (MATCHER_ERROR(matcher_rv))
+          {
+            vector_slot(commands, i) = NULL;
+            if (matcher_rv == MATCHER_AMBIGUOUS)
+              return CMD_ERR_AMBIGUOUS;
+            if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
+              return CMD_ERR_EXEED_ARGC_MAX;
+          }
+        else if (element_match > best_match)
+          {
+            best_match = element_match;
+          }
+      }
+  *match_type = best_match;
+  return CMD_SUCCESS;
+}
+
+/**
+ * Check whether a given commandline is complete if used for a specific
+ * cmd_element.
+ *
+ * @param cmd_element A cmd_element against which the commandline should be
+ *                    checked.
+ * @param vline The tokenized commandline.
+ * @return 1 if the given commandline is complete, 0 otherwise.
+ */
+static int
+cmd_is_complete(struct cmd_element *cmd_element,
+                vector vline)
+{
+  enum matcher_rv rv;
+
+  rv = cmd_element_match(cmd_element,
+                         FILTER_RELAXED,
+                         vline, -1,
+                         NULL, NULL,
+                         NULL, NULL);
+  return (rv == MATCHER_COMPLETE);
+}
+
+/**
+ * Parse a given commandline and construct a list of arguments for the
+ * given command_element.
+ *
+ * @param cmd_element The cmd_element for which we want to construct arguments.
+ * @param vline The tokenized commandline.
+ * @param argc Where to store the argument count.
+ * @param argv Where to store the argument list. Should be at least
+ *             CMD_ARGC_MAX elements long.
+ * @return CMD_SUCCESS if everything went alright, an error otherwise.
+ */
+static int
+cmd_parse(struct cmd_element *cmd_element,
+          vector vline,
+          int *argc, const char **argv)
+{
+  enum matcher_rv rv = cmd_element_match(cmd_element,
+                                         FILTER_RELAXED,
+                                         vline, -1,
+                                         NULL, NULL,
+                                         argc, argv);
+  switch (rv)
+    {
+    case MATCHER_COMPLETE:
+      return CMD_SUCCESS;
+
+    case MATCHER_NO_MATCH:
+      return CMD_ERR_NO_MATCH;
+
+    case MATCHER_AMBIGUOUS:
+      return CMD_ERR_AMBIGUOUS;
+
+    case MATCHER_EXCEED_ARGC_MAX:
+      return CMD_ERR_EXEED_ARGC_MAX;
+
+    default:
+      return CMD_ERR_INCOMPLETE;
+    }
+}
+
+/* Check ambiguous match */
+static int
+is_cmd_ambiguous (vector cmd_vector,
+                  const char *command,
+                  vector matches,
+                  enum match_type type)
+{
+  unsigned int i;
+  unsigned int j;
+  const char *str = NULL;
+  const char *matched = NULL;
+  vector match_vector;
+  struct cmd_token *cmd_token;
+
+  if (command == NULL)
+    command = "";
+
+  for (i = 0; i < vector_active (matches); i++)
+    if ((match_vector = vector_slot (matches, i)) != NULL)
+      {
+       int match = 0;
+
+       for (j = 0; j < vector_active (match_vector); j++)
+         if ((cmd_token = vector_slot (match_vector, j)) != NULL)
+           {
+             enum match_type ret;
+
+             assert(cmd_token->type == TOKEN_TERMINAL);
+             if (cmd_token->type != TOKEN_TERMINAL)
+               continue;
+
+             str = cmd_token->cmd;
+
+             switch (type)
+               {
+               case exact_match:
+                 if (!TERMINAL_RECORD (cmd_token->terminal)
+                     && strcmp (command, str) == 0)
+                   match++;
+                 break;
+               case partly_match:
+                 if (!TERMINAL_RECORD (cmd_token->terminal)
+                     && strncmp (command, str, strlen (command)) == 0)
+                   {
+                     if (matched && strcmp (matched, str) != 0)
+                       return 1;       /* There is ambiguous match. */
+                     else
+                       matched = str;
+                     match++;
+                   }
+                 break;
+               case range_match:
+                 if (cmd_range_match (str, command))
+                   {
+                     if (matched && strcmp (matched, str) != 0)
+                       return 1;
+                     else
+                       matched = str;
+                     match++;
+                   }
+                 break;
+#ifdef HAVE_IPV6
+               case ipv6_match:
+                 if (cmd_token->terminal == TERMINAL_IPV6)
+                   match++;
+                 break;
+               case ipv6_prefix_match:
+                 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
+                   {
+                     if (ret == partly_match)
+                       return 2;       /* There is incomplete match. */
+
+                     match++;
+                   }
+                 break;
+#endif /* HAVE_IPV6 */
+               case ipv4_match:
+                 if (cmd_token->terminal == TERMINAL_IPV4)
+                   match++;
+                 break;
+               case ipv4_prefix_match:
+                 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
+                   {
+                     if (ret == partly_match)
+                       return 2;       /* There is incomplete match. */
+
+                     match++;
+                   }
+                 break;
+               case extend_match:
+                 if (TERMINAL_RECORD (cmd_token->terminal))
+                   match++;
+                 break;
+               case no_match:
+               default:
+                 break;
+               }
+           }
+       if (!match)
+         vector_slot (cmd_vector, i) = NULL;
+      }
+  return 0;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+static const char *
+cmd_entry_function (const char *src, struct cmd_token *token)
+{
+  const char *dst = token->cmd;
+
+  /* Skip variable arguments. */
+  if (TERMINAL_RECORD (token->terminal))
+    return NULL;
+
+  /* In case of 'command \t', given src is NULL string. */
+  if (src == NULL)
+    return dst;
+
+  /* Matched with input string. */
+  if (strncmp (src, dst, strlen (src)) == 0)
+    return dst;
+
+  return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+   CMD_VARIABLE for '?' key processing */
+static const char *
+cmd_entry_function_desc (const char *src, struct cmd_token *token)
+{
+  const char *dst = token->cmd;
+
+  switch (token->terminal)
+    {
+      case TERMINAL_VARARG:
+        return dst;
+
+      case TERMINAL_RANGE:
+        if (cmd_range_match (dst, src))
+          return dst;
+        else
+          return NULL;
+
+      case TERMINAL_IPV6:
+        if (cmd_ipv6_match (src))
+          return dst;
+        else
+          return NULL;
+
+      case TERMINAL_IPV6_PREFIX:
+        if (cmd_ipv6_prefix_match (src))
+          return dst;
+        else
+          return NULL;
+
+      case TERMINAL_IPV4:
+        if (cmd_ipv4_match (src))
+          return dst;
+        else
+          return NULL;
+
+      case TERMINAL_IPV4_PREFIX:
+        if (cmd_ipv4_prefix_match (src))
+          return dst;
+        else
+          return NULL;
+
+      /* Optional or variable commands always match on '?' */
+      case TERMINAL_OPTION:
+      case TERMINAL_VARIABLE:
+        return dst;
+
+      case TERMINAL_LITERAL:
+        /* In case of 'command \t', given src is NULL string. */
+        if (src == NULL)
+          return dst;
+
+        if (strncmp (src, dst, strlen (src)) == 0)
+          return dst;
+        else
+          return NULL;
+
+      default:
+        assert(0);
+        return NULL;
+    }
+}
+
+/**
+ * Check whether a string is already present in a vector of strings.
+ * @param v A vector of char*.
+ * @param str A char*.
+ * @return 0 if str is already present in the vector, 1 otherwise.
+ */
+static int
+cmd_unique_string (vector v, const char *str)
+{
+  unsigned int i;
+  char *match;
+
+  for (i = 0; i < vector_active (v); i++)
+    if ((match = vector_slot (v, i)) != NULL)
+      if (strcmp (match, str) == 0)
+       return 0;
+  return 1;
+}
+
+/**
+ * Check whether a struct cmd_token matching a given string is already
+ * present in a vector of struct cmd_token.
+ * @param v A vector of struct cmd_token*.
+ * @param str A char* which should be searched for.
+ * @return 0 if there is a struct cmd_token* with its cmd matching str,
+ *         1 otherwise.
+ */
+static int
+desc_unique_string (vector v, const char *str)
+{
+  unsigned int i;
+  struct cmd_token *token;
+
+  for (i = 0; i < vector_active (v); i++)
+    if ((token = vector_slot (v, i)) != NULL)
+      if (strcmp (token->cmd, str) == 0)
+       return 0;
+  return 1;
+}
+
+static int 
+cmd_try_do_shortcut (enum node_type node, char* first_word) {
+  if ( first_word != NULL &&
+       node != AUTH_NODE &&
+       node != VIEW_NODE &&
+       node != AUTH_ENABLE_NODE &&
+       node != ENABLE_NODE &&
+       node != RESTRICTED_NODE &&
+       0 == strcmp( "do", first_word ) )
+    return 1;
+  return 0;
+}
+
+static void
+cmd_matches_free(vector *matches)
+{
+  unsigned int i;
+  vector cmd_matches;
+
+  for (i = 0; i < vector_active(*matches); i++)
+    if ((cmd_matches = vector_slot(*matches, i)) != NULL)
+      vector_free(cmd_matches);
+  vector_free(*matches);
+  *matches = NULL;
+}
+
+static int
+cmd_describe_cmp(const void *a, const void *b)
+{
+  const struct cmd_token *first = *(struct cmd_token * const *)a;
+  const struct cmd_token *second = *(struct cmd_token * const *)b;
+
+  return strcmp(first->cmd, second->cmd);
+}
+
+static void
+cmd_describe_sort(vector matchvec)
+{
+  qsort(matchvec->index, vector_active(matchvec),
+        sizeof(void*), cmd_describe_cmp);
+}
+
+/* '?' describe command support. */
+static vector
+cmd_describe_command_real (vector vline, struct vty *vty, int *status)
+{
+  unsigned int i;
+  vector cmd_vector;
+#define INIT_MATCHVEC_SIZE 10
+  vector matchvec;
+  struct cmd_element *cmd_element;
+  unsigned int index;
+  int ret;
+  enum match_type match;
+  char *command;
+  vector matches = NULL;
+  vector match_vector;
+  uint32_t command_found = 0;
+  const char *last_word;
+
+  /* Set index. */
+  if (vector_active (vline) == 0)
+    {
+      *status = CMD_ERR_NO_MATCH;
+      return NULL;
+    }
+
+  index = vector_active (vline) - 1;
+
+  /* Make copy vector of current node's command vector. */
+  cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+
+  /* Prepare match vector */
+  matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+  /* Filter commands and build a list how they could possibly continue. */
+  for (i = 0; i <= index; i++)
+    {
+      command = vector_slot (vline, i);
+
+      if (matches)
+       cmd_matches_free(&matches);
+
+      ret = cmd_vector_filter(cmd_vector,
+                             FILTER_RELAXED,
+                             vline, i,
+                             &match,
+                             &matches);
+
+      if (ret != CMD_SUCCESS)
+       {
+         vector_free (cmd_vector);
+         vector_free (matchvec);
+         cmd_matches_free(&matches);
+         *status = ret;
+         return NULL;
+       }
+
+      /* The last match may well be ambigious, so break here */
+      if (i == index)
+       break;
+
+      if (match == vararg_match)
+       {
+         /* We found a vararg match - so we can throw out the current matches here
+          * and don't need to continue checking the command input */
+         unsigned int j, k;
+
+         for (j = 0; j < vector_active (matches); j++)
+           if ((match_vector = vector_slot (matches, j)) != NULL)
+             for (k = 0; k < vector_active (match_vector); k++)
+               {
+                 struct cmd_token *token = vector_slot (match_vector, k);
+                 vector_set (matchvec, token);
+               }
+
+         *status = CMD_SUCCESS;
+         vector_set(matchvec, &token_cr);
+         vector_free (cmd_vector);
+         cmd_matches_free(&matches);
+         cmd_describe_sort(matchvec);
+         return matchvec;
+       }
+
+      ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
+      if (ret == 1)
+       {
+         vector_free (cmd_vector);
+         vector_free (matchvec);
+         cmd_matches_free(&matches);
+         *status = CMD_ERR_AMBIGUOUS;
+         return NULL;
+       }
+      else if (ret == 2)
+       {
+         vector_free (cmd_vector);
+         vector_free (matchvec);
+         cmd_matches_free(&matches);
+         *status = CMD_ERR_NO_MATCH;
+         return NULL;
+       }
+    }
+
+  /* Make description vector. */
+  for (i = 0; i < vector_active (matches); i++)
+    {
+      if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
+       {
+         unsigned int j;
+         vector vline_trimmed;
+
+         command_found++;
+         last_word = vector_slot(vline, vector_active(vline) - 1);
+         if (last_word == NULL || !strlen(last_word))
+           {
+             vline_trimmed = vector_copy(vline);
+             vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
+
+             if (cmd_is_complete(cmd_element, vline_trimmed)
+                 && desc_unique_string(matchvec, command_cr))
+               {
+                 if (match != vararg_match)
+                   vector_set(matchvec, &token_cr);
+               }
+
+             vector_free(vline_trimmed);
+           }
+
+         match_vector = vector_slot (matches, i);
+         if (match_vector)
+           {
+             for (j = 0; j < vector_active(match_vector); j++)
+               {
+                 struct cmd_token *token = vector_slot(match_vector, j);
+                 const char *string;
+
+                 string = cmd_entry_function_desc(command, token);
+                 if (string && desc_unique_string(matchvec, string))
+                   vector_set(matchvec, token);
+               }
+           }
+       }
+    }
+
+  /*
+   * We can get into this situation when the command is complete
+   * but the last part of the command is an optional piece of
+   * the cli.
+   */
+  last_word = vector_slot(vline, vector_active(vline) - 1);
+  if (command_found == 0 && (last_word == NULL || !strlen(last_word)))
+    vector_set(matchvec, &token_cr);
+
+  vector_free (cmd_vector);
+  cmd_matches_free(&matches);
+
+  if (vector_slot (matchvec, 0) == NULL)
+    {
+      vector_free (matchvec);
+      *status = CMD_ERR_NO_MATCH;
+      return NULL;
+    }
+
+  *status = CMD_SUCCESS;
+  cmd_describe_sort(matchvec);
+  return matchvec;
+}
+
+vector
+cmd_describe_command (vector vline, struct vty *vty, int *status)
+{
+  vector ret;
+
+  if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+    {
+      enum node_type onode;
+      vector shifted_vline;
+      unsigned int index;
+
+      onode = vty->node;
+      vty->node = ENABLE_NODE;
+      /* We can try it on enable node, cos' the vty is authenticated */
+
+      shifted_vline = vector_init (vector_count(vline));
+      /* use memcpy? */
+      for (index = 1; index < vector_active (vline); index++) 
+       {
+         vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
+       }
+
+      ret = cmd_describe_command_real (shifted_vline, vty, status);
+
+      vector_free(shifted_vline);
+      vty->node = onode;
+      return ret;
+  }
+
+
+  return cmd_describe_command_real (vline, vty, status);
+}
+
+
+/* Check LCD of matched command. */
+static int
+cmd_lcd (char **matched)
+{
+  int i;
+  int j;
+  int lcd = -1;
+  char *s1, *s2;
+  char c1, c2;
+
+  if (matched[0] == NULL || matched[1] == NULL)
+    return 0;
+
+  for (i = 1; matched[i] != NULL; i++)
+    {
+      s1 = matched[i - 1];
+      s2 = matched[i];
+
+      for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
+       if (c1 != c2)
+         break;
+
+      if (lcd < 0)
+       lcd = j;
+      else
+       {
+         if (lcd > j)
+           lcd = j;
+       }
+    }
+  return lcd;
+}
+
+static int
+cmd_complete_cmp(const void *a, const void *b)
+{
+  const char *first = *(char * const *)a;
+  const char *second = *(char * const *)b;
+
+  if (!first)
+    {
+      if (!second)
+        return 0;
+      return 1;
+    }
+  if (!second)
+    return -1;
+
+  return strcmp(first, second);
+}
+
+static void
+cmd_complete_sort(vector matchvec)
+{
+  qsort(matchvec->index, vector_active(matchvec),
+        sizeof(void*), cmd_complete_cmp);
+}
+
+/* Command line completion support. */
+static char **
+cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib)
+{
+  unsigned int i;
+  vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+#define INIT_MATCHVEC_SIZE 10
+  vector matchvec;
+  unsigned int index;
+  char **match_str;
+  struct cmd_token *token;
+  char *command;
+  int lcd;
+  vector matches = NULL;
+  vector match_vector;
+
+  if (vector_active (vline) == 0)
+    {
+      vector_free (cmd_vector);
+      *status = CMD_ERR_NO_MATCH;
+      return NULL;
+    }
+  else
+    index = vector_active (vline) - 1;
+
+  /* First, filter by command string */
+  for (i = 0; i <= index; i++)
+    {
+      command = vector_slot (vline, i);
+      enum match_type match;
+      int ret;
+
+      if (matches)
+        cmd_matches_free(&matches);
+
+      /* First try completion match, if there is exactly match return 1 */
+      ret = cmd_vector_filter(cmd_vector,
+                             FILTER_RELAXED,
+                             vline, i,
+                             &match,
+                             &matches);
+
+      if (ret != CMD_SUCCESS)
+       {
+         vector_free(cmd_vector);
+         cmd_matches_free(&matches);
+         *status = ret;
+         return NULL;
+       }
+
+      /* Break here - the completion mustn't be checked to be non-ambiguous */
+      if (i == index)
+       break;
+
+      /* If there is exact match then filter ambiguous match else check
+        ambiguousness. */
+      ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
+      if (ret == 1)
+       {
+         vector_free (cmd_vector);
+         cmd_matches_free(&matches);
+         *status = CMD_ERR_AMBIGUOUS;
+         return NULL;
+       }
+    }
+  
+  /* Prepare match vector. */
+  matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+  /* Build the possible list of continuations into a list of completions */
+  for (i = 0; i < vector_active (matches); i++)
+    if ((match_vector = vector_slot (matches, i)))
+      {
+       const char *string;
+       unsigned int j;
+
+       for (j = 0; j < vector_active (match_vector); j++)
+         if ((token = vector_slot (match_vector, j)))
+            {
+              string = cmd_entry_function (vector_slot (vline, index), token);
+              if (string && cmd_unique_string (matchvec, string))
+                vector_set (matchvec, (islib != 0 ?
+                                      XSTRDUP (MTYPE_TMP, string) :
+                                      strdup (string) /* rl freed */));
+            }
+      }
+
+  /* We don't need cmd_vector any more. */
+  vector_free (cmd_vector);
+  cmd_matches_free(&matches);
+
+  /* No matched command */
+  if (vector_slot (matchvec, 0) == NULL)
+    {
+      vector_free (matchvec);
+
+      /* In case of 'command \t' pattern.  Do you need '?' command at
+         the end of the line. */
+      if (vector_slot (vline, index) == '\0')
+       *status = CMD_ERR_NOTHING_TODO;
+      else
+       *status = CMD_ERR_NO_MATCH;
+      return NULL;
+    }
+
+  /* Only one matched */
+  if (vector_slot (matchvec, 1) == NULL)
+    {
+      match_str = (char **) matchvec->index;
+      vector_only_wrapper_free (matchvec);
+      *status = CMD_COMPLETE_FULL_MATCH;
+      return match_str;
+    }
+  /* Make it sure last element is NULL. */
+  vector_set (matchvec, NULL);
+
+  /* Check LCD of matched strings. */
+  if (vector_slot (vline, index) != NULL)
+    {
+      lcd = cmd_lcd ((char **) matchvec->index);
+
+      if (lcd)
+       {
+         int len = strlen (vector_slot (vline, index));
+
+         if (len < lcd)
+           {
+             char *lcdstr;
+
+             lcdstr = (islib != 0 ?
+                        XMALLOC (MTYPE_TMP, lcd + 1) :
+                        malloc(lcd + 1));
+             memcpy (lcdstr, matchvec->index[0], lcd);
+             lcdstr[lcd] = '\0';
+
+             /* Free matchvec. */
+             for (i = 0; i < vector_active (matchvec); i++)
+                {
+                  if (vector_slot (matchvec, i))
+                    {
+                      if (islib != 0)
+                        XFREE (MTYPE_TMP, vector_slot (matchvec, i));
+                      else
+                        free (vector_slot (matchvec, i));
+                    }
+                }
+             vector_free (matchvec);
+
+             /* Make new matchvec. */
+             matchvec = vector_init (INIT_MATCHVEC_SIZE);
+             vector_set (matchvec, lcdstr);
+             match_str = (char **) matchvec->index;
+             vector_only_wrapper_free (matchvec);
+
+             *status = CMD_COMPLETE_MATCH;
+             return match_str;
+           }
+       }
+    }
+
+  match_str = (char **) matchvec->index;
+  cmd_complete_sort(matchvec);
+  vector_only_wrapper_free (matchvec);
+  *status = CMD_COMPLETE_LIST_MATCH;
+  return match_str;
+}
+
+char **
+cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)
+{
+  char **ret;
+
+  if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+    {
+      enum node_type onode;
+      vector shifted_vline;
+      unsigned int index;
+
+      onode = vty->node;
+      vty->node = ENABLE_NODE;
+      /* We can try it on enable node, cos' the vty is authenticated */
+
+      shifted_vline = vector_init (vector_count(vline));
+      /* use memcpy? */
+      for (index = 1; index < vector_active (vline); index++) 
+       {
+         vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
+       }
+
+      ret = cmd_complete_command_real (shifted_vline, vty, status, islib);
+
+      vector_free(shifted_vline);
+      vty->node = onode;
+      return ret;
+  }
+
+  return cmd_complete_command_real (vline, vty, status, islib);
+}
+
+char **
+cmd_complete_command (vector vline, struct vty *vty, int *status)
+{
+  return cmd_complete_command_lib (vline, vty, status, 0);
+}
+
+/* return parent node */
+/* MUST eventually converge on CONFIG_NODE */
+enum node_type
+node_parent ( enum node_type node )
+{
+  enum node_type ret;
+
+  assert (node > CONFIG_NODE);
+
+  switch (node)
+    {
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+    case BGP_IPV4_NODE:
+    case BGP_IPV4M_NODE:
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+      ret = BGP_NODE;
+      break;
+    case KEYCHAIN_KEY_NODE:
+      ret = KEYCHAIN_NODE;
+      break;
+    case LINK_PARAMS_NODE:
+      ret = INTERFACE_NODE;
+      break;
+    default:
+      ret = CONFIG_NODE;
+      break;
+    }
+
+  return ret;
+}
+
+/* Execute command by argument vline vector. */
+static int
+cmd_execute_command_real (vector vline,
+                         enum filter_type filter,
+                         struct vty *vty,
+                         struct cmd_element **cmd)
+{
+  unsigned int i;
+  unsigned int index;
+  vector cmd_vector;
+  struct cmd_element *cmd_element;
+  struct cmd_element *matched_element;
+  unsigned int matched_count, incomplete_count;
+  int argc;
+  const char *argv[CMD_ARGC_MAX];
+  enum match_type match = 0;
+  char *command;
+  int ret;
+  vector matches;
+
+  /* Make copy of command elements. */
+  cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+
+  for (index = 0; index < vector_active (vline); index++)
+    {
+      command = vector_slot (vline, index);
+      ret = cmd_vector_filter(cmd_vector,
+                             filter,
+                             vline, index,
+                             &match,
+                             &matches);
+
+      if (ret != CMD_SUCCESS)
+       {
+         cmd_matches_free(&matches);
+         return ret;
+       }
+
+      if (match == vararg_match)
+       {
+         cmd_matches_free(&matches);
+         break;
+       }
+
+      ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
+      cmd_matches_free(&matches);
+
+      if (ret == 1)
+       {
+         vector_free(cmd_vector);
+         return CMD_ERR_AMBIGUOUS;
+       }
+      else if (ret == 2)
+       {
+         vector_free(cmd_vector);
+         return CMD_ERR_NO_MATCH;
+       }
+    }
+
+  /* Check matched count. */
+  matched_element = NULL;
+  matched_count = 0;
+  incomplete_count = 0;
+
+  for (i = 0; i < vector_active (cmd_vector); i++)
+    if ((cmd_element = vector_slot (cmd_vector, i)))
+      {
+       if (cmd_is_complete(cmd_element, vline))
+         {
+           matched_element = cmd_element;
+           matched_count++;
+         }
+       else
+         {
+           incomplete_count++;
+         }
+      }
+
+  /* Finish of using cmd_vector. */
+  vector_free (cmd_vector);
+
+  /* To execute command, matched_count must be 1. */
+  if (matched_count == 0)
+    {
+      if (incomplete_count)
+       return CMD_ERR_INCOMPLETE;
+      else
+       return CMD_ERR_NO_MATCH;
+    }
+
+  if (matched_count > 1)
+    return CMD_ERR_AMBIGUOUS;
+
+  ret = cmd_parse(matched_element, vline, &argc, argv);
+  if (ret != CMD_SUCCESS)
+    return ret;
+
+  /* For vtysh execution. */
+  if (cmd)
+    *cmd = matched_element;
+
+  if (matched_element->daemon)
+    return CMD_SUCCESS_DAEMON;
+
+  /* Execute matched command. */
+  return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+/**
+ * Execute a given command, handling things like "do ..." and checking
+ * whether the given command might apply at a parent node if doesn't
+ * apply for the current node.
+ *
+ * @param vline Command line input, vector of char* where each element is
+ *              one input token.
+ * @param vty The vty context in which the command should be executed.
+ * @param cmd Pointer where the struct cmd_element of the matched command
+ *            will be stored, if any. May be set to NULL if this info is
+ *            not needed.
+ * @param vtysh If set != 0, don't lookup the command at parent nodes.
+ * @return The status of the command that has been executed or an error code
+ *         as to why no command could be executed.
+ */
+int
+cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
+                    int vtysh) {
+  int ret, saved_ret, tried = 0;
+  enum node_type onode, try_node;
+
+  onode = try_node = vty->node;
+
+  if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+    {
+      vector shifted_vline;
+      unsigned int index;
+
+      vty->node = ENABLE_NODE;
+      /* We can try it on enable node, cos' the vty is authenticated */
+
+      shifted_vline = vector_init (vector_count(vline));
+      /* use memcpy? */
+      for (index = 1; index < vector_active (vline); index++) 
+       {
+         vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
+       }
+
+      ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
+
+      vector_free(shifted_vline);
+      vty->node = onode;
+      return ret;
+  }
+
+
+  saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
+
+  if (vtysh)
+    return saved_ret;
+
+  /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
+  while ( ret != CMD_SUCCESS && ret != CMD_WARNING 
+         && vty->node > CONFIG_NODE )
+    {
+      try_node = node_parent(try_node);
+      vty->node = try_node;
+      ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
+      tried = 1;
+      if (ret == CMD_SUCCESS || ret == CMD_WARNING)
+       {
+         /* succesfull command, leave the node as is */
+         return ret;
+       }
+    }
+  /* no command succeeded, reset the vty to the original node and
+     return the error for this node */
+  if ( tried )
+    vty->node = onode;
+  return saved_ret;
+}
+
+/**
+ * Execute a given command, matching it strictly against the current node.
+ * This mode is used when reading config files.
+ *
+ * @param vline Command line input, vector of char* where each element is
+ *              one input token.
+ * @param vty The vty context in which the command should be executed.
+ * @param cmd Pointer where the struct cmd_element* of the matched command
+ *            will be stored, if any. May be set to NULL if this info is
+ *            not needed.
+ * @return The status of the command that has been executed or an error code
+ *         as to why no command could be executed.
+ */
+int
+cmd_execute_command_strict (vector vline, struct vty *vty,
+                           struct cmd_element **cmd)
+{
+  return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
+}
+
+/**
+ * Parse one line of config, walking up the parse tree attempting to find a match
+ *
+ * @param vty The vty context in which the command should be executed.
+ * @param cmd Pointer where the struct cmd_element* of the match command
+ *            will be stored, if any.  May be set to NULL if this info is
+ *            not needed.
+ * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
+ *                   or not.
+ * @return The status of the command that has been executed or an error code
+ *         as to why no command could be executed.
+ */
+int
+command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
+{
+  vector vline;
+  int saved_node;
+  int ret;
+
+  vline = cmd_make_strvec (vty->buf);
+
+  /* In case of comment line */
+  if (vline == NULL)
+    return CMD_SUCCESS;
+
+  /* Execute configuration command : this is strict match */
+  ret = cmd_execute_command_strict (vline, vty, cmd);
+
+  saved_node = vty->node;
+
+  while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
+        ret != CMD_SUCCESS && ret != CMD_WARNING &&
+        ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) {
+    vty->node = node_parent(vty->node);
+    ret = cmd_execute_command_strict (vline, vty, NULL);
+  }
+
+  // If climbing the tree did not work then ignore the command and
+  // stay at the same node
+  if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
+      ret != CMD_SUCCESS && ret != CMD_WARNING &&
+      ret != CMD_ERR_NOTHING_TODO)
+    {
+      vty->node = saved_node;
+    }
+
+  cmd_free_strvec (vline);
+
+  return ret;
+}
+
+/* Configration make from file. */
+int
+config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
+{
+  int ret;
+  *line_num = 0;
+
+  while (fgets (vty->buf, vty->max, fp))
+    {
+      ++(*line_num);
+
+      ret = command_config_read_one_line (vty, NULL, 0);
+
+      if (ret != CMD_SUCCESS && ret != CMD_WARNING
+         && ret != CMD_ERR_NOTHING_TODO)
+       return ret;
+    }
+  return CMD_SUCCESS;
+}
+
+/* Configration from terminal */
+DEFUN (config_terminal,
+       config_terminal_cmd,
+       "configure terminal",
+       "Configuration from vty interface\n"
+       "Configuration terminal\n")
+{
+  if (vty_config_lock (vty))
+    vty->node = CONFIG_NODE;
+  else
+    {
+      vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+/* Enable command */
+DEFUN (enable, 
+       config_enable_cmd,
+       "enable",
+       "Turn on privileged mode command\n")
+{
+  /* If enable password is NULL, change to ENABLE_NODE */
+  if ((host.enable == NULL && host.enable_encrypt == NULL) ||
+      vty->type == VTY_SHELL_SERV)
+    vty->node = ENABLE_NODE;
+  else
+    vty->node = AUTH_ENABLE_NODE;
+
+  return CMD_SUCCESS;
+}
+
+/* Disable command */
+DEFUN (disable, 
+       config_disable_cmd,
+       "disable",
+       "Turn off privileged mode command\n")
+{
+  if (vty->node == ENABLE_NODE)
+    vty->node = VIEW_NODE;
+  return CMD_SUCCESS;
+}
+
+/* Down vty node level. */
+DEFUN (config_exit,
+       config_exit_cmd,
+       "exit",
+       "Exit current mode and down to previous mode\n")
+{
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+    case RESTRICTED_NODE:
+      if (vty_shell (vty))
+       exit (0);
+      else
+       vty->status = VTY_CLOSE;
+      break;
+    case CONFIG_NODE:
+      vty->node = ENABLE_NODE;
+      vty_config_unlock (vty);
+      break;
+    case INTERFACE_NODE:
+    case ZEBRA_NODE:
+    case BGP_NODE:
+    case RIP_NODE:
+    case RIPNG_NODE:
+    case BABEL_NODE:
+    case OSPF_NODE:
+    case OSPF6_NODE:
+    case ISIS_NODE:
+    case KEYCHAIN_NODE:
+    case MASC_NODE:
+    case RMAP_NODE:
+    case PIM_NODE:
+    case VTY_NODE:
+      vty->node = CONFIG_NODE;
+      break;
+    case BGP_IPV4_NODE:
+    case BGP_IPV4M_NODE:
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+      vty->node = BGP_NODE;
+      break;
+    case KEYCHAIN_KEY_NODE:
+      vty->node = KEYCHAIN_NODE;
+      break;
+    case LINK_PARAMS_NODE:
+      vty->node = INTERFACE_NODE;
+      break;
+    default:
+      break;
+    }
+  return CMD_SUCCESS;
+}
+
+/* quit is alias of exit. */
+ALIAS (config_exit,
+       config_quit_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+       
+/* End of configuration. */
+DEFUN (config_end,
+       config_end_cmd,
+       "end",
+       "End current mode and change to enable mode.")
+{
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+    case RESTRICTED_NODE:
+      /* Nothing to do. */
+      break;
+    case CONFIG_NODE:
+    case INTERFACE_NODE:
+    case ZEBRA_NODE:
+    case RIP_NODE:
+    case RIPNG_NODE:
+    case BABEL_NODE:
+    case BGP_NODE:
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_IPV4_NODE:
+    case BGP_IPV4M_NODE:
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+    case RMAP_NODE:
+    case OSPF_NODE:
+    case OSPF6_NODE:
+    case ISIS_NODE:
+    case KEYCHAIN_NODE:
+    case KEYCHAIN_KEY_NODE:
+    case MASC_NODE:
+    case PIM_NODE:
+    case VTY_NODE:
+    case LINK_PARAMS_NODE:
+      vty_config_unlock (vty);
+      vty->node = ENABLE_NODE;
+      break;
+    default:
+      break;
+    }
+  return CMD_SUCCESS;
+}
+
+/* Show version. */
+DEFUN (show_version,
+       show_version_cmd,
+       "show version",
+       SHOW_STR
+       "Displays zebra version\n")
+{
+  vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
+          VTY_NEWLINE);
+  vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
+  vty_out (vty, "configured with:%s    %s%s", VTY_NEWLINE,
+           QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+DEFUN (config_help,
+       config_help_cmd,
+       "help",
+       "Description of the interactive help system\n")
+{
+  vty_out (vty, 
+          "Quagga VTY provides advanced help feature.  When you need help,%s\
+anytime at the command line please press '?'.%s\
+%s\
+If nothing matches, the help list will be empty and you must backup%s\
+ until entering a '?' shows the available options.%s\
+Two styles of help are provided:%s\
+1. Full help is available when you are ready to enter a%s\
+command argument (e.g. 'show ?') and describes each possible%s\
+argument.%s\
+2. Partial help is provided when an abbreviated argument is entered%s\
+   and you want to know what arguments match the input%s\
+   (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+          VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+          VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+DEFUN (config_list,
+       config_list_cmd,
+       "list",
+       "Print command list\n")
+{
+  unsigned int i;
+  struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
+  struct cmd_element *cmd;
+
+  for (i = 0; i < vector_active (cnode->cmd_vector); i++)
+    if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
+        && !(cmd->attr == CMD_ATTR_DEPRECATED
+             || cmd->attr == CMD_ATTR_HIDDEN))
+      vty_out (vty, "  %s%s", cmd->string,
+              VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/* Write current configuration into file. */
+DEFUN (config_write_file, 
+       config_write_file_cmd,
+       "write file",  
+       "Write running configuration to memory, network, or terminal\n"
+       "Write to configuration file\n")
+{
+  unsigned int i;
+  int fd;
+  struct cmd_node *node;
+  char *config_file;
+  char *config_file_tmp = NULL;
+  char *config_file_sav = NULL;
+  int ret = CMD_WARNING;
+  struct vty *file_vty;
+
+  /* Check and see if we are operating under vtysh configuration */
+  if (host.config == NULL)
+    {
+      vty_out (vty, "Can't save to configuration file, using vtysh.%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get filename. */
+  config_file = host.config;
+  
+  config_file_sav =
+    XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
+  strcpy (config_file_sav, config_file);
+  strcat (config_file_sav, CONF_BACKUP_EXT);
+
+
+  config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
+  sprintf (config_file_tmp, "%s.XXXXXX", config_file);
+  
+  /* Open file to configuration write. */
+  fd = mkstemp (config_file_tmp);
+  if (fd < 0)
+    {
+      vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
+              VTY_NEWLINE);
+      goto finished;
+    }
+  
+  /* Make vty for configuration file. */
+  file_vty = vty_new ();
+  file_vty->wfd = fd;
+  file_vty->type = VTY_FILE;
+
+  /* Config file header print. */
+  vty_out (file_vty, "!\n! Zebra configuration saved from vty\n!   ");
+  vty_time_print (file_vty, 1);
+  vty_out (file_vty, "!\n");
+
+  for (i = 0; i < vector_active (cmdvec); i++)
+    if ((node = vector_slot (cmdvec, i)) && node->func)
+      {
+       if ((*node->func) (file_vty))
+         vty_out (file_vty, "!\n");
+      }
+  vty_close (file_vty);
+
+  if (unlink (config_file_sav) != 0)
+    if (errno != ENOENT)
+      {
+       vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
+                VTY_NEWLINE);
+        goto finished;
+      }
+  if (link (config_file, config_file_sav) != 0)
+    {
+      vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
+               VTY_NEWLINE);
+      goto finished;
+    }
+  sync ();
+  if (unlink (config_file) != 0)
+    {
+      vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
+               VTY_NEWLINE);
+      goto finished;
+    }
+  if (link (config_file_tmp, config_file) != 0)
+    {
+      vty_out (vty, "Can't save configuration file %s.%s", config_file,
+              VTY_NEWLINE);
+      goto finished;
+    }
+  sync ();
+  
+  if (chmod (config_file, CONFIGFILE_MASK) != 0)
+    {
+      vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", 
+       config_file, safe_strerror(errno), errno, VTY_NEWLINE);
+      goto finished;
+    }
+
+  vty_out (vty, "Configuration saved to %s%s", config_file,
+          VTY_NEWLINE);
+  ret = CMD_SUCCESS;
+
+finished:
+  unlink (config_file_tmp);
+  XFREE (MTYPE_TMP, config_file_tmp);
+  XFREE (MTYPE_TMP, config_file_sav);
+  return ret;
+}
+
+ALIAS (config_write_file, 
+       config_write_cmd,
+       "write",  
+       "Write running configuration to memory, network, or terminal\n")
+
+ALIAS (config_write_file, 
+       config_write_memory_cmd,
+       "write memory",  
+       "Write running configuration to memory, network, or terminal\n"
+       "Write configuration to the file (same as write file)\n")
+
+ALIAS (config_write_file, 
+       copy_runningconfig_startupconfig_cmd,
+       "copy running-config startup-config",  
+       "Copy configuration\n"
+       "Copy running config to... \n"
+       "Copy running config to startup config (same as write file)\n")
+
+/* Write current configuration into the terminal. */
+DEFUN (config_write_terminal,
+       config_write_terminal_cmd,
+       "write terminal",
+       "Write running configuration to memory, network, or terminal\n"
+       "Write to terminal\n")
+{
+  unsigned int i;
+  struct cmd_node *node;
+
+  if (vty->type == VTY_SHELL_SERV)
+    {
+      for (i = 0; i < vector_active (cmdvec); i++)
+       if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
+         {
+           if ((*node->func) (vty))
+             vty_out (vty, "!%s", VTY_NEWLINE);
+         }
+    }
+  else
+    {
+      vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
+              VTY_NEWLINE);
+      vty_out (vty, "!%s", VTY_NEWLINE);
+
+      for (i = 0; i < vector_active (cmdvec); i++)
+       if ((node = vector_slot (cmdvec, i)) && node->func)
+         {
+           if ((*node->func) (vty))
+             vty_out (vty, "!%s", VTY_NEWLINE);
+         }
+      vty_out (vty, "end%s",VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+/* Write current configuration into the terminal. */
+ALIAS (config_write_terminal,
+       show_running_config_cmd,
+       "show running-config",
+       SHOW_STR
+       "running configuration\n")
+
+/* Write startup configuration into the terminal. */
+DEFUN (show_startup_config,
+       show_startup_config_cmd,
+       "show startup-config",
+       SHOW_STR
+       "Contentes of startup configuration\n")
+{
+  char buf[BUFSIZ];
+  FILE *confp;
+
+  confp = fopen (host.config, "r");
+  if (confp == NULL)
+    {
+      vty_out (vty, "Can't open configuration file [%s]%s",
+              host.config, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  while (fgets (buf, BUFSIZ, confp))
+    {
+      char *cp = buf;
+
+      while (*cp != '\r' && *cp != '\n' && *cp != '\0')
+       cp++;
+      *cp = '\0';
+
+      vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+    }
+
+  fclose (confp);
+
+  return CMD_SUCCESS;
+}
+
+/* Hostname configuration */
+DEFUN (config_hostname, 
+       hostname_cmd,
+       "hostname WORD",
+       "Set system's network name\n"
+       "This system's network name\n")
+{
+  if (!isalpha((int) *argv[0]))
+    {
+      vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (host.name)
+    XFREE (MTYPE_HOST, host.name);
+    
+  host.name = XSTRDUP (MTYPE_HOST, argv[0]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_no_hostname, 
+       no_hostname_cmd,
+       "no hostname [HOSTNAME]",
+       NO_STR
+       "Reset system's network name\n"
+       "Host name of this router\n")
+{
+  if (host.name)
+    XFREE (MTYPE_HOST, host.name);
+  host.name = NULL;
+  return CMD_SUCCESS;
+}
+
+/* VTY interface password set. */
+DEFUN (config_password, password_cmd,
+       "password (8|) WORD",
+       "Assign the terminal connection password\n"
+       "Specifies a HIDDEN password will follow\n"
+       "dummy string \n"
+       "The HIDDEN line password string\n")
+{
+  /* Argument check. */
+  if (argc == 0)
+    {
+      vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      if (*argv[0] == '8')
+       {
+         if (host.password)
+           XFREE (MTYPE_HOST, host.password);
+         host.password = NULL;
+         if (host.password_encrypt)
+           XFREE (MTYPE_HOST, host.password_encrypt);
+         host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
+         return CMD_SUCCESS;
+       }
+      else
+       {
+         vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (!isalnum ((int) *argv[0]))
+    {
+      vty_out (vty, 
+              "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (host.password)
+    XFREE (MTYPE_HOST, host.password);
+  host.password = NULL;
+
+  if (host.encrypt)
+    {
+      if (host.password_encrypt)
+       XFREE (MTYPE_HOST, host.password_encrypt);
+      host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
+    }
+  else
+    host.password = XSTRDUP (MTYPE_HOST, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (config_password, password_text_cmd,
+       "password LINE",
+       "Assign the terminal connection password\n"
+       "The UNENCRYPTED (cleartext) line password\n")
+
+/* VTY enable password set. */
+DEFUN (config_enable_password, enable_password_cmd,
+       "enable password (8|) WORD",
+       "Modify enable password parameters\n"
+       "Assign the privileged level password\n"
+       "Specifies a HIDDEN password will follow\n"
+       "dummy string \n"
+       "The HIDDEN 'enable' password string\n")
+{
+  /* Argument check. */
+  if (argc == 0)
+    {
+      vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Crypt type is specified. */
+  if (argc == 2)
+    {
+      if (*argv[0] == '8')
+       {
+         if (host.enable)
+           XFREE (MTYPE_HOST, host.enable);
+         host.enable = NULL;
+
+         if (host.enable_encrypt)
+           XFREE (MTYPE_HOST, host.enable_encrypt);
+         host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
+
+         return CMD_SUCCESS;
+       }
+      else
+       {
+         vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  if (!isalnum ((int) *argv[0]))
+    {
+      vty_out (vty, 
+              "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (host.enable)
+    XFREE (MTYPE_HOST, host.enable);
+  host.enable = NULL;
+
+  /* Plain password input. */
+  if (host.encrypt)
+    {
+      if (host.enable_encrypt)
+       XFREE (MTYPE_HOST, host.enable_encrypt);
+      host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
+    }
+  else
+    host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (config_enable_password,
+       enable_password_text_cmd,
+       "enable password LINE",
+       "Modify enable password parameters\n"
+       "Assign the privileged level password\n"
+       "The UNENCRYPTED (cleartext) 'enable' password\n")
+
+/* VTY enable password delete. */
+DEFUN (no_config_enable_password, no_enable_password_cmd,
+       "no enable password",
+       NO_STR
+       "Modify enable password parameters\n"
+       "Assign the privileged level password\n")
+{
+  if (host.enable)
+    XFREE (MTYPE_HOST, host.enable);
+  host.enable = NULL;
+
+  if (host.enable_encrypt)
+    XFREE (MTYPE_HOST, host.enable_encrypt);
+  host.enable_encrypt = NULL;
+
+  return CMD_SUCCESS;
+}
+       
+DEFUN (service_password_encrypt,
+       service_password_encrypt_cmd,
+       "service password-encryption",
+       "Set up miscellaneous service\n"
+       "Enable encrypted passwords\n")
+{
+  if (host.encrypt)
+    return CMD_SUCCESS;
+
+  host.encrypt = 1;
+
+  if (host.password)
+    {
+      if (host.password_encrypt)
+       XFREE (MTYPE_HOST, host.password_encrypt);
+      host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
+    }
+  if (host.enable)
+    {
+      if (host.enable_encrypt)
+       XFREE (MTYPE_HOST, host.enable_encrypt);
+      host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_service_password_encrypt,
+       no_service_password_encrypt_cmd,
+       "no service password-encryption",
+       NO_STR
+       "Set up miscellaneous service\n"
+       "Enable encrypted passwords\n")
+{
+  if (! host.encrypt)
+    return CMD_SUCCESS;
+
+  host.encrypt = 0;
+
+  if (host.password_encrypt)
+    XFREE (MTYPE_HOST, host.password_encrypt);
+  host.password_encrypt = NULL;
+
+  if (host.enable_encrypt)
+    XFREE (MTYPE_HOST, host.enable_encrypt);
+  host.enable_encrypt = NULL;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_terminal_length, config_terminal_length_cmd,
+       "terminal length <0-512>",
+       "Set terminal line parameters\n"
+       "Set number of lines on a screen\n"
+       "Number of lines on screen (0 for no pausing)\n")
+{
+  int lines;
+  char *endptr = NULL;
+
+  lines = strtol (argv[0], &endptr, 10);
+  if (lines < 0 || lines > 512 || *endptr != '\0')
+    {
+      vty_out (vty, "length is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  vty->lines = lines;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
+       "terminal no length",
+       "Set terminal line parameters\n"
+       NO_STR
+       "Set number of lines on a screen\n")
+{
+  vty->lines = -1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (service_terminal_length, service_terminal_length_cmd,
+       "service terminal-length <0-512>",
+       "Set up miscellaneous service\n"
+       "System wide terminal length configuration\n"
+       "Number of lines of VTY (0 means no line control)\n")
+{
+  int lines;
+  char *endptr = NULL;
+
+  lines = strtol (argv[0], &endptr, 10);
+  if (lines < 0 || lines > 512 || *endptr != '\0')
+    {
+      vty_out (vty, "length is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  host.lines = lines;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
+       "no service terminal-length [<0-512>]",
+       NO_STR
+       "Set up miscellaneous service\n"
+       "System wide terminal length configuration\n"
+       "Number of lines of VTY (0 means no line control)\n")
+{
+  host.lines = -1;
+  return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (do_echo,
+             echo_cmd,
+             "echo .MESSAGE",
+             "Echo a message back to the vty\n"
+             "The message to echo\n")
+{
+  char *message;
+
+  vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
+          VTY_NEWLINE);
+  if (message)
+    XFREE(MTYPE_TMP, message);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_logmsg,
+       config_logmsg_cmd,
+       "logmsg "LOG_LEVELS" .MESSAGE",
+       "Send a message to enabled logging destinations\n"
+       LOG_LEVEL_DESC
+       "The message to send\n")
+{
+  int level;
+  char *message;
+
+  if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+
+  zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
+  if (message)
+    XFREE(MTYPE_TMP, message);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_logging,
+       show_logging_cmd,
+       "show logging",
+       SHOW_STR
+       "Show current logging configuration\n")
+{
+  struct zlog *zl = zlog_default;
+
+  vty_out (vty, "Syslog logging: ");
+  if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+    vty_out (vty, "disabled");
+  else
+    vty_out (vty, "level %s, facility %s, ident %s",
+            zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
+            facility_name(zl->facility), zl->ident);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "Stdout logging: ");
+  if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+    vty_out (vty, "disabled");
+  else
+    vty_out (vty, "level %s",
+            zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "Monitor logging: ");
+  if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+    vty_out (vty, "disabled");
+  else
+    vty_out (vty, "level %s",
+            zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "File logging: ");
+  if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
+      !zl->fp)
+    vty_out (vty, "disabled");
+  else
+    vty_out (vty, "level %s, filename %s",
+            zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
+            zl->filename);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "Protocol name: %s%s",
+          zlog_proto_names[zl->protocol], VTY_NEWLINE);
+  vty_out (vty, "Record priority: %s%s",
+          (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+  vty_out (vty, "Timestamp precision: %d%s",
+          zl->timestamp_precision, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_stdout,
+       config_log_stdout_cmd,
+       "log stdout",
+       "Logging control\n"
+       "Set stdout logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_stdout_level,
+       config_log_stdout_level_cmd,
+       "log stdout "LOG_LEVELS,
+       "Logging control\n"
+       "Set stdout logging level\n"
+       LOG_LEVEL_DESC)
+{
+  int level;
+
+  if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_stdout,
+       no_config_log_stdout_cmd,
+       "no log stdout [LEVEL]",
+       NO_STR
+       "Logging control\n"
+       "Cancel logging to stdout\n"
+       "Logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_monitor,
+       config_log_monitor_cmd,
+       "log monitor",
+       "Logging control\n"
+       "Set terminal line (monitor) logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_monitor_level,
+       config_log_monitor_level_cmd,
+       "log monitor "LOG_LEVELS,
+       "Logging control\n"
+       "Set terminal line (monitor) logging level\n"
+       LOG_LEVEL_DESC)
+{
+  int level;
+
+  if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_monitor,
+       no_config_log_monitor_cmd,
+       "no log monitor [LEVEL]",
+       NO_STR
+       "Logging control\n"
+       "Disable terminal line (monitor) logging\n"
+       "Logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+  return CMD_SUCCESS;
+}
+
+static int
+set_log_file(struct vty *vty, const char *fname, int loglevel)
+{
+  int ret;
+  char *p = NULL;
+  const char *fullpath;
+  
+  /* Path detection. */
+  if (! IS_DIRECTORY_SEP (*fname))
+    {
+      char cwd[MAXPATHLEN+1];
+      cwd[MAXPATHLEN] = '\0';
+      
+      if (getcwd (cwd, MAXPATHLEN) == NULL)
+        {
+          zlog_err ("config_log_file: Unable to alloc mem!");
+          return CMD_WARNING;
+        }
+      
+      if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
+          == NULL)
+        {
+          zlog_err ("config_log_file: Unable to alloc mem!");
+          return CMD_WARNING;
+        }
+      sprintf (p, "%s/%s", cwd, fname);
+      fullpath = p;
+    }
+  else
+    fullpath = fname;
+
+  ret = zlog_set_file (NULL, fullpath, loglevel);
+
+  if (p)
+    XFREE (MTYPE_TMP, p);
+
+  if (!ret)
+    {
+      vty_out (vty, "can't open logfile %s\n", fname);
+      return CMD_WARNING;
+    }
+
+  if (host.logfile)
+    XFREE (MTYPE_HOST, host.logfile);
+
+  host.logfile = XSTRDUP (MTYPE_HOST, fname);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_file,
+       config_log_file_cmd,
+       "log file FILENAME",
+       "Logging control\n"
+       "Logging to file\n"
+       "Logging filename\n")
+{
+  return set_log_file(vty, argv[0], zlog_default->default_lvl);
+}
+
+DEFUN (config_log_file_level,
+       config_log_file_level_cmd,
+       "log file FILENAME "LOG_LEVELS,
+       "Logging control\n"
+       "Logging to file\n"
+       "Logging filename\n"
+       LOG_LEVEL_DESC)
+{
+  int level;
+
+  if ((level = level_match(argv[1])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+  return set_log_file(vty, argv[0], level);
+}
+
+DEFUN (no_config_log_file,
+       no_config_log_file_cmd,
+       "no log file [FILENAME]",
+       NO_STR
+       "Logging control\n"
+       "Cancel logging to file\n"
+       "Logging file name\n")
+{
+  zlog_reset_file (NULL);
+
+  if (host.logfile)
+    XFREE (MTYPE_HOST, host.logfile);
+
+  host.logfile = NULL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_config_log_file,
+       no_config_log_file_level_cmd,
+       "no log file FILENAME LEVEL",
+       NO_STR
+       "Logging control\n"
+       "Cancel logging to file\n"
+       "Logging file name\n"
+       "Logging level\n")
+
+DEFUN (config_log_syslog,
+       config_log_syslog_cmd,
+       "log syslog",
+       "Logging control\n"
+       "Set syslog logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_syslog_level,
+       config_log_syslog_level_cmd,
+       "log syslog "LOG_LEVELS,
+       "Logging control\n"
+       "Set syslog logging level\n"
+       LOG_LEVEL_DESC)
+{
+  int level;
+
+  if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (config_log_syslog_facility,
+                 config_log_syslog_facility_cmd,
+                 "log syslog facility "LOG_FACILITIES,
+                 "Logging control\n"
+                 "Logging goes to syslog\n"
+                 "(Deprecated) Facility parameter for syslog messages\n"
+                 LOG_FACILITY_DESC)
+{
+  int facility;
+
+  if ((facility = facility_match(argv[0])) < 0)
+    return CMD_ERR_NO_MATCH;
+
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+  zlog_default->facility = facility;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_syslog,
+       no_config_log_syslog_cmd,
+       "no log syslog [LEVEL]",
+       NO_STR
+       "Logging control\n"
+       "Cancel logging to syslog\n"
+       "Logging level\n")
+{
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_config_log_syslog,
+       no_config_log_syslog_facility_cmd,
+       "no log syslog facility "LOG_FACILITIES,
+       NO_STR
+       "Logging control\n"
+       "Logging goes to syslog\n"
+       "Facility parameter for syslog messages\n"
+       LOG_FACILITY_DESC)
+
+DEFUN (config_log_facility,
+       config_log_facility_cmd,
+       "log facility "LOG_FACILITIES,
+       "Logging control\n"
+       "Facility parameter for syslog messages\n"
+       LOG_FACILITY_DESC)
+{
+  int facility;
+
+  if ((facility = facility_match(argv[0])) < 0)
+    return CMD_ERR_NO_MATCH;
+  zlog_default->facility = facility;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_facility,
+       no_config_log_facility_cmd,
+       "no log facility [FACILITY]",
+       NO_STR
+       "Logging control\n"
+       "Reset syslog facility to default (daemon)\n"
+       "Syslog facility\n")
+{
+  zlog_default->facility = LOG_DAEMON;
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (config_log_trap,
+                 config_log_trap_cmd,
+                 "log trap "LOG_LEVELS,
+                 "Logging control\n"
+                 "(Deprecated) Set logging level and default for all destinations\n"
+                 LOG_LEVEL_DESC)
+{
+  int new_level ;
+  int i;
+  
+  if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
+    return CMD_ERR_NO_MATCH;
+
+  zlog_default->default_lvl = new_level;
+  for (i = 0; i < ZLOG_NUM_DESTS; i++)
+    if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
+      zlog_default->maxlvl[i] = new_level;
+  return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED (no_config_log_trap,
+                 no_config_log_trap_cmd,
+                 "no log trap [LEVEL]",
+                 NO_STR
+                 "Logging control\n"
+                 "Permit all logging information\n"
+                 "Logging level\n")
+{
+  zlog_default->default_lvl = LOG_DEBUG;
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_record_priority,
+       config_log_record_priority_cmd,
+       "log record-priority",
+       "Logging control\n"
+       "Log the priority of the message within the message\n")
+{
+  zlog_default->record_priority = 1 ;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_record_priority,
+       no_config_log_record_priority_cmd,
+       "no log record-priority",
+       NO_STR
+       "Logging control\n"
+       "Do not log the priority of the message within the message\n")
+{
+  zlog_default->record_priority = 0 ;
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_log_timestamp_precision,
+       config_log_timestamp_precision_cmd,
+       "log timestamp precision <0-6>",
+       "Logging control\n"
+       "Timestamp configuration\n"
+       "Set the timestamp precision\n"
+       "Number of subsecond digits\n")
+{
+  if (argc != 1)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE("Timestamp Precision",
+                       zlog_default->timestamp_precision, argv[0], 0, 6);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_timestamp_precision,
+       no_config_log_timestamp_precision_cmd,
+       "no log timestamp precision",
+       NO_STR
+       "Logging control\n"
+       "Timestamp configuration\n"
+       "Reset the timestamp precision to the default value of 0\n")
+{
+  zlog_default->timestamp_precision = 0 ;
+  return CMD_SUCCESS;
+}
+
+DEFUN (banner_motd_file,
+       banner_motd_file_cmd,
+       "banner motd file [FILE]",
+       "Set banner\n"
+       "Banner for motd\n"
+       "Banner from a file\n"
+       "Filename\n")
+{
+  if (host.motdfile)
+    XFREE (MTYPE_HOST, host.motdfile);
+  host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (banner_motd_default,
+       banner_motd_default_cmd,
+       "banner motd default",
+       "Set banner string\n"
+       "Strings for motd\n"
+       "Default string\n")
+{
+  host.motd = default_motd;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_banner_motd,
+       no_banner_motd_cmd,
+       "no banner motd",
+       NO_STR
+       "Set banner string\n"
+       "Strings for motd\n")
+{
+  host.motd = NULL;
+  if (host.motdfile) 
+    XFREE (MTYPE_HOST, host.motdfile);
+  host.motdfile = NULL;
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_commandtree,
+       show_commandtree_cmd,
+       "show commandtree",
+       NO_STR
+       "Show command tree\n")
+{
+  /* TBD */
+  vector cmd_vector;
+  unsigned int i;
+
+  vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE);
+
+  /* vector of all commands installed at this node */
+  cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+
+  /* loop over all commands at this node */
+  for (i = 0; i < vector_active(cmd_vector); ++i)
+    {
+      struct cmd_element *cmd_element;
+
+      /* A cmd_element (seems to be) is an individual command */
+      if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
+        continue;
+
+      vty_out (vty, "    %s%s", cmd_element->string, VTY_NEWLINE);
+    }
+
+  vector_free (cmd_vector);
+  return CMD_SUCCESS;
+}
+
+/* Set config filename.  Called from vty.c */
+void
+host_config_set (char *filename)
+{
+  if (host.config)
+    XFREE (MTYPE_HOST, host.config);
+  host.config = XSTRDUP (MTYPE_HOST, filename);
+}
+
+const char *
+host_config_get (void)
+{
+  return host.config;
+}
+
+static void
+install_default_basic (enum node_type node)
+{
+  install_element (node, &config_exit_cmd);
+  install_element (node, &config_quit_cmd);
+  install_element (node, &config_help_cmd);
+  install_element (node, &config_list_cmd);
+}
+
+/* Install common/default commands for a privileged node */
+void
+install_default (enum node_type node)
+{
+  /* VIEW_NODE is inited below, via install_default_basic, and
+     install_element's of commands to VIEW_NODE automatically are
+     also installed to ENABLE_NODE.
+    
+     For all other nodes, we must ensure install_default_basic is
+     also called/
+   */
+  if (node != VIEW_NODE && node != ENABLE_NODE)
+    install_default_basic (node);
+  
+  install_element (node, &config_end_cmd);
+  install_element (node, &config_write_terminal_cmd);
+  install_element (node, &config_write_file_cmd);
+  install_element (node, &config_write_memory_cmd);
+  install_element (node, &config_write_cmd);
+  install_element (node, &show_running_config_cmd);
+}
+
+/* Initialize command interface. Install basic nodes and commands. */
+void
+cmd_init (int terminal)
+{
+  command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
+  token_cr.type = TOKEN_TERMINAL;
+  token_cr.terminal = TERMINAL_LITERAL;
+  token_cr.cmd = command_cr;
+  token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
+
+  /* Allocate initial top vector of commands. */
+  cmdvec = vector_init (VECTOR_MIN_SIZE);
+  
+  /* Default host value settings. */
+  host.name = NULL;
+  host.password = NULL;
+  host.enable = NULL;
+  host.logfile = NULL;
+  host.config = NULL;
+  host.lines = -1;
+  host.motd = default_motd;
+  host.motdfile = NULL;
+
+  /* Install top nodes. */
+  install_node (&view_node, NULL);
+  install_node (&enable_node, NULL);
+  install_node (&auth_node, NULL);
+  install_node (&auth_enable_node, NULL);
+  install_node (&restricted_node, NULL);
+  install_node (&config_node, config_write_host);
+
+  /* Each node's basic commands. */
+  install_element (VIEW_NODE, &show_version_cmd);
+  if (terminal)
+    {
+      install_default_basic (VIEW_NODE);
+      
+      install_element (VIEW_NODE, &config_enable_cmd);
+      install_element (VIEW_NODE, &config_terminal_length_cmd);
+      install_element (VIEW_NODE, &config_terminal_no_length_cmd);
+      install_element (VIEW_NODE, &show_logging_cmd);
+      install_element (VIEW_NODE, &show_commandtree_cmd);
+      install_element (VIEW_NODE, &echo_cmd);
+
+      install_element (RESTRICTED_NODE, &config_enable_cmd);
+      install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
+      install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
+      install_element (RESTRICTED_NODE, &show_commandtree_cmd);
+      install_element (RESTRICTED_NODE, &echo_cmd);
+    }
+
+  if (terminal)
+    {
+      install_default (ENABLE_NODE);
+      install_element (ENABLE_NODE, &config_disable_cmd);
+      install_element (ENABLE_NODE, &config_terminal_cmd);
+      install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+    }
+  install_element (ENABLE_NODE, &show_startup_config_cmd);
+
+  if (terminal)
+    {
+      install_element (ENABLE_NODE, &config_logmsg_cmd);
+
+      install_default (CONFIG_NODE);
+    }
+  
+  install_element (CONFIG_NODE, &hostname_cmd);
+  install_element (CONFIG_NODE, &no_hostname_cmd);
+
+  if (terminal)
+    {
+      install_element (CONFIG_NODE, &password_cmd);
+      install_element (CONFIG_NODE, &password_text_cmd);
+      install_element (CONFIG_NODE, &enable_password_cmd);
+      install_element (CONFIG_NODE, &enable_password_text_cmd);
+      install_element (CONFIG_NODE, &no_enable_password_cmd);
+
+      install_element (CONFIG_NODE, &config_log_stdout_cmd);
+      install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
+      install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
+      install_element (CONFIG_NODE, &config_log_monitor_cmd);
+      install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
+      install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
+      install_element (CONFIG_NODE, &config_log_file_cmd);
+      install_element (CONFIG_NODE, &config_log_file_level_cmd);
+      install_element (CONFIG_NODE, &no_config_log_file_cmd);
+      install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
+      install_element (CONFIG_NODE, &config_log_syslog_cmd);
+      install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
+      install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
+      install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
+      install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
+      install_element (CONFIG_NODE, &config_log_facility_cmd);
+      install_element (CONFIG_NODE, &no_config_log_facility_cmd);
+      install_element (CONFIG_NODE, &config_log_trap_cmd);
+      install_element (CONFIG_NODE, &no_config_log_trap_cmd);
+      install_element (CONFIG_NODE, &config_log_record_priority_cmd);
+      install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
+      install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
+      install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
+      install_element (CONFIG_NODE, &service_password_encrypt_cmd);
+      install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
+      install_element (CONFIG_NODE, &banner_motd_default_cmd);
+      install_element (CONFIG_NODE, &banner_motd_file_cmd);
+      install_element (CONFIG_NODE, &no_banner_motd_cmd);
+      install_element (CONFIG_NODE, &service_terminal_length_cmd);
+      install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
+
+      install_element (VIEW_NODE, &show_thread_cpu_cmd);
+      install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
+      
+      install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
+      install_element (VIEW_NODE, &show_work_queues_cmd);
+    }
+  install_element (CONFIG_NODE, &show_commandtree_cmd);
+  srandom(time(NULL));
+}
+
+static void
+cmd_terminate_token(struct cmd_token *token)
+{
+  unsigned int i, j;
+  vector keyword_vect;
+
+  if (token->multiple)
+    {
+      for (i = 0; i < vector_active(token->multiple); i++)
+        cmd_terminate_token(vector_slot(token->multiple, i));
+      vector_free(token->multiple);
+      token->multiple = NULL;
+    }
+
+  if (token->keyword)
+    {
+      for (i = 0; i < vector_active(token->keyword); i++)
+        {
+          keyword_vect = vector_slot(token->keyword, i);
+          for (j = 0; j < vector_active(keyword_vect); j++)
+            cmd_terminate_token(vector_slot(keyword_vect, j));
+          vector_free(keyword_vect);
+        }
+      vector_free(token->keyword);
+      token->keyword = NULL;
+    }
+
+  XFREE(MTYPE_CMD_TOKENS, token->cmd);
+  XFREE(MTYPE_CMD_TOKENS, token->desc);
+
+  XFREE(MTYPE_CMD_TOKENS, token);
+}
+
+static void
+cmd_terminate_element(struct cmd_element *cmd)
+{
+  unsigned int i;
+
+  if (cmd->tokens == NULL)
+    return;
+
+  for (i = 0; i < vector_active(cmd->tokens); i++)
+    cmd_terminate_token(vector_slot(cmd->tokens, i));
+
+  vector_free(cmd->tokens);
+  cmd->tokens = NULL;
+}
+
+void
+cmd_terminate ()
+{
+  unsigned int i, j;
+  struct cmd_node *cmd_node;
+  struct cmd_element *cmd_element;
+  vector cmd_node_v;
+
+  if (cmdvec)
+    {
+      for (i = 0; i < vector_active (cmdvec); i++) 
+        if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
+          {
+            cmd_node_v = cmd_node->cmd_vector;
+
+            for (j = 0; j < vector_active (cmd_node_v); j++)
+              if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
+                cmd_terminate_element(cmd_element);
+
+            vector_free (cmd_node_v);
+            hash_clean (cmd_node->cmd_hash, NULL);
+            hash_free (cmd_node->cmd_hash);
+            cmd_node->cmd_hash = NULL;
+          }
+
+      vector_free (cmdvec);
+      cmdvec = NULL;
+    }
+
+  if (command_cr)
+    XFREE(MTYPE_CMD_TOKENS, command_cr);
+  if (token_cr.desc)
+    XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
+  if (host.name)
+    XFREE (MTYPE_HOST, host.name);
+  if (host.password)
+    XFREE (MTYPE_HOST, host.password);
+  if (host.password_encrypt)
+    XFREE (MTYPE_HOST, host.password_encrypt);
+  if (host.enable)
+    XFREE (MTYPE_HOST, host.enable);
+  if (host.enable_encrypt)
+    XFREE (MTYPE_HOST, host.enable_encrypt);
+  if (host.logfile)
+    XFREE (MTYPE_HOST, host.logfile);
+  if (host.motdfile)
+    XFREE (MTYPE_HOST, host.motdfile);
+  if (host.config)
+    XFREE (MTYPE_HOST, host.config);
+}
diff --git a/lib/command.h b/lib/command.h
new file mode 100644 (file)
index 0000000..cc5dd08
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_H
+#define _ZEBRA_COMMAND_H
+
+#include "vector.h"
+#include "vty.h"
+#include "lib/route_types.h"
+#include "hash.h"
+
+/* Host configuration variable */
+struct host
+{
+  /* Host name of this router. */
+  char *name;
+
+  /* Password for vty interface. */
+  char *password;
+  char *password_encrypt;
+
+  /* Enable password */
+  char *enable;
+  char *enable_encrypt;
+
+  /* System wide terminal lines. */
+  int lines;
+
+  /* Log filename. */
+  char *logfile;
+
+  /* config file name of this host */
+  char *config;
+
+  /* Flags for services */
+  int advanced;
+  int encrypt;
+
+  /* Banner configuration. */
+  const char *motd;
+  char *motdfile;
+};
+
+/* There are some command levels which called from command node. */
+enum node_type 
+{
+  AUTH_NODE,                   /* Authentication mode of vty interface. */
+  RESTRICTED_NODE,             /* Restricted view mode */ 
+  VIEW_NODE,                   /* View node. Default mode of vty interface. */
+  AUTH_ENABLE_NODE,            /* Authentication mode for change enable. */
+  ENABLE_NODE,                 /* Enable node. */
+  CONFIG_NODE,                 /* Config node. Default mode of config file. */
+  VRF_NODE,                    /* VRF node. */
+  SERVICE_NODE,                /* Service node. */
+  DEBUG_NODE,                  /* Debug node. */
+  AAA_NODE,                    /* AAA node. */
+  KEYCHAIN_NODE,               /* Key-chain node. */
+  KEYCHAIN_KEY_NODE,           /* Key-chain key node. */
+  INTERFACE_NODE,              /* Interface mode node. */
+  ZEBRA_NODE,                  /* zebra connection node. */
+  TABLE_NODE,                  /* rtm_table selection node. */
+  RIP_NODE,                    /* RIP protocol mode node. */ 
+  RIPNG_NODE,                  /* RIPng protocol mode node. */
+  BABEL_NODE,                  /* Babel protocol mode node. */
+  BGP_NODE,                    /* BGP protocol mode which includes BGP4+ */
+  BGP_VPNV4_NODE,              /* BGP MPLS-VPN PE exchange. */
+  BGP_VPNV6_NODE,              /* BGP MPLS-VPN PE exchange. */
+  BGP_IPV4_NODE,               /* BGP IPv4 unicast address family.  */
+  BGP_IPV4M_NODE,              /* BGP IPv4 multicast address family.  */
+  BGP_IPV6_NODE,               /* BGP IPv6 address family */
+  BGP_IPV6M_NODE,              /* BGP IPv6 multicast address family. */
+  BGP_ENCAP_NODE,              /* BGP ENCAP SAFI */
+  BGP_ENCAPV6_NODE,            /* BGP ENCAP SAFI */
+  OSPF_NODE,                   /* OSPF protocol mode */
+  OSPF6_NODE,                  /* OSPF protocol for IPv6 mode */
+  ISIS_NODE,                   /* ISIS protocol mode */
+  PIM_NODE,                    /* PIM protocol mode */
+  MASC_NODE,                   /* MASC for multicast.  */
+  IRDP_NODE,                   /* ICMP Router Discovery Protocol mode. */ 
+  IP_NODE,                     /* Static ip route node. */
+  ACCESS_NODE,                 /* Access list node. */
+  PREFIX_NODE,                 /* Prefix list node. */
+  ACCESS_IPV6_NODE,            /* Access list node. */
+  PREFIX_IPV6_NODE,            /* Prefix list node. */
+  AS_LIST_NODE,                        /* AS list node. */
+  COMMUNITY_LIST_NODE,         /* Community list node. */
+  RMAP_NODE,                   /* Route map node. */
+  SMUX_NODE,                   /* SNMP configuration node. */
+  DUMP_NODE,                   /* Packet dump node. */
+  FORWARDING_NODE,             /* IP forwarding node. */
+  PROTOCOL_NODE,                /* protocol filtering node */
+  VTY_NODE,                    /* Vty node. */
+  LINK_PARAMS_NODE,            /* Link-parameters node */
+  ZEBRA_IF_DEFAULTS_NODE,      /* If defaults dummy node */
+};
+
+/* Node which has some commands and prompt string and configuration
+   function pointer . */
+struct cmd_node 
+{
+  /* Node index. */
+  enum node_type node;         
+
+  /* Prompt character at vty interface. */
+  const char *prompt;                  
+
+  /* Is this node's configuration goes to vtysh ? */
+  int vtysh;
+  
+  /* Node's configuration write function */
+  int (*func) (struct vty *);
+
+  /* Vector of this node's command list. */
+  vector cmd_vector;
+  
+  /* Hashed index of command node list, for de-dupping primarily */
+  struct hash *cmd_hash;
+};
+
+enum
+{
+  CMD_ATTR_DEPRECATED = 1,
+  CMD_ATTR_HIDDEN,
+};
+
+/* Structure of command element. */
+struct cmd_element 
+{
+  const char *string;                  /* Command specification by string. */
+  int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
+  const char *doc;                     /* Documentation of this command. */
+  int daemon;                   /* Daemon to which this command belong. */
+  vector tokens;               /* Vector of cmd_tokens */
+  u_char attr;                 /* Command attributes */
+};
+
+
+enum cmd_token_type
+{
+  TOKEN_TERMINAL = 0,
+  TOKEN_MULTIPLE,
+  TOKEN_KEYWORD,
+};
+
+enum cmd_terminal_type
+{
+  _TERMINAL_BUG = 0,
+  TERMINAL_LITERAL,
+  TERMINAL_OPTION,
+  TERMINAL_VARIABLE,
+  TERMINAL_VARARG,
+  TERMINAL_RANGE,
+  TERMINAL_IPV4,
+  TERMINAL_IPV4_PREFIX,
+  TERMINAL_IPV6,
+  TERMINAL_IPV6_PREFIX,
+};
+
+/* argument to be recorded on argv[] if it's not a literal */
+#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION)
+
+/* Command description structure. */
+struct cmd_token
+{
+  enum cmd_token_type type;
+  enum cmd_terminal_type terminal;
+
+  /* Used for type == MULTIPLE */
+  vector multiple; /* vector of cmd_token, type == FINAL */
+
+  /* Used for type == KEYWORD */
+  vector keyword; /* vector of vector of cmd_tokens */
+
+  /* Used for type == TERMINAL */
+  char *cmd;                    /* Command string. */
+  char *desc;                    /* Command's description. */
+};
+
+/* Return value of the commands. */
+#define CMD_SUCCESS              0
+#define CMD_WARNING              1
+#define CMD_ERR_NO_MATCH         2
+#define CMD_ERR_AMBIGUOUS        3
+#define CMD_ERR_INCOMPLETE       4
+#define CMD_ERR_EXEED_ARGC_MAX   5
+#define CMD_ERR_NOTHING_TODO     6
+#define CMD_COMPLETE_FULL_MATCH  7
+#define CMD_COMPLETE_MATCH       8
+#define CMD_COMPLETE_LIST_MATCH  9
+#define CMD_SUCCESS_DAEMON      10
+
+/* Argc max counts. */
+#define CMD_ARGC_MAX   25
+
+/* Turn off these macros when uisng cpp with extract.pl */
+#ifndef VTYSH_EXTRACT_PL  
+
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+  struct cmd_element cmdname = \
+  { \
+    .string = cmdstr, \
+    .func = funcname, \
+    .doc = helpstr, \
+    .attr = attrs, \
+    .daemon = dnum, \
+  };
+
+#define DEFUN_CMD_FUNC_DECL(funcname) \
+  static int funcname (struct cmd_element *, struct vty *, int, const char *[]);
+
+#define DEFUN_CMD_FUNC_TEXT(funcname) \
+  static int funcname \
+    (struct cmd_element *self __attribute__ ((unused)), \
+     struct vty *vty __attribute__ ((unused)), \
+     int argc __attribute__ ((unused)), \
+     const char *argv[] __attribute__ ((unused)) )
+
+/* DEFUN for vty command interafce. Little bit hacky ;-).
+ *
+ * DEFUN(funcname, cmdname, cmdstr, helpstr)
+ *
+ * funcname
+ * ========
+ *
+ * Name of the function that will be defined.
+ *
+ * cmdname
+ * =======
+ *
+ * Name of the struct that will be defined for the command.
+ *
+ * cmdstr
+ * ======
+ *
+ * The cmdstr defines the command syntax. It is used by the vty subsystem
+ * and vtysh to perform matching and completion in the cli. So you have to take
+ * care to construct it adhering to the following grammar. The names used
+ * for the production rules losely represent the names used in lib/command.c
+ *
+ * cmdstr = cmd_token , { " " , cmd_token } ;
+ *
+ * cmd_token = cmd_terminal
+ *           | cmd_multiple
+ *           | cmd_keyword ;
+ *
+ * cmd_terminal_fixed = fixed_string
+ *                    | variable
+ *                    | range
+ *                    | ipv4
+ *                    | ipv4_prefix
+ *                    | ipv6
+ *                    | ipv6_prefix ;
+ *
+ * cmd_terminal = cmd_terminal_fixed
+ *              | option
+ *              | vararg ;
+ *
+ * multiple_part = cmd_terminal_fixed ;
+ * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ;
+ *
+ * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ;
+ * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ;
+ *
+ * lowercase = "a" | ... | "z" ;
+ * uppercase = "A" | ... | "Z" ;
+ * digit = "0" | ... | "9" ;
+ * number = digit , { digit } ;
+ *
+ * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ;
+ * variable = uppercase , { uppercase | "_" } ;
+ * range = "<" , number , "-" , number , ">" ;
+ * ipv4 = "A.B.C.D" ;
+ * ipv4_prefix = "A.B.C.D/M" ;
+ * ipv6 = "X:X::X:X" ;
+ * ipv6_prefix = "X:X::X:X/M" ;
+ * option = "[" , variable , "]" ;
+ * vararg = "." , variable ;
+ *
+ * To put that all in a textual description: A cmdstr is a sequence of tokens,
+ * separated by spaces.
+ *
+ * Terminal Tokens:
+ *
+ * A very simple cmdstring would be something like: "show ip bgp". It consists
+ * of three Terminal Tokens, each containing a fixed string. When this command
+ * is called, no arguments will be passed down to the function implementing it,
+ * as it only consists of fixed strings.
+ *
+ * Apart from fixed strings, Terminal Tokens can also contain variables:
+ * An example would be "show ip bgp A.B.C.D". This command expects an IPv4
+ * as argument. As this is a variable, the IP address entered by the user will
+ * be passed down as an argument. Apart from two exceptions, the other options
+ * for Terminal Tokens behave exactly as we just discussed and only make a
+ * difference for the CLI. The two exceptions will be discussed in the next
+ * paragraphs.
+ *
+ * A Terminal Token can contain a so called option match. This is a simple
+ * string variable that the user may omit. An example would be:
+ * "show interface [IFNAME]". If the user calls this without an interface as
+ * argument, no arguments will be passed down to the function implementing
+ * this command. Otherwise, the interface name will be provided to the function
+ * as a regular argument.
+
+ * Also, a Terminal Token can contain a so called vararg. This is used e.g. in
+ * "show ip bgp regexp .LINE". The last token is a vararg match and will
+ * consume all the arguments the user inputs on the command line and append
+ * those to the list of arguments passed down to the function implementing this
+ * command. (Therefore, it doesn't make much sense to have any tokens after a
+ * vararg because the vararg will already consume all the words the user entered
+ * in the CLI)
+ *
+ * Multiple Tokens:
+ *
+ * The Multiple Token type can be used if there are multiple possibilities what
+ * arguments may be used for a command, but it should map to the same function
+ * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)"
+ * In that case both "reject" and "blackhole" would be acceptable as last
+ * arguments. The words matched by Multiple Tokens are always added to the
+ * argument list, even if they are matched by fixed strings. Such a Multiple
+ * Token can contain almost any type of token that would also be acceptable
+ * for a Terminal Token, the exception are optional variables and varag.
+ *
+ * There is one special case that is used in some places of Quagga that should be
+ * pointed out here shortly. An example would be "password (8|) WORD". This
+ * construct is used to have fixed strings communicated as arguments. (The "8"
+ * will be passed down as an argument in this case) It does not mean that
+ * the "8" is optional. Another historic and possibly surprising property of
+ * this construct is that it consumes two parts of helpstr. (Help
+ * strings will be explained later)
+ *
+ * Keyword Tokens:
+ *
+ * There are commands that take a lot of different and possibly optional arguments.
+ * An example from ospf would be the "default-information originate" command. This
+ * command takes a lot of optional arguments that may be provided in any order.
+ * To accomodate such commands, the Keyword Token has been implemented.
+ * Using the keyword token, the "default-information originate" command and all
+ * its possible options can be represented using this single cmdstr:
+ * "default-information originate \
+ *  {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}"
+ *
+ * Keywords always start with a fixed string and may be followed by arguments.
+ * Except optional variables and vararg, everything is permitted here.
+ *
+ * For the special case of a keyword without arguments, either NULL or the
+ * keyword itself will be pushed as an argument, depending on whether the
+ * keyword is present.
+ * For the other keywords, arguments will be only pushed for
+ * variables/Multiple Tokens. If the keyword is not present, the arguments that
+ * would have been pushed will be substituted by NULL.
+ *
+ * A few examples:
+ *   "default information originate metric-type 1 metric 1000"
+ * would yield the following arguments:
+ *   { NULL, "1000", "1", NULL }
+ *
+ *   "default information originate always route-map RMAP-DEFAULT"
+ * would yield the following arguments:
+ *   { "always", NULL, NULL, "RMAP-DEFAULT" }
+ *
+ * helpstr
+ * =======
+ *
+ * The helpstr is used to show a short explantion for the commands that
+ * are available when the user presses '?' on the CLI. It is the concatenation
+ * of the helpstrings for all the tokens that make up the command.
+ *
+ * There should be one helpstring for each token in the cmdstr except those
+ * containing other tokens, like Multiple or Keyword Tokens. For those, there
+ * will only be the helpstrings of the contained tokens.
+ *
+ * The individual helpstrings are expected to be in the same order as their
+ * respective Tokens appear in the cmdstr. They should each be terminated with
+ * a linefeed. The last helpstring should be terminated with a linefeed as well.
+ *
+ * Care should also be taken to avoid having similar tokens with different
+ * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp".
+ * they both contain a helpstring for "show", but only one will be displayed
+ * when the user enters "sh?". If those two helpstrings differ, it is not
+ * defined which one will be shown and the behavior is therefore unpredictable.
+ */
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+
+/* DEFUN_NOSH for commands that vtysh should ignore */
+#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+/* DEFSH for vtysh. */
+#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+
+/* DEFUN + DEFSH */
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* DEFUN + DEFSH with attributes */
+#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/* ALIAS macro which define existing command's alias. */
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+
+#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+
+#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+
+#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+
+#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+
+#endif /* VTYSH_EXTRACT_PL */
+
+/*
+ * Sometimes #defines create maximum values that
+ * need to have strings created from them that
+ * allow the parser to match against them.
+ * These macros allow that.
+ */
+#define CMD_CREATE_STR(s)  CMD_CREATE_STR_HELPER(s)
+#define CMD_CREATE_STR_HELPER(s) #s
+#define CMD_RANGE_STR(a,s) "<" CMD_CREATE_STR(a) "-" CMD_CREATE_STR(s) ">"
+
+
+/* Common descriptions. */
+#define SHOW_STR "Show running system information\n"
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define NO_STR "Negate a command or set its defaults\n"
+#define REDIST_STR "Redistribute information from another routing protocol\n"
+#define CLEAR_STR "Reset functions\n"
+#define RIP_STR "RIP information\n"
+#define BGP_STR "BGP information\n"
+#define BGP_SOFT_STR "Soft reconfig inbound and outbound updates\n"
+#define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n"
+#define BGP_SOFT_OUT_STR "Resend all outbound updates\n"
+#define BGP_SOFT_RSCLIENT_RIB_STR "Soft reconfig for rsclient RIB\n"
+#define OSPF_STR "OSPF information\n"
+#define NEIGHBOR_STR "Specify neighbor router\n"
+#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
+#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
+#define ROUTER_STR "Enable a routing process\n"
+#define AS_STR "AS number\n"
+#define MBGP_STR "MBGP information\n"
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+#define OUT_STR "Filter outgoing routing updates\n"
+#define IN_STR  "Filter incoming routing updates\n"
+#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
+#define OSPF6_NUMBER_STR "Specify by number\n"
+#define INTERFACE_STR "Interface infomation\n"
+#define IFNAME_STR "Interface name(e.g. ep0)\n"
+#define IP6_STR "IPv6 Information\n"
+#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
+#define OSPF6_ROUTER_STR "Enable a routing process\n"
+#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
+#define SECONDS_STR "<1-65535> Seconds\n"
+#define ROUTE_STR "Routing Table\n"
+#define PREFIX_LIST_STR "Build a prefix list\n"
+#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 AREA_TAG_STR "[area tag]\n"
+#define MPLS_TE_STR "MPLS-TE specific commands\n"
+#define LINK_PARAMS_STR "Configure interface link parameters\n"
+#define OSPF_RI_STR "OSPF Router Information specific commands\n"
+#define PCE_STR "PCE Router Information specific commands\n"
+
+#define CONF_BACKUP_EXT ".sav"
+
+/* IPv4 only machine should not accept IPv6 address for peer's IP
+   address.  So we replace VTY command string like below. */
+#ifdef HAVE_IPV6
+#define NEIGHBOR_CMD       "neighbor (A.B.C.D|X:X::X:X) "
+#define NO_NEIGHBOR_CMD    "no neighbor (A.B.C.D|X:X::X:X) "
+#define NEIGHBOR_ADDR_STR  "Neighbor address\nIPv6 address\n"
+#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
+#else
+#define NEIGHBOR_CMD       "neighbor A.B.C.D "
+#define NO_NEIGHBOR_CMD    "no neighbor A.B.C.D "
+#define NEIGHBOR_ADDR_STR  "Neighbor address\n"
+#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|WORD) "
+#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
+#endif /* HAVE_IPV6 */
+
+/* Prototypes. */
+extern void install_node (struct cmd_node *, int (*) (struct vty *));
+extern void install_default (enum node_type);
+extern void install_element (enum node_type, struct cmd_element *);
+
+/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
+   string with a space between each element (allocated using
+   XMALLOC(MTYPE_TMP)).  Returns NULL if shift >= argc. */
+extern char *argv_concat (const char **argv, int argc, int shift);
+
+extern vector cmd_make_strvec (const char *);
+extern void cmd_free_strvec (vector);
+extern vector cmd_describe_command (vector, struct vty *, int *status);
+extern char **cmd_complete_command (vector, struct vty *, int *status);
+extern char **cmd_complete_command_lib (vector, struct vty *, int *status, int islib);
+extern const char *cmd_prompt (enum node_type);
+extern int command_config_read_one_line (struct vty *vty, struct cmd_element **, int use_config_node);
+extern int config_from_file (struct vty *, FILE *, unsigned int *line_num);
+extern enum node_type node_parent (enum node_type);
+extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int);
+extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **);
+extern void cmd_init (int);
+extern void cmd_terminate (void);
+
+/* Export typical functions. */
+extern struct cmd_element config_end_cmd;
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_quit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+extern const char *host_config_get (void);
+extern void host_config_set (char *);
+
+extern void print_version (const char *);
+
+/* struct host global, ick */
+extern struct host host; 
+
+/* "<cr>" global */
+extern char *command_cr;
+#endif /* _ZEBRA_COMMAND_H */
diff --git a/lib/daemon.c b/lib/daemon.c
new file mode 100644 (file)
index 0000000..c473555
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Daemonize routine
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro
+ * 
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <log.h>
+
+#ifndef HAVE_DAEMON
+
+/* Daemonize myself. */
+int
+daemon (int nochdir, int noclose)
+{
+  pid_t pid;
+
+  pid = fork ();
+
+  /* In case of fork is error. */
+  if (pid < 0)
+    {
+      zlog_err ("fork failed: %s", safe_strerror(errno));
+      return -1;
+    }
+
+  /* In case of this is parent process. */
+  if (pid != 0)
+    exit (0);
+
+  /* Become session leader and get pid. */
+  pid = setsid();
+
+  if (pid == -1)
+    {
+      zlog_err ("setsid failed: %s", safe_strerror(errno));
+      return -1;
+    }
+
+  /* Change directory to root. */
+  if (! nochdir)
+    chdir ("/");
+
+  /* File descriptor close. */
+  if (! noclose)
+    {
+      int fd;
+
+      fd = open ("/dev/null", O_RDWR, 0);
+      if (fd != -1)
+       {
+         dup2 (fd, STDIN_FILENO);
+         dup2 (fd, STDOUT_FILENO);
+         dup2 (fd, STDERR_FILENO);
+         if (fd > 2)
+           close (fd);
+       }
+    }
+
+  umask (0027);
+
+  return 0;
+}
+
+#endif /* HAVE_DAEMON */
diff --git a/lib/distribute.c b/lib/distribute.c
new file mode 100644 (file)
index 0000000..583befb
--- /dev/null
@@ -0,0 +1,1020 @@
+/* Distribute list functions
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "if.h"
+#include "filter.h"
+#include "command.h"
+#include "distribute.h"
+#include "memory.h"
+
+/* Hash of distribute list. */
+struct hash *disthash;
+
+/* Hook functions. */
+void (*distribute_add_hook) (struct distribute *);
+void (*distribute_delete_hook) (struct distribute *);
+
+static struct distribute *
+distribute_new (void)
+{
+  return XCALLOC (MTYPE_DISTRIBUTE, sizeof (struct distribute));
+}
+
+/* Free distribute object. */
+static void
+distribute_free (struct distribute *dist)
+{
+  int i = 0;
+  if (dist->ifname)
+    XFREE (MTYPE_DISTRIBUTE_IFNAME, dist->ifname);
+
+  for (i=0; i < DISTRIBUTE_MAX; i++)
+    if (dist->list[i])
+      free(dist->list[i]);
+
+  for (i=0; i < DISTRIBUTE_MAX; i++)
+    if (dist->prefix[i])
+      free(dist->prefix[i]);
+
+  XFREE (MTYPE_DISTRIBUTE, dist);
+}
+
+static void
+distribute_free_if_empty(struct distribute *dist)
+{
+  int i;
+  for (i=0; i < DISTRIBUTE_MAX; i++)
+    if (dist->list[i] != NULL || dist->prefix[i] != NULL)
+      return;
+
+  hash_release (disthash, dist);
+  distribute_free (dist);
+}
+
+/* Lookup interface's distribute list. */
+struct distribute *
+distribute_lookup (const char *ifname)
+{
+  struct distribute key;
+  struct distribute *dist;
+
+  /* temporary reference */
+  key.ifname = (char *)ifname;
+
+  dist = hash_lookup (disthash, &key);
+  
+  return dist;
+}
+
+void
+distribute_list_add_hook (void (*func) (struct distribute *))
+{
+  distribute_add_hook = func;
+}
+
+void
+distribute_list_delete_hook (void (*func) (struct distribute *))
+{
+  distribute_delete_hook = func;
+}
+
+static void *
+distribute_hash_alloc (struct distribute *arg)
+{
+  struct distribute *dist;
+
+  dist = distribute_new ();
+  if (arg->ifname)
+    dist->ifname = XSTRDUP (MTYPE_DISTRIBUTE_IFNAME, arg->ifname);
+  else
+    dist->ifname = NULL;
+  return dist;
+}
+
+/* Make new distribute list and push into hash. */
+static struct distribute *
+distribute_get (const char *ifname)
+{
+  struct distribute key;
+
+  /* temporary reference */
+  key.ifname = (char *)ifname;
+  
+  return hash_get (disthash, &key, (void * (*) (void *))distribute_hash_alloc);
+}
+
+static unsigned int
+distribute_hash_make (void *arg)
+{
+  const struct distribute *dist = arg;
+
+  return dist->ifname ? string_hash_make (dist->ifname) : 0;
+}
+
+/* If two distribute-list have same value then return 1 else return
+   0. This function is used by hash package. */
+static int
+distribute_cmp (const struct distribute *dist1, const struct distribute *dist2)
+{
+  if (dist1->ifname && dist2->ifname)
+    if (strcmp (dist1->ifname, dist2->ifname) == 0)
+      return 1;
+  if (! dist1->ifname && ! dist2->ifname)
+    return 1;
+  return 0;
+}
+
+/* Set access-list name to the distribute list. */
+static struct distribute *
+distribute_list_set (const char *ifname, enum distribute_type type,
+                     const char *alist_name)
+{
+  struct distribute *dist;
+
+  dist = distribute_get (ifname);
+
+  if (dist->list[type])
+    free (dist->list[type]);
+  dist->list[type] = strdup (alist_name);
+
+  /* Apply this distribute-list to the interface. */
+  (*distribute_add_hook) (dist);
+
+  return dist;
+}
+
+/* Unset distribute-list.  If matched distribute-list exist then
+   return 1. */
+static int
+distribute_list_unset (const char *ifname, enum distribute_type type,
+                      const char *alist_name)
+{
+  struct distribute *dist;
+
+  dist = distribute_lookup (ifname);
+  if (!dist)
+    return 0;
+
+  if (!dist->list[type])
+    return 0;
+  if (strcmp (dist->list[type], alist_name) != 0)
+    return 0;
+
+  free (dist->list[type]);
+  dist->list[type] = NULL;
+
+  /* Apply this distribute-list to the interface. */
+  (*distribute_delete_hook) (dist);
+
+  /* If all dist are NULL, then free distribute list. */
+  distribute_free_if_empty(dist);
+  return 1;
+}
+
+/* Set access-list name to the distribute list. */
+static struct distribute *
+distribute_list_prefix_set (const char *ifname, enum distribute_type type,
+                           const char *plist_name)
+{
+  struct distribute *dist;
+
+  dist = distribute_get (ifname);
+
+  if (dist->prefix[type])
+    free (dist->prefix[type]);
+  dist->prefix[type] = strdup (plist_name);
+
+  /* Apply this distribute-list to the interface. */
+  (*distribute_add_hook) (dist);
+  
+  return dist;
+}
+
+/* Unset distribute-list.  If matched distribute-list exist then
+   return 1. */
+static int
+distribute_list_prefix_unset (const char *ifname, enum distribute_type type,
+                             const char *plist_name)
+{
+  struct distribute *dist;
+
+  dist = distribute_lookup (ifname);
+  if (!dist)
+    return 0;
+
+  if (!dist->prefix[type])
+    return 0;
+  if (strcmp (dist->prefix[type], plist_name) != 0)
+    return 0;
+
+  free (dist->prefix[type]);
+  dist->prefix[type] = NULL;
+
+  /* Apply this distribute-list to the interface. */
+  (*distribute_delete_hook) (dist);
+
+  /* If all dist are NULL, then free distribute list. */
+  distribute_free_if_empty(dist);
+  return 1;
+}
+
+DEFUN (distribute_list_all,
+       distribute_list_all_cmd,
+       "distribute-list WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_set (NULL, type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_distribute_list_all,
+       ipv6_distribute_list_all_cmd,
+       "ipv6 distribute-list WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_set (NULL, type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_distribute_list_all,
+       ipv6_as_v4_distribute_list_all_cmd,
+       "distribute-list WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFUN (no_distribute_list_all,
+       no_distribute_list_all_cmd,
+       "no distribute-list WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = distribute_list_unset (NULL, type, argv[0]);
+  if (! ret)
+    {
+      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_distribute_list_all,
+       no_ipv6_distribute_list_all_cmd,
+       "no ipv6 distribute-list WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = distribute_list_unset (NULL, type, argv[0]);
+  if (! ret)
+  {
+    vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_distribute_list_all,
+       no_ipv6_as_v4_distribute_list_all_cmd,
+       "no distribute-list WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFUN (distribute_list,
+       distribute_list_cmd,
+       "distribute-list WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_set (argv[2], type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_distribute_list,
+       ipv6_distribute_list_cmd,
+       "ipv6 distribute-list WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_set (argv[2], type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_distribute_list,
+       ipv6_as_v4_distribute_list_cmd,
+       "distribute-list WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFUN (no_distribute_list, no_distribute_list_cmd,
+       "no distribute-list WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = distribute_list_unset (argv[2], type, argv[0]);
+  if (! ret)
+    {
+      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_distribute_list,
+       no_ipv6_distribute_list_cmd,
+       "no ipv6 distribute-list WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = distribute_list_unset (argv[2], type, argv[0]);
+  if (! ret)
+  {
+    vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_distribute_list,
+       no_ipv6_as_v4_distribute_list_cmd,
+       "no distribute-list WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFUN (distribute_list_prefix_all,
+       distribute_list_prefix_all_cmd,
+       "distribute-list prefix WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_prefix_set (NULL, type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_distribute_list_prefix_all,
+       ipv6_distribute_list_prefix_all_cmd,
+       "ipv6 distribute-list prefix WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_prefix_set (NULL, type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_distribute_list_prefix_all,
+       ipv6_as_v4_distribute_list_prefix_all_cmd,
+       "distribute-list prefix WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFUN (no_distribute_list_prefix_all,
+       no_distribute_list_prefix_all_cmd,
+       "no distribute-list prefix WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = distribute_list_prefix_unset (NULL, type, argv[0]);
+  if (! ret)
+    {
+      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_distribute_list_prefix_all,
+       no_ipv6_distribute_list_prefix_all_cmd,
+       "no ipv6 distribute-list prefix WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = distribute_list_prefix_unset (NULL, type, argv[0]);
+  if (! ret)
+  {
+    vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_distribute_list_prefix_all,
+       no_ipv6_as_v4_distribute_list_prefix_all_cmd,
+       "no distribute-list prefix WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFUN (distribute_list_prefix, distribute_list_prefix_cmd,
+       "distribute-list prefix WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_prefix_set (argv[2], type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_distribute_list_prefix,
+       ipv6_distribute_list_prefix_cmd,
+       "ipv6 distribute-list prefix WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Get interface name corresponding distribute list. */
+  distribute_list_prefix_set (argv[2], type, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_distribute_list_prefix,
+       ipv6_as_v4_distribute_list_prefix_cmd,
+       "distribute-list prefix WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd,
+       "no distribute-list prefix WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V4_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V4_OUT;
+  else
+    {
+      vty_out (vty, "distribute list direction must be [in|out]%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = distribute_list_prefix_unset (argv[2], type, argv[0]);
+  if (! ret)
+    {
+      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_distribute_list_prefix,
+       no_ipv6_distribute_list_prefix_cmd,
+       "no ipv6 distribute-list prefix WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+{
+  int ret;
+  enum distribute_type type;
+
+  /* Check of distribute list type. */
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = DISTRIBUTE_V6_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = DISTRIBUTE_V6_OUT;
+  else
+  {
+    vty_out (vty, "distribute list direction must be [in|out]%s",
+             VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ret = distribute_list_prefix_unset (argv[2], type, argv[0]);
+  if (! ret)
+  {
+    vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_distribute_list_prefix,
+       no_ipv6_as_v4_distribute_list_prefix_cmd,
+       "no distribute-list prefix WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+static int
+distribute_print (struct vty *vty, char *tab[], int is_prefix,
+                  enum distribute_type type, int has_print)
+{
+  if (tab[type]) {
+    vty_out (vty, "%s %s%s",
+             has_print ? "," : "",
+             is_prefix ? "(prefix-list) " : "",
+             tab[type]);
+    return 1;
+  }
+  return has_print;
+}
+
+int
+config_show_distribute (struct vty *vty)
+{
+  unsigned int i;
+  int has_print = 0;
+  struct hash_backet *mp;
+  struct distribute *dist;
+
+  /* Output filter configuration. */
+  dist = distribute_lookup (NULL);
+  vty_out(vty, "  Outgoing update filter list for all interface is");
+  has_print = 0;
+  if (dist)
+    {
+      has_print = distribute_print(vty, dist->list,   0,
+                                   DISTRIBUTE_V4_OUT, has_print);
+      has_print = distribute_print(vty, dist->prefix, 1,
+                                   DISTRIBUTE_V4_OUT, has_print);
+      has_print = distribute_print(vty, dist->list,   0,
+                                   DISTRIBUTE_V6_OUT, has_print);
+      has_print = distribute_print(vty, dist->prefix, 1,
+                                   DISTRIBUTE_V6_OUT, has_print);
+    }
+  if (has_print)
+    vty_out (vty, "%s", VTY_NEWLINE);
+  else
+    vty_out (vty, " not set%s", VTY_NEWLINE);
+
+  for (i = 0; i < disthash->size; i++)
+    for (mp = disthash->index[i]; mp; mp = mp->next)
+      {
+       dist = mp->data;
+       if (dist->ifname)
+          {
+            vty_out (vty, "    %s filtered by", dist->ifname);
+            has_print = 0;
+            has_print = distribute_print(vty, dist->list,   0,
+                                         DISTRIBUTE_V4_OUT, has_print);
+            has_print = distribute_print(vty, dist->prefix, 1,
+                                         DISTRIBUTE_V4_OUT, has_print);
+            has_print = distribute_print(vty, dist->list,   0,
+                                         DISTRIBUTE_V6_OUT, has_print);
+            has_print = distribute_print(vty, dist->prefix, 1,
+                                         DISTRIBUTE_V6_OUT, has_print);
+            if (has_print)
+              vty_out (vty, "%s", VTY_NEWLINE);
+            else
+              vty_out(vty, " nothing%s", VTY_NEWLINE);
+          }
+      }
+
+
+  /* Input filter configuration. */
+  dist = distribute_lookup (NULL);
+  vty_out(vty, "  Incoming update filter list for all interface is");
+  has_print = 0;
+  if (dist)
+    {
+      has_print = distribute_print(vty, dist->list,   0,
+                                   DISTRIBUTE_V4_IN, has_print);
+      has_print = distribute_print(vty, dist->prefix, 1,
+                                   DISTRIBUTE_V4_IN, has_print);
+      has_print = distribute_print(vty, dist->list,   0,
+                                   DISTRIBUTE_V6_IN, has_print);
+      has_print = distribute_print(vty, dist->prefix, 1,
+                                   DISTRIBUTE_V6_IN, has_print);
+    }
+  if (has_print)
+    vty_out (vty, "%s", VTY_NEWLINE);
+  else
+    vty_out (vty, " not set%s", VTY_NEWLINE);
+
+  for (i = 0; i < disthash->size; i++)
+    for (mp = disthash->index[i]; mp; mp = mp->next)
+      {
+       dist = mp->data;
+        if (dist->ifname)
+          {
+            vty_out (vty, "    %s filtered by", dist->ifname);
+            has_print = 0;
+            has_print = distribute_print(vty, dist->list,   0,
+                                         DISTRIBUTE_V4_IN, has_print);
+            has_print = distribute_print(vty, dist->prefix, 1,
+                                         DISTRIBUTE_V4_IN, has_print);
+            has_print = distribute_print(vty, dist->list,   0,
+                                         DISTRIBUTE_V6_IN, has_print);
+            has_print = distribute_print(vty, dist->prefix, 1,
+                                         DISTRIBUTE_V6_IN, has_print);
+            if (has_print)
+              vty_out (vty, "%s", VTY_NEWLINE);
+            else
+              vty_out(vty, " nothing%s", VTY_NEWLINE);
+          }
+      }
+  return 0;
+}
+
+/* Configuration write function. */
+int
+config_write_distribute (struct vty *vty)
+{
+  unsigned int i;
+  int j;
+  int output, v6;
+  struct hash_backet *mp;
+  int write = 0;
+
+  for (i = 0; i < disthash->size; i++)
+    for (mp = disthash->index[i]; mp; mp = mp->next)
+      {
+       struct distribute *dist;
+
+       dist = mp->data;
+
+       for (j=0; j < DISTRIBUTE_MAX; j++)
+         if (dist->list[j]) {
+           output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT;
+            v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT;
+           vty_out (vty, " %sdistribute-list %s %s %s%s",
+                     v6 ? "ipv6 " : "",
+                    dist->list[j],
+                    output ? "out" : "in",
+                    dist->ifname ? dist->ifname : "",
+                    VTY_NEWLINE);
+           write++;
+         }
+
+       for (j=0; j < DISTRIBUTE_MAX; j++)
+         if (dist->prefix[j]) {
+           output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT;
+            v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT;
+           vty_out (vty, " %sdistribute-list prefix %s %s %s%s",
+                     v6 ? "ipv6 " : "",
+                    dist->prefix[j],
+                    output ? "out" : "in",
+                    dist->ifname ? dist->ifname : "",
+                    VTY_NEWLINE);
+           write++;
+         }
+      }
+  return write;
+}
+
+/* Clear all distribute list. */
+void
+distribute_list_reset ()
+{
+  hash_clean (disthash, (void (*) (void *)) distribute_free);
+}
+
+/* Initialize distribute list related hash. */
+void
+distribute_list_init (int node)
+{
+  disthash = hash_create (distribute_hash_make,
+                          (int (*) (const void *, const void *)) distribute_cmp);
+  /* install v4 */
+  if (node == RIP_NODE || node == BABEL_NODE) {
+    install_element (node, &distribute_list_all_cmd);
+    install_element (node, &no_distribute_list_all_cmd);
+    install_element (node, &distribute_list_cmd);
+    install_element (node, &no_distribute_list_cmd);
+    install_element (node, &distribute_list_prefix_all_cmd);
+    install_element (node, &no_distribute_list_prefix_all_cmd);
+    install_element (node, &distribute_list_prefix_cmd);
+    install_element (node, &no_distribute_list_prefix_cmd);
+  }
+
+  /* install v6 */
+  if (node == RIPNG_NODE || node == BABEL_NODE) {
+    install_element (node, &ipv6_distribute_list_all_cmd);
+    install_element (node, &no_ipv6_distribute_list_all_cmd);
+    install_element (node, &ipv6_distribute_list_cmd);
+    install_element (node, &no_ipv6_distribute_list_cmd);
+    install_element (node, &ipv6_distribute_list_prefix_all_cmd);
+    install_element (node, &no_ipv6_distribute_list_prefix_all_cmd);
+    install_element (node, &ipv6_distribute_list_prefix_cmd);
+    install_element (node, &no_ipv6_distribute_list_prefix_cmd);
+  }
+
+  /* install v4 syntax command for v6 only protocols. */
+  if (node == RIPNG_NODE) {
+    install_element (node, &ipv6_as_v4_distribute_list_all_cmd);
+    install_element (node, &no_ipv6_as_v4_distribute_list_all_cmd);
+    install_element (node, &ipv6_as_v4_distribute_list_cmd);
+    install_element (node, &no_ipv6_as_v4_distribute_list_cmd);
+    install_element (node, &ipv6_as_v4_distribute_list_prefix_all_cmd);
+    install_element (node, &no_ipv6_as_v4_distribute_list_prefix_all_cmd);
+    install_element (node, &ipv6_as_v4_distribute_list_prefix_cmd);
+    install_element (node, &no_ipv6_as_v4_distribute_list_prefix_cmd);
+  }
+}
diff --git a/lib/distribute.h b/lib/distribute.h
new file mode 100644 (file)
index 0000000..e9625a3
--- /dev/null
@@ -0,0 +1,63 @@
+/* Distribute list functions header
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_DISTRIBUTE_H
+#define _ZEBRA_DISTRIBUTE_H
+
+#include <zebra.h>
+#include "if.h"
+#include "filter.h"
+
+/* Disctirubte list types. */
+enum distribute_type
+{
+  DISTRIBUTE_V4_IN,
+  DISTRIBUTE_V6_IN,
+  DISTRIBUTE_V4_OUT,
+  DISTRIBUTE_V6_OUT,
+  DISTRIBUTE_MAX
+};
+
+struct distribute
+{
+  /* Name of the interface. */
+  char *ifname;
+
+  /* Filter name of `in' and `out' */
+  char *list[DISTRIBUTE_MAX];
+
+  /* prefix-list name of `in' and `out' */
+  char *prefix[DISTRIBUTE_MAX];
+};
+
+/* Prototypes for distribute-list. */
+extern void distribute_list_init (int);
+extern void distribute_list_reset (void);
+extern void distribute_list_add_hook (void (*) (struct distribute *));
+extern void distribute_list_delete_hook (void (*) (struct distribute *));
+extern struct distribute *distribute_lookup (const char *);
+extern int config_write_distribute (struct vty *);
+extern int config_show_distribute (struct vty *);
+
+extern enum filter_type distribute_apply_in (struct interface *, struct prefix *);
+extern enum filter_type distribute_apply_out (struct interface *, struct prefix *);
+
+#endif /* _ZEBRA_DISTRIBUTE_H */
diff --git a/lib/event_counter.c b/lib/event_counter.c
new file mode 100644 (file)
index 0000000..e94aa4c
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Christian Franke
+ *
+ * This file is part of Quagga.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, you can use, redistribute and/or modify it under the
+ * following terms:
+ *
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "event_counter.h"
+
+void
+event_counter_inc (struct event_counter *counter)
+{
+  counter->count++;
+  counter->last = time (NULL);
+}
+
+const char *
+event_counter_format (const struct event_counter *counter)
+{
+  struct tm last_change_store;
+  struct tm *last_change;
+  char timebuf[sizeof ("Thu, 01 Jan 1970 00:00:00 +0000")];
+  static char rv[20 + sizeof ("  last: ") + sizeof (timebuf)];
+
+  last_change = localtime_r (&counter->last, &last_change_store);
+  if (!last_change || strftime (timebuf, sizeof (timebuf),
+                                "%a, %d %b %Y %T %z", last_change) == 0)
+    {
+      strncpy (timebuf, "???", sizeof (timebuf));
+    }
+
+  snprintf (rv, sizeof (rv), "%5llu  last: %s", counter->count,
+            counter->last ? timebuf : "(never)");
+  return rv;
+}
diff --git a/lib/event_counter.h b/lib/event_counter.h
new file mode 100644 (file)
index 0000000..f40c6cd
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Christian Franke
+ *
+ * This file is part of Quagga.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, you can use, redistribute and/or modify it under the
+ * following terms:
+ *
+ * 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.
+ */
+
+#ifndef _ZEBRA_EVENT_COUNTER_H
+#define _ZEBRA_EVENT_COUNTER_H
+
+struct event_counter
+{
+  unsigned long long count;
+  time_t last;
+};
+
+void event_counter_inc (struct event_counter *counter);
+const char *event_counter_format (const struct event_counter *counter);
+
+#endif
diff --git a/lib/fifo.h b/lib/fifo.h
new file mode 100644 (file)
index 0000000..47a1e88
--- /dev/null
@@ -0,0 +1,63 @@
+/* FIFO common header.
+   Copyright (C) 2015 Kunihiro Ishiguro
+
+This file is part of Quagga.
+
+Quagga is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+Quagga is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+#ifndef __LIB_FIFO_H__
+#define __LIB_FIFO_H__
+
+/* FIFO -- first in first out structure and macros.  */
+struct fifo
+{
+  struct fifo *next;
+  struct fifo *prev;
+  u_int32_t count;
+};
+
+#define FIFO_INIT(F)                                  \
+  do {                                                \
+    struct fifo *Xfifo = (struct fifo *)(F);          \
+    Xfifo->next = Xfifo->prev = Xfifo;                \
+  } while (0)
+
+#define FIFO_ADD(F,N)                                 \
+  do {                                                \
+    struct fifo *Xfifo = (struct fifo *)(F);          \
+    struct fifo *Xnode = (struct fifo *)(N);          \
+    Xnode->next = Xfifo;                              \
+    Xnode->prev = Xfifo->prev;                        \
+    Xfifo->prev = Xfifo->prev->next = Xnode;          \
+  } while (0)
+
+#define FIFO_DEL(N)                                   \
+  do {                                                \
+    struct fifo *Xnode = (struct fifo *)(N);          \
+    Xnode->prev->next = Xnode->next;                  \
+    Xnode->next->prev = Xnode->prev;                  \
+  } while (0)
+
+#define FIFO_HEAD(F)                                  \
+  ((((struct fifo *)(F))->next == (struct fifo *)(F)) \
+  ? NULL : (F)->next)
+
+#define FIFO_EMPTY(F)                                 \
+  (((struct fifo *)(F))->next == (struct fifo *)(F))
+
+#define FIFO_TOP(F)                                   \
+  (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next)
+
+#endif /* __LIB_FIFO_H__ */
diff --git a/lib/filter.c b/lib/filter.c
new file mode 100644 (file)
index 0000000..a472941
--- /dev/null
@@ -0,0 +1,2036 @@
+/* Route filtering function.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "memory.h"
+#include "command.h"
+#include "sockunion.h"
+#include "buffer.h"
+#include "log.h"
+
+struct filter_cisco
+{
+  /* Cisco access-list */
+  int extended;
+  struct in_addr addr;
+  struct in_addr addr_mask;
+  struct in_addr mask;
+  struct in_addr mask_mask;
+};
+
+struct filter_zebra
+{
+  /* If this filter is "exact" match then this flag is set. */
+  int exact;
+
+  /* Prefix information. */
+  struct prefix prefix;
+};
+
+/* Filter element of access list */
+struct filter
+{
+  /* For doubly linked list. */
+  struct filter *next;
+  struct filter *prev;
+
+  /* Filter type information. */
+  enum filter_type type;
+
+  /* Cisco access-list */
+  int cisco;
+
+  union
+    {
+      struct filter_cisco cfilter;
+      struct filter_zebra zfilter;
+    } u;
+};
+
+/* List of access_list. */
+struct access_list_list
+{
+  struct access_list *head;
+  struct access_list *tail;
+};
+
+/* Master structure of access_list. */
+struct access_master
+{
+  /* List of access_list which name is number. */
+  struct access_list_list num;
+
+  /* List of access_list which name is string. */
+  struct access_list_list str;
+
+  /* Hook function which is executed when new access_list is added. */
+  void (*add_hook) (struct access_list *);
+
+  /* Hook function which is executed when access_list is deleted. */
+  void (*delete_hook) (struct access_list *);
+};
+
+/* Static structure for IPv4 access_list's master. */
+static struct access_master access_master_ipv4 = 
+{ 
+  {NULL, NULL},
+  {NULL, NULL},
+  NULL,
+  NULL,
+};
+
+#ifdef HAVE_IPV6
+/* Static structure for IPv6 access_list's master. */
+static struct access_master access_master_ipv6 = 
+{ 
+  {NULL, NULL},
+  {NULL, NULL},
+  NULL,
+  NULL,
+};
+#endif /* HAVE_IPV6 */
+
+static struct access_master *
+access_master_get (afi_t afi)
+{
+  if (afi == AFI_IP)
+    return &access_master_ipv4;
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6)
+    return &access_master_ipv6;
+#endif /* HAVE_IPV6 */
+  return NULL;
+}
+
+/* Allocate new filter structure. */
+static struct filter *
+filter_new (void)
+{
+  return (struct filter *) XCALLOC (MTYPE_ACCESS_FILTER,
+                                   sizeof (struct filter));
+}
+
+static void
+filter_free (struct filter *filter)
+{
+  XFREE (MTYPE_ACCESS_FILTER, filter);
+}
+
+/* Return string of filter_type. */
+static const char *
+filter_type_str (struct filter *filter)
+{
+  switch (filter->type)
+    {
+    case FILTER_PERMIT:
+      return "permit";
+      break;
+    case FILTER_DENY:
+      return "deny";
+      break;
+    case FILTER_DYNAMIC:
+      return "dynamic";
+      break;
+    default:
+      return "";
+      break;
+    }
+}
+
+/* If filter match to the prefix then return 1. */
+static int
+filter_match_cisco (struct filter *mfilter, struct prefix *p)
+{
+  struct filter_cisco *filter;
+  struct in_addr mask;
+  u_int32_t check_addr;
+  u_int32_t check_mask;
+
+  filter = &mfilter->u.cfilter;
+  check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr;
+
+  if (filter->extended)
+    {
+      masklen2ip (p->prefixlen, &mask);
+      check_mask = mask.s_addr & ~filter->mask_mask.s_addr;
+
+      if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0
+          && memcmp (&check_mask, &filter->mask.s_addr, 4) == 0)
+       return 1;
+    }
+  else if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0)
+    return 1;
+
+  return 0;
+}
+
+/* If filter match to the prefix then return 1. */
+static int
+filter_match_zebra (struct filter *mfilter, struct prefix *p)
+{
+  struct filter_zebra *filter;
+
+  filter = &mfilter->u.zfilter;
+
+  if (filter->prefix.family == p->family)
+    {
+      if (filter->exact)
+       {
+         if (filter->prefix.prefixlen == p->prefixlen)
+           return prefix_match (&filter->prefix, p);
+         else
+           return 0;
+       }
+      else
+       return prefix_match (&filter->prefix, p);
+    }
+  else
+    return 0;
+}
+
+/* Allocate new access list structure. */
+static struct access_list *
+access_list_new (void)
+{
+  return (struct access_list *) XCALLOC (MTYPE_ACCESS_LIST,
+                                        sizeof (struct access_list));
+}
+
+/* Free allocated access_list. */
+static void
+access_list_free (struct access_list *access)
+{
+  XFREE (MTYPE_ACCESS_LIST, access);
+}
+
+/* Delete access_list from access_master and free it. */
+static void
+access_list_delete (struct access_list *access)
+{
+  struct filter *filter;
+  struct filter *next;
+  struct access_list_list *list;
+  struct access_master *master;
+
+  for (filter = access->head; filter; filter = next)
+    {
+      next = filter->next;
+      filter_free (filter);
+    }
+
+  master = access->master;
+
+  if (access->type == ACCESS_TYPE_NUMBER)
+    list = &master->num;
+  else
+    list = &master->str;
+
+  if (access->next)
+    access->next->prev = access->prev;
+  else
+    list->tail = access->prev;
+
+  if (access->prev)
+    access->prev->next = access->next;
+  else
+    list->head = access->next;
+
+  if (access->name)
+    XFREE (MTYPE_ACCESS_LIST_STR, access->name);
+
+  if (access->remark)
+    XFREE (MTYPE_TMP, access->remark);
+
+  access_list_free (access);
+}
+
+/* Insert new access list to list of access_list.  Each acceess_list
+   is sorted by the name. */
+static struct access_list *
+access_list_insert (afi_t afi, const char *name)
+{
+  unsigned int i;
+  long number;
+  struct access_list *access;
+  struct access_list *point;
+  struct access_list_list *alist;
+  struct access_master *master;
+
+  master = access_master_get (afi);
+  if (master == NULL)
+    return NULL;
+
+  /* Allocate new access_list and copy given name. */
+  access = access_list_new ();
+  access->name = XSTRDUP (MTYPE_ACCESS_LIST_STR, name);
+  access->master = master;
+
+  /* If name is made by all digit character.  We treat it as
+     number. */
+  for (number = 0, i = 0; i < strlen (name); i++)
+    {
+      if (isdigit ((int) name[i]))
+       number = (number * 10) + (name[i] - '0');
+      else
+       break;
+    }
+
+  /* In case of name is all digit character */
+  if (i == strlen (name))
+    {
+      access->type = ACCESS_TYPE_NUMBER;
+
+      /* Set access_list to number list. */
+      alist = &master->num;
+
+      for (point = alist->head; point; point = point->next)
+       if (atol (point->name) >= number)
+         break;
+    }
+  else
+    {
+      access->type = ACCESS_TYPE_STRING;
+
+      /* Set access_list to string list. */
+      alist = &master->str;
+  
+      /* Set point to insertion point. */
+      for (point = alist->head; point; point = point->next)
+       if (strcmp (point->name, name) >= 0)
+         break;
+    }
+
+  /* In case of this is the first element of master. */
+  if (alist->head == NULL)
+    {
+      alist->head = alist->tail = access;
+      return access;
+    }
+
+  /* In case of insertion is made at the tail of access_list. */
+  if (point == NULL)
+    {
+      access->prev = alist->tail;
+      alist->tail->next = access;
+      alist->tail = access;
+      return access;
+    }
+
+  /* In case of insertion is made at the head of access_list. */
+  if (point == alist->head)
+    {
+      access->next = alist->head;
+      alist->head->prev = access;
+      alist->head = access;
+      return access;
+    }
+
+  /* Insertion is made at middle of the access_list. */
+  access->next = point;
+  access->prev = point->prev;
+
+  if (point->prev)
+    point->prev->next = access;
+  point->prev = access;
+
+  return access;
+}
+
+/* Lookup access_list from list of access_list by name. */
+struct access_list *
+access_list_lookup (afi_t afi, const char *name)
+{
+  struct access_list *access;
+  struct access_master *master;
+
+  if (name == NULL)
+    return NULL;
+
+  master = access_master_get (afi);
+  if (master == NULL)
+    return NULL;
+
+  for (access = master->num.head; access; access = access->next)
+    if (strcmp (access->name, name) == 0)
+      return access;
+
+  for (access = master->str.head; access; access = access->next)
+    if (strcmp (access->name, name) == 0)
+      return access;
+
+  return NULL;
+}
+
+/* Get access list from list of access_list.  If there isn't matched
+   access_list create new one and return it. */
+static struct access_list *
+access_list_get (afi_t afi, const char *name)
+{
+  struct access_list *access;
+
+  access = access_list_lookup (afi, name);
+  if (access == NULL)
+    access = access_list_insert (afi, name);
+  return access;
+}
+
+/* Apply access list to object (which should be struct prefix *). */
+enum filter_type
+access_list_apply (struct access_list *access, void *object)
+{
+  struct filter *filter;
+  struct prefix *p;
+
+  p = (struct prefix *) object;
+
+  if (access == NULL)
+    return FILTER_DENY;
+
+  for (filter = access->head; filter; filter = filter->next)
+    {
+      if (filter->cisco)
+       {
+         if (filter_match_cisco (filter, p))
+           return filter->type;
+       }
+      else
+       {
+         if (filter_match_zebra (filter, p))
+           return filter->type;
+       }
+    }
+
+  return FILTER_DENY;
+}
+
+/* Add hook function. */
+void
+access_list_add_hook (void (*func) (struct access_list *access))
+{
+  access_master_ipv4.add_hook = func;
+#ifdef HAVE_IPV6
+  access_master_ipv6.add_hook = func;
+#endif /* HAVE_IPV6 */
+}
+
+/* Delete hook function. */
+void
+access_list_delete_hook (void (*func) (struct access_list *access))
+{
+  access_master_ipv4.delete_hook = func;
+#ifdef HAVE_IPV6
+  access_master_ipv6.delete_hook = func;
+#endif /* HAVE_IPV6 */
+}
+
+/* Add new filter to the end of specified access_list. */
+static void
+access_list_filter_add (struct access_list *access, struct filter *filter)
+{
+  filter->next = NULL;
+  filter->prev = access->tail;
+
+  if (access->tail)
+    access->tail->next = filter;
+  else
+    access->head = filter;
+  access->tail = filter;
+
+  /* Run hook function. */
+  if (access->master->add_hook)
+    (*access->master->add_hook) (access);
+}
+
+/* If access_list has no filter then return 1. */
+static int
+access_list_empty (struct access_list *access)
+{
+  if (access->head == NULL && access->tail == NULL)
+    return 1;
+  else
+    return 0;
+}
+
+/* Delete filter from specified access_list.  If there is hook
+   function execute it. */
+static void
+access_list_filter_delete (struct access_list *access, struct filter *filter)
+{
+  struct access_master *master;
+
+  master = access->master;
+
+  if (filter->next)
+    filter->next->prev = filter->prev;
+  else
+    access->tail = filter->prev;
+
+  if (filter->prev)
+    filter->prev->next = filter->next;
+  else
+    access->head = filter->next;
+
+  filter_free (filter);
+
+  /* Run hook function. */
+  if (master->delete_hook)
+    (*master->delete_hook) (access);
+
+  /* If access_list becomes empty delete it from access_master. */
+  if (access_list_empty (access))
+    access_list_delete (access);
+}
+
+/*
+  deny    Specify packets to reject
+  permit  Specify packets to forward
+  dynamic ?
+*/
+
+/*
+  Hostname or A.B.C.D  Address to match
+  any                  Any source host
+  host                 A single host address
+*/
+
+static struct filter *
+filter_lookup_cisco (struct access_list *access, struct filter *mnew)
+{
+  struct filter *mfilter;
+  struct filter_cisco *filter;
+  struct filter_cisco *new;
+
+  new = &mnew->u.cfilter;
+
+  for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+    {
+      filter = &mfilter->u.cfilter;
+
+      if (filter->extended)
+       {
+         if (mfilter->type == mnew->type
+             && filter->addr.s_addr == new->addr.s_addr
+             && filter->addr_mask.s_addr == new->addr_mask.s_addr
+             && filter->mask.s_addr == new->mask.s_addr
+             && filter->mask_mask.s_addr == new->mask_mask.s_addr)
+           return mfilter;
+       }
+      else
+       {
+         if (mfilter->type == mnew->type
+             && filter->addr.s_addr == new->addr.s_addr
+             && filter->addr_mask.s_addr == new->addr_mask.s_addr)
+           return mfilter;
+       }
+    }
+
+  return NULL;
+}
+
+static struct filter *
+filter_lookup_zebra (struct access_list *access, struct filter *mnew)
+{
+  struct filter *mfilter;
+  struct filter_zebra *filter;
+  struct filter_zebra *new;
+
+  new = &mnew->u.zfilter;
+
+  for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+    {
+      filter = &mfilter->u.zfilter;
+
+      if (filter->exact == new->exact
+         && mfilter->type == mnew->type
+         && prefix_same (&filter->prefix, &new->prefix))
+       return mfilter;
+    }
+  return NULL;
+}
+
+static int
+vty_access_list_remark_unset (struct vty *vty, afi_t afi, const char *name)
+{
+  struct access_list *access;
+
+  access = access_list_lookup (afi, name);
+  if (! access)
+    {
+      vty_out (vty, "%% access-list %s doesn't exist%s", name,
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (access->remark)
+    {
+      XFREE (MTYPE_TMP, access->remark);
+      access->remark = NULL;
+    }
+
+  if (access->head == NULL && access->tail == NULL && access->remark == NULL)
+    access_list_delete (access);
+
+  return CMD_SUCCESS;
+}
+
+static int
+filter_set_cisco (struct vty *vty, const char *name_str, const char *type_str,
+                 const char *addr_str, const char *addr_mask_str,
+                 const char *mask_str, const char *mask_mask_str,
+                 int extended, int set)
+{
+  int ret;
+  enum filter_type type;
+  struct filter *mfilter;
+  struct filter_cisco *filter;
+  struct access_list *access;
+  struct in_addr addr;
+  struct in_addr addr_mask;
+  struct in_addr mask;
+  struct in_addr mask_mask;
+
+  /* Check of filter type. */
+  if (strncmp (type_str, "p", 1) == 0)
+    type = FILTER_PERMIT;
+  else if (strncmp (type_str, "d", 1) == 0)
+    type = FILTER_DENY;
+  else
+    {
+      vty_out (vty, "%% filter type must be permit or deny%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = inet_aton (addr_str, &addr);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%%Inconsistent address and mask%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = inet_aton (addr_mask_str, &addr_mask);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%%Inconsistent address and mask%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (extended)
+    {
+      ret = inet_aton (mask_str, &mask);
+      if (ret <= 0)
+       {
+         vty_out (vty, "%%Inconsistent address and mask%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      ret = inet_aton (mask_mask_str, &mask_mask);
+      if (ret <= 0)
+       {
+         vty_out (vty, "%%Inconsistent address and mask%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  mfilter = filter_new();
+  mfilter->type = type;
+  mfilter->cisco = 1;
+  filter = &mfilter->u.cfilter;
+  filter->extended = extended;
+  filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr;
+  filter->addr_mask.s_addr = addr_mask.s_addr;
+
+  if (extended)
+    {
+      filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr;
+      filter->mask_mask.s_addr = mask_mask.s_addr;
+    }
+
+  /* Install new filter to the access_list. */
+  access = access_list_get (AFI_IP, name_str);
+
+  if (set)
+    {
+      if (filter_lookup_cisco (access, mfilter))
+       filter_free (mfilter);
+      else
+       access_list_filter_add (access, mfilter);
+    }
+  else
+    {
+      struct filter *delete_filter;
+
+      delete_filter = filter_lookup_cisco (access, mfilter);
+      if (delete_filter)
+       access_list_filter_delete (access, delete_filter);
+
+      filter_free (mfilter);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Standard access-list */
+DEFUN (access_list_standard,
+       access_list_standard_cmd,
+       "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D",
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Address to match\n"
+       "Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3],
+                          NULL, NULL, 0, 1);
+}
+
+DEFUN (access_list_standard_nomask,
+       access_list_standard_nomask_cmd,
+       "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D",
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Address to match\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0",
+                          NULL, NULL, 0, 1);
+}
+
+DEFUN (access_list_standard_host,
+       access_list_standard_host_cmd,
+       "access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D",
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "A single host address\n"
+       "Address to match\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0",
+                          NULL, NULL, 0, 1);
+}
+
+DEFUN (access_list_standard_any,
+       access_list_standard_any_cmd,
+       "access-list (<1-99>|<1300-1999>) (deny|permit) any",
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any source host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", NULL, NULL, 0, 1);
+}
+
+DEFUN (no_access_list_standard,
+       no_access_list_standard_cmd,
+       "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Address to match\n"
+       "Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3],
+                          NULL, NULL, 0, 0);
+}
+
+DEFUN (no_access_list_standard_nomask,
+       no_access_list_standard_nomask_cmd,
+       "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Address to match\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0",
+                          NULL, NULL, 0, 0);
+}
+
+DEFUN (no_access_list_standard_host,
+       no_access_list_standard_host_cmd,
+       "no access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "A single host address\n"
+       "Address to match\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0",
+                          NULL, NULL, 0, 0);
+}
+
+DEFUN (no_access_list_standard_any,
+       no_access_list_standard_any_cmd,
+       "no access-list (<1-99>|<1300-1999>) (deny|permit) any",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP standard access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any source host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", NULL, NULL, 0, 0);
+}
+
+/* Extended access-list */
+DEFUN (access_list_extended,
+       access_list_extended_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], argv[4], argv[5], 1 ,1);
+}
+
+DEFUN (access_list_extended_mask_any,
+       access_list_extended_mask_any_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], "0.0.0.0",
+                          "255.255.255.255", 1, 1);
+}
+
+DEFUN (access_list_extended_any_mask,
+       access_list_extended_any_mask_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", argv[2],
+                          argv[3], 1, 1);
+}
+
+DEFUN (access_list_extended_any_any,
+       access_list_extended_any_any_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip any any",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", "0.0.0.0",
+                          "255.255.255.255", 1, 1);
+}
+
+DEFUN (access_list_extended_mask_host,
+       access_list_extended_mask_host_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], argv[4],
+                          "0.0.0.0", 1, 1);
+}
+
+DEFUN (access_list_extended_host_mask,
+       access_list_extended_host_mask_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", argv[3],
+                          argv[4], 1, 1);
+}
+
+DEFUN (access_list_extended_host_host,
+       access_list_extended_host_host_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", argv[3],
+                          "0.0.0.0", 1, 1);
+}
+
+DEFUN (access_list_extended_any_host,
+       access_list_extended_any_host_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", argv[2],
+                          "0.0.0.0", 1, 1);
+}
+
+DEFUN (access_list_extended_host_any,
+       access_list_extended_host_any_cmd,
+       "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any",
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", "0.0.0.0",
+                          "255.255.255.255", 1, 1);
+}
+
+DEFUN (no_access_list_extended,
+       no_access_list_extended_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], argv[4], argv[5], 1, 0);
+}
+
+DEFUN (no_access_list_extended_mask_any,
+       no_access_list_extended_mask_any_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], "0.0.0.0",
+                          "255.255.255.255", 1, 0);
+}
+
+DEFUN (no_access_list_extended_any_mask,
+       no_access_list_extended_any_mask_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", argv[2],
+                          argv[3], 1, 0);
+}
+
+DEFUN (no_access_list_extended_any_any,
+       no_access_list_extended_any_any_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any any",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", "0.0.0.0",
+                          "255.255.255.255", 1, 0);
+}
+
+DEFUN (no_access_list_extended_mask_host,
+       no_access_list_extended_mask_host_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Source address\n"
+       "Source wildcard bits\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          argv[3], argv[4],
+                          "0.0.0.0", 1, 0);
+}
+
+DEFUN (no_access_list_extended_host_mask,
+       no_access_list_extended_host_mask_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "Destination address\n"
+       "Destination Wildcard bits\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", argv[3],
+                          argv[4], 1, 0);
+}
+
+DEFUN (no_access_list_extended_host_host,
+       no_access_list_extended_host_host_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", argv[3],
+                          "0.0.0.0", 1, 0);
+}
+
+DEFUN (no_access_list_extended_any_host,
+       no_access_list_extended_any_host_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "Any source host\n"
+       "A single destination host\n"
+       "Destination address\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0",
+                          "255.255.255.255", argv[2],
+                          "0.0.0.0", 1, 0);
+}
+
+DEFUN (no_access_list_extended_host_any,
+       no_access_list_extended_host_any_cmd,
+       "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any",
+       NO_STR
+       "Add an access list entry\n"
+       "IP extended access list\n"
+       "IP extended access list (expanded range)\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any Internet Protocol\n"
+       "A single source host\n"
+       "Source address\n"
+       "Any destination host\n")
+{
+  return filter_set_cisco (vty, argv[0], argv[1], argv[2],
+                          "0.0.0.0", "0.0.0.0",
+                          "255.255.255.255", 1, 0);
+}
+
+static int
+filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str,
+                 afi_t afi, const char *prefix_str, int exact, int set)
+{
+  int ret;
+  enum filter_type type;
+  struct filter *mfilter;
+  struct filter_zebra *filter;
+  struct access_list *access;
+  struct prefix p;
+
+  /* Check of filter type. */
+  if (strncmp (type_str, "p", 1) == 0)
+    type = FILTER_PERMIT;
+  else if (strncmp (type_str, "d", 1) == 0)
+    type = FILTER_DENY;
+  else
+    {
+      vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check string format of prefix and prefixlen. */
+  if (afi == AFI_IP)
+    {
+      ret = str2prefix_ipv4 (prefix_str, (struct prefix_ipv4 *)&p);
+      if (ret <= 0)
+       {
+         vty_out (vty, "IP address prefix/prefixlen is malformed%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6)
+    {
+      ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *) &p);
+      if (ret <= 0)
+       {
+         vty_out (vty, "IPv6 address prefix/prefixlen is malformed%s",
+                  VTY_NEWLINE);
+                  return CMD_WARNING;
+       }
+    }
+#endif /* HAVE_IPV6 */
+  else
+    return CMD_WARNING;
+
+  mfilter = filter_new ();
+  mfilter->type = type;
+  filter = &mfilter->u.zfilter;
+  prefix_copy (&filter->prefix, &p);
+
+  /* "exact-match" */
+  if (exact)
+    filter->exact = 1;
+
+  /* Install new filter to the access_list. */
+  access = access_list_get (afi, name_str);
+
+  if (set)
+    {
+      if (filter_lookup_zebra (access, mfilter))
+       filter_free (mfilter);
+      else
+       access_list_filter_add (access, mfilter);
+    }
+  else
+    {
+      struct filter *delete_filter;
+
+      delete_filter = filter_lookup_zebra (access, mfilter);
+      if (delete_filter)
+        access_list_filter_delete (access, delete_filter);
+
+      filter_free (mfilter);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Zebra access-list */
+DEFUN (access_list,
+       access_list_cmd,
+       "access-list WORD (deny|permit) A.B.C.D/M",
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 1);
+}
+
+DEFUN (access_list_exact,
+       access_list_exact_cmd,
+       "access-list WORD (deny|permit) A.B.C.D/M exact-match",
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n"
+       "Exact match of the prefixes\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 1);
+}
+
+DEFUN (access_list_any,
+       access_list_any_cmd,
+       "access-list WORD (deny|permit) any",
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 1);
+}
+
+DEFUN (no_access_list,
+       no_access_list_cmd,
+       "no access-list WORD (deny|permit) A.B.C.D/M",
+       NO_STR
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 0);
+}
+
+DEFUN (no_access_list_exact,
+       no_access_list_exact_cmd,
+       "no access-list WORD (deny|permit) A.B.C.D/M exact-match",
+       NO_STR
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n"
+       "Exact match of the prefixes\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 0);
+}
+
+DEFUN (no_access_list_any,
+       no_access_list_any_cmd,
+       "no access-list WORD (deny|permit) any",
+       NO_STR
+       "Add an access list entry\n"
+       "IP zebra access-list name\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 10.0.0.0/8\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 0);
+}
+
+DEFUN (no_access_list_all,
+       no_access_list_all_cmd,
+       "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP extended access list\n"
+       "IP standard access list (expanded range)\n"
+       "IP extended access list (expanded range)\n"
+       "IP zebra access-list name\n")
+{
+  struct access_list *access;
+  struct access_master *master;
+
+  /* Looking up access_list. */
+  access = access_list_lookup (AFI_IP, argv[0]);
+  if (access == NULL)
+    {
+      vty_out (vty, "%% access-list %s doesn't exist%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  master = access->master;
+
+  /* Run hook function. */
+  if (master->delete_hook)
+    (*master->delete_hook) (access);
+  /* Delete all filter from access-list. */
+  access_list_delete (access);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (access_list_remark,
+       access_list_remark_cmd,
+       "access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE",
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP extended access list\n"
+       "IP standard access list (expanded range)\n"
+       "IP extended access list (expanded range)\n"
+       "IP zebra access-list\n"
+       "Access list entry comment\n"
+       "Comment up to 100 characters\n")
+{
+  struct access_list *access;
+
+  access = access_list_get (AFI_IP, argv[0]);
+
+  if (access->remark)
+    {
+      XFREE (MTYPE_TMP, access->remark);
+      access->remark = NULL;
+    }
+  access->remark = argv_concat(argv, argc, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_access_list_remark,
+       no_access_list_remark_cmd,
+       "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP extended access list\n"
+       "IP standard access list (expanded range)\n"
+       "IP extended access list (expanded range)\n"
+       "IP zebra access-list\n"
+       "Access list entry comment\n")
+{
+  return vty_access_list_remark_unset (vty, AFI_IP, argv[0]);
+}
+       
+ALIAS (no_access_list_remark,
+       no_access_list_remark_arg_cmd,
+       "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE",
+       NO_STR
+       "Add an access list entry\n"
+       "IP standard access list\n"
+       "IP extended access list\n"
+       "IP standard access list (expanded range)\n"
+       "IP extended access list (expanded range)\n"
+       "IP zebra access-list\n"
+       "Access list entry comment\n"
+       "Comment up to 100 characters\n")
+
+#ifdef HAVE_IPV6
+DEFUN (ipv6_access_list,
+       ipv6_access_list_cmd,
+       "ipv6 access-list WORD (deny|permit) X:X::X:X/M",
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 3ffe:506::/32\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 1);
+}
+
+DEFUN (ipv6_access_list_exact,
+       ipv6_access_list_exact_cmd,
+       "ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match",
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 3ffe:506::/32\n"
+       "Exact match of the prefixes\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 1);
+}
+
+DEFUN (ipv6_access_list_any,
+       ipv6_access_list_any_cmd,
+       "ipv6 access-list WORD (deny|permit) any",
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any prefixi to match\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 1);
+}
+
+DEFUN (no_ipv6_access_list,
+       no_ipv6_access_list_cmd,
+       "no ipv6 access-list WORD (deny|permit) X:X::X:X/M",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 3ffe:506::/32\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 0);
+}
+
+DEFUN (no_ipv6_access_list_exact,
+       no_ipv6_access_list_exact_cmd,
+       "no ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Prefix to match. e.g. 3ffe:506::/32\n"
+       "Exact match of the prefixes\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 0);
+}
+
+DEFUN (no_ipv6_access_list_any,
+       no_ipv6_access_list_any_cmd,
+       "no ipv6 access-list WORD (deny|permit) any",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "Any prefixi to match\n")
+{
+  return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 0);
+}
+
+
+DEFUN (no_ipv6_access_list_all,
+       no_ipv6_access_list_all_cmd,
+       "no ipv6 access-list WORD",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n")
+{
+  struct access_list *access;
+  struct access_master *master;
+
+  /* Looking up access_list. */
+  access = access_list_lookup (AFI_IP6, argv[0]);
+  if (access == NULL)
+    {
+      vty_out (vty, "%% access-list %s doesn't exist%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  master = access->master;
+
+  /* Run hook function. */
+  if (master->delete_hook)
+    (*master->delete_hook) (access);
+
+  /* Delete all filter from access-list. */
+  access_list_delete (access);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_access_list_remark,
+       ipv6_access_list_remark_cmd,
+       "ipv6 access-list WORD remark .LINE",
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Access list entry comment\n"
+       "Comment up to 100 characters\n")
+{
+  struct access_list *access;
+
+  access = access_list_get (AFI_IP6, argv[0]);
+
+  if (access->remark)
+    {
+      XFREE (MTYPE_TMP, access->remark);
+      access->remark = NULL;
+    }
+  access->remark = argv_concat(argv, argc, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_access_list_remark,
+       no_ipv6_access_list_remark_cmd,
+       "no ipv6 access-list WORD remark",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Access list entry comment\n")
+{
+  return vty_access_list_remark_unset (vty, AFI_IP6, argv[0]);
+}
+       
+ALIAS (no_ipv6_access_list_remark,
+       no_ipv6_access_list_remark_arg_cmd,
+       "no ipv6 access-list WORD remark .LINE",
+       NO_STR
+       IPV6_STR
+       "Add an access list entry\n"
+       "IPv6 zebra access-list\n"
+       "Access list entry comment\n"
+       "Comment up to 100 characters\n")
+#endif /* HAVE_IPV6 */
+
+void config_write_access_zebra (struct vty *, struct filter *);
+void config_write_access_cisco (struct vty *, struct filter *);
+
+/* show access-list command. */
+static int
+filter_show (struct vty *vty, const char *name, afi_t afi)
+{
+  struct access_list *access;
+  struct access_master *master;
+  struct filter *mfilter;
+  struct filter_cisco *filter;
+  int write = 0;
+
+  master = access_master_get (afi);
+  if (master == NULL)
+    return 0;
+
+  /* Print the name of the protocol */
+  if (zlog_default)
+      vty_out (vty, "%s:%s",
+      zlog_proto_names[zlog_default->protocol], VTY_NEWLINE);
+
+  for (access = master->num.head; access; access = access->next)
+    {
+      if (name && strcmp (access->name, name) != 0)
+       continue;
+
+      write = 1;
+
+      for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+       {
+         filter = &mfilter->u.cfilter;
+
+         if (write)
+           {
+             vty_out (vty, "%s IP%s access list %s%s",
+                      mfilter->cisco ? 
+                      (filter->extended ? "Extended" : "Standard") : "Zebra",
+                      afi == AFI_IP6 ? "v6" : "",
+                      access->name, VTY_NEWLINE);
+             write = 0;
+           }
+
+         vty_out (vty, "    %s%s", filter_type_str (mfilter),
+                  mfilter->type == FILTER_DENY ? "  " : "");
+
+         if (! mfilter->cisco)
+           config_write_access_zebra (vty, mfilter);
+         else if (filter->extended)
+           config_write_access_cisco (vty, mfilter);
+         else
+           {
+             if (filter->addr_mask.s_addr == 0xffffffff)
+               vty_out (vty, " any%s", VTY_NEWLINE);
+             else
+               {
+                 vty_out (vty, " %s", inet_ntoa (filter->addr));
+                 if (filter->addr_mask.s_addr != 0)
+                   vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask));
+                 vty_out (vty, "%s", VTY_NEWLINE);
+               }
+           }
+       }
+    }
+
+  for (access = master->str.head; access; access = access->next)
+    {
+      if (name && strcmp (access->name, name) != 0)
+       continue;
+
+      write = 1;
+
+      for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+       {
+         filter = &mfilter->u.cfilter;
+
+         if (write)
+           {
+             vty_out (vty, "%s IP%s access list %s%s",
+                      mfilter->cisco ? 
+                      (filter->extended ? "Extended" : "Standard") : "Zebra",
+                      afi == AFI_IP6 ? "v6" : "",
+                      access->name, VTY_NEWLINE);
+             write = 0;
+           }
+
+         vty_out (vty, "    %s%s", filter_type_str (mfilter),
+                  mfilter->type == FILTER_DENY ? "  " : "");
+
+         if (! mfilter->cisco)
+           config_write_access_zebra (vty, mfilter);
+         else if (filter->extended)
+           config_write_access_cisco (vty, mfilter);
+         else
+           {
+             if (filter->addr_mask.s_addr == 0xffffffff)
+               vty_out (vty, " any%s", VTY_NEWLINE);
+             else
+               {
+                 vty_out (vty, " %s", inet_ntoa (filter->addr));
+                 if (filter->addr_mask.s_addr != 0)
+                   vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask));
+                 vty_out (vty, "%s", VTY_NEWLINE);
+               }
+           }
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_access_list,
+       show_ip_access_list_cmd,
+       "show ip access-list",
+       SHOW_STR
+       IP_STR
+       "List IP access lists\n")
+{
+  return filter_show (vty, NULL, AFI_IP);
+}
+
+DEFUN (show_ip_access_list_name,
+       show_ip_access_list_name_cmd,
+       "show ip access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)",
+       SHOW_STR
+       IP_STR
+       "List IP access lists\n"
+       "IP standard access list\n"
+       "IP extended access list\n"
+       "IP standard access list (expanded range)\n"
+       "IP extended access list (expanded range)\n"
+       "IP zebra access-list\n")
+{
+  return filter_show (vty, argv[0], AFI_IP);
+}
+
+#ifdef HAVE_IPV6
+DEFUN (show_ipv6_access_list,
+       show_ipv6_access_list_cmd,
+       "show ipv6 access-list",
+       SHOW_STR
+       IPV6_STR
+       "List IPv6 access lists\n")
+{
+  return filter_show (vty, NULL, AFI_IP6);
+}
+
+DEFUN (show_ipv6_access_list_name,
+       show_ipv6_access_list_name_cmd,
+       "show ipv6 access-list WORD",
+       SHOW_STR
+       IPV6_STR
+       "List IPv6 access lists\n"
+       "IPv6 zebra access-list\n")
+{
+  return filter_show (vty, argv[0], AFI_IP6);
+}
+#endif /* HAVE_IPV6 */
+
+void
+config_write_access_cisco (struct vty *vty, struct filter *mfilter)
+{
+  struct filter_cisco *filter;
+
+  filter = &mfilter->u.cfilter;
+
+  if (filter->extended)
+    {
+      vty_out (vty, " ip");
+      if (filter->addr_mask.s_addr == 0xffffffff)
+       vty_out (vty, " any");
+      else if (filter->addr_mask.s_addr == 0)
+       vty_out (vty, " host %s", inet_ntoa (filter->addr));
+      else
+       {
+         vty_out (vty, " %s", inet_ntoa (filter->addr));
+         vty_out (vty, " %s", inet_ntoa (filter->addr_mask));
+        }
+
+      if (filter->mask_mask.s_addr == 0xffffffff)
+       vty_out (vty, " any");
+      else if (filter->mask_mask.s_addr == 0)
+       vty_out (vty, " host %s", inet_ntoa (filter->mask));
+      else
+       {
+         vty_out (vty, " %s", inet_ntoa (filter->mask));
+         vty_out (vty, " %s", inet_ntoa (filter->mask_mask));
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  else
+    {
+      if (filter->addr_mask.s_addr == 0xffffffff)
+       vty_out (vty, " any%s", VTY_NEWLINE);
+      else
+       {
+         vty_out (vty, " %s", inet_ntoa (filter->addr));
+         if (filter->addr_mask.s_addr != 0)
+           vty_out (vty, " %s", inet_ntoa (filter->addr_mask));
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+}
+
+void
+config_write_access_zebra (struct vty *vty, struct filter *mfilter)
+{
+  struct filter_zebra *filter;
+  struct prefix *p;
+  char buf[BUFSIZ];
+
+  filter = &mfilter->u.zfilter;
+  p = &filter->prefix;
+
+  if (p->prefixlen == 0 && ! filter->exact)
+    vty_out (vty, " any");
+  else
+    vty_out (vty, " %s/%d%s",
+            inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+            p->prefixlen,
+            filter->exact ? " exact-match" : "");
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static int
+config_write_access (struct vty *vty, afi_t afi)
+{
+  struct access_list *access;
+  struct access_master *master;
+  struct filter *mfilter;
+  int write = 0;
+
+  master = access_master_get (afi);
+  if (master == NULL)
+    return 0;
+
+  for (access = master->num.head; access; access = access->next)
+    {
+      if (access->remark)
+       {
+         vty_out (vty, "%saccess-list %s remark %s%s",
+                  afi == AFI_IP ? "" : "ipv6 ",
+                  access->name, access->remark,
+                  VTY_NEWLINE);
+         write++;
+       }
+
+      for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+       {
+         vty_out (vty, "%saccess-list %s %s",
+            afi == AFI_IP ? "" : "ipv6 ",
+            access->name,
+            filter_type_str (mfilter));
+
+         if (mfilter->cisco)
+           config_write_access_cisco (vty, mfilter);
+         else
+           config_write_access_zebra (vty, mfilter);
+
+         write++;
+       }
+    }
+
+  for (access = master->str.head; access; access = access->next)
+    {
+      if (access->remark)
+       {
+         vty_out (vty, "%saccess-list %s remark %s%s",
+                  afi == AFI_IP ? "" : "ipv6 ",
+                  access->name, access->remark,
+                  VTY_NEWLINE);
+         write++;
+       }
+
+      for (mfilter = access->head; mfilter; mfilter = mfilter->next)
+       {
+         vty_out (vty, "%saccess-list %s %s",
+            afi == AFI_IP ? "" : "ipv6 ",
+            access->name,
+            filter_type_str (mfilter));
+
+         if (mfilter->cisco)
+           config_write_access_cisco (vty, mfilter);
+         else
+           config_write_access_zebra (vty, mfilter);
+
+         write++;
+       }
+    }
+  return write;
+}
+
+/* Access-list node. */
+static struct cmd_node access_node =
+{
+  ACCESS_NODE,
+  "",                          /* Access list has no interface. */
+  1
+};
+
+static int
+config_write_access_ipv4 (struct vty *vty)
+{
+  return config_write_access (vty, AFI_IP);
+}
+
+static void
+access_list_reset_ipv4 (void)
+{
+  struct access_list *access;
+  struct access_list *next;
+  struct access_master *master;
+
+  master = access_master_get (AFI_IP);
+  if (master == NULL)
+    return;
+
+  for (access = master->num.head; access; access = next)
+    {
+      next = access->next;
+      access_list_delete (access);
+    }
+  for (access = master->str.head; access; access = next)
+    {
+      next = access->next;
+      access_list_delete (access);
+    }
+
+  assert (master->num.head == NULL);
+  assert (master->num.tail == NULL);
+
+  assert (master->str.head == NULL);
+  assert (master->str.tail == NULL);
+}
+
+/* Install vty related command. */
+static void
+access_list_init_ipv4 (void)
+{
+  install_node (&access_node, config_write_access_ipv4);
+
+  install_element (ENABLE_NODE, &show_ip_access_list_cmd);
+  install_element (ENABLE_NODE, &show_ip_access_list_name_cmd);
+
+  /* Zebra access-list */
+  install_element (CONFIG_NODE, &access_list_cmd);
+  install_element (CONFIG_NODE, &access_list_exact_cmd);
+  install_element (CONFIG_NODE, &access_list_any_cmd);
+  install_element (CONFIG_NODE, &no_access_list_cmd);
+  install_element (CONFIG_NODE, &no_access_list_exact_cmd);
+  install_element (CONFIG_NODE, &no_access_list_any_cmd);
+
+  /* Standard access-list */
+  install_element (CONFIG_NODE, &access_list_standard_cmd);
+  install_element (CONFIG_NODE, &access_list_standard_nomask_cmd);
+  install_element (CONFIG_NODE, &access_list_standard_host_cmd);
+  install_element (CONFIG_NODE, &access_list_standard_any_cmd);
+  install_element (CONFIG_NODE, &no_access_list_standard_cmd);
+  install_element (CONFIG_NODE, &no_access_list_standard_nomask_cmd);
+  install_element (CONFIG_NODE, &no_access_list_standard_host_cmd);
+  install_element (CONFIG_NODE, &no_access_list_standard_any_cmd);
+
+  /* Extended access-list */
+  install_element (CONFIG_NODE, &access_list_extended_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_any_mask_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_mask_any_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_any_any_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_host_mask_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_mask_host_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_host_host_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_any_host_cmd);
+  install_element (CONFIG_NODE, &access_list_extended_host_any_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_any_mask_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_mask_any_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_any_any_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_host_mask_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_mask_host_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_host_host_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_any_host_cmd);
+  install_element (CONFIG_NODE, &no_access_list_extended_host_any_cmd);
+
+  install_element (CONFIG_NODE, &access_list_remark_cmd);
+  install_element (CONFIG_NODE, &no_access_list_all_cmd);
+  install_element (CONFIG_NODE, &no_access_list_remark_cmd);
+  install_element (CONFIG_NODE, &no_access_list_remark_arg_cmd);
+}
+
+#ifdef HAVE_IPV6
+static struct cmd_node access_ipv6_node =
+{
+  ACCESS_IPV6_NODE,
+  "",
+  1
+};
+
+static int
+config_write_access_ipv6 (struct vty *vty)
+{
+  return config_write_access (vty, AFI_IP6);
+}
+
+static void
+access_list_reset_ipv6 (void)
+{
+  struct access_list *access;
+  struct access_list *next;
+  struct access_master *master;
+
+  master = access_master_get (AFI_IP6);
+  if (master == NULL)
+    return;
+
+  for (access = master->num.head; access; access = next)
+    {
+      next = access->next;
+      access_list_delete (access);
+    }
+  for (access = master->str.head; access; access = next)
+    {
+      next = access->next;
+      access_list_delete (access);
+    }
+
+  assert (master->num.head == NULL);
+  assert (master->num.tail == NULL);
+
+  assert (master->str.head == NULL);
+  assert (master->str.tail == NULL);
+}
+
+static void
+access_list_init_ipv6 (void)
+{
+  install_node (&access_ipv6_node, config_write_access_ipv6);
+
+  install_element (ENABLE_NODE, &show_ipv6_access_list_cmd);
+  install_element (ENABLE_NODE, &show_ipv6_access_list_name_cmd);
+
+  install_element (CONFIG_NODE, &ipv6_access_list_cmd);
+  install_element (CONFIG_NODE, &ipv6_access_list_exact_cmd);
+  install_element (CONFIG_NODE, &ipv6_access_list_any_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_access_list_exact_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_access_list_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_access_list_any_cmd);
+
+  install_element (CONFIG_NODE, &no_ipv6_access_list_all_cmd);
+  install_element (CONFIG_NODE, &ipv6_access_list_remark_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_access_list_remark_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_access_list_remark_arg_cmd);
+}
+#endif /* HAVE_IPV6 */
+
+void
+access_list_init ()
+{
+  access_list_init_ipv4 ();
+#ifdef HAVE_IPV6
+  access_list_init_ipv6();
+#endif /* HAVE_IPV6 */
+}
+
+void
+access_list_reset ()
+{
+  access_list_reset_ipv4 ();
+#ifdef HAVE_IPV6
+  access_list_reset_ipv6();
+#endif /* HAVE_IPV6 */
+}
diff --git a/lib/filter.h b/lib/filter.h
new file mode 100644 (file)
index 0000000..e6ccd33
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Route filtering function.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#ifndef _ZEBRA_FILTER_H
+#define _ZEBRA_FILTER_H
+
+#include "if.h"
+
+/* Filter direction.  */
+#define FILTER_IN                 0
+#define FILTER_OUT                1
+#define FILTER_MAX                2
+
+/* Filter type is made by `permit', `deny' and `dynamic'. */
+enum filter_type 
+{
+  FILTER_DENY,
+  FILTER_PERMIT,
+  FILTER_DYNAMIC
+};
+
+enum access_type
+{
+  ACCESS_TYPE_STRING,
+  ACCESS_TYPE_NUMBER
+};
+
+/* Access list */
+struct access_list
+{
+  char *name;
+  char *remark;
+
+  struct access_master *master;
+
+  enum access_type type;
+
+  struct access_list *next;
+  struct access_list *prev;
+
+  struct filter *head;
+  struct filter *tail;
+};
+
+/* Prototypes for access-list. */
+extern void access_list_init (void);
+extern void access_list_reset (void);
+extern void access_list_add_hook (void (*func)(struct access_list *));
+extern void access_list_delete_hook (void (*func)(struct access_list *));
+extern struct access_list *access_list_lookup (afi_t, const char *);
+extern enum filter_type access_list_apply (struct access_list *, void *);
+
+#endif /* _ZEBRA_FILTER_H */
diff --git a/lib/getopt.c b/lib/getopt.c
new file mode 100644 (file)
index 0000000..7a58a8a
--- /dev/null
@@ -0,0 +1,1056 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to drepper@gnu.org
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
+       Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@gnu.org.
+
+   This program 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.
+
+   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.  */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#include <zebra.h>
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+# ifndef const
+#  define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library.  */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+#  include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+# ifdef HAVE_LIBINTL_H
+#  include <libintl.h>
+#  define _(msgid)     gettext (msgid)
+# else
+#  define _(msgid)     (msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+# include <string.h>
+# define my_index      strchr
+#else
+
+# if HAVE_STRING_H
+#  include <string.h>
+# else
+#  include <strings.h>
+# endif
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+# ifdef text_set_element
+text_set_element (__libc_subinit, store_args_and_env);
+# endif /* text_set_element */
+
+# define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)                                               \
+    {                                                                        \
+      char __tmp = __getopt_nonoption_flags[ch1];                            \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
+      __getopt_nonoption_flags[ch2] = __tmp;                                 \
+    }
+#else  /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#ifdef _LIBC
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+        presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+       nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+       {
+         memset (__mempcpy (new_str, __getopt_nonoption_flags,
+                            nonoption_flags_max_len),
+                 '\0', top + 1 - nonoption_flags_max_len);
+         nonoption_flags_max_len = top + 1;
+         __getopt_nonoption_flags = new_str;
+       }
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+             SWAP_FLAGS (bottom + i, middle + i);
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+       {
+         if (__getopt_nonoption_flags == NULL
+             || __getopt_nonoption_flags[0] == '\0')
+           nonoption_flags_max_len = -1;
+         else
+           {
+             const char *orig_str = __getopt_nonoption_flags;
+             int len = nonoption_flags_max_len = strlen (orig_str);
+             if (nonoption_flags_max_len < argc)
+               nonoption_flags_max_len = argc;
+             __getopt_nonoption_flags =
+               (char *) malloc (nonoption_flags_max_len);
+             if (__getopt_nonoption_flags == NULL)
+               nonoption_flags_max_len = -1;
+             else
+               memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+                       '\0', nonoption_flags_max_len - len);
+           }
+       }
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+       optind = 1;     /* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'              \
+                     || (optind < nonoption_flags_len                        \
+                         && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+        moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+       last_nonopt = optind;
+      if (first_nonopt > optind)
+       first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc && NONOPTION_P)
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* The special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return -1;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+       {
+         if (ordering == REQUIRE_ORDER)
+           return -1;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+       /* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+        or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, nextchar, nameend - nextchar))
+         {
+           if ((unsigned int) (nameend - nextchar)
+               == (unsigned int) strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second or later nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*nameend)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = nameend + 1;
+             else
+               {
+                 if (opterr)
+                    {
+                     if (argv[optind - 1][1] == '-')
+                       /* --option */
+                       fprintf (stderr,
+                        _("%s: option `--%s' doesn't allow an argument\n"),
+                        argv[0], pfound->name);
+                     else
+                       /* +option or -option */
+                       fprintf (stderr,
+                        _("%s: option `%c%s' doesn't allow an argument\n"),
+                        argv[0], argv[optind - 1][0], pfound->name);
+                    }
+
+                 nextchar += strlen (nextchar);
+
+                 optopt = pfound->val;
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr,
+                          _("%s: option `%s' requires an argument\n"),
+                          argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 optopt = pfound->val;
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+           if (posixly_correct)
+             /* 1003.2 specifies the format of this message.  */
+             fprintf (stderr, _("%s: illegal option -- %c\n"),
+                      argv[0], c);
+           else
+             fprintf (stderr, _("%s: invalid option -- %c\n"),
+                      argv[0], c);
+         }
+       optopt = c;
+       return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+       char *nameend;
+       const struct option *p;
+       const struct option *pfound = NULL;
+       int exact = 0;
+       int ambig = 0;
+       int indfound = 0;
+       int option_index;
+
+       /* This is an option that requires an argument.  */
+       if (*nextchar != '\0')
+         {
+           optarg = nextchar;
+           /* If we end this ARGV-element by taking the rest as an arg,
+              we must advance to the next element now.  */
+           optind++;
+         }
+       else if (optind == argc)
+         {
+           if (opterr)
+             {
+               /* 1003.2 specifies the format of this message.  */
+               fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+                        argv[0], c);
+             }
+           optopt = c;
+           if (optstring[0] == ':')
+             c = ':';
+           else
+             c = '?';
+           return c;
+         }
+       else
+         /* We already incremented `optind' once;
+            increment it again when taking next ARGV-elt as argument.  */
+         optarg = argv[optind++];
+
+       /* optarg is now the argument, see if it's in the
+          table of longopts.  */
+
+       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+         /* Do nothing.  */ ;
+
+       /* Test all long options for either exact match
+          or abbreviated matches.  */
+       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+         if (!strncmp (p->name, nextchar, nameend - nextchar))
+           {
+             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+               {
+                 /* Exact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+                 exact = 1;
+                 break;
+               }
+             else if (pfound == NULL)
+               {
+                 /* First nonexact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+               }
+             else
+               /* Second or later nonexact match found.  */
+               ambig = 1;
+           }
+       if (ambig && !exact)
+         {
+           if (opterr)
+             fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+                      argv[0], argv[optind]);
+           nextchar += strlen (nextchar);
+           optind++;
+           return '?';
+         }
+       if (pfound != NULL)
+         {
+           option_index = indfound;
+           if (*nameend)
+             {
+               /* Don't test has_arg with >, because some C compilers don't
+                  allow it to be used on enums.  */
+               if (pfound->has_arg)
+                 optarg = nameend + 1;
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+                              argv[0], pfound->name);
+
+                   nextchar += strlen (nextchar);
+                   return '?';
+                 }
+             }
+           else if (pfound->has_arg == 1)
+             {
+               if (optind < argc)
+                 optarg = argv[optind++];
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr,
+                              _("%s: option `%s' requires an argument\n"),
+                              argv[0], argv[optind - 1]);
+                   nextchar += strlen (nextchar);
+                   return optstring[0] == ':' ? ':' : '?';
+                 }
+             }
+           nextchar += strlen (nextchar);
+           if (longind != NULL)
+             *longind = option_index;
+           if (pfound->flag)
+             {
+               *(pfound->flag) = pfound->val;
+               return 0;
+             }
+           return pfound->val;
+         }
+         nextchar = NULL;
+         return 'W';   /* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = NULL;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr,
+                          _("%s: option requires an argument -- %c\n"),
+                          argv[0], c);
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+#ifdef REALLY_NEED_PLAIN_GETOPT
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* REALLY_NEED_PLAIN_GETOPT */
+
+#endif /* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/getopt.h b/lib/getopt.h
new file mode 100644 (file)
index 0000000..b359a47
--- /dev/null
@@ -0,0 +1,159 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@gnu.org.
+
+   This program 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.
+
+   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 _GETOPT_H
+#define _GETOPT_H 1
+
+/*
+ * The operating system may or may not provide getopt_long(), and if
+ * so it may or may not be a version we are willing to use.  Our
+ * strategy is to declare getopt here, and then provide code unless
+ * the supplied version is adequate.  The difficult case is when a
+ * declaration for getopt is provided, as our declaration must match.
+ *
+ * XXX Arguably this version should be named differently, and the
+ * local names defined to refer to the system version when we choose
+ * to use the system version.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if defined (__STDC__) && __STDC__
+
+#if REALLY_NEED_PLAIN_GETOPT
+
+/*
+ * getopt is defined in POSIX.2.  Assume that if the system defines
+ * getopt that it complies with POSIX.2.  If not, an autoconf test
+ * should be written to define NONPOSIX_GETOPT_DEFINITION.
+ */
+#ifndef NONPOSIX_GETOPT_DEFINITION
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* NONPOSIX_GETOPT_DEFINITION */
+extern int getopt (void);
+#endif /* NONPOSIX_GETOPT_DEFINITION */
+
+#endif
+
+
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+
+#ifdef REALLY_NEED_PLAIN_GETOPT
+extern int getopt ();
+#endif /* REALLY_NEED_PLAIN_GETOPT */
+
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* getopt.h */
diff --git a/lib/getopt1.c b/lib/getopt1.c
new file mode 100644 (file)
index 0000000..bd3099e
--- /dev/null
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+     Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@gnu.org.
+
+   This program 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.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+#include <zebra.h>
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/gitversion.pl b/lib/gitversion.pl
new file mode 100644 (file)
index 0000000..8ddd9ff
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/perl -w
+use strict;
+
+my $dir = shift;
+chdir $dir || die "$dir: $!\n";
+
+my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`;
+chomp $gitdesc;
+my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
+
+printf STDERR "git suffix: %s\n", $gitsuffix;
+printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix;
+
+my $gitcommit = `git log -1 --format=\"%H\" || echo DEADBEEF`;
+chomp $gitcommit;
+open(BRANCHES, "git branch -a -v --abbrev=40|") || die "git branch: $!\n";
+my @names = ();
+while (<BRANCHES>) {
+       chomp $_;
+       if (/\s+(.*?)\s+$gitcommit/) {
+               my $branch = $1;
+               if ($branch =~ /^remotes\/(.*?)(\/.*)$/) {
+                       my $path = $2;
+                       my $url = `git config --get "remote.$1.url"`;
+                       chomp $url;
+                       $url =~ s/^(git:|https?:|git@)\/\/github\.com/github/i;
+                       $url =~ s/^(ssh|git):\/\/git\.sv\.gnu\.org\/srv\/git\//savannah:/i;
+                       $url =~ s/^(ssh|git):\/\/git\.savannah\.nongnu\.org\//savannah:/i;
+
+                       push @names, $url.$path;
+               } else {
+                       push @names, 'local:'.$branch;
+               }
+       }
+}
+
+printf STDERR "git branches: %s\n", join(", ", @names);
+
+my $cr = "\\r\\n\\";
+printf <<EOF, $gitdesc, join($cr."\n\\t", @names);
+#define GIT_INFO "$cr
+This is a git build of %s$cr
+Associated branch(es):$cr
+\\t%s$cr
+"
+EOF
+
diff --git a/lib/hash.c b/lib/hash.c
new file mode 100644 (file)
index 0000000..56e41fa
--- /dev/null
@@ -0,0 +1,259 @@
+/* Hash routine.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+
+/* Allocate a new hash.  */
+struct hash *
+hash_create_size (unsigned int size, unsigned int (*hash_key) (void *),
+                                     int (*hash_cmp) (const void *, const void *))
+{
+  struct hash *hash;
+
+  assert ((size & (size-1)) == 0);
+  hash = XMALLOC (MTYPE_HASH, sizeof (struct hash));
+  hash->index = XCALLOC (MTYPE_HASH_INDEX,
+                        sizeof (struct hash_backet *) * size);
+  hash->size = size;
+  hash->no_expand = 0;
+  hash->hash_key = hash_key;
+  hash->hash_cmp = hash_cmp;
+  hash->count = 0;
+
+  return hash;
+}
+
+/* Allocate a new hash with default hash size.  */
+struct hash *
+hash_create (unsigned int (*hash_key) (void *), 
+             int (*hash_cmp) (const void *, const void *))
+{
+  return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp);
+}
+
+/* Utility function for hash_get().  When this function is specified
+   as alloc_func, return arugment as it is.  This function is used for
+   intern already allocated value.  */
+void *
+hash_alloc_intern (void *arg)
+{
+  return arg;
+}
+
+/* Expand hash if the chain length exceeds the threshold. */
+static void hash_expand (struct hash *hash)
+{
+  unsigned int i, new_size, losers;
+  struct hash_backet *hb, *hbnext, **new_index;
+
+  new_size = hash->size * 2;
+  new_index = XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_backet *) * new_size);
+  if (new_index == NULL)
+    return;
+
+  for (i = 0; i < hash->size; i++)
+    for (hb = hash->index[i]; hb; hb = hbnext)
+      {
+       unsigned int h = hb->key & (new_size - 1);
+
+       hbnext = hb->next;
+       hb->next = new_index[h];
+       new_index[h] = hb;
+      }
+
+  /* Switch to new table */
+  XFREE(MTYPE_HASH_INDEX, hash->index);
+  hash->size = new_size;
+  hash->index = new_index;
+
+  /* Ideally, new index should have chains half as long as the original.
+     If expansion didn't help, then not worth expanding again,
+     the problem is the hash function. */
+  losers = 0;
+  for (i = 0; i < hash->size; i++)
+    {
+      unsigned int len = 0;
+      for (hb = hash->index[i]; hb; hb = hb->next)
+       {
+         if (++len > HASH_THRESHOLD/2)
+           ++losers;
+         if (len >= HASH_THRESHOLD)
+           hash->no_expand = 1;
+       }
+    }
+
+  if (losers > hash->count / 2)
+    hash->no_expand = 1;
+}
+
+/* Lookup and return hash backet in hash.  If there is no
+   corresponding hash backet and alloc_func is specified, create new
+   hash backet.  */
+void *
+hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *))
+{
+  unsigned int key;
+  unsigned int index;
+  void *newdata;
+  unsigned int len;
+  struct hash_backet *backet;
+
+  key = (*hash->hash_key) (data);
+  index = key & (hash->size - 1);
+  len = 0;
+
+  for (backet = hash->index[index]; backet != NULL; backet = backet->next)
+    {
+      if (backet->key == key && (*hash->hash_cmp) (backet->data, data))
+       return backet->data;
+      ++len;
+    }
+
+  if (alloc_func)
+    {
+      newdata = (*alloc_func) (data);
+      if (newdata == NULL)
+       return NULL;
+
+      if (len > HASH_THRESHOLD && !hash->no_expand)
+       {
+         hash_expand (hash);
+         index = key & (hash->size - 1);
+       }
+
+      backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet));
+      backet->data = newdata;
+      backet->key = key;
+      backet->next = hash->index[index];
+      hash->index[index] = backet;
+      hash->count++;
+      return backet->data;
+    }
+  return NULL;
+}
+
+/* Hash lookup.  */
+void *
+hash_lookup (struct hash *hash, void *data)
+{
+  return hash_get (hash, data, NULL);
+}
+
+/* Simple Bernstein hash which is simple and fast for common case */
+unsigned int string_hash_make (const char *str)
+{
+  unsigned int hash = 0;
+
+  while (*str)
+    hash = (hash * 33) ^ (unsigned int) *str++;
+
+  return hash;
+}
+
+/* This function release registered value from specified hash.  When
+   release is successfully finished, return the data pointer in the
+   hash backet.  */
+void *
+hash_release (struct hash *hash, void *data)
+{
+  void *ret;
+  unsigned int key;
+  unsigned int index;
+  struct hash_backet *backet;
+  struct hash_backet *pp;
+
+  key = (*hash->hash_key) (data);
+  index = key & (hash->size - 1);
+
+  for (backet = pp = hash->index[index]; backet; backet = backet->next)
+    {
+      if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) 
+       {
+         if (backet == pp) 
+           hash->index[index] = backet->next;
+         else 
+           pp->next = backet->next;
+
+         ret = backet->data;
+         XFREE (MTYPE_HASH_BACKET, backet);
+         hash->count--;
+         return ret;
+       }
+      pp = backet;
+    }
+  return NULL;
+}
+
+/* Iterator function for hash.  */
+void
+hash_iterate (struct hash *hash, 
+             void (*func) (struct hash_backet *, void *), void *arg)
+{
+  unsigned int i;
+  struct hash_backet *hb;
+  struct hash_backet *hbnext;
+
+  for (i = 0; i < hash->size; i++)
+    for (hb = hash->index[i]; hb; hb = hbnext)
+      {
+       /* get pointer to next hash backet here, in case (*func)
+        * decides to delete hb by calling hash_release
+        */
+       hbnext = hb->next;
+       (*func) (hb, arg);
+      }
+}
+
+/* Clean up hash.  */
+void
+hash_clean (struct hash *hash, void (*free_func) (void *))
+{
+  unsigned int i;
+  struct hash_backet *hb;
+  struct hash_backet *next;
+
+  for (i = 0; i < hash->size; i++)
+    {
+      for (hb = hash->index[i]; hb; hb = next)
+       {
+         next = hb->next;
+             
+         if (free_func)
+           (*free_func) (hb->data);
+
+         XFREE (MTYPE_HASH_BACKET, hb);
+         hash->count--;
+       }
+      hash->index[i] = NULL;
+    }
+}
+
+/* Free hash memory.  You may call hash_clean before call this
+   function.  */
+void
+hash_free (struct hash *hash)
+{
+  XFREE (MTYPE_HASH_INDEX, hash->index);
+  XFREE (MTYPE_HASH, hash);
+}
diff --git a/lib/hash.h b/lib/hash.h
new file mode 100644 (file)
index 0000000..920c668
--- /dev/null
@@ -0,0 +1,79 @@
+/* Hash routine.
+   Copyright (C) 1998 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifndef _ZEBRA_HASH_H
+#define _ZEBRA_HASH_H
+
+/* Default hash table size.  */ 
+#define HASH_INITIAL_SIZE     256      /* initial number of backets. */
+#define HASH_THRESHOLD       10        /* expand when backet. */
+
+struct hash_backet
+{
+  /* Linked list.  */
+  struct hash_backet *next;
+
+  /* Hash key. */
+  unsigned int key;
+
+  /* Data.  */
+  void *data;
+};
+
+struct hash
+{
+  /* Hash backet. */
+  struct hash_backet **index;
+
+  /* Hash table size. Must be power of 2 */
+  unsigned int size;
+
+  /* If expansion failed. */
+  int no_expand;
+
+  /* Key make function. */
+  unsigned int (*hash_key) (void *);
+
+  /* Data compare function. */
+  int (*hash_cmp) (const void *, const void *);
+
+  /* Backet alloc. */
+  unsigned long count;
+};
+
+extern struct hash *hash_create (unsigned int (*) (void *), 
+                                int (*) (const void *, const void *));
+extern struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), 
+                                             int (*) (const void *, const void *));
+
+extern void *hash_get (struct hash *, void *, void * (*) (void *));
+extern void *hash_alloc_intern (void *);
+extern void *hash_lookup (struct hash *, void *);
+extern void *hash_release (struct hash *, void *);
+
+extern void hash_iterate (struct hash *, 
+                  void (*) (struct hash_backet *, void *), void *);
+
+extern void hash_clean (struct hash *, void (*) (void *));
+extern void hash_free (struct hash *);
+
+extern unsigned int string_hash_make (const char *);
+
+#endif /* _ZEBRA_HASH_H */
diff --git a/lib/if.c b/lib/if.c
new file mode 100644 (file)
index 0000000..0fc4b60
--- /dev/null
+++ b/lib/if.c
@@ -0,0 +1,1171 @@
+
+/* 
+ * Interface functions.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "vrf.h"
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "memory.h"
+#include "table.h"
+#include "buffer.h"
+#include "str.h"
+#include "log.h"
+
+/* List of interfaces in only the default VRF */
+struct list *iflist;
+
+/* One for each program.  This structure is needed to store hooks. */
+struct if_master
+{
+  int (*if_new_hook) (struct interface *);
+  int (*if_delete_hook) (struct interface *);
+} if_master = {0,};
+
+/* Compare interface names, returning an integer greater than, equal to, or
+ * less than 0, (following the strcmp convention), according to the
+ * relationship between ifp1 and ifp2.  Interface names consist of an
+ * alphabetic prefix and a numeric suffix.  The primary sort key is
+ * lexicographic by name, and then numeric by number.  No number sorts
+ * before all numbers.  Examples: de0 < de1, de100 < fxp0 < xl0, devpty <
+ * devpty0, de0 < del0
+ */         
+int
+if_cmp_func (struct interface *ifp1, struct interface *ifp2)
+{
+  unsigned int l1, l2;
+  long int x1, x2;
+  char *p1, *p2;
+  int res;
+
+  p1 = ifp1->name;
+  p2 = ifp2->name;
+
+  while (*p1 && *p2) {
+    /* look up to any number */
+    l1 = strcspn(p1, "0123456789");
+    l2 = strcspn(p2, "0123456789");
+
+    /* name lengths are different -> compare names */
+    if (l1 != l2)
+      return (strcmp(p1, p2));
+
+    /* Note that this relies on all numbers being less than all letters, so
+     * that de0 < del0.
+     */
+    res = strncmp(p1, p2, l1);
+
+    /* names are different -> compare them */
+    if (res)
+      return res;
+
+    /* with identical name part, go to numeric part */
+    p1 += l1;
+    p2 += l1;
+
+    if (!*p1) 
+      return -1;
+    if (!*p2) 
+      return 1;
+
+    x1 = strtol(p1, &p1, 10);
+    x2 = strtol(p2, &p2, 10);
+
+    /* let's compare numbers now */
+    if (x1 < x2)
+      return -1;
+    if (x1 > x2)
+      return 1;
+
+    /* numbers were equal, lets do it again..
+    (it happens with name like "eth123.456:789") */
+  }
+  if (*p1)
+    return 1;
+  if (*p2)
+    return -1;
+  return 0;
+}
+
+/* Create new interface structure. */
+struct interface *
+if_create_vrf (const char *name, int namelen, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct list *intf_list = vrf_iflist_get (vrf_id);
+
+  ifp = XCALLOC (MTYPE_IF, sizeof (struct interface));
+  ifp->ifindex = IFINDEX_INTERNAL;
+  
+  assert (name);
+  assert (namelen <= INTERFACE_NAMSIZ);        /* Need space for '\0' at end. */
+  strncpy (ifp->name, name, namelen);
+  ifp->name[namelen] = '\0';
+  ifp->vrf_id = vrf_id;
+  if (if_lookup_by_name_vrf (ifp->name, vrf_id) == NULL)
+    listnode_add_sort (intf_list, ifp);
+  else
+    zlog_err("if_create(%s): corruption detected -- interface with this "
+             "name exists already in VRF %u!", ifp->name, vrf_id);
+  ifp->connected = list_new ();
+  ifp->connected->del = (void (*) (void *)) connected_free;
+
+  if (if_master.if_new_hook)
+    (*if_master.if_new_hook) (ifp);
+
+  return ifp;
+}
+
+struct interface *
+if_create (const char *name, int namelen)
+{
+  return if_create_vrf (name, namelen, VRF_DEFAULT);
+}
+
+/* Delete interface structure. */
+void
+if_delete_retain (struct interface *ifp)
+{
+  if (if_master.if_delete_hook)
+    (*if_master.if_delete_hook) (ifp);
+
+  /* Free connected address list */
+  list_delete_all_node (ifp->connected);
+}
+
+/* Delete and free interface structure. */
+void
+if_delete (struct interface *ifp)
+{
+  listnode_delete (vrf_iflist (ifp->vrf_id), ifp);
+
+  if_delete_retain(ifp);
+
+  list_free (ifp->connected);
+
+  if_link_params_free (ifp);
+  
+  XFREE (MTYPE_IF, ifp);
+}
+
+/* Add hook to interface master. */
+void
+if_add_hook (int type, int (*func)(struct interface *ifp))
+{
+  switch (type) {
+  case IF_NEW_HOOK:
+    if_master.if_new_hook = func;
+    break;
+  case IF_DELETE_HOOK:
+    if_master.if_delete_hook = func;
+    break;
+  default:
+    break;
+  }
+}
+
+/* Interface existance check by index. */
+struct interface *
+if_lookup_by_index_vrf (ifindex_t ifindex, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      if (ifp->ifindex == ifindex)
+       return ifp;
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_index (ifindex_t ifindex)
+{
+  return if_lookup_by_index_vrf (ifindex, VRF_DEFAULT);
+}
+
+const char *
+ifindex2ifname_vrf (ifindex_t ifindex, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  return ((ifp = if_lookup_by_index_vrf (ifindex, vrf_id)) != NULL) ?
+        ifp->name : "unknown";
+}
+
+const char *
+ifindex2ifname (ifindex_t ifindex)
+{
+  return ifindex2ifname_vrf (ifindex, VRF_DEFAULT);
+}
+
+ifindex_t
+ifname2ifindex_vrf (const char *name, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  return ((ifp = if_lookup_by_name_vrf (name, vrf_id)) != NULL) ? ifp->ifindex
+                                                   : IFINDEX_INTERNAL;
+}
+
+ifindex_t
+ifname2ifindex (const char *name)
+{
+  return ifname2ifindex_vrf (name, VRF_DEFAULT);
+}
+
+/* Interface existance check by interface name. */
+struct interface *
+if_lookup_by_name_vrf (const char *name, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  
+  if (name)
+    for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+      {
+        if (strcmp(name, ifp->name) == 0)
+          return ifp;
+      }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_name (const char *name)
+{
+  return if_lookup_by_name_vrf (name, VRF_DEFAULT);
+}
+
+struct interface *
+if_lookup_by_name_len_vrf (const char *name, size_t namelen, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  if (namelen > INTERFACE_NAMSIZ)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      if (!memcmp(name, ifp->name, namelen) && (ifp->name[namelen] == '\0'))
+       return ifp;
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_by_name_len(const char *name, size_t namelen)
+{
+  return if_lookup_by_name_len_vrf (name, namelen, VRF_DEFAULT);
+}
+
+/* Lookup interface by IPv4 address. */
+struct interface *
+if_lookup_exact_address_vrf (struct in_addr src, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct prefix *p;
+  struct connected *c;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+       {
+         p = c->address;
+
+         if (p && p->family == AF_INET)
+           {
+             if (IPV4_ADDR_SAME (&p->u.prefix4, &src))
+               return ifp;
+           }         
+       }
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_exact_address (struct in_addr src)
+{
+  return if_lookup_exact_address_vrf (src, VRF_DEFAULT);
+}
+
+/* Lookup interface by IPv4 address. */
+struct interface *
+if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct prefix addr;
+  int bestlen = 0;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *c;
+  struct interface *match;
+
+  addr.family = AF_INET;
+  addr.u.prefix4 = src;
+  addr.prefixlen = IPV4_MAX_BITLEN;
+
+  match = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+       {
+         if (c->address && (c->address->family == AF_INET) &&
+             prefix_match(CONNECTED_PREFIX(c), &addr) &&
+             (c->address->prefixlen > bestlen))
+           {
+             bestlen = c->address->prefixlen;
+             match = ifp;
+           }
+       }
+    }
+  return match;
+}
+
+struct interface *
+if_lookup_address (struct in_addr src)
+{
+  return if_lookup_address_vrf (src, VRF_DEFAULT);
+}
+
+/* Lookup interface by prefix */
+struct interface *
+if_lookup_prefix_vrf (struct prefix *prefix, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *c;
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+        {
+          if (prefix_cmp(c->address, prefix) == 0)
+            {
+              return ifp;
+            }
+        }
+    }
+  return NULL;
+}
+
+struct interface *
+if_lookup_prefix (struct prefix *prefix)
+{
+  return if_lookup_prefix_vrf (prefix, VRF_DEFAULT);
+}
+
+/* Get interface by name if given name interface doesn't exist create
+   one. */
+struct interface *
+if_get_by_name_vrf (const char *name, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  return ((ifp = if_lookup_by_name_vrf (name, vrf_id)) != NULL) ? ifp :
+         if_create_vrf (name, strlen(name), vrf_id);
+}
+
+struct interface *
+if_get_by_name (const char *name)
+{
+  return if_get_by_name_vrf (name, VRF_DEFAULT);
+}
+
+struct interface *
+if_get_by_name_len_vrf (const char *name, size_t namelen, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  return ((ifp = if_lookup_by_name_len_vrf (name, namelen, vrf_id)) != NULL) ? \
+         ifp : if_create_vrf (name, namelen, vrf_id);
+}
+
+struct interface *
+if_get_by_name_len (const char *name, size_t namelen)
+{
+  return if_get_by_name_len_vrf (name, namelen, VRF_DEFAULT);
+}
+
+/* Does interface up ? */
+int
+if_is_up (struct interface *ifp)
+{
+  return ifp->flags & IFF_UP;
+}
+
+/* Is interface running? */
+int
+if_is_running (struct interface *ifp)
+{
+  return ifp->flags & IFF_RUNNING;
+}
+
+/* Is the interface operative, eg. either UP & RUNNING
+   or UP & !ZEBRA_INTERFACE_LINK_DETECTION */
+int
+if_is_operative (struct interface *ifp)
+{
+  return ((ifp->flags & IFF_UP) &&
+         (ifp->flags & IFF_RUNNING || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)));
+}
+
+/* Is this loopback interface ? */
+int
+if_is_loopback (struct interface *ifp)
+{
+  /* XXX: Do this better, eg what if IFF_WHATEVER means X on platform M
+   * but Y on platform N?
+   */
+  return (ifp->flags & (IFF_LOOPBACK|IFF_NOXMIT|IFF_VIRTUAL));
+}
+
+/* Does this interface support broadcast ? */
+int
+if_is_broadcast (struct interface *ifp)
+{
+  return ifp->flags & IFF_BROADCAST;
+}
+
+/* Does this interface support broadcast ? */
+int
+if_is_pointopoint (struct interface *ifp)
+{
+  return ifp->flags & IFF_POINTOPOINT;
+}
+
+/* Does this interface support multicast ? */
+int
+if_is_multicast (struct interface *ifp)
+{
+  return ifp->flags & IFF_MULTICAST;
+}
+
+/* Printout flag information into log */
+const char *
+if_flag_dump (unsigned long flag)
+{
+  int separator = 0;
+  static char logbuf[BUFSIZ];
+
+#define IFF_OUT_LOG(X,STR) \
+  if (flag & (X)) \
+    { \
+      if (separator) \
+       strlcat (logbuf, ",", BUFSIZ); \
+      else \
+       separator = 1; \
+      strlcat (logbuf, STR, BUFSIZ); \
+    }
+
+  strlcpy (logbuf, "<", BUFSIZ);
+  IFF_OUT_LOG (IFF_UP, "UP");
+  IFF_OUT_LOG (IFF_BROADCAST, "BROADCAST");
+  IFF_OUT_LOG (IFF_DEBUG, "DEBUG");
+  IFF_OUT_LOG (IFF_LOOPBACK, "LOOPBACK");
+  IFF_OUT_LOG (IFF_POINTOPOINT, "POINTOPOINT");
+  IFF_OUT_LOG (IFF_NOTRAILERS, "NOTRAILERS");
+  IFF_OUT_LOG (IFF_RUNNING, "RUNNING");
+  IFF_OUT_LOG (IFF_NOARP, "NOARP");
+  IFF_OUT_LOG (IFF_PROMISC, "PROMISC");
+  IFF_OUT_LOG (IFF_ALLMULTI, "ALLMULTI");
+  IFF_OUT_LOG (IFF_OACTIVE, "OACTIVE");
+  IFF_OUT_LOG (IFF_SIMPLEX, "SIMPLEX");
+  IFF_OUT_LOG (IFF_LINK0, "LINK0");
+  IFF_OUT_LOG (IFF_LINK1, "LINK1");
+  IFF_OUT_LOG (IFF_LINK2, "LINK2");
+  IFF_OUT_LOG (IFF_MULTICAST, "MULTICAST");
+  IFF_OUT_LOG (IFF_NOXMIT, "NOXMIT");
+  IFF_OUT_LOG (IFF_NORTEXCH, "NORTEXCH");
+  IFF_OUT_LOG (IFF_VIRTUAL, "VIRTUAL");
+  IFF_OUT_LOG (IFF_IPV4, "IPv4");
+  IFF_OUT_LOG (IFF_IPV6, "IPv6");
+
+  strlcat (logbuf, ">", BUFSIZ);
+
+  return logbuf;
+#undef IFF_OUT_LOG
+}
+
+/* For debugging */
+static void
+if_dump (const struct interface *ifp)
+{
+  struct listnode *node;
+  struct connected *c __attribute__((unused));
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c))
+    zlog_info ("Interface %s vrf %u index %d metric %d mtu %d "
+#ifdef HAVE_IPV6
+               "mtu6 %d "
+#endif /* HAVE_IPV6 */
+               "%s",
+               ifp->name, ifp->vrf_id, ifp->ifindex, ifp->metric, ifp->mtu,
+#ifdef HAVE_IPV6
+               ifp->mtu6,
+#endif /* HAVE_IPV6 */
+               if_flag_dump (ifp->flags));
+}
+
+/* Interface printing for all interface. */
+void
+if_dump_all (void)
+{
+  struct list *intf_list;
+  struct listnode *node;
+  void *p;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((intf_list = vrf_iter2iflist (iter)) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (intf_list, node, p))
+        if_dump (p);
+}
+
+DEFUN (interface_desc, 
+       interface_desc_cmd,
+       "description .LINE",
+       "Interface specific description\n"
+       "Characters describing this interface\n")
+{
+  struct interface *ifp;
+
+  if (argc == 0)
+    return CMD_SUCCESS;
+
+  ifp = vty->index;
+  if (ifp->desc)
+    XFREE (MTYPE_TMP, ifp->desc);
+  ifp->desc = argv_concat(argv, argc, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_interface_desc, 
+       no_interface_desc_cmd,
+       "no description",
+       NO_STR
+       "Interface specific description\n")
+{
+  struct interface *ifp;
+
+  ifp = vty->index;
+  if (ifp->desc)
+    XFREE (MTYPE_TMP, ifp->desc);
+  ifp->desc = NULL;
+
+  return CMD_SUCCESS;
+}
+
+#ifdef SUNOS_5
+/* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created
+ * a seperate struct interface for each logical interface, so config
+ * file may be full of 'interface fooX:Y'. Solaris however does not
+ * expose logical interfaces via PF_ROUTE, so trying to track logical
+ * interfaces can be fruitless, for that reason Quagga only tracks
+ * the primary IP interface.
+ *
+ * We try accomodate SUNWzebra by:
+ * - looking up the interface name, to see whether it exists, if so
+ *   its useable
+ *   - for protocol daemons, this could only because zebra told us of
+ *     the interface
+ *   - for zebra, only because it learnt from kernel
+ * - if not:
+ *   - search the name to see if it contains a sub-ipif / logical interface
+ *     seperator, the ':' char. If it does:
+ *     - text up to that char must be the primary name - get that name.
+ *     if not:
+ *     - no idea, just get the name in its entirety.
+ */
+static struct interface *
+if_sunwzebra_get (const char *name, size_t nlen, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  size_t seppos = 0;
+
+  if ( (ifp = if_lookup_by_name_len_vrf (name, nlen, vrf_id)) != NULL)
+    return ifp;
+  
+  /* hunt the primary interface name... */
+  while (seppos < nlen && name[seppos] != ':')
+    seppos++;
+  
+  /* Wont catch seperator as last char, e.g. 'foo0:' but thats invalid */
+  if (seppos < nlen)
+    return if_get_by_name_len_vrf (name, seppos, vrf_id);
+  else
+    return if_get_by_name_len_vrf (name, nlen, vrf_id);
+}
+#endif /* SUNOS_5 */
+
+DEFUN (interface,
+       interface_cmd,
+       "interface IFNAME",
+       "Select an interface to configure\n"
+       "Interface's name\n")
+{
+  struct interface *ifp;
+  size_t sl;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if ((sl = strlen(argv[0])) > INTERFACE_NAMSIZ)
+    {
+      vty_out (vty, "%% Interface name %s is invalid: length exceeds "
+                   "%d characters%s",
+              argv[0], INTERFACE_NAMSIZ, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+#ifdef SUNOS_5
+  ifp = if_sunwzebra_get (argv[0], sl, vrf_id);
+#else
+  ifp = if_get_by_name_len_vrf (argv[0], sl, vrf_id);
+#endif /* SUNOS_5 */
+
+  vty->index = ifp;
+  vty->node = INTERFACE_NODE;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (interface,
+       interface_vrf_cmd,
+       "interface IFNAME " VRF_CMD_STR,
+       "Select an interface to configure\n"
+       "Interface's name\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN_NOSH (no_interface,
+           no_interface_cmd,
+           "no interface IFNAME",
+           NO_STR
+           "Delete a pseudo interface's configuration\n"
+           "Interface's name\n")
+{
+  // deleting interface
+  struct interface *ifp;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  ifp = if_lookup_by_name_vrf (argv[0], vrf_id);
+
+  if (ifp == NULL)
+    {
+      vty_out (vty, "%% Interface %s does not exist%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) 
+    {
+      vty_out (vty, "%% Only inactive interfaces can be deleted%s",
+             VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if_delete(ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_interface,
+       no_interface_vrf_cmd,
+       "no interface IFNAME " VRF_CMD_STR,
+       NO_STR
+       "Delete a pseudo interface's configuration\n"
+       "Interface's name\n"
+       VRF_CMD_HELP_STR)
+
+/* For debug purpose. */
+DEFUN (show_address,
+       show_address_cmd,
+       "show address",
+       SHOW_STR
+       "address\n")
+{
+  struct listnode *node;
+  struct listnode *node2;
+  struct interface *ifp;
+  struct connected *ifc;
+  struct prefix *p;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, node2, ifc))
+       {
+         p = ifc->address;
+
+         if (p->family == AF_INET)
+           vty_out (vty, "%s/%d%s", inet_ntoa (p->u.prefix4), p->prefixlen,
+                    VTY_NEWLINE);
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_address,
+       show_address_vrf_cmd,
+       "show address " VRF_CMD_STR,
+       SHOW_STR
+       "address\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_address_vrf_all,
+       show_address_vrf_all_cmd,
+       "show address " VRF_ALL_CMD_STR,
+       SHOW_STR
+       "address\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct list *intf_list;
+  struct listnode *node;
+  struct listnode *node2;
+  struct interface *ifp;
+  struct connected *ifc;
+  struct prefix *p;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      intf_list = vrf_iter2iflist (iter);
+      if (!intf_list || !listcount (intf_list))
+        continue;
+
+      vty_out (vty, "%sVRF %u%s%s", VTY_NEWLINE, vrf_iter2id (iter),
+               VTY_NEWLINE, VTY_NEWLINE);
+
+      for (ALL_LIST_ELEMENTS_RO (intf_list, node, ifp))
+        {
+          for (ALL_LIST_ELEMENTS_RO (ifp->connected, node2, ifc))
+            {
+              p = ifc->address;
+
+              if (p->family == AF_INET)
+                vty_out (vty, "%s/%d%s", inet_ntoa (p->u.prefix4), p->prefixlen,
+                         VTY_NEWLINE);
+            }
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Allocate connected structure. */
+struct connected *
+connected_new (void)
+{
+  return XCALLOC (MTYPE_CONNECTED, sizeof (struct connected));
+}
+
+/* Free connected structure. */
+void
+connected_free (struct connected *connected)
+{
+  if (connected->address)
+    prefix_free (connected->address);
+
+  if (connected->destination)
+    prefix_free (connected->destination);
+
+  if (connected->label)
+    XFREE (MTYPE_CONNECTED_LABEL, connected->label);
+
+  XFREE (MTYPE_CONNECTED, connected);
+}
+
+/* Print if_addr structure. */
+static void __attribute__ ((unused))
+connected_log (struct connected *connected, char *str)
+{
+  struct prefix *p;
+  struct interface *ifp;
+  char logbuf[BUFSIZ];
+  char buf[BUFSIZ];
+  
+  ifp = connected->ifp;
+  p = connected->address;
+
+  snprintf (logbuf, BUFSIZ, "%s interface %s vrf %u %s %s/%d ",
+           str, ifp->name, ifp->vrf_id, prefix_family_str (p),
+           inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+           p->prefixlen);
+
+  p = connected->destination;
+  if (p)
+    {
+      strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+              BUFSIZ - strlen(logbuf));
+    }
+  zlog (NULL, LOG_INFO, "%s", logbuf);
+}
+
+/* If two connected address has same prefix return 1. */
+static int
+connected_same_prefix (struct prefix *p1, struct prefix *p2)
+{
+  if (p1->family == p2->family)
+    {
+      if (p1->family == AF_INET &&
+         IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4))
+       return 1;
+#ifdef HAVE_IPV6
+      if (p1->family == AF_INET6 &&
+         IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6))
+       return 1;
+#endif /* HAVE_IPV6 */
+    }
+  return 0;
+}
+
+struct connected *
+connected_delete_by_prefix (struct interface *ifp, struct prefix *p)
+{
+  struct listnode *node;
+  struct listnode *next;
+  struct connected *ifc;
+
+  /* In case of same prefix come, replace it with new one. */
+  for (node = listhead (ifp->connected); node; node = next)
+    {
+      ifc = listgetdata (node);
+      next = node->next;
+
+      if (connected_same_prefix (ifc->address, p))
+       {
+         listnode_delete (ifp->connected, ifc);
+         return ifc;
+       }
+    }
+  return NULL;
+}
+
+/* Find the IPv4 address on our side that will be used when packets
+   are sent to dst. */
+struct connected *
+connected_lookup_address (struct interface *ifp, struct in_addr dst)
+{
+  struct prefix addr;
+  struct listnode *cnode;
+  struct connected *c;
+  struct connected *match;
+
+  addr.family = AF_INET;
+  addr.u.prefix4 = dst;
+  addr.prefixlen = IPV4_MAX_BITLEN;
+
+  match = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
+    {
+      if (c->address && (c->address->family == AF_INET) &&
+         prefix_match(CONNECTED_PREFIX(c), &addr) &&
+         (!match || (c->address->prefixlen > match->address->prefixlen)))
+       match = c;
+    }
+  return match;
+}
+
+struct connected *
+connected_add_by_prefix (struct interface *ifp, struct prefix *p, 
+                         struct prefix *destination)
+{
+  struct connected *ifc;
+
+  /* Allocate new connected address. */
+  ifc = connected_new ();
+  ifc->ifp = ifp;
+
+  /* Fetch interface address */
+  ifc->address = prefix_new();
+  memcpy (ifc->address, p, sizeof(struct prefix));
+
+  /* Fetch dest address */
+  if (destination)
+    {
+      ifc->destination = prefix_new();
+      memcpy (ifc->destination, destination, sizeof(struct prefix));
+    }
+
+  /* Add connected address to the interface. */
+  listnode_add (ifp->connected, ifc);
+  return ifc;
+}
+
+#ifndef HAVE_IF_NAMETOINDEX
+ifindex_t
+if_nametoindex (const char *name)
+{
+  struct interface *ifp;
+
+  return ((ifp = if_lookup_by_name_len(name, strnlen(name, IFNAMSIZ))) != NULL)
+        ? ifp->ifindex : 0;
+}
+#endif
+
+#ifndef HAVE_IF_INDEXTONAME
+char *
+if_indextoname (ifindex_t ifindex, char *name)
+{
+  struct interface *ifp;
+
+  if (!(ifp = if_lookup_by_index(ifindex)))
+    return NULL;
+  strncpy (name, ifp->name, IFNAMSIZ);
+  return ifp->name;
+}
+#endif
+
+#if 0 /* this route_table of struct connected's is unused
+       * however, it would be good to use a route_table rather than
+       * a list..
+       */
+/* Interface looking up by interface's address. */
+/* Interface's IPv4 address reverse lookup table. */
+struct route_table *ifaddr_ipv4_table;
+/* struct route_table *ifaddr_ipv6_table; */
+
+static void
+ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = *ifaddr;
+
+  rn = route_node_get (ifaddr_ipv4_table, (struct prefix *) &p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      zlog_info ("ifaddr_ipv4_add(): address %s is already added",
+                inet_ntoa (*ifaddr));
+      return;
+    }
+  rn->info = ifp;
+}
+
+static void
+ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = *ifaddr;
+
+  rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
+  if (! rn)
+    {
+      zlog_info ("ifaddr_ipv4_delete(): can't find address %s",
+                inet_ntoa (*ifaddr));
+      return;
+    }
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+}
+
+/* Lookup interface by interface's IP address or interface index. */
+static struct interface *
+ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
+{
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct interface *ifp;
+
+  if (addr)
+    {
+      p.family = AF_INET;
+      p.prefixlen = IPV4_MAX_PREFIXLEN;
+      p.prefix = *addr;
+
+      rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
+      if (! rn)
+       return NULL;
+      
+      ifp = rn->info;
+      route_unlock_node (rn);
+      return ifp;
+    }
+  else
+    return if_lookup_by_index(ifindex);
+}
+#endif /* ifaddr_ipv4_table */
+
+/* Initialize interface list. */
+void
+if_init (vrf_id_t vrf_id, struct list **intf_list)
+{
+  *intf_list = list_new ();
+#if 0
+  ifaddr_ipv4_table = route_table_init ();
+#endif /* ifaddr_ipv4_table */
+
+  (*intf_list)->cmp = (int (*)(void *, void *))if_cmp_func;
+
+  if (vrf_id == VRF_DEFAULT)
+    iflist = *intf_list;
+}
+
+void
+if_terminate (vrf_id_t vrf_id, struct list **intf_list)
+{
+  for (;;)
+    {
+      struct interface *ifp;
+
+      ifp = listnode_head (*intf_list);
+      if (ifp == NULL)
+       break;
+
+      if_delete (ifp);
+    }
+
+  list_delete (*intf_list);
+  *intf_list = NULL;
+
+  if (vrf_id == VRF_DEFAULT)
+    iflist = NULL;
+}
+
+const char *
+if_link_type_str (enum zebra_link_type llt)
+{
+  switch (llt)
+    {
+#define llts(T,S) case (T): return (S)
+      llts(ZEBRA_LLT_UNKNOWN,               "Unknown");
+      llts(ZEBRA_LLT_ETHER,                 "Ethernet");
+      llts(ZEBRA_LLT_EETHER,                "Experimental Ethernet");
+      llts(ZEBRA_LLT_AX25,                  "AX.25 Level 2");
+      llts(ZEBRA_LLT_PRONET,                "PROnet token ring");
+      llts(ZEBRA_LLT_IEEE802,               "IEEE 802.2 Ethernet/TR/TB");
+      llts(ZEBRA_LLT_ARCNET,                "ARCnet");
+      llts(ZEBRA_LLT_APPLETLK,              "AppleTalk");
+      llts(ZEBRA_LLT_DLCI,                  "Frame Relay DLCI");
+      llts(ZEBRA_LLT_ATM,                   "ATM");
+      llts(ZEBRA_LLT_METRICOM,              "Metricom STRIP");
+      llts(ZEBRA_LLT_IEEE1394,              "IEEE 1394 IPv4");
+      llts(ZEBRA_LLT_EUI64,                 "EUI-64");
+      llts(ZEBRA_LLT_INFINIBAND,            "InfiniBand");
+      llts(ZEBRA_LLT_SLIP,                  "SLIP");
+      llts(ZEBRA_LLT_CSLIP,                 "Compressed SLIP");
+      llts(ZEBRA_LLT_SLIP6,                 "SLIPv6");
+      llts(ZEBRA_LLT_CSLIP6,                "Compressed SLIPv6");
+      llts(ZEBRA_LLT_ROSE,                  "ROSE packet radio");
+      llts(ZEBRA_LLT_X25,                   "CCITT X.25");
+      llts(ZEBRA_LLT_PPP,                   "PPP");
+      llts(ZEBRA_LLT_CHDLC,                 "Cisco HDLC");
+      llts(ZEBRA_LLT_RAWHDLC,               "Raw HDLC");
+      llts(ZEBRA_LLT_LAPB,                  "LAPB");
+      llts(ZEBRA_LLT_IPIP,                  "IPIP Tunnel");
+      llts(ZEBRA_LLT_IPIP6,                 "IPIP6 Tunnel");
+      llts(ZEBRA_LLT_FRAD,                  "FRAD");
+      llts(ZEBRA_LLT_SKIP,                  "SKIP vif");
+      llts(ZEBRA_LLT_LOOPBACK,              "Loopback");
+      llts(ZEBRA_LLT_LOCALTLK,              "Localtalk");
+      llts(ZEBRA_LLT_FDDI,                  "FDDI");
+      llts(ZEBRA_LLT_SIT,                   "IPv6-in-IPv4 SIT");
+      llts(ZEBRA_LLT_IPDDP,                 "IP-in-DDP tunnel");
+      llts(ZEBRA_LLT_IPGRE,                 "GRE over IP");
+      llts(ZEBRA_LLT_PIMREG,                "PIMSM registration");
+      llts(ZEBRA_LLT_HIPPI,                 "HiPPI");
+      llts(ZEBRA_LLT_IRDA,                  "IrDA");
+      llts(ZEBRA_LLT_FCPP,                  "Fibre-Channel PtP");
+      llts(ZEBRA_LLT_FCAL,                  "Fibre-Channel Arbitrated Loop");
+      llts(ZEBRA_LLT_FCPL,                  "Fibre-Channel Public Loop");
+      llts(ZEBRA_LLT_FCFABRIC,              "Fibre-Channel Fabric");
+      llts(ZEBRA_LLT_IEEE802_TR,            "IEEE 802.2 Token Ring");
+      llts(ZEBRA_LLT_IEEE80211,             "IEEE 802.11");
+      llts(ZEBRA_LLT_IEEE80211_RADIOTAP,    "IEEE 802.11 Radiotap");
+      llts(ZEBRA_LLT_IEEE802154,            "IEEE 802.15.4");
+      llts(ZEBRA_LLT_IEEE802154_PHY,        "IEEE 802.15.4 Phy");
+      default:
+        zlog_warn ("Unknown value %d", llt);
+        return "Unknown type!";
+#undef llts
+    }
+  return NULL;
+}
+
+struct if_link_params *
+if_link_params_get (struct interface *ifp)
+{
+  int i;
+  
+  if (ifp->link_params != NULL)
+    return ifp->link_params;
+  
+  struct if_link_params *iflp = XCALLOC(MTYPE_IF_LINK_PARAMS,
+                                      sizeof (struct if_link_params));
+  if (iflp == NULL) return NULL;
+  
+  /* Set TE metric == standard metric */
+  iflp->te_metric = ifp->metric;
+
+  /* Compute default bandwidth based on interface */
+  int bw = (float)((ifp->bandwidth ? ifp->bandwidth : DEFAULT_BANDWIDTH) 
+                   * TE_KILO_BIT / TE_BYTE);
+  
+  /* Set Max, Reservable and Unreserved Bandwidth */
+  iflp->max_bw = bw;
+  iflp->max_rsv_bw = bw;
+  for (i = 0; i < MAX_CLASS_TYPE; i++)
+    iflp->unrsv_bw[i] = bw;
+  
+  /* Update Link parameters status */
+  iflp->lp_status = LP_TE | LP_MAX_BW | LP_MAX_RSV_BW | LP_UNRSV_BW;
+
+  /* Finally attach newly created Link Parameters */
+  ifp->link_params = iflp;
+
+  return iflp;
+}
+
+void
+if_link_params_free (struct interface *ifp)
+{
+  if (ifp->link_params == NULL) return;
+  XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params);
+  ifp->link_params = NULL;
+}
diff --git a/lib/if.h b/lib/if.h
new file mode 100644 (file)
index 0000000..862f7d4
--- /dev/null
+++ b/lib/if.h
@@ -0,0 +1,471 @@
+/* Interface related header.
+   Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifndef _ZEBRA_IF_H
+#define _ZEBRA_IF_H
+
+#include "zebra.h"
+#include "linklist.h"
+
+/* Interface link-layer type, if known. Derived from:
+ *
+ * net/if_arp.h on various platforms - Linux especially.
+ * http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
+ *
+ * Some of the more obviously defunct technologies left out.
+ */
+enum zebra_link_type {
+  ZEBRA_LLT_UNKNOWN = 0,
+  ZEBRA_LLT_ETHER,
+  ZEBRA_LLT_EETHER,
+  ZEBRA_LLT_AX25,
+  ZEBRA_LLT_PRONET,
+  ZEBRA_LLT_IEEE802,
+  ZEBRA_LLT_ARCNET,
+  ZEBRA_LLT_APPLETLK,
+  ZEBRA_LLT_DLCI,
+  ZEBRA_LLT_ATM,
+  ZEBRA_LLT_METRICOM,
+  ZEBRA_LLT_IEEE1394,
+  ZEBRA_LLT_EUI64,
+  ZEBRA_LLT_INFINIBAND,
+  ZEBRA_LLT_SLIP,
+  ZEBRA_LLT_CSLIP,
+  ZEBRA_LLT_SLIP6,
+  ZEBRA_LLT_CSLIP6,
+  ZEBRA_LLT_RSRVD,
+  ZEBRA_LLT_ADAPT,
+  ZEBRA_LLT_ROSE,
+  ZEBRA_LLT_X25,
+  ZEBRA_LLT_PPP,
+  ZEBRA_LLT_CHDLC,
+  ZEBRA_LLT_LAPB,
+  ZEBRA_LLT_RAWHDLC,
+  ZEBRA_LLT_IPIP,
+  ZEBRA_LLT_IPIP6,
+  ZEBRA_LLT_FRAD,
+  ZEBRA_LLT_SKIP,
+  ZEBRA_LLT_LOOPBACK,
+  ZEBRA_LLT_LOCALTLK,
+  ZEBRA_LLT_FDDI,
+  ZEBRA_LLT_SIT,
+  ZEBRA_LLT_IPDDP,
+  ZEBRA_LLT_IPGRE,
+  ZEBRA_LLT_IP6GRE,
+  ZEBRA_LLT_PIMREG,
+  ZEBRA_LLT_HIPPI,
+  ZEBRA_LLT_ECONET,
+  ZEBRA_LLT_IRDA,
+  ZEBRA_LLT_FCPP,
+  ZEBRA_LLT_FCAL,
+  ZEBRA_LLT_FCPL,
+  ZEBRA_LLT_FCFABRIC,
+  ZEBRA_LLT_IEEE802_TR,
+  ZEBRA_LLT_IEEE80211,
+  ZEBRA_LLT_IEEE80211_RADIOTAP,
+  ZEBRA_LLT_IEEE802154,
+  ZEBRA_LLT_IEEE802154_PHY,
+};
+
+/*
+  Interface name length.
+
+   Linux define value in /usr/include/linux/if.h.
+   #define IFNAMSIZ        16
+
+   FreeBSD define value in /usr/include/net/if.h.
+   #define IFNAMSIZ        16
+*/
+
+#define INTERFACE_NAMSIZ      20
+#define INTERFACE_HWADDR_MAX  20
+
+typedef signed int ifindex_t;
+
+#ifdef HAVE_PROC_NET_DEV
+struct if_stats
+{
+  unsigned long rx_packets;   /* total packets received       */
+  unsigned long tx_packets;   /* total packets transmitted    */
+  unsigned long rx_bytes;     /* total bytes received         */
+  unsigned long tx_bytes;     /* total bytes transmitted      */
+  unsigned long rx_errors;    /* bad packets received         */
+  unsigned long tx_errors;    /* packet transmit problems     */
+  unsigned long rx_dropped;   /* no space in linux buffers    */
+  unsigned long tx_dropped;   /* no space available in linux  */
+  unsigned long rx_multicast; /* multicast packets received   */
+  unsigned long rx_compressed;
+  unsigned long tx_compressed;
+  unsigned long collisions;
+
+  /* detailed rx_errors: */
+  unsigned long rx_length_errors;
+  unsigned long rx_over_errors;       /* receiver ring buff overflow  */
+  unsigned long rx_crc_errors;        /* recved pkt with crc error    */
+  unsigned long rx_frame_errors;      /* recv'd frame alignment error */
+  unsigned long rx_fifo_errors;       /* recv'r fifo overrun          */
+  unsigned long rx_missed_errors;     /* receiver missed packet     */
+  /* detailed tx_errors */
+  unsigned long tx_aborted_errors;
+  unsigned long tx_carrier_errors;
+  unsigned long tx_fifo_errors;
+  unsigned long tx_heartbeat_errors;
+  unsigned long tx_window_errors;
+};
+#endif /* HAVE_PROC_NET_DEV */
+
+/* Here are "non-official" architectural constants. */
+#define TE_EXT_MASK             0x0FFFFFFF
+#define TE_EXT_ANORMAL          0x80000000
+#define LOSS_PRECISION          0.000003
+#define TE_KILO_BIT             1000
+#define TE_BYTE                 8
+#define DEFAULT_BANDWIDTH       10000
+#define MAX_CLASS_TYPE          8
+#define MAX_PKT_LOSS            50.331642
+
+/* Link Parameters Status: 0: unset, 1: set, */
+#define LP_UNSET                0x0000
+#define LP_TE                   0x0001
+#define LP_MAX_BW               0x0002
+#define LP_MAX_RSV_BW           0x0004
+#define LP_UNRSV_BW             0x0008
+#define LP_ADM_GRP              0x0010
+#define LP_RMT_AS               0x0020
+#define LP_DELAY                0x0040
+#define LP_MM_DELAY             0x0080
+#define LP_DELAY_VAR            0x0100
+#define LP_PKT_LOSS             0x0200
+#define LP_RES_BW               0x0400
+#define LP_AVA_BW               0x0800
+#define LP_USE_BW               0x1000
+
+#define IS_PARAM_UNSET(lp, st) !(lp->lp_status & st)
+#define IS_PARAM_SET(lp, st) (lp->lp_status & st)
+#define IS_LINK_PARAMS_SET(lp) (lp->lp_status != LP_UNSET)
+
+#define SET_PARAM(lp, st) (lp->lp_status) |= (st)
+#define UNSET_PARAM(lp, st) (lp->lp_status) &= ~(st)
+#define RESET_LINK_PARAM(lp) (lp->lp_status = LP_UNSET)
+
+/* Link Parameters for Traffic Engineering */
+struct if_link_params {
+  u_int32_t lp_status;   /* Status of Link Parameters: */
+  u_int32_t te_metric;   /* Traffic Engineering metric */
+  float max_bw;          /* Maximum Bandwidth */
+  float max_rsv_bw;      /* Maximum Reservable Bandwidth */
+  float unrsv_bw[MAX_CLASS_TYPE];     /* Unreserved Bandwidth per Class Type (8) */
+  u_int32_t admin_grp;   /* Administrative group */
+  u_int32_t rmt_as;      /* Remote AS number */
+  struct in_addr rmt_ip; /* Remote IP address */
+  u_int32_t av_delay;    /* Link Average Delay */
+  u_int32_t min_delay;   /* Link Min Delay */
+  u_int32_t max_delay;   /* Link Max Delay */
+  u_int32_t delay_var;   /* Link Delay Variation */
+  float pkt_loss;        /* Link Packet Loss */
+  float res_bw;          /* Residual Bandwidth */
+  float ava_bw;          /* Available Bandwidth */
+  float use_bw;          /* Utilized Bandwidth */
+};
+
+#define INTERFACE_LINK_PARAMS_SIZE   sizeof(struct if_link_params)
+#define HAS_LINK_PARAMS(ifp)  ((ifp)->link_params != NULL)
+
+/* Interface structure */
+struct interface 
+{
+  /* Interface name.  This should probably never be changed after the
+     interface is created, because the configuration info for this interface
+     is associated with this structure.  For that reason, the interface
+     should also never be deleted (to avoid losing configuration info).
+     To delete, just set ifindex to IFINDEX_INTERNAL to indicate that the
+     interface does not exist in the kernel.
+   */
+  char name[INTERFACE_NAMSIZ + 1];
+
+  /* Interface index (should be IFINDEX_INTERNAL for non-kernel or
+     deleted interfaces). */
+  ifindex_t ifindex;
+#define IFINDEX_INTERNAL       0
+
+  /* Zebra internal interface status */
+  u_char status;
+#define ZEBRA_INTERFACE_ACTIVE     (1 << 0)
+#define ZEBRA_INTERFACE_SUB        (1 << 1)
+#define ZEBRA_INTERFACE_LINKDETECTION (1 << 2)
+  
+  /* Interface flags. */
+  uint64_t flags;
+
+  /* Interface metric */
+  int metric;
+
+  /* Interface MTU. */
+  unsigned int mtu;    /* IPv4 MTU */
+  unsigned int mtu6;   /* IPv6 MTU - probably, but not neccessarily same as mtu */
+
+  /* Link-layer information and hardware address */
+  enum zebra_link_type ll_type;
+  u_char hw_addr[INTERFACE_HWADDR_MAX];
+  int hw_addr_len;
+
+  /* interface bandwidth, kbits */
+  unsigned int bandwidth;
+  
+  /* Link parameters for Traffic Engineering */
+  struct if_link_params *link_params;
+
+  /* description of the interface. */
+  char *desc;                  
+
+  /* Distribute list. */
+  void *distribute_in;
+  void *distribute_out;
+
+  /* Connected address list. */
+  struct list *connected;
+
+  /* Daemon specific interface data pointer. */
+  void *info;
+
+  /* Statistics fileds. */
+#ifdef HAVE_PROC_NET_DEV
+  struct if_stats stats;
+#endif /* HAVE_PROC_NET_DEV */  
+#ifdef HAVE_NET_RT_IFLIST
+  struct if_data stats;
+#endif /* HAVE_NET_RT_IFLIST */
+
+  vrf_id_t vrf_id;
+};
+
+/* Connected address structure. */
+struct connected
+{
+  /* Attached interface. */
+  struct interface *ifp;
+
+  /* Flags for configuration. */
+  u_char conf;
+#define ZEBRA_IFC_REAL         (1 << 0)
+#define ZEBRA_IFC_CONFIGURED   (1 << 1)
+#define ZEBRA_IFC_QUEUED       (1 << 2)
+  /*
+     The ZEBRA_IFC_REAL flag should be set if and only if this address
+     exists in the kernel and is actually usable. (A case where it exists but
+     is not yet usable would be IPv6 with DAD)
+     The ZEBRA_IFC_CONFIGURED flag should be set if and only if this address
+     was configured by the user from inside quagga.
+     The ZEBRA_IFC_QUEUED flag should be set if and only if the address exists
+     in the kernel. It may and should be set although the address might not be
+     usable yet. (compare with ZEBRA_IFC_REAL)
+   */
+
+  /* Flags for connected address. */
+  u_char flags;
+#define ZEBRA_IFA_SECONDARY    (1 << 0)
+#define ZEBRA_IFA_PEER         (1 << 1)
+#define ZEBRA_IFA_UNNUMBERED   (1 << 2)
+  /* N.B. the ZEBRA_IFA_PEER flag should be set if and only if
+     a peer address has been configured.  If this flag is set,
+     the destination field must contain the peer address.  
+     Otherwise, if this flag is not set, the destination address
+     will either contain a broadcast address or be NULL.
+   */
+
+  /* Address of connected network. */
+  struct prefix *address;
+
+  /* Peer or Broadcast address, depending on whether ZEBRA_IFA_PEER is set.
+     Note: destination may be NULL if ZEBRA_IFA_PEER is not set. */
+  struct prefix *destination;
+
+  /* Label for Linux 2.2.X and upper. */
+  char *label;
+};
+
+/* Does the destination field contain a peer address? */
+#define CONNECTED_PEER(C) CHECK_FLAG((C)->flags, ZEBRA_IFA_PEER)
+
+/* Prefix to insert into the RIB */
+#define CONNECTED_PREFIX(C) \
+       (CONNECTED_PEER(C) ? (C)->destination : (C)->address)
+
+/* Identifying address.  We guess that if there's a peer address, but the
+   local address is in the same prefix, then the local address may be unique. */
+#define CONNECTED_ID(C)        \
+       ((CONNECTED_PEER(C) && !prefix_match((C)->destination, (C)->address)) ?\
+        (C)->destination : (C)->address)
+
+/* Interface hook sort. */
+#define IF_NEW_HOOK   0
+#define IF_DELETE_HOOK 1
+
+/* There are some interface flags which are only supported by some
+   operating system. */
+
+#ifndef IFF_NOTRAILERS
+#define IFF_NOTRAILERS 0x0
+#endif /* IFF_NOTRAILERS */
+#ifndef IFF_OACTIVE
+#define IFF_OACTIVE 0x0
+#endif /* IFF_OACTIVE */
+#ifndef IFF_SIMPLEX
+#define IFF_SIMPLEX 0x0
+#endif /* IFF_SIMPLEX */
+#ifndef IFF_LINK0
+#define IFF_LINK0 0x0
+#endif /* IFF_LINK0 */
+#ifndef IFF_LINK1
+#define IFF_LINK1 0x0
+#endif /* IFF_LINK1 */
+#ifndef IFF_LINK2
+#define IFF_LINK2 0x0
+#endif /* IFF_LINK2 */
+#ifndef IFF_NOXMIT
+#define IFF_NOXMIT 0x0
+#endif /* IFF_NOXMIT */
+#ifndef IFF_NORTEXCH
+#define IFF_NORTEXCH 0x0
+#endif /* IFF_NORTEXCH */
+#ifndef IFF_IPV4
+#define IFF_IPV4 0x0
+#endif /* IFF_IPV4 */
+#ifndef IFF_IPV6
+#define IFF_IPV6 0x0
+#endif /* IFF_IPV6 */
+#ifndef IFF_VIRTUAL
+#define IFF_VIRTUAL 0x0
+#endif /* IFF_VIRTUAL */
+
+/* Prototypes. */
+extern int if_cmp_func (struct interface *, struct interface *);
+extern struct interface *if_create (const char *name, int namelen);
+extern struct interface *if_lookup_by_index (ifindex_t);
+extern struct interface *if_lookup_exact_address (struct in_addr);
+extern struct interface *if_lookup_address (struct in_addr);
+extern struct interface *if_lookup_prefix (struct prefix *prefix);
+
+extern struct interface *if_create_vrf (const char *name, int namelen,
+                                vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_index_vrf (ifindex_t, vrf_id_t vrf_id);
+extern struct interface *if_lookup_exact_address_vrf (struct in_addr,
+                                vrf_id_t vrf_id);
+extern struct interface *if_lookup_address_vrf (struct in_addr,
+                                vrf_id_t vrf_id);
+extern struct interface *if_lookup_prefix_vrf (struct prefix *prefix,
+                                vrf_id_t vrf_id);
+
+/* These 2 functions are to be used when the ifname argument is terminated
+   by a '\0' character: */
+extern struct interface *if_lookup_by_name (const char *ifname);
+extern struct interface *if_get_by_name (const char *ifname);
+
+extern struct interface *if_lookup_by_name_vrf (const char *ifname,
+                                vrf_id_t vrf_id);
+extern struct interface *if_get_by_name_vrf (const char *ifname,
+                                vrf_id_t vrf_id);
+
+/* For these 2 functions, the namelen argument should be the precise length
+   of the ifname string (not counting any optional trailing '\0' character).
+   In most cases, strnlen should be used to calculate the namelen value. */
+extern struct interface *if_lookup_by_name_len(const char *ifname,
+                                              size_t namelen);
+extern struct interface *if_get_by_name_len(const char *ifname,size_t namelen);
+
+extern struct interface *if_lookup_by_name_len_vrf(const char *ifname,
+                                size_t namelen, vrf_id_t vrf_id);
+extern struct interface *if_get_by_name_len_vrf(const char *ifname,
+                                size_t namelen, vrf_id_t vrf_id);
+
+
+/* Delete the interface, but do not free the structure, and leave it in the
+   interface list.  It is often advisable to leave the pseudo interface 
+   structure because there may be configuration information attached. */
+extern void if_delete_retain (struct interface *);
+
+/* Delete and free the interface structure: calls if_delete_retain and then
+   deletes it from the interface list and frees the structure. */
+extern void if_delete (struct interface *);
+
+extern int if_is_up (struct interface *);
+extern int if_is_running (struct interface *);
+extern int if_is_operative (struct interface *);
+extern int if_is_loopback (struct interface *);
+extern int if_is_broadcast (struct interface *);
+extern int if_is_pointopoint (struct interface *);
+extern int if_is_multicast (struct interface *);
+extern void if_add_hook (int, int (*)(struct interface *));
+extern void if_init (vrf_id_t, struct list **);
+extern void if_terminate (vrf_id_t, struct list **);
+extern void if_dump_all (void);
+extern const char *if_flag_dump(unsigned long);
+extern const char *if_link_type_str (enum zebra_link_type);
+
+/* Please use ifindex2ifname instead of if_indextoname where possible;
+   ifindex2ifname uses internal interface info, whereas if_indextoname must
+   make a system call. */
+extern const char *ifindex2ifname (ifindex_t);
+extern const char *ifindex2ifname_vrf (ifindex_t, vrf_id_t vrf_id);
+
+/* Please use ifname2ifindex instead of if_nametoindex where possible;
+   ifname2ifindex uses internal interface info, whereas if_nametoindex must
+   make a system call. */
+extern ifindex_t ifname2ifindex(const char *ifname);
+extern ifindex_t ifname2ifindex_vrf(const char *ifname, vrf_id_t vrf_id);
+
+/* Connected address functions. */
+extern struct connected *connected_new (void);
+extern void connected_free (struct connected *);
+extern void connected_add (struct interface *, struct connected *);
+extern struct connected  *connected_add_by_prefix (struct interface *,
+                                            struct prefix *,
+                                            struct prefix *);
+extern struct connected  *connected_delete_by_prefix (struct interface *, 
+                                               struct prefix *);
+extern struct connected  *connected_lookup_address (struct interface *, 
+                                             struct in_addr);
+
+#ifndef HAVE_IF_NAMETOINDEX
+extern ifindex_t if_nametoindex (const char *);
+#endif
+#ifndef HAVE_IF_INDEXTONAME
+extern char *if_indextoname (ifindex_t, char *);
+#endif
+
+/* link parameters */
+struct if_link_params *if_link_params_get (struct interface *);
+void if_link_params_free (struct interface *);
+
+/* Exported variables. */
+extern struct list *iflist;
+extern struct cmd_element interface_desc_cmd;
+extern struct cmd_element no_interface_desc_cmd;
+extern struct cmd_element interface_cmd;
+extern struct cmd_element no_interface_cmd;
+extern struct cmd_element interface_vrf_cmd;
+extern struct cmd_element no_interface_vrf_cmd;
+extern struct cmd_element interface_pseudo_cmd;
+extern struct cmd_element no_interface_pseudo_cmd;
+extern struct cmd_element show_address_cmd;
+extern struct cmd_element show_address_vrf_cmd;
+extern struct cmd_element show_address_vrf_all_cmd;
+
+#endif /* _ZEBRA_IF_H */
diff --git a/lib/if_rmap.c b/lib/if_rmap.c
new file mode 100644 (file)
index 0000000..e4a83de
--- /dev/null
@@ -0,0 +1,330 @@
+/* route-map for interface.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "command.h"
+#include "memory.h"
+#include "if.h"
+#include "if_rmap.h"
+
+struct hash *ifrmaphash;
+
+/* Hook functions. */
+static void (*if_rmap_add_hook) (struct if_rmap *) = NULL;
+static void (*if_rmap_delete_hook) (struct if_rmap *) = NULL;
+
+static struct if_rmap *
+if_rmap_new (void)
+{
+  struct if_rmap *new;
+
+  new = XCALLOC (MTYPE_IF_RMAP, sizeof (struct if_rmap));
+
+  return new;
+}
+
+static void
+if_rmap_free (struct if_rmap *if_rmap)
+{
+  if (if_rmap->ifname)
+    XFREE (MTYPE_IF_RMAP_NAME, if_rmap->ifname);
+
+  if (if_rmap->routemap[IF_RMAP_IN])
+    XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
+  if (if_rmap->routemap[IF_RMAP_OUT])
+    XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
+
+  XFREE (MTYPE_IF_RMAP, if_rmap);
+}
+
+struct if_rmap *
+if_rmap_lookup (const char *ifname)
+{
+  struct if_rmap key;
+  struct if_rmap *if_rmap;
+
+  /* temporary copy */
+  key.ifname = (char *)ifname;
+
+  if_rmap = hash_lookup (ifrmaphash, &key);
+  
+  return if_rmap;
+}
+
+void
+if_rmap_hook_add (void (*func) (struct if_rmap *))
+{
+  if_rmap_add_hook = func;
+}
+
+void
+if_rmap_hook_delete (void (*func) (struct if_rmap *))
+{
+  if_rmap_delete_hook = func;
+}
+
+static void *
+if_rmap_hash_alloc (void *arg)
+{
+  struct if_rmap *ifarg = arg;
+  struct if_rmap *if_rmap;
+
+  if_rmap = if_rmap_new ();
+  if_rmap->ifname = XSTRDUP (MTYPE_IF_RMAP_NAME, ifarg->ifname);
+
+  return if_rmap;
+}
+
+static struct if_rmap *
+if_rmap_get (const char *ifname)
+{
+  struct if_rmap key;
+
+  /* temporary copy */
+  key.ifname = (char *)ifname;
+
+  return (struct if_rmap *) hash_get (ifrmaphash, &key, if_rmap_hash_alloc);
+}
+
+static unsigned int
+if_rmap_hash_make (void *data)
+{
+  const struct if_rmap *if_rmap = data;
+
+  return string_hash_make (if_rmap->ifname);
+}
+
+static int
+if_rmap_hash_cmp (const void *arg1, const void* arg2)
+{
+  const struct if_rmap *if_rmap1 = arg1;
+  const struct if_rmap *if_rmap2 = arg2;
+
+  return strcmp (if_rmap1->ifname, if_rmap2->ifname) == 0;
+}
+
+static struct if_rmap *
+if_rmap_set (const char *ifname, enum if_rmap_type type, 
+             const char *routemap_name)
+{
+  struct if_rmap *if_rmap;
+
+  if_rmap = if_rmap_get (ifname);
+
+  if (type == IF_RMAP_IN)
+    {
+      if (if_rmap->routemap[IF_RMAP_IN])
+       XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
+      if_rmap->routemap[IF_RMAP_IN] 
+        = XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name);
+    }
+  if (type == IF_RMAP_OUT)
+    {
+      if (if_rmap->routemap[IF_RMAP_OUT])
+       XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
+      if_rmap->routemap[IF_RMAP_OUT] 
+        = XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name);
+    }
+
+  if (if_rmap_add_hook)
+    (*if_rmap_add_hook) (if_rmap);
+  
+  return if_rmap;
+}
+
+static int
+if_rmap_unset (const char *ifname, enum if_rmap_type type, 
+               const char *routemap_name)
+{
+  struct if_rmap *if_rmap;
+
+  if_rmap = if_rmap_lookup (ifname);
+  if (!if_rmap)
+    return 0;
+
+  if (type == IF_RMAP_IN)
+    {
+      if (!if_rmap->routemap[IF_RMAP_IN])
+       return 0;
+      if (strcmp (if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0)
+       return 0;
+
+      XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
+      if_rmap->routemap[IF_RMAP_IN] = NULL;      
+    }
+
+  if (type == IF_RMAP_OUT)
+    {
+      if (!if_rmap->routemap[IF_RMAP_OUT])
+       return 0;
+      if (strcmp (if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0)
+       return 0;
+
+      XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
+      if_rmap->routemap[IF_RMAP_OUT] = NULL;      
+    }
+
+  if (if_rmap_delete_hook)
+    (*if_rmap_delete_hook) (if_rmap);
+
+  if (if_rmap->routemap[IF_RMAP_IN] == NULL &&
+      if_rmap->routemap[IF_RMAP_OUT] == NULL)
+    {
+      hash_release (ifrmaphash, if_rmap);
+      if_rmap_free (if_rmap);
+    }
+
+  return 1;
+}
+
+DEFUN (if_rmap,
+       if_rmap_cmd,
+       "route-map RMAP_NAME (in|out) IFNAME",
+       "Route map set\n"
+       "Route map name\n"
+       "Route map set for input filtering\n"
+       "Route map set for output filtering\n"
+       "Route map interface name\n")
+{
+  enum if_rmap_type type;
+
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = IF_RMAP_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = IF_RMAP_OUT;
+  else
+    {
+      vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if_rmap_set (argv[2], type, argv[0]);
+
+  return CMD_SUCCESS;
+}      
+
+ALIAS (if_rmap,
+       if_ipv6_rmap_cmd,
+       "route-map RMAP_NAME (in|out) IFNAME",
+       "Route map set\n"
+       "Route map name\n"
+       "Route map set for input filtering\n"
+       "Route map set for output filtering\n"
+       "Route map interface name\n")
+
+DEFUN (no_if_rmap,
+       no_if_rmap_cmd,
+       "no route-map ROUTEMAP_NAME (in|out) IFNAME",
+       NO_STR
+       "Route map unset\n"
+       "Route map name\n"
+       "Route map for input filtering\n"
+       "Route map for output filtering\n"
+       "Route map interface name\n")
+{
+  int ret;
+  enum if_rmap_type type;
+
+  if (strncmp (argv[1], "i", 1) == 0)
+    type = IF_RMAP_IN;
+  else if (strncmp (argv[1], "o", 1) == 0)
+    type = IF_RMAP_OUT;
+  else
+    {
+      vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = if_rmap_unset (argv[2], type, argv[0]);
+  if (! ret)
+    {
+      vty_out (vty, "route-map doesn't exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}      
+
+ALIAS (no_if_rmap,
+       no_if_ipv6_rmap_cmd,
+       "no route-map ROUTEMAP_NAME (in|out) IFNAME",
+       NO_STR
+       "Route map unset\n"
+       "Route map name\n"
+       "Route map for input filtering\n"
+       "Route map for output filtering\n"
+       "Route map interface name\n")
+
+/* Configuration write function. */
+int
+config_write_if_rmap (struct vty *vty)
+{
+  unsigned int i;
+  struct hash_backet *mp;
+  int write = 0;
+
+  for (i = 0; i < ifrmaphash->size; i++)
+    for (mp = ifrmaphash->index[i]; mp; mp = mp->next)
+      {
+       struct if_rmap *if_rmap;
+
+       if_rmap = mp->data;
+
+       if (if_rmap->routemap[IF_RMAP_IN])
+         {
+           vty_out (vty, " route-map %s in %s%s", 
+                    if_rmap->routemap[IF_RMAP_IN],
+                    if_rmap->ifname,
+                    VTY_NEWLINE);
+           write++;
+         }
+
+       if (if_rmap->routemap[IF_RMAP_OUT])
+         {
+           vty_out (vty, " route-map %s out %s%s", 
+                    if_rmap->routemap[IF_RMAP_OUT],
+                    if_rmap->ifname,
+                    VTY_NEWLINE);
+           write++;
+         }
+      }
+  return write;
+}
+
+void
+if_rmap_reset ()
+{
+  hash_clean (ifrmaphash, (void (*) (void *)) if_rmap_free);
+}
+
+void
+if_rmap_init (int node)
+{
+  ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp);
+  if (node == RIPNG_NODE) {
+    install_element (RIPNG_NODE, &if_ipv6_rmap_cmd);
+    install_element (RIPNG_NODE, &no_if_ipv6_rmap_cmd);
+  } else if (node == RIP_NODE) {
+    install_element (RIP_NODE, &if_rmap_cmd);
+    install_element (RIP_NODE, &no_if_rmap_cmd);
+  }
+}
diff --git a/lib/if_rmap.h b/lib/if_rmap.h
new file mode 100644 (file)
index 0000000..e6c2966
--- /dev/null
@@ -0,0 +1,47 @@
+/* route-map for interface.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_IF_RMAP_H
+#define _ZEBRA_IF_RMAP_H
+
+enum if_rmap_type
+{
+  IF_RMAP_IN,
+  IF_RMAP_OUT,
+  IF_RMAP_MAX
+};
+
+struct if_rmap
+{
+  /* Name of the interface. */
+  char *ifname;
+
+  char *routemap[IF_RMAP_MAX];
+};
+
+extern void if_rmap_init (int);
+extern void if_rmap_reset (void);
+extern void if_rmap_hook_add (void (*) (struct if_rmap *));
+extern void if_rmap_hook_delete (void (*) (struct if_rmap *));
+extern struct if_rmap *if_rmap_lookup (const char *);
+extern int config_write_if_rmap (struct vty *);
+
+#endif /* _ZEBRA_IF_RMAP_H */
diff --git a/lib/jhash.c b/lib/jhash.c
new file mode 100644 (file)
index 0000000..6154c34
--- /dev/null
@@ -0,0 +1,170 @@
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+ * hash(), hash2(), hash3, and mix() are externally useful functions.
+ * Routines to test the hash are included if SELF_TEST is defined.
+ * You can use this free for any purpose.  It has no warranty.
+ *
+ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are surely my fault.  -DaveM
+ */
+
+#include "zebra.h"
+#include "jhash.h"
+
+/* The golden ration: an arbitrary value */
+#define JHASH_GOLDEN_RATIO  0x9e3779b9
+
+/* NOTE: Arguments are modified. */
+#define __jhash_mix(a, b, c) \
+{ \
+  a -= b; a -= c; a ^= (c>>13); \
+  b -= c; b -= a; b ^= (a<<8); \
+  c -= a; c -= b; c ^= (b>>13); \
+  a -= b; a -= c; a ^= (c>>12);  \
+  b -= c; b -= a; b ^= (a<<16); \
+  c -= a; c -= b; c ^= (b>>5); \
+  a -= b; a -= c; a ^= (c>>3);  \
+  b -= c; b -= a; b ^= (a<<10); \
+  c -= a; c -= b; c ^= (b>>15); \
+}
+
+/* The most generic version, hashes an arbitrary sequence
+ * of bytes.  No alignment or length assumptions are made about
+ * the input key.
+ */
+u_int32_t
+jhash (const void *key, u_int32_t length, u_int32_t initval)
+{
+  u_int32_t a, b, c, len;
+  const u_int8_t *k = key;
+
+  len = length;
+  a = b = JHASH_GOLDEN_RATIO;
+  c = initval;
+
+  while (len >= 12)
+    {
+      a +=
+        (k[0] + ((u_int32_t) k[1] << 8) + ((u_int32_t) k[2] << 16) +
+         ((u_int32_t) k[3] << 24));
+      b +=
+        (k[4] + ((u_int32_t) k[5] << 8) + ((u_int32_t) k[6] << 16) +
+         ((u_int32_t) k[7] << 24));
+      c +=
+        (k[8] + ((u_int32_t) k[9] << 8) + ((u_int32_t) k[10] << 16) +
+         ((u_int32_t) k[11] << 24));
+
+      __jhash_mix (a, b, c);
+
+      k += 12;
+      len -= 12;
+    }
+
+  c += length;
+  switch (len)
+    {
+    case 11:
+      c += ((u_int32_t) k[10] << 24);
+    case 10:
+      c += ((u_int32_t) k[9] << 16);
+    case 9:
+      c += ((u_int32_t) k[8] << 8);
+    case 8:
+      b += ((u_int32_t) k[7] << 24);
+    case 7:
+      b += ((u_int32_t) k[6] << 16);
+    case 6:
+      b += ((u_int32_t) k[5] << 8);
+    case 5:
+      b += k[4];
+    case 4:
+      a += ((u_int32_t) k[3] << 24);
+    case 3:
+      a += ((u_int32_t) k[2] << 16);
+    case 2:
+      a += ((u_int32_t) k[1] << 8);
+    case 1:
+      a += k[0];
+    };
+
+  __jhash_mix (a, b, c);
+
+  return c;
+}
+
+/* A special optimized version that handles 1 or more of u_int32_ts.
+ * The length parameter here is the number of u_int32_ts in the key.
+ */
+u_int32_t
+jhash2 (const u_int32_t *k, u_int32_t length, u_int32_t initval)
+{
+  u_int32_t a, b, c, len;
+
+  a = b = JHASH_GOLDEN_RATIO;
+  c = initval;
+  len = length;
+
+  while (len >= 3)
+    {
+      a += k[0];
+      b += k[1];
+      c += k[2];
+      __jhash_mix (a, b, c);
+      k += 3;
+      len -= 3;
+    }
+
+  c += length * 4;
+
+  switch (len)
+    {
+    case 2:
+      b += k[1];
+    case 1:
+      a += k[0];
+    };
+
+  __jhash_mix (a, b, c);
+
+  return c;
+}
+
+
+/* A special ultra-optimized versions that knows they are hashing exactly
+ * 3, 2 or 1 word(s).
+ *
+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
+ *       done at the end is not done here.
+ */
+u_int32_t
+jhash_3words (u_int32_t a, u_int32_t b, u_int32_t c, u_int32_t initval)
+{
+  a += JHASH_GOLDEN_RATIO;
+  b += JHASH_GOLDEN_RATIO;
+  c += initval;
+
+  __jhash_mix (a, b, c);
+
+  return c;
+}
+
+u_int32_t
+jhash_2words (u_int32_t a, u_int32_t b, u_int32_t initval)
+{
+  return jhash_3words (a, b, 0, initval);
+}
+
+u_int32_t
+jhash_1word (u_int32_t a, u_int32_t initval)
+{
+  return jhash_3words (a, 0, 0, initval);
+}
diff --git a/lib/jhash.h b/lib/jhash.h
new file mode 100644 (file)
index 0000000..985ac94
--- /dev/null
@@ -0,0 +1,44 @@
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+ * hash(), hash2(), hash3, and mix() are externally useful functions.
+ * Routines to test the hash are included if SELF_TEST is defined.
+ * You can use this free for any purpose.  It has no warranty.
+ *
+ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are surely my fault.  -DaveM
+ */
+
+#ifndef _QUAGGA_JHASH_H
+#define _QUAGGA_JHASH_H
+
+/* The most generic version, hashes an arbitrary sequence
+ * of bytes.  No alignment or length assumptions are made about
+ * the input key.
+ */
+extern u_int32_t jhash(const void *key, u_int32_t length, u_int32_t initval);
+
+/* A special optimized version that handles 1 or more of u_int32_ts.
+ * The length parameter here is the number of u_int32_ts in the key.
+ */
+extern u_int32_t jhash2(const u_int32_t *k, u_int32_t length, u_int32_t initval);
+
+/* A special ultra-optimized versions that knows they are hashing exactly
+ * 3, 2 or 1 word(s).
+ *
+ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
+ *       done at the end is not done here.
+ */
+extern u_int32_t jhash_3words(u_int32_t a, u_int32_t b, u_int32_t c, u_int32_t initval);
+extern u_int32_t jhash_2words(u_int32_t a, u_int32_t b, u_int32_t initval);
+extern u_int32_t jhash_1word(u_int32_t a, u_int32_t initval);
+
+#endif /* _QUAGGA_JHASH_H */
diff --git a/lib/keychain.c b/lib/keychain.c
new file mode 100644 (file)
index 0000000..8a2fdd2
--- /dev/null
@@ -0,0 +1,988 @@
+/* key-chain for authentication.
+   Copyright (C) 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "memory.h"
+#include "linklist.h"
+#include "keychain.h"
+
+/* Master list of key chain. */
+struct list *keychain_list;
+
+static struct keychain *
+keychain_new (void)
+{
+  return XCALLOC (MTYPE_KEYCHAIN, sizeof (struct keychain));
+}
+
+static void
+keychain_free (struct keychain *keychain)
+{
+  XFREE (MTYPE_KEYCHAIN, keychain);
+}
+
+static struct key *
+key_new (void)
+{
+  return XCALLOC (MTYPE_KEY, sizeof (struct key));
+}
+
+static void
+key_free (struct key *key)
+{
+  XFREE (MTYPE_KEY, key);
+}
+
+struct keychain *
+keychain_lookup (const char *name)
+{
+  struct listnode *node;
+  struct keychain *keychain;
+
+  if (name == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain))
+    {
+      if (strcmp (keychain->name, name) == 0)
+       return keychain;
+    }
+  return NULL;
+}
+
+static int
+key_cmp_func (void *arg1, void *arg2)
+{
+  const struct key *k1 = arg1;
+  const struct key *k2 = arg2;
+  
+  if (k1->index > k2->index)
+    return 1;
+  if (k1->index < k2->index)
+    return -1;
+  return 0;
+}
+
+static void
+key_delete_func (struct key *key)
+{
+  if (key->string)
+    free (key->string);
+  key_free (key);
+}
+
+static struct keychain *
+keychain_get (const char *name)
+{
+  struct keychain *keychain;
+
+  keychain = keychain_lookup (name);
+
+  if (keychain)
+    return keychain;
+
+  keychain = keychain_new ();
+  keychain->name = strdup (name);
+  keychain->key = list_new ();
+  keychain->key->cmp = (int (*)(void *, void *)) key_cmp_func;
+  keychain->key->del = (void (*)(void *)) key_delete_func;
+  listnode_add (keychain_list, keychain);
+
+  return keychain;
+}
+
+static void
+keychain_delete (struct keychain *keychain)
+{
+  if (keychain->name)
+    free (keychain->name);
+
+  list_delete (keychain->key);
+  listnode_delete (keychain_list, keychain);
+  keychain_free (keychain);
+}
+
+static struct key *
+key_lookup (const struct keychain *keychain, u_int32_t index)
+{
+  struct listnode *node;
+  struct key *key;
+
+  for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key))
+    {
+      if (key->index == index)
+       return key;
+    }
+  return NULL;
+}
+
+struct key *
+key_lookup_for_accept (const struct keychain *keychain, u_int32_t index)
+{
+  struct listnode *node;
+  struct key *key;
+  time_t now;
+
+  now = time (NULL);
+
+  for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key))
+    {
+      if (key->index >= index)
+       {
+         if (key->accept.start == 0)
+           return key;
+
+         if (key->accept.start <= now)
+           if (key->accept.end >= now || key->accept.end == -1)
+             return key;
+       }
+    }
+  return NULL;
+}
+
+struct key *
+key_match_for_accept (const struct keychain *keychain, const char *auth_str)
+{
+  struct listnode *node;
+  struct key *key;
+  time_t now;
+
+  now = time (NULL);
+
+  for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key))
+    {
+      if (key->accept.start == 0 ||
+         (key->accept.start <= now &&
+          (key->accept.end >= now || key->accept.end == -1)))
+       if (strncmp (key->string, auth_str, 16) == 0)
+         return key;
+    }
+  return NULL;
+}
+
+struct key *
+key_lookup_for_send (const struct keychain *keychain)
+{
+  struct listnode *node;
+  struct key *key;
+  time_t now;
+
+  now = time (NULL);
+
+  for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key))
+    {
+      if (key->send.start == 0)
+       return key;
+
+      if (key->send.start <= now)
+       if (key->send.end >= now || key->send.end == -1)
+         return key;
+    }
+  return NULL;
+}
+
+static struct key *
+key_get (const struct keychain *keychain, u_int32_t index)
+{
+  struct key *key;
+
+  key = key_lookup (keychain, index);
+
+  if (key)
+    return key;
+
+  key = key_new ();
+  key->index = index;
+  listnode_add_sort (keychain->key, key);
+
+  return key;
+}
+
+static void
+key_delete (struct keychain *keychain, struct key *key)
+{
+  listnode_delete (keychain->key, key);
+
+  if (key->string)
+    free (key->string);
+  key_free (key);
+}
+
+DEFUN (key_chain,
+       key_chain_cmd,
+       "key chain WORD",
+       "Authentication key management\n"
+       "Key-chain management\n"
+       "Key-chain name\n")
+{
+  struct keychain *keychain;
+
+  keychain = keychain_get (argv[0]);
+  vty->index = keychain;
+  vty->node = KEYCHAIN_NODE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_key_chain,
+       no_key_chain_cmd,
+       "no key chain WORD",
+       NO_STR
+       "Authentication key management\n"
+       "Key-chain management\n"
+       "Key-chain name\n")
+{
+  struct keychain *keychain;
+
+  keychain = keychain_lookup (argv[0]);
+
+  if (! keychain)
+    {
+      vty_out (vty, "Can't find keychain %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  keychain_delete (keychain);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (key,
+       key_cmd,
+       "key <0-2147483647>",
+       "Configure a key\n"
+       "Key identifier number\n")
+{
+  struct keychain *keychain;
+  struct key *key;
+  u_int32_t index;
+
+  keychain = vty->index;
+
+  VTY_GET_INTEGER ("key identifier", index, argv[0]);
+  key = key_get (keychain, index);
+  vty->index_sub = key;
+  vty->node = KEYCHAIN_KEY_NODE;
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_key,
+       no_key_cmd,
+       "no key <0-2147483647>",
+       NO_STR
+       "Delete a key\n"
+       "Key identifier number\n")
+{
+  struct keychain *keychain;
+  struct key *key;
+  u_int32_t index;
+  
+  keychain = vty->index;
+
+  VTY_GET_INTEGER ("key identifier", index, argv[0]);
+  key = key_lookup (keychain, index);
+  if (! key)
+    {
+      vty_out (vty, "Can't find key %d%s", index, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  key_delete (keychain, key);
+
+  vty->node = KEYCHAIN_NODE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (key_string,
+       key_string_cmd,
+       "key-string LINE",
+       "Set key string\n"
+       "The key\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  if (key->string)
+    free (key->string);
+  key->string = strdup (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_key_string,
+       no_key_string_cmd,
+       "no key-string [LINE]",
+       NO_STR
+       "Unset key string\n"
+       "The key\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  if (key->string)
+    {
+      free (key->string);
+      key->string = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Convert HH:MM:SS MON DAY YEAR to time_t value.  -1 is returned when
+   given string is malformed. */
+static time_t 
+key_str2time (const char *time_str, const char *day_str, const char *month_str,
+             const char *year_str)
+{
+  int i = 0;
+  char *colon;
+  struct tm tm;
+  time_t time;
+  unsigned int sec, min, hour;
+  unsigned int day, month, year;
+
+  const char *month_name[] = 
+  {
+    "January",
+    "February",
+    "March",
+    "April",
+    "May",
+    "June",
+    "July",
+    "August",
+    "September",
+    "October",
+    "November",
+    "December",
+    NULL
+  };
+
+#define _GET_LONG_RANGE(V,STR,MMCOND) \
+{ \
+  unsigned long tmpl; \
+  char *endptr = NULL; \
+  tmpl = strtoul ((STR), &endptr, 10); \
+  if (*endptr != '\0' || tmpl == ULONG_MAX) \
+    return -1; \
+  if (MMCOND) \
+    return -1; \
+  (V) = tmpl; \
+}
+#define GET_LONG_RANGE(V,STR,MIN,MAX) \
+        _GET_LONG_RANGE(V,STR,tmpl < (MIN) || tmpl > (MAX))
+#define GET_LONG_RANGE0(V,STR,MAX) \
+        _GET_LONG_RANGE(V,STR,tmpl > (MAX))
+
+  /* Check hour field of time_str. */
+  colon = strchr (time_str, ':');
+  if (colon == NULL)
+    return -1;
+  *colon = '\0';
+
+  /* Hour must be between 0 and 23. */
+  GET_LONG_RANGE0 (hour, time_str, 23);
+
+  /* Check min field of time_str. */
+  time_str = colon + 1;
+  colon = strchr (time_str, ':');
+  if (*time_str == '\0' || colon == NULL)
+    return -1;
+  *colon = '\0';
+
+  /* Min must be between 0 and 59. */
+  GET_LONG_RANGE0 (min, time_str, 59);
+
+  /* Check sec field of time_str. */
+  time_str = colon + 1;
+  if (*time_str == '\0')
+    return -1;
+  
+  /* Sec must be between 0 and 59. */
+  GET_LONG_RANGE0 (sec, time_str, 59);
+  
+  /* Check day_str.  Day must be <1-31>. */
+  GET_LONG_RANGE (day, day_str, 1, 31);
+
+  /* Check month_str.  Month must match month_name. */
+  month = 0;
+  if (strlen (month_str) >= 3)
+    for (i = 0; month_name[i]; i++)
+      if (strncmp (month_str, month_name[i], strlen (month_str)) == 0)
+       {
+         month = i;
+         break;
+       }
+  if (! month_name[i])
+    return -1;
+
+  /* Check year_str.  Year must be <1993-2035>. */
+  GET_LONG_RANGE (year, year_str, 1993, 2035);
+  
+  memset (&tm, 0, sizeof (struct tm));
+  tm.tm_sec = sec;
+  tm.tm_min = min;
+  tm.tm_hour = hour;
+  tm.tm_mon = month;
+  tm.tm_mday = day;
+  tm.tm_year = year - 1900;
+    
+  time = mktime (&tm);
+  
+  return time;
+#undef GET_LONG_RANGE
+}
+
+static int
+key_lifetime_set (struct vty *vty, struct key_range *krange,
+                 const char *stime_str, const char *sday_str,
+                 const char *smonth_str, const char *syear_str,
+                 const char *etime_str, const char *eday_str,
+                 const char *emonth_str, const char *eyear_str)
+{
+  time_t time_start;
+  time_t time_end;
+  
+  time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
+  if (time_start < 0)
+    {
+      vty_out (vty, "Malformed time value%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  time_end = key_str2time (etime_str, eday_str, emonth_str, eyear_str);
+
+  if (time_end < 0)
+    {
+      vty_out (vty, "Malformed time value%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (time_end <= time_start)
+    {
+      vty_out (vty, "Expire time is not later than start time%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  krange->start = time_start;
+  krange->end = time_end;
+
+  return CMD_SUCCESS;
+}
+
+static int
+key_lifetime_duration_set (struct vty *vty, struct key_range *krange,
+                          const char *stime_str, const char *sday_str,
+                          const char *smonth_str, const char *syear_str,
+                          const char *duration_str)
+{
+  time_t time_start;
+  u_int32_t duration;
+    
+  time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
+  if (time_start < 0)
+    {
+      vty_out (vty, "Malformed time value%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  krange->start = time_start;
+
+  VTY_GET_INTEGER ("duration", duration, duration_str);
+  krange->duration = 1;
+  krange->end = time_start + duration;
+
+  return CMD_SUCCESS;
+}
+
+static int
+key_lifetime_infinite_set (struct vty *vty, struct key_range *krange,
+                          const char *stime_str, const char *sday_str,
+                          const char *smonth_str, const char *syear_str)
+{
+  time_t time_start;
+    
+  time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
+  if (time_start < 0)
+    {
+      vty_out (vty, "Malformed time value%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  krange->start = time_start;
+
+  krange->end = -1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (accept_lifetime_day_month_day_month,
+       accept_lifetime_day_month_day_month_cmd,
+       "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Day of th month to expire\n"
+       "Month of the year to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2],
+                          argv[3], argv[4], argv[5], argv[6], argv[7]);
+}
+
+DEFUN (accept_lifetime_day_month_month_day,
+       accept_lifetime_day_month_month_day_cmd,
+       "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Month of the year to expire\n"
+       "Day of th month to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2],
+                          argv[3], argv[4], argv[6], argv[5], argv[7]);
+}
+
+DEFUN (accept_lifetime_month_day_day_month,
+       accept_lifetime_month_day_day_month_cmd,
+       "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Day of th month to expire\n"
+       "Month of the year to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1],
+                          argv[3], argv[4], argv[5], argv[6], argv[7]);
+}
+
+DEFUN (accept_lifetime_month_day_month_day,
+       accept_lifetime_month_day_month_day_cmd,
+       "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Month of the year to expire\n"
+       "Day of th month to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1],
+                          argv[3], argv[4], argv[6], argv[5], argv[7]);
+}
+
+DEFUN (accept_lifetime_infinite_day_month,
+       accept_lifetime_infinite_day_month_cmd,
+       "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Never expires")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[1],
+                                   argv[2], argv[3]);
+}
+
+DEFUN (accept_lifetime_infinite_month_day,
+       accept_lifetime_infinite_month_day_cmd,
+       "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Never expires")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[2],
+                                   argv[1], argv[3]);
+}
+
+DEFUN (accept_lifetime_duration_day_month,
+       accept_lifetime_duration_day_month_cmd,
+       "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Duration of the key\n"
+       "Duration seconds\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[1],
+                                   argv[2], argv[3], argv[4]);
+}
+
+DEFUN (accept_lifetime_duration_month_day,
+       accept_lifetime_duration_month_day_cmd,
+       "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>",
+       "Set accept lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Duration of the key\n"
+       "Duration seconds\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2],
+                                   argv[1], argv[3], argv[4]);
+}
+
+DEFUN (send_lifetime_day_month_day_month,
+       send_lifetime_day_month_day_month_cmd,
+       "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Day of th month to expire\n"
+       "Month of the year to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3],
+                          argv[4], argv[5], argv[6], argv[7]);
+}
+
+DEFUN (send_lifetime_day_month_month_day,
+       send_lifetime_day_month_month_day_cmd,
+       "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Month of the year to expire\n"
+       "Day of th month to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3],
+                          argv[4], argv[6], argv[5], argv[7]);
+}
+
+DEFUN (send_lifetime_month_day_day_month,
+       send_lifetime_month_day_day_month_cmd,
+       "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Day of th month to expire\n"
+       "Month of the year to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3],
+                          argv[4], argv[5], argv[6], argv[7]);
+}
+
+DEFUN (send_lifetime_month_day_month_day,
+       send_lifetime_month_day_month_day_cmd,
+       "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Time to expire\n"
+       "Month of the year to expire\n"
+       "Day of th month to expire\n"
+       "Year to expire\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3],
+                          argv[4], argv[6], argv[5], argv[7]);
+}
+
+DEFUN (send_lifetime_infinite_day_month,
+       send_lifetime_infinite_day_month_cmd,
+       "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Never expires")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[1], argv[2],
+                                   argv[3]);
+}
+
+DEFUN (send_lifetime_infinite_month_day,
+       send_lifetime_infinite_month_day_cmd,
+       "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Never expires")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[2], argv[1],
+                                   argv[3]);
+}
+
+DEFUN (send_lifetime_duration_day_month,
+       send_lifetime_duration_day_month_cmd,
+       "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Day of th month to start\n"
+       "Month of the year to start\n"
+       "Year to start\n"
+       "Duration of the key\n"
+       "Duration seconds\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_duration_set (vty, &key->send, argv[0], argv[1], argv[2],
+                                   argv[3], argv[4]);
+}
+
+DEFUN (send_lifetime_duration_month_day,
+       send_lifetime_duration_month_day_cmd,
+       "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>",
+       "Set send lifetime of the key\n"
+       "Time to start\n"
+       "Month of the year to start\n"
+       "Day of th month to start\n"
+       "Year to start\n"
+       "Duration of the key\n"
+       "Duration seconds\n")
+{
+  struct key *key;
+
+  key = vty->index_sub;
+
+  return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1],
+                                   argv[3], argv[4]);
+}
+
+static struct cmd_node keychain_node =
+{
+  KEYCHAIN_NODE,
+  "%s(config-keychain)# ",
+  1
+};
+
+static struct cmd_node keychain_key_node =
+{
+  KEYCHAIN_KEY_NODE,
+  "%s(config-keychain-key)# ",
+  1
+};
+
+static int
+keychain_strftime (char *buf, int bufsiz, time_t *time)
+{
+  struct tm *tm;
+  size_t len;
+
+  tm = localtime (time);
+
+  len = strftime (buf, bufsiz, "%T %b %d %Y", tm);
+
+  return len;
+}
+
+static int
+keychain_config_write (struct vty *vty)
+{
+  struct keychain *keychain;
+  struct key *key;
+  struct listnode *node;
+  struct listnode *knode;
+  char buf[BUFSIZ];
+
+  for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain))
+    {
+      vty_out (vty, "key chain %s%s", keychain->name, VTY_NEWLINE);
+      
+      for (ALL_LIST_ELEMENTS_RO (keychain->key, knode, key))
+       {
+         vty_out (vty, " key %d%s", key->index, VTY_NEWLINE);
+
+         if (key->string)
+           vty_out (vty, "  key-string %s%s", key->string, VTY_NEWLINE);
+
+         if (key->accept.start)
+           {
+             keychain_strftime (buf, BUFSIZ, &key->accept.start);
+             vty_out (vty, "  accept-lifetime %s", buf);
+
+             if (key->accept.end == -1)
+               vty_out (vty, " infinite");
+             else if (key->accept.duration)
+               vty_out (vty, " duration %ld",
+                        (long)(key->accept.end - key->accept.start));
+             else
+               {
+                 keychain_strftime (buf, BUFSIZ, &key->accept.end);
+                 vty_out (vty, " %s", buf);
+               }
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+
+         if (key->send.start)
+           {
+             keychain_strftime (buf, BUFSIZ, &key->send.start);
+             vty_out (vty, "  send-lifetime %s", buf);
+
+             if (key->send.end == -1)
+               vty_out (vty, " infinite");
+             else if (key->send.duration)
+               vty_out (vty, " duration %ld", (long)(key->send.end - key->send.start));
+             else
+               {
+                 keychain_strftime (buf, BUFSIZ, &key->send.end);
+                 vty_out (vty, " %s", buf);
+               }
+             vty_out (vty, "%s", VTY_NEWLINE);
+           }
+       }
+      vty_out (vty, "!%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+void
+keychain_init ()
+{
+  keychain_list = list_new ();
+
+  install_node (&keychain_node, keychain_config_write);
+  install_node (&keychain_key_node, NULL);
+
+  install_default (KEYCHAIN_NODE);
+  install_default (KEYCHAIN_KEY_NODE);
+
+  install_element (CONFIG_NODE, &key_chain_cmd);
+  install_element (CONFIG_NODE, &no_key_chain_cmd);
+  install_element (KEYCHAIN_NODE, &key_cmd);
+  install_element (KEYCHAIN_NODE, &no_key_cmd);
+
+  install_element (KEYCHAIN_NODE, &key_chain_cmd);
+  install_element (KEYCHAIN_NODE, &no_key_chain_cmd);
+
+  install_element (KEYCHAIN_KEY_NODE, &key_string_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &no_key_string_cmd);
+
+  install_element (KEYCHAIN_KEY_NODE, &key_chain_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &no_key_chain_cmd);
+
+  install_element (KEYCHAIN_KEY_NODE, &key_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &no_key_cmd);
+
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_month_day_cmd);
+
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_month_day_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_day_month_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_month_day_cmd);
+}
diff --git a/lib/keychain.h b/lib/keychain.h
new file mode 100644 (file)
index 0000000..f962864
--- /dev/null
@@ -0,0 +1,56 @@
+/* key-chain for authentication.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_KEYCHAIN_H
+#define _ZEBRA_KEYCHAIN_H
+
+struct keychain
+{
+  char *name;
+
+  struct list *key;
+};
+
+struct key_range
+{
+  time_t start;
+  time_t end;
+
+  u_char duration;
+};
+
+struct key
+{
+  u_int32_t index;
+
+  char *string;
+
+  struct key_range send;
+  struct key_range accept;
+};
+
+extern void keychain_init (void);
+extern struct keychain *keychain_lookup (const char *);
+extern struct key *key_lookup_for_accept (const struct keychain *, u_int32_t);
+extern struct key *key_match_for_accept (const struct keychain *, const char *);
+extern struct key *key_lookup_for_send (const struct keychain *);
+
+#endif /* _ZEBRA_KEYCHAIN_H */
diff --git a/lib/libospf.h b/lib/libospf.h
new file mode 100644 (file)
index 0000000..9265ca5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Defines and structures common to OSPFv2 and OSPFv3
+ * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _LIBOSPFD_H
+#define _LIBOSPFD_H
+
+/* IP precedence. */
+#ifndef IPTOS_PREC_INTERNETCONTROL
+#define IPTOS_PREC_INTERNETCONTROL     0xC0
+#endif /* IPTOS_PREC_INTERNETCONTROL */
+
+/* Default protocol, port number. */
+#ifndef IPPROTO_OSPFIGP
+#define IPPROTO_OSPFIGP         89
+#endif /* IPPROTO_OSPFIGP */
+
+/* Architectual Constants */
+#ifdef DEBUG
+#define OSPF_LS_REFRESH_TIME                    60
+#else
+#define OSPF_LS_REFRESH_TIME                  1800
+#endif
+#define OSPF_MIN_LS_INTERVAL                  5000  /* msec */
+#define OSPF_MIN_LS_ARRIVAL                   1000  /* msec */
+#define OSPF_LSA_INITIAL_AGE                     0     /* useful for debug */
+#define OSPF_LSA_MAXAGE                       3600
+#define OSPF_CHECK_AGE                         300
+#define OSPF_LSA_MAXAGE_DIFF                   900
+#define OSPF_LS_INFINITY                  0xffffff
+#define OSPF_DEFAULT_DESTINATION        0x00000000      /* 0.0.0.0 */
+#define OSPF_INITIAL_SEQUENCE_NUMBER    0x80000001U
+#define OSPF_MAX_SEQUENCE_NUMBER        0x7fffffffU
+
+/* OSPF Interface Types */
+#define OSPF_IFTYPE_NONE               0
+#define OSPF_IFTYPE_POINTOPOINT                1
+#define OSPF_IFTYPE_BROADCAST          2
+#define OSPF_IFTYPE_NBMA               3
+#define OSPF_IFTYPE_POINTOMULTIPOINT   4
+#define OSPF_IFTYPE_VIRTUALLINK                5
+#define OSPF_IFTYPE_LOOPBACK            6
+#define OSPF_IFTYPE_MAX                        7
+
+/* OSPF interface default values. */
+#define OSPF_OUTPUT_COST_DEFAULT           10
+#define OSPF_OUTPUT_COST_INFINITE         UINT16_MAX
+#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT  40
+#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL   1
+#define OSPF_HELLO_INTERVAL_DEFAULT        10
+#define OSPF_ROUTER_PRIORITY_DEFAULT        1
+#define OSPF_RETRANSMIT_INTERVAL_DEFAULT    5
+#define OSPF_TRANSMIT_DELAY_DEFAULT         1
+#define OSPF_DEFAULT_BANDWIDTH          10000  /* Kbps */
+
+#define OSPF_DEFAULT_REF_BANDWIDTH     100000  /* Kbps */
+
+#define OSPF_POLL_INTERVAL_DEFAULT         60
+#define OSPF_NEIGHBOR_PRIORITY_DEFAULT      0
+
+#define OSPF_MTU_IGNORE_DEFAULT             0
+#define OSPF_FAST_HELLO_DEFAULT             0
+
+#define OSPF_AREA_BACKBONE              0x00000000      /* 0.0.0.0 */
+
+/* SPF Throttling timer values. */
+#define OSPF_SPF_DELAY_DEFAULT              0
+#define OSPF_SPF_HOLDTIME_DEFAULT           50
+#define OSPF_SPF_MAX_HOLDTIME_DEFAULT      5000
+
+#define OSPF_LSA_MAXAGE_CHECK_INTERVAL         30
+#define OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT   60
+
+#endif /* _LIBOSPFD_H */
diff --git a/lib/linklist.c b/lib/linklist.c
new file mode 100644 (file)
index 0000000..8b6a852
--- /dev/null
@@ -0,0 +1,361 @@
+/* Generic linked list routine.
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "memory.h"
+
+/* Allocate new list. */
+struct list *
+list_new (void)
+{
+  return XCALLOC (MTYPE_LINK_LIST, sizeof (struct list));
+}
+
+/* Free list. */
+void
+list_free (struct list *l)
+{
+  XFREE (MTYPE_LINK_LIST, l);
+}
+
+/* Allocate new listnode.  Internal use only. */
+static struct listnode *
+listnode_new (void)
+{
+  return XCALLOC (MTYPE_LINK_NODE, sizeof (struct listnode));
+}
+
+/* Free listnode. */
+static void
+listnode_free (struct listnode *node)
+{
+  XFREE (MTYPE_LINK_NODE, node);
+}
+
+/* Add new data to the list. */
+void
+listnode_add (struct list *list, void *val)
+{
+  struct listnode *node;
+  
+  assert (val != NULL);
+  
+  node = listnode_new ();
+
+  node->prev = list->tail;
+  node->data = val;
+
+  if (list->head == NULL)
+    list->head = node;
+  else
+    list->tail->next = node;
+  list->tail = node;
+
+  list->count++;
+}
+
+/*
+ * Add a node to the list.  If the list was sorted according to the
+ * cmp function, insert a new node with the given val such that the
+ * list remains sorted.  The new node is always inserted; there is no
+ * notion of omitting duplicates.
+ */
+void
+listnode_add_sort (struct list *list, void *val)
+{
+  struct listnode *n;
+  struct listnode *new;
+  
+  assert (val != NULL);
+  
+  new = listnode_new ();
+  new->data = val;
+
+  if (list->cmp)
+    {
+      for (n = list->head; n; n = n->next)
+       {
+         if ((*list->cmp) (val, n->data) < 0)
+           {       
+             new->next = n;
+             new->prev = n->prev;
+
+             if (n->prev)
+               n->prev->next = new;
+             else
+               list->head = new;
+             n->prev = new;
+             list->count++;
+             return;
+           }
+       }
+    }
+
+  new->prev = list->tail;
+
+  if (list->tail)
+    list->tail->next = new;
+  else
+    list->head = new;
+
+  list->tail = new;
+  list->count++;
+}
+
+void
+listnode_add_after (struct list *list, struct listnode *pp, void *val)
+{
+  struct listnode *nn;
+  
+  assert (val != NULL);
+  
+  nn = listnode_new ();
+  nn->data = val;
+
+  if (pp == NULL)
+    {
+      if (list->head)
+       list->head->prev = nn;
+      else
+       list->tail = nn;
+
+      nn->next = list->head;
+      nn->prev = pp;
+
+      list->head = nn;
+    }
+  else
+    {
+      if (pp->next)
+       pp->next->prev = nn;
+      else
+       list->tail = nn;
+
+      nn->next = pp->next;
+      nn->prev = pp;
+
+      pp->next = nn;
+    }
+  list->count++;
+}
+
+struct listnode *
+listnode_add_before (struct list *list, struct listnode *pp, void *val)
+{
+  struct listnode *nn;
+
+  assert (val != NULL);
+
+  nn = listnode_new ();
+  nn->data = val;
+
+  if (pp == NULL)
+    {
+      if (list->tail)
+        list->tail->next = nn;
+      else
+        list->head = nn;
+
+      nn->prev = list->tail;
+      nn->next = pp;
+
+      list->tail = nn;
+    }
+  else
+    {
+      if (pp->prev)
+       pp->prev->next = nn;
+      else
+       list->head = nn;
+
+      nn->prev = pp->prev;
+      nn->next = pp;
+
+      pp->prev = nn;
+    }
+  list->count++;
+  return nn;
+}
+
+/* Move given listnode to tail of the list */
+void
+listnode_move_to_tail (struct list *l, struct listnode *n)
+{
+  LISTNODE_DETACH(l,n);
+  LISTNODE_ATTACH(l,n);
+}
+
+/* Delete specific date pointer from the list. */
+void
+listnode_delete (struct list *list, void *val)
+{
+  struct listnode *node;
+
+  assert(list);
+  for (node = list->head; node; node = node->next)
+    {
+      if (node->data == val)
+       {
+         if (node->prev)
+           node->prev->next = node->next;
+         else
+           list->head = node->next;
+
+         if (node->next)
+           node->next->prev = node->prev;
+         else
+           list->tail = node->prev;
+
+         list->count--;
+         listnode_free (node);
+         return;
+       }
+    }
+}
+
+/* Return first node's data if it is there.  */
+void *
+listnode_head (struct list *list)
+{
+  struct listnode *node;
+
+  assert(list);
+  node = list->head;
+
+  if (node)
+    return node->data;
+  return NULL;
+}
+
+/* Delete all listnode from the list. */
+void
+list_delete_all_node (struct list *list)
+{
+  struct listnode *node;
+  struct listnode *next;
+
+  assert(list);
+  for (node = list->head; node; node = next)
+    {
+      next = node->next;
+      if (list->del)
+       (*list->del) (node->data);
+      listnode_free (node);
+    }
+  list->head = list->tail = NULL;
+  list->count = 0;
+}
+
+/* Delete all listnode then free list itself. */
+void
+list_delete (struct list *list)
+{
+  assert(list);
+  list_delete_all_node (list);
+  list_free (list);
+}
+
+/* Lookup the node which has given data. */
+struct listnode *
+listnode_lookup (struct list *list, void *data)
+{
+  struct listnode *node;
+
+  assert(list);
+  for (node = listhead(list); node; node = listnextnode (node))
+    if (data == listgetdata (node))
+      return node;
+  return NULL;
+}
+
+/* Delete the node from list.  For ospfd and ospf6d. */
+void
+list_delete_node (struct list *list, struct listnode *node)
+{
+  if (node->prev)
+    node->prev->next = node->next;
+  else
+    list->head = node->next;
+  if (node->next)
+    node->next->prev = node->prev;
+  else
+    list->tail = node->prev;
+  list->count--;
+  listnode_free (node);
+}
+
+/* ospf_spf.c */
+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;
+
+  if (current->prev == NULL)
+    list->head = node;
+  else
+    current->prev->next = node;
+
+  node->prev = current->prev;
+  current->prev = node;
+
+  list->count++;
+}
+
+/* ospf_spf.c */
+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;
+
+  if (current->next == NULL)
+    list->tail = node;
+  else
+    current->next->prev = node;
+
+  node->next = current->next;
+  current->next = node;
+
+  list->count++;
+}
+
+/* ospf_spf.c */
+void
+list_add_list (struct list *l, struct list *m)
+{
+  struct listnode *n;
+
+  for (n = listhead (m); n; n = listnextnode (n))
+    listnode_add (l, n->data);
+}
diff --git a/lib/linklist.h b/lib/linklist.h
new file mode 100644 (file)
index 0000000..96aaf43
--- /dev/null
@@ -0,0 +1,151 @@
+/* Generic linked list
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_LINKLIST_H
+#define _ZEBRA_LINKLIST_H
+
+/* listnodes must always contain data to be valid. Adding an empty node
+ * to a list is invalid
+ */
+struct listnode 
+{
+  struct listnode *next;
+  struct listnode *prev;
+  
+  /* private member, use getdata() to retrieve, do not access directly */
+  void *data;
+};
+
+struct list 
+{
+  struct listnode *head;
+  struct listnode *tail;
+
+  /* invariant: count is the number of listnodes in the list */
+  unsigned int count;
+
+  /*
+   * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2.
+   * Used as definition of sorted for listnode_add_sort
+   */
+  int (*cmp) (void *val1, void *val2);
+
+  /* callback to free user-owned data when listnode is deleted. supplying
+   * this callback is very much encouraged!
+   */
+  void (*del) (void *val);
+};
+
+#define listnextnode(X) ((X) ? ((X)->next) : NULL)
+#define listhead(X) ((X) ? ((X)->head) : NULL)
+#define listtail(X) ((X) ? ((X)->tail) : NULL)
+#define listcount(X) ((X)->count)
+#define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL)
+#define listgetdata(X) (assert((X)->data != NULL), (X)->data)
+
+/* Prototypes. */
+extern struct list *list_new(void); /* encouraged: set list.del callback on new lists */
+extern void list_free (struct list *);
+
+extern void listnode_add (struct list *, void *);
+extern void listnode_add_sort (struct list *, void *);
+extern void listnode_add_after (struct list *, struct listnode *, void *);
+extern struct listnode *listnode_add_before (struct list *, struct listnode *, void *);
+extern void listnode_move_to_tail (struct list *, struct listnode *);
+extern void listnode_delete (struct list *, void *);
+extern struct listnode *listnode_lookup (struct list *, void *);
+extern void *listnode_head (struct list *);
+
+extern void list_delete (struct list *);
+extern void list_delete_all_node (struct list *);
+
+/* For ospfd and ospf6d. */
+extern void list_delete_node (struct list *, struct listnode *);
+
+/* For ospf_spf.c */
+extern void list_add_node_prev (struct list *, struct listnode *, void *);
+extern void list_add_node_next (struct list *, struct listnode *, void *);
+extern void list_add_list (struct list *, struct list *);
+
+/* List iteration macro. 
+ * Usage: for (ALL_LIST_ELEMENTS (...) { ... }
+ * It is safe to delete the listnode using this macro.
+ */
+#define ALL_LIST_ELEMENTS(list,node,nextnode,data) \
+  (node) = listhead(list), ((data) = NULL); \
+  (node) != NULL && \
+    ((data) = listgetdata(node),(nextnode) = node->next, 1); \
+  (node) = (nextnode), ((data) = NULL)
+
+/* read-only list iteration macro.
+ * Usage: as per ALL_LIST_ELEMENTS, but not safe to delete the listnode Only
+ * use this macro when it is *immediately obvious* the listnode is not
+ * deleted in the body of the loop. Does not have forward-reference overhead
+ * of previous macro.
+ */
+#define ALL_LIST_ELEMENTS_RO(list,node,data) \
+  (node) = listhead(list), ((data) = NULL);\
+  (node) != NULL && ((data) = listgetdata(node), 1); \
+  (node) = listnextnode(node), ((data) = NULL)
+
+/* these *do not* cleanup list nodes and referenced data, as the functions
+ * do - these macros simply {de,at}tach a listnode from/to a list.
+ */
+/* List node attach macro.  */
+#define LISTNODE_ATTACH(L,N) \
+  do { \
+    (N)->prev = (L)->tail; \
+    (N)->next = NULL; \
+    if ((L)->head == NULL) \
+      (L)->head = (N); \
+    else \
+      (L)->tail->next = (N); \
+    (L)->tail = (N); \
+    (L)->count++; \
+  } while (0)
+
+/* List node detach macro.  */
+#define LISTNODE_DETACH(L,N) \
+  do { \
+    if ((N)->prev) \
+      (N)->prev->next = (N)->next; \
+    else \
+      (L)->head = (N)->next; \
+    if ((N)->next) \
+      (N)->next->prev = (N)->prev; \
+    else \
+      (L)->tail = (N)->prev; \
+    (L)->count--; \
+  } while (0)
+
+/* Deprecated: 20050406 */
+#if !defined(QUAGGA_NO_DEPRECATED_INTERFACES)
+#warning "Using deprecated libzebra interfaces"
+#define LISTNODE_ADD(L,N) LISTNODE_ATTACH(L,N)
+#define LISTNODE_DELETE(L,N) LISTNODE_DETACH(L,N)
+#define nextnode(X) ((X) = (X)->next)
+#define getdata(X) listgetdata(X)
+#define LIST_LOOP(L,V,N) \
+  for (ALL_LIST_ELEMENTS_RO (L,N,V))
+#endif /* QUAGGA_NO_DEPRECATED_INTERFACES */
+
+#endif /* _ZEBRA_LINKLIST_H */
diff --git a/lib/log.c b/lib/log.c
new file mode 100644 (file)
index 0000000..d437066
--- /dev/null
+++ b/lib/log.c
@@ -0,0 +1,1057 @@
+/*
+ * Logging of zebra
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#define QUAGGA_DEFINE_DESC_TABLE
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "command.h"
+#ifndef SUNOS_5
+#include <sys/un.h>
+#endif
+/* for printstack on solaris */
+#ifdef HAVE_UCONTEXT_H
+#include <ucontext.h>
+#endif
+
+static int logfile_fd = -1;    /* Used in signal handler. */
+
+struct zlog *zlog_default = NULL;
+
+const char *zlog_proto_names[] = 
+{
+  "NONE",
+  "DEFAULT",
+  "ZEBRA",
+  "RIP",
+  "BGP",
+  "OSPF",
+  "RIPNG",
+  "BABEL",
+  "OSPF6",
+  "ISIS",
+  "PIM",
+  "MASC",
+  "NHRP",
+  NULL,
+};
+
+const char *zlog_priority[] =
+{
+  "emergencies",
+  "alerts",
+  "critical",
+  "errors",
+  "warnings",
+  "notifications",
+  "informational",
+  "debugging",
+  NULL,
+};
+  
+
+
+/* For time string format. */
+
+size_t
+quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
+{
+  static struct {
+    time_t last;
+    size_t len;
+    char buf[28];
+  } cache;
+  struct timeval clock;
+
+  /* would it be sufficient to use global 'recent_time' here?  I fear not... */
+  gettimeofday(&clock, NULL);
+
+  /* first, we update the cache if the time has changed */
+  if (cache.last != clock.tv_sec)
+    {
+      struct tm *tm;
+      cache.last = clock.tv_sec;
+      tm = localtime(&cache.last);
+      cache.len = strftime(cache.buf, sizeof(cache.buf),
+                          "%Y/%m/%d %H:%M:%S", tm);
+    }
+  /* note: it's not worth caching the subsecond part, because
+     chances are that back-to-back calls are not sufficiently close together
+     for the clock not to have ticked forward */
+
+  if (buflen > cache.len)
+    {
+      memcpy(buf, cache.buf, cache.len);
+      if ((timestamp_precision > 0) &&
+         (buflen > cache.len+1+timestamp_precision))
+       {
+         /* should we worry about locale issues? */
+         static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1};
+         int prec;
+         char *p = buf+cache.len+1+(prec = timestamp_precision);
+         *p-- = '\0';
+         while (prec > 6)
+           /* this is unlikely to happen, but protect anyway */
+           {
+             *p-- = '0';
+             prec--;
+           }
+         clock.tv_usec /= divisor[prec];
+         do
+           {
+             *p-- = '0'+(clock.tv_usec % 10);
+             clock.tv_usec /= 10;
+           }
+         while (--prec > 0);
+         *p = '.';
+         return cache.len+1+timestamp_precision;
+       }
+      buf[cache.len] = '\0';
+      return cache.len;
+    }
+  if (buflen > 0)
+    buf[0] = '\0';
+  return 0;
+}
+
+/* Utility routine for current time printing. */
+static void
+time_print(FILE *fp, struct timestamp_control *ctl)
+{
+  if (!ctl->already_rendered)
+    {
+      ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
+      ctl->already_rendered = 1;
+    }
+  fprintf(fp, "%s ", ctl->buf);
+}
+  
+
+/* va_list version of zlog. */
+static void
+vzlog (struct zlog *zl, int priority, const char *format, va_list args)
+{
+  int original_errno = errno;
+  struct timestamp_control tsctl;
+  tsctl.already_rendered = 0;
+
+  /* If zlog is not specified, use default one. */
+  if (zl == NULL)
+    zl = zlog_default;
+
+  /* When zlog_default is also NULL, use stderr for logging. */
+  if (zl == NULL)
+    {
+      tsctl.precision = 0;
+      time_print(stderr, &tsctl);
+      fprintf (stderr, "%s: ", "unknown");
+      vfprintf (stderr, format, args);
+      fprintf (stderr, "\n");
+      fflush (stderr);
+
+      /* In this case we return at here. */
+      errno = original_errno;
+      return;
+    }
+  tsctl.precision = zl->timestamp_precision;
+
+  /* Syslog output */
+  if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+    {
+      va_list ac;
+      va_copy(ac, args);
+      vsyslog (priority|zlog_default->facility, format, ac);
+      va_end(ac);
+    }
+
+  /* File output. */
+  if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
+    {
+      va_list ac;
+      time_print (zl->fp, &tsctl);
+      if (zl->record_priority)
+       fprintf (zl->fp, "%s: ", zlog_priority[priority]);
+      fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]);
+      va_copy(ac, args);
+      vfprintf (zl->fp, format, ac);
+      va_end(ac);
+      fprintf (zl->fp, "\n");
+      fflush (zl->fp);
+    }
+
+  /* stdout output. */
+  if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
+    {
+      va_list ac;
+      time_print (stdout, &tsctl);
+      if (zl->record_priority)
+       fprintf (stdout, "%s: ", zlog_priority[priority]);
+      fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]);
+      va_copy(ac, args);
+      vfprintf (stdout, format, ac);
+      va_end(ac);
+      fprintf (stdout, "\n");
+      fflush (stdout);
+    }
+
+  /* Terminal monitor. */
+  if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
+    vty_log ((zl->record_priority ? zlog_priority[priority] : NULL),
+            zlog_proto_names[zl->protocol], format, &tsctl, args);
+
+  errno = original_errno;
+}
+
+static char *
+str_append(char *dst, int len, const char *src)
+{
+  while ((len-- > 0) && *src)
+    *dst++ = *src++;
+  return dst;
+}
+
+static char *
+num_append(char *s, int len, u_long x)
+{
+  char buf[30];
+  char *t;
+
+  if (!x)
+    return str_append(s,len,"0");
+  *(t = &buf[sizeof(buf)-1]) = '\0';
+  while (x && (t > buf))
+    {
+      *--t = '0'+(x % 10);
+      x /= 10;
+    }
+  return str_append(s,len,t);
+}
+
+#if defined(SA_SIGINFO) || defined(HAVE_STACK_TRACE)
+static char *
+hex_append(char *s, int len, u_long x)
+{
+  char buf[30];
+  char *t;
+
+  if (!x)
+    return str_append(s,len,"0");
+  *(t = &buf[sizeof(buf)-1]) = '\0';
+  while (x && (t > buf))
+    {
+      u_int cc = (x % 16);
+      *--t = ((cc < 10) ? ('0'+cc) : ('a'+cc-10));
+      x /= 16;
+    }
+  return str_append(s,len,t);
+}
+#endif
+
+/* Needs to be enhanced to support Solaris. */
+static int
+syslog_connect(void)
+{
+#ifdef SUNOS_5
+  return -1;
+#else
+  int fd;
+  char *s;
+  struct sockaddr_un addr;
+
+  if ((fd = socket(AF_UNIX,SOCK_DGRAM,0)) < 0)
+    return -1;
+  addr.sun_family = AF_UNIX;
+#ifdef _PATH_LOG
+#define SYSLOG_SOCKET_PATH _PATH_LOG
+#else
+#define SYSLOG_SOCKET_PATH "/dev/log"
+#endif
+  s = str_append(addr.sun_path,sizeof(addr.sun_path),SYSLOG_SOCKET_PATH);
+#undef SYSLOG_SOCKET_PATH
+  *s = '\0';
+  if (connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
+    {
+      close(fd);
+      return -1;
+    }
+  return fd;
+#endif
+}
+
+static void
+syslog_sigsafe(int priority, const char *msg, size_t msglen)
+{
+  static int syslog_fd = -1;
+  char buf[sizeof("<1234567890>ripngd[1234567890]: ")+msglen+50];
+  char *s;
+
+  if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0))
+    return;
+
+#define LOC s,buf+sizeof(buf)-s
+  s = buf;
+  s = str_append(LOC,"<");
+  s = num_append(LOC,priority);
+  s = str_append(LOC,">");
+  /* forget about the timestamp, too difficult in a signal handler */
+  s = str_append(LOC,zlog_default->ident);
+  if (zlog_default->syslog_options & LOG_PID)
+    {
+      s = str_append(LOC,"[");
+      s = num_append(LOC,getpid());
+      s = str_append(LOC,"]");
+    }
+  s = str_append(LOC,": ");
+  s = str_append(LOC,msg);
+  write(syslog_fd,buf,s-buf);
+#undef LOC
+}
+
+static int
+open_crashlog(void)
+{
+#define CRASHLOG_PREFIX "/var/tmp/quagga."
+#define CRASHLOG_SUFFIX "crashlog"
+  if (zlog_default && zlog_default->ident)
+    {
+      /* Avoid strlen since it is not async-signal-safe. */
+      const char *p;
+      size_t ilen;
+
+      for (p = zlog_default->ident, ilen = 0; *p; p++)
+       ilen++;
+      {
+       char buf[sizeof(CRASHLOG_PREFIX)+ilen+sizeof(CRASHLOG_SUFFIX)+3];
+       char *s = buf;
+#define LOC s,buf+sizeof(buf)-s
+       s = str_append(LOC, CRASHLOG_PREFIX);
+       s = str_append(LOC, zlog_default->ident);
+       s = str_append(LOC, ".");
+       s = str_append(LOC, CRASHLOG_SUFFIX);
+#undef LOC
+       *s = '\0';
+       return open(buf, O_WRONLY|O_CREAT|O_EXCL, LOGFILE_MASK);
+      }
+    }
+  return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, O_WRONLY|O_CREAT|O_EXCL,
+             LOGFILE_MASK);
+#undef CRASHLOG_SUFFIX
+#undef CRASHLOG_PREFIX
+}
+
+/* Note: the goal here is to use only async-signal-safe functions. */
+void
+zlog_signal(int signo, const char *action
+#ifdef SA_SIGINFO
+           , siginfo_t *siginfo, void *program_counter
+#endif
+          )
+{
+  time_t now;
+  char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100];
+  char *s = buf;
+  char *msgstart = buf;
+#define LOC s,buf+sizeof(buf)-s
+
+  time(&now);
+  if (zlog_default)
+    {
+      s = str_append(LOC,zlog_proto_names[zlog_default->protocol]);
+      *s++ = ':';
+      *s++ = ' ';
+      msgstart = s;
+    }
+  s = str_append(LOC,"Received signal ");
+  s = num_append(LOC,signo);
+  s = str_append(LOC," at ");
+  s = num_append(LOC,now);
+#ifdef SA_SIGINFO
+  s = str_append(LOC," (si_addr 0x");
+  s = hex_append(LOC,(u_long)(siginfo->si_addr));
+  if (program_counter)
+    {
+      s = str_append(LOC,", PC 0x");
+      s = hex_append(LOC,(u_long)program_counter);
+    }
+  s = str_append(LOC,"); ");
+#else /* SA_SIGINFO */
+  s = str_append(LOC,"; ");
+#endif /* SA_SIGINFO */
+  s = str_append(LOC,action);
+  if (s < buf+sizeof(buf))
+    *s++ = '\n';
+
+  /* N.B. implicit priority is most severe */
+#define PRI LOG_CRIT
+
+#define DUMP(FD) write(FD, buf, s-buf);
+  /* If no file logging configured, try to write to fallback log file. */
+  if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
+    DUMP(logfile_fd)
+  if (!zlog_default)
+    DUMP(STDERR_FILENO)
+  else
+    {
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
+        DUMP(STDOUT_FILENO)
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+        vty_log_fixed(buf,s-buf);
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+       syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
+    }
+#undef DUMP
+
+  zlog_backtrace_sigsafe(PRI,
+#ifdef SA_SIGINFO
+                        program_counter
+#else
+                        NULL
+#endif
+                       );
+
+  s = buf;
+  if (!thread_current)
+    s = str_append (LOC, "no thread information available\n");
+  else
+    {
+      s = str_append (LOC, "in thread ");
+      s = str_append (LOC, thread_current->funcname);
+      s = str_append (LOC, " scheduled from ");
+      s = str_append (LOC, thread_current->schedfrom);
+      s = str_append (LOC, ":");
+      s = num_append (LOC, thread_current->schedfrom_line);
+      s = str_append (LOC, "\n");
+    }
+
+#define DUMP(FD) write(FD, buf, s-buf);
+  /* If no file logging configured, try to write to fallback log file. */
+  if (logfile_fd >= 0)
+    DUMP(logfile_fd)
+  if (!zlog_default)
+    DUMP(STDERR_FILENO)
+  else
+    {
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
+        DUMP(STDOUT_FILENO)
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+        vty_log_fixed(buf,s-buf);
+      if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+       syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
+    }
+#undef DUMP
+
+#undef PRI
+#undef LOC
+}
+
+/* Log a backtrace using only async-signal-safe functions.
+   Needs to be enhanced to support syslog logging. */
+void
+zlog_backtrace_sigsafe(int priority, void *program_counter)
+{
+#ifdef HAVE_STACK_TRACE
+  static const char pclabel[] = "Program counter: ";
+  void *array[64];
+  int size;
+  char buf[100];
+  char *s, **bt = NULL;
+#define LOC s,buf+sizeof(buf)-s
+
+#ifdef HAVE_GLIBC_BACKTRACE
+  size = backtrace(array, array_size(array));
+  if (size <= 0 || (size_t)size > array_size(array))
+    return;
+
+#define DUMP(FD) { \
+  if (program_counter) \
+    { \
+      write(FD, pclabel, sizeof(pclabel)-1); \
+      backtrace_symbols_fd(&program_counter, 1, FD); \
+    } \
+  write(FD, buf, s-buf);       \
+  backtrace_symbols_fd(array, size, FD); \
+}
+#elif defined(HAVE_PRINTSTACK)
+#define DUMP(FD) { \
+  if (program_counter) \
+    write((FD), pclabel, sizeof(pclabel)-1); \
+  write((FD), buf, s-buf); \
+  printstack((FD)); \
+}
+#endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */
+
+  s = buf;
+  s = str_append(LOC,"Backtrace for ");
+  s = num_append(LOC,size);
+  s = str_append(LOC," stack frames:\n");
+
+  if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
+    DUMP(logfile_fd)
+  if (!zlog_default)
+    DUMP(STDERR_FILENO)
+  else
+    {
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
+       DUMP(STDOUT_FILENO)
+      /* Remove trailing '\n' for monitor and syslog */
+      *--s = '\0';
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+       vty_log_fixed(buf,s-buf);
+      if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+       syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
+      {
+       int i;
+#ifdef HAVE_GLIBC_BACKTRACE
+        bt = backtrace_symbols(array, size);
+#endif
+       /* Just print the function addresses. */
+       for (i = 0; i < size; i++)
+         {
+           s = buf;
+           if (bt) 
+             s = str_append(LOC, bt[i]);
+           else {
+             s = str_append(LOC,"[bt ");
+             s = num_append(LOC,i);
+             s = str_append(LOC,"] 0x");
+             s = hex_append(LOC,(u_long)(array[i]));
+           }
+           *s = '\0';
+           if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
+             vty_log_fixed(buf,s-buf);
+           if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
+             syslog_sigsafe(priority|zlog_default->facility,buf,s-buf);
+         }
+         if (bt)
+           free(bt);
+      }
+    }
+#undef DUMP
+#undef LOC
+#endif /* HAVE_STRACK_TRACE */
+}
+
+void
+zlog_backtrace(int priority)
+{
+#ifndef HAVE_GLIBC_BACKTRACE
+  zlog(NULL, priority, "No backtrace available on this platform.");
+#else
+  void *array[20];
+  int size, i;
+  char **strings;
+
+  size = backtrace(array, array_size(array));
+  if (size <= 0 || (size_t)size > array_size(array))
+    {
+      zlog_err("Cannot get backtrace, returned invalid # of frames %d "
+              "(valid range is between 1 and %lu)",
+              size, (unsigned long)(array_size(array)));
+      return;
+    }
+  zlog(NULL, priority, "Backtrace for %d stack frames:", size);
+  if (!(strings = backtrace_symbols(array, size)))
+    {
+      zlog_err("Cannot get backtrace symbols (out of memory?)");
+      for (i = 0; i < size; i++)
+       zlog(NULL, priority, "[bt %d] %p",i,array[i]);
+    }
+  else
+    {
+      for (i = 0; i < size; i++)
+       zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+      free(strings);
+    }
+#endif /* HAVE_GLIBC_BACKTRACE */
+}
+
+void
+zlog (struct zlog *zl, int priority, const char *format, ...)
+{
+  va_list args;
+
+  va_start(args, format);
+  vzlog (zl, priority, format, args);
+  va_end (args);
+}
+
+#define ZLOG_FUNC(FUNCNAME,PRIORITY) \
+void \
+FUNCNAME(const char *format, ...) \
+{ \
+  va_list args; \
+  va_start(args, format); \
+  vzlog (NULL, PRIORITY, format, args); \
+  va_end(args); \
+}
+
+ZLOG_FUNC(zlog_err, LOG_ERR)
+
+ZLOG_FUNC(zlog_warn, LOG_WARNING)
+
+ZLOG_FUNC(zlog_info, LOG_INFO)
+
+ZLOG_FUNC(zlog_notice, LOG_NOTICE)
+
+ZLOG_FUNC(zlog_debug, LOG_DEBUG)
+
+#undef ZLOG_FUNC
+
+#define PLOG_FUNC(FUNCNAME,PRIORITY) \
+void \
+FUNCNAME(struct zlog *zl, const char *format, ...) \
+{ \
+  va_list args; \
+  va_start(args, format); \
+  vzlog (zl, PRIORITY, format, args); \
+  va_end(args); \
+}
+
+PLOG_FUNC(plog_err, LOG_ERR)
+
+PLOG_FUNC(plog_warn, LOG_WARNING)
+
+PLOG_FUNC(plog_info, LOG_INFO)
+
+PLOG_FUNC(plog_notice, LOG_NOTICE)
+
+PLOG_FUNC(plog_debug, LOG_DEBUG)
+
+#undef PLOG_FUNC
+
+void zlog_thread_info (int log_level)
+{
+  if (thread_current)
+    zlog(NULL, log_level, "Current thread function %s, scheduled from "
+        "file %s, line %u", thread_current->funcname,
+        thread_current->schedfrom, thread_current->schedfrom_line);
+  else
+    zlog(NULL, log_level, "Current thread not known/applicable");
+}
+
+void
+_zlog_assert_failed (const char *assertion, const char *file,
+                    unsigned int line, const char *function)
+{
+  /* Force fallback file logging? */
+  if (zlog_default && !zlog_default->fp &&
+      ((logfile_fd = open_crashlog()) >= 0) &&
+      ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
+    zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
+  zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
+       assertion,file,line,(function ? function : "?"));
+  zlog_backtrace(LOG_CRIT);
+  zlog_thread_info(LOG_CRIT);
+  abort();
+}
+
+
+/* Open log stream */
+struct zlog *
+openzlog (const char *progname, zlog_proto_t protocol,
+         int syslog_flags, int syslog_facility)
+{
+  struct zlog *zl;
+  u_int i;
+
+  zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog));
+
+  zl->ident = progname;
+  zl->protocol = protocol;
+  zl->facility = syslog_facility;
+  zl->syslog_options = syslog_flags;
+
+  /* Set default logging levels. */
+  for (i = 0; i < array_size(zl->maxlvl); i++)
+    zl->maxlvl[i] = ZLOG_DISABLED;
+  zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
+  zl->default_lvl = LOG_DEBUG;
+
+  openlog (progname, syslog_flags, zl->facility);
+  
+  return zl;
+}
+
+void
+closezlog (struct zlog *zl)
+{
+  closelog();
+
+  if (zl->fp != NULL)
+    fclose (zl->fp);
+
+  if (zl->filename != NULL)
+    free (zl->filename);
+
+  XFREE (MTYPE_ZLOG, zl);
+}
+
+/* Called from command.c. */
+void
+zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
+{
+  if (zl == NULL)
+    zl = zlog_default;
+
+  zl->maxlvl[dest] = log_level;
+}
+
+int
+zlog_set_file (struct zlog *zl, const char *filename, int log_level)
+{
+  FILE *fp;
+  mode_t oldumask;
+
+  /* There is opend file.  */
+  zlog_reset_file (zl);
+
+  /* Set default zl. */
+  if (zl == NULL)
+    zl = zlog_default;
+
+  /* Open file. */
+  oldumask = umask (0777 & ~LOGFILE_MASK);
+  fp = fopen (filename, "a");
+  umask(oldumask);
+  if (fp == NULL)
+    return 0;
+
+  /* Set flags. */
+  zl->filename = strdup (filename);
+  zl->maxlvl[ZLOG_DEST_FILE] = log_level;
+  zl->fp = fp;
+  logfile_fd = fileno(fp);
+
+  return 1;
+}
+
+/* Reset opend file. */
+int
+zlog_reset_file (struct zlog *zl)
+{
+  if (zl == NULL)
+    zl = zlog_default;
+
+  if (zl->fp)
+    fclose (zl->fp);
+  zl->fp = NULL;
+  logfile_fd = -1;
+  zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+  if (zl->filename)
+    free (zl->filename);
+  zl->filename = NULL;
+
+  return 1;
+}
+
+/* Reopen log file. */
+int
+zlog_rotate (struct zlog *zl)
+{
+  int level;
+
+  if (zl == NULL)
+    zl = zlog_default;
+
+  if (zl->fp)
+    fclose (zl->fp);
+  zl->fp = NULL;
+  logfile_fd = -1;
+  level = zl->maxlvl[ZLOG_DEST_FILE];
+  zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+  if (zl->filename)
+    {
+      mode_t oldumask;
+      int save_errno;
+
+      oldumask = umask (0777 & ~LOGFILE_MASK);
+      zl->fp = fopen (zl->filename, "a");
+      save_errno = errno;
+      umask(oldumask);
+      if (zl->fp == NULL)
+        {
+         zlog_err("Log rotate failed: cannot open file %s for append: %s",
+                  zl->filename, safe_strerror(save_errno));
+         return -1;
+        }      
+      logfile_fd = fileno(zl->fp);
+      zl->maxlvl[ZLOG_DEST_FILE] = level;
+    }
+
+  return 1;
+}
+
+/* Message lookup function. */
+const char *
+lookup (const struct message *mes, int key)
+{
+  const struct message *pnt;
+
+  for (pnt = mes; pnt->key != 0; pnt++) 
+    if (pnt->key == key) 
+      return pnt->str;
+
+  return "";
+}
+
+/* Older/faster version of message lookup function, but requires caller to pass
+ * in the array size (instead of relying on a 0 key to terminate the search). 
+ *
+ * The return value is the message string if found, or the 'none' pointer
+ * provided otherwise.
+ */
+const char *
+mes_lookup (const struct message *meslist, int max, int index,
+  const char *none, const char *mesname)
+{
+  int pos = index - meslist[0].key;
+  
+  /* first check for best case: index is in range and matches the key
+   * value in that slot.
+   * NB: key numbering might be offset from 0. E.g. protocol constants
+   * often start at 1.
+   */
+  if ((pos >= 0) && (pos < max)
+      && (meslist[pos].key == index))
+    return meslist[pos].str;
+
+  /* fall back to linear search */
+  {
+    int i;
+
+    for (i = 0; i < max; i++, meslist++)
+      {
+       if (meslist->key == index)
+         {
+           const char *str = (meslist->str ? meslist->str : none);
+           
+           zlog_debug ("message index %d [%s] found in %s at position %d (max is %d)",
+                     index, str, mesname, i, max);
+           return str;
+         }
+      }
+  }
+  zlog_err("message index %d not found in %s (max is %d)", index, mesname, max);
+  assert (none);
+  return none;
+}
+
+/* Wrapper around strerror to handle case where it returns NULL. */
+const char *
+safe_strerror(int errnum)
+{
+  const char *s = strerror(errnum);
+  return (s != NULL) ? s : "Unknown error";
+}
+
+#define DESC_ENTRY(T) [(T)] = { (T), (#T), '\0' }
+static const struct zebra_desc_table command_types[] = {
+  DESC_ENTRY   (ZEBRA_INTERFACE_ADD),
+  DESC_ENTRY   (ZEBRA_INTERFACE_DELETE),
+  DESC_ENTRY   (ZEBRA_INTERFACE_ADDRESS_ADD),
+  DESC_ENTRY   (ZEBRA_INTERFACE_ADDRESS_DELETE),
+  DESC_ENTRY   (ZEBRA_INTERFACE_UP),
+  DESC_ENTRY   (ZEBRA_INTERFACE_DOWN),
+  DESC_ENTRY   (ZEBRA_IPV4_ROUTE_ADD),
+  DESC_ENTRY   (ZEBRA_IPV4_ROUTE_DELETE),
+  DESC_ENTRY   (ZEBRA_IPV6_ROUTE_ADD),
+  DESC_ENTRY   (ZEBRA_IPV6_ROUTE_DELETE),
+  DESC_ENTRY   (ZEBRA_REDISTRIBUTE_ADD),
+  DESC_ENTRY   (ZEBRA_REDISTRIBUTE_DELETE),
+  DESC_ENTRY   (ZEBRA_REDISTRIBUTE_DEFAULT_ADD),
+  DESC_ENTRY   (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE),
+  DESC_ENTRY   (ZEBRA_IPV4_NEXTHOP_LOOKUP),
+  DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_LOOKUP),
+  DESC_ENTRY   (ZEBRA_IPV4_IMPORT_LOOKUP),
+  DESC_ENTRY   (ZEBRA_IPV6_IMPORT_LOOKUP),
+  DESC_ENTRY   (ZEBRA_INTERFACE_RENAME),
+  DESC_ENTRY   (ZEBRA_ROUTER_ID_ADD),
+  DESC_ENTRY   (ZEBRA_ROUTER_ID_DELETE),
+  DESC_ENTRY   (ZEBRA_ROUTER_ID_UPDATE),
+  DESC_ENTRY   (ZEBRA_HELLO),
+  DESC_ENTRY   (ZEBRA_NEXTHOP_REGISTER),
+  DESC_ENTRY   (ZEBRA_NEXTHOP_UNREGISTER),
+  DESC_ENTRY   (ZEBRA_NEXTHOP_UPDATE),
+};
+#undef DESC_ENTRY
+
+static const struct zebra_desc_table unknown = { 0, "unknown", '?' };
+
+static const struct zebra_desc_table *
+zroute_lookup(u_int zroute)
+{
+  u_int i;
+
+  if (zroute >= array_size(route_types))
+    {
+      zlog_err("unknown zebra route type: %u", zroute);
+      return &unknown;
+    }
+  if (zroute == route_types[zroute].type)
+    return &route_types[zroute];
+  for (i = 0; i < array_size(route_types); i++)
+    {
+      if (zroute == route_types[i].type)
+        {
+         zlog_warn("internal error: route type table out of order "
+                   "while searching for %u, please notify developers", zroute);
+         return &route_types[i];
+        }
+    }
+  zlog_err("internal error: cannot find route type %u in table!", zroute);
+  return &unknown;
+}
+
+const char *
+zebra_route_string(u_int zroute)
+{
+  return zroute_lookup(zroute)->string;
+}
+
+char
+zebra_route_char(u_int zroute)
+{
+  return zroute_lookup(zroute)->chr;
+}
+
+const char *
+zserv_command_string (unsigned int command)
+{
+  if (command >= array_size(command_types))
+    {
+      zlog_err ("unknown zserv command type: %u", command);
+      return unknown.string;
+    }
+  return command_types[command].string;
+}
+
+int
+proto_name2num(const char *s)
+{
+   unsigned i;
+
+   for (i=0; i<array_size(route_types); ++i)
+     if (strcasecmp(s, route_types[i].string) == 0)
+       return route_types[i].type;
+   return -1;
+}
+
+int
+proto_redistnum(int afi, const char *s)
+{
+  if (! s)
+    return -1;
+
+  if (afi == AFI_IP)
+    {
+      if (strncmp (s, "k", 1) == 0)
+       return ZEBRA_ROUTE_KERNEL;
+      else if (strncmp (s, "c", 1) == 0)
+       return ZEBRA_ROUTE_CONNECT;
+      else if (strncmp (s, "s", 1) == 0)
+       return ZEBRA_ROUTE_STATIC;
+      else if (strncmp (s, "r", 1) == 0)
+       return ZEBRA_ROUTE_RIP;
+      else if (strncmp (s, "o", 1) == 0)
+       return ZEBRA_ROUTE_OSPF;
+      else if (strncmp (s, "i", 1) == 0)
+       return ZEBRA_ROUTE_ISIS;
+      else if (strncmp (s, "bg", 2) == 0)
+       return ZEBRA_ROUTE_BGP;
+      else if (strncmp (s, "ba", 2) == 0)
+       return ZEBRA_ROUTE_BABEL;
+      else if (strncmp (s, "n", 1) == 0)
+       return ZEBRA_ROUTE_NHRP;
+    }
+  if (afi == AFI_IP6)
+    {
+      if (strncmp (s, "k", 1) == 0)
+       return ZEBRA_ROUTE_KERNEL;
+      else if (strncmp (s, "c", 1) == 0)
+       return ZEBRA_ROUTE_CONNECT;
+      else if (strncmp (s, "s", 1) == 0)
+       return ZEBRA_ROUTE_STATIC;
+      else if (strncmp (s, "r", 1) == 0)
+       return ZEBRA_ROUTE_RIPNG;
+      else if (strncmp (s, "o", 1) == 0)
+       return ZEBRA_ROUTE_OSPF6;
+      else if (strncmp (s, "i", 1) == 0)
+       return ZEBRA_ROUTE_ISIS;
+      else if (strncmp (s, "bg", 2) == 0)
+       return ZEBRA_ROUTE_BGP;
+      else if (strncmp (s, "ba", 2) == 0)
+       return ZEBRA_ROUTE_BABEL;
+      else if (strncmp (s, "n", 1) == 0)
+       return ZEBRA_ROUTE_NHRP;
+    }
+  return -1;
+}
+
+void
+zlog_hexdump (void *mem, unsigned int len) {
+  unsigned long i = 0;
+  unsigned int j = 0;
+  unsigned int columns = 8;
+  char buf[(len * 4) + ((len/4) * 20) + 30];
+  char *s = buf;
+
+  for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
+    {
+      /* print offset */
+      if (i % columns == 0)
+        s += sprintf(s, "0x%016lx: ", (unsigned long)mem + i);
+
+      /* print hex data */
+      if (i < len)
+        s += sprintf(s, "%02x ", 0xFF & ((char*)mem)[i]);
+
+      /* end of block, just aligning for ASCII dump */
+      else
+        s += sprintf(s, "   ");
+
+      /* print ASCII dump */
+      if (i % columns == (columns - 1))
+        {
+          for (j = i - (columns - 1); j <= i; j++)
+            {
+              if (j >= len) /* end of block, not really printing */
+                s += sprintf(s, " ");
+
+              else if(isprint((int)((char*)mem)[j])) /* printable char */
+                s += sprintf(s, "%c", 0xFF & ((char*)mem)[j]);
+
+              else /* other char */
+                s += sprintf(s, ".");
+            }
+          s += sprintf(s, "\n");
+        }
+    }
+    zlog_debug("\n%s", buf);
+}
diff --git a/lib/log.h b/lib/log.h
new file mode 100644 (file)
index 0000000..59968ae
--- /dev/null
+++ b/lib/log.h
@@ -0,0 +1,241 @@
+/*
+ * Zebra logging funcions.
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_LOG_H
+#define _ZEBRA_LOG_H
+
+#include <syslog.h>
+#include <stdio.h>
+
+/* Here is some guidance on logging levels to use:
+ *
+ * LOG_DEBUG   - For all messages that are enabled by optional debugging
+ *               features, typically preceded by "if (IS...DEBUG...)"
+ * LOG_INFO    - Information that may be of interest, but everything seems
+ *               to be working properly.
+ * LOG_NOTICE  - Only for message pertaining to daemon startup or shutdown.
+ * LOG_WARNING - Warning conditions: unexpected events, but the daemon believes
+ *               it can continue to operate correctly.
+ * LOG_ERR     - Error situations indicating malfunctions.  Probably require
+ *               attention.
+ *
+ * Note: LOG_CRIT, LOG_ALERT, and LOG_EMERG are currently not used anywhere,
+ * please use LOG_ERR instead.
+ */
+
+typedef enum 
+{
+  ZLOG_NONE,
+  ZLOG_DEFAULT,
+  ZLOG_ZEBRA,
+  ZLOG_RIP,
+  ZLOG_BGP,
+  ZLOG_OSPF,
+  ZLOG_RIPNG,
+  ZLOG_BABEL,
+  ZLOG_OSPF6,
+  ZLOG_ISIS,
+  ZLOG_PIM,
+  ZLOG_MASC,
+  ZLOG_NHRP,
+} zlog_proto_t;
+
+/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
+   to that logging destination. */
+#define ZLOG_DISABLED  (LOG_EMERG-1)
+
+typedef enum
+{
+  ZLOG_DEST_SYSLOG = 0,
+  ZLOG_DEST_STDOUT,
+  ZLOG_DEST_MONITOR,
+  ZLOG_DEST_FILE
+} zlog_dest_t;
+#define ZLOG_NUM_DESTS         (ZLOG_DEST_FILE+1)
+
+struct zlog 
+{
+  const char *ident;   /* daemon name (first arg to openlog) */
+  zlog_proto_t protocol;
+  int maxlvl[ZLOG_NUM_DESTS];  /* maximum priority to send to associated
+                                  logging destination */
+  int default_lvl;     /* maxlvl to use if none is specified */
+  FILE *fp;
+  char *filename;
+  int facility;                /* as per syslog facility */
+  int record_priority; /* should messages logged through stdio include the
+                          priority of the message? */
+  int syslog_options;  /* 2nd arg to openlog */
+  int timestamp_precision;     /* # of digits of subsecond precision */
+};
+
+/* Message structure. */
+struct message
+{
+  int key;
+  const char *str;
+};
+
+/* Default logging strucutre. */
+extern struct zlog *zlog_default;
+
+/* Open zlog function */
+extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol,
+                             int syslog_options, int syslog_facility);
+
+/* Close zlog function. */
+extern void closezlog (struct zlog *zl);
+
+/* GCC have printf type attribute check.  */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/* Generic function for zlog. */
+extern void zlog (struct zlog *zl, int priority, const char *format, ...)
+  PRINTF_ATTRIBUTE(3, 4);
+
+/* Handy zlog functions. */
+extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+
+/* For bgpd's peer oriented log. */
+extern void plog_err (struct zlog *, const char *format, ...)
+  PRINTF_ATTRIBUTE(2, 3);
+extern void plog_warn (struct zlog *, const char *format, ...)
+  PRINTF_ATTRIBUTE(2, 3);
+extern void plog_info (struct zlog *, const char *format, ...)
+  PRINTF_ATTRIBUTE(2, 3);
+extern void plog_notice (struct zlog *, const char *format, ...)
+  PRINTF_ATTRIBUTE(2, 3);
+extern void plog_debug (struct zlog *, const char *format, ...)
+  PRINTF_ATTRIBUTE(2, 3);
+
+extern void zlog_thread_info (int log_level);
+
+/* Set logging level for the given destination.  If the log_level
+   argument is ZLOG_DISABLED, then the destination is disabled.
+   This function should not be used for file logging (use zlog_set_file
+   or zlog_reset_file instead). */
+extern void zlog_set_level (struct zlog *zl, zlog_dest_t, int log_level);
+
+/* Set logging to the given filename at the specified level. */
+extern int zlog_set_file (struct zlog *zl, const char *filename, int log_level);
+/* Disable file logging. */
+extern int zlog_reset_file (struct zlog *zl);
+
+/* Rotate log. */
+extern int zlog_rotate (struct zlog *);
+
+/* For hackey message lookup and check */
+#define LOOKUP_DEF(x, y, def) mes_lookup(x, x ## _max, y, def, #x)
+#define LOOKUP(x, y) LOOKUP_DEF(x, y, "(no item found)")
+
+extern const char *lookup (const struct message *, int);
+extern const char *mes_lookup (const struct message *meslist, 
+                               int max, int index,
+                               const char *no_item, const char *mesname);
+
+extern const char *zlog_priority[];
+extern const char *zlog_proto_names[];
+
+/* Safe version of strerror -- never returns NULL. */
+extern const char *safe_strerror(int errnum);
+
+/* To be called when a fatal signal is caught. */
+extern void zlog_signal(int signo, const char *action
+#ifdef SA_SIGINFO
+                       , siginfo_t *siginfo, void *program_counter
+#endif
+                      );
+
+/* Log a backtrace. */
+extern void zlog_backtrace(int priority);
+
+/* Log a backtrace, but in an async-signal-safe way.  Should not be
+   called unless the program is about to exit or abort, since it messes
+   up the state of zlog file pointers.  If program_counter is non-NULL,
+   that is logged in addition to the current backtrace. */
+extern void zlog_backtrace_sigsafe(int priority, void *program_counter);
+
+/* Puts a current timestamp in buf and returns the number of characters
+   written (not including the terminating NUL).  The purpose of
+   this function is to avoid calls to localtime appearing all over the code.
+   It caches the most recent localtime result and can therefore
+   avoid multiple calls within the same second.  If buflen is too small,
+   *buf will be set to '\0', and 0 will be returned. */
+#define QUAGGA_TIMESTAMP_LEN 40
+extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
+                              char *buf, size_t buflen);
+
+extern void zlog_hexdump(void *mem, unsigned int len);
+
+/* structure useful for avoiding repeated rendering of the same timestamp */
+struct timestamp_control {
+   size_t len;         /* length of rendered timestamp */
+   int precision;      /* configuration parameter */
+   int already_rendered; /* should be initialized to 0 */
+   char buf[QUAGGA_TIMESTAMP_LEN];     /* will contain the rendered timestamp */
+};
+
+/* Defines for use in command construction: */
+
+#define LOG_LEVELS "(emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)"
+
+#define LOG_LEVEL_DESC \
+  "System is unusable\n" \
+  "Immediate action needed\n" \
+  "Critical conditions\n" \
+  "Error conditions\n" \
+  "Warning conditions\n" \
+  "Normal but significant conditions\n" \
+  "Informational messages\n" \
+  "Debugging messages\n"
+
+#define LOG_FACILITIES "(kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)"
+
+#define LOG_FACILITY_DESC \
+       "Kernel\n" \
+       "User process\n" \
+       "Mail system\n" \
+       "System daemons\n" \
+       "Authorization system\n" \
+       "Syslog itself\n" \
+       "Line printer system\n" \
+       "USENET news\n" \
+       "Unix-to-Unix copy system\n" \
+       "Cron/at facility\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n" \
+       "Local use\n"
+
+#endif /* _ZEBRA_LOG_H */
diff --git a/lib/md5.c b/lib/md5.c
new file mode 100644 (file)
index 0000000..ce459bb
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,373 @@
+/* $USAGI: md5.c,v 1.2 2000/11/02 11:59:24 yoshfuji Exp $ */
+/*     $KAME: md5.c,v 1.2 2000/05/27 07:07:48 jinmei Exp $     */
+/*     $Id: md5.c,v 1.6 2006/01/17 23:39:04 vincent Exp $ */
+
+/*
+ * Copyright (C) 2004 6WIND
+ *                          <Vincent.Jardin@6WIND.com>
+ * All rights reserved.
+ *
+ * This MD5 code is Big endian and Little Endian compatible.
+ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <zebra.h>
+#include "md5.h"
+
+#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s))))
+
+#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z)))
+#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z)))
+#define H(X, Y, Z) ((X) ^ (Y) ^ (Z))
+#define I(X, Y, Z) ((Y) ^ ((X) | (~Z)))
+
+#define ROUND1(a, b, c, d, k, s, i) { \
+       (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \
+       (a) = SHIFT((a), (s)); \
+       (a) = (b) + (a); \
+}
+
+#define ROUND2(a, b, c, d, k, s, i) { \
+       (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \
+       (a) = SHIFT((a), (s)); \
+       (a) = (b) + (a); \
+}
+
+#define ROUND3(a, b, c, d, k, s, i) { \
+       (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \
+       (a) = SHIFT((a), (s)); \
+       (a) = (b) + (a); \
+}
+
+#define ROUND4(a, b, c, d, k, s, i) { \
+       (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \
+       (a) = SHIFT((a), (s)); \
+       (a) = (b) + (a); \
+}
+
+#define Sa      7
+#define Sb     12
+#define Sc     17
+#define Sd     22
+
+#define Se      5
+#define Sf      9
+#define Sg     14
+#define Sh     20
+
+#define Si      4
+#define Sj     11
+#define Sk     16
+#define Sl     23
+
+#define Sm      6
+#define Sn     10
+#define So     15
+#define Sp     21
+
+#define MD5_A0 0x67452301
+#define MD5_B0 0xefcdab89
+#define MD5_C0 0x98badcfe
+#define MD5_D0 0x10325476
+
+/* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */
+static const uint32_t T[65] = {
+       0,
+       0xd76aa478,     0xe8c7b756,     0x242070db,     0xc1bdceee,
+       0xf57c0faf,     0x4787c62a,     0xa8304613,     0xfd469501,
+       0x698098d8,     0x8b44f7af,     0xffff5bb1,     0x895cd7be,
+       0x6b901122,     0xfd987193,     0xa679438e,     0x49b40821,
+
+       0xf61e2562,     0xc040b340,     0x265e5a51,     0xe9b6c7aa,
+       0xd62f105d,     0x2441453,      0xd8a1e681,     0xe7d3fbc8,
+       0x21e1cde6,     0xc33707d6,     0xf4d50d87,     0x455a14ed,
+       0xa9e3e905,     0xfcefa3f8,     0x676f02d9,     0x8d2a4c8a,
+
+       0xfffa3942,     0x8771f681,     0x6d9d6122,     0xfde5380c,
+       0xa4beea44,     0x4bdecfa9,     0xf6bb4b60,     0xbebfbc70,
+       0x289b7ec6,     0xeaa127fa,     0xd4ef3085,     0x4881d05,
+       0xd9d4d039,     0xe6db99e5,     0x1fa27cf8,     0xc4ac5665,
+
+       0xf4292244,     0x432aff97,     0xab9423a7,     0xfc93a039,
+       0x655b59c3,     0x8f0ccc92,     0xffeff47d,     0x85845dd1,
+       0x6fa87e4f,     0xfe2ce6e0,     0xa3014314,     0x4e0811a1,
+       0xf7537e82,     0xbd3af235,     0x2ad7d2bb,     0xeb86d391,
+};
+
+static const uint8_t md5_paddat[MD5_BUFLEN] = {
+       0x80,   0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,      
+};
+
+static void md5_calc (const uint8_t *, md5_ctxt *);
+
+void md5_init(md5_ctxt *ctxt)
+{
+       ctxt->md5_n = 0;
+       ctxt->md5_i = 0;
+       ctxt->md5_sta = MD5_A0;
+       ctxt->md5_stb = MD5_B0;
+       ctxt->md5_stc = MD5_C0;
+       ctxt->md5_std = MD5_D0;
+       memset (ctxt->md5_buf, 0, sizeof(ctxt->md5_buf));
+}
+
+void md5_loop(md5_ctxt *ctxt, const void *vinput, uint len)
+{
+       uint gap, i;
+       const uint8_t *input = vinput;
+
+       ctxt->md5_n += len * 8; /* byte to bit */
+       gap = MD5_BUFLEN - ctxt->md5_i;
+
+       if (len >= gap) {
+               memcpy (ctxt->md5_buf + ctxt->md5_i, input, gap);
+               md5_calc(ctxt->md5_buf, ctxt);
+
+               for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN) {
+                       md5_calc((input + i), ctxt);
+               }
+               
+               ctxt->md5_i = len - i;
+               memcpy (ctxt->md5_buf, (input + i), ctxt->md5_i);
+       } else {
+               memcpy (ctxt->md5_buf + ctxt->md5_i, input, len);
+               ctxt->md5_i += len;
+       }
+}
+
+void md5_pad(md5_ctxt *ctxt)
+{
+       uint gap;
+
+       /* Don't count up padding. Keep md5_n. */       
+       gap = MD5_BUFLEN - ctxt->md5_i;
+       if (gap > 8) {
+               memcpy (ctxt->md5_buf + ctxt->md5_i, md5_paddat, 
+                       gap - sizeof(ctxt->md5_n));
+       } else {
+               /* including gap == 8 */
+               memcpy (ctxt->md5_buf + ctxt->md5_i, md5_paddat, gap);
+               md5_calc (ctxt->md5_buf, ctxt);
+               memcpy (ctxt->md5_buf, md5_paddat + gap,
+                       MD5_BUFLEN - sizeof(ctxt->md5_n));
+       }
+
+       /* 8 byte word */       
+       if (BYTE_ORDER == LITTLE_ENDIAN)
+         memcpy (&ctxt->md5_buf[56], &ctxt->md5_n8[0], 8);
+       else
+         {
+           ctxt->md5_buf[56] = ctxt->md5_n8[7];
+           ctxt->md5_buf[57] = ctxt->md5_n8[6];
+           ctxt->md5_buf[58] = ctxt->md5_n8[5];
+           ctxt->md5_buf[59] = ctxt->md5_n8[4];
+           ctxt->md5_buf[60] = ctxt->md5_n8[3];
+           ctxt->md5_buf[61] = ctxt->md5_n8[2];
+           ctxt->md5_buf[62] = ctxt->md5_n8[1];
+           ctxt->md5_buf[63] = ctxt->md5_n8[0];
+         }
+       md5_calc(ctxt->md5_buf, ctxt);
+}
+
+void md5_result(uint8_t *digest, md5_ctxt *ctxt)
+{
+       /* 4 byte words */
+       if (BYTE_ORDER == LITTLE_ENDIAN)
+         memcpy (digest, &ctxt->md5_st8[0], 16);
+       else if (BYTE_ORDER == BIG_ENDIAN)
+         {
+           digest[ 0] = ctxt->md5_st8[ 3]; digest[ 1] = ctxt->md5_st8[ 2];
+           digest[ 2] = ctxt->md5_st8[ 1]; digest[ 3] = ctxt->md5_st8[ 0];
+           digest[ 4] = ctxt->md5_st8[ 7]; digest[ 5] = ctxt->md5_st8[ 6];
+           digest[ 6] = ctxt->md5_st8[ 5]; digest[ 7] = ctxt->md5_st8[ 4];
+           digest[ 8] = ctxt->md5_st8[11]; digest[ 9] = ctxt->md5_st8[10];
+           digest[10] = ctxt->md5_st8[ 9]; digest[11] = ctxt->md5_st8[ 8];
+           digest[12] = ctxt->md5_st8[15]; digest[13] = ctxt->md5_st8[14];
+           digest[14] = ctxt->md5_st8[13]; digest[15] = ctxt->md5_st8[12];
+         }
+}
+
+static void md5_calc(const uint8_t *b64, md5_ctxt * ctxt)
+{
+       uint32_t A = ctxt->md5_sta;
+       uint32_t B = ctxt->md5_stb;
+       uint32_t C = ctxt->md5_stc;
+       uint32_t D = ctxt->md5_std;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+       const uint32_t *X = (const uint32_t *)b64;
+#elif (BYTE_ORDER == BIG_ENDIAN)
+       uint32_t X[16];
+
+       if (BYTE_ORDER == BIG_ENDIAN)
+         {
+           /* 4 byte words */
+           /* what a brute force but fast! */
+           uint8_t *y = (uint8_t *)X;
+           y[ 0] = b64[ 3]; y[ 1] = b64[ 2]; y[ 2] = b64[ 1]; y[ 3] = b64[ 0];
+           y[ 4] = b64[ 7]; y[ 5] = b64[ 6]; y[ 6] = b64[ 5]; y[ 7] = b64[ 4];
+           y[ 8] = b64[11]; y[ 9] = b64[10]; y[10] = b64[ 9]; y[11] = b64[ 8];
+           y[12] = b64[15]; y[13] = b64[14]; y[14] = b64[13]; y[15] = b64[12];
+           y[16] = b64[19]; y[17] = b64[18]; y[18] = b64[17]; y[19] = b64[16];
+           y[20] = b64[23]; y[21] = b64[22]; y[22] = b64[21]; y[23] = b64[20];
+           y[24] = b64[27]; y[25] = b64[26]; y[26] = b64[25]; y[27] = b64[24];
+           y[28] = b64[31]; y[29] = b64[30]; y[30] = b64[29]; y[31] = b64[28];
+           y[32] = b64[35]; y[33] = b64[34]; y[34] = b64[33]; y[35] = b64[32];
+           y[36] = b64[39]; y[37] = b64[38]; y[38] = b64[37]; y[39] = b64[36];
+           y[40] = b64[43]; y[41] = b64[42]; y[42] = b64[41]; y[43] = b64[40];
+           y[44] = b64[47]; y[45] = b64[46]; y[46] = b64[45]; y[47] = b64[44];
+           y[48] = b64[51]; y[49] = b64[50]; y[50] = b64[49]; y[51] = b64[48];
+           y[52] = b64[55]; y[53] = b64[54]; y[54] = b64[53]; y[55] = b64[52];
+           y[56] = b64[59]; y[57] = b64[58]; y[58] = b64[57]; y[59] = b64[56];
+           y[60] = b64[63]; y[61] = b64[62]; y[62] = b64[61]; y[63] = b64[60];
+         }
+#endif
+
+       ROUND1(A, B, C, D,  0, Sa,  1); ROUND1(D, A, B, C,  1, Sb,  2);
+       ROUND1(C, D, A, B,  2, Sc,  3); ROUND1(B, C, D, A,  3, Sd,  4);
+       ROUND1(A, B, C, D,  4, Sa,  5); ROUND1(D, A, B, C,  5, Sb,  6);
+       ROUND1(C, D, A, B,  6, Sc,  7); ROUND1(B, C, D, A,  7, Sd,  8);
+       ROUND1(A, B, C, D,  8, Sa,  9); ROUND1(D, A, B, C,  9, Sb, 10);
+       ROUND1(C, D, A, B, 10, Sc, 11); ROUND1(B, C, D, A, 11, Sd, 12);
+       ROUND1(A, B, C, D, 12, Sa, 13); ROUND1(D, A, B, C, 13, Sb, 14);
+       ROUND1(C, D, A, B, 14, Sc, 15); ROUND1(B, C, D, A, 15, Sd, 16);
+       
+       ROUND2(A, B, C, D,  1, Se, 17); ROUND2(D, A, B, C,  6, Sf, 18);
+       ROUND2(C, D, A, B, 11, Sg, 19); ROUND2(B, C, D, A,  0, Sh, 20);
+       ROUND2(A, B, C, D,  5, Se, 21); ROUND2(D, A, B, C, 10, Sf, 22);
+       ROUND2(C, D, A, B, 15, Sg, 23); ROUND2(B, C, D, A,  4, Sh, 24);
+       ROUND2(A, B, C, D,  9, Se, 25); ROUND2(D, A, B, C, 14, Sf, 26);
+       ROUND2(C, D, A, B,  3, Sg, 27); ROUND2(B, C, D, A,  8, Sh, 28);
+       ROUND2(A, B, C, D, 13, Se, 29); ROUND2(D, A, B, C,  2, Sf, 30);
+       ROUND2(C, D, A, B,  7, Sg, 31); ROUND2(B, C, D, A, 12, Sh, 32);
+
+       ROUND3(A, B, C, D,  5, Si, 33); ROUND3(D, A, B, C,  8, Sj, 34);
+       ROUND3(C, D, A, B, 11, Sk, 35); ROUND3(B, C, D, A, 14, Sl, 36);
+       ROUND3(A, B, C, D,  1, Si, 37); ROUND3(D, A, B, C,  4, Sj, 38);
+       ROUND3(C, D, A, B,  7, Sk, 39); ROUND3(B, C, D, A, 10, Sl, 40);
+       ROUND3(A, B, C, D, 13, Si, 41); ROUND3(D, A, B, C,  0, Sj, 42);
+       ROUND3(C, D, A, B,  3, Sk, 43); ROUND3(B, C, D, A,  6, Sl, 44);
+       ROUND3(A, B, C, D,  9, Si, 45); ROUND3(D, A, B, C, 12, Sj, 46);
+       ROUND3(C, D, A, B, 15, Sk, 47); ROUND3(B, C, D, A,  2, Sl, 48);
+       
+       ROUND4(A, B, C, D,  0, Sm, 49); ROUND4(D, A, B, C,  7, Sn, 50); 
+       ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A,  5, Sp, 52); 
+       ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C,  3, Sn, 54); 
+       ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A,  1, Sp, 56); 
+       ROUND4(A, B, C, D,  8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58); 
+       ROUND4(C, D, A, B,  6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60); 
+       ROUND4(A, B, C, D,  4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62); 
+       ROUND4(C, D, A, B,  2, So, 63); ROUND4(B, C, D, A,  9, Sp, 64);
+
+       ctxt->md5_sta += A;
+       ctxt->md5_stb += B;
+       ctxt->md5_stc += C;
+       ctxt->md5_std += D;
+}
+
+/* From RFC 2104 */
+void
+hmac_md5(text, text_len, key, key_len, digest)
+unsigned char*  text;                  /* pointer to data stream */
+int             text_len;              /* length of data stream */
+unsigned char*  key;                   /* pointer to authentication key */
+int             key_len;               /* length of authentication key */
+uint8_t *       digest;                        /* caller digest to be filled in */
+
+{
+    MD5_CTX context;
+    unsigned char k_ipad[65];    /* inner padding -
+                                * key XORd with ipad
+                                */
+    unsigned char k_opad[65];    /* outer padding -
+                                * key XORd with opad
+                                */
+    unsigned char tk[16];
+    int i;
+    /* if key is longer than 64 bytes reset it to key=MD5(key) */
+    if (key_len > 64) {
+
+       MD5_CTX      tctx;
+
+       MD5Init(&tctx);
+       MD5Update(&tctx, key, key_len);
+       MD5Final(tk, &tctx);
+
+       key = tk;
+       key_len = 16;
+    }
+
+    /*
+     * the HMAC_MD5 transform looks like:
+     *
+     * MD5(K XOR opad, MD5(K XOR ipad, text))
+     *
+     * where K is an n byte key
+     * ipad is the byte 0x36 repeated 64 times
+     * opad is the byte 0x5c repeated 64 times
+     * and text is the data being protected
+     */
+
+    /* start out by storing key in pads */
+    bzero( k_ipad, sizeof k_ipad);
+    bzero( k_opad, sizeof k_opad);
+    bcopy( key, k_ipad, key_len);
+    bcopy( key, k_opad, key_len);
+
+    /* XOR key with ipad and opad values */
+    for (i=0; i<64; i++) {
+       k_ipad[i] ^= 0x36;
+       k_opad[i] ^= 0x5c;
+    }
+    /*
+     * perform inner MD5
+     */
+    MD5Init(&context);                 /* init context for 1st
+                                        * pass */
+    MD5Update(&context, k_ipad, 64);   /* start with inner pad */
+    MD5Update(&context, text, text_len); /* then text of datagram */
+    MD5Final(digest, &context);        /* finish up 1st pass */
+    /*
+     * perform outer MD5
+     */
+    MD5Init(&context);                 /* init context for 2nd
+                                        * pass */
+    MD5Update(&context, k_opad, 64);   /* start with outer pad */
+    MD5Update(&context, digest, 16);   /* then results of 1st
+                                        * hash */
+    MD5Final(digest, &context);        /* finish up 2nd pass */
+}
diff --git a/lib/md5.h b/lib/md5.h
new file mode 100644 (file)
index 0000000..4e5ffbd
--- /dev/null
+++ b/lib/md5.h
@@ -0,0 +1,89 @@
+/* $USAGI: md5.h,v 1.2 2000/11/02 11:59:25 yoshfuji Exp $ */
+/*     $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $   */
+/*     $Id: md5.h,v 1.3 2006/01/17 17:40:45 paul Exp $ */
+
+/*
+ * Copyright (C) 2004 6WIND
+ *                          <Vincent.Jardin@6WIND.com>
+ * All rights reserved.
+ *
+ * This MD5 code is Big endian and Little Endian compatible.
+ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBZEBRA_MD5_H_
+#define _LIBZEBRA_MD5_H_
+
+#define MD5_BUFLEN     64
+
+typedef struct {
+       union {
+               uint32_t        md5_state32[4];
+               uint8_t md5_state8[16];
+       } md5_st;
+
+#define md5_sta                md5_st.md5_state32[0]
+#define md5_stb                md5_st.md5_state32[1]
+#define md5_stc                md5_st.md5_state32[2]
+#define md5_std                md5_st.md5_state32[3]
+#define md5_st8                md5_st.md5_state8
+
+       union {
+               uint64_t        md5_count64;
+               uint8_t md5_count8[8];
+       } md5_count;
+#define md5_n  md5_count.md5_count64
+#define md5_n8 md5_count.md5_count8
+
+       uint    md5_i;
+       uint8_t md5_buf[MD5_BUFLEN];
+} md5_ctxt;
+
+extern void md5_init (md5_ctxt *);
+extern void md5_loop (md5_ctxt *, const void *, u_int);
+extern void md5_pad (md5_ctxt *);
+extern void md5_result (uint8_t *, md5_ctxt *);
+
+/* compatibility */
+#define MD5_CTX                md5_ctxt
+#define MD5Init(x)     md5_init((x))
+#define MD5Update(x, y, z)     md5_loop((x), (y), (z))
+#define MD5Final(x, y) \
+do {                           \
+       md5_pad((y));           \
+       md5_result((x), (y));   \
+} while (0)
+
+/* From RFC 2104 */
+void hmac_md5(unsigned char* text, int text_len, unsigned char* key,
+              int key_len, uint8_t *digest);
+
+#endif /* ! _LIBZEBRA_MD5_H_*/
diff --git a/lib/memory.c b/lib/memory.c
new file mode 100644 (file)
index 0000000..b8305dd
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Memory management routine
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
+#if !defined(HAVE_STDLIB_H) || (defined(GNU_LINUX) && defined(HAVE_MALLINFO))
+#include <malloc.h>
+#endif /* !HAVE_STDLIB_H || HAVE_MALLINFO */
+
+#include "log.h"
+#include "memory.h"
+
+static void alloc_inc (int);
+static void alloc_dec (int);
+static void log_memstats(int log_priority);
+
+static const struct message mstr [] =
+{
+  { MTYPE_THREAD, "thread" },
+  { MTYPE_THREAD_MASTER, "thread_master" },
+  { MTYPE_VECTOR, "vector" },
+  { MTYPE_VECTOR_INDEX, "vector_index" },
+  { MTYPE_IF, "interface" },
+  { 0, NULL },
+};
+
+/* Fatal memory allocation error occured. */
+static void __attribute__ ((noreturn))
+zerror (const char *fname, int type, size_t size)
+{
+  zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n", 
+           fname, lookup (mstr, type), (int) size, safe_strerror(errno));
+  log_memstats(LOG_WARNING);
+  /* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since
+     that function should definitely be safe in an OOM condition.  But
+     unfortunately zlog_backtrace_sigsafe does not support syslog logging at
+     this time... */
+  zlog_backtrace(LOG_WARNING);
+  abort();
+}
+
+/*
+ * Allocate memory of a given size, to be tracked by a given type.
+ * Effects: Returns a pointer to usable memory.  If memory cannot
+ * be allocated, aborts execution.
+ */
+void *
+zmalloc (int type, size_t size)
+{
+  void *memory;
+
+  memory = malloc (size);
+
+  if (memory == NULL)
+    zerror ("malloc", type, size);
+
+  alloc_inc (type);
+
+  return memory;
+}
+
+/*
+ * Allocate memory as in zmalloc, and also clear the memory.
+ * Add an extra 'z' prefix to function name to avoid collision when linking
+ * statically with zlib that exports the 'zcalloc' symbol.
+ */
+void *
+zzcalloc (int type, size_t size)
+{
+  void *memory;
+
+  memory = calloc (1, size);
+
+  if (memory == NULL)
+    zerror ("calloc", type, size);
+
+  alloc_inc (type);
+
+  return memory;
+}
+
+/* 
+ * Given a pointer returned by zmalloc or zzcalloc, free it and
+ * return a pointer to a new size, basically acting like realloc().
+ * Requires: ptr was returned by zmalloc, zzcalloc, or zrealloc with the
+ * same type.
+ * Effects: Returns a pointer to the new memory, or aborts.
+ */
+void *
+zrealloc (int type, void *ptr, size_t size)
+{
+  void *memory;
+
+  if (ptr == NULL)              /* is really alloc */
+      return zzcalloc(type, size);
+
+  memory = realloc (ptr, size);
+  if (memory == NULL)
+    zerror ("realloc", type, size);
+  if (ptr == NULL)
+    alloc_inc (type);
+
+  return memory;
+}
+
+/*
+ * Free memory allocated by z*alloc or zstrdup.
+ * Requires: ptr was returned by zmalloc, zzcalloc, or zrealloc with the
+ * same type.
+ * Effects: The memory is freed and may no longer be referenced.
+ */
+void
+zfree (int type, void *ptr)
+{
+  if (ptr != NULL)
+    {
+      alloc_dec (type);
+      free (ptr);
+    }
+}
+
+/*
+ * Duplicate a string, counting memory usage by type.
+ * Effects: The string is duplicated, and the return value must
+ * eventually be passed to zfree with the same type.  The function will
+ * succeed or abort.
+ */
+char *
+zstrdup (int type, const char *str)
+{
+  void *dup;
+
+  dup = strdup (str);
+  if (dup == NULL)
+    zerror ("strdup", type, strlen (str));
+  alloc_inc (type);
+  return dup;
+}
+
+#ifdef MEMORY_LOG
+static struct 
+{
+  const char *name;
+  long alloc;
+  unsigned long t_malloc;
+  unsigned long c_malloc;
+  unsigned long t_calloc;
+  unsigned long c_calloc;
+  unsigned long t_realloc;
+  unsigned long t_free;
+  unsigned long c_strdup;
+} mstat [MTYPE_MAX];
+
+static void
+mtype_log (char *func, void *memory, const char *file, int line, int type)
+{
+  zlog_debug ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line);
+}
+
+void *
+mtype_zmalloc (const char *file, int line, int type, size_t size)
+{
+  void *memory;
+
+  mstat[type].c_malloc++;
+  mstat[type].t_malloc++;
+
+  memory = zmalloc (type, size);
+  mtype_log ("zmalloc", memory, file, line, type);
+
+  return memory;
+}
+
+void *
+mtype_zcalloc (const char *file, int line, int type, size_t size)
+{
+  void *memory;
+
+  mstat[type].c_calloc++;
+  mstat[type].t_calloc++;
+
+  memory = zzcalloc (type, size);
+  mtype_log ("xcalloc", memory, file, line, type);
+
+  return memory;
+}
+
+void *
+mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
+{
+  void *memory;
+
+  /* Realloc need before allocated pointer. */
+  mstat[type].t_realloc++;
+
+  memory = zrealloc (type, ptr, size);
+
+  mtype_log ("xrealloc", memory, file, line, type);
+
+  return memory;
+}
+
+/* Important function. */
+void 
+mtype_zfree (const char *file, int line, int type, void *ptr)
+{
+  mstat[type].t_free++;
+
+  mtype_log ("xfree", ptr, file, line, type);
+
+  zfree (type, ptr);
+}
+
+char *
+mtype_zstrdup (const char *file, int line, int type, const char *str)
+{
+  char *memory;
+
+  mstat[type].c_strdup++;
+
+  memory = zstrdup (type, str);
+  
+  mtype_log ("xstrdup", memory, file, line, type);
+
+  return memory;
+}
+#else
+static struct 
+{
+  char *name;
+  long alloc;
+} mstat [MTYPE_MAX];
+#endif /* MEMORY_LOG */
+
+/* Increment allocation counter. */
+static void
+alloc_inc (int type)
+{
+  mstat[type].alloc++;
+}
+
+/* Decrement allocation counter. */
+static void
+alloc_dec (int type)
+{
+  mstat[type].alloc--;
+}
+
+/* Looking up memory status from vty interface. */
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+
+static void
+log_memstats(int pri)
+{
+  struct mlist *ml;
+
+  for (ml = mlists; ml->list; ml++)
+    {
+      struct memory_list *m;
+
+      zlog (NULL, pri, "Memory utilization in module %s:", ml->name);
+      for (m = ml->list; m->index >= 0; m++)
+       if (m->index && mstat[m->index].alloc)
+         zlog (NULL, pri, "  %-30s: %10ld", m->format, mstat[m->index].alloc);
+    }
+}
+
+void
+log_memstats_stderr (const char *prefix)
+{
+  struct mlist *ml;
+  struct memory_list *m;
+  int i;
+  int j = 0;
+
+  for (ml = mlists; ml->list; ml++)
+    {
+      i = 0;
+
+      for (m = ml->list; m->index >= 0; m++)
+        if (m->index && mstat[m->index].alloc)
+          {
+            if (!i)
+              fprintf (stderr,
+                       "%s: memstats: Current memory utilization in module %s:\n",
+                       prefix,
+                       ml->name);
+            fprintf (stderr,
+                     "%s: memstats:  %-30s: %10ld%s\n",
+                     prefix,
+                     m->format,
+                     mstat[m->index].alloc,
+                     mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : "");
+            i = j = 1;
+          }
+    }
+
+  if (j)
+    fprintf (stderr,
+             "%s: memstats: NOTE: If configuration exists, utilization may be "
+             "expected.\n",
+             prefix);
+  else
+    fprintf (stderr,
+             "%s: memstats: No remaining tracked memory utilization.\n",
+             prefix);
+}
+
+static void
+show_separator(struct vty *vty)
+{
+  vty_out (vty, "-----------------------------\r\n");
+}
+
+static int
+show_memory_vty (struct vty *vty, struct memory_list *list)
+{
+  struct memory_list *m;
+  int needsep = 0;
+
+  for (m = list; m->index >= 0; m++)
+    if (m->index == 0)
+      {
+       if (needsep)
+         {
+           show_separator (vty);
+           needsep = 0;
+         }
+      }
+    else if (mstat[m->index].alloc)
+      {
+       vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc);
+       needsep = 1;
+      }
+  return needsep;
+}
+
+#ifdef HAVE_MALLINFO
+static int
+show_memory_mallinfo (struct vty *vty)
+{
+  struct mallinfo minfo = mallinfo();
+  char buf[MTYPE_MEMSTR_LEN];
+  
+  vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE);
+  vty_out (vty, "  Total heap allocated:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena),
+           VTY_NEWLINE);
+  vty_out (vty, "  Holding block headers: %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.hblkhd),
+           VTY_NEWLINE);
+  vty_out (vty, "  Used small blocks:     %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.usmblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Used ordinary blocks:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.uordblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Free small blocks:     %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fsmblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Free ordinary blocks:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fordblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Ordinary blocks:       %ld%s",
+           (unsigned long)minfo.ordblks,
+           VTY_NEWLINE);
+  vty_out (vty, "  Small blocks:          %ld%s",
+           (unsigned long)minfo.smblks,
+           VTY_NEWLINE);
+  vty_out (vty, "  Holding blocks:        %ld%s",
+           (unsigned long)minfo.hblks,
+           VTY_NEWLINE);
+  vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s",
+           VTY_NEWLINE);
+  return 1;
+}
+#endif /* HAVE_MALLINFO */
+
+DEFUN (show_memory,
+       show_memory_cmd,
+       "show memory",
+       "Show running system information\n"
+       "Memory statistics\n")
+{
+  struct mlist *ml;
+  int needsep = 0;
+  
+#ifdef HAVE_MALLINFO
+  needsep = show_memory_mallinfo (vty);
+#endif /* HAVE_MALLINFO */
+  
+  for (ml = mlists; ml->list; ml++)
+    {
+      if (needsep)
+       show_separator (vty);
+      needsep = show_memory_vty (vty, ml->list);
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+void
+memory_init (void)
+{
+  install_element (RESTRICTED_NODE, &show_memory_cmd);
+
+  install_element (VIEW_NODE, &show_memory_cmd);
+}
+
+/* Stats querying from users */
+/* Return a pointer to a human friendly string describing
+ * the byte count passed in. E.g:
+ * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
+ * Up to 4 significant figures will be given.
+ * The pointer returned may be NULL (indicating an error)
+ * or point to the given buffer, or point to static storage.
+ */
+const char *
+mtype_memstr (char *buf, size_t len, unsigned long bytes)
+{
+  unsigned int m, k;
+
+  /* easy cases */
+  if (!bytes)
+    return "0 bytes";
+  if (bytes == 1)
+    return "1 byte";
+
+  /*
+   * When we pass the 2gb barrier mallinfo() can no longer report
+   * correct data so it just does something odd...
+   * Reporting like Terrabytes of data.  Which makes users...
+   * edgy.. yes edgy that's the term for it.
+   * So let's just give up gracefully
+   */
+  if (bytes > 0x7fffffff)
+    return "> 2GB";
+
+  m = bytes >> 20;
+  k = bytes >> 10;
+
+ if (m > 10)
+    {
+      if (bytes & (1 << 19))
+        m++;
+      snprintf (buf, len, "%d MiB", m);
+    }
+  else if (k > 10)
+    {
+      if (bytes & (1 << 9))
+        k++;
+      snprintf (buf, len, "%d KiB", k);
+    }
+  else
+    snprintf (buf, len, "%ld bytes", bytes);
+  
+  return buf;
+}
+
+unsigned long
+mtype_stats_alloc (int type)
+{
+  return mstat[type].alloc;
+}
diff --git a/lib/memory.h b/lib/memory.h
new file mode 100644 (file)
index 0000000..5013529
--- /dev/null
@@ -0,0 +1,96 @@
+/* Memory management routine
+   Copyright (C) 1998 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _ZEBRA_MEMORY_H
+#define _ZEBRA_MEMORY_H
+
+#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
+
+/* For pretty printing of memory allocate information. */
+struct memory_list
+{
+  int index;
+  const char *format;
+};
+
+struct mlist {
+  struct memory_list *list;
+  const char *name;
+};
+#include "lib/memtypes.h"
+
+extern struct mlist mlists[];
+
+/* #define MEMORY_LOG */
+#ifdef MEMORY_LOG
+#define XMALLOC(mtype, size) \
+  mtype_zmalloc (__FILE__, __LINE__, (mtype), (size))
+#define XCALLOC(mtype, size) \
+  mtype_zcalloc (__FILE__, __LINE__, (mtype), (size))
+#define XREALLOC(mtype, ptr, size)  \
+  mtype_zrealloc (__FILE__, __LINE__, (mtype), (ptr), (size))
+#define XFREE(mtype, ptr) \
+  do { \
+    mtype_zfree (__FILE__, __LINE__, (mtype), (ptr)); \
+    ptr = NULL; } \
+  while (0)
+#define XSTRDUP(mtype, str) \
+  mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
+#else
+#define XMALLOC(mtype, size)       zmalloc ((mtype), (size))
+#define XCALLOC(mtype, size)       zzcalloc ((mtype), (size))
+#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size))
+#define XFREE(mtype, ptr)          do { \
+                                     zfree ((mtype), (ptr)); \
+                                     ptr = NULL; } \
+                                   while (0)
+#define XSTRDUP(mtype, str)        zstrdup ((mtype), (str))
+#endif /* MEMORY_LOG */
+
+/* Prototypes of memory function. */
+extern void *zmalloc (int type, size_t size);
+extern void *zzcalloc (int type, size_t size);
+extern void *zrealloc (int type, void *ptr, size_t size);
+extern void  zfree (int type, void *ptr);
+extern char *zstrdup (int type, const char *str);
+
+extern void *mtype_zmalloc (const char *file, int line, int type, size_t size);
+
+extern void *mtype_zcalloc (const char *file, int line, int type, size_t size);
+
+extern void *mtype_zrealloc (const char *file, int line, int type, void *ptr,
+                            size_t size);
+
+extern void mtype_zfree (const char *file, int line, int type,
+                        void *ptr);
+
+extern char *mtype_zstrdup (const char *file, int line, int type,
+                           const char *str);
+extern void memory_init (void);
+extern void log_memstats_stderr (const char *);
+
+/* return number of allocations outstanding for the type */
+extern unsigned long mtype_stats_alloc (int);
+
+/* Human friendly string for given byte count */
+#define MTYPE_MEMSTR_LEN 20
+extern const char *mtype_memstr (char *, size_t, unsigned long);
+#endif /* _ZEBRA_MEMORY_H */
diff --git a/lib/memtypes.awk b/lib/memtypes.awk
new file mode 100644 (file)
index 0000000..bd13327
--- /dev/null
@@ -0,0 +1,87 @@
+###
+# Copyright (C) Paul Jakma 2005
+#
+# This file is part of Quagga.
+#
+# Quagga is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any
+# later version.
+#
+# Quagga is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Quagga; see the file COPYING.  If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.  
+###
+#
+# Scan a file of memory definitions (see eg memtypes.c) and generate
+# a corresponding header file with an enum of the MTYPE's and declarations
+# for the struct memory_list arrays
+#
+# struct memory_list's must be declared as:
+# '\nstruct memory_list memory_list_<name>[] .....'
+#
+# Each MTYPE_ within the definition must the second token on the line,
+# tokens being delineated by whitespace. It may only consist of the set of
+# characters [[:upper:]_[:digit:]]. Eg:
+#
+# '\n  {  MTYPE_AWESOME_IPV8 , "Amazing new protocol, says genius" {}..boo'
+#
+# We try to ignore lines whose first token is /* or *, ie C comment lines.
+# So the following should work fine:
+#
+# '/* This is the best memory_list ever!
+# ' * It's got all my MTYPE's */
+# '
+# 'struct memory_list memory_list_my_amazing_mlist[] = =
+# '{
+# '  { MTYPE_DONGLE, "Dongle widget" }
+# '  { MTYPE_FROB, "Frobulator" },
+# '{  MTYPE_WIPPLE, "Wipple combombulator"}
+# '}}}
+#
+# Even if it isn't quite a valid C declaration.
+#
+
+BEGIN {
+       mlistregex = "memory_list_(.*)\\[\\]";
+       mtyperegex = "^(MTYPE_[[:upper:]_[:digit:]]+).*";
+       header = "/* Auto-generated from memtypes.c by " ARGV[0] ". */\n";
+       header = header "/* Do not edit! */\n";
+       header = header "\n#ifndef _QUAGGA_MEMTYPES_H\n";
+       header = header "#define _QUAGGA_MEMTYPES_H\n";
+       footer = "\n#endif /* _QUAGGA_MEMTYPES_H */\n\n";
+       mlistformat = "extern struct memory_list memory_list_%s[];";
+       printf ("%s\n", header);
+}
+
+# catch lines beginning with 'struct memory list ' and try snag the
+# memory_list name. Has to be 3rd field.
+($0 ~ /^struct memory_list /) && (NF >= 3) {
+       mlists[lcount++] = gensub(mlistregex, "\\1", "g",$3);
+}
+
+# snag the MTYPE, it must self-standing and the second field,
+# though we do manage to tolerate the , C seperator being appended
+($1 !~ /^\/?\*/) && ($2 ~ /^MTYPE_/) { 
+       mtype[tcount++] = gensub(mtyperegex, "\\1", "g", $2);
+} 
+
+END {
+       printf("enum\n{\n  MTYPE_TMP = 1,\n"); 
+       for (i = 0; i < tcount; i++) {
+               if (mtype[i] != "" && mtype[i] != "MTYPE_TMP")
+                       printf ("  %s,\n", mtype[i]);
+       }
+       printf ("  MTYPE_MAX,\n};\n\n");
+       for (i = 0; i < lcount; i++) {
+               if (mlists[i] != "")
+                       printf (mlistformat "\n", mlists[i]);
+       }
+       printf (footer);
+}
diff --git a/lib/memtypes.c b/lib/memtypes.c
new file mode 100644 (file)
index 0000000..e5b3546
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Memory type definitions. This file is parsed by memtypes.awk to extract
+ * MTYPE_ and memory_list_.. information in order to autogenerate 
+ * memtypes.h.
+ *
+ * The script is sensitive to the format (though not whitespace), see
+ * the top of memtypes.awk for more details.
+ */
+
+#include "zebra.h"
+#include "memory.h"
+
+struct memory_list memory_list_lib[] =
+{
+  { MTYPE_TMP,                 "Temporary memory"              },
+  { MTYPE_STRVEC,              "String vector"                 },
+  { MTYPE_VECTOR,              "Vector"                        },
+  { MTYPE_VECTOR_INDEX,                "Vector index"                  },
+  { MTYPE_LINK_LIST,           "Link List"                     },
+  { MTYPE_LINK_NODE,           "Link Node"                     },
+  { MTYPE_THREAD,              "Thread"                        },
+  { MTYPE_THREAD_MASTER,       "Thread master"                 },
+  { MTYPE_THREAD_STATS,                "Thread stats"                  },
+  { MTYPE_VTY,                 "VTY"                           },
+  { MTYPE_VTY_OUT_BUF,         "VTY output buffer"             },
+  { MTYPE_VTY_HIST,            "VTY history"                   },
+  { MTYPE_IF,                  "Interface"                     },
+  { MTYPE_CONNECTED,           "Connected"                     },
+  { MTYPE_CONNECTED_LABEL,     "Connected interface label"     },
+  { MTYPE_BUFFER,              "Buffer"                        },
+  { MTYPE_BUFFER_DATA,         "Buffer data"                   },
+  { MTYPE_STREAM,              "Stream"                        },
+  { MTYPE_STREAM_DATA,         "Stream data"                   },
+  { MTYPE_STREAM_FIFO,         "Stream FIFO"                   },
+  { MTYPE_PREFIX,              "Prefix"                        },
+  { MTYPE_PREFIX_IPV4,         "Prefix IPv4"                   },
+  { MTYPE_PREFIX_IPV6,         "Prefix IPv6"                   },
+  { MTYPE_HASH,                        "Hash"                          },
+  { MTYPE_HASH_BACKET,         "Hash Bucket"                   },
+  { MTYPE_HASH_INDEX,          "Hash Index"                    },
+  { MTYPE_ROUTE_TABLE,         "Route table"                   },
+  { MTYPE_ROUTE_NODE,          "Route node"                    },
+  { MTYPE_DISTRIBUTE,          "Distribute list"               },
+  { MTYPE_DISTRIBUTE_IFNAME,   "Dist-list ifname"              },
+  { MTYPE_ACCESS_LIST,         "Access List"                   },
+  { MTYPE_ACCESS_LIST_STR,     "Access List Str"               },
+  { MTYPE_ACCESS_FILTER,       "Access Filter"                 },
+  { MTYPE_PREFIX_LIST,         "Prefix List"                   },
+  { MTYPE_PREFIX_LIST_ENTRY,   "Prefix List Entry"             },
+  { MTYPE_PREFIX_LIST_STR,     "Prefix List Str"               },
+  { MTYPE_ROUTE_MAP,           "Route map"                     },
+  { MTYPE_ROUTE_MAP_NAME,      "Route map name"                },
+  { MTYPE_ROUTE_MAP_INDEX,     "Route map index"               },
+  { MTYPE_ROUTE_MAP_RULE,      "Route map rule"                },
+  { MTYPE_ROUTE_MAP_RULE_STR,  "Route map rule str"            },
+  { MTYPE_ROUTE_MAP_COMPILED,  "Route map compiled"            },
+  { MTYPE_CMD_TOKENS,          "Command desc"                  },
+  { MTYPE_KEY,                 "Key"                           },
+  { MTYPE_KEYCHAIN,            "Key chain"                     },
+  { MTYPE_IF_RMAP,             "Interface route map"           },
+  { MTYPE_IF_RMAP_NAME,                "I.f. route map name",          },
+  { MTYPE_SOCKUNION,           "Socket union"                  },
+  { MTYPE_PRIVS,               "Privilege information"         },
+  { MTYPE_ZLOG,                        "Logging"                       },
+  { MTYPE_ZCLIENT,             "Zclient"                       },
+  { MTYPE_WORK_QUEUE,          "Work queue"                    },
+  { MTYPE_WORK_QUEUE_ITEM,     "Work queue item"               },
+  { MTYPE_WORK_QUEUE_NAME,     "Work queue name string"        },
+  { MTYPE_PQUEUE,              "Priority queue"                },
+  { MTYPE_PQUEUE_DATA,         "Priority queue data"           },
+  { MTYPE_HOST,                        "Host config"                   },
+  { MTYPE_VRF,                 "VRF"                           },
+  { MTYPE_VRF_NAME,            "VRF name"                      },
+  { MTYPE_VRF_BITMAP,          "VRF bit-map"                   },
+  { MTYPE_IF_LINK_PARAMS,       "Informational Link Parameters" },
+  { -1, NULL },
+};
+
+struct memory_list memory_list_zebra[] = 
+{
+  { MTYPE_RTADV_PREFIX,                "Router Advertisement Prefix"   },
+  { MTYPE_ZEBRA_VRF,           "ZEBRA VRF"                             },
+  { MTYPE_NEXTHOP,             "Nexthop"                       },
+  { MTYPE_RIB,                 "RIB"                           },
+  { MTYPE_RIB_QUEUE,           "RIB process work queue"        },
+  { MTYPE_STATIC_ROUTE,                "Static route"                  },
+  { MTYPE_RIB_DEST,            "RIB destination"               },
+  { MTYPE_RIB_TABLE_INFO,      "RIB table info"                },
+  { MTYPE_NETLINK_NAME,        "Netlink name"                  },
+  { MTYPE_NETLINK_RCVBUF,      "Netlink receive buffer"        },
+  { MTYPE_RNH,                 "Nexthop tracking object"       },
+  { -1, NULL },
+};
+
+struct memory_list memory_list_bgp[] =
+{
+  { MTYPE_BGP,                 "BGP instance"                  },
+  { MTYPE_BGP_LISTENER,                "BGP listen socket details"     },
+  { MTYPE_BGP_PEER,            "BGP peer"                      },
+  { MTYPE_BGP_PEER_HOST,       "BGP peer hostname"             },
+  { MTYPE_PEER_GROUP,          "Peer group"                    },
+  { MTYPE_PEER_DESC,           "Peer description"              },
+  { MTYPE_PEER_PASSWORD,       "Peer password string"          },
+  { MTYPE_ATTR,                        "BGP attribute"                 },
+  { MTYPE_ATTR_EXTRA,          "BGP extra attributes"          },
+  { MTYPE_AS_PATH,             "BGP aspath"                    },
+  { MTYPE_AS_SEG,              "BGP aspath seg"                },
+  { MTYPE_AS_SEG_DATA,         "BGP aspath segment data"       },
+  { MTYPE_AS_STR,              "BGP aspath str"                },
+  { 0, NULL },
+  { MTYPE_BGP_TABLE,           "BGP table"                     },
+  { MTYPE_BGP_NODE,            "BGP node"                      },
+  { MTYPE_BGP_ROUTE,           "BGP route"                     },
+  { MTYPE_BGP_ROUTE_EXTRA,     "BGP ancillary route info"      },
+  { MTYPE_BGP_CONN,            "BGP connected"                 },
+  { MTYPE_BGP_STATIC,          "BGP static"                    },
+  { MTYPE_BGP_ADVERTISE_ATTR,  "BGP adv attr"                  },
+  { MTYPE_BGP_ADVERTISE,       "BGP adv"                       },
+  { MTYPE_BGP_SYNCHRONISE,     "BGP synchronise"               },
+  { MTYPE_BGP_ADJ_IN,          "BGP adj in"                    },
+  { MTYPE_BGP_ADJ_OUT,         "BGP adj out"                   },
+  { MTYPE_BGP_MPATH_INFO,      "BGP multipath info"            },
+  { 0, NULL },
+  { MTYPE_AS_LIST,             "BGP AS list"                   },
+  { MTYPE_AS_FILTER,           "BGP AS filter"                 },
+  { MTYPE_AS_FILTER_STR,       "BGP AS filter str"             },
+  { 0, NULL },
+  { MTYPE_COMMUNITY,           "community"                     },
+  { MTYPE_COMMUNITY_VAL,       "community val"                 },
+  { MTYPE_COMMUNITY_STR,       "community str"                 },
+  { 0, NULL },
+  { MTYPE_ECOMMUNITY,          "extcommunity"                  },
+  { MTYPE_ECOMMUNITY_VAL,      "extcommunity val"              },
+  { MTYPE_ECOMMUNITY_STR,      "extcommunity str"              },
+  { 0, NULL },
+  { MTYPE_COMMUNITY_LIST,      "community-list"                },
+  { MTYPE_COMMUNITY_LIST_NAME, "community-list name"           },
+  { MTYPE_COMMUNITY_LIST_ENTRY,        "community-list entry"          },
+  { MTYPE_COMMUNITY_LIST_CONFIG,  "community-list config"      },
+  { MTYPE_COMMUNITY_LIST_HANDLER, "community-list handler"     },
+  { 0, NULL },
+  { MTYPE_CLUSTER,             "Cluster list"                  },
+  { MTYPE_CLUSTER_VAL,         "Cluster list val"              },
+  { 0, NULL },
+  { MTYPE_BGP_PROCESS_QUEUE,   "BGP Process queue"             },
+  { MTYPE_BGP_CLEAR_NODE_QUEUE, "BGP node clear queue"         },
+  { 0, NULL },
+  { MTYPE_TRANSIT,             "BGP transit attr"              },
+  { MTYPE_TRANSIT_VAL,         "BGP transit val"               },
+  { 0, NULL },
+  { MTYPE_BGP_DISTANCE,                "BGP distance"                  },
+  { MTYPE_BGP_NEXTHOP_CACHE,   "BGP nexthop"                   },
+  { MTYPE_BGP_CONFED_LIST,     "BGP confed list"               },
+  { MTYPE_PEER_UPDATE_SOURCE,  "BGP peer update interface"     },
+  { MTYPE_BGP_DAMP_INFO,       "Dampening info"                },
+  { MTYPE_BGP_DAMP_ARRAY,      "BGP Dampening array"           },
+  { MTYPE_BGP_REGEXP,          "BGP regexp"                    },
+  { MTYPE_BGP_AGGREGATE,       "BGP aggregate"                 },
+  { MTYPE_BGP_ADDR,            "BGP own address"               },
+  { MTYPE_ENCAP_TLV,           "ENCAP TLV",                    },
+  { MTYPE_LCOMMUNITY,           "Large Community",              },
+  { MTYPE_LCOMMUNITY_STR,       "Large Community str",          },
+  { MTYPE_LCOMMUNITY_VAL,       "Large Community val",          },
+  { -1, NULL }
+};
+
+struct memory_list memory_list_rip[] =
+{
+  { MTYPE_RIP,                "RIP structure"                  },
+  { MTYPE_RIP_INFO,           "RIP route info"                 },
+  { MTYPE_RIP_INTERFACE,      "RIP interface"                  },
+  { MTYPE_RIP_PEER,           "RIP peer"                       },
+  { MTYPE_RIP_OFFSET_LIST,    "RIP offset list"                        },
+  { MTYPE_RIP_DISTANCE,       "RIP distance"                   },
+  { -1, NULL }
+};
+
+struct memory_list memory_list_ripng[] =
+{
+  { MTYPE_RIPNG,              "RIPng structure"                        },
+  { MTYPE_RIPNG_ROUTE,        "RIPng route info"               },
+  { MTYPE_RIPNG_AGGREGATE,    "RIPng aggregate"                        },
+  { MTYPE_RIPNG_PEER,         "RIPng peer"                     },
+  { MTYPE_RIPNG_OFFSET_LIST,  "RIPng offset lst"               },
+  { MTYPE_RIPNG_RTE_DATA,     "RIPng rte data"                 },
+  { -1, NULL }
+};
+
+struct memory_list memory_list_babel[] =
+{
+  { MTYPE_BABEL,              "Babel structure"                        },
+  { MTYPE_BABEL_IF,           "Babel interface"                        },
+  { -1, NULL }
+};
+
+struct memory_list memory_list_ospf[] =
+{
+  { MTYPE_OSPF_TOP,           "OSPF top"                       },
+  { MTYPE_OSPF_AREA,          "OSPF area"                      },
+  { MTYPE_OSPF_AREA_RANGE,    "OSPF area range"                        },
+  { MTYPE_OSPF_NETWORK,       "OSPF network"                   },
+  { MTYPE_OSPF_NEIGHBOR_STATIC,"OSPF static nbr"               },
+  { MTYPE_OSPF_IF,            "OSPF interface"                 },
+  { MTYPE_OSPF_NEIGHBOR,      "OSPF neighbor"                  },
+  { MTYPE_OSPF_ROUTE,         "OSPF route"                     },
+  { MTYPE_OSPF_TMP,           "OSPF tmp mem"                   },
+  { MTYPE_OSPF_LSA,           "OSPF LSA"                       },
+  { MTYPE_OSPF_LSA_DATA,      "OSPF LSA data"                  },
+  { MTYPE_OSPF_LSDB,          "OSPF LSDB"                      },
+  { MTYPE_OSPF_PACKET,        "OSPF packet"                    },
+  { MTYPE_OSPF_FIFO,          "OSPF FIFO queue"                        },
+  { MTYPE_OSPF_VERTEX,        "OSPF vertex"                    },
+  { MTYPE_OSPF_VERTEX_PARENT, "OSPF vertex parent",            },
+  { MTYPE_OSPF_NEXTHOP,       "OSPF nexthop"                   },
+  { MTYPE_OSPF_PATH,         "OSPF path"                       },
+  { MTYPE_OSPF_VL_DATA,       "OSPF VL data"                   },
+  { MTYPE_OSPF_CRYPT_KEY,     "OSPF crypt key"                 },
+  { MTYPE_OSPF_EXTERNAL_INFO, "OSPF ext. info"                 },
+  { MTYPE_OSPF_DISTANCE,      "OSPF distance"                  },
+  { MTYPE_OSPF_IF_INFO,       "OSPF if info"                   },
+  { MTYPE_OSPF_IF_PARAMS,     "OSPF if params"                 },
+  { MTYPE_OSPF_MESSAGE,                "OSPF message"                  },
+  { MTYPE_OSPF_MPLS_TE,       "OSPF MPLS parameters"            },
+  { MTYPE_OSPF_PCE_PARAMS,    "OSPF PCE parameters"             },
+  { -1, NULL },
+};
+
+struct memory_list memory_list_ospf6[] =
+{
+  { MTYPE_OSPF6_TOP,          "OSPF6 top"                      },
+  { MTYPE_OSPF6_AREA,         "OSPF6 area"                     },
+  { MTYPE_OSPF6_IF,           "OSPF6 interface"                        },
+  { MTYPE_OSPF6_NEIGHBOR,     "OSPF6 neighbor"                 },
+  { MTYPE_OSPF6_ROUTE,        "OSPF6 route"                    },
+  { MTYPE_OSPF6_PREFIX,       "OSPF6 prefix"                   },
+  { MTYPE_OSPF6_MESSAGE,      "OSPF6 message"                  },
+  { MTYPE_OSPF6_LSA,          "OSPF6 LSA"                      },
+  { MTYPE_OSPF6_LSA_SUMMARY,  "OSPF6 LSA summary"              },
+  { MTYPE_OSPF6_LSDB,         "OSPF6 LSA database"             },
+  { MTYPE_OSPF6_VERTEX,       "OSPF6 vertex"                   },
+  { MTYPE_OSPF6_SPFTREE,      "OSPF6 SPF tree"                 },
+  { MTYPE_OSPF6_NEXTHOP,      "OSPF6 nexthop"                  },
+  { MTYPE_OSPF6_EXTERNAL_INFO,"OSPF6 ext. info"                        },
+  { MTYPE_OSPF6_OTHER,        "OSPF6 other"                    },
+  { MTYPE_OSPF6_DISTANCE,     "OSPF6 distance"                 },
+  { -1, NULL },
+};
+
+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_DICT,          "ISIS dictionary"                        },
+  { MTYPE_ISIS_DICT_NODE,     "ISIS dictionary node"           },
+  { MTYPE_ISIS_MPLS_TE,       "ISIS MPLS_TE parameters"         },
+  { -1, NULL },
+};
+
+struct memory_list memory_list_pim[] =
+{
+  { MTYPE_PIM_CHANNEL_OIL,       "PIM SSM (S,G) channel OIL"      },
+  { MTYPE_PIM_INTERFACE,         "PIM interface"                 },
+  { MTYPE_PIM_IGMP_JOIN,         "PIM interface IGMP static join" },
+  { MTYPE_PIM_IGMP_SOCKET,       "PIM interface IGMP socket"      },
+  { MTYPE_PIM_IGMP_GROUP,        "PIM interface IGMP group"       },
+  { MTYPE_PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source"      },
+  { MTYPE_PIM_NEIGHBOR,          "PIM interface neighbor"         },
+  { MTYPE_PIM_IFCHANNEL,         "PIM interface (S,G) state"      },
+  { MTYPE_PIM_UPSTREAM,          "PIM upstream (S,G) state"       },
+  { MTYPE_PIM_SSMPINGD,          "PIM sspimgd socket"             },
+  { MTYPE_PIM_STATIC_ROUTE,      "PIM Static Route"               },
+  { -1, NULL },
+};
+
+struct memory_list memory_list_nhrp[] =
+{
+  { MTYPE_NHRP_IF,             "NHRP interface"                },
+  { MTYPE_NHRP_VC,             "NHRP virtual connection"       },
+  { MTYPE_NHRP_PEER,           "NHRP peer entry"               },
+  { MTYPE_NHRP_CACHE,          "NHRP cache entry"              },
+  { MTYPE_NHRP_NHS,            "NHRP next hop server"          },
+  { MTYPE_NHRP_REGISTRATION,   "NHRP registration entries"     },
+  { MTYPE_NHRP_SHORTCUT,       "NHRP shortcut"                 },
+  { MTYPE_NHRP_ROUTE,          "NHRP routing entry"            },
+  { -1, NULL }
+};
+
+struct memory_list memory_list_vtysh[] =
+{
+  { MTYPE_VTYSH_CONFIG,                "Vtysh configuration",          },
+  { MTYPE_VTYSH_CONFIG_LINE,   "Vtysh configuration line"      },
+  { -1, NULL },
+};
+
+struct mlist mlists[] __attribute__ ((unused)) = {
+  { memory_list_lib,   "LIB"   },
+  { memory_list_zebra, "ZEBRA" },
+  { memory_list_rip,   "RIP"   },
+  { memory_list_ripng, "RIPNG" },
+  { memory_list_ospf,  "OSPF"  },
+  { memory_list_ospf6, "OSPF6" },
+  { memory_list_isis,  "ISIS"  },
+  { memory_list_bgp,   "BGP"   },
+  { memory_list_pim,   "PIM"   },
+  { memory_list_nhrp,  "NHRP"  },
+  { NULL, NULL},
+};
diff --git a/lib/network.c b/lib/network.c
new file mode 100644 (file)
index 0000000..b982640
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Network library.
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "network.h"
+
+/* Read nbytes from fd and store into ptr. */
+int
+readn (int fd, u_char *ptr, int nbytes)
+{
+  int nleft;
+  int nread;
+
+  nleft = nbytes;
+
+  while (nleft > 0) 
+    {
+      nread = read (fd, ptr, nleft);
+
+      if (nread < 0) 
+       return (nread);
+      else
+       if (nread == 0) 
+         break;
+
+      nleft -= nread;
+      ptr += nread;
+    }
+
+  return nbytes - nleft;
+}  
+
+/* Write nbytes from ptr to fd. */
+int
+writen(int fd, const u_char *ptr, int nbytes)
+{
+  int nleft;
+  int nwritten;
+
+  nleft = nbytes;
+
+  while (nleft > 0) 
+    {
+      nwritten = write(fd, ptr, nleft);
+      
+      if (nwritten <= 0) 
+       return (nwritten);
+
+      nleft -= nwritten;
+      ptr += nwritten;
+    }
+  return nbytes - nleft;
+}
+
+int
+set_nonblocking(int fd)
+{
+  int flags;
+
+  /* According to the Single UNIX Spec, the return value for F_GETFL should
+     never be negative. */
+  if ((flags = fcntl(fd, F_GETFL)) < 0)
+    {
+      zlog_warn("fcntl(F_GETFL) failed for fd %d: %s",
+               fd, safe_strerror(errno));
+      return -1;
+    }
+  if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0)
+    {
+      zlog_warn("fcntl failed setting fd %d non-blocking: %s",
+               fd, safe_strerror(errno));
+      return -1;
+    }
+  return 0;
+}
+
+float
+htonf (float host)
+{
+#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 0
+#warning "Unknown floating-point format on platform, htonf may break"
+#endif
+  u_int32_t lu1, lu2;
+  float convert;
+  
+  memcpy (&lu1, &host, sizeof (u_int32_t));
+  lu2 = htonl (lu1);
+  memcpy (&convert, &lu2, sizeof (u_int32_t));
+  return convert;
+}
+
+float
+ntohf (float net)
+{
+  return htonf (net);
+}
diff --git a/lib/network.h b/lib/network.h
new file mode 100644 (file)
index 0000000..0fcb575
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Network library header.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_NETWORK_H
+#define _ZEBRA_NETWORK_H
+
+/* Both readn and writen are deprecated and will be removed.  They are not
+   suitable for use with non-blocking file descriptors.
+ */
+extern int readn (int, u_char *, int);
+extern int writen (int, const u_char *, int);
+
+/* Set the file descriptor to use non-blocking I/O.  Returns 0 for success,
+   -1 on error. */
+extern int set_nonblocking(int fd);
+
+/* Does the I/O error indicate that the operation should be retried later? */
+#define ERRNO_IO_RETRY(EN) \
+       (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+extern float htonf (float);
+extern float ntohf (float);
+
+#endif /* _ZEBRA_NETWORK_H */
diff --git a/lib/nexthop.c b/lib/nexthop.c
new file mode 100644 (file)
index 0000000..5eb2182
--- /dev/null
@@ -0,0 +1,168 @@
+/* A generic nexthop structure
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "str.h"
+#include "command.h"
+#include "if.h"
+#include "log.h"
+#include "sockunion.h"
+#include "linklist.h"
+#include "thread.h"
+#include "prefix.h"
+#include "nexthop.h"
+
+/* check if nexthops are same, non-recursive */
+int
+nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2)
+{
+  if (next1->type != next2->type)
+    return 0;
+
+  switch (next1->type)
+    {
+    case NEXTHOP_TYPE_IPV4:
+    case NEXTHOP_TYPE_IPV4_IFINDEX:
+      if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4))
+       return 0;
+      if (next1->ifindex && (next1->ifindex != next2->ifindex))
+       return 0;
+      break;
+    case NEXTHOP_TYPE_IFINDEX:
+    case NEXTHOP_TYPE_IFNAME:
+      if (next1->ifindex != next2->ifindex)
+       return 0;
+      break;
+#ifdef HAVE_IPV6
+    case NEXTHOP_TYPE_IPV6:
+      if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6))
+       return 0;
+      break;
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+    case NEXTHOP_TYPE_IPV6_IFNAME:
+      if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6))
+       return 0;
+      if (next1->ifindex != next2->ifindex)
+       return 0;
+      break;
+#endif /* HAVE_IPV6 */
+    default:
+      /* do nothing */
+      break;
+    }
+  return 1;
+}
+
+/*
+ * nexthop_type_to_str
+ */
+const char *
+nexthop_type_to_str (enum nexthop_types_t nh_type)
+{
+  static const char *desc[] = {
+    "none",
+    "Directly connected",
+    "Interface route",
+    "IPv4 nexthop",
+    "IPv4 nexthop with ifindex",
+    "IPv4 nexthop with ifname",
+    "IPv6 nexthop",
+    "IPv6 nexthop with ifindex",
+    "IPv6 nexthop with ifname",
+    "Null0 nexthop",
+  };
+
+  if (nh_type >= ZEBRA_NUM_OF (desc))
+    return "<Invalid nh type>";
+
+  return desc[nh_type];
+}
+
+struct nexthop *
+nexthop_new (void)
+{
+  return XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+}
+
+/* Add nexthop to the end of a nexthop list.  */
+void
+nexthop_add (struct nexthop **target, struct nexthop *nexthop)
+{
+  struct nexthop *last;
+
+  for (last = *target; last && last->next; last = last->next)
+    ;
+  if (last)
+    last->next = nexthop;
+  else
+    *target = nexthop;
+  nexthop->prev = last;
+}
+
+void
+copy_nexthops (struct nexthop **tnh, struct nexthop *nh)
+{
+  struct nexthop *nexthop;
+  struct nexthop *nh1;
+
+  for (nh1 = nh; nh1; nh1 = nh1->next)
+    {
+      nexthop = nexthop_new();
+      nexthop->flags = nh->flags;
+      nexthop->type = nh->type;
+      nexthop->ifindex = nh->ifindex;
+      if (nh->ifname)
+       nexthop->ifname = XSTRDUP(0, nh->ifname);
+      memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+      memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+      nexthop_add(tnh, nexthop);
+
+      if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
+       copy_nexthops(&nexthop->resolved, nh1->resolved);
+    }
+}
+
+/* Free nexthop. */
+void
+nexthop_free (struct nexthop *nexthop)
+{
+  if (nexthop->ifname)
+    XFREE (0, nexthop->ifname);
+  if (nexthop->resolved)
+    nexthops_free(nexthop->resolved);
+  XFREE (MTYPE_NEXTHOP, nexthop);
+}
+
+/* Frees a list of nexthops */
+void
+nexthops_free (struct nexthop *nexthop)
+{
+  struct nexthop *nh, *next;
+
+  for (nh = nexthop; nh; nh = next)
+    {
+      next = nh->next;
+      nexthop_free (nh);
+    }
+}
diff --git a/lib/nexthop.h b/lib/nexthop.h
new file mode 100644 (file)
index 0000000..0c0dfca
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Nexthop structure definition.
+ * Copyright (C) 1997, 98, 99, 2001 Kunihiro Ishiguro
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _LIB_NEXTHOP_H
+#define _LIB_NEXTHOP_H
+
+#include "prefix.h"
+
+union g_addr {
+  struct in_addr ipv4;
+#ifdef HAVE_IPV6
+  struct in6_addr ipv6;
+#endif /* HAVE_IPV6 */
+};
+
+enum nexthop_types_t
+{
+  NEXTHOP_TYPE_IFINDEX = 1,      /* Directly connected.  */
+  NEXTHOP_TYPE_IFNAME,           /* Interface route.  */
+  NEXTHOP_TYPE_IPV4,             /* IPv4 nexthop.  */
+  NEXTHOP_TYPE_IPV4_IFINDEX,     /* IPv4 nexthop with ifindex.  */
+  NEXTHOP_TYPE_IPV4_IFNAME,      /* IPv4 nexthop with ifname.  */
+  NEXTHOP_TYPE_IPV6,             /* IPv6 nexthop.  */
+  NEXTHOP_TYPE_IPV6_IFINDEX,     /* IPv6 nexthop with ifindex.  */
+  NEXTHOP_TYPE_IPV6_IFNAME,      /* IPv6 nexthop with ifname.  */
+  NEXTHOP_TYPE_BLACKHOLE,        /* Null0 nexthop.  */
+};
+
+/* Nexthop structure. */
+struct nexthop
+{
+  struct nexthop *next;
+  struct nexthop *prev;
+
+  /* Interface index. */
+  char *ifname;
+  ifindex_t ifindex;
+
+  enum nexthop_types_t type;
+
+  u_char flags;
+#define NEXTHOP_FLAG_ACTIVE     (1 << 0) /* This nexthop is alive. */
+#define NEXTHOP_FLAG_FIB        (1 << 1) /* FIB nexthop. */
+#define NEXTHOP_FLAG_RECURSIVE  (1 << 2) /* Recursive nexthop. */
+#define NEXTHOP_FLAG_ONLINK     (1 << 3) /* Nexthop should be installed onlink. */
+#define NEXTHOP_FLAG_MATCHED    (1 << 4) /* Already matched vs a nexthop */
+
+  /* Nexthop address */
+  union g_addr gate;
+  union g_addr src;
+
+  /* Nexthops obtained by recursive resolution.
+   *
+   * If the nexthop struct needs to be resolved recursively,
+   * NEXTHOP_FLAG_RECURSIVE will be set in flags and the nexthops
+   * obtained by recursive resolution will be added to `resolved'.
+   * Only one level of recursive resolution is currently supported. */
+  struct nexthop *resolved;
+};
+
+struct nexthop *nexthop_new (void);
+void nexthop_add (struct nexthop **target, struct nexthop *nexthop);
+
+void copy_nexthops (struct nexthop **tnh, struct nexthop *nh);
+void nexthop_free (struct nexthop *nexthop);
+void nexthops_free (struct nexthop *nexthop);
+
+extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type);
+extern int nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2);
+
+#endif /*_LIB_NEXTHOP_H */
diff --git a/lib/pid_output.c b/lib/pid_output.c
new file mode 100644 (file)
index 0000000..5261bab
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Process id output.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <fcntl.h>
+#include <log.h>
+#include "version.h"
+
+#define PIDFILE_MASK 0644
+#ifndef HAVE_FCNTL
+
+pid_t
+pid_output (const char *path)
+{
+  FILE *fp;
+  pid_t pid;
+  mode_t oldumask;
+
+  pid = getpid();
+
+  oldumask = umask(0777 & ~PIDFILE_MASK);
+  fp = fopen (path, "w");
+  if (fp != NULL) 
+    {
+      fprintf (fp, "%d\n", (int) pid);
+      fclose (fp);
+      umask(oldumask);
+      return pid;
+    }
+  /* XXX Why do we continue instead of exiting?  This seems incompatible
+     with the behavior of the fcntl version below. */
+  zlog_warn("Can't fopen pid lock file %s (%s), continuing",
+           path, safe_strerror(errno));
+  umask(oldumask);
+  return -1;
+}
+
+#else /* HAVE_FCNTL */
+
+pid_t
+pid_output (const char *path)
+{
+  int tmp;
+  int fd;
+  pid_t pid;
+  char buf[16];
+  struct flock lock;  
+  mode_t oldumask;
+
+  pid = getpid ();
+
+  oldumask = umask(0777 & ~PIDFILE_MASK);
+  fd = open (path, O_RDWR | O_CREAT, PIDFILE_MASK);
+  if (fd < 0)
+    {
+      zlog_err("Can't create pid lock file %s (%s), exiting",
+              path, safe_strerror(errno));
+      umask(oldumask);
+      exit(1);
+    }
+  else
+    {
+      size_t pidsize;
+
+      umask(oldumask);
+      memset (&lock, 0, sizeof(lock));
+
+      lock.l_type = F_WRLCK;
+      lock.l_whence = SEEK_SET;
+
+      if (fcntl(fd, F_SETLK, &lock) < 0)
+        {
+          zlog_err("Could not lock pid_file %s, exiting", path);
+          exit(1);
+        }
+
+      sprintf (buf, "%d\n", (int) pid);
+      pidsize = strlen(buf);
+      if ((tmp = write (fd, buf, pidsize)) != (int)pidsize)
+        zlog_err("Could not write pid %d to pid_file %s, rc was %d: %s",
+                (int)pid,path,tmp,safe_strerror(errno));
+      else if (ftruncate(fd, pidsize) < 0)
+        zlog_err("Could not truncate pid_file %s to %u bytes: %s",
+                path,(u_int)pidsize,safe_strerror(errno));
+    }
+  return pid;
+}
+
+#endif /* HAVE_FCNTL */
diff --git a/lib/plist.c b/lib/plist.c
new file mode 100644 (file)
index 0000000..644506b
--- /dev/null
@@ -0,0 +1,2789 @@
+/* Prefix list functions.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "plist.h"
+#include "sockunion.h"
+#include "buffer.h"
+#include "stream.h"
+#include "log.h"
+
+#include "plist_int.h"
+
+/* List of struct prefix_list. */
+struct prefix_list_list
+{
+  struct prefix_list *head;
+  struct prefix_list *tail;
+};
+
+/* Master structure of prefix_list. */
+struct prefix_master
+{
+  /* List of prefix_list which name is number. */
+  struct prefix_list_list num;
+
+  /* List of prefix_list which name is string. */
+  struct prefix_list_list str;
+
+  /* Whether sequential number is used. */
+  int seqnum;
+
+  /* The latest update. */
+  struct prefix_list *recent;
+
+  /* Hook function which is executed when new prefix_list is added. */
+  void (*add_hook) (struct prefix_list *);
+
+  /* Hook function which is executed when prefix_list is deleted. */
+  void (*delete_hook) (struct prefix_list *);
+};
+
+/* Static structure of IPv4 prefix_list's master. */
+static struct prefix_master prefix_master_ipv4 = 
+{ 
+  {NULL, NULL},
+  {NULL, NULL},
+  1,
+  NULL,
+  NULL,
+};
+
+#ifdef HAVE_IPV6
+/* Static structure of IPv6 prefix-list's master. */
+static struct prefix_master prefix_master_ipv6 = 
+{ 
+  {NULL, NULL},
+  {NULL, NULL},
+  1,
+  NULL,
+  NULL,
+};
+#endif /* HAVE_IPV6*/
+
+/* Static structure of BGP ORF prefix_list's master. */
+static struct prefix_master prefix_master_orf_v4 =
+{
+  {NULL, NULL},
+  {NULL, NULL},
+  1,
+  NULL,
+  NULL,
+};
+
+/* Static structure of BGP ORF prefix_list's master. */
+static struct prefix_master prefix_master_orf_v6 =
+{
+  {NULL, NULL},
+  {NULL, NULL},
+  1,
+  NULL,
+  NULL,
+};
+
+static struct prefix_master *
+prefix_master_get (afi_t afi, int orf)
+{
+  if (afi == AFI_IP)
+    return orf ? &prefix_master_orf_v4 : &prefix_master_ipv4;
+  if (afi == AFI_IP6)
+    return orf ? &prefix_master_orf_v6 : &prefix_master_ipv6;
+  return NULL;
+}
+
+const char *prefix_list_name (struct prefix_list *plist)
+{
+  return plist->name;
+}
+
+/* Lookup prefix_list from list of prefix_list by name. */
+static struct prefix_list *
+prefix_list_lookup_do (afi_t afi, int orf, const char *name)
+{
+  struct prefix_list *plist;
+  struct prefix_master *master;
+
+  if (name == NULL)
+    return NULL;
+
+  master = prefix_master_get (afi, orf);
+  if (master == NULL)
+    return NULL;
+
+  for (plist = master->num.head; plist; plist = plist->next)
+    if (strcmp (plist->name, name) == 0)
+      return plist;
+
+  for (plist = master->str.head; plist; plist = plist->next)
+    if (strcmp (plist->name, name) == 0)
+      return plist;
+
+  return NULL;
+}
+
+struct prefix_list *
+prefix_list_lookup (afi_t afi, const char *name)
+{
+  return prefix_list_lookup_do (afi, 0, name);
+}
+
+struct prefix_list *
+prefix_bgp_orf_lookup (afi_t afi, const char *name)
+{
+  return prefix_list_lookup_do (afi, 1, name);
+}
+
+static struct prefix_list *
+prefix_list_new (void)
+{
+  struct prefix_list *new;
+
+  new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list));
+  return new;
+}
+
+static void
+prefix_list_free (struct prefix_list *plist)
+{
+  XFREE (MTYPE_PREFIX_LIST, plist);
+}
+
+static struct prefix_list_entry *
+prefix_list_entry_new (void)
+{
+  struct prefix_list_entry *new;
+
+  new = XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry));
+  return new;
+}
+
+static void
+prefix_list_entry_free (struct prefix_list_entry *pentry)
+{
+  XFREE (MTYPE_PREFIX_LIST_ENTRY, pentry);
+}
+
+/* Insert new prefix list to list of prefix_list.  Each prefix_list
+   is sorted by the name. */
+static struct prefix_list *
+prefix_list_insert (afi_t afi, int orf, const char *name)
+{
+  unsigned int i;
+  long number;
+  struct prefix_list *plist;
+  struct prefix_list *point;
+  struct prefix_list_list *list;
+  struct prefix_master *master;
+
+  master = prefix_master_get (afi, orf);
+  if (master == NULL)
+    return NULL;
+
+  /* Allocate new prefix_list and copy given name. */
+  plist = prefix_list_new ();
+  plist->name = XSTRDUP (MTYPE_PREFIX_LIST_STR, name);
+  plist->master = master;
+
+  /* If name is made by all digit character.  We treat it as
+     number. */
+  for (number = 0, i = 0; i < strlen (name); i++)
+    {
+      if (isdigit ((int) name[i]))
+       number = (number * 10) + (name[i] - '0');
+      else
+       break;
+    }
+
+  /* In case of name is all digit character */
+  if (i == strlen (name))
+    {
+      plist->type = PREFIX_TYPE_NUMBER;
+
+      /* Set prefix_list to number list. */
+      list = &master->num;
+
+      for (point = list->head; point; point = point->next)
+       if (atol (point->name) >= number)
+         break;
+    }
+  else
+    {
+      plist->type = PREFIX_TYPE_STRING;
+
+      /* Set prefix_list to string list. */
+      list = &master->str;
+  
+      /* Set point to insertion point. */
+      for (point = list->head; point; point = point->next)
+       if (strcmp (point->name, name) >= 0)
+         break;
+    }
+
+  /* In case of this is the first element of master. */
+  if (list->head == NULL)
+    {
+      list->head = list->tail = plist;
+      return plist;
+    }
+
+  /* In case of insertion is made at the tail of access_list. */
+  if (point == NULL)
+    {
+      plist->prev = list->tail;
+      list->tail->next = plist;
+      list->tail = plist;
+      return plist;
+    }
+
+  /* In case of insertion is made at the head of access_list. */
+  if (point == list->head)
+    {
+      plist->next = list->head;
+      list->head->prev = plist;
+      list->head = plist;
+      return plist;
+    }
+
+  /* Insertion is made at middle of the access_list. */
+  plist->next = point;
+  plist->prev = point->prev;
+
+  if (point->prev)
+    point->prev->next = plist;
+  point->prev = plist;
+
+  return plist;
+}
+
+static struct prefix_list *
+prefix_list_get (afi_t afi, int orf, const char *name)
+{
+  struct prefix_list *plist;
+
+  plist = prefix_list_lookup_do (afi, orf, name);
+
+  if (plist == NULL)
+    plist = prefix_list_insert (afi, orf, name);
+  return plist;
+}
+
+/* Delete prefix-list from prefix_list_master and free it. */
+static void
+prefix_list_delete (struct prefix_list *plist)
+{
+  struct prefix_list_list *list;
+  struct prefix_master *master;
+  struct prefix_list_entry *pentry;
+  struct prefix_list_entry *next;
+
+  /* If prefix-list contain prefix_list_entry free all of it. */
+  for (pentry = plist->head; pentry; pentry = next)
+    {
+      next = pentry->next;
+      prefix_list_entry_free (pentry);
+      plist->count--;
+    }
+
+  master = plist->master;
+
+  if (plist->type == PREFIX_TYPE_NUMBER)
+    list = &master->num;
+  else
+    list = &master->str;
+
+  if (plist->next)
+    plist->next->prev = plist->prev;
+  else
+    list->tail = plist->prev;
+
+  if (plist->prev)
+    plist->prev->next = plist->next;
+  else
+    list->head = plist->next;
+
+  if (plist->desc)
+    XFREE (MTYPE_TMP, plist->desc);
+
+  /* Make sure master's recent changed prefix-list information is
+     cleared. */
+  master->recent = NULL;
+
+  if (plist->name)
+    XFREE (MTYPE_PREFIX_LIST_STR, plist->name);
+  
+  prefix_list_free (plist);
+  
+  if (master->delete_hook)
+    (*master->delete_hook) (NULL);
+}
+
+static struct prefix_list_entry *
+prefix_list_entry_make (struct prefix *prefix, enum prefix_list_type type,
+                       int seq, int le, int ge, int any)
+{
+  struct prefix_list_entry *pentry;
+
+  pentry = prefix_list_entry_new ();
+
+  if (any)
+    pentry->any = 1;
+
+  prefix_copy (&pentry->prefix, prefix);
+  pentry->type = type;
+  pentry->seq = seq;
+  pentry->le = le;
+  pentry->ge = ge;
+
+  return pentry;
+}
+
+/* Add hook function. */
+void
+prefix_list_add_hook (void (*func) (struct prefix_list *plist))
+{
+  prefix_master_ipv4.add_hook = func;
+#ifdef HAVE_IPV6
+  prefix_master_ipv6.add_hook = func;
+#endif /* HAVE_IPV6 */
+}
+
+/* Delete hook function. */
+void
+prefix_list_delete_hook (void (*func) (struct prefix_list *plist))
+{
+  prefix_master_ipv4.delete_hook = func;
+#ifdef HAVE_IPV6
+  prefix_master_ipv6.delete_hook = func;
+#endif /* HAVE_IPVt6 */
+}
+
+/* Calculate new sequential number. */
+static int
+prefix_new_seq_get (struct prefix_list *plist)
+{
+  int maxseq;
+  int newseq;
+  struct prefix_list_entry *pentry;
+
+  maxseq = newseq = 0;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      if (maxseq < pentry->seq)
+       maxseq = pentry->seq;
+    }
+
+  newseq = ((maxseq / 5) * 5) + 5;
+  
+  return newseq;
+}
+
+/* Return prefix list entry which has same seq number. */
+static struct prefix_list_entry *
+prefix_seq_check (struct prefix_list *plist, int seq)
+{
+  struct prefix_list_entry *pentry;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    if (pentry->seq == seq)
+      return pentry;
+  return NULL;
+}
+
+static struct prefix_list_entry *
+prefix_list_entry_lookup (struct prefix_list *plist, struct prefix *prefix,
+                         enum prefix_list_type type, int seq, int le, int ge)
+{
+  struct prefix_list_entry *pentry;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    if (prefix_same (&pentry->prefix, prefix) && pentry->type == type)
+      {
+       if (seq >= 0 && pentry->seq != seq)
+         continue;
+
+       if (pentry->le != le)
+         continue;
+       if (pentry->ge != ge)
+         continue;
+
+       return pentry;
+      }
+
+  return NULL;
+}
+
+static void
+prefix_list_entry_delete (struct prefix_list *plist, 
+                         struct prefix_list_entry *pentry,
+                         int update_list)
+{
+  if (plist == NULL || pentry == NULL)
+    return;
+  if (pentry->prev)
+    pentry->prev->next = pentry->next;
+  else
+    plist->head = pentry->next;
+  if (pentry->next)
+    pentry->next->prev = pentry->prev;
+  else
+    plist->tail = pentry->prev;
+
+  prefix_list_entry_free (pentry);
+
+  plist->count--;
+
+  if (update_list)
+    {
+      if (plist->master->delete_hook)
+       (*plist->master->delete_hook) (plist);
+
+      if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
+       prefix_list_delete (plist);
+      else
+       plist->master->recent = plist;
+    }
+}
+
+static void
+prefix_list_entry_add (struct prefix_list *plist,
+                      struct prefix_list_entry *pentry)
+{
+  struct prefix_list_entry *replace;
+  struct prefix_list_entry *point;
+
+  /* Automatic asignment of seq no. */
+  if (pentry->seq == -1)
+    pentry->seq = prefix_new_seq_get (plist);
+
+  /* Is there any same seq prefix list entry? */
+  replace = prefix_seq_check (plist, pentry->seq);
+  if (replace)
+    prefix_list_entry_delete (plist, replace, 0);
+
+  /* Check insert point. */
+  for (point = plist->head; point; point = point->next)
+    if (point->seq >= pentry->seq)
+      break;
+
+  /* In case of this is the first element of the list. */
+  pentry->next = point;
+
+  if (point)
+    {
+      if (point->prev)
+       point->prev->next = pentry;
+      else
+       plist->head = pentry;
+
+      pentry->prev = point->prev;
+      point->prev = pentry;
+    }
+  else
+    {
+      if (plist->tail)
+       plist->tail->next = pentry;
+      else
+       plist->head = pentry;
+
+      pentry->prev = plist->tail;
+      plist->tail = pentry;
+    }
+
+  /* Increment count. */
+  plist->count++;
+
+  /* Run hook function. */
+  if (plist->master->add_hook)
+    (*plist->master->add_hook) (plist);
+
+  plist->master->recent = plist;
+}
+
+/* Return string of prefix_list_type. */
+static const char *
+prefix_list_type_str (struct prefix_list_entry *pentry)
+{
+  switch (pentry->type)
+    {
+    case PREFIX_PERMIT:
+      return "permit";
+    case PREFIX_DENY:
+      return "deny";
+    default:
+      return "";
+    }
+}
+
+static int
+prefix_list_entry_match (struct prefix_list_entry *pentry, struct prefix *p)
+{
+  int ret;
+
+  ret = prefix_match (&pentry->prefix, p);
+  if (! ret)
+    return 0;
+  
+  /* In case of le nor ge is specified, exact match is performed. */
+  if (! pentry->le && ! pentry->ge)
+    {
+      if (pentry->prefix.prefixlen != p->prefixlen)
+       return 0;
+    }
+  else
+    {  
+      if (pentry->le)
+       if (p->prefixlen > pentry->le)
+         return 0;
+
+      if (pentry->ge)
+       if (p->prefixlen < pentry->ge)
+         return 0;
+    }
+  return 1;
+}
+
+enum prefix_list_type
+prefix_list_apply (struct prefix_list *plist, void *object)
+{
+  struct prefix_list_entry *pentry;
+  struct prefix *p;
+
+  p = (struct prefix *) object;
+
+  if (plist == NULL)
+    return PREFIX_DENY;
+
+  if (plist->count == 0)
+    return PREFIX_PERMIT;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      pentry->refcnt++;
+      if (prefix_list_entry_match (pentry, p))
+       {
+         pentry->hitcnt++;
+         return pentry->type;
+       }
+    }
+
+  return PREFIX_DENY;
+}
+
+static void __attribute__ ((unused))
+prefix_list_print (struct prefix_list *plist)
+{
+  struct prefix_list_entry *pentry;
+
+  if (plist == NULL)
+    return;
+
+  printf ("ip prefix-list %s: %d entries\n", plist->name, plist->count);
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      if (pentry->any)
+       printf ("any %s\n", prefix_list_type_str (pentry));
+      else
+       {
+         struct prefix *p;
+         char buf[BUFSIZ];
+         
+         p = &pentry->prefix;
+         
+         printf ("  seq %d %s %s/%d", 
+                 pentry->seq,
+                 prefix_list_type_str (pentry),
+                 inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                 p->prefixlen);
+         if (pentry->ge)
+           printf (" ge %d", pentry->ge);
+         if (pentry->le)
+           printf (" le %d", pentry->le);
+         printf ("\n");
+       }
+    }
+}
+
+/* Retrun 1 when plist already include pentry policy. */
+static struct prefix_list_entry *
+prefix_entry_dup_check (struct prefix_list *plist,
+                       struct prefix_list_entry *new)
+{
+  struct prefix_list_entry *pentry;
+  int seq = 0;
+
+  if (new->seq == -1)
+    seq = prefix_new_seq_get (plist);
+  else
+    seq = new->seq;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      if (prefix_same (&pentry->prefix, &new->prefix)
+         && pentry->type == new->type
+         && pentry->le == new->le
+         && pentry->ge == new->ge
+         && pentry->seq != seq)
+       return pentry;
+    }
+  return NULL;
+}
+
+static int
+vty_invalid_prefix_range (struct vty *vty, const char *prefix)
+{
+  vty_out (vty, "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value%s",
+           prefix, VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+static int
+vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, 
+                         const char *seq, const char *typestr,
+                        const char *prefix, const char *ge, const char *le)
+{
+  int ret;
+  enum prefix_list_type type;
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+  struct prefix_list_entry *dup;
+  struct prefix p;
+  int any = 0;
+  int seqnum = -1;
+  int lenum = 0;
+  int genum = 0;
+  
+  /* This code only works for IP.  Provide a safe-guard and user-visible
+   * warning
+   */
+  if (!(afi == AFI_IP || afi == AFI_IP6))
+    {
+      vty_out (vty, "%% prefix must be IPv4 or IPv6!%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  /* Sequential number. */
+  if (seq)
+    seqnum = atoi (seq);
+
+  /* ge and le number */
+  if (ge)
+    genum = atoi (ge);
+  if (le)
+    lenum = atoi (le);
+
+  /* Check filter type. */
+  if (strncmp ("permit", typestr, 1) == 0)
+    type = PREFIX_PERMIT;
+  else if (strncmp ("deny", typestr, 1) == 0)
+    type = PREFIX_DENY;
+  else
+    {
+      vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* "any" is special token for matching any IPv4 addresses.  */
+  switch (afi)
+    {
+    case AFI_IP:
+      if (strncmp ("any", prefix, strlen (prefix)) == 0)
+       {
+         ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
+         genum = 0;
+         lenum = IPV4_MAX_BITLEN;
+         any = 1;
+       }
+      else
+       ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
+
+      if (ret <= 0)
+       {
+         vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      break;
+    case AFI_IP6:
+      if (strncmp ("any", prefix, strlen (prefix)) == 0)
+       {
+         ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
+         genum = 0;
+         lenum = IPV6_MAX_BITLEN;
+         any = 1;
+       }
+      else
+       ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
+
+      if (ret <= 0)
+       {
+         vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      break;
+    case AFI_ETHER:
+    default:
+      vty_out (vty, "%% Unrecognized AFI (%d)%s", afi, VTY_NEWLINE);
+      return CMD_WARNING;
+      break;
+    }
+
+  /* ge and le check. */
+  if (genum && (genum <= p.prefixlen))
+    return vty_invalid_prefix_range (vty, prefix);
+
+  if (lenum && (lenum <= p.prefixlen))
+    return vty_invalid_prefix_range (vty, prefix);
+
+  if (lenum && (genum > lenum))
+    return vty_invalid_prefix_range (vty, prefix);
+
+  if (genum && (lenum == (afi == AFI_IP ? 32 : 128)))
+    lenum = 0;
+
+  /* Get prefix_list with name. */
+  plist = prefix_list_get (afi, 0, name);
+
+  /* Make prefix entry. */
+  pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any);
+    
+  /* Check same policy. */
+  dup = prefix_entry_dup_check (plist, pentry);
+
+  if (dup)
+    {
+      prefix_list_entry_free (pentry);
+      vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s",
+              VTY_NEWLINE);
+      vty_out (vty, "   seq %d %s %s", dup->seq, typestr, prefix);
+      if (! any && genum)
+       vty_out (vty, " ge %d", genum);
+      if (! any && lenum)
+       vty_out (vty, " le %d", lenum);
+      vty_out (vty, "%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Install new filter to the access_list. */
+  prefix_list_entry_add (plist, pentry);
+
+  return CMD_SUCCESS;
+}
+
+static int
+vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name, 
+                           const char *seq, const char *typestr,
+                          const char *prefix, const char *ge, const char *le)
+{
+  int ret;
+  enum prefix_list_type type;
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+  struct prefix p;
+  int seqnum = -1;
+  int lenum = 0;
+  int genum = 0;
+
+  /* Check prefix list name. */
+  plist = prefix_list_lookup (afi, name);
+  if (! plist)
+    {
+      vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Only prefix-list name specified, delete the entire prefix-list. */
+  if (seq == NULL && typestr == NULL && prefix == NULL && 
+      ge == NULL && le == NULL)
+    {
+      prefix_list_delete (plist);
+      return CMD_SUCCESS;
+    }
+
+  /* We must have, at a minimum, both the type and prefix here */
+  if ((typestr == NULL) || (prefix == NULL))
+    {
+      vty_out (vty, "%% Both prefix and type required%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check sequence number. */
+  if (seq)
+    seqnum = atoi (seq);
+
+  /* ge and le number */
+  if (ge)
+    genum = atoi (ge);
+  if (le)
+    lenum = atoi (le);
+
+  /* Check of filter type. */
+  if (strncmp ("permit", typestr, 1) == 0)
+    type = PREFIX_PERMIT;
+  else if (strncmp ("deny", typestr, 1) == 0)
+    type = PREFIX_DENY;
+  else
+    {
+      vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* "any" is special token for matching any IPv4 addresses.  */
+  if (afi == AFI_IP)
+    {
+      if (strncmp ("any", prefix, strlen (prefix)) == 0)
+       {
+         ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
+         genum = 0;
+         lenum = IPV4_MAX_BITLEN;
+       }
+      else
+       ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
+
+      if (ret <= 0)
+       {
+         vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6)
+    {
+      if (strncmp ("any", prefix, strlen (prefix)) == 0)
+       {
+         ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
+         genum = 0;
+         lenum = IPV6_MAX_BITLEN;
+       }
+      else
+       ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
+
+      if (ret <= 0)
+       {
+         vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+#endif /* HAVE_IPV6 */
+
+  /* Lookup prefix entry. */
+  pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum);
+
+  if (pentry == NULL)
+    {
+      vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Install new filter to the access_list. */
+  prefix_list_entry_delete (plist, pentry, 1);
+
+  return CMD_SUCCESS;
+}
+
+static int
+vty_prefix_list_desc_unset (struct vty *vty, afi_t afi, const char *name)
+{
+  struct prefix_list *plist;
+
+  plist = prefix_list_lookup (afi, name);
+  if (! plist)
+    {
+      vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (plist->desc)
+    {
+      XFREE (MTYPE_TMP, plist->desc);
+      plist->desc = NULL;
+    }
+
+  if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
+    prefix_list_delete (plist);
+
+  return CMD_SUCCESS;
+}
+
+enum display_type
+{
+  normal_display,
+  summary_display,
+  detail_display,
+  sequential_display,
+  longer_display,
+  first_match_display
+};
+
+static void
+vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist,
+                      struct prefix_master *master, enum display_type dtype,
+                      int seqnum)
+{
+  struct prefix_list_entry *pentry;
+
+  /* Print the name of the protocol */
+  if (zlog_default)
+      vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]);
+                                                                           
+  if (dtype == normal_display)
+    {
+      vty_out (vty, "ip%s prefix-list %s: %d entries%s",
+              afi == AFI_IP ? "" : "v6",
+              plist->name, plist->count, VTY_NEWLINE);
+      if (plist->desc)
+       vty_out (vty, "   Description: %s%s", plist->desc, VTY_NEWLINE);
+    }
+  else if (dtype == summary_display || dtype == detail_display)
+    {
+      vty_out (vty, "ip%s prefix-list %s:%s",
+              afi == AFI_IP ? "" : "v6", plist->name, VTY_NEWLINE);
+
+      if (plist->desc)
+       vty_out (vty, "   Description: %s%s", plist->desc, VTY_NEWLINE);
+
+      vty_out (vty, "   count: %d, range entries: %d, sequences: %d - %d%s",
+              plist->count, plist->rangecount, 
+              plist->head ? plist->head->seq : 0, 
+              plist->tail ? plist->tail->seq : 0,
+              VTY_NEWLINE);
+    }
+
+  if (dtype != summary_display)
+    {
+      for (pentry = plist->head; pentry; pentry = pentry->next)
+       {
+         if (dtype == sequential_display && pentry->seq != seqnum)
+           continue;
+           
+         vty_out (vty, "   ");
+
+         if (master->seqnum)
+           vty_out (vty, "seq %d ", pentry->seq);
+
+         vty_out (vty, "%s ", prefix_list_type_str (pentry));
+
+         if (pentry->any)
+           vty_out (vty, "any");
+         else
+           {
+             struct prefix *p = &pentry->prefix;
+             char buf[BUFSIZ];
+
+             vty_out (vty, "%s/%d",
+                      inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                      p->prefixlen);
+
+             if (pentry->ge)
+               vty_out (vty, " ge %d", pentry->ge);
+             if (pentry->le)
+               vty_out (vty, " le %d", pentry->le);
+           }
+
+         if (dtype == detail_display || dtype == sequential_display)
+           vty_out (vty, " (hit count: %ld, refcount: %ld)", 
+                    pentry->hitcnt, pentry->refcnt);
+         
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+}
+
+static int
+vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name,
+                     const char *seq, enum display_type dtype)
+{
+  struct prefix_list *plist;
+  struct prefix_master *master;
+  int seqnum = 0;
+
+  master = prefix_master_get (afi, 0);
+  if (master == NULL)
+    return CMD_WARNING;
+
+  if (seq)
+    seqnum = atoi (seq);
+
+  if (name)
+    {
+      plist = prefix_list_lookup (afi, name);
+      if (! plist)
+       {
+         vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+    }
+  else
+    {
+      if (dtype == detail_display || dtype == summary_display)
+       {
+         if (master->recent)
+           vty_out (vty, "Prefix-list with the last deletion/insertion: %s%s",
+                    master->recent->name, VTY_NEWLINE);
+       }
+
+      for (plist = master->num.head; plist; plist = plist->next)
+       vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+
+      for (plist = master->str.head; plist; plist = plist->next)
+       vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name, 
+                            const char *prefix, enum display_type type)
+{
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+  struct prefix p;
+  int ret;
+  int match;
+
+  plist = prefix_list_lookup (afi, name);
+  if (! plist)
+    {
+      vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = str2prefix (prefix, &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      match = 0;
+
+      if (type == normal_display || type == first_match_display)
+       if (prefix_same (&p, &pentry->prefix))
+         match = 1;
+
+      if (type == longer_display)
+       if (prefix_match (&p, &pentry->prefix))
+         match = 1;
+
+      if (match)
+       {
+         vty_out (vty, "   seq %d %s ", 
+                  pentry->seq,
+                  prefix_list_type_str (pentry));
+
+         if (pentry->any)
+           vty_out (vty, "any");
+         else
+           {
+             struct prefix *p = &pentry->prefix;
+             char buf[BUFSIZ];
+             
+             vty_out (vty, "%s/%d",
+                      inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                      p->prefixlen);
+
+             if (pentry->ge)
+               vty_out (vty, " ge %d", pentry->ge);
+             if (pentry->le)
+               vty_out (vty, " le %d", pentry->le);
+           }
+         
+         if (type == normal_display || type == first_match_display)
+           vty_out (vty, " (hit count: %ld, refcount: %ld)", 
+                    pentry->hitcnt, pentry->refcnt);
+
+         vty_out (vty, "%s", VTY_NEWLINE);
+
+         if (type == first_match_display)
+           return CMD_SUCCESS;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, 
+                       const char *prefix)
+{
+  struct prefix_master *master;
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+  int ret;
+  struct prefix p;
+
+  master = prefix_master_get (afi, 0);
+  if (master == NULL)
+    return CMD_WARNING;
+
+  if (name == NULL && prefix == NULL)
+    {
+      for (plist = master->num.head; plist; plist = plist->next)
+       for (pentry = plist->head; pentry; pentry = pentry->next)
+         pentry->hitcnt = 0;
+
+      for (plist = master->str.head; plist; plist = plist->next)
+       for (pentry = plist->head; pentry; pentry = pentry->next)
+         pentry->hitcnt = 0;
+    }
+  else
+    {
+      plist = prefix_list_lookup (afi, name);
+      if (! plist)
+       {
+         vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      if (prefix)
+       {
+         ret = str2prefix (prefix, &p);
+         if (ret <= 0)
+           {
+             vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE);
+             return CMD_WARNING;
+           }
+       }
+
+      for (pentry = plist->head; pentry; pentry = pentry->next)
+       {
+         if (prefix)
+           {
+             if (prefix_match (&pentry->prefix, &p))
+               pentry->hitcnt = 0;
+           }
+         else
+           pentry->hitcnt = 0;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_prefix_list,
+       ip_prefix_list_cmd,
+       "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, 
+                                 argv[1], argv[2], NULL, NULL);
+}
+
+DEFUN (ip_prefix_list_ge,
+       ip_prefix_list_ge_cmd,
+       "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], 
+                                argv[2], argv[3], NULL);
+}
+
+DEFUN (ip_prefix_list_ge_le,
+       ip_prefix_list_ge_le_cmd,
+       "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], 
+                                 argv[2], argv[3], argv[4]);
+}
+
+DEFUN (ip_prefix_list_le,
+       ip_prefix_list_le_cmd,
+       "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+                                 argv[2], NULL, argv[3]);
+}
+
+DEFUN (ip_prefix_list_le_ge,
+       ip_prefix_list_le_ge_cmd,
+       "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+                                 argv[2], argv[4], argv[3]);
+}
+
+DEFUN (ip_prefix_list_seq,
+       ip_prefix_list_seq_cmd,
+       "ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                 argv[3], NULL, NULL);
+}
+
+DEFUN (ip_prefix_list_seq_ge,
+       ip_prefix_list_seq_ge_cmd,
+       "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                 argv[3], argv[4], NULL);
+}
+
+DEFUN (ip_prefix_list_seq_ge_le,
+       ip_prefix_list_seq_ge_le_cmd,
+       "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                 argv[3], argv[4], argv[5]);
+}
+
+DEFUN (ip_prefix_list_seq_le,
+       ip_prefix_list_seq_le_cmd,
+       "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                 argv[3], NULL, argv[4]);
+}
+
+DEFUN (ip_prefix_list_seq_le_ge,
+       ip_prefix_list_seq_le_ge_cmd,
+       "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                 argv[3], argv[5], argv[4]);
+}
+
+DEFUN (no_ip_prefix_list,
+       no_ip_prefix_list_cmd,
+       "no ip prefix-list WORD",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, NULL,
+                                   NULL, NULL, NULL);
+}
+
+DEFUN (no_ip_prefix_list_prefix,
+       no_ip_prefix_list_prefix_cmd,
+       "no ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Any prefix match.  Same as \"0.0.0.0/0 le 32\"\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1],
+                                   argv[2], NULL, NULL);
+}
+
+DEFUN (no_ip_prefix_list_ge,
+       no_ip_prefix_list_ge_cmd,
+       "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1],
+                                   argv[2], argv[3], NULL);
+}
+
+DEFUN (no_ip_prefix_list_ge_le,
+       no_ip_prefix_list_ge_le_cmd,
+       "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1],
+                                   argv[2], argv[3], argv[4]);
+}
+
+DEFUN (no_ip_prefix_list_le,
+       no_ip_prefix_list_le_cmd,
+       "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1],
+                                   argv[2], NULL, argv[3]);
+}
+
+DEFUN (no_ip_prefix_list_le_ge,
+       no_ip_prefix_list_le_ge_cmd,
+       "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1],
+                                   argv[2], argv[4], argv[3]);
+}
+
+DEFUN (no_ip_prefix_list_seq,
+       no_ip_prefix_list_seq_cmd,
+       "no ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Any prefix match.  Same as \"0.0.0.0/0 le 32\"\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                   argv[3], NULL, NULL);
+}
+
+DEFUN (no_ip_prefix_list_seq_ge,
+       no_ip_prefix_list_seq_ge_cmd,
+       "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                   argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ip_prefix_list_seq_ge_le,
+       no_ip_prefix_list_seq_ge_le_cmd,
+       "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                   argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ip_prefix_list_seq_le,
+       no_ip_prefix_list_seq_le_cmd,
+       "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                   argv[3], NULL, argv[4]);
+}
+
+DEFUN (no_ip_prefix_list_seq_le_ge,
+       no_ip_prefix_list_seq_le_ge_cmd,
+       "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2],
+                                   argv[3], argv[5], argv[4]);
+}
+
+DEFUN (ip_prefix_list_sequence_number,
+       ip_prefix_list_sequence_number_cmd,
+       "ip prefix-list sequence-number",
+       IP_STR
+       PREFIX_LIST_STR
+       "Include/exclude sequence numbers in NVGEN\n")
+{
+  prefix_master_ipv4.seqnum = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_prefix_list_sequence_number,
+       no_ip_prefix_list_sequence_number_cmd,
+       "no ip prefix-list sequence-number",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Include/exclude sequence numbers in NVGEN\n")
+{
+  prefix_master_ipv4.seqnum = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_prefix_list_description,
+       ip_prefix_list_description_cmd,
+       "ip prefix-list WORD description .LINE",
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n"
+       "Up to 80 characters describing this prefix-list\n")
+{
+  struct prefix_list *plist;
+
+  plist = prefix_list_get (AFI_IP, 0, argv[0]);
+  
+  if (plist->desc)
+    {
+      XFREE (MTYPE_TMP, plist->desc);
+      plist->desc = NULL;
+    }
+  plist->desc = argv_concat(argv, argc, 1);
+
+  return CMD_SUCCESS;
+}       
+
+DEFUN (no_ip_prefix_list_description,
+       no_ip_prefix_list_description_cmd,
+       "no ip prefix-list WORD description",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n")
+{
+  return vty_prefix_list_desc_unset (vty, AFI_IP, argv[0]);
+}
+
+ALIAS (no_ip_prefix_list_description,
+       no_ip_prefix_list_description_arg_cmd,
+       "no ip prefix-list WORD description .LINE",
+       NO_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n"
+       "Up to 80 characters describing this prefix-list\n")
+
+DEFUN (show_ip_prefix_list,
+       show_ip_prefix_list_cmd,
+       "show ip prefix-list",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR)
+{
+  return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, normal_display);
+}
+
+DEFUN (show_ip_prefix_list_name,
+       show_ip_prefix_list_name_cmd,
+       "show ip prefix-list WORD",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, normal_display);
+}
+
+DEFUN (show_ip_prefix_list_name_seq,
+       show_ip_prefix_list_name_seq_cmd,
+       "show ip prefix-list WORD seq <1-4294967295>",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, argv[0], argv[1], sequential_display);
+}
+
+DEFUN (show_ip_prefix_list_prefix,
+       show_ip_prefix_list_prefix_cmd,
+       "show ip prefix-list WORD A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], normal_display);
+}
+
+DEFUN (show_ip_prefix_list_prefix_longer,
+       show_ip_prefix_list_prefix_longer_cmd,
+       "show ip prefix-list WORD A.B.C.D/M longer",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Lookup longer prefix\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], longer_display);
+}
+
+DEFUN (show_ip_prefix_list_prefix_first_match,
+       show_ip_prefix_list_prefix_first_match_cmd,
+       "show ip prefix-list WORD A.B.C.D/M first-match",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "First matched prefix\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], first_match_display);
+}
+
+DEFUN (show_ip_prefix_list_summary,
+       show_ip_prefix_list_summary_cmd,
+       "show ip prefix-list summary",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Summary of prefix lists\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, summary_display);
+}
+
+DEFUN (show_ip_prefix_list_summary_name,
+       show_ip_prefix_list_summary_name_cmd,
+       "show ip prefix-list summary WORD",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Summary of prefix lists\n"
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, summary_display);
+}
+
+
+DEFUN (show_ip_prefix_list_detail,
+       show_ip_prefix_list_detail_cmd,
+       "show ip prefix-list detail",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Detail of prefix lists\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, detail_display);
+}
+
+DEFUN (show_ip_prefix_list_detail_name,
+       show_ip_prefix_list_detail_name_cmd,
+       "show ip prefix-list detail WORD",
+       SHOW_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Detail of prefix lists\n"
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, detail_display);
+}
+
+DEFUN (clear_ip_prefix_list,
+       clear_ip_prefix_list_cmd,
+       "clear ip prefix-list",
+       CLEAR_STR
+       IP_STR
+       PREFIX_LIST_STR)
+{
+  return vty_clear_prefix_list (vty, AFI_IP, NULL, NULL);
+}
+
+DEFUN (clear_ip_prefix_list_name,
+       clear_ip_prefix_list_name_cmd,
+       "clear ip prefix-list WORD",
+       CLEAR_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_clear_prefix_list (vty, AFI_IP, argv[0], NULL);
+}
+
+DEFUN (clear_ip_prefix_list_name_prefix,
+       clear_ip_prefix_list_name_prefix_cmd,
+       "clear ip prefix-list WORD A.B.C.D/M",
+       CLEAR_STR
+       IP_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]);
+}
+
+#ifdef HAVE_IPV6
+DEFUN (ipv6_prefix_list,
+       ipv6_prefix_list_cmd,
+       "ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Any prefix match.  Same as \"::0/0 le 128\"\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, 
+                                 argv[1], argv[2], NULL, NULL);
+}
+
+DEFUN (ipv6_prefix_list_ge,
+       ipv6_prefix_list_ge_cmd,
+       "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], 
+                                argv[2], argv[3], NULL);
+}
+
+DEFUN (ipv6_prefix_list_ge_le,
+       ipv6_prefix_list_ge_le_cmd,
+       "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], 
+                                 argv[2], argv[3], argv[4]);
+}
+
+DEFUN (ipv6_prefix_list_le,
+       ipv6_prefix_list_le_cmd,
+       "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                 argv[2], NULL, argv[3]);
+}
+
+DEFUN (ipv6_prefix_list_le_ge,
+       ipv6_prefix_list_le_ge_cmd,
+       "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                 argv[2], argv[4], argv[3]);
+}
+
+DEFUN (ipv6_prefix_list_seq,
+       ipv6_prefix_list_seq_cmd,
+       "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Any prefix match.  Same as \"::0/0 le 128\"\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                 argv[3], NULL, NULL);
+}
+
+DEFUN (ipv6_prefix_list_seq_ge,
+       ipv6_prefix_list_seq_ge_cmd,
+       "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                 argv[3], argv[4], NULL);
+}
+
+DEFUN (ipv6_prefix_list_seq_ge_le,
+       ipv6_prefix_list_seq_ge_le_cmd,
+       "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                 argv[3], argv[4], argv[5]);
+}
+
+DEFUN (ipv6_prefix_list_seq_le,
+       ipv6_prefix_list_seq_le_cmd,
+       "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                 argv[3], NULL, argv[4]);
+}
+
+DEFUN (ipv6_prefix_list_seq_le_ge,
+       ipv6_prefix_list_seq_le_ge_cmd,
+       "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                 argv[3], argv[5], argv[4]);
+}
+
+DEFUN (no_ipv6_prefix_list,
+       no_ipv6_prefix_list_cmd,
+       "no ipv6 prefix-list WORD",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, NULL,
+                                   NULL, NULL, NULL);
+}
+
+DEFUN (no_ipv6_prefix_list_prefix,
+       no_ipv6_prefix_list_prefix_cmd,
+       "no ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Any prefix match.  Same as \"::0/0 le 128\"\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                   argv[2], NULL, NULL);
+}
+
+DEFUN (no_ipv6_prefix_list_ge,
+       no_ipv6_prefix_list_ge_cmd,
+       "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                   argv[2], argv[3], NULL);
+}
+
+DEFUN (no_ipv6_prefix_list_ge_le,
+       no_ipv6_prefix_list_ge_le_cmd,
+       "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                   argv[2], argv[3], argv[4]);
+}
+
+DEFUN (no_ipv6_prefix_list_le,
+       no_ipv6_prefix_list_le_cmd,
+       "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                   argv[2], NULL, argv[3]);
+}
+
+DEFUN (no_ipv6_prefix_list_le_ge,
+       no_ipv6_prefix_list_le_ge_cmd,
+       "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1],
+                                   argv[2], argv[4], argv[3]);
+}
+
+DEFUN (no_ipv6_prefix_list_seq,
+       no_ipv6_prefix_list_seq_cmd,
+       "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Any prefix match.  Same as \"::0/0 le 128\"\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                   argv[3], NULL, NULL);
+}
+
+DEFUN (no_ipv6_prefix_list_seq_ge,
+       no_ipv6_prefix_list_seq_ge_cmd,
+       "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                   argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ipv6_prefix_list_seq_ge_le,
+       no_ipv6_prefix_list_seq_ge_le_cmd,
+       "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                   argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ipv6_prefix_list_seq_le,
+       no_ipv6_prefix_list_seq_le_cmd,
+       "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                   argv[3], NULL, argv[4]);
+}
+
+DEFUN (no_ipv6_prefix_list_seq_le_ge,
+       no_ipv6_prefix_list_seq_le_ge_cmd,
+       "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n"
+       "Specify packets to reject\n"
+       "Specify packets to forward\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Maximum prefix length to be matched\n"
+       "Maximum prefix length\n"
+       "Minimum prefix length to be matched\n"
+       "Minimum prefix length\n")
+{
+  return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2],
+                                   argv[3], argv[5], argv[4]);
+}
+
+DEFUN (ipv6_prefix_list_sequence_number,
+       ipv6_prefix_list_sequence_number_cmd,
+       "ipv6 prefix-list sequence-number",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Include/exclude sequence numbers in NVGEN\n")
+{
+  prefix_master_ipv6.seqnum = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_prefix_list_sequence_number,
+       no_ipv6_prefix_list_sequence_number_cmd,
+       "no ipv6 prefix-list sequence-number",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Include/exclude sequence numbers in NVGEN\n")
+{
+  prefix_master_ipv6.seqnum = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_prefix_list_description,
+       ipv6_prefix_list_description_cmd,
+       "ipv6 prefix-list WORD description .LINE",
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n"
+       "Up to 80 characters describing this prefix-list\n")
+{
+  struct prefix_list *plist;
+
+  plist = prefix_list_get (AFI_IP6, 0, argv[0]);
+  
+  if (plist->desc)
+    {
+      XFREE (MTYPE_TMP, plist->desc);
+      plist->desc = NULL;
+    }
+  plist->desc = argv_concat(argv, argc, 1);
+
+  return CMD_SUCCESS;
+}       
+
+DEFUN (no_ipv6_prefix_list_description,
+       no_ipv6_prefix_list_description_cmd,
+       "no ipv6 prefix-list WORD description",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n")
+{
+  return vty_prefix_list_desc_unset (vty, AFI_IP6, argv[0]);
+}
+
+ALIAS (no_ipv6_prefix_list_description,
+       no_ipv6_prefix_list_description_arg_cmd,
+       "no ipv6 prefix-list WORD description .LINE",
+       NO_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "Prefix-list specific description\n"
+       "Up to 80 characters describing this prefix-list\n")
+
+DEFUN (show_ipv6_prefix_list,
+       show_ipv6_prefix_list_cmd,
+       "show ipv6 prefix-list",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR)
+{
+  return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, normal_display);
+}
+
+DEFUN (show_ipv6_prefix_list_name,
+       show_ipv6_prefix_list_name_cmd,
+       "show ipv6 prefix-list WORD",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, normal_display);
+}
+
+DEFUN (show_ipv6_prefix_list_name_seq,
+       show_ipv6_prefix_list_name_seq_cmd,
+       "show ipv6 prefix-list WORD seq <1-4294967295>",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "sequence number of an entry\n"
+       "Sequence number\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, argv[0], argv[1], sequential_display);
+}
+
+DEFUN (show_ipv6_prefix_list_prefix,
+       show_ipv6_prefix_list_prefix_cmd,
+       "show ipv6 prefix-list WORD X:X::X:X/M",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], normal_display);
+}
+
+DEFUN (show_ipv6_prefix_list_prefix_longer,
+       show_ipv6_prefix_list_prefix_longer_cmd,
+       "show ipv6 prefix-list WORD X:X::X:X/M longer",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "Lookup longer prefix\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], longer_display);
+}
+
+DEFUN (show_ipv6_prefix_list_prefix_first_match,
+       show_ipv6_prefix_list_prefix_first_match_cmd,
+       "show ipv6 prefix-list WORD X:X::X:X/M first-match",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+       "First matched prefix\n")
+{
+  return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], first_match_display);
+}
+
+DEFUN (show_ipv6_prefix_list_summary,
+       show_ipv6_prefix_list_summary_cmd,
+       "show ipv6 prefix-list summary",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Summary of prefix lists\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, summary_display);
+}
+
+DEFUN (show_ipv6_prefix_list_summary_name,
+       show_ipv6_prefix_list_summary_name_cmd,
+       "show ipv6 prefix-list summary WORD",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Summary of prefix lists\n"
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, summary_display);
+}
+
+DEFUN (show_ipv6_prefix_list_detail,
+       show_ipv6_prefix_list_detail_cmd,
+       "show ipv6 prefix-list detail",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Detail of prefix lists\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, detail_display);
+}
+
+DEFUN (show_ipv6_prefix_list_detail_name,
+       show_ipv6_prefix_list_detail_name_cmd,
+       "show ipv6 prefix-list detail WORD",
+       SHOW_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Detail of prefix lists\n"
+       "Name of a prefix list\n")
+{
+  return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, detail_display);
+}
+
+DEFUN (clear_ipv6_prefix_list,
+       clear_ipv6_prefix_list_cmd,
+       "clear ipv6 prefix-list",
+       CLEAR_STR
+       IPV6_STR
+       PREFIX_LIST_STR)
+{
+  return vty_clear_prefix_list (vty, AFI_IP6, NULL, NULL);
+}
+
+DEFUN (clear_ipv6_prefix_list_name,
+       clear_ipv6_prefix_list_name_cmd,
+       "clear ipv6 prefix-list WORD",
+       CLEAR_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n")
+{
+  return vty_clear_prefix_list (vty, AFI_IP6, argv[0], NULL);
+}
+
+DEFUN (clear_ipv6_prefix_list_name_prefix,
+       clear_ipv6_prefix_list_name_prefix_cmd,
+       "clear ipv6 prefix-list WORD X:X::X:X/M",
+       CLEAR_STR
+       IPV6_STR
+       PREFIX_LIST_STR
+       "Name of a prefix list\n"
+       "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n")
+{
+  return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]);
+}
+#endif /* HAVE_IPV6 */
+
+/* Configuration write function. */
+static int
+config_write_prefix_afi (afi_t afi, struct vty *vty)
+{
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+  struct prefix_master *master;
+  int write = 0;
+
+  master = prefix_master_get (afi, 0);
+  if (master == NULL)
+    return 0;
+
+  if (! master->seqnum)
+    {
+      vty_out (vty, "no ip%s prefix-list sequence-number%s", 
+              afi == AFI_IP ? "" : "v6", VTY_NEWLINE);
+      vty_out (vty, "!%s", VTY_NEWLINE);
+    }
+
+  for (plist = master->num.head; plist; plist = plist->next)
+    {
+      if (plist->desc)
+       {
+         vty_out (vty, "ip%s prefix-list %s description %s%s",
+                  afi == AFI_IP ? "" : "v6",
+                  plist->name, plist->desc, VTY_NEWLINE);
+         write++;
+       }
+
+      for (pentry = plist->head; pentry; pentry = pentry->next)
+       {
+         vty_out (vty, "ip%s prefix-list %s ",
+                  afi == AFI_IP ? "" : "v6",
+                  plist->name);
+
+         if (master->seqnum)
+           vty_out (vty, "seq %d ", pentry->seq);
+       
+         vty_out (vty, "%s ", prefix_list_type_str (pentry));
+
+         if (pentry->any)
+           vty_out (vty, "any");
+         else
+           {
+             struct prefix *p = &pentry->prefix;
+             char buf[BUFSIZ];
+
+             vty_out (vty, "%s/%d",
+                      inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                      p->prefixlen);
+
+             if (pentry->ge)
+               vty_out (vty, " ge %d", pentry->ge);
+             if (pentry->le)
+               vty_out (vty, " le %d", pentry->le);
+           }
+         vty_out (vty, "%s", VTY_NEWLINE);
+         write++;
+       }
+      /* vty_out (vty, "!%s", VTY_NEWLINE); */
+    }
+
+  for (plist = master->str.head; plist; plist = plist->next)
+    {
+      if (plist->desc)
+       {
+         vty_out (vty, "ip%s prefix-list %s description %s%s",
+                  afi == AFI_IP ? "" : "v6",
+                  plist->name, plist->desc, VTY_NEWLINE);
+         write++;
+       }
+
+      for (pentry = plist->head; pentry; pentry = pentry->next)
+       {
+         vty_out (vty, "ip%s prefix-list %s ",
+                  afi == AFI_IP ? "" : "v6",
+                  plist->name);
+
+         if (master->seqnum)
+           vty_out (vty, "seq %d ", pentry->seq);
+
+         vty_out (vty, "%s", prefix_list_type_str (pentry));
+
+         if (pentry->any)
+           vty_out (vty, " any");
+         else
+           {
+             struct prefix *p = &pentry->prefix;
+             char buf[BUFSIZ];
+
+             vty_out (vty, " %s/%d",
+                      inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                      p->prefixlen);
+
+             if (pentry->ge)
+               vty_out (vty, " ge %d", pentry->ge);
+             if (pentry->le)
+               vty_out (vty, " le %d", pentry->le);
+           }
+         vty_out (vty, "%s", VTY_NEWLINE);
+         write++;
+       }
+    }
+  
+  return write;
+}
+
+struct stream *
+prefix_bgp_orf_entry (struct stream *s, struct prefix_list *plist,
+                     u_char init_flag, u_char permit_flag, u_char deny_flag)
+{
+  struct prefix_list_entry *pentry;
+
+  if (! plist)
+    return s;
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      u_char flag = init_flag;
+      struct prefix *p = &pentry->prefix;
+
+      flag |= (pentry->type == PREFIX_PERMIT ?
+               permit_flag : deny_flag);
+      stream_putc (s, flag);
+      stream_putl (s, (u_int32_t)pentry->seq);
+      stream_putc (s, (u_char)pentry->ge);
+      stream_putc (s, (u_char)pentry->le);
+      stream_put_prefix (s, p);
+    }
+
+  return s;
+}
+
+int
+prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp,
+                   int permit, int set)
+{
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+
+  /* ge and le value check */ 
+  if (orfp->ge && orfp->ge <= orfp->p.prefixlen)
+    return CMD_WARNING;
+  if (orfp->le && orfp->le <= orfp->p.prefixlen)
+    return CMD_WARNING;
+  if (orfp->le && orfp->ge > orfp->le)
+    return CMD_WARNING;
+
+  if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128))
+    orfp->le = 0;
+
+  plist = prefix_list_get (afi, 1, name);
+  if (! plist)
+    return CMD_WARNING;
+
+  if (set)
+    {
+      pentry = prefix_list_entry_make (&orfp->p,
+                                      (permit ? PREFIX_PERMIT : PREFIX_DENY),
+                                      orfp->seq, orfp->le, orfp->ge, 0);
+
+      if (prefix_entry_dup_check (plist, pentry))
+       {
+         prefix_list_entry_free (pentry);
+         return CMD_WARNING;
+       }
+
+      prefix_list_entry_add (plist, pentry);
+    }
+  else
+    {
+      pentry = prefix_list_entry_lookup (plist, &orfp->p,
+                                        (permit ? PREFIX_PERMIT : PREFIX_DENY),
+                                        orfp->seq, orfp->le, orfp->ge);
+
+      if (! pentry)
+       return CMD_WARNING;
+
+      prefix_list_entry_delete (plist, pentry, 1);
+    }
+
+  return CMD_SUCCESS;
+}
+
+void
+prefix_bgp_orf_remove_all (afi_t afi, char *name)
+{
+  struct prefix_list *plist;
+
+  plist = prefix_bgp_orf_lookup (afi, name);
+  if (plist)
+    prefix_list_delete (plist);
+}
+
+/* return prefix count */
+int
+prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name)
+{
+  struct prefix_list *plist;
+  struct prefix_list_entry *pentry;
+
+  plist = prefix_bgp_orf_lookup (afi, name);
+  if (! plist)
+    return 0;
+
+  if (! vty)
+    return plist->count;
+
+  vty_out (vty, "ip%s prefix-list %s: %d entries%s",
+          afi == AFI_IP ? "" : "v6",
+          plist->name, plist->count, VTY_NEWLINE);
+
+  for (pentry = plist->head; pentry; pentry = pentry->next)
+    {
+      struct prefix *p = &pentry->prefix;
+      char buf[BUFSIZ];
+
+      vty_out (vty, "   seq %d %s %s/%d", pentry->seq,
+              prefix_list_type_str (pentry),
+              inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+              p->prefixlen);
+
+      if (pentry->ge)
+       vty_out (vty, " ge %d", pentry->ge);
+      if (pentry->le)
+       vty_out (vty, " le %d", pentry->le);
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return plist->count;
+}
+
+static void
+prefix_list_reset_afi (afi_t afi, int orf)
+{
+  struct prefix_list *plist;
+  struct prefix_list *next;
+  struct prefix_master *master;
+
+  master = prefix_master_get (afi, orf);
+  if (master == NULL)
+    return;
+
+  for (plist = master->num.head; plist; plist = next)
+    {
+      next = plist->next;
+      prefix_list_delete (plist);
+    }
+  for (plist = master->str.head; plist; plist = next)
+    {
+      next = plist->next;
+      prefix_list_delete (plist);
+    }
+
+  assert (master->num.head == NULL);
+  assert (master->num.tail == NULL);
+
+  assert (master->str.head == NULL);
+  assert (master->str.tail == NULL);
+
+  master->seqnum = 1;
+  master->recent = NULL;
+}
+
+
+/* Prefix-list node. */
+static struct cmd_node prefix_node =
+{
+  PREFIX_NODE,
+  "",                          /* Prefix list has no interface. */
+  1
+};
+
+static int
+config_write_prefix_ipv4 (struct vty *vty)
+{
+  return config_write_prefix_afi (AFI_IP, vty);
+}
+
+static void
+prefix_list_init_ipv4 (void)
+{
+  install_node (&prefix_node, config_write_prefix_ipv4);
+
+  install_element (CONFIG_NODE, &ip_prefix_list_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_ge_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_ge_le_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_le_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_le_ge_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_seq_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_le_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_seq_le_cmd);
+  install_element (CONFIG_NODE, &ip_prefix_list_seq_le_ge_cmd);
+
+  install_element (CONFIG_NODE, &no_ip_prefix_list_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_prefix_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_ge_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_ge_le_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_le_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_le_ge_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_seq_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_le_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_ge_cmd);
+
+  install_element (CONFIG_NODE, &ip_prefix_list_description_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_description_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_description_arg_cmd);
+
+  install_element (CONFIG_NODE, &ip_prefix_list_sequence_number_cmd);
+  install_element (CONFIG_NODE, &no_ip_prefix_list_sequence_number_cmd);
+
+  install_element (VIEW_NODE, &show_ip_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_name_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_name_seq_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_prefix_first_match_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_summary_name_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_detail_cmd);
+  install_element (VIEW_NODE, &show_ip_prefix_list_detail_name_cmd);
+
+  install_element (ENABLE_NODE, &clear_ip_prefix_list_cmd);
+  install_element (ENABLE_NODE, &clear_ip_prefix_list_name_cmd);
+  install_element (ENABLE_NODE, &clear_ip_prefix_list_name_prefix_cmd);
+}
+
+/* Prefix-list node. */
+static struct cmd_node prefix_ipv6_node =
+{
+  PREFIX_IPV6_NODE,
+  "",                          /* Prefix list has no interface. */
+  1
+};
+
+static int
+config_write_prefix_ipv6 (struct vty *vty)
+{
+  return config_write_prefix_afi (AFI_IP6, vty);
+}
+
+static void
+prefix_list_init_ipv6 (void)
+{
+  install_node (&prefix_ipv6_node, config_write_prefix_ipv6);
+
+  install_element (CONFIG_NODE, &ipv6_prefix_list_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_ge_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_ge_le_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_le_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_le_ge_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_seq_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_le_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_cmd);
+  install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_ge_cmd);
+
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_prefix_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_le_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_ge_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_le_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_ge_cmd);
+
+  install_element (CONFIG_NODE, &ipv6_prefix_list_description_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_arg_cmd);
+
+  install_element (CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_prefix_list_sequence_number_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_name_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_name_seq_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_name_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_name_cmd);
+
+  install_element (ENABLE_NODE, &clear_ipv6_prefix_list_cmd);
+  install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_cmd);
+  install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_prefix_cmd);
+}
+
+void
+prefix_list_init ()
+{
+  prefix_list_init_ipv4 ();
+#ifdef HAVE_IPV6
+  prefix_list_init_ipv6 ();
+#endif /* HAVE_IPV6 */
+}
+
+void
+prefix_list_reset ()
+{
+  prefix_list_reset_afi (AFI_IP,  0);
+  prefix_list_reset_afi (AFI_IP6, 0);
+  prefix_list_reset_afi (AFI_IP,  1);
+  prefix_list_reset_afi (AFI_IP6, 1);
+}
diff --git a/lib/plist.h b/lib/plist.h
new file mode 100644 (file)
index 0000000..aa14e74
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Prefix list functions.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_PLIST_H
+#define _QUAGGA_PLIST_H
+
+enum prefix_list_type 
+{
+  PREFIX_DENY,
+  PREFIX_PERMIT,
+};
+
+struct prefix_list;
+
+struct orf_prefix
+{
+  u_int32_t seq;
+  u_char ge;
+  u_char le;
+  struct prefix p;
+};
+
+/* Prototypes. */
+extern void prefix_list_init (void);
+extern void prefix_list_reset (void);
+extern void prefix_list_add_hook (void (*func) (struct prefix_list *));
+extern void prefix_list_delete_hook (void (*func) (struct prefix_list *));
+
+extern const char *prefix_list_name (struct prefix_list *);
+extern struct prefix_list *prefix_list_lookup (afi_t, const char *);
+extern enum prefix_list_type prefix_list_apply (struct prefix_list *, void *);
+
+extern struct prefix_list *prefix_bgp_orf_lookup (afi_t, const char *);
+extern struct stream * prefix_bgp_orf_entry (struct stream *,
+                                             struct prefix_list *,
+                                             u_char, u_char, u_char);
+extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int);
+extern void prefix_bgp_orf_remove_all (afi_t, char *);
+extern int prefix_bgp_show_prefix_list (struct vty *, afi_t, char *);
+
+#endif /* _QUAGGA_PLIST_H */
diff --git a/lib/plist_int.h b/lib/plist_int.h
new file mode 100644 (file)
index 0000000..6459579
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Prefix list internal definitions.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_PLIST_INT_H
+#define _QUAGGA_PLIST_INT_H
+
+enum prefix_name_type
+{
+  PREFIX_TYPE_STRING,
+  PREFIX_TYPE_NUMBER
+};
+
+struct prefix_list
+{
+  char *name;
+  char *desc;
+
+  struct prefix_master *master;
+
+  enum prefix_name_type type;
+
+  int count;
+  int rangecount;
+
+  struct prefix_list_entry *head;
+  struct prefix_list_entry *tail;
+
+  struct prefix_list *next;
+  struct prefix_list *prev;
+};
+
+/* Each prefix-list's entry. */
+struct prefix_list_entry
+{
+  int seq;
+
+  int le;
+  int ge;
+
+  enum prefix_list_type type;
+
+  int any;
+  struct prefix prefix;
+
+  unsigned long refcnt;
+  unsigned long hitcnt;
+
+  struct prefix_list_entry *next;
+  struct prefix_list_entry *prev;
+};
+
+#endif /* _QUAGGA_PLIST_INT_H */
diff --git a/lib/pqueue.c b/lib/pqueue.c
new file mode 100644 (file)
index 0000000..69ab8e6
--- /dev/null
@@ -0,0 +1,187 @@
+/* Priority queue functions.
+   Copyright (C) 2003 Yasuhiro Ohara
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "pqueue.h"
+
+/* priority queue using heap sort */
+
+/* pqueue->cmp() controls the order of sorting (i.e, ascending or
+   descending). If you want the left node to move upper of the heap
+   binary tree, make cmp() to return less than 0.  for example, if cmp
+   (10, 20) returns -1, the sorting is ascending order. if cmp (10,
+   20) returns 1, the sorting is descending order. if cmp (10, 20)
+   returns 0, this library does not do sorting (which will not be what
+   you want).  To be brief, if the contents of cmp_func (left, right)
+   is left - right, dequeue () returns the smallest node.  Otherwise
+   (if the contents is right - left), dequeue () returns the largest
+   node.  */
+
+#define DATA_SIZE (sizeof (void *))
+#define PARENT_OF(x) ((x - 1) / 2)
+#define LEFT_OF(x)  (2 * x + 1)
+#define RIGHT_OF(x) (2 * x + 2)
+#define HAVE_CHILD(x,q) (x < (q)->size / 2)
+
+void
+trickle_up (int index, struct pqueue *queue)
+{
+  void *tmp;
+
+  /* Save current node as tmp node.  */
+  tmp = queue->array[index];
+
+  /* Continue until the node reaches top or the place where the parent
+     node should be upper than the tmp node.  */
+  while (index > 0 &&
+         (*queue->cmp) (tmp, queue->array[PARENT_OF (index)]) < 0)
+    {
+      /* actually trickle up */
+      queue->array[index] = queue->array[PARENT_OF (index)];
+      if (queue->update != NULL)
+       (*queue->update) (queue->array[index], index);
+      index = PARENT_OF (index);
+    }
+
+  /* Restore the tmp node to appropriate place.  */
+  queue->array[index] = tmp;
+  if (queue->update != NULL)
+    (*queue->update) (tmp, index);
+}
+
+void
+trickle_down (int index, struct pqueue *queue)
+{
+  void *tmp;
+  int which;
+
+  /* Save current node as tmp node.  */
+  tmp = queue->array[index];
+
+  /* Continue until the node have at least one (left) child.  */
+  while (HAVE_CHILD (index, queue))
+    {
+      /* If right child exists, and if the right child is more proper
+         to be moved upper.  */
+      if (RIGHT_OF (index) < queue->size &&
+          (*queue->cmp) (queue->array[LEFT_OF (index)],
+                         queue->array[RIGHT_OF (index)]) > 0)
+        which = RIGHT_OF (index);
+      else
+        which = LEFT_OF (index);
+
+      /* If the tmp node should be upper than the child, break.  */
+      if ((*queue->cmp) (queue->array[which], tmp) > 0)
+        break;
+
+      /* Actually trickle down the tmp node.  */
+      queue->array[index] = queue->array[which];
+       if (queue->update != NULL)
+        (*queue->update) (queue->array[index], index);
+      index = which;
+    }
+
+  /* Restore the tmp node to appropriate place.  */
+  queue->array[index] = tmp;
+  if (queue->update != NULL)
+    (*queue->update) (tmp, index);
+}
+
+struct pqueue *
+pqueue_create (void)
+{
+  struct pqueue *queue;
+
+  queue = XCALLOC (MTYPE_PQUEUE, sizeof (struct pqueue));
+
+  queue->array = XCALLOC (MTYPE_PQUEUE_DATA, 
+                          DATA_SIZE * PQUEUE_INIT_ARRAYSIZE);
+  queue->array_size = PQUEUE_INIT_ARRAYSIZE;
+
+  /* By default we want nothing to happen when a node changes. */
+  queue->update = NULL;
+  return queue;
+}
+
+void
+pqueue_delete (struct pqueue *queue)
+{
+  XFREE (MTYPE_PQUEUE_DATA, queue->array);
+  XFREE (MTYPE_PQUEUE, queue);
+}
+
+static int
+pqueue_expand (struct pqueue *queue)
+{
+  void **newarray;
+
+  newarray = XCALLOC (MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2);
+  if (newarray == NULL)
+    return 0;
+
+  memcpy (newarray, queue->array, queue->array_size * DATA_SIZE);
+
+  XFREE (MTYPE_PQUEUE_DATA, queue->array);
+  queue->array = newarray;
+  queue->array_size *= 2;
+
+  return 1;
+}
+
+void
+pqueue_enqueue (void *data, struct pqueue *queue)
+{
+  if (queue->size + 2 >= queue->array_size && ! pqueue_expand (queue))
+    return;
+
+  queue->array[queue->size] = data;
+  if (queue->update != NULL)
+    (*queue->update) (data, queue->size);
+  trickle_up (queue->size, queue);
+  queue->size ++;
+}
+
+void *
+pqueue_dequeue (struct pqueue *queue)
+{
+  void *data = queue->array[0];
+  queue->array[0] =  queue->array[--queue->size];
+  trickle_down (0, queue);
+  return data;
+}
+
+void
+pqueue_remove_at (int index, struct pqueue *queue)
+{
+  queue->array[index] = queue->array[--queue->size];
+
+  if (index > 0
+      && (*queue->cmp) (queue->array[index],
+                        queue->array[PARENT_OF(index)]) < 0)
+    {
+      trickle_up (index, queue);
+    }
+  else
+    {
+      trickle_down (index, queue);
+    }
+}
diff --git a/lib/pqueue.h b/lib/pqueue.h
new file mode 100644 (file)
index 0000000..8bb6961
--- /dev/null
@@ -0,0 +1,46 @@
+/* Priority queue functions.
+   Copyright (C) 2003 Yasuhiro Ohara
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifndef _ZEBRA_PQUEUE_H
+#define _ZEBRA_PQUEUE_H
+
+struct pqueue
+{
+  void **array;
+  int array_size;
+  int size;
+
+  int (*cmp) (void *, void *);
+  void (*update) (void * node, int actual_position);
+};
+
+#define PQUEUE_INIT_ARRAYSIZE  32
+
+extern struct pqueue *pqueue_create (void);
+extern void pqueue_delete (struct pqueue *queue);
+
+extern void pqueue_enqueue (void *data, struct pqueue *queue);
+extern void *pqueue_dequeue (struct pqueue *queue);
+extern void pqueue_remove_at (int index, 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/lib/prefix.c b/lib/prefix.c
new file mode 100644 (file)
index 0000000..43fc317
--- /dev/null
@@ -0,0 +1,1042 @@
+/*
+ * Prefix related functions.
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "log.h"
+
+/* Maskbit. */
+static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
+                                0xf8, 0xfc, 0xfe, 0xff};
+
+static const struct in6_addr maskbytes6[] =
+{
+  /* /0   */ { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /1   */ { { { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /2   */ { { { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /3   */ { { { 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /4   */ { { { 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /5   */ { { { 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /6   */ { { { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /7   */ { { { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /8   */ { { { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /9   */ { { { 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /10  */ { { { 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /11  */ { { { 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /12  */ { { { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /13  */ { { { 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /14  */ { { { 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /15  */ { { { 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /16  */ { { { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /17  */ { { { 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /18  */ { { { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /19  */ { { { 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /20  */ { { { 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /21  */ { { { 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /22  */ { { { 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /23  */ { { { 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /24  */ { { { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /25  */ { { { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /26  */ { { { 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /27  */ { { { 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /28  */ { { { 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /29  */ { { { 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /30  */ { { { 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /31  */ { { { 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /32  */ { { { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /33  */ { { { 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /34  */ { { { 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /35  */ { { { 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /36  */ { { { 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /37  */ { { { 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /38  */ { { { 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /39  */ { { { 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /40  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /41  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /42  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /43  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /44  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /45  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /46  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /47  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /48  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /49  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /50  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /51  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /52  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /53  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /54  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /55  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /56  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /57  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /58  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /59  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /60  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /61  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /62  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /63  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /64  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /65  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /66  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /67  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /68  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /69  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /70  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /71  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /72  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /73  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /74  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /75  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /76  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /77  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /78  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /79  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /80  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /81  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /82  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /83  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /84  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /85  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /86  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /87  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /88  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /89  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /90  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /91  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /92  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /93  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /94  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /95  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /96  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
+  /* /97  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00 } } },
+  /* /98  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00 } } },
+  /* /99  */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00 } } },
+  /* /100 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 } } },
+  /* /101 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00 } } },
+  /* /102 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00 } } },
+  /* /103 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 } } },
+  /* /104 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } } },
+  /* /105 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 } } },
+  /* /106 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00 } } },
+  /* /107 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00 } } },
+  /* /108 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 } } },
+  /* /109 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00 } } },
+  /* /110 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00 } } },
+  /* /111 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00 } } },
+  /* /112 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } } },
+  /* /113 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00 } } },
+  /* /114 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 } } },
+  /* /115 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00 } } },
+  /* /116 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00 } } },
+  /* /117 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00 } } },
+  /* /118 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00 } } },
+  /* /119 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00 } } },
+  /* /120 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 } } },
+  /* /121 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 } } },
+  /* /122 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 } } },
+  /* /123 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0 } } },
+  /* /124 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 } } },
+  /* /125 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8 } } },
+  /* /126 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc } } },
+  /* /127 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe } } },
+  /* /128 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }
+};
+
+/* Number of bits in prefix type. */
+#ifndef PNBBY
+#define PNBBY 8
+#endif /* PNBBY */
+
+#define MASKBIT(offset)  ((0xff << (PNBBY - (offset))) & 0xff)
+
+unsigned int
+prefix_bit (const u_char *prefix, const u_char prefixlen)
+{
+  unsigned int offset = prefixlen / 8;
+  unsigned int shift  = 7 - (prefixlen % 8);
+  
+  return (prefix[offset] >> shift) & 1;
+}
+
+unsigned int
+prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen)
+{
+  return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen);
+}
+
+int
+str2family(const char *string)
+{
+  if (!strcmp("ipv4", string))
+    return AF_INET;
+  else if (!strcmp("ipv6", string))
+    return AF_INET6;
+  else if (!strcmp("ethernet", string))
+    return AF_ETHERNET;
+  return -1;
+}
+
+/* Address Famiy Identifier to Address Family converter. */
+int
+afi2family (afi_t afi)
+{
+  if (afi == AFI_IP)
+    return AF_INET;
+#ifdef HAVE_IPV6
+  else if (afi == AFI_IP6)
+    return AF_INET6;
+#endif /* HAVE_IPV6 */
+  else if (afi == AFI_ETHER)
+    return AF_ETHERNET;
+  return 0;
+}
+
+afi_t
+family2afi (int family)
+{
+  if (family == AF_INET)
+    return AFI_IP;
+#ifdef HAVE_IPV6
+  else if (family == AF_INET6)
+    return AFI_IP6;
+#endif /* HAVE_IPV6 */
+  else if (family == AF_ETHERNET)
+    return AFI_ETHER;
+  return 0;
+}
+
+const char *
+afi2str(afi_t afi)
+{
+  switch (afi) {
+    case AFI_IP:
+       return "IPv4";
+    case AFI_IP6:
+       return "IPv6";
+    case AFI_ETHER:
+       return "ethernet";
+  }
+  return NULL;
+}
+
+const char *
+safi2str(safi_t safi)
+{
+  switch (safi) {
+    case SAFI_UNICAST:
+       return "unicast";
+    case SAFI_MULTICAST:
+       return "multicast";
+    case SAFI_ENCAP:
+       return "encap";
+    case SAFI_MPLS_VPN:
+       return "vpn";
+  }
+  return NULL;
+}
+
+/* If n includes p prefix then return 1 else return 0. */
+int
+prefix_match (const struct prefix *n, const struct prefix *p)
+{
+  int offset;
+  int shift;
+  const u_char *np, *pp;
+
+  /* If n's prefix is longer than p's one return 0. */
+  if (n->prefixlen > p->prefixlen)
+    return 0;
+
+  /* Set both prefix's head pointer. */
+  np = (const u_char *)&n->u.prefix;
+  pp = (const u_char *)&p->u.prefix;
+  
+  offset = n->prefixlen / PNBBY;
+  shift =  n->prefixlen % PNBBY;
+
+  if (shift)
+    if (maskbit[shift] & (np[offset] ^ pp[offset]))
+      return 0;
+  
+  while (offset--)
+    if (np[offset] != pp[offset])
+      return 0;
+  return 1;
+}
+
+/* Copy prefix from src to dest. */
+void
+prefix_copy (struct prefix *dest, const struct prefix *src)
+{
+  dest->family = src->family;
+  dest->prefixlen = src->prefixlen;
+
+  if (src->family == AF_INET)
+    dest->u.prefix4 = src->u.prefix4;
+#ifdef HAVE_IPV6
+  else if (src->family == AF_INET6)
+    dest->u.prefix6 = src->u.prefix6;
+#endif /* HAVE_IPV6 */
+  else if (src->family == AF_UNSPEC)
+    {
+      dest->u.lp.id = src->u.lp.id;
+      dest->u.lp.adv_router = src->u.lp.adv_router;
+    }
+  else if (src->family == AF_ETHERNET)
+    {
+      dest->u.prefix_eth = src->u.prefix_eth;
+    }
+  else
+    {
+      zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d",
+             src->family);
+      assert (0);
+    }
+}
+
+/* 
+ * Return 1 if the address/netmask contained in the prefix structure
+ * is the same, and else return 0.  For this routine, 'same' requires
+ * that not only the prefix length and the network part be the same,
+ * but also the host part.  Thus, 10.0.0.1/8 and 10.0.0.2/8 are not
+ * the same.  Note that this routine has the same return value sense
+ * as '==' (which is different from prefix_cmp).
+ */
+int
+prefix_same (const struct prefix *p1, const struct prefix *p2)
+{
+  if (p1->family == p2->family && p1->prefixlen == p2->prefixlen)
+    {
+      if (p1->family == AF_INET)
+       if (IPV4_ADDR_SAME (&p1->u.prefix4.s_addr, &p2->u.prefix4.s_addr))
+         return 1;
+#ifdef HAVE_IPV6
+      if (p1->family == AF_INET6 )
+       if (IPV6_ADDR_SAME (&p1->u.prefix6.s6_addr, &p2->u.prefix6.s6_addr))
+         return 1;
+#endif /* HAVE_IPV6 */
+      if (p1->family == AF_ETHERNET) {
+       if (!memcmp(p1->u.prefix_eth.octet, p2->u.prefix_eth.octet, ETHER_ADDR_LEN))
+           return 1;
+      }
+    }
+  return 0;
+}
+
+/*
+ * Return 0 if the network prefixes represented by the struct prefix
+ * arguments are the same prefix, and 1 otherwise.  Network prefixes
+ * are considered the same if the prefix lengths are equal and the
+ * network parts are the same.  Host bits (which are considered masked
+ * by the prefix length) are not significant.  Thus, 10.0.0.1/8 and
+ * 10.0.0.2/8 are considered equivalent by this routine.  Note that
+ * this routine has the same return sense as strcmp (which is different
+ * from prefix_same).
+ */
+int
+prefix_cmp (const struct prefix *p1, const struct prefix *p2)
+{
+  int offset;
+  int shift;
+
+  /* Set both prefix's head pointer. */
+  const u_char *pp1 = (const u_char *)&p1->u.prefix;
+  const u_char *pp2 = (const u_char *)&p2->u.prefix;
+
+  if (p1->family != p2->family || p1->prefixlen != p2->prefixlen)
+    return 1;
+
+  offset = p1->prefixlen / PNBBY;
+  shift = p1->prefixlen % PNBBY;
+
+  if (shift)
+    if (maskbit[shift] & (pp1[offset] ^ pp2[offset]))
+      return 1;
+
+  while (offset--)
+    if (pp1[offset] != pp2[offset])
+      return 1;
+
+  return 0;
+}
+
+/*
+ * Count the number of common bits in 2 prefixes. The prefix length is
+ * ignored for this function; the whole prefix is compared. If the prefix
+ * address families don't match, return -1; otherwise the return value is
+ * in range 0 ... maximum prefix length for the address family.
+ */
+int
+prefix_common_bits (const struct prefix *p1, const struct prefix *p2)
+{
+  int pos, bit;
+  int length = 0;
+  u_char xor;
+
+  /* Set both prefix's head pointer. */
+  const u_char *pp1 = (const u_char *)&p1->u.prefix;
+  const u_char *pp2 = (const u_char *)&p2->u.prefix;
+
+  if (p1->family == AF_INET)
+    length = IPV4_MAX_BYTELEN;
+#ifdef HAVE_IPV6
+  if (p1->family == AF_INET6)
+    length = IPV6_MAX_BYTELEN;
+#endif
+  if (p1->family != p2->family || !length)
+    return -1;
+
+  for (pos = 0; pos < length; pos++)
+    if (pp1[pos] != pp2[pos])
+      break;
+  if (pos == length)
+    return pos * 8;
+
+  xor = pp1[pos] ^ pp2[pos];
+  for (bit = 0; bit < 8; bit++)
+    if (xor & (1 << (7 - bit)))
+      break;
+
+  return pos * 8 + bit;
+}
+
+/* Return prefix family type string. */
+const char *
+prefix_family_str (const struct prefix *p)
+{
+  if (p->family == AF_INET)
+    return "inet";
+#ifdef HAVE_IPV6
+  if (p->family == AF_INET6)
+    return "inet6";
+#endif /* HAVE_IPV6 */
+  if (p->family == AF_ETHERNET)
+    return "ether";
+  return "unspec";
+}
+
+/* Allocate new prefix_ipv4 structure. */
+struct prefix_ipv4 *
+prefix_ipv4_new ()
+{
+  struct prefix_ipv4 *p;
+
+  /* Call prefix_new to allocate a full-size struct prefix to avoid problems
+     where the struct prefix_ipv4 is cast to struct prefix and unallocated
+     bytes were being referenced (e.g. in structure assignments). */
+  p = (struct prefix_ipv4 *)prefix_new();
+  p->family = AF_INET;
+  return p;
+}
+
+/* Free prefix_ipv4 structure. */
+void
+prefix_ipv4_free (struct prefix_ipv4 *p)
+{
+  prefix_free((struct prefix *)p);
+}
+
+/* When string format is invalid return 0. */
+int
+str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
+{
+  int ret;
+  int plen;
+  char *pnt;
+  char *cp;
+
+  /* Find slash inside string. */
+  pnt = strchr (str, '/');
+
+  /* String doesn't contail slash. */
+  if (pnt == NULL) 
+    {
+      /* Convert string to prefix. */
+      ret = inet_aton (str, &p->prefix);
+      if (ret == 0)
+       return 0;
+
+      /* If address doesn't contain slash we assume it host address. */
+      p->family = AF_INET;
+      p->prefixlen = IPV4_MAX_BITLEN;
+
+      return ret;
+    }
+  else
+    {
+      cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
+      strncpy (cp, str, pnt - str);
+      *(cp + (pnt - str)) = '\0';
+      ret = inet_aton (cp, &p->prefix);
+      XFREE (MTYPE_TMP, cp);
+
+      /* Get prefix length. */
+      plen = (u_char) atoi (++pnt);
+      if (plen > IPV4_MAX_PREFIXLEN)
+       return 0;
+
+      p->family = AF_INET;
+      p->prefixlen = plen;
+    }
+
+  return ret;
+}
+
+/* When string format is invalid return 0. */
+int
+str2prefix_eth (const char *str, struct prefix_eth *p)
+{
+  int          ret = 0;
+  int          plen = 48;
+  char         *pnt;
+  char         *cp = NULL;
+  const char   *str_addr = str;
+  unsigned int a[6];
+  int          i;
+
+  /* Find slash inside string. */
+  pnt = strchr (str, '/');
+
+  if (pnt)
+    {
+      /* Get prefix length. */
+      plen = (u_char) atoi (++pnt);
+      if (plen > 48)
+       {
+         ret = 0;
+         goto done;
+       }
+
+      cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
+      strncpy (cp, str, pnt - str);
+      *(cp + (pnt - str)) = '\0';
+
+      str_addr = cp;
+    }
+
+  /* Convert string to prefix. */
+  if (sscanf(str_addr, "%2x:%2x:%2x:%2x:%2x:%2x",
+    a+0, a+1, a+2, a+3, a+4, a+5) != 6)
+    {
+      ret = 0;
+      goto done;
+    }
+  for (i = 0; i < 6; ++i)
+    {
+      p->eth_addr.octet[i] = a[i] & 0xff;
+    }
+  p->prefixlen = plen;
+  p->family = AF_ETHERNET;
+  ret = 1;
+
+done:
+  if (cp)
+    XFREE (MTYPE_TMP, cp);
+
+  return ret;
+}
+
+/* Convert masklen into IP address's netmask (network byte order). */
+void
+masklen2ip (const int masklen, struct in_addr *netmask)
+{
+  assert (masklen >= 0 && masklen <= IPV4_MAX_BITLEN);
+
+  /* left shift is only defined for less than the size of the type.
+   * we unconditionally use long long in case the target platform
+   * has defined behaviour for << 32 (or has a 64-bit left shift) */
+
+  if (sizeof(unsigned long long) > 4)
+    netmask->s_addr = htonl(0xffffffffULL << (32 - masklen));
+  else
+    netmask->s_addr = htonl(masklen ? 0xffffffffU << (32 - masklen) : 0);
+}
+
+/* Convert IP address's netmask into integer. We assume netmask is
+   sequential one. Argument netmask should be network byte order. */
+u_char
+ip_masklen (struct in_addr netmask)
+{
+  uint32_t tmp = ~ntohl(netmask.s_addr);
+  if (tmp)
+    /* clz: count leading zeroes. sadly, the behaviour of this builtin
+     * is undefined for a 0 argument, even though most CPUs give 32 */
+    return __builtin_clz(tmp);
+  else
+    return 32;
+}
+
+/* Apply mask to IPv4 prefix (network byte order). */
+void
+apply_mask_ipv4 (struct prefix_ipv4 *p)
+{
+  struct in_addr mask;
+  masklen2ip(p->prefixlen, &mask);
+  p->prefix.s_addr &= mask.s_addr;
+}
+
+/* If prefix is 0.0.0.0/0 then return 1 else return 0. */
+int
+prefix_ipv4_any (const struct prefix_ipv4 *p)
+{
+  return (p->prefix.s_addr == 0 && p->prefixlen == 0);
+}
+
+#ifdef HAVE_IPV6
+
+/* Allocate a new ip version 6 route */
+struct prefix_ipv6 *
+prefix_ipv6_new (void)
+{
+  struct prefix_ipv6 *p;
+
+  /* Allocate a full-size struct prefix to avoid problems with structure
+     size mismatches. */
+  p = (struct prefix_ipv6 *)prefix_new();
+  p->family = AF_INET6;
+  return p;
+}
+
+/* Free prefix for IPv6. */
+void
+prefix_ipv6_free (struct prefix_ipv6 *p)
+{
+  prefix_free((struct prefix *)p);
+}
+
+/* If given string is valid return pin6 else return NULL */
+int
+str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
+{
+  char *pnt;
+  char *cp;
+  int ret;
+
+  pnt = strchr (str, '/');
+
+  /* If string doesn't contain `/' treat it as host route. */
+  if (pnt == NULL) 
+    {
+      ret = inet_pton (AF_INET6, str, &p->prefix);
+      if (ret == 0)
+       return 0;
+      p->prefixlen = IPV6_MAX_BITLEN;
+    }
+  else 
+    {
+      int plen;
+
+      cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
+      strncpy (cp, str, pnt - str);
+      *(cp + (pnt - str)) = '\0';
+      ret = inet_pton (AF_INET6, cp, &p->prefix);
+      free (cp);
+      if (ret == 0)
+       return 0;
+      plen = (u_char) atoi (++pnt);
+      if (plen > IPV6_MAX_BITLEN)
+       return 0;
+      p->prefixlen = plen;
+    }
+  p->family = AF_INET6;
+
+  return ret;
+}
+
+/* Convert struct in6_addr netmask into integer.
+ * FIXME return u_char as ip_maskleni() does. */
+int
+ip6_masklen (struct in6_addr netmask)
+{
+  int len = 0;
+  unsigned char val;
+  unsigned char *pnt;
+  
+  pnt = (unsigned char *) & netmask;
+
+  while ((*pnt == 0xff) && len < IPV6_MAX_BITLEN)
+    {
+      len += 8;
+      pnt++;
+    } 
+  
+  if (len < IPV6_MAX_BITLEN)
+    {
+      val = *pnt;
+      while (val) 
+       {
+         len++;
+         val <<= 1;
+       }
+    }
+  return len;
+}
+
+void
+masklen2ip6 (const int masklen, struct in6_addr *netmask)
+{
+  assert (masklen >= 0 && masklen <= IPV6_MAX_BITLEN);
+  memcpy (netmask, maskbytes6 + masklen, sizeof (struct in6_addr));
+}
+
+void
+apply_mask_ipv6 (struct prefix_ipv6 *p)
+{
+  u_char *pnt;
+  int index;
+  int offset;
+
+  index = p->prefixlen / 8;
+
+  if (index < 16)
+    {
+      pnt = (u_char *) &p->prefix;
+      offset = p->prefixlen % 8;
+
+      pnt[index] &= maskbit[offset];
+      index++;
+
+      while (index < 16)
+       pnt[index++] = 0;
+    }
+}
+
+void
+str2in6_addr (const char *str, struct in6_addr *addr)
+{
+  int i;
+  unsigned int x;
+
+  /* %x must point to unsinged int */
+  for (i = 0; i < 16; i++)
+    {
+      sscanf (str + (i * 2), "%02x", &x);
+      addr->s6_addr[i] = x & 0xff;
+    }
+}
+#endif /* HAVE_IPV6 */
+
+void
+apply_mask (struct prefix *p)
+{
+  switch (p->family)
+    {
+      case AF_INET:
+        apply_mask_ipv4 ((struct prefix_ipv4 *)p);
+        break;
+#ifdef HAVE_IPV6
+      case AF_INET6:
+        apply_mask_ipv6 ((struct prefix_ipv6 *)p);
+        break;
+#endif /* HAVE_IPV6 */
+      default:
+        break;
+    }
+  return;
+}
+
+/* Utility function of convert between struct prefix <=> union sockunion.
+ * FIXME This function isn't used anywhere. */
+struct prefix *
+sockunion2prefix (const union sockunion *dest,
+                 const union sockunion *mask)
+{
+  if (dest->sa.sa_family == AF_INET)
+    {
+      struct prefix_ipv4 *p;
+
+      p = prefix_ipv4_new ();
+      p->family = AF_INET;
+      p->prefix = dest->sin.sin_addr;
+      p->prefixlen = ip_masklen (mask->sin.sin_addr);
+      return (struct prefix *) p;
+    }
+#ifdef HAVE_IPV6
+  if (dest->sa.sa_family == AF_INET6)
+    {
+      struct prefix_ipv6 *p;
+
+      p = prefix_ipv6_new ();
+      p->family = AF_INET6;
+      p->prefixlen = ip6_masklen (mask->sin6.sin6_addr);
+      memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr));
+      return (struct prefix *) p;
+    }
+#endif /* HAVE_IPV6 */
+  return NULL;
+}
+
+/* Utility function of convert between struct prefix <=> union sockunion. */
+struct prefix *
+sockunion2hostprefix (const union sockunion *su, struct prefix *prefix)
+{
+  if (su->sa.sa_family == AF_INET)
+    {
+      struct prefix_ipv4 *p;
+
+      p = prefix ? (struct prefix_ipv4 *) prefix : prefix_ipv4_new ();
+      p->family = AF_INET;
+      p->prefix = su->sin.sin_addr;
+      p->prefixlen = IPV4_MAX_BITLEN;
+      return (struct prefix *) p;
+    }
+#ifdef HAVE_IPV6
+  if (su->sa.sa_family == AF_INET6)
+    {
+      struct prefix_ipv6 *p;
+
+      p = prefix ? (struct prefix_ipv6 *) prefix : prefix_ipv6_new ();
+      p->family = AF_INET6;
+      p->prefixlen = IPV6_MAX_BITLEN;
+      memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr));
+      return (struct prefix *) p;
+    }
+#endif /* HAVE_IPV6 */
+  return NULL;
+}
+
+void
+prefix2sockunion (const struct prefix *p, union sockunion *su)
+{
+  memset (su, 0, sizeof (*su));
+
+  su->sa.sa_family = p->family;
+  if (p->family == AF_INET)
+    su->sin.sin_addr = p->u.prefix4;
+#ifdef HAVE_IPV6
+  if (p->family == AF_INET6)
+    memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr));
+#endif /* HAVE_IPV6 */
+}
+
+int
+prefix_blen (const struct prefix *p)
+{
+  switch (p->family) 
+    {
+    case AF_INET:
+      return IPV4_MAX_BYTELEN;
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return IPV6_MAX_BYTELEN;
+      break;
+#endif /* HAVE_IPV6 */
+    case AF_ETHERNET:
+      return ETHER_ADDR_LEN;
+    }
+  return 0;
+}
+
+/* Generic function for conversion string to struct prefix. */
+int
+str2prefix (const char *str, struct prefix *p)
+{
+  int ret;
+
+  /* First we try to convert string to struct prefix_ipv4. */
+  ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p);
+  if (ret)
+    return ret;
+
+#ifdef HAVE_IPV6
+  /* Next we try to convert string to struct prefix_ipv6. */
+  ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
+  if (ret)
+    return ret;
+#endif /* HAVE_IPV6 */
+
+  /* Next we try to convert string to struct prefix_eth. */
+  ret = str2prefix_eth (str, (struct prefix_eth *) p);
+  if (ret)
+    return ret;
+
+  return 0;
+}
+
+const char *
+prefix2str (union prefix46constptr pu, char *str, int size)
+{
+  const struct prefix *p = pu.p;
+  char buf[BUFSIZ];
+
+  if (p->family == AF_ETHERNET) {
+    int                i;
+    char       *s = str;
+
+    assert(size > (3*ETHER_ADDR_LEN) + 1 /* slash */ + 3 /* plen */ );
+    for (i = 0; i < ETHER_ADDR_LEN; ++i) {
+       sprintf(s, "%02x", p->u.prefix_eth.octet[i]);
+       if (i < (ETHER_ADDR_LEN - 1)) {
+           *(s+2) = ':';
+           s += 3;
+       } else {
+           s += 2;
+       }
+    }
+    sprintf(s, "/%d", p->prefixlen);
+    return 0;
+  }
+
+  inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ);
+  snprintf (str, size, "%s/%d", buf, p->prefixlen);
+  return str;
+}
+
+struct prefix *
+prefix_new ()
+{
+  struct prefix *p;
+
+  p = XCALLOC (MTYPE_PREFIX, sizeof *p);
+  return p;
+}
+
+/* Free prefix structure. */
+void
+prefix_free (struct prefix *p)
+{
+  XFREE (MTYPE_PREFIX, p);
+}
+
+/* Utility function.  Check the string only contains digit
+ * character.
+ * FIXME str.[c|h] would be better place for this function. */
+int
+all_digit (const char *str)
+{
+  for (; *str != '\0'; str++)
+    if (!isdigit ((int) *str))
+      return 0;
+  return 1;
+}
+
+/* Utility function to convert ipv4 prefixes to Classful prefixes */
+void apply_classful_mask_ipv4 (struct prefix_ipv4 *p)
+{
+
+  u_int32_t destination;
+  
+  destination = ntohl (p->prefix.s_addr);
+  
+  if (p->prefixlen == IPV4_MAX_PREFIXLEN);
+  /* do nothing for host routes */
+  else if (IN_CLASSC (destination)) 
+    {
+      p->prefixlen=24;
+      apply_mask_ipv4(p);
+    }
+  else if (IN_CLASSB(destination)) 
+    {
+      p->prefixlen=16;
+      apply_mask_ipv4(p);
+    }
+  else 
+    {
+      p->prefixlen=8;
+      apply_mask_ipv4(p);
+    }
+}
+
+in_addr_t
+ipv4_network_addr (in_addr_t hostaddr, int masklen)
+{
+  struct in_addr mask;
+
+  masklen2ip (masklen, &mask);
+  return hostaddr & mask.s_addr;
+}
+
+in_addr_t
+ipv4_broadcast_addr (in_addr_t hostaddr, int masklen)
+{
+  struct in_addr mask;
+
+  masklen2ip (masklen, &mask);
+  return (masklen != IPV4_MAX_PREFIXLEN-1) ?
+        /* normal case */
+         (hostaddr | ~mask.s_addr) :
+        /* special case for /31 */
+         (hostaddr ^ ~mask.s_addr);
+}
+
+/* Utility function to convert ipv4 netmask to prefixes 
+   ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
+   ex.) "1.0.0.0" NULL => "1.0.0.0/8"                   */
+int
+netmask_str2prefix_str (const char *net_str, const char *mask_str,
+                       char *prefix_str)
+{
+  struct in_addr network;
+  struct in_addr mask;
+  u_char prefixlen;
+  u_int32_t destination;
+  int ret;
+
+  ret = inet_aton (net_str, &network);
+  if (! ret)
+    return 0;
+
+  if (mask_str)
+    {
+      ret = inet_aton (mask_str, &mask);
+      if (! ret)
+        return 0;
+
+      prefixlen = ip_masklen (mask);
+    }
+  else 
+    {
+      destination = ntohl (network.s_addr);
+
+      if (network.s_addr == 0)
+       prefixlen = 0;
+      else if (IN_CLASSC (destination))
+       prefixlen = 24;
+      else if (IN_CLASSB (destination))
+       prefixlen = 16;
+      else if (IN_CLASSA (destination))
+       prefixlen = 8;
+      else
+       return 0;
+    }
+
+  sprintf (prefix_str, "%s/%d", net_str, prefixlen);
+
+  return 1;
+}
+
+#ifdef HAVE_IPV6
+/* Utility function for making IPv6 address string. */
+const char *
+inet6_ntoa (struct in6_addr addr)
+{
+  static char buf[INET6_ADDRSTRLEN];
+
+  inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN);
+  return buf;
+}
+#endif /* HAVE_IPV6 */
diff --git a/lib/prefix.h b/lib/prefix.h
new file mode 100644 (file)
index 0000000..2cf0b20
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Prefix structure.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_PREFIX_H
+#define _ZEBRA_PREFIX_H
+
+#ifdef SUNOS_5
+# include <sys/ethernet.h>
+#else
+# ifdef GNU_LINUX
+#  include <net/ethernet.h>
+# else
+#  include <netinet/if_ether.h>
+# endif
+#endif
+#include "sockunion.h"
+
+#ifndef ETHER_ADDR_LEN
+#define ETHER_ADDR_LEN  ETHERADDRL
+#endif
+
+/*
+ * there isn't a portable ethernet address type. We define our
+ * own to simplify internal handling
+ */
+struct ethaddr {
+    u_char octet[ETHER_ADDR_LEN];
+} __packed;
+
+
+/*
+ * A struct prefix contains an address family, a prefix length, and an
+ * address.  This can represent either a 'network prefix' as defined
+ * by CIDR, where the 'host bits' of the prefix are 0
+ * (e.g. AF_INET:10.0.0.0/8), or an address and netmask
+ * (e.g. AF_INET:10.0.0.9/8), such as might be configured on an
+ * interface.
+ */
+
+/* different OSes use different names */
+#if defined(AF_PACKET)
+#define AF_ETHERNET AF_PACKET
+#else
+#if defined(AF_LINK)
+#define AF_ETHERNET AF_LINK
+#endif
+#endif
+
+/* IPv4 and IPv6 unified prefix structure. */
+struct prefix
+{
+  u_char family;
+  u_char prefixlen;
+  union 
+  {
+    u_char prefix;
+    struct in_addr prefix4;
+#ifdef HAVE_IPV6
+    struct in6_addr prefix6;
+#endif /* HAVE_IPV6 */
+    struct 
+    {
+      struct in_addr id;
+      struct in_addr adv_router;
+    } lp;
+    struct ethaddr prefix_eth; /* AF_ETHERNET */
+    u_char val[8];
+    uintptr_t ptr;
+  } u __attribute__ ((aligned (8)));
+};
+
+/* IPv4 prefix structure. */
+struct prefix_ipv4
+{
+  u_char family;
+  u_char prefixlen;
+  struct in_addr prefix __attribute__ ((aligned (8)));
+};
+
+/* IPv6 prefix structure. */
+#ifdef HAVE_IPV6
+struct prefix_ipv6
+{
+  u_char family;
+  u_char prefixlen;
+  struct in6_addr prefix __attribute__ ((aligned (8)));
+};
+#endif /* HAVE_IPV6 */
+
+struct prefix_ls
+{
+  u_char family;
+  u_char prefixlen;
+  struct in_addr id __attribute__ ((aligned (8)));
+  struct in_addr adv_router;
+};
+
+/* Prefix for routing distinguisher. */
+struct prefix_rd
+{
+  u_char family;
+  u_char prefixlen;
+  u_char val[8] __attribute__ ((aligned (8)));
+};
+
+/* Prefix for ethernet. */
+struct prefix_eth
+{
+  u_char family;
+  u_char prefixlen;
+  struct ethaddr eth_addr __attribute__ ((aligned (8))); /* AF_ETHERNET */
+};
+
+/* Prefix for a generic pointer */
+struct prefix_ptr
+{
+  u_char family;
+  u_char prefixlen;
+  uintptr_t prefix __attribute__ ((aligned (8)));
+};
+
+/* helper to get type safety/avoid casts on calls
+ * (w/o this, functions accepting all prefix types need casts on the caller
+ * side, which strips type safety since the cast will accept any pointer
+ * type.)
+ */
+union prefix46ptr
+{
+  struct prefix *p;
+  struct prefix_ipv4 *p4;
+  struct prefix_ipv6 *p6;
+} __attribute__ ((transparent_union));
+
+union prefix46constptr
+{
+  const struct prefix *p;
+  const struct prefix_ipv4 *p4;
+  const struct prefix_ipv6 *p6;
+} __attribute__ ((transparent_union));
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif /* INET_ADDRSTRLEN */
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif /* INET6_ADDRSTRLEN */
+
+#ifndef INET6_BUFSIZ
+#define INET6_BUFSIZ 51
+#endif /* INET6_BUFSIZ */
+
+/* Maximum prefix string length (IPv6) */
+#define PREFIX_STRLEN 51
+
+/* Max bit/byte length of IPv4 address. */
+#define IPV4_MAX_BYTELEN    4
+#define IPV4_MAX_BITLEN    32
+#define IPV4_MAX_PREFIXLEN 32
+#define IPV4_ADDR_CMP(D,S)   memcmp ((D), (S), IPV4_MAX_BYTELEN)
+#define IPV4_ADDR_SAME(D,S)  (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0)
+#define IPV4_ADDR_COPY(D,S)  memcpy ((D), (S), IPV4_MAX_BYTELEN)
+
+#define IPV4_NET0(a)    ((((u_int32_t) (a)) & 0xff000000) == 0x00000000)
+#define IPV4_NET127(a)  ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
+#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
+#define IPV4_CLASS_DE(a)  ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
+
+/* Max bit/byte length of IPv6 address. */
+#define IPV6_MAX_BYTELEN    16
+#define IPV6_MAX_BITLEN    128
+#define IPV6_MAX_PREFIXLEN 128
+#define IPV6_ADDR_CMP(D,S)   memcmp ((D), (S), IPV6_MAX_BYTELEN)
+#define IPV6_ADDR_SAME(D,S)  (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0)
+#define IPV6_ADDR_COPY(D,S)  memcpy ((D), (S), IPV6_MAX_BYTELEN)
+
+/* Count prefix size from mask length */
+#define PSIZE(a) (((a) + 7) / (8))
+
+/* Prefix's family member. */
+#define PREFIX_FAMILY(p)  ((p)->family)
+
+/* glibc defines s6_addr32 to __in6_u.__u6_addr32 if __USE_{MISC || GNU} */
+#ifndef s6_addr32
+#if defined(SUNOS_5)
+/* Some SunOS define s6_addr32 only to kernel */
+#define s6_addr32 _S6_un._S6_u32
+#else
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif /* SUNOS_5 */
+#endif /*s6_addr32*/
+
+/* Prototypes. */
+extern int str2family(const char *);
+extern int afi2family (afi_t);
+extern afi_t family2afi (int);
+extern const char *safi2str(safi_t safi);
+extern const char *afi2str(afi_t afi);
+
+/* Check bit of the prefix. */
+extern unsigned int prefix_bit (const u_char *prefix, const u_char prefixlen);
+extern unsigned int prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen);
+
+extern struct prefix *prefix_new (void);
+extern void prefix_free (struct prefix *);
+extern const char *prefix_family_str (const struct prefix *);
+extern int prefix_blen (const struct prefix *);
+extern int str2prefix (const char *, struct prefix *);
+extern const char *prefix2str (union prefix46constptr, char *, int);
+extern int prefix_match (const struct prefix *, const struct prefix *);
+extern int prefix_same (const struct prefix *, const struct prefix *);
+extern int prefix_cmp (const struct prefix *, const struct prefix *);
+extern int prefix_common_bits (const struct prefix *, const struct prefix *);
+extern void prefix_copy (struct prefix *dest, const struct prefix *src);
+extern void apply_mask (struct prefix *);
+
+extern struct prefix *sockunion2prefix (const union sockunion *dest,
+                                        const union sockunion *mask);
+extern struct prefix *sockunion2hostprefix (const union sockunion *, struct prefix *p);
+extern void prefix2sockunion (const struct prefix *, union sockunion *);
+
+extern int str2prefix_eth (const char *, struct prefix_eth *);
+
+extern struct prefix_ipv4 *prefix_ipv4_new (void);
+extern void prefix_ipv4_free (struct prefix_ipv4 *);
+extern int str2prefix_ipv4 (const char *, struct prefix_ipv4 *);
+extern void apply_mask_ipv4 (struct prefix_ipv4 *);
+
+#define PREFIX_COPY_IPV4(DST, SRC)     \
+       *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC));
+
+extern int prefix_ipv4_any (const struct prefix_ipv4 *);
+extern void apply_classful_mask_ipv4 (struct prefix_ipv4 *);
+
+extern u_char ip_masklen (struct in_addr);
+extern void masklen2ip (const int, struct in_addr *);
+/* returns the network portion of the host address */
+extern in_addr_t ipv4_network_addr (in_addr_t hostaddr, int masklen);
+/* given the address of a host on a network and the network mask length,
+ * calculate the broadcast address for that network;
+ * special treatment for /31: returns the address of the other host
+ * on the network by flipping the host bit */
+extern in_addr_t ipv4_broadcast_addr (in_addr_t hostaddr, int masklen);
+
+extern int netmask_str2prefix_str (const char *, const char *, char *);
+
+#ifdef HAVE_IPV6
+extern struct prefix_ipv6 *prefix_ipv6_new (void);
+extern void prefix_ipv6_free (struct prefix_ipv6 *);
+extern int str2prefix_ipv6 (const char *, struct prefix_ipv6 *);
+extern void apply_mask_ipv6 (struct prefix_ipv6 *);
+
+#define PREFIX_COPY_IPV6(DST, SRC)     \
+       *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC));
+
+extern int ip6_masklen (struct in6_addr);
+extern void masklen2ip6 (const int, struct in6_addr *);
+
+extern void str2in6_addr (const char *, struct in6_addr *);
+extern const char *inet6_ntoa (struct in6_addr);
+
+#endif /* HAVE_IPV6 */
+
+extern int all_digit (const char *);
+
+static inline int ipv4_martian (struct in_addr *addr)
+{
+  in_addr_t ip = addr->s_addr;
+
+  if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_DE(ip)) {
+    return 1;
+  }
+  return 0;
+}
+
+#endif /* _ZEBRA_PREFIX_H */
diff --git a/lib/privs.c b/lib/privs.c
new file mode 100644 (file)
index 0000000..3fb96ae
--- /dev/null
@@ -0,0 +1,852 @@
+/* 
+ * Zebra privileges.
+ *
+ * Copyright (C) 2003 Paul Jakma.
+ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+#include <zebra.h>
+#include "log.h"
+#include "privs.h"
+#include "memory.h"
+
+#ifdef HAVE_CAPABILITIES
+/* sort out some generic internal types for:
+ *
+ * privilege values (cap_value_t, priv_t)      -> pvalue_t
+ * privilege set (..., priv_set_t)             -> pset_t
+ * privilege working storage (cap_t, ...)      -> pstorage_t
+ *
+ * values we think of as numeric (they're ints really, but we dont know)
+ * sets are mostly opaque, to hold a set of privileges, related in some way.
+ * storage binds together a set of sets we're interested in.
+ * (in reality: cap_value_t and priv_t are ints)
+ */ 
+#ifdef HAVE_LCAPS
+/* Linux doesn't have a 'set' type: a set of related privileges */
+struct _pset {
+  int num;
+  cap_value_t *caps;
+};
+typedef cap_value_t pvalue_t;
+typedef struct _pset pset_t;
+typedef cap_t pstorage_t;
+
+#elif defined (HAVE_SOLARIS_CAPABILITIES)
+typedef priv_t pvalue_t;
+typedef priv_set_t pset_t;
+typedef priv_set_t *pstorage_t;
+#else /* neither LCAPS nor SOLARIS_CAPABILITIES */
+#error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!"
+#endif /* HAVE_LCAPS */
+#endif /* HAVE_CAPABILITIES */
+
+/* the default NULL state we report is RAISED, but could be LOWERED if
+ * zprivs_terminate is called and the NULL handler is installed.
+ */
+static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED;
+
+/* internal privileges state */
+static struct _zprivs_t
+{
+#ifdef HAVE_CAPABILITIES
+  pstorage_t caps;             /* working storage        */
+  pset_t *syscaps_p;           /* system-type requested permitted caps    */
+  pset_t *syscaps_i;           /* system-type requested inheritable caps  */
+#endif /* HAVE_CAPABILITIES */
+  uid_t zuid,                 /* uid to run as            */
+        zsuid;                /* saved uid                */
+  gid_t zgid;                 /* gid to run as            */
+  gid_t vtygrp;               /* gid for vty sockets      */
+} zprivs_state;
+
+/* externally exported but not directly accessed functions */
+#ifdef HAVE_CAPABILITIES
+int zprivs_change_caps (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_caps (void);
+#endif /* HAVE_CAPABILITIES */
+int zprivs_change_uid (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_uid (void);
+int zprivs_change_null (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_null (void);
+
+#ifdef HAVE_CAPABILITIES
+/* internal capability API */
+static pset_t *zcaps2sys (zebra_capabilities_t *, int);
+static void zprivs_caps_init (struct zebra_privs_t *);
+static void zprivs_caps_terminate (void);
+
+/* Map of Quagga abstract capabilities to system capabilities */
+static struct
+{
+  int num;
+  pvalue_t *system_caps;
+} cap_map [ZCAP_MAX] =
+{
+#ifdef HAVE_LCAPS /* Quagga -> Linux capabilities mappings */
+  [ZCAP_SETID] =       { 2, (pvalue_t []) { CAP_SETGID,
+                                             CAP_SETUID                }, },
+  [ZCAP_BIND] =                { 2, (pvalue_t []) { CAP_NET_BIND_SERVICE       }, },
+  [ZCAP_NET_ADMIN] =   { 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               }, },
+  [ZCAP_PTRACE] =      { 1, (pvalue_t []) { CAP_SYS_PTRACE             }, },
+  [ZCAP_DAC_OVERRIDE] = { 1, (pvalue_t []) { CAP_DAC_OVERRIDE          }, },
+  [ZCAP_READ_SEARCH] =  { 1, (pvalue_t []) { CAP_DAC_READ_SEARCH       }, },
+  [ZCAP_SYS_ADMIN] =   { 1, (pvalue_t []) { CAP_SYS_ADMIN              }, },
+  [ZCAP_FOWNER] =      { 1, (pvalue_t []) { CAP_FOWNER                 }, },
+#elif defined(HAVE_SOLARIS_CAPABILITIES) /* HAVE_LCAPS */
+  /* 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           }, },
+  [ZCAP_NICE] =        { 1, (pvalue_t []) { PRIV_PROC_PRIOCNTL         }, },
+  [ZCAP_PTRACE] =      { 1, (pvalue_t []) { PRIV_PROC_SESSION          }, },
+  [ZCAP_DAC_OVERRIDE] = { 2, (pvalue_t []) { PRIV_FILE_DAC_EXECUTE, 
+                                             PRIV_FILE_DAC_READ,
+                                             PRIV_FILE_DAC_SEARCH,
+                                             PRIV_FILE_DAC_WRITE,
+                                             PRIV_FILE_DAC_SEARCH      }, },
+  [ZCAP_READ_SEARCH] = { 2, (pvalue_t []) { PRIV_FILE_DAC_SEARCH,
+                                             PRIV_FILE_DAC_READ                }, },
+  [ZCAP_SYS_ADMIN] =   { 1, (pvalue_t []) { PRIV_SYS_ADMIN             }, },
+  [ZCAP_FOWNER] =      { 1, (pvalue_t []) { PRIV_FILE_OWNER            }, },
+#endif /* HAVE_SOLARIS_CAPABILITIES */
+};
+
+#ifdef HAVE_LCAPS
+/* Linux forms of capabilities methods */
+/* convert zebras privileges to system capabilities */
+static pset_t *
+zcaps2sys (zebra_capabilities_t *zcaps, int num)
+{
+  pset_t *syscaps;
+  int i, j = 0, count = 0;
+  
+  if (!num)
+    return NULL;
+  
+  /* first count up how many system caps we have */
+  for (i= 0; i < num; i++)
+    count += cap_map[zcaps[i]].num;
+  
+  if ( (syscaps = XCALLOC (MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL)
+    {
+      fprintf (stderr, "%s: could not allocate syscaps!", __func__);
+      return NULL;
+    }
+  
+  syscaps->caps = XCALLOC (MTYPE_PRIVS, (sizeof (pvalue_t) * count));
+  
+  if (!syscaps->caps)
+    {
+      fprintf (stderr, "%s: could not XCALLOC caps!", __func__);
+      return NULL;
+    }
+  
+  /* copy the capabilities over */
+  count = 0;
+  for (i=0; i < num; i++)
+    for (j = 0; j < cap_map[zcaps[i]].num; j++)
+      syscaps->caps[count++] = cap_map[zcaps[i]].system_caps[j];
+  
+  /* iterations above should be exact same as previous count, obviously.. */
+  syscaps->num = count;
+  
+  return syscaps;
+}
+
+/* set or clear the effective capabilities to/from permitted */
+int 
+zprivs_change_caps (zebra_privs_ops_t op)
+{
+  cap_flag_value_t cflag;
+  
+  /* should be no possibility of being called without valid caps */
+  assert (zprivs_state.syscaps_p && zprivs_state.caps);
+  if (! (zprivs_state.syscaps_p && zprivs_state.caps))
+    exit (1);
+    
+  if (op == ZPRIVS_RAISE)
+    cflag = CAP_SET;
+  else if (op == ZPRIVS_LOWER)
+    cflag = CAP_CLEAR;
+  else
+    return -1;
+
+  if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
+                       zprivs_state.syscaps_p->num, 
+                       zprivs_state.syscaps_p->caps, 
+                       cflag))
+    return cap_set_proc (zprivs_state.caps);
+  return -1;
+}
+
+zebra_privs_current_t
+zprivs_state_caps (void)
+{
+  int i;
+  cap_flag_value_t val;
+
+  /* should be no possibility of being called without valid caps */
+  assert (zprivs_state.syscaps_p && zprivs_state.caps);
+  if (! (zprivs_state.syscaps_p && zprivs_state.caps))
+    exit (1);
+  
+  for (i=0; i < zprivs_state.syscaps_p->num; i++)
+    {
+      if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p->caps[i], 
+                         CAP_EFFECTIVE, &val) )
+        {
+          zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
+                     safe_strerror (errno) );
+          return ZPRIVS_UNKNOWN;
+        }
+      if (val == CAP_SET)
+        return ZPRIVS_RAISED;
+    }
+  return ZPRIVS_LOWERED;
+}
+
+static void
+zprivs_caps_init (struct zebra_privs_t *zprivs)
+{
+  zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
+  zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
+
+  /* Tell kernel we want caps maintained across uid changes */
+  if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
+    {
+      fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n",
+                safe_strerror (errno) );
+      exit(1);
+    }
+
+  if ( !zprivs_state.syscaps_p )
+    {
+      fprintf (stderr, "privs_init: capabilities enabled, "
+                       "but no capabilities supplied\n");
+    }
+
+  /* we have caps, we have no need to ever change back the original user */
+  if (zprivs_state.zuid)
+    {
+      if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
+        {
+          fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n", 
+                     safe_strerror (errno));
+          exit (1);
+        }
+    }
+  
+  if ( !(zprivs_state.caps = cap_init()) )
+    {
+      fprintf (stderr, "privs_init: failed to cap_init, %s\n", 
+               safe_strerror (errno));
+      exit (1);
+    }
+
+  if ( cap_clear (zprivs_state.caps) )
+    {
+      fprintf (stderr, "privs_init: failed to cap_clear, %s\n", 
+               safe_strerror (errno));
+      exit (1);
+    }
+  
+  /* set permitted caps */
+  cap_set_flag(zprivs_state.caps, CAP_PERMITTED, 
+               zprivs_state.syscaps_p->num,
+               zprivs_state.syscaps_p->caps,
+               CAP_SET);
+    
+  /* set inheritable caps, if any */
+  if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num)
+    {
+      cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, 
+                   zprivs_state.syscaps_i->num, 
+                   zprivs_state.syscaps_i->caps, 
+                   CAP_SET);
+    }
+  
+  /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as 
+   * and when, and only when, they are needed.
+   */
+  if ( cap_set_proc (zprivs_state.caps) ) 
+    {
+      cap_t current_caps;
+      char *current_caps_text = NULL;
+      char *wanted_caps_text = NULL;
+
+      fprintf(stderr, "privs_init: initial cap_set_proc failed: %s\n",
+              safe_strerror(errno));
+
+      current_caps = cap_get_proc();
+      if (current_caps)
+        {
+          current_caps_text = cap_to_text(current_caps, NULL);
+          cap_free(current_caps);
+        }
+
+      wanted_caps_text = cap_to_text(zprivs_state.caps, NULL);
+      fprintf(stderr, "Wanted caps: %s\n", wanted_caps_text ? wanted_caps_text : "???");
+      fprintf(stderr, "Have   caps: %s\n", current_caps_text ? current_caps_text : "???");
+      if (current_caps_text)
+          cap_free(current_caps_text);
+      if (wanted_caps_text)
+          cap_free(wanted_caps_text);
+
+      exit (1);
+    }
+  
+  /* set methods for the caller to use */
+  zprivs->change = zprivs_change_caps;
+  zprivs->current_state = zprivs_state_caps;
+}
+
+static void
+zprivs_caps_terminate (void)
+{
+  /* clear all capabilities */
+  if (zprivs_state.caps)
+      cap_clear (zprivs_state.caps);
+
+  /* and boom, capabilities are gone forever */
+  if ( cap_set_proc (zprivs_state.caps) ) 
+    {
+      fprintf (stderr, "privs_terminate: cap_set_proc failed, %s",
+                safe_strerror (errno) );
+      exit (1);
+    }  
+
+  /* free up private state */
+  if (zprivs_state.syscaps_p->num)
+    {
+      XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p->caps);
+      XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
+    }
+  
+  if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num)
+    {
+      XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i->caps);
+      XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
+    }
+  
+  cap_free (zprivs_state.caps);
+}
+#elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */
+
+/* Solaris specific capability/privilege methods 
+ *
+ * Resources:
+ * - the 'privileges' man page
+ * - http://cvs.opensolaris.org
+ * - 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)
+{
+  pset_t *syscaps;
+  int i, j = 0;
+  
+  if ((syscaps = priv_allocset()) == NULL)
+    {
+      fprintf (stderr, "%s: could not allocate syscaps!\n", __func__);
+      exit (1);
+    }
+    
+  priv_emptyset (syscaps);
+  
+  for (i=0; i < num; i++)
+    for (j = 0; j < cap_map[zcaps[i]].num; j++)
+      priv_addset (syscaps, cap_map[zcaps[i]].system_caps[j]);
+  
+  return syscaps;
+}
+
+/* callback exported to users to RAISE and LOWER effective privileges
+ * from nothing to the given permitted set and back down
+ */
+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 as our working effective set
+   * to lower: copy regular effective set stored in zprivs_state.caps
+   */
+  if (op == ZPRIVS_RAISE)
+    privset = zprivs_state.syscaps_p;
+  else if (op == ZPRIVS_LOWER)
+    privset = zprivs_state.caps;
+  else
+    return -1;
+  
+  if (setppriv (PRIV_SET, PRIV_EFFECTIVE, privset) != 0)
+    return -1;
+  
+  return 0;
+}
+
+/* Retrieve current privilege state, is it RAISED or LOWERED? */
+zebra_privs_current_t 
+zprivs_state_caps (void)
+{
+  zebra_privs_current_t result;
+  pset_t *effective;
+  
+  if ( (effective = priv_allocset()) == NULL)
+    {
+      fprintf (stderr, "%s: failed to get priv_allocset! %s\n", __func__,
+               safe_strerror (errno));
+      return ZPRIVS_UNKNOWN;
+    }
+  
+  if (getppriv (PRIV_EFFECTIVE, effective))
+    {
+      fprintf (stderr, "%s: failed to get state! %s\n", __func__,
+               safe_strerror (errno));
+      result = ZPRIVS_UNKNOWN;
+    }
+  else
+    {
+      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_UNKNOWN;
+    }
+  
+  priv_freeset (effective);
+  return result;
+}
+
+static void
+zprivs_caps_init (struct zebra_privs_t *zprivs)
+{
+  pset_t *basic;
+  pset_t *minimal;
+  
+  /* the specified sets */
+  zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
+  zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
+  
+  /* nonsensical to have gotten here but not have capabilities */
+  if (!zprivs_state.syscaps_p)
+    {
+      fprintf (stderr, "%s: capabilities enabled, "
+                       "but no valid capabilities supplied\n",
+                       __func__);
+    }
+  
+  /* We retain the basic set in our permitted set, as Linux has no
+   * equivalent. The basic set on Linux hence is implicit, always
+   * there.
+   */
+  if ((basic = priv_str_to_set("basic", ",", NULL)) == NULL)
+    {
+      fprintf (stderr, "%s: couldn't get basic set!\n", __func__);
+      exit (1);
+    }
+  /* Add the basic set to the permitted set */
+  priv_union (basic, zprivs_state.syscaps_p);
+  priv_freeset (basic);
+  
+  /* Hey kernel, we know about privileges! 
+   * this isn't strictly required, use of setppriv should have same effect
+   */
+  if (setpflags (PRIV_AWARE, 1))
+    {
+      fprintf (stderr, "%s: error setting PRIV_AWARE!, %s\n", __func__,
+               safe_strerror (errno) );
+      exit (1);
+    }
+  
+  /* need either valid or empty sets for both p and i.. */
+  assert (zprivs_state.syscaps_i && zprivs_state.syscaps_p);
+  
+  /* we have caps, we have no need to ever change back the original user
+   * change real, effective and saved to the specified user.
+   */
+  if (zprivs_state.zuid)
+    {
+      if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
+        {
+          fprintf (stderr, "%s: could not setreuid, %s\n", 
+                   __func__, safe_strerror (errno));
+          exit (1);
+        }
+    }
+  
+  /* set the permitted set */
+  if (setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.syscaps_p))
+    {
+      fprintf (stderr, "%s: error setting permitted set!, %s\n", __func__,
+               safe_strerror (errno) );
+      exit (1);
+    }
+  
+  /* set the inheritable set */
+  if (setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.syscaps_i))
+    {
+      fprintf (stderr, "%s: error setting inheritable set!, %s\n", __func__,
+               safe_strerror (errno) );
+      exit (1);
+    }
+
+  /* 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 the minimal set as our working-storage privset */
+  zprivs_state.caps = minimal;
+  
+  /* set methods for the caller to use */
+  zprivs->change = zprivs_change_caps;
+  zprivs->current_state = zprivs_state_caps;
+}
+
+static void
+zprivs_caps_terminate (void)
+{
+  assert (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);
+  
+  /* free up private state */
+  if (zprivs_state.syscaps_p)
+    priv_freeset (zprivs_state.syscaps_p);
+  if (zprivs_state.syscaps_i)
+    priv_freeset (zprivs_state.syscaps_i);
+  
+  priv_freeset (zprivs_state.caps);
+}
+#else /* !HAVE_LCAPS && ! HAVE_SOLARIS_CAPABILITIES */
+#error "Neither Solaris nor Linux capabilities, dazed and confused..."
+#endif /* HAVE_LCAPS */
+#endif /* HAVE_CAPABILITIES */
+
+int
+zprivs_change_uid (zebra_privs_ops_t op)
+{
+
+  if (op == ZPRIVS_RAISE)
+    return seteuid (zprivs_state.zsuid);
+  else if (op == ZPRIVS_LOWER)
+    return seteuid (zprivs_state.zuid);
+  else
+    return -1;
+}
+
+zebra_privs_current_t
+zprivs_state_uid (void)
+{
+  return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+}
+
+int
+zprivs_change_null (zebra_privs_ops_t op)
+{
+  return 0;
+}
+
+zebra_privs_current_t
+zprivs_state_null (void)
+{
+  return zprivs_null_state;
+}
+
+#ifndef HAVE_GETGROUPLIST
+/* Solaris 11 has no getgrouplist() */
+static int
+getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
+{
+  struct group *grp;
+  size_t usridx;
+  int pos = 0, ret;
+
+  if (pos < *ngroups)
+    groups[pos] = group;
+  pos++;
+
+  setgrent();
+  while ((grp = getgrent()))
+    {
+      if (grp->gr_gid == group)
+        continue;
+      for (usridx = 0; grp->gr_mem[usridx] != NULL; usridx++)
+        if (!strcmp (grp->gr_mem[usridx], user))
+          {
+            if (pos < *ngroups)
+              groups[pos] = grp->gr_gid;
+            pos++;
+            break;
+          }
+    }
+  endgrent();
+
+  ret = (pos <= *ngroups) ? pos : -1;
+  *ngroups = pos;
+  return ret;
+}
+#endif /* HAVE_GETGROUPLIST */
+
+void
+zprivs_init(struct zebra_privs_t *zprivs)
+{
+  struct passwd *pwentry = NULL;
+  struct group *grentry = NULL;
+  gid_t groups[NGROUPS_MAX];
+  int i, ngroups = 0;
+  int found = 0;
+
+  if (!zprivs)
+    {
+      fprintf (stderr, "zprivs_init: called with NULL arg!\n");
+      exit (1);
+    }
+
+  /* NULL privs */
+  if (! (zprivs->user || zprivs->group 
+         || zprivs->cap_num_p || zprivs->cap_num_i) )
+    {
+      zprivs->change = zprivs_change_null;
+      zprivs->current_state = zprivs_state_null;
+      return;
+    }
+
+  if (zprivs->user)
+    {
+      if ( (pwentry = getpwnam (zprivs->user)) == NULL )
+        {
+          /* cant use log.h here as it depends on vty */
+          fprintf (stderr, "privs_init: could not lookup user %s\n",
+                   zprivs->user);
+          exit (1);
+        }
+
+      zprivs_state.zuid = pwentry->pw_uid;
+      zprivs_state.zgid = pwentry->pw_gid;
+    }
+
+  grentry = NULL;
+
+  if (zprivs->group)
+    {
+      if ( (grentry = getgrnam (zprivs->group)) == NULL )
+        {
+          fprintf (stderr, "privs_init: could not lookup group %s\n",
+                   zprivs->group);
+          exit (1);
+        }
+
+      zprivs_state.zgid = grentry->gr_gid;
+    }
+
+  if (zprivs->user)
+    {
+      ngroups = sizeof(groups);
+      if ( (ngroups = getgrouplist (zprivs->user, zprivs_state.zgid, groups, &ngroups )) < 0 )
+        {
+          /* cant use log.h here as it depends on vty */
+          fprintf (stderr, "privs_init: could not getgrouplist for user %s\n",
+                   zprivs->user);
+          exit (1);
+        }
+    }
+
+  if (zprivs->vty_group)
+    /* Add the vty_group to the supplementary groups so it can be chowned to */
+    {
+      if ( (grentry = getgrnam (zprivs->vty_group)) )
+        {
+          zprivs_state.vtygrp = grentry->gr_gid;
+
+          for ( i = 0; i < ngroups; i++ )
+            if ( groups[i] == zprivs_state.vtygrp )
+              {
+                found++;
+                break;
+              }
+
+          if (!found)
+            {
+             fprintf (stderr, "privs_init: user(%s) is not part of vty group specified(%s)\n",
+                      zprivs->user, zprivs->vty_group);
+              exit (1);
+            }
+          if ( i >= ngroups && ngroups < (int) ZEBRA_NUM_OF(groups) )
+            {
+              groups[i] = zprivs_state.vtygrp;
+            }
+        }
+      else
+        {
+          fprintf (stderr, "privs_init: could not lookup vty group %s\n",
+                   zprivs->vty_group);
+          exit (1);
+        }
+    }
+
+  if (ngroups)
+    {
+      if ( setgroups (ngroups, groups) )
+        {
+          fprintf (stderr, "privs_init: could not setgroups, %s\n",
+                   safe_strerror (errno) );
+          exit (1);
+        }
+    }
+
+  if (zprivs_state.zgid)
+    {
+      /* change group now, forever. uid we do later */
+      if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
+        {
+          fprintf (stderr, "zprivs_init: could not setregid, %s\n",
+                    safe_strerror (errno) );
+          exit (1);
+        }
+    }
+  
+#ifdef HAVE_CAPABILITIES
+  zprivs_caps_init (zprivs);
+#else /* !HAVE_CAPABILITIES */
+  /* we dont have caps. we'll need to maintain rid and saved uid
+   * and change euid back to saved uid (who we presume has all neccessary
+   * privileges) whenever we are asked to raise our privileges.
+   *
+   * This is not worth that much security wise, but all we can do.
+   */
+  zprivs_state.zsuid = geteuid();  
+  if ( zprivs_state.zuid )
+    {
+      if ( setreuid (-1, zprivs_state.zuid) )
+        {
+          fprintf (stderr, "privs_init (uid): could not setreuid, %s\n", 
+                   safe_strerror (errno));
+          exit (1);
+        }
+    }
+  
+  zprivs->change = zprivs_change_uid;
+  zprivs->current_state = zprivs_state_uid;
+#endif /* HAVE_CAPABILITIES */
+}
+
+void 
+zprivs_terminate (struct zebra_privs_t *zprivs)
+{
+  if (!zprivs)
+    {
+      fprintf (stderr, "%s: no privs struct given, terminating", __func__);
+      exit (0);
+    }
+  
+#ifdef HAVE_CAPABILITIES
+  zprivs_caps_terminate();
+#else /* !HAVE_CAPABILITIES */
+  if (zprivs_state.zuid)
+    {
+      if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
+        {
+          fprintf (stderr, "privs_terminate: could not setreuid, %s", 
+                     safe_strerror (errno) );
+          exit (1);
+        }
+     }
+#endif /* HAVE_LCAPS */
+
+  zprivs->change = zprivs_change_null;
+  zprivs->current_state = zprivs_state_null;
+  zprivs_null_state = ZPRIVS_LOWERED;
+  return;
+}
+
+void
+zprivs_get_ids(struct zprivs_ids_t *ids)
+{
+
+   ids->uid_priv = getuid();
+   (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
+                     : (ids->uid_normal = -1);
+   (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
+                     : (ids->gid_normal = -1);
+   (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
+                       : (ids->gid_vty = -1);
+   
+   return;
+}
diff --git a/lib/privs.h b/lib/privs.h
new file mode 100644 (file)
index 0000000..46d614e
--- /dev/null
@@ -0,0 +1,90 @@
+/* 
+ * Zebra privileges header.
+ *
+ * Copyright (C) 2003 Paul Jakma.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_PRIVS_H
+#define _ZEBRA_PRIVS_H
+
+/* list of zebra capabilities */
+typedef enum 
+{
+  ZCAP_SETID,
+  ZCAP_BIND,
+  ZCAP_NET_ADMIN,
+  ZCAP_SYS_ADMIN,
+  ZCAP_NET_RAW,
+  ZCAP_CHROOT,
+  ZCAP_NICE,
+  ZCAP_PTRACE,
+  ZCAP_DAC_OVERRIDE,
+  ZCAP_READ_SEARCH,
+  ZCAP_FOWNER,
+  ZCAP_MAX
+} zebra_capabilities_t;
+
+typedef enum
+{
+  ZPRIVS_LOWERED,
+  ZPRIVS_RAISED,
+  ZPRIVS_UNKNOWN,
+} zebra_privs_current_t;
+
+typedef enum
+{
+  ZPRIVS_RAISE,
+  ZPRIVS_LOWER,
+} zebra_privs_ops_t;
+
+struct zebra_privs_t
+{
+  zebra_capabilities_t *caps_p;       /* caps required for operation */
+  zebra_capabilities_t *caps_i;       /* caps to allow inheritance of */
+  int cap_num_p;                      /* number of caps in arrays */
+  int cap_num_i;                    
+  const char *user;                   /* user and group to run as */
+  const char *group;
+  const char *vty_group;              /* group to chown vty socket to */
+  /* methods */
+  int 
+    (*change) (zebra_privs_ops_t);    /* change privileges, 0 on success */
+  zebra_privs_current_t 
+    (*current_state) (void);          /* current privilege state */
+};
+
+struct zprivs_ids_t
+{
+  /* -1 is undefined */
+  uid_t uid_priv;                     /* privileged uid */
+  uid_t uid_normal;                   /* normal uid */
+  gid_t gid_priv;                     /* privileged uid */
+  gid_t gid_normal;                   /* normal uid */
+  gid_t gid_vty;                      /* vty gid */
+};
+
+  /* initialise zebra privileges */
+extern void zprivs_init (struct zebra_privs_t *zprivs);
+  /* drop all and terminate privileges */ 
+extern void zprivs_terminate (struct zebra_privs_t *);
+  /* query for runtime uid's and gid's, eg vty needs this */
+extern void zprivs_get_ids(struct zprivs_ids_t *);
+
+#endif /* _ZEBRA_PRIVS_H */
diff --git a/lib/queue.h b/lib/queue.h
new file mode 100644 (file)
index 0000000..48b363e
--- /dev/null
@@ -0,0 +1,635 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *                             SLIST   LIST    STAILQ  TAILQ
+ * _HEAD                       +       +       +       +
+ * _HEAD_INITIALIZER           +       +       +       +
+ * _ENTRY                      +       +       +       +
+ * _INIT                       +       +       +       +
+ * _EMPTY                      +       +       +       +
+ * _FIRST                      +       +       +       +
+ * _NEXT                       +       +       +       +
+ * _PREV                       -       -       -       +
+ * _LAST                       -       -       +       +
+ * _FOREACH                    +       +       +       +
+ * _FOREACH_SAFE               +       +       +       +
+ * _FOREACH_REVERSE            -       -       -       +
+ * _FOREACH_REVERSE_SAFE       -       -       -       +
+ * _INSERT_HEAD                        +       +       +       +
+ * _INSERT_BEFORE              -       +       -       +
+ * _INSERT_AFTER               +       +       +       +
+ * _INSERT_TAIL                        -       -       +       +
+ * _CONCAT                     -       -       +       +
+ * _REMOVE_AFTER               +       -       +       -
+ * _REMOVE_HEAD                        +       -       +       -
+ * _REMOVE                     +       +       +       +
+ * _SWAP                       +       +       +       +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+       char * lastfile;
+       int lastline;
+       char * prevfile;
+       int prevline;
+};
+
+#define        TRACEBUF        struct qm_trace trace;
+#define        TRASHIT(x)      do {(x) = (void *)-1;} while (0)
+#define        QMD_SAVELINK(name, link)        void **name = (void *)&(link)
+
+#define        QMD_TRACE_HEAD(head) do {                                       \
+       (head)->trace.prevline = (head)->trace.lastline;                \
+       (head)->trace.prevfile = (head)->trace.lastfile;                \
+       (head)->trace.lastline = __LINE__;                              \
+       (head)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#define        QMD_TRACE_ELEM(elem) do {                                       \
+       (elem)->trace.prevline = (elem)->trace.lastline;                \
+       (elem)->trace.prevfile = (elem)->trace.lastfile;                \
+       (elem)->trace.lastline = __LINE__;                              \
+       (elem)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#else
+#define        QMD_TRACE_ELEM(elem)
+#define        QMD_TRACE_HEAD(head)
+#define        QMD_SAVELINK(name, link)
+#define        TRACEBUF
+#define        TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        SLIST_FOREACH_PREVPTR(var, varp, head, field)                   \
+       for ((varp) = &SLIST_FIRST((head));                             \
+           ((var) = *(varp)) != NULL;                                  \
+           (varp) = &SLIST_NEXT((var), field))
+
+#define        SLIST_INIT(head) do {                                           \
+       SLIST_FIRST((head)) = NULL;                                     \
+} while (0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
+       SLIST_NEXT((slistelm), field) = (elm);                          \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
+       SLIST_FIRST((head)) = (elm);                                    \
+} while (0)
+
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       QMD_SAVELINK(oldnext, (elm)->field.sle_next);                   \
+       if (SLIST_FIRST((head)) == (elm)) {                             \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = SLIST_FIRST((head));              \
+               while (SLIST_NEXT(curelm, field) != (elm))              \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_REMOVE_AFTER(curelm, field);                      \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do {                            \
+       SLIST_NEXT(elm, field) =                                        \
+           SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
+} while (0)
+
+#define SLIST_SWAP(head1, head2, type) do {                            \
+       struct type *swap_first = SLIST_FIRST(head1);                   \
+       SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \
+       SLIST_FIRST(head2) = swap_first;                                \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;/* first element */                     \
+       struct type **stqh_last;/* addr of last next element */         \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_CONCAT(head1, head2) do {                                \
+       if (!STAILQ_EMPTY((head2))) {                                   \
+               *(head1)->stqh_last = (head2)->stqh_first;              \
+               (head1)->stqh_last = (head2)->stqh_last;                \
+               STAILQ_INIT((head2));                                   \
+       }                                                               \
+} while (0)
+
+#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
+
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for((var) = STAILQ_FIRST((head));                               \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+
+#define        STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
+       for ((var) = STAILQ_FIRST((head));                              \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_INIT(head) do {                                          \
+       STAILQ_FIRST((head)) = NULL;                                    \
+       (head)->stqh_last = &STAILQ_FIRST((head));                      \
+} while (0)
+
+#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_NEXT((tqelm), field) = (elm);                            \
+} while (0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_FIRST((head)) = (elm);                                   \
+} while (0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       STAILQ_NEXT((elm), field) = NULL;                               \
+       *(head)->stqh_last = (elm);                                     \
+       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
+} while (0)
+
+#define        STAILQ_LAST(head, type, field)                                  \
+       (STAILQ_EMPTY((head)) ?                                         \
+               NULL :                                                  \
+               ((struct type *)(void *)                                \
+               ((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       QMD_SAVELINK(oldnext, (elm)->field.stqe_next);                  \
+       if (STAILQ_FIRST((head)) == (elm)) {                            \
+               STAILQ_REMOVE_HEAD((head), field);                      \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = STAILQ_FIRST((head));             \
+               while (STAILQ_NEXT(curelm, field) != (elm))             \
+                       curelm = STAILQ_NEXT(curelm, field);            \
+               STAILQ_REMOVE_AFTER(head, curelm, field);               \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do {                     \
+       if ((STAILQ_NEXT(elm, field) =                                  \
+            STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if ((STAILQ_FIRST((head)) =                                     \
+            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do {                           \
+       struct type *swap_first = STAILQ_FIRST(head1);                  \
+       struct type **swap_last = (head1)->stqh_last;                   \
+       STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \
+       (head1)->stqh_last = (head2)->stqh_last;                        \
+       STAILQ_FIRST(head2) = swap_first;                               \
+       (head2)->stqh_last = swap_last;                                 \
+       if (STAILQ_EMPTY(head1))                                        \
+               (head1)->stqh_last = &STAILQ_FIRST(head1);              \
+       if (STAILQ_EMPTY(head2))                                        \
+               (head2)->stqh_last = &STAILQ_FIRST(head2);              \
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define        QMD_LIST_CHECK_HEAD(head, field) do {                           \
+       if (LIST_FIRST((head)) != NULL &&                               \
+           LIST_FIRST((head))->field.le_prev !=                        \
+            &LIST_FIRST((head)))                                       \
+               panic("Bad list head %p first->prev != head", (head));  \
+} while (0)
+
+#define        QMD_LIST_CHECK_NEXT(elm, field) do {                            \
+       if (LIST_NEXT((elm), field) != NULL &&                          \
+           LIST_NEXT((elm), field)->field.le_prev !=                   \
+            &((elm)->field.le_next))                                   \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+#define        QMD_LIST_CHECK_PREV(elm, field) do {                            \
+       if (*(elm)->field.le_prev != (elm))                             \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_LIST_CHECK_HEAD(head, field)
+#define        QMD_LIST_CHECK_NEXT(elm, field)
+#define        QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
+
+#define        LIST_FIRST(head)        ((head)->lh_first)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = LIST_FIRST((head));                                \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+       for ((var) = LIST_FIRST((head));                                \
+           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
+           (var) = (tvar))
+
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST((head)) = NULL;                                      \
+} while (0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       QMD_LIST_CHECK_NEXT(listelm, field);                            \
+       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+               LIST_NEXT((listelm), field)->field.le_prev =            \
+                   &LIST_NEXT((elm), field);                           \
+       LIST_NEXT((listelm), field) = (elm);                            \
+       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       QMD_LIST_CHECK_PREV(listelm, field);                            \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       LIST_NEXT((elm), field) = (listelm);                            \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
+} while (0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       QMD_LIST_CHECK_HEAD((head), field);                             \
+       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
+               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+       LIST_FIRST((head)) = (elm);                                     \
+       (elm)->field.le_prev = &LIST_FIRST((head));                     \
+} while (0)
+
+#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       QMD_SAVELINK(oldnext, (elm)->field.le_next);                    \
+       QMD_SAVELINK(oldprev, (elm)->field.le_prev);                    \
+       QMD_LIST_CHECK_NEXT(elm, field);                                \
+       QMD_LIST_CHECK_PREV(elm, field);                                \
+       if (LIST_NEXT((elm), field) != NULL)                            \
+               LIST_NEXT((elm), field)->field.le_prev =                \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+} while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do {                      \
+       struct type *swap_tmp = LIST_FIRST((head1));                    \
+       LIST_FIRST((head1)) = LIST_FIRST((head2));                      \
+       LIST_FIRST((head2)) = swap_tmp;                                 \
+       if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head1));         \
+       if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head2));         \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define        TAILQ_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+       TRACEBUF                                                        \
+}
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { NULL, &(head).tqh_first }
+
+#define        TAILQ_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+       TRACEBUF                                                        \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define        QMD_TAILQ_CHECK_HEAD(head, field) do {                          \
+       if (!TAILQ_EMPTY(head) &&                                       \
+           TAILQ_FIRST((head))->field.tqe_prev !=                      \
+            &TAILQ_FIRST((head)))                                      \
+               panic("Bad tailq head %p first->prev != head", (head)); \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_TAIL(head, field) do {                          \
+       if (*(head)->tqh_last != NULL)                                  \
+               panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head));  \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_NEXT(elm, field) do {                           \
+       if (TAILQ_NEXT((elm), field) != NULL &&                         \
+           TAILQ_NEXT((elm), field)->field.tqe_prev !=                 \
+            &((elm)->field.tqe_next))                                  \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+#define        QMD_TAILQ_CHECK_PREV(elm, field) do {                           \
+       if (*(elm)->field.tqe_prev != (elm))                            \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_TAILQ_CHECK_HEAD(head, field)
+#define        QMD_TAILQ_CHECK_TAIL(head, headname)
+#define        QMD_TAILQ_CHECK_NEXT(elm, field)
+#define        QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define        TAILQ_CONCAT(head1, head2, field) do {                          \
+       if (!TAILQ_EMPTY(head2)) {                                      \
+               *(head1)->tqh_last = (head2)->tqh_first;                \
+               (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+               (head1)->tqh_last = (head2)->tqh_last;                  \
+               TAILQ_INIT((head2));                                    \
+               QMD_TRACE_HEAD(head1);                                  \
+               QMD_TRACE_HEAD(head2);                                  \
+       }                                                               \
+} while (0)
+
+#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
+
+#define        TAILQ_FIRST(head)       ((head)->tqh_first)
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
+           (var) = (tvar))
+
+#define        TAILQ_INIT(head) do {                                           \
+       TAILQ_FIRST((head)) = NULL;                                     \
+       (head)->tqh_last = &TAILQ_FIRST((head));                        \
+       QMD_TRACE_HEAD(head);                                           \
+} while (0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       QMD_TAILQ_CHECK_NEXT(listelm, field);                           \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   &TAILQ_NEXT((elm), field);                          \
+       else {                                                          \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       TAILQ_NEXT((listelm), field) = (elm);                           \
+       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&listelm->field);                                \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       QMD_TAILQ_CHECK_PREV(listelm, field);                           \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       TAILQ_NEXT((elm), field) = (listelm);                           \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&listelm->field);                                \
+} while (0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_HEAD(head, field);                              \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
+               TAILQ_FIRST((head))->field.tqe_prev =                   \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_FIRST((head)) = (elm);                                    \
+       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_TAIL(head, field);                              \
+       TAILQ_NEXT((elm), field) = NULL;                                \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_LAST(head, headname)                                      \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define        TAILQ_PREV(elm, headname, field)                                \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       QMD_SAVELINK(oldnext, (elm)->field.tqe_next);                   \
+       QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);                   \
+       QMD_TAILQ_CHECK_NEXT(elm, field);                               \
+       QMD_TAILQ_CHECK_PREV(elm, field);                               \
+       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   (elm)->field.tqe_prev;                              \
+       else {                                                          \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do {                     \
+       struct type *swap_first = (head1)->tqh_first;                   \
+       struct type **swap_last = (head1)->tqh_last;                    \
+       (head1)->tqh_first = (head2)->tqh_first;                        \
+       (head1)->tqh_last = (head2)->tqh_last;                          \
+       (head2)->tqh_first = swap_first;                                \
+       (head2)->tqh_last = swap_last;                                  \
+       if ((swap_first = (head1)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head1)->tqh_first;       \
+       else                                                            \
+               (head1)->tqh_last = &(head1)->tqh_first;                \
+       if ((swap_first = (head2)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head2)->tqh_first;       \
+       else                                                            \
+               (head2)->tqh_last = &(head2)->tqh_first;                \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/lib/regex-gnu.h b/lib/regex-gnu.h
new file mode 100644 (file)
index 0000000..4cee464
--- /dev/null
@@ -0,0 +1,542 @@
+/* Definitions for data structures and routines for the regular
+   expression library, version 0.12.
+   Copyright (C) 1985,89,90,91,92,93,95,96,97,98 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+/* Allow the use in C++ code.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+
+#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+   should be there.  */
+# include <stddef.h>
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+   wide enough to hold a value of a pointer.  For most ANSI compilers
+   ptrdiff_t and size_t should be likely OK.  Still size of these two
+   types is 2 for Microsoft C.  Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned long int reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals.
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically,
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES.
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+   If not set, then the GNU regex operators are recognized. */
+#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+   If not set, and debugging was on, turn it off.
+   This only works if regex.c is compiled -DDEBUG.
+   We define this bit always, so that all that's needed to turn on
+   debugging is to recompile regex.c; the calling code can always have
+   this bit set, and it won't affect anything in the normal case. */
+#define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK                                                  \
+  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL                    \
+   | RE_NO_BK_PARENS              | RE_NO_BK_REFS                      \
+   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES                 \
+   | RE_DOT_NEWLINE              | RE_CONTEXT_INDEP_ANCHORS            \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK                                              \
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG)        \
+   & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS))
+
+#define RE_SYNTAX_POSIX_AWK                                            \
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS             \
+   | RE_INTERVALS          | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GREP                                                 \
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                           \
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP                                                        \
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS                   \
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE                   \
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS                            \
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP                                          \
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON                                                \
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL             \
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC                                          \
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC                                  \
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED                                       \
+  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS                  \
+   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES                           \
+   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR                             \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                               \
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS                            \
+   | RE_NO_BK_VBAR         | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+#endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
+#define RE_DUP_MAX (0x7fff)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+#ifdef _XOPEN_SOURCE
+  REG_ENOSYS = -1,     /* This will never happen for this implementation.  */
+#endif
+
+  REG_NOERROR = 0,     /* Success.  */
+  REG_NOMATCH,         /* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,          /* Invalid pattern.  */
+  REG_ECOLLATE,                /* Not implemented.  */
+  REG_ECTYPE,          /* Invalid character class name.  */
+  REG_EESCAPE,         /* Trailing backslash.  */
+  REG_ESUBREG,         /* Invalid back reference.  */
+  REG_EBRACK,          /* Unmatched left bracket.  */
+  REG_EPAREN,          /* Parenthesis imbalance.  */
+  REG_EBRACE,          /* Unmatched \{.  */
+  REG_BADBR,           /* Invalid contents of \{\}.  */
+  REG_ERANGE,          /* Invalid range end.  */
+  REG_ESPACE,          /* Ran out of memory.  */
+  REG_BADRPT,          /* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,            /* Premature end.  */
+  REG_ESIZE,           /* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN          /* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+#ifndef RE_TRANSLATE_TYPE
+# define RE_TRANSLATE_TYPE char *
+#endif
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+       /* Space that holds the compiled pattern.  It is declared as
+          `unsigned char *' because its elements are
+           sometimes used as array indexes.  */
+  unsigned char *buffer;
+
+       /* Number of bytes to which `buffer' points.  */
+  unsigned long int allocated;
+
+       /* Number of bytes actually used in `buffer'.  */
+  unsigned long int used;
+
+        /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+           the fastmap, if there is one, to skip over impossible
+           starting points for matches.  */
+  char *fastmap;
+
+        /* Either a translate table to apply to all characters before
+           comparing them, or zero for no translation.  The translation
+           is applied to a pattern when it is compiled and to a string
+           when it is matched.  */
+  RE_TRANSLATE_TYPE translate;
+
+       /* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+        /* Zero if this pattern cannot match the empty string, one else.
+           Well, in truth it's used only in `re_search_2', to see
+           whether or not we should use the fastmap, so we don't set
+           this absolutely perfectly; see `re_compile_fastmap' (the
+           `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+             for `max (RE_NREGS, re_nsub + 1)' groups.
+           If REGS_REALLOCATE, reallocate space if necessary.
+           If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+        /* Set to zero when `regex_compile' compiles a pattern; set to one
+           by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+        /* If set, `re_match_2' does not return information about
+           subexpressions.  */
+  unsigned no_sub : 1;
+
+        /* If set, a beginning-of-line anchor doesn't match at the
+           beginning of the string.  */
+  unsigned not_bol : 1;
+
+        /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+        /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+# define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+
+/* Declarations for routines.  */
+
+/* To avoid duplicating every routine declaration -- once with a
+   prototype (if we are ANSI), and once without (if we aren't) -- we
+   use the following macro to declare argument types.  This
+   unfortunately clutters up the declarations a bit, but I think it's
+   worth it.  */
+
+#if __STDC__
+
+# define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+# define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+extern const char *re_compile_pattern
+  _RE_ARGS ((const char *pattern, size_t length,
+             struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+extern int re_search
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+            int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+extern int re_search_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+extern int re_match
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+             int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+extern int re_match_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+extern void re_set_registers
+  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+             unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+# ifndef _CRAY
+/* 4.2 bsd compatibility.  */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+# endif
+#endif
+
+/* POSIX compatibility.  */
+extern int regcomp _RE_ARGS ((regex_t *__preg, const char *__pattern,
+                             int __cflags));
+
+extern int regexec _RE_ARGS ((const regex_t *__preg,
+                             const char *__string, size_t __nmatch,
+                             regmatch_t __pmatch[], int __eflags));
+
+extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
+                                 char *__errbuf, size_t __errbuf_size));
+
+extern void regfree _RE_ARGS ((regex_t *__preg));
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/lib/regex.c b/lib/regex.c
new file mode 100644 (file)
index 0000000..122f447
--- /dev/null
@@ -0,0 +1,5895 @@
+/* Extended regular expression matching and search library,
+   version 0.12.
+   (Implements POSIX draft P1003.2/D11.2, except for some of the
+   internationalization features.)
+   Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined _AIX && !defined REGEX_MALLOC
+  #pragma alloca
+#endif
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#ifdef _WIN32
+/* Windows does not provide unistd.h, which is required for abort() */
+#include <process.h>
+#endif /* _WIN32 */
+
+#ifndef PARAMS
+# if defined __GNUC__ || (defined __STDC__ && __STDC__)
+#  define PARAMS(args) args
+# else
+#  define PARAMS(args) ()
+# endif  /* GCC.  */
+#endif  /* Not PARAMS.  */
+
+#if defined STDC_HEADERS && !defined emacs
+# include <stddef.h>
+#else
+/* We need this for `regex.h', and perhaps for the Emacs include files.  */
+# include <sys/types.h>
+#endif
+
+#define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC)
+
+/* For platform which support the ISO C amendement 1 functionality we
+   support user defined character classes.  */
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
+# include <wchar.h>
+# include <wctype.h>
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean.  */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+       __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+       __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+       __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+       __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+       __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+       __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+       __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+#define btowc __btowc
+#endif
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+   strings.  */
+# define gettext_noop(String) String
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+   that make sense only in Emacs. */
+#ifdef emacs
+
+# include "lisp.h"
+# include "buffer.h"
+# include "syntax.h"
+
+#else  /* not emacs */
+
+/* If we are not linking with Emacs proper,
+   we can't use the relocating allocator
+   even if config.h says that we can.  */
+# undef REL_ALLOC
+
+# if defined STDC_HEADERS || defined _LIBC
+#  include <stdlib.h>
+# else
+char *malloc ();
+char *realloc ();
+# endif
+
+/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow.
+   If nothing else has been done, use the method below.  */
+# ifdef INHIBIT_STRING_HEADER
+#  if !(defined HAVE_BZERO && defined HAVE_BCOPY)
+#   if !defined bzero && !defined bcopy
+#    undef INHIBIT_STRING_HEADER
+#   endif
+#  endif
+# endif
+
+/* This is the normal way of making sure we have a bcopy and a bzero.
+   This is used in most programs--a few other programs avoid this
+   by defining INHIBIT_STRING_HEADER.  */
+# ifndef INHIBIT_STRING_HEADER
+#  if defined HAVE_STRING_H || defined STDC_HEADERS || defined _LIBC
+#   include <string.h>
+#   ifndef bzero
+#    ifndef _LIBC
+#     define bzero(s, n)       (memset (s, '\0', n), (s))
+#    else
+#     define bzero(s, n)       __bzero (s, n)
+#    endif
+#   endif
+#  else
+#   include <strings.h>
+#   ifndef memcmp
+#    define memcmp(s1, s2, n)  bcmp (s1, s2, n)
+#   endif
+#   ifndef memcpy
+#    define memcpy(d, s, n)    (bcopy (s, d, n), (d))
+#   endif
+#  endif
+# endif
+
+/* Define the syntax stuff for \<, \>, etc.  */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+   commands in re_match_2.  */
+# ifndef Sword
+#  define Sword 1
+# endif
+
+# ifdef SWITCH_ENUM_BUG
+#  define SWITCH_ENUM_CAST(x) ((int)(x))
+# else
+#  define SWITCH_ENUM_CAST(x) (x)
+# endif
+
+/* How many characters in the character set.  */
+# define CHAR_SET_SIZE 256
+
+# ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+# else /* not SYNTAX_TABLE */
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+   register int c;
+   static int done;
+
+   if (done)
+     return;
+
+   memset (re_syntax_table, 0, sizeof re_syntax_table);
+
+   for (c = 'a'; c <= 'z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = 'A'; c <= 'Z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = '0'; c <= '9'; c++)
+     re_syntax_table[c] = Sword;
+
+   re_syntax_table['_'] = Sword;
+
+   done = 1;
+}
+
+# endif /* not SYNTAX_TABLE */
+
+# define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+/* Get the interface, including the syntax bits.  */
+#include <regex-gnu.h>
+
+/* isalpha etc. are used for the character classes.  */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+   "... Some ctype macros are valid only for character codes that
+   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+   using /bin/cc or gcc but without giving an ansi option).  So, all
+   ctype uses should be through macros like ISPRINT...  If
+   STDC_HEADERS is defined, then autoconf has verified that the ctype
+   macros don't need to be guarded with references to isascii. ...
+   Defining isascii to 1 should let any compiler worth its salt
+   eliminate the && through constant folding."
+   Solaris defines some of these symbols so we must undefine them first.  */
+
+#undef ISASCII
+#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII)
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII (c) && isblank (c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
+#else
+# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
+#endif
+
+#undef ISPRINT
+#define ISPRINT(c) (ISASCII (c) && isprint (c))
+#define ISDIGIT(c) (ISASCII (c) && isdigit (c))
+#define ISALNUM(c) (ISASCII (c) && isalnum (c))
+#define ISALPHA(c) (ISASCII (c) && isalpha (c))
+#define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
+#define ISLOWER(c) (ISASCII (c) && islower (c))
+#define ISPUNCT(c) (ISASCII (c) && ispunct (c))
+#define ISSPACE(c) (ISASCII (c) && isspace (c))
+#define ISUPPER(c) (ISASCII (c) && isupper (c))
+#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
+
+#ifdef _tolower
+# define TOLOWER(c) _tolower(c)
+#else
+# define TOLOWER(c) tolower(c)
+#endif
+
+#ifndef NULL
+# define NULL (void *)0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+   since ours (we hope) works properly with all combinations of
+   machines, compilers, `char' and `unsigned char' argument types.
+   (Per Bothner suggested the basic approach.)  */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+# define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else  /* not __STDC__ */
+/* As in Harbison and Steele.  */
+# define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
+   use `alloca' instead of `malloc'.  This is because using malloc in
+   re_search* or re_match* could cause memory leaks when C-g is used in
+   Emacs; also, malloc is slower and causes storage fragmentation.  On
+   the other hand, malloc is more portable, and easier to debug.
+
+   Because we sometimes use alloca, some routines have to be macros,
+   not functions -- `alloca'-allocated space disappears at the end of the
+   function it is called in.  */
+
+#ifdef REGEX_MALLOC
+
+# define REGEX_ALLOCATE malloc
+# define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+# define REGEX_FREE free
+
+#else /* not REGEX_MALLOC  */
+
+/* Emacs already defines alloca, sometimes.  */
+# ifndef alloca
+
+/* Make alloca work the best possible way.  */
+#  ifdef __GNUC__
+#   define alloca __builtin_alloca
+#  else /* not __GNUC__ */
+#   if HAVE_ALLOCA_H
+#    include <alloca.h>
+#   endif /* HAVE_ALLOCA_H */
+#  endif /* not __GNUC__ */
+
+# endif /* not alloca */
+
+# define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable.  */
+# define REGEX_REALLOCATE(source, osize, nsize)                                \
+  (destination = (char *) alloca (nsize),                              \
+   memcpy (destination, source, osize))
+
+/* No need to do anything to free, after alloca.  */
+# define REGEX_FREE(arg) ((void)0) /* Do nothing!  But inhibit gcc warning.  */
+
+#endif /* not REGEX_MALLOC */
+
+/* Define how to allocate the failure stack.  */
+
+#if defined REL_ALLOC && defined REGEX_MALLOC
+
+# define REGEX_ALLOCATE_STACK(size)                            \
+  r_alloc (&failure_stack_ptr, (size))
+# define REGEX_REALLOCATE_STACK(source, osize, nsize)          \
+  r_re_alloc (&failure_stack_ptr, (nsize))
+# define REGEX_FREE_STACK(ptr)                                 \
+  r_alloc_free (&failure_stack_ptr)
+
+#else /* not using relocating allocator */
+
+# ifdef REGEX_MALLOC
+
+#  define REGEX_ALLOCATE_STACK malloc
+#  define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize)
+#  define REGEX_FREE_STACK free
+
+# else /* not REGEX_MALLOC */
+
+#  define REGEX_ALLOCATE_STACK alloca
+
+#  define REGEX_REALLOCATE_STACK(source, osize, nsize)                 \
+   REGEX_REALLOCATE (source, osize, nsize)
+/* No need to explicitly free anything.  */
+#  define REGEX_FREE_STACK(arg)
+
+# endif /* not REGEX_MALLOC */
+#endif /* not using relocating allocator */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+   `string1' or just past its end.  This works if PTR is NULL, which is
+   a good thing.  */
+#define FIRST_STRING_P(ptr)                                    \
+  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail.  */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define RETALLOC_IF(addr, n, t) \
+  if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits.  */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp,
+                                       const char *string1, int size1,
+                                       const char *string2, int size2,
+                                       int pos,
+                                       struct re_registers *regs,
+                                       int stop));
+
+/* These are the command codes that appear in compiled regular
+   expressions.  Some opcodes are followed by argument bytes.  A
+   command code can specify any interpretation whatsoever for its
+   arguments.  Zero bytes may appear in the compiled regular expression.  */
+
+typedef enum
+{
+  no_op = 0,
+
+  /* Succeed right away--no more backtracking.  */
+  succeed,
+
+        /* Followed by one byte giving n, then by n literal bytes.  */
+  exactn,
+
+        /* Matches any (more or less) character.  */
+  anychar,
+
+        /* Matches any one char belonging to specified set.  First
+           following byte is number of bitmap bytes.  Then come bytes
+           for a bitmap saying which chars are in.  Bits in each byte
+           are ordered low-bit-first.  A character is in the set if its
+           bit is 1.  A character too large to have a bit in the map is
+           automatically not in the set.  */
+  charset,
+
+        /* Same parameters as charset, but match any character that is
+           not one of those specified.  */
+  charset_not,
+
+        /* Start remembering the text that is matched, for storing in a
+           register.  Followed by one byte with the register number, in
+           the range 0 to one less than the pattern buffer's re_nsub
+           field.  Then followed by one byte with the number of groups
+           inner to this one.  (This last has to be part of the
+           start_memory only because we need it in the on_failure_jump
+           of re_match_2.)  */
+  start_memory,
+
+        /* Stop remembering the text that is matched and store it in a
+           memory register.  Followed by one byte with the register
+           number, in the range 0 to one less than `re_nsub' in the
+           pattern buffer, and one byte with the number of inner groups,
+           just like `start_memory'.  (We need the number of inner
+           groups here because we don't have any easy way of finding the
+           corresponding start_memory when we're at a stop_memory.)  */
+  stop_memory,
+
+        /* Match a duplicate of something remembered. Followed by one
+           byte containing the register number.  */
+  duplicate,
+
+        /* Fail unless at beginning of line.  */
+  begline,
+
+        /* Fail unless at end of line.  */
+  endline,
+
+        /* Succeeds if at beginning of buffer (if emacs) or at beginning
+           of string to be matched (if not).  */
+  begbuf,
+
+        /* Analogously, for end of buffer/string.  */
+  endbuf,
+
+        /* Followed by two byte relative address to which to jump.  */
+  jump,
+
+       /* Same as jump, but marks the end of an alternative.  */
+  jump_past_alt,
+
+        /* Followed by two-byte relative address of place to resume at
+           in case of failure.  */
+  on_failure_jump,
+
+        /* Like on_failure_jump, but pushes a placeholder instead of the
+           current string position when executed.  */
+  on_failure_keep_string_jump,
+
+        /* Throw away latest failure point and then jump to following
+           two-byte relative address.  */
+  pop_failure_jump,
+
+        /* Change to pop_failure_jump if know won't have to backtrack to
+           match; otherwise change to jump.  This is used to jump
+           back to the beginning of a repeat.  If what follows this jump
+           clearly won't match what the repeat does, such that we can be
+           sure that there is no use backtracking out of repetitions
+           already matched, then we change it to a pop_failure_jump.
+           Followed by two-byte address.  */
+  maybe_pop_jump,
+
+        /* Jump to following two-byte address, and push a dummy failure
+           point. This failure point will be thrown away if an attempt
+           is made to use it for a failure.  A `+' construct makes this
+           before the first repeat.  Also used as an intermediary kind
+           of jump when compiling an alternative.  */
+  dummy_failure_jump,
+
+       /* Push a dummy failure point and continue.  Used at the end of
+          alternatives.  */
+  push_dummy_failure,
+
+        /* Followed by two-byte relative address and two-byte number n.
+           After matching N times, jump to the address upon failure.  */
+  succeed_n,
+
+        /* Followed by two-byte relative address, and two-byte number n.
+           Jump to the address N times, then fail.  */
+  jump_n,
+
+        /* Set the following two-byte relative address to the
+           subsequent two-byte number.  The address *includes* the two
+           bytes of number.  */
+  set_number_at,
+
+  wordchar,    /* Matches any word-constituent character.  */
+  notwordchar, /* Matches any char that is not a word-constituent.  */
+
+  wordbeg,     /* Succeeds if at word beginning.  */
+  wordend,     /* Succeeds if at word end.  */
+
+  wordbound,   /* Succeeds if at a word boundary.  */
+  notwordbound /* Succeeds if not at a word boundary.  */
+
+#ifdef emacs
+  ,before_dot, /* Succeeds if before point.  */
+  at_dot,      /* Succeeds if at point.  */
+  after_dot,   /* Succeeds if after point.  */
+
+       /* Matches any character whose syntax is specified.  Followed by
+           a byte which contains a syntax code, e.g., Sword.  */
+  syntaxspec,
+
+       /* Matches any character whose syntax is not that specified.  */
+  notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern.  */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
+
+#define STORE_NUMBER(destination, number)                              \
+  do {                                                                 \
+    (destination)[0] = (number) & 0377;                                        \
+    (destination)[1] = (number) >> 8;                                  \
+  } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+   the byte after where the number is stored.  Therefore, DESTINATION
+   must be an lvalue.  */
+
+#define STORE_NUMBER_AND_INCR(destination, number)                     \
+  do {                                                                 \
+    STORE_NUMBER (destination, number);                                        \
+    (destination) += 2;                                                        \
+  } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+   at SOURCE.  */
+
+#define EXTRACT_NUMBER(destination, source)                            \
+  do {                                                                 \
+    (destination) = *(source) & 0377;                                  \
+    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;          \
+  } while (0)
+
+#ifdef DEBUG
+static void extract_number _RE_ARGS ((int *dest, unsigned char *source));
+static void
+extract_number (dest, source)
+    int *dest;
+    unsigned char *source;
+{
+  int temp = SIGN_EXTEND_CHAR (*(source + 1));
+  *dest = *source & 0377;
+  *dest += temp << 8;
+}
+
+# ifndef EXTRACT_MACROS /* To debug the macros.  */
+#  undef EXTRACT_NUMBER
+#  define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+# endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+   SOURCE must be an lvalue.  */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source)                   \
+  do {                                                                 \
+    EXTRACT_NUMBER (destination, source);                              \
+    (source) += 2;                                                     \
+  } while (0)
+
+#ifdef DEBUG
+static void extract_number_and_incr _RE_ARGS ((int *destination,
+                                              unsigned char **source));
+static void
+extract_number_and_incr (destination, source)
+    int *destination;
+    unsigned char **source;
+{
+  extract_number (destination, *source);
+  *source += 2;
+}
+
+# ifndef EXTRACT_MACROS
+#  undef EXTRACT_NUMBER_AND_INCR
+#  define EXTRACT_NUMBER_AND_INCR(dest, src) \
+  extract_number_and_incr (&dest, &src)
+# endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+   it is doing (if the variable `debug' is nonzero).  If linked with the
+   main program in `iregex.c', you can enter patterns and strings
+   interactively.  And if linked with the main program in `main.c' and
+   the other test files, you can run the already-written tests.  */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging.  */
+# include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging.  */
+# include "zassert.h"
+
+static int debug;
+
+# define DEBUG_STATEMENT(e) e
+# define DEBUG_PRINT1(x) if (debug) printf (x)
+# define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+# define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+# define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                                 \
+  if (debug) print_partial_compiled_pattern (s, e)
+# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                        \
+  if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+/* Print the fastmap in human-readable form.  */
+
+void
+print_fastmap (fastmap)
+    char *fastmap;
+{
+  unsigned was_a_range = 0;
+  unsigned i = 0;
+
+  while (i < (1 << BYTEWIDTH))
+    {
+      if (fastmap[i++])
+       {
+         was_a_range = 0;
+          putchar (i - 1);
+          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
+            {
+              was_a_range = 1;
+              i++;
+            }
+         if (was_a_range)
+            {
+              printf ("-");
+              putchar (i - 1);
+            }
+        }
+    }
+  putchar ('\n');
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+   the START pointer into it and ending just before the pointer END.  */
+
+void
+print_partial_compiled_pattern (start, end)
+    unsigned char *start;
+    unsigned char *end;
+{
+  int mcnt, mcnt2;
+  unsigned char *p1;
+  unsigned char *p = start;
+  unsigned char *pend = end;
+
+  if (start == NULL)
+    {
+      printf ("(null)\n");
+      return;
+    }
+
+  /* Loop over pattern commands.  */
+  while (p < pend)
+    {
+      printf ("%d:\t", p - start);
+
+      switch ((re_opcode_t) *p++)
+       {
+        case no_op:
+          printf ("/no_op");
+          break;
+
+       case exactn:
+         mcnt = *p++;
+          printf ("/exactn/%d", mcnt);
+          do
+           {
+              putchar ('/');
+             putchar (*p++);
+            }
+          while (--mcnt);
+          break;
+
+       case start_memory:
+          mcnt = *p++;
+          printf ("/start_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case stop_memory:
+          mcnt = *p++;
+         printf ("/stop_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case duplicate:
+         printf ("/duplicate/%d", *p++);
+         break;
+
+       case anychar:
+         printf ("/anychar");
+         break;
+
+       case charset:
+        case charset_not:
+          {
+            register int c, last = -100;
+           register int in_range = 0;
+
+           printf ("/charset [%s",
+                   (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+
+            assert (p + *p < pend);
+
+            for (c = 0; c < 256; c++)
+             if (c / 8 < *p
+                 && (p[1 + (c/8)] & (1 << (c % 8))))
+               {
+                 /* Are we starting a range?  */
+                 if (last + 1 == c && ! in_range)
+                   {
+                     putchar ('-');
+                     in_range = 1;
+                   }
+                 /* Have we broken a range?  */
+                 else if (last + 1 != c && in_range)
+              {
+                     putchar (last);
+                     in_range = 0;
+                   }
+
+                 if (! in_range)
+                   putchar (c);
+
+                 last = c;
+              }
+
+           if (in_range)
+             putchar (last);
+
+           putchar (']');
+
+           p += 1 + *p;
+         }
+         break;
+
+       case begline:
+         printf ("/begline");
+          break;
+
+       case endline:
+          printf ("/endline");
+          break;
+
+       case on_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_jump to %d", p + mcnt - start);
+          break;
+
+       case on_failure_keep_string_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_keep_string_jump to %d", p + mcnt - start);
+          break;
+
+       case dummy_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/dummy_failure_jump to %d", p + mcnt - start);
+          break;
+
+       case push_dummy_failure:
+          printf ("/push_dummy_failure");
+          break;
+
+        case maybe_pop_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/maybe_pop_jump to %d", p + mcnt - start);
+         break;
+
+        case pop_failure_jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/pop_failure_jump to %d", p + mcnt - start);
+         break;
+
+        case jump_past_alt:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump_past_alt to %d", p + mcnt - start);
+         break;
+
+        case jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump to %d", p + mcnt - start);
+         break;
+
+        case succeed_n:
+          extract_number_and_incr (&mcnt, &p);
+         p1 = p + mcnt;
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/succeed_n to %d, %d times", p1 - start, mcnt2);
+          break;
+
+        case jump_n:
+          extract_number_and_incr (&mcnt, &p);
+         p1 = p + mcnt;
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/jump_n to %d, %d times", p1 - start, mcnt2);
+          break;
+
+        case set_number_at:
+          extract_number_and_incr (&mcnt, &p);
+         p1 = p + mcnt;
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/set_number_at location %d to %d", p1 - start, mcnt2);
+          break;
+
+        case wordbound:
+         printf ("/wordbound");
+         break;
+
+       case notwordbound:
+         printf ("/notwordbound");
+          break;
+
+       case wordbeg:
+         printf ("/wordbeg");
+         break;
+
+       case wordend:
+         printf ("/wordend");
+
+# ifdef emacs
+       case before_dot:
+         printf ("/before_dot");
+          break;
+
+       case at_dot:
+         printf ("/at_dot");
+          break;
+
+       case after_dot:
+         printf ("/after_dot");
+          break;
+
+       case syntaxspec:
+          printf ("/syntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+          break;
+
+       case notsyntaxspec:
+          printf ("/notsyntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+         break;
+# endif /* emacs */
+
+       case wordchar:
+         printf ("/wordchar");
+          break;
+
+       case notwordchar:
+         printf ("/notwordchar");
+          break;
+
+       case begbuf:
+         printf ("/begbuf");
+          break;
+
+       case endbuf:
+         printf ("/endbuf");
+          break;
+
+        default:
+          printf ("?%d", *(p-1));
+       }
+
+      putchar ('\n');
+    }
+
+  printf ("%d:\tend of pattern.\n", p - start);
+}
+
+
+void
+print_compiled_pattern (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  unsigned char *buffer = bufp->buffer;
+
+  print_partial_compiled_pattern (buffer, buffer + bufp->used);
+  printf ("%ld bytes used/%ld bytes allocated.\n",
+         bufp->used, bufp->allocated);
+
+  if (bufp->fastmap_accurate && bufp->fastmap)
+    {
+      printf ("fastmap: ");
+      print_fastmap (bufp->fastmap);
+    }
+
+  printf ("re_nsub: %d\t", bufp->re_nsub);
+  printf ("regs_alloc: %d\t", bufp->regs_allocated);
+  printf ("can_be_null: %d\t", bufp->can_be_null);
+  printf ("newline_anchor: %d\n", bufp->newline_anchor);
+  printf ("no_sub: %d\t", bufp->no_sub);
+  printf ("not_bol: %d\t", bufp->not_bol);
+  printf ("not_eol: %d\t", bufp->not_eol);
+  printf ("syntax: %lx\n", bufp->syntax);
+  /* Perhaps we should print the translate table?  */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+    const char *where;
+    const char *string1;
+    const char *string2;
+    int size1;
+    int size2;
+{
+  int this_char;
+
+  if (where == NULL)
+    printf ("(null)");
+  else
+    {
+      if (FIRST_STRING_P (where))
+        {
+          for (this_char = where - string1; this_char < size1; this_char++)
+            putchar (string1[this_char]);
+
+          where = string2;
+        }
+
+      for (this_char = where - string2; this_char < size2; this_char++)
+        putchar (string2[this_char]);
+    }
+}
+
+void
+printchar (c)
+     int c;
+{
+  putc (c, stderr);
+}
+
+#else /* not DEBUG */
+
+# undef assert
+# define assert(e)
+
+# define DEBUG_STATEMENT(e)
+# define DEBUG_PRINT1(x)
+# define DEBUG_PRINT2(x1, x2)
+# define DEBUG_PRINT3(x1, x2, x3)
+# define DEBUG_PRINT4(x1, x2, x3, x4)
+# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+#ifdef DEBUG
+  if (syntax & RE_DEBUG)
+    debug = 1;
+  else if (debug) /* was on but now is not */
+    debug = 0;
+#endif /* DEBUG */
+  return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+static const char re_error_msgid[] =
+  {
+#define REG_NOERROR_IDX        0
+    gettext_noop ("Success")   /* REG_NOERROR */
+    "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+    gettext_noop ("No match")  /* REG_NOMATCH */
+    "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+    "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+    "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+    "\0"
+#define REG_EESCAPE_IDX        (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+    "\0"
+#define REG_ESUBREG_IDX        (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+    "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+    gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+    "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+    "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+    "\0"
+#define REG_BADBR_IDX  (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+    "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+    gettext_noop ("Invalid range end") /* REG_ERANGE */
+    "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+    gettext_noop ("Memory exhausted") /* REG_ESPACE */
+    "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+    "\0"
+#define REG_EEND_IDX   (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+    gettext_noop ("Premature end of regular expression") /* REG_EEND */
+    "\0"
+#define REG_ESIZE_IDX  (REG_EEND_IDX + sizeof "Premature end of regular expression")
+    gettext_noop ("Regular expression too big") /* REG_ESIZE */
+    "\0"
+#define REG_ERPAREN_IDX        (REG_ESIZE_IDX + sizeof "Regular expression too big")
+    gettext_noop ("Unmatched ) or \\)"), /* REG_ERPAREN */
+  };
+
+static const size_t re_error_msgid_idx[] =
+  {
+    REG_NOERROR_IDX,
+    REG_NOMATCH_IDX,
+    REG_BADPAT_IDX,
+    REG_ECOLLATE_IDX,
+    REG_ECTYPE_IDX,
+    REG_EESCAPE_IDX,
+    REG_ESUBREG_IDX,
+    REG_EBRACK_IDX,
+    REG_EPAREN_IDX,
+    REG_EBRACE_IDX,
+    REG_BADBR_IDX,
+    REG_ERANGE_IDX,
+    REG_ESPACE_IDX,
+    REG_BADRPT_IDX,
+    REG_EEND_IDX,
+    REG_ESIZE_IDX,
+    REG_ERPAREN_IDX
+  };
+
+/* Avoiding alloca during matching, to placate r_alloc.  */
+
+/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the
+   searching and matching functions should not call alloca.  On some
+   systems, alloca is implemented in terms of malloc, and if we're
+   using the relocating allocator routines, then malloc could cause a
+   relocation, which might (if the strings being searched are in the
+   ralloc heap) shift the data out from underneath the regexp
+   routines.
+
+   Here's another reason to avoid allocation: Emacs
+   processes input from X in a signal handler; processing X input may
+   call malloc; if input arrives while a matching routine is calling
+   malloc, then we're scrod.  But Emacs can't just block input while
+   calling matching routines; then we don't notice interrupts when
+   they come in.  So, Emacs blocks input around all regexp calls
+   except the matching calls, which it leaves unprotected, in the
+   faith that they will not malloc.  */
+
+/* Normally, this is fine.  */
+#define MATCH_MAY_ALLOCATE
+
+/* When using GNU C, we are not REALLY using the C alloca, no matter
+   what config.h may say.  So don't take precautions for it.  */
+#ifdef __GNUC__
+# undef C_ALLOCA
+#endif
+
+/* The match routines may not allocate if (1) they would do it with malloc
+   and (2) it's not safe for them to use malloc.
+   Note that if REL_ALLOC is defined, matching would not use malloc for the
+   failure stack, but we would still use it for the register vectors;
+   so REL_ALLOC should not affect this.  */
+#if (defined C_ALLOCA || defined REGEX_MALLOC) && defined emacs
+# undef MATCH_MAY_ALLOCATE
+#endif
+
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+   re_match_2 use a failure stack.  These have to be macros because of
+   REGEX_ALLOCATE_STACK.  */
+
+
+/* Number of failure points for which to initially allocate space
+   when matching.  If this number is exceeded, we allocate more
+   space, so it is not a hard limit.  */
+#ifndef INIT_FAILURE_ALLOC
+# define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack.  Would be
+   exactly that if always used MAX_FAILURE_ITEMS items each time we failed.
+   This is a variable only so users of regex can assign to it; we never
+   change it ourselves.  */
+
+#ifdef INT_IS_16BIT
+
+# if defined MATCH_MAY_ALLOCATE
+/* 4400 was enough to cause a crash on Alpha OSF/1,
+   whose default stack limit is 2mb.  */
+long int re_max_failures = 4000;
+# else
+long int re_max_failures = 2000;
+# endif
+
+union fail_stack_elt
+{
+  unsigned char *pointer;
+  long int integer;
+};
+
+typedef union fail_stack_elt fail_stack_elt_t;
+
+typedef struct
+{
+  fail_stack_elt_t *stack;
+  unsigned long int size;
+  unsigned long int avail;             /* Offset of next open position.  */
+} fail_stack_type;
+
+#else /* not INT_IS_16BIT */
+
+# if defined MATCH_MAY_ALLOCATE
+/* 4400 was enough to cause a crash on Alpha OSF/1,
+   whose default stack limit is 2mb.  */
+int re_max_failures = 20000;
+# else
+int re_max_failures = 2000;
+# endif
+
+union fail_stack_elt
+{
+  unsigned char *pointer;
+  int integer;
+};
+
+typedef union fail_stack_elt fail_stack_elt_t;
+
+typedef struct
+{
+  fail_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} fail_stack_type;
+
+#endif /* INT_IS_16BIT */
+
+#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
+
+
+/* Define macros to initialize and free the failure stack.
+   Do `return -2' if the alloc fails.  */
+
+#ifdef MATCH_MAY_ALLOCATE
+# define INIT_FAIL_STACK()                                             \
+  do {                                                                 \
+    fail_stack.stack = (fail_stack_elt_t *)                            \
+      REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+                                                                       \
+    if (fail_stack.stack == NULL)                                      \
+      return -2;                                                       \
+                                                                       \
+    fail_stack.size = INIT_FAILURE_ALLOC;                              \
+    fail_stack.avail = 0;                                              \
+  } while (0)
+
+# define RESET_FAIL_STACK()  REGEX_FREE_STACK (fail_stack.stack)
+#else
+# define INIT_FAIL_STACK()                                             \
+  do {                                                                 \
+    fail_stack.avail = 0;                                              \
+  } while (0)
+
+# define RESET_FAIL_STACK()
+#endif
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+   Return 1 if succeeds, and 0 if either ran out of memory
+   allocating space for it or it was already too large.
+
+   REGEX_REALLOCATE_STACK requires `destination' be declared.   */
+
+#define DOUBLE_FAIL_STACK(fail_stack)                                  \
+  ((fail_stack).size > (unsigned) (re_max_failures * MAX_FAILURE_ITEMS)        \
+   ? 0                                                                 \
+   : ((fail_stack).stack = (fail_stack_elt_t *)                                \
+        REGEX_REALLOCATE_STACK ((fail_stack).stack,                    \
+          (fail_stack).size * sizeof (fail_stack_elt_t),               \
+          ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),       \
+                                                                       \
+      (fail_stack).stack == NULL                                       \
+      ? 0                                                              \
+      : ((fail_stack).size <<= 1,                                      \
+         1)))
+
+
+/* Push pointer POINTER on FAIL_STACK.
+   Return 1 if was able to do so and 0 if ran out of memory allocating
+   space to do so.  */
+#define PUSH_PATTERN_OP(POINTER, FAIL_STACK)                           \
+  ((FAIL_STACK_FULL ()                                                 \
+    && !DOUBLE_FAIL_STACK (FAIL_STACK))                                        \
+   ? 0                                                                 \
+   : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER,      \
+      1))
+
+/* Push a pointer value onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_POINTER(item)                                     \
+  fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item)
+
+/* This pushes an integer-valued item onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_INT(item)                                 \
+  fail_stack.stack[fail_stack.avail++].integer = (item)
+
+/* Push a fail_stack_elt_t value onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_ELT(item)                                 \
+  fail_stack.stack[fail_stack.avail++] =  (item)
+
+/* These three POP... operations complement the three PUSH... operations.
+   All assume that `fail_stack' is nonempty.  */
+#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer
+#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer
+#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging.  */
+#ifdef DEBUG
+# define DEBUG_PUSH PUSH_FAILURE_INT
+# define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT ()
+#else
+# define DEBUG_PUSH(item)
+# define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+   if we ever fail back to it.
+
+   Requires variables fail_stack, regstart, regend, reg_info, and
+   num_regs_pushed be declared.  DOUBLE_FAIL_STACK requires `destination'
+   be declared.
+
+   Does `return FAILURE_CODE' if runs out of memory.  */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
+  do {                                                                 \
+    char *destination;                                                 \
+    /* Must be int, so when we don't save any registers, the arithmetic        \
+       of 0 + -1 isn't done as unsigned.  */                           \
+    /* Can't be int, since there is not a shred of a guarantee that int        \
+       is wide enough to hold a value of something to which pointer can        \
+       be assigned */                                                  \
+    active_reg_t this_reg;                                             \
+                                                                       \
+    DEBUG_STATEMENT (failure_id++);                                    \
+    DEBUG_STATEMENT (nfailure_points_pushed++);                                \
+    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);          \
+    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
+    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
+                                                                       \
+    DEBUG_PRINT2 ("  slots needed: %ld\n", NUM_FAILURE_ITEMS);         \
+    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);      \
+                                                                       \
+    /* Ensure we have enough space allocated for what we will push.  */        \
+    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)                  \
+      {                                                                        \
+        if (!DOUBLE_FAIL_STACK (fail_stack))                           \
+          return failure_code;                                         \
+                                                                       \
+        DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",             \
+                      (fail_stack).size);                              \
+        DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+      }                                                                        \
+                                                                       \
+    /* Push the info, starting with the registers.  */                 \
+    DEBUG_PRINT1 ("\n");                                               \
+                                                                       \
+    if (1)                                                             \
+      for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+          this_reg++)                                                  \
+       {                                                               \
+         DEBUG_PRINT2 ("  Pushing reg: %lu\n", this_reg);              \
+         DEBUG_STATEMENT (num_regs_pushed++);                          \
+                                                                       \
+         DEBUG_PRINT2 ("    start: %p\n", regstart[this_reg]);         \
+         PUSH_FAILURE_POINTER (regstart[this_reg]);                    \
+                                                                       \
+         DEBUG_PRINT2 ("    end: %p\n", regend[this_reg]);             \
+         PUSH_FAILURE_POINTER (regend[this_reg]);                      \
+                                                                       \
+         DEBUG_PRINT2 ("    info: %p\n      ",                         \
+                       reg_info[this_reg].word.pointer);               \
+         DEBUG_PRINT2 (" match_null=%d",                               \
+                       REG_MATCH_NULL_STRING_P (reg_info[this_reg]));  \
+         DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));  \
+         DEBUG_PRINT2 (" matched_something=%d",                        \
+                       MATCHED_SOMETHING (reg_info[this_reg]));        \
+         DEBUG_PRINT2 (" ever_matched=%d",                             \
+                       EVER_MATCHED_SOMETHING (reg_info[this_reg]));   \
+         DEBUG_PRINT1 ("\n");                                          \
+         PUSH_FAILURE_ELT (reg_info[this_reg].word);                   \
+       }                                                               \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing  low active reg: %ld\n", lowest_active_reg);\
+    PUSH_FAILURE_INT (lowest_active_reg);                              \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing high active reg: %ld\n", highest_active_reg);\
+    PUSH_FAILURE_INT (highest_active_reg);                             \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing pattern %p:\n", pattern_place);           \
+    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);          \
+    PUSH_FAILURE_POINTER (pattern_place);                              \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing string %p: `", string_place);             \
+    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
+                                size2);                                \
+    DEBUG_PRINT1 ("'\n");                                              \
+    PUSH_FAILURE_POINTER (string_place);                               \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);           \
+    DEBUG_PUSH (failure_id);                                           \
+  } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+   for each register.  */
+#define NUM_REG_ITEMS  3
+
+/* Individual items aside from the registers.  */
+#ifdef DEBUG
+# define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
+#else
+# define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack.  */
+/* We used to use (num_regs - 1), which is the number of registers
+   this regexp will save; but that was changed to 5
+   to avoid stack overflow for a regexp with lots of parens.  */
+#define MAX_FAILURE_ITEMS (5 * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items.  */
+#define NUM_FAILURE_ITEMS                              \
+  (((0                                                 \
+     ? 0 : highest_active_reg - lowest_active_reg + 1) \
+    * NUM_REG_ITEMS)                                   \
+   + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it.  */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+   We restore into the parameters, all of which should be lvalues:
+     STR -- the saved data position.
+     PAT -- the saved pattern position.
+     LOW_REG, HIGH_REG -- the highest and lowest active registers.
+     REGSTART, REGEND -- arrays of string positions.
+     REG_INFO -- array of information about each subexpression.
+
+   Also assumes the variables `fail_stack' and (if debugging), `bufp',
+   `pend', `string1', `size1', `string2', and `size2'.  */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{                                                                      \
+  DEBUG_STATEMENT (unsigned failure_id;)                               \
+  active_reg_t this_reg;                                               \
+  const unsigned char *string_temp;                                    \
+                                                                       \
+  assert (!FAIL_STACK_EMPTY ());                                       \
+                                                                       \
+  /* Remove failure points and point to how many regs pushed.  */      \
+  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                               \
+  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);   \
+  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);    \
+                                                                       \
+  assert (fail_stack.avail >= NUM_NONREG_ITEMS);                       \
+                                                                       \
+  DEBUG_POP (&failure_id);                                             \
+  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);             \
+                                                                       \
+  /* If the saved string location is NULL, it came from an             \
+     on_failure_keep_string_jump opcode, and we want to throw away the \
+     saved NULL, thus retaining our current position in the string.  */        \
+  string_temp = POP_FAILURE_POINTER ();                                        \
+  if (string_temp != NULL)                                             \
+    str = (const char *) string_temp;                                  \
+                                                                       \
+  DEBUG_PRINT2 ("  Popping string %p: `", str);                                \
+  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);     \
+  DEBUG_PRINT1 ("'\n");                                                        \
+                                                                       \
+  pat = (unsigned char *) POP_FAILURE_POINTER ();                      \
+  DEBUG_PRINT2 ("  Popping pattern %p:\n", pat);                       \
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);                      \
+                                                                       \
+  /* Restore register info.  */                                                \
+  high_reg = (active_reg_t) POP_FAILURE_INT ();                                \
+  DEBUG_PRINT2 ("  Popping high active reg: %ld\n", high_reg);         \
+                                                                       \
+  low_reg = (active_reg_t) POP_FAILURE_INT ();                         \
+  DEBUG_PRINT2 ("  Popping  low active reg: %ld\n", low_reg);          \
+                                                                       \
+  if (1)                                                               \
+    for (this_reg = high_reg; this_reg >= low_reg; this_reg--)         \
+      {                                                                        \
+       DEBUG_PRINT2 ("    Popping reg: %ld\n", this_reg);              \
+                                                                       \
+       reg_info[this_reg].word = POP_FAILURE_ELT ();                   \
+       DEBUG_PRINT2 ("      info: %p\n",                               \
+                     reg_info[this_reg].word.pointer);                 \
+                                                                       \
+       regend[this_reg] = (const char *) POP_FAILURE_POINTER ();       \
+       DEBUG_PRINT2 ("      end: %p\n", regend[this_reg]);             \
+                                                                       \
+       regstart[this_reg] = (const char *) POP_FAILURE_POINTER ();     \
+       DEBUG_PRINT2 ("      start: %p\n", regstart[this_reg]);         \
+      }                                                                        \
+  else                                                                 \
+    {                                                                  \
+      for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \
+       {                                                               \
+         reg_info[this_reg].word.integer = 0;                          \
+         regend[this_reg] = 0;                                         \
+         regstart[this_reg] = 0;                                       \
+       }                                                               \
+      highest_active_reg = high_reg;                                   \
+    }                                                                  \
+                                                                       \
+  set_regs_matched_done = 0;                                           \
+  DEBUG_STATEMENT (nfailure_points_popped++);                          \
+} /* POP_FAILURE_POINT */
+
+
+
+/* Structure for per-register (a.k.a. per-group) information.
+   Other register information, such as the
+   starting and ending positions (which are addresses), and the list of
+   inner groups (which is a bits list) are maintained in separate
+   variables.
+
+   We are making a (strictly speaking) nonportable assumption here: that
+   the compiler will pack our bit fields into something that fits into
+   the type of `word', i.e., is something that fits into one item on the
+   failure stack.  */
+
+
+/* Declarations and macros for re_match_2.  */
+
+typedef union
+{
+  fail_stack_elt_t word;
+  struct
+  {
+      /* This field is one if this group can match the empty string,
+         zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
+#define MATCH_NULL_UNSET_VALUE 3
+    unsigned match_null_string_p : 2;
+    unsigned is_active : 1;
+    unsigned matched_something : 1;
+    unsigned ever_matched_something : 1;
+  } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R)  ((R).bits.is_active)
+#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+   for the subexpressions which we are currently inside.  Also records
+   that those subexprs have matched.  */
+#define SET_REGS_MATCHED()                                             \
+  do                                                                   \
+    {                                                                  \
+      if (!set_regs_matched_done)                                      \
+       {                                                               \
+         active_reg_t r;                                               \
+         set_regs_matched_done = 1;                                    \
+         for (r = lowest_active_reg; r <= highest_active_reg; r++)     \
+           {                                                           \
+             MATCHED_SOMETHING (reg_info[r])                           \
+               = EVER_MATCHED_SOMETHING (reg_info[r])                  \
+               = 1;                                                    \
+           }                                                           \
+       }                                                               \
+    }                                                                  \
+  while (0)
+
+/* Registers are set to a sentinel when they haven't yet matched.  */
+static char reg_unset_dummy;
+#define REG_UNSET_VALUE (&reg_unset_dummy)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+/* Subroutine declarations and macros for regex_compile.  */
+
+static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size,
+                                             reg_syntax_t syntax,
+                                             struct re_pattern_buffer *bufp));
+static void store_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, int arg));
+static void store_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+                                int arg1, int arg2));
+static void insert_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+                                 int arg, unsigned char *end));
+static void insert_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc,
+                                 int arg1, int arg2, unsigned char *end));
+static boolean at_begline_loc_p _RE_ARGS ((const char *pattern, const char *p,
+                                          reg_syntax_t syntax));
+static boolean at_endline_loc_p _RE_ARGS ((const char *p, const char *pend,
+                                          reg_syntax_t syntax));
+static reg_errcode_t compile_range _RE_ARGS ((const char **p_ptr,
+                                             const char *pend,
+                                             char *translate,
+                                             reg_syntax_t syntax,
+                                             unsigned char *b));
+
+/* Fetch the next character in the uncompiled pattern---translating it
+   if necessary.  Also cast from a signed character in the constant
+   string passed to us by the user to an unsigned char that we can use
+   as an array index (in, e.g., `translate').  */
+#ifndef PATFETCH
+# define PATFETCH(c)                                                   \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+    if (translate) c = (unsigned char) translate[c];                   \
+  } while (0)
+#endif
+
+/* Fetch the next character in the uncompiled pattern, with no
+   translation.  */
+#define PATFETCH_RAW(c)                                                        \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+  } while (0)
+
+/* Go backwards one character in the pattern.  */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D.  We
+   cast the subscript to translate because some data is declared as
+   `char *', to avoid warnings when a string constant is passed.  But
+   when we use a character as a subscript we must make it unsigned.  */
+#ifndef TRANSLATE
+# define TRANSLATE(d) \
+  (translate ? (char) translate[(unsigned char) (d)] : (d))
+#endif
+
+
+/* Macros for outputting the compiled pattern into `buffer'.  */
+
+/* If the buffer isn't allocated when it comes in, use this.  */
+#define INIT_BUF_SIZE  32
+
+/* Make sure we have at least N more bytes of space in buffer.  */
+#define GET_BUFFER_SPACE(n)                                            \
+    while ((unsigned long) (b - bufp->buffer + (n)) > bufp->allocated) \
+      EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it.  */
+#define BUF_PUSH(c)                                                    \
+  do {                                                                 \
+    GET_BUFFER_SPACE (1);                                              \
+    *b++ = (unsigned char) (c);                                                \
+  } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
+#define BUF_PUSH_2(c1, c2)                                             \
+  do {                                                                 \
+    GET_BUFFER_SPACE (2);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+  } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes.  */
+#define BUF_PUSH_3(c1, c2, c3)                                         \
+  do {                                                                 \
+    GET_BUFFER_SPACE (3);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+    *b++ = (unsigned char) (c3);                                       \
+  } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO.  We store a
+   relative address offset by the three bytes the jump itself occupies.  */
+#define STORE_JUMP(op, loc, to) \
+  store_op1 (op, loc, (int) ((to) - (loc) - 3))
+
+/* Likewise, for a two-argument jump.  */
+#define STORE_JUMP2(op, loc, to, arg) \
+  store_op2 (op, loc, (int) ((to) - (loc) - 3), arg)
+
+/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP(op, loc, to) \
+  insert_op1 (op, loc, (int) ((to) - (loc) - 3), b)
+
+/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP2(op, loc, to, arg) \
+  insert_op2 (op, loc, (int) ((to) - (loc) - 3), arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+   into the pattern are two bytes long.  So if 2^16 bytes turns out to
+   be too small, many things would have to change.  */
+/* Any other compiler which, like MSC, has allocation limit below 2^16
+   bytes will have to use approach similar to what was done below for
+   MSC and drop MAX_BUF_SIZE a bit.  Otherwise you may end up
+   reallocating to 0 bytes.  Such thing is not going to work too well.
+   You have been warned!!  */
+#if defined _MSC_VER  && !defined _WIN32
+/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes.
+   The REALLOC define eliminates a flurry of conversion warnings,
+   but is not required. */
+# define MAX_BUF_SIZE  65500L
+# define REALLOC(p,s) realloc ((p), (size_t) (s))
+#else
+# define MAX_BUF_SIZE (1L << 16)
+# define REALLOC(p,s) realloc ((p), (s))
+#endif
+
+/* Extend the buffer by twice its current size via realloc and
+   reset the pointers that pointed into the old block to point to the
+   correct places in the new one.  If extending the buffer results in it
+   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
+#define EXTEND_BUFFER()                                                        \
+  do {                                                                         \
+    unsigned char *old_buffer = bufp->buffer;                          \
+    if (bufp->allocated == MAX_BUF_SIZE)                               \
+      return REG_ESIZE;                                                        \
+    bufp->allocated <<= 1;                                             \
+    if (bufp->allocated > MAX_BUF_SIZE)                                        \
+      bufp->allocated = MAX_BUF_SIZE;                                  \
+    bufp->buffer = (unsigned char *) REALLOC (bufp->buffer, bufp->allocated);\
+    if (bufp->buffer == NULL)                                          \
+      return REG_ESPACE;                                               \
+    /* If the buffer moved, move all the pointers into it.  */         \
+    if (old_buffer != bufp->buffer)                                    \
+      {                                                                        \
+        b = (b - old_buffer) + bufp->buffer;                           \
+        begalt = (begalt - old_buffer) + bufp->buffer;                 \
+        if (fixup_alt_jump)                                            \
+          fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+        if (laststart)                                                 \
+          laststart = (laststart - old_buffer) + bufp->buffer;         \
+        if (pending_exact)                                             \
+          pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+      }                                                                        \
+  } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+   {start,stop}_memory, the maximum number of groups we can report
+   things about is what fits in that byte.  */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers.  We just
+   ignore the excess.  */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack.  */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
+/* int may be not enough when sizeof(int) == 2.  */
+typedef long pattern_offset_t;
+
+typedef struct
+{
+  pattern_offset_t begalt_offset;
+  pattern_offset_t fixup_alt_jump;
+  pattern_offset_t inner_group_offset;
+  pattern_offset_t laststart_offset;
+  regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+  compile_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
+
+/* The next available element.  */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list.  */
+#define SET_LIST_BIT(c)                               \
+  (b[((unsigned char) (c)) / BYTEWIDTH]               \
+   |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern.  */
+#define GET_UNSIGNED_NUMBER(num)                                       \
+  { if (p != pend)                                                     \
+     {                                                                 \
+       PATFETCH (c);                                                   \
+       while (ISDIGIT (c))                                             \
+         {                                                             \
+           if (num < 0)                                                        \
+              num = 0;                                                 \
+           num = num * 10 + c - '0';                                   \
+           if (p == pend)                                              \
+              break;                                                   \
+           PATFETCH (c);                                               \
+         }                                                             \
+       }                                                               \
+    }
+
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+/* The GNU C library provides support for user-defined character classes
+   and the functions from ISO C amendement 1.  */
+# ifdef CHARCLASS_NAME_MAX
+#  define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
+# else
+/* This shouldn't happen but some implementation might still have this
+   problem.  Use a reasonable default value.  */
+#  define CHAR_CLASS_MAX_LENGTH 256
+# endif
+
+# ifdef _LIBC
+#  define IS_CHAR_CLASS(string) __wctype (string)
+# else
+#  define IS_CHAR_CLASS(string) wctype (string)
+# endif
+#else
+# define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+# define IS_CHAR_CLASS(string)                                         \
+   (STREQ (string, "alpha") || STREQ (string, "upper")                 \
+    || STREQ (string, "lower") || STREQ (string, "digit")              \
+    || STREQ (string, "alnum") || STREQ (string, "xdigit")             \
+    || STREQ (string, "space") || STREQ (string, "print")              \
+    || STREQ (string, "punct") || STREQ (string, "graph")              \
+    || STREQ (string, "cntrl") || STREQ (string, "blank"))
+#endif
+
+#ifndef MATCH_MAY_ALLOCATE
+
+/* If we cannot allocate large objects within re_match_2_internal,
+   we make the fail stack and register vectors global.
+   The fail stack, we grow to the maximum size when a regexp
+   is compiled.
+   The register vectors, we adjust in size each time we
+   compile a regexp, according to the number of registers it needs.  */
+
+static fail_stack_type fail_stack;
+
+/* Size with which the following vectors are currently allocated.
+   That is so we can make them bigger as needed,
+   but never make them smaller.  */
+static int regs_allocated_size;
+
+static const char **     regstart, **     regend;
+static const char ** old_regstart, ** old_regend;
+static const char **best_regstart, **best_regend;
+static register_info_type *reg_info;
+static const char **reg_dummy;
+static register_info_type *reg_info_dummy;
+
+/* Make the register vectors big enough for NUM_REGS registers,
+   but don't make them smaller.  */
+
+static
+regex_grow_registers (num_regs)
+     int num_regs;
+{
+  if (num_regs > regs_allocated_size)
+    {
+      RETALLOC_IF (regstart,    num_regs, const char *);
+      RETALLOC_IF (regend,      num_regs, const char *);
+      RETALLOC_IF (old_regstart, num_regs, const char *);
+      RETALLOC_IF (old_regend,  num_regs, const char *);
+      RETALLOC_IF (best_regstart, num_regs, const char *);
+      RETALLOC_IF (best_regend,         num_regs, const char *);
+      RETALLOC_IF (reg_info,    num_regs, register_info_type);
+      RETALLOC_IF (reg_dummy,   num_regs, const char *);
+      RETALLOC_IF (reg_info_dummy, num_regs, register_info_type);
+
+      regs_allocated_size = num_regs;
+    }
+}
+
+#endif /* not MATCH_MAY_ALLOCATE */
+
+static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type
+                                                compile_stack,
+                                                regnum_t regnum));
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+   Returns one of error codes defined in `regex.h', or zero for success.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate'
+   fields are set in BUFP on entry.
+
+   If it succeeds, results are put in BUFP (if it returns an error, the
+   contents of BUFP are undefined):
+     `buffer' is the compiled pattern;
+     `syntax' is set to SYNTAX;
+     `used' is set to the length of the compiled pattern;
+     `fastmap_accurate' is zero;
+     `re_nsub' is the number of subexpressions in PATTERN;
+     `not_bol' and `not_eol' are zero;
+
+   The `fastmap' and `newline_anchor' fields are neither
+   examined nor set.  */
+
+/* Return, freeing storage we allocated.  */
+#define FREE_STACK_RETURN(value)               \
+  return (free (compile_stack.stack), value)
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+     const char *pattern;
+     size_t size;
+     reg_syntax_t syntax;
+     struct re_pattern_buffer *bufp;
+{
+  /* We fetch characters from PATTERN here.  Even though PATTERN is
+     `char *' (i.e., signed), we declare these variables as unsigned, so
+     they can be reliably used as array indices.  */
+  register unsigned char c, c1;
+
+  /* A random temporary spot in PATTERN.  */
+  const char *p1;
+
+  /* Points to the end of the buffer, where we should append.  */
+  register unsigned char *b;
+
+  /* Keeps track of unclosed groups.  */
+  compile_stack_type compile_stack;
+
+  /* Points to the current (ending) position in the pattern.  */
+  const char *p = pattern;
+  const char *pend = pattern + size;
+
+  /* How to translate the characters in the pattern.  */
+  RE_TRANSLATE_TYPE translate = bufp->translate;
+
+  /* Address of the count-byte of the most recently inserted `exactn'
+     command.  This makes it possible to tell if a new exact-match
+     character can be added to that command or if the character requires
+     a new `exactn' command.  */
+  unsigned char *pending_exact = 0;
+
+  /* Address of start of the most recently finished expression.
+     This tells, e.g., postfix * where to find the start of its
+     operand.  Reset at the beginning of groups and alternatives.  */
+  unsigned char *laststart = 0;
+
+  /* Address of beginning of regexp, or inside of last group.  */
+  unsigned char *begalt;
+
+  /* Place in the uncompiled pattern (i.e., the {) to
+     which to go back if the interval is invalid.  */
+  const char *beg_interval;
+
+  /* Address of the place where a forward jump should go to the end of
+     the containing expression.  Each alternative of an `or' -- except the
+     last -- ends with a forward jump of this sort.  */
+  unsigned char *fixup_alt_jump = 0;
+
+  /* Counts open-groups as they are encountered.  Remembered for the
+     matching close-group on the compile stack, so the same register
+     number is put in the stop_memory as the start_memory.  */
+  regnum_t regnum = 0;
+
+#ifdef DEBUG
+  DEBUG_PRINT1 ("\nCompiling pattern: ");
+  if (debug)
+    {
+      unsigned debug_count;
+
+      for (debug_count = 0; debug_count < size; debug_count++)
+        putchar (pattern[debug_count]);
+      putchar ('\n');
+    }
+#endif /* DEBUG */
+
+  /* Initialize the compile stack.  */
+  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+  if (compile_stack.stack == NULL)
+    return REG_ESPACE;
+
+  compile_stack.size = INIT_COMPILE_STACK_SIZE;
+  compile_stack.avail = 0;
+
+  /* Initialize the pattern buffer.  */
+  bufp->syntax = syntax;
+  bufp->fastmap_accurate = 0;
+  bufp->not_bol = bufp->not_eol = 0;
+
+  /* Set `used' to zero, so that if we return an error, the pattern
+     printer (for debugging) will think there's no pattern.  We reset it
+     at the end.  */
+  bufp->used = 0;
+
+  /* Always count groups, whether or not bufp->no_sub is set.  */
+  bufp->re_nsub = 0;
+
+#if !defined emacs && !defined SYNTAX_TABLE
+  /* Initialize the syntax table.  */
+   init_syntax_once ();
+#endif
+
+  if (bufp->allocated == 0)
+    {
+      if (bufp->buffer)
+       { /* If zero allocated, but buffer is non-null, try to realloc
+             enough space.  This loses if buffer's address is bogus, but
+             that is the user's responsibility.  */
+          RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+        }
+      else
+        { /* Caller did not allocate a buffer.  Do it for them.  */
+          bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+        }
+      if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE);
+
+      bufp->allocated = INIT_BUF_SIZE;
+    }
+
+  begalt = b = bufp->buffer;
+
+  /* Loop through the uncompiled pattern until we're at the end.  */
+  while (p != pend)
+    {
+      PATFETCH (c);
+
+      switch (c)
+        {
+        case '^':
+          {
+            if (   /* If at start of pattern, it's an operator.  */
+                   p == pattern + 1
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's come before.  */
+                || at_begline_loc_p (pattern, p, syntax))
+              BUF_PUSH (begline);
+            else
+              goto normal_char;
+          }
+          break;
+
+
+        case '$':
+          {
+            if (   /* If at end of pattern, it's an operator.  */
+                   p == pend
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's next.  */
+                || at_endline_loc_p (p, pend, syntax))
+               BUF_PUSH (endline);
+             else
+               goto normal_char;
+           }
+           break;
+
+
+       case '+':
+        case '?':
+          if ((syntax & RE_BK_PLUS_QM)
+              || (syntax & RE_LIMITED_OPS))
+            goto normal_char;
+        handle_plus:
+        case '*':
+          /* If there is no previous pattern... */
+          if (!laststart)
+            {
+              if (syntax & RE_CONTEXT_INVALID_OPS)
+                FREE_STACK_RETURN (REG_BADRPT);
+              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                goto normal_char;
+            }
+
+          {
+            /* Are we optimizing this jump?  */
+            boolean keep_string_p = false;
+
+            /* 1 means zero (many) matches is allowed.  */
+            char zero_times_ok = 0, many_times_ok = 0;
+
+            /* If there is a sequence of repetition chars, collapse it
+               down to just one (the right one).  We can't combine
+               interval operators with these because of, e.g., `a{2}*',
+               which should only match an even number of `a's.  */
+
+            for (;;)
+              {
+                zero_times_ok |= c != '+';
+                many_times_ok |= c != '?';
+
+                if (p == pend)
+                  break;
+
+                PATFETCH (c);
+
+                if (c == '*'
+                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+                  ;
+
+                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
+                  {
+                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+                    PATFETCH (c1);
+                    if (!(c1 == '+' || c1 == '?'))
+                      {
+                        PATUNFETCH;
+                        PATUNFETCH;
+                        break;
+                      }
+
+                    c = c1;
+                  }
+                else
+                  {
+                    PATUNFETCH;
+                    break;
+                  }
+
+                /* If we get here, we found another repeat character.  */
+               }
+
+            /* Star, etc. applied to an empty pattern is equivalent
+               to an empty pattern.  */
+            if (!laststart)
+              break;
+
+            /* Now we know whether or not zero matches is allowed
+               and also whether or not two or more matches is allowed.  */
+            if (many_times_ok)
+              { /* More than one repetition is allowed, so put in at the
+                   end a backward relative jump from `b' to before the next
+                   jump we're going to put in below (which jumps from
+                   laststart to after this jump).
+
+                   But if we are at the `*' in the exact sequence `.*\n',
+                   insert an unconditional jump backwards to the .,
+                   instead of the beginning of the loop.  This way we only
+                   push a failure point once, instead of every time
+                   through the loop.  */
+                assert (p - 1 > pattern);
+
+                /* Allocate the space for the jump.  */
+                GET_BUFFER_SPACE (3);
+
+                /* We know we are not at the first character of the pattern,
+                   because laststart was nonzero.  And we've already
+                   incremented `p', by the way, to be the character after
+                   the `*'.  Do we have to do something analogous here
+                   for null bytes, because of RE_DOT_NOT_NULL?  */
+                if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+                   && zero_times_ok
+                    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+                    && !(syntax & RE_DOT_NEWLINE))
+                  { /* We have .*\n.  */
+                    STORE_JUMP (jump, b, laststart);
+                    keep_string_p = true;
+                  }
+                else
+                  /* Anything else.  */
+                  STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+                /* We've added more stuff to the buffer.  */
+                b += 3;
+              }
+
+            /* On failure, jump from laststart to b + 3, which will be the
+               end of the buffer after this jump is inserted.  */
+            GET_BUFFER_SPACE (3);
+            INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+                                       : on_failure_jump,
+                         laststart, b + 3);
+            pending_exact = 0;
+            b += 3;
+
+            if (!zero_times_ok)
+              {
+                /* At least one repetition is required, so insert a
+                   `dummy_failure_jump' before the initial
+                   `on_failure_jump' instruction of the loop. This
+                   effects a skip over that instruction the first time
+                   we hit that loop.  */
+                GET_BUFFER_SPACE (3);
+                INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+                b += 3;
+              }
+            }
+         break;
+
+
+       case '.':
+          laststart = b;
+          BUF_PUSH (anychar);
+          break;
+
+
+        case '[':
+          {
+            boolean had_char_class = false;
+
+            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+            /* Ensure that we have enough space to push a charset: the
+               opcode, the length count, and the bitset; 34 bytes in all.  */
+           GET_BUFFER_SPACE (34);
+
+            laststart = b;
+
+            /* We test `*p == '^' twice, instead of using an if
+               statement, so we only need one BUF_PUSH.  */
+            BUF_PUSH (*p == '^' ? charset_not : charset);
+            if (*p == '^')
+              p++;
+
+            /* Remember the first position in the bracket expression.  */
+            p1 = p;
+
+            /* Push the number of bytes in the bitmap.  */
+            BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* Clear the whole map.  */
+            memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* charset_not matches newline according to a syntax bit.  */
+            if ((re_opcode_t) b[-2] == charset_not
+                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+              SET_LIST_BIT ('\n');
+
+            /* Read in characters and ranges, setting map bits.  */
+            for (;;)
+              {
+                if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                PATFETCH (c);
+
+                /* \ might escape characters inside [...] and [^...].  */
+                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+                  {
+                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+                    PATFETCH (c1);
+                    SET_LIST_BIT (c1);
+                    continue;
+                  }
+
+                /* Could be the end of the bracket expression.  If it's
+                   not (i.e., when the bracket expression is `[]' so
+                   far), the ']' character bit gets set way below.  */
+                if (c == ']' && p != p1 + 1)
+                  break;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character class.  */
+                if (had_char_class && c == '-' && *p != ']')
+                  FREE_STACK_RETURN (REG_ERANGE);
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character: if this is a hyphen not at the
+                   beginning or the end of a list, then it's the range
+                   operator.  */
+                if (c == '-'
+                    && !(p - 2 >= pattern && p[-2] == '[')
+                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+                    && *p != ']')
+                  {
+                    reg_errcode_t ret
+                      = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+                  }
+
+                else if (p[0] == '-' && p[1] != ']')
+                  { /* This handles ranges made up of characters only.  */
+                    reg_errcode_t ret;
+
+                   /* Move past the `-'.  */
+                    PATFETCH (c1);
+
+                    ret = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+                  }
+
+                /* See if we're at the beginning of a possible character
+                   class.  */
+
+                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+                  { /* Leave room for the null.  */
+                    char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+                    PATFETCH (c);
+                    c1 = 0;
+
+                    /* If pattern is `[[:'.  */
+                    if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                    for (;;)
+                      {
+                        PATFETCH (c);
+                        if ((c == ':' && *p == ']') || p == pend)
+                          break;
+                       if (c1 < CHAR_CLASS_MAX_LENGTH)
+                         str[c1++] = c;
+                       else
+                         /* This is in any case an invalid class name.  */
+                         str[0] = '\0';
+                      }
+                    str[c1] = '\0';
+
+                    /* If isn't a word bracketed by `[:' and `:]':
+                       undo the ending character, the letters, and leave
+                       the leading `:' and `[' (but set bits for them).  */
+                    if (c == ':' && *p == ']')
+                      {
+#if defined _LIBC || WIDE_CHAR_SUPPORT
+                        boolean is_lower = STREQ (str, "lower");
+                        boolean is_upper = STREQ (str, "upper");
+                       wctype_t wt;
+                        int ch;
+
+                       wt = IS_CHAR_CLASS (str);
+                       if (wt == 0)
+                         FREE_STACK_RETURN (REG_ECTYPE);
+
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);
+
+                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                        for (ch = 0; ch < 1 << BYTEWIDTH; ++ch)
+                         {
+# ifdef _LIBC
+                           if (__iswctype (__btowc (ch), wt))
+                             SET_LIST_BIT (ch);
+# else
+                           if (iswctype (btowc (ch), wt))
+                             SET_LIST_BIT (ch);
+# endif
+
+                           if (translate && (is_upper || is_lower)
+                               && (ISUPPER (ch) || ISLOWER (ch)))
+                             SET_LIST_BIT (ch);
+                         }
+
+                        had_char_class = true;
+#else
+                        int ch;
+                        boolean is_alnum = STREQ (str, "alnum");
+                        boolean is_alpha = STREQ (str, "alpha");
+                        boolean is_blank = STREQ (str, "blank");
+                        boolean is_cntrl = STREQ (str, "cntrl");
+                        boolean is_digit = STREQ (str, "digit");
+                        boolean is_graph = STREQ (str, "graph");
+                        boolean is_lower = STREQ (str, "lower");
+                        boolean is_print = STREQ (str, "print");
+                        boolean is_punct = STREQ (str, "punct");
+                        boolean is_space = STREQ (str, "space");
+                        boolean is_upper = STREQ (str, "upper");
+                        boolean is_xdigit = STREQ (str, "xdigit");
+
+                        if (!IS_CHAR_CLASS (str))
+                         FREE_STACK_RETURN (REG_ECTYPE);
+
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);
+
+                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                        for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+                          {
+                           /* This was split into 3 if's to
+                              avoid an arbitrary limit in some compiler.  */
+                            if (   (is_alnum  && ISALNUM (ch))
+                                || (is_alpha  && ISALPHA (ch))
+                                || (is_blank  && ISBLANK (ch))
+                                || (is_cntrl  && ISCNTRL (ch)))
+                             SET_LIST_BIT (ch);
+                           if (   (is_digit  && ISDIGIT (ch))
+                                || (is_graph  && ISGRAPH (ch))
+                                || (is_lower  && ISLOWER (ch))
+                                || (is_print  && ISPRINT (ch)))
+                             SET_LIST_BIT (ch);
+                           if (   (is_punct  && ISPUNCT (ch))
+                                || (is_space  && ISSPACE (ch))
+                                || (is_upper  && ISUPPER (ch))
+                                || (is_xdigit && ISXDIGIT (ch)))
+                             SET_LIST_BIT (ch);
+                           if (   translate && (is_upper || is_lower)
+                               && (ISUPPER (ch) || ISLOWER (ch)))
+                             SET_LIST_BIT (ch);
+                          }
+                        had_char_class = true;
+#endif /* libc || wctype.h */
+                      }
+                    else
+                      {
+                        c1++;
+                        while (c1--)
+                          PATUNFETCH;
+                        SET_LIST_BIT ('[');
+                        SET_LIST_BIT (':');
+                        had_char_class = false;
+                      }
+                  }
+                else
+                  {
+                    had_char_class = false;
+                    SET_LIST_BIT (c);
+                  }
+              }
+
+            /* Discard any (non)matching list bytes that are all 0 at the
+               end of the map.  Decrease the map-length byte too.  */
+            while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+              b[-1]--;
+            b += b[-1];
+          }
+          break;
+
+
+       case '(':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_open;
+          else
+            goto normal_char;
+
+
+        case ')':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_close;
+          else
+            goto normal_char;
+
+
+        case '\n':
+          if (syntax & RE_NEWLINE_ALT)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+       case '|':
+          if (syntax & RE_NO_BK_VBAR)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+        case '{':
+           if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+             goto handle_interval;
+           else
+             goto normal_char;
+
+
+        case '\\':
+          if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+          /* Do not translate the character after the \, so that we can
+             distinguish, e.g., \B from \b, even if we normally would
+             translate, e.g., B to b.  */
+          PATFETCH_RAW (c);
+
+          switch (c)
+            {
+            case '(':
+              if (syntax & RE_NO_BK_PARENS)
+                goto normal_backslash;
+
+            handle_open:
+              bufp->re_nsub++;
+              regnum++;
+
+              if (COMPILE_STACK_FULL)
+                {
+                  RETALLOC (compile_stack.stack, compile_stack.size << 1,
+                            compile_stack_elt_t);
+                  if (compile_stack.stack == NULL) return REG_ESPACE;
+
+                  compile_stack.size <<= 1;
+                }
+
+              /* These are the values to restore when we hit end of this
+                 group.  They are all relative offsets, so that if the
+                 whole pattern moves because of realloc, they will still
+                 be valid.  */
+              COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+              COMPILE_STACK_TOP.fixup_alt_jump
+                = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+              COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+              COMPILE_STACK_TOP.regnum = regnum;
+
+              /* We will eventually replace the 0 with the number of
+                 groups inner to this one.  But do not push a
+                 start_memory for groups beyond the last one we can
+                 represent in the compiled pattern.  */
+              if (regnum <= MAX_REGNUM)
+                {
+                  COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+                  BUF_PUSH_3 (start_memory, regnum, 0);
+                }
+
+              compile_stack.avail++;
+
+              fixup_alt_jump = 0;
+              laststart = 0;
+              begalt = b;
+             /* If we've reached MAX_REGNUM groups, then this open
+                won't actually generate any code, so we'll have to
+                clear pending_exact explicitly.  */
+             pending_exact = 0;
+              break;
+
+
+            case ')':
+              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+              if (COMPILE_STACK_EMPTY)
+               {
+                 if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                   goto normal_backslash;
+                 else
+                   FREE_STACK_RETURN (REG_ERPAREN);
+               }
+
+            handle_close:
+              if (fixup_alt_jump)
+                { /* Push a dummy failure point at the end of the
+                     alternative for a possible future
+                     `pop_failure_jump' to pop.  See comments at
+                     `push_dummy_failure' in `re_match_2'.  */
+                  BUF_PUSH (push_dummy_failure);
+
+                  /* We allocated space for this jump when we assigned
+                     to `fixup_alt_jump', in the `handle_alt' case below.  */
+                  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+                }
+
+              /* See similar code for backslashed left paren above.  */
+              if (COMPILE_STACK_EMPTY)
+               {
+                 if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                   goto normal_char;
+                 else
+                   FREE_STACK_RETURN (REG_ERPAREN);
+               }
+
+              /* Since we just checked for an empty stack above, this
+                 ``can't happen''.  */
+              assert (compile_stack.avail != 0);
+              {
+                /* We don't just want to restore into `regnum', because
+                   later groups should continue to be numbered higher,
+                   as in `(ab)c(de)' -- the second group is #2.  */
+                regnum_t this_group_regnum;
+
+                compile_stack.avail--;
+                begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+                fixup_alt_jump
+                  = COMPILE_STACK_TOP.fixup_alt_jump
+                    ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+                    : 0;
+                laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+                this_group_regnum = COMPILE_STACK_TOP.regnum;
+               /* If we've reached MAX_REGNUM groups, then this open
+                  won't actually generate any code, so we'll have to
+                  clear pending_exact explicitly.  */
+               pending_exact = 0;
+
+                /* We're at the end of the group, so now we know how many
+                   groups were inside this one.  */
+                if (this_group_regnum <= MAX_REGNUM)
+                  {
+                    unsigned char *inner_group_loc
+                      = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+                    *inner_group_loc = regnum - this_group_regnum;
+                    BUF_PUSH_3 (stop_memory, this_group_regnum,
+                                regnum - this_group_regnum);
+                  }
+              }
+              break;
+
+
+            case '|':                                  /* `\|'.  */
+              if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+                goto normal_backslash;
+            handle_alt:
+              if (syntax & RE_LIMITED_OPS)
+                goto normal_char;
+
+              /* Insert before the previous alternative a jump which
+                 jumps to this alternative if the former fails.  */
+              GET_BUFFER_SPACE (3);
+              INSERT_JUMP (on_failure_jump, begalt, b + 6);
+              pending_exact = 0;
+              b += 3;
+
+              /* The alternative before this one has a jump after it
+                 which gets executed if it gets matched.  Adjust that
+                 jump so it will jump to this alternative's analogous
+                 jump (put in below, which in turn will jump to the next
+                 (if any) alternative's such jump, etc.).  The last such
+                 jump jumps to the correct final destination.  A picture:
+                          _____ _____
+                          |   | |   |
+                          |   v |   v
+                         a | b   | c
+
+                 If we are at `b', then fixup_alt_jump right now points to a
+                 three-byte space after `a'.  We'll put in the jump, set
+                 fixup_alt_jump to right after `b', and leave behind three
+                 bytes which we'll fill in when we get to after `c'.  */
+
+              if (fixup_alt_jump)
+                STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+              /* Mark and leave space for a jump after this alternative,
+                 to be filled in later either by next alternative or
+                 when know we're at the end of a series of alternatives.  */
+              fixup_alt_jump = b;
+              GET_BUFFER_SPACE (3);
+              b += 3;
+
+              laststart = 0;
+              begalt = b;
+              break;
+
+
+            case '{':
+              /* If \{ is a literal.  */
+              if (!(syntax & RE_INTERVALS)
+                     /* If we're at `\{' and it's not the open-interval
+                        operator.  */
+                  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+                  || (p - 2 == pattern  &&  p == pend))
+                goto normal_backslash;
+
+            handle_interval:
+              {
+                /* If got here, then the syntax allows intervals.  */
+
+                /* At least (most) this many matches must be made.  */
+                int lower_bound = -1, upper_bound = -1;
+
+                beg_interval = p - 1;
+
+                if (p == pend)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      FREE_STACK_RETURN (REG_EBRACE);
+                  }
+
+                GET_UNSIGNED_NUMBER (lower_bound);
+
+                if (c == ',')
+                  {
+                    GET_UNSIGNED_NUMBER (upper_bound);
+                    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+                  }
+                else
+                  /* Interval such as `{1}' => match exactly once. */
+                  upper_bound = lower_bound;
+
+                if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+                    || lower_bound > upper_bound)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      FREE_STACK_RETURN (REG_BADBR);
+                  }
+
+                if (!(syntax & RE_NO_BK_BRACES))
+                  {
+                    if (c != '\\') FREE_STACK_RETURN (REG_EBRACE);
+
+                    PATFETCH (c);
+                  }
+
+                if (c != '}')
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      FREE_STACK_RETURN (REG_BADBR);
+                  }
+
+                /* We just parsed a valid interval.  */
+
+                /* If it's invalid to have no preceding re.  */
+                if (!laststart)
+                  {
+                    if (syntax & RE_CONTEXT_INVALID_OPS)
+                      FREE_STACK_RETURN (REG_BADRPT);
+                    else if (syntax & RE_CONTEXT_INDEP_OPS)
+                      laststart = b;
+                    else
+                      goto unfetch_interval;
+                  }
+
+                /* If the upper bound is zero, don't want to succeed at
+                   all; jump from `laststart' to `b + 3', which will be
+                   the end of the buffer after we insert the jump.  */
+                 if (upper_bound == 0)
+                   {
+                     GET_BUFFER_SPACE (3);
+                     INSERT_JUMP (jump, laststart, b + 3);
+                     b += 3;
+                   }
+
+                 /* Otherwise, we have a nontrivial interval.  When
+                    we're all done, the pattern will look like:
+                      set_number_at <jump count> <upper bound>
+                      set_number_at <succeed_n count> <lower bound>
+                      succeed_n <after jump addr> <succeed_n count>
+                      <body of loop>
+                      jump_n <succeed_n addr> <jump count>
+                    (The upper bound and `jump_n' are omitted if
+                    `upper_bound' is 1, though.)  */
+                 else
+                   { /* If the upper bound is > 1, we need to insert
+                        more at the end of the loop.  */
+                     unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+                     GET_BUFFER_SPACE (nbytes);
+
+                     /* Initialize lower bound of the `succeed_n', even
+                        though it will be set during matching by its
+                        attendant `set_number_at' (inserted next),
+                        because `re_compile_fastmap' needs to know.
+                        Jump to the `jump_n' we might insert below.  */
+                     INSERT_JUMP2 (succeed_n, laststart,
+                                   b + 5 + (upper_bound > 1) * 5,
+                                   lower_bound);
+                     b += 5;
+
+                     /* Code to initialize the lower bound.  Insert
+                        before the `succeed_n'.  The `5' is the last two
+                        bytes of this `set_number_at', plus 3 bytes of
+                        the following `succeed_n'.  */
+                     insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+                     b += 5;
+
+                     if (upper_bound > 1)
+                       { /* More than one repetition is allowed, so
+                            append a backward jump to the `succeed_n'
+                            that starts this interval.
+
+                            When we've reached this during matching,
+                            we'll have matched the interval once, so
+                            jump back only `upper_bound - 1' times.  */
+                         STORE_JUMP2 (jump_n, b, laststart + 5,
+                                      upper_bound - 1);
+                         b += 5;
+
+                         /* The location we want to set is the second
+                            parameter of the `jump_n'; that is `b-2' as
+                            an absolute address.  `laststart' will be
+                            the `set_number_at' we're about to insert;
+                            `laststart+3' the number to set, the source
+                            for the relative address.  But we are
+                            inserting into the middle of the pattern --
+                            so everything is getting moved up by 5.
+                            Conclusion: (b - 2) - (laststart + 3) + 5,
+                            i.e., b - laststart.
+
+                            We insert this at the beginning of the loop
+                            so that if we fail during matching, we'll
+                            reinitialize the bounds.  */
+                         insert_op2 (set_number_at, laststart, b - laststart,
+                                     upper_bound - 1, b);
+                         b += 5;
+                       }
+                   }
+                pending_exact = 0;
+                beg_interval = NULL;
+              }
+              break;
+
+            unfetch_interval:
+              /* If an invalid interval, match the characters as literals.  */
+               assert (beg_interval);
+               p = beg_interval;
+               beg_interval = NULL;
+
+               /* normal_char and normal_backslash need `c'.  */
+               PATFETCH (c);
+
+               if (!(syntax & RE_NO_BK_BRACES))
+                 {
+                   if (p > pattern  &&  p[-1] == '\\')
+                     goto normal_backslash;
+                 }
+               goto normal_char;
+
+#ifdef emacs
+            /* There is no way to specify the before_dot and after_dot
+               operators.  rms says this is ok.  --karl  */
+            case '=':
+              BUF_PUSH (at_dot);
+              break;
+
+            case 's':
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+              break;
+
+            case 'S':
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+              break;
+#endif /* emacs */
+
+
+            case 'w':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              laststart = b;
+              BUF_PUSH (wordchar);
+              break;
+
+
+            case 'W':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              laststart = b;
+              BUF_PUSH (notwordchar);
+              break;
+
+
+            case '<':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (wordbeg);
+              break;
+
+            case '>':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (wordend);
+              break;
+
+            case 'b':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (wordbound);
+              break;
+
+            case 'B':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (notwordbound);
+              break;
+
+            case '`':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (begbuf);
+              break;
+
+            case '\'':
+             if (syntax & RE_NO_GNU_OPS)
+               goto normal_char;
+              BUF_PUSH (endbuf);
+              break;
+
+            case '1': case '2': case '3': case '4': case '5':
+            case '6': case '7': case '8': case '9':
+              if (syntax & RE_NO_BK_REFS)
+                goto normal_char;
+
+              c1 = c - '0';
+
+              if (c1 > regnum)
+                FREE_STACK_RETURN (REG_ESUBREG);
+
+              /* Can't back reference to a subexpression if inside of it.  */
+              if (group_in_compile_stack (compile_stack, (regnum_t) c1))
+                goto normal_char;
+
+              laststart = b;
+              BUF_PUSH_2 (duplicate, c1);
+              break;
+
+
+            case '+':
+            case '?':
+              if (syntax & RE_BK_PLUS_QM)
+                goto handle_plus;
+              else
+                goto normal_backslash;
+
+            default:
+            normal_backslash:
+              /* You might think it would be useful for \ to mean
+                 not to translate; but if we don't translate it
+                 it will never match anything.  */
+              c = TRANSLATE (c);
+              goto normal_char;
+            }
+          break;
+
+
+       default:
+        /* Expects the character in `c'.  */
+       normal_char:
+             /* If no exactn currently being built.  */
+          if (!pending_exact
+
+              /* If last exactn not at current position.  */
+              || pending_exact + *pending_exact + 1 != b
+
+              /* We have only one byte following the exactn for the count.  */
+             || *pending_exact == (1 << BYTEWIDTH) - 1
+
+              /* If followed by a repetition operator.  */
+              || *p == '*' || *p == '^'
+             || ((syntax & RE_BK_PLUS_QM)
+                 ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+                 : (*p == '+' || *p == '?'))
+             || ((syntax & RE_INTERVALS)
+                  && ((syntax & RE_NO_BK_BRACES)
+                     ? *p == '{'
+                      : (p[0] == '\\' && p[1] == '{'))))
+           {
+             /* Start building a new exactn.  */
+
+              laststart = b;
+
+             BUF_PUSH_2 (exactn, 0);
+             pending_exact = b - 1;
+            }
+
+         BUF_PUSH (c);
+          (*pending_exact)++;
+         break;
+        } /* switch (c) */
+    } /* while p != pend */
+
+
+  /* Through the pattern now.  */
+
+  if (fixup_alt_jump)
+    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+  if (!COMPILE_STACK_EMPTY)
+    FREE_STACK_RETURN (REG_EPAREN);
+
+  /* If we don't want backtracking, force success
+     the first time we reach the end of the compiled pattern.  */
+  if (syntax & RE_NO_POSIX_BACKTRACKING)
+    BUF_PUSH (succeed);
+
+  free (compile_stack.stack);
+
+  /* We have succeeded; set the length of the buffer.  */
+  bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+  if (debug)
+    {
+      DEBUG_PRINT1 ("\nCompiled pattern: \n");
+      print_compiled_pattern (bufp);
+    }
+#endif /* DEBUG */
+
+#ifndef MATCH_MAY_ALLOCATE
+  /* Initialize the failure stack to the largest possible stack.  This
+     isn't necessary unless we're trying to avoid calling alloca in
+     the search and match routines.  */
+  {
+    int num_regs = bufp->re_nsub + 1;
+
+    /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
+       is strictly greater than re_max_failures, the largest possible stack
+       is 2 * re_max_failures failure points.  */
+    if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS))
+      {
+       fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);
+
+# ifdef emacs
+       if (! fail_stack.stack)
+         fail_stack.stack
+           = (fail_stack_elt_t *) xmalloc (fail_stack.size
+                                           * sizeof (fail_stack_elt_t));
+       else
+         fail_stack.stack
+           = (fail_stack_elt_t *) xrealloc (fail_stack.stack,
+                                            (fail_stack.size
+                                             * sizeof (fail_stack_elt_t)));
+# else /* not emacs */
+       if (! fail_stack.stack)
+         fail_stack.stack
+           = (fail_stack_elt_t *) malloc (fail_stack.size
+                                          * sizeof (fail_stack_elt_t));
+       else
+         fail_stack.stack
+           = (fail_stack_elt_t *) realloc (fail_stack.stack,
+                                           (fail_stack.size
+                                            * sizeof (fail_stack_elt_t)));
+# endif /* not emacs */
+      }
+
+    regex_grow_registers (num_regs);
+  }
+#endif /* not MATCH_MAY_ALLOCATE */
+
+  return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'.  */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG.  */
+
+static void
+store_op1 (op, loc, arg)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg1);
+  STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+   for OP followed by two-byte integer parameter ARG.  */
+
+static void
+insert_op1 (op, loc, arg, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+    unsigned char *end;
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 3;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+
+  store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+    unsigned char *end;
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 5;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+
+  store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
+   after an alternative or a begin-subexpression.  We assume there is at
+   least one character before the ^.  */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+    const char *pattern, *p;
+    reg_syntax_t syntax;
+{
+  const char *prev = p - 2;
+  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+  return
+       /* After a subexpression?  */
+       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+       /* After an alternative?  */
+    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
+   at least one character after the $, i.e., `P < PEND'.  */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+    const char *p, *pend;
+    reg_syntax_t syntax;
+{
+  const char *next = p;
+  boolean next_backslash = *next == '\\';
+  const char *next_next = p + 1 < pend ? p + 1 : 0;
+
+  return
+       /* Before a subexpression?  */
+       (syntax & RE_NO_BK_PARENS ? *next == ')'
+        : next_backslash && next_next && *next_next == ')')
+       /* Before an alternative?  */
+    || (syntax & RE_NO_BK_VBAR ? *next == '|'
+        : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+   false if it's not.  */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+    compile_stack_type compile_stack;
+    regnum_t regnum;
+{
+  int this_element;
+
+  for (this_element = compile_stack.avail - 1;
+       this_element >= 0;
+       this_element--)
+    if (compile_stack.stack[this_element].regnum == regnum)
+      return true;
+
+  return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
+   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
+   Then we set the translation of all bits between the starting and
+   ending characters (inclusive) in the compiled pattern B.
+
+   Return an error code.
+
+   We use these short variable names so we can use the same macros as
+   `regex_compile' itself.  */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+    const char **p_ptr, *pend;
+    RE_TRANSLATE_TYPE translate;
+    reg_syntax_t syntax;
+    unsigned char *b;
+{
+  unsigned this_char;
+
+  const char *p = *p_ptr;
+  unsigned int range_start, range_end;
+
+  if (p == pend)
+    return REG_ERANGE;
+
+  /* Even though the pattern is a signed `char *', we need to fetch
+     with unsigned char *'s; if the high bit of the pattern character
+     is set, the range endpoints will be negative if we fetch using a
+     signed char *.
+
+     We also want to fetch the endpoints without translating them; the
+     appropriate translation is done in the bit-setting loop below.  */
+  /* The SVR4 compiler on the 3B2 had trouble with unsigned const char *.  */
+  range_start = ((const unsigned char *) p)[-2];
+  range_end   = ((const unsigned char *) p)[0];
+
+  /* Have to increment the pointer into the pattern string, so the
+     caller isn't still at the ending character.  */
+  (*p_ptr)++;
+
+  /* If the start is after the end, the range is empty.  */
+  if (range_start > range_end)
+    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+  /* Here we see why `this_char' has to be larger than an `unsigned
+     char' -- the range is inclusive, so if `range_end' == 0xff
+     (assuming 8-bit characters), we would otherwise go into an infinite
+     loop, since all characters <= 0xff.  */
+  for (this_char = range_start; this_char <= range_end; this_char++)
+    {
+      SET_LIST_BIT (TRANSLATE (this_char));
+    }
+
+  return REG_NOERROR;
+}
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
+   characters can start a string that matches the pattern.  This fastmap
+   is used by re_search to skip quickly over impossible starting points.
+
+   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+   area as BUFP->fastmap.
+
+   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+   the pattern buffer.
+
+   Returns 0 if we succeed, -2 if an internal error.   */
+
+int
+re_compile_fastmap (bufp)
+     struct re_pattern_buffer *bufp;
+{
+  int j, k;
+#ifdef MATCH_MAY_ALLOCATE
+  fail_stack_type fail_stack;
+#endif
+#ifndef REGEX_MALLOC
+  char *destination;
+#endif
+
+  register char *fastmap = bufp->fastmap;
+  unsigned char *pattern = bufp->buffer;
+  unsigned char *p = pattern;
+  register unsigned char *pend = pattern + bufp->used;
+
+#ifdef REL_ALLOC
+  /* This holds the pointer to the failure stack, when
+     it is allocated relocatably.  */
+  fail_stack_elt_t *failure_stack_ptr;
+#endif
+
+  /* Assume that each path through the pattern can be null until
+     proven otherwise.  We set this false at the bottom of switch
+     statement, to which we get only if a particular path doesn't
+     match the empty string.  */
+  boolean path_can_be_null = true;
+
+  /* We aren't doing a `succeed_n' to begin with.  */
+  boolean succeed_n_p = false;
+
+  assert (fastmap != NULL && p != NULL);
+
+  INIT_FAIL_STACK ();
+  memset (fastmap, 0, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
+  bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
+  bufp->can_be_null = 0;
+
+  while (1)
+    {
+      if (p == pend || *p == succeed)
+       {
+         /* We have reached the (effective) end of pattern.  */
+         if (!FAIL_STACK_EMPTY ())
+           {
+             bufp->can_be_null |= path_can_be_null;
+
+             /* Reset for next path.  */
+             path_can_be_null = true;
+
+             p = fail_stack.stack[--fail_stack.avail].pointer;
+
+             continue;
+           }
+         else
+           break;
+       }
+
+      /* We should never be about to go beyond the end of the pattern.  */
+      assert (p < pend);
+
+      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+       {
+
+        /* I guess the idea here is to simply not bother with a fastmap
+           if a backreference is used, since it's too hard to figure out
+           the fastmap for the corresponding group.  Setting
+           `can_be_null' stops `re_search_2' from using the fastmap, so
+           that is all we do.  */
+       case duplicate:
+         bufp->can_be_null = 1;
+          goto done;
+
+
+      /* Following are the cases which match a character.  These end
+         with `break'.  */
+
+       case exactn:
+          fastmap[p[1]] = 1;
+         break;
+
+
+        case charset:
+          for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+              fastmap[j] = 1;
+         break;
+
+
+       case charset_not:
+         /* Chars beyond end of map must be allowed.  */
+         for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+         for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+              fastmap[j] = 1;
+          break;
+
+
+       case wordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == Sword)
+             fastmap[j] = 1;
+         break;
+
+
+       case notwordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != Sword)
+             fastmap[j] = 1;
+         break;
+
+
+        case anychar:
+         {
+           int fastmap_newline = fastmap['\n'];
+
+           /* `.' matches anything ...  */
+           for (j = 0; j < (1 << BYTEWIDTH); j++)
+             fastmap[j] = 1;
+
+           /* ... except perhaps newline.  */
+           if (!(bufp->syntax & RE_DOT_NEWLINE))
+             fastmap['\n'] = fastmap_newline;
+
+           /* Return if we have already set `can_be_null'; if we have,
+              then the fastmap is irrelevant.  Something's wrong here.  */
+           else if (bufp->can_be_null)
+             goto done;
+
+           /* Otherwise, have to check alternative paths.  */
+           break;
+         }
+
+#ifdef emacs
+        case syntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+       case notsyntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+      /* All cases after this match the empty string.  These end with
+         `continue'.  */
+
+
+       case before_dot:
+       case at_dot:
+       case after_dot:
+          continue;
+#endif /* emacs */
+
+
+        case no_op:
+        case begline:
+        case endline:
+       case begbuf:
+       case endbuf:
+       case wordbound:
+       case notwordbound:
+       case wordbeg:
+       case wordend:
+        case push_dummy_failure:
+          continue;
+
+
+       case jump_n:
+        case pop_failure_jump:
+       case maybe_pop_jump:
+       case jump:
+        case jump_past_alt:
+       case dummy_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+         p += j;
+         if (j > 0)
+           continue;
+
+          /* Jump backward implies we just went through the body of a
+             loop and matched nothing.  Opcode jumped to should be
+             `on_failure_jump' or `succeed_n'.  Just treat it like an
+             ordinary jump.  For a * loop, it has pushed its failure
+             point already; if so, discard that as redundant.  */
+          if ((re_opcode_t) *p != on_failure_jump
+             && (re_opcode_t) *p != succeed_n)
+           continue;
+
+          p++;
+          EXTRACT_NUMBER_AND_INCR (j, p);
+          p += j;
+
+          /* If what's on the stack is where we are now, pop it.  */
+          if (!FAIL_STACK_EMPTY ()
+             && fail_stack.stack[fail_stack.avail - 1].pointer == p)
+            fail_stack.avail--;
+
+          continue;
+
+
+        case on_failure_jump:
+        case on_failure_keep_string_jump:
+       handle_on_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+
+          /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+             end of the pattern.  We don't want to push such a point,
+             since when we restore it above, entering the switch will
+             increment `p' past the end of the pattern.  We don't need
+             to push such a point since we obviously won't find any more
+             fastmap entries beyond `pend'.  Such a pattern can match
+             the null string, though.  */
+          if (p + j < pend)
+            {
+              if (!PUSH_PATTERN_OP (p + j, fail_stack))
+               {
+                 RESET_FAIL_STACK ();
+                 return -2;
+               }
+            }
+          else
+            bufp->can_be_null = 1;
+
+          if (succeed_n_p)
+            {
+              EXTRACT_NUMBER_AND_INCR (k, p);  /* Skip the n.  */
+              succeed_n_p = false;
+           }
+
+          continue;
+
+
+       case succeed_n:
+          /* Get to the number of times to succeed.  */
+          p += 2;
+
+          /* Increment p past the n for when k != 0.  */
+          EXTRACT_NUMBER_AND_INCR (k, p);
+          if (k == 0)
+           {
+              p -= 4;
+             succeed_n_p = true;  /* Spaghetti code alert.  */
+              goto handle_on_failure_jump;
+            }
+          continue;
+
+
+       case set_number_at:
+          p += 4;
+          continue;
+
+
+       case start_memory:
+        case stop_memory:
+         p += 2;
+         continue;
+
+
+       default:
+          abort (); /* We have listed all the cases.  */
+        } /* switch *p++ */
+
+      /* Getting here means we have found the possible starting
+         characters for one path of the pattern -- and that the empty
+         string does not match.  We need not follow this path further.
+         Instead, look at the next alternative (remembered on the
+         stack), or quit if no more.  The test at the top of the loop
+         does these things.  */
+      path_can_be_null = false;
+      p = pend;
+    } /* while p */
+
+  /* Set `can_be_null' for the last path (also the first path, if the
+     pattern is empty).  */
+  bufp->can_be_null |= path_can_be_null;
+
+ done:
+  RESET_FAIL_STACK ();
+  return 0;
+} /* re_compile_fastmap */
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Searching routines.  */
+
+/* Like re_search_2, below, but only one string is specified, and
+   doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, startpos, range;
+     struct re_registers *regs;
+{
+  return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+                     regs, size);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+   virtual concatenation of STRING1 and STRING2, starting first at index
+   STARTPOS, then at STARTPOS + 1, and so on.
+
+   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+   RANGE is how far to scan while trying to match.  RANGE = 0 means try
+   only at STARTPOS; in general, the last start tried is STARTPOS +
+   RANGE.
+
+   In REGS, return the indices of the virtual concatenation of STRING1
+   and STRING2 that matched the entire BUFP->buffer and its contained
+   subexpressions.
+
+   Do not consider matching one past the index STOP in the virtual
+   concatenation of STRING1 and STRING2.
+
+   We return either the position in the strings at which the match was
+   found, -1 if no match, or -2 if error (such as failure
+   stack overflow).  */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+{
+  int val;
+  register char *fastmap = bufp->fastmap;
+  register RE_TRANSLATE_TYPE translate = bufp->translate;
+  int total_size = size1 + size2;
+  int endpos = startpos + range;
+
+  /* Check for out-of-range STARTPOS.  */
+  if (startpos < 0 || startpos > total_size)
+    return -1;
+
+  /* Fix up RANGE if it might eventually take us outside
+     the virtual concatenation of STRING1 and STRING2.
+     Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE.  */
+  if (endpos < 0)
+    range = 0 - startpos;
+  else if (endpos > total_size)
+    range = total_size - startpos;
+
+  /* If the search isn't to be a backwards one, don't waste time in a
+     search for a pattern that must be anchored.  */
+  if (bufp->used > 0 && range > 0
+      && ((re_opcode_t) bufp->buffer[0] == begbuf
+         /* `begline' is like `begbuf' if it cannot match at newlines.  */
+         || ((re_opcode_t) bufp->buffer[0] == begline
+             && !bufp->newline_anchor)))
+    {
+      if (startpos > 0)
+       return -1;
+      else
+       range = 1;
+    }
+
+#ifdef emacs
+  /* In a forward search for something that starts with \=.
+     don't keep searching past point.  */
+  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0)
+    {
+      range = PT - startpos;
+      if (range <= 0)
+       return -1;
+    }
+#endif /* emacs */
+
+  /* Update the fastmap now if not correct already.  */
+  if (fastmap && !bufp->fastmap_accurate)
+    if (re_compile_fastmap (bufp) == -2)
+      return -2;
+
+  /* Loop through the string, looking for a place to start matching.  */
+  for (;;)
+    {
+      /* If a fastmap is supplied, skip quickly over characters that
+         cannot be the start of a match.  If the pattern can match the
+         null string, however, we don't need to skip characters; we want
+         the first null string.  */
+      if (fastmap && startpos < total_size && !bufp->can_be_null)
+       {
+         if (range > 0)        /* Searching forwards.  */
+           {
+             register const char *d;
+             register int lim = 0;
+             int irange = range;
+
+              if (startpos < size1 && startpos + range >= size1)
+                lim = range - (size1 - startpos);
+
+             d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+              /* Written out as an if-else to avoid testing `translate'
+                 inside the loop.  */
+             if (translate)
+                while (range > lim
+                       && !fastmap[(unsigned char)
+                                  translate[(unsigned char) *d++]])
+                  range--;
+             else
+                while (range > lim && !fastmap[(unsigned char) *d++])
+                  range--;
+
+             startpos += irange - range;
+           }
+         else                          /* Searching backwards.  */
+           {
+             register char c = (size1 == 0 || startpos >= size1
+                                 ? string2[startpos - size1]
+                                 : string1[startpos]);
+
+             if (!fastmap[(unsigned char) TRANSLATE (c)])
+               goto advance;
+           }
+       }
+
+      /* If can't match the null string, and that's all we have left, fail.  */
+      if (range >= 0 && startpos == total_size && fastmap
+          && !bufp->can_be_null)
+       return -1;
+
+      val = re_match_2_internal (bufp, string1, size1, string2, size2,
+                                startpos, regs, stop);
+#ifndef REGEX_MALLOC
+# ifdef C_ALLOCA
+      alloca (0);
+# endif
+#endif
+
+      if (val >= 0)
+       return startpos;
+
+      if (val == -2)
+       return -2;
+
+    advance:
+      if (!range)
+        break;
+      else if (range > 0)
+        {
+          range--;
+          startpos++;
+        }
+      else
+        {
+          range++;
+          startpos--;
+        }
+    }
+  return -1;
+} /* re_search_2 */
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+   and `string2' into an offset from the beginning of that string.  */
+#define POINTER_TO_OFFSET(ptr)                 \
+  (FIRST_STRING_P (ptr)                                \
+   ? ((regoff_t) ((ptr) - string1))            \
+   : ((regoff_t) ((ptr) - string2 + size1)))
+
+/* Macros for dealing with the split strings in re_match_2.  */
+
+#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
+
+/* Call before fetching a character with *d.  This switches over to
+   string2 if necessary.  */
+#define PREFETCH()                                                     \
+  while (d == dend)                                                    \
+    {                                                                  \
+      /* End of string2 => fail.  */                                   \
+      if (dend == end_match_2)                                                 \
+        goto fail;                                                     \
+      /* End of string1 => advance to string2.  */                     \
+      d = string2;                                                     \
+      dend = end_match_2;                                              \
+    }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+   of `string1' and `string2'.  If only one string, it's `string2'.  */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent.  We have
+   two special cases to check for: if past the end of string1, look at
+   the first character in string2; and if before the beginning of
+   string2, look at the last character in string1.  */
+#define WORDCHAR_P(d)                                                  \
+  (SYNTAX ((d) == end1 ? *string2                                      \
+           : (d) == string2 - 1 ? *(end1 - 1) : *(d))                  \
+   == Sword)
+
+/* Disabled due to a compiler bug -- see comment at case wordbound */
+#if 0
+/* Test if the character before D and the one at D differ with respect
+   to being word-constituent.  */
+#define AT_WORD_BOUNDARY(d)                                            \
+  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)                            \
+   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+#endif
+
+/* Free everything we malloc.  */
+#ifdef MATCH_MAY_ALLOCATE
+# define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL
+# define FREE_VARIABLES()                                              \
+  do {                                                                 \
+    REGEX_FREE_STACK (fail_stack.stack);                               \
+    FREE_VAR (regstart);                                               \
+    FREE_VAR (regend);                                                 \
+    FREE_VAR (old_regstart);                                           \
+    FREE_VAR (old_regend);                                             \
+    FREE_VAR (best_regstart);                                          \
+    FREE_VAR (best_regend);                                            \
+    FREE_VAR (reg_info);                                               \
+    FREE_VAR (reg_dummy);                                              \
+    FREE_VAR (reg_info_dummy);                                         \
+  } while (0)
+#else
+# define FREE_VARIABLES() ((void)0) /* Do nothing!  But inhibit gcc warning. */
+#endif /* not MATCH_MAY_ALLOCATE */
+
+/* These values must meet several constraints.  They must not be valid
+   register values; since we have a limit of 255 registers (because
+   we use only one byte in the pattern for the register number), we can
+   use numbers larger than 255.  They must differ by 1, because of
+   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
+   be larger than the value for the highest register, so we do not try
+   to actually save any registers when none are active.  */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines.  */
+
+#ifndef emacs   /* Emacs never uses this.  */
+/* re_match is like re_match_2 except it takes only a single string.  */
+
+int
+re_match (bufp, string, size, pos, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, pos;
+     struct re_registers *regs;
+{
+  int result = re_match_2_internal (bufp, NULL, 0, string, size,
+                                   pos, regs, size);
+# ifndef REGEX_MALLOC
+#  ifdef C_ALLOCA
+  alloca (0);
+#  endif
+# endif
+  return result;
+}
+# ifdef _LIBC
+weak_alias (__re_match, re_match)
+# endif
+#endif /* not emacs */
+
+static boolean group_match_null_string_p _RE_ARGS ((unsigned char **p,
+                                                   unsigned char *end,
+                                               register_info_type *reg_info));
+static boolean alt_match_null_string_p _RE_ARGS ((unsigned char *p,
+                                                 unsigned char *end,
+                                               register_info_type *reg_info));
+static boolean common_op_match_null_string_p _RE_ARGS ((unsigned char **p,
+                                                       unsigned char *end,
+                                               register_info_type *reg_info));
+static int bcmp_translate _RE_ARGS ((const char *s1, const char *s2,
+                                    int len, char *translate));
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+   and SIZE2, respectively).  We start matching at POS, and stop
+   matching at STOP.
+
+   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+   store offsets for the substring each group matched in REGS.  See the
+   documentation for exactly how many groups we fill.
+
+   We return -1 if no match, -2 if an internal error (such as the
+   failure stack overflowing).  Otherwise, we return the length of the
+   matched substring.  */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  int result = re_match_2_internal (bufp, string1, size1, string2, size2,
+                                   pos, regs, stop);
+#ifndef REGEX_MALLOC
+# ifdef C_ALLOCA
+  alloca (0);
+# endif
+#endif
+  return result;
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+/* This is a separate function so that we can force an alloca cleanup
+   afterwards.  */
+static int
+re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  /* General temporaries.  */
+  int mcnt;
+  unsigned char *p1;
+
+  /* Just past the end of the corresponding string.  */
+  const char *end1, *end2;
+
+  /* Pointers into string1 and string2, just past the last characters in
+     each to consider matching.  */
+  const char *end_match_1, *end_match_2;
+
+  /* Where we are in the data, and the end of the current string.  */
+  const char *d, *dend;
+
+  /* Where we are in the pattern, and the end of the pattern.  */
+  unsigned char *p = bufp->buffer;
+  register unsigned char *pend = p + bufp->used;
+
+  /* Mark the opcode just after a start_memory, so we can test for an
+     empty subpattern when we get to the stop_memory.  */
+  unsigned char *just_past_start_mem = 0;
+
+  /* We use this to map every character in the string.  */
+  RE_TRANSLATE_TYPE translate = bufp->translate;
+
+  /* Failure point stack.  Each place that can handle a failure further
+     down the line pushes a failure point on this stack.  It consists of
+     restart, regend, and reg_info for all registers corresponding to
+     the subexpressions we're currently inside, plus the number of such
+     registers, and, finally, two char *'s.  The first char * is where
+     to resume scanning the pattern; the second one is where to resume
+     scanning the strings.  If the latter is zero, the failure point is
+     a ``dummy''; if a failure happens and the failure point is a dummy,
+     it gets discarded and the next next one is tried.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  fail_stack_type fail_stack;
+#endif
+#ifdef DEBUG
+  static unsigned failure_id;
+  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+#ifdef REL_ALLOC
+  /* This holds the pointer to the failure stack, when
+     it is allocated relocatably.  */
+  fail_stack_elt_t *failure_stack_ptr;
+#endif
+
+  /* We fill all the registers internally, independent of what we
+     return, for use in backreferences.  The number here includes
+     an element for register zero.  */
+  size_t num_regs = bufp->re_nsub + 1;
+
+  /* The currently active registers.  */
+  active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+  active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+  /* Information on the contents of registers. These are pointers into
+     the input strings; they record just what was matched (on this
+     attempt) by a subexpression part of the pattern, that is, the
+     regnum-th regstart pointer points to where in the pattern we began
+     matching and the regnum-th regend points to right after where we
+     stopped matching the regnum-th subexpression.  (The zeroth register
+     keeps track of what the whole pattern matches.)  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **regstart, **regend;
+#endif
+
+  /* If a group that's operated upon by a repetition operator fails to
+     match anything, then the register for its start will need to be
+     restored because it will have been set to wherever in the string we
+     are when we last see its open-group operator.  Similarly for a
+     register's end.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **old_regstart, **old_regend;
+#endif
+
+  /* The is_active field of reg_info helps us keep track of which (possibly
+     nested) subexpressions we are currently in. The matched_something
+     field of reg_info[reg_num] helps us tell whether or not we have
+     matched any of the pattern so far this time through the reg_num-th
+     subexpression.  These two fields get reset each time through any
+     loop their register is in.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  register_info_type *reg_info;
+#endif
+
+  /* The following record the register info as found in the above
+     variables when we find a match better than any we've seen before.
+     This happens as we backtrack through the failure points, which in
+     turn happens only if we have not yet matched the entire string. */
+  unsigned best_regs_set = false;
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **best_regstart, **best_regend;
+#endif
+
+  /* Logically, this is `best_regend[0]'.  But we don't want to have to
+     allocate space for that if we're not allocating space for anything
+     else (see below).  Also, we never need info about register 0 for
+     any of the other register vectors, and it seems rather a kludge to
+     treat `best_regend' differently than the rest.  So we keep track of
+     the end of the best match so far in a separate variable.  We
+     initialize this to NULL so that when we backtrack the first time
+     and need to test it, it's not garbage.  */
+  const char *match_end = NULL;
+
+  /* This helps SET_REGS_MATCHED avoid doing redundant work.  */
+  int set_regs_matched_done = 0;
+
+  /* Used when we pop values we don't care about.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **reg_dummy;
+  register_info_type *reg_info_dummy;
+#endif
+
+#ifdef DEBUG
+  /* Counts the total number of registers pushed.  */
+  unsigned num_regs_pushed = 0;
+#endif
+
+  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+  INIT_FAIL_STACK ();
+
+#ifdef MATCH_MAY_ALLOCATE
+  /* Do not bother to initialize all the register variables if there are
+     no groups in the pattern, as it takes a fair amount of time.  If
+     there are groups, we include space for register 0 (the whole
+     pattern), even though we never use it, since it simplifies the
+     array indexing.  We should fix this.  */
+  if (bufp->re_nsub)
+    {
+      regstart = REGEX_TALLOC (num_regs, const char *);
+      regend = REGEX_TALLOC (num_regs, const char *);
+      old_regstart = REGEX_TALLOC (num_regs, const char *);
+      old_regend = REGEX_TALLOC (num_regs, const char *);
+      best_regstart = REGEX_TALLOC (num_regs, const char *);
+      best_regend = REGEX_TALLOC (num_regs, const char *);
+      reg_info = REGEX_TALLOC (num_regs, register_info_type);
+      reg_dummy = REGEX_TALLOC (num_regs, const char *);
+      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+      if (!(regstart && regend && old_regstart && old_regend && reg_info
+            && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+        {
+          FREE_VARIABLES ();
+          return -2;
+        }
+    }
+  else
+    {
+      /* We must initialize all our variables to NULL, so that
+         `FREE_VARIABLES' doesn't try to free them.  */
+      regstart = regend = old_regstart = old_regend = best_regstart
+        = best_regend = reg_dummy = NULL;
+      reg_info = reg_info_dummy = (register_info_type *) NULL;
+    }
+#endif /* MATCH_MAY_ALLOCATE */
+
+  /* The starting position is bogus.  */
+  if (pos < 0 || pos > size1 + size2)
+    {
+      FREE_VARIABLES ();
+      return -1;
+    }
+
+  /* Initialize subexpression text positions to -1 to mark ones that no
+     start_memory/stop_memory has been seen for. Also initialize the
+     register information struct.  */
+  for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+    {
+      regstart[mcnt] = regend[mcnt]
+        = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+      IS_ACTIVE (reg_info[mcnt]) = 0;
+      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+    }
+
+  /* We move `string1' into `string2' if the latter's empty -- but not if
+     `string1' is null.  */
+  if (size2 == 0 && string1 != NULL)
+    {
+      string2 = string1;
+      size2 = size1;
+      string1 = 0;
+      size1 = 0;
+    }
+  end1 = string1 + size1;
+  end2 = string2 + size2;
+
+  /* Compute where to stop matching, within the two strings.  */
+  if (stop <= size1)
+    {
+      end_match_1 = string1 + stop;
+      end_match_2 = string2;
+    }
+  else
+    {
+      end_match_1 = end1;
+      end_match_2 = string2 + stop - size1;
+    }
+
+  /* `p' scans through the pattern as `d' scans through the data.
+     `dend' is the end of the input string that `d' points within.  `d'
+     is advanced into the following input string whenever necessary, but
+     this happens before fetching; therefore, at the beginning of the
+     loop, `d' can be pointing at the end of a string, but it cannot
+     equal `string2'.  */
+  if (size1 > 0 && pos <= size1)
+    {
+      d = string1 + pos;
+      dend = end_match_1;
+    }
+  else
+    {
+      d = string2 + pos - size1;
+      dend = end_match_2;
+    }
+
+  DEBUG_PRINT1 ("The compiled pattern is:\n");
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+  DEBUG_PRINT1 ("The string to match is: `");
+  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+  DEBUG_PRINT1 ("'\n");
+
+  /* This loops over pattern commands.  It exits by returning from the
+     function if the match is complete, or it drops through if the match
+     fails at this starting point in the input data.  */
+  for (;;)
+    {
+#ifdef _LIBC
+      DEBUG_PRINT2 ("\n%p: ", p);
+#else
+      DEBUG_PRINT2 ("\n0x%x: ", p);
+#endif
+
+      if (p == pend)
+       { /* End of pattern means we might have succeeded.  */
+          DEBUG_PRINT1 ("end of pattern ... ");
+
+         /* If we haven't matched the entire string, and we want the
+             longest match, try backtracking.  */
+          if (d != end_match_2)
+           {
+             /* 1 if this match ends in the same string (string1 or string2)
+                as the best previous match.  */
+             boolean same_str_p = (FIRST_STRING_P (match_end)
+                                   == MATCHING_IN_FIRST_STRING);
+             /* 1 if this match is the best seen so far.  */
+             boolean best_match_p;
+
+             /* AIX compiler got confused when this was combined
+                with the previous declaration.  */
+             if (same_str_p)
+               best_match_p = d > match_end;
+             else
+               best_match_p = !MATCHING_IN_FIRST_STRING;
+
+              DEBUG_PRINT1 ("backtracking.\n");
+
+              if (!FAIL_STACK_EMPTY ())
+                { /* More failure points to try.  */
+
+                  /* If exceeds best match so far, save it.  */
+                  if (!best_regs_set || best_match_p)
+                    {
+                      best_regs_set = true;
+                      match_end = d;
+
+                      DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+                      for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+                        {
+                          best_regstart[mcnt] = regstart[mcnt];
+                          best_regend[mcnt] = regend[mcnt];
+                        }
+                    }
+                  goto fail;
+                }
+
+              /* If no failure points, don't restore garbage.  And if
+                 last match is real best match, don't restore second
+                 best one. */
+              else if (best_regs_set && !best_match_p)
+                {
+               restore_best_regs:
+                  /* Restore best match.  It may happen that `dend ==
+                     end_match_1' while the restored d is in string2.
+                     For example, the pattern `x.*y.*z' against the
+                     strings `x-' and `y-z-', if the two strings are
+                     not consecutive in memory.  */
+                  DEBUG_PRINT1 ("Restoring best registers.\n");
+
+                  d = match_end;
+                  dend = ((d >= string1 && d <= end1)
+                          ? end_match_1 : end_match_2);
+
+                 for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
+                   {
+                     regstart[mcnt] = best_regstart[mcnt];
+                     regend[mcnt] = best_regend[mcnt];
+                   }
+                }
+            } /* d != end_match_2 */
+
+       succeed_label:
+          DEBUG_PRINT1 ("Accepting match.\n");
+
+          /* If caller wants register contents data back, do it.  */
+          if (regs && !bufp->no_sub)
+           {
+              /* Have the register data arrays been allocated?  */
+              if (bufp->regs_allocated == REGS_UNALLOCATED)
+                { /* No.  So allocate them with malloc.  We need one
+                     extra element beyond `num_regs' for the `-1' marker
+                     GNU code uses.  */
+                  regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+                  regs->start = TALLOC (regs->num_regs, regoff_t);
+                  regs->end = TALLOC (regs->num_regs, regoff_t);
+                  if (regs->start == NULL || regs->end == NULL)
+                   {
+                     FREE_VARIABLES ();
+                     return -2;
+                   }
+                  bufp->regs_allocated = REGS_REALLOCATE;
+                }
+              else if (bufp->regs_allocated == REGS_REALLOCATE)
+                { /* Yes.  If we need more elements than were already
+                     allocated, reallocate them.  If we need fewer, just
+                     leave it alone.  */
+                  if (regs->num_regs < num_regs + 1)
+                    {
+                      regs->num_regs = num_regs + 1;
+                      RETALLOC (regs->start, regs->num_regs, regoff_t);
+                      RETALLOC (regs->end, regs->num_regs, regoff_t);
+                      if (regs->start == NULL || regs->end == NULL)
+                       {
+                         FREE_VARIABLES ();
+                         return -2;
+                       }
+                    }
+                }
+              else
+               {
+                 /* These braces fend off a "empty body in an else-statement"
+                    warning under GCC when assert expands to nothing.  */
+                 assert (bufp->regs_allocated == REGS_FIXED);
+               }
+
+              /* Convert the pointer data in `regstart' and `regend' to
+                 indices.  Register zero has to be set differently,
+                 since we haven't kept track of any info for it.  */
+              if (regs->num_regs > 0)
+                {
+                  regs->start[0] = pos;
+                  regs->end[0] = (MATCHING_IN_FIRST_STRING
+                                 ? ((regoff_t) (d - string1))
+                                 : ((regoff_t) (d - string2 + size1)));
+                }
+
+              /* Go through the first `min (num_regs, regs->num_regs)'
+                 registers, since that is all we initialized.  */
+             for (mcnt = 1; (unsigned) mcnt < MIN (num_regs, regs->num_regs);
+                  mcnt++)
+               {
+                  if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+                    regs->start[mcnt] = regs->end[mcnt] = -1;
+                  else
+                    {
+                     regs->start[mcnt]
+                       = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]);
+                      regs->end[mcnt]
+                       = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]);
+                    }
+               }
+
+              /* If the regs structure we return has more elements than
+                 were in the pattern, set the extra elements to -1.  If
+                 we (re)allocated the registers, this is the case,
+                 because we always allocate enough to have at least one
+                 -1 at the end.  */
+              for (mcnt = num_regs; (unsigned) mcnt < regs->num_regs; mcnt++)
+                regs->start[mcnt] = regs->end[mcnt] = -1;
+           } /* regs && !bufp->no_sub */
+
+          DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+                        nfailure_points_pushed, nfailure_points_popped,
+                        nfailure_points_pushed - nfailure_points_popped);
+          DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+          mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+                           ? string1
+                           : string2 - size1);
+
+          DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+          FREE_VARIABLES ();
+          return mcnt;
+        }
+
+      /* Otherwise match next pattern command.  */
+      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+       {
+        /* Ignore these.  Used to ignore the n of succeed_n's which
+           currently have n == 0.  */
+        case no_op:
+          DEBUG_PRINT1 ("EXECUTING no_op.\n");
+          break;
+
+       case succeed:
+          DEBUG_PRINT1 ("EXECUTING succeed.\n");
+         goto succeed_label;
+
+        /* Match the next n pattern characters exactly.  The following
+           byte in the pattern defines n, and the n bytes after that
+           are the characters to match.  */
+       case exactn:
+         mcnt = *p++;
+          DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+          /* This is written out as an if-else so we don't waste time
+             testing `translate' inside the loop.  */
+          if (translate)
+           {
+             do
+               {
+                 PREFETCH ();
+                 if ((unsigned char) translate[(unsigned char) *d++]
+                     != (unsigned char) *p++)
+                    goto fail;
+               }
+             while (--mcnt);
+           }
+         else
+           {
+             do
+               {
+                 PREFETCH ();
+                 if (*d++ != (char) *p++) goto fail;
+               }
+             while (--mcnt);
+           }
+         SET_REGS_MATCHED ();
+          break;
+
+
+        /* Match any character except possibly a newline or a null.  */
+       case anychar:
+          DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+          PREFETCH ();
+
+          if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+              || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+           goto fail;
+
+          SET_REGS_MATCHED ();
+          DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
+          d++;
+         break;
+
+
+       case charset:
+       case charset_not:
+         {
+           register unsigned char c;
+           boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+            DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+           PREFETCH ();
+           c = TRANSLATE (*d); /* The character to match.  */
+
+            /* Cast to `unsigned' instead of `unsigned char' in case the
+               bit list is a full 32 bytes long.  */
+           if (c < (unsigned) (*p * BYTEWIDTH)
+               && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+             not = !not;
+
+           p += 1 + *p;
+
+           if (!not) goto fail;
+
+           SET_REGS_MATCHED ();
+            d++;
+           break;
+         }
+
+
+        /* The beginning of a group is represented by start_memory.
+           The arguments are the register number in the next byte, and the
+           number of groups inner to this one in the next.  The text
+           matched within the group is recorded (in the internal
+           registers data structure) under the register number.  */
+        case start_memory:
+         DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+          /* Find out if this group can match the empty string.  */
+         p1 = p;               /* To send to group_match_null_string_p.  */
+
+          if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+            REG_MATCH_NULL_STRING_P (reg_info[*p])
+              = group_match_null_string_p (&p1, pend, reg_info);
+
+          /* Save the position in the string where we were the last time
+             we were at this open-group operator in case the group is
+             operated upon by a repetition operator, e.g., with `(a*)*b'
+             against `ab'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                             ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+                             : regstart[*p];
+         DEBUG_PRINT2 ("  old_regstart: %d\n",
+                        POINTER_TO_OFFSET (old_regstart[*p]));
+
+          regstart[*p] = d;
+         DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+          IS_ACTIVE (reg_info[*p]) = 1;
+          MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+         /* Clear this whenever we change the register activity status.  */
+         set_regs_matched_done = 0;
+
+          /* This is the new highest active register.  */
+          highest_active_reg = *p;
+
+          /* If nothing was active before, this is the new lowest active
+             register.  */
+          if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+            lowest_active_reg = *p;
+
+          /* Move past the register number and inner group count.  */
+          p += 2;
+         just_past_start_mem = p;
+
+          break;
+
+
+        /* The stop_memory opcode represents the end of a group.  Its
+           arguments are the same as start_memory's: the register
+           number, and the number of inner groups.  */
+       case stop_memory:
+         DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+          /* We need to save the string position the last time we were at
+             this close-group operator in case the group is operated
+             upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+             against `aba'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                           ? REG_UNSET (regend[*p]) ? d : regend[*p]
+                          : regend[*p];
+         DEBUG_PRINT2 ("      old_regend: %d\n",
+                        POINTER_TO_OFFSET (old_regend[*p]));
+
+          regend[*p] = d;
+         DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+          /* This register isn't active anymore.  */
+          IS_ACTIVE (reg_info[*p]) = 0;
+
+         /* Clear this whenever we change the register activity status.  */
+         set_regs_matched_done = 0;
+
+          /* If this was the only register active, nothing is active
+             anymore.  */
+          if (lowest_active_reg == highest_active_reg)
+            {
+              lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+              highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+            }
+          else
+            { /* We must scan for the new highest active register, since
+                 it isn't necessarily one less than now: consider
+                 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
+                 new highest active register is 1.  */
+              unsigned char r = *p - 1;
+              while (r > 0 && !IS_ACTIVE (reg_info[r]))
+                r--;
+
+              /* If we end up at register zero, that means that we saved
+                 the registers as the result of an `on_failure_jump', not
+                 a `start_memory', and we jumped to past the innermost
+                 `stop_memory'.  For example, in ((.)*) we save
+                 registers 1 and 2 as a result of the *, but when we pop
+                 back to the second ), we are at the stop_memory 1.
+                 Thus, nothing is active.  */
+             if (r == 0)
+                {
+                  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+                  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+                }
+              else
+                highest_active_reg = r;
+            }
+
+          /* If just failed to match something this time around with a
+             group that's operated on by a repetition operator, try to
+             force exit from the ``loop'', and restore the register
+             information for this group that we had before trying this
+             last match.  */
+          if ((!MATCHED_SOMETHING (reg_info[*p])
+               || just_past_start_mem == p - 1)
+             && (p + 2) < pend)
+            {
+              boolean is_a_jump_n = false;
+
+              p1 = p + 2;
+              mcnt = 0;
+              switch ((re_opcode_t) *p1++)
+                {
+                  case jump_n:
+                   is_a_jump_n = true;
+                  case pop_failure_jump:
+                 case maybe_pop_jump:
+                 case jump:
+                 case dummy_failure_jump:
+                    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                   if (is_a_jump_n)
+                     p1 += 2;
+                    break;
+
+                  default:
+                    /* do nothing */ ;
+                }
+             p1 += mcnt;
+
+              /* If the next operation is a jump backwards in the pattern
+                to an on_failure_jump right before the start_memory
+                 corresponding to this stop_memory, exit from the loop
+                 by forcing a failure after pushing on the stack the
+                 on_failure_jump's jump in the pattern, and d.  */
+              if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+                  && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+               {
+                  /* If this group ever matched anything, then restore
+                     what its registers were before trying this last
+                     failed match, e.g., with `(a*)*b' against `ab' for
+                     regstart[1], and, e.g., with `((a*)*(b*)*)*'
+                     against `aba' for regend[3].
+
+                     Also restore the registers for inner groups for,
+                     e.g., `((a*)(b*))*' against `aba' (register 3 would
+                     otherwise get trashed).  */
+
+                  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+                   {
+                     unsigned r;
+
+                      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+                     /* Restore this and inner groups' (if any) registers.  */
+                      for (r = *p; r < (unsigned) *p + (unsigned) *(p + 1);
+                          r++)
+                        {
+                          regstart[r] = old_regstart[r];
+
+                          /* xx why this test?  */
+                          if (old_regend[r] >= regstart[r])
+                            regend[r] = old_regend[r];
+                        }
+                    }
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+                  goto fail;
+                }
+            }
+
+          /* Move past the register number and the inner group count.  */
+          p += 2;
+          break;
+
+
+       /* \<digit> has been turned into a `duplicate' command which is
+           followed by the numeric value of <digit> as the register number.  */
+        case duplicate:
+         {
+           register const char *d2, *dend2;
+           int regno = *p++;   /* Get which register to match against.  */
+           DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+           /* Can't back reference a group which we've never matched.  */
+            if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+              goto fail;
+
+            /* Where in input to try to start matching.  */
+            d2 = regstart[regno];
+
+            /* Where to stop matching; if both the place to start and
+               the place to stop matching are in the same string, then
+               set to the place to stop, otherwise, for now have to use
+               the end of the first string.  */
+
+            dend2 = ((FIRST_STRING_P (regstart[regno])
+                     == FIRST_STRING_P (regend[regno]))
+                    ? regend[regno] : end_match_1);
+           for (;;)
+             {
+               /* If necessary, advance to next segment in register
+                   contents.  */
+               while (d2 == dend2)
+                 {
+                   if (dend2 == end_match_2) break;
+                   if (dend2 == regend[regno]) break;
+
+                    /* End of string1 => advance to string2. */
+                    d2 = string2;
+                    dend2 = regend[regno];
+                 }
+               /* At end of register contents => success */
+               if (d2 == dend2) break;
+
+               /* If necessary, advance to next segment in data.  */
+               PREFETCH ();
+
+               /* How many characters left in this segment to match.  */
+               mcnt = dend - d;
+
+               /* Want how many consecutive characters we can match in
+                   one shot, so, if necessary, adjust the count.  */
+                if (mcnt > dend2 - d2)
+                 mcnt = dend2 - d2;
+
+               /* Compare that many; failure if mismatch, else move
+                   past them.  */
+               if (translate
+                    ? bcmp_translate (d, d2, mcnt, translate)
+                    : memcmp (d, d2, mcnt))
+                 goto fail;
+               d += mcnt, d2 += mcnt;
+
+               /* Do this because we've match some characters.  */
+               SET_REGS_MATCHED ();
+             }
+         }
+         break;
+
+
+        /* begline matches the empty string at the beginning of the string
+           (unless `not_bol' is set in `bufp'), and, if
+           `newline_anchor' is set, after newlines.  */
+       case begline:
+          DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+          if (AT_STRINGS_BEG (d))
+            {
+              if (!bufp->not_bol) break;
+            }
+          else if (d[-1] == '\n' && bufp->newline_anchor)
+            {
+              break;
+            }
+          /* In all other cases, we fail.  */
+          goto fail;
+
+
+        /* endline is the dual of begline.  */
+       case endline:
+          DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+          if (AT_STRINGS_END (d))
+            {
+              if (!bufp->not_eol) break;
+            }
+
+          /* We have to ``prefetch'' the next character.  */
+          else if ((d == end1 ? *string2 : *d) == '\n'
+                   && bufp->newline_anchor)
+            {
+              break;
+            }
+          goto fail;
+
+
+       /* Match at the very beginning of the data.  */
+        case begbuf:
+          DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+          if (AT_STRINGS_BEG (d))
+            break;
+          goto fail;
+
+
+       /* Match at the very end of the data.  */
+        case endbuf:
+          DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+         if (AT_STRINGS_END (d))
+           break;
+          goto fail;
+
+
+        /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
+           pushes NULL as the value for the string on the stack.  Then
+           `pop_failure_point' will keep the current value for the
+           string, instead of restoring it.  To see why, consider
+           matching `foo\nbar' against `.*\n'.  The .* matches the foo;
+           then the . fails against the \n.  But the next thing we want
+           to do is match the \n against the \n; if we restored the
+           string value, we would be back at the foo.
+
+           Because this is used only in specific cases, we don't need to
+           check all the things that `on_failure_jump' does, to make
+           sure the right things get saved on the stack.  Hence we don't
+           share its code.  The only reason to push anything on the
+           stack at all is that otherwise we would have to change
+           `anychar's code to do something besides goto fail in this
+           case; that seems worse than this.  */
+        case on_failure_keep_string_jump:
+          DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+          DEBUG_PRINT3 (" %d (to %p):\n", mcnt, p + mcnt);
+#else
+          DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+#endif
+
+          PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+          break;
+
+
+       /* Uses of on_failure_jump:
+
+           Each alternative starts with an on_failure_jump that points
+           to the beginning of the next alternative.  Each alternative
+           except the last ends with a jump that in effect jumps past
+           the rest of the alternatives.  (They really jump to the
+           ending jump of the following alternative, because tensioning
+           these jumps is a hassle.)
+
+           Repeats start with an on_failure_jump that points past both
+           the repetition text and either the following jump or
+           pop_failure_jump back to this on_failure_jump.  */
+       case on_failure_jump:
+        on_failure:
+          DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+          DEBUG_PRINT3 (" %d (to %p)", mcnt, p + mcnt);
+#else
+          DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+#endif
+
+          /* If this on_failure_jump comes right before a group (i.e.,
+             the original * applied to a group), save the information
+             for that group and all inner ones, so that if we fail back
+             to this point, the group's information will be correct.
+             For example, in \(a*\)*\1, we need the preceding group,
+             and in \(zz\(a*\)b*\)\2, we need the inner group.  */
+
+          /* We can't use `p' to check ahead because we push
+             a failure point to `p + mcnt' after we do this.  */
+          p1 = p;
+
+          /* We need to skip no_op's before we look for the
+             start_memory in case this on_failure_jump is happening as
+             the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+             against aba.  */
+          while (p1 < pend && (re_opcode_t) *p1 == no_op)
+            p1++;
+
+          if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+            {
+              /* We have a new highest active register now.  This will
+                 get reset at the start_memory we are about to get to,
+                 but we will have saved all the registers relevant to
+                 this repetition op, as described above.  */
+              highest_active_reg = *(p1 + 1) + *(p1 + 2);
+              if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+                lowest_active_reg = *(p1 + 1);
+            }
+
+          DEBUG_PRINT1 (":\n");
+          PUSH_FAILURE_POINT (p + mcnt, d, -2);
+          break;
+
+
+        /* A smart repeat ends with `maybe_pop_jump'.
+          We change it to either `pop_failure_jump' or `jump'.  */
+        case maybe_pop_jump:
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+          {
+           register unsigned char *p2 = p;
+
+            /* Compare the beginning of the repeat with what in the
+               pattern follows its end. If we can establish that there
+               is nothing that they would both match, i.e., that we
+               would have to backtrack because of (as in, e.g., `a*a')
+               then we can change to pop_failure_jump, because we'll
+               never have to backtrack.
+
+               This is not true in the case of alternatives: in
+               `(a|ab)*' we do need to backtrack to the `ab' alternative
+               (e.g., if the string was `ab').  But instead of trying to
+               detect that here, the alternative has put on a dummy
+               failure point which is what we will end up popping.  */
+
+           /* Skip over open/close-group commands.
+              If what follows this loop is a ...+ construct,
+              look at what begins its body, since we will have to
+              match at least one of that.  */
+           while (1)
+             {
+               if (p2 + 2 < pend
+                   && ((re_opcode_t) *p2 == stop_memory
+                       || (re_opcode_t) *p2 == start_memory))
+                 p2 += 3;
+               else if (p2 + 6 < pend
+                        && (re_opcode_t) *p2 == dummy_failure_jump)
+                 p2 += 6;
+               else
+                 break;
+             }
+
+           p1 = p + mcnt;
+           /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+              to the `maybe_finalize_jump' of this case.  Examine what
+              follows.  */
+
+            /* If we're at the end of the pattern, we can change.  */
+            if (p2 == pend)
+             {
+               /* Consider what happens when matching ":\(.*\)"
+                  against ":/".  I don't really understand this code
+                  yet.  */
+               p[-3] = (unsigned char) pop_failure_jump;
+                DEBUG_PRINT1
+                  ("  End of pattern: change to `pop_failure_jump'.\n");
+              }
+
+            else if ((re_opcode_t) *p2 == exactn
+                    || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+             {
+               register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+                if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+                  {
+                   p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+
+               else if ((re_opcode_t) p1[3] == charset
+                        || (re_opcode_t) p1[3] == charset_not)
+                 {
+                   int not = (re_opcode_t) p1[3] == charset_not;
+
+                   if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+                       && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+                     not = !not;
+
+                    /* `not' is equal to 1 if c would match, which means
+                        that we can't change to pop_failure_jump.  */
+                   if (!not)
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+             }
+            else if ((re_opcode_t) *p2 == charset)
+             {
+#ifdef DEBUG
+               register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+#endif
+
+#if 0
+                if ((re_opcode_t) p1[3] == exactn
+                   && ! ((int) p2[1] * BYTEWIDTH > (int) p1[5]
+                         && (p2[2 + p1[5] / BYTEWIDTH]
+                             & (1 << (p1[5] % BYTEWIDTH)))))
+#else
+                if ((re_opcode_t) p1[3] == exactn
+                   && ! ((int) p2[1] * BYTEWIDTH > (int) p1[4]
+                         && (p2[2 + p1[4] / BYTEWIDTH]
+                             & (1 << (p1[4] % BYTEWIDTH)))))
+#endif
+                  {
+                   p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+
+               else if ((re_opcode_t) p1[3] == charset_not)
+                 {
+                   int idx;
+                   /* We win if the charset_not inside the loop
+                      lists every character listed in the charset after.  */
+                   for (idx = 0; idx < (int) p2[1]; idx++)
+                     if (! (p2[2 + idx] == 0
+                            || (idx < (int) p1[4]
+                                && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
+                       break;
+
+                   if (idx == p2[1])
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+               else if ((re_opcode_t) p1[3] == charset)
+                 {
+                   int idx;
+                   /* We win if the charset inside the loop
+                      has no overlap with the one after the loop.  */
+                   for (idx = 0;
+                        idx < (int) p2[1] && idx < (int) p1[4];
+                        idx++)
+                     if ((p2[2 + idx] & p1[5 + idx]) != 0)
+                       break;
+
+                   if (idx == p2[1] || idx == p1[4])
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+             }
+         }
+         p -= 2;               /* Point at relative address again.  */
+         if ((re_opcode_t) p[-1] != pop_failure_jump)
+           {
+             p[-1] = (unsigned char) jump;
+              DEBUG_PRINT1 ("  Match => jump.\n");
+             goto unconditional_jump;
+           }
+        /* Note fall through.  */
+
+
+       /* The end of a simple repeat has a pop_failure_jump back to
+           its matching on_failure_jump, where the latter will push a
+           failure point.  The pop_failure_jump takes off failure
+           points put on by this pop_failure_jump's matching
+           on_failure_jump; we got through the pattern to here from the
+           matching on_failure_jump, so didn't fail.  */
+        case pop_failure_jump:
+          {
+            /* We need to pass separate storage for the lowest and
+               highest registers, even though we don't care about the
+               actual values.  Otherwise, we will restore only one
+               register from the stack, since lowest will == highest in
+               `pop_failure_point'.  */
+            active_reg_t dummy_low_reg, dummy_high_reg;
+            unsigned char *pdummy;
+            const char *sdummy;
+
+            DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+            POP_FAILURE_POINT (sdummy, pdummy,
+                               dummy_low_reg, dummy_high_reg,
+                               reg_dummy, reg_dummy, reg_info_dummy);
+          }
+         /* Note fall through.  */
+
+       unconditional_jump:
+#ifdef _LIBC
+         DEBUG_PRINT2 ("\n%p: ", p);
+#else
+         DEBUG_PRINT2 ("\n0x%x: ", p);
+#endif
+          /* Note fall through.  */
+
+        /* Unconditionally jump (without popping any failure points).  */
+        case jump:
+         EXTRACT_NUMBER_AND_INCR (mcnt, p);    /* Get the amount to jump.  */
+          DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+         p += mcnt;                            /* Do the jump.  */
+#ifdef _LIBC
+          DEBUG_PRINT2 ("(to %p).\n", p);
+#else
+          DEBUG_PRINT2 ("(to 0x%x).\n", p);
+#endif
+         break;
+
+
+        /* We need this opcode so we can detect where alternatives end
+           in `group_match_null_string_p' et al.  */
+        case jump_past_alt:
+          DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+          goto unconditional_jump;
+
+
+        /* Normally, the on_failure_jump pushes a failure point, which
+           then gets popped at pop_failure_jump.  We will end up at
+           pop_failure_jump, also, and with a pattern of, say, `a+', we
+           are skipping over the on_failure_jump, so we have to push
+           something meaningless for pop_failure_jump to pop.  */
+        case dummy_failure_jump:
+          DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+          /* It doesn't matter what we push for the string here.  What
+             the code at `fail' tests is the value for the pattern.  */
+          PUSH_FAILURE_POINT (NULL, NULL, -2);
+          goto unconditional_jump;
+
+
+        /* At the end of an alternative, we need to push a dummy failure
+           point in case we are followed by a `pop_failure_jump', because
+           we don't want the failure point for the alternative to be
+           popped.  For example, matching `(a|ab)*' against `aab'
+           requires that we match the `ab' alternative.  */
+        case push_dummy_failure:
+          DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+          /* See comments just above at `dummy_failure_jump' about the
+             two zeroes.  */
+          PUSH_FAILURE_POINT (NULL, NULL, -2);
+          break;
+
+        /* Have to succeed matching what follows at least n times.
+           After that, handle like `on_failure_jump'.  */
+        case succeed_n:
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+          assert (mcnt >= 0);
+          /* Originally, this is how many times we HAVE to succeed.  */
+          if (mcnt > 0)
+            {
+               mcnt--;
+              p += 2;
+               STORE_NUMBER_AND_INCR (p, mcnt);
+#ifdef _LIBC
+               DEBUG_PRINT3 ("  Setting %p to %d.\n", p - 2, mcnt);
+#else
+               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p - 2, mcnt);
+#endif
+            }
+         else if (mcnt == 0)
+            {
+#ifdef _LIBC
+              DEBUG_PRINT2 ("  Setting two bytes from %p to no_op.\n", p+2);
+#else
+              DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
+#endif
+             p[2] = (unsigned char) no_op;
+              p[3] = (unsigned char) no_op;
+              goto on_failure;
+            }
+          break;
+
+        case jump_n:
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+          /* Originally, this is how many times we CAN jump.  */
+          if (mcnt)
+            {
+               mcnt--;
+               STORE_NUMBER (p + 2, mcnt);
+#ifdef _LIBC
+               DEBUG_PRINT3 ("  Setting %p to %d.\n", p + 2, mcnt);
+#else
+               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p + 2, mcnt);
+#endif
+              goto unconditional_jump;
+            }
+          /* If don't have to jump any more, skip over the rest of command.  */
+         else
+           p += 4;
+          break;
+
+       case set_number_at:
+         {
+            DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            p1 = p + mcnt;
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+#ifdef _LIBC
+            DEBUG_PRINT3 ("  Setting %p to %d.\n", p1, mcnt);
+#else
+            DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
+#endif
+           STORE_NUMBER (p1, mcnt);
+            break;
+          }
+
+#if 0
+       /* The DEC Alpha C compiler 3.x generates incorrect code for the
+          test  WORDCHAR_P (d - 1) != WORDCHAR_P (d)  in the expansion of
+          AT_WORD_BOUNDARY, so this code is disabled.  Expanding the
+          macro and introducing temporary variables works around the bug.  */
+
+       case wordbound:
+         DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+         if (AT_WORD_BOUNDARY (d))
+           break;
+         goto fail;
+
+       case notwordbound:
+         DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+         if (AT_WORD_BOUNDARY (d))
+           goto fail;
+         break;
+#else
+       case wordbound:
+       {
+         boolean prevchar, thischar;
+
+         DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+         if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
+           break;
+
+         prevchar = WORDCHAR_P (d - 1);
+         thischar = WORDCHAR_P (d);
+         if (prevchar != thischar)
+           break;
+         goto fail;
+       }
+
+      case notwordbound:
+       {
+         boolean prevchar, thischar;
+
+         DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+         if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
+           goto fail;
+
+         prevchar = WORDCHAR_P (d - 1);
+         thischar = WORDCHAR_P (d);
+         if (prevchar != thischar)
+           goto fail;
+         break;
+       }
+#endif
+
+       case wordbeg:
+          DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+         if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+           break;
+          goto fail;
+
+       case wordend:
+          DEBUG_PRINT1 ("EXECUTING wordend.\n");
+         if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+              && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+           break;
+          goto fail;
+
+#ifdef emacs
+       case before_dot:
+          DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+           goto fail;
+         break;
+
+       case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) != point)
+           goto fail;
+         break;
+
+       case after_dot:
+          DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+          if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+           goto fail;
+         break;
+
+       case syntaxspec:
+          DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchsyntax;
+
+        case wordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+         mcnt = (int) Sword;
+        matchsyntax:
+         PREFETCH ();
+         /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
+         d++;
+         if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt)
+           goto fail;
+          SET_REGS_MATCHED ();
+         break;
+
+       case notsyntaxspec:
+          DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchnotsyntax;
+
+        case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+         mcnt = (int) Sword;
+        matchnotsyntax:
+         PREFETCH ();
+         /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
+         d++;
+         if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt)
+           goto fail;
+         SET_REGS_MATCHED ();
+          break;
+
+#else /* not emacs */
+       case wordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+         PREFETCH ();
+          if (!WORDCHAR_P (d))
+            goto fail;
+         SET_REGS_MATCHED ();
+          d++;
+         break;
+
+       case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+         PREFETCH ();
+         if (WORDCHAR_P (d))
+            goto fail;
+          SET_REGS_MATCHED ();
+          d++;
+         break;
+#endif /* not emacs */
+
+        default:
+          abort ();
+       }
+      continue;  /* Successfully executed one pattern command; keep going.  */
+
+
+    /* We goto here if a matching operation fails. */
+    fail:
+      if (!FAIL_STACK_EMPTY ())
+       { /* A restart point is known.  Restore to that state.  */
+          DEBUG_PRINT1 ("\nFAIL:\n");
+          POP_FAILURE_POINT (d, p,
+                             lowest_active_reg, highest_active_reg,
+                             regstart, regend, reg_info);
+
+          /* If this failure point is a dummy, try the next one.  */
+          if (!p)
+           goto fail;
+
+          /* If we failed to the end of the pattern, don't examine *p.  */
+         assert (p <= pend);
+          if (p < pend)
+            {
+              boolean is_a_jump_n = false;
+
+              /* If failed to a backwards jump that's part of a repetition
+                 loop, need to pop this failure point and use the next one.  */
+              switch ((re_opcode_t) *p)
+                {
+                case jump_n:
+                  is_a_jump_n = true;
+                case maybe_pop_jump:
+                case pop_failure_jump:
+                case jump:
+                  p1 = p + 1;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  p1 += mcnt;
+
+                  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+                      || (!is_a_jump_n
+                          && (re_opcode_t) *p1 == on_failure_jump))
+                    goto fail;
+                  break;
+                default:
+                  /* do nothing */ ;
+                }
+            }
+
+          if (d >= string1 && d <= end1)
+           dend = end_match_1;
+        }
+      else
+        break;   /* Matching at this starting point really fails.  */
+    } /* for (;;) */
+
+  if (best_regs_set)
+    goto restore_best_regs;
+
+  FREE_VARIABLES ();
+
+  return -1;                           /* Failure to match.  */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2.  */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+   Return true if the pattern up to the corresponding stop_memory can
+   match the empty string, and false otherwise.
+
+   If we find the matching stop_memory, sets P to point to one past its number.
+   Otherwise, sets P to an undefined byte less than or equal to END.
+
+   We don't handle duplicates properly (yet).  */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  /* Point to after the args to the start_memory.  */
+  unsigned char *p1 = *p + 2;
+
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and return true or
+        false, as appropriate, when we get to one that can't, or to the
+         matching stop_memory.  */
+
+      switch ((re_opcode_t) *p1)
+        {
+        /* Could be either a loop or a series of alternatives.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+          /* If the next operation is not a jump backwards in the
+            pattern.  */
+
+         if (mcnt >= 0)
+           {
+              /* Go through the on_failure_jumps of the alternatives,
+                 seeing if any of the alternatives cannot match nothing.
+                 The last alternative starts with only a jump,
+                 whereas the rest start with on_failure_jump and end
+                 with a jump, e.g., here is the pattern for `a|b|c':
+
+                 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+                 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+                 /exactn/1/c
+
+                 So, we have to first go through the first (n-1)
+                 alternatives and then deal with the last one separately.  */
+
+
+              /* Deal with the first (n-1) alternatives, which start
+                 with an on_failure_jump (see above) that jumps to right
+                 past a jump_past_alt.  */
+
+              while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+                {
+                  /* `mcnt' holds how many bytes long the alternative
+                     is, including the ending `jump_past_alt' and
+                     its number.  */
+
+                  if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+                                                     reg_info))
+                    return false;
+
+                  /* Move to right after this alternative, including the
+                    jump_past_alt.  */
+                  p1 += mcnt;
+
+                  /* Break if it's the beginning of an n-th alternative
+                     that doesn't begin with an on_failure_jump.  */
+                  if ((re_opcode_t) *p1 != on_failure_jump)
+                    break;
+
+                 /* Still have to check that it's not an n-th
+                    alternative that starts with an on_failure_jump.  */
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+                    {
+                     /* Get to the beginning of the n-th alternative.  */
+                      p1 -= 3;
+                      break;
+                    }
+                }
+
+              /* Deal with the last alternative: go back and get number
+                 of the `jump_past_alt' just before it.  `mcnt' contains
+                 the length of the alternative.  */
+              EXTRACT_NUMBER (mcnt, p1 - 2);
+
+              if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+                return false;
+
+              p1 += mcnt;      /* Get past the n-th alternative.  */
+            } /* if mcnt > 0 */
+          break;
+
+
+        case stop_memory:
+         assert (p1[1] == **p);
+          *p = p1 + 2;
+          return true;
+
+
+        default:
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    } /* while p1 < end */
+
+  return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+   It expects P to be the first byte of a single alternative and END one
+   byte past the last. The alternative can contain groups.  */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+    unsigned char *p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  unsigned char *p1 = p;
+
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and break when we get
+         to one that can't.  */
+
+      switch ((re_opcode_t) *p1)
+        {
+       /* It's a loop.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+          break;
+
+       default:
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    }  /* while p1 < end */
+
+  return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+   alt_match_null_string_p.
+
+   Sets P to one after the op and its arguments, if any.  */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  boolean ret;
+  int reg_no;
+  unsigned char *p1 = *p;
+
+  switch ((re_opcode_t) *p1++)
+    {
+    case no_op:
+    case begline:
+    case endline:
+    case begbuf:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case wordbound:
+    case notwordbound:
+#ifdef emacs
+    case before_dot:
+    case at_dot:
+    case after_dot:
+#endif
+      break;
+
+    case start_memory:
+      reg_no = *p1;
+      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+      ret = group_match_null_string_p (&p1, end, reg_info);
+
+      /* Have to set this here in case we're checking a group which
+         contains a group and a back reference to it.  */
+
+      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+        REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+      if (!ret)
+        return false;
+      break;
+
+    /* If this is an optimized succeed_n for zero times, make the jump.  */
+    case jump:
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+      if (mcnt >= 0)
+        p1 += mcnt;
+      else
+        return false;
+      break;
+
+    case succeed_n:
+      /* Get to the number of times to succeed.  */
+      p1 += 2;
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+      if (mcnt == 0)
+        {
+          p1 -= 4;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+        }
+      else
+        return false;
+      break;
+
+    case duplicate:
+      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+        return false;
+      break;
+
+    case set_number_at:
+      p1 += 4;
+
+    default:
+      /* All other opcodes mean we cannot match the empty string.  */
+      return false;
+  }
+
+  *p = p1;
+  return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+   bytes; nonzero otherwise.  */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+     const char *s1, *s2;
+     register int len;
+     RE_TRANSLATE_TYPE translate;
+{
+  register const unsigned char *p1 = (const unsigned char *) s1;
+  register const unsigned char *p2 = (const unsigned char *) s2;
+  while (len)
+    {
+      if (translate[*p1++] != translate[*p2++]) return 1;
+      len--;
+    }
+  return 0;
+}
+
+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length SIZE) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.
+
+   We call regex_compile to do the actual compilation.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+     const char *pattern;
+     size_t length;
+     struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+
+  /* GNU code is written to assume at least RE_NREGS registers will be set
+     (and at least one extra will be -1).  */
+  bufp->regs_allocated = REGS_UNALLOCATED;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  bufp->no_sub = 0;
+
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+
+  ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+  if (!ret)
+    return NULL;
+  return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+#ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+   these names if they don't use our functions, and still use
+   regcomp/regexec below without link errors.  */
+weak_function
+#endif
+re_comp (s)
+    const char *s;
+{
+  reg_errcode_t ret;
+
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+       return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (!re_comp_buf.buffer)
+    {
+      re_comp_buf.buffer = (unsigned char *) malloc (200);
+      if (re_comp_buf.buffer == NULL)
+        return (char *) gettext (re_error_msgid
+                                + re_error_msgid_idx[(int) REG_ESPACE]);
+      re_comp_buf.allocated = 200;
+
+      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+      if (re_comp_buf.fastmap == NULL)
+       return (char *) gettext (re_error_msgid
+                                + re_error_msgid_idx[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
+}
+
+
+int
+#ifdef _LIBC
+weak_function
+#endif
+re_exec (s)
+    const char *s;
+{
+  const int len = strlen (s);
+  return
+    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+
+#endif /* _REGEX_RE_COMP */
+
+/* POSIX.2 functions.  Don't define these for Emacs.  */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' to an allocated space for the fastmap;
+     `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *preg;
+    const char *pattern;
+    int cflags;
+{
+  reg_errcode_t ret;
+  reg_syntax_t syntax
+    = (cflags & REG_EXTENDED) ?
+      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+  /* regex_compile will allocate the space for the compiled pattern.  */
+  preg->buffer = 0;
+  preg->allocated = 0;
+  preg->used = 0;
+
+  /* Try to allocate space for the fastmap.  */
+  preg->fastmap = (char *) malloc (1 << BYTEWIDTH);
+
+  if (cflags & REG_ICASE)
+    {
+      unsigned i;
+
+      preg->translate
+       = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE
+                                     * sizeof (*(RE_TRANSLATE_TYPE)0));
+      if (preg->translate == NULL)
+        return (int) REG_ESPACE;
+
+      /* Map uppercase characters to corresponding lowercase ones.  */
+      for (i = 0; i < CHAR_SET_SIZE; i++)
+        preg->translate[i] = ISUPPER (i) ? TOLOWER (i) : i;
+    }
+  else
+    preg->translate = NULL;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+
+  preg->no_sub = !!(cflags & REG_NOSUB);
+
+  /* POSIX says a null character in the pattern terminates it, so we
+     can use strlen here in compiling the pattern.  */
+  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+  if (ret == REG_NOERROR && preg->fastmap)
+    {
+      /* Compute the fastmap now, since regexec cannot modify the pattern
+        buffer.  */
+      if (re_compile_fastmap (preg) == -2)
+       {
+         /* Some error occured while computing the fastmap, just forget
+            about it.  */
+         free (preg->fastmap);
+         preg->fastmap = NULL;
+       }
+    }
+
+  return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *preg;
+    const char *string;
+    size_t nmatch;
+    regmatch_t pmatch[];
+    int eflags;
+{
+  int ret;
+  struct re_registers regs;
+  regex_t private_preg;
+  int len = strlen (string);
+  boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+  private_preg = *preg;
+
+  private_preg.not_bol = !!(eflags & REG_NOTBOL);
+  private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+  /* The user has told us exactly how many registers to return
+     information about, via `nmatch'.  We have to pass that on to the
+     matching routines.  */
+  private_preg.regs_allocated = REGS_FIXED;
+
+  if (want_reg_info)
+    {
+      regs.num_regs = nmatch;
+      regs.start = TALLOC (nmatch * 2, regoff_t);
+      if (regs.start == NULL)
+        return (int) REG_NOMATCH;
+      regs.end = regs.start + nmatch;
+    }
+
+  /* Perform the searching operation.  */
+  ret = re_search (&private_preg, string, len,
+                   /* start: */ 0, /* range: */ len,
+                   want_reg_info ? &regs : (struct re_registers *) 0);
+
+  /* Copy the register information to the POSIX structure.  */
+  if (want_reg_info)
+    {
+      if (ret >= 0)
+        {
+          unsigned r;
+
+          for (r = 0; r < nmatch; r++)
+            {
+              pmatch[r].rm_so = regs.start[r];
+              pmatch[r].rm_eo = regs.end[r];
+            }
+        }
+
+      /* If we needed the temporary register info, free the space now.  */
+      free (regs.start);
+    }
+
+  /* We want zero return to mean success, unlike `re_search'.  */
+  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+#ifdef _LIBC
+weak_alias (__regexec, regexec)
+#endif
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (err, preg, errbuf, errbuf_size)
+    int err;
+    const regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (err < 0
+      || err >= (int) (sizeof (re_error_msgid_idx)
+                          / sizeof (re_error_msgid_idx[0])))
+    /* Only error codes returned by the rest of the code should be passed
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (re_error_msgid + re_error_msgid_idx[err]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+
+  if (errbuf_size != 0)
+    {
+      if (msg_size > errbuf_size)
+        {
+#if defined HAVE_MEMPCPY || defined _LIBC
+         *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+          memcpy (errbuf, msg, errbuf_size - 1);
+          errbuf[errbuf_size - 1] = 0;
+#endif
+        }
+      else
+        memcpy (errbuf, msg, msg_size);
+    }
+
+  return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  if (preg->buffer != NULL)
+    free (preg->buffer);
+  preg->buffer = NULL;
+
+  preg->allocated = 0;
+  preg->used = 0;
+
+  if (preg->fastmap != NULL)
+    free (preg->fastmap);
+  preg->fastmap = NULL;
+  preg->fastmap_accurate = 0;
+
+  if (preg->translate != NULL)
+    free (preg->translate);
+  preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+#endif /* not emacs  */
diff --git a/lib/route_types.pl b/lib/route_types.pl
new file mode 100755 (executable)
index 0000000..e1595af
--- /dev/null
@@ -0,0 +1,199 @@
+#!/usr/bin/perl
+##
+## Scan a file of route-type definitions (see eg route_types.txt) and
+## generate a corresponding header file with:
+##
+## - enum of Zserv route-types
+## - redistribute strings for the various Quagga daemons
+##
+## See route_types.txt for the format.
+##
+##
+## Copyright (C) 2009 David Lamparter.
+## This file is part of GNU Zebra.
+##
+## GNU Zebra 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.
+##
+## GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+## 02111-1307, USA.
+##
+
+use strict;
+
+# input processing
+#
+my @protos;
+my %protodetail;
+
+my %daemons;
+
+while (<STDIN>) {
+       # skip comments and empty lines
+       next if (/^\s*(#|$)/);
+
+       # strip whitespace
+       chomp;
+       $_ =~ s/^\s*//;
+       $_ =~ s/\s*$//;
+
+       # match help strings
+       if (/^(ZEBRA_ROUTE_[^\s]+)\s*,\s*"(.*)"$/) {
+               $protodetail{$1}->{'longhelp'} = $2;
+               next;
+       }
+
+       $_ =~ s/\s*,\s*/,/g;
+
+       # else: 7-field line
+       my @f = split(/,/, $_);
+       unless (@f == 7) {
+               die "invalid input on route_types line $.\n";
+       }
+
+       my $proto = $f[0];
+       $f[3] = $1 if ($f[3] =~ /^'(.*)'$/);
+       $f[6] = $1 if ($f[6] =~ /^"(.*)"$/);
+
+       $protodetail{$proto} = {
+               "number" => scalar @protos,
+               "type" => $f[0],
+               "cname" => $f[1],
+               "daemon" => $f[2],
+               "char" => $f[3],
+               "ipv4" => int($f[4]),
+               "ipv6" => int($f[5]),
+               "shorthelp" => $f[6],
+       };
+       push @protos, $proto;
+       $daemons{$f[2]} = {
+               "ipv4" => int($f[4]),
+               "ipv6" => int($f[5])
+       } unless ($f[2] eq "NULL");
+}
+
+# output
+printf <<EOF, $ARGV[0];
+/* Auto-generated from route_types.txt by %s. */
+/* Do not edit! */
+
+#ifndef _QUAGGA_ROUTE_TYPES_H
+#define _QUAGGA_ROUTE_TYPES_H
+
+/* Zebra route's types. */
+EOF
+
+push @protos, "ZEBRA_ROUTE_MAX";
+my (@protosv4, @protosv6) = ((), ());
+for (my $c = 0; $c < @protos; $c++) {
+       my $p = $protos[$c];
+       printf "#define %-32s %d\n", $p, $c;
+       push @protosv4, $p if ($protodetail{$p}->{"ipv4"});
+       push @protosv6, $p if ($protodetail{$p}->{"ipv6"});
+}
+pop @protos;
+
+sub codelist {
+       my (@protos) = @_;
+       my (@lines) = ();
+       my $str = "  \"Codes: ";
+       for my $p (@protos) {
+               my $s = sprintf("%s - %s, ",
+                       $protodetail{$p}->{"char"},
+                       $protodetail{$p}->{"shorthelp"});
+               if (length($str . $s) > 70) {
+                       $str =~ s/ $//;
+                       push @lines, $str . "%s\" \\\n";
+                       $str = "  \"       ";
+               }
+               $str .= $s;
+       }
+       $str =~ s/ $//;
+       push @lines, $str . "%s\" \\\n";
+       push @lines, "  \"       > - selected route, * - FIB route%s%s\", \\\n";
+       my @nl = ();
+       for (my $c = 0; $c < @lines + 1; $c++) {
+               push @nl, "VTY_NEWLINE"
+       }
+       return join("", @lines) ."  ". join(", ", @nl);
+}
+
+print "\n";
+printf "#define SHOW_ROUTE_V4_HEADER \\\n%s\n", codelist(@protosv4);
+printf "#define SHOW_ROUTE_V6_HEADER \\\n%s\n", codelist(@protosv6);
+print "\n";
+
+sub collect {
+       my ($daemon, $ipv4, $ipv6) = @_;
+       my (@names, @help) = ((), ());
+       for my $p (@protos) {
+               next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra");
+               next unless (($ipv4 && $protodetail{$p}->{"ipv4"})
+                               || ($ipv6 && $protodetail{$p}->{"ipv6"}));
+               push @names, $protodetail{$p}->{"cname"};
+               push @help, "  \"".$protodetail{$p}->{"longhelp"}."\\n\"";
+       }
+       return ("\"(" . join("|", @names) . ")\"", join(" \\\n", @help));
+}
+
+for my $daemon (sort keys %daemons) {
+       next unless ($daemons{$daemon}->{"ipv4"} || $daemons{$daemon}->{"ipv6"});
+       printf "/* %s */\n", $daemon;
+       if ($daemons{$daemon}->{"ipv4"} && $daemons{$daemon}->{"ipv6"}) {
+               my ($names, $help) = collect($daemon, 1, 1);
+               printf "#define QUAGGA_REDIST_STR_%s \\\n  %s\n", uc $daemon, $names;
+               printf "#define QUAGGA_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help;
+               ($names, $help) = collect($daemon, 1, 0);
+               printf "#define QUAGGA_IP_REDIST_STR_%s \\\n  %s\n", uc $daemon, $names;
+               printf "#define QUAGGA_IP_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help;
+               ($names, $help) = collect($daemon, 0, 1);
+               printf "#define QUAGGA_IP6_REDIST_STR_%s \\\n  %s\n", uc $daemon, $names;
+               printf "#define QUAGGA_IP6_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help;
+       } else {
+               my ($names, $help) = collect($daemon,
+                       $daemons{$daemon}->{"ipv4"}, $daemons{$daemon}->{"ipv6"});
+               printf "#define QUAGGA_REDIST_STR_%s \\\n  %s\n", uc $daemon, $names;
+               printf "#define QUAGGA_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help;
+       }
+       print "\n";
+}
+
+print <<EOF;
+
+#ifdef QUAGGA_DEFINE_DESC_TABLE
+
+struct zebra_desc_table
+{
+  unsigned int type;
+  const char *string;
+  char chr;
+};
+
+#define DESC_ENTRY(T,S,C) [(T)] = { (T), (S), (C) }
+static const struct zebra_desc_table route_types[] = {
+EOF
+
+for (my $c = 0; $c < @protos; $c++) {
+       my $p = $protos[$c];
+       printf "  DESC_ENTRY\t(%s\t \"%s\",\t'%s' ),\n",
+              $p.",", $protodetail{$p}->{"cname"}, $protodetail{$p}->{"char"};
+}
+
+print <<EOF;
+};
+#undef DESC_ENTRY
+
+#endif /* QUAGGA_DEFINE_DESC_TABLE */
+
+#endif /* _QUAGGA_ROUTE_TYPES_H */
+EOF
+
diff --git a/lib/route_types.txt b/lib/route_types.txt
new file mode 100644 (file)
index 0000000..811d24e
--- /dev/null
@@ -0,0 +1,80 @@
+# Canonical Zserv route types information registry for Quagga.
+#
+# Used to construct route_types.c and route_types.h
+#
+# comma-seperated fields of either 2 fields (help strings) or 7 fields.
+# White space before and after the comma seperators is stripped.
+# Lines /beginning/ with # are comments.
+#
+####
+# 7 field line has format:
+# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, short desc
+#
+# Zserv route type:    Corresponding with zebra.h. Key field.
+# canonical name:      Typically derived from the route type definition.
+#                      Used in 'redistribute' commands in daemons.
+#                      Key field.
+# daemon:              The daemon which may originates this route type
+#                      for redistribution to other daemons.
+#                      NULL if not applicable.
+#                      M:N definitions of type:daemon are allowed.
+#                      Used to construct vty command strings.
+# route char:          Single character to denote the route, if applicable.
+#                      Used to denote route type where space is tight,
+#                      e.g. 'show ip route' / 'show ipv6 route'.
+#                      'X' is reserved as the 'not needed' placeholder.
+# ipv4:                        IPv4 capable? yes/no, or 1/0.
+# ipv6:                        IPv6 capable? ditto.
+# short desc:          Very brief description. Used in header of
+#                      'show ip route'. May be specified as NULL
+#                      if the canonical name suffices.
+#
+# Key fields obviously must be a unique ASCII alpha-numeric word.
+#   Lower-case is required, brevity is optional but highly desirable.
+#
+####
+# 2 field format:
+#
+# Zserv route type, Long description
+#
+# Long description:     Full description, but should try fit on a line.
+####
+
+##  type                cname      daemon  C    4  6  short help
+ZEBRA_ROUTE_SYSTEM,     system,    NULL,   'X', 0, 0, "Reserved"
+ZEBRA_ROUTE_KERNEL,     kernel,    zebra,  'K', 1, 1, "kernel route"
+ZEBRA_ROUTE_CONNECT,    connected, zebra,  'C', 1, 1, "connected"
+ZEBRA_ROUTE_STATIC,     static,    zebra,  'S', 1, 1, "static"
+ZEBRA_ROUTE_RIP,        rip,       ripd,   'R', 1, 0, "RIP"
+ZEBRA_ROUTE_RIPNG,      ripng,     ripngd, 'R', 0, 1, "RIPng"
+ZEBRA_ROUTE_OSPF,       ospf,      ospfd,  'O', 1, 0, "OSPF"
+ZEBRA_ROUTE_OSPF6,      ospf6,     ospf6d, 'O', 0, 1, "OSPFv6"
+ZEBRA_ROUTE_ISIS,       isis,      isisd,  'I', 1, 1, "IS-IS"
+ZEBRA_ROUTE_BGP,        bgp,       bgpd,   'B', 1, 1, "BGP"
+ZEBRA_ROUTE_PIM,       pim,       pimd,   'P', 1, 0, "PIM"
+# HSLS and OLSR both are AFI independent (so: 1, 1), however
+# we want to disable for them for general Quagga distribution.
+# This at least makes it trivial for users of these protocols
+# to 'switch on' redist support (direct numeric entry remaining
+# possible).
+ZEBRA_ROUTE_HSLS,       hsls,      hslsd,  'H', 0, 0, "HSLS"
+ZEBRA_ROUTE_OLSR,       olsr,      olsrd,  'o', 0, 0, "OLSR"
+ZEBRA_ROUTE_BABEL,      babel,     babeld, 'A', 1, 1, "Babel"
+ZEBRA_ROUTE_NHRP,       nhrp,      nhrpd,  'N', 1, 1, "NHRP"
+
+## help strings
+ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only"
+ZEBRA_ROUTE_KERNEL, "Kernel routes (not installed via the zebra RIB)"
+ZEBRA_ROUTE_CONNECT,"Connected routes (directly attached subnet or host)"
+ZEBRA_ROUTE_STATIC, "Statically configured routes"
+ZEBRA_ROUTE_RIP,    "Routing Information Protocol (RIP)"
+ZEBRA_ROUTE_RIPNG,  "Routing Information Protocol next-generation (IPv6) (RIPng)"
+ZEBRA_ROUTE_OSPF,   "Open Shortest Path First (OSPFv2)"
+ZEBRA_ROUTE_OSPF6,  "Open Shortest Path First (IPv6) (OSPFv3)"
+ZEBRA_ROUTE_ISIS,   "Intermediate System to Intermediate System (IS-IS)"
+ZEBRA_ROUTE_BGP,    "Border Gateway Protocol (BGP)"
+ZEBRA_ROUTE_PIM,    "Protocol Independent Multicast (PIM)"
+ZEBRA_ROUTE_HSLS,   "Hazy-Sighted Link State Protocol (HSLS)"
+ZEBRA_ROUTE_OLSR,   "Optimised Link State Routing (OLSR)"
+ZEBRA_ROUTE_BABEL,  "Babel routing protocol (Babel)"
+ZEBRA_ROUTE_NHRP,   "Next Hop Resolution Protocol (NHRP)"
diff --git a/lib/routemap.c b/lib/routemap.c
new file mode 100644 (file)
index 0000000..c392226
--- /dev/null
@@ -0,0 +1,1363 @@
+/* Route map function.
+   Copyright (C) 1998, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "memory.h"
+#include "vector.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "command.h"
+#include "vty.h"
+#include "log.h"
+
+/* Vector for route match rules. */
+static vector route_match_vec;
+
+/* Vector for route set rules. */
+static vector route_set_vec;
+
+/* Route map rule. This rule has both `match' rule and `set' rule. */
+struct route_map_rule
+{
+  /* Rule type. */
+  struct route_map_rule_cmd *cmd;
+
+  /* For pretty printing. */
+  char *rule_str;
+
+  /* Pre-compiled match rule. */
+  void *value;
+
+  /* Linked list. */
+  struct route_map_rule *next;
+  struct route_map_rule *prev;
+};
+
+/* Making route map list. */
+struct route_map_list
+{
+  struct route_map *head;
+  struct route_map *tail;
+
+  void (*add_hook) (const char *);
+  void (*delete_hook) (const char *);
+  void (*event_hook) (route_map_event_t, const char *); 
+};
+
+/* Master list of route map. */
+static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL };
+
+static void
+route_map_rule_delete (struct route_map_rule_list *,
+                      struct route_map_rule *);
+
+static void
+route_map_index_delete (struct route_map_index *, int);
+
+/* New route map allocation. Please note route map's name must be
+   specified. */
+static struct route_map *
+route_map_new (const char *name)
+{
+  struct route_map *new;
+
+  new =  XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map));
+  new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name);
+  return new;
+}
+
+/* Add new name to route_map. */
+static struct route_map *
+route_map_add (const char *name)
+{
+  struct route_map *map;
+  struct route_map_list *list;
+
+  map = route_map_new (name);
+  list = &route_map_master;
+    
+  map->next = NULL;
+  map->prev = list->tail;
+  if (list->tail)
+    list->tail->next = map;
+  else
+    list->head = map;
+  list->tail = map;
+
+  /* Execute hook. */
+  if (route_map_master.add_hook)
+    (*route_map_master.add_hook) (name);
+
+  return map;
+}
+
+/* Route map delete from list. */
+static void
+route_map_delete (struct route_map *map)
+{
+  struct route_map_list *list;
+  struct route_map_index *index;
+  char *name;
+  
+  while ((index = map->head) != NULL)
+    route_map_index_delete (index, 0);
+
+  name = map->name;
+
+  list = &route_map_master;
+
+  if (map->next)
+    map->next->prev = map->prev;
+  else
+    list->tail = map->prev;
+
+  if (map->prev)
+    map->prev->next = map->next;
+  else
+    list->head = map->next;
+
+  XFREE (MTYPE_ROUTE_MAP, map);
+
+  /* Execute deletion hook. */
+  if (route_map_master.delete_hook)
+    (*route_map_master.delete_hook) (name);
+
+  if (name)
+    XFREE (MTYPE_ROUTE_MAP_NAME, name);
+
+}
+
+/* Lookup route map by route map name string. */
+struct route_map *
+route_map_lookup_by_name (const char *name)
+{
+  struct route_map *map;
+
+  for (map = route_map_master.head; map; map = map->next)
+    if (strcmp (map->name, name) == 0)
+      return map;
+  return NULL;
+}
+
+/* Lookup route map.  If there isn't route map create one and return
+   it. */
+static struct route_map *
+route_map_get (const char *name)
+{
+  struct route_map *map;
+
+  map = route_map_lookup_by_name (name);
+  if (map == NULL)
+    map = route_map_add (name);
+  return map;
+}
+
+/* Return route map's type string. */
+static const char *
+route_map_type_str (enum route_map_type type)
+{
+  switch (type)
+    {
+    case RMAP_PERMIT:
+      return "permit";
+      break;
+    case RMAP_DENY:
+      return "deny";
+      break;
+    default:
+      return "";
+      break;
+    }
+}
+
+static int
+route_map_empty (struct route_map *map)
+{
+  if (map->head == NULL && map->tail == NULL)
+    return 1;
+  else
+    return 0;
+}
+
+/* show route-map */
+static void
+vty_show_route_map_entry (struct vty *vty, struct route_map *map)
+{
+  struct route_map_index *index;
+  struct route_map_rule *rule;
+
+  /* Print the name of the protocol */
+  if (zlog_default)
+    vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol],
+             VTY_NEWLINE);
+
+  for (index = map->head; index; index = index->next)
+    {
+      vty_out (vty, "route-map %s, %s, sequence %d%s",
+               map->name, route_map_type_str (index->type),
+               index->pref, VTY_NEWLINE);
+
+      /* Description */
+      if (index->description)
+       vty_out (vty, "  Description:%s    %s%s", VTY_NEWLINE,
+                index->description, VTY_NEWLINE);
+      
+      /* Match clauses */
+      vty_out (vty, "  Match clauses:%s", VTY_NEWLINE);
+      for (rule = index->match_list.head; rule; rule = rule->next)
+        vty_out (vty, "    %s %s%s", 
+                 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
+      
+      vty_out (vty, "  Set clauses:%s", VTY_NEWLINE);
+      for (rule = index->set_list.head; rule; rule = rule->next)
+        vty_out (vty, "    %s %s%s",
+                 rule->cmd->str, rule->rule_str, VTY_NEWLINE);
+      
+      /* Call clause */
+      vty_out (vty, "  Call clause:%s", VTY_NEWLINE);
+      if (index->nextrm)
+        vty_out (vty, "    Call %s%s", index->nextrm, VTY_NEWLINE);
+      
+      /* Exit Policy */
+      vty_out (vty, "  Action:%s", VTY_NEWLINE);
+      if (index->exitpolicy == RMAP_GOTO)
+        vty_out (vty, "    Goto %d%s", index->nextpref, VTY_NEWLINE);
+      else if (index->exitpolicy == RMAP_NEXT)
+        vty_out (vty, "    Continue to next entry%s", VTY_NEWLINE);
+      else if (index->exitpolicy == RMAP_EXIT)
+        vty_out (vty, "    Exit routemap%s", VTY_NEWLINE);
+    }
+}
+
+static int
+vty_show_route_map (struct vty *vty, const char *name)
+{
+  struct route_map *map;
+
+  if (name)
+    {
+      map = route_map_lookup_by_name (name);
+
+      if (map)
+        {
+          vty_show_route_map_entry (vty, map);
+          return CMD_SUCCESS;
+        }
+      else
+        {
+          vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  else
+    {
+      for (map = route_map_master.head; map; map = map->next)
+       vty_show_route_map_entry (vty, map);
+    }
+  return CMD_SUCCESS;
+}
+
+
+/* New route map allocation. Please note route map's name must be
+   specified. */
+static struct route_map_index *
+route_map_index_new (void)
+{
+  struct route_map_index *new;
+
+  new =  XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
+  new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
+  return new;
+}
+
+/* Free route map index. */
+static void
+route_map_index_delete (struct route_map_index *index, int notify)
+{
+  struct route_map_rule *rule;
+
+  /* Free route match. */
+  while ((rule = index->match_list.head) != NULL)
+    route_map_rule_delete (&index->match_list, rule);
+
+  /* Free route set. */
+  while ((rule = index->set_list.head) != NULL)
+    route_map_rule_delete (&index->set_list, rule);
+
+  /* Remove index from route map list. */
+  if (index->next)
+    index->next->prev = index->prev;
+  else
+    index->map->tail = index->prev;
+
+  if (index->prev)
+    index->prev->next = index->next;
+  else
+    index->map->head = index->next;
+
+  /* Free 'char *nextrm' if not NULL */
+  if (index->nextrm)
+    XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
+
+    /* Execute event hook. */
+  if (route_map_master.event_hook && notify)
+    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
+                                   index->map->name);
+
+  XFREE (MTYPE_ROUTE_MAP_INDEX, index);
+}
+
+/* Lookup index from route map. */
+static struct route_map_index *
+route_map_index_lookup (struct route_map *map, enum route_map_type type,
+                       int pref)
+{
+  struct route_map_index *index;
+
+  for (index = map->head; index; index = index->next)
+    if ((index->type == type || type == RMAP_ANY)
+       && index->pref == pref)
+      return index;
+  return NULL;
+}
+
+/* Add new index to route map. */
+static struct route_map_index *
+route_map_index_add (struct route_map *map, enum route_map_type type,
+                    int pref)
+{
+  struct route_map_index *index;
+  struct route_map_index *point;
+
+  /* Allocate new route map inex. */
+  index = route_map_index_new ();
+  index->map = map;
+  index->type = type;
+  index->pref = pref;
+  
+  /* Compare preference. */
+  for (point = map->head; point; point = point->next)
+    if (point->pref >= pref)
+      break;
+
+  if (map->head == NULL)
+    {
+      map->head = map->tail = index;
+    }
+  else if (point == NULL)
+    {
+      index->prev = map->tail;
+      map->tail->next = index;
+      map->tail = index;
+    }
+  else if (point == map->head)
+    {
+      index->next = map->head;
+      map->head->prev = index;
+      map->head = index;
+    }
+  else
+    {
+      index->next = point;
+      index->prev = point->prev;
+      if (point->prev)
+       point->prev->next = index;
+      point->prev = index;
+    }
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
+                                   map->name);
+
+  return index;
+}
+
+/* Get route map index. */
+static struct route_map_index *
+route_map_index_get (struct route_map *map, enum route_map_type type, 
+                    int pref)
+{
+  struct route_map_index *index;
+
+  index = route_map_index_lookup (map, RMAP_ANY, pref);
+  if (index && index->type != type)
+    {
+      /* Delete index from route map. */
+      route_map_index_delete (index, 1);
+      index = NULL;
+    }
+  if (index == NULL)
+    index = route_map_index_add (map, type, pref);
+  return index;
+}
+
+/* New route map rule */
+static struct route_map_rule *
+route_map_rule_new (void)
+{
+  struct route_map_rule *new;
+
+  new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
+  return new;
+}
+
+/* Install rule command to the match list. */
+void
+route_map_install_match (struct route_map_rule_cmd *cmd)
+{
+  vector_set (route_match_vec, cmd);
+}
+
+/* Install rule command to the set list. */
+void
+route_map_install_set (struct route_map_rule_cmd *cmd)
+{
+  vector_set (route_set_vec, cmd);
+}
+
+/* Lookup rule command from match list. */
+static struct route_map_rule_cmd *
+route_map_lookup_match (const char *name)
+{
+  unsigned int i;
+  struct route_map_rule_cmd *rule;
+
+  for (i = 0; i < vector_active (route_match_vec); i++)
+    if ((rule = vector_slot (route_match_vec, i)) != NULL)
+      if (strcmp (rule->str, name) == 0)
+       return rule;
+  return NULL;
+}
+
+/* Lookup rule command from set list. */
+static struct route_map_rule_cmd *
+route_map_lookup_set (const char *name)
+{
+  unsigned int i;
+  struct route_map_rule_cmd *rule;
+
+  for (i = 0; i < vector_active (route_set_vec); i++)
+    if ((rule = vector_slot (route_set_vec, i)) != NULL)
+      if (strcmp (rule->str, name) == 0)
+       return rule;
+  return NULL;
+}
+
+/* Add match and set rule to rule list. */
+static void
+route_map_rule_add (struct route_map_rule_list *list,
+                   struct route_map_rule *rule)
+{
+  rule->next = NULL;
+  rule->prev = list->tail;
+  if (list->tail)
+    list->tail->next = rule;
+  else
+    list->head = rule;
+  list->tail = rule;
+}
+
+/* Delete rule from rule list. */
+static void
+route_map_rule_delete (struct route_map_rule_list *list,
+                      struct route_map_rule *rule)
+{
+  if (rule->cmd->func_free)
+    (*rule->cmd->func_free) (rule->value);
+
+  if (rule->rule_str)
+    XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
+
+  if (rule->next)
+    rule->next->prev = rule->prev;
+  else
+    list->tail = rule->prev;
+  if (rule->prev)
+    rule->prev->next = rule->next;
+  else
+    list->head = rule->next;
+
+  XFREE (MTYPE_ROUTE_MAP_RULE, rule);
+}
+
+/* strcmp wrapper function which don't crush even argument is NULL. */
+static int
+rulecmp (const char *dst, const char *src)
+{
+  if (dst == NULL)
+    {
+      if (src ==  NULL)
+       return 0;
+      else
+       return 1;
+    }
+  else
+    {
+      if (src == NULL)
+       return 1;
+      else
+       return strcmp (dst, src);
+    }
+  return 1;
+}
+
+/* Add match statement to route map. */
+int
+route_map_add_match (struct route_map_index *index, const char *match_name,
+                     const char *match_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule *next;
+  struct route_map_rule_cmd *cmd;
+  void *compile;
+  int replaced = 0;
+
+  /* First lookup rule for add match statement. */
+  cmd = route_map_lookup_match (match_name);
+  if (cmd == NULL)
+    return RMAP_RULE_MISSING;
+
+  /* Next call compile function for this match statement. */
+  if (cmd->func_compile)
+    {
+      compile= (*cmd->func_compile)(match_arg);
+      if (compile == NULL)
+       return RMAP_COMPILE_ERROR;
+    }
+  else
+    compile = NULL;
+
+  /* If argument is completely same ignore it. */
+  for (rule = index->match_list.head; rule; rule = next)
+    {
+      next = rule->next;
+      if (rule->cmd == cmd)
+       {       
+         route_map_rule_delete (&index->match_list, rule);
+         replaced = 1;
+       }
+    }
+
+  /* Add new route map match rule. */
+  rule = route_map_rule_new ();
+  rule->cmd = cmd;
+  rule->value = compile;
+  if (match_arg)
+    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
+  else
+    rule->rule_str = NULL;
+
+  /* Add new route match rule to linked list. */
+  route_map_rule_add (&index->match_list, rule);
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (replaced ?
+                                   RMAP_EVENT_MATCH_REPLACED:
+                                   RMAP_EVENT_MATCH_ADDED,
+                                   index->map->name);
+
+  return 0;
+}
+
+/* Delete specified route match rule. */
+int
+route_map_delete_match (struct route_map_index *index, const char *match_name,
+                        const char *match_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule_cmd *cmd;
+
+  cmd = route_map_lookup_match (match_name);
+  if (cmd == NULL)
+    return 1;
+  
+  for (rule = index->match_list.head; rule; rule = rule->next)
+    if (rule->cmd == cmd && 
+       (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
+      {
+       route_map_rule_delete (&index->match_list, rule);
+       /* Execute event hook. */
+       if (route_map_master.event_hook)
+         (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
+                                         index->map->name);
+       return 0;
+      }
+  /* Can't find matched rule. */
+  return 1;
+}
+
+/* Add route-map set statement to the route map. */
+int
+route_map_add_set (struct route_map_index *index, const char *set_name,
+                   const char *set_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule *next;
+  struct route_map_rule_cmd *cmd;
+  void *compile;
+  int replaced = 0;
+
+  cmd = route_map_lookup_set (set_name);
+  if (cmd == NULL)
+    return RMAP_RULE_MISSING;
+
+  /* Next call compile function for this match statement. */
+  if (cmd->func_compile)
+    {
+      compile= (*cmd->func_compile)(set_arg);
+      if (compile == NULL)
+       return RMAP_COMPILE_ERROR;
+    }
+  else
+    compile = NULL;
+
+ /* Add by WJL. if old set command of same kind exist, delete it first
+    to ensure only one set command of same kind exist under a
+    route_map_index. */
+  for (rule = index->set_list.head; rule; rule = next)
+    {
+      next = rule->next;
+      if (rule->cmd == cmd)
+       {
+         route_map_rule_delete (&index->set_list, rule);
+         replaced = 1;
+       }
+    }
+
+  /* Add new route map match rule. */
+  rule = route_map_rule_new ();
+  rule->cmd = cmd;
+  rule->value = compile;
+  if (set_arg)
+    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
+  else
+    rule->rule_str = NULL;
+
+  /* Add new route match rule to linked list. */
+  route_map_rule_add (&index->set_list, rule);
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (replaced ?
+                                   RMAP_EVENT_SET_REPLACED:
+                                   RMAP_EVENT_SET_ADDED,
+                                   index->map->name);
+  return 0;
+}
+
+/* Delete route map set rule. */
+int
+route_map_delete_set (struct route_map_index *index, const char *set_name,
+                      const char *set_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule_cmd *cmd;
+
+  cmd = route_map_lookup_set (set_name);
+  if (cmd == NULL)
+    return 1;
+  
+  for (rule = index->set_list.head; rule; rule = rule->next)
+    if ((rule->cmd == cmd) &&
+         (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
+      {
+        route_map_rule_delete (&index->set_list, rule);
+       /* Execute event hook. */
+       if (route_map_master.event_hook)
+         (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
+                                         index->map->name);
+        return 0;
+      }
+  /* Can't find matched rule. */
+  return 1;
+}
+
+/* Apply route map's each index to the object.
+
+   The matrix for a route-map looks like this:
+   (note, this includes the description for the "NEXT"
+   and "GOTO" frobs now
+  
+              Match   |   No Match
+                      |
+    permit    action  |     cont
+                      |
+    ------------------+---------------
+                      |
+    deny      deny    |     cont
+                      |
+  
+   action)
+      -Apply Set statements, accept route
+      -If Call statement is present jump to the specified route-map, if it
+         denies the route we finish.
+      -If NEXT is specified, goto NEXT statement
+      -If GOTO is specified, goto the first clause where pref > nextpref
+      -If nothing is specified, do as Cisco and finish
+   deny)
+      -Route is denied by route-map.
+   cont)
+      -Goto Next index
+  
+   If we get no matches after we've processed all updates, then the route
+   is dropped too.
+  
+   Some notes on the new "CALL", "NEXT" and "GOTO"
+     call WORD        - If this clause is matched, then the set statements
+                        are executed and then we jump to route-map 'WORD'. If
+                        this route-map denies the route, we finish, in other case we
+                        do whatever the exit policy (EXIT, NEXT or GOTO) tells.
+     on-match next    - If this clause is matched, then the set statements
+                        are executed and then we drop through to the next clause
+     on-match goto n  - If this clause is matched, then the set statments
+                        are executed and then we goto the nth clause, or the
+                        first clause greater than this. In order to ensure
+                        route-maps *always* exit, you cannot jump backwards.
+                        Sorry ;)
+  
+   We need to make sure our route-map processing matches the above
+*/
+
+static route_map_result_t
+route_map_apply_match (struct route_map_rule_list *match_list,
+                       struct prefix *prefix, route_map_object_t type,
+                       void *object)
+{
+  route_map_result_t ret = RMAP_NOMATCH;
+  struct route_map_rule *match;
+
+
+  /* Check all match rule and if there is no match rule, go to the
+     set statement. */
+  if (!match_list->head)
+    ret = RMAP_MATCH;
+  else
+    {
+      for (match = match_list->head; match; match = match->next)
+        {
+          /* Try each match statement in turn, If any do not return
+             RMAP_MATCH, return, otherwise continue on to next match 
+             statement. All match statements must match for end-result
+             to be a match. */
+          ret = (*match->cmd->func_apply) (match->value, prefix,
+                                           type, object);
+          if (ret != RMAP_MATCH)
+            return ret;
+        }
+    }
+  return ret;
+}
+
+/* Apply route map to the object. */
+route_map_result_t
+route_map_apply (struct route_map *map, struct prefix *prefix,
+                 route_map_object_t type, void *object)
+{
+  static int recursion = 0;
+  int ret = 0;
+  struct route_map_index *index;
+  struct route_map_rule *set;
+
+  if (recursion > RMAP_RECURSION_LIMIT)
+    {
+      zlog (NULL, LOG_WARNING,
+            "route-map recursion limit (%d) reached, discarding route",
+            RMAP_RECURSION_LIMIT);
+      recursion = 0;
+      return RMAP_DENYMATCH;
+    }
+
+  if (map == NULL)
+    return RMAP_DENYMATCH;
+
+  for (index = map->head; index; index = index->next)
+    {
+      /* Apply this index. */
+      ret = route_map_apply_match (&index->match_list, prefix, type, object);
+
+      /* Now we apply the matrix from above */
+      if (ret == RMAP_NOMATCH)
+        /* 'cont' from matrix - continue to next route-map sequence */
+        continue;
+      else if (ret == RMAP_MATCH)
+        {
+          if (index->type == RMAP_PERMIT)
+            /* 'action' */
+            {
+              /* permit+match must execute sets */
+              for (set = index->set_list.head; set; set = set->next)
+                ret = (*set->cmd->func_apply) (set->value, prefix,
+                                               type, object);
+
+              /* Call another route-map if available */
+              if (index->nextrm)
+                {
+                  struct route_map *nextrm =
+                                    route_map_lookup_by_name (index->nextrm);
+
+                  if (nextrm) /* Target route-map found, jump to it */
+                    {
+                      recursion++;
+                      ret = route_map_apply (nextrm, prefix, type, object);
+                      recursion--;
+                    }
+
+                  /* If nextrm returned 'deny', finish. */
+                  if (ret == RMAP_DENYMATCH)
+                    return ret;
+                }
+                
+              switch (index->exitpolicy)
+                {
+                  case RMAP_EXIT:
+                    return ret;
+                  case RMAP_NEXT:
+                    continue;
+                  case RMAP_GOTO:
+                    {
+                      /* Find the next clause to jump to */
+                      struct route_map_index *next = index->next;
+                      int nextpref = index->nextpref;
+
+                      while (next && next->pref < nextpref)
+                        {
+                          index = next;
+                          next = next->next;
+                        }
+                      if (next == NULL)
+                        {
+                          /* No clauses match! */
+                          return ret;
+                        }
+                    }
+                }
+            }
+          else if (index->type == RMAP_DENY)
+            /* 'deny' */
+            {
+                return RMAP_DENYMATCH;
+            }
+        }
+    }
+  /* Finally route-map does not match at all. */
+  return RMAP_DENYMATCH;
+}
+
+void
+route_map_add_hook (void (*func) (const char *))
+{
+  route_map_master.add_hook = func;
+}
+
+void
+route_map_delete_hook (void (*func) (const char *))
+{
+  route_map_master.delete_hook = func;
+}
+
+void
+route_map_event_hook (void (*func) (route_map_event_t, const char *))
+{
+  route_map_master.event_hook = func;
+}
+
+void
+route_map_init (void)
+{
+  /* Make vector for match and set. */
+  route_match_vec = vector_init (1);
+  route_set_vec = vector_init (1);
+}
+
+void
+route_map_finish (void)
+{
+  vector_free (route_match_vec);
+  route_match_vec = NULL;
+  vector_free (route_set_vec);
+  route_set_vec = NULL;
+  /* cleanup route_map */                                                    
+  while (route_map_master.head)                                              
+    route_map_delete (route_map_master.head); 
+}
+
+/* VTY related functions. */
+DEFUN (route_map,
+       route_map_cmd,
+       "route-map WORD (deny|permit) <1-65535>",
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n"
+       "Route map denies set operations\n"
+       "Route map permits set operations\n"
+       "Sequence to insert to/delete from existing route-map entry\n")
+{
+  int permit;
+  unsigned long pref;
+  struct route_map *map;
+  struct route_map_index *index;
+  char *endptr = NULL;
+
+  /* Permit check. */
+  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
+    permit = RMAP_PERMIT;
+  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
+    permit = RMAP_DENY;
+  else
+    {
+      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Preference check. */
+  pref = strtoul (argv[2], &endptr, 10);
+  if (pref == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "the fourth field must be positive integer%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pref == 0 || pref > 65535)
+    {
+      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get route map. */
+  map = route_map_get (argv[0]);
+  index = route_map_index_get (map, permit, pref);
+
+  vty->index = index;
+  vty->node = RMAP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_route_map_all,
+       no_route_map_all_cmd,
+       "no route-map WORD",
+       NO_STR
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n")
+{
+  struct route_map *map;
+
+  map = route_map_lookup_by_name (argv[0]);
+  if (map == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map %s%s",
+              argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  route_map_delete (map);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_route_map,
+       no_route_map_cmd,
+       "no route-map WORD (deny|permit) <1-65535>",
+       NO_STR
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n"
+       "Route map denies set operations\n"
+       "Route map permits set operations\n"
+       "Sequence to insert to/delete from existing route-map entry\n")
+{
+  int permit;
+  unsigned long pref;
+  struct route_map *map;
+  struct route_map_index *index;
+  char *endptr = NULL;
+
+  /* Permit check. */
+  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
+    permit = RMAP_PERMIT;
+  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
+    permit = RMAP_DENY;
+  else
+    {
+      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Preference. */
+  pref = strtoul (argv[2], &endptr, 10);
+  if (pref == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "the fourth field must be positive integer%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pref == 0 || pref > 65535)
+    {
+      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Existence check. */
+  map = route_map_lookup_by_name (argv[0]);
+  if (map == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map %s%s",
+              argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Lookup route map index. */
+  index = route_map_index_lookup (map, permit, pref);
+  if (index == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map entry %s %s%s", 
+              argv[0], argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Delete index from route map. */
+  route_map_index_delete (index, 1);
+
+  /* If this route rule is the last one, delete route map itself. */
+  if (route_map_empty (map))
+    route_map_delete (map);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rmap_onmatch_next,
+       rmap_onmatch_next_cmd,
+       "on-match next",
+       "Exit policy on matches\n"
+       "Next clause\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index)
+    index->exitpolicy = RMAP_NEXT;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_onmatch_next,
+       no_rmap_onmatch_next_cmd,
+       "no on-match next",
+       NO_STR
+       "Exit policy on matches\n"
+       "Next clause\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  
+  if (index)
+    index->exitpolicy = RMAP_EXIT;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rmap_onmatch_goto,
+       rmap_onmatch_goto_cmd,
+       "on-match goto <1-65535>",
+       "Exit policy on matches\n"
+       "Goto Clause number\n"
+       "Number\n")
+{
+  struct route_map_index *index = vty->index;
+  int d = 0;
+
+  if (index)
+    {
+      if (argc == 1 && argv[0])
+        VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
+      else
+        d = index->pref + 1;
+      
+      if (d <= index->pref)
+       {
+         /* Can't allow you to do that, Dave */
+         vty_out (vty, "can't jump backwards in route-maps%s", 
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      else
+       {
+         index->exitpolicy = RMAP_GOTO;
+         index->nextpref = d;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_onmatch_goto,
+       no_rmap_onmatch_goto_cmd,
+       "no on-match goto",
+       NO_STR
+       "Exit policy on matches\n"
+       "Goto Clause number\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index)
+    index->exitpolicy = RMAP_EXIT;
+  
+  return CMD_SUCCESS;
+}
+
+/* Cisco/GNU Zebra compatible ALIASes for on-match next */
+ALIAS (rmap_onmatch_goto,
+       rmap_continue_cmd,
+       "continue",
+       "Continue on a different entry within the route-map\n")
+
+ALIAS (no_rmap_onmatch_goto,
+       no_rmap_continue_cmd,
+       "no continue",
+       NO_STR
+       "Continue on a different entry within the route-map\n")
+
+/* GNU Zebra compatible */
+ALIAS (rmap_onmatch_goto,
+       rmap_continue_seq_cmd,
+       "continue <1-65535>",
+       "Continue on a different entry within the route-map\n"
+       "Route-map entry sequence number\n")
+
+ALIAS (no_rmap_onmatch_goto,
+       no_rmap_continue_seq,
+       "no continue <1-65535>",
+       NO_STR
+       "Continue on a different entry within the route-map\n"
+       "Route-map entry sequence number\n")
+
+DEFUN (rmap_show_name,
+       rmap_show_name_cmd,
+       "show route-map [WORD]",
+       SHOW_STR
+       "route-map information\n"
+       "route-map name\n")
+{
+    const char *name = NULL;
+    if (argc)
+      name = argv[0];
+    return vty_show_route_map (vty, name);
+}
+
+ALIAS (rmap_onmatch_goto,
+      rmap_continue_index_cmd,
+      "continue <1-65536>",
+      "Exit policy on matches\n"
+      "Goto Clause number\n")
+
+DEFUN (rmap_call,
+       rmap_call_cmd,
+       "call WORD",
+       "Jump to another Route-Map after match+set\n"
+       "Target route-map name\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  if (index)
+    {
+      if (index->nextrm)
+          XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
+      index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_call,
+       no_rmap_call_cmd,
+       "no call",
+       NO_STR
+       "Jump to another Route-Map after match+set\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index->nextrm)
+    {
+      XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
+      index->nextrm = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rmap_description,
+       rmap_description_cmd,
+       "description .LINE",
+       "Route-map comment\n"
+       "Comment describing this route-map rule\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  if (index)
+    {
+      if (index->description)
+       XFREE (MTYPE_TMP, index->description);
+      index->description = argv_concat (argv, argc, 0);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_description,
+       no_rmap_description_cmd,
+       "no description",
+       NO_STR
+       "Route-map comment\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  if (index)
+    {
+      if (index->description)
+       XFREE (MTYPE_TMP, index->description);
+      index->description = NULL;
+    }
+  return CMD_SUCCESS;
+}
+
+/* Configuration write function. */
+static int
+route_map_config_write (struct vty *vty)
+{
+  struct route_map *map;
+  struct route_map_index *index;
+  struct route_map_rule *rule;
+  int first = 1;
+  int write = 0;
+
+  for (map = route_map_master.head; map; map = map->next)
+    for (index = map->head; index; index = index->next)
+      {
+       if (!first)
+         vty_out (vty, "!%s", VTY_NEWLINE);
+       else
+         first = 0;
+
+       vty_out (vty, "route-map %s %s %d%s", 
+                map->name,
+                route_map_type_str (index->type),
+                index->pref, VTY_NEWLINE);
+
+       if (index->description)
+         vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
+
+       for (rule = index->match_list.head; rule; rule = rule->next)
+         vty_out (vty, " match %s %s%s", rule->cmd->str, 
+                  rule->rule_str ? rule->rule_str : "",
+                  VTY_NEWLINE);
+
+       for (rule = index->set_list.head; rule; rule = rule->next)
+         vty_out (vty, " set %s %s%s", rule->cmd->str,
+                  rule->rule_str ? rule->rule_str : "",
+                  VTY_NEWLINE);
+   if (index->nextrm)
+     vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
+       if (index->exitpolicy == RMAP_GOTO)
+      vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
+       if (index->exitpolicy == RMAP_NEXT)
+         vty_out (vty," on-match next%s", VTY_NEWLINE);
+       
+       write++;
+      }
+  return write;
+}
+
+/* Route map node structure. */
+static struct cmd_node rmap_node =
+{
+  RMAP_NODE,
+  "%s(config-route-map)# ",
+  1
+};
+
+/* Common route map rules */
+
+void *
+route_map_rule_tag_compile (const char *arg)
+{
+  unsigned long int tmp;
+  char *endptr;
+  route_tag_t *tag;
+
+  errno = 0;
+  tmp = strtoul(arg, &endptr, 0);
+  if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX)
+    return NULL;
+
+  tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag));
+  *tag = tmp;
+
+  return tag;
+}
+
+void
+route_map_rule_tag_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Initialization of route map vector. */
+void
+route_map_init_vty (void)
+{
+  /* Install route map top node. */
+  install_node (&rmap_node, route_map_config_write);
+
+  /* Install route map commands. */
+  install_default (RMAP_NODE);
+  install_element (CONFIG_NODE, &route_map_cmd);
+  install_element (CONFIG_NODE, &no_route_map_cmd);
+  install_element (CONFIG_NODE, &no_route_map_all_cmd);
+
+  /* Install the on-match stuff */
+  install_element (RMAP_NODE, &route_map_cmd);
+  install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
+  install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
+  install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
+  install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
+  
+  /* Install the continue stuff (ALIAS of on-match). */
+  install_element (RMAP_NODE, &rmap_continue_cmd);
+  install_element (RMAP_NODE, &no_rmap_continue_cmd);
+  install_element (RMAP_NODE, &rmap_continue_index_cmd);
+  
+  /* Install the call stuff. */
+  install_element (RMAP_NODE, &rmap_call_cmd);
+  install_element (RMAP_NODE, &no_rmap_call_cmd);
+
+  /* Install description commands. */
+  install_element (RMAP_NODE, &rmap_description_cmd);
+  install_element (RMAP_NODE, &no_rmap_description_cmd);
+   
+  /* Install show command */
+  install_element (ENABLE_NODE, &rmap_show_name_cmd);
+}
diff --git a/lib/routemap.h b/lib/routemap.h
new file mode 100644 (file)
index 0000000..68129e1
--- /dev/null
@@ -0,0 +1,204 @@
+/* Route map function.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_ROUTEMAP_H
+#define _ZEBRA_ROUTEMAP_H
+
+#include "prefix.h"
+
+/* Route map's type. */
+enum route_map_type
+{
+  RMAP_PERMIT,
+  RMAP_DENY,
+  RMAP_ANY
+};
+
+typedef enum 
+{
+  RMAP_MATCH,
+  RMAP_DENYMATCH,
+  RMAP_NOMATCH,
+  RMAP_ERROR,
+  RMAP_OKAY
+} route_map_result_t;
+
+typedef enum
+{
+  RMAP_RIP,
+  RMAP_RIPNG,
+  RMAP_BABEL,
+  RMAP_OSPF,
+  RMAP_OSPF6,
+  RMAP_BGP,
+  RMAP_ZEBRA,
+  RMAP_ISIS,
+} route_map_object_t;
+
+typedef enum
+{
+  RMAP_EXIT,
+  RMAP_GOTO,
+  RMAP_NEXT
+} route_map_end_t;
+
+typedef enum
+{
+  RMAP_EVENT_SET_ADDED,
+  RMAP_EVENT_SET_DELETED,
+  RMAP_EVENT_SET_REPLACED,
+  RMAP_EVENT_MATCH_ADDED,
+  RMAP_EVENT_MATCH_DELETED,
+  RMAP_EVENT_MATCH_REPLACED,
+  RMAP_EVENT_INDEX_ADDED,
+  RMAP_EVENT_INDEX_DELETED
+} route_map_event_t;
+
+/* Depth limit in RMAP recursion using RMAP_CALL. */
+#define RMAP_RECURSION_LIMIT      10
+
+/* Route map rule structure for matching and setting. */
+struct route_map_rule_cmd
+{
+  /* Route map rule name (e.g. as-path, metric) */
+  const char *str;
+
+  /* Function for value set or match. */
+  route_map_result_t (*func_apply)(void *, struct prefix *, 
+                                  route_map_object_t, void *);
+
+  /* Compile argument and return result as void *. */
+  void *(*func_compile)(const char *);
+
+  /* Free allocated value by func_compile (). */
+  void (*func_free)(void *);
+};
+
+/* Route map apply error. */
+enum
+{
+  /* Route map rule is missing. */
+  RMAP_RULE_MISSING = 1,
+
+  /* Route map rule can't compile */
+  RMAP_COMPILE_ERROR
+};
+
+/* Route map rule list. */
+struct route_map_rule_list
+{
+  struct route_map_rule *head;
+  struct route_map_rule *tail;
+};
+
+/* Route map index structure. */
+struct route_map_index
+{
+  struct route_map *map;
+  char *description;
+
+  /* Preference of this route map rule. */
+  int pref;
+
+  /* Route map type permit or deny. */
+  enum route_map_type type;                    
+
+  /* Do we follow old rules, or hop forward? */
+  route_map_end_t exitpolicy;
+
+  /* If we're using "GOTO", to where do we go? */
+  int nextpref;
+
+  /* If we're using "CALL", to which route-map do ew go? */
+  char *nextrm;
+
+  /* Matching rule list. */
+  struct route_map_rule_list match_list;
+  struct route_map_rule_list set_list;
+
+  /* Make linked list. */
+  struct route_map_index *next;
+  struct route_map_index *prev;
+};
+
+/* Route map list structure. */
+struct route_map
+{
+  /* Name of route map. */
+  char *name;
+
+  /* Route map's rule. */
+  struct route_map_index *head;
+  struct route_map_index *tail;
+
+  /* Make linked list. */
+  struct route_map *next;
+  struct route_map *prev;
+};
+
+/* Prototypes. */
+extern void route_map_init (void);
+extern void route_map_init_vty (void);
+extern void route_map_finish (void);
+
+/* Add match statement to route map. */
+extern int route_map_add_match (struct route_map_index *index,
+                               const char *match_name,
+                               const char *match_arg);
+
+/* Delete specified route match rule. */
+extern int route_map_delete_match (struct route_map_index *index,
+                                  const char *match_name,
+                                  const char *match_arg);
+
+/* Add route-map set statement to the route map. */
+extern int route_map_add_set (struct route_map_index *index, 
+                             const char *set_name,
+                             const char *set_arg);
+
+/* Delete route map set rule. */
+extern int route_map_delete_set (struct route_map_index *index,
+                                 const char *set_name,
+                                 const char *set_arg);
+
+/* Install rule command to the match list. */
+extern void route_map_install_match (struct route_map_rule_cmd *cmd);
+
+/* Install rule command to the set list. */
+extern void route_map_install_set (struct route_map_rule_cmd *cmd);
+
+/* Lookup route map by name. */
+extern struct route_map * route_map_lookup_by_name (const char *name);
+
+/* Apply route map to the object. */
+extern route_map_result_t route_map_apply (struct route_map *map,
+                                           struct prefix *,
+                                           route_map_object_t object_type,
+                                           void *object);
+
+extern void route_map_add_hook (void (*func) (const char *));
+extern void route_map_delete_hook (void (*func) (const char *));
+extern void route_map_event_hook (void (*func) (route_map_event_t, const char *));
+
+extern void *route_map_rule_tag_compile (const char *arg);
+extern void route_map_rule_tag_free (void *rule);
+
+#endif /* _ZEBRA_ROUTEMAP_H */
diff --git a/lib/sigevent.c b/lib/sigevent.c
new file mode 100644 (file)
index 0000000..a120028
--- /dev/null
@@ -0,0 +1,367 @@
+/* Quagga signal handling functions.
+ * Copyright (C) 2004 Paul Jakma,
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include <log.h>
+#include <memory.h>
+
+#ifdef SA_SIGINFO
+#ifdef HAVE_UCONTEXT_H
+#ifdef GNU_LINUX
+/* get REG_EIP from ucontext.h */
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif /* __USE_GNU */
+#endif /* GNU_LINUX */
+#include <ucontext.h>
+#endif /* HAVE_UCONTEXT_H */
+#endif /* SA_SIGINFO */
+
+
+/* master signals descriptor struct */
+struct quagga_sigevent_master_t
+{
+  struct thread *t;
+
+  struct quagga_signal_t *signals; 
+  int sigc;
+  
+  volatile sig_atomic_t caught;
+} sigmaster;
+
+/* Generic signal handler 
+ * Schedules signal event thread
+ */
+static void
+quagga_signal_handler (int signo)
+{
+  int i;
+  struct quagga_signal_t *sig;
+  
+  for (i = 0; i < sigmaster.sigc; i++)
+    {
+      sig = &(sigmaster.signals[i]);
+      
+      if (sig->signal == signo)
+        sig->caught = 1;
+    }
+  
+  sigmaster.caught = 1;
+} 
+
+/* check if signals have been caught and run appropriate handlers */
+int
+quagga_sigevent_process (void)
+{
+  struct quagga_signal_t *sig;
+  int i;
+#ifdef SIGEVENT_BLOCK_SIGNALS
+  /* shouldnt need to block signals, but potentially may be needed */
+  sigset_t newmask, oldmask;
+
+  /*
+   * Block most signals, but be careful not to defer SIGTRAP because
+   * doing so breaks gdb, at least on NetBSD 2.0.  Avoid asking to
+   * block SIGKILL, just because we shouldn't be able to do so.
+   */
+  sigfillset (&newmask);
+  sigdelset (&newmask, SIGTRAP);
+  sigdelset (&newmask, SIGKILL);
+   
+  if ( (sigprocmask (SIG_BLOCK, &newmask, &oldmask)) < 0)
+    {
+      zlog_err ("quagga_signal_timer: couldnt block signals!");
+      return -1;
+    }
+#endif /* SIGEVENT_BLOCK_SIGNALS */
+
+  if (sigmaster.caught > 0)
+    {
+      sigmaster.caught = 0;
+      /* must not read or set sigmaster.caught after here,
+       * race condition with per-sig caught flags if one does
+       */
+      
+      for (i = 0; i < sigmaster.sigc; i++)
+        {
+          sig = &(sigmaster.signals[i]);
+
+          if (sig->caught > 0)
+            {
+              sig->caught = 0;
+              sig->handler ();
+            }
+        }
+    }
+
+#ifdef SIGEVENT_BLOCK_SIGNALS
+  if ( sigprocmask (SIG_UNBLOCK, &oldmask, NULL) < 0 );
+    return -1;
+#endif /* SIGEVENT_BLOCK_SIGNALS */
+
+  return 0;
+}
+
+#ifdef SIGEVENT_SCHEDULE_THREAD
+/* timer thread to check signals. Shouldnt be needed */
+int
+quagga_signal_timer (struct thread *t)
+{
+  struct quagga_sigevent_master_t *sigm;
+  struct quagga_signal_t *sig;
+  int i;
+
+  sigm = THREAD_ARG (t);
+  sigm->t = thread_add_timer (sigm->t->master, quagga_signal_timer, &sigmaster,
+                              QUAGGA_SIGNAL_TIMER_INTERVAL);
+  return quagga_sigevent_process ();
+}
+#endif /* SIGEVENT_SCHEDULE_THREAD */
+
+/* Initialization of signal handles. */
+/* Signal wrapper. */
+static int
+signal_set (int signo)
+{
+  int ret;
+  struct sigaction sig;
+  struct sigaction osig;
+
+  sig.sa_handler = &quagga_signal_handler;
+  sigfillset (&sig.sa_mask);
+  sig.sa_flags = 0;
+  if (signo == SIGALRM) {
+#ifdef SA_INTERRUPT
+      sig.sa_flags |= SA_INTERRUPT; /* SunOS */
+#endif
+  } else {
+#ifdef SA_RESTART
+      sig.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+  }
+
+  ret = sigaction (signo, &sig, &osig);
+  if (ret < 0) 
+    return ret;
+  else
+    return 0;
+}
+
+#ifdef SA_SIGINFO
+
+/* XXX This function should be enhanced to support more platforms
+       (it currently works only on Linux/x86). */
+static void *
+program_counter(void *context)
+{
+#ifdef HAVE_UCONTEXT_H
+#ifdef GNU_LINUX
+  /* these are from GNU libc, rather than Linux, strictly speaking */
+# if defined(REG_EIP)
+#  define REG_INDEX REG_EIP
+# elif defined(REG_RIP)
+#  define REG_INDEX REG_RIP
+# elif defined(__powerpc__)
+#  define REG_INDEX 32
+# endif
+#elif defined(SUNOS_5) /* !GNU_LINUX */
+# define REG_INDEX REG_PC
+#endif /* GNU_LINUX */
+
+#ifdef REG_INDEX
+# ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS
+#  define REGS gregs[REG_INDEX]
+# elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS)
+#  define REGS uc_regs->gregs[REG_INDEX]
+# endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */
+#endif /* REG_INDEX */
+
+#ifdef REGS
+  if (context)
+    return (void *)(((ucontext_t *)context)->uc_mcontext.REGS);
+#elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP) 
+  /* older Linux / struct pt_regs ? */
+  if (context)
+    return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip);
+#endif /* REGS */
+
+#endif /* HAVE_UCONTEXT_H */
+  return NULL;
+}
+
+#endif /* SA_SIGINFO */
+
+static void __attribute__ ((noreturn))
+exit_handler(int signo
+#ifdef SA_SIGINFO
+            , siginfo_t *siginfo, void *context
+#endif
+           )
+{
+  zlog_signal(signo, "exiting..."
+#ifdef SA_SIGINFO
+             , siginfo, program_counter(context)
+#endif
+            );
+  _exit(128+signo);
+}
+
+static void __attribute__ ((noreturn))
+core_handler(int signo
+#ifdef SA_SIGINFO
+            , siginfo_t *siginfo, void *context
+#endif
+           )
+{
+  zlog_signal(signo, "aborting..."
+#ifdef SA_SIGINFO
+             , siginfo, program_counter(context)
+#endif
+            );
+  /* dump memory stats on core */
+  log_memstats_stderr ("core_handler");
+  abort();
+}
+
+static void
+trap_default_signals(void)
+{
+  static const int core_signals[] = {
+    SIGQUIT,
+    SIGILL,
+#ifdef SIGEMT
+    SIGEMT,
+#endif
+    SIGFPE,
+    SIGBUS,
+    SIGSEGV,
+#ifdef SIGSYS
+    SIGSYS,
+#endif
+#ifdef SIGXCPU
+    SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+    SIGXFSZ,
+#endif
+  };
+  static const int exit_signals[] = {
+    SIGHUP,
+    SIGINT,
+    SIGALRM,
+    SIGTERM,
+    SIGUSR1,
+    SIGUSR2,
+#ifdef SIGPOLL
+    SIGPOLL, 
+#endif
+#ifdef SIGVTALRM
+    SIGVTALRM,
+#endif
+#ifdef SIGSTKFLT
+    SIGSTKFLT, 
+#endif
+  };
+  static const int ignore_signals[] = {
+    SIGPIPE,
+  };
+  static const struct {
+    const int *sigs;
+    u_int nsigs;
+    void (*handler)(int signo
+#ifdef SA_SIGINFO
+                   , siginfo_t *info, void *context
+#endif
+                  );
+  } sigmap[] = {
+    { core_signals, array_size(core_signals), core_handler},
+    { exit_signals, array_size(exit_signals), exit_handler},
+    { ignore_signals, array_size(ignore_signals), NULL},
+  };
+  u_int i;
+
+  for (i = 0; i < array_size(sigmap); i++)
+    {
+      u_int j;
+
+      for (j = 0; j < sigmap[i].nsigs; j++)
+        {
+         struct sigaction oact;
+         if ((sigaction(sigmap[i].sigs[j],NULL,&oact) == 0) &&
+             (oact.sa_handler == SIG_DFL))
+           {
+             struct sigaction act;
+             sigfillset (&act.sa_mask);
+             if (sigmap[i].handler == NULL)
+               {
+                 act.sa_handler = SIG_IGN;
+                 act.sa_flags = 0;
+               }
+             else
+               {
+#ifdef SA_SIGINFO
+                 /* Request extra arguments to signal handler. */
+                 act.sa_sigaction = sigmap[i].handler;
+                 act.sa_flags = SA_SIGINFO;
+#else
+                 act.sa_handler = sigmap[i].handler;
+                 act.sa_flags = 0;
+#endif
+               }
+             if (sigaction(sigmap[i].sigs[j],&act,NULL) < 0)
+               zlog_warn("Unable to set signal handler for signal %d: %s",
+                         sigmap[i].sigs[j],safe_strerror(errno));
+
+           }
+        }
+    }
+}
+
+void 
+signal_init (struct thread_master *m, int sigc, 
+             struct quagga_signal_t signals[])
+{
+
+  int i = 0;
+  struct quagga_signal_t *sig;
+
+  /* First establish some default handlers that can be overridden by
+     the application. */
+  trap_default_signals();
+  
+  while (i < sigc)
+    {
+      sig = &signals[i];
+      if ( signal_set (sig->signal) < 0 )
+        exit (-1);
+      i++;
+    }
+
+  sigmaster.sigc = sigc;
+  sigmaster.signals = signals;
+
+#ifdef SIGEVENT_SCHEDULE_THREAD  
+  sigmaster.t = 
+    thread_add_timer (m, quagga_signal_timer, &sigmaster, 
+                      QUAGGA_SIGNAL_TIMER_INTERVAL);
+#endif /* SIGEVENT_SCHEDULE_THREAD */
+}
diff --git a/lib/sigevent.h b/lib/sigevent.h
new file mode 100644 (file)
index 0000000..248fa2c
--- /dev/null
@@ -0,0 +1,52 @@
+/* 
+ * Quagga Signal handling header.
+ *
+ * Copyright (C) 2004 Paul Jakma.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _QUAGGA_SIGNAL_H
+#define _QUAGGA_SIGNAL_H
+
+#include <thread.h>
+
+#define QUAGGA_SIGNAL_TIMER_INTERVAL 2L
+
+struct quagga_signal_t
+{
+  int signal;                     /* signal number    */
+  void (*handler) (void);         /* handler to call  */
+
+  volatile sig_atomic_t caught;   /* private member   */
+};
+
+/* initialise sigevent system
+ * takes:
+ * - pointer to valid struct thread_master
+ * - number of elements in passed in signals array
+ * - array of quagga_signal_t's describing signals to handle
+ *   and handlers to use for each signal
+ */
+extern void signal_init (struct thread_master *m, int sigc, 
+                         struct quagga_signal_t *signals);
+
+/* check whether there are signals to handle, process any found */
+extern int quagga_sigevent_process (void);
+
+#endif /* _QUAGGA_SIGNAL_H */
diff --git a/lib/smux.c b/lib/smux.c
new file mode 100644 (file)
index 0000000..03da99f
--- /dev/null
@@ -0,0 +1,1502 @@
+/* SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#if defined HAVE_SNMP && defined SNMP_SMUX
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "log.h"
+#include "thread.h"
+#include "linklist.h"
+#include "command.h"
+#include <lib/version.h>
+#include "memory.h"
+#include "sockunion.h"
+#include "smux.h"
+
+#define SMUX_PORT_DEFAULT 199
+
+#define SMUXMAXPKTSIZE    1500
+#define SMUXMAXSTRLEN      256
+
+#define SMUX_OPEN       (ASN_APPLICATION | ASN_CONSTRUCTOR | 0)
+#define SMUX_CLOSE      (ASN_APPLICATION | ASN_PRIMITIVE | 1)
+#define SMUX_RREQ       (ASN_APPLICATION | ASN_CONSTRUCTOR | 2)
+#define SMUX_RRSP       (ASN_APPLICATION | ASN_PRIMITIVE | 3)
+#define SMUX_SOUT       (ASN_APPLICATION | ASN_PRIMITIVE | 4)
+
+#define SMUX_GET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0)
+#define SMUX_GETNEXT    (ASN_CONTEXT | ASN_CONSTRUCTOR | 1)
+#define SMUX_GETRSP     (ASN_CONTEXT | ASN_CONSTRUCTOR | 2)
+#define SMUX_SET       (ASN_CONTEXT | ASN_CONSTRUCTOR | 3)
+#define SMUX_TRAP      (ASN_CONTEXT | ASN_CONSTRUCTOR | 4)
+
+#define SMUX_MAX_FAILURE 3
+
+/* SNMP tree. */
+struct subtree
+{
+  /* Tree's oid. */
+  oid name[MAX_OID_LEN];
+  u_char name_len;
+
+  /* List of the variables. */
+  struct variable *variables;
+
+  /* Length of the variables list. */
+  int variables_num;
+
+  /* Width of the variables list. */
+  int variables_width;
+
+  /* Registered flag. */
+  int registered;
+};
+
+#define min(A,B) ((A) < (B) ? (A) : (B))
+
+enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ};
+
+void smux_event (enum smux_event, int);
+
+
+/* SMUX socket. */
+int smux_sock = -1;
+
+/* SMUX subtree list. */
+struct list *treelist;
+
+/* SMUX oid. */
+oid *smux_oid = NULL;
+size_t smux_oid_len;
+
+/* SMUX password. */
+char *smux_passwd = NULL;
+
+/* SMUX read threads. */
+struct thread *smux_read_thread;
+
+/* SMUX connect thrads. */
+struct thread *smux_connect_thread;
+
+/* SMUX debug flag. */
+int debug_smux = 0;
+
+/* SMUX failure count. */
+int fail = 0;
+
+/* SMUX node. */
+static struct cmd_node smux_node =
+{
+  SMUX_NODE,
+  ""                            /* SMUX has no interface. */
+};
+
+/* thread master */
+static struct thread_master *smux_master;
+
+static int
+oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len)
+{
+  int i;
+
+  for (i = 0; i < min (o1_len, o2_len); i++)
+    {
+      if (o1[i] < o2[i])
+       return -1;
+      else if (o1[i] > o2[i])
+       return 1;
+    }
+  if (o1_len < o2_len)
+    return -1;
+
+  return 0;
+}
+
+static void
+smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len)
+{
+  unsigned int i;
+  int first = 1;
+  char buf[MAX_OID_LEN * 3];
+
+  buf[0] = '\0';
+
+  for (i = 0; i < oid_len; i++)
+    {
+      sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) oid[i]);
+      first = 0;
+    }
+  zlog_debug ("%s: %s", prefix, buf);
+}
+
+static int
+smux_socket (void)
+{
+  int ret;
+#ifdef HAVE_IPV6
+  struct addrinfo hints, *res0, *res;
+  int gai;
+#else
+  struct sockaddr_in serv;
+  struct servent *sp;
+#endif
+  int sock = 0;
+
+#ifdef HAVE_IPV6
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = PF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  gai = getaddrinfo(NULL, "smux", &hints, &res0);
+  if (gai == EAI_SERVICE)
+    {
+      char servbuf[NI_MAXSERV];
+      sprintf(servbuf,"%d",SMUX_PORT_DEFAULT);
+      servbuf[sizeof (servbuf) - 1] = '\0';
+      gai = getaddrinfo(NULL, servbuf, &hints, &res0);
+    }
+  if (gai)
+    {
+      zlog_warn("Cannot locate loopback service smux");
+      return -1;
+    }
+  for(res=res0; res; res=res->ai_next)
+    {
+      if (res->ai_family != AF_INET 
+#ifdef HAVE_IPV6
+         && res->ai_family != AF_INET6
+#endif /* HAVE_IPV6 */
+         )
+       continue;
+
+      sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+      if (sock < 0)
+       continue;
+      sockopt_reuseaddr (sock);
+      sockopt_reuseport (sock);
+      ret = connect (sock, res->ai_addr, res->ai_addrlen);
+      if (ret < 0)
+       {
+         close(sock);
+         sock = -1;
+         continue;
+       }
+      break;
+    }
+  freeaddrinfo(res0);
+  if (sock < 0)
+    zlog_warn ("Can't connect to SNMP agent with SMUX");
+#else
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      zlog_warn ("Can't make socket for SNMP");
+      return -1;
+    }
+
+  memset (&serv, 0, sizeof (struct sockaddr_in));
+  serv.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  serv.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  sp = getservbyname ("smux", "tcp");
+  if (sp != NULL) 
+    serv.sin_port = sp->s_port;
+  else
+    serv.sin_port = htons (SMUX_PORT_DEFAULT);
+
+  serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+  sockopt_reuseaddr (sock);
+  sockopt_reuseport (sock);
+
+  ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in));
+  if (ret < 0)
+    {
+      close (sock);
+      smux_sock = -1;
+      zlog_warn ("Can't connect to SNMP agent with SMUX");
+      return -1;
+    }
+#endif
+  return sock;
+}
+
+static void
+smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
+                  long errindex, u_char val_type, void *arg, size_t arg_len)
+{
+  u_char buf[BUFSIZ];
+  u_char *ptr, *h1, *h1e, *h2, *h2e;
+  size_t len, length;
+
+  ptr = buf;
+  len = BUFSIZ;
+  length = len;
+
+  if (debug_smux)
+    {
+      zlog_debug ("SMUX GETRSP send");
+      zlog_debug ("SMUX GETRSP reqid: %ld", reqid);
+    }
+
+  h1 = ptr;
+  /* Place holder h1 for complete sequence */
+  ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
+  h1e = ptr;
+  ptr = asn_build_int (ptr, &len,
+                      (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      &reqid, sizeof (reqid));
+
+  if (debug_smux)
+    zlog_debug ("SMUX GETRSP errstat: %ld", errstat);
+
+  ptr = asn_build_int (ptr, &len,
+                      (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      &errstat, sizeof (errstat));
+  if (debug_smux)
+    zlog_debug ("SMUX GETRSP errindex: %ld", errindex);
+
+  ptr = asn_build_int (ptr, &len,
+                      (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      &errindex, sizeof (errindex));
+
+  h2 = ptr;
+  /* Place holder h2 for one variable */
+  ptr = asn_build_sequence (ptr, &len, 
+                          (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
+                          0);
+  h2e = ptr;
+
+  ptr = snmp_build_var_op (ptr, objid, &objid_len, 
+                          val_type, arg_len, arg, &len);
+
+  /* Now variable size is known, fill in size */
+  asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e);
+
+  /* Fill in size of whole sequence */
+  asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e);
+
+  if (debug_smux)
+    zlog_debug ("SMUX getresp send: %td", (ptr - buf));
+  
+  send (smux_sock, buf, (ptr - buf), 0);
+}
+
+static u_char *
+smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len,
+          size_t *var_val_len,
+          u_char *var_val_type,
+          void **var_value)
+{
+  u_char type;
+  u_char val_type;
+  size_t val_len;
+  u_char *val;
+
+  if (debug_smux)
+    zlog_debug ("SMUX var parse: len %zd", len);
+
+  /* Parse header. */
+  ptr = asn_parse_header (ptr, &len, &type);
+  
+  if (debug_smux)
+    {
+      zlog_debug ("SMUX var parse: type %d len %zd", type, len);
+      zlog_debug ("SMUX var parse: type must be %d", 
+                (ASN_SEQUENCE | ASN_CONSTRUCTOR));
+    }
+
+  /* Parse var option. */
+  *objid_len = MAX_OID_LEN;
+  ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type, 
+                         &val_len, &val, &len);
+
+  if (var_val_len)
+    *var_val_len = val_len;
+
+  if (var_value)
+    *var_value = (void*) val;
+
+  if (var_val_type)
+    *var_val_type = val_type;
+
+  /* Requested object id length is objid_len. */
+  if (debug_smux)
+    smux_oid_dump ("Request OID", objid, *objid_len);
+
+  if (debug_smux)
+    zlog_debug ("SMUX val_type: %d", val_type);
+
+  /* Check request value type. */
+  if (debug_smux)
+  switch (val_type)
+    {
+    case ASN_NULL:
+      /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to
+         ASN_NULL. */
+      zlog_debug ("ASN_NULL");
+      break;
+
+    case ASN_INTEGER:
+      zlog_debug ("ASN_INTEGER");
+      break;
+    case ASN_COUNTER:
+    case ASN_GAUGE:
+    case ASN_TIMETICKS:
+    case ASN_UINTEGER:
+      zlog_debug ("ASN_COUNTER");
+      break;
+    case ASN_COUNTER64:
+      zlog_debug ("ASN_COUNTER64");
+      break;
+    case ASN_IPADDRESS:
+      zlog_debug ("ASN_IPADDRESS");
+      break;
+    case ASN_OCTET_STR:
+      zlog_debug ("ASN_OCTET_STR");
+      break;
+    case ASN_OPAQUE:
+    case ASN_NSAP:
+    case ASN_OBJECT_ID:
+      zlog_debug ("ASN_OPAQUE");
+      break;
+    case SNMP_NOSUCHOBJECT:
+      zlog_debug ("SNMP_NOSUCHOBJECT");
+      break;
+    case SNMP_NOSUCHINSTANCE:
+      zlog_debug ("SNMP_NOSUCHINSTANCE");
+      break;
+    case SNMP_ENDOFMIBVIEW:
+      zlog_debug ("SNMP_ENDOFMIBVIEW");
+      break;
+    case ASN_BIT_STR:
+      zlog_debug ("ASN_BIT_STR");
+      break;
+    default:
+      zlog_debug ("Unknown type");
+      break;
+    }
+  return ptr;
+}
+
+/* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on
+   ucd-snmp smux and as such suppose, that the peer receives in the message
+   only one variable. Fortunately, IBM seems to do the same in AIX. */
+
+static int
+smux_set (oid *reqid, size_t *reqid_len,
+          u_char val_type, void *val, size_t val_len, int action)
+{
+  int j;
+  struct subtree *subtree;
+  struct variable *v;
+  int subresult;
+  oid *suffix;
+  size_t suffix_len;
+  int result;
+  u_char *statP = NULL;
+  WriteMethod *write_method = NULL;
+  struct listnode *node, *nnode;
+
+  /* Check */
+  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
+    {
+      subresult = oid_compare_part (reqid, *reqid_len,
+                                    subtree->name, subtree->name_len);
+
+      /* Subtree matched. */
+      if (subresult == 0)
+        {
+          /* Prepare suffix. */
+          suffix = reqid + subtree->name_len;
+          suffix_len = *reqid_len - subtree->name_len;
+          result = subresult;
+
+          /* Check variables. */
+          for (j = 0; j < subtree->variables_num; j++)
+            {
+              v = &subtree->variables[j];
+
+              /* Always check suffix */
+              result = oid_compare_part (suffix, suffix_len,
+                                         v->name, v->namelen);
+
+              /* This is exact match so result must be zero. */
+              if (result == 0)
+                {
+                  if (debug_smux)
+                    zlog_debug ("SMUX function call index is %d", v->magic);
+                 
+                  statP = (*v->findVar) (v, suffix, &suffix_len, 1,
+                                        &val_len, &write_method);
+
+                  if (write_method)
+                    {
+                      return (*write_method)(action, val, val_type, val_len,
+                                            statP, suffix, suffix_len);
+                    }
+                  else
+                    {
+                      return SNMP_ERR_READONLY;
+                    }
+                }
+
+              /* If above execution is failed or oid is small (so
+                 there is no further match). */
+              if (result < 0)
+                return SNMP_ERR_NOSUCHNAME;
+            }
+        }
+    }
+  return SNMP_ERR_NOSUCHNAME;
+}
+
+static int
+smux_get (oid *reqid, size_t *reqid_len, int exact, 
+         u_char *val_type,void **val, size_t *val_len)
+{
+  int j;
+  struct subtree *subtree;
+  struct variable *v;
+  int subresult;
+  oid *suffix;
+  size_t suffix_len;
+  int result;
+  WriteMethod *write_method=NULL;
+  struct listnode *node, *nnode;
+
+  /* Check */
+  for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree))
+    {
+      subresult = oid_compare_part (reqid, *reqid_len, 
+                                   subtree->name, subtree->name_len);
+
+      /* Subtree matched. */
+      if (subresult == 0)
+       {
+         /* Prepare suffix. */
+         suffix = reqid + subtree->name_len;
+         suffix_len = *reqid_len - subtree->name_len;
+         result = subresult;
+
+         /* Check variables. */
+         for (j = 0; j < subtree->variables_num; j++)
+           {
+             v = &subtree->variables[j];
+
+             /* Always check suffix */
+             result = oid_compare_part (suffix, suffix_len,
+                                        v->name, v->namelen);
+
+             /* This is exact match so result must be zero. */
+             if (result == 0)
+               {
+                 if (debug_smux)
+                   zlog_debug ("SMUX function call index is %d", v->magic);
+
+                 *val = (*v->findVar) (v, suffix, &suffix_len, exact,
+                                       val_len, &write_method);
+
+                 /* There is no instance. */
+                 if (*val == NULL)
+                   return SNMP_NOSUCHINSTANCE;
+
+                 /* Call is suceed. */
+                 *val_type = v->type;
+
+                 return 0;
+               }
+
+             /* If above execution is failed or oid is small (so
+                 there is no further match). */
+             if (result < 0)
+               return SNMP_ERR_NOSUCHNAME;
+           }
+       }
+    }
+  return SNMP_ERR_NOSUCHNAME;
+}
+
+static int
+smux_getnext (oid *reqid, size_t *reqid_len, int exact, 
+             u_char *val_type,void **val, size_t *val_len)
+{
+  int j;
+  oid save[MAX_OID_LEN];
+  int savelen = 0;
+  struct subtree *subtree;
+  struct variable *v;
+  int subresult;
+  oid *suffix;
+  size_t suffix_len;
+  int result;
+  WriteMethod *write_method=NULL;
+  struct listnode *node, *nnode;
+
+
+  /* Save incoming request. */
+  oid_copy (save, reqid, *reqid_len);
+  savelen = *reqid_len;
+
+  /* Check */
+  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
+    {
+      subresult = oid_compare_part (reqid, *reqid_len, 
+                                   subtree->name, subtree->name_len);
+
+      /* If request is in the tree. The agent has to make sure we
+         only receive requests we have registered for. */
+      /* Unfortunately, that's not true. In fact, a SMUX subagent has to
+         behave as if it manages the whole SNMP MIB tree itself. It's the
+         duty of the master agent to collect the best answer and return it
+         to the manager. See RFC 1227 chapter 3.1.6 for the glory details
+         :-). ucd-snmp really behaves bad here as it actually might ask
+         multiple times for the same GETNEXT request as it throws away the
+         answer when it expects it in a different subtree and might come
+         back later with the very same request. --jochen */
+
+      if (subresult <= 0)
+       {
+         /* Prepare suffix. */
+         suffix = reqid + subtree->name_len;
+         suffix_len = *reqid_len - subtree->name_len;
+         if (subresult < 0)
+           {
+             oid_copy(reqid, subtree->name, subtree->name_len);
+             *reqid_len = subtree->name_len;
+           }
+         for (j = 0; j < subtree->variables_num; j++)
+           {
+             result = subresult;
+             v = &subtree->variables[j];
+
+             /* Next then check result >= 0. */
+             if (result == 0)
+               result = oid_compare_part (suffix, suffix_len,
+                                          v->name, v->namelen);
+
+             if (result <= 0)
+               {
+                 if (debug_smux)
+                   zlog_debug ("SMUX function call index is %d", v->magic);
+                 if(result<0)
+                   {
+                     oid_copy(suffix, v->name, v->namelen);
+                     suffix_len = v->namelen;
+                   }
+                 *val = (*v->findVar) (v, suffix, &suffix_len, exact,
+                                       val_len, &write_method);
+                 *reqid_len = suffix_len + subtree->name_len;
+                 if (*val)
+                   {
+                     *val_type = v->type;
+                     return 0;
+                   }
+               }
+           }
+       }
+    }
+  memcpy (reqid, save, savelen * sizeof(oid));
+  *reqid_len = savelen;
+
+  return SNMP_ERR_NOSUCHNAME;
+}
+
+/* GET message header. */
+static u_char *
+smux_parse_get_header (u_char *ptr, size_t *len, long *reqid)
+{
+  u_char type;
+  long errstat;
+  long errindex;
+
+  /* Request ID. */
+  ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid));
+
+  if (debug_smux)
+    zlog_debug ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len);
+
+  /* Error status. */
+  ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat));
+
+  if (debug_smux)
+    zlog_debug ("SMUX GET errstat %ld len: %zd", errstat, *len);
+
+  /* Error index. */
+  ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
+
+  if (debug_smux)
+    zlog_debug ("SMUX GET errindex %ld len: %zd", errindex, *len);
+
+  return ptr;
+}
+
+static void
+smux_parse_set (u_char *ptr, size_t len, int action)
+{
+  long reqid;
+  oid oid[MAX_OID_LEN];
+  size_t oid_len;
+  u_char val_type;
+  void *val;
+  size_t val_len;
+  int ret;
+
+  if (debug_smux)
+    zlog_debug ("SMUX SET(%s) message parse: len %zd",
+               (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"),
+               len);
+
+  /* Parse SET message header. */
+  ptr = smux_parse_get_header (ptr, &len, &reqid);
+
+  /* Parse SET message object ID. */
+  ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val);
+
+  ret = smux_set (oid, &oid_len, val_type, val, val_len, action);
+  if (debug_smux)
+    zlog_debug ("SMUX SET ret %d", ret);
+
+  /* Return result. */
+  if (RESERVE1 == action)
+    smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
+}
+
+static void
+smux_parse_get (u_char *ptr, size_t len, int exact)
+{
+  long reqid;
+  oid oid[MAX_OID_LEN];
+  size_t oid_len;
+  u_char val_type;
+  void *val;
+  size_t val_len;
+  int ret;
+
+  if (debug_smux)
+    zlog_debug ("SMUX GET message parse: len %zd", len);
+  
+  /* Parse GET message header. */
+  ptr = smux_parse_get_header (ptr, &len, &reqid);
+  
+  /* Parse GET message object ID. We needn't the value come */
+  ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL);
+
+  /* Traditional getstatptr. */
+  if (exact)
+    ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len);
+  else
+    ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len);
+
+  /* Return result. */
+  if (ret == 0)
+    smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len);
+  else
+    smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
+}
+
+/* Parse SMUX_CLOSE message. */
+static void
+smux_parse_close (u_char *ptr, int len)
+{
+  long reason = 0;
+
+  while (len--)
+    {
+      reason = (reason << 8) | (long) *ptr;
+      ptr++;
+    }
+  zlog_info ("SMUX_CLOSE with reason: %ld", reason);
+}
+
+/* SMUX_RRSP message. */
+static void
+smux_parse_rrsp (u_char *ptr, size_t len)
+{
+  u_char val;
+  long errstat;
+  
+  ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
+
+  if (debug_smux)
+    zlog_debug ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
+}
+
+/* Parse SMUX message. */
+static int
+smux_parse (u_char *ptr, size_t len)
+{
+  /* This buffer we'll use for SOUT message. We could allocate it with
+     malloc and save only static pointer/lenght, but IMHO static
+     buffer is a faster solusion. */
+  static u_char sout_save_buff[SMUXMAXPKTSIZE];
+  static int sout_save_len = 0;
+
+  int len_income = len; /* see note below: YYY */
+  u_char type;
+  u_char rollback;
+
+  rollback = ptr[2]; /* important only for SMUX_SOUT */
+
+process_rest: /* see note below: YYY */
+
+  /* Parse SMUX message type and subsequent length. */
+  ptr = asn_parse_header (ptr, &len, &type);
+
+  if (debug_smux)
+    zlog_debug ("SMUX message received type: %d rest len: %zd", type, len);
+
+  switch (type)
+    {
+    case SMUX_OPEN:
+      /* Open must be not send from SNMP agent. */
+      zlog_warn ("SMUX_OPEN received: resetting connection.");
+      return -1;
+      break;
+    case SMUX_RREQ:
+      /* SMUX_RREQ message is invalid for us. */
+      zlog_warn ("SMUX_RREQ received: resetting connection.");
+      return -1;
+      break;
+    case SMUX_SOUT:
+      /* SMUX_SOUT message is now valied for us. */
+      if (debug_smux)
+        zlog_debug ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit");
+
+      if (sout_save_len > 0)
+        {
+          smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT);
+          sout_save_len = 0;
+        }
+      else
+        zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
+
+      if (len_income > 3) 
+        {
+          /* YYY: this strange code has to solve the "slow peer"
+             problem: When agent sends SMUX_SOUT message it doesn't
+             wait any responce and may send some next message to
+             subagent. Then the peer in 'smux_read()' will recieve
+             from socket the 'concatenated' buffer, contaning both
+             SMUX_SOUT message and the next one
+             (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if
+             the buffer is longer than 3 ( length of SMUX_SOUT ), we
+             must process the rest of it.  This effect may be observed
+             if 'debug_smux' is set to '1' */
+          ptr++;
+          len = len_income - 3;
+          goto process_rest;
+        }
+      break;
+    case SMUX_GETRSP:
+      /* SMUX_GETRSP message is invalid for us. */
+      zlog_warn ("SMUX_GETRSP received: resetting connection.");
+      return -1;
+      break;
+    case SMUX_CLOSE:
+      /* Close SMUX connection. */
+      if (debug_smux)
+       zlog_debug ("SMUX_CLOSE");
+      smux_parse_close (ptr, len);
+      return -1;
+      break;
+    case SMUX_RRSP:
+      /* This is response for register message. */
+      if (debug_smux)
+       zlog_debug ("SMUX_RRSP");
+      smux_parse_rrsp (ptr, len);
+      break;
+    case SMUX_GET:
+      /* Exact request for object id. */
+      if (debug_smux)
+       zlog_debug ("SMUX_GET");
+      smux_parse_get (ptr, len, 1);
+      break;
+    case SMUX_GETNEXT:
+      /* Next request for object id. */
+      if (debug_smux)
+       zlog_debug ("SMUX_GETNEXT");
+      smux_parse_get (ptr, len, 0);
+      break;
+    case SMUX_SET:
+      /* SMUX_SET is supported with some limitations. */
+      if (debug_smux)
+       zlog_debug ("SMUX_SET");
+
+      /* save the data for future SMUX_SOUT */
+      memcpy (sout_save_buff, ptr, len);
+      sout_save_len = len;
+      smux_parse_set (ptr, len, RESERVE1);
+      break;
+    default:
+      zlog_info ("Unknown type: %d", type);
+      break;
+    }
+  return 0;
+}
+
+/* SMUX message read function. */
+static int
+smux_read (struct thread *t)
+{
+  int sock;
+  int len;
+  u_char buf[SMUXMAXPKTSIZE];
+  int ret;
+
+  /* Clear thread. */
+  sock = THREAD_FD (t);
+  smux_read_thread = NULL;
+
+  if (debug_smux)
+    zlog_debug ("SMUX read start");
+
+  /* Read message from SMUX socket. */
+  len = recv (sock, buf, SMUXMAXPKTSIZE, 0);
+
+  if (len < 0)
+    {
+      zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno));
+      close (sock);
+      smux_sock = -1;
+      smux_event (SMUX_CONNECT, 0);
+      return -1;
+    }
+
+  if (len == 0)
+    {
+      zlog_warn ("SMUX connection closed: %d", sock);
+      close (sock);
+      smux_sock = -1;
+      smux_event (SMUX_CONNECT, 0);
+      return -1;
+    }
+
+  if (debug_smux)
+    zlog_debug ("SMUX read len: %d", len);
+
+  /* Parse the message. */
+  ret = smux_parse (buf, len);
+
+  if (ret < 0)
+    {
+      close (sock);
+      smux_sock = -1;
+      smux_event (SMUX_CONNECT, 0);
+      return -1;
+    }
+
+  /* Regiser read thread. */
+  smux_event (SMUX_READ, sock);
+
+  return 0;
+}
+
+static int
+smux_open (int sock)
+{
+  u_char buf[BUFSIZ];
+  u_char *ptr;
+  size_t len;
+  long version;
+  const char progname[] = QUAGGA_PROGNAME "-" QUAGGA_VERSION;
+
+  if (debug_smux)
+    {
+      smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
+      zlog_debug ("SMUX open progname: %s", progname);
+      zlog_debug ("SMUX open password: %s", smux_passwd);
+    }
+
+  ptr = buf;
+  len = BUFSIZ;
+
+  /* SMUX Header.  As placeholder. */
+  ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0);
+
+  /* SMUX Open. */
+  version = 0;
+  ptr = asn_build_int (ptr, &len, 
+                      (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      &version, sizeof (version));
+
+  /* SMUX connection oid. */
+  ptr = asn_build_objid (ptr, &len,
+                        (u_char) 
+                        (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
+                        smux_oid, smux_oid_len);
+
+  /* SMUX connection description. */
+  ptr = asn_build_string (ptr, &len, 
+                         (u_char)
+                         (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
+                         (const u_char *) progname, strlen (progname));
+
+  /* SMUX connection password. */
+  ptr = asn_build_string (ptr, &len, 
+                         (u_char)
+                         (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
+                         (u_char *)smux_passwd, strlen (smux_passwd));
+
+  /* Fill in real SMUX header.  We exclude ASN header size (2). */
+  len = BUFSIZ;
+  asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2);
+
+  return send (sock, buf, (ptr - buf), 0);
+}
+
+/* `ename` is ignored. Instead of using the provided enterprise OID,
+   the SMUX peer is used. This keep compatibility with the previous
+   versions of Quagga.
+
+   All other fields are used as they are intended. */
+int
+smux_trap (struct variable *vp, size_t vp_len,
+          const oid *ename, size_t enamelen,
+          const oid *name, size_t namelen,
+          const oid *iname, size_t inamelen,
+          const struct trap_object *trapobj, size_t trapobjlen,
+          u_char sptrap)
+{
+  unsigned int i;
+  u_char buf[BUFSIZ];
+  u_char *ptr;
+  size_t len, length;
+  struct in_addr addr;
+  unsigned long val;
+  u_char *h1, *h1e;
+
+  ptr = buf;
+  len = BUFSIZ;
+  length = len;
+
+  /* When SMUX connection is not established. */
+  if (smux_sock < 0)
+    return 0;
+
+  /* SMUX header. */
+  ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0);
+
+  /* Sub agent enterprise oid. */
+  ptr = asn_build_objid (ptr, &len,
+                        (u_char) 
+                        (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
+                        smux_oid, smux_oid_len);
+
+  /* IP address. */
+  addr.s_addr = 0;
+  ptr = asn_build_string (ptr, &len, 
+                         (u_char)
+                         (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS),
+                         (u_char *)&addr, sizeof (addr));
+
+  /* Generic trap integer. */
+  val = SNMP_TRAP_ENTERPRISESPECIFIC;
+  ptr = asn_build_int (ptr, &len, 
+                      (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      (long *)&val, sizeof (val));
+
+  /* Specific trap integer. */
+  val = sptrap;
+  ptr = asn_build_int (ptr, &len, 
+                      (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                      (long *)&val, sizeof (val));
+
+  /* Timeticks timestamp. */
+  val = 0;
+  ptr = asn_build_unsigned_int (ptr, &len, 
+                               (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS),
+                               &val, sizeof (val));
+  
+  /* Variables. */
+  h1 = ptr;
+  ptr = asn_build_sequence (ptr, &len, 
+                           (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
+                           0);
+
+
+  /* Iteration for each objects. */
+  h1e = ptr;
+  for (i = 0; i < trapobjlen; i++)
+    {
+      int ret;
+      oid oid[MAX_OID_LEN];
+      size_t oid_len;
+      void *val;
+      size_t val_len;
+      u_char val_type;
+
+      /* Make OID. */
+      if (trapobj[i].namelen > 0) 
+        {
+          oid_copy (oid, name, namelen);
+          oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen);
+          oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen);
+          oid_len = namelen + trapobj[i].namelen + inamelen;
+        }
+      else 
+        {
+          oid_copy (oid, name, namelen);
+          oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1));
+          oid_len = namelen + trapobj[i].namelen * (-1) ;
+        }
+
+      if (debug_smux) 
+        {
+          smux_oid_dump ("Trap", name, namelen);
+          if (trapobj[i].namelen < 0)
+            smux_oid_dump ("Trap", 
+                           trapobj[i].name, (- 1) * (trapobj[i].namelen));
+          else 
+            {
+              smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen));
+              smux_oid_dump ("Trap", iname, inamelen);
+            }
+          smux_oid_dump ("Trap", oid, oid_len);
+          zlog_info ("BUFSIZ: %d // oid_len: %lu", BUFSIZ, (u_long)oid_len);
+      }
+
+      ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len);
+
+      if (debug_smux)
+       zlog_debug ("smux_get result %d", ret);
+
+      if (ret == 0)
+       ptr = snmp_build_var_op (ptr, oid, &oid_len,
+                                val_type, val_len, val, &len);
+    }
+
+  /* Now variable size is known, fill in size */
+  asn_build_sequence(h1, &length,
+                    (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
+                    ptr - h1e);
+
+  /* Fill in size of whole sequence */
+  len = BUFSIZ;
+  asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2);
+
+  return send (smux_sock, buf, (ptr - buf), 0);
+}
+
+static int
+smux_register (int sock)
+{
+  u_char buf[BUFSIZ];
+  u_char *ptr;
+  int ret;
+  size_t len;
+  long priority;
+  long operation;
+  struct subtree *subtree;
+  struct listnode *node, *nnode;
+
+  ret = 0;
+
+  for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree))
+    {
+      ptr = buf;
+      len = BUFSIZ;
+
+      /* SMUX RReq Header. */
+      ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
+
+      /* Register MIB tree. */
+      ptr = asn_build_objid (ptr, &len,
+                           (u_char)
+                           (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
+                           subtree->name, subtree->name_len);
+
+      /* Priority. */
+      priority = -1;
+      ptr = asn_build_int (ptr, &len, 
+                         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                         &priority, sizeof (priority));
+
+      /* Operation. */
+      operation = 2; /* Register R/W */
+      ptr = asn_build_int (ptr, &len, 
+                         (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+                         &operation, sizeof (operation));
+
+      if (debug_smux)
+        {
+          smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
+          zlog_debug ("SMUX register priority: %ld", priority);
+          zlog_debug ("SMUX register operation: %ld", operation);
+        }
+
+      len = BUFSIZ;
+      asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
+      ret = send (sock, buf, (ptr - buf), 0);
+      if (ret < 0)
+        return ret;
+    }
+  return ret;
+}
+
+/* Try to connect to SNMP agent. */
+static int
+smux_connect (struct thread *t)
+{
+  int ret;
+
+  if (debug_smux)
+    zlog_debug ("SMUX connect try %d", fail + 1);
+
+  /* Clear thread poner of myself. */
+  smux_connect_thread = NULL;
+
+  /* Make socket.  Try to connect. */
+  smux_sock = smux_socket ();
+  if (smux_sock < 0)
+    {
+      if (++fail < SMUX_MAX_FAILURE)
+       smux_event (SMUX_CONNECT, 0);
+      return 0;
+    }
+
+  /* Send OPEN PDU. */
+  ret = smux_open (smux_sock);
+  if (ret < 0)
+    {
+      zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno));
+      close (smux_sock);
+      smux_sock = -1;
+      if (++fail < SMUX_MAX_FAILURE)
+       smux_event (SMUX_CONNECT, 0);
+      return -1;
+    }
+
+  /* Send any outstanding register PDUs. */
+  ret = smux_register (smux_sock);
+  if (ret < 0)
+    {
+      zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno));
+      close (smux_sock);
+      smux_sock = -1;
+      if (++fail < SMUX_MAX_FAILURE)
+       smux_event (SMUX_CONNECT, 0);
+      return -1;
+    }
+
+  /* Everything goes fine. */
+  smux_event (SMUX_READ, smux_sock);
+
+  return 0;
+}
+
+/* Clear all SMUX related resources. */
+static void
+smux_stop (void)
+{
+  if (smux_read_thread)
+    {
+      thread_cancel (smux_read_thread);
+      smux_read_thread = NULL;
+    }
+
+  if (smux_connect_thread)
+    {
+      thread_cancel (smux_connect_thread);
+      smux_connect_thread = NULL;
+    }
+
+  if (smux_sock >= 0)
+    {
+      close (smux_sock);
+      smux_sock = -1;
+    }
+}
+
+
+
+void
+smux_event (enum smux_event event, int sock)
+{
+  switch (event)
+    {
+    case SMUX_SCHEDULE:
+      smux_connect_thread = thread_add_event (smux_master, smux_connect, NULL, 0);
+      break;
+    case SMUX_CONNECT:
+      smux_connect_thread = thread_add_timer (smux_master, smux_connect, NULL, 10);
+      break;
+    case SMUX_READ:
+      smux_read_thread = thread_add_read (smux_master, smux_read, NULL, sock);
+      break;
+    default:
+      break;
+    }
+}
+
+static int
+smux_str2oid (const char *str, oid *oid, size_t *oid_len)
+{
+  int len;
+  int val;
+
+  len = 0;
+  val = 0;
+  *oid_len = 0;
+
+  if (*str == '.')
+    str++;
+  if (*str == '\0')
+    return 0;
+
+  while (1)
+    {
+      if (! isdigit (*str))
+       return -1;
+
+      while (isdigit (*str))
+       {
+         val *= 10;
+         val += (*str - '0');
+         str++;
+       }
+
+      if (*str == '\0')
+       break;
+      if (*str != '.')
+       return -1;
+
+      oid[len++] = val;
+      val = 0;
+      str++;
+    }
+
+  oid[len++] = val;
+  *oid_len = len;
+
+  return 0;
+}
+
+static oid *
+smux_oid_dup (oid *objid, size_t objid_len)
+{
+  oid *new;
+
+  new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len);
+  oid_copy (new, objid, objid_len);
+
+  return new;
+}
+
+static int
+smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str)
+{
+  int ret;
+  oid oid[MAX_OID_LEN];
+  size_t oid_len;
+
+  ret = smux_str2oid (oid_str, oid, &oid_len);
+  if (ret != 0)
+    {
+      vty_out (vty, "object ID malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (smux_oid)
+    {
+      free (smux_oid);
+      smux_oid = NULL;
+    }
+
+  /* careful, smux_passwd might point to string constant */
+  if (smux_passwd)
+    {
+      free (smux_passwd);
+      smux_passwd = NULL;
+    }
+
+  smux_oid = smux_oid_dup (oid, oid_len);
+  smux_oid_len = oid_len;
+
+  if (passwd_str)
+    smux_passwd = strdup (passwd_str);
+  else
+    smux_passwd = strdup ("");
+
+  return 0;
+}
+
+static int
+smux_peer_default (void)
+{
+  if (smux_oid)
+    {
+      free (smux_oid);
+      smux_oid = NULL;
+    }
+  
+  /* careful, smux_passwd might be pointing at string constant */
+  if (smux_passwd)
+    {
+      free (smux_passwd);
+      smux_passwd = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (smux_peer,
+       smux_peer_cmd,
+       "smux peer OID",
+       "SNMP MUX protocol settings\n"
+       "SNMP MUX peer settings\n"
+       "Object ID used in SMUX peering\n")
+{
+  if (smux_peer_oid (vty, argv[0], NULL) == 0)
+    {
+      smux_start();
+      return CMD_SUCCESS;
+    }
+  else
+    return CMD_WARNING;
+}
+
+DEFUN (smux_peer_password,
+       smux_peer_password_cmd,
+       "smux peer OID PASSWORD",
+       "SNMP MUX protocol settings\n"
+       "SNMP MUX peer settings\n"
+       "SMUX peering object ID\n"
+       "SMUX peering password\n")
+{
+  if (smux_peer_oid (vty, argv[0], argv[1]) == 0)
+    {
+      smux_start();
+      return CMD_SUCCESS;
+    }
+  else
+    return CMD_WARNING;
+}
+
+DEFUN (no_smux_peer,
+       no_smux_peer_cmd,
+       "no smux peer",
+       NO_STR
+       "SNMP MUX protocol settings\n"
+       "SNMP MUX peer settings\n")
+{
+  smux_stop();
+  return smux_peer_default ();
+}
+
+ALIAS (no_smux_peer,
+       no_smux_peer_oid_cmd,
+       "no smux peer OID",
+       NO_STR
+       "SNMP MUX protocol settings\n"
+       "SNMP MUX peer settings\n"
+       "SMUX peering object ID\n")
+
+ALIAS (no_smux_peer,
+       no_smux_peer_oid_password_cmd,
+       "no smux peer OID PASSWORD",
+       NO_STR
+       "SNMP MUX protocol settings\n"
+       "SNMP MUX peer settings\n"
+       "SMUX peering object ID\n"
+       "SMUX peering password\n")
+
+static int
+config_write_smux (struct vty *vty)
+{
+  int first = 1;
+  unsigned int i;
+
+  if (smux_oid)
+    {
+      vty_out (vty, "smux peer ");
+      for (i = 0; i < smux_oid_len; i++)
+       {
+         vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]);
+         first = 0;
+       }
+      vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE);
+    }
+  return 0;
+}
+
+/* Register subtree to smux master tree. */
+void
+smux_register_mib (const char *descr, struct variable *var, 
+                   size_t width, int num, 
+                  oid name[], size_t namelen)
+{
+  struct subtree *tree;
+
+  tree = (struct subtree *)malloc(sizeof(struct subtree));
+  oid_copy (tree->name, name, namelen);
+  tree->name_len = namelen;
+  tree->variables = var;
+  tree->variables_num = num;
+  tree->variables_width = width;
+  tree->registered = 0;
+  listnode_add_sort(treelist, tree);
+}
+
+/* Compare function to keep treelist sorted */
+static int
+smux_tree_cmp(struct subtree *tree1, struct subtree *tree2)
+{
+  return oid_compare(tree1->name, tree1->name_len, 
+                    tree2->name, tree2->name_len);
+}
+
+/* Initialize some values then schedule first SMUX connection. */
+void
+smux_init (struct thread_master *tm)
+{
+  /* copy callers thread master */
+  smux_master = tm;
+  
+  /* Make MIB tree. */
+  treelist = list_new();
+  treelist->cmp = (int (*)(void *, void *))smux_tree_cmp;
+
+  /* Install commands. */
+  install_node (&smux_node, config_write_smux);
+
+  install_element (CONFIG_NODE, &smux_peer_cmd);
+  install_element (CONFIG_NODE, &smux_peer_password_cmd);
+  install_element (CONFIG_NODE, &no_smux_peer_cmd);
+  install_element (CONFIG_NODE, &no_smux_peer_oid_cmd);
+  install_element (CONFIG_NODE, &no_smux_peer_oid_password_cmd);
+}
+
+void
+smux_start(void)
+{
+  /* Close any existing connections. */
+  smux_stop();
+
+  /* Schedule first connection. */
+  smux_event (SMUX_SCHEDULE, 0);
+}
+#endif /* HAVE_SNMP */
diff --git a/lib/smux.h b/lib/smux.h
new file mode 100644 (file)
index 0000000..dc91cac
--- /dev/null
@@ -0,0 +1,116 @@
+/* SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_SNMP_H
+#define _ZEBRA_SNMP_H
+
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+
+#include "thread.h"
+
+/* Structures here are mostly compatible with UCD SNMP 4.1.1 */
+#define MATCH_FAILED     (-1)
+#define MATCH_SUCCEEDED  0
+
+/* SYNTAX TruthValue from SNMPv2-TC. */
+#define SNMP_TRUE  1
+#define SNMP_FALSE 2
+
+/* SYNTAX RowStatus from SNMPv2-TC. */
+#define SNMP_VALID  1
+#define SNMP_INVALID 2
+
+#define IN_ADDR_SIZE sizeof(struct in_addr)
+
+#undef REGISTER_MIB
+#define REGISTER_MIB(descr, var, vartype, theoid)              \
+    smux_register_mib(descr, (struct variable *)var, sizeof(struct vartype), \
+    sizeof(var)/sizeof(struct vartype),                        \
+    theoid, sizeof(theoid)/sizeof(oid))
+
+struct trap_object
+{
+  int namelen; /* Negative if the object is not indexed */
+  oid name[MAX_OID_LEN];
+};
+
+/* Declare SMUX return value. */
+#define SNMP_LOCAL_VARIABLES \
+  static long snmp_int_val __attribute__ ((unused)); \
+  static struct in_addr snmp_in_addr_val __attribute__ ((unused));
+
+#define SNMP_INTEGER(V) \
+  ( \
+    *var_len = sizeof (snmp_int_val), \
+    snmp_int_val = V, \
+    (u_char *) &snmp_int_val \
+  )
+
+#define SNMP_IPADDRESS(V) \
+  ( \
+    *var_len = sizeof (struct in_addr), \
+    snmp_in_addr_val = V, \
+    (u_char *) &snmp_in_addr_val \
+  )
+
+extern void smux_init (struct thread_master *tm);
+extern void smux_register_mib(const char *, struct variable *, 
+                              size_t, int, oid [], size_t);
+extern int smux_header_generic (struct variable *, oid [], size_t *, 
+                                int, size_t *, WriteMethod **);
+extern int smux_header_table (struct variable *, oid *, size_t *, 
+                             int, size_t *, WriteMethod **);
+
+/* For traps, three OID are provided:
+
+ 1. The enterprise OID to use (the last argument will be appended to
+    it to form the SNMP trap OID)
+
+ 2. The base OID for objects to be sent in traps.
+
+ 3. The index OID for objects to be sent in traps. This index is used
+    to designate a particular instance of a column.
+
+ The provided trap object contains the bindings to be sent with the
+ trap. The base OID will be prefixed to the provided OID and, if the
+ length is positive, the requested OID is assumed to be a columnar
+ object and the index OID will be appended.
+
+ The two first arguments are the MIB registry used to locate the trap
+ objects.
+
+ The use of the arguments may differ depending on the implementation
+ used.
+*/
+extern int smux_trap (struct variable *, size_t,
+                     const oid *, size_t,
+                     const oid *, size_t,
+                     const oid *, size_t,
+                     const struct trap_object *, size_t,
+                     u_char);
+
+extern int oid_compare (const oid *, int, const oid *, int);
+extern void oid2in_addr (oid [], int, struct in_addr *);
+extern void *oid_copy (void *, const void *, size_t);
+extern void oid_copy_addr (oid [], struct in_addr *, int);
+
+#endif /* _ZEBRA_SNMP_H */
diff --git a/lib/snmp.c b/lib/snmp.c
new file mode 100644 (file)
index 0000000..f6f9845
--- /dev/null
@@ -0,0 +1,133 @@
+/* SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "smux.h"
+
+#define min(A,B) ((A) < (B) ? (A) : (B))
+
+int
+oid_compare (const oid *o1, int o1_len, const oid *o2, int o2_len)
+{
+  int i;
+
+  for (i = 0; i < min (o1_len, o2_len); i++)
+    {
+      if (o1[i] < o2[i])
+       return -1;
+      else if (o1[i] > o2[i])
+       return 1;
+    }
+  if (o1_len < o2_len)
+    return -1;
+  if (o1_len > o2_len)
+    return 1;
+
+  return 0;
+}
+
+void *
+oid_copy (void *dest, const void *src, size_t size)
+{
+  return memcpy (dest, src, size * sizeof (oid));
+}
+
+void
+oid2in_addr (oid oid[], int len, struct in_addr *addr)
+{
+  int i;
+  u_char *pnt;
+  
+  if (len == 0)
+    return;
+
+  pnt = (u_char *) addr;
+
+  for (i = 0; i < len; i++)
+    *pnt++ = oid[i];
+}
+
+void
+oid_copy_addr (oid oid[], struct in_addr *addr, int len)
+{
+  int i;
+  u_char *pnt;
+  
+  if (len == 0)
+    return;
+
+  pnt = (u_char *) addr;
+
+  for (i = 0; i < len; i++)
+    oid[i] = *pnt++;
+}
+
+int
+smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
+                    size_t *var_len, WriteMethod **write_method)
+{
+  oid fulloid[MAX_OID_LEN];
+  int ret;
+
+  oid_copy (fulloid, v->name, v->namelen);
+  fulloid[v->namelen] = 0;
+  /* Check against full instance. */
+  ret = oid_compare (name, *length, fulloid, v->namelen + 1);
+
+  /* Check single instance. */
+  if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
+       return MATCH_FAILED;
+
+  /* In case of getnext, fill in full instance. */
+  memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
+  *length = v->namelen + 1;
+
+  *write_method = 0;
+  *var_len = sizeof(long);    /* default to 'long' results */
+
+  return MATCH_SUCCEEDED;
+}
+
+int
+smux_header_table (struct variable *v, oid *name, size_t *length, int exact,
+                  size_t *var_len, WriteMethod **write_method)
+{
+  /* If the requested OID name is less than OID prefix we
+     handle, adjust it to our prefix. */
+  if ((oid_compare (name, *length, v->name, v->namelen)) < 0)
+    {
+      if (exact)
+       return MATCH_FAILED;
+      oid_copy(name, v->name, v->namelen);
+      *length = v->namelen;
+    }
+
+  *write_method = 0;
+  *var_len = sizeof(long);
+
+  return MATCH_SUCCEEDED;
+}
+#endif /* HAVE_SNMP */
diff --git a/lib/sockopt.c b/lib/sockopt.c
new file mode 100644 (file)
index 0000000..3e6ee73
--- /dev/null
@@ -0,0 +1,669 @@
+/* setsockopt functions
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifdef SUNOS_5
+#include <ifaddrs.h>
+#endif
+
+#include "log.h"
+#include "sockopt.h"
+#include "sockunion.h"
+
+int
+setsockopt_so_recvbuf (int sock, int size)
+{
+  int ret;
+  
+  if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
+                          &size, sizeof (int))) < 0)
+    zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
+             sock,size,safe_strerror(errno));
+
+  return ret;
+}
+
+int
+setsockopt_so_sendbuf (const int sock, int size)
+{
+  int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
+    (char *)&size, sizeof (int));
+  
+  if (ret < 0)
+    zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
+      sock, size, safe_strerror (errno));
+
+  return ret;
+}
+
+int
+getsockopt_so_sendbuf (const int sock)
+{
+  u_int32_t optval;
+  socklen_t optlen = sizeof (optval);
+  int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF,
+    (char *)&optval, &optlen);
+  if (ret < 0)
+  {
+    zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
+      sock, errno, safe_strerror (errno));
+    return ret;
+  }
+  return optval;
+}
+
+static void *
+getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
+{
+  struct cmsghdr *cmsg;
+  void *ptr = NULL;
+  
+  for (cmsg = ZCMSG_FIRSTHDR(msgh); 
+       cmsg != NULL;
+       cmsg = CMSG_NXTHDR(msgh, cmsg))
+    if (cmsg->cmsg_level == level && cmsg->cmsg_type)
+      return (ptr = CMSG_DATA(cmsg));
+
+  return NULL;
+}
+
+#ifdef HAVE_IPV6
+/* Set IPv6 packet info to the socket. */
+int
+setsockopt_ipv6_pktinfo (int sock, int val)
+{
+  int ret;
+    
+#ifdef IPV6_RECVPKTINFO                /*2292bis-01*/
+  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
+#else  /*RFC2292*/
+  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
+#endif /* INIA_IPV6 */
+  return ret;
+}
+
+/* Set multicast hops val to the socket. */
+int
+setsockopt_ipv6_checksum (int sock, int val)
+{
+  int ret;
+
+#ifdef GNU_LINUX
+  ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
+#else
+  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
+#endif /* GNU_LINUX */
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_CHECKSUM");
+  return ret;
+}
+
+/* Set multicast hops val to the socket. */
+int
+setsockopt_ipv6_multicast_hops (int sock, int val)
+{
+  int ret;
+
+  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
+  return ret;
+}
+
+/* Set multicast hops val to the socket. */
+int
+setsockopt_ipv6_unicast_hops (int sock, int val)
+{
+  int ret;
+
+  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
+  return ret;
+}
+
+int
+setsockopt_ipv6_hoplimit (int sock, int val)
+{
+  int ret;
+
+#ifdef IPV6_RECVHOPLIMIT       /*2292bis-01*/
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
+#else  /*RFC2292*/
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
+#endif
+  return ret;
+}
+
+/* Set multicast loop zero to the socket. */
+int
+setsockopt_ipv6_multicast_loop (int sock, int val)
+{
+  int ret;
+    
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+                   sizeof (val));
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
+  return ret;
+}
+
+static int
+getsockopt_ipv6_ifindex (struct msghdr *msgh)
+{
+  struct in6_pktinfo *pktinfo;
+  
+  pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
+  
+  return pktinfo->ipi6_ifindex;
+}
+
+int
+setsockopt_ipv6_tclass(int sock, int tclass)
+{
+  int ret = 0;
+
+#ifdef IPV6_TCLASS /* RFC3542 */
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof (tclass));
+  if (ret < 0)
+    zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s",
+              sock, tclass, safe_strerror(errno));
+#endif
+  return ret;
+}
+#endif /* HAVE_IPV6 */
+
+/*
+ * Process multicast socket options for IPv4 in an OS-dependent manner.
+ * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
+ *
+ * Many operating systems have a limit on the number of groups that
+ * can be joined per socket (where each group and local address
+ * counts).  This impacts OSPF, which joins groups on each interface
+ * using a single socket.  The limit is typically 20, derived from the
+ * original BSD multicast implementation.  Some systems have
+ * mechanisms for increasing this limit.
+ *
+ * In many 4.4BSD-derived systems, multicast group operations are not
+ * allowed on interfaces that are not UP.  Thus, a previous attempt to
+ * leave the group may have failed, leaving it still joined, and we
+ * drop/join quietly to recover.  This may not be necessary, but aims to
+ * defend against unknown behavior in that we will still return an error
+ * if the second join fails.  It is not clear how other systems
+ * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
+ * but this behavior should not be harmful if they behave the same way,
+ * allow leaves, or implicitly leave all groups joined to down interfaces.
+ */
+int
+setsockopt_ipv4_multicast(int sock,
+                       int optname, 
+                       unsigned int mcast_addr,
+                       ifindex_t ifindex)
+{
+#ifdef HAVE_RFC3678
+  struct group_req gr;
+  struct sockaddr_in *si;
+  int ret;
+  memset (&gr, 0, sizeof(gr));
+  si = (struct sockaddr_in *)&gr.gr_group;
+  gr.gr_interface = ifindex;
+  si->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  si->sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  si->sin_addr.s_addr = mcast_addr;
+  ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? 
+    MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
+  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+    {
+      setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
+      ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr));
+    }
+  return ret;
+
+#elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
+  struct ip_mreqn mreqn;
+  int ret;
+  
+  assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
+  memset (&mreqn, 0, sizeof(mreqn));
+
+  mreqn.imr_multiaddr.s_addr = mcast_addr;
+  mreqn.imr_ifindex = ifindex;
+  
+  ret = setsockopt(sock, IPPROTO_IP, optname,
+                   (void *)&mreqn, sizeof(mreqn));
+  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+    {
+      /* see above: handle possible problem when interface comes back up */
+      char buf[1][INET_ADDRSTRLEN];
+      zlog_info("setsockopt_ipv4_multicast attempting to drop and "
+                "re-add (fd %d, mcast %s, ifindex %u)",
+                sock,
+                inet_ntop(AF_INET, &mreqn.imr_multiaddr,
+                          buf[0], sizeof(buf[0])), ifindex);
+      setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+                 (void *)&mreqn, sizeof(mreqn));
+      ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                       (void *)&mreqn, sizeof(mreqn));
+    }
+  return ret;
+
+  /* Example defines for another OS, boilerplate off other code in this
+     function, AND handle optname as per other sections for consistency !! */
+  /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
+  /* Add your favourite OS here! */
+
+#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ 
+  /* standard BSD API */
+
+  struct in_addr m;
+  struct ip_mreq mreq;
+  int ret;
+
+  assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
+
+  m.s_addr = htonl(ifindex);
+
+  memset (&mreq, 0, sizeof(mreq));
+  mreq.imr_multiaddr.s_addr = mcast_addr;
+  mreq.imr_interface = m;
+  
+  ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
+  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+    {
+      /* see above: handle possible problem when interface comes back up */
+      char buf[1][INET_ADDRSTRLEN];
+      zlog_info("setsockopt_ipv4_multicast attempting to drop and "
+                "re-add (fd %d, mcast %s, ifindex %u)",
+                sock,
+                inet_ntop(AF_INET, &mreq.imr_multiaddr,
+                          buf[0], sizeof(buf[0])), ifindex);
+      setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+                  (void *)&mreq, sizeof(mreq));
+      ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                        (void *)&mreq, sizeof(mreq));
+    }
+  return ret;
+
+#else
+  #error "Unsupported multicast API"
+#endif /* #if OS_TYPE */
+
+}
+
+/*
+ * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
+ */
+int
+setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex)
+{
+
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+  struct ip_mreqn mreqn;
+  memset (&mreqn, 0, sizeof(mreqn));
+
+  mreqn.imr_ifindex = ifindex;
+  return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn));
+
+  /* Example defines for another OS, boilerplate off other code in this
+     function */
+  /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
+  /* Add your favourite OS here! */
+#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
+  struct in_addr m;
+
+  m.s_addr = htonl(ifindex);
+
+  return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m));
+#elif defined(SUNOS_5)
+  char ifname[IF_NAMESIZE];
+  struct ifaddrs *ifa, *ifap;
+  struct in_addr ifaddr;
+
+  if (if_indextoname(ifindex, ifname) == NULL)
+    return -1;
+
+  if (getifaddrs(&ifa) != 0)
+    return -1;
+
+  for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
+    {
+      struct sockaddr_in *sa;
+
+      if (strcmp(ifap->ifa_name, ifname) != 0)
+        continue;
+      if (ifap->ifa_addr->sa_family != AF_INET)
+        continue;
+      sa = (struct sockaddr_in*)ifap->ifa_addr;
+      memcpy(&ifaddr, &sa->sin_addr, sizeof(ifaddr));
+      break;
+    }
+
+  freeifaddrs(ifa);
+  if (!ifap) /* This means we did not find an IP */
+    return -1;
+
+  return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&ifaddr, sizeof(ifaddr));
+#else
+  #error "Unsupported multicast API"
+#endif
+}
+  
+static int
+setsockopt_ipv4_ifindex (int sock, ifindex_t val)
+{
+  int ret;
+
+#if defined (IP_PKTINFO)
+  if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
+    zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
+              sock,val,safe_strerror(errno));
+#elif defined (IP_RECVIF)
+  if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
+    zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
+              sock,val,safe_strerror(errno));
+#else
+#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
+#warning "Will not be able to receive link info."
+#warning "Things might be seriously broken.."
+  /* XXX Does this ever happen?  Should there be a zlog_warn message here? */
+  ret = -1;
+#endif
+  return ret;
+}
+
+int
+setsockopt_ipv4_tos(int sock, int tos)
+{
+  int ret;
+
+  ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
+  if (ret < 0)
+    zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
+              sock, tos, safe_strerror(errno));
+  return ret;
+}
+
+
+int
+setsockopt_ifindex (int af, int sock, ifindex_t val)
+{
+  int ret = -1;
+  
+  switch (af)
+    {
+      case AF_INET:
+        ret = setsockopt_ipv4_ifindex (sock, val);
+        break;
+#ifdef HAVE_IPV6
+      case AF_INET6:
+        ret = setsockopt_ipv6_pktinfo (sock, val);
+        break;
+#endif
+      default:
+        zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
+    }
+  return ret;
+}
+  
+/*
+ * Requires: msgh is not NULL and points to a valid struct msghdr, which
+ * may or may not have control data about the incoming interface.
+ *
+ * Returns the interface index (small integer >= 1) if it can be
+ * determined, or else 0.
+ */
+static ifindex_t
+getsockopt_ipv4_ifindex (struct msghdr *msgh)
+{
+  /* XXX: initialize to zero?  (Always overwritten, so just cosmetic.) */
+  ifindex_t ifindex = -1;
+
+#if defined(IP_PKTINFO)
+/* Linux pktinfo based ifindex retrieval */
+  struct in_pktinfo *pktinfo;
+  
+  pktinfo = 
+    (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
+  /* XXX Can pktinfo be NULL?  Clean up post 0.98. */
+  ifindex = pktinfo->ipi_ifindex;
+  
+#elif defined(IP_RECVIF)
+
+  /* retrieval based on IP_RECVIF */
+
+#ifndef SUNOS_5
+  /* BSD systems use a sockaddr_dl as the control message payload. */
+  struct sockaddr_dl *sdl;
+#else
+  /* SUNOS_5 uses an integer with the index. */
+  ifindex_t *ifindex_p;
+#endif /* SUNOS_5 */
+
+#ifndef SUNOS_5
+  /* BSD */
+  sdl = 
+    (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
+  if (sdl != NULL)
+    ifindex = sdl->sdl_index;
+  else
+    ifindex = 0;
+#else
+  /*
+   * Solaris.  On Solaris 8, IP_RECVIF is defined, but the call to
+   * enable it fails with errno=99, and the struct msghdr has
+   * controllen 0.
+   */
+  ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); 
+  if (ifindex_p != NULL)
+    ifindex = *ifindex_p;
+  else
+    ifindex = 0;
+#endif /* SUNOS_5 */
+
+#else
+  /*
+   * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
+   * XXX Decide if this is a core service, or if daemons have to cope.
+   * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
+   * daemons have to cope.
+   */
+#warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
+#warning "Some daemons may fail to operate correctly!"
+  ifindex = 0;
+
+#endif /* IP_PKTINFO */ 
+
+  return ifindex;
+}
+
+/* return ifindex, 0 if none found */
+ifindex_t
+getsockopt_ifindex (int af, struct msghdr *msgh)
+{
+  switch (af)
+    {
+      case AF_INET:
+        return (getsockopt_ipv4_ifindex (msgh));
+        break;
+#ifdef HAVE_IPV6
+      case AF_INET6:
+        return (getsockopt_ipv6_ifindex (msgh));
+        break;
+#endif
+      default:
+        zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
+        return 0;
+    }
+}
+
+/* swab iph between order system uses for IP_HDRINCL and host order */
+void
+sockopt_iphdrincl_swab_htosys (struct ip *iph)
+{
+  /* BSD and derived take iph in network order, except for 
+   * ip_len and ip_off
+   */
+#ifndef HAVE_IP_HDRINCL_BSD_ORDER
+  iph->ip_len = htons(iph->ip_len);
+  iph->ip_off = htons(iph->ip_off);
+#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
+
+  iph->ip_id = htons(iph->ip_id);
+}
+
+void
+sockopt_iphdrincl_swab_systoh (struct ip *iph)
+{
+#ifndef HAVE_IP_HDRINCL_BSD_ORDER
+  iph->ip_len = ntohs(iph->ip_len);
+  iph->ip_off = ntohs(iph->ip_off);
+#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
+
+  iph->ip_id = ntohs(iph->ip_id);
+}
+
+int
+sockopt_tcp_rtt (int sock)
+{
+#ifdef TCP_INFO
+  struct tcp_info ti;
+  socklen_t len = sizeof(ti);
+
+  if (getsockopt (sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0)
+    return 0;
+
+  return ti.tcpi_rtt / 1000;
+#else
+  return 0;
+#endif
+}
+
+int
+sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
+{
+#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
+  /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
+   * version of the Quagga patch (based on work by Rick Payne, and Bruce
+   * Simpson)
+   */
+#define TCP_MD5_AUTH 13
+#define TCP_MD5_AUTH_ADD 1
+#define TCP_MD5_AUTH_DEL 2
+  struct tcp_rfc2385_cmd {
+    u_int8_t     command;    /* Command - Add/Delete */
+    u_int32_t    address;    /* IPV4 address associated */
+    u_int8_t     keylen;     /* MD5 Key len (do NOT assume 0 terminated ascii) */
+    void         *key;       /* MD5 Key */
+  } cmd;
+  struct in_addr *addr = &su->sin.sin_addr;
+  
+  cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL);
+  cmd.address = addr->s_addr;
+  cmd.keylen = (password != NULL ? strlen (password) : 0);
+  cmd.key = password;
+  
+  return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd);
+  
+#elif HAVE_DECL_TCP_MD5SIG
+  int ret;
+#ifndef GNU_LINUX
+  /*
+   * XXX Need to do PF_KEY operation here to add/remove an SA entry,
+   * and add/remove an SP entry for this peer's packet flows also.
+   */
+  int md5sig = password && *password ? 1 : 0;
+#else
+  int keylen = password ? strlen (password) : 0;
+  struct tcp_md5sig md5sig;
+  union sockunion *su2, *susock;
+  
+  /* Figure out whether the socket and the sockunion are the same family..
+   * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
+   */
+  if (!(susock = sockunion_getsockname (sock)))
+    return -1;
+  
+  if (susock->sa.sa_family == su->sa.sa_family)
+    su2 = su;
+  else
+    {
+      /* oops.. */
+      su2 = susock;
+      
+      if (su2->sa.sa_family == AF_INET)
+        {
+          sockunion_free (susock);
+          return 0;
+        }
+      
+#ifdef HAVE_IPV6
+      /* If this does not work, then all users of this sockopt will need to
+       * differentiate between IPv4 and IPv6, and keep seperate sockets for
+       * each. 
+       *
+       * Sadly, it doesn't seem to work at present. It's unknown whether
+       * this is a bug or not.
+       */
+      if (su2->sa.sa_family == AF_INET6
+          && su->sa.sa_family == AF_INET)
+        {
+           su2->sin6.sin6_family = AF_INET6;
+           /* V4Map the address */
+           memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+           su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+           memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
+        }
+#endif
+    }
+  
+  memset (&md5sig, 0, sizeof (md5sig));
+  memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2));
+  md5sig.tcpm_keylen = keylen;
+  if (keylen)
+    memcpy (md5sig.tcpm_key, password, keylen);
+  sockunion_free (susock);
+#endif /* GNU_LINUX */
+  if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0)
+    {
+      /* ENOENT is harmless.  It is returned when we clear a password for which
+        one was not previously set. */
+      if (ENOENT == errno)
+       ret = 0;
+      else
+       zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
+                 sock, safe_strerror(errno));
+    }
+  return ret;
+#else /* HAVE_TCP_MD5SIG */
+  return -2;
+#endif /* !HAVE_TCP_MD5SIG */
+}
diff --git a/lib/sockopt.h b/lib/sockopt.h
new file mode 100644 (file)
index 0000000..a9b8aca
--- /dev/null
@@ -0,0 +1,105 @@
+/* Router advertisement
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_SOCKOPT_H
+#define _ZEBRA_SOCKOPT_H
+
+#include "sockunion.h"
+
+extern int setsockopt_so_recvbuf (int sock, int size);
+extern int setsockopt_so_sendbuf (const int sock, int size);
+extern int getsockopt_so_sendbuf (const int sock);
+
+#ifdef HAVE_IPV6
+extern int setsockopt_ipv6_pktinfo (int, int);
+extern int setsockopt_ipv6_checksum (int, int);
+extern int setsockopt_ipv6_multicast_hops (int, int);
+extern int setsockopt_ipv6_unicast_hops (int, int);
+extern int setsockopt_ipv6_hoplimit (int, int);
+extern int setsockopt_ipv6_multicast_loop (int, int);
+extern int setsockopt_ipv6_tclass (int, int);
+#endif /* HAVE_IPV6 */
+
+/*
+ * It is OK to reference in6_pktinfo here without a protecting #if
+ * because this macro will only be used #if HAVE_IPV6, and in6_pktinfo
+ * is not optional for HAVE_IPV6.
+ */
+#define SOPT_SIZE_CMSG_PKTINFO_IPV6() (sizeof (struct in6_pktinfo));
+
+/*
+ * Size defines for control messages used to get ifindex.  We define
+ * values for each method, and define a macro that can be used by code
+ * that is unaware of which method is in use.
+ * These values are without any alignment needed (see CMSG_SPACE in RFC3542).
+ */
+#if defined (IP_PKTINFO)
+/* Linux in_pktinfo. */
+#define SOPT_SIZE_CMSG_PKTINFO_IPV4()  (CMSG_SPACE(sizeof (struct in_pktinfo)))
+/* XXX This should perhaps be defined even if IP_PKTINFO is not. */
+#define SOPT_SIZE_CMSG_PKTINFO(af) \
+  ((af == AF_INET) ? SOPT_SIZE_CMSG_PKTINFO_IPV4() \
+                   : SOPT_SIZE_CMSG_PKTINFO_IPV6()
+#endif /* IP_PKTINFO */
+
+#if defined (IP_RECVIF)
+/* BSD/Solaris */
+
+#if defined (SUNOS_5)
+#define SOPT_SIZE_CMSG_RECVIF_IPV4()  (sizeof (uint_t))
+#else
+#define SOPT_SIZE_CMSG_RECVIF_IPV4()   (sizeof (struct sockaddr_dl))
+#endif /* SUNOS_5 */
+#endif /* IP_RECVIF */
+
+/* SOPT_SIZE_CMSG_IFINDEX_IPV4 - portable type */
+#if defined (SOPT_SIZE_CMSG_PKTINFO)
+#define SOPT_SIZE_CMSG_IFINDEX_IPV4() SOPT_SIZE_CMSG_PKTINFO_IPV4()
+#elif defined (SOPT_SIZE_CMSG_RECVIF_IPV4)
+#define SOPT_SIZE_CMSG_IFINDEX_IPV4() SOPT_SIZE_CMSG_RECVIF_IPV4()
+#else /* Nothing available */
+#define SOPT_SIZE_CMSG_IFINDEX_IPV4() (sizeof (char *))
+#endif /* SOPT_SIZE_CMSG_IFINDEX_IPV4 */
+
+#define SOPT_SIZE_CMSG_IFINDEX(af) \
+  (((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \
+                    ? SOPT_SIZE_CMSG_PKTINFO_IPV6())
+
+extern int setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex);
+extern int setsockopt_ipv4_multicast(int sock, int optname,
+                                     unsigned int mcast_addr,
+                                    ifindex_t ifindex);
+extern int setsockopt_ipv4_tos(int sock, int tos);
+
+/* Ask for, and get, ifindex, by whatever method is supported. */
+extern int setsockopt_ifindex (int, int, ifindex_t);
+extern ifindex_t getsockopt_ifindex (int, struct msghdr *);
+
+/* swab the fields in iph between the host order and system order expected 
+ * for IP_HDRINCL.
+ */
+extern void sockopt_iphdrincl_swab_htosys (struct ip *iph);
+extern void sockopt_iphdrincl_swab_systoh (struct ip *iph);
+
+extern int sockopt_tcp_rtt (int);
+extern int sockopt_tcp_signature(int sock, union sockunion *su,
+                                 const char *password);
+#endif /*_ZEBRA_SOCKOPT_H */
diff --git a/lib/sockunion.c b/lib/sockunion.c
new file mode 100644 (file)
index 0000000..8e0ec24
--- /dev/null
@@ -0,0 +1,858 @@
+/* Socket union related function.
+ * Copyright (c) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "str.h"
+#include "log.h"
+#include "jhash.h"
+
+#ifndef HAVE_INET_ATON
+int
+inet_aton (const char *cp, struct in_addr *inaddr)
+{
+  int dots = 0;
+  register u_long addr = 0;
+  register u_long val = 0, base = 10;
+
+  do
+    {
+      register char c = *cp;
+
+      switch (c)
+       {
+       case '0': case '1': case '2': case '3': case '4': case '5':
+       case '6': case '7': case '8': case '9':
+         val = (val * base) + (c - '0');
+         break;
+       case '.':
+         if (++dots > 3)
+           return 0;
+       case '\0':
+         if (val > 255)
+           return 0;
+         addr = addr << 8 | val;
+         val = 0;
+         break;
+       default:
+         return 0;
+       }
+    } while (*cp++) ;
+
+  if (dots < 3)
+    addr <<= 8 * (3 - dots);
+  if (inaddr)
+    inaddr->s_addr = htonl (addr);
+  return 1;
+}
+#endif /* ! HAVE_INET_ATON */
+
+
+#ifndef HAVE_INET_PTON
+int
+inet_pton (int family, const char *strptr, void *addrptr)
+{
+  if (family == AF_INET)
+    {
+      struct in_addr in_val;
+
+      if (inet_aton (strptr, &in_val))
+       {
+         memcpy (addrptr, &in_val, sizeof (struct in_addr));
+         return 1;
+       }
+      return 0;
+    }
+  errno = EAFNOSUPPORT;
+  return -1;
+}
+#endif /* ! HAVE_INET_PTON */
+
+#ifndef HAVE_INET_NTOP
+const char *
+inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
+{
+  unsigned char *p = (unsigned char *) addrptr;
+
+  if (family == AF_INET) 
+    {
+      char temp[INET_ADDRSTRLEN];
+
+      snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+
+      if (strlen(temp) >= len) 
+       {
+         errno = ENOSPC;
+         return NULL;
+       }
+      strcpy(strptr, temp);
+      return strptr;
+    }
+
+  errno = EAFNOSUPPORT;
+  return NULL;
+}
+#endif /* ! HAVE_INET_NTOP */
+
+const char *
+inet_sutop (const union sockunion *su, char *str)
+{
+  switch (su->sa.sa_family)
+    {
+    case AF_INET:
+      inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
+      break;
+#endif /* HAVE_IPV6 */
+    }
+  return str;
+}
+
+int
+str2sockunion (const char *str, union sockunion *su)
+{
+  int ret;
+
+  memset (su, 0, sizeof (union sockunion));
+
+  ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
+  if (ret > 0)                 /* Valid IPv4 address format. */
+    {
+      su->sin.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+      su->sin.sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+      return 0;
+    }
+#ifdef HAVE_IPV6
+  ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
+  if (ret > 0)                 /* Valid IPv6 address format. */
+    {
+      su->sin6.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+      su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif /* SIN6_LEN */
+      return 0;
+    }
+#endif /* HAVE_IPV6 */
+  return -1;
+}
+
+const char *
+sockunion2str (const union sockunion *su, char *buf, size_t len)
+{
+  switch (sockunion_family(su))
+    {
+    case AF_UNSPEC:
+      snprintf (buf, len, "(unspec)");
+      return buf;
+    case AF_INET:
+      return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
+#endif /* HAVE_IPV6 */
+    }
+  snprintf (buf, len, "(af %d)", sockunion_family(su));
+  return buf;
+}
+
+union sockunion *
+sockunion_str2su (const char *str)
+{
+  union sockunion *su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+  
+  if (!str2sockunion (str, su))
+    return su;
+  
+  XFREE (MTYPE_SOCKUNION, su);
+  return NULL;
+}
+
+/* Convert IPv4 compatible IPv6 address to IPv4 address. */
+static void
+sockunion_normalise_mapped (union sockunion *su)
+{
+  struct sockaddr_in sin;
+  
+#ifdef HAVE_IPV6
+  if (su->sa.sa_family == AF_INET6 
+      && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
+    {
+      memset (&sin, 0, sizeof (struct sockaddr_in));
+      sin.sin_family = AF_INET;
+      sin.sin_port = su->sin6.sin6_port;
+      memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
+      memcpy (su, &sin, sizeof (struct sockaddr_in));
+    }
+#endif /* HAVE_IPV6 */
+}
+
+/* Return socket of sockunion. */
+int
+sockunion_socket (const union sockunion *su)
+{
+  int sock;
+
+  sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror (errno));
+      return -1;
+    }
+
+  return sock;
+}
+
+/* Return accepted new socket file descriptor. */
+int
+sockunion_accept (int sock, union sockunion *su)
+{
+  socklen_t len;
+  int client_sock;
+
+  len = sizeof (union sockunion);
+  client_sock = accept (sock, (struct sockaddr *) su, &len);
+  
+  sockunion_normalise_mapped (su);
+  return client_sock;
+}
+
+/* Return sizeof union sockunion.  */
+static int
+sockunion_sizeof (const union sockunion *su)
+{
+  int ret;
+
+  ret = 0;
+  switch (su->sa.sa_family)
+    {
+    case AF_INET:
+      ret = sizeof (struct sockaddr_in);
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      ret = sizeof (struct sockaddr_in6);
+      break;
+#endif /* AF_INET6 */
+    }
+  return ret;
+}
+
+/* return sockunion structure : this function should be revised. */
+static const char *
+sockunion_log (const union sockunion *su, char *buf, size_t len)
+{
+  switch (su->sa.sa_family) 
+    {
+    case AF_INET:
+      return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
+
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len);
+      break;
+#endif /* HAVE_IPV6 */
+
+    default:
+      snprintf (buf, len, "af_unknown %d ", su->sa.sa_family);
+      return buf;
+    }
+}
+
+/* sockunion_connect returns
+   -1 : error occured
+   0 : connect success
+   1 : connect is in progress */
+enum connect_result
+sockunion_connect (int fd, const union sockunion *peersu, unsigned short port,
+                  ifindex_t ifindex)
+{
+  int ret;
+  int val;
+  union sockunion su;
+
+  memcpy (&su, peersu, sizeof (union sockunion));
+
+  switch (su.sa.sa_family)
+    {
+    case AF_INET:
+      su.sin.sin_port = port;
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      su.sin6.sin6_port  = port;
+#ifdef KAME
+      if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
+       {
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+         /* su.sin6.sin6_scope_id = ifindex; */
+#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
+         SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
+       }
+#endif /* KAME */
+      break;
+#endif /* HAVE_IPV6 */
+    }      
+
+  /* Make socket non-block. */
+  val = fcntl (fd, F_GETFL, 0);
+  fcntl (fd, F_SETFL, val|O_NONBLOCK);
+
+  /* Call connect function. */
+  ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
+
+  /* Immediate success */
+  if (ret == 0)
+    {
+      fcntl (fd, F_SETFL, val);
+      return connect_success;
+    }
+
+  /* If connect is in progress then return 1 else it's real error. */
+  if (ret < 0)
+    {
+      if (errno != EINPROGRESS)
+       {
+         char str[SU_ADDRSTRLEN];
+         zlog_info ("can't connect to %s fd %d : %s",
+                    sockunion_log (&su, str, sizeof str),
+                    fd, safe_strerror (errno));
+         return connect_error;
+       }
+    }
+
+  fcntl (fd, F_SETFL, val);
+
+  return connect_in_progress;
+}
+
+/* Make socket from sockunion union. */
+int
+sockunion_stream_socket (union sockunion *su)
+{
+  int sock;
+
+  if (su->sa.sa_family == 0)
+    su->sa.sa_family = AF_INET_UNION;
+
+  sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
+
+  if (sock < 0)
+    zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
+
+  return sock;
+}
+
+/* Bind socket to specified address. */
+int
+sockunion_bind (int sock, union sockunion *su, unsigned short port, 
+               union sockunion *su_addr)
+{
+  int size = 0;
+  int ret;
+
+  if (su->sa.sa_family == AF_INET)
+    {
+      size = sizeof (struct sockaddr_in);
+      su->sin.sin_port = htons (port);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+      su->sin.sin_len = size;
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+      if (su_addr == NULL)
+       sockunion2ip (su) = htonl (INADDR_ANY);
+    }
+#ifdef HAVE_IPV6
+  else if (su->sa.sa_family == AF_INET6)
+    {
+      size = sizeof (struct sockaddr_in6);
+      su->sin6.sin6_port = htons (port);
+#ifdef SIN6_LEN
+      su->sin6.sin6_len = size;
+#endif /* SIN6_LEN */
+      if (su_addr == NULL)
+       {
+#ifdef LINUX_IPV6
+         memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+#else
+         su->sin6.sin6_addr = in6addr_any;
+#endif /* LINUX_IPV6 */
+       }
+    }
+#endif /* HAVE_IPV6 */
+  
+
+  ret = bind (sock, (struct sockaddr *)su, size);
+  if (ret < 0)
+    zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno));
+
+  return ret;
+}
+
+int
+sockopt_reuseaddr (int sock)
+{
+  int ret;
+  int on = 1;
+
+  ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, 
+                   (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
+      return -1;
+    }
+  return 0;
+}
+
+#ifdef SO_REUSEPORT
+int
+sockopt_reuseport (int sock)
+{
+  int ret;
+  int on = 1;
+
+  ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, 
+                   (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
+      return -1;
+    }
+  return 0;
+}
+#else
+int
+sockopt_reuseport (int sock)
+{
+  return 0;
+}
+#endif /* 0 */
+
+int
+sockopt_ttl (int family, int sock, int ttl)
+{
+  int ret;
+
+#ifdef IP_TTL
+  if (family == AF_INET)
+    {
+      ret = setsockopt (sock, IPPROTO_IP, IP_TTL, 
+                       (void *) &ttl, sizeof (int));
+      if (ret < 0)
+       {
+         zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock);
+         return -1;
+       }
+      return 0;
+    }
+#endif /* IP_TTL */
+#ifdef HAVE_IPV6
+  if (family == AF_INET6)
+    {
+      ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 
+                       (void *) &ttl, sizeof (int));
+      if (ret < 0)
+       {
+         zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
+                   ttl, sock);
+         return -1;
+       }
+      return 0;
+    }
+#endif /* HAVE_IPV6 */
+  return 0;
+}
+
+int
+sockopt_cork (int sock, int onoff)
+{
+#ifdef TCP_CORK
+  return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
+#else
+  return 0;
+#endif
+}
+
+int
+sockopt_minttl (int family, int sock, int minttl)
+{
+#ifdef IP_MINTTL
+  if (family == AF_INET)
+    {
+      int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
+      if (ret < 0)
+         zlog (NULL, LOG_WARNING,
+               "can't set sockopt IP_MINTTL to %d on socket %d: %s",
+               minttl, sock, safe_strerror (errno));
+      return ret;
+    }
+#endif /* IP_MINTTL */
+#ifdef IPV6_MINHOPCNT
+  if (family == AF_INET6)
+    {
+      int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl));
+      if (ret < 0)
+         zlog (NULL, LOG_WARNING,
+               "can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s",
+               minttl, sock, safe_strerror (errno));
+      return ret;
+    }
+#endif
+
+  errno = EOPNOTSUPP;
+  return -1;
+}
+
+int
+sockopt_v6only (int family, int sock)
+{
+  int ret, on = 1;
+
+#ifdef HAVE_IPV6
+#ifdef IPV6_V6ONLY
+  if (family == AF_INET6)
+    {
+      ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY,
+                       (void *) &on, sizeof (int));
+      if (ret < 0)
+       {
+         zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY "
+                   "to socket %d", sock);
+         return -1;
+       }
+      return 0;
+    }
+#endif /* IPV6_V6ONLY */
+#endif /* HAVE_IPV6 */
+  return 0;
+}
+
+/* If same family and same prefix return 1. */
+int
+sockunion_same (const union sockunion *su1, const union sockunion *su2)
+{
+  int ret = 0;
+
+  if (su1->sa.sa_family != su2->sa.sa_family)
+    return 0;
+
+  switch (su1->sa.sa_family)
+    {
+    case AF_INET:
+      ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr,
+                   sizeof (struct in_addr));
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
+                   sizeof (struct in6_addr));
+      break;
+#endif /* HAVE_IPV6 */
+    }
+  if (ret == 0)
+    return 1;
+  else
+    return 0;
+}
+
+unsigned int
+sockunion_hash (const union sockunion *su)
+{
+  switch (sockunion_family(su))
+    {
+    case AF_INET:
+      return jhash_1word(su->sin.sin_addr.s_addr, 0);
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return jhash2(su->sin6.sin6_addr.s6_addr32, ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0);
+#endif /* HAVE_IPV6 */
+    }
+  return 0;
+}
+
+size_t
+family2addrsize(int family)
+{
+  switch (family)
+    {
+    case AF_INET:
+      return sizeof(struct in_addr);
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return sizeof(struct in6_addr);
+#endif /* HAVE_IPV6 */
+    }
+  return 0;
+}
+
+size_t
+sockunion_get_addrlen(const union sockunion *su)
+{
+  return family2addrsize(sockunion_family(su));
+}
+
+const u_char *
+sockunion_get_addr(const union sockunion *su)
+{
+  switch (sockunion_family(su))
+    {
+    case AF_INET:
+      return (const u_char *) &su->sin.sin_addr.s_addr;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return (const u_char *) &su->sin6.sin6_addr;
+#endif /* HAVE_IPV6 */
+    }
+  return NULL;
+}
+
+unsigned short
+sockunion_get_port (const union sockunion *su)
+{
+  switch (sockunion_family (su))
+    {
+    case AF_INET:
+      return ntohs(su->sin.sin_port);
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return ntohs(su->sin6.sin6_port);
+#endif /* HAVE_IPV6 */
+    }
+  return 0;
+}
+
+void
+sockunion_set(union sockunion *su, int family, const u_char *addr, size_t bytes)
+{
+  if (family2addrsize(family) != bytes)
+    return;
+
+  sockunion_family(su) = family;
+  switch (family)
+    {
+    case AF_INET:
+      memcpy(&su->sin.sin_addr.s_addr, addr, bytes);
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      memcpy(&su->sin6.sin6_addr, addr, bytes);
+      break;
+#endif /* HAVE_IPV6 */
+    }
+}
+
+/* After TCP connection is established.  Get local address and port. */
+union sockunion *
+sockunion_getsockname (int fd)
+{
+  int ret;
+  socklen_t len;
+  union
+  {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sin6;
+#endif /* HAVE_IPV6 */
+    char tmp_buffer[128];
+  } name;
+  union sockunion *su;
+
+  memset (&name, 0, sizeof name);
+  len = sizeof name;
+
+  ret = getsockname (fd, (struct sockaddr *)&name, &len);
+  if (ret < 0)
+    {
+      zlog_warn ("Can't get local address and port by getsockname: %s",
+                safe_strerror (errno));
+      return NULL;
+    }
+
+  if (name.sa.sa_family == AF_INET)
+    {
+      su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+      memcpy (su, &name, sizeof (struct sockaddr_in));
+      return su;
+    }
+#ifdef HAVE_IPV6
+  if (name.sa.sa_family == AF_INET6)
+    {
+      su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+      memcpy (su, &name, sizeof (struct sockaddr_in6));
+      sockunion_normalise_mapped (su);
+      return su;
+    }
+#endif /* HAVE_IPV6 */
+  return NULL;
+}
+
+/* After TCP connection is established.  Get remote address and port. */
+union sockunion *
+sockunion_getpeername (int fd)
+{
+  int ret;
+  socklen_t len;
+  union
+  {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 sin6;
+#endif /* HAVE_IPV6 */
+    char tmp_buffer[128];
+  } name;
+  union sockunion *su;
+
+  memset (&name, 0, sizeof name);
+  len = sizeof name;
+  ret = getpeername (fd, (struct sockaddr *)&name, &len);
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s",
+           safe_strerror (errno));
+      return NULL;
+    }
+
+  if (name.sa.sa_family == AF_INET)
+    {
+      su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+      memcpy (su, &name, sizeof (struct sockaddr_in));
+      return su;
+    }
+#ifdef HAVE_IPV6
+  if (name.sa.sa_family == AF_INET6)
+    {
+      su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+      memcpy (su, &name, sizeof (struct sockaddr_in6));
+      sockunion_normalise_mapped (su);
+      return su;
+    }
+#endif /* HAVE_IPV6 */
+  return NULL;
+}
+
+/* Print sockunion structure */
+static void __attribute__ ((unused))
+sockunion_print (const union sockunion *su)
+{
+  if (su == NULL)
+    return;
+
+  switch (su->sa.sa_family) 
+    {
+    case AF_INET:
+      printf ("%s\n", inet_ntoa (su->sin.sin_addr));
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      {
+       char buf [SU_ADDRSTRLEN];
+
+       printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr),
+                                buf, sizeof (buf)));
+      }
+      break;
+#endif /* HAVE_IPV6 */
+
+#ifdef AF_LINK
+    case AF_LINK:
+      {
+       struct sockaddr_dl *sdl;
+
+       sdl = (struct sockaddr_dl *)&(su->sa);
+       printf ("link#%d\n", sdl->sdl_index);
+      }
+      break;
+#endif /* AF_LINK */
+    default:
+      printf ("af_unknown %d\n", su->sa.sa_family);
+      break;
+    }
+}
+
+#ifdef HAVE_IPV6
+static int
+in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2)
+{
+  unsigned int i;
+  u_char *p1, *p2;
+
+  p1 = (u_char *)addr1;
+  p2 = (u_char *)addr2;
+
+  for (i = 0; i < sizeof (struct in6_addr); i++)
+    {
+      if (p1[i] > p2[i])
+       return 1;
+      else if (p1[i] < p2[i])
+       return -1;
+    }
+  return 0;
+}
+#endif /* HAVE_IPV6 */
+
+int
+sockunion_cmp (const union sockunion *su1, const union sockunion *su2)
+{
+  if (su1->sa.sa_family > su2->sa.sa_family)
+    return 1;
+  if (su1->sa.sa_family < su2->sa.sa_family)
+    return -1;
+
+  if (su1->sa.sa_family == AF_INET)
+    {
+      if (ntohl (sockunion2ip (su1)) == ntohl (sockunion2ip (su2)))
+       return 0;
+      if (ntohl (sockunion2ip (su1)) > ntohl (sockunion2ip (su2)))
+       return 1;
+      else
+       return -1;
+    }
+#ifdef HAVE_IPV6
+  if (su1->sa.sa_family == AF_INET6)
+    return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
+#endif /* HAVE_IPV6 */
+  return 0;
+}
+
+/* Duplicate sockunion. */
+union sockunion *
+sockunion_dup (const union sockunion *su)
+{
+  union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+  memcpy (dup, su, sizeof (union sockunion));
+  return dup;
+}
+
+void
+sockunion_free (union sockunion *su)
+{
+  XFREE (MTYPE_SOCKUNION, su);
+}
diff --git a/lib/sockunion.h b/lib/sockunion.h
new file mode 100644 (file)
index 0000000..3613073
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Socket union header.
+ * Copyright (c) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_SOCKUNION_H
+#define _ZEBRA_SOCKUNION_H
+
+#include "if.h"
+
+#if 0
+union sockunion {
+  struct sockinet {
+    u_char si_len;
+    u_char si_family;
+    u_short si_port;
+  } su_si;
+  struct sockaddr_in  su_sin;
+  struct sockaddr_in6 su_sin6;
+};
+#define su_len                su_si.si_len
+#define su_family     su_si.si_family
+#define su_port               su_si.si_port
+#endif /* 0 */
+
+union sockunion 
+{
+  struct sockaddr sa;
+  struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+  struct sockaddr_in6 sin6;
+#endif /* HAVE_IPV6 */
+};
+
+enum connect_result
+{
+  connect_error,
+  connect_success,
+  connect_in_progress
+};
+
+/* Default address family. */
+#ifdef HAVE_IPV6
+#define AF_INET_UNION AF_INET6
+#else
+#define AF_INET_UNION AF_INET
+#endif
+
+/* Sockunion address string length.  Same as INET6_ADDRSTRLEN. */
+#define SU_ADDRSTRLEN 46
+
+/* Macro to set link local index to the IPv6 address.  For KAME IPv6
+   stack. */
+#ifdef KAME
+#define        IN6_LINKLOCAL_IFINDEX(a)  ((a).s6_addr[2] << 8 | (a).s6_addr[3])
+#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
+  do { \
+    (a).s6_addr[2] = ((i) >> 8) & 0xff; \
+    (a).s6_addr[3] = (i) & 0xff; \
+  } while (0)
+#else
+#define        IN6_LINKLOCAL_IFINDEX(a)
+#define SET_IN6_LINKLOCAL_IFINDEX(a, i)
+#endif /* KAME */
+
+#define sockunion_family(X)  (X)->sa.sa_family
+
+#define sockunion2ip(X)      (X)->sin.sin_addr.s_addr
+
+/* Prototypes. */
+extern int str2sockunion (const char *, union sockunion *);
+extern const char *sockunion2str (const union sockunion *, char *, size_t);
+extern int sockunion_cmp (const union sockunion *, const union sockunion *);
+extern int sockunion_same (const union sockunion *, const union sockunion *);
+extern unsigned int sockunion_hash (const union sockunion *);
+
+extern size_t family2addrsize(int family);
+extern size_t sockunion_get_addrlen(const union sockunion *);
+extern const u_char *sockunion_get_addr(const union sockunion *);
+extern unsigned short sockunion_get_port (const union sockunion *);
+extern void sockunion_set(union sockunion *, int family, const u_char *addr, size_t bytes);
+
+extern union sockunion *sockunion_str2su (const char *str);
+extern int sockunion_accept (int sock, union sockunion *);
+extern int sockunion_stream_socket (union sockunion *);
+extern int sockopt_reuseaddr (int);
+extern int sockopt_reuseport (int);
+extern int sockopt_v6only (int family, int sock);
+extern int sockunion_bind (int sock, union sockunion *, 
+                           unsigned short, union sockunion *);
+extern int sockopt_ttl (int family, int sock, int ttl);
+extern int sockopt_minttl (int family, int sock, int minttl);
+extern int sockopt_cork (int sock, int onoff);
+extern int sockunion_socket (const union sockunion *su);
+extern const char *inet_sutop (const union sockunion *su, char *str);
+extern enum connect_result sockunion_connect (int fd, const union sockunion *su,
+                                              unsigned short port,
+                                              ifindex_t);
+extern union sockunion *sockunion_getsockname (int);
+extern union sockunion *sockunion_getpeername (int);
+extern union sockunion *sockunion_dup (const union sockunion *);
+extern void sockunion_free (union sockunion *);
+
+#ifndef HAVE_INET_NTOP
+extern const char * inet_ntop (int family, const void *addrptr, 
+                               char *strptr, size_t len);
+#endif /* HAVE_INET_NTOP */
+
+#ifndef HAVE_INET_PTON
+extern int inet_pton (int family, const char *strptr, void *addrptr);
+#endif /* HAVE_INET_PTON */
+
+#ifndef HAVE_INET_ATON
+extern int inet_aton (const char *cp, struct in_addr *inaddr);
+#endif
+
+#endif /* _ZEBRA_SOCKUNION_H */
diff --git a/lib/str.c b/lib/str.c
new file mode 100644 (file)
index 0000000..d8f039a
--- /dev/null
+++ b/lib/str.c
@@ -0,0 +1,127 @@
+/*
+ * zebra string function
+ *
+ * XXX This version of snprintf does not check bounds!
+ */
+
+/*
+ The implementations of strlcpy and strlcat are copied from rsync (GPL):
+    Copyright (C) Andrew Tridgell 1998
+    Copyright (C) 2002 by Martin Pool
+
+ Note that these are not terribly efficient, since they make more than one
+ pass over the argument strings.  At some point, they should be optimized.
+ The implementation of strndup is copied from glibc-2.3.5:
+    Copyright (C) 1996, 1997, 1998, 2001, 2002 Free Software Foundation, Inc.
+*/
+
+/* 
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifndef HAVE_SNPRINTF
+/*
+ * snprint() is a real basic wrapper around the standard sprintf()
+ * without any bounds checking
+ */
+int
+snprintf(char *str, size_t size, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+
+  return vsprintf (str, format, args);
+}
+#endif
+
+#ifndef HAVE_STRLCPY
+/**
+ * Like strncpy but does not 0 fill the buffer and always null 
+ * terminates.
+ *
+ * @param bufsize is the size of the destination buffer.
+ *
+ * @return index of the terminating byte.
+ **/
+size_t
+strlcpy(char *d, const char *s, size_t bufsize)
+{
+       size_t len = strlen(s);
+       size_t ret = len;
+       if (bufsize > 0) {
+               if (len >= bufsize)
+                       len = bufsize-1;
+               memcpy(d, s, len);
+               d[len] = 0;
+       }
+       return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/**
+ * Like strncat() but does not 0 fill the buffer and always null 
+ * terminates.
+ *
+ * @param bufsize length of the buffer, which should be one more than
+ * the maximum resulting string length.
+ **/
+size_t
+strlcat(char *d, const char *s, size_t bufsize)
+{
+       size_t len1 = strlen(d);
+       size_t len2 = strlen(s);
+       size_t ret = len1 + len2;
+
+       if (len1 < bufsize - 1) {
+               if (len2 >= bufsize - len1)
+                       len2 = bufsize - len1 - 1;
+               memcpy(d+len1, s, len2);
+               d[len1+len2] = 0;
+       }
+       return ret;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t
+strnlen(const char *s, size_t maxlen)
+{
+  const char *p;
+  return (p = (const char *)memchr(s, '\0', maxlen)) ? (size_t)(p-s) : maxlen;
+}
+#endif
+
+#ifndef HAVE_STRNDUP
+char *
+strndup (const char *s, size_t maxlen)
+{
+    size_t len = strnlen (s, maxlen);
+    char *new = (char *) malloc (len + 1);
+
+    if (new == NULL)
+      return NULL;
+
+    new[len] = '\0';
+    return (char *) memcpy (new, s, len);
+}
+#endif
diff --git a/lib/str.h b/lib/str.h
new file mode 100644 (file)
index 0000000..7b83fe1
--- /dev/null
+++ b/lib/str.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: str.h,v 1.4 2005/09/19 09:53:21 hasso Exp $
+ */
+
+#ifndef _ZEBRA_STR_H
+#define _ZEBRA_STR_H
+
+#ifndef HAVE_SNPRINTF
+extern int snprintf(char *, size_t, const char *, ...);
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#define vsnprintf(buf, size, format, args) vsprintf(buf, format, args)
+#endif
+
+#ifndef HAVE_STRLCPY
+extern size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLCAT
+extern size_t strlcat(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#endif
+
+#ifndef HAVE_STRNDUP
+extern char * strndup (const char *, size_t);
+#endif
+
+#endif /* _ZEBRA_STR_H */
+
diff --git a/lib/stream.c b/lib/stream.c
new file mode 100644 (file)
index 0000000..ed677c1
--- /dev/null
@@ -0,0 +1,1074 @@
+  /*
+ * Packet interface
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <stddef.h>
+
+#include "stream.h"
+#include "memory.h"
+#include "network.h"
+#include "prefix.h"
+#include "log.h"
+
+/* Tests whether a position is valid */ 
+#define GETP_VALID(S,G) \
+  ((G) <= (S)->endp)
+#define PUT_AT_VALID(S,G) GETP_VALID(S,G)
+#define ENDP_VALID(S,E) \
+  ((E) <= (S)->size)
+
+/* asserting sanity checks. Following must be true before
+ * stream functions are called:
+ *
+ * Following must always be true of stream elements
+ * before and after calls to stream functions:
+ *
+ * getp <= endp <= size
+ *
+ * Note that after a stream function is called following may be true:
+ * if (getp == endp) then stream is no longer readable
+ * if (endp == size) then stream is no longer writeable
+ *
+ * It is valid to put to anywhere within the size of the stream, but only
+ * using stream_put..._at() functions.
+ */
+#define STREAM_WARN_OFFSETS(S) \
+  zlog_warn ("&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
+             (void *)(S), \
+             (unsigned long) (S)->size, \
+             (unsigned long) (S)->getp, \
+             (unsigned long) (S)->endp)\
+
+#define STREAM_VERIFY_SANE(S) \
+  do { \
+    if ( !(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp)) ) \
+      STREAM_WARN_OFFSETS(S); \
+    assert ( GETP_VALID(S, (S)->getp) ); \
+    assert ( ENDP_VALID(S, (S)->endp) ); \
+  } while (0)
+
+#define STREAM_BOUND_WARN(S, WHAT) \
+  do { \
+    zlog_warn ("%s: Attempt to %s out of bounds", __func__, (WHAT)); \
+    STREAM_WARN_OFFSETS(S); \
+    assert (0); \
+  } while (0)
+
+/* XXX: Deprecated macro: do not use */
+#define CHECK_SIZE(S, Z) \
+  do { \
+    if (((S)->endp + (Z)) > (S)->size) \
+      { \
+        zlog_warn ("CHECK_SIZE: truncating requested size %lu\n", \
+                   (unsigned long) (Z)); \
+        STREAM_WARN_OFFSETS(S); \
+        (Z) = (S)->size - (S)->endp; \
+      } \
+  } while (0);
+
+/* Make stream buffer. */
+struct stream *
+stream_new (size_t size)
+{
+  struct stream *s;
+
+  assert (size > 0);
+  
+  if (size == 0)
+    {
+      zlog_warn ("stream_new(): called with 0 size!");
+      return NULL;
+    }
+  
+  s = XCALLOC (MTYPE_STREAM, sizeof (struct stream));
+
+  if (s == NULL)
+    return s;
+  
+  if ( (s->data = XMALLOC (MTYPE_STREAM_DATA, size)) == NULL)
+    {
+      XFREE (MTYPE_STREAM, s);
+      return NULL;
+    }
+  
+  s->size = size;
+  return s;
+}
+
+/* Free it now. */
+void
+stream_free (struct stream *s)
+{
+  if (!s)
+    return;
+  
+  XFREE (MTYPE_STREAM_DATA, s->data);
+  XFREE (MTYPE_STREAM, s);
+}
+
+struct stream *
+stream_copy (struct stream *new, struct stream *src)
+{
+  STREAM_VERIFY_SANE (src);
+  
+  assert (new != NULL);
+  assert (STREAM_SIZE(new) >= src->endp);
+
+  new->endp = src->endp;
+  new->getp = src->getp;
+  
+  memcpy (new->data, src->data, src->endp);
+  
+  return new;
+}
+
+struct stream *
+stream_dup (struct stream *s)
+{
+  struct stream *new;
+
+  STREAM_VERIFY_SANE (s);
+
+  if ( (new = stream_new (s->endp)) == NULL)
+    return NULL;
+
+  return (stream_copy (new, s));
+}
+
+struct stream *
+stream_dupcat (struct stream *s1, struct stream *s2, size_t offset)
+{
+  struct stream *new;
+
+  STREAM_VERIFY_SANE (s1);
+  STREAM_VERIFY_SANE (s2);
+
+  if ( (new = stream_new (s1->endp + s2->endp)) == NULL)
+    return NULL;
+
+  memcpy (new->data, s1->data, offset);
+  memcpy (new->data + offset, s2->data, s2->endp);
+  memcpy (new->data + offset + s2->endp, s1->data + offset,
+         (s1->endp - offset));
+  new->endp = s1->endp + s2->endp;
+  return new;
+}
+
+size_t
+stream_resize (struct stream *s, size_t newsize)
+{
+  u_char *newdata;
+  STREAM_VERIFY_SANE (s);
+  
+  newdata = XREALLOC (MTYPE_STREAM_DATA, s->data, newsize);
+  
+  if (newdata == NULL)
+    return s->size;
+  
+  s->data = newdata;
+  s->size = newsize;
+  
+  if (s->endp > s->size)
+    s->endp = s->size;
+  if (s->getp > s->endp)
+    s->getp = s->endp;
+  
+  STREAM_VERIFY_SANE (s);
+  
+  return s->size;
+}
+
+size_t
+stream_get_getp (struct stream *s)
+{
+  STREAM_VERIFY_SANE(s);
+  return s->getp;
+}
+
+size_t
+stream_get_endp (struct stream *s)
+{
+  STREAM_VERIFY_SANE(s);
+  return s->endp;
+}
+
+size_t
+stream_get_size (struct stream *s)
+{
+  STREAM_VERIFY_SANE(s);
+  return s->size;
+}
+
+/* Stream structre' stream pointer related functions.  */
+void
+stream_set_getp (struct stream *s, size_t pos)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, pos))
+    {
+      STREAM_BOUND_WARN (s, "set getp");
+      pos = s->endp;
+    }
+
+  s->getp = pos;
+}
+
+void
+stream_set_endp (struct stream *s, size_t pos)
+{
+  STREAM_VERIFY_SANE(s);
+
+  if (!ENDP_VALID(s, pos))
+    {
+      STREAM_BOUND_WARN (s, "set endp");
+      return;
+    }
+
+  /*
+   * Make sure the current read pointer is not beyond the new endp.
+   */
+  if (s->getp > pos)
+    {
+      STREAM_BOUND_WARN(s, "set endp");
+      return;
+    }
+
+  s->endp = pos;
+  STREAM_VERIFY_SANE(s);
+}
+
+/* Forward pointer. */
+void
+stream_forward_getp (struct stream *s, size_t size)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, s->getp + size))
+    {
+      STREAM_BOUND_WARN (s, "seek getp");
+      return;
+    }
+  
+  s->getp += size;
+}
+
+void
+stream_forward_endp (struct stream *s, size_t size)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!ENDP_VALID (s, s->endp + size))
+    {
+      STREAM_BOUND_WARN (s, "seek endp");
+      return;
+    }
+  
+  s->endp += size;
+}
+
+/* Copy from stream to destination. */
+void
+stream_get (void *dst, struct stream *s, size_t size)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_READABLE(s) < size)
+    {
+      STREAM_BOUND_WARN (s, "get");
+      return;
+    }
+  
+  memcpy (dst, s->data + s->getp, size);
+  s->getp += size;
+}
+
+/* Get next character from the stream. */
+u_char
+stream_getc (struct stream *s)
+{
+  u_char c;
+  
+  STREAM_VERIFY_SANE (s);
+
+  if (STREAM_READABLE(s) < sizeof (u_char))
+    {
+      STREAM_BOUND_WARN (s, "get char");
+      return 0;
+    }
+  c = s->data[s->getp++];
+  
+  return c;
+}
+
+/* Get next character from the stream. */
+u_char
+stream_getc_from (struct stream *s, size_t from)
+{
+  u_char c;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, from + sizeof (u_char)))
+    {
+      STREAM_BOUND_WARN (s, "get char");
+      return 0;
+    }
+  
+  c = s->data[from];
+  
+  return c;
+}
+
+/* Get next word from the stream. */
+u_int16_t
+stream_getw (struct stream *s)
+{
+  u_int16_t w;
+
+  STREAM_VERIFY_SANE (s);
+
+  if (STREAM_READABLE (s) < sizeof (u_int16_t))
+    {
+      STREAM_BOUND_WARN (s, "get ");
+      return 0;
+    }
+  
+  w = s->data[s->getp++] << 8;
+  w |= s->data[s->getp++];
+  
+  return w;
+}
+
+/* Get next word from the stream. */
+u_int16_t
+stream_getw_from (struct stream *s, size_t from)
+{
+  u_int16_t w;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, from + sizeof (u_int16_t)))
+    {
+      STREAM_BOUND_WARN (s, "get ");
+      return 0;
+    }
+  
+  w = s->data[from++] << 8;
+  w |= s->data[from];
+  
+  return w;
+}
+
+/* Get next long word from the stream. */
+u_int32_t
+stream_getl_from (struct stream *s, size_t from)
+{
+  u_int32_t l;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, from + sizeof (u_int32_t)))
+    {
+      STREAM_BOUND_WARN (s, "get long");
+      return 0;
+    }
+  
+  l  = s->data[from++] << 24;
+  l |= s->data[from++] << 16;
+  l |= s->data[from++] << 8;
+  l |= s->data[from];
+  
+  return l;
+}
+
+u_int32_t
+stream_getl (struct stream *s)
+{
+  u_int32_t l;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_READABLE (s) < sizeof (u_int32_t))
+    {
+      STREAM_BOUND_WARN (s, "get long");
+      return 0;
+    }
+  
+  l  = s->data[s->getp++] << 24;
+  l |= s->data[s->getp++] << 16;
+  l |= s->data[s->getp++] << 8;
+  l |= s->data[s->getp++];
+  
+  return l;
+}
+
+/* Get next quad word from the stream. */
+uint64_t
+stream_getq_from (struct stream *s, size_t from)
+{
+  uint64_t q;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (!GETP_VALID (s, from + sizeof (uint64_t)))
+    {
+      STREAM_BOUND_WARN (s, "get quad");
+      return 0;
+    }
+  
+  q  = ((uint64_t) s->data[from++]) << 56;
+  q |= ((uint64_t) s->data[from++]) << 48;
+  q |= ((uint64_t) s->data[from++]) << 40;
+  q |= ((uint64_t) s->data[from++]) << 32;  
+  q |= ((uint64_t) s->data[from++]) << 24;
+  q |= ((uint64_t) s->data[from++]) << 16;
+  q |= ((uint64_t) s->data[from++]) << 8;
+  q |= ((uint64_t) s->data[from++]);
+  
+  return q;
+}
+
+uint64_t
+stream_getq (struct stream *s)
+{
+  uint64_t q;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_READABLE (s) < sizeof (uint64_t))
+    {
+      STREAM_BOUND_WARN (s, "get quad");
+      return 0;
+    }
+  
+  q  = ((uint64_t) s->data[s->getp++]) << 56;
+  q |= ((uint64_t) s->data[s->getp++]) << 48;
+  q |= ((uint64_t) s->data[s->getp++]) << 40;
+  q |= ((uint64_t) s->data[s->getp++]) << 32;  
+  q |= ((uint64_t) s->data[s->getp++]) << 24;
+  q |= ((uint64_t) s->data[s->getp++]) << 16;
+  q |= ((uint64_t) s->data[s->getp++]) << 8;
+  q |= ((uint64_t) s->data[s->getp++]);
+  
+  return q;
+}
+
+/* Get next long word from the stream. */
+u_int32_t
+stream_get_ipv4 (struct stream *s)
+{
+  u_int32_t l;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_READABLE (s) < sizeof(u_int32_t))
+    {
+      STREAM_BOUND_WARN (s, "get ipv4");
+      return 0;
+    }
+  
+  memcpy (&l, s->data + s->getp, sizeof(u_int32_t));
+  s->getp += sizeof(u_int32_t);
+
+  return l;
+}
+
+float
+stream_getf (struct stream *s)
+{
+#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1
+#warning "Unknown floating-point format, __func__ may be wrong"
+#endif
+/* we assume 'float' is in the single precision IEC 60559 binary
+   format, in host byte order */
+  union {
+    float r;
+    uint32_t d;
+  } u;
+  u.d = stream_getl (s);
+  return u.r;
+}
+
+double
+stream_getd (struct stream *s)
+{
+#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1
+#warning "Unknown floating-point format, __func__ may be wrong"
+#endif
+  union {
+    double r;
+    uint64_t d;
+  } u;
+  u.d = stream_getq (s);
+  return u.r;
+}
+
+/* Copy to source to stream.
+ *
+ * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap
+ * around. This should be fixed once the stream updates are working.
+ *
+ * stream_write() is saner
+ */
+void
+stream_put (struct stream *s, const void *src, size_t size)
+{
+
+  /* XXX: CHECK_SIZE has strange semantics. It should be deprecated */
+  CHECK_SIZE(s, size);
+  
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return;
+    }
+  
+  if (src)
+    memcpy (s->data + s->endp, src, size);
+  else
+    memset (s->data + s->endp, 0, size);
+
+  s->endp += size;
+}
+
+/* Put character to the stream. */
+int
+stream_putc (struct stream *s, u_char c)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < sizeof(u_char))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[s->endp++] = c;
+  return sizeof (u_char);
+}
+
+/* Put word to the stream. */
+int
+stream_putw (struct stream *s, u_int16_t w)
+{
+  STREAM_VERIFY_SANE (s);
+
+  if (STREAM_WRITEABLE (s) < sizeof (u_int16_t))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[s->endp++] = (u_char)(w >>  8);
+  s->data[s->endp++] = (u_char) w;
+
+  return 2;
+}
+
+/* Put long word to the stream. */
+int
+stream_putl (struct stream *s, u_int32_t l)
+{
+  STREAM_VERIFY_SANE (s);
+
+  if (STREAM_WRITEABLE (s) < sizeof (u_int32_t))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[s->endp++] = (u_char)(l >> 24);
+  s->data[s->endp++] = (u_char)(l >> 16);
+  s->data[s->endp++] = (u_char)(l >>  8);
+  s->data[s->endp++] = (u_char)l;
+
+  return 4;
+}
+
+/* Put quad word to the stream. */
+int
+stream_putq (struct stream *s, uint64_t q)
+{
+  STREAM_VERIFY_SANE (s);
+
+  if (STREAM_WRITEABLE (s) < sizeof (uint64_t))
+    {
+      STREAM_BOUND_WARN (s, "put quad");
+      return 0;
+    }
+  
+  s->data[s->endp++] = (u_char)(q >> 56);
+  s->data[s->endp++] = (u_char)(q >> 48);
+  s->data[s->endp++] = (u_char)(q >> 40);
+  s->data[s->endp++] = (u_char)(q >> 32);
+  s->data[s->endp++] = (u_char)(q >> 24);
+  s->data[s->endp++] = (u_char)(q >> 16);
+  s->data[s->endp++] = (u_char)(q >>  8);
+  s->data[s->endp++] = (u_char)q;
+
+  return 8;
+}
+
+int
+stream_putf (struct stream *s, float f)
+{
+#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1
+#warning "Unknown floating-point format, __func__ may be wrong"
+#endif
+
+/* we can safely assume 'float' is in the single precision
+   IEC 60559 binary format in host order */
+  union {
+    float i;
+    uint32_t o;
+  } u;
+  u.i = f;
+  return stream_putl (s, u.o);
+}
+
+int
+stream_putd (struct stream *s, double d)
+{
+#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1
+#warning "Unknown floating-point format, __func__ may be wrong"
+#endif
+  union {
+    double i;
+    uint64_t o;
+  } u;
+  u.i = d;
+  return stream_putq (s, u.o);
+}
+
+int
+stream_putc_at (struct stream *s, size_t putp, u_char c)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!PUT_AT_VALID (s, putp + sizeof (u_char)))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[putp] = c;
+  
+  return 1;
+}
+
+int
+stream_putw_at (struct stream *s, size_t putp, u_int16_t w)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!PUT_AT_VALID (s, putp + sizeof (u_int16_t)))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[putp] = (u_char)(w >>  8);
+  s->data[putp + 1] = (u_char) w;
+  
+  return 2;
+}
+
+int
+stream_putl_at (struct stream *s, size_t putp, u_int32_t l)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!PUT_AT_VALID (s, putp + sizeof (u_int32_t)))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  s->data[putp] = (u_char)(l >> 24);
+  s->data[putp + 1] = (u_char)(l >> 16);
+  s->data[putp + 2] = (u_char)(l >>  8);
+  s->data[putp + 3] = (u_char)l;
+  
+  return 4;
+}
+
+int
+stream_putq_at (struct stream *s, size_t putp, uint64_t q)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (!PUT_AT_VALID (s, putp + sizeof (uint64_t)))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  s->data[putp] =     (u_char)(q >> 56);
+  s->data[putp + 1] = (u_char)(q >> 48);
+  s->data[putp + 2] = (u_char)(q >> 40);
+  s->data[putp + 3] = (u_char)(q >> 32);
+  s->data[putp + 4] = (u_char)(q >> 24);
+  s->data[putp + 5] = (u_char)(q >> 16);
+  s->data[putp + 6] = (u_char)(q >>  8);
+  s->data[putp + 7] = (u_char)q;
+  
+  return 8;
+}
+
+/* Put long word to the stream. */
+int
+stream_put_ipv4 (struct stream *s, u_int32_t l)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < sizeof (u_int32_t))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  memcpy (s->data + s->endp, &l, sizeof (u_int32_t));
+  s->endp += sizeof (u_int32_t);
+
+  return sizeof (u_int32_t);
+}
+
+/* Put long word to the stream. */
+int
+stream_put_in_addr (struct stream *s, struct in_addr *addr)
+{
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < sizeof (u_int32_t))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+
+  memcpy (s->data + s->endp, addr, sizeof (u_int32_t));
+  s->endp += sizeof (u_int32_t);
+
+  return sizeof (u_int32_t);
+}
+
+/* Put prefix by nlri type format. */
+int
+stream_put_prefix (struct stream *s, struct prefix *p)
+{
+  size_t psize;
+  
+  STREAM_VERIFY_SANE(s);
+  
+  psize = PSIZE (p->prefixlen);
+  
+  if (STREAM_WRITEABLE (s) < (psize + sizeof (u_char)))
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  s->data[s->endp++] = p->prefixlen;
+  memcpy (s->data + s->endp, &p->u.prefix, psize);
+  s->endp += psize;
+  
+  return psize;
+}
+
+/* Read size from fd. */
+int
+stream_read (struct stream *s, int fd, size_t size)
+{
+  int nbytes;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  nbytes = readn (fd, s->data + s->endp, size);
+
+  if (nbytes > 0)
+    s->endp += nbytes;
+  
+  return nbytes;
+}
+
+ssize_t
+stream_read_try(struct stream *s, int fd, size_t size)
+{
+  ssize_t nbytes;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE(s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      /* Fatal (not transient) error, since retrying will not help
+         (stream is too small to contain the desired data). */
+      return -1;
+    }
+
+  if ((nbytes = read(fd, s->data + s->endp, size)) >= 0)
+    {
+      s->endp += nbytes;
+      return nbytes;
+    }
+  /* Error: was it transient (return -2) or fatal (return -1)? */
+  if (ERRNO_IO_RETRY(errno))
+    return -2;
+  zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno));
+  return -1;
+}
+
+/* Read up to size bytes into the stream from the fd, using recvmsgfrom
+ * whose arguments match the remaining arguments to this function
+ */
+ssize_t 
+stream_recvfrom (struct stream *s, int fd, size_t size, int flags,
+                 struct sockaddr *from, socklen_t *fromlen)                     
+{
+  ssize_t nbytes;
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE(s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      /* Fatal (not transient) error, since retrying will not help
+         (stream is too small to contain the desired data). */
+      return -1;
+    }
+
+  if ((nbytes = recvfrom (fd, s->data + s->endp, size, 
+                          flags, from, fromlen)) >= 0)
+    {
+      s->endp += nbytes;
+      return nbytes;
+    }
+  /* Error: was it transient (return -2) or fatal (return -1)? */
+  if (ERRNO_IO_RETRY(errno))
+    return -2;
+  zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno));
+  return -1;
+}
+
+/* Read up to smaller of size or SIZE_REMAIN() bytes to the stream, starting
+ * from endp.
+ * First iovec will be used to receive the data.
+ * Stream need not be empty.
+ */
+ssize_t
+stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags, 
+                size_t size)
+{
+  int nbytes;
+  struct iovec *iov;
+  
+  STREAM_VERIFY_SANE(s);
+  assert (msgh->msg_iovlen > 0);  
+  
+  if (STREAM_WRITEABLE (s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      /* This is a logic error in the calling code: the stream is too small
+         to hold the desired data! */
+      return -1;
+    }
+  
+  iov = &(msgh->msg_iov[0]);
+  iov->iov_base = (s->data + s->endp);
+  iov->iov_len = size;
+  
+  nbytes = recvmsg (fd, msgh, flags);
+  
+  if (nbytes > 0)
+    s->endp += nbytes;
+  
+  return nbytes;
+}
+  
+/* Write data to buffer. */
+size_t
+stream_write (struct stream *s, const void *ptr, size_t size)
+{
+
+  CHECK_SIZE(s, size);
+
+  STREAM_VERIFY_SANE(s);
+  
+  if (STREAM_WRITEABLE (s) < size)
+    {
+      STREAM_BOUND_WARN (s, "put");
+      return 0;
+    }
+  
+  memcpy (s->data + s->endp, ptr, size);
+  s->endp += size;
+
+  return size;
+}
+
+/* Return current read pointer. 
+ * DEPRECATED!
+ * Use stream_get_pnt_to if you must, but decoding streams properly
+ * is preferred
+ */
+u_char *
+stream_pnt (struct stream *s)
+{
+  STREAM_VERIFY_SANE(s);
+  return s->data + s->getp;
+}
+
+/* Check does this stream empty? */
+int
+stream_empty (struct stream *s)
+{
+  STREAM_VERIFY_SANE(s);
+
+  return (s->endp == 0);
+}
+
+/* Reset stream. */
+void
+stream_reset (struct stream *s)
+{
+  STREAM_VERIFY_SANE (s);
+
+  s->getp = s->endp = 0;
+}
+
+/* Discard read data (prior to the getp), and move the unread data
+ * to the beginning of the stream.
+ *
+ * See also stream_fifo_* functions, for another approach to managing
+ * streams.
+ */
+void
+stream_discard (struct stream *s)
+{
+  STREAM_VERIFY_SANE (s);
+  
+  if (s->getp == 0)
+    return;
+  
+  if (s->getp == s->endp)
+    {
+      stream_reset (s);
+      return;
+    }
+  
+  s->data = memmove (s->data, s->data + s->getp, s->endp - s->getp);
+  s->endp -= s->getp;
+  s->getp = 0;
+}
+
+/* Write stream contens to the file discriptor. */
+int
+stream_flush (struct stream *s, int fd)
+{
+  int nbytes;
+  
+  STREAM_VERIFY_SANE(s);
+  
+  nbytes = write (fd, s->data + s->getp, s->endp - s->getp);
+  
+  return nbytes;
+}
+
+/* Stream first in first out queue. */
+
+struct stream_fifo *
+stream_fifo_new (void)
+{
+  struct stream_fifo *new;
+  new = XCALLOC (MTYPE_STREAM_FIFO, sizeof (struct stream_fifo));
+  return new;
+}
+
+/* Add new stream to fifo. */
+void
+stream_fifo_push (struct stream_fifo *fifo, struct stream *s)
+{
+  if (fifo->tail)
+    fifo->tail->next = s;
+  else
+    fifo->head = s;
+     
+  fifo->tail = s;
+
+  fifo->count++;
+}
+
+/* Delete first stream from fifo. */
+struct stream *
+stream_fifo_pop (struct stream_fifo *fifo)
+{
+  struct stream *s;
+  
+  s = fifo->head; 
+
+  if (s)
+    { 
+      fifo->head = s->next;
+
+      if (fifo->head == NULL)
+       fifo->tail = NULL;
+
+      fifo->count--;
+    }
+
+  return s; 
+}
+
+/* Return first fifo entry. */
+struct stream *
+stream_fifo_head (struct stream_fifo *fifo)
+{
+  return fifo->head;
+}
+
+void
+stream_fifo_clean (struct stream_fifo *fifo)
+{
+  struct stream *s;
+  struct stream *next;
+
+  for (s = fifo->head; s; s = next)
+    {
+      next = s->next;
+      stream_free (s);
+    }
+  fifo->head = fifo->tail = NULL;
+  fifo->count = 0;
+}
+
+void
+stream_fifo_free (struct stream_fifo *fifo)
+{
+  stream_fifo_clean (fifo);
+  XFREE (MTYPE_STREAM_FIFO, fifo);
+}
diff --git a/lib/stream.h b/lib/stream.h
new file mode 100644 (file)
index 0000000..9127bcd
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Packet interface
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_STREAM_H
+#define _ZEBRA_STREAM_H
+
+#include "prefix.h"
+
+/*
+ * A stream is an arbitrary buffer, whose contents generally are assumed to
+ * be in network order.
+ *
+ * A stream has the following attributes associated with it:
+ *
+ * - size: the allocated, invariant size of the buffer.
+ *
+ * - getp: the get position marker, denoting the offset in the stream where
+ *         the next read (or 'get') will be from. This getp marker is
+ *         automatically adjusted when data is read from the stream, the
+ *         user may also manipulate this offset as they wish, within limits
+ *         (see below)
+ *
+ * - endp: the end position marker, denoting the offset in the stream where
+ *         valid data ends, and if the user attempted to write (or
+ *         'put') data where that data would be written (or 'put') to.
+ *
+ * These attributes are all size_t values.
+ *
+ * Constraints:
+ *
+ * 1. getp can never exceed endp
+ *
+ * - hence if getp is equal to endp, there is no more valid data that can be
+ *   gotten from the stream (though, the user may reposition getp to earlier in
+ *   the stream, if they wish).
+ *
+ * 2. endp can never exceed size
+ *
+ * - hence, if endp is equal to size, then the stream is full, and no more
+ *   data can be written to the stream.
+ *
+ * In other words the following must always be true, and the stream
+ * abstraction is allowed internally to assert that the following property
+ * holds true for a stream, as and when it wishes:
+ *
+ *        getp <= endp <= size
+ *
+ * It is the users responsibility to ensure this property is never violated.
+ *
+ * A stream therefore can be thought of like this:
+ *
+ *     ---------------------------------------------------
+ *     |XXXXXXXXXXXXXXXXXXXXXXXX                         |
+ *     ---------------------------------------------------
+ *               ^               ^                        ^
+ *               getp            endp                     size
+ *
+ * This shows a stream containing data (shown as 'X') up to the endp offset.
+ * The stream is empty from endp to size. Without adjusting getp, there are
+ * still endp-getp bytes of valid data to be read from the stream.
+ *
+ * Methods are provided to get and put to/from the stream, as well as 
+ * retrieve the values of the 3 markers and manipulate the getp marker.
+ *
+ * Note:
+ * At the moment, newly allocated streams are zero filled. Hence, one can
+ * use stream_forward_endp() to effectively create arbitrary zero-fill
+ * padding. However, note that stream_reset() does *not* zero-out the
+ * stream. This property should **not** be relied upon.
+ *
+ * Best practice is to use stream_put (<stream *>, NULL, <size>) to zero out
+ * any part of a stream which isn't otherwise written to.
+ */
+
+/* Stream buffer. */
+struct stream
+{
+  struct stream *next;
+
+  /* Remainder is ***private*** to stream
+   * direct access is frowned upon!
+   * Use the appropriate functions/macros 
+   */
+  size_t getp;                 /* next get position */
+  size_t endp;         /* last valid data position */
+  size_t size;         /* size of data segment */
+  unsigned char *data; /* data pointer */
+};
+
+/* First in first out queue structure. */
+struct stream_fifo
+{
+  size_t count;
+
+  struct stream *head;
+  struct stream *tail;
+};
+
+/* Utility macros. */
+#define STREAM_SIZE(S)  ((S)->size)
+  /* number of bytes which can still be written */
+#define STREAM_WRITEABLE(S) ((S)->size - (S)->endp)
+  /* number of bytes still to be read */
+#define STREAM_READABLE(S) ((S)->endp - (S)->getp)
+
+#define STREAM_CONCAT_REMAIN(S1, S2, size) \
+  ((size) - (S1)->endp - (S2)->endp)
+
+/* deprecated macros - do not use in new code */
+#define STREAM_PNT(S)   stream_pnt((S))
+#define STREAM_DATA(S)  ((S)->data)
+#define STREAM_REMAIN(S) STREAM_WRITEABLE((S))
+
+/* Stream prototypes. 
+ * For stream_{put,get}S, the S suffix mean:
+ *
+ * c: character (unsigned byte)
+ * w: word (two bytes)
+ * l: long (two words)
+ * q: quad (four words)
+ */
+extern struct stream *stream_new (size_t);
+extern void stream_free (struct stream *);
+extern struct stream * stream_copy (struct stream *, struct stream *src);
+extern struct stream *stream_dup (struct stream *);
+extern size_t stream_resize (struct stream *, size_t);
+extern size_t stream_get_getp (struct stream *);
+extern size_t stream_get_endp (struct stream *);
+extern size_t stream_get_size (struct stream *);
+extern u_char *stream_get_data (struct stream *);
+
+/**
+ * Create a new stream structure; copy offset bytes from s1 to the new
+ * stream; copy s2 data to the new stream; copy rest of s1 data to the
+ * new stream.
+ */
+extern struct stream *stream_dupcat(struct stream *s1, struct stream *s2,
+                                   size_t offset);
+
+extern void stream_set_getp (struct stream *, size_t);
+extern void stream_set_endp (struct stream *, size_t);
+extern void stream_forward_getp (struct stream *, size_t);
+extern void stream_forward_endp (struct stream *, size_t);
+
+/* steam_put: NULL source zeroes out size_t bytes of stream */
+extern void stream_put (struct stream *, const void *, size_t);
+extern int stream_putc (struct stream *, u_char);
+extern int stream_putc_at (struct stream *, size_t, u_char);
+extern int stream_putw (struct stream *, u_int16_t);
+extern int stream_putw_at (struct stream *, size_t, u_int16_t);
+extern int stream_putl (struct stream *, u_int32_t);
+extern int stream_putl_at (struct stream *, size_t, u_int32_t);
+extern int stream_putq (struct stream *, uint64_t);
+extern int stream_putq_at (struct stream *, size_t, uint64_t);
+extern int stream_put_ipv4 (struct stream *, u_int32_t);
+extern int stream_put_in_addr (struct stream *, struct in_addr *);
+extern int stream_put_prefix (struct stream *, struct prefix *);
+
+extern void stream_get (void *, struct stream *, size_t);
+extern u_char stream_getc (struct stream *);
+extern u_char stream_getc_from (struct stream *, size_t);
+extern u_int16_t stream_getw (struct stream *);
+extern u_int16_t stream_getw_from (struct stream *, size_t);
+extern u_int32_t stream_getl (struct stream *);
+extern u_int32_t stream_getl_from (struct stream *, size_t);
+extern uint64_t stream_getq (struct stream *);
+extern uint64_t stream_getq_from (struct stream *, size_t);
+extern u_int32_t stream_get_ipv4 (struct stream *);
+
+/* IEEE-754 floats */
+extern float stream_getf (struct stream *);
+extern double stream_getd (struct stream *);
+extern int stream_putf (struct stream *, float);
+extern int stream_putd (struct stream *, double);
+
+#undef stream_read
+#undef stream_write
+
+/* Deprecated: assumes blocking I/O.  Will be removed. 
+   Use stream_read_try instead.  */
+extern int stream_read (struct stream *, int, size_t);
+
+/* Read up to size bytes into the stream.
+   Return code:
+     >0: number of bytes read
+     0: end-of-file
+     -1: fatal error
+     -2: transient error, should retry later (i.e. EAGAIN or EINTR)
+   This is suitable for use with non-blocking file descriptors.
+ */
+extern ssize_t stream_read_try(struct stream *s, int fd, size_t size);
+
+extern ssize_t stream_recvmsg (struct stream *s, int fd, struct msghdr *,
+                               int flags, size_t size);
+extern ssize_t stream_recvfrom (struct stream *s, int fd, size_t len, 
+                                int flags, struct sockaddr *from, 
+                                socklen_t *fromlen);
+extern size_t stream_write (struct stream *, const void *, size_t);
+
+/* reset the stream. See Note above */
+extern void stream_reset (struct stream *);
+/* move unread data to start of stream, discarding read data */
+extern void stream_discard (struct stream *);
+extern int stream_flush (struct stream *, int);
+extern int stream_empty (struct stream *); /* is the stream empty? */
+
+/* deprecated */
+extern u_char *stream_pnt (struct stream *);
+
+/* Stream fifo. */
+extern struct stream_fifo *stream_fifo_new (void);
+extern void stream_fifo_push (struct stream_fifo *fifo, struct stream *s);
+extern struct stream *stream_fifo_pop (struct stream_fifo *fifo);
+extern struct stream *stream_fifo_head (struct stream_fifo *fifo);
+extern void stream_fifo_clean (struct stream_fifo *fifo);
+extern void stream_fifo_free (struct stream_fifo *fifo);
+
+#endif /* _ZEBRA_STREAM_H */
diff --git a/lib/table.c b/lib/table.c
new file mode 100644 (file)
index 0000000..da21361
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ * Routing Table functions.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "sockunion.h"
+
+static void route_node_delete (struct route_node *);
+static void route_table_free (struct route_table *);
+
+
+/*
+ * route_table_init_with_delegate
+ */
+struct route_table *
+route_table_init_with_delegate (route_table_delegate_t *delegate)
+{
+  struct route_table *rt;
+
+  rt = XCALLOC (MTYPE_ROUTE_TABLE, sizeof (struct route_table));
+  rt->delegate = delegate;
+  return rt;
+}
+
+void
+route_table_finish (struct route_table *rt)
+{
+  route_table_free (rt);
+}
+
+/* Allocate new route node. */
+static struct route_node *
+route_node_new (struct route_table *table)
+{
+  return table->delegate->create_node (table->delegate, table);
+}
+
+/* Allocate new route node with prefix set. */
+static struct route_node *
+route_node_set (struct route_table *table, const struct prefix *prefix)
+{
+  struct route_node *node;
+  
+  node = route_node_new (table);
+
+  prefix_copy (&node->p, prefix);
+  node->table = table;
+
+  return node;
+}
+
+/* Free route node. */
+static void
+route_node_free (struct route_table *table, struct route_node *node)
+{
+  table->delegate->destroy_node (table->delegate, table, node);
+}
+
+/* Free route table. */
+static void
+route_table_free (struct route_table *rt)
+{
+  struct route_node *tmp_node;
+  struct route_node *node;
+  if (rt == NULL)
+    return;
+
+  node = rt->top;
+
+  /* Bulk deletion of nodes remaining in this table.  This function is not
+     called until workers have completed their dependency on this table.
+     A final route_unlock_node() will not be called for these nodes. */
+  while (node)
+    {
+      if (node->l_left)
+       {
+         node = node->l_left;
+         continue;
+       }
+
+      if (node->l_right)
+       {
+         node = node->l_right;
+         continue;
+       }
+
+      tmp_node = node;
+      node = node->parent;
+
+      tmp_node->table->count--;
+      tmp_node->lock = 0;  /* to cause assert if unlocked after this */
+      route_node_free (rt, tmp_node);
+
+      if (node != NULL)
+       {
+         if (node->l_left == tmp_node)
+           node->l_left = NULL;
+         else
+           node->l_right = NULL;
+       }
+      else
+       {
+         break;
+       }
+    }
+  assert (rt->count == 0);
+
+  XFREE (MTYPE_ROUTE_TABLE, rt);
+  return;
+}
+
+/* Utility mask array. */
+static const u_char maskbit[] =
+{
+  0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+/* Common prefix route genaration. */
+static void
+route_common (const struct prefix *n, const struct prefix *p, struct prefix *new)
+{
+  int i;
+  u_char diff;
+  u_char mask;
+
+  const u_char *np = (const u_char *)&n->u.prefix;
+  const u_char *pp = (const u_char *)&p->u.prefix;
+  u_char *newp = (u_char *)&new->u.prefix;
+
+  for (i = 0; i < p->prefixlen / 8; i++)
+    {
+      if (np[i] == pp[i])
+       newp[i] = np[i];
+      else
+       break;
+    }
+
+  new->prefixlen = i * 8;
+
+  if (new->prefixlen != p->prefixlen)
+    {
+      diff = np[i] ^ pp[i];
+      mask = 0x80;
+      while (new->prefixlen < p->prefixlen && !(mask & diff))
+       {
+         mask >>= 1;
+         new->prefixlen++;
+       }
+      newp[i] = np[i] & maskbit[new->prefixlen % 8];
+    }
+}
+
+static void
+set_link (struct route_node *node, struct route_node *new)
+{
+  unsigned int bit = prefix_bit (&new->p.u.prefix, node->p.prefixlen);
+
+  node->link[bit] = new;
+  new->parent = node;
+}
+
+/* Lock node. */
+struct route_node *
+route_lock_node (struct route_node *node)
+{
+  node->lock++;
+  return node;
+}
+
+/* Unlock node. */
+void
+route_unlock_node (struct route_node *node)
+{
+  assert (node->lock > 0);
+  node->lock--;
+
+  if (node->lock == 0)
+    route_node_delete (node);
+}
+
+/* Find matched prefix. */
+struct route_node *
+route_node_match (const struct route_table *table, const struct prefix *p)
+{
+  struct route_node *node;
+  struct route_node *matched;
+
+  matched = NULL;
+  node = table->top;
+
+  /* Walk down tree.  If there is matched route then store it to
+     matched. */
+  while (node && node->p.prefixlen <= p->prefixlen && 
+        prefix_match (&node->p, p))
+    {
+      if (node->info)
+       matched = node;
+      
+      if (node->p.prefixlen == p->prefixlen)
+        break;
+      
+      node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)];
+    }
+
+  /* If matched route found, return it. */
+  if (matched)
+    return route_lock_node (matched);
+
+  return NULL;
+}
+
+struct route_node *
+route_node_match_ipv4 (const struct route_table *table,
+                      const struct in_addr *addr)
+{
+  struct prefix_ipv4 p;
+
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = *addr;
+
+  return route_node_match (table, (struct prefix *) &p);
+}
+
+#ifdef HAVE_IPV6
+struct route_node *
+route_node_match_ipv6 (const struct route_table *table,
+                      const struct in6_addr *addr)
+{
+  struct prefix_ipv6 p;
+
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = IPV6_MAX_PREFIXLEN;
+  p.prefix = *addr;
+
+  return route_node_match (table, (struct prefix *) &p);
+}
+#endif /* HAVE_IPV6 */
+
+/* Lookup same prefix node.  Return NULL when we can't find route. */
+struct route_node *
+route_node_lookup (const struct route_table *table, const struct prefix *p)
+{
+  struct route_node *node;
+  u_char prefixlen = p->prefixlen;
+  const u_char *prefix = &p->u.prefix;
+
+  node = table->top;
+
+  while (node && node->p.prefixlen <= prefixlen &&
+        prefix_match (&node->p, p))
+    {
+      if (node->p.prefixlen == prefixlen)
+        return node->info ? route_lock_node (node) : NULL;
+
+      node = node->link[prefix_bit(prefix, node->p.prefixlen)];
+    }
+
+  return NULL;
+}
+
+/* Add node to routing table. */
+struct route_node *
+route_node_get (struct route_table *const table, const struct prefix *p)
+{
+  struct route_node *new;
+  struct route_node *node;
+  struct route_node *match;
+  u_char prefixlen = p->prefixlen;
+  const u_char *prefix = &p->u.prefix;
+
+  match = NULL;
+  node = table->top;
+  while (node && node->p.prefixlen <= prefixlen &&
+        prefix_match (&node->p, p))
+    {
+      if (node->p.prefixlen == prefixlen)
+        return route_lock_node (node);
+
+      match = node;
+      node = node->link[prefix_bit(prefix, node->p.prefixlen)];
+    }
+
+  if (node == NULL)
+    {
+      new = route_node_set (table, p);
+      if (match)
+       set_link (match, new);
+      else
+       table->top = new;
+    }
+  else
+    {
+      new = route_node_new (table);
+      route_common (&node->p, p, &new->p);
+      new->p.family = p->family;
+      new->table = table;
+      set_link (new, node);
+
+      if (match)
+       set_link (match, new);
+      else
+       table->top = new;
+
+      if (new->p.prefixlen != p->prefixlen)
+       {
+         match = new;
+         new = route_node_set (table, p);
+         set_link (match, new);
+         table->count++;
+       }
+    }
+  table->count++;
+  route_lock_node (new);
+  
+  return new;
+}
+
+/* Delete node from the routing table. */
+static void
+route_node_delete (struct route_node *node)
+{
+  struct route_node *child;
+  struct route_node *parent;
+
+  assert (node->lock == 0);
+  assert (node->info == NULL);
+
+  if (node->l_left && node->l_right)
+    return;
+
+  if (node->l_left)
+    child = node->l_left;
+  else
+    child = node->l_right;
+
+  parent = node->parent;
+
+  if (child)
+    child->parent = parent;
+
+  if (parent)
+    {
+      if (parent->l_left == node)
+       parent->l_left = child;
+      else
+       parent->l_right = child;
+    }
+  else
+    node->table->top = child;
+
+  node->table->count--;
+
+  route_node_free (node->table, node);
+
+  /* If parent node is stub then delete it also. */
+  if (parent && parent->lock == 0)
+    route_node_delete (parent);
+}
+
+/* Get fist node and lock it.  This function is useful when one want
+   to lookup all the node exist in the routing table. */
+struct route_node *
+route_top (struct route_table *table)
+{
+  /* If there is no node in the routing table return NULL. */
+  if (table->top == NULL)
+    return NULL;
+
+  /* Lock the top node and return it. */
+  route_lock_node (table->top);
+  return table->top;
+}
+
+/* Unlock current node and lock next node then return it. */
+struct route_node *
+route_next (struct route_node *node)
+{
+  struct route_node *next;
+  struct route_node *start;
+
+  /* Node may be deleted from route_unlock_node so we have to preserve
+     next node's pointer. */
+
+  if (node->l_left)
+    {
+      next = node->l_left;
+      route_lock_node (next);
+      route_unlock_node (node);
+      return next;
+    }
+  if (node->l_right)
+    {
+      next = node->l_right;
+      route_lock_node (next);
+      route_unlock_node (node);
+      return next;
+    }
+
+  start = node;
+  while (node->parent)
+    {
+      if (node->parent->l_left == node && node->parent->l_right)
+       {
+         next = node->parent->l_right;
+         route_lock_node (next);
+         route_unlock_node (start);
+         return next;
+       }
+      node = node->parent;
+    }
+  route_unlock_node (start);
+  return NULL;
+}
+
+/* Unlock current node and lock next node until limit. */
+struct route_node *
+route_next_until (struct route_node *node, struct route_node *limit)
+{
+  struct route_node *next;
+  struct route_node *start;
+
+  /* Node may be deleted from route_unlock_node so we have to preserve
+     next node's pointer. */
+
+  if (node->l_left)
+    {
+      next = node->l_left;
+      route_lock_node (next);
+      route_unlock_node (node);
+      return next;
+    }
+  if (node->l_right)
+    {
+      next = node->l_right;
+      route_lock_node (next);
+      route_unlock_node (node);
+      return next;
+    }
+
+  start = node;
+  while (node->parent && node != limit)
+    {
+      if (node->parent->l_left == node && node->parent->l_right)
+       {
+         next = node->parent->l_right;
+         route_lock_node (next);
+         route_unlock_node (start);
+         return next;
+       }
+      node = node->parent;
+    }
+  route_unlock_node (start);
+  return NULL;
+}
+
+unsigned long
+route_table_count (const struct route_table *table)
+{
+  return table->count;
+}
+
+/**
+ * route_node_create
+ *
+ * Default function for creating a route node.
+ */
+static struct route_node *
+route_node_create (route_table_delegate_t *delegate,
+                  struct route_table *table)
+{
+  struct route_node *node;
+  node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node));
+  return node;
+}
+
+/**
+ * route_node_destroy
+ *
+ * Default function for destroying a route node.
+ */
+static void
+route_node_destroy (route_table_delegate_t *delegate,
+                   struct route_table *table, struct route_node *node)
+{
+  XFREE (MTYPE_ROUTE_NODE, node);
+}
+
+/*
+ * Default delegate.
+ */
+static route_table_delegate_t default_delegate = {
+  .create_node = route_node_create,
+  .destroy_node = route_node_destroy
+};
+
+/*
+ * route_table_init
+ */
+struct route_table *
+route_table_init (void)
+{
+  return route_table_init_with_delegate (&default_delegate);
+}
+
+/**
+ * route_table_prefix_iter_cmp
+ *
+ * Compare two prefixes according to the order in which they appear in
+ * an iteration over a tree.
+ * 
+ * @return -1 if p1 occurs before p2 (p1 < p2)
+ *          0 if the prefixes are identical (p1 == p2)
+ *         +1 if p1 occurs after p2 (p1 > p2)
+ */
+int
+route_table_prefix_iter_cmp (struct prefix *p1, struct prefix *p2)
+{
+  struct prefix common_space;
+  struct prefix *common = &common_space;
+
+  if (p1->prefixlen <= p2->prefixlen)
+    {
+      if (prefix_match (p1, p2))
+       {
+
+         /*
+          * p1 contains p2, or is equal to it.
+          */
+         return (p1->prefixlen == p2->prefixlen) ? 0 : -1;
+       }
+    }
+  else
+    {
+
+      /*
+       * Check if p2 contains p1.
+       */
+      if (prefix_match (p2, p1))
+         return 1;
+    }
+
+  route_common (p1, p2, common);
+  assert (common->prefixlen < p1->prefixlen);
+  assert (common->prefixlen < p2->prefixlen);
+
+  /*
+   * Both prefixes are longer than the common prefix.
+   *
+   * We need to check the bit after the common prefixlen to determine
+   * which one comes later.
+   */
+  if (prefix_bit (&p1->u.prefix, common->prefixlen))
+    {
+
+      /*
+       * We branch to the right to get to p1 from the common prefix.
+       */
+      assert (!prefix_bit (&p2->u.prefix, common->prefixlen));
+      return 1;
+    }
+
+  /*
+   * We branch to the right to get to p2 from the common prefix.
+   */
+  assert (prefix_bit (&p2->u.prefix, common->prefixlen));
+  return -1;
+}
+
+/*
+ * route_get_subtree_next
+ *
+ * Helper function that returns the first node that follows the nodes
+ * in the sub-tree under 'node' in iteration order.
+ */
+static struct route_node *
+route_get_subtree_next (struct route_node *node)
+{
+  while (node->parent)
+    {
+      if (node->parent->l_left == node && node->parent->l_right)
+       return node->parent->l_right;
+
+      node = node->parent;
+    }
+
+  return NULL;
+}
+
+/**
+ * route_table_get_next_internal
+ *
+ * Helper function to find the node that occurs after the given prefix in
+ * order of iteration.
+ *
+ * @see route_table_get_next
+ */
+static struct route_node *
+route_table_get_next_internal (const struct route_table *table,
+                              struct prefix *p)
+{
+  struct route_node *node, *tmp_node;
+  int cmp;
+
+  node = table->top;
+
+  while (node)
+    {
+      int match;
+
+      if (node->p.prefixlen < p->prefixlen)
+       match = prefix_match (&node->p, p);
+      else
+       match = prefix_match (p, &node->p);
+
+      if (match)
+       {
+         if (node->p.prefixlen == p->prefixlen)
+           {
+
+             /*
+              * The prefix p exists in the tree, just return the next
+              * node.
+              */
+             route_lock_node (node);
+             node = route_next (node);
+             if (node)
+               route_unlock_node (node);
+
+             return (node);
+           }
+
+         if (node->p.prefixlen > p->prefixlen)
+           {
+
+             /*
+              * Node is in the subtree of p, and hence greater than p.
+              */
+             return node;
+           }
+
+         /*
+          * p is in the sub-tree under node.
+          */
+         tmp_node = node->link[prefix_bit (&p->u.prefix, node->p.prefixlen)];
+
+         if (tmp_node)
+           {
+             node = tmp_node;
+             continue;
+           }
+
+         /*
+          * There are no nodes in the direction where p should be. If
+          * node has a right child, then it must be greater than p.
+          */
+         if (node->l_right)
+           return node->l_right;
+
+         /*
+          * No more children to follow, go upwards looking for the next
+          * node.
+          */
+         return route_get_subtree_next (node);
+       }
+
+      /*
+       * Neither node prefix nor 'p' contains the other.
+       */
+      cmp = route_table_prefix_iter_cmp (&node->p, p);
+      if (cmp > 0)
+       {
+
+         /*
+          * Node follows p in iteration order. Return it.
+          */
+         return node;
+       }
+
+      assert (cmp < 0);
+
+      /*
+       * Node and the subtree under it come before prefix p in
+       * iteration order. Prefix p and its sub-tree are not present in
+       * the tree. Go upwards and find the first node that follows the
+       * subtree. That node will also succeed p.
+       */
+      return route_get_subtree_next (node);
+    }
+
+  return NULL;
+}
+
+/**
+ * route_table_get_next
+ *
+ * Find the node that occurs after the given prefix in order of
+ * iteration.
+ */
+struct route_node *
+route_table_get_next (const struct route_table *table, struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_table_get_next_internal (table, p);
+  if (node)
+    {
+      assert (route_table_prefix_iter_cmp (&node->p, p) > 0);
+      route_lock_node (node);
+    }
+  return node;
+}
+
+/*
+ * route_table_iter_init
+ */
+void
+route_table_iter_init (route_table_iter_t * iter, struct route_table *table)
+{
+  memset (iter, 0, sizeof (*iter));
+  iter->state = RT_ITER_STATE_INIT;
+  iter->table = table;
+}
+
+/*
+ * route_table_iter_pause
+ *
+ * Pause an iteration over the table. This allows the iteration to be
+ * resumed point after arbitrary additions/deletions from the table.
+ * An iteration can be resumed by just calling route_table_iter_next()
+ * on the iterator.
+ */
+void
+route_table_iter_pause (route_table_iter_t * iter)
+{
+  switch (iter->state)
+    {
+
+    case RT_ITER_STATE_INIT:
+    case RT_ITER_STATE_PAUSED:
+    case RT_ITER_STATE_DONE:
+      return;
+
+    case RT_ITER_STATE_ITERATING:
+
+      /*
+       * Save the prefix that we are currently at. The next call to
+       * route_table_iter_next() will return the node after this prefix
+       * in the tree.
+       */
+      prefix_copy (&iter->pause_prefix, &iter->current->p);
+      route_unlock_node (iter->current);
+      iter->current = NULL;
+      iter->state = RT_ITER_STATE_PAUSED;
+      return;
+
+    default:
+      assert (0);
+    }
+
+}
+
+/*
+ * route_table_iter_cleanup
+ *
+ * Release any resources held by the iterator.
+ */
+void
+route_table_iter_cleanup (route_table_iter_t * iter)
+{
+  if (iter->state == RT_ITER_STATE_ITERATING)
+    {
+      route_unlock_node (iter->current);
+      iter->current = NULL;
+    }
+  assert (!iter->current);
+
+  /*
+   * Set the state to RT_ITER_STATE_DONE to make any
+   * route_table_iter_next() calls on this iterator return NULL.
+   */
+  iter->state = RT_ITER_STATE_DONE;
+}
diff --git a/lib/table.h b/lib/table.h
new file mode 100644 (file)
index 0000000..2ffd79b
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Routing Table
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_TABLE_H
+#define _ZEBRA_TABLE_H
+
+/*
+ * Forward declarations.
+ */
+struct route_node;
+struct route_table;
+
+/*
+ * route_table_delegate_t
+ *
+ * Function vector that can be used by a client to customize the
+ * behavior of one or more route tables.
+ */
+typedef struct route_table_delegate_t_ route_table_delegate_t;
+
+typedef struct route_node * (*route_table_create_node_func_t) 
+  (route_table_delegate_t *, struct route_table *);
+
+typedef void (*route_table_destroy_node_func_t) 
+  (route_table_delegate_t *, struct route_table *, struct route_node *);
+
+struct route_table_delegate_t_ 
+{
+  route_table_create_node_func_t create_node;
+  route_table_destroy_node_func_t destroy_node;
+};
+
+/* Routing table top structure. */
+struct route_table
+{
+  struct route_node *top;
+
+  /*
+   * Delegate that performs certain functions for this table.
+   */
+  route_table_delegate_t *delegate;
+  
+  unsigned long count;
+  
+  /*
+   * User data.
+   */
+  void *info;
+};
+
+/*
+ * Macro that defines all fields in a route node.
+ */
+#define ROUTE_NODE_FIELDS                      \
+  /* Actual prefix of this radix. */           \
+  struct prefix p;                             \
+                                               \
+  /* Tree link. */                             \
+  struct route_table *table;                   \
+  struct route_node *parent;                   \
+  struct route_node *link[2];                  \
+                                               \
+  /* Lock of this radix */                     \
+  unsigned int lock;                           \
+                                               \
+  /* Each node of route. */                    \
+  void *info;                                  \
+                                               \
+  /* Aggregation. */                           \
+  void *aggregate;
+
+
+/* Each routing entry. */
+struct route_node
+{
+  ROUTE_NODE_FIELDS
+
+#define l_left   link[0]
+#define l_right  link[1]
+};
+
+typedef struct route_table_iter_t_ route_table_iter_t;
+
+typedef enum 
+{
+  RT_ITER_STATE_INIT,
+  RT_ITER_STATE_ITERATING,
+  RT_ITER_STATE_PAUSED,
+  RT_ITER_STATE_DONE
+} route_table_iter_state_t;
+
+/*
+ * route_table_iter_t
+ * 
+ * Structure that holds state for iterating over a route table.
+ */
+struct route_table_iter_t_ 
+{
+
+  route_table_iter_state_t state;
+
+  /*
+   * Routing table that we are iterating over. The caller must ensure
+   * that that table outlives the iterator.
+   */
+  struct route_table *table;
+
+  /*
+   * The node that the iterator is currently on.
+   */
+  struct route_node *current;
+
+  /*
+   * The last prefix that the iterator processed before it was paused.
+   */  
+  struct prefix pause_prefix;
+};
+
+/* Prototypes. */
+extern struct route_table *route_table_init (void);
+
+extern struct route_table *
+route_table_init_with_delegate (route_table_delegate_t *);
+
+extern void route_table_finish (struct route_table *);
+extern void route_unlock_node (struct route_node *node);
+extern struct route_node *route_top (struct route_table *);
+extern struct route_node *route_next (struct route_node *);
+extern struct route_node *route_next_until (struct route_node *,
+                                            struct route_node *);
+extern struct route_node *route_node_get (struct route_table *const,
+                                          const struct prefix *);
+extern struct route_node *route_node_lookup (const struct route_table *,
+                                             const struct prefix *);
+extern struct route_node *route_lock_node (struct route_node *node);
+extern struct route_node *route_node_match (const struct route_table *,
+                                            const struct prefix *);
+extern struct route_node *route_node_match_ipv4 (const struct route_table *,
+                                                const struct in_addr *);
+#ifdef HAVE_IPV6
+extern struct route_node *route_node_match_ipv6 (const struct route_table *,
+                                                const struct in6_addr *);
+#endif /* HAVE_IPV6 */
+
+extern unsigned long route_table_count (const struct route_table *);
+
+extern struct route_node *
+route_table_get_next (const struct route_table *table, struct prefix *p);
+extern int
+route_table_prefix_iter_cmp (struct prefix *p1, struct prefix *p2);
+
+/*
+ * Iterator functions.
+ */
+extern void route_table_iter_init (route_table_iter_t *iter,
+                                  struct route_table *table);
+extern void route_table_iter_pause (route_table_iter_t *iter);
+extern void route_table_iter_cleanup (route_table_iter_t *iter);
+
+/*
+ * Inline functions.
+ */
+
+/*
+ * route_table_iter_next
+ *
+ * Get the next node in the tree.
+ */
+static inline struct route_node *
+route_table_iter_next (route_table_iter_t * iter)
+{
+  struct route_node *node;
+
+  switch (iter->state)
+    {
+
+    case RT_ITER_STATE_INIT:
+
+      /*
+       * We're just starting the iteration.
+       */
+      node = route_top (iter->table);
+      break;
+
+    case RT_ITER_STATE_ITERATING:
+      node = route_next (iter->current);
+      break;
+
+    case RT_ITER_STATE_PAUSED:
+
+      /*
+       * Start with the node following pause_prefix.
+       */
+      node = route_table_get_next (iter->table, &iter->pause_prefix);
+      break;
+
+    case RT_ITER_STATE_DONE:
+      return NULL;
+
+    default:
+      assert (0);
+    }
+
+  iter->current = node;
+  if (node)
+    iter->state = RT_ITER_STATE_ITERATING;
+  else
+    iter->state = RT_ITER_STATE_DONE;
+
+  return node;
+}
+
+/*
+ * route_table_iter_is_done
+ *
+ * Returns TRUE if the iteration is complete.
+ */
+static inline int
+route_table_iter_is_done (route_table_iter_t *iter)
+{
+  return iter->state == RT_ITER_STATE_DONE;
+}
+
+/*
+ * route_table_iter_started
+ *
+ * Returns TRUE if this iterator has started iterating over the tree.
+ */
+static inline int
+route_table_iter_started (route_table_iter_t *iter)
+{
+  return iter->state != RT_ITER_STATE_INIT;
+}
+
+#endif /* _ZEBRA_TABLE_H */
diff --git a/lib/thread.c b/lib/thread.c
new file mode 100644 (file)
index 0000000..de4d76d
--- /dev/null
@@ -0,0 +1,1381 @@
+/* Thread management routine
+ * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* #define DEBUG */
+
+#include <zebra.h>
+#include <sys/resource.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "log.h"
+#include "hash.h"
+#include "pqueue.h"
+#include "command.h"
+#include "sigevent.h"
+
+#if defined(__APPLE__)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+
+/* Recent absolute time of day */
+struct timeval recent_time;
+static struct timeval last_recent_time;
+/* Relative time, since startup */
+static struct timeval relative_time;
+static struct timeval relative_time_base;
+/* init flag */
+static unsigned short timers_inited;
+
+static struct hash *cpu_record = NULL;
+
+/* Struct timeval's tv_usec one second value.  */
+#define TIMER_SECOND_MICRO 1000000L
+
+/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
+   And change negative values to 0. */
+static struct timeval
+timeval_adjust (struct timeval a)
+{
+  while (a.tv_usec >= TIMER_SECOND_MICRO)
+    {
+      a.tv_usec -= TIMER_SECOND_MICRO;
+      a.tv_sec++;
+    }
+
+  while (a.tv_usec < 0)
+    {
+      a.tv_usec += TIMER_SECOND_MICRO;
+      a.tv_sec--;
+    }
+
+  if (a.tv_sec < 0)
+      /* Change negative timeouts to 0. */
+      a.tv_sec = a.tv_usec = 0;
+
+  return a;
+}
+
+static struct timeval
+timeval_subtract (struct timeval a, struct timeval b)
+{
+  struct timeval ret;
+
+  ret.tv_usec = a.tv_usec - b.tv_usec;
+  ret.tv_sec = a.tv_sec - b.tv_sec;
+
+  return timeval_adjust (ret);
+}
+
+static long
+timeval_cmp (struct timeval a, struct timeval b)
+{
+  return (a.tv_sec == b.tv_sec
+         ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
+}
+
+unsigned long
+timeval_elapsed (struct timeval a, struct timeval b)
+{
+  return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
+         + (a.tv_usec - b.tv_usec));
+}
+
+#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__)
+static void
+quagga_gettimeofday_relative_adjust (void)
+{
+  struct timeval diff;
+  if (timeval_cmp (recent_time, last_recent_time) < 0)
+    {
+      relative_time.tv_sec++;
+      relative_time.tv_usec = 0;
+    }
+  else
+    {
+      diff = timeval_subtract (recent_time, last_recent_time);
+      relative_time.tv_sec += diff.tv_sec;
+      relative_time.tv_usec += diff.tv_usec;
+      relative_time = timeval_adjust (relative_time);
+    }
+  last_recent_time = recent_time;
+}
+#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
+
+/* gettimeofday wrapper, to keep recent_time updated */
+static int
+quagga_gettimeofday (struct timeval *tv)
+{
+  int ret;
+  
+  assert (tv);
+  
+  if (!(ret = gettimeofday (&recent_time, NULL)))
+    {
+      /* init... */
+      if (!timers_inited)
+        {
+          relative_time_base = last_recent_time = recent_time;
+          timers_inited = 1;
+        }
+      /* avoid copy if user passed recent_time pointer.. */
+      if (tv != &recent_time)
+        *tv = recent_time;
+      return 0;
+    }
+  return ret;
+}
+
+static int
+quagga_get_relative (struct timeval *tv)
+{
+  int ret;
+
+#ifdef HAVE_CLOCK_MONOTONIC
+  {
+    struct timespec tp;
+    if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
+      {
+        relative_time.tv_sec = tp.tv_sec;
+        relative_time.tv_usec = tp.tv_nsec / 1000;
+      }
+  }
+#elif defined(__APPLE__)
+  {
+    uint64_t ticks;
+    uint64_t useconds;
+    static mach_timebase_info_data_t timebase_info;
+
+    ticks = mach_absolute_time();
+    if (timebase_info.denom == 0)
+      mach_timebase_info(&timebase_info);
+
+    useconds = ticks * timebase_info.numer / timebase_info.denom / 1000;
+    relative_time.tv_sec = useconds / 1000000;
+    relative_time.tv_usec = useconds % 1000000;
+
+    return 0;
+  }
+#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
+  if (!(ret = quagga_gettimeofday (&recent_time)))
+    quagga_gettimeofday_relative_adjust();
+#endif /* HAVE_CLOCK_MONOTONIC */
+
+  if (tv)
+    *tv = relative_time;
+
+  return ret;
+}
+
+/* Get absolute time stamp, but in terms of the internal timer
+ * Could be wrong, but at least won't go back.
+ */
+static void
+quagga_real_stabilised (struct timeval *tv)
+{
+  *tv = relative_time_base;
+  tv->tv_sec += relative_time.tv_sec;
+  tv->tv_usec += relative_time.tv_usec;
+  *tv = timeval_adjust (*tv);
+}
+
+/* Exported Quagga timestamp function.
+ * Modelled on POSIX clock_gettime.
+ */
+int
+quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
+{
+  switch (clkid)
+    {
+      case QUAGGA_CLK_REALTIME:
+        return quagga_gettimeofday (tv);
+      case QUAGGA_CLK_MONOTONIC:
+        return quagga_get_relative (tv);
+      case QUAGGA_CLK_REALTIME_STABILISED:
+        quagga_real_stabilised (tv);
+        return 0;
+      default:
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+/* time_t value in terms of stabilised absolute time. 
+ * replacement for POSIX time()
+ */
+time_t
+quagga_time (time_t *t)
+{
+  struct timeval tv;
+  quagga_real_stabilised (&tv);
+  if (t)
+    *t = tv.tv_sec;
+  return tv.tv_sec;
+}
+
+/* Public export of recent_relative_time by value */
+struct timeval
+recent_relative_time (void)
+{
+  return relative_time;
+}
+
+static unsigned int
+cpu_record_hash_key (struct cpu_thread_history *a)
+{
+  return (uintptr_t) a->func;
+}
+
+static int 
+cpu_record_hash_cmp (const struct cpu_thread_history *a,
+                    const struct cpu_thread_history *b)
+{
+  return a->func == b->func;
+}
+
+static void *
+cpu_record_hash_alloc (struct cpu_thread_history *a)
+{
+  struct cpu_thread_history *new;
+  new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
+  new->func = a->func;
+  new->funcname = a->funcname;
+  return new;
+}
+
+static void
+cpu_record_hash_free (void *a)
+{
+  struct cpu_thread_history *hist = a;
+  XFREE (MTYPE_THREAD_STATS, hist);
+}
+
+static void 
+vty_out_cpu_thread_history(struct vty* vty,
+                          struct cpu_thread_history *a)
+{
+#ifdef HAVE_RUSAGE
+  vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
+         a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
+         a->cpu.total/a->total_calls, a->cpu.max,
+         a->real.total/a->total_calls, a->real.max);
+#else
+  vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
+         a->real.total/1000, a->real.total%1000, a->total_calls,
+         a->real.total/a->total_calls, a->real.max);
+#endif
+  vty_out(vty, " %c%c%c%c%c%c %s%s",
+         a->types & (1 << THREAD_READ) ? 'R':' ',
+         a->types & (1 << THREAD_WRITE) ? 'W':' ',
+         a->types & (1 << THREAD_TIMER) ? 'T':' ',
+         a->types & (1 << THREAD_EVENT) ? 'E':' ',
+         a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
+         a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
+         a->funcname, VTY_NEWLINE);
+}
+
+static void
+cpu_record_hash_print(struct hash_backet *bucket, 
+                     void *args[])
+{
+  struct cpu_thread_history *totals = args[0];
+  struct vty *vty = args[1];
+  thread_type *filter = args[2];
+  struct cpu_thread_history *a = bucket->data;
+  
+  a = bucket->data;
+  if ( !(a->types & *filter) )
+       return;
+  vty_out_cpu_thread_history(vty,a);
+  totals->total_calls += a->total_calls;
+  totals->real.total += a->real.total;
+  if (totals->real.max < a->real.max)
+    totals->real.max = a->real.max;
+#ifdef HAVE_RUSAGE
+  totals->cpu.total += a->cpu.total;
+  if (totals->cpu.max < a->cpu.max)
+    totals->cpu.max = a->cpu.max;
+#endif
+}
+
+static void
+cpu_record_print(struct vty *vty, thread_type filter)
+{
+  struct cpu_thread_history tmp;
+  void *args[3] = {&tmp, vty, &filter};
+
+  memset(&tmp, 0, sizeof tmp);
+  tmp.funcname = "TOTAL";
+  tmp.types = filter;
+
+#ifdef HAVE_RUSAGE
+  vty_out(vty, "%21s %18s %18s%s",
+         "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
+#endif
+  vty_out(vty, "Runtime(ms)   Invoked Avg uSec Max uSecs");
+#ifdef HAVE_RUSAGE
+  vty_out(vty, " Avg uSec Max uSecs");
+#endif
+  vty_out(vty, "  Type  Thread%s", VTY_NEWLINE);
+  hash_iterate(cpu_record,
+              (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
+              args);
+
+  if (tmp.total_calls > 0)
+    vty_out_cpu_thread_history(vty, &tmp);
+}
+
+DEFUN(show_thread_cpu,
+      show_thread_cpu_cmd,
+      "show thread cpu [FILTER]",
+      SHOW_STR
+      "Thread information\n"
+      "Thread CPU usage\n"
+      "Display filter (rwtexb)\n")
+{
+  int i = 0;
+  thread_type filter = (thread_type) -1U;
+
+  if (argc > 0)
+    {
+      filter = 0;
+      while (argv[0][i] != '\0')
+       {
+         switch ( argv[0][i] )
+           {
+           case 'r':
+           case 'R':
+             filter |= (1 << THREAD_READ);
+             break;
+           case 'w':
+           case 'W':
+             filter |= (1 << THREAD_WRITE);
+             break;
+           case 't':
+           case 'T':
+             filter |= (1 << THREAD_TIMER);
+             break;
+           case 'e':
+           case 'E':
+             filter |= (1 << THREAD_EVENT);
+             break;
+           case 'x':
+           case 'X':
+             filter |= (1 << THREAD_EXECUTE);
+             break;
+           case 'b':
+           case 'B':
+             filter |= (1 << THREAD_BACKGROUND);
+             break;
+           default:
+             break;
+           }
+         ++i;
+       }
+      if (filter == 0)
+       {
+         vty_out(vty, "Invalid filter \"%s\" specified,"
+                  " must contain at least one of 'RWTEXB'%s",
+                 argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  cpu_record_print(vty, filter);
+  return CMD_SUCCESS;
+}
+
+static void
+cpu_record_hash_clear (struct hash_backet *bucket, 
+                     void *args)
+{
+  thread_type *filter = args;
+  struct cpu_thread_history *a = bucket->data;
+  
+  a = bucket->data;
+  if ( !(a->types & *filter) )
+       return;
+  
+  hash_release (cpu_record, bucket->data);
+}
+
+static void
+cpu_record_clear (thread_type filter)
+{
+  thread_type *tmp = &filter;
+  hash_iterate (cpu_record,
+               (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
+               tmp);
+}
+
+DEFUN(clear_thread_cpu,
+      clear_thread_cpu_cmd,
+      "clear thread cpu [FILTER]",
+      "Clear stored data\n"
+      "Thread information\n"
+      "Thread CPU usage\n"
+      "Display filter (rwtexb)\n")
+{
+  int i = 0;
+  thread_type filter = (thread_type) -1U;
+
+  if (argc > 0)
+    {
+      filter = 0;
+      while (argv[0][i] != '\0')
+       {
+         switch ( argv[0][i] )
+           {
+           case 'r':
+           case 'R':
+             filter |= (1 << THREAD_READ);
+             break;
+           case 'w':
+           case 'W':
+             filter |= (1 << THREAD_WRITE);
+             break;
+           case 't':
+           case 'T':
+             filter |= (1 << THREAD_TIMER);
+             break;
+           case 'e':
+           case 'E':
+             filter |= (1 << THREAD_EVENT);
+             break;
+           case 'x':
+           case 'X':
+             filter |= (1 << THREAD_EXECUTE);
+             break;
+           case 'b':
+           case 'B':
+             filter |= (1 << THREAD_BACKGROUND);
+             break;
+           default:
+             break;
+           }
+         ++i;
+       }
+      if (filter == 0)
+       {
+         vty_out(vty, "Invalid filter \"%s\" specified,"
+                  " must contain at least one of 'RWTEXB'%s",
+                 argv[0], VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+
+  cpu_record_clear (filter);
+  return CMD_SUCCESS;
+}
+
+static int
+thread_timer_cmp(void *a, void *b)
+{
+  struct thread *thread_a = a;
+  struct thread *thread_b = b;
+
+  long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands);
+
+  if (cmp < 0)
+    return -1;
+  if (cmp > 0)
+    return 1;
+  return 0;
+}
+
+static void
+thread_timer_update(void *node, int actual_position)
+{
+  struct thread *thread = node;
+
+  thread->index = actual_position;
+}
+
+/* Allocate new thread master.  */
+struct thread_master *
+thread_master_create ()
+{
+  struct thread_master *rv;
+  struct rlimit limit;
+
+  getrlimit(RLIMIT_NOFILE, &limit);
+
+  if (cpu_record == NULL) 
+    cpu_record 
+      = hash_create ((unsigned int (*) (void *))cpu_record_hash_key,
+                    (int (*) (const void *, const void *))cpu_record_hash_cmp);
+
+  rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master));
+  if (rv == NULL)
+    {
+      return NULL;
+    }
+
+  rv->fd_limit = (int)limit.rlim_cur;
+  rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit);
+  if (rv->read == NULL)
+    {
+      XFREE (MTYPE_THREAD_MASTER, rv);
+      return NULL;
+    }
+
+  rv->write = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit);
+  if (rv->write == NULL)
+    {
+      XFREE (MTYPE_THREAD, rv->read);
+      XFREE (MTYPE_THREAD_MASTER, rv);
+      return NULL;
+    }
+
+  /* Initialize the timer queues */
+  rv->timer = pqueue_create();
+  rv->background = pqueue_create();
+  rv->timer->cmp = rv->background->cmp = thread_timer_cmp;
+  rv->timer->update = rv->background->update = thread_timer_update;
+
+  return rv;
+}
+
+/* Add a new thread to the list.  */
+static void
+thread_list_add (struct thread_list *list, struct thread *thread)
+{
+  thread->next = NULL;
+  thread->prev = list->tail;
+  if (list->tail)
+    list->tail->next = thread;
+  else
+    list->head = thread;
+  list->tail = thread;
+  list->count++;
+}
+
+/* Delete a thread from the list. */
+static struct thread *
+thread_list_delete (struct thread_list *list, struct thread *thread)
+{
+  if (thread->next)
+    thread->next->prev = thread->prev;
+  else
+    list->tail = thread->prev;
+  if (thread->prev)
+    thread->prev->next = thread->next;
+  else
+    list->head = thread->next;
+  thread->next = thread->prev = NULL;
+  list->count--;
+  return thread;
+}
+
+static void
+thread_delete_fd (struct thread **thread_array, struct thread *thread)
+{
+  thread_array[thread->u.fd] = NULL;
+}
+
+static void
+thread_add_fd (struct thread **thread_array, struct thread *thread)
+{
+  thread_array[thread->u.fd] = thread;
+}
+
+/* Move thread to unuse list. */
+static void
+thread_add_unuse (struct thread_master *m, struct thread *thread)
+{
+  assert (m != NULL && thread != NULL);
+  assert (thread->next == NULL);
+  assert (thread->prev == NULL);
+  assert (thread->type == THREAD_UNUSED);
+  thread_list_add (&m->unuse, thread);
+}
+
+/* Free all unused thread. */
+static void
+thread_list_free (struct thread_master *m, struct thread_list *list)
+{
+  struct thread *t;
+  struct thread *next;
+
+  for (t = list->head; t; t = next)
+    {
+      next = t->next;
+      XFREE (MTYPE_THREAD, t);
+      list->count--;
+      m->alloc--;
+    }
+}
+
+static void
+thread_array_free (struct thread_master *m, struct thread **thread_array)
+{
+  struct thread *t;
+  int index;
+
+  for (index = 0; index < m->fd_limit; ++index)
+    {
+      t = thread_array[index];
+      if (t)
+        {
+          thread_array[index] = NULL;
+          XFREE (MTYPE_THREAD, t);
+          m->alloc--;
+        }
+    }
+  XFREE (MTYPE_THREAD, thread_array);
+}
+
+static void
+thread_queue_free (struct thread_master *m, struct pqueue *queue)
+{
+  int i;
+
+  for (i = 0; i < queue->size; i++)
+    XFREE(MTYPE_THREAD, queue->array[i]);
+
+  m->alloc -= queue->size;
+  pqueue_delete(queue);
+}
+
+/* Stop thread scheduler. */
+void
+thread_master_free (struct thread_master *m)
+{
+  thread_array_free (m, m->read);
+  thread_array_free (m, m->write);
+  thread_queue_free (m, m->timer);
+  thread_list_free (m, &m->event);
+  thread_list_free (m, &m->ready);
+  thread_list_free (m, &m->unuse);
+  thread_queue_free (m, m->background);
+  
+  XFREE (MTYPE_THREAD_MASTER, m);
+
+  if (cpu_record)
+    {
+      hash_clean (cpu_record, cpu_record_hash_free);
+      hash_free (cpu_record);
+      cpu_record = NULL;
+    }
+}
+
+/* Thread list is empty or not.  */
+static int
+thread_empty (struct thread_list *list)
+{
+  return  list->head ? 0 : 1;
+}
+
+/* Delete top of the list and return it. */
+static struct thread *
+thread_trim_head (struct thread_list *list)
+{
+  if (!thread_empty (list))
+    return thread_list_delete (list, list->head);
+  return NULL;
+}
+
+/* Return remain time in second. */
+unsigned long
+thread_timer_remain_second (struct thread *thread)
+{
+  quagga_get_relative (NULL);
+  
+  if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
+    return thread->u.sands.tv_sec - relative_time.tv_sec;
+  else
+    return 0;
+}
+
+struct timeval
+thread_timer_remain(struct thread *thread)
+{
+  quagga_get_relative(NULL);
+
+  return timeval_subtract(thread->u.sands, relative_time);
+}
+
+#define debugargdef  const char *funcname, const char *schedfrom, int fromln
+#define debugargpass funcname, schedfrom, fromln
+
+/* Get new thread.  */
+static struct thread *
+thread_get (struct thread_master *m, u_char type,
+           int (*func) (struct thread *), void *arg, debugargdef)
+{
+  struct thread *thread = thread_trim_head (&m->unuse);
+
+  if (! thread)
+    {
+      thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
+      m->alloc++;
+    }
+  thread->type = type;
+  thread->add_type = type;
+  thread->master = m;
+  thread->func = func;
+  thread->arg = arg;
+  thread->index = -1;
+
+  thread->funcname = funcname;
+  thread->schedfrom = schedfrom;
+  thread->schedfrom_line = fromln;
+
+  return thread;
+}
+
+#define fd_copy_fd_set(X) (X)
+
+static int
+fd_select (int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *t)
+{
+  return(select(size, read, write, except, t));
+}
+
+static int
+fd_is_set (int fd, thread_fd_set *fdset)
+{
+  return FD_ISSET (fd, fdset);
+}
+
+static int
+fd_clear_read_write (int fd, thread_fd_set *fdset)
+{
+  if (!FD_ISSET (fd, fdset))
+    return 0;
+
+  FD_CLR (fd, fdset);
+  return 1;
+}
+
+static struct thread *
+funcname_thread_add_read_write (int dir, struct thread_master *m, 
+                int (*func) (struct thread *), void *arg, int fd,
+                debugargdef)
+{
+  struct thread *thread = NULL;
+  thread_fd_set *fdset = NULL;
+
+  if (dir == THREAD_READ)
+    fdset = &m->readfd;
+  else
+    fdset = &m->writefd;
+
+  if (FD_ISSET (fd, fdset))
+    {
+      zlog (NULL, LOG_WARNING, "There is already %s fd [%d]",
+           (dir = THREAD_READ) ? "read" : "write", fd);
+      return NULL;
+    }
+
+  FD_SET (fd, fdset);
+
+  thread = thread_get (m, dir, func, arg, debugargpass);
+  thread->u.fd = fd;
+  if (dir == THREAD_READ)
+    thread_add_fd (m->read, thread);
+  else
+    thread_add_fd (m->write, thread);
+
+  return thread;
+}
+
+/* Add new read thread. */
+struct thread *
+funcname_thread_add_read (struct thread_master *m, 
+                int (*func) (struct thread *), void *arg, int fd,
+                debugargdef)
+{
+  return funcname_thread_add_read_write (THREAD_READ, m, func,
+                                         arg, fd, debugargpass);
+}
+
+/* Add new write thread. */
+struct thread *
+funcname_thread_add_write (struct thread_master *m,
+                int (*func) (struct thread *), void *arg, int fd,
+                debugargdef)
+{
+  return funcname_thread_add_read_write (THREAD_WRITE, m, func, 
+                                         arg, fd, debugargpass);
+}
+
+static struct thread *
+funcname_thread_add_timer_timeval (struct thread_master *m,
+                                   int (*func) (struct thread *), 
+                                  int type,
+                                  void *arg, 
+                                  struct timeval *time_relative,
+                                 debugargdef)
+{
+  struct thread *thread;
+  struct pqueue *queue;
+  struct timeval alarm_time;
+
+  assert (m != NULL);
+
+  assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
+  assert (time_relative);
+  
+  queue = ((type == THREAD_TIMER) ? m->timer : m->background);
+  thread = thread_get (m, type, func, arg, debugargpass);
+
+  /* Do we need jitter here? */
+  quagga_get_relative (NULL);
+  alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
+  alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
+  thread->u.sands = timeval_adjust(alarm_time);
+
+  pqueue_enqueue(thread, queue);
+  return thread;
+}
+
+
+/* Add timer event thread. */
+struct thread *
+funcname_thread_add_timer (struct thread_master *m,
+                          int (*func) (struct thread *), 
+                          void *arg, long timer,
+                          debugargdef)
+{
+  struct timeval trel;
+
+  assert (m != NULL);
+
+  trel.tv_sec = timer;
+  trel.tv_usec = 0;
+
+  return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, 
+                                            &trel, debugargpass);
+}
+
+/* Add timer event thread with "millisecond" resolution */
+struct thread *
+funcname_thread_add_timer_msec (struct thread_master *m,
+                                int (*func) (struct thread *), 
+                                void *arg, long timer,
+                               debugargdef)
+{
+  struct timeval trel;
+
+  assert (m != NULL);
+
+  trel.tv_sec = timer / 1000;
+  trel.tv_usec = 1000*(timer % 1000);
+
+  return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, 
+                                            arg, &trel, debugargpass);
+}
+
+/* Add timer event thread with "millisecond" resolution */
+struct thread *
+funcname_thread_add_timer_tv (struct thread_master *m,
+                              int (*func) (struct thread *),
+                              void *arg, struct timeval *tv,
+                              debugargdef)
+{
+  return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
+                                            arg, tv, debugargpass);
+}
+
+/* Add a background thread, with an optional millisec delay */
+struct thread *
+funcname_thread_add_background (struct thread_master *m,
+                                int (*func) (struct thread *),
+                                void *arg, long delay,
+                               debugargdef)
+{
+  struct timeval trel;
+  
+  assert (m != NULL);
+  
+  if (delay)
+    {
+      trel.tv_sec = delay / 1000;
+      trel.tv_usec = 1000*(delay % 1000);
+    }
+  else
+    {
+      trel.tv_sec = 0;
+      trel.tv_usec = 0;
+    }
+
+  return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
+                                            arg, &trel, debugargpass);
+}
+
+/* Add simple event thread. */
+struct thread *
+funcname_thread_add_event (struct thread_master *m,
+                 int (*func) (struct thread *), void *arg, int val,
+                 debugargdef)
+{
+  struct thread *thread;
+
+  assert (m != NULL);
+
+  thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass);
+  thread->u.val = val;
+  thread_list_add (&m->event, thread);
+
+  return thread;
+}
+
+/* Cancel thread from scheduler. */
+void
+thread_cancel (struct thread *thread)
+{
+  struct thread_list *list = NULL;
+  struct pqueue *queue = NULL;
+  struct thread **thread_array = NULL;
+  
+  switch (thread->type)
+    {
+    case THREAD_READ:
+      assert (fd_clear_read_write (thread->u.fd, &thread->master->readfd));
+      thread_array = thread->master->read;
+      break;
+    case THREAD_WRITE:
+      assert (fd_clear_read_write (thread->u.fd, &thread->master->writefd));
+      thread_array = thread->master->write;
+      break;
+    case THREAD_TIMER:
+      queue = thread->master->timer;
+      break;
+    case THREAD_EVENT:
+      list = &thread->master->event;
+      break;
+    case THREAD_READY:
+      list = &thread->master->ready;
+      break;
+    case THREAD_BACKGROUND:
+      queue = thread->master->background;
+      break;
+    default:
+      return;
+      break;
+    }
+
+  if (queue)
+    {
+      assert(thread->index >= 0);
+      assert(thread == queue->array[thread->index]);
+      pqueue_remove_at(thread->index, queue);
+    }
+  else if (list)
+    {
+      thread_list_delete (list, thread);
+    }
+  else if (thread_array)
+    {
+      thread_delete_fd (thread_array, thread);
+    }
+  else
+    {
+      assert(!"Thread should be either in queue or list or array!");
+    }
+
+  thread->type = THREAD_UNUSED;
+  thread_add_unuse (thread->master, thread);
+}
+
+/* Delete all events which has argument value arg. */
+unsigned int
+thread_cancel_event (struct thread_master *m, void *arg)
+{
+  unsigned int ret = 0;
+  struct thread *thread;
+
+  thread = m->event.head;
+  while (thread)
+    {
+      struct thread *t;
+
+      t = thread;
+      thread = t->next;
+
+      if (t->arg == arg)
+        {
+          ret++;
+          thread_list_delete (&m->event, t);
+          t->type = THREAD_UNUSED;
+          thread_add_unuse (m, t);
+        }
+    }
+
+  /* thread can be on the ready list too */
+  thread = m->ready.head;
+  while (thread)
+    {
+      struct thread *t;
+
+      t = thread;
+      thread = t->next;
+
+      if (t->arg == arg)
+        {
+          ret++;
+          thread_list_delete (&m->ready, t);
+          t->type = THREAD_UNUSED;
+          thread_add_unuse (m, t);
+        }
+    }
+  return ret;
+}
+
+static struct timeval *
+thread_timer_wait (struct pqueue *queue, struct timeval *timer_val)
+{
+  if (queue->size)
+    {
+      struct thread *next_timer = queue->array[0];
+      *timer_val = timeval_subtract (next_timer->u.sands, relative_time);
+      return timer_val;
+    }
+  return NULL;
+}
+
+static struct thread *
+thread_run (struct thread_master *m, struct thread *thread,
+           struct thread *fetch)
+{
+  *fetch = *thread;
+  thread->type = THREAD_UNUSED;
+  thread_add_unuse (m, thread);
+  return fetch;
+}
+
+static int
+thread_process_fds_helper (struct thread_master *m, struct thread *thread, thread_fd_set *fdset)
+{
+  thread_fd_set *mfdset = NULL;
+  struct thread **thread_array;
+
+  if (!thread)
+    return 0;
+
+  if (thread->type == THREAD_READ)
+    {
+      mfdset = &m->readfd;
+      thread_array = m->read;
+    }
+  else
+    {
+      mfdset = &m->writefd;
+      thread_array = m->write;
+    }
+
+  if (fd_is_set (THREAD_FD (thread), fdset))
+    {
+      fd_clear_read_write (THREAD_FD (thread), mfdset);
+      thread_delete_fd (thread_array, thread);
+      thread_list_add (&m->ready, thread);
+      thread->type = THREAD_READY;
+      return 1;
+    }
+  return 0;
+}
+
+static int
+thread_process_fds (struct thread_master *m, thread_fd_set *rset, thread_fd_set *wset, int num)
+{
+  int ready = 0, index;
+
+  for (index = 0; index < m->fd_limit && ready < num; ++index)
+    {
+      ready += thread_process_fds_helper (m, m->read[index], rset);
+      ready += thread_process_fds_helper (m, m->write[index], wset);
+    }
+  return num - ready;
+}
+
+/* Add all timers that have popped to the ready list. */
+static unsigned int
+thread_timer_process (struct pqueue *queue, struct timeval *timenow)
+{
+  struct thread *thread;
+  unsigned int ready = 0;
+  
+  while (queue->size)
+    {
+      thread = queue->array[0];
+      if (timeval_cmp (*timenow, thread->u.sands) < 0)
+        return ready;
+      pqueue_dequeue(queue);
+      thread->type = THREAD_READY;
+      thread_list_add (&thread->master->ready, thread);
+      ready++;
+    }
+  return ready;
+}
+
+/* process a list en masse, e.g. for event thread lists */
+static unsigned int
+thread_process (struct thread_list *list)
+{
+  struct thread *thread;
+  struct thread *next;
+  unsigned int ready = 0;
+  
+  for (thread = list->head; thread; thread = next)
+    {
+      next = thread->next;
+      thread_list_delete (list, thread);
+      thread->type = THREAD_READY;
+      thread_list_add (&thread->master->ready, thread);
+      ready++;
+    }
+  return ready;
+}
+
+
+/* Fetch next ready thread. */
+struct thread *
+thread_fetch (struct thread_master *m, struct thread *fetch)
+{
+  struct thread *thread;
+  thread_fd_set readfd;
+  thread_fd_set writefd;
+  thread_fd_set exceptfd;
+  struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
+  struct timeval timer_val_bg;
+  struct timeval *timer_wait = &timer_val;
+  struct timeval *timer_wait_bg;
+
+  while (1)
+    {
+      int num = 0;
+
+      /* Signals pre-empt everything */
+      quagga_sigevent_process ();
+       
+      /* Drain the ready queue of already scheduled jobs, before scheduling
+       * more.
+       */
+      if ((thread = thread_trim_head (&m->ready)) != NULL)
+        return thread_run (m, thread, fetch);
+      
+      /* To be fair to all kinds of threads, and avoid starvation, we
+       * need to be careful to consider all thread types for scheduling
+       * in each quanta. I.e. we should not return early from here on.
+       */
+       
+      /* Normal event are the next highest priority.  */
+      thread_process (&m->event);
+      
+      /* Structure copy.  */
+      readfd = fd_copy_fd_set(m->readfd);
+      writefd = fd_copy_fd_set(m->writefd);
+      exceptfd = fd_copy_fd_set(m->exceptfd);
+      
+      /* Calculate select wait timer if nothing else to do */
+      if (m->ready.count == 0)
+        {
+          quagga_get_relative (NULL);
+          timer_wait = thread_timer_wait (m->timer, &timer_val);
+          timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg);
+          
+          if (timer_wait_bg &&
+              (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
+            timer_wait = timer_wait_bg;
+        }
+      
+      num = fd_select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
+      
+      /* Signals should get quick treatment */
+      if (num < 0)
+        {
+          if (errno == EINTR)
+            continue; /* signal received - process it */
+          zlog_warn ("select() error: %s", safe_strerror (errno));
+          return NULL;
+        }
+
+      /* Check foreground timers.  Historically, they have had higher
+         priority than I/O threads, so let's push them onto the ready
+        list in front of the I/O threads. */
+      quagga_get_relative (NULL);
+      thread_timer_process (m->timer, &relative_time);
+      
+      /* Got IO, process it */
+      if (num > 0)
+        thread_process_fds (m, &readfd, &writefd, num);
+
+#if 0
+      /* If any threads were made ready above (I/O or foreground timer),
+         perhaps we should avoid adding background timers to the ready
+        list at this time.  If this is code is uncommented, then background
+        timer threads will not run unless there is nothing else to do. */
+      if ((thread = thread_trim_head (&m->ready)) != NULL)
+        return thread_run (m, thread, fetch);
+#endif
+
+      /* Background timer/events, lowest priority */
+      thread_timer_process (m->background, &relative_time);
+      
+      if ((thread = thread_trim_head (&m->ready)) != NULL)
+        return thread_run (m, thread, fetch);
+    }
+}
+
+unsigned long
+thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
+{
+#ifdef HAVE_RUSAGE
+  /* This is 'user + sys' time.  */
+  *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
+            timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
+#else
+  *cputime = 0;
+#endif /* HAVE_RUSAGE */
+  return timeval_elapsed (now->real, start->real);
+}
+
+/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds. 
+   Note: we are using real (wall clock) time for this calculation.
+   It could be argued that CPU time may make more sense in certain
+   contexts.  The things to consider are whether the thread may have
+   blocked (in which case wall time increases, but CPU time does not),
+   or whether the system is heavily loaded with other processes competing
+   for CPU time.  On balance, wall clock time seems to make sense. 
+   Plus it has the added benefit that gettimeofday should be faster
+   than calling getrusage. */
+int
+thread_should_yield (struct thread *thread)
+{
+  quagga_get_relative (NULL);
+  unsigned long t = timeval_elapsed(relative_time, thread->real);
+  return ((t > THREAD_YIELD_TIME_SLOT) ? t : 0);
+}
+
+void
+thread_getrusage (RUSAGE_T *r)
+{
+  quagga_get_relative (NULL);
+#ifdef HAVE_RUSAGE
+  getrusage(RUSAGE_SELF, &(r->cpu));
+#endif
+  r->real = relative_time;
+
+#ifdef HAVE_CLOCK_MONOTONIC
+  /* quagga_get_relative() only updates recent_time if gettimeofday
+   * based, not when using CLOCK_MONOTONIC. As we export recent_time
+   * and guarantee to update it before threads are run...
+   */
+  quagga_gettimeofday(&recent_time);
+#endif /* HAVE_CLOCK_MONOTONIC */
+}
+
+struct thread *thread_current = NULL;
+
+/* We check thread consumed time. If the system has getrusage, we'll
+   use that to get in-depth stats on the performance of the thread in addition
+   to wall clock time stats from gettimeofday. */
+void
+thread_call (struct thread *thread)
+{
+  unsigned long realtime, cputime;
+  RUSAGE_T before, after;
+
+ /* Cache a pointer to the relevant cpu history thread, if the thread
+  * does not have it yet.
+  *
+  * Callers submitting 'dummy threads' hence must take care that
+  * thread->cpu is NULL
+  */
+  if (!thread->hist)
+    {
+      struct cpu_thread_history tmp;
+      
+      tmp.func = thread->func;
+      tmp.funcname = thread->funcname;
+      
+      thread->hist = hash_get (cpu_record, &tmp, 
+                    (void * (*) (void *))cpu_record_hash_alloc);
+    }
+
+  GETRUSAGE (&before);
+  thread->real = before.real;
+
+  thread_current = thread;
+  (*thread->func) (thread);
+  thread_current = NULL;
+
+  GETRUSAGE (&after);
+
+  realtime = thread_consumed_time (&after, &before, &cputime);
+  thread->hist->real.total += realtime;
+  if (thread->hist->real.max < realtime)
+    thread->hist->real.max = realtime;
+#ifdef HAVE_RUSAGE
+  thread->hist->cpu.total += cputime;
+  if (thread->hist->cpu.max < cputime)
+    thread->hist->cpu.max = cputime;
+#endif
+
+  ++(thread->hist->total_calls);
+  thread->hist->types |= (1 << thread->add_type);
+
+#ifdef CONSUMED_TIME_CHECK
+  if (realtime > CONSUMED_TIME_CHECK)
+    {
+      /*
+       * We have a CPU Hog on our hands.
+       * Whinge about it now, so we're aware this is yet another task
+       * to fix.
+       */
+      zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
+                thread->funcname,
+                (unsigned long) thread->func,
+                realtime/1000, cputime/1000);
+    }
+#endif /* CONSUMED_TIME_CHECK */
+}
+
+/* Execute thread */
+struct thread *
+funcname_thread_execute (struct thread_master *m,
+                int (*func)(struct thread *), 
+                void *arg,
+                int val,
+               debugargdef)
+{
+  struct thread dummy; 
+
+  memset (&dummy, 0, sizeof (struct thread));
+
+  dummy.type = THREAD_EVENT;
+  dummy.add_type = THREAD_EXECUTE;
+  dummy.master = NULL;
+  dummy.func = func;
+  dummy.arg = arg;
+  dummy.u.val = val;
+
+  dummy.funcname = funcname;
+  dummy.schedfrom = schedfrom;
+  dummy.schedfrom_line = fromln;
+
+  thread_call (&dummy);
+
+  return NULL;
+}
diff --git a/lib/thread.h b/lib/thread.h
new file mode 100644 (file)
index 0000000..3f16216
--- /dev/null
@@ -0,0 +1,254 @@
+/* Thread management routine header.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_THREAD_H
+#define _ZEBRA_THREAD_H
+
+#include <zebra.h>
+
+struct rusage_t
+{
+#ifdef HAVE_RUSAGE
+  struct rusage cpu;
+#endif
+  struct timeval real;
+};
+#define RUSAGE_T        struct rusage_t
+
+#define GETRUSAGE(X) thread_getrusage(X)
+
+/* Linked list of thread. */
+struct thread_list
+{
+  struct thread *head;
+  struct thread *tail;
+  int count;
+};
+
+struct pqueue;
+
+/*
+ * Abstract it so we can use different methodologies to
+ * select on data.
+ */
+typedef fd_set thread_fd_set;
+
+/* Master of the theads. */
+struct thread_master
+{
+  struct thread **read;
+  struct thread **write;
+  struct pqueue *timer;
+  struct thread_list event;
+  struct thread_list ready;
+  struct thread_list unuse;
+  struct pqueue *background;
+  int fd_limit;
+  thread_fd_set readfd;
+  thread_fd_set writefd;
+  thread_fd_set exceptfd;
+  unsigned long alloc;
+};
+
+typedef unsigned char thread_type;
+
+/* Thread itself. */
+struct thread
+{
+  thread_type type;            /* thread type */
+  thread_type add_type;                /* thread type */
+  struct thread *next;         /* next pointer of the thread */   
+  struct thread *prev;         /* previous pointer of the thread */
+  struct thread_master *master;        /* pointer to the struct thread_master. */
+  int (*func) (struct thread *); /* event function */
+  void *arg;                   /* event argument */
+  union {
+    int val;                   /* second argument of the event. */
+    int fd;                    /* file descriptor in case of read/write. */
+    struct timeval sands;      /* rest of time sands value. */
+  } u;
+  int index;                   /* used for timers to store position in queue */
+  struct timeval real;
+  struct cpu_thread_history *hist; /* cache pointer to cpu_history */
+  const char *funcname;
+  const char *schedfrom;
+  int schedfrom_line;
+};
+
+struct cpu_thread_history 
+{
+  int (*func)(struct thread *);
+  unsigned int total_calls;
+  struct time_stats
+  {
+    unsigned long total, max;
+  } real;
+#ifdef HAVE_RUSAGE
+  struct time_stats cpu;
+#endif
+  thread_type types;
+  const char *funcname;
+};
+
+/* Clocks supported by Quagga */
+enum quagga_clkid {
+  QUAGGA_CLK_REALTIME = 0,     /* ala gettimeofday() */
+  QUAGGA_CLK_MONOTONIC,                /* monotonic, against an indeterminate base */
+  QUAGGA_CLK_REALTIME_STABILISED, /* like realtime, but non-decrementing */
+};
+
+/* Thread types. */
+#define THREAD_READ           0
+#define THREAD_WRITE          1
+#define THREAD_TIMER          2
+#define THREAD_EVENT          3
+#define THREAD_READY          4
+#define THREAD_BACKGROUND     5
+#define THREAD_UNUSED         6
+#define THREAD_EXECUTE        7
+
+/* Thread yield time.  */
+#define THREAD_YIELD_TIME_SLOT     10 * 1000L /* 10ms */
+
+/* Macros. */
+#define THREAD_ARG(X) ((X)->arg)
+#define THREAD_FD(X)  ((X)->u.fd)
+#define THREAD_VAL(X) ((X)->u.val)
+
+#define THREAD_READ_ON(master,thread,func,arg,sock) \
+  do { \
+    if (! thread) \
+      thread = thread_add_read (master, func, arg, sock); \
+  } while (0)
+
+#define THREAD_WRITE_ON(master,thread,func,arg,sock) \
+  do { \
+    if (! thread) \
+      thread = thread_add_write (master, func, arg, sock); \
+  } while (0)
+
+#define THREAD_TIMER_ON(master,thread,func,arg,time) \
+  do { \
+    if (! thread) \
+      thread = thread_add_timer (master, func, arg, time); \
+  } while (0)
+
+#define THREAD_TIMER_MSEC_ON(master,thread,func,arg,time) \
+  do { \
+    if (! thread) \
+      thread = thread_add_timer_msec (master, func, arg, time); \
+  } while (0)
+
+#define THREAD_OFF(thread) \
+  do { \
+    if (thread) \
+      { \
+        thread_cancel (thread); \
+        thread = NULL; \
+      } \
+  } while (0)
+
+#define THREAD_READ_OFF(thread)  THREAD_OFF(thread)
+#define THREAD_WRITE_OFF(thread)  THREAD_OFF(thread)
+#define THREAD_TIMER_OFF(thread)  THREAD_OFF(thread)
+
+#define debugargdef  const char *funcname, const char *schedfrom, int fromln
+
+#define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_add_timer_tv(m,f,a,v) funcname_thread_add_timer_tv(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f,__FILE__,__LINE__)
+#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__)
+
+/* The 4th arg to thread_add_background is the # of milliseconds to delay. */
+#define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f,__FILE__,__LINE__)
+
+/* Prototypes. */
+extern struct thread_master *thread_master_create (void);
+extern void thread_master_free (struct thread_master *);
+
+extern struct thread *funcname_thread_add_read (struct thread_master *, 
+                                               int (*)(struct thread *),
+                                               void *, int, debugargdef);
+extern struct thread *funcname_thread_add_write (struct thread_master *,
+                                                int (*)(struct thread *),
+                                                void *, int, debugargdef);
+extern struct thread *funcname_thread_add_timer (struct thread_master *,
+                                                int (*)(struct thread *),
+                                                void *, long, debugargdef);
+extern struct thread *funcname_thread_add_timer_msec (struct thread_master *,
+                                                     int (*)(struct thread *),
+                                                     void *, long, debugargdef);
+extern struct thread *funcname_thread_add_timer_tv (struct thread_master *,
+                                                   int (*)(struct thread *),
+                                                   void *, struct timeval *,
+                                                   debugargdef);
+extern struct thread *funcname_thread_add_event (struct thread_master *,
+                                                int (*)(struct thread *),
+                                                void *, int, debugargdef);
+extern struct thread *funcname_thread_add_background (struct thread_master *,
+                                               int (*func)(struct thread *),
+                                              void *arg,
+                                              long milliseconds_to_delay,
+                                              debugargdef);
+extern struct thread *funcname_thread_execute (struct thread_master *,
+                                               int (*)(struct thread *),
+                                               void *, int, debugargdef);
+#undef debugargdef
+
+extern void thread_cancel (struct thread *);
+extern unsigned int thread_cancel_event (struct thread_master *, void *);
+extern struct thread *thread_fetch (struct thread_master *, struct thread *);
+extern void thread_call (struct thread *);
+extern unsigned long thread_timer_remain_second (struct thread *);
+extern struct timeval thread_timer_remain(struct thread*);
+extern int thread_should_yield (struct thread *);
+extern unsigned long timeval_elapsed (struct timeval a, struct timeval b);
+
+/* Internal libzebra exports */
+extern void thread_getrusage (RUSAGE_T *);
+extern struct cmd_element show_thread_cpu_cmd;
+extern struct cmd_element clear_thread_cpu_cmd;
+
+/* replacements for the system gettimeofday(), clock_gettime() and
+ * time() functions, providing support for non-decrementing clock on
+ * all systems, and fully monotonic on /some/ systems.
+ */
+extern int quagga_gettime (enum quagga_clkid, struct timeval *);
+extern time_t quagga_time (time_t *);
+
+/* Returns elapsed real (wall clock) time. */
+extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
+                                         unsigned long *cpu_time_elapsed);
+
+/* Global variable containing a recent result from gettimeofday.  This can
+   be used instead of calling gettimeofday if a recent value is sufficient.
+   This is guaranteed to be refreshed before a thread is called. */
+extern struct timeval recent_time;
+/* Similar to recent_time, but a monotonically increasing time value */
+extern struct timeval recent_relative_time (void);
+
+/* only for use in logging functions! */
+extern struct thread *thread_current;
+
+#endif /* _ZEBRA_THREAD_H */
diff --git a/lib/vector.c b/lib/vector.c
new file mode 100644 (file)
index 0000000..7c14862
--- /dev/null
@@ -0,0 +1,189 @@
+/* Generic vector interface routine
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "memory.h"
+
+/* Initialize vector : allocate memory and return vector. */
+vector
+vector_init (unsigned int size)
+{
+  vector v = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+
+  /* allocate at least one slot */
+  if (size == 0)
+    size = 1;
+
+  v->alloced = size;
+  v->active = 0;
+  v->index = XCALLOC (MTYPE_VECTOR_INDEX, sizeof (void *) * size);
+  return v;
+}
+
+void
+vector_only_wrapper_free (vector v)
+{
+  XFREE (MTYPE_VECTOR, v);
+}
+
+void
+vector_only_index_free (void *index)
+{
+  XFREE (MTYPE_VECTOR_INDEX, index);
+}
+
+void
+vector_free (vector v)
+{
+  XFREE (MTYPE_VECTOR_INDEX, v->index);
+  XFREE (MTYPE_VECTOR, v);
+}
+
+vector
+vector_copy (vector v)
+{
+  unsigned int size;
+  vector new = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+
+  new->active = v->active;
+  new->alloced = v->alloced;
+
+  size = sizeof (void *) * (v->alloced);
+  new->index = XCALLOC (MTYPE_VECTOR_INDEX, size);
+  memcpy (new->index, v->index, size);
+
+  return new;
+}
+
+/* Check assigned index, and if it runs short double index pointer */
+void
+vector_ensure (vector v, unsigned int num)
+{
+  if (v->alloced > num)
+    return;
+
+  v->index = XREALLOC (MTYPE_VECTOR_INDEX, 
+                      v->index, sizeof (void *) * (v->alloced * 2));
+  memset (&v->index[v->alloced], 0, sizeof (void *) * v->alloced);
+  v->alloced *= 2;
+  
+  if (v->alloced <= num)
+    vector_ensure (v, num);
+}
+
+/* This function only returns next empty slot index.  It dose not mean
+   the slot's index memory is assigned, please call vector_ensure()
+   after calling this function. */
+int
+vector_empty_slot (vector v)
+{
+  unsigned int i;
+
+  if (v->active == 0)
+    return 0;
+
+  for (i = 0; i < v->active; i++)
+    if (v->index[i] == 0)
+      return i;
+
+  return i;
+}
+
+/* Set value to the smallest empty slot. */
+int
+vector_set (vector v, void *val)
+{
+  unsigned int i;
+
+  i = vector_empty_slot (v);
+  vector_ensure (v, i);
+
+  v->index[i] = val;
+
+  if (v->active <= i)
+    v->active = i + 1;
+
+  return i;
+}
+
+/* Set value to specified index slot. */
+int
+vector_set_index (vector v, unsigned int i, void *val)
+{
+  vector_ensure (v, i);
+
+  v->index[i] = val;
+
+  if (v->active <= i)
+    v->active = i + 1;
+
+  return i;
+}
+
+/* Look up vector.  */
+void *
+vector_lookup (vector v, unsigned int i)
+{
+  if (i >= v->active)
+    return NULL;
+  return v->index[i];
+}
+
+/* Lookup vector, ensure it. */
+void *
+vector_lookup_ensure (vector v, unsigned int i)
+{
+  vector_ensure (v, i);
+  return v->index[i];
+}
+
+/* Unset value at specified index slot. */
+void
+vector_unset (vector v, unsigned int i)
+{
+  if (i >= v->alloced)
+    return;
+
+  v->index[i] = NULL;
+
+  if (i + 1 == v->active) 
+    {
+      v->active--;
+      while (i && v->index[--i] == NULL && v->active--) 
+       ;                               /* Is this ugly ? */
+    }
+}
+
+/* Count the number of not emplty slot. */
+unsigned int
+vector_count (vector v)
+{
+  unsigned int i;
+  unsigned count = 0;
+
+  for (i = 0; i < v->active; i++) 
+    if (v->index[i] != NULL)
+      count++;
+
+  return count;
+}
diff --git a/lib/vector.h b/lib/vector.h
new file mode 100644 (file)
index 0000000..6b27fd9
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Generic vector interface header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_VECTOR_H
+#define _ZEBRA_VECTOR_H
+
+/* struct for vector */
+struct _vector 
+{
+  unsigned int active;         /* number of active slots */
+  unsigned int alloced;                /* number of allocated slot */
+  void **index;                        /* index to data */
+};
+typedef struct _vector *vector;
+
+#define VECTOR_MIN_SIZE 1
+
+/* (Sometimes) usefull macros.  This macro convert index expression to
+ array expression. */
+/* Reference slot at given index, caller must ensure slot is active */
+#define vector_slot(V,I)  ((V)->index[(I)])
+/* Number of active slots. 
+ * Note that this differs from vector_count() as it the count returned
+ * will include any empty slots
+ */
+#define vector_active(V) ((V)->active)
+
+/* Prototypes. */
+extern vector vector_init (unsigned int size);
+extern void vector_ensure (vector v, unsigned int num);
+extern int vector_empty_slot (vector v);
+extern int vector_set (vector v, void *val);
+extern int vector_set_index (vector v, unsigned int i, void *val);
+extern void vector_unset (vector v, unsigned int i);
+extern unsigned int vector_count (vector v);
+extern void vector_only_wrapper_free (vector v);
+extern void vector_only_index_free (void *index);
+extern void vector_free (vector v);
+extern vector vector_copy (vector v);
+
+extern void *vector_lookup (vector, unsigned int);
+extern void *vector_lookup_ensure (vector, unsigned int);
+
+#endif /* _ZEBRA_VECTOR_H */
diff --git a/lib/version.h.in b/lib/version.h.in
new file mode 100644 (file)
index 0000000..aef1d09
--- /dev/null
@@ -0,0 +1,56 @@
+/* @configure_input@
+ *
+ * Quagga version
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro
+ * 
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_VERSION_H
+#define _ZEBRA_VERSION_H
+
+#ifdef GIT_VERSION
+#include "gitversion.h"
+#endif
+
+#ifndef GIT_SUFFIX
+#define GIT_SUFFIX ""
+#endif
+#ifndef GIT_INFO
+#define GIT_INFO ""
+#endif
+
+#define QUAGGA_PROGNAME   "@PACKAGE_NAME@"
+
+#define QUAGGA_VERSION     "@PACKAGE_VERSION@" GIT_SUFFIX
+
+#define ZEBRA_BUG_ADDRESS "@PACKAGE_BUGREPORT@"
+
+#define QUAGGA_URL "http://www.quagga.net"
+
+#define QUAGGA_COPYRIGHT "Copyright 1996-2005 Kunihiro Ishiguro, et al."
+
+#define QUAGGA_CONFIG_ARGS "@CONFIG_ARGS@"
+
+pid_t pid_output (const char *);
+
+#ifndef HAVE_DAEMON
+int daemon(int, int);
+#endif
+
+#endif /* _ZEBRA_VERSION_H */
diff --git a/lib/vrf.c b/lib/vrf.c
new file mode 100644 (file)
index 0000000..29be02a
--- /dev/null
+++ b/lib/vrf.c
@@ -0,0 +1,726 @@
+/*
+ * VRF functions.
+ * Copyright (C) 2014 6WIND S.A.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_NETNS
+#undef  _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <sched.h>
+#endif
+
+#include "if.h"
+#include "vrf.h"
+#include "prefix.h"
+#include "table.h"
+#include "log.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+
+
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
+#endif
+
+#ifndef HAVE_SETNS
+static inline int setns(int fd, int nstype)
+{
+#ifdef __NR_setns
+  return syscall(__NR_setns, fd, nstype);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+#endif /* HAVE_SETNS */
+
+#define VRF_RUN_DIR         "/var/run/netns"
+
+#ifdef HAVE_NETNS
+
+#define VRF_DEFAULT_NAME    "/proc/self/ns/net"
+static int have_netns_enabled = -1;
+
+#else /* !HAVE_NETNS */
+
+#define VRF_DEFAULT_NAME    "Default-IP-Routing-Table"
+
+#endif /* HAVE_NETNS */
+
+static int have_netns(void)
+{
+#ifdef HAVE_NETNS
+  if (have_netns_enabled < 0)
+    {
+        int fd = open (VRF_DEFAULT_NAME, O_RDONLY);
+
+        if (fd < 0)
+          have_netns_enabled = 0;
+        else
+          {
+            have_netns_enabled = 1;
+            close(fd);
+          }
+    }
+  return have_netns_enabled;
+#else
+  return 0;
+#endif
+}
+
+struct vrf
+{
+  /* Identifier, same as the vector index */
+  vrf_id_t vrf_id;
+  /* Name */
+  char *name;
+  /* File descriptor */
+  int fd;
+
+  /* Master list of interfaces belonging to this VRF */
+  struct list *iflist;
+
+  /* User data */
+  void *info;
+};
+
+/* Holding VRF hooks  */
+struct vrf_master
+{
+  int (*vrf_new_hook) (vrf_id_t, void **);
+  int (*vrf_delete_hook) (vrf_id_t, void **);
+  int (*vrf_enable_hook) (vrf_id_t, void **);
+  int (*vrf_disable_hook) (vrf_id_t, void **);
+} vrf_master = {0,};
+
+/* VRF table */
+struct route_table *vrf_table = NULL;
+
+static int vrf_is_enabled (struct vrf *vrf);
+static int vrf_enable (struct vrf *vrf);
+static void vrf_disable (struct vrf *vrf);
+
+
+/* Build the table key */
+static void
+vrf_build_key (vrf_id_t vrf_id, struct prefix *p)
+{
+  p->family = AF_INET;
+  p->prefixlen = IPV4_MAX_BITLEN;
+  p->u.prefix4.s_addr = vrf_id;
+}
+
+/* Get a VRF. If not found, create one. */
+static struct vrf *
+vrf_get (vrf_id_t vrf_id)
+{
+  struct prefix p;
+  struct route_node *rn;
+  struct vrf *vrf;
+
+  vrf_build_key (vrf_id, &p);
+  rn = route_node_get (vrf_table, &p);
+  if (rn->info)
+    {
+      vrf = (struct vrf *)rn->info;
+      route_unlock_node (rn); /* get */
+      return vrf;
+    }
+
+  vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf));
+  vrf->vrf_id = vrf_id;
+  vrf->fd = -1;
+  rn->info = vrf;
+
+  /* Initialize interfaces. */
+  if_init (vrf_id, &vrf->iflist);
+
+  zlog_info ("VRF %u is created.", vrf_id);
+
+  if (vrf_master.vrf_new_hook)
+    (*vrf_master.vrf_new_hook) (vrf_id, &vrf->info);
+
+  return vrf;
+}
+
+/* Delete a VRF. This is called in vrf_terminate(). */
+static void
+vrf_delete (struct vrf *vrf)
+{
+  zlog_info ("VRF %u is to be deleted.", vrf->vrf_id);
+
+  vrf_disable (vrf);
+
+  if (vrf_master.vrf_delete_hook)
+    (*vrf_master.vrf_delete_hook) (vrf->vrf_id, &vrf->info);
+
+  if_terminate (vrf->vrf_id, &vrf->iflist);
+
+  if (vrf->name)
+    XFREE (MTYPE_VRF_NAME, vrf->name);
+
+  XFREE (MTYPE_VRF, vrf);
+}
+
+/* Look up a VRF by identifier. */
+static struct vrf *
+vrf_lookup (vrf_id_t vrf_id)
+{
+  struct prefix p;
+  struct route_node *rn;
+  struct vrf *vrf = NULL;
+
+  vrf_build_key (vrf_id, &p);
+  rn = route_node_lookup (vrf_table, &p);
+  if (rn)
+    {
+      vrf = (struct vrf *)rn->info;
+      route_unlock_node (rn); /* lookup */
+    }
+  return vrf;
+}
+
+/*
+ * Check whether the VRF is enabled - that is, whether the VRF
+ * is ready to allocate resources. Currently there's only one
+ * type of resource: socket.
+ */
+static int
+vrf_is_enabled (struct vrf *vrf)
+{
+  if (have_netns())
+      return vrf && vrf->fd >= 0;
+  else
+      return vrf && vrf->fd == -2 && vrf->vrf_id == VRF_DEFAULT;
+}
+
+/*
+ * Enable a VRF - that is, let the VRF be ready to use.
+ * The VRF_ENABLE_HOOK callback will be called to inform
+ * that they can allocate resources in this VRF.
+ *
+ * RETURN: 1 - enabled successfully; otherwise, 0.
+ */
+static int
+vrf_enable (struct vrf *vrf)
+{
+
+  if (!vrf_is_enabled (vrf))
+    {
+      if (have_netns()) {
+        vrf->fd = open (vrf->name, O_RDONLY);
+      } else {
+        vrf->fd = -2; /* Remember that vrf_enable_hook has been called */
+        errno = -ENOTSUP;
+      }
+
+      if (!vrf_is_enabled (vrf))
+        {
+          zlog_err ("Can not enable VRF %u: %s!",
+                    vrf->vrf_id, safe_strerror (errno));
+          return 0;
+        }
+
+      if (have_netns())
+        zlog_info ("VRF %u is associated with NETNS %s.",
+                   vrf->vrf_id, vrf->name);
+
+      zlog_info ("VRF %u is enabled.", vrf->vrf_id);
+      if (vrf_master.vrf_enable_hook)
+        (*vrf_master.vrf_enable_hook) (vrf->vrf_id, &vrf->info);
+    }
+
+  return 1;
+}
+
+/*
+ * Disable a VRF - that is, let the VRF be unusable.
+ * The VRF_DELETE_HOOK callback will be called to inform
+ * that they must release the resources in the VRF.
+ */
+static void
+vrf_disable (struct vrf *vrf)
+{
+  if (vrf_is_enabled (vrf))
+    {
+      zlog_info ("VRF %u is to be disabled.", vrf->vrf_id);
+
+      if (vrf_master.vrf_disable_hook)
+        (*vrf_master.vrf_disable_hook) (vrf->vrf_id, &vrf->info);
+
+      if (have_netns())
+        close (vrf->fd);
+
+      vrf->fd = -1;
+    }
+}
+
+
+/* Add a VRF hook. Please add hooks before calling vrf_init(). */
+void
+vrf_add_hook (int type, int (*func)(vrf_id_t, void **))
+{
+  switch (type) {
+  case VRF_NEW_HOOK:
+    vrf_master.vrf_new_hook = func;
+    break;
+  case VRF_DELETE_HOOK:
+    vrf_master.vrf_delete_hook = func;
+    break;
+  case VRF_ENABLE_HOOK:
+    vrf_master.vrf_enable_hook = func;
+    break;
+  case VRF_DISABLE_HOOK:
+    vrf_master.vrf_disable_hook = func;
+    break;
+  default:
+    break;
+  }
+}
+
+/* Return the iterator of the first VRF. */
+vrf_iter_t
+vrf_first (void)
+{
+  struct route_node *rn;
+
+  for (rn = route_top (vrf_table); rn; rn = route_next (rn))
+    if (rn->info)
+      {
+        route_unlock_node (rn); /* top/next */
+        return (vrf_iter_t)rn;
+      }
+  return VRF_ITER_INVALID;
+}
+
+/* Return the next VRF iterator to the given iterator. */
+vrf_iter_t
+vrf_next (vrf_iter_t iter)
+{
+  struct route_node *rn = NULL;
+
+  /* Lock it first because route_next() will unlock it. */
+  if (iter != VRF_ITER_INVALID)
+    rn = route_next (route_lock_node ((struct route_node *)iter));
+
+  for (; rn; rn = route_next (rn))
+    if (rn->info)
+      {
+        route_unlock_node (rn); /* next */
+        return (vrf_iter_t)rn;
+      }
+  return VRF_ITER_INVALID;
+}
+
+/* Return the VRF iterator of the given VRF ID. If it does not exist,
+ * the iterator of the next existing VRF is returned. */
+vrf_iter_t
+vrf_iterator (vrf_id_t vrf_id)
+{
+  struct prefix p;
+  struct route_node *rn;
+
+  vrf_build_key (vrf_id, &p);
+  rn = route_node_get (vrf_table, &p);
+  if (rn->info)
+    {
+      /* OK, the VRF exists. */
+      route_unlock_node (rn); /* get */
+      return (vrf_iter_t)rn;
+    }
+
+  /* Find the next VRF. */
+  for (rn = route_next (rn); rn; rn = route_next (rn))
+    if (rn->info)
+      {
+        route_unlock_node (rn); /* next */
+        return (vrf_iter_t)rn;
+      }
+
+  return VRF_ITER_INVALID;
+}
+
+/* Obtain the VRF ID from the given VRF iterator. */
+vrf_id_t
+vrf_iter2id (vrf_iter_t iter)
+{
+  struct route_node *rn = (struct route_node *) iter;
+  return (rn && rn->info) ? ((struct vrf *)rn->info)->vrf_id : VRF_DEFAULT;
+}
+
+/* Obtain the data pointer from the given VRF iterator. */
+void *
+vrf_iter2info (vrf_iter_t iter)
+{
+  struct route_node *rn = (struct route_node *) iter;
+  return (rn && rn->info) ? ((struct vrf *)rn->info)->info : NULL;
+}
+
+/* Obtain the interface list from the given VRF iterator. */
+struct list *
+vrf_iter2iflist (vrf_iter_t iter)
+{
+  struct route_node *rn = (struct route_node *) iter;
+  return (rn && rn->info) ? ((struct vrf *)rn->info)->iflist : NULL;
+}
+
+/* Get the data pointer of the specified VRF. If not found, create one. */
+void *
+vrf_info_get (vrf_id_t vrf_id)
+{
+  struct vrf *vrf = vrf_get (vrf_id);
+  return vrf->info;
+}
+
+/* Look up the data pointer of the specified VRF. */
+void *
+vrf_info_lookup (vrf_id_t vrf_id)
+{
+  struct vrf *vrf = vrf_lookup (vrf_id);
+  return vrf ? vrf->info : NULL;
+}
+
+/* Look up the interface list in a VRF. */
+struct list *
+vrf_iflist (vrf_id_t vrf_id)
+{
+   struct vrf * vrf = vrf_lookup (vrf_id);
+   return vrf ? vrf->iflist : NULL;
+}
+
+/* Get the interface list of the specified VRF. Create one if not find. */
+struct list *
+vrf_iflist_get (vrf_id_t vrf_id)
+{
+   struct vrf * vrf = vrf_get (vrf_id);
+   return vrf->iflist;
+}
+
+/*
+ * VRF bit-map
+ */
+
+#define VRF_BITMAP_NUM_OF_GROUPS            8
+#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP \
+    (UINT16_MAX / VRF_BITMAP_NUM_OF_GROUPS)
+#define VRF_BITMAP_NUM_OF_BYTES_IN_GROUP \
+    (VRF_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */
+
+#define VRF_BITMAP_GROUP(_id) \
+    ((_id) / VRF_BITMAP_NUM_OF_BITS_IN_GROUP)
+#define VRF_BITMAP_BIT_OFFSET(_id) \
+    ((_id) % VRF_BITMAP_NUM_OF_BITS_IN_GROUP)
+
+#define VRF_BITMAP_INDEX_IN_GROUP(_bit_offset) \
+    ((_bit_offset) / CHAR_BIT)
+#define VRF_BITMAP_FLAG(_bit_offset) \
+    (((u_char)1) << ((_bit_offset) % CHAR_BIT))
+
+struct vrf_bitmap
+{
+  u_char *groups[VRF_BITMAP_NUM_OF_GROUPS];
+};
+
+vrf_bitmap_t
+vrf_bitmap_init (void)
+{
+  return (vrf_bitmap_t) XCALLOC (MTYPE_VRF_BITMAP, sizeof (struct vrf_bitmap));
+}
+
+void
+vrf_bitmap_free (vrf_bitmap_t bmap)
+{
+  struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap;
+  int i;
+
+  if (bmap == VRF_BITMAP_NULL)
+    return;
+
+  for (i = 0; i < VRF_BITMAP_NUM_OF_GROUPS; i++)
+    if (bm->groups[i])
+      XFREE (MTYPE_VRF_BITMAP, bm->groups[i]);
+
+  XFREE (MTYPE_VRF_BITMAP, bm);
+}
+
+void
+vrf_bitmap_set (vrf_bitmap_t bmap, vrf_id_t vrf_id)
+{
+  struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap;
+  u_char group = VRF_BITMAP_GROUP (vrf_id);
+  u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id);
+
+  if (bmap == VRF_BITMAP_NULL)
+    return;
+
+  if (bm->groups[group] == NULL)
+    bm->groups[group] = XCALLOC (MTYPE_VRF_BITMAP,
+                                 VRF_BITMAP_NUM_OF_BYTES_IN_GROUP);
+
+  SET_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)],
+            VRF_BITMAP_FLAG (offset));
+}
+
+void
+vrf_bitmap_unset (vrf_bitmap_t bmap, vrf_id_t vrf_id)
+{
+  struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap;
+  u_char group = VRF_BITMAP_GROUP (vrf_id);
+  u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id);
+
+  if (bmap == VRF_BITMAP_NULL || bm->groups[group] == NULL)
+    return;
+
+  UNSET_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)],
+              VRF_BITMAP_FLAG (offset));
+}
+
+int
+vrf_bitmap_check (vrf_bitmap_t bmap, vrf_id_t vrf_id)
+{
+  struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap;
+  u_char group = VRF_BITMAP_GROUP (vrf_id);
+  u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id);
+
+  if (bmap == VRF_BITMAP_NULL || bm->groups[group] == NULL)
+    return 0;
+
+  return CHECK_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)],
+                     VRF_BITMAP_FLAG (offset)) ? 1 : 0;
+}
+
+/*
+ * VRF realization with NETNS
+ */
+
+static char *
+vrf_netns_pathname (struct vty *vty, const char *name)
+{
+  static char pathname[PATH_MAX];
+  char *result;
+
+  if (name[0] == '/') /* absolute pathname */
+    result = realpath (name, pathname);
+  else /* relevant pathname */
+    {
+      char tmp_name[PATH_MAX];
+      snprintf (tmp_name, PATH_MAX, "%s/%s", VRF_RUN_DIR, name);
+      result = realpath (tmp_name, pathname);
+    }
+
+  if (! result)
+    {
+      vty_out (vty, "Invalid pathname: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return NULL;
+    }
+  return pathname;
+}
+
+DEFUN (vrf_netns,
+       vrf_netns_cmd,
+       "vrf <1-65535> netns NAME",
+       "Enable a VRF\n"
+       "Specify the VRF identifier\n"
+       "Associate with a NETNS\n"
+       "The file name in " VRF_RUN_DIR ", or a full pathname\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  struct vrf *vrf = NULL;
+  char *pathname = vrf_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+  vrf = vrf_get (vrf_id);
+
+  if (vrf->name && strcmp (vrf->name, pathname) != 0)
+    {
+      vty_out (vty, "VRF %u is already configured with NETNS %s%s",
+               vrf->vrf_id, vrf->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!vrf->name)
+    vrf->name = XSTRDUP (MTYPE_VRF_NAME, pathname);
+
+  if (!vrf_enable (vrf))
+    {
+      vty_out (vty, "Can not associate VRF %u with NETNS %s%s",
+               vrf->vrf_id, vrf->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_vrf_netns,
+       no_vrf_netns_cmd,
+       "no vrf <1-65535> netns NAME",
+       NO_STR
+       "Enable a VRF\n"
+       "Specify the VRF identifier\n"
+       "Associate with a NETNS\n"
+       "The file name in " VRF_RUN_DIR ", or a full pathname\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  struct vrf *vrf = NULL;
+  char *pathname = vrf_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+  vrf = vrf_lookup (vrf_id);
+
+  if (!vrf)
+    {
+      vty_out (vty, "VRF %u is not found%s", vrf_id, VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  if (vrf->name && strcmp (vrf->name, pathname) != 0)
+    {
+      vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vrf_disable (vrf);
+
+  if (vrf->name)
+    {
+      XFREE (MTYPE_VRF_NAME, vrf->name);
+      vrf->name = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* VRF node. */
+static struct cmd_node vrf_node =
+{
+  VRF_NODE,
+  "",       /* VRF node has no interface. */
+  1
+};
+
+/* VRF configuration write function. */
+static int
+vrf_config_write (struct vty *vty)
+{
+  struct route_node *rn;
+  struct vrf *vrf;
+  int write = 0;
+
+  for (rn = route_top (vrf_table); rn; rn = route_next (rn))
+    if ((vrf = rn->info) != NULL &&
+        vrf->vrf_id != VRF_DEFAULT && vrf->name)
+      {
+        vty_out (vty, "vrf %u netns %s%s", vrf->vrf_id, vrf->name, VTY_NEWLINE);
+        write++;
+      }
+
+  return write;
+}
+
+/* Initialize VRF module. */
+void
+vrf_init (void)
+{
+  struct vrf *default_vrf;
+
+  /* Allocate VRF table.  */
+  vrf_table = route_table_init ();
+
+  /* The default VRF always exists. */
+  default_vrf = vrf_get (VRF_DEFAULT);
+  if (!default_vrf)
+    {
+      zlog_err ("vrf_init: failed to create the default VRF!");
+      exit (1);
+    }
+
+  /* Set the default VRF name. */
+  default_vrf->name = XSTRDUP (MTYPE_VRF_NAME, VRF_DEFAULT_NAME);
+
+  /* Enable the default VRF. */
+  if (!vrf_enable (default_vrf))
+    {
+      zlog_err ("vrf_init: failed to enable the default VRF!");
+      exit (1);
+    }
+
+  if (have_netns())
+    {
+      /* Install VRF commands. */
+      install_node (&vrf_node, vrf_config_write);
+      install_element (CONFIG_NODE, &vrf_netns_cmd);
+      install_element (CONFIG_NODE, &no_vrf_netns_cmd);
+    }
+}
+
+/* Terminate VRF module. */
+void
+vrf_terminate (void)
+{
+  struct route_node *rn;
+  struct vrf *vrf;
+
+  for (rn = route_top (vrf_table); rn; rn = route_next (rn))
+    if ((vrf = rn->info) != NULL)
+      vrf_delete (vrf);
+
+  route_table_finish (vrf_table);
+  vrf_table = NULL;
+}
+
+/* Create a socket for the VRF. */
+int
+vrf_socket (int domain, int type, int protocol, vrf_id_t vrf_id)
+{
+  struct vrf *vrf = vrf_lookup (vrf_id);
+  int ret = -1;
+
+  if (!vrf_is_enabled (vrf))
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  if (have_netns())
+    {
+      ret = (vrf_id != VRF_DEFAULT) ? setns (vrf->fd, CLONE_NEWNET) : 0;
+      if (ret >= 0)
+        {
+          ret = socket (domain, type, protocol);
+          if (vrf_id != VRF_DEFAULT)
+            setns (vrf_lookup (VRF_DEFAULT)->fd, CLONE_NEWNET);
+        }
+    }
+  else
+    ret = socket (domain, type, protocol);
+
+  return ret;
+}
diff --git a/lib/vrf.h b/lib/vrf.h
new file mode 100644 (file)
index 0000000..8b29b80
--- /dev/null
+++ b/lib/vrf.h
@@ -0,0 +1,140 @@
+/*
+ * VRF related header.
+ * Copyright (C) 2014 6WIND S.A.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VRF_H
+#define _ZEBRA_VRF_H
+
+#include "linklist.h"
+
+/* The default VRF ID */
+#define VRF_DEFAULT 0
+
+/*
+ * The command strings
+ */
+
+#define VRF_CMD_STR         "vrf <0-65535>"
+#define VRF_CMD_HELP_STR    "Specify the VRF\nThe VRF ID\n"
+
+#define VRF_ALL_CMD_STR         "vrf all"
+#define VRF_ALL_CMD_HELP_STR    "Specify the VRF\nAll VRFs\n"
+
+/*
+ * VRF hooks
+ */
+
+#define VRF_NEW_HOOK        0   /* a new VRF is just created */
+#define VRF_DELETE_HOOK     1   /* a VRF is to be deleted */
+#define VRF_ENABLE_HOOK     2   /* a VRF is ready to use */
+#define VRF_DISABLE_HOOK    3   /* a VRF is to be unusable */
+
+/*
+ * Add a specific hook to VRF module.
+ * @param1: hook type
+ * @param2: the callback function
+ *          - param 1: the VRF ID
+ *          - param 2: the address of the user data pointer (the user data
+ *                     can be stored in or freed from there)
+ */
+extern void vrf_add_hook (int, int (*)(vrf_id_t, void **));
+
+/*
+ * VRF iteration
+ */
+
+typedef void *              vrf_iter_t;
+#define VRF_ITER_INVALID    NULL    /* invalid value of the iterator */
+
+/*
+ * VRF iteration utilities. Example for the usage:
+ *
+ *   vrf_iter_t iter = vrf_first();
+ *   for (; iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+ *
+ * or
+ *
+ *   vrf_iter_t iter = vrf_iterator (<a given VRF ID>);
+ *   for (; iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+ */
+
+/* Return the iterator of the first VRF. */
+extern vrf_iter_t vrf_first (void);
+/* Return the next VRF iterator to the given iterator. */
+extern vrf_iter_t vrf_next (vrf_iter_t);
+/* Return the VRF iterator of the given VRF ID. If it does not exist,
+ * the iterator of the next existing VRF is returned. */
+extern vrf_iter_t vrf_iterator (vrf_id_t);
+
+/*
+ * VRF iterator to properties
+ */
+extern vrf_id_t vrf_iter2id (vrf_iter_t);
+extern void *vrf_iter2info (vrf_iter_t);
+extern struct list *vrf_iter2iflist (vrf_iter_t);
+
+/*
+ * Utilities to obtain the user data
+ */
+
+/* Get the data pointer of the specified VRF. If not found, create one. */
+extern void *vrf_info_get (vrf_id_t);
+/* Look up the data pointer of the specified VRF. */
+extern void *vrf_info_lookup (vrf_id_t);
+
+/*
+ * Utilities to obtain the interface list
+ */
+
+/* Look up the interface list of the specified VRF. */
+extern struct list *vrf_iflist (vrf_id_t);
+/* Get the interface list of the specified VRF. Create one if not find. */
+extern struct list *vrf_iflist_get (vrf_id_t);
+
+/*
+ * VRF bit-map: maintaining flags, one bit per VRF ID
+ */
+
+typedef void *              vrf_bitmap_t;
+#define VRF_BITMAP_NULL     NULL
+
+extern vrf_bitmap_t vrf_bitmap_init (void);
+extern void vrf_bitmap_free (vrf_bitmap_t);
+extern void vrf_bitmap_set (vrf_bitmap_t, vrf_id_t);
+extern void vrf_bitmap_unset (vrf_bitmap_t, vrf_id_t);
+extern int vrf_bitmap_check (vrf_bitmap_t, vrf_id_t);
+
+/*
+ * VRF initializer/destructor
+ */
+/* Please add hooks before calling vrf_init(). */
+extern void vrf_init (void);
+extern void vrf_terminate (void);
+
+/*
+ * VRF utilities
+ */
+
+/* Create a socket serving for the given VRF */
+extern int vrf_socket (int, int, int, vrf_id_t);
+
+#endif /*_ZEBRA_VRF_H*/
+
diff --git a/lib/vty.c b/lib/vty.c
new file mode 100644 (file)
index 0000000..7ca8354
--- /dev/null
+++ b/lib/vty.c
@@ -0,0 +1,3209 @@
+/*
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "buffer.h"
+#include <lib/version.h>
+#include "command.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "str.h"
+#include "log.h"
+#include "prefix.h"
+#include "filter.h"
+#include "vty.h"
+#include "privs.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <termios.h>
+
+#define VTY_BUFSIZ 4096
+
+/* Vty events */
+enum event 
+{
+  VTY_SERV,
+  VTY_READ,
+  VTY_WRITE,
+  VTY_TIMEOUT_RESET,
+#ifdef VTYSH
+  VTYSH_SERV,
+  VTYSH_READ,
+  VTYSH_WRITE
+#endif /* VTYSH */
+};
+
+static void vty_event (enum event, int, struct vty *);
+
+/* Extern host structure from command.c */
+extern struct host host;
+
+/* Vector which store each vty structure. */
+static vector vtyvec;
+
+/* Vty timeout value. */
+static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+
+/* Vty access-class command */
+static char *vty_accesslist_name = NULL;
+
+/* Vty access-calss for IPv6. */
+static char *vty_ipv6_accesslist_name = NULL;
+
+/* VTY server thread. */
+static vector Vvty_serv_thread;
+
+/* Current directory. */
+char *vty_cwd = NULL;
+
+/* Configure lock. */
+static int vty_config;
+
+/* Login password check. */
+static int no_password_check = 0;
+
+/* Restrict unauthenticated logins? */
+static const u_char restricted_mode_default = 0;
+static u_char restricted_mode = 0;
+
+/* Integrated configuration file path */
+char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
+
+static int do_log_commands = 0;
+
+static void
+vty_buf_assert (struct vty *vty)
+{
+  assert (vty->cp <= vty->length);
+  assert (vty->length < vty->max); 
+  assert (vty->buf[vty->length] == '\0');
+}
+
+/* Sanity/safety wrappers around access to vty->buf */
+static void
+vty_buf_put (struct vty *vty, char c)
+{
+  vty_buf_assert (vty);
+  vty->buf[vty->cp] = c;
+  vty->buf[vty->max - 1] = '\0';
+}
+
+/* VTY standard output function. */
+int
+vty_out (struct vty *vty, const char *format, ...)
+{
+  va_list args;
+  int len = 0;
+  int size = 1024;
+  char buf[1024];
+  char *p = NULL;
+
+  if (vty_shell (vty))
+    {
+      va_start (args, format);
+      vprintf (format, args);
+      va_end (args);
+    }
+  else
+    {
+      /* Try to write to initial buffer.  */
+      va_start (args, format);
+      len = vsnprintf (buf, sizeof(buf), format, args);
+      va_end (args);
+
+      /* Initial buffer is not enough.  */
+      if (len < 0 || len >= size)
+       {
+         while (1)
+           {
+             if (len > -1)
+               size = len + 1;
+             else
+               size = size * 2;
+
+             p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
+             if (! p)
+               return -1;
+
+             va_start (args, format);
+             len = vsnprintf (p, size, format, args);
+             va_end (args);
+
+             if (len > -1 && len < size)
+               break;
+           }
+       }
+
+      /* When initial buffer is enough to store all output.  */
+      if (! p)
+       p = buf;
+
+      /* Pointer p must point out buffer. */
+      buffer_put (vty->obuf, (u_char *) p, len);
+
+      /* If p is not different with buf, it is allocated buffer.  */
+      if (p != buf)
+       XFREE (MTYPE_VTY_OUT_BUF, p);
+    }
+
+  return len;
+}
+
+static int
+vty_log_out (struct vty *vty, const char *level, const char *proto_str,
+            const char *format, struct timestamp_control *ctl, va_list va)
+{
+  int ret;
+  int len;
+  char buf[1024];
+
+  if (!ctl->already_rendered)
+    {
+      ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
+      ctl->already_rendered = 1;
+    }
+  if (ctl->len+1 >= sizeof(buf))
+    return -1;
+  memcpy(buf, ctl->buf, len = ctl->len);
+  buf[len++] = ' ';
+  buf[len] = '\0';
+
+  if (level)
+    ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
+  else
+    ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
+  if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
+    return -1;
+
+  if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
+      ((size_t)((len += ret)+2) > sizeof(buf)))
+    return -1;
+
+  buf[len++] = '\r';
+  buf[len++] = '\n';
+
+  if (write(vty->wfd, buf, len) < 0)
+    {
+      if (ERRNO_IO_RETRY(errno))
+       /* Kernel buffer is full, probably too much debugging output, so just
+          drop the data and ignore. */
+       return -1;
+      /* Fatal I/O error. */
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("%s: write failed to vty client fd %d, closing: %s",
+               __func__, vty->fd, safe_strerror(errno));
+      buffer_reset(vty->obuf);
+      /* cannot call vty_close, because a parent routine may still try
+         to access the vty struct */
+      vty->status = VTY_CLOSE;
+      shutdown(vty->fd, SHUT_RDWR);
+      return -1;
+    }
+  return 0;
+}
+
+/* Output current time to the vty. */
+void
+vty_time_print (struct vty *vty, int cr)
+{
+  char buf[QUAGGA_TIMESTAMP_LEN];
+  
+  if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
+    {
+      zlog (NULL, LOG_INFO, "quagga_timestamp error");
+      return;
+    }
+  if (cr)
+    vty_out (vty, "%s\n", buf);
+  else
+    vty_out (vty, "%s ", buf);
+
+  return;
+}
+
+/* Say hello to vty interface. */
+void
+vty_hello (struct vty *vty)
+{
+  if (host.motdfile)
+    {
+      FILE *f;
+      char buf[4096];
+
+      f = fopen (host.motdfile, "r");
+      if (f)
+       {
+         while (fgets (buf, sizeof (buf), f))
+           {
+             char *s;
+             /* work backwards to ignore trailling isspace() */
+             for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
+                  s--);
+             *s = '\0';
+             vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+           }
+         fclose (f);
+       }
+      else
+       vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
+    }
+  else if (host.motd)
+    vty_out (vty, "%s", host.motd);
+}
+
+/* Put out prompt and wait input from user. */
+static void
+vty_prompt (struct vty *vty)
+{
+  struct utsname names;
+  const char*hostname;
+
+  if (vty->type == VTY_TERM)
+    {
+      hostname = host.name;
+      if (!hostname)
+       {
+         uname (&names);
+         hostname = names.nodename;
+       }
+      vty_out (vty, cmd_prompt (vty->node), hostname);
+    }
+}
+
+/* Send WILL TELOPT_ECHO to remote server. */
+static void
+vty_will_echo (struct vty *vty)
+{
+  unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+  vty_out (vty, "%s", cmd);
+}
+
+/* Make suppress Go-Ahead telnet option. */
+static void
+vty_will_suppress_go_ahead (struct vty *vty)
+{
+  unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+  vty_out (vty, "%s", cmd);
+}
+
+/* Make don't use linemode over telnet. */
+static void
+vty_dont_linemode (struct vty *vty)
+{
+  unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+  vty_out (vty, "%s", cmd);
+}
+
+/* Use window size. */
+static void
+vty_do_window_size (struct vty *vty)
+{
+  unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+  vty_out (vty, "%s", cmd);
+}
+
+#if 0 /* Currently not used. */
+/* Make don't use lflow vty interface. */
+static void
+vty_dont_lflow_ahead (struct vty *vty)
+{
+  unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
+  vty_out (vty, "%s", cmd);
+}
+#endif /* 0 */
+
+/* Allocate new vty struct. */
+struct vty *
+vty_new ()
+{
+  struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+
+  new->obuf = buffer_new(0);   /* Use default buffer size. */
+  new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
+  new->max = VTY_BUFSIZ;
+
+  return new;
+}
+
+/* Authentication of vty */
+static void
+vty_auth (struct vty *vty, char *buf)
+{
+  char *passwd = NULL;
+  enum node_type next_node = 0;
+  int fail;
+  char *crypt (const char *, const char *);
+
+  switch (vty->node)
+    {
+    case AUTH_NODE:
+      if (host.encrypt)
+       passwd = host.password_encrypt;
+      else
+       passwd = host.password;
+      if (host.advanced)
+       next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+      else
+       next_node = VIEW_NODE;
+      break;
+    case AUTH_ENABLE_NODE:
+      if (host.encrypt)
+       passwd = host.enable_encrypt;
+      else
+       passwd = host.enable;
+      next_node = ENABLE_NODE;
+      break;
+    }
+
+  if (passwd)
+    {
+      if (host.encrypt)
+       fail = strcmp (crypt(buf, passwd), passwd);
+      else
+       fail = strcmp (buf, passwd);
+    }
+  else
+    fail = 1;
+
+  if (! fail)
+    {
+      vty->fail = 0;
+      vty->node = next_node;   /* Success ! */
+    }
+  else
+    {
+      vty->fail++;
+      if (vty->fail >= 3)
+       {
+         if (vty->node == AUTH_NODE)
+           {
+             vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
+             vty->status = VTY_CLOSE;
+           }
+         else                  
+           {
+             /* AUTH_ENABLE_NODE */
+             vty->fail = 0;
+             vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
+             vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
+           }
+       }
+    }
+}
+
+/* Command execution over the vty interface. */
+static int
+vty_command (struct vty *vty, char *buf)
+{
+  int ret;
+  vector vline;
+  const char *protocolname;
+  char *cp = NULL;
+
+  /*
+   * Log non empty command lines
+   */
+  if (do_log_commands)
+    cp = buf;
+  if (cp != NULL)
+    {
+      /* Skip white spaces. */
+      while (isspace ((int) *cp) && *cp != '\0')
+        cp++;
+    }
+  if (cp != NULL && *cp != '\0')
+    {
+      unsigned i;
+      char     vty_str[VTY_BUFSIZ];
+      char        prompt_str[VTY_BUFSIZ];
+
+      /* format the base vty info */
+      snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
+      if (vty)
+        for (i = 0; i < vector_active (vtyvec); i++)
+          if (vty == vector_slot (vtyvec, i))
+            {
+              snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
+                                                 i, vty->address);
+              break;
+            }
+
+      /* format the prompt */
+      snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str);
+
+      /* now log the command */
+      zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf);
+    }
+  /* Split readline string up into the vector */
+  vline = cmd_make_strvec (buf);
+
+  if (vline == NULL)
+    return CMD_SUCCESS;
+
+#ifdef CONSUMED_TIME_CHECK
+  {
+    RUSAGE_T before;
+    RUSAGE_T after;
+    unsigned long realtime, cputime;
+
+    GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
+
+  ret = cmd_execute_command (vline, vty, NULL, 0);
+
+  /* Get the name of the protocol if any */
+  if (zlog_default)
+      protocolname = zlog_proto_names[zlog_default->protocol];
+  else
+      protocolname = zlog_proto_names[ZLOG_NONE];
+                                                                           
+#ifdef CONSUMED_TIME_CHECK
+    GETRUSAGE(&after);
+    if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
+       CONSUMED_TIME_CHECK)
+      /* Warn about CPU hog that must be fixed. */
+      zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
+               realtime/1000, cputime/1000, buf);
+  }
+#endif /* CONSUMED_TIME_CHECK */
+
+  if (ret != CMD_SUCCESS)
+    switch (ret)
+      {
+      case CMD_WARNING:
+       if (vty->type == VTY_FILE)
+         vty_out (vty, "Warning...%s", VTY_NEWLINE);
+       break;
+      case CMD_ERR_AMBIGUOUS:
+       vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+       break;
+      case CMD_ERR_NO_MATCH:
+       vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
+       break;
+      case CMD_ERR_INCOMPLETE:
+       vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
+       break;
+      }
+  cmd_free_strvec (vline);
+
+  return ret;
+}
+
+static const char telnet_backward_char = 0x08;
+static const char telnet_space_char = ' ';
+
+/* Basic function to write buffer to vty. */
+static void
+vty_write (struct vty *vty, const char *buf, size_t nbytes)
+{
+  if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+    return;
+
+  /* Should we do buffering here ?  And make vty_flush (vty) ? */
+  buffer_put (vty->obuf, buf, nbytes);
+}
+
+/* Basic function to insert character into vty. */
+static void
+vty_self_insert (struct vty *vty, char c)
+{
+  int i;
+  int length;
+  
+  vty_buf_assert (vty);
+  
+  /* length is sans nul, max is with */
+  if (vty->length + 1 >= vty->max)
+    return;
+
+  length = vty->length - vty->cp;
+  memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
+  vty->length++;
+  vty->buf[vty->length] = '\0';
+
+  vty_buf_put (vty, c);
+
+  vty_write (vty, &vty->buf[vty->cp], length + 1);
+  for (i = 0; i < length; i++)
+    vty_write (vty, &telnet_backward_char, 1);
+
+  vty->cp++;
+  
+  vty_buf_assert (vty);
+}
+
+/* Self insert character 'c' in overwrite mode. */
+static void
+vty_self_insert_overwrite (struct vty *vty, char c)
+{
+  vty_buf_assert (vty);
+  
+  if (vty->cp == vty->length)
+    {
+      vty_self_insert (vty, c);
+      return;
+    }
+
+  vty_buf_put (vty, c);
+  vty->cp++;
+  
+  vty_buf_assert (vty);
+  
+  vty_write (vty, &c, 1);
+}
+
+/**
+ * Insert a string into vty->buf at the current cursor position.
+ *
+ * If the resultant string would be larger than VTY_BUFSIZ it is
+ * truncated to fit.
+ */
+static void
+vty_insert_word_overwrite (struct vty *vty, char *str)
+{
+  vty_buf_assert (vty);
+  
+  size_t nwrite = MIN ((int) strlen (str), vty->max - vty->cp - 1);
+  memcpy (&vty->buf[vty->cp], str, nwrite);
+  vty->cp += nwrite;
+  vty->length = vty->cp;
+  vty->buf[vty->length] = '\0';
+  vty_buf_assert (vty);
+  
+  vty_write (vty, str, nwrite);
+}
+
+/* Forward character. */
+static void
+vty_forward_char (struct vty *vty)
+{
+  vty_buf_assert (vty);
+  
+  if (vty->cp < vty->length)
+    {
+      vty_write (vty, &vty->buf[vty->cp], 1);
+      vty->cp++;
+    }
+  
+  vty_buf_assert (vty);
+}
+
+/* Backward character. */
+static void
+vty_backward_char (struct vty *vty)
+{
+  vty_buf_assert (vty);
+  
+  if (vty->cp > 0)
+    {
+      vty->cp--;
+      vty_write (vty, &telnet_backward_char, 1);
+    }
+  
+  vty_buf_assert (vty);
+}
+
+/* Move to the beginning of the line. */
+static void
+vty_beginning_of_line (struct vty *vty)
+{
+  while (vty->cp)
+    vty_backward_char (vty);
+}
+
+/* Move to the end of the line. */
+static void
+vty_end_of_line (struct vty *vty)
+{
+  while (vty->cp < vty->length)
+    vty_forward_char (vty);
+}
+
+static void vty_kill_line_from_beginning (struct vty *);
+static void vty_redraw_line (struct vty *);
+
+/* Print command line history.  This function is called from
+   vty_next_line and vty_previous_line. */
+static void
+vty_history_print (struct vty *vty)
+{
+  int length;
+
+  vty_kill_line_from_beginning (vty);
+
+  /* Get previous line from history buffer */
+  length = strlen (vty->hist[vty->hp]);
+  memcpy (vty->buf, vty->hist[vty->hp], length);
+  vty->cp = vty->length = length;
+  vty->buf[vty->length] = '\0';
+  vty_buf_assert (vty);
+  
+  /* Redraw current line */
+  vty_redraw_line (vty);
+}
+
+/* Show next command line history. */
+static void
+vty_next_line (struct vty *vty)
+{
+  int try_index;
+
+  if (vty->hp == vty->hindex)
+    return;
+
+  /* Try is there history exist or not. */
+  try_index = vty->hp;
+  if (try_index == (VTY_MAXHIST - 1))
+    try_index = 0;
+  else
+    try_index++;
+
+  /* If there is not history return. */
+  if (vty->hist[try_index] == NULL)
+    return;
+  else
+    vty->hp = try_index;
+
+  vty_history_print (vty);
+}
+
+/* Show previous command line history. */
+static void
+vty_previous_line (struct vty *vty)
+{
+  int try_index;
+
+  try_index = vty->hp;
+  if (try_index == 0)
+    try_index = VTY_MAXHIST - 1;
+  else
+    try_index--;
+
+  if (vty->hist[try_index] == NULL)
+    return;
+  else
+    vty->hp = try_index;
+
+  vty_history_print (vty);
+}
+
+/* This function redraw all of the command line character. */
+static void
+vty_redraw_line (struct vty *vty)
+{
+  vty_write (vty, vty->buf, vty->length);
+  vty->cp = vty->length;
+  
+  vty_buf_assert (vty);
+}
+
+/* Forward word. */
+static void
+vty_forward_word (struct vty *vty)
+{
+  while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+    vty_forward_char (vty);
+  
+  while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+    vty_forward_char (vty);
+}
+
+/* Backward word without skipping training space. */
+static void
+vty_backward_pure_word (struct vty *vty)
+{
+  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+    vty_backward_char (vty);
+}
+
+/* Backward word. */
+static void
+vty_backward_word (struct vty *vty)
+{
+  while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+    vty_backward_char (vty);
+
+  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+    vty_backward_char (vty);
+}
+
+/* When '^D' is typed at the beginning of the line we move to the down
+   level. */
+static void
+vty_down_level (struct vty *vty)
+{
+  vty_out (vty, "%s", VTY_NEWLINE);
+  (*config_exit_cmd.func)(NULL, vty, 0, NULL);
+  vty_prompt (vty);
+  vty->cp = 0;
+}
+
+/* When '^Z' is received from vty, move down to the enable mode. */
+static void
+vty_end_config (struct vty *vty)
+{
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+    case RESTRICTED_NODE:
+      /* Nothing to do. */
+      break;
+    case CONFIG_NODE:
+    case INTERFACE_NODE:
+    case ZEBRA_NODE:
+    case RIP_NODE:
+    case RIPNG_NODE:
+    case BABEL_NODE:
+    case BGP_NODE:
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+    case BGP_IPV4_NODE:
+    case BGP_IPV4M_NODE:
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+    case RMAP_NODE:
+    case OSPF_NODE:
+    case OSPF6_NODE:
+    case ISIS_NODE:
+    case KEYCHAIN_NODE:
+    case KEYCHAIN_KEY_NODE:
+    case MASC_NODE:
+    case PIM_NODE:
+    case VTY_NODE:
+      vty_config_unlock (vty);
+      vty->node = ENABLE_NODE;
+      break;
+    default:
+      /* Unknown node, we have to ignore it. */
+      break;
+    }
+
+  vty_prompt (vty);
+  vty->cp = 0;
+}
+
+/* Delete a charcter at the current point. */
+static void
+vty_delete_char (struct vty *vty)
+{
+  int i;
+  int size;
+
+  if (vty->length == 0)
+    {
+      vty_down_level (vty);
+      return;
+    }
+  
+  if (vty->cp == vty->length)
+    return;                    /* completion need here? */
+
+  vty_buf_assert (vty);
+  
+  size = vty->length - vty->cp;
+
+  vty->length--;
+  memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
+  vty->buf[vty->length] = '\0';
+  
+  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+    return;
+
+  vty_write (vty, &vty->buf[vty->cp], size - 1);
+  vty_write (vty, &telnet_space_char, 1);
+
+  for (i = 0; i < size; i++)
+    vty_write (vty, &telnet_backward_char, 1);
+}
+
+/* Delete a character before the point. */
+static void
+vty_delete_backward_char (struct vty *vty)
+{
+  if (vty->cp == 0)
+    return;
+
+  vty_backward_char (vty);
+  vty_delete_char (vty);
+}
+
+/* Kill rest of line from current point. */
+static void
+vty_kill_line (struct vty *vty)
+{
+  int i;
+  int size;
+
+  size = vty->length - vty->cp;
+  
+  if (size == 0)
+    return;
+
+  for (i = 0; i < size; i++)
+    vty_write (vty, &telnet_space_char, 1);
+  for (i = 0; i < size; i++)
+    vty_write (vty, &telnet_backward_char, 1);
+
+  memset (&vty->buf[vty->cp], 0, size);
+  vty->length = vty->cp;
+  vty_buf_assert (vty);
+}
+
+/* Kill line from the beginning. */
+static void
+vty_kill_line_from_beginning (struct vty *vty)
+{
+  vty_beginning_of_line (vty);
+  vty_kill_line (vty);
+}
+
+/* Delete a word before the point. */
+static void
+vty_forward_kill_word (struct vty *vty)
+{
+  while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+    vty_delete_char (vty);
+  while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+    vty_delete_char (vty);
+}
+
+/* Delete a word before the point. */
+static void
+vty_backward_kill_word (struct vty *vty)
+{
+  while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+    vty_delete_backward_char (vty);
+  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+    vty_delete_backward_char (vty);
+}
+
+/* Transpose chars before or at the point. */
+static void
+vty_transpose_chars (struct vty *vty)
+{
+  char c1, c2;
+
+  /* If length is short or point is near by the beginning of line then
+     return. */
+  if (vty->length < 2 || vty->cp < 1)
+    return;
+
+  /* In case of point is located at the end of the line. */
+  if (vty->cp == vty->length)
+    {
+      c1 = vty->buf[vty->cp - 1];
+      c2 = vty->buf[vty->cp - 2];
+
+      vty_backward_char (vty);
+      vty_backward_char (vty);
+      vty_self_insert_overwrite (vty, c1);
+      vty_self_insert_overwrite (vty, c2);
+    }
+  else
+    {
+      c1 = vty->buf[vty->cp];
+      c2 = vty->buf[vty->cp - 1];
+
+      vty_backward_char (vty);
+      vty_self_insert_overwrite (vty, c1);
+      vty_self_insert_overwrite (vty, c2);
+    }
+}
+
+/* Do completion at vty interface. */
+static void
+vty_complete_command (struct vty *vty)
+{
+  int i;
+  int ret;
+  char **matched = NULL;
+  vector vline;
+
+  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+    return;
+
+  vline = cmd_make_strvec (vty->buf);
+  if (vline == NULL)
+    return;
+
+  /* In case of 'help \t'. */
+  if (isspace ((int) vty->buf[vty->length - 1]))
+    vector_set (vline, NULL);
+
+  matched = cmd_complete_command_lib (vline, vty, &ret, 1);
+  
+  cmd_free_strvec (vline);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+  switch (ret)
+    {
+    case CMD_ERR_AMBIGUOUS:
+      vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      break;
+    case CMD_ERR_NO_MATCH:
+      /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      break;
+    case CMD_COMPLETE_FULL_MATCH:
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      vty_backward_pure_word (vty);
+      vty_insert_word_overwrite (vty, matched[0]);
+      vty_self_insert (vty, ' ');
+      XFREE (MTYPE_TMP, matched[0]);
+      break;
+    case CMD_COMPLETE_MATCH:
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      vty_backward_pure_word (vty);
+      vty_insert_word_overwrite (vty, matched[0]);
+      XFREE (MTYPE_TMP, matched[0]);
+      vector_only_index_free (matched);
+      return;
+      break;
+    case CMD_COMPLETE_LIST_MATCH:
+      for (i = 0; matched[i] != NULL; i++)
+       {
+         if (i != 0 && ((i % 6) == 0))
+           vty_out (vty, "%s", VTY_NEWLINE);
+         vty_out (vty, "%-10s ", matched[i]);
+         XFREE (MTYPE_TMP, matched[i]);
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      break;
+    case CMD_ERR_NOTHING_TODO:
+      vty_prompt (vty);
+      vty_redraw_line (vty);
+      break;
+    default:
+      break;
+    }
+  if (matched)
+    vector_only_index_free (matched);
+}
+
+static void
+vty_describe_fold (struct vty *vty, int cmd_width,
+                  unsigned int desc_width, struct cmd_token *token)
+{
+  char *buf;
+  const char *cmd, *p;
+  int pos;
+
+  cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
+
+  if (desc_width <= 0)
+    {
+      vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
+      return;
+    }
+
+  buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
+
+  for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
+    {
+      for (pos = desc_width; pos > 0; pos--)
+      if (*(p + pos) == ' ')
+        break;
+
+      if (pos == 0)
+      break;
+
+      strncpy (buf, p, pos);
+      buf[pos] = '\0';
+      vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+
+      cmd = "";
+    }
+
+  vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+
+  XFREE (MTYPE_TMP, buf);
+}
+
+/* Describe matched command function. */
+static void
+vty_describe_command (struct vty *vty)
+{
+  int ret;
+  vector vline;
+  vector describe;
+  unsigned int i, width, desc_width;
+  struct cmd_token *token, *token_cr = NULL;
+
+  vline = cmd_make_strvec (vty->buf);
+
+  /* In case of '> ?'. */
+  if (vline == NULL)
+    {
+      vline = vector_init (1);
+      vector_set (vline, NULL);
+    }
+  else 
+    if (isspace ((int) vty->buf[vty->length - 1]))
+      vector_set (vline, NULL);
+
+  describe = cmd_describe_command (vline, vty, &ret);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  /* Ambiguous error. */
+  switch (ret)
+    {
+    case CMD_ERR_AMBIGUOUS:
+      vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+      goto out;
+      break;
+    case CMD_ERR_NO_MATCH:
+      vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
+      goto out;
+      break;
+    }  
+
+  /* Get width of command string. */
+  width = 0;
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
+      {
+       unsigned int len;
+
+       if (token->cmd[0] == '\0')
+         continue;
+
+       len = strlen (token->cmd);
+       if (token->cmd[0] == '.')
+         len--;
+
+       if (width < len)
+         width = len;
+      }
+
+  /* Get width of description string. */
+  desc_width = vty->width - (width + 6);
+
+  /* Print out description. */
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
+      {
+       if (token->cmd[0] == '\0')
+         continue;
+       
+       if (strcmp (token->cmd, command_cr) == 0)
+         {
+           token_cr = token;
+           continue;
+         }
+
+       if (!token->desc)
+         vty_out (vty, "  %-s%s",
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                  VTY_NEWLINE);
+       else if (desc_width >= strlen (token->desc))
+         vty_out (vty, "  %-*s  %s%s", width,
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                  token->desc, VTY_NEWLINE);
+       else
+         vty_describe_fold (vty, width, desc_width, token);
+
+#if 0
+       vty_out (vty, "  %-*s %s%s", width
+                desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                desc->str ? desc->str : "", VTY_NEWLINE);
+#endif /* 0 */
+      }
+
+  if ((token = token_cr))
+    {
+      if (!token->desc)
+       vty_out (vty, "  %-s%s",
+                token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                VTY_NEWLINE);
+      else if (desc_width >= strlen (token->desc))
+       vty_out (vty, "  %-*s  %s%s", width,
+                token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                token->desc, VTY_NEWLINE);
+      else
+       vty_describe_fold (vty, width, desc_width, token);
+    }
+
+out:
+  cmd_free_strvec (vline);
+  if (describe)
+    vector_free (describe);
+
+  vty_prompt (vty);
+  vty_redraw_line (vty);
+}
+
+static void
+vty_clear_buf (struct vty *vty)
+{
+  memset (vty->buf, 0, vty->max);
+}
+
+/* ^C stop current input and do not add command line to the history. */
+static void
+vty_stop_input (struct vty *vty)
+{
+  vty->cp = vty->length = 0;
+  vty_clear_buf (vty);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+    case RESTRICTED_NODE:
+      /* Nothing to do. */
+      break;
+    case CONFIG_NODE:
+    case INTERFACE_NODE:
+    case ZEBRA_NODE:
+    case RIP_NODE:
+    case RIPNG_NODE:
+    case BABEL_NODE:
+    case BGP_NODE:
+    case RMAP_NODE:
+    case OSPF_NODE:
+    case OSPF6_NODE:
+    case ISIS_NODE:
+    case KEYCHAIN_NODE:
+    case KEYCHAIN_KEY_NODE:
+    case MASC_NODE:
+    case PIM_NODE:
+    case VTY_NODE:
+      vty_config_unlock (vty);
+      vty->node = ENABLE_NODE;
+      break;
+    default:
+      /* Unknown node, we have to ignore it. */
+      break;
+    }
+  vty_prompt (vty);
+
+  /* Set history pointer to the latest one. */
+  vty->hp = vty->hindex;
+}
+
+/* Add current command line to the history buffer. */
+static void
+vty_hist_add (struct vty *vty)
+{
+  int index;
+
+  if (vty->length == 0)
+    return;
+
+  index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
+
+  /* Ignore the same string as previous one. */
+  if (vty->hist[index])
+    if (strcmp (vty->buf, vty->hist[index]) == 0)
+      {
+      vty->hp = vty->hindex;
+      return;
+      }
+
+  /* Insert history entry. */
+  if (vty->hist[vty->hindex])
+    XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
+  vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
+
+  /* History index rotation. */
+  vty->hindex++;
+  if (vty->hindex == VTY_MAXHIST)
+    vty->hindex = 0;
+
+  vty->hp = vty->hindex;
+}
+
+/* #define TELNET_OPTION_DEBUG */
+
+/* Get telnet window size. */
+static int
+vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+{
+#ifdef TELNET_OPTION_DEBUG
+  int i;
+
+  for (i = 0; i < nbytes; i++)
+    {
+      switch (buf[i])
+       {
+       case IAC:
+         vty_out (vty, "IAC ");
+         break;
+       case WILL:
+         vty_out (vty, "WILL ");
+         break;
+       case WONT:
+         vty_out (vty, "WONT ");
+         break;
+       case DO:
+         vty_out (vty, "DO ");
+         break;
+       case DONT:
+         vty_out (vty, "DONT ");
+         break;
+       case SB:
+         vty_out (vty, "SB ");
+         break;
+       case SE:
+         vty_out (vty, "SE ");
+         break;
+       case TELOPT_ECHO:
+         vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
+         break;
+       case TELOPT_SGA:
+         vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
+         break;
+       case TELOPT_NAWS:
+         vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
+         break;
+       default:
+         vty_out (vty, "%x ", buf[i]);
+         break;
+       }
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+#endif /* TELNET_OPTION_DEBUG */
+
+  switch (buf[0])
+    {
+    case SB:
+      vty->sb_len = 0;
+      vty->iac_sb_in_progress = 1;
+      return 0;
+      break;
+    case SE: 
+      {
+       if (!vty->iac_sb_in_progress)
+         return 0;
+
+       if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
+         {
+           vty->iac_sb_in_progress = 0;
+           return 0;
+         }
+       switch (vty->sb_buf[0])
+         {
+         case TELOPT_NAWS:
+           if (vty->sb_len != TELNET_NAWS_SB_LEN)
+             zlog_warn("RFC 1073 violation detected: telnet NAWS option "
+                       "should send %d characters, but we received %lu",
+                       TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+           else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
+             zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+                      "too small to handle the telnet NAWS option",
+                      (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+           else
+             {
+               vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
+               vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
+#ifdef TELNET_OPTION_DEBUG
+               vty_out(vty, "TELNET NAWS window size negotiation completed: "
+                             "width %d, height %d%s",
+                       vty->width, vty->height, VTY_NEWLINE);
+#endif
+             }
+           break;
+         }
+       vty->iac_sb_in_progress = 0;
+       return 0;
+       break;
+      }
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Execute current command line. */
+static int
+vty_execute (struct vty *vty)
+{
+  int ret;
+
+  ret = CMD_SUCCESS;
+
+  switch (vty->node)
+    {
+    case AUTH_NODE:
+    case AUTH_ENABLE_NODE:
+      vty_auth (vty, vty->buf);
+      break;
+    default:
+      ret = vty_command (vty, vty->buf);
+      if (vty->type == VTY_TERM)
+       vty_hist_add (vty);
+      break;
+    }
+
+  /* Clear command line buffer. */
+  vty->cp = vty->length = 0;
+  vty_clear_buf (vty);
+
+  if (vty->status != VTY_CLOSE )
+    vty_prompt (vty);
+
+  return ret;
+}
+
+#define CONTROL(X)  ((X) - '@')
+#define VTY_NORMAL     0
+#define VTY_PRE_ESCAPE 1  /* Esc seen */
+#define VTY_ESCAPE     2  /* ANSI terminal escape (Esc-[) seen */
+#define VTY_LITERAL    3  /* Next char taken as literal */
+
+/* Escape character command map. */
+static void
+vty_escape_map (unsigned char c, struct vty *vty)
+{
+  switch (c)
+    {
+    case ('A'):
+      vty_previous_line (vty);
+      break;
+    case ('B'):
+      vty_next_line (vty);
+      break;
+    case ('C'):
+      vty_forward_char (vty);
+      break;
+    case ('D'):
+      vty_backward_char (vty);
+      break;
+    default:
+      break;
+    }
+
+  /* Go back to normal mode. */
+  vty->escape = VTY_NORMAL;
+}
+
+/* Quit print out to the buffer. */
+static void
+vty_buffer_reset (struct vty *vty)
+{
+  buffer_reset (vty->obuf);
+  vty_prompt (vty);
+  vty_redraw_line (vty);
+}
+
+/* Read data via vty socket. */
+static int
+vty_read (struct thread *thread)
+{
+  int i;
+  int nbytes;
+  unsigned char buf[VTY_READ_BUFSIZ];
+
+  int vty_sock = THREAD_FD (thread);
+  struct vty *vty = THREAD_ARG (thread);
+  vty->t_read = NULL;
+
+  /* Read raw data from socket */
+  if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
+    {
+      if (nbytes < 0)
+       {
+         if (ERRNO_IO_RETRY(errno))
+           {
+             vty_event (VTY_READ, vty_sock, vty);
+             return 0;
+           }
+         vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+         zlog_warn("%s: read error on vty client fd %d, closing: %s",
+                   __func__, vty->fd, safe_strerror(errno));
+          buffer_reset(vty->obuf);
+       }
+      vty->status = VTY_CLOSE;
+    }
+
+  for (i = 0; i < nbytes; i++) 
+    {
+      if (buf[i] == IAC)
+       {
+         if (!vty->iac)
+           {
+             vty->iac = 1;
+             continue;
+           }
+         else
+           {
+             vty->iac = 0;
+           }
+       }
+      
+      if (vty->iac_sb_in_progress && !vty->iac)
+       {
+           if (vty->sb_len < sizeof(vty->sb_buf))
+             vty->sb_buf[vty->sb_len] = buf[i];
+           vty->sb_len++;
+           continue;
+       }
+
+      if (vty->iac)
+       {
+         /* In case of telnet command */
+         int ret = 0;
+         ret = vty_telnet_option (vty, buf + i, nbytes - i);
+         vty->iac = 0;
+         i += ret;
+         continue;
+       }
+               
+
+      if (vty->status == VTY_MORE)
+       {
+         switch (buf[i])
+           {
+           case CONTROL('C'):
+           case 'q':
+           case 'Q':
+             vty_buffer_reset (vty);
+             break;
+#if 0 /* More line does not work for "show ip bgp".  */
+           case '\n':
+           case '\r':
+             vty->status = VTY_MORELINE;
+             break;
+#endif
+           default:
+             break;
+           }
+         continue;
+       }
+
+      /* Escape character. */
+      if (vty->escape == VTY_ESCAPE)
+       {
+         vty_escape_map (buf[i], vty);
+         continue;
+       }
+      
+      if (vty->escape == VTY_LITERAL)
+        {
+          vty_self_insert (vty, buf[i]);
+          vty->escape = VTY_NORMAL;
+          continue;
+        }
+      
+      /* Pre-escape status. */
+      if (vty->escape == VTY_PRE_ESCAPE)
+       {
+         switch (buf[i])
+           {
+           case '[':
+             vty->escape = VTY_ESCAPE;
+             break;
+           case 'b':
+             vty_backward_word (vty);
+             vty->escape = VTY_NORMAL;
+             break;
+           case 'f':
+             vty_forward_word (vty);
+             vty->escape = VTY_NORMAL;
+             break;
+           case 'd':
+             vty_forward_kill_word (vty);
+             vty->escape = VTY_NORMAL;
+             break;
+           case CONTROL('H'):
+           case 0x7f:
+             vty_backward_kill_word (vty);
+             vty->escape = VTY_NORMAL;
+             break;
+           default:
+             vty->escape = VTY_NORMAL;
+             break;
+           }
+         continue;
+       }
+
+      switch (buf[i])
+       {
+       case CONTROL('A'):
+         vty_beginning_of_line (vty);
+         break;
+       case CONTROL('B'):
+         vty_backward_char (vty);
+         break;
+       case CONTROL('C'):
+         vty_stop_input (vty);
+         break;
+       case CONTROL('D'):
+         vty_delete_char (vty);
+         break;
+       case CONTROL('E'):
+         vty_end_of_line (vty);
+         break;
+       case CONTROL('F'):
+         vty_forward_char (vty);
+         break;
+       case CONTROL('H'):
+       case 0x7f:
+         vty_delete_backward_char (vty);
+         break;
+       case CONTROL('K'):
+         vty_kill_line (vty);
+         break;
+       case CONTROL('N'):
+         vty_next_line (vty);
+         break;
+       case CONTROL('P'):
+         vty_previous_line (vty);
+         break;
+       case CONTROL('T'):
+         vty_transpose_chars (vty);
+         break;
+       case CONTROL('U'):
+         vty_kill_line_from_beginning (vty);
+         break;
+        case CONTROL('V'):
+          vty->escape = VTY_LITERAL;
+          break;
+       case CONTROL('W'):
+         vty_backward_kill_word (vty);
+         break;
+       case CONTROL('Z'):
+         vty_end_config (vty);
+         break;
+       case '\n':
+       case '\r':
+         vty_out (vty, "%s", VTY_NEWLINE);
+         vty_execute (vty);
+         break;
+       case '\t':
+         vty_complete_command (vty);
+         break;
+       case '?':
+         if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+           vty_self_insert (vty, buf[i]);
+         else
+           vty_describe_command (vty);
+         break;
+       case '\033':
+         if (i + 1 < nbytes && buf[i + 1] == '[')
+           {
+             vty->escape = VTY_ESCAPE;
+             i++;
+           }
+         else
+           vty->escape = VTY_PRE_ESCAPE;
+         break;
+       default:
+         if (buf[i] > 31 && buf[i] < 127)
+           vty_self_insert (vty, buf[i]);
+         break;
+       }
+    }
+
+  /* Check status. */
+  if (vty->status == VTY_CLOSE)
+    vty_close (vty);
+  else
+    {
+      vty_event (VTY_WRITE, vty->wfd, vty);
+      vty_event (VTY_READ, vty_sock, vty);
+    }
+  return 0;
+}
+
+/* Flush buffer to the vty. */
+static int
+vty_flush (struct thread *thread)
+{
+  int erase;
+  buffer_status_t flushrc;
+  int vty_sock = THREAD_FD (thread);
+  struct vty *vty = THREAD_ARG (thread);
+
+  vty->t_write = NULL;
+
+  /* Tempolary disable read thread. */
+  if ((vty->lines == 0) && vty->t_read)
+    {
+      thread_cancel (vty->t_read);
+      vty->t_read = NULL;
+    }
+
+  /* Function execution continue. */
+  erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
+
+  /* N.B. if width is 0, that means we don't know the window size. */
+  if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
+    flushrc = buffer_flush_available(vty->obuf, vty_sock);
+  else if (vty->status == VTY_MORELINE)
+    flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
+                                 1, erase, 0);
+  else
+    flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
+                                 vty->lines >= 0 ? vty->lines :
+                                                   vty->height,
+                                 erase, 0);
+  switch (flushrc)
+    {
+    case BUFFER_ERROR:
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("buffer_flush failed on vty client fd %d, closing",
+               vty->fd);
+      buffer_reset(vty->obuf);
+      vty_close(vty);
+      return 0;
+    case BUFFER_EMPTY:
+      if (vty->status == VTY_CLOSE)
+       vty_close (vty);
+      else
+       {
+         vty->status = VTY_NORMAL;
+         if (vty->lines == 0)
+           vty_event (VTY_READ, vty_sock, vty);
+       }
+      break;
+    case BUFFER_PENDING:
+      /* There is more data waiting to be written. */
+      vty->status = VTY_MORE;
+      if (vty->lines == 0)
+       vty_event (VTY_WRITE, vty_sock, vty);
+      break;
+    }
+
+  return 0;
+}
+
+/* allocate and initialise vty */
+static struct vty *
+vty_new_init (int vty_sock)
+{
+  struct vty *vty;
+
+  vty = vty_new ();
+  vty->fd = vty_sock;
+  vty->wfd = vty_sock;
+  vty->type = VTY_TERM;
+  vty->node = AUTH_NODE;
+  vty->fail = 0;
+  vty->cp = 0;
+  vty_clear_buf (vty);
+  vty->length = 0;
+  memset (vty->hist, 0, sizeof (vty->hist));
+  vty->hp = 0;
+  vty->hindex = 0;
+  vector_set_index (vtyvec, vty_sock, vty);
+  vty->status = VTY_NORMAL;
+  vty->lines = -1;
+  vty->iac = 0;
+  vty->iac_sb_in_progress = 0;
+  vty->sb_len = 0;
+
+  return vty;
+}
+
+/* Create new vty structure. */
+static struct vty *
+vty_create (int vty_sock, union sockunion *su)
+{
+  char buf[SU_ADDRSTRLEN];
+  struct vty *vty;
+
+  sockunion2str(su, buf, SU_ADDRSTRLEN);
+
+  /* Allocate new vty structure and set up default values. */
+  vty = vty_new_init (vty_sock);
+
+  /* configurable parameters not part of basic init */
+  vty->v_timeout = vty_timeout_val;
+  strcpy (vty->address, buf);
+  if (no_password_check)
+    {
+      if (restricted_mode)
+        vty->node = RESTRICTED_NODE;
+      else if (host.advanced)
+       vty->node = ENABLE_NODE;
+      else
+       vty->node = VIEW_NODE;
+    }
+  if (host.lines >= 0)
+    vty->lines = host.lines;
+
+  if (! no_password_check)
+    {
+      /* Vty is not available if password isn't set. */
+      if (host.password == NULL && host.password_encrypt == NULL)
+       {
+         vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
+         vty->status = VTY_CLOSE;
+         vty_close (vty);
+         return NULL;
+       }
+    }
+
+  /* Say hello to the world. */
+  vty_hello (vty);
+  if (! no_password_check)
+    vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  /* Setting up terminal. */
+  vty_will_echo (vty);
+  vty_will_suppress_go_ahead (vty);
+
+  vty_dont_linemode (vty);
+  vty_do_window_size (vty);
+  /* vty_dont_lflow_ahead (vty); */
+
+  vty_prompt (vty);
+
+  /* Add read/write thread. */
+  vty_event (VTY_WRITE, vty_sock, vty);
+  vty_event (VTY_READ, vty_sock, vty);
+
+  return vty;
+}
+
+/* create vty for stdio */
+static struct termios stdio_orig_termios;
+static struct vty *stdio_vty = NULL;
+static void (*stdio_vty_atclose)(void);
+
+static void
+vty_stdio_reset (void)
+{
+  if (stdio_vty)
+    {
+      tcsetattr (0, TCSANOW, &stdio_orig_termios);
+      stdio_vty = NULL;
+
+      if (stdio_vty_atclose)
+        stdio_vty_atclose ();
+      stdio_vty_atclose = NULL;
+    }
+}
+
+struct vty *
+vty_stdio (void (*atclose)())
+{
+  struct vty *vty;
+  struct termios termios;
+
+  /* refuse creating two vtys on stdio */
+  if (stdio_vty)
+    return NULL;
+
+  vty = stdio_vty = vty_new_init (0);
+  stdio_vty_atclose = atclose;
+  vty->wfd = 1;
+
+  /* always have stdio vty in a known _unchangeable_ state, don't want config
+   * to have any effect here to make sure scripting this works as intended */
+  vty->node = ENABLE_NODE;
+  vty->v_timeout = 0;
+  strcpy (vty->address, "console");
+
+  if (!tcgetattr (0, &stdio_orig_termios))
+    {
+      termios = stdio_orig_termios;
+      termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+                           | INLCR | IGNCR | ICRNL | IXON);
+      termios.c_oflag &= ~OPOST;
+      termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+      termios.c_cflag &= ~(CSIZE | PARENB);
+      termios.c_cflag |= CS8;
+      tcsetattr (0, TCSANOW, &termios);
+    }
+
+  vty_prompt (vty);
+
+  /* Add read/write thread. */
+  vty_event (VTY_WRITE, 1, vty);
+  vty_event (VTY_READ, 0, vty);
+
+  return vty;
+}
+
+/* Accept connection from the network. */
+static int
+vty_accept (struct thread *thread)
+{
+  int vty_sock;
+  union sockunion su;
+  int ret;
+  unsigned int on;
+  int accept_sock;
+  struct prefix p;
+  struct access_list *acl = NULL;
+  char buf[SU_ADDRSTRLEN];
+
+  accept_sock = THREAD_FD (thread);
+
+  /* We continue hearing vty socket. */
+  vty_event (VTY_SERV, accept_sock, NULL);
+
+  memset (&su, 0, sizeof (union sockunion));
+
+  /* We can handle IPv4 or IPv6 socket. */
+  vty_sock = sockunion_accept (accept_sock, &su);
+  if (vty_sock < 0)
+    {
+      zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
+      return -1;
+    }
+  set_nonblocking(vty_sock);
+
+  sockunion2hostprefix (&su, &p);
+
+  /* VTY's accesslist apply. */
+  if (p.family == AF_INET && vty_accesslist_name)
+    {
+      if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
+         (access_list_apply (acl, &p) == FILTER_DENY))
+       {
+         zlog (NULL, LOG_INFO, "Vty connection refused from %s",
+               sockunion2str (&su, buf, SU_ADDRSTRLEN));
+         close (vty_sock);
+         
+         /* continue accepting connections */
+         vty_event (VTY_SERV, accept_sock, NULL);
+         
+         return 0;
+       }
+    }
+
+#ifdef HAVE_IPV6
+  /* VTY's ipv6 accesslist apply. */
+  if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
+    {
+      if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
+         (access_list_apply (acl, &p) == FILTER_DENY))
+       {
+         zlog (NULL, LOG_INFO, "Vty connection refused from %s",
+               sockunion2str (&su, buf, SU_ADDRSTRLEN));
+         close (vty_sock);
+         
+         /* continue accepting connections */
+         vty_event (VTY_SERV, accept_sock, NULL);
+         
+         return 0;
+       }
+    }
+#endif /* HAVE_IPV6 */
+  
+  on = 1;
+  ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, 
+                   (char *) &on, sizeof (on));
+  if (ret < 0)
+    zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", 
+         safe_strerror (errno));
+
+  zlog (NULL, LOG_INFO, "Vty connection from %s",
+       sockunion2str (&su, buf, SU_ADDRSTRLEN));
+
+  vty_create (vty_sock, &su);
+
+  return 0;
+}
+
+#ifdef HAVE_IPV6
+static void
+vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+{
+  int ret;
+  struct addrinfo req;
+  struct addrinfo *ainfo;
+  struct addrinfo *ainfo_save;
+  int sock;
+  char port_str[BUFSIZ];
+
+  memset (&req, 0, sizeof (struct addrinfo));
+  req.ai_flags = AI_PASSIVE;
+  req.ai_family = AF_UNSPEC;
+  req.ai_socktype = SOCK_STREAM;
+  sprintf (port_str, "%d", port);
+  port_str[sizeof (port_str) - 1] = '\0';
+
+  ret = getaddrinfo (hostname, port_str, &req, &ainfo);
+
+  if (ret != 0)
+    {
+      fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
+      exit (1);
+    }
+
+  ainfo_save = ainfo;
+
+  do
+    {
+      if (ainfo->ai_family != AF_INET
+#ifdef HAVE_IPV6
+         && ainfo->ai_family != AF_INET6
+#endif /* HAVE_IPV6 */
+         )
+       continue;
+
+      sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
+      if (sock < 0)
+       continue;
+
+      sockopt_v6only (ainfo->ai_family, sock);
+      sockopt_reuseaddr (sock);
+      sockopt_reuseport (sock);
+
+      ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
+      if (ret < 0)
+       {
+         close (sock); /* Avoid sd leak. */
+       continue;
+       }
+
+      ret = listen (sock, 3);
+      if (ret < 0) 
+       {
+         close (sock); /* Avoid sd leak. */
+       continue;
+       }
+
+      vty_event (VTY_SERV, sock, NULL);
+    }
+  while ((ainfo = ainfo->ai_next) != NULL);
+
+  freeaddrinfo (ainfo_save);
+}
+#else /* HAVE_IPV6 */
+
+/* Make vty server socket. */
+static void
+vty_serv_sock_family (const char* addr, unsigned short port, int family)
+{
+  int ret;
+  union sockunion su;
+  int accept_sock;
+  void* naddr=NULL;
+
+  memset (&su, 0, sizeof (union sockunion));
+  su.sa.sa_family = family;
+  if(addr)
+    switch(family)
+    {
+      case AF_INET:
+        naddr=&su.sin.sin_addr;
+        break;
+#ifdef HAVE_IPV6
+      case AF_INET6:
+        naddr=&su.sin6.sin6_addr;
+        break;
+#endif 
+    }
+
+  if(naddr)
+    switch(inet_pton(family,addr,naddr))
+    {
+      case -1:
+        zlog_err("bad address %s",addr);
+       naddr=NULL;
+       break;
+      case 0:
+        zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
+       naddr=NULL;
+    }
+
+  /* Make new socket. */
+  accept_sock = sockunion_stream_socket (&su);
+  if (accept_sock < 0)
+    return;
+
+  /* This is server, so reuse address. */
+  sockopt_reuseaddr (accept_sock);
+  sockopt_reuseport (accept_sock);
+
+  /* Bind socket to universal address and given port. */
+  ret = sockunion_bind (accept_sock, &su, port, naddr);
+  if (ret < 0)
+    {
+      zlog_warn("can't bind socket");
+      close (accept_sock);     /* Avoid sd leak. */
+      return;
+    }
+
+  /* Listen socket under queue 3. */
+  ret = listen (accept_sock, 3);
+  if (ret < 0) 
+    {
+      zlog (NULL, LOG_WARNING, "can't listen socket");
+      close (accept_sock);     /* Avoid sd leak. */
+      return;
+    }
+
+  /* Add vty server event. */
+  vty_event (VTY_SERV, accept_sock, NULL);
+}
+#endif /* HAVE_IPV6 */
+
+#ifdef VTYSH
+/* For sockaddr_un. */
+#include <sys/un.h>
+
+/* VTY shell UNIX domain socket. */
+static void
+vty_serv_un (const char *path)
+{
+  int ret;
+  int sock, len;
+  struct sockaddr_un serv;
+  mode_t old_mask;
+  struct zprivs_ids_t ids;
+  
+  /* First of all, unlink existing socket */
+  unlink (path);
+
+  /* Set umask */
+  old_mask = umask (0007);
+
+  /* Make UNIX domain socket. */
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
+      return;
+    }
+
+  /* Make server socket. */
+  memset (&serv, 0, sizeof (struct sockaddr_un));
+  serv.sun_family = AF_UNIX;
+  strncpy (serv.sun_path, path, strlen (path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+  len = serv.sun_len = SUN_LEN(&serv);
+#else
+  len = sizeof (serv.sun_family) + strlen (serv.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+  ret = bind (sock, (struct sockaddr *) &serv, len);
+  if (ret < 0)
+    {
+      zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
+      close (sock);    /* Avoid sd leak. */
+      return;
+    }
+
+  ret = listen (sock, 5);
+  if (ret < 0)
+    {
+      zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
+      close (sock);    /* Avoid sd leak. */
+      return;
+    }
+
+  umask (old_mask);
+
+  zprivs_get_ids(&ids);
+  
+  if (ids.gid_vty > 0)
+    {
+      /* set group of socket */
+      if ( chown (path, -1, ids.gid_vty) )
+        {
+          zlog_err ("vty_serv_un: could chown socket, %s",
+                     safe_strerror (errno) );
+        }
+    }
+
+  vty_event (VTYSH_SERV, sock, NULL);
+}
+
+/* #define VTYSH_DEBUG 1 */
+
+static int
+vtysh_accept (struct thread *thread)
+{
+  int accept_sock;
+  int sock;
+  int client_len;
+  struct sockaddr_un client;
+  struct vty *vty;
+  
+  accept_sock = THREAD_FD (thread);
+
+  vty_event (VTYSH_SERV, accept_sock, NULL);
+
+  memset (&client, 0, sizeof (struct sockaddr_un));
+  client_len = sizeof (struct sockaddr_un);
+
+  sock = accept (accept_sock, (struct sockaddr *) &client,
+                (socklen_t *) &client_len);
+
+  if (sock < 0)
+    {
+      zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
+      return -1;
+    }
+
+  if (set_nonblocking(sock) < 0)
+    {
+      zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
+                 " %s, closing", sock, safe_strerror (errno));
+      close (sock);
+      return -1;
+    }
+  
+#ifdef VTYSH_DEBUG
+  printf ("VTY shell accept\n");
+#endif /* VTYSH_DEBUG */
+
+  vty = vty_new ();
+  vty->fd = sock;
+  vty->wfd = sock;
+  vty->type = VTY_SHELL_SERV;
+  vty->node = VIEW_NODE;
+
+  vty_event (VTYSH_READ, sock, vty);
+
+  return 0;
+}
+
+static int
+vtysh_flush(struct vty *vty)
+{
+  switch (buffer_flush_available(vty->obuf, vty->wfd))
+    {
+    case BUFFER_PENDING:
+      vty_event(VTYSH_WRITE, vty->wfd, vty);
+      break;
+    case BUFFER_ERROR:
+      vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+      zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
+      buffer_reset(vty->obuf);
+      vty_close(vty);
+      return -1;
+      break;
+    case BUFFER_EMPTY:
+      break;
+    }
+  return 0;
+}
+
+static int
+vtysh_read (struct thread *thread)
+{
+  int ret;
+  int sock;
+  int nbytes;
+  struct vty *vty;
+  unsigned char buf[VTY_READ_BUFSIZ];
+  unsigned char *p;
+  u_char header[4] = {0, 0, 0, 0};
+
+  sock = THREAD_FD (thread);
+  vty = THREAD_ARG (thread);
+  vty->t_read = NULL;
+
+  if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
+    {
+      if (nbytes < 0)
+       {
+         if (ERRNO_IO_RETRY(errno))
+           {
+             vty_event (VTYSH_READ, sock, vty);
+             return 0;
+           }
+         vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
+         zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
+                   __func__, sock, safe_strerror(errno));
+       }
+      buffer_reset(vty->obuf);
+      vty_close (vty);
+#ifdef VTYSH_DEBUG
+      printf ("close vtysh\n");
+#endif /* VTYSH_DEBUG */
+      return 0;
+    }
+
+#ifdef VTYSH_DEBUG
+  printf ("line: %.*s\n", nbytes, buf);
+#endif /* VTYSH_DEBUG */
+
+  if (vty->length + nbytes >= vty->max)
+    {
+      /* Clear command line buffer. */
+      vty->cp = vty->length = 0;
+      vty_clear_buf (vty);
+      vty_out (vty, "%% Command is too long.%s", VTY_NEWLINE);
+      goto out;
+    }
+  
+  for (p = buf; p < buf+nbytes; p++)
+    {
+      vty->buf[vty->length++] = *p;
+      if (*p == '\0')
+       {
+         
+         /* Pass this line to parser. */
+         ret = vty_execute (vty);
+         /* Note that vty_execute clears the command buffer and resets
+            vty->length to 0. */
+
+         /* Return result. */
+#ifdef VTYSH_DEBUG
+         printf ("result: %d\n", ret);
+         printf ("vtysh node: %d\n", vty->node);
+#endif /* VTYSH_DEBUG */
+
+         header[3] = ret;
+         buffer_put(vty->obuf, header, 4);
+
+         if (!vty->t_write && (vtysh_flush(vty) < 0))
+           /* Try to flush results; exit if a write error occurs. */
+           return 0;
+       }
+    }
+
+out:
+  vty_event (VTYSH_READ, sock, vty);
+
+  return 0;
+}
+
+static int
+vtysh_write (struct thread *thread)
+{
+  struct vty *vty = THREAD_ARG (thread);
+
+  vty->t_write = NULL;
+  vtysh_flush(vty);
+  return 0;
+}
+
+#endif /* VTYSH */
+
+/* Determine address family to bind. */
+void
+vty_serv_sock (const char *addr, unsigned short port, const char *path)
+{
+  /* If port is set to 0, do not listen on TCP/IP at all! */
+  if (port)
+    {
+
+#ifdef HAVE_IPV6
+      vty_serv_sock_addrinfo (addr, port);
+#else /* ! HAVE_IPV6 */
+      vty_serv_sock_family (addr,port, AF_INET);
+#endif /* HAVE_IPV6 */
+    }
+
+#ifdef VTYSH
+  vty_serv_un (path);
+#endif /* VTYSH */
+}
+
+/* Close vty interface.  Warning: call this only from functions that
+   will be careful not to access the vty afterwards (since it has
+   now been freed).  This is safest from top-level functions (called
+   directly by the thread dispatcher). */
+void
+vty_close (struct vty *vty)
+{
+  int i;
+
+  /* Cancel threads.*/
+  if (vty->t_read)
+    thread_cancel (vty->t_read);
+  if (vty->t_write)
+    thread_cancel (vty->t_write);
+  if (vty->t_timeout)
+    thread_cancel (vty->t_timeout);
+
+  /* Flush buffer. */
+  buffer_flush_all (vty->obuf, vty->wfd);
+
+  /* Free input buffer. */
+  buffer_free (vty->obuf);
+
+  /* Free command history. */
+  for (i = 0; i < VTY_MAXHIST; i++)
+    if (vty->hist[i])
+      XFREE (MTYPE_VTY_HIST, vty->hist[i]);
+
+  /* Unset vector. */
+  vector_unset (vtyvec, vty->fd);
+
+  /* Close socket. */
+  if (vty->fd > 0)
+    close (vty->fd);
+  else
+    vty_stdio_reset ();
+
+  if (vty->buf)
+    XFREE (MTYPE_VTY, vty->buf);
+
+  /* Check configure. */
+  vty_config_unlock (vty);
+
+  /* OK free vty. */
+  XFREE (MTYPE_VTY, vty);
+}
+
+/* When time out occur output message then close connection. */
+static int
+vty_timeout (struct thread *thread)
+{
+  struct vty *vty;
+
+  vty = THREAD_ARG (thread);
+  vty->t_timeout = NULL;
+  vty->v_timeout = 0;
+
+  /* Clear buffer*/
+  buffer_reset (vty->obuf);
+  vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
+
+  /* Close connection. */
+  vty->status = VTY_CLOSE;
+  vty_close (vty);
+
+  return 0;
+}
+
+/* Read up configuration file from file_name. */
+static void
+vty_read_file (FILE *confp)
+{
+  int ret;
+  struct vty *vty;
+  unsigned int line_num = 0;
+
+  vty = vty_new ();
+  vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
+  if (vty->wfd < 0)
+  {
+    /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
+    vty->wfd = STDOUT_FILENO;
+  }
+  vty->fd = STDIN_FILENO;
+  vty->type = VTY_FILE;
+  vty->node = CONFIG_NODE;
+  
+  /* Execute configuration file */
+  ret = config_from_file (vty, confp, &line_num);
+
+  /* Flush any previous errors before printing messages below */
+  buffer_flush_all (vty->obuf, vty->fd);
+
+  if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) 
+    {
+      switch (ret)
+       {
+         case CMD_ERR_AMBIGUOUS:
+           fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
+           break;
+         case CMD_ERR_NO_MATCH:
+           fprintf (stderr, "*** Error reading config: There is no such command.\n");
+           break;
+       }
+      fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
+                      line_num, vty->buf);
+      vty_close (vty);
+      exit (1);
+    }
+
+  vty_close (vty);
+}
+
+static FILE *
+vty_use_backup_config (char *fullpath)
+{
+  char *fullpath_sav, *fullpath_tmp;
+  FILE *ret = NULL;
+  struct stat buf;
+  int tmp, sav;
+  int c;
+  char buffer[512];
+  
+  fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
+  strcpy (fullpath_sav, fullpath);
+  strcat (fullpath_sav, CONF_BACKUP_EXT);
+  if (stat (fullpath_sav, &buf) == -1)
+    {
+      free (fullpath_sav);
+      return NULL;
+    }
+
+  fullpath_tmp = malloc (strlen (fullpath) + 8);
+  sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
+  
+  /* Open file to configuration write. */
+  tmp = mkstemp (fullpath_tmp);
+  if (tmp < 0)
+    {
+      free (fullpath_sav);
+      free (fullpath_tmp);
+      return NULL;
+    }
+
+  sav = open (fullpath_sav, O_RDONLY);
+  if (sav < 0)
+    {
+      unlink (fullpath_tmp);
+      free (fullpath_sav);
+      free (fullpath_tmp);
+      return NULL;
+    }
+  
+  while((c = read (sav, buffer, 512)) > 0)
+    write (tmp, buffer, c);
+  
+  close (sav);
+  close (tmp);
+  
+  if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
+    {
+      unlink (fullpath_tmp);
+      free (fullpath_sav);
+      free (fullpath_tmp);
+      return NULL;
+    }
+  
+  if (link (fullpath_tmp, fullpath) == 0)
+    ret = fopen (fullpath, "r");
+
+  unlink (fullpath_tmp);
+  
+  free (fullpath_sav);
+  free (fullpath_tmp);
+  return ret;
+}
+
+/* Read up configuration file from file_name. */
+void
+vty_read_config (char *config_file,
+                 char *config_default_dir)
+{
+  char cwd[MAXPATHLEN];
+  FILE *confp = NULL;
+  char *fullpath;
+  char *tmp = NULL;
+
+  /* If -f flag specified. */
+  if (config_file != NULL)
+    {
+      if (! IS_DIRECTORY_SEP (config_file[0]))
+        {
+          getcwd (cwd, MAXPATHLEN);
+          tmp = XMALLOC (MTYPE_TMP, 
+                             strlen (cwd) + strlen (config_file) + 2);
+          sprintf (tmp, "%s/%s", cwd, config_file);
+          fullpath = tmp;
+        }
+      else
+        fullpath = config_file;
+
+      confp = fopen (fullpath, "r");
+
+      if (confp == NULL)
+        {
+          fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+                   __func__, fullpath, safe_strerror (errno));
+          
+          confp = vty_use_backup_config (fullpath);
+          if (confp)
+            fprintf (stderr, "WARNING: using backup configuration file!\n");
+          else
+            {
+              fprintf (stderr, "can't open configuration file [%s]\n", 
+                      config_file);
+              exit(1);
+            }
+        }
+    }
+  else
+    {
+#ifdef VTYSH
+      int ret;
+      struct stat conf_stat;
+
+      /* !!!!PLEASE LEAVE!!!!
+       * This is NEEDED for use with vtysh -b, or else you can get
+       * a real configuration food fight with a lot garbage in the
+       * merged configuration file it creates coming from the per
+       * daemon configuration files.  This also allows the daemons
+       * to start if there default configuration file is not
+       * present or ignore them, as needed when using vtysh -b to
+       * configure the daemons at boot - MAG
+       */
+
+      /* Stat for vtysh Zebra.conf, if found startup and wait for
+       * boot configuration
+       */
+
+      if ( strstr(config_default_dir, "vtysh") == NULL)
+        {
+          ret = stat (integrate_default, &conf_stat);
+          if (ret >= 0)
+            return;
+        }
+#endif /* VTYSH */
+
+      confp = fopen (config_default_dir, "r");
+      if (confp == NULL)
+        {
+          fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+                   __func__, config_default_dir, safe_strerror (errno));
+          
+          confp = vty_use_backup_config (config_default_dir);
+          if (confp)
+            {
+              fprintf (stderr, "WARNING: using backup configuration file!\n");
+              fullpath = config_default_dir;
+            }
+          else
+            {
+              fprintf (stderr, "can't open configuration file [%s]\n",
+                                config_default_dir);
+                 exit (1);
+            }
+        }      
+      else
+        fullpath = config_default_dir;
+    }
+
+  vty_read_file (confp);
+
+  fclose (confp);
+
+  host_config_set (fullpath);
+  
+  if (tmp)
+    XFREE (MTYPE_TMP, fullpath);
+}
+
+/* Small utility function which output log to the VTY. */
+void
+vty_log (const char *level, const char *proto_str,
+        const char *format, struct timestamp_control *ctl, va_list va)
+{
+  unsigned int i;
+  struct vty *vty;
+  
+  if (!vtyvec)
+    return;
+
+  for (i = 0; i < vector_active (vtyvec); i++)
+    if ((vty = vector_slot (vtyvec, i)) != NULL)
+      if (vty->monitor)
+       {
+         va_list ac;
+         va_copy(ac, va);
+         vty_log_out (vty, level, proto_str, format, ctl, ac);
+         va_end(ac);
+       }
+}
+
+/* Async-signal-safe version of vty_log for fixed strings. */
+void
+vty_log_fixed (char *buf, size_t len)
+{
+  unsigned int i;
+  struct iovec iov[2];
+
+  /* vty may not have been initialised */
+  if (!vtyvec)
+    return;
+  
+  iov[0].iov_base = buf;
+  iov[0].iov_len = len;
+  iov[1].iov_base = (void *)"\r\n";
+  iov[1].iov_len = 2;
+
+  for (i = 0; i < vector_active (vtyvec); i++)
+    {
+      struct vty *vty;
+      if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
+       /* N.B. We don't care about the return code, since process is
+          most likely just about to die anyway. */
+       writev(vty->wfd, iov, 2);
+    }
+}
+
+int
+vty_config_lock (struct vty *vty)
+{
+  if (vty_config == 0)
+    {
+      vty->config = 1;
+      vty_config = 1;
+    }
+  return vty->config;
+}
+
+int
+vty_config_unlock (struct vty *vty)
+{
+  if (vty_config == 1 && vty->config == 1)
+    {
+      vty->config = 0;
+      vty_config = 0;
+    }
+  return vty->config;
+}
+
+/* Master of the threads. */
+static struct thread_master *vty_master;
+
+static void
+vty_event (enum event event, int sock, struct vty *vty)
+{
+  struct thread *vty_serv_thread;
+
+  switch (event)
+    {
+    case VTY_SERV:
+      vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
+      vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
+      break;
+#ifdef VTYSH
+    case VTYSH_SERV:
+      vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
+      vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
+      break;
+    case VTYSH_READ:
+      vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
+      break;
+    case VTYSH_WRITE:
+      vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
+      break;
+#endif /* VTYSH */
+    case VTY_READ:
+      vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
+
+      /* Time out treatment. */
+      if (vty->v_timeout)
+       {
+         if (vty->t_timeout)
+           thread_cancel (vty->t_timeout);
+         vty->t_timeout = 
+           thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
+       }
+      break;
+    case VTY_WRITE:
+      if (! vty->t_write)
+       vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
+      break;
+    case VTY_TIMEOUT_RESET:
+      if (vty->t_timeout)
+       {
+         thread_cancel (vty->t_timeout);
+         vty->t_timeout = NULL;
+       }
+      if (vty->v_timeout)
+       {
+         vty->t_timeout = 
+           thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
+       }
+      break;
+    }
+}
+
+DEFUN (who,
+       who_cmd,
+       "who",
+       "Display who is on vty\n")
+{
+  unsigned int i;
+  struct vty *v;
+
+  for (i = 0; i < vector_active (vtyvec); i++)
+    if ((v = vector_slot (vtyvec, i)) != NULL)
+      vty_out (vty, "%svty[%d] connected from %s.%s",
+              v->config ? "*" : " ",
+              i, v->address, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/* Move to vty configuration mode. */
+DEFUN (line_vty,
+       line_vty_cmd,
+       "line vty",
+       "Configure a terminal line\n"
+       "Virtual terminal\n")
+{
+  vty->node = VTY_NODE;
+  return CMD_SUCCESS;
+}
+
+/* Set time out value. */
+static int
+exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
+{
+  unsigned long timeout = 0;
+
+  /* min_str and sec_str are already checked by parser.  So it must be
+     all digit string. */
+  if (min_str)
+    {
+      timeout = strtol (min_str, NULL, 10);
+      timeout *= 60;
+    }
+  if (sec_str)
+    timeout += strtol (sec_str, NULL, 10);
+
+  vty_timeout_val = timeout;
+  vty->v_timeout = timeout;
+  vty_event (VTY_TIMEOUT_RESET, 0, vty);
+
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (exec_timeout_min,
+       exec_timeout_min_cmd,
+       "exec-timeout <0-35791>",
+       "Set timeout value\n"
+       "Timeout value in minutes\n")
+{
+  return exec_timeout (vty, argv[0], NULL);
+}
+
+DEFUN (exec_timeout_sec,
+       exec_timeout_sec_cmd,
+       "exec-timeout <0-35791> <0-2147483>",
+       "Set the EXEC timeout\n"
+       "Timeout in minutes\n"
+       "Timeout in seconds\n")
+{
+  return exec_timeout (vty, argv[0], argv[1]);
+}
+
+DEFUN (no_exec_timeout,
+       no_exec_timeout_cmd,
+       "no exec-timeout",
+       NO_STR
+       "Set the EXEC timeout\n")
+{
+  return exec_timeout (vty, NULL, NULL);
+}
+
+/* Set vty access class. */
+DEFUN (vty_access_class,
+       vty_access_class_cmd,
+       "access-class WORD",
+       "Filter connections based on an IP access list\n"
+       "IP access list\n")
+{
+  if (vty_accesslist_name)
+    XFREE(MTYPE_VTY, vty_accesslist_name);
+
+  vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+/* Clear vty access class. */
+DEFUN (no_vty_access_class,
+       no_vty_access_class_cmd,
+       "no access-class [WORD]",
+       NO_STR
+       "Filter connections based on an IP access list\n"
+       "IP access list\n")
+{
+  if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
+    {
+      vty_out (vty, "Access-class is not currently applied to vty%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  XFREE(MTYPE_VTY, vty_accesslist_name);
+
+  vty_accesslist_name = NULL;
+
+  return CMD_SUCCESS;
+}
+
+#ifdef HAVE_IPV6
+/* Set vty access class. */
+DEFUN (vty_ipv6_access_class,
+       vty_ipv6_access_class_cmd,
+       "ipv6 access-class WORD",
+       IPV6_STR
+       "Filter connections based on an IP access list\n"
+       "IPv6 access list\n")
+{
+  if (vty_ipv6_accesslist_name)
+    XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
+
+  vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+/* Clear vty access class. */
+DEFUN (no_vty_ipv6_access_class,
+       no_vty_ipv6_access_class_cmd,
+       "no ipv6 access-class [WORD]",
+       NO_STR
+       IPV6_STR
+       "Filter connections based on an IP access list\n"
+       "IPv6 access list\n")
+{
+  if (! vty_ipv6_accesslist_name ||
+      (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
+    {
+      vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
+
+  vty_ipv6_accesslist_name = NULL;
+
+  return CMD_SUCCESS;
+}
+#endif /* HAVE_IPV6 */
+
+/* vty login. */
+DEFUN (vty_login,
+       vty_login_cmd,
+       "login",
+       "Enable password checking\n")
+{
+  no_password_check = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_vty_login,
+       no_vty_login_cmd,
+       "no login",
+       NO_STR
+       "Enable password checking\n")
+{
+  no_password_check = 1;
+  return CMD_SUCCESS;
+}
+
+/* initial mode. */
+DEFUN (vty_restricted_mode,
+       vty_restricted_mode_cmd,
+       "anonymous restricted",
+       "Restrict view commands available in anonymous, unauthenticated vty\n")
+{
+  restricted_mode = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (vty_no_restricted_mode,
+       vty_no_restricted_mode_cmd,
+       "no anonymous restricted",
+       NO_STR
+       "Enable password checking\n")
+{
+  restricted_mode = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (service_advanced_vty,
+       service_advanced_vty_cmd,
+       "service advanced-vty",
+       "Set up miscellaneous service\n"
+       "Enable advanced mode vty interface\n")
+{
+  host.advanced = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_service_advanced_vty,
+       no_service_advanced_vty_cmd,
+       "no service advanced-vty",
+       NO_STR
+       "Set up miscellaneous service\n"
+       "Enable advanced mode vty interface\n")
+{
+  host.advanced = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (terminal_monitor,
+       terminal_monitor_cmd,
+       "terminal monitor",
+       "Set terminal line parameters\n"
+       "Copy debug output to the current terminal line\n")
+{
+  vty->monitor = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (terminal_no_monitor,
+       terminal_no_monitor_cmd,
+       "terminal no monitor",
+       "Set terminal line parameters\n"
+       NO_STR
+       "Copy debug output to the current terminal line\n")
+{
+  vty->monitor = 0;
+  return CMD_SUCCESS;
+}
+
+ALIAS (terminal_no_monitor,
+       no_terminal_monitor_cmd,
+       "no terminal monitor",
+       NO_STR
+       "Set terminal line parameters\n"
+       "Copy debug output to the current terminal line\n")
+
+DEFUN (show_history,
+       show_history_cmd,
+       "show history",
+       SHOW_STR
+       "Display the session command history\n")
+{
+  int index;
+
+  for (index = vty->hindex + 1; index != vty->hindex;)
+    {
+      if (index == VTY_MAXHIST)
+       {
+         index = 0;
+         continue;
+       }
+
+      if (vty->hist[index] != NULL)
+       vty_out (vty, "  %s%s", vty->hist[index], VTY_NEWLINE);
+
+      index++;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* vty login. */
+DEFUN (log_commands,
+       log_commands_cmd,
+       "log commands",
+       "Logging control\n"
+       "Log all commands (can't be unset without restart)\n")
+{
+  do_log_commands = 1;
+  return CMD_SUCCESS;
+}
+
+/* Display current configuration. */
+static int
+vty_config_write (struct vty *vty)
+{
+  vty_out (vty, "line vty%s", VTY_NEWLINE);
+
+  if (vty_accesslist_name)
+    vty_out (vty, " access-class %s%s",
+            vty_accesslist_name, VTY_NEWLINE);
+
+  if (vty_ipv6_accesslist_name)
+    vty_out (vty, " ipv6 access-class %s%s",
+            vty_ipv6_accesslist_name, VTY_NEWLINE);
+
+  /* exec-timeout */
+  if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
+    vty_out (vty, " exec-timeout %ld %ld%s", 
+            vty_timeout_val / 60,
+            vty_timeout_val % 60, VTY_NEWLINE);
+
+  /* login */
+  if (no_password_check)
+    vty_out (vty, " no login%s", VTY_NEWLINE);
+    
+  if (restricted_mode != restricted_mode_default)
+    {
+      if (restricted_mode_default)
+        vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
+      else
+        vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
+    }
+  
+  if (do_log_commands)
+    vty_out (vty, "log commands%s", VTY_NEWLINE);
+     
+  vty_out (vty, "!%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+struct cmd_node vty_node =
+{
+  VTY_NODE,
+  "%s(config-line)# ",
+  1,
+};
+
+/* Reset all VTY status. */
+void
+vty_reset ()
+{
+  unsigned int i;
+  struct vty *vty;
+  struct thread *vty_serv_thread;
+
+  for (i = 0; i < vector_active (vtyvec); i++)
+    if ((vty = vector_slot (vtyvec, i)) != NULL)
+      {
+       buffer_reset (vty->obuf);
+       vty->status = VTY_CLOSE;
+       vty_close (vty);
+      }
+
+  for (i = 0; i < vector_active (Vvty_serv_thread); i++)
+    if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
+      {
+       thread_cancel (vty_serv_thread);
+       vector_slot (Vvty_serv_thread, i) = NULL;
+        close (i);
+      }
+
+  vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+
+  if (vty_accesslist_name)
+    {
+      XFREE(MTYPE_VTY, vty_accesslist_name);
+      vty_accesslist_name = NULL;
+    }
+
+  if (vty_ipv6_accesslist_name)
+    {
+      XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
+      vty_ipv6_accesslist_name = NULL;
+    }
+}
+
+static void
+vty_save_cwd (void)
+{
+  char cwd[MAXPATHLEN];
+  char *c;
+
+  c = getcwd (cwd, MAXPATHLEN);
+
+  if (!c)
+    {
+      chdir (SYSCONFDIR);
+      getcwd (cwd, MAXPATHLEN);
+    }
+
+  vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
+  strcpy (vty_cwd, cwd);
+}
+
+char *
+vty_get_cwd ()
+{
+  return vty_cwd;
+}
+
+int
+vty_shell (struct vty *vty)
+{
+  return vty->type == VTY_SHELL ? 1 : 0;
+}
+
+int
+vty_shell_serv (struct vty *vty)
+{
+  return vty->type == VTY_SHELL_SERV ? 1 : 0;
+}
+
+void
+vty_init_vtysh ()
+{
+  vtyvec = vector_init (VECTOR_MIN_SIZE);
+}
+
+/* Install vty's own commands like `who' command. */
+void
+vty_init (struct thread_master *master_thread)
+{
+  /* For further configuration read, preserve current directory. */
+  vty_save_cwd ();
+
+  vtyvec = vector_init (VECTOR_MIN_SIZE);
+
+  vty_master = master_thread;
+
+  atexit (vty_stdio_reset);
+
+  /* Initilize server thread vector. */
+  Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
+
+  /* Install bgp top node. */
+  install_node (&vty_node, vty_config_write);
+
+  install_element (RESTRICTED_NODE, &who_cmd);
+  install_element (RESTRICTED_NODE, &show_history_cmd);
+  install_element (VIEW_NODE, &who_cmd);
+  install_element (VIEW_NODE, &show_history_cmd);
+  install_element (CONFIG_NODE, &line_vty_cmd);
+  install_element (CONFIG_NODE, &service_advanced_vty_cmd);
+  install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
+  install_element (CONFIG_NODE, &show_history_cmd);
+  install_element (CONFIG_NODE, &log_commands_cmd);
+  install_element (ENABLE_NODE, &terminal_monitor_cmd);
+  install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
+  install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
+
+  install_default (VTY_NODE);
+  install_element (VTY_NODE, &exec_timeout_min_cmd);
+  install_element (VTY_NODE, &exec_timeout_sec_cmd);
+  install_element (VTY_NODE, &no_exec_timeout_cmd);
+  install_element (VTY_NODE, &vty_access_class_cmd);
+  install_element (VTY_NODE, &no_vty_access_class_cmd);
+  install_element (VTY_NODE, &vty_login_cmd);
+  install_element (VTY_NODE, &no_vty_login_cmd);
+  install_element (VTY_NODE, &vty_restricted_mode_cmd);
+  install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
+#ifdef HAVE_IPV6
+  install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
+  install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
+#endif /* HAVE_IPV6 */
+}
+
+void
+vty_terminate (void)
+{
+  if (vty_cwd)
+    XFREE (MTYPE_TMP, vty_cwd);
+
+  if (vtyvec && Vvty_serv_thread)
+    {
+      vty_reset ();
+      vector_free (vtyvec);
+      vector_free (Vvty_serv_thread);
+    }
+}
diff --git a/lib/vty.h b/lib/vty.h
new file mode 100644 (file)
index 0000000..1e3b124
--- /dev/null
+++ b/lib/vty.h
@@ -0,0 +1,258 @@
+/* Virtual terminal [aka TeletYpe] interface routine
+   Copyright (C) 1997 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _ZEBRA_VTY_H
+#define _ZEBRA_VTY_H
+
+#include "thread.h"
+#include "log.h"
+#include "sockunion.h"
+
+#define VTY_MAXHIST 20
+
+/* VTY struct. */
+struct vty 
+{
+  /* File descripter of this vty. */
+  int fd;
+
+  /* output FD, to support stdin/stdout combination */
+  int wfd;
+
+  /* Is this vty connect to file or not */
+  enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} type;
+
+  /* Node status of this vty */
+  int node;
+
+  /* Failure count */
+  int fail;
+
+  /* Output buffer. */
+  struct buffer *obuf;
+
+  /* Command input buffer */
+  char *buf;
+
+  /* Command cursor point */
+  int cp;
+
+  /* Command length */
+  int length;
+
+  /* Command max length. */
+  int max;
+
+  /* Histry of command */
+  char *hist[VTY_MAXHIST];
+
+  /* History lookup current point */
+  int hp;
+
+  /* History insert end point */
+  int hindex;
+
+  /* For current referencing point of interface, route-map,
+     access-list etc... */
+  void *index;
+
+  /* For multiple level index treatment such as key chain and key. */
+  void *index_sub;
+
+  /* For escape character. */
+  unsigned char escape;
+
+  /* Current vty status. */
+  enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status;
+
+  /* IAC handling: was the last character received the
+     IAC (interpret-as-command) escape character (and therefore the next
+     character will be the command code)?  Refer to Telnet RFC 854. */
+  unsigned char iac;
+
+  /* IAC SB (option subnegotiation) handling */
+  unsigned char iac_sb_in_progress;
+  /* At the moment, we care only about the NAWS (window size) negotiation,
+     and that requires just a 5-character buffer (RFC 1073):
+       <NAWS char> <16-bit width> <16-bit height> */
+#define TELNET_NAWS_SB_LEN 5
+  unsigned char sb_buf[TELNET_NAWS_SB_LEN];
+  /* How many subnegotiation characters have we received?  We just drop
+     those that do not fit in the buffer. */
+  size_t sb_len;
+
+  /* Window width/height. */
+  int width;
+  int height;
+
+  /* Configure lines. */
+  int lines;
+
+  /* Terminal monitor. */
+  int monitor;
+
+  /* In configure mode. */
+  int config;
+
+  /* Read and write thread. */
+  struct thread *t_read;
+  struct thread *t_write;
+
+  /* Timeout seconds and thread. */
+  unsigned long v_timeout;
+  struct thread *t_timeout;
+
+  /* What address is this vty comming from. */
+  char address[SU_ADDRSTRLEN];
+};
+
+/* Integrated configuration file. */
+#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
+
+/* Small macro to determine newline is newline only or linefeed needed. */
+#define VTY_NEWLINE  ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+
+/* Default time out value */
+#define VTY_TIMEOUT_DEFAULT 600
+
+/* Vty read buffer size. */
+#define VTY_READ_BUFSIZ 512
+
+/* Directory separator. */
+#ifndef DIRECTORY_SEP
+#define DIRECTORY_SEP '/'
+#endif /* DIRECTORY_SEP */
+
+#ifndef IS_DIRECTORY_SEP
+#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP)
+#endif
+
+/* GCC have printf type attribute check.  */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/* Utility macros to convert VTY argument to unsigned long */
+#define VTY_GET_ULONG(NAME,V,STR) \
+do { \
+  char *endptr = NULL; \
+  errno = 0; \
+  (V) = strtoul ((STR), &endptr, 10); \
+  if (*(STR) == '-' || *endptr != '\0' || errno) \
+    { \
+      vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \
+      return CMD_WARNING; \
+    } \
+} while (0)
+
+/*
+ * The logic below ((TMPL) <= ((MIN) && (TMPL) != (MIN)) is
+ * done to circumvent the compiler complaining about
+ * comparing unsigned numbers against zero, if MIN is zero.
+ * NB: The compiler isn't smart enough to supress the warning
+ * if you write (MIN) != 0 && tmpl < (MIN).
+ */
+#define VTY_GET_INTEGER_RANGE_HEART(NAME,TMPL,STR,MIN,MAX)      \
+do {                                                            \
+  VTY_GET_ULONG(NAME, (TMPL), STR);                             \
+  if ( ((TMPL) <= (MIN) && (TMPL) != (MIN)) || (TMPL) > (MAX) ) \
+    {                                                           \
+      vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE);\
+      return CMD_WARNING;                                       \
+    }                                                           \
+} while (0)
+
+#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX)               \
+do {                                                            \
+  unsigned long tmpl;                                           \
+  VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX);           \
+  (V) = tmpl;                                                   \
+} while (0)
+
+#define VTY_CHECK_INTEGER_RANGE(NAME,STR,MIN,MAX)               \
+do {                                                            \
+  unsigned long tmpl;                                           \
+  VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX);           \
+} while (0)
+
+#define VTY_GET_INTEGER(NAME,V,STR)                             \
+    VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX)
+
+#define VTY_GET_IPV4_ADDRESS(NAME,V,STR)                                      \
+do {                                                                             \
+  int retv;                                                                   \
+  retv = inet_aton ((STR), &(V));                                             \
+  if (!retv)                                                                  \
+    {                                                                         \
+      vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE);              \
+      return CMD_WARNING;                                                     \
+    }                                                                         \
+} while (0)
+
+#define VTY_GET_IPV4_PREFIX(NAME,V,STR)                                       \
+do {                                                                             \
+  int retv;                                                                   \
+  retv = str2prefix_ipv4 ((STR), &(V));                                       \
+  if (retv <= 0)                                                              \
+    {                                                                         \
+      vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE);              \
+      return CMD_WARNING;                                                     \
+    }                                                                         \
+} while (0)
+
+#define VTY_WARN_EXPERIMENTAL()                                               \
+do {                                                                          \
+  vty_out (vty, "%% WARNING: this command is experimental. Both its name and" \
+                " parameters may%s%% change in a future version of Quagga,"   \
+                " possibly breaking your configuration!%s",                   \
+                VTY_NEWLINE, VTY_NEWLINE);                                    \
+} while (0)
+
+/* Exported variables */
+extern char integrate_default[];
+
+/* Prototypes. */
+extern void vty_init (struct thread_master *);
+extern void vty_init_vtysh (void);
+extern void vty_terminate (void);
+extern void vty_reset (void);
+extern struct vty *vty_new (void);
+extern struct vty *vty_stdio (void (*atclose)(void));
+extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
+extern void vty_read_config (char *, char *);
+extern void vty_time_print (struct vty *, int);
+extern void vty_serv_sock (const char *, unsigned short, const char *);
+extern void vty_close (struct vty *);
+extern char *vty_get_cwd (void);
+extern void vty_log (const char *level, const char *proto, 
+                     const char *fmt, struct timestamp_control *, va_list);
+extern int vty_config_lock (struct vty *);
+extern int vty_config_unlock (struct vty *);
+extern int vty_shell (struct vty *);
+extern int vty_shell_serv (struct vty *);
+extern void vty_hello (struct vty *);
+
+/* Send a fixed-size message to all vty terminal monitors; this should be
+   an async-signal-safe function. */
+extern void vty_log_fixed (char *buf, size_t len);
+
+#endif /* _ZEBRA_VTY_H */
diff --git a/lib/workqueue.c b/lib/workqueue.c
new file mode 100644 (file)
index 0000000..6453e7b
--- /dev/null
@@ -0,0 +1,408 @@
+/* 
+ * Quagga Work Queue Support.
+ *
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * 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.  
+ */
+
+#include <zebra.h>
+#include "thread.h"
+#include "memory.h"
+#include "workqueue.h"
+#include "linklist.h"
+#include "command.h"
+#include "log.h"
+
+/* master list of work_queues */
+static struct list _work_queues;
+/* pointer primarily to avoid an otherwise harmless warning on
+ * ALL_LIST_ELEMENTS_RO 
+ */
+static struct list *work_queues = &_work_queues;
+
+#define WORK_QUEUE_MIN_GRANULARITY 1
+
+static struct work_queue_item *
+work_queue_item_new (struct work_queue *wq)
+{
+  struct work_queue_item *item;
+  assert (wq);
+
+  item = XCALLOC (MTYPE_WORK_QUEUE_ITEM, 
+                  sizeof (struct work_queue_item));
+  
+  return item;
+}
+
+static void
+work_queue_item_free (struct work_queue_item *item)
+{
+  XFREE (MTYPE_WORK_QUEUE_ITEM, item);
+  return;
+}
+
+/* create new work queue */
+struct work_queue *
+work_queue_new (struct thread_master *m, const char *queue_name)
+{
+  struct work_queue *new;
+  
+  new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct work_queue));
+
+  if (new == NULL)
+    return new;
+  
+  new->name = XSTRDUP (MTYPE_WORK_QUEUE_NAME, queue_name);
+  new->master = m;
+  SET_FLAG (new->flags, WQ_UNPLUGGED);
+  
+  if ( (new->items = list_new ()) == NULL)
+    {
+      XFREE (MTYPE_WORK_QUEUE_NAME, new->name);
+      XFREE (MTYPE_WORK_QUEUE, new);
+      
+      return NULL;
+    }
+  
+  new->items->del = (void (*)(void *)) work_queue_item_free;  
+  
+  listnode_add (work_queues, new);
+  
+  new->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY;
+  new->cycles.worst = UINT_MAX;
+  
+  /* Default values, can be overriden by caller */
+  new->spec.hold = WORK_QUEUE_DEFAULT_HOLD;
+    
+  return new;
+}
+
+void
+work_queue_free (struct work_queue *wq)
+{
+  if (wq->thread != NULL)
+    thread_cancel(wq->thread);
+  
+  /* list_delete frees items via callback */
+  list_delete (wq->items);
+  listnode_delete (work_queues, wq);
+  
+  XFREE (MTYPE_WORK_QUEUE_NAME, wq->name);
+  XFREE (MTYPE_WORK_QUEUE, wq);
+  return;
+}
+
+bool
+work_queue_is_scheduled (struct work_queue *wq)
+{
+  return (wq->thread != NULL);
+}
+
+static int
+work_queue_schedule (struct work_queue *wq, unsigned int delay)
+{
+  /* if appropriate, schedule work queue thread */
+  if ( CHECK_FLAG (wq->flags, WQ_UNPLUGGED)
+       && (wq->thread == NULL)
+       && (listcount (wq->items) > 0) )
+    {
+      wq->thread = thread_add_background (wq->master, work_queue_run, 
+                                          wq, delay);
+      return 1;
+    }
+  else
+    return 0;
+}
+  
+void
+work_queue_add (struct work_queue *wq, void *data)
+{
+  struct work_queue_item *item;
+  
+  assert (wq);
+
+  if (!(item = work_queue_item_new (wq)))
+    {
+      zlog_err ("%s: unable to get new queue item", __func__);
+      return;
+    }
+  
+  item->data = data;
+  listnode_add (wq->items, item);
+  
+  work_queue_schedule (wq, wq->spec.hold);
+  
+  return;
+}
+
+static void
+work_queue_item_remove (struct work_queue *wq, struct listnode *ln)
+{
+  struct work_queue_item *item = listgetdata (ln);
+
+  assert (item && item->data);
+
+  /* call private data deletion callback if needed */  
+  if (wq->spec.del_item_data)
+    wq->spec.del_item_data (wq, item->data);
+
+  list_delete_node (wq->items, ln);
+  work_queue_item_free (item);
+  
+  return;
+}
+
+static void
+work_queue_item_requeue (struct work_queue *wq, struct listnode *ln)
+{
+  LISTNODE_DETACH (wq->items, ln);
+  LISTNODE_ATTACH (wq->items, ln); /* attach to end of list */
+}
+
+DEFUN(show_work_queues,
+      show_work_queues_cmd,
+      "show work-queues",
+      SHOW_STR
+      "Work Queue information\n")
+{
+  struct listnode *node;
+  struct work_queue *wq;
+  
+  vty_out (vty, 
+           "%c %8s %5s %8s %21s %6s %5s%s",
+           ' ', "List","(ms) ","Q. Runs","Cycle Counts   ",
+           " ","Worst",
+           VTY_NEWLINE);
+  vty_out (vty,
+           "%c %8s %5s %8s %7s %6s %6s %6s %5s %s%s",
+           'P',
+           "Items",
+           "Hold",
+           "Total",
+           "Best","Worst","Gran.","Avg.", "Lat.",
+           "Name", 
+           VTY_NEWLINE);
+  for (ALL_LIST_ELEMENTS_RO (work_queues, node, wq))
+    {
+      vty_out (vty,"%c %8u %5u %8lu %7u %6u %6u %6u %5lu %s%s",
+               (CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'),
+               listcount (wq->items),
+               wq->spec.hold,
+               wq->runs,
+               wq->cycles.best, 
+               MIN(wq->cycles.best, wq->cycles.worst),
+               wq->cycles.granularity,
+                 (wq->runs) ? 
+                   (unsigned int) (wq->cycles.total / wq->runs) : 0,
+               wq->worst_usec,
+               wq->name,
+               VTY_NEWLINE);
+    }
+    
+  return CMD_SUCCESS;
+}
+
+/* 'plug' a queue: Stop it from being scheduled,
+ * ie: prevent the queue from draining.
+ */
+void
+work_queue_plug (struct work_queue *wq)
+{
+  if (wq->thread)
+    thread_cancel (wq->thread);
+  
+  wq->thread = NULL;
+  
+  UNSET_FLAG (wq->flags, WQ_UNPLUGGED);
+}
+
+/* unplug queue, schedule it again, if appropriate
+ * Ie: Allow the queue to be drained again
+ */
+void
+work_queue_unplug (struct work_queue *wq)
+{
+  SET_FLAG (wq->flags, WQ_UNPLUGGED);
+
+  /* if thread isnt already waiting, add one */
+  work_queue_schedule (wq, wq->spec.hold);
+}
+
+/* timer thread to process a work queue
+ * will reschedule itself if required,
+ * otherwise work_queue_item_add 
+ */
+int
+work_queue_run (struct thread *thread)
+{
+  struct work_queue *wq;
+  struct work_queue_item *item;
+  unsigned long took;
+  wq_item_status ret;
+  unsigned int cycles = 0;
+  struct listnode *node, *nnode;
+  char yielded = 0;
+
+  wq = THREAD_ARG (thread);
+  wq->thread = NULL;
+
+  assert (wq && wq->items);
+
+  /* calculate cycle granularity:
+   * list iteration == 1 cycle
+   * granularity == # cycles between checks whether we should yield.
+   *
+   * granularity should be > 0, and can increase slowly after each run to
+   * provide some hysteris, but not past cycles.best or 2*cycles.
+   *
+   * Best: starts low, can only increase
+   *
+   * Worst: starts at MAX, can only decrease.
+   *
+   * Granularity: starts at WORK_QUEUE_MIN_GRANULARITY, can be decreased 
+   *              if we run to end of time slot, can increase otherwise 
+   *              by a small factor.
+   *
+   * We could use just the average and save some work, however we want to be
+   * able to adjust quickly to CPU pressure. Average wont shift much if
+   * daemon has been running a long time.
+   */
+   if (wq->cycles.granularity == 0)
+     wq->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY;
+
+  for (ALL_LIST_ELEMENTS (wq->items, node, nnode, item))
+  {
+    assert (item && item->data);
+    
+    /* dont run items which are past their allowed retries */
+    if (item->ran > wq->spec.max_retries)
+      {
+        /* run error handler, if any */
+       if (wq->spec.errorfunc)
+         wq->spec.errorfunc (wq, item->data);
+       work_queue_item_remove (wq, node);
+       continue;
+      }
+
+    /* run and take care of items that want to be retried immediately */
+    do
+      {
+        ret = wq->spec.workfunc (wq, item->data);
+        item->ran++;
+      }
+    while ((ret == WQ_RETRY_NOW) 
+           && (item->ran < wq->spec.max_retries));
+
+    switch (ret)
+      {
+      case WQ_QUEUE_BLOCKED:
+        {
+          /* decrement item->ran again, cause this isn't an item
+           * specific error, and fall through to WQ_RETRY_LATER
+           */
+          item->ran--;
+        }
+      case WQ_RETRY_LATER:
+       {
+         goto stats;
+       }
+      case WQ_REQUEUE:
+       {
+         item->ran--;
+         work_queue_item_requeue (wq, node);
+         break;
+       }
+      case WQ_RETRY_NOW:
+        /* a RETRY_NOW that gets here has exceeded max_tries, same as ERROR */
+      case WQ_ERROR:
+       {
+         if (wq->spec.errorfunc)
+           wq->spec.errorfunc (wq, item);
+       }
+       /* fall through here is deliberate */
+      case WQ_SUCCESS:
+      default:
+       {
+         work_queue_item_remove (wq, node);
+         break;
+       }
+      }
+
+    /* completed cycle */
+    cycles++;
+
+    /* test if we should yield */
+    if ( !(cycles % wq->cycles.granularity) 
+        && (took = thread_should_yield (thread)))
+      {
+        yielded = 1;
+        goto stats;
+      }
+  }
+
+stats:
+
+#define WQ_HYSTERESIS_FACTOR 4
+
+  if (cycles > wq->cycles.best)
+    wq->cycles.best = cycles;
+  
+  if (took > wq->worst_usec)
+    wq->worst_usec = took;
+    
+  /* we yielded, check whether granularity should be reduced */
+  if (yielded && (cycles < wq->cycles.granularity))
+    {
+      wq->cycles.granularity = ((cycles > 0) ? cycles 
+                                             : WORK_QUEUE_MIN_GRANULARITY);
+      if (cycles < wq->cycles.worst)
+        wq->cycles.worst = cycles;
+    }
+  /* otherwise, should granularity increase? */
+  else if (cycles >= (wq->cycles.granularity))
+    {
+      /* along with yielded check, provides hysteresis for granularity */      
+      if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR
+                                           * WQ_HYSTERESIS_FACTOR))
+        wq->cycles.granularity *= WQ_HYSTERESIS_FACTOR; /* quick ramp-up */
+      else if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR))
+        wq->cycles.granularity += WQ_HYSTERESIS_FACTOR;
+        
+      /* clamp granularity down to the worst yielded cycle count */
+      wq->cycles.granularity = MIN(wq->cycles.granularity, wq->cycles.worst);
+    }
+#undef WQ_HYSTERIS_FACTOR
+  
+  wq->runs++;
+  wq->cycles.total += cycles;
+
+#if 0
+  printf ("%s: cycles %d, new: best %d, worst %d\n",
+            __func__, cycles, wq->cycles.best, wq->cycles.granularity);
+#endif
+  
+  /* Is the queue done yet? If it is, call the completion callback. */
+  if (listcount (wq->items) > 0)
+    work_queue_schedule (wq, 0);
+  else if (wq->spec.completion_func)
+    wq->spec.completion_func (wq);
+  
+  return 0;
+}
diff --git a/lib/workqueue.h b/lib/workqueue.h
new file mode 100644 (file)
index 0000000..aac7860
--- /dev/null
@@ -0,0 +1,129 @@
+/* 
+ * Quagga Work Queues.
+ *
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _QUAGGA_WORK_QUEUE_H
+#define _QUAGGA_WORK_QUEUE_H
+
+/* Hold time for the initial schedule of a queue run, in  millisec */
+#define WORK_QUEUE_DEFAULT_HOLD  50 
+
+/* action value, for use by item processor and item error handlers */
+typedef enum
+{
+  WQ_SUCCESS = 0,
+  WQ_ERROR,             /* Error, run error handler if provided */
+  WQ_RETRY_NOW,         /* retry immediately */
+  WQ_RETRY_LATER,       /* retry later, cease processing work queue */
+  WQ_REQUEUE,          /* requeue item, continue processing work queue */
+  WQ_QUEUE_BLOCKED,    /* Queue cant be processed at this time.
+                         * Similar to WQ_RETRY_LATER, but doesn't penalise
+                         * the particular item.. */
+} wq_item_status;
+
+/* A single work queue item, unsurprisingly */
+struct work_queue_item
+{
+  void *data;                           /* opaque data */
+  unsigned short ran;                  /* # of times item has been run */
+};
+
+#define WQ_UNPLUGGED   (1 << 0) /* available for draining */
+
+struct work_queue
+{
+  /* Everything but the specification struct is private
+   * the following may be read
+   */
+  struct thread_master *master;       /* thread master */
+  struct thread *thread;              /* thread, if one is active */
+  char *name;                         /* work queue name */
+  
+  /* Specification for this work queue.
+   * Public, must be set before use by caller. May be modified at will.
+   */
+  struct {
+    /* optional opaque user data, global to the queue. */
+    void *data;
+    
+    /* work function to process items with:
+     * First argument is the workqueue queue.
+     * Second argument is the item data
+     */
+    wq_item_status (*workfunc) (struct work_queue *, void *);
+
+    /* error handling function, optional */
+    void (*errorfunc) (struct work_queue *, struct work_queue_item *);
+    
+    /* callback to delete user specific item data */
+    void (*del_item_data) (struct work_queue *, void *);
+    
+    /* completion callback, called when queue is emptied, optional */
+    void (*completion_func) (struct work_queue *);
+    
+    /* max number of retries to make for item that errors */
+    unsigned int max_retries;  
+
+    unsigned int hold; /* hold time for first run, in ms */
+  } spec;
+  
+  /* remaining fields should be opaque to users */
+  struct list *items;                 /* queue item list */
+  unsigned long runs;                 /* runs count */
+  unsigned long worst_usec;
+  
+  struct {
+    unsigned int best;
+    unsigned int worst;
+    unsigned int granularity;
+    unsigned long total;
+  } cycles;    /* cycle counts */
+  
+  /* private state */
+  u_int16_t flags;             /* user set flag */
+};
+
+/* User API */
+
+/* create a new work queue, of given name. 
+ * user must fill in the spec of the returned work queue before adding
+ * anything to it
+ */
+extern struct work_queue *work_queue_new (struct thread_master *,
+                                          const char *);
+/* destroy work queue */
+extern void work_queue_free (struct work_queue *);
+
+/* Add the supplied data as an item onto the workqueue */
+extern void work_queue_add (struct work_queue *, void *);
+
+/* plug the queue, ie prevent it from being drained / processed */
+extern void work_queue_plug (struct work_queue *wq);
+/* unplug the queue, allow it to be drained again */
+extern void work_queue_unplug (struct work_queue *wq);
+
+bool work_queue_is_scheduled (struct work_queue *);
+
+/* Helpers, exported for thread.c and command.c */
+extern int work_queue_run (struct thread *);
+extern struct cmd_element show_work_queues_cmd;
+#endif /* _QUAGGA_WORK_QUEUE_H */
diff --git a/lib/zassert.h b/lib/zassert.h
new file mode 100644 (file)
index 0000000..bf0a851
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * $Id: zassert.h,v 1.2 2004/12/03 18:01:04 ajs Exp $
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _QUAGGA_ASSERT_H
+#define _QUAGGA_ASSERT_H
+
+extern void _zlog_assert_failed (const char *assertion, const char *file,
+                                unsigned int line, const char *function)
+                                __attribute__ ((noreturn));
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define __ASSERT_FUNCTION    __func__
+#elif defined(__GNUC__)
+#define __ASSERT_FUNCTION    __FUNCTION__
+#else
+#define __ASSERT_FUNCTION    NULL
+#endif
+
+#define zassert(EX) ((void)((EX) ?  0 :        \
+                           (_zlog_assert_failed(#EX, __FILE__, __LINE__, \
+                                                __ASSERT_FUNCTION), 0)))
+
+#undef assert
+#define assert(EX) zassert(EX)
+
+#endif /* _QUAGGA_ASSERT_H */
diff --git a/lib/zclient.c b/lib/zclient.c
new file mode 100644 (file)
index 0000000..a32faaa
--- /dev/null
@@ -0,0 +1,1311 @@
+/* Zebra's client library.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ * Copyright (C) 2005 Andrew J. Schorr
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "stream.h"
+#include "buffer.h"
+#include "network.h"
+#include "if.h"
+#include "log.h"
+#include "thread.h"
+#include "zclient.h"
+#include "memory.h"
+#include "table.h"
+
+/* Zebra client events. */
+enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT};
+
+/* Prototype for event manager. */
+static void zclient_event (enum event, struct zclient *);
+
+const char *zclient_serv_path = NULL;
+
+/* This file local debug flag. */
+int zclient_debug = 0;
+
+/* Allocate zclient structure. */
+struct zclient *
+zclient_new (struct thread_master *master)
+{
+  struct zclient *zclient;
+  zclient = XCALLOC (MTYPE_ZCLIENT, sizeof (struct zclient));
+
+  zclient->ibuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
+  zclient->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
+  zclient->wb = buffer_new(0);
+  zclient->master = master;
+
+  return zclient;
+}
+
+/* This function is only called when exiting, because
+   many parts of the code do not check for I/O errors, so they could
+   reference an invalid pointer if the structure was ever freed.
+
+   Free zclient structure. */
+void
+zclient_free (struct zclient *zclient)
+{
+  if (zclient->ibuf)
+    stream_free(zclient->ibuf);
+  if (zclient->obuf)
+    stream_free(zclient->obuf);
+  if (zclient->wb)
+    buffer_free(zclient->wb);
+
+  XFREE (MTYPE_ZCLIENT, zclient);
+}
+
+/* Initialize zebra client.  Argument redist_default is unwanted
+   redistribute route type. */
+void
+zclient_init (struct zclient *zclient, int redist_default)
+{
+  int i;
+  
+  /* Enable zebra client connection by default. */
+  zclient->enable = 1;
+
+  /* Set -1 to the default socket value. */
+  zclient->sock = -1;
+
+  /* Clear redistribution flags. */
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    zclient->redist[i] = vrf_bitmap_init ();
+
+  /* Set unwanted redistribute route.  bgpd does not need BGP route
+     redistribution. */
+  zclient->redist_default = redist_default;
+
+  /* Set default-information redistribute to zero. */
+  zclient->default_information = vrf_bitmap_init ();
+
+  /* Schedule first zclient connection. */
+  if (zclient_debug)
+    zlog_debug ("zclient start scheduled");
+
+  zclient_event (ZCLIENT_SCHEDULE, zclient);
+}
+
+/* Stop zebra client services. */
+void
+zclient_stop (struct zclient *zclient)
+{
+  int i;
+
+  if (zclient_debug)
+    zlog_debug ("zclient stopped");
+
+  /* Stop threads. */
+  THREAD_OFF(zclient->t_read);
+  THREAD_OFF(zclient->t_connect);
+  THREAD_OFF(zclient->t_write);
+
+  /* Reset streams. */
+  stream_reset(zclient->ibuf);
+  stream_reset(zclient->obuf);
+
+  /* Empty the write buffer. */
+  buffer_reset(zclient->wb);
+
+  /* Close socket. */
+  if (zclient->sock >= 0)
+    {
+      close (zclient->sock);
+      zclient->sock = -1;
+    }
+  zclient->fail = 0;
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    {
+      vrf_bitmap_free(zclient->redist[i]);
+      zclient->redist[i] = VRF_BITMAP_NULL;
+    }
+  vrf_bitmap_free(zclient->default_information);
+  zclient->default_information = VRF_BITMAP_NULL;
+}
+
+void
+zclient_reset (struct zclient *zclient)
+{
+  zclient_stop (zclient);
+  zclient_init (zclient, zclient->redist_default);
+}
+
+#ifdef HAVE_TCP_ZEBRA
+
+/* Make socket to zebra daemon. Return zebra socket. */
+static int
+zclient_socket(void)
+{
+  int sock;
+  int ret;
+  struct sockaddr_in serv;
+
+  /* We should think about IPv6 connection. */
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+  
+  /* Make server socket. */ 
+  memset (&serv, 0, sizeof (struct sockaddr_in));
+  serv.sin_family = AF_INET;
+  serv.sin_port = htons (ZEBRA_PORT);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  serv.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+  /* Connect to zebra. */
+  ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv));
+  if (ret < 0)
+    {
+      zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno);
+      close (sock);
+      return -1;
+    }
+  return sock;
+}
+
+#else
+
+/* For sockaddr_un. */
+#include <sys/un.h>
+
+static int
+zclient_socket_un (const char *path)
+{
+  int ret;
+  int sock, len;
+  struct sockaddr_un addr;
+
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+  
+  /* Make server socket. */ 
+  memset (&addr, 0, sizeof (struct sockaddr_un));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, path, strlen (path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+  len = addr.sun_len = SUN_LEN(&addr);
+#else
+  len = sizeof (addr.sun_family) + strlen (addr.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+  ret = connect (sock, (struct sockaddr *) &addr, len);
+  if (ret < 0)
+    {
+      zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno);
+      close (sock);
+      return -1;
+    }
+  return sock;
+}
+
+#endif /* HAVE_TCP_ZEBRA */
+
+/**
+ * Connect to zebra daemon.
+ * @param zclient a pointer to zclient structure
+ * @return socket fd just to make sure that connection established
+ * @see zclient_init
+ * @see zclient_new
+ */
+int
+zclient_socket_connect (struct zclient *zclient)
+{
+#ifdef HAVE_TCP_ZEBRA
+  zclient->sock = zclient_socket ();
+#else
+  zclient->sock = zclient_socket_un (zclient_serv_path_get());
+#endif
+  return zclient->sock;
+}
+
+static int
+zclient_failed(struct zclient *zclient)
+{
+  zclient->fail++;
+  zclient_stop(zclient);
+  zclient_event(ZCLIENT_CONNECT, zclient);
+  return -1;
+}
+
+static int
+zclient_flush_data(struct thread *thread)
+{
+  struct zclient *zclient = THREAD_ARG(thread);
+
+  zclient->t_write = NULL;
+  if (zclient->sock < 0)
+    return -1;
+  switch (buffer_flush_available(zclient->wb, zclient->sock))
+    {
+    case BUFFER_ERROR:
+      zlog_warn("%s: buffer_flush_available failed on zclient fd %d, closing",
+               __func__, zclient->sock);
+      return zclient_failed(zclient);
+      break;
+    case BUFFER_PENDING:
+      zclient->t_write = thread_add_write (zclient->master, zclient_flush_data,
+                                          zclient, zclient->sock);
+      break;
+    case BUFFER_EMPTY:
+      break;
+    }
+  return 0;
+}
+
+int
+zclient_send_message(struct zclient *zclient)
+{
+  if (zclient->sock < 0)
+    return -1;
+  switch (buffer_write(zclient->wb, zclient->sock, STREAM_DATA(zclient->obuf),
+                      stream_get_endp(zclient->obuf)))
+    {
+    case BUFFER_ERROR:
+      zlog_warn("%s: buffer_write failed to zclient fd %d, closing",
+                __func__, zclient->sock);
+      return zclient_failed(zclient);
+      break;
+    case BUFFER_EMPTY:
+      THREAD_OFF(zclient->t_write);
+      break;
+    case BUFFER_PENDING:
+      THREAD_WRITE_ON (zclient->master, zclient->t_write,
+                      zclient_flush_data, zclient, zclient->sock);
+      break;
+    }
+  return 0;
+}
+
+void
+zclient_create_header (struct stream *s, uint16_t command, vrf_id_t vrf_id)
+{
+  /* length placeholder, caller can update */
+  stream_putw (s, ZEBRA_HEADER_SIZE);
+  stream_putc (s, ZEBRA_HEADER_MARKER);
+  stream_putc (s, ZSERV_VERSION);
+  stream_putw (s, vrf_id);
+  stream_putw (s, command);
+}
+
+int
+zclient_read_header (struct stream *s, int sock, u_int16_t *size, u_char *marker,
+                     u_char *version, u_int16_t *vrf_id, u_int16_t *cmd)
+{
+  if (stream_read (s, sock, ZEBRA_HEADER_SIZE) != ZEBRA_HEADER_SIZE)
+    return -1;
+
+  *size = stream_getw (s) - ZEBRA_HEADER_SIZE;
+  *marker = stream_getc (s);
+  *version = stream_getc (s);
+  *vrf_id = stream_getw (s);
+  *cmd = stream_getw (s);
+
+  if (*version != ZSERV_VERSION || *marker != ZEBRA_HEADER_MARKER)
+    {
+      zlog_err("%s: socket %d version mismatch, marker %d, version %d",
+               __func__, sock, *marker, *version);
+      return -1;
+    }
+
+  if (*size && stream_read (s, sock, *size) != *size)
+    return -1;
+
+  return 0;
+}
+
+/* Send simple Zebra message. */
+static int
+zebra_message_send (struct zclient *zclient, int command, vrf_id_t vrf_id)
+{
+  struct stream *s;
+
+  /* Get zclient output buffer. */
+  s = zclient->obuf;
+  stream_reset (s);
+
+  /* Send very simple command only Zebra message. */
+  zclient_create_header (s, command, vrf_id);
+  
+  return zclient_send_message(zclient);
+}
+
+static int
+zebra_hello_send (struct zclient *zclient)
+{
+  struct stream *s;
+
+  if (zclient->redist_default)
+    {
+      s = zclient->obuf;
+      stream_reset (s);
+
+      /* The VRF ID in the HELLO message is always 0. */
+      zclient_create_header (s, ZEBRA_HELLO, VRF_DEFAULT);
+      stream_putc (s, zclient->redist_default);
+      stream_putw_at (s, 0, stream_get_endp (s));
+      return zclient_send_message(zclient);
+    }
+
+  return 0;
+}
+
+/* Send requests to zebra daemon for the information in a VRF. */
+void
+zclient_send_requests (struct zclient *zclient, vrf_id_t vrf_id)
+{
+  int i;
+
+  /* zclient is disabled. */
+  if (! zclient->enable)
+    return;
+
+  /* If not connected to the zebra yet. */
+  if (zclient->sock < 0)
+    return;
+
+  if (zclient_debug)
+    zlog_debug ("%s: send messages for VRF %u", __func__, vrf_id);
+
+  /* We need router-id information. */
+  zebra_message_send (zclient, ZEBRA_ROUTER_ID_ADD, vrf_id);
+
+  /* We need interface information. */
+  zebra_message_send (zclient, ZEBRA_INTERFACE_ADD, vrf_id);
+
+  /* Set unwanted redistribute route. */
+  vrf_bitmap_set (zclient->redist[zclient->redist_default], vrf_id);
+
+  /* Flush all redistribute request. */
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    if (i != zclient->redist_default &&
+        vrf_bitmap_check (zclient->redist[i], vrf_id))
+      zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, i, vrf_id);
+
+  /* If default information is needed. */
+  if (vrf_bitmap_check (zclient->default_information, VRF_DEFAULT))
+    zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_ADD, vrf_id);
+}
+
+/* Make connection to zebra daemon. */
+int
+zclient_start (struct zclient *zclient)
+{
+  if (zclient_debug)
+    zlog_debug ("zclient_start is called");
+
+  /* zclient is disabled. */
+  if (! zclient->enable)
+    return 0;
+
+  /* If already connected to the zebra. */
+  if (zclient->sock >= 0)
+    return 0;
+
+  /* Check connect thread. */
+  if (zclient->t_connect)
+    return 0;
+
+  /*
+   * If we fail to connect to the socket on initialization,
+   * Let's wait a second and see if we can reconnect.
+   * Cause if we don't connect, we never attempt to
+   * reconnect.  On startup if zebra is slow we
+   * can get into this situation.
+   */
+  while (zclient_socket_connect(zclient) < 0 && zclient->fail < 5)
+    {
+      if (zclient_debug)
+       zlog_debug ("zclient connection fail");
+      zclient->fail++;
+      sleep (1);
+    }
+
+  if (zclient->sock < 0)
+    {
+      zclient_event (ZCLIENT_CONNECT, zclient);
+      return -1;
+    }
+
+  if (set_nonblocking(zclient->sock) < 0)
+    zlog_warn("%s: set_nonblocking(%d) failed", __func__, zclient->sock);
+
+  /* Clear fail count. */
+  zclient->fail = 0;
+  if (zclient_debug)
+    zlog_debug ("zclient connect success with socket [%d]", zclient->sock);
+      
+  /* Create read thread. */
+  zclient_event (ZCLIENT_READ, zclient);
+
+  zebra_hello_send (zclient);
+
+  /* Inform the successful connection. */
+  if (zclient->zebra_connected)
+    (*zclient->zebra_connected) (zclient);
+
+  return 0;
+}
+
+/* This function is a wrapper function for calling zclient_start from
+   timer or event thread. */
+static int
+zclient_connect (struct thread *t)
+{
+  struct zclient *zclient;
+
+  zclient = THREAD_ARG (t);
+  zclient->t_connect = NULL;
+
+  if (zclient_debug)
+    zlog_debug ("zclient_connect is called");
+
+  return zclient_start (zclient);
+}
+
+ /* 
+  * "xdr_encode"-like interface that allows daemon (client) to send
+  * a message to zebra server for a route that needs to be
+  * added/deleted to the kernel. Info about the route is specified
+  * by the caller in a struct zapi_ipv4. zapi_ipv4_read() then writes
+  * the info down the zclient socket using the stream_* functions.
+  * 
+  * The corresponding read ("xdr_decode") function on the server
+  * side is zread_ipv4_add()/zread_ipv4_delete().
+  *
+  *  0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  * |            Length (2)         |    Command    | Route Type    |
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  * | ZEBRA Flags   | Message Flags | Prefix length |
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  * | Destination IPv4 Prefix for route                             |
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  * | Nexthop count | 
+  * +-+-+-+-+-+-+-+-+
+  *
+  * 
+  * A number of IPv4 nexthop(s) or nexthop interface index(es) are then 
+  * described, as per the Nexthop count. Each nexthop described as:
+  *
+  * +-+-+-+-+-+-+-+-+
+  * | Nexthop Type  |  Set to one of ZEBRA_NEXTHOP_*
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  * |       IPv4 Nexthop address or Interface Index number          |
+  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  *
+  * Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or
+  * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_ 
+  * nexthop information is provided, and the message describes a prefix
+  * to blackhole or reject route.
+  *
+  * If ZAPI_MESSAGE_DISTANCE is set, the distance value is written as a 1
+  * byte value.
+  * 
+  * If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8
+  * byte value.
+  *
+  * If ZAPI_MESSAGE_TAG is set, the tag value is written as a 4 byte value
+  *
+  * XXX: No attention paid to alignment.
+  */ 
+int
+zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p,
+                 struct zapi_ipv4 *api)
+{
+  int i;
+  int psize;
+  struct stream *s;
+
+  /* Reset stream. */
+  s = zclient->obuf;
+  stream_reset (s);
+
+  zclient_create_header (s, cmd, api->vrf_id);
+  
+  /* Put type and nexthop. */
+  stream_putc (s, api->type);
+  stream_putc (s, api->flags);
+  stream_putc (s, api->message);
+  stream_putw (s, api->safi);
+
+  /* Put prefix information. */
+  psize = PSIZE (p->prefixlen);
+  stream_putc (s, p->prefixlen);
+  stream_write (s, (u_char *) & p->prefix, psize);
+
+  /* Nexthop, ifindex, distance and metric information. */
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE))
+        {
+          stream_putc (s, 1);
+          stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE);
+          /* XXX assert(api->nexthop_num == 0); */
+          /* XXX assert(api->ifindex_num == 0); */
+        }
+      else
+        stream_putc (s, api->nexthop_num + api->ifindex_num);
+
+      for (i = 0; i < api->nexthop_num; i++)
+        {
+          stream_putc (s, ZEBRA_NEXTHOP_IPV4);
+          stream_put_in_addr (s, api->nexthop[i]);
+        }
+      for (i = 0; i < api->ifindex_num; i++)
+        {
+          stream_putc (s, ZEBRA_NEXTHOP_IFINDEX);
+          stream_putl (s, api->ifindex[i]);
+        }
+    }
+
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE))
+    stream_putc (s, api->distance);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC))
+    stream_putl (s, api->metric);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU))
+    stream_putl (s, api->mtu);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG))
+    stream_putl (s, api->tag);
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zclient_send_message(zclient);
+}
+
+#ifdef HAVE_IPV6
+int
+zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
+              struct zapi_ipv6 *api)
+{
+  int i;
+  int psize;
+  struct stream *s;
+
+  /* Reset stream. */
+  s = zclient->obuf;
+  stream_reset (s);
+
+  zclient_create_header (s, cmd, api->vrf_id);
+
+  /* Put type and nexthop. */
+  stream_putc (s, api->type);
+  stream_putc (s, api->flags);
+  stream_putc (s, api->message);
+  stream_putw (s, api->safi);
+  
+  /* Put prefix information. */
+  psize = PSIZE (p->prefixlen);
+  stream_putc (s, p->prefixlen);
+  stream_write (s, (u_char *)&p->prefix, psize);
+
+  /* Nexthop, ifindex, distance and metric information. */
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      stream_putc (s, api->nexthop_num + api->ifindex_num);
+
+      for (i = 0; i < api->nexthop_num; i++)
+       {
+         stream_putc (s, ZEBRA_NEXTHOP_IPV6);
+         stream_write (s, (u_char *)api->nexthop[i], 16);
+       }
+      for (i = 0; i < api->ifindex_num; i++)
+       {
+         stream_putc (s, ZEBRA_NEXTHOP_IFINDEX);
+         stream_putl (s, api->ifindex[i]);
+       }
+    }
+
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE))
+    stream_putc (s, api->distance);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC))
+    stream_putl (s, api->metric);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU))
+    stream_putl (s, api->mtu);
+  if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG))
+    stream_putl (s, api->tag);
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zclient_send_message(zclient);
+}
+#endif /* HAVE_IPV6 */
+
+/* 
+ * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
+ * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
+ * then set/unset redist[type] in the client handle (a struct zserv) for the 
+ * sending client
+ */
+int
+zebra_redistribute_send (int command, struct zclient *zclient, int type,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+
+  s = zclient->obuf;
+  stream_reset(s);
+  
+  zclient_create_header (s, command, vrf_id);
+  stream_putc (s, type);
+  
+  stream_putw_at (s, 0, stream_get_endp (s));
+  
+  return zclient_send_message(zclient);
+}
+
+/* Get prefix in ZServ format; family should be filled in on prefix */
+static void
+zclient_stream_get_prefix (struct stream *s, struct prefix *p)
+{
+  size_t plen = prefix_blen (p);
+  u_char c;
+  p->prefixlen = 0;
+  
+  if (plen == 0)
+    return;
+  
+  stream_get (&p->u.prefix, s, plen);
+  c = stream_getc(s);
+  p->prefixlen = MIN(plen * 8, c);
+}
+
+/* Router-id update from zebra daemon. */
+void
+zebra_router_id_update_read (struct stream *s, struct prefix *rid)
+{
+  /* Fetch interface address. */
+  rid->family = stream_getc (s);
+  
+  zclient_stream_get_prefix (s, rid);
+}
+
+/* Interface addition from zebra daemon. */
+/*  
+ * The format of the message sent with type ZEBRA_INTERFACE_ADD or
+ * ZEBRA_INTERFACE_DELETE from zebra to the client is:
+ *     0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  ifname                                                       |
+ * |                                                               |
+ * |                                                               |
+ * |                                                               |
+ * |                                                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  ifindex                                                      |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  status       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  if_flags                                                     |
+ * |                                                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  metric                                                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  ifmtu                                                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  ifmtu6                                                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  bandwidth                                                    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Link Layer Type                                              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Harware Address Length                                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Hardware Address      if HW lenght different from 0          |
+ * |   ...                  max INTERFACE_HWADDR_MAX               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Link_params? |  Whether a link-params follows: 1 or 0.       
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Link_params    0 or 1 INTERFACE_LINK_PARAMS_SIZE sized       |
+ * |   ....          (struct if_link_params).                      |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct interface *
+zebra_interface_add_read (struct stream *s, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  char ifname_tmp[INTERFACE_NAMSIZ];
+
+  /* Read interface name. */
+  stream_get (ifname_tmp, s, INTERFACE_NAMSIZ);
+
+  /* Lookup/create interface by name. */
+  ifp = if_get_by_name_len_vrf (ifname_tmp,
+                                strnlen (ifname_tmp, INTERFACE_NAMSIZ),
+                                vrf_id);
+
+  zebra_interface_if_set_value (s, ifp);
+
+  return ifp;
+}
+
+/* 
+ * Read interface up/down msg (ZEBRA_INTERFACE_UP/ZEBRA_INTERFACE_DOWN)
+ * from zebra server.  The format of this message is the same as
+ * that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE (see
+ * comments for zebra_interface_add_read), except that no sockaddr_dl
+ * is sent at the tail of the message.
+ */
+struct interface *
+zebra_interface_state_read (struct stream *s, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  char ifname_tmp[INTERFACE_NAMSIZ];
+
+  /* Read interface name. */
+  stream_get (ifname_tmp, s, INTERFACE_NAMSIZ);
+
+  /* Lookup this by interface index. */
+  ifp = if_lookup_by_name_len_vrf (ifname_tmp,
+                                   strnlen (ifname_tmp, INTERFACE_NAMSIZ),
+                                   vrf_id);
+
+  /* If such interface does not exist, indicate an error */
+  if (! ifp)
+     return NULL;
+
+  zebra_interface_if_set_value (s, ifp);
+
+  return ifp;
+}
+
+static void
+link_params_set_value(struct stream *s, struct if_link_params *iflp)
+{
+  
+  if (iflp == NULL)
+    return;
+  
+  iflp->lp_status = stream_getl (s);
+  iflp->te_metric = stream_getl (s);
+  iflp->max_bw = stream_getf (s);
+  iflp->max_rsv_bw = stream_getf (s);
+  uint32_t bwclassnum = stream_getl (s);
+  {
+    unsigned int i;
+    for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++)
+      iflp->unrsv_bw[i] = stream_getf (s);
+    if (i < bwclassnum)
+      zlog_err ("%s: received %d > %d (MAX_CLASS_TYPE) bw entries"
+                " - outdated library?",
+                __func__, bwclassnum, MAX_CLASS_TYPE);
+  }
+  iflp->admin_grp = stream_getl (s);
+  iflp->rmt_as = stream_getl (s);
+  iflp->rmt_ip.s_addr = stream_get_ipv4 (s);
+  
+  iflp->av_delay = stream_getl (s);
+  iflp->min_delay = stream_getl (s);
+  iflp->max_delay = stream_getl (s);
+  iflp->delay_var = stream_getl (s);
+  
+  iflp->pkt_loss = stream_getf (s);
+  iflp->res_bw = stream_getf (s);
+  iflp->ava_bw = stream_getf (s);
+  iflp->use_bw = stream_getf (s);
+}
+
+struct interface *
+zebra_interface_link_params_read (struct stream *s)
+{
+  struct if_link_params *iflp;
+  uint32_t ifindex = stream_getl (s);
+
+  struct interface *ifp = if_lookup_by_index (ifindex);
+
+  if (ifp == NULL || s == NULL)
+    {
+      zlog_err ("%s: unknown ifindex %u, shouldn't happen",
+                __func__, ifindex);
+      return NULL;
+    }
+
+  if ((iflp = if_link_params_get (ifp)) == NULL)
+    return NULL;
+
+  link_params_set_value(s, iflp);
+  
+  return ifp;
+}
+
+void
+zebra_interface_if_set_value (struct stream *s, struct interface *ifp)
+{
+  u_char link_params_status = 0;
+
+  /* Read interface's index. */
+  ifp->ifindex = stream_getl (s);
+  ifp->status = stream_getc (s);
+
+  /* Read interface's value. */
+  ifp->flags = stream_getq (s);
+  ifp->metric = stream_getl (s);
+  ifp->mtu = stream_getl (s);
+  ifp->mtu6 = stream_getl (s);
+  ifp->bandwidth = stream_getl (s);
+  ifp->ll_type = stream_getl (s);
+  ifp->hw_addr_len = stream_getl (s);
+  if (ifp->hw_addr_len)
+    stream_get (ifp->hw_addr, s, MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX));
+
+  /* Read Traffic Engineering status */
+  link_params_status = stream_getc (s);
+  /* Then, Traffic Engineering parameters if any */
+  if (link_params_status)
+    {
+      struct if_link_params *iflp = if_link_params_get (ifp);
+      link_params_set_value(s, iflp);
+    }
+}
+
+size_t
+zebra_interface_link_params_write (struct stream *s, struct interface *ifp)
+{
+  size_t w;
+  struct if_link_params *iflp;
+  int i;
+  
+  if (s == NULL || ifp == NULL || ifp->link_params == NULL)
+    return 0;
+  
+  iflp = ifp->link_params;
+  w = 0;
+    
+  w += stream_putl (s, iflp->lp_status);
+
+  w += stream_putl (s, iflp->te_metric);
+  w += stream_putf (s, iflp->max_bw);
+  w += stream_putf (s, iflp->max_rsv_bw);
+  
+  w += stream_putl (s, MAX_CLASS_TYPE);
+  for (i = 0; i < MAX_CLASS_TYPE; i++)
+    w += stream_putf (s, iflp->unrsv_bw[i]);
+  
+  w += stream_putl (s, iflp->admin_grp);
+  w += stream_putl (s, iflp->rmt_as);
+  w += stream_put_in_addr (s, &iflp->rmt_ip);
+  
+  w += stream_putl (s, iflp->av_delay);
+  w += stream_putl (s, iflp->min_delay);
+  w += stream_putl (s, iflp->max_delay);
+  w += stream_putl (s, iflp->delay_var);
+  
+  w += stream_putf (s, iflp->pkt_loss);
+  w += stream_putf (s, iflp->res_bw);
+  w += stream_putf (s, iflp->ava_bw);
+  w += stream_putf (s, iflp->use_bw);
+  
+  return w;
+}
+
+/*
+ * format of message for address additon is:
+ *    0
+ *  0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |   type        |  ZEBRA_INTERFACE_ADDRESS_ADD or
+ * +-+-+-+-+-+-+-+-+  ZEBRA_INTERFACE_ADDRES_DELETE
+ * |               |
+ * +               +
+ * |   ifindex     |
+ * +               +
+ * |               |
+ * +               +
+ * |               |
+ * +-+-+-+-+-+-+-+-+
+ * |   ifc_flags   |  flags for connected address
+ * +-+-+-+-+-+-+-+-+
+ * |  addr_family  |
+ * +-+-+-+-+-+-+-+-+
+ * |    addr...    |
+ * :               :
+ * |               |
+ * +-+-+-+-+-+-+-+-+
+ * |    addr_len   |  len of addr. E.g., addr_len = 4 for ipv4 addrs.
+ * +-+-+-+-+-+-+-+-+
+ * |     daddr..   |
+ * :               :
+ * |               |
+ * +-+-+-+-+-+-+-+-+
+ */
+
+static int
+memconstant(const void *s, int c, size_t n)
+{
+  const u_char *p = s;
+
+  while (n-- > 0)
+    if (*p++ != c)
+      return 0;
+  return 1;
+}
+
+struct connected *
+zebra_interface_address_read (int type, struct stream *s, vrf_id_t vrf_id)
+{
+  ifindex_t ifindex;
+  struct interface *ifp;
+  struct connected *ifc;
+  struct prefix p, d, *dp;
+  int plen;
+  u_char ifc_flags;
+
+  memset (&p, 0, sizeof(p));
+  memset (&d, 0, sizeof(d));
+
+  /* Get interface index. */
+  ifindex = stream_getl (s);
+
+  /* Lookup index. */
+  ifp = if_lookup_by_index_vrf (ifindex, vrf_id);
+  if (ifp == NULL)
+    {
+      zlog_warn ("zebra_interface_address_read(%s): "
+                 "Can't find interface by ifindex: %d ",
+                 (type == ZEBRA_INTERFACE_ADDRESS_ADD? "ADD" : "DELETE"),
+                 ifindex);
+      return NULL;
+    }
+
+  /* Fetch flag. */
+  ifc_flags = stream_getc (s);
+
+  /* Fetch interface address. */
+  d.family = p.family = stream_getc (s);
+  plen = prefix_blen (&d);
+  
+  zclient_stream_get_prefix (s, &p);
+
+  /* Fetch destination address. */
+  stream_get (&d.u.prefix, s, plen);
+  
+  /* N.B. NULL destination pointers are encoded as all zeroes */
+  dp = memconstant(&d.u.prefix,0,plen) ? NULL : &d;
+  
+  if (type == ZEBRA_INTERFACE_ADDRESS_ADD) 
+    {
+       /* N.B. NULL destination pointers are encoded as all zeroes */
+       ifc = connected_add_by_prefix(ifp, &p, dp);
+       if (ifc != NULL)
+        {
+          ifc->flags = ifc_flags;
+          if (ifc->destination)
+            ifc->destination->prefixlen = ifc->address->prefixlen;
+          else if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER))
+            {
+              /* carp interfaces on OpenBSD with 0.0.0.0/0 as "peer" */
+              char buf[PREFIX_STRLEN];
+              zlog_warn("warning: interface %s address %s "
+                   "with peer flag set, but no peer address!",
+                   ifp->name,
+                   prefix2str (ifc->address, buf, sizeof buf));
+              UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+            }
+        }
+    }
+  else
+    {
+      assert (type == ZEBRA_INTERFACE_ADDRESS_DELETE);
+      ifc = connected_delete_by_prefix(ifp, &p);
+    }
+
+  return ifc;
+}
+
+
+/* Zebra client message read function. */
+static int
+zclient_read (struct thread *thread)
+{
+  size_t already;
+  uint16_t length, command;
+  uint8_t marker, version;
+  vrf_id_t vrf_id;
+  struct zclient *zclient;
+
+  /* Get socket to zebra. */
+  zclient = THREAD_ARG (thread);
+  zclient->t_read = NULL;
+
+  /* Read zebra header (if we don't have it already). */
+  if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE)
+    {
+      ssize_t nbyte;
+      if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock,
+                                    ZEBRA_HEADER_SIZE-already)) == 0) ||
+         (nbyte == -1))
+       {
+         if (zclient_debug)
+          zlog_debug ("zclient connection closed socket [%d].", zclient->sock);
+         return zclient_failed(zclient);
+       }
+      if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE-already))
+       {
+         /* Try again later. */
+         zclient_event (ZCLIENT_READ, zclient);
+         return 0;
+       }
+      already = ZEBRA_HEADER_SIZE;
+    }
+
+  /* Reset to read from the beginning of the incoming packet. */
+  stream_set_getp(zclient->ibuf, 0);
+
+  /* Fetch header values. */
+  length = stream_getw (zclient->ibuf);
+  marker = stream_getc (zclient->ibuf);
+  version = stream_getc (zclient->ibuf);
+  vrf_id = stream_getw (zclient->ibuf);
+  command = stream_getw (zclient->ibuf);
+  
+  if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION)
+    {
+      zlog_err("%s: socket %d version mismatch, marker %d, version %d",
+               __func__, zclient->sock, marker, version);
+      return zclient_failed(zclient);
+    }
+  
+  if (length < ZEBRA_HEADER_SIZE) 
+    {
+      zlog_err("%s: socket %d message length %u is less than %d ",
+              __func__, zclient->sock, length, ZEBRA_HEADER_SIZE);
+      return zclient_failed(zclient);
+    }
+
+  /* Length check. */
+  if (length > STREAM_SIZE(zclient->ibuf))
+    {
+      struct stream *ns;
+      zlog_warn("%s: message size %u exceeds buffer size %lu, expanding...",
+               __func__, length, (u_long)STREAM_SIZE(zclient->ibuf));
+      ns = stream_new(length);
+      stream_copy(ns, zclient->ibuf);
+      stream_free (zclient->ibuf);
+      zclient->ibuf = ns;
+    }
+
+  /* Read rest of zebra packet. */
+  if (already < length)
+    {
+      ssize_t nbyte;
+      if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock,
+                                    length-already)) == 0) ||
+         (nbyte == -1))
+       {
+         if (zclient_debug)
+           zlog_debug("zclient connection closed socket [%d].", zclient->sock);
+         return zclient_failed(zclient);
+       }
+      if (nbyte != (ssize_t)(length-already))
+       {
+         /* Try again later. */
+         zclient_event (ZCLIENT_READ, zclient);
+         return 0;
+       }
+    }
+
+  length -= ZEBRA_HEADER_SIZE;
+
+  if (zclient_debug)
+    zlog_debug("zclient 0x%p command 0x%x VRF %u\n", (void *)zclient, command, vrf_id);
+
+  switch (command)
+    {
+    case ZEBRA_ROUTER_ID_UPDATE:
+      if (zclient->router_id_update)
+       (*zclient->router_id_update) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_ADD:
+      if (zclient->interface_add)
+       (*zclient->interface_add) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_DELETE:
+      if (zclient->interface_delete)
+       (*zclient->interface_delete) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_ADDRESS_ADD:
+      if (zclient->interface_address_add)
+       (*zclient->interface_address_add) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_ADDRESS_DELETE:
+      if (zclient->interface_address_delete)
+       (*zclient->interface_address_delete) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_UP:
+      if (zclient->interface_up)
+       (*zclient->interface_up) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_DOWN:
+      if (zclient->interface_down)
+       (*zclient->interface_down) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_IPV4_ROUTE_ADD:
+      if (zclient->ipv4_route_add)
+       (*zclient->ipv4_route_add) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_IPV4_ROUTE_DELETE:
+      if (zclient->ipv4_route_delete)
+       (*zclient->ipv4_route_delete) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_IPV6_ROUTE_ADD:
+      if (zclient->ipv6_route_add)
+       (*zclient->ipv6_route_add) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_IPV6_ROUTE_DELETE:
+      if (zclient->ipv6_route_delete)
+       (*zclient->ipv6_route_delete) (command, zclient, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_LINK_PARAMS:
+      if (zclient->interface_link_params)
+        (*zclient->interface_link_params) (command, zclient, length);
+    case ZEBRA_NEXTHOP_UPDATE:
+      if (zclient->nexthop_update)
+       (*zclient->nexthop_update) (command, zclient, length, vrf_id);
+      break;
+    default:
+      break;
+    }
+
+  if (zclient->sock < 0)
+    /* Connection was closed during packet processing. */
+    return -1;
+
+  /* Register read thread. */
+  stream_reset(zclient->ibuf);
+  zclient_event (ZCLIENT_READ, zclient);
+
+  return 0;
+}
+
+void
+zclient_redistribute (int command, struct zclient *zclient, int type,
+    vrf_id_t vrf_id)
+{
+
+  if (command == ZEBRA_REDISTRIBUTE_ADD) 
+    {
+      if (vrf_bitmap_check (zclient->redist[type], vrf_id))
+         return;
+      vrf_bitmap_set (zclient->redist[type], vrf_id);
+    }
+  else
+    {
+      if (!vrf_bitmap_check (zclient->redist[type], vrf_id))
+         return;
+      vrf_bitmap_unset (zclient->redist[type], vrf_id);
+    }
+
+  if (zclient->sock > 0)
+    zebra_redistribute_send (command, zclient, type, vrf_id);
+}
+
+
+void
+zclient_redistribute_default (int command, struct zclient *zclient,
+    vrf_id_t vrf_id)
+{
+
+  if (command == ZEBRA_REDISTRIBUTE_DEFAULT_ADD)
+    {
+      if (vrf_bitmap_check (zclient->default_information, vrf_id))
+        return;
+      vrf_bitmap_set (zclient->default_information, vrf_id);
+    }
+  else 
+    {
+      if (!vrf_bitmap_check (zclient->default_information, vrf_id))
+        return;
+      vrf_bitmap_unset (zclient->default_information, vrf_id);
+    }
+
+  if (zclient->sock > 0)
+    zebra_message_send (zclient, command, vrf_id);
+}
+
+static void
+zclient_event (enum event event, struct zclient *zclient)
+{
+  switch (event)
+    {
+    case ZCLIENT_SCHEDULE:
+      if (! zclient->t_connect)
+       zclient->t_connect =
+         thread_add_event (zclient->master, zclient_connect, zclient, 0);
+      break;
+    case ZCLIENT_CONNECT:
+      if (zclient->fail >= 10)
+       return;
+      if (zclient_debug)
+       zlog_debug ("zclient connect schedule interval is %d", 
+                  zclient->fail < 3 ? 10 : 60);
+      if (! zclient->t_connect)
+       zclient->t_connect = 
+         thread_add_timer (zclient->master, zclient_connect, zclient,
+                           zclient->fail < 3 ? 10 : 60);
+      break;
+    case ZCLIENT_READ:
+      zclient->t_read = 
+       thread_add_read (zclient->master, zclient_read, zclient, zclient->sock);
+      break;
+    }
+}
+
+const char *zclient_serv_path_get()
+{
+  return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH;
+}
+
+void
+zclient_serv_path_set (char *path)
+{
+  struct stat sb;
+
+  /* reset */
+  zclient_serv_path = NULL;
+
+  /* test if `path' is socket. don't set it otherwise. */
+  if (stat(path, &sb) == -1)
+    {
+      zlog_warn ("%s: zebra socket `%s' does not exist", __func__, path);
+      return;
+    }
+
+  if ((sb.st_mode & S_IFMT) != S_IFSOCK)
+    {
+      zlog_warn ("%s: `%s' is not unix socket, sir", __func__, path);
+      return;
+    }
+
+  /* it seems that path is unix socket */
+  zclient_serv_path = path;
+}
+
diff --git a/lib/zclient.h b/lib/zclient.h
new file mode 100644 (file)
index 0000000..d46728d
--- /dev/null
@@ -0,0 +1,229 @@
+/* Zebra's client header.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_ZCLIENT_H
+#define _ZEBRA_ZCLIENT_H
+
+/* For struct zapi_ipv{4,6}. */
+#include "prefix.h"
+
+/* For struct interface and struct connected. */
+#include "if.h"
+
+/* For vrf_bitmap_t. */
+#include "vrf.h"
+
+/* For input/output buffer to zebra. */
+#define ZEBRA_MAX_PACKET_SIZ          4096
+
+/* Zebra header size. */
+#define ZEBRA_HEADER_SIZE             8
+
+/* Structure for the zebra client. */
+struct zclient
+{
+  /* The thread master we schedule ourselves on */
+  struct thread_master *master;
+
+  /* Socket to zebra daemon. */
+  int sock;
+
+  /* Flag of communication to zebra is enabled or not.  Default is on.
+     This flag is disabled by `no router zebra' statement. */
+  int enable;
+
+  /* Connection failure count. */
+  int fail;
+
+  /* Input buffer for zebra message. */
+  struct stream *ibuf;
+
+  /* Output buffer for zebra message. */
+  struct stream *obuf;
+
+  /* Buffer of data waiting to be written to zebra. */
+  struct buffer *wb;
+
+  /* Read and connect thread. */
+  struct thread *t_read;
+  struct thread *t_connect;
+
+  /* Thread to write buffered data to zebra. */
+  struct thread *t_write;
+
+  /* Redistribute information. */
+  u_char redist_default;
+  vrf_bitmap_t redist[ZEBRA_ROUTE_MAX];
+
+  /* Redistribute defauilt. */
+  vrf_bitmap_t default_information;
+
+  /* Pointer to the callback functions. */
+  void (*zebra_connected) (struct zclient *);
+  int (*router_id_update) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_add) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_delete) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_up) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_down) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_address_add) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_address_delete) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*interface_link_params) (int, struct zclient *, uint16_t);
+  int (*ipv4_route_add) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*ipv4_route_delete) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*ipv6_route_add) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*ipv6_route_delete) (int, struct zclient *, uint16_t, vrf_id_t);
+  int (*nexthop_update) (int, struct zclient *, uint16_t, vrf_id_t);
+};
+
+/* Zebra API message flag. */
+#define ZAPI_MESSAGE_NEXTHOP  0x01
+#define ZAPI_MESSAGE_IFINDEX  0x02
+#define ZAPI_MESSAGE_DISTANCE 0x04
+#define ZAPI_MESSAGE_METRIC   0x08
+#define ZAPI_MESSAGE_MTU      0x10
+#define ZAPI_MESSAGE_TAG      0x20
+
+/* Zserv protocol message header */
+struct zserv_header
+{
+  uint16_t length;
+  uint8_t marker;      /* corresponds to command field in old zserv
+                         * always set to 255 in new zserv.
+                         */
+  uint8_t version;
+#define ZSERV_VERSION  3
+  vrf_id_t vrf_id;
+  uint16_t command;
+};
+
+/* Zebra IPv4 route message API. */
+struct zapi_ipv4
+{
+  u_char type;
+
+  u_char flags;
+
+  u_char message;
+
+  safi_t safi;
+
+  u_char nexthop_num;
+  struct in_addr **nexthop;
+
+  u_char ifindex_num;
+  ifindex_t *ifindex;
+
+  u_char distance;
+
+  route_tag_t tag;
+
+  u_int32_t metric;
+
+  u_int32_t mtu;
+
+  vrf_id_t vrf_id;
+};
+
+/* Prototypes of zebra client service functions. */
+extern struct zclient *zclient_new (struct thread_master *);
+extern void zclient_init (struct zclient *, int);
+extern int zclient_start (struct zclient *);
+extern void zclient_stop (struct zclient *);
+extern void zclient_reset (struct zclient *);
+extern void zclient_free (struct zclient *);
+
+extern int  zclient_socket_connect (struct zclient *);
+extern void zclient_serv_path_set  (char *path);
+extern const char *zclient_serv_path_get (void);
+
+extern void zclient_send_requests (struct zclient *, vrf_id_t);
+
+/* Send redistribute command to zebra daemon. Do not update zclient state. */
+extern int zebra_redistribute_send (int command, struct zclient *, int type,
+    vrf_id_t vrf_id);
+
+/* If state has changed, update state and call zebra_redistribute_send. */
+extern void zclient_redistribute (int command, struct zclient *, int type,
+    vrf_id_t vrf_id);
+
+/* If state has changed, update state and send the command to zebra. */
+extern void zclient_redistribute_default (int command, struct zclient *,
+    vrf_id_t vrf_id);
+
+/* Send the message in zclient->obuf to the zebra daemon (or enqueue it).
+   Returns 0 for success or -1 on an I/O error. */
+extern int zclient_send_message(struct zclient *);
+
+/* create header for command, length to be filled in by user later */
+extern void zclient_create_header (struct stream *, uint16_t, vrf_id_t);
+extern int zclient_read_header (struct stream *s, int sock, u_int16_t *size,
+                               u_char *marker, u_char *version,
+                               u_int16_t *vrf_id, u_int16_t *cmd);
+
+extern struct interface *zebra_interface_add_read (struct stream *,
+    vrf_id_t);
+extern struct interface *zebra_interface_state_read (struct stream *,
+    vrf_id_t);
+extern struct connected *zebra_interface_address_read (int, struct stream *,
+    vrf_id_t);
+extern void zebra_interface_if_set_value (struct stream *, struct interface *);
+extern void zebra_router_id_update_read (struct stream *s, struct prefix *rid);
+extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *, 
+                            struct zapi_ipv4 *);
+
+extern struct interface *zebra_interface_link_params_read (struct stream *);
+extern size_t zebra_interface_link_params_write (struct stream *,
+                                                 struct interface *);
+#ifdef HAVE_IPV6
+/* IPv6 prefix add and delete function prototype. */
+
+struct zapi_ipv6
+{
+  u_char type;
+
+  u_char flags;
+
+  u_char message;
+
+  safi_t safi;
+
+  u_char nexthop_num;
+  struct in6_addr **nexthop;
+
+  u_char ifindex_num;
+  ifindex_t *ifindex;
+
+  u_char distance;
+
+  route_tag_t tag;
+
+  u_int32_t metric;
+
+  u_int32_t mtu;
+
+  vrf_id_t vrf_id;
+};
+
+extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient, 
+                     struct prefix_ipv6 *p, struct zapi_ipv6 *api);
+#endif /* HAVE_IPV6 */
+
+#endif /* _ZEBRA_ZCLIENT_H */
diff --git a/lib/zebra.h b/lib/zebra.h
new file mode 100644 (file)
index 0000000..a405d46
--- /dev/null
@@ -0,0 +1,541 @@
+/* Zebra common header.
+   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _ZEBRA_H
+#define _ZEBRA_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef SUNOS_5
+#define _XPG4_2
+typedef unsigned int    u_int32_t;
+typedef unsigned short  u_int16_t;
+typedef unsigned char   u_int8_t;
+#endif /* SUNOS_5 */
+
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif /* HAVE_SOCKLEN_T */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif /* HAVE_STROPTS_H */
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#ifdef GNU_LINUX
+#include <linux/types.h>
+#endif
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_CONF_H
+#include <sys/conf.h>
+#endif /* HAVE_SYS_CONF_H */
+#ifdef HAVE_SYS_KSYM_H
+#include <sys/ksym.h>
+#endif /* HAVE_SYS_KSYM_H */
+#include <syslog.h>
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif /* TIME_WITH_SYS_TIME */
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#ifdef HAVE_RUSAGE
+#include <sys/resource.h>
+#endif /* HAVE_RUSAGE */
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#endif
+/* primarily for __STDC_IEC_559__ with clang */
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+/* machine dependent includes */
+#ifdef SUNOS_5
+#include <strings.h>
+#endif /* SUNOS_5 */
+
+/* machine dependent includes */
+#ifdef HAVE_LINUX_VERSION_H
+#include <linux/version.h>
+#endif /* HAVE_LINUX_VERSION_H */
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif /* HAVE_ASM_TYPES_H */
+
+/* misc include group */
+#include <stdarg.h>
+#if !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+/* Not C99; do we need to define va_copy? */
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy(DST,SRC) __va_copy(DST,SRC)
+#else
+/* Now we are desperate; this should work on many typical platforms. 
+   But this is slightly dangerous, because the standard does not require
+   va_copy to be a macro. */
+#define va_copy(DST,SRC) memcpy(&(DST), &(SRC), sizeof(va_list))
+#warning "Not C99 and no va_copy macro available, falling back to memcpy"
+#endif /* __va_copy */
+#endif /* !va_copy */
+#endif /* !C99 */
+
+
+#ifdef HAVE_LCAPS
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#endif /* HAVE_LCAPS */
+
+#ifdef HAVE_SOLARIS_CAPABILITIES
+#include <priv.h>
+#endif /* HAVE_SOLARIS_CAPABILITIES */
+
+/* network include group */
+
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif /* HAVE_SYS_SOCKIO_H */
+
+#ifdef __APPLE__
+#define __APPLE_USE_RFC_3542
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef HAVE_NET_NETOPT_H
+#include <net/netopt.h>
+#endif /* HAVE_NET_NETOPT_H */
+
+#include <net/if.h>
+
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif /* HAVE_NET_IF_DL_H */
+
+#ifdef HAVE_NET_IF_VAR_H
+#include <net/if_var.h>
+#endif /* HAVE_NET_IF_VAR_H */
+
+#ifdef HAVE_NET_ROUTE_H
+#include <net/route.h>
+#endif /* HAVE_NET_ROUTE_H */
+
+#ifdef HAVE_NETLINK
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/filter.h>
+#else
+#define RT_TABLE_MAIN          0
+#endif /* HAVE_NETLINK */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif /* HAVE_NETDB_H */
+
+#include <arpa/inet.h>
+
+#ifdef HAVE_INET_ND_H
+#include <inet/nd.h>
+#endif /* HAVE_INET_ND_H */
+
+#ifdef HAVE_NETINET_IN_VAR_H
+#include <netinet/in_var.h>
+#endif /* HAVE_NETINET_IN_VAR_H */
+
+#ifdef HAVE_NETINET6_IN6_VAR_H
+#include <netinet6/in6_var.h>
+#endif /* HAVE_NETINET6_IN6_VAR_H */
+
+#ifdef HAVE_NETINET_IN6_VAR_H
+#include <netinet/in6_var.h>
+#endif /* HAVE_NETINET_IN6_VAR_H */
+
+#ifdef HAVE_NETINET6_IN_H
+#include <netinet6/in.h>
+#endif /* HAVE_NETINET6_IN_H */
+
+
+#ifdef HAVE_NETINET6_IP6_H
+#include <netinet6/ip6.h>
+#endif /* HAVE_NETINET6_IP6_H */
+
+#ifdef HAVE_NETINET_ICMP6_H
+#include <netinet/icmp6.h>
+#endif /* HAVE_NETINET_ICMP6_H */
+
+#ifdef HAVE_NETINET6_ND6_H
+#include <netinet6/nd6.h>
+#endif /* HAVE_NETINET6_ND6_H */
+
+/* Some systems do not define UINT32_MAX, etc.. from inttypes.h
+ * e.g. this makes life easier for FBSD 4.11 users.
+ */
+#ifndef INT8_MAX
+#define INT8_MAX       (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX      (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX      (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX      (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX     (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX     (4294967295U)
+#endif
+
+#ifdef HAVE_GLIBC_BACKTRACE
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
+/* Local includes: */
+#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL)) 
+#define __attribute__(x)
+#endif  /* !__GNUC__ || VTYSH_EXTRACT_PL */
+
+#include "zassert.h"
+#include "str.h"
+
+
+#ifdef HAVE_BROKEN_CMSG_FIRSTHDR
+/* This bug is present in Solaris 8 and pre-patch Solaris 9 <sys/socket.h>;
+   please refer to http://bugzilla.quagga.net/show_bug.cgi?id=142 */
+
+/* Check that msg_controllen is large enough. */
+#define ZCMSG_FIRSTHDR(mhdr) \
+  (((size_t)((mhdr)->msg_controllen) >= sizeof(struct cmsghdr)) ? \
+   CMSG_FIRSTHDR(mhdr) : (struct cmsghdr *)NULL)
+
+#warning "CMSG_FIRSTHDR is broken on this platform, using a workaround"
+
+#else /* HAVE_BROKEN_CMSG_FIRSTHDR */
+#define ZCMSG_FIRSTHDR(M) CMSG_FIRSTHDR(M)
+#endif /* HAVE_BROKEN_CMSG_FIRSTHDR */
+
+
+
+/* 
+ * RFC 3542 defines several macros for using struct cmsghdr.
+ * Here, we define those that are not present
+ */
+
+/*
+ * Internal defines, for use only in this file.
+ * These are likely wrong on other than ILP32 machines, so warn.
+ */
+#ifndef _CMSG_DATA_ALIGN
+#define _CMSG_DATA_ALIGN(n)           (((n) + 3) & ~3)
+#endif /* _CMSG_DATA_ALIGN */
+
+#ifndef _CMSG_HDR_ALIGN
+#define _CMSG_HDR_ALIGN(n)            (((n) + 3) & ~3)
+#endif /* _CMSG_HDR_ALIGN */
+
+/*
+ * CMSG_SPACE and CMSG_LEN are required in RFC3542, but were new in that
+ * version.
+ */
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(l)       (_CMSG_DATA_ALIGN(sizeof(struct cmsghdr)) + \
+                              _CMSG_HDR_ALIGN(l))
+#warning "assuming 4-byte alignment for CMSG_SPACE"
+#endif  /* CMSG_SPACE */
+
+
+#ifndef CMSG_LEN
+#define CMSG_LEN(l)         (_CMSG_DATA_ALIGN(sizeof(struct cmsghdr)) + (l))
+#warning "assuming 4-byte alignment for CMSG_LEN"
+#endif /* CMSG_LEN */
+
+
+/*  The definition of struct in_pktinfo is missing in old version of
+    GLIBC 2.1 (Redhat 6.1).  */
+#if defined (GNU_LINUX) && ! defined (HAVE_STRUCT_IN_PKTINFO)
+struct in_pktinfo
+{
+  int ipi_ifindex;
+  struct in_addr ipi_spec_dst;
+  struct in_addr ipi_addr;
+};
+#endif
+
+/* 
+ * OSPF Fragmentation / fragmented writes
+ *
+ * ospfd can support writing fragmented packets, for cases where
+ * kernel will not fragment IP_HDRINCL and/or multicast destined
+ * packets (ie TTBOMK all kernels, BSD, SunOS, Linux). However,
+ * SunOS, probably BSD too, clobber the user supplied IP ID and IP
+ * flags fields, hence user-space fragmentation will not work.
+ * Only Linux is known to leave IP header unmolested.
+ * Further, fragmentation really should be done the kernel, which already
+ * supports it, and which avoids nasty IP ID state problems.
+ *
+ * Fragmentation of OSPF packets can be required on networks with router
+ * with many many interfaces active in one area, or on networks with links
+ * with low MTUs.
+ */
+#ifdef GNU_LINUX
+#define WANT_OSPF_WRITE_FRAGMENT
+#endif
+
+/* 
+ * IP_HDRINCL / struct ip byte order
+ *
+ * Linux: network byte order
+ * *BSD: network, except for length and offset. (cf Stevens)
+ * SunOS: nominally as per BSD. but bug: network order on LE.
+ * OpenBSD: network byte order, apart from older versions which are as per 
+ *          *BSD
+ */
+#if defined(__NetBSD__) \
+   || (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) \
+   || (defined(__OpenBSD__) && (OpenBSD < 200311)) \
+   || (defined(__APPLE__)) \
+   || (defined(SUNOS_5) && defined(WORDS_BIGENDIAN))
+#define HAVE_IP_HDRINCL_BSD_ORDER
+#endif
+
+/* Define BYTE_ORDER, if not defined. Useful for compiler conditional
+ * code, rather than preprocessor conditional.
+ * Not all the world has this BSD define.
+ */
+#ifndef BYTE_ORDER
+#define BIG_ENDIAN     4321    /* least-significant byte first (vax, pc) */
+#define LITTLE_ENDIAN  1234    /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN     3412    /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(WORDS_BIGENDIAN)
+#define BYTE_ORDER     BIG_ENDIAN
+#else /* !WORDS_BIGENDIAN */
+#define BYTE_ORDER     LITTLE_ENDIAN
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* ndef BYTE_ORDER */
+
+/* MAX / MIN are not commonly defined, but useful */
+#ifndef MAX
+#define MAX(a, b) \
+       ({ typeof (a) _a = (a); \
+          typeof (b) _b = (b); \
+          _a > _b ? _a : _b; })
+#endif
+#ifndef MIN
+#define MIN(a, b) \
+       ({ typeof (a) _a = (a); \
+          typeof (b) _b = (b); \
+          _a < _b ? _a : _b; })
+#endif
+
+#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0]))
+
+/* For old definition. */
+#ifndef IN6_ARE_ADDR_EQUAL
+#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL
+#endif /* IN6_ARE_ADDR_EQUAL */
+
+/* default zebra TCP port for zclient */
+#define ZEBRA_PORT                     2600
+
+/* Zebra message types. */
+#define ZEBRA_INTERFACE_ADD                1
+#define ZEBRA_INTERFACE_DELETE             2
+#define ZEBRA_INTERFACE_ADDRESS_ADD        3
+#define ZEBRA_INTERFACE_ADDRESS_DELETE     4
+#define ZEBRA_INTERFACE_UP                 5
+#define ZEBRA_INTERFACE_DOWN               6
+#define ZEBRA_IPV4_ROUTE_ADD               7
+#define ZEBRA_IPV4_ROUTE_DELETE            8
+#define ZEBRA_IPV6_ROUTE_ADD               9
+#define ZEBRA_IPV6_ROUTE_DELETE           10
+#define ZEBRA_REDISTRIBUTE_ADD            11
+#define ZEBRA_REDISTRIBUTE_DELETE         12
+#define ZEBRA_REDISTRIBUTE_DEFAULT_ADD    13
+#define ZEBRA_REDISTRIBUTE_DEFAULT_DELETE 14
+#define ZEBRA_IPV4_NEXTHOP_LOOKUP         15
+#define ZEBRA_IPV6_NEXTHOP_LOOKUP         16
+#define ZEBRA_IPV4_IMPORT_LOOKUP          17
+#define ZEBRA_IPV6_IMPORT_LOOKUP          18
+#define ZEBRA_INTERFACE_RENAME            19
+#define ZEBRA_ROUTER_ID_ADD               20
+#define ZEBRA_ROUTER_ID_DELETE            21
+#define ZEBRA_ROUTER_ID_UPDATE            22
+#define ZEBRA_HELLO                       23
+#define ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB    24
+#define ZEBRA_VRF_UNREGISTER              25
+#define ZEBRA_INTERFACE_LINK_PARAMS       26
+#define ZEBRA_NEXTHOP_REGISTER            27
+#define ZEBRA_NEXTHOP_UNREGISTER          28
+#define ZEBRA_NEXTHOP_UPDATE              29
+#define ZEBRA_MESSAGE_MAX                 30
+
+/* Marker value used in new Zserv, in the byte location corresponding
+ * the command value in the old zserv header. To allow old and new
+ * Zserv headers to be distinguished from each other.
+ */
+#define ZEBRA_HEADER_MARKER              255
+
+/* Zebra route's types are defined in route_types.h */
+#include "route_types.h"
+
+/* Note: whenever a new route-type or zserv-command is added the
+ * corresponding {command,route}_types[] table in lib/log.c MUST be
+ * updated! */
+
+/* Map a route type to a string.  For example, ZEBRA_ROUTE_RIPNG -> "ripng". */
+extern const char *zebra_route_string(unsigned int route_type);
+/* Map a route type to a char.  For example, ZEBRA_ROUTE_RIPNG -> 'R'. */
+extern char zebra_route_char(unsigned int route_type);
+/* Map a zserv command type to the same string, 
+ * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */
+/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/
+extern int proto_name2num(const char *s);
+/* Map redistribute X argument to protocol number.
+ * unlike proto_name2num, this accepts shorthands and takes
+ * an AFI value to restrict input */
+extern int proto_redistnum(int afi, const char *s);
+
+extern const char *zserv_command_string (unsigned int command);
+
+/* Error codes of zebra. */
+#define ZEBRA_ERR_NOERROR                0
+#define ZEBRA_ERR_RTEXIST               -1
+#define ZEBRA_ERR_RTUNREACH             -2
+#define ZEBRA_ERR_EPERM                 -3
+#define ZEBRA_ERR_RTNOEXIST             -4
+#define ZEBRA_ERR_KERNEL                -5
+
+/* Zebra message flags */
+#define ZEBRA_FLAG_INTERNAL           0x01
+#define ZEBRA_FLAG_SELFROUTE          0x02
+#define ZEBRA_FLAG_BLACKHOLE          0x04
+#define ZEBRA_FLAG_IBGP               0x08
+#define ZEBRA_FLAG_SELECTED           0x10
+#define ZEBRA_FLAG_FIB_OVERRIDE       0x20
+#define ZEBRA_FLAG_STATIC             0x40
+#define ZEBRA_FLAG_REJECT             0x80
+
+/* Zebra nexthop flags. */
+#define ZEBRA_NEXTHOP_IFINDEX            1
+#define ZEBRA_NEXTHOP_IFNAME             2
+#define ZEBRA_NEXTHOP_IPV4               3
+#define ZEBRA_NEXTHOP_IPV4_IFINDEX       4
+#define ZEBRA_NEXTHOP_IPV4_IFNAME        5
+#define ZEBRA_NEXTHOP_IPV6               6
+#define ZEBRA_NEXTHOP_IPV6_IFINDEX       7
+#define ZEBRA_NEXTHOP_IPV6_IFNAME        8
+#define ZEBRA_NEXTHOP_BLACKHOLE          9
+
+#ifndef INADDR_LOOPBACK
+#define        INADDR_LOOPBACK 0x7f000001      /* Internet address 127.0.0.1.  */
+#endif
+
+/* Address family numbers from RFC1700. */
+typedef enum {
+  AFI_IP  = 1,
+  AFI_IP6 = 2,
+  AFI_ETHER = 3,                /* RFC 1700 has "6" for 802.* */
+#define AFI_MAX 4
+} afi_t;
+
+/* Subsequent Address Family Identifier. */
+#define SAFI_UNICAST              1
+#define SAFI_MULTICAST            2
+#define SAFI_RESERVED_3           3
+#define SAFI_MPLS_VPN             4
+#define SAFI_ENCAP               7 /* per IANA */
+#define SAFI_MAX                  8
+
+/* Default Administrative Distance of each protocol. */
+#define ZEBRA_KERNEL_DISTANCE_DEFAULT      0
+#define ZEBRA_CONNECT_DISTANCE_DEFAULT     0
+#define ZEBRA_STATIC_DISTANCE_DEFAULT      1
+#define ZEBRA_RIP_DISTANCE_DEFAULT       120
+#define ZEBRA_RIPNG_DISTANCE_DEFAULT     120
+#define ZEBRA_OSPF_DISTANCE_DEFAULT      110
+#define ZEBRA_OSPF6_DISTANCE_DEFAULT     110
+#define ZEBRA_ISIS_DISTANCE_DEFAULT      115
+#define ZEBRA_IBGP_DISTANCE_DEFAULT      200
+#define ZEBRA_EBGP_DISTANCE_DEFAULT       20
+
+/* Flag manipulation macros. */
+#define CHECK_FLAG(V,F)      ((V) & (F))
+#define SET_FLAG(V,F)        (V) |= (F)
+#define UNSET_FLAG(V,F)      (V) &= ~(F)
+#define RESET_FLAG(V)        (V) = 0
+
+typedef u_int8_t safi_t;
+
+/* Zebra types. Used in Zserv message header. */
+typedef u_int16_t zebra_size_t;
+typedef u_int16_t zebra_command_t;
+
+/* VRF ID type. */
+typedef u_int16_t vrf_id_t;
+
+typedef uint32_t route_tag_t;
+#define ROUTE_TAG_MAX UINT32_MAX
+
+#endif /* _ZEBRA_H */
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644 (file)
index 0000000..3f3bd0a
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644 (file)
index 0000000..49a29bd
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST=Makefile.am README.txt
diff --git a/m4/README.txt b/m4/README.txt
new file mode 100644 (file)
index 0000000..ce06853
--- /dev/null
@@ -0,0 +1,18 @@
+This directory contains local additions to/overrides for the Quagga
+autoconf build system.
+
+At this time additions are:
+
+- m4 files taken from libtool CVS circa august 2004. These have been
+imported into Quagga as they are more robust with respect to configuring
+libtool support for languages which Quagga does not use. As and when libtool
+releases become commonly available with that capability, these can be
+removed. The files are:
+
+       argz.m4
+       libtool.m4
+       ltdl.m4
+       ltoptions.m4
+       ltsugar.m4
+       ltversion.m4
+
diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4
new file mode 100644 (file)
index 0000000..27b0f0c
--- /dev/null
@@ -0,0 +1,333 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_SYS_WEAK_ALIAS
+#
+# DESCRIPTION
+#
+#   Determines whether weak aliases are supported on the system, and if so,
+#   what scheme is used to declare them. Also checks to see if aliases can
+#   cross object file boundaries, as some systems don't permit them to.
+#
+#   Most systems permit something called a "weak alias" or "weak symbol."
+#   These aliases permit a library to provide a stub form of a routine
+#   defined in another library, thus allowing the first library to operate
+#   even if the other library is not linked. This macro will check for
+#   support of weak aliases, figure out what schemes are available, and
+#   determine some characteristics of the weak alias support -- primarily,
+#   whether a weak alias declared in one object file may be referenced from
+#   another object file.
+#
+#   There are four known schemes of declaring weak symbols; each scheme is
+#   checked in turn, and the first one found is prefered. Note that only one
+#   of the mentioned preprocessor macros will be defined!
+#
+#   1. Function attributes
+#
+#   This scheme was first introduced by the GNU C compiler, and attaches
+#   attributes to particular functions. It is among the easiest to use, and
+#   so is the first one checked. If this scheme is detected, the
+#   preprocessor macro HAVE_SYS_WEAK_ALIAS_ATTRIBUTE will be defined to 1.
+#   This scheme is used as in the following code fragment:
+#
+#     void __weakf(int c)
+#     {
+#       /* Function definition... */
+#     }
+#
+#     void weakf(int c) __attribute__((weak, alias("__weakf")));
+#
+#   2. #pragma weak
+#
+#   This scheme is in use by many compilers other than the GNU C compiler.
+#   It is also particularly easy to use, and fairly portable -- well, as
+#   portable as these things get. If this scheme is detected first, the
+#   preprocessor macro HAVE_SYS_WEAK_ALIAS_PRAGMA will be defined to 1. This
+#   scheme is used as in the following code fragment:
+#
+#     extern void weakf(int c);
+#     #pragma weak weakf = __weakf
+#     void __weakf(int c)
+#     {
+#       /* Function definition... */
+#     }
+#
+#   3. #pragma _HP_SECONDARY_DEF
+#
+#   This scheme appears to be in use by the HP compiler. As it is rather
+#   specialized, this is one of the last schemes checked. If it is the first
+#   one detected, the preprocessor macro HAVE_SYS_WEAK_ALIAS_HPSECONDARY
+#   will be defined to 1. This scheme is used as in the following code
+#   fragment:
+#
+#     extern void weakf(int c);
+#     #pragma _HP_SECONDARY_DEF __weakf weakf
+#     void __weakf(int c)
+#     {
+#       /* Function definition... */
+#     }
+#
+#   4. #pragma _CRI duplicate
+#
+#   This scheme appears to be in use by the Cray compiler. As it is rather
+#   specialized, it too is one of the last schemes checked. If it is the
+#   first one detected, the preprocessor macro
+#   HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE will be defined to 1. This scheme is
+#   used as in the following code fragment:
+#
+#     extern void weakf(int c);
+#     #pragma _CRI duplicate weakf as __weakf
+#     void __weakf(int c)
+#     {
+#       /* Function definition... */
+#     }
+#
+#   In addition to the preprocessor macros listed above, if any scheme is
+#   found, the preprocessor macro HAVE_SYS_WEAK_ALIAS will also be defined
+#   to 1.
+#
+#   Once a weak aliasing scheme has been found, a check will be performed to
+#   see if weak aliases are honored across object file boundaries. If they
+#   are, the HAVE_SYS_WEAK_ALIAS_CROSSFILE preprocessor macro is defined to
+#   1.
+#
+#   This Autoconf macro also makes two substitutions. The first, WEAK_ALIAS,
+#   contains the name of the scheme found (one of "attribute", "pragma",
+#   "hpsecondary", or "criduplicate"), or "no" if no weak aliasing scheme
+#   was found. The second, WEAK_ALIAS_CROSSFILE, is set to "yes" or "no"
+#   depending on whether or not weak aliases may cross object file
+#   boundaries.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Kevin L. Mitchell <klmitch@mit.edu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AU_ALIAS([KLM_SYS_WEAK_ALIAS], [AX_SYS_WEAK_ALIAS])
+AC_DEFUN([AX_SYS_WEAK_ALIAS], [
+  # starting point: no aliasing scheme yet...
+  ax_sys_weak_alias=no
+
+  # Figure out what kind of aliasing may be supported...
+  _AX_SYS_WEAK_ALIAS_ATTRIBUTE
+  _AX_SYS_WEAK_ALIAS_PRAGMA
+  _AX_SYS_WEAK_ALIAS_HPSECONDARY
+  _AX_SYS_WEAK_ALIAS_CRIDUPLICATE
+
+  # Do we actually support aliasing?
+  AC_CACHE_CHECK([how to create weak aliases with $CC],
+                 [ax_cv_sys_weak_alias],
+ [ax_cv_sys_weak_alias=$ax_sys_weak_alias])
+
+  # OK, set a #define
+  AS_IF([test $ax_cv_sys_weak_alias != no], [
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS], 1,
+              [Define this if your system can create weak aliases])
+  ])
+
+  # Can aliases cross object file boundaries?
+  _AX_SYS_WEAK_ALIAS_CROSSFILE
+
+  # OK, remember the results
+  AC_SUBST([WEAK_ALIAS], [$ax_cv_sys_weak_alias])
+  AC_SUBST([WEAK_ALIAS_CROSSFILE], [$ax_cv_sys_weak_alias_crossfile])
+])
+
+AC_DEFUN([_AX_SYS_WEAK_ALIAS_ATTRIBUTE],
+[ # Test whether compiler accepts __attribute__ form of weak aliasing
+  AC_CACHE_CHECK([whether $CC accepts function __attribute__((weak,alias()))],
+  [ax_cv_sys_weak_alias_attribute], [
+    # We add -Werror if it's gcc to force an error exit if the weak attribute
+    # isn't understood
+    AS_IF([test $GCC = yes], [
+      save_CFLAGS=$CFLAGS
+      CFLAGS=-Werror])
+
+    # Try linking with a weak alias...
+    AC_LINK_IFELSE([
+      AC_LANG_PROGRAM([
+void __weakf(int c) {}
+void weakf(int c) __attribute__((weak, alias("__weakf")));],
+        [weakf(0)])],
+      [ax_cv_sys_weak_alias_attribute=yes],
+      [ax_cv_sys_weak_alias_attribute=no])
+
+    # Restore original CFLAGS
+    AS_IF([test $GCC = yes], [
+      CFLAGS=$save_CFLAGS])
+  ])
+
+  # What was the result of the test?
+  AS_IF([test $ax_cv_sys_weak_alias_attribute = yes], [
+    test $ax_sys_weak_alias = no && ax_sys_weak_alias=attribute
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1,
+              [Define this if weak aliases may be created with __attribute__])
+  ])
+])
+
+AC_DEFUN([_AX_SYS_WEAK_ALIAS_PRAGMA],
+[ # Test whether compiler accepts #pragma form of weak aliasing
+  AC_CACHE_CHECK([whether $CC supports @%:@pragma weak],
+  [ax_cv_sys_weak_alias_pragma], [
+
+    # Try linking with a weak alias...
+    AC_LINK_IFELSE([
+      AC_LANG_PROGRAM([
+extern void weakf(int c);
+@%:@pragma weak weakf = __weakf
+void __weakf(int c) {}],
+        [weakf(0)])],
+      [ax_cv_sys_weak_alias_pragma=yes],
+      [ax_cv_sys_weak_alias_pragma=no])
+  ])
+
+  # What was the result of the test?
+  AS_IF([test $ax_cv_sys_weak_alias_pragma = yes], [
+    test $ax_sys_weak_alias = no && ax_sys_weak_alias=pragma
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1,
+              [Define this if weak aliases may be created with @%:@pragma weak])
+  ])
+])
+
+AC_DEFUN([_AX_SYS_WEAK_ALIAS_HPSECONDARY],
+[ # Test whether compiler accepts _HP_SECONDARY_DEF pragma from HP...
+  AC_CACHE_CHECK([whether $CC supports @%:@pragma _HP_SECONDARY_DEF],
+  [ax_cv_sys_weak_alias_hpsecondary], [
+
+    # Try linking with a weak alias...
+    AC_LINK_IFELSE([
+      AC_LANG_PROGRAM([
+extern void weakf(int c);
+@%:@pragma _HP_SECONDARY_DEF __weakf weakf
+void __weakf(int c) {}],
+        [weakf(0)])],
+      [ax_cv_sys_weak_alias_hpsecondary=yes],
+      [ax_cv_sys_weak_alias_hpsecondary=no])
+  ])
+
+  # What was the result of the test?
+  AS_IF([test $ax_cv_sys_weak_alias_hpsecondary = yes], [
+    test $ax_sys_weak_alias = no && ax_sys_weak_alias=hpsecondary
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1,
+              [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF])
+  ])
+])
+
+AC_DEFUN([_AX_SYS_WEAK_ALIAS_CRIDUPLICATE],
+[ # Test whether compiler accepts "_CRI duplicate" pragma from Cray
+  AC_CACHE_CHECK([whether $CC supports @%:@pragma _CRI duplicate],
+  [ax_cv_sys_weak_alias_criduplicate], [
+
+    # Try linking with a weak alias...
+    AC_LINK_IFELSE([
+      AC_LANG_PROGRAM([
+extern void weakf(int c);
+@%:@pragma _CRI duplicate weakf as __weakf
+void __weakf(int c) {}],
+        [weakf(0)])],
+      [ax_cv_sys_weak_alias_criduplicate=yes],
+      [ax_cv_sys_weak_alias_criduplicate=no])
+  ])
+
+  # What was the result of the test?
+  AS_IF([test $ax_cv_sys_weak_alias_criduplicate = yes], [
+    test $ax_sys_weak_alias = no && ax_sys_weak_alias=criduplicate
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1,
+              [Define this if weak aliases may be created with @%:@pragma _CRI duplicate])
+  ])
+])
+
+dnl Note: This macro is modeled closely on AC_LINK_IFELSE, and in fact
+dnl depends on some implementation details of that macro, particularly
+dnl its use of _AC_MSG_LOG_CONFTEST to log the failed test program and
+dnl its use of ac_link for running the linker.
+AC_DEFUN([_AX_SYS_WEAK_ALIAS_CROSSFILE],
+[ # Check to see if weak aliases can cross object file boundaries
+  AC_CACHE_CHECK([whether $CC supports weak aliases across object file boundaries],
+  [ax_cv_sys_weak_alias_crossfile], [
+    AS_IF([test $ax_cv_sys_weak_alias = no],
+          [ax_cv_sys_weak_alias_crossfile=no], [
+dnl Must build our own test files...
+      # conftest1 contains our weak alias definition...
+      cat >conftest1.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+      cat confdefs.h >>conftest1.$ac_ext
+      cat >>conftest1.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+@%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE
+extern void weakf(int c);
+@%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA)
+@%:@pragma weak weakf = __weakf
+@%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY)
+@%:@pragma _HP_SECONDARY_DEF __weakf weakf
+@%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE)
+@%:@pragma _CRI duplicate weakf as __weakf
+@%:@endif
+@%:@endif
+void __weakf(int c) {}
+@%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE
+void weakf(int c) __attribute((weak, alias("__weakf")));
+@%:@endif
+_ACEOF
+      # And conftest2 contains our main routine that calls it
+      cat >conftest2.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+      cat confdefs.h >> conftest2.$ac_ext
+      cat >>conftest2.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+extern void weakf(int c);
+int
+main ()
+{
+  weakf(0);
+  return 0;
+}
+_ACEOF
+      # We must remove the object files (if any) ourselves...
+      rm -f conftest2.$ac_objext conftest$ac_exeext
+
+      # Change ac_link to compile *2* files together
+      save_aclink=$ac_link
+      ac_link=`echo "$ac_link" | \
+               sed -e 's/conftest\(\.\$ac_ext\)/conftest1\1 conftest2\1/'`
+dnl Substitute our own routine for logging the conftest
+m4_pushdef([_AC_MSG_LOG_CONFTEST],
+[echo "$as_me: failed program was:" >&AS_MESSAGE_LOG_FD
+echo ">>> conftest1.$ac_ext" >&AS_MESSAGE_LOG_FD
+sed "s/^/| /" conftest1.$ac_ext >&AS_MESSAGE_LOG_FD
+echo ">>> conftest2.$ac_ext" >&AS_MESSAGE_LOG_FD
+sed "s/^/| /" conftest2.$ac_ext >&AS_MESSAGE_LOG_FD
+])dnl
+      # Since we created the files ourselves, don't use SOURCE argument
+      AC_LINK_IFELSE(, [ax_cv_sys_weak_alias_crossfile=yes],
+                     [ax_cv_sys_weak_alias_crossfile=no])
+dnl Restore _AC_MSG_LOG_CONFTEST
+m4_popdef([_AC_MSG_LOG_CONFTEST])dnl
+      # Restore ac_link
+      ac_link=$save_aclink
+
+      # We must remove the object files (if any) and C files ourselves...
+      rm -f conftest1.$ac_ext conftest2.$ac_ext \
+            conftest1.$ac_objext conftest2.$ac_objext
+    ])
+  ])
+
+  # What were the results of the test?
+  AS_IF([test $ax_cv_sys_weak_alias_crossfile = yes], [
+    AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CROSSFILE], 1,
+              [Define this if weak aliases in other files are honored])
+  ])
+])
diff --git a/nhrpd/Makefile.am b/nhrpd/Makefile.am
new file mode 100644 (file)
index 0000000..a9f70e2
--- /dev/null
@@ -0,0 +1,38 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DQUAGGA_NO_DEPRECATED_INTERFACES
+DEFS = @DEFS@ @CARES_CFLAGS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(PICFLAGS) #$(WERROR)
+AM_LDFLAGS = $(PICLDFLAGS)
+
+sbin_PROGRAMS = nhrpd
+
+nhrpd_SOURCES = \
+       zbuf.c \
+       znl.c \
+       resolver.c \
+       linux.c \
+       netlink_arp.c \
+       netlink_gre.c \
+       vici.c \
+       reqid.c \
+       nhrp_event.c \
+       nhrp_packet.c \
+       nhrp_interface.c \
+       nhrp_vc.c \
+       nhrp_peer.c \
+       nhrp_cache.c \
+       nhrp_nhs.c \
+       nhrp_route.c \
+       nhrp_shortcut.c \
+       nhrp_vty.c \
+       nhrp_main.c
+
+nhrpd_LDADD = ../lib/libzebra.la @LIBCAP@ @CARES_LIBS@
+
+noinst_HEADERS = debug.h netlink.h nhrpd.h vici.h znl.h list.h \
+       nhrp_protocol.h os.h zbuf.h
+
+#dist_examples_DATA = nhrpd.conf.sample
diff --git a/nhrpd/README.kernel b/nhrpd/README.kernel
new file mode 100644 (file)
index 0000000..3fe1f65
--- /dev/null
@@ -0,0 +1,147 @@
+LINUX KERNEL REQUIREMENTS
+=========================
+
+The linux kernel has had various major regressions, performance
+issues and subtle bugs (especially in pmtu). Here is a short list
+of some -stable kernels that have been tested (at least briefly)
+and seem to be working well with Quagga/NHRP:
+  3.12.8 or later
+  3.14.54 or later
+  3.18.22 or later[1]
+  4.4.52 or later
+  4.9.30 or later
+
+[1] But you need to apply the following two backported commits:
+    3cdaa5be9e ipv4: Don't increase PMTU with Datagram Too Big message
+    cb6ccf09d6 route: Use ipv4_mtu instead of raw rt_pmtu
+
+See below for list of known issues in various kernel versions.
+
+Kernels earlier than 3.12 need CONFIG_ARPD enabled in the configuration.
+Many distributions do not enable it by default, and you may need to
+compile your own kernel.
+
+KERNEL BUGS
+===========
+
+DMVPN and mGRE support in the kernel has been brittle. There are various
+regressions in multiple kernel versions.
+
+This list tries to collect them to one source of information:
+
+- forward pmtu is disabled intentionally (but tunnel devices rely on it)
+  Broken since 3.14-rc1:
+    commit "ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing"
+  Workaround:
+    Set sysctl net.ipv4.ip_forward_use_pmtu=1
+    (Should fix kernel to have this by default on for tunnel devices)
+
+- subtle path mtu mishandling issues
+  Broken since (uncertain)
+  Fixed in 4.1-rc2:
+    commit "ipv4: Don't increase PMTU with Datagram Too Big message."
+    commit "route: Use ipv4_mtu instead of raw rt_pmtu"
+
+- fragmentation of large packets inside tunnel not working
+  Broken since 3.11-rc1
+    commit "ip_tunnels: Use skb-len to PMTU check."
+  Fixed in 3.14.54, 3.18.22, 4.1.9, 4.2-rc3
+    commit "ip_tunnel: fix ipv4 pmtu check to honor inner ip header df"
+
+- ipsec will crash during xfrm gc
+  Broke since 3.15-rc1
+    commit "flowcache: Make flow cache name space aware"
+  Fixed in 3.18.10, 4.0
+    commit "flowcache: Fix kernel panic in flow_cache_flush_task"
+
+- TSO on GRE tunnels failed, and resulted in very slow performance
+  Broke since 3.14.24, 3.18-rc3
+    commit "gre: Use inner mac length when computing tunnel length"
+  Fixed in 3.14.30, 3.18.4
+    commit "gre: fix the inner mac header in nbma tunnel xmit path"
+    commit "gre: Set inner mac header in gro complete"
+
+- NAPI GRO handling was broken; causing immediate crash (32-bit only?)
+  Broken since 3.13-rc1
+    commit "net: gro: allow to build full sized skb"
+  Fixed 3.14.5, 3.15-rc7
+    commit "net: gro: make sure skb->cb[] initial content has not to be zero"
+
+- ip_gre dst caching broke NBMA GRE tunnels
+  Broken since 3.14-rc1
+  Fixed in 3.14.5, 3.15-rc6
+    commit "ipv4: ip_tunnels: disable cache for nbma gre tunnels"
+
+- Few packets can be lost when neighbor entry is in NUD_PROBE state,
+  and there is continuous traffic to it.
+  Broken since dawn of time
+  Fixed in 3.15-rc1
+    commit "neigh: probe application via netlink in NUD_PROBE"
+
+- GRO was implemented for GRE, but the hw capabilities were not updated
+  correctly. In practice forwarding from non-GRE (physical) interface
+  to GRE interface with gro/gso/tx offloads enabled (also on the target
+  interface) does not work properly.
+  Broken around 3.9 to 3.11, need to check details.
+
+- recvfrom() returned incorrect NBMA address, breaking NAT detection
+  Broken since 3.10-rc1
+    commit "GRE: Refactor GRE tunneling code."
+  Fixed in 3.10.27, 3.12.8, 3.13-rc7
+    commit "ip_gre: fix msg_name parsing for recvfrom/recvmsg"
+
+- sendto() was broken causing opennhrp not work at all
+  Broken since 3.10-rc1
+    commit "GRE: Refactor GRE tunneling code."
+  Fixed in 3.10.12, 3.11-rc6
+    commit "ip_gre: fix ipgre_header to return correct offset"
+
+- PMTU was broken due to GRE driver rewrite
+  Broken since 3.10-rc1
+    commit "GRE: Refactor GRE tunneling code."
+  Fixed in 3.11-rc1
+    commit "ip_tunnels: Use skb-len to PMTU check."
+
+- PMTU was broken due to routing cache removal
+  Broken since 3.6-rc1
+    commit "ipv4: Cache input routes in fib_info nexthops"
+  Fixed in 3.11-rc1
+    commit "ipv4: use next hop exceptions also for input routes"
+    + 3 other commits
+    Patches exist for 3.10, but they were not approved to 3.10-stable.
+
+- Race condition during bootup: changing ARP flag did not flush
+  existing neighbor entries, causing problems if traffic was routed
+  to gre interface before opennhrp was running.
+  Broken since dawn of time
+  Fixed in 3.11-rc1
+    commit "arp: flush arp cache on IFF_NOARP change"
+
+- Crash in IPsec
+  Broken since 3.9-rc1
+    commit "xfrm: removes a superfluous check and add a statistic"
+  Fixed in 3.10-rc3
+    commit "xfrm: properly handle invalid states as an error"
+
+- An incorrect ip_gre change broke NHRP traffic over GRE
+  Broken since 3.8-rc2
+    commit "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally"
+  Fixed in 3.8.5, 3.9-rc4
+    commit "Revert "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally""
+
+- Multicast traffic over mGRE was broken.
+  Broken since 2.6.34-rc2
+    commit "gre: fix hard header destination address checking"
+  Fixed in 2.6.39-rc2
+    commit "net: gre: provide multicast mappings for ipv4 and ipv6"
+
+- Serious performance issues causing small throughput on medium to large DMVPN networks
+  Broken since dawn of time
+  Fixed in 2.6.35
+    multiple commits rewriting ipsec caching
+
+- Even though around 2.6.24 is the first version where opennhrp started
+  to work, there has been various PMTU, performance, and functionality
+  bugs before 2.6.34. That's one of the first version I consider stable
+  wrt. to opennhrp functionality.
+
diff --git a/nhrpd/README.nhrpd b/nhrpd/README.nhrpd
new file mode 100644 (file)
index 0000000..af358fa
--- /dev/null
@@ -0,0 +1,140 @@
+Quagga / NHRP Design and Configuration Notes
+============================================
+
+Quagga/NHRP is an NHRP (RFC2332) implementation for Linux. The primary
+use case is to implement DMVPN. The aim is thus to be compatible with
+Cisco DMVPN (and potentially with FlexVPN in the future).
+
+
+Current Status
+--------------
+
+Implemented:
+- IPsec integration with strongSwan (requires patched strongSwan)
+- IPv4 over IPv4 NBMA GRE
+- IPv6 over IPv4 NBMA GRE -- majority of code exist; but is not tested
+- Spoke (NHC) functionality
+- Hub (NHS) functionality
+
+Not yet implemented:
+- NHRP Authentication
+- NHRP Groups
+- Multicast support (OSPF will not work)
+- Full Cisco FlexVPN compatibility (IKEv2 routing)
+
+
+Routing Design
+--------------
+
+In contrast to opennhrp routing design, Quagga/NHRP routes each NHRP
+domain address individually (similar to Cisco FlexVPN).
+
+To create NBMA GRE tunnel you might use following:
+       ip tunnel add gre1 mode gre key 42 ttl 64 dev eth0
+       ip addr add 10.255.255.2/32 dev gre1
+       ip link set gre1 up
+       sysctl net.ipv4.ip_forward_use_pmtu=1 #for kernels>=3.14
+
+This has two important differences compared to opennhrp setup:
+ 1. The 'tunnel add' now specifies physical device binding. Quagga/NHRP
+    wants to know stable protocol address to NBMA address mapping. Thus,
+    add 'dev <physdev>' binding, or specify 'local <nbma-address>'. If
+    neither of this is specified, NHRP will not be enabled on the interface.
+    Alternatively you can skip 'dev' binding on tunnel if you allow
+    nhrpd to manage it using 'tunnel source' command (see below).
+
+ 2. The 'addr add' now has host prefix. In opennhrp you would have used
+    the GRE subnet prefix length here instead, e.g. /24.
+
+Quagga/NHRP will automatically create additional host routes pointing to
+gre1 when a connection with these hosts is established. The gre1 subnet
+should be announced by routing protocol. This allows routing protocol
+to decide which is the closest hub and get the gre addresses' traffic.
+
+The second benefit is that hubs can then easily exchange host prefixes
+of directly connected gre addresses. And thus routing of gre addresses
+inside hubs is based on routing protocol's shortest path choice -- not
+on random choice from next hop server list.
+
+
+Configuring nhrpd
+-----------------
+
+The configuration is done using vtysh, and most commands do what they
+do in Cisco. As minimal configuration example one can do:
+ configure terminal
+ interface gre1
+   tunnel protection vici profile dmvpn
+   tunnel source eth0
+   ip nhrp network-id 1
+   ip nhrp shortcut
+   ip nhrp registration no-unique
+   ip nhrp nhs dynamic nbma hubs.example.com
+
+There's important notes about the "ip nhrp nhs" command:
+
+ 1. The 'dynamic' works only against Cisco (or nhrpd), but is not
+    compatible with opennhrp. To use dynamic detection of opennhrp hub's
+    protocol address use the GRE broadcast address there. For the above
+    example of 10.255.255.0/24 the configuration should read instead:
+      ip nhrp nhs 10.255.255.255 nbma hubs.example.com
+
+ 2. nbma <FQDN> works like opennhrp dynamic-map. That is, all of the
+    A-records are configured as NBMA addresses of different hubs, and
+    each hub protocol address will be dynamically detected.
+
+
+Hub functionality
+-----------------
+
+Sending Traffic Indication (redirect) notifications is now accomplished
+using NFLOG.
+
+Use:
+iptables -A FORWARD -i gre1 -o gre1 \
+       -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \
+       --hashlimit-mode srcip,dstip --hashlimit-srcmask 16 --hashlimit-dstmask 16 \
+       --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+
+or similar to get rate-limited samples of the packets that match traffic
+flow needing redirection. This kernel NFLOG target's nflog-group is configured
+in global nhrp config with:
+       nhrp nflog-group 1
+
+To start sending these traffic notices out from hubs, use the nhrp per-interface
+directive:
+       ip nhrp redirect
+
+opennhrp used PF_PACKET and tried to create packet filter to get only
+the packets of interest. Though, this was bad if shortcut fails to
+establish (remote policy, or both are behind NAT or restrictive
+firewalls), all of the relayaed traffic would match always.
+
+
+Getting information via vtysh
+-----------------------------
+
+Some commands of interest:
+ - show dmvpn
+ - show ip nhrp nhs
+ - show ip nhrp cache
+ - show ip nhrp shortcut
+ - show ip route nhrp
+ - clear ip nhrp cache
+ - clear ip nhrp shortcut
+
+
+Integration with strongSwan
+---------------------------
+
+Contrary to opennhrp, Quagga/NHRP has tight integration with IKE daemon.
+Currently strongSwan is supported using the VICI protocol. strongSwan
+is connected using UNIX socket (hardcoded now as /var/run/charon.vici).
+Thus nhrpd needs to be run as user that can open that file.
+
+Currently, you will need patched strongSwan. The working tree is at:
+       http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras
+
+And the branch with patches against latest release are:
+       http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release
+
diff --git a/nhrpd/debug.h b/nhrpd/debug.h
new file mode 100644 (file)
index 0000000..b1f49aa
--- /dev/null
@@ -0,0 +1,42 @@
+#include "log.h"
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define likely(_x) __builtin_expect(!!(_x), 1)
+#define unlikely(_x) __builtin_expect(!!(_x), 0)
+#else
+#define likely(_x) !!(_x)
+#define unlikely(_x) !!(_x)
+#endif
+
+#define NHRP_DEBUG_COMMON      (1 << 0)
+#define NHRP_DEBUG_KERNEL      (1 << 1)
+#define NHRP_DEBUG_IF          (1 << 2)
+#define NHRP_DEBUG_ROUTE       (1 << 3)
+#define NHRP_DEBUG_VICI                (1 << 4)
+#define NHRP_DEBUG_EVENT       (1 << 5)
+#define NHRP_DEBUG_ALL         (0xFFFF)
+
+extern unsigned int debug_flags;
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+
+#define debugf(level, ...) \
+       do {                                            \
+               if (unlikely(debug_flags & level))      \
+                       zlog_debug(__VA_ARGS__);        \
+       } while(0)
+
+#elif defined __GNUC__
+
+#define debugf(level, _args...)                                \
+       do {                                            \
+               if (unlikely(debug_flags & level))      \
+                       zlog_debug(_args);              \
+       } while(0)
+
+#else
+
+static inline void debugf(int level, const char *format, ...) { }
+
+#endif
+
diff --git a/nhrpd/linux.c b/nhrpd/linux.c
new file mode 100644 (file)
index 0000000..75a16ea
--- /dev/null
@@ -0,0 +1,153 @@
+/* NHRP daemon Linux specific glue
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
+
+#include "nhrp_protocol.h"
+#include "os.h"
+#include "netlink.h"
+
+static int nhrp_socket_fd = -1;
+
+int os_socket(void)
+{
+       if (nhrp_socket_fd < 0)
+               nhrp_socket_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_NHRP));
+       return nhrp_socket_fd;
+}
+
+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen)
+{
+       struct sockaddr_ll lladdr;
+       struct iovec iov = {
+               .iov_base = (void*) buf,
+               .iov_len = len,
+       };
+       struct msghdr msg = {
+               .msg_name = &lladdr,
+               .msg_namelen = sizeof(lladdr),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+       };
+       int status;
+
+       if (addrlen > sizeof(lladdr.sll_addr))
+               return -1;
+
+       memset(&lladdr, 0, sizeof(lladdr));
+       lladdr.sll_family = AF_PACKET;
+       lladdr.sll_protocol = htons(ETH_P_NHRP);
+       lladdr.sll_ifindex = ifindex;
+       lladdr.sll_halen = addrlen;
+       memcpy(lladdr.sll_addr, addr, addrlen);
+
+       status = sendmsg(nhrp_socket_fd, &msg, 0);
+       if (status < 0)
+               return -1;
+
+       return 0;
+}
+
+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen)
+{
+       struct sockaddr_ll lladdr;
+       struct iovec iov = {
+               .iov_base = buf,
+               .iov_len = *len,
+       };
+       struct msghdr msg = {
+               .msg_name = &lladdr,
+               .msg_namelen = sizeof(lladdr),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+       };
+       int r;
+
+       r = recvmsg(nhrp_socket_fd, &msg, MSG_DONTWAIT);
+       if (r < 0)
+               return r;
+
+       *len = r;
+       *ifindex = lladdr.sll_ifindex;
+
+       if (*addrlen <= (size_t) lladdr.sll_addr) {
+               if (memcmp(lladdr.sll_addr, "\x00\x00\x00\x00", 4) != 0) {
+                       memcpy(addr, lladdr.sll_addr, lladdr.sll_halen);
+                       *addrlen = lladdr.sll_halen;
+               } else {
+                       *addrlen = 0;
+               }
+       }
+
+       return 0;
+}
+
+static int linux_configure_arp(const char *iface, int on)
+{
+       struct ifreq ifr;
+
+       strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+       if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr))
+               return -1;
+
+       if (on)
+               ifr.ifr_flags &= ~IFF_NOARP;
+       else
+               ifr.ifr_flags |= IFF_NOARP;
+
+       if (ioctl(nhrp_socket_fd, SIOCSIFFLAGS, &ifr))
+               return -1;
+
+       return 0;
+}
+
+static int linux_icmp_redirect_off(const char *iface)
+{
+       char fname[256];
+       int fd, ret = -1;
+
+       sprintf(fname, "/proc/sys/net/ipv4/conf/%s/send_redirects", iface);
+       fd = open(fname, O_WRONLY);
+       if (fd < 0)
+               return -1;
+       if (write(fd, "0\n", 2) == 2)
+               ret = 0;
+       close(fd);
+
+       return ret;
+}
+
+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af)
+{
+       int ret = 0;
+
+       switch (af) {
+       case AF_INET:
+               ret |= linux_icmp_redirect_off("all");
+               ret |= linux_icmp_redirect_off(ifname);
+               break;
+       }
+       ret |= linux_configure_arp(ifname, 1);
+       ret |= netlink_configure_arp(ifindex, af);
+
+       return ret;
+}
diff --git a/nhrpd/list.h b/nhrpd/list.h
new file mode 100644 (file)
index 0000000..32f21ed
--- /dev/null
@@ -0,0 +1,191 @@
+/* Linux kernel style list handling function
+ *
+ * Written from scratch by Timo Teräs <timo.teras@iki.fi>, but modeled
+ * after the linux kernel code.
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next;
+       struct hlist_node **pprev;
+};
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline int hlist_hashed(const struct hlist_node *n)
+{
+       return n->pprev != NULL;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+
+       n->next = NULL;
+       n->pprev = NULL;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       n->pprev = &h->first;
+       h->first = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
+{
+       n->next = prev->next;
+       n->pprev = &prev->next;
+       prev->next = n;
+}
+
+static inline struct hlist_node **hlist_tail_ptr(struct hlist_head *h)
+{
+       struct hlist_node *n = h->first;
+       if (n == NULL)
+               return &h->first;
+       while (n->next != NULL)
+               n = n->next;
+       return &n->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); pos = n)
+
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first; pos &&                                 \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_INITIALIZER(l) { .next = &l, .prev = &l }
+
+static inline void list_init(struct list_head *list)
+{
+       list->next = list;
+       list->prev = list;
+}
+
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = NULL;
+       entry->prev = NULL;
+}
+
+static inline int list_hashed(const struct list_head *n)
+{
+       return n->next != n && n->next != NULL;
+}
+
+static inline int list_empty(const struct list_head *n)
+{
+       return !list_hashed(n);
+}
+
+#define list_next(ptr, type, member) \
+       (list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL)
+
+#define list_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif
diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h
new file mode 100644 (file)
index 0000000..f05596b
--- /dev/null
@@ -0,0 +1,24 @@
+/* NHRP netlink/neighbor table API
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdint.h>
+
+union sockunion;
+struct interface;
+
+extern int netlink_nflog_group;
+extern int netlink_req_fd;
+
+int netlink_init(void);
+int netlink_configure_arp(unsigned int ifindex, int pf);
+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma);
+void netlink_set_nflog_group(int nlgroup);
+
+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr);
+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index);
diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c
new file mode 100644 (file)
index 0000000..a418eca
--- /dev/null
@@ -0,0 +1,275 @@
+/* NHRP netlink/neighbor table arpd code
+ * Copyright (c) 2014-2016 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fcntl.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <linux/netlink.h>
+#include <linux/neighbour.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+#include "thread.h"
+#include "nhrpd.h"
+#include "netlink.h"
+#include "znl.h"
+
+int netlink_req_fd = -1;
+int netlink_nflog_group;
+static int netlink_log_fd = -1;
+static struct thread *netlink_log_thread;
+static int netlink_listen_fd = -1;
+
+typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb);
+
+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma)
+{
+       struct nlmsghdr *n;
+       struct ndmsg *ndm;
+       struct zbuf *zb = zbuf_alloc(512);
+
+       n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+       ndm = znl_push(zb, sizeof(*ndm));
+       *ndm = (struct ndmsg) {
+               .ndm_family = sockunion_family(proto),
+               .ndm_ifindex = ifp->ifindex,
+               .ndm_type = RTN_UNICAST,
+               .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED,
+       };
+       znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), family2addrsize(sockunion_family(proto)));
+       if (nbma)
+               znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), family2addrsize(sockunion_family(nbma)));
+       znl_nlmsg_complete(zb, n);
+       zbuf_send(zb, netlink_req_fd);
+       zbuf_recv(zb, netlink_req_fd);
+       zbuf_free(zb);
+}
+
+static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb)
+{
+       struct ndmsg *ndm;
+       struct rtattr *rta;
+       struct nhrp_cache *c;
+       struct interface *ifp;
+       struct zbuf payload;
+       union sockunion addr;
+       size_t len;
+       char buf[SU_ADDRSTRLEN];
+       int state;
+
+       ndm = znl_pull(zb, sizeof(*ndm));
+       if (!ndm) return;
+
+       sockunion_family(&addr) = AF_UNSPEC;
+       while ((rta = znl_rta_pull(zb, &payload)) != NULL) {
+               len = zbuf_used(&payload);
+               switch (rta->rta_type) {
+               case NDA_DST:
+                       sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len);
+                       break;
+               }
+       }
+
+       ifp = if_lookup_by_index(ndm->ndm_ifindex);
+       if (!ifp || sockunion_family(&addr) == AF_UNSPEC)
+               return;
+
+       c = nhrp_cache_get(ifp, &addr, 0);
+       if (!c)
+               return;
+
+       if (msg->nlmsg_type == RTM_GETNEIGH) {
+               debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s",
+                       sockunion2str(&addr, buf, sizeof buf),
+                       ifp->name);
+
+               if (c->cur.type >= NHRP_CACHE_CACHED) {
+                       nhrp_cache_set_used(c, 1);
+                       netlink_update_binding(ifp, &addr, &c->cur.peer->vc->remote.nbma);
+               }
+       } else {
+               debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x",
+                       sockunion2str(&addr, buf, sizeof buf),
+                       ifp->name, ndm->ndm_state);
+
+               state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED;
+               nhrp_cache_set_used(c, state == NUD_REACHABLE);
+       }
+}
+
+static int netlink_route_recv(struct thread *t)
+{
+       uint8_t buf[ZNL_BUFFER_SIZE];
+       int fd = THREAD_FD(t);
+       struct zbuf payload, zb;
+       struct nlmsghdr *n;
+
+       zbuf_init(&zb, buf, sizeof(buf), 0);
+       while (zbuf_recv(&zb, fd) > 0) {
+               while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
+                       debugf(NHRP_DEBUG_KERNEL, "Netlink: Received msg_type %u, msg_flags %u",
+                               n->nlmsg_type, n->nlmsg_flags);
+                       switch (n->nlmsg_type) {
+                       case RTM_GETNEIGH:
+                       case RTM_NEWNEIGH:
+                       case RTM_DELNEIGH:
+                               netlink_neigh_msg(n, &payload);
+                               break;
+                       }
+               }
+       }
+
+       thread_add_read(master, netlink_route_recv, 0, fd);
+
+       return 0;
+}
+
+static void netlink_log_register(int fd, int group)
+{
+       struct nlmsghdr *n;
+       struct nfgenmsg *nf;
+       struct nfulnl_msg_config_cmd cmd;
+       struct zbuf *zb = zbuf_alloc(512);
+
+       n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_CONFIG, NLM_F_REQUEST | NLM_F_ACK);
+       nf = znl_push(zb, sizeof(*nf));
+       *nf = (struct nfgenmsg) {
+               .nfgen_family = AF_UNSPEC,
+               .version = NFNETLINK_V0,
+               .res_id = htons(group),
+       };
+       cmd.command = NFULNL_CFG_CMD_BIND;
+       znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
+       znl_nlmsg_complete(zb, n);
+
+       zbuf_send(zb, fd);
+       zbuf_free(zb);
+}
+
+static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb)
+{
+       struct nfgenmsg *nf;
+       struct rtattr *rta;
+       struct zbuf rtapl, pktpl;
+       struct interface *ifp;
+       struct nfulnl_msg_packet_hdr *pkthdr = NULL;
+       uint32_t *in_ndx = NULL;
+
+       nf = znl_pull(zb, sizeof(*nf));
+       if (!nf) return;
+
+       memset(&pktpl, 0, sizeof(pktpl));
+       while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
+               switch (rta->rta_type) {
+               case NFULA_PACKET_HDR:
+                       pkthdr = znl_pull(&rtapl, sizeof(*pkthdr));
+                       break;
+               case NFULA_IFINDEX_INDEV:
+                       in_ndx = znl_pull(&rtapl, sizeof(*in_ndx));
+                       break;
+               case NFULA_PAYLOAD:
+                       pktpl = rtapl;
+                       break;
+               /* NFULA_HWHDR exists and is supposed to contain source
+                * hardware address. However, for ip_gre it seems to be
+                * the nexthop destination address if the packet matches
+                * route. */
+               }
+       }
+
+       if (!pkthdr || !in_ndx || !zbuf_used(&pktpl))
+               return;
+
+       ifp = if_lookup_by_index(htonl(*in_ndx));
+       if (!ifp)
+               return;
+
+       nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl);
+}
+
+static int netlink_log_recv(struct thread *t)
+{
+       uint8_t buf[ZNL_BUFFER_SIZE];
+       int fd = THREAD_FD(t);
+       struct zbuf payload, zb;
+       struct nlmsghdr *n;
+
+       netlink_log_thread = NULL;
+
+       zbuf_init(&zb, buf, sizeof(buf), 0);
+       while (zbuf_recv(&zb, fd) > 0) {
+               while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
+                       debugf(NHRP_DEBUG_KERNEL, "Netlink-log: Received msg_type %u, msg_flags %u",
+                               n->nlmsg_type, n->nlmsg_flags);
+                       switch (n->nlmsg_type) {
+                       case (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_PACKET:
+                               netlink_log_indication(n, &payload);
+                               break;
+                       }
+               }
+       }
+
+       THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
+
+       return 0;
+}
+
+void netlink_set_nflog_group(int nlgroup)
+{
+       if (netlink_log_fd >= 0) {
+               THREAD_OFF(netlink_log_thread);
+               close(netlink_log_fd);
+               netlink_log_fd = -1;
+       }
+       netlink_nflog_group = nlgroup;
+       if (nlgroup) {
+               netlink_log_fd = znl_open(NETLINK_NETFILTER,  0);
+               netlink_log_register(netlink_log_fd, nlgroup);
+               THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
+       }
+}
+
+int netlink_init(void)
+{
+       netlink_req_fd = znl_open(NETLINK_ROUTE, 0);
+       netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH);
+       thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd);
+
+       return 0;
+}
+
+int netlink_configure_arp(unsigned int ifindex, int pf)
+{
+       struct nlmsghdr *n;
+       struct ndtmsg *ndtm;
+       struct rtattr *rta;
+       struct zbuf *zb = zbuf_alloc(512);
+       int r;
+
+       n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE);
+       ndtm = znl_push(zb, sizeof(*ndtm));
+       *ndtm = (struct ndtmsg) {
+               .ndtm_family = pf,
+       };
+
+       znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", 10);
+
+       rta = znl_rta_nested_push(zb, NDTA_PARMS);
+       znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex);
+       znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1);
+       znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0);
+       znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0);
+       znl_rta_nested_complete(zb, rta);
+
+       znl_nlmsg_complete(zb, n);
+       r = zbuf_send(zb, netlink_req_fd);
+       zbuf_recv(zb, netlink_req_fd);
+       zbuf_free(zb);
+
+       return r;
+}
diff --git a/nhrpd/netlink_gre.c b/nhrpd/netlink_gre.c
new file mode 100644 (file)
index 0000000..93998dc
--- /dev/null
@@ -0,0 +1,142 @@
+/* NHRP netlink/GRE tunnel configuration code
+ * Copyright (c) 2014-2016 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_tunnel.h>
+
+#include "debug.h"
+#include "netlink.h"
+#include "znl.h"
+
+static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex)
+{
+       struct nlmsghdr *n;
+       struct ifinfomsg *ifi;
+       struct zbuf payload, rtapayload;
+       struct rtattr *rta;
+
+       debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex);
+
+       n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST);
+       ifi = znl_push(zb, sizeof(*ifi));
+       *ifi = (struct ifinfomsg) {
+               .ifi_index = ifindex,
+       };
+       znl_nlmsg_complete(zb, n);
+
+       if (zbuf_send(zb, netlink_req_fd) < 0 ||
+           zbuf_recv(zb, netlink_req_fd) < 0)
+               return -1;
+
+       n = znl_nlmsg_pull(zb, &payload);
+       if (!n) return -1;
+
+       if (n->nlmsg_type != RTM_NEWLINK)
+               return -1;
+
+       ifi = znl_pull(&payload, sizeof(struct ifinfomsg));
+       if (!ifi)
+               return -1;
+
+       debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u",
+               ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags);
+
+       if (ifi->ifi_index != ifindex)
+               return -1;
+
+       while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
+               if (rta->rta_type == IFLA_LINKINFO)
+                       break;
+       if (!rta) return -1;
+
+       payload = rtapayload;
+       while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
+               if (rta->rta_type == IFLA_INFO_DATA)
+                       break;
+       if (!rta) return -1;
+
+       *data = rtapayload;
+       return 0;
+}
+
+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr)
+{
+       struct zbuf *zb = zbuf_alloc(8192), data, rtapl;
+       struct rtattr *rta;
+
+       *link_index = 0;
+       *gre_key = 0;
+       saddr->s_addr = 0;
+
+       if (__netlink_gre_get_data(zb, &data, ifindex) < 0)
+               goto err;
+
+       while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
+               switch (rta->rta_type) {
+               case IFLA_GRE_LINK:
+                       *link_index = zbuf_get32(&rtapl);
+                       break;
+               case IFLA_GRE_IKEY:
+               case IFLA_GRE_OKEY:
+                       *gre_key = zbuf_get32(&rtapl);
+                       break;
+               case IFLA_GRE_LOCAL:
+                       saddr->s_addr = zbuf_get32(&rtapl);
+                       break;
+               }
+       }
+err:
+       zbuf_free(zb);
+}
+
+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index)
+{
+       struct nlmsghdr *n;
+       struct ifinfomsg *ifi;
+       struct rtattr *rta_info, *rta_data, *rta;
+       struct zbuf *zr = zbuf_alloc(8192), data, rtapl;
+       struct zbuf *zb = zbuf_alloc(8192);
+       size_t len;
+
+       if (__netlink_gre_get_data(zr, &data, ifindex) < 0)
+               goto err;
+
+       n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST);
+       ifi = znl_push(zb, sizeof(*ifi));
+       *ifi = (struct ifinfomsg) {
+               .ifi_index = ifindex,
+       };
+       rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO);
+       znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3);
+       rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA);
+
+       znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index);
+       while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
+               if (rta->rta_type == IFLA_GRE_LINK)
+                       continue;
+               len = zbuf_used(&rtapl);
+               znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len);
+       }
+
+       znl_rta_nested_complete(zb, rta_data);
+       znl_rta_nested_complete(zb, rta_info);
+
+       znl_nlmsg_complete(zb, n);
+       zbuf_send(zb, netlink_req_fd);
+       zbuf_recv(zb, netlink_req_fd);
+err:
+       zbuf_free(zb);
+       zbuf_free(zr);
+}
diff --git a/nhrpd/nhrp-events.lua b/nhrpd/nhrp-events.lua
new file mode 100755 (executable)
index 0000000..e5e3bbf
--- /dev/null
@@ -0,0 +1,272 @@
+#!/usr/bin/lua5.2
+
+-- Example NHRP events processing script which validates
+-- NHRP registration GRE address against certificate subjectAltName IP
+-- and auto-creates BGP pairings and filters based on sbgp extensions.
+
+-- Depends on lua5.2 lua5.2-posix lua5.2-cqueues lua5.2-ossl lua-asn1
+
+local posix = require 'posix'
+local struct = require 'struct'
+local cq = require 'cqueues'
+local cqs = require 'cqueues.socket'
+local x509 = require 'openssl.x509'
+local x509an = require 'openssl.x509.altname'
+local rfc3779 = require 'asn1.rfc3779'
+
+local SOCK = "/var/run/nhrp-events.sock"
+posix.unlink(SOCK)
+
+local loop = cq.new()
+local nulfd = posix.open("/dev/null", posix.O_RDWR)
+local listener = cqs.listen{path=SOCK}
+
+posix.chown(SOCK, "quagga", "quagga")
+posix.setpid("u", "quagga")
+posix.setpid("g", "quagga")
+posix.openlog("nhrp-events", "np")
+
+function string.hex2bin(str)
+       return str:gsub('..', function(cc) return string.char(tonumber(cc, 16)) end)
+end
+
+local function decode_ext(cert, name, tpe)
+       local ext = cert:getExtension(name)
+       if not ext then return end
+       return tpe.decode(ext:getData())
+end
+
+local function do_parse_cert(cert, out)
+       for type, value in pairs(cert:getSubjectAlt()) do
+               if type == 'IP' then
+                       table.insert(out.GRE, value)
+               end
+       end
+       if #out.GRE == 0 then return end
+
+       local asn = decode_ext(cert, 'sbgp-autonomousSysNum', rfc3779.ASIdentifiers)
+       if asn and asn.asnum and asn.asnum.asIdsOrRanges then
+               for _, as in ipairs(asn.asnum.asIdsOrRanges) do
+                       if as.id then
+                               out.AS = tonumber(as.id)
+                               break
+                       end
+               end
+       end
+
+       local addrBlocks = decode_ext(cert, 'sbgp-ipAddrBlock', rfc3779.IPAddrBlocks)
+       for _, ab in ipairs(addrBlocks or {}) do
+               if ab.ipAddressChoice and ab.ipAddressChoice.addressesOrRanges then
+                       for _, a in ipairs(ab.ipAddressChoice.addressesOrRanges) do
+                               if a.addressPrefix then
+                                       table.insert(out.NET, a.addressPrefix)
+                               end
+                       end
+               end
+       end
+
+       return true
+end
+
+local function parse_cert(certhex)
+       local out = {
+               cn = "(no CN)",
+               AS = 0,
+               GRE = {},
+               NET = {},
+       }
+       local cert = x509.new(certhex:hex2bin(), 'der')
+       out.cn = tostring(cert:getSubject())
+       -- Recognize hubs by certificate's CN to have OU=Hubs
+       out.hub = out.cn:match("/OU=Hubs/") and true or nil
+       do_parse_cert(cert, out)
+       return out
+end
+
+local function execute(desc, cmd, ...)
+       local piper, pipew = posix.pipe()
+       if piper == nil then
+               return error("Pipe failed")
+       end
+
+       local pid = posix.fork()
+       if pid == -1 then
+               return error("Fork failed")
+       end
+       if pid == 0 then
+               posix.close(piper)
+               posix.dup2(nulfd, 0)
+               posix.dup2(pipew, 1)
+               posix.dup2(nulfd, 2)
+               posix.execp(cmd, ...)
+               os.exit(1)
+       end
+       posix.close(pipew)
+
+       -- This blocks -- perhaps should handle command executions in separate queue.
+       local output = {}
+       while true do
+               local d = posix.read(piper, 8192)
+               if d == nil or d == "" then break end
+               table.insert(output, d)
+       end
+       posix.close(piper)
+
+       local _, reason, status = posix.wait(pid)
+       if status == 0 then
+               posix.syslog(6, ("Executed '%s' successfully"):format(desc))
+       else
+               posix.syslog(3, ("Failed to execute '%s': %s %d"):format(desc, reason, status))
+       end
+       return status, table.concat(output)
+end
+
+local function configure_bgp(desc, ...)
+       local args = {
+               "-d", "bgpd",
+               "-c", "configure terminal",
+       }
+       for _, val in ipairs({...}) do
+               table.insert(args, "-c")
+               table.insert(args, val)
+       end
+       return execute(desc, "vtysh", table.unpack(args))
+end
+
+local last_bgp_reset = 0
+
+local function bgp_reset(msg, local_cert)
+       local now = os.time()
+       if last_bgp_reset + 60 > now then return end
+       last_bgp_reset = now
+
+       configure_bgp("spoke reset",
+               "route-map RTT-SET permit 10", "set metric rtt", "exit",
+               "route-map RTT-ADD permit 10", "set metric +rtt", "exit",
+               ("router bgp %d"):format(local_cert.AS),
+               "no neighbor hubs",
+               "neighbor hubs peer-group",
+               "neighbor hubs remote-as 65000",
+               "neighbor hubs ebgp-multihop 1",
+               "neighbor hubs disable-connected-check",
+               "neighbor hubs timers 10 30",
+               "neighbor hubs timers connect 10",
+               "neighbor hubs next-hop-self all",
+               "neighbor hubs soft-reconfiguration inbound",
+               "neighbor hubs route-map RTT-ADD in")
+end
+
+local function bgp_nhs_up(msg, remote_cert, local_cert)
+       configure_bgp(("nhs-up %s"):format(msg.remote_addr),
+               ("router bgp %s"):format(local_cert.AS),
+               ("neighbor %s peer-group hubs"):format(msg.remote_addr))
+end
+
+local function bgp_nhs_down(msg, remote_cert, local_cert)
+       configure_bgp(("nhs-down %s"):format(msg.remote_addr),
+               ("router bgp %s"):format(local_cert.AS),
+               ("no neighbor %s"):format(msg.remote_addr))
+end
+
+local function bgp_create_spoke_rules(msg, remote_cert, local_cert)
+       if not local_cert.hub then return end
+
+       local bgpcfg = {}
+       for seq, net in ipairs(remote_cert.NET) do
+               table.insert(bgpcfg,
+                       ("ip prefix-list net-%s-in seq %d permit %s le %d"):format(
+                               msg.remote_addr, seq * 5, net,
+                               remote_cert.hub and 32 or 26))
+       end
+       table.insert(bgpcfg, ("router bgp %s"):format(local_cert.AS))
+       if remote_cert.hub then
+               table.insert(bgpcfg, ("neighbor %s peer-group hubs"):format(msg.remote_addr))
+       elseif local_cert.AS == remote_cert.AS then
+               table.insert(bgpcfg, ("neighbor %s peer-group spoke-ibgp"):format(msg.remote_addr))
+       else
+               table.insert(bgpcfg, ("neighbor %s remote-as %s"):format(msg.remote_addr, remote_cert.AS))
+               table.insert(bgpcfg, ("neighbor %s peer-group spoke-ebgp"):format(msg.remote_addr))
+       end
+       table.insert(bgpcfg, ("neighbor %s prefix-list net-%s-in in"):format(msg.remote_addr, msg.remote_addr))
+
+       local status, output = configure_bgp(("nhc-register %s"):format(msg.remote_addr), table.unpack(bgpcfg))
+       if output:find("Cannot") then
+               posix.syslog(6, "BGP: "..output)
+               configure_bgp(
+                       ("nhc-recreate %s"):format(msg.remote_addr),
+                       ("router bgp %s"):format(local_cert.AS),
+                       ("no neighbor %s"):format(msg.remote_addr),
+                       table.unpack(bgpcfg))
+       end
+end
+
+local function handle_message(msg)
+       if msg.event ~= "authorize-binding" then return end
+
+       -- Verify protocol address against certificate
+       local auth = false
+       local local_cert = parse_cert(msg.local_cert)
+       local remote_cert = parse_cert(msg.remote_cert)
+       for _, gre in pairs(remote_cert.GRE) do
+               if gre == msg.remote_addr then auth = true end
+       end
+       if not auth then
+               posix.syslog(3, ("GRE %s to NBMA %s DENIED (cert '%s', allows: %s)"):format(
+                       msg.remote_addr, msg.remote_nbma,
+                       remote_cert.cn, table.concat(remote_cert.GRE, " ")))
+               return "deny"
+       end
+       posix.syslog(6, ("GRE %s to NBMA %s authenticated for %s"):format(
+               msg.remote_addr, msg.remote_nbma, remote_cert.cn))
+
+       -- Automatic BGP binding for hub-spoke connections
+       if msg.type == "nhs" and msg.old_type ~= "nhs" then
+               if not local_cert.hub then
+                       if tonumber(msg.num_nhs) == 0 and msg.vc_initiated == "yes" then
+                               bgp_reset(msg, local_cert)
+                       end
+                       bgp_nhs_up(msg, remote_cert, local_cert)
+               else
+                       bgp_create_spoke_rules(msg, remote_cert, local_cert)
+               end
+       elseif msg.type ~= "nhs" and msg.old_type == "nhs" then
+               bgp_nhs_down(msg, remote_cert, local_cert)
+       elseif msg.type == "dynamic" and msg.old_type ~= "dynamic" then
+               bgp_create_spoke_rules(msg, remote_cert, local_cert)
+       end
+
+       return "accept"
+end
+
+local function handle_connection(conn)
+       local msg = {}
+       for l in conn:lines() do
+               if l == "" then
+                       res = handle_message(msg)
+                       if msg.eventid then
+                               conn:write(("eventid=%s\nresult=%s\n\n"):format(msg.eventid, res or "default"))
+                       end
+                       msg = {}
+               else
+                       local key, value = l:match('([^=]*)=(.*)')
+                       if key and value then
+                               msg[key] = value
+                       end
+               end
+       end
+       conn:close()
+end
+
+loop:wrap(function()
+       while true do
+               local conn = listener:accept()
+               conn:setmode("b", "bl")
+               loop:wrap(function()
+                       local ok, msg = pcall(handle_connection, conn)
+                       if not ok then posix.syslog(3, msg) end
+                       conn:close()
+               end)
+       end
+end)
+
+print(loop:loop())
diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c
new file mode 100644 (file)
index 0000000..447a814
--- /dev/null
@@ -0,0 +1,341 @@
+/* NHRP cache
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zebra.h"
+#include "memory.h"
+#include "thread.h"
+#include "hash.h"
+#include "nhrpd.h"
+
+#include "netlink.h"
+
+unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
+const char * const nhrp_cache_type_str[] = {
+       [NHRP_CACHE_INVALID]    = "invalid",
+       [NHRP_CACHE_INCOMPLETE] = "incomplete",
+       [NHRP_CACHE_NEGATIVE]   = "negative",
+       [NHRP_CACHE_CACHED]     = "cached",
+       [NHRP_CACHE_DYNAMIC]    = "dynamic",
+       [NHRP_CACHE_NHS]        = "nhs",
+       [NHRP_CACHE_STATIC]     = "static",
+       [NHRP_CACHE_LOCAL]      = "local",
+};
+
+static unsigned int nhrp_cache_protocol_key(void *peer_data)
+{
+       struct nhrp_cache *p = peer_data;
+       return sockunion_hash(&p->remote_addr);
+}
+
+static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data)
+{
+       const struct nhrp_cache *a = cache_data;
+       const struct nhrp_cache *b = key_data;
+       return sockunion_same(&a->remote_addr, &b->remote_addr);
+}
+
+static void *nhrp_cache_alloc(void *data)
+{
+       struct nhrp_cache *p, *key = data;
+
+       p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
+       if (p) {
+               *p = (struct nhrp_cache) {
+                       .cur.type = NHRP_CACHE_INVALID,
+                       .new.type = NHRP_CACHE_INVALID,
+                       .remote_addr = key->remote_addr,
+                       .ifp = key->ifp,
+                       .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
+               };
+               nhrp_cache_counts[p->cur.type]++;
+       }
+
+       return p;
+}
+
+static void nhrp_cache_free(struct nhrp_cache *c)
+{
+       struct nhrp_interface *nifp = c->ifp->info;
+
+       zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
+       zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
+       nhrp_cache_counts[c->cur.type]--;
+       notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
+       zassert(!notifier_active(&c->notifier_list));
+       hash_release(nifp->cache_hash, c);
+       XFREE(MTYPE_NHRP_CACHE, c);
+}
+
+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_cache key;
+
+       if (!nifp->cache_hash) {
+               nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp);
+               if (!nifp->cache_hash)
+                       return NULL;
+       }
+
+       key.remote_addr = *remote_addr;
+       key.ifp = ifp;
+
+       return hash_get(nifp->cache_hash, &key, create ? nhrp_cache_alloc : NULL);
+}
+
+static int nhrp_cache_do_free(struct thread *t)
+{
+       struct nhrp_cache *c = THREAD_ARG(t);
+       c->t_timeout = NULL;
+       nhrp_cache_free(c);
+       return 0;
+}
+
+static int nhrp_cache_do_timeout(struct thread *t)
+{
+       struct nhrp_cache *c = THREAD_ARG(t);
+       c->t_timeout = NULL;
+       if (c->cur.type != NHRP_CACHE_INVALID)
+               nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+       return 0;
+}
+
+static void nhrp_cache_update_route(struct nhrp_cache *c)
+{
+       struct prefix pfx;
+       struct nhrp_peer *p = c->cur.peer;
+
+       sockunion2hostprefix(&c->remote_addr, &pfx);
+
+       if (p && nhrp_peer_check(p, 1)) {
+               netlink_update_binding(p->ifp, &c->remote_addr, &p->vc->remote.nbma);
+               nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu);
+               if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
+                       nhrp_route_update_nhrp(&pfx, c->ifp);
+                       c->nhrp_route_installed = 1;
+               } else if (c->nhrp_route_installed) {
+                       nhrp_route_update_nhrp(&pfx, NULL);
+                       c->nhrp_route_installed = 0;
+               }
+               if (!c->route_installed) {
+                       notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
+                       c->route_installed = 1;
+               }
+       } else {
+               if (c->nhrp_route_installed) {
+                       nhrp_route_update_nhrp(&pfx, NULL);
+                       c->nhrp_route_installed = 0;
+               }
+               if (c->route_installed) {
+                       sockunion2hostprefix(&c->remote_addr, &pfx);
+                       notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
+                       nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL, 0);
+                       c->route_installed = 0;
+               }
+       }
+}
+
+static void nhrp_cache_peer_notifier(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_cache *c = container_of(n, struct nhrp_cache, peer_notifier);
+
+       switch (cmd) {
+       case NOTIFY_PEER_UP:
+               nhrp_cache_update_route(c);
+               break;
+       case NOTIFY_PEER_DOWN:
+       case NOTIFY_PEER_IFCONFIG_CHANGED:
+               notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
+               nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+               break;
+       case NOTIFY_PEER_NBMA_CHANGING:
+               if (c->cur.type == NHRP_CACHE_DYNAMIC)
+                       c->cur.peer->vc->abort_migration = 1;
+               break;
+       }
+}
+
+static void nhrp_cache_reset_new(struct nhrp_cache *c)
+{
+       THREAD_OFF(c->t_auth);
+       if (list_hashed(&c->newpeer_notifier.notifier_entry))
+               nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
+       nhrp_peer_unref(c->new.peer);
+       memset(&c->new, 0, sizeof(c->new));
+       c->new.type = NHRP_CACHE_INVALID;
+}
+
+static void nhrp_cache_update_timers(struct nhrp_cache *c)
+{
+       THREAD_OFF(c->t_timeout);
+
+       switch (c->cur.type) {
+       case NHRP_CACHE_INVALID:
+               if (!c->t_auth)
+                       THREAD_TIMER_MSEC_ON(master, c->t_timeout, nhrp_cache_do_free, c, 10);
+               break;
+       default:
+               if (c->cur.expires)
+                       THREAD_TIMER_ON(master, c->t_timeout, nhrp_cache_do_timeout, c, c->cur.expires - recent_relative_time().tv_sec);
+               break;
+       }
+}
+
+static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
+{
+       struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
+       char buf[SU_ADDRSTRLEN];
+
+       debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s",
+               c->ifp->name, sockunion2str(&c->remote_addr, buf, sizeof buf),
+               (const char *) arg);
+
+       nhrp_reqid_free(&nhrp_event_reqid, r);
+
+       if (arg && strcmp(arg, "accept") == 0) {
+               if (c->cur.peer) {
+                       netlink_update_binding(c->cur.peer->ifp, &c->remote_addr, NULL);
+                       nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
+                       nhrp_peer_unref(c->cur.peer);
+               }
+               nhrp_cache_counts[c->cur.type]--;
+               nhrp_cache_counts[c->new.type]++;
+               c->cur = c->new;
+               c->cur.peer = nhrp_peer_ref(c->cur.peer);
+               nhrp_cache_reset_new(c);
+               if (c->cur.peer)
+                       nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier);
+               nhrp_cache_update_route(c);
+               notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
+       } else {
+               nhrp_cache_reset_new(c);
+       }
+
+       nhrp_cache_update_timers(c);
+}
+
+static int nhrp_cache_do_auth_timeout(struct thread *t)
+{
+       struct nhrp_cache *c = THREAD_ARG(t);
+       c->t_auth = NULL;
+       nhrp_cache_authorize_binding(&c->eventid, (void *) "timeout");
+       return 0;
+}
+
+static void nhrp_cache_newpeer_notifier(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_cache *c = container_of(n, struct nhrp_cache, newpeer_notifier);
+
+       switch (cmd) {
+       case NOTIFY_PEER_UP:
+               if (nhrp_peer_check(c->new.peer, 1)) {
+                       evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding);
+                       THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 10);
+               }
+               break;
+       case NOTIFY_PEER_DOWN:
+       case NOTIFY_PEER_IFCONFIG_CHANGED:
+               nhrp_cache_reset_new(c);
+               break;
+       }
+}
+
+int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa)
+{
+       if (c->cur.type > type || c->new.type > type) {
+               nhrp_peer_unref(p);
+               return 0;
+       }
+
+       /* Sanitize MTU */
+       switch (sockunion_family(&c->remote_addr)) {
+       case AF_INET:
+               if (mtu < 576 || mtu >= 1500)
+                       mtu = 0;
+               /* Opennhrp announces nbma mtu, but we use protocol mtu.
+                * This heuristic tries to fix up it. */
+               if (mtu > 1420) mtu = (mtu & -16) - 80;
+               break;
+       default:
+               mtu = 0;
+               break;
+       }
+
+       nhrp_cache_reset_new(c);
+       if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
+               if (holding_time > 0) c->cur.expires = recent_relative_time().tv_sec + holding_time;
+               if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa;
+               else memset(&c->cur.remote_nbma_natoa, 0, sizeof c->cur.remote_nbma_natoa);
+               nhrp_peer_unref(p);
+       } else {
+               c->new.type = type;
+               c->new.peer = p;
+               c->new.mtu = mtu;
+               if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa;
+
+               if (holding_time > 0)
+                       c->new.expires = recent_relative_time().tv_sec + holding_time;
+               else if (holding_time < 0)
+                       c->new.type = NHRP_CACHE_INVALID;
+
+               if (c->new.type == NHRP_CACHE_INVALID ||
+                   c->new.type >= NHRP_CACHE_STATIC ||
+                   c->map) {
+                       nhrp_cache_authorize_binding(&c->eventid, (void *) "accept");
+               } else {
+                       nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier, nhrp_cache_newpeer_notifier);
+                       nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP);
+                       THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 60);
+               }
+       }
+       nhrp_cache_update_timers(c);
+
+       return 1;
+}
+
+void nhrp_cache_set_used(struct nhrp_cache *c, int used)
+{
+       c->used = used;
+       if (c->used)
+               notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
+}
+
+struct nhrp_cache_iterator_ctx {
+       void (*cb)(struct nhrp_cache *, void *);
+       void *ctx;
+};
+
+static void nhrp_cache_iterator(struct hash_backet *b, void *ctx)
+{
+       struct nhrp_cache_iterator_ctx *ic = ctx;
+       ic->cb(b->data, ic->ctx);
+}
+
+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_cache_iterator_ctx ic = {
+               .cb = cb,
+               .ctx = ctx,
+       };
+
+       if (nifp->cache_hash)
+               hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
+}
+
+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n, notifier_fn_t fn)
+{
+       notifier_add(n, &c->notifier_list, fn);
+}
+
+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
+{
+       notifier_del(n);
+}
diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c
new file mode 100644 (file)
index 0000000..34cb065
--- /dev/null
@@ -0,0 +1,283 @@
+/* NHRP event manager
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "thread.h"
+#include "zbuf.h"
+#include "log.h"
+#include "nhrpd.h"
+
+const char *nhrp_event_socket_path;
+struct nhrp_reqid_pool nhrp_event_reqid;
+
+struct event_manager {
+       struct thread *t_reconnect, *t_read, *t_write;
+       struct zbuf ibuf;
+       struct zbuf_queue obuf;
+       int fd;
+       uint8_t ibuf_data[4*1024];
+};
+
+static int evmgr_reconnect(struct thread *t);
+
+static void evmgr_connection_error(struct event_manager *evmgr)
+{
+       THREAD_OFF(evmgr->t_read);
+       THREAD_OFF(evmgr->t_write);
+       zbuf_reset(&evmgr->ibuf);
+       zbufq_reset(&evmgr->obuf);
+
+       if (evmgr->fd >= 0)
+               close(evmgr->fd);
+       evmgr->fd = -1;
+       if (nhrp_event_socket_path)
+               THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect,
+                                    evmgr, 10);
+}
+
+static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb)
+{
+       struct zbuf zl;
+       uint32_t eventid = 0;
+       size_t len;
+       char buf[256], result[64] = "";
+
+       while (zbuf_may_pull_until(zb, "\n", &zl)) {
+               len = zbuf_used(&zl) - 1;
+               if (len >= sizeof(buf)-1)
+                       continue;
+               memcpy(buf, zbuf_pulln(&zl, len), len);
+               buf[len] = 0;
+
+               debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf);
+               sscanf(buf, "eventid=%d", &eventid);
+               sscanf(buf, "result=%63s", result);
+       }
+       debugf(NHRP_DEBUG_EVENT, "evmgr: received: eventid=%d result=%s", eventid, result);
+       if (eventid && result[0]) {
+               struct nhrp_reqid *r = nhrp_reqid_lookup(&nhrp_event_reqid, eventid);
+               if (r) r->cb(r, result);
+       }
+}
+
+static int evmgr_read(struct thread *t)
+{
+       struct event_manager *evmgr = THREAD_ARG(t);
+       struct zbuf *ibuf = &evmgr->ibuf;
+       struct zbuf msg;
+
+       evmgr->t_read = NULL;
+       if (zbuf_read(ibuf, evmgr->fd, (size_t) -1) < 0) {
+               evmgr_connection_error(evmgr);
+               return 0;
+       }
+
+       /* Process all messages in buffer */
+       while (zbuf_may_pull_until(ibuf, "\n\n", &msg))
+               evmgr_recv_message(evmgr, &msg);
+
+       THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
+       return 0;
+}
+
+static int evmgr_write(struct thread *t)
+{
+       struct event_manager *evmgr = THREAD_ARG(t);
+       int r;
+
+       evmgr->t_write = NULL;
+       r = zbufq_write(&evmgr->obuf, evmgr->fd);
+       if (r > 0) {
+               THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
+       } else if (r < 0) {
+               evmgr_connection_error(evmgr);
+       }
+
+       return 0;
+}
+
+static void evmgr_hexdump(struct zbuf *zb, const uint8_t *val, size_t vallen)
+{
+       static const char xd[] = "0123456789abcdef";
+       size_t i;
+       char *ptr;
+
+       ptr  = zbuf_pushn(zb, 2*vallen);
+       if (!ptr) return;
+
+       for (i = 0; i < vallen; i++) {
+               uint8_t b = val[i];
+               *(ptr++) = xd[b >> 4];
+               *(ptr++) = xd[b & 0xf];
+       }
+}
+
+static void evmgr_put(struct zbuf *zb, const char *fmt, ...)
+{
+       const char *pos, *nxt, *str;
+       const uint8_t *bin;
+       const union sockunion *su;
+       int len;
+       va_list va;
+
+       va_start(va, fmt);
+       for (pos = fmt; (nxt = strchr(pos, '%')) != NULL; pos = nxt + 2) {
+               zbuf_put(zb, pos, nxt-pos);
+               switch (nxt[1]) {
+               case '%':
+                       zbuf_put8(zb, '%');
+                       break;
+               case 'u':
+                       zb->tail += snprintf((char *) zb->tail, zbuf_tailroom(zb), "%u", va_arg(va, uint32_t));
+                       break;
+               case 's':
+                       str = va_arg(va, const char *);
+                       zbuf_put(zb, str, strlen(str));
+                       break;
+               case 'U':
+                       su = va_arg(va, const union sockunion *);
+                       if (sockunion2str(su, (char *) zb->tail, zbuf_tailroom(zb)))
+                               zb->tail += strlen((char *) zb->tail);
+                       else
+                               zbuf_set_werror(zb);
+                       break;
+               case 'H':
+                       bin = va_arg(va, const uint8_t *);
+                       len = va_arg(va, int);
+                       evmgr_hexdump(zb, bin, len);
+                       break;
+               }
+       }
+       va_end(va);
+       zbuf_put(zb, pos, strlen(pos));
+}
+
+static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf)
+{
+       if (obuf->error) {
+               zbuf_free(obuf);
+               return;
+       }
+       zbuf_put(obuf, "\n", 1);
+       zbufq_queue(&evmgr->obuf, obuf);
+       if (evmgr->fd >= 0)
+               THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
+}
+
+static int evmgr_reconnect(struct thread *t)
+{
+       struct event_manager *evmgr = THREAD_ARG(t);
+       int fd;
+
+       evmgr->t_reconnect = NULL;
+       if (evmgr->fd >= 0 || !nhrp_event_socket_path) return 0;
+
+       fd = sock_open_unix(nhrp_event_socket_path);
+       if (fd < 0) {
+               zlog_warn("%s: failure connecting nhrp-event socket: %s",
+                       __PRETTY_FUNCTION__, strerror(errno));
+               zbufq_reset(&evmgr->obuf);
+               THREAD_TIMER_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
+               return 0;
+       }
+
+       zlog_info("Connected to Event Manager");
+       evmgr->fd = fd;
+       THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
+
+       return 0;
+}
+
+static struct event_manager evmgr_connection;
+
+void evmgr_init(void)
+{
+       struct event_manager *evmgr = &evmgr_connection;
+
+       evmgr->fd = -1;
+       zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0);
+       zbufq_init(&evmgr->obuf);
+       THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
+}
+
+void evmgr_set_socket(const char *socket)
+{
+       if (nhrp_event_socket_path) {
+               free((char *) nhrp_event_socket_path);
+               nhrp_event_socket_path = NULL;
+       }
+       if (socket)
+               nhrp_event_socket_path = strdup(socket);
+       evmgr_connection_error(&evmgr_connection);
+}
+
+void evmgr_terminate(void)
+{
+}
+
+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *))
+{
+       struct event_manager *evmgr = &evmgr_connection;
+       struct nhrp_vc *vc;
+       struct nhrp_interface *nifp = c->ifp->info;
+       struct zbuf *zb;
+       afi_t afi = family2afi(sockunion_family(&c->remote_addr));
+
+       if (!nhrp_event_socket_path) {
+               cb(&c->eventid, (void*) "accept");
+               return;
+       }
+
+       debugf(NHRP_DEBUG_EVENT, "evmgr: sending event %s", name);
+
+       vc = c->new.peer ? c->new.peer->vc : NULL;
+       zb = zbuf_alloc(1024 + (vc ? (vc->local.certlen + vc->remote.certlen) * 2 : 0));
+
+       if (cb) {
+               nhrp_reqid_free(&nhrp_event_reqid, &c->eventid);
+               evmgr_put(zb,
+                       "eventid=%u\n",
+                       nhrp_reqid_alloc(&nhrp_event_reqid, &c->eventid, cb));
+       }
+
+       evmgr_put(zb,
+               "event=%s\n"
+               "type=%s\n"
+               "old_type=%s\n"
+               "num_nhs=%u\n"
+               "interface=%s\n"
+               "local_addr=%U\n",
+               name,
+               nhrp_cache_type_str[c->new.type],
+               nhrp_cache_type_str[c->cur.type],
+               (unsigned int) nhrp_cache_counts[NHRP_CACHE_NHS],
+               c->ifp->name,
+               &nifp->afi[afi].addr);
+
+       if (vc) {
+               evmgr_put(zb,
+                       "vc_initiated=%s\n"
+                       "local_nbma=%U\n"
+                       "local_cert=%H\n"
+                       "remote_addr=%U\n"
+                       "remote_nbma=%U\n"
+                       "remote_cert=%H\n",
+                       c->new.peer->requested ? "yes" : "no",
+                       &vc->local.nbma,
+                       vc->local.cert, vc->local.certlen,
+                       &c->remote_addr, &vc->remote.nbma,
+                       vc->remote.cert, vc->remote.certlen);
+       }
+
+       evmgr_submit(evmgr, zb);
+}
+
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
new file mode 100644 (file)
index 0000000..4ea8076
--- /dev/null
@@ -0,0 +1,406 @@
+/* NHRP interface
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <net/if_arp.h>
+#include "zebra.h"
+#include "linklist.h"
+#include "memory.h"
+#include "thread.h"
+
+#include "nhrpd.h"
+#include "os.h"
+#include "netlink.h"
+
+static int nhrp_if_new_hook(struct interface *ifp)
+{
+       struct nhrp_interface *nifp;
+       afi_t afi;
+
+       nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface));
+       if (!nifp) return 0;
+
+       ifp->info = nifp;
+       nifp->ifp = ifp;
+
+       notifier_init(&nifp->notifier_list);
+       for (afi = 0; afi < AFI_MAX; afi++) {
+               struct nhrp_afi_data *ad = &nifp->afi[afi];
+               ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
+               list_init(&ad->nhslist_head);
+       }
+
+       return 0;
+}
+
+static int nhrp_if_delete_hook(struct interface *ifp)
+{
+       XFREE(MTYPE_NHRP_IF, ifp->info);
+       return 0;
+}
+
+void nhrp_interface_init(void)
+{
+       if_add_hook(IF_NEW_HOOK,    nhrp_if_new_hook);
+       if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook);
+}
+
+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *if_ad = &nifp->afi[afi];
+       unsigned short new_mtu;
+
+       if (if_ad->configured_mtu < 0)
+               new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0;
+       else
+               new_mtu = if_ad->configured_mtu;
+       if (new_mtu >= 1500)
+               new_mtu = 0;
+
+       if (new_mtu != if_ad->mtu) {
+               debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name, new_mtu);
+               if_ad->mtu = new_mtu;
+               notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_MTU_CHANGED);
+       }
+}
+
+static void nhrp_interface_update_source(struct interface *ifp)
+{
+       struct nhrp_interface *nifp = ifp->info;
+
+       if (!nifp->source || !nifp->nbmaifp ||
+           nifp->linkidx == nifp->nbmaifp->ifindex)
+               return;
+
+       nifp->linkidx = nifp->nbmaifp->ifindex;
+       debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name, nifp->linkidx);
+       netlink_gre_set_link(ifp->ifindex, nifp->linkidx);
+}
+
+static void nhrp_interface_interface_notifier(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_interface *nifp = container_of(n, struct nhrp_interface, nbmanifp_notifier);
+       struct interface *nbmaifp = nifp->nbmaifp;
+       struct nhrp_interface *nbmanifp = nbmaifp->info;
+       char buf[SU_ADDRSTRLEN];
+
+       switch (cmd) {
+       case NOTIFY_INTERFACE_CHANGED:
+               nhrp_interface_update_mtu(nifp->ifp, AFI_IP);
+               nhrp_interface_update_source(nifp->ifp);
+               break;
+       case NOTIFY_INTERFACE_ADDRESS_CHANGED:
+               nifp->nbma = nbmanifp->afi[AFI_IP].addr;
+               nhrp_interface_update(nifp->ifp);
+               notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
+               debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s",
+                       nifp->ifp->name,
+                       sockunion2str(&nifp->nbma, buf, sizeof buf));
+               break;
+       }
+}
+
+static void nhrp_interface_update_nbma(struct interface *ifp)
+{
+       struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL;
+       struct interface *nbmaifp = NULL;
+       union sockunion nbma;
+
+       sockunion_family(&nbma) = AF_UNSPEC;
+
+       if (nifp->source)
+               nbmaifp = if_lookup_by_name(nifp->source);
+
+       switch (ifp->ll_type) {
+       case ZEBRA_LLT_IPGRE: {
+                       struct in_addr saddr = {0};
+                       netlink_gre_get_info(ifp->ifindex, &nifp->grekey, &nifp->linkidx, &saddr);
+                       debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr);
+                       if (saddr.s_addr)
+                               sockunion_set(&nbma, AF_INET, (u_char *) &saddr.s_addr, sizeof(saddr.s_addr));
+                       else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL)
+                               nbmaifp = if_lookup_by_index(nifp->linkidx);
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (nbmaifp)
+               nbmanifp = nbmaifp->info;
+
+       if (nbmaifp != nifp->nbmaifp) {
+               if (nifp->nbmaifp)
+                       notifier_del(&nifp->nbmanifp_notifier);
+               nifp->nbmaifp = nbmaifp;
+               if (nbmaifp) {
+                       notifier_add(&nifp->nbmanifp_notifier, &nbmanifp->notifier_list, nhrp_interface_interface_notifier);
+                       debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name, nbmaifp->name);
+               }
+       }
+
+       if (nbmaifp) {
+               if (sockunion_family(&nbma) == AF_UNSPEC)
+                       nbma = nbmanifp->afi[AFI_IP].addr;
+               nhrp_interface_update_mtu(ifp, AFI_IP);
+               nhrp_interface_update_source(ifp);
+       }
+
+       if (!sockunion_same(&nbma, &nifp->nbma)) {
+               nifp->nbma = nbma;
+               nhrp_interface_update(nifp->ifp);
+               debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name);
+               notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
+       }
+
+       nhrp_interface_update(ifp);
+}
+
+static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, int force)
+{
+       const int family = afi2family(afi);
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *if_ad = &nifp->afi[afi];
+       struct nhrp_cache *nc;
+       struct connected *c, *best;
+       struct listnode *cnode;
+       union sockunion addr;
+       char buf[PREFIX_STRLEN];
+
+       /* Select new best match preferring primary address */
+       best = NULL;
+       for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+               if (PREFIX_FAMILY(c->address) != family)
+                       continue;
+               if (best == NULL) {
+                       best = c;
+                       continue;
+               }
+               if ((best->flags & ZEBRA_IFA_SECONDARY) && !(c->flags & ZEBRA_IFA_SECONDARY)) {
+                       best = c;
+                       continue;
+               }
+               if (!(best->flags & ZEBRA_IFA_SECONDARY) && (c->flags & ZEBRA_IFA_SECONDARY))
+                       continue;
+               if (best->address->prefixlen > c->address->prefixlen) {
+                       best = c;
+                       continue;
+               }
+               if (best->address->prefixlen < c->address->prefixlen)
+                       continue;
+       }
+
+       /* On NHRP interfaces a host prefix is required */
+       if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) {
+               zlog_notice("%s: %s is not a host prefix", ifp->name,
+                       prefix2str(best->address, buf, sizeof buf));
+               best = NULL;
+       }
+
+       /* Update address if it changed */
+       if (best)
+               prefix2sockunion(best->address, &addr);
+       else
+               memset(&addr, 0, sizeof(addr));
+
+       if (!force && sockunion_same(&if_ad->addr, &addr))
+               return;
+
+       if (sockunion_family(&if_ad->addr) != AF_UNSPEC) {
+               nc = nhrp_cache_get(ifp, &if_ad->addr, 0);
+               if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, NULL, 0, NULL);
+       }
+
+       debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s",
+               ifp->name, afi == AFI_IP ? 4 : 6,
+               best ? prefix2str(best->address, buf, sizeof buf) : "(none)");
+       if_ad->addr = addr;
+
+       if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) {
+               nc = nhrp_cache_get(ifp, &addr, 1);
+               if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
+       }
+
+       notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
+}
+
+void nhrp_interface_update(struct interface *ifp)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *if_ad;
+       afi_t afi;
+       int enabled = 0;
+
+       notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED);
+
+       for (afi = 0; afi < AFI_MAX; afi++) {
+               if_ad = &nifp->afi[afi];
+
+               if (sockunion_family(&nifp->nbma) == AF_UNSPEC ||
+                   ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp) ||
+                   !if_ad->network_id) {
+                       if (if_ad->configured) {
+                               if_ad->configured = 0;
+                               nhrp_interface_update_address(ifp, afi, 1);
+                       }
+                       continue;
+               }
+
+               if (!if_ad->configured) {
+                       os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi));
+                       if_ad->configured = 1;
+                       nhrp_interface_update_address(ifp, afi, 1);
+               }
+
+               enabled = 1;
+       }
+
+       if (enabled != nifp->enabled) {
+               nifp->enabled = enabled;
+               notifier_call(&nifp->notifier_list, enabled ? NOTIFY_INTERFACE_UP : NOTIFY_INTERFACE_DOWN);
+       }
+}
+
+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct interface *ifp;
+
+       /* read and add the interface in the iflist. */
+       ifp = zebra_interface_add_read(client->ibuf, vrf_id);
+       if (ifp == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s",
+               ifp->name, ifp->ifindex,
+               ifp->ll_type, if_link_type_str(ifp->ll_type));
+
+       nhrp_interface_update_nbma(ifp);
+
+       return 0;
+}
+
+int nhrp_interface_delete(int cmd, struct zclient *client,
+                         zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct interface *ifp;
+       struct stream *s;
+
+       s = client->ibuf;
+       ifp = zebra_interface_state_read(s, vrf_id);
+       if (ifp == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
+       ifp->ifindex = IFINDEX_INTERNAL;
+       nhrp_interface_update(ifp);
+       /* if_delete(ifp); */
+       return 0;
+}
+
+int nhrp_interface_up(int cmd, struct zclient *client,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct interface *ifp;
+
+       ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+       if (ifp == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
+       nhrp_interface_update_nbma(ifp);
+
+       return 0;
+}
+
+int nhrp_interface_down(int cmd, struct zclient *client,
+                       zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct interface *ifp;
+
+       ifp = zebra_interface_state_read(client->ibuf, vrf_id);
+       if (ifp == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name);
+       nhrp_interface_update(ifp);
+       return 0;
+}
+
+int nhrp_interface_address_add(int cmd, struct zclient *client,
+                              zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct connected *ifc;
+       char buf[PREFIX_STRLEN];
+
+       ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+       if (ifc == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s",
+               ifc->ifp->name,
+               prefix2str(ifc->address, buf, sizeof buf));
+
+       nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
+
+       return 0;
+}
+
+int nhrp_interface_address_delete(int cmd, struct zclient *client,
+                                 zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct connected *ifc;
+       char buf[PREFIX_STRLEN];
+
+       ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
+       if (ifc == NULL)
+               return 0;
+
+       debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s",
+               ifc->ifp->name,
+               prefix2str(ifc->address, buf, sizeof buf));
+
+       nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
+       connected_free(ifc);
+
+       return 0;
+}
+
+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       notifier_add(n, &nifp->notifier_list, fn);
+}
+
+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
+{
+       notifier_del(n);
+}
+
+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile)
+{
+       struct nhrp_interface *nifp = ifp->info;
+
+       if (nifp->ipsec_profile) free(nifp->ipsec_profile);
+       nifp->ipsec_profile = profile ? strdup(profile) : NULL;
+
+       if (nifp->ipsec_fallback_profile) free(nifp->ipsec_fallback_profile);
+       nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL;
+
+       notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
+}
+
+void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
+{
+       struct nhrp_interface *nifp = ifp->info;
+
+       if (nifp->source) free(nifp->source);
+       nifp->source = ifname ? strdup(ifname) : NULL;
+
+       nhrp_interface_update_nbma(ifp);
+}
diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
new file mode 100644 (file)
index 0000000..56ea271
--- /dev/null
@@ -0,0 +1,247 @@
+/* NHRP daemon main functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <libgen.h>
+
+#include "zebra.h"
+#include "privs.h"
+#include "getopt.h"
+#include "thread.h"
+#include "sigevent.h"
+#include "version.h"
+#include "log.h"
+#include "memory.h"
+#include "command.h"
+
+#include "nhrpd.h"
+#include "netlink.h"
+
+unsigned int debug_flags = 0;
+
+struct thread_master *master;
+struct timeval current_time;
+static const char *pid_file = PATH_NHRPD_PID;
+static char config_default[] = SYSCONFDIR NHRP_DEFAULT_CONFIG;
+static char *config_file = NULL;
+static char *vty_addr = NULL;
+static int vty_port = NHRP_VTY_PORT;
+static int do_daemonise = 0;
+
+/* nhrpd options. */
+struct option longopts[] = {
+       { "daemon",      no_argument,       NULL, 'd'},
+       { "config_file", required_argument, NULL, 'f'},
+       { "pid_file",    required_argument, NULL, 'i'},
+       { "socket",      required_argument, NULL, 'z'},
+       { "help",        no_argument,       NULL, 'h'},
+       { "vty_addr",    required_argument, NULL, 'A'},
+       { "vty_port",    required_argument, NULL, 'P'},
+       { "user",        required_argument, NULL, 'u'},
+       { "group",       required_argument, NULL, 'g'},
+       { "version",     no_argument,       NULL, 'v'},
+       { 0 }
+};
+
+/* nhrpd privileges */
+static zebra_capabilities_t _caps_p [] = {
+       ZCAP_NET_RAW,
+       ZCAP_NET_ADMIN,
+       ZCAP_DAC_OVERRIDE,      /* for now needed to write to /proc/sys/net/ipv4/<if>/send_redirect */
+};
+
+static struct zebra_privs_t nhrpd_privs = {
+#ifdef QUAGGA_USER
+       .user = QUAGGA_USER,
+#endif
+#ifdef QUAGGA_GROUP
+       .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+       .vty_group = VTY_GROUP,
+#endif
+       .caps_p = _caps_p,
+       .cap_num_p = ZEBRA_NUM_OF(_caps_p),
+};
+
+static void usage(const char *progname, int status)
+{
+       if (status != 0)
+               fprintf(stderr, "Try `%s --help' for more information.\n", progname);
+       else
+               printf(
+"Usage : %s [OPTION...]\n\
+Daemon which manages NHRP protocol.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-v, --version      Print program version\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+
+       exit(status);
+}
+
+static void parse_arguments(const char *progname, int argc, char **argv)
+{
+       int opt;
+
+       while (1) {
+               opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0);
+               if(opt < 0) break;
+
+               switch (opt) {
+               case 0:
+                       break;
+               case 'd':
+                       do_daemonise = -1;
+                       break;
+               case 'f':
+                       config_file = optarg;
+                       break;
+               case 'i':
+                       pid_file = optarg;
+                       break;
+               case 'z':
+                       zclient_serv_path_set(optarg);
+                       break;
+               case 'A':
+                       vty_addr = optarg;
+                       break;
+               case 'P':
+                       vty_port = atoi (optarg);
+                       if (vty_port <= 0 || vty_port > 0xffff)
+                               vty_port = NHRP_VTY_PORT;
+                       break;
+               case 'u':
+                       nhrpd_privs.user = optarg;
+                       break;
+               case 'g':
+                       nhrpd_privs.group = optarg;
+                       break;
+               case 'v':
+                       print_version(progname);
+                       exit(0);
+                       break;
+               case 'h':
+                       usage(progname, 0);
+                       break;
+               default:
+                       usage(progname, 1);
+                       break;
+               }
+       }
+}
+
+static void nhrp_sigusr1(void)
+{
+       zlog_rotate(NULL);
+}
+
+static void nhrp_request_stop(void)
+{
+       debugf(NHRP_DEBUG_COMMON, "Exiting...");
+
+       nhrp_shortcut_terminate();
+       nhrp_nhs_terminate();
+       nhrp_zebra_terminate();
+       vici_terminate();
+       evmgr_terminate();
+       nhrp_vc_terminate();
+       vrf_terminate();
+       /* memory_terminate(); */
+       /* vty_terminate(); */
+       cmd_terminate();
+       /* signal_terminate(); */
+       zprivs_terminate(&nhrpd_privs);
+
+       debugf(NHRP_DEBUG_COMMON, "Remove pid file.");
+       if (pid_file) unlink(pid_file);
+       debugf(NHRP_DEBUG_COMMON, "Done.");
+
+       closezlog(zlog_default);
+
+       exit(0);
+}
+
+static struct quagga_signal_t sighandlers[] = {
+       { .signal = SIGUSR1, .handler = &nhrp_sigusr1, },
+       { .signal = SIGINT,  .handler = &nhrp_request_stop, },
+       { .signal = SIGTERM, .handler = &nhrp_request_stop, },
+};
+
+int main(int argc, char **argv)
+{
+       struct thread thread;
+       const char *progname;
+
+       /* Set umask before anything for security */
+       umask(0027);
+       progname = basename(argv[0]);
+       zlog_default = openzlog(progname, ZLOG_NHRP, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+       zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING);
+
+       parse_arguments(progname, argc, argv);
+
+       /* Library inits. */
+       master = thread_master_create();
+       zprivs_init(&nhrpd_privs);
+       signal_init(master, array_size(sighandlers), sighandlers);
+       cmd_init(1);
+       vty_init(master);
+       memory_init();
+       nhrp_interface_init();
+       vrf_init();
+       resolver_init();
+
+       /* Run with elevated capabilities, as for all netlink activity
+        * we need privileges anyway. */
+       nhrpd_privs.change(ZPRIVS_RAISE);
+
+       netlink_init();
+       evmgr_init();
+       nhrp_vc_init();
+       nhrp_packet_init();
+       vici_init();
+       nhrp_zebra_init();
+       nhrp_shortcut_init();
+
+       nhrp_config_init();
+
+       /* Get zebra configuration file. */
+       zlog_set_level(NULL, ZLOG_DEST_STDOUT, do_daemonise ? ZLOG_DISABLED : LOG_DEBUG);
+       vty_read_config(config_file, config_default);
+
+       if (do_daemonise && daemon(0, 0) < 0) {
+               zlog_err("daemonise: %s", safe_strerror(errno));
+               exit (1);
+       }
+
+       /* write pid file */
+       if (pid_output(pid_file) < 0) {
+               zlog_err("error while writing pidfile");
+               exit (1);
+       }
+
+       /* Create VTY socket */
+       vty_serv_sock(vty_addr, vty_port, NHRP_VTYSH_PATH);
+       zlog_notice("nhrpd starting: vty@%d", vty_port);
+
+       /* Main loop */
+       while (thread_fetch(master, &thread))
+               thread_call(&thread);
+
+       return 0;
+}
diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c
new file mode 100644 (file)
index 0000000..d77ec0b
--- /dev/null
@@ -0,0 +1,371 @@
+/* NHRP NHC nexthop server functions (registration)
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zebra.h"
+#include "zbuf.h"
+#include "memory.h"
+#include "thread.h"
+#include "nhrpd.h"
+#include "nhrp_protocol.h"
+
+static int nhrp_nhs_resolve(struct thread *t);
+static int nhrp_reg_send_req(struct thread *t);
+
+static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
+{
+       struct nhrp_packet_parser *p = arg;
+       struct nhrp_registration *r = container_of(reqid, struct nhrp_registration, reqid);
+       struct nhrp_nhs *nhs = r->nhs;
+       struct interface *ifp = nhs->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_extension_header *ext;
+       struct nhrp_cie_header *cie;
+       struct nhrp_cache *c;
+       struct zbuf extpl;
+       union sockunion cie_nbma, cie_proto, *proto;
+       char buf[64];
+       int ok = 0, holdtime;
+
+       nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
+
+       if (p->hdr->type != NHRP_PACKET_REGISTRATION_REPLY) {
+               debugf(NHRP_DEBUG_COMMON, "NHS: Registration failed");
+               return;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "NHS: Reg.reply received");
+
+       ok = 1;
+       while ((cie = nhrp_cie_pull(&p->payload, p->hdr, &cie_nbma, &cie_proto)) != NULL) {
+               proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &p->src_proto;
+               debugf(NHRP_DEBUG_COMMON, "NHS: CIE registration: %s: %d",
+                       sockunion2str(proto, buf, sizeof(buf)),
+                       cie->code);
+               if (!((cie->code == NHRP_CODE_SUCCESS) ||
+                      (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED && nhs->hub)))
+                       ok = 0;
+       }
+
+       if (!ok)
+               return;
+
+       /* Parse extensions */
+       sockunion_family(&nifp->nat_nbma) = AF_UNSPEC;
+       while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) {
+               switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+               case NHRP_EXTENSION_NAT_ADDRESS:
+                       /* NHS adds second CIE if NAT is detected */
+                       if (nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto) &&
+                           nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto)) {
+                               nifp->nat_nbma = cie_nbma;
+                               debugf(NHRP_DEBUG_IF, "%s: NAT detected, real NBMA address: %s",
+                                       ifp->name, sockunion2str(&nifp->nbma, buf, sizeof(buf)));
+                       }
+                       break;
+               }
+       }
+
+       /* Success - schedule next registration, and route NHS */
+       r->timeout = 2;
+       holdtime = nifp->afi[nhs->afi].holdtime;
+       THREAD_OFF(r->t_register);
+
+       /* RFC 2332 5.2.3 - Registration is recommend to be renewed
+        * every one third of holdtime */
+       THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, holdtime / 3);
+
+       r->proto_addr = p->dst_proto;
+       c = nhrp_cache_get(ifp, &p->dst_proto, 1);
+       if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime, nhrp_peer_ref(r->peer), 0, NULL);
+}
+
+static int nhrp_reg_timeout(struct thread *t)
+{
+       struct nhrp_registration *r = THREAD_ARG(t);
+       struct nhrp_cache *c;
+
+       r->t_register = NULL;
+
+       if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) {
+               nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
+               c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0);
+               if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL, 0, NULL);
+               sockunion_family(&r->proto_addr) = AF_UNSPEC;
+       }
+
+       r->timeout <<= 1;
+       if (r->timeout > 64) r->timeout = 2;
+       THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
+
+       return 0;
+}
+
+static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_registration *r = container_of(n, struct nhrp_registration, peer_notifier);
+       char buf[SU_ADDRSTRLEN];
+
+       switch (cmd) {
+       case NOTIFY_PEER_UP:
+       case NOTIFY_PEER_DOWN:
+       case NOTIFY_PEER_IFCONFIG_CHANGED:
+       case NOTIFY_PEER_MTU_CHANGED:
+               debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s",
+                       sockunion2str(&r->peer->vc->remote.nbma, buf, sizeof buf));
+               THREAD_TIMER_OFF(r->t_register);
+               THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
+               break;
+       }
+}
+
+static int nhrp_reg_send_req(struct thread *t)
+{
+       struct nhrp_registration *r = THREAD_ARG(t);
+       struct nhrp_nhs *nhs = r->nhs;
+       char buf1[SU_ADDRSTRLEN], buf2[SU_ADDRSTRLEN];
+       struct interface *ifp = nhs->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi];
+       union sockunion *dst_proto;
+       struct zbuf *zb;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_extension_header *ext;
+       struct nhrp_cie_header *cie;
+
+       r->t_register = NULL;
+       if (!nhrp_peer_check(r->peer, 2)) {
+               debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s",
+                       sockunion2str(&r->peer->vc->remote.nbma, buf1, sizeof buf1));
+               THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, 120);
+               return 0;
+       }
+
+       THREAD_TIMER_ON(master, r->t_register, nhrp_reg_timeout, r, r->timeout);
+
+       /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */
+       dst_proto = &nhs->proto_addr;
+       if (sockunion_family(dst_proto) == AF_UNSPEC)
+               dst_proto = &if_ad->addr;
+
+       sockunion2str(&if_ad->addr, buf1, sizeof(buf1));
+       sockunion2str(dst_proto, buf2, sizeof(buf2));
+       debugf(NHRP_DEBUG_COMMON, "NHS: Register %s -> %s (timeout %d)", buf1, buf2, r->timeout);
+
+       /* No protocol address configured for tunnel interface */
+       if (sockunion_family(&if_ad->addr) == AF_UNSPEC)
+               return 0;
+
+       zb = zbuf_alloc(1400);
+       hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REQUEST, &nifp->nbma, &if_ad->addr, dst_proto);
+       hdr->hop_count = 1;
+       if (!(if_ad->flags & NHRP_IFF_REG_NO_UNIQUE))
+               hdr->flags |= htons(NHRP_FLAG_REGISTRATION_UNIQUE);
+
+       hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &r->reqid, nhrp_reg_reply));
+
+       /* FIXME: push CIE for each local protocol address */
+       cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
+       cie->prefix_length = 0xff;
+       cie->holding_time = htons(if_ad->holdtime);
+       cie->mtu = htons(if_ad->mtu);
+
+       nhrp_ext_request(zb, hdr, ifp);
+
+       /* Cisco NAT detection extension */
+       hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT);
+       ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+       cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
+       cie->prefix_length = 8 * sockunion_get_addrlen(&if_ad->addr);
+       nhrp_ext_complete(zb, ext);
+
+       nhrp_packet_complete(zb, hdr);
+       nhrp_peer_send(r->peer, zb);
+       zbuf_free(zb);
+
+       return 0;
+}
+
+static void nhrp_reg_delete(struct nhrp_registration *r)
+{
+       nhrp_peer_notify_del(r->peer, &r->peer_notifier);
+       nhrp_peer_unref(r->peer);
+       list_del(&r->reglist_entry);
+       THREAD_OFF(r->t_register);
+       XFREE(MTYPE_NHRP_REGISTRATION, r);
+}
+
+static struct nhrp_registration *nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr)
+{
+       struct nhrp_registration *r;
+
+       list_for_each_entry(r, &nhs->reglist_head, reglist_entry)
+               if (sockunion_same(&r->peer->vc->remote.nbma, nbma_addr))
+                       return r;
+       return NULL;
+}
+
+static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, union sockunion *addrs)
+{
+       struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve);
+       struct nhrp_interface *nifp = nhs->ifp->info;
+       struct nhrp_registration *reg, *regn;
+       int i;
+
+       nhs->t_resolve = NULL;
+       if (n < 0) {
+               /* Failed, retry in a moment */
+               THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 5);
+               return;
+       }
+
+       THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 2*60*60);
+
+       list_for_each_entry(reg, &nhs->reglist_head, reglist_entry)
+               reg->mark = 1;
+
+       nhs->hub = 0;
+       for (i = 0; i < n; i++) {
+               if (sockunion_same(&addrs[i], &nifp->nbma)) {
+                       nhs->hub = 1;
+                       continue;
+               }
+
+               reg = nhrp_reg_by_nbma(nhs, &addrs[i]);
+               if (reg) {
+                       reg->mark = 0;
+                       continue;
+               }
+
+               reg = XCALLOC(MTYPE_NHRP_REGISTRATION, sizeof(*reg));
+               reg->peer = nhrp_peer_get(nhs->ifp, &addrs[i]);
+               reg->nhs = nhs;
+               reg->timeout = 1;
+               list_init(&reg->reglist_entry);
+               list_add_tail(&reg->reglist_entry, &nhs->reglist_head);
+               nhrp_peer_notify_add(reg->peer, &reg->peer_notifier, nhrp_reg_peer_notify);
+               THREAD_TIMER_MSEC_ON(master, reg->t_register, nhrp_reg_send_req, reg, 50);
+       }
+
+       list_for_each_entry_safe(reg, regn, &nhs->reglist_head, reglist_entry) {
+               if (reg->mark)
+                       nhrp_reg_delete(reg);
+       }
+}
+
+static int nhrp_nhs_resolve(struct thread *t)
+{
+       struct nhrp_nhs *nhs = THREAD_ARG(t);
+
+       resolver_resolve(&nhs->dns_resolve, AF_INET, nhs->nbma_fqdn, nhrp_nhs_resolve_cb);
+
+       return 0;
+}
+
+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_nhs *nhs;
+
+       if (sockunion_family(proto_addr) != AF_UNSPEC &&
+           sockunion_family(proto_addr) != afi2family(afi))
+               return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
+
+       list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
+               if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC &&
+                   sockunion_family(proto_addr) != AF_UNSPEC &&
+                   sockunion_same(&nhs->proto_addr, proto_addr))
+                       return NHRP_ERR_ENTRY_EXISTS;
+
+               if (strcmp(nhs->nbma_fqdn, nbma_fqdn) == 0)
+                       return NHRP_ERR_ENTRY_EXISTS;
+       }
+
+       nhs = XMALLOC(MTYPE_NHRP_NHS, sizeof(struct nhrp_nhs));
+       if (!nhs) return NHRP_ERR_NO_MEMORY;
+
+       *nhs = (struct nhrp_nhs) {
+               .afi = afi,
+               .ifp = ifp,
+               .proto_addr = *proto_addr,
+               .nbma_fqdn = strdup(nbma_fqdn),
+               .reglist_head = LIST_INITIALIZER(nhs->reglist_head),
+       };
+       list_add_tail(&nhs->nhslist_entry, &nifp->afi[afi].nhslist_head);
+       THREAD_TIMER_MSEC_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 1000);
+
+       return NHRP_OK;
+}
+
+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_nhs *nhs, *nnhs;
+       int ret = NHRP_ERR_ENTRY_NOT_FOUND;
+
+       if (sockunion_family(proto_addr) != AF_UNSPEC &&
+           sockunion_family(proto_addr) != afi2family(afi))
+               return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
+
+       list_for_each_entry_safe(nhs, nnhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
+               if (!sockunion_same(&nhs->proto_addr, proto_addr))
+                       continue;
+               if (strcmp(nhs->nbma_fqdn, nbma_fqdn) != 0)
+                       continue;
+
+               nhrp_nhs_free(nhs);
+               ret = NHRP_OK;
+       }
+
+       return ret;
+}
+
+int nhrp_nhs_free(struct nhrp_nhs *nhs)
+{
+       struct nhrp_registration *r, *rn;
+
+       list_for_each_entry_safe(r, rn, &nhs->reglist_head, reglist_entry)
+               nhrp_reg_delete(r);
+       THREAD_OFF(nhs->t_resolve);
+       list_del(&nhs->nhslist_entry);
+       free((void*) nhs->nbma_fqdn);
+       XFREE(MTYPE_NHRP_NHS, nhs);
+       return 0;
+}
+
+void nhrp_nhs_terminate(void)
+{
+       struct interface *ifp;
+       struct nhrp_interface *nifp;
+       struct nhrp_nhs *nhs, *tmp;
+       struct listnode *node;
+       afi_t afi;
+
+       for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+               nifp = ifp->info;
+               for (afi = 0; afi < AFI_MAX; afi++) {
+                       list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head, nhslist_entry)
+                               nhrp_nhs_free(nhs);
+               }
+       }
+}
+
+void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, void (*cb)(struct nhrp_nhs *, struct nhrp_registration *, void *), void *ctx)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_nhs *nhs;
+       struct nhrp_registration *reg;
+
+       list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
+               if (!list_empty(&nhs->reglist_head)) {
+                       list_for_each_entry(reg, &nhs->reglist_head, reglist_entry)
+                               cb(nhs, reg, ctx);
+               } else
+                       cb(nhs, 0, ctx);
+       }
+}
diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c
new file mode 100644 (file)
index 0000000..5d2866a
--- /dev/null
@@ -0,0 +1,312 @@
+/* NHRP packet handling functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <netinet/if_ether.h>
+#include "nhrpd.h"
+#include "zbuf.h"
+#include "thread.h"
+#include "hash.h"
+
+#include "nhrp_protocol.h"
+#include "os.h"
+
+struct nhrp_reqid_pool nhrp_packet_reqid;
+
+static uint16_t family2proto(int family)
+{
+       switch (family) {
+       case AF_INET: return ETH_P_IP;
+       case AF_INET6: return ETH_P_IPV6;
+       }
+       return 0;
+}
+
+static int proto2family(uint16_t proto)
+{
+       switch (proto) {
+       case ETH_P_IP: return AF_INET;
+       case ETH_P_IPV6: return AF_INET6;
+       }
+       return AF_UNSPEC;
+}
+
+struct nhrp_packet_header *nhrp_packet_push(
+       struct zbuf *zb, uint8_t type,
+       const union sockunion *src_nbma,
+       const union sockunion *src_proto,
+       const union sockunion *dst_proto)
+{
+       struct nhrp_packet_header *hdr;
+
+       hdr = zbuf_push(zb, struct nhrp_packet_header);
+       if (!hdr) return NULL;
+
+       *hdr = (struct nhrp_packet_header) {
+               .afnum = htons(family2afi(sockunion_family(src_nbma))),
+               .protocol_type = htons(family2proto(sockunion_family(src_proto))),
+               .version = NHRP_VERSION_RFC2332,
+               .type = type,
+               .hop_count = 64,
+               .src_nbma_address_len = sockunion_get_addrlen(src_nbma),
+               .src_protocol_address_len = sockunion_get_addrlen(src_proto),
+               .dst_protocol_address_len = sockunion_get_addrlen(dst_proto),
+       };
+
+       zbuf_put(zb, sockunion_get_addr(src_nbma), hdr->src_nbma_address_len);
+       zbuf_put(zb, sockunion_get_addr(src_proto), hdr->src_protocol_address_len);
+       zbuf_put(zb, sockunion_get_addr(dst_proto), hdr->dst_protocol_address_len);
+
+       return hdr;
+}
+
+struct nhrp_packet_header *nhrp_packet_pull(
+       struct zbuf *zb,
+       union sockunion *src_nbma,
+       union sockunion *src_proto,
+       union sockunion *dst_proto)
+{
+       struct nhrp_packet_header *hdr;
+
+       hdr = zbuf_pull(zb, struct nhrp_packet_header);
+       if (!hdr) return NULL;
+
+       sockunion_set(
+               src_nbma, afi2family(htons(hdr->afnum)),
+               zbuf_pulln(zb, hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len),
+               hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len);
+       sockunion_set(
+               src_proto, proto2family(htons(hdr->protocol_type)),
+               zbuf_pulln(zb, hdr->src_protocol_address_len),
+               hdr->src_protocol_address_len);
+       sockunion_set(
+               dst_proto, proto2family(htons(hdr->protocol_type)),
+               zbuf_pulln(zb, hdr->dst_protocol_address_len),
+               hdr->dst_protocol_address_len);
+
+       return hdr;
+}
+
+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
+{
+       const uint16_t *pdu16 = (const uint16_t *) pdu;
+       uint32_t csum = 0;
+       int i;
+
+       for (i = 0; i < len / 2; i++)
+               csum += pdu16[i];
+       if (len & 1)
+               csum += htons(pdu[len - 1]);
+
+       while (csum & 0xffff0000)
+               csum = (csum & 0xffff) + (csum >> 16);
+
+       return (~csum) & 0xffff;
+}
+
+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr)
+{
+       unsigned short size;
+
+       if (hdr->extension_offset)
+               nhrp_ext_push(zb, hdr, NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY);
+
+       size = zb->tail - (uint8_t *)hdr;
+       hdr->packet_size = htons(size);
+       hdr->checksum = 0;
+       hdr->checksum = nhrp_packet_calculate_checksum((uint8_t *) hdr, size);
+}
+
+struct nhrp_cie_header *nhrp_cie_push(
+       struct zbuf *zb,
+       uint8_t code,
+       const union sockunion *nbma,
+       const union sockunion *proto)
+{
+       struct nhrp_cie_header *cie;
+
+       cie = zbuf_push(zb, struct nhrp_cie_header);
+       *cie = (struct nhrp_cie_header) {
+               .code = code,
+       };
+       if (nbma) {
+               cie->nbma_address_len = sockunion_get_addrlen(nbma);
+               zbuf_put(zb, sockunion_get_addr(nbma), cie->nbma_address_len);
+       }
+       if (proto) {
+               cie->protocol_address_len = sockunion_get_addrlen(proto);
+               zbuf_put(zb, sockunion_get_addr(proto), cie->protocol_address_len);
+       }
+
+       return cie;
+}
+
+struct nhrp_cie_header *nhrp_cie_pull(
+       struct zbuf *zb,
+       struct nhrp_packet_header *hdr,
+       union sockunion *nbma,
+       union sockunion *proto)
+{
+       struct nhrp_cie_header *cie;
+
+       cie = zbuf_pull(zb, struct nhrp_cie_header);
+       if (!cie) return NULL;
+
+       if (cie->nbma_address_len + cie->nbma_subaddress_len) {
+               sockunion_set(
+                       nbma, afi2family(htons(hdr->afnum)),
+                       zbuf_pulln(zb, cie->nbma_address_len + cie->nbma_subaddress_len),
+                       cie->nbma_address_len + cie->nbma_subaddress_len);
+       } else {
+               sockunion_family(nbma) = AF_UNSPEC;
+       }
+
+       if (cie->protocol_address_len) {
+               sockunion_set(
+                       proto, proto2family(htons(hdr->protocol_type)),
+                       zbuf_pulln(zb, cie->protocol_address_len),
+                       cie->protocol_address_len);
+       } else {
+               sockunion_family(proto) = AF_UNSPEC;
+       }
+
+       return cie;
+}
+
+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type)
+{
+       struct nhrp_extension_header *ext;
+       ext = zbuf_push(zb, struct nhrp_extension_header);
+       if (!ext) return NULL;
+
+       if (!hdr->extension_offset)
+               hdr->extension_offset = htons(zb->tail - (uint8_t*) hdr - sizeof(struct nhrp_extension_header));
+
+       *ext = (struct nhrp_extension_header) {
+               .type = htons(type),
+               .length = 0,
+       };
+       return ext;
+}
+
+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext)
+{
+       ext->length = htons(zb->tail - (uint8_t*)ext - sizeof(struct nhrp_extension_header));
+}
+
+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload)
+{
+       struct nhrp_extension_header *ext;
+       uint16_t plen;
+
+       ext = zbuf_pull(zb, struct nhrp_extension_header);
+       if (!ext) return NULL;
+
+       plen = htons(ext->length);
+       zbuf_init(payload, zbuf_pulln(zb, plen), plen, plen);
+       return ext;
+}
+
+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp)
+{
+       /* Place holders for standard extensions */
+       nhrp_ext_push(zb, hdr, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
+       nhrp_ext_push(zb, hdr, NHRP_EXTENSION_REVERSE_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
+       nhrp_ext_push(zb, hdr, NHRP_EXTENSION_RESPONDER_ADDRESS | NHRP_EXTENSION_FLAG_COMPULSORY);
+}
+
+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *ad = &nifp->afi[htons(hdr->afnum)];
+       struct nhrp_extension_header *dst;
+       struct nhrp_cie_header *cie;
+       uint16_t type;
+
+       type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
+       if (type == NHRP_EXTENSION_END)
+               return 0;
+
+       dst = nhrp_ext_push(zb, hdr, htons(ext->type));
+       if (!dst) goto err;
+
+       switch (type) {
+       case NHRP_EXTENSION_RESPONDER_ADDRESS:
+               cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &ad->addr);
+               if (!cie) goto err;
+               cie->holding_time = htons(ad->holdtime);
+               break;
+       default:
+               if (type & NHRP_EXTENSION_FLAG_COMPULSORY)
+                       goto err;
+       case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
+       case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
+               /* Supported compulsory extensions, and any
+                * non-compulsory that is not explicitly handled,
+                * should be just copied. */
+               zbuf_copy(zb, extpayload, zbuf_used(extpayload));
+               break;
+       }
+       nhrp_ext_complete(zb, dst);
+       return 0;
+err:
+       zbuf_set_werror(zb);
+       return -1;
+}
+
+static int nhrp_packet_recvraw(struct thread *t)
+{
+       int fd = THREAD_FD(t), ifindex;
+       struct zbuf *zb;
+       struct interface *ifp;
+       struct nhrp_peer *p;
+       union sockunion remote_nbma;
+       uint8_t addr[64];
+       size_t len, addrlen;
+
+       thread_add_read(master, nhrp_packet_recvraw, 0, fd);
+
+       zb = zbuf_alloc(1500);
+       if (!zb) return 0;
+
+       len = zbuf_size(zb);
+       addrlen = sizeof(addr);
+       if (os_recvmsg(zb->buf, &len, &ifindex, addr, &addrlen) < 0)
+               goto err;
+
+       zb->head = zb->buf;
+       zb->tail = zb->buf + len;
+
+       switch (addrlen) {
+       case 4:
+               sockunion_set(&remote_nbma, AF_INET, addr, addrlen);
+               break;
+       default:
+               goto err;
+       }
+
+       ifp = if_lookup_by_index(ifindex);
+       if (!ifp) goto err;
+
+       p = nhrp_peer_get(ifp, &remote_nbma);
+       if (!p) goto err;
+
+       nhrp_peer_recv(p, zb);
+       nhrp_peer_unref(p);
+       return 0;
+
+err:
+       zbuf_free(zb);
+       return 0;
+}
+
+int nhrp_packet_init(void)
+{
+       thread_add_read(master, nhrp_packet_recvraw, 0, os_socket());
+       return 0;
+}
diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
new file mode 100644 (file)
index 0000000..5095d55
--- /dev/null
@@ -0,0 +1,877 @@
+/* NHRP peer functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <netinet/if_ether.h>
+
+#include "zebra.h"
+#include "memory.h"
+#include "thread.h"
+#include "hash.h"
+
+#include "nhrpd.h"
+#include "nhrp_protocol.h"
+#include "os.h"
+
+struct ipv6hdr {
+       uint8_t priority_version;
+       uint8_t flow_lbl[3];
+       uint16_t payload_len;
+       uint8_t nexthdr;
+       uint8_t hop_limit;
+       struct in6_addr saddr;
+       struct in6_addr daddr;
+};
+
+static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
+
+static void nhrp_peer_check_delete(struct nhrp_peer *p)
+{
+       struct nhrp_interface *nifp = p->ifp->info;
+
+       if (p->ref || notifier_active(&p->notifier_list))
+               return;
+
+       THREAD_OFF(p->t_fallback);
+       hash_release(nifp->peer_hash, p);
+       nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
+       nhrp_vc_notify_del(p->vc, &p->vc_notifier);
+       XFREE(MTYPE_NHRP_PEER, p);
+}
+
+static int nhrp_peer_notify_up(struct thread *t)
+{
+       struct nhrp_peer *p = THREAD_ARG(t);
+       struct nhrp_vc *vc = p->vc;
+       struct interface *ifp = p->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+
+       p->t_fallback = NULL;
+       if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) {
+               p->online = 1;
+               nhrp_peer_ref(p);
+               notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
+               nhrp_peer_unref(p);
+       }
+
+       return 0;
+}
+
+static void __nhrp_peer_check(struct nhrp_peer *p)
+{
+       struct nhrp_vc *vc = p->vc;
+       struct interface *ifp = p->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+       unsigned online;
+
+       online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec);
+       if (p->online != online) {
+               THREAD_OFF(p->t_fallback);
+               if (online && notifier_active(&p->notifier_list)) {
+                       /* If we requested the IPsec connection, delay
+                        * the up notification a bit to allow things
+                        * settle down. This allows IKE to install
+                        * SPDs and SAs. */
+                       THREAD_TIMER_MSEC_ON(
+                               master, p->t_fallback,
+                               nhrp_peer_notify_up, p, 50);
+               } else {
+                       nhrp_peer_ref(p);
+                       p->online = online;
+                       if (online) {
+                               notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
+                       } else {
+                               p->requested = p->fallback_requested = 0;
+                               notifier_call(&p->notifier_list, NOTIFY_PEER_DOWN);
+                       }
+                       nhrp_peer_unref(p);
+               }
+       }
+}
+
+static void nhrp_peer_vc_notify(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_peer *p = container_of(n, struct nhrp_peer, vc_notifier);
+
+       switch (cmd) {
+       case NOTIFY_VC_IPSEC_CHANGED:
+               __nhrp_peer_check(p);
+               break;
+       case NOTIFY_VC_IPSEC_UPDATE_NBMA:
+               nhrp_peer_ref(p);
+               notifier_call(&p->notifier_list, NOTIFY_PEER_NBMA_CHANGING);
+               nhrp_peer_unref(p);
+               break;
+       }
+}
+
+static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_peer *p = container_of(n, struct nhrp_peer, ifp_notifier);
+       struct nhrp_interface *nifp;
+       struct nhrp_vc *vc;
+
+       nhrp_peer_ref(p);
+       switch (cmd) {
+       case NOTIFY_INTERFACE_UP:
+       case NOTIFY_INTERFACE_DOWN:
+               __nhrp_peer_check(p);
+               break;
+       case NOTIFY_INTERFACE_NBMA_CHANGED:
+               /* Source NBMA changed, rebind to new VC */
+               nifp = p->ifp->info;
+               vc = nhrp_vc_get(&nifp->nbma, &p->vc->remote.nbma, 1);
+               if (vc && p->vc != vc) {
+                       nhrp_vc_notify_del(p->vc, &p->vc_notifier);
+                       p->vc = vc;
+                       nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
+                       __nhrp_peer_check(p);
+               }
+               /* Fall-through to post config update */
+       case NOTIFY_INTERFACE_ADDRESS_CHANGED:
+               notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
+               break;
+       case NOTIFY_INTERFACE_MTU_CHANGED:
+               notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
+               break;
+       }
+       nhrp_peer_unref(p);
+}
+
+static unsigned int nhrp_peer_key(void *peer_data)
+{
+       struct nhrp_peer *p = peer_data;
+       return sockunion_hash(&p->vc->remote.nbma);
+}
+
+static int nhrp_peer_cmp(const void *cache_data, const void *key_data)
+{
+       const struct nhrp_peer *a = cache_data;
+       const struct nhrp_peer *b = key_data;
+       return a->ifp == b->ifp && a->vc == b->vc;
+}
+
+static void *nhrp_peer_create(void *data)
+{
+       struct nhrp_peer *p, *key = data;
+
+       p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p));
+       if (p) {
+               *p = (struct nhrp_peer) {
+                       .ref = 0,
+                       .ifp = key->ifp,
+                       .vc = key->vc,
+                       .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
+               };
+               nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
+               nhrp_interface_notify_add(p->ifp, &p->ifp_notifier, nhrp_peer_ifp_notify);
+       }
+       return p;
+}
+
+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma)
+{
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_peer key, *p;
+       struct nhrp_vc *vc;
+
+       if (!nifp->peer_hash) {
+               nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp);
+               if (!nifp->peer_hash) return NULL;
+       }
+
+       vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1);
+       if (!vc) return NULL;
+
+       key.ifp = ifp;
+       key.vc = vc;
+
+       p = hash_get(nifp->peer_hash, &key, nhrp_peer_create);
+       nhrp_peer_ref(p);
+       if (p->ref == 1) __nhrp_peer_check(p);
+
+       return p;
+}
+
+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p)
+{
+       if (p) p->ref++;
+       return p;
+}
+
+void nhrp_peer_unref(struct nhrp_peer *p)
+{
+       if (p) {
+               p->ref--;
+               nhrp_peer_check_delete(p);
+       }
+}
+
+static int nhrp_peer_request_timeout(struct thread *t)
+{
+       struct nhrp_peer *p = THREAD_ARG(t);
+       struct nhrp_vc *vc = p->vc;
+       struct interface *ifp = p->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+
+       p->t_fallback = NULL;
+
+       if (p->online)
+               return 0;
+
+       if (nifp->ipsec_fallback_profile && !p->prio && !p->fallback_requested) {
+               p->fallback_requested = 1;
+               vici_request_vc(nifp->ipsec_fallback_profile,
+                               &vc->local.nbma, &vc->remote.nbma, p->prio);
+               THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p, 30);
+       } else {
+               p->requested = p->fallback_requested = 0;
+       }
+
+       return 0;
+}
+
+int nhrp_peer_check(struct nhrp_peer *p, int establish)
+{
+       struct nhrp_vc *vc = p->vc;
+       struct interface *ifp = p->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+
+       if (p->online)
+               return 1;
+       if (!establish)
+               return 0;
+       if (p->requested)
+               return 0;
+       if (!nifp->ipsec_profile)
+               return 0;
+       if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
+               return 0;
+
+       p->prio = establish > 1;
+       p->requested = 1;
+       vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio);
+       THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p,
+                       (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30);
+
+       return 0;
+}
+
+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n, notifier_fn_t fn)
+{
+       notifier_add(n, &p->notifier_list, fn);
+}
+
+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n)
+{
+       notifier_del(n);
+       nhrp_peer_check_delete(p);
+}
+
+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb)
+{
+       char buf[2][256];
+
+       nhrp_packet_debug(zb, "Send");
+
+       if (!p->online)
+               return;
+
+       debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %s -> %s",
+               sockunion2str(&p->vc->local.nbma, buf[0], sizeof buf[0]),
+               sockunion2str(&p->vc->remote.nbma, buf[1], sizeof buf[1]));
+
+       os_sendmsg(zb->head, zbuf_used(zb),
+               p->ifp->ifindex,
+               sockunion_get_addr(&p->vc->remote.nbma),
+               sockunion_get_addrlen(&p->vc->remote.nbma));
+       zbuf_reset(zb);
+}
+
+static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p)
+{
+       struct zbuf *zb, payload;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_cie_header *cie;
+       struct nhrp_extension_header *ext;
+       struct nhrp_interface *nifp;
+       struct nhrp_peer *peer;
+
+       if (!(p->if_ad->flags & NHRP_IFF_SHORTCUT)) {
+               debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled");
+               /* FIXME: Send error indication? */
+               return;
+       }
+
+       if (p->if_ad->network_id &&
+           p->route_type == NHRP_ROUTE_OFF_NBMA &&
+           p->route_prefix.prefixlen < 8) {
+               debugf(NHRP_DEBUG_COMMON, "Shortcut to more generic than /8 dropped");
+               return;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req");
+
+       if (nhrp_route_address(p->ifp, &p->src_proto, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
+               return;
+
+#if 0
+       /* FIXME: Update requestors binding if CIE specifies holding time */
+       nhrp_cache_update_binding(
+                       NHRP_CACHE_CACHED, &p->src_proto,
+                       nhrp_peer_get(p->ifp, &p->src_nbma),
+                       htons(cie->holding_time));
+#endif
+
+       nifp = peer->ifp->info;
+
+       /* Create reply */
+       zb = zbuf_alloc(1500);
+       hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &p->src_nbma, &p->src_proto, &p->dst_proto);
+
+       /* Copied information from request */
+       hdr->flags = p->hdr->flags & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER|NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
+       hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE);
+       hdr->u.request_id = p->hdr->u.request_id;
+
+       /* CIE payload */
+       cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &p->if_ad->addr);
+       cie->holding_time = htons(p->if_ad->holdtime);
+       cie->mtu = htons(p->if_ad->mtu);
+       if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA)
+               cie->prefix_length = p->route_prefix.prefixlen;
+       else
+               cie->prefix_length = 8 * sockunion_get_addrlen(&p->if_ad->addr);
+
+       /* Handle extensions */
+       while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
+               switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+               case NHRP_EXTENSION_NAT_ADDRESS:
+                       if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC)
+                               break;
+                       ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+                       if (!ext) goto err;
+                       cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nat_nbma, &p->if_ad->addr);
+                       if (!cie) goto err;
+                       nhrp_ext_complete(zb, ext);
+                       break;
+               default:
+                       if (nhrp_ext_reply(zb, hdr, p->ifp, ext, &payload) < 0)
+                               goto err;
+                       break;
+               }
+       }
+
+       nhrp_packet_complete(zb, hdr);
+       nhrp_peer_send(peer, zb);
+err:
+       nhrp_peer_unref(peer);
+       zbuf_free(zb);
+}
+
+static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
+{
+       struct interface *ifp = p->ifp;
+       struct zbuf *zb, payload;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_cie_header *cie;
+       struct nhrp_extension_header *ext;
+       struct nhrp_cache *c;
+       union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr, *nbma_natoa;
+       int holdtime, prefix_len, hostprefix_len, natted = 0;
+       size_t paylen;
+       void *pay;
+
+       debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req");
+       hostprefix_len = 8 * sockunion_get_addrlen(&p->if_ad->addr);
+
+       if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma))
+               natted = 1;
+
+       /* Create reply */
+       zb = zbuf_alloc(1500);
+       hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY,
+               &p->src_nbma, &p->src_proto, &p->if_ad->addr);
+
+       /* Copied information from request */
+       hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE | NHRP_FLAG_REGISTRATION_NAT);
+       hdr->u.request_id = p->hdr->u.request_id;
+
+       /* Copy payload CIEs */
+       paylen = zbuf_used(&p->payload);
+       pay = zbuf_pushn(zb, paylen);
+       if (!pay) goto err;
+       memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen);
+       zbuf_init(&payload, pay, paylen, paylen);
+
+       while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto)) != NULL) {
+               prefix_len = cie->prefix_length;
+               if (prefix_len == 0 || prefix_len >= hostprefix_len)
+                       prefix_len = hostprefix_len;
+
+               if (prefix_len != hostprefix_len && !(p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
+                       cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
+                       continue;
+               }
+
+               /* We currently support only unique prefix registrations */
+               if (prefix_len != hostprefix_len) {
+                       cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
+                       continue;
+               }
+
+               proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) ? &p->src_proto : &cie_proto;
+               nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) ? &p->src_nbma : &cie_nbma;
+               nbma_natoa = NULL;
+               if (natted) {
+                       nbma_natoa = nbma_addr;
+                       nbma_addr = &p->peer->vc->remote.nbma;
+               }
+
+               holdtime = htons(cie->holding_time);
+               if (!holdtime) holdtime = p->if_ad->holdtime;
+
+               c = nhrp_cache_get(ifp, proto_addr, 1);
+               if (!c) {
+                       cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
+                       continue;
+               }
+
+               if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, nhrp_peer_ref(p->peer), htons(cie->mtu), nbma_natoa)) {
+                       cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
+                       continue;
+               }
+
+               cie->code = NHRP_CODE_SUCCESS;
+       }
+
+       /* Handle extensions */
+       while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
+               switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+               case NHRP_EXTENSION_NAT_ADDRESS:
+                       ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+                       if (!ext) goto err;
+                       zbuf_copy(zb, &payload, zbuf_used(&payload));
+                       if (natted) {
+                               nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
+                                       &p->peer->vc->remote.nbma,
+                                       &p->src_proto);
+                       }
+                       nhrp_ext_complete(zb, ext);
+                       break;
+               default:
+                       if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
+                               goto err;
+                       break;
+               }
+       }
+
+       nhrp_packet_complete(zb, hdr);
+       nhrp_peer_send(p->peer, zb);
+err:
+       zbuf_free(zb);
+}
+
+static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type, union sockunion *src, union sockunion *dst)
+{
+       switch (protocol_type) {
+       case ETH_P_IP: {
+                       struct iphdr *iph = zbuf_pull(zb, struct iphdr);
+                       if (iph) {
+                               if (src) sockunion_set(src, AF_INET, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
+                               if (dst) sockunion_set(dst, AF_INET, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
+                       }
+               }
+               break;
+       case ETH_P_IPV6: {
+                       struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr);
+                       if (iph) {
+                               if (src) sockunion_set(src, AF_INET6, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
+                               if (dst) sockunion_set(dst, AF_INET6, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
+                       }
+               }
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type, struct zbuf *pkt)
+{
+       union sockunion dst;
+       struct zbuf *zb, payload;
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_afi_data *if_ad;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_peer *p;
+       char buf[2][SU_ADDRSTRLEN];
+
+       if (!nifp->enabled) return;
+
+       payload = *pkt;
+       if (!parse_ether_packet(&payload, protocol_type, &dst, NULL))
+               return;
+
+       if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP)
+               return;
+
+       if_ad = &nifp->afi[family2afi(sockunion_family(&dst))];
+       if (!(if_ad->flags & NHRP_IFF_REDIRECT)) {
+               debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s about packet to %s ignored",
+                       sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
+                       sockunion2str(&dst, buf[1], sizeof buf[1]));
+               return;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s (online=%d) about packet to %s",
+               sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
+               p->online,
+               sockunion2str(&dst, buf[1], sizeof buf[1]));
+
+       /* Create reply */
+       zb = zbuf_alloc(1500);
+       hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma, &if_ad->addr, &dst);
+       hdr->hop_count = 0;
+
+       /* Payload is the packet causing indication */
+       zbuf_copy(zb, pkt, zbuf_used(pkt));
+       nhrp_packet_complete(zb, hdr);
+       nhrp_peer_send(p, zb);
+       nhrp_peer_unref(p);
+       zbuf_free(zb);
+}
+
+static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp)
+{
+       struct zbuf origmsg = pp->payload;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_reqid *reqid;
+       union sockunion src_nbma, src_proto, dst_proto;
+       char buf[2][SU_ADDRSTRLEN];
+
+       hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto);
+       if (!hdr) return;
+
+       debugf(NHRP_DEBUG_COMMON, "Error Indication from %s about packet to %s ignored",
+               sockunion2str(&pp->src_proto, buf[0], sizeof buf[0]),
+               sockunion2str(&dst_proto, buf[1], sizeof buf[1]));
+
+       reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
+       if (reqid)
+               reqid->cb(reqid, pp);
+}
+
+static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p)
+{
+       union sockunion dst;
+       char buf[2][SU_ADDRSTRLEN];
+
+       if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL, &dst))
+               return;
+
+       debugf(NHRP_DEBUG_COMMON, "Traffic Indication from %s about packet to %s: %s",
+               sockunion2str(&p->src_proto, buf[0], sizeof buf[0]),
+               sockunion2str(&dst, buf[1], sizeof buf[1]),
+               (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut" : "ignored");
+
+       if (p->if_ad->flags & NHRP_IFF_SHORTCUT)
+               nhrp_shortcut_initiate(&dst);
+}
+
+enum packet_type_t {
+       PACKET_UNKNOWN = 0,
+       PACKET_REQUEST,
+       PACKET_REPLY,
+       PACKET_INDICATION,
+};
+
+static struct {
+       enum packet_type_t type;
+       const char *name;
+       void (*handler)(struct nhrp_packet_parser *);
+} packet_types[] = {
+       [NHRP_PACKET_RESOLUTION_REQUEST] = {
+               .type = PACKET_REQUEST,
+               .name = "Resolution-Request",
+               .handler = nhrp_handle_resolution_req,
+       },
+       [NHRP_PACKET_RESOLUTION_REPLY] = {
+               .type = PACKET_REPLY,
+               .name = "Resolution-Reply",
+       },
+       [NHRP_PACKET_REGISTRATION_REQUEST] = {
+               .type = PACKET_REQUEST,
+               .name = "Registration-Request",
+               .handler = nhrp_handle_registration_request,
+       },
+       [NHRP_PACKET_REGISTRATION_REPLY] = {
+               .type = PACKET_REPLY,
+               .name = "Registration-Reply",
+       },
+       [NHRP_PACKET_PURGE_REQUEST] = {
+               .type = PACKET_REQUEST,
+               .name = "Purge-Request",
+       },
+       [NHRP_PACKET_PURGE_REPLY] = {
+               .type = PACKET_REPLY,
+               .name = "Purge-Reply",
+       },
+       [NHRP_PACKET_ERROR_INDICATION] = {
+               .type = PACKET_INDICATION,
+               .name = "Error-Indication",
+               .handler = nhrp_handle_error_ind,
+       },
+       [NHRP_PACKET_TRAFFIC_INDICATION] = {
+               .type = PACKET_INDICATION,
+               .name = "Traffic-Indication",
+               .handler = nhrp_handle_traffic_ind,
+       }
+};
+
+static void nhrp_peer_forward(struct nhrp_peer *p, struct nhrp_packet_parser *pp)
+{
+       struct zbuf *zb, extpl;
+       struct nhrp_packet_header *hdr;
+       struct nhrp_extension_header *ext, *dst;
+       struct nhrp_cie_header *cie;
+       struct nhrp_interface *nifp = pp->ifp->info;
+       struct nhrp_afi_data *if_ad = pp->if_ad;
+       union sockunion cie_nbma, cie_protocol;
+       uint16_t type, len;
+
+       if (pp->hdr->hop_count == 0)
+               return;
+
+       /* Create forward packet - copy header */
+       zb = zbuf_alloc(1500);
+       hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto, &pp->dst_proto);
+       hdr->flags = pp->hdr->flags;
+       hdr->hop_count = pp->hdr->hop_count - 1;
+       hdr->u.request_id = pp->hdr->u.request_id;
+
+       /* Copy payload */
+       zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
+
+       /* Copy extensions */
+       while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
+               type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
+               len = htons(ext->length);
+
+               if (type == NHRP_EXTENSION_END)
+                       break;
+
+               dst = nhrp_ext_push(zb, hdr, htons(ext->type));
+               if (!dst) goto err;
+
+               switch (type) {
+               case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
+               case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
+                       zbuf_put(zb, extpl.head, len);
+                       if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS) ==
+                           (packet_types[hdr->type].type == PACKET_REPLY)) {
+                               /* Check NHS list for forwarding loop */
+                               while ((cie = nhrp_cie_pull(&extpl, pp->hdr, &cie_nbma, &cie_protocol)) != NULL) {
+                                       if (sockunion_same(&p->vc->remote.nbma, &cie_nbma))
+                                               goto err;
+                               }
+                               /* Append our selves to the list */
+                               cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
+                               if (!cie) goto err;
+                               cie->holding_time = htons(if_ad->holdtime);
+                       }
+                       break;
+               default:
+                       if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY)
+                               /* FIXME: RFC says to just copy, but not
+                                * append our selves to the transit NHS list */
+                               goto err;
+               case NHRP_EXTENSION_RESPONDER_ADDRESS:
+                       /* Supported compulsory extensions, and any
+                        * non-compulsory that is not explicitly handled,
+                        * should be just copied. */
+                       zbuf_copy(zb, &extpl, len);
+                       break;
+               }
+               nhrp_ext_complete(zb, dst);
+       }
+
+       nhrp_packet_complete(zb, hdr);
+       nhrp_peer_send(p, zb);
+       zbuf_free(zb);
+       return;
+err:
+       nhrp_packet_debug(pp->pkt, "FWD-FAIL");
+       zbuf_free(zb);
+}
+
+static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
+{
+       char buf[2][SU_ADDRSTRLEN];
+       union sockunion src_nbma, src_proto, dst_proto;
+       struct nhrp_packet_header *hdr;
+       struct zbuf zhdr;
+       int reply;
+
+       if (likely(!(debug_flags & NHRP_DEBUG_COMMON)))
+               return;
+
+       zbuf_init(&zhdr, zb->buf, zb->tail-zb->buf, zb->tail-zb->buf);
+       hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto);
+
+       sockunion2str(&src_proto, buf[0], sizeof buf[0]);
+       sockunion2str(&dst_proto, buf[1], sizeof buf[1]);
+
+       reply = packet_types[hdr->type].type == PACKET_REPLY;
+       debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s",
+               dir,
+               packet_types[hdr->type].name ? : "Unknown",
+               hdr->type,
+               reply ? buf[1] : buf[0],
+               reply ? buf[0] : buf[1]);
+}
+
+static int proto2afi(uint16_t proto)
+{
+       switch (proto) {
+       case ETH_P_IP: return AFI_IP;
+       case ETH_P_IPV6: return AFI_IP6;
+       }
+       return AF_UNSPEC;
+}
+
+struct nhrp_route_info {
+       int local;
+       struct interface *ifp;
+       struct nhrp_vc *vc;
+};
+
+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
+{
+       char buf[2][SU_ADDRSTRLEN];
+       struct nhrp_packet_header *hdr;
+       struct nhrp_vc *vc = p->vc;
+       struct interface *ifp = p->ifp;
+       struct nhrp_interface *nifp = ifp->info;
+       struct nhrp_packet_parser pp;
+       struct nhrp_peer *peer = NULL;
+       struct nhrp_reqid *reqid;
+       const char *info = NULL;
+       union sockunion *target_addr;
+       unsigned paylen, extoff, extlen, realsize;
+       afi_t nbma_afi, proto_afi;
+
+       debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %s -> %s",
+               sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+               sockunion2str(&vc->local.nbma, buf[1], sizeof buf[1]));
+
+       if (!p->online) {
+               info = "peer not online";
+               goto drop;
+       }
+
+       if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) {
+               info = "bad checksum";
+               goto drop;
+       }
+
+       realsize = zbuf_used(zb);
+       hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto);
+       if (!hdr) {
+               info = "corrupt header";
+               goto drop;
+       }
+
+       pp.ifp = ifp;
+       pp.pkt = zb;
+       pp.hdr = hdr;
+       pp.peer = p;
+
+       nbma_afi = htons(hdr->afnum);
+       proto_afi = proto2afi(htons(hdr->protocol_type));
+       if (hdr->type > ZEBRA_NUM_OF(packet_types) ||
+           hdr->version != NHRP_VERSION_RFC2332 ||
+           nbma_afi >= AFI_MAX || proto_afi == AF_UNSPEC ||
+           packet_types[hdr->type].type == PACKET_UNKNOWN ||
+           htons(hdr->packet_size) > realsize) {
+               zlog_info("From %s: error: packet type %d, version %d, AFI %d, proto %x, size %d (real size %d)",
+                          sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+                          (int) hdr->type, (int) hdr->version,
+                          (int) nbma_afi, (int) htons(hdr->protocol_type),
+                          (int) htons(hdr->packet_size), (int) realsize);
+               goto drop;
+       }
+       pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[proto_afi];
+
+       extoff = htons(hdr->extension_offset);
+       if (extoff) {
+               if (extoff >= realsize) {
+                       info = "extoff larger than packet";
+                       goto drop;
+               }
+               paylen = extoff - (zb->head - zb->buf);
+       } else {
+               paylen = zbuf_used(zb);
+       }
+       zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen);
+       extlen = zbuf_used(zb);
+       zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen);
+
+       if (!nifp->afi[proto_afi].network_id) {
+               info = "nhrp not enabled";
+               goto drop;
+       }
+
+       nhrp_packet_debug(zb, "Recv");
+
+       /* FIXME: Check authentication here. This extension needs to be
+        * pre-handled. */
+
+       /* Figure out if this is local */
+       target_addr = (packet_types[hdr->type].type == PACKET_REPLY) ? &pp.src_proto : &pp.dst_proto;
+
+       if (sockunion_same(&pp.src_proto, &pp.dst_proto))
+               pp.route_type = NHRP_ROUTE_LOCAL;
+       else
+               pp.route_type = nhrp_route_address(pp.ifp, target_addr, &pp.route_prefix, &peer);
+
+       switch (pp.route_type) {
+       case NHRP_ROUTE_LOCAL:
+               nhrp_packet_debug(zb, "!LOCAL");
+               if (packet_types[hdr->type].type == PACKET_REPLY) {
+                       reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
+                       if (reqid) {
+                               reqid->cb(reqid, &pp);
+                               break;
+                       } else {
+                               nhrp_packet_debug(zb, "!UNKNOWN-REQID");
+                               /* FIXME: send error-indication */
+                       }
+               }
+       case NHRP_ROUTE_OFF_NBMA:
+               if (packet_types[hdr->type].handler) {
+                       packet_types[hdr->type].handler(&pp);
+                       break;
+               }
+               break;
+       case NHRP_ROUTE_NBMA_NEXTHOP:
+               nhrp_peer_forward(peer, &pp);
+               break;
+       case NHRP_ROUTE_BLACKHOLE:
+               break;
+       }
+
+drop:
+       if (info) {
+               zlog_info("From %s: error: %s",
+                         sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
+                         info);
+       }
+       if (peer) nhrp_peer_unref(peer);
+       zbuf_free(zb);
+}
diff --git a/nhrpd/nhrp_protocol.h b/nhrpd/nhrp_protocol.h
new file mode 100644 (file)
index 0000000..a4bc9fa
--- /dev/null
@@ -0,0 +1,128 @@
+/* nhrp_protocol.h - NHRP protocol definitions
+ *
+ * Copyright (c) 2007-2012 Timo Teräs <timo.teras@iki.fi>
+ *
+ * This software is licensed under the MIT License.
+ * See MIT-LICENSE.txt for additional details.
+ */
+
+#ifndef NHRP_PROTOCOL_H
+#define NHRP_PROTOCOL_H
+
+#include <stdint.h>
+
+/* NHRP Ethernet protocol number */
+#define ETH_P_NHRP                             0x2001
+
+/* NHRP Version */
+#define NHRP_VERSION_RFC2332                   1
+
+/* NHRP Packet Types */
+#define NHRP_PACKET_RESOLUTION_REQUEST         1
+#define NHRP_PACKET_RESOLUTION_REPLY           2
+#define NHRP_PACKET_REGISTRATION_REQUEST       3
+#define NHRP_PACKET_REGISTRATION_REPLY         4
+#define NHRP_PACKET_PURGE_REQUEST              5
+#define NHRP_PACKET_PURGE_REPLY                        6
+#define NHRP_PACKET_ERROR_INDICATION           7
+#define NHRP_PACKET_TRAFFIC_INDICATION         8
+
+/* NHRP Extension Types */
+#define NHRP_EXTENSION_FLAG_COMPULSORY         0x8000
+#define NHRP_EXTENSION_END                     0
+#define NHRP_EXTENSION_PAYLOAD                 0
+#define NHRP_EXTENSION_RESPONDER_ADDRESS       3
+#define NHRP_EXTENSION_FORWARD_TRANSIT_NHS     4
+#define NHRP_EXTENSION_REVERSE_TRANSIT_NHS     5
+#define NHRP_EXTENSION_AUTHENTICATION          7
+#define NHRP_EXTENSION_VENDOR                  8
+#define NHRP_EXTENSION_NAT_ADDRESS             9
+
+/* NHRP Error Indication Codes */
+#define NHRP_ERROR_UNRECOGNIZED_EXTENSION      1
+#define NHRP_ERROR_LOOP_DETECTED               2
+#define NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE        6
+#define NHRP_ERROR_PROTOCOL_ERROR              7
+#define NHRP_ERROR_SDU_SIZE_EXCEEDED           8
+#define NHRP_ERROR_INVALID_EXTENSION           9
+#define NHRP_ERROR_INVALID_RESOLUTION_REPLY    10
+#define NHRP_ERROR_AUTHENTICATION_FAILURE      11
+#define NHRP_ERROR_HOP_COUNT_EXCEEDED          15
+
+/* NHRP CIE Codes */
+#define NHRP_CODE_SUCCESS                      0
+#define NHRP_CODE_ADMINISTRATIVELY_PROHIBITED  4
+#define NHRP_CODE_INSUFFICIENT_RESOURCES       5
+#define NHRP_CODE_NO_BINDING_EXISTS            11
+#define NHRP_CODE_BINDING_NON_UNIQUE           13
+#define NHRP_CODE_UNIQUE_ADDRESS_REGISTERED     14
+
+/* NHRP Flags for Resolution request/reply */
+#define NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER  0x8000
+#define NHRP_FLAG_RESOLUTION_AUTHORATIVE       0x4000
+#define NHRP_FLAG_RESOLUTION_DESTINATION_STABLE        0x2000
+#define NHRP_FLAG_RESOLUTION_UNIQUE            0x1000
+#define NHRP_FLAG_RESOLUTION_SOURCE_STABLE     0x0800
+#define NHRP_FLAG_RESOLUTION_NAT               0x0002
+
+/* NHRP Flags for Registration request/reply */
+#define NHRP_FLAG_REGISTRATION_UNIQUE          0x8000
+#define NHRP_FLAG_REGISTRATION_NAT             0x0002
+
+/* NHRP Flags for Purge request/reply */
+#define NHRP_FLAG_PURGE_NO_REPLY               0x8000
+
+/* NHRP Authentication extension types (ala Cisco) */
+#define NHRP_AUTHENTICATION_PLAINTEXT          0x00000001
+
+/* NHRP Packet Structures */
+struct nhrp_packet_header {
+       /* Fixed header */
+       uint16_t        afnum;
+       uint16_t        protocol_type;
+       uint8_t         snap[5];
+       uint8_t         hop_count;
+       uint16_t        packet_size;
+       uint16_t        checksum;
+       uint16_t        extension_offset;
+       uint8_t         version;
+       uint8_t         type;
+       uint8_t         src_nbma_address_len;
+       uint8_t         src_nbma_subaddress_len;
+
+       /* Mandatory header */
+       uint8_t         src_protocol_address_len;
+       uint8_t         dst_protocol_address_len;
+       uint16_t        flags;
+       union {
+               uint32_t                request_id;
+               struct {
+                       uint16_t        code;
+                       uint16_t        offset;
+               } error;
+       } u;
+} __attribute__((packed));
+
+struct nhrp_cie_header {
+       uint8_t         code;
+       uint8_t         prefix_length;
+       uint16_t        unused;
+       uint16_t        mtu;
+       uint16_t        holding_time;
+       uint8_t         nbma_address_len;
+       uint8_t         nbma_subaddress_len;
+       uint8_t         protocol_address_len;
+       uint8_t         preference;
+} __attribute__((packed));
+
+struct nhrp_extension_header {
+       uint16_t        type;
+       uint16_t        length;
+} __attribute__((packed));
+
+struct nhrp_cisco_authentication_extension {
+       uint32_t        type;
+       uint8_t         secret[8];
+} __attribute__((packed));
+
+#endif
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
new file mode 100644 (file)
index 0000000..a80f3e0
--- /dev/null
@@ -0,0 +1,383 @@
+/* NHRP routing functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "nhrpd.h"
+#include "table.h"
+#include "memory.h"
+#include "stream.h"
+#include "log.h"
+#include "zclient.h"
+
+static struct zclient *zclient;
+static struct route_table *zebra_rib[AFI_MAX];
+
+struct route_info {
+       union sockunion via;
+       struct interface *ifp;
+       struct interface *nhrp_ifp;
+};
+
+static void nhrp_zebra_connected(struct zclient *zclient)
+{
+       /* No real VRF support yet -- bind only to the default vrf */
+       zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+static struct route_node *nhrp_route_update_get(const struct prefix *p, int create)
+{
+       struct route_node *rn;
+       afi_t afi = family2afi(PREFIX_FAMILY(p));
+
+       if (!zebra_rib[afi])
+               return NULL;
+
+       if (create) {
+               rn = route_node_get(zebra_rib[afi], p);
+               if (!rn->info) {
+                       rn->info = XCALLOC(MTYPE_NHRP_ROUTE, sizeof(struct route_info));
+                       route_lock_node(rn);
+               }
+               return rn;
+       } else {
+               return route_node_lookup(zebra_rib[afi], p);
+       }
+}
+
+static void nhrp_route_update_put(struct route_node *rn)
+{
+       struct route_info *ri = rn->info;
+
+       if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) {
+               XFREE(MTYPE_NHRP_ROUTE, rn->info);
+               rn->info = NULL;
+               route_unlock_node(rn);
+       }
+       route_unlock_node(rn);
+}
+
+static void nhrp_route_update_zebra(const struct prefix *p, union sockunion *nexthop, struct interface *ifp)
+{
+       struct route_node *rn;
+       struct route_info *ri;
+
+       rn = nhrp_route_update_get(p, (sockunion_family(nexthop) != AF_UNSPEC) || ifp);
+       if (rn) {
+               ri = rn->info;
+               ri->via = *nexthop;
+               ri->ifp = ifp;
+               nhrp_route_update_put(rn);
+       }
+}
+
+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp)
+{
+       struct route_node *rn;
+       struct route_info *ri;
+
+       rn = nhrp_route_update_get(p, ifp != NULL);
+       if (rn) {
+               ri = rn->info;
+               ri->nhrp_ifp = ifp;
+               nhrp_route_update_put(rn);
+       }
+}
+
+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu)
+{
+       int flags = 0;
+
+       if (zclient->sock < 0)
+               return;
+
+       switch (type) {
+       case NHRP_CACHE_NEGATIVE:
+               SET_FLAG(flags, ZEBRA_FLAG_REJECT);
+               break;
+       case NHRP_CACHE_DYNAMIC:
+       case NHRP_CACHE_NHS:
+       case NHRP_CACHE_STATIC:
+               /* Regular route, so these are announced
+                * to other routing daemons */
+               break;
+       default:
+               SET_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE);
+               break;
+       }
+       SET_FLAG(flags, ZEBRA_FLAG_INTERNAL);
+
+       if (p->family == AF_INET) {
+               struct in_addr *nexthop_ipv4;
+               struct zapi_ipv4 api;
+
+               memset(&api, 0, sizeof(api));
+               api.flags = flags;
+               api.type = ZEBRA_ROUTE_NHRP;
+               api.safi = SAFI_UNICAST;
+
+               SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+               if (nexthop) {
+                       nexthop_ipv4 = (struct in_addr *) sockunion_get_addr(nexthop);
+                       api.nexthop_num = 1;
+                       api.nexthop = &nexthop_ipv4;
+               }
+               if (ifp) {
+                       SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX);
+                       api.ifindex_num = 1;
+                       api.ifindex = &ifp->ifindex;
+               }
+               if (mtu) {
+                       SET_FLAG(api.message, ZAPI_MESSAGE_MTU);
+                       api.mtu = mtu;
+               }
+
+               if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) {
+                       char buf[2][INET_ADDRSTRLEN];
+                       zlog_debug("Zebra send: IPv4 route %s %s/%d nexthop %s metric %u"
+                               " count %d dev %s",
+                               add ? "add" : "del",
+                               inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
+                               p->prefixlen,
+                               nexthop ? inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])) : "<onlink>",
+                               api.metric, api.nexthop_num, ifp->name);
+               }
+
+               zapi_ipv4_route(
+                       add ? ZEBRA_IPV4_ROUTE_ADD : ZEBRA_IPV4_ROUTE_DELETE,
+                       zclient, (struct prefix_ipv4 *) p, &api);
+       } else if (p->family == AF_INET6) {
+               struct in6_addr *nexthop_ipv6;
+               struct zapi_ipv6 api;
+
+               memset(&api, 0, sizeof(api));
+               api.flags = flags;
+               api.type = ZEBRA_ROUTE_NHRP;
+               api.safi = SAFI_UNICAST;
+
+               SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+               if (nexthop) {
+                       nexthop_ipv6 = (struct in6_addr *) sockunion_get_addr(nexthop);
+                       api.nexthop_num = 1;
+                       api.nexthop = &nexthop_ipv6;
+               }
+               if (ifp) {
+                       SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX);
+                       api.ifindex_num = 1;
+                       api.ifindex = &ifp->ifindex;
+               }
+               if (mtu) {
+                       SET_FLAG(api.message, ZAPI_MESSAGE_MTU);
+                       api.mtu = mtu;
+               }
+
+               if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) {
+                       char buf[2][INET6_ADDRSTRLEN];
+                       zlog_debug("Zebra send: IPv6 route %s %s/%d nexthop %s metric %u"
+                               " count %d dev %s",
+                               add ? "add" : "del",
+                               inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])),
+                               p->prefixlen,
+                               nexthop ? inet_ntop(AF_INET6, api.nexthop[0], buf[1], sizeof(buf[1])) : "<onlink>",
+                               api.metric, api.nexthop_num, ifp->name);
+               }
+
+               zapi_ipv6_route(
+                       add ? ZEBRA_IPV6_ROUTE_ADD : ZEBRA_IPV6_ROUTE_DELETE,
+                       zclient, (struct prefix_ipv6 *) p, &api);
+       }
+}
+
+int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id)
+{
+       struct stream *s;
+       struct interface *ifp = NULL;
+       struct prefix prefix;
+       union sockunion nexthop_addr;
+       unsigned char message, nexthop_num, ifindex_num;
+       unsigned ifindex;
+       char buf[2][PREFIX_STRLEN];
+       int i, afaddrlen, added;
+
+       s = zclient->ibuf;
+       memset(&prefix, 0, sizeof(prefix));
+       sockunion_family(&nexthop_addr) = AF_UNSPEC;
+
+       /* Type, flags, message. */
+       /*type =*/ stream_getc(s);
+       /*flags =*/ stream_getc(s);
+       message = stream_getc(s);
+
+       /* Prefix */
+       switch (cmd) {
+       case ZEBRA_IPV4_ROUTE_ADD:
+       case ZEBRA_IPV4_ROUTE_DELETE:
+               prefix.family = AF_INET;
+               break;
+       case ZEBRA_IPV6_ROUTE_ADD:
+       case ZEBRA_IPV6_ROUTE_DELETE:
+               prefix.family = AF_INET6;
+               break;
+       default:
+               return -1;
+       }
+       afaddrlen = family2addrsize(prefix.family);
+       prefix.prefixlen = stream_getc(s);
+       stream_get(&prefix.u.val, s, PSIZE(prefix.prefixlen));
+
+       /* Nexthop, ifindex, distance, metric. */
+       if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP|ZAPI_MESSAGE_IFINDEX)) {
+               nexthop_num = stream_getc(s);
+               for (i = 0; i < nexthop_num; i++) {
+                       stream_get(buf[0], s, afaddrlen);
+                       if (i == 0) sockunion_set(&nexthop_addr, prefix.family, (u_char*) buf[0], afaddrlen);
+               }
+               ifindex_num = stream_getc(s);
+               for (i = 0; i < ifindex_num; i++) {
+                       ifindex = stream_getl(s);
+                       if (i == 0 && ifindex != IFINDEX_INTERNAL)
+                               ifp = if_lookup_by_index(ifindex);
+               }
+       }
+       if (CHECK_FLAG(message, ZAPI_MESSAGE_DISTANCE))
+               /*distance =*/ stream_getc(s);
+       if (CHECK_FLAG(message, ZAPI_MESSAGE_METRIC))
+               /*metric =*/ stream_getl(s);
+
+       added = (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV6_ROUTE_ADD);
+       debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %s via %s dev %s",
+               added ? "add" : "del",
+               prefix2str(&prefix, buf[0], sizeof buf[0]),
+               sockunion2str(&nexthop_addr, buf[1], sizeof buf[1]),
+               ifp ? ifp->name : "(none)");
+
+       nhrp_route_update_zebra(&prefix, &nexthop_addr, ifp);
+       nhrp_shortcut_prefix_change(&prefix, !added);
+
+       return 0;
+}
+
+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp)
+{
+       struct route_node *rn;
+       struct route_info *ri;
+       struct prefix lookup;
+       afi_t afi = family2afi(sockunion_family(addr));
+       char buf[PREFIX_STRLEN];
+
+       sockunion2hostprefix(addr, &lookup);
+
+       rn = route_node_match(zebra_rib[afi], &lookup);
+       if (!rn) return 0;
+
+       ri = rn->info;
+       if (ri->nhrp_ifp) {
+               debugf(NHRP_DEBUG_ROUTE, "lookup %s: nhrp_if=%s",
+                       prefix2str(&lookup, buf, sizeof buf),
+                       ri->nhrp_ifp->name);
+
+               if (via) sockunion_family(via) = AF_UNSPEC;
+               if (ifp) *ifp = ri->nhrp_ifp;
+       } else {
+               debugf(NHRP_DEBUG_ROUTE, "lookup %s: zebra route dev %s",
+                       prefix2str(&lookup, buf, sizeof buf),
+                       ri->ifp ? ri->ifp->name : "(none)");
+
+               if (via) *via = ri->via;
+               if (ifp) *ifp = ri->ifp;
+       }
+       if (p) *p = rn->p;
+       route_unlock_node(rn);
+       return 1;
+}
+
+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer)
+{
+       struct interface *ifp = in_ifp;
+       struct nhrp_interface *nifp;
+       struct nhrp_cache *c;
+       union sockunion via[4];
+       uint32_t network_id = 0;
+       afi_t afi = family2afi(sockunion_family(addr));
+       int i;
+
+       if (ifp) {
+               nifp = ifp->info;
+               network_id = nifp->afi[afi].network_id;
+
+               c = nhrp_cache_get(ifp, addr, 0);
+               if (c && c->cur.type == NHRP_CACHE_LOCAL) {
+                       if (p) memset(p, 0, sizeof(*p));
+                       return NHRP_ROUTE_LOCAL;
+               }
+       }
+
+       for (i = 0; i < 4; i++) {
+               if (!nhrp_route_get_nexthop(addr, p, &via[i], &ifp))
+                       return NHRP_ROUTE_BLACKHOLE;
+               if (ifp) {
+                       /* Departing from nbma network? */
+                       nifp = ifp->info;
+                       if (network_id && network_id != nifp->afi[afi].network_id)
+                               return NHRP_ROUTE_OFF_NBMA;
+               }
+               if (sockunion_family(&via[i]) == AF_UNSPEC)
+                       break;
+               /* Resolve via node, but return the prefix of first match */
+               addr = &via[i];
+               p = NULL;
+       }
+
+       if (ifp) {
+               c = nhrp_cache_get(ifp, addr, 0);
+               if (c && c->cur.type >= NHRP_CACHE_DYNAMIC) {
+                       if (p) memset(p, 0, sizeof(*p));
+                       if (c->cur.type == NHRP_CACHE_LOCAL)
+                               return NHRP_ROUTE_LOCAL;
+                       if (peer) *peer = nhrp_peer_ref(c->cur.peer);
+                       return NHRP_ROUTE_NBMA_NEXTHOP;
+               }
+       }
+
+       return NHRP_ROUTE_BLACKHOLE;
+}
+
+void nhrp_zebra_init(void)
+{
+       zebra_rib[AFI_IP] = route_table_init();
+       zebra_rib[AFI_IP6] = route_table_init();
+
+       zclient = zclient_new(master);
+       zclient->zebra_connected = nhrp_zebra_connected;
+       zclient->interface_add = nhrp_interface_add;
+       zclient->interface_delete = nhrp_interface_delete;
+       zclient->interface_up = nhrp_interface_up;
+       zclient->interface_down = nhrp_interface_down;
+       zclient->interface_address_add = nhrp_interface_address_add;
+       zclient->interface_address_delete = nhrp_interface_address_delete;
+       zclient->ipv4_route_add = nhrp_route_read;
+       zclient->ipv4_route_delete = nhrp_route_read;
+       zclient->ipv6_route_add = nhrp_route_read;
+       zclient->ipv6_route_delete = nhrp_route_read;
+
+       zclient_init(zclient, ZEBRA_ROUTE_NHRP);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_KERNEL, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_CONNECT, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_STATIC, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_RIP, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_OSPF, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_ISIS, VRF_DEFAULT);
+       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_BGP, VRF_DEFAULT);
+}
+
+void nhrp_zebra_terminate(void)
+{
+       zclient_stop(zclient);
+       route_table_finish(zebra_rib[AFI_IP]);
+       route_table_finish(zebra_rib[AFI_IP6]);
+}
+
diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c
new file mode 100644 (file)
index 0000000..60c6392
--- /dev/null
@@ -0,0 +1,402 @@
+/* NHRP shortcut related functions
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "nhrpd.h"
+#include "table.h"
+#include "memory.h"
+#include "thread.h"
+#include "log.h"
+#include "nhrp_protocol.h"
+
+static struct route_table *shortcut_rib[AFI_MAX];
+
+static int nhrp_shortcut_do_purge(struct thread *t);
+static void nhrp_shortcut_delete(struct nhrp_shortcut *s);
+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s);
+
+static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
+{
+       char buf[PREFIX_STRLEN];
+
+       if (s->expiring && s->cache && s->cache->used) {
+               debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
+                       prefix2str(s->p, buf, sizeof buf));
+               nhrp_shortcut_send_resolution_req(s);
+       }
+}
+
+static int nhrp_shortcut_do_expire(struct thread *t)
+{
+       struct nhrp_shortcut *s = THREAD_ARG(t);
+
+       s->t_timer = NULL;
+       THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, s->holding_time/3);
+       s->expiring = 1;
+       nhrp_shortcut_check_use(s);
+
+       return 0;
+}
+
+static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd)
+{
+       struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier);
+
+       switch (cmd) {
+       case NOTIFY_CACHE_UP:
+               if (!s->route_installed) {
+                       nhrp_route_announce(1, s->type, s->p, NULL, &s->cache->remote_addr, 0);
+                       s->route_installed = 1;
+               }
+               break;
+       case NOTIFY_CACHE_USED:
+               nhrp_shortcut_check_use(s);
+               break;
+       case NOTIFY_CACHE_DOWN:
+       case NOTIFY_CACHE_DELETE:
+               if (s->route_installed) {
+                       nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
+                       s->route_installed = 0;
+               }
+               if (cmd == NOTIFY_CACHE_DELETE)
+                       nhrp_shortcut_delete(s);
+               break;
+       }
+}
+
+static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time)
+{
+       s->type = type;
+       if (c != s->cache) {
+               if (s->cache) {
+                       nhrp_cache_notify_del(s->cache, &s->cache_notifier);
+                       s->cache = NULL;
+               }
+               s->cache = c;
+               if (s->cache) {
+                       nhrp_cache_notify_add(s->cache, &s->cache_notifier, nhrp_shortcut_cache_notify);
+                       if (s->cache->route_installed) {
+                               /* Force renewal of Zebra announce on prefix change */
+                               s->route_installed = 0;
+                               nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP);
+                       }
+               }
+               if (!s->cache || !s->cache->route_installed)
+                       nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN);
+       }
+       if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) {
+               nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0);
+               s->route_installed = 1;
+       } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) {
+               nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
+               s->route_installed = 0;
+       }
+
+       THREAD_OFF(s->t_timer);
+       if (holding_time) {
+               s->expiring = 0;
+               s->holding_time = holding_time;
+               THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_expire, s, 2*holding_time/3);
+       }
+}
+
+static void nhrp_shortcut_delete(struct nhrp_shortcut *s)
+{
+       struct route_node *rn;
+       afi_t afi = family2afi(PREFIX_FAMILY(s->p));
+       char buf[PREFIX_STRLEN];
+
+       THREAD_OFF(s->t_timer);
+       nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+
+       debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged",
+               prefix2str(s->p, buf, sizeof buf));
+
+       nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0);
+
+       /* Delete node */
+       rn = route_node_lookup(shortcut_rib[afi], s->p);
+       if (rn) {
+               XFREE(MTYPE_NHRP_SHORTCUT, rn->info);
+               rn->info = NULL;
+               route_unlock_node(rn);
+               route_unlock_node(rn);
+       }
+}
+
+static int nhrp_shortcut_do_purge(struct thread *t)
+{
+       struct nhrp_shortcut *s = THREAD_ARG(t);
+       s->t_timer = NULL;
+       nhrp_shortcut_delete(s);
+       return 0;
+}
+
+static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p)
+{
+       struct nhrp_shortcut *s;
+       struct route_node *rn;
+       char buf[PREFIX_STRLEN];
+       afi_t afi = family2afi(PREFIX_FAMILY(p));
+
+       if (!shortcut_rib[afi])
+               return 0;
+
+       rn = route_node_get(shortcut_rib[afi], p);
+       if (!rn->info) {
+               s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT, sizeof(struct nhrp_shortcut));
+               s->type = NHRP_CACHE_INVALID;
+               s->p = &rn->p;
+
+               debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
+                       prefix2str(s->p, buf, sizeof buf));
+       } else {
+               s = rn->info;
+               route_unlock_node(rn);
+       }
+       return s;
+}
+
+static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg)
+{
+       struct nhrp_packet_parser *pp = arg;
+       struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid);
+       struct nhrp_shortcut *ps;
+       struct nhrp_extension_header *ext;
+       struct nhrp_cie_header *cie;
+       struct nhrp_cache *c = NULL;
+       union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, nat_nbma;
+       struct prefix prefix, route_prefix;
+       struct zbuf extpl;
+       char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN];
+       int holding_time = pp->if_ad->holdtime;
+
+       nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+       THREAD_OFF(s->t_timer);
+       THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 1);
+
+       if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
+               if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION &&
+                   pp->hdr->u.error.code == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) {
+                       debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution: Protocol address unreachable");
+                       nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE, NULL, holding_time);
+               } else {
+                       debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution failed");
+               }
+               return;
+       }
+
+       /* Parse extensions */
+       memset(&nat_nbma, 0, sizeof nat_nbma);
+       while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
+               switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
+               case NHRP_EXTENSION_NAT_ADDRESS:
+                       nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto);
+                       break;
+               }
+       }
+
+       /* Minor sanity check */
+       prefix2sockunion(s->p, &cie_proto);
+       if (!sockunion_same(&cie_proto, &pp->dst_proto)) {
+               debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s",
+                       sockunion2str(&cie_proto, buf[0], sizeof buf[0]),
+                       sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1]));
+       }
+
+       /* One or more CIEs should be given as reply, we support only one */
+       cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto);
+       if (!cie || cie->code != NHRP_CODE_SUCCESS) {
+               debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d", cie ? cie->code : -1);
+               return;
+       }
+
+       proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &pp->dst_proto;
+       if (cie->holding_time)
+               holding_time = htons(cie->holding_time);
+
+       prefix = *s->p;
+       prefix.prefixlen = cie->prefix_length;
+
+       /* Sanity check prefix length */
+       if (prefix.prefixlen >= 8*prefix_blen(&prefix) || prefix.prefixlen == 0) {
+               prefix.prefixlen = 8*prefix_blen(&prefix);
+       } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL) == NHRP_ROUTE_NBMA_NEXTHOP) {
+               if (prefix.prefixlen < route_prefix.prefixlen)
+                       prefix.prefixlen = route_prefix.prefixlen;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
+               prefix2str(&prefix, bufp, sizeof bufp),
+               sockunion2str(proto, buf[0], sizeof buf[0]),
+               sockunion2str(&cie_nbma, buf[1], sizeof buf[1]),
+               sockunion2str(&nat_nbma, buf[2], sizeof buf[2]),
+               htons(cie->holding_time));
+
+       /* Update cache entry for the protocol to nbma binding */
+       if (sockunion_family(&nat_nbma) != AF_UNSPEC) {
+               nbma = &nat_nbma;
+               nbma_natoa = &cie_nbma;
+       } else {
+               nbma = &cie_nbma;
+               nbma_natoa = NULL;
+       }
+       if (sockunion_family(nbma)) {
+               c = nhrp_cache_get(pp->ifp, proto, 1);
+               if (c) {
+                       nhrp_cache_update_binding(
+                                       c, NHRP_CACHE_CACHED, holding_time,
+                                       nhrp_peer_get(pp->ifp, nbma),
+                                       htons(cie->mtu), nbma_natoa);
+               }
+       }
+
+       /* Update shortcut entry for subnet to protocol gw binding */
+       if (c && !sockunion_same(proto, &pp->dst_proto)) {
+               ps = nhrp_shortcut_get(&prefix);
+               if (ps) {
+                       ps->addr = s->addr;
+                       nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, holding_time);
+               }
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
+}
+
+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
+{
+       struct zbuf *zb;
+       struct nhrp_packet_header *hdr;
+       struct interface *ifp;
+       struct nhrp_interface *nifp;
+       struct nhrp_peer *peer;
+
+       if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
+               return;
+
+       if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
+               s->type = NHRP_CACHE_INCOMPLETE;
+
+       ifp = peer->ifp;
+       nifp = ifp->info;
+
+       /* Create request */
+       zb = zbuf_alloc(1500);
+       hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REQUEST,
+               &nifp->nbma, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &s->addr);
+       hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, nhrp_shortcut_recv_resolution_rep));
+       hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER |
+                          NHRP_FLAG_RESOLUTION_AUTHORATIVE |
+                          NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
+
+       /* RFC2332 - One or zero CIEs, if CIE is present contains:
+        *  - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
+        *  - MTU: MTU of the source station
+        *  - Holding Time: Max time to cache the source information
+        * */
+       /* FIXME: Send holding time, and MTU */
+
+       nhrp_ext_request(zb, hdr, ifp);
+
+       /* Cisco NAT detection extension */
+       hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
+       nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
+
+       nhrp_packet_complete(zb, hdr);
+
+       nhrp_peer_send(peer, zb);
+       nhrp_peer_unref(peer);
+       zbuf_free(zb);
+}
+
+void nhrp_shortcut_initiate(union sockunion *addr)
+{
+       struct prefix p;
+       struct nhrp_shortcut *s;
+
+       sockunion2hostprefix(addr, &p);
+       s = nhrp_shortcut_get(&p);
+       if (s && s->type != NHRP_CACHE_INCOMPLETE) {
+               s->addr = *addr;
+               THREAD_OFF(s->t_timer);
+               THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 30);
+               nhrp_shortcut_send_resolution_req(s);
+       }
+}
+
+void nhrp_shortcut_init(void)
+{
+       shortcut_rib[AFI_IP] = route_table_init();
+       shortcut_rib[AFI_IP6] = route_table_init();
+}
+
+void nhrp_shortcut_terminate(void)
+{
+       route_table_finish(shortcut_rib[AFI_IP]);
+       route_table_finish(shortcut_rib[AFI_IP6]);
+}
+
+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx)
+{
+       struct route_table *rt = shortcut_rib[afi];
+       struct route_node *rn;
+       route_table_iter_t iter;
+
+       if (!rt) return;
+
+       route_table_iter_init(&iter, rt);
+       while ((rn = route_table_iter_next(&iter)) != NULL) {
+               if (rn->info) cb(rn->info, ctx);
+       }
+       route_table_iter_cleanup(&iter);
+}
+
+struct purge_ctx {
+       const struct prefix *p;
+       int deleted;
+};
+
+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
+{
+       THREAD_OFF(s->t_timer);
+       nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
+
+       if (force) {
+               /* Immediate purge on route with draw or pending shortcut */
+               THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 5);
+       } else {
+               /* Soft expire - force immediate renewal, but purge
+                * in few seconds to make sure stale route is not
+                * used too long. In practice most purges are caused
+                * by hub bgp change, but target usually stays same.
+                * This allows to keep nhrp route up, and to not
+                * cause temporary rerouting via hubs causing latency
+                * jitter. */
+               THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 3000);
+               s->expiring = 1;
+               nhrp_shortcut_check_use(s);
+       }
+}
+
+static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx)
+{
+       struct purge_ctx *pctx = ctx;
+
+       if (prefix_match(pctx->p, s->p))
+               nhrp_shortcut_purge(s, pctx->deleted || !s->cache);
+}
+
+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
+{
+       struct purge_ctx pctx = {
+               .p = p,
+               .deleted = deleted,
+       };
+       nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)), nhrp_shortcut_purge_prefix, &pctx);
+}
+
diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c
new file mode 100644 (file)
index 0000000..f9e1ee0
--- /dev/null
@@ -0,0 +1,217 @@
+/* NHRP virtual connection
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zebra.h"
+#include "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "thread.h"
+#include "jhash.h"
+
+#include "nhrpd.h"
+#include "os.h"
+
+struct child_sa {
+       uint32_t id;
+       struct nhrp_vc *vc;
+       struct list_head childlist_entry;
+};
+
+static struct hash *nhrp_vc_hash;
+static struct list_head childlist_head[512];
+
+static unsigned int nhrp_vc_key(void *peer_data)
+{
+       struct nhrp_vc *vc = peer_data;
+       return jhash_2words(
+               sockunion_hash(&vc->local.nbma),
+               sockunion_hash(&vc->remote.nbma),
+               0);
+}
+
+static int nhrp_vc_cmp(const void *cache_data, const void *key_data)
+{
+       const struct nhrp_vc *a = cache_data;
+       const struct nhrp_vc *b = key_data;
+       return  sockunion_same(&a->local.nbma, &b->local.nbma) &&
+               sockunion_same(&a->remote.nbma, &b->remote.nbma);
+}
+
+static void *nhrp_vc_alloc(void *data)
+{
+       struct nhrp_vc *vc, *key = data;
+
+       vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc));
+       if (vc) {
+               *vc = (struct nhrp_vc) {
+                       .local.nbma = key->local.nbma,
+                       .remote.nbma = key->remote.nbma,
+                       .notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list),
+               };
+       }
+
+       return vc;
+}
+
+static void nhrp_vc_free(void *data)
+{
+       XFREE(MTYPE_NHRP_VC, data);
+}
+
+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create)
+{
+       struct nhrp_vc key;
+       key.local.nbma = *src;
+       key.remote.nbma = *dst;
+       return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0);
+}
+
+static void nhrp_vc_check_delete(struct nhrp_vc *vc)
+{
+       if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list))
+               return;
+       hash_release(nhrp_vc_hash, vc);
+       nhrp_vc_free(vc);
+}
+
+static void nhrp_vc_update(struct nhrp_vc *vc, long cmd)
+{
+       vc->updating = 1;
+       notifier_call(&vc->notifier_list, cmd);
+       vc->updating = 0;
+       nhrp_vc_check_delete(vc);
+}
+
+static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc)
+{
+       vc->local.id[0] = 0;
+       vc->local.certlen = 0;
+       vc->remote.id[0] = 0;
+       vc->remote.certlen = 0;
+}
+
+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc)
+{
+       char buf[2][SU_ADDRSTRLEN];
+       struct child_sa *sa = NULL, *lsa;
+       uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head);
+       int abort_migration = 0;
+
+       list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) {
+               if (lsa->id == child_id) {
+                       sa = lsa;
+                       break;
+               }
+       }
+
+       if (!sa) {
+               if (!vc) return 0;
+
+               sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa));
+               if (!sa) return 0;
+
+               *sa = (struct child_sa) {
+                       .id = child_id,
+                       .childlist_entry = LIST_INITIALIZER(sa->childlist_entry),
+                       .vc = NULL,
+               };
+               list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]);
+       }
+
+       if (sa->vc == vc)
+               return 0;
+
+       if (vc) {
+               /* Attach first to new VC */
+               vc->ipsec++;
+               nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED);
+       }
+       if (sa->vc && vc) {
+               /* Notify old VC of migration */
+               sa->vc->abort_migration = 0;
+               debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s",
+                       sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]),
+                       sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]));
+               nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA);
+               abort_migration = sa->vc->abort_migration;
+       }
+       if (sa->vc) {
+               /* Deattach old VC */
+               sa->vc->ipsec--;
+               if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc);
+               nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED);
+       }
+
+       /* Update */
+       sa->vc = vc;
+       if (!vc) {
+               list_del(&sa->childlist_entry);
+               XFREE(MTYPE_NHRP_VC, sa);
+       }
+
+       return abort_migration;
+}
+
+void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action)
+{
+       notifier_add(n, &vc->notifier_list, action);
+}
+
+void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n)
+{
+       notifier_del(n);
+       nhrp_vc_check_delete(vc);
+}
+
+
+struct nhrp_vc_iterator_ctx {
+       void (*cb)(struct nhrp_vc *, void *);
+       void *ctx;
+};
+
+static void nhrp_vc_iterator(struct hash_backet *b, void *ctx)
+{
+       struct nhrp_vc_iterator_ctx *ic = ctx;
+       ic->cb(b->data, ic->ctx);
+}
+
+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx)
+{
+       struct nhrp_vc_iterator_ctx ic = {
+               .cb = cb,
+               .ctx = ctx,
+       };
+       hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic);
+}
+
+void nhrp_vc_init(void)
+{
+       size_t i;
+
+       nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp);
+       for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++)
+               list_init(&childlist_head[i]);
+}
+
+void nhrp_vc_reset(void)
+{
+       struct child_sa *sa, *n;
+       size_t i;
+
+       for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) {
+               list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry)
+                       nhrp_vc_ipsec_updown(sa->id, 0);
+       }
+}
+
+void nhrp_vc_terminate(void)
+{
+       nhrp_vc_reset();
+       hash_clean(nhrp_vc_hash, nhrp_vc_free);
+}
diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c
new file mode 100644 (file)
index 0000000..91269f2
--- /dev/null
@@ -0,0 +1,983 @@
+/* NHRP vty handling
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zebra.h"
+#include "command.h"
+#include "zclient.h"
+#include "stream.h"
+
+#include "nhrpd.h"
+#include "netlink.h"
+
+static struct cmd_node zebra_node = {
+       .node   = ZEBRA_NODE,
+       .prompt = "%s(config-router)# ",
+       .vtysh  = 1,
+};
+
+static struct cmd_node nhrp_interface_node = {
+       .node   = INTERFACE_NODE,
+       .prompt = "%s(config-if)# ",
+       .vtysh  = 1,
+};
+
+#define NHRP_DEBUG_FLAGS_CMD "(all|common|event|interface|kernel|route|vici)"
+
+#define NHRP_DEBUG_FLAGS_STR           \
+       "All messages\n"                \
+       "Common messages (default)\n"   \
+       "Event manager messages\n"      \
+       "Interface messages\n"          \
+       "Kernel messages\n"             \
+       "Route messages\n"              \
+       "VICI messages\n"
+
+static const struct message debug_flags_desc[] = {
+       { NHRP_DEBUG_ALL, "all" },
+       { NHRP_DEBUG_COMMON, "common" },
+       { NHRP_DEBUG_IF, "interface" },
+       { NHRP_DEBUG_KERNEL, "kernel" },
+       { NHRP_DEBUG_ROUTE, "route" },
+       { NHRP_DEBUG_VICI, "vici" },
+       { NHRP_DEBUG_EVENT, "event" },
+       { 0, NULL },
+};
+
+static const struct message interface_flags_desc[] = {
+       { NHRP_IFF_SHORTCUT, "shortcut" },
+       { NHRP_IFF_REDIRECT, "redirect" },
+       { NHRP_IFF_REG_NO_UNIQUE, "registration no-unique" },
+       { 0, NULL },
+};
+
+static int nhrp_vty_return(struct vty *vty, int ret)
+{
+       static const char * const errmsgs[] = {
+               [NHRP_ERR_FAIL]                         = "Command failed",
+               [NHRP_ERR_NO_MEMORY]                    = "Out of memory",
+               [NHRP_ERR_UNSUPPORTED_INTERFACE]        = "NHRP not supported on this interface",
+               [NHRP_ERR_NHRP_NOT_ENABLED]             = "NHRP not enabled (set 'nhrp network-id' first)",
+               [NHRP_ERR_ENTRY_EXISTS]                 = "Entry exists already",
+               [NHRP_ERR_ENTRY_NOT_FOUND]              = "Entry not found",
+               [NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH]    = "Protocol address family does not match command (ip/ipv6 mismatch)",
+       };
+       const char *str = NULL;
+       char buf[256];
+
+       if (ret == NHRP_OK)
+               return CMD_SUCCESS;
+
+       if (ret > 0 && ret <= (int)ZEBRA_NUM_OF(errmsgs))
+               if (errmsgs[ret])
+                       str = errmsgs[ret];
+
+       if (!str) {
+               str = buf;
+               snprintf(buf, sizeof(buf), "Unknown error %d", ret);
+       }
+
+       vty_out (vty, "%% %s%s", str, VTY_NEWLINE);
+
+       return CMD_WARNING;
+}
+
+static int toggle_flag(
+       struct vty *vty, const struct message *flag_desc,
+       const char *name, int on_off, unsigned *flags)
+{
+       int i;
+
+       for (i = 0; flag_desc[i].str != NULL; i++) {
+               if (strcmp(flag_desc[i].str, name) != 0)
+                       continue;
+               if (on_off)
+                       *flags |= flag_desc[i].key;
+               else
+                       *flags &= ~flag_desc[i].key;
+               return CMD_SUCCESS;
+       }
+
+       vty_out(vty, "%% Invalid value %s%s", name, VTY_NEWLINE);
+       return CMD_WARNING;
+}
+
+#ifndef NO_DEBUG
+
+DEFUN(show_debugging_nhrp, show_debugging_nhrp_cmd,
+       "show debugging nhrp",
+       SHOW_STR
+       "Debugging information\n"
+       "NHRP configuration\n")
+{
+       int i;
+
+       vty_out(vty, "NHRP debugging status:%s", VTY_NEWLINE);
+
+       for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+               if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+                       continue;
+               if (!(debug_flags_desc[i].key & debug_flags))
+                       continue;
+
+               vty_out(vty, "  NHRP %s debugging is on%s",
+                       debug_flags_desc[i].str, VTY_NEWLINE);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(debug_nhrp, debug_nhrp_cmd,
+       "debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+       "Enable debug messages for specific or all parts.\n"
+       "NHRP information\n"
+       NHRP_DEBUG_FLAGS_STR)
+{
+       return toggle_flag(vty, debug_flags_desc, argv[0], 1, &debug_flags);
+}
+
+DEFUN(no_debug_nhrp, no_debug_nhrp_cmd,
+       "no debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+       NO_STR
+       "Disable debug messages for specific or all parts.\n"
+       "NHRP information\n"
+       NHRP_DEBUG_FLAGS_STR)
+{
+       return toggle_flag(vty, debug_flags_desc, argv[0], 0, &debug_flags);
+}
+
+#endif /* NO_DEBUG */
+
+static int nhrp_config_write(struct vty *vty)
+{
+#ifndef NO_DEBUG
+       if (debug_flags == NHRP_DEBUG_ALL) {
+               vty_out(vty, "debug nhrp all%s", VTY_NEWLINE);
+       } else {
+               int i;
+
+               for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+                       if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+                               continue;
+                       if (!(debug_flags & debug_flags_desc[i].key))
+                               continue;
+                       vty_out(vty, "debug nhrp %s%s", debug_flags_desc[i].str, VTY_NEWLINE);
+               }
+       }
+       vty_out(vty, "!%s", VTY_NEWLINE);
+#endif /* NO_DEBUG */
+
+       if (nhrp_event_socket_path) {
+               vty_out(vty, "nhrp event socket %s%s",
+                       nhrp_event_socket_path, VTY_NEWLINE);
+       }
+       if (netlink_nflog_group) {
+               vty_out(vty, "nhrp nflog-group %d%s",
+                       netlink_nflog_group, VTY_NEWLINE);
+       }
+
+       return 0;
+}
+
+#define IP_STR         "IP information\n"
+#define IPV6_STR       "IPv6 information\n"
+#define AFI_CMD                "(ip|ipv6)"
+#define AFI_STR                IP_STR IPV6_STR
+#define NHRP_STR       "Next Hop Resolution Protocol functions\n"
+
+static afi_t cmd_to_afi(const char *cmd)
+{
+       return strncmp(cmd, "ipv6", 4) == 0 ? AFI_IP6 : AFI_IP;
+}
+
+static const char *afi_to_cmd(afi_t afi)
+{
+       if (afi == AFI_IP6) return "ipv6";
+       return "ip";
+}
+
+DEFUN(nhrp_event_socket, nhrp_event_socket_cmd,
+       "nhrp event socket SOCKET",
+       NHRP_STR
+       "Event Manager commands\n"
+       "Event Manager unix socket path\n"
+       "Unix path for the socket\n")
+{
+       evmgr_set_socket(argv[0]);
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_event_socket, no_nhrp_event_socket_cmd,
+       "no nhrp event socket [SOCKET]",
+       NO_STR
+       NHRP_STR
+       "Event Manager commands\n"
+       "Event Manager unix socket path\n"
+       "Unix path for the socket\n")
+{
+       evmgr_set_socket(NULL);
+       return CMD_SUCCESS;
+}
+
+DEFUN(nhrp_nflog_group, nhrp_nflog_group_cmd,
+       "nhrp nflog-group <1-65535>",
+       NHRP_STR
+       "Specify NFLOG group number\n"
+       "NFLOG group number\n")
+{
+       uint32_t nfgroup;
+
+       VTY_GET_INTEGER_RANGE("nflog-group", nfgroup, argv[0], 1, 65535);
+       netlink_set_nflog_group(nfgroup);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_nflog_group, no_nhrp_nflog_group_cmd,
+       "no nhrp nflog-group [<1-65535>]",
+       NO_STR
+       NHRP_STR
+       "Specify NFLOG group number\n"
+       "NFLOG group number\n")
+{
+       netlink_set_nflog_group(0);
+       return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_protection, tunnel_protection_cmd,
+       "tunnel protection vici profile PROFILE {fallback-profile FALLBACK}",
+       "NHRP/GRE integration\n"
+       "IPsec protection\n"
+       "VICI (StrongSwan)\n"
+       "IPsec profile\n"
+       "IPsec profile name\n"
+       "Fallback IPsec profile\n"
+       "Fallback IPsec profile name\n")
+{
+       struct interface *ifp = vty->index;
+
+       nhrp_interface_set_protection(ifp, argv[0], argv[1]);
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_protection, no_tunnel_protection_cmd,
+       "no tunnel protection",
+       NO_STR
+       "NHRP/GRE integration\n"
+       "IPsec protection\n")
+{
+       struct interface *ifp = vty->index;
+
+       nhrp_interface_set_protection(ifp, NULL, NULL);
+       return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_source, tunnel_source_cmd,
+       "tunnel source INTERFACE",
+       "NHRP/GRE integration\n"
+       "Tunnel device binding tracking\n"
+       "Interface name\n")
+{
+       struct interface *ifp = vty->index;
+       nhrp_interface_set_source(ifp, argv[0]);
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_source, no_tunnel_source_cmd,
+       "no tunnel source",
+       "NHRP/GRE integration\n"
+       "Tunnel device binding tracking\n"
+       "Interface name\n")
+{
+       struct interface *ifp = vty->index;
+       nhrp_interface_set_source(ifp, NULL);
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_network_id, if_nhrp_network_id_cmd,
+       AFI_CMD " nhrp network-id <1-4294967295>",
+       AFI_STR
+       NHRP_STR
+       "Enable NHRP and specify network-id\n"
+       "System local ID to specify interface group\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       VTY_GET_INTEGER_RANGE("network-id", nifp->afi[afi].network_id, argv[1], 1, 4294967295);
+       nhrp_interface_update(ifp);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_network_id, if_no_nhrp_network_id_cmd,
+       "no " AFI_CMD " nhrp network-id [<1-4294967295>]",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Enable NHRP and specify network-id\n"
+       "System local ID to specify interface group\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       nifp->afi[afi].network_id = 0;
+       nhrp_interface_update(ifp);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_flags, if_nhrp_flags_cmd,
+       AFI_CMD " nhrp (shortcut|redirect)",
+       AFI_STR
+       NHRP_STR
+       "Allow shortcut establishment\n"
+       "Send redirect notifications\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       return toggle_flag(vty, interface_flags_desc, argv[1], 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_flags, if_no_nhrp_flags_cmd,
+       "no " AFI_CMD " nhrp (shortcut|redirect)",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Allow shortcut establishment\n"
+       "Send redirect notifications\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       return toggle_flag(vty, interface_flags_desc, argv[1], 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_reg_flags, if_nhrp_reg_flags_cmd,
+       AFI_CMD " nhrp registration (no-unique)",
+       AFI_STR
+       NHRP_STR
+       "Registration configuration\n"
+       "Don't set unique flag\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+       char name[256];
+       snprintf(name, sizeof(name), "registration %s", argv[1]);
+       return toggle_flag(vty, interface_flags_desc, name, 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_reg_flags, if_no_nhrp_reg_flags_cmd,
+       "no " AFI_CMD " nhrp registration (no-unique)",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Registration configuration\n"
+       "Don't set unique flag\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+       char name[256];
+       snprintf(name, sizeof(name), "registration %s", argv[1]);
+       return toggle_flag(vty, interface_flags_desc, name, 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_holdtime, if_nhrp_holdtime_cmd,
+       AFI_CMD " nhrp holdtime <1-65000>",
+       AFI_STR
+       NHRP_STR
+       "Specify NBMA address validity time\n"
+       "Time in seconds that NBMA addresses are advertised valid\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       VTY_GET_INTEGER_RANGE("holdtime", nifp->afi[afi].holdtime, argv[1], 1, 65000);
+       nhrp_interface_update(ifp);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd,
+       "no " AFI_CMD " nhrp holdtime [1-65000]",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Specify NBMA address validity time\n"
+       "Time in seconds that NBMA addresses are advertised valid\n")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+       afi_t afi = cmd_to_afi(argv[0]);
+
+       nifp->afi[afi].holdtime = NHRPD_DEFAULT_HOLDTIME;
+       nhrp_interface_update(ifp);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd,
+       "ip nhrp mtu (<576-1500>|opennhrp)",
+       IP_STR
+       NHRP_STR
+       "Configure NHRP advertised MTU\n"
+       "MTU value\n"
+       "Advertise bound interface MTU similar to OpenNHRP")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+
+       if (argv[0][0] == 'o') {
+               nifp->afi[AFI_IP].configured_mtu = -1;
+       } else {
+               VTY_GET_INTEGER_RANGE("mtu", nifp->afi[AFI_IP].configured_mtu, argv[0], 576, 1500);
+       }
+       nhrp_interface_update_mtu(ifp, AFI_IP);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_mtu, if_no_nhrp_mtu_cmd,
+       "no ip nhrp mtu [(<576-1500>|opennhrp)]",
+       NO_STR
+       IP_STR
+       NHRP_STR
+       "Configure NHRP advertised MTU\n"
+       "MTU value\n"
+       "Advertise bound interface MTU similar to OpenNHRP")
+{
+       struct interface *ifp = vty->index;
+       struct nhrp_interface *nifp = ifp->info;
+
+       nifp->afi[AFI_IP].configured_mtu = 0;
+       nhrp_interface_update_mtu(ifp, AFI_IP);
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_map, if_nhrp_map_cmd,
+       AFI_CMD " nhrp map (A.B.C.D|X:X::X:X) (A.B.C.D|local)",
+       AFI_STR
+       NHRP_STR
+       "Nexthop Server configuration\n"
+       "IPv4 protocol address\n"
+       "IPv6 protocol address\n"
+       "IPv4 NBMA address\n"
+       "Handle protocol address locally\n")
+{
+       struct interface *ifp = vty->index;
+       afi_t afi = cmd_to_afi(argv[0]);
+       union sockunion proto_addr, nbma_addr;
+       struct nhrp_cache *c;
+
+       if (str2sockunion(argv[1], &proto_addr) < 0 ||
+           afi2family(afi) != sockunion_family(&proto_addr))
+               return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
+       c = nhrp_cache_get(ifp, &proto_addr, 1);
+       if (!c)
+               return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+
+       c->map = 1;
+       if (strcmp(argv[2], "local") == 0) {
+               nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
+       } else{
+               if (str2sockunion(argv[2], &nbma_addr) < 0)
+                       return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+               nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
+                       nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd,
+       "no " AFI_CMD " nhrp map (A.B.C.D|X:X::X:X)",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Nexthop Server configuration\n"
+       "IPv4 protocol address\n"
+       "IPv6 protocol address\n")
+{
+       struct interface *ifp = vty->index;
+       afi_t afi = cmd_to_afi(argv[0]);
+       union sockunion proto_addr;
+       struct nhrp_cache *c;
+
+       if (str2sockunion(argv[1], &proto_addr) < 0 ||
+           afi2family(afi) != sockunion_family(&proto_addr))
+               return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
+       c = nhrp_cache_get(ifp, &proto_addr, 0);
+       if (!c || !c->map)
+               return nhrp_vty_return(vty, NHRP_ERR_ENTRY_NOT_FOUND);
+
+       nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+       return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
+       AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)",
+       AFI_STR
+       NHRP_STR
+       "Nexthop Server configuration\n"
+       "IPv4 protocol address\n"
+       "IPv6 protocol address\n"
+       "Automatic detection of protocol address\n"
+       "IPv4 NBMA address\n"
+       "Fully qualified domain name for NBMA address(es)\n")
+{
+       struct interface *ifp = vty->index;
+       afi_t afi = cmd_to_afi(argv[0]);
+       union sockunion proto_addr;
+       int ret;
+
+       if (str2sockunion(argv[1], &proto_addr) < 0)
+               sockunion_family(&proto_addr) = AF_UNSPEC;
+
+       ret = nhrp_nhs_add(ifp, afi, &proto_addr, argv[2]);
+       return nhrp_vty_return(vty, ret);
+}
+
+DEFUN(if_no_nhrp_nhs, if_no_nhrp_nhs_cmd,
+       "no " AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)",
+       NO_STR
+       AFI_STR
+       NHRP_STR
+       "Nexthop Server configuration\n"
+       "IPv4 protocol address\n"
+       "IPv6 protocol address\n"
+       "Automatic detection of protocol address\n"
+       "IPv4 NBMA address\n"
+       "Fully qualified domain name for NBMA address(es)\n")
+{
+       struct interface *ifp = vty->index;
+       afi_t afi = cmd_to_afi(argv[0]);
+       union sockunion proto_addr;
+       int ret;
+
+       if (str2sockunion(argv[1], &proto_addr) < 0)
+               sockunion_family(&proto_addr) = AF_UNSPEC;
+
+       ret = nhrp_nhs_del(ifp, afi, &proto_addr, argv[2]);
+       return nhrp_vty_return(vty, ret);
+}
+
+struct info_ctx {
+       struct vty *vty;
+       afi_t afi;
+       int count;
+};
+
+static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+       struct info_ctx *ctx = pctx;
+       struct vty *vty = ctx->vty;
+       char buf[2][SU_ADDRSTRLEN];
+
+       if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+               return;
+
+       if (!ctx->count) {
+               vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s%s",
+                       "Iface",
+                       "Type",
+                       "Protocol",
+                       "NBMA",
+                       "Flags",
+                       "Identity",
+                       VTY_NEWLINE);
+       }
+       ctx->count++;
+
+       vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c    %s%s",
+               c->ifp->name,
+               nhrp_cache_type_str[c->cur.type],
+               sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+               c->cur.peer ? sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]) : "-",
+               c->used ? 'U' : ' ',
+               c->t_timeout ? 'T' : ' ',
+               c->t_auth ? 'A' : ' ',
+               c->cur.peer ? c->cur.peer->vc->remote.id : "-",
+               VTY_NEWLINE);
+}
+
+static void show_ip_nhrp_nhs(struct nhrp_nhs *n, struct nhrp_registration *reg, void *pctx)
+{
+       struct info_ctx *ctx = pctx;
+       struct vty *vty = ctx->vty;
+       char buf[2][SU_ADDRSTRLEN];
+
+       if (!ctx->count) {
+               vty_out(vty, "%-8s %-24s %-16s %-16s%s",
+                       "Iface",
+                       "FQDN",
+                       "NBMA",
+                       "Protocol",
+                       VTY_NEWLINE);
+       }
+       ctx->count++;
+
+       vty_out(vty, "%-8s %-24s %-16s %-16s%s",
+               n->ifp->name,
+               n->nbma_fqdn,
+               (reg && reg->peer) ? sockunion2str(&reg->peer->vc->remote.nbma, buf[0], sizeof buf[0]) : "-",
+               sockunion2str(reg ? &reg->proto_addr : &n->proto_addr, buf[1], sizeof buf[1]),
+               VTY_NEWLINE);
+}
+
+static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx)
+{
+       struct info_ctx *ctx = pctx;
+       struct nhrp_cache *c;
+       struct vty *vty = ctx->vty;
+       char buf1[PREFIX_STRLEN], buf2[SU_ADDRSTRLEN];
+
+       if (!ctx->count) {
+               vty_out(vty, "%-8s %-24s %-24s %s%s",
+                       "Type",
+                       "Prefix",
+                       "Via",
+                       "Identity",
+                       VTY_NEWLINE);
+       }
+       ctx->count++;
+
+       c = s->cache;
+       vty_out(ctx->vty, "%-8s %-24s %-24s %s%s",
+               nhrp_cache_type_str[s->type],
+               prefix2str(s->p, buf1, sizeof buf1),
+               c ? sockunion2str(&c->remote_addr, buf2, sizeof buf2) : "",
+               (c && c->cur.peer) ? c->cur.peer->vc->remote.id : "",
+               VTY_NEWLINE);
+}
+
+static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+       struct info_ctx *ctx = pctx;
+       struct vty *vty = ctx->vty;
+       char buf[SU_ADDRSTRLEN];
+
+       if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+               return;
+
+       vty_out(ctx->vty,
+               "Type: %s%s"
+               "Flags:%s%s%s"
+               "Protocol-Address: %s/%zu%s",
+               nhrp_cache_type_str[c->cur.type],
+               VTY_NEWLINE,
+               (c->cur.peer && c->cur.peer->online) ? " up": "",
+               c->used ? " used": "",
+               VTY_NEWLINE,
+               sockunion2str(&c->remote_addr, buf, sizeof buf),
+               8 * family2addrsize(sockunion_family(&c->remote_addr)),
+               VTY_NEWLINE);
+
+       if (c->cur.peer) {
+               vty_out(ctx->vty,
+                       "NBMA-Address: %s%s",
+                       sockunion2str(&c->cur.peer->vc->remote.nbma, buf, sizeof buf),
+                       VTY_NEWLINE);
+       }
+
+       if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
+               vty_out(ctx->vty,
+                       "NBMA-NAT-OA-Address: %s%s",
+                       sockunion2str(&c->cur.remote_nbma_natoa, buf, sizeof buf),
+                       VTY_NEWLINE);
+       }
+
+       vty_out(ctx->vty, "%s", VTY_NEWLINE);
+}
+
+DEFUN(show_ip_nhrp, show_ip_nhrp_cmd,
+       "show " AFI_CMD " nhrp (cache|nhs|shortcut|opennhrp|)",
+       SHOW_STR
+       AFI_STR
+       "NHRP information\n"
+       "Forwarding cache information\n"
+       "Next hop server information\n"
+       "Shortcut information\n"
+       "opennhrpctl style cache dump\n")
+{
+       struct listnode *node;
+       struct interface *ifp;
+       struct info_ctx ctx = {
+               .vty = vty,
+               .afi = cmd_to_afi(argv[0]),
+       };
+
+       if (!argv[1] || argv[1][0] == 'c') {
+               for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+                       nhrp_cache_foreach(ifp, show_ip_nhrp_cache, &ctx);
+       } else if (argv[1][0] == 'n') {
+               for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+                       nhrp_nhs_foreach(ifp, ctx.afi, show_ip_nhrp_nhs, &ctx);
+       } else if (argv[1][0] == 's') {
+               nhrp_shortcut_foreach(ctx.afi, show_ip_nhrp_shortcut, &ctx);
+       } else {
+               vty_out(vty, "Status: ok%s%s", VTY_NEWLINE, VTY_NEWLINE);
+               ctx.count++;
+               for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+                       nhrp_cache_foreach(ifp, show_ip_opennhrp_cache, &ctx);
+       }
+
+       if (!ctx.count) {
+               vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+static void show_dmvpn_entry(struct nhrp_vc *vc, void *ctx)
+{
+       struct vty *vty = ctx;
+       char buf[2][SU_ADDRSTRLEN];
+
+       vty_out(vty, "%-24s %-24s %c      %-4d %-24s%s",
+               sockunion2str(&vc->local.nbma, buf[0], sizeof buf[0]),
+               sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]),
+               notifier_active(&vc->notifier_list) ? 'n' : ' ',
+               vc->ipsec,
+               vc->remote.id,
+               VTY_NEWLINE);
+}
+
+DEFUN(show_dmvpn, show_dmvpn_cmd,
+       "show dmvpn",
+       SHOW_STR
+       "DMVPN information\n")
+{
+       vty_out(vty, "%-24s %-24s %-6s %-4s %-24s%s",
+               "Src",
+               "Dst",
+               "Flags",
+               "SAs",
+               "Identity",
+               VTY_NEWLINE);
+
+       nhrp_vc_foreach(show_dmvpn_entry, vty);
+
+       return CMD_SUCCESS;
+}
+
+static void clear_nhrp_cache(struct nhrp_cache *c, void *data)
+{
+       struct info_ctx *ctx = data;
+       if (c->cur.type <= NHRP_CACHE_CACHED) {
+               nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+               ctx->count++;
+       }
+}
+
+static void clear_nhrp_shortcut(struct nhrp_shortcut *s, void *data)
+{
+       struct info_ctx *ctx = data;
+       nhrp_shortcut_purge(s, 1);
+       ctx->count++;
+}
+
+DEFUN(clear_nhrp, clear_nhrp_cmd,
+       "clear " AFI_CMD " nhrp (cache|shortcut)",
+       CLEAR_STR
+       AFI_STR
+       NHRP_STR
+       "Dynamic cache entries\n"
+       "Shortcut entries\n")
+{
+       struct listnode *node;
+       struct interface *ifp;
+       struct info_ctx ctx = {
+               .vty = vty,
+               .afi = cmd_to_afi(argv[0]),
+               .count = 0,
+       };
+
+       if (!argv[1] || argv[1][0] == 'c') {
+               for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+                       nhrp_cache_foreach(ifp, clear_nhrp_cache, &ctx);
+       } else {
+               nhrp_shortcut_foreach(ctx.afi, clear_nhrp_shortcut, &ctx);
+       }
+
+       if (!ctx.count) {
+               vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       vty_out(vty, "%% %d entries cleared%s", ctx.count, VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+struct write_map_ctx {
+       struct vty *vty;
+       int family;
+       const char *aficmd;
+};
+
+static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data)
+{
+       struct write_map_ctx *ctx = data;
+       struct vty *vty = ctx->vty;
+       char buf[2][SU_ADDRSTRLEN];
+
+       if (!c->map) return;
+       if (sockunion_family(&c->remote_addr) != ctx->family) return;
+
+       vty_out(vty, " %s nhrp map %s %s%s",
+               ctx->aficmd,
+               sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+               c->cur.type == NHRP_CACHE_LOCAL ? "local" :
+               sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]),
+               VTY_NEWLINE);
+}
+
+static int interface_config_write(struct vty *vty)
+{
+       struct write_map_ctx mapctx;
+       struct listnode *node;
+       struct interface *ifp;
+       struct nhrp_interface *nifp;
+       struct nhrp_nhs *nhs;
+       const char *aficmd;
+       afi_t afi;
+       char buf[SU_ADDRSTRLEN];
+       int i;
+
+       for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+               vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+               if (ifp->desc)
+                       vty_out(vty, " description %s%s", ifp->desc, VTY_NEWLINE);
+
+               nifp = ifp->info;
+               if (nifp->ipsec_profile) {
+                       vty_out(vty, " tunnel protection vici profile %s",
+                               nifp->ipsec_profile);
+                       if (nifp->ipsec_fallback_profile)
+                               vty_out(vty, " fallback-profile %s",
+                                       nifp->ipsec_fallback_profile);
+                       vty_out(vty, "%s", VTY_NEWLINE);
+               }
+               if (nifp->source)
+                       vty_out(vty, " tunnel source %s%s",
+                               nifp->source, VTY_NEWLINE);
+
+               for (afi = 0; afi < AFI_MAX; afi++) {
+                       struct nhrp_afi_data *ad = &nifp->afi[afi];
+
+                       aficmd = afi_to_cmd(afi);
+
+                       if (ad->network_id)
+                               vty_out(vty, " %s nhrp network-id %u%s",
+                                       aficmd, ad->network_id,
+                                       VTY_NEWLINE);
+
+                       if (ad->holdtime != NHRPD_DEFAULT_HOLDTIME)
+                               vty_out(vty, " %s nhrp holdtime %u%s",
+                                       aficmd, ad->holdtime,
+                                       VTY_NEWLINE);
+
+                       if (ad->configured_mtu < 0)
+                               vty_out(vty, " %s nhrp mtu opennhrp%s",
+                                       aficmd, VTY_NEWLINE);
+                       else if (ad->configured_mtu)
+                               vty_out(vty, " %s nhrp mtu %u%s",
+                                       aficmd, ad->configured_mtu,
+                                       VTY_NEWLINE);
+
+                       for (i = 0; interface_flags_desc[i].str != NULL; i++) {
+                               if (!(ad->flags & interface_flags_desc[i].key))
+                                       continue;
+                               vty_out(vty, " %s nhrp %s%s",
+                                       aficmd, interface_flags_desc[i].str, VTY_NEWLINE);
+                       }
+
+                       mapctx = (struct write_map_ctx) {
+                               .vty = vty,
+                               .family = afi2family(afi),
+                               .aficmd = aficmd,
+                       };
+                       nhrp_cache_foreach(ifp, interface_config_write_nhrp_map, &mapctx);
+
+                       list_for_each_entry(nhs, &ad->nhslist_head, nhslist_entry) {
+                               vty_out(vty, " %s nhrp nhs %s nbma %s%s",
+                                       aficmd,
+                                       sockunion_family(&nhs->proto_addr) == AF_UNSPEC ? "dynamic" : sockunion2str(&nhs->proto_addr, buf, sizeof buf),
+                                       nhs->nbma_fqdn,
+                                       VTY_NEWLINE);
+                       }
+               }
+
+               vty_out (vty, "!%s", VTY_NEWLINE);
+       }
+
+       return 0;
+}
+
+void nhrp_config_init(void)
+{
+       install_node(&zebra_node, nhrp_config_write);
+       install_default(ZEBRA_NODE);
+
+       /* global commands */
+       install_element(VIEW_NODE, &show_debugging_nhrp_cmd);
+       install_element(VIEW_NODE, &show_ip_nhrp_cmd);
+       install_element(VIEW_NODE, &show_dmvpn_cmd);
+       install_element(ENABLE_NODE, &show_debugging_nhrp_cmd);
+       install_element(ENABLE_NODE, &show_ip_nhrp_cmd);
+       install_element(ENABLE_NODE, &show_dmvpn_cmd);
+       install_element(ENABLE_NODE, &clear_nhrp_cmd);
+
+       install_element(ENABLE_NODE, &debug_nhrp_cmd);
+       install_element(ENABLE_NODE, &no_debug_nhrp_cmd);
+
+       install_element(CONFIG_NODE, &debug_nhrp_cmd);
+       install_element(CONFIG_NODE, &no_debug_nhrp_cmd);
+
+       install_element(CONFIG_NODE, &nhrp_event_socket_cmd);
+       install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd);
+       install_element(CONFIG_NODE, &nhrp_nflog_group_cmd);
+       install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd);
+
+       /* interface specific commands */
+       install_node(&nhrp_interface_node, interface_config_write);
+       install_default(INTERFACE_NODE);
+
+       install_element(CONFIG_NODE, &interface_cmd);
+       install_element(CONFIG_NODE, &no_interface_cmd);
+       install_element(INTERFACE_NODE, &interface_cmd);
+       install_element(INTERFACE_NODE, &no_interface_cmd);
+       install_element(INTERFACE_NODE, &tunnel_protection_cmd);
+       install_element(INTERFACE_NODE, &no_tunnel_protection_cmd);
+       install_element(INTERFACE_NODE, &tunnel_source_cmd);
+       install_element(INTERFACE_NODE, &no_tunnel_source_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_network_id_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_flags_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_flags_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_reg_flags_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd);
+       install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
+       install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
+}
diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
new file mode 100644 (file)
index 0000000..9222ad4
--- /dev/null
@@ -0,0 +1,413 @@
+/* NHRP daemon internal structures and function prototypes
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef NHRPD_H
+#define NHRPD_H
+
+#include "list.h"
+
+#include "zbuf.h"
+#include "zclient.h"
+#include "debug.h"
+
+#define NHRPD_DEFAULT_HOLDTIME 7200
+
+#define NHRP_VTY_PORT          2612
+#define NHRP_DEFAULT_CONFIG    "nhrpd.conf"
+
+extern struct thread_master *master;
+
+enum {
+       NHRP_OK = 0,
+       NHRP_ERR_FAIL,
+       NHRP_ERR_NO_MEMORY,
+       NHRP_ERR_UNSUPPORTED_INTERFACE,
+       NHRP_ERR_NHRP_NOT_ENABLED,
+       NHRP_ERR_ENTRY_EXISTS,
+       NHRP_ERR_ENTRY_NOT_FOUND,
+       NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH,
+};
+
+struct notifier_block;
+
+typedef void (*notifier_fn_t)(struct notifier_block *, unsigned long);
+
+struct notifier_block {
+       struct list_head notifier_entry;
+       notifier_fn_t action;
+};
+
+struct notifier_list {
+       struct list_head notifier_head;
+};
+
+#define NOTIFIER_LIST_INITIALIZER(l) \
+       { .notifier_head = LIST_INITIALIZER((l)->notifier_head) }
+
+static inline void notifier_init(struct notifier_list *l)
+{
+       list_init(&l->notifier_head);
+}
+
+static inline void notifier_add(struct notifier_block *n, struct notifier_list *l, notifier_fn_t action)
+{
+       n->action = action;
+       list_add_tail(&n->notifier_entry, &l->notifier_head);
+}
+
+static inline void notifier_del(struct notifier_block *n)
+{
+       list_del(&n->notifier_entry);
+}
+
+static inline void notifier_call(struct notifier_list *l, int cmd)
+{
+       struct notifier_block *n, *nn;
+       list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry)
+               n->action(n, cmd);
+}
+
+static inline int notifier_active(struct notifier_list *l)
+{
+       return !list_empty(&l->notifier_head);
+}
+
+struct resolver_query {
+       void (*callback)(struct resolver_query *, int n, union sockunion *);
+};
+
+void resolver_init(void);
+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*cb)(struct resolver_query *, int, union sockunion *));
+
+void nhrp_zebra_init(void);
+void nhrp_zebra_terminate(void);
+
+struct zbuf;
+struct nhrp_vc;
+struct nhrp_cache;
+struct nhrp_nhs;
+struct nhrp_interface;
+
+#define MAX_ID_LENGTH                  64
+#define MAX_CERT_LENGTH                        2048
+
+enum nhrp_notify_type {
+       NOTIFY_INTERFACE_UP,
+       NOTIFY_INTERFACE_DOWN,
+       NOTIFY_INTERFACE_CHANGED,
+       NOTIFY_INTERFACE_ADDRESS_CHANGED,
+       NOTIFY_INTERFACE_NBMA_CHANGED,
+       NOTIFY_INTERFACE_MTU_CHANGED,
+
+       NOTIFY_VC_IPSEC_CHANGED,
+       NOTIFY_VC_IPSEC_UPDATE_NBMA,
+
+       NOTIFY_PEER_UP,
+       NOTIFY_PEER_DOWN,
+       NOTIFY_PEER_IFCONFIG_CHANGED,
+       NOTIFY_PEER_MTU_CHANGED,
+       NOTIFY_PEER_NBMA_CHANGING,
+
+       NOTIFY_CACHE_UP,
+       NOTIFY_CACHE_DOWN,
+       NOTIFY_CACHE_DELETE,
+       NOTIFY_CACHE_USED,
+       NOTIFY_CACHE_BINDING_CHANGE,
+};
+
+struct nhrp_vc {
+       struct notifier_list notifier_list;
+       uint8_t ipsec;
+       uint8_t updating;
+       uint8_t abort_migration;
+
+       struct nhrp_vc_peer {
+               union sockunion nbma;
+               char id[MAX_ID_LENGTH];
+               uint16_t certlen;
+               uint8_t cert[MAX_CERT_LENGTH];
+       } local, remote;
+};
+
+enum nhrp_route_type {
+       NHRP_ROUTE_BLACKHOLE,
+       NHRP_ROUTE_LOCAL,
+       NHRP_ROUTE_NBMA_NEXTHOP,
+       NHRP_ROUTE_OFF_NBMA,
+};
+
+struct nhrp_peer {
+       unsigned int ref;
+       unsigned online : 1;
+       unsigned requested : 1;
+       unsigned fallback_requested : 1;
+       unsigned prio : 1;
+       struct notifier_list notifier_list;
+       struct interface *ifp;
+       struct nhrp_vc *vc;
+       struct thread *t_fallback;
+       struct notifier_block vc_notifier, ifp_notifier;
+};
+
+struct nhrp_packet_parser {
+       struct interface *ifp;
+       struct nhrp_afi_data *if_ad;
+       struct nhrp_peer *peer;
+       struct zbuf *pkt;
+       struct zbuf payload;
+       struct zbuf extensions;
+       struct nhrp_packet_header *hdr;
+       enum nhrp_route_type route_type;
+       struct prefix route_prefix;
+       union sockunion src_nbma, src_proto, dst_proto;
+};
+
+struct nhrp_reqid_pool {
+       struct hash *reqid_hash;
+       uint32_t next_request_id;
+};
+
+struct nhrp_reqid {
+       uint32_t request_id;
+       void (*cb)(struct nhrp_reqid *, void *);
+};
+
+extern struct nhrp_reqid_pool nhrp_packet_reqid;
+extern struct nhrp_reqid_pool nhrp_event_reqid;
+
+enum nhrp_cache_type {
+       NHRP_CACHE_INVALID = 0,
+       NHRP_CACHE_INCOMPLETE,
+       NHRP_CACHE_NEGATIVE,
+       NHRP_CACHE_CACHED,
+       NHRP_CACHE_DYNAMIC,
+       NHRP_CACHE_NHS,
+       NHRP_CACHE_STATIC,
+       NHRP_CACHE_LOCAL,
+       NHRP_CACHE_NUM_TYPES
+};
+
+extern const char * const nhrp_cache_type_str[];
+extern unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
+
+struct nhrp_cache {
+       struct interface *ifp;
+       union sockunion remote_addr;
+
+       unsigned map : 1;
+       unsigned used : 1;
+       unsigned route_installed : 1;
+       unsigned nhrp_route_installed : 1;
+
+       struct notifier_block peer_notifier;
+       struct notifier_block newpeer_notifier;
+       struct notifier_list notifier_list;
+       struct nhrp_reqid eventid;
+       struct thread *t_timeout;
+       struct thread *t_auth;
+
+       struct {
+               enum nhrp_cache_type type;
+               union sockunion remote_nbma_natoa;
+               struct nhrp_peer *peer;
+               time_t expires;
+               uint32_t mtu;
+       } cur, new;
+};
+
+struct nhrp_shortcut {
+       struct prefix *p;
+       union sockunion addr;
+
+       struct nhrp_reqid reqid;
+       struct thread *t_timer;
+
+       enum nhrp_cache_type type;
+       unsigned int holding_time;
+       unsigned route_installed : 1;
+       unsigned expiring : 1;
+
+       struct nhrp_cache *cache;
+       struct notifier_block cache_notifier;
+};
+
+struct nhrp_nhs {
+       struct interface *ifp;
+       struct list_head nhslist_entry;
+
+       unsigned hub : 1;
+       afi_t afi;
+       union sockunion proto_addr;
+       const char *nbma_fqdn;                  /* IP-address or FQDN */
+
+       struct thread *t_resolve;
+       struct resolver_query dns_resolve;
+       struct list_head reglist_head;
+};
+
+struct nhrp_registration {
+       struct list_head reglist_entry;
+       struct thread *t_register;
+       struct nhrp_nhs *nhs;
+       struct nhrp_reqid reqid;
+       unsigned int timeout;
+       unsigned mark : 1;
+       union sockunion proto_addr;
+       struct nhrp_peer *peer;
+       struct notifier_block peer_notifier;
+};
+
+#define NHRP_IFF_SHORTCUT              0x0001
+#define NHRP_IFF_REDIRECT              0x0002
+#define NHRP_IFF_REG_NO_UNIQUE         0x0100
+
+struct nhrp_interface {
+       struct interface *ifp;
+
+       unsigned enabled : 1;
+
+       char *ipsec_profile, *ipsec_fallback_profile, *source;
+       union sockunion nbma;
+       union sockunion nat_nbma;
+       unsigned int linkidx;
+       uint32_t grekey;
+
+       struct hash *peer_hash;
+       struct hash *cache_hash;
+
+       struct notifier_list notifier_list;
+
+       struct interface *nbmaifp;
+       struct notifier_block nbmanifp_notifier;
+
+       struct nhrp_afi_data {
+               unsigned flags;
+               unsigned short configured : 1;
+               union sockunion addr;
+               uint32_t network_id;
+               short configured_mtu;
+               unsigned short mtu;
+               unsigned int holdtime;
+               struct list_head nhslist_head;
+       } afi[AFI_MAX];
+};
+
+int sock_open_unix(const char *path);
+
+void nhrp_interface_init(void);
+void nhrp_interface_update(struct interface *ifp);
+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi);
+
+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_address_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_interface_address_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
+
+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn);
+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n);
+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile);
+void nhrp_interface_set_source(struct interface *ifp, const char *ifname);
+
+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
+int nhrp_nhs_free(struct nhrp_nhs *nhs);
+void nhrp_nhs_terminate(void);
+void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, void (*cb)(struct nhrp_nhs *, struct nhrp_registration *, void *), void *ctx);
+
+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu);
+int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id);
+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp);
+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer);
+
+void nhrp_config_init(void);
+
+void nhrp_shortcut_init(void);
+void nhrp_shortcut_terminate(void);
+void nhrp_shortcut_initiate(union sockunion *addr);
+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx);
+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
+
+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create);
+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx);
+void nhrp_cache_set_used(struct nhrp_cache *, int);
+int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_natoa);
+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *, notifier_fn_t);
+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *);
+
+void nhrp_vc_init(void);
+void nhrp_vc_terminate(void);
+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create);
+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc);
+void nhrp_vc_notify_add(struct nhrp_vc *, struct notifier_block *, notifier_fn_t);
+void nhrp_vc_notify_del(struct nhrp_vc *, struct notifier_block *);
+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx);
+void nhrp_vc_reset(void);
+
+void vici_init(void);
+void vici_terminate(void);
+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio);
+
+extern const char *nhrp_event_socket_path;
+
+void evmgr_init(void);
+void evmgr_terminate(void);
+void evmgr_set_socket(const char *socket);
+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *));
+
+struct nhrp_packet_header *nhrp_packet_push(
+       struct zbuf *zb, uint8_t type,
+       const union sockunion *src_nbma,
+       const union sockunion *src_proto,
+       const union sockunion *dst_proto);
+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr);
+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len);
+
+struct nhrp_packet_header *nhrp_packet_pull(
+       struct zbuf *zb,
+       union sockunion *src_nbma,
+       union sockunion *src_proto,
+       union sockunion *dst_proto);
+
+struct nhrp_cie_header *nhrp_cie_push(
+       struct zbuf *zb, uint8_t code,
+       const union sockunion *nbma,
+       const union sockunion *proto);
+struct nhrp_cie_header *nhrp_cie_pull(
+       struct zbuf *zb,
+       struct nhrp_packet_header *hdr,
+       union sockunion *nbma,
+       union sockunion *proto);
+
+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type);
+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext);
+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload);
+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *);
+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload);
+
+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *));
+void nhrp_reqid_free(struct nhrp_reqid_pool *, struct nhrp_reqid *r);
+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid);
+
+int nhrp_packet_init(void);
+
+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma);
+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p);
+void nhrp_peer_unref(struct nhrp_peer *p);
+int nhrp_peer_check(struct nhrp_peer *p, int establish);
+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *, notifier_fn_t);
+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *);
+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb);
+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb);
+void nhrp_peer_send_indication(struct interface *ifp, uint16_t, struct zbuf *);
+
+#endif
diff --git a/nhrpd/os.h b/nhrpd/os.h
new file mode 100644 (file)
index 0000000..0fbe8b0
--- /dev/null
@@ -0,0 +1,5 @@
+
+int os_socket(void);
+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen);
+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen);
+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c
new file mode 100644 (file)
index 0000000..24b3199
--- /dev/null
@@ -0,0 +1,49 @@
+#include "zebra.h"
+#include "hash.h"
+#include "nhrpd.h"
+
+static unsigned int nhrp_reqid_key(void *data)
+{
+       struct nhrp_reqid *r = data;
+       return r->request_id;
+}
+
+static int nhrp_reqid_cmp(const void *data, const void *key)
+{
+       const struct nhrp_reqid *a = data, *b = key;
+       return a->request_id == b->request_id;
+}
+
+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *))
+{
+       if (!p->reqid_hash) {
+               p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp);
+               p->next_request_id = 1;
+       }
+
+       if (r->cb != cb) {
+               r->request_id = p->next_request_id;
+               if (++p->next_request_id == 0) p->next_request_id = 1;
+               r->cb = cb;
+               hash_get(p->reqid_hash, r, hash_alloc_intern);
+       }
+       return r->request_id;
+}
+
+void nhrp_reqid_free(struct nhrp_reqid_pool *p, struct nhrp_reqid *r)
+{
+       if (r->cb) {
+               hash_release(p->reqid_hash, r);
+               r->cb = NULL;
+       }
+}
+
+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *p, uint32_t reqid)
+{
+       struct nhrp_reqid key;
+       if (!p->reqid_hash) return 0;
+       key.request_id = reqid;
+       return hash_lookup(p->reqid_hash, &key);
+}
+
+
diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c
new file mode 100644 (file)
index 0000000..07bdb73
--- /dev/null
@@ -0,0 +1,190 @@
+/* C-Ares integration to Quagga mainloop
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ares.h>
+#include <ares_version.h>
+
+#include "vector.h"
+#include "thread.h"
+#include "nhrpd.h"
+
+struct resolver_state {
+       ares_channel channel;
+       struct thread *timeout;
+       vector read_threads, write_threads;
+};
+
+static struct resolver_state state;
+
+#define THREAD_RUNNING ((struct thread *)-1)
+
+static void resolver_update_timeouts(struct resolver_state *r);
+
+static int resolver_cb_timeout(struct thread *t)
+{
+       struct resolver_state *r = THREAD_ARG(t);
+
+       r->timeout = THREAD_RUNNING;
+       ares_process(r->channel, NULL, NULL);
+       r->timeout = NULL;
+       resolver_update_timeouts(r);
+
+       return 0;
+}
+
+static int resolver_cb_socket_readable(struct thread *t)
+{
+       struct resolver_state *r = THREAD_ARG(t);
+       int fd = THREAD_FD(t);
+
+       vector_set_index(r->read_threads, fd, THREAD_RUNNING);
+       ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
+       if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
+               t = NULL;
+               THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
+               vector_set_index(r->read_threads, fd, t);
+       }
+       resolver_update_timeouts(r);
+
+       return 0;
+}
+
+static int resolver_cb_socket_writable(struct thread *t)
+{
+       struct resolver_state *r = THREAD_ARG(t);
+       int fd = THREAD_FD(t);
+
+       vector_set_index(r->write_threads, fd, THREAD_RUNNING);
+       ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
+       if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
+               t = NULL;
+               THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd);
+               vector_set_index(r->write_threads, fd, t);
+       }
+       resolver_update_timeouts(r);
+
+       return 0;
+}
+
+static void resolver_update_timeouts(struct resolver_state *r)
+{
+       struct timeval *tv, tvbuf;
+
+       if (r->timeout == THREAD_RUNNING) return;
+
+       THREAD_OFF(r->timeout);
+       tv = ares_timeout(r->channel, NULL, &tvbuf);
+       if (tv) {
+               unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+               THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms);
+       }
+}
+
+static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable)
+{
+       struct resolver_state *r = (struct resolver_state *) data;
+       struct thread *t;
+
+       if (readable) {
+               t = vector_lookup_ensure(r->read_threads, fd);
+               if (!t) {
+                       THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
+                       vector_set_index(r->read_threads, fd, t);
+               }
+       } else {
+               t = vector_lookup(r->read_threads, fd);
+               if (t) {
+                       if (t != THREAD_RUNNING) {
+                               THREAD_OFF(t);
+                       }
+                       vector_unset(r->read_threads, fd);
+               }
+       }
+
+       if (writable) {
+               t = vector_lookup_ensure(r->write_threads, fd);
+               if (!t) {
+                       THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd);
+                       vector_set_index(r->write_threads, fd, t);
+               }
+       } else {
+               t = vector_lookup(r->write_threads, fd);
+               if (t) {
+                       if (t != THREAD_RUNNING) {
+                               THREAD_OFF(t);
+                       }
+                       vector_unset(r->write_threads, fd);
+               }
+       }
+}
+
+void resolver_init(void)
+{
+       struct ares_options ares_opts;
+
+       state.read_threads = vector_init(1);
+       state.write_threads = vector_init(1);
+
+       ares_opts = (struct ares_options) {
+               .sock_state_cb = &ares_socket_cb,
+               .sock_state_cb_data = &state,
+               .timeout = 2,
+               .tries = 3,
+       };
+
+       ares_init_options(&state.channel, &ares_opts,
+               ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT |
+               ARES_OPT_TRIES);
+}
+
+
+static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he)
+{
+       struct resolver_query *query = (struct resolver_query *) arg;
+       union sockunion addr[16];
+       size_t i;
+
+       if (status != ARES_SUCCESS) {
+               debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
+               query->callback(query, -1, NULL);
+               query->callback = NULL;
+               return;
+       }
+
+       for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) {
+               memset(&addr[i], 0, sizeof(addr[i]));
+               addr[i].sa.sa_family = he->h_addrtype;
+               switch (he->h_addrtype) {
+               case AF_INET:
+                       memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
+                       break;
+               case AF_INET6:
+                       memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
+                       break;
+               }
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i);
+       query->callback(query, i, &addr[0]);
+       query->callback = NULL;
+}
+
+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *))
+{
+       if (query->callback != NULL) {
+               zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname);
+               return;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
+
+       query->callback = callback;
+       ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
+       resolver_update_timeouts(&state);
+}
diff --git a/nhrpd/vici.c b/nhrpd/vici.c
new file mode 100644 (file)
index 0000000..5491bac
--- /dev/null
@@ -0,0 +1,502 @@
+/* strongSwan VICI protocol implementation for NHRP
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "thread.h"
+#include "zbuf.h"
+#include "log.h"
+#include "nhrpd.h"
+
+#include "vici.h"
+
+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+struct blob {
+       char *ptr;
+       int len;
+};
+
+static int blob_equal(const struct blob *b, const char *str)
+{
+       if (b->len != (int) strlen(str)) return 0;
+       return memcmp(b->ptr, str, b->len) == 0;
+}
+
+static int blob2buf(const struct blob *b, char *buf, size_t n)
+{
+       if (b->len >= (int) n) return 0;
+       memcpy(buf, b->ptr, b->len);
+       buf[b->len] = 0;
+       return 1;
+}
+
+struct vici_conn {
+       struct thread *t_reconnect, *t_read, *t_write;
+       struct zbuf ibuf;
+       struct zbuf_queue obuf;
+       int fd;
+       uint8_t ibuf_data[VICI_MAX_MSGLEN];
+};
+
+struct vici_message_ctx {
+       const char *sections[8];
+       int nsections;
+};
+
+static int vici_reconnect(struct thread *t);
+static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
+
+static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
+{
+       size_t len = strlen(str);
+       zbuf_put8(obuf, len);
+       zbuf_put(obuf, str, len);
+}
+
+static void vici_connection_error(struct vici_conn *vici)
+{
+       nhrp_vc_reset();
+
+       THREAD_OFF(vici->t_read);
+       THREAD_OFF(vici->t_write);
+       zbuf_reset(&vici->ibuf);
+       zbufq_reset(&vici->obuf);
+
+       close(vici->fd);
+       vici->fd = -1;
+       THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
+}
+
+static void vici_parse_message(
+       struct vici_conn *vici, struct zbuf *msg,
+       void (*parser)(struct vici_message_ctx *ctx, enum vici_type_t msgtype, const struct blob *key, const struct blob *val),
+       struct vici_message_ctx *ctx)
+{
+       uint8_t *type;
+       struct blob key;
+       struct blob val;
+
+       while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
+               switch (*type) {
+               case VICI_SECTION_START:
+                       key.len = zbuf_get8(msg);
+                       key.ptr = zbuf_pulln(msg, key.len);
+                       debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'", key.len, key.ptr);
+                       parser(ctx, *type, &key, NULL);
+                       ctx->nsections++;
+                       break;
+               case VICI_SECTION_END:
+                       debugf(NHRP_DEBUG_VICI, "VICI: Section end");
+                       parser(ctx, *type, NULL, NULL);
+                       ctx->nsections--;
+                       break;
+               case VICI_KEY_VALUE:
+                       key.len = zbuf_get8(msg);
+                       key.ptr = zbuf_pulln(msg, key.len);
+                       val.len = zbuf_get_be16(msg);
+                       val.ptr = zbuf_pulln(msg, val.len);
+                       debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'", key.len, key.ptr, val.len, val.ptr);
+                       parser(ctx, *type, &key, &val);
+                       break;
+               case VICI_LIST_START:
+                       key.len = zbuf_get8(msg);
+                       key.ptr = zbuf_pulln(msg, key.len);
+                       debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'", key.len, key.ptr);
+                       break;
+               case VICI_LIST_ITEM:
+                       val.len = zbuf_get_be16(msg);
+                       val.ptr = zbuf_pulln(msg, val.len);
+                       debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'", val.len, val.ptr);
+                       parser(ctx, *type, &key, &val);
+                       break;
+               case VICI_LIST_END:
+                       debugf(NHRP_DEBUG_VICI, "VICI: List end");
+                       break;
+               default:
+                       debugf(NHRP_DEBUG_VICI, "VICI: Unsupported message component type %d", *type);
+                       return;
+               }
+       }
+}
+
+struct handle_sa_ctx {
+       struct vici_message_ctx msgctx;
+       int event;
+       int child_ok;
+       int kill_ikesa;
+       uint32_t child_uniqueid, ike_uniqueid;
+       struct {
+               union sockunion host;
+               struct blob id, cert;
+       } local, remote;
+};
+
+static void parse_sa_message(
+       struct vici_message_ctx *ctx,
+       enum vici_type_t msgtype,
+       const struct blob *key, const struct blob *val)
+{
+       struct handle_sa_ctx *sactx = container_of(ctx, struct handle_sa_ctx, msgctx);
+       struct nhrp_vc *vc;
+       char buf[512];
+
+       switch (msgtype) {
+       case VICI_SECTION_START:
+               if (ctx->nsections == 3) {
+                       /* Begin of child-sa section, reset child vars */
+                       sactx->child_uniqueid = 0;
+                       sactx->child_ok = 0;
+               }
+               break;
+       case VICI_SECTION_END:
+               if (ctx->nsections == 3) {
+                       /* End of child-sa section, update nhrp_vc */
+                       int up = sactx->child_ok || sactx->event == 1;
+                       if (up) {
+                               vc = nhrp_vc_get(&sactx->local.host, &sactx->remote.host, up);
+                               if (vc) {
+                                       blob2buf(&sactx->local.id, vc->local.id, sizeof(vc->local.id));
+                                       if (blob2buf(&sactx->local.cert, (char*)vc->local.cert, sizeof(vc->local.cert)))
+                                               vc->local.certlen = sactx->local.cert.len;
+                                       blob2buf(&sactx->remote.id, vc->remote.id, sizeof(vc->remote.id));
+                                       if (blob2buf(&sactx->remote.cert, (char*)vc->remote.cert, sizeof(vc->remote.cert)))
+                                               vc->remote.certlen = sactx->remote.cert.len;
+                                       sactx->kill_ikesa |= nhrp_vc_ipsec_updown(sactx->child_uniqueid, vc);
+                               }
+                       } else {
+                               nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
+                       }
+               }
+               break;
+       default:
+               switch (key->ptr[0]) {
+               case 'l':
+                       if (blob_equal(key, "local-host") && ctx->nsections == 1) {
+                               if (blob2buf(val, buf, sizeof(buf)))
+                                       str2sockunion(buf, &sactx->local.host);
+                       } else if (blob_equal(key, "local-id") && ctx->nsections == 1) {
+                               sactx->local.id = *val;
+                       } else if (blob_equal(key, "local-cert-data") && ctx->nsections == 1) {
+                               sactx->local.cert = *val;
+                       }
+                       break;
+               case 'r':
+                       if (blob_equal(key, "remote-host") && ctx->nsections == 1) {
+                               if (blob2buf(val, buf, sizeof(buf)))
+                                       str2sockunion(buf, &sactx->remote.host);
+                       } else if (blob_equal(key, "remote-id") && ctx->nsections == 1) {
+                               sactx->remote.id = *val;
+                       } else if (blob_equal(key, "remote-cert-data") && ctx->nsections == 1) {
+                               sactx->remote.cert = *val;
+                       }
+                       break;
+               case 'u':
+                       if (blob_equal(key, "uniqueid") && blob2buf(val, buf, sizeof(buf))) {
+                               if (ctx->nsections == 3)
+                                       sactx->child_uniqueid = strtoul(buf, NULL, 0);
+                               else if (ctx->nsections == 1)
+                                       sactx->ike_uniqueid = strtoul(buf, NULL, 0);
+                       }
+                       break;
+               case 's':
+                       if (blob_equal(key, "state") && ctx->nsections == 3) {
+                               sactx->child_ok =
+                                       (sactx->event == 0 &&
+                                        (blob_equal(val, "INSTALLED") ||
+                                         blob_equal(val, "REKEYED")));
+                       }
+                       break;
+               }
+               break;
+       }
+}
+
+static void parse_cmd_response(
+       struct vici_message_ctx *ctx,
+       enum vici_type_t msgtype,
+       const struct blob *key, const struct blob *val)
+{
+       char buf[512];
+
+       switch (msgtype) {
+       case VICI_KEY_VALUE:
+               if (blob_equal(key, "errmsg") && blob2buf(val, buf, sizeof(buf)))
+                       zlog_err("VICI: strongSwan: %s", buf);
+               break;
+       default:
+               break;
+       }
+}
+
+static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
+{
+       char buf[32];
+       struct handle_sa_ctx ctx = {
+               .event = event,
+       };
+
+       vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
+
+       if (ctx.kill_ikesa && ctx.ike_uniqueid) {
+               debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid);
+               snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
+               vici_submit_request(
+                       vici, "terminate",
+                       VICI_KEY_VALUE, "ike-id", strlen(buf), buf,
+                       VICI_END);
+       }
+}
+
+static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
+{
+       uint32_t msglen;
+       uint8_t msgtype;
+       struct blob name;
+
+       msglen = zbuf_get_be32(msg);
+       msgtype = zbuf_get8(msg);
+       debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
+
+       switch (msgtype) {
+       case VICI_EVENT:
+               name.len = zbuf_get8(msg);
+               name.ptr = zbuf_pulln(msg, name.len);
+
+               debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr);
+               if (blob_equal(&name, "list-sa") ||
+                   blob_equal(&name, "child-updown") ||
+                   blob_equal(&name, "child-rekey"))
+                       vici_recv_sa(vici, msg, 0);
+               else if (blob_equal(&name, "child-state-installed") ||
+                        blob_equal(&name, "child-state-rekeyed"))
+                       vici_recv_sa(vici, msg, 1);
+               else if (blob_equal(&name, "child-state-destroying"))
+                       vici_recv_sa(vici, msg, 2);
+               break;
+       case VICI_CMD_RESPONSE:
+               vici_parse_message(vici, msg, parse_cmd_response, 0);
+               break;
+       case VICI_EVENT_UNKNOWN:
+       case VICI_CMD_UNKNOWN:
+               zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
+               break;
+       case VICI_EVENT_CONFIRM:
+               break;
+       default:
+               zlog_notice("VICI: Unrecognized message type %d", msgtype);
+               break;
+       }
+}
+
+static int vici_read(struct thread *t)
+{
+       struct vici_conn *vici = THREAD_ARG(t);
+       struct zbuf *ibuf = &vici->ibuf;
+       struct zbuf pktbuf;
+
+       vici->t_read = NULL;
+       if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) {
+               vici_connection_error(vici);
+               return 0;
+       }
+
+       /* Process all messages in buffer */
+       do {
+               uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
+               if (!hdrlen)
+                       break;
+               if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
+                       zbuf_reset_head(ibuf, hdrlen);
+                       break;
+               }
+
+               /* Handle packet */
+               zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4);
+               vici_recv_message(vici, &pktbuf);
+       } while (1);
+
+       THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
+       return 0;
+}
+
+static int vici_write(struct thread *t)
+{
+       struct vici_conn *vici = THREAD_ARG(t);
+       int r;
+
+       vici->t_write = NULL;
+       r = zbufq_write(&vici->obuf, vici->fd);
+       if (r > 0) {
+               THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
+       } else if (r < 0) {
+               vici_connection_error(vici);
+       }
+
+       return 0;
+}
+
+static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
+{
+       if (vici->fd < 0) {
+               zbuf_free(obuf);
+               return;
+       }
+
+       zbufq_queue(&vici->obuf, obuf);
+       THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
+}
+
+static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
+{
+       struct zbuf *obuf;
+       uint32_t *hdrlen;
+       va_list va;
+       size_t len;
+       int type;
+
+       obuf = zbuf_alloc(256);
+       if (!obuf) return;
+
+       hdrlen = zbuf_push(obuf, uint32_t);
+       zbuf_put8(obuf, VICI_CMD_REQUEST);
+       vici_zbuf_puts(obuf, name);
+
+       va_start(va, name);
+       for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
+               zbuf_put8(obuf, type);
+               switch (type) {
+               case VICI_KEY_VALUE:
+                       vici_zbuf_puts(obuf, va_arg(va, const char *));
+                       len = va_arg(va, size_t);
+                       zbuf_put_be16(obuf, len);
+                       zbuf_put(obuf, va_arg(va, void *), len);
+                       break;
+               case VICI_END:
+                       break;
+               default:
+                       break;
+               }
+       }
+       va_end(va);
+       *hdrlen = htonl(zbuf_used(obuf) - 4);
+       vici_submit(vici, obuf);
+}
+
+static void vici_register_event(struct vici_conn *vici, const char *name)
+{
+       struct zbuf *obuf;
+       uint32_t *hdrlen;
+       uint8_t namelen;
+
+       namelen = strlen(name);
+       obuf = zbuf_alloc(4 + 1 + 1 + namelen);
+       if (!obuf) return;
+
+       hdrlen = zbuf_push(obuf, uint32_t);
+       zbuf_put8(obuf, VICI_EVENT_REGISTER);
+       zbuf_put8(obuf, namelen);
+       zbuf_put(obuf, name, namelen);
+       *hdrlen = htonl(zbuf_used(obuf) - 4);
+
+       vici_submit(vici, obuf);
+}
+
+static int vici_reconnect(struct thread *t)
+{
+       struct vici_conn *vici = THREAD_ARG(t);
+       int fd;
+
+       vici->t_reconnect = NULL;
+       if (vici->fd >= 0) return 0;
+
+       fd = sock_open_unix("/var/run/charon.vici");
+       if (fd < 0) {
+               zlog_warn("%s: failure connecting VICI socket: %s",
+                       __PRETTY_FUNCTION__, strerror(errno));
+               THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
+               return 0;
+       }
+
+       debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
+       vici->fd = fd;
+       THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
+
+       /* Send event subscribtions */
+       //vici_register_event(vici, "child-updown");
+       //vici_register_event(vici, "child-rekey");
+       vici_register_event(vici, "child-state-installed");
+       vici_register_event(vici, "child-state-rekeyed");
+       vici_register_event(vici, "child-state-destroying");
+       vici_register_event(vici, "list-sa");
+       vici_submit_request(vici, "list-sas", VICI_END);
+
+       return 0;
+}
+
+static struct vici_conn vici_connection;
+
+void vici_init(void)
+{
+       struct vici_conn *vici = &vici_connection;
+
+       vici->fd = -1;
+       zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
+       zbufq_init(&vici->obuf);
+       THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10);
+}
+
+void vici_terminate(void)
+{
+}
+
+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio)
+{
+       struct vici_conn *vici = &vici_connection;
+       char buf[2][SU_ADDRSTRLEN];
+
+       sockunion2str(src, buf[0], sizeof buf[0]);
+       sockunion2str(dst, buf[1], sizeof buf[1]);
+
+       vici_submit_request(
+               vici, "initiate",
+               VICI_KEY_VALUE, "child", strlen(profile), profile,
+               VICI_KEY_VALUE, "timeout", (size_t) 2, "-1",
+               VICI_KEY_VALUE, "async", (size_t) 1, "1",
+               VICI_KEY_VALUE, "init-limits", (size_t) 1, prio ? "0" : "1",
+               VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0],
+               VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1],
+               VICI_END);
+}
+
+int sock_open_unix(const char *path)
+{
+       int ret, fd;
+       struct sockaddr_un addr;
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof (struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, path, strlen (path));
+
+       ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path));
+       if (ret < 0) {
+               close(fd);
+               return -1;
+       }
+
+       fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+       return fd;
+}
diff --git a/nhrpd/vici.h b/nhrpd/vici.h
new file mode 100644 (file)
index 0000000..24b900b
--- /dev/null
@@ -0,0 +1,24 @@
+
+enum vici_type_t {
+       VICI_START =         0,
+       VICI_SECTION_START = 1,
+       VICI_SECTION_END =   2,
+       VICI_KEY_VALUE =     3,
+       VICI_LIST_START =    4,
+       VICI_LIST_ITEM =     5,
+       VICI_LIST_END =      6,
+       VICI_END =           7
+};
+
+enum vici_operation_t {
+       VICI_CMD_REQUEST = 0,
+       VICI_CMD_RESPONSE,
+       VICI_CMD_UNKNOWN,
+       VICI_EVENT_REGISTER,
+       VICI_EVENT_UNREGISTER,
+       VICI_EVENT_CONFIRM,
+       VICI_EVENT_UNKNOWN,
+       VICI_EVENT,
+};
+
+#define VICI_MAX_MSGLEN                (512*1024)
diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c
new file mode 100644 (file)
index 0000000..ead7cfd
--- /dev/null
@@ -0,0 +1,219 @@
+/* Stream/packet buffer API implementation
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "zassert.h"
+#include "zbuf.h"
+#include "memory.h"
+#include "memtypes.h"
+#include "nhrpd.h"
+
+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+struct zbuf *zbuf_alloc(size_t size)
+{
+       struct zbuf *zb;
+
+       zb = XMALLOC(MTYPE_STREAM_DATA, sizeof(*zb) + size);
+       if (!zb)
+               return NULL;
+
+       zbuf_init(zb, zb+1, size, 0);
+       zb->allocated = 1;
+
+       return zb;
+}
+
+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen)
+{
+       *zb = (struct zbuf) {
+               .buf = buf,
+               .end = (uint8_t *)buf + len,
+               .head = buf,
+               .tail = (uint8_t *)buf + datalen,
+       };
+}
+
+void zbuf_free(struct zbuf *zb)
+{
+       if (zb->allocated)
+               XFREE(MTYPE_STREAM_DATA, zb);
+}
+
+void zbuf_reset(struct zbuf *zb)
+{
+       zb->head = zb->tail = zb->buf;
+       zb->error = 0;
+}
+
+void zbuf_reset_head(struct zbuf *zb, void *ptr)
+{
+       zassert((void*)zb->buf <= ptr && ptr <= (void*)zb->tail);
+       zb->head = ptr;
+}
+
+static void zbuf_remove_headroom(struct zbuf *zb)
+{
+       ssize_t headroom = zbuf_headroom(zb);
+       if (!headroom)
+               return;
+       memmove(zb->buf, zb->head, zbuf_used(zb));
+       zb->head -= headroom;
+       zb->tail -= headroom;
+}
+
+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen)
+{
+       ssize_t r;
+
+       if (zb->error)
+               return -3;
+
+       zbuf_remove_headroom(zb);
+       if (maxlen > zbuf_tailroom(zb))
+               maxlen = zbuf_tailroom(zb);
+
+       r = read(fd, zb->tail, maxlen);
+       if (r > 0) zb->tail += r;
+       else if (r == 0) r = -2;
+       else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+
+       return r;
+}
+
+ssize_t zbuf_write(struct zbuf *zb, int fd)
+{
+       ssize_t r;
+
+       if (zb->error)
+               return -3;
+
+       r = write(fd, zb->head, zbuf_used(zb));
+       if (r > 0) {
+               zb->head += r;
+               if (zb->head == zb->tail)
+                       zbuf_reset(zb);
+       }
+       else if (r == 0) r = -2;
+       else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+
+       return r;
+}
+
+ssize_t zbuf_recv(struct zbuf *zb, int fd)
+{
+       ssize_t r;
+
+       if (zb->error)
+               return -3;
+
+       zbuf_remove_headroom(zb);
+       r = recv(fd, zb->tail, zbuf_tailroom(zb), 0);
+       if (r > 0) zb->tail += r;
+       else if (r == 0) r = -2;
+       else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
+       return r;
+}
+
+ssize_t zbuf_send(struct zbuf *zb, int fd)
+{
+       ssize_t r;
+
+       if (zb->error)
+               return -3;
+
+       r = send(fd, zb->head, zbuf_used(zb), 0);
+       if (r >= 0)
+               zbuf_reset(zb);
+
+       return r;
+}
+
+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg)
+{
+       size_t seplen = strlen(sep), len;
+       uint8_t *ptr;
+
+       ptr = memmem(zb->head, zbuf_used(zb), sep, seplen);
+       if (!ptr) return NULL;
+
+       len = ptr - zb->head + seplen;
+       zbuf_init(msg, zbuf_pulln(zb, len), len, len);
+       return msg->head;
+}
+
+void zbufq_init(struct zbuf_queue *zbq)
+{
+       *zbq = (struct zbuf_queue) {
+               .queue_head = LIST_INITIALIZER(zbq->queue_head),
+       };
+}
+
+void zbufq_reset(struct zbuf_queue *zbq)
+{
+       struct zbuf *buf, *bufn;
+
+       list_for_each_entry_safe(buf, bufn, &zbq->queue_head, queue_list) {
+               list_del(&buf->queue_list);
+               zbuf_free(buf);
+       }
+}
+
+void zbufq_queue(struct zbuf_queue *zbq, struct zbuf *zb)
+{
+       list_add_tail(&zb->queue_list, &zbq->queue_head);
+}
+
+int zbufq_write(struct zbuf_queue *zbq, int fd)
+{
+       struct iovec iov[16];
+       struct zbuf *zb, *zbn;
+       ssize_t r;
+       size_t iovcnt = 0;
+
+       list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
+               iov[iovcnt++] = (struct iovec) {
+                       .iov_base = zb->head,
+                       .iov_len = zbuf_used(zb),
+               };
+               if (iovcnt >= ZEBRA_NUM_OF(iov))
+                       break;
+       }
+
+       r = writev(fd, iov, iovcnt);
+       if (r < 0)
+               return r;
+
+       list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
+               if (r < (ssize_t)zbuf_used(zb)) {
+                       zb->head += r;
+                       return 1;
+               }
+
+               r -= zbuf_used(zb);
+               list_del(&zb->queue_list);
+               zbuf_free(zb);
+       }
+
+       return 0;
+}
+
+void zbuf_copy(struct zbuf *zdst, struct zbuf *zsrc, size_t len)
+{
+       const void *src;
+       void *dst;
+
+       dst = zbuf_pushn(zdst, len);
+       src = zbuf_pulln(zsrc, len);
+       if (!dst || !src) return;
+       memcpy(dst, src, len);
+}
diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h
new file mode 100644 (file)
index 0000000..73d7073
--- /dev/null
@@ -0,0 +1,189 @@
+/* Stream/packet buffer API
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ZBUF_H
+#define ZBUF_H
+
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <sys/types.h>
+
+#include "zassert.h"
+#include "list.h"
+
+struct zbuf {
+       struct list_head queue_list;
+       unsigned allocated : 1;
+       unsigned error : 1;
+       uint8_t *buf, *end;
+       uint8_t *head, *tail;
+};
+
+struct zbuf_queue {
+       struct list_head queue_head;
+};
+
+struct zbuf *zbuf_alloc(size_t size);
+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen);
+void zbuf_free(struct zbuf *zb);
+
+static inline size_t zbuf_size(struct zbuf *zb)
+{
+       return zb->end - zb->buf;
+}
+
+static inline size_t zbuf_used(struct zbuf *zb)
+{
+       return zb->tail - zb->head;
+}
+
+static inline size_t zbuf_tailroom(struct zbuf *zb)
+{
+       return zb->end - zb->tail;
+}
+
+static inline size_t zbuf_headroom(struct zbuf *zb)
+{
+       return zb->head - zb->buf;
+}
+
+void zbuf_reset(struct zbuf *zb);
+void zbuf_reset_head(struct zbuf *zb, void *ptr);
+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen);
+ssize_t zbuf_write(struct zbuf *zb, int fd);
+ssize_t zbuf_recv(struct zbuf *zb, int fd);
+ssize_t zbuf_send(struct zbuf *zb, int fd);
+
+static inline void zbuf_set_rerror(struct zbuf *zb)
+{
+       zb->error = 1;
+       zb->head = zb->tail;
+}
+
+static inline void zbuf_set_werror(struct zbuf *zb)
+{
+       zb->error = 1;
+       zb->tail = zb->end;
+}
+
+static inline void *__zbuf_pull(struct zbuf *zb, size_t size, int error)
+{
+       void *head = zb->head;
+       if (size > zbuf_used(zb)) {
+               if (error) zbuf_set_rerror(zb);
+               return NULL;
+       }
+       zb->head += size;
+       return head;
+}
+
+#define zbuf_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 1))
+#define zbuf_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 1))
+#define zbuf_may_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 0))
+#define zbuf_may_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 0))
+
+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg);
+
+static inline void zbuf_get(struct zbuf *zb, void *dst, size_t len)
+{
+       void *src = zbuf_pulln(zb, len);
+       if (src) memcpy(dst, src, len);
+}
+
+static inline uint8_t zbuf_get8(struct zbuf *zb)
+{
+       uint8_t *src = zbuf_pull(zb, uint8_t);
+       if (src) return *src;
+       return 0;
+}
+
+static inline uint32_t zbuf_get32(struct zbuf *zb)
+{
+       struct unaligned32 {
+               uint32_t value;
+       } __attribute__((packed));
+
+       struct unaligned32 *v = zbuf_pull(zb, struct unaligned32);
+       if (v) return v->value;
+       return 0;
+}
+
+static inline uint16_t zbuf_get_be16(struct zbuf *zb)
+{
+       struct unaligned16 {
+               uint16_t value;
+       } __attribute__((packed));
+
+       struct unaligned16 *v = zbuf_pull(zb, struct unaligned16);
+       if (v) return be16toh(v->value);
+       return 0;
+}
+
+static inline uint32_t zbuf_get_be32(struct zbuf *zb)
+{
+       return be32toh(zbuf_get32(zb));
+}
+
+static inline void *__zbuf_push(struct zbuf *zb, size_t size, int error)
+{
+       void *tail = zb->tail;
+       if (size > zbuf_tailroom(zb)) {
+               if (error) zbuf_set_werror(zb);
+               return NULL;
+       }
+       zb->tail += size;
+       return tail;
+}
+
+#define zbuf_push(zb, type) ((type *)__zbuf_push(zb, sizeof(type), 1))
+#define zbuf_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 1))
+#define zbuf_may_push(zb, type) ((type *)__zbuf_may_push(zb, sizeof(type), 0))
+#define zbuf_may_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 0))
+
+static inline void zbuf_put(struct zbuf *zb, const void *src, size_t len)
+{
+       void *dst = zbuf_pushn(zb, len);
+       if (dst) memcpy(dst, src, len);
+}
+
+static inline void zbuf_put8(struct zbuf *zb, uint8_t val)
+{
+       uint8_t *dst = zbuf_push(zb, uint8_t);
+       if (dst) *dst = val;
+}
+
+static inline void zbuf_put_be16(struct zbuf *zb, uint16_t val)
+{
+       struct unaligned16 {
+               uint16_t value;
+       } __attribute__((packed));
+
+       struct unaligned16 *v = zbuf_push(zb, struct unaligned16);
+       if (v) v->value = htobe16(val);
+}
+
+static inline void zbuf_put_be32(struct zbuf *zb, uint32_t val)
+{
+       struct unaligned32 {
+               uint32_t value;
+       } __attribute__((packed));
+
+       struct unaligned32 *v = zbuf_push(zb, struct unaligned32);
+       if (v) v->value = htobe32(val);
+}
+
+void zbuf_copy(struct zbuf *zb, struct zbuf *src, size_t len);
+
+void zbufq_init(struct zbuf_queue *);
+void zbufq_reset(struct zbuf_queue *);
+void zbufq_queue(struct zbuf_queue *, struct zbuf *);
+int zbufq_write(struct zbuf_queue *, int);
+
+#endif
diff --git a/nhrpd/znl.c b/nhrpd/znl.c
new file mode 100644 (file)
index 0000000..2216d97
--- /dev/null
@@ -0,0 +1,160 @@
+/* Netlink helpers for zbuf
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "znl.h"
+
+#define ZNL_ALIGN(len)         (((len)+3) & ~3)
+
+void *znl_push(struct zbuf *zb, size_t n)
+{
+       return zbuf_pushn(zb, ZNL_ALIGN(n));
+}
+
+void *znl_pull(struct zbuf *zb, size_t n)
+{
+       return zbuf_pulln(zb, ZNL_ALIGN(n));
+}
+
+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags)
+{
+       struct nlmsghdr *n;
+
+       n = znl_push(zb, sizeof(*n));
+       if (!n) return NULL;
+
+       *n = (struct nlmsghdr) {
+               .nlmsg_type = type,
+               .nlmsg_flags = flags,
+       };
+       return n;
+}
+
+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n)
+{
+       n->nlmsg_len = zb->tail - (uint8_t*)n;
+}
+
+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload)
+{
+       struct nlmsghdr *n;
+       size_t plen;
+
+       n = znl_pull(zb, sizeof(*n));
+       if (!n) return NULL;
+
+       plen = n->nlmsg_len - sizeof(*n);
+       zbuf_init(payload, znl_pull(zb, plen), plen, plen);
+       zbuf_may_pulln(zb, ZNL_ALIGN(plen) - plen);
+
+       return n;
+}
+
+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len)
+{
+       struct rtattr *rta;
+       uint8_t *dst;
+
+       rta = znl_push(zb, ZNL_ALIGN(sizeof(*rta)) + ZNL_ALIGN(len));
+       if (!rta) return NULL;
+
+       *rta = (struct rtattr) {
+               .rta_type = type,
+               .rta_len  = ZNL_ALIGN(sizeof(*rta)) + len,
+       };
+
+       dst = (uint8_t *)(rta+1);
+       memcpy(dst, val, len);
+       memset(dst+len, 0, ZNL_ALIGN(len) - len);
+
+       return rta;
+}
+
+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val)
+{
+       return znl_rta_push(zb, type, &val, sizeof(val));
+}
+
+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type)
+{
+       struct rtattr *rta;
+
+       rta = znl_push(zb, sizeof(*rta));
+       if (!rta) return NULL;
+
+       *rta = (struct rtattr) {
+               .rta_type = type,
+       };
+       return rta;
+}
+
+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta)
+{
+       size_t len = zb->tail - (uint8_t*) rta;
+       size_t align = ZNL_ALIGN(len) - len;
+
+       if (align) {
+               void *dst = zbuf_pushn(zb, align);
+               if (dst) memset(dst, 0, align);
+       }
+       rta->rta_len = len;
+}
+
+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload)
+{
+       struct rtattr *rta;
+       size_t plen;
+
+       rta = znl_pull(zb, sizeof(*rta));
+       if (!rta) return NULL;
+
+       if (rta->rta_len > sizeof(*rta)) {
+               plen = rta->rta_len - sizeof(*rta);
+               zbuf_init(payload, znl_pull(zb, plen), plen, plen);
+       } else {
+               zbuf_init(payload, NULL, 0, 0);
+       }
+
+       return rta;
+}
+
+int znl_open(int protocol, int groups)
+{
+       struct sockaddr_nl addr;
+       int fd, buf = 128 * 1024;
+
+       fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+       if (fd < 0)
+               return -1;
+
+       fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+       if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf)) < 0)
+               goto error;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = groups;
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+               goto error;
+
+       return fd;
+error:
+       close(fd);
+       return -1;
+}
+
diff --git a/nhrpd/znl.h b/nhrpd/znl.h
new file mode 100644 (file)
index 0000000..2cd630b
--- /dev/null
@@ -0,0 +1,29 @@
+/* Netlink helpers for zbuf
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zbuf.h"
+
+#define ZNL_BUFFER_SIZE                8192
+
+void *znl_push(struct zbuf *zb, size_t n);
+void *znl_pull(struct zbuf *zb, size_t n);
+
+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags);
+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n);
+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload);
+
+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len);
+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val);
+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type);
+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta);
+
+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload);
+
+int znl_open(int protocol, int groups);
+
diff --git a/ospf6d/.gitignore b/ospf6d/.gitignore
new file mode 100644 (file)
index 0000000..3fef0b7
--- /dev/null
@@ -0,0 +1,18 @@
+Makefile.in
+Makefile
+*.o
+*.patch
+ospf6d
+ospf6d.conf
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+*.a
diff --git a/ospf6d/Makefile.am b/ospf6d/Makefile.am
new file mode 100644 (file)
index 0000000..75f2257
--- /dev/null
@@ -0,0 +1,32 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = libospf6.a
+sbin_PROGRAMS = ospf6d
+
+libospf6_a_SOURCES = \
+       ospf6_network.c ospf6_message.c ospf6_lsa.c ospf6_lsdb.c \
+       ospf6_top.c ospf6_area.c ospf6_interface.c ospf6_neighbor.c \
+       ospf6_flood.c ospf6_route.c ospf6_intra.c ospf6_zebra.c \
+       ospf6_spf.c ospf6_proto.c ospf6_asbr.c ospf6_abr.c ospf6_snmp.c \
+       ospf6d.c
+
+noinst_HEADERS = \
+       ospf6_network.h ospf6_message.h ospf6_lsa.h ospf6_lsdb.h \
+       ospf6_top.h ospf6_area.h ospf6_interface.h ospf6_neighbor.h \
+       ospf6_flood.h ospf6_route.h ospf6_intra.h ospf6_zebra.h \
+       ospf6_spf.h ospf6_proto.h ospf6_asbr.h ospf6_abr.h ospf6_snmp.h \
+       ospf6d.h
+
+ospf6d_SOURCES = \
+       ospf6_main.c $(libospf6_a_SOURCES)
+
+ospf6d_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = ospf6d.conf.sample
diff --git a/ospf6d/OSPFv3-MIB.txt b/ospf6d/OSPFv3-MIB.txt
new file mode 100644 (file)
index 0000000..258f533
--- /dev/null
@@ -0,0 +1,3951 @@
+ OSPFV3-MIB DEFINITIONS ::= BEGIN
+
+ IMPORTS
+         MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, mib-2,
+         Counter32, Gauge32, Integer32, Unsigned32
+                 FROM SNMPv2-SMI
+         TEXTUAL-CONVENTION, TruthValue, RowStatus, TimeStamp
+                 FROM SNMPv2-TC
+         MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP
+                 FROM SNMPv2-CONF
+         InterfaceIndex
+                 FROM IF-MIB
+         InetAddressType, InetAddress, InetAddressPrefixLength,
+         InetAddressIPv6
+                 FROM INET-ADDRESS-MIB
+         Metric, BigMetric, Status,
+         HelloRange, DesignatedRouterPriority
+                 FROM OSPF-MIB;
+
+ ospfv3MIB MODULE-IDENTITY
+         LAST-UPDATED "200908130000Z"
+         ORGANIZATION "IETF OSPF Working Group"
+         CONTACT-INFO
+             "WG E-Mail: ospf@ietf.org
+              WG Chairs: Acee Lindem
+                         acee@redback.com
+
+                         Abhay Roy
+                         akr@cisco.com
+
+              Editors:   Dan Joyal
+                         Nortel
+                         600 Technology Park Drive
+                         Billerica, MA  01821, USA
+                         djoyal@nortel.com
+
+                         Vishwas Manral
+                         IP Infusion
+                         Almora, Uttarakhand
+                         India
+                         vishwas@ipinfusion.com"
+          DESCRIPTION
+             "The MIB module for OSPF version 3.
+
+              Copyright (c) 2009 IETF Trust and the persons
+              identified as authors of the code.  All rights
+              reserved.
+
+              Redistribution and use in source and binary forms, with
+              or without modification, are permitted provided that
+              the following conditions are met:
+
+              - Redistributions of source code must retain the above
+                copyright notice, this list of conditions and the
+                following disclaimer.
+
+             - Redistributions in binary form must reproduce the
+               above copyright notice, this list of conditions and
+               the following disclaimer in the documentation and/or
+               other materials provided with the distribution.
+
+             - Neither the name of Internet Society, IETF or IETF
+               Trust, nor the names of specific contributors, may be
+               used to endorse or promote products derived from this
+               software without specific prior written permission.
+
+               THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+               CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED
+               WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+               WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+               PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+               THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+               DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+               CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+               PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+               USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+               HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+               IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+               NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+               USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+               POSSIBILITY OF SUCH DAMAGE.
+
+               This version of this MIB module is part of RFC 5643;
+               see the RFC itself for full legal notices."
+
+          REVISION "200908130000Z"
+          DESCRIPTION
+              "Initial version, published as RFC 5643"
+          ::= { mib-2 191 }
+
+ -- Textual conventions
+
+ Ospfv3UpToRefreshIntervalTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS        current
+          DESCRIPTION
+               "The values one might be able to configure for
+               variables bounded by the Refresh Interval."
+          REFERENCE
+               "OSPF Version 2, Appendix B, Architectural Constants"
+          SYNTAX      Unsigned32 (1..1800)
+
+ Ospfv3DeadIntervalRangeTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS        current
+          DESCRIPTION
+               "The range, in seconds, of dead interval value."
+          REFERENCE
+               "OSPF for IPv6, Appendix C.3, Router Interface
+               Parameters"
+          SYNTAX      Unsigned32 (1..'FFFF'h)
+
+ Ospfv3RouterIdTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+               "A 32-bit, unsigned integer uniquely identifying the
+               router in the Autonomous System.  To ensure
+               uniqueness, this may default to the value of one of
+               the router's IPv4 host addresses if IPv4 is
+               configured on the router."
+          REFERENCE
+               "OSPF for IPv6, Appendix C.1, Global Parameters"
+          SYNTAX      Unsigned32 (1..'FFFFFFFF'h)
+
+ Ospfv3LsIdTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+               "A unique 32-bit identifier of the piece of the
+               routing domain that is being described by a link
+               state advertisement.  In contrast to OSPFv2, the
+               Link State ID (LSID) has no addressing semantics."
+          REFERENCE
+               "OSPF Version 2, Section 12.1.4, Link State ID"
+          SYNTAX      Unsigned32 (1..'FFFFFFFF'h)
+
+ Ospfv3AreaIdTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+               "An OSPFv3 Area Identifier.  A value of zero
+               identifies the backbone area."
+          REFERENCE
+               "OSPF for IPv6, Appendix C.3 Router Interface
+               Parameters"
+          SYNTAX      Unsigned32 (0..'FFFFFFFF'h)
+
+ Ospfv3IfInstIdTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+               "An OSPFv3 Interface Instance ID."
+          REFERENCE
+               "OSPF for IPv6, Appendix C.3, Router Interface
+               Parameters"
+          SYNTAX      Unsigned32 (0..255)
+
+ Ospfv3LsaSequenceTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+             "The sequence number field is a signed 32-bit
+             integer.  It is used to detect old and duplicate
+             link state advertisements.  The space of
+             sequence numbers is linearly ordered.  The
+             larger the sequence number, the more recent the
+             advertisement."
+          REFERENCE
+             "OSPF Version 2, Section 12.1.6, LS sequence
+             number"
+          SYNTAX      Integer32
+
+ Ospfv3LsaAgeTC ::= TEXTUAL-CONVENTION
+          DISPLAY-HINT "d"
+          STATUS      current
+          DESCRIPTION
+             "The age of the link state advertisement in
+             seconds.  The high-order bit of the LS age
+             field is considered the DoNotAge bit for
+             support of on-demand circuits."
+          REFERENCE
+             "OSPF Version 2, Section 12.1.1, LS age;
+              Extending OSPF to Support Demand Circuits,
+              Section 2.2, The LS age field"
+          SYNTAX      Unsigned32 (0..3600 | 32768..36368)
+
+ -- Top-level structure of MIB
+ ospfv3Notifications  OBJECT IDENTIFIER ::= { ospfv3MIB 0 }
+ ospfv3Objects        OBJECT IDENTIFIER ::= { ospfv3MIB 1 }
+ ospfv3Conformance    OBJECT IDENTIFIER ::= { ospfv3MIB 2 }
+
+ -- OSPFv3 General Variables
+
+ -- These parameters apply globally to the Router's
+ -- OSPFv3 Process.
+
+ ospfv3GeneralGroup OBJECT IDENTIFIER ::= { ospfv3Objects 1 }
+
+ ospfv3RouterId OBJECT-TYPE
+         SYNTAX         Ospfv3RouterIdTC
+         MAX-ACCESS     read-write
+         STATUS         current
+         DESCRIPTION
+             "A 32-bit unsigned integer uniquely identifying
+             the router in the Autonomous System.  To ensure
+             uniqueness, this may default to the 32-bit
+             unsigned integer representation of one of
+             the router's IPv4 interface addresses (if IPv4
+             is configured on the router).
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         REFERENCE
+               "OSPF for IPv6, Appendix C.1, Global Parameters"
+         ::= { ospfv3GeneralGroup 1 }
+
+ ospfv3AdminStatus OBJECT-TYPE
+         SYNTAX          Status
+         MAX-ACCESS      read-write
+         STATUS          current
+         DESCRIPTION
+             "The administrative status of OSPFv3 in the
+             router.  The value 'enabled' denotes that the
+             OSPFv3 Process is active on at least one
+             interface; 'disabled' disables it on all
+             interfaces.
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         ::= { ospfv3GeneralGroup 2 }
+
+ ospfv3VersionNumber OBJECT-TYPE
+         SYNTAX          INTEGER { version3 (3) }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The version number of OSPF for IPv6 is 3."
+         ::= { ospfv3GeneralGroup 3 }
+
+ ospfv3AreaBdrRtrStatus OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "A flag to denote whether this router is an area
+             border router.  The value of this object is true (1)
+             when the router is an area border router."
+         REFERENCE
+             "OSPF Version 2, Section 3, Splitting the AS into
+             Areas"
+         ::= { ospfv3GeneralGroup 4 }
+
+ ospfv3ASBdrRtrStatus OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-write
+         STATUS          current
+         DESCRIPTION
+             "A flag to note whether this router is
+             configured as an Autonomous System border router.
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         REFERENCE
+             "OSPF Version 2, Section 3.3, Classification of
+             routers"
+         ::= { ospfv3GeneralGroup 5 }
+
+ ospfv3AsScopeLsaCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of AS-scope (e.g., AS-External) link state
+             advertisements in the link state database."
+         ::= { ospfv3GeneralGroup 6 }
+
+ ospfv3AsScopeLsaCksumSum OBJECT-TYPE
+         SYNTAX          Unsigned32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit unsigned sum of the LS checksums of
+             the AS-scoped link state advertisements
+             contained in the link state database.  This sum
+             can be used to determine if there has been a
+             change in a router's link state database or
+             to compare the link state database of two
+             routers."
+         ::= { ospfv3GeneralGroup 7 }
+
+ ospfv3OriginateNewLsas OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of new link state advertisements
+             that have been originated.  This number is
+             incremented each time the router originates a new
+             LSA.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3GeneralGroup 8 }
+
+ ospfv3RxNewLsas OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of link state advertisements
+             received that are determined to be new
+             instantiations.  This number does not include
+             newer instantiations of self-originated link state
+             advertisements.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3GeneralGroup 9 }
+
+ ospfv3ExtLsaCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+              "The number of External (LS type 0x4005) in the
+              link state database."
+         ::= { ospfv3GeneralGroup 10 }
+
+ ospfv3ExtAreaLsdbLimit OBJECT-TYPE
+         SYNTAX          Integer32 (-1..'7FFFFFFF'h)
+         MAX-ACCESS      read-write
+         STATUS          current
+         DESCRIPTION
+             "The maximum number of non-default
+             AS-external-LSA entries that can be stored in the
+             link state database.  If the value is -1, then
+             there is no limit.
+
+             When the number of non-default AS-external-LSAs
+             in a router's link state database reaches
+             ospfv3ExtAreaLsdbLimit, the router enters Overflow
+             state.  The router never holds more than
+             ospfv3ExtAreaLsdbLimit non-default AS-external-LSAs
+             in its database.  ospfv3ExtAreaLsdbLimit MUST be set
+             identically in all routers attached to the OSPFv3
+             backbone and/or any regular OSPFv3 area (i.e.,
+             OSPFv3 stub areas and not-so-stubby-areas (NSSAs)
+             are excluded).
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         ::= { ospfv3GeneralGroup 11 }
+
+ ospfv3ExitOverflowInterval OBJECT-TYPE
+         SYNTAX          Unsigned32
+         UNITS           "seconds"
+         MAX-ACCESS      read-write
+         STATUS          current
+         DESCRIPTION
+             "The number of seconds that, after entering
+             Overflow state, a router will attempt to leave
+             Overflow state.  This allows the router to again
+             originate non-default, AS-External-LSAs.  When
+             set to 0, the router will not leave Overflow
+             state until restarted.
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         ::= { ospfv3GeneralGroup 12 }
+
+ ospfv3DemandExtensions OBJECT-TYPE
+         SYNTAX         TruthValue
+         MAX-ACCESS     read-write
+         STATUS         current
+         DESCRIPTION
+             "The router's support for demand circuits.
+             The value of this object is true (1) when
+             demand circuits are supported.
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+         REFERENCE
+             "OSPF Version 2; Extending OSPF to Support Demand
+             Circuits"
+         ::= { ospfv3GeneralGroup 13 }
+
+ ospfv3ReferenceBandwidth OBJECT-TYPE
+        SYNTAX       Unsigned32
+        UNITS        "kilobits per second"
+        MAX-ACCESS   read-write
+        STATUS       current
+        DESCRIPTION
+            "Reference bandwidth in kilobits per second for
+            calculating default interface metrics.  The
+            default value is 100,000 KBPS (100 MBPS).
+
+            This object is persistent, and when written, the
+            entity SHOULD save the change to non-volatile
+            storage."
+        REFERENCE
+            "OSPF Version 2, Appendix C.3, Router interface
+            parameters"
+        DEFVAL { 100000 }
+     ::= { ospfv3GeneralGroup 14 }
+
+ ospfv3RestartSupport OBJECT-TYPE
+        SYNTAX       INTEGER { none(1),
+                               plannedOnly(2),
+                               plannedAndUnplanned(3)
+                          }
+        MAX-ACCESS   read-write
+        STATUS       current
+        DESCRIPTION
+            "The router's support for OSPF graceful restart.
+            Options include no restart support, only planned
+
+            restarts, or both planned and unplanned restarts.
+
+            This object is persistent, and when written, the
+            entity SHOULD save the change to non-volatile
+            storage."
+        REFERENCE "Graceful OSPF Restart, Appendix B.1, Global
+                     Parameters (Minimum subset)"
+        ::= { ospfv3GeneralGroup 15 }
+
+ ospfv3RestartInterval OBJECT-TYPE
+        SYNTAX       Ospfv3UpToRefreshIntervalTC
+        UNITS        "seconds"
+        MAX-ACCESS   read-write
+        STATUS       current
+        DESCRIPTION
+            "Configured OSPF graceful restart timeout interval.
+
+            This object is persistent, and when written, the
+            entity SHOULD save the change to non-volatile
+            storage."
+        REFERENCE "Graceful OSPF Restart, Appendix B.1, Global
+                  Parameters (Minimum subset)"
+        DEFVAL { 120 }
+        ::= { ospfv3GeneralGroup 16 }
+
+ ospfv3RestartStrictLsaChecking OBJECT-TYPE
+       SYNTAX       TruthValue
+       MAX-ACCESS   read-write
+       STATUS       current
+       DESCRIPTION
+          "Indicates if strict LSA checking is enabled for
+          graceful restart.  A value of true (1) indicates that
+          strict LSA checking is enabled.
+
+          This object is persistent, and when written,
+          the entity SHOULD save the change to non-volatile
+          storage."
+       REFERENCE "Graceful OSPF Restart, Appendix B.2, Global
+                 Parameters (Optional)"
+       DEFVAL { true }
+       ::= { ospfv3GeneralGroup 17 }
+
+ ospfv3RestartStatus OBJECT-TYPE
+        SYNTAX       INTEGER { notRestarting(1),
+                               plannedRestart(2),
+                               unplannedRestart(3)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "The current status of OSPF graceful restart capability."
+        ::= { ospfv3GeneralGroup 18 }
+
+ ospfv3RestartAge OBJECT-TYPE
+        SYNTAX       Ospfv3UpToRefreshIntervalTC
+        UNITS        "seconds"
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "Remaining time in the current OSPF graceful restart
+           interval."
+        ::= { ospfv3GeneralGroup 19 }
+
+ ospfv3RestartExitReason OBJECT-TYPE
+        SYNTAX       INTEGER { none(1),
+                               inProgress(2),
+                               completed(3),
+                               timedOut(4),
+                               topologyChanged(5)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "Describes the outcome of the last attempt at a
+           graceful restart.
+
+           none:            no restart has yet been attempted.
+           inProgress:      a restart attempt is currently underway.
+           completed:       the last restart completed successfully.
+           timedOut:        the last restart timed out.
+           topologyChanged: the last restart was aborted due to
+                            a topology change."
+     ::= { ospfv3GeneralGroup 20 }
+
+ ospfv3NotificationEnable OBJECT-TYPE
+        SYNTAX TruthValue
+        MAX-ACCESS read-write
+        STATUS current
+        DESCRIPTION
+            "This object provides a coarse level of control
+             over the generation of OSPFv3 notifications.
+
+             If this object is set to true (1), then it enables
+             the generation of OSPFv3 notifications.  If it is
+             set to false (2), these notifications are not
+             generated.
+
+             This object is persistent, and when written, the
+             entity SHOULD save the change to non-volatile
+             storage."
+    ::= { ospfv3GeneralGroup 21 }
+
+ospfv3StubRouterSupport OBJECT-TYPE
+     SYNTAX       TruthValue
+     MAX-ACCESS   read-only
+     STATUS       current
+     DESCRIPTION
+         "The router's support for stub router functionality.  An
+         object value of true (1) indicates that stub router
+         functionality is supported."
+     REFERENCE
+         "OSPF Stub Router Advertisement"
+     ::= { ospfv3GeneralGroup 22 }
+
+ ospfv3StubRouterAdvertisement OBJECT-TYPE
+     SYNTAX       INTEGER {
+                        doNotAdvertise(1),
+                        advertise(2)
+                        }
+     MAX-ACCESS   read-write
+     STATUS       current
+     DESCRIPTION
+         "This object controls the advertisement of
+         stub LSAs by the router.  The value
+         doNotAdvertise (1) will result in the advertisement
+         of standard LSAs and is the default value.
+
+         This object is persistent, and when written,
+         the entity SHOULD save the change to non-volatile
+         storage."
+     REFERENCE
+         "OSPF Stub Router Advertisement, Section 2, Proposed
+         Solution"
+     DEFVAL { doNotAdvertise }
+     ::= { ospfv3GeneralGroup 23 }
+
+ospfv3DiscontinuityTime OBJECT-TYPE
+    SYNTAX     TimeStamp
+    MAX-ACCESS read-only
+    STATUS     current
+    DESCRIPTION
+       "The value of sysUpTime on the most recent occasion
+        at which any one of this MIB's counters suffered
+        a discontinuity.
+
+        If no such discontinuities have occurred since the last
+        re-initialization of the local management subsystem,
+        then this object contains a zero value."
+    ::= { ospfv3GeneralGroup 24 }
+
+  ospfv3RestartTime OBJECT-TYPE
+      SYNTAX     TimeStamp
+      MAX-ACCESS read-only
+      STATUS     current
+      DESCRIPTION
+         "The value of sysUpTime on the most recent occasion
+          at which the ospfv3RestartExitReason was updated."
+      ::= { ospfv3GeneralGroup 25 }
+
+ -- The OSPFv3 Area Data Structure contains information
+ -- regarding the various areas.  The interfaces and
+ -- virtual links are configured as part of these areas.
+ -- Area 0, by definition, is the backbone area.
+
+ ospfv3AreaTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3AreaEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Information describing the configured
+             parameters and cumulative statistics of the router's
+             attached areas.  The interfaces and
+             virtual links are configured as part of these areas.
+             Area 0, by definition, is the backbone area."
+         REFERENCE
+             "OSPF Version 2, Section 6, The Area Data
+             Structure"
+         ::= { ospfv3Objects 2 }
+
+ ospfv3AreaEntry OBJECT-TYPE
+         SYNTAX          Ospfv3AreaEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Information describing the configured
+             parameters and cumulative statistics of one of the
+             router's attached areas.
+
+             The information in this table is persistent,
+             and when written, the entity SHOULD save the a
+             change to non-volatile storage."
+         INDEX           { ospfv3AreaId }
+         ::= { ospfv3AreaTable 1 }
+
+ Ospfv3AreaEntry ::= SEQUENCE {
+         ospfv3AreaId
+                 Ospfv3AreaIdTC,
+         ospfv3AreaImportAsExtern
+                 INTEGER,
+         ospfv3AreaSpfRuns
+                 Counter32,
+         ospfv3AreaBdrRtrCount
+                 Gauge32,
+         ospfv3AreaAsBdrRtrCount
+                 Gauge32,
+         ospfv3AreaScopeLsaCount
+                 Gauge32,
+         ospfv3AreaScopeLsaCksumSum
+                 Unsigned32,
+         ospfv3AreaSummary
+                 INTEGER,
+         ospfv3AreaRowStatus
+                 RowStatus,
+         ospfv3AreaStubMetric
+                 BigMetric,
+         ospfv3AreaNssaTranslatorRole
+                 INTEGER,
+         ospfv3AreaNssaTranslatorState
+                 INTEGER,
+         ospfv3AreaNssaTranslatorStabInterval
+                 Unsigned32,
+         ospfv3AreaNssaTranslatorEvents
+                 Counter32,
+         ospfv3AreaStubMetricType
+                 INTEGER,
+         ospfv3AreaTEEnabled
+                 TruthValue
+         }
+
+ ospfv3AreaId OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A 32-bit unsigned integer uniquely identifying an area.
+             Area ID 0 is used for the OSPFv3 backbone."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3AreaEntry 1 }
+
+ ospfv3AreaImportAsExtern OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         importExternal(1),   -- normal area
+                         importNoExternal(2), -- stub area
+                         importNssa(3)        -- not-so-stubby-area
+                         }
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "Indicates whether an area is a stub area, NSSA, or
+             standard area.  AS-scope LSAs are not imported into stub
+             areas or NSSAs.  NSSAs import AS-External data as NSSA
+             LSAs that have Area-scope."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         DEFVAL { importExternal }
+         ::= { ospfv3AreaEntry 2 }
+
+ ospfv3AreaSpfRuns OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of times that the intra-area route
+             table has been calculated using this area's
+             link state database.  This is typically done
+             using Dijkstra's algorithm.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3AreaEntry 3 }
+
+ ospfv3AreaBdrRtrCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The total number of area border routers
+             reachable within this area.  This is initially zero,
+             and is calculated in each Shortest Path First (SPF)
+             pass."
+         DEFVAL { 0 }
+         ::= { ospfv3AreaEntry 4 }
+
+ ospfv3AreaAsBdrRtrCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The total number of Autonomous System border
+             routers reachable within this area.  This is
+             initially zero, and is calculated in each SPF
+             pass."
+         DEFVAL { 0 }
+         ::= { ospfv3AreaEntry 5 }
+
+ ospfv3AreaScopeLsaCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The total number of Area-scope link state
+             advertisements in this area's link state
+             database."
+          DEFVAL { 0 }
+         ::= { ospfv3AreaEntry 6 }
+
+ ospfv3AreaScopeLsaCksumSum OBJECT-TYPE
+         SYNTAX          Unsigned32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit unsigned sum of the Area-scope link state
+             advertisements' LS checksums contained in this
+             area's link state database.  The sum can be used
+             to determine if there has been a change in a
+             router's link state database or to compare the
+             link state database of two routers."
+         ::= { ospfv3AreaEntry 7 }
+
+ ospfv3AreaSummary OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         noAreaSummary(1),
+                         sendAreaSummary(2)
+                         }
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The variable ospfv3AreaSummary controls the
+             import of Inter-Area LSAs into stub and
+             NSSA areas.  It has no effect on other areas.
+
+             If it is noAreaSummary, the router will neither
+             originate nor propagate Inter-Area LSAs into the
+             stub or NSSA area.  It will only advertise a
+             default route.
+
+             If it is sendAreaSummary, the router will both
+             summarize and propagate Inter-Area LSAs."
+         DEFVAL   { sendAreaSummary }
+         ::= { ospfv3AreaEntry 8 }
+
+ ospfv3AreaRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3AreaEntry 9 }
+
+ ospfv3AreaStubMetric OBJECT-TYPE
+         SYNTAX          BigMetric
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The metric value advertised for the default route
+             into stub and NSSA areas.  By default, this equals the
+             least metric among the interfaces to other areas."
+         ::= { ospfv3AreaEntry 10 }
+
+ ospfv3AreaNssaTranslatorRole OBJECT-TYPE
+         SYNTAX          INTEGER { always(1), candidate(2) }
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "Indicates an NSSA border router's policy to
+             perform NSSA translation of NSSA-LSAs into
+             AS-External-LSAs."
+         DEFVAL { candidate }
+         ::= { ospfv3AreaEntry 11 }
+
+ ospfv3AreaNssaTranslatorState OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         enabled(1),
+                         elected(2),
+                         disabled(3)
+                         }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+              "Indicates if and how an NSSA border router is
+              performing NSSA translation of NSSA-LSAs into
+              AS-External-LSAs.  When this object is set to
+              'enabled', the NSSA border router's
+              ospfv3AreaNssaTranslatorRole has been set to 'always'.
+              When this object is set to 'elected', a candidate
+              NSSA border router is translating NSSA-LSAs into
+              AS-External-LSAs.  When this object is set to
+              'disabled', a candidate NSSA Border router is NOT
+              translating NSSA-LSAs into AS-External-LSAs."
+         ::= { ospfv3AreaEntry 12 }
+
+ ospfv3AreaNssaTranslatorStabInterval OBJECT-TYPE
+         SYNTAX          Unsigned32
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The stability interval defined as the number of
+             seconds after an elected translator determines its
+             services are no longer required that it should
+             continue to perform its translation duties."
+         DEFVAL { 40 }
+         ::= { ospfv3AreaEntry 13 }
+
+ ospfv3AreaNssaTranslatorEvents OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "Indicates the number of Translator state changes
+             that have occurred since the last start-up of the
+             OSPFv3 routing process.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3AreaEntry 14 }
+
+ ospfv3AreaStubMetricType OBJECT-TYPE
+         SYNTAX       INTEGER {
+                         ospfv3Metric(1),   -- OSPF Metric
+                         comparableCost(2), -- external type 1
+                         nonComparable(3)   -- external type 2
+                         }
+         MAX-ACCESS   read-create
+         STATUS       current
+         DESCRIPTION
+             "This variable assigns the type of metric
+             advertised as a default route."
+         DEFVAL { ospfv3Metric }
+         ::= { ospfv3AreaEntry 15 }
+
+ ospfv3AreaTEEnabled OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+                "Indicates whether or not traffic engineering
+                is enabled in the area.  The object is set
+                to the value true (1) to enable traffic engineering.
+                Traffic engineering is disabled by default."
+         DEFVAL { false }
+         ::= { ospfv3AreaEntry 16 }
+
+ -- OSPFv3 AS-Scope Link State Database
+
+ ospfv3AsLsdbTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3AsLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Process's AS-scope link state database
+             (LSDB).  The LSDB contains the AS-scope link state
+             advertisements from throughout the areas that the
+             device is attached to."
+         ::= { ospfv3Objects 3 }
+
+ ospfv3AsLsdbEntry OBJECT-TYPE
+         SYNTAX          Ospfv3AsLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A single AS-scope link state advertisement."
+         INDEX           { ospfv3AsLsdbType,
+                           ospfv3AsLsdbRouterId,
+                           ospfv3AsLsdbLsid }
+         ::= { ospfv3AsLsdbTable 1 }
+
+ Ospfv3AsLsdbEntry ::= SEQUENCE {
+         ospfv3AsLsdbType
+                 Unsigned32,
+         ospfv3AsLsdbRouterId
+                 Ospfv3RouterIdTC,
+         ospfv3AsLsdbLsid
+                 Ospfv3LsIdTC,
+         ospfv3AsLsdbSequence
+                 Ospfv3LsaSequenceTC,
+         ospfv3AsLsdbAge
+                 Ospfv3LsaAgeTC,
+         ospfv3AsLsdbChecksum
+                 Integer32,
+         ospfv3AsLsdbAdvertisement
+                 OCTET STRING,
+         ospfv3AsLsdbTypeKnown
+                 TruthValue
+         }
+
+ ospfv3AsLsdbType OBJECT-TYPE
+         SYNTAX          Unsigned32(0..'FFFFFFFF'h)
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The type of the link state advertisement.
+             Each link state type has a separate
+             advertisement format.  AS-scope LSAs not recognized
+             by the router may be stored in the database."
+         ::= { ospfv3AsLsdbEntry 1 }
+
+ ospfv3AsLsdbRouterId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit number that uniquely identifies the
+             originating router in the Autonomous System."
+         REFERENCE
+             "OSPF Version 2, Appendix C.1, Global parameters"
+         ::= { ospfv3AsLsdbEntry 2 }
+
+ ospfv3AsLsdbLsid OBJECT-TYPE
+         SYNTAX          Ospfv3LsIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Link State ID is an LS type-specific field
+             containing a unique identifier;
+             it identifies the piece of the routing domain
+             that is being described by the advertisement.
+             In contrast to OSPFv2, the LSID has no
+             addressing semantics."
+         ::= { ospfv3AsLsdbEntry 3 }
+
+ -- Note that the OSPF sequence number is a 32-bit signed
+ -- integer.  It starts with the value '80000001'h
+ -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h.
+ -- Thus, a typical sequence number will be very negative.
+
+ ospfv3AsLsdbSequence OBJECT-TYPE
+         SYNTAX          Ospfv3LsaSequenceTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The sequence number field is a signed 32-bit
+             integer.  It is used to detect old and duplicate
+             link state advertisements.  The space of
+             sequence numbers is linearly ordered.  The
+             larger the sequence number, the more recent the
+             advertisement."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.6, LS sequence
+             number"
+         ::= { ospfv3AsLsdbEntry 4 }
+
+ ospfv3AsLsdbAge OBJECT-TYPE
+         SYNTAX          Ospfv3LsaAgeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the age of the link state
+             advertisement in seconds.  The high-order bit
+             of the LS age field is considered the DoNotAge
+             bit for support of on-demand circuits."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.1, LS age;
+              Extending OSPF to Support Demand Circuits,
+              Section 2.2, The LS age field."
+         ::= { ospfv3AsLsdbEntry 5 }
+
+ ospfv3AsLsdbChecksum OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the checksum of the complete
+             contents of the advertisement, excepting the
+             age field.  The age field is excepted so that
+             an advertisement's age can be incremented
+             without updating the checksum.  The checksum
+             used is the same that is used for ISO
+             connectionless datagrams; it is commonly
+             referred to as the Fletcher checksum."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.7, LS checksum"
+         ::= { ospfv3AsLsdbEntry 6 }
+
+ ospfv3AsLsdbAdvertisement OBJECT-TYPE
+         SYNTAX          OCTET STRING (SIZE (1..65535))
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The entire link state advertisement, including
+             its header."
+         ::= { ospfv3AsLsdbEntry 7 }
+
+ ospfv3AsLsdbTypeKnown OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The value true (1) indicates that the LSA type
+             is recognized by this router."
+         ::= { ospfv3AsLsdbEntry 8 }
+
+  --  OSPFv3 Area-Scope Link State Database
+
+ ospfv3AreaLsdbTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3AreaLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Process's Area-scope LSDB.
+             The LSDB contains the Area-scope link state
+             advertisements from throughout the area that the
+             device is attached to."
+         ::= { ospfv3Objects 4 }
+
+ ospfv3AreaLsdbEntry OBJECT-TYPE
+         SYNTAX          Ospfv3AreaLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A single Area-scope link state advertisement."
+         INDEX           { ospfv3AreaLsdbAreaId,
+                           ospfv3AreaLsdbType,
+                           ospfv3AreaLsdbRouterId,
+                           ospfv3AreaLsdbLsid }
+         ::= { ospfv3AreaLsdbTable 1 }
+
+ Ospfv3AreaLsdbEntry ::= SEQUENCE {
+         ospfv3AreaLsdbAreaId
+                 Ospfv3AreaIdTC,
+         ospfv3AreaLsdbType
+                 Unsigned32,
+         ospfv3AreaLsdbRouterId
+                 Ospfv3RouterIdTC,
+         ospfv3AreaLsdbLsid
+                 Ospfv3LsIdTC,
+         ospfv3AreaLsdbSequence
+                 Ospfv3LsaSequenceTC,
+         ospfv3AreaLsdbAge
+                 Ospfv3LsaAgeTC,
+         ospfv3AreaLsdbChecksum
+                 Integer32,
+         ospfv3AreaLsdbAdvertisement
+                 OCTET STRING,
+         ospfv3AreaLsdbTypeKnown
+                 TruthValue
+         }
+
+ ospfv3AreaLsdbAreaId OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit identifier of the Area from which the
+             LSA was received."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3AreaLsdbEntry 1 }
+
+ ospfv3AreaLsdbType OBJECT-TYPE
+         SYNTAX          Unsigned32(0..'FFFFFFFF'h)
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The type of the link state advertisement.
+             Each link state type has a separate
+             advertisement format.  Area-scope LSAs unrecognized
+             by the router are also stored in this database."
+         ::= { ospfv3AreaLsdbEntry 2 }
+
+ ospfv3AreaLsdbRouterId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit number that uniquely identifies the
+             originating router in the Autonomous System."
+         REFERENCE
+             "OSPF Version 2, Appendix C.1, Global parameters"
+         ::= { ospfv3AreaLsdbEntry 3 }
+
+ ospfv3AreaLsdbLsid OBJECT-TYPE
+         SYNTAX          Ospfv3LsIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Link State ID is an LS type-specific field
+             containing a unique identifier;
+             it identifies the piece of the routing domain
+             that is being described by the advertisement.
+             In contrast to OSPFv2, the LSID has no
+             addressing semantics."
+         ::= { ospfv3AreaLsdbEntry 4 }
+
+ -- Note that the OSPF sequence number is a 32-bit signed
+ -- integer.  It starts with the value '80000001'h
+ -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h.
+ -- Thus, a typical sequence number will be very negative.
+
+ ospfv3AreaLsdbSequence OBJECT-TYPE
+         SYNTAX          Ospfv3LsaSequenceTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The sequence number field is a signed 32-bit
+             integer.  It is used to detect old and
+             duplicate link state advertisements.  The space
+             of sequence numbers is linearly ordered.  The
+             larger the sequence number, the more recent the
+             advertisement."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.6, LS sequence
+             number"
+         ::= { ospfv3AreaLsdbEntry 5 }
+
+ ospfv3AreaLsdbAge OBJECT-TYPE
+         SYNTAX          Ospfv3LsaAgeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the age of the link state
+             advertisement in seconds.  The high-order bit
+             of the LS age field is considered the DoNotAge
+             bit for support of on-demand circuits."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.1, LS age;
+              Extending OSPF to Support Demand Circuits,
+              Section 2.2, The LS age field."
+         ::= { ospfv3AreaLsdbEntry 6 }
+
+ ospfv3AreaLsdbChecksum OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the checksum of the complete
+             contents of the advertisement, excepting the
+             age field.  The age field is excepted so that
+             an advertisement's age can be incremented
+             without updating the checksum.  The checksum
+             used is the same that is used for ISO
+             connectionless datagrams; it is commonly
+             referred to as the Fletcher checksum."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.7, LS checksum"
+         ::= { ospfv3AreaLsdbEntry 7 }
+
+ ospfv3AreaLsdbAdvertisement OBJECT-TYPE
+         SYNTAX          OCTET STRING (SIZE (1..65535))
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The entire link state advertisement, including
+             its header."
+         ::= { ospfv3AreaLsdbEntry 8 }
+
+ ospfv3AreaLsdbTypeKnown OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The value true (1) indicates that the LSA type is
+             recognized by this router."
+         ::= { ospfv3AreaLsdbEntry 9 }
+
+ -- OSPFv3 Link-Scope Link State Database, for non-virtual interfaces
+
+ ospfv3LinkLsdbTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3LinkLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Process's Link-scope LSDB for non-virtual
+             interfaces.  The LSDB contains the Link-scope link
+             state advertisements from the interfaces that the
+             device is attached to."
+         ::= { ospfv3Objects 5 }
+
+ ospfv3LinkLsdbEntry OBJECT-TYPE
+         SYNTAX          Ospfv3LinkLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A single Link-scope link state advertisement."
+         INDEX           { ospfv3LinkLsdbIfIndex,
+                           ospfv3LinkLsdbIfInstId,
+                           ospfv3LinkLsdbType,
+                           ospfv3LinkLsdbRouterId,
+                           ospfv3LinkLsdbLsid }
+         ::= { ospfv3LinkLsdbTable 1 }
+
+ Ospfv3LinkLsdbEntry ::= SEQUENCE {
+         ospfv3LinkLsdbIfIndex
+                 InterfaceIndex,
+         ospfv3LinkLsdbIfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3LinkLsdbType
+                 Unsigned32,
+         ospfv3LinkLsdbRouterId
+                 Ospfv3RouterIdTC,
+         ospfv3LinkLsdbLsid
+                 Ospfv3LsIdTC,
+         ospfv3LinkLsdbSequence
+                 Ospfv3LsaSequenceTC,
+         ospfv3LinkLsdbAge
+                 Ospfv3LsaAgeTC,
+         ospfv3LinkLsdbChecksum
+                 Integer32,
+         ospfv3LinkLsdbAdvertisement
+                 OCTET STRING,
+         ospfv3LinkLsdbTypeKnown
+                 TruthValue
+         }
+
+ ospfv3LinkLsdbIfIndex OBJECT-TYPE
+         SYNTAX         InterfaceIndex
+         MAX-ACCESS     not-accessible
+         STATUS         current
+         DESCRIPTION
+             "The identifier of the link from which the LSA
+             was received."
+         ::= { ospfv3LinkLsdbEntry 1 }
+
+ ospfv3LinkLsdbIfInstId OBJECT-TYPE
+         SYNTAX         Ospfv3IfInstIdTC
+         MAX-ACCESS     not-accessible
+         STATUS         current
+         DESCRIPTION
+             "The identifier of the interface instance from
+             which the LSA was received."
+         ::= { ospfv3LinkLsdbEntry 2 }
+
+ ospfv3LinkLsdbType OBJECT-TYPE
+         SYNTAX          Unsigned32(0..'FFFFFFFF'h)
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The type of the link state advertisement.
+             Each link state type has a separate
+             advertisement format.  Link-scope LSAs unrecognized
+             by the router are also stored in this database."
+         ::= { ospfv3LinkLsdbEntry 3 }
+
+ ospfv3LinkLsdbRouterId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit number that uniquely identifies the
+             originating router in the Autonomous System."
+         REFERENCE
+             "OSPF Version 2, Appendix C.1, Global parameters"
+         ::= { ospfv3LinkLsdbEntry 4 }
+
+ ospfv3LinkLsdbLsid OBJECT-TYPE
+         SYNTAX        Ospfv3LsIdTC
+         MAX-ACCESS    not-accessible
+         STATUS        current
+         DESCRIPTION
+             "The Link State ID is an LS type-specific field
+             containing a unique identifier;
+             it identifies the piece of the routing domain
+             that is being described by the advertisement.
+             In contrast to OSPFv2, the LSID has no
+             addressing semantics.  However, in OSPFv3
+             the Link State ID always contains the flooding
+             scope of the LSA."
+         ::= { ospfv3LinkLsdbEntry 5 }
+
+ -- Note that the OSPF sequence number is a 32-bit signed
+ -- integer.  It starts with the value '80000001'h
+ -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h.
+ -- Thus, a typical sequence number will be very negative.
+
+ ospfv3LinkLsdbSequence OBJECT-TYPE
+         SYNTAX          Ospfv3LsaSequenceTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The sequence number field is a signed 32-bit
+             integer.  It is used to detect old and duplicate
+             link state advertisements.  The space of
+             sequence numbers is linearly ordered.  The
+             larger the sequence number, the more recent the
+             advertisement."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.6, LS sequence
+             number"
+         ::= { ospfv3LinkLsdbEntry 6 }
+
+ ospfv3LinkLsdbAge OBJECT-TYPE
+         SYNTAX          Ospfv3LsaAgeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the age of the link state
+             advertisement in seconds.  The high-order bit
+             of the LS age field is considered the DoNotAge
+             bit for support of on-demand circuits."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.1, LS age;
+              Extending OSPF to Support Demand Circuits,
+              Section 2.2, The LS age field."
+         ::= { ospfv3LinkLsdbEntry 7 }
+
+ ospfv3LinkLsdbChecksum OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the checksum of the complete
+             contents of the advertisement, excepting the
+             age field.  The age field is excepted so that
+             an advertisement's age can be incremented
+             without updating the checksum.  The checksum
+             used is the same that is used for ISO
+             connectionless datagrams; it is commonly
+             referred to as the Fletcher checksum."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.7, LS checksum"
+         ::= { ospfv3LinkLsdbEntry 8 }
+
+ ospfv3LinkLsdbAdvertisement OBJECT-TYPE
+         SYNTAX          OCTET STRING (SIZE (1..65535))
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The entire link state advertisement, including
+             its header."
+         ::= { ospfv3LinkLsdbEntry 9 }
+
+ ospfv3LinkLsdbTypeKnown OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The value true (1) indicates that the LSA type is
+             recognized by this router."
+         ::= { ospfv3LinkLsdbEntry 10 }
+
+ -- OSPF Host Table
+
+ ospfv3HostTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3HostEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Host/Metric Table indicates what hosts are
+             directly attached to the router and their
+             corresponding metrics."
+         REFERENCE
+             "OSPF Version 2, Appendix C.7, Host route
+             parameters"
+         ::= { ospfv3Objects 6 }
+
+ ospfv3HostEntry OBJECT-TYPE
+         SYNTAX          Ospfv3HostEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A metric to be advertised when a given host is
+             reachable.
+
+             The information in this table is persistent, and
+             when written, the entity SHOULD save the change
+             to non-volatile storage."
+         INDEX           { ospfv3HostAddressType,
+                           ospfv3HostAddress }
+         ::= { ospfv3HostTable 1 }
+
+ Ospfv3HostEntry ::= SEQUENCE {
+         ospfv3HostAddressType
+                 InetAddressType,
+         ospfv3HostAddress
+                 InetAddress,
+         ospfv3HostMetric
+                 Metric,
+         ospfv3HostRowStatus
+                 RowStatus,
+         ospfv3HostAreaID
+                 Ospfv3AreaIdTC
+         }
+
+ ospfv3HostAddressType OBJECT-TYPE
+         SYNTAX          InetAddressType
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The address type of ospfv3HostAddress.  Only IPv6
+             global address type is expected."
+         REFERENCE
+             "OSPF Version 2, Appendix C.7, Host route
+             parameters"
+         ::= { ospfv3HostEntry 1 }
+
+ ospfv3HostAddress OBJECT-TYPE
+         SYNTAX          InetAddress
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The IPv6 address of the host.  Must be an
+             IPv6 global address."
+         REFERENCE
+             "OSPF Version 2, Appendix C.7, Host route
+             parameters"
+         ::= { ospfv3HostEntry 2 }
+
+ ospfv3HostMetric OBJECT-TYPE
+         SYNTAX          Metric
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The metric to be advertised."
+         REFERENCE
+             "OSPF Version 2, Appendix C.7, Host route
+             parameters"
+         ::= { ospfv3HostEntry 3 }
+
+ ospfv3HostRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3HostEntry 4 }
+
+ ospfv3HostAreaID OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The Area the host entry is to be found within.
+             By default, the area for the subsuming OSPFv3
+             interface, or Area 0 if there is no subsuming
+             interface."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3HostEntry 5 }
+
+ -- OSPFv3 Interface Table
+
+ ospfv3IfTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3IfEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Interface Table describes the
+             interfaces from the viewpoint of OSPFv3."
+         REFERENCE
+             "OSPF for IPv6, Appendix C.3, Router Interface
+             Parameters"
+         ::= { ospfv3Objects 7 }
+
+ ospfv3IfEntry OBJECT-TYPE
+         SYNTAX          Ospfv3IfEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Interface Entry describes one
+             interface from the viewpoint of OSPFv3.
+
+             The information in this table is persistent,
+             and when written, the entity SHOULD save the
+             change to non-volatile storage."
+         INDEX           { ospfv3IfIndex,
+                           ospfv3IfInstId }
+         ::= { ospfv3IfTable 1 }
+
+ Ospfv3IfEntry ::= SEQUENCE {
+         ospfv3IfIndex
+                 InterfaceIndex,
+         ospfv3IfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3IfAreaId
+                 Ospfv3AreaIdTC,
+         ospfv3IfType
+                 INTEGER,
+         ospfv3IfAdminStatus
+                 Status,
+         ospfv3IfRtrPriority
+                 DesignatedRouterPriority,
+         ospfv3IfTransitDelay
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3IfRetransInterval
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3IfHelloInterval
+                 HelloRange,
+         ospfv3IfRtrDeadInterval
+                  Ospfv3DeadIntervalRangeTC,
+         ospfv3IfPollInterval
+                 Unsigned32,
+         ospfv3IfState
+                 INTEGER,
+         ospfv3IfDesignatedRouter
+                 Ospfv3RouterIdTC,
+         ospfv3IfBackupDesignatedRouter
+                 Ospfv3RouterIdTC,
+         ospfv3IfEvents
+                 Counter32,
+         ospfv3IfRowStatus
+                 RowStatus,
+         ospfv3IfDemand
+                 TruthValue,
+         ospfv3IfMetricValue
+                 Metric,
+         ospfv3IfLinkScopeLsaCount
+                 Gauge32,
+         ospfv3IfLinkLsaCksumSum
+                 Unsigned32,
+         ospfv3IfDemandNbrProbe
+                 TruthValue,
+         ospfv3IfDemandNbrProbeRetransLimit
+                 Unsigned32,
+         ospfv3IfDemandNbrProbeInterval
+                 Unsigned32,
+         ospfv3IfTEDisabled
+                 TruthValue,
+         ospfv3IfLinkLSASuppression
+                 TruthValue
+         }
+
+ ospfv3IfIndex OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The interface index of this OSPFv3 interface.
+             It corresponds to the interface index of the
+             IPv6 interface on which OSPFv3 is configured."
+         ::= { ospfv3IfEntry 1 }
+
+ ospfv3IfInstId OBJECT-TYPE
+         SYNTAX          Ospfv3IfInstIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Enables multiple interface instances of OSPFv3
+             to be run over a single link.  Each interface
+             instance would be assigned a separate ID.  This ID
+             has local link significance only."
+         ::= { ospfv3IfEntry 2 }
+
+ ospfv3IfAreaId OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "A 32-bit integer uniquely identifying the area
+             to which the interface connects.  Area ID
+             0 is used for the OSPFv3 backbone."
+         DEFVAL          { 0 }
+         ::= { ospfv3IfEntry 3 }
+
+ ospfv3IfType OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         broadcast(1),
+                         nbma(2),
+                         pointToPoint(3),
+                         pointToMultipoint(5)
+                         }
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 interface type."
+         ::= { ospfv3IfEntry 4 }
+
+ ospfv3IfAdminStatus OBJECT-TYPE
+         SYNTAX          Status
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 interface's administrative status.
+             The value formed on the interface; the interface
+             will be advertised as an internal route to some
+             area.  The value 'disabled' denotes that the
+             interface is external to OSPFv3.
+
+             Note that a value of 'disabled' for the object
+             ospfv3AdminStatus will override a value of
+             'enabled' for the interface."
+         DEFVAL          { enabled }
+         ::= { ospfv3IfEntry 5 }
+
+ ospfv3IfRtrPriority OBJECT-TYPE
+         SYNTAX          DesignatedRouterPriority
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The priority of this interface.  Used in
+             multi-access networks, this field is used in
+             the designated-router election algorithm.  The
+             value 0 signifies that the router is not
+             eligible to become the Designated Router on this
+             particular network.  In the event of a tie in
+             this value, routers will use their Router ID as
+             a tie breaker."
+         DEFVAL          { 1 }
+         ::= { ospfv3IfEntry 6 }
+
+ ospfv3IfTransitDelay OBJECT-TYPE
+         SYNTAX          Ospfv3UpToRefreshIntervalTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The estimated number of seconds it takes to transmit
+             a Link State Update packet over this interface.  LSAs
+             contained in the update packet must have their age
+             incremented by this amount before transmission.  This
+             value should take into account the transmission and
+             propagation delays of the interface."
+         REFERENCE
+             "OSPF for IPv6, Appendix C.3, Router Interface
+             Parameters."
+         DEFVAL          { 1 }
+         ::= { ospfv3IfEntry 7 }
+
+ ospfv3IfRetransInterval OBJECT-TYPE
+         SYNTAX          Ospfv3UpToRefreshIntervalTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The number of seconds between link state
+             advertisement retransmissions for adjacencies
+
+             belonging to this interface.  This value is
+             also used when retransmitting database
+             description and Link State Request packets."
+         DEFVAL          { 5 }
+         ::= { ospfv3IfEntry 8 }
+
+ ospfv3IfHelloInterval OBJECT-TYPE
+         SYNTAX          HelloRange
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The length of time, in seconds, between the
+             Hello packets that the router sends on the
+             interface.  This value must be the same for all
+             routers attached to a common network."
+         DEFVAL          { 10 }
+         ::= { ospfv3IfEntry 9 }
+
+ ospfv3IfRtrDeadInterval OBJECT-TYPE
+         SYNTAX          Ospfv3DeadIntervalRangeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The number of seconds that a router's Hello
+             packets have not been seen before its
+             neighbors declare the router down on the interface.
+             This should be some multiple of the Hello interval.
+             This value must be the same for all routers attached
+             to a common network."
+         DEFVAL          { 40 }
+         ::= { ospfv3IfEntry 10 }
+
+ ospfv3IfPollInterval OBJECT-TYPE
+         SYNTAX          Unsigned32
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The larger time interval, in seconds, between
+             the Hello packets sent to an inactive,
+             non-broadcast multi-access neighbor."
+         DEFVAL          { 120 }
+         ::= { ospfv3IfEntry 11 }
+
+ ospfv3IfState OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         down(1),
+                         loopback(2),
+                         waiting(3),
+                         pointToPoint(4),
+                         designatedRouter(5),
+                         backupDesignatedRouter(6),
+                         otherDesignatedRouter(7),
+                         standby(8)
+                         }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 interface state.  An interface may be
+             in standby state if there are multiple interfaces
+             on the link and another interface is active.  The
+             interface may be in Down state if the underlying
+             IPv6 interface is down or if the admin status is
+             'disabled' either globally or for the interface."
+         ::= { ospfv3IfEntry 12 }
+
+ ospfv3IfDesignatedRouter OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The Router ID of the Designated Router."
+         ::= { ospfv3IfEntry 13 }
+
+ ospfv3IfBackupDesignatedRouter OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The Router ID of the Backup Designated
+             Router."
+         ::= { ospfv3IfEntry 14 }
+
+ ospfv3IfEvents OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of times this OSPFv3 interface has
+             changed its state or an error has occurred.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3IfEntry 15 }
+
+  ospfv3IfRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3IfEntry 16 }
+
+ ospfv3IfDemand OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "Indicates whether Demand OSPFv3 procedures
+             (Hello suppression to FULL neighbors and
+             setting the DoNotAge flag on propagated LSAs)
+             should be performed on this interface."
+         DEFVAL { false }
+         ::= { ospfv3IfEntry 17 }
+
+ ospfv3IfMetricValue OBJECT-TYPE
+         SYNTAX          Metric
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The metric assigned to this interface.
+              The default value of the metric is
+              'Reference Bandwidth / ifSpeed'.  The value
+              of the reference bandwidth can be set
+              in the ospfv3ReferenceBandwidth object."
+         ::= { ospfv3IfEntry 18 }
+
+  ospfv3IfLinkScopeLsaCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The total number of Link-scope link state
+             advertisements in this link's link state
+             database."
+         ::= { ospfv3IfEntry 19 }
+
+  ospfv3IfLinkLsaCksumSum OBJECT-TYPE
+         SYNTAX          Unsigned32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit unsigned sum of the Link-scope link state
+             advertisements' LS checksums contained in this
+             link's link state database.  The sum can be used
+             to determine if there has been a change in a
+             router's link state database or to compare the
+             link state database of two routers."
+         ::= { ospfv3IfEntry 20 }
+
+ ospfv3IfDemandNbrProbe OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+                "Indicates whether or not neighbor probing is
+                enabled to determine whether or not the neighbor
+                is inactive.  Neighbor probing is disabled by
+                default."
+         DEFVAL { false }
+         ::= { ospfv3IfEntry 21 }
+
+ospfv3IfDemandNbrProbeRetransLimit OBJECT-TYPE
+        SYNTAX       Unsigned32
+        MAX-ACCESS   read-create
+        STATUS       current
+        DESCRIPTION
+           "The number of consecutive LSA retransmissions before
+           the neighbor is deemed inactive and the neighbor
+           adjacency is brought down."
+        DEFVAL          { 10 }
+        ::= { ospfv3IfEntry 22}
+
+ospfv3IfDemandNbrProbeInterval OBJECT-TYPE
+        SYNTAX       Unsigned32
+        UNITS        "seconds"
+        MAX-ACCESS   read-create
+        STATUS       current
+        DESCRIPTION
+           "Defines how often the neighbor will be probed."
+        DEFVAL          { 120 }
+        ::= { ospfv3IfEntry 23 }
+
+ ospfv3IfTEDisabled OBJECT-TYPE
+        SYNTAX          TruthValue
+        MAX-ACCESS      read-create
+        STATUS          current
+        DESCRIPTION
+           "Indicates whether or not traffic engineering
+           is disabled on the interface when traffic
+           engineering is enabled in the area where the
+           interface is attached.  The object is set
+           to the value true (1) to disable traffic engineering
+           on the interface.  Traffic engineering is enabled
+           by default on the interface when traffic engineering
+           is enabled in the area where the interface is
+           attached."
+        DEFVAL { false }
+        ::= { ospfv3IfEntry 24 }
+
+ ospfv3IfLinkLSASuppression OBJECT-TYPE
+        SYNTAX          TruthValue
+        MAX-ACCESS      read-create
+        STATUS          current
+        DESCRIPTION
+           "Specifies whether or not link LSA origination is
+           suppressed for broadcast or NBMA interface types.
+           The object is set to value true (1) to suppress
+           the origination."
+        REFERENCE
+             "OSPF for IPv6, Appendix C.3, Router Interface
+                 Parameters"
+        DEFVAL { false }
+        ::= { ospfv3IfEntry 25 }
+
+ -- OSPFv3 Virtual Interface Table
+
+ ospfv3VirtIfTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3VirtIfEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Information about this router's virtual
+             interfaces that the OSPFv3 Process is configured
+             to carry on."
+         REFERENCE
+             "OSPF for IPv6, Appendix C.4, Virtual Link
+             Parameters"
+         ::= { ospfv3Objects 8 }
+
+ ospfv3VirtIfEntry OBJECT-TYPE
+         SYNTAX          Ospfv3VirtIfEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Information about a single virtual interface.
+
+             The information in this table is persistent,
+             and when written, the entity SHOULD save the
+             change to non-volatile storage."
+         INDEX           { ospfv3VirtIfAreaId,
+                           ospfv3VirtIfNeighbor }
+         ::= { ospfv3VirtIfTable 1 }
+
+ Ospfv3VirtIfEntry ::= SEQUENCE {
+         ospfv3VirtIfAreaId
+                 Ospfv3AreaIdTC,
+         ospfv3VirtIfNeighbor
+                 Ospfv3RouterIdTC,
+         ospfv3VirtIfIndex
+                 InterfaceIndex,
+         ospfv3VirtIfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3VirtIfTransitDelay
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3VirtIfRetransInterval
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3VirtIfHelloInterval
+                 HelloRange,
+         ospfv3VirtIfRtrDeadInterval
+                 Ospfv3DeadIntervalRangeTC,
+         ospfv3VirtIfState
+                 INTEGER,
+         ospfv3VirtIfEvents
+                 Counter32,
+         ospfv3VirtIfRowStatus
+                 RowStatus,
+         ospfv3VirtIfLinkScopeLsaCount
+                 Gauge32,
+         ospfv3VirtIfLinkLsaCksumSum
+                 Unsigned32
+         }
+
+ ospfv3VirtIfAreaId OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The transit area that the virtual link
+             traverses.  By definition, this is not
+             Area 0."
+         ::= { ospfv3VirtIfEntry 1 }
+
+ ospfv3VirtIfNeighbor OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Router ID of the virtual neighbor."
+         ::= { ospfv3VirtIfEntry 2 }
+
+ ospfv3VirtIfIndex OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The local interface index assigned by the
+             OSPFv3 Process to this OSPFv3 virtual interface.
+             It is advertised in Hellos sent over the virtual
+             link and in the router's router-LSAs."
+         ::= { ospfv3VirtIfEntry 3 }
+
+ ospfv3VirtIfInstId OBJECT-TYPE
+         SYNTAX          Ospfv3IfInstIdTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The local Interface Instance ID assigned by the
+             OSPFv3 Process to this OSPFv3 virtual interface."
+         ::= { ospfv3VirtIfEntry 4 }
+
+ ospfv3VirtIfTransitDelay OBJECT-TYPE
+         SYNTAX          Ospfv3UpToRefreshIntervalTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The estimated number of seconds it takes to
+             transmit a Link State Update packet over this
+             interface."
+         DEFVAL          { 1 }
+         ::= { ospfv3VirtIfEntry 5 }
+
+ ospfv3VirtIfRetransInterval OBJECT-TYPE
+         SYNTAX          Ospfv3UpToRefreshIntervalTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The number of seconds between link state
+             advertisement retransmissions for adjacencies
+             belonging to this interface.  This value is
+             also used when retransmitting database
+             description and Link State Request packets.  This
+             value should be well over the expected
+             round-trip time."
+         DEFVAL          { 5 }
+         ::= { ospfv3VirtIfEntry 6 }
+
+ ospfv3VirtIfHelloInterval OBJECT-TYPE
+         SYNTAX          HelloRange
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The length of time, in seconds, between the
+             Hello packets that the router sends on the
+             interface.  This value must be the same for the
+             virtual neighbor."
+         DEFVAL          { 10 }
+         ::= { ospfv3VirtIfEntry 7 }
+
+ ospfv3VirtIfRtrDeadInterval OBJECT-TYPE
+         SYNTAX          Ospfv3DeadIntervalRangeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The number of seconds that a router's Hello
+             packets have not been seen before its
+             neighbors declare the router down.  This should
+             be some multiple of the Hello interval.  This
+             value must be the same for the virtual
+             neighbor."
+         DEFVAL          { 60 }
+         ::= { ospfv3VirtIfEntry 8 }
+
+ ospfv3VirtIfState OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         down(1),
+                         pointToPoint(4)
+                         }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "OSPF virtual interface states.  The same encoding
+             as the ospfV3IfTable is used."
+         ::= { ospfv3VirtIfEntry 9 }
+
+ ospfv3VirtIfEvents OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of state changes or error events on
+             this virtual link.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3VirtIfEntry 10 }
+
+ ospfv3VirtIfRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3VirtIfEntry 11 }
+
+ ospfv3VirtIfLinkScopeLsaCount OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The total number of Link-scope link state
+             advertisements in this virtual link's link state
+             database."
+         ::= { ospfv3VirtIfEntry 12 }
+
+ ospfv3VirtIfLinkLsaCksumSum OBJECT-TYPE
+         SYNTAX          Unsigned32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit unsigned sum of the Link-scope link state
+             advertisements' LS checksums contained in this
+             virtual link's link state database.  The sum can be used
+             to determine if there has been a change in a
+             router's link state database or to compare the
+             link state database of two routers."
+         ::= { ospfv3VirtIfEntry 13 }
+
+ -- OSPFv3 Neighbor Table
+
+ ospfv3NbrTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3NbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A table describing all neighbors in the
+             locality of the OSPFv3 router."
+         REFERENCE
+             "OSPF Version 2, Section 10, The Neighbor Data
+             Structure"
+         ::= { ospfv3Objects 9 }
+
+ ospfv3NbrEntry OBJECT-TYPE
+         SYNTAX          Ospfv3NbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The information regarding a single neighbor."
+         REFERENCE
+             "OSPF Version 2, Section 10, The Neighbor Data
+             Structure"
+         INDEX           { ospfv3NbrIfIndex,
+                           ospfv3NbrIfInstId,
+                           ospfv3NbrRtrId }
+         ::= { ospfv3NbrTable 1 }
+
+ Ospfv3NbrEntry ::= SEQUENCE {
+         ospfv3NbrIfIndex
+                 InterfaceIndex,
+         ospfv3NbrIfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3NbrRtrId
+                 Ospfv3RouterIdTC,
+         ospfv3NbrAddressType
+                 InetAddressType,
+         ospfv3NbrAddress
+                 InetAddress,
+         ospfv3NbrOptions
+                 Integer32,
+         ospfv3NbrPriority
+                 DesignatedRouterPriority,
+         ospfv3NbrState
+                 INTEGER,
+         ospfv3NbrEvents
+                 Counter32,
+         ospfv3NbrLsRetransQLen
+                 Gauge32,
+         ospfv3NbrHelloSuppressed
+                 TruthValue,
+         ospfv3NbrIfId
+                 InterfaceIndex,
+         ospfv3NbrRestartHelperStatus
+                 INTEGER,
+         ospfv3NbrRestartHelperAge
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3NbrRestartHelperExitReason
+                 INTEGER
+         }
+
+ ospfv3NbrIfIndex OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Local Link ID of the link over which the
+              neighbor can be reached."
+         ::= { ospfv3NbrEntry 1 }
+
+ ospfv3NbrIfInstId OBJECT-TYPE
+         SYNTAX          Ospfv3IfInstIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Interface instance over which the neighbor
+             can be reached.  This ID has local link
+             significance only."
+         ::= { ospfv3NbrEntry 2 }
+
+ ospfv3NbrRtrId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A 32-bit unsigned integer uniquely identifying the
+             neighboring router in the Autonomous System."
+         ::= { ospfv3NbrEntry 3 }
+
+ ospfv3NbrAddressType OBJECT-TYPE
+         SYNTAX          InetAddressType
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The address type of ospfv3NbrAddress.  Only IPv6
+             addresses without zone index are expected."
+         ::= { ospfv3NbrEntry 4 }
+
+ ospfv3NbrAddress OBJECT-TYPE
+         SYNTAX          InetAddress
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The IPv6 address of the neighbor associated with
+             the local link."
+         ::= { ospfv3NbrEntry 5 }
+
+ ospfv3NbrOptions OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "A bit mask corresponding to the neighbor's
+             options field."
+         REFERENCE
+             "OSPF for IPv6, Appendix A.2, The Options Field"
+         ::= { ospfv3NbrEntry 6 }
+
+ ospfv3NbrPriority OBJECT-TYPE
+         SYNTAX          DesignatedRouterPriority
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The priority of this neighbor in the designated-
+             router election algorithm.  The value 0 signifies
+             that the neighbor is not eligible to become the
+             Designated Router on this particular network."
+         ::= { ospfv3NbrEntry 7 }
+
+ ospfv3NbrState OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         down(1),
+                         attempt(2),
+                         init(3),
+                         twoWay(4),
+                         exchangeStart(5),
+                         exchange(6),
+                         loading(7),
+                         full(8)
+                         }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The state of the relationship with this
+             neighbor."
+         REFERENCE
+             "OSPF Version 2, Section 10.1, Neighbor states"
+         ::= { ospfv3NbrEntry 8 }
+
+ ospfv3NbrEvents OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of times this neighbor relationship
+             has changed state or an error has occurred.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3NbrEntry 9 }
+
+ ospfv3NbrLsRetransQLen OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The current length of the retransmission
+             queue."
+         ::= { ospfv3NbrEntry 10 }
+
+ ospfv3NbrHelloSuppressed OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "Indicates whether Hellos are being suppressed
+             to the neighbor."
+         ::= { ospfv3NbrEntry 11 }
+
+ ospfv3NbrIfId OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The Interface ID that the neighbor advertises
+             in its Hello packets on this link, that is, the
+             neighbor's local interface index."
+         ::= { ospfv3NbrEntry 12 }
+
+ ospfv3NbrRestartHelperStatus OBJECT-TYPE
+        SYNTAX       INTEGER { notHelping(1),
+                               helping(2)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "Indicates whether the router is acting
+           as a graceful restart helper for the neighbor."
+           ::= { ospfv3NbrEntry 13 }
+
+ ospfv3NbrRestartHelperAge OBJECT-TYPE
+        SYNTAX       Ospfv3UpToRefreshIntervalTC
+        UNITS        "seconds"
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "Remaining time in current OSPF graceful restart
+           interval, if the router is acting as a restart
+           helper for the neighbor."
+        ::= { ospfv3NbrEntry 14 }
+
+ ospfv3NbrRestartHelperExitReason OBJECT-TYPE
+        SYNTAX       INTEGER { none(1),
+                               inProgress(2),
+                               completed(3),
+                               timedOut(4),
+                               topologyChanged(5)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+           "Describes the outcome of the last attempt at acting
+           as a graceful restart helper for the neighbor.
+
+           none:            no restart has yet been attempted.
+           inProgress:      a restart attempt is currently underway.
+           completed:       the last restart completed successfully.
+           timedOut:        the last restart timed out.
+           topologyChanged: the last restart was aborted due to
+                            a topology change."
+     ::= { ospfv3NbrEntry 15 }
+
+ -- OSPFv3 Configured Neighbor Table
+
+ ospfv3CfgNbrTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3CfgNbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A table describing all configured neighbors.
+
+             The Configured Neighbors table just gives
+             OSPFv3 information for sending OSPFv3 packets
+             to potential neighbors and is typically used
+             on NBMA and Point-to-Multipoint networks.
+             Once a Hello is received from a neighbor in
+             the Configured Neighbor table, an entry for
+             that neighbor is created in the Neighbor table
+             and adjacency state is maintained there.
+             Neighbors on multi-access or Point-to-Point
+             networks can use multicast addressing, so only
+             Neighbor table entries are created for them."
+         REFERENCE
+             "OSPF Version 2, Section 10, The Neighbor Data
+             Structure"
+         ::= { ospfv3Objects 10 }
+
+ ospfv3CfgNbrEntry OBJECT-TYPE
+         SYNTAX          Ospfv3CfgNbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The information regarding a single configured
+             neighbor.
+
+             The information in this table is persistent,
+             and when written, the entity SHOULD save the
+             change to non-volatile storage."
+         REFERENCE
+             "OSPF Version 2, Section 10, The Neighbor Data
+             Structure"
+         INDEX           { ospfv3CfgNbrIfIndex,
+                           ospfv3CfgNbrIfInstId,
+                           ospfv3CfgNbrAddressType,
+                           ospfv3CfgNbrAddress }
+         ::= { ospfv3CfgNbrTable 1 }
+
+ Ospfv3CfgNbrEntry ::= SEQUENCE {
+         ospfv3CfgNbrIfIndex
+                 InterfaceIndex,
+         ospfv3CfgNbrIfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3CfgNbrAddressType
+                 InetAddressType,
+         ospfv3CfgNbrAddress
+                 InetAddress,
+         ospfv3CfgNbrPriority
+                 DesignatedRouterPriority,
+         ospfv3CfgNbrRowStatus
+                 RowStatus
+         }
+
+ ospfv3CfgNbrIfIndex OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Local Link ID of the link over which the
+              neighbor can be reached."
+         ::= { ospfv3CfgNbrEntry 1 }
+
+ ospfv3CfgNbrIfInstId OBJECT-TYPE
+         SYNTAX          Ospfv3IfInstIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Interface instance over which the neighbor
+             can be reached.  This ID has local link
+             significance only."
+         ::= { ospfv3CfgNbrEntry 2 }
+
+ ospfv3CfgNbrAddressType OBJECT-TYPE
+         SYNTAX          InetAddressType
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The address type of ospfv3NbrAddress.  Only IPv6
+             addresses without zone index are expected."
+         ::= { ospfv3CfgNbrEntry 3 }
+
+ ospfv3CfgNbrAddress OBJECT-TYPE
+         SYNTAX          InetAddress
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The IPv6 address of the neighbor associated with
+             the local link."
+         ::= { ospfv3CfgNbrEntry 4 }
+
+ ospfv3CfgNbrPriority OBJECT-TYPE
+         SYNTAX          DesignatedRouterPriority
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "The priority of this neighbor in the designated-
+             router election algorithm.  The value 0 signifies
+             that the neighbor is not eligible to become the
+             Designated Router on this particular network."
+         DEFVAL          { 1 }
+         ::= { ospfv3CfgNbrEntry 5 }
+
+ ospfv3CfgNbrRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3CfgNbrEntry 6 }
+
+ -- OSPFv3 Virtual Neighbor Table
+
+ ospfv3VirtNbrTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3VirtNbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A table describing all virtual neighbors."
+         REFERENCE
+             "OSPF Version 2, Section 15, Virtual Links"
+         ::= { ospfv3Objects 11 }
+
+ ospfv3VirtNbrEntry OBJECT-TYPE
+         SYNTAX          Ospfv3VirtNbrEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "Virtual neighbor information."
+         INDEX           { ospfv3VirtNbrArea,
+                           ospfv3VirtNbrRtrId }
+         ::= { ospfv3VirtNbrTable 1 }
+
+ Ospfv3VirtNbrEntry ::= SEQUENCE {
+         ospfv3VirtNbrArea
+                 Ospfv3AreaIdTC,
+         ospfv3VirtNbrRtrId
+                 Ospfv3RouterIdTC,
+         ospfv3VirtNbrIfIndex
+                 InterfaceIndex,
+         ospfv3VirtNbrIfInstId
+                 Ospfv3IfInstIdTC,
+         ospfv3VirtNbrAddressType
+                 InetAddressType,
+         ospfv3VirtNbrAddress
+                 InetAddress,
+         ospfv3VirtNbrOptions
+                 Integer32,
+         ospfv3VirtNbrState
+                 INTEGER,
+         ospfv3VirtNbrEvents
+                 Counter32,
+         ospfv3VirtNbrLsRetransQLen
+                 Gauge32,
+         ospfv3VirtNbrHelloSuppressed
+                 TruthValue,
+         ospfv3VirtNbrIfId
+                 InterfaceIndex,
+         ospfv3VirtNbrRestartHelperStatus
+                 INTEGER,
+         ospfv3VirtNbrRestartHelperAge
+                 Ospfv3UpToRefreshIntervalTC,
+         ospfv3VirtNbrRestartHelperExitReason
+                 INTEGER
+         }
+
+ ospfv3VirtNbrArea OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The transit area Identifier."
+         ::= { ospfv3VirtNbrEntry 1 }
+
+ ospfv3VirtNbrRtrId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A 32-bit integer uniquely identifying the
+             neighboring router in the Autonomous System."
+         ::= { ospfv3VirtNbrEntry 2 }
+
+ ospfv3VirtNbrIfIndex OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The local Interface ID for the virtual link over
+             which the neighbor can be reached."
+         ::= { ospfv3VirtNbrEntry 3 }
+
+ ospfv3VirtNbrIfInstId OBJECT-TYPE
+         SYNTAX          Ospfv3IfInstIdTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The interface instance for the virtual link over
+             which the neighbor can be reached."
+         ::= { ospfv3VirtNbrEntry 4 }
+
+ ospfv3VirtNbrAddressType OBJECT-TYPE
+         SYNTAX          InetAddressType
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The address type of ospfv3VirtNbrAddress.  Only IPv6
+             addresses without zone index are expected."
+         ::= { ospfv3VirtNbrEntry 5 }
+
+ ospfv3VirtNbrAddress OBJECT-TYPE
+         SYNTAX          InetAddress
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The IPv6 address advertised by this virtual neighbor.
+             It must be a global scope address."
+         ::= { ospfv3VirtNbrEntry 6 }
+
+ ospfv3VirtNbrOptions OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "A bit mask corresponding to the neighbor's options
+             field."
+         REFERENCE
+             "OSPF for IPv6, Appendix A.2, The Options Field"
+         ::= { ospfv3VirtNbrEntry 7 }
+
+ ospfv3VirtNbrState OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         down(1),
+                         attempt(2),
+                         init(3),
+                         twoWay(4),
+                         exchangeStart(5),
+                         exchange(6),
+                         loading(7),
+                         full(8)
+                         }
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The state of the virtual neighbor relationship."
+         ::= { ospfv3VirtNbrEntry 8 }
+
+ ospfv3VirtNbrEvents OBJECT-TYPE
+         SYNTAX          Counter32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The number of times this virtual link has
+             changed its state or an error has occurred.
+
+             Discontinuities in the value of this counter
+             can occur at re-initialization of the management
+             system and at other times as indicated by the
+             value of ospfv3DiscontinuityTime."
+         ::= { ospfv3VirtNbrEntry 9 }
+
+ ospfv3VirtNbrLsRetransQLen OBJECT-TYPE
+         SYNTAX          Gauge32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The current length of the retransmission
+             queue."
+         ::= { ospfv3VirtNbrEntry 10 }
+
+ ospfv3VirtNbrHelloSuppressed OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "Indicates whether Hellos are being suppressed
+             to the neighbor."
+         ::= { ospfv3VirtNbrEntry 11 }
+
+ ospfv3VirtNbrIfId OBJECT-TYPE
+         SYNTAX          InterfaceIndex
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The Interface ID that the neighbor advertises
+             in its Hello packets on this virtual link, that is,
+             the neighbor's local Interface ID."
+         ::= { ospfv3VirtNbrEntry 12 }
+
+ospfv3VirtNbrRestartHelperStatus OBJECT-TYPE
+        SYNTAX       INTEGER { notHelping(1),
+                               helping(2)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+            "Indicates whether the router is acting
+            as a graceful restart helper for the neighbor."
+           ::= { ospfv3VirtNbrEntry 13 }
+
+ ospfv3VirtNbrRestartHelperAge OBJECT-TYPE
+        SYNTAX       Ospfv3UpToRefreshIntervalTC
+        UNITS        "seconds"
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+            "Remaining time in the current OSPF graceful restart
+            interval, if the router is acting as a restart
+            helper for the neighbor."
+        ::= { ospfv3VirtNbrEntry 14 }
+
+ ospfv3VirtNbrRestartHelperExitReason OBJECT-TYPE
+        SYNTAX       INTEGER { none(1),
+                               inProgress(2),
+                               completed(3),
+                               timedOut(4),
+                               topologyChanged(5)
+                             }
+        MAX-ACCESS   read-only
+        STATUS       current
+        DESCRIPTION
+            "Describes the outcome of the last attempt at acting
+            as a graceful restart helper for the neighbor.
+
+            none:            no restart has yet been attempted.
+            inProgress:      a restart attempt is currently underway.
+            completed:       the last restart completed successfully.
+            timedOut:        the last restart timed out.
+            topologyChanged: the last restart was aborted due to
+                             a topology change."
+     ::= { ospfv3VirtNbrEntry 15 }
+
+ --
+ -- The OSPFv3 Area Aggregate Table
+ --
+
+ ospfv3AreaAggregateTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3AreaAggregateEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Area Aggregate Table acts as an adjunct
+             to the Area Table.  It describes those address
+             aggregates that are configured to be propagated
+             from an area.  Its purpose is to reduce the amount
+             of information that is known beyond an area's
+             borders.
+
+             A range of IPv6 prefixes specified by a
+             prefix / prefix length pair.  Note that if
+             ranges are configured such that one range
+             subsumes another range, the most specific
+             match is the preferred one."
+         ::= { ospfv3Objects 12 }
+
+ ospfv3AreaAggregateEntry OBJECT-TYPE
+         SYNTAX          Ospfv3AreaAggregateEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A single area aggregate entry.
+
+             Information in this table is persistent, and
+             when this object is written, the entity SHOULD
+             save the change to non-volatile storage."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         INDEX           { ospfv3AreaAggregateAreaID,
+                           ospfv3AreaAggregateAreaLsdbType,
+                           ospfv3AreaAggregatePrefixType,
+                           ospfv3AreaAggregatePrefix,
+                           ospfv3AreaAggregatePrefixLength }
+         ::= { ospfv3AreaAggregateTable 1 }
+
+ Ospfv3AreaAggregateEntry ::= SEQUENCE {
+         ospfv3AreaAggregateAreaID
+                 Ospfv3AreaIdTC,
+         ospfv3AreaAggregateAreaLsdbType
+                 INTEGER,
+         ospfv3AreaAggregatePrefixType
+                 InetAddressType,
+         ospfv3AreaAggregatePrefix
+                 InetAddress,
+         ospfv3AreaAggregatePrefixLength
+                 InetAddressPrefixLength,
+         ospfv3AreaAggregateRowStatus
+                 RowStatus,
+         ospfv3AreaAggregateEffect
+                 INTEGER,
+         ospfv3AreaAggregateRouteTag
+                 Unsigned32
+         }
+
+ ospfv3AreaAggregateAreaID OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The area the Address Aggregate is to be found
+             within."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3AreaAggregateEntry 1 }
+
+ ospfv3AreaAggregateAreaLsdbType OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         interAreaPrefixLsa(8195), -- 0x2003
+                         nssaExternalLsa(8199)     -- 0x2007
+                         }
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The type of the Address Aggregate.  This field
+             specifies the Area LSDB type that this Address
+             Aggregate applies to."
+         REFERENCE
+             "OSPF Version 2, Appendix A.4.1, The LSA header"
+         ::= { ospfv3AreaAggregateEntry 2 }
+
+ ospfv3AreaAggregatePrefixType OBJECT-TYPE
+         SYNTAX          InetAddressType
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The prefix type of ospfv3AreaAggregatePrefix.  Only
+             IPv6 addresses are expected."
+         ::= { ospfv3AreaAggregateEntry 3 }
+
+ ospfv3AreaAggregatePrefix OBJECT-TYPE
+         SYNTAX          InetAddress (SIZE (0..16))
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The IPv6 prefix."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3AreaAggregateEntry 4 }
+
+ ospfv3AreaAggregatePrefixLength OBJECT-TYPE
+         SYNTAX          InetAddressPrefixLength (3..128)
+         UNITS           "bits"
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The length of the prefix (in bits).  A prefix can
+             not be shorter than 3 bits."
+         REFERENCE
+             "OSPF Version 2, Appendix C.2, Area parameters"
+         ::= { ospfv3AreaAggregateEntry 5 }
+
+ ospfv3AreaAggregateRowStatus OBJECT-TYPE
+         SYNTAX          RowStatus
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This object permits management of the table by
+             facilitating actions such as row creation,
+             construction, and destruction.
+
+             The value of this object has no effect on
+             whether other objects in this conceptual row can be
+             modified."
+         ::= { ospfv3AreaAggregateEntry 6 }
+
+ ospfv3AreaAggregateEffect OBJECT-TYPE
+         SYNTAX          INTEGER {
+                         advertiseMatching(1),
+                         doNotAdvertiseMatching(2)
+                         }
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "Prefixes subsumed by ranges will either trigger the
+             advertisement of the indicated aggregate
+             (advertiseMatching) or result in the prefix not
+             being advertised at all outside the area."
+         DEFVAL          { advertiseMatching }
+         ::= { ospfv3AreaAggregateEntry 7 }
+
+ ospfv3AreaAggregateRouteTag OBJECT-TYPE
+         SYNTAX          Unsigned32
+         MAX-ACCESS      read-create
+         STATUS          current
+         DESCRIPTION
+             "This tag is advertised only in the summarized
+             As-External LSA when summarizing from NSSA-LSAs to
+             AS-External-LSAs."
+         DEFVAL         { 0 }
+         ::= { ospfv3AreaAggregateEntry 8 }
+
+ -- OSPFv3 Link-Scope Link State Database, for virtual interfaces
+
+ ospfv3VirtLinkLsdbTable OBJECT-TYPE
+         SYNTAX          SEQUENCE OF Ospfv3VirtLinkLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The OSPFv3 Process's Link-scope LSDB for virtual
+             interfaces.  The LSDB contains the Link-scope link
+             state advertisements from virtual interfaces."
+         ::= { ospfv3Objects 13 }
+
+ ospfv3VirtLinkLsdbEntry OBJECT-TYPE
+         SYNTAX          Ospfv3VirtLinkLsdbEntry
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "A single Link-scope link state advertisement
+             for a virtual interface."
+         INDEX           { ospfv3VirtLinkLsdbIfAreaId,
+                           ospfv3VirtLinkLsdbIfNeighbor,
+                           ospfv3VirtLinkLsdbType,
+                           ospfv3VirtLinkLsdbRouterId,
+                           ospfv3VirtLinkLsdbLsid }
+         ::= { ospfv3VirtLinkLsdbTable 1 }
+
+ Ospfv3VirtLinkLsdbEntry ::= SEQUENCE {
+         ospfv3VirtLinkLsdbIfAreaId
+                 Ospfv3AreaIdTC,
+         ospfv3VirtLinkLsdbIfNeighbor
+                 Ospfv3RouterIdTC,
+         ospfv3VirtLinkLsdbType
+                 Unsigned32,
+         ospfv3VirtLinkLsdbRouterId
+                 Ospfv3RouterIdTC,
+         ospfv3VirtLinkLsdbLsid
+                 Ospfv3LsIdTC,
+         ospfv3VirtLinkLsdbSequence
+                 Ospfv3LsaSequenceTC,
+         ospfv3VirtLinkLsdbAge
+                 Ospfv3LsaAgeTC,
+         ospfv3VirtLinkLsdbChecksum
+                 Integer32,
+         ospfv3VirtLinkLsdbAdvertisement
+                 OCTET STRING,
+         ospfv3VirtLinkLsdbTypeKnown
+                 TruthValue
+         }
+
+ ospfv3VirtLinkLsdbIfAreaId OBJECT-TYPE
+         SYNTAX          Ospfv3AreaIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The transit area that the virtual link
+             traverses.  By definition, this is not
+             Area 0."
+         ::= { ospfv3VirtLinkLsdbEntry 1 }
+
+ ospfv3VirtLinkLsdbIfNeighbor OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The Router ID of the virtual neighbor."
+         ::= { ospfv3VirtLinkLsdbEntry 2 }
+
+ ospfv3VirtLinkLsdbType OBJECT-TYPE
+         SYNTAX          Unsigned32(0..'FFFFFFFF'h)
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The type of the link state advertisement.
+             Each link state type has a separate
+             advertisement format.  Link-scope LSAs unrecognized
+             by the router are also stored in this database."
+         ::= { ospfv3VirtLinkLsdbEntry 3 }
+
+ ospfv3VirtLinkLsdbRouterId OBJECT-TYPE
+         SYNTAX          Ospfv3RouterIdTC
+         MAX-ACCESS      not-accessible
+         STATUS          current
+         DESCRIPTION
+             "The 32-bit number that uniquely identifies the
+             originating router in the Autonomous System."
+         REFERENCE
+             "OSPF Version 2, Appendix C.1, Global parameters"
+         ::= { ospfv3VirtLinkLsdbEntry 4 }
+
+ ospfv3VirtLinkLsdbLsid OBJECT-TYPE
+         SYNTAX        Ospfv3LsIdTC
+         MAX-ACCESS    not-accessible
+         STATUS        current
+         DESCRIPTION
+             "The Link State ID is an LS type-specific field
+             containing a unique identifier;
+             it identifies the piece of the routing domain
+             that is being described by the advertisement.
+             In contrast to OSPFv2, the LSID has no
+             addressing semantics."
+         ::= { ospfv3VirtLinkLsdbEntry 5 }
+
+ -- Note that the OSPF sequence number is a 32-bit signed
+ -- integer.  It starts with the value '80000001'h
+ -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h.
+ -- Thus, a typical sequence number will be very negative.
+
+ ospfv3VirtLinkLsdbSequence OBJECT-TYPE
+         SYNTAX          Ospfv3LsaSequenceTC
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The sequence number field is a signed 32-bit
+             integer.  It is used to detect old and duplicate
+             link state advertisements.  The space of
+             sequence numbers is linearly ordered.  The
+             larger the sequence number, the more recent the
+             advertisement."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.6, LS sequence
+             number"
+         ::= { ospfv3VirtLinkLsdbEntry 6 }
+
+ ospfv3VirtLinkLsdbAge OBJECT-TYPE
+         SYNTAX          Ospfv3LsaAgeTC
+         UNITS           "seconds"
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the age of the link state
+             advertisement in seconds.  The high-order bit
+             of the LS age field is considered the DoNotAge
+             bit for support of on-demand circuits."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.1, LS age;
+              Extending OSPF to Support Demand Circuits,
+              Section 2.2, The LS age field."
+         ::= { ospfv3VirtLinkLsdbEntry 7 }
+
+ ospfv3VirtLinkLsdbChecksum OBJECT-TYPE
+         SYNTAX          Integer32
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "This field is the checksum of the complete
+             contents of the advertisement, excepting the
+             age field.  The age field is excepted so that
+             an advertisement's age can be incremented
+             without updating the checksum.  The checksum
+             used is the same that is used for ISO
+             connectionless datagrams; it is commonly
+             referred to as the Fletcher checksum."
+         REFERENCE
+             "OSPF Version 2, Section 12.1.7, LS checksum"
+         ::= { ospfv3VirtLinkLsdbEntry 8 }
+
+ ospfv3VirtLinkLsdbAdvertisement OBJECT-TYPE
+         SYNTAX          OCTET STRING (SIZE (1..65535))
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The entire link state advertisement, including
+             its header."
+         ::= { ospfv3VirtLinkLsdbEntry 9 }
+
+ ospfv3VirtLinkLsdbTypeKnown OBJECT-TYPE
+         SYNTAX          TruthValue
+         MAX-ACCESS      read-only
+         STATUS          current
+         DESCRIPTION
+             "The value true (1) indicates that the LSA type is
+             recognized by this router."
+         ::= { ospfv3VirtLinkLsdbEntry 10 }
+
+ -- The Ospfv3 Notification Table
+
+ -- The Ospfv3 Notification Table records fields that are
+ -- required for notifications.
+
+ ospfv3NotificationEntry OBJECT IDENTIFIER
+         ::= { ospfv3Objects 14 }
+
+ ospfv3ConfigErrorType OBJECT-TYPE
+     SYNTAX       INTEGER {
+                     badVersion(1),
+                     areaMismatch(2),
+                     unknownNbmaNbr(3), -- Router is DR eligible
+                     unknownVirtualNbr(4),
+                     helloIntervalMismatch(5),
+                     deadIntervalMismatch(6),
+                     optionMismatch(7),
+                     mtuMismatch(8),
+                     duplicateRouterId(9),
+                     noError(10) }
+     MAX-ACCESS   accessible-for-notify
+     STATUS   current
+     DESCRIPTION
+         "Potential types of configuration conflicts.
+         Used by the ospfv3ConfigError and
+         ospfv3ConfigVirtError notifications."
+     ::= { ospfv3NotificationEntry 1 }
+
+ ospfv3PacketType OBJECT-TYPE
+     SYNTAX       INTEGER {
+                     hello(1),
+                     dbDescript(2),
+                     lsReq(3),
+                     lsUpdate(4),
+                     lsAck(5),
+                     nullPacket(6) }
+     MAX-ACCESS   accessible-for-notify
+     STATUS       current
+     DESCRIPTION
+         "OSPFv3 packet types."
+     ::= { ospfv3NotificationEntry 2 }
+
+ ospfv3PacketSrc  OBJECT-TYPE
+     SYNTAX       InetAddressIPv6
+     MAX-ACCESS   accessible-for-notify
+     STATUS       current
+     DESCRIPTION
+         "The IPv6 address of an inbound packet that cannot
+         be identified by a neighbor instance.
+
+         Only IPv6 addresses without zone index are expected."
+     ::= { ospfv3NotificationEntry 3 }
+
+ -- Notification Definitions
+
+ -- The notifications need to be throttled so as to not overwhelm the
+ -- management agent in case of rapid changes to the OSPFv3 module.
+
+ospfv3VirtIfStateChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId,  -- The originator of the notification
+               ospfv3VirtIfState  -- The new state
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3VirtIfStateChange notification signifies that
+         there has been a change in the state of an OSPFv3 virtual
+         interface.
+
+         This notification should be generated when the interface
+         state regresses (e.g., goes from Point-to-Point to Down)
+         or progresses to a terminal state (i.e., Point-to-Point)."
+     ::= { ospfv3Notifications 1 }
+
+ospfv3NbrStateChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+               ospfv3NbrState  -- The new state
+
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3NbrStateChange notification signifies that
+         there has been a change in the state of a
+         non-virtual OSPFv3 neighbor.  This notification should be
+         generated when the neighbor state regresses
+         (e.g., goes from Attempt or Full to 1-Way or
+         Down) or progresses to a terminal state (e.g.,
+         2-Way or Full).  When a neighbor transitions
+         from or to Full on non-broadcast multi-access
+         and broadcast networks, the notification should be
+         generated by the Designated Router.  A Designated
+         Router transitioning to Down will be noted by
+         ospfIfStateChange."
+     ::= { ospfv3Notifications 2 }
+
+ospfv3VirtNbrStateChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+               ospfv3VirtNbrState  -- The new state
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3VirtNbrStateChange notification signifies
+         that there has been a change in the state of an OSPFv3
+         virtual neighbor.  This notification should be generated
+         when the neighbor state regresses (e.g., goes
+         from Attempt or Full to 1-Way or Down) or
+         progresses to a terminal state (e.g., Full)."
+     ::= { ospfv3Notifications 3 }
+
+ospfv3IfConfigError NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3IfState,         -- State of the interface
+        ospfv3PacketSrc,       -- IPv6 address of source
+        ospfv3ConfigErrorType, -- Type of error
+        ospfv3PacketType       -- Type of packet
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3IfConfigError notification signifies that a
+         packet has been received on a non-virtual
+         interface from a router whose configuration
+         parameters conflict with this router's
+         configuration parameters.  Note that the event
+         optionMismatch should cause a notification only if it
+         prevents an adjacency from forming."
+     ::= { ospfv3Notifications 4 }
+
+ospfv3VirtIfConfigError NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3VirtIfState,     -- State of the interface
+        ospfv3ConfigErrorType, -- Type of error
+        ospfv3PacketType
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3VirtIfConfigError notification signifies that a
+         packet has been received on a virtual interface
+         from a router whose configuration parameters
+         conflict with this router's configuration
+         parameters.  Note that the event optionMismatch
+         should cause a notification only if it prevents an
+         adjacency from forming."
+     ::= { ospfv3Notifications 5 }
+
+ospfv3IfRxBadPacket NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3IfState,         -- State of the interface
+        ospfv3PacketSrc,       -- The source IPv6 address
+        ospfv3PacketType       -- Type of packet
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3IfRxBadPacket notification signifies that an
+         OSPFv3 packet that cannot be parsed has been received on a
+         non-virtual interface."
+     ::= { ospfv3Notifications 6 }
+
+ospfv3VirtIfRxBadPacket NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+       ospfv3VirtIfState,      -- State of the interface
+       ospfv3PacketType        -- Type of packet
+       }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3VirtIfRxBadPacket notification signifies
+         that an OSPFv3 packet that cannot be parsed has been
+         received on a virtual interface."
+     ::= { ospfv3Notifications 7 }
+
+ospfv3LsdbOverflow NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3ExtAreaLsdbLimit -- Limit on External LSAs
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3LsdbOverflow notification signifies that the
+         number of LSAs in the router's link state
+         database has exceeded ospfv3ExtAreaLsdbLimit."
+     ::= { ospfv3Notifications 8 }
+
+ospfv3LsdbApproachingOverflow NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3ExtAreaLsdbLimit
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3LsdbApproachingOverflow notification signifies
+         that the number of LSAs in the router's
+         link state database has exceeded ninety percent of
+         ospfv3ExtAreaLsdbLimit."
+     ::= { ospfv3Notifications 9 }
+
+ospfv3IfStateChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3IfState   -- The new state
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3IfStateChange notification signifies that there
+         has been a change in the state of a non-virtual
+         OSPFv3 interface.  This notification should be generated
+         when the interface state regresses (e.g., goes
+         from DR to Down) or progresses to a terminal
+         state (i.e., Point-to-Point, DR Other, DR, or
+         Backup)."
+     ::= { ospfv3Notifications 10 }
+
+ospfv3NssaTranslatorStatusChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+        ospfv3AreaNssaTranslatorState  -- new state
+        }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3NssaTranslatorStatusChange notification
+         indicates that there has been a change in the router's
+         ability to translate OSPFv3 NSSA LSAs into OSPFv3 External
+         LSAs.  This notification should be generated when the
+         Translator Status transitions from or to any defined
+         status on a per-area basis."
+     ::= { ospfv3Notifications 11 }
+
+ospfv3RestartStatusChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+               ospfv3RestartStatus,  -- new status
+               ospfv3RestartInterval,
+               ospfv3RestartExitReason
+             }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3RestartStatusChange notification signifies that
+         there has been a change in the graceful restart
+         state for the router.  This notification should be
+         generated when the router restart status
+         changes."
+     ::= { ospfv3Notifications 12 }
+
+ospfv3NbrRestartHelperStatusChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+               ospfv3NbrRestartHelperStatus,  -- new status
+               ospfv3NbrRestartHelperAge,
+               ospfv3NbrRestartHelperExitReason
+             }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3NbrRestartHelperStatusChange notification
+         signifies that there has been a change in the
+         graceful restart helper state for the neighbor.
+         This notification should be generated when the
+         neighbor restart helper status transitions for a neighbor."
+     ::= { ospfv3Notifications 13 }
+
+ospfv3VirtNbrRestartHelperStatusChange NOTIFICATION-TYPE
+     OBJECTS { ospfv3RouterId, -- The originator of the notification
+               ospfv3VirtNbrRestartHelperStatus,  -- new status
+               ospfv3VirtNbrRestartHelperAge,
+               ospfv3VirtNbrRestartHelperExitReason
+             }
+     STATUS       current
+     DESCRIPTION
+         "An ospfv3VirtNbrRestartHelperStatusChange
+         notification signifies that there has been a
+         change in the graceful restart helper state for
+         the virtual neighbor.  This notification should be
+         generated when the virtual neighbor restart helper status
+         transitions for a virtual neighbor."
+     ::= { ospfv3Notifications 14 }
+
+ -- Conformance Information
+
+ ospfv3Groups      OBJECT IDENTIFIER ::= { ospfv3Conformance 1 }
+ ospfv3Compliances OBJECT IDENTIFIER ::= { ospfv3Conformance 2 }
+
+ -- Compliance Statements
+
+ ospfv3FullCompliance MODULE-COMPLIANCE
+         STATUS          current
+         DESCRIPTION     "The compliance statement"
+         MODULE          -- this module
+         MANDATORY-GROUPS {
+                         ospfv3BasicGroup,
+                         ospfv3AreaGroup,
+                         ospfv3IfGroup,
+                         ospfv3VirtIfGroup,
+                         ospfv3NbrGroup,
+                         ospfv3CfgNbrGroup,
+                         ospfv3VirtNbrGroup,
+                         ospfv3AreaAggregateGroup
+                         }
+
+         GROUP           ospfv3AsLsdbGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             display their AS-scope link state database."
+
+         GROUP           ospfv3AreaLsdbGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             display their Area-scope link state database."
+
+         GROUP           ospfv3LinkLsdbGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             display their Link-scope link state database
+             for non-virtual interfaces."
+
+         GROUP           ospfv3VirtLinkLsdbGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             display their Link-scope link state database
+             for virtual interfaces."
+
+         GROUP           ospfv3HostGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             support attached hosts."
+
+         GROUP           ospfv3NotificationObjectGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             support OSPFv3 notifications."
+
+         GROUP           ospfv3NotificationGroup
+         DESCRIPTION
+             "This group is required for OSPFv3 systems that
+             support OSPFv3 notifications."
+
+         OBJECT          ospfv3NbrAddressType
+         SYNTAX          InetAddressType { ipv6(2) }
+         DESCRIPTION
+             "An implementation is only required to support IPv6
+             address without zone index."
+
+         OBJECT          ospfv3NbrAddress
+         SYNTAX          InetAddress (SIZE (16))
+         DESCRIPTION
+             "An implementation is only required to support IPv6
+             address without zone index."
+
+         OBJECT          ospfv3VirtNbrAddressType
+         SYNTAX          InetAddressType { ipv6(2) }
+         DESCRIPTION
+             "An implementation is only required to support IPv6
+             address without zone index."
+
+         OBJECT          ospfv3VirtNbrAddress
+         SYNTAX          InetAddress (SIZE (16))
+         DESCRIPTION
+             "An implementation is only required to support IPv6
+             address without zone index."
+         ::= { ospfv3Compliances 1 }
+
+    ospfv3ReadOnlyCompliance MODULE-COMPLIANCE
+       STATUS     current
+       DESCRIPTION
+               "When this MIB module is implemented without
+               support for read-create (i.e., in read-only
+               mode), the implementation can claim read-only
+               compliance.  Such a device can then be monitored,
+               but cannot be configured with this MIB."
+
+       MODULE -- this module
+            MANDATORY-GROUPS {
+                    ospfv3BasicGroup,
+                    ospfv3AreaGroup,
+                    ospfv3IfGroup,
+                    ospfv3VirtIfGroup,
+                    ospfv3NbrGroup,
+                    ospfv3CfgNbrGroup,
+                    ospfv3VirtNbrGroup,
+                    ospfv3AreaAggregateGroup
+                    }
+
+       GROUP           ospfv3AsLsdbGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           display their AS-scope link state database."
+
+       GROUP           ospfv3AreaLsdbGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           display their Area-scope link state database."
+
+       GROUP           ospfv3LinkLsdbGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           display their Link-scope link state database
+           for non-virtual interfaces."
+
+       GROUP           ospfv3VirtLinkLsdbGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           display their Link-scope link state database
+           for virtual interfaces."
+
+       GROUP           ospfv3HostGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           support attached hosts."
+
+       GROUP           ospfv3NotificationObjectGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           support OSPFv3 notifications."
+
+       GROUP           ospfv3NotificationGroup
+       DESCRIPTION
+           "This group is required for OSPFv3 systems that
+           support OSPFv3 notifications."
+
+       OBJECT ospfv3RouterId
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AdminStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3ExtAreaLsdbLimit
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3ExitOverflowInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3DemandExtensions
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3ReferenceBandwidth
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3RestartSupport
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3RestartInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3RestartStrictLsaChecking
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3NotificationEnable
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3StubRouterAdvertisement
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaImportAsExtern
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaSummary
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaStubMetric
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaNssaTranslatorRole
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaNssaTranslatorStabInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaStubMetricType
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaTEEnabled
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3HostMetric
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3HostRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3HostAreaID
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfAreaId
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfType
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfAdminStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfRtrPriority
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfTransitDelay
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfRetransInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfHelloInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfRtrDeadInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfPollInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfDemand
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfMetricValue
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfDemandNbrProbe
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfDemandNbrProbeRetransLimit
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfDemandNbrProbeInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfTEDisabled
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3IfLinkLSASuppression
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3VirtIfTransitDelay
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3VirtIfRetransInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3VirtIfHelloInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3VirtIfRtrDeadInterval
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3VirtIfRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3CfgNbrPriority
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3CfgNbrRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaAggregateRowStatus
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaAggregateEffect
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+
+       OBJECT ospfv3AreaAggregateRouteTag
+       MIN-ACCESS read-only
+       DESCRIPTION
+            "Write access is not required."
+    ::= { ospfv3Compliances 2 }
+
+ -- units of conformance
+
+ ospfv3BasicGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3RouterId,
+                         ospfv3AdminStatus,
+                         ospfv3VersionNumber,
+                         ospfv3AreaBdrRtrStatus,
+                         ospfv3ASBdrRtrStatus,
+                         ospfv3AsScopeLsaCount,
+                         ospfv3AsScopeLsaCksumSum,
+                         ospfv3OriginateNewLsas,
+                         ospfv3RxNewLsas,
+                         ospfv3ExtLsaCount,
+                         ospfv3ExtAreaLsdbLimit,
+                         ospfv3ExitOverflowInterval,
+                         ospfv3DemandExtensions,
+                         ospfv3ReferenceBandwidth,
+                         ospfv3RestartSupport,
+                         ospfv3RestartInterval,
+                         ospfv3RestartStrictLsaChecking,
+                         ospfv3RestartStatus,
+                         ospfv3RestartAge,
+                         ospfv3RestartExitReason,
+                         ospfv3NotificationEnable,
+                         ospfv3StubRouterSupport,
+                         ospfv3StubRouterAdvertisement,
+                         ospfv3DiscontinuityTime,
+                         ospfv3RestartTime
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for managing/monitoring
+             OSPFv3 global parameters."
+         ::= { ospfv3Groups 1 }
+
+ ospfv3AreaGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3AreaImportAsExtern,
+                         ospfv3AreaSpfRuns,
+                         ospfv3AreaBdrRtrCount,
+                         ospfv3AreaAsBdrRtrCount,
+                         ospfv3AreaScopeLsaCount,
+                         ospfv3AreaScopeLsaCksumSum,
+                         ospfv3AreaSummary,
+                         ospfv3AreaRowStatus,
+                         ospfv3AreaStubMetric,
+                         ospfv3AreaNssaTranslatorRole,
+                         ospfv3AreaNssaTranslatorState,
+                         ospfv3AreaNssaTranslatorStabInterval,
+                         ospfv3AreaNssaTranslatorEvents,
+                         ospfv3AreaStubMetricType,
+                         ospfv3AreaTEEnabled
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             supporting areas."
+         ::= { ospfv3Groups 2 }
+
+ ospfv3AsLsdbGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3AsLsdbSequence,
+                         ospfv3AsLsdbAge,
+                         ospfv3AsLsdbChecksum,
+                         ospfv3AsLsdbAdvertisement,
+                         ospfv3AsLsdbTypeKnown
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             that display their AS-scope link state database."
+         ::= { ospfv3Groups 3 }
+
+ ospfv3AreaLsdbGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3AreaLsdbSequence,
+                         ospfv3AreaLsdbAge,
+                         ospfv3AreaLsdbChecksum,
+                         ospfv3AreaLsdbAdvertisement,
+                         ospfv3AreaLsdbTypeKnown
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             that display their Area-scope link state database."
+         ::= { ospfv3Groups 4 }
+
+ ospfv3LinkLsdbGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3LinkLsdbSequence,
+                         ospfv3LinkLsdbAge,
+                         ospfv3LinkLsdbChecksum,
+                         ospfv3LinkLsdbAdvertisement,
+                         ospfv3LinkLsdbTypeKnown
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             that display their Link-scope link state database
+             for non-virtual interfaces."
+         ::= { ospfv3Groups 5 }
+
+ ospfv3HostGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3HostMetric,
+                         ospfv3HostRowStatus,
+                         ospfv3HostAreaID
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             that support attached hosts."
+         ::= { ospfv3Groups 6 }
+
+ ospfv3IfGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3IfAreaId,
+                         ospfv3IfType,
+                         ospfv3IfAdminStatus,
+                         ospfv3IfRtrPriority,
+                         ospfv3IfTransitDelay,
+                         ospfv3IfRetransInterval,
+                         ospfv3IfHelloInterval,
+                         ospfv3IfRtrDeadInterval,
+                         ospfv3IfPollInterval,
+                         ospfv3IfState,
+                         ospfv3IfDesignatedRouter,
+                         ospfv3IfBackupDesignatedRouter,
+                         ospfv3IfEvents,
+                         ospfv3IfRowStatus,
+                         ospfv3IfDemand,
+                         ospfv3IfMetricValue,
+                         ospfv3IfLinkScopeLsaCount,
+                         ospfv3IfLinkLsaCksumSum,
+                         ospfv3IfDemandNbrProbe,
+                         ospfv3IfDemandNbrProbeRetransLimit,
+                         ospfv3IfDemandNbrProbeInterval,
+                         ospfv3IfTEDisabled,
+                         ospfv3IfLinkLSASuppression
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These interface objects are used for
+             managing/monitoring OSPFv3 interfaces."
+         ::= { ospfv3Groups 7 }
+
+ ospfv3VirtIfGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3VirtIfIndex,
+                         ospfv3VirtIfInstId,
+                         ospfv3VirtIfTransitDelay,
+                         ospfv3VirtIfRetransInterval,
+                         ospfv3VirtIfHelloInterval,
+                         ospfv3VirtIfRtrDeadInterval,
+                         ospfv3VirtIfState,
+                         ospfv3VirtIfEvents,
+                         ospfv3VirtIfRowStatus,
+                         ospfv3VirtIfLinkScopeLsaCount,
+                         ospfv3VirtIfLinkLsaCksumSum
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These virtual interface objects are used for
+             managing/monitoring OSPFv3 virtual interfaces."
+         ::= { ospfv3Groups 8 }
+
+ ospfv3NbrGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3NbrAddressType,
+                         ospfv3NbrAddress,
+                         ospfv3NbrOptions,
+                         ospfv3NbrPriority,
+                         ospfv3NbrState,
+                         ospfv3NbrEvents,
+                         ospfv3NbrLsRetransQLen,
+                         ospfv3NbrHelloSuppressed,
+                         ospfv3NbrIfId,
+                         ospfv3NbrRestartHelperStatus,
+                         ospfv3NbrRestartHelperAge,
+                         ospfv3NbrRestartHelperExitReason
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These neighbor objects are used for
+             managing/monitoring OSPFv3 neighbors."
+         ::= { ospfv3Groups 9 }
+
+ ospfv3CfgNbrGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3CfgNbrPriority,
+                         ospfv3CfgNbrRowStatus
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These configured neighbor objects are used for
+             managing/monitoring OSPFv3-configured neighbors."
+         ::= { ospfv3Groups 10 }
+
+ ospfv3VirtNbrGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3VirtNbrIfIndex,
+                         ospfv3VirtNbrIfInstId,
+                         ospfv3VirtNbrAddressType,
+                         ospfv3VirtNbrAddress,
+                         ospfv3VirtNbrOptions,
+                         ospfv3VirtNbrState,
+                         ospfv3VirtNbrEvents,
+                         ospfv3VirtNbrLsRetransQLen,
+                         ospfv3VirtNbrHelloSuppressed,
+                         ospfv3VirtNbrIfId,
+                         ospfv3VirtNbrRestartHelperStatus,
+                         ospfv3VirtNbrRestartHelperAge,
+                         ospfv3VirtNbrRestartHelperExitReason
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These virtual neighbor objects are used for
+             managing/monitoring OSPFv3 virtual neighbors."
+         ::= { ospfv3Groups 11 }
+
+ ospfv3AreaAggregateGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3AreaAggregateRowStatus,
+                         ospfv3AreaAggregateEffect,
+                         ospfv3AreaAggregateRouteTag
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These area aggregate objects are required for
+             aggregating OSPFv3 prefixes for summarization
+             across areas."
+         ::= { ospfv3Groups 12 }
+
+ ospfv3VirtLinkLsdbGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3VirtLinkLsdbSequence,
+                         ospfv3VirtLinkLsdbAge,
+                         ospfv3VirtLinkLsdbChecksum,
+                         ospfv3VirtLinkLsdbAdvertisement,
+                         ospfv3VirtLinkLsdbTypeKnown
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used for OSPFv3 systems
+             that display their Link-scope link state database
+             for virtual interfaces."
+         ::= { ospfv3Groups 13 }
+
+ ospfv3NotificationObjectGroup OBJECT-GROUP
+         OBJECTS         {
+                         ospfv3ConfigErrorType,
+                         ospfv3PacketType,
+                         ospfv3PacketSrc
+                         }
+         STATUS          current
+         DESCRIPTION
+             "These objects are used to record notification
+             parameters."
+         ::= { ospfv3Groups 14 }
+
+ ospfv3NotificationGroup NOTIFICATION-GROUP
+         NOTIFICATIONS   {
+                         ospfv3VirtIfStateChange,
+                         ospfv3NbrStateChange,
+                         ospfv3VirtNbrStateChange,
+                         ospfv3IfConfigError,
+                         ospfv3VirtIfConfigError,
+                         ospfv3IfRxBadPacket,
+                         ospfv3VirtIfRxBadPacket,
+                         ospfv3LsdbOverflow,
+                         ospfv3LsdbApproachingOverflow,
+                         ospfv3IfStateChange,
+                         ospfv3NssaTranslatorStatusChange,
+                         ospfv3RestartStatusChange,
+                         ospfv3NbrRestartHelperStatusChange,
+                         ospfv3VirtNbrRestartHelperStatusChange
+                         }
+         STATUS          current
+         DESCRIPTION
+             "This group is used for OSPFv3 notifications."
+         ::= { ospfv3Groups 15 }
+
+ END
diff --git a/ospf6d/README b/ospf6d/README
new file mode 100644 (file)
index 0000000..f5a0046
--- /dev/null
@@ -0,0 +1,102 @@
+
+                 Zebra OSPF daemon for IPv6 network
+
+                            2003/08/18
+
+README for newer code is not yet. General usage should remain
+the same. For further usage, see command helps by typing '?'
+in vty, and then imagin ! ;p) Previous README contents follows.
+
+                 Zebra OSPF daemon for IPv6 network
+
+                            2001/12/20
+
+Zebra OSPF6d is OSPF version 3 daemon which is specified by
+"OSPF for IPv6" (RFC 2740).
+
+*** NOTE ***
+  Zebra ospf6d is in development yet. It may lack some functionalities,
+  and may have some bugs. Use the latest version from the anoncvs
+  repository (http://www.zebra.org/cvs.html) !
+
+This file README is like memo yet, so please feel free to ask
+<yasu@sfc.wide.ad.jp> by E-mail. Patches will be appriciated.
+
+ospf6d's vty port was default to 2606/tcp.
+Use commands below.
+
+VIEW NODE:
+  show ipv6 ospf6
+    To see Router-ID, uptime of ospf6d, some statistics.
+
+  show ipv6 ospf6 database ...
+    This command shows LSA database. You can specify
+    LS-type/LS-ID/Advertising-Router of LSAs. '*' is recognized.
+
+  show ipv6 ospf6 interface ...
+    To see the status of the OSPF interface, and the configuration
+    like interface costs.
+
+  show ipv6 ospf6 neighbor ...
+    Shows state of neighbors and choosed (Backup) DR on the I/F.
+
+  show ipv6 ospf6 route (X::X)
+    This command shows internal routing table of the ospf6d.
+    Routes not calculated by OSPFv3 (like connected routes)
+    are not shown. If Address is specified (X::X), shows the route
+    that the address matches.
+
+  show ipv6 ospf6 route redistribute (X::X)
+    Shows the routes advertised as AS-External routes by the router
+    itself. If Address is specified (X::X), shows the route
+    that the address matches.
+
+CONFIG NODE:
+  interface NAME
+    To enter INTERFACE NODE
+
+  router ospf6 ...
+    To enter OSPF6 NODE
+
+INTERFACE NODE:
+  ipv6 ospf6 cost COST
+    Sets the interface's output cost. Depends on interface bandwidth by default.
+
+  ipv6 ospf6 hello-interval HELLOINTERVAL
+    Sets the interface's Hello Interval. default 10
+
+  ipv6 ospf6 dead-interval DEADINTERVAL
+    Sets the interface's Router Dead Interval. default 40
+
+  ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL
+    Sets the interface's Rxmt Interval. default 5
+
+  ipv6 ospf6 priority PRIORITY
+    Sets the interface's Router Priority. default 1
+
+  ipv6 ospf6 transmit-delay TRANSMITDELAY
+    Sets the interface's Inf-Trans-Delay. default 1
+
+OSPF6 NODE:
+  router-id A.B.C.D
+    Sets the router's Router-ID
+
+  interface NAME area AREA
+    Binds interface to specified Area, and start
+    sending OSPFv3 packets.
+
+  auto-cost reference-bandwidth COST
+    Sets the reference bandwidth for cost calculations, where this
+    bandwidth is considered equivalent to an OSPF cost of 1, specified
+    in Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth
+    100Mbit/s or higher will have a cost of 1. Cost of lower bandwidth
+    links will be scaled with reference to this cost).  This
+    configuration setting MUST be consistent across all routers within
+    the OSPF domain.
+
+Sample configuration is in ospf6d.conf.sample.
+
+--
+Yasuhiro Ohara <yasu@sfc.wide.ad.jp>
+Kunihiro Ishiguro <kunihiro@zebra.org>
+
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
new file mode 100644 (file)
index 0000000..7e94cef
--- /dev/null
@@ -0,0 +1,945 @@
+/*
+ * Area Border Router function.
+ * Copyright (C) 2004 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "plist.h"
+#include "filter.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_route.h"
+#include "ospf6_lsa.h"
+#include "ospf6_route.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+#include "ospf6_intra.h"
+#include "ospf6_abr.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_abr;
+
+int
+ospf6_is_router_abr (struct ospf6 *o)
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+  int area_count = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, node, oa))
+    if (IS_AREA_ENABLED (oa))
+      area_count++;
+
+  if (area_count > 1)
+    return 1;
+  return 0;
+}
+
+void
+ospf6_abr_enable_area (struct ospf6_area *area)
+{
+  struct ospf6_area *oa;
+  struct ospf6_route *ro;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (area->ospf6->area_list, node, nnode, oa))
+    {
+      /* update B bit for each area */
+      OSPF6_ROUTER_LSA_SCHEDULE (oa);
+
+      /* install other area's configured address range */
+      if (oa != area)
+        {
+          for (ro = ospf6_route_head (oa->range_table); ro;
+               ro = ospf6_route_next (ro))
+            {
+              if (CHECK_FLAG (ro->flag, OSPF6_ROUTE_ACTIVE_SUMMARY))
+                ospf6_abr_originate_summary_to_area (ro, area);
+            }
+        }
+    }
+
+  /* install calculated routes to border routers */
+  for (ro = ospf6_route_head (area->ospf6->brouter_table); ro;
+       ro = ospf6_route_next (ro))
+    ospf6_abr_originate_summary_to_area (ro, area);
+
+  /* install calculated routes to network (may be rejected by ranges) */
+  for (ro = ospf6_route_head (area->ospf6->route_table); ro;
+       ro = ospf6_route_next (ro))
+    ospf6_abr_originate_summary_to_area (ro, area);
+}
+
+void
+ospf6_abr_disable_area (struct ospf6_area *area)
+{
+  struct ospf6_area *oa;
+  struct ospf6_route *ro, *nro;
+  struct ospf6_lsa *old;
+  struct listnode *node, *nnode;
+
+  /* Withdraw all summary prefixes previously originated */
+  for (ro = ospf6_route_head (area->summary_prefix); ro; ro = nro)
+    {
+      nro = ospf6_route_next (ro);
+      old = ospf6_lsdb_lookup (ro->path.origin.type, ro->path.origin.id,
+                               area->ospf6->router_id, area->lsdb);
+      if (old)
+        ospf6_lsa_purge (old);
+      ospf6_route_remove (ro, area->summary_prefix);
+    }
+
+  /* Withdraw all summary router-routes previously originated */
+  for (ro = ospf6_route_head (area->summary_router); ro; ro = nro)
+    {
+      nro = ospf6_route_next (ro);
+      old = ospf6_lsdb_lookup (ro->path.origin.type, ro->path.origin.id,
+                               area->ospf6->router_id, area->lsdb);
+      if (old)
+        ospf6_lsa_purge (old);
+      ospf6_route_remove (ro, area->summary_router);
+    }
+
+  /* Schedule Router-LSA for each area (ABR status may change) */
+  for (ALL_LIST_ELEMENTS (area->ospf6->area_list, node, nnode, oa))
+    /* update B bit for each area */
+    OSPF6_ROUTER_LSA_SCHEDULE (oa);
+}
+
+/* RFC 2328 12.4.3. Summary-LSAs */
+void
+ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
+                                     struct ospf6_area *area)
+{
+  struct ospf6_lsa *lsa, *old = NULL;
+  struct ospf6_interface *oi;
+  struct ospf6_route *summary, *range = NULL;
+  struct ospf6_area *route_area;
+  char buffer[OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  caddr_t p;
+  struct ospf6_inter_prefix_lsa *prefix_lsa;
+  struct ospf6_inter_router_lsa *router_lsa;
+  struct ospf6_route_table *summary_table = NULL;
+  u_int16_t type;
+  char buf[64];
+  int is_debug = 0;
+
+  if (route->type == OSPF6_DEST_TYPE_ROUTER)
+    {
+      if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE (INTER_ROUTER))
+        {
+          is_debug++;
+          inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+                     buf, sizeof (buf));
+          zlog_debug ("Originating summary in area %s for ASBR %s",
+                     area->name, buf);
+        }
+      summary_table = area->summary_router;
+    }
+  else
+    {
+      if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE (INTER_PREFIX))
+        {
+          is_debug++;
+          prefix2str (&route->prefix, buf, sizeof (buf));
+          zlog_debug ("Originating summary in area %s for %s",
+                     area->name, buf);
+        }
+      summary_table = area->summary_prefix;
+    }
+
+  summary = ospf6_route_lookup (&route->prefix, summary_table);
+  if (summary)
+    old = ospf6_lsdb_lookup (summary->path.origin.type,
+                             summary->path.origin.id,
+                             area->ospf6->router_id, area->lsdb);
+
+  /* if this route has just removed, remove corresponding LSA */
+  if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE))
+    {
+      if (is_debug)
+        zlog_debug ("The route has just removed, purge previous LSA");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* Only destination type network, range or ASBR are considered */
+  if (route->type != OSPF6_DEST_TYPE_NETWORK &&
+      route->type != OSPF6_DEST_TYPE_RANGE &&
+      (route->type != OSPF6_DEST_TYPE_ROUTER ||
+       ! CHECK_FLAG (route->path.router_bits, OSPF6_ROUTER_BIT_E)))
+    {
+      if (is_debug)
+        zlog_debug ("Route type is none of network, range nor ASBR, withdraw");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* AS External routes are never considered */
+  if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 ||
+      route->path.type == OSPF6_PATH_TYPE_EXTERNAL2)
+    {
+      if (is_debug)
+        zlog_debug ("Path type is external, withdraw");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* do not generate if the path's area is the same as target area */
+  if (route->path.area_id == area->area_id)
+    {
+      if (is_debug)
+        zlog_debug ("The route is in the area itself, ignore");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* do not generate if the nexthops belongs to the target area */
+  oi = ospf6_interface_lookup_by_ifindex (route->nexthop[0].ifindex);
+  if (oi && oi->area && oi->area == area)
+    {
+      if (is_debug)
+        zlog_debug ("The route's nexthop is in the same area, ignore");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* do not generate if the route cost is greater or equal to LSInfinity */
+  if (route->path.cost >= OSPF_LS_INFINITY)
+    {
+      if (is_debug)
+        zlog_debug ("The cost exceeds LSInfinity, withdraw");
+      if (summary)
+        ospf6_route_remove (summary, summary_table);
+      if (old)
+        ospf6_lsa_purge (old);
+      return;
+    }
+
+  /* if this is a route to ASBR */
+  if (route->type == OSPF6_DEST_TYPE_ROUTER)
+    {
+      /* Only the prefered best path is considered */
+      if (! CHECK_FLAG (route->flag, OSPF6_ROUTE_BEST))
+        {
+          if (is_debug)
+            zlog_debug ("This is the secondary path to the ASBR, ignore");
+          if (summary)
+            ospf6_route_remove (summary, summary_table);
+          if (old)
+            ospf6_lsa_purge (old);
+          return;
+        }
+
+      /* Do not generate if the area is stub */
+      /* XXX */
+    }
+
+  /* if this is an intra-area route, this may be suppressed by aggregation */
+  if (route->type == OSPF6_DEST_TYPE_NETWORK &&
+      route->path.type == OSPF6_PATH_TYPE_INTRA)
+    {
+      /* search for configured address range for the route's area */
+      route_area = ospf6_area_lookup (route->path.area_id, area->ospf6);
+      assert (route_area);
+      range = ospf6_route_lookup_bestmatch (&route->prefix,
+                                            route_area->range_table);
+
+      /* ranges are ignored when originate backbone routes to transit area.
+         Otherwise, if ranges are configured, the route is suppressed. */
+      if (range && ! CHECK_FLAG (range->flag, OSPF6_ROUTE_REMOVE) &&
+          (route->path.area_id != OSPF_AREA_BACKBONE ||
+           ! IS_AREA_TRANSIT (area)))
+        {
+          if (is_debug)
+            {
+              prefix2str (&range->prefix, buf, sizeof (buf));
+              zlog_debug ("Suppressed by range %s of area %s",
+                         buf, route_area->name);
+            }
+
+          if (summary)
+            ospf6_route_remove (summary, summary_table);
+          if (old)
+            ospf6_lsa_purge (old);
+          return;
+        }
+    }
+
+  /* If this is a configured address range */
+  if (route->type == OSPF6_DEST_TYPE_RANGE)
+    {
+      /* If DoNotAdvertise is set */
+      if (CHECK_FLAG (route->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE))
+        {
+          if (is_debug)
+            zlog_debug ("This is the range with DoNotAdvertise set. ignore");
+          if (summary)
+            ospf6_route_remove (summary, summary_table);
+          if (old)
+            ospf6_lsa_purge (old);
+          return;
+        }
+
+      /* Whether the route have active longer prefix */
+      if (! CHECK_FLAG (route->flag, OSPF6_ROUTE_ACTIVE_SUMMARY))
+        {
+          if (is_debug)
+            zlog_debug ("The range is not active. withdraw");
+          if (summary)
+            ospf6_route_remove (summary, summary_table);
+          if (old)
+            ospf6_lsa_purge (old);
+          return;
+        }
+    }
+
+  /* Check export list */
+  if (EXPORT_NAME (area))
+    {
+      if (EXPORT_LIST (area) == NULL)
+        EXPORT_LIST (area) =
+          access_list_lookup (AFI_IP6, EXPORT_NAME (area));
+
+      if (EXPORT_LIST (area))
+        if (access_list_apply (EXPORT_LIST (area),
+                               &route->prefix) == FILTER_DENY)
+          {
+            if (is_debug)
+              {
+                inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+                           buf, sizeof(buf));
+                zlog_debug ("prefix %s was denied by export list", buf);
+              }
+            return;
+          }
+    }
+
+  /* Check filter-list */
+  if (PREFIX_NAME_OUT (area))
+    {
+      if (PREFIX_LIST_OUT (area) == NULL)
+        PREFIX_LIST_OUT (area) =
+          prefix_list_lookup(AFI_IP6, PREFIX_NAME_OUT (area));
+
+      if (PREFIX_LIST_OUT (area))
+         if (prefix_list_apply (PREFIX_LIST_OUT (area), 
+                                &route->prefix) != PREFIX_PERMIT) 
+           {
+             if (is_debug)
+               {
+                 inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)),
+                            buf, sizeof (buf));
+                 zlog_debug ("prefix %s was denied by filter-list out", buf);
+               }
+             return;
+           }
+    }
+
+  /* the route is going to be originated. store it in area's summary_table */
+  if (summary == NULL)
+    {
+      summary = ospf6_route_copy (route);
+      if (route->type == OSPF6_DEST_TYPE_NETWORK ||
+          route->type == OSPF6_DEST_TYPE_RANGE)
+        summary->path.origin.type = htons (OSPF6_LSTYPE_INTER_PREFIX);
+      else
+        summary->path.origin.type = htons (OSPF6_LSTYPE_INTER_ROUTER);
+      summary->path.origin.adv_router = area->ospf6->router_id;
+      summary->path.origin.id =
+        ospf6_new_ls_id (summary->path.origin.type,
+                         summary->path.origin.adv_router, area->lsdb);
+      summary = ospf6_route_add (summary, summary_table);
+    }
+  else
+    {
+      summary->type = route->type;
+      quagga_gettime (QUAGGA_CLK_MONOTONIC, &summary->changed);
+    }
+
+  summary->path.router_bits = route->path.router_bits;
+  summary->path.options[0] = route->path.options[0];
+  summary->path.options[1] = route->path.options[1];
+  summary->path.options[2] = route->path.options[2];
+  summary->path.prefix_options = route->path.prefix_options;
+  summary->path.area_id = area->area_id;
+  summary->path.type = OSPF6_PATH_TYPE_INTER;
+  summary->path.cost = route->path.cost;
+  summary->nexthop[0] = route->nexthop[0];
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+
+  if (route->type == OSPF6_DEST_TYPE_ROUTER)
+    {
+      router_lsa = (struct ospf6_inter_router_lsa *)
+        ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+      p = (caddr_t) router_lsa + sizeof (struct ospf6_inter_router_lsa);
+
+      /* Fill Inter-Area-Router-LSA */
+      router_lsa->options[0] = route->path.options[0];
+      router_lsa->options[1] = route->path.options[1];
+      router_lsa->options[2] = route->path.options[2];
+      OSPF6_ABR_SUMMARY_METRIC_SET (router_lsa, route->path.cost);
+      router_lsa->router_id = ADV_ROUTER_IN_PREFIX (&route->prefix);
+      type = htons (OSPF6_LSTYPE_INTER_ROUTER);
+    }
+  else
+    {
+      prefix_lsa = (struct ospf6_inter_prefix_lsa *)
+        ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+      p = (caddr_t) prefix_lsa + sizeof (struct ospf6_inter_prefix_lsa);
+
+      /* Fill Inter-Area-Prefix-LSA */
+      OSPF6_ABR_SUMMARY_METRIC_SET (prefix_lsa, route->path.cost);
+      prefix_lsa->prefix.prefix_length = route->prefix.prefixlen;
+      prefix_lsa->prefix.prefix_options = route->path.prefix_options;
+
+      /* set Prefix */
+      memcpy (p, &route->prefix.u.prefix6,
+              OSPF6_PREFIX_SPACE (route->prefix.prefixlen));
+      ospf6_prefix_apply_mask (&prefix_lsa->prefix);
+      p += OSPF6_PREFIX_SPACE (route->prefix.prefixlen);
+      type = htons (OSPF6_LSTYPE_INTER_PREFIX);
+    }
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = type;
+  lsa_header->id = summary->path.origin.id;
+  lsa_header->adv_router = area->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, area->lsdb);
+  lsa_header->length = htons ((caddr_t) p - (caddr_t) lsa_header);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_area (lsa, area);
+}
+
+static void
+ospf6_abr_range_update (struct ospf6_route *range)
+{
+  u_int32_t cost = 0;
+  struct ospf6_route *ro;
+
+  assert (range->type == OSPF6_DEST_TYPE_RANGE);
+
+  /* update range's cost and active flag */
+  for (ro = ospf6_route_match_head (&range->prefix, ospf6->route_table);
+       ro; ro = ospf6_route_match_next (&range->prefix, ro))
+    {
+      if (ro->path.area_id == range->path.area_id &&
+          ! CHECK_FLAG (ro->flag, OSPF6_ROUTE_REMOVE))
+        cost = MAX (cost, ro->path.cost);
+    }
+
+  if (range->path.cost != cost)
+    {
+      range->path.cost = cost;
+
+      if (range->path.cost)
+        SET_FLAG (range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY);
+      else
+        UNSET_FLAG (range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY);
+
+      ospf6_abr_originate_summary (range);
+    }
+}
+
+void
+ospf6_abr_originate_summary (struct ospf6_route *route)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+  struct ospf6_route *range = NULL;
+
+  if (route->type == OSPF6_DEST_TYPE_NETWORK)
+    {
+      oa = ospf6_area_lookup (route->path.area_id, ospf6);
+      range = ospf6_route_lookup_bestmatch (&route->prefix, oa->range_table);
+      if (range)
+        ospf6_abr_range_update (range);
+    }
+
+  for (ALL_LIST_ELEMENTS (ospf6->area_list, node, nnode, oa))
+    ospf6_abr_originate_summary_to_area (route, oa);
+}
+
+/* RFC 2328 16.2. Calculating the inter-area routes */
+void
+ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa)
+{
+  struct prefix prefix, abr_prefix;
+  struct ospf6_route_table *table = NULL;
+  struct ospf6_route *range, *route, *old = NULL;
+  struct ospf6_route *abr_entry;
+  u_char type = 0;
+  char options[3] = {0, 0, 0};
+  u_int8_t prefix_options = 0;
+  u_int32_t cost = 0;
+  u_char router_bits = 0;
+  int i;
+  char buf[64];
+  int is_debug = 0;
+  struct ospf6_inter_prefix_lsa *prefix_lsa = NULL;
+  struct ospf6_inter_router_lsa *router_lsa = NULL;
+
+  memset (&prefix, 0, sizeof (prefix));
+
+  if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX))
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (INTER_PREFIX))
+        {
+          is_debug++;
+          zlog_debug ("Examin %s in area %s", lsa->name, oa->name);
+        }
+
+      prefix_lsa = (struct ospf6_inter_prefix_lsa *)
+        OSPF6_LSA_HEADER_END (lsa->header);
+      prefix.family = AF_INET6;
+      prefix.prefixlen = prefix_lsa->prefix.prefix_length;
+      ospf6_prefix_in6_addr (&prefix.u.prefix6, &prefix_lsa->prefix);
+      if (is_debug)
+        prefix2str (&prefix, buf, sizeof (buf));
+      table = oa->ospf6->route_table;
+      type = OSPF6_DEST_TYPE_NETWORK;
+      prefix_options = prefix_lsa->prefix.prefix_options;
+      cost = OSPF6_ABR_SUMMARY_METRIC (prefix_lsa);
+    }
+  else if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER))
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (INTER_ROUTER))
+        {
+          is_debug++;
+          zlog_debug ("Examin %s in area %s", lsa->name, oa->name);
+        }
+
+      router_lsa = (struct ospf6_inter_router_lsa *)
+        OSPF6_LSA_HEADER_END (lsa->header);
+      ospf6_linkstate_prefix (router_lsa->router_id, htonl (0), &prefix);
+      if (is_debug)
+        inet_ntop (AF_INET, &router_lsa->router_id, buf, sizeof (buf));
+      table = oa->ospf6->brouter_table;
+      type = OSPF6_DEST_TYPE_ROUTER;
+      options[0] = router_lsa->options[0];
+      options[1] = router_lsa->options[1];
+      options[2] = router_lsa->options[2];
+      cost = OSPF6_ABR_SUMMARY_METRIC (router_lsa);
+      SET_FLAG (router_bits, OSPF6_ROUTER_BIT_E);
+    }
+  else
+    assert (0);
+
+  /* Find existing route */
+  route = ospf6_route_lookup (&prefix, table);
+  if (route)
+    ospf6_route_lock (route);
+  while (route && ospf6_route_is_prefix (&prefix, route))
+    {
+      if (route->path.area_id == oa->area_id &&
+          route->path.origin.type == lsa->header->type &&
+          route->path.origin.id == lsa->header->id &&
+          route->path.origin.adv_router == lsa->header->adv_router &&
+          ! CHECK_FLAG (route->flag, OSPF6_ROUTE_WAS_REMOVED))
+        old = route;
+      route = ospf6_route_next (route);
+    }
+
+  /* (1) if cost == LSInfinity or if the LSA is MaxAge */
+  if (cost == OSPF_LS_INFINITY)
+    {
+      if (is_debug)
+        zlog_debug ("cost is LS_INFINITY, ignore");
+      if (old)
+        ospf6_route_remove (old, table);
+      return;
+    }
+  if (OSPF6_LSA_IS_MAXAGE (lsa))
+    {
+      if (is_debug)
+        zlog_debug ("LSA is MaxAge, ignore");
+      if (old)
+        ospf6_route_remove (old, table);
+      return;
+    }
+
+  /* (2) if the LSA is self-originated, ignore */
+  if (lsa->header->adv_router == oa->ospf6->router_id)
+    {
+      if (is_debug)
+        zlog_debug ("LSA is self-originated, ignore");
+      if (old)
+        ospf6_route_remove (old, table);
+      return;
+    }
+
+  /* (3) if the prefix is equal to an active configured address range */
+  /*     or if the NU bit is set in the prefix */
+  if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX))
+    {
+      range = ospf6_route_lookup (&prefix, oa->range_table);
+      if (range)
+        {
+          if (is_debug)
+            zlog_debug ("Prefix is equal to address range, ignore");
+          if (old)
+            ospf6_route_remove (old, table);
+          return;
+        }
+
+      if (CHECK_FLAG (prefix_lsa->prefix.prefix_options,
+                     OSPF6_PREFIX_OPTION_NU) ||
+         CHECK_FLAG (prefix_lsa->prefix.prefix_options,
+                     OSPF6_PREFIX_OPTION_LA))
+       {
+          if (is_debug)
+            zlog_debug ("Prefix has NU/LA bit set, ignore");
+          if (old)
+            ospf6_route_remove (old, table);
+          return;
+       }
+    }
+
+  if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER))
+    {
+      /* To pass test suites */
+      if (! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_R) ||
+         ! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_V6))
+       {
+          if (is_debug)
+            zlog_debug ("Prefix has NU/LA bit set, ignore");
+          if (old)
+            ospf6_route_remove (old, table);
+          return;
+       }
+    }
+
+  /* (4) if the routing table entry for the ABR does not exist */
+  ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &abr_prefix);
+  abr_entry = ospf6_route_lookup (&abr_prefix, oa->ospf6->brouter_table);
+  if (abr_entry == NULL || abr_entry->path.area_id != oa->area_id ||
+      CHECK_FLAG (abr_entry->flag, OSPF6_ROUTE_REMOVE) ||
+      ! CHECK_FLAG (abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B))
+    {
+      if (is_debug)
+        zlog_debug ("ABR router entry does not exist, ignore");
+      if (old)
+        ospf6_route_remove (old, table);
+      return;
+    }
+
+  /* Check import list */
+  if (IMPORT_NAME (oa))
+    {
+      if (IMPORT_LIST (oa) == NULL)
+        IMPORT_LIST (oa) = access_list_lookup (AFI_IP6, IMPORT_NAME (oa));
+
+      if (IMPORT_LIST (oa))
+        if (access_list_apply (IMPORT_LIST (oa), &prefix) == FILTER_DENY)
+          {
+            if (is_debug)
+              zlog_debug ("Prefix was denied by import-list");
+            if (old)
+              ospf6_route_remove (old, table);
+            return;
+          }
+    }
+
+  /* Check input prefix-list */
+  if (PREFIX_NAME_IN (oa))
+    {
+      if (PREFIX_LIST_IN (oa) == NULL)
+        PREFIX_LIST_IN (oa) = prefix_list_lookup (AFI_IP6, PREFIX_NAME_IN (oa));
+
+      if (PREFIX_LIST_IN (oa))
+        if (prefix_list_apply (PREFIX_LIST_IN (oa), &prefix) != PREFIX_PERMIT)
+          {
+            if (is_debug)
+              zlog_debug ("Prefix was denied by prefix-list");
+            if (old)
+              ospf6_route_remove (old, table);
+            return;
+          }
+    }
+
+  /* (5),(6),(7) the path preference is handled by the sorting
+     in the routing table. Always install the path by substituting
+     old route (if any). */
+  if (old)
+    route = ospf6_route_copy (old);
+  else
+    route = ospf6_route_create ();
+
+  route->type = type;
+  route->prefix = prefix;
+  route->path.origin.type = lsa->header->type;
+  route->path.origin.id = lsa->header->id;
+  route->path.origin.adv_router = lsa->header->adv_router;
+  route->path.router_bits = router_bits;
+  route->path.options[0] = options[0];
+  route->path.options[1] = options[1];
+  route->path.options[2] = options[2];
+  route->path.prefix_options = prefix_options;
+  route->path.area_id = oa->area_id;
+  route->path.type = OSPF6_PATH_TYPE_INTER;
+  route->path.cost = abr_entry->path.cost + cost;
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
+    route->nexthop[i] = abr_entry->nexthop[i];
+
+  if (is_debug)
+    zlog_debug ("Install route: %s", buf);
+  ospf6_route_add (route, table);
+}
+
+void
+ospf6_abr_examin_brouter (u_int32_t router_id)
+{
+  struct ospf6_lsa *lsa;
+  struct ospf6_area *oa;
+  struct listnode *node, *nnode;
+  u_int16_t type;
+
+  for (ALL_LIST_ELEMENTS (ospf6->area_list, node, nnode, oa))
+    {
+      type = htons (OSPF6_LSTYPE_INTER_ROUTER);
+      for (lsa = ospf6_lsdb_type_router_head (type, router_id, oa->lsdb); lsa;
+           lsa = ospf6_lsdb_type_router_next (type, router_id, lsa))
+        ospf6_abr_examin_summary (lsa, oa);
+
+      type = htons (OSPF6_LSTYPE_INTER_PREFIX);
+      for (lsa = ospf6_lsdb_type_router_head (type, router_id, oa->lsdb); lsa;
+           lsa = ospf6_lsdb_type_router_next (type, router_id, lsa))
+        ospf6_abr_examin_summary (lsa, oa);
+    }
+}
+
+void
+ospf6_abr_reimport (struct ospf6_area *oa)
+{
+  struct ospf6_lsa *lsa;
+  u_int16_t type;
+
+  type = htons (OSPF6_LSTYPE_INTER_ROUTER);
+  for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa;
+       lsa = ospf6_lsdb_type_next (type, lsa))
+    ospf6_abr_examin_summary (lsa, oa);
+
+  type = htons (OSPF6_LSTYPE_INTER_PREFIX);
+  for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa;
+       lsa = ospf6_lsdb_type_next (type, lsa))
+    ospf6_abr_examin_summary (lsa, oa);
+}
+
+
+
+/* Display functions */
+static char *
+ospf6_inter_area_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf,
+                                           int buflen, int pos)
+{
+  struct ospf6_inter_prefix_lsa *prefix_lsa;
+  struct in6_addr in6;
+
+  if (lsa != NULL)
+    {
+      prefix_lsa = (struct ospf6_inter_prefix_lsa *)
+       OSPF6_LSA_HEADER_END (lsa->header);
+
+      ospf6_prefix_in6_addr (&in6, &prefix_lsa->prefix);
+      if (buf)
+       {
+         inet_ntop (AF_INET6, &in6, buf, buflen);
+         sprintf (&buf[strlen(buf)], "/%d", prefix_lsa->prefix.prefix_length);
+       }
+    }
+
+  return (buf);
+}
+
+static int
+ospf6_inter_area_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  struct ospf6_inter_prefix_lsa *prefix_lsa;
+  char buf[INET6_ADDRSTRLEN];
+
+  prefix_lsa = (struct ospf6_inter_prefix_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  vty_out (vty, "     Metric: %lu%s",
+           (u_long) OSPF6_ABR_SUMMARY_METRIC (prefix_lsa), VNL);
+
+  ospf6_prefix_options_printbuf (prefix_lsa->prefix.prefix_options,
+                                 buf, sizeof (buf));
+  vty_out (vty, "     Prefix Options: %s%s", buf, VNL);
+
+  vty_out (vty, "     Prefix: %s%s",
+          ospf6_inter_area_prefix_lsa_get_prefix_str (lsa, buf, sizeof(buf),
+                                                      0), VNL);
+
+  return 0;
+}
+
+static char *
+ospf6_inter_area_router_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf,
+                                           int buflen, int pos)
+{
+  struct ospf6_inter_router_lsa *router_lsa;
+
+  if (lsa != NULL)
+    {
+      router_lsa = (struct ospf6_inter_router_lsa *)
+       OSPF6_LSA_HEADER_END (lsa->header);
+
+
+      if (buf)
+       inet_ntop (AF_INET, &router_lsa->router_id, buf, buflen);
+    }
+
+  return (buf);
+}
+
+static int
+ospf6_inter_area_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  struct ospf6_inter_router_lsa *router_lsa;
+  char buf[64];
+
+  router_lsa = (struct ospf6_inter_router_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  ospf6_options_printbuf (router_lsa->options, buf, sizeof (buf));
+  vty_out (vty, "     Options: %s%s", buf, VNL);
+  vty_out (vty, "     Metric: %lu%s",
+           (u_long) OSPF6_ABR_SUMMARY_METRIC (router_lsa), VNL);
+
+  inet_ntop (AF_INET, &router_lsa->router_id, buf, sizeof (buf));
+  vty_out (vty, "     Destination Router ID: %s%s", buf, VNL);
+
+  return 0;
+}
+
+/* Debug commands */
+DEFUN (debug_ospf6_abr,
+       debug_ospf6_abr_cmd,
+       "debug ospf6 abr",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 ABR function\n"
+      )
+{
+  OSPF6_DEBUG_ABR_ON ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_abr,
+       no_debug_ospf6_abr_cmd,
+       "no debug ospf6 abr",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 ABR function\n"
+      )
+{
+  OSPF6_DEBUG_ABR_OFF ();
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_abr (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_ABR)
+    vty_out (vty, "debug ospf6 abr%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_abr (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_abr_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_abr_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_abr_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_abr_cmd);
+}
+
+struct ospf6_lsa_handler inter_prefix_handler =
+{
+  OSPF6_LSTYPE_INTER_PREFIX,
+  "Inter-Prefix",
+  "IAP",
+  ospf6_inter_area_prefix_lsa_show,
+  ospf6_inter_area_prefix_lsa_get_prefix_str,
+};
+
+struct ospf6_lsa_handler inter_router_handler =
+{
+  OSPF6_LSTYPE_INTER_ROUTER,
+  "Inter-Router",
+  "IAR",
+  ospf6_inter_area_router_lsa_show,
+  ospf6_inter_area_router_lsa_get_prefix_str,
+};
+
+void
+ospf6_abr_init (void)
+{
+  ospf6_install_lsa_handler (&inter_prefix_handler);
+  ospf6_install_lsa_handler (&inter_router_handler);
+}
+
+
diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h
new file mode 100644 (file)
index 0000000..e5e2660
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_ABR_H
+#define OSPF6_ABR_H
+
+/* for struct ospf6_route */
+#include "ospf6_route.h"
+/* for struct ospf6_prefix */
+#include "ospf6_proto.h"
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_abr;
+#define OSPF6_DEBUG_ABR_ON() \
+  (conf_debug_ospf6_abr = 1)
+#define OSPF6_DEBUG_ABR_OFF() \
+  (conf_debug_ospf6_abr = 0)
+#define IS_OSPF6_DEBUG_ABR \
+  (conf_debug_ospf6_abr)
+
+/* Inter-Area-Prefix-LSA */
+#define OSPF6_INTER_PREFIX_LSA_MIN_SIZE        4U /* w/o IPv6 prefix */
+struct ospf6_inter_prefix_lsa
+{
+  u_int32_t metric;
+  struct ospf6_prefix prefix;
+};
+
+/* Inter-Area-Router-LSA */
+#define OSPF6_INTER_ROUTER_LSA_FIX_SIZE       12U
+struct ospf6_inter_router_lsa
+{
+  u_char mbz;
+  u_char options[3];
+  u_int32_t metric;
+  u_int32_t router_id;
+};
+
+#define OSPF6_ABR_SUMMARY_METRIC(E) (ntohl ((E)->metric & htonl (0x00ffffff)))
+#define OSPF6_ABR_SUMMARY_METRIC_SET(E,C) \
+  { (E)->metric &= htonl (0x00000000); \
+    (E)->metric |= htonl (0x00ffffff) & htonl (C); }
+
+extern int ospf6_is_router_abr (struct ospf6 *o);
+
+extern void ospf6_abr_enable_area (struct ospf6_area *oa);
+extern void ospf6_abr_disable_area (struct ospf6_area *oa);
+
+extern void ospf6_abr_originate_summary_to_area (struct ospf6_route *route,
+                                                struct ospf6_area *area);
+extern void ospf6_abr_originate_summary (struct ospf6_route *route);
+extern void ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa);
+extern void ospf6_abr_examin_brouter (u_int32_t router_id);
+extern void ospf6_abr_reimport (struct ospf6_area *oa);
+
+extern int config_write_ospf6_debug_abr (struct vty *vty);
+extern void install_element_ospf6_debug_abr (void);
+extern int ospf6_abr_config_write (struct vty *vty);
+
+extern void ospf6_abr_init (void);
+
+#endif /*OSPF6_ABR_H*/
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
new file mode 100644 (file)
index 0000000..1861fe7
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "linklist.h"
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "if.h"
+#include "prefix.h"
+#include "table.h"
+#include "plist.h"
+#include "filter.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_spf.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_intra.h"
+#include "ospf6_abr.h"
+#include "ospf6d.h"
+
+int
+ospf6_area_cmp (void *va, void *vb)
+{
+  struct ospf6_area *oa = (struct ospf6_area *) va;
+  struct ospf6_area *ob = (struct ospf6_area *) vb;
+  return (ntohl (oa->area_id) < ntohl (ob->area_id) ? -1 : 1);
+}
+
+/* schedule routing table recalculation */
+static void
+ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa)
+{
+  switch (ntohs (lsa->header->type))
+    {
+    case OSPF6_LSTYPE_ROUTER:
+    case OSPF6_LSTYPE_NETWORK:
+      if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type))
+        {
+          zlog_debug ("Examin %s", lsa->name);
+          zlog_debug ("Schedule SPF Calculation for %s",
+                     OSPF6_AREA (lsa->lsdb->data)->name);
+        }
+      ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6),
+                         ospf6_lsadd_to_spf_reason(lsa));
+      break;
+
+    case OSPF6_LSTYPE_INTRA_PREFIX:
+      ospf6_intra_prefix_lsa_add (lsa);
+      break;
+
+    case OSPF6_LSTYPE_INTER_PREFIX:
+    case OSPF6_LSTYPE_INTER_ROUTER:
+      ospf6_abr_examin_summary (lsa, (struct ospf6_area *) lsa->lsdb->data);
+      break;
+
+    default:
+      break;
+    }
+}
+
+static void
+ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa)
+{
+  switch (ntohs (lsa->header->type))
+    {
+    case OSPF6_LSTYPE_ROUTER:
+    case OSPF6_LSTYPE_NETWORK:
+      if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type))
+        {
+          zlog_debug ("LSA disappearing: %s", lsa->name);
+          zlog_debug ("Schedule SPF Calculation for %s",
+                     OSPF6_AREA (lsa->lsdb->data)->name);
+        }
+      ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6),
+                         ospf6_lsremove_to_spf_reason(lsa));
+      break;
+
+    case OSPF6_LSTYPE_INTRA_PREFIX:
+      ospf6_intra_prefix_lsa_remove (lsa);
+      break;
+
+    case OSPF6_LSTYPE_INTER_PREFIX:
+    case OSPF6_LSTYPE_INTER_ROUTER:
+      ospf6_abr_examin_summary (lsa, (struct ospf6_area *) lsa->lsdb->data);
+      break;
+
+    default:
+      break;
+    }
+}
+
+static void
+ospf6_area_route_hook_add (struct ospf6_route *route)
+{
+  struct ospf6_route *copy = ospf6_route_copy (route);
+  ospf6_route_add (copy, ospf6->route_table);
+}
+
+static void
+ospf6_area_route_hook_remove (struct ospf6_route *route)
+{
+  struct ospf6_route *copy;
+
+  copy = ospf6_route_lookup_identical (route, ospf6->route_table);
+  if (copy)
+    ospf6_route_remove (copy, ospf6->route_table);
+}
+
+/* Make new area structure */
+struct ospf6_area *
+ospf6_area_create (u_int32_t area_id, struct ospf6 *o)
+{
+  struct ospf6_area *oa;
+  struct ospf6_route *route;
+
+  oa = XCALLOC (MTYPE_OSPF6_AREA, sizeof (struct ospf6_area));
+
+  inet_ntop (AF_INET, &area_id, oa->name, sizeof (oa->name));
+  oa->area_id = area_id;
+  oa->if_list = list_new ();
+
+  oa->lsdb = ospf6_lsdb_create (oa);
+  oa->lsdb->hook_add = ospf6_area_lsdb_hook_add;
+  oa->lsdb->hook_remove = ospf6_area_lsdb_hook_remove;
+  oa->lsdb_self = ospf6_lsdb_create (oa);
+
+  oa->spf_table = OSPF6_ROUTE_TABLE_CREATE (AREA, SPF_RESULTS);
+  oa->spf_table->scope = oa;
+  oa->route_table = OSPF6_ROUTE_TABLE_CREATE (AREA, ROUTES);
+  oa->route_table->scope = oa;
+  oa->route_table->hook_add = ospf6_area_route_hook_add;
+  oa->route_table->hook_remove = ospf6_area_route_hook_remove;
+
+  oa->range_table = OSPF6_ROUTE_TABLE_CREATE (AREA, PREFIX_RANGES);
+  oa->range_table->scope = oa;
+  oa->summary_prefix = OSPF6_ROUTE_TABLE_CREATE (AREA, SUMMARY_PREFIXES);
+  oa->summary_prefix->scope = oa;
+  oa->summary_router = OSPF6_ROUTE_TABLE_CREATE (AREA, SUMMARY_ROUTERS);
+  oa->summary_router->scope = oa;
+
+  /* set default options */
+  if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER))
+    {
+      OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6);
+      OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R);
+    }
+  else
+    {
+      OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6);
+      OSPF6_OPT_SET (oa->options, OSPF6_OPT_R);
+    }
+
+  OSPF6_OPT_SET (oa->options, OSPF6_OPT_E);
+
+  oa->ospf6 = o;
+  listnode_add_sort (o->area_list, oa);
+
+  /* import athoer area's routes as inter-area routes */
+  for (route = ospf6_route_head (o->route_table); route;
+       route = ospf6_route_next (route))
+    ospf6_abr_originate_summary_to_area (route, oa);
+
+  return oa;
+}
+
+void
+ospf6_area_delete (struct ospf6_area *oa)
+{
+  struct listnode *n;
+  struct ospf6_interface *oi;
+
+  ospf6_route_table_delete (oa->range_table);
+  ospf6_route_table_delete (oa->summary_prefix);
+  ospf6_route_table_delete (oa->summary_router);
+
+  /* The ospf6_interface structs store configuration
+   * information which should not be lost/reset when
+   * deleting an area.
+   * So just detach the interface from the area and
+   * keep it around. */
+  for (ALL_LIST_ELEMENTS_RO (oa->if_list, n, oi))
+    oi->area = NULL;
+
+  list_delete (oa->if_list);
+
+  ospf6_lsdb_delete (oa->lsdb);
+  ospf6_lsdb_delete (oa->lsdb_self);
+
+  ospf6_spf_table_finish (oa->spf_table);
+  ospf6_route_table_delete (oa->spf_table);
+  ospf6_route_table_delete (oa->route_table);
+
+  THREAD_OFF (oa->thread_spf_calculation);
+  THREAD_OFF (oa->thread_route_calculation);
+
+  listnode_delete (oa->ospf6->area_list, oa);
+  oa->ospf6 = NULL;
+
+  /* free area */
+  XFREE (MTYPE_OSPF6_AREA, oa);
+}
+
+struct ospf6_area *
+ospf6_area_lookup (u_int32_t area_id, struct ospf6 *ospf6)
+{
+  struct ospf6_area *oa;
+  struct listnode *n;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, n, oa))
+    if (oa->area_id == area_id)
+      return oa;
+
+  return (struct ospf6_area *) NULL;
+}
+
+static struct ospf6_area *
+ospf6_area_get (u_int32_t area_id, struct ospf6 *o)
+{
+  struct ospf6_area *oa;
+  oa = ospf6_area_lookup (area_id, o);
+  if (oa == NULL)
+    oa = ospf6_area_create (area_id, o);
+  return oa;
+}
+
+void
+ospf6_area_enable (struct ospf6_area *oa)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_interface *oi;
+
+  SET_FLAG (oa->flag, OSPF6_AREA_ENABLE);
+
+  for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
+    ospf6_interface_enable (oi);
+  ospf6_abr_enable_area (oa);
+}
+
+void
+ospf6_area_disable (struct ospf6_area *oa)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_interface *oi;
+
+  UNSET_FLAG (oa->flag, OSPF6_AREA_ENABLE);
+
+  for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
+    ospf6_interface_disable (oi);
+
+  ospf6_abr_disable_area (oa);
+  ospf6_lsdb_remove_all (oa->lsdb);
+  ospf6_lsdb_remove_all (oa->lsdb_self);
+
+  ospf6_spf_table_finish(oa->spf_table);
+  ospf6_route_remove_all(oa->route_table);
+
+  THREAD_OFF (oa->thread_spf_calculation);
+  THREAD_OFF (oa->thread_route_calculation);
+
+  THREAD_OFF (oa->thread_router_lsa);
+  THREAD_OFF (oa->thread_intra_prefix_lsa);
+}
+
+
+void
+ospf6_area_show (struct vty *vty, struct ospf6_area *oa)
+{
+  struct listnode *i;
+  struct ospf6_interface *oi;
+
+  vty_out (vty, " Area %s%s", oa->name, VNL);
+  vty_out (vty, "     Number of Area scoped LSAs is %u%s",
+           oa->lsdb->count, VNL);
+
+  vty_out (vty, "     Interface attached to this area:");
+  for (ALL_LIST_ELEMENTS_RO (oa->if_list, i, oi))
+    vty_out (vty, " %s", oi->interface->name);
+  
+  vty_out (vty, "%s", VNL);
+}
+
+
+#define OSPF6_CMD_AREA_LOOKUP(str, oa)                     \
+{                                                          \
+  u_int32_t area_id = 0;                                   \
+  if (inet_pton (AF_INET, str, &area_id) != 1)             \
+    {                                                      \
+      vty_out (vty, "Malformed Area-ID: %s%s", str, VNL);  \
+      return CMD_SUCCESS;                                  \
+    }                                                      \
+  oa = ospf6_area_lookup (area_id, ospf6);                 \
+  if (oa == NULL)                                          \
+    {                                                      \
+      vty_out (vty, "No such Area: %s%s", str, VNL);       \
+      return CMD_SUCCESS;                                  \
+    }                                                      \
+}
+
+#define OSPF6_CMD_AREA_GET(str, oa)                        \
+{                                                          \
+  u_int32_t area_id = 0;                                   \
+  if (inet_pton (AF_INET, str, &area_id) != 1)             \
+    {                                                      \
+      vty_out (vty, "Malformed Area-ID: %s%s", str, VNL);  \
+      return CMD_SUCCESS;                                  \
+    }                                                      \
+  oa = ospf6_area_get (area_id, ospf6);                    \
+}
+
+DEFUN (area_range,
+       area_range_cmd,
+       "area A.B.C.D range X:X::X:X/M",
+       "OSPF area parameters\n"
+       OSPF6_AREA_ID_STR
+       "Configured address range\n"
+       "Specify IPv6 prefix\n"
+       )
+{
+  int ret;
+  struct ospf6_area *oa;
+  struct prefix prefix;
+  struct ospf6_route *range;
+
+  OSPF6_CMD_AREA_GET (argv[0], oa);
+  argc--;
+  argv++;
+
+  ret = str2prefix (argv[0], &prefix);
+  if (ret != 1 || prefix.family != AF_INET6)
+    {
+      vty_out (vty, "Malformed argument: %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+  argc--;
+  argv++;
+
+  range = ospf6_route_lookup (&prefix, oa->range_table);
+  if (range == NULL)
+    {
+      range = ospf6_route_create ();
+      range->type = OSPF6_DEST_TYPE_RANGE;
+      range->prefix = prefix;
+    }
+
+  if (argc)
+    {
+      if (! strcmp (argv[0], "not-advertise"))
+        SET_FLAG (range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE);
+      else if (! strcmp (argv[0], "advertise"))
+        UNSET_FLAG (range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE);
+    }
+
+  if (range->rnode)
+    {
+      vty_out (vty, "Range already defined: %s%s", argv[-1], VNL);
+      return CMD_WARNING;
+    }
+
+  ospf6_route_add (range, oa->range_table);
+  return CMD_SUCCESS;
+}
+
+ALIAS (area_range,
+       area_range_advertise_cmd,
+       "area A.B.C.D range X:X::X:X/M (advertise|not-advertise)",
+       "OSPF area parameters\n"
+       OSPF6_AREA_ID_STR
+       "Configured address range\n"
+       "Specify IPv6 prefix\n"
+       )
+
+DEFUN (no_area_range,
+       no_area_range_cmd,
+       "no area A.B.C.D range X:X::X:X/M",
+       "OSPF area parameters\n"
+       OSPF6_AREA_ID_STR
+       "Configured address range\n"
+       "Specify IPv6 prefix\n"
+       )
+{
+  int ret;
+  struct ospf6_area *oa;
+  struct prefix prefix;
+  struct ospf6_route *range;
+
+  OSPF6_CMD_AREA_GET (argv[0], oa);
+  argc--;
+  argv++;
+
+  ret = str2prefix (argv[0], &prefix);
+  if (ret != 1 || prefix.family != AF_INET6)
+    {
+      vty_out (vty, "Malformed argument: %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  range = ospf6_route_lookup (&prefix, oa->range_table);
+  if (range == NULL)
+    {
+      vty_out (vty, "Range %s does not exists.%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  ospf6_route_remove (range, oa->range_table);
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf6_area_config_write (struct vty *vty)
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+  struct ospf6_route *range;
+  char buf[128];
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+    {
+      for (range = ospf6_route_head (oa->range_table); range;
+           range = ospf6_route_next (range))
+        {
+          prefix2str (&range->prefix, buf, sizeof (buf));
+          vty_out (vty, " area %s range %s%s", oa->name, buf, VNL);
+        }
+      if (PREFIX_NAME_IN (oa))
+        vty_out (vty, " area %s filter-list prefix %s in%s",
+                 oa->name, PREFIX_NAME_IN (oa), VNL);
+      if (PREFIX_NAME_OUT (oa))
+        vty_out (vty, " area %s filter-list prefix %s out%s",
+                 oa->name, PREFIX_NAME_OUT (oa), VNL);
+      if (IMPORT_NAME (oa))
+        vty_out (vty, " area %s import-list %s%s",
+                 oa->name, IMPORT_NAME (oa), VNL);
+      if (EXPORT_NAME (oa))
+        vty_out (vty, " area %s export-list %s%s",
+                 oa->name, EXPORT_NAME (oa), VNL);
+    }
+}
+
+DEFUN (area_filter_list,
+       area_filter_list_cmd,
+       "area A.B.C.D filter-list prefix WORD (in|out)",
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Filter networks between OSPFv6 areas\n"
+       "Filter prefixes between OSPFv6 areas\n"
+       "Name of an IPv6 prefix-list\n"
+       "Filter networks sent to this area\n"
+       "Filter networks sent from this area\n")
+{
+  struct ospf6_area *area;
+  struct prefix_list *plist;
+
+  OSPF6_CMD_AREA_GET (argv[0], area);
+  argc--;
+  argv++;
+
+  plist = prefix_list_lookup (AFI_IP6, argv[0]);
+  if (strncmp (argv[1], "in", 2) == 0)
+    {
+      PREFIX_LIST_IN (area) = plist;
+      if (PREFIX_NAME_IN (area))
+       free (PREFIX_NAME_IN (area));
+
+      PREFIX_NAME_IN (area) = strdup (argv[0]);
+      ospf6_abr_reimport (area);
+    }
+  else
+    {
+      PREFIX_LIST_OUT (area) = plist;
+      if (PREFIX_NAME_OUT (area))
+       free (PREFIX_NAME_OUT (area));
+
+      PREFIX_NAME_OUT (area) = strdup (argv[0]);
+      ospf6_abr_enable_area (area);
+    }
+
+  return CMD_SUCCESS;
+}
+     
+DEFUN (no_area_filter_list,
+       no_area_filter_list_cmd,
+       "no area A.B.C.D filter-list prefix WORD (in|out)",
+       NO_STR
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Filter networks between OSPFv6 areas\n"
+       "Filter prefixes between OSPFv6 areas\n"
+       "Name of an IPv6 prefix-list\n"
+       "Filter networks sent to this area\n"
+       "Filter networks sent from this area\n")
+{
+  struct ospf6_area *area;
+
+  OSPF6_CMD_AREA_GET (argv[0], area);
+  argc--;
+  argv++;
+
+  if (strncmp (argv[1], "in", 2) == 0)
+    {
+      if (PREFIX_NAME_IN (area))
+       if (strcmp (PREFIX_NAME_IN (area), argv[0]) != 0)
+         return CMD_SUCCESS;
+
+      PREFIX_LIST_IN (area) = NULL;
+      if (PREFIX_NAME_IN (area))
+       free (PREFIX_NAME_IN (area));
+
+      PREFIX_NAME_IN (area) = NULL;
+      ospf6_abr_reimport (area);
+    }
+  else
+    {
+      if (PREFIX_NAME_OUT (area))
+       if (strcmp (PREFIX_NAME_OUT (area), argv[0]) != 0)
+         return CMD_SUCCESS;
+
+      PREFIX_LIST_OUT (area) = NULL;
+      if (PREFIX_NAME_OUT (area))
+       free (PREFIX_NAME_OUT (area));
+
+      PREFIX_NAME_OUT (area) = NULL;
+      ospf6_abr_enable_area (area);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (area_import_list,
+       area_import_list_cmd,
+       "area A.B.C.D import-list NAME",
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Set the filter for networks from other areas announced to the specified one\n"
+       "Name of the acess-list\n")
+{
+  struct ospf6_area *area;
+  struct access_list *list;
+
+  OSPF6_CMD_AREA_GET(argv[0], area);
+
+  list = access_list_lookup (AFI_IP6, argv[1]);
+
+  IMPORT_LIST (area) = list;
+
+  if (IMPORT_NAME (area))
+    free (IMPORT_NAME (area));
+
+  IMPORT_NAME (area) = strdup (argv[1]);
+  ospf6_abr_reimport (area);
+
+  return CMD_SUCCESS; 
+}
+
+DEFUN (no_area_import_list,
+       no_area_import_list_cmd,
+       "no area A.B.C.D import-list NAME",
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Unset the filter for networks announced to other areas\n"
+       "NAme of the access-list\n")
+{
+  struct ospf6_area *area;
+
+  OSPF6_CMD_AREA_GET(argv[0], area);
+
+  IMPORT_LIST (area) = 0;
+
+  if (IMPORT_NAME (area))
+    free (IMPORT_NAME (area));
+
+  IMPORT_NAME (area) = NULL;
+  ospf6_abr_reimport (area);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (area_export_list,
+       area_export_list_cmd,
+       "area A.B.C.D export-list NAME",
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Set the filter for networks announced to other areas\n"
+       "Name of the acess-list\n")
+{
+  struct ospf6_area *area;
+  struct access_list *list;
+
+  OSPF6_CMD_AREA_GET(argv[0], area);
+
+  list = access_list_lookup (AFI_IP6, argv[1]);
+
+  EXPORT_LIST (area) = list;
+
+  if (EXPORT_NAME (area))
+    free (EXPORT_NAME (area));
+
+  EXPORT_NAME (area) = strdup (argv[1]);
+  ospf6_abr_enable_area (area);
+
+  return CMD_SUCCESS; 
+}
+
+DEFUN (no_area_export_list,
+       no_area_export_list_cmd,
+       "no area A.B.C.D export-list NAME",
+       "OSPFv6 area parameters\n"
+       "OSPFv6 area ID in IP address format\n"
+       "Unset the filter for networks announced to other areas\n"
+       "Name of the access-list\n")
+{
+  struct ospf6_area *area;
+
+  OSPF6_CMD_AREA_GET(argv[0], area);
+
+  EXPORT_LIST (area) = 0;
+
+  if (EXPORT_NAME (area))
+    free (EXPORT_NAME (area));
+
+  EXPORT_NAME (area) = NULL;
+  ospf6_abr_enable_area (area);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ospf6_spf_tree,
+       show_ipv6_ospf6_spf_tree_cmd,
+       "show ipv6 ospf6 spf tree",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Shortest Path First caculation\n"
+       "Show SPF tree\n")
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+  struct ospf6_vertex *root;
+  struct ospf6_route *route;
+  struct prefix prefix;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_linkstate_prefix (ospf6->router_id, htonl (0), &prefix);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+    {
+      route = ospf6_route_lookup (&prefix, oa->spf_table);
+      if (route == NULL)
+        {
+          vty_out (vty, "LS entry for root not found in area %s%s",
+                   oa->name, VNL);
+          continue;
+        }
+      root = (struct ospf6_vertex *) route->route_option;
+      ospf6_spf_display_subtree (vty, "", 0, root);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ospf6_area_spf_tree,
+       show_ipv6_ospf6_area_spf_tree_cmd,
+       "show ipv6 ospf6 area A.B.C.D spf tree",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       OSPF6_AREA_STR
+       OSPF6_AREA_ID_STR
+       "Shortest Path First caculation\n"
+       "Show SPF tree\n")
+{
+  u_int32_t area_id;
+  struct ospf6_area *oa;
+  struct ospf6_vertex *root;
+  struct ospf6_route *route;
+  struct prefix prefix;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_linkstate_prefix (ospf6->router_id, htonl (0), &prefix);
+
+  if (inet_pton (AF_INET, argv[0], &area_id) != 1)
+    {
+      vty_out (vty, "Malformed Area-ID: %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+  oa = ospf6_area_lookup (area_id, ospf6);
+  if (oa == NULL)
+    {
+      vty_out (vty, "No such Area: %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  route = ospf6_route_lookup (&prefix, oa->spf_table);
+  if (route == NULL)
+    {
+      vty_out (vty, "LS entry for root not found in area %s%s",
+               oa->name, VNL);
+      return CMD_SUCCESS;
+    }
+  root = (struct ospf6_vertex *) route->route_option;
+  ospf6_spf_display_subtree (vty, "", 0, root);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ospf6_simulate_spf_tree_root,
+       show_ipv6_ospf6_simulate_spf_tree_root_cmd,
+       "show ipv6 ospf6 simulate spf-tree A.B.C.D area A.B.C.D",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Shortest Path First caculation\n"
+       "Show SPF tree\n"
+       "Specify root's router-id to calculate another router's SPF tree\n")
+{
+  u_int32_t area_id;
+  struct ospf6_area *oa;
+  struct ospf6_vertex *root;
+  struct ospf6_route *route;
+  struct prefix prefix;
+  u_int32_t router_id;
+  struct ospf6_route_table *spf_table;
+  unsigned char tmp_debug_ospf6_spf = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  inet_pton (AF_INET, argv[0], &router_id);
+  ospf6_linkstate_prefix (router_id, htonl (0), &prefix);
+
+  if (inet_pton (AF_INET, argv[1], &area_id) != 1)
+    {
+      vty_out (vty, "Malformed Area-ID: %s%s", argv[1], VNL);
+      return CMD_SUCCESS;
+    }
+  oa = ospf6_area_lookup (area_id, ospf6);
+  if (oa == NULL)
+    {
+      vty_out (vty, "No such Area: %s%s", argv[1], VNL);
+      return CMD_SUCCESS;
+    }
+
+  tmp_debug_ospf6_spf = conf_debug_ospf6_spf;
+  conf_debug_ospf6_spf = 0;
+
+  spf_table = OSPF6_ROUTE_TABLE_CREATE (NONE, SPF_RESULTS);
+  ospf6_spf_calculation (router_id, spf_table, oa);
+
+  conf_debug_ospf6_spf = tmp_debug_ospf6_spf;
+
+  route = ospf6_route_lookup (&prefix, spf_table);
+  if (route == NULL)
+    {
+      ospf6_spf_table_finish (spf_table);
+      ospf6_route_table_delete (spf_table);
+      return CMD_SUCCESS;
+    }
+  root = (struct ospf6_vertex *) route->route_option;
+  ospf6_spf_display_subtree (vty, "", 0, root);
+
+  ospf6_spf_table_finish (spf_table);
+  ospf6_route_table_delete (spf_table);
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf6_area_init (void)
+{
+  install_element (VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_area_spf_tree_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd);
+
+  install_element (OSPF6_NODE, &area_range_cmd);
+  install_element (OSPF6_NODE, &area_range_advertise_cmd);
+  install_element (OSPF6_NODE, &no_area_range_cmd);
+
+  install_element (OSPF6_NODE, &area_import_list_cmd);
+  install_element (OSPF6_NODE, &no_area_import_list_cmd);
+  install_element (OSPF6_NODE, &area_export_list_cmd);
+  install_element (OSPF6_NODE, &no_area_export_list_cmd);
+
+  install_element (OSPF6_NODE, &area_filter_list_cmd);
+  install_element (OSPF6_NODE, &no_area_filter_list_cmd);
+
+}
+
+
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
new file mode 100644 (file)
index 0000000..655967a
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF_AREA_H
+#define OSPF_AREA_H
+
+#include "ospf6_top.h"
+
+struct ospf6_area
+{
+  /* Reference to Top data structure */
+  struct ospf6 *ospf6;
+
+  /* Area-ID */
+  u_int32_t area_id;
+
+  /* Area-ID string */
+  char name[16];
+
+  /* flag */
+  u_char flag;
+
+  /* OSPF Option */
+  u_char options[3];
+
+  /* Summary routes to be originated (includes Configured Address Ranges) */
+  struct ospf6_route_table *range_table;
+  struct ospf6_route_table *summary_prefix;
+  struct ospf6_route_table *summary_router;
+
+  /* OSPF interface list */
+  struct list *if_list;
+
+  struct ospf6_lsdb *lsdb;
+  struct ospf6_lsdb *lsdb_self;
+
+  struct ospf6_route_table *spf_table;
+  struct ospf6_route_table *route_table;
+
+  struct thread  *thread_spf_calculation;
+  struct thread  *thread_route_calculation;
+  u_int32_t spf_calculation;   /* SPF calculation count */
+
+  struct thread *thread_router_lsa;
+  struct thread *thread_intra_prefix_lsa;
+  u_int32_t router_lsa_size_limit;
+
+  /* Area announce list */
+  struct
+  {
+    char *name;
+    struct access_list *list;
+  } _export;
+#define EXPORT_NAME(A)  (A)->_export.name
+#define EXPORT_LIST(A)  (A)->_export.list
+
+  /* Area acceptance list */
+  struct
+  {
+    char *name;
+    struct access_list *list;
+  } import;
+#define IMPORT_NAME(A)  (A)->import.name
+#define IMPORT_LIST(A)  (A)->import.list
+
+  /* Type 3 LSA Area prefix-list */
+  struct
+  {
+    char *name;
+    struct prefix_list *list;
+  } plist_in;
+#define PREFIX_NAME_IN(A)  (A)->plist_in.name
+#define PREFIX_LIST_IN(A)  (A)->plist_in.list
+
+  struct
+  {
+    char *name;
+    struct prefix_list *list;
+  } plist_out;
+#define PREFIX_NAME_OUT(A)  (A)->plist_out.name
+#define PREFIX_LIST_OUT(A)  (A)->plist_out.list
+
+};
+
+#define OSPF6_AREA_ENABLE     0x01
+#define OSPF6_AREA_ACTIVE     0x02
+#define OSPF6_AREA_TRANSIT    0x04 /* TransitCapability */
+#define OSPF6_AREA_STUB       0x08
+
+#define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE))
+#define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE))
+#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT))
+#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB))
+
+/* prototypes */
+extern int ospf6_area_cmp (void *va, void *vb);
+
+extern struct ospf6_area *ospf6_area_create (u_int32_t, struct ospf6 *);
+extern void ospf6_area_delete (struct ospf6_area *);
+extern struct ospf6_area *ospf6_area_lookup (u_int32_t, struct ospf6 *);
+
+extern void ospf6_area_enable (struct ospf6_area *);
+extern void ospf6_area_disable (struct ospf6_area *);
+
+extern void ospf6_area_show (struct vty *, struct ospf6_area *);
+
+extern void ospf6_area_config_write (struct vty *vty);
+extern void ospf6_area_init (void);
+
+#endif /* OSPF_AREA_H */
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
new file mode 100644 (file)
index 0000000..a442506
--- /dev/null
@@ -0,0 +1,1580 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "vty.h"
+#include "routemap.h"
+#include "table.h"
+#include "plist.h"
+#include "thread.h"
+#include "linklist.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_zebra.h"
+#include "ospf6_message.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_asbr.h"
+#include "ospf6_intra.h"
+#include "ospf6_flood.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_asbr = 0;
+
+#define ZROUTE_NAME(x) zebra_route_string(x)
+
+/* AS External LSA origination */
+static void
+ospf6_as_external_lsa_originate (struct ospf6_route *route)
+{
+  char buffer[OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *lsa;
+  struct ospf6_external_info *info = route->route_option;
+
+  struct ospf6_as_external_lsa *as_external_lsa;
+  char buf[64];
+  caddr_t p;
+
+  if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE (AS_EXTERNAL))
+    {
+      prefix2str (&route->prefix, buf, sizeof (buf));
+      zlog_debug ("Originate AS-External-LSA for %s", buf);
+    }
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  as_external_lsa = (struct ospf6_as_external_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+  p = (caddr_t)
+    ((caddr_t) as_external_lsa + sizeof (struct ospf6_as_external_lsa));
+
+  /* Fill AS-External-LSA */
+  /* Metric type */
+  if (route->path.metric_type == 2)
+    SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+  else
+    UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+
+  /* forwarding address */
+  if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding))
+    SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+  else
+    UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+
+  /* external route tag */
+  if (info->tag)
+    SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+  else
+    UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+
+  /* Set metric */
+  OSPF6_ASBR_METRIC_SET (as_external_lsa, route->path.cost);
+
+  /* prefixlen */
+  as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
+
+  /* PrefixOptions */
+  as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+
+  /* don't use refer LS-type */
+  as_external_lsa->prefix.prefix_refer_lstype = htons (0);
+
+  /* set Prefix */
+  memcpy (p, &route->prefix.u.prefix6,
+          OSPF6_PREFIX_SPACE (route->prefix.prefixlen));
+  ospf6_prefix_apply_mask (&as_external_lsa->prefix);
+  p += OSPF6_PREFIX_SPACE (route->prefix.prefixlen);
+
+  /* Forwarding address */
+  if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F))
+    {
+      memcpy (p, &info->forwarding, sizeof (struct in6_addr));
+      p += sizeof (struct in6_addr);
+    }
+
+  /* External Route Tag */
+  if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T))
+    {
+      route_tag_t network_order = htonl(info->tag);
+
+      memcpy (p, &network_order, sizeof(network_order));
+      p += sizeof(network_order);
+    }
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
+  lsa_header->id = route->path.origin.id;
+  lsa_header->adv_router = ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, ospf6->lsdb);
+  lsa_header->length = htons ((caddr_t) p - (caddr_t) lsa_header);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_process (lsa, ospf6);
+}
+
+static route_tag_t
+ospf6_as_external_lsa_get_tag (struct ospf6_lsa *lsa)
+{
+  struct ospf6_as_external_lsa *external;
+  ptrdiff_t tag_offset;
+  route_tag_t network_order;
+
+  if (!lsa)
+    return 0;
+
+  external = (struct ospf6_as_external_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  if (!CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T))
+    return 0;
+
+  tag_offset = sizeof(*external) + OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F))
+    tag_offset += sizeof(struct in6_addr);
+
+  memcpy(&network_order, (caddr_t)external + tag_offset, sizeof(network_order));
+  return ntohl(network_order);
+}
+
+void
+ospf6_asbr_lsa_add (struct ospf6_lsa *lsa)
+{
+  struct ospf6_as_external_lsa *external;
+  struct prefix asbr_id;
+  struct ospf6_route *asbr_entry, *route;
+  char buf[64];
+  int i;
+
+  external = (struct ospf6_as_external_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+    zlog_debug ("Calculate AS-External route for %s", lsa->name);
+
+  if (lsa->header->adv_router == ospf6->router_id)
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        zlog_debug ("Ignore self-originated AS-External-LSA");
+      return;
+    }
+
+  if (OSPF6_ASBR_METRIC (external) == OSPF_LS_INFINITY)
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        zlog_debug ("Ignore LSA with LSInfinity Metric");
+      return;
+    }
+
+  if (CHECK_FLAG(external->prefix.prefix_options, OSPF6_PREFIX_OPTION_NU))
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        zlog_debug ("Ignore LSA with NU bit set Metric");
+      return;
+    }
+
+  ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &asbr_id);
+  asbr_entry = ospf6_route_lookup (&asbr_id, ospf6->brouter_table);
+  if (asbr_entry == NULL ||
+      ! CHECK_FLAG (asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E))
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        {
+          prefix2str (&asbr_id, buf, sizeof (buf));
+          zlog_debug ("ASBR entry not found: %s", buf);
+        }
+      return;
+    }
+
+  route = ospf6_route_create ();
+  route->type = OSPF6_DEST_TYPE_NETWORK;
+  route->prefix.family = AF_INET6;
+  route->prefix.prefixlen = external->prefix.prefix_length;
+  ospf6_prefix_in6_addr (&route->prefix.u.prefix6, &external->prefix);
+
+  route->path.area_id = asbr_entry->path.area_id;
+  route->path.origin.type = lsa->header->type;
+  route->path.origin.id = lsa->header->id;
+  route->path.origin.adv_router = lsa->header->adv_router;
+
+  route->path.prefix_options = external->prefix.prefix_options;
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E))
+    {
+      route->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+      route->path.metric_type = 2;
+      route->path.cost = asbr_entry->path.cost;
+      route->path.cost_e2 = OSPF6_ASBR_METRIC (external);
+    }
+  else
+    {
+      route->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+      route->path.metric_type = 1;
+      route->path.cost = asbr_entry->path.cost + OSPF6_ASBR_METRIC (external);
+      route->path.cost_e2 = 0;
+    }
+
+  route->path.tag = ospf6_as_external_lsa_get_tag (lsa);
+  
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
+    ospf6_nexthop_copy (&route->nexthop[i], &asbr_entry->nexthop[i]);
+
+  if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+    {
+      prefix2str (&route->prefix, buf, sizeof (buf));
+      zlog_debug ("AS-External route add: %s", buf);
+    }
+
+  ospf6_route_add (route, ospf6->route_table);
+}
+
+void
+ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa)
+{
+  struct ospf6_as_external_lsa *external;
+  struct prefix prefix;
+  struct ospf6_route *route, *nroute;
+  char buf[64];
+
+  external = (struct ospf6_as_external_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+    zlog_debug ("Withdraw AS-External route for %s", lsa->name);
+
+  if (lsa->header->adv_router == ospf6->router_id)
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        zlog_debug ("Ignore self-originated AS-External-LSA");
+      return;
+    }
+
+  memset (&prefix, 0, sizeof (struct prefix));
+  prefix.family = AF_INET6;
+  prefix.prefixlen = external->prefix.prefix_length;
+  ospf6_prefix_in6_addr (&prefix.u.prefix6, &external->prefix);
+
+  route = ospf6_route_lookup (&prefix, ospf6->route_table);
+  if (route == NULL)
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        {
+          prefix2str (&prefix, buf, sizeof (buf));
+          zlog_debug ("AS-External route %s not found", buf);
+        }
+      return;
+    }
+
+  for (ospf6_route_lock (route);
+       route && ospf6_route_is_prefix (&prefix, route);
+       route = nroute)
+    {
+      nroute = ospf6_route_next (route);
+      if (route->type != OSPF6_DEST_TYPE_NETWORK)
+        continue;
+      if (route->path.origin.type != lsa->header->type)
+        continue;
+      if (route->path.origin.id != lsa->header->id)
+        continue;
+      if (route->path.origin.adv_router != lsa->header->adv_router)
+        continue;
+
+      if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
+        {
+          prefix2str (&route->prefix, buf, sizeof (buf));
+          zlog_debug ("AS-External route remove: %s", buf);
+        }
+      ospf6_route_remove (route, ospf6->route_table);
+    }
+  if (route != NULL)
+    ospf6_route_unlock (route);
+}
+
+void
+ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry)
+{
+  struct ospf6_lsa *lsa;
+  u_int16_t type;
+  u_int32_t router;
+
+  if (! CHECK_FLAG (asbr_entry->flag, OSPF6_ROUTE_BEST))
+    {
+      char buf[16];
+      inet_ntop (AF_INET, &ADV_ROUTER_IN_PREFIX (&asbr_entry->prefix),
+                 buf, sizeof (buf));
+       zlog_info ("ignore non-best path: lsentry %s add", buf);
+      return;
+    }
+
+  type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
+  router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
+  for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb); lsa;
+       lsa = ospf6_lsdb_type_router_next (type, router, lsa))
+    {
+      if (! OSPF6_LSA_IS_MAXAGE (lsa))
+        ospf6_asbr_lsa_add (lsa);
+    }
+}
+
+void
+ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry)
+{
+  struct ospf6_lsa *lsa;
+  u_int16_t type;
+  u_int32_t router;
+
+  type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
+  router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
+  for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb);
+       lsa; lsa = ospf6_lsdb_type_router_next (type, router, lsa))
+    ospf6_asbr_lsa_remove (lsa);
+}
+
+
+
+/* redistribute function */
+
+static void
+ospf6_asbr_routemap_set (int type, const char *mapname)
+{
+  if (ospf6->rmap[type].name)
+    free (ospf6->rmap[type].name);
+  ospf6->rmap[type].name = strdup (mapname);
+  ospf6->rmap[type].map = route_map_lookup_by_name (mapname);
+}
+
+static void
+ospf6_asbr_routemap_unset (int type)
+{
+  if (ospf6->rmap[type].name)
+    free (ospf6->rmap[type].name);
+  ospf6->rmap[type].name = NULL;
+  ospf6->rmap[type].map = NULL;
+}
+
+static void
+ospf6_asbr_routemap_update (const char *mapname)
+{
+  int type;
+
+  if (ospf6 == NULL)
+    return;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (ospf6->rmap[type].name)
+        ospf6->rmap[type].map =
+          route_map_lookup_by_name (ospf6->rmap[type].name);
+      else
+        ospf6->rmap[type].map = NULL;
+    }
+}
+
+int
+ospf6_asbr_is_asbr (struct ospf6 *o)
+{
+  return o->external_table->count;
+}
+
+static void
+ospf6_asbr_redistribute_set (int type)
+{
+  ospf6_zebra_redistribute (type);
+}
+
+static void
+ospf6_asbr_redistribute_unset (int type)
+{
+  struct ospf6_route *route;
+  struct ospf6_external_info *info;
+
+  ospf6_zebra_no_redistribute (type);
+
+  for (route = ospf6_route_head (ospf6->external_table); route;
+       route = ospf6_route_next (route))
+    {
+      info = route->route_option;
+      if (info->type != type)
+        continue;
+
+      ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex,
+                                      &route->prefix);
+    }
+
+  ospf6_asbr_routemap_unset (type);
+}
+
+void
+ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix,
+                             u_int nexthop_num, struct in6_addr *nexthop, route_tag_t tag)
+{
+  int ret;
+  struct ospf6_route troute;
+  struct ospf6_external_info tinfo;
+  struct ospf6_route *route, *match;
+  struct ospf6_external_info *info;
+  struct prefix prefix_id;
+  struct route_node *node;
+  char pbuf[64], ibuf[16];
+  struct listnode *lnode, *lnnode;
+  struct ospf6_area *oa;
+
+  if (! ospf6_zebra_is_redistribute (type))
+    return;
+
+  if (IS_OSPF6_DEBUG_ASBR)
+    {
+      prefix2str (prefix, pbuf, sizeof (pbuf));
+      zlog_debug ("Redistribute %s (%s)", pbuf, ZROUTE_NAME (type));
+    }
+
+  /* if route-map was specified but not found, do not advertise */
+  if (ospf6->rmap[type].name)
+    {
+      if (ospf6->rmap[type].map == NULL)
+        ospf6_asbr_routemap_update (NULL);
+      if (ospf6->rmap[type].map == NULL)
+        {
+          zlog_warn ("route-map \"%s\" not found, suppress redistributing",
+                     ospf6->rmap[type].name);
+          return;
+        }
+    }
+
+  /* apply route-map */
+  if (ospf6->rmap[type].map)
+    {
+      memset (&troute, 0, sizeof (troute));
+      memset (&tinfo, 0, sizeof (tinfo));
+      troute.route_option = &tinfo;
+      tinfo.ifindex = ifindex;
+      tinfo.tag = tag;
+
+      ret = route_map_apply (ospf6->rmap[type].map, prefix,
+                             RMAP_OSPF6, &troute);
+      if (ret == RMAP_DENYMATCH)
+        {
+          if (IS_OSPF6_DEBUG_ASBR)
+            zlog_debug ("Denied by route-map \"%s\"", ospf6->rmap[type].name);
+          return;
+        }
+    }
+
+  match = ospf6_route_lookup (prefix, ospf6->external_table);
+  if (match)
+    {
+      info = match->route_option;
+
+      /* copy result of route-map */
+      if (ospf6->rmap[type].map)
+        {
+          if (troute.path.metric_type)
+            match->path.metric_type = troute.path.metric_type;
+          if (troute.path.cost)
+            match->path.cost = troute.path.cost;
+          if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
+            memcpy (&info->forwarding, &tinfo.forwarding,
+                    sizeof (struct in6_addr));
+          info->tag = tinfo.tag;
+        }
+      else
+        {
+          /* If there is no route-map, simply update the tag */
+          info->tag = tag;
+        }
+
+      info->type = type;
+      match->nexthop[0].ifindex = ifindex;
+      if (nexthop_num && nexthop)
+        memcpy (&match->nexthop[0].address, nexthop, sizeof (struct in6_addr));
+
+      /* create/update binding in external_id_table */
+      prefix_id.family = AF_INET;
+      prefix_id.prefixlen = 32;
+      prefix_id.u.prefix4.s_addr = htonl (info->id);
+      node = route_node_get (ospf6->external_id_table, &prefix_id);
+      node->info = match;
+
+      if (IS_OSPF6_DEBUG_ASBR)
+        {
+          inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
+          zlog_debug ("Advertise as AS-External Id:%s", ibuf);
+        }
+
+      match->path.origin.id = htonl (info->id);
+      ospf6_as_external_lsa_originate (match);
+      return;
+    }
+
+  /* create new entry */
+  route = ospf6_route_create ();
+  route->type = OSPF6_DEST_TYPE_NETWORK;
+  memcpy (&route->prefix, prefix, sizeof (struct prefix));
+
+  info = (struct ospf6_external_info *)
+    XCALLOC (MTYPE_OSPF6_EXTERNAL_INFO, sizeof (struct ospf6_external_info));
+  route->route_option = info;
+  info->id = ospf6->external_id++;
+
+  /* copy result of route-map */
+  if (ospf6->rmap[type].map)
+    {
+      if (troute.path.metric_type)
+        route->path.metric_type = troute.path.metric_type;
+      if (troute.path.cost)
+        route->path.cost = troute.path.cost;
+      if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
+        memcpy (&info->forwarding, &tinfo.forwarding,
+                sizeof (struct in6_addr));
+      info->tag = tinfo.tag;
+    }
+  else
+    {
+      /* If there is no route-map, simply set the tag */
+      info->tag = tag;
+    }
+
+  info->type = type;
+  route->nexthop[0].ifindex = ifindex;
+  if (nexthop_num && nexthop)
+    memcpy (&route->nexthop[0].address, nexthop, sizeof (struct in6_addr));
+
+  /* create/update binding in external_id_table */
+  prefix_id.family = AF_INET;
+  prefix_id.prefixlen = 32;
+  prefix_id.u.prefix4.s_addr = htonl (info->id);
+  node = route_node_get (ospf6->external_id_table, &prefix_id);
+  node->info = route;
+
+  route = ospf6_route_add (route, ospf6->external_table);
+  route->route_option = info;
+
+  if (IS_OSPF6_DEBUG_ASBR)
+    {
+      inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
+      zlog_debug ("Advertise as AS-External Id:%s", ibuf);
+    }
+
+  route->path.origin.id = htonl (info->id);
+  ospf6_as_external_lsa_originate (route);
+
+  /* Router-Bit (ASBR Flag) may have to be updated */
+  for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa))
+    OSPF6_ROUTER_LSA_SCHEDULE (oa);
+}
+
+void
+ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex,
+                                struct prefix *prefix)
+{
+  struct ospf6_route *match;
+  struct ospf6_external_info *info = NULL;
+  struct route_node *node;
+  struct ospf6_lsa *lsa;
+  struct prefix prefix_id;
+  char pbuf[64], ibuf[16];
+  struct listnode *lnode, *lnnode;
+  struct ospf6_area *oa;
+
+  match = ospf6_route_lookup (prefix, ospf6->external_table);
+  if (match == NULL)
+    {
+      if (IS_OSPF6_DEBUG_ASBR)
+        {
+          prefix2str (prefix, pbuf, sizeof (pbuf));
+          zlog_debug ("No such route %s to withdraw", pbuf);
+        }
+      return;
+    }
+
+  info = match->route_option;
+  assert (info);
+
+  if (info->type != type)
+    {
+      if (IS_OSPF6_DEBUG_ASBR)
+        {
+          prefix2str (prefix, pbuf, sizeof (pbuf));
+          zlog_debug ("Original protocol mismatch: %s", pbuf);
+        }
+      return;
+    }
+
+  if (IS_OSPF6_DEBUG_ASBR)
+    {
+      prefix2str (prefix, pbuf, sizeof (pbuf));
+      inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
+      zlog_debug ("Withdraw %s (AS-External Id:%s)", pbuf, ibuf);
+    }
+
+  lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_AS_EXTERNAL),
+                           htonl (info->id), ospf6->router_id, ospf6->lsdb);
+  if (lsa)
+    ospf6_lsa_purge (lsa);
+
+  /* remove binding in external_id_table */
+  prefix_id.family = AF_INET;
+  prefix_id.prefixlen = 32;
+  prefix_id.u.prefix4.s_addr = htonl (info->id);
+  node = route_node_lookup (ospf6->external_id_table, &prefix_id);
+  assert (node);
+  node->info = NULL;
+  route_unlock_node (node);
+
+  ospf6_route_remove (match, ospf6->external_table);
+  XFREE (MTYPE_OSPF6_EXTERNAL_INFO, info);
+
+  /* Router-Bit (ASBR Flag) may have to be updated */
+  for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa))
+    OSPF6_ROUTER_LSA_SCHEDULE (oa);
+}
+
+DEFUN (ospf6_redistribute,
+       ospf6_redistribute_cmd,
+       "redistribute " QUAGGA_REDIST_STR_OSPF6D,
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_OSPF6D
+      )
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
+    return CMD_WARNING;
+
+  ospf6_asbr_redistribute_unset (type);
+  ospf6_asbr_redistribute_set (type);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_redistribute_routemap,
+       ospf6_redistribute_routemap_cmd,
+       "redistribute " QUAGGA_REDIST_STR_OSPF6D " route-map WORD",
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_OSPF6D
+       "Route map reference\n"
+       "Route map name\n"
+      )
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
+    return CMD_WARNING;
+
+  ospf6_asbr_redistribute_unset (type);
+  ospf6_asbr_routemap_set (type, argv[1]);
+  ospf6_asbr_redistribute_set (type);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_redistribute,
+       no_ospf6_redistribute_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_OSPF6D,
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_OSPF6D
+      )
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+  if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
+    return CMD_WARNING;
+
+  ospf6_asbr_redistribute_unset (type);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf6_redistribute,
+       no_ospf6_redistribute_route_map_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_OSPF6D " route-map WORD",
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_OSPF6D
+       "Route map reference\n"
+       "Route map name\n")
+
+int
+ospf6_redistribute_config_write (struct vty *vty)
+{
+  int type;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (type == ZEBRA_ROUTE_OSPF6)
+        continue;
+      if (! ospf6_zebra_is_redistribute (type))
+        continue;
+
+      if (ospf6->rmap[type].name)
+        vty_out (vty, " redistribute %s route-map %s%s",
+                 ZROUTE_NAME (type), ospf6->rmap[type].name, VNL);
+      else
+        vty_out (vty, " redistribute %s%s",
+                 ZROUTE_NAME (type), VNL);
+    }
+
+  return 0;
+}
+
+static void
+ospf6_redistribute_show_config (struct vty *vty)
+{
+  int type;
+  int nroute[ZEBRA_ROUTE_MAX];
+  int total;
+  struct ospf6_route *route;
+  struct ospf6_external_info *info;
+
+  total = 0;
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    nroute[type] = 0;
+  for (route = ospf6_route_head (ospf6->external_table); route;
+       route = ospf6_route_next (route))
+    {
+      info = route->route_option;
+      nroute[info->type]++;
+      total++;
+    }
+
+  vty_out (vty, "Redistributing External Routes from:%s", VNL);
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (type == ZEBRA_ROUTE_OSPF6)
+        continue;
+      if (! ospf6_zebra_is_redistribute (type))
+        continue;
+
+      if (ospf6->rmap[type].name)
+        vty_out (vty, "    %d: %s with route-map \"%s\"%s%s", nroute[type],
+                 ZROUTE_NAME (type), ospf6->rmap[type].name,
+                 (ospf6->rmap[type].map ? "" : " (not found !)"),
+                 VNL);
+      else
+        vty_out (vty, "    %d: %s%s", nroute[type],
+                 ZROUTE_NAME (type), VNL);
+    }
+  vty_out (vty, "Total %d routes%s", total, VNL);
+}
+
+
+
+/* Routemap Functions */
+static route_map_result_t
+ospf6_routemap_rule_match_address_prefixlist (void *rule,
+                                              struct prefix *prefix,
+                                              route_map_object_t type,
+                                              void *object)
+{
+  struct prefix_list *plist;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_NOMATCH;
+
+  plist = prefix_list_lookup (AFI_IP6, (char *) rule);
+  if (plist == NULL)
+    return RMAP_NOMATCH;
+
+  return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+          RMAP_NOMATCH : RMAP_MATCH);
+}
+
+static void *
+ospf6_routemap_rule_match_address_prefixlist_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+ospf6_routemap_rule_match_address_prefixlist_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd
+ospf6_routemap_rule_match_address_prefixlist_cmd =
+{
+  "ipv6 address prefix-list",
+  ospf6_routemap_rule_match_address_prefixlist,
+  ospf6_routemap_rule_match_address_prefixlist_compile,
+  ospf6_routemap_rule_match_address_prefixlist_free,
+};
+
+/* `match interface IFNAME' */
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+ospf6_routemap_rule_match_interface (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct interface   *ifp;
+  struct ospf6_external_info *ei;
+
+  if (type == RMAP_OSPF6)
+    {
+      ei = ((struct ospf6_route *) object)->route_option;
+      ifp = if_lookup_by_name ((char *)rule);
+
+      if (ifp != NULL
+      &&  ei->ifindex == ifp->ifindex)
+          return RMAP_MATCH;
+    }
+
+  return RMAP_NOMATCH;
+}
+
+/* Route map `interface' match statement.  `arg' should be
+   interface name. */
+static void *
+ospf6_routemap_rule_match_interface_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `interface' value. */
+static void
+ospf6_routemap_rule_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for interface matching. */
+struct route_map_rule_cmd
+ospf6_routemap_rule_match_interface_cmd =
+{
+  "interface",
+  ospf6_routemap_rule_match_interface,
+  ospf6_routemap_rule_match_interface_compile,
+  ospf6_routemap_rule_match_interface_free
+};
+
+/* Match function for matching route tags */
+static route_map_result_t
+ospf6_routemap_rule_match_tag (void *rule, struct prefix *prefix,
+                               route_map_object_t type, void *object)
+{
+  route_tag_t *tag = rule;
+  struct ospf6_route *route = object;
+  struct ospf6_external_info *info = route->route_option;
+
+  if (type == RMAP_OSPF6 && info->tag == *tag)
+    return RMAP_MATCH;
+
+  return RMAP_NOMATCH;
+}
+
+static struct route_map_rule_cmd
+ospf6_routemap_rule_match_tag_cmd =
+{
+  "tag",
+  ospf6_routemap_rule_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+static route_map_result_t
+ospf6_routemap_rule_set_metric_type (void *rule, struct prefix *prefix,
+                                     route_map_object_t type, void *object)
+{
+  char *metric_type = rule;
+  struct ospf6_route *route = object;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_OKAY;
+
+  if (strcmp (metric_type, "type-2") == 0)
+    route->path.metric_type = 2;
+  else
+    route->path.metric_type = 1;
+
+  return RMAP_OKAY;
+}
+
+static void *
+ospf6_routemap_rule_set_metric_type_compile (const char *arg)
+{
+  if (strcmp (arg, "type-2") && strcmp (arg, "type-1"))
+    return NULL;
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+ospf6_routemap_rule_set_metric_type_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd
+ospf6_routemap_rule_set_metric_type_cmd =
+{
+  "metric-type",
+  ospf6_routemap_rule_set_metric_type,
+  ospf6_routemap_rule_set_metric_type_compile,
+  ospf6_routemap_rule_set_metric_type_free,
+};
+
+static route_map_result_t
+ospf6_routemap_rule_set_metric (void *rule, struct prefix *prefix,
+                                route_map_object_t type, void *object)
+{
+  char *metric = rule;
+  struct ospf6_route *route = object;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_OKAY;
+
+  route->path.cost = atoi (metric);
+  return RMAP_OKAY;
+}
+
+static void *
+ospf6_routemap_rule_set_metric_compile (const char *arg)
+{
+  u_int32_t metric;
+  char *endp;
+  metric = strtoul (arg, &endp, 0);
+  if (metric > OSPF_LS_INFINITY || *endp != '\0')
+    return NULL;
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+ospf6_routemap_rule_set_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd
+ospf6_routemap_rule_set_metric_cmd =
+{
+  "metric",
+  ospf6_routemap_rule_set_metric,
+  ospf6_routemap_rule_set_metric_compile,
+  ospf6_routemap_rule_set_metric_free,
+};
+
+static route_map_result_t
+ospf6_routemap_rule_set_forwarding (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  char *forwarding = rule;
+  struct ospf6_route *route = object;
+  struct ospf6_external_info *info = route->route_option;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_OKAY;
+
+  if (inet_pton (AF_INET6, forwarding, &info->forwarding) != 1)
+    {
+      memset (&info->forwarding, 0, sizeof (struct in6_addr));
+      return RMAP_ERROR;
+    }
+
+  return RMAP_OKAY;
+}
+
+static void *
+ospf6_routemap_rule_set_forwarding_compile (const char *arg)
+{
+  struct in6_addr a;
+  if (inet_pton (AF_INET6, arg, &a) != 1)
+    return NULL;
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+ospf6_routemap_rule_set_forwarding_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd
+ospf6_routemap_rule_set_forwarding_cmd =
+{
+  "forwarding-address",
+  ospf6_routemap_rule_set_forwarding,
+  ospf6_routemap_rule_set_forwarding_compile,
+  ospf6_routemap_rule_set_forwarding_free,
+};
+
+static route_map_result_t
+ospf6_routemap_rule_set_tag (void *rule, struct prefix *prefix,
+                             route_map_object_t type, void *object)
+{
+  route_tag_t *tag = rule;
+  struct ospf6_route *route = object;
+  struct ospf6_external_info *info = route->route_option;
+
+  if (type != RMAP_OSPF6)
+    return RMAP_OKAY;
+
+  info->tag = *tag;
+  return RMAP_OKAY;
+}
+
+static struct route_map_rule_cmd
+ospf6_routemap_rule_set_tag_cmd =
+{
+  "tag",
+  ospf6_routemap_rule_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+static int
+route_map_command_status (struct vty *vty, int ret)
+{
+  if (! ret)
+    return CMD_SUCCESS;
+
+  switch (ret)
+    {
+    case RMAP_RULE_MISSING:
+      vty_out (vty, "OSPF6 Can't find rule.%s", VNL);
+      break;
+    case RMAP_COMPILE_ERROR:
+      vty_out (vty, "OSPF6 Argument is malformed.%s", VNL);
+      break;
+    default:
+      vty_out (vty, "OSPF6 route-map add set failed.%s", VNL);
+      break;
+    }
+  return CMD_WARNING;
+}
+
+/* add "match address" */
+DEFUN (ospf6_routemap_match_address_prefixlist,
+       ospf6_routemap_match_address_prefixlist_cmd,
+       "match ipv6 address prefix-list WORD",
+       "Match values\n"
+       IPV6_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IPv6 prefix-list name\n")
+{
+  int ret = route_map_add_match ((struct route_map_index *) vty->index,
+                                 "ipv6 address prefix-list", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "match address" */
+DEFUN (ospf6_routemap_no_match_address_prefixlist,
+       ospf6_routemap_no_match_address_prefixlist_cmd,
+       "no match ipv6 address prefix-list WORD",
+       NO_STR
+       "Match values\n"
+       IPV6_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IPv6 prefix-list name\n")
+{
+  int ret = route_map_delete_match ((struct route_map_index *) vty->index,
+                                    "ipv6 address prefix-list", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* "match interface" */
+DEFUN (ospf6_routemap_match_interface,
+       ospf6_routemap_match_interface_cmd,
+       "match interface WORD",
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+{
+  return route_map_add_match ((struct route_map_index *) vty->index,
+                              "interface", argv[0]);
+}
+
+/* "no match interface WORD" */
+DEFUN (ospf6_routemap_no_match_interface,
+       ospf6_routemap_no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n")
+{
+  int ret = route_map_delete_match ((struct route_map_index *) vty->index,
+                                    "interface", (argc == 0) ? NULL : argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (ospf6_routemap_no_match_interface,
+       ospf6_routemap_no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+/* add "match tag" */
+DEFUN (ospf6_routemap_match_tag,
+       ospf6_routemap_match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  int ret = route_map_add_match ((struct route_map_index *) vty->index,
+                               "tag", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "match tag" */
+DEFUN (ospf6_routemap_no_match_tag,
+       ospf6_routemap_no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Tag value for routing protocol\n")
+{
+  int ret = route_map_delete_match ((struct route_map_index *) vty->index,
+                                  "tag", argc ? argv[0] : NULL);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (ospf6_routemap_no_match_tag,
+       ospf6_routemap_no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+/* add "set metric-type" */
+DEFUN (ospf6_routemap_set_metric_type,
+       ospf6_routemap_set_metric_type_cmd,
+       "set metric-type (type-1|type-2)",
+       "Set value\n"
+       "Type of metric\n"
+       "OSPF6 external type 1 metric\n"
+       "OSPF6 external type 2 metric\n")
+{
+  int ret = route_map_add_set ((struct route_map_index *) vty->index,
+                               "metric-type", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "set metric-type" */
+DEFUN (ospf6_routemap_no_set_metric_type,
+       ospf6_routemap_no_set_metric_type_cmd,
+       "no set metric-type (type-1|type-2)",
+       NO_STR
+       "Set value\n"
+       "Type of metric\n"
+       "OSPF6 external type 1 metric\n"
+       "OSPF6 external type 2 metric\n")
+{
+  int ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                  "metric-type", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* add "set metric" */
+DEFUN (set_metric,
+       set_metric_cmd,
+       "set metric <0-4294967295>",
+       "Set value\n"
+       "Metric value\n"
+       "Metric value\n")
+{
+  int ret = route_map_add_set ((struct route_map_index *) vty->index,
+                               "metric", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "set metric" */
+DEFUN (no_set_metric,
+       no_set_metric_cmd,
+       "no set metric",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n")
+{
+  int ret = 0;
+
+  if (argc == 0)
+    ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                "metric", NULL);
+  else
+    ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                "metric", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (no_set_metric,
+       no_set_metric_val_cmd,
+       "no set metric <0-4294967295>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+
+/* add "set forwarding-address" */
+DEFUN (ospf6_routemap_set_forwarding,
+       ospf6_routemap_set_forwarding_cmd,
+       "set forwarding-address X:X::X:X",
+       "Set value\n"
+       "Forwarding Address\n"
+       "IPv6 Address\n")
+{
+  int ret = route_map_add_set ((struct route_map_index *) vty->index,
+                               "forwarding-address", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "set forwarding-address" */
+DEFUN (ospf6_routemap_no_set_forwarding,
+       ospf6_routemap_no_set_forwarding_cmd,
+       "no set forwarding-address X:X::X:X",
+       NO_STR
+       "Set value\n"
+       "Forwarding Address\n"
+       "IPv6 Address\n")
+{
+  int ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                  "forwarding-address", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* add "set tag" */
+DEFUN (ospf6_routemap_set_tag,
+       ospf6_routemap_set_tag_cmd,
+       "set tag <1-4294967295>",
+       "Set value\n"
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  int ret = route_map_add_set ((struct route_map_index *) vty->index,
+                               "tag", argv[0]);
+  return route_map_command_status (vty, ret);
+}
+
+/* delete "set tag" */
+DEFUN (ospf6_routemap_no_set_tag,
+       ospf6_routemap_no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       "Set value\n"
+       "Tag value for routing protocol\n")
+{
+  int ret = route_map_delete_set ((struct route_map_index *) vty->index,
+                                  "tag", argc ? argv[0] : NULL);
+  return route_map_command_status (vty, ret);
+}
+
+ALIAS (ospf6_routemap_no_set_tag,
+       ospf6_routemap_no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       "Set value\n"
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+static void
+ospf6_routemap_init (void)
+{
+  route_map_init ();
+  route_map_init_vty ();
+  route_map_add_hook (ospf6_asbr_routemap_update);
+  route_map_delete_hook (ospf6_asbr_routemap_update);
+
+  route_map_install_match (&ospf6_routemap_rule_match_address_prefixlist_cmd);
+  route_map_install_match (&ospf6_routemap_rule_match_interface_cmd);
+  route_map_install_match (&ospf6_routemap_rule_match_tag_cmd);
+
+  route_map_install_set (&ospf6_routemap_rule_set_metric_type_cmd);
+  route_map_install_set (&ospf6_routemap_rule_set_metric_cmd);
+  route_map_install_set (&ospf6_routemap_rule_set_forwarding_cmd);
+  route_map_install_set (&ospf6_routemap_rule_set_tag_cmd);
+
+  /* Match address prefix-list */
+  install_element (RMAP_NODE, &ospf6_routemap_match_address_prefixlist_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_address_prefixlist_cmd);
+
+  /* Match interface */
+  install_element (RMAP_NODE, &ospf6_routemap_match_interface_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_val_cmd);
+
+  /* Match tag */
+  install_element (RMAP_NODE, &ospf6_routemap_match_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_val_cmd);
+
+  /* ASE Metric Type (e.g. Type-1/Type-2) */
+  install_element (RMAP_NODE, &ospf6_routemap_set_metric_type_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd);
+
+  /* ASE Metric */
+  install_element (RMAP_NODE, &set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+
+  /* Forwarding address */
+  install_element (RMAP_NODE, &ospf6_routemap_set_forwarding_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd);
+
+  /* Tag */
+  install_element (RMAP_NODE, &ospf6_routemap_set_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_cmd);
+  install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_val_cmd);
+}
+
+
+/* Display functions */
+static char *
+ospf6_as_external_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf,
+                                     int buflen, int pos)
+{
+  struct ospf6_as_external_lsa *external;
+  struct in6_addr in6;
+  int prefix_length = 0;
+
+  if (lsa)
+    {
+        external = (struct ospf6_as_external_lsa *)
+         OSPF6_LSA_HEADER_END (lsa->header);
+
+       if (pos == 0)
+         {
+           ospf6_prefix_in6_addr (&in6, &external->prefix);
+           prefix_length = external->prefix.prefix_length;
+         }
+       else {
+         in6 = *((struct in6_addr *)
+                 ((caddr_t) external + sizeof (struct ospf6_as_external_lsa) +
+                  OSPF6_PREFIX_SPACE (external->prefix.prefix_length)));
+       }
+       if (buf)
+         {
+           inet_ntop (AF_INET6, &in6, buf, buflen);
+           if (prefix_length)
+             sprintf (&buf[strlen(buf)], "/%d", prefix_length);
+         }
+    }
+  return (buf);
+}
+
+static int
+ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  struct ospf6_as_external_lsa *external;
+  char buf[64];
+
+  assert (lsa->header);
+  external = (struct ospf6_as_external_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+  
+  /* bits */
+  snprintf (buf, sizeof (buf), "%c%c%c",
+    (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E) ? 'E' : '-'),
+    (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F) ? 'F' : '-'),
+    (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T' : '-'));
+
+  vty_out (vty, "     Bits: %s%s", buf, VNL);
+  vty_out (vty, "     Metric: %5lu%s", (u_long) OSPF6_ASBR_METRIC (external),
+           VNL);
+
+  ospf6_prefix_options_printbuf (external->prefix.prefix_options,
+                                 buf, sizeof (buf));
+  vty_out (vty, "     Prefix Options: %s%s", buf,
+           VNL);
+
+  vty_out (vty, "     Referenced LSType: %d%s",
+           ntohs (external->prefix.prefix_refer_lstype),
+           VNL);
+
+  vty_out (vty, "     Prefix: %s%s",
+          ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 0), VNL);
+
+  /* Forwarding-Address */
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F))
+    {
+      vty_out (vty, "     Forwarding-Address: %s%s",
+              ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 1),
+              VNL);
+    }
+
+  /* Tag */
+  if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T))
+    {
+      vty_out (vty, "     Tag: %u%s",
+               ospf6_as_external_lsa_get_tag (lsa), VNL);
+    }
+
+  return 0;
+}
+
+static void
+ospf6_asbr_external_route_show (struct vty *vty, struct ospf6_route *route)
+{
+  struct ospf6_external_info *info = route->route_option;
+  char prefix[64], id[16], forwarding[64];
+  u_int32_t tmp_id;
+
+  prefix2str (&route->prefix, prefix, sizeof (prefix));
+  tmp_id = ntohl (info->id);
+  inet_ntop (AF_INET, &tmp_id, id, sizeof (id));
+  if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding))
+    inet_ntop (AF_INET6, &info->forwarding, forwarding, sizeof (forwarding));
+  else
+    snprintf (forwarding, sizeof (forwarding), ":: (ifindex %d)",
+              route->nexthop[0].ifindex);
+
+  vty_out (vty, "%c %-32s %-15s type-%d %5lu %s%s",
+           zebra_route_char(info->type),
+           prefix, id, route->path.metric_type,
+           (u_long) (route->path.metric_type == 2 ?
+                     route->path.cost_e2 : route->path.cost),
+           forwarding, VNL);
+}
+
+DEFUN (show_ipv6_ospf6_redistribute,
+       show_ipv6_ospf6_redistribute_cmd,
+       "show ipv6 ospf6 redistribute",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "redistributing External information\n"
+       )
+{
+  struct ospf6_route *route;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_redistribute_show_config (vty);
+
+  for (route = ospf6_route_head (ospf6->external_table); route;
+       route = ospf6_route_next (route))
+    ospf6_asbr_external_route_show (vty, route);
+
+  return CMD_SUCCESS;
+}
+
+struct ospf6_lsa_handler as_external_handler =
+{
+  OSPF6_LSTYPE_AS_EXTERNAL,
+  "AS-External",
+  "ASE",
+  ospf6_as_external_lsa_show,
+  ospf6_as_external_lsa_get_prefix_str
+};
+
+void
+ospf6_asbr_init (void)
+{
+  ospf6_routemap_init ();
+
+  ospf6_install_lsa_handler (&as_external_handler);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd);
+
+  install_element (OSPF6_NODE, &ospf6_redistribute_cmd);
+  install_element (OSPF6_NODE, &ospf6_redistribute_routemap_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_redistribute_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_redistribute_route_map_cmd);
+}
+
+void
+ospf6_asbr_redistribute_reset (void)
+{
+  int type;
+
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    {
+      if (type == ZEBRA_ROUTE_OSPF6)
+        continue;
+      if (ospf6_zebra_is_redistribute (type))
+        ospf6_asbr_redistribute_unset(type);
+    }
+}
+
+void
+ospf6_asbr_terminate (void)
+{
+  route_map_finish ();
+}
+
+DEFUN (debug_ospf6_asbr,
+       debug_ospf6_asbr_cmd,
+       "debug ospf6 asbr",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 ASBR function\n"
+      )
+{
+  OSPF6_DEBUG_ASBR_ON ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_asbr,
+       no_debug_ospf6_asbr_cmd,
+       "no debug ospf6 asbr",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 ASBR function\n"
+      )
+{
+  OSPF6_DEBUG_ASBR_OFF ();
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_asbr (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_ASBR)
+    vty_out (vty, "debug ospf6 asbr%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_asbr ()
+{
+  install_element (ENABLE_NODE, &debug_ospf6_asbr_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_asbr_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_asbr_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_asbr_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
new file mode 100644 (file)
index 0000000..14113b0
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2001 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_ASBR_H
+#define OSPF6_ASBR_H
+
+/* for struct ospf6_prefix */
+#include "ospf6_proto.h"
+/* for struct ospf6_lsa */
+#include "ospf6_lsa.h"
+/* for struct ospf6_route */
+#include "ospf6_route.h"
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_asbr;
+#define OSPF6_DEBUG_ASBR_ON() \
+  (conf_debug_ospf6_asbr = 1)
+#define OSPF6_DEBUG_ASBR_OFF() \
+  (conf_debug_ospf6_asbr = 0)
+#define IS_OSPF6_DEBUG_ASBR \
+  (conf_debug_ospf6_asbr)
+
+struct ospf6_external_info
+{
+  /* External route type */
+  int type;
+
+  /* Originating Link State ID */
+  u_int32_t id;
+
+  struct in6_addr forwarding;
+
+  route_tag_t tag;
+
+  ifindex_t ifindex;
+};
+
+/* AS-External-LSA */
+#define OSPF6_AS_EXTERNAL_LSA_MIN_SIZE         4U /* w/o IPv6 prefix */
+struct ospf6_as_external_lsa
+{
+  u_int32_t bits_metric;
+
+  struct ospf6_prefix prefix;
+  /* followed by none or one forwarding address */
+  /* followed by none or one external route tag */
+  /* followed by none or one referenced LS-ID */
+};
+
+#define OSPF6_ASBR_BIT_T  ntohl (0x01000000)
+#define OSPF6_ASBR_BIT_F  ntohl (0x02000000)
+#define OSPF6_ASBR_BIT_E  ntohl (0x04000000)
+
+#define OSPF6_ASBR_METRIC(E) (ntohl ((E)->bits_metric & htonl (0x00ffffff)))
+#define OSPF6_ASBR_METRIC_SET(E,C) \
+  { (E)->bits_metric &= htonl (0xff000000); \
+    (E)->bits_metric |= htonl (0x00ffffff) & htonl (C); }
+
+extern void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa);
+extern void ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa);
+extern void ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry);
+extern void ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry);
+
+extern int ospf6_asbr_is_asbr (struct ospf6 *o);
+extern void ospf6_asbr_redistribute_add (int type, ifindex_t ifindex,
+                                         struct prefix *prefix,
+                                         u_int nexthop_num,
+                                         struct in6_addr *nexthop,
+                                         route_tag_t tag);
+extern void ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex,
+                                            struct prefix *prefix);
+
+extern int ospf6_redistribute_config_write (struct vty *vty);
+
+extern void ospf6_asbr_init (void);
+extern void ospf6_asbr_redistribute_reset (void);
+extern void ospf6_asbr_terminate (void);
+
+extern int config_write_ospf6_debug_asbr (struct vty *vty);
+extern void install_element_ospf6_debug_asbr (void);
+
+#endif /* OSPF6_ASBR_H */
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
new file mode 100644 (file)
index 0000000..815db7b
--- /dev/null
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "thread.h"
+#include "linklist.h"
+#include "vty.h"
+#include "command.h"
+
+#include "ospf6d.h"
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_route.h"
+#include "ospf6_spf.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+
+unsigned char conf_debug_ospf6_flooding;
+
+struct ospf6_lsdb *
+ospf6_get_scoped_lsdb (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsdb *lsdb = NULL;
+  switch (OSPF6_LSA_SCOPE (lsa->header->type))
+    {
+    case OSPF6_SCOPE_LINKLOCAL:
+      lsdb = OSPF6_INTERFACE (lsa->lsdb->data)->lsdb;
+      break;
+    case OSPF6_SCOPE_AREA:
+      lsdb = OSPF6_AREA (lsa->lsdb->data)->lsdb;
+      break;
+    case OSPF6_SCOPE_AS:
+      lsdb = OSPF6_PROCESS (lsa->lsdb->data)->lsdb;
+      break;
+    default:
+      assert (0);
+      break;
+    }
+  return lsdb;
+}
+
+struct ospf6_lsdb *
+ospf6_get_scoped_lsdb_self (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsdb *lsdb_self = NULL;
+  switch (OSPF6_LSA_SCOPE (lsa->header->type))
+    {
+    case OSPF6_SCOPE_LINKLOCAL:
+      lsdb_self = OSPF6_INTERFACE (lsa->lsdb->data)->lsdb_self;
+      break;
+    case OSPF6_SCOPE_AREA:
+      lsdb_self = OSPF6_AREA (lsa->lsdb->data)->lsdb_self;
+      break;
+    case OSPF6_SCOPE_AS:
+      lsdb_self = OSPF6_PROCESS (lsa->lsdb->data)->lsdb_self;
+      break;
+    default:
+      assert (0);
+      break;
+    }
+  return lsdb_self;
+}
+
+void
+ospf6_lsa_originate (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsa *old;
+  struct ospf6_lsdb *lsdb_self;
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                           lsa->header->adv_router, lsa->lsdb);
+
+  /* if the new LSA does not differ from previous,
+     suppress this update of the LSA */
+  if (old && ! OSPF6_LSA_IS_DIFFER (lsa, old))
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type))
+        zlog_debug ("Suppress updating LSA: %s", lsa->name);
+      ospf6_lsa_delete (lsa);
+      return;
+    }
+
+  /* store it in the LSDB for self-originated LSAs */
+  lsdb_self = ospf6_get_scoped_lsdb_self (lsa);
+  ospf6_lsdb_add (ospf6_lsa_copy (lsa), lsdb_self);
+
+  lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa,
+                                   OSPF_LS_REFRESH_TIME);
+
+  if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) ||
+      IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type))
+    {
+      zlog_debug ("LSA Originate:");
+      ospf6_lsa_header_print (lsa);
+    }
+
+  ospf6_install_lsa (lsa);
+  ospf6_flood (NULL, lsa);
+}
+
+void
+ospf6_lsa_originate_process (struct ospf6_lsa *lsa,
+                             struct ospf6 *process)
+{
+  lsa->lsdb = process->lsdb;
+  ospf6_lsa_originate (lsa);
+}
+
+void
+ospf6_lsa_originate_area (struct ospf6_lsa *lsa,
+                          struct ospf6_area *oa)
+{
+  lsa->lsdb = oa->lsdb;
+  ospf6_lsa_originate (lsa);
+}
+
+void
+ospf6_lsa_originate_interface (struct ospf6_lsa *lsa,
+                               struct ospf6_interface *oi)
+{
+  lsa->lsdb = oi->lsdb;
+  ospf6_lsa_originate (lsa);
+}
+
+void
+ospf6_lsa_purge (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsa *self;
+  struct ospf6_lsdb *lsdb_self;
+
+  /* remove it from the LSDB for self-originated LSAs */
+  lsdb_self = ospf6_get_scoped_lsdb_self (lsa);
+  self = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                            lsa->header->adv_router, lsdb_self);
+  if (self)
+    {
+      THREAD_OFF (self->expire);
+      THREAD_OFF (self->refresh);
+      ospf6_lsdb_remove (self, lsdb_self);
+    }
+
+  ospf6_lsa_premature_aging (lsa);
+}
+
+
+void
+ospf6_increment_retrans_count (struct ospf6_lsa *lsa)
+{
+  /* The LSA must be the original one (see the description
+     in ospf6_decrement_retrans_count () below) */
+  lsa->retrans_count++;
+}
+
+void
+ospf6_decrement_retrans_count (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsdb *lsdb;
+  struct ospf6_lsa *orig;
+
+  /* The LSA must be on the retrans-list of a neighbor. It means
+     the "lsa" is a copied one, and we have to decrement the
+     retransmission count of the original one (instead of this "lsa"'s).
+     In order to find the original LSA, first we have to find
+     appropriate LSDB that have the original LSA. */
+  lsdb = ospf6_get_scoped_lsdb (lsa);
+
+  /* Find the original LSA of which the retrans_count should be decremented */
+  orig = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                            lsa->header->adv_router, lsdb);
+  if (orig)
+    {
+      orig->retrans_count--;
+      assert (orig->retrans_count >= 0);
+    }
+}
+
+/* RFC2328 section 13.2 Installing LSAs in the database */
+void
+ospf6_install_lsa (struct ospf6_lsa *lsa)
+{
+  struct timeval now;
+  struct ospf6_lsa *old;
+
+  if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) ||
+      IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type))
+    zlog_debug ("Install LSA: %s", lsa->name);
+
+  /* Remove the old instance from all neighbors' Link state
+     retransmission list (RFC2328 13.2 last paragraph) */
+  old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                           lsa->header->adv_router, lsa->lsdb);
+  if (old)
+    {
+      THREAD_OFF (old->expire);
+      THREAD_OFF (old->refresh);
+      ospf6_flood_clear (old);
+    }
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  if (! OSPF6_LSA_IS_MAXAGE (lsa))
+    lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa,
+                                    OSPF_LSA_MAXAGE + lsa->birth.tv_sec - now.tv_sec);
+  else
+    lsa->expire = NULL;
+
+  if (OSPF6_LSA_IS_SEQWRAP(lsa) &&
+      ! (CHECK_FLAG(lsa->flag,OSPF6_LSA_SEQWRAPPED) &&
+         lsa->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER)))
+   {
+     if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type))
+       zlog_debug("lsa install wrapping: sequence 0x%x",
+                  ntohl(lsa->header->seqnum));
+     SET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED);
+     /* in lieu of premature_aging, since we do not want to recreate this lsa
+      * and/or mess with timers etc, we just want to wrap the sequence number
+      * and reflood the lsa before continuing.
+      * NOTE: Flood needs to be called right after this function call, by the
+      * caller
+      */
+     lsa->header->seqnum = htonl (OSPF_MAX_SEQUENCE_NUMBER);
+     lsa->header->age = htons (OSPF_LSA_MAXAGE);
+     ospf6_lsa_checksum (lsa->header);
+   }
+
+  /* actually install */
+  lsa->installed = now;
+  ospf6_lsdb_add (lsa, lsa->lsdb);
+
+  return;
+}
+
+/* RFC2740 section 3.5.2. Sending Link State Update packets */
+/* RFC2328 section 13.3 Next step in the flooding procedure */
+static void
+ospf6_flood_interface (struct ospf6_neighbor *from,
+                       struct ospf6_lsa *lsa, struct ospf6_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *req;
+  int retrans_added = 0;
+  int is_debug = 0;
+
+  if (IS_OSPF6_DEBUG_FLOODING ||
+      IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type))
+    {
+      is_debug++;
+      zlog_debug ("Flooding on %s: %s", oi->interface->name, lsa->name);
+    }
+
+  /* (1) For each neighbor */
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      if (is_debug)
+        zlog_debug ("To neighbor %s", on->name);
+
+      /* (a) if neighbor state < Exchange, examin next */
+      if (on->state < OSPF6_NEIGHBOR_EXCHANGE)
+        {
+          if (is_debug)
+            zlog_debug ("Neighbor state less than ExChange, next neighbor");
+          continue;
+        }
+
+      /* (b) if neighbor not yet Full, check request-list */
+      if (on->state != OSPF6_NEIGHBOR_FULL)
+        {
+          if (is_debug)
+            zlog_debug ("Neighbor not yet Full");
+
+          req = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                                   lsa->header->adv_router, on->request_list);
+          if (req == NULL)
+            {
+              if (is_debug)
+                zlog_debug ("Not on request-list for this neighbor");
+              /* fall through */
+            }
+          else
+            {
+              /* If new LSA less recent, examin next neighbor */
+              if (ospf6_lsa_compare (lsa, req) > 0)
+                {
+                  if (is_debug)
+                    zlog_debug ("Requesting is older, next neighbor");
+                  continue;
+                }
+
+              /* If the same instance, delete from request-list and
+                 examin next neighbor */
+              if (ospf6_lsa_compare (lsa, req) == 0)
+                {
+                 if (is_debug)
+                   zlog_debug ("Requesting the same, remove it, next neighbor");
+                 if (req == on->last_ls_req)
+                   {
+                     ospf6_lsa_unlock (req);
+                     on->last_ls_req = NULL;
+                   }
+                  ospf6_lsdb_remove (req, on->request_list);
+                 ospf6_check_nbr_loading (on);
+                  continue;
+                }
+
+              /* If the new LSA is more recent, delete from request-list */
+              if (ospf6_lsa_compare (lsa, req) < 0)
+                {
+                 if (is_debug)
+                   zlog_debug ("Received is newer, remove requesting");
+                 if (req == on->last_ls_req)
+                   {
+                     ospf6_lsa_unlock (req);
+                     on->last_ls_req = NULL;
+                   }
+                  ospf6_lsdb_remove (req, on->request_list);
+                 ospf6_check_nbr_loading (on);
+                  /* fall through */
+                }
+            }
+        }
+
+      /* (c) If the new LSA was received from this neighbor,
+         examin next neighbor */
+      if (from == on)
+        {
+          if (is_debug)
+            zlog_debug ("Received is from the neighbor, next neighbor");
+          continue;
+        }
+
+      /* (d) add retrans-list, schedule retransmission */
+      if (is_debug)
+        zlog_debug ("Add retrans-list of this neighbor");
+      ospf6_increment_retrans_count (lsa);
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list);
+      if (on->thread_send_lsupdate == NULL)
+        on->thread_send_lsupdate =
+          thread_add_timer (master, ospf6_lsupdate_send_neighbor,
+                            on, on->ospf6_if->rxmt_interval);
+      retrans_added++;
+    }
+
+  /* (2) examin next interface if not added to retrans-list */
+  if (retrans_added == 0)
+    {
+      if (is_debug)
+        zlog_debug ("No retransmission scheduled, next interface");
+      return;
+    }
+
+  /* (3) If the new LSA was received on this interface,
+     and it was from DR or BDR, examin next interface */
+  if (from && from->ospf6_if == oi &&
+      (from->router_id == oi->drouter || from->router_id == oi->bdrouter))
+    {
+      if (is_debug)
+        zlog_debug ("Received is from the I/F's DR or BDR, next interface");
+      return;
+    }
+
+  /* (4) If the new LSA was received on this interface,
+     and the interface state is BDR, examin next interface */
+  if (from && from->ospf6_if == oi)
+    {
+      if (oi->state == OSPF6_INTERFACE_BDR)
+       {
+         if (is_debug)
+           zlog_debug ("Received is from the I/F, itself BDR, next interface");
+         return;
+       }
+      SET_FLAG(lsa->flag, OSPF6_LSA_FLOODBACK);
+    }
+
+  /* (5) flood the LSA out the interface. */
+  if (is_debug)
+    zlog_debug ("Schedule flooding for the interface");
+  if ((oi->type == OSPF_IFTYPE_BROADCAST) ||
+      (oi->type == OSPF_IFTYPE_POINTOPOINT))
+    {
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsupdate_list);
+      if (oi->thread_send_lsupdate == NULL)
+        oi->thread_send_lsupdate =
+          thread_add_event (master, ospf6_lsupdate_send_interface, oi, 0);
+    }
+  else
+    {
+      /* reschedule retransmissions to all neighbors */
+      for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+        {
+          THREAD_OFF (on->thread_send_lsupdate);
+          on->thread_send_lsupdate =
+            thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0);
+        }
+    }
+}
+
+static void
+ospf6_flood_area (struct ospf6_neighbor *from,
+                  struct ospf6_lsa *lsa, struct ospf6_area *oa)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
+    {
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL &&
+          oi != OSPF6_INTERFACE (lsa->lsdb->data))
+        continue;
+
+#if 0
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AS &&
+          ospf6_is_interface_virtual_link (oi))
+        continue;
+#endif/*0*/
+
+      ospf6_flood_interface (from, lsa, oi);
+    }
+}
+
+static void
+ospf6_flood_process (struct ospf6_neighbor *from,
+                     struct ospf6_lsa *lsa, struct ospf6 *process)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+
+  for (ALL_LIST_ELEMENTS (process->area_list, node, nnode, oa))
+    {
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AREA &&
+          oa != OSPF6_AREA (lsa->lsdb->data))
+        continue;
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL &&
+          oa != OSPF6_INTERFACE (lsa->lsdb->data)->area)
+        continue;
+
+      if (ntohs (lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL &&
+          IS_AREA_STUB (oa))
+        continue;
+
+      ospf6_flood_area (from, lsa, oa);
+    }
+}
+
+void
+ospf6_flood (struct ospf6_neighbor *from, struct ospf6_lsa *lsa)
+{
+  ospf6_flood_process (from, lsa, ospf6);
+}
+
+static void
+ospf6_flood_clear_interface (struct ospf6_lsa *lsa, struct ospf6_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *rem;
+
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      rem = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                               lsa->header->adv_router, on->retrans_list);
+      if (rem && ! ospf6_lsa_compare (rem, lsa))
+        {
+          if (IS_OSPF6_DEBUG_FLOODING ||
+              IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type))
+            zlog_debug ("Remove %s from retrans_list of %s",
+                       rem->name, on->name);
+          ospf6_decrement_retrans_count (rem);
+          ospf6_lsdb_remove (rem, on->retrans_list);
+        }
+    }
+}
+
+static void
+ospf6_flood_clear_area (struct ospf6_lsa *lsa, struct ospf6_area *oa)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
+    {
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL &&
+          oi != OSPF6_INTERFACE (lsa->lsdb->data))
+        continue;
+
+#if 0
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AS &&
+          ospf6_is_interface_virtual_link (oi))
+        continue;
+#endif/*0*/
+
+      ospf6_flood_clear_interface (lsa, oi);
+    }
+}
+
+static void
+ospf6_flood_clear_process (struct ospf6_lsa *lsa, struct ospf6 *process)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+
+  for (ALL_LIST_ELEMENTS (process->area_list, node, nnode, oa))
+    {
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AREA &&
+          oa != OSPF6_AREA (lsa->lsdb->data))
+        continue;
+      if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL &&
+          oa != OSPF6_INTERFACE (lsa->lsdb->data)->area)
+        continue;
+
+      if (ntohs (lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL &&
+          IS_AREA_STUB (oa))
+        continue;
+
+      ospf6_flood_clear_area (lsa, oa);
+    }
+}
+
+void
+ospf6_flood_clear (struct ospf6_lsa *lsa)
+{
+  ospf6_flood_clear_process (lsa, ospf6);
+}
+
+
+/* RFC2328 13.5 (Table 19): Sending link state acknowledgements. */
+static void
+ospf6_acknowledge_lsa_bdrouter (struct ospf6_lsa *lsa, int ismore_recent,
+                                struct ospf6_neighbor *from)
+{
+  struct ospf6_interface *oi;
+  int is_debug = 0;
+
+  if (IS_OSPF6_DEBUG_FLOODING ||
+      IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type))
+    is_debug++;
+
+  assert (from && from->ospf6_if);
+  oi = from->ospf6_if;
+
+  /* LSA is more recent than database copy, but was not flooded
+     back out receiving interface. Delayed acknowledgement sent
+     if advertisement received from Designated Router,
+     otherwide do nothing. */
+  if (ismore_recent < 0)
+    {
+      if (oi->drouter == from->router_id)
+        {
+          if (is_debug)
+            zlog_debug ("Delayed acknowledgement (BDR & MoreRecent & from DR)");
+          /* Delayed acknowledgement */
+          ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list);
+          if (oi->thread_send_lsack == NULL)
+            oi->thread_send_lsack =
+              thread_add_timer (master, ospf6_lsack_send_interface, oi, 3);
+        }
+      else
+        {
+          if (is_debug)
+            zlog_debug ("No acknowledgement (BDR & MoreRecent & ! from DR)");
+        }
+      return;
+    }
+
+  /* LSA is a duplicate, and was treated as an implied acknowledgement.
+     Delayed acknowledgement sent if advertisement received from
+     Designated Router, otherwise do nothing */
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) &&
+      CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK))
+    {
+      if (oi->drouter == from->router_id)
+        {
+          if (is_debug)
+            zlog_debug ("Delayed acknowledgement (BDR & Duplicate & ImpliedAck & from DR)");
+          /* Delayed acknowledgement */
+          ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list);
+          if (oi->thread_send_lsack == NULL)
+            oi->thread_send_lsack =
+              thread_add_timer (master, ospf6_lsack_send_interface, oi, 3);
+        }
+      else
+        {
+          if (is_debug)
+            zlog_debug ("No acknowledgement (BDR & Duplicate & ImpliedAck & ! from DR)");
+        }
+      return;
+    }
+
+  /* LSA is a duplicate, and was not treated as an implied acknowledgement.
+     Direct acknowledgement sent */
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) &&
+      ! CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK))
+    {
+      if (is_debug)
+        zlog_debug ("Direct acknowledgement (BDR & Duplicate)");
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), from->lsack_list);
+      if (from->thread_send_lsack == NULL)
+        from->thread_send_lsack =
+          thread_add_event (master, ospf6_lsack_send_neighbor, from, 0);
+      return;
+    }
+
+  /* LSA's LS age is equal to Maxage, and there is no current instance
+     of the LSA in the link state database, and none of router's
+     neighbors are in states Exchange or Loading */
+  /* Direct acknowledgement sent, but this case is handled in
+     early of ospf6_receive_lsa () */
+}
+
+static void
+ospf6_acknowledge_lsa_allother (struct ospf6_lsa *lsa, int ismore_recent,
+                                struct ospf6_neighbor *from)
+{
+  struct ospf6_interface *oi;
+  int is_debug = 0;
+
+  if (IS_OSPF6_DEBUG_FLOODING ||
+      IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type))
+    is_debug++;
+
+  assert (from && from->ospf6_if);
+  oi = from->ospf6_if;
+
+  /* LSA has been flood back out receiving interface.
+     No acknowledgement sent. */
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_FLOODBACK))
+    {
+      if (is_debug)
+        zlog_debug ("No acknowledgement (AllOther & FloodBack)");
+      return;
+    }
+
+  /* LSA is more recent than database copy, but was not flooded
+     back out receiving interface. Delayed acknowledgement sent. */
+  if (ismore_recent < 0)
+    {
+      if (is_debug)
+        zlog_debug ("Delayed acknowledgement (AllOther & MoreRecent)");
+      /* Delayed acknowledgement */
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list);
+      if (oi->thread_send_lsack == NULL)
+        oi->thread_send_lsack =
+          thread_add_timer (master, ospf6_lsack_send_interface, oi, 3);
+      return;
+    }
+
+  /* LSA is a duplicate, and was treated as an implied acknowledgement.
+     No acknowledgement sent. */
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) &&
+      CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK))
+    {
+      if (is_debug)
+        zlog_debug ("No acknowledgement (AllOther & Duplicate & ImpliedAck)");
+      return;
+    }
+
+  /* LSA is a duplicate, and was not treated as an implied acknowledgement.
+     Direct acknowledgement sent */
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) &&
+      ! CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK))
+    {
+      if (is_debug)
+        zlog_debug ("Direct acknowledgement (AllOther & Duplicate)");
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), from->lsack_list);
+      if (from->thread_send_lsack == NULL)
+        from->thread_send_lsack =
+          thread_add_event (master, ospf6_lsack_send_neighbor, from, 0);
+      return;
+    }
+
+  /* LSA's LS age is equal to Maxage, and there is no current instance
+     of the LSA in the link state database, and none of router's
+     neighbors are in states Exchange or Loading */
+  /* Direct acknowledgement sent, but this case is handled in
+     early of ospf6_receive_lsa () */
+}
+
+static void
+ospf6_acknowledge_lsa (struct ospf6_lsa *lsa, int ismore_recent,
+                       struct ospf6_neighbor *from)
+{
+  struct ospf6_interface *oi;
+
+  assert (from && from->ospf6_if);
+  oi = from->ospf6_if;
+
+  if (oi->state == OSPF6_INTERFACE_BDR)
+    ospf6_acknowledge_lsa_bdrouter (lsa, ismore_recent, from);
+  else
+    ospf6_acknowledge_lsa_allother (lsa, ismore_recent, from);
+}
+
+/* RFC2328 section 13 (4):
+   if MaxAge LSA and if we have no instance, and no neighbor
+   is in states Exchange or Loading
+   returns 1 if match this case, else returns 0 */
+static int
+ospf6_is_maxage_lsa_drop (struct ospf6_lsa *lsa, struct ospf6_neighbor *from)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_interface *oi;
+  struct ospf6_area *oa;
+  struct ospf6 *process = NULL;
+  struct listnode *i, *j, *k;
+  int count = 0;
+
+  if (! OSPF6_LSA_IS_MAXAGE (lsa))
+    return 0;
+
+  if (ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                         lsa->header->adv_router, lsa->lsdb))
+    return 0;
+
+  process = from->ospf6_if->area->ospf6;
+
+  for (ALL_LIST_ELEMENTS_RO (process->area_list, i, oa))
+    for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on))
+        if (on->state == OSPF6_NEIGHBOR_EXCHANGE ||
+            on->state == OSPF6_NEIGHBOR_LOADING)
+          count++;
+
+  if (count == 0)
+    return 1;
+  return 0;
+}
+
+/* RFC2328 section 13 The Flooding Procedure */
+void
+ospf6_receive_lsa (struct ospf6_neighbor *from,
+                   struct ospf6_lsa_header *lsa_header)
+{
+  struct ospf6_lsa *new = NULL, *old = NULL, *rem = NULL;
+  int ismore_recent;
+  int is_debug = 0;
+
+  ismore_recent = 1;
+  assert (from);
+
+  /* make lsa structure for received lsa */
+  new = ospf6_lsa_create (lsa_header);
+
+  if (IS_OSPF6_DEBUG_FLOODING ||
+      IS_OSPF6_DEBUG_FLOOD_TYPE (new->header->type))
+    {
+      is_debug++;
+      zlog_debug ("LSA Receive from %s", from->name);
+      ospf6_lsa_header_print (new);
+    }
+
+  /* (1) LSA Checksum */
+  if (! ospf6_lsa_checksum_valid (new->header))
+    {
+      if (is_debug)
+        zlog_debug ("Wrong LSA Checksum, discard");
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (2) Examine the LSA's LS type. 
+     RFC2470 3.5.1. Receiving Link State Update packets  */
+  if (IS_AREA_STUB (from->ospf6_if->area) &&
+      OSPF6_LSA_SCOPE (new->header->type) == OSPF6_SCOPE_AS)
+    {
+      if (is_debug)
+        zlog_debug ("AS-External-LSA (or AS-scope LSA) in stub area, discard");
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (3) LSA which have reserved scope is discarded
+     RFC2470 3.5.1. Receiving Link State Update packets  */
+  /* Flooding scope check. LSAs with unknown scope are discarded here.
+     Set appropriate LSDB for the LSA */
+  switch (OSPF6_LSA_SCOPE (new->header->type))
+    {
+    case OSPF6_SCOPE_LINKLOCAL:
+      new->lsdb = from->ospf6_if->lsdb;
+      break;
+    case OSPF6_SCOPE_AREA:
+      new->lsdb = from->ospf6_if->area->lsdb;
+      break;
+    case OSPF6_SCOPE_AS:
+      new->lsdb = from->ospf6_if->area->ospf6->lsdb;
+      break;
+    default:
+      if (is_debug)
+        zlog_debug ("LSA has reserved scope, discard");
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (4) if MaxAge LSA and if we have no instance, and no neighbor
+         is in states Exchange or Loading */
+  if (ospf6_is_maxage_lsa_drop (new, from))
+    {
+      /* log */
+      if (is_debug)
+       zlog_debug ("Drop MaxAge LSA with direct acknowledgement.");
+
+      /* a) Acknowledge back to neighbor (Direct acknowledgement, 13.5) */
+      ospf6_lsdb_add (ospf6_lsa_copy (new), from->lsack_list);
+      if (from->thread_send_lsack == NULL)
+        from->thread_send_lsack =
+          thread_add_event (master, ospf6_lsack_send_neighbor, from, 0);
+
+      /* b) Discard */
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (5) */
+  /* lookup the same database copy in lsdb */
+  old = ospf6_lsdb_lookup (new->header->type, new->header->id,
+                           new->header->adv_router, new->lsdb);
+  if (old)
+    {
+      ismore_recent = ospf6_lsa_compare (new, old);
+      if (ntohl (new->header->seqnum) == ntohl (old->header->seqnum))
+        {
+          if (is_debug)
+            zlog_debug ("Received is duplicated LSA");
+          SET_FLAG (new->flag, OSPF6_LSA_DUPLICATE);
+        }
+    }
+
+  /* if no database copy or received is more recent */
+  if (old == NULL || ismore_recent < 0)
+    {
+      /* in case we have no database copy */
+      ismore_recent = -1;
+
+      /* (a) MinLSArrival check */
+      if (old)
+        {
+          struct timeval now, res;
+          quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+          timersub (&now, &old->installed, &res);
+          if (res.tv_sec < (OSPF_MIN_LS_ARRIVAL / 1000))
+            {
+              if (is_debug)
+                zlog_debug ("LSA can't be updated within MinLSArrival, discard");
+              ospf6_lsa_delete (new);
+              return;   /* examin next lsa */
+            }
+        }
+
+      quagga_gettime (QUAGGA_CLK_MONOTONIC, &new->received);
+
+      if (is_debug)
+        zlog_debug ("Install, Flood, Possibly acknowledge the received LSA");
+
+      /* Remove older copies of this LSA from retx lists */
+      if (old)
+       ospf6_flood_clear (old);
+
+      /* (b) immediately flood and (c) remove from all retrans-list */
+      /* Prevent self-originated LSA to be flooded. this is to make
+      reoriginated instance of the LSA not to be rejected by other routers
+      due to MinLSArrival. */
+      if (new->header->adv_router != from->ospf6_if->area->ospf6->router_id)
+        ospf6_flood (from, new);
+
+      /* (d), installing lsdb, which may cause routing
+              table calculation (replacing database copy) */
+      ospf6_install_lsa (new);
+
+      /* (e) possibly acknowledge */
+      ospf6_acknowledge_lsa (new, ismore_recent, from);
+
+      /* (f) Self Originated LSA, section 13.4 */
+      if (new->header->adv_router == from->ospf6_if->area->ospf6->router_id)
+        {
+          /* Self-originated LSA (newer than ours) is received from
+             another router. We have to make a new instance of the LSA
+             or have to flush this LSA. */
+          if (is_debug)
+            {
+              zlog_debug ("Newer instance of the self-originated LSA");
+              zlog_debug ("Schedule reorigination");
+            }
+          new->refresh = thread_add_event (master, ospf6_lsa_refresh, new, 0);
+        }
+
+      return;
+    }
+
+  /* (6) if there is instance on sending neighbor's request list */
+  if (ospf6_lsdb_lookup (new->header->type, new->header->id,
+                         new->header->adv_router, from->request_list))
+    {
+      /* if no database copy, should go above state (5) */
+      assert (old);
+
+      if (is_debug)
+        {
+          zlog_debug ("Received is not newer, on the neighbor's request-list");
+          zlog_debug ("BadLSReq, discard the received LSA");
+        }
+
+      /* BadLSReq */
+      thread_add_event (master, bad_lsreq, from, 0);
+
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (7) if neither one is more recent */
+  if (ismore_recent == 0)
+    {
+      if (is_debug)
+        zlog_debug ("The same instance as database copy (neither recent)");
+
+      /* (a) if on retrans-list, Treat this LSA as an Ack: Implied Ack */
+      rem = ospf6_lsdb_lookup (new->header->type, new->header->id,
+                               new->header->adv_router, from->retrans_list);
+      if (rem)
+        {
+          if (is_debug)
+            {
+              zlog_debug ("It is on the neighbor's retrans-list.");
+              zlog_debug ("Treat as an Implied acknowledgement");
+            }
+          SET_FLAG (new->flag, OSPF6_LSA_IMPLIEDACK);
+          ospf6_decrement_retrans_count (rem);
+          ospf6_lsdb_remove (rem, from->retrans_list);
+        }
+
+      if (is_debug)
+        zlog_debug ("Possibly acknowledge and then discard");
+
+      /* (b) possibly acknowledge */
+      ospf6_acknowledge_lsa (new, ismore_recent, from);
+
+      ospf6_lsa_delete (new);
+      return;
+    }
+
+  /* (8) previous database copy is more recent */
+    {
+      assert (old);
+
+      /* If database copy is in 'Seqnumber Wrapping',
+         simply discard the received LSA */
+      if (OSPF6_LSA_IS_MAXAGE (old) &&
+          old->header->seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER))
+        {
+          if (is_debug)
+            {
+              zlog_debug ("The LSA is in Seqnumber Wrapping");
+              zlog_debug ("MaxAge & MaxSeqNum, discard");
+            }
+         ospf6_lsa_delete (new);
+         return;
+        }
+
+      /* Otherwise, Send database copy of this LSA to this neighbor */
+        {
+          if (is_debug)
+            {
+              zlog_debug ("Database copy is more recent.");
+              zlog_debug ("Send back directly and then discard");
+            }
+
+          /* XXX, MinLSArrival check !? RFC 2328 13 (8) */
+
+          ospf6_lsdb_add (ospf6_lsa_copy (old), from->lsupdate_list);
+          if (from->thread_send_lsupdate == NULL)
+            from->thread_send_lsupdate =
+              thread_add_event (master, ospf6_lsupdate_send_neighbor, from, 0);
+         ospf6_lsa_delete (new);
+         return;
+        }
+      return;
+    }
+}
+
+
+DEFUN (debug_ospf6_flooding,
+       debug_ospf6_flooding_cmd,
+       "debug ospf6 flooding",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 flooding function\n"
+      )
+{
+  OSPF6_DEBUG_FLOODING_ON ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_flooding,
+       no_debug_ospf6_flooding_cmd,
+       "no debug ospf6 flooding",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 flooding function\n"
+      )
+{
+  OSPF6_DEBUG_FLOODING_OFF ();
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_flood (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_FLOODING)
+    vty_out (vty, "debug ospf6 flooding%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_flood (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_flooding_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_flooding_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_flooding_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_flooding_cmd);
+}
+
+
+
+
+
diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h
new file mode 100644 (file)
index 0000000..3a6f300
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_FLOOD_H
+#define OSPF6_FLOOD_H
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_flooding;
+#define OSPF6_DEBUG_FLOODING_ON() \
+  (conf_debug_ospf6_flooding = 1)
+#define OSPF6_DEBUG_FLOODING_OFF() \
+  (conf_debug_ospf6_flooding = 0)
+#define IS_OSPF6_DEBUG_FLOODING \
+  (conf_debug_ospf6_flooding)
+
+/* Function Prototypes */
+extern struct ospf6_lsdb *ospf6_get_scoped_lsdb (struct ospf6_lsa *lsa);
+extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self (struct ospf6_lsa *lsa);
+
+/* origination & purging */
+extern void ospf6_lsa_originate (struct ospf6_lsa *lsa);
+extern void ospf6_lsa_originate_process (struct ospf6_lsa *lsa,
+                                         struct ospf6 *process);
+extern void ospf6_lsa_originate_area (struct ospf6_lsa *lsa,
+                                     struct ospf6_area *oa);
+extern void ospf6_lsa_originate_interface (struct ospf6_lsa *lsa,
+                                           struct ospf6_interface *oi);
+extern void ospf6_lsa_purge (struct ospf6_lsa *lsa);
+
+/* access method to retrans_count */
+extern void ospf6_increment_retrans_count (struct ospf6_lsa *lsa);
+extern void ospf6_decrement_retrans_count (struct ospf6_lsa *lsa);
+
+/* flooding & clear flooding */
+extern void ospf6_flood_clear (struct ospf6_lsa *lsa);
+extern void ospf6_flood (struct ospf6_neighbor *from, struct ospf6_lsa *lsa);
+
+/* receive & install */
+extern void ospf6_receive_lsa (struct ospf6_neighbor *from,
+                               struct ospf6_lsa_header *header);
+extern void ospf6_install_lsa (struct ospf6_lsa *lsa);
+
+extern int config_write_ospf6_debug_flood (struct vty *vty);
+extern void install_element_ospf6_debug_flood (void);
+
+#endif /* OSPF6_FLOOD_H */
+
+
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
new file mode 100644 (file)
index 0000000..fa6509f
--- /dev/null
@@ -0,0 +1,2000 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "if.h"
+#include "log.h"
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "plist.h"
+
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_network.h"
+#include "ospf6_message.h"
+#include "ospf6_route.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_intra.h"
+#include "ospf6_spf.h"
+#include "ospf6_snmp.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_interface = 0;
+
+const char *ospf6_interface_state_str[] =
+{
+  "None",
+  "Down",
+  "Loopback",
+  "Waiting",
+  "PointToPoint",
+  "DROther",
+  "BDR",
+  "DR",
+  NULL
+};
+
+struct ospf6_interface *
+ospf6_interface_lookup_by_ifindex (ifindex_t ifindex)
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = if_lookup_by_index (ifindex);
+  if (ifp == NULL)
+    return (struct ospf6_interface *) NULL;
+
+  oi = (struct ospf6_interface *) ifp->info;
+  return oi;
+}
+
+/* schedule routing table recalculation */
+static void
+ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa, unsigned int reason)
+{
+  struct ospf6_interface *oi;
+
+  if (lsa == NULL)
+    return;
+
+  oi = lsa->lsdb->data;
+  switch (ntohs (lsa->header->type))
+    {
+      case OSPF6_LSTYPE_LINK:
+        if (oi->state == OSPF6_INTERFACE_DR)
+          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+        ospf6_spf_schedule (oi->area->ospf6, reason);
+        break;
+
+      default:
+        break;
+    }
+}
+
+static void
+ospf6_interface_lsdb_hook_add (struct ospf6_lsa *lsa)
+{
+  ospf6_interface_lsdb_hook(lsa, ospf6_lsadd_to_spf_reason(lsa));
+}
+
+static void
+ospf6_interface_lsdb_hook_remove (struct ospf6_lsa *lsa)
+{
+  ospf6_interface_lsdb_hook(lsa, ospf6_lsremove_to_spf_reason(lsa));
+}
+
+static u_char
+ospf6_default_iftype(struct interface *ifp)
+{
+  if (if_is_pointopoint (ifp))
+    return OSPF_IFTYPE_POINTOPOINT;
+  else if (if_is_loopback (ifp))
+    return OSPF_IFTYPE_LOOPBACK;
+  else
+    return OSPF_IFTYPE_BROADCAST;
+}
+
+static u_int32_t
+ospf6_interface_get_cost (struct ospf6_interface *oi)
+{
+  /* If all else fails, use default OSPF cost */
+  u_int32_t cost;
+  u_int32_t bw, refbw;
+
+  bw = oi->interface->bandwidth ? oi->interface->bandwidth : OSPF6_INTERFACE_BANDWIDTH;
+  refbw = ospf6 ? ospf6->ref_bandwidth : OSPF6_REFERENCE_BANDWIDTH;
+
+  /* A specifed ip ospf cost overrides a calculated one. */
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST))
+    cost = oi->cost;
+  else
+    {
+      cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5);
+      if (cost < 1) cost = 1;
+      else if (cost > UINT32_MAX) cost = UINT32_MAX;
+    }
+
+  return cost;
+}
+
+static void
+ospf6_interface_recalculate_cost (struct ospf6_interface *oi)
+{
+  u_int32_t newcost;
+
+  newcost = ospf6_interface_get_cost (oi);
+  if (newcost == oi->cost) return;
+  oi->cost = newcost;
+
+  /* update cost held in route_connected list in ospf6_interface */
+  ospf6_interface_connected_route_update (oi->interface);
+
+  /* execute LSA hooks */
+  if (oi->area)
+    {
+      OSPF6_LINK_LSA_SCHEDULE (oi);
+      OSPF6_ROUTER_LSA_SCHEDULE (oi->area);
+      OSPF6_NETWORK_LSA_SCHEDULE (oi);
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+    }
+}
+
+/* Create new ospf6 interface structure */
+struct ospf6_interface *
+ospf6_interface_create (struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+  unsigned int iobuflen;
+
+  oi = (struct ospf6_interface *)
+    XCALLOC (MTYPE_OSPF6_IF, sizeof (struct ospf6_interface));
+
+  if (!oi)
+    {
+      zlog_err ("Can't malloc ospf6_interface for ifindex %d", ifp->ifindex);
+      return (struct ospf6_interface *) NULL;
+    }
+
+  oi->area = (struct ospf6_area *) NULL;
+  oi->neighbor_list = list_new ();
+  oi->neighbor_list->cmp = ospf6_neighbor_cmp;
+  oi->linklocal_addr = (struct in6_addr *) NULL;
+  oi->instance_id = OSPF6_INTERFACE_INSTANCE_ID;
+  oi->transdelay = OSPF6_INTERFACE_TRANSDELAY;
+  oi->priority = OSPF6_INTERFACE_PRIORITY;
+
+  oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT;
+  oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
+  oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+  oi->type = ospf6_default_iftype (ifp);
+  oi->state = OSPF6_INTERFACE_DOWN;
+  oi->flag = 0;
+  oi->mtu_ignore = 0;
+
+  /* Try to adjust I/O buffer size with IfMtu */
+  oi->ifmtu = ifp->mtu6;
+  iobuflen = ospf6_iobuf_size (ifp->mtu6);
+  if (oi->ifmtu > iobuflen)
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+        zlog_debug ("Interface %s: IfMtu is adjusted to I/O buffer size: %d.",
+                   ifp->name, iobuflen);
+      oi->ifmtu = iobuflen;
+    }
+
+  oi->lsupdate_list = ospf6_lsdb_create (oi);
+  oi->lsack_list = ospf6_lsdb_create (oi);
+  oi->lsdb = ospf6_lsdb_create (oi);
+  oi->lsdb->hook_add = ospf6_interface_lsdb_hook_add;
+  oi->lsdb->hook_remove = ospf6_interface_lsdb_hook_remove;
+  oi->lsdb_self = ospf6_lsdb_create (oi);
+
+  oi->route_connected = OSPF6_ROUTE_TABLE_CREATE (INTERFACE, CONNECTED_ROUTES);
+  oi->route_connected->scope = oi;
+
+  /* link both */
+  oi->interface = ifp;
+  ifp->info = oi;
+
+  /* Compute cost. */
+  oi->cost = ospf6_interface_get_cost(oi);
+
+  return oi;
+}
+
+void
+ospf6_interface_delete (struct ospf6_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+      ospf6_neighbor_delete (on);
+  
+  list_delete (oi->neighbor_list);
+
+  THREAD_OFF (oi->thread_send_hello);
+  THREAD_OFF (oi->thread_send_lsupdate);
+  THREAD_OFF (oi->thread_send_lsack);
+
+  ospf6_lsdb_remove_all (oi->lsdb);
+  ospf6_lsdb_remove_all (oi->lsupdate_list);
+  ospf6_lsdb_remove_all (oi->lsack_list);
+
+  ospf6_lsdb_delete (oi->lsdb);
+  ospf6_lsdb_delete (oi->lsdb_self);
+
+  ospf6_lsdb_delete (oi->lsupdate_list);
+  ospf6_lsdb_delete (oi->lsack_list);
+
+  ospf6_route_table_delete (oi->route_connected);
+
+  /* cut link */
+  oi->interface->info = NULL;
+
+  /* plist_name */
+  if (oi->plist_name)
+    XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name);
+
+  XFREE (MTYPE_OSPF6_IF, oi);
+}
+
+void
+ospf6_interface_enable (struct ospf6_interface *oi)
+{
+  UNSET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE);
+  ospf6_interface_state_update (oi->interface);
+}
+
+void
+ospf6_interface_disable (struct ospf6_interface *oi)
+{
+  SET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE);
+
+  thread_execute (master, interface_down, oi, 0);
+
+  ospf6_lsdb_remove_all (oi->lsdb);
+  ospf6_lsdb_remove_all (oi->lsdb_self);
+  ospf6_lsdb_remove_all (oi->lsupdate_list);
+  ospf6_lsdb_remove_all (oi->lsack_list);
+
+  THREAD_OFF (oi->thread_send_hello);
+  THREAD_OFF (oi->thread_send_lsupdate);
+  THREAD_OFF (oi->thread_send_lsack);
+
+  THREAD_OFF (oi->thread_network_lsa);
+  THREAD_OFF (oi->thread_link_lsa);
+  THREAD_OFF (oi->thread_intra_prefix_lsa);
+}
+
+static struct in6_addr *
+ospf6_interface_get_linklocal_address (struct interface *ifp)
+{
+  struct listnode *n;
+  struct connected *c;
+  struct in6_addr *l = (struct in6_addr *) NULL;
+
+  /* for each connected address */
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, n, c))
+    {
+      /* if family not AF_INET6, ignore */
+      if (c->address->family != AF_INET6)
+        continue;
+
+      /* linklocal scope check */
+      if (IN6_IS_ADDR_LINKLOCAL (&c->address->u.prefix6))
+        l = &c->address->u.prefix6;
+    }
+  return l;
+}
+
+void
+ospf6_interface_if_add (struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+  unsigned int iobuflen;
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    return;
+
+  /* Try to adjust I/O buffer size with IfMtu */
+  if (oi->ifmtu == 0)
+    oi->ifmtu = ifp->mtu6;
+  iobuflen = ospf6_iobuf_size (ifp->mtu6);
+  if (oi->ifmtu > iobuflen)
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+        zlog_debug ("Interface %s: IfMtu is adjusted to I/O buffer size: %d.",
+                   ifp->name, iobuflen);
+      oi->ifmtu = iobuflen;
+    }
+
+  /* interface start */
+  ospf6_interface_state_update(oi->interface);
+}
+
+void
+ospf6_interface_state_update (struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    return;
+  if (oi->area == NULL)
+    return;
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    return;
+
+  if (if_is_operative (ifp)
+      && (ospf6_interface_get_linklocal_address(oi->interface)
+          || if_is_loopback(oi->interface)))
+    thread_add_event (master, interface_up, oi, 0);
+  else
+    thread_add_event (master, interface_down, oi, 0);
+
+  return;
+}
+
+void
+ospf6_interface_connected_route_update (struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+  struct ospf6_route *route;
+  struct connected *c;
+  struct listnode *node, *nnode;
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    return;
+
+  /* reset linklocal pointer */
+  oi->linklocal_addr = ospf6_interface_get_linklocal_address (ifp);
+
+  /* if area is null, do not make connected-route list */
+  if (oi->area == NULL)
+    return;
+
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    return;
+
+  /* update "route to advertise" interface route table */
+  ospf6_route_remove_all (oi->route_connected);
+
+  for (ALL_LIST_ELEMENTS (oi->interface->connected, node, nnode, c))
+    {
+      if (c->address->family != AF_INET6)
+        continue;
+
+      CONTINUE_IF_ADDRESS_LINKLOCAL (IS_OSPF6_DEBUG_INTERFACE, c->address);
+      CONTINUE_IF_ADDRESS_UNSPECIFIED (IS_OSPF6_DEBUG_INTERFACE, c->address);
+      CONTINUE_IF_ADDRESS_LOOPBACK (IS_OSPF6_DEBUG_INTERFACE, c->address);
+      CONTINUE_IF_ADDRESS_V4COMPAT (IS_OSPF6_DEBUG_INTERFACE, c->address);
+      CONTINUE_IF_ADDRESS_V4MAPPED (IS_OSPF6_DEBUG_INTERFACE, c->address);
+
+      /* apply filter */
+      if (oi->plist_name)
+        {
+          struct prefix_list *plist;
+          enum prefix_list_type ret;
+          char buf[128];
+
+          prefix2str (c->address, buf, sizeof (buf));
+          plist = prefix_list_lookup (AFI_IP6, oi->plist_name);
+          ret = prefix_list_apply (plist, (void *) c->address);
+          if (ret == PREFIX_DENY)
+            {
+              if (IS_OSPF6_DEBUG_INTERFACE)
+                zlog_debug ("%s on %s filtered by prefix-list %s ",
+                           buf, oi->interface->name, oi->plist_name);
+              continue;
+            }
+        }
+
+      route = ospf6_route_create ();
+      memcpy (&route->prefix, c->address, sizeof (struct prefix));
+      apply_mask (&route->prefix);
+      route->type = OSPF6_DEST_TYPE_NETWORK;
+      route->path.area_id = oi->area->area_id;
+      route->path.type = OSPF6_PATH_TYPE_INTRA;
+      route->path.cost = oi->cost;
+      route->nexthop[0].ifindex = oi->interface->ifindex;
+      inet_pton (AF_INET6, "::1", &route->nexthop[0].address);
+      ospf6_route_add (route, oi->route_connected);
+    }
+
+  /* create new Link-LSA */
+  OSPF6_LINK_LSA_SCHEDULE (oi);
+  OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+  OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+}
+
+static void
+ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi)
+{
+  u_char prev_state;
+
+  prev_state = oi->state;
+  oi->state = next_state;
+
+  if (prev_state == next_state)
+    return;
+
+  /* log */
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    {
+      zlog_debug ("Interface state change %s: %s -> %s", oi->interface->name,
+                 ospf6_interface_state_str[prev_state],
+                 ospf6_interface_state_str[next_state]);
+    }
+  oi->state_change++;
+
+  if ((prev_state == OSPF6_INTERFACE_DR ||
+       prev_state == OSPF6_INTERFACE_BDR) &&
+      (next_state != OSPF6_INTERFACE_DR &&
+       next_state != OSPF6_INTERFACE_BDR))
+    ospf6_sso (oi->interface->ifindex, &alldrouters6, IPV6_LEAVE_GROUP);
+
+  if ((prev_state != OSPF6_INTERFACE_DR &&
+       prev_state != OSPF6_INTERFACE_BDR) &&
+      (next_state == OSPF6_INTERFACE_DR ||
+       next_state == OSPF6_INTERFACE_BDR))
+    ospf6_sso (oi->interface->ifindex, &alldrouters6, IPV6_JOIN_GROUP);
+
+  OSPF6_ROUTER_LSA_SCHEDULE (oi->area);
+  if (next_state == OSPF6_INTERFACE_DOWN)
+    {
+      OSPF6_NETWORK_LSA_EXECUTE (oi);
+      OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT (oi);
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+    }
+  else if (prev_state == OSPF6_INTERFACE_DR ||
+           next_state == OSPF6_INTERFACE_DR)
+    {
+      OSPF6_NETWORK_LSA_SCHEDULE (oi);
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+    }
+
+#ifdef HAVE_SNMP
+  /* Terminal state or regression */ 
+  if ((next_state == OSPF6_INTERFACE_POINTTOPOINT) ||
+      (next_state == OSPF6_INTERFACE_DROTHER) ||
+      (next_state == OSPF6_INTERFACE_BDR) ||
+      (next_state == OSPF6_INTERFACE_DR) ||
+      (next_state < prev_state))
+    ospf6TrapIfStateChange (oi);
+#endif
+
+}
+
+
+/* DR Election, RFC2328 section 9.4 */
+
+#define IS_ELIGIBLE(n) \
+  ((n)->state >= OSPF6_NEIGHBOR_TWOWAY && (n)->priority != 0)
+
+static struct ospf6_neighbor *
+better_bdrouter (struct ospf6_neighbor *a, struct ospf6_neighbor *b)
+{
+  if ((a == NULL || ! IS_ELIGIBLE (a) || a->drouter == a->router_id) &&
+      (b == NULL || ! IS_ELIGIBLE (b) || b->drouter == b->router_id))
+    return NULL;
+  else if (a == NULL || ! IS_ELIGIBLE (a) || a->drouter == a->router_id)
+    return b;
+  else if (b == NULL || ! IS_ELIGIBLE (b) || b->drouter == b->router_id)
+    return a;
+
+  if (a->bdrouter == a->router_id && b->bdrouter != b->router_id)
+    return a;
+  if (a->bdrouter != a->router_id && b->bdrouter == b->router_id)
+    return b;
+
+  if (a->priority > b->priority)
+    return a;
+  if (a->priority < b->priority)
+    return b;
+
+  if (ntohl (a->router_id) > ntohl (b->router_id))
+    return a;
+  if (ntohl (a->router_id) < ntohl (b->router_id))
+    return b;
+
+  zlog_warn ("Router-ID duplicate ?");
+  return a;
+}
+
+static struct ospf6_neighbor *
+better_drouter (struct ospf6_neighbor *a, struct ospf6_neighbor *b)
+{
+  if ((a == NULL || ! IS_ELIGIBLE (a) || a->drouter != a->router_id) &&
+      (b == NULL || ! IS_ELIGIBLE (b) || b->drouter != b->router_id))
+    return NULL;
+  else if (a == NULL || ! IS_ELIGIBLE (a) || a->drouter != a->router_id)
+    return b;
+  else if (b == NULL || ! IS_ELIGIBLE (b) || b->drouter != b->router_id)
+    return a;
+
+  if (a->drouter == a->router_id && b->drouter != b->router_id)
+    return a;
+  if (a->drouter != a->router_id && b->drouter == b->router_id)
+    return b;
+
+  if (a->priority > b->priority)
+    return a;
+  if (a->priority < b->priority)
+    return b;
+
+  if (ntohl (a->router_id) > ntohl (b->router_id))
+    return a;
+  if (ntohl (a->router_id) < ntohl (b->router_id))
+    return b;
+
+  zlog_warn ("Router-ID duplicate ?");
+  return a;
+}
+
+static u_char
+dr_election (struct ospf6_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on, *drouter, *bdrouter, myself;
+  struct ospf6_neighbor *best_drouter, *best_bdrouter;
+  u_char next_state = 0;
+
+  drouter = bdrouter = NULL;
+  best_drouter = best_bdrouter = NULL;
+
+  /* pseudo neighbor myself, including noting current DR/BDR (1) */
+  memset (&myself, 0, sizeof (myself));
+  inet_ntop (AF_INET, &oi->area->ospf6->router_id, myself.name,
+             sizeof (myself.name));
+  myself.state = OSPF6_NEIGHBOR_TWOWAY;
+  myself.drouter = oi->drouter;
+  myself.bdrouter = oi->bdrouter;
+  myself.priority = oi->priority;
+  myself.router_id = oi->area->ospf6->router_id;
+
+  /* Electing BDR (2) */
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    bdrouter = better_bdrouter (bdrouter, on);
+  
+  best_bdrouter = bdrouter;
+  bdrouter = better_bdrouter (best_bdrouter, &myself);
+
+  /* Electing DR (3) */
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    drouter = better_drouter (drouter, on);
+
+  best_drouter = drouter;
+  drouter = better_drouter (best_drouter, &myself);
+  if (drouter == NULL)
+    drouter = bdrouter;
+
+  /* the router itself is newly/no longer DR/BDR (4) */
+  if ((drouter == &myself && myself.drouter != myself.router_id) ||
+      (drouter != &myself && myself.drouter == myself.router_id) ||
+      (bdrouter == &myself && myself.bdrouter != myself.router_id) ||
+      (bdrouter != &myself && myself.bdrouter == myself.router_id))
+    {
+      myself.drouter = (drouter ? drouter->router_id : htonl (0));
+      myself.bdrouter = (bdrouter ? bdrouter->router_id : htonl (0));
+
+      /* compatible to Electing BDR (2) */
+      bdrouter = better_bdrouter (best_bdrouter, &myself);
+
+      /* compatible to Electing DR (3) */
+      drouter = better_drouter (best_drouter, &myself);
+      if (drouter == NULL)
+        drouter = bdrouter;
+    }
+
+  /* Set interface state accordingly (5) */
+  if (drouter && drouter == &myself)
+    next_state = OSPF6_INTERFACE_DR;
+  else if (bdrouter && bdrouter == &myself)
+    next_state = OSPF6_INTERFACE_BDR;
+  else
+    next_state = OSPF6_INTERFACE_DROTHER;
+
+  /* If NBMA, schedule Start for each neighbor having priority of 0 (6) */
+  /* XXX */
+
+  /* If DR or BDR change, invoke AdjOK? for each neighbor (7) */
+  /* RFC 2328 section 12.4. Originating LSAs (3) will be handled
+     accordingly after AdjOK */
+  if (oi->drouter != (drouter ? drouter->router_id : htonl (0)) ||
+      oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl (0)))
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+        zlog_debug ("DR Election on %s: DR: %s BDR: %s", oi->interface->name,
+                   (drouter ? drouter->name : "0.0.0.0"),
+                   (bdrouter ? bdrouter->name : "0.0.0.0"));
+
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, node, on))
+        {
+          if (on->state < OSPF6_NEIGHBOR_TWOWAY)
+            continue;
+          /* Schedule AdjOK. */
+          thread_add_event (master, adj_ok, on, 0);
+        }
+    }
+
+  oi->drouter = (drouter ? drouter->router_id : htonl (0));
+  oi->bdrouter = (bdrouter ? bdrouter->router_id : htonl (0));
+  return next_state;
+}
+
+
+/* Interface State Machine */
+int
+interface_up (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  assert (oi && oi->interface);
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface Event %s: [InterfaceUp]",
+               oi->interface->name);
+
+  /* check physical interface is up */
+  if (! if_is_operative (oi->interface))
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+        zlog_debug ("Interface %s is down, can't execute [InterfaceUp]",
+                   oi->interface->name);
+      return 0;
+    }
+
+  /* check interface has a link-local address */
+  if (! (ospf6_interface_get_linklocal_address(oi->interface)
+         || if_is_loopback(oi->interface)))
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+       zlog_debug ("Interface %s has no link local address, can't execute [InterfaceUp]",
+                   oi->interface->name);
+      return 0;
+    }
+
+  /* Recompute cost */
+  ospf6_interface_recalculate_cost (oi);
+
+  /* if already enabled, do nothing */
+  if (oi->state > OSPF6_INTERFACE_DOWN)
+    {
+      if (IS_OSPF6_DEBUG_INTERFACE)
+        zlog_debug ("Interface %s already enabled",
+                   oi->interface->name);
+      return 0;
+    }
+
+  /* If no area assigned, return */
+  if (oi->area == NULL)
+    {
+      zlog_debug ("%s: Not scheduleing Hello for %s as there is no area assigned yet", __func__,
+                 oi->interface->name);
+      return 0;
+    }
+
+  /* Join AllSPFRouters */
+  if (ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP) < 0)
+    {
+      if (oi->sso_try_cnt++ < OSPF6_INTERFACE_SSO_RETRY_MAX)
+        {
+          zlog_info("Scheduling %s for sso retry, trial count: %d",
+                    oi->interface->name, oi->sso_try_cnt);
+          thread_add_timer (master, interface_up, oi,
+                            OSPF6_INTERFACE_SSO_RETRY_INT);
+        }
+      return 0;
+    }
+  oi->sso_try_cnt = 0; /* Reset on success */
+
+  /* Update interface route */
+  ospf6_interface_connected_route_update (oi->interface);
+
+  /* Schedule Hello */
+  if (! CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE))
+    oi->thread_send_hello = thread_add_event (master, ospf6_hello_send, oi, 0);
+
+  /* decide next interface state */
+  if ((if_is_pointopoint (oi->interface)) ||
+      (oi->type == OSPF_IFTYPE_POINTOPOINT)) {
+    ospf6_interface_state_change (OSPF6_INTERFACE_POINTTOPOINT, oi);
+  }
+  else if (oi->priority == 0)
+    ospf6_interface_state_change (OSPF6_INTERFACE_DROTHER, oi);
+  else
+    {
+      ospf6_interface_state_change (OSPF6_INTERFACE_WAITING, oi);
+      thread_add_timer (master, wait_timer, oi, oi->dead_interval);
+    }
+
+  return 0;
+}
+
+int
+wait_timer (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  assert (oi && oi->interface);
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface Event %s: [WaitTimer]",
+               oi->interface->name);
+
+  if (oi->state == OSPF6_INTERFACE_WAITING)
+    ospf6_interface_state_change (dr_election (oi), oi);
+
+  return 0;
+}
+
+int
+backup_seen (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  assert (oi && oi->interface);
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface Event %s: [BackupSeen]",
+               oi->interface->name);
+
+  if (oi->state == OSPF6_INTERFACE_WAITING)
+    ospf6_interface_state_change (dr_election (oi), oi);
+
+  return 0;
+}
+
+int
+neighbor_change (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  assert (oi && oi->interface);
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface Event %s: [NeighborChange]",
+               oi->interface->name);
+
+  if (oi->state == OSPF6_INTERFACE_DROTHER ||
+      oi->state == OSPF6_INTERFACE_BDR ||
+      oi->state == OSPF6_INTERFACE_DR)
+    ospf6_interface_state_change (dr_election (oi), oi);
+
+  return 0;
+}
+
+int
+interface_down (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  assert (oi && oi->interface);
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface Event %s: [InterfaceDown]",
+               oi->interface->name);
+
+  /* Stop Hellos */
+  THREAD_OFF (oi->thread_send_hello);
+
+  /* Leave AllSPFRouters */
+  if (oi->state > OSPF6_INTERFACE_DOWN)
+    ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_LEAVE_GROUP);
+
+  ospf6_interface_state_change (OSPF6_INTERFACE_DOWN, oi);
+
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    ospf6_neighbor_delete (on);
+  
+  list_delete_all_node (oi->neighbor_list);
+
+  /* When interface state is reset, also reset information about
+   * DR election, as it is no longer valid. */
+  oi->drouter = oi->prev_drouter = htonl(0);
+  oi->bdrouter = oi->prev_bdrouter = htonl(0);
+  return 0;
+}
+
+
+/* show specified interface structure */
+static int
+ospf6_interface_show (struct vty *vty, struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+  struct connected *c;
+  struct prefix *p;
+  struct listnode *i;
+  char strbuf[64], drouter[32], bdrouter[32];
+  const char *updown[3] = {"down", "up", NULL};
+  const char *type;
+  struct timeval res, now;
+  char duration[32];
+  struct ospf6_lsa *lsa;
+
+  /* check physical interface type */
+  if (if_is_loopback (ifp))
+    type = "LOOPBACK";
+  else if (if_is_broadcast (ifp))
+    type = "BROADCAST";
+  else if (if_is_pointopoint (ifp))
+    type = "POINTOPOINT";
+  else
+    type = "UNKNOWN";
+
+  vty_out (vty, "%s is %s, type %s%s",
+           ifp->name, updown[if_is_operative (ifp)], type,
+          VNL);
+  vty_out (vty, "  Interface ID: %d%s", ifp->ifindex, VNL);
+
+  if (ifp->info == NULL)
+    {
+      vty_out (vty, "   OSPF not enabled on this interface%s", VNL);
+      return 0;
+    }
+  else
+    oi = (struct ospf6_interface *) ifp->info;
+
+  vty_out (vty, "  Internet Address:%s", VNL);
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, i, c))
+    {
+      p = c->address;
+      prefix2str (p, strbuf, sizeof (strbuf));
+      switch (p->family)
+        {
+        case AF_INET:
+          vty_out (vty, "    inet : %s%s", strbuf,
+                  VNL);
+          break;
+        case AF_INET6:
+          vty_out (vty, "    inet6: %s%s", strbuf,
+                  VNL);
+          break;
+        default:
+          vty_out (vty, "    ???  : %s%s", strbuf,
+                  VNL);
+          break;
+        }
+    }
+
+  if (oi->area)
+    {
+      vty_out (vty, "  Instance ID %d, Interface MTU %d (autodetect: %d)%s",
+              oi->instance_id, oi->ifmtu, ifp->mtu6, VNL);
+      vty_out (vty, "  MTU mismatch detection: %s%s", oi->mtu_ignore ?
+              "disabled" : "enabled", VNL);
+      inet_ntop (AF_INET, &oi->area->area_id,
+                 strbuf, sizeof (strbuf));
+      vty_out (vty, "  Area ID %s, Cost %u%s", strbuf, oi->cost,
+              VNL);
+    }
+  else
+    vty_out (vty, "  Not Attached to Area%s", VNL);
+
+  vty_out (vty, "  State %s, Transmit Delay %d sec, Priority %d%s",
+           ospf6_interface_state_str[oi->state],
+           oi->transdelay, oi->priority,
+          VNL);
+  vty_out (vty, "  Timer intervals configured:%s", VNL);
+  vty_out (vty, "   Hello %d, Dead %d, Retransmit %d%s",
+           oi->hello_interval, oi->dead_interval, oi->rxmt_interval,
+          VNL);
+
+  inet_ntop (AF_INET, &oi->drouter, drouter, sizeof (drouter));
+  inet_ntop (AF_INET, &oi->bdrouter, bdrouter, sizeof (bdrouter));
+  vty_out (vty, "  DR: %s BDR: %s%s", drouter, bdrouter, VNL);
+
+  vty_out (vty, "  Number of I/F scoped LSAs is %u%s",
+           oi->lsdb->count, VNL);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+
+  timerclear (&res);
+  if (oi->thread_send_lsupdate)
+    timersub (&oi->thread_send_lsupdate->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for LSUpdate in Time %s [thread %s]%s",
+           oi->lsupdate_list->count, duration,
+           (oi->thread_send_lsupdate ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (oi->lsupdate_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  timerclear (&res);
+  if (oi->thread_send_lsack)
+    timersub (&oi->thread_send_lsack->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for LSAck in Time %s [thread %s]%s",
+           oi->lsack_list->count, duration,
+           (oi->thread_send_lsack ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (oi->lsack_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  return 0;
+}
+
+/* show interface */
+DEFUN (show_ipv6_ospf6_interface,
+       show_ipv6_ospf6_interface_ifname_cmd,
+       "show ipv6 ospf6 interface IFNAME",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       IFNAME_STR
+       )
+{
+  struct interface *ifp;
+  struct listnode *i;
+
+  if (argc)
+    {
+      ifp = if_lookup_by_name (argv[0]);
+      if (ifp == NULL)
+        {
+          vty_out (vty, "No such Interface: %s%s", argv[0],
+                   VNL);
+          return CMD_WARNING;
+        }
+      ospf6_interface_show (vty, ifp);
+    }
+  else
+    {
+      for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp))
+        ospf6_interface_show (vty, ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_interface,
+       show_ipv6_ospf6_interface_cmd,
+       "show ipv6 ospf6 interface",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       )
+
+DEFUN (show_ipv6_ospf6_interface_ifname_prefix,
+       show_ipv6_ospf6_interface_ifname_prefix_cmd,
+       "show ipv6 ospf6 interface IFNAME prefix",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       IFNAME_STR
+       "Display connected prefixes to advertise\n"
+       )
+{
+  struct interface *ifp;
+  struct ospf6_interface *oi;
+
+  ifp = if_lookup_by_name (argv[0]);
+  if (ifp == NULL)
+    {
+      vty_out (vty, "No such Interface: %s%s", argv[0], VNL);
+      return CMD_WARNING;
+    }
+
+  oi = ifp->info;
+  if (oi == NULL)
+    {
+      vty_out (vty, "OSPFv3 is not enabled on %s%s", argv[0], VNL);
+      return CMD_WARNING;
+    }
+
+  argc--;
+  argv++;
+  ospf6_route_table_show (vty, argc, argv, oi->route_connected);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_interface_ifname_prefix,
+       show_ipv6_ospf6_interface_ifname_prefix_detail_cmd,
+       "show ipv6 ospf6 interface IFNAME prefix (X:X::X:X|X:X::X:X/M|detail)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       IFNAME_STR
+       "Display connected prefixes to advertise\n"
+       OSPF6_ROUTE_ADDRESS_STR
+       OSPF6_ROUTE_PREFIX_STR
+       "Display details of the prefixes\n"
+       )
+
+ALIAS (show_ipv6_ospf6_interface_ifname_prefix,
+       show_ipv6_ospf6_interface_ifname_prefix_match_cmd,
+       "show ipv6 ospf6 interface IFNAME prefix X:X::X:X/M (match|detail)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       IFNAME_STR
+       "Display connected prefixes to advertise\n"
+       OSPF6_ROUTE_PREFIX_STR
+       OSPF6_ROUTE_MATCH_STR
+       "Display details of the prefixes\n"
+       )
+
+DEFUN (show_ipv6_ospf6_interface_prefix,
+       show_ipv6_ospf6_interface_prefix_cmd,
+       "show ipv6 ospf6 interface prefix",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       "Display connected prefixes to advertise\n"
+       )
+{
+  struct listnode *i;
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp))
+    {
+      oi = (struct ospf6_interface *) ifp->info;
+      if (oi == NULL)
+        continue;
+
+      ospf6_route_table_show (vty, argc, argv, oi->route_connected);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_interface_prefix,
+       show_ipv6_ospf6_interface_prefix_detail_cmd,
+       "show ipv6 ospf6 interface prefix (X:X::X:X|X:X::X:X/M|detail)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       "Display connected prefixes to advertise\n"
+       OSPF6_ROUTE_ADDRESS_STR
+       OSPF6_ROUTE_PREFIX_STR
+       "Display details of the prefixes\n"
+       )
+
+ALIAS (show_ipv6_ospf6_interface_prefix,
+       show_ipv6_ospf6_interface_prefix_match_cmd,
+       "show ipv6 ospf6 interface prefix X:X::X:X/M (match|detail)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       "Display connected prefixes to advertise\n"
+       OSPF6_ROUTE_PREFIX_STR
+       OSPF6_ROUTE_MATCH_STR
+       "Display details of the prefixes\n"
+       )
+
+
+/* interface variable set command */
+DEFUN (ipv6_ospf6_ifmtu,
+       ipv6_ospf6_ifmtu_cmd,
+       "ipv6 ospf6 ifmtu <1-65535>",
+       IP6_STR
+       OSPF6_STR
+       "Interface MTU\n"
+       "OSPFv3 Interface MTU\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  unsigned int ifmtu, iobuflen;
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  ifmtu = strtol (argv[0], NULL, 10);
+
+  if (oi->ifmtu == ifmtu)
+    return CMD_SUCCESS;
+
+  if (ifp->mtu6 != 0 && ifp->mtu6 < ifmtu)
+    {
+      vty_out (vty, "%s's ospf6 ifmtu cannot go beyond physical mtu (%d)%s",
+               ifp->name, ifp->mtu6, VNL);
+      return CMD_WARNING;
+    }
+
+  if (oi->ifmtu < ifmtu)
+    {
+      iobuflen = ospf6_iobuf_size (ifmtu);
+      if (iobuflen < ifmtu)
+        {
+          vty_out (vty, "%s's ifmtu is adjusted to I/O buffer size (%d).%s",
+                   ifp->name, iobuflen, VNL);
+          oi->ifmtu = iobuflen;
+        }
+      else
+        oi->ifmtu = ifmtu;
+    }
+  else
+    oi->ifmtu = ifmtu;
+
+  /* re-establish adjacencies */
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      THREAD_OFF (on->inactivity_timer);
+      thread_add_event (master, inactivity_timer, on, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_ifmtu,
+       no_ipv6_ospf6_ifmtu_cmd,
+       "no ipv6 ospf6 ifmtu",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "Interface MTU\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  unsigned int iobuflen;
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  if (oi->ifmtu < ifp->mtu)
+    {
+      iobuflen = ospf6_iobuf_size (ifp->mtu);
+      if (iobuflen < ifp->mtu)
+        {
+          vty_out (vty, "%s's ifmtu is adjusted to I/O buffer size (%d).%s",
+                   ifp->name, iobuflen, VNL);
+          oi->ifmtu = iobuflen;
+        }
+      else
+        oi->ifmtu = ifp->mtu;
+    }
+  else
+    oi->ifmtu = ifp->mtu;
+
+  /* re-establish adjacencies */
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      THREAD_OFF (on->inactivity_timer);
+      thread_add_event (master, inactivity_timer, on, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_cost,
+       ipv6_ospf6_cost_cmd,
+       "ipv6 ospf6 cost <1-65535>",
+       IP6_STR
+       OSPF6_STR
+       "Interface cost\n"
+       "Outgoing metric of this interface\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  unsigned long int lcost;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  lcost = strtol (argv[0], NULL, 10);
+
+  if (lcost > UINT32_MAX)
+    {
+      vty_out (vty, "Cost %ld is out of range%s", lcost, VNL);
+      return CMD_WARNING;
+    }
+  
+  if (oi->cost == lcost)
+    return CMD_SUCCESS;
+  
+  oi->cost = lcost;
+  SET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST);
+
+  ospf6_interface_recalculate_cost(oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_cost,
+       no_ipv6_ospf6_cost_cmd,
+       "no ipv6 ospf6 cost",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "Calculate interface cost from bandwidth\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  UNSET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST);
+
+  ospf6_interface_recalculate_cost(oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (auto_cost_reference_bandwidth,
+       auto_cost_reference_bandwidth_cmd,
+       "auto-cost reference-bandwidth <1-4294967>",
+       "Calculate OSPF interface cost according to bandwidth\n"
+       "Use reference bandwidth method to assign OSPF cost\n"
+       "The reference bandwidth in terms of Mbits per second\n")
+{
+  struct ospf6 *o = vty->index;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  struct listnode *i, *j;
+  u_int32_t refbw;
+
+  refbw = strtol (argv[0], NULL, 10);
+  if (refbw < 1 || refbw > 4294967)
+    {
+      vty_out (vty, "reference-bandwidth value is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* If reference bandwidth is changed. */
+  if ((refbw * 1000) == o->ref_bandwidth)
+    return CMD_SUCCESS;
+
+  o->ref_bandwidth = refbw * 1000;
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+          ospf6_interface_recalculate_cost (oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_auto_cost_reference_bandwidth,
+       no_auto_cost_reference_bandwidth_cmd,
+       "no auto-cost reference-bandwidth",
+       NO_STR
+       "Calculate OSPF interface cost according to bandwidth\n"
+       "Use reference bandwidth method to assign OSPF cost\n")
+{
+  struct ospf6 *o = vty->index;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  struct listnode *i, *j;
+
+  if (o->ref_bandwidth == OSPF6_REFERENCE_BANDWIDTH)
+    return CMD_SUCCESS;
+
+  o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH;
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+          ospf6_interface_recalculate_cost (oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_hellointerval,
+       ipv6_ospf6_hellointerval_cmd,
+       "ipv6 ospf6 hello-interval <1-65535>",
+       IP6_STR
+       OSPF6_STR
+       "Interval time of Hello packets\n"
+       SECONDS_STR
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->hello_interval = strtol (argv[0], NULL, 10);
+  return CMD_SUCCESS;
+}
+
+/* interface variable set command */
+DEFUN (ipv6_ospf6_deadinterval,
+       ipv6_ospf6_deadinterval_cmd,
+       "ipv6 ospf6 dead-interval <1-65535>",
+       IP6_STR
+       OSPF6_STR
+       "Interval time after which a neighbor is declared down\n"
+       SECONDS_STR
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->dead_interval = strtol (argv[0], NULL, 10);
+  return CMD_SUCCESS;
+}
+
+/* interface variable set command */
+DEFUN (ipv6_ospf6_transmitdelay,
+       ipv6_ospf6_transmitdelay_cmd,
+       "ipv6 ospf6 transmit-delay <1-3600>",
+       IP6_STR
+       OSPF6_STR
+       "Transmit delay of this interface\n"
+       SECONDS_STR
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->transdelay = strtol (argv[0], NULL, 10);
+  return CMD_SUCCESS;
+}
+
+/* interface variable set command */
+DEFUN (ipv6_ospf6_retransmitinterval,
+       ipv6_ospf6_retransmitinterval_cmd,
+       "ipv6 ospf6 retransmit-interval <1-65535>",
+       IP6_STR
+       OSPF6_STR
+       "Time between retransmitting lost link state advertisements\n"
+       SECONDS_STR
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->rxmt_interval = strtol (argv[0], NULL, 10);
+  return CMD_SUCCESS;
+}
+
+/* interface variable set command */
+DEFUN (ipv6_ospf6_priority,
+       ipv6_ospf6_priority_cmd,
+       "ipv6 ospf6 priority <0-255>",
+       IP6_STR
+       OSPF6_STR
+       "Router priority\n"
+       "Priority value\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->priority = strtol (argv[0], NULL, 10);
+
+  if (oi->area &&
+      (oi->state == OSPF6_INTERFACE_DROTHER ||
+       oi->state == OSPF6_INTERFACE_BDR ||
+       oi->state == OSPF6_INTERFACE_DR))
+    ospf6_interface_state_change (dr_election (oi), oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_instance,
+       ipv6_ospf6_instance_cmd,
+       "ipv6 ospf6 instance-id <0-255>",
+       IP6_STR
+       OSPF6_STR
+       "Instance ID for this interface\n"
+       "Instance ID value\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *)vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *)ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->instance_id = strtol (argv[0], NULL, 10);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_passive,
+       ipv6_ospf6_passive_cmd,
+       "ipv6 ospf6 passive",
+       IP6_STR
+       OSPF6_STR
+       "passive interface, No adjacency will be formed on this interface\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  SET_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE);
+  THREAD_OFF (oi->thread_send_hello);
+
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      THREAD_OFF (on->inactivity_timer);
+      thread_add_event (master, inactivity_timer, on, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_passive,
+       no_ipv6_ospf6_passive_cmd,
+       "no ipv6 ospf6 passive",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "passive interface: No Adjacency will be formed on this I/F\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  UNSET_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE);
+  THREAD_OFF (oi->thread_send_hello);
+  oi->thread_send_hello =
+    thread_add_event (master, ospf6_hello_send, oi, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_mtu_ignore,
+       ipv6_ospf6_mtu_ignore_cmd,
+       "ipv6 ospf6 mtu-ignore",
+       IP6_STR
+       OSPF6_STR
+       "Ignore MTU mismatch on this interface\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->mtu_ignore = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_mtu_ignore,
+       no_ipv6_ospf6_mtu_ignore_cmd,
+       "no ipv6 ospf6 mtu-ignore",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "Ignore MTU mismatch on this interface\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  oi->mtu_ignore = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_advertise_prefix_list,
+       ipv6_ospf6_advertise_prefix_list_cmd,
+       "ipv6 ospf6 advertise prefix-list WORD",
+       IP6_STR
+       OSPF6_STR
+       "Advertising options\n"
+       "Filter prefix using prefix-list\n"
+       "Prefix list name\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  if (oi->plist_name)
+    XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name);
+  oi->plist_name = XSTRDUP (MTYPE_PREFIX_LIST_STR, argv[0]);
+
+  ospf6_interface_connected_route_update (oi->interface);
+
+  if (oi->area)
+    {
+      OSPF6_LINK_LSA_SCHEDULE (oi);
+      if (oi->state == OSPF6_INTERFACE_DR)
+        {
+          OSPF6_NETWORK_LSA_SCHEDULE (oi);
+          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+        }
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_advertise_prefix_list,
+       no_ipv6_ospf6_advertise_prefix_list_cmd,
+       "no ipv6 ospf6 advertise prefix-list",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "Advertising options\n"
+       "Filter prefix using prefix-list\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  assert (oi);
+
+  if (oi->plist_name)
+    {
+      XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name);
+      oi->plist_name = NULL;
+    }
+
+  ospf6_interface_connected_route_update (oi->interface);
+
+  if (oi->area)
+    {
+      OSPF6_LINK_LSA_SCHEDULE (oi);
+      if (oi->state == OSPF6_INTERFACE_DR)
+        {
+          OSPF6_NETWORK_LSA_SCHEDULE (oi);
+          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi);
+        }
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ospf6_network,
+       ipv6_ospf6_network_cmd,
+       "ipv6 ospf6 network (broadcast|point-to-point)",
+       IP6_STR
+       OSPF6_STR
+       "Network Type\n"
+       "Specify OSPFv6 broadcast network\n"
+       "Specify OSPF6 point-to-point network\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL) {
+    oi = ospf6_interface_create (ifp);
+  }
+  assert (oi);
+
+  if (strncmp (argv[0], "b", 1) == 0)
+    {
+      if (oi->type == OSPF_IFTYPE_BROADCAST)
+       return CMD_SUCCESS;
+
+      oi->type = OSPF_IFTYPE_BROADCAST;
+    }
+    else if (strncmp (argv[0], "point-to-p", 10) == 0)
+      {
+       if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
+         return CMD_SUCCESS;
+       }
+       oi->type = OSPF_IFTYPE_POINTOPOINT;
+      }
+
+  /* Reset the interface */
+  thread_add_event (master, interface_down, oi, 0);
+  thread_add_event (master, interface_up, oi, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_network,
+       no_ipv6_ospf6_network_cmd,
+       "no ipv6 ospf6 network",
+       NO_STR
+       IP6_STR
+       OSPF6_STR
+       "Network Type\n"
+       "Default to whatever interface type system specifies"
+       )
+{
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  int type;
+
+  ifp = (struct interface *) vty->index;
+  assert (ifp);
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL) {
+    return CMD_SUCCESS;
+  }
+
+  type = ospf6_default_iftype (ifp);
+  if (oi->type == type)
+    {
+      return CMD_SUCCESS;
+    }
+  oi->type = type;
+
+  /* Reset the interface */
+  thread_add_event (master, interface_down, oi, 0);
+  thread_add_event (master, interface_up, oi, 0);
+
+  return CMD_SUCCESS;
+}
+
+static int
+config_write_ospf6_interface (struct vty *vty)
+{
+  struct listnode *i;
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp))
+    {
+      oi = (struct ospf6_interface *) ifp->info;
+      if (oi == NULL)
+        continue;
+
+      vty_out (vty, "interface %s%s",
+               oi->interface->name, VNL);
+
+      if (ifp->desc)
+        vty_out (vty, " description %s%s", ifp->desc, VNL);
+      if (ifp->mtu6 != oi->ifmtu)
+        vty_out (vty, " ipv6 ospf6 ifmtu %d%s", oi->ifmtu, VNL);
+
+      if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST))
+        vty_out (vty, " ipv6 ospf6 cost %d%s",
+                 oi->cost, VNL);
+
+      if (oi->hello_interval != OSPF6_INTERFACE_HELLO_INTERVAL)
+        vty_out (vty, " ipv6 ospf6 hello-interval %d%s",
+                 oi->hello_interval, VNL);
+
+      if (oi->dead_interval != OSPF6_INTERFACE_DEAD_INTERVAL)
+        vty_out (vty, " ipv6 ospf6 dead-interval %d%s",
+                 oi->dead_interval, VNL);
+
+      if (oi->rxmt_interval != OSPF6_INTERFACE_RXMT_INTERVAL)
+        vty_out (vty, " ipv6 ospf6 retransmit-interval %d%s",
+                 oi->rxmt_interval, VNL);
+
+      if (oi->priority != OSPF6_INTERFACE_PRIORITY)
+        vty_out (vty, " ipv6 ospf6 priority %d%s",
+                 oi->priority, VNL);
+
+      if (oi->transdelay != OSPF6_INTERFACE_TRANSDELAY)
+        vty_out (vty, " ipv6 ospf6 transmit-delay %d%s",
+                 oi->transdelay, VNL);
+
+      if (oi->instance_id != OSPF6_INTERFACE_INSTANCE_ID)
+        vty_out (vty, " ipv6 ospf6 instance-id %d%s",
+                 oi->instance_id, VNL);
+
+      if (oi->plist_name)
+        vty_out (vty, " ipv6 ospf6 advertise prefix-list %s%s",
+                 oi->plist_name, VNL);
+
+      if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE))
+        vty_out (vty, " ipv6 ospf6 passive%s", VNL);
+
+      if (oi->mtu_ignore)
+        vty_out (vty, " ipv6 ospf6 mtu-ignore%s", VNL);
+
+      if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+        vty_out (vty, " ipv6 ospf6 network point-to-point%s", VNL);
+      else if (oi->type == OSPF_IFTYPE_BROADCAST)
+       vty_out (vty, " ipv6 ospf6 network broadcast%s", VNL);
+
+      vty_out (vty, "!%s", VNL);
+    }
+  return 0;
+}
+
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1 /* VTYSH */
+};
+
+void
+ospf6_interface_init (void)
+{
+  /* Install interface node. */
+  install_node (&interface_node, config_write_ospf6_interface);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_match_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_match_cmd);
+
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_cost_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_ifmtu_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_priority_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ospf6_instance_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ospf6_passive_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_passive_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ospf6_mtu_ignore_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_mtu_ignore_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_advertise_prefix_list_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ospf6_network_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ospf6_network_cmd);
+
+  /* reference bandwidth commands */
+  install_element (OSPF6_NODE, &auto_cost_reference_bandwidth_cmd);
+  install_element (OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd);
+}
+
+/* Clear the specified interface structure */
+static void
+ospf6_interface_clear (struct vty *vty, struct interface *ifp)
+{
+  struct ospf6_interface *oi;
+
+  if (!if_is_operative (ifp))
+    return;
+
+  if (ifp->info == NULL)
+    return;
+
+  oi = (struct ospf6_interface *) ifp->info;
+
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    zlog_debug ("Interface %s: clear by reset", ifp->name);
+
+  /* Reset the interface */
+  thread_add_event (master, interface_down, oi, 0);
+  thread_add_event (master, interface_up, oi, 0);
+}
+
+/* Clear interface */
+DEFUN (clear_ipv6_ospf6_interface,
+       clear_ipv6_ospf6_interface_cmd,
+       "clear ipv6 ospf6 interface [IFNAME]",
+       CLEAR_STR
+       IP6_STR
+       OSPF6_STR
+       INTERFACE_STR
+       IFNAME_STR
+       )
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  if (argc == 0) /* Clear all the ospfv3 interfaces. */
+    {
+      for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+        ospf6_interface_clear (vty, ifp);
+    }
+  else /* Interface name is specified. */
+    {
+      if ((ifp = if_lookup_by_name (argv[0])) == NULL)
+        {
+          vty_out (vty, "No such Interface: %s%s", argv[0], VNL);
+          return CMD_WARNING;
+        }
+      ospf6_interface_clear (vty, ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+void
+install_element_ospf6_clear_interface (void)
+{
+  install_element (ENABLE_NODE, &clear_ipv6_ospf6_interface_cmd);
+}
+
+DEFUN (debug_ospf6_interface,
+       debug_ospf6_interface_cmd,
+       "debug ospf6 interface",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Interface\n"
+      )
+{
+  OSPF6_DEBUG_INTERFACE_ON ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_interface,
+       no_debug_ospf6_interface_cmd,
+       "no debug ospf6 interface",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Interface\n"
+      )
+{
+  OSPF6_DEBUG_INTERFACE_OFF ();
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_interface (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_INTERFACE)
+    vty_out (vty, "debug ospf6 interface%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_interface (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_interface_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_interface_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_interface_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_interface_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
new file mode 100644 (file)
index 0000000..2fbb83c
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_INTERFACE_H
+#define OSPF6_INTERFACE_H
+
+#include "if.h"
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_interface;
+#define OSPF6_DEBUG_INTERFACE_ON() \
+  (conf_debug_ospf6_interface = 1)
+#define OSPF6_DEBUG_INTERFACE_OFF() \
+  (conf_debug_ospf6_interface = 0)
+#define IS_OSPF6_DEBUG_INTERFACE \
+  (conf_debug_ospf6_interface)
+
+/* Interface structure */
+struct ospf6_interface
+{
+  /* IF info from zebra */
+  struct interface *interface;
+
+  /* back pointer */
+  struct ospf6_area *area;
+
+  /* list of ospf6 neighbor */
+  struct list *neighbor_list;
+
+  /* linklocal address of this I/F */
+  struct in6_addr *linklocal_addr;
+
+  /* Interface ID; use interface->ifindex */
+
+  /* ospf6 instance id */
+  u_char instance_id;
+
+  /* I/F transmission delay */
+  u_int32_t transdelay;
+
+  /* Network Type */
+  u_char type;
+
+  /* Router Priority */
+  u_char priority;
+
+  /* Time Interval */
+  u_int16_t hello_interval;
+  u_int16_t dead_interval;
+  u_int32_t rxmt_interval;
+
+  u_int32_t state_change;
+
+  /* Cost */
+  u_int32_t cost;
+
+  /* I/F MTU */
+  u_int32_t ifmtu;
+
+  /* Interface State */
+  u_char state;
+
+  /* Interface socket setting trial counter, resets on success */
+  u_char sso_try_cnt;
+
+  /* OSPF6 Interface flag */
+  char flag;
+
+  /* MTU mismatch check */
+  u_char mtu_ignore;
+
+  /* Decision of DR Election */
+  u_int32_t drouter;
+  u_int32_t bdrouter;
+  u_int32_t prev_drouter;
+  u_int32_t prev_bdrouter;
+
+  /* Linklocal LSA Database: includes Link-LSA */
+  struct ospf6_lsdb *lsdb;
+  struct ospf6_lsdb *lsdb_self;
+
+  struct ospf6_lsdb *lsupdate_list;
+  struct ospf6_lsdb *lsack_list;
+
+  /* Ongoing Tasks */
+  struct thread *thread_send_hello;
+  struct thread *thread_send_lsupdate;
+  struct thread *thread_send_lsack;
+
+  struct thread *thread_network_lsa;
+  struct thread *thread_link_lsa;
+  struct thread *thread_intra_prefix_lsa;
+
+  struct ospf6_route_table *route_connected;
+
+  /* prefix-list name to filter connected prefix */
+  char *plist_name;
+};
+
+/* interface state */
+#define OSPF6_INTERFACE_NONE             0
+#define OSPF6_INTERFACE_DOWN             1
+#define OSPF6_INTERFACE_LOOPBACK         2
+#define OSPF6_INTERFACE_WAITING          3
+#define OSPF6_INTERFACE_POINTTOPOINT     4
+#define OSPF6_INTERFACE_DROTHER          5
+#define OSPF6_INTERFACE_BDR              6
+#define OSPF6_INTERFACE_DR               7
+#define OSPF6_INTERFACE_MAX              8
+
+extern const char *ospf6_interface_state_str[];
+
+/* flags */
+#define OSPF6_INTERFACE_DISABLE      0x01
+#define OSPF6_INTERFACE_PASSIVE      0x02
+#define OSPF6_INTERFACE_NOAUTOCOST   0x04
+
+/* default values */
+#define OSPF6_INTERFACE_HELLO_INTERVAL 10
+#define OSPF6_INTERFACE_DEAD_INTERVAL  40
+#define OSPF6_INTERFACE_RXMT_INTERVAL  5
+#define OSPF6_INTERFACE_COST           1
+#define OSPF6_INTERFACE_PRIORITY       1
+#define OSPF6_INTERFACE_TRANSDELAY     1
+#define OSPF6_INTERFACE_INSTANCE_ID    0
+#define OSPF6_INTERFACE_BANDWIDTH      10000   /* Kbps */
+#define OSPF6_REFERENCE_BANDWIDTH      100000  /* Kbps */
+#define OSPF6_INTERFACE_SSO_RETRY_INT  1
+#define OSPF6_INTERFACE_SSO_RETRY_MAX  5
+
+
+/* Function Prototypes */
+
+extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex (int);
+extern struct ospf6_interface *ospf6_interface_create (struct interface *);
+extern void ospf6_interface_delete (struct ospf6_interface *);
+
+extern void ospf6_interface_enable (struct ospf6_interface *);
+extern void ospf6_interface_disable (struct ospf6_interface *);
+
+extern void ospf6_interface_if_add (struct interface *);
+extern void ospf6_interface_state_update (struct interface *);
+extern void ospf6_interface_connected_route_update (struct interface *);
+
+/* interface event */
+extern int interface_up (struct thread *);
+extern int interface_down (struct thread *);
+extern int wait_timer (struct thread *);
+extern int backup_seen (struct thread *);
+extern int neighbor_change (struct thread *);
+
+extern void ospf6_interface_init (void);
+
+extern void install_element_ospf6_clear_interface (void);
+
+extern int config_write_ospf6_debug_interface (struct vty *vty);
+extern void install_element_ospf6_debug_interface (void);
+
+#endif /* OSPF6_INTERFACE_H */
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
new file mode 100644 (file)
index 0000000..591dab3
--- /dev/null
@@ -0,0 +1,1789 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+#include "if.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "command.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_message.h"
+#include "ospf6_route.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_intra.h"
+#include "ospf6_asbr.h"
+#include "ospf6_abr.h"
+#include "ospf6_flood.h"
+#include "ospf6d.h"
+#include "ospf6_spf.h"
+
+unsigned char conf_debug_ospf6_brouter = 0;
+u_int32_t conf_debug_ospf6_brouter_specific_router_id;
+u_int32_t conf_debug_ospf6_brouter_specific_area_id;
+
+/******************************/
+/* RFC2740 3.4.3.1 Router-LSA */
+/******************************/
+
+static char *
+ospf6_router_lsa_get_nbr_id (struct ospf6_lsa *lsa, char *buf, int buflen,
+                            int pos)
+{
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsdesc *lsdesc;
+  char *start, *end;
+  char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN];
+
+  if (lsa)
+    {
+      router_lsa = (struct ospf6_router_lsa *)
+       ((char *) lsa->header + sizeof (struct ospf6_lsa_header));
+      start = (char *) router_lsa + sizeof (struct ospf6_router_lsa);
+      end = (char *) lsa->header + ntohs (lsa->header->length);
+
+      lsdesc = (struct ospf6_router_lsdesc *)
+       (start + pos*(sizeof (struct ospf6_router_lsdesc)));
+      if ((char *)lsdesc < end)
+       {
+         if (buf && (buflen > INET_ADDRSTRLEN*2))
+           {
+             inet_ntop (AF_INET, &lsdesc->neighbor_interface_id,
+                        buf1, sizeof(buf1));
+             inet_ntop (AF_INET, &lsdesc->neighbor_router_id,
+                        buf2, sizeof(buf2));
+             sprintf (buf, "%s/%s", buf2, buf1);
+           }
+       }
+      else
+       return NULL;
+    }
+
+  return buf;
+}
+
+static int
+ospf6_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  char buf[32], name[32], bits[16], options[32];
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsdesc *lsdesc;
+
+  router_lsa = (struct ospf6_router_lsa *)
+    ((char *) lsa->header + sizeof (struct ospf6_lsa_header));
+
+  ospf6_capability_printbuf (router_lsa->bits, bits, sizeof (bits));
+  ospf6_options_printbuf (router_lsa->options, options, sizeof (options));
+  vty_out (vty, "    Bits: %s Options: %s%s", bits, options, VNL);
+
+  start = (char *) router_lsa + sizeof (struct ospf6_router_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+  for (current = start; current + sizeof (struct ospf6_router_lsdesc) <= end;
+       current += sizeof (struct ospf6_router_lsdesc))
+    {
+      lsdesc = (struct ospf6_router_lsdesc *) current;
+
+      if (lsdesc->type == OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+        snprintf (name, sizeof (name), "Point-To-Point");
+      else if (lsdesc->type == OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK)
+        snprintf (name, sizeof (name), "Transit-Network");
+      else if (lsdesc->type == OSPF6_ROUTER_LSDESC_STUB_NETWORK)
+        snprintf (name, sizeof (name), "Stub-Network");
+      else if (lsdesc->type == OSPF6_ROUTER_LSDESC_VIRTUAL_LINK)
+        snprintf (name, sizeof (name), "Virtual-Link");
+      else
+        snprintf (name, sizeof (name), "Unknown (%#x)", lsdesc->type);
+
+      vty_out (vty, "    Type: %s Metric: %d%s",
+               name, ntohs (lsdesc->metric), VNL);
+      vty_out (vty, "    Interface ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->interface_id,
+                          buf, sizeof (buf)), VNL);
+      vty_out (vty, "    Neighbor Interface ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->neighbor_interface_id,
+                          buf, sizeof (buf)), VNL);
+      vty_out (vty, "    Neighbor Router ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->neighbor_router_id,
+                          buf, sizeof (buf)), VNL);
+    }
+  return 0;
+}
+
+int
+ospf6_router_is_stub_router (struct ospf6_lsa *lsa)
+{
+  struct ospf6_router_lsa *rtr_lsa;
+
+  if (lsa != NULL && OSPF6_LSA_IS_TYPE (ROUTER, lsa))
+    {
+      rtr_lsa = (struct ospf6_router_lsa *)
+       ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+      if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_R))
+       {
+         return (OSPF6_IS_STUB_ROUTER);
+       }
+      else if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_V6))
+       {
+         return (OSPF6_IS_STUB_ROUTER_V6);
+       }
+    }
+
+  return (OSPF6_NOT_STUB_ROUTER);
+}
+
+int
+ospf6_router_lsa_originate (struct thread *thread)
+{
+  struct ospf6_area *oa;
+
+  char buffer [OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *lsa;
+
+  u_int32_t link_state_id = 0;
+  struct listnode *node, *nnode;
+  struct listnode *j;
+  struct ospf6_interface *oi;
+  struct ospf6_neighbor *on, *drouter = NULL;
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsdesc *lsdesc;
+  u_int16_t type;
+  u_int32_t router;
+  int count;
+
+  oa = (struct ospf6_area *) THREAD_ARG (thread);
+  oa->thread_router_lsa = NULL;
+
+  if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER))
+    zlog_debug ("Originate Router-LSA for Area %s", oa->name);
+
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  router_lsa = (struct ospf6_router_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_V6);
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_E);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_MC);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_N);
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_R);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_DC);
+
+  if (ospf6_is_router_abr (ospf6))
+    SET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_B);
+  else
+    UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_B);
+  if (ospf6_asbr_is_asbr (ospf6))
+    SET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_E);
+  else
+    UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_E);
+  UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_V);
+  UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_W);
+
+  /* describe links for each interfaces */
+  lsdesc = (struct ospf6_router_lsdesc *)
+    ((caddr_t) router_lsa + sizeof (struct ospf6_router_lsa));
+
+  for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi))
+    {
+      /* Interfaces in state Down or Loopback are not described */
+      if (oi->state == OSPF6_INTERFACE_DOWN ||
+          oi->state == OSPF6_INTERFACE_LOOPBACK)
+        continue;
+
+      /* Nor are interfaces without any full adjacencies described */
+      count = 0;
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on))
+        if (on->state == OSPF6_NEIGHBOR_FULL)
+          count++;
+      
+      if (count == 0)
+        continue;
+
+      /* Multiple Router-LSA instance according to size limit setting */
+      if ( (oa->router_lsa_size_limit != 0)
+          && ((size_t)((char *)lsdesc - buffer)
+                 + sizeof (struct ospf6_router_lsdesc)
+               > oa->router_lsa_size_limit))
+        {
+          if ((caddr_t) lsdesc == (caddr_t) router_lsa +
+                                  sizeof (struct ospf6_router_lsa))
+            {
+              if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER))
+                zlog_debug ("Size limit setting for Router-LSA too short");
+              return 0;
+            }
+
+          link_state_id ++;
+        }
+
+      /* Point-to-Point interfaces */
+      if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+        {
+          for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on))
+            {
+              if (on->state != OSPF6_NEIGHBOR_FULL)
+                continue;
+
+              lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT;
+              lsdesc->metric = htons (oi->cost);
+              lsdesc->interface_id = htonl (oi->interface->ifindex);
+              lsdesc->neighbor_interface_id = htonl (on->ifindex);
+              lsdesc->neighbor_router_id = on->router_id;
+
+              lsdesc++;
+            }
+        }
+
+      /* Broadcast and NBMA interfaces */
+      else if (oi->type == OSPF_IFTYPE_BROADCAST)
+        {
+          /* If this router is not DR,
+             and If this router not fully adjacent with DR,
+             this interface is not transit yet: ignore. */
+          if (oi->state != OSPF6_INTERFACE_DR)
+            {
+              drouter = ospf6_neighbor_lookup (oi->drouter, oi);
+              if (drouter == NULL || drouter->state != OSPF6_NEIGHBOR_FULL)
+                continue;
+            }
+
+          lsdesc->type = OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK;
+          lsdesc->metric = htons (oi->cost);
+          lsdesc->interface_id = htonl (oi->interface->ifindex);
+          if (oi->state != OSPF6_INTERFACE_DR)
+            {
+              lsdesc->neighbor_interface_id = htonl (drouter->ifindex);
+              lsdesc->neighbor_router_id = drouter->router_id;
+            }
+          else
+            {
+              lsdesc->neighbor_interface_id = htonl (oi->interface->ifindex);
+              lsdesc->neighbor_router_id = oi->area->ospf6->router_id;
+            }
+
+          lsdesc++;
+        }
+      else
+       {
+         assert (0);           /* Unknown interface type */
+       }
+
+      /* Virtual links */
+        /* xxx */
+      /* Point-to-Multipoint interfaces */
+        /* xxx */
+    }
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_ROUTER);
+  lsa_header->id = htonl (link_state_id);
+  lsa_header->adv_router = oa->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                        lsa_header->adv_router, oa->lsdb);
+  lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_area (lsa, oa);
+
+  link_state_id ++;
+
+  /* Do premature-aging of rest, undesired Router-LSAs */
+  type = ntohs (OSPF6_LSTYPE_ROUTER);
+  router = oa->ospf6->router_id;
+  for (lsa = ospf6_lsdb_type_router_head (type, router, oa->lsdb); lsa;
+       lsa = ospf6_lsdb_type_router_next (type, router, lsa))
+    {
+      if (ntohl (lsa->header->id) < link_state_id)
+        continue;
+      ospf6_lsa_purge (lsa);
+    }
+
+  return 0;
+}
+
+/*******************************/
+/* RFC2740 3.4.3.2 Network-LSA */
+/*******************************/
+
+static char *
+ospf6_network_lsa_get_ar_id (struct ospf6_lsa *lsa, char *buf, int buflen,
+                            int pos)
+{
+  char *start, *end, *current;
+  struct ospf6_network_lsa *network_lsa;
+  struct ospf6_network_lsdesc *lsdesc;
+
+  if (lsa)
+    {
+      network_lsa = (struct ospf6_network_lsa *)
+       ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+      start = (char *) network_lsa + sizeof (struct ospf6_network_lsa);
+      end = (char *) lsa->header + ntohs (lsa->header->length);
+      current = start + pos*(sizeof (struct ospf6_network_lsdesc));
+
+      if ((current + sizeof(struct ospf6_network_lsdesc)) <= end)
+       {
+         lsdesc = (struct ospf6_network_lsdesc *)current;
+         if (buf)
+           inet_ntop (AF_INET, &lsdesc->router_id, buf, buflen);
+       }
+      else
+       return NULL;
+    }
+
+  return (buf);
+}
+
+static int
+ospf6_network_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  struct ospf6_network_lsa *network_lsa;
+  struct ospf6_network_lsdesc *lsdesc;
+  char buf[128], options[32];
+
+  network_lsa = (struct ospf6_network_lsa *)
+    ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+  ospf6_options_printbuf (network_lsa->options, options, sizeof (options));
+  vty_out (vty, "     Options: %s%s", options, VNL);
+
+  start = (char *) network_lsa + sizeof (struct ospf6_network_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+  for (current = start; current + sizeof (struct ospf6_network_lsdesc) <= end;
+       current += sizeof (struct ospf6_network_lsdesc))
+    {
+      lsdesc = (struct ospf6_network_lsdesc *) current;
+      inet_ntop (AF_INET, &lsdesc->router_id, buf, sizeof (buf));
+      vty_out (vty, "     Attached Router: %s%s", buf, VNL);
+    }
+  return 0;
+}
+
+int
+ospf6_network_lsa_originate (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  char buffer [OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+
+  int count;
+  struct ospf6_lsa *old, *lsa;
+  struct ospf6_network_lsa *network_lsa;
+  struct ospf6_network_lsdesc *lsdesc;
+  struct ospf6_neighbor *on;
+  struct ospf6_link_lsa *link_lsa;
+  struct listnode *i;
+  u_int16_t type;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_network_lsa = NULL;
+
+  /* The interface must be enabled until here. A Network-LSA of a
+     disabled interface (but was once enabled) should be flushed
+     by ospf6_lsa_refresh (), and does not come here. */
+  assert (oi->area);
+
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_NETWORK),
+                           htonl (oi->interface->ifindex),
+                           oi->area->ospf6->router_id, oi->area->lsdb);
+
+  /* Do not originate Network-LSA if not DR */
+  if (oi->state != OSPF6_INTERFACE_DR)
+    {
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  if (IS_OSPF6_DEBUG_ORIGINATE (NETWORK))
+    zlog_debug ("Originate Network-LSA for Interface %s", oi->interface->name);
+
+  /* If none of neighbor is adjacent to us */
+  count = 0;
+  
+  for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on))
+    if (on->state == OSPF6_NEIGHBOR_FULL)
+      count++;
+  
+  if (count == 0)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (NETWORK))
+        zlog_debug ("Interface stub, ignore");
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  network_lsa = (struct ospf6_network_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+
+  /* Collect the interface's Link-LSAs to describe
+     network's optional capabilities */
+  type = htons (OSPF6_LSTYPE_LINK);
+  for (lsa = ospf6_lsdb_type_head (type, oi->lsdb); lsa;
+       lsa = ospf6_lsdb_type_next (type, lsa))
+    {
+      link_lsa = (struct ospf6_link_lsa *)
+        ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+      network_lsa->options[0] |= link_lsa->options[0];
+      network_lsa->options[1] |= link_lsa->options[1];
+      network_lsa->options[2] |= link_lsa->options[2];
+    }
+
+  lsdesc = (struct ospf6_network_lsdesc *)
+    ((caddr_t) network_lsa + sizeof (struct ospf6_network_lsa));
+
+  /* set Link Description to the router itself */
+  lsdesc->router_id = oi->area->ospf6->router_id;
+  lsdesc++;
+
+  /* Walk through the neighbors */
+  for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on))
+    {
+      if (on->state != OSPF6_NEIGHBOR_FULL)
+        continue;
+
+      /* set this neighbor's Router-ID to LSA */
+      lsdesc->router_id = on->router_id;
+      lsdesc++;
+    }
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_NETWORK);
+  lsa_header->id = htonl (oi->interface->ifindex);
+  lsa_header->adv_router = oi->area->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, oi->area->lsdb);
+  lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_area (lsa, oi->area);
+
+  return 0;
+}
+
+
+/****************************/
+/* RFC2740 3.4.3.6 Link-LSA */
+/****************************/
+
+static char *
+ospf6_link_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, int buflen,
+                              int pos)
+{
+  char *start, *end, *current;
+  struct ospf6_link_lsa *link_lsa;
+  struct in6_addr in6;
+  struct ospf6_prefix *prefix;
+  int cnt = 0, prefixnum;
+
+  if (lsa)
+    {
+      link_lsa = (struct ospf6_link_lsa *)
+       ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+      if (pos == 0) {
+       inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, buflen);
+       return (buf);
+      }
+
+      prefixnum = ntohl (link_lsa->prefix_num);
+      if (pos > prefixnum)
+       return (NULL);
+
+      start = (char *) link_lsa + sizeof (struct ospf6_link_lsa);
+      end = (char *) lsa->header + ntohs (lsa->header->length);
+      current = start;
+
+      do
+       {
+         prefix = (struct ospf6_prefix *) current;
+         if (prefix->prefix_length == 0 ||
+             current + OSPF6_PREFIX_SIZE (prefix) > end)
+           {
+             return (NULL);
+           }
+
+         if (cnt < pos)
+           {
+             current = start + pos*OSPF6_PREFIX_SIZE(prefix);
+             cnt++;
+           }
+         else
+           {
+             memset (&in6, 0, sizeof (in6));
+             memcpy (&in6, OSPF6_PREFIX_BODY (prefix),
+                     OSPF6_PREFIX_SPACE (prefix->prefix_length));
+             inet_ntop (AF_INET6, &in6, buf, buflen);
+             return (buf);
+           }
+       } while (current <= end);
+    }
+  return (NULL);
+}
+
+static int
+ospf6_link_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  struct ospf6_link_lsa *link_lsa;
+  int prefixnum;
+  char buf[128], options[32];
+  struct ospf6_prefix *prefix;
+  const char *p, *mc, *la, *nu;
+  struct in6_addr in6;
+
+  link_lsa = (struct ospf6_link_lsa *)
+    ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+  ospf6_options_printbuf (link_lsa->options, options, sizeof (options));
+  inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf));
+  prefixnum = ntohl (link_lsa->prefix_num);
+
+  vty_out (vty, "     Priority: %d Options: %s%s",
+           link_lsa->priority, options, VNL);
+  vty_out (vty, "     LinkLocal Address: %s%s", buf, VNL);
+  vty_out (vty, "     Number of Prefix: %d%s", prefixnum, VNL);
+
+  start = (char *) link_lsa + sizeof (struct ospf6_link_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length); 
+  for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix))
+    {
+      prefix = (struct ospf6_prefix *) current;
+      if (prefix->prefix_length == 0 ||
+          current + OSPF6_PREFIX_SIZE (prefix) > end)
+        break;
+
+      p = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_P) ?
+           "P" : "--");
+      mc = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_MC) ?
+           "MC" : "--");
+      la = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_LA) ?
+           "LA" : "--");
+      nu = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ?
+           "NU" : "--");
+      vty_out (vty, "     Prefix Options: %s|%s|%s|%s%s",
+               p, mc, la, nu, VNL);
+
+      memset (&in6, 0, sizeof (in6));
+      memcpy (&in6, OSPF6_PREFIX_BODY (prefix),
+              OSPF6_PREFIX_SPACE (prefix->prefix_length));
+      inet_ntop (AF_INET6, &in6, buf, sizeof (buf));
+      vty_out (vty, "     Prefix: %s/%d%s",
+               buf, prefix->prefix_length, VNL);
+    }
+
+  return 0;
+}
+
+int
+ospf6_link_lsa_originate (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  char buffer[OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *old, *lsa;
+
+  struct ospf6_link_lsa *link_lsa;
+  struct ospf6_route *route;
+  struct ospf6_prefix *op;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_link_lsa = NULL;
+
+  assert (oi->area);
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_LINK),
+                           htonl (oi->interface->ifindex),
+                           oi->area->ospf6->router_id, oi->lsdb);
+
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    {
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  if (IS_OSPF6_DEBUG_ORIGINATE (LINK))
+    zlog_debug ("Originate Link-LSA for Interface %s", oi->interface->name);
+
+  /* can't make Link-LSA if linklocal address not set */
+  if (oi->linklocal_addr == NULL)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (LINK))
+        zlog_debug ("No Linklocal address on %s, defer originating",
+                   oi->interface->name);
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  link_lsa = (struct ospf6_link_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+
+  /* Fill Link-LSA */
+  link_lsa->priority = oi->priority;
+  memcpy (link_lsa->options, oi->area->options, 3);
+  memcpy (&link_lsa->linklocal_addr, oi->linklocal_addr,
+          sizeof (struct in6_addr));
+  link_lsa->prefix_num = htonl (oi->route_connected->count);
+
+  op = (struct ospf6_prefix *)
+    ((caddr_t) link_lsa + sizeof (struct ospf6_link_lsa));
+
+  /* connected prefix to advertise */
+  for (route = ospf6_route_head (oi->route_connected); route;
+       route = ospf6_route_next (route))
+    {
+      op->prefix_length = route->prefix.prefixlen;
+      op->prefix_options = route->path.prefix_options;
+      op->prefix_metric = htons (0);
+      memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6,
+              OSPF6_PREFIX_SPACE (op->prefix_length));
+      op = OSPF6_PREFIX_NEXT (op);
+    }
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_LINK);
+  lsa_header->id = htonl (oi->interface->ifindex);
+  lsa_header->adv_router = oi->area->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, oi->lsdb);
+  lsa_header->length = htons ((caddr_t) op - (caddr_t) buffer);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_interface (lsa, oi);
+
+  return 0;
+}
+
+
+/*****************************************/
+/* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */
+/*****************************************/
+static char *
+ospf6_intra_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf,
+                                      int buflen, int pos)
+{
+  char *start, *end, *current;
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct in6_addr in6;
+  int prefixnum, cnt = 0;
+  struct ospf6_prefix *prefix;
+
+  if (lsa)
+    {
+      intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+       ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+      prefixnum = ntohs (intra_prefix_lsa->prefix_num);
+      if (pos > prefixnum)
+       return (NULL);
+
+      start = (char *) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa);
+      end = (char *) lsa->header + ntohs (lsa->header->length);
+      current = start;
+
+      do
+       {
+         prefix = (struct ospf6_prefix *) current;
+         if (prefix->prefix_length == 0 ||
+             current + OSPF6_PREFIX_SIZE (prefix) > end)
+           {
+             return NULL;
+           }
+
+         if (cnt < pos)
+           {
+             current = start + pos*OSPF6_PREFIX_SIZE(prefix);
+             cnt++;
+           }
+         else
+           {
+             memset (&in6, 0, sizeof (in6));
+             memcpy (&in6, OSPF6_PREFIX_BODY (prefix),
+                     OSPF6_PREFIX_SPACE (prefix->prefix_length));
+             inet_ntop (AF_INET6, &in6, buf, buflen);
+             sprintf(&buf[strlen(buf)], "/%d", prefix->prefix_length);
+             return (buf);
+           }
+       } while (current <= end);
+    }
+  return (buf);
+}
+
+static int
+ospf6_intra_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  int prefixnum;
+  char buf[128];
+  struct ospf6_prefix *prefix;
+  char id[16], adv_router[16];
+  const char *p, *mc, *la, *nu;
+  struct in6_addr in6;
+
+  intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+    ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+  prefixnum = ntohs (intra_prefix_lsa->prefix_num);
+
+  vty_out (vty, "     Number of Prefix: %d%s", prefixnum, VNL);
+
+  inet_ntop (AF_INET, &intra_prefix_lsa->ref_id, id, sizeof (id));
+  inet_ntop (AF_INET, &intra_prefix_lsa->ref_adv_router,
+             adv_router, sizeof (adv_router));
+  vty_out (vty, "     Reference: %s Id: %s Adv: %s%s",
+           ospf6_lstype_name (intra_prefix_lsa->ref_type), id, adv_router,
+           VNL);
+
+  start = (char *) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length); 
+  for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix))
+    {
+      prefix = (struct ospf6_prefix *) current;
+      if (prefix->prefix_length == 0 ||
+          current + OSPF6_PREFIX_SIZE (prefix) > end)
+        break;
+
+      p = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_P) ?
+           "P" : "--");
+      mc = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_MC) ?
+           "MC" : "--");
+      la = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_LA) ?
+           "LA" : "--");
+      nu = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ?
+           "NU" : "--");
+      vty_out (vty, "     Prefix Options: %s|%s|%s|%s%s",
+               p, mc, la, nu, VNL);
+
+      memset (&in6, 0, sizeof (in6));
+      memcpy (&in6, OSPF6_PREFIX_BODY (prefix),
+              OSPF6_PREFIX_SPACE (prefix->prefix_length));
+      inet_ntop (AF_INET6, &in6, buf, sizeof (buf));
+      vty_out (vty, "     Prefix: %s/%d%s",
+               buf, prefix->prefix_length, VNL);
+    }
+
+  return 0;
+}
+
+int
+ospf6_intra_prefix_lsa_originate_stub (struct thread *thread)
+{
+  struct ospf6_area *oa;
+
+  char buffer[OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *old, *lsa;
+
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct ospf6_interface *oi;
+  struct ospf6_neighbor *on;
+  struct ospf6_route *route;
+  struct ospf6_prefix *op;
+  struct listnode *i, *j;
+  int full_count = 0;
+  unsigned short prefix_num = 0;
+  char buf[BUFSIZ];
+  struct ospf6_route_table *route_advertise;
+
+  oa = (struct ospf6_area *) THREAD_ARG (thread);
+  oa->thread_intra_prefix_lsa = NULL;
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_INTRA_PREFIX),
+                           htonl (0), oa->ospf6->router_id, oa->lsdb);
+
+  if (! IS_AREA_ENABLED (oa))
+    {
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+    zlog_debug ("Originate Intra-Area-Prefix-LSA for area %s's stub prefix",
+               oa->name);
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+
+  /* Fill Intra-Area-Prefix-LSA */
+  intra_prefix_lsa->ref_type = htons (OSPF6_LSTYPE_ROUTER);
+  intra_prefix_lsa->ref_id = htonl (0);
+  intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id;
+
+  route_advertise = ospf6_route_table_create (0, 0);
+
+  for (ALL_LIST_ELEMENTS_RO (oa->if_list, i, oi))
+    {
+      if (oi->state == OSPF6_INTERFACE_DOWN)
+        {
+          if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+            zlog_debug ("  Interface %s is down, ignore", oi->interface->name);
+          continue;
+        }
+
+      full_count = 0;
+
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on))
+        if (on->state == OSPF6_NEIGHBOR_FULL)
+          full_count++;
+
+      if (oi->state != OSPF6_INTERFACE_LOOPBACK &&
+          oi->state != OSPF6_INTERFACE_POINTTOPOINT &&
+          full_count != 0)
+        {
+          if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+            zlog_debug ("  Interface %s is not stub, ignore",
+                       oi->interface->name);
+          continue;
+        }
+
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("  Interface %s:", oi->interface->name);
+
+      /* connected prefix to advertise */
+      for (route = ospf6_route_head (oi->route_connected); route;
+           route = ospf6_route_best_next (route))
+        {
+          if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+            {
+              prefix2str (&route->prefix, buf, sizeof (buf));
+              zlog_debug ("    include %s", buf);
+            }
+          ospf6_route_add (ospf6_route_copy (route), route_advertise);
+        }
+    }
+
+  if (route_advertise->count == 0)
+    {
+      if (old)
+        ospf6_lsa_purge (old);
+      ospf6_route_table_delete (route_advertise);
+      return 0;
+    }
+
+  /* put prefixes to advertise */
+  prefix_num = 0;
+  op = (struct ospf6_prefix *)
+    ((caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa));
+  for (route = ospf6_route_head (route_advertise); route;
+       route = ospf6_route_best_next (route))
+    {
+      op->prefix_length = route->prefix.prefixlen;
+      op->prefix_options = route->path.prefix_options;
+      op->prefix_metric = htons (route->path.cost);
+      memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6,
+              OSPF6_PREFIX_SPACE (op->prefix_length));
+      op = OSPF6_PREFIX_NEXT (op);
+      prefix_num++;
+    }
+
+  ospf6_route_table_delete (route_advertise);
+
+  if (prefix_num == 0)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("Quit to Advertise Intra-Prefix: no route to advertise");
+      return 0;
+    }
+
+  intra_prefix_lsa->prefix_num = htons (prefix_num);
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_INTRA_PREFIX);
+  lsa_header->id = htonl (0);
+  lsa_header->adv_router = oa->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, oa->lsdb);
+  lsa_header->length = htons ((caddr_t) op - (caddr_t) lsa_header);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_area (lsa, oa);
+
+  return 0;
+}
+
+
+int
+ospf6_intra_prefix_lsa_originate_transit (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+
+  char buffer[OSPF6_MAX_LSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *old, *lsa;
+
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct ospf6_neighbor *on;
+  struct ospf6_route *route;
+  struct ospf6_prefix *op;
+  struct listnode *i;
+  int full_count = 0;
+  unsigned short prefix_num = 0;
+  struct ospf6_route_table *route_advertise;
+  struct ospf6_link_lsa *link_lsa;
+  char *start, *end, *current;
+  u_int16_t type;
+  char buf[BUFSIZ];
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_intra_prefix_lsa = NULL;
+
+  assert (oi->area);
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_INTRA_PREFIX),
+                           htonl (oi->interface->ifindex),
+                           oi->area->ospf6->router_id, oi->area->lsdb);
+
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE))
+    {
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+    zlog_debug ("Originate Intra-Area-Prefix-LSA for interface %s's prefix",
+               oi->interface->name);
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+  intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+    ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
+
+  /* Fill Intra-Area-Prefix-LSA */
+  intra_prefix_lsa->ref_type = htons (OSPF6_LSTYPE_NETWORK);
+  intra_prefix_lsa->ref_id = htonl (oi->interface->ifindex);
+  intra_prefix_lsa->ref_adv_router = oi->area->ospf6->router_id;
+
+  if (oi->state != OSPF6_INTERFACE_DR)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("  Interface is not DR");
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  full_count = 0;
+  for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on))
+    if (on->state == OSPF6_NEIGHBOR_FULL)
+      full_count++;
+  
+  if (full_count == 0)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("  Interface is stub");
+      if (old)
+        ospf6_lsa_purge (old);
+      return 0;
+    }
+
+  /* connected prefix to advertise */
+  route_advertise = ospf6_route_table_create (0, 0);
+
+  type = ntohs (OSPF6_LSTYPE_LINK);
+  for (lsa = ospf6_lsdb_type_head (type, oi->lsdb); lsa;
+       lsa = ospf6_lsdb_type_next (type, lsa))
+    {
+      if (OSPF6_LSA_IS_MAXAGE (lsa))
+        continue;
+
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("  include prefix from %s", lsa->name);
+
+      if (lsa->header->adv_router != oi->area->ospf6->router_id)
+        {
+          on = ospf6_neighbor_lookup (lsa->header->adv_router, oi);
+          if (on == NULL || on->state != OSPF6_NEIGHBOR_FULL)
+            {
+              if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+                zlog_debug ("    Neighbor not found or not Full, ignore");
+              continue;
+            }
+        }
+
+      link_lsa = (struct ospf6_link_lsa *)
+        ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header));
+
+      prefix_num = (unsigned short) ntohl (link_lsa->prefix_num);
+      start = (char *) link_lsa + sizeof (struct ospf6_link_lsa);
+      end = (char *) lsa->header + ntohs (lsa->header->length); 
+      for (current = start; current < end && prefix_num;
+           current += OSPF6_PREFIX_SIZE (op))
+        {
+          op = (struct ospf6_prefix *) current;
+          if (op->prefix_length == 0 ||
+              current + OSPF6_PREFIX_SIZE (op) > end)
+            break;
+
+          route = ospf6_route_create ();
+
+          route->type = OSPF6_DEST_TYPE_NETWORK;
+          route->prefix.family = AF_INET6;
+          route->prefix.prefixlen = op->prefix_length;
+          memset (&route->prefix.u.prefix6, 0, sizeof (struct in6_addr));
+          memcpy (&route->prefix.u.prefix6, OSPF6_PREFIX_BODY (op),
+                  OSPF6_PREFIX_SPACE (op->prefix_length));
+
+          route->path.origin.type = lsa->header->type;
+          route->path.origin.id = lsa->header->id;
+          route->path.origin.adv_router = lsa->header->adv_router;
+          route->path.options[0] = link_lsa->options[0];
+          route->path.options[1] = link_lsa->options[1];
+          route->path.options[2] = link_lsa->options[2];
+          route->path.prefix_options = op->prefix_options;
+          route->path.area_id = oi->area->area_id;
+          route->path.type = OSPF6_PATH_TYPE_INTRA;
+
+          if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+            {
+              prefix2str (&route->prefix, buf, sizeof (buf));
+              zlog_debug ("    include %s", buf);
+            }
+
+          ospf6_route_add (route, route_advertise);
+          prefix_num--;
+        }
+      if (current != end && IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("Trailing garbage in %s", lsa->name);
+    }
+
+  op = (struct ospf6_prefix *)
+    ((caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa));
+
+  prefix_num = 0;
+  for (route = ospf6_route_head (route_advertise); route;
+       route = ospf6_route_best_next (route))
+    {
+      op->prefix_length = route->prefix.prefixlen;
+      op->prefix_options = route->path.prefix_options;
+      op->prefix_metric = htons (0);
+      memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6,
+              OSPF6_PREFIX_SPACE (op->prefix_length));
+      op = OSPF6_PREFIX_NEXT (op);
+      prefix_num++;
+    }
+
+  ospf6_route_table_delete (route_advertise);
+
+  if (prefix_num == 0)
+    {
+      if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX))
+        zlog_debug ("Quit to Advertise Intra-Prefix: no route to advertise");
+      return 0;
+    }
+
+  intra_prefix_lsa->prefix_num = htons (prefix_num);
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = htons (OSPF6_LSTYPE_INTRA_PREFIX);
+  lsa_header->id = htonl (oi->interface->ifindex);
+  lsa_header->adv_router = oi->area->ospf6->router_id;
+  lsa_header->seqnum =
+    ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
+                         lsa_header->adv_router, oi->area->lsdb);
+  lsa_header->length = htons ((caddr_t) op - (caddr_t) lsa_header);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create (lsa_header);
+
+  /* Originate */
+  ospf6_lsa_originate_area (lsa, oi->area);
+
+  return 0;
+}
+
+void
+ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa)
+{
+  struct ospf6_area *oa;
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct prefix ls_prefix;
+  struct ospf6_route *route, *ls_entry;
+  int i, prefix_num;
+  struct ospf6_prefix *op;
+  char *start, *current, *end;
+  char buf[64];
+  struct interface *ifp;
+  int direct_connect = 0;
+
+  if (OSPF6_LSA_IS_MAXAGE (lsa))
+    return;
+
+  if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("%s found", lsa->name);
+
+  oa = OSPF6_AREA (lsa->lsdb->data);
+
+  intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+  if (intra_prefix_lsa->ref_type == htons (OSPF6_LSTYPE_ROUTER))
+    ospf6_linkstate_prefix (intra_prefix_lsa->ref_adv_router,
+                            htonl (0), &ls_prefix);
+  else if (intra_prefix_lsa->ref_type == htons (OSPF6_LSTYPE_NETWORK))
+    ospf6_linkstate_prefix (intra_prefix_lsa->ref_adv_router,
+                            intra_prefix_lsa->ref_id, &ls_prefix);
+  else
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+        zlog_debug ("Unknown reference LS-type: %#hx",
+                   ntohs (intra_prefix_lsa->ref_type));
+      return;
+    }
+
+  ls_entry = ospf6_route_lookup (&ls_prefix, oa->spf_table);
+  if (ls_entry == NULL)
+    {
+      if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+        {
+          ospf6_linkstate_prefix2str (&ls_prefix, buf, sizeof (buf));
+          zlog_debug ("LS entry does not exist: %s", buf);
+        }
+      return;
+    }
+
+  if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id)
+    {
+      /* the intra-prefix are directly connected */
+      direct_connect = 1;
+    }
+
+  prefix_num = ntohs (intra_prefix_lsa->prefix_num);
+  start = (caddr_t) intra_prefix_lsa +
+          sizeof (struct ospf6_intra_prefix_lsa);
+  end = OSPF6_LSA_END (lsa->header);
+  for (current = start; current < end; current += OSPF6_PREFIX_SIZE (op))
+    {
+      op = (struct ospf6_prefix *) current;
+      if (prefix_num == 0)
+        break;
+      if (end < current + OSPF6_PREFIX_SIZE (op))
+        break;
+
+      /* Appendix A.4.1.1 */
+      if (CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_NU))
+       {
+         if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+           {
+             ospf6_linkstate_prefix2str ((struct prefix *)OSPF6_PREFIX_BODY(op),
+                                         buf, sizeof (buf));
+             zlog_debug ("%s: Skipping Prefix %s has NU option set",
+                         __func__, buf);
+           }
+         continue;
+       }
+
+      route = ospf6_route_create ();
+
+      memset (&route->prefix, 0, sizeof (struct prefix));
+      route->prefix.family = AF_INET6;
+      route->prefix.prefixlen = op->prefix_length;
+      ospf6_prefix_in6_addr (&route->prefix.u.prefix6, op);
+
+      route->type = OSPF6_DEST_TYPE_NETWORK;
+      route->path.origin.type = lsa->header->type;
+      route->path.origin.id = lsa->header->id;
+      route->path.origin.adv_router = lsa->header->adv_router;
+      route->path.prefix_options = op->prefix_options;
+      route->path.area_id = oa->area_id;
+      route->path.type = OSPF6_PATH_TYPE_INTRA;
+      route->path.metric_type = 1;
+      route->path.cost = ls_entry->path.cost +
+                         ntohs (op->prefix_metric);
+
+      if (direct_connect)
+        {
+          ifp = if_lookup_prefix(&route->prefix);
+          if (ifp)
+            route->nexthop[0].ifindex = ifp->ifindex;
+        }
+      else
+        {
+          for (i = 0; i < OSPF6_MULTI_PATH_LIMIT &&
+               ospf6_nexthop_is_set (&ls_entry->nexthop[i]); i++)
+            ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]);
+        }
+
+      if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+        {
+          prefix2str (&route->prefix, buf, sizeof (buf));
+          zlog_debug ("  add %s", buf);
+        }
+
+      ospf6_route_add (route, oa->route_table);
+      prefix_num--;
+    }
+
+  if (current != end && IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("Trailing garbage ignored");
+}
+
+void
+ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa)
+{
+  struct ospf6_area *oa;
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct prefix prefix;
+  struct ospf6_route *route, *nroute;
+  int prefix_num;
+  struct ospf6_prefix *op;
+  char *start, *current, *end;
+  char buf[64];
+
+  if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("%s disappearing", lsa->name);
+
+  oa = OSPF6_AREA (lsa->lsdb->data);
+
+  intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)
+    OSPF6_LSA_HEADER_END (lsa->header);
+
+  prefix_num = ntohs (intra_prefix_lsa->prefix_num);
+  start = (caddr_t) intra_prefix_lsa +
+          sizeof (struct ospf6_intra_prefix_lsa);
+  end = OSPF6_LSA_END (lsa->header);
+  for (current = start; current < end; current += OSPF6_PREFIX_SIZE (op))
+    {
+      op = (struct ospf6_prefix *) current;
+      if (prefix_num == 0)
+        break;
+      if (end < current + OSPF6_PREFIX_SIZE (op))
+        break;
+      prefix_num--;
+
+      memset (&prefix, 0, sizeof (struct prefix));
+      prefix.family = AF_INET6;
+      prefix.prefixlen = op->prefix_length;
+      ospf6_prefix_in6_addr (&prefix.u.prefix6, op);
+
+      route = ospf6_route_lookup (&prefix, oa->route_table);
+      if (route == NULL)
+        continue;
+
+      for (ospf6_route_lock (route);
+           route && ospf6_route_is_prefix (&prefix, route);
+           route = nroute)
+        {
+          nroute = ospf6_route_next (route);
+          if (route->type != OSPF6_DEST_TYPE_NETWORK)
+            continue;
+          if (route->path.area_id != oa->area_id)
+            continue;
+          if (route->path.type != OSPF6_PATH_TYPE_INTRA)
+            continue;
+          if (route->path.origin.type != lsa->header->type ||
+              route->path.origin.id != lsa->header->id ||
+              route->path.origin.adv_router != lsa->header->adv_router)
+            continue;
+
+          if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+            {
+              prefix2str (&route->prefix, buf, sizeof (buf));
+              zlog_debug ("remove %s", buf);
+            }
+          ospf6_route_remove (route, oa->route_table);
+        }
+      if (route)
+       ospf6_route_unlock (route);
+    }
+
+  if (current != end && IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("Trailing garbage ignored");
+}
+
+void
+ospf6_intra_route_calculation (struct ospf6_area *oa)
+{
+  struct ospf6_route *route, *nroute;
+  u_int16_t type;
+  struct ospf6_lsa *lsa;
+  void (*hook_add) (struct ospf6_route *) = NULL;
+  void (*hook_remove) (struct ospf6_route *) = NULL;
+
+  if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("Re-examin intra-routes for area %s", oa->name);
+
+  hook_add = oa->route_table->hook_add;
+  hook_remove = oa->route_table->hook_remove;
+  oa->route_table->hook_add = NULL;
+  oa->route_table->hook_remove = NULL;
+
+  for (route = ospf6_route_head (oa->route_table); route;
+       route = ospf6_route_next (route))
+    route->flag = OSPF6_ROUTE_REMOVE;
+
+  type = htons (OSPF6_LSTYPE_INTRA_PREFIX);
+  for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa;
+       lsa = ospf6_lsdb_type_next (type, lsa))
+    ospf6_intra_prefix_lsa_add (lsa);
+
+  oa->route_table->hook_add = hook_add;
+  oa->route_table->hook_remove = hook_remove;
+
+  for (route = ospf6_route_head (oa->route_table); route;
+       route = nroute)
+    {
+      nroute = ospf6_route_next (route);
+      if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE) &&
+          CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD))
+        {
+          UNSET_FLAG (route->flag, OSPF6_ROUTE_REMOVE);
+          UNSET_FLAG (route->flag, OSPF6_ROUTE_ADD);
+        }
+
+      if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE))
+        ospf6_route_remove (route, oa->route_table);
+      else if (CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD) ||
+               CHECK_FLAG (route->flag, OSPF6_ROUTE_CHANGE))
+        {
+          if (hook_add)
+            (*hook_add) (route);
+        }
+
+      route->flag = 0;
+    }
+
+  if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX))
+    zlog_debug ("Re-examin intra-routes for area %s: Done", oa->name);
+}
+
+static void
+ospf6_brouter_debug_print (struct ospf6_route *brouter)
+{
+  u_int32_t brouter_id;
+  char brouter_name[16];
+  char area_name[16];
+  char destination[64];
+  char installed[16], changed[16];
+  struct timeval now, res;
+  char id[16], adv_router[16];
+  char capa[16], options[16];
+
+  brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+  inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
+  inet_ntop (AF_INET, &brouter->path.area_id, area_name, sizeof (area_name));
+  ospf6_linkstate_prefix2str (&brouter->prefix, destination,
+                              sizeof (destination));
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &brouter->installed, &res);
+  timerstring (&res, installed, sizeof (installed));
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &brouter->changed, &res);
+  timerstring (&res, changed, sizeof (changed));
+
+  inet_ntop (AF_INET, &brouter->path.origin.id, id, sizeof (id));
+  inet_ntop (AF_INET, &brouter->path.origin.adv_router, adv_router,
+             sizeof (adv_router));
+
+  ospf6_options_printbuf (brouter->path.options, options, sizeof (options));
+  ospf6_capability_printbuf (brouter->path.router_bits, capa, sizeof (capa));
+
+  zlog_info ("Brouter: %s via area %s", brouter_name, area_name);
+  zlog_info ("  memory: prev: %p this: %p next: %p parent rnode: %p",
+             (void *)brouter->prev, (void *)brouter, (void *)brouter->next,
+             (void *)brouter->rnode);
+  zlog_info ("  type: %d prefix: %s installed: %s changed: %s",
+             brouter->type, destination, installed, changed);
+  zlog_info ("  lock: %d flags: %s%s%s%s", brouter->lock,
+           (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_BEST)   ? "B" : "-"),
+           (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD)    ? "A" : "-"),
+           (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"),
+           (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-"));
+  zlog_info ("  path type: %s ls-origin %s id: %s adv-router %s",
+             OSPF6_PATH_TYPE_NAME (brouter->path.type),
+             ospf6_lstype_name (brouter->path.origin.type),
+             id, adv_router);
+  zlog_info ("  options: %s router-bits: %s metric-type: %d metric: %d/%d",
+             options, capa, brouter->path.metric_type,
+             brouter->path.cost, brouter->path.cost_e2);
+}
+
+void
+ospf6_intra_brouter_calculation (struct ospf6_area *oa)
+{
+  struct ospf6_route *brouter, *nbrouter, *copy;
+  void (*hook_add) (struct ospf6_route *) = NULL;
+  void (*hook_remove) (struct ospf6_route *) = NULL;
+  u_int32_t brouter_id;
+  char brouter_name[16];
+  
+  if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id))
+    zlog_info ("border-router calculation for area %s", oa->name);
+  
+  hook_add = oa->ospf6->brouter_table->hook_add;
+  hook_remove = oa->ospf6->brouter_table->hook_remove;
+  oa->ospf6->brouter_table->hook_add = NULL;
+  oa->ospf6->brouter_table->hook_remove = NULL;
+
+  /* withdraw the previous router entries for the area */
+  for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter;
+       brouter = ospf6_route_next (brouter))
+    {
+      brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+      inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
+      if (brouter->path.area_id != oa->area_id)
+        continue;
+      brouter->flag = OSPF6_ROUTE_REMOVE;
+
+      if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) ||
+          IS_OSPF6_DEBUG_ROUTE (MEMORY))
+        {
+          zlog_info ("%p: mark as removing: area %s brouter %s",
+                     (void *)brouter, oa->name, brouter_name);
+          ospf6_brouter_debug_print (brouter);
+        }
+    }
+
+  for (brouter = ospf6_route_head (oa->spf_table); brouter;
+       brouter = ospf6_route_next (brouter))
+    {
+      brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+      inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
+
+      if (brouter->type != OSPF6_DEST_TYPE_LINKSTATE)
+        continue;
+      if (ospf6_linkstate_prefix_id (&brouter->prefix) != htonl (0))
+        continue;
+      if (! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_E) &&
+          ! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_B))
+        continue;
+
+      if (! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_V6) ||
+         ! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_R))
+       continue;
+
+      copy = ospf6_route_copy (brouter);
+      copy->type = OSPF6_DEST_TYPE_ROUTER;
+      copy->path.area_id = oa->area_id;
+      ospf6_route_add (copy, oa->ospf6->brouter_table);
+
+      if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) ||
+          IS_OSPF6_DEBUG_ROUTE (MEMORY))
+        {
+          zlog_info ("%p: transfer: area %s brouter %s",
+                     (void *)brouter, oa->name, brouter_name);
+          ospf6_brouter_debug_print (brouter);
+        }
+    }
+
+  oa->ospf6->brouter_table->hook_add = hook_add;
+  oa->ospf6->brouter_table->hook_remove = hook_remove;
+
+  for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter;
+       brouter = nbrouter)
+    {
+      nbrouter = ospf6_route_next (brouter);
+      brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix);
+      inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name));
+      
+      if (brouter->path.area_id != oa->area_id)
+        continue;
+
+      if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_WAS_REMOVED))
+        continue;
+
+      if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE) &&
+          CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD))
+        {
+          UNSET_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE);
+          UNSET_FLAG (brouter->flag, OSPF6_ROUTE_ADD);
+        }
+
+      if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE))
+        {
+          if (IS_OSPF6_DEBUG_BROUTER ||
+              IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) ||
+              IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id))
+            zlog_info ("brouter %s disappears via area %s",
+                       brouter_name, oa->name);
+          ospf6_route_remove (brouter, oa->ospf6->brouter_table);
+        }
+      else if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD) ||
+               CHECK_FLAG (brouter->flag, OSPF6_ROUTE_CHANGE))
+        {
+          if (IS_OSPF6_DEBUG_BROUTER ||
+              IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) ||
+              IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id))
+            zlog_info ("brouter %s appears via area %s",
+                       brouter_name, oa->name);
+
+          /* newly added */
+          if (hook_add)
+            (*hook_add) (brouter);
+        }
+      else
+        {
+          if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) ||
+              IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id))
+            zlog_info ("brouter %s still exists via area %s",
+                       brouter_name, oa->name);
+        }
+
+      brouter->flag = 0;
+    }
+
+  if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id))
+    zlog_info ("border-router calculation for area %s: done", oa->name);
+}
+
+struct ospf6_lsa_handler router_handler =
+{
+  OSPF6_LSTYPE_ROUTER,
+  "Router",
+  "Rtr",
+  ospf6_router_lsa_show,
+  ospf6_router_lsa_get_nbr_id
+};
+
+struct ospf6_lsa_handler network_handler =
+{
+  OSPF6_LSTYPE_NETWORK,
+  "Network",
+  "Net",
+  ospf6_network_lsa_show,
+  ospf6_network_lsa_get_ar_id
+};
+
+struct ospf6_lsa_handler link_handler =
+{
+  OSPF6_LSTYPE_LINK,
+  "Link",
+  "Lnk",
+  ospf6_link_lsa_show,
+  ospf6_link_lsa_get_prefix_str
+};
+
+struct ospf6_lsa_handler intra_prefix_handler =
+{
+  OSPF6_LSTYPE_INTRA_PREFIX,
+  "Intra-Prefix",
+  "INP",
+  ospf6_intra_prefix_lsa_show,
+  ospf6_intra_prefix_lsa_get_prefix_str
+};
+
+void
+ospf6_intra_init (void)
+{
+  ospf6_install_lsa_handler (&router_handler);
+  ospf6_install_lsa_handler (&network_handler);
+  ospf6_install_lsa_handler (&link_handler);
+  ospf6_install_lsa_handler (&intra_prefix_handler);
+}
+
+DEFUN (debug_ospf6_brouter,
+       debug_ospf6_brouter_cmd,
+       "debug ospf6 border-routers",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+      )
+{
+  OSPF6_DEBUG_BROUTER_ON ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_brouter,
+       no_debug_ospf6_brouter_cmd,
+       "no debug ospf6 border-routers",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+      )
+{
+  OSPF6_DEBUG_BROUTER_OFF ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf6_brouter_router,
+       debug_ospf6_brouter_router_cmd,
+       "debug ospf6 border-routers router-id A.B.C.D",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+       "Debug specific border router\n"
+       "Specify border-router's router-id\n"
+      )
+{
+  u_int32_t router_id;
+  inet_pton (AF_INET, argv[0], &router_id);
+  OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON (router_id);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_brouter_router,
+       no_debug_ospf6_brouter_router_cmd,
+       "no debug ospf6 border-routers router-id",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+       "Debug specific border router\n"
+      )
+{
+  OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf6_brouter_area,
+       debug_ospf6_brouter_area_cmd,
+       "debug ospf6 border-routers area-id A.B.C.D",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+       "Debug border routers in specific Area\n"
+       "Specify Area-ID\n"
+      )
+{
+  u_int32_t area_id;
+  inet_pton (AF_INET, argv[0], &area_id);
+  OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON (area_id);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_brouter_area,
+       no_debug_ospf6_brouter_area_cmd,
+       "no debug ospf6 border-routers area-id",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug border router\n"
+       "Debug border routers in specific Area\n"
+      )
+{
+  OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF ();
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_brouter (struct vty *vty)
+{
+  char buf[16];
+  if (IS_OSPF6_DEBUG_BROUTER)
+    vty_out (vty, "debug ospf6 border-routers%s", VNL);
+  if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER)
+    {
+      inet_ntop (AF_INET, &conf_debug_ospf6_brouter_specific_router_id,
+                 buf, sizeof (buf));
+      vty_out (vty, "debug ospf6 border-routers router-id %s%s", buf, VNL);
+    }
+  if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA)
+    {
+      inet_ntop (AF_INET, &conf_debug_ospf6_brouter_specific_area_id,
+                 buf, sizeof (buf));
+      vty_out (vty, "debug ospf6 border-routers area-id %s%s", buf, VNL);
+    }
+  return 0;
+}
+
+void
+install_element_ospf6_debug_brouter (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_brouter_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_brouter_router_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_brouter_area_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_brouter_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_brouter_router_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_brouter_area_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_brouter_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_brouter_router_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_brouter_area_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_brouter_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_brouter_router_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_brouter_area_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h
new file mode 100644 (file)
index 0000000..c9660b6
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_INTRA_H
+#define OSPF6_INTRA_H
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_brouter;
+extern u_int32_t conf_debug_ospf6_brouter_specific_router_id;
+extern u_int32_t conf_debug_ospf6_brouter_specific_area_id;
+#define OSPF6_DEBUG_BROUTER_SUMMARY         0x01
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER 0x02
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA   0x04
+#define OSPF6_DEBUG_BROUTER_ON() \
+  (conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SUMMARY)
+#define OSPF6_DEBUG_BROUTER_OFF() \
+  (conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SUMMARY)
+#define IS_OSPF6_DEBUG_BROUTER \
+  (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SUMMARY)
+
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON(router_id)             \
+  do {                                                                \
+    conf_debug_ospf6_brouter_specific_router_id = (router_id);        \
+    conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER;  \
+  } while (0)
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF()                     \
+  do {                                                                \
+    conf_debug_ospf6_brouter_specific_router_id = 0;                  \
+    conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER; \
+  } while (0)
+#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER                        \
+  (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER)
+#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(router_id)          \
+  (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER &&                          \
+   conf_debug_ospf6_brouter_specific_router_id == (router_id))
+
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON(area_id)                 \
+  do {                                                                \
+    conf_debug_ospf6_brouter_specific_area_id = (area_id);            \
+    conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SPECIFIC_AREA;    \
+  } while (0)
+#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF()                       \
+  do {                                                                \
+    conf_debug_ospf6_brouter_specific_area_id = 0;                    \
+    conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SPECIFIC_AREA;   \
+  } while (0)
+#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA                          \
+  (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_AREA)
+#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(area_id)              \
+  (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA &&                            \
+   conf_debug_ospf6_brouter_specific_area_id == (area_id))
+
+/* Router-LSA */
+#define OSPF6_ROUTER_LSA_MIN_SIZE              4U
+struct ospf6_router_lsa
+{
+  u_char bits;
+  u_char options[3];
+  /* followed by ospf6_router_lsdesc(s) */
+};
+
+/* Link State Description in Router-LSA */
+#define OSPF6_ROUTER_LSDESC_FIX_SIZE          16U
+struct ospf6_router_lsdesc
+{
+  u_char    type;
+  u_char    reserved;
+  u_int16_t metric;                /* output cost */
+  u_int32_t interface_id;
+  u_int32_t neighbor_interface_id;
+  u_int32_t neighbor_router_id;
+};
+
+#define OSPF6_ROUTER_LSDESC_POINTTOPOINT       1
+#define OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK    2
+#define OSPF6_ROUTER_LSDESC_STUB_NETWORK       3
+#define OSPF6_ROUTER_LSDESC_VIRTUAL_LINK       4
+
+enum stub_router_mode
+  {
+    OSPF6_NOT_STUB_ROUTER,
+    OSPF6_IS_STUB_ROUTER,
+    OSPF6_IS_STUB_ROUTER_V6,
+  };
+
+#define ROUTER_LSDESC_IS_TYPE(t,x)                         \
+  ((((struct ospf6_router_lsdesc *)(x))->type ==           \
+   OSPF6_ROUTER_LSDESC_ ## t) ? 1 : 0)
+#define ROUTER_LSDESC_GET_METRIC(x)                        \
+  (ntohs (((struct ospf6_router_lsdesc *)(x))->metric))
+#define ROUTER_LSDESC_GET_IFID(x)                          \
+  (ntohl (((struct ospf6_router_lsdesc *)(x))->interface_id))
+#define ROUTER_LSDESC_GET_NBR_IFID(x)                      \
+  (ntohl (((struct ospf6_router_lsdesc *)(x))->neighbor_interface_id))
+#define ROUTER_LSDESC_GET_NBR_ROUTERID(x)                  \
+  (((struct ospf6_router_lsdesc *)(x))->neighbor_router_id)
+
+/* Network-LSA */
+#define OSPF6_NETWORK_LSA_MIN_SIZE             4U
+struct ospf6_network_lsa
+{
+  u_char reserved;
+  u_char options[3];
+  /* followed by ospf6_netowrk_lsd(s) */
+};
+
+/* Link State Description in Router-LSA */
+#define OSPF6_NETWORK_LSDESC_FIX_SIZE          4U
+struct ospf6_network_lsdesc
+{
+  u_int32_t router_id;
+};
+#define NETWORK_LSDESC_GET_NBR_ROUTERID(x)                  \
+  (((struct ospf6_network_lsdesc *)(x))->router_id)
+
+/* Link-LSA */
+#define OSPF6_LINK_LSA_MIN_SIZE               24U /* w/o 1st IPv6 prefix */
+struct ospf6_link_lsa
+{
+  u_char          priority;
+  u_char          options[3];
+  struct in6_addr linklocal_addr;
+  u_int32_t       prefix_num;
+  /* followed by ospf6 prefix(es) */
+};
+
+/* Intra-Area-Prefix-LSA */
+#define OSPF6_INTRA_PREFIX_LSA_MIN_SIZE       12U /* w/o 1st IPv6 prefix */
+struct ospf6_intra_prefix_lsa
+{
+  u_int16_t prefix_num;
+  u_int16_t ref_type;
+  u_int32_t ref_id;
+  u_int32_t ref_adv_router;
+  /* followed by ospf6 prefix(es) */
+};
+
+
+#define OSPF6_ROUTER_LSA_SCHEDULE(oa) \
+  do { \
+    if (! (oa)->thread_router_lsa \
+        && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \
+      (oa)->thread_router_lsa = \
+        thread_add_event (master, ospf6_router_lsa_originate, oa, 0); \
+  } while (0)
+#define OSPF6_NETWORK_LSA_SCHEDULE(oi) \
+  do { \
+    if (! (oi)->thread_network_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+      (oi)->thread_network_lsa = \
+        thread_add_event (master, ospf6_network_lsa_originate, oi, 0); \
+  } while (0)
+#define OSPF6_LINK_LSA_SCHEDULE(oi) \
+  do { \
+    if (! (oi)->thread_link_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+      (oi)->thread_link_lsa = \
+        thread_add_event (master, ospf6_link_lsa_originate, oi, 0); \
+  } while (0)
+#define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \
+  do { \
+    if (! (oa)->thread_intra_prefix_lsa \
+        && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \
+      (oa)->thread_intra_prefix_lsa = \
+        thread_add_event (master, ospf6_intra_prefix_lsa_originate_stub, \
+                          oa, 0); \
+  } while (0)
+#define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \
+  do { \
+    if (! (oi)->thread_intra_prefix_lsa \
+        && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+      (oi)->thread_intra_prefix_lsa = \
+        thread_add_event (master, ospf6_intra_prefix_lsa_originate_transit, \
+                          oi, 0); \
+  } while (0)
+
+#define OSPF6_NETWORK_LSA_EXECUTE(oi) \
+  do { \
+    THREAD_OFF ((oi)->thread_network_lsa); \
+    thread_execute (master, ospf6_network_lsa_originate, oi, 0); \
+  } while (0)
+#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \
+  do { \
+    THREAD_OFF ((oi)->thread_intra_prefix_lsa); \
+    thread_execute (master, ospf6_intra_prefix_lsa_originate_transit, oi, 0); \
+  } while (0)
+
+
+/* Function Prototypes */
+extern char *ospf6_router_lsdesc_lookup (u_char type, u_int32_t interface_id,
+                                         u_int32_t neighbor_interface_id,
+                                         u_int32_t neighbor_router_id,
+                                         struct ospf6_lsa *lsa);
+extern char *ospf6_network_lsdesc_lookup (u_int32_t router_id,
+                                          struct ospf6_lsa *lsa);
+
+extern int ospf6_router_is_stub_router (struct ospf6_lsa *lsa);
+extern int ospf6_router_lsa_originate (struct thread *);
+extern int ospf6_network_lsa_originate (struct thread *);
+extern int ospf6_link_lsa_originate (struct thread *);
+extern int ospf6_intra_prefix_lsa_originate_transit (struct thread *);
+extern int ospf6_intra_prefix_lsa_originate_stub (struct thread *);
+extern void ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa);
+extern void ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa);
+
+extern void ospf6_intra_route_calculation (struct ospf6_area *oa);
+extern void ospf6_intra_brouter_calculation (struct ospf6_area *oa);
+
+extern void ospf6_intra_init (void);
+
+extern int config_write_ospf6_debug_brouter (struct vty *vty);
+extern void install_element_ospf6_debug_brouter (void);
+
+#endif /* OSPF6_LSA_H */
+
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
new file mode 100644 (file)
index 0000000..3f008d3
--- /dev/null
@@ -0,0 +1,967 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+/* Include other stuffs */
+#include "log.h"
+#include "linklist.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "thread.h"
+#include "checksum.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+#include "ospf6d.h"
+
+vector ospf6_lsa_handler_vector;
+
+static int
+ospf6_unknown_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  u_char *start, *end, *current;
+  char byte[4];
+
+  start = (u_char *) lsa->header + sizeof (struct ospf6_lsa_header);
+  end = (u_char *) lsa->header + ntohs (lsa->header->length);
+
+  vty_out (vty, "        Unknown contents:%s", VNL);
+  for (current = start; current < end; current ++)
+    {
+      if ((current - start) % 16 == 0)
+        vty_out (vty, "%s        ", VNL);
+      else if ((current - start) % 4 == 0)
+        vty_out (vty, " ");
+
+      snprintf (byte, sizeof (byte), "%02x", *current);
+      vty_out (vty, "%s", byte);
+    }
+
+  vty_out (vty, "%s%s", VNL, VNL);
+  return 0;
+}
+
+struct ospf6_lsa_handler unknown_handler =
+{
+  OSPF6_LSTYPE_UNKNOWN,
+  "Unknown",
+  "Unk",
+  ospf6_unknown_lsa_show,
+  NULL,
+  OSPF6_LSA_DEBUG,
+};
+
+void
+ospf6_install_lsa_handler (struct ospf6_lsa_handler *handler)
+{
+  /* type in handler is host byte order */
+  int index = handler->type & OSPF6_LSTYPE_FCODE_MASK;
+  vector_set_index (ospf6_lsa_handler_vector, index, handler);
+}
+
+struct ospf6_lsa_handler *
+ospf6_get_lsa_handler (u_int16_t type)
+{
+  struct ospf6_lsa_handler *handler = NULL;
+  unsigned int index = ntohs (type) & OSPF6_LSTYPE_FCODE_MASK;
+
+  if (index >= vector_active (ospf6_lsa_handler_vector))
+    handler = &unknown_handler;
+  else
+    handler = vector_slot (ospf6_lsa_handler_vector, index);
+
+  if (handler == NULL)
+    handler = &unknown_handler;
+
+  return handler;
+}
+
+const char *
+ospf6_lstype_name (u_int16_t type)
+{
+  static char buf[8];
+  struct ospf6_lsa_handler *handler;
+
+  handler = ospf6_get_lsa_handler (type);
+  if (handler && handler != &unknown_handler)
+    return handler->name;
+
+  snprintf (buf, sizeof (buf), "0x%04hx", ntohs (type));
+  return buf;
+}
+
+const char *
+ospf6_lstype_short_name (u_int16_t type)
+{
+  static char buf[8];
+  struct ospf6_lsa_handler *handler;
+
+  handler = ospf6_get_lsa_handler (type);
+  if (handler && handler != &unknown_handler)
+    return handler->short_name;
+
+  snprintf (buf, sizeof (buf), "0x%04hx", ntohs (type));
+  return buf;
+}
+
+u_char
+ospf6_lstype_debug (u_int16_t type)
+{
+  struct ospf6_lsa_handler *handler;
+  handler = ospf6_get_lsa_handler (type);
+  return handler->debug;
+}
+
+/* RFC2328: Section 13.2 */
+int
+ospf6_lsa_is_differ (struct ospf6_lsa *lsa1,
+                     struct ospf6_lsa *lsa2)
+{
+  int len;
+
+  assert (OSPF6_LSA_IS_SAME (lsa1, lsa2));
+
+  /* XXX, Options ??? */
+
+  ospf6_lsa_age_current (lsa1);
+  ospf6_lsa_age_current (lsa2);
+  if (ntohs (lsa1->header->age) == OSPF_LSA_MAXAGE &&
+      ntohs (lsa2->header->age) != OSPF_LSA_MAXAGE)
+    return 1;
+  if (ntohs (lsa1->header->age) != OSPF_LSA_MAXAGE &&
+      ntohs (lsa2->header->age) == OSPF_LSA_MAXAGE)
+    return 1;
+
+  /* compare body */
+  if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length))
+    return 1;
+
+  len = ntohs (lsa1->header->length) - sizeof (struct ospf6_lsa_header);
+  return memcmp (lsa1->header + 1, lsa2->header + 1, len);
+}
+
+int
+ospf6_lsa_is_changed (struct ospf6_lsa *lsa1,
+                      struct ospf6_lsa *lsa2)
+{
+  int length;
+
+  if (OSPF6_LSA_IS_MAXAGE (lsa1) ^ OSPF6_LSA_IS_MAXAGE (lsa2))
+    return 1;
+  if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length))
+    return 1;
+  /* Going beyond LSA headers to compare the payload only makes sense, when both LSAs aren't header-only. */
+  if (CHECK_FLAG (lsa1->flag, OSPF6_LSA_HEADERONLY) != CHECK_FLAG (lsa2->flag, OSPF6_LSA_HEADERONLY))
+  {
+    zlog_warn ("%s: only one of two (%s, %s) LSAs compared is header-only", __func__, lsa1->name, lsa2->name);
+    return 1;
+  }
+  if (CHECK_FLAG (lsa1->flag, OSPF6_LSA_HEADERONLY))
+    return 0;
+
+  length = OSPF6_LSA_SIZE (lsa1->header) - sizeof (struct ospf6_lsa_header);
+  /* Once upper layer verifies LSAs received, length underrun should become a warning. */
+  if (length <= 0)
+    return 0;
+
+  return memcmp (OSPF6_LSA_HEADER_END (lsa1->header),
+                 OSPF6_LSA_HEADER_END (lsa2->header), length);
+}
+
+/* ospf6 age functions */
+/* calculate birth */
+static void
+ospf6_lsa_age_set (struct ospf6_lsa *lsa)
+{
+  struct timeval now;
+
+  assert (lsa && lsa->header);
+
+  if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0)
+    zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s",
+               safe_strerror (errno));
+
+  lsa->birth.tv_sec = now.tv_sec - ntohs (lsa->header->age);
+  lsa->birth.tv_usec = now.tv_usec;
+
+  return;
+}
+
+/* this function calculates current age from its birth,
+   then update age field of LSA header. return value is current age */
+u_int16_t
+ospf6_lsa_age_current (struct ospf6_lsa *lsa)
+{
+  struct timeval now;
+  u_int32_t ulage;
+  u_int16_t age;
+
+  assert (lsa);
+  assert (lsa->header);
+
+  /* current time */
+  if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0)
+    zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s",
+               safe_strerror (errno));
+
+  if (ntohs (lsa->header->age) >= OSPF_LSA_MAXAGE)
+    {
+      /* ospf6_lsa_premature_aging () sets age to MAXAGE; when using
+         relative time, we cannot compare against lsa birth time, so
+         we catch this special case here. */
+      lsa->header->age = htons (OSPF_LSA_MAXAGE);
+      return OSPF_LSA_MAXAGE;
+    }
+  /* calculate age */
+  ulage = now.tv_sec - lsa->birth.tv_sec;
+
+  /* if over MAXAGE, set to it */
+  age = (ulage > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage);
+
+  lsa->header->age = htons (age);
+  return age;
+}
+
+/* update age field of LSA header with adding InfTransDelay */
+void
+ospf6_lsa_age_update_to_send (struct ospf6_lsa *lsa, u_int32_t transdelay)
+{
+  unsigned short age;
+
+  age = ospf6_lsa_age_current (lsa) + transdelay;
+  if (age > OSPF_LSA_MAXAGE)
+    age = OSPF_LSA_MAXAGE;
+  lsa->header->age = htons (age);
+}
+
+void
+ospf6_lsa_premature_aging (struct ospf6_lsa *lsa)
+{
+  /* log */
+  if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type))
+    zlog_debug ("LSA: Premature aging: %s", lsa->name);
+
+  THREAD_OFF (lsa->expire);
+  THREAD_OFF (lsa->refresh);
+
+  /*
+   * We clear the LSA from the neighbor retx lists now because it
+   * will not get deleted later. Essentially, changing the age to
+   * MaxAge will prevent this LSA from being matched with its
+   * existing entries in the retx list thereby causing those entries
+   * to be silently replaced with its MaxAged version, but with ever
+   * increasing retx count causing this LSA to remain forever and
+   * for the MaxAge remover thread to be called forever too.
+   *
+   * The reason the previous entry silently disappears is that when
+   * entry is added to a neighbor's retx list, it replaces the existing
+   * entry. But since the ospf6_lsdb_add() routine is generic and not aware
+   * of the special semantics of retx count, the retx count is not
+   * decremented when its replaced. Attempting to add the incr and decr
+   * retx count routines as the hook_add and hook_remove for the retx lists
+   * have a problem because the hook_remove routine is called for MaxAge
+   * entries (as will be the case in a traditional LSDB, unlike in this case
+   * where an LSDB is used as an efficient tree structure to store all kinds
+   * of data) that are added instead of calling the hook_add routine.
+   */
+
+  ospf6_flood_clear (lsa);
+
+  lsa->header->age = htons (OSPF_LSA_MAXAGE);
+  thread_execute (master, ospf6_lsa_expire, lsa, 0);
+}
+
+/* check which is more recent. if a is more recent, return -1;
+   if the same, return 0; otherwise(b is more recent), return 1 */
+int
+ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b)
+{
+  int32_t seqnuma, seqnumb;
+  u_int16_t cksuma, cksumb;
+  u_int16_t agea, ageb;
+
+  assert (a && a->header);
+  assert (b && b->header);
+  assert (OSPF6_LSA_IS_SAME (a, b));
+
+  seqnuma = (int32_t) ntohl (a->header->seqnum);
+  seqnumb = (int32_t) ntohl (b->header->seqnum);
+
+  /* compare by sequence number */
+  if (seqnuma > seqnumb)
+    return -1;
+  if (seqnuma < seqnumb)
+    return 1;
+
+  /* Checksum */
+  cksuma = ntohs (a->header->checksum);
+  cksumb = ntohs (b->header->checksum);
+  if (cksuma > cksumb)
+    return -1;
+  if (cksuma < cksumb)
+    return 0;
+
+  /* Update Age */
+  agea = ospf6_lsa_age_current (a);
+  ageb = ospf6_lsa_age_current (b);
+
+  /* MaxAge check */
+  if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE)
+    return -1;
+  else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE)
+    return 1;
+
+  /* Age check */
+  if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF)
+    return 1;
+  else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF)
+    return -1;
+
+  /* neither recent */
+  return 0;
+}
+
+char *
+ospf6_lsa_printbuf (struct ospf6_lsa *lsa, char *buf, int size)
+{
+  char id[16], adv_router[16];
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router, adv_router,
+             sizeof (adv_router));
+  snprintf (buf, size, "[%s Id:%s Adv:%s]",
+            ospf6_lstype_name (lsa->header->type), id, adv_router);
+  return buf;
+}
+
+void
+ospf6_lsa_header_print_raw (struct ospf6_lsa_header *header)
+{
+  char id[16], adv_router[16];
+  inet_ntop (AF_INET, &header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &header->adv_router, adv_router,
+             sizeof (adv_router));
+  zlog_debug ("    [%s Id:%s Adv:%s]",
+             ospf6_lstype_name (header->type), id, adv_router);
+  zlog_debug ("    Age: %4hu SeqNum: %#08lx Cksum: %04hx Len: %d",
+             ntohs (header->age), (u_long) ntohl (header->seqnum),
+             ntohs (header->checksum), ntohs (header->length));
+}
+
+void
+ospf6_lsa_header_print (struct ospf6_lsa *lsa)
+{
+  ospf6_lsa_age_current (lsa);
+  ospf6_lsa_header_print_raw (lsa->header);
+}
+
+void
+ospf6_lsa_show_summary_header (struct vty *vty)
+{
+  vty_out (vty, "%-4s %-15s%-15s%4s %8s %30s%s",
+           "Type", "LSId", "AdvRouter", "Age", "SeqNum",
+           "Payload", VNL);
+}
+
+void
+ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char adv_router[16], id[16];
+  int type;
+  struct ospf6_lsa_handler *handler;
+  char buf[64], tmpbuf[80];
+  int cnt = 0;
+
+  assert (lsa);
+  assert (lsa->header);
+
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router, adv_router,
+             sizeof (adv_router));
+
+  type = ntohs(lsa->header->type);
+  handler = ospf6_get_lsa_handler (lsa->header->type);
+  if ((type == OSPF6_LSTYPE_INTER_PREFIX) ||
+      (type == OSPF6_LSTYPE_INTER_ROUTER) ||
+      (type == OSPF6_LSTYPE_AS_EXTERNAL))
+    {
+      vty_out (vty, "%-4s %-15s%-15s%4hu %8lx %30s%s",
+              ospf6_lstype_short_name (lsa->header->type),
+              id, adv_router, ospf6_lsa_age_current (lsa),
+              (u_long) ntohl (lsa->header->seqnum),
+              handler->get_prefix_str(lsa, buf, sizeof(buf), 0), VNL);
+    }
+  else if (type != OSPF6_LSTYPE_UNKNOWN)
+    {
+      sprintf (tmpbuf, "%-4s %-15s%-15s%4hu %8lx",
+              ospf6_lstype_short_name (lsa->header->type),
+              id, adv_router, ospf6_lsa_age_current (lsa),
+              (u_long) ntohl (lsa->header->seqnum));
+
+      while (handler->get_prefix_str(lsa, buf, sizeof(buf), cnt) != NULL)
+       {
+         vty_out (vty, "%s %30s%s", tmpbuf, buf, VNL);
+         cnt++;
+       }
+    }
+  else
+    {
+      vty_out (vty, "%-4s %-15s%-15s%4hu %8lx%s",
+              ospf6_lstype_short_name (lsa->header->type),
+              id, adv_router, ospf6_lsa_age_current (lsa),
+              (u_long) ntohl (lsa->header->seqnum), VNL);
+    }
+}
+
+void
+ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  u_char *start, *end, *current;
+  char byte[4];
+
+  start = (u_char *) lsa->header;
+  end = (u_char *) lsa->header + ntohs (lsa->header->length);
+
+  vty_out (vty, "%s", VNL);
+  vty_out (vty, "%s:%s", lsa->name, VNL);
+
+  for (current = start; current < end; current ++)
+    {
+      if ((current - start) % 16 == 0)
+        vty_out (vty, "%s        ", VNL);
+      else if ((current - start) % 4 == 0)
+        vty_out (vty, " ");
+
+      snprintf (byte, sizeof (byte), "%02x", *current);
+      vty_out (vty, "%s", byte);
+    }
+
+  vty_out (vty, "%s%s", VNL, VNL);
+  return;
+}
+
+void
+ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char adv_router[64], id[64];
+
+  assert (lsa && lsa->header);
+
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router,
+             adv_router, sizeof (adv_router));
+
+  vty_out (vty, "%s", VNL);
+  vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa),
+           ospf6_lstype_name (lsa->header->type), VNL);
+  vty_out (vty, "Link State ID: %s%s", id, VNL);
+  vty_out (vty, "Advertising Router: %s%s", adv_router, VNL);
+  vty_out (vty, "LS Sequence Number: %#010lx%s",
+           (u_long) ntohl (lsa->header->seqnum), VNL);
+  vty_out (vty, "CheckSum: %#06hx Length: %hu%s",
+           ntohs (lsa->header->checksum),
+           ntohs (lsa->header->length), VNL);
+  vty_out (vty, "Flag: %x %s", lsa->flag, VNL);
+  vty_out (vty, "Lock: %d %s", lsa->lock, VNL);
+  vty_out (vty, "ReTx Count: %d%s", lsa->retrans_count, VNL);
+  vty_out (vty, "Threads: Expire: 0x%p, Refresh: 0x%p %s",
+           (void *)lsa->expire, (void *)lsa->refresh, VNL);
+  vty_out (vty, "%s", VNL);
+  return;
+}
+
+void
+ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char adv_router[64], id[64];
+  struct ospf6_lsa_handler *handler;
+  struct timeval now, res;
+  char duration[16];
+
+  assert (lsa && lsa->header);
+
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router,
+             adv_router, sizeof (adv_router));
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &lsa->installed, &res);
+  timerstring (&res, duration, sizeof (duration));
+
+  vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa),
+           ospf6_lstype_name (lsa->header->type), VNL);
+  vty_out (vty, "Link State ID: %s%s", id, VNL);
+  vty_out (vty, "Advertising Router: %s%s", adv_router, VNL);
+  vty_out (vty, "LS Sequence Number: %#010lx%s",
+           (u_long) ntohl (lsa->header->seqnum), VNL);
+  vty_out (vty, "CheckSum: %#06hx Length: %hu%s",
+           ntohs (lsa->header->checksum),
+           ntohs (lsa->header->length), VNL);
+  vty_out (vty, "Duration: %s%s", duration, VNL);
+
+  handler = ospf6_get_lsa_handler (lsa->header->type);
+  if (handler->show == NULL)
+    handler = &unknown_handler;
+  (*handler->show) (vty, lsa);
+
+  vty_out (vty, "%s", VNL);
+}
+
+/* OSPFv3 LSA creation/deletion function */
+struct ospf6_lsa *
+ospf6_lsa_create (struct ospf6_lsa_header *header)
+{
+  struct ospf6_lsa *lsa = NULL;
+  struct ospf6_lsa_header *new_header = NULL;
+  u_int16_t lsa_size = 0;
+
+  /* size of the entire LSA */
+  lsa_size = ntohs (header->length);   /* XXX vulnerable */
+
+  /* allocate memory for this LSA */
+  new_header = (struct ospf6_lsa_header *)
+    XMALLOC (MTYPE_OSPF6_LSA, lsa_size);
+
+  /* copy LSA from original header */
+  memcpy (new_header, header, lsa_size);
+
+  /* LSA information structure */
+  /* allocate memory */
+  lsa = (struct ospf6_lsa *)
+    XCALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa));
+
+  lsa->header = (struct ospf6_lsa_header *) new_header;
+
+  /* dump string */
+  ospf6_lsa_printbuf (lsa, lsa->name, sizeof (lsa->name));
+
+  /* calculate birth of this lsa */
+  ospf6_lsa_age_set (lsa);
+
+  return lsa;
+}
+
+struct ospf6_lsa *
+ospf6_lsa_create_headeronly (struct ospf6_lsa_header *header)
+{
+  struct ospf6_lsa *lsa = NULL;
+  struct ospf6_lsa_header *new_header = NULL;
+
+  /* allocate memory for this LSA */
+  new_header = (struct ospf6_lsa_header *)
+    XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa_header));
+
+  /* copy LSA from original header */
+  memcpy (new_header, header, sizeof (struct ospf6_lsa_header));
+
+  /* LSA information structure */
+  /* allocate memory */
+  lsa = (struct ospf6_lsa *)
+    XCALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa));
+
+  lsa->header = (struct ospf6_lsa_header *) new_header;
+  SET_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY);
+
+  /* dump string */
+  ospf6_lsa_printbuf (lsa, lsa->name, sizeof (lsa->name));
+
+  /* calculate birth of this lsa */
+  ospf6_lsa_age_set (lsa);
+
+  return lsa;
+}
+
+void
+ospf6_lsa_delete (struct ospf6_lsa *lsa)
+{
+  assert (lsa->lock == 0);
+
+  /* cancel threads */
+  THREAD_OFF (lsa->expire);
+  THREAD_OFF (lsa->refresh);
+
+  /* do free */
+  XFREE (MTYPE_OSPF6_LSA, lsa->header);
+  XFREE (MTYPE_OSPF6_LSA, lsa);
+}
+
+struct ospf6_lsa *
+ospf6_lsa_copy (struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsa *copy = NULL;
+
+  ospf6_lsa_age_current (lsa);
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY))
+    copy = ospf6_lsa_create_headeronly (lsa->header);
+  else
+    copy = ospf6_lsa_create (lsa->header);
+  assert (copy->lock == 0);
+
+  copy->birth = lsa->birth;
+  copy->originated = lsa->originated;
+  copy->received = lsa->received;
+  copy->installed = lsa->installed;
+  copy->lsdb = lsa->lsdb;
+  copy->rn = NULL;
+
+  return copy;
+}
+
+/* increment reference counter of struct ospf6_lsa */
+void
+ospf6_lsa_lock (struct ospf6_lsa *lsa)
+{
+  lsa->lock++;
+  return;
+}
+
+/* decrement reference counter of struct ospf6_lsa */
+void
+ospf6_lsa_unlock (struct ospf6_lsa *lsa)
+{
+  /* decrement reference counter */
+  assert (lsa->lock > 0);
+  lsa->lock--;
+
+  if (lsa->lock != 0)
+    return;
+
+  ospf6_lsa_delete (lsa);
+}
+
+
+/* ospf6 lsa expiry */
+int
+ospf6_lsa_expire (struct thread *thread)
+{
+  struct ospf6_lsa *lsa;
+
+  lsa = (struct ospf6_lsa *) THREAD_ARG (thread);
+
+  assert (lsa && lsa->header);
+  assert (OSPF6_LSA_IS_MAXAGE (lsa));
+  assert (! lsa->refresh);
+
+  lsa->expire = (struct thread *) NULL;
+
+  if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type))
+    {
+      zlog_debug ("LSA Expire:");
+      ospf6_lsa_header_print (lsa);
+    }
+
+  if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY))
+    return 0;    /* dbexchange will do something ... */
+
+  /* reinstall lsa */
+  ospf6_install_lsa (lsa);
+
+  /* reflood lsa */
+  ospf6_flood (NULL, lsa);
+
+  /* schedule maxage remover */
+  ospf6_maxage_remove (ospf6);
+
+  return 0;
+}
+
+int
+ospf6_lsa_refresh (struct thread *thread)
+{
+  struct ospf6_lsa *old, *self, *new;
+  struct ospf6_lsdb *lsdb_self;
+
+  assert (thread);
+  old = (struct ospf6_lsa *) THREAD_ARG (thread);
+  assert (old && old->header);
+
+  old->refresh = (struct thread *) NULL;
+
+  lsdb_self = ospf6_get_scoped_lsdb_self (old);
+  self = ospf6_lsdb_lookup (old->header->type, old->header->id,
+                            old->header->adv_router, lsdb_self);
+  if (self == NULL)
+    {
+      if (IS_OSPF6_DEBUG_LSA_TYPE (old->header->type))
+        zlog_debug ("Refresh: could not find self LSA, flush %s", old->name);
+      ospf6_lsa_premature_aging (old);
+      return 0;
+    }
+
+  /* Reset age, increment LS sequence number. */
+  self->header->age = htons (0);
+  self->header->seqnum =
+    ospf6_new_ls_seqnum (self->header->type, self->header->id,
+                         self->header->adv_router, old->lsdb);
+  ospf6_lsa_checksum (self->header);
+
+  new = ospf6_lsa_create (self->header);
+  new->lsdb = old->lsdb;
+  new->refresh = thread_add_timer (master, ospf6_lsa_refresh, new,
+                                   OSPF_LS_REFRESH_TIME);
+
+  /* store it in the LSDB for self-originated LSAs */
+  ospf6_lsdb_add (ospf6_lsa_copy (new), lsdb_self);
+
+  if (IS_OSPF6_DEBUG_LSA_TYPE (new->header->type))
+    {
+      zlog_debug ("LSA Refresh:");
+      ospf6_lsa_header_print (new);
+    }
+
+  ospf6_install_lsa (new);
+  ospf6_flood (NULL, new);
+
+  return 0;
+}
+
+
+
+/* Fletcher Checksum -- Refer to RFC1008. */
+
+/* All the offsets are zero-based. The offsets in the RFC1008 are
+   one-based. */
+unsigned short
+ospf6_lsa_checksum (struct ospf6_lsa_header *lsa_header)
+{
+  u_char *buffer = (u_char *) &lsa_header->type;
+  int type_offset = buffer - (u_char *) &lsa_header->age; /* should be 2 */
+
+  /* Skip the AGE field */
+  u_int16_t len = ntohs(lsa_header->length) - type_offset;
+
+  /* Checksum offset starts from "type" field, not the beginning of the
+     lsa_header struct. The offset is 14, rather than 16. */
+  int checksum_offset = (u_char *) &lsa_header->checksum - buffer;
+
+  return (unsigned short)fletcher_checksum(buffer, len, checksum_offset);
+}
+
+int
+ospf6_lsa_checksum_valid (struct ospf6_lsa_header *lsa_header)
+{
+  u_char *buffer = (u_char *) &lsa_header->type;
+  int type_offset = buffer - (u_char *) &lsa_header->age; /* should be 2 */
+
+  /* Skip the AGE field */
+  u_int16_t len = ntohs(lsa_header->length) - type_offset;
+
+  return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) == 0);
+}
+
+void
+ospf6_lsa_init (void)
+{
+  ospf6_lsa_handler_vector = vector_init (0);
+  ospf6_install_lsa_handler (&unknown_handler);
+}
+
+void
+ospf6_lsa_terminate (void)
+{
+  vector_free (ospf6_lsa_handler_vector);
+}
+
+static char *
+ospf6_lsa_handler_name (struct ospf6_lsa_handler *h)
+{
+  static char buf[64];
+  unsigned int i; 
+  unsigned int size = strlen (h->name);
+
+  if (!strcmp(h->name, "unknown") &&
+      h->type != OSPF6_LSTYPE_UNKNOWN)
+    {
+      snprintf (buf, sizeof (buf), "%#04hx", h->type);
+      return buf;
+    }
+
+  for (i = 0; i < MIN (size, sizeof (buf)); i++)
+    {
+      if (! islower ((unsigned char)h->name[i]))
+        buf[i] = tolower ((unsigned char)h->name[i]);
+      else
+        buf[i] = h->name[i];
+    }
+  buf[size] = '\0';
+  return buf;
+}
+
+DEFUN (debug_ospf6_lsa_type,
+       debug_ospf6_lsa_hex_cmd,
+       "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug Link State Advertisements (LSAs)\n"
+       "Specify LS type as Hexadecimal\n"
+      )
+{
+  unsigned int i;
+  struct ospf6_lsa_handler *handler = NULL;
+
+  assert (argc);
+
+  for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++)
+    {
+      handler = vector_slot (ospf6_lsa_handler_vector, i);
+      if (handler == NULL)
+        continue;
+      if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0)
+        break;
+      if (! strcasecmp (argv[0], handler->name))
+        break;
+      handler = NULL;
+    }
+
+  if (handler == NULL)
+    handler = &unknown_handler;
+
+  if (argc >= 2)
+    {
+      if (! strcmp (argv[1], "originate"))
+        SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE);
+      if (! strcmp (argv[1], "examine"))
+        SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN);
+      if (! strcmp (argv[1], "flooding"))
+        SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD);
+    }
+  else
+    SET_FLAG (handler->debug, OSPF6_LSA_DEBUG);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf6_lsa_type,
+       debug_ospf6_lsa_hex_detail_cmd,
+       "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown) (originate|examine|flooding)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug Link State Advertisements (LSAs)\n"
+       "Specify LS type as Hexadecimal\n"
+      )
+
+DEFUN (no_debug_ospf6_lsa_type,
+       no_debug_ospf6_lsa_hex_cmd,
+       "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug Link State Advertisements (LSAs)\n"
+       "Specify LS type as Hexadecimal\n"
+      )
+{
+  u_int i;
+  struct ospf6_lsa_handler *handler = NULL;
+
+  assert (argc);
+
+  for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++)
+    {
+      handler = vector_slot (ospf6_lsa_handler_vector, i);
+      if (handler == NULL)
+        continue;
+      if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0)
+        break;
+      if (! strcasecmp (argv[0], handler->name))
+        break;
+    }
+
+  if (handler == NULL)
+    return CMD_SUCCESS;
+
+  if (argc >= 2)
+    {
+      if (! strcmp (argv[1], "originate"))
+        UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE);
+      if (! strcmp (argv[1], "examine"))
+        UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN);
+      if (! strcmp (argv[1], "flooding"))
+        UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD);
+    }
+  else
+    UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf6_lsa_type,
+       no_debug_ospf6_lsa_hex_detail_cmd,
+       "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix) (originate|examine|flooding)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug Link State Advertisements (LSAs)\n"
+       "Specify LS type as Hexadecimal\n"
+      )
+
+void
+install_element_ospf6_debug_lsa (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_detail_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_detail_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_detail_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_detail_cmd);
+}
+
+int
+config_write_ospf6_debug_lsa (struct vty *vty)
+{
+  u_int i;
+  struct ospf6_lsa_handler *handler;
+
+  for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++)
+    {
+      handler = vector_slot (ospf6_lsa_handler_vector, i);
+      if (handler == NULL)
+        continue;
+      if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG))
+        vty_out (vty, "debug ospf6 lsa %s%s",
+                 ospf6_lsa_handler_name (handler), VNL);
+      if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE))
+        vty_out (vty, "debug ospf6 lsa %s originate%s",
+                 ospf6_lsa_handler_name (handler), VNL);
+      if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN))
+        vty_out (vty, "debug ospf6 lsa %s examine%s",
+                 ospf6_lsa_handler_name (handler), VNL);
+      if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD))
+        vty_out (vty, "debug ospf6 lsa %s flooding%s",
+                 ospf6_lsa_handler_name (handler), VNL);
+    }
+
+  return 0;
+}
+
+
diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h
new file mode 100644 (file)
index 0000000..a85ca66
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_LSA_H
+#define OSPF6_LSA_H
+
+/* Debug option */
+#define OSPF6_LSA_DEBUG           0x01
+#define OSPF6_LSA_DEBUG_ORIGINATE 0x02
+#define OSPF6_LSA_DEBUG_EXAMIN    0x04
+#define OSPF6_LSA_DEBUG_FLOOD     0x08
+
+#define IS_OSPF6_DEBUG_LSA(name)                        \
+  (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \
+   OSPF6_LSA_DEBUG)
+#define IS_OSPF6_DEBUG_ORIGINATE(name)                  \
+  (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \
+   OSPF6_LSA_DEBUG_ORIGINATE)
+#define IS_OSPF6_DEBUG_EXAMIN(name)                     \
+  (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \
+   OSPF6_LSA_DEBUG_EXAMIN)
+#define IS_OSPF6_DEBUG_LSA_TYPE(type) \
+  (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG)
+#define IS_OSPF6_DEBUG_ORIGINATE_TYPE(type) \
+  (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_ORIGINATE)
+#define IS_OSPF6_DEBUG_EXAMIN_TYPE(type) \
+  (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_EXAMIN)
+#define IS_OSPF6_DEBUG_FLOOD_TYPE(type) \
+  (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_FLOOD)
+
+/* LSA definition */
+
+#define OSPF6_MAX_LSASIZE      4096
+
+/* Type */
+#define OSPF6_LSTYPE_UNKNOWN          0x0000
+#define OSPF6_LSTYPE_ROUTER           0x2001
+#define OSPF6_LSTYPE_NETWORK          0x2002
+#define OSPF6_LSTYPE_INTER_PREFIX     0x2003
+#define OSPF6_LSTYPE_INTER_ROUTER     0x2004
+#define OSPF6_LSTYPE_AS_EXTERNAL      0x4005
+#define OSPF6_LSTYPE_GROUP_MEMBERSHIP 0x2006
+#define OSPF6_LSTYPE_TYPE_7           0x2007
+#define OSPF6_LSTYPE_LINK             0x0008
+#define OSPF6_LSTYPE_INTRA_PREFIX     0x2009
+#define OSPF6_LSTYPE_SIZE             0x000a
+
+/* Masks for LS Type : RFC 2740 A.4.2.1 "LS type" */
+#define OSPF6_LSTYPE_UBIT_MASK        0x8000
+#define OSPF6_LSTYPE_SCOPE_MASK       0x6000
+#define OSPF6_LSTYPE_FCODE_MASK       0x1fff
+
+/* LSA scope */
+#define OSPF6_SCOPE_LINKLOCAL  0x0000
+#define OSPF6_SCOPE_AREA       0x2000
+#define OSPF6_SCOPE_AS         0x4000
+#define OSPF6_SCOPE_RESERVED   0x6000
+
+/* XXX U-bit handling should be treated here */
+#define OSPF6_LSA_SCOPE(type) \
+  (ntohs (type) & OSPF6_LSTYPE_SCOPE_MASK)
+
+/* LSA Header */
+#define OSPF6_LSA_HEADER_SIZE                 20U
+struct ospf6_lsa_header
+{
+  u_int16_t age;        /* LS age */
+  u_int16_t type;       /* LS type */
+  u_int32_t id;         /* Link State ID */
+  u_int32_t adv_router; /* Advertising Router */
+  u_int32_t seqnum;     /* LS sequence number */
+  u_int16_t checksum;   /* LS checksum */
+  u_int16_t length;     /* LSA length */
+};
+
+#define OSPF6_LSA_HEADER_END(h) \
+  ((caddr_t)(h) + sizeof (struct ospf6_lsa_header))
+#define OSPF6_LSA_SIZE(h) \
+  (ntohs (((struct ospf6_lsa_header *) (h))->length))
+#define OSPF6_LSA_END(h) \
+  ((caddr_t)(h) + ntohs (((struct ospf6_lsa_header *) (h))->length))
+#define OSPF6_LSA_IS_TYPE(t, L) \
+  ((L)->header->type == htons (OSPF6_LSTYPE_ ## t) ? 1 : 0)
+#define OSPF6_LSA_IS_SAME(L1, L2) \
+  ((L1)->header->adv_router == (L2)->header->adv_router && \
+   (L1)->header->id == (L2)->header->id && \
+   (L1)->header->type == (L2)->header->type)
+#define OSPF6_LSA_IS_MATCH(t, i, a, L) \
+  ((L)->header->adv_router == (a) && (L)->header->id == (i) && \
+   (L)->header->type == (t))
+#define OSPF6_LSA_IS_DIFFER(L1, L2)  ospf6_lsa_is_differ (L1, L2)
+#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == OSPF_LSA_MAXAGE)
+#define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2)
+#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1))
+
+
+struct ospf6_lsa
+{
+  char              name[64];   /* dump string */
+
+  struct route_node *rn;
+
+  unsigned char     lock;           /* reference counter */
+  unsigned char     flag;           /* special meaning (e.g. floodback) */
+
+  struct timeval    birth;          /* tv_sec when LS age 0 */
+  struct timeval    originated;     /* used by MinLSInterval check */
+  struct timeval    received;       /* used by MinLSArrival check */
+  struct timeval    installed;
+
+  struct thread    *expire;
+  struct thread    *refresh;        /* For self-originated LSA */
+
+  int               retrans_count;
+
+  struct ospf6_lsdb *lsdb;
+
+  /* lsa instance */
+  struct ospf6_lsa_header *header;
+};
+
+#define OSPF6_LSA_HEADERONLY 0x01
+#define OSPF6_LSA_FLOODBACK  0x02
+#define OSPF6_LSA_DUPLICATE  0x04
+#define OSPF6_LSA_IMPLIEDACK 0x08
+#define OSPF6_LSA_SEQWRAPPED 0x20
+
+struct ospf6_lsa_handler
+{
+  u_int16_t type; /* host byte order */
+  const char *name;
+  const char *short_name;
+  int (*show) (struct vty *, struct ospf6_lsa *);
+  char *(*get_prefix_str) (struct ospf6_lsa *, char *buf, int buflen, int pos);
+  u_char debug;
+};
+
+extern struct ospf6_lsa_handler unknown_handler;
+#define OSPF6_LSA_IS_KNOWN(type) \
+  (ospf6_get_lsa_handler (type) != &unknown_handler ? 1 : 0)
+
+/* Macro for LSA Origination */
+/* addr is (struct prefix *) */
+#define CONTINUE_IF_ADDRESS_LINKLOCAL(debug,addr)      \
+  if (IN6_IS_ADDR_LINKLOCAL (&(addr)->u.prefix6))      \
+    {                                                  \
+      char buf[64];                                    \
+      prefix2str (addr, buf, sizeof (buf));            \
+      if (debug)                                       \
+        zlog_debug ("Filter out Linklocal: %s", buf);  \
+      continue;                                        \
+    }
+
+#define CONTINUE_IF_ADDRESS_UNSPECIFIED(debug,addr)    \
+  if (IN6_IS_ADDR_UNSPECIFIED (&(addr)->u.prefix6))    \
+    {                                                  \
+      char buf[64];                                    \
+      prefix2str (addr, buf, sizeof (buf));            \
+      if (debug)                                       \
+        zlog_debug ("Filter out Unspecified: %s", buf);\
+      continue;                                        \
+    }
+
+#define CONTINUE_IF_ADDRESS_LOOPBACK(debug,addr)       \
+  if (IN6_IS_ADDR_LOOPBACK (&(addr)->u.prefix6))       \
+    {                                                  \
+      char buf[64];                                    \
+      prefix2str (addr, buf, sizeof (buf));            \
+      if (debug)                                       \
+        zlog_debug ("Filter out Loopback: %s", buf);   \
+      continue;                                        \
+    }
+
+#define CONTINUE_IF_ADDRESS_V4COMPAT(debug,addr)       \
+  if (IN6_IS_ADDR_V4COMPAT (&(addr)->u.prefix6))       \
+    {                                                  \
+      char buf[64];                                    \
+      prefix2str (addr, buf, sizeof (buf));            \
+      if (debug)                                       \
+        zlog_debug ("Filter out V4Compat: %s", buf);   \
+      continue;                                        \
+    }
+
+#define CONTINUE_IF_ADDRESS_V4MAPPED(debug,addr)       \
+  if (IN6_IS_ADDR_V4MAPPED (&(addr)->u.prefix6))       \
+    {                                                  \
+      char buf[64];                                    \
+      prefix2str (addr, buf, sizeof (buf));            \
+      if (debug)                                       \
+        zlog_debug ("Filter out V4Mapped: %s", buf);   \
+      continue;                                        \
+    }
+
+
+/* Function Prototypes */
+extern const char *ospf6_lstype_name (u_int16_t type);
+extern const char *ospf6_lstype_short_name (u_int16_t type);
+extern u_char ospf6_lstype_debug (u_int16_t type);
+extern int ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2);
+extern int ospf6_lsa_is_changed (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2);
+extern u_int16_t ospf6_lsa_age_current (struct ospf6_lsa *);
+extern void ospf6_lsa_age_update_to_send (struct ospf6_lsa *, u_int32_t);
+extern void ospf6_lsa_premature_aging (struct ospf6_lsa *);
+extern int ospf6_lsa_compare (struct ospf6_lsa *, struct ospf6_lsa *);
+
+extern char *ospf6_lsa_printbuf (struct ospf6_lsa *lsa, char *buf, int size);
+extern void ospf6_lsa_header_print_raw (struct ospf6_lsa_header *header);
+extern void ospf6_lsa_header_print (struct ospf6_lsa *lsa);
+extern void ospf6_lsa_show_summary_header (struct vty *vty);
+extern void ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa);
+extern void ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa);
+extern void ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa);
+extern void ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa);
+
+extern struct ospf6_lsa *ospf6_lsa_create (struct ospf6_lsa_header *header);
+extern struct ospf6_lsa *ospf6_lsa_create_headeronly (struct ospf6_lsa_header *header);
+extern void ospf6_lsa_delete (struct ospf6_lsa *lsa);
+extern struct ospf6_lsa *ospf6_lsa_copy (struct ospf6_lsa *);
+
+extern void ospf6_lsa_lock (struct ospf6_lsa *);
+extern void ospf6_lsa_unlock (struct ospf6_lsa *);
+
+extern int ospf6_lsa_expire (struct thread *);
+extern int ospf6_lsa_refresh (struct thread *);
+
+extern unsigned short ospf6_lsa_checksum (struct ospf6_lsa_header *);
+extern int ospf6_lsa_checksum_valid (struct ospf6_lsa_header *);
+extern int ospf6_lsa_prohibited_duration (u_int16_t type, u_int32_t id,
+                                          u_int32_t adv_router, void *scope);
+
+extern void ospf6_install_lsa_handler (struct ospf6_lsa_handler *handler);
+extern struct ospf6_lsa_handler *ospf6_get_lsa_handler (u_int16_t type);
+
+extern void ospf6_lsa_init (void);
+extern void ospf6_lsa_terminate (void);
+
+extern int config_write_ospf6_debug_lsa (struct vty *vty);
+extern void install_element_ospf6_debug_lsa (void);
+
+#endif /* OSPF6_LSA_H */
+
diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c
new file mode 100644 (file)
index 0000000..b5a4587
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "log.h"
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6d.h"
+
+struct ospf6_lsdb *
+ospf6_lsdb_create (void *data)
+{
+  struct ospf6_lsdb *lsdb;
+
+  lsdb = XCALLOC (MTYPE_OSPF6_LSDB, sizeof (struct ospf6_lsdb));
+  if (lsdb == NULL)
+    {
+      zlog_warn ("Can't malloc lsdb");
+      return NULL;
+    }
+  memset (lsdb, 0, sizeof (struct ospf6_lsdb));
+
+  lsdb->data = data;
+  lsdb->table = route_table_init ();
+  return lsdb;
+}
+
+void
+ospf6_lsdb_delete (struct ospf6_lsdb *lsdb)
+{
+  if (lsdb != NULL)
+    {
+      ospf6_lsdb_remove_all (lsdb);
+      route_table_finish (lsdb->table);
+      XFREE (MTYPE_OSPF6_LSDB, lsdb);
+    }
+}
+
+static void
+ospf6_lsdb_set_key (struct prefix_ipv6 *key, void *value, int len)
+{
+  assert (key->prefixlen % 8 == 0);
+
+  memcpy ((caddr_t) &key->prefix + key->prefixlen / 8,
+          (caddr_t) value, len);
+  key->family = AF_INET6;
+  key->prefixlen += len * 8;
+}
+
+#ifdef DEBUG
+static void
+_lsdb_count_assert (struct ospf6_lsdb *lsdb)
+{
+  struct ospf6_lsa *debug;
+  unsigned int num = 0;
+  for (debug = ospf6_lsdb_head (lsdb); debug;
+       debug = ospf6_lsdb_next (debug))
+    num++;
+
+  if (num == lsdb->count)
+    return;
+
+  zlog_debug ("PANIC !! lsdb[%p]->count = %d, real = %d",
+             lsdb, lsdb->count, num);
+  for (debug = ospf6_lsdb_head (lsdb); debug;
+       debug = ospf6_lsdb_next (debug))
+    zlog_debug ("%p %p %s lsdb[%p]", debug->prev, debug->next, debug->name,
+               debug->lsdb);
+  zlog_debug ("DUMP END");
+
+  assert (num == lsdb->count);
+}
+#define ospf6_lsdb_count_assert(t) (_lsdb_count_assert (t))
+#else /*DEBUG*/
+#define ospf6_lsdb_count_assert(t) ((void) 0)
+#endif /*DEBUG*/
+
+void
+ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb)
+{
+  struct prefix_ipv6 key;
+  struct route_node *current;
+  struct ospf6_lsa *old = NULL;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &lsa->header->type, sizeof (lsa->header->type));
+  ospf6_lsdb_set_key (&key, &lsa->header->adv_router,
+                      sizeof (lsa->header->adv_router));
+  ospf6_lsdb_set_key (&key, &lsa->header->id, sizeof (lsa->header->id));
+
+  current = route_node_get (lsdb->table, (struct prefix *) &key);
+  old = current->info;
+  current->info = lsa;
+  lsa->rn = current;
+  ospf6_lsa_lock (lsa);
+
+  if (!old)
+    {
+      lsdb->count++;
+
+      if (OSPF6_LSA_IS_MAXAGE (lsa))
+       {
+         if (lsdb->hook_remove)
+           (*lsdb->hook_remove) (lsa);
+       }
+      else
+       {
+         if (lsdb->hook_add)
+           (*lsdb->hook_add) (lsa);
+       }
+    }
+  else
+    {
+      if (OSPF6_LSA_IS_CHANGED (old, lsa))
+        {
+          if (OSPF6_LSA_IS_MAXAGE (lsa))
+            {
+              if (lsdb->hook_remove)
+                {
+                  (*lsdb->hook_remove) (old);
+                  (*lsdb->hook_remove) (lsa);
+                }
+            }
+          else if (OSPF6_LSA_IS_MAXAGE (old))
+            {
+              if (lsdb->hook_add)
+                (*lsdb->hook_add) (lsa);
+            }
+          else
+            {
+              if (lsdb->hook_remove)
+                (*lsdb->hook_remove) (old);
+              if (lsdb->hook_add)
+                (*lsdb->hook_add) (lsa);
+            }
+        }
+      ospf6_lsa_unlock (old);
+    }
+
+  ospf6_lsdb_count_assert (lsdb);
+}
+
+void
+ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+  struct prefix_ipv6 key;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &lsa->header->type, sizeof (lsa->header->type));
+  ospf6_lsdb_set_key (&key, &lsa->header->adv_router,
+                      sizeof (lsa->header->adv_router));
+  ospf6_lsdb_set_key (&key, &lsa->header->id, sizeof (lsa->header->id));
+
+  node = route_node_lookup (lsdb->table, (struct prefix *) &key);
+  assert (node && node->info == lsa);
+
+  node->info = NULL;
+  lsdb->count--;
+
+  if (lsdb->hook_remove)
+    (*lsdb->hook_remove) (lsa);
+
+  route_unlock_node (node);    /* to free the lookup lock */
+  route_unlock_node (node);    /* to free the original lock */
+  ospf6_lsa_unlock (lsa);
+
+  ospf6_lsdb_count_assert (lsdb);
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                   struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+  struct prefix_ipv6 key;
+
+  if (lsdb == NULL)
+    return NULL;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &type, sizeof (type));
+  ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router));
+  ospf6_lsdb_set_key (&key, &id, sizeof (id));
+
+  node = route_node_lookup (lsdb->table, (struct prefix *) &key);
+  if (node == NULL || node->info == NULL)
+    return NULL;
+
+  route_unlock_node (node);
+  return (struct ospf6_lsa *) node->info;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                        struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+  struct route_node *matched = NULL;
+  struct prefix_ipv6 key;
+  struct prefix *p;
+
+  if (lsdb == NULL)
+    return NULL;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &type, sizeof (type));
+  ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router));
+  ospf6_lsdb_set_key (&key, &id, sizeof (id));
+  p = (struct prefix *) &key;
+
+  {
+    char buf[64];
+    prefix2str (p, buf, sizeof (buf));
+    zlog_debug ("lsdb_lookup_next: key: %s", buf);
+  }
+
+  node = lsdb->table->top;
+  /* walk down tree. */
+  while (node && node->p.prefixlen <= p->prefixlen &&
+         prefix_match (&node->p, p))
+    {
+      matched = node;
+      node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)];
+    }
+
+  if (matched)
+    node = matched;
+  else
+    node = lsdb->table->top;
+  route_lock_node (node);
+
+  /* skip to real existing entry */
+  while (node && node->info == NULL)
+    node = route_next (node);
+
+  if (! node)
+    return NULL;
+
+  if (prefix_same (&node->p, p))
+    {
+      node = route_next (node);
+      while (node && node->info == NULL)
+        node = route_next (node);
+    }
+
+  if (! node)
+    return NULL;
+
+  route_unlock_node (node);
+  return (struct ospf6_lsa *) node->info;
+}
+
+/* Iteration function */
+struct ospf6_lsa *
+ospf6_lsdb_head (struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+
+  node = route_top (lsdb->table);
+  if (node == NULL)
+    return NULL;
+
+  /* skip to the existing lsdb entry */
+  while (node && node->info == NULL)
+    node = route_next (node);
+  if (node == NULL)
+    return NULL;
+
+  if (node->info)
+    ospf6_lsa_lock ((struct ospf6_lsa *) node->info);
+  return (struct ospf6_lsa *) node->info;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_next (struct ospf6_lsa *lsa)
+{
+  struct route_node *node = lsa->rn;
+  struct ospf6_lsa *next = NULL;
+
+  do {
+    node = route_next (node);
+  } while (node && node->info == NULL);
+
+  if ((node != NULL) && (node->info != NULL))
+    {
+      next = node->info;
+      ospf6_lsa_lock (next);
+    }
+
+  ospf6_lsa_unlock (lsa);
+  return next;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_type_router_head (u_int16_t type, u_int32_t adv_router,
+                             struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+  struct prefix_ipv6 key;
+  struct ospf6_lsa *lsa;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &type, sizeof (type));
+  ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router));
+
+  node = lsdb->table->top;
+
+  /* Walk down tree. */
+  while (node && node->p.prefixlen <= key.prefixlen &&
+        prefix_match (&node->p, (struct prefix *) &key))
+    node = node->link[prefix6_bit(&key.prefix, node->p.prefixlen)];
+
+  if (node)
+    route_lock_node (node);
+  while (node && node->info == NULL)
+    node = route_next (node);
+
+  if (node == NULL)
+    return NULL;
+
+  if (! prefix_match ((struct prefix *) &key, &node->p))
+    return NULL;
+
+  lsa = node->info;
+  ospf6_lsa_lock (lsa);
+
+  return lsa;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_type_router_next (u_int16_t type, u_int32_t adv_router,
+                             struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsa *next = ospf6_lsdb_next(lsa);
+
+  if (next)
+    {
+      if (next->header->type != type ||
+          next->header->adv_router != adv_router)
+       {
+         route_unlock_node (next->rn);
+         ospf6_lsa_unlock (next);
+         next = NULL;
+       }
+    }
+
+  return next;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb)
+{
+  struct route_node *node;
+  struct prefix_ipv6 key;
+  struct ospf6_lsa *lsa;
+
+  memset (&key, 0, sizeof (key));
+  ospf6_lsdb_set_key (&key, &type, sizeof (type));
+
+  /* Walk down tree. */
+  node = lsdb->table->top;
+  while (node && node->p.prefixlen <= key.prefixlen &&
+        prefix_match (&node->p, (struct prefix *) &key))
+    node = node->link[prefix6_bit(&key.prefix, node->p.prefixlen)];
+
+  if (node)
+    route_lock_node (node);
+  while (node && node->info == NULL)
+    node = route_next (node);
+
+  if (node == NULL)
+    return NULL;
+
+  if (! prefix_match ((struct prefix *) &key, &node->p))
+    return NULL;
+
+  lsa = node->info;
+  ospf6_lsa_lock (lsa);
+
+  return lsa;
+}
+
+struct ospf6_lsa *
+ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa)
+{
+  struct ospf6_lsa *next = ospf6_lsdb_next (lsa);
+
+  if (next)
+    {
+      if (next->header->type != type)
+       {
+         route_unlock_node (next->rn);
+         ospf6_lsa_unlock (next);
+         next = NULL;
+       }
+    }
+
+  return next;
+}
+
+void
+ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb)
+{
+  struct ospf6_lsa *lsa;
+
+  if (lsdb == NULL)
+    return;
+
+  for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa))
+    ospf6_lsdb_remove (lsa, lsdb);
+}
+
+void
+ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      if (lsa->rn != NULL)
+       route_unlock_node (lsa->rn);
+      ospf6_lsa_unlock (lsa);
+    }
+}
+
+int
+ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb)
+{
+  int reschedule = 0;
+  struct ospf6_lsa *lsa;
+
+  for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa))
+    {
+      if (! OSPF6_LSA_IS_MAXAGE (lsa))
+       continue;
+      if (lsa->retrans_count != 0)
+       {
+         reschedule = 1;
+         continue;
+       }
+      if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type))
+       zlog_debug ("Remove MaxAge %s", lsa->name);
+      if (CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED))
+      {
+        UNSET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED);
+        /*
+         * lsa->header->age = 0;
+         */
+        lsa->header->seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER + 1);
+        ospf6_lsa_checksum (lsa->header);
+
+       THREAD_OFF(lsa->refresh);
+        thread_execute (master, ospf6_lsa_refresh, lsa, 0);
+      } else {
+        ospf6_lsdb_remove (lsa, lsdb);
+      }
+    }
+
+  return (reschedule);
+}
+
+void
+ospf6_lsdb_show (struct vty *vty, enum ospf_lsdb_show_level level,
+                 u_int16_t *type, u_int32_t *id, u_int32_t *adv_router,
+                 struct ospf6_lsdb *lsdb)
+{
+  struct ospf6_lsa *lsa;
+  void (*showfunc) (struct vty *, struct ospf6_lsa *) = NULL;
+
+  switch (level)
+  {
+    case OSPF6_LSDB_SHOW_LEVEL_DETAIL:
+      showfunc = ospf6_lsa_show;
+      break;
+    case OSPF6_LSDB_SHOW_LEVEL_INTERNAL:
+      showfunc = ospf6_lsa_show_internal;
+      break;
+    case OSPF6_LSDB_SHOW_LEVEL_DUMP:
+      showfunc = ospf6_lsa_show_dump;
+      break;
+    case OSPF6_LSDB_SHOW_LEVEL_NORMAL:
+    default:
+      showfunc = ospf6_lsa_show_summary;
+  }
+  
+  if (type && id && adv_router)
+    {
+      lsa = ospf6_lsdb_lookup (*type, *id, *adv_router, lsdb);
+      if (lsa)
+        {
+          if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL)
+            ospf6_lsa_show (vty, lsa);
+          else
+            (*showfunc) (vty, lsa);
+        }
+      return;
+    }
+
+  if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL)
+    ospf6_lsa_show_summary_header (vty);
+
+  if (type && adv_router)
+    lsa = ospf6_lsdb_type_router_head (*type, *adv_router, lsdb);
+  else if (type)
+    lsa = ospf6_lsdb_type_head (*type, lsdb);
+  else
+    lsa = ospf6_lsdb_head (lsdb);
+  while (lsa)
+    {
+      if ((! adv_router || lsa->header->adv_router == *adv_router) &&
+          (! id || lsa->header->id == *id))
+        (*showfunc) (vty, lsa);
+
+      if (type && adv_router)
+        lsa = ospf6_lsdb_type_router_next (*type, *adv_router, lsa);
+      else if (type)
+        lsa = ospf6_lsdb_type_next (*type, lsa);
+      else
+        lsa = ospf6_lsdb_next (lsa);
+    }
+}
+
+/* Decide new Link State ID to originate.
+   note return value is network byte order */
+u_int32_t
+ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router,
+                 struct ospf6_lsdb *lsdb)
+{
+  struct ospf6_lsa *lsa;
+  u_int32_t id = 1;
+
+  for (lsa = ospf6_lsdb_type_router_head (type, adv_router, lsdb); lsa;
+       lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa))
+    {
+      if (ntohl (lsa->header->id) < id)
+        continue;
+      if (ntohl (lsa->header->id) > id)
+      {
+       ospf6_lsdb_lsa_unlock (lsa);
+        break;
+      }
+      id++;
+    }
+
+  return ((u_int32_t) htonl (id));
+}
+
+/* Decide new LS sequence number to originate.
+   note return value is network byte order */
+u_int32_t
+ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                     struct ospf6_lsdb *lsdb)
+{
+  struct ospf6_lsa *lsa;
+  signed long seqnum = 0;
+
+  /* if current database copy not found, return InitialSequenceNumber */
+  lsa = ospf6_lsdb_lookup (type, id, adv_router, lsdb);
+  if (lsa == NULL)
+    seqnum = OSPF_INITIAL_SEQUENCE_NUMBER;
+  else
+    seqnum = (signed long) ntohl (lsa->header->seqnum) + 1;
+
+  return ((u_int32_t) htonl (seqnum));
+}
+
+
diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h
new file mode 100644 (file)
index 0000000..425f615
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_LSDB_H
+#define OSPF6_LSDB_H
+
+#include "prefix.h"
+#include "table.h"
+
+struct ospf6_lsdb
+{
+  void *data; /* data structure that holds this lsdb */
+  struct route_table *table;
+  u_int32_t count;
+  void (*hook_add) (struct ospf6_lsa *);
+  void (*hook_remove) (struct ospf6_lsa *);
+};
+
+/* Function Prototypes */
+extern struct ospf6_lsdb *ospf6_lsdb_create (void *data);
+extern void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb);
+
+extern struct ospf6_lsa *ospf6_lsdb_lookup (u_int16_t type, u_int32_t id,
+                                            u_int32_t adv_router,
+                                            struct ospf6_lsdb *lsdb);
+extern struct ospf6_lsa *ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id,
+                                                 u_int32_t adv_router,
+                                                 struct ospf6_lsdb *lsdb);
+
+extern void ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb);
+extern void ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb);
+
+extern struct ospf6_lsa *ospf6_lsdb_head (struct ospf6_lsdb *lsdb);
+extern struct ospf6_lsa *ospf6_lsdb_next (struct ospf6_lsa *lsa);
+
+extern struct ospf6_lsa *ospf6_lsdb_type_router_head (u_int16_t type,
+                                               u_int32_t adv_router,
+                                               struct ospf6_lsdb *lsdb);
+extern struct ospf6_lsa *ospf6_lsdb_type_router_next (u_int16_t type,
+                                               u_int32_t adv_router,
+                                               struct ospf6_lsa *lsa);
+
+extern struct ospf6_lsa *ospf6_lsdb_type_head (u_int16_t type,
+                                               struct ospf6_lsdb *lsdb);
+extern struct ospf6_lsa *ospf6_lsdb_type_next (u_int16_t type,
+                                               struct ospf6_lsa *lsa);
+
+extern void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb);
+extern void ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa);
+
+enum ospf_lsdb_show_level {
+ OSPF6_LSDB_SHOW_LEVEL_NORMAL = 0,
+ OSPF6_LSDB_SHOW_LEVEL_DETAIL,
+ OSPF6_LSDB_SHOW_LEVEL_INTERNAL,
+ OSPF6_LSDB_SHOW_LEVEL_DUMP,
+};
+
+extern void ospf6_lsdb_show (struct vty *vty,
+                             enum ospf_lsdb_show_level level, u_int16_t *type,
+                             u_int32_t *id, u_int32_t *adv_router,
+                             struct ospf6_lsdb *lsdb);
+
+extern u_int32_t ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router,
+                                  struct ospf6_lsdb *lsdb);
+extern u_int32_t ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id,
+                                      u_int32_t adv_router,
+                                      struct ospf6_lsdb *lsdb);
+extern int ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb);
+
+#endif /* OSPF6_LSDB_H */
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
new file mode 100644 (file)
index 0000000..1afe84a
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 1999 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <lib/version.h>
+
+#include "getopt.h"
+#include "thread.h"
+#include "log.h"
+#include "command.h"
+#include "vty.h"
+#include "memory.h"
+#include "if.h"
+#include "filter.h"
+#include "prefix.h"
+#include "plist.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "vrf.h"
+
+#include "ospf6d.h"
+#include "ospf6_top.h"
+#include "ospf6_message.h"
+#include "ospf6_asbr.h"
+#include "ospf6_lsa.h"
+#include "ospf6_interface.h"
+#include "ospf6_zebra.h"
+
+/* Default configuration file name for ospf6d. */
+#define OSPF6_DEFAULT_CONFIG       "ospf6d.conf"
+
+/* Default port values. */
+#define OSPF6_VTY_PORT             2606
+
+/* ospf6d privileges */
+zebra_capabilities_t _caps_p [] =
+{
+  ZCAP_NET_RAW,
+  ZCAP_BIND
+};
+
+struct zebra_privs_t ospf6d_privs =
+{
+#if defined(QUAGGA_USER)
+  .user = QUAGGA_USER,
+#endif
+#if defined QUAGGA_GROUP
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = 2,
+  .cap_num_i = 0
+};
+
+/* ospf6d options, we use GNU getopt library. */
+struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "version",     no_argument,       NULL, 'v'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+  { "help",        no_argument,       NULL, 'h'},
+  { 0 }
+};
+
+/* Configuration file and directory. */
+char config_default[] = SYSCONFDIR OSPF6_DEFAULT_CONFIG;
+
+/* ospf6d program name. */
+char *progname;
+
+/* is daemon? */
+int daemon_mode = 0;
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_OSPF6D_PID;
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\n\
+Daemon which manages OSPF version 3.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-v, --version      Print program version\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+static void __attribute__ ((noreturn))
+ospf6_exit (int status)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  if (ospf6)
+    ospf6_delete (ospf6);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+    if (ifp->info != NULL)
+      ospf6_interface_delete(ifp->info);
+
+  ospf6_message_terminate ();
+  ospf6_asbr_terminate ();
+  ospf6_lsa_terminate ();
+
+  vrf_terminate ();
+  vty_terminate ();
+  cmd_terminate ();
+
+  if (zclient)
+    zclient_free (zclient);
+
+  if (master)
+    thread_master_free (master);
+
+  if (zlog_default)
+    closezlog (zlog_default);
+
+  exit (status);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog_info ("SIGHUP received");
+}
+
+/* SIGINT handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal SIGINT");
+  ospf6_exit (0);
+}
+
+/* SIGTERM handler. */
+static void
+sigterm (void)
+{
+  zlog_notice ("Terminating on signal SIGTERM");
+  ospf6_clean();
+  ospf6_exit (0);
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_info ("SIGUSR1 received");
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t ospf6_signals[] =
+{
+  {
+    .signal = SIGHUP,
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigterm,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+};
+
+/* Main routine of ospf6d. Treatment of argument and starting ospf finite
+   state machine is handled here. */
+int
+main (int argc, char *argv[], char *envp[])
+{
+  char *p;
+  int opt;
+  char *vty_addr = NULL;
+  int vty_port = 0;
+  char *config_file = NULL;
+  struct thread thread;
+  int dryrun = 0;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* Preserve name of myself. */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  /* Command line argument treatment. */
+  while (1) 
+    {
+      opt = getopt_long (argc, argv, "df:i:z:hp:A:P:u:g:vC", longopts, 0);
+    
+      if (opt == EOF)
+        break;
+
+      switch (opt) 
+        {
+        case 0:
+          break;
+        case 'd':
+          daemon_mode = 1;
+          break;
+        case 'f':
+          config_file = optarg;
+          break;
+        case 'A':
+          vty_addr = optarg;
+          break;
+        case 'i':
+          pid_file = optarg;
+          break;
+        case 'z':
+          zclient_serv_path_set (optarg);
+          break;
+        case 'P':
+         /* Deal with atoi() returning 0 on failure, and ospf6d not
+             listening on ospf6d port... */
+          if (strcmp(optarg, "0") == 0)
+            {
+              vty_port = 0;
+              break;
+            }
+          vty_port = atoi (optarg);
+          if (vty_port <= 0 || vty_port > 0xffff)
+            vty_port = OSPF6_VTY_PORT;
+          break;
+        case 'u':
+          ospf6d_privs.user = optarg;
+          break;
+       case 'g':
+         ospf6d_privs.group = optarg;
+         break;
+        case 'v':
+          print_version (progname);
+          exit (0);
+          break;
+       case 'C':
+         dryrun = 1;
+         break;
+        case 'h':
+          usage (progname, 0);
+          break;
+        default:
+          usage (progname, 1);
+          break;
+        }
+    }
+
+  if (geteuid () != 0)
+    {
+      errno = EPERM;
+      perror (progname);
+      exit (1);
+    }
+
+  /* thread master */
+  master = thread_master_create ();
+
+  /* Initializations. */
+  zlog_default = openzlog (progname, ZLOG_OSPF6,
+                           LOG_CONS|LOG_NDELAY|LOG_PID,
+                           LOG_DAEMON);
+  zprivs_init (&ospf6d_privs);
+  /* initialize zebra libraries */
+  signal_init (master, array_size(ospf6_signals), ospf6_signals);
+  cmd_init (1);
+  vty_init (master);
+  memory_init ();
+  vrf_init ();
+  access_list_init ();
+  prefix_list_init ();
+
+  /* initialize ospf6 */
+  ospf6_init ();
+
+  /* parse config file */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if (dryrun)
+    return(0);
+  
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("OSPF6d daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* pid file create */
+  pid_output (pid_file);
+
+  /* Make ospf6 vty socket. */
+  if (!vty_port)
+    vty_port = OSPF6_VTY_PORT;
+  vty_serv_sock (vty_addr, vty_port, OSPF6_VTYSH_PATH);
+
+  /* Print start message */
+  zlog_notice ("OSPF6d (Quagga-%s ospf6d-%s) starts: vty@%d",
+               QUAGGA_VERSION, OSPF6_DAEMON_VERSION,vty_port);
+
+  /* Start finite state machine, here we go! */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Log in case thread failed */
+  zlog_warn ("Thread failed");
+
+  /* Not reached. */
+  ospf6_exit (0);
+}
+
+
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
new file mode 100644 (file)
index 0000000..d382f03
--- /dev/null
@@ -0,0 +1,2550 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "log.h"
+#include "vty.h"
+#include "command.h"
+#include "thread.h"
+#include "linklist.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_network.h"
+#include "ospf6_message.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_interface.h"
+
+/* for structures and macros ospf6_lsa_examin() needs */
+#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
+#include "ospf6_intra.h"
+
+#include "ospf6_flood.h"
+#include "ospf6d.h"
+
+#include <netinet/ip6.h>
+
+unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};
+static const struct message ospf6_message_type_str [] =
+{
+  { OSPF6_MESSAGE_TYPE_HELLO,    "Hello"    },
+  { OSPF6_MESSAGE_TYPE_DBDESC,   "DbDesc"   },
+  { OSPF6_MESSAGE_TYPE_LSREQ,    "LSReq"    },
+  { OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate" },
+  { OSPF6_MESSAGE_TYPE_LSACK,    "LSAck"    },
+};
+static const size_t ospf6_message_type_str_max = array_size(ospf6_message_type_str);
+
+/* Minimum (besides the standard OSPF packet header) lengths for OSPF
+   packets of particular types, offset is the "type" field. */
+const u_int16_t ospf6_packet_minlen[OSPF6_MESSAGE_TYPE_ALL] =
+{
+  0,
+  OSPF6_HELLO_MIN_SIZE,
+  OSPF6_DB_DESC_MIN_SIZE,
+  OSPF6_LS_REQ_MIN_SIZE,
+  OSPF6_LS_UPD_MIN_SIZE,
+  OSPF6_LS_ACK_MIN_SIZE
+};
+
+/* Minimum (besides the standard LSA header) lengths for LSAs of particular
+   types, offset is the "LSA function code" portion of "LSA type" field. */
+const u_int16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] =
+{
+  0,
+  /* 0x2001 */ OSPF6_ROUTER_LSA_MIN_SIZE,
+  /* 0x2002 */ OSPF6_NETWORK_LSA_MIN_SIZE,
+  /* 0x2003 */ OSPF6_INTER_PREFIX_LSA_MIN_SIZE,
+  /* 0x2004 */ OSPF6_INTER_ROUTER_LSA_FIX_SIZE,
+  /* 0x4005 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE,
+  /* 0x2006 */ 0,
+  /* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE,
+  /* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE,
+  /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE
+};
+
+/* print functions */
+
+static void
+ospf6_header_print (struct ospf6_header *oh)
+{
+  char router_id[16], area_id[16];
+  inet_ntop (AF_INET, &oh->router_id, router_id, sizeof (router_id));
+  inet_ntop (AF_INET, &oh->area_id, area_id, sizeof (area_id));
+
+  zlog_debug ("    OSPFv%d Type:%d Len:%hu Router-ID:%s",
+             oh->version, oh->type, ntohs (oh->length), router_id);
+  zlog_debug ("    Area-ID:%s Cksum:%hx Instance-ID:%d",
+             area_id, ntohs (oh->checksum), oh->instance_id);
+}
+
+void
+ospf6_hello_print (struct ospf6_header *oh)
+{
+  struct ospf6_hello *hello;
+  char options[16];
+  char drouter[16], bdrouter[16], neighbor[16];
+  char *p;
+
+  ospf6_header_print (oh);
+  assert (oh->type == OSPF6_MESSAGE_TYPE_HELLO);
+
+  hello = (struct ospf6_hello *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  inet_ntop (AF_INET, &hello->drouter, drouter, sizeof (drouter));
+  inet_ntop (AF_INET, &hello->bdrouter, bdrouter, sizeof (bdrouter));
+  ospf6_options_printbuf (hello->options, options, sizeof (options));
+
+  zlog_debug ("    I/F-Id:%ld Priority:%d Option:%s",
+             (u_long) ntohl (hello->interface_id), hello->priority, options);
+  zlog_debug ("    HelloInterval:%hu DeadInterval:%hu",
+             ntohs (hello->hello_interval), ntohs (hello->dead_interval));
+  zlog_debug ("    DR:%s BDR:%s", drouter, bdrouter);
+
+  for (p = (char *) ((caddr_t) hello + sizeof (struct ospf6_hello));
+       p + sizeof (u_int32_t) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (u_int32_t))
+    {
+      inet_ntop (AF_INET, (void *) p, neighbor, sizeof (neighbor));
+      zlog_debug ("    Neighbor: %s", neighbor);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+void
+ospf6_dbdesc_print (struct ospf6_header *oh)
+{
+  struct ospf6_dbdesc *dbdesc;
+  char options[16];
+  char *p;
+
+  ospf6_header_print (oh);
+  assert (oh->type == OSPF6_MESSAGE_TYPE_DBDESC);
+
+  dbdesc = (struct ospf6_dbdesc *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  ospf6_options_printbuf (dbdesc->options, options, sizeof (options));
+
+  zlog_debug ("    MBZ: %#x Option: %s IfMTU: %hu",
+             dbdesc->reserved1, options, ntohs (dbdesc->ifmtu));
+  zlog_debug ("    MBZ: %#x Bits: %s%s%s SeqNum: %#lx",
+             dbdesc->reserved2,
+             (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) ? "I" : "-"),
+             (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) ? "M" : "-"),
+             (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) ? "m" : "s"),
+             (u_long) ntohl (dbdesc->seqnum));
+
+  for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc));
+       p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsa_header))
+    ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p);
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+void
+ospf6_lsreq_print (struct ospf6_header *oh)
+{
+  char id[16], adv_router[16];
+  char *p;
+
+  ospf6_header_print (oh);
+  assert (oh->type == OSPF6_MESSAGE_TYPE_LSREQ);
+
+  for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header));
+       p + sizeof (struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsreq_entry))
+    {
+      struct ospf6_lsreq_entry *e = (struct ospf6_lsreq_entry *) p;
+      inet_ntop (AF_INET, &e->adv_router, adv_router, sizeof (adv_router));
+      inet_ntop (AF_INET, &e->id, id, sizeof (id));
+      zlog_debug ("    [%s Id:%s Adv:%s]",
+                 ospf6_lstype_name (e->type), id, adv_router);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+void
+ospf6_lsupdate_print (struct ospf6_header *oh)
+{
+  struct ospf6_lsupdate *lsupdate;
+  u_long num;
+  char *p;
+
+  ospf6_header_print (oh);
+  assert (oh->type == OSPF6_MESSAGE_TYPE_LSUPDATE);
+
+  lsupdate = (struct ospf6_lsupdate *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  num = ntohl (lsupdate->lsa_number);
+  zlog_debug ("    Number of LSA: %ld", num);
+
+  for (p = (char *) ((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate));
+       p < OSPF6_MESSAGE_END (oh) &&
+       p + OSPF6_LSA_SIZE (p) <= OSPF6_MESSAGE_END (oh);
+       p += OSPF6_LSA_SIZE (p))
+    {
+      ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+void
+ospf6_lsack_print (struct ospf6_header *oh)
+{
+  char *p;
+
+  ospf6_header_print (oh);
+  assert (oh->type == OSPF6_MESSAGE_TYPE_LSACK);
+
+  for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header));
+       p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsa_header))
+    ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p);
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+static void
+ospf6_hello_recv (struct in6_addr *src, struct in6_addr *dst,
+                  struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  struct ospf6_hello *hello;
+  struct ospf6_neighbor *on;
+  char *p;
+  int twoway = 0;
+  int neighborchange = 0;
+  int backupseen = 0;
+
+  hello = (struct ospf6_hello *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  /* HelloInterval check */
+  if (ntohs (hello->hello_interval) != oi->hello_interval)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("HelloInterval mismatch");
+      return;
+    }
+
+  /* RouterDeadInterval check */
+  if (ntohs (hello->dead_interval) != oi->dead_interval)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("RouterDeadInterval mismatch");
+      return;
+    }
+
+  /* E-bit check */
+  if (OSPF6_OPT_ISSET (hello->options, OSPF6_OPT_E) !=
+      OSPF6_OPT_ISSET (oi->area->options, OSPF6_OPT_E))
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("E-bit mismatch");
+      return;
+    }
+
+  /* Find neighbor, create if not exist */
+  on = ospf6_neighbor_lookup (oh->router_id, oi);
+  if (on == NULL)
+    {
+      on = ospf6_neighbor_create (oh->router_id, oi);
+      on->prev_drouter = on->drouter = hello->drouter;
+      on->prev_bdrouter = on->bdrouter = hello->bdrouter;
+      on->priority = hello->priority;
+    }
+
+  /* always override neighbor's source address and ifindex */
+  on->ifindex = ntohl (hello->interface_id);
+  memcpy (&on->linklocal_addr, src, sizeof (struct in6_addr));
+
+  /* TwoWay check */
+  for (p = (char *) ((caddr_t) hello + sizeof (struct ospf6_hello));
+       p + sizeof (u_int32_t) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (u_int32_t))
+    {
+      u_int32_t *router_id = (u_int32_t *) p;
+
+      if (*router_id == oi->area->ospf6->router_id)
+        twoway++;
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+
+  /* RouterPriority check */
+  if (on->priority != hello->priority)
+    {
+      on->priority = hello->priority;
+      neighborchange++;
+    }
+
+  /* DR check */
+  if (on->drouter != hello->drouter)
+    {
+      on->prev_drouter = on->drouter;
+      on->drouter = hello->drouter;
+      if (on->prev_drouter == on->router_id || on->drouter == on->router_id)
+        neighborchange++;
+    }
+
+  /* BDR check */
+  if (on->bdrouter != hello->bdrouter)
+    {
+      on->prev_bdrouter = on->bdrouter;
+      on->bdrouter = hello->bdrouter;
+      if (on->prev_bdrouter == on->router_id || on->bdrouter == on->router_id)
+        neighborchange++;
+    }
+
+  /* BackupSeen check */
+  if (oi->state == OSPF6_INTERFACE_WAITING)
+    {
+      if (hello->bdrouter == on->router_id)
+        backupseen++;
+      else if (hello->drouter == on->router_id && hello->bdrouter == htonl (0))
+        backupseen++;
+    }
+
+  /* Execute neighbor events */
+  thread_execute (master, hello_received, on, 0);
+  if (twoway)
+    thread_execute (master, twoway_received, on, 0);
+  else
+    thread_execute (master, oneway_received, on, 0);
+
+  /* Schedule interface events */
+  if (backupseen)
+    thread_add_event (master, backup_seen, oi, 0);
+  if (neighborchange)
+    thread_add_event (master, neighbor_change, oi, 0);
+}
+
+static void
+ospf6_dbdesc_recv_master (struct ospf6_header *oh,
+                          struct ospf6_neighbor *on)
+{
+  struct ospf6_dbdesc *dbdesc;
+  char *p;
+
+  dbdesc = (struct ospf6_dbdesc *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  if (on->state < OSPF6_NEIGHBOR_INIT)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state less than Init, ignore");
+      return;
+    }
+
+  switch (on->state)
+    {
+    case OSPF6_NEIGHBOR_TWOWAY:
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state is 2-Way, ignore");
+      return;
+
+    case OSPF6_NEIGHBOR_INIT:
+      thread_execute (master, twoway_received, on, 0);
+      if (on->state != OSPF6_NEIGHBOR_EXSTART)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Neighbor state is not ExStart, ignore");
+          return;
+        }
+      /* else fall through to ExStart */
+
+    case OSPF6_NEIGHBOR_EXSTART:
+      /* if neighbor obeys us as our slave, schedule negotiation_done
+         and process LSA Headers. Otherwise, ignore this message */
+      if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) &&
+          ! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) &&
+          ntohl (dbdesc->seqnum) == on->dbdesc_seqnum)
+        {
+          /* execute NegotiationDone */
+          thread_execute (master, negotiation_done, on, 0);
+
+          /* Record neighbor options */
+          memcpy (on->options, dbdesc->options, sizeof (on->options));
+        }
+      else
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Negotiation failed");
+          return;
+        }
+      /* fall through to exchange */
+
+    case OSPF6_NEIGHBOR_EXCHANGE:
+      if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc)))
+        {
+          /* Duplicated DatabaseDescription is dropped by master */
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Duplicated dbdesc discarded by Master, ignore");
+          return;
+        }
+
+      if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Master/Slave bit mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Initialize bit mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (memcmp (on->options, dbdesc->options, sizeof (on->options)))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Option field mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (ntohl (dbdesc->seqnum) != on->dbdesc_seqnum)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Sequence number mismatch (%#lx expected)",
+                       (u_long) on->dbdesc_seqnum);
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+      break;
+
+    case OSPF6_NEIGHBOR_LOADING:
+    case OSPF6_NEIGHBOR_FULL:
+      if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc)))
+        {
+          /* Duplicated DatabaseDescription is dropped by master */
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Duplicated dbdesc discarded by Master, ignore");
+          return;
+        }
+
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Not duplicate dbdesc in state %s",
+                   ospf6_neighbor_state_str[on->state]);
+      thread_add_event (master, seqnumber_mismatch, on, 0);
+      return;
+
+    default:
+      assert (0);
+      break;
+    }
+
+  /* Process LSA headers */
+  for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc));
+       p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsa_header))
+    {
+      struct ospf6_lsa *his, *mine;
+      struct ospf6_lsdb *lsdb = NULL;
+
+      his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p);
+
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("%s", his->name);
+
+      switch (OSPF6_LSA_SCOPE (his->header->type))
+        {
+        case OSPF6_SCOPE_LINKLOCAL:
+          lsdb = on->ospf6_if->lsdb;
+          break;
+        case OSPF6_SCOPE_AREA:
+          lsdb = on->ospf6_if->area->lsdb;
+          break;
+        case OSPF6_SCOPE_AS:
+          lsdb = on->ospf6_if->area->ospf6->lsdb;
+          break;
+        case OSPF6_SCOPE_RESERVED:
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Ignoring LSA of reserved scope");
+          ospf6_lsa_delete (his);
+          continue;
+          break;
+        }
+
+      if (ntohs (his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL &&
+          IS_AREA_STUB (on->ospf6_if->area))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("SeqNumMismatch (E-bit mismatch), discard");
+          ospf6_lsa_delete (his);
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      mine = ospf6_lsdb_lookup (his->header->type, his->header->id,
+                                his->header->adv_router, lsdb);
+      if (mine == NULL)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Add request (No database copy)");
+          ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list);
+        }
+      else if (ospf6_lsa_compare (his, mine) < 0)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Add request (Received MoreRecent)");
+          ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list);
+        }
+      else
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Discard (Existing MoreRecent)");
+        }
+      ospf6_lsa_delete (his);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+
+  /* Increment sequence number */
+  on->dbdesc_seqnum ++;
+
+  /* schedule send lsreq */
+  if (on->request_list->count && (on->thread_send_lsreq == NULL))
+    on->thread_send_lsreq =
+      thread_add_event (master, ospf6_lsreq_send, on, 0);
+
+  THREAD_OFF (on->thread_send_dbdesc);
+
+  /* More bit check */
+  if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) &&
+      ! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT))
+    thread_add_event (master, exchange_done, on, 0);
+  else
+    on->thread_send_dbdesc =
+      thread_add_event (master, ospf6_dbdesc_send_newone, on, 0);
+
+  /* save last received dbdesc */
+  memcpy (&on->dbdesc_last, dbdesc, sizeof (struct ospf6_dbdesc));
+}
+
+static void
+ospf6_dbdesc_recv_slave (struct ospf6_header *oh,
+                         struct ospf6_neighbor *on)
+{
+  struct ospf6_dbdesc *dbdesc;
+  char *p;
+
+  dbdesc = (struct ospf6_dbdesc *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  if (on->state < OSPF6_NEIGHBOR_INIT)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state less than Init, ignore");
+      return;
+    }
+
+  switch (on->state)
+    {
+    case OSPF6_NEIGHBOR_TWOWAY:
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state is 2-Way, ignore");
+      return;
+
+    case OSPF6_NEIGHBOR_INIT:
+      thread_execute (master, twoway_received, on, 0);
+      if (on->state != OSPF6_NEIGHBOR_EXSTART)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Neighbor state is not ExStart, ignore");
+          return;
+        }
+      /* else fall through to ExStart */
+
+    case OSPF6_NEIGHBOR_EXSTART:
+      /* If the neighbor is Master, act as Slave. Schedule negotiation_done
+         and process LSA Headers. Otherwise, ignore this message */
+      if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) &&
+          CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) &&
+          CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) &&
+          ntohs (oh->length) == sizeof (struct ospf6_header) +
+                                sizeof (struct ospf6_dbdesc))
+        {
+          /* set the master/slave bit to slave */
+          UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
+
+          /* set the DD sequence number to one specified by master */
+          on->dbdesc_seqnum = ntohl (dbdesc->seqnum);
+
+          /* schedule NegotiationDone */
+          thread_execute (master, negotiation_done, on, 0);
+
+          /* Record neighbor options */
+          memcpy (on->options, dbdesc->options, sizeof (on->options));
+        }
+      else
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Negotiation failed");
+          return;
+        }
+      break;
+
+    case OSPF6_NEIGHBOR_EXCHANGE:
+      if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc)))
+        {
+          /* Duplicated DatabaseDescription causes slave to retransmit */
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Duplicated dbdesc causes retransmit");
+          THREAD_OFF (on->thread_send_dbdesc);
+          on->thread_send_dbdesc =
+            thread_add_event (master, ospf6_dbdesc_send, on, 0);
+          return;
+        }
+
+      if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Master/Slave bit mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Initialize bit mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (memcmp (on->options, dbdesc->options, sizeof (on->options)))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Option field mismatch");
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      if (ntohl (dbdesc->seqnum) != on->dbdesc_seqnum + 1)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Sequence number mismatch (%#lx expected)",
+                       (u_long) on->dbdesc_seqnum + 1);
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+      break;
+
+    case OSPF6_NEIGHBOR_LOADING:
+    case OSPF6_NEIGHBOR_FULL:
+      if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc)))
+        {
+          /* Duplicated DatabaseDescription causes slave to retransmit */
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Duplicated dbdesc causes retransmit");
+          THREAD_OFF (on->thread_send_dbdesc);
+          on->thread_send_dbdesc =
+            thread_add_event (master, ospf6_dbdesc_send, on, 0);
+          return;
+        }
+
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Not duplicate dbdesc in state %s",
+                   ospf6_neighbor_state_str[on->state]);
+      thread_add_event (master, seqnumber_mismatch, on, 0);
+      return;
+
+    default:
+      assert (0);
+      break;
+    }
+
+  /* Process LSA headers */
+  for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc));
+       p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsa_header))
+    {
+      struct ospf6_lsa *his, *mine;
+      struct ospf6_lsdb *lsdb = NULL;
+
+      his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p);
+
+      switch (OSPF6_LSA_SCOPE (his->header->type))
+        {
+        case OSPF6_SCOPE_LINKLOCAL:
+          lsdb = on->ospf6_if->lsdb;
+          break;
+        case OSPF6_SCOPE_AREA:
+          lsdb = on->ospf6_if->area->lsdb;
+          break;
+        case OSPF6_SCOPE_AS:
+          lsdb = on->ospf6_if->area->ospf6->lsdb;
+          break;
+        case OSPF6_SCOPE_RESERVED:
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Ignoring LSA of reserved scope");
+          ospf6_lsa_delete (his);
+          continue;
+          break;
+        }
+
+      if (OSPF6_LSA_SCOPE (his->header->type) == OSPF6_SCOPE_AS &&
+          IS_AREA_STUB (on->ospf6_if->area))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("E-bit mismatch with LSA Headers");
+          ospf6_lsa_delete (his);
+          thread_add_event (master, seqnumber_mismatch, on, 0);
+          return;
+        }
+
+      mine = ospf6_lsdb_lookup (his->header->type, his->header->id,
+                                his->header->adv_router, lsdb);
+      if (mine == NULL || ospf6_lsa_compare (his, mine) < 0)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Add request-list: %s", his->name);
+          ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list);
+        }
+      ospf6_lsa_delete (his);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+
+  /* Set sequence number to Master's */
+  on->dbdesc_seqnum = ntohl (dbdesc->seqnum);
+
+  /* schedule send lsreq */
+  if ((on->thread_send_lsreq == NULL) &&
+      (on->request_list->count))
+    on->thread_send_lsreq =
+      thread_add_event (master, ospf6_lsreq_send, on, 0);
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  on->thread_send_dbdesc =
+    thread_add_event (master, ospf6_dbdesc_send_newone, on, 0);
+
+  /* save last received dbdesc */
+  memcpy (&on->dbdesc_last, dbdesc, sizeof (struct ospf6_dbdesc));
+}
+
+static void
+ospf6_dbdesc_recv (struct in6_addr *src, struct in6_addr *dst,
+                   struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_dbdesc *dbdesc;
+
+  on = ospf6_neighbor_lookup (oh->router_id, oi);
+  if (on == NULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor not found, ignore");
+      return;
+    }
+
+  dbdesc = (struct ospf6_dbdesc *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  /* Interface MTU check */
+  if (!oi->mtu_ignore && ntohs (dbdesc->ifmtu) != oi->ifmtu)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("I/F MTU mismatch");
+      return;
+    }
+
+  if (dbdesc->reserved1 || dbdesc->reserved2)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Non-0 reserved field in %s's DbDesc, correct",
+                   on->name);
+      dbdesc->reserved1 = 0;
+      dbdesc->reserved2 = 0;
+    }
+
+  if (ntohl (oh->router_id) < ntohl (ospf6->router_id))
+    ospf6_dbdesc_recv_master (oh, on);
+  else if (ntohl (ospf6->router_id) < ntohl (oh->router_id))
+    ospf6_dbdesc_recv_slave (oh, on);
+  else
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Can't decide which is master, ignore");
+    }
+}
+
+static void
+ospf6_lsreq_recv (struct in6_addr *src, struct in6_addr *dst,
+                  struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  struct ospf6_neighbor *on;
+  char *p;
+  struct ospf6_lsreq_entry *e;
+  struct ospf6_lsdb *lsdb = NULL;
+  struct ospf6_lsa *lsa;
+
+  on = ospf6_neighbor_lookup (oh->router_id, oi);
+  if (on == NULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor not found, ignore");
+      return;
+    }
+
+  if (on->state != OSPF6_NEIGHBOR_EXCHANGE &&
+      on->state != OSPF6_NEIGHBOR_LOADING &&
+      on->state != OSPF6_NEIGHBOR_FULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state less than Exchange, ignore");
+      return;
+    }
+
+  /* Process each request */
+  for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header));
+       p + sizeof (struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsreq_entry))
+    {
+      e = (struct ospf6_lsreq_entry *) p;
+
+      switch (OSPF6_LSA_SCOPE (e->type))
+        {
+        case OSPF6_SCOPE_LINKLOCAL:
+          lsdb = on->ospf6_if->lsdb;
+          break;
+        case OSPF6_SCOPE_AREA:
+          lsdb = on->ospf6_if->area->lsdb;
+          break;
+        case OSPF6_SCOPE_AS:
+          lsdb = on->ospf6_if->area->ospf6->lsdb;
+          break;
+        default:
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Ignoring LSA of reserved scope");
+          continue;
+          break;
+        }
+
+      /* Find database copy */
+      lsa = ospf6_lsdb_lookup (e->type, e->id, e->adv_router, lsdb);
+      if (lsa == NULL)
+        {
+          char id[16], adv_router[16];
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            {
+              inet_ntop (AF_INET, &e->id, id, sizeof (id));
+              inet_ntop (AF_INET, &e->adv_router, adv_router,
+                     sizeof (adv_router));
+              zlog_debug ("Can't find requested [%s Id:%s Adv:%s]",
+                         ospf6_lstype_name (e->type), id, adv_router);
+            }
+          thread_add_event (master, bad_lsreq, on, 0);
+          return;
+        }
+
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->lsupdate_list);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+
+  /* schedule send lsupdate */
+  THREAD_OFF (on->thread_send_lsupdate);
+  on->thread_send_lsupdate =
+    thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0);
+}
+
+/* Verify, that the specified memory area contains exactly N valid IPv6
+   prefixes as specified by RFC5340, A.4.1. */
+static unsigned
+ospf6_prefixes_examin
+(
+  struct ospf6_prefix *current, /* start of buffer    */
+  unsigned length,
+  const u_int32_t req_num_pfxs  /* always compared with the actual number of prefixes */
+)
+{
+  u_char requested_pfx_bytes;
+  u_int32_t real_num_pfxs = 0;
+
+  while (length)
+  {
+    if (length < OSPF6_PREFIX_MIN_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: undersized IPv6 prefix header", __func__);
+      return MSG_NG;
+    }
+    /* safe to look deeper */
+    if (current->prefix_length > IPV6_MAX_BITLEN)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: invalid PrefixLength (%u bits)", __func__, current->prefix_length);
+      return MSG_NG;
+    }
+    /* covers both fixed- and variable-sized fields */
+    requested_pfx_bytes = OSPF6_PREFIX_MIN_SIZE + OSPF6_PREFIX_SPACE (current->prefix_length);
+    if (requested_pfx_bytes > length)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: undersized IPv6 prefix", __func__);
+      return MSG_NG;
+    }
+    /* next prefix */
+    length -= requested_pfx_bytes;
+    current = (struct ospf6_prefix *) ((caddr_t) current + requested_pfx_bytes);
+    real_num_pfxs++;
+  }
+  if (real_num_pfxs != req_num_pfxs)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: IPv6 prefix number mismatch (%u required, %u real)",
+                  __func__, req_num_pfxs, real_num_pfxs);
+    return MSG_NG;
+  }
+  return MSG_OK;
+}
+
+/* Verify an LSA to have a valid length and dispatch further (where
+   appropriate) to check if the contents, including nested IPv6 prefixes,
+   is properly sized/aligned within the LSA. Note that this function gets
+   LSA type in network byte order, uses in host byte order and passes to
+   ospf6_lstype_name() in network byte order again. */
+static unsigned
+ospf6_lsa_examin (struct ospf6_lsa_header *lsah, const u_int16_t lsalen, const u_char headeronly)
+{
+  struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
+  struct ospf6_as_external_lsa *as_external_lsa;
+  struct ospf6_link_lsa *link_lsa;
+  unsigned exp_length;
+  u_int8_t ltindex;
+  u_int16_t lsatype;
+
+  /* In case an additional minimum length constraint is defined for current
+     LSA type, make sure that this constraint is met. */
+  lsatype = ntohs (lsah->type);
+  ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK;
+  if
+  (
+    ltindex < OSPF6_LSTYPE_SIZE &&
+    ospf6_lsa_minlen[ltindex] &&
+    lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE
+  )
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: undersized (%u B) LSA", __func__, lsalen);
+    return MSG_NG;
+  }
+  switch (lsatype)
+  {
+  case OSPF6_LSTYPE_ROUTER:
+    /* RFC5340 A.4.3, LSA header + OSPF6_ROUTER_LSA_MIN_SIZE bytes followed
+       by N>=0 interface descriptions. */
+    if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE) % OSPF6_ROUTER_LSDESC_FIX_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: interface description alignment error", __func__);
+      return MSG_NG;
+    }
+    break;
+  case OSPF6_LSTYPE_NETWORK:
+    /* RFC5340 A.4.4, LSA header + OSPF6_NETWORK_LSA_MIN_SIZE bytes
+       followed by N>=0 attached router descriptions. */
+    if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_NETWORK_LSA_MIN_SIZE) % OSPF6_NETWORK_LSDESC_FIX_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: router description alignment error", __func__);
+      return MSG_NG;
+    }
+    break;
+  case OSPF6_LSTYPE_INTER_PREFIX:
+    /* RFC5340 A.4.5, LSA header + OSPF6_INTER_PREFIX_LSA_MIN_SIZE bytes
+       followed by 3-4 fields of a single IPv6 prefix. */
+    if (headeronly)
+      break;
+    return ospf6_prefixes_examin
+    (
+      (struct ospf6_prefix *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_PREFIX_LSA_MIN_SIZE),
+      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTER_PREFIX_LSA_MIN_SIZE,
+      1
+    );
+  case OSPF6_LSTYPE_INTER_ROUTER:
+    /* RFC5340 A.4.6, fixed-size LSA. */
+    if (lsalen > OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: oversized (%u B) LSA", __func__, lsalen);
+      return MSG_NG;
+    }
+    break;
+  case OSPF6_LSTYPE_AS_EXTERNAL: /* RFC5340 A.4.7, same as A.4.8. */
+  case OSPF6_LSTYPE_TYPE_7:
+    /* RFC5340 A.4.8, LSA header + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE bytes
+       followed by 3-4 fields of IPv6 prefix and 3 conditional LSA fields:
+       16 bytes of forwarding address, 4 bytes of external route tag,
+       4 bytes of referenced link state ID. */
+    if (headeronly)
+      break;
+    as_external_lsa = (struct ospf6_as_external_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE);
+    exp_length = OSPF6_LSA_HEADER_SIZE + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE;
+    /* To find out if the last optional field (Referenced Link State ID) is
+       assumed in this LSA, we need to access fixed fields of the IPv6
+       prefix before ospf6_prefix_examin() confirms its sizing. */
+    if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen);
+      return MSG_NG;
+    }
+    /* forwarding address */
+    if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F))
+      exp_length += 16;
+    /* external route tag */
+    if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T))
+      exp_length += 4;
+    /* referenced link state ID */
+    if (as_external_lsa->prefix.u._prefix_referenced_lstype)
+      exp_length += 4;
+    /* All the fixed-size fields (mandatory and optional) must fit. I.e.,
+       this check does not include any IPv6 prefix fields. */
+    if (exp_length > lsalen)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen);
+      return MSG_NG;
+    }
+    /* The last call completely covers the remainder (IPv6 prefix). */
+    return ospf6_prefixes_examin
+    (
+      (struct ospf6_prefix *) ((caddr_t) as_external_lsa + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE),
+      lsalen - exp_length,
+      1
+    );
+  case OSPF6_LSTYPE_LINK:
+    /* RFC5340 A.4.9, LSA header + OSPF6_LINK_LSA_MIN_SIZE bytes followed
+       by N>=0 IPv6 prefix blocks (with N declared beforehand). */
+    if (headeronly)
+      break;
+    link_lsa = (struct ospf6_link_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE);
+    return ospf6_prefixes_examin
+    (
+      (struct ospf6_prefix *) ((caddr_t) link_lsa + OSPF6_LINK_LSA_MIN_SIZE),
+      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_LINK_LSA_MIN_SIZE,
+      ntohl (link_lsa->prefix_num) /* 32 bits */
+    );
+  case OSPF6_LSTYPE_INTRA_PREFIX:
+  /* RFC5340 A.4.10, LSA header + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE bytes
+     followed by N>=0 IPv6 prefixes (with N declared beforehand). */
+    if (headeronly)
+      break;
+    intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE);
+    return ospf6_prefixes_examin
+    (
+      (struct ospf6_prefix *) ((caddr_t) intra_prefix_lsa + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE),
+      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTRA_PREFIX_LSA_MIN_SIZE,
+      ntohs (intra_prefix_lsa->prefix_num) /* 16 bits */
+    );
+  }
+  /* No additional validation is possible for unknown LSA types, which are
+     themselves valid in OPSFv3, hence the default decision is to accept. */
+  return MSG_OK;
+}
+
+/* Verify if the provided input buffer is a valid sequence of LSAs. This
+   includes verification of LSA blocks length/alignment and dispatching
+   of deeper-level checks. */
+static unsigned
+ospf6_lsaseq_examin
+(
+  struct ospf6_lsa_header *lsah, /* start of buffered data */
+  size_t length,
+  const u_char headeronly,
+  /* When declared_num_lsas is not 0, compare it to the real number of LSAs
+     and treat the difference as an error. */
+  const u_int32_t declared_num_lsas
+)
+{
+  u_int32_t counted_lsas = 0;
+
+  while (length)
+  {
+    u_int16_t lsalen;
+    if (length < OSPF6_LSA_HEADER_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: undersized (%zu B) trailing (#%u) LSA header",
+                    __func__, length, counted_lsas);
+      return MSG_NG;
+    }
+    /* save on ntohs() calls here and in the LSA validator */
+    lsalen = OSPF6_LSA_SIZE (lsah);
+    if (lsalen < OSPF6_LSA_HEADER_SIZE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: malformed LSA header #%u, declared length is %u B",
+                    __func__, counted_lsas, lsalen);
+      return MSG_NG;
+    }
+    if (headeronly)
+    {
+      /* less checks here and in ospf6_lsa_examin() */
+      if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 1))
+      {
+        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+          zlog_debug ("%s: anomaly in header-only %s LSA #%u", __func__,
+                      ospf6_lstype_name (lsah->type), counted_lsas);
+        return MSG_NG;
+      }
+      lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE);
+      length -= OSPF6_LSA_HEADER_SIZE;
+    }
+    else
+    {
+      /* make sure the input buffer is deep enough before further checks */
+      if (lsalen > length)
+      {
+        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+          zlog_debug ("%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B",
+                      __func__, ospf6_lstype_name (lsah->type), counted_lsas, lsalen, length);
+        return MSG_NG;
+      }
+      if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 0))
+      {
+        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+          zlog_debug ("%s: anomaly in %s LSA #%u", __func__,
+                      ospf6_lstype_name (lsah->type), counted_lsas);
+        return MSG_NG;
+      }
+      lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + lsalen);
+      length -= lsalen;
+    }
+    counted_lsas++;
+  }
+
+  if (declared_num_lsas && counted_lsas != declared_num_lsas)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)",
+                  __func__, declared_num_lsas, counted_lsas);
+    return MSG_NG;
+  }
+  return MSG_OK;
+}
+
+/* Verify a complete OSPF packet for proper sizing/alignment. */
+static unsigned
+ospf6_packet_examin (struct ospf6_header *oh, const unsigned bytesonwire)
+{
+  struct ospf6_lsupdate *lsupd;
+  unsigned test;
+
+  /* length, 1st approximation */
+  if (bytesonwire < OSPF6_HEADER_SIZE)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: undersized (%u B) packet", __func__, bytesonwire);
+    return MSG_NG;
+  }
+  /* Now it is safe to access header fields. */
+  if (bytesonwire != ntohs (oh->length))
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: packet length error (%u real, %u declared)",
+                  __func__, bytesonwire, ntohs (oh->length));
+    return MSG_NG;
+  }
+  /* version check */
+  if (oh->version != OSPFV3_VERSION)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version);
+    return MSG_NG;
+  }
+  /* length, 2nd approximation */
+  if
+  (
+    oh->type < OSPF6_MESSAGE_TYPE_ALL &&
+    ospf6_packet_minlen[oh->type] &&
+    bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]
+  )
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: undersized (%u B) %s packet", __func__,
+                  bytesonwire, LOOKUP (ospf6_message_type_str, oh->type));
+    return MSG_NG;
+  }
+  /* type-specific deeper validation */
+  switch (oh->type)
+  {
+  case OSPF6_MESSAGE_TYPE_HELLO:
+    /* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes followed
+       by N>=0 router-IDs. */
+    if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) % 4)
+      return MSG_OK;
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: alignment error in %s packet",
+                  __func__, LOOKUP (ospf6_message_type_str, oh->type));
+    return MSG_NG;
+  case OSPF6_MESSAGE_TYPE_DBDESC:
+    /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes followed
+       by N>=0 header-only LSAs. */
+    test = ospf6_lsaseq_examin
+    (
+      (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_DB_DESC_MIN_SIZE),
+      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_DB_DESC_MIN_SIZE,
+      1,
+      0
+    );
+    break;
+  case OSPF6_MESSAGE_TYPE_LSREQ:
+    /* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */
+    if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE)
+      return MSG_OK;
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: alignment error in %s packet",
+                  __func__, LOOKUP (ospf6_message_type_str, oh->type));
+    return MSG_NG;
+  case OSPF6_MESSAGE_TYPE_LSUPDATE:
+    /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes followed
+       by N>=0 full LSAs (with N declared beforehand). */
+    lsupd = (struct ospf6_lsupdate *) ((caddr_t) oh + OSPF6_HEADER_SIZE);
+    test = ospf6_lsaseq_examin
+    (
+      (struct ospf6_lsa_header *) ((caddr_t) lsupd + OSPF6_LS_UPD_MIN_SIZE),
+      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE,
+      0,
+      ntohl (lsupd->lsa_number) /* 32 bits */
+    );
+    break;
+  case OSPF6_MESSAGE_TYPE_LSACK:
+    /* RFC5340 A.3.6, packet header + N>=0 header-only LSAs. */
+    test = ospf6_lsaseq_examin
+    (
+      (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_LS_ACK_MIN_SIZE),
+      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE,
+      1,
+      0
+    );
+    break;
+  default:
+    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+      zlog_debug ("%s: invalid (%u) message type", __func__, oh->type);
+    return MSG_NG;
+  }
+  if (test != MSG_OK && IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+    zlog_debug ("%s: anomaly in %s packet", __func__, LOOKUP (ospf6_message_type_str, oh->type));
+  return test;
+}
+
+/* Verify particular fields of otherwise correct received OSPF packet to
+   meet the requirements of RFC. */
+static int
+ospf6_rxpacket_examin (struct ospf6_interface *oi, struct ospf6_header *oh, const unsigned bytesonwire)
+{
+  char buf[2][INET_ADDRSTRLEN];
+
+  if (MSG_OK != ospf6_packet_examin (oh, bytesonwire))
+    return MSG_NG;
+
+  /* Area-ID check */
+  if (oh->area_id != oi->area->area_id)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+    {
+      if (oh->area_id == OSPF_AREA_BACKBONE)
+        zlog_debug ("%s: Message may be via Virtual Link: not supported", __func__);
+      else
+        zlog_debug
+        (
+          "%s: Area-ID mismatch (my %s, rcvd %s)", __func__,
+          inet_ntop (AF_INET, &oi->area->area_id, buf[0], INET_ADDRSTRLEN),
+          inet_ntop (AF_INET, &oh->area_id, buf[1], INET_ADDRSTRLEN)
+         );
+    }
+    return MSG_NG;
+  }
+
+  /* Instance-ID check */
+  if (oh->instance_id != oi->instance_id)
+  {
+    if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+      zlog_debug ("%s: Instance-ID mismatch (my %u, rcvd %u)", __func__, oi->instance_id, oh->instance_id);
+    return MSG_NG;
+  }
+
+  /* Router-ID check */
+  if (oh->router_id == oi->area->ospf6->router_id)
+  {
+    zlog_warn ("%s: Duplicate Router-ID (%s)", __func__, inet_ntop (AF_INET, &oh->router_id, buf[0], INET_ADDRSTRLEN));
+    return MSG_NG;
+  }
+  return MSG_OK;
+}
+
+static void
+ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst,
+                     struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsupdate *lsupdate;
+  char *p;
+
+  on = ospf6_neighbor_lookup (oh->router_id, oi);
+  if (on == NULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor not found, ignore");
+      return;
+    }
+
+  if (on->state != OSPF6_NEIGHBOR_EXCHANGE &&
+      on->state != OSPF6_NEIGHBOR_LOADING &&
+      on->state != OSPF6_NEIGHBOR_FULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state less than Exchange, ignore");
+      return;
+    }
+
+  lsupdate = (struct ospf6_lsupdate *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  /* Process LSAs */
+  for (p = (char *) ((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate));
+       p < OSPF6_MESSAGE_END (oh) &&
+       p + OSPF6_LSA_SIZE (p) <= OSPF6_MESSAGE_END (oh);
+       p += OSPF6_LSA_SIZE (p))
+    {
+      ospf6_receive_lsa (on, (struct ospf6_lsa_header *) p);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+
+}
+
+static void
+ospf6_lsack_recv (struct in6_addr *src, struct in6_addr *dst,
+                  struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  struct ospf6_neighbor *on;
+  char *p;
+  struct ospf6_lsa *his, *mine;
+  struct ospf6_lsdb *lsdb = NULL;
+
+  assert (oh->type == OSPF6_MESSAGE_TYPE_LSACK);
+
+  on = ospf6_neighbor_lookup (oh->router_id, oi);
+  if (on == NULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor not found, ignore");
+      return;
+    }
+
+  if (on->state != OSPF6_NEIGHBOR_EXCHANGE &&
+      on->state != OSPF6_NEIGHBOR_LOADING &&
+      on->state != OSPF6_NEIGHBOR_FULL)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Neighbor state less than Exchange, ignore");
+      return;
+    }
+
+  for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header));
+       p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh);
+       p += sizeof (struct ospf6_lsa_header))
+    {
+      his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p);
+
+      switch (OSPF6_LSA_SCOPE (his->header->type))
+        {
+        case OSPF6_SCOPE_LINKLOCAL:
+          lsdb = on->ospf6_if->lsdb;
+          break;
+        case OSPF6_SCOPE_AREA:
+          lsdb = on->ospf6_if->area->lsdb;
+          break;
+        case OSPF6_SCOPE_AS:
+          lsdb = on->ospf6_if->area->ospf6->lsdb;
+          break;
+        case OSPF6_SCOPE_RESERVED:
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Ignoring LSA of reserved scope");
+          ospf6_lsa_delete (his);
+          continue;
+          break;
+        }
+
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("%s acknowledged by %s", his->name, on->name);
+
+      /* Find database copy */
+      mine = ospf6_lsdb_lookup (his->header->type, his->header->id,
+                                his->header->adv_router, lsdb);
+      if (mine == NULL)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("No database copy");
+          ospf6_lsa_delete (his);
+          continue;
+        }
+
+      /* Check if the LSA is on his retrans-list */
+      mine = ospf6_lsdb_lookup (his->header->type, his->header->id,
+                                his->header->adv_router, on->retrans_list);
+      if (mine == NULL)
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Not on %s's retrans-list", on->name);
+          ospf6_lsa_delete (his);
+          continue;
+        }
+
+      if (ospf6_lsa_compare (his, mine) != 0)
+        {
+          /* Log this questionable acknowledgement,
+             and examine the next one. */
+          if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+            zlog_debug ("Questionable acknowledgement");
+          ospf6_lsa_delete (his);
+          continue;
+        }
+
+      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+        zlog_debug ("Acknowledged, remove from %s's retrans-list",
+                   on->name);
+
+      ospf6_decrement_retrans_count (mine);
+      if (OSPF6_LSA_IS_MAXAGE (mine))
+        ospf6_maxage_remove (on->ospf6_if->area->ospf6);
+      ospf6_lsdb_remove (mine, on->retrans_list);
+      ospf6_lsa_delete (his);
+    }
+
+  assert (p == OSPF6_MESSAGE_END (oh));
+}
+
+static u_char *recvbuf = NULL;
+static u_char *sendbuf = NULL;
+static unsigned int iobuflen = 0;
+
+int
+ospf6_iobuf_size (unsigned int size)
+{
+  u_char *recvnew, *sendnew;
+
+  if (size <= iobuflen)
+    return iobuflen;
+
+  recvnew = XMALLOC (MTYPE_OSPF6_MESSAGE, size);
+  sendnew = XMALLOC (MTYPE_OSPF6_MESSAGE, size);
+  if (recvnew == NULL || sendnew == NULL)
+    {
+      if (recvnew)
+        XFREE (MTYPE_OSPF6_MESSAGE, recvnew);
+      if (sendnew)
+        XFREE (MTYPE_OSPF6_MESSAGE, sendnew);
+      zlog_debug ("Could not allocate I/O buffer of size %d.", size);
+      return iobuflen;
+    }
+
+  if (recvbuf)
+    XFREE (MTYPE_OSPF6_MESSAGE, recvbuf);
+  if (sendbuf)
+    XFREE (MTYPE_OSPF6_MESSAGE, sendbuf);
+  recvbuf = recvnew;
+  sendbuf = sendnew;
+  iobuflen = size;
+
+  return iobuflen;
+}
+
+void
+ospf6_message_terminate (void)
+{
+  if (recvbuf)
+    {
+      XFREE (MTYPE_OSPF6_MESSAGE, recvbuf);
+      recvbuf = NULL;
+    }
+
+  if (sendbuf)
+    {
+      XFREE (MTYPE_OSPF6_MESSAGE, sendbuf);
+      sendbuf = NULL;
+    }
+
+  iobuflen = 0;
+}
+
+int
+ospf6_receive (struct thread *thread)
+{
+  int sockfd;
+  unsigned int len;
+  char srcname[64], dstname[64];
+  struct in6_addr src, dst;
+  ifindex_t ifindex;
+  struct iovec iovector[2];
+  struct ospf6_interface *oi;
+  struct ospf6_header *oh;
+
+  /* add next read thread */
+  sockfd = THREAD_FD (thread);
+  thread_add_read (master, ospf6_receive, NULL, sockfd);
+
+  /* initialize */
+  memset (&src, 0, sizeof (src));
+  memset (&dst, 0, sizeof (dst));
+  ifindex = 0;
+  memset (recvbuf, 0, iobuflen);
+  iovector[0].iov_base = recvbuf;
+  iovector[0].iov_len = iobuflen;
+  iovector[1].iov_base = NULL;
+  iovector[1].iov_len = 0;
+
+  /* receive message */
+  len = ospf6_recvmsg (&src, &dst, &ifindex, iovector);
+  if (len > iobuflen)
+    {
+      zlog_err ("Excess message read");
+      return 0;
+    }
+
+  oi = ospf6_interface_lookup_by_ifindex (ifindex);
+  if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE))
+    {
+      zlog_debug ("Message received on disabled interface");
+      return 0;
+    }
+  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE))
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+        zlog_debug ("%s: Ignore message on passive interface %s",
+                    __func__, oi->interface->name);
+      return 0;
+    }
+
+  oh = (struct ospf6_header *) recvbuf;
+  if (ospf6_rxpacket_examin (oi, oh, len) != MSG_OK)
+    return 0;
+
+  /* Being here means, that no sizing/alignment issues were detected in
+     the input packet. This renders the additional checks performed below
+     and also in the type-specific dispatching functions a dead code,
+     which can be dismissed in a cleanup-focused review round later. */
+
+  /* Log */
+  if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV))
+    {
+      inet_ntop (AF_INET6, &src, srcname, sizeof (srcname));
+      inet_ntop (AF_INET6, &dst, dstname, sizeof (dstname));
+      zlog_debug ("%s received on %s",
+                 LOOKUP (ospf6_message_type_str, oh->type), oi->interface->name);
+      zlog_debug ("    src: %s", srcname);
+      zlog_debug ("    dst: %s", dstname);
+
+      switch (oh->type)
+        {
+          case OSPF6_MESSAGE_TYPE_HELLO:
+            ospf6_hello_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_DBDESC:
+            ospf6_dbdesc_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSREQ:
+            ospf6_lsreq_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSUPDATE:
+            ospf6_lsupdate_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSACK:
+            ospf6_lsack_print (oh);
+            break;
+          default:
+            assert (0);
+        }
+    }
+
+  switch (oh->type)
+    {
+      case OSPF6_MESSAGE_TYPE_HELLO:
+        ospf6_hello_recv (&src, &dst, oi, oh);
+        break;
+
+      case OSPF6_MESSAGE_TYPE_DBDESC:
+        ospf6_dbdesc_recv (&src, &dst, oi, oh);
+        break;
+
+      case OSPF6_MESSAGE_TYPE_LSREQ:
+        ospf6_lsreq_recv (&src, &dst, oi, oh);
+        break;
+
+      case OSPF6_MESSAGE_TYPE_LSUPDATE:
+        ospf6_lsupdate_recv (&src, &dst, oi, oh);
+        break;
+
+      case OSPF6_MESSAGE_TYPE_LSACK:
+        ospf6_lsack_recv (&src, &dst, oi, oh);
+        break;
+
+      default:
+        assert (0);
+    }
+
+  return 0;
+}
+
+static void
+ospf6_send (struct in6_addr *src, struct in6_addr *dst,
+            struct ospf6_interface *oi, struct ospf6_header *oh)
+{
+  unsigned int len;
+  char srcname[64], dstname[64];
+  struct iovec iovector[2];
+
+  /* initialize */
+  iovector[0].iov_base = (caddr_t) oh;
+  iovector[0].iov_len = ntohs (oh->length);
+  iovector[1].iov_base = NULL;
+  iovector[1].iov_len = 0;
+
+  /* fill OSPF header */
+  oh->version = OSPFV3_VERSION;
+  /* message type must be set before */
+  /* message length must be set before */
+  oh->router_id = oi->area->ospf6->router_id;
+  oh->area_id = oi->area->area_id;
+  /* checksum is calculated by kernel */
+  oh->instance_id = oi->instance_id;
+  oh->reserved = 0;
+
+  /* Log */
+  if (IS_OSPF6_DEBUG_MESSAGE (oh->type, SEND))
+    {
+      inet_ntop (AF_INET6, dst, dstname, sizeof (dstname));
+      if (src)
+        inet_ntop (AF_INET6, src, srcname, sizeof (srcname));
+      else
+        memset (srcname, 0, sizeof (srcname));
+      zlog_debug ("%s send on %s",
+                 LOOKUP (ospf6_message_type_str, oh->type), oi->interface->name);
+      zlog_debug ("    src: %s", srcname);
+      zlog_debug ("    dst: %s", dstname);
+
+      switch (oh->type)
+        {
+          case OSPF6_MESSAGE_TYPE_HELLO:
+            ospf6_hello_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_DBDESC:
+            ospf6_dbdesc_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSREQ:
+            ospf6_lsreq_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSUPDATE:
+            ospf6_lsupdate_print (oh);
+            break;
+          case OSPF6_MESSAGE_TYPE_LSACK:
+            ospf6_lsack_print (oh);
+            break;
+          default:
+            zlog_debug ("Unknown message");
+            assert (0);
+            break;
+        }
+    }
+
+  /* send message */
+  len = ospf6_sendmsg (src, dst, &oi->interface->ifindex, iovector);
+  if (len != ntohs (oh->length))
+    zlog_err ("Could not send entire message");
+}
+
+static uint32_t
+ospf6_packet_max(struct ospf6_interface *oi)
+{
+  assert (oi->ifmtu > sizeof (struct ip6_hdr));
+  return oi->ifmtu - (sizeof (struct ip6_hdr));
+}
+
+int
+ospf6_hello_send (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+  struct ospf6_header *oh;
+  struct ospf6_hello *hello;
+  u_char *p;
+  struct listnode *node, *nnode;
+  struct ospf6_neighbor *on;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_send_hello = (struct thread *) NULL;
+
+  if (oi->state <= OSPF6_INTERFACE_DOWN)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_HELLO, SEND))
+        zlog_debug ("Unable to send Hello on down interface %s",
+                   oi->interface->name);
+      return 0;
+    }
+
+  if (iobuflen == 0)
+    {
+      zlog_debug ("Unable to send Hello on interface %s iobuflen is 0",
+                 oi->interface->name);
+      return 0;
+    }
+
+  /* set next thread */
+  oi->thread_send_hello = thread_add_timer (master, ospf6_hello_send,
+                                            oi, oi->hello_interval);
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  hello = (struct ospf6_hello *)((caddr_t) oh + sizeof (struct ospf6_header));
+
+  hello->interface_id = htonl (oi->interface->ifindex);
+  hello->priority = oi->priority;
+  hello->options[0] = oi->area->options[0];
+  hello->options[1] = oi->area->options[1];
+  hello->options[2] = oi->area->options[2];
+  hello->hello_interval = htons (oi->hello_interval);
+  hello->dead_interval = htons (oi->dead_interval);
+  hello->drouter = oi->drouter;
+  hello->bdrouter = oi->bdrouter;
+
+  p = (u_char *)((caddr_t) hello + sizeof (struct ospf6_hello));
+
+  for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on))
+    {
+      if (on->state < OSPF6_NEIGHBOR_INIT)
+        continue;
+
+      if (p - sendbuf + sizeof (u_int32_t) > ospf6_packet_max(oi))
+        {
+          if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_HELLO, SEND))
+            zlog_debug ("sending Hello message: exceeds I/F MTU");
+          break;
+        }
+
+      memcpy (p, &on->router_id, sizeof (u_int32_t));
+      p += sizeof (u_int32_t);
+    }
+
+  oh->type = OSPF6_MESSAGE_TYPE_HELLO;
+  oh->length = htons (p - sendbuf);
+
+  ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh);
+  return 0;
+}
+
+int
+ospf6_dbdesc_send (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_header *oh;
+  struct ospf6_dbdesc *dbdesc;
+  u_char *p;
+  struct ospf6_lsa *lsa;
+  struct in6_addr *dst;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  on->thread_send_dbdesc = (struct thread *) NULL;
+
+  if (on->state < OSPF6_NEIGHBOR_EXSTART)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_DBDESC, SEND))
+        zlog_debug ("Quit to send DbDesc to neighbor %s state %s",
+                   on->name, ospf6_neighbor_state_str[on->state]);
+      return 0;
+    }
+
+  /* set next thread if master */
+  if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
+    on->thread_send_dbdesc =
+      thread_add_timer (master, ospf6_dbdesc_send, on,
+                        on->ospf6_if->rxmt_interval);
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  dbdesc = (struct ospf6_dbdesc *)((caddr_t) oh +
+                                   sizeof (struct ospf6_header));
+
+  /* if this is initial one, initialize sequence number for DbDesc */
+  if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) &&
+      (on->dbdesc_seqnum == 0))
+    {
+      struct timeval tv;
+      if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0)
+        tv.tv_sec = 1;
+      on->dbdesc_seqnum = tv.tv_sec;
+    }
+
+  dbdesc->options[0] = on->ospf6_if->area->options[0];
+  dbdesc->options[1] = on->ospf6_if->area->options[1];
+  dbdesc->options[2] = on->ospf6_if->area->options[2];
+  dbdesc->ifmtu = htons (on->ospf6_if->ifmtu);
+  dbdesc->bits = on->dbdesc_bits;
+  dbdesc->seqnum = htonl (on->dbdesc_seqnum);
+
+  /* if this is not initial one, set LSA headers in dbdesc */
+  p = (u_char *)((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc));
+  if (! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT))
+    {
+      for (lsa = ospf6_lsdb_head (on->dbdesc_list); lsa;
+           lsa = ospf6_lsdb_next (lsa))
+        {
+          ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay);
+
+          /* MTU check */
+          if (p - sendbuf + sizeof (struct ospf6_lsa_header) >
+              ospf6_packet_max(on->ospf6_if))
+            {
+              ospf6_lsdb_lsa_unlock (lsa);
+              break;
+            }
+          memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header));
+          p += sizeof (struct ospf6_lsa_header);
+        }
+    }
+
+  oh->type = OSPF6_MESSAGE_TYPE_DBDESC;
+  oh->length = htons (p - sendbuf);
+
+
+  if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
+    dst = &allspfrouters6;
+  else
+    dst = &on->linklocal_addr;
+
+  ospf6_send (on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh);
+
+  return 0;
+}
+
+int
+ospf6_dbdesc_send_newone (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+  unsigned int size = 0;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  ospf6_lsdb_remove_all (on->dbdesc_list);
+
+  /* move LSAs from summary_list to dbdesc_list (within neighbor structure)
+     so that ospf6_send_dbdesc () can send those LSAs */
+  size = sizeof (struct ospf6_lsa_header) + sizeof (struct ospf6_dbdesc);
+  for (lsa = ospf6_lsdb_head (on->summary_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      if (size + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if))
+        {
+          ospf6_lsdb_lsa_unlock (lsa);
+          break;
+        }
+
+      ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->dbdesc_list);
+      ospf6_lsdb_remove (lsa, on->summary_list);
+      size += sizeof (struct ospf6_lsa_header);
+    }
+
+  if (on->summary_list->count == 0)
+    UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT);
+
+  /* If slave, More bit check must be done here */
+  if (! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT) && /* Slave */
+      ! CHECK_FLAG (on->dbdesc_last.bits, OSPF6_DBDESC_MBIT) &&
+      ! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT))
+    thread_add_event (master, exchange_done, on, 0);
+
+  thread_execute (master, ospf6_dbdesc_send, on, 0);
+  return 0;
+}
+
+int
+ospf6_lsreq_send (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_header *oh;
+  struct ospf6_lsreq_entry *e;
+  u_char *p;
+  struct ospf6_lsa *lsa, *last_req;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  on->thread_send_lsreq = (struct thread *) NULL;
+
+  /* LSReq will be sent only in ExStart or Loading */
+  if (on->state != OSPF6_NEIGHBOR_EXCHANGE &&
+      on->state != OSPF6_NEIGHBOR_LOADING)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSREQ, SEND))
+        zlog_debug ("Quit to send LSReq to neighbor %s state %s",
+                   on->name, ospf6_neighbor_state_str[on->state]);
+      return 0;
+    }
+
+  /* schedule loading_done if request list is empty */
+  if (on->request_list->count == 0)
+    {
+      thread_add_event (master, loading_done, on, 0);
+      return 0;
+    }
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  last_req = NULL;
+
+  /* set Request entries in lsreq */
+  p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header));
+  for (lsa = ospf6_lsdb_head (on->request_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if (p - sendbuf + sizeof (struct ospf6_lsreq_entry) > ospf6_packet_max(on->ospf6_if))
+        {
+          ospf6_lsdb_lsa_unlock (lsa);
+          break;
+        }
+
+      e = (struct ospf6_lsreq_entry *) p;
+      e->type = lsa->header->type;
+      e->id = lsa->header->id;
+      e->adv_router = lsa->header->adv_router;
+      p += sizeof (struct ospf6_lsreq_entry);
+      last_req = lsa;
+    }
+
+  if (last_req != NULL)
+    {
+      if (on->last_ls_req != NULL)
+       {
+         ospf6_lsa_unlock (on->last_ls_req);
+       }
+      ospf6_lsa_lock (last_req);
+      on->last_ls_req = last_req;
+    }
+
+  oh->type = OSPF6_MESSAGE_TYPE_LSREQ;
+  oh->length = htons (p - sendbuf);
+
+  if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
+    ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6,
+              on->ospf6_if, oh);
+  else
+    ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr,
+               on->ospf6_if, oh);
+
+  /* set next thread */
+  if (on->request_list->count != 0)
+    {
+      on->thread_send_lsreq =
+       thread_add_timer (master, ospf6_lsreq_send, on,
+                         on->ospf6_if->rxmt_interval);
+    }
+
+  return 0;
+}
+
+int
+ospf6_lsupdate_send_neighbor (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_header *oh;
+  struct ospf6_lsupdate *lsupdate;
+  u_char *p;
+  int lsa_cnt;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  on->thread_send_lsupdate = (struct thread *) NULL;
+
+  if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
+    zlog_debug ("LSUpdate to neighbor %s", on->name);
+
+  if (on->state < OSPF6_NEIGHBOR_EXCHANGE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
+        zlog_debug ("Quit to send (neighbor state %s)",
+                   ospf6_neighbor_state_str[on->state]);
+      return 0;
+    }
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  lsupdate = (struct ospf6_lsupdate *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+
+  p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate));
+  lsa_cnt = 0;
+
+  /* lsupdate_list lists those LSA which doesn't need to be
+     retransmitted. remove those from the list */
+  for (lsa = ospf6_lsdb_head (on->lsupdate_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header))
+          > ospf6_packet_max(on->ospf6_if))
+       {
+         ospf6_lsdb_lsa_unlock (lsa);
+         break;
+       }
+
+      ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay);
+      memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header));
+      p += OSPF6_LSA_SIZE (lsa->header);
+      lsa_cnt++;
+
+      assert (lsa->lock == 2);
+      ospf6_lsdb_remove (lsa, on->lsupdate_list);
+    }
+
+  if (lsa_cnt)
+    {
+      oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+      oh->length = htons (p - sendbuf);
+      lsupdate->lsa_number = htonl (lsa_cnt);
+
+      if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) ||
+         (on->ospf6_if->state == OSPF6_INTERFACE_DR) ||
+         (on->ospf6_if->state == OSPF6_INTERFACE_BDR))
+       ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6,
+                   on->ospf6_if, oh);
+      else
+       ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr,
+                   on->ospf6_if, oh);
+    }
+
+  /* The addresses used for retransmissions are different from those sent the
+     first time and so we need to separate them here.
+  */
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  lsupdate = (struct ospf6_lsupdate *)
+    ((caddr_t) oh + sizeof (struct ospf6_header));
+  p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate));
+  lsa_cnt = 0;
+
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header))
+          > ospf6_packet_max(on->ospf6_if))
+       {
+         ospf6_lsdb_lsa_unlock (lsa);
+         break;
+       }
+
+      ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay);
+      memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header));
+      p += OSPF6_LSA_SIZE (lsa->header);
+      lsa_cnt++;
+    }
+
+  if (lsa_cnt)
+    {
+      oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+      oh->length = htons (p - sendbuf);
+      lsupdate->lsa_number = htonl (lsa_cnt);
+
+      if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
+       ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6,
+                   on->ospf6_if, oh);
+      else
+       ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr,
+                   on->ospf6_if, oh);
+    }
+
+  if (on->lsupdate_list->count != 0)
+    on->thread_send_lsupdate =
+      thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0);
+  else if (on->retrans_list->count != 0)
+    on->thread_send_lsupdate =
+      thread_add_timer (master, ospf6_lsupdate_send_neighbor, on,
+                       on->ospf6_if->rxmt_interval);
+  return 0;
+}
+
+int
+ospf6_lsupdate_send_interface (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+  struct ospf6_header *oh;
+  struct ospf6_lsupdate *lsupdate;
+  u_char *p;
+  int lsa_cnt;
+  struct ospf6_lsa *lsa;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_send_lsupdate = (struct thread *) NULL;
+
+  if (oi->state <= OSPF6_INTERFACE_WAITING)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
+        zlog_debug ("Quit to send LSUpdate to interface %s state %s",
+                   oi->interface->name, ospf6_interface_state_str[oi->state]);
+      return 0;
+    }
+
+  /* if we have nothing to send, return */
+  if (oi->lsupdate_list->count == 0)
+    return 0;
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+  lsupdate = (struct ospf6_lsupdate *)((caddr_t) oh +
+                                      sizeof (struct ospf6_header));
+
+  p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate));
+  lsa_cnt = 0;
+
+  for (lsa = ospf6_lsdb_head (oi->lsupdate_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if ( (p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE (lsa->header)))
+          > ospf6_packet_max(oi))
+       {
+         ospf6_lsdb_lsa_unlock (lsa);
+         break;
+       }
+
+      ospf6_lsa_age_update_to_send (lsa, oi->transdelay);
+      memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header));
+      p += OSPF6_LSA_SIZE (lsa->header);
+      lsa_cnt++;
+
+      assert (lsa->lock == 2);
+      ospf6_lsdb_remove (lsa, oi->lsupdate_list);
+    }
+
+  if (lsa_cnt)
+    {
+      lsupdate->lsa_number = htonl (lsa_cnt);
+
+      oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+      oh->length = htons (p - sendbuf);
+
+      if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) ||
+         (oi->state == OSPF6_INTERFACE_DR) ||
+         (oi->state == OSPF6_INTERFACE_BDR))
+       ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh);
+      else
+       ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh);
+
+    }
+
+  if (oi->lsupdate_list->count > 0)
+    {
+      oi->thread_send_lsupdate =
+        thread_add_event (master, ospf6_lsupdate_send_interface, oi, 0);
+    }
+
+  return 0;
+}
+
+int
+ospf6_lsack_send_neighbor (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_header *oh;
+  u_char *p;
+  struct ospf6_lsa *lsa;
+  int lsa_cnt = 0;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  on->thread_send_lsack = (struct thread *) NULL;
+
+  if (on->state < OSPF6_NEIGHBOR_EXCHANGE)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSACK, SEND))
+        zlog_debug ("Quit to send LSAck to neighbor %s state %s",
+                   on->name, ospf6_neighbor_state_str[on->state]);
+      return 0;
+    }
+
+  /* if we have nothing to send, return */
+  if (on->lsack_list->count == 0)
+    return 0;
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+
+  p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header));
+
+  for (lsa = ospf6_lsdb_head (on->lsack_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if))
+       {
+         /* if we run out of packet size/space here,
+            better to try again soon. */
+         THREAD_OFF (on->thread_send_lsack);
+         on->thread_send_lsack =
+           thread_add_event (master, ospf6_lsack_send_neighbor, on, 0);
+
+         ospf6_lsdb_lsa_unlock (lsa);
+         break;
+       }
+
+      ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay);
+      memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header));
+      p += sizeof (struct ospf6_lsa_header);
+
+      assert (lsa->lock == 2);
+      ospf6_lsdb_remove (lsa, on->lsack_list);
+      lsa_cnt++;
+    }
+
+  if (lsa_cnt)
+    {
+      oh->type = OSPF6_MESSAGE_TYPE_LSACK;
+      oh->length = htons (p - sendbuf);
+
+      ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr,
+                 on->ospf6_if, oh);
+    }
+
+  if (on->thread_send_lsack == NULL && on->lsack_list->count > 0)
+    {
+      on->thread_send_lsack =
+        thread_add_event (master, ospf6_lsack_send_neighbor, on, 0);
+    }
+
+  return 0;
+}
+
+int
+ospf6_lsack_send_interface (struct thread *thread)
+{
+  struct ospf6_interface *oi;
+  struct ospf6_header *oh;
+  u_char *p;
+  struct ospf6_lsa *lsa;
+  int lsa_cnt = 0;
+
+  oi = (struct ospf6_interface *) THREAD_ARG (thread);
+  oi->thread_send_lsack = (struct thread *) NULL;
+
+  if (oi->state <= OSPF6_INTERFACE_WAITING)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSACK, SEND))
+        zlog_debug ("Quit to send LSAck to interface %s state %s",
+                   oi->interface->name, ospf6_interface_state_str[oi->state]);
+      return 0;
+    }
+
+  /* if we have nothing to send, return */
+  if (oi->lsack_list->count == 0)
+    return 0;
+
+  memset (sendbuf, 0, iobuflen);
+  oh = (struct ospf6_header *) sendbuf;
+
+  p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header));
+
+  for (lsa = ospf6_lsdb_head (oi->lsack_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      /* MTU check */
+      if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(oi))
+       {
+         /* if we run out of packet size/space here,
+            better to try again soon. */
+         THREAD_OFF (oi->thread_send_lsack);
+         oi->thread_send_lsack =
+           thread_add_event (master, ospf6_lsack_send_interface, oi, 0);
+
+         ospf6_lsdb_lsa_unlock (lsa);
+         break;
+       }
+
+      ospf6_lsa_age_update_to_send (lsa, oi->transdelay);
+      memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header));
+      p += sizeof (struct ospf6_lsa_header);
+
+      assert (lsa->lock == 2);
+      ospf6_lsdb_remove (lsa, oi->lsack_list);
+      lsa_cnt++;
+    }
+
+  if (lsa_cnt)
+    {
+      oh->type = OSPF6_MESSAGE_TYPE_LSACK;
+      oh->length = htons (p - sendbuf);
+
+      if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) ||
+         (oi->state == OSPF6_INTERFACE_DR) ||
+         (oi->state == OSPF6_INTERFACE_BDR))
+       ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh);
+      else
+       ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh);
+    }
+
+  if (oi->thread_send_lsack == NULL && oi->lsack_list->count > 0)
+    {
+      oi->thread_send_lsack =
+        thread_add_event (master, ospf6_lsack_send_interface, oi, 0);
+    }
+
+  return 0;
+}
+
+
+/* Commands */
+DEFUN (debug_ospf6_message,
+       debug_ospf6_message_cmd,
+       "debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 message\n"
+       "Debug Unknown message\n"
+       "Debug Hello message\n"
+       "Debug Database Description message\n"
+       "Debug Link State Request message\n"
+       "Debug Link State Update message\n"
+       "Debug Link State Acknowledgement message\n"
+       "Debug All message\n"
+       )
+{
+  unsigned char level = 0;
+  int type = 0;
+  int i;
+
+  assert (argc > 0);
+
+  /* check type */
+  if (! strncmp (argv[0], "u", 1))
+    type = OSPF6_MESSAGE_TYPE_UNKNOWN;
+  else if (! strncmp (argv[0], "h", 1))
+    type = OSPF6_MESSAGE_TYPE_HELLO;
+  else if (! strncmp (argv[0], "d", 1))
+    type = OSPF6_MESSAGE_TYPE_DBDESC;
+  else if (! strncmp (argv[0], "lsr", 3))
+    type = OSPF6_MESSAGE_TYPE_LSREQ;
+  else if (! strncmp (argv[0], "lsu", 3))
+    type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+  else if (! strncmp (argv[0], "lsa", 3))
+    type = OSPF6_MESSAGE_TYPE_LSACK;
+  else if (! strncmp (argv[0], "a", 1))
+    type = OSPF6_MESSAGE_TYPE_ALL;
+
+  if (argc == 1)
+    level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV;
+  else if (! strncmp (argv[1], "s", 1))
+    level = OSPF6_DEBUG_MESSAGE_SEND;
+  else if (! strncmp (argv[1], "r", 1))
+    level = OSPF6_DEBUG_MESSAGE_RECV;
+
+  if (type == OSPF6_MESSAGE_TYPE_ALL)
+    {
+      for (i = 0; i < 6; i++)
+        OSPF6_DEBUG_MESSAGE_ON (i, level);
+    }
+  else
+    OSPF6_DEBUG_MESSAGE_ON (type, level);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf6_message,
+       debug_ospf6_message_sendrecv_cmd,
+       "debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all) (send|recv)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 message\n"
+       "Debug Unknown message\n"
+       "Debug Hello message\n"
+       "Debug Database Description message\n"
+       "Debug Link State Request message\n"
+       "Debug Link State Update message\n"
+       "Debug Link State Acknowledgement message\n"
+       "Debug All message\n"
+       "Debug only sending message\n"
+       "Debug only receiving message\n"
+       )
+
+
+DEFUN (no_debug_ospf6_message,
+       no_debug_ospf6_message_cmd,
+       "no debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 message\n"
+       "Debug Unknown message\n"
+       "Debug Hello message\n"
+       "Debug Database Description message\n"
+       "Debug Link State Request message\n"
+       "Debug Link State Update message\n"
+       "Debug Link State Acknowledgement message\n"
+       "Debug All message\n"
+       )
+{
+  unsigned char level = 0;
+  int type = 0;
+  int i;
+
+  assert (argc > 0);
+
+  /* check type */
+  if (! strncmp (argv[0], "u", 1))
+    type = OSPF6_MESSAGE_TYPE_UNKNOWN;
+  else if (! strncmp (argv[0], "h", 1))
+    type = OSPF6_MESSAGE_TYPE_HELLO;
+  else if (! strncmp (argv[0], "d", 1))
+    type = OSPF6_MESSAGE_TYPE_DBDESC;
+  else if (! strncmp (argv[0], "lsr", 3))
+    type = OSPF6_MESSAGE_TYPE_LSREQ;
+  else if (! strncmp (argv[0], "lsu", 3))
+    type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+  else if (! strncmp (argv[0], "lsa", 3))
+    type = OSPF6_MESSAGE_TYPE_LSACK;
+  else if (! strncmp (argv[0], "a", 1))
+    type = OSPF6_MESSAGE_TYPE_ALL;
+
+  if (argc == 1)
+    level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV;
+  else if (! strncmp (argv[1], "s", 1))
+    level = OSPF6_DEBUG_MESSAGE_SEND;
+  else if (! strncmp (argv[1], "r", 1))
+    level = OSPF6_DEBUG_MESSAGE_RECV;
+
+  if (type == OSPF6_MESSAGE_TYPE_ALL)
+    {
+      for (i = 0; i < 6; i++)
+        OSPF6_DEBUG_MESSAGE_OFF (i, level);
+    }
+  else
+    OSPF6_DEBUG_MESSAGE_OFF (type, level);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf6_message,
+       no_debug_ospf6_message_sendrecv_cmd,
+       "no debug ospf6 message "
+       "(unknown|hello|dbdesc|lsreq|lsupdate|lsack|all) (send|recv)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 message\n"
+       "Debug Unknown message\n"
+       "Debug Hello message\n"
+       "Debug Database Description message\n"
+       "Debug Link State Request message\n"
+       "Debug Link State Update message\n"
+       "Debug Link State Acknowledgement message\n"
+       "Debug All message\n"
+       "Debug only sending message\n"
+       "Debug only receiving message\n"
+       )
+
+int
+config_write_ospf6_debug_message (struct vty *vty)
+{
+  const char *type_str[] = {"unknown", "hello", "dbdesc",
+                      "lsreq", "lsupdate", "lsack"};
+  unsigned char s = 0, r = 0;
+  int i;
+
+  for (i = 0; i < 6; i++)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (i, SEND))
+        s |= 1 << i;
+      if (IS_OSPF6_DEBUG_MESSAGE (i, RECV))
+        r |= 1 << i;
+    }
+
+  if (s == 0x3f && r == 0x3f)
+    {
+      vty_out (vty, "debug ospf6 message all%s", VNL);
+      return 0;
+    }
+
+  if (s == 0x3f && r == 0)
+    {
+      vty_out (vty, "debug ospf6 message all send%s", VNL);
+      return 0;
+    }
+  else if (s == 0 && r == 0x3f)
+    {
+      vty_out (vty, "debug ospf6 message all recv%s", VNL);
+      return 0;
+    }
+
+  /* Unknown message is logged by default */
+  if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, SEND) &&
+      ! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+    vty_out (vty, "no debug ospf6 message unknown%s", VNL);
+  else if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, SEND))
+    vty_out (vty, "no debug ospf6 message unknown send%s", VNL);
+  else if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
+    vty_out (vty, "no debug ospf6 message unknown recv%s", VNL);
+
+  for (i = 1; i < 6; i++)
+    {
+      if (IS_OSPF6_DEBUG_MESSAGE (i, SEND) &&
+          IS_OSPF6_DEBUG_MESSAGE (i, RECV))
+        vty_out (vty, "debug ospf6 message %s%s", type_str[i], VNL);
+      else if (IS_OSPF6_DEBUG_MESSAGE (i, SEND))
+        vty_out (vty, "debug ospf6 message %s send%s", type_str[i],
+                 VNL);
+      else if (IS_OSPF6_DEBUG_MESSAGE (i, RECV))
+        vty_out (vty, "debug ospf6 message %s recv%s", type_str[i],
+                 VNL);
+    }
+
+  return 0;
+}
+
+void
+install_element_ospf6_debug_message (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_message_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_message_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_message_sendrecv_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_message_sendrecv_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_message_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_message_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_message_sendrecv_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_message_sendrecv_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h
new file mode 100644 (file)
index 0000000..b085a96
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 1999-2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_MESSAGE_H
+#define OSPF6_MESSAGE_H
+
+#define OSPF6_MESSAGE_BUFSIZ  4096
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_message[];
+#define OSPF6_DEBUG_MESSAGE_SEND 0x01
+#define OSPF6_DEBUG_MESSAGE_RECV 0x02
+#define OSPF6_DEBUG_MESSAGE_ON(type, level) \
+  (conf_debug_ospf6_message[type] |= (level))
+#define OSPF6_DEBUG_MESSAGE_OFF(type, level) \
+  (conf_debug_ospf6_message[type] &= ~(level))
+#define IS_OSPF6_DEBUG_MESSAGE(t, e) \
+  (conf_debug_ospf6_message[t] & OSPF6_DEBUG_MESSAGE_ ## e)
+
+/* Type */
+#define OSPF6_MESSAGE_TYPE_UNKNOWN  0x0
+#define OSPF6_MESSAGE_TYPE_HELLO    0x1  /* Discover/maintain neighbors */
+#define OSPF6_MESSAGE_TYPE_DBDESC   0x2  /* Summarize database contents */
+#define OSPF6_MESSAGE_TYPE_LSREQ    0x3  /* Database download request */
+#define OSPF6_MESSAGE_TYPE_LSUPDATE 0x4  /* Database update */
+#define OSPF6_MESSAGE_TYPE_LSACK    0x5  /* Flooding acknowledgment */
+#define OSPF6_MESSAGE_TYPE_ALL      0x6  /* For debug option */
+
+/* OSPFv3 packet header */
+#define OSPF6_HEADER_SIZE                     16U
+struct ospf6_header
+{
+  u_char    version;
+  u_char    type;
+  u_int16_t length;
+  u_int32_t router_id;
+  u_int32_t area_id;
+  u_int16_t checksum;
+  u_char    instance_id;
+  u_char    reserved;
+};
+
+#define OSPF6_MESSAGE_END(H) ((caddr_t) (H) + ntohs ((H)->length))
+
+/* Hello */
+#define OSPF6_HELLO_MIN_SIZE                  20U
+struct ospf6_hello
+{
+  u_int32_t interface_id;
+  u_char    priority;
+  u_char    options[3];
+  u_int16_t hello_interval;
+  u_int16_t dead_interval;
+  u_int32_t drouter;
+  u_int32_t bdrouter;
+  /* Followed by Router-IDs */
+};
+
+/* Database Description */
+#define OSPF6_DB_DESC_MIN_SIZE                12U
+struct ospf6_dbdesc
+{
+  u_char    reserved1;
+  u_char    options[3];
+  u_int16_t ifmtu;
+  u_char    reserved2;
+  u_char    bits;
+  u_int32_t seqnum;
+  /* Followed by LSA Headers */
+};
+
+#define OSPF6_DBDESC_MSBIT (0x01) /* master/slave bit */
+#define OSPF6_DBDESC_MBIT  (0x02) /* more bit */
+#define OSPF6_DBDESC_IBIT  (0x04) /* initial bit */
+
+/* Link State Request */
+#define OSPF6_LS_REQ_MIN_SIZE                  0U
+/* It is just a sequence of entries below */
+#define OSPF6_LSREQ_LSDESC_FIX_SIZE           12U
+struct ospf6_lsreq_entry
+{
+  u_int16_t reserved;     /* Must Be Zero */
+  u_int16_t type;         /* LS type */
+  u_int32_t id;           /* Link State ID */
+  u_int32_t adv_router;   /* Advertising Router */
+};
+
+/* Link State Update */
+#define OSPF6_LS_UPD_MIN_SIZE                  4U
+struct ospf6_lsupdate
+{
+  u_int32_t lsa_number;
+  /* Followed by LSAs */
+};
+
+/* Link State Acknowledgement */
+#define OSPF6_LS_ACK_MIN_SIZE                  0U
+/* It is just a sequence of LSA Headers */
+
+/* Function definition */
+extern void ospf6_hello_print (struct ospf6_header *);
+extern void ospf6_dbdesc_print (struct ospf6_header *);
+extern void ospf6_lsreq_print (struct ospf6_header *);
+extern void ospf6_lsupdate_print (struct ospf6_header *);
+extern void ospf6_lsack_print (struct ospf6_header *);
+
+extern int ospf6_iobuf_size (unsigned int size);
+extern void ospf6_message_terminate (void);
+extern int ospf6_receive (struct thread *thread);
+
+extern int ospf6_hello_send (struct thread *thread);
+extern int ospf6_dbdesc_send (struct thread *thread);
+extern int ospf6_dbdesc_send_newone (struct thread *thread);
+extern int ospf6_lsreq_send (struct thread *thread);
+extern int ospf6_lsupdate_send_interface (struct thread *thread);
+extern int ospf6_lsupdate_send_neighbor (struct thread *thread);
+extern int ospf6_lsack_send_interface (struct thread *thread);
+extern int ospf6_lsack_send_neighbor (struct thread *thread);
+
+extern int config_write_ospf6_debug_message (struct vty *);
+extern void install_element_ospf6_debug_message (void);
+
+#endif /* OSPF6_MESSAGE_H */
+
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
new file mode 100644 (file)
index 0000000..fbad62a
--- /dev/null
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "thread.h"
+#include "linklist.h"
+#include "vty.h"
+#include "command.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_intra.h"
+#include "ospf6_flood.h"
+#include "ospf6_snmp.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_neighbor = 0;
+
+const char *ospf6_neighbor_state_str[] =
+{ "None", "Down", "Attempt", "Init", "Twoway", "ExStart", "ExChange",
+  "Loading", "Full", NULL };
+
+static const char *ospf6_neighbor_event_str[] =
+  {
+    "NoEvent",
+    "HelloReceived",
+    "2-WayReceived",
+    "NegotiationDone",
+    "ExchangeDone",
+    "LoadingDone",
+    "AdjOK?",
+    "SeqNumberMismatch",
+    "BadLSReq",
+    "1-WayReceived",
+    "InactivityTimer",
+  };
+
+static const char *
+ospf6_neighbor_event_string (int event)
+{
+  #define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent"
+
+  if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT)
+      return ospf6_neighbor_event_str[event];
+  return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING;
+}
+
+int
+ospf6_neighbor_cmp (void *va, void *vb)
+{
+  struct ospf6_neighbor *ona = (struct ospf6_neighbor *) va;
+  struct ospf6_neighbor *onb = (struct ospf6_neighbor *) vb;
+  return (ntohl (ona->router_id) < ntohl (onb->router_id) ? -1 : 1);
+}
+
+struct ospf6_neighbor *
+ospf6_neighbor_lookup (u_int32_t router_id,
+                       struct ospf6_interface *oi)
+{
+  struct listnode *n;
+  struct ospf6_neighbor *on;
+
+  for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, n, on))
+    if (on->router_id == router_id)
+      return on;
+  
+  return (struct ospf6_neighbor *) NULL;
+}
+
+/* create ospf6_neighbor */
+struct ospf6_neighbor *
+ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi)
+{
+  struct ospf6_neighbor *on;
+  char buf[16];
+
+  on = (struct ospf6_neighbor *)
+    XMALLOC (MTYPE_OSPF6_NEIGHBOR, sizeof (struct ospf6_neighbor));
+  if (on == NULL)
+    {
+      zlog_warn ("neighbor: malloc failed");
+      return NULL;
+    }
+
+  memset (on, 0, sizeof (struct ospf6_neighbor));
+  inet_ntop (AF_INET, &router_id, buf, sizeof (buf));
+  snprintf (on->name, sizeof (on->name), "%s%%%s",
+            buf, oi->interface->name);
+  on->ospf6_if = oi;
+  on->state = OSPF6_NEIGHBOR_DOWN;
+  on->state_change = 0;
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed);
+  on->router_id = router_id;
+
+  on->summary_list = ospf6_lsdb_create (on);
+  on->request_list = ospf6_lsdb_create (on);
+  on->retrans_list = ospf6_lsdb_create (on);
+
+  on->dbdesc_list = ospf6_lsdb_create (on);
+  on->lsupdate_list = ospf6_lsdb_create (on);
+  on->lsack_list = ospf6_lsdb_create (on);
+
+  listnode_add_sort (oi->neighbor_list, on);
+  return on;
+}
+
+void
+ospf6_neighbor_delete (struct ospf6_neighbor *on)
+{
+  struct ospf6_lsa *lsa;
+
+  ospf6_lsdb_remove_all (on->summary_list);
+  ospf6_lsdb_remove_all (on->request_list);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      ospf6_decrement_retrans_count (lsa);
+      ospf6_lsdb_remove (lsa, on->retrans_list);
+    }
+
+  ospf6_lsdb_remove_all (on->dbdesc_list);
+  ospf6_lsdb_remove_all (on->lsupdate_list);
+  ospf6_lsdb_remove_all (on->lsack_list);
+
+  ospf6_lsdb_delete (on->summary_list);
+  ospf6_lsdb_delete (on->request_list);
+  ospf6_lsdb_delete (on->retrans_list);
+
+  ospf6_lsdb_delete (on->dbdesc_list);
+  ospf6_lsdb_delete (on->lsupdate_list);
+  ospf6_lsdb_delete (on->lsack_list);
+
+  THREAD_OFF (on->inactivity_timer);
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  THREAD_OFF (on->thread_send_lsreq);
+  THREAD_OFF (on->thread_send_lsupdate);
+  THREAD_OFF (on->thread_send_lsack);
+
+  XFREE (MTYPE_OSPF6_NEIGHBOR, on);
+}
+
+static void
+ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on, int event)
+{
+  u_char prev_state;
+
+  prev_state = on->state;
+  on->state = next_state;
+
+  if (prev_state == next_state)
+    return;
+
+  on->state_change++;
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed);
+
+  /* log */
+  if (IS_OSPF6_DEBUG_NEIGHBOR (STATE))
+    {
+      zlog_debug ("Neighbor state change %s: [%s]->[%s] (%s)", on->name,
+                 ospf6_neighbor_state_str[prev_state],
+                 ospf6_neighbor_state_str[next_state],
+                 ospf6_neighbor_event_string(event));
+    }
+
+  /* Optionally notify about adjacency changes */
+  if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags,
+                OSPF6_LOG_ADJACENCY_CHANGES) &&
+      (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags,
+                 OSPF6_LOG_ADJACENCY_DETAIL) ||
+       (next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state)))
+    zlog_notice("AdjChg: Nbr %s: %s -> %s (%s)", on->name,
+               ospf6_neighbor_state_str[prev_state],
+               ospf6_neighbor_state_str[next_state],
+               ospf6_neighbor_event_string(event));
+
+  if (prev_state == OSPF6_NEIGHBOR_FULL || next_state == OSPF6_NEIGHBOR_FULL)
+    {
+      OSPF6_ROUTER_LSA_SCHEDULE (on->ospf6_if->area);
+      if (on->ospf6_if->state == OSPF6_INTERFACE_DR)
+        {
+          OSPF6_NETWORK_LSA_SCHEDULE (on->ospf6_if);
+          OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (on->ospf6_if);
+        }
+      OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (on->ospf6_if->area);
+    }
+
+  if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE ||
+       prev_state == OSPF6_NEIGHBOR_LOADING) &&
+      (next_state != OSPF6_NEIGHBOR_EXCHANGE &&
+       next_state != OSPF6_NEIGHBOR_LOADING))
+    ospf6_maxage_remove (on->ospf6_if->area->ospf6);
+
+#ifdef HAVE_SNMP
+  /* Terminal state or regression */ 
+  if ((next_state == OSPF6_NEIGHBOR_FULL)  ||
+      (next_state == OSPF6_NEIGHBOR_TWOWAY) ||
+      (next_state < prev_state))
+    ospf6TrapNbrStateChange (on);
+#endif
+
+}
+
+/* RFC2328 section 10.4 */
+static int
+need_adjacency (struct ospf6_neighbor *on)
+{
+  if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT ||
+      on->ospf6_if->state == OSPF6_INTERFACE_DR ||
+      on->ospf6_if->state == OSPF6_INTERFACE_BDR)
+    return 1;
+
+  if (on->ospf6_if->drouter == on->router_id ||
+      on->ospf6_if->bdrouter == on->router_id)
+    return 1;
+
+  return 0;
+}
+
+int
+hello_received (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *HelloReceived*", on->name);
+
+  /* reset Inactivity Timer */
+  THREAD_OFF (on->inactivity_timer);
+  on->inactivity_timer = thread_add_timer (master, inactivity_timer, on,
+                                           on->ospf6_if->dead_interval);
+
+  if (on->state <= OSPF6_NEIGHBOR_DOWN)
+    ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on,
+                                OSPF6_NEIGHBOR_EVENT_HELLO_RCVD);
+
+  return 0;
+}
+
+int
+twoway_received (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state > OSPF6_NEIGHBOR_INIT)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *2Way-Received*", on->name);
+
+  thread_add_event (master, neighbor_change, on->ospf6_if, 0);
+
+  if (! need_adjacency (on))
+    {
+      ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on,
+                                  OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD);
+      return 0;
+    }
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on,
+                              OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT);
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  on->thread_send_dbdesc =
+    thread_add_event (master, ospf6_dbdesc_send, on, 0);
+
+  return 0;
+}
+
+int
+negotiation_done (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state != OSPF6_NEIGHBOR_EXSTART)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *NegotiationDone*", on->name);
+
+  /* clear ls-list */
+  ospf6_lsdb_remove_all (on->summary_list);
+  ospf6_lsdb_remove_all (on->request_list);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      ospf6_decrement_retrans_count (lsa);
+      ospf6_lsdb_remove (lsa, on->retrans_list);
+    }
+
+  /* Interface scoped LSAs */
+  for (lsa = ospf6_lsdb_head (on->ospf6_if->lsdb); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      if (OSPF6_LSA_IS_MAXAGE (lsa))
+        {
+          ospf6_increment_retrans_count (lsa);
+          ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list);
+        }
+      else
+        ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list);
+    }
+
+  /* Area scoped LSAs */
+  for (lsa = ospf6_lsdb_head (on->ospf6_if->area->lsdb); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      if (OSPF6_LSA_IS_MAXAGE (lsa))
+        {
+          ospf6_increment_retrans_count (lsa);
+          ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list);
+        }
+      else
+        ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list);
+    }
+
+  /* AS scoped LSAs */
+  for (lsa = ospf6_lsdb_head (on->ospf6_if->area->ospf6->lsdb); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      if (OSPF6_LSA_IS_MAXAGE (lsa))
+        {
+          ospf6_increment_retrans_count (lsa);
+          ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list);
+        }
+      else
+        ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list);
+    }
+
+  UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT);
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on,
+                              OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE);
+
+  return 0;
+}
+
+int
+exchange_done (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state != OSPF6_NEIGHBOR_EXCHANGE)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *ExchangeDone*", on->name);
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  ospf6_lsdb_remove_all (on->dbdesc_list);
+
+/* XXX
+  thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on,
+                    on->ospf6_if->dead_interval);
+*/
+
+  if (on->request_list->count == 0)
+    ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on,
+                                OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE);
+  else
+    {
+      ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on,
+                                  OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE);
+
+      if (on->thread_send_lsreq == NULL)
+       on->thread_send_lsreq =
+         thread_add_event (master, ospf6_lsreq_send, on, 0);
+    }
+
+  return 0;
+}
+
+/* Check loading state. */
+void
+ospf6_check_nbr_loading (struct ospf6_neighbor *on)
+{
+
+  /* RFC2328 Section 10.9: When the neighbor responds to these requests
+     with the proper Link State Update packet(s), the Link state request
+     list is truncated and a new Link State Request packet is sent.
+  */
+  if ((on->state == OSPF6_NEIGHBOR_LOADING) ||
+      (on->state == OSPF6_NEIGHBOR_EXCHANGE))
+    {
+      if (on->request_list->count == 0)
+       thread_add_event (master, loading_done, on, 0);
+      else if (on->last_ls_req == NULL)
+       {
+         if (on->thread_send_lsreq != NULL)
+           THREAD_OFF (on->thread_send_lsreq);
+         on->thread_send_lsreq =
+           thread_add_event (master, ospf6_lsreq_send, on, 0);
+       }
+    }
+}
+
+int
+loading_done (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state != OSPF6_NEIGHBOR_LOADING)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *LoadingDone*", on->name);
+
+  assert (on->request_list->count == 0);
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on,
+                              OSPF6_NEIGHBOR_EVENT_LOADING_DONE);
+
+  return 0;
+}
+
+int
+adj_ok (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *AdjOK?*", on->name);
+
+  if (on->state == OSPF6_NEIGHBOR_TWOWAY && need_adjacency (on))
+    {
+      ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on,
+                                  OSPF6_NEIGHBOR_EVENT_ADJ_OK);
+      SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
+      SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT);
+      SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT);
+
+      THREAD_OFF (on->thread_send_dbdesc);
+      on->thread_send_dbdesc =
+        thread_add_event (master, ospf6_dbdesc_send, on, 0);
+
+    }
+  else if (on->state >= OSPF6_NEIGHBOR_EXSTART &&
+           ! need_adjacency (on))
+    {
+      ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on,
+                                  OSPF6_NEIGHBOR_EVENT_ADJ_OK);
+      ospf6_lsdb_remove_all (on->summary_list);
+      ospf6_lsdb_remove_all (on->request_list);
+      for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+           lsa = ospf6_lsdb_next (lsa))
+        {
+          ospf6_decrement_retrans_count (lsa);
+          ospf6_lsdb_remove (lsa, on->retrans_list);
+        }
+    }
+
+  return 0;
+}
+
+int
+seqnumber_mismatch (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state < OSPF6_NEIGHBOR_EXCHANGE)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *SeqNumberMismatch*", on->name);
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on,
+                              OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT);
+
+  ospf6_lsdb_remove_all (on->summary_list);
+  ospf6_lsdb_remove_all (on->request_list);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      ospf6_decrement_retrans_count (lsa);
+      ospf6_lsdb_remove (lsa, on->retrans_list);
+    }
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  on->dbdesc_seqnum++;         /* Incr seqnum as per RFC2328, sec 10.3 */
+
+  on->thread_send_dbdesc =
+    thread_add_event (master, ospf6_dbdesc_send, on, 0);
+
+  return 0;
+}
+
+int
+bad_lsreq (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state < OSPF6_NEIGHBOR_EXCHANGE)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *BadLSReq*", on->name);
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on,
+                              OSPF6_NEIGHBOR_EVENT_BAD_LSREQ);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT);
+  SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT);
+
+  ospf6_lsdb_remove_all (on->summary_list);
+  ospf6_lsdb_remove_all (on->request_list);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      ospf6_decrement_retrans_count (lsa);
+      ospf6_lsdb_remove (lsa, on->retrans_list);
+    }
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  on->dbdesc_seqnum++;         /* Incr seqnum as per RFC2328, sec 10.3 */
+
+  on->thread_send_dbdesc =
+    thread_add_event (master, ospf6_dbdesc_send, on, 0);
+
+  return 0;
+}
+
+int
+oneway_received (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_lsa *lsa;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (on->state < OSPF6_NEIGHBOR_TWOWAY)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *1Way-Received*", on->name);
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on,
+                              OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD);
+  thread_add_event (master, neighbor_change, on->ospf6_if, 0);
+
+  ospf6_lsdb_remove_all (on->summary_list);
+  ospf6_lsdb_remove_all (on->request_list);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    {
+      ospf6_decrement_retrans_count (lsa);
+      ospf6_lsdb_remove (lsa, on->retrans_list);
+    }
+
+  THREAD_OFF (on->thread_send_dbdesc);
+  THREAD_OFF (on->thread_send_lsreq);
+  THREAD_OFF (on->thread_send_lsupdate);
+  THREAD_OFF (on->thread_send_lsack);
+
+  return 0;
+}
+
+int
+inactivity_timer (struct thread *thread)
+{
+  struct ospf6_neighbor *on;
+
+  on = (struct ospf6_neighbor *) THREAD_ARG (thread);
+  assert (on);
+
+  if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    zlog_debug ("Neighbor Event %s: *InactivityTimer*", on->name);
+
+  on->inactivity_timer = NULL;
+  on->drouter = on->prev_drouter = 0;
+  on->bdrouter = on->prev_bdrouter = 0;
+
+  ospf6_neighbor_state_change (OSPF6_NEIGHBOR_DOWN, on,
+                              OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER);
+  thread_add_event (master, neighbor_change, on->ospf6_if, 0);
+
+  listnode_delete (on->ospf6_if->neighbor_list, on);
+  ospf6_neighbor_delete (on);
+
+  return 0;
+}
+
+
+
+/* vty functions */
+/* show neighbor structure */
+static void
+ospf6_neighbor_show (struct vty *vty, struct ospf6_neighbor *on)
+{
+  char router_id[16];
+  char duration[16];
+  struct timeval now, res;
+  char nstate[16];
+  char deadtime[16];
+  long h, m, s;
+
+  /* Router-ID (Name) */
+  inet_ntop (AF_INET, &on->router_id, router_id, sizeof (router_id));
+#ifdef HAVE_GETNAMEINFO
+  {
+  }
+#endif /*HAVE_GETNAMEINFO*/
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+
+  /* Dead time */
+  h = m = s = 0;
+  if (on->inactivity_timer)
+    {
+      s = on->inactivity_timer->u.sands.tv_sec - recent_relative_time().tv_sec;
+      h = s / 3600;
+      s -= h * 3600;
+      m = s / 60;
+      s -= m * 60;
+    }
+  snprintf (deadtime, sizeof (deadtime), "%02ld:%02ld:%02ld", h, m, s);
+
+  /* Neighbor State */
+  if (if_is_pointopoint (on->ospf6_if->interface))
+    snprintf (nstate, sizeof (nstate), "PointToPoint");
+  else
+    {
+      if (on->router_id == on->drouter)
+        snprintf (nstate, sizeof (nstate), "DR");
+      else if (on->router_id == on->bdrouter)
+        snprintf (nstate, sizeof (nstate), "BDR");
+      else
+        snprintf (nstate, sizeof (nstate), "DROther");
+    }
+
+  /* Duration */
+  timersub (&now, &on->last_changed, &res);
+  timerstring (&res, duration, sizeof (duration));
+
+  /*
+  vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]%s",
+           "Neighbor ID", "Pri", "DeadTime", "State", "", "Duration",
+           "I/F", "State", VNL);
+  */
+
+  vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]%s",
+           router_id, on->priority, deadtime,
+           ospf6_neighbor_state_str[on->state], nstate, duration,
+           on->ospf6_if->interface->name,
+           ospf6_interface_state_str[on->ospf6_if->state], VNL);
+}
+
+static void
+ospf6_neighbor_show_drchoice (struct vty *vty, struct ospf6_neighbor *on)
+{
+  char router_id[16];
+  char drouter[16], bdrouter[16];
+  char duration[16];
+  struct timeval now, res;
+
+/*
+    vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s",
+             "RouterID", "State", "Duration", "DR", "BDR", "I/F",
+             "State", VNL);
+*/
+
+  inet_ntop (AF_INET, &on->router_id, router_id, sizeof (router_id));
+  inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter));
+  inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter));
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &on->last_changed, &res);
+  timerstring (&res, duration, sizeof (duration));
+
+  vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s",
+           router_id, ospf6_neighbor_state_str[on->state],
+           duration, drouter, bdrouter, on->ospf6_if->interface->name,
+           ospf6_interface_state_str[on->ospf6_if->state],
+           VNL);
+}
+
+static void
+ospf6_neighbor_show_detail (struct vty *vty, struct ospf6_neighbor *on)
+{
+  char drouter[16], bdrouter[16];
+  char linklocal_addr[64], duration[32];
+  struct timeval now, res;
+  struct ospf6_lsa *lsa;
+
+  inet_ntop (AF_INET6, &on->linklocal_addr, linklocal_addr,
+             sizeof (linklocal_addr));
+  inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter));
+  inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter));
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &on->last_changed, &res);
+  timerstring (&res, duration, sizeof (duration));
+
+  vty_out (vty, " Neighbor %s%s", on->name,
+           VNL);
+  vty_out (vty, "    Area %s via interface %s (ifindex %d)%s",
+           on->ospf6_if->area->name,
+           on->ospf6_if->interface->name,
+           on->ospf6_if->interface->ifindex,
+           VNL);
+  vty_out (vty, "    His IfIndex: %d Link-local address: %s%s",
+           on->ifindex, linklocal_addr,
+           VNL);
+  vty_out (vty, "    State %s for a duration of %s%s",
+           ospf6_neighbor_state_str[on->state], duration,
+           VNL);
+  vty_out (vty, "    His choice of DR/BDR %s/%s, Priority %d%s",
+           drouter, bdrouter, on->priority,
+           VNL);
+  vty_out (vty, "    DbDesc status: %s%s%s SeqNum: %#lx%s",
+           (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) ? "Initial " : ""),
+           (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More " : ""),
+           (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT) ?
+            "Master" : "Slave"), (u_long) ntohl (on->dbdesc_seqnum),
+           VNL);
+
+  vty_out (vty, "    Summary-List: %d LSAs%s", on->summary_list->count,
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->summary_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  vty_out (vty, "    Request-List: %d LSAs%s", on->request_list->count,
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->request_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  vty_out (vty, "    Retrans-List: %d LSAs%s", on->retrans_list->count,
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->retrans_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  timerclear (&res);
+  if (on->thread_send_dbdesc)
+    timersub (&on->thread_send_dbdesc->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for DbDesc in Time %s [thread %s]%s",
+           on->dbdesc_list->count, duration,
+           (on->thread_send_dbdesc ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->dbdesc_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  timerclear (&res);
+  if (on->thread_send_lsreq)
+    timersub (&on->thread_send_lsreq->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for LSReq in Time %s [thread %s]%s",
+           on->request_list->count, duration,
+           (on->thread_send_lsreq ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->request_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  timerclear (&res);
+  if (on->thread_send_lsupdate)
+    timersub (&on->thread_send_lsupdate->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for LSUpdate in Time %s [thread %s]%s",
+           on->lsupdate_list->count, duration,
+           (on->thread_send_lsupdate ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->lsupdate_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+  timerclear (&res);
+  if (on->thread_send_lsack)
+    timersub (&on->thread_send_lsack->u.sands, &now, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "    %d Pending LSAs for LSAck in Time %s [thread %s]%s",
+           on->lsack_list->count, duration,
+           (on->thread_send_lsack ? "on" : "off"),
+           VNL);
+  for (lsa = ospf6_lsdb_head (on->lsack_list); lsa;
+       lsa = ospf6_lsdb_next (lsa))
+    vty_out (vty, "      %s%s", lsa->name, VNL);
+
+}
+
+DEFUN (show_ipv6_ospf6_neighbor,
+       show_ipv6_ospf6_neighbor_cmd,
+       "show ipv6 ospf6 neighbor",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Neighbor list\n"
+      )
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_interface *oi;
+  struct ospf6_area *oa;
+  struct listnode *i, *j, *k;
+  void (*showfunc) (struct vty *, struct ospf6_neighbor *);
+
+  OSPF6_CMD_CHECK_RUNNING ();
+  showfunc = ospf6_neighbor_show;
+
+  if (argc)
+    {
+      if (! strncmp (argv[0], "de", 2))
+        showfunc = ospf6_neighbor_show_detail;
+      else if (! strncmp (argv[0], "dr", 2))
+        showfunc = ospf6_neighbor_show_drchoice;
+    }
+
+  if (showfunc == ospf6_neighbor_show)
+    vty_out (vty, "%-15s %3s %11s %6s/%-12s %11s %s[%s]%s",
+             "Neighbor ID", "Pri", "DeadTime", "State", "IfState", "Duration",
+             "I/F", "State", VNL);
+  else if (showfunc == ospf6_neighbor_show_drchoice)
+    vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s",
+             "RouterID", "State", "Duration", "DR", "BDR", "I/F",
+             "State", VNL);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, i, oa))
+    for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on))
+        (*showfunc) (vty, on);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_neighbor,
+       show_ipv6_ospf6_neighbor_detail_cmd,
+       "show ipv6 ospf6 neighbor (detail|drchoice)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Neighbor list\n"
+       "Display details\n"
+       "Display DR choices\n"
+      )
+
+DEFUN (show_ipv6_ospf6_neighbor_one,
+       show_ipv6_ospf6_neighbor_one_cmd,
+       "show ipv6 ospf6 neighbor A.B.C.D",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Neighbor list\n"
+       "Specify Router-ID as IPv4 address notation\n"
+      )
+{
+  struct ospf6_neighbor *on;
+  struct ospf6_interface *oi;
+  struct ospf6_area *oa;
+  struct listnode *i, *j, *k;
+  void (*showfunc) (struct vty *, struct ospf6_neighbor *);
+  u_int32_t router_id;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+  showfunc = ospf6_neighbor_show_detail;
+
+  if ((inet_pton (AF_INET, argv[0], &router_id)) != 1)
+    {
+      vty_out (vty, "Router-ID is not parsable: %s%s", argv[0],
+               VNL);
+      return CMD_SUCCESS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, i, oa))
+    for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+      for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on))
+        (*showfunc) (vty, on);
+  
+  return CMD_SUCCESS;
+}
+
+void
+ospf6_neighbor_init (void)
+{
+  install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_detail_cmd);
+}
+
+DEFUN (debug_ospf6_neighbor,
+       debug_ospf6_neighbor_cmd,
+       "debug ospf6 neighbor",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Neighbor\n"
+      )
+{
+  unsigned char level = 0;
+  if (argc)
+    {
+      if (! strncmp (argv[0], "s", 1))
+        level = OSPF6_DEBUG_NEIGHBOR_STATE;
+      if (! strncmp (argv[0], "e", 1))
+        level = OSPF6_DEBUG_NEIGHBOR_EVENT;
+    }
+  else
+    level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT;
+
+  OSPF6_DEBUG_NEIGHBOR_ON (level);
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf6_neighbor,
+       debug_ospf6_neighbor_detail_cmd,
+       "debug ospf6 neighbor (state|event)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Neighbor\n"
+       "Debug OSPFv3 Neighbor State Change\n"
+       "Debug OSPFv3 Neighbor Event\n"
+      )
+
+DEFUN (no_debug_ospf6_neighbor,
+       no_debug_ospf6_neighbor_cmd,
+       "no debug ospf6 neighbor",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Neighbor\n"
+      )
+{
+  unsigned char level = 0;
+  if (argc)
+    {
+      if (! strncmp (argv[0], "s", 1))
+        level = OSPF6_DEBUG_NEIGHBOR_STATE;
+      if (! strncmp (argv[0], "e", 1))
+        level = OSPF6_DEBUG_NEIGHBOR_EVENT;
+    }
+  else
+    level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT;
+
+  OSPF6_DEBUG_NEIGHBOR_OFF (level);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf6_neighbor,
+       no_debug_ospf6_neighbor_detail_cmd,
+       "no debug ospf6 neighbor (state|event)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug OSPFv3 Neighbor\n"
+       "Debug OSPFv3 Neighbor State Change\n"
+       "Debug OSPFv3 Neighbor Event\n"
+      )
+
+int
+config_write_ospf6_debug_neighbor (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_NEIGHBOR (STATE) &&
+      IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    vty_out (vty, "debug ospf6 neighbor%s", VNL);
+  else if (IS_OSPF6_DEBUG_NEIGHBOR (STATE))
+    vty_out (vty, "debug ospf6 neighbor state%s", VNL);
+  else if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT))
+    vty_out (vty, "debug ospf6 neighbor event%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_neighbor (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_neighbor_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_neighbor_detail_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_neighbor_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_neighbor_detail_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_neighbor_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_neighbor_detail_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_neighbor_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_neighbor_detail_cmd);
+}
+
+
+
diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h
new file mode 100644 (file)
index 0000000..54b7ffe
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_NEIGHBOR_H
+#define OSPF6_NEIGHBOR_H
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_neighbor;
+#define OSPF6_DEBUG_NEIGHBOR_STATE   0x01
+#define OSPF6_DEBUG_NEIGHBOR_EVENT   0x02
+#define OSPF6_DEBUG_NEIGHBOR_ON(level) \
+  (conf_debug_ospf6_neighbor |= (level))
+#define OSPF6_DEBUG_NEIGHBOR_OFF(level) \
+  (conf_debug_ospf6_neighbor &= ~(level))
+#define IS_OSPF6_DEBUG_NEIGHBOR(level) \
+  (conf_debug_ospf6_neighbor & OSPF6_DEBUG_NEIGHBOR_ ## level)
+
+/* Neighbor structure */
+struct ospf6_neighbor
+{
+  /* Neighbor Router ID String */
+  char name[32];
+
+  /* OSPFv3 Interface this neighbor belongs to */
+  struct ospf6_interface *ospf6_if;
+
+  /* Neighbor state */
+  u_char state;
+
+  /* timestamp of last changing state */
+  u_int32_t state_change;
+  struct timeval last_changed;
+
+  /* Neighbor Router ID */
+  u_int32_t router_id;
+
+  /* Neighbor Interface ID */
+  ifindex_t ifindex;
+
+  /* Router Priority of this neighbor */
+  u_char priority;
+
+  u_int32_t drouter;
+  u_int32_t bdrouter;
+  u_int32_t prev_drouter;
+  u_int32_t prev_bdrouter;
+
+  /* Options field (Capability) */
+  char options[3];
+
+  /* IPaddr of I/F on our side link */
+  struct in6_addr linklocal_addr;
+
+  /* For Database Exchange */
+  u_char               dbdesc_bits;
+  u_int32_t            dbdesc_seqnum;
+  /* Last received Database Description packet */
+  struct ospf6_dbdesc  dbdesc_last;
+
+  /* LS-list */
+  struct ospf6_lsdb *summary_list;
+  struct ospf6_lsdb *request_list;
+  struct ospf6_lsdb *retrans_list;
+
+  /* LSA list for message transmission */
+  struct ospf6_lsdb *dbdesc_list;
+  struct ospf6_lsdb *lsreq_list;
+  struct ospf6_lsdb *lsupdate_list;
+  struct ospf6_lsdb *lsack_list;
+
+  struct ospf6_lsa *last_ls_req;
+
+  /* Inactivity timer */
+  struct thread *inactivity_timer;
+
+  /* Thread for sending message */
+  struct thread *thread_send_dbdesc;
+  struct thread *thread_send_lsreq;
+  struct thread *thread_send_lsupdate;
+  struct thread *thread_send_lsack;
+};
+
+/* Neighbor state */
+#define OSPF6_NEIGHBOR_DOWN     1
+#define OSPF6_NEIGHBOR_ATTEMPT  2
+#define OSPF6_NEIGHBOR_INIT     3
+#define OSPF6_NEIGHBOR_TWOWAY   4
+#define OSPF6_NEIGHBOR_EXSTART  5
+#define OSPF6_NEIGHBOR_EXCHANGE 6
+#define OSPF6_NEIGHBOR_LOADING  7
+#define OSPF6_NEIGHBOR_FULL     8
+
+/* Neighbor Events */
+#define OSPF6_NEIGHBOR_EVENT_NO_EVENT             0
+#define OSPF6_NEIGHBOR_EVENT_HELLO_RCVD           1
+#define OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD          2
+#define OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE     3
+#define OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE        4
+#define OSPF6_NEIGHBOR_EVENT_LOADING_DONE         5
+#define OSPF6_NEIGHBOR_EVENT_ADJ_OK               6
+#define OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH   7
+#define OSPF6_NEIGHBOR_EVENT_BAD_LSREQ            8
+#define OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD          9
+#define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER    10
+#define OSPF6_NEIGHBOR_EVENT_MAX_EVENT           11
+
+extern const char *ospf6_neighbor_state_str[];
+
+/* Function Prototypes */
+int ospf6_neighbor_cmp (void *va, void *vb);
+void ospf6_neighbor_dbex_init (struct ospf6_neighbor *on);
+
+struct ospf6_neighbor *ospf6_neighbor_lookup (u_int32_t,
+                                              struct ospf6_interface *);
+struct ospf6_neighbor *ospf6_neighbor_create (u_int32_t,
+                                              struct ospf6_interface *);
+void ospf6_neighbor_delete (struct ospf6_neighbor *);
+
+/* Neighbor event */
+extern int hello_received (struct thread *);
+extern int twoway_received (struct thread *);
+extern int negotiation_done (struct thread *);
+extern int exchange_done (struct thread *);
+extern int loading_done (struct thread *);
+extern int adj_ok (struct thread *);
+extern int seqnumber_mismatch (struct thread *);
+extern int bad_lsreq (struct thread *);
+extern int oneway_received (struct thread *);
+extern int inactivity_timer (struct thread *);
+extern void ospf6_check_nbr_loading (struct ospf6_neighbor *);
+
+extern void ospf6_neighbor_init (void);
+extern int config_write_ospf6_debug_neighbor (struct vty *vty);
+extern void install_element_ospf6_debug_neighbor (void);
+
+#endif /* OSPF6_NEIGHBOR_H */
diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c
new file mode 100644 (file)
index 0000000..53d6c35
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "privs.h"
+
+#include "libospf.h"
+#include "ospf6_proto.h"
+#include "ospf6_network.h"
+
+extern struct zebra_privs_t ospf6d_privs;
+
+int  ospf6_sock;
+struct in6_addr allspfrouters6;
+struct in6_addr alldrouters6;
+
+/* setsockopt MulticastLoop to off */
+static void
+ospf6_reset_mcastloop (void)
+{
+  u_int off = 0;
+  if (setsockopt (ospf6_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                  &off, sizeof (u_int)) < 0)
+    zlog_warn ("Network: reset IPV6_MULTICAST_LOOP failed: %s",
+               safe_strerror (errno));
+}
+
+static void
+ospf6_set_pktinfo (void)
+{
+  setsockopt_ipv6_pktinfo (ospf6_sock, 1);
+}
+
+static void
+ospf6_set_transport_class (void)
+{
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  setsockopt_ipv6_tclass (ospf6_sock, IPTOS_PREC_INTERNETCONTROL);
+#endif
+}
+
+static void
+ospf6_set_checksum (void)
+{
+  int offset = 12;
+#ifndef DISABLE_IPV6_CHECKSUM
+  if (setsockopt (ospf6_sock, IPPROTO_IPV6, IPV6_CHECKSUM,
+                  &offset, sizeof (offset)) < 0)
+    zlog_warn ("Network: set IPV6_CHECKSUM failed: %s", safe_strerror (errno));
+#else
+  zlog_warn ("Network: Don't set IPV6_CHECKSUM");
+#endif /* DISABLE_IPV6_CHECKSUM */
+}
+
+/* Make ospf6d's server socket. */
+int
+ospf6_serv_sock (void)
+{
+  if (ospf6d_privs.change (ZPRIVS_RAISE))
+    zlog_err ("ospf6_serv_sock: could not raise privs");
+
+  ospf6_sock = socket (AF_INET6, SOCK_RAW, IPPROTO_OSPFIGP);
+  if (ospf6_sock < 0)
+    {
+      zlog_warn ("Network: can't create OSPF6 socket.");
+      if (ospf6d_privs.change (ZPRIVS_LOWER))
+        zlog_err ("ospf_sock_init: could not lower privs");
+      return -1;
+    }
+  if (ospf6d_privs.change (ZPRIVS_LOWER))
+      zlog_err ("ospf_sock_init: could not lower privs");
+
+  /* set socket options */
+#if 1
+  sockopt_reuseaddr (ospf6_sock);
+#else
+  ospf6_set_reuseaddr ();
+#endif /*1*/
+  ospf6_reset_mcastloop ();
+  ospf6_set_pktinfo ();
+  ospf6_set_transport_class ();
+  ospf6_set_checksum ();
+
+  /* setup global in6_addr, allspf6 and alldr6 for later use */
+  inet_pton (AF_INET6, ALLSPFROUTERS6, &allspfrouters6);
+  inet_pton (AF_INET6, ALLDROUTERS6, &alldrouters6);
+
+  return 0;
+}
+
+/* ospf6 set socket option */
+int
+ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option)
+{
+  struct ipv6_mreq mreq6;
+  int ret;
+
+  assert (ifindex);
+  mreq6.ipv6mr_interface = ifindex;
+  memcpy (&mreq6.ipv6mr_multiaddr, group, sizeof (struct in6_addr));
+
+  ret = setsockopt (ospf6_sock, IPPROTO_IPV6, option,
+                    &mreq6, sizeof (mreq6));
+  if (ret < 0)
+    {
+      zlog_err ("Network: setsockopt (%d) on ifindex %d failed: %s",
+                option, ifindex, safe_strerror (errno));
+    }
+
+  return ret;
+}
+
+static int
+iov_count (struct iovec *iov)
+{
+  int i;
+  for (i = 0; iov[i].iov_base; i++)
+    ;
+  return i;
+}
+
+static int
+iov_totallen (struct iovec *iov)
+{
+  int i;
+  int totallen = 0;
+  for (i = 0; iov[i].iov_base; i++)
+    totallen += iov[i].iov_len;
+  return totallen;
+}
+
+int
+ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst,
+               ifindex_t *ifindex, struct iovec *message)
+{
+  int retval;
+  struct msghdr smsghdr;
+  struct cmsghdr *scmsgp;
+  u_char cmsgbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))];
+  struct in6_pktinfo *pktinfo;
+  struct sockaddr_in6 dst_sin6;
+
+  assert (dst);
+  assert (*ifindex);
+
+  scmsgp = (struct cmsghdr *)cmsgbuf;
+  pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
+  memset (&dst_sin6, 0, sizeof (struct sockaddr_in6));
+
+  /* source address */
+  pktinfo->ipi6_ifindex = *ifindex;
+  if (src)
+    memcpy (&pktinfo->ipi6_addr, src, sizeof (struct in6_addr));
+  else
+    memset (&pktinfo->ipi6_addr, 0, sizeof (struct in6_addr));
+
+  /* destination address */
+  dst_sin6.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+  dst_sin6.sin6_len = sizeof (struct sockaddr_in6);
+#endif /*SIN6_LEN*/
+  memcpy (&dst_sin6.sin6_addr, dst, sizeof (struct in6_addr));
+#ifdef HAVE_SIN6_SCOPE_ID
+  dst_sin6.sin6_scope_id = *ifindex;
+#endif
+
+  /* send control msg */
+  scmsgp->cmsg_level = IPPROTO_IPV6;
+  scmsgp->cmsg_type = IPV6_PKTINFO;
+  scmsgp->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo));
+  /* scmsgp = CMSG_NXTHDR (&smsghdr, scmsgp); */
+
+  /* send msg hdr */
+  memset (&smsghdr, 0, sizeof (smsghdr));
+  smsghdr.msg_iov = message;
+  smsghdr.msg_iovlen = iov_count (message);
+  smsghdr.msg_name = (caddr_t) &dst_sin6;
+  smsghdr.msg_namelen = sizeof (struct sockaddr_in6);
+  smsghdr.msg_control = (caddr_t) cmsgbuf;
+  smsghdr.msg_controllen = scmsgp->cmsg_len;
+
+  retval = sendmsg (ospf6_sock, &smsghdr, 0);
+  if (retval != iov_totallen (message))
+    zlog_warn ("sendmsg failed: ifindex: %d: %s (%d)",
+               *ifindex, safe_strerror (errno), errno);
+
+  return retval;
+}
+
+int
+ospf6_recvmsg (struct in6_addr *src, struct in6_addr *dst,
+               ifindex_t *ifindex, struct iovec *message)
+{
+  int retval;
+  struct msghdr rmsghdr;
+  struct cmsghdr *rcmsgp;
+  u_char cmsgbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))];
+  struct in6_pktinfo *pktinfo;
+  struct sockaddr_in6 src_sin6;
+
+  rcmsgp = (struct cmsghdr *)cmsgbuf;
+  pktinfo = (struct in6_pktinfo *)(CMSG_DATA(rcmsgp));
+  memset (&src_sin6, 0, sizeof (struct sockaddr_in6));
+
+  /* receive control msg */
+  rcmsgp->cmsg_level = IPPROTO_IPV6;
+  rcmsgp->cmsg_type = IPV6_PKTINFO;
+  rcmsgp->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo));
+  /* rcmsgp = CMSG_NXTHDR (&rmsghdr, rcmsgp); */
+
+  /* receive msg hdr */
+  memset (&rmsghdr, 0, sizeof (rmsghdr));
+  rmsghdr.msg_iov = message;
+  rmsghdr.msg_iovlen = iov_count (message);
+  rmsghdr.msg_name = (caddr_t) &src_sin6;
+  rmsghdr.msg_namelen = sizeof (struct sockaddr_in6);
+  rmsghdr.msg_control = (caddr_t) cmsgbuf;
+  rmsghdr.msg_controllen = sizeof (cmsgbuf);
+
+  retval = recvmsg (ospf6_sock, &rmsghdr, 0);
+  if (retval < 0)
+    zlog_warn ("recvmsg failed: %s", safe_strerror (errno));
+  else if (retval == iov_totallen (message))
+    zlog_warn ("recvmsg read full buffer size: %d", retval);
+
+  /* source address */
+  assert (src);
+  memcpy (src, &src_sin6.sin6_addr, sizeof (struct in6_addr));
+
+  /* destination address */
+  if (ifindex)
+    *ifindex = pktinfo->ipi6_ifindex;
+  if (dst)
+    memcpy (dst, &pktinfo->ipi6_addr, sizeof (struct in6_addr));
+
+  return retval;
+}
+
+
diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h
new file mode 100644 (file)
index 0000000..4fa2839
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_NETWORK_H
+#define OSPF6_NETWORK_H
+
+
+
+extern int ospf6_sock;
+extern struct in6_addr allspfrouters6;
+extern struct in6_addr alldrouters6;
+
+extern int ospf6_serv_sock (void);
+extern int ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option);
+
+extern int ospf6_sendmsg (struct in6_addr *, struct in6_addr *,
+                          ifindex_t *, struct iovec *);
+extern int ospf6_recvmsg (struct in6_addr *, struct in6_addr *,
+                          ifindex_t *, struct iovec *);
+
+#endif /* OSPF6_NETWORK_H */
+
diff --git a/ospf6d/ospf6_proto.c b/ospf6d/ospf6_proto.c
new file mode 100644 (file)
index 0000000..d011601
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "ospf6_proto.h"
+
+void
+ospf6_prefix_apply_mask (struct ospf6_prefix *op)
+{
+  u_char *pnt, mask;
+  int index, offset;
+
+  pnt = (u_char *)((caddr_t) op + sizeof (struct ospf6_prefix));
+  index = op->prefix_length / 8;
+  offset = op->prefix_length % 8;
+  mask = 0xff << (8 - offset);
+
+  if (index > 16)
+    {
+      zlog_warn ("Prefix length too long: %d", op->prefix_length);
+      return;
+    }
+
+  /* nonzero mask means no check for this byte because if it contains
+   * prefix bits it must be there for us to write */
+  if (mask)
+    pnt[index++] &= mask;
+
+  while (index < OSPF6_PREFIX_SPACE (op->prefix_length))
+    pnt[index++] = 0;
+}
+
+void
+ospf6_prefix_options_printbuf (u_int8_t prefix_options, char *buf, int size)
+{
+  snprintf (buf, size, "xxx");
+}
+
+void
+ospf6_capability_printbuf (char capability, char *buf, int size)
+{
+  char w, v, e, b;
+  w = (capability & OSPF6_ROUTER_BIT_W ? 'W' : '-');
+  v = (capability & OSPF6_ROUTER_BIT_V ? 'V' : '-');
+  e = (capability & OSPF6_ROUTER_BIT_E ? 'E' : '-');
+  b = (capability & OSPF6_ROUTER_BIT_B ? 'B' : '-');
+  snprintf (buf, size, "----%c%c%c%c", w, v, e, b);
+}
+
+void
+ospf6_options_printbuf (u_char *options, char *buf, int size)
+{
+  const char *dc, *r, *n, *mc, *e, *v6;
+  dc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_DC) ? "DC" : "--");
+  r  = (OSPF6_OPT_ISSET (options, OSPF6_OPT_R)  ? "R"  : "-" );
+  n  = (OSPF6_OPT_ISSET (options, OSPF6_OPT_N)  ? "N"  : "-" );
+  mc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_MC) ? "MC" : "--");
+  e  = (OSPF6_OPT_ISSET (options, OSPF6_OPT_E)  ? "E"  : "-" );
+  v6 = (OSPF6_OPT_ISSET (options, OSPF6_OPT_V6) ? "V6" : "--");
+  snprintf (buf, size, "%s|%s|%s|%s|%s|%s", dc, r, n, mc, e, v6);
+}
+
+
diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h
new file mode 100644 (file)
index 0000000..af60eb9
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Common protocol data and data structures.
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_PROTO_H
+#define OSPF6_PROTO_H
+
+/* OSPF protocol version */
+#define OSPFV3_VERSION           3
+
+/* TOS field normaly null */
+#define DEFAULT_TOS_VALUE      0x0
+
+#define ALLSPFROUTERS6 "ff02::5"
+#define ALLDROUTERS6   "ff02::6"
+
+#define OSPF6_ROUTER_BIT_W     (1 << 3)
+#define OSPF6_ROUTER_BIT_V     (1 << 2)
+#define OSPF6_ROUTER_BIT_E     (1 << 1)
+#define OSPF6_ROUTER_BIT_B     (1 << 0)
+
+/* OSPF options */
+/* present in HELLO, DD, LSA */
+#define OSPF6_OPT_SET(x,opt)   ((x)[2] |=  (opt))
+#define OSPF6_OPT_ISSET(x,opt) ((x)[2] &   (opt))
+#define OSPF6_OPT_CLEAR(x,opt) ((x)[2] &= ~(opt))
+#define OSPF6_OPT_CLEAR_ALL(x) ((x)[0] = (x)[1] = (x)[2] = 0)
+
+#define OSPF6_OPT_DC (1 << 5)   /* Demand Circuit handling Capability */
+#define OSPF6_OPT_R  (1 << 4)   /* Forwarding Capability (Any Protocol) */
+#define OSPF6_OPT_N  (1 << 3)   /* Handling Type-7 LSA Capability */
+#define OSPF6_OPT_MC (1 << 2)   /* Multicasting Capability */
+#define OSPF6_OPT_E  (1 << 1)   /* AS External Capability */
+#define OSPF6_OPT_V6 (1 << 0)   /* IPv6 forwarding Capability */
+
+/* OSPF6 Prefix */
+#define OSPF6_PREFIX_MIN_SIZE                  4U /* .length == 0 */
+struct ospf6_prefix
+{
+  u_int8_t prefix_length;
+  u_int8_t prefix_options;
+  union {
+    u_int16_t _prefix_metric;
+    u_int16_t _prefix_referenced_lstype;
+  } u;
+#define prefix_metric        u._prefix_metric
+#define prefix_refer_lstype  u._prefix_referenced_lstype
+  /* followed by one address_prefix */
+};
+
+#define OSPF6_PREFIX_OPTION_NU (1 << 0)  /* No Unicast */
+#define OSPF6_PREFIX_OPTION_LA (1 << 1)  /* Local Address */
+#define OSPF6_PREFIX_OPTION_MC (1 << 2)  /* MultiCast */
+#define OSPF6_PREFIX_OPTION_P  (1 << 3)  /* Propagate (NSSA) */
+
+/* caddr_t OSPF6_PREFIX_BODY (struct ospf6_prefix *); */
+#define OSPF6_PREFIX_BODY(x) ((caddr_t)(x) + sizeof (struct ospf6_prefix))
+
+/* size_t OSPF6_PREFIX_SPACE (int prefixlength); */
+#define OSPF6_PREFIX_SPACE(x) ((((x) + 31) / 32) * 4)
+
+/* size_t OSPF6_PREFIX_SIZE (struct ospf6_prefix *); */
+#define OSPF6_PREFIX_SIZE(x) \
+   (OSPF6_PREFIX_SPACE ((x)->prefix_length) + sizeof (struct ospf6_prefix))
+
+/* struct ospf6_prefix *OSPF6_PREFIX_NEXT (struct ospf6_prefix *); */
+#define OSPF6_PREFIX_NEXT(x) \
+   ((struct ospf6_prefix *)((caddr_t)(x) + OSPF6_PREFIX_SIZE (x)))
+
+#define ospf6_prefix_in6_addr(in6, op)                         \
+do {                                                           \
+  memset (in6, 0, sizeof (struct in6_addr));                   \
+  memcpy (in6, (caddr_t) (op) + sizeof (struct ospf6_prefix),  \
+          OSPF6_PREFIX_SPACE ((op)->prefix_length));           \
+} while (0)
+
+extern void ospf6_prefix_apply_mask (struct ospf6_prefix *op);
+extern void ospf6_prefix_options_printbuf (u_int8_t prefix_options,
+                                           char *buf, int size);
+extern void ospf6_capability_printbuf (char capability, char *buf, int size);
+extern void ospf6_options_printbuf (u_char *options, char *buf, int size);
+
+#endif /* OSPF6_PROTO_H */
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
new file mode 100644 (file)
index 0000000..3cfbab5
--- /dev/null
@@ -0,0 +1,1413 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "command.h"
+#include "linklist.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_route = 0;
+
+static char *
+ospf6_route_table_name (struct ospf6_route_table *table)
+{
+  static char name[32];
+  switch (table->scope_type)
+    {
+      case OSPF6_SCOPE_TYPE_GLOBAL:
+        {
+          switch (table->table_type)
+            {
+              case OSPF6_TABLE_TYPE_ROUTES:
+                snprintf (name, sizeof (name), "global route table");
+                break;
+              case OSPF6_TABLE_TYPE_BORDER_ROUTERS:
+                snprintf (name, sizeof (name), "global brouter table");
+                break;
+              case OSPF6_TABLE_TYPE_EXTERNAL_ROUTES:
+                snprintf (name, sizeof (name), "global external table");
+                break;
+              default:
+                snprintf (name, sizeof (name), "global unknown table");
+                break;
+            }
+        }
+        break;
+
+      case OSPF6_SCOPE_TYPE_AREA:
+        {
+          struct ospf6_area *oa = (struct ospf6_area *) table->scope;
+          switch (table->table_type)
+            {
+              case OSPF6_TABLE_TYPE_SPF_RESULTS:
+                snprintf (name, sizeof (name),
+                          "area %s spf table", oa->name);
+                break;
+              case OSPF6_TABLE_TYPE_ROUTES:
+                snprintf (name, sizeof (name),
+                          "area %s route table", oa->name);
+                break;
+              case OSPF6_TABLE_TYPE_PREFIX_RANGES:
+                snprintf (name, sizeof (name),
+                          "area %s range table", oa->name);
+                break;
+              case OSPF6_TABLE_TYPE_SUMMARY_PREFIXES:
+                snprintf (name, sizeof (name),
+                          "area %s summary prefix table", oa->name);
+                break;
+              case OSPF6_TABLE_TYPE_SUMMARY_ROUTERS:
+                snprintf (name, sizeof (name),
+                          "area %s summary router table", oa->name);
+                break;
+              default:
+                snprintf (name, sizeof (name),
+                          "area %s unknown table", oa->name);
+                break;
+            }
+        }
+        break;
+
+      case OSPF6_SCOPE_TYPE_INTERFACE:
+        {
+          struct ospf6_interface *oi = (struct ospf6_interface *) table->scope;
+          switch (table->table_type)
+            {
+              case OSPF6_TABLE_TYPE_CONNECTED_ROUTES:
+                snprintf (name, sizeof (name), "interface %s connected table",
+                          oi->interface->name);
+                break;
+              default:
+                snprintf (name, sizeof (name), "interface %s unknown table",
+                          oi->interface->name);
+                break;
+            }
+        }
+        break;
+
+      default:
+        {
+          switch (table->table_type)
+            {
+              case OSPF6_TABLE_TYPE_SPF_RESULTS:
+                snprintf (name, sizeof (name), "temporary spf table");
+                break;
+              default:
+                snprintf (name, sizeof (name), "temporary unknown table");
+                break;
+            }
+        }
+        break;
+    }
+  return name;
+}
+
+void
+ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id,
+                        struct prefix *prefix)
+{
+  memset (prefix, 0, sizeof (struct prefix));
+  prefix->family = AF_INET6;
+  prefix->prefixlen = 64;
+  memcpy (&prefix->u.prefix6.s6_addr[0], &adv_router, 4);
+  memcpy (&prefix->u.prefix6.s6_addr[4], &id, 4);
+}
+
+void
+ospf6_linkstate_prefix2str (struct prefix *prefix, char *buf, int size)
+{
+  u_int32_t adv_router, id;
+  char adv_router_str[16], id_str[16];
+  memcpy (&adv_router, &prefix->u.prefix6.s6_addr[0], 4);
+  memcpy (&id, &prefix->u.prefix6.s6_addr[4], 4);
+  inet_ntop (AF_INET, &adv_router, adv_router_str, sizeof (adv_router_str));
+  inet_ntop (AF_INET, &id, id_str, sizeof (id_str));
+  if (ntohl (id))
+    snprintf (buf, size, "%s Net-ID: %s", adv_router_str, id_str);
+  else
+    snprintf (buf, size, "%s", adv_router_str);
+}
+
+/* Global strings for logging */
+const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX] =
+{ "Unknown", "Router", "Network", "Discard", "Linkstate", "AddressRange", };
+
+const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX] =
+{ "?", "R", "N", "D", "L", "A", };
+
+const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX] =
+{ "Unknown", "Intra-Area", "Inter-Area", "External-1", "External-2", };
+
+const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX] =
+{ "??", "IA", "IE", "E1", "E2", };
+
+
+struct ospf6_route *
+ospf6_route_create (void)
+{
+  struct ospf6_route *route;
+  route = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route));
+  return route;
+}
+
+void
+ospf6_route_delete (struct ospf6_route *route)
+{
+  XFREE (MTYPE_OSPF6_ROUTE, route);
+}
+
+struct ospf6_route *
+ospf6_route_copy (struct ospf6_route *route)
+{
+  struct ospf6_route *new;
+
+  new = ospf6_route_create ();
+  memcpy (new, route, sizeof (struct ospf6_route));
+  new->rnode = NULL;
+  new->prev = NULL;
+  new->next = NULL;
+  new->table = NULL;
+  new->lock = 0;
+  return new;
+}
+
+void
+ospf6_route_lock (struct ospf6_route *route)
+{
+  route->lock++;
+}
+
+void
+ospf6_route_unlock (struct ospf6_route *route)
+{
+  assert (route->lock > 0);
+  route->lock--;
+  if (route->lock == 0)
+    {
+      /* Can't detach from the table until here
+         because ospf6_route_next () will use
+         the 'route->table' pointer for logging */
+      route->table = NULL;
+      ospf6_route_delete (route);
+    }
+}
+
+/* Route compare function. If ra is more preferred, it returns
+   less than 0. If rb is more preferred returns greater than 0.
+   Otherwise (neither one is preferred), returns 0 */
+static int
+ospf6_route_cmp (struct ospf6_route *ra, struct ospf6_route *rb)
+{
+  assert (ospf6_route_is_same (ra, rb));
+  assert (OSPF6_PATH_TYPE_NONE < ra->path.type &&
+          ra->path.type < OSPF6_PATH_TYPE_MAX);
+  assert (OSPF6_PATH_TYPE_NONE < rb->path.type &&
+          rb->path.type < OSPF6_PATH_TYPE_MAX);
+
+  if (ra->type != rb->type)
+    return (ra->type - rb->type);
+
+  if (ra->path.area_id != rb->path.area_id)
+    return (ntohl (ra->path.area_id) - ntohl (rb->path.area_id));
+
+  if (ra->path.type != rb->path.type)
+    return (ra->path.type - rb->path.type);
+
+  if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2)
+    {
+      if (ra->path.cost_e2 != rb->path.cost_e2)
+        return (ra->path.cost_e2 - rb->path.cost_e2);
+    }
+  else
+    {
+      if (ra->path.cost != rb->path.cost)
+        return (ra->path.cost - rb->path.cost);
+    }
+
+  return 0;
+}
+
+struct ospf6_route *
+ospf6_route_lookup (struct prefix *prefix,
+                    struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route *route;
+
+  node = route_node_lookup (table->table, prefix);
+  if (node == NULL)
+    return NULL;
+
+  route = (struct ospf6_route *) node->info;
+  return route;
+}
+
+struct ospf6_route *
+ospf6_route_lookup_identical (struct ospf6_route *route,
+                              struct ospf6_route_table *table)
+{
+  struct ospf6_route *target;
+
+  for (target = ospf6_route_lookup (&route->prefix, table);
+       target; target = target->next)
+    {
+      if (ospf6_route_is_identical (target, route))
+        return target;
+    }
+  return NULL;
+}
+
+struct ospf6_route *
+ospf6_route_lookup_bestmatch (struct prefix *prefix,
+                              struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route *route;
+
+  node = route_node_match (table->table, prefix);
+  if (node == NULL)
+    return NULL;
+  route_unlock_node (node);
+
+  route = (struct ospf6_route *) node->info;
+  return route;
+}
+
+#ifdef DEBUG
+static void
+route_table_assert (struct ospf6_route_table *table)
+{
+  struct ospf6_route *prev, *r, *next;
+  char buf[64];
+  unsigned int link_error = 0, num = 0;
+  
+  r = ospf6_route_head (table);
+  prev = NULL;
+  while (r)
+    {
+      if (r->prev != prev)
+        link_error++;
+      
+      next = ospf6_route_next (r);
+      
+      if (r->next != next)
+        link_error++;
+      
+      prev = r;
+      r = next;
+    }
+  
+  for (r = ospf6_route_head (table); r; r = ospf6_route_next (r))
+    num++;
+  
+  if (link_error == 0 && num == table->count)
+    return;
+
+  zlog_err ("PANIC !!");
+  zlog_err ("Something has gone wrong with ospf6_route_table[%p]", table);
+  zlog_debug ("table count = %d, real number = %d", table->count, num);
+  zlog_debug ("DUMP START");
+  for (r = ospf6_route_head (table); r; r = ospf6_route_next (r))
+    {
+      prefix2str (&r->prefix, buf, sizeof (buf));
+      zlog_info ("%p<-[%p]->%p : %s", r->prev, r, r->next, buf);
+    }
+  zlog_debug ("DUMP END");
+
+  assert (link_error == 0 && num == table->count);
+}
+#define ospf6_route_table_assert(t) (route_table_assert (t))
+#else
+#define ospf6_route_table_assert(t) ((void) 0)
+#endif /*DEBUG*/
+
+struct ospf6_route *
+ospf6_route_add (struct ospf6_route *route,
+                 struct ospf6_route_table *table)
+{
+  struct route_node *node, *nextnode, *prevnode;
+  struct ospf6_route *current = NULL;
+  struct ospf6_route *prev = NULL, *old = NULL, *next = NULL;
+  char buf[64];
+  struct timeval now;
+
+  assert (route->rnode == NULL);
+  assert (route->lock == 0);
+  assert (route->next == NULL);
+  assert (route->prev == NULL);
+
+  if (route->type == OSPF6_DEST_TYPE_LINKSTATE)
+    ospf6_linkstate_prefix2str (&route->prefix, buf, sizeof (buf));
+  else
+    prefix2str (&route->prefix, buf, sizeof (buf));
+
+  if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+    zlog_debug ("%s %p: route add %p: %s", ospf6_route_table_name (table),
+                (void *)table, (void *)route, buf);
+  else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+    zlog_debug ("%s: route add: %s", ospf6_route_table_name (table), buf);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+
+  node = route_node_get (table->table, &route->prefix);
+  route->rnode = node;
+
+  /* find place to insert */
+  for (current = node->info; current; current = current->next)
+    {
+      if (! ospf6_route_is_same (current, route))
+        next = current;
+      else if (current->type != route->type)
+        prev = current;
+      else if (ospf6_route_is_same_origin (current, route))
+        old = current;
+      else if (ospf6_route_cmp (current, route) > 0)
+        next = current;
+      else
+        prev = current;
+
+      if (old || next)
+        break;
+    }
+
+  if (old)
+    {
+      /* if route does not actually change, return unchanged */
+      if (ospf6_route_is_identical (old, route))
+        {
+          if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+            zlog_debug ("%s %p: route add %p: needless update of %p",
+                        ospf6_route_table_name (table),
+                        (void *)table, (void *)route, (void *)old);
+          else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+            zlog_debug ("%s: route add: needless update",
+                        ospf6_route_table_name (table));
+
+          ospf6_route_delete (route);
+          SET_FLAG (old->flag, OSPF6_ROUTE_ADD);
+          ospf6_route_table_assert (table);
+
+          return old;
+        }
+
+      if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+        zlog_debug ("%s %p: route add %p: update of %p",
+                    ospf6_route_table_name (table),
+                    (void *)table, (void *)route, (void *)old);
+      else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+        zlog_debug ("%s: route add: update",
+                    ospf6_route_table_name (table));
+
+      /* replace old one if exists */
+      if (node->info == old)
+        {
+          node->info = route;
+          SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
+        }
+
+      if (old->prev)
+        old->prev->next = route;
+      route->prev = old->prev;
+      if (old->next)
+        old->next->prev = route;
+      route->next = old->next;
+
+      route->installed = old->installed;
+      route->changed = now;
+      assert (route->table == NULL);
+      route->table = table;
+
+      ospf6_route_unlock (old); /* will be deleted later */
+      ospf6_route_lock (route);
+
+      SET_FLAG (route->flag, OSPF6_ROUTE_CHANGE);
+      ospf6_route_table_assert (table);
+
+      if (table->hook_add)
+        (*table->hook_add) (route);
+
+      return route;
+    }
+
+  /* insert if previous or next node found */
+  if (prev || next)
+    {
+      if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+        zlog_debug ("%s %p: route add %p: another path: prev %p, next %p",
+                    ospf6_route_table_name (table),
+                    (void *)table, (void *)route, (void *)prev, (void *)next);
+      else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+        zlog_debug ("%s: route add: another path found",
+                    ospf6_route_table_name (table));
+
+      if (prev == NULL)
+        prev = next->prev;
+      if (next == NULL)
+        next = prev->next;
+
+      if (prev)
+        prev->next = route;
+      route->prev = prev;
+      if (next)
+        next->prev = route;
+      route->next = next;
+
+      if (node->info == next)
+        {
+          assert (next->rnode == node);
+          node->info = route;
+          UNSET_FLAG (next->flag, OSPF6_ROUTE_BEST);
+          SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
+          if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+            zlog_info ("%s %p: route add %p: replacing previous best: %p",
+                       ospf6_route_table_name (table),
+                       (void *)table, (void *)route, (void *)next);
+        }
+
+      route->installed = now;
+      route->changed = now;
+      assert (route->table == NULL);
+      route->table = table;
+
+      ospf6_route_lock (route);
+      table->count++;
+      ospf6_route_table_assert (table);
+
+      SET_FLAG (route->flag, OSPF6_ROUTE_ADD);
+      if (table->hook_add)
+        (*table->hook_add) (route);
+
+      return route;
+    }
+
+  /* Else, this is the brand new route regarding to the prefix */
+  if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+    zlog_debug ("%s %p: route add %p: brand new route",
+                ospf6_route_table_name (table), (void *)table, (void *)route);
+  else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+    zlog_debug ("%s: route add: brand new route",
+                ospf6_route_table_name (table));
+
+  assert (node->info == NULL);
+  node->info = route;
+  SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
+  ospf6_route_lock (route);
+  route->installed = now;
+  route->changed = now;
+  assert (route->table == NULL);
+  route->table = table;
+
+  /* lookup real existing next route */
+  nextnode = node;
+  route_lock_node (nextnode);
+  do {
+    nextnode = route_next (nextnode);
+  } while (nextnode && nextnode->info == NULL);
+
+  /* set next link */
+  if (nextnode == NULL)
+    route->next = NULL;
+  else
+    {
+      route_unlock_node (nextnode);
+
+      next = nextnode->info;
+      route->next = next;
+      next->prev = route;
+    }
+
+  /* lookup real existing prev route */
+  prevnode = node;
+  route_lock_node (prevnode);
+  do {
+    prevnode = route_prev (prevnode);
+  } while (prevnode && prevnode->info == NULL);
+
+  /* set prev link */
+  if (prevnode == NULL)
+    route->prev = NULL;
+  else
+    {
+      route_unlock_node (prevnode);
+
+      prev = prevnode->info;
+      while (prev->next && ospf6_route_is_same (prev, prev->next))
+        prev = prev->next;
+      route->prev = prev;
+      prev->next = route;
+    }
+
+  table->count++;
+  ospf6_route_table_assert (table);
+
+  SET_FLAG (route->flag, OSPF6_ROUTE_ADD);
+  if (table->hook_add)
+    (*table->hook_add) (route);
+
+  return route;
+}
+
+void
+ospf6_route_remove (struct ospf6_route *route,
+                    struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route *current;
+  char buf[64];
+
+  if (route->type == OSPF6_DEST_TYPE_LINKSTATE)
+    ospf6_linkstate_prefix2str (&route->prefix, buf, sizeof (buf));
+  else
+    prefix2str (&route->prefix, buf, sizeof (buf));
+
+  if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+    zlog_debug ("%s %p: route remove %p: %s",
+                ospf6_route_table_name (table),
+                (void *)table, (void *)route, buf);
+  else if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+    zlog_debug ("%s: route remove: %s", ospf6_route_table_name (table), buf);
+
+  node = route_node_lookup (table->table, &route->prefix);
+  assert (node);
+
+  /* find the route to remove, making sure that the route pointer
+     is from the route table. */
+  current = node->info;
+  while (current && ospf6_route_is_same (current, route))
+    {
+      if (current == route)
+        break;
+      current = current->next;
+    }
+  assert (current == route);
+
+  /* adjust doubly linked list */
+  if (route->prev)
+    route->prev->next = route->next;
+  if (route->next)
+    route->next->prev = route->prev;
+
+  if (node->info == route)
+    {
+      if (route->next && route->next->rnode == node)
+        {
+          node->info = route->next;
+          SET_FLAG (route->next->flag, OSPF6_ROUTE_BEST);
+        }
+      else
+        node->info = NULL; /* should unlock route_node here ? */
+    }
+
+  table->count--;
+  ospf6_route_table_assert (table);
+
+  SET_FLAG (route->flag, OSPF6_ROUTE_WAS_REMOVED);
+
+  if (table->hook_remove)
+    (*table->hook_remove) (route);
+
+  ospf6_route_unlock (route);
+}
+
+struct ospf6_route *
+ospf6_route_head (struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route *route;
+
+  node = route_top (table->table);
+  if (node == NULL)
+    return NULL;
+
+  /* skip to the real existing entry */
+  while (node && node->info == NULL)
+    node = route_next (node);
+  if (node == NULL)
+    return NULL;
+
+  route_unlock_node (node);
+  assert (node->info);
+
+  route = (struct ospf6_route *) node->info;
+  assert (route->prev == NULL);
+  assert (route->table == table);
+  ospf6_route_lock (route);
+
+  if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+    zlog_info ("%s %p: route head: %p<-[%p]->%p",
+               ospf6_route_table_name (table), (void *)table,
+               (void *)route->prev, (void *)route, (void *)route->next);
+
+  return route;
+}
+
+struct ospf6_route *
+ospf6_route_next (struct ospf6_route *route)
+{
+  struct ospf6_route *next = route->next;
+
+  if (IS_OSPF6_DEBUG_ROUTE (MEMORY))
+    zlog_info ("%s %p: route next: %p<-[%p]->%p",
+               ospf6_route_table_name (route->table), (void *)route->table,
+               (void *)route->prev, (void *)route, (void *)route->next);
+
+  ospf6_route_unlock (route);
+  if (next)
+    ospf6_route_lock (next);
+
+  return next;
+}
+
+struct ospf6_route *
+ospf6_route_best_next (struct ospf6_route *route)
+{
+  struct route_node *rnode;
+  struct ospf6_route *next;
+
+  ospf6_route_unlock (route);
+
+  rnode = route->rnode;
+  route_lock_node (rnode);
+  rnode = route_next (rnode);
+  while (rnode && rnode->info == NULL)
+    rnode = route_next (rnode);
+  if (rnode == NULL)
+    return NULL;
+  route_unlock_node (rnode);
+
+  assert (rnode->info);
+  next = (struct ospf6_route *) rnode->info;
+  ospf6_route_lock (next);
+  return next;
+}
+
+struct ospf6_route *
+ospf6_route_match_head (struct prefix *prefix,
+                        struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route *route;
+
+  /* Walk down tree. */
+  node = table->table->top;
+  while (node && node->p.prefixlen < prefix->prefixlen &&
+        prefix_match (&node->p, prefix))
+    node = node->link[prefix_bit(&prefix->u.prefix, node->p.prefixlen)];
+
+  if (node)
+    route_lock_node (node);
+  while (node && node->info == NULL)
+    node = route_next (node);
+  if (node == NULL)
+    return NULL;
+  route_unlock_node (node);
+
+  if (! prefix_match (prefix, &node->p))
+    return NULL;
+
+  route = node->info;
+  ospf6_route_lock (route);
+  return route;
+}
+
+struct ospf6_route *
+ospf6_route_match_next (struct prefix *prefix,
+                        struct ospf6_route *route)
+{
+  struct ospf6_route *next;
+
+  next = ospf6_route_next (route);
+  if (next && ! prefix_match (prefix, &next->prefix))
+    {
+      ospf6_route_unlock (next);
+      next = NULL;
+    }
+
+  return next;
+}
+
+void
+ospf6_route_remove_all (struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+  for (route = ospf6_route_head (table); route;
+       route = ospf6_route_next (route))
+    ospf6_route_remove (route, table);
+}
+
+struct ospf6_route_table *
+ospf6_route_table_create (int s, int t)
+{
+  struct ospf6_route_table *new;
+  new = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_table));
+  new->table = route_table_init ();
+  new->scope_type = s;
+  new->table_type = t;
+  return new;
+}
+
+void
+ospf6_route_table_delete (struct ospf6_route_table *table)
+{
+  ospf6_route_remove_all (table);
+  route_table_finish (table->table);
+  XFREE (MTYPE_OSPF6_ROUTE, table);
+}
+
+
+/* VTY commands */
+void
+ospf6_route_show (struct vty *vty, struct ospf6_route *route)
+{
+  int i;
+  char destination[64], nexthop[64];
+  char duration[16];
+  const char *ifname;
+  struct timeval now, res;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &route->changed, &res);
+  timerstring (&res, duration, sizeof (duration));
+
+  /* destination */
+  if (route->type == OSPF6_DEST_TYPE_LINKSTATE)
+    ospf6_linkstate_prefix2str (&route->prefix, destination,
+                                sizeof (destination));
+  else if (route->type == OSPF6_DEST_TYPE_ROUTER)
+    inet_ntop (route->prefix.family, &route->prefix.u.prefix,
+               destination, sizeof (destination));
+  else
+    prefix2str (&route->prefix, destination, sizeof (destination));
+
+  /* nexthop */
+  inet_ntop (AF_INET6, &route->nexthop[0].address, nexthop,
+             sizeof (nexthop));
+  ifname = ifindex2ifname (route->nexthop[0].ifindex);
+
+  vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s",
+           (ospf6_route_is_best (route) ? '*' : ' '),
+           OSPF6_DEST_TYPE_SUBSTR (route->type),
+           OSPF6_PATH_TYPE_SUBSTR (route->path.type),
+           destination, nexthop, IFNAMSIZ, ifname, duration, VNL);
+
+  for (i = 1; i < OSPF6_MULTI_PATH_LIMIT &&
+       ospf6_nexthop_is_set (&route->nexthop[i]); i++)
+    {
+      /* nexthop */
+      inet_ntop (AF_INET6, &route->nexthop[i].address, nexthop,
+                 sizeof (nexthop));
+      ifname = ifindex2ifname (route->nexthop[i].ifindex);
+
+      vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s",
+               ' ', "", "", "", nexthop, IFNAMSIZ, ifname, "", VNL);
+    }
+}
+
+void
+ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route)
+{
+  const char *ifname;
+  char destination[64], nexthop[64];
+  char area_id[16], id[16], adv_router[16], capa[16], options[16];
+  struct timeval now, res;
+  char duration[16];
+  int i;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+
+  /* destination */
+  if (route->type == OSPF6_DEST_TYPE_LINKSTATE)
+    ospf6_linkstate_prefix2str (&route->prefix, destination,
+                                sizeof (destination));
+  else if (route->type == OSPF6_DEST_TYPE_ROUTER)
+    inet_ntop (route->prefix.family, &route->prefix.u.prefix,
+               destination, sizeof (destination));
+  else
+    prefix2str (&route->prefix, destination, sizeof (destination));
+  vty_out (vty, "Destination: %s%s", destination, VNL);
+
+  /* destination type */
+  vty_out (vty, "Destination type: %s%s",
+           OSPF6_DEST_TYPE_NAME (route->type),
+           VNL);
+
+  /* Time */
+  timersub (&now, &route->installed, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "Installed Time: %s ago%s", duration, VNL);
+
+  timersub (&now, &route->changed, &res);
+  timerstring (&res, duration, sizeof (duration));
+  vty_out (vty, "  Changed Time: %s ago%s", duration, VNL);
+
+  /* Debugging info */
+  vty_out (vty, "Lock: %d Flags: %s%s%s%s%s", route->lock,
+           (CHECK_FLAG (route->flag, OSPF6_ROUTE_BEST)   ? "B" : "-"),
+           (CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD)    ? "A" : "-"),
+           (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"),
+           (CHECK_FLAG (route->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-"),
+           VNL);
+  vty_out (vty, "Memory: prev: %p this: %p next: %p%s",
+           (void *)route->prev, (void *)route, (void *)route->next, VNL);
+
+  /* Path section */
+
+  /* Area-ID */
+  inet_ntop (AF_INET, &route->path.area_id, area_id, sizeof (area_id));
+  vty_out (vty, "Associated Area: %s%s", area_id, VNL);
+
+  /* Path type */
+  vty_out (vty, "Path Type: %s%s",
+           OSPF6_PATH_TYPE_NAME (route->path.type), VNL);
+
+  /* LS Origin */
+  inet_ntop (AF_INET, &route->path.origin.id, id, sizeof (id));
+  inet_ntop (AF_INET, &route->path.origin.adv_router, adv_router,
+             sizeof (adv_router));
+  vty_out (vty, "LS Origin: %s Id: %s Adv: %s%s",
+           ospf6_lstype_name (route->path.origin.type),
+           id, adv_router, VNL);
+
+  /* Options */
+  ospf6_options_printbuf (route->path.options, options, sizeof (options));
+  vty_out (vty, "Options: %s%s", options, VNL);
+
+  /* Router Bits */
+  ospf6_capability_printbuf (route->path.router_bits, capa, sizeof (capa));
+  vty_out (vty, "Router Bits: %s%s", capa, VNL);
+
+  /* Prefix Options */
+  vty_out (vty, "Prefix Options: xxx%s", VNL);
+
+  /* Metrics */
+  vty_out (vty, "Metric Type: %d%s", route->path.metric_type,
+           VNL);
+  vty_out (vty, "Metric: %d (%d)%s",
+           route->path.cost, route->path.cost_e2, VNL);
+
+  /* Nexthops */
+  vty_out (vty, "Nexthop:%s", VNL);
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT &&
+       ospf6_nexthop_is_set (&route->nexthop[i]); i++)
+    {
+      /* nexthop */
+      inet_ntop (AF_INET6, &route->nexthop[i].address, nexthop,
+                 sizeof (nexthop));
+      ifname = ifindex2ifname (route->nexthop[i].ifindex);
+      vty_out (vty, "  %s %.*s%s", nexthop, IFNAMSIZ, ifname, VNL);
+    }
+  vty_out (vty, "%s", VNL);
+}
+
+static void
+ospf6_route_show_table_summary (struct vty *vty,
+                                struct ospf6_route_table *table)
+{
+  struct ospf6_route *route, *prev = NULL;
+  int i, pathtype[OSPF6_PATH_TYPE_MAX];
+  unsigned int number = 0;
+  int nhinval = 0, ecmp = 0;
+  int alternative = 0, destination = 0;
+
+  for (i = 0; i < OSPF6_PATH_TYPE_MAX; i++)
+    pathtype[i] = 0;
+
+  for (route = ospf6_route_head (table); route;
+       route = ospf6_route_next (route))
+    {
+      if (prev == NULL || ! ospf6_route_is_same (prev, route))
+        destination++;
+      else
+        alternative++;
+      if (! ospf6_nexthop_is_set (&route->nexthop[0]))
+        nhinval++;
+      else if (ospf6_nexthop_is_set (&route->nexthop[1]))
+        ecmp++;
+      pathtype[route->path.type]++;
+      number++;
+
+      prev = route;
+    }
+
+  assert (number == table->count);
+
+  vty_out (vty, "Number of OSPFv3 routes: %d%s", number, VNL);
+  vty_out (vty, "Number of Destination: %d%s", destination, VNL);
+  vty_out (vty, "Number of Alternative routes: %d%s", alternative, VNL);
+  vty_out (vty, "Number of Equal Cost Multi Path: %d%s", ecmp, VNL);
+  for (i = OSPF6_PATH_TYPE_INTRA; i <= OSPF6_PATH_TYPE_EXTERNAL2; i++)
+    {
+      vty_out (vty, "Number of %s routes: %d%s",
+               OSPF6_PATH_TYPE_NAME (i), pathtype[i], VNL);
+    }
+}
+
+static void
+ospf6_route_show_table_prefix (struct vty *vty,
+                               struct prefix *prefix,
+                               struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  route = ospf6_route_lookup (prefix, table);
+  if (route == NULL)
+    return;
+
+  ospf6_route_lock (route);
+  while (route && ospf6_route_is_prefix (prefix, route))
+    {
+      /* Specifying a prefix will always display details */
+      ospf6_route_show_detail (vty, route);
+      route = ospf6_route_next (route);
+    }
+  if (route)
+    ospf6_route_unlock (route);
+}
+
+static void
+ospf6_route_show_table_address (struct vty *vty,
+                                struct prefix *prefix,
+                                struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  route = ospf6_route_lookup_bestmatch (prefix, table);
+  if (route == NULL)
+    return;
+
+  prefix = &route->prefix;
+  ospf6_route_lock (route);
+  while (route && ospf6_route_is_prefix (prefix, route))
+    {
+      /* Specifying a prefix will always display details */
+      ospf6_route_show_detail (vty, route);
+      route = ospf6_route_next (route);
+    }
+  if (route)
+    ospf6_route_unlock (route);
+}
+
+static void
+ospf6_route_show_table_match (struct vty *vty, int detail,
+                              struct prefix *prefix,
+                              struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+  assert (prefix->family);
+
+  route = ospf6_route_match_head (prefix, table);
+  while (route)
+    {
+      if (detail)
+        ospf6_route_show_detail (vty, route);
+      else
+        ospf6_route_show (vty, route);
+      route = ospf6_route_match_next (prefix, route);
+    }
+}
+
+static void
+ospf6_route_show_table_type (struct vty *vty, int detail, u_char type,
+                             struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  route = ospf6_route_head (table);
+  while (route)
+    {
+      if (route->path.type == type)
+        {
+          if (detail)
+            ospf6_route_show_detail (vty, route);
+          else
+            ospf6_route_show (vty, route);
+        }
+      route = ospf6_route_next (route);
+    }
+}
+
+static void
+ospf6_route_show_table (struct vty *vty, int detail,
+                        struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  route = ospf6_route_head (table);
+  while (route)
+    {
+      if (detail)
+        ospf6_route_show_detail (vty, route);
+      else
+        ospf6_route_show (vty, route);
+      route = ospf6_route_next (route);
+    }
+}
+
+int
+ospf6_route_table_show (struct vty *vty, int argc, const char *argv[],
+                        struct ospf6_route_table *table)
+{
+  int summary = 0;
+  int match = 0;
+  int detail = 0;
+  int slash = 0;
+  int isprefix = 0;
+  int i, ret;
+  struct prefix prefix;
+  u_char type = 0;
+
+  memset (&prefix, 0, sizeof (struct prefix));
+
+  for (i = 0; i < argc; i++)
+    {
+      if (! strcmp (argv[i], "summary"))
+        {
+          summary++;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "intra-area"))
+        {
+          type = OSPF6_PATH_TYPE_INTRA;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "inter-area"))
+        {
+          type = OSPF6_PATH_TYPE_INTER;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "external-1"))
+        {
+          type = OSPF6_PATH_TYPE_EXTERNAL1;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "external-2"))
+        {
+          type = OSPF6_PATH_TYPE_EXTERNAL2;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "detail"))
+        {
+          detail++;
+          continue;
+        }
+
+      if (! strcmp (argv[i], "match"))
+        {
+          match++;
+          continue;
+        }
+
+      ret = str2prefix (argv[i], &prefix);
+      if (ret == 1 && prefix.family == AF_INET6)
+        {
+          isprefix++;
+          if (strchr (argv[i], '/'))
+            slash++;
+          continue;
+        }
+
+      vty_out (vty, "Malformed argument: %s%s", argv[i], VNL);
+      return CMD_SUCCESS;
+    }
+
+  /* Give summary of this route table */
+  if (summary)
+    {
+      ospf6_route_show_table_summary (vty, table);
+      return CMD_SUCCESS;
+    }
+
+  /* Give exact prefix-match route */
+  if (isprefix && ! match)
+    {
+      /* If exact address, give best matching route */
+      if (! slash)
+        ospf6_route_show_table_address (vty, &prefix, table);
+      else
+        ospf6_route_show_table_prefix (vty, &prefix, table);
+
+      return CMD_SUCCESS;
+    }
+
+  if (match)
+    ospf6_route_show_table_match (vty, detail, &prefix, table);
+  else if (type)
+    ospf6_route_show_table_type (vty, detail, type, table);
+  else
+    ospf6_route_show_table (vty, detail, table);
+
+  return CMD_SUCCESS;
+}
+
+static void
+ospf6_linkstate_show_header (struct vty *vty)
+{
+  vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %s%s",
+           "Type", "Router-ID", "Net-ID", "Rtr-Bits", "Options", "Cost", VNL);
+}
+
+static void
+ospf6_linkstate_show (struct vty *vty, struct ospf6_route *route)
+{
+  u_int32_t router, id;
+  char routername[16], idname[16], rbits[16], options[16];
+
+  router = ospf6_linkstate_prefix_adv_router (&route->prefix);
+  inet_ntop (AF_INET, &router, routername, sizeof (routername));
+  id = ospf6_linkstate_prefix_id (&route->prefix);
+  inet_ntop (AF_INET, &id, idname, sizeof (idname));
+
+  ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits));
+  ospf6_options_printbuf (route->path.options, options, sizeof (options));
+
+  if (ntohl (id))
+    vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %lu%s",
+             "Network", routername, idname, rbits, options,
+             (unsigned long) route->path.cost, VNL);
+  else
+    vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %lu%s",
+             "Router", routername, idname, rbits, options,
+             (unsigned long) route->path.cost, VNL);
+}
+
+
+static void
+ospf6_linkstate_show_table_exact (struct vty *vty,
+                                  struct prefix *prefix,
+                                  struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  route = ospf6_route_lookup (prefix, table);
+  if (route == NULL)
+    return;
+
+  ospf6_route_lock (route);
+  while (route && ospf6_route_is_prefix (prefix, route))
+    {
+      /* Specifying a prefix will always display details */
+      ospf6_route_show_detail (vty, route);
+      route = ospf6_route_next (route);
+    }
+  if (route)
+    ospf6_route_unlock (route);
+}
+
+static void
+ospf6_linkstate_show_table (struct vty *vty, int detail,
+                            struct ospf6_route_table *table)
+{
+  struct ospf6_route *route;
+
+  if (! detail)
+    ospf6_linkstate_show_header (vty);
+
+  route = ospf6_route_head (table);
+  while (route)
+    {
+      if (detail)
+        ospf6_route_show_detail (vty, route);
+      else
+        ospf6_linkstate_show (vty, route);
+      route = ospf6_route_next (route);
+    }
+}
+
+int
+ospf6_linkstate_table_show (struct vty *vty, int argc, const char *argv[],
+                            struct ospf6_route_table *table)
+{
+  int detail = 0;
+  int is_id = 0;
+  int is_router = 0;
+  int i, ret;
+  struct prefix router, id, prefix;
+
+  memset (&router, 0, sizeof (struct prefix));
+  memset (&id, 0, sizeof (struct prefix));
+  memset (&prefix, 0, sizeof (struct prefix));
+
+  for (i = 0; i < argc; i++)
+    {
+      if (! strcmp (argv[i], "detail"))
+        {
+          detail++;
+          continue;
+        }
+
+      if (! is_router)
+        {
+          ret = str2prefix (argv[i], &router);
+          if (ret == 1 && router.family == AF_INET)
+            {
+              is_router++;
+              continue;
+            }
+          vty_out (vty, "Malformed argument: %s%s", argv[i], VNL);
+          return CMD_SUCCESS;
+        }
+
+      if (! is_id)
+        {
+          ret = str2prefix (argv[i], &id);
+          if (ret == 1 && id.family == AF_INET)
+            {
+              is_id++;
+              continue;
+            }
+          vty_out (vty, "Malformed argument: %s%s", argv[i], VNL);
+          return CMD_SUCCESS;
+        }
+
+      vty_out (vty, "Malformed argument: %s%s", argv[i], VNL);
+      return CMD_SUCCESS;
+    }
+
+  if (is_router)
+    ospf6_linkstate_prefix (router.u.prefix4.s_addr,
+                            id.u.prefix4.s_addr, &prefix);
+
+  if (prefix.family)
+    ospf6_linkstate_show_table_exact (vty, &prefix, table);
+  else
+    ospf6_linkstate_show_table (vty, detail, table);
+
+  return CMD_SUCCESS;
+}
+
+
+void
+ospf6_brouter_show_header (struct vty *vty)
+{
+  vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s",
+           "Router-ID", "Rtr-Bits", "Options", "Path-Type", "Area", VNL);
+}
+
+void
+ospf6_brouter_show (struct vty *vty, struct ospf6_route *route)
+{
+  u_int32_t adv_router;
+  char adv[16], rbits[16], options[16], area[16];
+
+  adv_router = ospf6_linkstate_prefix_adv_router (&route->prefix);
+  inet_ntop (AF_INET, &adv_router, adv, sizeof (adv));
+  ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits));
+  ospf6_options_printbuf (route->path.options, options, sizeof (options));
+  inet_ntop (AF_INET, &route->path.area_id, area, sizeof (area));
+
+  /* vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s",
+           "Router-ID", "Rtr-Bits", "Options", "Path-Type", "Area", VNL); */
+  vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s",
+           adv, rbits, options, OSPF6_PATH_TYPE_NAME (route->path.type),
+           area, VNL);
+}
+
+DEFUN (debug_ospf6_route,
+       debug_ospf6_route_cmd,
+       "debug ospf6 route (table|intra-area|inter-area|memory)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug route table calculation\n"
+       "Debug detail\n"
+       "Debug intra-area route calculation\n"
+       "Debug inter-area route calculation\n"
+       "Debug route memory use\n"
+       )
+{
+  unsigned char level = 0;
+
+  if (! strncmp (argv[0], "table", 5))
+    level = OSPF6_DEBUG_ROUTE_TABLE;
+  else if (! strncmp (argv[0], "intra", 5))
+    level = OSPF6_DEBUG_ROUTE_INTRA;
+  else if (! strncmp (argv[0], "inter", 5))
+    level = OSPF6_DEBUG_ROUTE_INTER;
+  else if (! strncmp (argv[0], "memor", 5))
+    level = OSPF6_DEBUG_ROUTE_MEMORY;
+  OSPF6_DEBUG_ROUTE_ON (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_route,
+       no_debug_ospf6_route_cmd,
+       "no debug ospf6 route (table|intra-area|inter-area|memory)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug route table calculation\n"
+       "Debug intra-area route calculation\n"
+       "Debug route memory use\n")
+{
+  unsigned char level = 0;
+
+  if (! strncmp (argv[0], "table", 5))
+    level = OSPF6_DEBUG_ROUTE_TABLE;
+  else if (! strncmp (argv[0], "intra", 5))
+    level = OSPF6_DEBUG_ROUTE_INTRA;
+  else if (! strncmp (argv[0], "inter", 5))
+    level = OSPF6_DEBUG_ROUTE_INTER;
+  else if (! strncmp (argv[0], "memor", 5))
+    level = OSPF6_DEBUG_ROUTE_MEMORY;
+  OSPF6_DEBUG_ROUTE_OFF (level);
+  return CMD_SUCCESS;
+}
+
+int
+config_write_ospf6_debug_route (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_ROUTE (TABLE))
+    vty_out (vty, "debug ospf6 route table%s", VNL);
+  if (IS_OSPF6_DEBUG_ROUTE (INTRA))
+    vty_out (vty, "debug ospf6 route intra-area%s", VNL);
+  if (IS_OSPF6_DEBUG_ROUTE (INTER))
+    vty_out (vty, "debug ospf6 route inter-area%s", VNL);
+  return 0;
+}
+
+void
+install_element_ospf6_debug_route (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_route_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_route_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_route_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_route_cmd);
+}
+
+
+
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
new file mode 100644 (file)
index 0000000..027a648
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_ROUTE_H
+#define OSPF6_ROUTE_H
+
+#define OSPF6_MULTI_PATH_LIMIT    4
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_route;
+#define OSPF6_DEBUG_ROUTE_TABLE   0x01
+#define OSPF6_DEBUG_ROUTE_INTRA   0x02
+#define OSPF6_DEBUG_ROUTE_INTER   0x04
+#define OSPF6_DEBUG_ROUTE_MEMORY  0x80
+#define OSPF6_DEBUG_ROUTE_ON(level) \
+  (conf_debug_ospf6_route |= (level))
+#define OSPF6_DEBUG_ROUTE_OFF(level) \
+  (conf_debug_ospf6_route &= ~(level))
+#define IS_OSPF6_DEBUG_ROUTE(e) \
+  (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_ ## e)
+
+/* Nexthop */
+struct ospf6_nexthop
+{
+  /* Interface index */
+  ifindex_t ifindex;
+
+  /* IP address, if any */
+  struct in6_addr address;
+};
+
+#define ospf6_nexthop_is_set(x)                                \
+  ((x)->ifindex || ! IN6_IS_ADDR_UNSPECIFIED (&(x)->address))
+#define ospf6_nexthop_is_same(a,b)                             \
+  ((a)->ifindex == (b)->ifindex &&                            \
+   IN6_ARE_ADDR_EQUAL (&(a)->address, &(b)->address))
+#define ospf6_nexthop_clear(x)                                \
+  do {                                                        \
+    (x)->ifindex = 0;                                         \
+    memset (&(x)->address, 0, sizeof (struct in6_addr));      \
+  } while (0)
+#define ospf6_nexthop_copy(a, b)                              \
+  do {                                                        \
+    (a)->ifindex = (b)->ifindex;                              \
+    memcpy (&(a)->address, &(b)->address,                     \
+            sizeof (struct in6_addr));                        \
+  } while (0)
+
+/* Path */
+struct ospf6_ls_origin
+{
+  u_int16_t type;
+  u_int32_t id;
+  u_int32_t adv_router;
+};
+
+struct ospf6_path
+{
+  /* Link State Origin */
+  struct ospf6_ls_origin origin;
+
+  /* Router bits */
+  u_char router_bits;
+
+  /* Optional Capabilities */
+  u_char options[3];
+
+  /* Prefix Options */
+  u_char prefix_options;
+
+  /* Associated Area */
+  u_int32_t area_id;
+
+  /* Path-type */
+  u_char type;
+  u_char subtype; /* only used for redistribute i.e ZEBRA_ROUTE_XXX */
+
+  /* Cost */
+  u_int8_t metric_type;
+  u_int32_t cost;
+  u_int32_t cost_e2;
+  u_int32_t tag;
+};
+
+#define OSPF6_PATH_TYPE_NONE         0
+#define OSPF6_PATH_TYPE_INTRA        1
+#define OSPF6_PATH_TYPE_INTER        2
+#define OSPF6_PATH_TYPE_EXTERNAL1    3
+#define OSPF6_PATH_TYPE_EXTERNAL2    4
+#define OSPF6_PATH_TYPE_REDISTRIBUTE 5
+#define OSPF6_PATH_TYPE_MAX          6
+
+#include "prefix.h"
+#include "table.h"
+
+struct ospf6_route
+{
+  struct route_node *rnode;
+  struct ospf6_route_table *table;
+  struct ospf6_route *prev;
+  struct ospf6_route *next;
+
+  unsigned int lock;
+
+  /* Destination Type */
+  u_char type;
+
+  /* XXX: It would likely be better to use separate struct in_addr's
+   * for the advertising router-ID and prefix IDs, instead of stuffing them
+   * into one. See also XXX below.
+   */
+  /* Destination ID */
+  struct prefix prefix;
+
+  /* Time */
+  struct timeval installed;
+  struct timeval changed;
+
+  /* flag */
+  u_char flag;
+
+  /* path */
+  struct ospf6_path path;
+
+  /* nexthop */
+  struct ospf6_nexthop nexthop[OSPF6_MULTI_PATH_LIMIT];
+
+  /* route option */
+  void *route_option;
+
+  /* link state id for advertising */
+  u_int32_t linkstate_id;
+};
+
+#define OSPF6_DEST_TYPE_NONE       0
+#define OSPF6_DEST_TYPE_ROUTER     1
+#define OSPF6_DEST_TYPE_NETWORK    2
+#define OSPF6_DEST_TYPE_DISCARD    3
+#define OSPF6_DEST_TYPE_LINKSTATE  4
+#define OSPF6_DEST_TYPE_RANGE      5
+#define OSPF6_DEST_TYPE_MAX        6
+
+#define OSPF6_ROUTE_CHANGE           0x01
+#define OSPF6_ROUTE_ADD              0x02
+#define OSPF6_ROUTE_REMOVE           0x04
+#define OSPF6_ROUTE_BEST             0x08
+#define OSPF6_ROUTE_ACTIVE_SUMMARY   0x10
+#define OSPF6_ROUTE_DO_NOT_ADVERTISE 0x20
+#define OSPF6_ROUTE_WAS_REMOVED      0x40
+
+struct ospf6_route_table
+{
+  int scope_type;
+  int table_type;
+  void *scope;
+
+  /* patricia tree */
+  struct route_table *table;
+
+  u_int32_t count;
+
+  /* hooks */
+  void (*hook_add) (struct ospf6_route *);
+  void (*hook_change) (struct ospf6_route *);
+  void (*hook_remove) (struct ospf6_route *);
+};
+
+#define OSPF6_SCOPE_TYPE_NONE      0
+#define OSPF6_SCOPE_TYPE_GLOBAL    1
+#define OSPF6_SCOPE_TYPE_AREA      2
+#define OSPF6_SCOPE_TYPE_INTERFACE 3
+
+#define OSPF6_TABLE_TYPE_NONE              0
+#define OSPF6_TABLE_TYPE_ROUTES            1
+#define OSPF6_TABLE_TYPE_BORDER_ROUTERS    2
+#define OSPF6_TABLE_TYPE_CONNECTED_ROUTES  3
+#define OSPF6_TABLE_TYPE_EXTERNAL_ROUTES   4
+#define OSPF6_TABLE_TYPE_SPF_RESULTS       5
+#define OSPF6_TABLE_TYPE_PREFIX_RANGES     6
+#define OSPF6_TABLE_TYPE_SUMMARY_PREFIXES  7
+#define OSPF6_TABLE_TYPE_SUMMARY_ROUTERS   8
+
+#define OSPF6_ROUTE_TABLE_CREATE(s, t) \
+  ospf6_route_table_create (OSPF6_SCOPE_TYPE_ ## s, \
+                            OSPF6_TABLE_TYPE_ ## t)
+
+extern const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX];
+extern const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX];
+#define OSPF6_DEST_TYPE_NAME(x)                       \
+  (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ?             \
+   ospf6_dest_type_str[(x)] : ospf6_dest_type_str[0])
+#define OSPF6_DEST_TYPE_SUBSTR(x)                           \
+  (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ?                   \
+   ospf6_dest_type_substr[(x)] : ospf6_dest_type_substr[0])
+
+extern const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX];
+extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX];
+#define OSPF6_PATH_TYPE_NAME(x)                       \
+  (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ?             \
+   ospf6_path_type_str[(x)] : ospf6_path_type_str[0])
+#define OSPF6_PATH_TYPE_SUBSTR(x)                           \
+  (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ?                   \
+   ospf6_path_type_substr[(x)] : ospf6_path_type_substr[0])
+
+#define OSPF6_ROUTE_ADDRESS_STR "Display the route bestmatches the address\n"
+#define OSPF6_ROUTE_PREFIX_STR  "Display the route\n"
+#define OSPF6_ROUTE_MATCH_STR   "Display the route matches the prefix\n"
+
+#define ospf6_route_is_prefix(p, r) \
+  (memcmp (p, &(r)->prefix, sizeof (struct prefix)) == 0)
+#define ospf6_route_is_same(ra, rb) \
+  (prefix_same (&(ra)->prefix, &(rb)->prefix))
+#define ospf6_route_is_same_origin(ra, rb) \
+  ((ra)->path.area_id == (rb)->path.area_id && \
+   memcmp (&(ra)->path.origin, &(rb)->path.origin, \
+           sizeof (struct ospf6_ls_origin)) == 0)
+#define ospf6_route_is_identical(ra, rb) \
+  ((ra)->type == (rb)->type && \
+   memcmp (&(ra)->prefix, &(rb)->prefix, sizeof (struct prefix)) == 0 && \
+   memcmp (&(ra)->path, &(rb)->path, sizeof (struct ospf6_path)) == 0 && \
+   memcmp (&(ra)->nexthop, &(rb)->nexthop,                               \
+           sizeof (struct ospf6_nexthop) * OSPF6_MULTI_PATH_LIMIT) == 0)
+#define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST))
+
+#define ospf6_linkstate_prefix_adv_router(x) \
+  ((x)->u.lp.id.s_addr)
+#define ospf6_linkstate_prefix_id(x) \
+  ((x)->u.lp.adv_router.s_addr)
+
+#define ADV_ROUTER_IN_PREFIX(x) \
+  ((x)->u.lp.id.s_addr)
+#define ID_IN_PREFIX(x) \
+  ((x)->u.lp.adv_router.s_addr)
+
+/* Function prototype */
+extern void ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id,
+                                    struct prefix *prefix);
+extern void ospf6_linkstate_prefix2str (struct prefix *prefix, char *buf,
+                                        int size);
+
+extern struct ospf6_route *ospf6_route_create (void);
+extern void ospf6_route_delete (struct ospf6_route *);
+extern struct ospf6_route *ospf6_route_copy (struct ospf6_route *route);
+
+extern void ospf6_route_lock (struct ospf6_route *route);
+extern void ospf6_route_unlock (struct ospf6_route *route);
+
+extern struct ospf6_route *ospf6_route_lookup (struct prefix *prefix,
+                                               struct ospf6_route_table *table);
+extern struct ospf6_route *ospf6_route_lookup_identical (struct ospf6_route *route,
+                                            struct ospf6_route_table *table);
+extern struct ospf6_route *ospf6_route_lookup_bestmatch (struct prefix *prefix,
+                                              struct ospf6_route_table *table);
+
+extern struct ospf6_route *ospf6_route_add (struct ospf6_route *route,
+                                            struct ospf6_route_table *table);
+extern void ospf6_route_remove (struct ospf6_route *route,
+                                struct ospf6_route_table *table);
+
+extern struct ospf6_route *ospf6_route_head (struct ospf6_route_table *table);
+extern struct ospf6_route *ospf6_route_next (struct ospf6_route *route);
+extern struct ospf6_route *ospf6_route_best_next (struct ospf6_route *route);
+
+extern struct ospf6_route *ospf6_route_match_head (struct prefix *prefix,
+                                            struct ospf6_route_table *table);
+extern struct ospf6_route *ospf6_route_match_next (struct prefix *prefix,
+                                            struct ospf6_route *route);
+
+extern void ospf6_route_remove_all (struct ospf6_route_table *);
+extern struct ospf6_route_table *ospf6_route_table_create (int s, int t);
+extern void ospf6_route_table_delete (struct ospf6_route_table *);
+extern void ospf6_route_dump (struct ospf6_route_table *table);
+
+
+extern void ospf6_route_show (struct vty *vty, struct ospf6_route *route);
+extern void ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route);
+
+extern int ospf6_route_table_show (struct vty *, int, const char *[],
+                                   struct ospf6_route_table *);
+extern int ospf6_linkstate_table_show (struct vty *vty, int argc,
+                                       const char *argv[],
+                                       struct ospf6_route_table *table);
+
+extern void ospf6_brouter_show_header (struct vty *vty);
+extern void ospf6_brouter_show (struct vty *vty, struct ospf6_route *route);
+
+extern int config_write_ospf6_debug_route (struct vty *vty);
+extern void install_element_ospf6_debug_route (void);
+extern void ospf6_route_init (void);
+extern void ospf6_clean (void);
+
+#endif /* OSPF6_ROUTE_H */
+
diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c
new file mode 100644 (file)
index 0000000..266031e
--- /dev/null
@@ -0,0 +1,1183 @@
+/* OSPFv3 SNMP support
+ * Copyright (C) 2004 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "log.h"
+#include "vty.h"
+#include "linklist.h"
+#include "smux.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_message.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
+#include "ospf6d.h"
+#include "ospf6_snmp.h"
+
+/* OSPFv3-MIB */
+#define OSPFv3MIB 1,3,6,1,2,1,191
+
+/* OSPFv3 MIB General Group values. */
+#define OSPFv3ROUTERID                   1
+#define OSPFv3ADMINSTAT                  2
+#define OSPFv3VERSIONNUMBER              3
+#define OSPFv3AREABDRRTRSTATUS           4
+#define OSPFv3ASBDRRTRSTATUS             5
+#define OSPFv3ASSCOPELSACOUNT            6
+#define OSPFv3ASSCOPELSACHECKSUMSUM      7
+#define OSPFv3ORIGINATENEWLSAS           8
+#define OSPFv3RXNEWLSAS                  9
+#define OSPFv3EXTLSACOUNT               10
+#define OSPFv3EXTAREALSDBLIMIT          11
+#define OSPFv3EXITOVERFLOWINTERVAL      12
+#define OSPFv3DEMANDEXTENSIONS          13
+#define OSPFv3REFERENCEBANDWIDTH        14
+#define OSPFv3RESTARTSUPPORT            15
+#define OSPFv3RESTARTINTERVAL           16
+#define OSPFv3RESTARTSTRICTLSACHECKING  17
+#define OSPFv3RESTARTSTATUS             18
+#define OSPFv3RESTARTAGE                19
+#define OSPFv3RESTARTEXITREASON         20
+#define OSPFv3NOTIFICATIONENABLE        21
+#define OSPFv3STUBROUTERSUPPORT         22
+#define OSPFv3STUBROUTERADVERTISEMENT   23
+#define OSPFv3DISCONTINUITYTIME         24
+#define OSPFv3RESTARTTIME               25
+
+/* OSPFv3 MIB Area Table values: ospfv3AreaTable */
+#define OSPFv3IMPORTASEXTERN             2
+#define OSPFv3AREASPFRUNS                3
+#define OSPFv3AREABDRRTRCOUNT            4
+#define OSPFv3AREAASBDRRTRCOUNT          5
+#define OSPFv3AREASCOPELSACOUNT          6
+#define OSPFv3AREASCOPELSACKSUMSUM       7
+#define OSPFv3AREASUMMARY                8
+#define OSPFv3AREAROWSTATUS              9
+#define OSPFv3AREASTUBMETRIC            10
+#define OSPFv3AREANSSATRANSLATORROLE    11
+#define OSPFv3AREANSSATRANSLATORSTATE   12
+#define OSPFv3AREANSSATRANSLATORSTABINTERVAL    13
+#define OSPFv3AREANSSATRANSLATOREVENTS  14
+#define OSPFv3AREASTUBMETRICTYPE        15
+#define OSPFv3AREATEENABLED             16
+
+/* OSPFv3 MIB * Lsdb Table values: ospfv3*LsdbTable */
+#define OSPFv3WWLSDBSEQUENCE             1
+#define OSPFv3WWLSDBAGE                  2
+#define OSPFv3WWLSDBCHECKSUM             3
+#define OSPFv3WWLSDBADVERTISEMENT        4
+#define OSPFv3WWLSDBTYPEKNOWN            5
+
+/* Three first bits are to identify column */
+#define OSPFv3WWCOLUMN 0x7
+/* Then we use other bits to identify table */
+#define OSPFv3WWASTABLE   (1 << 3)
+#define OSPFv3WWAREATABLE (1 << 4)
+#define OSPFv3WWLINKTABLE (1 << 5)
+#define OSPFv3WWVIRTLINKTABLE (1 << 6)
+
+/* OSPFv3 MIB Host Table values: ospfv3HostTable */
+#define OSPFv3HOSTMETRIC                 3
+#define OSPFv3HOSTROWSTATUS              4
+#define OSPFv3HOSTAREAID                 5
+
+/* OSPFv3 MIB Interface Table values: ospfv3IfTable */
+#define OSPFv3IFAREAID                   3
+#define OSPFv3IFTYPE                     4
+#define OSPFv3IFADMINSTATUS              5
+#define OSPFv3IFRTRPRIORITY              6
+#define OSPFv3IFTRANSITDELAY             7
+#define OSPFv3IFRETRANSINTERVAL          8
+#define OSPFv3IFHELLOINTERVAL            9
+#define OSPFv3IFRTRDEADINTERVAL         10
+#define OSPFv3IFPOLLINTERVAL            11
+#define OSPFv3IFSTATE                   12
+#define OSPFv3IFDESIGNATEDROUTER        13
+#define OSPFv3IFBACKUPDESIGNATEDROUTER  14
+#define OSPFv3IFEVENTS                  15
+#define OSPFv3IFROWSTATUS               16
+#define OSPFv3IFDEMAND                  17
+#define OSPFv3IFMETRICVALUE             18
+#define OSPFv3IFLINKSCOPELSACOUNT       19
+#define OSPFv3IFLINKLSACKSUMSUM         20
+#define OSPFv3IFDEMANDNBRPROBE          21
+#define OSPFv3IFDEMANDNBRPROBERETRANSLIMIT 22
+#define OSPFv3IFDEMANDNBRPROBEINTERVAL  23
+#define OSPFv3IFTEDISABLED              24
+#define OSPFv3IFLINKLSASUPPRESSION      25
+
+/* OSPFv3 MIB Virtual Interface Table values: ospfv3VirtIfTable */
+#define OSPFv3VIRTIFINDEX           3
+#define OSPFv3VIRTIFINSTID          4
+#define OSPFv3VIRTIFTRANSITDELAY    5
+#define OSPFv3VIRTIFRETRANSINTERVAL 6
+#define OSPFv3VIRTIFHELLOINTERVAL   7
+#define OSPFv3VIRTIFRTRDEADINTERVAL 8
+#define OSPFv3VIRTIFSTATE           9
+#define OSPFv3VIRTIFEVENTS         10
+#define OSPFv3VIRTIFROWSTATUS      11
+#define OSPFv3VIRTIFLINKSCOPELSACOUNT 12
+#define OSPFv3VIRTIFLINKLSACKSUMSUM   13
+
+/* OSPFv3 MIB Neighbors Table values: ospfv3NbrTable */
+#define OSPFv3NBRADDRESSTYPE      4
+#define OSPFv3NBRADDRESS          5
+#define OSPFv3NBROPTIONS          6
+#define OSPFv3NBRPRIORITY         7
+#define OSPFv3NBRSTATE            8
+#define OSPFv3NBREVENTS           9
+#define OSPFv3NBRLSRETRANSQLEN   10
+#define OSPFv3NBRHELLOSUPPRESSED 11
+#define OSPFv3NBRIFID            12
+#define OSPFv3NBRRESTARTHELPERSTATUS     13
+#define OSPFv3NBRRESTARTHELPERAGE        14
+#define OSPFv3NBRRESTARTHELPEREXITREASON 15
+
+/* OSPFv3 MIB Configured Neighbors Table values: ospfv3CfgNbrTable */
+#define OSPFv3CFGNBRPRIORITY  5
+#define OSPFv3CFGNBRROWSTATUS 6
+
+/* OSPFv3 MIB Virtual Neighbors Table values: ospfv3VirtNbrTable */
+#define OSPFv3VIRTNBRIFINDEX          3
+#define OSPFv3VIRTNBRIFINSTID         4
+#define OSPFv3VIRTNBRADDRESSTYPE      5
+#define OSPFv3VIRTNBRADDRESS          6
+#define OSPFv3VIRTNBROPTIONS          7
+#define OSPFv3VIRTNBRSTATE            8
+#define OSPFv3VIRTNBREVENTS           9
+#define OSPFv3VIRTNBRLSRETRANSQLEN   10
+#define OSPFv3VIRTNBRHELLOSUPPRESSED 11
+#define OSPFv3VIRTNBRIFID            12
+#define OSPFv3VIRTNBRRESTARTHELPERSTATUS     13
+#define OSPFv3VIRTNBRRESTARTHELPERAGE        14
+#define OSPFv3VIRTNBRRESTARTHELPEREXITREASON 15
+
+/* OSPFv3 MIB Area Aggregate Table values: ospfv3AreaAggregateTable */
+#define OSPFv3AREAAGGREGATEROWSTATUS  6
+#define OSPFv3AREAAGGREGATEEFFECT     7
+#define OSPFv3AREAAGGREGATEROUTETAG   8
+
+/* SYNTAX Status from OSPF-MIB. */
+#define OSPF_STATUS_ENABLED  1
+#define OSPF_STATUS_DISABLED 2
+
+/* SNMP value hack. */
+#define COUNTER     ASN_COUNTER
+#define INTEGER     ASN_INTEGER
+#define GAUGE       ASN_GAUGE
+#define UNSIGNED    ASN_UNSIGNED
+#define TIMETICKS   ASN_TIMETICKS
+#define IPADDRESS   ASN_IPADDRESS
+#define STRING      ASN_OCTET_STR
+
+/* For return values e.g. SNMP_INTEGER macro */
+SNMP_LOCAL_VARIABLES
+
+/* OSPFv3-MIB instances. */
+oid ospfv3_oid [] = { OSPFv3MIB };
+oid ospfv3_trap_oid [] = { OSPFv3MIB, 0 };
+
+/* Hook functions. */
+static u_char *ospfv3GeneralGroup (struct variable *, oid *, size_t *,
+                                  int, size_t *, WriteMethod **);
+static u_char *ospfv3AreaEntry (struct variable *, oid *, size_t *,
+                               int, size_t *, WriteMethod **);
+static u_char *ospfv3WwLsdbEntry (struct variable *, oid *, size_t *,
+                                 int, size_t *, WriteMethod **);
+static u_char *ospfv3NbrEntry (struct variable *, oid *, size_t *,
+                              int, size_t *, WriteMethod **);
+static u_char *ospfv3IfEntry (struct variable *, oid *, size_t *,
+                             int, size_t *, WriteMethod **);
+
+struct variable ospfv3_variables[] =
+{
+  /* OSPF general variables */
+  {OSPFv3ROUTERID,             UNSIGNED,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 1}},
+  {OSPFv3ADMINSTAT,             INTEGER,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 2}},
+  {OSPFv3VERSIONNUMBER,         INTEGER,   RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 3}},
+  {OSPFv3AREABDRRTRSTATUS,      INTEGER,   RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 4}},
+  {OSPFv3ASBDRRTRSTATUS,        INTEGER,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 5}},
+  {OSPFv3ASSCOPELSACOUNT,       GAUGE,     RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 6}},
+  {OSPFv3ASSCOPELSACHECKSUMSUM,UNSIGNED,   RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 7}},
+  {OSPFv3ORIGINATENEWLSAS,      COUNTER,   RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 8}},
+  {OSPFv3RXNEWLSAS,             COUNTER,   RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 9}},
+  {OSPFv3EXTLSACOUNT,           GAUGE,     RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 10}},
+  {OSPFv3EXTAREALSDBLIMIT,      INTEGER,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 11}},
+  {OSPFv3EXITOVERFLOWINTERVAL, UNSIGNED,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 12}},
+  {OSPFv3DEMANDEXTENSIONS,      INTEGER,   RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 13}},
+  {OSPFv3REFERENCEBANDWIDTH,   UNSIGNED, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 14}},
+  {OSPFv3RESTARTSUPPORT,        INTEGER, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 15}},
+  {OSPFv3RESTARTINTERVAL,      UNSIGNED, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 16}},
+  {OSPFv3RESTARTSTRICTLSACHECKING, INTEGER, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 17}},
+  {OSPFv3RESTARTSTATUS,         INTEGER, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 18}},
+  {OSPFv3RESTARTAGE,           UNSIGNED, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 19}},
+  {OSPFv3RESTARTEXITREASON,     INTEGER, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 20}},
+  {OSPFv3NOTIFICATIONENABLE,    INTEGER, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 21}},
+  {OSPFv3STUBROUTERSUPPORT,     INTEGER, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 22}},
+  {OSPFv3STUBROUTERADVERTISEMENT, INTEGER, RWRITE, ospfv3GeneralGroup,
+   3, {1, 1, 23}},
+  {OSPFv3DISCONTINUITYTIME,     TIMETICKS, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 24}},
+  {OSPFv3RESTARTTIME,           TIMETICKS, RONLY,  ospfv3GeneralGroup,
+   3, {1, 1, 25}},
+
+  /* OSPFv3 Area Data Structure */
+  {OSPFv3IMPORTASEXTERN,        INTEGER,   RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 2}},
+  {OSPFv3AREASPFRUNS,           COUNTER,   RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 3}},
+  {OSPFv3AREABDRRTRCOUNT,       GAUGE,     RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 4}},
+  {OSPFv3AREAASBDRRTRCOUNT,     GAUGE,     RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 5}},
+  {OSPFv3AREASCOPELSACOUNT,     GAUGE,     RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 6}},
+  {OSPFv3AREASCOPELSACKSUMSUM, UNSIGNED,   RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 7}},
+  {OSPFv3AREASUMMARY,           INTEGER,   RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 8}},
+  {OSPFv3AREAROWSTATUS,         INTEGER,   RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 9}},
+  {OSPFv3AREASTUBMETRIC,        INTEGER,   RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 10}},
+  {OSPFv3AREANSSATRANSLATORROLE, INTEGER,  RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 11}},
+  {OSPFv3AREANSSATRANSLATORSTATE, INTEGER, RONLY,  ospfv3AreaEntry,
+   4, {1, 2, 1, 12}},
+  {OSPFv3AREANSSATRANSLATORSTABINTERVAL, UNSIGNED, RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 13}},
+  {OSPFv3AREANSSATRANSLATOREVENTS, COUNTER, RONLY, ospfv3AreaEntry,
+   4, {1, 2, 1, 14}},
+  {OSPFv3AREASTUBMETRICTYPE,    INTEGER, RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 15}},
+  {OSPFv3AREATEENABLED,         INTEGER, RWRITE, ospfv3AreaEntry,
+   4, {1, 2, 1, 16}},
+
+  /* OSPFv3 AS LSDB */
+  {OSPFv3WWLSDBSEQUENCE | OSPFv3WWASTABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 3, 1, 4}},
+  {OSPFv3WWLSDBAGE | OSPFv3WWASTABLE,           UNSIGNED,  RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 3, 1, 5}},
+  {OSPFv3WWLSDBCHECKSUM | OSPFv3WWASTABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 3, 1, 6}},
+  {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWASTABLE, STRING,    RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 3, 1, 7}},
+  {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWASTABLE,     INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 3, 1, 8}},
+
+  /* OSPFv3 Area LSDB */
+  {OSPFv3WWLSDBSEQUENCE | OSPFv3WWAREATABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 4, 1, 5}},
+  {OSPFv3WWLSDBAGE | OSPFv3WWAREATABLE,           UNSIGNED,  RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 4, 1, 6}},
+  {OSPFv3WWLSDBCHECKSUM | OSPFv3WWAREATABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 4, 1, 7}},
+  {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWAREATABLE, STRING,    RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 4, 1, 8}},
+  {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWAREATABLE,     INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 4, 1, 9}},
+
+  /* OSPFv3 Link LSDB */
+  {OSPFv3WWLSDBSEQUENCE | OSPFv3WWLINKTABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 5, 1, 6}},
+  {OSPFv3WWLSDBAGE | OSPFv3WWLINKTABLE,           UNSIGNED,  RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 5, 1, 7}},
+  {OSPFv3WWLSDBCHECKSUM | OSPFv3WWLINKTABLE,      INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 5, 1, 8}},
+  {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWLINKTABLE, STRING,    RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 5, 1, 9}},
+  {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWLINKTABLE,     INTEGER,   RONLY,  ospfv3WwLsdbEntry,
+   4, {1, 5, 1, 10}},
+
+  /* OSPFv3 interfaces */
+  {OSPFv3IFAREAID,             UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 3}},
+  {OSPFv3IFTYPE,               INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 4}},
+  {OSPFv3IFADMINSTATUS,        INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 5}},
+  {OSPFv3IFRTRPRIORITY,        INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 6}},
+  {OSPFv3IFTRANSITDELAY,       UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 7}},
+  {OSPFv3IFRETRANSINTERVAL,    UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 8}},
+  {OSPFv3IFHELLOINTERVAL,      INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 9}},
+  {OSPFv3IFRTRDEADINTERVAL,    UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 10}},
+  {OSPFv3IFPOLLINTERVAL,       UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 11}},
+  {OSPFv3IFSTATE,              INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 12}},
+  {OSPFv3IFDESIGNATEDROUTER,   UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 13}},
+  {OSPFv3IFBACKUPDESIGNATEDROUTER, UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 14}},
+  {OSPFv3IFEVENTS,             COUNTER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 15}},
+  {OSPFv3IFROWSTATUS,          INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 16}},
+  {OSPFv3IFDEMAND,             INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 17}},
+  {OSPFv3IFMETRICVALUE,        INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 18}},
+  {OSPFv3IFLINKSCOPELSACOUNT,  GAUGE,    RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 19}},
+  {OSPFv3IFLINKLSACKSUMSUM,    UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 20}},
+  {OSPFv3IFDEMANDNBRPROBE,     INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 21}},
+  {OSPFv3IFDEMANDNBRPROBERETRANSLIMIT, UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 22}},
+  {OSPFv3IFDEMANDNBRPROBEINTERVAL, UNSIGNED, RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 23}},
+  {OSPFv3IFTEDISABLED,         INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 24}},
+  {OSPFv3IFLINKLSASUPPRESSION, INTEGER,  RONLY, ospfv3IfEntry,
+   4, {1, 7, 1, 25}},
+
+  /* OSPFv3 neighbors */
+  {OSPFv3NBRADDRESSTYPE,        INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 4}},
+  {OSPFv3NBRADDRESS,            STRING,    RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 5}},
+  {OSPFv3NBROPTIONS,            INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 6}},
+  {OSPFv3NBRPRIORITY,           INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 7}},
+  {OSPFv3NBRSTATE,              INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 8}},
+  {OSPFv3NBREVENTS,             COUNTER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 9}},
+  {OSPFv3NBRLSRETRANSQLEN,        GAUGE,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 10}},
+  {OSPFv3NBRHELLOSUPPRESSED,    INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 11}},
+  {OSPFv3NBRIFID,               INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 12}},
+  {OSPFv3NBRRESTARTHELPERSTATUS, INTEGER,  RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 13}},
+  {OSPFv3NBRRESTARTHELPERAGE,  UNSIGNED,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 14}},
+  {OSPFv3NBRRESTARTHELPEREXITREASON, INTEGER, RONLY, ospfv3NbrEntry,
+   4, {1, 9, 1, 15}},
+};
+
+static u_char *
+ospfv3GeneralGroup (struct variable *v, oid *name, size_t *length,
+                    int exact, size_t *var_len, WriteMethod **write_method)
+{
+  u_int16_t sum;
+  u_int32_t count;
+  struct ospf6_lsa *lsa = NULL;
+
+  /* Check whether the instance identifier is valid */
+  if (smux_header_generic (v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic)
+    {
+    case OSPFv3ROUTERID:
+      /* Router-ID of this OSPF instance. */
+      if (ospf6)
+       return SNMP_INTEGER (ntohl (ospf6->router_id));
+      return SNMP_INTEGER (0);
+    case OSPFv3ADMINSTAT:
+      if (ospf6)
+       return SNMP_INTEGER (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)?
+                            OSPF_STATUS_DISABLED:OSPF_STATUS_ENABLED);
+      return SNMP_INTEGER (OSPF_STATUS_DISABLED);
+    case OSPFv3VERSIONNUMBER:
+      return SNMP_INTEGER (3);
+    case OSPFv3AREABDRRTRSTATUS:
+      if (ospf6)
+       return SNMP_INTEGER (ospf6_is_router_abr (ospf6)?SNMP_TRUE:SNMP_FALSE);
+      return SNMP_INTEGER (SNMP_FALSE);
+    case OSPFv3ASBDRRTRSTATUS:
+      if (ospf6)
+       return SNMP_INTEGER (ospf6_asbr_is_asbr (ospf6)?SNMP_TRUE:SNMP_FALSE);
+      return SNMP_INTEGER (SNMP_FALSE);
+    case OSPFv3ASSCOPELSACOUNT:
+      if (ospf6)
+       return SNMP_INTEGER (ospf6->lsdb->count);
+      return SNMP_INTEGER (0);
+    case OSPFv3ASSCOPELSACHECKSUMSUM:
+      if (ospf6)
+        {
+          for (sum = 0, lsa = ospf6_lsdb_head (ospf6->lsdb);
+               lsa;
+               lsa = ospf6_lsdb_next (lsa))
+            sum += ntohs (lsa->header->checksum);
+          return SNMP_INTEGER (sum);
+        }
+      return SNMP_INTEGER (0);
+    case OSPFv3ORIGINATENEWLSAS:
+      return SNMP_INTEGER (0); /* Don't know where to get this value... */
+    case OSPFv3RXNEWLSAS:
+      return SNMP_INTEGER (0); /* Don't know where to get this value... */
+    case OSPFv3EXTLSACOUNT:
+      if (ospf6)
+        {
+          for (count = 0, lsa = ospf6_lsdb_type_head (htons (OSPF6_LSTYPE_AS_EXTERNAL),
+                                                      ospf6->lsdb);
+               lsa;
+               lsa = ospf6_lsdb_type_next (htons (OSPF6_LSTYPE_AS_EXTERNAL),
+                                           lsa))
+            count += 1;
+          return SNMP_INTEGER (count);
+        }
+      return SNMP_INTEGER (0);
+    case OSPFv3EXTAREALSDBLIMIT:
+      return SNMP_INTEGER (-1);
+    case OSPFv3EXITOVERFLOWINTERVAL:
+      return SNMP_INTEGER (0); /* Not supported */
+    case OSPFv3DEMANDEXTENSIONS:
+      return SNMP_INTEGER (0); /* Not supported */
+    case OSPFv3REFERENCEBANDWIDTH:
+      if (ospf6)
+        return SNMP_INTEGER (ospf6->ref_bandwidth);
+      /* Otherwise, like for "not implemented". */
+    case OSPFv3RESTARTSUPPORT:
+    case OSPFv3RESTARTINTERVAL:
+    case OSPFv3RESTARTSTRICTLSACHECKING:
+    case OSPFv3RESTARTSTATUS:
+    case OSPFv3RESTARTAGE:
+    case OSPFv3RESTARTEXITREASON:
+    case OSPFv3NOTIFICATIONENABLE:
+    case OSPFv3STUBROUTERSUPPORT:
+    case OSPFv3STUBROUTERADVERTISEMENT:
+    case OSPFv3DISCONTINUITYTIME:
+    case OSPFv3RESTARTTIME:
+      /* TODO: Not implemented */
+      return NULL;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfv3AreaEntry (struct variable *v, oid *name, size_t *length,
+                 int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf6_area *oa, *area = NULL;
+  struct ospf6_lsa *lsa = NULL;
+  u_int32_t area_id = 0;
+  u_int32_t count;
+  u_int16_t sum;
+  struct listnode *node;
+  unsigned int len;
+  char a[16];
+  struct ospf6_route *ro;
+
+  if (ospf6 == NULL)
+    return NULL;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  len = *length - v->namelen;
+  len = (len >= 1 ? 1 : 0);
+  if (exact && len != 1)
+    return NULL;
+  if (len)
+    area_id  = htonl (name[v->namelen]);
+
+  inet_ntop (AF_INET, &area_id, a, sizeof (a));
+  zlog_debug ("SNMP access by area: %s, exact=%d len=%d length=%lu",
+             a, exact, len, (u_long)*length);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+    {
+      if (area == NULL)
+        {
+          if (len == 0) /* return first area entry */
+            area = oa;
+          else if (exact && ntohl (oa->area_id) == ntohl (area_id))
+            area = oa;
+          else if (ntohl (oa->area_id) > ntohl (area_id))
+            area = oa;
+        }
+    }
+
+  if (area == NULL)
+    return NULL;
+
+  *length = v->namelen + 1;
+  name[v->namelen] = ntohl (area->area_id);
+
+  inet_ntop (AF_INET, &area->area_id, a, sizeof (a));
+  zlog_debug ("SNMP found area: %s, exact=%d len=%d length=%lu",
+             a, exact, len, (u_long)*length);
+
+  switch (v->magic)
+    {
+    case OSPFv3IMPORTASEXTERN:
+      /* No NSSA support */
+      return SNMP_INTEGER (IS_AREA_STUB(area)?2:1);
+    case OSPFv3AREASPFRUNS:
+      return SNMP_INTEGER (area->spf_calculation);
+    case OSPFv3AREABDRRTRCOUNT:
+    case OSPFv3AREAASBDRRTRCOUNT:
+      count = 0;
+      for (ro = ospf6_route_head (ospf6->brouter_table); ro;
+          ro = ospf6_route_next (ro))
+        {
+          if (ntohl (ro->path.area_id) != ntohl (area->area_id)) continue;
+          if (v->magic == OSPFv3AREABDRRTRCOUNT &&
+              CHECK_FLAG (ro->path.router_bits, OSPF6_ROUTER_BIT_B))
+            count++;
+          if (v->magic == OSPFv3AREAASBDRRTRCOUNT &&
+              CHECK_FLAG (ro->path.router_bits, OSPF6_ROUTER_BIT_E))
+            count++;
+        }
+      return SNMP_INTEGER (count);
+    case OSPFv3AREASCOPELSACOUNT:
+      return SNMP_INTEGER (area->lsdb->count);
+    case OSPFv3AREASCOPELSACKSUMSUM:
+      for (sum = 0, lsa = ospf6_lsdb_head (area->lsdb);
+          lsa;
+          lsa = ospf6_lsdb_next (lsa))
+       sum += ntohs (lsa->header->checksum);
+      return SNMP_INTEGER (sum);
+    case OSPFv3AREASUMMARY:
+      return SNMP_INTEGER (2); /* sendAreaSummary */
+    case OSPFv3AREAROWSTATUS:
+      return SNMP_INTEGER (1); /* Active */
+    case OSPFv3AREASTUBMETRIC:
+    case OSPFv3AREANSSATRANSLATORROLE:
+    case OSPFv3AREANSSATRANSLATORSTATE:
+    case OSPFv3AREANSSATRANSLATORSTABINTERVAL:
+    case OSPFv3AREANSSATRANSLATOREVENTS:
+    case OSPFv3AREASTUBMETRICTYPE:
+    case OSPFv3AREATEENABLED:
+      /* Not implemented. */
+      return NULL;
+    }
+  return NULL;
+}
+
+static int
+if_icmp_func (struct interface *ifp1, struct interface *ifp2)
+{
+  return (ifp1->ifindex - ifp2->ifindex);
+}
+
+static u_char *
+ospfv3WwLsdbEntry (struct variable *v, oid *name, size_t *length,
+                     int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf6_lsa *lsa = NULL;
+  ifindex_t ifindex, area_id, id, instid, adv_router;
+  u_int16_t type;
+  int len;
+  oid *offset;
+  int offsetlen;
+  struct ospf6_area *oa = NULL;
+  struct listnode *node;
+  struct interface *iif;
+  struct ospf6_interface *oi = NULL;
+  struct list *ifslist;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  instid = ifindex = area_id = type = id = adv_router = 0;
+
+  /* Check OSPFv3 instance. */
+  if (ospf6 == NULL)
+    return NULL;
+
+  /* Get variable length. */
+  offset = name + v->namelen;
+  offsetlen = *length - v->namelen;
+
+  if (exact && (v->magic & OSPFv3WWASTABLE) && offsetlen != 3)
+    return NULL;
+  if (exact && (v->magic & OSPFv3WWAREATABLE) && offsetlen != 4)
+    return NULL;
+  if (exact && (v->magic & OSPFv3WWLINKTABLE) && offsetlen != 5)
+    return NULL;
+
+  if (v->magic & OSPFv3WWLINKTABLE)
+    {
+      /* Parse ifindex */
+      len = (offsetlen < 1 ? 0 : 1);
+      if (len)
+        ifindex = *offset;
+      offset += len;
+      offsetlen -= len;
+
+      /* Parse instance ID */
+      len = (offsetlen < 1 ? 0 : 1);
+      if (len)
+        instid = *offset;
+      offset += len;
+      offsetlen -= len;
+    }
+  else if (v->magic & OSPFv3WWAREATABLE)
+    {
+      /* Parse area-id */
+      len = (offsetlen < 1 ? 0 : 1);
+      if (len)
+        area_id = htonl (*offset);
+      offset += len;
+      offsetlen -= len;
+    }
+
+  /* Parse type */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    type = htons (*offset);
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse Router-ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    adv_router = htonl (*offset);
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse LS-ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    id = htonl (*offset);
+  offset += len;
+  offsetlen -= len;
+
+  if (exact)
+    {
+      if (v->magic & OSPFv3WWASTABLE)
+        {
+          lsa = ospf6_lsdb_lookup (type, id, adv_router, ospf6->lsdb);
+        }
+      else if (v->magic & OSPFv3WWAREATABLE)
+        {
+          oa = ospf6_area_lookup (area_id, ospf6);
+          if (!oa) return NULL;
+          lsa = ospf6_lsdb_lookup (type, id, adv_router, oa->lsdb);
+        }
+      else if (v->magic & OSPFv3WWLINKTABLE)
+        {
+          oi = ospf6_interface_lookup_by_ifindex (ifindex);
+          if (!oi || oi->instance_id != instid) return NULL;
+          lsa = ospf6_lsdb_lookup (type, id, adv_router, oi->lsdb);
+        }
+    }
+  else
+    {
+      if (v->magic & OSPFv3WWASTABLE)
+       {
+         if (ospf6->lsdb->count)
+           lsa = ospf6_lsdb_lookup_next (type, id, adv_router,
+                                         ospf6->lsdb);
+       }
+      else if (v->magic & OSPFv3WWAREATABLE)
+       for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+          {
+            if (oa->area_id < area_id)
+              continue;
+
+            if (oa->lsdb->count)
+              lsa = ospf6_lsdb_lookup_next (type, id, adv_router,
+                                            oa->lsdb);
+            if (lsa) break;
+            type = 0;
+            id = 0;
+            adv_router = 0;
+          }
+      else if (v->magic & OSPFv3WWLINKTABLE)
+        {
+          /* We build a sorted list of interfaces */
+          ifslist = list_new ();
+          if (!ifslist) return NULL;
+          ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
+          for (ALL_LIST_ELEMENTS_RO (iflist, node, iif))
+            listnode_add_sort (ifslist, iif);
+          
+          for (ALL_LIST_ELEMENTS_RO (ifslist, node, iif))
+            {
+              if (!iif->ifindex) continue;
+              oi = ospf6_interface_lookup_by_ifindex (iif->ifindex);
+              if (!oi) continue;
+              if (iif->ifindex < ifindex) continue;
+              if (oi->instance_id < instid) continue;
+              
+              if (oi->lsdb->count)
+                lsa = ospf6_lsdb_lookup_next (type, id, adv_router,
+                                            oi->lsdb);
+              if (lsa) break;
+              type = 0;
+              id = 0;
+              adv_router = 0;
+              oi = NULL;
+            }
+
+          list_delete_all_node (ifslist);
+        }
+    }
+
+  if (! lsa)
+      return NULL;
+
+  /* Add indexes */
+  if (v->magic & OSPFv3WWASTABLE)
+    {
+      *length = v->namelen + 3;
+      offset = name + v->namelen;
+    }
+  else if (v->magic & OSPFv3WWAREATABLE)
+    {
+      *length = v->namelen + 4;
+      offset = name + v->namelen;
+      *offset = ntohl (oa->area_id);
+      offset++;
+    }
+  else if (v->magic & OSPFv3WWLINKTABLE)
+    {
+      *length = v->namelen + 5;
+      offset = name + v->namelen;
+      *offset = oi->interface->ifindex;
+      offset++;
+      *offset = oi->instance_id;
+      offset++;
+    }
+  *offset = ntohs (lsa->header->type);
+  offset++;
+  *offset = ntohl (lsa->header->adv_router);
+  offset++;
+  *offset = ntohl (lsa->header->id);
+  offset++;
+
+  /* Return the current value of the variable */
+  switch (v->magic & OSPFv3WWCOLUMN)
+    {
+    case OSPFv3WWLSDBSEQUENCE:
+      return SNMP_INTEGER (ntohl (lsa->header->seqnum));
+      break;
+    case OSPFv3WWLSDBAGE:
+      ospf6_lsa_age_current (lsa);
+      return SNMP_INTEGER (ntohs (lsa->header->age));
+      break;
+    case OSPFv3WWLSDBCHECKSUM:
+      return SNMP_INTEGER (ntohs (lsa->header->checksum));
+      break;
+    case OSPFv3WWLSDBADVERTISEMENT:
+      *var_len = ntohs (lsa->header->length);
+      return (u_char *) lsa->header;
+      break;
+    case OSPFv3WWLSDBTYPEKNOWN:
+      return SNMP_INTEGER (OSPF6_LSA_IS_KNOWN (lsa->header->type) ?
+                           SNMP_TRUE : SNMP_FALSE);
+      break;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfv3IfEntry (struct variable *v, oid *name, size_t *length,
+               int exact, size_t *var_len, WriteMethod **write_method)
+{
+  ifindex_t ifindex = 0;
+  unsigned int instid = 0;
+  struct ospf6_interface *oi = NULL;
+  struct ospf6_lsa *lsa = NULL;
+  struct interface      *iif;
+  struct listnode *i;
+  struct list *ifslist;
+  oid *offset;
+  int offsetlen, len;
+  u_int32_t sum;
+
+  if (smux_header_table (v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Check OSPFv3 instance. */
+  if (ospf6 == NULL)
+    return NULL;
+
+  /* Get variable length. */
+  offset = name + v->namelen;
+  offsetlen = *length - v->namelen;
+
+  if (exact && offsetlen != 2)
+    return NULL;
+
+  /* Parse if index */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    ifindex = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse instance ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    instid = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  if (exact)
+    {
+      oi = ospf6_interface_lookup_by_ifindex (ifindex);
+      if (!oi || oi->instance_id != instid) return NULL;
+    }
+  else
+    {
+      /* We build a sorted list of interfaces */
+      ifslist = list_new ();
+      if (!ifslist) return NULL;
+      ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
+      for (ALL_LIST_ELEMENTS_RO (iflist, i, iif))
+       listnode_add_sort (ifslist, iif);
+
+      for (ALL_LIST_ELEMENTS_RO (ifslist, i, iif))
+        {
+          if (!iif->ifindex) continue;
+          oi = ospf6_interface_lookup_by_ifindex (iif->ifindex);
+          if (!oi) continue;
+          if (iif->ifindex > ifindex ||
+              (iif->ifindex == ifindex &&
+               (oi->instance_id > instid)))
+            break;
+          oi = NULL;
+        }
+
+      list_delete_all_node (ifslist);
+    }
+
+  if (!oi) return NULL;
+
+  /* Add Index (IfIndex, IfInstId) */
+  *length = v->namelen + 2;
+  offset = name + v->namelen;
+  *offset = oi->interface->ifindex;
+  offset++;
+  *offset = oi->instance_id;
+  offset++;
+
+  /* Return the current value of the variable */
+  switch (v->magic)
+    {
+    case OSPFv3IFAREAID:
+      if (oi->area)
+       return SNMP_INTEGER (ntohl (oi->area->area_id));
+      break;
+    case OSPFv3IFTYPE:
+      if (if_is_broadcast (oi->interface))
+       return SNMP_INTEGER (1);
+      else if (if_is_pointopoint (oi->interface))
+       return SNMP_INTEGER (3);
+      else break;              /* Unknown, don't put anything */
+    case OSPFv3IFADMINSTATUS:
+      if (oi->area)
+       return SNMP_INTEGER (OSPF_STATUS_ENABLED);
+      return SNMP_INTEGER (OSPF_STATUS_DISABLED);
+    case OSPFv3IFRTRPRIORITY:
+      return SNMP_INTEGER (oi->priority);
+    case OSPFv3IFTRANSITDELAY:
+      return SNMP_INTEGER (oi->transdelay);
+    case OSPFv3IFRETRANSINTERVAL:
+      return SNMP_INTEGER (oi->rxmt_interval);
+    case OSPFv3IFHELLOINTERVAL:
+      return SNMP_INTEGER (oi->hello_interval);
+    case OSPFv3IFRTRDEADINTERVAL:
+      return SNMP_INTEGER (oi->dead_interval);
+    case OSPFv3IFPOLLINTERVAL:
+      /* No support for NBMA */
+      break;
+    case OSPFv3IFSTATE:
+      return SNMP_INTEGER (oi->state);
+    case OSPFv3IFDESIGNATEDROUTER:
+      return SNMP_INTEGER (ntohl (oi->drouter));
+    case OSPFv3IFBACKUPDESIGNATEDROUTER:
+      return SNMP_INTEGER (ntohl (oi->bdrouter));
+    case OSPFv3IFEVENTS:
+      return SNMP_INTEGER (oi->state_change);
+    case OSPFv3IFROWSTATUS:
+      return SNMP_INTEGER (1);
+    case OSPFv3IFDEMAND:
+      return SNMP_INTEGER (SNMP_FALSE);
+    case OSPFv3IFMETRICVALUE:
+      return SNMP_INTEGER (oi->cost);
+    case OSPFv3IFLINKSCOPELSACOUNT:
+      return SNMP_INTEGER (oi->lsdb->count);
+    case OSPFv3IFLINKLSACKSUMSUM:
+      for (sum = 0, lsa = ospf6_lsdb_head (oi->lsdb);
+          lsa;
+          lsa = ospf6_lsdb_next (lsa))
+       sum += ntohs (lsa->header->checksum);
+      return SNMP_INTEGER (sum);
+    case OSPFv3IFDEMANDNBRPROBE:
+    case OSPFv3IFDEMANDNBRPROBERETRANSLIMIT:
+    case OSPFv3IFDEMANDNBRPROBEINTERVAL:
+    case OSPFv3IFTEDISABLED:
+    case OSPFv3IFLINKLSASUPPRESSION:
+      /* Not implemented. Only works if all the last ones are not
+        implemented! */
+      return NULL;
+    }
+
+  /* Try an internal getnext. Some columns are missing in this table. */
+  if (!exact && (name[*length-1] < MAX_SUBID))
+    return ospfv3IfEntry(v, name, length,
+                        exact, var_len, write_method);
+  return NULL;
+}
+
+static u_char *
+ospfv3NbrEntry (struct variable *v, oid *name, size_t *length,
+               int exact, size_t *var_len, WriteMethod **write_method)
+{
+  ifindex_t ifindex = 0;
+  unsigned int instid, rtrid;
+  struct ospf6_interface *oi = NULL;
+  struct ospf6_neighbor  *on = NULL;
+  struct interface      *iif;
+  struct listnode *i, *j;
+  struct list *ifslist;
+  oid *offset;
+  int offsetlen, len;
+
+  if (smux_header_table (v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  instid = rtrid = 0;
+
+  /* Check OSPFv3 instance. */
+  if (ospf6 == NULL)
+    return NULL;
+
+  /* Get variable length. */
+  offset = name + v->namelen;
+  offsetlen = *length - v->namelen;
+
+  if (exact && offsetlen != 3)
+    return NULL;
+
+  /* Parse if index */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    ifindex = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse instance ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    instid = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse router ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    rtrid = htonl (*offset);
+  offset += len;
+  offsetlen -= len;
+
+  if (exact)
+    {
+      oi = ospf6_interface_lookup_by_ifindex (ifindex);
+      if (!oi || oi->instance_id != instid) return NULL;
+      on = ospf6_neighbor_lookup (rtrid, oi);
+    }
+  else
+    {
+      /* We build a sorted list of interfaces */
+      ifslist = list_new ();
+      if (!ifslist) return NULL;
+      ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
+      for (ALL_LIST_ELEMENTS_RO (iflist, i, iif))
+       listnode_add_sort (ifslist, iif);
+
+      for (ALL_LIST_ELEMENTS_RO (ifslist, i, iif))
+        {
+          if (!iif->ifindex) continue;
+          oi = ospf6_interface_lookup_by_ifindex (iif->ifindex);
+          if (!oi) continue;
+          for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) {
+            if (iif->ifindex > ifindex ||
+                (iif->ifindex == ifindex &&
+                 (oi->instance_id > instid ||
+                  (oi->instance_id == instid &&
+                   ntohl (on->router_id) > ntohl (rtrid)))))
+              break;
+          }
+          if (on) break;
+          oi = NULL;
+          on = NULL;
+        }
+
+      list_delete_all_node (ifslist);
+    }
+
+  if (!oi || !on) return NULL;
+
+  /* Add Index (IfIndex, IfInstId, RtrId) */
+  *length = v->namelen + 3;
+  offset = name + v->namelen;
+  *offset = oi->interface->ifindex;
+  offset++;
+  *offset = oi->instance_id;
+  offset++;
+  *offset = ntohl (on->router_id);
+  offset++;
+
+  /* Return the current value of the variable */
+  switch (v->magic)
+    {
+    case OSPFv3NBRADDRESSTYPE:
+      return SNMP_INTEGER (2); /* IPv6 only */
+    case OSPFv3NBRADDRESS:
+      *var_len = sizeof (struct in6_addr);
+      return (u_char *) &on->linklocal_addr;
+    case OSPFv3NBROPTIONS:
+      return SNMP_INTEGER (on->options[2]);
+    case OSPFv3NBRPRIORITY:
+      return SNMP_INTEGER (on->priority);
+    case OSPFv3NBRSTATE:
+      return SNMP_INTEGER (on->state);
+    case OSPFv3NBREVENTS:
+      return SNMP_INTEGER (on->state_change);
+    case OSPFv3NBRLSRETRANSQLEN:
+      return SNMP_INTEGER (on->retrans_list->count);
+    case OSPFv3NBRHELLOSUPPRESSED:
+      return SNMP_INTEGER (SNMP_FALSE);
+    case OSPFv3NBRIFID:
+      return SNMP_INTEGER (on->ifindex);
+    case OSPFv3NBRRESTARTHELPERSTATUS:
+    case OSPFv3NBRRESTARTHELPERAGE:
+    case OSPFv3NBRRESTARTHELPEREXITREASON:
+      /* Not implemented. Only works if all the last ones are not
+        implemented! */
+      return NULL;
+    }
+
+  return NULL;
+}
+
+/* OSPF Traps. */
+#define NBRSTATECHANGE      2
+#define IFSTATECHANGE      10
+
+static struct trap_object ospf6NbrTrapList[] =
+{
+  {-3, {1, 1, OSPFv3ROUTERID}},
+  {4, {1, 9, 1, OSPFv3NBRADDRESSTYPE}},
+  {4, {1, 9, 1, OSPFv3NBRADDRESS}},
+  {4, {1, 9, 1, OSPFv3NBRSTATE}}
+};
+
+static struct trap_object ospf6IfTrapList[] =
+{
+  {-3, {1, 1, OSPFv3ROUTERID}},
+  {4, {1, 7, 1, OSPFv3IFSTATE}},
+  {4, {1, 7, 1, OSPFv3IFADMINSTATUS}},
+  {4, {1, 7, 1, OSPFv3IFAREAID}}
+};
+
+void
+ospf6TrapNbrStateChange (struct ospf6_neighbor *on)
+{
+  oid index[3];
+
+  index[0] = on->ospf6_if->interface->ifindex;
+  index[1] = on->ospf6_if->instance_id;
+  index[2] = ntohl (on->router_id);
+
+  smux_trap (ospfv3_variables, sizeof ospfv3_variables / sizeof (struct variable),
+            ospfv3_trap_oid, sizeof ospfv3_trap_oid / sizeof (oid),
+            ospfv3_oid, sizeof ospfv3_oid / sizeof (oid),
+             index,  3,
+             ospf6NbrTrapList, 
+             sizeof ospf6NbrTrapList / sizeof (struct trap_object),
+             NBRSTATECHANGE);
+}
+
+void
+ospf6TrapIfStateChange (struct ospf6_interface *oi)
+{
+  oid index[2];
+
+  index[0] = oi->interface->ifindex;
+  index[1] = oi->instance_id;
+
+  smux_trap (ospfv3_variables, sizeof ospfv3_variables / sizeof (struct variable),
+            ospfv3_trap_oid, sizeof ospfv3_trap_oid / sizeof (oid),
+            ospfv3_oid, sizeof ospfv3_oid / sizeof (oid),
+             index,  2,
+             ospf6IfTrapList, 
+             sizeof ospf6IfTrapList / sizeof (struct trap_object),
+             IFSTATECHANGE);
+}
+
+/* Register OSPFv3-MIB. */
+void
+ospf6_snmp_init (struct thread_master *master)
+{
+  smux_init (master);
+  REGISTER_MIB ("OSPFv3MIB", ospfv3_variables, variable, ospfv3_oid);
+}
+
+#endif /* HAVE_SNMP */
+
diff --git a/ospf6d/ospf6_snmp.h b/ospf6d/ospf6_snmp.h
new file mode 100644 (file)
index 0000000..fa1b0c3
--- /dev/null
@@ -0,0 +1,31 @@
+/* OSPFv3 SNMP support
+ * Copyright (C) 2004 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_SNMP_H
+#define OSPF6_SNMP_H
+
+extern void ospf6TrapNbrStateChange (struct ospf6_neighbor *);
+extern void ospf6TrapIfStateChange (struct ospf6_interface *);
+extern void ospf6_snmp_init (struct thread_master *);
+
+#endif /*OSPF6_SNMP_H*/
+
+
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
new file mode 100644 (file)
index 0000000..b6dbd0d
--- /dev/null
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+/* Shortest Path First calculation for OSPFv3 */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "prefix.h"
+#include "pqueue.h"
+#include "linklist.h"
+#include "thread.h"
+
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_area.h"
+#include "ospf6_spf.h"
+#include "ospf6_intra.h"
+#include "ospf6_interface.h"
+#include "ospf6d.h"
+#include "ospf6_abr.h"
+
+unsigned char conf_debug_ospf6_spf = 0;
+
+static int
+ospf6_vertex_cmp (void *a, void *b)
+{
+  struct ospf6_vertex *va = (struct ospf6_vertex *) a;
+  struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
+
+  /* ascending order */
+  if (va->cost != vb->cost)
+    return (va->cost - vb->cost);
+  return (va->hops - vb->hops);
+}
+
+static int
+ospf6_vertex_id_cmp (void *a, void *b)
+{
+  struct ospf6_vertex *va = (struct ospf6_vertex *) a;
+  struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
+  int ret = 0;
+
+  ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) -
+        ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id));
+  if (ret)
+    return ret;
+
+  ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) -
+        ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id));
+  return ret;
+}
+
+static struct ospf6_vertex *
+ospf6_vertex_create (struct ospf6_lsa *lsa)
+{
+  struct ospf6_vertex *v;
+  int i;
+
+  v = (struct ospf6_vertex *)
+    XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex));
+
+  /* type */
+  if (ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER)
+    v->type = OSPF6_VERTEX_TYPE_ROUTER;
+  else if (ntohs (lsa->header->type) == OSPF6_LSTYPE_NETWORK)
+    v->type = OSPF6_VERTEX_TYPE_NETWORK;
+  else
+    assert (0);
+
+  /* vertex_id */
+  ospf6_linkstate_prefix (lsa->header->adv_router, lsa->header->id,
+                          &v->vertex_id);
+
+  /* name */
+  ospf6_linkstate_prefix2str (&v->vertex_id, v->name, sizeof (v->name));
+
+  /* Associated LSA */
+  v->lsa = lsa;
+
+  /* capability bits + options */
+  v->capability = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header));
+  v->options[0] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 1);
+  v->options[1] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 2);
+  v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 3);
+
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
+    ospf6_nexthop_clear (&v->nexthop[i]);
+
+  v->parent = NULL;
+  v->child_list = list_new ();
+  v->child_list->cmp = ospf6_vertex_id_cmp;
+
+  return v;
+}
+
+static void
+ospf6_vertex_delete (struct ospf6_vertex *v)
+{
+  list_delete (v->child_list);
+  XFREE (MTYPE_OSPF6_VERTEX, v);
+}
+
+static struct ospf6_lsa *
+ospf6_lsdesc_lsa (caddr_t lsdesc, struct ospf6_vertex *v)
+{
+  struct ospf6_lsa *lsa;
+  u_int16_t type = 0;
+  u_int32_t id = 0, adv_router = 0;
+
+  if (VERTEX_IS_TYPE (NETWORK, v))
+    {
+      type = htons (OSPF6_LSTYPE_ROUTER);
+      id = htonl (0);
+      adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc);
+    }
+  else
+    {
+      if (ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
+        {
+          type = htons (OSPF6_LSTYPE_ROUTER);
+          id = htonl (0);
+          adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
+        }
+      else if (ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, lsdesc))
+        {
+          type = htons (OSPF6_LSTYPE_NETWORK);
+          id = htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc));
+          adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
+        }
+    }
+
+  lsa = ospf6_lsdb_lookup (type, id, adv_router, v->area->lsdb);
+
+  if (IS_OSPF6_DEBUG_SPF (PROCESS))
+    {
+      char ibuf[16], abuf[16];
+      inet_ntop (AF_INET, &id, ibuf, sizeof (ibuf));
+      inet_ntop (AF_INET, &adv_router, abuf, sizeof (abuf));
+      if (lsa)
+        zlog_debug ("  Link to: %s", lsa->name);
+      else
+        zlog_debug ("  Link to: [%s Id:%s Adv:%s] No LSA",
+                   ospf6_lstype_name (type), ibuf, abuf);
+    }
+
+  return lsa;
+}
+
+static char *
+ospf6_lsdesc_backlink (struct ospf6_lsa *lsa,
+                       caddr_t lsdesc, struct ospf6_vertex *v)
+{
+  caddr_t backlink, found = NULL;
+  int size;
+
+  size = (OSPF6_LSA_IS_TYPE (ROUTER, lsa) ?
+          sizeof (struct ospf6_router_lsdesc) :
+          sizeof (struct ospf6_network_lsdesc));
+  for (backlink = OSPF6_LSA_HEADER_END (lsa->header) + 4;
+       backlink + size <= OSPF6_LSA_END (lsa->header); backlink += size)
+    {
+      assert (! (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
+                 VERTEX_IS_TYPE (NETWORK, v)));
+
+      if (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
+          NETWORK_LSDESC_GET_NBR_ROUTERID (backlink)
+            == v->lsa->header->adv_router)
+        found = backlink;
+      else if (VERTEX_IS_TYPE (NETWORK, v) &&
+          ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, backlink) &&
+          ROUTER_LSDESC_GET_NBR_ROUTERID (backlink)
+            == v->lsa->header->adv_router &&
+          ROUTER_LSDESC_GET_NBR_IFID (backlink)
+            == ntohl (v->lsa->header->id))
+        found = backlink;
+      else
+        {
+          if (! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, backlink) ||
+              ! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
+            continue;
+          if (ROUTER_LSDESC_GET_NBR_IFID (backlink) !=
+              ROUTER_LSDESC_GET_IFID (lsdesc) ||
+              ROUTER_LSDESC_GET_NBR_IFID (lsdesc) !=
+              ROUTER_LSDESC_GET_IFID (backlink))
+            continue;
+          if (ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) !=
+              v->lsa->header->adv_router ||
+              ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc) !=
+              lsa->header->adv_router)
+            continue;
+          found = backlink;
+        }
+    }
+
+  if (IS_OSPF6_DEBUG_SPF (PROCESS))
+    zlog_debug ("  Backlink %s", (found ? "OK" : "FAIL"));
+
+  return found;
+}
+
+static void
+ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v,
+                    caddr_t lsdesc)
+{
+  int i;
+  ifindex_t ifindex;
+  struct ospf6_interface *oi;
+  u_int16_t type;
+  u_int32_t adv_router;
+  struct ospf6_lsa *lsa;
+  struct ospf6_link_lsa *link_lsa;
+  char buf[64];
+
+  assert (VERTEX_IS_TYPE (ROUTER, w));
+  ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? v->nexthop[0].ifindex :
+             /* v is the local router & the interface_id is a local ifindex */
+             (ifindex_t) ROUTER_LSDESC_GET_IFID (lsdesc));
+  assert (ifindex >= 0);
+  
+  oi = ospf6_interface_lookup_by_ifindex (ifindex);
+  if (oi == NULL)
+    {
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+        zlog_debug ("Can't find interface in SPF: ifindex %d", ifindex);
+      return;
+    }
+
+  type = htons (OSPF6_LSTYPE_LINK);
+  adv_router = (VERTEX_IS_TYPE (NETWORK, v) ?
+                NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc) :
+                ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc));
+
+  i = 0;
+  for (lsa = ospf6_lsdb_type_router_head (type, adv_router, oi->lsdb); lsa;
+       lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa))
+    {
+      if (VERTEX_IS_TYPE (ROUTER, v) &&
+          htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)) != lsa->header->id)
+        continue;
+
+      link_lsa = (struct ospf6_link_lsa *) OSPF6_LSA_HEADER_END (lsa->header);
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+        {
+          inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf));
+          zlog_debug ("  nexthop %s from %s", buf, lsa->name);
+        }
+
+      if (i < OSPF6_MULTI_PATH_LIMIT)
+        {
+          memcpy (&w->nexthop[i].address, &link_lsa->linklocal_addr,
+                  sizeof (struct in6_addr));
+          w->nexthop[i].ifindex = ifindex;
+          i++;
+        }
+    }
+
+  if (i == 0 && IS_OSPF6_DEBUG_SPF (PROCESS))
+    zlog_debug ("No nexthop for %s found", w->name);
+}
+
+static int
+ospf6_spf_install (struct ospf6_vertex *v,
+                   struct ospf6_route_table *result_table)
+{
+  struct ospf6_route *route;
+  int i, j;
+  struct ospf6_vertex *prev;
+
+  if (IS_OSPF6_DEBUG_SPF (PROCESS))
+    zlog_debug ("SPF install %s hops %d cost %d",
+               v->name, v->hops, v->cost);
+
+  route = ospf6_route_lookup (&v->vertex_id, result_table);
+  if (route && route->path.cost < v->cost)
+    {
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+        zlog_debug ("  already installed with lower cost (%d), ignore",
+                   route->path.cost);
+      ospf6_vertex_delete (v);
+      return -1;
+    }
+  else if (route && route->path.cost == v->cost)
+    {
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+        zlog_debug ("  another path found, merge");
+
+      for (i = 0; i < OSPF6_MULTI_PATH_LIMIT &&
+           ospf6_nexthop_is_set (&v->nexthop[i]); i++)
+        {
+          for (j = 0; j < OSPF6_MULTI_PATH_LIMIT; j++)
+            {
+              if (ospf6_nexthop_is_set (&route->nexthop[j]))
+                {
+                  if (ospf6_nexthop_is_same (&route->nexthop[j],
+                                             &v->nexthop[i]))
+                    break;
+                  else
+                    continue;
+                }
+              ospf6_nexthop_copy (&route->nexthop[j], &v->nexthop[i]);
+              break;
+            }
+        }
+
+      prev = (struct ospf6_vertex *) route->route_option;
+      assert (prev->hops <= v->hops);
+      ospf6_vertex_delete (v);
+
+      return -1;
+    }
+
+  /* There should be no case where candidate being installed (variable
+     "v") is closer than the one in the SPF tree (variable "route").
+     In the case something has gone wrong with the behavior of
+     Priority-Queue. */
+
+  /* the case where the route exists already is handled and returned
+     up to here. */
+  assert (route == NULL);
+
+  route = ospf6_route_create ();
+  memcpy (&route->prefix, &v->vertex_id, sizeof (struct prefix));
+  route->type = OSPF6_DEST_TYPE_LINKSTATE;
+  route->path.type = OSPF6_PATH_TYPE_INTRA;
+  route->path.origin.type = v->lsa->header->type;
+  route->path.origin.id = v->lsa->header->id;
+  route->path.origin.adv_router = v->lsa->header->adv_router;
+  route->path.metric_type = 1;
+  route->path.cost = v->cost;
+  route->path.cost_e2 = v->hops;
+  route->path.router_bits = v->capability;
+  route->path.options[0] = v->options[0];
+  route->path.options[1] = v->options[1];
+  route->path.options[2] = v->options[2];
+
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT &&
+       ospf6_nexthop_is_set (&v->nexthop[i]); i++)
+    ospf6_nexthop_copy (&route->nexthop[i], &v->nexthop[i]);
+
+  if (v->parent)
+    listnode_add_sort (v->parent->child_list, v);
+  route->route_option = v;
+
+  ospf6_route_add (route, result_table);
+  return 0;
+}
+
+void
+ospf6_spf_table_finish (struct ospf6_route_table *result_table)
+{
+  struct ospf6_route *route, *nroute;
+  struct ospf6_vertex *v;
+  for (route = ospf6_route_head (result_table); route;
+       route = nroute)
+    {
+      nroute = ospf6_route_next (route);
+      v = (struct ospf6_vertex *) route->route_option;
+      ospf6_vertex_delete (v);
+      ospf6_route_remove (route, result_table);
+    }
+}
+
+static const char *ospf6_spf_reason_str[] =
+  {
+    "R+",
+    "R-",
+    "N+",
+    "N-",
+    "L+",
+    "L-",
+    "R*",
+    "N*",
+  };
+
+void ospf6_spf_reason_string (unsigned int reason, char *buf, int size)
+{
+  size_t bit;
+  int len = 0;
+
+  if (!buf)
+    return;
+
+  for (bit = 0; bit < array_size(ospf6_spf_reason_str); bit++)
+    {
+      if ((reason & (1 << bit)) && (len < size))
+       {
+         len += snprintf((buf + len), (size - len), "%s%s",
+                         (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]);
+       }
+    }
+}
+
+/* RFC2328 16.1.  Calculating the shortest-path tree for an area */
+/* RFC2740 3.8.1.  Calculating the shortest path tree for an area */
+void
+ospf6_spf_calculation (u_int32_t router_id,
+                       struct ospf6_route_table *result_table,
+                       struct ospf6_area *oa)
+{
+  struct pqueue *candidate_list;
+  struct ospf6_vertex *root, *v, *w;
+  int i;
+  int size;
+  caddr_t lsdesc;
+  struct ospf6_lsa *lsa;
+
+  ospf6_spf_table_finish (result_table);
+
+  /* Install the calculating router itself as the root of the SPF tree */
+  /* construct root vertex */
+  lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_ROUTER), htonl (0),
+                           router_id, oa->lsdb);
+  if (lsa == NULL)
+    return;
+
+  /* initialize */
+  candidate_list = pqueue_create ();
+  candidate_list->cmp = ospf6_vertex_cmp;
+
+  root = ospf6_vertex_create (lsa);
+  root->area = oa;
+  root->cost = 0;
+  root->hops = 0;
+  root->nexthop[0].ifindex = 0; /* loopbak I/F is better ... */
+  inet_pton (AF_INET6, "::1", &root->nexthop[0].address);
+
+  /* Actually insert root to the candidate-list as the only candidate */
+  pqueue_enqueue (root, candidate_list);
+
+  /* Iterate until candidate-list becomes empty */
+  while (candidate_list->size)
+    {
+      /* get closest candidate from priority queue */
+      v = pqueue_dequeue (candidate_list);
+
+      /* installing may result in merging or rejecting of the vertex */
+      if (ospf6_spf_install (v, result_table) < 0)
+        continue;
+
+      /* Skip overloaded routers */
+      if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) &&
+          ospf6_router_is_stub_router (v->lsa)))
+       continue;
+
+      /* For each LS description in the just-added vertex V's LSA */
+      size = (VERTEX_IS_TYPE (ROUTER, v) ?
+              sizeof (struct ospf6_router_lsdesc) :
+              sizeof (struct ospf6_network_lsdesc));
+      for (lsdesc = OSPF6_LSA_HEADER_END (v->lsa->header) + 4;
+           lsdesc + size <= OSPF6_LSA_END (v->lsa->header); lsdesc += size)
+        {
+          lsa = ospf6_lsdesc_lsa (lsdesc, v);
+          if (lsa == NULL)
+            continue;
+
+          if (! ospf6_lsdesc_backlink (lsa, lsdesc, v))
+            continue;
+
+          w = ospf6_vertex_create (lsa);
+          w->area = oa;
+          w->parent = v;
+          if (VERTEX_IS_TYPE (ROUTER, v))
+            {
+              w->cost = v->cost + ROUTER_LSDESC_GET_METRIC (lsdesc);
+              w->hops = v->hops + (VERTEX_IS_TYPE (NETWORK, w) ? 0 : 1);
+            }
+          else /* NETWORK */
+            {
+              w->cost = v->cost;
+              w->hops = v->hops + 1;
+            }
+
+          /* nexthop calculation */
+          if (w->hops == 0)
+            w->nexthop[0].ifindex = ROUTER_LSDESC_GET_IFID (lsdesc);
+          else if (w->hops == 1 && v->hops == 0)
+            ospf6_nexthop_calc (w, v, lsdesc);
+          else
+            {
+              for (i = 0; i < OSPF6_MULTI_PATH_LIMIT &&
+                   ospf6_nexthop_is_set (&v->nexthop[i]); i++)
+                ospf6_nexthop_copy (&w->nexthop[i], &v->nexthop[i]);
+            }
+
+          /* add new candidate to the candidate_list */
+          if (IS_OSPF6_DEBUG_SPF (PROCESS))
+            zlog_debug ("  New candidate: %s hops %d cost %d",
+                       w->name, w->hops, w->cost);
+          pqueue_enqueue (w, candidate_list);
+        }
+    }
+
+  pqueue_delete (candidate_list);
+
+  oa->spf_calculation++;
+}
+
+static void
+ospf6_spf_log_database (struct ospf6_area *oa)
+{
+  char *p, *end, buffer[256];
+  struct listnode *node;
+  struct ospf6_interface *oi;
+
+  p = buffer;
+  end = buffer + sizeof (buffer);
+
+  snprintf (p, end - p, "SPF on DB (#LSAs):");
+  p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
+  snprintf (p, end - p, " Area %s: %d", oa->name, oa->lsdb->count);
+  p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
+
+  for (ALL_LIST_ELEMENTS_RO (oa->if_list, node, oi))
+    {
+      snprintf (p, end - p, " I/F %s: %d",
+                oi->interface->name, oi->lsdb->count);
+      p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
+    }
+
+  zlog_debug ("%s", buffer);
+}
+
+static int
+ospf6_spf_calculation_thread (struct thread *t)
+{
+  struct ospf6_area *oa;
+  struct ospf6 *ospf6;
+  struct timeval start, end, runtime;
+  struct listnode *node;
+  struct ospf6_route *route;
+  int areas_processed = 0;
+  char rbuf[32];
+
+  ospf6 = (struct ospf6 *)THREAD_ARG (t);
+  ospf6->t_spf_calc = NULL;
+
+  /* execute SPF calculation */
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &start);
+
+  for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa))
+    {
+
+      if (oa == ospf6->backbone)
+       continue;
+
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+       zlog_debug ("SPF calculation for Area %s", oa->name);
+      if (IS_OSPF6_DEBUG_SPF (DATABASE))
+       ospf6_spf_log_database (oa);
+
+      ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa);
+      ospf6_intra_route_calculation (oa);
+      ospf6_intra_brouter_calculation (oa);
+
+      areas_processed++;
+    }
+
+  if (ospf6->backbone)
+    {
+      if (IS_OSPF6_DEBUG_SPF (PROCESS))
+       zlog_debug ("SPF calculation for Backbone area %s",
+                   ospf6->backbone->name);
+      if (IS_OSPF6_DEBUG_SPF (DATABASE))
+       ospf6_spf_log_database(ospf6->backbone);
+
+      ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table,
+                           ospf6->backbone);
+      ospf6_intra_route_calculation(ospf6->backbone);
+      ospf6_intra_brouter_calculation(ospf6->backbone);
+      areas_processed++;
+    }
+
+  /* Redo summaries if required */
+  for (route = ospf6_route_head (ospf6->route_table); route;
+       route = ospf6_route_next (route))
+    ospf6_abr_originate_summary(route);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &end);
+  timersub (&end, &start, &runtime);
+
+  ospf6->ts_spf_duration = runtime;
+
+  ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf));
+
+  if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+    zlog_debug ("SPF runtime: %lld sec %lld usec",
+               (long long)runtime.tv_sec, (long long)runtime.tv_usec);
+
+  zlog_info("SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, "
+           "Reason: %s\n", areas_processed,
+           (long long)runtime.tv_sec, (long long)runtime.tv_usec,
+           rbuf);
+  ospf6->last_spf_reason = ospf6->spf_reason;
+  ospf6_reset_spf_reason(ospf6);
+  return 0;
+}
+
+/* Add schedule for SPF calculation.  To avoid frequenst SPF calc, we
+   set timer for SPF calc. */
+void
+ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason)
+{
+  unsigned long delay, elapsed, ht;
+  struct timeval now, result;
+
+  ospf6_set_spf_reason(ospf6, reason);
+
+  if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+    {
+      char rbuf[32];
+      ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf));
+      zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf);
+    }
+
+  /* OSPF instance does not exist. */
+  if (ospf6 == NULL)
+    return;
+
+  /* SPF calculation timer is already scheduled. */
+  if (ospf6->t_spf_calc)
+    {
+      if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+        zlog_debug ("SPF: calculation timer is already scheduled: %p",
+                    (void *)ospf6->t_spf_calc);
+      return;
+    }
+
+  /* XXX Monotic timers: we only care about relative time here. */
+  now = recent_relative_time ();
+  timersub (&now, &ospf6->ts_spf, &result);
+
+  elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
+  ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier;
+
+  if (ht > ospf6->spf_max_holdtime)
+    ht = ospf6->spf_max_holdtime;
+
+  /* Get SPF calculation delay time. */
+  if (elapsed < ht)
+    {
+      /* Got an event within the hold time of last SPF. We need to
+       * increase the hold_multiplier, if it's not already at/past
+       * maximum value, and wasn't already increased..
+       */
+      if (ht < ospf6->spf_max_holdtime)
+        ospf6->spf_hold_multiplier++;
+
+      /* always honour the SPF initial delay */
+      if ( (ht - elapsed) < ospf6->spf_delay)
+        delay = ospf6->spf_delay;
+      else
+        delay = ht - elapsed;
+    }
+  else
+    {
+      /* Event is past required hold-time of last SPF */
+      delay = ospf6->spf_delay;
+      ospf6->spf_hold_multiplier = 1;
+    }
+
+  if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
+    zlog_debug ("SPF: calculation timer delay = %ld", delay);
+
+  zlog_info ("SPF: Scheduled in %ld msec", delay);
+
+  ospf6->t_spf_calc =
+    thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay);
+}
+
+void
+ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest,
+                           struct ospf6_vertex *v)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_vertex *c;
+  char *next_prefix;
+  int len;
+  int restnum;
+
+  /* "prefix" is the space prefix of the display line */
+  vty_out (vty, "%s+-%s [%d]%s", prefix, v->name, v->cost, VNL);
+
+  len = strlen (prefix) + 4;
+  next_prefix = (char *) malloc (len);
+  if (next_prefix == NULL)
+    {
+      vty_out (vty, "malloc failed%s", VNL);
+      return;
+    }
+  snprintf (next_prefix, len, "%s%s", prefix, (rest ? "|  " : "   "));
+
+  restnum = listcount (v->child_list);
+  for (ALL_LIST_ELEMENTS (v->child_list, node, nnode, c))
+    {
+      restnum--;
+      ospf6_spf_display_subtree (vty, next_prefix, restnum, c);
+    }
+
+  free (next_prefix);
+}
+
+DEFUN (debug_ospf6_spf_process,
+       debug_ospf6_spf_process_cmd,
+       "debug ospf6 spf process",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug SPF Calculation\n"
+       "Debug Detailed SPF Process\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_PROCESS;
+  OSPF6_DEBUG_SPF_ON (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf6_spf_time,
+       debug_ospf6_spf_time_cmd,
+       "debug ospf6 spf time",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug SPF Calculation\n"
+       "Measure time taken by SPF Calculation\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_TIME;
+  OSPF6_DEBUG_SPF_ON (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf6_spf_database,
+       debug_ospf6_spf_database_cmd,
+       "debug ospf6 spf database",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug SPF Calculation\n"
+       "Log number of LSAs at SPF Calculation time\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_DATABASE;
+  OSPF6_DEBUG_SPF_ON (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_spf_process,
+       no_debug_ospf6_spf_process_cmd,
+       "no debug ospf6 spf process",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Quit Debugging SPF Calculation\n"
+       "Quit Debugging Detailed SPF Process\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_PROCESS;
+  OSPF6_DEBUG_SPF_OFF (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_spf_time,
+       no_debug_ospf6_spf_time_cmd,
+       "no debug ospf6 spf time",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Quit Debugging SPF Calculation\n"
+       "Quit Measuring time taken by SPF Calculation\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_TIME;
+  OSPF6_DEBUG_SPF_OFF (level);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_spf_database,
+       no_debug_ospf6_spf_database_cmd,
+       "no debug ospf6 spf database",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug SPF Calculation\n"
+       "Quit Logging number of LSAs at SPF Calculation time\n"
+      )
+{
+  unsigned char level = 0;
+  level = OSPF6_DEBUG_SPF_DATABASE;
+  OSPF6_DEBUG_SPF_OFF (level);
+  return CMD_SUCCESS;
+}
+
+static int
+ospf6_timers_spf_set (struct vty *vty, unsigned int delay,
+                     unsigned int hold,
+                     unsigned int max)
+{
+  struct ospf6 *ospf = vty->index;
+
+  ospf->spf_delay = delay;
+  ospf->spf_holdtime = hold;
+  ospf->spf_max_holdtime = max;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_timers_throttle_spf,
+       ospf6_timers_throttle_spf_cmd,
+       "timers throttle spf <0-600000> <0-600000> <0-600000>",
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF6 SPF timers\n"
+       "Delay (msec) from first change received till SPF calculation\n"
+       "Initial hold time (msec) between consecutive SPF calculations\n"
+       "Maximum hold time (msec)\n")
+{
+  unsigned int delay, hold, max;
+
+  if (argc != 3)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000);
+
+  return ospf6_timers_spf_set (vty, delay, hold, max);
+}
+
+DEFUN (no_ospf6_timers_throttle_spf,
+       no_ospf6_timers_throttle_spf_cmd,
+       "no timers throttle spf",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF6 SPF timers\n")
+{
+  return ospf6_timers_spf_set (vty,
+                              OSPF_SPF_DELAY_DEFAULT,
+                              OSPF_SPF_HOLDTIME_DEFAULT,
+                              OSPF_SPF_MAX_HOLDTIME_DEFAULT);
+}
+
+int
+config_write_ospf6_debug_spf (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_SPF (PROCESS))
+    vty_out (vty, "debug ospf6 spf process%s", VNL);
+  if (IS_OSPF6_DEBUG_SPF (TIME))
+    vty_out (vty, "debug ospf6 spf time%s", VNL);
+  if (IS_OSPF6_DEBUG_SPF (DATABASE))
+    vty_out (vty, "debug ospf6 spf database%s", VNL);
+  return 0;
+}
+
+void
+ospf6_spf_config_write (struct vty *vty)
+{
+
+  if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
+      ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
+      ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT)
+    vty_out (vty, " timers throttle spf %d %d %d%s",
+            ospf6->spf_delay, ospf6->spf_holdtime,
+            ospf6->spf_max_holdtime, VTY_NEWLINE);
+
+}
+
+void
+install_element_ospf6_debug_spf (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_spf_process_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_spf_time_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_spf_database_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_spf_process_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_spf_time_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_spf_database_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_spf_process_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_spf_time_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_spf_database_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_spf_process_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_spf_time_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_spf_database_cmd);
+}
+
+void
+ospf6_spf_init (void)
+{
+  install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
+}
diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h
new file mode 100644 (file)
index 0000000..b3481dc
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_SPF_H
+#define OSPF6_SPF_H
+
+#include "ospf6_top.h"
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_spf;
+#define OSPF6_DEBUG_SPF_PROCESS   0x01
+#define OSPF6_DEBUG_SPF_TIME      0x02
+#define OSPF6_DEBUG_SPF_DATABASE  0x04
+#define OSPF6_DEBUG_SPF_ON(level) \
+  (conf_debug_ospf6_spf |= (level))
+#define OSPF6_DEBUG_SPF_OFF(level) \
+  (conf_debug_ospf6_spf &= ~(level))
+#define IS_OSPF6_DEBUG_SPF(level) \
+  (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_ ## level)
+
+/* Transit Vertex */
+struct ospf6_vertex
+{
+  /* type of this vertex */
+  u_int8_t type;
+
+  /* Vertex Identifier */
+  struct prefix vertex_id;
+
+  /* Identifier String */
+  char name[128];
+
+  /* Associated Area */
+  struct ospf6_area *area;
+
+  /* Associated LSA */
+  struct ospf6_lsa *lsa;
+
+  /* Distance from Root (i.e. Cost) */
+  u_int32_t cost;
+
+  /* Router hops to this node */
+  u_char hops;
+
+  /* nexthops to this node */
+  struct ospf6_nexthop nexthop[OSPF6_MULTI_PATH_LIMIT];
+
+  /* capability bits */
+  u_char capability;
+
+  /* Optional capabilities */
+  u_char options[3];
+
+  /* For tree display */
+  struct ospf6_vertex *parent;
+  struct list *child_list;
+};
+
+#define OSPF6_VERTEX_TYPE_ROUTER  0x01
+#define OSPF6_VERTEX_TYPE_NETWORK 0x02
+#define VERTEX_IS_TYPE(t, v) \
+  ((v)->type == OSPF6_VERTEX_TYPE_ ## t ? 1 : 0)
+
+/* What triggered the SPF? */
+#define OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED         (1 << 0)
+#define OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED       (1 << 1)
+#define OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED        (1 << 2)
+#define OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED      (1 << 3)
+#define OSPF6_SPF_FLAGS_LINK_LSA_ADDED           (1 << 4)
+#define OSPF6_SPF_FLAGS_LINK_LSA_REMOVED         (1 << 5)
+#define OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED    (1 << 6)
+#define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED   (1 << 7)
+
+static inline void
+ospf6_set_spf_reason (struct ospf6* ospf, unsigned int reason)
+{
+  ospf->spf_reason |= reason;
+}
+
+static inline void
+ospf6_reset_spf_reason (struct ospf6 *ospf)
+{
+  ospf->spf_reason = 0;
+}
+
+static inline unsigned int
+ospf6_lsadd_to_spf_reason (struct ospf6_lsa *lsa)
+{
+  unsigned int reason = 0;
+
+  switch (ntohs (lsa->header->type))
+    {
+    case OSPF6_LSTYPE_ROUTER:
+      reason = OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED;
+      break;
+    case OSPF6_LSTYPE_NETWORK:
+      reason = OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED;
+      break;
+    case OSPF6_LSTYPE_LINK:
+      reason = OSPF6_SPF_FLAGS_LINK_LSA_ADDED;
+      break;
+    default:
+      break;
+    }
+  return (reason);
+}
+
+static inline unsigned int
+ospf6_lsremove_to_spf_reason (struct ospf6_lsa *lsa)
+{
+  unsigned int reason = 0;
+
+  switch (ntohs (lsa->header->type))
+    {
+    case OSPF6_LSTYPE_ROUTER:
+      reason = OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED;
+      break;
+    case OSPF6_LSTYPE_NETWORK:
+      reason = OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED;
+      break;
+    case OSPF6_LSTYPE_LINK:
+      reason = OSPF6_SPF_FLAGS_LINK_LSA_REMOVED;
+      break;
+    default:
+      break;
+    }
+  return (reason);
+}
+
+extern void ospf6_spf_table_finish (struct ospf6_route_table *result_table);
+extern void ospf6_spf_calculation (u_int32_t router_id,
+                                   struct ospf6_route_table *result_table,
+                                   struct ospf6_area *oa);
+extern void ospf6_spf_schedule (struct ospf6 *ospf, unsigned int reason);
+
+extern void ospf6_spf_display_subtree (struct vty *vty, const char *prefix,
+                                       int rest, struct ospf6_vertex *v);
+
+extern void ospf6_spf_config_write (struct vty *vty);
+extern int config_write_ospf6_debug_spf (struct vty *vty);
+extern void install_element_ospf6_debug_spf (void);
+extern void ospf6_spf_init (void);
+extern void ospf6_spf_reason_string (unsigned int reason, char *buf, int size);
+
+#endif /* OSPF6_SPF_H */
+
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
new file mode 100644 (file)
index 0000000..7ec4309
--- /dev/null
@@ -0,0 +1,1463 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "vty.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "table.h"
+#include "thread.h"
+#include "command.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_message.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_zebra.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+#include "ospf6_asbr.h"
+#include "ospf6_abr.h"
+#include "ospf6_intra.h"
+#include "ospf6_spf.h"
+#include "ospf6d.h"
+
+/* global ospf6d variable */
+struct ospf6 *ospf6;
+
+static void ospf6_disable (struct ospf6 *o);
+
+static void
+ospf6_top_lsdb_hook_add (struct ospf6_lsa *lsa)
+{
+  switch (ntohs (lsa->header->type))
+    {
+      case OSPF6_LSTYPE_AS_EXTERNAL:
+        ospf6_asbr_lsa_add (lsa);
+        break;
+
+      default:
+        break;
+    }
+}
+
+static void
+ospf6_top_lsdb_hook_remove (struct ospf6_lsa *lsa)
+{
+  switch (ntohs (lsa->header->type))
+    {
+      case OSPF6_LSTYPE_AS_EXTERNAL:
+        ospf6_asbr_lsa_remove (lsa);
+        break;
+
+      default:
+        break;
+    }
+}
+
+static void
+ospf6_top_route_hook_add (struct ospf6_route *route)
+{
+  ospf6_abr_originate_summary (route);
+  ospf6_zebra_route_update_add (route);
+}
+
+static void
+ospf6_top_route_hook_remove (struct ospf6_route *route)
+{
+  ospf6_abr_originate_summary (route);
+  ospf6_zebra_route_update_remove (route);
+}
+
+static void
+ospf6_top_brouter_hook_add (struct ospf6_route *route)
+{
+  ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix));
+  ospf6_asbr_lsentry_add (route);
+  ospf6_abr_originate_summary (route);
+}
+
+static void
+ospf6_top_brouter_hook_remove (struct ospf6_route *route)
+{
+  ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix));
+  ospf6_asbr_lsentry_remove (route);
+  ospf6_abr_originate_summary (route);
+}
+
+static struct ospf6 *
+ospf6_create (void)
+{
+  struct ospf6 *o;
+
+  o = XCALLOC (MTYPE_OSPF6_TOP, sizeof (struct ospf6));
+
+  /* initialize */
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &o->starttime);
+  o->area_list = list_new ();
+  o->area_list->cmp = ospf6_area_cmp;
+  o->lsdb = ospf6_lsdb_create (o);
+  o->lsdb_self = ospf6_lsdb_create (o);
+  o->lsdb->hook_add = ospf6_top_lsdb_hook_add;
+  o->lsdb->hook_remove = ospf6_top_lsdb_hook_remove;
+
+  o->spf_delay = OSPF_SPF_DELAY_DEFAULT;
+  o->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT;
+  o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT;
+  o->spf_hold_multiplier = 1;
+
+  o->route_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, ROUTES);
+  o->route_table->scope = o;
+  o->route_table->hook_add = ospf6_top_route_hook_add;
+  o->route_table->hook_remove = ospf6_top_route_hook_remove;
+
+  o->brouter_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, BORDER_ROUTERS);
+  o->brouter_table->scope = o;
+  o->brouter_table->hook_add = ospf6_top_brouter_hook_add;
+  o->brouter_table->hook_remove = ospf6_top_brouter_hook_remove;
+
+  o->external_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, EXTERNAL_ROUTES);
+  o->external_table->scope = o;
+
+  o->external_id_table = route_table_init ();
+
+  o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH;
+
+  o->distance_table = route_table_init ();
+
+  return o;
+}
+
+void
+ospf6_delete (struct ospf6 *o)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+
+  ospf6_disable (ospf6);
+
+  for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
+    ospf6_area_delete (oa);
+
+
+  list_delete (o->area_list);
+
+  ospf6_lsdb_delete (o->lsdb);
+  ospf6_lsdb_delete (o->lsdb_self);
+
+  ospf6_route_table_delete (o->route_table);
+  ospf6_route_table_delete (o->brouter_table);
+
+  ospf6_route_table_delete (o->external_table);
+  route_table_finish (o->external_id_table);
+
+  ospf6_distance_reset (o);
+  route_table_finish (o->distance_table);
+
+  XFREE (MTYPE_OSPF6_TOP, o);
+}
+
+static void
+__attribute__((unused))
+ospf6_enable (struct ospf6 *o)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+
+  if (CHECK_FLAG (o->flag, OSPF6_DISABLED))
+    {
+      UNSET_FLAG (o->flag, OSPF6_DISABLED);
+      for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
+        ospf6_area_enable (oa);
+    }
+}
+
+static void
+ospf6_disable (struct ospf6 *o)
+{
+  struct listnode *node, *nnode;
+  struct ospf6_area *oa;
+
+  if (! CHECK_FLAG (o->flag, OSPF6_DISABLED))
+    {
+      SET_FLAG (o->flag, OSPF6_DISABLED);
+      
+      for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa))
+        ospf6_area_disable (oa);
+
+      /* XXX: This also changes persistent settings */
+      ospf6_asbr_redistribute_reset();
+
+      ospf6_lsdb_remove_all (o->lsdb);
+      ospf6_route_remove_all (o->route_table);
+      ospf6_route_remove_all (o->brouter_table);
+
+      THREAD_OFF(o->maxage_remover);
+      THREAD_OFF(o->t_spf_calc);
+      THREAD_OFF(o->t_ase_calc);
+    }
+}
+
+static int
+ospf6_maxage_remover (struct thread *thread)
+{
+  struct ospf6 *o = (struct ospf6 *) THREAD_ARG (thread);
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  struct ospf6_neighbor *on;
+  struct listnode *i, *j, *k;
+  int reschedule = 0;
+
+  o->maxage_remover = (struct thread *) NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on))
+            {
+              if (on->state != OSPF6_NEIGHBOR_EXCHANGE &&
+                  on->state != OSPF6_NEIGHBOR_LOADING)
+                 continue;
+
+             ospf6_maxage_remove (o);
+              return 0;
+            }
+        }
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+       {
+         if (ospf6_lsdb_maxage_remover (oi->lsdb))
+           {
+             reschedule = 1;
+           }
+       }
+      
+      if (ospf6_lsdb_maxage_remover (oa->lsdb))
+       {
+           reschedule = 1;
+       }
+    }
+
+  if (ospf6_lsdb_maxage_remover (o->lsdb))
+    {
+      reschedule = 1;
+    }
+
+  if (reschedule)
+    {
+      ospf6_maxage_remove (o);
+    }
+
+  return 0;
+}
+
+void
+ospf6_maxage_remove (struct ospf6 *o)
+{
+  if (o && ! o->maxage_remover)
+    o->maxage_remover = thread_add_timer (master, ospf6_maxage_remover, o,
+                                         OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT);
+}
+
+/* start ospf6 */
+DEFUN (router_ospf6,
+       router_ospf6_cmd,
+       "router ospf6",
+       ROUTER_STR
+       OSPF6_STR)
+{
+  if (ospf6 == NULL)
+    ospf6 = ospf6_create ();
+
+  /* set current ospf point. */
+  vty->node = OSPF6_NODE;
+  vty->index = ospf6;
+
+  return CMD_SUCCESS;
+}
+
+/* stop ospf6 */
+DEFUN (no_router_ospf6,
+       no_router_ospf6_cmd,
+       "no router ospf6",
+       NO_STR
+       OSPF6_ROUTER_STR)
+{
+  if (ospf6 == NULL)
+    vty_out (vty, "OSPFv3 is not configured%s", VNL);
+  else
+    {
+      ospf6_delete (ospf6);
+      ospf6 = NULL;
+    }
+
+  /* return to config node . */
+  vty->node = CONFIG_NODE;
+  vty->index = NULL;
+
+  return CMD_SUCCESS;
+}
+
+/* change Router_ID commands. */
+DEFUN (ospf6_router_id,
+       ospf6_router_id_cmd,
+       "router-id A.B.C.D",
+       "Configure OSPF Router-ID\n"
+       V4NOTATION_STR)
+{
+  int ret;
+  u_int32_t router_id;
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  ret = inet_pton (AF_INET, argv[0], &router_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "malformed OSPF Router-ID: %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  o->router_id_static = router_id;
+  if (o->router_id  == 0)
+    o->router_id  = router_id;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_log_adjacency_changes,
+       ospf6_log_adjacency_changes_cmd,
+       "log-adjacency-changes",
+       "Log changes in adjacency state\n")
+{
+  struct ospf6 *ospf6 = vty->index;
+
+  SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_log_adjacency_changes_detail,
+       ospf6_log_adjacency_changes_detail_cmd,
+       "log-adjacency-changes detail",
+              "Log changes in adjacency state\n"
+       "Log all state changes\n")
+{
+  struct ospf6 *ospf6 = vty->index;
+
+  SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
+  SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_log_adjacency_changes,
+       no_ospf6_log_adjacency_changes_cmd,
+       "no log-adjacency-changes",
+              NO_STR
+       "Log changes in adjacency state\n")
+{
+  struct ospf6 *ospf6 = vty->index;
+
+  UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL);
+  UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_log_adjacency_changes_detail,
+       no_ospf6_log_adjacency_changes_detail_cmd,
+       "no log-adjacency-changes detail",
+              NO_STR
+              "Log changes in adjacency state\n"
+       "Log all state changes\n")
+{
+  struct ospf6 *ospf6 = vty->index;
+
+  UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance,
+       ospf6_distance_cmd,
+       "distance <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_all = atoi (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_distance,
+       no_ospf6_distance_cmd,
+       "no distance <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_all = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_distance_ospf6,
+       no_ospf6_distance_ospf6_cmd,
+       "no distance ospf6",
+       NO_STR
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "OSPF6 Distance\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = 0;
+  o->distance_inter = 0;
+  o->distance_external = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_intra,
+       ospf6_distance_ospf6_intra_cmd,
+       "distance ospf6 intra-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = atoi (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_intra_inter,
+       ospf6_distance_ospf6_intra_inter_cmd,
+       "distance ospf6 intra-area <1-255> inter-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = atoi (argv[0]);
+  o->distance_inter = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_intra_external,
+       ospf6_distance_ospf6_intra_external_cmd,
+       "distance ospf6 intra-area <1-255> external <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = atoi (argv[0]);
+  o->distance_external = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_intra_inter_external,
+       ospf6_distance_ospf6_intra_inter_external_cmd,
+       "distance ospf6 intra-area <1-255> inter-area <1-255> external <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = atoi (argv[0]);
+  o->distance_inter = atoi (argv[1]);
+  o->distance_external = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_intra_external_inter,
+       ospf6_distance_ospf6_intra_external_inter_cmd,
+       "distance ospf6 intra-area <1-255> external <1-255> inter-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_intra = atoi (argv[0]);
+  o->distance_external = atoi (argv[1]);
+  o->distance_inter = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_inter,
+       ospf6_distance_ospf6_inter_cmd,
+       "distance ospf6 inter-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_inter = atoi (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_inter_intra,
+       ospf6_distance_ospf6_inter_intra_cmd,
+       "distance ospf6 inter-area <1-255> intra-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_inter = atoi (argv[0]);
+  o->distance_intra = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_inter_external,
+       ospf6_distance_ospf6_inter_external_cmd,
+       "distance ospf6 inter-area <1-255> external <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_inter = atoi (argv[0]);
+  o->distance_external = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_inter_intra_external,
+       ospf6_distance_ospf6_inter_intra_external_cmd,
+       "distance ospf6 inter-area <1-255> intra-area <1-255> external <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_inter = atoi (argv[0]);
+  o->distance_intra = atoi (argv[1]);
+  o->distance_external = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_inter_external_intra,
+       ospf6_distance_ospf6_inter_external_intra_cmd,
+       "distance ospf6 inter-area <1-255> external <1-255> intra-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_inter = atoi (argv[0]);
+  o->distance_external = atoi (argv[1]);
+  o->distance_intra = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_external,
+       ospf6_distance_ospf6_external_cmd,
+       "distance ospf6 external <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_external = atoi (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_external_intra,
+       ospf6_distance_ospf6_external_intra_cmd,
+       "distance ospf6 external <1-255> intra-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_external = atoi (argv[0]);
+  o->distance_intra = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_external_inter,
+       ospf6_distance_ospf6_external_inter_cmd,
+       "distance ospf6 external <1-255> inter-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_external = atoi (argv[0]);
+  o->distance_inter = atoi (argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_external_intra_inter,
+       ospf6_distance_ospf6_external_intra_inter_cmd,
+       "distance ospf6 external <1-255> intra-area <1-255> inter-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_external = atoi (argv[0]);
+  o->distance_intra = atoi (argv[1]);
+  o->distance_inter = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_ospf6_external_inter_intra,
+       ospf6_distance_ospf6_external_inter_intra_cmd,
+       "distance ospf6 external <1-255> inter-area <1-255> intra-area <1-255>",
+       "Define an administrative distance\n"
+       "OSPF6 Administrative distance\n"
+       "External routes\n"
+       "Distance for external routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  o->distance_external = atoi (argv[0]);
+  o->distance_inter = atoi (argv[1]);
+  o->distance_intra = atoi (argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_source,
+       ospf6_distance_source_cmd,
+       "distance <1-255> X:X::X:X/M",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  ospf6_distance_set (vty, o, argv[0], argv[1], NULL);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_distance_source,
+       no_ospf6_distance_source_cmd,
+       "no distance <1-255> X:X::X:X/M",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+  
+  /* XXX: distance arg seems to be irrelevant */
+  ospf6_distance_unset (vty, o, argv[1], NULL);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_distance_source_access_list,
+       ospf6_distance_source_access_list_cmd,
+       "distance <1-255> X:X::X:X/M WORD",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  ospf6_distance_set (vty, o, argv[0], argv[1], argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_distance_source_access_list,
+       no_ospf6_distance_source_access_list_cmd,
+       "no distance <1-255> X:X::X:X/M WORD",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  struct ospf6 *o;
+
+  o = (struct ospf6 *) vty->index;
+
+  ospf6_distance_unset (vty, o, argv[1], argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_interface_area,
+       ospf6_interface_area_cmd,
+       "interface IFNAME area A.B.C.D",
+       "Enable routing on an IPv6 interface\n"
+       IFNAME_STR
+       "Specify the OSPF6 area ID\n"
+       "OSPF6 area ID in IPv4 address notation\n"
+      )
+{
+  struct ospf6 *o;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  struct interface *ifp;
+  u_int32_t area_id;
+
+  o = (struct ospf6 *) vty->index;
+
+  /* find/create ospf6 interface */
+  ifp = if_get_by_name (argv[0]);
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    oi = ospf6_interface_create (ifp);
+  if (oi->area)
+    {
+      vty_out (vty, "%s already attached to Area %s%s",
+               oi->interface->name, oi->area->name, VNL);
+      return CMD_SUCCESS;
+    }
+
+  /* parse Area-ID */
+  if (inet_pton (AF_INET, argv[1], &area_id) != 1)
+    {
+      vty_out (vty, "Invalid Area-ID: %s%s", argv[1], VNL);
+      return CMD_SUCCESS;
+    }
+
+  /* find/create ospf6 area */
+  oa = ospf6_area_lookup (area_id, o);
+  if (oa == NULL)
+    oa = ospf6_area_create (area_id, o);
+
+  /* attach interface to area */
+  listnode_add (oa->if_list, oi); /* sort ?? */
+  oi->area = oa;
+
+  SET_FLAG (oa->flag, OSPF6_AREA_ENABLE);
+
+  /* ospf6 process is currently disabled, not much more to do */
+  if (CHECK_FLAG (o->flag, OSPF6_DISABLED))
+    return CMD_SUCCESS;
+
+  /* start up */
+  ospf6_interface_enable (oi);
+
+  /* If the router is ABR, originate summary routes */
+  if (ospf6_is_router_abr (o))
+    ospf6_abr_enable_area (oa);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_interface_area,
+       no_ospf6_interface_area_cmd,
+       "no interface IFNAME area A.B.C.D",
+       NO_STR
+       "Disable routing on an IPv6 interface\n"
+       IFNAME_STR
+       "Specify the OSPF6 area ID\n"
+       "OSPF6 area ID in IPv4 address notation\n"
+       )
+{
+  struct ospf6_interface *oi;
+  struct ospf6_area *oa;
+  struct interface *ifp;
+  u_int32_t area_id;
+
+  ifp = if_lookup_by_name (argv[0]);
+  if (ifp == NULL)
+    {
+      vty_out (vty, "No such interface %s%s", argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  oi = (struct ospf6_interface *) ifp->info;
+  if (oi == NULL)
+    {
+      vty_out (vty, "Interface %s not enabled%s", ifp->name, VNL);
+      return CMD_SUCCESS;
+    }
+
+  /* parse Area-ID */
+  if (inet_pton (AF_INET, argv[1], &area_id) != 1)
+    {
+      vty_out (vty, "Invalid Area-ID: %s%s", argv[1], VNL);
+      return CMD_SUCCESS;
+    }
+
+  /* Verify Area */
+  if (oi->area == NULL)
+    {
+      vty_out (vty, "No such Area-ID: %s%s", argv[1], VNL);
+      return CMD_SUCCESS;
+    }
+
+  if (oi->area->area_id != area_id)
+    {
+      vty_out (vty, "Wrong Area-ID: %s is attached to area %s%s",
+               oi->interface->name, oi->area->name, VNL);
+      return CMD_SUCCESS;
+    }
+
+  thread_execute (master, interface_down, oi, 0);
+
+  oa = oi->area;
+  listnode_delete (oi->area->if_list, oi);
+  oi->area = (struct ospf6_area *) NULL;
+
+  /* Withdraw inter-area routes from this area, if necessary */
+  if (oa->if_list->count == 0)
+    {
+      UNSET_FLAG (oa->flag, OSPF6_AREA_ENABLE);
+      ospf6_abr_disable_area (oa);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_stub_router_admin,
+       ospf6_stub_router_admin_cmd,
+       "stub-router administrative",
+       "Make router a stub router\n"
+       "Advertise inability to be a transit router\n"
+       "Administratively applied, for an indefinite period\n")
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+
+  if (!CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+       {
+          OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6);
+          OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R);
+          OSPF6_ROUTER_LSA_SCHEDULE (oa);
+       }
+      SET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_stub_router_admin,
+       no_ospf6_stub_router_admin_cmd,
+       "no stub-router administrative",
+       NO_STR
+       "Make router a stub router\n"
+       "Advertise ability to be a transit router\n"
+       "Administratively applied, for an indefinite period\n")
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+
+  if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+       {
+          OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6);
+          OSPF6_OPT_SET (oa->options, OSPF6_OPT_R);
+          OSPF6_ROUTER_LSA_SCHEDULE (oa);
+       }
+      UNSET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_stub_router_startup,
+       ospf6_stub_router_startup_cmd,
+       "stub-router on-startup <5-86400>",
+       "Make router a stub router\n"
+       "Advertise inability to be a transit router\n"
+       "Automatically advertise as stub-router on startup of OSPF6\n"
+       "Time (seconds) to advertise self as stub-router\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_stub_router_startup,
+       no_ospf6_stub_router_startup_cmd,
+       "no stub-router on-startup",
+       NO_STR
+       "Make router a stub router\n"
+       "Advertise inability to be a transit router\n"
+       "Automatically advertise as stub-router on startup of OSPF6\n"
+       "Time (seconds) to advertise self as stub-router\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf6_stub_router_shutdown,
+       ospf6_stub_router_shutdown_cmd,
+       "stub-router on-shutdown <5-86400>",
+       "Make router a stub router\n"
+       "Advertise inability to be a transit router\n"
+       "Automatically advertise as stub-router before shutdown\n"
+       "Time (seconds) to advertise self as stub-router\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_stub_router_shutdown,
+       no_ospf6_stub_router_shutdown_cmd,
+       "no stub-router on-shutdown",
+       NO_STR
+       "Make router a stub router\n"
+       "Advertise inability to be a transit router\n"
+       "Automatically advertise as stub-router before shutdown\n"
+       "Time (seconds) to advertise self as stub-router\n")
+{
+  return CMD_SUCCESS;
+}
+
+static void
+ospf6_show (struct vty *vty, struct ospf6 *o)
+{
+  struct listnode *n;
+  struct ospf6_area *oa;
+  char router_id[16], duration[32];
+  struct timeval now, running, result;
+  char buf[32], rbuf[32];
+
+  /* process id, router id */
+  inet_ntop (AF_INET, &o->router_id, router_id, sizeof (router_id));
+  vty_out (vty, " OSPFv3 Routing Process (0) with Router-ID %s%s",
+           router_id, VNL);
+
+  /* running time */
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  timersub (&now, &o->starttime, &running);
+  timerstring (&running, duration, sizeof (duration));
+  vty_out (vty, " Running %s%s", duration, VNL);
+
+  /* Redistribute configuration */
+  /* XXX */
+
+  /* Show SPF parameters */
+  vty_out(vty, " Initial SPF scheduling delay %d millisec(s)%s"
+         " Minimum hold time between consecutive SPFs %d millsecond(s)%s"
+         " Maximum hold time between consecutive SPFs %d millsecond(s)%s"
+         " Hold time multiplier is currently %d%s",
+         o->spf_delay, VNL,
+         o->spf_holdtime, VNL,
+         o->spf_max_holdtime, VNL,
+         o->spf_hold_multiplier, VNL);
+
+  vty_out(vty, " SPF algorithm ");
+  if (o->ts_spf.tv_sec || o->ts_spf.tv_usec)
+    {
+      timersub(&now, &o->ts_spf, &result);
+      timerstring(&result, buf, sizeof(buf));
+      ospf6_spf_reason_string(o->last_spf_reason, rbuf, sizeof(rbuf));
+      vty_out(vty, "last executed %s ago, reason %s%s", buf, rbuf, VNL);
+      vty_out (vty, " Last SPF duration %lld sec %lld usec%s",
+               (long long)o->ts_spf_duration.tv_sec,
+               (long long)o->ts_spf_duration.tv_usec, VNL);
+    }
+  else
+    vty_out(vty, "has not been run$%s", VNL);
+  threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf));
+  vty_out (vty, " SPF timer %s%s%s",
+          (o->t_spf_calc ? "due in " : "is "), buf, VNL);
+
+  if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER))
+    vty_out (vty, " Router Is Stub Router%s", VNL);
+
+  /* LSAs */
+  vty_out (vty, " Number of AS scoped LSAs is %u%s",
+           o->lsdb->count, VNL);
+
+  /* Areas */
+  vty_out (vty, " Number of areas in this router is %u%s",
+           listcount (o->area_list), VNL);
+
+  if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES))
+    {
+      if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_DETAIL))
+       vty_out(vty, " All adjacency changes are logged%s",VTY_NEWLINE);
+      else
+       vty_out(vty, " Adjacency changes are logged%s",VTY_NEWLINE);
+    }
+
+  vty_out (vty, "%s",VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, n, oa))
+    ospf6_area_show (vty, oa);
+}
+
+/* show top level structures */
+DEFUN (show_ipv6_ospf6,
+       show_ipv6_ospf6_cmd,
+       "show ipv6 ospf6",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR)
+{
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_show (vty, ospf6);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ospf6_route,
+       show_ipv6_ospf6_route_cmd,
+       "show ipv6 ospf6 route",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       )
+{
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_route_table_show (vty, argc, argv, ospf6->route_table);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_route,
+       show_ipv6_ospf6_route_detail_cmd,
+       "show ipv6 ospf6 route (X:X::X:X|X:X::X:X/M|detail|summary)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Specify IPv6 address\n"
+       "Specify IPv6 prefix\n"
+       "Detailed information\n"
+       "Summary of route table\n"
+       )
+
+DEFUN (show_ipv6_ospf6_route_match,
+       show_ipv6_ospf6_route_match_cmd,
+       "show ipv6 ospf6 route X:X::X:X/M match",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Specify IPv6 prefix\n"
+       "Display routes which match the specified route\n"
+       )
+{
+  const char *sargv[CMD_ARGC_MAX];
+  int i, sargc;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  /* copy argv to sargv and then append "match" */
+  for (i = 0; i < argc; i++)
+    sargv[i] = argv[i];
+  sargc = argc;
+  sargv[sargc++] = "match";
+  sargv[sargc] = NULL;
+
+  ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ospf6_route_match_detail,
+       show_ipv6_ospf6_route_match_detail_cmd,
+       "show ipv6 ospf6 route X:X::X:X/M match detail",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Specify IPv6 prefix\n"
+       "Display routes which match the specified route\n"
+       "Detailed information\n"
+       )
+{
+  const char *sargv[CMD_ARGC_MAX];
+  int i, sargc;
+
+  /* copy argv to sargv and then append "match" and "detail" */
+  for (i = 0; i < argc; i++)
+    sargv[i] = argv[i];
+  sargc = argc;
+  sargv[sargc++] = "match";
+  sargv[sargc++] = "detail";
+  sargv[sargc] = NULL;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_route_match,
+       show_ipv6_ospf6_route_longer_cmd,
+       "show ipv6 ospf6 route X:X::X:X/M longer",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Specify IPv6 prefix\n"
+       "Display routes longer than the specified route\n"
+       )
+
+DEFUN (show_ipv6_ospf6_route_match_detail,
+       show_ipv6_ospf6_route_longer_detail_cmd,
+       "show ipv6 ospf6 route X:X::X:X/M longer detail",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Specify IPv6 prefix\n"
+       "Display routes longer than the specified route\n"
+       "Detailed information\n"
+       );
+
+ALIAS (show_ipv6_ospf6_route,
+       show_ipv6_ospf6_route_type_cmd,
+       "show ipv6 ospf6 route (intra-area|inter-area|external-1|external-2)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Display Intra-Area routes\n"
+       "Display Inter-Area routes\n"
+       "Display Type-1 External routes\n"
+       "Display Type-2 External routes\n"
+       )
+
+DEFUN (show_ipv6_ospf6_route_type_detail,
+       show_ipv6_ospf6_route_type_detail_cmd,
+       "show ipv6 ospf6 route (intra-area|inter-area|external-1|external-2) detail",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       ROUTE_STR
+       "Display Intra-Area routes\n"
+       "Display Inter-Area routes\n"
+       "Display Type-1 External routes\n"
+       "Display Type-2 External routes\n"
+       "Detailed information\n"
+       )
+{
+  const char *sargv[CMD_ARGC_MAX];
+  int i, sargc;
+
+  /* copy argv to sargv and then append "detail" */
+  for (i = 0; i < argc; i++)
+    sargv[i] = argv[i];
+  sargc = argc;
+  sargv[sargc++] = "detail";
+  sargv[sargc] = NULL;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table);
+  return CMD_SUCCESS;
+}
+
+static void
+ospf6_stub_router_config_write (struct vty *vty)
+{
+  if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER))
+    {
+      vty_out (vty, " stub-router administrative%s", VNL);
+    }
+    return;
+}
+
+static int
+ospf6_distance_config_write (struct vty *vty)
+{
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  if (ospf6->distance_all)
+    vty_out (vty, " distance %d%s", ospf6->distance_all, VTY_NEWLINE);
+
+  if (ospf6->distance_intra
+      || ospf6->distance_inter
+      || ospf6->distance_external)
+    {
+      vty_out (vty, " distance ospf6");
+
+      if (ospf6->distance_intra)
+        vty_out (vty, " intra-area %d", ospf6->distance_intra);
+      if (ospf6->distance_inter)
+        vty_out (vty, " inter-area %d", ospf6->distance_inter);
+      if (ospf6->distance_external)
+        vty_out (vty, " external %d", ospf6->distance_external);
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  for (rn = route_top (ospf6->distance_table); rn; rn = route_next (rn))
+    if ((odistance = rn->info) != NULL)
+      {
+        char pstr[128];
+        vty_out (vty, " distance %d %s %s%s", odistance->distance,
+                 prefix2str (&rn->p, pstr, sizeof(pstr)),
+                 odistance->access_list ? odistance->access_list : "",
+                 VTY_NEWLINE);
+      }
+  return 0;
+}
+
+/* OSPF configuration write function. */
+static int
+config_write_ospf6 (struct vty *vty)
+{
+  char router_id[16];
+  struct listnode *j, *k;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+
+  /* OSPFv6 configuration. */
+  if (ospf6 == NULL)
+    return CMD_SUCCESS;
+
+  inet_ntop (AF_INET, &ospf6->router_id_static, router_id, sizeof (router_id));
+  vty_out (vty, "router ospf6%s", VNL);
+  if (ospf6->router_id_static != 0)
+    vty_out (vty, " router-id %s%s", router_id, VNL);
+
+  /* log-adjacency-changes flag print. */
+  if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES))
+    {
+      vty_out(vty, " log-adjacency-changes");
+      if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL))
+       vty_out(vty, " detail");
+      vty_out(vty, "%s", VTY_NEWLINE);
+    }
+
+  if (ospf6->ref_bandwidth != OSPF6_REFERENCE_BANDWIDTH)
+    vty_out (vty, " auto-cost reference-bandwidth %d%s", ospf6->ref_bandwidth / 1000,
+             VNL);
+
+  ospf6_stub_router_config_write (vty);
+  ospf6_redistribute_config_write (vty);
+  ospf6_area_config_write (vty);
+  ospf6_spf_config_write (vty);
+  ospf6_distance_config_write (vty);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, j, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, k, oi))
+        vty_out (vty, " interface %s area %s%s",
+                 oi->interface->name, oa->name, VNL);
+    }
+  vty_out (vty, "!%s", VNL);
+  return 0;
+}
+
+/* OSPF6 node structure. */
+static struct cmd_node ospf6_node =
+{
+  OSPF6_NODE,
+  "%s(config-ospf6)# ",
+  1 /* VTYSH */
+};
+
+/* Install ospf related commands. */
+void
+ospf6_top_init (void)
+{
+  /* Install ospf6 top node. */
+  install_node (&ospf6_node, config_write_ospf6);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_cmd);
+  install_element (CONFIG_NODE, &router_ospf6_cmd);
+  install_element (CONFIG_NODE, &no_router_ospf6_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_match_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_match_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_longer_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_longer_detail_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_detail_cmd);
+
+  install_default (OSPF6_NODE);
+  install_element (OSPF6_NODE, &ospf6_router_id_cmd);
+  install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_cmd);
+  install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_detail_cmd);
+  install_element (OSPF6_NODE, &ospf6_interface_area_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_interface_area_cmd);
+  install_element (OSPF6_NODE, &ospf6_stub_router_admin_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_stub_router_admin_cmd);
+  /* For a later time
+  install_element (OSPF6_NODE, &ospf6_stub_router_startup_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_stub_router_startup_cmd);
+  install_element (OSPF6_NODE, &ospf6_stub_router_shutdown_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_stub_router_shutdown_cmd);
+  */
+
+  install_element (OSPF6_NODE, &ospf6_distance_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_distance_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_distance_ospf6_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_inter_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_external_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_inter_external_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_external_inter_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_intra_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_external_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_intra_external_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_external_intra_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_intra_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_inter_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_intra_inter_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_inter_intra_cmd);
+
+  install_element (OSPF6_NODE, &ospf6_distance_source_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_distance_source_cmd);
+  install_element (OSPF6_NODE, &ospf6_distance_source_access_list_cmd);
+  install_element (OSPF6_NODE, &no_ospf6_distance_source_access_list_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
new file mode 100644 (file)
index 0000000..97fac0d
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_TOP_H
+#define OSPF6_TOP_H
+
+#include "routemap.h"
+
+/* OSPFv3 top level data structure */
+struct ospf6
+{
+  /* my router id */
+  u_int32_t router_id;
+
+  /* static router id */
+  u_int32_t router_id_static;
+
+  /* start time */
+  struct timeval starttime;
+
+  /* list of areas */
+  struct list *area_list;
+  struct ospf6_area *backbone;
+
+  /* AS scope link state database */
+  struct ospf6_lsdb *lsdb;
+  struct ospf6_lsdb *lsdb_self;
+
+  struct ospf6_route_table *route_table;
+  struct ospf6_route_table *brouter_table;
+
+  struct ospf6_route_table *external_table;
+  struct route_table *external_id_table;
+  u_int32_t external_id;
+
+  /* redistribute route-map */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } rmap[ZEBRA_ROUTE_MAX];
+
+  u_char flag;
+
+  /* Configured flags */
+  u_char config_flags;
+#define OSPF6_LOG_ADJACENCY_CHANGES      (1 << 0)
+#define OSPF6_LOG_ADJACENCY_DETAIL       (1 << 1)
+
+  /* SPF parameters */
+  unsigned int spf_delay;              /* SPF delay time. */
+  unsigned int spf_holdtime;           /* SPF hold time. */
+  unsigned int spf_max_holdtime;       /* SPF maximum-holdtime */
+  unsigned int spf_hold_multiplier;    /* Adaptive multiplier for hold time */
+  unsigned int spf_reason;              /* reason bits while scheduling SPF */
+
+  struct timeval ts_spf;               /* SPF calculation time stamp. */
+  struct timeval ts_spf_duration;      /* Execution time of last SPF */
+  unsigned int last_spf_reason;         /* Last SPF reason */
+
+  /* Threads */
+  struct thread *t_spf_calc;           /* SPF calculation timer. */
+  struct thread *t_ase_calc;           /* ASE calculation timer. */
+  struct thread *maxage_remover;
+
+  u_int32_t ref_bandwidth;
+
+  /* Distance parameters */
+  u_char distance_all;
+  u_char distance_intra;
+  u_char distance_inter;
+  u_char distance_external;
+
+  struct route_table *distance_table;
+};
+
+#define OSPF6_DISABLED    0x01
+#define OSPF6_STUB_ROUTER 0x02
+
+/* global pointer for OSPF top data structure */
+extern struct ospf6 *ospf6;
+
+/* prototypes */
+extern void ospf6_top_init (void);
+extern void ospf6_delete (struct ospf6 *o);
+
+extern void ospf6_maxage_remove (struct ospf6 *o);
+
+#endif /* OSPF6_TOP_H */
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
new file mode 100644 (file)
index 0000000..2976214
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "stream.h"
+#include "zclient.h"
+#include "memory.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_top.h"
+#include "ospf6_interface.h"
+#include "ospf6_route.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_asbr.h"
+#include "ospf6_zebra.h"
+#include "ospf6d.h"
+
+unsigned char conf_debug_ospf6_zebra = 0;
+
+/* information about zebra. */
+struct zclient *zclient = NULL;
+
+struct in_addr router_id_zebra;
+
+/* Router-id update message from zebra. */
+static int
+ospf6_router_id_update_zebra (int command, struct zclient *zclient,
+                             zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct prefix router_id;
+  struct ospf6 *o = ospf6;
+
+  zebra_router_id_update_read(zclient->ibuf,&router_id);
+  router_id_zebra = router_id.u.prefix4;
+
+  if (o == NULL)
+    return 0;
+
+  if (o->router_id  == 0)
+    o->router_id = (u_int32_t) router_id_zebra.s_addr;
+
+  return 0;
+}
+
+/* redistribute function */
+void
+ospf6_zebra_redistribute (int type)
+{
+  if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return;
+  vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT);
+  if (zclient->sock > 0)
+    zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type,
+                             VRF_DEFAULT);
+}
+
+void
+ospf6_zebra_no_redistribute (int type)
+{
+  if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return;
+  vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT);
+  if (zclient->sock > 0)
+    zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type,
+                             VRF_DEFAULT);
+}
+
+/* Inteface addition message from zebra. */
+static int
+ospf6_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    zlog_debug ("Zebra Interface add: %s index %d mtu %d",
+               ifp->name, ifp->ifindex, ifp->mtu6);
+  ospf6_interface_if_add (ifp);
+  return 0;
+}
+
+static int
+ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  if (!(ifp = zebra_interface_state_read (zclient->ibuf, vrf_id)))
+    return 0;
+
+  if (if_is_up (ifp))
+    zlog_warn ("Zebra: got delete of %s, but interface is still up", ifp->name);
+
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    zlog_debug ("Zebra Interface delete: %s index %d mtu %d",
+               ifp->name, ifp->ifindex, ifp->mtu6);
+
+  ifp->ifindex = IFINDEX_INTERNAL;
+  return 0;
+}
+
+static int
+ospf6_zebra_if_state_update (int command, struct zclient *zclient,
+                             zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_state_read (zclient->ibuf, vrf_id);
+  if (ifp == NULL)
+    return 0;
+  
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    zlog_debug ("Zebra Interface state change: "
+                "%s index %d flags %llx metric %d mtu %d bandwidth %d",
+               ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, 
+               ifp->metric, ifp->mtu6, ifp->bandwidth);
+
+  ospf6_interface_state_update (ifp);
+  return 0;
+}
+
+static int
+ospf6_zebra_if_address_update_add (int command, struct zclient *zclient,
+                                   zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  char buf[128];
+
+  c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, zclient->ibuf,
+                                    vrf_id);
+  if (c == NULL)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    zlog_debug ("Zebra Interface address add: %s %5s %s/%d",
+               c->ifp->name, prefix_family_str (c->address),
+               inet_ntop (c->address->family, &c->address->u.prefix,
+                          buf, sizeof (buf)), c->address->prefixlen);
+
+  if (c->address->family == AF_INET6)
+    {
+      ospf6_interface_state_update (c->ifp);
+      ospf6_interface_connected_route_update (c->ifp);
+    }
+  return 0;
+}
+
+static int
+ospf6_zebra_if_address_update_delete (int command, struct zclient *zclient,
+                               zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  char buf[128];
+
+  c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, zclient->ibuf,
+                                    vrf_id);
+  if (c == NULL)
+    return 0;
+
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    zlog_debug ("Zebra Interface address delete: %s %5s %s/%d",
+               c->ifp->name, prefix_family_str (c->address),
+               inet_ntop (c->address->family, &c->address->u.prefix,
+                          buf, sizeof (buf)), c->address->prefixlen);
+
+  if (c->address->family == AF_INET6)
+    {
+      ospf6_interface_connected_route_update (c->ifp);
+      ospf6_interface_state_update (c->ifp);
+    }
+
+  return 0;
+}
+
+static int
+ospf6_zebra_read_ipv6 (int command, struct zclient *zclient,
+                       zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv6 api;
+  unsigned long ifindex;
+  struct prefix_ipv6 p;
+  struct in6_addr *nexthop;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  nexthop = NULL;
+  memset (&api, 0, sizeof (api));
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv6 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      nexthop = (struct in6_addr *)
+        malloc (api.nexthop_num * sizeof (struct in6_addr));
+      stream_get (nexthop, s, api.nexthop_num * sizeof (struct in6_addr));
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+    {
+      char prefixstr[128], nexthopstr[128];
+      prefix2str ((struct prefix *)&p, prefixstr, sizeof (prefixstr));
+      if (nexthop)
+        inet_ntop (AF_INET6, nexthop, nexthopstr, sizeof (nexthopstr));
+      else
+        snprintf (nexthopstr, sizeof (nexthopstr), "::");
+
+      zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %u",
+                 (command == ZEBRA_IPV6_ROUTE_ADD ? "add" : "delete"),
+                 zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag);
+    }
+  if (command == ZEBRA_IPV6_ROUTE_ADD)
+    ospf6_asbr_redistribute_add (api.type, ifindex, (struct prefix *) &p,
+                                 api.nexthop_num, nexthop, api.tag);
+  else
+    ospf6_asbr_redistribute_remove (api.type, ifindex, (struct prefix *) &p);
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    free (nexthop);
+
+  return 0;
+}
+
+
+
+
+DEFUN (show_zebra,
+       show_zebra_cmd,
+       "show zebra",
+       SHOW_STR
+       "Zebra information\n")
+{
+  int i;
+  if (zclient == NULL)
+    {
+      vty_out (vty, "Not connected to zebra%s", VNL);
+      return CMD_SUCCESS;
+    }
+
+  vty_out (vty, "Zebra Infomation%s", VNL);
+  vty_out (vty, "  enable: %d fail: %d%s",
+           zclient->enable, zclient->fail, VNL);
+  vty_out (vty, "  redistribute default: %d%s",
+           vrf_bitmap_check (zclient->default_information, VRF_DEFAULT),
+           VNL);
+  vty_out (vty, "  redistribute:");
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    {
+      if (vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT))
+        vty_out (vty, " %s", zebra_route_string(i));
+    }
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (router_zebra,
+       router_zebra_cmd,
+       "router zebra",
+       "Enable a routing process\n"
+       "Make connection to zebra daemon\n")
+{
+  vty->node = ZEBRA_NODE;
+  zclient->enable = 1;
+  zclient_start (zclient);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_zebra,
+       no_router_zebra_cmd,
+       "no router zebra",
+       NO_STR
+       "Configure routing process\n"
+       "Disable connection to zebra daemon\n")
+{
+  zclient->enable = 0;
+  zclient_stop (zclient);
+  return CMD_SUCCESS;
+}
+
+/* Zebra configuration write function. */
+static int
+config_write_ospf6_zebra (struct vty *vty)
+{
+  if (! zclient->enable)
+    {
+      vty_out (vty, "no router zebra%s", VNL);
+      vty_out (vty, "!%s", VNL);
+    }
+  else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT))
+    {
+      vty_out (vty, "router zebra%s", VNL);
+      vty_out (vty, " no redistribute ospf6%s", VNL);
+      vty_out (vty, "!%s", VNL);
+    }
+  return 0;
+}
+
+/* Zebra node structure. */
+static struct cmd_node zebra_node =
+{
+  ZEBRA_NODE,
+  "%s(config-zebra)# ",
+};
+
+#define ADD    0
+#define REM    1
+static void
+ospf6_zebra_route_update (int type, struct ospf6_route *request)
+{
+  struct zapi_ipv6 api;
+  char buf[64];
+  int nhcount;
+  struct in6_addr **nexthops;
+  ifindex_t *ifindexes;
+  int i, ret = 0;
+  struct prefix_ipv6 *dest;
+
+  if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+    {
+      prefix2str (&request->prefix, buf, sizeof (buf));
+      zlog_debug ("Send %s route: %s",
+                 (type == REM ? "remove" : "add"), buf);
+    }
+
+  if (zclient->sock < 0)
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        zlog_debug ("  Not connected to Zebra");
+      return;
+    }
+
+  if (request->path.origin.adv_router == ospf6->router_id &&
+      (request->path.type == OSPF6_PATH_TYPE_EXTERNAL1 ||
+       request->path.type == OSPF6_PATH_TYPE_EXTERNAL2))
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        zlog_debug ("  Ignore self-originated external route");
+      return;
+    }
+
+  /* If removing is the best path and if there's another path,
+     treat this request as add the secondary path */
+  if (type == REM && ospf6_route_is_best (request) &&
+      request->next && ospf6_route_is_same (request, request->next))
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        zlog_debug ("  Best-path removal resulted Sencondary addition");
+      type = ADD;
+      request = request->next;
+    }
+
+  /* Only the best path will be sent to zebra. */
+  if (! ospf6_route_is_best (request))
+    {
+      /* this is not preferred best route, ignore */
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        zlog_debug ("  Ignore non-best route");
+      return;
+    }
+
+  nhcount = 0;
+  for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
+    if (ospf6_nexthop_is_set (&request->nexthop[i]))
+      nhcount++;
+
+  if (nhcount == 0)
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        zlog_debug ("  No nexthop, ignore");
+      return;
+    }
+
+  /* allocate memory for nexthop_list */
+  nexthops = XCALLOC (MTYPE_OSPF6_OTHER,
+                      nhcount * sizeof (struct in6_addr *));
+  if (nexthops == NULL)
+    {
+      zlog_warn ("Can't send route to zebra: malloc failed");
+      return;
+    }
+
+  /* allocate memory for ifindex_list */
+  ifindexes = XCALLOC (MTYPE_OSPF6_OTHER,
+                       nhcount * sizeof (unsigned int));
+  if (ifindexes == NULL)
+    {
+      zlog_warn ("Can't send route to zebra: malloc failed");
+      XFREE (MTYPE_OSPF6_OTHER, nexthops);
+      return;
+    }
+
+  for (i = 0; i < nhcount; i++)
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+       {
+         const char *ifname;
+         inet_ntop (AF_INET6, &request->nexthop[i].address,
+                    buf, sizeof (buf));
+         ifname = ifindex2ifname (request->nexthop[i].ifindex);
+         zlog_debug ("  nexthop: %s%%%.*s(%d)", buf, IFNAMSIZ, ifname,
+                     request->nexthop[i].ifindex);
+       }
+      nexthops[i] = &request->nexthop[i].address;
+      ifindexes[i] = request->nexthop[i].ifindex;
+    }
+
+  api.vrf_id = VRF_DEFAULT;
+  api.type = ZEBRA_ROUTE_OSPF6;
+  api.flags = 0;
+  api.message = 0;
+  api.safi = SAFI_UNICAST;
+  SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+  api.nexthop_num = nhcount;
+  api.nexthop = nexthops;
+  SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+  api.ifindex_num = nhcount;
+  api.ifindex = ifindexes;
+  SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+  api.metric = (request->path.metric_type == 2 ?
+                request->path.cost_e2 : request->path.cost);
+  if (request->path.tag)
+    {
+      SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+      api.tag = request->path.tag;
+    }
+  
+  dest = (struct prefix_ipv6 *) &request->prefix;
+  if (type == REM)
+    ret = zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, dest, &api);
+  else
+    ret = zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, dest, &api);
+
+  if (ret < 0)
+    zlog_err ("zapi_ipv6_route() %s failed: %s",
+              (type == REM ? "delete" : "add"), safe_strerror (errno));
+
+  XFREE (MTYPE_OSPF6_OTHER, nexthops);
+  XFREE (MTYPE_OSPF6_OTHER, ifindexes);
+
+  return;
+}
+
+void
+ospf6_zebra_route_update_add (struct ospf6_route *request)
+{
+  if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT))
+    {
+      ospf6->route_table->hook_add = NULL;
+      ospf6->route_table->hook_remove = NULL;
+      return;
+    }
+  ospf6_zebra_route_update (ADD, request);
+}
+
+void
+ospf6_zebra_route_update_remove (struct ospf6_route *request)
+{
+  if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT))
+    {
+      ospf6->route_table->hook_add = NULL;
+      ospf6->route_table->hook_remove = NULL;
+      return;
+    }
+  ospf6_zebra_route_update (REM, request);
+}
+
+DEFUN (redistribute_ospf6,
+       redistribute_ospf6_cmd,
+       "redistribute ospf6",
+       "Redistribute control\n"
+       "OSPF6 route\n")
+{
+  struct ospf6_route *route;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT))
+    return CMD_SUCCESS;
+
+  vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT);
+
+  if (ospf6 == NULL)
+    return CMD_SUCCESS;
+
+  /* send ospf6 route to zebra route table */
+  for (route = ospf6_route_head (ospf6->route_table); route;
+       route = ospf6_route_next (route))
+    ospf6_zebra_route_update_add (route);
+
+  ospf6->route_table->hook_add = ospf6_zebra_route_update_add;
+  ospf6->route_table->hook_remove = ospf6_zebra_route_update_remove;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_redistribute_ospf6,
+       no_redistribute_ospf6_cmd,
+       "no redistribute ospf6",
+       NO_STR
+       "Redistribute control\n"
+       "OSPF6 route\n")
+{
+  struct ospf6_route *route;
+
+  if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT))
+    return CMD_SUCCESS;
+
+  vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT);
+
+  if (ospf6 == NULL)
+    return CMD_SUCCESS;
+
+  ospf6->route_table->hook_add = NULL;
+  ospf6->route_table->hook_remove = NULL;
+
+  /* withdraw ospf6 route from zebra route table */
+  for (route = ospf6_route_head (ospf6->route_table); route;
+       route = ospf6_route_next (route))
+    ospf6_zebra_route_update_remove (route);
+
+  return CMD_SUCCESS;
+}
+
+static void
+ospf6_zebra_connected (struct zclient *zclient)
+{
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+static struct ospf6_distance *
+ospf6_distance_new (void)
+{
+  return XCALLOC (MTYPE_OSPF6_DISTANCE, sizeof (struct ospf6_distance));
+}
+
+static void
+ospf6_distance_free (struct ospf6_distance *odistance)
+{
+  XFREE (MTYPE_OSPF6_DISTANCE, odistance);
+}
+
+int
+ospf6_distance_set (struct vty *vty, struct ospf6 *o,
+                    const char *distance_str,
+                    const char *ip_str,
+                    const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  u_char distance;
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  ret = str2prefix_ipv6 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  /* Get OSPF6 distance node. */
+  rn = route_node_get (o->distance_table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      odistance = rn->info;
+      route_unlock_node (rn);
+    }
+  else
+    {
+      odistance = ospf6_distance_new ();
+      rn->info = odistance;
+    }
+
+  /* Set distance value. */
+  odistance->distance = distance;
+
+  /*Reset access-list configuration. */
+  if (odistance->access_list)
+    {
+      free (odistance->access_list);
+      odistance->access_list = NULL;
+    }
+  if (access_list_str)
+    odistance->access_list = strdup (access_list_str);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf6_distance_unset (struct vty *vty, struct ospf6 *o,
+                      const char *ip_str,
+                      const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  ret = str2prefix_ipv6 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rn = route_node_lookup (o->distance_table, (struct prefix *) &p);
+  if (!rn)
+    {
+      vty_out (vty, "Cant't find specified prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  odistance = rn->info;
+
+  if (odistance->access_list)
+    free (odistance->access_list);
+  ospf6_distance_free (odistance);
+
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf6_distance_reset (struct ospf6 *o)
+{
+  struct route_node *rn;
+  struct ospf6_distance *odistance;
+
+  for (rn = route_top (o->distance_table); rn; rn = route_next (rn))
+    if ((odistance = rn->info) != NULL)
+      {
+        if (odistance->access_list)
+          free (odistance->access_list);
+        ospf6_distance_free (odistance);
+        rn->info = NULL;
+        route_unlock_node (rn);
+      }
+}
+
+u_char
+ospf6_distance_apply (struct ospf6_route *or, struct ospf6 *o)
+{
+
+  if (o == NULL)
+    return 0;
+
+  if (o->distance_intra)
+    if (or->path.type == OSPF6_PATH_TYPE_INTRA)
+      return o->distance_intra;
+
+  if (o->distance_inter)
+    if (or->path.type == OSPF6_PATH_TYPE_INTER)
+      return o->distance_inter;
+
+  if (o->distance_external)
+    if(or->path.type == OSPF6_PATH_TYPE_EXTERNAL1
+       || or->path.type == OSPF6_PATH_TYPE_EXTERNAL2)
+      return o->distance_external;
+
+  if (o->distance_all)
+    return o->distance_all;
+
+  return 0;
+}
+
+void
+ospf6_zebra_init (struct thread_master *master)
+{
+  /* Allocate zebra structure. */
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_OSPF6);
+  zclient->zebra_connected = ospf6_zebra_connected;
+  zclient->router_id_update = ospf6_router_id_update_zebra;
+  zclient->interface_add = ospf6_zebra_if_add;
+  zclient->interface_delete = ospf6_zebra_if_del;
+  zclient->interface_up = ospf6_zebra_if_state_update;
+  zclient->interface_down = ospf6_zebra_if_state_update;
+  zclient->interface_address_add = ospf6_zebra_if_address_update_add;
+  zclient->interface_address_delete = ospf6_zebra_if_address_update_delete;
+  zclient->ipv4_route_add = NULL;
+  zclient->ipv4_route_delete = NULL;
+  zclient->ipv6_route_add = ospf6_zebra_read_ipv6;
+  zclient->ipv6_route_delete = ospf6_zebra_read_ipv6;
+
+  /* redistribute connected route by default */
+  /* ospf6_zebra_redistribute (ZEBRA_ROUTE_CONNECT); */
+
+  /* Install zebra node. */
+  install_node (&zebra_node, config_write_ospf6_zebra);
+
+  /* Install command element for zebra node. */
+  install_element (VIEW_NODE, &show_zebra_cmd);
+  install_element (CONFIG_NODE, &router_zebra_cmd);
+  install_element (CONFIG_NODE, &no_router_zebra_cmd);
+
+  install_default (ZEBRA_NODE);
+  install_element (ZEBRA_NODE, &redistribute_ospf6_cmd);
+  install_element (ZEBRA_NODE, &no_redistribute_ospf6_cmd);
+
+  return;
+}
+
+/* Debug */
+
+DEFUN (debug_ospf6_zebra_sendrecv,
+       debug_ospf6_zebra_sendrecv_cmd,
+       "debug ospf6 zebra (send|recv)",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug connection between zebra\n"
+       "Debug Sending zebra\n"
+       "Debug Receiving zebra\n"
+      )
+{
+  unsigned char level = 0;
+
+  if (argc)
+    {
+      if (! strncmp (argv[0], "s", 1))
+        level = OSPF6_DEBUG_ZEBRA_SEND;
+      else if (! strncmp (argv[0], "r", 1))
+        level = OSPF6_DEBUG_ZEBRA_RECV;
+    }
+  else
+    level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV;
+
+  OSPF6_DEBUG_ZEBRA_ON (level);
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf6_zebra_sendrecv,
+       debug_ospf6_zebra_cmd,
+       "debug ospf6 zebra",
+       DEBUG_STR
+       OSPF6_STR
+       "Debug connection between zebra\n"
+      )
+
+
+DEFUN (no_debug_ospf6_zebra_sendrecv,
+       no_debug_ospf6_zebra_sendrecv_cmd,
+       "no debug ospf6 zebra (send|recv)",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug connection between zebra\n"
+       "Debug Sending zebra\n"
+       "Debug Receiving zebra\n"
+      )
+{
+  unsigned char level = 0;
+
+  if (argc)
+    {
+      if (! strncmp (argv[0], "s", 1))
+        level = OSPF6_DEBUG_ZEBRA_SEND;
+      else if (! strncmp (argv[0], "r", 1))
+        level = OSPF6_DEBUG_ZEBRA_RECV;
+    }
+  else
+    level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV;
+
+  OSPF6_DEBUG_ZEBRA_OFF (level);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf6_zebra_sendrecv,
+       no_debug_ospf6_zebra_cmd,
+       "no debug ospf6 zebra",
+       NO_STR
+       DEBUG_STR
+       OSPF6_STR
+       "Debug connection between zebra\n"
+      )
+
+int
+config_write_ospf6_debug_zebra (struct vty *vty)
+{
+  if (IS_OSPF6_DEBUG_ZEBRA (SEND) && IS_OSPF6_DEBUG_ZEBRA (RECV))
+    vty_out (vty, "debug ospf6 zebra%s", VNL);
+  else
+    {
+      if (IS_OSPF6_DEBUG_ZEBRA (SEND))
+        vty_out (vty, "debug ospf6 zebra send%s", VNL);
+      if (IS_OSPF6_DEBUG_ZEBRA (RECV))
+        vty_out (vty, "debug ospf6 zebra recv%s", VNL);
+    }
+  return 0;
+}
+
+void
+install_element_ospf6_debug_zebra (void)
+{
+  install_element (ENABLE_NODE, &debug_ospf6_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_zebra_cmd);
+  install_element (ENABLE_NODE, &debug_ospf6_zebra_sendrecv_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf6_zebra_sendrecv_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_zebra_cmd);
+  install_element (CONFIG_NODE, &debug_ospf6_zebra_sendrecv_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf6_zebra_sendrecv_cmd);
+}
+
+
diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h
new file mode 100644 (file)
index 0000000..51eb9d7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6_ZEBRA_H
+#define OSPF6_ZEBRA_H
+
+#include "zclient.h"
+#include "ospf6_top.h"
+
+/* Debug option */
+extern unsigned char conf_debug_ospf6_zebra;
+#define OSPF6_DEBUG_ZEBRA_SEND 0x01
+#define OSPF6_DEBUG_ZEBRA_RECV 0x02
+#define OSPF6_DEBUG_ZEBRA_ON(level) \
+  (conf_debug_ospf6_zebra |= level)
+#define OSPF6_DEBUG_ZEBRA_OFF(level) \
+  (conf_debug_ospf6_zebra &= ~(level))
+#define IS_OSPF6_DEBUG_ZEBRA(e) \
+  (conf_debug_ospf6_zebra & OSPF6_DEBUG_ZEBRA_ ## e)
+
+/* OSPF6 distance */
+struct ospf6_distance
+{
+  /* Distance value for the IP source prefix */
+  u_char distance;
+
+  /* Name of the access-list to be matched */
+  char *access_list;
+};
+
+extern struct zclient *zclient;
+
+extern void ospf6_zebra_route_update_add (struct ospf6_route *request);
+extern void ospf6_zebra_route_update_remove (struct ospf6_route *request);
+
+extern void ospf6_zebra_redistribute (int);
+extern void ospf6_zebra_no_redistribute (int);
+#define ospf6_zebra_is_redistribute(type) \
+    vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)
+
+extern void ospf6_distance_reset (struct ospf6 *);
+extern u_char ospf6_distance_apply (struct ospf6_route *,
+                                    struct ospf6 *);
+extern int ospf6_distance_set (struct vty *, struct ospf6 *, const char *,
+                               const char *, const char *);
+extern int ospf6_distance_unset (struct vty *, struct ospf6 *, const char *,
+                                 const char *);
+
+extern void ospf6_zebra_init(struct thread_master *);
+
+extern int config_write_ospf6_debug_zebra (struct vty *vty);
+extern void install_element_ospf6_debug_zebra (void);
+
+#endif /*OSPF6_ZEBRA_H*/
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
new file mode 100644 (file)
index 0000000..387f692
--- /dev/null
@@ -0,0 +1,1863 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "vty.h"
+#include "command.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_network.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_route.h"
+#include "ospf6_zebra.h"
+#include "ospf6_spf.h"
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_intra.h"
+#include "ospf6_asbr.h"
+#include "ospf6_abr.h"
+#include "ospf6_flood.h"
+#include "ospf6d.h"
+
+#ifdef HAVE_SNMP
+#include "ospf6_snmp.h"
+#endif /*HAVE_SNMP*/
+
+char ospf6_daemon_version[] = OSPF6_DAEMON_VERSION;
+
+struct route_node *
+route_prev (struct route_node *node)
+{
+  struct route_node *end;
+  struct route_node *prev = NULL;
+
+  end = node;
+  node = node->parent;
+  if (node)
+    route_lock_node (node);
+  while (node)
+    {
+      prev = node;
+      node = route_next (node);
+      if (node == end)
+        {
+          route_unlock_node (node);
+          node = NULL;
+        }
+    }
+  route_unlock_node (end);
+  if (prev)
+    route_lock_node (prev);
+
+  return prev;
+}
+
+
+/* show database functions */
+DEFUN (show_version_ospf6,
+       show_version_ospf6_cmd,
+       "show version ospf6",
+       SHOW_STR
+       "Displays ospf6d version\n"
+      )
+{
+  vty_out (vty, "Zebra OSPF6d Version: %s%s",
+           ospf6_daemon_version, VNL);
+
+  return CMD_SUCCESS;
+}
+
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",
+  1 /* VTYSH */
+};
+
+static int
+config_write_ospf6_debug (struct vty *vty)
+{
+  config_write_ospf6_debug_message (vty);
+  config_write_ospf6_debug_lsa (vty);
+  config_write_ospf6_debug_zebra (vty);
+  config_write_ospf6_debug_interface (vty);
+  config_write_ospf6_debug_neighbor (vty);
+  config_write_ospf6_debug_spf (vty);
+  config_write_ospf6_debug_route (vty);
+  config_write_ospf6_debug_brouter (vty);
+  config_write_ospf6_debug_asbr (vty);
+  config_write_ospf6_debug_abr (vty);
+  config_write_ospf6_debug_flood (vty);
+  vty_out (vty, "!%s", VNL);
+  return 0;
+}
+
+#define AREA_LSDB_TITLE_FORMAT \
+  "%s        Area Scoped Link State Database (Area %s)%s%s"
+#define IF_LSDB_TITLE_FORMAT \
+  "%s        I/F Scoped Link State Database (I/F %s in Area %s)%s%s"
+#define AS_LSDB_TITLE_FORMAT \
+  "%s        AS Scoped Link State Database%s%s"
+
+static int
+parse_show_level (int argc, const char *argv[])
+{
+  int level = 0;
+  if (argc)
+    {
+      if (! strncmp (argv[0], "de", 2))
+        level = OSPF6_LSDB_SHOW_LEVEL_DETAIL;
+      else if (! strncmp (argv[0], "du", 2))
+        level = OSPF6_LSDB_SHOW_LEVEL_DUMP;
+      else if (! strncmp (argv[0], "in", 2))
+        level = OSPF6_LSDB_SHOW_LEVEL_INTERNAL;
+    }
+  else
+    level = OSPF6_LSDB_SHOW_LEVEL_NORMAL;
+  return level;
+}
+
+static u_int16_t
+parse_type_spec (int argc, const char *argv[])
+{
+  u_int16_t type = 0;
+  assert (argc);
+  if (! strcmp (argv[0], "router"))
+    type = htons (OSPF6_LSTYPE_ROUTER);
+  else if (! strcmp (argv[0], "network"))
+    type = htons (OSPF6_LSTYPE_NETWORK);
+  else if (! strcmp (argv[0], "as-external"))
+    type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
+  else if (! strcmp (argv[0], "intra-prefix"))
+    type = htons (OSPF6_LSTYPE_INTRA_PREFIX);
+  else if (! strcmp (argv[0], "inter-router"))
+    type = htons (OSPF6_LSTYPE_INTER_ROUTER);
+  else if (! strcmp (argv[0], "inter-prefix"))
+    type = htons (OSPF6_LSTYPE_INTER_PREFIX);
+  else if (! strcmp (argv[0], "link"))
+    type = htons (OSPF6_LSTYPE_LINK);
+  return type;
+}
+
+DEFUN (show_ipv6_ospf6_database,
+       show_ipv6_ospf6_database_cmd,
+       "show ipv6 ospf6 database",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  level = parse_show_level (argc, argv);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, NULL, NULL, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, NULL, NULL, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, NULL, NULL, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database,
+       show_ipv6_ospf6_database_detail_cmd,
+       "show ipv6 ospf6 database (detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type,
+       show_ipv6_ospf6_database_type_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, NULL, NULL, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, NULL, NULL, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, NULL, NULL, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type,
+       show_ipv6_ospf6_database_type_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_id,
+       show_ipv6_ospf6_database_id_cmd,
+       "show ipv6 ospf6 database * A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int32_t id = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link State ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, &id, NULL, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, &id, NULL, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, &id, NULL, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_id,
+       show_ipv6_ospf6_database_id_detail_cmd,
+       "show ipv6 ospf6 database * A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_id,
+       show_ipv6_ospf6_database_linkstate_id_cmd,
+       "show ipv6 ospf6 database linkstate-id A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_id,
+       show_ipv6_ospf6_database_linkstate_id_detail_cmd,
+       "show ipv6 ospf6 database linkstate-id A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_router,
+       show_ipv6_ospf6_database_router_cmd,
+       "show ipv6 ospf6 database * * A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Any Link state ID\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_router,
+       show_ipv6_ospf6_database_router_detail_cmd,
+       "show ipv6 ospf6 database * * A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Any Link state ID\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_router,
+       show_ipv6_ospf6_database_adv_router_cmd,
+       "show ipv6 ospf6 database adv-router A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_router,
+       show_ipv6_ospf6_database_adv_router_detail_cmd,
+       "show ipv6 ospf6 database adv-router A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_id,
+       show_ipv6_ospf6_database_type_id_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t id = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link state ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, &id, NULL, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, &id, NULL, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, &id, NULL, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_id,
+       show_ipv6_ospf6_database_type_id_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_type_id,
+       show_ipv6_ospf6_database_type_linkstate_id_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) linkstate-id A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_type_id,
+       show_ipv6_ospf6_database_type_linkstate_id_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) linkstate-id A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_router,
+       show_ipv6_ospf6_database_type_router_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) * A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Any Link state ID\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_router,
+       show_ipv6_ospf6_database_type_router_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) * A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Any Link state ID\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_type_router,
+       show_ipv6_ospf6_database_type_adv_router_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) adv-router A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+
+ALIAS (show_ipv6_ospf6_database_type_router,
+       show_ipv6_ospf6_database_type_adv_router_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) adv-router A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_id_router,
+       show_ipv6_ospf6_database_id_router_cmd,
+       "show ipv6 ospf6 database * A.B.C.D A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int32_t id = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link state ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_id_router,
+       show_ipv6_ospf6_database_id_router_detail_cmd,
+       "show ipv6 ospf6 database * A.B.C.D A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Any Link state Type\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_adv_router_linkstate_id,
+       show_ipv6_ospf6_database_adv_router_linkstate_id_cmd,
+       "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int32_t id = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link state ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_adv_router_linkstate_id,
+       show_ipv6_ospf6_database_adv_router_linkstate_id_detail_cmd,
+       "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_id_router,
+       show_ipv6_ospf6_database_type_id_router_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t id = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link state ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_id_router,
+       show_ipv6_ospf6_database_type_id_router_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D A.B.C.D "
+       "(dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id,
+       show_ipv6_ospf6_database_type_adv_router_linkstate_id_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) "
+       "adv-router A.B.C.D linkstate-id A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t id = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+    {
+      vty_out (vty, "Advertising Router is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link state ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_adv_router_linkstate_id,
+       show_ipv6_ospf6_database_type_adv_router_linkstate_id_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) "
+       "adv-router A.B.C.D linkstate-id A.B.C.D "
+       "(dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_self_originated,
+       show_ipv6_ospf6_database_self_originated_cmd,
+       "show ipv6 ospf6 database self-originated",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Self-originated LSAs\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  level = parse_show_level (argc, argv);
+
+  adv_router = o->router_id;
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+      ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oa->lsdb);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+    {
+      for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+        {
+          vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                   oi->interface->name, oa->name, VNL, VNL);
+          ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oi->lsdb);
+        }
+    }
+
+  vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+  ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, o->lsdb);
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_self_originated,
+       show_ipv6_ospf6_database_self_originated_detail_cmd,
+       "show ipv6 ospf6 database self-originated "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Self-originated LSAs\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_self_originated,
+       show_ipv6_ospf6_database_type_self_originated_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) self-originated",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display Self-originated LSAs\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t adv_router = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  adv_router = o->router_id;
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_self_originated,
+       show_ipv6_ospf6_database_type_self_originated_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) self-originated "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display Self-originated LSAs\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_self_originated_linkstate_id,
+       show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) self-originated "
+       "linkstate-id A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display Self-originated LSAs\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t adv_router = 0;
+  u_int32_t id = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link State ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  adv_router = o->router_id;
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_self_originated_linkstate_id,
+       show_ipv6_ospf6_database_type_self_originated_linkstate_id_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) self-originated "
+       "linkstate-id A.B.C.D (detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display Self-originated LSAs\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+DEFUN (show_ipv6_ospf6_database_type_id_self_originated,
+       show_ipv6_ospf6_database_type_id_self_originated_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D self-originated",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display Self-originated LSAs\n"
+      )
+{
+  int level;
+  struct listnode *i, *j;
+  struct ospf6 *o = ospf6;
+  struct ospf6_area *oa;
+  struct ospf6_interface *oi;
+  u_int16_t type = 0;
+  u_int32_t adv_router = 0;
+  u_int32_t id = 0;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  type = parse_type_spec (argc, argv);
+  argc--;
+  argv++;
+
+  if ((inet_pton (AF_INET, argv[0], &id)) != 1)
+    {
+      vty_out (vty, "Link State ID is not parsable: %s%s",
+               argv[0], VNL);
+      return CMD_SUCCESS;
+    }
+
+  argc--;
+  argv++;
+  level = parse_show_level (argc, argv);
+
+  adv_router = o->router_id;
+
+  switch (OSPF6_LSA_SCOPE (type))
+    {
+      case OSPF6_SCOPE_AREA:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL);
+            ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb);
+          }
+        break;
+
+      case OSPF6_SCOPE_LINKLOCAL:
+        for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa))
+          {
+            for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi))
+              {
+                vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL,
+                         oi->interface->name, oa->name, VNL, VNL);
+                ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb);
+              }
+          }
+        break;
+
+      case OSPF6_SCOPE_AS:
+        vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL);
+        ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb);
+        break;
+
+      default:
+        assert (0);
+        break;
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_database_type_id_self_originated,
+       show_ipv6_ospf6_database_type_id_self_originated_detail_cmd,
+       "show ipv6 ospf6 database "
+       "(router|network|inter-prefix|inter-router|as-external|"
+       "group-membership|type-7|link|intra-prefix) A.B.C.D self-originated "
+       "(detail|dump|internal)",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Display Router LSAs\n"
+       "Display Network LSAs\n"
+       "Display Inter-Area-Prefix LSAs\n"
+       "Display Inter-Area-Router LSAs\n"
+       "Display As-External LSAs\n"
+       "Display Group-Membership LSAs\n"
+       "Display Type-7 LSAs\n"
+       "Display Link LSAs\n"
+       "Display Intra-Area-Prefix LSAs\n"
+       "Display Self-originated LSAs\n"
+       "Search by Link state ID\n"
+       "Specify Link state ID as IPv4 address notation\n"
+       "Display details of LSAs\n"
+       "Dump LSAs\n"
+       "Display LSA's internal information\n"
+      )
+
+
+DEFUN (show_ipv6_ospf6_border_routers,
+       show_ipv6_ospf6_border_routers_cmd,
+       "show ipv6 ospf6 border-routers",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display routing table for ABR and ASBR\n"
+      )
+{
+  u_int32_t adv_router;
+  void (*showfunc) (struct vty *, struct ospf6_route *);
+  struct ospf6_route *ro;
+  struct prefix prefix;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  if (argc && ! strcmp ("detail", argv[0]))
+    {
+      showfunc = ospf6_route_show_detail;
+      argc--;
+      argv++;
+    }
+  else
+    showfunc = ospf6_brouter_show;
+
+  if (argc)
+    {
+      if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1)
+        {
+          vty_out (vty, "Router ID is not parsable: %s%s", argv[0], VNL);
+          return CMD_SUCCESS;
+        }
+
+      ospf6_linkstate_prefix (adv_router, 0, &prefix);
+      ro = ospf6_route_lookup (&prefix, ospf6->brouter_table);
+      if (!ro)
+        {
+          vty_out (vty, "No Route found for Router ID: %s%s", argv[0], VNL);
+          return CMD_SUCCESS;
+        }
+
+      ospf6_route_show_detail (vty, ro);
+      return CMD_SUCCESS;
+    }
+
+  if (showfunc == ospf6_brouter_show)
+    ospf6_brouter_show_header (vty);
+
+  for (ro = ospf6_route_head (ospf6->brouter_table); ro;
+       ro = ospf6_route_next (ro))
+    (*showfunc) (vty, ro);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_border_routers,
+       show_ipv6_ospf6_border_routers_detail_cmd,
+       "show ipv6 ospf6 border-routers (A.B.C.D|detail)",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display routing table for ABR and ASBR\n"
+       "Specify Router-ID\n"
+       "Display Detail\n"
+      )
+
+DEFUN (show_ipv6_ospf6_linkstate,
+       show_ipv6_ospf6_linkstate_cmd,
+       "show ipv6 ospf6 linkstate",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display linkstate routing table\n"
+      )
+{
+  struct listnode *node;
+  struct ospf6_area *oa;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+    {
+      vty_out (vty, "%s        SPF Result in Area %s%s%s",
+               VNL, oa->name, VNL, VNL);
+      ospf6_linkstate_table_show (vty, argc, argv, oa->spf_table);
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_ospf6_linkstate,
+       show_ipv6_ospf6_linkstate_router_cmd,
+       "show ipv6 ospf6 linkstate router A.B.C.D",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display linkstate routing table\n"
+       "Display Router Entry\n"
+       "Specify Router ID as IPv4 address notation\n"
+      )
+
+ALIAS (show_ipv6_ospf6_linkstate,
+       show_ipv6_ospf6_linkstate_network_cmd,
+       "show ipv6 ospf6 linkstate network A.B.C.D A.B.C.D",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display linkstate routing table\n"
+       "Display Network Entry\n"
+       "Specify Router ID as IPv4 address notation\n"
+       "Specify Link state ID as IPv4 address notation\n"
+      )
+
+DEFUN (show_ipv6_ospf6_linkstate_detail,
+       show_ipv6_ospf6_linkstate_detail_cmd,
+       "show ipv6 ospf6 linkstate detail",
+       SHOW_STR
+       IP6_STR
+       OSPF6_STR
+       "Display linkstate routing table\n"
+      )
+{
+  const char *sargv[CMD_ARGC_MAX];
+  int i, sargc;
+  struct listnode *node;
+  struct ospf6_area *oa;
+
+  OSPF6_CMD_CHECK_RUNNING ();
+
+  /* copy argv to sargv and then append "detail" */
+  for (i = 0; i < argc; i++)
+    sargv[i] = argv[i];
+  sargc = argc;
+  sargv[sargc++] = "detail";
+  sargv[sargc] = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa))
+    {
+      vty_out (vty, "%s        SPF Result in Area %s%s%s",
+               VNL, oa->name, VNL, VNL);
+      ospf6_linkstate_table_show (vty, sargc, sargv, oa->spf_table);
+    }
+
+  vty_out (vty, "%s", VNL);
+  return CMD_SUCCESS;
+}
+
+/* Install ospf related commands. */
+void
+ospf6_init (void)
+{
+  ospf6_top_init ();
+  ospf6_area_init ();
+  ospf6_interface_init ();
+  ospf6_neighbor_init ();
+  ospf6_zebra_init (master);
+
+  ospf6_lsa_init ();
+  ospf6_spf_init ();
+  ospf6_intra_init ();
+  ospf6_asbr_init ();
+  ospf6_abr_init ();
+
+#ifdef HAVE_SNMP
+  ospf6_snmp_init (master);
+#endif /*HAVE_SNMP*/
+
+  install_node (&debug_node, config_write_ospf6_debug);
+
+  install_element_ospf6_debug_message ();
+  install_element_ospf6_debug_lsa ();
+  install_element_ospf6_debug_interface ();
+  install_element_ospf6_debug_neighbor ();
+  install_element_ospf6_debug_zebra ();
+  install_element_ospf6_debug_spf ();
+  install_element_ospf6_debug_route ();
+  install_element_ospf6_debug_brouter ();
+  install_element_ospf6_debug_asbr ();
+  install_element_ospf6_debug_abr ();
+  install_element_ospf6_debug_flood ();
+
+  install_element_ospf6_clear_interface ();
+
+  install_element (VIEW_NODE, &show_version_ospf6_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_detail_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_router_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_network_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_detail_cmd);
+
+#define INSTALL(n,c) \
+  install_element (n ## _NODE, &show_ipv6_ospf6_ ## c)
+
+  INSTALL (VIEW, database_cmd);
+  INSTALL (VIEW, database_detail_cmd);
+  INSTALL (VIEW, database_type_cmd);
+  INSTALL (VIEW, database_type_detail_cmd);
+  INSTALL (VIEW, database_id_cmd);
+  INSTALL (VIEW, database_id_detail_cmd);
+  INSTALL (VIEW, database_linkstate_id_cmd);
+  INSTALL (VIEW, database_linkstate_id_detail_cmd);
+  INSTALL (VIEW, database_router_cmd);
+  INSTALL (VIEW, database_router_detail_cmd);
+  INSTALL (VIEW, database_adv_router_cmd);
+  INSTALL (VIEW, database_adv_router_detail_cmd);
+  INSTALL (VIEW, database_type_id_cmd);
+  INSTALL (VIEW, database_type_id_detail_cmd);
+  INSTALL (VIEW, database_type_linkstate_id_cmd);
+  INSTALL (VIEW, database_type_linkstate_id_detail_cmd);
+  INSTALL (VIEW, database_type_router_cmd);
+  INSTALL (VIEW, database_type_router_detail_cmd);
+  INSTALL (VIEW, database_type_adv_router_cmd);
+  INSTALL (VIEW, database_type_adv_router_detail_cmd);
+  INSTALL (VIEW, database_adv_router_linkstate_id_cmd);
+  INSTALL (VIEW, database_adv_router_linkstate_id_detail_cmd);
+  INSTALL (VIEW, database_id_router_cmd);
+  INSTALL (VIEW, database_id_router_detail_cmd);
+  INSTALL (VIEW, database_type_id_router_cmd);
+  INSTALL (VIEW, database_type_id_router_detail_cmd);
+  INSTALL (VIEW, database_type_adv_router_linkstate_id_cmd);
+  INSTALL (VIEW, database_type_adv_router_linkstate_id_detail_cmd);
+  INSTALL (VIEW, database_self_originated_cmd);
+  INSTALL (VIEW, database_self_originated_detail_cmd);
+  INSTALL (VIEW, database_type_self_originated_cmd);
+  INSTALL (VIEW, database_type_self_originated_detail_cmd);
+  INSTALL (VIEW, database_type_id_self_originated_cmd);
+  INSTALL (VIEW, database_type_id_self_originated_detail_cmd);
+  INSTALL (VIEW, database_type_self_originated_linkstate_id_cmd);
+  INSTALL (VIEW, database_type_self_originated_linkstate_id_detail_cmd);
+
+  /* Make ospf protocol socket. */
+  ospf6_serv_sock ();
+  thread_add_read (master, ospf6_receive, NULL, ospf6_sock);
+}
+
+void
+ospf6_clean (void)
+{
+  if (!ospf6)
+    return;
+  if (ospf6->route_table)
+    ospf6_route_remove_all (ospf6->route_table);
+  if (ospf6->brouter_table)
+    ospf6_route_remove_all (ospf6->brouter_table);
+}
diff --git a/ospf6d/ospf6d.conf.sample b/ospf6d/ospf6d.conf.sample
new file mode 100644 (file)
index 0000000..0a6ddb7
--- /dev/null
@@ -0,0 +1,52 @@
+!
+! Zebra configuration saved from vty
+!   2003/11/28 00:49:49
+!
+hostname ospf6d@plant
+password zebra
+log stdout
+service advanced-vty
+!
+debug ospf6 neighbor state
+!
+interface fxp0
+ ipv6 ospf6 cost 1
+ ipv6 ospf6 hello-interval 10
+ ipv6 ospf6 dead-interval 40
+ ipv6 ospf6 retransmit-interval 5
+ ipv6 ospf6 priority 0
+ ipv6 ospf6 transmit-delay 1
+ ipv6 ospf6 instance-id 0
+!
+interface lo0
+ ipv6 ospf6 cost 1
+ ipv6 ospf6 hello-interval 10
+ ipv6 ospf6 dead-interval 40
+ ipv6 ospf6 retransmit-interval 5
+ ipv6 ospf6 priority 1
+ ipv6 ospf6 transmit-delay 1
+ ipv6 ospf6 instance-id 0
+!
+router ospf6
+ router-id 255.1.1.1
+ redistribute static route-map static-ospf6
+ interface fxp0 area 0.0.0.0
+!
+access-list access4 permit 127.0.0.1/32
+!
+ipv6 access-list access6 permit 3ffe:501::/32
+ipv6 access-list access6 permit 2001:200::/48
+ipv6 access-list access6 permit ::1/128
+!
+ipv6 prefix-list test-prefix seq 1000 deny any
+!
+route-map static-ospf6 permit 10
+ match ipv6 address prefix-list test-prefix
+ set metric-type type-2
+ set metric 2000
+!
+line vty
+ access-class access4
+ ipv6 access-class access6
+ exec-timeout 0 0
+!
diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h
new file mode 100644 (file)
index 0000000..9e2efb4
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef OSPF6D_H
+#define OSPF6D_H
+
+#define OSPF6_DAEMON_VERSION    "0.9.7r"
+
+#include "libospf.h"
+#include "thread.h"
+
+/* global variables */
+extern struct thread_master *master;
+
+/* Historical for KAME.  */
+#ifndef IPV6_JOIN_GROUP
+#ifdef IPV6_ADD_MEMBERSHIP
+#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
+#endif /* IPV6_ADD_MEMBERSHIP. */
+#ifdef IPV6_JOIN_MEMBERSHIP
+#define IPV6_JOIN_GROUP  IPV6_JOIN_MEMBERSHIP
+#endif /* IPV6_JOIN_MEMBERSHIP. */
+#endif /* ! IPV6_JOIN_GROUP*/
+
+#ifndef IPV6_LEAVE_GROUP
+#ifdef IPV6_DROP_MEMBERSHIP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
+#endif /* IPV6_DROP_MEMBERSHIP */
+#endif /* ! IPV6_LEAVE_GROUP */
+
+#define MSG_OK    0
+#define MSG_NG    1
+
+/* cast macro: XXX - these *must* die, ick ick. */
+#define OSPF6_PROCESS(x) ((struct ospf6 *) (x))
+#define OSPF6_AREA(x) ((struct ospf6_area *) (x))
+#define OSPF6_INTERFACE(x) ((struct ospf6_interface *) (x))
+#define OSPF6_NEIGHBOR(x) ((struct ospf6_neighbor *) (x))
+
+/* operation on timeval structure */
+#ifndef timerclear
+#define timerclear(a) (a)->tv_sec = (tvp)->tv_usec = 0
+#endif /*timerclear*/
+#ifndef timersub
+#define timersub(a, b, res)                           \
+  do {                                                \
+    (res)->tv_sec = (a)->tv_sec - (b)->tv_sec;        \
+    (res)->tv_usec = (a)->tv_usec - (b)->tv_usec;     \
+    if ((res)->tv_usec < 0)                           \
+      {                                               \
+        (res)->tv_sec--;                              \
+        (res)->tv_usec += 1000000;                    \
+      }                                               \
+  } while (0)
+#endif /*timersub*/
+#define timerstring(tv, buf, size)                      \
+  do {                                                  \
+    if ((tv)->tv_sec / 60 / 60 / 24)                    \
+      snprintf (buf, size, "%lldd%02lld:%02lld:%02lld", \
+                (tv)->tv_sec / 60LL / 60 / 24,          \
+                (tv)->tv_sec / 60LL / 60 % 24,          \
+                (tv)->tv_sec / 60LL % 60,               \
+                (tv)->tv_sec % 60LL);                   \
+    else                                                \
+      snprintf (buf, size, "%02lld:%02lld:%02lld",      \
+                (tv)->tv_sec / 60LL / 60 % 24,          \
+                (tv)->tv_sec / 60LL % 60,               \
+                (tv)->tv_sec % 60LL);                   \
+  } while (0)
+#define timerstring_local(tv, buf, size)                  \
+  do {                                                    \
+    int ret;                                              \
+    struct tm *tm;                                        \
+    tm = localtime (&(tv)->tv_sec);                       \
+    ret = strftime (buf, size, "%Y/%m/%d %H:%M:%S", tm);  \
+    if (ret == 0)                                         \
+      zlog_warn ("strftime error");                       \
+  } while (0)
+
+#define threadtimer_string(now, t, buf, size)                         \
+  do {                                                                \
+    struct timeval result;                                            \
+    if (!t)                                                           \
+      snprintf(buf, size, "inactive");                               \
+    else {                                                            \
+      timersub(&t->u.sands, &now, &result);                           \
+      timerstring(&result, buf, size);                                \
+    }                                                                 \
+} while (0)
+
+/* for commands */
+#define OSPF6_AREA_STR      "Area information\n"
+#define OSPF6_AREA_ID_STR   "Area ID (as an IPv4 notation)\n"
+#define OSPF6_SPF_STR       "Shortest Path First tree information\n"
+#define OSPF6_ROUTER_ID_STR "Specify Router-ID\n"
+#define OSPF6_LS_ID_STR     "Specify Link State ID\n"
+
+#define VNL VTY_NEWLINE
+#define OSPF6_CMD_CHECK_RUNNING() \
+  if (ospf6 == NULL) \
+    { \
+      vty_out (vty, "OSPFv3 is not running%s", VTY_NEWLINE); \
+      return CMD_SUCCESS; \
+    }
+
+
+/* Function Prototypes */
+extern struct route_node *route_prev (struct route_node *node);
+
+extern void ospf6_debug (void);
+extern void ospf6_init (void);
+
+#endif /* OSPF6D_H */
+
+
diff --git a/ospfclient/.gitignore b/ospfclient/.gitignore
new file mode 100644 (file)
index 0000000..191322b
--- /dev/null
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+*.o
+ospfclient
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/ospfclient/AUTHORS b/ospfclient/AUTHORS
new file mode 100644 (file)
index 0000000..b865c55
--- /dev/null
@@ -0,0 +1 @@
+Ralph Keller <keller@tik.ee.ethz.ch>
diff --git a/ospfclient/COPYING b/ospfclient/COPYING
new file mode 100644 (file)
index 0000000..b8cf3a1
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ospfclient/INSTALL b/ospfclient/INSTALL
new file mode 100644 (file)
index 0000000..b42a17a
--- /dev/null
@@ -0,0 +1,182 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
diff --git a/ospfclient/Makefile.am b/ospfclient/Makefile.am
new file mode 100644 (file)
index 0000000..1fca431
--- /dev/null
@@ -0,0 +1,27 @@
+## Automake.am for OSPF API client
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+AM_CFLAGS = $(WERROR)
+
+lib_LTLIBRARIES = libospfapiclient.la
+libospfapiclient_la_LDFLAGS = -version-info 0:0:0
+libospfapiclient_la_LIBADD = ../ospfd/libospf.la ../lib/libzebra.la
+
+sbin_PROGRAMS = ospfclient
+
+libospfapiclient_la_SOURCES = \
+       ospf_apiclient.c
+
+ospfapiheaderdir = $(pkgincludedir)/ospfapi
+
+ospfapiheader_HEADERS = \
+       ospf_apiclient.h
+
+ospfclient_SOURCES = \
+       ospfclient.c
+
+ospfclient_LDADD = libospfapiclient.la \
+       ../ospfd/libospf.la ../lib/libzebra.la @LIBCAP@
+
+ospfclient_CFLAGS = $(AM_CFLAGS)
+ospfclient_LDFLAGS = $(AM_LDFLAGS)
diff --git a/ospfclient/NEWS b/ospfclient/NEWS
new file mode 100644 (file)
index 0000000..5b1ec4f
--- /dev/null
@@ -0,0 +1 @@
+This file contains news.
diff --git a/ospfclient/README b/ospfclient/README
new file mode 100644 (file)
index 0000000..894cd78
--- /dev/null
@@ -0,0 +1,4 @@
+For more information about this software check out:
+
+http://www.tik.ee.ethz.ch/~keller/ospfapi/
+
diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c
new file mode 100644 (file)
index 0000000..ed7ca94
--- /dev/null
@@ -0,0 +1,746 @@
+/*
+ * Client side of OSPF API.
+ * Copyright (C) 2001, 2002, 2003 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "if.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "filter.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_opaque.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_api.h"
+
+#include "ospf_apiclient.h"
+
+/* Backlog for listen */
+#define BACKLOG 5
+
+/* -----------------------------------------------------------
+ * Forward declarations
+ * -----------------------------------------------------------
+ */
+
+void ospf_apiclient_handle_reply (struct ospf_apiclient *oclient,
+                                 struct msg *msg);
+void ospf_apiclient_handle_update_notify (struct ospf_apiclient *oclient,
+                                         struct msg *msg);
+void ospf_apiclient_handle_delete_notify (struct ospf_apiclient *oclient,
+                                         struct msg *msg);
+
+/* -----------------------------------------------------------
+ * Initialization
+ * -----------------------------------------------------------
+ */
+
+static unsigned short
+ospf_apiclient_getport (void)
+{
+  struct servent *sp = getservbyname ("ospfapi", "tcp");
+
+  return sp ? ntohs (sp->s_port) : OSPF_API_SYNC_PORT;
+}
+
+/* -----------------------------------------------------------
+ * Followings are functions for connection management
+ * -----------------------------------------------------------
+ */
+
+struct ospf_apiclient *
+ospf_apiclient_connect (char *host, int syncport)
+{
+  struct sockaddr_in myaddr_sync;
+  struct sockaddr_in myaddr_async;
+  struct sockaddr_in peeraddr;
+  struct hostent *hp;
+  struct ospf_apiclient *new;
+  int size = 0;
+  unsigned int peeraddrlen;
+  int async_server_sock;
+  int fd1, fd2;
+  int ret;
+  int on = 1;
+
+  /* There are two connections between the client and the server.
+     First the client opens a connection for synchronous requests/replies 
+     to the server. The server will accept this connection and
+     as a reaction open a reverse connection channel for 
+     asynchronous messages. */
+
+  async_server_sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (async_server_sock < 0)
+    {
+      fprintf (stderr,
+              "ospf_apiclient_connect: creating async socket failed\n");
+      return NULL;
+    }
+
+  /* Prepare socket for asynchronous messages */
+  /* Initialize async address structure */
+  memset (&myaddr_async, 0, sizeof (struct sockaddr_in));
+  myaddr_async.sin_family = AF_INET;
+  myaddr_async.sin_addr.s_addr = htonl (INADDR_ANY);
+  myaddr_async.sin_port = htons (syncport+1);
+  size = sizeof (struct sockaddr_in);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  myaddr_async.sin_len = size;
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  /* This is a server socket, reuse addr and port */
+  ret = setsockopt (async_server_sock, SOL_SOCKET,
+                   SO_REUSEADDR, (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: SO_REUSEADDR failed\n");
+      close (async_server_sock);
+      return NULL;
+    }
+
+#ifdef SO_REUSEPORT
+  ret = setsockopt (async_server_sock, SOL_SOCKET, SO_REUSEPORT,
+                   (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: SO_REUSEPORT failed\n");
+      close (async_server_sock);
+      return NULL;
+    }
+#endif /* SO_REUSEPORT */
+
+  /* Bind socket to address structure */
+  ret = bind (async_server_sock, (struct sockaddr *) &myaddr_async, size);
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: bind async socket failed\n");
+      close (async_server_sock);
+      return NULL;
+    }
+
+  /* Wait for reverse channel connection establishment from server */
+  ret = listen (async_server_sock, BACKLOG);
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: listen: %s\n", safe_strerror (errno));
+      close (async_server_sock);
+      return NULL;
+    }
+
+  /* Make connection for synchronous requests and connect to server */
+  /* Resolve address of server */
+  hp = gethostbyname (host);
+  if (!hp)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: no such host %s\n", host);
+      close (async_server_sock);
+      return NULL;
+    }
+
+  fd1 = socket (AF_INET, SOCK_STREAM, 0);
+  if (fd1 < 0)
+    {
+      fprintf (stderr,
+              "ospf_apiclient_connect: creating sync socket failed\n");
+      return NULL;
+    }
+
+
+  /* Reuse addr and port */
+  ret = setsockopt (fd1, SOL_SOCKET,
+                    SO_REUSEADDR, (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: SO_REUSEADDR failed\n");
+      close (fd1);
+      return NULL;
+    }
+
+#ifdef SO_REUSEPORT
+  ret = setsockopt (fd1, SOL_SOCKET, SO_REUSEPORT,
+                    (void *) &on, sizeof (on));
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: SO_REUSEPORT failed\n");
+      close (fd1);
+      return NULL;
+    }
+#endif /* SO_REUSEPORT */
+
+
+  /* Bind sync socket to address structure. This is needed since we
+     want the sync port number on a fixed port number. The reverse
+     async channel will be at this port+1 */
+
+  memset (&myaddr_sync, 0, sizeof (struct sockaddr_in));
+  myaddr_sync.sin_family = AF_INET;
+  myaddr_sync.sin_port = htons (syncport);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  myaddr_sync.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  ret = bind (fd1, (struct sockaddr *) &myaddr_sync, size);
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: bind sync socket failed\n");
+      close (fd1);
+      return NULL;
+    }
+
+  /* Prepare address structure for connect */
+  memcpy (&myaddr_sync.sin_addr, hp->h_addr, hp->h_length);
+  myaddr_sync.sin_family = AF_INET;
+  myaddr_sync.sin_port = htons(ospf_apiclient_getport ());
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  myaddr_sync.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  /* Now establish synchronous channel with OSPF daemon */
+  ret = connect (fd1, (struct sockaddr *) &myaddr_sync,
+                sizeof (struct sockaddr_in));
+  if (ret < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: sync connect failed\n");
+      close (async_server_sock);
+      close (fd1);
+      return NULL;
+    }
+
+  /* Accept reverse connection */
+  peeraddrlen = sizeof (struct sockaddr_in);
+  memset (&peeraddr, 0, peeraddrlen);
+
+  fd2 =
+    accept (async_server_sock, (struct sockaddr *) &peeraddr, &peeraddrlen);
+  if (fd2 < 0)
+    {
+      fprintf (stderr, "ospf_apiclient_connect: accept async failed\n");
+      close (async_server_sock);
+      close (fd1);
+      return NULL;
+    }
+
+  /* Server socket is not needed anymore since we are not accepting more 
+     connections */
+  close (async_server_sock);
+
+  /* Create new client-side instance */
+  new = XCALLOC (MTYPE_OSPF_APICLIENT, sizeof (struct ospf_apiclient));
+
+  /* Initialize socket descriptors for sync and async channels */
+  new->fd_sync = fd1;
+  new->fd_async = fd2;
+
+  return new;
+}
+
+int
+ospf_apiclient_close (struct ospf_apiclient *oclient)
+{
+
+  if (oclient->fd_sync >= 0)
+    {
+      close (oclient->fd_sync);
+    }
+
+  if (oclient->fd_async >= 0)
+    {
+      close (oclient->fd_async);
+    }
+
+  /* Free client structure */
+  XFREE (MTYPE_OSPF_APICLIENT, oclient);
+  return 0;
+}
+
+/* -----------------------------------------------------------
+ * Followings are functions to send a request to OSPFd
+ * -----------------------------------------------------------
+ */
+
+/* Send synchronous request, wait for reply */
+static int
+ospf_apiclient_send_request (struct ospf_apiclient *oclient, struct msg *msg)
+{
+  u_int32_t reqseq;
+  struct msg_reply *msgreply;
+  int rc;
+
+  /* NB: Given "msg" is freed inside this function. */
+
+  /* Remember the sequence number of the request */
+  reqseq = ntohl (msg->hdr.msgseq);
+
+  /* Write message to OSPFd */
+  rc = msg_write (oclient->fd_sync, msg);
+  msg_free (msg);
+
+  if (rc < 0)
+    {
+      return -1;
+    }
+
+  /* Wait for reply *//* NB: New "msg" is allocated by "msg_read()". */
+  msg = msg_read (oclient->fd_sync);
+  if (!msg)
+    return -1;
+
+  assert (msg->hdr.msgtype == MSG_REPLY);
+  assert (ntohl (msg->hdr.msgseq) == reqseq);
+
+  msgreply = (struct msg_reply *) STREAM_DATA (msg->s);
+  rc = msgreply->errcode;
+  msg_free (msg);
+
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Helper functions
+ * -----------------------------------------------------------
+ */
+
+static u_int32_t
+ospf_apiclient_get_seqnr (void)
+{
+  static u_int32_t seqnr = MIN_SEQ;
+  u_int32_t tmp;
+
+  tmp = seqnr;
+  /* Increment sequence number */
+  if (seqnr < MAX_SEQ)
+    {
+      seqnr++;
+    }
+  else
+    {
+      seqnr = MIN_SEQ;
+    }
+  return tmp;
+}
+
+/* -----------------------------------------------------------
+ * API to access OSPF daemon by client applications.
+ * -----------------------------------------------------------
+ */
+
+/*
+ * Synchronous request to register opaque type.
+ */
+int
+ospf_apiclient_register_opaque_type (struct ospf_apiclient *cl,
+                                    u_char ltype, u_char otype)
+{
+  struct msg *msg;
+  int rc;
+
+  /* just put 1 as a sequence number. */
+  msg = new_msg_register_opaque_type (ospf_apiclient_get_seqnr (),
+                                     ltype, otype);
+  if (!msg)
+    {
+      fprintf (stderr, "new_msg_register_opaque_type failed\n");
+      return -1;
+    }
+
+  rc = ospf_apiclient_send_request (cl, msg);
+  return rc;
+}
+
+/* 
+ * Synchronous request to synchronize with OSPF's LSDB.
+ * Two steps required: register_event in order to get
+ * dynamic updates and LSDB_Sync.
+ */
+int
+ospf_apiclient_sync_lsdb (struct ospf_apiclient *oclient)
+{
+  struct msg *msg;
+  int rc;
+  struct lsa_filter_type filter;
+
+  filter.typemask = 0xFFFF;    /* all LSAs */
+  filter.origin = ANY_ORIGIN;
+  filter.num_areas = 0;                /* all Areas. */
+
+  msg = new_msg_register_event (ospf_apiclient_get_seqnr (), &filter);
+  if (!msg)
+    {
+      fprintf (stderr, "new_msg_register_event failed\n");
+      return -1;
+    }
+  rc = ospf_apiclient_send_request (oclient, msg);
+
+  if (rc != 0)
+    goto out;
+
+  msg = new_msg_sync_lsdb (ospf_apiclient_get_seqnr (), &filter);
+  if (!msg)
+    {
+      fprintf (stderr, "new_msg_sync_lsdb failed\n");
+      return -1;
+    }
+  rc = ospf_apiclient_send_request (oclient, msg);
+
+out:
+  return rc;
+}
+
+/* 
+ * Synchronous request to originate or update an LSA.
+ */
+
+int
+ospf_apiclient_lsa_originate (struct ospf_apiclient *oclient,
+                             struct in_addr ifaddr,
+                             struct in_addr area_id,
+                             u_char lsa_type,
+                             u_char opaque_type, u_int32_t opaque_id,
+                             void *opaquedata, int opaquelen)
+{
+  struct msg *msg;
+  int rc;
+  u_char buf[OSPF_MAX_LSA_SIZE];
+  struct lsa_header *lsah;
+  u_int32_t tmp;
+
+
+  /* We can only originate opaque LSAs */
+  if (!IS_OPAQUE_LSA (lsa_type))
+    {
+      fprintf (stderr, "Cannot originate non-opaque LSA type %d\n", lsa_type);
+      return OSPF_API_ILLEGALLSATYPE;
+    }
+
+  /* Make a new LSA from parameters */
+  lsah = (struct lsa_header *) buf;
+  lsah->ls_age = 0;
+  lsah->options = 0;
+  lsah->type = lsa_type;
+
+  tmp = SET_OPAQUE_LSID (opaque_type, opaque_id);
+  lsah->id.s_addr = htonl (tmp);
+  lsah->adv_router.s_addr = 0;
+  lsah->ls_seqnum = 0;
+  lsah->checksum = 0;
+  lsah->length = htons (sizeof (struct lsa_header) + opaquelen);
+
+  memcpy (((u_char *) lsah) + sizeof (struct lsa_header), opaquedata,
+         opaquelen);
+
+  msg = new_msg_originate_request (ospf_apiclient_get_seqnr (),
+                                  ifaddr, area_id, lsah);
+  if (!msg)
+    {
+      fprintf (stderr, "new_msg_originate_request failed\n");
+      return OSPF_API_NOMEMORY;
+    }
+
+  rc = ospf_apiclient_send_request (oclient, msg);
+  return rc;
+}
+
+int
+ospf_apiclient_lsa_delete (struct ospf_apiclient *oclient,
+                          struct in_addr area_id, u_char lsa_type,
+                          u_char opaque_type, u_int32_t opaque_id)
+{
+  struct msg *msg;
+  int rc;
+
+  /* Only opaque LSA can be deleted */
+  if (!IS_OPAQUE_LSA (lsa_type))
+    {
+      fprintf (stderr, "Cannot delete non-opaque LSA type %d\n", lsa_type);
+      return OSPF_API_ILLEGALLSATYPE;
+    }
+
+  /* opaque_id is in host byte order and will be converted
+   * to network byte order by new_msg_delete_request */
+  msg = new_msg_delete_request (ospf_apiclient_get_seqnr (),
+                               area_id, lsa_type, opaque_type, opaque_id);
+
+  rc = ospf_apiclient_send_request (oclient, msg);
+  return rc;
+}
+
+/* -----------------------------------------------------------
+ * Followings are handlers for messages from OSPF daemon
+ * -----------------------------------------------------------
+ */
+
+static void
+ospf_apiclient_handle_ready (struct ospf_apiclient *oclient, struct msg *msg)
+{
+  struct msg_ready_notify *r;
+  r = (struct msg_ready_notify *) STREAM_DATA (msg->s);
+
+  /* Invoke registered callback function. */
+  if (oclient->ready_notify)
+    {
+      (oclient->ready_notify) (r->lsa_type, r->opaque_type, r->addr);
+    }
+}
+
+static void
+ospf_apiclient_handle_new_if (struct ospf_apiclient *oclient, struct msg *msg)
+{
+  struct msg_new_if *n;
+  n = (struct msg_new_if *) STREAM_DATA (msg->s);
+
+  /* Invoke registered callback function. */
+  if (oclient->new_if)
+    {
+      (oclient->new_if) (n->ifaddr, n->area_id);
+    }
+}
+
+static void
+ospf_apiclient_handle_del_if (struct ospf_apiclient *oclient, struct msg *msg)
+{
+  struct msg_del_if *d;
+  d = (struct msg_del_if *) STREAM_DATA (msg->s);
+
+  /* Invoke registered callback function. */
+  if (oclient->del_if)
+    {
+      (oclient->del_if) (d->ifaddr);
+    }
+}
+
+static void
+ospf_apiclient_handle_ism_change (struct ospf_apiclient *oclient,
+                                 struct msg *msg)
+{
+  struct msg_ism_change *m;
+  m = (struct msg_ism_change *) STREAM_DATA (msg->s);
+
+  /* Invoke registered callback function. */
+  if (oclient->ism_change)
+    {
+      (oclient->ism_change) (m->ifaddr, m->area_id, m->status);
+    }
+
+}
+
+static void
+ospf_apiclient_handle_nsm_change (struct ospf_apiclient *oclient,
+                                 struct msg *msg)
+{
+  struct msg_nsm_change *m;
+  m = (struct msg_nsm_change *) STREAM_DATA (msg->s);
+
+  /* Invoke registered callback function. */
+  if (oclient->nsm_change)
+    {
+      (oclient->nsm_change) (m->ifaddr, m->nbraddr, m->router_id, m->status);
+    }
+}
+
+static void
+ospf_apiclient_handle_lsa_update (struct ospf_apiclient *oclient,
+                                 struct msg *msg)
+{
+  struct msg_lsa_change_notify *cn;
+  struct lsa_header *lsa;
+  int lsalen;
+
+  cn = (struct msg_lsa_change_notify *) STREAM_DATA (msg->s);
+
+  /* Extract LSA from message */
+  lsalen = ntohs (cn->data.length);
+  lsa = XMALLOC (MTYPE_OSPF_APICLIENT, lsalen);
+  if (!lsa)
+    {
+      fprintf (stderr, "LSA update: Cannot allocate memory for LSA\n");
+      return;
+    }
+  memcpy (lsa, &(cn->data), lsalen);
+
+  /* Invoke registered update callback function */
+  if (oclient->update_notify)
+    {
+      (oclient->update_notify) (cn->ifaddr, cn->area_id, 
+                               cn->is_self_originated, lsa);
+    }
+
+  /* free memory allocated by ospf apiclient library */
+  XFREE (MTYPE_OSPF_APICLIENT, lsa);
+}
+
+static void
+ospf_apiclient_handle_lsa_delete (struct ospf_apiclient *oclient,
+                                 struct msg *msg)
+{
+  struct msg_lsa_change_notify *cn;
+  struct lsa_header *lsa;
+  int lsalen;
+
+  cn = (struct msg_lsa_change_notify *) STREAM_DATA (msg->s);
+
+  /* Extract LSA from message */
+  lsalen = ntohs (cn->data.length);
+  lsa = XMALLOC (MTYPE_OSPF_APICLIENT, lsalen);
+  if (!lsa)
+    {
+      fprintf (stderr, "LSA delete: Cannot allocate memory for LSA\n");
+      return;
+    }
+  memcpy (lsa, &(cn->data), lsalen);
+
+  /* Invoke registered update callback function */
+  if (oclient->delete_notify)
+    {
+      (oclient->delete_notify) (cn->ifaddr, cn->area_id, 
+                               cn->is_self_originated, lsa);
+    }
+
+  /* free memory allocated by ospf apiclient library */
+  XFREE (MTYPE_OSPF_APICLIENT, lsa);
+}
+
+static void
+ospf_apiclient_msghandle (struct ospf_apiclient *oclient, struct msg *msg)
+{
+  /* Call message handler function. */
+  switch (msg->hdr.msgtype)
+    {
+    case MSG_READY_NOTIFY:
+      ospf_apiclient_handle_ready (oclient, msg);
+      break;
+    case MSG_NEW_IF:
+      ospf_apiclient_handle_new_if (oclient, msg);
+      break;
+    case MSG_DEL_IF:
+      ospf_apiclient_handle_del_if (oclient, msg);
+      break;
+    case MSG_ISM_CHANGE:
+      ospf_apiclient_handle_ism_change (oclient, msg);
+      break;
+    case MSG_NSM_CHANGE:
+      ospf_apiclient_handle_nsm_change (oclient, msg);
+      break;
+    case MSG_LSA_UPDATE_NOTIFY:
+      ospf_apiclient_handle_lsa_update (oclient, msg);
+      break;
+    case MSG_LSA_DELETE_NOTIFY:
+      ospf_apiclient_handle_lsa_delete (oclient, msg);
+      break;
+    default:
+      fprintf (stderr, "ospf_apiclient_read: Unknown message type: %d\n",
+              msg->hdr.msgtype);
+      break;
+    }
+}
+
+/* -----------------------------------------------------------
+ * Callback handler registration
+ * -----------------------------------------------------------
+ */
+
+void
+ospf_apiclient_register_callback (struct ospf_apiclient *oclient,
+                                 void (*ready_notify) (u_char lsa_type,
+                                                       u_char opaque_type,
+                                                       struct in_addr addr),
+                                 void (*new_if) (struct in_addr ifaddr,
+                                                 struct in_addr area_id),
+                                 void (*del_if) (struct in_addr ifaddr),
+                                 void (*ism_change) (struct in_addr ifaddr,
+                                                     struct in_addr area_id,
+                                                     u_char status),
+                                 void (*nsm_change) (struct in_addr ifaddr,
+                                                     struct in_addr nbraddr,
+                                                     struct in_addr
+                                                     router_id,
+                                                     u_char status),
+                                 void (*update_notify) (struct in_addr
+                                                        ifaddr,
+                                                        struct in_addr
+                                                        area_id,
+                                                        u_char self_origin,
+                                                        struct lsa_header *
+                                                        lsa),
+                                 void (*delete_notify) (struct in_addr
+                                                        ifaddr,
+                                                        struct in_addr
+                                                        area_id,
+                                                        u_char self_origin,
+                                                        struct lsa_header *
+                                                        lsa))
+{
+  assert (oclient);
+  assert (update_notify);
+
+  /* Register callback function */
+  oclient->ready_notify = ready_notify;
+  oclient->new_if = new_if;
+  oclient->del_if = del_if;
+  oclient->ism_change = ism_change;
+  oclient->nsm_change = nsm_change;
+  oclient->update_notify = update_notify;
+  oclient->delete_notify = delete_notify;
+}
+
+/* -----------------------------------------------------------
+ * Asynchronous message handling
+ * -----------------------------------------------------------
+ */
+
+int
+ospf_apiclient_handle_async (struct ospf_apiclient *oclient)
+{
+  struct msg *msg;
+
+  /* Get a message */
+  msg = msg_read (oclient->fd_async);
+
+  if (!msg)
+    {
+      /* Connection broke down */
+      return -1;
+    }
+
+  /* Handle message */
+  ospf_apiclient_msghandle (oclient, msg);
+
+  /* Don't forget to free this message */
+  msg_free (msg);
+
+  return 0;
+}
diff --git a/ospfclient/ospf_apiclient.h b/ospfclient/ospf_apiclient.h
new file mode 100644 (file)
index 0000000..8098619
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Client side of OSPF API.
+ * Copyright (C) 2001, 2002, 2003 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _OSPF_APICLIENT_H
+#define _OSPF_APICLIENT_H
+
+#define MTYPE_OSPF_APICLIENT MTYPE_TMP
+
+/* Structure for the OSPF API client */
+struct ospf_apiclient
+{
+
+  /* Sockets for sync requests and async notifications */
+  int fd_sync;
+  int fd_async;
+
+  /* Pointer to callback functions */
+  void (*ready_notify) (u_char lsa_type, u_char opaque_type,
+                       struct in_addr addr);
+  void (*new_if) (struct in_addr ifaddr, struct in_addr area_id);
+  void (*del_if) (struct in_addr ifaddr);
+  void (*ism_change) (struct in_addr ifaddr, struct in_addr area_id,
+                     u_char status);
+  void (*nsm_change) (struct in_addr ifaddr, struct in_addr nbraddr,
+                     struct in_addr router_id, u_char status);
+  void (*update_notify) (struct in_addr ifaddr, struct in_addr area_id,
+                        u_char self_origin,
+                        struct lsa_header * lsa);
+  void (*delete_notify) (struct in_addr ifaddr, struct in_addr area_id,
+                        u_char self_origin,
+                        struct lsa_header * lsa);
+};
+
+
+/* ---------------------------------------------------------
+ * API function prototypes.
+ * --------------------------------------------------------- */
+
+/* Open connection to OSPF daemon. Two ports will be allocated on
+   client, sync channel at syncport and reverse channel at syncport+1 */
+struct ospf_apiclient *ospf_apiclient_connect (char *host, int syncport);
+
+/* Shutdown connection to OSPF daemon. */
+int ospf_apiclient_close (struct ospf_apiclient *oclient);
+
+/* Synchronous request to register opaque type. */
+int ospf_apiclient_register_opaque_type (struct ospf_apiclient *oclient,
+                                        u_char ltype, u_char otype);
+
+/* Synchronous request to register event mask. */
+int ospf_apiclient_register_events (struct ospf_apiclient *oclient,
+                                   u_int32_t mask);
+
+/* Register callback functions.*/
+void ospf_apiclient_register_callback (struct ospf_apiclient *oclient,
+                                      void (*ready_notify) (u_char lsa_type,
+                                                            u_char
+                                                            opaque_type,
+                                                            struct in_addr
+                                                            addr),
+                                      void (*new_if) (struct in_addr ifaddr,
+                                                      struct in_addr
+                                                      area_id),
+                                      void (*del_if) (struct in_addr ifaddr),
+                                      void (*ism_change) (struct in_addr
+                                                          ifaddr,
+                                                          struct in_addr
+                                                          area_id,
+                                                          u_char status),
+                                      void (*nsm_change) (struct in_addr
+                                                          ifaddr,
+                                                          struct in_addr
+                                                          nbraddr,
+                                                          struct in_addr
+                                                          router_id,
+                                                          u_char status),
+                                      void (*update_notify) (struct in_addr
+                                                             ifaddr,
+                                                             struct in_addr
+                                                             area_id,
+                                                             u_char selforig,
+                                                             struct
+                                                             lsa_header *
+                                                             lsa),
+                                      void (*delete_notify) (struct in_addr
+                                                             ifaddr,
+                                                             struct in_addr
+                                                             area_id,
+                                                             u_char selforig,
+                                                             struct
+                                                             lsa_header *
+                                                             lsa));
+
+/* Synchronous request to synchronize LSDB. */
+int ospf_apiclient_sync_lsdb (struct ospf_apiclient *oclient);
+
+/* Synchronous request to originate or update opaque LSA. */
+int
+ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient,
+                             struct in_addr ifaddr,
+                             struct in_addr area_id,
+                             u_char lsa_type,
+                             u_char opaque_type, u_int32_t opaque_id,
+                             void *opaquedata, int opaquelen);
+
+
+/* Synchronous request to delete opaque LSA. Parameter opaque_id is in
+   host byte order */
+int ospf_apiclient_lsa_delete (struct ospf_apiclient *oclient,
+                              struct in_addr area_id, u_char lsa_type,
+                              u_char opaque_type, u_int32_t opaque_id);
+
+/* Fetch async message and handle it  */
+int ospf_apiclient_handle_async (struct ospf_apiclient *oclient);
+
+#endif /* _OSPF_APICLIENT_H */
diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c
new file mode 100644 (file)
index 0000000..1de7644
--- /dev/null
@@ -0,0 +1,352 @@
+/* This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* 
+ * Simple program to demonstrate how OSPF API can be used. This
+ * application retrieves the LSDB from the OSPF daemon and then
+ * originates, updates and finally deletes an application-specific
+ * opaque LSA. You can use this application as a template when writing
+ * your own application.
+ */
+
+/* The following includes are needed in all OSPF API client
+   applications. */
+
+#include <zebra.h>
+#include "prefix.h" /* needed by ospf_asbr.h */
+#include "privs.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_opaque.h"
+#include "ospfd/ospf_api.h"
+#include "ospf_apiclient.h"
+
+/* privileges struct. 
+ * set cap_num_* and uid/gid to nothing to use NULL privs
+ * as ospfapiclient links in libospf.a which uses privs.
+ */
+struct zebra_privs_t ospfd_privs =
+{
+  .user = NULL,
+  .group = NULL,
+  .cap_num_p = 0,
+  .cap_num_i = 0
+};
+
+/* The following includes are specific to this application. For
+   example it uses threads from libzebra, however your application is
+   free to use any thread library (like pthreads). */
+
+#include "ospfd/ospf_dump.h" /* for ospf_lsa_header_dump */
+#include "thread.h"
+#include "log.h"
+
+/* Local portnumber for async channel. Note that OSPF API library will also
+   allocate a sync channel at ASYNCPORT+1. */
+#define ASYNCPORT 4000
+
+/* Master thread */
+struct thread_master *master;
+
+/* Global variables */
+struct ospf_apiclient *oclient;
+char **args;
+
+/* Our opaque LSAs have the following format. */
+struct my_opaque_lsa
+{
+  struct lsa_header hdr; /* include common LSA header */
+  u_char data[4]; /* our own data format then follows here */
+};
+
+
+/* ---------------------------------------------------------
+ * Threads for asynchronous messages and LSA update/delete 
+ * ---------------------------------------------------------
+ */
+
+static int
+lsa_delete (struct thread *t)
+{
+  struct ospf_apiclient *oclient;
+  struct in_addr area_id;
+  int rc;
+
+  oclient = THREAD_ARG (t);
+
+  inet_aton (args[6], &area_id);
+
+  printf ("Deleting LSA... ");
+  rc = ospf_apiclient_lsa_delete (oclient, 
+                                 area_id, 
+                                 atoi (args[2]),       /* lsa type */
+                                 atoi (args[3]),       /* opaque type */
+                                 atoi (args[4]));      /* opaque ID */
+  printf ("done, return code is = %d\n", rc);
+  return rc;
+}
+
+static int
+lsa_inject (struct thread *t)
+{
+  struct ospf_apiclient *cl;
+  struct in_addr ifaddr;
+  struct in_addr area_id;
+  u_char lsa_type;
+  u_char opaque_type;
+  u_int32_t opaque_id;
+  void *opaquedata;
+  int opaquelen;
+
+  static u_int32_t counter = 1;        /* Incremented each time invoked */
+  int rc;
+
+  cl = THREAD_ARG (t);
+
+  inet_aton (args[5], &ifaddr);
+  inet_aton (args[6], &area_id);
+  lsa_type = atoi (args[2]);
+  opaque_type = atoi (args[3]);
+  opaque_id = atoi (args[4]);
+  opaquedata = &counter;
+  opaquelen = sizeof (u_int32_t);
+
+  printf ("Originating/updating LSA with counter=%d... ", counter);
+  rc = ospf_apiclient_lsa_originate(cl, ifaddr, area_id,
+                                   lsa_type,
+                                   opaque_type, opaque_id,
+                                   opaquedata, opaquelen);
+
+  printf ("done, return code is %d\n", rc);
+
+  counter++;
+
+  return 0;
+}
+
+
+/* This thread handles asynchronous messages coming in from the OSPF
+   API server */
+static int
+lsa_read (struct thread *thread)
+{
+  struct ospf_apiclient *oclient;
+  int fd;
+  int ret;
+
+  printf ("lsa_read called\n");
+
+  oclient = THREAD_ARG (thread);
+  fd = THREAD_FD (thread);
+
+  /* Handle asynchronous message */
+  ret = ospf_apiclient_handle_async (oclient);
+  if (ret < 0) {
+    printf ("Connection closed, exiting...");
+    exit(0);
+  }
+
+  /* Reschedule read thread */
+  thread_add_read (master, lsa_read, oclient, fd);
+
+  return 0;
+}
+
+/* ---------------------------------------------------------
+ * Callback functions for asynchronous events 
+ * ---------------------------------------------------------
+ */
+
+static void
+lsa_update_callback (struct in_addr ifaddr, struct in_addr area_id,
+                    u_char is_self_originated,
+                    struct lsa_header *lsa)
+{
+  printf ("lsa_update_callback: ");
+  printf ("ifaddr: %s ", inet_ntoa (ifaddr));
+  printf ("area: %s\n", inet_ntoa (area_id));
+  printf ("is_self_origin: %u\n", is_self_originated);
+
+  /* It is important to note that lsa_header does indeed include the
+     header and the LSA payload. To access the payload, first check
+     the LSA type and then typecast lsa into the corresponding type,
+     e.g.:
+     
+     if (lsa->type == OSPF_ROUTER_LSA) {
+       struct router_lsa *rl = (struct router_lsa) lsa;
+       ...
+       u_int16_t links = rl->links;
+       ...
+    }
+  */
+       
+  ospf_lsa_header_dump (lsa);
+}
+
+static void
+lsa_delete_callback (struct in_addr ifaddr, struct in_addr area_id,
+                    u_char is_self_originated,
+                    struct lsa_header *lsa)
+{
+  printf ("lsa_delete_callback: ");
+  printf ("ifaddr: %s ", inet_ntoa (ifaddr));
+  printf ("area: %s\n", inet_ntoa (area_id));
+  printf ("is_self_origin: %u\n", is_self_originated);
+
+  ospf_lsa_header_dump (lsa);
+}
+
+static void
+ready_callback (u_char lsa_type, u_char opaque_type, struct in_addr addr)
+{
+  printf ("ready_callback: lsa_type: %d opaque_type: %d addr=%s\n",
+         lsa_type, opaque_type, inet_ntoa (addr));
+
+  /* Schedule opaque LSA originate in 5 secs */
+  thread_add_timer (master, lsa_inject, oclient, 5);
+
+  /* Schedule opaque LSA update with new value */
+  thread_add_timer (master, lsa_inject, oclient, 10);
+
+  /* Schedule delete */
+  thread_add_timer (master, lsa_delete, oclient, 30);
+}
+
+static void
+new_if_callback (struct in_addr ifaddr, struct in_addr area_id)
+{
+  printf ("new_if_callback: ifaddr: %s ", inet_ntoa (ifaddr));
+  printf ("area_id: %s\n", inet_ntoa (area_id));
+}
+
+static void
+del_if_callback (struct in_addr ifaddr)
+{
+  printf ("new_if_callback: ifaddr: %s\n ", inet_ntoa (ifaddr));
+}
+
+static void
+ism_change_callback (struct in_addr ifaddr, struct in_addr area_id,
+                    u_char state)
+{
+  printf ("ism_change: ifaddr: %s ", inet_ntoa (ifaddr));
+  printf ("area_id: %s\n", inet_ntoa (area_id));
+  printf ("state: %d [%s]\n", state, LOOKUP (ospf_ism_state_msg, state));
+}
+
+static void
+nsm_change_callback (struct in_addr ifaddr, struct in_addr nbraddr,
+                    struct in_addr router_id, u_char state)
+{
+  printf ("nsm_change: ifaddr: %s ", inet_ntoa (ifaddr));
+  printf ("nbraddr: %s\n", inet_ntoa (nbraddr));
+  printf ("router_id: %s\n", inet_ntoa (router_id));
+  printf ("state: %d [%s]\n", state, LOOKUP (ospf_nsm_state_msg, state));
+}
+
+
+/* ---------------------------------------------------------
+ * Main program 
+ * ---------------------------------------------------------
+ */
+
+static int usage()
+{
+  printf("Usage: ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>\n");
+  printf("where ospfd     : router where API-enabled OSPF daemon is running\n");
+  printf("      lsatype   : either 9, 10, or 11 depending on flooding scope\n");
+  printf("      opaquetype: 0-255 (e.g., experimental applications use > 128)\n");
+  printf("      opaqueid  : arbitrary application instance (24 bits)\n");
+  printf("      ifaddr    : interface IP address (for type 9) otherwise ignored\n");
+  printf("      areaid    : area in IP address format (for type 10) otherwise ignored\n");
+  
+  exit(1);
+}
+
+int
+main (int argc, char *argv[])
+{
+  struct thread thread;
+
+  args = argv;
+
+  /* ospfclient should be started with the following arguments:
+   * 
+   * (1) host (2) lsa_type (3) opaque_type (4) opaque_id (5) if_addr 
+   * (6) area_id
+   * 
+   * host: name or IP of host where ospfd is running
+   * lsa_type: 9, 10, or 11
+   * opaque_type: 0-255 (e.g., experimental applications use > 128) 
+   * opaque_id: arbitrary application instance (24 bits)
+   * if_addr: interface IP address (for type 9) otherwise ignored
+   * area_id: area in IP address format (for type 10) otherwise ignored
+   */
+
+  if (argc != 7)
+    {
+      usage();
+    }
+
+  /* Initialization */
+  zprivs_init (&ospfd_privs);
+  master = thread_master_create ();
+
+  /* Open connection to OSPF daemon */
+  oclient = ospf_apiclient_connect (args[1], ASYNCPORT);
+  if (!oclient)
+    {
+      printf ("Connecting to OSPF daemon on %s failed!\n",
+             args[1]);
+      exit (1);
+    }
+
+  /* Register callback functions. */
+  ospf_apiclient_register_callback (oclient,
+                                   ready_callback,
+                                   new_if_callback,
+                                   del_if_callback,
+                                   ism_change_callback,
+                                   nsm_change_callback,
+                                   lsa_update_callback, 
+                                   lsa_delete_callback);
+
+  /* Register LSA type and opaque type. */
+  ospf_apiclient_register_opaque_type (oclient, atoi (args[2]),
+                                      atoi (args[3]));
+
+  /* Synchronize database with OSPF daemon. */
+  ospf_apiclient_sync_lsdb (oclient);
+
+  /* Schedule thread that handles asynchronous messages */
+  thread_add_read (master, lsa_read, oclient, oclient->fd_async);
+
+  /* Now connection is established, run loop */
+  while (1)
+    {
+      thread_fetch (master, &thread);
+      thread_call (&thread);
+    }
+
+  /* Never reached */
+  return 0;
+}
+
diff --git a/ospfd/.gitignore b/ospfd/.gitignore
new file mode 100644 (file)
index 0000000..f7d6f09
--- /dev/null
@@ -0,0 +1,17 @@
+Makefile
+Makefile.in
+*.o
+ospfd
+ospfd.conf
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/ospfd/ChangeLog.opaque.txt b/ospfd/ChangeLog.opaque.txt
new file mode 100644 (file)
index 0000000..782e332
--- /dev/null
@@ -0,0 +1,221 @@
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2002.12.20
+
+1. Bug fixes
+
+  1.1 When an opaque LSA is being removed from (or added to) the LSDB,
+      it does not mean a change in network topology. Therefore, SPF
+      recalculation should not be triggered in that case.
+      There was an assertion failure problem "assert (rn && rn->info)"
+      inside the function "ospf_ase_incremental_update()", because
+      the upper function "ospf_lsa_maxage_walker_remover()" called it
+      when a type-11 opaque LSA is removed due to MaxAge.
+
+  1.2 Type-9 LSA is defined to have "link-local" flooding scope.
+      In the Database exchange procedure with a new neighbor, a type-9
+      LSA was added in the database summary of a DD message, even if
+      the link is different from the one that have bound to.
+
+2. Feature enhancements
+
+  2.1 Though a "wildcard" concept to handle type-9/10/11 LSAs altogether
+      has introduced about a year ago, it was only a symbol definition
+      and actual handling mechanism was not implemented. Now it works.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2002.7.8
+
+1. Bug fixes
+
+  1.1 When "ospf_delete_opaque_functab()" is called, internal structure
+      "oipt" remain unfreed. If register/delete functab is repeated,
+      illegal memory access happens due to this "oipt".
+
+  1.2 In "free_opaque_info_per_id()", there was a crucial typo which
+      ignores a condition test.
+
+      "if (oipi->lsa != NULL);" <-- semicolon!
+
+2. Feature enhancements
+
+  None.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.12.03
+
+1. Bug fixes
+
+  1.1 Though a new member "oi" has added to "struct ospf_lsa" to control
+      flooding scope of type-9 Opaque-LSAs, the value was always NULL
+      because no one set it.
+
+  1.2 In the function "show_ip_ospf_database_summary()" and "show_lsa_
+      detail_adv_router()", VTY output for type-11 Opaque-LSAs did not
+      work properly.
+
+  1.3 URL for the opaque-type assignment reference has changed.
+
+  1.4 In the file "ospf_mpls_te.c", printf formats have changed to
+      avoid compiler warning messages; "%lu" -> "%u", "%lx" -> "%x".
+      Note that this hack depends on OS, compiler and their versions. 
+
+  1.5 One of attached documentation "opaque_lsa.txt" has changed to
+      reflect the latest coding.
+
+2. Feature enhancements
+
+  2.1 Knowing that it is an ugly hack, an "officially unallocated"
+      opaque-type value 0 has newly introduced as a "wildcard",
+      which matches to all opaque-type.
+      This value must not be flooded to the network, of course.
+
+  2.2 The Opaque-core module makes use of newly introduced hooks to
+      dispatch every LSDB change (LSA installation and deletion) to
+      preregistered opaque users.
+      Therefore, by providing appropriate callback functions as new
+      parameters of "ospf_register_opaque_functab()", an opaque user
+      can refer to every LSA instance to be installed into, or to be
+      deleted from, the LSDB.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.10.31
+
+1. Bug fixes
+
+  1.1 Since each LSA has their own lifetime, they will remain in a
+      routing domain (being stored in LSDB of each router), until their
+      age naturally reach to MaxAge or explicitly being flushed by the
+      originated router. Therefore, if a router restarted with a short
+      downtime, it is possible that previously flooded self-originated
+      LSAs might received if the NSM status is not less than Exchange.
+
+      There were some problems in the way of handling self-originated
+      Opaque-LSAs if they are contained in a received LSUpd message,
+      but not installed to the local LSDB yet.
+      Regardless of some conditions to start originating Opaque-LSAs
+      (there should be at least one opaque-capable full-state neighbor),
+      the function "ospf_flood()" will be called to flood and install
+      this brand-new looking LSA.
+      As the result, when the NSM of an opaque-capable neighbor gets
+      full, internal state inconsistency happens; a user of Opaque-LSA
+      such as MPLS-TE can refer to self-originated LSAs in the local
+      LSDB, but cannot modify their contents...
+
+      Above problems have fixed with a policy "flush it from the whole
+      routing domain and keep silent until the flushing completed".
+      By using this sweeping technique, we can be free from confusion
+      caused by self-originated LSAs received via network. 
+
+  1.2 The function "ospf_opaque_type_name()" contained massive ifdefs
+      corresponding to each "opaque-type".
+      These unnecessary ifdefs are removed completely.
+
+  1.3 In the function "ospf_delete_opaque_functab()", there was an
+      improper loop control that causes illegal memory access.
+      Original coding was "next = nextnode (node)".
+
+  1.4 The function "ospf_mpls_te_ism_change()" could not handle the
+      case when the ISM changes from Waiting to DR/BDR/Other.
+      So, there was a case that even if one of an ISM become
+      operational and MPLS-TE module has started, the corresponding
+      Opaque-LSA cannot be originated.
+
+  1.5 The function "ospf_opaque_lsa_reoriginate_schedule()" did not
+      allow to be called multiple times, simply because handling
+      module for the given "lsa-type & opaque-type" already exists.
+      But this assumption seems to be wrong.
+      Change the policy to allow this function to be called multiple
+      times and let the caller to decide what should do when the
+      corresponding callback function "(* functab->lsa_originator)()"
+      is called.
+
+2. Feature enhancements
+
+  2.1 The global bitmap "opaque" has introduced instead of former flag
+      "OpaqueCapable", to store complex conditions to handle Opaque-LSAs.
+
+  2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic
+      -06.txt", no significant changes with 05 version, though.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.08.03
+
+1. Bug fixes
+
+  1.1 Even if the ospfd started with opaque capability enabled, when
+      the ospfd receives an unknown opaque-type (unregistered by the
+      function "ospf_register_opaque_functab()" beforehand), the LSA
+      was discarded. As the result, only the opaque-LSAs that have
+      commonly registered by opaque-capable ospf routers can be
+      flooded in a routing domain.
+
+      This behavior has fixed so that arbitrary opaque-type LSAs can
+      be flooded among opaque-capable ospf routers.
+      If the ospfd has opaque-LSA capability but disabled at runtime,
+      received opaque-LSAs can be accepted and registered to LSDB as
+      is, but not be flooded to the network; those opaque LSAs will
+      remain in LSDB until explicitly flushed by incoming LSUpd
+      messages with MaxAge, or their age naturally reaches to MaxAge.
+
+  1.2 The function "ospf_register_opaque_functab()" did not check
+      if the entry corresponding to the given "lsa-type, opaque-type"
+      combination already exists or not.
+      This problem has fixed not to allow multiple registration.
+
+  1.3 Since type-11 (AS external) LSAs will be flooded beyond areas,
+      there is little relationship between "struct lsa" and "struct
+      area". More specifically, the pointer address "lsa->area" can
+      be NULL if the lsa-type is 11, thus an illegal memory access
+      will happen. This problem has fixed.
+
+  1.4 When self-originated opaque-LSAs are received via network and
+      if the corresponding opaque-type functions are not available
+      (they have already deleted) at that time, those LSAs were
+      dropped due to "unknown opaque-type" error.
+      After the problem 1.1 has fixed, those "self-originated" LSAs
+      were registered to LSDB and then flooded to the network, even
+      if the processing functions did not exist...
+
+      After all, this problem has fixed so that those LSAs should
+      explicitly be flushed from the routing domain immediately, if
+      the processing functions cannot find at that time.
+
+  1.5 Some typo have fixed.
+
+      --- EXAMPLE ---
+      static int
+      opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent)
+                                                          ^^^^^
+      --- EXAMPLE ---
+
+2. Feature enhancements
+
+  2.1 According to the description of rfc2328 in section 10.8, any
+      change in the router's optional capabilities should trigger
+      the option re-negotiation procedures with neighbors.
+
+      --- EXCERPT ---
+                              If for some reason the router's optional
+        capabilities change, the Database Exchange procedure should be
+        restarted by reverting to neighbor state ExStart.
+      --- EXCERPT ---
+
+      For the opaque-capability changes, this feature has implemented.
+      More specifically, if "ospf opaque-lsa" or "no ospf opaque-lsa"
+      VTY command is given at runtime, all self-originated LSAs will
+      be flushed immediately and then all neighbor status will be
+      forced to ExStart by generating SeqNumberMismatch events.
+
+  2.1 When we change opaque-capability dynamically (ON -> OFF -> ON),
+      there was no trigger at "OFF->ON" timing to reactivate opaque
+      LSA handling modules (such as MPLS-TE) that have once forcibly
+      stopped at "ON->OFF" timing.
+      Now this dynamic reactivation feature has added.
+
+  2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic
+      -05.txt", no significant changes with 04 version, though.
+
+----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * -----
+Changes 2001.03.28
+
+  Initial release of Opaque-LSA/MPLS-TE extensions for the zebra/ospfd.
diff --git a/ospfd/Makefile.am b/ospfd/Makefile.am
new file mode 100644 (file)
index 0000000..f586d73
--- /dev/null
@@ -0,0 +1,40 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+AM_CFLAGS = $(WERROR)
+DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+lib_LTLIBRARIES = libospf.la
+libospf_la_LDFLAGS = -version-info 0:0:0
+libospf_la_LIBADD = ../lib/libzebra.la
+
+sbin_PROGRAMS = ospfd
+
+libospf_la_SOURCES = \
+       ospfd.c ospf_zebra.c ospf_interface.c ospf_ism.c ospf_neighbor.c \
+       ospf_nsm.c ospf_dump.c ospf_network.c ospf_packet.c ospf_lsa.c \
+       ospf_spf.c ospf_route.c ospf_ase.c ospf_abr.c ospf_ia.c ospf_flood.c \
+       ospf_lsdb.c ospf_asbr.c ospf_routemap.c ospf_snmp.c \
+       ospf_opaque.c ospf_te.c ospf_ri.c ospf_vty.c ospf_api.c ospf_apiserver.c
+
+ospfdheaderdir = $(pkgincludedir)/ospfd
+
+ospfdheader_HEADERS = \
+       ospf_api.h ospf_asbr.h ospf_dump.h ospf_lsa.h ospf_lsdb.h \
+       ospf_nsm.h ospf_ism.h ospf_opaque.h ospfd.h
+
+noinst_HEADERS = \
+       ospf_interface.h ospf_neighbor.h ospf_network.h ospf_packet.h \
+       ospf_zebra.h ospf_spf.h ospf_route.h ospf_ase.h ospf_abr.h ospf_ia.h \
+       ospf_flood.h ospf_snmp.h ospf_te.h ospf_ri.h ospf_vty.h ospf_apiserver.h
+
+ospfd_SOURCES = ospf_main.c
+
+ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@ @LIBM@
+
+EXTRA_DIST = OSPF-MIB.txt OSPF-TRAP-MIB.txt ChangeLog.opaque.txt
+
+examplesdir = $(exampledir)
+dist_examples_DATA = ospfd.conf.sample
+
diff --git a/ospfd/OSPF-ALIGNMENT.txt b/ospfd/OSPF-ALIGNMENT.txt
new file mode 100644 (file)
index 0000000..dac6182
--- /dev/null
@@ -0,0 +1,119 @@
+$Id: OSPF-ALIGNMENT.txt,v 1.1 2004/11/17 17:59:52 gdt Exp $
+
+Greg Troxel <gdt@ir.bbn.com>
+2004-11-17
+
+The OSPF specification (RFC2328) and the OSPF Opaque LSA specification
+(RFC2370) are ambiguous about LSAs whose data section is not an
+integral multiple of 4 octets.  This note examines the issue and
+proposes clarifications to ensure interoperability.
+
+RFC2328 does not specify that LSA lengths be a multiple of 4.
+It does not require that LSAs in update packets be aligned.
+However, all structures defined by RFC2328 are multiples of 4, and
+thus update packets with those structures must be aligned.
+LSA length is defined in Appendix A.4 as
+
+    length
+        The length in bytes of the LSA.  This includes the 20 byte LSA
+        header.
+
+RFC2370 defines Opaque LSAs, which are intended to contain arbitrary
+data:
+
+   This memo defines enhancements to the OSPF protocol to support a new
+   class of link-state advertisements (LSA) called Opaque LSAs.  Opaque
+   LSAs provide a generalized mechanism to allow for the future
+   extensibility of OSPF. Opaque LSAs consist of a standard LSA header
+   followed by application-specific information.  The information field
+   may be used directly by OSPF or by other applications.  Standard OSPF
+   link-state database flooding mechanisms are used to distribute Opaque
+   LSAs to all or some limited portion of the OSPF topology.
+
+
+Later, 2370 says:
+
+   Opaque LSAs contain some number of octets (of application-specific
+   data) padded to 32-bit alignment.
+
+This can be interpreted in several ways:
+
+A) The payload may be any number of octets, and the length field
+reflects the payload length (e.g. length 23 for 3 octets of payload),
+but there are padding octets following the LSA in packets, so that the
+next LSA starts on a 4-octet boundary.  (This approach is common in
+the BSD user/kernel interface.)
+
+B) The payload must be a multiple of 4 octets, so that the length is a
+multiple of 4 octets.  This corresponds to an implementation that
+treats an Opaque LSA publish request that is not a multiple of 4
+octets as an error.
+
+C) The payload can be any number of octets, but padding is added and
+included in the length field.  This interpretation corresponds to an
+OSPF implementation that accepts a publish request for an Opaque LSA
+that is not a multiple of 4 octets.  This interpretation is
+nonsensical, because it claims to represent arbitrary lengths, but
+does not actually do so --- the receiver cannot distinguish padding
+from supplied data.
+
+D) Accept according to A, and transmit according to B.
+
+Option A arguably violates RFC 2328, which doesn't say anything about
+adding padding (A.3.5 shows a diagram of adjacent LSAs which are shown
+as all multiples of 4).  This option is thus likely to lead to a lack
+of interoperability.
+
+Option B restricts what data can be represented as an Opaque LSA, but
+probably not in a serious way.  It is likely to lead to
+interoperability in that the complex case of non-multiple-of-4 lengths
+will not arise.
+
+However, an implementation that follows A and emits an LSA with
+payload length not a multiple of 4 will not interoperate with an
+Option B implementation.
+
+Given that all known and documented uses of Opaque LSAs seem to be
+multiples of 4 octets, we choose Option B as the clarification.
+
+CLARIFYING TEXT
+
+In RFC2328:
+
+In section A.4, add a second sentence about length:
+
+    length
+        The length in bytes of the LSA.  This includes the 20 byte LSA
+        header.  The length must be an integral multiple of 4 bytes.
+
+Add to the list in Section 13:
+
+    Verify that the length of the LSA is a multiple of 4 bytes.  If
+    not, discard the entire Link State Update Packet.
+
+In RFC2380:
+
+Change text:
+
+   Opaque LSAs contain some number of octets (of application-specific
+   data) padded to 32-bit alignment.
+
+to:
+
+   Opaque LSAs contain some a number of octets (of
+   application-specific data).  The number of octets must be a
+   multiple of four.
+
+
+HOW THIS ISSUE AROSE
+
+At BBN, we use Opaque LSAs to exchange data among routers; the format
+of the data is naturally aligned to 4 bytes, and thus does not raise
+this issue.  We created a test program to publish Opaque data via IPC
+to the OSPF daemon (quagga), and this program accepts strings on the
+command line to publish.  We then used this test program to publish
+software version strings.  Quagga's ospfd then crashed on a
+NetBSD/sparc64 machine with an alignment fault, because the odd-length
+LSAs were marshalled into a link-state update packet with no padding.
+While this behavior was a clear violation of RFC2380, it was not clear
+how to remedy the problem.
diff --git a/ospfd/OSPF-MIB.txt b/ospfd/OSPF-MIB.txt
new file mode 100644 (file)
index 0000000..217c1e5
--- /dev/null
@@ -0,0 +1,2723 @@
+OSPF-MIB DEFINITIONS ::= BEGIN
+
+    IMPORTS
+            MODULE-IDENTITY, OBJECT-TYPE, Counter32, Gauge32,
+            Integer32, IpAddress
+                FROM SNMPv2-SMI
+            TEXTUAL-CONVENTION, TruthValue, RowStatus
+                FROM SNMPv2-TC
+            MODULE-COMPLIANCE, OBJECT-GROUP          FROM SNMPv2-CONF
+            mib-2                                    FROM RFC1213-MIB;
+
+--  This MIB module uses the extended OBJECT-TYPE macro as
+--  defined in [9].
+
+ospf MODULE-IDENTITY
+        LAST-UPDATED "9501201225Z" -- Fri Jan 20 12:25:50 PST 1995
+        ORGANIZATION "IETF OSPF Working Group"
+        CONTACT-INFO
+       "       Fred Baker
+       Postal: Cisco Systems
+               519 Lado Drive
+               Santa Barbara, California 93111
+       Tel:    +1 805 681 0115
+       E-Mail: fred@cisco.com
+
+               Rob Coltun
+       Postal: RainbowBridge Communications
+       Tel:    (301) 340-9416
+       E-Mail: rcoltun@rainbow-bridge.com"
+    DESCRIPTION
+       "The MIB module to describe the OSPF Version 2
+       Protocol"
+    ::= { mib-2 14 }
+
+--  The Area ID, in OSPF, has the same format as an IP Address,
+--  but has the function of defining a summarization point for
+--  Link State Advertisements
+
+AreaID ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "An OSPF Area Identifier."
+    SYNTAX      IpAddress
+
+
+--  The Router ID, in OSPF, has the same format as an IP Address,
+--  but identifies the router independent of its IP Address.
+
+RouterID ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "A OSPF Router Identifier."
+    SYNTAX      IpAddress
+
+
+--  The OSPF Metric is defined as an unsigned value in the range
+
+Metric ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The OSPF Internal Metric."
+    SYNTAX      Integer32 (0..'FFFF'h)
+
+BigMetric ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The OSPF External Metric."
+    SYNTAX      Integer32 (0..'FFFFFF'h)
+
+--  Status Values
+
+Status ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The status of an interface: 'enabled' indicates that
+       it is willing to communicate with other OSPF Routers,
+       while 'disabled' indicates that it is not."
+    SYNTAX      INTEGER { enabled (1), disabled (2) }
+
+--  Time Durations measured in seconds
+
+PositiveInteger ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "A positive integer. Values in excess are precluded as
+       unnecessary and prone to interoperability issues."
+    SYNTAX      Integer32 (0..'7FFFFFFF'h)
+
+HelloRange ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The range of intervals on which hello messages are
+       exchanged."
+    SYNTAX      Integer32 (1..'FFFF'h)
+
+UpToMaxAge ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The values that one might find or configure for
+       variables bounded by the maximum age of an LSA."
+    SYNTAX      Integer32 (0..3600)
+
+
+--  The range of ifIndex
+
+InterfaceIndex ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The range of ifIndex."
+    SYNTAX      Integer32
+
+
+--  Potential Priorities for the Designated Router Election
+
+DesignatedRouterPriority ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "The values defined for the priority of a system for
+       becoming the designated router."
+    SYNTAX      Integer32 (0..'FF'h)
+
+TOSType ::= TEXTUAL-CONVENTION
+    STATUS      current
+    DESCRIPTION
+       "Type of Service is defined as a mapping to the IP Type of
+       Service Flags as defined in the IP Forwarding Table MIB
+
+       +-----+-----+-----+-----+-----+-----+-----+-----+
+       |                 |                       |     |
+       |   PRECEDENCE    |    TYPE OF SERVICE    |  0  |
+       |                 |                       |     |
+       +-----+-----+-----+-----+-----+-----+-----+-----+
+
+                IP TOS                IP TOS
+           Field     Policy      Field     Policy
+
+           Contents    Code      Contents    Code
+           0 0 0 0  ==>   0      0 0 0 1  ==>   2
+           0 0 1 0  ==>   4      0 0 1 1  ==>   6
+           0 1 0 0  ==>   8      0 1 0 1  ==>  10
+           0 1 1 0  ==>  12      0 1 1 1  ==>  14
+           1 0 0 0  ==>  16      1 0 0 1  ==>  18
+           1 0 1 0  ==>  20      1 0 1 1  ==>  22
+           1 1 0 0  ==>  24      1 1 0 1  ==>  26
+           1 1 1 0  ==>  28      1 1 1 1  ==>  30
+
+       The remaining values are left for future definition."
+    SYNTAX      Integer32 (0..30)
+
+
+--  OSPF General Variables
+
+--      These parameters apply globally to the Router's
+--      OSPF Process.
+
+ospfGeneralGroup OBJECT IDENTIFIER ::= { ospf 1 }
+
+
+    ospfRouterId OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "A  32-bit  integer  uniquely  identifying  the
+           router in the Autonomous System.
+
+           By  convention,  to  ensure  uniqueness,   this
+           should  default  to  the  value  of  one of the
+           router's IP interface addresses."
+       REFERENCE
+          "OSPF Version 2, C.1 Global parameters"
+      ::= { ospfGeneralGroup 1 }
+
+
+    ospfAdminStat OBJECT-TYPE
+        SYNTAX   Status
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "The  administrative  status  of  OSPF  in  the
+           router.   The  value 'enabled' denotes that the
+           OSPF Process is active on at least  one  inter-
+           face;  'disabled'  disables  it  on  all inter-
+           faces."
+       ::= { ospfGeneralGroup 2 }
+
+    ospfVersionNumber OBJECT-TYPE
+        SYNTAX   INTEGER    { version2 (2) }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The current version number of the OSPF  proto-
+           col is 2."
+       REFERENCE
+          "OSPF Version 2, Title"
+      ::= { ospfGeneralGroup 3 }
+
+
+    ospfAreaBdrRtrStatus OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A flag to note whether this router is an  area
+           border router."
+       REFERENCE
+          "OSPF Version 2, Section 3 Splitting the AS into
+          Areas"
+      ::= { ospfGeneralGroup 4 }
+
+
+    ospfASBdrRtrStatus OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "A flag to note whether this router is  config-
+           ured as an Autonomous System border router."
+       REFERENCE
+          "OSPF Version 2, Section 3.3  Classification  of
+          routers"
+      ::= { ospfGeneralGroup 5 }
+
+    ospfExternLsaCount OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of external (LS type 5)  link-state
+           advertisements in the link-state database."
+       REFERENCE
+          "OSPF Version 2, Appendix A.4.5 AS external link
+          advertisements"
+      ::= { ospfGeneralGroup 6 }
+
+
+    ospfExternLsaCksumSum OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32-bit unsigned sum of the LS checksums of
+           the  external  link-state  advertisements  con-
+           tained in the link-state  database.   This  sum
+           can  be  used  to determine if there has been a
+           change in a router's link state  database,  and
+           to  compare  the  link-state  database  of  two
+           routers."
+       ::= { ospfGeneralGroup 7 }
+
+
+    ospfTOSSupport OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "The router's support for type-of-service rout-
+           ing."
+       REFERENCE
+          "OSPF Version 2,  Appendix  F.1.2  Optional  TOS
+          support"
+      ::= { ospfGeneralGroup 8 }
+
+    ospfOriginateNewLsas OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of  new  link-state  advertisements
+           that  have been originated.  This number is in-
+           cremented each time the router originates a new
+           LSA."
+       ::= { ospfGeneralGroup 9 }
+
+
+    ospfRxNewLsas OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of  link-state  advertisements  re-
+           ceived  determined  to  be  new instantiations.
+           This number does not include  newer  instantia-
+           tions  of self-originated link-state advertise-
+           ments."
+       ::= { ospfGeneralGroup 10 }
+
+    ospfExtLsdbLimit OBJECT-TYPE
+        SYNTAX   Integer32 (-1..'7FFFFFFF'h)
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "The  maximum   number   of   non-default   AS-
+           external-LSAs entries that can be stored in the
+           link-state database.  If the value is -1,  then
+           there is no limit.
+
+           When the number of non-default AS-external-LSAs
+           in   a  router's  link-state  database  reaches
+           ospfExtLsdbLimit, the router  enters  Overflow-
+           State.   The   router  never  holds  more  than
+           ospfExtLsdbLimit  non-default  AS-external-LSAs
+           in  its  database. OspfExtLsdbLimit MUST be set
+           identically in all routers attached to the OSPF
+           backbone  and/or  any regular OSPF area. (i.e.,
+           OSPF stub areas and NSSAs are excluded)."
+       DEFVAL { -1 }
+       ::= { ospfGeneralGroup 11 }
+
+    ospfMulticastExtensions OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "A Bit Mask indicating whether  the  router  is
+           forwarding  IP  multicast  (Class  D) datagrams
+           based on the algorithms defined in  the  Multi-
+           cast Extensions to OSPF.
+
+           Bit 0, if set, indicates that  the  router  can
+           forward  IP multicast datagrams in the router's
+           directly attached areas (called intra-area mul-
+           ticast routing).
+
+           Bit 1, if set, indicates that  the  router  can
+           forward  IP  multicast  datagrams  between OSPF
+           areas (called inter-area multicast routing).
+
+           Bit 2, if set, indicates that  the  router  can
+           forward  IP  multicast  datagrams between Auto-
+           nomous Systems (called inter-AS multicast rout-
+           ing).
+
+           Only certain combinations of bit  settings  are
+           allowed,  namely: 0 (no multicast forwarding is
+           enabled), 1 (intra-area multicasting  only),  3
+           (intra-area  and  inter-area  multicasting),  5
+           (intra-area and inter-AS  multicasting)  and  7
+           (multicasting  everywhere). By default, no mul-
+           ticast forwarding is enabled."
+       DEFVAL { 0 }
+       ::= { ospfGeneralGroup 12 }
+
+    ospfExitOverflowInterval OBJECT-TYPE
+        SYNTAX   PositiveInteger
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "The number of  seconds  that,  after  entering
+           OverflowState,  a  router will attempt to leave
+           OverflowState. This allows the router to  again
+           originate  non-default  AS-external-LSAs.  When
+           set to 0, the router will not  leave  Overflow-
+           State until restarted."
+       DEFVAL { 0 }
+       ::= { ospfGeneralGroup 13 }
+
+
+    ospfDemandExtensions OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "The router's support for demand routing."
+       REFERENCE
+          "OSPF Version 2, Appendix on Demand Routing"
+      ::= { ospfGeneralGroup 14 }
+
+
+--      The OSPF Area Data Structure contains information
+--      regarding the various areas. The interfaces and
+--      virtual links are configured as part of these areas.
+--      Area 0.0.0.0, by definition, is the Backbone Area
+
+
+    ospfAreaTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfAreaEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "Information describing the configured  parame-
+           ters  and cumulative statistics of the router's
+           attached areas."
+       REFERENCE
+          "OSPF Version 2, Section 6  The Area Data Struc-
+          ture"
+      ::= { ospf 2 }
+
+
+    ospfAreaEntry OBJECT-TYPE
+        SYNTAX   OspfAreaEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "Information describing the configured  parame-
+           ters  and  cumulative  statistics of one of the
+           router's attached areas."
+       INDEX { ospfAreaId }
+       ::= { ospfAreaTable 1 }
+
+OspfAreaEntry ::=
+    SEQUENCE {
+        ospfAreaId
+            AreaID,
+        ospfAuthType
+            Integer32,
+        ospfImportAsExtern
+            INTEGER,
+        ospfSpfRuns
+            Counter32,
+        ospfAreaBdrRtrCount
+            Gauge32,
+        ospfAsBdrRtrCount
+            Gauge32,
+        ospfAreaLsaCount
+            Gauge32,
+        ospfAreaLsaCksumSum
+            Integer32,
+        ospfAreaSummary
+            INTEGER,
+        ospfAreaStatus
+            RowStatus
+              }
+
+    ospfAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A 32-bit integer uniquely identifying an area.
+           Area ID 0.0.0.0 is used for the OSPF backbone."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaEntry 1 }
+
+
+    ospfAuthType OBJECT-TYPE
+        SYNTAX   Integer32
+                    -- none (0),
+                    -- simplePassword (1)
+                    -- md5 (2)
+                    -- reserved for specification by IANA (> 2)
+        MAX-ACCESS   read-create
+        STATUS   obsolete
+        DESCRIPTION
+           "The authentication type specified for an area.
+           Additional authentication types may be assigned
+           locally on a per Area basis."
+       REFERENCE
+          "OSPF Version 2, Appendix E Authentication"
+      DEFVAL { 0 }        -- no authentication, by default
+      ::= { ospfAreaEntry 2 }
+
+    ospfImportAsExtern OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    importExternal (1),
+                    importNoExternal (2),
+                    importNssa (3)
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The area's support for importing  AS  external
+           link- state advertisements."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      DEFVAL { importExternal }
+      ::= { ospfAreaEntry 3 }
+
+
+    ospfSpfRuns OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of times that the intra-area  route
+           table  has  been  calculated  using this area's
+           link-state database.  This  is  typically  done
+           using Dijkstra's algorithm."
+       ::= { ospfAreaEntry 4 }
+
+
+    ospfAreaBdrRtrCount OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The total number of area border routers reach-
+           able within this area.  This is initially zero,
+           and is calculated in each SPF Pass."
+       ::= { ospfAreaEntry 5 }
+
+    ospfAsBdrRtrCount OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The total number of Autonomous  System  border
+           routers  reachable  within  this area.  This is
+           initially zero, and is calculated in  each  SPF
+           Pass."
+       ::= { ospfAreaEntry 6 }
+
+
+    ospfAreaLsaCount OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The total number of link-state  advertisements
+           in  this  area's link-state database, excluding
+           AS External LSA's."
+       ::= { ospfAreaEntry 7 }
+
+
+    ospfAreaLsaCksumSum OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32-bit unsigned sum of the link-state  ad-
+           vertisements'  LS  checksums  contained in this
+           area's link-state database.  This sum  excludes
+           external (LS type 5) link-state advertisements.
+           The sum can be used to determine if  there  has
+           been  a  change  in a router's link state data-
+           base, and to compare the link-state database of
+           two routers."
+       DEFVAL   { 0 }
+       ::= { ospfAreaEntry 8 }
+
+    ospfAreaSummary OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    noAreaSummary (1),
+                    sendAreaSummary (2)
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The variable ospfAreaSummary controls the  im-
+           port  of  summary LSAs into stub areas.  It has
+           no effect on other areas.
+
+           If it is noAreaSummary, the router will neither
+           originate  nor  propagate summary LSAs into the
+           stub area.  It will rely entirely  on  its  de-
+           fault route.
+
+           If it is sendAreaSummary, the router will  both
+           summarize and propagate summary LSAs."
+       DEFVAL   { noAreaSummary }
+       ::= { ospfAreaEntry 9 }
+
+
+    ospfAreaStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfAreaEntry 10 }
+
+
+--  OSPF Area Default Metric Table
+
+--      The OSPF Area Default Metric Table describes the metrics
+--      that a default Area Border Router will advertise into a
+--      Stub area.
+
+
+    ospfStubAreaTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfStubAreaEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The set of metrics that will be advertised  by
+           a default Area Border Router into a stub area."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2, Area Parameters"
+      ::= { ospf 3 }
+
+
+    ospfStubAreaEntry OBJECT-TYPE
+        SYNTAX   OspfStubAreaEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The metric for a given Type  of  Service  that
+           will  be  advertised  by  a default Area Border
+           Router into a stub area."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2, Area Parameters"
+      INDEX { ospfStubAreaId, ospfStubTOS }
+      ::= { ospfStubAreaTable 1 }
+
+OspfStubAreaEntry ::=
+    SEQUENCE {
+        ospfStubAreaId
+            AreaID,
+        ospfStubTOS
+            TOSType,
+        ospfStubMetric
+            BigMetric,
+        ospfStubStatus
+            RowStatus,
+        ospfStubMetricType
+            INTEGER
+              }
+
+    ospfStubAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32 bit identifier for the Stub  Area.   On
+           creation,  this  can  be  derived  from the in-
+           stance."
+       ::= { ospfStubAreaEntry 1 }
+
+
+    ospfStubTOS OBJECT-TYPE
+        SYNTAX   TOSType
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The  Type  of  Service  associated  with   the
+           metric.   On creation, this can be derived from
+           the instance."
+       ::= { ospfStubAreaEntry 2 }
+
+
+    ospfStubMetric OBJECT-TYPE
+        SYNTAX   BigMetric
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The metric value applied at the indicated type
+           of  service.  By default, this equals the least
+           metric at the type of service among the  inter-
+           faces to other areas."
+       ::= { ospfStubAreaEntry 3 }
+
+
+    ospfStubStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfStubAreaEntry 4 }
+
+    ospfStubMetricType OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    ospfMetric (1),                -- OSPF Metric
+                    comparableCost (2),        -- external type 1
+                    nonComparable  (3)        -- external type 2
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the type of metric  ad-
+           vertised as a default route."
+       DEFVAL   { ospfMetric }
+       ::= { ospfStubAreaEntry 5 }
+
+--  OSPF Link State Database
+
+--      The Link State Database contains the Link State
+--      Advertisements from throughout the areas that the
+--      device is attached to.
+
+
+    ospfLsdbTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfLsdbEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The OSPF Process's Link State Database."
+       REFERENCE
+          "OSPF Version 2, Section 12  Link  State  Adver-
+          tisements"
+      ::= { ospf 4 }
+
+
+    ospfLsdbEntry OBJECT-TYPE
+        SYNTAX   OspfLsdbEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A single Link State Advertisement."
+       INDEX { ospfLsdbAreaId, ospfLsdbType,
+               ospfLsdbLsid, ospfLsdbRouterId }
+       ::= { ospfLsdbTable 1 }
+
+OspfLsdbEntry ::=
+    SEQUENCE {
+        ospfLsdbAreaId
+            AreaID,
+        ospfLsdbType
+            INTEGER,
+        ospfLsdbLsid
+            IpAddress,
+        ospfLsdbRouterId
+            RouterID,
+        ospfLsdbSequence
+            Integer32,
+        ospfLsdbAge
+            Integer32,
+        ospfLsdbChecksum
+            Integer32,
+        ospfLsdbAdvertisement
+            OCTET STRING
+              }
+    ospfLsdbAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32 bit identifier of the Area  from  which
+           the LSA was received."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfLsdbEntry 1 }
+
+-- External Link State Advertisements are permitted
+-- for backward compatibility, but should be displayed in
+-- the ospfExtLsdbTable rather than here.
+
+    ospfLsdbType OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    routerLink (1),
+                    networkLink (2),
+                    summaryLink (3),
+                    asSummaryLink (4),
+                    asExternalLink (5), -- but see ospfExtLsdbTable
+                    multicastLink (6),
+                    nssaExternalLink (7)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The type  of  the  link  state  advertisement.
+           Each  link state type has a separate advertise-
+           ment format."
+       REFERENCE
+          "OSPF Version 2, Appendix A.4.1 The  Link  State
+          Advertisement header"
+      ::= { ospfLsdbEntry 2 }
+
+    ospfLsdbLsid OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Link State ID is an LS Type Specific field
+           containing either a Router ID or an IP Address;
+           it identifies the piece of the  routing  domain
+           that is being described by the advertisement."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.4 Link State ID"
+      ::= { ospfLsdbEntry 3 }
+    ospfLsdbRouterId OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32 bit number that uniquely identifies the
+           originating router in the Autonomous System."
+       REFERENCE
+          "OSPF Version 2, Appendix C.1 Global parameters"
+      ::= { ospfLsdbEntry 4 }
+
+--  Note that the OSPF Sequence Number is a 32 bit signed
+--  integer.  It starts with the value '80000001'h,
+--  or -'7FFFFFFF'h, and increments until '7FFFFFFF'h
+--  Thus, a typical sequence number will be very negative.
+
+    ospfLsdbSequence OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The sequence number field is a  signed  32-bit
+           integer.   It  is used to detect old and dupli-
+           cate link state advertisements.  The  space  of
+           sequence  numbers  is  linearly  ordered.   The
+           larger the sequence number the more recent  the
+           advertisement."
+       REFERENCE
+          "OSPF Version  2,  Section  12.1.6  LS  sequence
+          number"
+      ::= { ospfLsdbEntry 5 }
+
+
+    ospfLsdbAge OBJECT-TYPE
+        SYNTAX   Integer32    -- Should be 0..MaxAge
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "This field is the age of the link state adver-
+           tisement in seconds."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.1 LS age"
+      ::= { ospfLsdbEntry 6 }
+
+    ospfLsdbChecksum OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "This field is the  checksum  of  the  complete
+           contents  of  the  advertisement, excepting the
+           age field.  The age field is excepted  so  that
+           an   advertisement's  age  can  be  incremented
+           without updating the  checksum.   The  checksum
+           used  is  the same that is used for ISO connec-
+           tionless datagrams; it is commonly referred  to
+           as the Fletcher checksum."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.7 LS checksum"
+      ::= { ospfLsdbEntry 7 }
+
+
+    ospfLsdbAdvertisement OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE (1..65535))
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The entire Link State Advertisement, including
+           its header."
+       REFERENCE
+          "OSPF Version 2, Section 12  Link  State  Adver-
+          tisements"
+      ::= { ospfLsdbEntry 8 }
+
+
+--  Address Range Table
+
+--      The Address Range Table acts as an adjunct to the Area
+--      Table; It describes those Address Range Summaries that
+--      are configured to be propagated from an Area to reduce
+--      the amount of information about it which is known beyond
+--      its borders.
+
+    ospfAreaRangeTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfAreaRangeEntry
+        MAX-ACCESS   not-accessible
+        STATUS   obsolete
+        DESCRIPTION
+           "A range if IP addresses  specified  by  an  IP
+           address/IP  network  mask  pair.   For example,
+           class B address range of X.X.X.X with a network
+           mask  of  255.255.0.0 includes all IP addresses
+           from X.X.0.0 to X.X.255.255"
+       REFERENCE
+          "OSPF Version 2, Appendix C.2  Area parameters"
+      ::= { ospf 5 }
+    ospfAreaRangeEntry OBJECT-TYPE
+        SYNTAX   OspfAreaRangeEntry
+        MAX-ACCESS   not-accessible
+        STATUS   obsolete
+        DESCRIPTION
+           "A range if IP addresses  specified  by  an  IP
+           address/IP  network  mask  pair.   For example,
+           class B address range of X.X.X.X with a network
+           mask  of  255.255.0.0 includes all IP addresses
+           from X.X.0.0 to X.X.255.255"
+       REFERENCE
+          "OSPF Version 2, Appendix C.2  Area parameters"
+      INDEX { ospfAreaRangeAreaId, ospfAreaRangeNet }
+      ::= { ospfAreaRangeTable 1 }
+
+OspfAreaRangeEntry ::=
+    SEQUENCE {
+        ospfAreaRangeAreaId
+            AreaID,
+        ospfAreaRangeNet
+            IpAddress,
+        ospfAreaRangeMask
+            IpAddress,
+        ospfAreaRangeStatus
+            RowStatus,
+        ospfAreaRangeEffect
+            INTEGER
+              }
+
+    ospfAreaRangeAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   obsolete
+        DESCRIPTION
+           "The Area the Address  Range  is  to  be  found
+           within."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaRangeEntry 1 }
+
+
+    ospfAreaRangeNet OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   obsolete
+        DESCRIPTION
+           "The IP Address of the Net or Subnet  indicated
+           by the range."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaRangeEntry 2 }
+
+
+    ospfAreaRangeMask OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-create
+        STATUS   obsolete
+        DESCRIPTION
+           "The Subnet Mask that pertains to  the  Net  or
+           Subnet."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaRangeEntry 3 }
+
+    ospfAreaRangeStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   obsolete
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfAreaRangeEntry 4 }
+
+
+    ospfAreaRangeEffect OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    advertiseMatching (1),
+                    doNotAdvertiseMatching (2)
+                  }
+        MAX-ACCESS   read-create
+        STATUS   obsolete
+        DESCRIPTION
+           "Subnets subsumed by ranges either trigger  the
+           advertisement  of the indicated summary (adver-
+           tiseMatching), or result in  the  subnet's  not
+           being advertised at all outside the area."
+       DEFVAL   { advertiseMatching }
+       ::= { ospfAreaRangeEntry 5 }
+
+
+
+--  OSPF Host Table
+
+--      The Host/Metric Table indicates what hosts are directly
+--      attached to the Router, and what metrics and types of
+--      service should be advertised for them.
+
+    ospfHostTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfHostEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The list of Hosts, and their metrics, that the
+           router will advertise as host routes."
+       REFERENCE
+          "OSPF Version 2, Appendix C.6  Host route param-
+          eters"
+      ::= { ospf 6 }
+
+
+    ospfHostEntry OBJECT-TYPE
+        SYNTAX   OspfHostEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A metric to be advertised, for a given type of
+           service, when a given host is reachable."
+       INDEX { ospfHostIpAddress, ospfHostTOS }
+       ::= { ospfHostTable 1 }
+
+OspfHostEntry ::=
+    SEQUENCE {
+        ospfHostIpAddress
+            IpAddress,
+        ospfHostTOS
+            TOSType,
+        ospfHostMetric
+            Metric,
+        ospfHostStatus
+            RowStatus,
+        ospfHostAreaID
+            AreaID
+              }
+
+    ospfHostIpAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address of the Host."
+       REFERENCE
+          "OSPF Version 2, Appendix C.6 Host route parame-
+          ters"
+      ::= { ospfHostEntry 1 }
+
+
+    ospfHostTOS OBJECT-TYPE
+        SYNTAX   TOSType
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Type of Service of the route being config-
+           ured."
+       REFERENCE
+          "OSPF Version 2, Appendix C.6 Host route parame-
+          ters"
+      ::= { ospfHostEntry 2 }
+
+
+    ospfHostMetric OBJECT-TYPE
+        SYNTAX   Metric
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The Metric to be advertised."
+       REFERENCE
+          "OSPF Version 2, Appendix C.6 Host route parame-
+          ters"
+      ::= { ospfHostEntry 3 }
+
+    ospfHostStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfHostEntry 4 }
+
+
+    ospfHostAreaID OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Area the Host Entry is to be found within.
+           By  default, the area that a subsuming OSPF in-
+           terface is in, or 0.0.0.0"
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfHostEntry 5 }
+
+
+--  OSPF Interface Table
+
+--      The OSPF Interface Table augments the ipAddrTable
+--             with OSPF specific information.
+
+    ospfIfTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfIfEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The OSPF Interface Table describes the  inter-
+           faces from the viewpoint of OSPF."
+       REFERENCE
+          "OSPF Version 2, Appendix C.3  Router  interface
+          parameters"
+      ::= { ospf 7 }
+
+
+    ospfIfEntry OBJECT-TYPE
+        SYNTAX   OspfIfEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The OSPF Interface Entry describes one  inter-
+           face from the viewpoint of OSPF."
+       INDEX { ospfIfIpAddress, ospfAddressLessIf }
+       ::= { ospfIfTable 1 }
+
+OspfIfEntry ::=
+    SEQUENCE {
+        ospfIfIpAddress
+            IpAddress,
+        ospfAddressLessIf
+            Integer32,
+        ospfIfAreaId
+            AreaID,
+        ospfIfType
+            INTEGER,
+        ospfIfAdminStat
+            Status,
+        ospfIfRtrPriority
+            DesignatedRouterPriority,
+        ospfIfTransitDelay
+            UpToMaxAge,
+        ospfIfRetransInterval
+            UpToMaxAge,
+        ospfIfHelloInterval
+            HelloRange,
+        ospfIfRtrDeadInterval
+            PositiveInteger,
+        ospfIfPollInterval
+            PositiveInteger,
+        ospfIfState
+            INTEGER,
+        ospfIfDesignatedRouter
+            IpAddress,
+        ospfIfBackupDesignatedRouter
+            IpAddress,
+        ospfIfEvents
+            Counter32,
+        ospfIfAuthType
+            INTEGER,
+        ospfIfAuthKey
+            OCTET STRING,
+        ospfIfStatus
+            RowStatus,
+        ospfIfMulticastForwarding
+            INTEGER,
+        ospfIfDemand
+            TruthValue
+              }
+
+    ospfIfIpAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP address of this OSPF interface."
+       ::= { ospfIfEntry 1 }
+
+    ospfAddressLessIf OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "For the purpose of easing  the  instancing  of
+           addressed   and  addressless  interfaces;  This
+           variable takes the value 0 on  interfaces  with
+           IP  Addresses,  and  the corresponding value of
+           ifIndex for interfaces having no IP Address."
+       ::= { ospfIfEntry 2 }
+    ospfIfAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "A 32-bit integer uniquely identifying the area
+           to  which  the  interface  connects.   Area  ID
+           0.0.0.0 is used for the OSPF backbone."
+       DEFVAL   { '00000000'H }    -- 0.0.0.0
+       ::= { ospfIfEntry 3 }
+
+    ospfIfType OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    broadcast (1),
+                    nbma (2),
+                    pointToPoint (3),
+                    pointToMultipoint (5)
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The OSPF interface type.
+
+           By way of a default, this field may be intuited
+           from the corresponding value of ifType.  Broad-
+           cast LANs, such as  Ethernet  and  IEEE  802.5,
+           take  the  value  'broadcast', X.25 and similar
+           technologies take the value 'nbma',  and  links
+           that  are  definitively point to point take the
+           value 'pointToPoint'."
+       ::= { ospfIfEntry 4 }
+
+
+    ospfIfAdminStat OBJECT-TYPE
+        SYNTAX   Status
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The OSPF  interface's  administrative  status.
+           The  value formed on the interface, and the in-
+           terface will be advertised as an internal route
+           to  some  area.   The  value 'disabled' denotes
+           that the interface is external to OSPF."
+       DEFVAL { enabled }
+       ::= { ospfIfEntry 5 }
+
+    ospfIfRtrPriority OBJECT-TYPE
+        SYNTAX   DesignatedRouterPriority
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The  priority  of  this  interface.   Used  in
+           multi-access  networks,  this  field is used in
+           the designated router election algorithm.   The
+           value 0 signifies that the router is not eligi-
+           ble to become the  designated  router  on  this
+           particular  network.   In the event of a tie in
+           this value, routers will use their Router ID as
+           a tie breaker."
+       DEFVAL { 1 }
+       ::= { ospfIfEntry 6 }
+
+
+    ospfIfTransitDelay OBJECT-TYPE
+        SYNTAX   UpToMaxAge
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The estimated number of seconds  it  takes  to
+           transmit  a  link state update packet over this
+           interface."
+       DEFVAL { 1 }
+       ::= { ospfIfEntry 7 }
+
+
+    ospfIfRetransInterval OBJECT-TYPE
+        SYNTAX   UpToMaxAge
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The number of seconds between  link-state  ad-
+           vertisement  retransmissions,  for  adjacencies
+           belonging to this  interface.   This  value  is
+           also used when retransmitting database descrip-
+           tion and link-state request packets."
+       DEFVAL { 5 }
+       ::= { ospfIfEntry 8 }
+
+
+    ospfIfHelloInterval OBJECT-TYPE
+        SYNTAX   HelloRange
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The length of time, in  seconds,  between  the
+           Hello  packets that the router sends on the in-
+           terface.  This value must be the same  for  all
+           routers attached to a common network."
+       DEFVAL { 10 }
+       ::= { ospfIfEntry 9 }
+
+
+    ospfIfRtrDeadInterval OBJECT-TYPE
+        SYNTAX   PositiveInteger
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The number of seconds that  a  router's  Hello
+           packets  have  not been seen before it's neigh-
+           bors declare the router down.  This  should  be
+           some  multiple  of  the  Hello  interval.  This
+           value must be the same for all routers attached
+           to a common network."
+       DEFVAL { 40 }
+       ::= { ospfIfEntry 10 }
+
+
+    ospfIfPollInterval OBJECT-TYPE
+        SYNTAX   PositiveInteger
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The larger time interval, in seconds,  between
+           the  Hello  packets  sent  to  an inactive non-
+           broadcast multi- access neighbor."
+       DEFVAL { 120 }
+       ::= { ospfIfEntry 11 }
+
+
+    ospfIfState OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    down (1),
+                    loopback (2),
+                    waiting (3),
+                    pointToPoint (4),
+                    designatedRouter (5),
+                    backupDesignatedRouter (6),
+                    otherDesignatedRouter (7)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The OSPF Interface State."
+       DEFVAL { down }
+       ::= { ospfIfEntry 12 }
+
+
+    ospfIfDesignatedRouter OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address of the Designated Router."
+       DEFVAL   { '00000000'H }    -- 0.0.0.0
+       ::= { ospfIfEntry 13 }
+
+
+    ospfIfBackupDesignatedRouter OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The  IP  Address  of  the  Backup   Designated
+           Router."
+       DEFVAL   { '00000000'H }    -- 0.0.0.0
+       ::= { ospfIfEntry 14 }
+
+    ospfIfEvents OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of times this  OSPF  interface  has
+           changed its state, or an error has occurred."
+       ::= { ospfIfEntry 15 }
+
+
+    ospfIfAuthKey OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE (0..256))
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The Authentication Key.  If the Area's Author-
+           ization  Type  is  simplePassword,  and the key
+           length is shorter than 8 octets, the agent will
+           left adjust and zero fill to 8 octets.
+
+           Note that unauthenticated  interfaces  need  no
+           authentication key, and simple password authen-
+           tication cannot use a key of more  than  8  oc-
+           tets.  Larger keys are useful only with authen-
+           tication mechanisms not specified in this docu-
+           ment.
+
+           When read, ospfIfAuthKey always returns an  Oc-
+           tet String of length zero."
+       REFERENCE
+          "OSPF Version 2, Section 9  The  Interface  Data
+          Structure"
+      DEFVAL   { '0000000000000000'H }    -- 0.0.0.0.0.0.0.0
+      ::= { ospfIfEntry 16 }
+
+    ospfIfStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfIfEntry 17 }
+
+
+    ospfIfMulticastForwarding OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                            blocked (1),        -- no multicast forwarding
+                            multicast (2),        -- using multicast address
+                            unicast (3)        -- to each OSPF neighbor
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The way multicasts should  forwarded  on  this
+           interface;  not  forwarded,  forwarded  as data
+           link multicasts, or forwarded as data link uni-
+           casts.   Data link multicasting is not meaning-
+           ful on point to point and NBMA interfaces,  and
+           setting ospfMulticastForwarding to 0 effective-
+           ly disables all multicast forwarding."
+       DEFVAL { blocked }
+       ::= { ospfIfEntry 18 }
+
+
+    ospfIfDemand OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "Indicates whether Demand OSPF procedures (hel-
+           lo suppression to FULL neighbors and setting the
+           DoNotAge flag on proogated LSAs) should be per-
+           formed on this interface."
+       DEFVAL { false }
+       ::= { ospfIfEntry 19 }
+
+
+    ospfIfAuthType OBJECT-TYPE
+        SYNTAX   INTEGER (0..255)
+                    -- none (0),
+                    -- simplePassword (1)
+                    -- md5 (2)
+                    -- reserved for specification by IANA (> 2)
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The authentication type specified for  an  in-
+           terface.   Additional  authentication types may
+           be assigned locally."
+       REFERENCE
+          "OSPF Version 2, Appendix E Authentication"
+      DEFVAL { 0 }        -- no authentication, by default
+      ::= { ospfIfEntry 20 }
+
+
+--  OSPF Interface Metric Table
+
+--      The Metric Table describes the metrics to be advertised
+--      for a specified interface at the various types of service.
+--      As such, this table is an adjunct of the OSPF Interface
+--      Table.
+
+-- Types of service, as defined by RFC 791, have the ability
+-- to request low delay, high bandwidth, or reliable linkage.
+
+-- For the purposes of this specification, the measure of
+-- bandwidth
+
+--      Metric = 10^8 / ifSpeed
+
+-- is the default value.  For multiple link interfaces, note
+-- that ifSpeed is the sum of the individual link speeds.
+-- This yields a number having the following typical values:
+
+--      Network Type/bit rate   Metric
+
+--      >= 100 MBPS                 1
+--      Ethernet/802.3             10
+--      E1                         48
+--      T1 (ESF)                   65
+--       64 KBPS                 1562
+--       56 KBPS                 1785
+--       19.2 KBPS               5208
+--        9.6 KBPS              10416
+
+-- Routes that are not specified use the default (TOS 0) metric
+
+    ospfIfMetricTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfIfMetricEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The TOS metrics for  a  non-virtual  interface
+           identified by the interface index."
+       REFERENCE
+          "OSPF Version 2, Appendix C.3  Router  interface
+          parameters"
+      ::= { ospf 8 }
+
+    ospfIfMetricEntry OBJECT-TYPE
+        SYNTAX   OspfIfMetricEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A particular TOS metric for a non-virtual  in-
+           terface identified by the interface index."
+       REFERENCE
+          "OSPF Version 2, Appendix C.3  Router  interface
+          parameters"
+      INDEX { ospfIfMetricIpAddress,
+  ospfIfMetricAddressLessIf,
+  ospfIfMetricTOS }
+      ::= { ospfIfMetricTable 1 }
+
+OspfIfMetricEntry ::=
+    SEQUENCE {
+        ospfIfMetricIpAddress
+            IpAddress,
+        ospfIfMetricAddressLessIf
+            Integer32,
+        ospfIfMetricTOS
+            TOSType,
+        ospfIfMetricValue
+            Metric,
+        ospfIfMetricStatus
+            RowStatus
+              }
+
+    ospfIfMetricIpAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP address of this OSPF interface.  On row
+           creation,  this  can  be  derived  from the in-
+           stance."
+       ::= { ospfIfMetricEntry 1 }
+
+    ospfIfMetricAddressLessIf OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "For the purpose of easing  the  instancing  of
+           addressed   and  addressless  interfaces;  This
+           variable takes the value 0 on  interfaces  with
+           IP  Addresses, and the value of ifIndex for in-
+           terfaces having no IP Address.   On  row  crea-
+           tion, this can be derived from the instance."
+       ::= { ospfIfMetricEntry 2 }
+
+
+    ospfIfMetricTOS OBJECT-TYPE
+        SYNTAX   TOSType
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The type of service metric  being  referenced.
+           On  row  creation, this can be derived from the
+           instance."
+       ::= { ospfIfMetricEntry 3 }
+
+
+    ospfIfMetricValue OBJECT-TYPE
+        SYNTAX   Metric
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The metric of using this type  of  service  on
+           this interface.  The default value of the TOS 0
+           Metric is 10^8 / ifSpeed."
+       ::= { ospfIfMetricEntry 4 }
+
+    ospfIfMetricStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfIfMetricEntry 5 }
+
+
+--  OSPF Virtual Interface Table
+
+--      The Virtual Interface Table describes the virtual
+--      links that the OSPF Process is configured to
+--      carry on.
+
+    ospfVirtIfTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfVirtIfEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "Information about this router's virtual inter-
+           faces."
+       REFERENCE
+          "OSPF Version  2,  Appendix  C.4   Virtual  link
+          parameters"
+      ::= { ospf 9 }
+
+
+    ospfVirtIfEntry OBJECT-TYPE
+        SYNTAX   OspfVirtIfEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "Information about a single Virtual Interface."
+       INDEX { ospfVirtIfAreaId, ospfVirtIfNeighbor }
+       ::= { ospfVirtIfTable 1 }
+
+OspfVirtIfEntry ::=
+    SEQUENCE {
+        ospfVirtIfAreaId
+            AreaID,
+        ospfVirtIfNeighbor
+            RouterID,
+        ospfVirtIfTransitDelay
+            UpToMaxAge,
+        ospfVirtIfRetransInterval
+            UpToMaxAge,
+        ospfVirtIfHelloInterval
+            HelloRange,
+        ospfVirtIfRtrDeadInterval
+            PositiveInteger,
+        ospfVirtIfState
+            INTEGER,
+        ospfVirtIfEvents
+            Counter32,
+        ospfVirtIfAuthType
+            INTEGER,
+        ospfVirtIfAuthKey
+            OCTET STRING,
+        ospfVirtIfStatus
+            RowStatus
+              }
+
+    ospfVirtIfAreaId OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The  Transit  Area  that  the   Virtual   Link
+           traverses.  By definition, this is not 0.0.0.0"
+       ::= { ospfVirtIfEntry 1 }
+
+
+    ospfVirtIfNeighbor OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Router ID of the Virtual Neighbor."
+       ::= { ospfVirtIfEntry 2 }
+
+
+    ospfVirtIfTransitDelay OBJECT-TYPE
+        SYNTAX   UpToMaxAge
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The estimated number of seconds  it  takes  to
+           transmit  a link- state update packet over this
+           interface."
+       DEFVAL { 1 }
+       ::= { ospfVirtIfEntry 3 }
+
+
+    ospfVirtIfRetransInterval OBJECT-TYPE
+        SYNTAX   UpToMaxAge
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The number of seconds between  link-state  ad-
+           vertisement  retransmissions,  for  adjacencies
+           belonging to this  interface.   This  value  is
+           also used when retransmitting database descrip-
+           tion  and  link-state  request  packets.   This
+           value  should  be well over the expected round-
+           trip time."
+       DEFVAL { 5 }
+       ::= { ospfVirtIfEntry 4 }
+
+
+    ospfVirtIfHelloInterval OBJECT-TYPE
+        SYNTAX   HelloRange
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The length of time, in  seconds,  between  the
+           Hello  packets that the router sends on the in-
+           terface.  This value must be the same  for  the
+           virtual neighbor."
+       DEFVAL { 10 }
+       ::= { ospfVirtIfEntry 5 }
+
+
+    ospfVirtIfRtrDeadInterval OBJECT-TYPE
+        SYNTAX   PositiveInteger
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The number of seconds that  a  router's  Hello
+           packets  have  not been seen before it's neigh-
+           bors declare the router down.  This  should  be
+           some  multiple  of  the  Hello  interval.  This
+           value must be the same for the  virtual  neigh-
+           bor."
+       DEFVAL { 60 }
+       ::= { ospfVirtIfEntry 6 }
+
+
+    ospfVirtIfState OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    down (1),            -- these use the same encoding
+                    pointToPoint (4)     -- as the ospfIfTable
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "OSPF virtual interface states."
+       DEFVAL   { down }
+       ::= { ospfVirtIfEntry 7 }
+
+
+    ospfVirtIfEvents OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of state changes or error events on
+           this Virtual Link"
+       ::= { ospfVirtIfEntry 8 }
+
+
+    ospfVirtIfAuthKey OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE(0..256))
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "If Authentication Type is simplePassword,  the
+           device  will left adjust and zero fill to 8 oc-
+           tets.
+
+           Note that unauthenticated  interfaces  need  no
+           authentication key, and simple password authen-
+           tication cannot use a key of more  than  8  oc-
+           tets.  Larger keys are useful only with authen-
+           tication mechanisms not specified in this docu-
+           ment.
+
+           When  read,  ospfVifAuthKey  always  returns  a
+           string of length zero."
+       REFERENCE
+          "OSPF Version 2, Section 9  The  Interface  Data
+          Structure"
+      DEFVAL   { '0000000000000000'H }    -- 0.0.0.0.0.0.0.0
+      ::= { ospfVirtIfEntry 9 }
+
+
+    ospfVirtIfStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfVirtIfEntry 10 }
+
+
+    ospfVirtIfAuthType OBJECT-TYPE
+        SYNTAX   INTEGER (0..255)
+                    -- none (0),
+                    -- simplePassword (1)
+                    -- md5 (2)
+                    -- reserved for specification by IANA (> 2)
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The authentication type specified for a virtu-
+           al  interface.  Additional authentication types
+           may be assigned locally."
+       REFERENCE
+          "OSPF Version 2, Appendix E Authentication"
+      DEFVAL { 0 }        -- no authentication, by default
+      ::= { ospfVirtIfEntry 11 }
+
+
+--  OSPF Neighbor Table
+
+--      The OSPF Neighbor Table describes all neighbors in
+--      the locality of the subject router.
+
+    ospfNbrTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfNbrEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A table of non-virtual neighbor information."
+       REFERENCE
+          "OSPF Version 2, Section 10  The  Neighbor  Data
+          Structure"
+      ::= { ospf 10 }
+
+
+    ospfNbrEntry OBJECT-TYPE
+        SYNTAX   OspfNbrEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The information regarding a single neighbor."
+       REFERENCE
+          "OSPF Version 2, Section 10  The  Neighbor  Data
+          Structure"
+      INDEX { ospfNbrIpAddr, ospfNbrAddressLessIndex }
+      ::= { ospfNbrTable 1 }
+
+OspfNbrEntry ::=
+    SEQUENCE {
+        ospfNbrIpAddr
+            IpAddress,
+        ospfNbrAddressLessIndex
+            InterfaceIndex,
+        ospfNbrRtrId
+            RouterID,
+        ospfNbrOptions
+            Integer32,
+        ospfNbrPriority
+            DesignatedRouterPriority,
+        ospfNbrState
+            INTEGER,
+        ospfNbrEvents
+            Counter32,
+        ospfNbrLsRetransQLen
+            Gauge32,
+        ospfNbmaNbrStatus
+            RowStatus,
+        ospfNbmaNbrPermanence
+            INTEGER,
+        ospfNbrHelloSuppressed
+            TruthValue
+              }
+
+    ospfNbrIpAddr OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP address this neighbor is using  in  its
+           IP  Source  Address.  Note that, on addressless
+           links, this will not be 0.0.0.0,  but  the  ad-
+           dress of another of the neighbor's interfaces."
+       ::= { ospfNbrEntry 1 }
+
+
+    ospfNbrAddressLessIndex OBJECT-TYPE
+        SYNTAX   InterfaceIndex
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "On an interface having an  IP  Address,  zero.
+           On  addressless  interfaces,  the corresponding
+           value of ifIndex in the Internet Standard  MIB.
+           On  row  creation, this can be derived from the
+           instance."
+       ::= { ospfNbrEntry 2 }
+
+
+    ospfNbrRtrId OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A 32-bit integer (represented as a type  IpAd-
+           dress)  uniquely  identifying  the  neighboring
+           router in the Autonomous System."
+       DEFVAL   { '00000000'H }    -- 0.0.0.0
+       ::= { ospfNbrEntry 3 }
+
+
+    ospfNbrOptions OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A Bit Mask corresponding to the neighbor's op-
+           tions field.
+
+           Bit 0, if set, indicates that the  system  will
+           operate  on  Type of Service metrics other than
+           TOS 0.  If zero, the neighbor will  ignore  all
+           metrics except the TOS 0 metric.
+
+           Bit 1, if set, indicates  that  the  associated
+           area  accepts and operates on external informa-
+           tion; if zero, it is a stub area.
+
+           Bit 2, if set, indicates that the system is ca-
+           pable  of routing IP Multicast datagrams; i.e.,
+           that it implements the Multicast Extensions  to
+           OSPF.
+
+           Bit 3, if set, indicates  that  the  associated
+           area  is  an  NSSA.  These areas are capable of
+           carrying type 7 external advertisements,  which
+           are  translated into type 5 external advertise-
+           ments at NSSA borders."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.2 Options"
+      DEFVAL { 0 }
+      ::= { ospfNbrEntry 4 }
+
+
+    ospfNbrPriority OBJECT-TYPE
+        SYNTAX   DesignatedRouterPriority
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The priority of this neighbor in the designat-
+           ed router election algorithm.  The value 0 sig-
+           nifies that the neighbor is not eligible to be-
+           come  the  designated router on this particular
+           network."
+       DEFVAL { 1 }
+       ::= { ospfNbrEntry 5 }
+
+
+    ospfNbrState OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    down (1),
+                    attempt (2),
+                    init (3),
+                    twoWay (4),
+                    exchangeStart (5),
+                    exchange (6),
+                    loading (7),
+                    full (8)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The State of the relationship with this Neigh-
+           bor."
+       REFERENCE
+          "OSPF Version 2, Section 10.1 Neighbor States"
+      DEFVAL   { down }
+      ::= { ospfNbrEntry 6 }
+
+
+    ospfNbrEvents OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of times this neighbor relationship
+           has changed state, or an error has occurred."
+       ::= { ospfNbrEntry 7 }
+
+
+    ospfNbrLsRetransQLen OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The  current  length  of  the   retransmission
+           queue."
+       ::= { ospfNbrEntry 8 }
+
+
+    ospfNbmaNbrStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfNbrEntry 9 }
+
+
+    ospfNbmaNbrPermanence OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    dynamic (1),        -- learned through protocol
+                    permanent (2)       -- configured address
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.   'dynamic'  and  'permanent' refer to how
+           the neighbor became known."
+       DEFVAL { permanent }
+       ::= { ospfNbrEntry 10 }
+
+
+    ospfNbrHelloSuppressed OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "Indicates whether Hellos are being  suppressed
+           to the neighbor"
+       ::= { ospfNbrEntry 11 }
+
+
+--  OSPF Virtual Neighbor Table
+
+--      This table describes all virtual neighbors.
+--      Since Virtual Links are configured in the
+--      virtual interface table, this table is read-only.
+
+    ospfVirtNbrTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfVirtNbrEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A table of virtual neighbor information."
+       REFERENCE
+          "OSPF Version 2, Section 15  Virtual Links"
+      ::= { ospf 11 }
+
+
+    ospfVirtNbrEntry OBJECT-TYPE
+        SYNTAX   OspfVirtNbrEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "Virtual neighbor information."
+       INDEX { ospfVirtNbrArea, ospfVirtNbrRtrId }
+       ::= { ospfVirtNbrTable 1 }
+
+OspfVirtNbrEntry ::=
+    SEQUENCE {
+        ospfVirtNbrArea
+            AreaID,
+        ospfVirtNbrRtrId
+            RouterID,
+        ospfVirtNbrIpAddr
+            IpAddress,
+        ospfVirtNbrOptions
+            Integer32,
+        ospfVirtNbrState
+            INTEGER,
+        ospfVirtNbrEvents
+            Counter32,
+        ospfVirtNbrLsRetransQLen
+            Gauge32,
+        ospfVirtNbrHelloSuppressed
+                TruthValue
+              }
+
+    ospfVirtNbrArea OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Transit Area Identifier."
+       ::= { ospfVirtNbrEntry 1 }
+
+
+    ospfVirtNbrRtrId OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A  32-bit  integer  uniquely  identifying  the
+           neighboring router in the Autonomous System."
+       ::= { ospfVirtNbrEntry 2 }
+
+
+    ospfVirtNbrIpAddr OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP address this Virtual  Neighbor  is  us-
+           ing."
+       ::= { ospfVirtNbrEntry 3 }
+
+
+    ospfVirtNbrOptions OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "A Bit Mask corresponding to the neighbor's op-
+           tions field.
+
+           Bit 1, if set, indicates that the  system  will
+           operate  on  Type of Service metrics other than
+           TOS 0.  If zero, the neighbor will  ignore  all
+           metrics except the TOS 0 metric.
+
+           Bit 2, if set, indicates  that  the  system  is
+           Network  Multicast  capable; ie, that it imple-
+           ments OSPF Multicast Routing."
+       ::= { ospfVirtNbrEntry 4 }
+    ospfVirtNbrState OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    down (1),
+                    attempt (2),
+                    init (3),
+                    twoWay (4),
+                    exchangeStart (5),
+                    exchange (6),
+                    loading (7),
+                    full (8)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The state of the  Virtual  Neighbor  Relation-
+           ship."
+       ::= { ospfVirtNbrEntry 5 }
+
+
+    ospfVirtNbrEvents OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of  times  this  virtual  link  has
+           changed its state, or an error has occurred."
+       ::= { ospfVirtNbrEntry 6 }
+
+
+    ospfVirtNbrLsRetransQLen OBJECT-TYPE
+        SYNTAX   Gauge32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The  current  length  of  the   retransmission
+           queue."
+       ::= { ospfVirtNbrEntry 7 }
+
+
+    ospfVirtNbrHelloSuppressed OBJECT-TYPE
+        SYNTAX   TruthValue
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "Indicates whether Hellos are being  suppressed
+           to the neighbor"
+       ::= { ospfVirtNbrEntry 8 }
+
+--  OSPF Link State Database, External
+
+--      The Link State Database contains the Link State
+--      Advertisements from throughout the areas that the
+--      device is attached to.
+
+--             This table is identical to the OSPF LSDB Table in
+--      format, but contains only External Link State
+--             Advertisements.  The purpose is to allow external
+--      LSAs to be displayed once for the router rather
+--      than once in each non-stub area.
+
+    ospfExtLsdbTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfExtLsdbEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "The OSPF Process's Links State Database."
+       REFERENCE
+          "OSPF Version 2, Section 12  Link  State  Adver-
+          tisements"
+      ::= { ospf 12 }
+
+
+    ospfExtLsdbEntry OBJECT-TYPE
+        SYNTAX   OspfExtLsdbEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A single Link State Advertisement."
+       INDEX { ospfExtLsdbType, ospfExtLsdbLsid, ospfExtLsdbRouterId }
+       ::= { ospfExtLsdbTable 1 }
+
+OspfExtLsdbEntry ::=
+    SEQUENCE {
+        ospfExtLsdbType
+            INTEGER,
+        ospfExtLsdbLsid
+            IpAddress,
+        ospfExtLsdbRouterId
+            RouterID,
+        ospfExtLsdbSequence
+            Integer32,
+        ospfExtLsdbAge
+            Integer32,
+        ospfExtLsdbChecksum
+            Integer32,
+        ospfExtLsdbAdvertisement
+            OCTET STRING
+              }
+
+    ospfExtLsdbType OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    asExternalLink (5)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The type  of  the  link  state  advertisement.
+           Each  link state type has a separate advertise-
+           ment format."
+       REFERENCE
+          "OSPF Version 2, Appendix A.4.1 The  Link  State
+          Advertisement header"
+      ::= { ospfExtLsdbEntry 1 }
+
+
+    ospfExtLsdbLsid OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Link State ID is an LS Type Specific field
+           containing either a Router ID or an IP Address;
+           it identifies the piece of the  routing  domain
+           that is being described by the advertisement."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.4 Link State ID"
+      ::= { ospfExtLsdbEntry 2 }
+
+
+    ospfExtLsdbRouterId OBJECT-TYPE
+        SYNTAX   RouterID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The 32 bit number that uniquely identifies the
+           originating router in the Autonomous System."
+       REFERENCE
+          "OSPF Version 2, Appendix C.1 Global parameters"
+      ::= { ospfExtLsdbEntry 3 }
+
+--  Note that the OSPF Sequence Number is a 32 bit signed
+--  integer.  It starts with the value '80000001'h,
+--  or -'7FFFFFFF'h, and increments until '7FFFFFFF'h
+--  Thus, a typical sequence number will be very negative.
+    ospfExtLsdbSequence OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The sequence number field is a  signed  32-bit
+           integer.   It  is used to detect old and dupli-
+           cate link state advertisements.  The  space  of
+           sequence  numbers  is  linearly  ordered.   The
+           larger the sequence number the more recent  the
+           advertisement."
+       REFERENCE
+          "OSPF Version  2,  Section  12.1.6  LS  sequence
+          number"
+      ::= { ospfExtLsdbEntry 4 }
+
+
+    ospfExtLsdbAge OBJECT-TYPE
+        SYNTAX   Integer32    -- Should be 0..MaxAge
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "This field is the age of the link state adver-
+           tisement in seconds."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.1 LS age"
+      ::= { ospfExtLsdbEntry 5 }
+
+
+    ospfExtLsdbChecksum OBJECT-TYPE
+        SYNTAX   Integer32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "This field is the  checksum  of  the  complete
+           contents  of  the  advertisement, excepting the
+           age field.  The age field is excepted  so  that
+           an   advertisement's  age  can  be  incremented
+           without updating the  checksum.   The  checksum
+           used  is  the same that is used for ISO connec-
+           tionless datagrams; it is commonly referred  to
+           as the Fletcher checksum."
+       REFERENCE
+          "OSPF Version 2, Section 12.1.7 LS checksum"
+      ::= { ospfExtLsdbEntry 6 }
+
+
+    ospfExtLsdbAdvertisement OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE(36))
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The entire Link State Advertisement, including
+           its header."
+       REFERENCE
+          "OSPF Version 2, Section 12  Link  State  Adver-
+          tisements"
+      ::= { ospfExtLsdbEntry 7 }
+
+
+--  OSPF Use of the CIDR Route Table
+
+ospfRouteGroup           OBJECT IDENTIFIER ::= { ospf 13 }
+
+-- The IP Forwarding Table defines a number of objects for use by
+-- the routing protocol to externalize its information.  Most of
+-- the variables (ipForwardDest, ipForwardMask, ipForwardPolicy,
+-- ipForwardNextHop, ipForwardIfIndex, ipForwardType,
+-- ipForwardProto, ipForwardAge, and ipForwardNextHopAS) are
+-- defined there.
+
+-- Those that leave some discretion are defined here.
+
+-- ipCidrRouteProto is, of course, ospf (13).
+
+-- ipCidrRouteAge is the time since the route was first calculated,
+-- as opposed to the time since the last SPF run.
+
+-- ipCidrRouteInfo is an OBJECT IDENTIFIER for use by the routing
+-- protocol.  The following values shall be found there depending
+-- on the way the route was calculated.
+
+ospfIntraArea      OBJECT IDENTIFIER ::= { ospfRouteGroup 1 }
+ospfInterArea      OBJECT IDENTIFIER ::= { ospfRouteGroup 2 }
+ospfExternalType1  OBJECT IDENTIFIER ::= { ospfRouteGroup 3 }
+ospfExternalType2  OBJECT IDENTIFIER ::= { ospfRouteGroup 4 }
+
+-- ipCidrRouteMetric1 is, by definition, the primary routing
+-- metric.  Therefore, it should be the metric that route
+-- selection is based on.  For intra-area and inter-area routes,
+-- it is an OSPF metric.  For External Type 1 (comparable value)
+-- routes, it is an OSPF metric plus the External Metric.  For
+-- external Type 2 (non-comparable value) routes, it is the
+-- external metric.
+
+-- ipCidrRouteMetric2 is, by definition, a secondary routing
+-- metric.  Therefore, it should be the metric that breaks a tie
+-- among routes having equal metric1 values and the same
+-- calculation rule.  For intra-area, inter-area routes, and
+-- External Type 1 (comparable value) routes, it is unused.  For
+-- external Type 2 (non-comparable value) routes, it is the metric
+-- to the AS border router.
+
+-- ipCidrRouteMetric3, ipCidrRouteMetric4, and ipCidrRouteMetric5 are
+-- unused.
+
+--
+--      The OSPF Area Aggregate Table
+--
+--      This table replaces the OSPF Area Summary Table, being an
+--      extension of that for CIDR routers.
+
+    ospfAreaAggregateTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF OspfAreaAggregateEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A range of IP addresses  specified  by  an  IP
+           address/IP  network  mask  pair.   For example,
+           class B address range of X.X.X.X with a network
+           mask  of  255.255.0.0 includes all IP addresses
+           from X.X.0.0  to  X.X.255.255.   Note  that  if
+           ranges  are configured such that one range sub-
+           sumes  another  range  (e.g.,   10.0.0.0   mask
+           255.0.0.0  and  10.1.0.0 mask 255.255.0.0), the
+           most specific match is the preferred one."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2  Area parameters"
+      ::= { ospf 14 }
+
+
+    ospfAreaAggregateEntry OBJECT-TYPE
+        SYNTAX   OspfAreaAggregateEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A range of IP addresses  specified  by  an  IP
+           address/IP  network  mask  pair.   For example,
+           class B address range of X.X.X.X with a network
+           mask  of  255.255.0.0 includes all IP addresses
+           from X.X.0.0  to  X.X.255.255.   Note  that  if
+           ranges are range configured such that one range
+           subsumes another  range  (e.g.,  10.0.0.0  mask
+           255.0.0.0  and  10.1.0.0 mask 255.255.0.0), the
+           most specific match is the preferred one."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2  Area parameters"
+      INDEX { ospfAreaAggregateAreaID, ospfAreaAggregateLsdbType,
+              ospfAreaAggregateNet, ospfAreaAggregateMask }
+      ::= { ospfAreaAggregateTable 1 }
+
+
+OspfAreaAggregateEntry ::=
+    SEQUENCE {
+        ospfAreaAggregateAreaID
+            AreaID,
+        ospfAreaAggregateLsdbType
+            INTEGER,
+        ospfAreaAggregateNet
+            IpAddress,
+        ospfAreaAggregateMask
+            IpAddress,
+        ospfAreaAggregateStatus
+            RowStatus,
+        ospfAreaAggregateEffect
+            INTEGER
+              }
+
+    ospfAreaAggregateAreaID OBJECT-TYPE
+        SYNTAX   AreaID
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Area the Address Aggregate is to be  found
+           within."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaAggregateEntry 1 }
+
+
+    ospfAreaAggregateLsdbType OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    summaryLink (3),
+                    nssaExternalLink (7)
+                  }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The type of the Address Aggregate.  This field
+           specifies  the  Lsdb type that this Address Ag-
+           gregate applies to."
+       REFERENCE
+          "OSPF Version 2, Appendix A.4.1 The  Link  State
+          Advertisement header"
+      ::= { ospfAreaAggregateEntry 2 }
+
+
+    ospfAreaAggregateNet OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address of the Net or Subnet  indicated
+           by the range."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaAggregateEntry 3 }
+
+
+    ospfAreaAggregateMask OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The Subnet Mask that pertains to  the  Net  or
+           Subnet."
+       REFERENCE
+          "OSPF Version 2, Appendix C.2 Area parameters"
+      ::= { ospfAreaAggregateEntry 4 }
+
+
+    ospfAreaAggregateStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable displays the status of  the  en-
+           try.  Setting it to 'invalid' has the effect of
+           rendering it inoperative.  The internal  effect
+           (row removal) is implementation dependent."
+       ::= { ospfAreaAggregateEntry 5 }
+
+
+    ospfAreaAggregateEffect OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    advertiseMatching (1),
+                    doNotAdvertiseMatching (2)
+                  }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "Subnets subsumed by ranges either trigger  the
+           advertisement  of  the indicated aggregate (ad-
+           vertiseMatching), or result in the subnet's not
+           being advertised at all outside the area."
+       DEFVAL   { advertiseMatching }
+       ::= { ospfAreaAggregateEntry 6 }
+
+
+-- conformance information
+
+ospfConformance OBJECT IDENTIFIER ::= { ospf 15 }
+
+ospfGroups      OBJECT IDENTIFIER ::= { ospfConformance 1 }
+ospfCompliances OBJECT IDENTIFIER ::= { ospfConformance 2 }
+
+-- compliance statements
+
+    ospfCompliance MODULE-COMPLIANCE
+        STATUS  current
+        DESCRIPTION
+           "The compliance statement "
+       MODULE  -- this module
+       MANDATORY-GROUPS {
+                    ospfBasicGroup,
+                    ospfAreaGroup,
+                    ospfStubAreaGroup,
+                    ospfIfGroup,
+                    ospfIfMetricGroup,
+                    ospfVirtIfGroup,
+                    ospfNbrGroup,
+                    ospfVirtNbrGroup,
+                    ospfAreaAggregateGroup
+           }
+       ::= { ospfCompliances 1 }
+
+
+-- units of conformance
+
+    ospfBasicGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfRouterId,
+                    ospfAdminStat,
+                    ospfVersionNumber,
+                    ospfAreaBdrRtrStatus,
+                    ospfASBdrRtrStatus,
+                    ospfExternLsaCount,
+                    ospfExternLsaCksumSum,
+                    ospfTOSSupport,
+                    ospfOriginateNewLsas,
+                    ospfRxNewLsas,
+                    ospfExtLsdbLimit,
+                    ospfMulticastExtensions,
+                    ospfExitOverflowInterval,
+                    ospfDemandExtensions
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 1 }
+
+
+    ospfAreaGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfAreaId,
+                    ospfImportAsExtern,
+                    ospfSpfRuns,
+                    ospfAreaBdrRtrCount,
+                    ospfAsBdrRtrCount,
+                    ospfAreaLsaCount,
+                    ospfAreaLsaCksumSum,
+                    ospfAreaSummary,
+                    ospfAreaStatus
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  for  OSPF  systems
+           supporting areas."
+       ::= { ospfGroups 2 }
+
+
+    ospfStubAreaGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfStubAreaId,
+                    ospfStubTOS,
+                    ospfStubMetric,
+                    ospfStubStatus,
+                    ospfStubMetricType
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  for  OSPF  systems
+           supporting stub areas."
+       ::= { ospfGroups 3 }
+
+
+    ospfLsdbGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfLsdbAreaId,
+                    ospfLsdbType,
+                    ospfLsdbLsid,
+                    ospfLsdbRouterId,
+                    ospfLsdbSequence,
+                    ospfLsdbAge,
+                    ospfLsdbChecksum,
+                    ospfLsdbAdvertisement
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  for  OSPF  systems
+           that display their link state database."
+       ::= { ospfGroups 4 }
+
+
+    ospfAreaRangeGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfAreaRangeAreaId,
+                    ospfAreaRangeNet,
+                    ospfAreaRangeMask,
+                    ospfAreaRangeStatus,
+                    ospfAreaRangeEffect
+        }
+        STATUS  obsolete
+        DESCRIPTION
+           "These objects are required for  non-CIDR  OSPF
+           systems that support multiple areas."
+       ::= { ospfGroups 5 }
+
+
+    ospfHostGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfHostIpAddress,
+                    ospfHostTOS,
+                    ospfHostMetric,
+                    ospfHostStatus,
+                    ospfHostAreaID
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  for  OSPF  systems
+           that support attached hosts."
+       ::= { ospfGroups 6 }
+
+
+    ospfIfGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfIfAreaId,
+                    ospfIfType,
+                    ospfIfAdminStat,
+                    ospfIfRtrPriority,
+                    ospfIfTransitDelay,
+                    ospfIfRetransInterval,
+                    ospfIfHelloInterval,
+                    ospfIfRtrDeadInterval,
+                    ospfIfPollInterval,
+                    ospfIfState,
+                    ospfIfDesignatedRouter,
+                    ospfIfBackupDesignatedRouter,
+                    ospfIfEvents,
+                    ospfIfAuthType,
+                    ospfIfAuthKey,
+                    ospfIfStatus,
+                    ospfIfMulticastForwarding,
+                    ospfIfDemand
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 7 }
+
+
+    ospfIfMetricGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfIfMetricIpAddress,
+                    ospfIfMetricAddressLessIf,
+                    ospfIfMetricTOS,
+                    ospfIfMetricValue,
+                    ospfIfMetricStatus
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 8 }
+
+
+    ospfVirtIfGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfVirtIfTransitDelay,
+                    ospfVirtIfRetransInterval,
+                    ospfVirtIfHelloInterval,
+                    ospfVirtIfRtrDeadInterval,
+                    ospfVirtIfState,
+                    ospfVirtIfEvents,
+                    ospfVirtIfAuthType,
+                    ospfVirtIfAuthKey,
+                    ospfVirtIfStatus
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 9 }
+
+
+    ospfNbrGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfNbrIpAddr,
+                    ospfNbrAddressLessIndex,
+                    ospfNbrRtrId,
+                    ospfNbrOptions,
+                    ospfNbrPriority,
+                    ospfNbrState,
+                    ospfNbrEvents,
+                    ospfNbrLsRetransQLen,
+                    ospfNbmaNbrStatus,
+                    ospfNbmaNbrPermanence,
+                    ospfNbrHelloSuppressed
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 10 }
+
+
+    ospfVirtNbrGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfVirtNbrArea,
+                    ospfVirtNbrRtrId,
+                    ospfVirtNbrIpAddr,
+                    ospfVirtNbrOptions,
+                    ospfVirtNbrState,
+                    ospfVirtNbrEvents,
+                    ospfVirtNbrLsRetransQLen,
+                    ospfVirtNbrHelloSuppressed
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 11 }
+
+
+    ospfExtLsdbGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfExtLsdbType,
+                    ospfExtLsdbLsid,
+                    ospfExtLsdbRouterId,
+                    ospfExtLsdbSequence,
+                    ospfExtLsdbAge,
+                    ospfExtLsdbChecksum,
+                    ospfExtLsdbAdvertisement
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  for  OSPF  systems
+           that display their link state database."
+       ::= { ospfGroups 12 }
+
+
+    ospfAreaAggregateGroup    OBJECT-GROUP
+        OBJECTS {
+                    ospfAreaAggregateAreaID,
+                    ospfAreaAggregateLsdbType,
+                    ospfAreaAggregateNet,
+                    ospfAreaAggregateMask,
+                    ospfAreaAggregateStatus,
+                    ospfAreaAggregateEffect
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required for OSPF systems."
+       ::= { ospfGroups 13 }
+
+END
diff --git a/ospfd/OSPF-TRAP-MIB.txt b/ospfd/OSPF-TRAP-MIB.txt
new file mode 100644 (file)
index 0000000..8a3ab99
--- /dev/null
@@ -0,0 +1,443 @@
+OSPF-TRAP-MIB DEFINITIONS ::= BEGIN
+
+    IMPORTS
+            MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, IpAddress
+                FROM SNMPv2-SMI
+            MODULE-COMPLIANCE, OBJECT-GROUP
+                FROM SNMPv2-CONF
+            ospfRouterId, ospfIfIpAddress, ospfAddressLessIf, ospfIfState,
+            ospfVirtIfAreaId, ospfVirtIfNeighbor, ospfVirtIfState,
+            ospfNbrIpAddr, ospfNbrAddressLessIndex, ospfNbrRtrId,
+            ospfNbrState, ospfVirtNbrArea, ospfVirtNbrRtrId, ospfVirtNbrState,
+            ospfLsdbType, ospfLsdbLsid, ospfLsdbRouterId, ospfLsdbAreaId,
+            ospfExtLsdbLimit, ospf
+                FROM OSPF-MIB;
+
+    ospfTrap MODULE-IDENTITY
+           LAST-UPDATED "9501201225Z" -- Fri Jan 20 12:25:50 PST 1995
+           ORGANIZATION "IETF OSPF Working Group"
+           CONTACT-INFO
+           "                      Fred Baker
+           Postal:                Cisco Systems
+                                  519 Lado Drive
+                                  Santa Barbara, California 93111
+           Tel:                   +1 805 681 0115
+           E-Mail:                fred@cisco.com
+
+                                  Rob Coltun
+           Postal:                RainbowBridge Communications
+           Tel:                   (301) 340-9416
+           E-Mail:                rcoltun@rainbow-bridge.com"
+       DESCRIPTION
+          "The MIB module to describe traps for  the  OSPF
+          Version 2 Protocol."
+      ::= { ospf 16 }
+
+-- Trap Support Objects
+
+--         The following are support objects for the OSPF traps.
+
+ospfTrapControl OBJECT IDENTIFIER ::= { ospfTrap 1 }
+ospfTraps OBJECT IDENTIFIER ::= { ospfTrap 2 }
+
+    ospfSetTrap OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE(4))
+        MAX-ACCESS   read-write
+        STATUS   current
+        DESCRIPTION
+           "A four-octet string serving as a bit  map  for
+           the trap events defined by the OSPF traps. This
+           object is used to enable and  disable  specific
+           OSPF   traps   where  a  1  in  the  bit  field
+           represents enabled.  The right-most bit  (least
+           significant) represents trap 0."
+       ::= { ospfTrapControl 1 }
+
+
+    ospfConfigErrorType OBJECT-TYPE
+        SYNTAX   INTEGER   {
+                    badVersion (1),
+                    areaMismatch (2),
+                    unknownNbmaNbr (3), -- Router is Dr eligible
+                    unknownVirtualNbr (4),
+                    authTypeMismatch(5),
+                    authFailure (6),
+                    netMaskMismatch (7),
+                    helloIntervalMismatch (8),
+                    deadIntervalMismatch (9),
+                    optionMismatch (10) }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "Potential types  of  configuration  conflicts.
+           Used  by the ospfConfigError and ospfConfigVir-
+           tError traps."
+   ::= { ospfTrapControl 2 }
+
+
+    ospfPacketType OBJECT-TYPE
+        SYNTAX   INTEGER   {
+                    hello (1),
+                    dbDescript (2),
+                    lsReq (3),
+                    lsUpdate (4),
+                    lsAck (5) }
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "OSPF packet types."
+   ::= { ospfTrapControl 3 }
+
+
+    ospfPacketSrc OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP address of an inbound packet that  can-
+           not be identified by a neighbor instance."
+       ::= { ospfTrapControl 4 }
+
+
+-- Traps
+
+
+    ospfIfStateChange NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfIfState   -- The new state
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfStateChange trap signifies that there
+           has been a change in the state of a non-virtual
+           OSPF interface. This trap should  be  generated
+           when  the interface state regresses (e.g., goes
+           from Dr to Down) or progresses  to  a  terminal
+           state  (i.e.,  Point-to-Point, DR Other, Dr, or
+           Backup)."
+   ::= { ospfTraps 16 }
+
+
+    ospfVirtIfStateChange NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfVirtIfState  -- The new state
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfStateChange trap signifies that there
+           has  been a change in the state of an OSPF vir-
+           tual interface.
+           This trap should be generated when  the  inter-
+           face  state  regresses  (e.g., goes from Point-
+           to-Point to Down) or progresses to  a  terminal
+           state (i.e., Point-to-Point)."
+   ::= { ospfTraps 1 }
+
+
+    ospfNbrStateChange NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfNbrIpAddr,
+                    ospfNbrAddressLessIndex,
+                    ospfNbrRtrId,
+                    ospfNbrState  -- The new state
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An  ospfNbrStateChange  trap  signifies   that
+           there  has been a change in the state of a non-
+           virtual OSPF neighbor.   This  trap  should  be
+           generated  when  the  neighbor  state regresses
+           (e.g., goes from Attempt or Full  to  1-Way  or
+           Down)  or progresses to a terminal state (e.g.,
+           2-Way or Full).  When an  neighbor  transitions
+           from  or  to Full on non-broadcast multi-access
+           and broadcast networks, the trap should be gen-
+           erated  by the designated router.  A designated
+           router transitioning to Down will be  noted  by
+           ospfIfStateChange."
+   ::= { ospfTraps 2 }
+
+
+    ospfVirtNbrStateChange NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtNbrArea,
+                    ospfVirtNbrRtrId,
+                    ospfVirtNbrState  -- The new state
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfStateChange trap signifies that there
+           has  been a change in the state of an OSPF vir-
+           tual neighbor.  This trap should  be  generated
+           when  the  neighbor state regresses (e.g., goes
+           from Attempt or  Full  to  1-Way  or  Down)  or
+           progresses to a terminal state (e.g., Full)."
+   ::= { ospfTraps 3 }
+    ospfIfConfigError NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfPacketSrc,  -- The source IP address
+                    ospfConfigErrorType, -- Type of error
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfConfigError  trap  signifies  that  a
+           packet  has  been received on a non-virtual in-
+           terface  from  a  router  whose   configuration
+           parameters  conflict  with this router's confi-
+           guration parameters.  Note that the  event  op-
+           tionMismatch  should  cause  a  trap only if it
+           prevents an adjacency from forming."
+                  ::= { ospfTraps 4 }
+
+
+    ospfVirtIfConfigError NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfConfigErrorType, -- Type of error
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfConfigError trap signifies that a pack-
+           et  has  been  received  on a virtual interface
+           from a router  whose  configuration  parameters
+           conflict   with   this  router's  configuration
+           parameters.  Note that the event optionMismatch
+           should  cause a trap only if it prevents an ad-
+           jacency from forming."
+   ::= { ospfTraps 5 }
+
+
+    ospfIfAuthFailure NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfPacketSrc,  -- The source IP address
+                    ospfConfigErrorType, -- authTypeMismatch or
+                                         -- authFailure
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfAuthFailure  trap  signifies  that  a
+           packet  has  been received on a non-virtual in-
+           terface from a router whose authentication  key
+           or  authentication  type  conflicts  with  this
+           router's authentication key  or  authentication
+           type."
+   ::= { ospfTraps 6 }
+
+
+    ospfVirtIfAuthFailure NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfConfigErrorType, -- authTypeMismatch or
+                                         -- authFailure
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfVirtIfAuthFailure trap signifies that a
+           packet has been received on a virtual interface
+           from a router whose authentication key  or  au-
+           thentication  type conflicts with this router's
+           authentication key or authentication type."
+   ::= { ospfTraps 7 }
+
+
+    ospfIfRxBadPacket NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfPacketSrc,  -- The source IP address
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfIfRxBadPacket trap  signifies  that  an
+           OSPF  packet has been received on a non-virtual
+           interface that cannot be parsed."
+   ::= { ospfTraps 8 }
+
+    ospfVirtIfRxBadPacket NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfPacketType
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfRxBadPacket trap signifies that an OSPF
+           packet has been received on a virtual interface
+           that cannot be parsed."
+   ::= { ospfTraps 9 }
+
+
+    ospfTxRetransmit NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfIfIpAddress,
+                    ospfAddressLessIf,
+                    ospfNbrRtrId, -- Destination
+                    ospfPacketType,
+                    ospfLsdbType,
+                    ospfLsdbLsid,
+                    ospfLsdbRouterId
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfTxRetransmit  trap  signifies  than  an
+           OSPF  packet  has  been retransmitted on a non-
+           virtual interface.  All packets that may be re-
+           transmitted  are associated with an LSDB entry.
+           The LS type, LS ID, and Router ID are  used  to
+           identify the LSDB entry."
+   ::= { ospfTraps 10 }
+
+
+    ospfVirtIfTxRetransmit NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfVirtIfAreaId,
+                    ospfVirtIfNeighbor,
+                    ospfPacketType,
+                    ospfLsdbType,
+                    ospfLsdbLsid,
+                    ospfLsdbRouterId
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfTxRetransmit  trap  signifies  than  an
+           OSPF packet has been retransmitted on a virtual
+           interface.  All packets that may be retransmit-
+           ted  are  associated with an LSDB entry. The LS
+           type, LS ID, and Router ID are used to identify
+           the LSDB entry."
+   ::= { ospfTraps 11 }
+
+
+    ospfOriginateLsa NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfLsdbAreaId,  -- 0.0.0.0 for AS Externals
+                    ospfLsdbType,
+                    ospfLsdbLsid,
+                    ospfLsdbRouterId
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfOriginateLsa trap signifies that a  new
+           LSA  has  been originated by this router.  This
+           trap should not be invoked for simple refreshes
+           of  LSAs  (which happesn every 30 minutes), but
+           instead will only be invoked  when  an  LSA  is
+           (re)originated due to a topology change.  Addi-
+           tionally, this trap does not include LSAs  that
+           are  being  flushed  because  they have reached
+           MaxAge."
+   ::= { ospfTraps 12 }
+
+
+    ospfMaxAgeLsa NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfLsdbAreaId,  -- 0.0.0.0 for AS Externals
+                    ospfLsdbType,
+                    ospfLsdbLsid,
+                    ospfLsdbRouterId
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfMaxAgeLsa trap signifies  that  one  of
+           the LSA in the router's link-state database has
+           aged to MaxAge."
+   ::= { ospfTraps 13 }
+
+
+    ospfLsdbOverflow NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfExtLsdbLimit
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfLsdbOverflow trap  signifies  that  the
+           number of LSAs in the router's link-state data-
+           base has exceeded ospfExtLsdbLimit."
+   ::= { ospfTraps 14 }
+
+
+    ospfLsdbApproachingOverflow NOTIFICATION-TYPE
+        OBJECTS {
+                    ospfRouterId, -- The originator of the trap
+                    ospfExtLsdbLimit
+                  }
+        STATUS             current
+        DESCRIPTION
+           "An ospfLsdbApproachingOverflow trap  signifies
+           that  the  number of LSAs in the router's link-
+           state database has exceeded ninety  percent  of
+           ospfExtLsdbLimit."
+   ::= { ospfTraps 15 }
+
+
+-- conformance information
+
+ospfTrapConformance OBJECT IDENTIFIER ::= { ospfTrap 3 }
+
+ospfTrapGroups      OBJECT IDENTIFIER ::= { ospfTrapConformance 1 }
+ospfTrapCompliances OBJECT IDENTIFIER ::= { ospfTrapConformance 2 }
+
+-- compliance statements
+
+    ospfTrapCompliance MODULE-COMPLIANCE
+        STATUS  current
+        DESCRIPTION
+           "The compliance statement "
+       MODULE  -- this module
+       MANDATORY-GROUPS { ospfTrapControlGroup }
+
+
+        GROUP       ospfTrapControlGroup
+        DESCRIPTION
+           "This group is optional but recommended for all
+           OSPF systems"
+       ::= { ospfTrapCompliances 1 }
+
+
+-- units of conformance
+
+    ospfTrapControlGroup    OBJECT-GROUP
+        OBJECTS {
+                           ospfSetTrap,
+                           ospfConfigErrorType,
+                           ospfPacketType,
+                           ospfPacketSrc
+        }
+        STATUS  current
+        DESCRIPTION
+           "These objects are required  to  control  traps
+           from OSPF systems."
+       ::= { ospfTrapGroups 1 }
+
+
+END
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
new file mode 100644 (file)
index 0000000..95c2d46
--- /dev/null
@@ -0,0 +1,1887 @@
+/*
+ * OSPF ABR functions.
+ * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "vty.h"
+#include "filter.h"
+#include "plist.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ia.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+
+static struct ospf_area_range *
+ospf_area_range_new (struct prefix_ipv4 *p)
+{
+  struct ospf_area_range *range;
+
+  range = XCALLOC (MTYPE_OSPF_AREA_RANGE, sizeof (struct ospf_area_range));
+  range->addr = p->prefix;
+  range->masklen = p->prefixlen;
+  range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC;
+
+  return range;
+}
+
+static void
+ospf_area_range_free (struct ospf_area_range *range)
+{
+  XFREE (MTYPE_OSPF_AREA_RANGE, range);
+}
+
+static void
+ospf_area_range_add (struct ospf_area *area, struct ospf_area_range *range)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefixlen = range->masklen;
+  p.prefix = range->addr;
+
+  rn = route_node_get (area->ranges, (struct prefix *)&p);
+  if (rn->info)
+    route_unlock_node (rn);
+  else
+    rn->info = range;
+}
+
+static void
+ospf_area_range_delete (struct ospf_area *area, struct route_node *rn)
+{
+  struct ospf_area_range *range = rn->info;
+
+  if (range->specifics != 0)
+    ospf_delete_discard_route (area->ospf->new_table,
+                              (struct prefix_ipv4 *) &rn->p);
+
+  ospf_area_range_free (range);
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+}
+
+struct ospf_area_range *
+ospf_area_range_lookup (struct ospf_area *area, struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+
+  rn = route_node_lookup (area->ranges, (struct prefix *)p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      return rn->info;
+    }
+  return NULL;
+}
+
+struct ospf_area_range *
+ospf_area_range_lookup_next (struct ospf_area *area, 
+                             struct in_addr *range_net,
+                             int first)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+  struct ospf_area_range *find;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.prefix = *range_net;
+
+  if (first)
+    rn = route_top (area->ranges);
+  else
+    {
+      rn = route_node_get (area->ranges, (struct prefix *) &p);
+      rn = route_next (rn);
+    }
+
+  for (; rn; rn = route_next (rn))
+    if (rn->info)
+      break;
+
+  if (rn && rn->info)
+    {
+      find = rn->info;
+      *range_net = rn->p.u.prefix4;
+      route_unlock_node (rn);
+      return find;
+    }
+  return NULL;
+}
+
+static struct ospf_area_range *
+ospf_area_range_match (struct ospf_area *area, struct prefix_ipv4 *p)
+{
+  struct route_node *node;
+
+  node = route_node_match (area->ranges, (struct prefix *) p);
+  if (node)
+    {
+      route_unlock_node (node);
+      return node->info;
+    }
+  return NULL;
+}
+
+struct ospf_area_range *
+ospf_area_range_match_any (struct ospf *ospf, struct prefix_ipv4 *p)
+{
+  struct ospf_area_range *range;
+  struct ospf_area *area;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    if ((range = ospf_area_range_match (area, p)))
+      return range;
+
+  return NULL;
+}
+
+int
+ospf_area_range_active (struct ospf_area_range *range)
+{
+  return range->specifics;
+}
+
+static int
+ospf_area_actively_attached (struct ospf_area *area)
+{
+  return area->act_ints;
+}
+
+int
+ospf_area_range_set (struct ospf *ospf, struct in_addr area_id,
+                    struct prefix_ipv4 *p, int advertise)
+{
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+  int ret = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, ret);
+  if (area == NULL)
+    return 0;
+
+  range = ospf_area_range_lookup (area, p);
+  if (range != NULL)
+    {
+      if ((CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)
+          && !CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE))
+         || (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)
+             && CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE)))
+       ospf_schedule_abr_task (ospf);
+    }
+  else
+    {
+      range = ospf_area_range_new (p);
+      ospf_area_range_add (area, range);
+      ospf_schedule_abr_task (ospf);
+    }
+
+  if (CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE))
+    SET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE);
+  else
+    UNSET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE);
+
+  return 1;
+}
+
+int
+ospf_area_range_cost_set (struct ospf *ospf, struct in_addr area_id,
+                         struct prefix_ipv4 *p, u_int32_t cost)
+{
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+  int ret = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, ret);
+  if (area == NULL)
+    return 0;
+
+  range = ospf_area_range_lookup (area, p);
+  if (range == NULL)
+    return 0;
+
+  if (range->cost_config != cost)
+    {
+      range->cost_config = cost;
+      if (ospf_area_range_active (range))
+       ospf_schedule_abr_task (ospf);
+    }
+
+  return 1;
+}
+
+int
+ospf_area_range_unset (struct ospf *ospf, struct in_addr area_id,
+                      struct prefix_ipv4 *p)
+{
+  struct ospf_area *area;
+  struct route_node *rn;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  rn = route_node_lookup (area->ranges, (struct prefix*)p);
+  if (rn == NULL)
+    return 0;
+
+  if (ospf_area_range_active (rn->info))
+    ospf_schedule_abr_task (ospf);
+
+  ospf_area_range_delete (area, rn);
+
+  return 1;
+}
+
+int
+ospf_area_range_substitute_set (struct ospf *ospf, struct in_addr area_id,
+                               struct prefix_ipv4 *p, struct prefix_ipv4 *s)
+{
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+  int ret = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, ret);
+  range = ospf_area_range_lookup (area, p);
+
+  if (range != NULL)
+    {
+      if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE) ||
+         !CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE))
+       ospf_schedule_abr_task (ospf);
+    }
+  else
+    {
+      range = ospf_area_range_new (p);
+      ospf_area_range_add (area, range);
+      ospf_schedule_abr_task (ospf);
+    }
+
+  SET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE);
+  SET_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE);
+  range->subst_addr = s->prefix;
+  range->subst_masklen = s->prefixlen;
+
+  return 1;
+}
+
+int
+ospf_area_range_substitute_unset (struct ospf *ospf, struct in_addr area_id,
+                                 struct prefix_ipv4 *p)
+{
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  range = ospf_area_range_lookup (area, p);
+  if (range == NULL)
+    return 0;
+
+  if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE))
+    if (ospf_area_range_active (range))
+      ospf_schedule_abr_task (ospf);
+
+  UNSET_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE);
+  range->subst_addr.s_addr = 0;
+  range->subst_masklen = 0;
+
+  return 1;
+}
+
+int
+ospf_act_bb_connection (struct ospf *ospf)
+{
+  if (ospf->backbone == NULL)
+    return 0;
+
+  return ospf->backbone->full_nbrs;
+}
+
+/* Determine whether this router is elected translator or not for area */
+static int
+ospf_abr_nssa_am_elected (struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+  struct router_lsa *rlsa;
+  struct in_addr *best = NULL;
+  
+  LSDB_LOOP ( ROUTER_LSDB (area), rn, lsa)
+    {
+      /* sanity checks */
+      if (!lsa 
+          || (lsa->data->type != OSPF_ROUTER_LSA) 
+          || IS_LSA_SELF (lsa))
+        continue;
+      
+      rlsa = (struct router_lsa *) lsa->data;
+      
+      /* ignore non-ABR routers */
+      if (!IS_ROUTER_LSA_BORDER (rlsa))
+        continue;
+      
+      /* Router has Nt flag - always translate */
+      if (IS_ROUTER_LSA_NT (rlsa))
+        {
+          if (IS_DEBUG_OSPF_NSSA)
+            zlog_debug ("ospf_abr_nssa_am_elected: "
+                       "router %s asserts Nt",
+                       inet_ntoa (lsa->data->id) );
+          return 0;
+        }
+      
+      if (best == NULL)
+       best = &lsa->data->id;
+      else
+        if (IPV4_ADDR_CMP (&best->s_addr, &lsa->data->id.s_addr) < 0)
+          best = &lsa->data->id;
+    }
+    
+    if (IS_DEBUG_OSPF_NSSA)
+      zlog_debug ("ospf_abr_nssa_am_elected: best electable ABR is: %s",
+                  (best) ? inet_ntoa (*best) : "<none>" );
+                  
+    if (best == NULL)
+      return 1;
+    
+    if (IPV4_ADDR_CMP (&best->s_addr, &area->ospf->router_id.s_addr) < 0)
+      return 1;
+    else
+      return 0;
+}
+
+/* Check NSSA ABR status
+ * assumes there are nssa areas
+ */
+static void 
+ospf_abr_nssa_check_status (struct ospf *ospf)
+{
+  struct ospf_area *area;
+  struct listnode *lnode, *nnode;
+  
+  for (ALL_LIST_ELEMENTS (ospf->areas, lnode, nnode, area))
+    {
+      u_char old_state = area->NSSATranslatorState;
+
+      if (area->external_routing != OSPF_AREA_NSSA)
+        continue;
+
+      if (IS_DEBUG_OSPF (nssa, NSSA))
+        zlog_debug ("ospf_abr_nssa_check_status: "
+                    "checking area %s",
+                    inet_ntoa (area->area_id));
+
+      if (!IS_OSPF_ABR (area->ospf))
+        {
+          if (IS_DEBUG_OSPF (nssa, NSSA))
+            zlog_debug ("ospf_abr_nssa_check_status: " 
+                        "not ABR");
+          area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
+        }
+      else
+        {
+          switch (area->NSSATranslatorRole)
+            {
+            case OSPF_NSSA_ROLE_NEVER:
+              /* We never Translate Type-7 LSA. */
+              /* TODO: check previous state and flush? */
+              if (IS_DEBUG_OSPF (nssa, NSSA))
+                zlog_debug ("ospf_abr_nssa_check_status: "
+                           "never translate");
+              area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
+              break;
+
+            case OSPF_NSSA_ROLE_ALWAYS:
+              /* We always translate if we are an ABR
+               * TODO: originate new LSAs if state change?
+               * or let the nssa abr task take care of it?
+               */
+              if (IS_DEBUG_OSPF (nssa, NSSA))
+                zlog_debug ("ospf_abr_nssa_check_status: "
+                            "translate always");
+              area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_ENABLED;
+              break;
+
+            case OSPF_NSSA_ROLE_CANDIDATE:
+              /* We are a candidate for Translation */
+              if (ospf_abr_nssa_am_elected (area) > 0)
+                {
+                  area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_ENABLED;
+                  if (IS_DEBUG_OSPF (nssa, NSSA))
+                    zlog_debug ("ospf_abr_nssa_check_status: "
+                                "elected translator");
+                }
+              else
+                {
+                  area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
+                  if (IS_DEBUG_OSPF (nssa, NSSA))
+                    zlog_debug ("ospf_abr_nssa_check_status: " "not elected");
+                }
+              break;
+            }
+        }
+      /* RFC3101, 3.1:
+       * All NSSA border routers must set the E-bit in the Type-1 router-LSAs
+       * of their directly attached non-stub areas, even when they are not
+       * translating.
+       */
+      if (old_state != area->NSSATranslatorState)
+       {
+          if (old_state == OSPF_NSSA_TRANSLATE_DISABLED)
+           ospf_asbr_status_update (ospf, ++ospf->redistribute);
+         else if (area->NSSATranslatorState == OSPF_NSSA_TRANSLATE_DISABLED)
+           ospf_asbr_status_update (ospf, --ospf->redistribute);
+       }
+    }
+}
+
+/* Check area border router status. */
+void
+ospf_check_abr_status (struct ospf *ospf)
+{
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+  int bb_configured = 0;
+  int bb_act_attached = 0;
+  int areas_configured = 0;
+  int areas_act_attached = 0;
+  u_char new_flags = ospf->flags;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_check_abr_status(): Start");
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      if (listcount (area->oiflist)) 
+       {
+         areas_configured++;
+         
+         if (OSPF_IS_AREA_BACKBONE (area))
+           bb_configured = 1;
+       }
+
+      if (ospf_area_actively_attached (area))
+       {
+         areas_act_attached++;
+         
+         if (OSPF_IS_AREA_BACKBONE (area))
+            bb_act_attached = 1;
+       }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("ospf_check_abr_status(): looked through areas");
+      zlog_debug ("ospf_check_abr_status(): bb_configured: %d", bb_configured);
+      zlog_debug ("ospf_check_abr_status(): bb_act_attached: %d",
+                bb_act_attached);
+      zlog_debug ("ospf_check_abr_status(): areas_configured: %d",
+                areas_configured);
+      zlog_debug ("ospf_check_abr_status(): areas_act_attached: %d",
+                areas_act_attached);
+    }
+
+  switch (ospf->abr_type)
+    {
+    case OSPF_ABR_SHORTCUT:
+    case OSPF_ABR_STAND:
+      if (areas_act_attached > 1)
+       SET_FLAG (new_flags, OSPF_FLAG_ABR);
+      else
+       UNSET_FLAG (new_flags, OSPF_FLAG_ABR);
+      break;
+
+    case OSPF_ABR_IBM:
+      if ((areas_act_attached > 1) && bb_configured)
+       SET_FLAG (new_flags, OSPF_FLAG_ABR);
+      else
+       UNSET_FLAG (new_flags, OSPF_FLAG_ABR);
+      break;
+
+    case OSPF_ABR_CISCO:
+      if ((areas_configured > 1) && bb_act_attached)
+       SET_FLAG (new_flags, OSPF_FLAG_ABR);
+      else
+       UNSET_FLAG (new_flags, OSPF_FLAG_ABR);
+      break;
+    default:
+      break;
+    }
+
+  if (new_flags != ospf->flags)
+    {
+      ospf_spf_calculate_schedule (ospf, SPF_FLAG_ABR_STATUS_CHANGE);
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_check_abr_status(): new router flags: %x",new_flags);
+      ospf->flags = new_flags;
+      ospf_router_lsa_update (ospf);
+    }
+}
+
+static void
+ospf_abr_update_aggregate (struct ospf_area_range *range,
+                           struct ospf_route *or, struct ospf_area *area)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_update_aggregate(): Start");
+
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED) &&
+      (range->cost != OSPF_STUB_MAX_METRIC_SUMMARY_COST))
+    {
+      range->cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST;
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_update_aggregate(): use summary max-metric 0x%08x",
+                   range->cost);
+    }
+  else if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_update_aggregate(): use configured cost %d",
+                   range->cost_config);
+
+      range->cost = range->cost_config;
+    }
+  else
+    {
+      if (range->specifics == 0)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_update_aggregate(): use or->cost %d",
+                       or->cost);
+
+         range->cost = or->cost; /* 1st time get 1st cost */
+       }
+
+      if (or->cost > range->cost)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_update_aggregate(): update to %d", or->cost);
+
+          range->cost = or->cost;
+        }
+    }
+
+  range->specifics++;
+}
+
+static void
+set_metric (struct ospf_lsa *lsa, u_int32_t metric)
+{
+  struct summary_lsa *header;
+  u_char *mp;
+  metric = htonl (metric);
+  mp = (u_char *) &metric;
+  mp++;
+  header = (struct summary_lsa *) lsa->data;
+  memcpy(header->metric, mp, 3);
+}
+
+/* ospf_abr_translate_nssa */
+static int
+ospf_abr_translate_nssa (struct ospf_area *area, struct ospf_lsa *lsa)
+{
+  /* Incoming Type-7 or later aggregated Type-7 
+   *
+   * LSA is skipped if P-bit is off.
+   * LSA is aggregated if within range.
+   *
+   * The Type-7 is translated, Installed/Approved as a Type-5 into
+   * global LSDB, then Flooded through AS
+   *
+   *  Later, any Unapproved Translated Type-5's are flushed/discarded 
+   */
+
+  struct ospf_lsa *old = NULL,
+                  *new = NULL;
+  struct as_external_lsa *ext7;
+  struct prefix_ipv4 p;
+
+  if (! CHECK_FLAG (lsa->data->options, OSPF_OPTION_NP))
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, P-bit off, NO Translation",
+                   inet_ntoa (lsa->data->id));
+      return 1; 
+    }
+  
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, TRANSLATING 7 to 5",
+               inet_ntoa (lsa->data->id));
+
+  ext7 = (struct as_external_lsa *)(lsa->data);
+  p.prefix = lsa->data->id;
+  p.prefixlen = ip_masklen (ext7->mask);
+  
+  if (ext7->e[0].fwd_addr.s_addr == OSPF_DEFAULT_DESTINATION)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, "
+                   "Forward address is 0, NO Translation",
+                   inet_ntoa (lsa->data->id));
+      return 1;
+    }
+  
+  /* try find existing AS-External LSA for this prefix */
+
+  old = ospf_external_info_find_lsa (area->ospf, &p);
+
+  if (old)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_abr_translate_nssa(): " 
+                   "found old translated LSA Id %s, refreshing",
+                   inet_ntoa (old->data->id));
+            
+      /* refresh */
+      new = ospf_translated_nssa_refresh (area->ospf, lsa, old);
+      if (!new)
+        {
+          if (IS_DEBUG_OSPF_NSSA)
+            zlog_debug ("ospf_abr_translate_nssa(): "
+              "could not refresh translated LSA Id %s",
+              inet_ntoa (old->data->id));
+        }
+    }
+  else
+    {
+      /* no existing external route for this LSA Id
+       * originate translated LSA 
+       */
+      
+         if ((new = ospf_translated_nssa_originate (area->ospf, lsa)) 
+              == NULL)
+           {
+             if (IS_DEBUG_OSPF_NSSA)
+               zlog_debug ("ospf_abr_translate_nssa(): Could not translate "
+                          "Type-7 for %s to Type-5", 
+                          inet_ntoa (lsa->data->id));
+              return 1;
+           }
+    }
+
+  /* Area where Aggregate testing will be inserted, just like summary
+     advertisements */
+  /* ospf_abr_check_nssa_range (p_arg, lsa-> cost, lsa -> area); */
+
+  return 0;
+}
+
+static void
+ospf_abr_translate_nssa_range (struct prefix_ipv4 *p, u_int32_t cost)
+{
+  /* The Type-7 is created from the aggregated prefix and forwarded
+     for lsa installation and flooding... to be added... */
+}
+
+void
+ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost,
+                                  struct ospf_area *area)
+{
+  struct ospf_lsa *lsa, *old = NULL;
+  struct summary_lsa *sl = NULL;
+  u_int32_t full_cost;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_network_to_area(): Start");
+
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+    full_cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST;
+  else
+    full_cost = cost;
+
+  old = ospf_lsa_lookup_by_prefix (area->lsdb, OSPF_SUMMARY_LSA, 
+                                   (struct prefix_ipv4 *) p,
+                                   area->ospf->router_id);
+  if (old)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_network_to_area(): old summary found");
+
+      sl = (struct summary_lsa *) old->data;
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_network_to_area(): "
+                  "old metric: %d, new metric: %d",
+               GET_METRIC (sl->metric), cost);
+
+      if ((GET_METRIC (sl->metric) == full_cost) &&
+         ((old->flags & OSPF_LSA_IN_MAXAGE) == 0))
+        {
+          /* unchanged. simply reapprove it */
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_announce_network_to_area(): "
+                       "old summary approved"); 
+          SET_FLAG (old->flags, OSPF_LSA_APPROVED);
+        }
+      else
+        {
+          /* LSA is changed, refresh it */
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_announce_network_to_area(): "
+                       "refreshing summary");
+          set_metric (old, full_cost);
+          lsa = ospf_lsa_refresh (area->ospf, old);
+          
+          if (!lsa)
+            {
+             char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */
+             
+             prefix2str ((struct prefix *) p, buf, sizeof(buf));
+             zlog_warn ("%s: Could not refresh %s to %s",
+                        __func__,
+                        buf,
+                        inet_ntoa (area->area_id));
+             return;
+           }
+         
+          SET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+          /* This will flood through area. */
+        }
+    }
+  else
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_network_to_area(): "
+                  "creating new summary");
+      lsa = ospf_summary_lsa_originate ( (struct prefix_ipv4 *)p, full_cost, area);
+          /* This will flood through area. */
+      
+      if (!lsa)
+       {
+         char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */
+         
+         prefix2str ((struct prefix *)p, buf, sizeof(buf));
+         zlog_warn ("%s: Could not originate %s to %s",
+                    __func__,
+                    buf,
+                    inet_ntoa (area->area_id));
+         return;
+       }
+      
+      SET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_network_to_area(): "
+                  "flooding new version of summary");
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_network_to_area(): Stop");
+}
+
+static int
+ospf_abr_nexthops_belong_to_area (struct ospf_route *or,
+                                 struct ospf_area *area)
+{
+  struct listnode *node, *nnode;
+  struct ospf_path *path;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS_RO (or->paths, node, path))
+    for (ALL_LIST_ELEMENTS_RO (area->oiflist, nnode, oi))
+      if (oi->ifp && oi->ifp->ifindex == path->ifindex)
+       return 1;
+
+  return 0;
+}
+
+static int
+ospf_abr_should_accept (struct prefix_ipv4 *p, struct ospf_area *area)
+{
+  if (IMPORT_NAME (area))
+    {
+      if (IMPORT_LIST (area) == NULL)
+       IMPORT_LIST (area) = access_list_lookup (AFI_IP, IMPORT_NAME (area));
+
+      if (IMPORT_LIST (area))
+        if (access_list_apply (IMPORT_LIST (area), p) == FILTER_DENY)
+           return 0;
+    }
+
+ return 1;
+}
+
+static int
+ospf_abr_plist_in_check (struct ospf_area *area, struct ospf_route *or,
+                        struct prefix_ipv4 *p)
+{
+  if (PREFIX_NAME_IN (area))
+    {
+      if (PREFIX_LIST_IN (area) == NULL)
+       PREFIX_LIST_IN (area) = prefix_list_lookup (AFI_IP,
+                                                   PREFIX_NAME_IN (area));
+      if (PREFIX_LIST_IN (area))
+       if (prefix_list_apply (PREFIX_LIST_IN (area), p) != PREFIX_PERMIT)
+         return 0;
+    }
+  return 1;
+}
+
+static int
+ospf_abr_plist_out_check (struct ospf_area *area, struct ospf_route *or,
+                         struct prefix_ipv4 *p)
+{
+  if (PREFIX_NAME_OUT (area))
+    {
+      if (PREFIX_LIST_OUT (area) == NULL)
+       PREFIX_LIST_OUT (area) = prefix_list_lookup (AFI_IP,
+                                                    PREFIX_NAME_OUT (area));
+      if (PREFIX_LIST_OUT (area))
+       if (prefix_list_apply (PREFIX_LIST_OUT (area), p) != PREFIX_PERMIT)
+         return 0;
+    }
+  return 1;
+}
+
+static void
+ospf_abr_announce_network (struct ospf *ospf,
+                          struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  struct ospf_area_range *range;
+  struct ospf_area *area, *or_area;
+  struct listnode *node;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_network(): Start");
+
+  or_area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id); 
+  assert (or_area);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_network(): looking at area %s",
+                  inet_ntoa (area->area_id));
+
+      if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id))
+       continue;
+
+      if (ospf_abr_nexthops_belong_to_area (or, area))
+       continue;
+
+      if (!ospf_abr_should_accept (p, area))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_network(): "
+                      "prefix %s/%d was denied by import-list",
+                      inet_ntoa (p->prefix), p->prefixlen);
+         continue; 
+       }
+
+      if (!ospf_abr_plist_in_check (area, or, p))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_network(): "
+                      "prefix %s/%d was denied by prefix-list",
+                      inet_ntoa (p->prefix), p->prefixlen);
+         continue; 
+       }
+
+      if (area->external_routing != OSPF_AREA_DEFAULT && area->no_summary)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_network(): "
+                      "area %s is stub and no_summary",
+                      inet_ntoa (area->area_id));
+          continue;
+       }
+
+      if (or->path_type == OSPF_PATH_INTER_AREA)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_network(): this is "
+                      "inter-area route to %s/%d",
+                      inet_ntoa (p->prefix), p->prefixlen);
+
+          if (!OSPF_IS_AREA_BACKBONE (area))
+           ospf_abr_announce_network_to_area (p, or->cost, area);
+       }
+
+      if (or->path_type == OSPF_PATH_INTRA_AREA)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_announce_network(): "
+                       "this is intra-area route to %s/%d",
+                       inet_ntoa (p->prefix), p->prefixlen);
+          if ((range = ospf_area_range_match (or_area, p)) 
+               && !ospf_area_is_transit (area))
+            ospf_abr_update_aggregate (range, or, area);
+          else
+            ospf_abr_announce_network_to_area (p, or->cost, area);
+        }
+    }
+}
+
+static int
+ospf_abr_should_announce (struct ospf *ospf,
+                         struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id);
+
+  assert (area);
+  
+  if (EXPORT_NAME (area))
+    {
+      if (EXPORT_LIST (area) == NULL)
+       EXPORT_LIST (area) = access_list_lookup (AFI_IP, EXPORT_NAME (area));
+
+      if (EXPORT_LIST (area))
+        if (access_list_apply (EXPORT_LIST (area), p) == FILTER_DENY)
+           return 0;
+    }
+
+  return 1;
+}
+
+static void
+ospf_abr_process_nssa_translates (struct ospf *ospf)
+{
+  /* Scan through all NSSA_LSDB records for all areas;
+
+     If P-bit is on, translate all Type-7's to 5's and aggregate or
+     flood install as approved in Type-5 LSDB with XLATE Flag on
+     later, do same for all aggregates...  At end, DISCARD all
+     remaining UNAPPROVED Type-5's (Aggregate is for future ) */
+  struct listnode *node;
+  struct ospf_area *area;
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_process_nssa_translates(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (! area->NSSATranslatorState)
+        continue; /* skip if not translator */
+      
+      if (area->external_routing != OSPF_AREA_NSSA)
+        continue;  /* skip if not Nssa Area */
+
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_abr_process_nssa_translates(): "
+                   "looking at area %s", inet_ntoa (area->area_id));
+      
+      LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
+        ospf_abr_translate_nssa (area, lsa);
+    }
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_process_nssa_translates(): Stop");
+
+}
+
+static void
+ospf_abr_process_network_rt (struct ospf *ospf,
+                            struct route_table *rt)
+{
+  struct ospf_area *area;
+  struct ospf_route *or;
+  struct route_node *rn;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_process_network_rt(): Start");
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+      if ((or = rn->info) == NULL)
+       continue;
+
+      if (!(area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id)))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_process_network_rt(): area %s no longer exists",
+                      inet_ntoa (or->u.std.area_id));
+         continue;
+       }
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_process_network_rt(): this is a route to %s/%d",
+                  inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
+      if (or->path_type >= OSPF_PATH_TYPE1_EXTERNAL)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_process_network_rt(): "
+                      "this is an External router, skipping");
+         continue;
+       }
+
+      if (or->cost >= OSPF_LS_INFINITY)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_process_network_rt():"
+                      " this route's cost is infinity, skipping");
+         continue;
+       }
+
+      if (or->type == OSPF_DESTINATION_DISCARD)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_process_network_rt():"
+                      " this is a discard entry, skipping");
+         continue;
+       }
+
+      if (or->path_type == OSPF_PATH_INTRA_AREA &&
+         !ospf_abr_should_announce (ospf, (struct prefix_ipv4 *) &rn->p, or))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug("ospf_abr_process_network_rt(): denied by export-list");
+         continue;
+       }
+
+      if (or->path_type == OSPF_PATH_INTRA_AREA &&
+         !ospf_abr_plist_out_check (area, or, (struct prefix_ipv4 *) &rn->p))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug("ospf_abr_process_network_rt(): denied by prefix-list");
+         continue;
+       }
+
+      if ((or->path_type == OSPF_PATH_INTER_AREA) &&
+          !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_process_network_rt():"
+                      " this is route is not backbone one, skipping");
+         continue;
+       }
+
+
+      if ((ospf->abr_type == OSPF_ABR_CISCO) ||
+          (ospf->abr_type == OSPF_ABR_IBM))
+
+          if (!ospf_act_bb_connection (ospf) &&
+              or->path_type != OSPF_PATH_INTRA_AREA)
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("ospf_abr_process_network_rt(): ALT ABR: "
+                           "No BB connection, skip not intra-area routes");
+              continue;
+            }
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_process_network_rt(): announcing");
+      ospf_abr_announce_network (ospf, (struct prefix_ipv4 *)&rn->p, or);
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_process_network_rt(): Stop");
+}
+
+static void
+ospf_abr_announce_rtr_to_area (struct prefix_ipv4 *p, u_int32_t cost,
+                              struct ospf_area *area)
+{
+  struct ospf_lsa *lsa, *old = NULL;
+  struct summary_lsa *slsa = NULL;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_rtr_to_area(): Start");
+
+  old = ospf_lsa_lookup_by_prefix (area->lsdb, OSPF_ASBR_SUMMARY_LSA,
+                                  p, area->ospf->router_id);
+  if (old)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_rtr_to_area(): old summary found");
+      slsa = (struct summary_lsa *) old->data;
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_network_to_area(): "
+                  "old metric: %d, new metric: %d",
+                  GET_METRIC (slsa->metric), cost);
+    }
+
+  if (old && (GET_METRIC (slsa->metric) == cost) &&
+      ((old->flags & OSPF_LSA_IN_MAXAGE) == 0))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_rtr_to_area(): old summary approved");
+      SET_FLAG (old->flags, OSPF_LSA_APPROVED);
+    }
+  else
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_rtr_to_area(): 2.2");
+       
+      if (old) 
+       { 
+         set_metric (old, cost);
+         lsa = ospf_lsa_refresh (area->ospf, old);
+       }
+      else
+       lsa = ospf_summary_asbr_lsa_originate (p, cost, area);
+      if (!lsa)
+        {
+          char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */
+          
+          prefix2str ((struct prefix *)p, buf, sizeof(buf));
+          zlog_warn ("%s: Could not refresh/originate %s to %s",
+                     __func__,
+                     buf,
+                     inet_ntoa (area->area_id));
+          return;
+        }
+      
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_rtr_to_area(): "
+                  "flooding new version of summary");
+
+      /*
+      zlog_info ("ospf_abr_announce_rtr_to_area(): creating new summary");
+      lsa = ospf_summary_asbr_lsa (p, cost, area, old); */
+
+      SET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+      /* ospf_flood_through_area (area, NULL, lsa);*/
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_rtr_to_area(): Stop");
+}
+
+
+static void
+ospf_abr_announce_rtr (struct ospf *ospf,
+                      struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_rtr(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_rtr(): looking at area %s",
+                  inet_ntoa (area->area_id));
+
+      if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id))
+       continue;
+
+      if (ospf_abr_nexthops_belong_to_area (or, area))
+       continue;
+
+      if (area->external_routing != OSPF_AREA_DEFAULT)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_rtr(): "
+                      "area %s doesn't support external routing",
+                      inet_ntoa(area->area_id));
+          continue;
+       }
+
+      if (or->path_type == OSPF_PATH_INTER_AREA)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_rtr(): "
+                      "this is inter-area route to %s", inet_ntoa (p->prefix));
+          if (!OSPF_IS_AREA_BACKBONE (area))
+           ospf_abr_announce_rtr_to_area (p, or->cost, area);
+       }
+
+      if (or->path_type == OSPF_PATH_INTRA_AREA)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_abr_announce_rtr(): "
+                      "this is intra-area route to %s", inet_ntoa (p->prefix));
+          ospf_abr_announce_rtr_to_area (p, or->cost, area);
+       }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_rtr(): Stop");
+}
+
+static void
+ospf_abr_process_router_rt (struct ospf *ospf, struct route_table *rt)
+{
+  struct ospf_route *or;
+  struct route_node *rn;
+  struct list *l;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_process_router_rt(): Start");
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+      struct listnode *node, *nnode;
+      char flag = 0;
+      struct ospf_route *best = NULL;
+
+      if (rn->info == NULL)
+       continue;
+
+      l = rn->info;
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_process_router_rt(): this is a route to %s",
+                  inet_ntoa (rn->p.u.prefix4));
+
+      for (ALL_LIST_ELEMENTS (l, node, nnode, or))
+       {
+         if (!ospf_area_lookup_by_area_id (ospf, or->u.std.area_id))
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("ospf_abr_process_router_rt(): area %s no longer exists",
+                        inet_ntoa (or->u.std.area_id));
+             continue;
+           }
+
+
+         if (!CHECK_FLAG (or->u.std.flags, ROUTER_LSA_EXTERNAL))
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("ospf_abr_process_router_rt(): "
+                          "This is not an ASBR, skipping");
+             continue;
+           }
+
+         if (!flag)
+           {
+             best = ospf_find_asbr_route (ospf, rt,
+                                          (struct prefix_ipv4 *) &rn->p);
+             flag = 1;
+           }
+         
+        if (best == NULL)
+         continue;
+       
+        if (or != best)
+         {
+           if (IS_DEBUG_OSPF_EVENT)
+             zlog_debug ("ospf_abr_process_router_rt(): "
+                        "This route is not the best among possible, skipping");
+           continue;
+         }
+       
+        if (or->path_type == OSPF_PATH_INTER_AREA &&
+            !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id))
+         {
+           if (IS_DEBUG_OSPF_EVENT)
+             zlog_debug ("ospf_abr_process_router_rt(): "
+                        "This route is not a backbone one, skipping");
+           continue;
+         }
+
+        if (or->cost >= OSPF_LS_INFINITY)
+         {
+           if (IS_DEBUG_OSPF_EVENT)
+             zlog_debug ("ospf_abr_process_router_rt(): "
+                        "This route has LS_INFINITY metric, skipping");
+           continue;
+         }
+
+        if (ospf->abr_type == OSPF_ABR_CISCO
+           || ospf->abr_type == OSPF_ABR_IBM)
+         if (!ospf_act_bb_connection (ospf)
+             && or->path_type != OSPF_PATH_INTRA_AREA)
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("ospf_abr_process_network_rt(): ALT ABR: "
+                         "No BB connection, skip not intra-area routes");
+             continue;
+           }
+
+        ospf_abr_announce_rtr (ospf, (struct prefix_ipv4 *) &rn->p, or);
+
+       }
+
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_process_router_rt(): Stop");
+}
+
+static void
+ospf_abr_unapprove_translates (struct ospf *ospf) /* For NSSA Translations */
+{
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_unapprove_translates(): Start");
+
+  /* NSSA Translator is not checked, because it may have gone away,
+    and we would want to flush any residuals anyway */
+
+  LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+    if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+      {
+        UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+        if (IS_DEBUG_OSPF_NSSA)
+          zlog_debug ("ospf_abr_unapprove_translates(): "
+                     "approved unset on link id %s",
+                     inet_ntoa (lsa->data->id));
+      }
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_unapprove_translates(): Stop");
+}
+
+static void
+ospf_abr_unapprove_summaries (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_unapprove_summaries(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_unapprove_summaries(): "
+                   "considering area %s",
+                   inet_ntoa (area->area_id)); 
+      LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+      if (ospf_lsa_is_self_originated (ospf, lsa))
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_unapprove_summaries(): "
+                       "approved unset on summary link id %s",
+                       inet_ntoa (lsa->data->id)); 
+          UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+        }
+
+      LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+      if (ospf_lsa_is_self_originated (ospf, lsa))
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("ospf_abr_unapprove_summaries(): "
+                       "approved unset on asbr-summary link id %s",
+                       inet_ntoa (lsa->data->id));
+          UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED);
+        }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_unapprove_summaries(): Stop");
+}
+
+static void
+ospf_abr_prepare_aggregates (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct route_node *rn;
+  struct ospf_area_range *range;
+  struct ospf_area *area;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_prepare_aggregates(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      for (rn = route_top (area->ranges); rn; rn = route_next (rn))
+       if ((range = rn->info) != NULL)
+         {
+           range->cost = 0;
+           range->specifics = 0;
+         }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_prepare_aggregates(): Stop");
+}
+
+static void
+ospf_abr_announce_aggregates (struct ospf *ospf)
+{
+  struct ospf_area *area, *ar;
+  struct ospf_area_range *range;
+  struct route_node *rn;
+  struct prefix p;
+  struct listnode *node, *n;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_aggregates(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_announce_aggregates(): looking at area %s",
+                  inet_ntoa (area->area_id));
+
+      for (rn = route_top (area->ranges); rn; rn = route_next (rn))
+       if ((range =  rn->info))
+         {
+           if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE))
+             {
+               if (IS_DEBUG_OSPF_EVENT)
+                 zlog_debug ("ospf_abr_announce_aggregates():"
+                            " discarding suppress-ranges");
+               continue;
+             }
+
+           p.family = AF_INET;
+           p.u.prefix4 = range->addr;
+           p.prefixlen = range->masklen;
+
+           if (IS_DEBUG_OSPF_EVENT)
+             zlog_debug ("ospf_abr_announce_aggregates():"
+                        " this is range: %s/%d",
+                        inet_ntoa (p.u.prefix4), p.prefixlen);
+
+           if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE))
+             {
+               p.family = AF_INET;
+               p.u.prefix4 = range->subst_addr;
+               p.prefixlen = range->subst_masklen;
+             }
+
+           if (range->specifics)
+             {
+               if (IS_DEBUG_OSPF_EVENT)
+                 zlog_debug ("ospf_abr_announce_aggregates(): active range");
+
+               for (ALL_LIST_ELEMENTS_RO (ospf->areas, n, ar))
+                 {
+                   if (ar == area)
+                     continue;
+
+                   /* We do not check nexthops here, because
+                      intra-area routes can be associated with
+                      one area only */
+
+                   /* backbone routes are not summarized
+                      when announced into transit areas */
+
+                   if (ospf_area_is_transit (ar) &&
+                       OSPF_IS_AREA_BACKBONE (area))
+                     {
+                       if (IS_DEBUG_OSPF_EVENT)
+                         zlog_debug ("ospf_abr_announce_aggregates(): Skipping "
+                                    "announcement of BB aggregate into"
+                                    " a transit area");
+                       continue; 
+                     }
+                   ospf_abr_announce_network_to_area ((struct prefix_ipv4 *)&p, range->cost, ar);
+                 }
+             }
+         }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_aggregates(): Stop");
+}
+
+static void
+ospf_abr_send_nssa_aggregates (struct ospf *ospf) /* temporarily turned off */
+{
+  struct listnode *node; /*, n; */
+  struct ospf_area *area; /*, *ar; */
+  struct route_node *rn;
+  struct ospf_area_range *range;
+  struct prefix_ipv4 p;
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_send_nssa_aggregates(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (! area->NSSATranslatorState)
+       continue;
+
+      if (IS_DEBUG_OSPF_NSSA)
+       zlog_debug ("ospf_abr_send_nssa_aggregates(): looking at area %s",
+                  inet_ntoa (area->area_id));
+
+      for (rn = route_top (area->ranges); rn; rn = route_next (rn))
+       {
+          if (rn->info == NULL)
+           continue;
+
+         range = rn->info;
+
+          if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE))
+           {
+             if (IS_DEBUG_OSPF_NSSA)
+               zlog_debug ("ospf_abr_send_nssa_aggregates():"
+                          " discarding suppress-ranges");
+             continue;
+           }
+
+          p.family = AF_INET;
+          p.prefix = range->addr;
+          p.prefixlen = range->masklen;
+
+         if (IS_DEBUG_OSPF_NSSA)
+           zlog_debug ("ospf_abr_send_nssa_aggregates():"
+                      " this is range: %s/%d",
+                      inet_ntoa (p.prefix), p.prefixlen);
+
+          if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE))
+           {
+             p.family = AF_INET;
+             p.prefix = range->subst_addr;
+             p.prefixlen = range->subst_masklen;
+           }
+
+          if (range->specifics)
+            {
+              if (IS_DEBUG_OSPF_NSSA)
+                zlog_debug ("ospf_abr_send_nssa_aggregates(): active range");
+
+              /* Fetch LSA-Type-7 from aggregate prefix, and then
+               *  translate, Install (as Type-5), Approve, and Flood
+               */
+              ospf_abr_translate_nssa_range (&p, range->cost);
+            }
+        } /* all area ranges*/
+    } /* all areas */
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_send_nssa_aggregates(): Stop");
+}
+
+static void
+ospf_abr_announce_stub_defaults (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  struct prefix_ipv4 p;
+
+  if (! IS_OSPF_ABR (ospf))
+    return;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_stub_defaults(): Start");
+
+  p.family = AF_INET;
+  p.prefix.s_addr = OSPF_DEFAULT_DESTINATION;
+  p.prefixlen = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_stub_defaults(): looking at area %s",
+                   inet_ntoa (area->area_id));
+
+     if ( (area->external_routing != OSPF_AREA_STUB)
+          && (area->external_routing != OSPF_AREA_NSSA)
+        )
+       continue;
+
+      if (OSPF_IS_AREA_BACKBONE (area))
+        continue; /* Sanity Check */
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_abr_announce_stub_defaults(): "
+                   "announcing 0.0.0.0/0 to area %s",
+                 inet_ntoa (area->area_id));
+      ospf_abr_announce_network_to_area (&p, area->default_cost, area);
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_stub_defaults(): Stop");
+}
+
+static int
+ospf_abr_remove_unapproved_translates_apply (struct ospf *ospf,
+                                            struct ospf_lsa *lsa)
+{
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)
+      && ! CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED))
+    {
+      zlog_info ("ospf_abr_remove_unapproved_translates(): "
+                "removing unapproved translates, ID: %s",
+                inet_ntoa (lsa->data->id));
+
+      /* FLUSH THROUGHOUT AS */
+      ospf_lsa_flush_as (ospf, lsa);
+
+      /* DISCARD from LSDB  */
+    }
+  return 0;
+}
+
+static void
+ospf_abr_remove_unapproved_translates (struct ospf *ospf)
+{
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  /* All AREA PROCESS should have APPROVED necessary LSAs */
+  /* Remove any left over and not APPROVED */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_remove_unapproved_translates(): Start");
+
+  LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+    ospf_abr_remove_unapproved_translates_apply (ospf, lsa);
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_remove_unapproved_translates(): Stop");
+}
+
+static void
+ospf_abr_remove_unapproved_summaries (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_remove_unapproved_summaries(): Start");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_remove_unapproved_summaries(): "
+                  "looking at area %s", inet_ntoa (area->area_id));
+
+      LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+       if (ospf_lsa_is_self_originated (ospf, lsa))
+         if (!CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED))
+           ospf_lsa_flush_area (lsa, area);
+
+      LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+       if (ospf_lsa_is_self_originated (ospf, lsa))
+         if (!CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED))
+           ospf_lsa_flush_area (lsa, area);
+    }
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_remove_unapproved_summaries(): Stop");
+}
+
+static void
+ospf_abr_manage_discard_routes (struct ospf *ospf)
+{
+  struct listnode *node, *nnode;
+  struct route_node *rn;
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    for (rn = route_top (area->ranges); rn; rn = route_next (rn))
+      if ((range = rn->info) != NULL)
+       if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE))
+         {
+           if (range->specifics)
+             ospf_add_discard_route (ospf->new_table, area,
+                                     (struct prefix_ipv4 *) &rn->p);
+           else
+             ospf_delete_discard_route (ospf->new_table,
+                                        (struct prefix_ipv4 *) &rn->p);
+         }
+}
+
+/* This is the function taking care about ABR NSSA, i.e.  NSSA
+   Translator, -LSA aggregation and flooding. For all NSSAs
+
+   Any SELF-AS-LSA is in the Type-5 LSDB and Type-7 LSDB.  These LSA's
+   are refreshed from the Type-5 LSDB, installed into the Type-7 LSDB
+   with the P-bit set.
+
+   Any received Type-5s are legal for an ABR, else illegal for IR.
+   Received Type-7s are installed, by area, with incoming P-bit.  They
+   are flooded; if the Elected NSSA Translator, then P-bit off.
+
+   Additionally, this ABR will place "translated type-7's" into the
+   Type-5 LSDB in order to keep track of APPROVAL or not.
+
+   It will scan through every area, looking for Type-7 LSAs with P-Bit
+   SET. The Type-7's are either AS-FLOODED & 5-INSTALLED or
+   AGGREGATED.  Later, the AGGREGATED LSAs are AS-FLOODED &
+   5-INSTALLED.
+
+   5-INSTALLED is into the Type-5 LSDB; Any UNAPPROVED Type-5 LSAs
+   left over are FLUSHED and DISCARDED.
+
+   For External Calculations, any NSSA areas use the Type-7 AREA-LSDB,
+   any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */
+
+static void
+ospf_abr_nssa_task (struct ospf *ospf) /* called only if any_nssa */
+{
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("Check for NSSA-ABR Tasks():");
+
+  if (! IS_OSPF_ABR (ospf))
+    return;
+
+  if (! ospf->anyNSSA)
+    return;
+
+  /* Each area must confirm TranslatorRole */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): Start");
+
+  /* For all Global Entries flagged "local-translate", unset APPROVED */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): unapprove translates");
+
+  ospf_abr_unapprove_translates (ospf);
+
+  /* RESET all Ranges in every Area, same as summaries */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): NSSA initialize aggregates");
+  ospf_abr_prepare_aggregates (ospf);  /*TURNED OFF just for now */
+
+  /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or
+   *  Aggregate as Type-7
+   * Install or Approve in Type-5 Global LSDB 
+   */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): process translates");
+  ospf_abr_process_nssa_translates (ospf);
+
+  /* Translate/Send any "ranged" aggregates, and also 5-Install and
+   *  Approve
+   * Scan Type-7's for aggregates, translate to Type-5's,
+   *  Install/Flood/Approve 
+   */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug("ospf_abr_nssa_task(): send NSSA aggregates");
+  ospf_abr_send_nssa_aggregates (ospf);  /*TURNED OFF FOR NOW */
+
+  /* Send any NSSA defaults as Type-5 
+   *if (IS_DEBUG_OSPF_NSSA)
+   * zlog_debug ("ospf_abr_nssa_task(): announce nssa defaults");
+   *ospf_abr_announce_nssa_defaults (ospf);
+   * havnt a clue what above is supposed to do.
+   */
+   
+  /* Flush any unapproved previous translates from Global Data Base */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): remove unapproved translates");
+  ospf_abr_remove_unapproved_translates (ospf);
+
+  ospf_abr_manage_discard_routes (ospf); /* same as normal...discard */
+
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_abr_nssa_task(): Stop");
+}
+
+/* This is the function taking care about ABR stuff, i.e.
+   summary-LSA origination and flooding. */
+void
+ospf_abr_task (struct ospf *ospf)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_task(): Start");
+
+  if (ospf->new_table == NULL || ospf->new_rtrs == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_task(): Routing tables are not yet ready");
+      return;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_task(): unapprove summaries");
+  ospf_abr_unapprove_summaries (ospf);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_task(): prepare aggregates");
+  ospf_abr_prepare_aggregates (ospf);
+
+  if (IS_OSPF_ABR (ospf))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_task(): process network RT");
+      ospf_abr_process_network_rt (ospf, ospf->new_table);
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_task(): process router RT");
+      ospf_abr_process_router_rt (ospf, ospf->new_rtrs);
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_task(): announce aggregates");
+      ospf_abr_announce_aggregates (ospf);
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_abr_task(): announce stub defaults");
+      ospf_abr_announce_stub_defaults (ospf);
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_task(): remove unapproved summaries");
+  ospf_abr_remove_unapproved_summaries (ospf);
+
+  ospf_abr_manage_discard_routes (ospf);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_task(): Stop");
+}
+
+static int
+ospf_abr_task_timer (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+
+  ospf->t_abr_task = 0;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Running ABR task on timer");
+
+  ospf_check_abr_status (ospf);
+  ospf_abr_nssa_check_status (ospf);
+
+  ospf_abr_task (ospf);
+  ospf_abr_nssa_task (ospf); /* if nssa-abr, then scan Type-7 LSDB */
+
+  return 0;
+}
+
+void
+ospf_schedule_abr_task (struct ospf *ospf)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Scheduling ABR task");
+
+  if (ospf->t_abr_task == NULL)
+    ospf->t_abr_task = thread_add_timer (master, ospf_abr_task_timer,
+                                        ospf, OSPF_ABR_TASK_DELAY);
+}
diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h
new file mode 100644 (file)
index 0000000..e367e44
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * OSPF ABR functions.
+ * Copyright (C) 1999 Alex Zinin
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ABR_H
+#define _ZEBRA_OSPF_ABR_H
+
+#define OSPF_ABR_TASK_DELAY    7
+
+#define OSPF_AREA_RANGE_ADVERTISE      (1 << 0)
+#define OSPF_AREA_RANGE_SUBSTITUTE     (1 << 1)
+
+/* Area range. */
+struct ospf_area_range
+{
+  /* Area range address. */
+  struct in_addr addr;
+
+  /* Area range masklen. */
+  u_char masklen;
+
+  /* Flags. */
+  u_char flags;
+
+  /* Number of more specific prefixes. */
+  int specifics;
+
+  /* Addr and masklen to substitute. */
+  struct in_addr subst_addr;
+  u_char subst_masklen;
+
+  /* Range cost. */
+  u_int32_t cost;
+
+  /* Configured range cost. */
+  u_int32_t cost_config;
+#define OSPF_AREA_RANGE_COST_UNSPEC    -1U
+};
+
+/* Prototypes. */
+extern struct ospf_area_range *ospf_area_range_lookup (struct ospf_area *,
+                                                      struct prefix_ipv4 *);
+
+extern struct ospf_area_range *ospf_some_area_range_match (struct prefix_ipv4
+                                                          *);
+
+extern struct ospf_area_range *ospf_area_range_lookup_next (struct ospf_area
+                                                           *,
+                                                           struct in_addr *,
+                                                           int);
+
+extern int ospf_area_range_set (struct ospf *, struct in_addr,
+                               struct prefix_ipv4 *, int);
+extern int ospf_area_range_cost_set (struct ospf *, struct in_addr,
+                                    struct prefix_ipv4 *, u_int32_t);
+extern int ospf_area_range_unset (struct ospf *, struct in_addr,
+                                 struct prefix_ipv4 *);
+extern int ospf_area_range_substitute_set (struct ospf *, struct in_addr,
+                                          struct prefix_ipv4 *,
+                                          struct prefix_ipv4 *);
+extern int ospf_area_range_substitute_unset (struct ospf *, struct in_addr,
+                                            struct prefix_ipv4 *);
+extern struct ospf_area_range *ospf_area_range_match_any (struct ospf *,
+                                                         struct prefix_ipv4
+                                                         *);
+extern int ospf_area_range_active (struct ospf_area_range *);
+extern int ospf_act_bb_connection (struct ospf *);
+
+extern void ospf_check_abr_status (struct ospf *);
+extern void ospf_abr_task (struct ospf *);
+extern void ospf_schedule_abr_task (struct ospf *);
+
+extern void ospf_abr_announce_network_to_area (struct prefix_ipv4 *, 
+                                               u_int32_t,
+                                               struct ospf_area *);
+#endif /* _ZEBRA_OSPF_ABR_H */
diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c
new file mode 100644 (file)
index 0000000..e92f162
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * API message handling module for OSPF daemon and client.
+ * Copyright (C) 2001, 2002 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#ifdef SUPPORT_OSPF_API
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"         /* for inet_aton() */
+#include "buffer.h"
+#include "network.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+
+#include "ospfd/ospf_api.h"
+
+
+/* For debugging only, will be removed */
+void
+api_opaque_lsa_print (struct lsa_header *data)
+{
+  struct opaque_lsa
+  {
+    struct lsa_header header;
+    u_char mydata[];
+  };
+
+  struct opaque_lsa *olsa;
+  int opaquelen;
+  int i;
+
+  ospf_lsa_header_dump (data);
+
+  olsa = (struct opaque_lsa *) data;
+
+  opaquelen = ntohs (data->length) - OSPF_LSA_HEADER_SIZE;
+  zlog_debug ("apiserver_lsa_print: opaquelen=%d\n", opaquelen);
+
+  for (i = 0; i < opaquelen; i++)
+    {
+      zlog_debug ("0x%x ", olsa->mydata[i]);
+    }
+  zlog_debug ("\n");
+}
+
+/* -----------------------------------------------------------
+ * Generic messages
+ * -----------------------------------------------------------
+ */
+
+struct msg *
+msg_new (u_char msgtype, void *msgbody, u_int32_t seqnum, u_int16_t msglen)
+{
+  struct msg *new;
+
+  new = XCALLOC (MTYPE_OSPF_API_MSG, sizeof (struct msg));
+
+  new->hdr.version = OSPF_API_VERSION;
+  new->hdr.msgtype = msgtype;
+  new->hdr.msglen = htons (msglen);
+  new->hdr.msgseq = htonl (seqnum);
+
+  new->s = stream_new (msglen);
+  assert (new->s);
+  stream_put (new->s, msgbody, msglen);
+
+  return new;
+}
+
+
+/* Duplicate a message by copying content. */
+struct msg *
+msg_dup (struct msg *msg)
+{
+  struct msg *new;
+
+  assert (msg);
+
+  new = msg_new (msg->hdr.msgtype, STREAM_DATA (msg->s),
+                ntohl (msg->hdr.msgseq), ntohs (msg->hdr.msglen));
+  return new;
+}
+
+
+/* XXX only for testing, will be removed */
+
+struct nametab {
+  int value;
+  const char *name;
+};
+
+const char *
+ospf_api_typename (int msgtype)
+{
+  struct nametab NameTab[] = {
+    { MSG_REGISTER_OPAQUETYPE,   "Register opaque-type",   },
+    { MSG_UNREGISTER_OPAQUETYPE, "Unregister opaque-type", },
+    { MSG_REGISTER_EVENT,        "Register event",         },
+    { MSG_SYNC_LSDB,             "Sync LSDB",              },
+    { MSG_ORIGINATE_REQUEST,     "Originate request",      },
+    { MSG_DELETE_REQUEST,        "Delete request",         },
+    { MSG_REPLY,                 "Reply",                  },
+    { MSG_READY_NOTIFY,          "Ready notify",           },
+    { MSG_LSA_UPDATE_NOTIFY,     "LSA update notify",      },
+    { MSG_LSA_DELETE_NOTIFY,     "LSA delete notify",      },
+    { MSG_NEW_IF,                "New interface",          },
+    { MSG_DEL_IF,                "Del interface",          },
+    { MSG_ISM_CHANGE,            "ISM change",             },
+    { MSG_NSM_CHANGE,            "NSM change",             },
+  };
+
+  int i, n = array_size(NameTab);
+  const char *name = NULL;
+
+  for (i = 0; i < n; i++)
+    {
+      if (NameTab[i].value == msgtype)
+        {
+          name = NameTab[i].name;
+          break;
+        }
+    }
+
+  return name ? name : "?";
+}
+
+const char *
+ospf_api_errname (int errcode)
+{
+  struct nametab NameTab[] = {
+    { OSPF_API_OK,                      "OK",                         },
+    { OSPF_API_NOSUCHINTERFACE,         "No such interface",          },
+    { OSPF_API_NOSUCHAREA,              "No such area",               },
+    { OSPF_API_NOSUCHLSA,               "No such LSA",                },
+    { OSPF_API_ILLEGALLSATYPE,          "Illegal LSA type",           },
+    { OSPF_API_OPAQUETYPEINUSE,         "Opaque type in use",         },
+    { OSPF_API_OPAQUETYPENOTREGISTERED, "Opaque type not registered", },
+    { OSPF_API_NOTREADY,                "Not ready",                  },
+    { OSPF_API_NOMEMORY,                "No memory",                  },
+    { OSPF_API_ERROR,                   "Other error",                },
+    { OSPF_API_UNDEF,                   "Undefined",                  },
+  };
+
+  int i, n = array_size(NameTab);
+  const char *name = NULL;
+
+  for (i = 0; i < n; i++)
+    {
+      if (NameTab[i].value == errcode)
+        {
+          name = NameTab[i].name;
+          break;
+        }
+    }
+
+  return name ? name : "?";
+}
+
+void
+msg_print (struct msg *msg)
+{
+  if (!msg)
+    {
+      zlog_debug ("msg_print msg=NULL!\n");
+      return;
+    }
+
+#ifdef ORIGINAL_CODING
+  zlog_debug
+    ("msg=%p msgtype=%d msglen=%d msgseq=%d streamdata=%p streamsize=%lu\n",
+     msg, msg->hdr.msgtype, ntohs (msg->hdr.msglen), ntohl (msg->hdr.msgseq),
+     STREAM_DATA (msg->s), STREAM_SIZE (msg->s));
+#else /* ORIGINAL_CODING */
+  /* API message common header part. */
+  zlog_debug
+    ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)",
+     ospf_api_typename (msg->hdr.msgtype), msg->hdr.msgtype, 
+     ntohs (msg->hdr.msglen), (unsigned long) ntohl (msg->hdr.msgseq),
+     STREAM_DATA (msg->s), STREAM_SIZE (msg->s));
+
+  /* API message body part. */
+#ifdef ndef
+  /* Generic Hex/Ascii dump */
+  DumpBuf (STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); /* Sorry, deleted! */
+#else /* ndef */
+  /* Message-type dependent dump function. */
+#endif /* ndef */
+
+  return;
+#endif /* ORIGINAL_CODING */
+}
+
+void
+msg_free (struct msg *msg)
+{
+  if (msg->s)
+    stream_free (msg->s);
+
+  XFREE (MTYPE_OSPF_API_MSG, msg);
+}
+
+
+/* Set sequence number of message */
+void
+msg_set_seq (struct msg *msg, u_int32_t seqnr)
+{
+  assert (msg);
+  msg->hdr.msgseq = htonl (seqnr);
+}
+
+/* Get sequence number of message */
+u_int32_t
+msg_get_seq (struct msg *msg)
+{
+  assert (msg);
+  return ntohl (msg->hdr.msgseq);
+}
+
+/* -----------------------------------------------------------
+ * Message fifo queues
+ * -----------------------------------------------------------
+ */
+
+struct msg_fifo *
+msg_fifo_new ()
+{
+  return XCALLOC (MTYPE_OSPF_API_FIFO, sizeof (struct msg_fifo));
+}
+
+/* Add new message to fifo. */
+void
+msg_fifo_push (struct msg_fifo *fifo, struct msg *msg)
+{
+  if (fifo->tail)
+    fifo->tail->next = msg;
+  else
+    fifo->head = msg;
+
+  fifo->tail = msg;
+  fifo->count++;
+}
+
+
+/* Remove first message from fifo. */
+struct msg *
+msg_fifo_pop (struct msg_fifo *fifo)
+{
+  struct msg *msg;
+
+  msg = fifo->head;
+  if (msg)
+    {
+      fifo->head = msg->next;
+
+      if (fifo->head == NULL)
+       fifo->tail = NULL;
+
+      fifo->count--;
+    }
+  return msg;
+}
+
+/* Return first fifo entry but do not remove it. */
+struct msg *
+msg_fifo_head (struct msg_fifo *fifo)
+{
+  return fifo->head;
+}
+
+/* Flush message fifo. */
+void
+msg_fifo_flush (struct msg_fifo *fifo)
+{
+  struct msg *op;
+  struct msg *next;
+
+  for (op = fifo->head; op; op = next)
+    {
+      next = op->next;
+      msg_free (op);
+    }
+
+  fifo->head = fifo->tail = NULL;
+  fifo->count = 0;
+}
+
+/* Free API message fifo. */
+void
+msg_fifo_free (struct msg_fifo *fifo)
+{
+  msg_fifo_flush (fifo);
+
+  XFREE (MTYPE_OSPF_API_FIFO, fifo);
+}
+
+struct msg *
+msg_read (int fd)
+{
+  struct msg *msg;
+  struct apimsghdr hdr;
+  u_char buf[OSPF_API_MAX_MSG_SIZE];
+  int bodylen;
+  int rlen;
+
+  /* Read message header */
+  rlen = readn (fd, (u_char *) &hdr, sizeof (struct apimsghdr));
+
+  if (rlen < 0)
+    {
+      zlog_warn ("msg_read: readn %s", safe_strerror (errno));
+      return NULL;
+    }
+  else if (rlen == 0)
+    {
+      zlog_warn ("msg_read: Connection closed by peer");
+      return NULL;
+    }
+  else if (rlen != sizeof (struct apimsghdr))
+    {
+      zlog_warn ("msg_read: Cannot read message header!");
+      return NULL;
+    }
+
+  /* Check version of API protocol */
+  if (hdr.version != OSPF_API_VERSION)
+    {
+      zlog_warn ("msg_read: OSPF API protocol version mismatch");
+      return NULL;
+    }
+
+  /* Determine body length. */
+  bodylen = ntohs (hdr.msglen);
+  if (bodylen > 0)
+    {
+
+      /* Read message body */
+      rlen = readn (fd, buf, bodylen);
+      if (rlen < 0)
+       {
+         zlog_warn ("msg_read: readn %s", safe_strerror (errno));
+         return NULL;
+       }
+      else if (rlen == 0)
+       {
+         zlog_warn ("msg_read: Connection closed by peer");
+         return NULL;
+       }
+      else if (rlen != bodylen)
+       {
+         zlog_warn ("msg_read: Cannot read message body!");
+         return NULL;
+       }
+    }
+
+  /* Allocate new message */
+  msg = msg_new (hdr.msgtype, buf, ntohl (hdr.msgseq), ntohs (hdr.msglen));
+
+  return msg;
+}
+
+int
+msg_write (int fd, struct msg *msg)
+{
+  u_char buf[OSPF_API_MAX_MSG_SIZE];
+  int l;
+  int wlen;
+
+  assert (msg);
+  assert (msg->s);
+
+  /* Length of message including header */
+  l = sizeof (struct apimsghdr) + ntohs (msg->hdr.msglen);
+
+  /* Make contiguous memory buffer for message */
+  memcpy (buf, &msg->hdr, sizeof (struct apimsghdr));
+  memcpy (buf + sizeof (struct apimsghdr), STREAM_DATA (msg->s),
+         ntohs (msg->hdr.msglen));
+
+  wlen = writen (fd, buf, l);
+  if (wlen < 0)
+    {
+      zlog_warn ("msg_write: writen %s", safe_strerror (errno));
+      return -1;
+    }
+  else if (wlen == 0)
+    {
+      zlog_warn ("msg_write: Connection closed by peer");
+      return -1;
+    }
+  else if (wlen != l)
+    {
+      zlog_warn ("msg_write: Cannot write API message");
+      return -1;
+    }
+  return 0;
+}
+
+/* -----------------------------------------------------------
+ * Specific messages
+ * -----------------------------------------------------------
+ */
+
+struct msg *
+new_msg_register_opaque_type (u_int32_t seqnum, u_char ltype, u_char otype)
+{
+  struct msg_register_opaque_type rmsg;
+
+  rmsg.lsatype = ltype;
+  rmsg.opaquetype = otype;
+  memset (&rmsg.pad, 0, sizeof (rmsg.pad));
+
+  return msg_new (MSG_REGISTER_OPAQUETYPE, &rmsg, seqnum,
+                 sizeof (struct msg_register_opaque_type));
+}
+
+struct msg *
+new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter)
+{
+  u_char buf[OSPF_API_MAX_MSG_SIZE];
+  struct msg_register_event *emsg;
+  size_t len;
+
+  emsg = (struct msg_register_event *) buf;
+  len = sizeof (struct msg_register_event) +
+    filter->num_areas * sizeof (struct in_addr);
+  emsg->filter.typemask = htons (filter->typemask);
+  emsg->filter.origin = filter->origin;
+  emsg->filter.num_areas = filter->num_areas;
+  if (len > sizeof (buf))
+    len = sizeof(buf);
+  /* API broken - missing memcpy to fill data */
+  return msg_new (MSG_REGISTER_EVENT, emsg, seqnum, len);
+}
+
+struct msg *
+new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter)
+{
+  u_char buf[OSPF_API_MAX_MSG_SIZE];
+  struct msg_sync_lsdb *smsg;
+  size_t len;
+
+  smsg = (struct msg_sync_lsdb *) buf;
+  len = sizeof (struct msg_sync_lsdb) +
+    filter->num_areas * sizeof (struct in_addr);
+  smsg->filter.typemask = htons (filter->typemask);
+  smsg->filter.origin = filter->origin;
+  smsg->filter.num_areas = filter->num_areas;
+  if (len > sizeof (buf))
+    len = sizeof(buf);
+  /* API broken - missing memcpy to fill data */
+  return msg_new (MSG_SYNC_LSDB, smsg, seqnum, len);
+}
+
+
+struct msg *
+new_msg_originate_request (u_int32_t seqnum,
+                          struct in_addr ifaddr,
+                          struct in_addr area_id, struct lsa_header *data)
+{
+  struct msg_originate_request *omsg;
+  size_t omsglen;
+  char buf[OSPF_API_MAX_MSG_SIZE];
+
+  omsg = (struct msg_originate_request *) buf;
+  omsg->ifaddr = ifaddr;
+  omsg->area_id = area_id;
+
+  omsglen = ntohs (data->length);
+  if (omsglen > sizeof (buf) - offsetof (struct msg_originate_request, data))
+    omsglen = sizeof (buf) - offsetof (struct msg_originate_request, data);
+  memcpy (&omsg->data, data, omsglen);
+  omsglen += sizeof (struct msg_originate_request) - sizeof (struct lsa_header);
+
+  return msg_new (MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen);
+}
+
+struct msg *
+new_msg_delete_request (u_int32_t seqnum,
+                       struct in_addr area_id, u_char lsa_type,
+                       u_char opaque_type, u_int32_t opaque_id)
+{
+  struct msg_delete_request dmsg;
+  dmsg.area_id = area_id;
+  dmsg.lsa_type = lsa_type;
+  dmsg.opaque_type = opaque_type;
+  dmsg.opaque_id = htonl (opaque_id);
+  memset (&dmsg.pad, 0, sizeof (dmsg.pad));
+
+  return msg_new (MSG_DELETE_REQUEST, &dmsg, seqnum,
+                 sizeof (struct msg_delete_request));
+}
+
+
+struct msg *
+new_msg_reply (u_int32_t seqnr, u_char rc)
+{
+  struct msg *msg;
+  struct msg_reply rmsg;
+
+  /* Set return code */
+  rmsg.errcode = rc;
+  memset (&rmsg.pad, 0, sizeof (rmsg.pad));
+
+  msg = msg_new (MSG_REPLY, &rmsg, seqnr, sizeof (struct msg_reply));
+
+  return msg;
+}
+
+struct msg *
+new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type,
+                     u_char opaque_type, struct in_addr addr)
+{
+  struct msg_ready_notify rmsg;
+
+  rmsg.lsa_type = lsa_type;
+  rmsg.opaque_type = opaque_type;
+  memset (&rmsg.pad, 0, sizeof (rmsg.pad));
+  rmsg.addr = addr;
+
+  return msg_new (MSG_READY_NOTIFY, &rmsg, seqnr,
+                 sizeof (struct msg_ready_notify));
+}
+
+struct msg *
+new_msg_new_if (u_int32_t seqnr,
+               struct in_addr ifaddr, struct in_addr area_id)
+{
+  struct msg_new_if nmsg;
+
+  nmsg.ifaddr = ifaddr;
+  nmsg.area_id = area_id;
+
+  return msg_new (MSG_NEW_IF, &nmsg, seqnr, sizeof (struct msg_new_if));
+}
+
+struct msg *
+new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr)
+{
+  struct msg_del_if dmsg;
+
+  dmsg.ifaddr = ifaddr;
+
+  return msg_new (MSG_DEL_IF, &dmsg, seqnr, sizeof (struct msg_del_if));
+}
+
+struct msg *
+new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr,
+                   struct in_addr area_id, u_char status)
+{
+  struct msg_ism_change imsg;
+
+  imsg.ifaddr = ifaddr;
+  imsg.area_id = area_id;
+  imsg.status = status;
+  memset (&imsg.pad, 0, sizeof (imsg.pad));
+
+  return msg_new (MSG_ISM_CHANGE, &imsg, seqnr,
+                 sizeof (struct msg_ism_change));
+}
+
+struct msg *
+new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr,
+                   struct in_addr nbraddr,
+                   struct in_addr router_id, u_char status)
+{
+  struct msg_nsm_change nmsg;
+
+  nmsg.ifaddr = ifaddr;
+  nmsg.nbraddr = nbraddr;
+  nmsg.router_id = router_id;
+  nmsg.status = status;
+  memset (&nmsg.pad, 0, sizeof (nmsg.pad));
+
+  return msg_new (MSG_NSM_CHANGE, &nmsg, seqnr,
+                 sizeof (struct msg_nsm_change));
+}
+
+struct msg *
+new_msg_lsa_change_notify (u_char msgtype,
+                          u_int32_t seqnum,
+                          struct in_addr ifaddr,
+                          struct in_addr area_id,
+                          u_char is_self_originated, struct lsa_header *data)
+{
+  u_char buf[OSPF_API_MAX_MSG_SIZE];
+  struct msg_lsa_change_notify *nmsg;
+  size_t len;
+
+  assert (data);
+
+  nmsg = (struct msg_lsa_change_notify *) buf;
+  nmsg->ifaddr = ifaddr;
+  nmsg->area_id = area_id;
+  nmsg->is_self_originated = is_self_originated;
+  memset (&nmsg->pad, 0, sizeof (nmsg->pad));
+
+  len = ntohs (data->length);
+  if (len > sizeof (buf) - offsetof (struct msg_lsa_change_notify, data))
+    len = sizeof (buf) - offsetof (struct msg_lsa_change_notify, data);
+  memcpy (&nmsg->data, data, len);
+  len += sizeof (struct msg_lsa_change_notify) - sizeof (struct lsa_header);
+
+  return msg_new (msgtype, nmsg, seqnum, len);
+}
+
+#endif /* SUPPORT_OSPF_API */
diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h
new file mode 100644 (file)
index 0000000..eb5ad0a
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * API message handling module for OSPF daemon and client.
+ * Copyright (C) 2001, 2002 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+/* This file is used both by the OSPFd and client applications to
+   define message formats used for communication. */
+
+#ifndef _OSPF_API_H
+#define _OSPF_API_H
+
+#define OSPF_API_VERSION           1
+
+/* MTYPE definition is not reflected to "memory.h". */
+#define MTYPE_OSPF_API_MSG      MTYPE_TMP
+#define MTYPE_OSPF_API_FIFO     MTYPE_TMP
+
+/* Default API server port to accept connection request from client-side. */
+/* This value could be overridden by "ospfapi" entry in "/etc/services". */
+#define OSPF_API_SYNC_PORT      2607
+
+/* -----------------------------------------------------------
+ * Generic messages 
+ * -----------------------------------------------------------
+ */
+
+/* Message header structure, fields are in network byte order and
+   aligned to four octets. */
+struct apimsghdr
+{
+  u_char version;              /* OSPF API protocol version */
+  u_char msgtype;              /* Type of message */
+  u_int16_t msglen;            /* Length of message w/o header */
+  u_int32_t msgseq;            /* Sequence number */
+};
+
+/* Message representation with header and body */
+struct msg
+{
+  struct msg *next;            /* to link into fifo */
+
+  /* Message header */
+  struct apimsghdr hdr;
+
+  /* Message body */
+  struct stream *s;
+};
+
+/* Prototypes for generic messages. */
+extern struct msg *msg_new (u_char msgtype, void *msgbody,
+                    u_int32_t seqnum, u_int16_t msglen);
+extern struct msg *msg_dup (struct msg *msg);
+extern void msg_print (struct msg *msg);       /* XXX debug only */
+extern void msg_free (struct msg *msg);
+struct msg *msg_read (int fd);
+extern int msg_write (int fd, struct msg *msg);
+
+/* For requests, the message sequence number is between MIN_SEQ and
+   MAX_SEQ. For notifications, the sequence number is 0. */
+
+#define MIN_SEQ          1
+#define MAX_SEQ 2147483647
+
+extern void msg_set_seq (struct msg *msg, u_int32_t seqnr);
+extern u_int32_t msg_get_seq (struct msg *msg);
+
+/* -----------------------------------------------------------
+ * Message fifo queues
+ * -----------------------------------------------------------
+ */
+
+/* Message queue structure. */
+struct msg_fifo
+{
+  unsigned long count;
+
+  struct msg *head;
+  struct msg *tail;
+};
+
+/* Prototype for message fifo queues. */
+extern struct msg_fifo *msg_fifo_new (void);
+extern void msg_fifo_push (struct msg_fifo *, struct msg *msg);
+extern struct msg *msg_fifo_pop (struct msg_fifo *fifo);
+extern struct msg *msg_fifo_head (struct msg_fifo *fifo);
+extern void msg_fifo_flush (struct msg_fifo *fifo);
+extern void msg_fifo_free (struct msg_fifo *fifo);
+
+/* -----------------------------------------------------------
+ * Specific message type and format definitions
+ * -----------------------------------------------------------
+ */
+
+/* Messages to OSPF daemon. */
+#define MSG_REGISTER_OPAQUETYPE   1
+#define MSG_UNREGISTER_OPAQUETYPE 2
+#define MSG_REGISTER_EVENT        3
+#define MSG_SYNC_LSDB             4
+#define MSG_ORIGINATE_REQUEST     5
+#define MSG_DELETE_REQUEST        6
+
+/* Messages from OSPF daemon. */
+#define MSG_REPLY                10
+#define MSG_READY_NOTIFY         11
+#define MSG_LSA_UPDATE_NOTIFY    12
+#define MSG_LSA_DELETE_NOTIFY    13
+#define MSG_NEW_IF               14
+#define MSG_DEL_IF               15
+#define MSG_ISM_CHANGE           16
+#define MSG_NSM_CHANGE           17
+
+struct msg_register_opaque_type
+{
+  u_char lsatype;
+  u_char opaquetype;
+  u_char pad[2];               /* padding */
+};
+
+struct msg_unregister_opaque_type
+{
+  u_char lsatype;
+  u_char opaquetype;
+  u_char pad[2];               /* padding */
+};
+
+/* Power2 is needed to convert LSA types into bit positions,
+ * see typemask below. Type definition starts at 1, so
+ * Power2[0] is not used. */
+
+
+#ifdef ORIGINAL_CODING
+static const u_int16_t
+  Power2[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+  0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000
+};
+#else
+static const u_int16_t
+  Power2[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4),
+  (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9),
+  (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14),
+  (1 << 15)
+};
+#endif /* ORIGINAL_CODING */
+
+struct lsa_filter_type
+{
+  u_int16_t typemask;          /* bitmask for selecting LSA types (1..16) */
+  u_char origin;               /* selects according to origin. */
+#define NON_SELF_ORIGINATED    0
+#define        SELF_ORIGINATED  (OSPF_LSA_SELF)
+#define        ANY_ORIGIN 2
+
+  u_char num_areas;            /* number of areas in the filter. */
+  /* areas, if any, go here. */
+};
+
+struct msg_register_event
+{
+  struct lsa_filter_type filter;
+};
+
+struct msg_sync_lsdb
+{
+  struct lsa_filter_type filter;
+};
+
+struct msg_originate_request
+{
+  /* Used for LSA type 9 otherwise ignored */
+  struct in_addr ifaddr;
+
+  /* Used for LSA type 10 otherwise ignored */
+  struct in_addr area_id;
+
+  /* LSA header and LSA-specific part */
+  struct lsa_header data;
+};
+
+struct msg_delete_request
+{
+  struct in_addr area_id;      /* "0.0.0.0" for AS-external opaque LSAs */
+  u_char lsa_type;
+  u_char opaque_type;
+  u_char pad[2];               /* padding */
+  u_int32_t opaque_id;
+};
+
+struct msg_reply
+{
+  signed char errcode;
+#define OSPF_API_OK                         0
+#define OSPF_API_NOSUCHINTERFACE          (-1)
+#define OSPF_API_NOSUCHAREA               (-2)
+#define OSPF_API_NOSUCHLSA                (-3)
+#define OSPF_API_ILLEGALLSATYPE           (-4)
+#define OSPF_API_OPAQUETYPEINUSE          (-5)
+#define OSPF_API_OPAQUETYPENOTREGISTERED  (-6)
+#define OSPF_API_NOTREADY                 (-7)
+#define OSPF_API_NOMEMORY                 (-8)
+#define OSPF_API_ERROR                    (-9)
+#define OSPF_API_UNDEF                   (-10)
+  u_char pad[3];               /* padding to four byte alignment */
+};
+
+/* Message to tell client application that it ospf daemon is 
+ * ready to accept opaque LSAs for a given interface or area. */
+
+struct msg_ready_notify
+{
+  u_char lsa_type;
+  u_char opaque_type;
+  u_char pad[2];               /* padding */
+  struct in_addr addr;         /* interface address or area address */
+};
+
+/* These messages have a dynamic length depending on the embodied LSA.
+   They are aligned to four octets. msg_lsa_change_notify is used for
+   both LSA update and LSAs delete. */
+
+struct msg_lsa_change_notify
+{
+  /* Used for LSA type 9 otherwise ignored */
+  struct in_addr ifaddr;
+  /* Area ID. Not valid for AS-External and Opaque11 LSAs. */
+  struct in_addr area_id;
+  u_char is_self_originated;   /* 1 if self originated. */
+  u_char pad[3];
+  struct lsa_header data;
+};
+
+struct msg_new_if
+{
+  struct in_addr ifaddr;       /* interface IP address */
+  struct in_addr area_id;      /* area this interface belongs to */
+};
+
+struct msg_del_if
+{
+  struct in_addr ifaddr;       /* interface IP address */
+};
+
+struct msg_ism_change
+{
+  struct in_addr ifaddr;       /* interface IP address */
+  struct in_addr area_id;      /* area this interface belongs to */
+  u_char status;               /* interface status (up/down) */
+  u_char pad[3];               /* not used */
+};
+
+struct msg_nsm_change
+{
+  struct in_addr ifaddr;       /* attached interface */
+  struct in_addr nbraddr;      /* Neighbor interface address */
+  struct in_addr router_id;    /* Router ID of neighbor */
+  u_char status;               /* NSM status */
+  u_char pad[3];
+};
+
+/* We make use of a union to define a structure that covers all
+   possible API messages. This allows us to find out how much memory
+   needs to be reserved for the largest API message. */
+struct apimsg
+{
+  struct apimsghdr hdr;
+  union
+  {
+    struct msg_register_opaque_type register_opaque_type;
+    struct msg_register_event register_event;
+    struct msg_sync_lsdb sync_lsdb;
+    struct msg_originate_request originate_request;
+    struct msg_delete_request delete_request;
+    struct msg_reply reply;
+    struct msg_ready_notify ready_notify;
+    struct msg_new_if new_if;
+    struct msg_del_if del_if;
+    struct msg_ism_change ism_change;
+    struct msg_nsm_change nsm_change;
+    struct msg_lsa_change_notify lsa_change_notify;
+  }
+  u;
+};
+
+#define OSPF_API_MAX_MSG_SIZE (sizeof(struct apimsg) + OSPF_MAX_LSA_SIZE)
+
+/* -----------------------------------------------------------
+ * Prototypes for specific messages
+ * -----------------------------------------------------------
+ */
+
+/* For debugging only. */
+extern void api_opaque_lsa_print (struct lsa_header *data);
+
+/* Messages sent by client */
+extern struct msg *new_msg_register_opaque_type (u_int32_t seqnum,
+                                                u_char ltype, u_char otype);
+extern struct msg *new_msg_register_event (u_int32_t seqnum,
+                                          struct lsa_filter_type *filter);
+extern struct msg *new_msg_sync_lsdb (u_int32_t seqnum,
+                                     struct lsa_filter_type *filter);
+extern struct msg *new_msg_originate_request (u_int32_t seqnum,
+                                             struct in_addr ifaddr,
+                                             struct in_addr area_id,
+                                             struct lsa_header *data);
+extern struct msg *new_msg_delete_request (u_int32_t seqnum,
+                                          struct in_addr area_id,
+                                          u_char lsa_type,
+                                          u_char opaque_type,
+                                          u_int32_t opaque_id);
+
+/* Messages sent by OSPF daemon */
+extern struct msg *new_msg_reply (u_int32_t seqnum, u_char rc);
+
+extern struct msg *new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type,
+                                        u_char opaque_type,
+                                        struct in_addr addr);
+
+extern struct msg *new_msg_new_if (u_int32_t seqnr,
+                                  struct in_addr ifaddr,
+                                  struct in_addr area);
+
+extern struct msg *new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr);
+
+extern struct msg *new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr,
+                                      struct in_addr area, u_char status);
+
+extern struct msg *new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr,
+                                      struct in_addr nbraddr,
+                                      struct in_addr router_id,
+                                      u_char status);
+
+/* msgtype is MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY */
+extern struct msg *new_msg_lsa_change_notify (u_char msgtype,
+                                             u_int32_t seqnum,
+                                             struct in_addr ifaddr,
+                                             struct in_addr area_id,
+                                             u_char is_self_originated,
+                                             struct lsa_header *data);
+
+/* string printing functions */
+extern const char *ospf_api_errname (int errcode);
+extern const char *ospf_api_typename (int msgtype);
+
+#endif /* _OSPF_API_H */
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
new file mode 100644 (file)
index 0000000..aac8ef4
--- /dev/null
@@ -0,0 +1,2641 @@
+/*
+ * Server side of OSPF API.
+ * Copyright (C) 2001, 2002 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#ifdef SUPPORT_OSPF_API
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"         /* for inet_aton() */
+#include "buffer.h"
+
+#include <sys/types.h>
+
+#include "ospfd/ospfd.h"        /* for "struct thread_master" */
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+
+#include "ospfd/ospf_api.h"
+#include "ospfd/ospf_apiserver.h"
+
+/* This is an implementation of an API to the OSPF daemon that allows
+ * external applications to access the OSPF daemon through socket
+ * connections. The application can use this API to inject its own
+ * opaque LSAs and flood them to other OSPF daemons. Other OSPF
+ * daemons then receive these LSAs and inform applications through the
+ * API by sending a corresponding message. The application can also
+ * register to receive all LSA types (in addition to opaque types) and
+ * use this information to reconstruct the OSPF's LSDB. The OSPF
+ * daemon supports multiple applications concurrently.  */
+
+/* List of all active connections. */
+struct list *apiserver_list;
+
+/* -----------------------------------------------------------
+ * Functions to lookup interfaces
+ * -----------------------------------------------------------
+ */
+
+struct ospf_interface *
+ospf_apiserver_if_lookup_by_addr (struct in_addr address)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  struct ospf *ospf;
+
+  if (!(ospf = ospf_lookup ()))
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+      if (IPV4_ADDR_SAME (&address, &oi->address->u.prefix4))
+        return oi;
+
+  return NULL;
+}
+
+struct ospf_interface *
+ospf_apiserver_if_lookup_by_ifp (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  struct ospf *ospf;
+
+  if (!(ospf = ospf_lookup ()))
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    if (oi->ifp == ifp)
+      return oi;
+
+  return NULL;
+}
+
+/* -----------------------------------------------------------
+ * Initialization
+ * -----------------------------------------------------------
+ */
+
+unsigned short
+ospf_apiserver_getport (void)
+{
+  struct servent *sp = getservbyname ("ospfapi", "tcp");
+
+  return sp ? ntohs (sp->s_port) : OSPF_API_SYNC_PORT;
+}
+
+/* Initialize OSPF API module. Invoked from ospf_opaque_init() */
+int
+ospf_apiserver_init (void)
+{
+  int fd;
+  int rc = -1;
+
+  /* Create new socket for synchronous messages. */
+  fd = ospf_apiserver_serv_sock_family (ospf_apiserver_getport (), AF_INET);
+
+  if (fd < 0)
+    goto out;
+
+  /* Schedule new thread that handles accepted connections. */
+  ospf_apiserver_event (OSPF_APISERVER_ACCEPT, fd, NULL);
+
+  /* Initialize list that keeps track of all connections. */
+  apiserver_list = list_new ();
+
+  /* Register opaque-independent call back functions. These functions
+     are invoked on ISM, NSM changes and LSA update and LSA deletes */
+  rc =
+    ospf_register_opaque_functab (0 /* all LSAs */, 
+                                 0 /* all opaque types */,
+                                 ospf_apiserver_new_if,
+                                 ospf_apiserver_del_if,
+                                 ospf_apiserver_ism_change,
+                                 ospf_apiserver_nsm_change,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 NULL, /* ospf_apiserver_show_info */
+                                 NULL, /* originator_func */
+                                 NULL, /* ospf_apiserver_lsa_refresher */
+                                 ospf_apiserver_lsa_update,
+                                 ospf_apiserver_lsa_delete);
+  if (rc != 0)
+    {
+      zlog_warn ("ospf_apiserver_init: Failed to register opaque type [0/0]");
+    }
+
+  rc = 0;
+
+out:
+  return rc;
+}
+
+/* Terminate OSPF API module. */
+void
+ospf_apiserver_term (void)
+{
+  struct ospf_apiserver *apiserv;
+
+  /* Unregister wildcard [0/0] type */
+  ospf_delete_opaque_functab (0 /* all LSAs */, 
+                             0 /* all opaque types */);
+
+  /*
+   * Free all client instances.  ospf_apiserver_free removes the node
+   * from the list, so we examine the head of the list anew each time.
+   */
+  while ( apiserver_list &&
+         (apiserv = listgetdata (listhead (apiserver_list))) != NULL)
+    ospf_apiserver_free (apiserv);
+
+  /* Free client list itself */
+  if (apiserver_list)
+    list_delete (apiserver_list);
+
+  /* Free wildcard list */
+  /* XXX  */
+}
+
+static struct ospf_apiserver *
+lookup_apiserver (u_char lsa_type, u_char opaque_type)
+{
+  struct listnode *n1, *n2;
+  struct registered_opaque_type *r;
+  struct ospf_apiserver *apiserv, *found = NULL;
+
+  /* XXX: this approaches O(n**2) */
+  for (ALL_LIST_ELEMENTS_RO (apiserver_list, n1, apiserv))
+    {
+      for (ALL_LIST_ELEMENTS_RO (apiserv->opaque_types, n2, r))
+        if (r->lsa_type == lsa_type && r->opaque_type == opaque_type)
+          {
+            found = apiserv;
+            goto out;
+          }
+    }
+out:
+  return found;
+}
+
+static struct ospf_apiserver *
+lookup_apiserver_by_lsa (struct ospf_lsa *lsa)
+{
+  struct lsa_header *lsah = lsa->data;
+  struct ospf_apiserver *found = NULL;
+
+  if (IS_OPAQUE_LSA (lsah->type))
+    {
+      found = lookup_apiserver (lsah->type,
+                                GET_OPAQUE_TYPE (ntohl (lsah->id.s_addr)));
+    }
+  return found;
+}
+
+/* -----------------------------------------------------------
+ * Followings are functions to manage client connections.
+ * -----------------------------------------------------------
+ */
+static int
+ospf_apiserver_new_lsa_hook (struct ospf_lsa *lsa)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: Put LSA(%p)[%s] into reserve, total=%ld", (void *)lsa,
+                dump_lsa_key (lsa), lsa->lsdb->total);
+  return 0;
+}
+
+static int
+ospf_apiserver_del_lsa_hook (struct ospf_lsa *lsa)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: Get LSA(%p)[%s] from reserve, total=%ld", (void *)lsa,
+                dump_lsa_key (lsa), lsa->lsdb->total);
+  return 0;
+}
+
+/* Allocate new connection structure. */
+struct ospf_apiserver *
+ospf_apiserver_new (int fd_sync, int fd_async)
+{
+  struct ospf_apiserver *new =
+    XMALLOC (MTYPE_OSPF_APISERVER, sizeof (struct ospf_apiserver));
+
+  new->filter =
+    XMALLOC (MTYPE_OSPF_APISERVER_MSGFILTER, sizeof (struct lsa_filter_type));
+
+  new->fd_sync = fd_sync;
+  new->fd_async = fd_async;
+
+  /* list of registered opaque types that application uses */
+  new->opaque_types = list_new ();
+
+  /* Initialize temporary strage for LSA instances to be refreshed. */
+  memset (&new->reserve, 0, sizeof (struct ospf_lsdb));
+  ospf_lsdb_init (&new->reserve);
+
+  new->reserve.new_lsa_hook = ospf_apiserver_new_lsa_hook; /* debug */
+  new->reserve.del_lsa_hook = ospf_apiserver_del_lsa_hook; /* debug */
+
+  new->out_sync_fifo = msg_fifo_new ();
+  new->out_async_fifo = msg_fifo_new ();
+  new->t_sync_read = NULL;
+#ifdef USE_ASYNC_READ
+  new->t_async_read = NULL;
+#endif /* USE_ASYNC_READ */
+  new->t_sync_write = NULL;
+  new->t_async_write = NULL;
+
+  new->filter->typemask = 0;   /* filter all LSAs */
+  new->filter->origin = ANY_ORIGIN;
+  new->filter->num_areas = 0;
+
+  return new;
+}
+
+void
+ospf_apiserver_event (enum event event, int fd,
+                     struct ospf_apiserver *apiserv)
+{
+  switch (event)
+    {
+    case OSPF_APISERVER_ACCEPT:
+      (void)thread_add_read (master, ospf_apiserver_accept, apiserv, fd);
+      break;
+    case OSPF_APISERVER_SYNC_READ:
+      apiserv->t_sync_read =
+       thread_add_read (master, ospf_apiserver_read, apiserv, fd);
+      break;
+#ifdef USE_ASYNC_READ
+    case OSPF_APISERVER_ASYNC_READ:
+      apiserv->t_async_read =
+       thread_add_read (master, ospf_apiserver_read, apiserv, fd);
+      break;
+#endif /* USE_ASYNC_READ */
+    case OSPF_APISERVER_SYNC_WRITE:
+      if (!apiserv->t_sync_write)
+       {
+         apiserv->t_sync_write =
+           thread_add_write (master, ospf_apiserver_sync_write, apiserv, fd);
+       }
+      break;
+    case OSPF_APISERVER_ASYNC_WRITE:
+      if (!apiserv->t_async_write)
+       {
+         apiserv->t_async_write =
+           thread_add_write (master, ospf_apiserver_async_write, apiserv, fd);
+       }
+      break;
+    }
+}
+
+/* Free instance. First unregister all opaque types used by
+   application, flush opaque LSAs injected by application 
+   from network and close connection. */
+void
+ospf_apiserver_free (struct ospf_apiserver *apiserv)
+{
+  struct listnode *node;
+
+  /* Cancel read and write threads. */
+  if (apiserv->t_sync_read)
+    {
+      thread_cancel (apiserv->t_sync_read);
+    }
+#ifdef USE_ASYNC_READ
+  if (apiserv->t_async_read)
+    {
+      thread_cancel (apiserv->t_async_read);
+    }
+#endif /* USE_ASYNC_READ */
+  if (apiserv->t_sync_write)
+    {
+      thread_cancel (apiserv->t_sync_write);
+    }
+
+  if (apiserv->t_async_write)
+    {
+      thread_cancel (apiserv->t_async_write);
+    }
+
+  /* Unregister all opaque types that application registered 
+     and flush opaque LSAs if still in LSDB. */
+
+  while ((node = listhead (apiserv->opaque_types)) != NULL)
+    {
+      struct registered_opaque_type *regtype = listgetdata(node);
+
+      ospf_apiserver_unregister_opaque_type (apiserv, regtype->lsa_type,
+                                            regtype->opaque_type);
+
+    }
+
+  /* Close connections to OSPFd. */
+  if (apiserv->fd_sync > 0)
+    {
+      close (apiserv->fd_sync);
+    }
+
+  if (apiserv->fd_async > 0)
+    {
+      close (apiserv->fd_async);
+    }
+
+  /* Free fifos */
+  msg_fifo_free (apiserv->out_sync_fifo);
+  msg_fifo_free (apiserv->out_async_fifo);
+
+  /* Clear temporary strage for LSA instances to be refreshed. */
+  ospf_lsdb_delete_all (&apiserv->reserve);
+  ospf_lsdb_cleanup (&apiserv->reserve);
+
+  /* Remove from the list of active clients. */
+  listnode_delete (apiserver_list, apiserv);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: Delete apiserv(%p), total#(%d)",
+                (void *)apiserv, apiserver_list->count);
+
+  /* And free instance. */
+  XFREE (MTYPE_OSPF_APISERVER, apiserv);
+}
+
+int
+ospf_apiserver_read (struct thread *thread)
+{
+  struct ospf_apiserver *apiserv;
+  struct msg *msg;
+  int fd;
+  int rc = -1;
+  enum event event;
+
+  apiserv = THREAD_ARG (thread);
+  fd = THREAD_FD (thread);
+
+  if (fd == apiserv->fd_sync)
+    {
+      event = OSPF_APISERVER_SYNC_READ;
+      apiserv->t_sync_read = NULL;
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("API: ospf_apiserver_read: Peer: %s/%u",
+                    inet_ntoa (apiserv->peer_sync.sin_addr),
+                    ntohs (apiserv->peer_sync.sin_port));
+    }
+#ifdef USE_ASYNC_READ
+  else if (fd == apiserv->fd_async)
+    {
+      event = OSPF_APISERVER_ASYNC_READ;
+      apiserv->t_async_read = NULL;
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("API: ospf_apiserver_read: Peer: %s/%u",
+                    inet_ntoa (apiserv->peer_async.sin_addr),
+                    ntohs (apiserv->peer_async.sin_port));
+    }
+#endif /* USE_ASYNC_READ */
+  else
+    {
+      zlog_warn ("ospf_apiserver_read: Unknown fd(%d)", fd);
+      ospf_apiserver_free (apiserv);
+      goto out;
+    }
+
+  /* Read message from fd. */
+  msg = msg_read (fd);
+  if (msg == NULL)
+    {
+      zlog_warn
+       ("ospf_apiserver_read: read failed on fd=%d, closing connection", fd);
+
+      /* Perform cleanup. */
+      ospf_apiserver_free (apiserv);
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    msg_print (msg);
+
+  /* Dispatch to corresponding message handler. */
+  rc = ospf_apiserver_handle_msg (apiserv, msg);
+
+  /* Prepare for next message, add read thread. */
+  ospf_apiserver_event (event, fd, apiserv);
+
+  msg_free (msg);
+
+out:
+  return rc;
+}
+
+int
+ospf_apiserver_sync_write (struct thread *thread)
+{
+  struct ospf_apiserver *apiserv;
+  struct msg *msg;
+  int fd;
+  int rc = -1;
+
+  apiserv = THREAD_ARG (thread);
+  assert (apiserv);
+  fd = THREAD_FD (thread);
+
+  apiserv->t_sync_write = NULL;
+
+  /* Sanity check */
+  if (fd != apiserv->fd_sync)
+    {
+      zlog_warn ("ospf_apiserver_sync_write: Unknown fd=%d", fd);
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: ospf_apiserver_sync_write: Peer: %s/%u",
+                inet_ntoa (apiserv->peer_sync.sin_addr),
+                ntohs (apiserv->peer_sync.sin_port));
+
+  /* Check whether there is really a message in the fifo. */
+  msg = msg_fifo_pop (apiserv->out_sync_fifo);
+  if (!msg)
+    {
+      zlog_warn ("API: ospf_apiserver_sync_write: No message in Sync-FIFO?");
+      return 0;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    msg_print (msg);
+
+  rc = msg_write (fd, msg);
+
+  /* Once a message is dequeued, it should be freed anyway. */
+  msg_free (msg);
+
+  if (rc < 0)
+    {
+      zlog_warn
+        ("ospf_apiserver_sync_write: write failed on fd=%d", fd);
+      goto out;
+    }
+
+
+  /* If more messages are in sync message fifo, schedule write thread. */
+  if (msg_fifo_head (apiserv->out_sync_fifo))
+    {
+      ospf_apiserver_event (OSPF_APISERVER_SYNC_WRITE, apiserv->fd_sync,
+                            apiserv);
+    }
+  
+ out:
+
+  if (rc < 0)
+  {
+      /* Perform cleanup and disconnect with peer */
+      ospf_apiserver_free (apiserv);
+    }
+
+  return rc;
+}
+
+
+int
+ospf_apiserver_async_write (struct thread *thread)
+{
+  struct ospf_apiserver *apiserv;
+  struct msg *msg;
+  int fd;
+  int rc = -1;
+
+  apiserv = THREAD_ARG (thread);
+  assert (apiserv);
+  fd = THREAD_FD (thread);
+
+  apiserv->t_async_write = NULL;
+
+  /* Sanity check */
+  if (fd != apiserv->fd_async)
+    {
+      zlog_warn ("ospf_apiserver_async_write: Unknown fd=%d", fd);
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: ospf_apiserver_async_write: Peer: %s/%u",
+                inet_ntoa (apiserv->peer_async.sin_addr),
+                ntohs (apiserv->peer_async.sin_port));
+
+  /* Check whether there is really a message in the fifo. */
+  msg = msg_fifo_pop (apiserv->out_async_fifo);
+  if (!msg)
+    {
+      zlog_warn ("API: ospf_apiserver_async_write: No message in Async-FIFO?");
+      return 0;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    msg_print (msg);
+
+  rc = msg_write (fd, msg);
+
+  /* Once a message is dequeued, it should be freed anyway. */
+  msg_free (msg);
+
+  if (rc < 0)
+    {
+      zlog_warn
+        ("ospf_apiserver_async_write: write failed on fd=%d", fd);
+      goto out;
+    }
+
+
+  /* If more messages are in async message fifo, schedule write thread. */
+  if (msg_fifo_head (apiserv->out_async_fifo))
+    {
+      ospf_apiserver_event (OSPF_APISERVER_ASYNC_WRITE, apiserv->fd_async,
+                            apiserv);
+    }
+
+ out:
+
+  if (rc < 0)
+    {
+      /* Perform cleanup and disconnect with peer */
+      ospf_apiserver_free (apiserv);
+    }
+
+  return rc;
+}
+
+
+int
+ospf_apiserver_serv_sock_family (unsigned short port, int family)
+{
+  union sockunion su;
+  int accept_sock;
+  int rc;
+
+  memset (&su, 0, sizeof (union sockunion));
+  su.sa.sa_family = family;
+
+  /* Make new socket */
+  accept_sock = sockunion_stream_socket (&su);
+  if (accept_sock < 0)
+    return accept_sock;
+
+  /* This is a server, so reuse address and port */
+  sockopt_reuseaddr (accept_sock);
+  sockopt_reuseport (accept_sock);
+
+  /* Bind socket to address and given port. */
+  rc = sockunion_bind (accept_sock, &su, port, NULL);
+  if (rc < 0)
+    {
+      close (accept_sock);     /* Close socket */
+      return rc;
+    }
+
+  /* Listen socket under queue length 3. */
+  rc = listen (accept_sock, 3);
+  if (rc < 0)
+    {
+      zlog_warn ("ospf_apiserver_serv_sock_family: listen: %s",
+                 safe_strerror (errno));
+      close (accept_sock);     /* Close socket */
+      return rc;
+    }
+  return accept_sock;
+}
+
+
+/* Accept connection request from external applications. For each
+   accepted connection allocate own connection instance. */
+int
+ospf_apiserver_accept (struct thread *thread)
+{
+  int accept_sock;
+  int new_sync_sock;
+  int new_async_sock;
+  union sockunion su;
+  struct ospf_apiserver *apiserv;
+  struct sockaddr_in peer_async;
+  struct sockaddr_in peer_sync;
+  unsigned int peerlen;
+  int ret;
+
+  /* THREAD_ARG (thread) is NULL */
+  accept_sock = THREAD_FD (thread);
+
+  /* Keep hearing on socket for further connections. */
+  ospf_apiserver_event (OSPF_APISERVER_ACCEPT, accept_sock, NULL);
+
+  memset (&su, 0, sizeof (union sockunion));
+  /* Accept connection for synchronous messages */
+  new_sync_sock = sockunion_accept (accept_sock, &su);
+  if (new_sync_sock < 0)
+    {
+      zlog_warn ("ospf_apiserver_accept: accept: %s", safe_strerror (errno));
+      return -1;
+    }
+
+  /* Get port address and port number of peer to make reverse connection.
+     The reverse channel uses the port number of the peer port+1. */
+
+  memset(&peer_sync, 0, sizeof(struct sockaddr_in));
+  peerlen = sizeof (struct sockaddr_in);
+
+  ret = getpeername (new_sync_sock, (struct sockaddr *)&peer_sync, &peerlen);
+  if (ret < 0)
+    {
+      zlog_warn ("ospf_apiserver_accept: getpeername: %s", safe_strerror (errno));
+      close (new_sync_sock);
+      return -1;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: ospf_apiserver_accept: New peer: %s/%u",
+               inet_ntoa (peer_sync.sin_addr), ntohs (peer_sync.sin_port));
+
+  /* Create new socket for asynchronous messages. */
+  peer_async = peer_sync;
+  peer_async.sin_port = htons(ntohs(peer_sync.sin_port) + 1);
+
+  /* Check if remote port number to make reverse connection is valid one. */
+  if (ntohs (peer_async.sin_port) == ospf_apiserver_getport ())
+    {
+      zlog_warn ("API: ospf_apiserver_accept: Peer(%s/%u): Invalid async port number?",
+               inet_ntoa (peer_async.sin_addr), ntohs (peer_async.sin_port));
+      close (new_sync_sock);
+      return -1;
+    }
+
+  new_async_sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (new_async_sock < 0)
+    {
+      zlog_warn ("ospf_apiserver_accept: socket: %s", safe_strerror (errno));
+      close (new_sync_sock);
+      return -1;
+    }
+
+  ret = connect (new_async_sock, (struct sockaddr *) &peer_async,
+                sizeof (struct sockaddr_in));
+
+  if (ret < 0)
+    {
+      zlog_warn ("ospf_apiserver_accept: connect: %s", safe_strerror (errno));
+      close (new_sync_sock);
+      close (new_async_sock);
+      return -1;
+    }
+
+#ifdef USE_ASYNC_READ
+#else /* USE_ASYNC_READ */
+  /* Make the asynchronous channel write-only. */
+  ret = shutdown (new_async_sock, SHUT_RD);
+  if (ret < 0)
+    {
+      zlog_warn ("ospf_apiserver_accept: shutdown: %s", safe_strerror (errno));
+      close (new_sync_sock);
+      close (new_async_sock);
+      return -1;
+    }
+#endif /* USE_ASYNC_READ */
+
+  /* Allocate new server-side connection structure */
+  apiserv = ospf_apiserver_new (new_sync_sock, new_async_sock);
+
+  /* Add to active connection list */
+  listnode_add (apiserver_list, apiserv);
+  apiserv->peer_sync = peer_sync;
+  apiserv->peer_async = peer_async;
+
+  /* And add read threads for new connection */
+  ospf_apiserver_event (OSPF_APISERVER_SYNC_READ, new_sync_sock, apiserv);
+#ifdef USE_ASYNC_READ
+  ospf_apiserver_event (OSPF_APISERVER_ASYNC_READ, new_async_sock, apiserv);
+#endif /* USE_ASYNC_READ */
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: New apiserv(%p), total#(%d)",
+                (void *)apiserv, apiserver_list->count);
+
+  return 0;
+}
+
+
+/* -----------------------------------------------------------
+ * Send reply with return code to client application
+ * -----------------------------------------------------------
+ */
+
+static int
+ospf_apiserver_send_msg (struct ospf_apiserver *apiserv, struct msg *msg)
+{
+  struct msg_fifo *fifo;
+  struct msg *msg2;
+  enum event event;
+  int fd;
+
+  switch (msg->hdr.msgtype)
+    {
+    case MSG_REPLY:
+      fifo = apiserv->out_sync_fifo;
+      fd = apiserv->fd_sync;
+      event = OSPF_APISERVER_SYNC_WRITE;
+      break;
+    case MSG_READY_NOTIFY:
+    case MSG_LSA_UPDATE_NOTIFY:
+    case MSG_LSA_DELETE_NOTIFY:
+    case MSG_NEW_IF:
+    case MSG_DEL_IF:
+    case MSG_ISM_CHANGE:
+    case MSG_NSM_CHANGE:
+      fifo = apiserv->out_async_fifo;
+      fd = apiserv->fd_async;
+      event = OSPF_APISERVER_ASYNC_WRITE;
+      break;
+    default:
+      zlog_warn ("ospf_apiserver_send_msg: Unknown message type %d",
+                msg->hdr.msgtype);
+      return -1;
+    }
+
+  /* Make a copy of the message and put in the fifo. Once the fifo
+     gets drained by the write thread, the message will be freed. */
+  /* NB: Given "msg" is untouched in this function. */
+  msg2 = msg_dup (msg);
+
+  /* Enqueue message into corresponding fifo queue */
+  msg_fifo_push (fifo, msg2);
+
+  /* Schedule write thread */
+  ospf_apiserver_event (event, fd, apiserv);
+  return 0;
+}
+
+int
+ospf_apiserver_send_reply (struct ospf_apiserver *apiserv, u_int32_t seqnr,
+                          u_char rc)
+{
+  struct msg *msg = new_msg_reply (seqnr, rc);
+  int ret;
+
+  if (!msg)
+    {
+      zlog_warn ("ospf_apiserver_send_reply: msg_new failed");
+#ifdef NOTYET
+      /* Cannot allocate new message. What should we do? */
+      ospf_apiserver_free (apiserv);
+#endif
+      return -1;
+    }
+
+  ret = ospf_apiserver_send_msg (apiserv, msg);
+  msg_free (msg);
+  return ret;
+}
+
+
+/* -----------------------------------------------------------
+ * Generic message dispatching handler function
+ * -----------------------------------------------------------
+ */
+
+int
+ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv, struct msg *msg)
+{
+  int rc;
+
+  /* Call corresponding message handler function. */
+  switch (msg->hdr.msgtype)
+    {
+    case MSG_REGISTER_OPAQUETYPE:
+      rc = ospf_apiserver_handle_register_opaque_type (apiserv, msg);
+      break;
+    case MSG_UNREGISTER_OPAQUETYPE:
+      rc = ospf_apiserver_handle_unregister_opaque_type (apiserv, msg);
+      break;
+    case MSG_REGISTER_EVENT:
+      rc = ospf_apiserver_handle_register_event (apiserv, msg);
+      break;
+    case MSG_SYNC_LSDB:
+      rc = ospf_apiserver_handle_sync_lsdb (apiserv, msg);
+      break;
+    case MSG_ORIGINATE_REQUEST:
+      rc = ospf_apiserver_handle_originate_request (apiserv, msg);
+      break;
+    case MSG_DELETE_REQUEST:
+      rc = ospf_apiserver_handle_delete_request (apiserv, msg);
+      break;
+    default:
+      zlog_warn ("ospf_apiserver_handle_msg: Unknown message type: %d",
+                msg->hdr.msgtype);
+      rc = -1;
+    }
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Following are functions for opaque type registration
+ * -----------------------------------------------------------
+ */
+
+int
+ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv,
+                                    u_char lsa_type, u_char opaque_type)
+{
+  struct registered_opaque_type *regtype;
+  int (*originator_func) (void *arg);
+  int rc;
+
+  switch (lsa_type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      originator_func = ospf_apiserver_lsa9_originator;
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      originator_func = ospf_apiserver_lsa10_originator;
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      originator_func = ospf_apiserver_lsa11_originator;
+      break;
+    default:
+      zlog_warn ("ospf_apiserver_register_opaque_type: lsa_type(%d)",
+                lsa_type);
+      return OSPF_API_ILLEGALLSATYPE;
+    }
+  
+
+  /* Register opaque function table */
+  /* NB: Duplicated registration will be detected inside the function. */
+  rc =
+    ospf_register_opaque_functab (lsa_type, opaque_type,
+                                 NULL, /* ospf_apiserver_new_if */
+                                 NULL, /* ospf_apiserver_del_if */
+                                 NULL, /* ospf_apiserver_ism_change */
+                                 NULL, /* ospf_apiserver_nsm_change */
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 ospf_apiserver_show_info,
+                                 originator_func,
+                                 ospf_apiserver_lsa_refresher,
+                                 NULL, /* ospf_apiserver_lsa_update */
+                                 NULL /* ospf_apiserver_lsa_delete */);
+
+  if (rc != 0)
+    {
+      zlog_warn ("Failed to register opaque type [%d/%d]",
+                lsa_type, opaque_type);
+      return OSPF_API_OPAQUETYPEINUSE;
+    }
+
+  /* Remember the opaque type that application registers so when
+     connection shuts down, we can flush all LSAs of this opaque
+     type. */
+
+  regtype =
+    XCALLOC (MTYPE_OSPF_APISERVER, sizeof (struct registered_opaque_type));
+  regtype->lsa_type = lsa_type;
+  regtype->opaque_type = opaque_type;
+
+  /* Add to list of registered opaque types */
+  listnode_add (apiserv->opaque_types, regtype);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("API: Add LSA-type(%d)/Opaque-type(%d) into"
+               " apiserv(%p), total#(%d)", 
+               lsa_type, opaque_type, (void *)apiserv, 
+               listcount (apiserv->opaque_types));
+
+  return 0;
+}
+
+int
+ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserv,
+                                      u_char lsa_type, u_char opaque_type)
+{
+  struct listnode *node, *nnode;
+  struct registered_opaque_type *regtype;
+
+  for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, regtype))
+    {
+      /* Check if we really registered this opaque type */
+      if (regtype->lsa_type == lsa_type &&
+         regtype->opaque_type == opaque_type)
+       {
+
+         /* Yes, we registered this opaque type. Flush
+            all existing opaque LSAs of this type */
+
+         ospf_apiserver_flush_opaque_lsa (apiserv, lsa_type, opaque_type);
+         ospf_delete_opaque_functab (lsa_type, opaque_type);
+
+         /* Remove from list of registered opaque types */
+         listnode_delete (apiserv->opaque_types, regtype);
+
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("API: Del LSA-type(%d)/Opaque-type(%d)"
+                       " from apiserv(%p), total#(%d)", 
+                       lsa_type, opaque_type, (void *)apiserv,
+                       listcount (apiserv->opaque_types));
+
+         return 0;
+       }
+    }
+
+  /* Opaque type is not registered */
+  zlog_warn ("Failed to unregister opaque type [%d/%d]",
+            lsa_type, opaque_type);
+  return OSPF_API_OPAQUETYPENOTREGISTERED;
+}
+
+
+static int
+apiserver_is_opaque_type_registered (struct ospf_apiserver *apiserv,
+                                    u_char lsa_type, u_char opaque_type)
+{
+  struct listnode *node, *nnode;
+  struct registered_opaque_type *regtype;
+
+  /* XXX: how many types are there? if few, why not just a bitmap? */
+  for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, regtype))
+    {
+      /* Check if we really registered this opaque type */
+      if (regtype->lsa_type == lsa_type &&
+         regtype->opaque_type == opaque_type)
+       {
+         /* Yes registered */
+         return 1;
+       }
+    }
+  /* Not registered */
+  return 0;
+}
+
+int
+ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver *apiserv,
+                                           struct msg *msg)
+{
+  struct msg_register_opaque_type *rmsg;
+  u_char lsa_type;
+  u_char opaque_type;
+  int rc = 0;
+
+  /* Extract parameters from register opaque type message */
+  rmsg = (struct msg_register_opaque_type *) STREAM_DATA (msg->s);
+
+  lsa_type = rmsg->lsatype;
+  opaque_type = rmsg->opaquetype;
+
+  rc = ospf_apiserver_register_opaque_type (apiserv, lsa_type, opaque_type);
+
+  /* Send a reply back to client including return code */
+  rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc);
+  if (rc < 0)
+    goto out;
+
+  /* Now inform application about opaque types that are ready */
+  switch (lsa_type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      ospf_apiserver_notify_ready_type9 (apiserv);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      ospf_apiserver_notify_ready_type10 (apiserv);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      ospf_apiserver_notify_ready_type11 (apiserv);
+      break;
+    }
+out:
+  return rc;
+}
+
+
+/* Notify specific client about all opaque types 9 that are ready. */
+void
+ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv)
+{
+  struct listnode *node, *nnode;
+  struct listnode *node2, *nnode2;
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+  struct registered_opaque_type *r;
+
+  ospf = ospf_lookup ();
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    {
+      /* Check if this interface is indeed ready for type 9 */
+      if (!ospf_apiserver_is_ready_type9 (oi))
+       continue;
+
+      /* Check for registered opaque type 9 types */
+      /* XXX: loop-de-loop - optimise me */
+      for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
+       {
+         struct msg *msg;
+
+         if (r->lsa_type == OSPF_OPAQUE_LINK_LSA)
+           {
+
+             /* Yes, this opaque type is ready */
+             msg = new_msg_ready_notify (0, OSPF_OPAQUE_LINK_LSA,
+                                         r->opaque_type,
+                                         oi->address->u.prefix4);
+             if (!msg)
+               {
+                 zlog_warn ("apiserver_notify_ready_type9: msg_new failed");
+#ifdef NOTYET
+                 /* Cannot allocate new message. What should we do? */
+                 ospf_apiserver_free (apiserv);
+#endif
+                 goto out;
+               }
+             ospf_apiserver_send_msg (apiserv, msg);
+             msg_free (msg);
+           }
+       }
+    }
+
+out:
+  return;
+}
+
+
+/* Notify specific client about all opaque types 10 that are ready. */
+void
+ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv)
+{
+  struct listnode *node, *nnode;
+  struct listnode *node2, *nnode2;
+  struct ospf *ospf;
+  struct ospf_area *area;
+  
+  ospf = ospf_lookup ();
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      struct registered_opaque_type *r;
+      
+      if (!ospf_apiserver_is_ready_type10 (area))
+       {
+         continue;
+       }
+
+      /* Check for registered opaque type 10 types */
+      /* XXX: loop in loop - optimise me */
+      for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
+       {
+         struct msg *msg;
+         
+         if (r->lsa_type == OSPF_OPAQUE_AREA_LSA)
+           {
+             /* Yes, this opaque type is ready */
+             msg =
+               new_msg_ready_notify (0, OSPF_OPAQUE_AREA_LSA,
+                                     r->opaque_type, area->area_id);
+             if (!msg)
+               {
+                 zlog_warn ("apiserver_notify_ready_type10: msg_new failed");
+#ifdef NOTYET
+                 /* Cannot allocate new message. What should we do? */
+                 ospf_apiserver_free (apiserv);
+#endif
+                 goto out;
+               }
+             ospf_apiserver_send_msg (apiserv, msg);
+             msg_free (msg);
+           }
+       }
+    }
+
+out:
+  return;
+}
+
+/* Notify specific client about all opaque types 11 that are ready */
+void
+ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv)
+{
+  struct listnode *node, *nnode;
+  struct ospf *ospf;
+  struct registered_opaque_type *r;
+
+  ospf = ospf_lookup ();
+
+  /* Can type 11 be originated? */
+  if (!ospf_apiserver_is_ready_type11 (ospf))
+    goto out;
+
+  /* Check for registered opaque type 11 types */
+  for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, r))
+    {
+      struct msg *msg;
+      struct in_addr noarea_id = { .s_addr = 0L };
+      
+      if (r->lsa_type == OSPF_OPAQUE_AS_LSA)
+       {
+         /* Yes, this opaque type is ready */
+         msg = new_msg_ready_notify (0, OSPF_OPAQUE_AS_LSA,
+                                     r->opaque_type, noarea_id);
+
+         if (!msg)
+           {
+             zlog_warn ("apiserver_notify_ready_type11: msg_new failed");
+#ifdef NOTYET
+             /* Cannot allocate new message. What should we do? */
+             ospf_apiserver_free (apiserv);
+#endif
+             goto out;
+           }
+         ospf_apiserver_send_msg (apiserv, msg);
+         msg_free (msg);
+       }
+    }
+
+out:
+  return;
+}
+
+int
+ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver *apiserv,
+                                             struct msg *msg)
+{
+  struct msg_unregister_opaque_type *umsg;
+  u_char ltype;
+  u_char otype;
+  int rc = 0;
+
+  /* Extract parameters from unregister opaque type message */
+  umsg = (struct msg_unregister_opaque_type *) STREAM_DATA (msg->s);
+
+  ltype = umsg->lsatype;
+  otype = umsg->opaquetype;
+
+  rc = ospf_apiserver_unregister_opaque_type (apiserv, ltype, otype);
+
+  /* Send a reply back to client including return code */
+  rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc);
+
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Following are functions for event (filter) registration.
+ * -----------------------------------------------------------
+ */
+int
+ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv,
+                                     struct msg *msg)
+{
+  struct msg_register_event *rmsg;
+  int rc;
+  u_int32_t seqnum;
+
+  rmsg = (struct msg_register_event *) STREAM_DATA (msg->s);
+
+  /* Get request sequence number */
+  seqnum = msg_get_seq (msg);
+
+  /* Free existing filter in apiserv. */
+  XFREE (MTYPE_OSPF_APISERVER_MSGFILTER, apiserv->filter);
+  /* Alloc new space for filter. */
+
+  apiserv->filter = XMALLOC (MTYPE_OSPF_APISERVER_MSGFILTER,
+                            ntohs (msg->hdr.msglen));
+  if (apiserv->filter)
+    {
+      /* copy it over. */
+      memcpy (apiserv->filter, &rmsg->filter, ntohs (msg->hdr.msglen));
+      rc = OSPF_API_OK;
+    }
+  else
+    {
+      rc = OSPF_API_NOMEMORY;
+    }
+  /* Send a reply back to client with return code */
+  rc = ospf_apiserver_send_reply (apiserv, seqnum, rc);
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Followings are functions for LSDB synchronization.
+ * -----------------------------------------------------------
+ */
+
+static int
+apiserver_sync_callback (struct ospf_lsa *lsa, void *p_arg, int int_arg)
+{
+  struct ospf_apiserver *apiserv;
+  int seqnum;
+  struct msg *msg;
+  struct param_t
+  {
+    struct ospf_apiserver *apiserv;
+    struct lsa_filter_type *filter;
+  }
+   *param;
+  int rc = -1;
+
+  /* Sanity check */
+  assert (lsa->data);
+  assert (p_arg);
+
+  param = (struct param_t *) p_arg;
+  apiserv = param->apiserv;
+  seqnum = (u_int32_t) int_arg;
+
+  /* Check origin in filter. */
+  if ((param->filter->origin == ANY_ORIGIN) ||
+      (param->filter->origin == (lsa->flags & OSPF_LSA_SELF)))
+    {
+
+      /* Default area for AS-External and Opaque11 LSAs */
+      struct in_addr area_id = { .s_addr = 0L };
+
+      /* Default interface for non Opaque9 LSAs */
+      struct in_addr ifaddr = { .s_addr = 0L };
+      
+      if (lsa->area)
+       {
+         area_id = lsa->area->area_id;
+       }
+      if (lsa->data->type == OSPF_OPAQUE_LINK_LSA)
+       {
+         ifaddr = lsa->oi->address->u.prefix4;
+       }
+
+      msg = new_msg_lsa_change_notify (MSG_LSA_UPDATE_NOTIFY,
+                                      seqnum,
+                                      ifaddr, area_id,
+                                      lsa->flags & OSPF_LSA_SELF, lsa->data);
+      if (!msg)
+       {
+         zlog_warn ("apiserver_sync_callback: new_msg_update failed");
+#ifdef NOTYET
+         /* Cannot allocate new message. What should we do? */
+/*        ospf_apiserver_free (apiserv);*//* Do nothing here XXX */
+#endif
+         goto out;
+       }
+
+      /* Send LSA */
+      ospf_apiserver_send_msg (apiserv, msg);
+      msg_free (msg);
+    }
+  rc = 0;
+
+out:
+  return rc;
+}
+
+int
+ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv,
+                                struct msg *msg)
+{
+  struct listnode *node, *nnode;
+  u_int32_t seqnum;
+  int rc = 0;
+  struct msg_sync_lsdb *smsg;
+  struct ospf_apiserver_param_t
+  {
+    struct ospf_apiserver *apiserv;
+    struct lsa_filter_type *filter;
+  } param;
+  u_int16_t mask;
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+  struct ospf *ospf;
+  struct ospf_area *area;
+
+  ospf = ospf_lookup ();
+
+  /* Get request sequence number */
+  seqnum = msg_get_seq (msg);
+  /* Set sync msg. */
+  smsg = (struct msg_sync_lsdb *) STREAM_DATA (msg->s);
+
+  /* Set parameter struct. */
+  param.apiserv = apiserv;
+  param.filter = &smsg->filter;
+
+  /* Remember mask. */
+  mask = ntohs (smsg->filter.typemask);
+
+  /* Iterate over all areas. */
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      int i;
+      u_int32_t *area_id = NULL;
+
+      /* Compare area_id with area_ids in sync request. */
+      if ((i = smsg->filter.num_areas) > 0)
+       {
+         /* Let area_id point to the list of area IDs,
+          * which is at the end of smsg->filter. */
+         area_id = (u_int32_t *) (&smsg->filter + 1);
+         while (i)
+           {
+             if (*area_id == area->area_id.s_addr)
+               {
+                 break;
+               }
+             i--;
+             area_id++;
+           }
+       }
+      else
+       {
+         i = 1;
+       }
+
+      /* If area was found, then i>0 here. */
+      if (i)
+       {
+         /* Check msg type. */
+         if (mask & Power2[OSPF_ROUTER_LSA])
+           LSDB_LOOP (ROUTER_LSDB (area), rn, lsa)
+             apiserver_sync_callback(lsa, (void *) &param, seqnum);
+         if (mask & Power2[OSPF_NETWORK_LSA])
+            LSDB_LOOP (NETWORK_LSDB (area), rn, lsa)
+              apiserver_sync_callback(lsa, (void *) &param, seqnum);
+         if (mask & Power2[OSPF_SUMMARY_LSA])
+            LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+              apiserver_sync_callback(lsa, (void *) &param, seqnum);
+         if (mask & Power2[OSPF_ASBR_SUMMARY_LSA])
+            LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+              apiserver_sync_callback(lsa, (void *) &param, seqnum);
+         if (mask & Power2[OSPF_OPAQUE_LINK_LSA])
+            LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+              apiserver_sync_callback(lsa, (void *) &param, seqnum);
+         if (mask & Power2[OSPF_OPAQUE_AREA_LSA])
+            LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+              apiserver_sync_callback(lsa, (void *) &param, seqnum);
+       }
+    }
+
+  /* For AS-external LSAs */
+  if (ospf->lsdb)
+    {
+      if (mask & Power2[OSPF_AS_EXTERNAL_LSA])
+       LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+         apiserver_sync_callback(lsa, (void *) &param, seqnum);
+    }
+
+  /* For AS-external opaque LSAs */
+  if (ospf->lsdb)
+    {
+      if (mask & Power2[OSPF_OPAQUE_AS_LSA])
+       LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa)
+         apiserver_sync_callback(lsa, (void *) &param, seqnum);
+    }
+
+  /* Send a reply back to client with return code */
+  rc = ospf_apiserver_send_reply (apiserv, seqnum, rc);
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Followings are functions to originate or update LSA
+ * from an application.
+ * -----------------------------------------------------------
+ */
+
+/* Create a new internal opaque LSA by taking prototype and filling in
+   missing fields such as age, sequence number, advertising router,
+   checksum and so on. The interface parameter is used for type 9
+   LSAs, area parameter for type 10. Type 11 LSAs do neither need area
+   nor interface. */
+
+struct ospf_lsa *
+ospf_apiserver_opaque_lsa_new (struct ospf_area *area,
+                              struct ospf_interface *oi,
+                              struct lsa_header *protolsa)
+{
+  struct stream *s;
+  struct lsa_header *newlsa;
+  struct ospf_lsa *new = NULL;
+  u_char options = 0x0;
+  u_int16_t length;
+
+  struct ospf *ospf;
+
+  ospf = ospf_lookup();
+  assert(ospf);
+
+  /* Create a stream for internal opaque LSA */
+  if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL)
+    {
+      zlog_warn ("ospf_apiserver_opaque_lsa_new: stream_new failed");
+      return NULL;
+    }
+
+  newlsa = (struct lsa_header *) STREAM_DATA (s);
+
+  /* XXX If this is a link-local LSA or an AS-external LSA, how do we
+     have to set options? */
+
+  if (area)
+    {
+      options = LSA_OPTIONS_GET (area);
+      options |= LSA_OPTIONS_NSSA_GET (area);
+    }
+
+  options |= OSPF_OPTION_O;    /* Don't forget to set option bit */
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Creating an Opaque-LSA instance",
+                protolsa->type, inet_ntoa (protolsa->id));
+    }
+
+  /* Set opaque-LSA header fields. */
+  lsa_header_set (s, options, protolsa->type, protolsa->id, 
+                  ospf->router_id);
+
+  /* Set opaque-LSA body fields. */
+  stream_put (s, ((u_char *) protolsa) + sizeof (struct lsa_header),
+             ntohs (protolsa->length) - sizeof (struct lsa_header));
+
+  /* Determine length of LSA. */
+  length = stream_get_endp (s);
+  newlsa->length = htons (length);
+
+  /* Create OSPF LSA. */
+  if ((new = ospf_lsa_new ()) == NULL)
+    {
+      zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_new() ?");
+      stream_free (s);
+      return NULL;
+    }
+
+  if ((new->data = ospf_lsa_data_new (length)) == NULL)
+    {
+      zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_data_new() ?");
+      ospf_lsa_unlock (&new);
+      stream_free (s);
+      return NULL;
+    }
+
+  new->area = area;
+  new->oi = oi;
+
+  SET_FLAG (new->flags, OSPF_LSA_SELF);
+  memcpy (new->data, newlsa, length);
+  stream_free (s);
+
+  return new;
+}
+
+
+int
+ospf_apiserver_is_ready_type9 (struct ospf_interface *oi)
+{
+  /* Type 9 opaque LSA can be originated if there is at least one
+     active opaque-capable neighbor attached to the outgoing
+     interface. */
+
+  return (ospf_nbr_count_opaque_capable (oi) > 0);
+}
+
+int
+ospf_apiserver_is_ready_type10 (struct ospf_area *area)
+{
+  /* Type 10 opaque LSA can be originated if there is at least one
+     interface belonging to the area that has an active opaque-capable
+     neighbor. */
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi))
+    /* Is there an active neighbor attached to this interface? */
+    if (ospf_apiserver_is_ready_type9 (oi))
+      return 1;
+
+  /* No active neighbor in area */
+  return 0;
+}
+
+int
+ospf_apiserver_is_ready_type11 (struct ospf *ospf)
+{
+  /* Type 11 opaque LSA can be originated if there is at least one interface
+     that has an active opaque-capable neighbor. */
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    /* Is there an active neighbor attached to this interface? */
+    if (ospf_apiserver_is_ready_type9 (oi))
+      return 1;
+
+  /* No active neighbor at all */
+  return 0;
+}
+
+
+int
+ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv,
+                                        struct msg *msg)
+{
+  struct msg_originate_request *omsg;
+  struct lsa_header *data;
+  struct ospf_lsa *new;
+  struct ospf_lsa *old;
+  struct ospf_area *area = NULL;
+  struct ospf_interface *oi = NULL;
+  struct ospf_lsdb *lsdb = NULL;
+  struct ospf *ospf;
+  int lsa_type, opaque_type;
+  int ready = 0;
+  int rc = 0;
+  
+  ospf = ospf_lookup();
+
+  /* Extract opaque LSA data from message */
+  omsg = (struct msg_originate_request *) STREAM_DATA (msg->s);
+  data = &omsg->data;
+
+  /* Determine interface for type9 or area for type10 LSAs. */
+  switch (data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      oi = ospf_apiserver_if_lookup_by_addr (omsg->ifaddr);
+      if (!oi)
+       {
+         zlog_warn ("apiserver_originate: unknown interface %s",
+                    inet_ntoa (omsg->ifaddr));
+         rc = OSPF_API_NOSUCHINTERFACE;
+         goto out;
+       }
+      area = oi->area;
+      lsdb = area->lsdb;
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      area = ospf_area_lookup_by_area_id (ospf, omsg->area_id);
+      if (!area)
+       {
+         zlog_warn ("apiserver_originate: unknown area %s",
+                    inet_ntoa (omsg->area_id));
+         rc = OSPF_API_NOSUCHAREA;
+         goto out;
+       }
+      lsdb = area->lsdb;
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      lsdb = ospf->lsdb;
+      break;
+    default:
+      /* We can only handle opaque types here */
+      zlog_warn ("apiserver_originate: Cannot originate non-opaque LSA type %d",
+                data->type);
+      rc = OSPF_API_ILLEGALLSATYPE;
+      goto out;
+    }
+
+  /* Check if we registered this opaque type */
+  lsa_type = data->type;
+  opaque_type = GET_OPAQUE_TYPE (ntohl (data->id.s_addr));
+
+  if (!apiserver_is_opaque_type_registered (apiserv, lsa_type, opaque_type))
+    {
+      zlog_warn ("apiserver_originate: LSA-type(%d)/Opaque-type(%d): Not registered", lsa_type, opaque_type);
+      rc = OSPF_API_OPAQUETYPENOTREGISTERED;
+      goto out;
+    }
+
+  /* Make sure that the neighbors are ready before we can originate */
+  switch (data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      ready = ospf_apiserver_is_ready_type9 (oi);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      ready = ospf_apiserver_is_ready_type10 (area);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      ready = ospf_apiserver_is_ready_type11 (ospf);
+      break;
+    default:
+      break;
+    }
+
+  if (!ready)
+    {
+      zlog_warn ("Neighbors not ready to originate type %d", data->type);
+      rc = OSPF_API_NOTREADY;
+      goto out;
+    }
+
+  /* Create OSPF's internal opaque LSA representation */
+  new = ospf_apiserver_opaque_lsa_new (area, oi, data);
+  if (!new)
+    {
+      rc = OSPF_API_NOMEMORY;  /* XXX */
+      goto out;
+    }
+
+  /* Determine if LSA is new or an update for an existing one. */
+  old = ospf_lsdb_lookup (lsdb, new);
+
+  if (!old)
+    {
+      /* New LSA install in LSDB. */
+      rc = ospf_apiserver_originate1 (new);
+    }
+  else
+    {
+      /*
+       * Keep the new LSA instance in the "waiting place" until the next
+       * refresh timing. If several LSA update requests for the same LSID
+       * have issued by peer, the last one takes effect.
+       */
+      new->lsdb = &apiserv->reserve;
+      ospf_lsdb_add (&apiserv->reserve, new);
+
+      /* Kick the scheduler function. */
+      ospf_opaque_lsa_refresh_schedule (old);
+    }
+
+out:
+
+  /* Send a reply back to client with return code */
+  rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc);
+  return rc;
+}
+
+
+/* -----------------------------------------------------------
+ * Flood an LSA within its flooding scope. 
+ * -----------------------------------------------------------
+ */
+
+/* XXX We can probably use ospf_flood_through instead of this function
+   but then we need the neighbor parameter. If we set nbr to 
+   NULL then ospf_flood_through crashes due to dereferencing NULL. */
+
+void
+ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa)
+{
+  assert (lsa);
+
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      /* Increment counters? XXX */
+
+      /* Flood LSA through local network. */
+      ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      /* Update LSA origination count. */
+      assert (lsa->area);
+      lsa->area->ospf->lsa_originate_count++;
+
+      /* Flood LSA through area. */
+      ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      {
+       struct ospf *ospf;
+
+       ospf = ospf_lookup();
+       assert(ospf);
+
+       /* Increment counters? XXX */
+
+       /* Flood LSA through AS. */
+       ospf_flood_through_as (ospf, NULL /*nbr */ , lsa);
+       break;
+      }
+    }
+}
+
+int
+ospf_apiserver_originate1 (struct ospf_lsa *lsa)
+{
+  struct ospf *ospf;
+
+  ospf = ospf_lookup();
+  assert(ospf);
+
+  /* Install this LSA into LSDB. */
+  if (ospf_lsa_install (ospf, lsa->oi, lsa) == NULL)
+    {
+      zlog_warn ("ospf_apiserver_originate1: ospf_lsa_install failed");
+      return -1;
+    }
+
+  /* Flood LSA within scope */
+
+#ifdef NOTYET
+  /*
+   * NB: Modified version of "ospf_flood_though ()" accepts NULL "inbr"
+   *     parameter, and thus it does not cause SIGSEGV error.
+   */
+  ospf_flood_through (NULL /*nbr */ , lsa);
+#else /* NOTYET */
+
+  ospf_apiserver_flood_opaque_lsa (lsa);
+#endif /* NOTYET */
+
+  return 0;
+}
+
+
+/* Opaque LSAs of type 9 on a specific interface can now be
+   originated. Tell clients that registered type 9. */
+int
+ospf_apiserver_lsa9_originator (void *arg)
+{
+  struct ospf_interface *oi;
+
+  oi = (struct ospf_interface *) arg;
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_ready_type9 (oi);
+  }
+  return 0;
+}
+
+int
+ospf_apiserver_lsa10_originator (void *arg)
+{
+  struct ospf_area *area;
+
+  area = (struct ospf_area *) arg;
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_ready_type10 (area);
+  }
+  return 0;
+}
+
+int
+ospf_apiserver_lsa11_originator (void *arg)
+{
+  struct ospf *ospf;
+
+  ospf = (struct ospf *) arg;
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_ready_type11 (ospf);
+  }
+  return 0;
+}
+
+
+/* Periodically refresh opaque LSAs so that they do not expire in
+   other routers. */
+struct ospf_lsa *
+ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa)
+{
+  struct ospf_apiserver *apiserv;
+  struct ospf_lsa *new = NULL;
+  struct ospf * ospf;
+
+  ospf = ospf_lookup();
+  assert(ospf);
+
+  apiserv = lookup_apiserver_by_lsa (lsa);
+  if (!apiserv)
+    {
+      zlog_warn ("ospf_apiserver_lsa_refresher: LSA[%s]: No apiserver?", dump_lsa_key (lsa));
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */
+    }
+
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      ospf_opaque_lsa_flush_schedule (lsa);
+      goto out;
+    }
+
+  /* Check if updated version of LSA instance has already prepared. */
+  new = ospf_lsdb_lookup (&apiserv->reserve, lsa);
+  if (!new)
+    {
+      /* This is a periodic refresh, driven by core OSPF mechanism. */
+      new = ospf_apiserver_opaque_lsa_new (lsa->area, lsa->oi, lsa->data);
+      if (!new)
+        {
+          zlog_warn ("ospf_apiserver_lsa_refresher: Cannot create a new LSA?");
+          goto out;
+        }
+    }
+  else
+    {
+      /* This is a forcible refresh, requested by OSPF-API client. */
+      ospf_lsdb_delete (&apiserv->reserve, new);
+      new->lsdb = NULL;
+    }
+
+  /* Increment sequence number */
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  /* New LSA is in same area. */
+  new->area = lsa->area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF);
+
+  /* Install LSA into LSDB. */
+  if (ospf_lsa_install (ospf, new->oi, new) == NULL)
+    {
+      zlog_warn ("ospf_apiserver_lsa_refresher: ospf_lsa_install failed");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Flood updated LSA through interface, area or AS */
+
+#ifdef NOTYET
+  ospf_flood_through (NULL /*nbr */ , new);
+#endif /* NOTYET */
+  ospf_apiserver_flood_opaque_lsa (new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Refresh Opaque LSA",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+out:
+  return new;
+}
+
+
+/* -----------------------------------------------------------
+ * Followings are functions to delete LSAs
+ * -----------------------------------------------------------
+ */
+
+int
+ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv,
+                                     struct msg *msg)
+{
+  struct msg_delete_request *dmsg;
+  struct ospf_lsa *old;
+  struct ospf_area *area = NULL;
+  struct in_addr id;
+  int lsa_type, opaque_type;
+  int rc = 0;
+  struct ospf * ospf;
+
+  ospf = ospf_lookup();
+  assert(ospf);
+
+  /* Extract opaque LSA from message */
+  dmsg = (struct msg_delete_request *) STREAM_DATA (msg->s);
+
+  /* Lookup area for link-local and area-local opaque LSAs */
+  switch (dmsg->lsa_type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+      area = ospf_area_lookup_by_area_id (ospf, dmsg->area_id);
+      if (!area)
+       {
+         zlog_warn ("ospf_apiserver_lsa_delete: unknown area %s",
+                    inet_ntoa (dmsg->area_id));
+         rc = OSPF_API_NOSUCHAREA;
+         goto out;
+       }
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      /* AS-external opaque LSAs have no designated area */
+      area = NULL;
+      break;
+    default:
+      zlog_warn
+       ("ospf_apiserver_lsa_delete: Cannot delete non-opaque LSA type %d",
+        dmsg->lsa_type);
+      rc = OSPF_API_ILLEGALLSATYPE;
+      goto out;
+    }
+
+  /* Check if we registered this opaque type */
+  lsa_type = dmsg->lsa_type;
+  opaque_type = dmsg->opaque_type;
+
+  if (!apiserver_is_opaque_type_registered (apiserv, lsa_type, opaque_type))
+    {
+      zlog_warn ("ospf_apiserver_lsa_delete: LSA-type(%d)/Opaque-type(%d): Not registered", lsa_type, opaque_type);
+      rc = OSPF_API_OPAQUETYPENOTREGISTERED;
+      goto out;
+    }
+
+  /* opaque_id is in network byte order */
+  id.s_addr = htonl (SET_OPAQUE_LSID (dmsg->opaque_type,
+                                     ntohl (dmsg->opaque_id)));
+
+  /*
+   * Even if the target LSA has once scheduled to flush, it remains in
+   * the LSDB until it is finally handled by the maxage remover thread.
+   * Therefore, the lookup function below may return non-NULL result.
+   */
+  old = ospf_lsa_lookup (area, dmsg->lsa_type, id, ospf->router_id);
+  if (!old)
+    {
+      zlog_warn ("ospf_apiserver_lsa_delete: LSA[Type%d:%s] not in LSDB",
+                dmsg->lsa_type, inet_ntoa (id));
+      rc = OSPF_API_NOSUCHLSA;
+      goto out;
+    }
+
+  /* Schedule flushing of LSA from LSDB */
+  /* NB: Multiple scheduling will produce a warning message, but harmless. */
+  ospf_opaque_lsa_flush_schedule (old);
+
+out:
+
+  /* Send reply back to client including return code */
+  rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc);
+  return rc;
+}
+
+/* Flush self-originated opaque LSA */
+static int
+apiserver_flush_opaque_type_callback (struct ospf_lsa *lsa,
+                                     void *p_arg, int int_arg)
+{
+  struct param_t
+  {
+    struct ospf_apiserver *apiserv;
+    u_char lsa_type;
+    u_char opaque_type;
+  }
+   *param;
+
+  /* Sanity check */
+  assert (lsa->data);
+  assert (p_arg);
+  param = (struct param_t *) p_arg;
+
+  /* If LSA matches type and opaque type then delete it */
+  if (IS_LSA_SELF (lsa) && lsa->data->type == param->lsa_type
+      && GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)) == param->opaque_type)
+    {
+      ospf_opaque_lsa_flush_schedule (lsa);
+    }
+  return 0;
+}
+
+/* Delete self-originated opaque LSAs of a given opaque type. This
+   function is called when an application unregisters a given opaque
+   type or a connection to an application closes and all those opaque
+   LSAs need to be flushed the LSDB. */
+void
+ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv,
+                                u_char lsa_type, u_char opaque_type)
+{
+  struct param_t
+  {
+    struct ospf_apiserver *apiserv;
+    u_char lsa_type;
+    u_char opaque_type;
+  } param;
+  struct listnode *node, *nnode;
+  struct ospf * ospf;
+  struct ospf_area *area;
+  
+  ospf = ospf_lookup();
+  assert(ospf);
+
+  /* Set parameter struct. */
+  param.apiserv = apiserv;
+  param.lsa_type = lsa_type;
+  param.opaque_type = opaque_type;
+
+  switch (lsa_type)
+    {
+      struct route_node *rn;
+      struct ospf_lsa *lsa;
+
+    case OSPF_OPAQUE_LINK_LSA:
+      for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+        LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+          apiserver_flush_opaque_type_callback(lsa, (void *) &param, 0);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+        LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+          apiserver_flush_opaque_type_callback(lsa, (void *) &param, 0);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      LSDB_LOOP (OPAQUE_LINK_LSDB (ospf), rn, lsa)
+       apiserver_flush_opaque_type_callback(lsa, (void *) &param, 0);
+      break;
+    default:
+      break;
+    }
+  return;
+}
+
+
+/* -----------------------------------------------------------
+ * Followings are callback functions to handle opaque types 
+ * -----------------------------------------------------------
+ */
+
+int
+ospf_apiserver_new_if (struct interface *ifp)
+{
+  struct ospf_interface *oi;
+
+  /* For some strange reason it seems possible that we are invoked
+     with an interface that has no name. This seems to happen during
+     initialization. Return if this happens */
+
+  if (ifp->name[0] == '\0') {
+    /* interface has empty name */
+    zlog_warn ("ospf_apiserver_new_if: interface has no name?");
+    return 0;
+  }
+
+  /* zlog_warn for debugging */
+  zlog_warn ("ospf_apiserver_new_if");
+  zlog_warn ("ifp name=%s status=%d index=%d", ifp->name, ifp->status,
+            ifp->ifindex);
+
+  if (ifp->name[0] == '\0') {
+    /* interface has empty name */
+    zlog_warn ("ospf_apiserver_new_if: interface has no name?");
+    return 0;
+  }
+
+  oi = ospf_apiserver_if_lookup_by_ifp (ifp);
+  
+  if (!oi) {
+    /* This interface is known to Zebra but not to OSPF daemon yet. */
+    zlog_warn ("ospf_apiserver_new_if: interface %s not known to OSPFd?", 
+              ifp->name);
+    return 0;
+  }
+
+  assert (oi);
+
+  /* New interface added to OSPF, tell clients about it */
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_new_if (oi);
+  }
+  return 0;
+}
+
+int
+ospf_apiserver_del_if (struct interface *ifp)
+{
+  struct ospf_interface *oi;
+
+  /* zlog_warn for debugging */
+  zlog_warn ("ospf_apiserver_del_if");
+  zlog_warn ("ifp name=%s status=%d index=%d\n", ifp->name, ifp->status,
+            ifp->ifindex);
+
+  oi = ospf_apiserver_if_lookup_by_ifp (ifp);
+
+  if (!oi) {
+    /* This interface is known to Zebra but not to OSPF daemon
+       anymore. No need to tell clients about it */
+    return 0;
+  }
+
+  /* Interface deleted, tell clients about it */
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_del_if (oi);
+  }
+  return 0;
+}
+
+void
+ospf_apiserver_ism_change (struct ospf_interface *oi, int old_state)
+{
+  /* Tell clients about interface change */
+
+  /* zlog_warn for debugging */
+  zlog_warn ("ospf_apiserver_ism_change");
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_ism_change (oi);
+  }
+
+  zlog_warn ("oi->ifp->name=%s", oi->ifp->name);
+  zlog_warn ("old_state=%d", old_state);
+  zlog_warn ("oi->state=%d", oi->state);
+}
+
+void
+ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status)
+{
+  /* Neighbor status changed, tell clients about it */
+  zlog_warn ("ospf_apiserver_nsm_change");
+  if (listcount (apiserver_list) > 0) {
+    ospf_apiserver_clients_notify_nsm_change (nbr);
+  }
+}
+
+void
+ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa)
+{
+  struct opaque_lsa
+  {
+    struct lsa_header header;
+    u_char data[1]; /* opaque data have variable length. This is start
+                       address */
+  };
+  struct opaque_lsa *olsa;
+  int opaquelen;
+
+  olsa = (struct opaque_lsa *) lsa->data;
+
+  if (VALID_OPAQUE_INFO_LEN (lsa->data))
+    opaquelen = ntohs (lsa->data->length) - OSPF_LSA_HEADER_SIZE;
+  else
+    opaquelen = 0;
+
+  /* Output information about opaque LSAs */
+  if (vty != NULL)
+    {
+      int i;
+      vty_out (vty, "  Added using OSPF API: %u octets of opaque data %s%s",
+              opaquelen,
+              VALID_OPAQUE_INFO_LEN (lsa->data) ? "" : "(Invalid length?)",
+              VTY_NEWLINE);
+      vty_out (vty, "  Opaque data: ");
+
+      for (i = 0; i < opaquelen; i++)
+       {
+         vty_out (vty, "0x%x ", olsa->data[i]);
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  else
+    {
+      int i;
+      zlog_debug ("    Added using OSPF API: %u octets of opaque data %s",
+                opaquelen,
+                VALID_OPAQUE_INFO_LEN (lsa->
+                                       data) ? "" : "(Invalid length?)");
+      zlog_debug ("    Opaque data: ");
+
+      for (i = 0; i < opaquelen; i++)
+       {
+         zlog_debug ("0x%x ", olsa->data[i]);
+       }
+      zlog_debug ("\n");
+    }
+  return;
+}
+
+/* -----------------------------------------------------------
+ * Followings are functions to notify clients about events
+ * -----------------------------------------------------------
+ */
+
+/* Send a message to all clients. This is useful for messages
+   that need to be notified to all clients (such as interface
+   changes) */
+
+void
+ospf_apiserver_clients_notify_all (struct msg *msg)
+{
+  struct listnode *node, *nnode;
+  struct ospf_apiserver *apiserv;
+
+  /* Send message to all clients */
+  for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv))
+    ospf_apiserver_send_msg (apiserv, msg);
+}
+
+/* An interface is now ready to accept opaque LSAs. Notify all
+   clients that registered to use this opaque type */
+void
+ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct msg *msg;
+  struct ospf_apiserver *apiserv;
+
+  assert (oi);
+  if (!oi->address)
+    {
+      zlog_warn ("Interface has no address?");
+      return;
+    }
+
+  if (!ospf_apiserver_is_ready_type9 (oi))
+    {
+      zlog_warn ("Interface not ready for type 9?");
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv))
+    {
+      struct listnode *node2, *nnode2;
+      struct registered_opaque_type *r;
+
+      for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
+       {
+         if (r->lsa_type == OSPF_OPAQUE_LINK_LSA)
+           {
+             msg = new_msg_ready_notify (0, OSPF_OPAQUE_LINK_LSA,
+                                         r->opaque_type,
+                                         oi->address->u.prefix4);
+             if (!msg)
+               {
+                 zlog_warn
+                   ("ospf_apiserver_clients_notify_ready_type9: new_msg_ready_notify failed");
+#ifdef NOTYET
+                 /* Cannot allocate new message. What should we do? */
+                 ospf_apiserver_free (apiserv);
+#endif
+                 goto out;
+               }
+
+             ospf_apiserver_send_msg (apiserv, msg);
+             msg_free (msg);
+           }
+       }
+    }
+
+out:
+  return;
+}
+
+void
+ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area)
+{
+  struct listnode *node, *nnode;
+  struct msg *msg;
+  struct ospf_apiserver *apiserv;
+
+  assert (area);
+
+  if (!ospf_apiserver_is_ready_type10 (area))
+    {
+      zlog_warn ("Area not ready for type 10?");
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv))
+    {
+      struct listnode *node2, *nnode2;
+      struct registered_opaque_type *r;
+
+      for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
+       {
+         if (r->lsa_type == OSPF_OPAQUE_AREA_LSA)
+           {
+             msg = new_msg_ready_notify (0, OSPF_OPAQUE_AREA_LSA,
+                                         r->opaque_type, area->area_id);
+             if (!msg)
+               {
+                 zlog_warn
+                   ("ospf_apiserver_clients_notify_ready_type10: new_msg_ready_nofity failed");
+#ifdef NOTYET
+                 /* Cannot allocate new message. What should we do? */
+                 ospf_apiserver_free (apiserv);
+#endif
+                  goto out;
+               }
+
+             ospf_apiserver_send_msg (apiserv, msg);
+             msg_free (msg);
+           }
+       }
+    }
+
+out:
+  return;
+}
+
+
+void
+ospf_apiserver_clients_notify_ready_type11 (struct ospf *top)
+{
+  struct listnode *node, *nnode;
+  struct msg *msg;
+  struct in_addr id_null = { .s_addr = 0L };
+  struct ospf_apiserver *apiserv;
+  
+  assert (top);
+  
+  if (!ospf_apiserver_is_ready_type11 (top))
+    {
+      zlog_warn ("AS not ready for type 11?");
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv))
+    {
+      struct listnode *node2, *nnode2;
+      struct registered_opaque_type *r;
+
+      for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r))
+       {
+         if (r->lsa_type == OSPF_OPAQUE_AS_LSA)
+           {
+             msg = new_msg_ready_notify (0, OSPF_OPAQUE_AS_LSA,
+                                         r->opaque_type, id_null);
+             if (!msg)
+               {
+                 zlog_warn
+                   ("ospf_apiserver_clients_notify_ready_type11: new_msg_ready_notify failed");
+#ifdef NOTYET
+                 /* Cannot allocate new message. What should we do? */
+                 ospf_apiserver_free (apiserv);
+#endif
+                 goto out;
+               }
+
+             ospf_apiserver_send_msg (apiserv, msg);
+             msg_free (msg);
+           }
+       }
+    }
+
+out:
+  return;
+}
+
+void
+ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi)
+{
+  struct msg *msg;
+
+  msg = new_msg_new_if (0, oi->address->u.prefix4, oi->area->area_id);
+  if (msg != NULL)
+    {
+      ospf_apiserver_clients_notify_all (msg);
+      msg_free (msg);
+    }
+}
+
+void
+ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi)
+{
+  struct msg *msg;
+
+  msg = new_msg_del_if (0, oi->address->u.prefix4);
+  if (msg != NULL)
+    {
+      ospf_apiserver_clients_notify_all (msg);
+      msg_free (msg);
+    }
+}
+
+void
+ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi)
+{
+  struct msg *msg;
+  struct in_addr ifaddr = { .s_addr = 0L };
+  struct in_addr area_id = { .s_addr = 0L };
+  
+  assert (oi);
+  assert (oi->ifp);
+  
+  if (oi->address)
+    {
+      ifaddr = oi->address->u.prefix4;
+    }
+  if (oi->area)
+    {
+      area_id = oi->area->area_id;
+    }
+
+  msg = new_msg_ism_change (0, ifaddr, area_id, oi->state);
+  if (!msg)
+    {
+      zlog_warn ("apiserver_clients_notify_ism_change: msg_new failed");
+      return;
+    }
+
+  ospf_apiserver_clients_notify_all (msg);
+  msg_free (msg);
+}
+
+void
+ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr)
+{
+  struct msg *msg;
+  struct in_addr ifaddr = { .s_addr = 0L };
+  struct in_addr nbraddr = { .s_addr = 0L };
+
+  assert (nbr);
+
+  if (nbr->oi)
+    {
+      ifaddr = nbr->oi->address->u.prefix4;
+    }
+
+  nbraddr = nbr->address.u.prefix4;
+
+  msg = new_msg_nsm_change (0, ifaddr, nbraddr, nbr->router_id, nbr->state);
+  if (!msg)
+    {
+      zlog_warn ("apiserver_clients_notify_nsm_change: msg_new failed");
+      return;
+    }
+
+  ospf_apiserver_clients_notify_all (msg);
+  msg_free (msg);
+}
+
+static void
+apiserver_clients_lsa_change_notify (u_char msgtype, struct ospf_lsa *lsa)
+{
+  struct msg *msg;
+  struct listnode *node, *nnode;
+  struct ospf_apiserver *apiserv;
+
+  /* Default area for AS-External and Opaque11 LSAs */
+  struct in_addr area_id = { .s_addr = 0L };
+
+  /* Default interface for non Opaque9 LSAs */
+  struct in_addr ifaddr = { .s_addr = 0L };
+
+  if (lsa->area)
+    {
+      area_id = lsa->area->area_id;
+    }
+  if (lsa->data->type == OSPF_OPAQUE_LINK_LSA)
+    {
+      assert (lsa->oi);
+      ifaddr = lsa->oi->address->u.prefix4;
+    }
+
+  /* Prepare message that can be sent to clients that have a matching
+     filter */
+  msg = new_msg_lsa_change_notify (msgtype, 0L,        /* no sequence number */
+                                  ifaddr, area_id,
+                                  lsa->flags & OSPF_LSA_SELF, lsa->data);
+  if (!msg)
+    {
+      zlog_warn ("apiserver_clients_lsa_change_notify: msg_new failed");
+      return;
+    }
+
+  /* Now send message to all clients with a matching filter */
+  for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv))
+    {
+      struct lsa_filter_type *filter;
+      u_int16_t mask;
+      u_int32_t *area;
+      int i;
+
+      /* Check filter for this client. */
+      filter = apiserv->filter;
+
+      /* Check area IDs in case of non AS-E LSAs.
+       * If filter has areas (num_areas > 0),
+       * then one of the areas must match the area ID of this LSA. */
+
+      i = filter->num_areas;
+      if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA) ||
+         (lsa->data->type == OSPF_OPAQUE_AS_LSA))
+       {
+         i = 0;
+       }
+
+      if (i > 0)
+       {
+         area = (u_int32_t *) (filter + 1);
+         while (i)
+           {
+             if (*area == area_id.s_addr)
+               {
+                 break;
+               }
+             i--;
+             area++;
+           }
+       }
+      else
+       {
+         i = 1;
+       }
+
+      if (i > 0)
+       {
+         /* Area match. Check LSA type. */
+         mask = ntohs (filter->typemask);
+
+         if (mask & Power2[lsa->data->type])
+           {
+             /* Type also matches. Check origin. */
+             if ((filter->origin == ANY_ORIGIN) ||
+                 (filter->origin == IS_LSA_SELF (lsa)))
+               {
+                 ospf_apiserver_send_msg (apiserv, msg);
+               }
+           }
+       }
+    }
+  /* Free message since it is not used anymore */
+  msg_free (msg);
+}
+
+
+/* -------------------------------------------------------------
+ * Followings are hooks invoked when LSAs are updated or deleted
+ * -------------------------------------------------------------
+ */
+
+
+static int
+apiserver_notify_clients_lsa (u_char msgtype, struct ospf_lsa *lsa)
+{
+  struct msg *msg;
+  /* default area for AS-External and Opaque11 LSAs */
+  struct in_addr area_id = { .s_addr = 0L };
+
+  /* default interface for non Opaque9 LSAs */
+  struct in_addr ifaddr = { .s_addr = 0L };
+
+  /* Only notify this update if the LSA's age is smaller than
+     MAXAGE. Otherwise clients would see LSA updates with max age just
+     before they are deleted from the LSDB. LSA delete messages have
+     MAXAGE too but should not be filtered. */
+  if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) {
+    return 0;
+  }
+
+  if (lsa->area)
+    {
+      area_id = lsa->area->area_id;
+    }
+  if (lsa->data->type == OSPF_OPAQUE_LINK_LSA)
+    {
+      ifaddr = lsa->oi->address->u.prefix4;
+    }
+  msg = new_msg_lsa_change_notify (msgtype, 0L,        /* no sequence number */
+                                  ifaddr, area_id,
+                                  lsa->flags & OSPF_LSA_SELF, lsa->data);
+  if (!msg)
+    {
+      zlog_warn ("notify_clients_lsa: msg_new failed");
+      return -1;
+    }
+  /* Notify all clients that new LSA is added/updated */
+  apiserver_clients_lsa_change_notify (msgtype, lsa);
+
+  /* Clients made their own copies of msg so we can free msg here */
+  msg_free (msg);
+
+  return 0;
+}
+
+int
+ospf_apiserver_lsa_update (struct ospf_lsa *lsa)
+{
+  return apiserver_notify_clients_lsa (MSG_LSA_UPDATE_NOTIFY, lsa);
+}
+
+int
+ospf_apiserver_lsa_delete (struct ospf_lsa *lsa)
+{
+  return apiserver_notify_clients_lsa (MSG_LSA_DELETE_NOTIFY, lsa);
+}
+
+#endif /* SUPPORT_OSPF_API */
+
diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h
new file mode 100644 (file)
index 0000000..b60f56b
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Server side of OSPF API.
+ * Copyright (C) 2001, 2002 Ralph Keller
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _OSPF_APISERVER_H
+#define _OSPF_APISERVER_H
+
+/* MTYPE definition is not reflected to "memory.h". */
+#define MTYPE_OSPF_APISERVER MTYPE_TMP
+#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP
+
+/* List of opaque types that application registered */
+struct registered_opaque_type
+{
+  u_char lsa_type;
+  u_char opaque_type;
+};
+
+
+/* Server instance for each accepted client connection. */
+struct ospf_apiserver
+{
+  /* Socket connections for synchronous commands and asynchronous
+     notifications */
+  int fd_sync;                 /* synchronous requests */
+  struct sockaddr_in peer_sync;
+
+  int fd_async;                        /* asynchronous notifications */
+  struct sockaddr_in peer_async;
+
+  /* List of all opaque types that application registers to use. Using
+     a single connection with the OSPF daemon, multiple
+     <lsa,opaque_type> pairs can be registered. However, each
+     combination can only be registered once by all applications. */
+  struct list *opaque_types;           /* of type registered_opaque_type */
+
+  /* Temporary storage for LSA instances to be refreshed. */
+  struct ospf_lsdb reserve;
+
+  /* filter for LSA update/delete notifies */
+  struct lsa_filter_type *filter;
+
+  /* Fifo buffers for outgoing messages */
+  struct msg_fifo *out_sync_fifo;
+  struct msg_fifo *out_async_fifo;
+
+  /* Read and write threads */
+  struct thread *t_sync_read;
+#ifdef USE_ASYNC_READ
+  struct thread *t_async_read;
+#endif /* USE_ASYNC_READ */
+  struct thread *t_sync_write;
+  struct thread *t_async_write;
+};
+
+enum event
+{
+  OSPF_APISERVER_ACCEPT,
+  OSPF_APISERVER_SYNC_READ,
+#ifdef USE_ASYNC_READ
+  OSPF_APISERVER_ASYNC_READ,
+#endif /* USE_ASYNC_READ */
+  OSPF_APISERVER_SYNC_WRITE,
+  OSPF_APISERVER_ASYNC_WRITE
+};
+
+/* -----------------------------------------------------------
+ * Followings are functions to manage client connections.
+ * -----------------------------------------------------------
+ */
+
+extern unsigned short ospf_apiserver_getport (void);
+extern int ospf_apiserver_init (void);
+extern void ospf_apiserver_term (void);
+extern struct ospf_apiserver *ospf_apiserver_new (int fd_sync, int fd_async);
+extern void ospf_apiserver_free (struct ospf_apiserver *apiserv);
+extern void ospf_apiserver_event (enum event event, int fd,
+                          struct ospf_apiserver *apiserv);
+extern int ospf_apiserver_serv_sock_family (unsigned short port, int family);
+extern int ospf_apiserver_accept (struct thread *thread);
+extern int ospf_apiserver_read (struct thread *thread);
+extern int ospf_apiserver_sync_write (struct thread *thread);
+extern int ospf_apiserver_async_write (struct thread *thread);
+extern int ospf_apiserver_send_reply (struct ospf_apiserver *apiserv,
+                              u_int32_t seqnr, u_char rc);
+
+/* -----------------------------------------------------------
+ * Followings are message handler functions
+ * -----------------------------------------------------------
+ */
+
+extern int ospf_apiserver_lsa9_originator (void *arg);
+extern int ospf_apiserver_lsa10_originator (void *arg);
+extern int ospf_apiserver_lsa11_originator (void *arg);
+
+extern void ospf_apiserver_clients_notify_all (struct msg *msg);
+
+extern void ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi);
+extern void ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area);
+extern void ospf_apiserver_clients_notify_ready_type11 (struct ospf *top);
+
+extern void ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi);
+extern void ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi);
+extern void ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi);
+extern void ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr);
+
+extern int ospf_apiserver_is_ready_type9 (struct ospf_interface *oi);
+extern int ospf_apiserver_is_ready_type10 (struct ospf_area *area);
+extern int ospf_apiserver_is_ready_type11 (struct ospf *ospf);
+
+extern void ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv);
+extern void ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv);
+extern void ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv);
+
+extern int ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv,
+                              struct msg *msg);
+extern int ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver
+                                               *apiserv, struct msg *msg);
+extern int ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver
+                                                 *apiserv, struct msg *msg);
+extern int ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv,
+                                         struct msg *msg);
+extern int ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv,
+                                            struct msg *msg);
+extern int ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv,
+                                         struct msg *msg);
+extern int ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv,
+                                    struct msg *msg);
+
+
+/* -----------------------------------------------------------
+ * Followings are functions for LSA origination/deletion
+ * -----------------------------------------------------------
+ */
+
+extern int ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserver,
+                                        u_char lsa_type, u_char opaque_type);
+extern int ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserver,
+                                          u_char lsa_type,
+                                          u_char opaque_type);
+extern struct ospf_lsa *ospf_apiserver_opaque_lsa_new (struct ospf_area *area,
+                                               struct ospf_interface *oi,
+                                               struct lsa_header *protolsa);
+extern struct ospf_interface *ospf_apiserver_if_lookup_by_addr (struct in_addr
+                                                        address);
+extern struct ospf_interface *ospf_apiserver_if_lookup_by_ifp (struct interface
+                                                       *ifp);
+extern int ospf_apiserver_originate1 (struct ospf_lsa *lsa);
+extern void ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa);
+
+
+/* -----------------------------------------------------------
+ * Followings are callback functions to handle opaque types 
+ * -----------------------------------------------------------
+ */
+
+extern int ospf_apiserver_new_if (struct interface *ifp);
+extern int ospf_apiserver_del_if (struct interface *ifp);
+extern void ospf_apiserver_ism_change (struct ospf_interface *oi, int old_status);
+extern void ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status);
+extern void ospf_apiserver_config_write_router (struct vty *vty);
+extern void ospf_apiserver_config_write_if (struct vty *vty, struct interface *ifp);
+extern void ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa);
+extern int ospf_ospf_apiserver_lsa_originator (void *arg);
+extern struct ospf_lsa *ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa);
+extern void ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv,
+                                     u_char lsa_type, u_char opaque_type);
+
+/* -----------------------------------------------------------
+ * Followings are hooks when LSAs are updated or deleted
+ * -----------------------------------------------------------
+ */
+
+
+/* Hooks that are invoked from ospf opaque module */
+
+extern int ospf_apiserver_lsa_update (struct ospf_lsa *lsa);
+extern int ospf_apiserver_lsa_delete (struct ospf_lsa *lsa);
+
+extern void ospf_apiserver_clients_lsa_change_notify (u_char msgtype,
+                                              struct ospf_lsa *lsa);
+
+#endif /* _OSPF_APISERVER_H */
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
new file mode 100644 (file)
index 0000000..0a411e4
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * OSPF AS Boundary Router functions.
+ * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "vty.h"
+#include "filter.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+
+
+/* Remove external route. */
+void
+ospf_external_route_remove (struct ospf *ospf, struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  rn = route_node_lookup (ospf->old_external_route, (struct prefix *) p);
+  if (rn)
+    if ((or = rn->info))
+      {
+       zlog_info ("Route[%s/%d]: external path deleted",
+                  inet_ntoa (p->prefix), p->prefixlen);
+
+       /* Remove route from zebra. */
+        if (or->type == OSPF_DESTINATION_NETWORK)
+         ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or);
+
+       ospf_route_free (or);
+       rn->info = NULL;
+
+       route_unlock_node (rn);
+       route_unlock_node (rn);
+       return;
+      }
+
+  zlog_info ("Route[%s/%d]: no such external path",
+            inet_ntoa (p->prefix), p->prefixlen);
+}
+
+/* Lookup external route. */
+struct ospf_route *
+ospf_external_route_lookup (struct ospf *ospf,
+                           struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+
+  rn = route_node_lookup (ospf->old_external_route, (struct prefix *) p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      if (rn->info)
+       return rn->info;
+    }
+
+  zlog_warn ("Route[%s/%d]: lookup, no such prefix",
+            inet_ntoa (p->prefix), p->prefixlen);
+
+  return NULL;
+}
+
+
+/* Add an External info for AS-external-LSA. */
+struct external_info *
+ospf_external_info_new (u_char type)
+{
+  struct external_info *new;
+
+  new = (struct external_info *)
+    XCALLOC (MTYPE_OSPF_EXTERNAL_INFO, sizeof (struct external_info));
+  new->type = type;
+
+  ospf_reset_route_map_set_values (&new->route_map_set);
+  return new;
+}
+
+static void
+ospf_external_info_free (struct external_info *ei)
+{
+  XFREE (MTYPE_OSPF_EXTERNAL_INFO, ei);
+}
+
+void
+ospf_reset_route_map_set_values (struct route_map_set_values *values)
+{
+  values->metric = -1;
+  values->metric_type = -1;
+}
+
+int
+ospf_route_map_set_compare (struct route_map_set_values *values1,
+                           struct route_map_set_values *values2)
+{
+  return values1->metric == values2->metric &&
+    values1->metric_type == values2->metric_type;
+}
+
+/* Add an External info for AS-external-LSA. */
+struct external_info *
+ospf_external_info_add (u_char type, struct prefix_ipv4 p,
+                       ifindex_t ifindex, struct in_addr nexthop,
+                        route_tag_t tag)
+{
+  struct external_info *new;
+  struct route_node *rn;
+
+  /* Initialize route table. */
+  if (EXTERNAL_INFO (type) == NULL)
+    EXTERNAL_INFO (type) = route_table_init ();
+
+  rn = route_node_get (EXTERNAL_INFO (type), (struct prefix *) &p);
+  /* If old info exists, -- discard new one or overwrite with new one? */
+  if (rn)
+    if (rn->info)
+      {
+       route_unlock_node (rn);
+       zlog_warn ("Redistribute[%s]: %s/%d already exists, discard.",
+                  ospf_redist_string(type),
+                  inet_ntoa (p.prefix), p.prefixlen);
+       /* XFREE (MTYPE_OSPF_TMP, rn->info); */
+       return rn->info;
+      }
+
+  /* Create new External info instance. */
+  new = ospf_external_info_new (type);
+  new->p = p;
+  new->ifindex = ifindex;
+  new->nexthop = nexthop;
+  new->tag = tag;
+
+  if (rn)
+    rn->info = new;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("Redistribute[%s]: %s/%d external info created.",
+              ospf_redist_string(type),
+              inet_ntoa (p.prefix), p.prefixlen);
+  return new;
+}
+
+void
+ospf_external_info_delete (u_char type, struct prefix_ipv4 p)
+{
+  struct route_node *rn;
+
+  rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) &p);
+  if (rn)
+    {
+      ospf_external_info_free (rn->info);
+      rn->info = NULL;
+      route_unlock_node (rn);
+      route_unlock_node (rn);
+    }
+}
+
+struct external_info *
+ospf_external_info_lookup (u_char type, struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+  rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      if (rn->info)
+       return rn->info;
+    }
+
+  return NULL;
+}
+
+struct ospf_lsa *
+ospf_external_info_find_lsa (struct ospf *ospf,
+                            struct prefix_ipv4 *p)
+{
+  struct ospf_lsa *lsa;
+  struct as_external_lsa *al;
+  struct in_addr mask, id;
+
+  lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
+                               p->prefix, ospf->router_id);
+
+  if (!lsa)
+    return NULL;
+
+  al = (struct as_external_lsa *) lsa->data;
+
+  masklen2ip (p->prefixlen, &mask);
+
+  if (mask.s_addr != al->mask.s_addr)
+    {
+      id.s_addr = p->prefix.s_addr | (~mask.s_addr);
+      lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
+                                  id, ospf->router_id);
+      if (!lsa)
+       return NULL;
+    }
+
+  return lsa;
+}
+
+
+/* Update ASBR status. */
+void
+ospf_asbr_status_update (struct ospf *ospf, u_char status)
+{
+  zlog_info ("ASBR[Status:%d]: Update", status);
+
+  /* ASBR on. */
+  if (status)
+    {
+      /* Already ASBR. */
+      if (IS_OSPF_ASBR (ospf))
+       {
+         zlog_info ("ASBR[Status:%d]: Already ASBR", status);
+         return;
+       }
+      SET_FLAG (ospf->flags, OSPF_FLAG_ASBR);
+    }
+  else
+    {
+      /* Already non ASBR. */
+      if (! IS_OSPF_ASBR (ospf))
+       {
+         zlog_info ("ASBR[Status:%d]: Already non ASBR", status);
+         return;
+       }
+      UNSET_FLAG (ospf->flags, OSPF_FLAG_ASBR);
+    }
+
+  /* Transition from/to status ASBR, schedule timer. */
+  ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_STATUS_CHANGE);
+  ospf_router_lsa_update (ospf);
+}
+
+void
+ospf_redistribute_withdraw (struct ospf *ospf, u_char type)
+{
+  struct route_node *rn;
+  struct external_info *ei;
+
+  /* Delete external info for specified type. */
+  if (EXTERNAL_INFO (type))
+    for (rn = route_top (EXTERNAL_INFO (type)); rn; rn = route_next (rn))
+      if ((ei = rn->info))
+       if (ospf_external_info_find_lsa (ospf, &ei->p))
+         {
+           if (is_prefix_default (&ei->p) &&
+               ospf->default_originate != DEFAULT_ORIGINATE_NONE)
+             continue;
+           ospf_external_lsa_flush (ospf, type, &ei->p,
+                                    ei->ifindex /*, ei->nexthop */);
+
+           ospf_external_info_free (ei);
+           route_unlock_node (rn);
+           rn->info = NULL;
+         }
+}
diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h
new file mode 100644 (file)
index 0000000..0fc3302
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * OSPF AS Boundary Router functions.
+ * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ASBR_H
+#define _ZEBRA_OSPF_ASBR_H
+
+struct route_map_set_values
+{
+  int32_t metric;
+  int32_t metric_type;
+};
+
+/* Redistributed external information. */
+struct external_info
+{
+  /* Type of source protocol. */
+  u_char type;
+
+  /* Prefix. */
+  struct prefix_ipv4 p;
+
+  /* Interface index. */
+  ifindex_t ifindex;
+
+  /* Nexthop address. */
+  struct in_addr nexthop;
+
+  /* Additional Route tag: this is the wire type */
+  u_int32_t tag;
+
+  struct route_map_set_values route_map_set;
+#define ROUTEMAP_METRIC(E)      (E)->route_map_set.metric
+#define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type
+};
+
+#define OSPF_ASBR_CHECK_DELAY 30
+
+extern void ospf_external_route_remove (struct ospf *, struct prefix_ipv4 *);
+extern struct external_info *ospf_external_info_new (u_char);
+extern void ospf_reset_route_map_set_values (struct route_map_set_values *);
+extern int ospf_route_map_set_compare (struct route_map_set_values *,
+                               struct route_map_set_values *);
+extern struct external_info *ospf_external_info_add (u_char, 
+                                              struct prefix_ipv4,
+                                             ifindex_t, 
+                                             struct in_addr,
+                                             route_tag_t);
+extern void ospf_external_info_delete (u_char, struct prefix_ipv4);
+extern struct external_info *ospf_external_info_lookup (u_char, 
+                                                 struct prefix_ipv4 *);
+extern struct ospf_route *ospf_external_route_lookup (struct ospf *, 
+                                               struct prefix_ipv4 *);
+extern void ospf_asbr_status_update (struct ospf *, u_char);
+
+extern void ospf_redistribute_withdraw (struct ospf *, u_char);
+extern void ospf_asbr_check (void);
+extern void ospf_schedule_asbr_check (void);
+extern void ospf_asbr_route_install_lsa (struct ospf_lsa *);
+extern struct ospf_lsa *ospf_external_info_find_lsa (struct ospf *,
+                                             struct prefix_ipv4 *p);
+
+#endif /* _ZEBRA_OSPF_ASBR_H */
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
new file mode 100644 (file)
index 0000000..fe40b10
--- /dev/null
@@ -0,0 +1,867 @@
+/*
+ * OSPF AS external route calculation.
+ * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+
+struct ospf_route *
+ospf_find_asbr_route (struct ospf *ospf,
+                     struct route_table *rtrs, struct prefix_ipv4 *asbr)
+{
+  struct route_node *rn;
+  struct ospf_route *or, *best = NULL;
+  struct listnode *node;
+  struct list *chosen;
+
+  /* Sanity check. */
+  if (rtrs == NULL)
+    return NULL;
+
+  rn = route_node_lookup (rtrs, (struct prefix *) asbr);
+  if (! rn)
+    return NULL;
+
+  route_unlock_node (rn);
+
+  chosen = list_new ();
+
+  /* First try to find intra-area non-bb paths. */
+  if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+    for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or))
+      if (or->cost < OSPF_LS_INFINITY)
+        if (!OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id) &&
+            or->path_type == OSPF_PATH_INTRA_AREA)
+          listnode_add (chosen, or);
+
+  /* If none is found -- look through all. */
+  if (listcount (chosen) == 0)
+    {
+      list_free (chosen);
+      chosen = rn->info;
+    }
+
+  /* Now find the route with least cost. */
+  for (ALL_LIST_ELEMENTS_RO (chosen, node, or))
+    if (or->cost < OSPF_LS_INFINITY)
+      {
+        if (best == NULL)
+          best = or;
+        else if (best->cost > or->cost)
+          best = or;
+        else if (best->cost == or->cost &&
+                 IPV4_ADDR_CMP (&best->u.std.area_id,
+                                &or->u.std.area_id) < 0)
+          best = or;
+      }
+
+  if (chosen != rn->info)
+    list_delete (chosen);
+
+  return best;
+}
+
+struct ospf_route * 
+ospf_find_asbr_route_through_area (struct route_table *rtrs, 
+                                  struct prefix_ipv4 *asbr, 
+                                  struct ospf_area *area)
+{
+  struct route_node *rn;
+
+  /* Sanity check. */
+  if (rtrs == NULL)
+    return NULL;
+
+  rn = route_node_lookup (rtrs, (struct prefix *) asbr);
+  if (rn)
+    {
+      struct listnode *node;
+      struct ospf_route *or;
+
+      route_unlock_node (rn);
+
+      for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or))
+        if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id))
+          return or;
+    }
+
+  return NULL;
+}
+
+static void
+ospf_ase_complete_direct_routes (struct ospf_route *ro, struct in_addr nexthop)
+{
+  struct listnode *node;
+  struct ospf_path *op;
+
+  for (ALL_LIST_ELEMENTS_RO (ro->paths, node, op))
+    if (op->nexthop.s_addr == 0)
+      op->nexthop.s_addr = nexthop.s_addr;
+}
+
+static int
+ospf_ase_forward_address_check (struct ospf *ospf, struct in_addr fwd_addr)
+{
+  struct listnode *ifn;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, ifn, oi))
+    if (if_is_operative (oi->ifp))
+      if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+        if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &fwd_addr))
+          return 0;
+  
+  return 1;
+}
+
+#if 0
+/* Calculate ASBR route. */
+static struct ospf_route *
+ospf_ase_calculate_asbr_route (struct ospf *ospf,
+                              struct route_table *rt_network,
+                              struct route_table *rt_router,
+                              struct as_external_lsa *al)
+{
+  struct prefix_ipv4 asbr;
+  struct ospf_route *asbr_route;
+  struct route_node *rn;
+
+  /* Find ASBR route from Router routing table. */
+  asbr.family = AF_INET;
+  asbr.prefix = al->header.adv_router;
+  asbr.prefixlen = IPV4_MAX_BITLEN;
+  apply_mask_ipv4 (&asbr);
+
+  asbr_route = ospf_find_asbr_route (ospf, rt_router, &asbr);
+
+  if (asbr_route == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("ospf_ase_calculate(): Route to ASBR %s not found",
+                   inet_ntoa (asbr.prefix));
+      return NULL;
+    }
+
+  if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("ospf_ase_calculate(): Originating router is not an ASBR");
+      return NULL;
+    }
+   
+  if (al->e[0].fwd_addr.s_addr != 0)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("ospf_ase_calculate(): "
+                   "Forwarding address is not 0.0.0.0.");
+
+      if (! ospf_ase_forward_address_check (ospf, al->e[0].fwd_addr))
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("ospf_ase_calculate(): "
+                       "Forwarding address is one of our addresses, Ignore.");
+         return NULL;
+        }
+
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("ospf_ase_calculate(): "
+                   "Looking up in the Network Routing Table.");
+
+      /* Looking up the path to the fwd_addr from Network route. */
+      asbr.family = AF_INET;
+      asbr.prefix = al->e[0].fwd_addr;
+      asbr.prefixlen = IPV4_MAX_BITLEN;
+
+      rn = route_node_match (rt_network, (struct prefix *) &asbr);
+   
+      if (rn == NULL)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("ospf_ase_calculate(): "
+                       "Couldn't find a route to the forwarding address.");
+         return NULL;
+       }
+
+      route_unlock_node (rn);
+
+      if ((asbr_route = rn->info) == NULL)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("ospf_ase_calculate(): "
+                       "Somehow OSPF route to ASBR is lost");
+         return NULL;
+       }
+    }
+
+  return asbr_route;
+}
+#endif
+
+static struct ospf_route *
+ospf_ase_calculate_new_route (struct ospf_lsa *lsa,
+                             struct ospf_route *asbr_route, u_int32_t metric)
+{
+  struct as_external_lsa *al;
+  struct ospf_route *new;
+
+  al = (struct as_external_lsa *) lsa->data;
+
+  new = ospf_route_new ();
+
+  /* Set redistributed type -- does make sense? */
+  /* new->type = type; */
+  new->id = al->header.id;
+  new->mask = al->mask;
+
+  if (!IS_EXTERNAL_METRIC (al->e[0].tos))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: type-1 created.");
+      new->path_type = OSPF_PATH_TYPE1_EXTERNAL;
+      new->cost = asbr_route->cost + metric;           /* X + Y */
+    }
+  else
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: type-2 created.");
+      new->path_type = OSPF_PATH_TYPE2_EXTERNAL;
+      new->cost = asbr_route->cost;                    /* X */
+      new->u.ext.type2_cost = metric;                  /* Y */
+    }
+
+  new->type = OSPF_DESTINATION_NETWORK;
+  new->u.ext.origin = lsa;
+  new->u.ext.tag = ntohl (al->e[0].route_tag);
+  new->u.ext.asbr = asbr_route;
+
+  assert (new != asbr_route);
+
+  return new;
+}
+
+#define OSPF_ASE_CALC_INTERVAL 1
+
+int
+ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa)
+{
+  u_int32_t metric;
+  struct as_external_lsa *al;
+  struct ospf_route *asbr_route;
+  struct prefix_ipv4 asbr, p;
+  struct route_node *rn;
+  struct ospf_route *new, *or;
+  int ret;
+  
+  assert (lsa);
+  al = (struct as_external_lsa *) lsa->data;
+
+  if (lsa->data->type == OSPF_AS_NSSA_LSA)
+    if (IS_DEBUG_OSPF_NSSA)
+      zlog_debug ("ospf_ase_calc(): Processing Type-7");
+
+  /* Stay away from any Local Translated Type-7 LSAs */
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+       zlog_debug ("ospf_ase_calc(): Rejecting Local Xlt'd");
+      return 0;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA))
+    zlog_debug ("Route[External]: Calculate AS-external-LSA to %s/%d",
+               inet_ntoa (al->header.id), ip_masklen (al->mask));
+  /* (1) If the cost specified by the LSA is LSInfinity, or if the
+         LSA's LS age is equal to MaxAge, then examine the next LSA. */
+  if ((metric = GET_METRIC (al->e[0].metric)) >= OSPF_LS_INFINITY)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: Metric is OSPF_LS_INFINITY");
+      return 0;
+    }
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: AS-external-LSA is MAXAGE");
+      return 0;
+    }
+  
+  /* (2) If the LSA was originated by the calculating router itself,
+     examine the next LSA. */
+  if (IS_LSA_SELF (lsa))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: AS-external-LSA is self originated");
+      return 0;
+    }
+
+  /* (3) Call the destination described by the LSA N.  N's address is
+         obtained by masking the LSA's Link State ID with the
+        network/subnet mask contained in the body of the LSA.  Look
+        up the routing table entries (potentially one per attached
+        area) for the AS boundary router (ASBR) that originated the
+        LSA. If no entries exist for router ASBR (i.e., ASBR is
+        unreachable), do nothing with this LSA and consider the next
+        in the list. */
+  
+  asbr.family = AF_INET;
+  asbr.prefix = al->header.adv_router;
+  asbr.prefixlen = IPV4_MAX_BITLEN;
+  apply_mask_ipv4 (&asbr);
+  
+  asbr_route = ospf_find_asbr_route (ospf, ospf->new_rtrs, &asbr);
+  if (asbr_route == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: Can't find originating ASBR route");
+      return 0;
+    }
+  if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: Originating router is not an ASBR");
+      return 0;
+    }
+  
+  /*     Else, this LSA describes an AS external path to destination
+        N.  Examine the forwarding address specified in the AS-
+        external-LSA.  This indicates the IP address to which
+        packets for the destination should be forwarded. */
+  
+  if (al->e[0].fwd_addr.s_addr == 0)
+    {
+      /* If the forwarding address is set to 0.0.0.0, packets should
+        be sent to the ASBR itself. Among the multiple routing table
+        entries for the ASBR, select the preferred entry as follows.
+        If RFC1583Compatibility is set to "disabled", prune the set
+        of routing table entries for the ASBR as described in
+        Section 16.4.1. In any case, among the remaining routing
+        table entries, select the routing table entry with the least
+        cost; when there are multiple least cost routing table
+        entries the entry whose associated area has the largest OSPF
+        Area ID (when considered as an unsigned 32-bit integer) is
+        chosen. */
+
+      /* asbr_route already contains the requested route */
+    }
+  else
+    {
+      /* If the forwarding address is non-zero, look up the
+        forwarding address in the routing table.[24] The matching
+        routing table entry must specify an intra-area or inter-area
+        path; if no such path exists, do nothing with the LSA and
+        consider the next in the list. */
+      if (! ospf_ase_forward_address_check (ospf, al->e[0].fwd_addr))
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("Route[External]: Forwarding address is our router "
+                       "address");
+         return 0;
+       }
+      
+      asbr.family = AF_INET;
+      asbr.prefix = al->e[0].fwd_addr;
+      asbr.prefixlen = IPV4_MAX_BITLEN;
+
+      rn = route_node_match (ospf->new_table, (struct prefix *) &asbr);
+      
+      if (rn == NULL || (asbr_route = rn->info) == NULL)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("Route[External]: Can't find route to forwarding "
+                       "address");
+         if (rn)
+           route_unlock_node (rn);
+         return 0;
+       }
+
+      route_unlock_node (rn);
+    }
+
+  /* (4) Let X be the cost specified by the preferred routing table
+         entry for the ASBR/forwarding address, and Y the cost
+        specified in the LSA.  X is in terms of the link state
+        metric, and Y is a type 1 or 2 external metric. */
+                        
+
+  /* (5) Look up the routing table entry for the destination N.  If
+         no entry exists for N, install the AS external path to N,
+        with next hop equal to the list of next hops to the
+        forwarding address, and advertising router equal to ASBR.
+        If the external metric type is 1, then the path-type is set
+        to type 1 external and the cost is equal to X+Y.  If the
+        external metric type is 2, the path-type is set to type 2
+        external, the link state component of the route's cost is X,
+        and the type 2 cost is Y. */
+  new = ospf_ase_calculate_new_route (lsa, asbr_route, metric);
+
+  /* (6) Compare the AS external path described by the LSA with the
+         existing paths in N's routing table entry, as follows. If
+        the new path is preferred, it replaces the present paths in
+        N's routing table entry.  If the new path is of equal
+        preference, it is added to N's routing table entry's list of
+        paths. */
+
+  /* Set prefix. */
+  p.family = AF_INET;
+  p.prefix = al->header.id;
+  p.prefixlen = ip_masklen (al->mask);
+
+  /* if there is a Intra/Inter area route to the N
+     do not install external route */
+  if ((rn = route_node_lookup (ospf->new_table,
+                             (struct prefix *) &p)))
+    {
+      route_unlock_node(rn);
+      if (rn->info == NULL)
+       zlog_info ("Route[External]: rn->info NULL");
+      if (new)
+       ospf_route_free (new);
+      return 0;
+    }
+  /* Find a route to the same dest */
+  /* If there is no route, create new one. */
+  if ((rn = route_node_lookup (ospf->new_external_route,
+                              (struct prefix *) &p)))
+      route_unlock_node(rn);
+
+  if (!rn || (or = rn->info) == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA))
+       zlog_debug ("Route[External]: Adding a new route %s/%d",
+                   inet_ntoa (p.prefix), p.prefixlen);
+
+      ospf_route_add (ospf->new_external_route, &p, new, asbr_route);
+
+      if (al->e[0].fwd_addr.s_addr)
+       ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr);
+      return 0;
+    }
+  else
+    {
+      /* (a) Intra-area and inter-area paths are always preferred
+             over AS external paths.
+
+         (b) Type 1 external paths are always preferred over type 2
+             external paths. When all paths are type 2 external
+            paths, the paths with the smallest advertised type 2
+            metric are always preferred. */
+      ret = ospf_route_cmp (ospf, new, or);
+  
+  /*     (c) If the new AS external path is still indistinguishable
+             from the current paths in the N's routing table entry,
+            and RFC1583Compatibility is set to "disabled", select
+            the preferred paths based on the intra-AS paths to the
+            ASBR/forwarding addresses, as specified in Section
+            16.4.1.
+
+         (d) If the new AS external path is still indistinguishable
+             from the current paths in the N's routing table entry,
+            select the preferred path based on a least cost
+            comparison.  Type 1 external paths are compared by
+            looking at the sum of the distance to the forwarding
+            address and the advertised type 1 metric (X+Y).  Type 2
+            external paths advertising equal type 2 metrics are
+            compared by looking at the distance to the forwarding
+            addresses.
+  */
+      /* New route is better */
+      if (ret < 0)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("Route[External]: New route is better");
+         ospf_route_subst (rn, new, asbr_route);
+         if (al->e[0].fwd_addr.s_addr)
+           ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr);
+         or = new;
+         new = NULL;
+       }
+      /* Old route is better */
+      else if (ret > 0)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("Route[External]: Old route is better");
+         /* do nothing */
+       }
+      /* Routes are equal */
+      else
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA))
+           zlog_debug ("Route[External]: Routes are equal");
+         ospf_route_copy_nexthops (or, asbr_route->paths);
+         if (al->e[0].fwd_addr.s_addr)
+           ospf_ase_complete_direct_routes (or, al->e[0].fwd_addr);
+       }
+    }
+  /* Make sure setting newly calculated ASBR route.*/
+  or->u.ext.asbr = asbr_route;
+  if (new)
+    ospf_route_free (new);
+
+  lsa->route = or;
+  return 0;
+}
+
+static int
+ospf_ase_route_match_same (struct route_table *rt, struct prefix *prefix,
+                          struct ospf_route *newor)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct ospf_path *op;
+  struct ospf_path *newop;
+  struct listnode *n1;
+  struct listnode *n2;
+
+  if (! rt || ! prefix)
+    return 0;
+
+   rn = route_node_lookup (rt, prefix);
+   if (! rn)
+     return 0;
+   route_unlock_node (rn);
+
+   or = rn->info;
+   if (or->path_type != newor->path_type)
+     return 0;
+
+   switch (or->path_type)
+     {
+     case OSPF_PATH_TYPE1_EXTERNAL:
+       if (or->cost != newor->cost)
+        return 0;
+       break;
+     case OSPF_PATH_TYPE2_EXTERNAL:
+       if ((or->cost != newor->cost) ||
+          (or->u.ext.type2_cost != newor->u.ext.type2_cost))
+        return 0;
+       break;
+     default:
+       assert (0);
+       return 0;
+     }
+   
+   if (or->paths->count != newor->paths->count)
+     return 0;
+       
+   /* Check each path. */
+   for (n1 = listhead (or->paths), n2 = listhead (newor->paths);
+       n1 && n2; n1 = listnextnode (n1), n2 = listnextnode (n2))
+     { 
+       op = listgetdata (n1);
+       newop = listgetdata (n2);
+       
+       if (! IPV4_ADDR_SAME (&op->nexthop, &newop->nexthop))
+        return 0;
+       if (op->ifindex != newop->ifindex)
+        return 0;
+     }
+
+   if (or->u.ext.tag != newor->u.ext.tag)
+     return 0;
+
+   return 1;
+}
+
+static int
+ospf_ase_compare_tables (struct route_table *new_external_route,
+                        struct route_table *old_external_route)
+{
+  struct route_node *rn, *new_rn;
+  struct ospf_route *or;
+  
+  /* Remove deleted routes */
+  for (rn = route_top (old_external_route); rn; rn = route_next (rn))
+    if ((or = rn->info))
+      {
+       if (! (new_rn = route_node_lookup (new_external_route, &rn->p)))
+         ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or);
+       else
+         route_unlock_node (new_rn);
+      }
+  
+       
+  /* Install new routes */
+  for (rn = route_top (new_external_route); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      if (! ospf_ase_route_match_same (old_external_route, &rn->p, or))
+       ospf_zebra_add ((struct prefix_ipv4 *) &rn->p, or);
+                                      
+  return 0;
+}
+
+static int
+ospf_ase_calculate_timer (struct thread *t)
+{
+  struct ospf *ospf;
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+  struct listnode *node;
+  struct ospf_area *area;
+  struct timeval start_time, stop_time;
+
+  ospf = THREAD_ARG (t);
+  ospf->t_ase_calc = NULL;
+
+  if (ospf->ase_calc)
+    {
+      ospf->ase_calc = 0;
+
+      quagga_gettime(QUAGGA_CLK_MONOTONIC, &start_time);
+
+      /* Calculate external route for each AS-external-LSA */
+      LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+       ospf_ase_calculate_route (ospf, lsa);
+
+      /*  This version simple adds to the table all NSSA areas  */
+      if (ospf->anyNSSA)
+       for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+         {
+           if (IS_DEBUG_OSPF_NSSA)
+             zlog_debug ("ospf_ase_calculate_timer(): looking at area %s",
+                        inet_ntoa (area->area_id));
+
+           if (area->external_routing == OSPF_AREA_NSSA)
+             LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
+               ospf_ase_calculate_route (ospf, lsa);
+         }
+      /* kevinm: And add the NSSA routes in ospf_top */
+      LSDB_LOOP (NSSA_LSDB (ospf),rn,lsa)
+               ospf_ase_calculate_route(ospf,lsa);
+
+      /* Compare old and new external routing table and install the
+        difference info zebra/kernel */
+      ospf_ase_compare_tables (ospf->new_external_route,
+                              ospf->old_external_route);
+
+      /* Delete old external routing table */
+      ospf_route_table_free (ospf->old_external_route);
+      ospf->old_external_route = ospf->new_external_route;
+      ospf->new_external_route = route_table_init ();
+
+      quagga_gettime(QUAGGA_CLK_MONOTONIC, &stop_time);
+
+      zlog_info ("SPF Processing Time(usecs): External Routes: %lld\n",
+                (stop_time.tv_sec - start_time.tv_sec)*1000000LL+
+                (stop_time.tv_usec - start_time.tv_usec));
+    }
+  return 0;
+}
+
+void
+ospf_ase_calculate_schedule (struct ospf *ospf)
+{
+  if (ospf == NULL)
+    return;
+
+  ospf->ase_calc = 1;
+}
+
+void
+ospf_ase_calculate_timer_add (struct ospf *ospf)
+{
+  if (ospf == NULL)
+    return;
+
+  if (! ospf->t_ase_calc)
+    ospf->t_ase_calc = thread_add_timer (master, ospf_ase_calculate_timer,
+                                        ospf, OSPF_ASE_CALC_INTERVAL);
+}
+
+void
+ospf_ase_register_external_lsa (struct ospf_lsa *lsa, struct ospf *top)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+  struct list *lst;
+  struct as_external_lsa *al;
+
+  al = (struct as_external_lsa *) lsa->data;
+  p.family = AF_INET;
+  p.prefix = lsa->data->id;
+  p.prefixlen = ip_masklen (al->mask);
+  apply_mask_ipv4 (&p);
+
+  rn = route_node_get (top->external_lsas, (struct prefix *) &p);
+  if ((lst = rn->info) == NULL)
+    rn->info = lst = list_new();
+  else
+    route_unlock_node (rn);
+
+  /* We assume that if LSA is deleted from DB
+     is is also deleted from this RT */
+  listnode_add (lst, ospf_lsa_lock (lsa)); /* external_lsas lst */
+}
+
+void
+ospf_ase_unregister_external_lsa (struct ospf_lsa *lsa, struct ospf *top)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+  struct list *lst;
+  struct as_external_lsa *al;
+
+  al = (struct as_external_lsa *) lsa->data;
+  p.family = AF_INET;
+  p.prefix = lsa->data->id;
+  p.prefixlen = ip_masklen (al->mask);
+  apply_mask_ipv4 (&p);
+
+  rn = route_node_lookup (top->external_lsas, (struct prefix *) &p);
+
+  if (rn) {
+    lst = rn->info;
+    listnode_delete (lst, lsa);
+    ospf_lsa_unlock (&lsa); /* external_lsas list */
+    route_unlock_node (rn);
+  }
+}
+
+void
+ospf_ase_external_lsas_finish (struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+  struct list *lst;
+  struct listnode *node, *nnode;
+  
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((lst = rn->info) != NULL)
+      {
+       for (ALL_LIST_ELEMENTS (lst, node, nnode, lsa))
+          ospf_lsa_unlock (&lsa); /* external_lsas lst */
+       list_delete (lst);
+      }
+    
+  route_table_finish (rt);
+}
+
+void
+ospf_ase_incremental_update (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct list *lsas;
+  struct listnode *node;
+  struct route_node *rn, *rn2;
+  struct prefix_ipv4 p;
+  struct route_table *tmp_old;
+  struct as_external_lsa *al;
+
+  al = (struct as_external_lsa *) lsa->data;
+  p.family = AF_INET;
+  p.prefix = lsa->data->id;
+  p.prefixlen = ip_masklen (al->mask);
+  apply_mask_ipv4 (&p);
+
+  /* if new_table is NULL, there was no spf calculation, thus
+     incremental update is unneeded */
+  if (!ospf->new_table)
+    return;
+  
+  /* If there is already an intra-area or inter-area route
+     to the destination, no recalculation is necessary
+     (internal routes take precedence). */
+  
+  rn = route_node_lookup (ospf->new_table, (struct prefix *) &p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      if (rn->info)
+       return;
+    }
+
+  rn = route_node_lookup (ospf->external_lsas, (struct prefix *) &p);
+  assert (rn); 
+  assert (rn->info);
+  lsas = rn->info;
+  route_unlock_node (rn);
+
+  for (ALL_LIST_ELEMENTS_RO (lsas, node, lsa))
+    ospf_ase_calculate_route (ospf, lsa);
+
+  /* prepare temporary old routing table for compare */
+  tmp_old = route_table_init ();
+  rn = route_node_lookup (ospf->old_external_route, (struct prefix *) &p);
+  if (rn && rn->info)
+    {
+      rn2 = route_node_get (tmp_old, (struct prefix *) &p);
+      rn2->info = rn->info;
+      route_unlock_node (rn);
+    }
+
+  /* install changes to zebra */
+  ospf_ase_compare_tables (ospf->new_external_route, tmp_old);
+
+  /* update ospf->old_external_route table */
+  if (rn && rn->info)
+    ospf_route_free ((struct ospf_route *) rn->info);
+
+  rn2 = route_node_lookup (ospf->new_external_route, (struct prefix *) &p);
+  /* if new route exists, install it to ospf->old_external_route */
+  if (rn2 && rn2->info)
+    {
+      if (!rn)
+       rn = route_node_get (ospf->old_external_route, (struct prefix *) &p);
+      rn->info = rn2->info;
+    }
+  else
+    {
+      /* remove route node from ospf->old_external_route */
+      if (rn)
+       {
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+    }
+
+  if (rn2)
+    {
+      /* rn2->info is stored in route node of ospf->old_external_route */
+      rn2->info = NULL;
+      route_unlock_node (rn2);
+      route_unlock_node (rn2);
+    }
+
+  route_table_finish (tmp_old);
+}
diff --git a/ospfd/ospf_ase.h b/ospfd/ospf_ase.h
new file mode 100644 (file)
index 0000000..e6a1b2f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * OSPF AS External route calculation.
+ * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ASE_H
+#define _ZEBRA_OSPF_ASE_H
+
+
+extern struct ospf_route *ospf_find_asbr_route (struct ospf *,
+                                               struct route_table *,
+                                               struct prefix_ipv4 *);
+extern struct ospf_route *ospf_find_asbr_route_through_area (struct
+                                                            route_table *,
+                                                            struct
+                                                            prefix_ipv4 *,
+                                                            struct ospf_area
+                                                            *);
+
+extern int ospf_ase_calculate_route (struct ospf *, struct ospf_lsa *);
+extern void ospf_ase_calculate_schedule (struct ospf *);
+extern void ospf_ase_calculate_timer_add (struct ospf *);
+
+extern void ospf_ase_external_lsas_finish (struct route_table *);
+extern void ospf_ase_incremental_update (struct ospf *, struct ospf_lsa *);
+extern void ospf_ase_register_external_lsa (struct ospf_lsa *, struct ospf *);
+extern void ospf_ase_unregister_external_lsa (struct ospf_lsa *,
+                                             struct ospf *);
+
+#endif /* _ZEBRA_OSPF_ASE_H */
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
new file mode 100644 (file)
index 0000000..be9fa2a
--- /dev/null
@@ -0,0 +1,1748 @@
+/*
+ * OSPFd dump routine.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "prefix.h"
+#include "command.h"
+#include "stream.h"
+#include "log.h"
+#include "sockopt.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_network.h"
+
+const struct message ospf_ism_state_msg[] =
+{
+  { ISM_DependUpon,   "DependUpon" },
+  { ISM_Down,         "Down" },
+  { ISM_Loopback,     "Loopback" },
+  { ISM_Waiting,      "Waiting" },
+  { ISM_PointToPoint, "Point-To-Point" },
+  { ISM_DROther,      "DROther" },
+  { ISM_Backup,       "Backup" },
+  { ISM_DR,           "DR" },
+};
+const int ospf_ism_state_msg_max = OSPF_ISM_STATE_MAX;
+
+const struct message ospf_nsm_state_msg[] =
+{
+  { NSM_DependUpon, "DependUpon" },
+  { NSM_Deleted,    "Deleted"    },
+  { NSM_Down,       "Down" },
+  { NSM_Attempt,    "Attempt" },
+  { NSM_Init,       "Init" },
+  { NSM_TwoWay,     "2-Way" },
+  { NSM_ExStart,    "ExStart" },
+  { NSM_Exchange,   "Exchange" },
+  { NSM_Loading,    "Loading" },
+  { NSM_Full,       "Full" },
+};
+const int ospf_nsm_state_msg_max = OSPF_NSM_STATE_MAX;
+
+const struct message ospf_lsa_type_msg[] =
+{
+  { OSPF_UNKNOWN_LSA,      "unknown" },
+  { OSPF_ROUTER_LSA,       "router-LSA" },
+  { OSPF_NETWORK_LSA,      "network-LSA" },
+  { OSPF_SUMMARY_LSA,      "summary-LSA" },
+  { OSPF_ASBR_SUMMARY_LSA, "summary-LSA" },
+  { OSPF_AS_EXTERNAL_LSA,  "AS-external-LSA" },
+  { OSPF_GROUP_MEMBER_LSA, "GROUP MEMBER LSA" },
+  { OSPF_AS_NSSA_LSA,      "NSSA-LSA" },
+  { 8,                     "Type-8 LSA" },
+  { OSPF_OPAQUE_LINK_LSA,  "Link-Local Opaque-LSA" },
+  { OSPF_OPAQUE_AREA_LSA,  "Area-Local Opaque-LSA" },
+  { OSPF_OPAQUE_AS_LSA,    "AS-external Opaque-LSA" },
+};
+const int ospf_lsa_type_msg_max = OSPF_MAX_LSA;
+
+const struct message ospf_link_state_id_type_msg[] =
+{
+  { OSPF_UNKNOWN_LSA,      "(unknown)" },
+  { OSPF_ROUTER_LSA,       "" },
+  { OSPF_NETWORK_LSA,      "(address of Designated Router)" },
+  { OSPF_SUMMARY_LSA,      "(summary Network Number)" },
+  { OSPF_ASBR_SUMMARY_LSA, "(AS Boundary Router address)" },
+  { OSPF_AS_EXTERNAL_LSA,  "(External Network Number)" },
+  { OSPF_GROUP_MEMBER_LSA, "(Group membership information)" },
+  { OSPF_AS_NSSA_LSA,      "(External Network Number for NSSA)" },
+  { 8,                     "(Type-8 LSID)" },
+  { OSPF_OPAQUE_LINK_LSA,  "(Link-Local Opaque-Type/ID)" },
+  { OSPF_OPAQUE_AREA_LSA,  "(Area-Local Opaque-Type/ID)" },
+  { OSPF_OPAQUE_AS_LSA,    "(AS-external Opaque-Type/ID)" },
+};
+const int ospf_link_state_id_type_msg_max = OSPF_MAX_LSA;
+
+const struct message ospf_network_type_msg[] =
+{
+  { OSPF_IFTYPE_NONE,            "NONE" },
+  { OSPF_IFTYPE_POINTOPOINT,      "Point-to-Point" },
+  { OSPF_IFTYPE_BROADCAST,        "Broadcast" },
+  { OSPF_IFTYPE_NBMA,             "NBMA" },
+  { OSPF_IFTYPE_POINTOMULTIPOINT, "Point-to-MultiPoint" },
+  { OSPF_IFTYPE_VIRTUALLINK,      "Virtual-Link" },
+};
+const int ospf_network_type_msg_max = OSPF_IFTYPE_MAX;
+
+/* AuType */
+const struct message ospf_auth_type_str[] =
+{
+  { OSPF_AUTH_NULL,          "Null"          },
+  { OSPF_AUTH_SIMPLE,        "Simple"        },
+  { OSPF_AUTH_CRYPTOGRAPHIC, "Cryptographic" },
+};
+const size_t ospf_auth_type_str_max = sizeof (ospf_auth_type_str) /
+  sizeof (ospf_auth_type_str[0]);
+
+/* Configuration debug option variables. */
+unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
+unsigned long conf_debug_ospf_event = 0;
+unsigned long conf_debug_ospf_ism = 0;
+unsigned long conf_debug_ospf_nsm = 0;
+unsigned long conf_debug_ospf_lsa = 0;
+unsigned long conf_debug_ospf_zebra = 0;
+unsigned long conf_debug_ospf_nssa = 0;
+unsigned long conf_debug_ospf_te = 0;
+
+/* Enable debug option variables -- valid only session. */
+unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
+unsigned long term_debug_ospf_event = 0;
+unsigned long term_debug_ospf_ism = 0;
+unsigned long term_debug_ospf_nsm = 0;
+unsigned long term_debug_ospf_lsa = 0;
+unsigned long term_debug_ospf_zebra = 0;
+unsigned long term_debug_ospf_nssa = 0;
+unsigned long term_debug_ospf_te = 0;
+
+
+const char *
+ospf_redist_string(u_int route_type)
+{
+  return (route_type == ZEBRA_ROUTE_MAX) ?
+        "Default" : zebra_route_string(route_type);
+}
+
+#define OSPF_AREA_STRING_MAXLEN  16
+const char *
+ospf_area_name_string (struct ospf_area *area)
+{
+  static char buf[OSPF_AREA_STRING_MAXLEN] = "";
+  u_int32_t area_id;
+
+  if (!area)
+    return "-";
+
+  area_id = ntohl (area->area_id.s_addr);
+  snprintf (buf, OSPF_AREA_STRING_MAXLEN, "%d.%d.%d.%d",
+            (area_id >> 24) & 0xff, (area_id >> 16) & 0xff,
+            (area_id >> 8) & 0xff, area_id & 0xff);
+  return buf;
+}
+
+#define OSPF_AREA_DESC_STRING_MAXLEN  23
+const char *
+ospf_area_desc_string (struct ospf_area *area)
+{
+  static char buf[OSPF_AREA_DESC_STRING_MAXLEN] = "";
+  u_char type;
+
+  if (!area)
+    return "(incomplete)";
+
+  type = area->external_routing;
+  switch (type)
+    {
+    case OSPF_AREA_NSSA:
+      snprintf (buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [NSSA]",
+                ospf_area_name_string (area));
+      break;
+    case OSPF_AREA_STUB:
+      snprintf (buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [Stub]",
+                ospf_area_name_string (area));
+      break;
+    default:
+      return ospf_area_name_string (area);
+    }
+
+  return buf;
+}
+
+#define OSPF_IF_STRING_MAXLEN  40
+const char *
+ospf_if_name_string (struct ospf_interface *oi)
+{
+  static char buf[OSPF_IF_STRING_MAXLEN] = "";
+  u_int32_t ifaddr;
+
+  if (!oi)
+    return "inactive";
+
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    return oi->ifp->name;
+
+  ifaddr = ntohl (oi->address->u.prefix4.s_addr);
+  snprintf (buf, OSPF_IF_STRING_MAXLEN,
+            "%s:%d.%d.%d.%d", oi->ifp->name,
+            (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff,
+            (ifaddr >> 8) & 0xff, ifaddr & 0xff);
+  return buf;
+}
+
+
+void
+ospf_nbr_state_message (struct ospf_neighbor *nbr, char *buf, size_t size)
+{
+  int state;
+  struct ospf_interface *oi = nbr->oi;
+
+  if (IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4))
+    state = ISM_DR;
+  else if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4))
+    state = ISM_Backup;
+  else
+    state = ISM_DROther;
+
+  memset (buf, 0, size);
+
+  snprintf (buf, size, "%s/%s",
+           LOOKUP (ospf_nsm_state_msg, nbr->state),
+           LOOKUP (ospf_ism_state_msg, state));
+}
+
+const char *
+ospf_timeval_dump (struct timeval *t, char *buf, size_t size)
+{
+  /* Making formatted timer strings. */
+#define MINUTE_IN_SECONDS      60
+#define HOUR_IN_SECONDS                (60*MINUTE_IN_SECONDS)
+#define DAY_IN_SECONDS         (24*HOUR_IN_SECONDS)
+#define WEEK_IN_SECONDS                (7*DAY_IN_SECONDS)
+  unsigned long w, d, h, m, s, ms, us;
+  
+  if (!t)
+    return "inactive";
+  
+  w = d = h = m = s = ms = us = 0;
+  memset (buf, 0, size);
+
+  us = t->tv_usec;
+  if (us >= 1000)
+    {
+      ms = us / 1000;
+      us %= 1000;
+    }
+
+  if (ms >= 1000)
+    {
+      t->tv_sec += ms / 1000;
+      ms %= 1000;
+    }
+  
+  if (t->tv_sec > WEEK_IN_SECONDS)
+    {
+      w = t->tv_sec / WEEK_IN_SECONDS;
+      t->tv_sec -= w * WEEK_IN_SECONDS;
+    }
+  
+  if (t->tv_sec > DAY_IN_SECONDS)
+    {
+      d = t->tv_sec / DAY_IN_SECONDS;
+      t->tv_sec -= d * DAY_IN_SECONDS;
+    }
+  
+  if (t->tv_sec >= HOUR_IN_SECONDS)
+    {
+      h = t->tv_sec / HOUR_IN_SECONDS;
+      t->tv_sec -= h * HOUR_IN_SECONDS;
+    }
+  
+  if (t->tv_sec >= MINUTE_IN_SECONDS)
+    {
+      m = t->tv_sec / MINUTE_IN_SECONDS;
+      t->tv_sec -= m * MINUTE_IN_SECONDS;
+    }
+  
+  if (w > 99)
+    snprintf (buf, size, "%ldw%1ldd", w, d);
+  else if (w)
+    snprintf (buf, size, "%ldw%1ldd%02ldh", w, d, h);
+  else if (d)
+    snprintf (buf, size, "%1ldd%02ldh%02ldm", d, h, m);
+  else if (h)
+    snprintf (buf, size, "%ldh%02ldm%02lds", h, m, (long)t->tv_sec);
+  else if (m)
+    snprintf (buf, size, "%ldm%02lds", m, (long)t->tv_sec);
+  else if (ms)
+    snprintf (buf, size, "%ld.%03lds", (long)t->tv_sec, ms);
+  else
+    snprintf (buf, size, "%ld usecs", (long)t->tv_usec);
+
+  return buf;
+}
+
+const char *
+ospf_timer_dump (struct thread *t, char *buf, size_t size)
+{
+  struct timeval result;
+  if (!t)
+    return "inactive";
+  
+  result = tv_sub (t->u.sands, recent_relative_time());
+  return ospf_timeval_dump (&result, buf, size);
+}
+
+#define OSPF_OPTION_STR_MAXLEN         24
+
+char *
+ospf_options_dump (u_char options)
+{
+  static char buf[OSPF_OPTION_STR_MAXLEN];
+
+  snprintf (buf, OSPF_OPTION_STR_MAXLEN, "*|%s|%s|%s|%s|%s|%s|%s",
+           (options & OSPF_OPTION_O) ? "O" : "-",
+           (options & OSPF_OPTION_DC) ? "DC" : "-",
+           (options & OSPF_OPTION_EA) ? "EA" : "-",
+           (options & OSPF_OPTION_NP) ? "N/P" : "-",
+           (options & OSPF_OPTION_MC) ? "MC" : "-",
+           (options & OSPF_OPTION_E) ? "E" : "-",
+           (options & OSPF_OPTION_MT) ? "M/T" : "-");
+
+  return buf;
+}
+
+static void
+ospf_packet_hello_dump (struct stream *s, u_int16_t length)
+{
+  struct ospf_hello *hello;
+  int i;
+
+  hello = (struct ospf_hello *) STREAM_PNT (s);
+
+  zlog_debug ("Hello");
+  zlog_debug ("  NetworkMask %s", inet_ntoa (hello->network_mask));
+  zlog_debug ("  HelloInterval %d", ntohs (hello->hello_interval));
+  zlog_debug ("  Options %d (%s)", hello->options,
+            ospf_options_dump (hello->options));
+  zlog_debug ("  RtrPriority %d", hello->priority);
+  zlog_debug ("  RtrDeadInterval %ld", (u_long)ntohl (hello->dead_interval));
+  zlog_debug ("  DRouter %s", inet_ntoa (hello->d_router));
+  zlog_debug ("  BDRouter %s", inet_ntoa (hello->bd_router));
+
+  length -= OSPF_HEADER_SIZE + OSPF_HELLO_MIN_SIZE;
+  zlog_debug ("  # Neighbors %d", length / 4);
+  for (i = 0; length > 0; i++, length -= sizeof (struct in_addr))
+    zlog_debug ("    Neighbor %s", inet_ntoa (hello->neighbors[i]));
+}
+
+static char *
+ospf_dd_flags_dump (u_char flags, char *buf, size_t size)
+{
+  memset (buf, 0, size);
+
+  snprintf (buf, size, "%s|%s|%s",
+           (flags & OSPF_DD_FLAG_I) ? "I" : "-",
+           (flags & OSPF_DD_FLAG_M) ? "M" : "-",
+           (flags & OSPF_DD_FLAG_MS) ? "MS" : "-");
+
+  return buf;
+}
+
+void
+ospf_lsa_header_dump (struct lsa_header *lsah)
+{
+  const char *lsah_type = LOOKUP (ospf_lsa_type_msg, lsah->type);
+  
+  zlog_debug ("  LSA Header");
+  zlog_debug ("    LS age %d", ntohs (lsah->ls_age));
+  zlog_debug ("    Options %d (%s)", lsah->options,
+            ospf_options_dump (lsah->options));
+  zlog_debug ("    LS type %d (%s)", lsah->type,
+             (lsah->type ? lsah_type : "unknown type"));
+  zlog_debug ("    Link State ID %s", inet_ntoa (lsah->id));
+  zlog_debug ("    Advertising Router %s", inet_ntoa (lsah->adv_router));
+  zlog_debug ("    LS sequence number 0x%lx", (u_long)ntohl (lsah->ls_seqnum));
+  zlog_debug ("    LS checksum 0x%x", ntohs (lsah->checksum));
+  zlog_debug ("    length %d", ntohs (lsah->length));
+}
+
+static char *
+ospf_router_lsa_flags_dump (u_char flags, char *buf, size_t size)
+{
+  memset (buf, 0, size);
+
+  snprintf (buf, size, "%s|%s|%s",
+           (flags & ROUTER_LSA_VIRTUAL) ? "V" : "-",
+           (flags & ROUTER_LSA_EXTERNAL) ? "E" : "-",
+           (flags & ROUTER_LSA_BORDER) ? "B" : "-");
+
+  return buf;
+}
+
+static void
+ospf_router_lsa_dump (struct stream *s, u_int16_t length)
+{
+  char buf[BUFSIZ];
+  struct router_lsa *rl;
+  int i, len;
+
+  rl = (struct router_lsa *) STREAM_PNT (s);
+
+  zlog_debug ("  Router-LSA");
+  zlog_debug ("    flags %s", 
+            ospf_router_lsa_flags_dump (rl->flags, buf, BUFSIZ));
+  zlog_debug ("    # links %d", ntohs (rl->links));
+
+  len = ntohs (rl->header.length) - OSPF_LSA_HEADER_SIZE - 4;
+  for (i = 0; len > 0; i++)
+    {
+      zlog_debug ("    Link ID %s", inet_ntoa (rl->link[i].link_id));
+      zlog_debug ("    Link Data %s", inet_ntoa (rl->link[i].link_data));
+      zlog_debug ("    Type %d", (u_char) rl->link[i].type);
+      zlog_debug ("    TOS %d", (u_char) rl->link[i].tos);
+      zlog_debug ("    metric %d", ntohs (rl->link[i].metric));
+
+      len -= 12;
+    }
+}
+
+static void
+ospf_network_lsa_dump (struct stream *s, u_int16_t length)
+{
+  struct network_lsa *nl;
+  int i, cnt;
+
+  nl = (struct network_lsa *) STREAM_PNT (s);
+  cnt = (ntohs (nl->header.length) - (OSPF_LSA_HEADER_SIZE + 4)) / 4;
+  
+  zlog_debug ("  Network-LSA");
+  /*
+  zlog_debug ("LSA total size %d", ntohs (nl->header.length));
+  zlog_debug ("Network-LSA size %d", 
+  ntohs (nl->header.length) - OSPF_LSA_HEADER_SIZE);
+  */
+  zlog_debug ("    Network Mask %s", inet_ntoa (nl->mask));
+  zlog_debug ("    # Attached Routers %d", cnt);
+  for (i = 0; i < cnt; i++)
+    zlog_debug ("      Attached Router %s", inet_ntoa (nl->routers[i]));
+}
+
+static void
+ospf_summary_lsa_dump (struct stream *s, u_int16_t length)
+{
+  struct summary_lsa *sl;
+  int size;
+  int i;
+
+  sl = (struct summary_lsa *) STREAM_PNT (s);
+
+  zlog_debug ("  Summary-LSA");
+  zlog_debug ("    Network Mask %s", inet_ntoa (sl->mask));
+
+  size = ntohs (sl->header.length) - OSPF_LSA_HEADER_SIZE - 4;
+  for (i = 0; size > 0; size -= 4, i++)
+    zlog_debug ("    TOS=%d metric %d", sl->tos,
+              GET_METRIC (sl->metric));
+}
+
+static void
+ospf_as_external_lsa_dump (struct stream *s, u_int16_t length)
+{
+  struct as_external_lsa *al;
+  int size;
+  int i;
+
+  al = (struct as_external_lsa *) STREAM_PNT (s);
+  zlog_debug ("  %s", ospf_lsa_type_msg[al->header.type].str);
+  zlog_debug ("    Network Mask %s", inet_ntoa (al->mask));
+
+  size = ntohs (al->header.length) - OSPF_LSA_HEADER_SIZE -4;
+  for (i = 0; size > 0; size -= 12, i++)
+    {
+      zlog_debug ("    bit %s TOS=%d metric %d",
+                IS_EXTERNAL_METRIC (al->e[i].tos) ? "E" : "-",
+                al->e[i].tos & 0x7f, GET_METRIC (al->e[i].metric));
+      zlog_debug ("    Forwarding address %s", inet_ntoa (al->e[i].fwd_addr));
+      zlog_debug ("    External Route Tag %u", al->e[i].route_tag);
+    }
+}
+
+static void
+ospf_lsa_header_list_dump (struct stream *s, u_int16_t length)
+{
+  struct lsa_header *lsa;
+
+  zlog_debug ("  # LSA Headers %d", length / OSPF_LSA_HEADER_SIZE);
+
+  /* LSA Headers. */
+  while (length > 0)
+    {
+      lsa = (struct lsa_header *) STREAM_PNT (s);
+      ospf_lsa_header_dump (lsa);
+
+      stream_forward_getp (s, OSPF_LSA_HEADER_SIZE);
+      length -= OSPF_LSA_HEADER_SIZE;
+    }
+}
+
+static void
+ospf_packet_db_desc_dump (struct stream *s, u_int16_t length)
+{
+  struct ospf_db_desc *dd;
+  char dd_flags[8];
+
+  u_int32_t gp;
+
+  gp = stream_get_getp (s);
+  dd = (struct ospf_db_desc *) STREAM_PNT (s);
+
+  zlog_debug ("Database Description");
+  zlog_debug ("  Interface MTU %d", ntohs (dd->mtu));
+  zlog_debug ("  Options %d (%s)", dd->options,
+            ospf_options_dump (dd->options));
+  zlog_debug ("  Flags %d (%s)", dd->flags,
+            ospf_dd_flags_dump (dd->flags, dd_flags, sizeof dd_flags));
+  zlog_debug ("  Sequence Number 0x%08lx", (u_long)ntohl (dd->dd_seqnum));
+
+  length -= OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE;
+
+  stream_forward_getp (s, OSPF_DB_DESC_MIN_SIZE);
+
+  ospf_lsa_header_list_dump (s, length);
+
+  stream_set_getp (s, gp);
+}
+
+static void
+ospf_packet_ls_req_dump (struct stream *s, u_int16_t length)
+{
+  u_int32_t sp;
+  u_int32_t ls_type;
+  struct in_addr ls_id;
+  struct in_addr adv_router;
+
+  sp = stream_get_getp (s);
+
+  length -= OSPF_HEADER_SIZE;
+
+  zlog_debug ("Link State Request");
+  zlog_debug ("  # Requests %d", length / 12);
+
+  for (; length > 0; length -= 12)
+    {
+      ls_type = stream_getl (s);
+      ls_id.s_addr = stream_get_ipv4 (s);
+      adv_router.s_addr = stream_get_ipv4 (s);
+
+      zlog_debug ("  LS type %d", ls_type);
+      zlog_debug ("  Link State ID %s", inet_ntoa (ls_id));
+      zlog_debug ("  Advertising Router %s",
+                inet_ntoa (adv_router));
+    }
+
+  stream_set_getp (s, sp);
+}
+
+static void
+ospf_packet_ls_upd_dump (struct stream *s, u_int16_t length)
+{
+  u_int32_t sp;
+  struct lsa_header *lsa;
+  int lsa_len;
+  u_int32_t count;
+
+  length -= OSPF_HEADER_SIZE;
+
+  sp = stream_get_getp (s);
+
+  count = stream_getl (s);
+  length -= 4;
+
+  zlog_debug ("Link State Update");
+  zlog_debug ("  # LSAs %d", count);
+
+  while (length > 0 && count > 0)
+    {
+      if (length < OSPF_HEADER_SIZE || length % 4 != 0)
+       {
+          zlog_debug ("  Remaining %d bytes; Incorrect length.", length);
+         break;
+       }
+
+      lsa = (struct lsa_header *) STREAM_PNT (s);
+      lsa_len = ntohs (lsa->length);
+      ospf_lsa_header_dump (lsa);
+
+      switch (lsa->type)
+       {
+       case OSPF_ROUTER_LSA:
+         ospf_router_lsa_dump (s, length);
+         break;
+       case OSPF_NETWORK_LSA:
+         ospf_network_lsa_dump (s, length);
+         break;
+       case OSPF_SUMMARY_LSA:
+       case OSPF_ASBR_SUMMARY_LSA:
+         ospf_summary_lsa_dump (s, length);
+         break;
+       case OSPF_AS_EXTERNAL_LSA:
+         ospf_as_external_lsa_dump (s, length);
+         break;
+       case OSPF_AS_NSSA_LSA:
+         ospf_as_external_lsa_dump (s, length);
+         break;
+       case OSPF_OPAQUE_LINK_LSA:
+       case OSPF_OPAQUE_AREA_LSA:
+       case OSPF_OPAQUE_AS_LSA:
+         ospf_opaque_lsa_dump (s, length);
+         break;
+       default:
+         break;
+       }
+
+      stream_forward_getp (s, lsa_len);
+      length -= lsa_len;
+      count--;
+    }
+
+  stream_set_getp (s, sp);
+}
+
+static void
+ospf_packet_ls_ack_dump (struct stream *s, u_int16_t length)
+{
+  u_int32_t sp;
+
+  length -= OSPF_HEADER_SIZE;
+  sp = stream_get_getp (s);
+
+  zlog_debug ("Link State Acknowledgment");
+  ospf_lsa_header_list_dump (s, length);
+
+  stream_set_getp (s, sp);
+}
+
+/* Expects header to be in host order */
+void
+ospf_ip_header_dump (struct ip *iph)
+{
+  /* IP Header dump. */
+  zlog_debug ("ip_v %d", iph->ip_v);
+  zlog_debug ("ip_hl %d", iph->ip_hl);
+  zlog_debug ("ip_tos %d", iph->ip_tos);
+  zlog_debug ("ip_len %d", iph->ip_len);
+  zlog_debug ("ip_id %u", (u_int32_t) iph->ip_id);
+  zlog_debug ("ip_off %u", (u_int32_t) iph->ip_off);
+  zlog_debug ("ip_ttl %d", iph->ip_ttl);
+  zlog_debug ("ip_p %d", iph->ip_p);
+  zlog_debug ("ip_sum 0x%x", (u_int32_t) iph->ip_sum);
+  zlog_debug ("ip_src %s",  inet_ntoa (iph->ip_src));
+  zlog_debug ("ip_dst %s", inet_ntoa (iph->ip_dst));
+}
+
+static void
+ospf_header_dump (struct ospf_header *ospfh)
+{
+  char buf[9];
+  u_int16_t auth_type = ntohs (ospfh->auth_type);
+
+  zlog_debug ("Header");
+  zlog_debug ("  Version %d", ospfh->version);
+  zlog_debug ("  Type %d (%s)", ospfh->type,
+            LOOKUP (ospf_packet_type_str, ospfh->type));
+  zlog_debug ("  Packet Len %d", ntohs (ospfh->length));
+  zlog_debug ("  Router ID %s", inet_ntoa (ospfh->router_id));
+  zlog_debug ("  Area ID %s", inet_ntoa (ospfh->area_id));
+  zlog_debug ("  Checksum 0x%x", ntohs (ospfh->checksum));
+  zlog_debug ("  AuType %s", LOOKUP (ospf_auth_type_str, auth_type));
+
+  switch (auth_type)
+    {
+    case OSPF_AUTH_NULL:
+      break;
+    case OSPF_AUTH_SIMPLE:
+      memset (buf, 0, 9);
+      strncpy (buf, (char *) ospfh->u.auth_data, 8);
+      zlog_debug ("  Simple Password %s", buf);
+      break;
+    case OSPF_AUTH_CRYPTOGRAPHIC:
+      zlog_debug ("  Cryptographic Authentication");
+      zlog_debug ("  Key ID %d", ospfh->u.crypt.key_id);
+      zlog_debug ("  Auth Data Len %d", ospfh->u.crypt.auth_data_len);
+      zlog_debug ("  Sequence number %ld",
+                (u_long)ntohl (ospfh->u.crypt.crypt_seqnum));
+      break;
+    default:
+      zlog_debug ("* This is not supported authentication type");
+      break;
+    }
+    
+}
+
+void
+ospf_packet_dump (struct stream *s)
+{
+  struct ospf_header *ospfh;
+  unsigned long gp;
+
+  /* Preserve pointer. */
+  gp = stream_get_getp (s);
+
+  /* OSPF Header dump. */
+  ospfh = (struct ospf_header *) STREAM_PNT (s);
+
+  /* Until detail flag is set, return. */
+  if (!(term_debug_ospf_packet[ospfh->type - 1] & OSPF_DEBUG_DETAIL))
+    return;
+
+  /* Show OSPF header detail. */
+  ospf_header_dump (ospfh);
+  stream_forward_getp (s, OSPF_HEADER_SIZE);
+
+  switch (ospfh->type)
+    {
+    case OSPF_MSG_HELLO:
+      ospf_packet_hello_dump (s, ntohs (ospfh->length));
+      break;
+    case OSPF_MSG_DB_DESC:
+      ospf_packet_db_desc_dump (s, ntohs (ospfh->length));
+      break;
+    case OSPF_MSG_LS_REQ:
+      ospf_packet_ls_req_dump (s, ntohs (ospfh->length));
+      break;
+    case OSPF_MSG_LS_UPD:
+      ospf_packet_ls_upd_dump (s, ntohs (ospfh->length));
+      break;
+    case OSPF_MSG_LS_ACK:
+      ospf_packet_ls_ack_dump (s, ntohs (ospfh->length));
+      break;
+    default:
+      break;
+    }
+
+  stream_set_getp (s, gp);
+}
+
+
+/*
+   [no] debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)
+                          [send|recv [detail]]
+*/
+DEFUN (debug_ospf_packet,
+       debug_ospf_packet_all_cmd,
+       "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n")
+{
+  int type = 0;
+  int flag = 0;
+  int i;
+
+  assert (argc > 0);
+
+  /* Check packet type. */
+  if (strncmp (argv[0], "h", 1) == 0)
+    type = OSPF_DEBUG_HELLO;
+  else if (strncmp (argv[0], "d", 1) == 0)
+    type = OSPF_DEBUG_DB_DESC;
+  else if (strncmp (argv[0], "ls-r", 4) == 0)
+    type = OSPF_DEBUG_LS_REQ;
+  else if (strncmp (argv[0], "ls-u", 4) == 0)
+    type = OSPF_DEBUG_LS_UPD;
+  else if (strncmp (argv[0], "ls-a", 4) == 0)
+    type = OSPF_DEBUG_LS_ACK;
+  else if (strncmp (argv[0], "a", 1) == 0)
+    type = OSPF_DEBUG_ALL;
+
+  /* Default, both send and recv. */
+  if (argc == 1)
+    flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV;
+
+  /* send or recv. */
+  if (argc >= 2)
+    {
+      if (strncmp (argv[1], "s", 1) == 0)
+       flag = OSPF_DEBUG_SEND;
+      else if (strncmp (argv[1], "r", 1) == 0)
+       flag = OSPF_DEBUG_RECV;
+      else if (strncmp (argv[1], "d", 1) == 0)
+       flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL;
+    }
+
+  /* detail. */
+  if (argc == 3)
+    if (strncmp (argv[2], "d", 1) == 0)
+      flag |= OSPF_DEBUG_DETAIL;
+
+  for (i = 0; i < 5; i++)
+    if (type & (0x01 << i))
+      {
+       if (vty->node == CONFIG_NODE)
+         DEBUG_PACKET_ON (i, flag);
+       else
+         TERM_DEBUG_PACKET_ON (i, flag);
+      }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf_packet,
+       debug_ospf_packet_send_recv_cmd,
+       "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv|detail)",
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n"
+       "Packet sent\n"
+       "Packet received\n"
+       "Detail information\n")
+
+ALIAS (debug_ospf_packet,
+       debug_ospf_packet_send_recv_detail_cmd,
+       "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) (detail|)",
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n"
+       "Packet sent\n"
+       "Packet received\n"
+       "Detail Information\n")
+       
+
+DEFUN (no_debug_ospf_packet,
+       no_debug_ospf_packet_all_cmd,
+       "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n")
+{
+  int type = 0;
+  int flag = 0;
+  int i;
+
+  assert (argc > 0);
+
+  /* Check packet type. */
+  if (strncmp (argv[0], "h", 1) == 0)
+    type = OSPF_DEBUG_HELLO;
+  else if (strncmp (argv[0], "d", 1) == 0)
+    type = OSPF_DEBUG_DB_DESC;
+  else if (strncmp (argv[0], "ls-r", 4) == 0)
+    type = OSPF_DEBUG_LS_REQ;
+  else if (strncmp (argv[0], "ls-u", 4) == 0)
+    type = OSPF_DEBUG_LS_UPD;
+  else if (strncmp (argv[0], "ls-a", 4) == 0)
+    type = OSPF_DEBUG_LS_ACK;
+  else if (strncmp (argv[0], "a", 1) == 0)
+    type = OSPF_DEBUG_ALL;
+
+  /* Default, both send and recv. */
+  if (argc == 1)
+    flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL ;
+
+  /* send or recv. */
+  if (argc == 2)
+    {
+      if (strncmp (argv[1], "s", 1) == 0)
+       flag = OSPF_DEBUG_SEND | OSPF_DEBUG_DETAIL;
+      else if (strncmp (argv[1], "r", 1) == 0)
+       flag = OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL;
+      else if (strncmp (argv[1], "d", 1) == 0)
+       flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL;
+    }
+
+  /* detail. */
+  if (argc == 3)
+    if (strncmp (argv[2], "d", 1) == 0)
+      flag = OSPF_DEBUG_DETAIL;
+
+  for (i = 0; i < 5; i++)
+    if (type & (0x01 << i))
+      {
+       if (vty->node == CONFIG_NODE)
+         DEBUG_PACKET_OFF (i, flag);
+       else
+         TERM_DEBUG_PACKET_OFF (i, flag);
+      }
+
+#ifdef DEBUG
+  /*
+  for (i = 0; i < 5; i++)
+    zlog_debug ("flag[%d] = %d", i, ospf_debug_packet[i]);
+  */
+#endif /* DEBUG */
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf_packet,
+       no_debug_ospf_packet_send_recv_cmd,
+       "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv|detail)",
+       NO_STR
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n"
+       "Packet sent\n"
+       "Packet received\n"
+       "Detail Information\n")
+
+ALIAS (no_debug_ospf_packet,
+       no_debug_ospf_packet_send_recv_detail_cmd,
+       "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) (detail|)",
+       NO_STR
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF packets\n"
+       "OSPF Hello\n"
+       "OSPF Database Description\n"
+       "OSPF Link State Request\n"
+       "OSPF Link State Update\n"
+       "OSPF Link State Acknowledgment\n"
+       "OSPF all packets\n"
+       "Packet sent\n"
+       "Packet received\n"
+       "Detail Information\n")
+
+
+DEFUN (debug_ospf_ism,
+       debug_ospf_ism_cmd,
+       "debug ospf ism",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Interface State Machine\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_ON (ism, ISM);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "s", 1) == 0)
+           DEBUG_ON (ism, ISM_STATUS);
+         else if (strncmp (argv[0], "e", 1) == 0)
+           DEBUG_ON (ism, ISM_EVENTS);
+         else if (strncmp (argv[0], "t", 1) == 0)
+           DEBUG_ON (ism, ISM_TIMERS);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_ON (ism, ISM);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "s", 1) == 0)
+       TERM_DEBUG_ON (ism, ISM_STATUS);
+      else if (strncmp (argv[0], "e", 1) == 0)
+       TERM_DEBUG_ON (ism, ISM_EVENTS);
+      else if (strncmp (argv[0], "t", 1) == 0)
+       TERM_DEBUG_ON (ism, ISM_TIMERS);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf_ism,
+       debug_ospf_ism_sub_cmd,
+       "debug ospf ism (status|events|timers)",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Interface State Machine\n"
+       "ISM Status Information\n"
+       "ISM Event Information\n"
+       "ISM TImer Information\n")
+
+DEFUN (no_debug_ospf_ism,
+       no_debug_ospf_ism_cmd,
+       "no debug ospf ism",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Interface State Machine")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_OFF (ism, ISM);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "s", 1) == 0)
+           DEBUG_OFF (ism, ISM_STATUS);
+         else if (strncmp (argv[0], "e", 1) == 0)
+           DEBUG_OFF (ism, ISM_EVENTS);
+         else if (strncmp (argv[0], "t", 1) == 0)
+           DEBUG_OFF (ism, ISM_TIMERS);
+       }
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_OFF (ism, ISM);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "s", 1) == 0)
+       TERM_DEBUG_OFF (ism, ISM_STATUS);
+      else if (strncmp (argv[0], "e", 1) == 0)
+       TERM_DEBUG_OFF (ism, ISM_EVENTS);
+      else if (strncmp (argv[0], "t", 1) == 0)
+       TERM_DEBUG_OFF (ism, ISM_TIMERS);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf_ism,
+       no_debug_ospf_ism_sub_cmd,
+       "no debug ospf ism (status|events|timers)",
+       NO_STR
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF Interface State Machine\n"
+       "ISM Status Information\n"
+       "ISM Event Information\n"
+       "ISM Timer Information\n")
+
+
+DEFUN (debug_ospf_nsm,
+       debug_ospf_nsm_cmd,
+       "debug ospf nsm",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Neighbor State Machine\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_ON (nsm, NSM);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "s", 1) == 0)
+           DEBUG_ON (nsm, NSM_STATUS);
+         else if (strncmp (argv[0], "e", 1) == 0)
+           DEBUG_ON (nsm, NSM_EVENTS);
+         else if (strncmp (argv[0], "t", 1) == 0)
+           DEBUG_ON (nsm, NSM_TIMERS);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_ON (nsm, NSM);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "s", 1) == 0)
+       TERM_DEBUG_ON (nsm, NSM_STATUS);
+      else if (strncmp (argv[0], "e", 1) == 0)
+       TERM_DEBUG_ON (nsm, NSM_EVENTS);
+      else if (strncmp (argv[0], "t", 1) == 0)
+       TERM_DEBUG_ON (nsm, NSM_TIMERS);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf_nsm,
+       debug_ospf_nsm_sub_cmd,
+       "debug ospf nsm (status|events|timers)",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Neighbor State Machine\n"
+       "NSM Status Information\n"
+       "NSM Event Information\n"
+       "NSM Timer Information\n")
+
+DEFUN (no_debug_ospf_nsm,
+       no_debug_ospf_nsm_cmd,
+       "no debug ospf nsm",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Neighbor State Machine")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_OFF (nsm, NSM);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "s", 1) == 0)
+           DEBUG_OFF (nsm, NSM_STATUS);
+         else if (strncmp (argv[0], "e", 1) == 0)
+           DEBUG_OFF (nsm, NSM_EVENTS);
+         else if (strncmp (argv[0], "t", 1) == 0)
+           DEBUG_OFF (nsm, NSM_TIMERS);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_OFF (nsm, NSM);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "s", 1) == 0)
+       TERM_DEBUG_OFF (nsm, NSM_STATUS);
+      else if (strncmp (argv[0], "e", 1) == 0)
+       TERM_DEBUG_OFF (nsm, NSM_EVENTS);
+      else if (strncmp (argv[0], "t", 1) == 0)
+       TERM_DEBUG_OFF (nsm, NSM_TIMERS);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf_nsm,
+       no_debug_ospf_nsm_sub_cmd,
+       "no debug ospf nsm (status|events|timers)",
+       NO_STR
+       "Debugging functions\n"
+       "OSPF information\n"
+       "OSPF Interface State Machine\n"
+       "NSM Status Information\n"
+       "NSM Event Information\n"
+       "NSM Timer Information\n")
+
+
+DEFUN (debug_ospf_lsa,
+       debug_ospf_lsa_cmd,
+       "debug ospf lsa",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Link State Advertisement\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_ON (lsa, LSA);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "g", 1) == 0)
+           DEBUG_ON (lsa, LSA_GENERATE);
+         else if (strncmp (argv[0], "f", 1) == 0)
+           DEBUG_ON (lsa, LSA_FLOODING);
+         else if (strncmp (argv[0], "i", 1) == 0)
+           DEBUG_ON (lsa, LSA_INSTALL);
+         else if (strncmp (argv[0], "r", 1) == 0)
+           DEBUG_ON (lsa, LSA_REFRESH);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_ON (lsa, LSA);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "g", 1) == 0)
+       TERM_DEBUG_ON (lsa, LSA_GENERATE);
+      else if (strncmp (argv[0], "f", 1) == 0)
+       TERM_DEBUG_ON (lsa, LSA_FLOODING);
+      else if (strncmp (argv[0], "i", 1) == 0)
+       TERM_DEBUG_ON (lsa, LSA_INSTALL);
+      else if (strncmp (argv[0], "r", 1) == 0)
+       TERM_DEBUG_ON (lsa, LSA_REFRESH);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf_lsa,
+       debug_ospf_lsa_sub_cmd,
+       "debug ospf lsa (generate|flooding|install|refresh)",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Link State Advertisement\n"
+       "LSA Generation\n"
+       "LSA Flooding\n"
+       "LSA Install/Delete\n"
+       "LSA Refresh\n")
+
+DEFUN (no_debug_ospf_lsa,
+       no_debug_ospf_lsa_cmd,
+       "no debug ospf lsa",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Link State Advertisement\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_OFF (lsa, LSA);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "g", 1) == 0)
+           DEBUG_OFF (lsa, LSA_GENERATE);
+         else if (strncmp (argv[0], "f", 1) == 0)
+           DEBUG_OFF (lsa, LSA_FLOODING);
+         else if (strncmp (argv[0], "i", 1) == 0)
+           DEBUG_OFF (lsa, LSA_INSTALL);
+         else if (strncmp (argv[0], "r", 1) == 0)
+           DEBUG_OFF (lsa, LSA_REFRESH);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_OFF (lsa, LSA);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "g", 1) == 0)
+       TERM_DEBUG_OFF (lsa, LSA_GENERATE);
+      else if (strncmp (argv[0], "f", 1) == 0)
+       TERM_DEBUG_OFF (lsa, LSA_FLOODING);
+      else if (strncmp (argv[0], "i", 1) == 0)
+       TERM_DEBUG_OFF (lsa, LSA_INSTALL);
+      else if (strncmp (argv[0], "r", 1) == 0)
+       TERM_DEBUG_OFF (lsa, LSA_REFRESH);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf_lsa,
+       no_debug_ospf_lsa_sub_cmd,
+       "no debug ospf lsa (generate|flooding|install|refresh)",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Link State Advertisement\n"
+       "LSA Generation\n"
+       "LSA Flooding\n"
+       "LSA Install/Delete\n"
+       "LSA Refres\n")
+
+
+DEFUN (debug_ospf_zebra,
+       debug_ospf_zebra_cmd,
+       "debug ospf zebra",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Zebra information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_ON (zebra, ZEBRA);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "i", 1) == 0)
+           DEBUG_ON (zebra, ZEBRA_INTERFACE);
+         else if (strncmp (argv[0], "r", 1) == 0)
+           DEBUG_ON (zebra, ZEBRA_REDISTRIBUTE);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_ON (zebra, ZEBRA);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "i", 1) == 0)
+       TERM_DEBUG_ON (zebra, ZEBRA_INTERFACE);
+      else if (strncmp (argv[0], "r", 1) == 0)
+       TERM_DEBUG_ON (zebra, ZEBRA_REDISTRIBUTE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (debug_ospf_zebra,
+       debug_ospf_zebra_sub_cmd,
+       "debug ospf zebra (interface|redistribute)",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Zebra information\n"
+       "Zebra interface\n"
+       "Zebra redistribute\n")
+
+DEFUN (no_debug_ospf_zebra,
+       no_debug_ospf_zebra_cmd,
+       "no debug ospf zebra",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Zebra information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    {
+      if (argc == 0)
+       DEBUG_OFF (zebra, ZEBRA);
+      else if (argc == 1)
+       {
+         if (strncmp (argv[0], "i", 1) == 0)
+           DEBUG_OFF (zebra, ZEBRA_INTERFACE);
+         else if (strncmp (argv[0], "r", 1) == 0)
+           DEBUG_OFF (zebra, ZEBRA_REDISTRIBUTE);
+       }
+
+      return CMD_SUCCESS;
+    }
+
+  /* ENABLE_NODE. */
+  if (argc == 0)
+    TERM_DEBUG_OFF (zebra, ZEBRA);
+  else if (argc == 1)
+    {
+      if (strncmp (argv[0], "i", 1) == 0)
+       TERM_DEBUG_OFF (zebra, ZEBRA_INTERFACE);
+      else if (strncmp (argv[0], "r", 1) == 0)
+       TERM_DEBUG_OFF (zebra, ZEBRA_REDISTRIBUTE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ospf_zebra,
+       no_debug_ospf_zebra_sub_cmd,
+       "no debug ospf zebra (interface|redistribute)",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF Zebra information\n"
+       "Zebra interface\n"
+       "Zebra redistribute\n")
+
+DEFUN (debug_ospf_event,
+       debug_ospf_event_cmd,
+       "debug ospf event",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF event information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_ON (event, EVENT);
+  TERM_DEBUG_ON (event, EVENT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_event,
+       no_debug_ospf_event_cmd,
+       "no debug ospf event",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF event information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_OFF (event, EVENT);
+  TERM_DEBUG_OFF (event, EVENT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf_nssa,
+       debug_ospf_nssa_cmd,
+       "debug ospf nssa",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF nssa information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_ON (nssa, NSSA);
+  TERM_DEBUG_ON (nssa, NSSA);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_nssa,
+       no_debug_ospf_nssa_cmd,
+       "no debug ospf nssa",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF nssa information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_OFF (nssa, NSSA);
+  TERM_DEBUG_OFF (nssa, NSSA);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ospf_te,
+       debug_ospf_te_cmd,
+       "debug ospf te",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-TE information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_ON (te, TE);
+  TERM_DEBUG_ON (te, TE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_te,
+       no_debug_ospf_te_cmd,
+       "no debug ospf te",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-TE information\n")
+{
+  if (vty->node == CONFIG_NODE)
+    CONF_DEBUG_OFF (te, TE);
+  TERM_DEBUG_OFF (te, TE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_debugging_ospf,
+       show_debugging_ospf_cmd,
+       "show debugging ospf",
+       SHOW_STR
+       DEBUG_STR
+       OSPF_STR)
+{
+  int i;
+
+  vty_out (vty, "OSPF debugging status:%s", VTY_NEWLINE);
+
+  /* Show debug status for events. */
+  if (IS_DEBUG_OSPF(event,EVENT))
+    vty_out (vty, "  OSPF event debugging is on%s", VTY_NEWLINE);
+
+  /* Show debug status for ISM. */
+  if (IS_DEBUG_OSPF (ism, ISM) == OSPF_DEBUG_ISM)
+    vty_out (vty, "  OSPF ISM debugging is on%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_DEBUG_OSPF (ism, ISM_STATUS))
+       vty_out (vty, "  OSPF ISM status debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+       vty_out (vty, "  OSPF ISM event debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
+       vty_out (vty, "  OSPF ISM timer debugging is on%s", VTY_NEWLINE);
+    }
+
+  /* Show debug status for NSM. */
+  if (IS_DEBUG_OSPF (nsm, NSM) == OSPF_DEBUG_NSM)
+    vty_out (vty, "  OSPF NSM debugging is on%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_DEBUG_OSPF (nsm, NSM_STATUS))
+       vty_out (vty, "  OSPF NSM status debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
+       vty_out (vty, "  OSPF NSM event debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
+       vty_out (vty, "  OSPF NSM timer debugging is on%s", VTY_NEWLINE);
+    }
+
+  /* Show debug status for OSPF Packets. */
+  for (i = 0; i < 5; i++)
+    if (IS_DEBUG_OSPF_PACKET (i, SEND) && IS_DEBUG_OSPF_PACKET (i, RECV))
+      {
+       vty_out (vty, "  OSPF packet %s%s debugging is on%s",
+                LOOKUP (ospf_packet_type_str, i + 1),
+                IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "",
+                VTY_NEWLINE);
+      }
+    else
+      {
+       if (IS_DEBUG_OSPF_PACKET (i, SEND))
+         vty_out (vty, "  OSPF packet %s send%s debugging is on%s",
+                  LOOKUP (ospf_packet_type_str, i + 1),
+                  IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "",
+                  VTY_NEWLINE);
+       if (IS_DEBUG_OSPF_PACKET (i, RECV))
+         vty_out (vty, "  OSPF packet %s receive%s debugging is on%s",
+                  LOOKUP (ospf_packet_type_str, i + 1),
+                  IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "",
+                  VTY_NEWLINE);
+      }
+
+  /* Show debug status for OSPF LSAs. */
+  if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA)
+    vty_out (vty, "  OSPF LSA debugging is on%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+       vty_out (vty, "  OSPF LSA generation debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+       vty_out (vty, "  OSPF LSA flooding debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (lsa, LSA_INSTALL))
+       vty_out (vty, "  OSPF LSA install debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+       vty_out (vty, "  OSPF LSA refresh debugging is on%s", VTY_NEWLINE);
+    }
+
+  /* Show debug status for Zebra. */
+  if (IS_DEBUG_OSPF (zebra, ZEBRA) == OSPF_DEBUG_ZEBRA)
+    vty_out (vty, "  OSPF Zebra debugging is on%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+       vty_out (vty, "  OSPF Zebra interface debugging is on%s", VTY_NEWLINE);
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+       vty_out (vty, "  OSPF Zebra redistribute debugging is on%s", VTY_NEWLINE);
+    }
+
+  /* Show debug status for NSSA. */
+  if (IS_DEBUG_OSPF (nssa, NSSA) == OSPF_DEBUG_NSSA)
+    vty_out (vty, "  OSPF NSSA debugging is on%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+/* Debug node. */
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",
+  1 /* VTYSH */
+};
+
+static int
+config_write_debug (struct vty *vty)
+{
+  int write = 0;
+  int i, r;
+
+  const char *type_str[] = {"hello", "dd", "ls-request", "ls-update", "ls-ack"};
+  const char *detail_str[] = {"", " send", " recv", "", " detail",
+                       " send detail", " recv detail", " detail"};
+
+  /* debug ospf ism (status|events|timers). */
+  if (IS_CONF_DEBUG_OSPF (ism, ISM) == OSPF_DEBUG_ISM)
+    vty_out (vty, "debug ospf ism%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_CONF_DEBUG_OSPF (ism, ISM_STATUS))
+       vty_out (vty, "debug ospf ism status%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (ism, ISM_EVENTS))
+       vty_out (vty, "debug ospf ism event%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (ism, ISM_TIMERS))
+       vty_out (vty, "debug ospf ism timer%s", VTY_NEWLINE);
+    }
+
+  /* debug ospf nsm (status|events|timers). */
+  if (IS_CONF_DEBUG_OSPF (nsm, NSM) == OSPF_DEBUG_NSM)
+    vty_out (vty, "debug ospf nsm%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_CONF_DEBUG_OSPF (nsm, NSM_STATUS))
+       vty_out (vty, "debug ospf nsm status%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (nsm, NSM_EVENTS))
+       vty_out (vty, "debug ospf nsm event%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (nsm, NSM_TIMERS))
+       vty_out (vty, "debug ospf nsm timer%s", VTY_NEWLINE);
+    }
+
+  /* debug ospf lsa (generate|flooding|install|refresh). */
+  if (IS_CONF_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA)
+    vty_out (vty, "debug ospf lsa%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_CONF_DEBUG_OSPF (lsa, LSA_GENERATE))
+       vty_out (vty, "debug ospf lsa generate%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (lsa, LSA_FLOODING))
+       vty_out (vty, "debug ospf lsa flooding%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (lsa, LSA_INSTALL))
+       vty_out (vty, "debug ospf lsa install%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (lsa, LSA_REFRESH))
+       vty_out (vty, "debug ospf lsa refresh%s", VTY_NEWLINE);
+
+      write = 1;
+    }
+
+  /* debug ospf zebra (interface|redistribute). */
+  if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA) == OSPF_DEBUG_ZEBRA)
+    vty_out (vty, "debug ospf zebra%s", VTY_NEWLINE);
+  else
+    {
+      if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+       vty_out (vty, "debug ospf zebra interface%s", VTY_NEWLINE);
+      if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+       vty_out (vty, "debug ospf zebra redistribute%s", VTY_NEWLINE);
+
+      write = 1;
+    }
+
+  /* debug ospf event. */
+  if (IS_CONF_DEBUG_OSPF (event, EVENT) == OSPF_DEBUG_EVENT)
+    {
+      vty_out (vty, "debug ospf event%s", VTY_NEWLINE);
+      write = 1;
+    }
+
+  /* debug ospf nssa. */
+  if (IS_CONF_DEBUG_OSPF (nssa, NSSA) == OSPF_DEBUG_NSSA)
+    {
+      vty_out (vty, "debug ospf nssa%s", VTY_NEWLINE);
+      write = 1;
+    }
+  
+  /* debug ospf packet all detail. */
+  r = OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL;
+  for (i = 0; i < 5; i++)
+    r &= conf_debug_ospf_packet[i] & (OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL);
+  if (r == (OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL))
+    {
+      vty_out (vty, "debug ospf packet all detail%s", VTY_NEWLINE);
+      return 1;
+    }
+
+  /* debug ospf packet all. */
+  r = OSPF_DEBUG_SEND_RECV;
+  for (i = 0; i < 5; i++)
+    r &= conf_debug_ospf_packet[i] & OSPF_DEBUG_SEND_RECV;
+  if (r == OSPF_DEBUG_SEND_RECV)
+    {
+      vty_out (vty, "debug ospf packet all%s", VTY_NEWLINE);
+      for (i = 0; i < 5; i++)
+       if (conf_debug_ospf_packet[i] & OSPF_DEBUG_DETAIL)
+         vty_out (vty, "debug ospf packet %s detail%s",
+                  type_str[i],
+                  VTY_NEWLINE);
+      return 1;
+    }
+
+  /* debug ospf packet (hello|dd|ls-request|ls-update|ls-ack)
+     (send|recv) (detail). */
+  for (i = 0; i < 5; i++)
+    {
+      if (conf_debug_ospf_packet[i] == 0)
+       continue;
+      
+      vty_out (vty, "debug ospf packet %s%s%s",
+              type_str[i], detail_str[conf_debug_ospf_packet[i]],
+              VTY_NEWLINE);
+      write = 1;
+    }
+
+  return write;
+}
+
+/* Initialize debug commands. */
+void
+debug_init ()
+{
+  install_node (&debug_node, config_write_debug);
+
+  install_element (ENABLE_NODE, &show_debugging_ospf_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_packet_send_recv_detail_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_packet_send_recv_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_packet_all_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_ism_sub_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_ism_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_nsm_sub_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_nsm_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_lsa_sub_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_lsa_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_zebra_sub_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_zebra_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_event_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_nssa_cmd);
+  install_element (ENABLE_NODE, &debug_ospf_te_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_packet_send_recv_detail_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_packet_send_recv_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_packet_all_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_ism_sub_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_ism_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_nsm_sub_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_nsm_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_lsa_sub_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_lsa_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_zebra_sub_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_event_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_nssa_cmd);
+  install_element (ENABLE_NODE, &no_debug_ospf_te_cmd);
+
+  install_element (CONFIG_NODE, &debug_ospf_packet_send_recv_detail_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_packet_send_recv_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_packet_all_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_ism_sub_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_ism_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_nsm_sub_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_nsm_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_lsa_sub_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_lsa_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_zebra_sub_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_zebra_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_event_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_nssa_cmd);
+  install_element (CONFIG_NODE, &debug_ospf_te_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_packet_send_recv_detail_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_packet_send_recv_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_packet_all_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_ism_sub_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_ism_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_nsm_sub_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_nsm_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_lsa_sub_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_lsa_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_zebra_sub_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_event_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_nssa_cmd);
+  install_element (CONFIG_NODE, &no_debug_ospf_te_cmd);
+}
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
new file mode 100644 (file)
index 0000000..f843df0
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * OSPFd dump routine.
+ * Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#ifndef _ZEBRA_OSPF_DUMP_H
+#define _ZEBRA_OSPF_DUMP_H
+
+/* Debug Flags. */
+#define OSPF_DEBUG_HELLO       0x01
+#define OSPF_DEBUG_DB_DESC     0x02
+#define OSPF_DEBUG_LS_REQ      0x04
+#define OSPF_DEBUG_LS_UPD      0x08
+#define OSPF_DEBUG_LS_ACK      0x10
+#define OSPF_DEBUG_ALL         0x1f
+
+#define OSPF_DEBUG_SEND                0x01
+#define OSPF_DEBUG_RECV                0x02
+#define OSPF_DEBUG_SEND_RECV    0x03
+#define OSPF_DEBUG_DETAIL      0x04
+
+#define OSPF_DEBUG_ISM_STATUS  0x01
+#define OSPF_DEBUG_ISM_EVENTS  0x02
+#define OSPF_DEBUG_ISM_TIMERS  0x04
+#define OSPF_DEBUG_ISM         0x07
+#define OSPF_DEBUG_NSM_STATUS  0x01
+#define OSPF_DEBUG_NSM_EVENTS  0x02
+#define OSPF_DEBUG_NSM_TIMERS   0x04
+#define OSPF_DEBUG_NSM         0x07
+
+#define OSPF_DEBUG_LSA_GENERATE 0x01
+#define OSPF_DEBUG_LSA_FLOODING        0x02
+#define OSPF_DEBUG_LSA_INSTALL  0x04
+#define OSPF_DEBUG_LSA_REFRESH  0x08
+#define OSPF_DEBUG_LSA         0x0F
+
+#define OSPF_DEBUG_ZEBRA_INTERFACE     0x01
+#define OSPF_DEBUG_ZEBRA_REDISTRIBUTE  0x02
+#define OSPF_DEBUG_ZEBRA              0x03
+
+#define OSPF_DEBUG_EVENT        0x01
+#define OSPF_DEBUG_NSSA                0x02
+#define OSPF_DEBUG_TE          0x04
+
+/* Macro for setting debug option. */
+#define CONF_DEBUG_PACKET_ON(a, b)         conf_debug_ospf_packet[a] |= (b)
+#define CONF_DEBUG_PACKET_OFF(a, b)        conf_debug_ospf_packet[a] &= ~(b)
+#define TERM_DEBUG_PACKET_ON(a, b)         term_debug_ospf_packet[a] |= (b)
+#define TERM_DEBUG_PACKET_OFF(a, b)        term_debug_ospf_packet[a] &= ~(b)
+#define DEBUG_PACKET_ON(a, b) \
+    do { \
+      CONF_DEBUG_PACKET_ON(a, b); \
+      TERM_DEBUG_PACKET_ON(a, b); \
+    } while (0)
+#define DEBUG_PACKET_OFF(a, b) \
+    do { \
+      CONF_DEBUG_PACKET_OFF(a, b); \
+      TERM_DEBUG_PACKET_OFF(a, b); \
+    } while (0)
+
+#define CONF_DEBUG_ON(a, b)     conf_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b)
+#define CONF_DEBUG_OFF(a, b)    conf_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b)
+#define TERM_DEBUG_ON(a, b)     term_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b)
+#define TERM_DEBUG_OFF(a, b)    term_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b)
+#define DEBUG_ON(a, b) \
+     do { \
+       CONF_DEBUG_ON(a, b); \
+       TERM_DEBUG_ON(a, b); \
+     } while (0)
+#define DEBUG_OFF(a, b) \
+     do { \
+       CONF_DEBUG_OFF(a, b); \
+       TERM_DEBUG_OFF(a, b); \
+     } while (0)
+
+/* Macro for checking debug option. */
+#define IS_DEBUG_OSPF_PACKET(a, b) \
+       (term_debug_ospf_packet[a] & OSPF_DEBUG_ ## b)
+#define IS_DEBUG_OSPF(a, b) \
+       (term_debug_ospf_ ## a & OSPF_DEBUG_ ## b)
+#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event,EVENT)
+
+#define IS_DEBUG_OSPF_NSSA  IS_DEBUG_OSPF(nssa,NSSA)
+
+#define IS_DEBUG_OSPF_TE  IS_DEBUG_OSPF(te,TE)
+
+#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
+       (conf_debug_ospf_packet[a] & OSPF_DEBUG_ ## b)
+#define IS_CONF_DEBUG_OSPF(a, b) \
+       (conf_debug_ospf_ ## a & OSPF_DEBUG_ ## b)
+
+#ifdef ORIGINAL_CODING
+#else /* ORIGINAL_CODING */
+struct stream;
+#endif /* ORIGINAL_CODING */
+
+#define AREA_NAME(A)    ospf_area_name_string ((A))
+#define IF_NAME(I)      ospf_if_name_string ((I))
+
+/* Extern debug flag. */
+extern unsigned long term_debug_ospf_packet[];
+extern unsigned long term_debug_ospf_event;
+extern unsigned long term_debug_ospf_ism;
+extern unsigned long term_debug_ospf_nsm;
+extern unsigned long term_debug_ospf_lsa;
+extern unsigned long term_debug_ospf_zebra;
+extern unsigned long term_debug_ospf_nssa;
+extern unsigned long term_debug_ospf_te;
+
+/* Message Strings. */
+extern char *ospf_lsa_type_str[];
+extern const struct message ospf_auth_type_str[];
+extern const size_t ospf_auth_type_str_max;
+
+/* Prototypes. */
+extern const char *ospf_area_name_string (struct ospf_area *);
+extern const char *ospf_area_desc_string (struct ospf_area *);
+extern const char *ospf_if_name_string (struct ospf_interface *);
+extern void ospf_nbr_state_message (struct ospf_neighbor *, char *, size_t);
+extern char *ospf_options_dump (u_char);
+extern const char *ospf_timer_dump (struct thread *, char *, size_t);
+extern const char *ospf_timeval_dump (struct timeval *, char *, size_t);
+extern void ospf_ip_header_dump (struct ip *);
+extern void ospf_packet_dump (struct stream *);
+extern void ospf_lsa_header_dump (struct lsa_header *);
+extern void debug_init (void);
+
+/* Appropriate buffer size to use with ospf_timer_dump and ospf_timeval_dump: */
+#define OSPF_TIME_DUMP_SIZE    16
+
+#endif /* _ZEBRA_OSPF_DUMP_H */
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
new file mode 100644 (file)
index 0000000..8de4974
--- /dev/null
@@ -0,0 +1,997 @@
+/*
+ * OSPF Flooding -- RFC2328 Section 13.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "command.h"
+#include "table.h"
+#include "thread.h"
+#include "memory.h"
+#include "log.h"
+#include "zclient.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+
+extern struct zclient *zclient;
+
+/* Do the LSA acking specified in table 19, Section 13.5, row 2
+ * This get called from ospf_flood_out_interface. Declared inline 
+ * for speed. */
+static void
+ospf_flood_delayed_lsa_ack (struct ospf_neighbor *inbr, struct ospf_lsa *lsa)
+{
+  /* LSA is more recent than database copy, but was not
+     flooded back out receiving interface.  Delayed
+     acknowledgment sent. If interface is in Backup state
+     delayed acknowledgment sent only if advertisement
+     received from Designated Router, otherwise do nothing See
+     RFC 2328 Section 13.5 */
+
+  /* Whether LSA is more recent or not, and whether this is in
+     response to the LSA being sent out recieving interface has been 
+     worked out previously */
+
+  /* Deal with router as BDR */
+  if (inbr->oi->state == ISM_Backup && ! NBR_IS_DR (inbr))
+    return;
+
+  /* Schedule a delayed LSA Ack to be sent */ 
+  listnode_add (inbr->oi->ls_ack, ospf_lsa_lock (lsa)); /* delayed LSA Ack */
+}
+
+/* Check LSA is related to external info. */
+struct external_info *
+ospf_external_info_check (struct ospf_lsa *lsa)
+{
+  struct as_external_lsa *al;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  int type;
+
+  al = (struct as_external_lsa *) lsa->data;
+
+  p.family = AF_INET;
+  p.prefix = lsa->data->id;
+  p.prefixlen = ip_masklen (al->mask);
+
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      int redist_type = is_prefix_default (&p) ? DEFAULT_ROUTE : type;
+      if (ospf_is_type_redistributed (redist_type))
+       if (EXTERNAL_INFO (type))
+         {
+           rn = route_node_lookup (EXTERNAL_INFO (type),
+                                   (struct prefix *) &p);
+           if (rn)
+             {
+               route_unlock_node (rn);
+               if (rn->info != NULL)
+                 return (struct external_info *) rn->info;
+             }
+         }
+    }
+
+  return NULL;
+}
+
+static void
+ospf_process_self_originated_lsa (struct ospf *ospf,
+                                 struct ospf_lsa *new, struct ospf_area *area)
+{
+  struct ospf_interface *oi;
+  struct external_info *ei;
+  struct listnode *node;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("LSA[Type%d:%s]: Process self-originated LSA seq 0x%x",
+              new->data->type, inet_ntoa (new->data->id), 
+              ntohl(new->data->ls_seqnum));
+
+  /* If we're here, we installed a self-originated LSA that we received
+     from a neighbor, i.e. it's more recent.  We must see whether we want
+     to originate it.
+     If yes, we should use this LSA's sequence number and reoriginate
+     a new instance.
+     if not --- we must flush this LSA from the domain. */
+  switch (new->data->type)
+    {
+    case OSPF_ROUTER_LSA:
+      /* Originate a new instance and schedule flooding */
+      if (area->router_lsa_self)
+       area->router_lsa_self->data->ls_seqnum = new->data->ls_seqnum;
+      ospf_router_lsa_update_area (area);
+      return;
+    case OSPF_NETWORK_LSA:
+    case OSPF_OPAQUE_LINK_LSA:
+      /* We must find the interface the LSA could belong to.
+        If the interface is no more a broadcast type or we are no more
+        the DR, we flush the LSA otherwise -- create the new instance and
+        schedule flooding. */
+
+      /* Look through all interfaces, not just area, since interface
+        could be moved from one area to another. */
+      for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+       /* These are sanity check. */
+        if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &new->data->id))
+          {
+            if (oi->area != area ||
+                oi->type != OSPF_IFTYPE_BROADCAST ||
+                !IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi)))
+              {
+                ospf_schedule_lsa_flush_area (area, new);
+                return;
+              }
+            
+            if (new->data->type == OSPF_OPAQUE_LINK_LSA)
+              {
+                ospf_opaque_lsa_refresh (new);
+                return;
+              }
+
+            if (oi->network_lsa_self)
+             oi->network_lsa_self->data->ls_seqnum = new->data->ls_seqnum;
+            /* Schedule network-LSA origination. */
+            ospf_network_lsa_update (oi);
+            return;
+          }
+      break;
+    case OSPF_SUMMARY_LSA:
+    case OSPF_ASBR_SUMMARY_LSA:
+      ospf_schedule_abr_task (ospf);
+      break;
+    case OSPF_AS_EXTERNAL_LSA :
+    case OSPF_AS_NSSA_LSA:
+       if ( (new->data->type == OSPF_AS_EXTERNAL_LSA)
+             && CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT))
+         {
+           ospf_translated_nssa_refresh (ospf, NULL, new);
+           return;
+         }
+      ei = ospf_external_info_check (new);
+      if (ei)
+        ospf_external_lsa_refresh (ospf, new, ei, LSA_REFRESH_FORCE);
+      else
+        ospf_lsa_flush_as (ospf, new);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      ospf_opaque_lsa_refresh (new);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      ospf_opaque_lsa_refresh (new); /* Reconsideration may needed. *//* XXX */
+      break;
+    default:
+      break;
+    }
+}
+
+/* OSPF LSA flooding -- RFC2328 Section 13.(5). */
+
+/* Now Updated for NSSA operation, as follows:
+
+
+       Type-5's have no change.  Blocked to STUB or NSSA.
+
+       Type-7's can be received, and if a DR
+       they will also flood the local NSSA Area as Type-7's
+
+       If a Self-Originated LSA (now an ASBR), 
+       The LSDB will be updated as Type-5's, (for continual re-fresh)
+
+           If an NSSA-IR it is installed/flooded as Type-7, P-bit on.
+           if an NSSA-ABR it is installed/flooded as Type-7, P-bit off.
+
+       Later, during the ABR TASK, if the ABR is the Elected NSSA
+       translator, then All Type-7s (with P-bit ON) are Translated to
+       Type-5's and flooded to all non-NSSA/STUB areas.
+
+       During ASE Calculations, 
+           non-ABRs calculate external routes from Type-7's
+           ABRs calculate external routes from Type-5's and non-self Type-7s
+*/
+int
+ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr,
+           struct ospf_lsa *current, struct ospf_lsa *new)
+{
+  struct ospf_interface *oi;
+  int lsa_ack_flag;
+
+  /* Type-7 LSA's will be flooded throughout their native NSSA area,
+     but will also be flooded as Type-5's into ABR capable links.  */
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("LSA[Flooding]: start, NBR %s (%s), cur(%p), New-LSA[%s]",
+               inet_ntoa (nbr->router_id),
+               LOOKUP (ospf_nsm_state_msg, nbr->state),
+               (void *)current,
+               dump_lsa_key (new));
+
+  lsa_ack_flag = 0;
+  oi = nbr->oi;
+
+  /* If there is already a database copy, and if the
+     database copy was received via flooding and installed less
+     than MinLSArrival seconds ago, discard the new LSA
+     (without acknowledging it). */
+  if (current != NULL)         /* -- endo. */
+    {
+      if (IS_LSA_SELF (current)
+      && (ntohs (current->data->ls_age)    == 0
+      &&  ntohl (current->data->ls_seqnum) == OSPF_INITIAL_SEQUENCE_NUMBER))
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("LSA[Flooding]: Got a self-originated LSA, "
+                      "while local one is initial instance.");
+          ; /* Accept this LSA for quick LSDB resynchronization. */
+        }
+      else if (tv_cmp (tv_sub (recent_relative_time (), current->tv_recv),
+                      msec2tv (ospf->min_ls_arrival)) < 0)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("LSA[Flooding]: LSA is received recently.");
+          return -1;
+        }
+    }
+
+  /* Flood the new LSA out some subset of the router's interfaces.
+     In some cases (e.g., the state of the receiving interface is
+     DR and the LSA was received from a router other than the
+     Backup DR) the LSA will be flooded back out the receiving
+     interface. */
+  lsa_ack_flag = ospf_flood_through (ospf, nbr, new);
+
+  /* Remove the current database copy from all neighbors' Link state
+     retransmission lists.  AS_EXTERNAL and AS_EXTERNAL_OPAQUE does
+                                        ^^^^^^^^^^^^^^^^^^^^^^^
+     not have area ID.
+     All other (even NSSA's) do have area ID.  */
+  if (current)
+    {
+      switch (current->data->type)
+        {
+        case OSPF_AS_EXTERNAL_LSA:
+        case OSPF_OPAQUE_AS_LSA:
+          ospf_ls_retransmit_delete_nbr_as (ospf, current);
+          break;
+        default:
+          ospf_ls_retransmit_delete_nbr_area (nbr->oi->area, current);
+          break;
+        }
+    }
+
+  /* Do some internal house keeping that is needed here */
+  SET_FLAG (new->flags, OSPF_LSA_RECEIVED);
+  ospf_lsa_is_self_originated (ospf, new); /* Let it set the flag */
+
+  /* Install the new LSA in the link state database
+     (replacing the current database copy).  This may cause the
+     routing table calculation to be scheduled.  In addition,
+     timestamp the new LSA with the current time.  The flooding
+     procedure cannot overwrite the newly installed LSA until
+     MinLSArrival seconds have elapsed. */  
+
+  if (! (new = ospf_lsa_install (ospf, nbr->oi, new)))
+    return -1; /* unknown LSA type or any other error condition */
+
+  /* Acknowledge the receipt of the LSA by sending a Link State
+     Acknowledgment packet back out the receiving interface. */
+  if (lsa_ack_flag)
+    ospf_flood_delayed_lsa_ack (nbr, new);     
+
+  /* If this new LSA indicates that it was originated by the
+     receiving router itself, the router must take special action,
+     either updating the LSA or in some cases flushing it from
+     the routing domain. */
+  if (ospf_lsa_is_self_originated (ospf, new))
+    ospf_process_self_originated_lsa (ospf, new, oi->area);
+  else
+    /* Update statistics value for OSPF-MIB. */
+    ospf->rx_lsa_count++;
+
+  return 0;
+}
+
+/* OSPF LSA flooding -- RFC2328 Section 13.3. */
+static int
+ospf_flood_through_interface (struct ospf_interface *oi,
+                             struct ospf_neighbor *inbr,
+                             struct ospf_lsa *lsa)
+{
+  struct ospf_neighbor *onbr;
+  struct route_node *rn;
+  int retx_flag;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_flood_through_interface(): "
+              "considering int %s, INBR(%s), LSA[%s]",
+              IF_NAME (oi), inbr ? inet_ntoa (inbr->router_id) : "NULL",
+               dump_lsa_key (lsa));
+
+  if (!ospf_if_is_enable (oi))
+    return 0;
+
+  /* Remember if new LSA is aded to a retransmit list. */
+  retx_flag = 0;
+
+  /* Each of the neighbors attached to this interface are examined,
+     to determine whether they must receive the new LSA.  The following
+     steps are executed for each neighbor: */
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    {
+      struct ospf_lsa *ls_req;
+      if (rn->info == NULL)
+       continue;
+
+      onbr = rn->info;
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_flood_through_interface(): considering nbr %s (%s)",
+                  inet_ntoa (onbr->router_id),
+                   LOOKUP (ospf_nsm_state_msg, onbr->state));
+
+      /* If the neighbor is in a lesser state than Exchange, it
+        does not participate in flooding, and the next neighbor
+        should be examined. */
+      if (onbr->state < NSM_Exchange)
+       continue;
+
+      /* If the adjacency is not yet full (neighbor state is
+        Exchange or Loading), examine the Link state request
+        list associated with this adjacency.  If there is an
+        instance of the new LSA on the list, it indicates that
+        the neighboring router has an instance of the LSA
+        already.  Compare the new LSA to the neighbor's copy: */
+      if (onbr->state < NSM_Full)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_flood_through_interface(): nbr adj is not Full");
+         ls_req = ospf_ls_request_lookup (onbr, lsa);
+         if (ls_req != NULL)
+           {
+             int ret;
+
+             ret = ospf_lsa_more_recent (ls_req, lsa);
+             /* The new LSA is less recent. */
+             if (ret > 0)
+               continue;
+             /* The two copies are the same instance, then delete
+                the LSA from the Link state request list. */
+             else if (ret == 0)
+               {
+                 ospf_ls_request_delete (onbr, ls_req);
+                 ospf_check_nbr_loading (onbr);
+                 continue;
+               }
+             /* The new LSA is more recent.  Delete the LSA
+                from the Link state request list. */
+             else
+               {
+                 ospf_ls_request_delete (onbr, ls_req);
+                 ospf_check_nbr_loading (onbr);
+               }
+           }
+       }
+
+      if (IS_OPAQUE_LSA (lsa->data->type))
+        {
+          if (! CHECK_FLAG (onbr->options, OSPF_OPTION_O))
+            {
+              if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+                zlog_debug ("Skip this neighbor: Not Opaque-capable.");
+              continue;
+            }
+        }
+
+      /* If the new LSA was received from this neighbor,
+        examine the next neighbor. */
+#ifdef ORIGINAL_CODING
+      if (inbr)
+       if (IPV4_ADDR_SAME (&inbr->router_id, &onbr->router_id))
+         continue;
+#else /* ORIGINAL_CODING */
+      if (inbr)
+        {
+          /*
+           * Triggered by LSUpd message parser "ospf_ls_upd ()".
+           * E.g., all LSAs handling here is received via network.
+           */
+          if (IPV4_ADDR_SAME (&inbr->router_id, &onbr->router_id))
+            {
+              if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+                zlog_debug ("Skip this neighbor: inbr == onbr");
+              continue;
+            }
+        }
+      else
+        {
+          /*
+           * Triggered by MaxAge remover, so far.
+           * NULL "inbr" means flooding starts from this node.
+           */
+          if (IPV4_ADDR_SAME (&lsa->data->adv_router, &onbr->router_id))
+            {
+              if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+                zlog_debug ("Skip this neighbor: lsah->adv_router == onbr");
+              continue;
+            }
+        }
+#endif /* ORIGINAL_CODING */
+
+      /* Add the new LSA to the Link state retransmission list
+        for the adjacency. The LSA will be retransmitted
+        at intervals until an acknowledgment is seen from
+        the neighbor. */
+      ospf_ls_retransmit_add (onbr, lsa);
+      retx_flag = 1;
+    }
+
+  /* If in the previous step, the LSA was NOT added to any of
+     the Link state retransmission lists, there is no need to
+     flood the LSA out the interface. */
+  if (retx_flag == 0) 
+    {
+      return (inbr && inbr->oi == oi);
+    }
+
+  /* if we've received the lsa on this interface we need to perform
+     additional checking */
+  if (inbr && (inbr->oi == oi))
+    {
+      /* If the new LSA was received on this interface, and it was
+        received from either the Designated Router or the Backup
+        Designated Router, chances are that all the neighbors have
+        received the LSA already. */
+      if (NBR_IS_DR (inbr) || NBR_IS_BDR (inbr))
+       {
+         if (IS_DEBUG_OSPF_NSSA)
+           zlog_debug ("ospf_flood_through_interface(): "
+                      "DR/BDR NOT SEND to int %s", IF_NAME (oi));
+         return 1;
+       }
+         
+      /* If the new LSA was received on this interface, and the
+        interface state is Backup, examine the next interface.  The
+        Designated Router will do the flooding on this interface.
+        However, if the Designated Router fails the router will
+        end up retransmitting the updates. */
+
+      if (oi->state == ISM_Backup)
+       {
+         if (IS_DEBUG_OSPF_NSSA)
+           zlog_debug ("ospf_flood_through_interface(): "
+                      "ISM_Backup NOT SEND to int %s", IF_NAME (oi));
+         return 1;
+       }
+    }
+
+  /* The LSA must be flooded out the interface. Send a Link State
+     Update packet (including the new LSA as contents) out the
+     interface.  The LSA's LS age must be incremented by InfTransDelay
+     (which    must be > 0) when it is copied into the outgoing Link
+     State Update packet (until the LS age field reaches the maximum
+     value of MaxAge). */
+  /* XXX HASSO: Is this IS_DEBUG_OSPF_NSSA really correct? */
+  if (IS_DEBUG_OSPF_NSSA)
+    zlog_debug ("ospf_flood_through_interface(): "
+              "DR/BDR sending upd to int %s", IF_NAME (oi));
+
+  /*  RFC2328  Section 13.3
+      On non-broadcast networks, separate      Link State Update
+      packets must be sent, as unicasts, to each adjacent      neighbor
+      (i.e., those in state Exchange or greater).       The destination
+      IP addresses for these packets are the neighbors' IP
+      addresses.   */
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    {
+      struct route_node *rn;
+      struct ospf_neighbor *nbr;
+
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+        if ((nbr = rn->info) != NULL)
+         if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange)
+           ospf_ls_upd_send_lsa (nbr, lsa, OSPF_SEND_PACKET_DIRECT);
+    }
+  else
+    ospf_ls_upd_send_lsa (oi->nbr_self, lsa, OSPF_SEND_PACKET_INDIRECT);
+
+  return 0;
+}
+
+int
+ospf_flood_through_area (struct ospf_area *area,
+                        struct ospf_neighbor *inbr, struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  int lsa_ack_flag = 0;
+
+  /* All other types are specific to a single area (Area A).  The
+     eligible interfaces are all those interfaces attaching to the
+     Area A.  If Area A is the backbone, this includes all the virtual
+     links.  */
+  for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi))
+    {
+      if (area->area_id.s_addr != OSPF_AREA_BACKBONE &&
+         oi->type ==  OSPF_IFTYPE_VIRTUALLINK) 
+       continue;
+
+      if ((lsa->data->type == OSPF_OPAQUE_LINK_LSA) && (lsa->oi != oi))
+        {
+          /*
+           * Link local scoped Opaque-LSA should only be flooded
+           * for the link on which the LSA has received.
+           */
+          if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+            zlog_debug ("Type-9 Opaque-LSA: lsa->oi(%p) != oi(%p)",
+                        (void *)lsa->oi, (void *)oi);
+          continue;
+        }
+
+      if (ospf_flood_through_interface (oi, inbr, lsa))
+       lsa_ack_flag = 1;
+    }
+
+  return (lsa_ack_flag);
+}
+
+int
+ospf_flood_through_as (struct ospf *ospf, struct ospf_neighbor *inbr,
+                      struct ospf_lsa *lsa)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  int lsa_ack_flag;
+
+  lsa_ack_flag = 0;
+
+  /* The incoming LSA is type 5 or type 7  (AS-EXTERNAL or AS-NSSA )
+
+    Divert the Type-5 LSA's to all non-NSSA/STUB areas
+
+    Divert the Type-7 LSA's to all NSSA areas
+
+     AS-external-LSAs are flooded throughout the entire AS, with the
+     exception of stub areas (see Section 3.6).  The eligible
+     interfaces are all the router's interfaces, excluding virtual
+     links and those interfaces attaching to stub areas.  */
+
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) /* Translated from 7  */
+    if (IS_DEBUG_OSPF_NSSA)
+      zlog_debug ("Flood/AS: NSSA TRANSLATED LSA");
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      int continue_flag = 0;
+      struct listnode *if_node;
+      struct ospf_interface *oi;
+
+      switch (area->external_routing)
+       {
+         /* Don't send AS externals into stub areas.  Various types
+             of support for partial stub areas can be implemented
+             here.  NSSA's will receive Type-7's that have areas
+             matching the originl LSA. */
+       case OSPF_AREA_NSSA:    /* Sending Type 5 or 7 into NSSA area */
+         /* Type-7, flood NSSA area */
+          if (lsa->data->type == OSPF_AS_NSSA_LSA
+             && area == lsa->area)
+           /* We will send it. */
+           continue_flag = 0;
+          else
+           continue_flag = 1;  /* Skip this NSSA area for Type-5's et al */
+          break;
+
+       case OSPF_AREA_TYPE_MAX:
+       case OSPF_AREA_STUB:
+         continue_flag = 1;    /* Skip this area. */
+         break;
+
+       case OSPF_AREA_DEFAULT:
+       default:
+         /* No Type-7 into normal area */
+          if (lsa->data->type == OSPF_AS_NSSA_LSA) 
+           continue_flag = 1; /* skip Type-7 */
+          else
+           continue_flag = 0;  /* Do this area. */
+         break;
+       }
+      
+      /* Do continue for above switch.  Saves a big if then mess */
+      if (continue_flag) 
+       continue; /* main for-loop */
+      
+      /* send to every interface in this area */
+
+      for (ALL_LIST_ELEMENTS_RO (area->oiflist, if_node, oi))
+       {
+         /* Skip virtual links */
+         if (oi->type !=  OSPF_IFTYPE_VIRTUALLINK)
+           if (ospf_flood_through_interface (oi, inbr, lsa)) /* lsa */
+             lsa_ack_flag = 1;
+       }
+    } /* main area for-loop */
+  
+  return (lsa_ack_flag);
+}
+
+int
+ospf_flood_through (struct ospf *ospf,
+                   struct ospf_neighbor *inbr, struct ospf_lsa *lsa)
+{
+  int lsa_ack_flag = 0;
+  
+  /* Type-7 LSA's for NSSA are flooded throughout the AS here, and
+     upon return are updated in the LSDB for Type-7's.  Later,
+     re-fresh will re-send them (and also, if ABR, packet code will
+     translate to Type-5's)
+  
+     As usual, Type-5 LSA's (if not DISCARDED because we are STUB or
+     NSSA) are flooded throughout the AS, and are updated in the
+     global table.  */
+#ifdef ORIGINAL_CODING
+  switch (lsa->data->type)
+    {
+    case OSPF_ROUTER_LSA:
+    case OSPF_NETWORK_LSA:
+    case OSPF_SUMMARY_LSA:
+    case OSPF_ASBR_SUMMARY_LSA:
+    case OSPF_OPAQUE_LINK_LSA: /* ospf_flood_through_interface ? */
+    case OSPF_OPAQUE_AREA_LSA:
+      lsa_ack_flag = ospf_flood_through_area (inbr->oi->area, inbr, lsa);
+      break;
+    case OSPF_AS_EXTERNAL_LSA: /* Type-5 */
+    case OSPF_OPAQUE_AS_LSA:
+      lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa);
+      break;
+      /* Type-7 Only received within NSSA, then flooded */
+    case OSPF_AS_NSSA_LSA:
+      /* Any P-bit was installed with the Type-7. */
+      lsa_ack_flag = ospf_flood_through_area (inbr->oi->area, inbr, lsa);
+
+      if (IS_DEBUG_OSPF_NSSA)
+       zlog_debug ("ospf_flood_through: LOCAL NSSA FLOOD of Type-7.");
+      break;
+    default:
+      break;
+    }
+#else /* ORIGINAL_CODING */
+  /*
+   * At the common sub-sub-function "ospf_flood_through_interface()",
+   * a parameter "inbr" will be used to distinguish the called context
+   * whether the given LSA was received from the neighbor, or the
+   * flooding for the LSA starts from this node (e.g. the LSA was self-
+   * originated, or the LSA is going to be flushed from routing domain).
+   *
+   * So, for consistency reasons, this function "ospf_flood_through()"
+   * should also allow the usage that the given "inbr" parameter to be
+   * NULL. If we do so, corresponding AREA parameter should be referred
+   * by "lsa->area", instead of "inbr->oi->area".
+   */
+  switch (lsa->data->type)
+    {
+    case OSPF_AS_EXTERNAL_LSA: /* Type-5 */
+    case OSPF_OPAQUE_AS_LSA:
+      lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa);
+      break;
+      /* Type-7 Only received within NSSA, then flooded */
+    case OSPF_AS_NSSA_LSA:
+      /* Any P-bit was installed with the Type-7. */
+
+      if (IS_DEBUG_OSPF_NSSA)
+       zlog_debug ("ospf_flood_through: LOCAL NSSA FLOOD of Type-7.");
+      /* Fallthrough */
+    default:
+      lsa_ack_flag = ospf_flood_through_area (lsa->area, inbr, lsa);
+      break;
+    }
+#endif /* ORIGINAL_CODING */
+  
+  return (lsa_ack_flag);
+}
+
+
+
+/* Management functions for neighbor's Link State Request list. */
+void
+ospf_ls_request_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  /*
+   * We cannot make use of the newly introduced callback function
+   * "lsdb->new_lsa_hook" to replace debug output below, just because
+   * it seems no simple and smart way to pass neighbor information to
+   * the common function "ospf_lsdb_add()" -- endo.
+   */
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+      zlog_debug ("RqstL(%lu)++, NBR(%s), LSA[%s]",
+                  ospf_ls_request_count (nbr),
+                  inet_ntoa (nbr->router_id), dump_lsa_key (lsa));
+
+  ospf_lsdb_add (&nbr->ls_req, lsa);
+}
+
+unsigned long
+ospf_ls_request_count (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_count_all (&nbr->ls_req);
+}
+
+int
+ospf_ls_request_isempty (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_isempty (&nbr->ls_req);
+}
+
+/* Remove LSA from neighbor's ls-request list. */
+void
+ospf_ls_request_delete (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  if (nbr->ls_req_last == lsa)
+    {
+      ospf_lsa_unlock (&nbr->ls_req_last);
+      nbr->ls_req_last = NULL;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))       /* -- endo. */
+      zlog_debug ("RqstL(%lu)--, NBR(%s), LSA[%s]",
+                  ospf_ls_request_count (nbr),
+                  inet_ntoa (nbr->router_id), dump_lsa_key (lsa));
+
+  ospf_lsdb_delete (&nbr->ls_req, lsa);
+}
+
+/* Remove all LSA from neighbor's ls-requenst list. */
+void
+ospf_ls_request_delete_all (struct ospf_neighbor *nbr)
+{
+  ospf_lsa_unlock (&nbr->ls_req_last);
+  nbr->ls_req_last = NULL;
+  ospf_lsdb_delete_all (&nbr->ls_req);
+}
+
+/* Lookup LSA from neighbor's ls-request list. */
+struct ospf_lsa *
+ospf_ls_request_lookup (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  return ospf_lsdb_lookup (&nbr->ls_req, lsa);
+}
+
+struct ospf_lsa *
+ospf_ls_request_new (struct lsa_header *lsah)
+{
+  struct ospf_lsa *new;
+
+  new = ospf_lsa_new ();
+  new->data = ospf_lsa_data_new (OSPF_LSA_HEADER_SIZE);
+  memcpy (new->data, lsah, OSPF_LSA_HEADER_SIZE);
+
+  return new;
+}
+
+
+/* Management functions for neighbor's ls-retransmit list. */
+unsigned long
+ospf_ls_retransmit_count (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_count_all (&nbr->ls_rxmt);
+}
+
+unsigned long
+ospf_ls_retransmit_count_self (struct ospf_neighbor *nbr, int lsa_type)
+{
+  return ospf_lsdb_count_self (&nbr->ls_rxmt, lsa_type);
+}
+
+int
+ospf_ls_retransmit_isempty (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_isempty (&nbr->ls_rxmt);
+}
+
+/* Add LSA to be retransmitted to neighbor's ls-retransmit list. */
+void
+ospf_ls_retransmit_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *old;
+
+  old = ospf_ls_retransmit_lookup (nbr, lsa);
+
+  if (ospf_lsa_more_recent (old, lsa) < 0)
+    {
+      if (old)
+       {
+         old->retransmit_counter--;
+         ospf_lsdb_delete (&nbr->ls_rxmt, old);
+       }
+      lsa->retransmit_counter++;
+      /*
+       * We cannot make use of the newly introduced callback function
+       * "lsdb->new_lsa_hook" to replace debug output below, just because
+       * it seems no simple and smart way to pass neighbor information to
+       * the common function "ospf_lsdb_add()" -- endo.
+       */
+      if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+         zlog_debug ("RXmtL(%lu)++, NBR(%s), LSA[%s]",
+                     ospf_ls_retransmit_count (nbr),
+                    inet_ntoa (nbr->router_id), dump_lsa_key (lsa));
+      ospf_lsdb_add (&nbr->ls_rxmt, lsa);
+    }
+}
+
+/* Remove LSA from neibghbor's ls-retransmit list. */
+void
+ospf_ls_retransmit_delete (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  if (ospf_ls_retransmit_lookup (nbr, lsa))
+    {
+      lsa->retransmit_counter--;  
+      if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))           /* -- endo. */
+         zlog_debug ("RXmtL(%lu)--, NBR(%s), LSA[%s]",
+                     ospf_ls_retransmit_count (nbr),
+                    inet_ntoa (nbr->router_id), dump_lsa_key (lsa));
+      ospf_lsdb_delete (&nbr->ls_rxmt, lsa);
+    }
+}
+
+/* Clear neighbor's ls-retransmit list. */
+void
+ospf_ls_retransmit_clear (struct ospf_neighbor *nbr)
+{
+  struct ospf_lsdb *lsdb;
+  int i;
+
+  lsdb = &nbr->ls_rxmt;
+
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      struct route_table *table = lsdb->type[i].db;
+      struct route_node *rn;
+      struct ospf_lsa *lsa;
+
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if ((lsa = rn->info) != NULL)
+         ospf_ls_retransmit_delete (nbr, lsa);
+    }
+
+  ospf_lsa_unlock (&nbr->ls_req_last);
+  nbr->ls_req_last = NULL;
+}
+
+/* Lookup LSA from neighbor's ls-retransmit list. */
+struct ospf_lsa *
+ospf_ls_retransmit_lookup (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  return ospf_lsdb_lookup (&nbr->ls_rxmt, lsa);
+}
+
+static void
+ospf_ls_retransmit_delete_nbr_if (struct ospf_interface *oi,
+                                 struct ospf_lsa *lsa)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+  struct ospf_lsa *lsr;
+
+  if (ospf_if_is_enable (oi))
+    for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+      /* If LSA find in LS-retransmit list, then remove it. */
+      if ((nbr = rn->info) != NULL)
+       {
+         lsr = ospf_ls_retransmit_lookup (nbr, lsa);
+            
+         /* If LSA find in ls-retransmit list, remove it. */
+         if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum)
+           ospf_ls_retransmit_delete (nbr, lsr);
+       }
+}
+
+void
+ospf_ls_retransmit_delete_nbr_area (struct ospf_area *area,
+                                   struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi))
+    ospf_ls_retransmit_delete_nbr_if (oi, lsa);
+}
+
+void
+ospf_ls_retransmit_delete_nbr_as (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    ospf_ls_retransmit_delete_nbr_if (oi, lsa);
+}
+
+
+/* Sets ls_age to MaxAge and floods throu the area. 
+   When we implement ASE routing, there will be anothe function
+   flushing an LSA from the whole domain. */
+void
+ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area)
+{
+  /* Reset the lsa origination time such that it gives
+     more time for the ACK to be received and avoid
+     retransmissions */
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+  lsa->tv_recv = recent_relative_time ();
+  lsa->tv_orig = lsa->tv_recv;
+  ospf_flood_through_area (area, NULL, lsa);
+  ospf_lsa_maxage (area->ospf, lsa);
+}
+
+void
+ospf_lsa_flush_as (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  /* Reset the lsa origination time such that it gives
+     more time for the ACK to be received and avoid
+     retransmissions */
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+  lsa->tv_recv = recent_relative_time ();
+  lsa->tv_orig = lsa->tv_recv;
+  ospf_flood_through_as (ospf, NULL, lsa);
+  ospf_lsa_maxage (ospf, lsa);
+}
+
+void
+ospf_lsa_flush (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+  
+  switch (lsa->data->type)
+    {
+      case OSPF_ROUTER_LSA:
+      case OSPF_NETWORK_LSA:
+      case OSPF_SUMMARY_LSA:
+      case OSPF_ASBR_SUMMARY_LSA:
+      case OSPF_AS_NSSA_LSA:
+      case OSPF_OPAQUE_LINK_LSA:
+      case OSPF_OPAQUE_AREA_LSA:
+        ospf_lsa_flush_area (lsa, lsa->area);
+        break;
+      case OSPF_AS_EXTERNAL_LSA:
+      case OSPF_OPAQUE_AS_LSA:
+        ospf_lsa_flush_as (ospf, lsa);
+        break;
+      default:
+        zlog_info ("%s: Unknown LSA type %u", __func__, lsa->data->type);
+        break;
+    }
+}
diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h
new file mode 100644 (file)
index 0000000..1ab11b8
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * OSPF Flooding -- RFC2328 Section 13.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_FLOOD_H
+#define _ZEBRA_OSPF_FLOOD_H
+
+extern int ospf_flood (struct ospf *, struct ospf_neighbor *,
+                      struct ospf_lsa *, struct ospf_lsa *);
+extern int ospf_flood_through (struct ospf *, struct ospf_neighbor *,
+                              struct ospf_lsa *);
+extern int ospf_flood_through_area (struct ospf_area *,
+                                   struct ospf_neighbor *,
+                                   struct ospf_lsa *);
+extern int ospf_flood_through_as (struct ospf *, struct ospf_neighbor *,
+                                 struct ospf_lsa *);
+
+extern unsigned long ospf_ls_request_count (struct ospf_neighbor *);
+extern int ospf_ls_request_isempty (struct ospf_neighbor *);
+extern struct ospf_lsa *ospf_ls_request_new (struct lsa_header *);
+extern void ospf_ls_request_free (struct ospf_lsa *);
+extern void ospf_ls_request_add (struct ospf_neighbor *, struct ospf_lsa *);
+extern void ospf_ls_request_delete (struct ospf_neighbor *,
+                                   struct ospf_lsa *);
+extern void ospf_ls_request_delete_all (struct ospf_neighbor *);
+extern struct ospf_lsa *ospf_ls_request_lookup (struct ospf_neighbor *,
+                                               struct ospf_lsa *);
+
+extern unsigned long ospf_ls_retransmit_count (struct ospf_neighbor *);
+extern unsigned long ospf_ls_retransmit_count_self (struct ospf_neighbor *,
+                                                   int);
+extern int ospf_ls_retransmit_isempty (struct ospf_neighbor *);
+extern void ospf_ls_retransmit_add (struct ospf_neighbor *,
+                                   struct ospf_lsa *);
+extern void ospf_ls_retransmit_delete (struct ospf_neighbor *,
+                                      struct ospf_lsa *);
+extern void ospf_ls_retransmit_clear (struct ospf_neighbor *);
+extern struct ospf_lsa *ospf_ls_retransmit_lookup (struct ospf_neighbor *,
+                                                  struct ospf_lsa *);
+extern void ospf_ls_retransmit_delete_nbr_area (struct ospf_area *,
+                                               struct ospf_lsa *);
+extern void ospf_ls_retransmit_delete_nbr_as (struct ospf *,
+                                             struct ospf_lsa *);
+extern void ospf_ls_retransmit_add_nbr_all (struct ospf_interface *,
+                                           struct ospf_lsa *);
+
+extern void ospf_flood_lsa_area (struct ospf_lsa *, struct ospf_area *);
+extern void ospf_flood_lsa_as (struct ospf_lsa *);
+extern void ospf_lsa_flush_area (struct ospf_lsa *, struct ospf_area *);
+extern void ospf_lsa_flush_as (struct ospf *, struct ospf_lsa *);
+extern void ospf_lsa_flush (struct ospf *, struct ospf_lsa *);
+extern struct external_info *ospf_external_info_check (struct ospf_lsa *);
+
+extern void ospf_lsdb_init (struct ospf_lsdb *);
+
+#endif /* _ZEBRA_OSPF_FLOOD_H */
diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c
new file mode 100644 (file)
index 0000000..b2d0fae
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * OSPF inter-area routing.
+ * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "table.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_ia.h"
+#include "ospfd/ospf_dump.h"
+
+static struct ospf_route *
+ospf_find_abr_route (struct route_table *rtrs, 
+                     struct prefix_ipv4 *abr,
+                     struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct listnode *node;
+
+  if ((rn = route_node_lookup (rtrs, (struct prefix *) abr)) == NULL)
+    return NULL;
+
+  route_unlock_node (rn);
+
+  for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or))
+    if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id) 
+        && (or->u.std.flags & ROUTER_LSA_BORDER))
+      return or;
+
+  return NULL;
+}
+
+static void
+ospf_ia_network_route (struct ospf *ospf, struct route_table *rt,
+                      struct prefix_ipv4 *p, struct ospf_route *new_or,
+                      struct ospf_route *abr_or)
+{
+  struct route_node *rn1;
+  struct ospf_route *or;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ia_network_route(): processing summary route to %s/%d", 
+              inet_ntoa (p->prefix), p->prefixlen);
+
+  /* Find a route to the same dest */
+  if ((rn1 = route_node_lookup (rt, (struct prefix *) p)))
+    {
+      int res;
+
+      route_unlock_node (rn1);
+
+      if ((or = rn1->info))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_network_route(): "
+                      "Found a route to the same network");
+         /* Check the existing route. */
+         if ((res = ospf_route_cmp (ospf, new_or, or)) < 0)
+           {
+             /* New route is better, so replace old one. */
+             ospf_route_subst (rn1, new_or, abr_or);
+           }
+         else if (res == 0)
+           {
+             /* New and old route are equal, so next hops can be added. */
+             route_lock_node (rn1);
+             ospf_route_copy_nexthops (or, abr_or->paths);
+             route_unlock_node (rn1);
+
+             /* new route can be deleted, because existing route has been updated. */
+             ospf_route_free (new_or);
+           }
+         else
+           {
+             /* New route is worse, so free it. */
+             ospf_route_free (new_or);
+             return;
+           }
+       } /* if (or)*/
+    } /*if (rn1)*/
+  else
+    { /* no route */
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_ia_network_route(): add new route to %s/%d",
+                  inet_ntoa (p->prefix), p->prefixlen);
+      ospf_route_add (rt, p, new_or, abr_or);
+    }
+}
+
+static void
+ospf_ia_router_route (struct ospf *ospf, struct route_table *rtrs,
+                     struct prefix_ipv4 *p,
+                      struct ospf_route *new_or, struct ospf_route *abr_or)
+{
+  struct ospf_route *or = NULL;
+  struct route_node *rn;
+  int ret;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ia_router_route(): considering %s/%d", 
+              inet_ntoa (p->prefix), p->prefixlen);
+  /* Find a route to the same dest */
+  rn = route_node_get (rtrs, (struct prefix *) p);
+   
+  if (rn->info == NULL)
+    /* This is a new route */
+    rn->info = list_new ();
+  else
+    {
+      struct ospf_area *or_area;
+      or_area = ospf_area_lookup_by_area_id (ospf, new_or->u.std.area_id);
+      assert (or_area);
+      /* This is an additional route */
+      route_unlock_node (rn);
+      or = ospf_find_asbr_route_through_area (rtrs, p, or_area);
+    }
+
+  if (or)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_ia_router_route(): "
+                  "a route to the same ABR through the same area exists");
+      /* New route is better */
+      if ((ret = ospf_route_cmp (ospf, new_or, or)) < 0)
+       {
+         listnode_delete (rn->info, or);
+         ospf_route_free (or);
+         /* proceed down */
+       }
+      /* Routes are the same */
+      else if (ret == 0)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_router_route(): merging the new route");
+
+         ospf_route_copy_nexthops (or, abr_or->paths);
+         ospf_route_free (new_or);
+         return;
+       }
+      /* New route is worse */
+      else
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_router_route(): skipping the new route");
+         ospf_route_free (new_or);
+         return;
+       }
+    }
+
+  ospf_route_copy_nexthops (new_or, abr_or->paths);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ia_router_route(): adding the new route"); 
+
+  listnode_add (rn->info, new_or);
+}
+
+
+static int
+process_summary_lsa (struct ospf_area *area, struct route_table *rt,
+                    struct route_table *rtrs, struct ospf_lsa *lsa)
+{
+  struct ospf *ospf = area->ospf;
+  struct ospf_area_range *range;
+  struct ospf_route *abr_or, *new_or;
+  struct summary_lsa *sl;
+  struct prefix_ipv4 p, abr;
+  u_int32_t metric;
+
+  if (lsa == NULL)
+    return 0;
+
+  sl = (struct summary_lsa *) lsa->data;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("process_summary_lsa(): LS ID: %s", inet_ntoa (sl->header.id));
+
+  metric = GET_METRIC (sl->metric);
+   
+  if (metric == OSPF_LS_INFINITY)
+    return 0;
+
+  if (IS_LSA_MAXAGE (lsa))
+    return 0;
+
+  if (ospf_lsa_is_self_originated (area->ospf, lsa))
+    return 0;
+
+  p.family = AF_INET;
+  p.prefix = sl->header.id;
+   
+  if (sl->header.type == OSPF_SUMMARY_LSA)
+    p.prefixlen = ip_masklen (sl->mask);
+  else
+    p.prefixlen = IPV4_MAX_BITLEN;
+      
+  apply_mask_ipv4 (&p);
+
+  if (sl->header.type == OSPF_SUMMARY_LSA &&
+      (range = ospf_area_range_match_any (ospf, &p)) &&
+      ospf_area_range_active (range))
+    return 0;
+
+  /* XXX: This check seems dubious to me. If an ABR has already decided
+   * to consider summaries received in this area, then why would one wish
+   * to exclude default? 
+   */
+  if (IS_OSPF_ABR(ospf) && 
+      ospf->abr_type != OSPF_ABR_STAND &&
+      area->external_routing != OSPF_AREA_DEFAULT &&
+      p.prefix.s_addr == OSPF_DEFAULT_DESTINATION &&
+      p.prefixlen == 0)
+    return 0; /* Ignore summary default from a stub area */
+
+  abr.family = AF_INET;
+  abr.prefix = sl->header.adv_router;
+  abr.prefixlen = IPV4_MAX_BITLEN;
+  apply_mask_ipv4 (&abr);
+
+  abr_or = ospf_find_abr_route (rtrs, &abr, area);
+
+  if (abr_or == NULL)
+    return 0;
+
+  new_or = ospf_route_new ();
+  new_or->type = OSPF_DESTINATION_NETWORK;
+  new_or->id = sl->header.id;
+  new_or->mask = sl->mask;
+  new_or->u.std.options = sl->header.options;
+  new_or->u.std.origin = (struct lsa_header *) sl;
+  new_or->cost = abr_or->cost + metric;
+  new_or->u.std.area_id = area->area_id;
+  new_or->u.std.external_routing = area->external_routing;
+  new_or->path_type = OSPF_PATH_INTER_AREA;
+
+  if (sl->header.type == OSPF_SUMMARY_LSA)
+    ospf_ia_network_route (ospf, rt, &p, new_or, abr_or);
+  else 
+    {
+      new_or->type = OSPF_DESTINATION_ROUTER;
+      new_or->u.std.flags = ROUTER_LSA_EXTERNAL;
+      ospf_ia_router_route (ospf, rtrs, &p, new_or, abr_or);
+    }
+
+  return 0;
+}
+
+static void
+ospf_examine_summaries (struct ospf_area *area,
+                       struct route_table *lsdb_rt,
+                        struct route_table *rt,
+                        struct route_table *rtrs)
+{
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+
+  LSDB_LOOP (lsdb_rt, rn, lsa)
+    process_summary_lsa (area, rt, rtrs, lsa);
+}
+
+int
+ospf_area_is_transit (struct ospf_area *area)
+{
+  return (area->transit == OSPF_TRANSIT_TRUE) ||
+    ospf_full_virtual_nbrs(area); /* Cisco forgets to set the V-bit :( */
+}
+
+static void
+ospf_update_network_route (struct ospf *ospf,
+                          struct route_table *rt, 
+                           struct route_table *rtrs,
+                           struct summary_lsa *lsa,
+                           struct prefix_ipv4 *p,
+                           struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_route *or, *abr_or, *new_or;
+  struct prefix_ipv4 abr;
+  u_int32_t cost;
+
+  abr.family = AF_INET;
+  abr.prefix =lsa->header.adv_router;
+  abr.prefixlen = IPV4_MAX_BITLEN;
+  apply_mask_ipv4 (&abr);
+
+  abr_or = ospf_find_abr_route (rtrs, &abr, area);
+
+  if (abr_or == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): can't find a route to the ABR");
+      return;
+    }
+
+  cost = abr_or->cost + GET_METRIC (lsa->metric);
+
+  rn = route_node_lookup (rt, (struct prefix *) p);
+
+  if (! rn)
+    {
+      if (ospf->abr_type != OSPF_ABR_SHORTCUT)
+        return; /* Standard ABR can update only already installed
+                   backbone paths                                       */
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): "
+                  "Allowing Shortcut ABR to add new route");
+      new_or = ospf_route_new ();
+      new_or->type = OSPF_DESTINATION_NETWORK;
+      new_or->id = lsa->header.id;
+      new_or->mask = lsa->mask;
+      new_or->u.std.options = lsa->header.options;
+      new_or->u.std.origin = (struct lsa_header *) lsa;
+      new_or->cost = cost;
+      new_or->u.std.area_id = area->area_id;
+      new_or->u.std.external_routing = area->external_routing;
+      new_or->path_type = OSPF_PATH_INTER_AREA;
+      ospf_route_add (rt, p, new_or, abr_or);
+
+      return;
+    }
+  else
+    {
+      route_unlock_node (rn);
+      if (rn->info == NULL)
+        return;
+    }
+
+  or = rn->info;
+
+  if (or->path_type != OSPF_PATH_INTRA_AREA &&
+      or->path_type != OSPF_PATH_INTER_AREA)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): ERR: path type is wrong");
+      return;
+    }
+
+  if (ospf->abr_type == OSPF_ABR_SHORTCUT)
+    {
+      if (or->path_type == OSPF_PATH_INTRA_AREA &&
+         !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_update_network_route(): Shortcut: "
+                      "this intra-area path is not backbone");
+         return;
+       }
+    }
+  else   /* Not Shortcut ABR */
+    {
+      if (!OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_update_network_route(): "
+                      "route is not BB-associated");
+         return; /* We can update only BB routes */
+       }
+    }
+
+  if (or->cost < cost)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): new route is worse");
+      return;
+    }
+
+  if (or->cost == cost)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): "
+                  "new route is same distance, adding nexthops");
+      ospf_route_copy_nexthops (or, abr_or->paths);
+    }
+
+  if (or->cost > cost)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_network_route(): "
+                  "new route is better, overriding nexthops");
+      ospf_route_subst_nexthops (or, abr_or->paths);
+      or->cost = cost;
+
+      if ((ospf->abr_type == OSPF_ABR_SHORTCUT) &&
+         !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id))
+       {
+         or->path_type = OSPF_PATH_INTER_AREA;
+         or->u.std.area_id = area->area_id;
+         or->u.std.external_routing = area->external_routing;
+          /* Note that we can do this only in Shortcut ABR mode,
+             because standard ABR must leave the route type and area
+             unchanged
+          */
+        }
+    }
+}
+
+static void
+ospf_update_router_route (struct ospf *ospf,
+                         struct route_table *rtrs, 
+                          struct summary_lsa *lsa,
+                          struct prefix_ipv4 *p,
+                          struct ospf_area *area)
+{
+  struct ospf_route *or, *abr_or, *new_or;
+  struct prefix_ipv4 abr;
+  u_int32_t cost;
+
+  abr.family = AF_INET;
+  abr.prefix = lsa->header.adv_router;
+  abr.prefixlen = IPV4_MAX_BITLEN;
+  apply_mask_ipv4 (&abr);
+
+  abr_or = ospf_find_abr_route (rtrs, &abr, area);
+
+  if (abr_or == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_update_router_route(): can't find a route to the ABR");
+      return;
+    }
+
+  cost = abr_or->cost + GET_METRIC (lsa->metric);
+
+  /* First try to find a backbone path,
+     because standard ABR can update only BB-associated paths */
+
+  if ((ospf->backbone == NULL) &&
+      (ospf->abr_type != OSPF_ABR_SHORTCUT))
+     return; /* no BB area, not Shortcut ABR, exiting */
+  
+  /* find the backbone route, if possible */
+  if ((ospf->backbone == NULL)
+      || !(or = ospf_find_asbr_route_through_area (rtrs, p, ospf->backbone)))
+    {
+      if (ospf->abr_type != OSPF_ABR_SHORTCUT)
+
+         /* route to ASBR through the BB not found
+            the router is not Shortcut ABR, exiting */
+
+          return;
+      else
+       /* We're a Shortcut ABR*/
+       {
+         /* Let it either add a new router or update the route
+            through the same (non-BB) area. */
+
+         new_or = ospf_route_new ();
+         new_or->type = OSPF_DESTINATION_ROUTER;
+         new_or->id = lsa->header.id;
+         new_or->mask = lsa->mask;
+         new_or->u.std.options = lsa->header.options;
+         new_or->u.std.origin = (struct lsa_header *)lsa;
+         new_or->cost = cost;
+         new_or->u.std.area_id = area->area_id;
+         new_or->u.std.external_routing = area->external_routing;
+         new_or->path_type = OSPF_PATH_INTER_AREA;
+         new_or->u.std.flags = ROUTER_LSA_EXTERNAL;
+         ospf_ia_router_route (ospf, rtrs, p, new_or, abr_or);
+
+          return;
+        }
+    }
+
+  /* At this point the "or" is always bb-associated */
+
+  if (!(or->u.std.flags & ROUTER_LSA_EXTERNAL))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_upd_router_route(): the remote router is not an ASBR");
+      return;
+    }
+
+  if (or->path_type != OSPF_PATH_INTRA_AREA &&
+      or->path_type != OSPF_PATH_INTER_AREA)
+    return;
+
+  if (or->cost < cost)
+    return;
+
+  else if (or->cost == cost)
+    ospf_route_copy_nexthops (or, abr_or->paths);
+
+  else if (or->cost > cost)
+    {
+      ospf_route_subst_nexthops (or, abr_or->paths);
+      or->cost = cost;
+
+      /* Even if the ABR runs in Shortcut mode, we can't change
+         the path type and area, because the "or" is always bb-associated
+         at this point and even Shortcut ABR can't change these attributes */
+    }
+}
+
+static int
+process_transit_summary_lsa (struct ospf_area *area, struct route_table *rt,
+                            struct route_table *rtrs, struct ospf_lsa *lsa)
+{
+  struct ospf *ospf = area->ospf;
+  struct summary_lsa *sl;
+  struct prefix_ipv4 p;
+  u_int32_t metric;
+
+  if (lsa == NULL)
+    return 0;
+
+  sl = (struct summary_lsa *) lsa->data;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("process_transit_summaries(): LS ID: %s",
+              inet_ntoa (lsa->data->id));
+  metric = GET_METRIC (sl->metric);
+   
+  if (metric == OSPF_LS_INFINITY)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("process_transit_summaries(): metric is infinity, skip");
+      return 0;
+    }
+
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("process_transit_summaries(): This LSA is too old");
+      return 0;
+    }
+
+  if (ospf_lsa_is_self_originated (area->ospf, lsa))
+    { 
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("process_transit_summaries(): This LSA is mine, skip");
+      return 0;
+    }
+
+  p.family = AF_INET;
+  p.prefix = sl->header.id;
+   
+  if (sl->header.type == OSPF_SUMMARY_LSA)
+    p.prefixlen = ip_masklen (sl->mask);
+  else
+    p.prefixlen = IPV4_MAX_BITLEN;
+      
+  apply_mask_ipv4 (&p);
+
+  if (sl->header.type == OSPF_SUMMARY_LSA)
+    ospf_update_network_route (ospf, rt, rtrs, sl, &p, area);
+  else
+    ospf_update_router_route (ospf, rtrs, sl, &p, area);
+  return 0;
+}
+
+static void
+ospf_examine_transit_summaries (struct ospf_area *area,
+                               struct route_table *lsdb_rt,
+                                struct route_table *rt,
+                                struct route_table *rtrs)
+{
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+
+  LSDB_LOOP (lsdb_rt, rn, lsa)
+    process_transit_summary_lsa (area, rt, rtrs, lsa);
+}
+
+void
+ospf_ia_routing (struct ospf *ospf,
+                struct route_table *rt,
+                 struct route_table *rtrs)
+{
+  struct ospf_area * area;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ia_routing():start");
+
+  if (IS_OSPF_ABR (ospf))
+    {
+      struct listnode *node; 
+      struct ospf_area *area;
+
+      switch (ospf->abr_type)
+        {
+        case OSPF_ABR_STAND:
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_routing():Standard ABR");
+
+          if ((area = ospf->backbone))
+            {
+              struct listnode *node;
+
+             if (IS_DEBUG_OSPF_EVENT)
+               {
+                 zlog_debug ("ospf_ia_routing():backbone area found");
+                 zlog_debug ("ospf_ia_routing():examining summaries");
+               }
+
+              OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs);
+
+             for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+                if (area != ospf->backbone)
+                  if (ospf_area_is_transit (area))
+                    OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs);
+            }
+          else
+           if (IS_DEBUG_OSPF_EVENT)
+             zlog_debug ("ospf_ia_routing():backbone area NOT found");
+          break;
+        case OSPF_ABR_IBM:
+        case OSPF_ABR_CISCO:
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_routing():Alternative Cisco/IBM ABR");
+          area = ospf->backbone; /* Find the BB */
+
+          /* If we have an active BB connection */
+          if (area && ospf_act_bb_connection (ospf))
+            {
+             if (IS_DEBUG_OSPF_EVENT)
+               {
+                 zlog_debug ("ospf_ia_routing(): backbone area found");
+                 zlog_debug ("ospf_ia_routing(): examining BB summaries");
+               }
+
+              OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs);
+
+             for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+                if (area != ospf->backbone)
+                  if (ospf_area_is_transit (area))
+                    OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs);
+            }
+          else
+            { /* No active BB connection--consider all areas */
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("ospf_ia_routing(): "
+                          "Active BB connection not found");
+             for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+                OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs);
+            }
+          break;
+        case OSPF_ABR_SHORTCUT:
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_ia_routing():Alternative Shortcut");
+          area = ospf->backbone; /* Find the BB */
+
+          /* If we have an active BB connection */
+          if (area && ospf_act_bb_connection (ospf))
+            {
+             if (IS_DEBUG_OSPF_EVENT)
+               {
+                 zlog_debug ("ospf_ia_routing(): backbone area found");
+                 zlog_debug ("ospf_ia_routing(): examining BB summaries");
+               }
+              OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs);
+            }
+
+         for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+            if (area != ospf->backbone)
+              if (ospf_area_is_transit (area) ||
+                  ((area->shortcut_configured != OSPF_SHORTCUT_DISABLE) &&
+                  ((ospf->backbone == NULL) ||
+                  ((area->shortcut_configured == OSPF_SHORTCUT_ENABLE) &&
+                  area->shortcut_capability))))
+                OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs);
+          break;
+        default:
+          break;
+        }
+    }
+  else 
+    {
+      struct listnode *node;
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_ia_routing():not ABR, considering all areas");
+
+      for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+        OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs);
+    }
+}
diff --git a/ospfd/ospf_ia.h b/ospfd/ospf_ia.h
new file mode 100644 (file)
index 0000000..b65b938
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * OSPF inter-area routing.
+ * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_IA_H
+#define _ZEBRA_OSPF_IA_H
+
+/* Macros. */
+#define OSPF_EXAMINE_SUMMARIES_ALL(A,N,R) \
+       { \
+         ospf_examine_summaries ((A), SUMMARY_LSDB ((A)), (N), (R)); \
+         ospf_examine_summaries ((A), ASBR_SUMMARY_LSDB ((A)), (N), (R)); \
+       }
+
+#define OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL(A,N,R) \
+       { \
+         ospf_examine_transit_summaries ((A), SUMMARY_LSDB ((A)), (N), (R)); \
+         ospf_examine_transit_summaries ((A), ASBR_SUMMARY_LSDB ((A)), (N), (R)); \
+       }
+
+extern void ospf_ia_routing (struct ospf *, struct route_table *,
+                            struct route_table *);
+extern int ospf_area_is_transit (struct ospf_area *);
+
+#endif /* _ZEBRA_OSPF_IA_H */
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
new file mode 100644 (file)
index 0000000..a46ca6d
--- /dev/null
@@ -0,0 +1,1278 @@
+/*
+ * OSPF Interface functions.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "stream.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_dump.h"
+#ifdef HAVE_SNMP
+#include "ospfd/ospf_snmp.h"
+#endif /* HAVE_SNMP */
+
+
+int
+ospf_if_get_output_cost (struct ospf_interface *oi)
+{
+  /* If all else fails, use default OSPF cost */
+  u_int32_t cost;
+  u_int32_t bw, refbw;
+
+  bw = oi->ifp->bandwidth ? oi->ifp->bandwidth : OSPF_DEFAULT_BANDWIDTH;
+  refbw = oi->ospf->ref_bandwidth;
+
+  /* A specifed ip ospf cost overrides a calculated one. */
+  if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (oi->ifp), output_cost_cmd) ||
+      OSPF_IF_PARAM_CONFIGURED (oi->params, output_cost_cmd))
+    cost = OSPF_IF_PARAM (oi, output_cost_cmd);
+  /* See if a cost can be calculated from the zebra processes
+     interface bandwidth field. */
+  else
+    {
+      cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5);
+      if (cost < 1)
+       cost = 1;
+      else if (cost > 65535)
+       cost = 65535;
+    }
+
+  return cost;
+}
+
+void
+ospf_if_recalculate_output_cost (struct interface *ifp)
+{
+  u_int32_t newcost;
+  struct route_node *rn;
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi;
+      
+      if ( (oi = rn->info) == NULL)
+       continue;
+
+      newcost = ospf_if_get_output_cost (oi);
+
+      /* Is actual output cost changed? */
+      if (oi->output_cost != newcost)
+       {
+         oi->output_cost = newcost;
+         ospf_router_lsa_update_area (oi->area);
+       }
+    }
+}
+
+/* Simulate down/up on the interface.  This is needed, for example, when 
+   the MTU changes. */
+void
+ospf_if_reset(struct interface *ifp)
+{
+  struct route_node *rn;
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi;
+      
+      if ( (oi = rn->info) == NULL)
+       continue;
+
+      ospf_if_down(oi);
+      ospf_if_up(oi);
+    }
+}
+
+void
+ospf_if_reset_variables (struct ospf_interface *oi)
+{
+  /* Set default values. */
+  /* don't clear this flag.  oi->flag = OSPF_IF_DISABLE; */
+
+  if (oi->vl_data)
+    oi->type = OSPF_IFTYPE_VIRTUALLINK;
+  else 
+  /* preserve network-type */
+  if (oi->type != OSPF_IFTYPE_NBMA)
+    oi->type = OSPF_IFTYPE_BROADCAST;
+
+  oi->state = ISM_Down;
+
+  oi->crypt_seqnum = 0;
+
+  /* This must be short, (less than RxmtInterval) 
+     - RFC 2328 Section 13.5 para 3.  Set to 1 second to avoid Acks being
+       held back for too long - MAG */
+  oi->v_ls_ack = 1;  
+}
+
+void
+ospf_if_reset_type (struct interface *ifp, u_char type)
+{
+  struct route_node *rn;
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+      u_char orig_ism_state;
+      
+      if (!oi)
+       continue;
+      
+      orig_ism_state = oi->state;
+      OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown);
+      
+      oi->type = IF_DEF_PARAMS (ifp)->type;
+      
+      if (orig_ism_state > ISM_Down)
+        OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceUp);
+    }
+}
+
+/* lookup oi for specified prefix/ifp */
+struct ospf_interface *
+ospf_if_table_lookup (struct interface *ifp, struct prefix *prefix)
+{
+  struct prefix p;
+  struct route_node *rn;
+  struct ospf_interface *rninfo = NULL;
+  
+  p = *prefix;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  
+  /* route_node_get implicitely locks */
+  if ((rn = route_node_lookup (IF_OIFS (ifp), &p)))
+    {
+      rninfo = (struct ospf_interface *) rn->info;
+      route_unlock_node (rn);
+    }
+  
+  return rninfo;
+}
+
+static void
+ospf_add_to_if (struct interface *ifp, struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct prefix p;
+
+  p = *oi->address;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+
+  rn = route_node_get (IF_OIFS (ifp), &p);
+  /* rn->info should either be NULL or equal to this oi
+   * as route_node_get may return an existing node
+   */
+  assert (!rn->info || rn->info == oi);
+  rn->info = oi;
+}
+
+static void
+ospf_delete_from_if (struct interface *ifp, struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct prefix p;
+
+  p = *oi->address;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+
+  rn = route_node_lookup (IF_OIFS (oi->ifp), &p);
+  assert (rn);
+  assert (rn->info);
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+}
+
+struct ospf_interface *
+ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p)
+{
+  struct ospf_interface *oi;
+
+  if ((oi = ospf_if_table_lookup (ifp, p)) == NULL)
+    {
+      oi = XCALLOC (MTYPE_OSPF_IF, sizeof (struct ospf_interface));
+      memset (oi, 0, sizeof (struct ospf_interface));
+    }
+  else
+    return oi;
+    
+  /* Set zebra interface pointer. */
+  oi->ifp = ifp;
+  oi->address = p;
+  
+  ospf_add_to_if (ifp, oi);
+  listnode_add (ospf->oiflist, oi);
+  
+  /* Initialize neighbor list. */
+  oi->nbrs = route_table_init ();
+
+  /* Initialize static neighbor list. */
+  oi->nbr_nbma = list_new ();
+
+  /* Initialize Link State Acknowledgment list. */
+  oi->ls_ack = list_new ();
+  oi->ls_ack_direct.ls_ack = list_new ();
+
+  /* Set default values. */
+  ospf_if_reset_variables (oi);
+
+  /* Set pseudo neighbor to Null */
+  oi->nbr_self = NULL;
+
+  oi->ls_upd_queue = route_table_init ();
+  oi->t_ls_upd_event = NULL;
+  oi->t_ls_ack_direct = NULL;
+
+  oi->crypt_seqnum = time (NULL);
+
+  ospf_opaque_type9_lsa_init (oi);
+
+  oi->ospf = ospf;
+  
+  return oi;
+}
+
+/* Restore an interface to its pre UP state
+   Used from ism_interface_down only */
+void
+ospf_if_cleanup (struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct listnode *node, *nnode;
+  struct ospf_neighbor *nbr;
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct ospf_lsa *lsa;
+
+  /* oi->nbrs and oi->nbr_nbma should be deleted on InterfaceDown event */
+  /* delete all static neighbors attached to this interface */
+  for (ALL_LIST_ELEMENTS (oi->nbr_nbma, node, nnode, nbr_nbma))
+    {
+      OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll);
+
+      if (nbr_nbma->nbr)
+       {
+         nbr_nbma->nbr->nbr_nbma = NULL;
+         nbr_nbma->nbr = NULL;
+       }
+
+      nbr_nbma->oi = NULL;
+      
+      listnode_delete (oi->nbr_nbma, nbr_nbma);
+    }
+
+  /* send Neighbor event KillNbr to all associated neighbors. */
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      if (nbr != oi->nbr_self)
+       OSPF_NSM_EVENT_EXECUTE (nbr, NSM_KillNbr);
+
+  /* Cleanup Link State Acknowlegdment list. */
+  for (ALL_LIST_ELEMENTS (oi->ls_ack, node, nnode, lsa))
+    ospf_lsa_unlock (&lsa); /* oi->ls_ack */
+  list_delete_all_node (oi->ls_ack);
+
+  oi->crypt_seqnum = 0;
+  
+  /* Empty link state update queue */
+  ospf_ls_upd_queue_empty (oi);
+  
+  /* Reset pseudo neighbor. */
+  ospf_nbr_self_reset (oi);
+}
+
+void
+ospf_if_free (struct ospf_interface *oi)
+{
+  ospf_if_down (oi);
+
+  assert (oi->state == ISM_Down);
+
+  ospf_opaque_type9_lsa_term (oi);
+
+  /* Free Pseudo Neighbour */
+  ospf_nbr_delete (oi->nbr_self);
+  
+  route_table_finish (oi->nbrs);
+  route_table_finish (oi->ls_upd_queue);
+  
+  /* Free any lists that should be freed */
+  list_free (oi->nbr_nbma);
+  
+  list_free (oi->ls_ack);
+  list_free (oi->ls_ack_direct.ls_ack);
+  
+  ospf_delete_from_if (oi->ifp, oi);
+
+  listnode_delete (oi->ospf->oiflist, oi);
+  listnode_delete (oi->area->oiflist, oi);
+
+  thread_cancel_event (master, oi);
+
+  memset (oi, 0, sizeof (*oi));
+  XFREE (MTYPE_OSPF_IF, oi);
+}
+
+
+/*
+*  check if interface with given address is configured and
+*  return it if yes.  special treatment for PtP networks.
+*/
+struct ospf_interface *
+ospf_if_is_configured (struct ospf *ospf, struct in_addr *address)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  struct prefix_ipv4 addr;
+
+  addr.family = AF_INET;
+  addr.prefix = *address;
+  addr.prefixlen = IPV4_MAX_PREFIXLEN;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+      {
+        if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+         {
+           /* special leniency: match if addr is anywhere on peer subnet */
+           if (prefix_match(CONNECTED_PREFIX(oi->connected),
+                            (struct prefix *)&addr))
+             return oi;
+         }
+        else
+         {
+           if (IPV4_ADDR_SAME (address, &oi->address->u.prefix4))
+             return oi;
+         }
+      }
+  return NULL;
+}
+
+int
+ospf_if_is_up (struct ospf_interface *oi)
+{
+  return if_is_up (oi->ifp);
+}
+
+struct ospf_interface *
+ospf_if_exists (struct ospf_interface *oic)
+{ 
+  struct listnode *node;
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+
+  if ((ospf = ospf_lookup ()) == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    if (oi == oic)
+      return oi;
+
+  return NULL;
+}
+
+/* Lookup OSPF interface by router LSA posistion */
+struct ospf_interface *
+ospf_if_lookup_by_lsa_pos (struct ospf_area *area, int lsa_pos)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+    {
+      if (lsa_pos >= oi->lsa_pos_beg && lsa_pos < oi->lsa_pos_end)
+       return oi;
+    }
+  return NULL;
+}
+
+struct ospf_interface *
+ospf_if_lookup_by_local_addr (struct ospf *ospf,
+                             struct interface *ifp, struct in_addr address)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+  
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+      {
+       if (ifp && oi->ifp != ifp)
+         continue;
+       
+       if (IPV4_ADDR_SAME (&address, &oi->address->u.prefix4))
+         return oi;
+      }
+
+  return NULL;
+}
+
+struct ospf_interface *
+ospf_if_lookup_by_prefix (struct ospf *ospf, struct prefix_ipv4 *p)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+  
+  /* Check each Interface. */
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    {
+      if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+       {
+         struct prefix ptmp;
+
+         prefix_copy (&ptmp, CONNECTED_PREFIX(oi->connected));
+         apply_mask (&ptmp);
+         if (prefix_same (&ptmp, (struct prefix *) p))
+           return oi;
+       }
+    }
+  return NULL;
+}
+
+/* determine receiving interface by ifp and source address */
+struct ospf_interface *
+ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src,
+                       struct interface *ifp)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 addr;
+  struct ospf_interface *oi, *match;
+
+  addr.family = AF_INET;
+  addr.prefix = src;
+  addr.prefixlen = IPV4_MAX_BITLEN;
+
+  match = NULL;
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      oi = rn->info;
+
+      if (!oi) /* oi can be NULL for PtP aliases */
+       continue;
+
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+       continue;
+
+      if (if_is_loopback (oi->ifp))
+        continue;
+
+      if (prefix_match (CONNECTED_PREFIX(oi->connected),
+                       (struct prefix *) &addr))
+       {
+         if ( (match == NULL) || 
+              (match->address->prefixlen < oi->address->prefixlen)
+            )
+           match = oi;
+       }
+    }
+
+  return match;
+}
+
+void
+ospf_if_stream_set (struct ospf_interface *oi)
+{
+  /* set output fifo queue. */
+  if (oi->obuf == NULL) 
+    oi->obuf = ospf_fifo_new ();
+}
+
+void
+ospf_if_stream_unset (struct ospf_interface *oi)
+{
+  struct ospf *ospf = oi->ospf;
+
+  if (oi->obuf)
+    {
+     ospf_fifo_free (oi->obuf);
+     oi->obuf = NULL;
+
+     if (oi->on_write_q)
+       {
+        listnode_delete (ospf->oi_write_q, oi);
+         if (list_isempty(ospf->oi_write_q))
+           OSPF_TIMER_OFF (ospf->t_write);
+        oi->on_write_q = 0;
+       }
+    }
+}
+
+
+static struct ospf_if_params *
+ospf_new_if_params (void)
+{
+  struct ospf_if_params *oip;
+
+  oip = XCALLOC (MTYPE_OSPF_IF_PARAMS, sizeof (struct ospf_if_params));
+
+  if (!oip)
+    return NULL;
+
+  UNSET_IF_PARAM (oip, output_cost_cmd);
+  UNSET_IF_PARAM (oip, transmit_delay);
+  UNSET_IF_PARAM (oip, retransmit_interval);
+  UNSET_IF_PARAM (oip, passive_interface);
+  UNSET_IF_PARAM (oip, v_hello);
+  UNSET_IF_PARAM (oip, fast_hello);
+  UNSET_IF_PARAM (oip, v_wait);
+  UNSET_IF_PARAM (oip, priority);
+  UNSET_IF_PARAM (oip, type);
+  UNSET_IF_PARAM (oip, auth_simple);
+  UNSET_IF_PARAM (oip, auth_crypt);
+  UNSET_IF_PARAM (oip, auth_type);
+
+  oip->auth_crypt = list_new ();
+  
+  oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER);
+
+  return oip;
+}
+
+void
+ospf_del_if_params (struct ospf_if_params *oip)
+{
+  list_delete (oip->auth_crypt);
+  XFREE (MTYPE_OSPF_IF_PARAMS, oip);
+}
+
+void
+ospf_free_if_params (struct interface *ifp, struct in_addr addr)
+{
+  struct ospf_if_params *oip;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = addr;
+  rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p);
+  if (!rn || !rn->info)
+    return;
+
+  oip = rn->info;
+  route_unlock_node (rn);
+  
+  if (!OSPF_IF_PARAM_CONFIGURED (oip, output_cost_cmd) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, transmit_delay) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, retransmit_interval) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, passive_interface) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, v_hello) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, fast_hello) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, v_wait) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, priority) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, type) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, auth_simple) &&
+      !OSPF_IF_PARAM_CONFIGURED (oip, auth_type) &&
+      listcount (oip->auth_crypt) == 0 &&
+      ntohl (oip->network_lsa_seqnum) != OSPF_INITIAL_SEQUENCE_NUMBER)
+    {
+      ospf_del_if_params (oip);
+      rn->info = NULL;
+      route_unlock_node (rn);
+    }
+}
+
+struct ospf_if_params *
+ospf_lookup_if_params (struct interface *ifp, struct in_addr addr)
+{
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = addr;
+
+  rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p);
+  
+  if (rn)
+    {
+      route_unlock_node (rn);
+      return rn->info;
+    }
+
+  return NULL;
+}
+
+struct ospf_if_params *
+ospf_get_if_params (struct interface *ifp, struct in_addr addr)
+{
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = addr;
+
+  rn = route_node_get (IF_OIFS_PARAMS (ifp), (struct prefix*)&p);
+  
+  if (rn->info == NULL)
+    rn->info = ospf_new_if_params ();
+  else
+    route_unlock_node (rn);
+  
+  return rn->info;
+}
+
+void
+ospf_if_update_params (struct interface *ifp, struct in_addr addr)
+{
+  struct route_node *rn;
+  struct ospf_interface *oi;
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      if ((oi = rn->info) == NULL)
+       continue;
+
+      if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &addr))
+       oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4);
+    }
+}
+
+int
+ospf_if_new_hook (struct interface *ifp)
+{
+  int rc = 0;
+
+  ifp->info = XCALLOC (MTYPE_OSPF_IF_INFO, sizeof (struct ospf_if_info));
+  
+  IF_OIFS (ifp) = route_table_init ();
+  IF_OIFS_PARAMS (ifp) = route_table_init ();
+  
+  IF_DEF_PARAMS (ifp) = ospf_new_if_params ();
+  
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), transmit_delay);
+  IF_DEF_PARAMS (ifp)->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT;
+  
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), retransmit_interval);
+  IF_DEF_PARAMS (ifp)->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), priority);
+  IF_DEF_PARAMS (ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT;
+
+  IF_DEF_PARAMS (ifp)->mtu_ignore = OSPF_MTU_IGNORE_DEFAULT;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_hello);
+  IF_DEF_PARAMS (ifp)->v_hello = OSPF_HELLO_INTERVAL_DEFAULT;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), fast_hello);
+  IF_DEF_PARAMS (ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_wait);
+  IF_DEF_PARAMS (ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_simple);
+  memset (IF_DEF_PARAMS (ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE);
+  
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type);
+  IF_DEF_PARAMS (ifp)->auth_type = OSPF_AUTH_NOTSET;
+  
+  rc = ospf_opaque_new_if (ifp);
+  return rc;
+}
+
+static int
+ospf_if_delete_hook (struct interface *ifp)
+{
+  int rc = 0;
+  struct route_node *rn;
+  rc = ospf_opaque_del_if (ifp);
+
+  route_table_finish (IF_OIFS (ifp));
+
+  for (rn = route_top (IF_OIFS_PARAMS (ifp)); rn; rn = route_next (rn))
+    if (rn->info)
+      ospf_del_if_params (rn->info);
+  route_table_finish (IF_OIFS_PARAMS (ifp));
+
+  ospf_del_if_params ((struct ospf_if_params *) IF_DEF_PARAMS (ifp));
+  XFREE (MTYPE_OSPF_IF_INFO, ifp->info);
+  ifp->info = NULL;
+
+  return rc;
+}
+
+int
+ospf_if_is_enable (struct ospf_interface *oi)
+{
+  if (!if_is_loopback (oi->ifp))
+    if (if_is_up (oi->ifp))
+       return 1;
+
+  return 0;
+}
+
+void
+ospf_if_set_multicast(struct ospf_interface *oi)
+{
+  if ((oi->state > ISM_Loopback) &&
+      (oi->type != OSPF_IFTYPE_LOOPBACK) &&
+      (oi->type != OSPF_IFTYPE_VIRTUALLINK) &&
+      (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE))
+    {
+      /* The interface should belong to the OSPF-all-routers group. */
+      if (!OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) &&
+         (ospf_if_add_allspfrouters(oi->ospf, oi->address,
+                                    oi->ifp->ifindex) >= 0))
+         /* Set the flag only if the system call to join succeeded. */
+         OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS);
+    }
+  else
+    {
+      /* The interface should NOT belong to the OSPF-all-routers group. */
+      if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS))
+        {
+          /* Only actually drop if this is the last reference */
+          if (OI_MEMBER_COUNT(oi, MEMBER_ALLROUTERS) == 1)
+           ospf_if_drop_allspfrouters (oi->ospf, oi->address,
+                                       oi->ifp->ifindex);
+         /* Unset the flag regardless of whether the system call to leave
+            the group succeeded, since it's much safer to assume that
+            we are not a member. */
+          OI_MEMBER_LEFT(oi,MEMBER_ALLROUTERS);
+        }
+    }
+
+  if (((oi->type == OSPF_IFTYPE_BROADCAST) ||
+       (oi->type == OSPF_IFTYPE_POINTOPOINT)) &&
+      ((oi->state == ISM_DR) || (oi->state == ISM_Backup)) &&
+      (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE))
+    {
+      /* The interface should belong to the OSPF-designated-routers group. */
+      if (!OI_MEMBER_CHECK(oi, MEMBER_DROUTERS) &&
+         (ospf_if_add_alldrouters(oi->ospf, oi->address,
+                                  oi->ifp->ifindex) >= 0))
+       /* Set the flag only if the system call to join succeeded. */
+       OI_MEMBER_JOINED(oi, MEMBER_DROUTERS);
+    }
+  else
+    {
+      /* The interface should NOT belong to the OSPF-designated-routers group */
+      if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS))
+        {
+          /* drop only if last reference */
+          if (OI_MEMBER_COUNT(oi, MEMBER_DROUTERS) == 1)
+           ospf_if_drop_alldrouters(oi->ospf, oi->address, oi->ifp->ifindex);
+          
+         /* Unset the flag regardless of whether the system call to leave
+            the group succeeded, since it's much safer to assume that
+            we are not a member. */
+          OI_MEMBER_LEFT(oi, MEMBER_DROUTERS);
+        }
+    }
+}
+
+int
+ospf_if_up (struct ospf_interface *oi)
+{
+  if (oi == NULL)
+    return 0;
+
+  if (oi->type == OSPF_IFTYPE_LOOPBACK)
+    OSPF_ISM_EVENT_SCHEDULE (oi, ISM_LoopInd);
+  else
+    {
+      struct ospf *ospf = ospf_lookup ();
+      if (ospf != NULL)
+        ospf_adjust_sndbuflen (ospf, oi->ifp->mtu);
+      else
+        zlog_warn ("%s: ospf_lookup() returned NULL", __func__);
+      ospf_if_stream_set (oi);
+      OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceUp);
+    }
+
+  return 1;
+}
+
+int
+ospf_if_down (struct ospf_interface *oi)
+{
+  if (oi == NULL)
+    return 0;
+
+  OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown);
+  /* delete position in router LSA */
+  oi->lsa_pos_beg = 0;
+  oi->lsa_pos_end = 0;
+  /* Shutdown packet reception and sending */
+  ospf_if_stream_unset (oi);
+
+  return 1;
+}
+
+
+/* Virtual Link related functions. */
+
+struct ospf_vl_data *
+ospf_vl_data_new (struct ospf_area *area, struct in_addr vl_peer)
+{
+  struct ospf_vl_data *vl_data;
+
+  vl_data = XCALLOC (MTYPE_OSPF_VL_DATA, sizeof (struct ospf_vl_data));
+
+  vl_data->vl_peer.s_addr = vl_peer.s_addr;
+  vl_data->vl_area_id = area->area_id;
+  vl_data->format = area->format;
+
+  return vl_data;
+}
+
+void
+ospf_vl_data_free (struct ospf_vl_data *vl_data)
+{
+  XFREE (MTYPE_OSPF_VL_DATA, vl_data);
+}
+
+u_int vlink_count = 0;
+
+struct ospf_interface * 
+ospf_vl_new (struct ospf *ospf, struct ospf_vl_data *vl_data)
+{
+  struct ospf_interface * voi;
+  struct interface * vi;
+  char   ifname[INTERFACE_NAMSIZ + 1];
+  struct ospf_area *area;
+  struct in_addr area_id;
+  struct connected *co;
+  struct prefix_ipv4 *p;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): Start");
+  if (vlink_count == OSPF_VL_MAX_COUNT)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_vl_new(): Alarm: "
+                  "cannot create more than OSPF_MAX_VL_COUNT virtual links");
+      return NULL;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): creating pseudo zebra interface");
+
+  snprintf (ifname, sizeof(ifname), "VLINK%d", vlink_count);
+  vi = if_create (ifname, strnlen(ifname, sizeof(ifname)));
+  /* Ensure that linkdetection is not enabled on the stub interfaces
+   * created for OSPF virtual links. */
+  UNSET_FLAG(vi->status, ZEBRA_INTERFACE_LINKDETECTION);
+  co = connected_new ();
+  co->ifp = vi;
+  listnode_add (vi->connected, co);
+
+  p = prefix_ipv4_new ();
+  p->family = AF_INET;
+  p->prefix.s_addr = 0;
+  p->prefixlen = 0;
+  co->address = (struct prefix *)p;
+  
+  voi = ospf_if_new (ospf, vi, co->address);
+  if (voi == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_vl_new(): Alarm: OSPF int structure is not created");
+      return NULL;
+    }
+  voi->connected = co;
+  voi->vl_data = vl_data;
+  voi->ifp->mtu = OSPF_VL_MTU;
+  voi->type = OSPF_IFTYPE_VIRTUALLINK;
+
+  vlink_count++;
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): Created name: %s", ifname);
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): set if->name to %s", vi->name);
+
+  area_id.s_addr = 0;
+  area = ospf_area_get (ospf, area_id, OSPF_AREA_ID_FORMAT_ADDRESS);
+  voi->area = area;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): set associated area to the backbone");
+
+  /* Add pseudo neighbor. */
+  ospf_nbr_self_reset (voi);
+
+  ospf_area_add_if (voi->area, voi);
+
+  ospf_if_stream_set (voi);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_vl_new(): Stop");
+  return voi;
+}
+
+static void
+ospf_vl_if_delete (struct ospf_vl_data *vl_data)
+{
+  struct interface *ifp = vl_data->vl_oi->ifp;
+  vl_data->vl_oi->address->u.prefix4.s_addr = 0;
+  vl_data->vl_oi->address->prefixlen = 0;
+  ospf_if_free (vl_data->vl_oi);
+  if_delete (ifp);
+  vlink_count--;
+}
+
+/* Look up vl_data for given peer, optionally qualified to be in the
+ * specified area. NULL area returns first found..
+ */
+struct ospf_vl_data *
+ospf_vl_lookup (struct ospf *ospf, struct ospf_area *area,
+                struct in_addr vl_peer)
+{
+  struct ospf_vl_data *vl_data;
+  struct listnode *node;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("%s: Looking for %s", __func__, inet_ntoa (vl_peer));
+      if (area)
+        zlog_debug ("%s: in area %s", __func__, inet_ntoa (area->area_id));
+    }
+  
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("%s: VL %s, peer %s", __func__,
+                    vl_data->vl_oi->ifp->name,
+                    inet_ntoa (vl_data->vl_peer));
+      
+      if (area && !IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id))
+        continue;
+      
+      if (IPV4_ADDR_SAME (&vl_data->vl_peer, &vl_peer))
+        return vl_data;
+    }
+
+  return NULL;
+}
+
+static void 
+ospf_vl_shutdown (struct ospf_vl_data *vl_data)
+{
+  struct ospf_interface *oi;
+
+  if ((oi = vl_data->vl_oi) == NULL)
+    return;
+
+  oi->address->u.prefix4.s_addr = 0;
+  oi->address->prefixlen = 0;
+
+  UNSET_FLAG (oi->ifp->flags, IFF_UP);
+  /* OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceDown); */
+  OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown);
+}
+
+void
+ospf_vl_add (struct ospf *ospf, struct ospf_vl_data *vl_data)
+{
+  listnode_add (ospf->vlinks, vl_data);
+#ifdef HAVE_SNMP
+  ospf_snmp_vl_add (vl_data);
+#endif /* HAVE_SNMP */
+}
+
+void
+ospf_vl_delete (struct ospf *ospf, struct ospf_vl_data *vl_data)
+{
+  ospf_vl_shutdown (vl_data);
+  ospf_vl_if_delete (vl_data);
+
+#ifdef HAVE_SNMP
+  ospf_snmp_vl_delete (vl_data);
+#endif /* HAVE_SNMP */
+  listnode_delete (ospf->vlinks, vl_data);
+
+  ospf_vl_data_free (vl_data);
+}
+
+static int
+ospf_vl_set_params (struct ospf_vl_data *vl_data, struct vertex *v)
+{
+  int changed = 0;
+  struct ospf_interface *voi;
+  struct listnode *node;
+  struct vertex_parent *vp = NULL;
+  unsigned int i;
+  struct router_lsa *rl;
+
+  voi = vl_data->vl_oi;
+
+  if (voi->output_cost != v->distance)
+    {
+     
+      voi->output_cost = v->distance;
+      changed = 1;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
+    {
+      vl_data->nexthop.oi = vp->nexthop->oi;
+      vl_data->nexthop.router = vp->nexthop->router;
+      
+      if (!IPV4_ADDR_SAME(&voi->address->u.prefix4,
+                          &vl_data->nexthop.oi->address->u.prefix4))
+        changed = 1;
+        
+      voi->address->u.prefix4 = vl_data->nexthop.oi->address->u.prefix4;
+      voi->address->prefixlen = vl_data->nexthop.oi->address->prefixlen;
+
+      break; /* We take the first interface. */
+    }
+
+  rl = (struct router_lsa *)v->lsa;
+
+  /* use SPF determined backlink index in struct vertex
+   * for virtual link destination address
+   */
+  if (vp && vp->backlink >= 0)
+    {
+      if (!IPV4_ADDR_SAME (&vl_data->peer_addr,
+                           &rl->link[vp->backlink].link_data))
+        changed = 1;
+      vl_data->peer_addr = rl->link[vp->backlink].link_data;
+    }
+  else
+    {
+      /* This is highly odd, there is no backlink index
+       * there should be due to the ospf_spf_has_link() check
+       * in SPF. Lets warn and try pick a link anyway.
+       */
+      zlog_warn ("ospf_vl_set_params: No backlink for %s!",
+                 vl_data->vl_oi->ifp->name);
+      for (i = 0; i < ntohs (rl->links); i++)
+        {
+          switch (rl->link[i].type)
+            {
+              case LSA_LINK_TYPE_VIRTUALLINK:
+                if (IS_DEBUG_OSPF_EVENT)
+                  zlog_debug ("found back link through VL");
+              case LSA_LINK_TYPE_TRANSIT:
+              case LSA_LINK_TYPE_POINTOPOINT:
+                if (!IPV4_ADDR_SAME (&vl_data->peer_addr,
+                                     &rl->link[i].link_data))
+                  changed = 1;
+                vl_data->peer_addr = rl->link[i].link_data;
+            }
+        }
+    }
+    
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("%s: %s peer address: %s, cost: %d,%schanged", __func__,
+               vl_data->vl_oi->ifp->name,
+               inet_ntoa(vl_data->peer_addr),
+               voi->output_cost,
+               (changed ? " " : " un"));
+               
+  return changed;
+}
+
+
+void
+ospf_vl_up_check (struct ospf_area *area, struct in_addr rid,
+                  struct vertex *v)
+{
+  struct ospf *ospf = area->ospf;
+  struct listnode *node;
+  struct ospf_vl_data *vl_data;
+  struct ospf_interface *oi;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("ospf_vl_up_check(): Start");
+      zlog_debug ("ospf_vl_up_check(): Router ID is %s", inet_ntoa (rid));
+      zlog_debug ("ospf_vl_up_check(): Area is %s", inet_ntoa (area->area_id));
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       {
+         zlog_debug ("%s: considering VL, %s in area %s", __func__,
+                    vl_data->vl_oi->ifp->name,
+                    inet_ntoa (vl_data->vl_area_id));
+         zlog_debug ("%s: peer ID: %s", __func__,
+                    inet_ntoa (vl_data->vl_peer));
+       }
+
+      if (IPV4_ADDR_SAME (&vl_data->vl_peer, &rid) &&
+          IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id))
+        {
+          oi = vl_data->vl_oi;
+          SET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED);
+
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_vl_up_check(): this VL matched");
+
+          if (oi->state == ISM_Down)
+            {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("ospf_vl_up_check(): VL is down, waking it up");
+              SET_FLAG (oi->ifp->flags, IFF_UP);
+              OSPF_ISM_EVENT_EXECUTE(oi,ISM_InterfaceUp);
+            }
+
+         if (ospf_vl_set_params (vl_data, v))
+           {
+             if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+               zlog_debug ("ospf_vl_up_check: VL cost change,"
+                          " scheduling router lsa refresh");
+             if (ospf->backbone)
+               ospf_router_lsa_update_area (ospf->backbone);
+             else if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+               zlog_debug ("ospf_vl_up_check: VL cost change, no backbone!");
+           }
+        }
+    }
+}
+
+void
+ospf_vl_unapprove (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_vl_data *vl_data;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data))
+    UNSET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED);
+}
+
+void
+ospf_vl_shut_unapproved (struct ospf *ospf)
+{
+  struct listnode *node, *nnode;
+  struct ospf_vl_data *vl_data;
+
+  for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data))
+    if (!CHECK_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED))
+      ospf_vl_shutdown (vl_data);
+}
+
+int
+ospf_full_virtual_nbrs (struct ospf_area *area)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("counting fully adjacent virtual neighbors in area %s",
+                inet_ntoa (area->area_id));
+      zlog_debug ("there are %d of them", area->full_vls);
+    }
+
+  return area->full_vls;
+}
+
+int
+ospf_vls_in_area (struct ospf_area *area)
+{
+  struct listnode *node;
+  struct ospf_vl_data *vl_data;
+  int c = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (area->ospf->vlinks, node, vl_data))
+    if (IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id))
+      c++;
+
+  return c;
+}
+
+
+struct crypt_key *
+ospf_crypt_key_new ()
+{
+  return XCALLOC (MTYPE_OSPF_CRYPT_KEY, sizeof (struct crypt_key));
+}
+
+void
+ospf_crypt_key_add (struct list *crypt, struct crypt_key *ck)
+{
+  listnode_add (crypt, ck);
+}
+
+struct crypt_key *
+ospf_crypt_key_lookup (struct list *auth_crypt, u_char key_id)
+{
+  struct listnode *node;
+  struct crypt_key *ck;
+
+  for (ALL_LIST_ELEMENTS_RO (auth_crypt, node, ck))
+    if (ck->key_id == key_id)
+      return ck;
+
+  return NULL;
+}
+
+int
+ospf_crypt_key_delete (struct list *auth_crypt, u_char key_id)
+{
+  struct listnode *node, *nnode;
+  struct crypt_key *ck;
+
+  for (ALL_LIST_ELEMENTS (auth_crypt, node, nnode, ck))
+    {
+      if (ck->key_id == key_id)
+        {
+          listnode_delete (auth_crypt, ck);
+          XFREE (MTYPE_OSPF_CRYPT_KEY, ck);
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+u_char
+ospf_default_iftype(struct interface *ifp)
+{
+  if (if_is_pointopoint (ifp))
+    return OSPF_IFTYPE_POINTOPOINT;
+  else if (if_is_loopback (ifp))
+    return OSPF_IFTYPE_LOOPBACK;
+  else
+    return OSPF_IFTYPE_BROADCAST;
+}
+
+void
+ospf_if_init ()
+{
+  /* Initialize Zebra interface data structure. */
+  om->iflist = iflist;
+  if_add_hook (IF_NEW_HOOK, ospf_if_new_hook);
+  if_add_hook (IF_DELETE_HOOK, ospf_if_delete_hook);
+}
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
new file mode 100644 (file)
index 0000000..7070353
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * OSPF Interface functions.
+ * Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_INTERFACE_H
+#define _ZEBRA_OSPF_INTERFACE_H
+
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+
+#define IF_OSPF_IF_INFO(I) ((struct ospf_if_info *)((I)->info))
+#define IF_DEF_PARAMS(I) (IF_OSPF_IF_INFO (I)->def_params)
+#define IF_OIFS(I)  (IF_OSPF_IF_INFO (I)->oifs)
+#define IF_OIFS_PARAMS(I) (IF_OSPF_IF_INFO (I)->params)
+
+/* Despite the name, this macro probably is for specialist use only */
+#define OSPF_IF_PARAM_CONFIGURED(S, P) ((S) && (S)->P##__config)
+
+/* Test whether an OSPF interface parameter is set, generally, given some
+ * existing ospf interface
+ */
+#define OSPF_IF_PARAM_IS_SET(O,P) \
+      (OSPF_IF_PARAM_CONFIGURED ((O)->params, P) || \
+      OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp)->P))
+
+#define OSPF_IF_PARAM(O, P) \
+        (OSPF_IF_PARAM_CONFIGURED ((O)->params, P)?\
+                        (O)->params->P:IF_DEF_PARAMS((O)->ifp)->P)
+
+#define DECLARE_IF_PARAM(T, P) T P; u_char P##__config:1
+#define UNSET_IF_PARAM(S, P) ((S)->P##__config) = 0
+#define SET_IF_PARAM(S, P) ((S)->P##__config) = 1
+
+struct ospf_if_params
+{
+  DECLARE_IF_PARAM (u_int32_t, transmit_delay); /* Interface Transmisson Delay */
+  DECLARE_IF_PARAM (u_int32_t, output_cost_cmd);/* Command Interface Output Cost */
+  DECLARE_IF_PARAM (u_int32_t, retransmit_interval); /* Retransmission Interval */
+  DECLARE_IF_PARAM (u_char, passive_interface);      /* OSPF Interface is passive: no sending or receiving (no need to join multicast groups) */
+  DECLARE_IF_PARAM (u_char, priority);               /* OSPF Interface priority */
+  DECLARE_IF_PARAM (struct in_addr, if_area);        /* Enable OSPF on this interface with area if_area */
+  DECLARE_IF_PARAM (u_char, type);                   /* type of interface */
+#define OSPF_IF_ACTIVE                  0
+#define OSPF_IF_PASSIVE                        1
+
+#define OSPF_IF_PASSIVE_STATUS(O) \
+       (OSPF_IF_PARAM_CONFIGURED((O)->params, passive_interface) ? \
+         (O)->params->passive_interface : \
+         (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp), passive_interface) ? \
+           IF_DEF_PARAMS((O)->ifp)->passive_interface : \
+           (O)->ospf->passive_interface_default))
+  
+  DECLARE_IF_PARAM (u_int32_t, v_hello);             /* Hello Interval */
+  DECLARE_IF_PARAM (u_int32_t, v_wait);              /* Router Dead Interval */
+
+  /* MTU mismatch check (see RFC2328, chap 10.6) */ 
+  DECLARE_IF_PARAM (u_char, mtu_ignore);
+  
+  /* Fast-Hellos */
+  DECLARE_IF_PARAM (u_char, fast_hello);
+  
+  /* Authentication data. */
+  u_char auth_simple[OSPF_AUTH_SIMPLE_SIZE + 1];       /* Simple password. */
+  u_char auth_simple__config:1;
+  
+  DECLARE_IF_PARAM (struct list *, auth_crypt);    /* List of Auth cryptographic data. */
+  DECLARE_IF_PARAM (int, auth_type);               /* OSPF authentication type */
+  
+  /* Other, non-configuration state */
+  u_int32_t network_lsa_seqnum;                /* Network LSA seqnum */
+};
+
+enum
+{
+  MEMBER_ALLROUTERS = 0,
+  MEMBER_DROUTERS,
+  MEMBER_MAX,
+};
+
+struct ospf_if_info
+{
+  struct ospf_if_params *def_params;
+  struct route_table *params;
+  struct route_table *oifs;
+  unsigned int membership_counts[MEMBER_MAX];  /* multicast group refcnts */
+};
+
+struct ospf_interface;
+
+struct ospf_vl_data
+{
+  struct in_addr    vl_peer;      /* Router-ID of the peer for VLs. */
+  struct in_addr    vl_area_id;           /* Transit area for this VL. */
+  int format;                      /* area ID format */
+  struct ospf_interface *vl_oi;           /* Interface data structure for the VL. */
+  struct vertex_nexthop nexthop;   /* Nexthop router and oi to use */
+  struct in_addr    peer_addr;    /* Address used to reach the peer. */
+  u_char flags;
+};
+
+
+#define OSPF_VL_MAX_COUNT 256
+#define OSPF_VL_MTU      1500
+
+#define OSPF_VL_FLAG_APPROVED 0x01
+
+struct crypt_key
+{
+  u_char key_id;
+  u_char auth_key[OSPF_AUTH_MD5_SIZE + 1];
+};
+
+/* OSPF interface structure. */
+struct ospf_interface
+{
+  /* This interface's parent ospf instance. */
+  struct ospf *ospf;
+
+  /* OSPF Area. */
+  struct ospf_area *area;
+
+  /* Position range in Router LSA */
+  uint16_t lsa_pos_beg; /* inclusive, >= */
+  uint16_t lsa_pos_end; /* exclusive, <  */
+
+  /* Interface data from zebra. */
+  struct interface *ifp;
+  struct ospf_vl_data *vl_data;                /* Data for Virtual Link */
+  
+  /* Packet send buffer. */
+  struct ospf_fifo *obuf;              /* Output queue */
+
+  /* OSPF Network Type. */
+  u_char type;
+
+  /* State of Interface State Machine. */
+  u_char state;
+
+  /* To which multicast groups do we currently belong? */
+  u_char multicast_memberships;
+#define OI_MEMBER_FLAG(M) (1 << (M))
+#define OI_MEMBER_COUNT(O,M) (IF_OSPF_IF_INFO(oi->ifp)->membership_counts[(M)])
+#define OI_MEMBER_CHECK(O,M) \
+    (CHECK_FLAG((O)->multicast_memberships, OI_MEMBER_FLAG(M)))
+#define OI_MEMBER_JOINED(O,M) \
+  do { \
+    SET_FLAG ((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \
+    IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]++; \
+  } while (0)
+#define OI_MEMBER_LEFT(O,M) \
+  do { \
+    UNSET_FLAG ((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \
+    IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]--; \
+  } while (0)
+
+  struct prefix *address;              /* Interface prefix */
+  struct connected *connected;          /* Pointer to connected */ 
+
+  /* Configured varables. */
+  struct ospf_if_params *params;
+  
+  u_int32_t crypt_seqnum;              /* Cryptographic Sequence Number */ 
+  u_int32_t output_cost;               /* Acutual Interface Output Cost */
+
+  /* Neighbor information. */
+  struct route_table *nbrs;             /* OSPF Neighbor List */
+  struct ospf_neighbor *nbr_self;      /* Neighbor Self */
+#define DR(I)                  ((I)->nbr_self->d_router)
+#define BDR(I)                 ((I)->nbr_self->bd_router)
+#define OPTIONS(I)             ((I)->nbr_self->options)
+#define PRIORITY(I)            ((I)->nbr_self->priority)
+
+  /* List of configured NBMA neighbor. */
+  struct list *nbr_nbma;
+
+  /* self-originated LSAs. */
+  struct ospf_lsa *network_lsa_self;   /* network-LSA. */
+  struct list *opaque_lsa_self;                        /* Type-9 Opaque-LSAs */
+
+  struct route_table *ls_upd_queue;
+
+  struct list *ls_ack;                 /* Link State Acknowledgment list. */
+  
+  struct
+  {
+    struct list *ls_ack;
+    struct in_addr dst;
+  } ls_ack_direct;
+
+  /* Timer values. */
+  u_int32_t v_ls_ack;                  /* Delayed Link State Acknowledgment */
+
+  /* Threads. */
+  struct thread *t_hello;               /* timer */
+  struct thread *t_wait;                /* timer */
+  struct thread *t_ls_ack;              /* timer */
+  struct thread *t_ls_ack_direct;       /* event */
+  struct thread *t_ls_upd_event;        /* event */
+  struct thread *t_opaque_lsa_self;     /* Type-9 Opaque-LSAs */
+
+  int on_write_q;
+  
+  /* Statistics fields. */
+  u_int32_t hello_in;          /* Hello message input count. */
+  u_int32_t hello_out;         /* Hello message output count. */
+  u_int32_t db_desc_in;         /* database desc. message input count. */
+  u_int32_t db_desc_out;        /* database desc. message output count. */
+  u_int32_t ls_req_in;          /* LS request message input count. */
+  u_int32_t ls_req_out;         /* LS request message output count. */
+  u_int32_t ls_upd_in;          /* LS update message input count. */
+  u_int32_t ls_upd_out;         /* LS update message output count. */
+  u_int32_t ls_ack_in;          /* LS Ack message input count. */
+  u_int32_t ls_ack_out;         /* LS Ack message output count. */
+  u_int32_t discarded;         /* discarded input count by error. */
+  u_int32_t state_change;      /* Number of status change. */
+
+  u_int32_t full_nbrs;
+};
+
+/* Prototypes. */
+extern char *ospf_if_name (struct ospf_interface *);
+extern struct ospf_interface *ospf_if_new (struct ospf *, struct interface *,
+                                          struct prefix *);
+extern void ospf_if_cleanup (struct ospf_interface *);
+extern void ospf_if_free (struct ospf_interface *);
+extern int ospf_if_up (struct ospf_interface *);
+extern int ospf_if_down (struct ospf_interface *);
+
+extern int ospf_if_is_up (struct ospf_interface *);
+extern struct ospf_interface *ospf_if_exists (struct ospf_interface *);
+extern struct ospf_interface *ospf_if_lookup_by_lsa_pos (struct ospf_area *,
+                                                        int);
+extern struct ospf_interface *ospf_if_lookup_by_local_addr (struct ospf *,
+                                                           struct interface
+                                                           *,
+                                                           struct in_addr);
+extern struct ospf_interface *ospf_if_lookup_by_prefix (struct ospf *,
+                                                       struct prefix_ipv4 *);
+extern struct ospf_interface *ospf_if_table_lookup (struct interface *,
+                                                   struct prefix *);
+extern struct ospf_interface *ospf_if_addr_local (struct in_addr);
+extern struct ospf_interface *ospf_if_lookup_recv_if (struct ospf *,
+                                                     struct in_addr,
+                                                     struct interface *);
+extern struct ospf_interface *ospf_if_is_configured (struct ospf *,
+                                                    struct in_addr *);
+
+extern struct ospf_if_params *ospf_lookup_if_params (struct interface *,
+                                                    struct in_addr);
+extern struct ospf_if_params *ospf_get_if_params (struct interface *,
+                                                 struct in_addr);
+extern void ospf_del_if_params (struct ospf_if_params *);
+extern void ospf_free_if_params (struct interface *, struct in_addr);
+extern void ospf_if_update_params (struct interface *, struct in_addr);
+
+extern int ospf_if_new_hook (struct interface *);
+extern void ospf_if_init (void);
+extern void ospf_if_stream_set (struct ospf_interface *);
+extern void ospf_if_stream_unset (struct ospf_interface *);
+extern void ospf_if_reset_variables (struct ospf_interface *);
+extern void ospf_if_reset_type (struct interface *, u_char type);
+extern int ospf_if_is_enable (struct ospf_interface *);
+extern int ospf_if_get_output_cost (struct ospf_interface *);
+extern void ospf_if_recalculate_output_cost (struct interface *);
+
+/* Simulate down/up on the interface. */
+extern void ospf_if_reset (struct interface *);
+
+extern struct ospf_interface *ospf_vl_new (struct ospf *,
+                                          struct ospf_vl_data *);
+extern struct ospf_vl_data *ospf_vl_data_new (struct ospf_area *,
+                                             struct in_addr);
+extern struct ospf_vl_data *ospf_vl_lookup (struct ospf *, struct ospf_area *,
+                                           struct in_addr);
+extern void ospf_vl_data_free (struct ospf_vl_data *);
+extern void ospf_vl_add (struct ospf *, struct ospf_vl_data *);
+extern void ospf_vl_delete (struct ospf *, struct ospf_vl_data *);
+extern void ospf_vl_up_check (struct ospf_area *, struct in_addr,
+                             struct vertex *);
+extern void ospf_vl_unapprove (struct ospf *);
+extern void ospf_vl_shut_unapproved (struct ospf *);
+extern int ospf_full_virtual_nbrs (struct ospf_area *);
+extern int ospf_vls_in_area (struct ospf_area *);
+
+extern struct crypt_key *ospf_crypt_key_lookup (struct list *, u_char);
+extern struct crypt_key *ospf_crypt_key_new (void);
+extern void ospf_crypt_key_add (struct list *, struct crypt_key *);
+extern int ospf_crypt_key_delete (struct list *, u_char);
+
+extern u_char ospf_default_iftype (struct interface *ifp);
+
+/* Set all multicast memberships appropriately based on the type and
+   state of the interface. */
+extern void ospf_if_set_multicast (struct ospf_interface *);
+
+#endif /* _ZEBRA_OSPF_INTERFACE_H */
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c
new file mode 100644 (file)
index 0000000..ae6d0cd
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * OSPF version 2  Interface State Machine
+ *   From RFC2328 [OSPF Version 2] 
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_snmp.h"
+
+/* elect DR and BDR. Refer to RFC2319 section 9.4 */
+static struct ospf_neighbor *
+ospf_dr_election_sub (struct list *routers)
+{
+  struct listnode *node;
+  struct ospf_neighbor *nbr, *max = NULL;
+
+  /* Choose highest router priority.
+     In case of tie, choose highest Router ID. */
+  for (ALL_LIST_ELEMENTS_RO (routers, node, nbr))
+    {
+      if (max == NULL)
+       max = nbr;
+      else
+       {
+         if (max->priority < nbr->priority)
+           max = nbr;
+         else if (max->priority == nbr->priority)
+           if (IPV4_ADDR_CMP (&max->router_id, &nbr->router_id) < 0)
+             max = nbr;
+       }
+    }
+
+  return max;
+}
+
+static struct ospf_neighbor *
+ospf_elect_dr (struct ospf_interface *oi, struct list *el_list)
+{
+  struct list *dr_list;
+  struct listnode *node;
+  struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL;
+
+  dr_list = list_new ();
+
+  /* Add neighbors to the list. */
+  for (ALL_LIST_ELEMENTS_RO (el_list, node, nbr))
+    {
+      /* neighbor declared to be DR. */
+      if (NBR_IS_DR (nbr))
+       listnode_add (dr_list, nbr);
+
+      /* Preserve neighbor BDR. */
+      if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4))
+       bdr = nbr;
+    }
+
+  /* Elect Designated Router. */
+  if (listcount (dr_list) > 0)
+    dr = ospf_dr_election_sub (dr_list);
+  else
+    dr = bdr;
+
+  /* Set DR to interface. */
+  if (dr)
+    DR (oi) = dr->address.u.prefix4;
+  else
+    DR (oi).s_addr = 0;
+
+  list_delete (dr_list);
+
+  return dr;
+}
+
+static struct ospf_neighbor *
+ospf_elect_bdr (struct ospf_interface *oi, struct list *el_list)
+{
+  struct list *bdr_list, *no_dr_list;
+  struct listnode *node;
+  struct ospf_neighbor *nbr, *bdr = NULL;
+
+  bdr_list = list_new ();
+  no_dr_list = list_new ();
+
+  /* Add neighbors to the list. */
+  for (ALL_LIST_ELEMENTS_RO (el_list, node, nbr))
+    {
+      /* neighbor declared to be DR. */
+      if (NBR_IS_DR (nbr))
+       continue;
+
+      /* neighbor declared to be BDR. */
+      if (NBR_IS_BDR (nbr))
+       listnode_add (bdr_list, nbr);
+
+      listnode_add (no_dr_list, nbr);
+    }
+
+  /* Elect Backup Designated Router. */
+  if (listcount (bdr_list) > 0)
+    bdr = ospf_dr_election_sub (bdr_list);
+  else
+    bdr = ospf_dr_election_sub (no_dr_list);
+
+  /* Set BDR to interface. */
+  if (bdr)
+    BDR (oi) = bdr->address.u.prefix4;
+  else
+    BDR (oi).s_addr = 0;
+
+  list_delete (bdr_list);
+  list_delete (no_dr_list);
+
+  return bdr;
+}
+
+static int
+ospf_ism_state (struct ospf_interface *oi)
+{
+  if (IPV4_ADDR_SAME (&DR (oi), &oi->address->u.prefix4))
+    return ISM_DR;
+  else if (IPV4_ADDR_SAME (&BDR (oi), &oi->address->u.prefix4))
+    return ISM_Backup;
+  else
+    return ISM_DROther;
+}
+
+static void
+ospf_dr_eligible_routers (struct route_table *nbrs, struct list *el_list)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  for (rn = route_top (nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      /* Ignore 0.0.0.0 node*/
+      if (nbr->router_id.s_addr != 0)
+       /* Is neighbor eligible? */
+       if (nbr->priority > 0)
+         /* Is neighbor upper 2-Way? */
+         if (nbr->state >= NSM_TwoWay)
+           listnode_add (el_list, nbr);
+}
+
+/* Generate AdjOK? NSM event. */
+static void
+ospf_dr_change (struct ospf *ospf, struct route_table *nbrs)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  for (rn = route_top (nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      /* Ignore 0.0.0.0 node*/
+      if (nbr->router_id.s_addr != 0)
+       /* Is neighbor upper 2-Way? */
+       if (nbr->state >= NSM_TwoWay)
+         /* Ignore myself. */
+         if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf->router_id))
+           OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_AdjOK);
+}
+
+static int
+ospf_dr_election (struct ospf_interface *oi)
+{
+  struct in_addr old_dr, old_bdr;
+  int old_state, new_state;
+  struct list *el_list;
+
+  /* backup current values. */
+  old_dr = DR (oi);
+  old_bdr = BDR (oi);
+  old_state = oi->state;
+
+  el_list = list_new ();
+
+  /* List eligible routers. */
+  ospf_dr_eligible_routers (oi->nbrs, el_list);
+
+  /* First election of DR and BDR. */
+  ospf_elect_bdr (oi, el_list);
+  ospf_elect_dr (oi, el_list);
+
+  new_state = ospf_ism_state (oi);
+
+  zlog_debug ("DR-Election[1st]: Backup %s", inet_ntoa (BDR (oi)));
+  zlog_debug ("DR-Election[1st]: DR     %s", inet_ntoa (DR (oi)));
+
+  if (new_state != old_state &&
+      !(new_state == ISM_DROther && old_state < ISM_DROther))
+    {
+      ospf_elect_bdr (oi, el_list);
+      ospf_elect_dr (oi, el_list); 
+
+      new_state = ospf_ism_state (oi);
+
+      zlog_debug ("DR-Election[2nd]: Backup %s", inet_ntoa (BDR (oi)));
+      zlog_debug ("DR-Election[2nd]: DR     %s", inet_ntoa (DR (oi)));
+    }
+
+  list_delete (el_list);
+
+  /* if DR or BDR changes, cause AdjOK? neighbor event. */
+  if (!IPV4_ADDR_SAME (&old_dr, &DR (oi)) ||
+      !IPV4_ADDR_SAME (&old_bdr, &BDR (oi)))
+    ospf_dr_change (oi->ospf, oi->nbrs);
+
+  return new_state;
+}
+
+
+int
+ospf_hello_timer (struct thread *thread)
+{
+  struct ospf_interface *oi;
+
+  oi = THREAD_ARG (thread);
+  oi->t_hello = NULL;
+
+  if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Hello timer expire)",
+         IF_NAME (oi));
+
+  /* Sending hello packet. */
+  ospf_hello_send (oi);
+
+  /* Hello timer set. */
+  OSPF_HELLO_TIMER_ON (oi);
+  
+  return 0;
+}
+
+static int
+ospf_wait_timer (struct thread *thread)
+{
+  struct ospf_interface *oi;
+
+  oi = THREAD_ARG (thread);
+  oi->t_wait = NULL;
+
+  if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Wait timer expire)",
+         IF_NAME (oi));
+
+  OSPF_ISM_EVENT_SCHEDULE (oi, ISM_WaitTimer);
+
+  return 0;
+}
+
+/* Hook function called after ospf ISM event is occured. And vty's
+   network command invoke this function after making interface
+   structure. */
+static void
+ism_timer_set (struct ospf_interface *oi)
+{
+  switch (oi->state)
+    {
+    case ISM_Down:
+      /* First entry point of ospf interface state machine. In this state
+        interface parameters must be set to initial values, and timers are
+        reset also. */
+      OSPF_ISM_TIMER_OFF (oi->t_hello);
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
+      break;
+    case ISM_Loopback:
+      /* In this state, the interface may be looped back and will be
+        unavailable for regular data traffic. */
+      OSPF_ISM_TIMER_OFF (oi->t_hello);
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
+      break;
+    case ISM_Waiting:
+      /* The router is trying to determine the identity of DRouter and
+        BDRouter. The router begin to receive and send Hello Packets. */
+      /* send first hello immediately */
+      OSPF_ISM_TIMER_MSEC_ON (oi->t_hello, ospf_hello_timer, 1);
+      OSPF_ISM_TIMER_ON (oi->t_wait, ospf_wait_timer,
+                        OSPF_IF_PARAM (oi, v_wait));
+      OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
+      break;
+    case ISM_PointToPoint:
+      /* The interface connects to a physical Point-to-point network or
+        virtual link. The router attempts to form an adjacency with
+        neighboring router. Hello packets are also sent. */
+      /* send first hello immediately */
+      OSPF_ISM_TIMER_MSEC_ON (oi->t_hello, ospf_hello_timer, 1);      
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
+      break;
+    case ISM_DROther:
+      /* The network type of the interface is broadcast or NBMA network,
+        and the router itself is neither Designated Router nor
+        Backup Designated Router. */
+      OSPF_HELLO_TIMER_ON (oi);
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
+      break;
+    case ISM_Backup:
+      /* The network type of the interface is broadcast os NBMA network,
+        and the router is Backup Designated Router. */
+      OSPF_HELLO_TIMER_ON (oi);
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
+      break;
+    case ISM_DR:
+      /* The network type of the interface is broadcast or NBMA network,
+        and the router is Designated Router. */
+      OSPF_HELLO_TIMER_ON (oi);
+      OSPF_ISM_TIMER_OFF (oi->t_wait);
+      OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
+      break;
+    }
+}
+
+static int
+ism_interface_up (struct ospf_interface *oi)
+{
+  int next_state = 0;
+
+  /* if network type is point-to-point, Point-to-MultiPoint or virtual link,
+     the state transitions to Point-to-Point. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
+      oi->type == OSPF_IFTYPE_POINTOMULTIPOINT ||
+      oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    next_state = ISM_PointToPoint;
+  /* Else if the router is not eligible to DR, the state transitions to
+     DROther. */
+  else if (PRIORITY (oi) == 0) /* router is eligible? */
+    next_state = ISM_DROther;
+  else
+    /* Otherwise, the state transitions to Waiting. */
+    next_state = ISM_Waiting;
+
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    ospf_nbr_nbma_if_update (oi->ospf, oi);
+
+  /*  ospf_ism_event (t); */
+  return next_state;
+}
+
+static int
+ism_loop_ind (struct ospf_interface *oi)
+{
+  int ret = 0;
+
+  /* call ism_interface_down. */
+  /* ret = ism_interface_down (oi); */
+
+  return ret;
+}
+
+/* Interface down event handler. */
+static int
+ism_interface_down (struct ospf_interface *oi)
+{
+  ospf_if_cleanup (oi);
+  return 0;
+}
+
+
+static int
+ism_backup_seen (struct ospf_interface *oi)
+{
+  return ospf_dr_election (oi);
+}
+
+static int
+ism_wait_timer (struct ospf_interface *oi)
+{
+  return ospf_dr_election (oi);
+}
+
+static int
+ism_neighbor_change (struct ospf_interface *oi)
+{
+  return ospf_dr_election (oi);
+}
+
+static int
+ism_ignore (struct ospf_interface *oi)
+{
+  if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: ism_ignore called", IF_NAME (oi));
+
+  return 0;
+}
+
+/* Interface State Machine */
+struct {
+  int (*func) (struct ospf_interface *);
+  int next_state;
+} ISM [OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] =
+{
+  {
+    /* DependUpon: dummy state. */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_DependUpon },    /* InterfaceUp    */
+    { ism_ignore,          ISM_DependUpon },    /* WaitTimer      */
+    { ism_ignore,          ISM_DependUpon },    /* BackupSeen     */
+    { ism_ignore,          ISM_DependUpon },    /* NeighborChange */
+    { ism_ignore,          ISM_DependUpon },    /* LoopInd        */
+    { ism_ignore,          ISM_DependUpon },    /* UnloopInd      */
+    { ism_ignore,          ISM_DependUpon },    /* InterfaceDown  */
+  },
+  {
+    /* Down:*/
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_interface_up,    ISM_DependUpon },    /* InterfaceUp    */
+    { ism_ignore,          ISM_Down },          /* WaitTimer      */
+    { ism_ignore,          ISM_Down },          /* BackupSeen     */
+    { ism_ignore,          ISM_Down },          /* NeighborChange */
+    { ism_loop_ind,        ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_Down },          /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* Loopback: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_Loopback },      /* InterfaceUp    */
+    { ism_ignore,          ISM_Loopback },      /* WaitTimer      */
+    { ism_ignore,          ISM_Loopback },      /* BackupSeen     */
+    { ism_ignore,          ISM_Loopback },      /* NeighborChange */
+    { ism_ignore,          ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_Down },          /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* Waiting: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_Waiting },       /* InterfaceUp    */
+    { ism_wait_timer,     ISM_DependUpon },    /* WaitTimer      */
+    { ism_backup_seen,     ISM_DependUpon },    /* BackupSeen     */
+    { ism_ignore,          ISM_Waiting },       /* NeighborChange */
+    { ism_loop_ind,       ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_Waiting },       /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* Point-to-Point: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_PointToPoint },  /* InterfaceUp    */
+    { ism_ignore,          ISM_PointToPoint },  /* WaitTimer      */
+    { ism_ignore,          ISM_PointToPoint },  /* BackupSeen     */
+    { ism_ignore,          ISM_PointToPoint },  /* NeighborChange */
+    { ism_loop_ind,       ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_PointToPoint },  /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* DROther: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_DROther },       /* InterfaceUp    */
+    { ism_ignore,          ISM_DROther },       /* WaitTimer      */
+    { ism_ignore,          ISM_DROther },       /* BackupSeen     */
+    { ism_neighbor_change, ISM_DependUpon },    /* NeighborChange */
+    { ism_loop_ind,        ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_DROther },       /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* Backup: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_Backup },        /* InterfaceUp    */
+    { ism_ignore,          ISM_Backup },        /* WaitTimer      */
+    { ism_ignore,          ISM_Backup },        /* BackupSeen     */
+    { ism_neighbor_change, ISM_DependUpon },    /* NeighborChange */
+    { ism_loop_ind,        ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_Backup },        /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+  {
+    /* DR: */
+    { ism_ignore,          ISM_DependUpon },    /* NoEvent        */
+    { ism_ignore,          ISM_DR },            /* InterfaceUp    */
+    { ism_ignore,          ISM_DR },            /* WaitTimer      */
+    { ism_ignore,          ISM_DR },            /* BackupSeen     */
+    { ism_neighbor_change, ISM_DependUpon },    /* NeighborChange */
+    { ism_loop_ind,        ISM_Loopback },      /* LoopInd        */
+    { ism_ignore,          ISM_DR },            /* UnloopInd      */
+    { ism_interface_down,  ISM_Down },          /* InterfaceDown  */
+  },
+};  
+
+static const char *ospf_ism_event_str[] =
+{
+  "NoEvent",
+  "InterfaceUp",
+  "WaitTimer",
+  "BackupSeen",
+  "NeighborChange",
+  "LoopInd",
+  "UnLoopInd",
+  "InterfaceDown",
+};
+
+static void
+ism_change_state (struct ospf_interface *oi, int state)
+{
+  int old_state;
+  struct ospf_lsa *lsa;
+
+  /* Logging change of state. */
+  if (IS_DEBUG_OSPF (ism, ISM_STATUS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: State change %s -> %s", IF_NAME (oi),
+         LOOKUP (ospf_ism_state_msg, oi->state),
+         LOOKUP (ospf_ism_state_msg, state));
+
+  old_state = oi->state;
+  oi->state = state;
+  oi->state_change++;
+
+#ifdef HAVE_SNMP
+  /* Terminal state or regression */ 
+  if ((state == ISM_DR) || (state == ISM_Backup) || (state == ISM_DROther) ||
+      (state == ISM_PointToPoint) || (state < old_state))
+    {
+      /* ospfVirtIfStateChange */
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+        ospfTrapVirtIfStateChange (oi);
+      /* ospfIfStateChange */
+      else
+        ospfTrapIfStateChange (oi);
+    }
+#endif
+
+  /* Set multicast memberships appropriately for new state. */
+  ospf_if_set_multicast(oi);
+
+  if (old_state == ISM_Down || state == ISM_Down)
+    ospf_check_abr_status (oi->ospf);
+
+  /* Originate router-LSA. */
+  if (state == ISM_Down)
+    {
+      if (oi->area->act_ints > 0)
+        oi->area->act_ints--;
+    }
+  else if (old_state == ISM_Down)
+    oi->area->act_ints++;
+
+  /* schedule router-LSA originate. */
+  ospf_router_lsa_update_area (oi->area);
+
+  /* Originate network-LSA. */
+  if (old_state != ISM_DR && state == ISM_DR)
+    ospf_network_lsa_update (oi);
+  else if (old_state == ISM_DR && state != ISM_DR)
+    {
+      /* Free self originated network LSA. */
+      lsa = oi->network_lsa_self;
+      if (lsa)
+        ospf_lsa_flush_area (lsa, oi->area);
+
+      ospf_lsa_unlock (&oi->network_lsa_self);
+      oi->network_lsa_self = NULL;
+    }
+
+  ospf_opaque_ism_change (oi, old_state);
+
+  /* Check area border status.  */
+  ospf_check_abr_status (oi->ospf);
+}
+
+/* Execute ISM event process. */
+int
+ospf_ism_event (struct thread *thread)
+{
+  int event;
+  int next_state;
+  struct ospf_interface *oi;
+
+  oi = THREAD_ARG (thread);
+  event = THREAD_VAL (thread);
+
+  /* Call function. */
+  next_state = (*(ISM [oi->state][event].func))(oi);
+
+  if (! next_state)
+    next_state = ISM [oi->state][event].next_state;
+
+  if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: %s (%s)", IF_NAME (oi),
+         LOOKUP (ospf_ism_state_msg, oi->state),
+         ospf_ism_event_str[event]);
+
+  /* If state is changed. */
+  if (next_state != oi->state)
+    ism_change_state (oi, next_state);
+
+  /* Make sure timer is set. */
+  ism_timer_set (oi);
+
+  return 0;
+}
+
diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h
new file mode 100644 (file)
index 0000000..f0357a4
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * OSPF version 2  Interface State Machine.
+ *   From RFC2328 [OSPF Version 2]
+ * Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ISM_H
+#define _ZEBRA_OSPF_ISM_H
+
+/* OSPF Interface State Machine Status. */
+#define ISM_DependUpon                    0
+#define ISM_Down                          1
+#define ISM_Loopback                      2
+#define ISM_Waiting                       3
+#define ISM_PointToPoint                  4
+#define ISM_DROther                       5
+#define ISM_Backup                        6
+#define ISM_DR                            7
+#define OSPF_ISM_STATE_MAX               8
+
+/* Because DR/DROther values are exhanged wrt RFC */
+#define ISM_SNMP(x) (((x) == ISM_DROther) ? ISM_DR : \
+                     ((x) == ISM_DR) ? ISM_DROther : (x))
+
+/* OSPF Interface State Machine Event. */
+#define ISM_NoEvent                       0
+#define ISM_InterfaceUp                   1
+#define ISM_WaitTimer                     2
+#define ISM_BackupSeen                    3
+#define ISM_NeighborChange                4
+#define ISM_LoopInd                       5
+#define ISM_UnloopInd                     6
+#define ISM_InterfaceDown                 7
+#define OSPF_ISM_EVENT_MAX                8
+
+#define OSPF_ISM_WRITE_ON(O)                                                  \
+      do                                                                      \
+        {                                                                     \
+          if (oi->on_write_q == 0)                                            \
+           {                                                                 \
+              listnode_add ((O)->oi_write_q, oi);                             \
+             oi->on_write_q = 1;                                             \
+           }                                                                 \
+         if ((O)->t_write == NULL)                                           \
+           (O)->t_write =                                                    \
+             thread_add_write (master, ospf_write, (O), (O)->fd);            \
+        } while (0)
+     
+/* Macro for OSPF ISM timer turn on. */
+#define OSPF_ISM_TIMER_ON(T,F,V) \
+  do { \
+    if (!(T)) \
+      (T) = thread_add_timer (master, (F), oi, (V)); \
+  } while (0)
+#define OSPF_ISM_TIMER_MSEC_ON(T,F,V) \
+  do { \
+    if (!(T)) \
+      (T) = thread_add_timer_msec (master, (F), oi, (V)); \
+  } while (0)
+
+/* convenience macro to set hello timer correctly, according to
+ * whether fast-hello is set or not
+ */
+#define OSPF_HELLO_TIMER_ON(O) \
+  do { \
+    if (OSPF_IF_PARAM ((O), fast_hello)) \
+        OSPF_ISM_TIMER_MSEC_ON ((O)->t_hello, ospf_hello_timer, \
+                                1000 / OSPF_IF_PARAM ((O), fast_hello)); \
+    else \
+        OSPF_ISM_TIMER_ON ((O)->t_hello, ospf_hello_timer, \
+                                OSPF_IF_PARAM ((O), v_hello)); \
+  } while (0)
+
+/* Macro for OSPF ISM timer turn off. */
+#define OSPF_ISM_TIMER_OFF(X) \
+  do { \
+    if (X) \
+      { \
+       thread_cancel (X); \
+       (X) = NULL; \
+      } \
+  } while (0)
+
+/* Macro for OSPF schedule event. */
+#define OSPF_ISM_EVENT_SCHEDULE(I,E) \
+      thread_add_event (master, ospf_ism_event, (I), (E))
+
+/* Macro for OSPF execute event. */
+#define OSPF_ISM_EVENT_EXECUTE(I,E) \
+      thread_execute (master, ospf_ism_event, (I), (E))
+
+/* Prototypes. */
+extern int ospf_ism_event (struct thread *);
+extern void ism_change_status (struct ospf_interface *, int);
+extern int ospf_hello_timer (struct thread *thread);
+
+#endif /* _ZEBRA_OSPF_ISM_H */
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
new file mode 100644 (file)
index 0000000..f49e263
--- /dev/null
@@ -0,0 +1,3780 @@
+/*
+ * OSPF Link State Advertisement
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"         /* for inet_aton() */
+#include "checksum.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_abr.h"
+
+
+u_int32_t
+get_metric (u_char *metric)
+{
+  u_int32_t m;
+  m = metric[0];
+  m = (m << 8) + metric[1];
+  m = (m << 8) + metric[2];
+  return m;
+}
+
+
+struct timeval
+tv_adjust (struct timeval a)
+{
+  while (a.tv_usec >= 1000000)
+    {
+      a.tv_usec -= 1000000;
+      a.tv_sec++;
+    }
+
+  while (a.tv_usec < 0)
+    {
+      a.tv_usec += 1000000;
+      a.tv_sec--;
+    }
+
+  return a;
+}
+
+int
+tv_ceil (struct timeval a)
+{
+  a = tv_adjust (a);
+
+  return (a.tv_usec ? a.tv_sec + 1 : a.tv_sec);
+}
+
+int
+tv_floor (struct timeval a)
+{
+  a = tv_adjust (a);
+
+  return a.tv_sec;
+}
+
+struct timeval
+int2tv (int a)
+{
+  struct timeval ret;
+
+  ret.tv_sec = a;
+  ret.tv_usec = 0;
+
+  return ret;
+}
+
+struct timeval
+msec2tv (int a)
+{
+  struct timeval ret;
+
+  ret.tv_sec = 0;
+  ret.tv_usec = a * 1000;
+
+  return tv_adjust (ret);
+}
+
+struct timeval
+tv_add (struct timeval a, struct timeval b)
+{
+  struct timeval ret;
+
+  ret.tv_sec = a.tv_sec + b.tv_sec;
+  ret.tv_usec = a.tv_usec + b.tv_usec;
+
+  return tv_adjust (ret);
+}
+
+struct timeval
+tv_sub (struct timeval a, struct timeval b)
+{
+  struct timeval ret;
+
+  ret.tv_sec = a.tv_sec - b.tv_sec;
+  ret.tv_usec = a.tv_usec - b.tv_usec;
+
+  return tv_adjust (ret);
+}
+
+int
+tv_cmp (struct timeval a, struct timeval b)
+{
+  return (a.tv_sec == b.tv_sec ?
+         a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
+}
+
+int
+ospf_lsa_refresh_delay (struct ospf_lsa *lsa)
+{
+  struct timeval delta, now;
+  int delay = 0;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+  delta = tv_sub (now, lsa->tv_orig);
+
+  if (tv_cmp (delta, msec2tv (OSPF_MIN_LS_INTERVAL)) < 0)
+    {
+      delay = tv_ceil (tv_sub (msec2tv (OSPF_MIN_LS_INTERVAL), delta));
+
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d:%s]: Refresh timer delay %d seconds",
+                  lsa->data->type, inet_ntoa (lsa->data->id), delay);
+
+      assert (delay > 0);
+    }
+
+  return delay;
+}
+
+
+int
+get_age (struct ospf_lsa *lsa)
+{
+  int age;
+
+  age = ntohs (lsa->data->ls_age) 
+        + tv_floor (tv_sub (recent_relative_time (), lsa->tv_recv));
+
+  return age;
+}
+
+
+/* Fletcher Checksum -- Refer to RFC1008. */
+
+/* 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 *buffer = (u_char *) &lsa->options;
+  int options_offset = buffer - (u_char *) &lsa->ls_age; /* should be 2 */
+
+  /* Skip the AGE field */
+  u_int16_t len = ntohs(lsa->length) - options_offset; 
+
+  /* Checksum offset starts from "options" field, not the beginning of the
+     lsa_header struct. The offset is 14, rather than 16. */
+  int checksum_offset = (u_char *) &lsa->checksum - buffer;
+
+  return fletcher_checksum(buffer, len, checksum_offset);
+}
+
+int
+ospf_lsa_checksum_valid (struct lsa_header *lsa)
+{
+  u_char *buffer = (u_char *) &lsa->options;
+  int options_offset = buffer - (u_char *) &lsa->ls_age; /* should be 2 */
+
+  /* Skip the AGE field */
+  u_int16_t len = ntohs(lsa->length) - options_offset;
+
+  return(fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) == 0);
+}
+
+
+
+/* Create OSPF LSA. */
+struct ospf_lsa *
+ospf_lsa_new ()
+{
+  struct ospf_lsa *new;
+
+  new = XCALLOC (MTYPE_OSPF_LSA, sizeof (struct ospf_lsa));
+
+  new->flags = 0;
+  new->lock = 1;
+  new->retransmit_counter = 0;
+  new->tv_recv = recent_relative_time ();
+  new->tv_orig = new->tv_recv;
+  new->refresh_list = -1;
+  
+  return new;
+}
+
+/* Duplicate OSPF LSA. */
+struct ospf_lsa *
+ospf_lsa_dup (struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *new;
+
+  if (lsa == NULL)
+    return NULL;
+
+  new = XCALLOC (MTYPE_OSPF_LSA, sizeof (struct ospf_lsa));
+
+  memcpy (new, lsa, sizeof (struct ospf_lsa));
+  UNSET_FLAG (new->flags, OSPF_LSA_DISCARD);
+  new->lock = 1;
+  new->retransmit_counter = 0;
+  new->data = ospf_lsa_data_dup (lsa->data);
+
+  /* kevinm: Clear the refresh_list, otherwise there are going
+     to be problems when we try to remove the LSA from the
+     queue (which it's not a member of.)
+     XXX: Should we add the LSA to the refresh_list queue? */
+  new->refresh_list = -1;
+
+  if (IS_DEBUG_OSPF (lsa, LSA))
+    zlog_debug ("LSA: duplicated %p (new: %p)", (void *)lsa, (void *)new);
+
+  return new;
+}
+
+/* Free OSPF LSA. */
+void
+ospf_lsa_free (struct ospf_lsa *lsa)
+{
+  assert (lsa->lock == 0);
+  
+  if (IS_DEBUG_OSPF (lsa, LSA))
+    zlog_debug ("LSA: freed %p", (void *)lsa);
+
+  /* Delete LSA data. */
+  if (lsa->data != NULL)
+    ospf_lsa_data_free (lsa->data);
+
+  assert (lsa->refresh_list < 0);
+
+  memset (lsa, 0, sizeof (struct ospf_lsa)); 
+  XFREE (MTYPE_OSPF_LSA, lsa);
+}
+
+/* Lock LSA. */
+struct ospf_lsa *
+ospf_lsa_lock (struct ospf_lsa *lsa)
+{
+  lsa->lock++;
+  return lsa;
+}
+
+/* Unlock LSA. */
+void
+ospf_lsa_unlock (struct ospf_lsa **lsa)
+{
+  /* This is sanity check. */
+  if (!lsa || !*lsa)
+    return;
+  
+  (*lsa)->lock--;
+
+  assert ((*lsa)->lock >= 0);
+
+  if ((*lsa)->lock == 0)
+    {
+      assert (CHECK_FLAG ((*lsa)->flags, OSPF_LSA_DISCARD));
+      ospf_lsa_free (*lsa);
+      *lsa = NULL;
+    }
+}
+
+/* Check discard flag. */
+void
+ospf_lsa_discard (struct ospf_lsa *lsa)
+{
+  if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD))
+    {
+      SET_FLAG (lsa->flags, OSPF_LSA_DISCARD);
+      ospf_lsa_unlock (&lsa);
+    }
+}
+
+/* Create LSA data. */
+struct lsa_header *
+ospf_lsa_data_new (size_t size)
+{
+  return XCALLOC (MTYPE_OSPF_LSA_DATA, size);
+}
+
+/* Duplicate LSA data. */
+struct lsa_header *
+ospf_lsa_data_dup (struct lsa_header *lsah)
+{
+  struct lsa_header *new;
+
+  new = ospf_lsa_data_new (ntohs (lsah->length));
+  memcpy (new, lsah, ntohs (lsah->length));
+
+  return new;
+}
+
+/* Free LSA data. */
+void
+ospf_lsa_data_free (struct lsa_header *lsah)
+{
+  if (IS_DEBUG_OSPF (lsa, LSA))
+    zlog_debug ("LSA[Type%d:%s]: data freed %p",
+              lsah->type, inet_ntoa (lsah->id), (void *)lsah);
+
+  XFREE (MTYPE_OSPF_LSA_DATA, lsah);
+}
+
+
+/* LSA general functions. */
+
+const char *
+dump_lsa_key (struct ospf_lsa *lsa)
+{
+  static char buf[] = {
+    "Type255,id(255.255.255.255),ar(255.255.255.255)"
+  };
+  struct lsa_header *lsah;
+
+  if (lsa != NULL && (lsah = lsa->data) != NULL)
+    {
+      char id[INET_ADDRSTRLEN], ar[INET_ADDRSTRLEN];
+      strcpy (id, inet_ntoa (lsah->id));
+      strcpy (ar, inet_ntoa (lsah->adv_router));
+
+      sprintf (buf, "Type%d,id(%s),ar(%s)", lsah->type, id, ar);
+    }
+  else
+    strcpy (buf, "NULL");
+
+  return buf;
+}
+
+u_int32_t
+lsa_seqnum_increment (struct ospf_lsa *lsa)
+{
+  u_int32_t seqnum;
+
+  seqnum = ntohl (lsa->data->ls_seqnum) + 1;
+
+  return htonl (seqnum);
+}
+
+void
+lsa_header_set (struct stream *s, u_char options,
+               u_char type, struct in_addr id, struct in_addr router_id)
+{
+  struct lsa_header *lsah;
+
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  lsah->ls_age = htons (OSPF_LSA_INITIAL_AGE);
+  lsah->options = options;
+  lsah->type = type;
+  lsah->id = id;
+  lsah->adv_router = router_id;
+  lsah->ls_seqnum = htonl (OSPF_INITIAL_SEQUENCE_NUMBER);
+
+  stream_forward_endp (s, OSPF_LSA_HEADER_SIZE);
+}
+
+
+/* router-LSA related functions. */
+/* Get router-LSA flags. */
+static u_char
+router_lsa_flags (struct ospf_area *area)
+{
+  u_char flags;
+
+  flags = area->ospf->flags;
+
+  /* Set virtual link flag. */
+  if (ospf_full_virtual_nbrs (area))
+    SET_FLAG (flags, ROUTER_LSA_VIRTUAL);
+  else
+    /* Just sanity check */
+    UNSET_FLAG (flags, ROUTER_LSA_VIRTUAL);
+
+  /* Set Shortcut ABR behabiour flag. */
+  UNSET_FLAG (flags, ROUTER_LSA_SHORTCUT);
+  if (area->ospf->abr_type == OSPF_ABR_SHORTCUT)
+    if (!OSPF_IS_AREA_BACKBONE (area))
+      if ((area->shortcut_configured == OSPF_SHORTCUT_DEFAULT &&
+          area->ospf->backbone == NULL) ||
+         area->shortcut_configured == OSPF_SHORTCUT_ENABLE)
+       SET_FLAG (flags, ROUTER_LSA_SHORTCUT);
+
+  /* ASBR can't exit in stub area. */
+  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))
+    SET_FLAG (flags, ROUTER_LSA_EXTERNAL);
+
+  /* Set ABR dependent flags */
+  if (IS_OSPF_ABR (area->ospf))
+    {
+      SET_FLAG (flags,  ROUTER_LSA_BORDER);
+      /* If Area is NSSA and we are both ABR and unconditional translator, 
+       * set Nt bit to inform other routers.
+       */
+      if ( (area->external_routing == OSPF_AREA_NSSA)
+           && (area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS))
+        SET_FLAG (flags, ROUTER_LSA_NT);
+    }
+  return flags;
+}
+
+/* Lookup neighbor other than myself.
+   And check neighbor count,
+   Point-to-Point link must have only 1 neighbor. */
+struct ospf_neighbor *
+ospf_nbr_lookup_ptop (struct ospf_interface *oi)
+{
+  struct ospf_neighbor *nbr = NULL;
+  struct route_node *rn;
+
+  /* Search neighbor, there must be one of two nbrs. */
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+       if (nbr->state == NSM_Full)
+         {
+           route_unlock_node (rn);
+           break;
+         }
+
+  /* PtoP link must have only 1 neighbor. */
+  if (ospf_nbr_count (oi, 0) > 1)
+    zlog_warn ("Point-to-Point link has more than 1 neighobrs.");
+
+  return nbr;
+}
+
+/* Determine cost of link, taking RFC3137 stub-router support into
+ * consideration
+ */
+static u_int16_t
+ospf_link_cost (struct ospf_interface *oi)
+{
+  /* RFC3137 stub router support */
+  if (!CHECK_FLAG (oi->area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+    return oi->output_cost;
+  else
+    return OSPF_OUTPUT_COST_INFINITE;
+}
+
+/* Set a link information. */
+static char
+link_info_set (struct stream *s, struct in_addr id,
+              struct in_addr data, u_char type, u_char tos, u_int16_t cost)
+{
+  /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits
+   * vast majority of cases. Some rare routers with lots of links need more.
+   * we try accomodate those here.
+   */
+  if (STREAM_WRITEABLE(s) < OSPF_ROUTER_LSA_LINK_SIZE)
+    {
+      size_t ret = OSPF_MAX_LSA_SIZE;
+      
+      /* Can we enlarge the stream still? */
+      if (STREAM_SIZE(s) == OSPF_MAX_LSA_SIZE)
+        {
+          /* we futz the size here for simplicity, really we need to account
+           * for just:
+           * IP Header - (sizeof (struct ip))
+           * OSPF Header - OSPF_HEADER_SIZE
+           * LSA Header - OSPF_LSA_HEADER_SIZE
+           * MD5 auth data, if MD5 is configured - OSPF_AUTH_MD5_SIZE.
+           *
+           * Simpler just to subtract OSPF_MAX_LSA_SIZE though.
+           */
+          ret = stream_resize (s, OSPF_MAX_PACKET_SIZE - OSPF_MAX_LSA_SIZE);
+        }
+      
+      if (ret == OSPF_MAX_LSA_SIZE)
+        {
+          zlog_warn ("%s: Out of space in LSA stream, left %zd, size %zd",
+                     __func__, STREAM_REMAIN (s), STREAM_SIZE (s));
+          return 0;
+        }
+    }
+  
+  /* TOS based routing is not supported. */
+  stream_put_ipv4 (s, id.s_addr);              /* Link ID. */
+  stream_put_ipv4 (s, data.s_addr);            /* Link Data. */
+  stream_putc (s, type);                       /* Link Type. */
+  stream_putc (s, tos);                                /* TOS = 0. */
+  stream_putw (s, cost);                       /* Link Cost. */
+  
+  return 1;
+}
+
+/* Describe Point-to-Point link (Section 12.4.1.1). */
+static int
+lsa_link_ptop_set (struct stream *s, struct ospf_interface *oi)
+{
+  int links = 0;
+  struct ospf_neighbor *nbr;
+  struct in_addr id, mask;
+  u_int16_t cost = ospf_link_cost (oi);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type1]: Set link Point-to-Point");
+
+  if ((nbr = ospf_nbr_lookup_ptop (oi)))
+    if (nbr->state == NSM_Full)
+      {
+       /* For unnumbered point-to-point networks, the Link Data field
+          should specify the interface's MIB-II ifIndex value. */
+       links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+                               LSA_LINK_TYPE_POINTOPOINT, 0, cost);
+      }
+
+  /* Regardless of the state of the neighboring router, we must
+     add a Type 3 link (stub network).
+     N.B. Options 1 & 2 share basically the same logic. */
+  masklen2ip (oi->address->prefixlen, &mask);
+  id.s_addr = CONNECTED_PREFIX(oi->connected)->u.prefix4.s_addr & mask.s_addr;
+  links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                         oi->output_cost);
+  return links;
+}
+
+/* Describe Broadcast Link. */
+static int
+lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi)
+{
+  struct ospf_neighbor *dr;
+  struct in_addr id, mask;
+  u_int16_t cost = ospf_link_cost (oi);
+  
+  /* Describe Type 3 Link. */
+  if (oi->state == ISM_Waiting)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type1]: Interface %s is in state Waiting. "
+                    "Adding stub interface", oi->ifp->name);
+      masklen2ip (oi->address->prefixlen, &mask);
+      id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
+      return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                            oi->output_cost);
+    }
+
+  dr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi));
+  /* Describe Type 2 link. */
+  if (dr && (dr->state == NSM_Full ||
+            IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))) &&
+      ospf_nbr_count (oi, NSM_Full) > 0)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type1]: Interface %s has a DR. "
+                    "Adding transit interface", oi->ifp->name);
+      return link_info_set (s, DR (oi), oi->address->u.prefix4,
+                            LSA_LINK_TYPE_TRANSIT, 0, cost);
+    }
+  /* Describe type 3 link. */
+  else
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type1]: Interface %s has no DR. "
+                    "Adding stub interface", oi->ifp->name);
+      masklen2ip (oi->address->prefixlen, &mask);
+      id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
+      return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0,
+                            oi->output_cost);
+    }
+}
+
+static int
+lsa_link_loopback_set (struct stream *s, struct ospf_interface *oi)
+{
+  struct in_addr id, mask;
+  
+  /* Describe Type 3 Link. */
+  if (oi->state != ISM_Loopback)
+    return 0;
+
+  mask.s_addr = 0xffffffff;
+  id.s_addr = oi->address->u.prefix4.s_addr;
+  return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0);
+}
+
+/* Describe Virtual Link. */
+static int
+lsa_link_virtuallink_set (struct stream *s, struct ospf_interface *oi)
+{
+  struct ospf_neighbor *nbr;
+  u_int16_t cost = ospf_link_cost (oi);
+
+  if (oi->state == ISM_PointToPoint)
+    if ((nbr = ospf_nbr_lookup_ptop (oi)))
+      if (nbr->state == NSM_Full)
+       {
+         return link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+                               LSA_LINK_TYPE_VIRTUALLINK, 0, cost);
+       }
+
+  return 0;
+}
+
+#define lsa_link_nbma_set(S,O)  lsa_link_broadcast_set (S, O)
+
+/* this function add for support point-to-multipoint ,see rfc2328 
+12.4.1.4.*/
+/* from "edward rrr" <edward_rrr@hotmail.com>
+   http://marc.theaimsgroup.com/?l=zebra&m=100739222210507&w=2 */
+static int
+lsa_link_ptomp_set (struct stream *s, struct ospf_interface *oi)
+{
+  int links = 0;
+  struct route_node *rn;
+  struct ospf_neighbor *nbr = NULL;
+  struct in_addr id, mask;
+  u_int16_t cost = ospf_link_cost (oi);
+
+  mask.s_addr = 0xffffffff;
+  id.s_addr = oi->address->u.prefix4.s_addr;
+  links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("PointToMultipoint: running ptomultip_set");
+
+  /* Search neighbor, */
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      /* Ignore myself. */
+      if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+       if (nbr->state == NSM_Full)
+
+         {
+           links += link_info_set (s, nbr->router_id, oi->address->u.prefix4,
+                                   LSA_LINK_TYPE_POINTOPOINT, 0, cost);
+            if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+             zlog_debug ("PointToMultipoint: set link to %s",
+                        inet_ntoa(oi->address->u.prefix4));
+         }
+  
+  return links;
+}
+
+/* Set router-LSA link information. */
+static int
+router_lsa_link_set (struct stream *s, struct ospf_area *area)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+  int links = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+    {
+      struct interface *ifp = oi->ifp;
+
+      /* Check interface is up, OSPF is enable. */
+      if (if_is_operative (ifp))
+       {
+         if (oi->state != ISM_Down)
+           {
+             oi->lsa_pos_beg = links;
+             /* Describe each link. */
+             switch (oi->type)
+               {
+               case OSPF_IFTYPE_POINTOPOINT:
+                 links += lsa_link_ptop_set (s, oi);
+                 break;
+               case OSPF_IFTYPE_BROADCAST:
+                 links += lsa_link_broadcast_set (s, oi);
+                 break;
+               case OSPF_IFTYPE_NBMA:
+                 links += lsa_link_nbma_set (s, oi);
+                 break;
+               case OSPF_IFTYPE_POINTOMULTIPOINT:
+                 links += lsa_link_ptomp_set (s, oi);
+                 break;
+               case OSPF_IFTYPE_VIRTUALLINK:
+                 links += lsa_link_virtuallink_set (s, oi);
+                 break;
+               case OSPF_IFTYPE_LOOPBACK:
+                 links += lsa_link_loopback_set (s, oi); 
+               }
+             oi->lsa_pos_end = links;
+           }
+       }
+    }
+
+  return links;
+}
+
+/* Set router-LSA body. */
+static void
+ospf_router_lsa_body_set (struct stream *s, struct ospf_area *area)
+{
+  unsigned long putp;
+  u_int16_t cnt;
+
+  /* Set flags. */
+  stream_putc (s, router_lsa_flags (area));
+
+  /* Set Zero fields. */
+  stream_putc (s, 0);
+
+  /* Keep pointer to # links. */
+  putp = stream_get_endp(s);
+
+  /* Forward word */
+  stream_putw(s, 0);
+
+  /* Set all link information. */
+  cnt = router_lsa_link_set (s, area);
+
+  /* Set # of links here. */
+  stream_putw_at (s, putp, cnt);
+}
+
+static int
+ospf_stub_router_timer (struct thread *t)
+{
+  struct ospf_area *area = THREAD_ARG (t);
+  
+  area->t_stub_router = NULL;
+  
+  SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED);
+  
+  /* clear stub route state and generate router-lsa refresh, don't
+   * clobber an administratively set stub-router state though.
+   */
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED))
+    return 0;
+  
+  UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED);
+  
+  ospf_router_lsa_update_area (area);
+  
+  return 0;
+}
+
+static void
+ospf_stub_router_check (struct ospf_area *area)
+{
+  /* area must either be administratively configured to be stub
+   * or startup-time stub-router must be configured and we must in a pre-stub
+   * state.
+   */
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED))
+    {
+      SET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED);
+      return;
+    }
+  
+  /* not admin-stubbed, check whether startup stubbing is configured and
+   * whether it's not been done yet
+   */
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED))
+    return;
+  
+  if (area->ospf->stub_router_startup_time == OSPF_STUB_ROUTER_UNCONFIGURED)
+    {
+      /* stub-router is hence done forever for this area, even if someone
+       * tries configure it (take effect next restart).
+       */
+      SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED);
+      return;
+    }
+  
+  /* startup stub-router configured and not yet done */
+  SET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED);
+  
+  OSPF_AREA_TIMER_ON (area->t_stub_router, ospf_stub_router_timer,
+                      area->ospf->stub_router_startup_time);
+}
+/* Create new router-LSA. */
+static struct ospf_lsa *
+ospf_router_lsa_new (struct ospf_area *area)
+{
+  struct ospf *ospf = area->ospf;
+  struct stream *s;
+  struct lsa_header *lsah;
+  struct ospf_lsa *new;
+  int length;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type1]: Create router-LSA instance");
+
+  /* check whether stub-router is desired, and if this is the first 
+   * router LSA.
+   */
+  ospf_stub_router_check (area);
+  
+  /* Create a stream for LSA. */
+  s = stream_new (OSPF_MAX_LSA_SIZE);
+  /* Set LSA common header fields. */
+  lsa_header_set (s, LSA_OPTIONS_GET (area) | LSA_OPTIONS_NSSA_GET (area),
+                 OSPF_ROUTER_LSA, ospf->router_id, ospf->router_id);
+
+  /* Set router-LSA body fields. */
+  ospf_router_lsa_body_set (s, area);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+  lsah->length = htons (length);
+
+  /* Now, create OSPF LSA instance. */
+  if ( (new = ospf_lsa_new ()) == NULL)
+    {
+      zlog_err ("%s: Unable to create new lsa", __func__);
+      return NULL;
+    }
+  
+  new->area = area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
+
+  /* Copy LSA data to store, discard stream. */
+  new->data = ospf_lsa_data_new (length);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+  return new;
+}
+
+/* Originate Router-LSA. */
+static struct ospf_lsa *
+ospf_router_lsa_originate (struct ospf_area *area)
+{
+  struct ospf_lsa *new;
+  
+  /* Create new router-LSA instance. */
+  if ( (new = ospf_router_lsa_new (area)) == NULL)
+    {
+      zlog_err ("%s: ospf_router_lsa_new returned NULL", __func__);
+      return NULL;
+    }
+
+  /* Sanity check. */
+  if (new->data->adv_router.s_addr == 0)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("LSA[Type1]: AdvRouter is 0, discard");
+      ospf_lsa_discard (new);
+      return NULL;
+    }
+
+  /* Install LSA to LSDB. */
+  new = ospf_lsa_install (area->ospf, NULL, new);
+
+  /* Update LSA origination count. */
+  area->ospf->lsa_originate_count++;
+
+  /* Flooding new LSA through area. */
+  ospf_flood_through_area (area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate router-LSA %p",
+                new->data->type, inet_ntoa (new->data->id), (void *)new);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+/* Refresh router-LSA. */
+static struct ospf_lsa *
+ospf_router_lsa_refresh (struct ospf_lsa *lsa)
+{
+  struct ospf_area *area = lsa->area;
+  struct ospf_lsa *new;
+
+  /* Sanity check. */
+  assert (lsa->data);
+
+  /* Delete LSA from neighbor retransmit-list. */
+  ospf_ls_retransmit_delete_nbr_area (area, lsa);
+
+  /* Unregister LSA from refresh-list */
+  ospf_refresher_unregister_lsa (area->ospf, lsa);
+  
+  /* Create new router-LSA instance. */
+  if ( (new = ospf_router_lsa_new (area)) == NULL)
+    {
+      zlog_err ("%s: ospf_router_lsa_new returned NULL", __func__);
+      return NULL;
+    }
+  
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  ospf_lsa_install (area->ospf, NULL, new);
+
+  /* Flood LSA through area. */
+  ospf_flood_through_area (area, NULL, new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: router-LSA refresh",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return NULL;
+}
+
+int
+ospf_router_lsa_update_area (struct ospf_area *area)
+{
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("[router-LSA]: (router-LSA area update)");
+
+  /* Now refresh router-LSA. */
+  if (area->router_lsa_self)
+    ospf_lsa_refresh (area->ospf, area->router_lsa_self);
+  /* Newly originate router-LSA. */
+  else
+    ospf_router_lsa_originate (area);
+
+  return 0;
+}
+
+int
+ospf_router_lsa_update (struct ospf *ospf)
+{
+  struct listnode *node, *nnode;
+  struct ospf_area *area;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("Timer[router-LSA Update]: (timer expire)");
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      struct ospf_lsa *lsa = area->router_lsa_self;
+      struct router_lsa *rl;
+      const char *area_str;
+
+      /* Keep Area ID string. */
+      area_str = AREA_NAME (area);
+
+      /* If LSA not exist in this Area, originate new. */
+      if (lsa == NULL)
+        {
+         if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+           zlog_debug("LSA[Type1]: Create router-LSA for Area %s", area_str);
+
+         ospf_router_lsa_originate (area);
+        }
+      /* If router-ID is changed, Link ID must change.
+        First flush old LSA, then originate new. */
+      else if (!IPV4_ADDR_SAME (&lsa->data->id, &ospf->router_id))
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+           zlog_debug("LSA[Type%d:%s]: Refresh router-LSA for Area %s",
+                     lsa->data->type, inet_ntoa (lsa->data->id), area_str);
+          ospf_refresher_unregister_lsa (ospf, lsa);
+         ospf_lsa_flush_area (lsa, area);
+         ospf_lsa_unlock (&area->router_lsa_self);
+         area->router_lsa_self = NULL;
+
+         /* Refresh router-LSA, (not install) and flood through area. */
+         ospf_router_lsa_update_area (area);
+       }
+      else
+       {
+         rl = (struct router_lsa *) lsa->data;
+         /* Refresh router-LSA, (not install) and flood through area. */
+         if (rl->flags != ospf->flags)
+           ospf_router_lsa_update_area (area);
+       }
+    }
+
+  return 0;
+}
+
+
+/* network-LSA related functions. */
+/* Originate Network-LSA. */
+static void
+ospf_network_lsa_body_set (struct stream *s, struct ospf_interface *oi)
+{
+  struct in_addr mask;
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  masklen2ip (oi->address->prefixlen, &mask);
+  stream_put_ipv4 (s, mask.s_addr);
+
+  /* The network-LSA lists those routers that are fully adjacent to
+    the Designated Router; each fully adjacent router is identified by
+    its OSPF Router ID.  The Designated Router includes itself in this
+    list. RFC2328, Section 12.4.2 */
+
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      if (nbr->state == NSM_Full || nbr == oi->nbr_self)
+       stream_put_ipv4 (s, nbr->router_id.s_addr);
+}
+
+static struct ospf_lsa *
+ospf_network_lsa_new (struct ospf_interface *oi)
+{
+  struct stream *s;
+  struct ospf_lsa *new;
+  struct lsa_header *lsah;
+  struct ospf_if_params *oip;
+  int length;
+
+  /* If there are no neighbours on this network (the net is stub),
+     the router does not originate network-LSA (see RFC 12.4.2) */
+  if (oi->full_nbrs == 0)
+    return NULL;
+  
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type2]: Create network-LSA instance");
+
+  /* Create new stream for LSA. */
+  s = stream_new (OSPF_MAX_LSA_SIZE);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  lsa_header_set (s, (OPTIONS (oi) | LSA_OPTIONS_GET (oi->area)),
+                 OSPF_NETWORK_LSA, DR (oi), oi->ospf->router_id);
+
+  /* Set network-LSA body fields. */
+  ospf_network_lsa_body_set (s, oi);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Create OSPF LSA instance. */
+  if ( (new = ospf_lsa_new ()) == NULL)
+    {
+      zlog_err ("%s: ospf_lsa_new returned NULL", __func__);
+      return NULL;
+    }
+  
+  new->area = oi->area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
+
+  /* Copy LSA to store. */
+  new->data = ospf_lsa_data_new (length);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+  
+  /* Remember prior network LSA sequence numbers, even if we stop
+   * originating one for this oi, to try avoid re-originating LSAs with a
+   * prior sequence number, and thus speed up adjency forming & convergence.
+   */
+  if ((oip = ospf_lookup_if_params (oi->ifp, oi->address->u.prefix4)))
+    {
+      new->data->ls_seqnum = oip->network_lsa_seqnum;
+      new->data->ls_seqnum = lsa_seqnum_increment (new);
+    }
+  else
+    {
+      oip = ospf_get_if_params (oi->ifp, oi->address->u.prefix4);
+      ospf_if_update_params (oi->ifp, oi->address->u.prefix4);
+    }
+  oip->network_lsa_seqnum = new->data->ls_seqnum;
+  
+  return new;
+}
+
+/* Originate network-LSA. */
+void
+ospf_network_lsa_update (struct ospf_interface *oi)
+{
+  struct ospf_lsa *new;
+  
+  if (oi->network_lsa_self != NULL)
+    {
+      ospf_lsa_refresh (oi->ospf, oi->network_lsa_self);
+      return;
+    }
+  
+  /* Create new network-LSA instance. */
+  new = ospf_network_lsa_new (oi);
+  if (new == NULL)
+    return;
+
+  /* Install LSA to LSDB. */
+  new = ospf_lsa_install (oi->ospf, oi, new);
+
+  /* Update LSA origination count. */
+  oi->ospf->lsa_originate_count++;
+
+  /* Flooding new LSA through area. */
+  ospf_flood_through_area (oi->area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate network-LSA %p",
+                new->data->type, inet_ntoa (new->data->id), (void *)new);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return;
+}
+
+static struct ospf_lsa *
+ospf_network_lsa_refresh (struct ospf_lsa *lsa)
+{
+  struct ospf_area *area = lsa->area;
+  struct ospf_lsa *new, *new2;
+  struct ospf_if_params *oip;
+  struct ospf_interface *oi;
+  
+  assert (lsa->data);
+  
+  /* Retrieve the oi for the network LSA */
+  oi = ospf_if_lookup_by_local_addr (area->ospf, NULL, lsa->data->id);
+  if (oi == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        {
+          zlog_debug ("LSA[Type%d:%s]: network-LSA refresh: "
+                      "no oi found, ick, ignoring.",
+                     lsa->data->type, inet_ntoa (lsa->data->id));
+          ospf_lsa_header_dump (lsa->data);
+        }
+      return NULL;
+    }
+  /* Delete LSA from neighbor retransmit-list. */
+  ospf_ls_retransmit_delete_nbr_area (area, lsa);
+
+  /* Unregister LSA from refresh-list */
+  ospf_refresher_unregister_lsa (area->ospf, lsa);
+  
+  /* Create new network-LSA instance. */
+  new = ospf_network_lsa_new (oi);
+  if (new == NULL)
+    return NULL;
+  
+  oip = ospf_lookup_if_params (oi->ifp, oi->address->u.prefix4);
+  assert (oip != NULL);
+  oip->network_lsa_seqnum = new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  new2 = ospf_lsa_install (area->ospf, oi, new);
+  
+  assert (new2 == new);
+  
+  /* Flood LSA through aera. */
+  ospf_flood_through_area (area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: network-LSA refresh",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+static void
+stream_put_ospf_metric (struct stream *s, u_int32_t metric_value)
+{
+  u_int32_t metric;
+  char *mp;
+
+  /* Put 0 metric. TOS metric is not supported. */
+  metric = htonl (metric_value);
+  mp = (char *) &metric;
+  mp++;
+  stream_put (s, mp, 3);
+}
+
+/* summary-LSA related functions. */
+static void
+ospf_summary_lsa_body_set (struct stream *s, struct prefix *p,
+                          u_int32_t metric)
+{
+  struct in_addr mask;
+
+  masklen2ip (p->prefixlen, &mask);
+
+  /* Put Network Mask. */
+  stream_put_ipv4 (s, mask.s_addr);
+
+  /* Set # TOS. */
+  stream_putc (s, (u_char) 0);
+
+  /* Set metric. */
+  stream_put_ospf_metric (s, metric);
+}
+
+static struct ospf_lsa *
+ospf_summary_lsa_new (struct ospf_area *area, struct prefix *p,
+                     u_int32_t metric, struct in_addr id)
+{
+  struct stream *s;
+  struct ospf_lsa *new;
+  struct lsa_header *lsah;
+  int length;
+
+  if (id.s_addr == 0xffffffff)
+    {
+      /* Maybe Link State ID not available. */
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d]: Link ID not available, can't originate",
+                    OSPF_SUMMARY_LSA);
+      return NULL;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type3]: Create summary-LSA instance");
+
+  /* Create new stream for LSA. */
+  s = stream_new (OSPF_MAX_LSA_SIZE);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  lsa_header_set (s, LSA_OPTIONS_GET (area), OSPF_SUMMARY_LSA,
+                 id, area->ospf->router_id);
+
+  /* Set summary-LSA body fields. */
+  ospf_summary_lsa_body_set (s, p, metric);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Create OSPF LSA instance. */
+  new = ospf_lsa_new ();
+  new->area = area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
+
+  /* Copy LSA to store. */
+  new->data = ospf_lsa_data_new (length);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+  return new;
+}
+
+/* Originate Summary-LSA. */
+struct ospf_lsa *
+ospf_summary_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, 
+                           struct ospf_area *area)
+{
+  struct ospf_lsa *new;
+  struct in_addr id;
+  
+  id = ospf_lsa_unique_id (area->ospf, area->lsdb, OSPF_SUMMARY_LSA, p);
+
+  if (id.s_addr == 0xffffffff)
+    {
+      /* Maybe Link State ID not available. */
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d]: Link ID not available, can't originate",
+                    OSPF_SUMMARY_LSA);
+      return NULL;
+    }
+  
+  /* Create new summary-LSA instance. */
+  if ( !(new = ospf_summary_lsa_new (area, (struct prefix *) p, metric, id)))
+    return NULL;
+
+  /* Instlal LSA to LSDB. */
+  new = ospf_lsa_install (area->ospf, NULL, new);
+
+  /* Update LSA origination count. */
+  area->ospf->lsa_originate_count++;
+
+  /* Flooding new LSA through area. */
+  ospf_flood_through_area (area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate summary-LSA %p",
+                new->data->type, inet_ntoa (new->data->id), (void *)new);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+static struct ospf_lsa*
+ospf_summary_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *new;
+  struct summary_lsa *sl;
+  struct prefix p;
+  
+  /* Sanity check. */
+  assert (lsa->data);
+
+  sl = (struct summary_lsa *)lsa->data;
+  p.prefixlen = ip_masklen (sl->mask);
+  new = ospf_summary_lsa_new (lsa->area, &p, GET_METRIC (sl->metric),
+                             sl->header.id);
+  
+  if (!new)
+    return NULL;
+  
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  ospf_lsa_install (ospf, NULL, new);
+  
+  /* Flood LSA through AS. */
+  ospf_flood_through_area (new->area, NULL, new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: summary-LSA refresh",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+  
+  return new;
+}
+
+
+/* summary-ASBR-LSA related functions. */
+static void
+ospf_summary_asbr_lsa_body_set (struct stream *s, struct prefix *p,
+                               u_int32_t metric)
+{
+  /* Put Network Mask. */
+  stream_put_ipv4 (s, (u_int32_t) 0);
+
+  /* Set # TOS. */
+  stream_putc (s, (u_char) 0);
+
+  /* Set metric. */
+  stream_put_ospf_metric (s, metric);
+}
+
+static struct ospf_lsa *
+ospf_summary_asbr_lsa_new (struct ospf_area *area, struct prefix *p,
+                          u_int32_t metric, struct in_addr id)
+{
+  struct stream *s;
+  struct ospf_lsa *new;
+  struct lsa_header *lsah;
+  int length;
+
+  if (id.s_addr == 0xffffffff)
+    {
+      /* Maybe Link State ID not available. */
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d]: Link ID not available, can't originate",
+                    OSPF_ASBR_SUMMARY_LSA);
+      return NULL;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type3]: Create summary-LSA instance");
+
+  /* Create new stream for LSA. */
+  s = stream_new (OSPF_MAX_LSA_SIZE);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  lsa_header_set (s, LSA_OPTIONS_GET (area), OSPF_ASBR_SUMMARY_LSA,
+                 id, area->ospf->router_id);
+
+  /* Set summary-LSA body fields. */
+  ospf_summary_asbr_lsa_body_set (s, p, metric);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Create OSPF LSA instance. */
+  new = ospf_lsa_new ();
+  new->area = area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
+
+  /* Copy LSA to store. */
+  new->data = ospf_lsa_data_new (length);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+  return new;
+}
+
+/* Originate summary-ASBR-LSA. */
+struct ospf_lsa *
+ospf_summary_asbr_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, 
+                                struct ospf_area *area)
+{
+  struct ospf_lsa *new;
+  struct in_addr id;
+  
+  id = ospf_lsa_unique_id (area->ospf, area->lsdb, OSPF_ASBR_SUMMARY_LSA, p);
+
+  if (id.s_addr == 0xffffffff)
+    {
+      /* Maybe Link State ID not available. */
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d]: Link ID not available, can't originate",
+                    OSPF_ASBR_SUMMARY_LSA);
+      return NULL;
+    }
+  
+  /* Create new summary-LSA instance. */
+  new = ospf_summary_asbr_lsa_new (area, (struct prefix *) p, metric, id);
+  if (!new)
+    return NULL;
+
+  /* Install LSA to LSDB. */
+  new = ospf_lsa_install (area->ospf, NULL, new);
+  
+  /* Update LSA origination count. */
+  area->ospf->lsa_originate_count++;
+
+  /* Flooding new LSA through area. */
+  ospf_flood_through_area (area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate summary-ASBR-LSA %p",
+                new->data->type, inet_ntoa (new->data->id), (void *)new);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+static struct ospf_lsa*
+ospf_summary_asbr_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *new;
+  struct summary_lsa *sl;
+  struct prefix p;
+
+  /* Sanity check. */
+  assert (lsa->data);
+
+  sl = (struct summary_lsa *)lsa->data;
+  p.prefixlen = ip_masklen (sl->mask);
+  new = ospf_summary_asbr_lsa_new (lsa->area, &p, GET_METRIC (sl->metric),
+                                  sl->header.id);
+  if (!new)
+    return NULL;
+  
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  ospf_lsa_install (ospf, NULL, new);
+  
+  /* Flood LSA through area. */
+  ospf_flood_through_area (new->area, NULL, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: summary-ASBR-LSA refresh",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+/* AS-external-LSA related functions. */
+
+/* Get nexthop for AS-external-LSAs.  Return nexthop if its interface
+   is connected, else 0*/
+static struct in_addr
+ospf_external_lsa_nexthop_get (struct ospf *ospf, struct in_addr nexthop)
+{
+  struct in_addr fwd;
+  struct prefix nh;
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  fwd.s_addr = 0;
+
+  if (!nexthop.s_addr)
+    return fwd;
+
+  /* Check whether nexthop is covered by OSPF network. */
+  nh.family = AF_INET;
+  nh.u.prefix4 = nexthop;
+  nh.prefixlen = IPV4_MAX_BITLEN;
+  
+  /* XXX/SCALE: If there were a lot of oi's on an ifp, then it'd be
+   * better to make use of the per-ifp table of ois.
+   */
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    if (if_is_operative (oi->ifp))
+      if (oi->address->family == AF_INET)
+        if (prefix_match (oi->address, &nh))
+          return nexthop;
+
+  return fwd;
+}
+
+/* NSSA-external-LSA related functions. */
+
+/* Get 1st IP connection for Forward Addr */
+
+struct in_addr
+ospf_get_ip_from_ifp (struct ospf_interface *oi)
+{
+  struct in_addr fwd;
+
+  fwd.s_addr = 0;
+
+  if (if_is_operative (oi->ifp))
+    return oi->address->u.prefix4;
+  
+  return fwd;
+}
+
+/* Get 1st IP connection for Forward Addr */
+struct in_addr
+ospf_get_nssa_ip (struct ospf_area *area)
+{
+  struct in_addr fwd;
+  struct in_addr best_default;
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  fwd.s_addr = 0;
+  best_default.s_addr = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (area->ospf->oiflist, node, oi))
+    {
+      if (if_is_operative (oi->ifp))
+       if (oi->area->external_routing == OSPF_AREA_NSSA)
+         if (oi->address && oi->address->family == AF_INET)
+           {
+             if (best_default.s_addr == 0)
+               best_default = oi->address->u.prefix4;
+             if (oi->area == area)
+               return oi->address->u.prefix4;
+           }
+    }
+  if (best_default.s_addr != 0)
+    return best_default;
+
+  if (best_default.s_addr != 0)
+    return best_default;
+
+  return fwd;
+}
+
+#define DEFAULT_DEFAULT_METRIC              20
+#define DEFAULT_DEFAULT_ORIGINATE_METRIC     10
+#define DEFAULT_DEFAULT_ALWAYS_METRIC        1
+
+#define DEFAULT_METRIC_TYPE                 EXTERNAL_METRIC_TYPE_2
+
+int
+metric_type (struct ospf *ospf, u_char src)
+{
+  return (ospf->dmetric[src].type < 0 ?
+         DEFAULT_METRIC_TYPE : ospf->dmetric[src].type);
+}
+
+int
+metric_value (struct ospf *ospf, u_char src)
+{
+  if (ospf->dmetric[src].value < 0)
+    {
+      if (src == DEFAULT_ROUTE)
+       {
+         if (ospf->default_originate == DEFAULT_ORIGINATE_ZEBRA)
+           return DEFAULT_DEFAULT_ORIGINATE_METRIC;
+         else
+           return DEFAULT_DEFAULT_ALWAYS_METRIC;
+       }
+      else if (ospf->default_metric < 0)
+       return DEFAULT_DEFAULT_METRIC;
+      else
+       return ospf->default_metric;
+    }
+
+  return ospf->dmetric[src].value;
+}
+
+/* Set AS-external-LSA body. */
+static void
+ospf_external_lsa_body_set (struct stream *s, struct external_info *ei,
+                           struct ospf *ospf)
+{
+  struct prefix_ipv4 *p = &ei->p;
+  struct in_addr mask, fwd_addr;
+  u_int32_t mvalue;
+  int mtype;
+  int type;
+
+  /* Put Network Mask. */
+  masklen2ip (p->prefixlen, &mask);
+  stream_put_ipv4 (s, mask.s_addr);
+
+  /* If prefix is default, specify DEFAULT_ROUTE. */
+  type = is_prefix_default (&ei->p) ? DEFAULT_ROUTE : ei->type;
+  
+  mtype = (ROUTEMAP_METRIC_TYPE (ei) != -1) ?
+    ROUTEMAP_METRIC_TYPE (ei) : metric_type (ospf, type);
+
+  mvalue = (ROUTEMAP_METRIC (ei) != -1) ?
+    ROUTEMAP_METRIC (ei) : metric_value (ospf, type);
+
+  /* Put type of external metric. */
+  stream_putc (s, (mtype == EXTERNAL_METRIC_TYPE_2 ? 0x80 : 0));
+
+  /* Put 0 metric. TOS metric is not supported. */
+  stream_put_ospf_metric (s, mvalue);
+  
+  /* Get forwarding address to nexthop if on the Connection List, else 0. */
+  fwd_addr = ospf_external_lsa_nexthop_get (ospf, ei->nexthop);
+
+  /* Put forwarding address. */
+  stream_put_ipv4 (s, fwd_addr.s_addr);
+  
+  /* Put route tag */
+  stream_putl (s, ei->tag);
+}
+
+/* Create new external-LSA. */
+static struct ospf_lsa *
+ospf_external_lsa_new (struct ospf *ospf,
+                      struct external_info *ei, struct in_addr *old_id)
+{
+  struct stream *s;
+  struct lsa_header *lsah;
+  struct ospf_lsa *new;
+  struct in_addr id;
+  int length;
+
+  if (ei == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+       zlog_debug ("LSA[Type5]: External info is NULL, can't originate");
+      return NULL;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type5]: Originate AS-external-LSA instance");
+
+  /* If old Link State ID is specified, refresh LSA with same ID. */
+  if (old_id)
+    id = *old_id;
+  /* Get Link State with unique ID. */
+  else
+    {
+      id = ospf_lsa_unique_id (ospf, ospf->lsdb, OSPF_AS_EXTERNAL_LSA, &ei->p);
+      if (id.s_addr == 0xffffffff)
+       {
+         /* Maybe Link State ID not available. */
+         if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+           zlog_debug ("LSA[Type5]: Link ID not available, can't originate");
+         return NULL;
+       }
+    }
+
+  /* Create new stream for LSA. */
+  s = stream_new (OSPF_MAX_LSA_SIZE);
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  /* Set LSA common header fields. */
+  lsa_header_set (s, OSPF_OPTION_E, OSPF_AS_EXTERNAL_LSA,
+                 id, ospf->router_id);
+
+  /* Set AS-external-LSA body fields. */
+  ospf_external_lsa_body_set (s, ei, ospf);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Now, create OSPF LSA instance. */
+  new = ospf_lsa_new ();
+  new->area = NULL;
+  SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_APPROVED | OSPF_LSA_SELF_CHECKED);
+
+  /* Copy LSA data to store, discard stream. */
+  new->data = ospf_lsa_data_new (length);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+  return new;
+}
+
+/* As Type-7 */
+static void
+ospf_install_flood_nssa (struct ospf *ospf, 
+                        struct ospf_lsa *lsa, struct external_info *ei)
+{
+  struct ospf_lsa *new;
+  struct as_external_lsa *extlsa;
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+
+  /* LSA may be a Type-5 originated via translation of a Type-7 LSA
+   * which originated from an NSSA area. In which case it should not be 
+   * flooded back to NSSA areas.
+   */
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+    return;
+    
+  /* NSSA Originate or Refresh (If anyNSSA)
+
+  LSA is self-originated. And just installed as Type-5.
+  Additionally, install as Type-7 LSDB for every attached NSSA.
+
+  P-Bit controls which ABR performs translation to outside world; If
+  we are an ABR....do not set the P-bit, because we send the Type-5,
+  not as the ABR Translator, but as the ASBR owner within the AS!
+
+  If we are NOT ABR, Flood through NSSA as Type-7 w/P-bit set.  The
+  elected ABR Translator will see the P-bit, Translate, and re-flood.
+
+  Later, ABR_TASK and P-bit will scan Type-7 LSDB and translate to
+  Type-5's to non-NSSA Areas.  (it will also attempt a re-install) */
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      /* Don't install Type-7 LSA's into nonNSSA area */
+      if (area->external_routing != OSPF_AREA_NSSA)
+        continue;
+
+      /* make lsa duplicate, lock=1 */
+      new = ospf_lsa_dup (lsa);
+      new->area = area;
+      new->data->type = OSPF_AS_NSSA_LSA;
+
+      /* set P-bit if not ABR */
+      if (! IS_OSPF_ABR (ospf))
+        {
+         SET_FLAG(new->data->options, OSPF_OPTION_NP);
+       
+         /* set non-zero FWD ADDR
+       
+         draft-ietf-ospf-nssa-update-09.txt
+       
+         if the network between the NSSA AS boundary router and the
+         adjacent AS is advertised into OSPF as an internal OSPF route,
+         the forwarding address should be the next op address as is cu
+         currently done with type-5 LSAs.  If the intervening network is
+         not adversited into OSPF as an internal OSPF route and the
+         type-7 LSA's P-bit is set a forwarding address should be
+         selected from one of the router's active OSPF inteface addresses
+         which belong to the NSSA.  If no such addresses exist, then
+         no type-7 LSA's with the P-bit set should originate from this
+         router.   */
+       
+         /* kevinm: not updating lsa anymore, just new */
+         extlsa = (struct as_external_lsa *)(new->data);
+       
+         if (extlsa->e[0].fwd_addr.s_addr == 0)
+           extlsa->e[0].fwd_addr = ospf_get_nssa_ip(area); /* this NSSA area in ifp */
+
+         if (extlsa->e[0].fwd_addr.s_addr == 0) 
+         {
+           if (IS_DEBUG_OSPF_NSSA)
+             zlog_debug ("LSA[Type-7]: Could not build FWD-ADDR");
+           ospf_lsa_discard (new);
+           return;
+         }
+       }
+
+      /* install also as Type-7 */
+      ospf_lsa_install (ospf, NULL, new);   /* Remove Old, Lock New = 2 */
+
+      /* will send each copy, lock=2+n */
+      ospf_flood_through_as (ospf, NULL, new); /* all attached NSSA's, no AS/STUBs */
+    }
+}
+
+static struct ospf_lsa *
+ospf_lsa_translated_nssa_new (struct ospf *ospf, 
+                             struct ospf_lsa *type7)
+{
+
+  struct ospf_lsa *new;
+  struct as_external_lsa *ext, *extnew;
+  struct external_info ei;
+  
+  ext = (struct as_external_lsa *)(type7->data);
+
+  /* need external_info struct, fill in bare minimum */  
+  ei.p.family = AF_INET;
+  ei.p.prefix = type7->data->id;
+  ei.p.prefixlen = ip_masklen (ext->mask);
+  ei.type = ZEBRA_ROUTE_OSPF;
+  ei.nexthop = ext->header.adv_router;
+  ei.route_map_set.metric = -1;
+  ei.route_map_set.metric_type = -1;
+  ei.tag = 0;
+  
+  if ( (new = ospf_external_lsa_new (ospf, &ei, &type7->data->id)) == NULL)
+  {
+    if (IS_DEBUG_OSPF_NSSA)
+      zlog_debug ("ospf_nssa_translate_originate(): Could not originate "
+                 "Translated Type-5 for %s", 
+                 inet_ntoa (ei.p.prefix));
+    return NULL;
+  }
+
+  extnew = (struct as_external_lsa *)(new->data);
+   
+  /* copy over Type-7 data to new */
+  extnew->e[0].tos = ext->e[0].tos;
+  extnew->e[0].route_tag = ext->e[0].route_tag;
+  extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr;
+  new->data->ls_seqnum = type7->data->ls_seqnum;
+
+  /* add translated flag, checksum and lock new lsa */
+  SET_FLAG (new->flags, OSPF_LSA_LOCAL_XLT); /* Translated from 7  */   
+  new = ospf_lsa_lock (new);
+  
+  return new; 
+}
+
+/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */
+struct ospf_lsa *
+ospf_translated_nssa_originate (struct ospf *ospf, struct ospf_lsa *type7)
+{
+  struct ospf_lsa *new;
+  struct as_external_lsa *extnew;
+  
+  /* we cant use ospf_external_lsa_originate() as we need to set
+   * the OSPF_LSA_LOCAL_XLT flag, must originate by hand
+   */
+  
+  if ( (new = ospf_lsa_translated_nssa_new (ospf, type7)) == NULL)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_translated_nssa_originate(): Could not translate "
+                 "Type-7, Id %s, to Type-5",
+                 inet_ntoa (type7->data->id));
+      return NULL;
+    }
+    
+  extnew = (struct as_external_lsa *)new;
+  
+  if (IS_DEBUG_OSPF_NSSA)
+    {
+      zlog_debug ("ospf_translated_nssa_originate(): "
+                 "translated Type 7, installed:");
+      ospf_lsa_header_dump (new->data);
+      zlog_debug ("   Network mask: %d",ip_masklen (extnew->mask));
+      zlog_debug ("   Forward addr: %s", inet_ntoa (extnew->e[0].fwd_addr));
+    }
+  
+  if ( (new = ospf_lsa_install (ospf, NULL, new)) == NULL)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_lsa_translated_nssa_originate(): "
+                   "Could not install LSA "
+                   "id %s", inet_ntoa (type7->data->id));
+      return NULL;
+    }
+    
+  ospf->lsa_originate_count++;
+  ospf_flood_through_as (ospf, NULL, new);
+
+  return new;
+}
+
+/* Refresh Translated from NSSA AS-external-LSA. */
+struct ospf_lsa *
+ospf_translated_nssa_refresh (struct ospf *ospf, struct ospf_lsa *type7, 
+                              struct ospf_lsa *type5)
+{
+  struct ospf_lsa *new = NULL;
+  
+  /* Sanity checks. */
+  assert (type7 || type5);
+  if (!(type7 || type5))
+    return NULL;
+  if (type7)
+    assert (type7->data);
+  if (type5)
+    assert (type5->data);
+  assert (ospf->anyNSSA);
+
+  /* get required data according to what has been given */
+  if (type7 && type5 == NULL)
+    {
+      /* find the translated Type-5 for this Type-7 */
+      struct as_external_lsa *ext = (struct as_external_lsa *)(type7->data);
+      struct prefix_ipv4 p = 
+        { 
+          .prefix = type7->data->id,
+          .prefixlen = ip_masklen (ext->mask),
+          .family = AF_INET,
+        };
+
+      type5 = ospf_external_info_find_lsa (ospf, &p);
+    }
+  else if (type5 && type7 == NULL)
+    {
+      /* find the type-7 from which supplied type-5 was translated,
+       * ie find first type-7 with same LSA Id.
+       */
+      struct listnode *ln, *lnn;
+      struct route_node *rn;
+      struct ospf_lsa *lsa;
+      struct ospf_area *area;
+          
+      for (ALL_LIST_ELEMENTS (ospf->areas, ln, lnn, area))
+        {
+          if (area->external_routing != OSPF_AREA_NSSA 
+              && !type7)
+            continue;
+            
+          LSDB_LOOP (NSSA_LSDB(area), rn, lsa)
+            {
+              if (lsa->data->id.s_addr == type5->data->id.s_addr)
+                {
+                  type7 = lsa;
+                  break;
+                }
+            }
+        }
+    }
+
+  /* do we have type7? */
+  if (!type7)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_translated_nssa_refresh(): no Type-7 found for "
+                   "Type-5 LSA Id %s",
+                   inet_ntoa (type5->data->id));
+      return NULL;
+    }
+
+  /* do we have valid translated type5? */
+  if (type5 == NULL || !CHECK_FLAG (type5->flags, OSPF_LSA_LOCAL_XLT) )
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_translated_nssa_refresh(): No translated Type-5 "
+                   "found for Type-7 with Id %s",
+                   inet_ntoa (type7->data->id));
+      return NULL;
+    }
+
+  /* Delete LSA from neighbor retransmit-list. */
+  ospf_ls_retransmit_delete_nbr_as (ospf, type5);
+  
+  /* create new translated LSA */
+  if ( (new = ospf_lsa_translated_nssa_new (ospf, type7)) == NULL)
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_translated_nssa_refresh(): Could not translate "
+                   "Type-7 for %s to Type-5",
+                   inet_ntoa (type7->data->id));
+      return NULL;
+    }
+
+  if ( !(new = ospf_lsa_install (ospf, NULL, new)) )
+    {
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("ospf_translated_nssa_refresh(): Could not install "
+                   "translated LSA, Id %s",
+                   inet_ntoa (type7->data->id));
+      return NULL;
+    }
+  
+  /* Flood LSA through area. */
+  ospf_flood_through_as (ospf, NULL, new);
+
+  return new;
+}
+
+int
+is_prefix_default (struct prefix_ipv4 *p)
+{
+  struct prefix_ipv4 q;
+
+  q.family = AF_INET;
+  q.prefix.s_addr = 0;
+  q.prefixlen = 0;
+
+  return prefix_same ((struct prefix *) p, (struct prefix *) &q);
+}
+
+/* Originate an AS-external-LSA, install and flood. */
+struct ospf_lsa *
+ospf_external_lsa_originate (struct ospf *ospf, struct external_info *ei)
+{
+  struct ospf_lsa *new;
+
+  /* Added for NSSA project....
+
+       External LSAs are originated in ASBRs as usual, but for NSSA systems.
+     there is the global Type-5 LSDB and a Type-7 LSDB installed for
+     every area.  The Type-7's are flooded to every IR and every ABR; We
+     install the Type-5 LSDB so that the normal "refresh" code operates
+     as usual, and flag them as not used during ASE calculations.  The
+     Type-7 LSDB is used for calculations.  Each Type-7 has a Forwarding
+     Address of non-zero.
+
+     If an ABR is the elected NSSA translator, following SPF and during
+     the ABR task it will translate all the scanned Type-7's, with P-bit
+     ON and not-self generated, and translate to Type-5's throughout the
+     non-NSSA/STUB AS.
+
+     A difference in operation depends whether this ASBR is an ABR
+     or not.  If not an ABR, the P-bit is ON, to indicate that any
+     elected NSSA-ABR can perform its translation.
+
+     If an ABR, the P-bit is OFF;  No ABR will perform translation and
+     this ASBR will flood the Type-5 LSA as usual.
+
+     For the case where this ASBR is not an ABR, the ASE calculations
+     are based on the Type-5 LSDB;  The Type-7 LSDB exists just to
+     demonstrate to the user that there are LSA's that belong to any
+     attached NSSA.
+
+     Finally, it just so happens that when the ABR is translating every
+     Type-7 into Type-5, it installs it into the Type-5 LSDB as an
+     approved Type-5 (translated from Type-7);  at the end of translation
+     if any Translated Type-5's remain unapproved, then they must be
+     flushed from the AS.
+
+     */
+  
+  /* Check the AS-external-LSA should be originated. */
+  if (!ospf_redistribute_check (ospf, ei, NULL))
+    return NULL;
+  
+  /* Create new AS-external-LSA instance. */
+  if ((new = ospf_external_lsa_new (ospf, ei, NULL)) == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("LSA[Type5:%s]: Could not originate AS-external-LSA",
+                  inet_ntoa (ei->p.prefix));
+      return NULL;
+    }
+
+  /* Install newly created LSA into Type-5 LSDB, lock = 1. */
+  ospf_lsa_install (ospf, NULL, new);
+
+  /* Update LSA origination count. */
+  ospf->lsa_originate_count++;
+
+  /* Flooding new LSA. only to AS (non-NSSA/STUB) */
+  ospf_flood_through_as (ospf, NULL, new);
+
+  /* If there is any attached NSSA, do special handling */
+  if (ospf->anyNSSA &&
+      /* stay away from translated LSAs! */
+      !(CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT)))
+    ospf_install_flood_nssa (ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate AS-external-LSA %p",
+                new->data->type, inet_ntoa (new->data->id), (void *)new);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+/* Originate AS-external-LSA from external info with initial flag. */
+int
+ospf_external_lsa_originate_timer (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+  struct route_node *rn;
+  struct external_info *ei;
+  struct route_table *rt;
+  int type = THREAD_VAL (thread);
+
+  ospf->t_external_lsa = NULL;
+
+  /* Originate As-external-LSA from all type of distribute source. */
+  if ((rt = EXTERNAL_INFO (type)))
+    for (rn = route_top (rt); rn; rn = route_next (rn))
+      if ((ei = rn->info) != NULL)
+       if (!is_prefix_default ((struct prefix_ipv4 *)&ei->p))
+         if (!ospf_external_lsa_originate (ospf, ei))
+           zlog_warn ("LSA: AS-external-LSA was not originated.");
+  
+  return 0;
+}
+
+static struct external_info *
+ospf_default_external_info (struct ospf *ospf)
+{
+  int type;
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+  
+  p.family = AF_INET;
+  p.prefix.s_addr = 0;
+  p.prefixlen = 0;
+
+  /* First, lookup redistributed default route. */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    if (EXTERNAL_INFO (type) && type != ZEBRA_ROUTE_OSPF)
+      {
+       rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) &p);
+       if (rn != NULL)
+         {
+           route_unlock_node (rn);
+           assert (rn->info);
+           if (ospf_redistribute_check (ospf, rn->info, NULL))
+             return rn->info;
+         }
+      }
+
+  return NULL;
+}
+
+int
+ospf_default_originate_timer (struct thread *thread)
+{
+  struct prefix_ipv4 p;
+  struct in_addr nexthop;
+  struct external_info *ei;
+  struct ospf *ospf;
+  
+  ospf = THREAD_ARG (thread);
+
+  p.family = AF_INET;
+  p.prefix.s_addr = 0;
+  p.prefixlen = 0;
+
+  if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS)
+    {
+      /* If there is no default route via redistribute,
+        then originate AS-external-LSA with nexthop 0 (self). */
+      nexthop.s_addr = 0;
+      ospf_external_info_add (DEFAULT_ROUTE, p, 0, nexthop, 0);
+    }
+
+  if ((ei = ospf_default_external_info (ospf)))
+    ospf_external_lsa_originate (ospf, ei);
+  
+  return 0;
+}
+
+/* Flush any NSSA LSAs for given prefix */
+void
+ospf_nssa_lsa_flush (struct ospf *ospf, struct prefix_ipv4 *p)
+{
+  struct listnode *node, *nnode;
+  struct ospf_lsa *lsa;
+  struct ospf_area *area;
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+  {
+    if (area->external_routing == OSPF_AREA_NSSA)
+    {
+      if (!(lsa = ospf_lsa_lookup (area, OSPF_AS_NSSA_LSA, p->prefix,
+                                ospf->router_id))) 
+      {
+        if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) 
+          zlog_debug ("LSA: There is no such AS-NSSA-LSA %s/%d in LSDB",
+                    inet_ntoa (p->prefix), p->prefixlen);
+        continue;
+      }
+      ospf_ls_retransmit_delete_nbr_area (area, lsa);
+      if (!IS_LSA_MAXAGE (lsa)) 
+      {
+        ospf_refresher_unregister_lsa (ospf, lsa);
+        ospf_lsa_flush_area (lsa, area);
+      }
+    }
+  }
+}
+
+/* Flush an AS-external-LSA from LSDB and routing domain. */
+void
+ospf_external_lsa_flush (struct ospf *ospf,
+                        u_char type, struct prefix_ipv4 *p,
+                        ifindex_t ifindex /*, struct in_addr nexthop */)
+{
+  struct ospf_lsa *lsa;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+    zlog_debug ("LSA: Flushing AS-external-LSA %s/%d",
+              inet_ntoa (p->prefix), p->prefixlen);
+
+  /* First lookup LSA from LSDB. */
+  if (!(lsa = ospf_external_info_find_lsa (ospf, p)))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+       zlog_debug ("LSA: There is no such AS-external-LSA %s/%d in LSDB",
+                  inet_ntoa (p->prefix), p->prefixlen);
+      return;
+    }
+
+  /* If LSA is selforiginated, not a translated LSA, and there is 
+   * NSSA area, flush Type-7 LSA's at first. 
+   */
+  if (IS_LSA_SELF(lsa) && (ospf->anyNSSA)
+      && !(CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)))
+    ospf_nssa_lsa_flush (ospf, p);
+
+  /* Sweep LSA from Link State Retransmit List. */
+  ospf_ls_retransmit_delete_nbr_as (ospf, lsa);
+
+  /* There must be no self-originated LSA in rtrs_external. */
+#if 0
+  /* Remove External route from Zebra. */
+  ospf_zebra_delete ((struct prefix_ipv4 *) p, &nexthop);
+#endif
+
+  if (!IS_LSA_MAXAGE (lsa))
+    {
+      /* Unregister LSA from Refresh queue. */
+      ospf_refresher_unregister_lsa (ospf, lsa);
+
+      /* Flush AS-external-LSA through AS. */
+      ospf_lsa_flush_as (ospf, lsa);
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+    zlog_debug ("ospf_external_lsa_flush(): stop");
+}
+
+void
+ospf_external_lsa_refresh_default (struct ospf *ospf)
+{
+  struct prefix_ipv4 p;
+  struct external_info *ei;
+  struct ospf_lsa *lsa;
+
+  p.family = AF_INET;
+  p.prefixlen = 0;
+  p.prefix.s_addr = 0;
+
+  ei = ospf_default_external_info (ospf);
+  lsa = ospf_external_info_find_lsa (ospf, &p);
+
+  if (ei)
+    {
+      if (lsa)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p",
+                      (void *)lsa);
+         ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_FORCE);
+       }
+      else
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("LSA[Type5:0.0.0.0]: Originate AS-external-LSA");
+         ospf_external_lsa_originate (ospf, ei);
+       }
+    }
+  else
+    {
+      if (lsa)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("LSA[Type5:0.0.0.0]: Flush AS-external-LSA");
+          ospf_refresher_unregister_lsa (ospf, lsa);
+         ospf_lsa_flush_as (ospf, lsa);
+       }
+    }
+}
+
+void
+ospf_external_lsa_refresh_type (struct ospf *ospf, u_char type, int force)
+{
+  struct route_node *rn;
+  struct external_info *ei;
+
+  if (type != DEFAULT_ROUTE)
+    if (EXTERNAL_INFO(type))
+      /* Refresh each redistributed AS-external-LSAs. */
+      for (rn = route_top (EXTERNAL_INFO (type)); rn; rn = route_next (rn))
+       if ((ei = rn->info))
+         if (!is_prefix_default (&ei->p))
+           {
+             struct ospf_lsa *lsa;
+
+             if ((lsa = ospf_external_info_find_lsa (ospf, &ei->p)))
+               ospf_external_lsa_refresh (ospf, lsa, ei, force);
+             else
+               ospf_external_lsa_originate (ospf, ei);
+           }
+}
+
+/* Refresh AS-external-LSA. */
+struct ospf_lsa *
+ospf_external_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa,
+                          struct external_info *ei, int force)
+{
+  struct ospf_lsa *new;
+  int changed;
+  
+  /* Check the AS-external-LSA should be originated. */
+  if (!ospf_redistribute_check (ospf, ei, &changed))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d:%s]: Could not be refreshed, "
+                   "redist check fail", 
+                   lsa->data->type, inet_ntoa (lsa->data->id));
+      ospf_external_lsa_flush (ospf, ei->type, &ei->p,
+                              ei->ifindex /*, ei->nexthop */);
+      return NULL;
+    }
+
+  if (!changed && !force)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+        zlog_debug ("LSA[Type%d:%s]: Not refreshed, not changed/forced",
+                   lsa->data->type, inet_ntoa (lsa->data->id));
+      return NULL;
+    }
+
+  /* Delete LSA from neighbor retransmit-list. */
+  ospf_ls_retransmit_delete_nbr_as (ospf, lsa);
+
+  /* Unregister AS-external-LSA from refresh-list. */
+  ospf_refresher_unregister_lsa (ospf, lsa);
+
+  new = ospf_external_lsa_new (ospf, ei, &lsa->data->id);
+  
+  if (new == NULL)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+       zlog_debug ("LSA[Type%d:%s]: Could not be refreshed", lsa->data->type,
+                  inet_ntoa (lsa->data->id));
+      return NULL;
+    }
+  
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  ospf_lsa_install (ospf, NULL, new);  /* As type-5. */
+
+  /* Flood LSA through AS. */
+  ospf_flood_through_as (ospf, NULL, new);
+
+  /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */
+  if (ospf->anyNSSA && !(CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT)))
+    ospf_install_flood_nssa (ospf, new, ei); /* Install/Flood per new rules */
+
+  /* Register self-originated LSA to refresh queue. 
+   * Translated LSAs should not be registered, but refreshed upon 
+   * refresh of the Type-7
+   */
+  if ( !CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT) )
+    ospf_refresher_register_lsa (ospf, new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: AS-external-LSA refresh",
+                 new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  return new;
+}
+
+
+/* LSA installation functions. */
+
+/* Install router-LSA to an area. */
+static struct ospf_lsa *
+ospf_router_lsa_install (struct ospf *ospf, struct ospf_lsa *new,
+                         int rt_recalc)
+{
+  struct ospf_area *area = new->area;
+
+  /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs
+     The entire routing table must be recalculated, starting with
+     the shortest path calculations for each area (not just the
+     area whose link-state database has changed). 
+  */
+
+  if (IS_LSA_SELF (new))
+    {
+
+      /* Only install LSA if it is originated/refreshed by us.
+       * If LSA was received by flooding, the RECEIVED flag is set so do
+       * not link the LSA */
+      if (CHECK_FLAG (new->flags, OSPF_LSA_RECEIVED))
+       return new; /* ignore stale LSA */
+
+      /* Set self-originated router-LSA. */
+      ospf_lsa_unlock (&area->router_lsa_self);
+      area->router_lsa_self = ospf_lsa_lock (new);
+
+      ospf_refresher_register_lsa (ospf, new);
+    }
+  if (rt_recalc)
+    ospf_spf_calculate_schedule (ospf, SPF_FLAG_ROUTER_LSA_INSTALL);
+  return new;
+}
+
+#define OSPF_INTERFACE_TIMER_ON(T,F,V) \
+       if (!(T)) \
+         (T) = thread_add_timer (master, (F), oi, (V))
+
+/* Install network-LSA to an area. */
+static struct ospf_lsa *
+ospf_network_lsa_install (struct ospf *ospf,
+                         struct ospf_interface *oi, 
+                         struct ospf_lsa *new,
+                         int rt_recalc)
+{
+
+  /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs
+     The entire routing table must be recalculated, starting with
+     the shortest path calculations for each area (not just the
+     area whose link-state database has changed). 
+  */
+  if (IS_LSA_SELF (new))
+    {
+      /* We supposed that when LSA is originated by us, we pass the int
+        for which it was originated. If LSA was received by flooding,
+        the RECEIVED flag is set, so we do not link the LSA to the int. */
+      if (CHECK_FLAG (new->flags, OSPF_LSA_RECEIVED))
+       return new; /* ignore stale LSA */
+
+      ospf_lsa_unlock (&oi->network_lsa_self);
+      oi->network_lsa_self = ospf_lsa_lock (new);
+      ospf_refresher_register_lsa (ospf, new);
+    }
+  if (rt_recalc)
+    ospf_spf_calculate_schedule (ospf, SPF_FLAG_NETWORK_LSA_INSTALL);
+
+  return new;
+}
+
+/* Install summary-LSA to an area. */
+static struct ospf_lsa *
+ospf_summary_lsa_install (struct ospf *ospf, struct ospf_lsa *new,
+                         int rt_recalc)
+{
+  if (rt_recalc && !IS_LSA_SELF (new))
+    {
+      /* RFC 2328 Section 13.2 Summary-LSAs
+        The best route to the destination described by the summary-
+        LSA must be recalculated (see Section 16.5).  If this
+        destination is an AS boundary router, it may also be
+        necessary to re-examine all the AS-external-LSAs.
+      */
+
+#if 0
+      /* This doesn't exist yet... */
+      ospf_summary_incremental_update(new); */
+#else /* #if 0 */
+      ospf_spf_calculate_schedule (ospf, SPF_FLAG_SUMMARY_LSA_INSTALL);
+#endif /* #if 0 */
+    }
+
+  if (IS_LSA_SELF (new))
+    ospf_refresher_register_lsa (ospf, new);
+
+  return new;
+}
+
+/* Install ASBR-summary-LSA to an area. */
+static struct ospf_lsa *
+ospf_summary_asbr_lsa_install (struct ospf *ospf, struct ospf_lsa *new,
+                              int rt_recalc)
+{
+  if (rt_recalc && !IS_LSA_SELF (new))
+    {
+      /* RFC 2328 Section 13.2 Summary-LSAs
+        The best route to the destination described by the summary-
+        LSA must be recalculated (see Section 16.5).  If this
+        destination is an AS boundary router, it may also be
+        necessary to re-examine all the AS-external-LSAs.
+      */
+#if 0
+      /* These don't exist yet... */
+      ospf_summary_incremental_update(new);
+      /* Isn't this done by the above call? 
+        - RFC 2328 Section 16.5 implies it should be */
+      /* ospf_ase_calculate_schedule(); */
+#else  /* #if 0 */
+      ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL);
+#endif /* #if 0 */
+    }
+
+  /* register LSA to refresh-list. */
+  if (IS_LSA_SELF (new))
+    ospf_refresher_register_lsa (ospf, new);
+
+  return new;
+}
+
+/* Install AS-external-LSA. */
+static struct ospf_lsa *
+ospf_external_lsa_install (struct ospf *ospf, struct ospf_lsa *new,
+                          int rt_recalc)
+{
+  ospf_ase_register_external_lsa (new, ospf);
+  /* If LSA is not self-originated, calculate an external route. */
+  if (rt_recalc)
+    {
+      /* RFC 2328 Section 13.2 AS-external-LSAs
+            The best route to the destination described by the AS-
+            external-LSA must be recalculated (see Section 16.6).
+      */
+
+      if (!IS_LSA_SELF (new))
+        ospf_ase_incremental_update (ospf, new);
+    }
+
+  if (new->data->type == OSPF_AS_NSSA_LSA)
+    {
+      /* There is no point to register selforiginate Type-7 LSA for
+       * refreshing. We rely on refreshing Type-5 LSA's 
+       */
+      if (IS_LSA_SELF (new))
+        return new;
+      else
+        {
+          /* Try refresh type-5 translated LSA for this LSA, if one exists.
+           * New translations will be taken care of by the abr_task.
+           */ 
+          ospf_translated_nssa_refresh (ospf, new, NULL);
+          ospf_schedule_abr_task(ospf);
+        }
+    }
+
+  /* Register self-originated LSA to refresh queue. 
+   * Leave Translated LSAs alone if NSSA is enabled
+   */
+  if (IS_LSA_SELF (new) && !CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT ) )
+    ospf_refresher_register_lsa (ospf, new);
+
+  return new;
+}
+
+void
+ospf_discard_from_db (struct ospf *ospf,
+                     struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *old;
+  
+  if (!lsdb)
+    {
+      zlog_warn ("%s: Called with NULL lsdb!", __func__);
+      if (!lsa)
+        zlog_warn ("%s: and NULL LSA!", __func__);
+      else
+        zlog_warn ("LSA[Type%d:%s]: not associated with LSDB!",
+                   lsa->data->type, inet_ntoa (lsa->data->id));
+      return;
+    }
+  
+  old = ospf_lsdb_lookup (lsdb, lsa);
+
+  if (!old)
+    return;
+
+  if (old->refresh_list >= 0)
+    ospf_refresher_unregister_lsa (ospf, old);
+
+  switch (old->data->type)
+    {
+    case OSPF_AS_EXTERNAL_LSA:
+      ospf_ase_unregister_external_lsa (old, ospf);
+      ospf_ls_retransmit_delete_nbr_as (ospf, old);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      ospf_ls_retransmit_delete_nbr_as (ospf, old);
+      break;
+    case OSPF_AS_NSSA_LSA:
+      ospf_ls_retransmit_delete_nbr_area (old->area, old);
+      ospf_ase_unregister_external_lsa (old, ospf);
+      break;
+    default:
+      ospf_ls_retransmit_delete_nbr_area (old->area, old);
+      break;
+    }
+
+  ospf_lsa_maxage_delete (ospf, old);
+  ospf_lsa_discard (old);
+}
+
+struct ospf_lsa *
+ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi,
+                 struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *new = NULL;
+  struct ospf_lsa *old = NULL;
+  struct ospf_lsdb *lsdb = NULL;
+  int rt_recalc;
+
+  /* Set LSDB. */
+  switch (lsa->data->type)
+    {
+      /* kevinm */
+    case OSPF_AS_NSSA_LSA:
+      if (lsa->area)
+       lsdb = lsa->area->lsdb;
+      else
+       lsdb = ospf->lsdb;
+      break;
+    case OSPF_AS_EXTERNAL_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      lsdb = ospf->lsdb;
+      break;
+    default:
+      lsdb = lsa->area->lsdb;
+      break;
+    }
+
+  assert (lsdb);
+
+  /*  RFC 2328 13.2.  Installing LSAs in the database
+
+        Installing a new LSA in the database, either as the result of
+        flooding or a newly self-originated LSA, may cause the OSPF
+        routing table structure to be recalculated.  The contents of the
+        new LSA should be compared to the old instance, if present.  If
+        there is no difference, there is no need to recalculate the
+        routing table. When comparing an LSA to its previous instance,
+        the following are all considered to be differences in contents:
+
+            o   The LSA's Options field has changed.
+
+            o   One of the LSA instances has LS age set to MaxAge, and
+                the other does not.
+
+            o   The length field in the LSA header has changed.
+
+            o   The body of the LSA (i.e., anything outside the 20-byte
+                LSA header) has changed. Note that this excludes changes
+                in LS Sequence Number and LS Checksum.
+
+  */
+  /* Look up old LSA and determine if any SPF calculation or incremental
+     update is needed */
+  old = ospf_lsdb_lookup (lsdb, lsa);
+
+  /* Do comparision and record if recalc needed. */
+  rt_recalc = 0;
+  if (  old == NULL || ospf_lsa_different(old, lsa))
+    rt_recalc = 1;
+
+  /*
+     Sequence number check (Section 14.1 of rfc 2328)
+     "Premature aging is used when it is time for a self-originated
+      LSA's sequence number field to wrap.  At this point, the current
+      LSA instance (having LS sequence number MaxSequenceNumber) must
+      be prematurely aged and flushed from the routing domain before a
+      new instance with sequence number equal to InitialSequenceNumber
+      can be originated. "
+   */
+
+  if (ntohl(lsa->data->ls_seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER)
+    {
+      if (ospf_lsa_is_self_originated(ospf, lsa))
+        {
+          lsa->data->ls_seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER);
+          
+          if (!IS_LSA_MAXAGE(lsa))
+            lsa->flags |= OSPF_LSA_PREMATURE_AGE;
+          lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+       
+          if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+            {
+             zlog_debug ("ospf_lsa_install() Premature Aging "
+                        "lsa 0x%p, seqnum 0x%x",
+                        (void *)lsa, ntohl(lsa->data->ls_seqnum));
+             ospf_lsa_header_dump (lsa->data);
+            }
+        }
+      else
+        {
+          if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+            {
+             zlog_debug ("ospf_lsa_install() got an lsa with seq 0x80000000 "
+                        "that was not self originated. Ignoring\n");
+             ospf_lsa_header_dump (lsa->data);
+            }
+         return old;
+        }
+    }
+
+  /* discard old LSA from LSDB */
+  if (old != NULL)
+    ospf_discard_from_db (ospf, lsdb, lsa);
+
+  /* Calculate Checksum if self-originated?. */
+  if (IS_LSA_SELF (lsa))
+    ospf_lsa_checksum (lsa->data);
+
+  /* Insert LSA to LSDB. */
+  ospf_lsdb_add (lsdb, lsa);
+  lsa->lsdb = lsdb;
+
+  /* Do LSA specific installation process. */
+  switch (lsa->data->type)
+    {
+    case OSPF_ROUTER_LSA:
+      new = ospf_router_lsa_install (ospf, lsa, rt_recalc);
+      break;
+    case OSPF_NETWORK_LSA:
+      assert (oi);
+      new = ospf_network_lsa_install (ospf, oi, lsa, rt_recalc);
+      break;
+    case OSPF_SUMMARY_LSA:
+      new = ospf_summary_lsa_install (ospf, lsa, rt_recalc);
+      break;
+    case OSPF_ASBR_SUMMARY_LSA:
+      new = ospf_summary_asbr_lsa_install (ospf, lsa, rt_recalc);
+      break;
+    case OSPF_AS_EXTERNAL_LSA:
+      new = ospf_external_lsa_install (ospf, lsa, rt_recalc);
+      break;
+    case OSPF_OPAQUE_LINK_LSA:
+      if (IS_LSA_SELF (lsa))
+       lsa->oi = oi; /* Specify outgoing ospf-interface for this LSA. */
+      else
+        {
+          /* Incoming "oi" for this LSA has set at LSUpd reception. */
+        }
+      /* Fallthrough */
+    case OSPF_OPAQUE_AREA_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      new = ospf_opaque_lsa_install (lsa, rt_recalc);
+      break;
+    case OSPF_AS_NSSA_LSA:
+      new = ospf_external_lsa_install (ospf, lsa, rt_recalc);
+    default: /* type-6,8,9....nothing special */
+      break;
+    }
+
+  if (new == NULL)
+    return new;  /* Installation failed, cannot proceed further -- endo. */
+
+  /* Debug logs. */
+  if (IS_DEBUG_OSPF (lsa, LSA_INSTALL))
+    {
+      char area_str[INET_ADDRSTRLEN];
+
+      switch (lsa->data->type)
+        {
+        case OSPF_AS_EXTERNAL_LSA:
+        case OSPF_OPAQUE_AS_LSA:
+        case OSPF_AS_NSSA_LSA:
+          zlog_debug ("LSA[%s]: Install %s",
+                 dump_lsa_key (new),
+                 LOOKUP (ospf_lsa_type_msg, new->data->type));
+          break;
+        default:
+         strcpy (area_str, inet_ntoa (new->area->area_id));
+          zlog_debug ("LSA[%s]: Install %s to Area %s",
+                 dump_lsa_key (new),
+                 LOOKUP (ospf_lsa_type_msg, new->data->type), area_str);
+          break;
+        }
+    }
+
+  /* 
+     If received LSA' ls_age is MaxAge, or lsa is being prematurely aged
+     (it's getting flushed out of the area), set LSA on MaxAge LSA list. 
+   */
+  if (IS_LSA_MAXAGE (new))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_INSTALL))
+        zlog_debug ("LSA[Type%d:%s]: Install LSA 0x%p, MaxAge",
+                   new->data->type,
+                   inet_ntoa (new->data->id),
+                   (void *)lsa);
+      ospf_lsa_maxage (ospf, lsa);
+    }
+
+  return new;
+}
+
+
+int
+ospf_check_nbr_status (struct ospf *ospf)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    {
+      struct route_node *rn;
+      struct ospf_neighbor *nbr;
+
+      if (ospf_if_is_enable (oi))
+       for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+          if ((nbr = rn->info) != NULL)
+           if (nbr->state == NSM_Exchange || nbr->state == NSM_Loading)
+             {
+               route_unlock_node (rn);
+               return 0;
+             }
+    }
+
+  return 1;
+}
+
+
+
+static int
+ospf_maxage_lsa_remover (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+  int reschedule = 0;
+
+  ospf->t_maxage = NULL;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+    zlog_debug ("LSA[MaxAge]: remover Start");
+
+  reschedule = !ospf_check_nbr_status (ospf);
+
+  if (!reschedule)
+    for (rn = route_top(ospf->maxage_lsa); rn; rn = route_next(rn))
+      {
+       if ((lsa = rn->info) == NULL)
+         {
+           continue;
+         }
+
+        /* There is at least one neighbor from which we still await an ack
+         * for that LSA, so we are not allowed to remove it from our lsdb yet
+         * as per RFC 2328 section 14 para 4 a) */
+        if (lsa->retransmit_counter > 0)
+          {
+            reschedule = 1;
+            continue;
+          }
+        
+        /* TODO: maybe convert this function to a work-queue */
+        if (thread_should_yield (thread))
+          {
+            OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0);
+            route_unlock_node(rn); /* route_top/route_next */
+            return 0;
+          }
+          
+        /* Remove LSA from the LSDB */
+        if (IS_LSA_SELF (lsa))
+          if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+            zlog_debug ("LSA[Type%d:%s]: LSA 0x%lx is self-originated: ",
+                       lsa->data->type, inet_ntoa (lsa->data->id), (u_long)lsa);
+
+        if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+          zlog_debug ("LSA[Type%d:%s]: MaxAge LSA removed from list",
+                     lsa->data->type, inet_ntoa (lsa->data->id));
+
+       if (CHECK_FLAG (lsa->flags, OSPF_LSA_PREMATURE_AGE))
+          {
+            if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+              zlog_debug ("originating new lsa for lsa 0x%p\n", (void *)lsa);
+            ospf_lsa_refresh (ospf, lsa);
+          }
+
+       /* Remove from lsdb. */
+       if (lsa->lsdb)
+         {
+           ospf_discard_from_db (ospf, lsa->lsdb, lsa);
+           ospf_lsdb_delete (lsa->lsdb, lsa);
+          }
+        else
+          zlog_warn ("%s: LSA[Type%d:%s]: No associated LSDB!", __func__,
+                     lsa->data->type, inet_ntoa (lsa->data->id));
+      }
+
+  /*    A MaxAge LSA must be removed immediately from the router's link
+        state database as soon as both a) it is no longer contained on any
+        neighbor Link state retransmission lists and b) none of the router's
+        neighbors are in states Exchange or Loading. */
+  if (reschedule)
+    OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover,
+                   ospf->maxage_delay);
+
+  return 0;
+}
+
+void
+ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct route_node *rn;
+  struct prefix_ptr lsa_prefix;
+
+  lsa_prefix.family = 0;
+  lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT;
+  lsa_prefix.prefix = (uintptr_t) lsa;
+
+  if ((rn = route_node_lookup(ospf->maxage_lsa,
+                             (struct prefix *)&lsa_prefix)))
+    {
+      if (rn->info == lsa)
+       {
+         UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE);
+         ospf_lsa_unlock (&lsa); /* maxage_lsa */
+         rn->info = NULL;
+         route_unlock_node (rn); /* unlock node because lsa is deleted */
+       }
+      route_unlock_node (rn); /* route_node_lookup */
+    }
+}
+
+/* Add LSA onto the MaxAge list, and schedule for removal.
+ * This does *not* lead to the LSA being flooded, that must be taken
+ * care of elsewhere, see, e.g., ospf_lsa_flush* (which are callers of this
+ * function).
+ */
+void
+ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct prefix_ptr lsa_prefix;
+  struct route_node *rn;
+
+  /* When we saw a MaxAge LSA flooded to us, we put it on the list
+     and schedule the MaxAge LSA remover. */
+  if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE))
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+       zlog_debug ("LSA[Type%d:%s]: %p already exists on MaxAge LSA list",
+                  lsa->data->type, inet_ntoa (lsa->data->id), (void *)lsa);
+      return;
+    }
+
+  lsa_prefix.family = 0;
+  lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT;
+  lsa_prefix.prefix = (uintptr_t) lsa;
+
+  if ((rn = route_node_get (ospf->maxage_lsa,
+                           (struct prefix *)&lsa_prefix)) != NULL)
+    {
+      if (rn->info != NULL)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+           zlog_debug ("LSA[%s]: found LSA (%p) in table for LSA %p %d",
+                       dump_lsa_key (lsa), rn->info, (void *)lsa,
+                       lsa_prefix.prefixlen);
+         route_unlock_node (rn);
+       }
+      else
+       {
+         rn->info = ospf_lsa_lock(lsa);
+         SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE);
+       }
+    }
+  else
+    {
+      zlog_err("Unable to allocate memory for maxage lsa\n");
+      assert(0);
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+    zlog_debug ("LSA[%s]: MaxAge LSA remover scheduled.", dump_lsa_key (lsa));
+
+  OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover,
+                 ospf->maxage_delay);
+}
+
+static int
+ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  /* Stay away from any Local Translated Type-7 LSAs */
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+    return 0;
+
+  if (IS_LSA_MAXAGE (lsa))
+    /* Self-originated LSAs should NOT time-out instead,
+       they're flushed and submitted to the max_age list explicitly. */
+    if (!ospf_lsa_is_self_originated (ospf, lsa))
+      {
+       if (IS_DEBUG_OSPF (lsa, LSA_FLOODING))
+         zlog_debug("LSA[%s]: is MaxAge", dump_lsa_key (lsa));
+
+        switch (lsa->data->type)
+          {
+          case OSPF_OPAQUE_LINK_LSA:
+          case OSPF_OPAQUE_AREA_LSA:
+          case OSPF_OPAQUE_AS_LSA:
+            /*
+             * As a general rule, whenever network topology has changed
+             * (due to an LSA removal in this case), routing recalculation
+             * should be triggered. However, this is not true for opaque
+             * LSAs. Even if an opaque LSA instance is going to be removed
+             * from the routing domain, it does not mean a change in network
+             * topology, and thus, routing recalculation is not needed here.
+             */
+            break;
+          case OSPF_AS_EXTERNAL_LSA:
+          case OSPF_AS_NSSA_LSA:
+           ospf_ase_incremental_update (ospf, lsa);
+            break;
+          default:
+           ospf_spf_calculate_schedule (ospf, SPF_FLAG_MAXAGE);
+            break;
+          }
+       ospf_lsa_maxage (ospf, lsa);
+      }
+
+  if (IS_LSA_MAXAGE (lsa) && !ospf_lsa_is_self_originated (ospf, lsa))
+    if (LS_AGE (lsa) > OSPF_LSA_MAXAGE + 30)
+      printf ("Eek! Shouldn't happen!\n");
+
+  return 0;
+}
+
+/* Periodical check of MaxAge LSA. */
+int
+ospf_lsa_maxage_walker (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+
+  ospf->t_maxage_walker = NULL;
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      LSDB_LOOP (ROUTER_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (NETWORK_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
+        ospf_lsa_maxage_walker_remover (ospf, lsa);
+    }
+
+  /* for AS-external-LSAs. */
+  if (ospf->lsdb)
+    {
+      LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+      LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa)
+       ospf_lsa_maxage_walker_remover (ospf, lsa);
+    }
+
+  OSPF_TIMER_ON (ospf->t_maxage_walker, ospf_lsa_maxage_walker,
+                OSPF_LSA_MAXAGE_CHECK_INTERVAL);
+  return 0;
+}
+
+struct ospf_lsa *
+ospf_lsa_lookup_by_prefix (struct ospf_lsdb *lsdb, u_char type,
+                          struct prefix_ipv4 *p, struct in_addr router_id)
+{
+  struct ospf_lsa *lsa;
+  struct in_addr mask, id;
+  struct lsa_header_mask
+  {
+    struct lsa_header header;
+    struct in_addr mask;
+  } *hmask;
+
+  lsa = ospf_lsdb_lookup_by_id (lsdb, type, p->prefix, router_id);
+  if (lsa == NULL)
+    return NULL;
+
+  masklen2ip (p->prefixlen, &mask);
+
+  hmask = (struct lsa_header_mask *) lsa->data;
+
+  if (mask.s_addr != hmask->mask.s_addr)
+    {
+      id.s_addr = p->prefix.s_addr | (~mask.s_addr);
+      lsa = ospf_lsdb_lookup_by_id (lsdb, type, id, router_id);
+      if (!lsa)
+        return NULL;
+    }
+
+  return lsa;
+}
+
+struct ospf_lsa *
+ospf_lsa_lookup (struct ospf_area *area, u_int32_t type,
+                 struct in_addr id, struct in_addr adv_router)
+{
+  struct ospf *ospf = ospf_lookup();
+  assert(ospf);
+
+  switch (type)
+    {
+    case OSPF_ROUTER_LSA:
+    case OSPF_NETWORK_LSA:
+    case OSPF_SUMMARY_LSA:
+    case OSPF_ASBR_SUMMARY_LSA:
+    case OSPF_AS_NSSA_LSA:
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+      return ospf_lsdb_lookup_by_id (area->lsdb, type, id, adv_router);
+    case OSPF_AS_EXTERNAL_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      return ospf_lsdb_lookup_by_id (ospf->lsdb, type, id, adv_router);
+    default:
+      break;
+    }
+
+  return NULL;
+}
+
+struct ospf_lsa *
+ospf_lsa_lookup_by_id (struct ospf_area *area, u_int32_t type, 
+                       struct in_addr id)
+{
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+
+  switch (type)
+    {
+    case OSPF_ROUTER_LSA:
+      return ospf_lsdb_lookup_by_id (area->lsdb, type, id, id);
+    case OSPF_NETWORK_LSA:
+      for (rn = route_top (NETWORK_LSDB (area)); rn; rn = route_next (rn))
+       if ((lsa = rn->info))
+         if (IPV4_ADDR_SAME (&lsa->data->id, &id))
+           {
+             route_unlock_node (rn);
+             return lsa;
+           }
+      break;
+    case OSPF_SUMMARY_LSA:
+    case OSPF_ASBR_SUMMARY_LSA:
+      /* Currently not used. */
+      assert (1);
+      return ospf_lsdb_lookup_by_id (area->lsdb, type, id, id);
+    case OSPF_AS_EXTERNAL_LSA:
+    case OSPF_AS_NSSA_LSA:
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      /* Currently not used. */
+      break;
+    default:
+      break;
+    }
+
+  return NULL;
+}
+
+struct ospf_lsa *
+ospf_lsa_lookup_by_header (struct ospf_area *area, struct lsa_header *lsah)
+{
+  struct ospf_lsa *match;
+
+  /*
+   * Strictly speaking, the LSA-ID field for Opaque-LSAs (type-9/10/11)
+   * is redefined to have two subfields; opaque-type and opaque-id.
+   * However, it is harmless to treat the two sub fields together, as if
+   * they two were forming a unique LSA-ID.
+   */
+
+  match = ospf_lsa_lookup (area, lsah->type, lsah->id, lsah->adv_router);
+
+  if (match == NULL)
+    if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA)
+      zlog_debug ("LSA[Type%d:%s]: Lookup by header, NO MATCH",
+                lsah->type, inet_ntoa (lsah->id));
+
+  return match;
+}
+
+/* return +n, l1 is more recent.
+   return -n, l2 is more recent.
+   return 0, l1 and l2 is identical. */
+int
+ospf_lsa_more_recent (struct ospf_lsa *l1, struct ospf_lsa *l2)
+{
+  int r;
+  int x, y;
+
+  if (l1 == NULL && l2 == NULL)
+    return 0;
+  if (l1 == NULL)
+    return -1;
+  if (l2 == NULL)
+    return 1;
+
+  /* compare LS sequence number. */
+  x = (int) ntohl (l1->data->ls_seqnum);
+  y = (int) ntohl (l2->data->ls_seqnum);
+  if (x > y)
+    return 1;
+  if (x < y)
+    return -1;
+
+  /* compare LS checksum. */
+  r = ntohs (l1->data->checksum) - ntohs (l2->data->checksum);
+  if (r)
+    return r;
+
+  /* compare LS age. */
+  if (IS_LSA_MAXAGE (l1) && !IS_LSA_MAXAGE (l2))
+    return 1;
+  else if (!IS_LSA_MAXAGE (l1) && IS_LSA_MAXAGE (l2))
+    return -1;
+
+  /* compare LS age with MaxAgeDiff. */
+  if (LS_AGE (l1) - LS_AGE (l2) > OSPF_LSA_MAXAGE_DIFF)
+    return -1;
+  else if (LS_AGE (l2) - LS_AGE (l1) > OSPF_LSA_MAXAGE_DIFF)
+    return 1;
+
+  /* LSAs are identical. */
+  return 0;
+}
+
+/* If two LSAs are different, return 1, otherwise return 0. */
+int
+ospf_lsa_different (struct ospf_lsa *l1, struct ospf_lsa *l2)
+{
+  char *p1, *p2;
+  assert (l1);
+  assert (l2);
+  assert (l1->data);
+  assert (l2->data);
+
+  if (l1->data->options != l2->data->options)
+    return 1;
+
+  if (IS_LSA_MAXAGE (l1) && !IS_LSA_MAXAGE (l2))
+    return 1;
+
+  if (IS_LSA_MAXAGE (l2) && !IS_LSA_MAXAGE (l1))
+    return 1;
+
+  if (l1->data->length != l2->data->length)
+    return 1;
+
+  if (l1->data->length ==  0)
+    return 1;
+
+  if (CHECK_FLAG ((l1->flags ^ l2->flags), OSPF_LSA_RECEIVED))
+    return 1; /* May be a stale LSA in the LSBD */
+
+  assert ( ntohs(l1->data->length) > OSPF_LSA_HEADER_SIZE);
+
+  p1 = (char *) l1->data;
+  p2 = (char *) l2->data;
+
+  if (memcmp (p1 + OSPF_LSA_HEADER_SIZE, p2 + OSPF_LSA_HEADER_SIZE,
+              ntohs( l1->data->length ) - OSPF_LSA_HEADER_SIZE) != 0)
+    return 1;
+
+  return 0;
+}
+
+#ifdef ORIGINAL_CODING
+void
+ospf_lsa_flush_self_originated (struct ospf_neighbor *nbr,
+                                struct ospf_lsa *self,
+                                struct ospf_lsa *new)
+{
+  u_int32_t seqnum;
+
+  /* Adjust LS Sequence Number. */
+  seqnum = ntohl (new->data->ls_seqnum) + 1;
+  self->data->ls_seqnum = htonl (seqnum);
+
+  /* Recalculate LSA checksum. */
+  ospf_lsa_checksum (self->data);
+
+  /* Reflooding LSA. */
+  /*  RFC2328  Section 13.3
+           On non-broadcast networks, separate Link State Update
+           packets must be sent, as unicasts, to each adjacent neighbor
+           (i.e., those in state Exchange or greater).  The destination
+           IP addresses for these packets are the neighbors' IP
+           addresses.   */
+  if (nbr->oi->type == OSPF_IFTYPE_NBMA)
+    {
+      struct route_node *rn;
+      struct ospf_neighbor *onbr;
+
+      for (rn = route_top (nbr->oi->nbrs); rn; rn = route_next (rn))
+       if ((onbr = rn->info) != NULL)
+         if (onbr != nbr->oi->nbr_self && onbr->status >= NSM_Exchange)
+           ospf_ls_upd_send_lsa (onbr, self, OSPF_SEND_PACKET_DIRECT);
+    }
+  else
+  ospf_ls_upd_send_lsa (nbr, self, OSPF_SEND_PACKET_INDIRECT);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type%d:%s]: Flush self-originated LSA",
+              self->data->type, inet_ntoa (self->data->id));
+}
+#else /* ORIGINAL_CODING */
+static int
+ospf_lsa_flush_schedule (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  if (lsa == NULL || !IS_LSA_SELF (lsa))
+    return 0;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH", lsa->data->type, inet_ntoa (lsa->data->id));
+
+  /* Force given lsa's age to MaxAge. */
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+
+  switch (lsa->data->type)
+    {
+    /* Opaque wants to be notified of flushes */
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      ospf_opaque_lsa_refresh (lsa);
+      break;
+    default:
+      ospf_refresher_unregister_lsa (ospf, lsa);
+      ospf_lsa_flush (ospf, lsa);
+      break;
+    }
+
+  return 0;
+}
+
+void
+ospf_flush_self_originated_lsas_now (struct ospf *ospf)
+{
+  struct listnode *node, *nnode;
+  struct listnode *node2, *nnode2;
+  struct ospf_area *area;
+  struct ospf_interface *oi;
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+  int need_to_flush_ase = 0;
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      if ((lsa = area->router_lsa_self) != NULL)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH",
+                        lsa->data->type, inet_ntoa (lsa->data->id));
+          
+          ospf_refresher_unregister_lsa (ospf, lsa);
+          ospf_lsa_flush_area (lsa, area);
+          ospf_lsa_unlock (&area->router_lsa_self);
+          area->router_lsa_self = NULL;
+        }
+
+      for (ALL_LIST_ELEMENTS (area->oiflist, node2, nnode2, oi))
+        {
+          if ((lsa = oi->network_lsa_self) != NULL
+               &&   oi->state == ISM_DR
+               &&   oi->full_nbrs > 0)
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH",
+                            lsa->data->type, inet_ntoa (lsa->data->id));
+              
+              ospf_refresher_unregister_lsa (ospf, oi->network_lsa_self);
+              ospf_lsa_flush_area (oi->network_lsa_self, area);
+              ospf_lsa_unlock (&oi->network_lsa_self);
+              oi->network_lsa_self = NULL;
+            }
+
+          if (oi->type != OSPF_IFTYPE_VIRTUALLINK
+          &&  area->external_routing == OSPF_AREA_DEFAULT)
+            need_to_flush_ase = 1;
+        }
+
+      LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+      LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+      LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+      LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+    }
+
+  if (need_to_flush_ase)
+    {
+      LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+      LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa)
+       ospf_lsa_flush_schedule (ospf, lsa);
+    }
+
+  /*
+   * Make sure that the MaxAge LSA remover is executed immediately,
+   * without conflicting to other threads.
+   */
+  if (ospf->t_maxage != NULL)
+    {
+      OSPF_TIMER_OFF (ospf->t_maxage);
+      thread_execute (master, ospf_maxage_lsa_remover, ospf, 0);
+    }
+
+  return;
+}
+#endif /* ORIGINAL_CODING */
+
+/* If there is self-originated LSA, then return 1, otherwise return 0. */
+/* An interface-independent version of ospf_lsa_is_self_originated */
+int 
+ospf_lsa_is_self_originated (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  /* This LSA is already checked. */
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_SELF_CHECKED))
+    return IS_LSA_SELF (lsa);
+
+  /* Make sure LSA is self-checked. */
+  SET_FLAG (lsa->flags, OSPF_LSA_SELF_CHECKED);
+
+  /* AdvRouter and Router ID is the same. */
+  if (IPV4_ADDR_SAME (&lsa->data->adv_router, &ospf->router_id))
+    SET_FLAG (lsa->flags, OSPF_LSA_SELF);
+
+  /* LSA is router-LSA. */
+  else if (lsa->data->type == OSPF_ROUTER_LSA &&
+      IPV4_ADDR_SAME (&lsa->data->id, &ospf->router_id))
+    SET_FLAG (lsa->flags, OSPF_LSA_SELF);
+
+  /* LSA is network-LSA.  Compare Link ID with all interfaces. */
+  else if (lsa->data->type == OSPF_NETWORK_LSA)
+    for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+      {
+       /* Ignore virtual link. */
+        if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+         if (oi->address->family == AF_INET)
+           if (IPV4_ADDR_SAME (&lsa->data->id, &oi->address->u.prefix4))
+             {
+               /* to make it easier later */
+               SET_FLAG (lsa->flags, OSPF_LSA_SELF);
+               return IS_LSA_SELF (lsa);
+             }
+      }
+
+  return IS_LSA_SELF (lsa);
+}
+
+/* Get unique Link State ID. */
+struct in_addr
+ospf_lsa_unique_id (struct ospf *ospf,
+                   struct ospf_lsdb *lsdb, u_char type, struct prefix_ipv4 *p)
+{
+  struct ospf_lsa *lsa;
+  struct in_addr mask, id;
+
+  id = p->prefix;
+
+  /* Check existence of LSA instance. */
+  lsa = ospf_lsdb_lookup_by_id (lsdb, type, id, ospf->router_id);
+  if (lsa)
+    {
+      struct as_external_lsa *al = (struct as_external_lsa *) lsa->data;
+      if (ip_masklen (al->mask) == p->prefixlen)
+       {
+         if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+           zlog_debug ("ospf_lsa_unique_id(): "
+                      "Can't get Link State ID for %s/%d",
+                      inet_ntoa (p->prefix), p->prefixlen);
+         /*      id.s_addr = 0; */
+         id.s_addr = 0xffffffff;
+         return id;
+       }
+      /* Masklen differs, then apply wildcard mask to Link State ID. */
+      else
+       {
+         masklen2ip (p->prefixlen, &mask);
+
+         id.s_addr = p->prefix.s_addr | (~mask.s_addr);
+         lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, type,
+                                      id, ospf->router_id);
+         if (lsa)
+           {
+             if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+               zlog_debug ("ospf_lsa_unique_id(): "
+                          "Can't get Link State ID for %s/%d",
+                          inet_ntoa (p->prefix), p->prefixlen);
+             /*              id.s_addr = 0; */
+             id.s_addr = 0xffffffff;
+             return id;
+           }
+       }
+    }
+
+  return id;
+}
+
+
+#define LSA_ACTION_FLOOD_AREA 1
+#define LSA_ACTION_FLUSH_AREA 2
+
+struct lsa_action
+{
+  u_char action;
+  struct ospf_area *area;
+  struct ospf_lsa *lsa;
+};
+
+static int
+ospf_lsa_action (struct thread *t)
+{
+  struct lsa_action *data;
+
+  data = THREAD_ARG (t);
+
+  if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA)
+    zlog_debug ("LSA[Action]: Performing scheduled LSA action: %d",
+              data->action);
+
+  switch (data->action)
+    {
+    case LSA_ACTION_FLOOD_AREA:
+      ospf_flood_through_area (data->area, NULL, data->lsa);
+      break;
+    case LSA_ACTION_FLUSH_AREA:
+      ospf_lsa_flush_area (data->lsa, data->area);
+      break;
+    }
+
+  ospf_lsa_unlock (&data->lsa); /* Message */
+  XFREE (MTYPE_OSPF_MESSAGE, data);
+  return 0;
+}
+
+void
+ospf_schedule_lsa_flood_area (struct ospf_area *area, struct ospf_lsa *lsa)
+{
+  struct lsa_action *data;
+
+  data = XCALLOC (MTYPE_OSPF_MESSAGE, sizeof (struct lsa_action));
+  data->action = LSA_ACTION_FLOOD_AREA;
+  data->area = area;
+  data->lsa  = ospf_lsa_lock (lsa); /* Message / Flood area */
+
+  thread_add_event (master, ospf_lsa_action, data, 0);
+}
+
+void
+ospf_schedule_lsa_flush_area (struct ospf_area *area, struct ospf_lsa *lsa)
+{
+  struct lsa_action *data;
+
+  data = XCALLOC (MTYPE_OSPF_MESSAGE, sizeof (struct lsa_action));
+  data->action = LSA_ACTION_FLUSH_AREA;
+  data->area = area;
+  data->lsa  = ospf_lsa_lock (lsa); /* Message / Flush area */
+
+  thread_add_event (master, ospf_lsa_action, data, 0);
+}
+
+
+/* LSA Refreshment functions. */
+struct ospf_lsa *
+ospf_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  struct external_info *ei;
+  struct ospf_lsa *new = NULL;
+  assert (CHECK_FLAG (lsa->flags, OSPF_LSA_SELF));
+  assert (IS_LSA_SELF (lsa));
+  assert (lsa->lock > 0);
+
+  switch (lsa->data->type)
+    {
+      /* Router and Network LSAs are processed differently. */
+    case OSPF_ROUTER_LSA:
+      new = ospf_router_lsa_refresh (lsa);
+      break;
+    case OSPF_NETWORK_LSA: 
+      new = ospf_network_lsa_refresh (lsa);
+      break;
+    case OSPF_SUMMARY_LSA:
+      new = ospf_summary_lsa_refresh (ospf, lsa);
+      break;
+    case OSPF_ASBR_SUMMARY_LSA:
+      new = ospf_summary_asbr_lsa_refresh (ospf, lsa);
+      break;
+    case OSPF_AS_EXTERNAL_LSA:
+      /* Translated from NSSA Type-5s are refreshed when 
+       * from refresh of Type-7 - do not refresh these directly.
+       */
+      if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+        break;
+      ei = ospf_external_info_check (lsa);
+      if (ei)
+        new = ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_FORCE);
+      else
+        ospf_lsa_flush_as (ospf, lsa);
+      break;
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      new = ospf_opaque_lsa_refresh (lsa);
+      break;
+    default:
+      break;
+    }
+  return new;
+}
+
+void
+ospf_refresher_register_lsa (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  u_int16_t index, current_index;
+  
+  assert (lsa->lock > 0);
+  assert (IS_LSA_SELF (lsa));
+
+  if (lsa->refresh_list < 0)
+    {
+      int delay;
+
+      if (LS_AGE (lsa) == 0 &&
+         ntohl (lsa->data->ls_seqnum) == OSPF_INITIAL_SEQUENCE_NUMBER)
+       /* Randomize first update by  OSPF_LS_REFRESH_SHIFT factor */ 
+       delay = OSPF_LS_REFRESH_SHIFT + (random () % OSPF_LS_REFRESH_TIME);
+      else
+       /* Randomize another updates by +-OSPF_LS_REFRESH_JITTER factor */
+       delay = OSPF_LS_REFRESH_TIME - LS_AGE (lsa) - OSPF_LS_REFRESH_JITTER
+         + (random () % (2*OSPF_LS_REFRESH_JITTER)); 
+
+      if (delay < 0)
+       delay = 0;
+
+      current_index = ospf->lsa_refresh_queue.index + (quagga_time (NULL)
+                - ospf->lsa_refresher_started)/OSPF_LSA_REFRESHER_GRANULARITY;
+      
+      index = (current_index + delay/OSPF_LSA_REFRESHER_GRANULARITY)
+             % (OSPF_LSA_REFRESHER_SLOTS);
+
+      if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+       zlog_debug ("LSA[Refresh]: lsa %s with age %d added to index %d",
+                  inet_ntoa (lsa->data->id), LS_AGE (lsa), index);
+      if (!ospf->lsa_refresh_queue.qs[index])
+       ospf->lsa_refresh_queue.qs[index] = list_new ();
+      listnode_add (ospf->lsa_refresh_queue.qs[index],
+                    ospf_lsa_lock (lsa)); /* lsa_refresh_queue */
+      lsa->refresh_list = index;
+      if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+        zlog_debug ("LSA[Refresh:%s]: ospf_refresher_register_lsa(): "
+                   "setting refresh_list on lsa %p (slod %d)", 
+                   inet_ntoa (lsa->data->id), (void *)lsa, index);
+    }
+}
+
+void
+ospf_refresher_unregister_lsa (struct ospf *ospf, struct ospf_lsa *lsa)
+{
+  assert (lsa->lock > 0);
+  assert (IS_LSA_SELF (lsa));
+  if (lsa->refresh_list >= 0)
+    {
+      struct list *refresh_list = ospf->lsa_refresh_queue.qs[lsa->refresh_list];
+      listnode_delete (refresh_list, lsa);
+      if (!listcount (refresh_list))
+       {
+         list_free (refresh_list);
+         ospf->lsa_refresh_queue.qs[lsa->refresh_list] = NULL;
+       }
+      ospf_lsa_unlock (&lsa); /* lsa_refresh_queue */
+      lsa->refresh_list = -1;
+    }
+}
+
+int
+ospf_lsa_refresh_walker (struct thread *t)
+{
+  struct list *refresh_list;
+  struct listnode *node, *nnode;
+  struct ospf *ospf = THREAD_ARG (t);
+  struct ospf_lsa *lsa;
+  int i;
+  struct list *lsa_to_refresh = list_new ();
+
+  if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+    zlog_debug ("LSA[Refresh]:ospf_lsa_refresh_walker(): start");
+
+  
+  i = ospf->lsa_refresh_queue.index;
+  
+  /* Note: if clock has jumped backwards, then time change could be negative,
+     so we are careful to cast the expression to unsigned before taking
+     modulus. */
+  ospf->lsa_refresh_queue.index =
+   ((unsigned long)(ospf->lsa_refresh_queue.index +
+                   (quagga_time (NULL) - ospf->lsa_refresher_started)
+                   / OSPF_LSA_REFRESHER_GRANULARITY))
+                   % OSPF_LSA_REFRESHER_SLOTS;
+
+  if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+    zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): next index %d",
+              ospf->lsa_refresh_queue.index);
+
+  for (;i != ospf->lsa_refresh_queue.index;
+       i = (i + 1) % OSPF_LSA_REFRESHER_SLOTS)
+    {
+      if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+       zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): "
+                  "refresh index %d", i);
+
+      refresh_list = ospf->lsa_refresh_queue.qs [i];
+      
+      assert (i >= 0);
+
+      ospf->lsa_refresh_queue.qs [i] = NULL;
+
+      if (refresh_list)
+       {
+         for (ALL_LIST_ELEMENTS (refresh_list, node, nnode, lsa))
+           {
+             if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+               zlog_debug ("LSA[Refresh:%s]: ospf_lsa_refresh_walker(): "
+                          "refresh lsa %p (slot %d)",
+                          inet_ntoa (lsa->data->id), (void *)lsa, i);
+
+             assert (lsa->lock > 0);
+             list_delete_node (refresh_list, node);
+             lsa->refresh_list = -1;
+             listnode_add (lsa_to_refresh, lsa);
+           }
+         list_free (refresh_list);
+       }
+    }
+
+  ospf->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker,
+                                          ospf, ospf->lsa_refresh_interval);
+  ospf->lsa_refresher_started = quagga_time (NULL);
+
+  for (ALL_LIST_ELEMENTS (lsa_to_refresh, node, nnode, lsa))
+    {
+      ospf_lsa_refresh (ospf, lsa);
+      assert (lsa->lock > 0);
+      ospf_lsa_unlock (&lsa); /* lsa_refresh_queue & temp for lsa_to_refresh*/
+    }
+  
+  list_delete (lsa_to_refresh);
+  
+  if (IS_DEBUG_OSPF (lsa, LSA_REFRESH))
+    zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): end");
+  
+  return 0;
+}
+
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
new file mode 100644 (file)
index 0000000..3c87962
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * OSPF Link State Advertisement
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_LSA_H
+#define _ZEBRA_OSPF_LSA_H
+
+#include "stream.h"
+
+/* OSPF LSA Range definition. */
+#define OSPF_MIN_LSA           1  /* begin range here */
+#define OSPF_MAX_LSA           12
+
+/* OSPF LSA Type definition. */
+#define OSPF_UNKNOWN_LSA             0
+#define OSPF_ROUTER_LSA               1
+#define OSPF_NETWORK_LSA              2
+#define OSPF_SUMMARY_LSA              3
+#define OSPF_ASBR_SUMMARY_LSA         4
+#define OSPF_AS_EXTERNAL_LSA          5
+#define OSPF_GROUP_MEMBER_LSA        6  /* Not supported. */
+#define OSPF_AS_NSSA_LSA                     7
+#define OSPF_EXTERNAL_ATTRIBUTES_LSA  8  /* Not supported. */
+#define OSPF_OPAQUE_LINK_LSA         9
+#define OSPF_OPAQUE_AREA_LSA        10
+#define OSPF_OPAQUE_AS_LSA          11
+
+#define OSPF_LSA_HEADER_SIZE        20U
+#define OSPF_ROUTER_LSA_LINK_SIZE    12U
+#define OSPF_ROUTER_LSA_TOS_SIZE      4U
+#define OSPF_MAX_LSA_SIZE         1500U
+
+/* AS-external-LSA refresh method. */
+#define LSA_REFRESH_IF_CHANGED 0
+#define LSA_REFRESH_FORCE      1
+
+/* OSPF LSA header. */
+struct lsa_header
+{
+  u_int16_t ls_age;
+  u_char options;
+  u_char type;
+  struct in_addr id;
+  struct in_addr adv_router;
+  u_int32_t ls_seqnum;
+  u_int16_t checksum;
+  u_int16_t length;
+};
+
+/* OSPF LSA. */
+struct ospf_lsa
+{
+  /* LSA origination flag. */
+  u_char flags;
+#define OSPF_LSA_SELF            0x01
+#define OSPF_LSA_SELF_CHECKED    0x02
+#define OSPF_LSA_RECEIVED        0x04
+#define OSPF_LSA_APPROVED        0x08
+#define OSPF_LSA_DISCARD         0x10
+#define OSPF_LSA_LOCAL_XLT       0x20
+#define OSPF_LSA_PREMATURE_AGE   0x40
+#define OSPF_LSA_IN_MAXAGE       0x80
+
+  /* LSA data. */
+  struct lsa_header *data;
+
+  /* Received time stamp. */
+  struct timeval tv_recv;
+
+  /* Last time it was originated */
+  struct timeval tv_orig;
+
+  /* All of reference count, also lock to remove. */
+  int lock;
+
+  /* Flags for the SPF calculation. */
+  int stat;
+  #define LSA_SPF_NOT_EXPLORED -1
+  #define LSA_SPF_IN_SPFTREE   -2
+  /* If stat >= 0, stat is LSA position in candidates heap. */
+  
+  /* References to this LSA in neighbor retransmission lists*/
+  int retransmit_counter;
+
+  /* Area the LSA belongs to, may be NULL if AS-external-LSA. */
+  struct ospf_area *area;
+
+  /* Parent LSDB. */
+  struct ospf_lsdb *lsdb;
+
+  /* Related Route. */
+  void *route;
+
+  /* Refreshement List or Queue */
+  int refresh_list;
+  
+  /* For Type-9 Opaque-LSAs */
+  struct ospf_interface *oi;
+};
+
+/* OSPF LSA Link Type. */
+#define LSA_LINK_TYPE_POINTOPOINT      1
+#define LSA_LINK_TYPE_TRANSIT          2
+#define LSA_LINK_TYPE_STUB             3
+#define LSA_LINK_TYPE_VIRTUALLINK      4
+
+/* OSPF Router LSA Flag. */
+#define ROUTER_LSA_BORDER             0x01 /* The router is an ABR */
+#define ROUTER_LSA_EXTERNAL           0x02 /* The router is an ASBR */
+#define ROUTER_LSA_VIRTUAL            0x04 /* The router has a VL in this area */
+#define ROUTER_LSA_NT                 0x10 /* The routers always translates Type-7 */
+#define ROUTER_LSA_SHORTCUT           0x20 /* Shortcut-ABR specific flag */
+
+#define IS_ROUTER_LSA_VIRTUAL(x)       ((x)->flags & ROUTER_LSA_VIRTUAL)
+#define IS_ROUTER_LSA_EXTERNAL(x)      ((x)->flags & ROUTER_LSA_EXTERNAL)
+#define IS_ROUTER_LSA_BORDER(x)               ((x)->flags & ROUTER_LSA_BORDER)
+#define IS_ROUTER_LSA_SHORTCUT(x)      ((x)->flags & ROUTER_LSA_SHORTCUT)
+#define IS_ROUTER_LSA_NT(x)            ((x)->flags & ROUTER_LSA_NT)
+
+/* OSPF Router-LSA Link information. */
+struct router_lsa_link
+{
+  struct in_addr link_id;
+  struct in_addr link_data;
+  struct
+  {
+    u_char type;
+    u_char tos_count;
+    u_int16_t metric;
+  } m[1];
+};
+
+/* OSPF Router-LSAs structure. */
+#define OSPF_ROUTER_LSA_MIN_SIZE                   4U /* w/0 link descriptors */
+/* There is an edge case, when number of links in a Router-LSA may be 0 without
+   breaking the specification. A router, which has no other links to backbone
+   area besides one virtual link, will not put any VL descriptor blocks into
+   the Router-LSA generated for area 0 until a full adjacency over the VL is
+   reached (RFC2328 12.4.1.3). In this case the Router-LSA initially received
+   by the other end of the VL will have 0 link descriptor blocks, but soon will
+   be replaced with the next revision having 1 descriptor block. */
+struct router_lsa
+{
+  struct lsa_header header;
+  u_char flags;
+  u_char zero;
+  u_int16_t links;
+  struct
+  {
+    struct in_addr link_id;
+    struct in_addr link_data;
+    u_char type;
+    u_char tos;
+    u_int16_t metric;
+  } link[1];
+};
+
+/* OSPF Network-LSAs structure. */
+#define OSPF_NETWORK_LSA_MIN_SIZE                  8U /* w/1 router-ID */
+struct network_lsa
+{
+  struct lsa_header header;
+  struct in_addr mask;
+  struct in_addr routers[1];
+};
+
+/* OSPF Summary-LSAs structure. */
+#define OSPF_SUMMARY_LSA_MIN_SIZE                  8U /* w/1 TOS metric block */
+struct summary_lsa
+{
+  struct lsa_header header;
+  struct in_addr mask;
+  u_char tos;
+  u_char metric[3];
+};
+
+/* OSPF AS-external-LSAs structure. */
+#define OSPF_AS_EXTERNAL_LSA_MIN_SIZE             16U /* w/1 TOS forwarding block */
+struct as_external_lsa
+{
+  struct lsa_header header;
+  struct in_addr mask;
+  struct
+  {
+    u_char tos;
+    u_char metric[3];
+    struct in_addr fwd_addr;
+    u_int32_t route_tag;
+  } e[1];
+};
+
+#include "ospfd/ospf_opaque.h"
+
+/* Macros. */
+#define GET_METRIC(x) get_metric(x)
+#define IS_EXTERNAL_METRIC(x)   ((x) & 0x80)
+
+#define GET_AGE(x)     (ntohs ((x)->data->ls_age) + time (NULL) - (x)->tv_recv)
+#define LS_AGE(x)      (OSPF_LSA_MAXAGE < get_age(x) ? \
+                                           OSPF_LSA_MAXAGE : get_age(x))
+#define IS_LSA_SELF(L)          (CHECK_FLAG ((L)->flags, OSPF_LSA_SELF))
+#define IS_LSA_MAXAGE(L)        (LS_AGE ((L)) == OSPF_LSA_MAXAGE)
+
+#define OSPF_LSA_UPDATE_DELAY          2
+
+#define OSPF_LSA_UPDATE_TIMER_ON(T,F) \
+      if (!(T)) \
+        (T) = thread_add_timer (master, (F), 0, 2)
+
+/* Prototypes. */
+/* XXX: Eek, time functions, similar are in lib/thread.c */
+extern struct timeval tv_adjust (struct timeval);
+extern int tv_ceil (struct timeval);
+extern int tv_floor (struct timeval);
+extern struct timeval int2tv (int);
+extern struct timeval msec2tv (int);
+extern struct timeval tv_add (struct timeval, struct timeval);
+extern struct timeval tv_sub (struct timeval, struct timeval);
+extern int tv_cmp (struct timeval, struct timeval);
+
+extern int get_age (struct ospf_lsa *);
+extern u_int16_t ospf_lsa_checksum (struct lsa_header *);
+extern int ospf_lsa_checksum_valid (struct lsa_header *);
+extern int ospf_lsa_refresh_delay (struct ospf_lsa *);
+
+extern const char *dump_lsa_key (struct ospf_lsa *);
+extern u_int32_t lsa_seqnum_increment (struct ospf_lsa *);
+extern void lsa_header_set (struct stream *, u_char, u_char, struct in_addr,
+                    struct in_addr);
+extern struct ospf_neighbor *ospf_nbr_lookup_ptop (struct ospf_interface *);
+extern int ospf_check_nbr_status (struct ospf *);
+
+/* Prototype for LSA primitive. */
+extern struct ospf_lsa *ospf_lsa_new (void);
+extern struct ospf_lsa *ospf_lsa_dup (struct ospf_lsa *);
+extern void ospf_lsa_free (struct ospf_lsa *);
+extern struct ospf_lsa *ospf_lsa_lock (struct ospf_lsa *);
+extern void ospf_lsa_unlock (struct ospf_lsa **);
+extern void ospf_lsa_discard (struct ospf_lsa *);
+
+extern struct lsa_header *ospf_lsa_data_new (size_t);
+extern struct lsa_header *ospf_lsa_data_dup (struct lsa_header *);
+extern void ospf_lsa_data_free (struct lsa_header *);
+
+/* Prototype for various LSAs */
+extern int ospf_router_lsa_update (struct ospf *);
+extern int ospf_router_lsa_update_area (struct ospf_area *);
+
+extern void ospf_network_lsa_update (struct ospf_interface *);
+
+extern struct ospf_lsa *ospf_summary_lsa_originate (struct prefix_ipv4 *, u_int32_t,
+                                            struct ospf_area *);
+extern struct ospf_lsa *ospf_summary_asbr_lsa_originate (struct prefix_ipv4 *,
+                                                 u_int32_t,
+                                                 struct ospf_area *);
+
+extern struct ospf_lsa *ospf_lsa_install (struct ospf *,
+                                  struct ospf_interface *, struct ospf_lsa *);
+
+extern void ospf_nssa_lsa_flush (struct ospf *ospf, struct prefix_ipv4 *p);
+extern void ospf_external_lsa_flush (struct ospf *, u_char, struct prefix_ipv4 *,
+                                    ifindex_t /* , struct in_addr nexthop */);
+
+extern struct in_addr ospf_get_ip_from_ifp (struct ospf_interface *);
+
+extern struct ospf_lsa *ospf_external_lsa_originate (struct ospf *, struct external_info *);
+extern int ospf_external_lsa_originate_timer (struct thread *);
+extern int ospf_default_originate_timer (struct thread *);
+extern struct ospf_lsa *ospf_lsa_lookup (struct ospf_area *, u_int32_t,
+                                 struct in_addr, struct in_addr);
+extern struct ospf_lsa *ospf_lsa_lookup_by_id (struct ospf_area *,
+                                        u_int32_t, 
+                                        struct in_addr);
+extern struct ospf_lsa *ospf_lsa_lookup_by_header (struct ospf_area *,
+                                           struct lsa_header *);
+extern int ospf_lsa_more_recent (struct ospf_lsa *, struct ospf_lsa *);
+extern int ospf_lsa_different (struct ospf_lsa *, struct ospf_lsa *);
+extern void ospf_flush_self_originated_lsas_now (struct ospf *);
+
+extern int ospf_lsa_is_self_originated (struct ospf *, struct ospf_lsa *);
+
+extern struct ospf_lsa *ospf_lsa_lookup_by_prefix (struct ospf_lsdb *, u_char,
+                                           struct prefix_ipv4 *,
+                                           struct in_addr);
+
+extern void ospf_lsa_maxage (struct ospf *, struct ospf_lsa *);
+extern u_int32_t get_metric (u_char *);
+
+extern int ospf_lsa_maxage_walker (struct thread *);
+extern struct ospf_lsa *ospf_lsa_refresh (struct ospf *, struct ospf_lsa *);
+extern void ospf_external_lsa_refresh_default (struct ospf *);
+
+extern void ospf_external_lsa_refresh_type (struct ospf *, u_char, int);
+extern struct ospf_lsa *ospf_external_lsa_refresh (struct ospf *,
+                                                   struct ospf_lsa *,
+                                                   struct external_info *,
+                                                   int);
+extern struct in_addr ospf_lsa_unique_id (struct ospf *, struct ospf_lsdb *, u_char,
+                                  struct prefix_ipv4 *);
+extern void ospf_schedule_lsa_flood_area (struct ospf_area *, struct ospf_lsa *);
+extern void ospf_schedule_lsa_flush_area (struct ospf_area *, struct ospf_lsa *);
+
+extern void ospf_refresher_register_lsa (struct ospf *, struct ospf_lsa *);
+extern void ospf_refresher_unregister_lsa (struct ospf *, struct ospf_lsa *);
+extern int ospf_lsa_refresh_walker (struct thread *);
+
+extern void ospf_lsa_maxage_delete (struct ospf *, struct ospf_lsa *);
+
+extern void ospf_discard_from_db (struct ospf *, struct ospf_lsdb *, struct ospf_lsa*);
+extern int is_prefix_default (struct prefix_ipv4 *);
+
+extern int metric_type (struct ospf *, u_char);
+extern int metric_value (struct ospf *, u_char);
+
+extern struct in_addr ospf_get_nssa_ip (struct ospf_area *);
+extern int ospf_translated_nssa_compare (struct ospf_lsa *, struct ospf_lsa *);
+extern struct ospf_lsa *ospf_translated_nssa_refresh (struct ospf *, struct ospf_lsa *,
+                                   struct ospf_lsa *);
+extern struct ospf_lsa *ospf_translated_nssa_originate (struct ospf *, struct ospf_lsa *);
+
+#endif /* _ZEBRA_OSPF_LSA_H */
diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c
new file mode 100644 (file)
index 0000000..b92e749
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * OSPF LSDB support.
+ * Copyright (C) 1999, 2000 Alex Zinin, Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+
+struct ospf_lsdb *
+ospf_lsdb_new ()
+{
+  struct ospf_lsdb *new;
+
+  new = XCALLOC (MTYPE_OSPF_LSDB, sizeof (struct ospf_lsdb));
+  ospf_lsdb_init (new);
+
+  return new;
+}
+
+void
+ospf_lsdb_init (struct ospf_lsdb *lsdb)
+{
+  int i;
+  
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    lsdb->type[i].db = route_table_init ();
+}
+
+void
+ospf_lsdb_free (struct ospf_lsdb *lsdb)
+{
+  ospf_lsdb_cleanup (lsdb);
+  XFREE (MTYPE_OSPF_LSDB, lsdb);
+}
+
+void
+ospf_lsdb_cleanup (struct ospf_lsdb *lsdb)
+{
+  int i;
+  assert (lsdb);
+  assert (lsdb->total == 0);
+
+  ospf_lsdb_delete_all (lsdb);
+  
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    route_table_finish (lsdb->type[i].db);
+}
+
+void
+ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa)
+{
+  if (lp && lsa && lsa->data)
+    {
+      lp->family = 0;
+      lp->prefixlen = 64;
+      lp->id = lsa->data->id;
+      lp->adv_router = lsa->data->adv_router;
+    }
+}
+
+static void
+ospf_lsdb_delete_entry (struct ospf_lsdb *lsdb, struct route_node *rn)
+{
+  struct ospf_lsa *lsa = rn->info;
+  
+  if (!lsa)
+    return;
+  
+  assert (rn->table == lsdb->type[lsa->data->type].db);
+  
+  if (IS_LSA_SELF (lsa))
+    lsdb->type[lsa->data->type].count_self--;
+  lsdb->type[lsa->data->type].count--;
+  lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum);
+  lsdb->total--;
+  rn->info = NULL;
+  route_unlock_node (rn);
+#ifdef MONITOR_LSDB_CHANGE
+  if (lsdb->del_lsa_hook != NULL)
+    (* lsdb->del_lsa_hook)(lsa);
+#endif /* MONITOR_LSDB_CHANGE */
+  ospf_lsa_unlock (&lsa); /* lsdb */
+  return;
+}
+
+/* Add new LSA to lsdb. */
+void
+ospf_lsdb_add (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
+{
+  struct route_table *table;
+  struct prefix_ls lp;
+  struct route_node *rn;
+
+  table = lsdb->type[lsa->data->type].db;
+  ls_prefix_set (&lp, lsa);
+  rn = route_node_get (table, (struct prefix *)&lp);
+  
+  /* nothing to do? */
+  if (rn->info && rn->info == lsa)
+    {
+      route_unlock_node (rn);
+      return;
+    }
+  
+  /* purge old entry? */
+  if (rn->info)
+    ospf_lsdb_delete_entry (lsdb, rn);
+
+  if (IS_LSA_SELF (lsa))
+    lsdb->type[lsa->data->type].count_self++;
+  lsdb->type[lsa->data->type].count++;
+  lsdb->total++;
+
+#ifdef MONITOR_LSDB_CHANGE
+  if (lsdb->new_lsa_hook != NULL)
+    (* lsdb->new_lsa_hook)(lsa);
+#endif /* MONITOR_LSDB_CHANGE */
+  lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum);
+  rn->info = ospf_lsa_lock (lsa); /* lsdb */
+}
+
+void
+ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
+{
+  struct route_table *table;
+  struct prefix_ls lp;
+  struct route_node *rn;
+
+  if (!lsdb)
+    {
+      zlog_warn ("%s: Called with NULL LSDB", __func__);
+      if (lsa)
+        zlog_warn ("LSA[Type%d:%s]: LSA %p, lsa->lsdb %p",
+                   lsa->data->type, inet_ntoa (lsa->data->id),
+                   (void *)lsa, (void *)lsa->lsdb);
+      return;
+    }
+  
+  if (!lsa)
+    {
+      zlog_warn ("%s: Called with NULL LSA", __func__);
+      return;
+    }
+  
+  assert (lsa->data->type < OSPF_MAX_LSA);
+  table = lsdb->type[lsa->data->type].db;
+  ls_prefix_set (&lp, lsa);
+  if ((rn = route_node_lookup (table, (struct prefix *) &lp)))
+    {
+      if (rn->info == lsa)
+        ospf_lsdb_delete_entry (lsdb, rn);
+      route_unlock_node (rn); /* route_node_lookup */
+    }
+}
+
+void
+ospf_lsdb_delete_all (struct ospf_lsdb *lsdb)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  int i;
+
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      table = lsdb->type[i].db;
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if (rn->info != NULL)
+         ospf_lsdb_delete_entry (lsdb, rn);
+    }
+}
+
+void
+ospf_lsdb_clean_stat (struct ospf_lsdb *lsdb)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+  int i;
+
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      table = lsdb->type[i].db;
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if ((lsa = (rn->info)) != NULL)
+         lsa->stat = LSA_SPF_NOT_EXPLORED;
+    }
+}
+
+struct ospf_lsa *
+ospf_lsdb_lookup (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
+{
+  struct route_table *table;
+  struct prefix_ls lp;
+  struct route_node *rn;
+  struct ospf_lsa *find;
+
+  table = lsdb->type[lsa->data->type].db;
+  ls_prefix_set (&lp, lsa);
+  rn = route_node_lookup (table, (struct prefix *) &lp);
+  if (rn)
+    {
+      find = rn->info;
+      route_unlock_node (rn);
+      return find;
+    }
+  return NULL;
+}
+
+struct ospf_lsa *
+ospf_lsdb_lookup_by_id (struct ospf_lsdb *lsdb, u_char type,
+                      struct in_addr id, struct in_addr adv_router)
+{
+  struct route_table *table;
+  struct prefix_ls lp;
+  struct route_node *rn;
+  struct ospf_lsa *find;
+
+  table = lsdb->type[type].db;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = id;
+  lp.adv_router = adv_router;
+
+  rn = route_node_lookup (table, (struct prefix *) &lp);
+  if (rn)
+    {
+      find = rn->info;
+      route_unlock_node (rn);
+      return find;
+    }
+  return NULL;
+}
+
+struct ospf_lsa *
+ospf_lsdb_lookup_by_id_next (struct ospf_lsdb *lsdb, u_char type,
+                           struct in_addr id, struct in_addr adv_router,
+                           int first)
+{
+  struct route_table *table;
+  struct prefix_ls lp;
+  struct route_node *rn;
+  struct ospf_lsa *find;
+
+  table = lsdb->type[type].db;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = id;
+  lp.adv_router = adv_router;
+
+  if (first)
+      rn = route_top (table);
+  else
+    {
+      if ((rn = route_node_lookup (table, (struct prefix *) &lp)) == NULL)
+        return NULL;
+      rn = route_next (rn);
+    }
+
+  for (; rn; rn = route_next (rn))
+    if (rn->info)
+      break;
+
+  if (rn && rn->info)
+    {
+      find = rn->info;
+      route_unlock_node (rn);
+      return find;
+    }
+  return NULL;
+}
+
+unsigned long
+ospf_lsdb_count_all (struct ospf_lsdb *lsdb)
+{
+  return lsdb->total;
+}
+
+unsigned long
+ospf_lsdb_count (struct ospf_lsdb *lsdb, int type)
+{
+  return lsdb->type[type].count;
+}
+
+unsigned long
+ospf_lsdb_count_self (struct ospf_lsdb *lsdb, int type)
+{
+  return lsdb->type[type].count_self;
+}
+
+unsigned int
+ospf_lsdb_checksum (struct ospf_lsdb *lsdb, int type)
+{
+  return lsdb->type[type].checksum;
+}
+
+unsigned long
+ospf_lsdb_isempty (struct ospf_lsdb *lsdb)
+{
+  return (lsdb->total == 0);
+}
diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h
new file mode 100644 (file)
index 0000000..51ae45b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * OSPF LSDB support.
+ * Copyright (C) 1999, 2000 Alex Zinin, Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_LSDB_H
+#define _ZEBRA_OSPF_LSDB_H
+
+/* OSPF LSDB structure. */
+struct ospf_lsdb
+{
+  struct
+  {
+    unsigned long count;
+    unsigned long count_self;
+    unsigned int checksum;
+    struct route_table *db;
+  } type[OSPF_MAX_LSA];
+  unsigned long total;
+#define MONITOR_LSDB_CHANGE 1 /* XXX */
+#ifdef MONITOR_LSDB_CHANGE
+  /* Hooks for callback functions to catch every add/del event. */
+  int (* new_lsa_hook)(struct ospf_lsa *);
+  int (* del_lsa_hook)(struct ospf_lsa *);
+#endif /* MONITOR_LSDB_CHANGE */
+};
+
+/* Macros. */
+#define LSDB_LOOP(T,N,L)                                                      \
+  if ((T) != NULL)                                                            \
+  for ((N) = route_top ((T)); ((N)); ((N)) = route_next ((N)))                \
+    if (((L) = (N)->info))
+
+#define ROUTER_LSDB(A)       ((A)->lsdb->type[OSPF_ROUTER_LSA].db)
+#define NETWORK_LSDB(A)             ((A)->lsdb->type[OSPF_NETWORK_LSA].db)
+#define SUMMARY_LSDB(A)      ((A)->lsdb->type[OSPF_SUMMARY_LSA].db)
+#define ASBR_SUMMARY_LSDB(A) ((A)->lsdb->type[OSPF_ASBR_SUMMARY_LSA].db)
+#define EXTERNAL_LSDB(O)     ((O)->lsdb->type[OSPF_AS_EXTERNAL_LSA].db)
+#define NSSA_LSDB(A)         ((A)->lsdb->type[OSPF_AS_NSSA_LSA].db)
+#define OPAQUE_LINK_LSDB(A)  ((A)->lsdb->type[OSPF_OPAQUE_LINK_LSA].db)
+#define OPAQUE_AREA_LSDB(A)  ((A)->lsdb->type[OSPF_OPAQUE_AREA_LSA].db)
+#define OPAQUE_AS_LSDB(O)    ((O)->lsdb->type[OSPF_OPAQUE_AS_LSA].db)
+
+#define AREA_LSDB(A,T)       ((A)->lsdb->type[(T)].db)
+#define AS_LSDB(O,T)         ((O)->lsdb->type[(T)].db)
+
+/* OSPF LSDB related functions. */
+extern struct ospf_lsdb *ospf_lsdb_new (void);
+extern void ospf_lsdb_init (struct ospf_lsdb *);
+extern void ospf_lsdb_free (struct ospf_lsdb *);
+extern void ospf_lsdb_cleanup (struct ospf_lsdb *);
+extern void ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa);
+extern void ospf_lsdb_add (struct ospf_lsdb *, struct ospf_lsa *);
+extern void ospf_lsdb_delete (struct ospf_lsdb *, struct ospf_lsa *);
+extern void ospf_lsdb_delete_all (struct ospf_lsdb *);
+/* Set all stats to -1 (LSA_SPF_NOT_EXPLORED). */
+extern void ospf_lsdb_clean_stat (struct ospf_lsdb *lsdb);
+extern struct ospf_lsa *ospf_lsdb_lookup (struct ospf_lsdb *, struct ospf_lsa *);
+extern struct ospf_lsa *ospf_lsdb_lookup_by_id (struct ospf_lsdb *, u_char,
+                                       struct in_addr, struct in_addr);
+extern struct ospf_lsa *ospf_lsdb_lookup_by_id_next (struct ospf_lsdb *, u_char,
+                                            struct in_addr, struct in_addr,
+                                            int);
+extern unsigned long ospf_lsdb_count_all (struct ospf_lsdb *);
+extern unsigned long ospf_lsdb_count (struct ospf_lsdb *, int);
+extern unsigned long ospf_lsdb_count_self (struct ospf_lsdb *, int);
+extern unsigned int ospf_lsdb_checksum (struct ospf_lsdb *, int);
+extern unsigned long ospf_lsdb_isempty (struct ospf_lsdb *);
+
+#endif /* _ZEBRA_OSPF_LSDB_H */
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
new file mode 100644 (file)
index 0000000..32b64e2
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * OSPFd main routine.
+ *   Copyright (C) 1998, 99 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "if.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "filter.h"
+#include "plist.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "vrf.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_vty.h"
+
+/* ospfd privileges */
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_RAW,
+  ZCAP_BIND,
+  ZCAP_NET_ADMIN,
+};
+
+struct zebra_privs_t ospfd_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#if defined(VTY_GROUP)
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = array_size(_caps_p),
+  .cap_num_i = 0
+};
+
+/* Configuration filename and directory. */
+char config_default[] = SYSCONFDIR OSPF_DEFAULT_CONFIG;
+
+/* OSPFd options. */
+struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "apiserver",   no_argument,       NULL, 'a'},
+  { "version",     no_argument,       NULL, 'v'},
+  { 0 }
+};
+
+/* OSPFd program name */
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_OSPFD_PID;
+
+#ifdef SUPPORT_OSPF_API
+extern int ospf_apiserver_enable;
+#endif /* SUPPORT_OSPF_API */
+
+/* Help information display. */
+static void __attribute__ ((noreturn))
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\
+Daemon which manages OSPF.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-a. --apiserver    Enable OSPF apiserver\n\
+-v, --version      Print program version\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+  exit (status);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog (NULL, LOG_INFO, "SIGHUP received");
+}
+
+/* SIGINT / SIGTERM handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+  ospf_terminate ();
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t ospf_signals[] =
+{
+  {
+    .signal = SIGHUP,
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },  
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};
+
+/* OSPFd main routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  char *vty_addr = NULL;
+  int vty_port = OSPF_VTY_PORT;
+  int daemon_mode = 0;
+  char *config_file = NULL;
+  char *progname;
+  struct thread thread;
+  int dryrun = 0;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+#ifdef SUPPORT_OSPF_API
+  /* OSPF apiserver is disabled by default. */
+  ospf_apiserver_enable = 0;
+#endif /* SUPPORT_OSPF_API */
+
+  /* get program name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  while (1) 
+    {
+      int opt;
+
+      opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:avC", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+        case 'i':
+          pid_file = optarg;
+          break;
+       case 'z':
+         zclient_serv_path_set (optarg);
+         break;
+       case 'P':
+          /* Deal with atoi() returning 0 on failure, and ospfd not
+             listening on ospfd port... */
+          if (strcmp(optarg, "0") == 0) 
+            {
+              vty_port = 0;
+              break;
+            } 
+          vty_port = atoi (optarg);
+          if (vty_port <= 0 || vty_port > 0xffff)
+            vty_port = OSPF_VTY_PORT;
+         break;
+       case 'u':
+         ospfd_privs.user = optarg;
+         break;
+       case 'g':
+         ospfd_privs.group = optarg;
+         break;
+#ifdef SUPPORT_OSPF_API
+       case 'a':
+         ospf_apiserver_enable = 1;
+         break;
+#endif /* SUPPORT_OSPF_API */
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Invoked by a priviledged user? -- endo. */
+  if (geteuid () != 0)
+    {
+      errno = EPERM;
+      perror (progname);
+      exit (1);
+    }
+
+  zlog_default = openzlog (progname, ZLOG_OSPF,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  /* OSPF master init. */
+  ospf_master_init ();
+
+  /* Initializations. */
+  master = om->master;
+
+  /* Library inits. */
+  zprivs_init (&ospfd_privs);
+  signal_init (master, array_size(ospf_signals), ospf_signals);
+  cmd_init (1);
+  debug_init ();
+  vty_init (master);
+  memory_init ();
+  vrf_init ();
+
+  access_list_init ();
+  prefix_list_init ();
+
+  /* OSPFd inits. */
+  ospf_if_init ();
+  ospf_zebra_init (master);
+
+  /* OSPF vty inits. */
+  ospf_vty_init ();
+  ospf_vty_show_init ();
+  ospf_vty_clear_init ();
+
+  ospf_route_map_init ();
+#ifdef HAVE_SNMP
+  ospf_snmp_init ();
+#endif /* HAVE_SNMP */
+  ospf_opaque_init ();
+  
+  /* Get configuration file. */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if (dryrun)
+    return(0);
+  
+  /* Change to the daemon program. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("OSPFd daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Process id file create. */
+  pid_output (pid_file);
+
+  /* Create VTY socket */
+  vty_serv_sock (vty_addr, vty_port, OSPF_VTYSH_PATH);
+
+  /* Print banner. */
+  zlog_notice ("OSPFd %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  /* Fetch next active thread. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  return (0);
+}
+
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
new file mode 100644 (file)
index 0000000..06e63dd
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * OSPF Neighbor functions.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "command.h"
+#include "thread.h"
+#include "stream.h"
+#include "table.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_dump.h"
+
+/* Fill in the the 'key' as appropriate to retrieve the entry for nbr
+ * from the ospf_interface's nbrs table. Indexed by interface address
+ * for all cases except Virtual-link and PointToPoint interfaces, where
+ * neighbours are indexed by router-ID instead.
+ */
+static void
+ospf_nbr_key (struct ospf_interface *oi, struct ospf_neighbor *nbr,
+              struct prefix *key)
+{
+  key->family = AF_INET;
+  key->prefixlen = IPV4_MAX_BITLEN;
+
+  /* vlinks are indexed by router-id */
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+      oi->type == OSPF_IFTYPE_POINTOPOINT)
+    key->u.prefix4 = nbr->router_id;
+  else
+    key->u.prefix4 = nbr->src;
+  return;
+}
+
+struct ospf_neighbor *
+ospf_nbr_new (struct ospf_interface *oi)
+{
+  struct ospf_neighbor *nbr;
+
+  /* Allcate new neighbor. */
+  nbr = XCALLOC (MTYPE_OSPF_NEIGHBOR, sizeof (struct ospf_neighbor));
+
+  /* Relate neighbor to the interface. */
+  nbr->oi = oi;
+
+  /* Set default values. */
+  nbr->state = NSM_Down;
+
+  /* Set inheritance values. */
+  nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait);
+  nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval);
+  nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval);
+  nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval);
+  nbr->priority = -1;
+
+  /* DD flags. */
+  nbr->dd_flags = OSPF_DD_FLAG_MS|OSPF_DD_FLAG_M|OSPF_DD_FLAG_I;
+
+  /* Last received and sent DD. */
+  nbr->last_send = NULL;
+
+  nbr->nbr_nbma = NULL;
+
+  ospf_lsdb_init (&nbr->db_sum);
+  ospf_lsdb_init (&nbr->ls_rxmt);
+  ospf_lsdb_init (&nbr->ls_req);
+
+  nbr->crypt_seqnum = 0;
+
+  return nbr;
+}
+
+void
+ospf_nbr_free (struct ospf_neighbor *nbr)
+{
+  /* Free DB summary list. */
+  if (ospf_db_summary_count (nbr))
+    ospf_db_summary_clear (nbr);
+    /* ospf_db_summary_delete_all (nbr); */
+
+  /* Free ls request list. */
+  if (ospf_ls_request_count (nbr))
+    ospf_ls_request_delete_all (nbr);
+
+  /* Free retransmit list. */
+  if (ospf_ls_retransmit_count (nbr))
+    ospf_ls_retransmit_clear (nbr);
+
+  /* Cleanup LSDBs. */
+  ospf_lsdb_cleanup (&nbr->db_sum);
+  ospf_lsdb_cleanup (&nbr->ls_req);
+  ospf_lsdb_cleanup (&nbr->ls_rxmt);
+  
+  /* Clear last send packet. */
+  if (nbr->last_send)
+    ospf_packet_free (nbr->last_send);
+
+  if (nbr->nbr_nbma)
+    {
+      nbr->nbr_nbma->nbr = NULL;
+      nbr->nbr_nbma = NULL;
+    }
+
+  /* Cancel all timers. */
+  OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
+  OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
+  OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
+  OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
+
+  /* Cancel all events. *//* Thread lookup cost would be negligible. */
+  thread_cancel_event (master, nbr);
+
+  XFREE (MTYPE_OSPF_NEIGHBOR, nbr);
+}
+
+/* Delete specified OSPF neighbor from interface. */
+void
+ospf_nbr_delete (struct ospf_neighbor *nbr)
+{
+  struct ospf_interface *oi;
+  struct route_node *rn;
+  struct prefix p;
+
+  oi = nbr->oi;
+  
+  /* get appropriate prefix 'key' */
+  ospf_nbr_key (oi, nbr, &p);
+
+  rn = route_node_lookup (oi->nbrs, &p);
+  if (rn)
+    {
+      /* If lookup for a NBR succeeds, the leaf route_node could
+       * only exist because there is (or was) a nbr there.
+       * If the nbr was deleted, the leaf route_node should have
+       * lost its last refcount too, and be deleted.
+       * Therefore a looked-up leaf route_node in nbrs table
+       * should never have NULL info.
+       */
+      assert (rn->info);
+      
+      if (rn->info)
+       {
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+      else
+       zlog_info ("Can't find neighbor %s in the interface %s",
+                  inet_ntoa (nbr->src), IF_NAME (oi));
+
+      route_unlock_node (rn);
+    }
+  else
+    {
+      /*
+       * This neighbor was not found, but before we move on and
+       * free the neighbor structre, make sure that it was not
+       * indexed incorrectly and ended up in the "worng" place
+       */
+
+      /* Reverse the lookup rules */
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+         oi->type == OSPF_IFTYPE_POINTOPOINT)
+       p.u.prefix4 = nbr->src;
+      else
+       p.u.prefix4 = nbr->router_id;
+
+      rn = route_node_lookup (oi->nbrs, &p);
+      if (rn){
+       /* We found the neighbor!
+        * Now make sure it is not the exact same neighbor
+        * structure that we are about to free
+        */
+       if (nbr == rn->info){
+         /* Same neighbor, drop the reference to it */
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+       route_unlock_node (rn);
+      }
+    }
+
+  /* Free ospf_neighbor structure. */
+  ospf_nbr_free (nbr);
+}
+
+/* Check myself is in the neighbor list. */
+int
+ospf_nbr_bidirectional (struct in_addr *router_id,
+                       struct in_addr *neighbors, int size)
+{
+  int i;
+  int max;
+
+  max = size / sizeof (struct in_addr);
+
+  for (i = 0; i < max; i ++)
+    if (IPV4_ADDR_SAME (router_id, &neighbors[i]))
+      return 1;
+
+  return 0;
+}
+
+/* reset nbr_self */
+void
+ospf_nbr_self_reset (struct ospf_interface *oi)
+{
+  if (oi->nbr_self)
+    ospf_nbr_delete (oi->nbr_self);
+
+  oi->nbr_self = ospf_nbr_new (oi);
+  ospf_nbr_add_self (oi);
+}
+
+/* Add self to nbr list. */
+void
+ospf_nbr_add_self (struct ospf_interface *oi)
+{
+  struct prefix p;
+  struct route_node *rn;
+
+  /* Initial state */
+  oi->nbr_self->address = *oi->address;
+  oi->nbr_self->priority = OSPF_IF_PARAM (oi, priority);
+  oi->nbr_self->router_id = oi->ospf->router_id;
+  oi->nbr_self->src = oi->address->u.prefix4;
+  oi->nbr_self->state = NSM_TwoWay;
+  
+  switch (oi->area->external_routing)
+    {
+      case OSPF_AREA_DEFAULT:
+        SET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+        break;
+      case OSPF_AREA_STUB:
+        UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+        break;
+      case OSPF_AREA_NSSA:
+        UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+        SET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP);
+        break;
+    }
+  
+  /* Add nbr_self to nbrs table */
+  ospf_nbr_key (oi, oi->nbr_self, &p);
+  
+  rn = route_node_get (oi->nbrs, &p);
+  if (rn->info)
+    {
+      /* There is already pseudo neighbor. */
+      assert (oi->nbr_self == rn->info);
+      route_unlock_node (rn);
+    }
+  else
+    rn->info = oi->nbr_self;
+}
+
+/* Get neighbor count by status.
+   Specify status = 0, get all neighbor other than myself. */
+int
+ospf_nbr_count (struct ospf_interface *oi, int state)
+{
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+  int count = 0;
+
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+       if (state == 0 || nbr->state == state)
+         count++;
+
+  return count;
+}
+
+int
+ospf_nbr_count_opaque_capable (struct ospf_interface *oi)
+{
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+  int count = 0;
+
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+       if (nbr->state == NSM_Full)
+         if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+           count++;
+
+  return count;
+}
+
+/* lookup nbr by address - use this only if you know you must
+ * otherwise use the ospf_nbr_lookup() wrapper, which deals
+ * with virtual link and PointToPoint neighbours
+ */
+struct ospf_neighbor *
+ospf_nbr_lookup_by_addr (struct route_table *nbrs,
+                        struct in_addr *addr)
+{
+  struct prefix p;
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.u.prefix4 = *addr;
+
+  rn = route_node_lookup (nbrs, &p);
+  if (! rn)
+    return NULL;
+  
+  /* See comment in ospf_nbr_delete */
+  assert (rn->info);
+
+  if (rn->info == NULL)
+    {
+      route_unlock_node (rn);
+      return NULL;
+    }
+
+  nbr = (struct ospf_neighbor *) rn->info;
+  route_unlock_node (rn);
+
+  return nbr;
+}
+
+struct ospf_neighbor *
+ospf_nbr_lookup_by_routerid (struct route_table *nbrs,
+                            struct in_addr *id)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  for (rn = route_top (nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info) != NULL)
+      if (IPV4_ADDR_SAME (&nbr->router_id, id))
+       {
+         route_unlock_node(rn);
+         return nbr;
+       }
+
+  return NULL;
+}
+
+void
+ospf_renegotiate_optional_capabilities (struct ospf *top)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+  struct route_table *nbrs;
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  /* At first, flush self-originated LSAs from routing domain. */
+  ospf_flush_self_originated_lsas_now (top);
+
+  /* Revert all neighbor status to ExStart. */
+  for (ALL_LIST_ELEMENTS_RO (top->oiflist, node, oi))
+    {
+      if ((nbrs = oi->nbrs) == NULL)
+        continue;
+
+      for (rn = route_top (nbrs); rn; rn = route_next (rn))
+        {
+          if ((nbr = rn->info) == NULL || nbr == oi->nbr_self)
+            continue;
+
+          if (nbr->state < NSM_ExStart)
+            continue;
+
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("Renegotiate optional capabilities with neighbor(%s)", inet_ntoa (nbr->router_id));
+
+          OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+        }
+    }
+
+  return;
+}
+
+
+struct ospf_neighbor *
+ospf_nbr_lookup (struct ospf_interface *oi, struct ip *iph,
+                 struct ospf_header *ospfh)
+{
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+      oi->type == OSPF_IFTYPE_POINTOPOINT)
+    return (ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id));
+  else
+    return (ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src));
+}
+
+static struct ospf_neighbor *
+ospf_nbr_add (struct ospf_interface *oi, struct ospf_header *ospfh,
+              struct prefix *p)
+{
+  struct ospf_neighbor *nbr;
+  
+  nbr = ospf_nbr_new (oi);
+  nbr->state = NSM_Down;
+  nbr->src = p->u.prefix4;
+  memcpy (&nbr->address, p, sizeof (struct prefix));
+
+  nbr->nbr_nbma = NULL;
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    {
+      struct ospf_nbr_nbma *nbr_nbma;
+      struct listnode *node;
+
+      for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, node, nbr_nbma))
+        {
+          if (IPV4_ADDR_SAME(&nbr_nbma->addr, &nbr->src))
+            {
+              nbr_nbma->nbr = nbr;
+              nbr->nbr_nbma = nbr_nbma;
+
+              if (nbr_nbma->t_poll)
+                OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll);
+
+              nbr->state_change = nbr_nbma->state_change + 1;
+            }
+        }
+    }
+      
+  /* New nbr, save the crypto sequence number if necessary */
+  if (ntohs (ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC)
+    nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("NSM[%s:%s]: start", IF_NAME (nbr->oi),
+               inet_ntoa (nbr->router_id));
+  
+  return nbr;
+}
+
+struct ospf_neighbor *
+ospf_nbr_get (struct ospf_interface *oi, struct ospf_header *ospfh,
+              struct ip *iph, struct prefix *p)
+{
+  struct route_node *rn;
+  struct prefix key;
+  struct ospf_neighbor *nbr;
+  
+  key.family = AF_INET;
+  key.prefixlen = IPV4_MAX_BITLEN;
+
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK ||
+      oi->type == OSPF_IFTYPE_POINTOPOINT)
+    key.u.prefix4 = ospfh->router_id;/* index vlink and ptp nbrs by router-id */
+  else
+    key.u.prefix4 = iph->ip_src;
+
+  rn = route_node_get (oi->nbrs, &key);
+  if (rn->info)
+    {
+      route_unlock_node (rn);
+      nbr = rn->info;
+      
+      if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt)
+        {
+          nbr->src = iph->ip_src;
+          memcpy (&nbr->address, p, sizeof (struct prefix));
+        }
+    }
+  else
+    {
+      rn->info = nbr = ospf_nbr_add (oi, ospfh, p);
+    }
+  
+  nbr->router_id = ospfh->router_id;
+
+  return nbr;
+}
diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h
new file mode 100644 (file)
index 0000000..38f8cb5
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * OSPF Neighbor functions.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ * 
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_NEIGHBOR_H
+#define _ZEBRA_OSPF_NEIGHBOR_H
+
+#include <ospfd/ospf_packet.h>
+
+/* Neighbor Data Structure */
+struct ospf_neighbor
+{
+  /* This neighbor's parent ospf interface. */
+  struct ospf_interface *oi;
+
+  /* OSPF neighbor Information */
+  u_char state;                                /* NSM status. */
+  u_char dd_flags;                     /* DD bit flags. */
+  u_int32_t dd_seqnum;                 /* DD Sequence Number. */
+
+  /* Neighbor Information from Hello. */
+  struct prefix address;               /* Neighbor Interface Address. */
+
+  struct in_addr src;                  /* Src address. */
+  struct in_addr router_id;            /* Router ID. */
+  u_char options;                      /* Options. */
+  int priority;                                /* Router Priority. */
+  struct in_addr d_router;             /* Designated Router. */
+  struct in_addr bd_router;            /* Backup Designated Router. */
+
+  /* Last sent Database Description packet. */
+  struct ospf_packet *last_send;
+  /* Timestemp when last Database Description packet was sent */
+  struct timeval last_send_ts;
+
+  /* Last received Databse Description packet. */
+  struct
+  {
+    u_char options;
+    u_char flags;
+    u_int32_t dd_seqnum;
+  } last_recv;
+
+  /* LSA data. */
+  struct ospf_lsdb ls_rxmt;
+  struct ospf_lsdb db_sum;
+  struct ospf_lsdb ls_req;
+  struct ospf_lsa *ls_req_last;
+
+  u_int32_t crypt_seqnum;           /* Cryptographic Sequence Number. */
+
+  /* Timer values. */
+  u_int32_t v_inactivity;
+  u_int32_t v_db_desc;
+  u_int32_t v_ls_req;
+  u_int32_t v_ls_upd;
+
+  /* Threads. */
+  struct thread *t_inactivity;
+  struct thread *t_db_desc;
+  struct thread *t_ls_req;
+  struct thread *t_ls_upd;
+  struct thread *t_hello_reply;
+
+  /* NBMA configured neighbour */
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  /* Statistics */
+  struct timeval ts_last_progress;  /* last advance of NSM            */
+  struct timeval ts_last_regress;   /* last regressive NSM change     */
+  const char *last_regress_str;     /* Event which last regressed NSM */
+  u_int32_t state_change;           /* NSM state change counter       */
+};
+
+/* Macros. */
+#define NBR_IS_DR(n)   IPV4_ADDR_SAME (&n->address.u.prefix4, &n->d_router)
+#define NBR_IS_BDR(n)   IPV4_ADDR_SAME (&n->address.u.prefix4, &n->bd_router)
+
+/* Prototypes. */
+extern struct ospf_neighbor *ospf_nbr_new (struct ospf_interface *);
+extern void ospf_nbr_free (struct ospf_neighbor *);
+extern void ospf_nbr_delete (struct ospf_neighbor *);
+extern int ospf_nbr_bidirectional (struct in_addr *, struct in_addr *, int);
+extern void ospf_nbr_self_reset (struct ospf_interface *);
+extern void ospf_nbr_add_self (struct ospf_interface *);
+extern int ospf_nbr_count (struct ospf_interface *, int);
+extern int ospf_nbr_count_opaque_capable (struct ospf_interface *);
+extern struct ospf_neighbor *ospf_nbr_get (struct ospf_interface *,
+                                          struct ospf_header *,
+                                          struct ip *, struct prefix *);
+extern struct ospf_neighbor *ospf_nbr_lookup (struct ospf_interface *,
+                                             struct ip *,
+                                             struct ospf_header *);
+extern struct ospf_neighbor *ospf_nbr_lookup_by_addr (struct route_table *,
+                                                     struct in_addr *);
+extern struct ospf_neighbor *ospf_nbr_lookup_by_routerid (struct route_table
+                                                         *,
+                                                         struct in_addr *);
+extern void ospf_renegotiate_optional_capabilities (struct ospf *top);
+
+#endif /* _ZEBRA_OSPF_NEIGHBOR_H */
diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c
new file mode 100644 (file)
index 0000000..02ddf92
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * OSPF network related functions
+ *   Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "sockunion.h"
+#include "log.h"
+#include "sockopt.h"
+#include "privs.h"
+
+extern struct zebra_privs_t ospfd_privs;
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_dump.h"
+
+
+
+/* Join to the OSPF ALL SPF ROUTERS multicast group. */
+int
+ospf_if_add_allspfrouters (struct ospf *top, struct prefix *p,
+                          ifindex_t ifindex)
+{
+  int ret;
+  
+  ret = setsockopt_ipv4_multicast (top->fd, IP_ADD_MEMBERSHIP,
+                                   htonl (OSPF_ALLSPFROUTERS),
+                                   ifindex);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, "
+              "ifindex %u, AllSPFRouters): %s; perhaps a kernel limit "
+              "on # of multicast group memberships has been exceeded?",
+               top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno));
+  else
+    zlog_debug ("interface %s [%u] join AllSPFRouters Multicast group.",
+              inet_ntoa (p->u.prefix4), ifindex);
+
+  return ret;
+}
+
+int
+ospf_if_drop_allspfrouters (struct ospf *top, struct prefix *p,
+                           ifindex_t ifindex)
+{
+  int ret;
+
+  ret = setsockopt_ipv4_multicast (top->fd, IP_DROP_MEMBERSHIP,
+                                   htonl (OSPF_ALLSPFROUTERS),
+                                   ifindex);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, "
+              "ifindex %u, AllSPFRouters): %s",
+               top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno));
+  else
+    zlog_debug ("interface %s [%u] leave AllSPFRouters Multicast group.",
+               inet_ntoa (p->u.prefix4), ifindex);
+
+  return ret;
+}
+
+/* Join to the OSPF ALL Designated ROUTERS multicast group. */
+int
+ospf_if_add_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex)
+{
+  int ret;
+
+  ret = setsockopt_ipv4_multicast (top->fd, IP_ADD_MEMBERSHIP,
+                                   htonl (OSPF_ALLDROUTERS),
+                                   ifindex);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, "
+              "ifindex %u, AllDRouters): %s; perhaps a kernel limit "
+              "on # of multicast group memberships has been exceeded?",
+               top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno));
+  else
+    zlog_debug ("interface %s [%u] join AllDRouters Multicast group.",
+               inet_ntoa (p->u.prefix4), ifindex);
+
+  return ret;
+}
+
+int
+ospf_if_drop_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex)
+{
+  int ret;
+
+  ret = setsockopt_ipv4_multicast (top->fd, IP_DROP_MEMBERSHIP,
+                                   htonl (OSPF_ALLDROUTERS),
+                                   ifindex);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, "
+              "ifindex %u, AllDRouters): %s",
+               top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno));
+  else
+    zlog_debug ("interface %s [%u] leave AllDRouters Multicast group.",
+               inet_ntoa (p->u.prefix4), ifindex);
+
+  return ret;
+}
+
+int
+ospf_if_ipmulticast (struct ospf *top, struct prefix *p, ifindex_t ifindex)
+{
+  u_char val;
+  int ret, len;
+  
+  val = 0;
+  len = sizeof (val);
+  
+  /* Prevent receiving self-origined multicast packets. */
+  ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, len);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s",
+              top->fd, safe_strerror(errno));
+  
+  /* Explicitly set multicast ttl to 1 -- endo. */
+  val = 1;
+  ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s",
+              top->fd, safe_strerror (errno));
+
+  ret = setsockopt_ipv4_multicast_if (top->fd, ifindex);
+  if (ret < 0)
+    zlog_warn("can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, "
+             "ifindex %u): %s",
+             top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno));
+
+  return ret;
+}
+
+int
+ospf_sock_init (void)
+{
+  int ospf_sock;
+  int ret, hincl = 1;
+
+  if ( ospfd_privs.change (ZPRIVS_RAISE) )
+    zlog_err ("ospf_sock_init: could not raise privs, %s",
+               safe_strerror (errno) );
+    
+  ospf_sock = socket (AF_INET, SOCK_RAW, IPPROTO_OSPFIGP);
+  if (ospf_sock < 0)
+    {
+      int save_errno = errno;
+      if ( ospfd_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("ospf_sock_init: could not lower privs, %s",
+                   safe_strerror (errno) );
+      zlog_err ("ospf_read_sock_init: socket: %s", safe_strerror (save_errno));
+      exit(1);
+    }
+    
+#ifdef IP_HDRINCL
+  /* we will include IP header with packet */
+  ret = setsockopt (ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof (hincl));
+  if (ret < 0)
+    {
+      int save_errno = errno;
+      if ( ospfd_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("ospf_sock_init: could not lower privs, %s",
+                   safe_strerror (errno) );
+      zlog_warn ("Can't set IP_HDRINCL option for fd %d: %s",
+                ospf_sock, safe_strerror(save_errno));
+    }
+#elif defined (IPTOS_PREC_INTERNETCONTROL)
+#warning "IP_HDRINCL not available on this system"
+#warning "using IPTOS_PREC_INTERNETCONTROL"
+  ret = setsockopt_ipv4_tos(ospf_sock, IPTOS_PREC_INTERNETCONTROL);
+  if (ret < 0)
+    {
+      int save_errno = errno;
+      if ( ospfd_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("ospf_sock_init: could not lower privs, %s",
+                   safe_strerror (errno) );
+      zlog_warn ("can't set sockopt IP_TOS %d to socket %d: %s",
+                tos, ospf_sock, safe_strerror(save_errno));
+      close (ospf_sock);       /* Prevent sd leak. */
+      return ret;
+    }
+#else /* !IPTOS_PREC_INTERNETCONTROL */
+#warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL"
+  zlog_warn ("IP_HDRINCL option not available");
+#endif /* IP_HDRINCL */
+
+  ret = setsockopt_ifindex (AF_INET, ospf_sock, 1);
+
+  if (ret < 0)
+     zlog_warn ("Can't set pktinfo option for fd %d", ospf_sock);
+
+  if (ospfd_privs.change (ZPRIVS_LOWER))
+    {
+      zlog_err ("ospf_sock_init: could not lower privs, %s",
+               safe_strerror (errno) );
+    }
+  return ospf_sock;
+}
+
+void
+ospf_adjust_sndbuflen (struct ospf * ospf, unsigned int buflen)
+{
+  int ret, newbuflen;
+  /* Check if any work has to be done at all. */
+  if (ospf->maxsndbuflen >= buflen)
+    return;
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug ("%s: adjusting OSPF send buffer size to %d",
+      __func__, buflen);
+  if (ospfd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs, %s", __func__,
+      safe_strerror (errno));
+  /* Now we try to set SO_SNDBUF to what our caller has requested
+   * (the MTU of a newly added interface). However, if the OS has
+   * truncated the actual buffer size to somewhat less size, try
+   * to detect it and update our records appropriately. The OS
+   * may allocate more buffer space, than requested, this isn't
+   * a error.
+   */
+  ret = setsockopt_so_sendbuf (ospf->fd, buflen);
+  newbuflen = getsockopt_so_sendbuf (ospf->fd);
+  if (ret < 0 || newbuflen < 0 || newbuflen < (int) buflen)
+    zlog_warn ("%s: tried to set SO_SNDBUF to %u, but got %d",
+      __func__, buflen, newbuflen);
+  if (newbuflen >= 0)
+    ospf->maxsndbuflen = (unsigned int)newbuflen;
+  else
+    zlog_warn ("%s: failed to get SO_SNDBUF", __func__);
+  if (ospfd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs, %s", __func__,
+      safe_strerror (errno));
+}
diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h
new file mode 100644 (file)
index 0000000..8257adb
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * OSPF network related functions.
+ *   Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_NETWORK_H
+#define _ZEBRA_OSPF_NETWORK_H
+
+/* Prototypes. */
+extern int ospf_if_add_allspfrouters (struct ospf *, struct prefix *,
+                                     ifindex_t);
+extern int ospf_if_drop_allspfrouters (struct ospf *, struct prefix *,
+                                      ifindex_t);
+extern int ospf_if_add_alldrouters (struct ospf *, struct prefix *,
+                                   ifindex_t);
+extern int ospf_if_drop_alldrouters (struct ospf *, struct prefix *,
+                                    ifindex_t);
+extern int ospf_if_ipmulticast (struct ospf *, struct prefix *, ifindex_t);
+extern int ospf_sock_init (void);
+extern void ospf_adjust_sndbuflen (struct ospf *, unsigned int);
+
+#endif /* _ZEBRA_OSPF_NETWORK_H */
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
new file mode 100644 (file)
index 0000000..5c01a58
--- /dev/null
@@ -0,0 +1,867 @@
+/*
+ * OSPF version 2  Neighbor State Machine
+ * From RFC2328 [OSPF Version 2]
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "stream.h"
+#include "table.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_snmp.h"
+
+static void nsm_clear_adj (struct ospf_neighbor *);
+
+/* OSPF NSM Timer functions. */
+static int
+ospf_inactivity_timer (struct thread *thread)
+{
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  nbr->t_inactivity = NULL;
+
+  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Inactivity timer expire)",
+         IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
+
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_InactivityTimer);
+
+  return 0;
+}
+
+static int
+ospf_db_desc_timer (struct thread *thread)
+{
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  nbr->t_db_desc = NULL;
+
+  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (DD Retransmit timer expire)",
+         IF_NAME (nbr->oi), inet_ntoa (nbr->src));
+
+  /* resent last send DD packet. */
+  assert (nbr->last_send);
+  ospf_db_desc_resend (nbr);
+
+  /* DD Retransmit timer set. */
+  OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc);
+
+  return 0;
+}
+
+/* Hook function called after ospf NSM event is occured.
+ *
+ * Set/clear any timers whose condition is implicit to the neighbour
+ * state. There may be other timers which are set/unset according to other
+ * state.
+ *
+ * We rely on this function to properly clear timers in lower states,
+ * particularly before deleting a neighbour.
+ */
+static void
+nsm_timer_set (struct ospf_neighbor *nbr)
+{
+  switch (nbr->state)
+    {
+    case NSM_Deleted:
+    case NSM_Down:
+      OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
+      OSPF_NSM_TIMER_OFF (nbr->t_hello_reply);
+    case NSM_Attempt:
+    case NSM_Init:
+    case NSM_TwoWay:
+      OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
+      OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
+      OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
+      break;
+    case NSM_ExStart:
+      OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc);
+      OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
+      OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
+      break;
+    case NSM_Exchange:
+      OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd);
+      if (!IS_SET_DD_MS (nbr->dd_flags))      
+       OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
+      break;
+    case NSM_Loading:
+    case NSM_Full:
+    default:
+      OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
+      break;
+    }
+}
+
+/* 10.4 of RFC2328, indicate whether an adjacency is appropriate with
+ * the given neighbour
+ */
+static int
+nsm_should_adj (struct ospf_neighbor *nbr)
+{
+  struct ospf_interface *oi = nbr->oi;
+
+      /* These network types must always form adjacencies. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT
+      || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
+      || oi->type == OSPF_IFTYPE_VIRTUALLINK
+      /* Router itself is the DRouter or the BDRouter. */
+      || IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))
+      || IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi))
+      /* Neighboring Router is the DRouter or the BDRouter. */
+      || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &DR (oi))
+      || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &BDR (oi)))
+    return 1;
+
+  return 0;
+}
+
+/* OSPF NSM functions. */
+static int
+nsm_packet_received (struct ospf_neighbor *nbr)
+{
+  /* Start or Restart Inactivity Timer. */
+  OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
+  
+  OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer,
+                    nbr->v_inactivity);
+
+  if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma)
+    OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll);
+
+  return 0;
+}
+
+static int
+nsm_start (struct ospf_neighbor *nbr)
+{
+  if (nbr->nbr_nbma)
+      OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll);
+
+  OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
+  
+  OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer,
+                     nbr->v_inactivity);
+
+  return 0;
+}
+
+static int
+nsm_twoway_received (struct ospf_neighbor *nbr)
+{
+  return (nsm_should_adj (nbr) ? NSM_ExStart : NSM_TwoWay);
+}
+
+int
+ospf_db_summary_count (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_count_all (&nbr->db_sum);
+}
+
+int
+ospf_db_summary_isempty (struct ospf_neighbor *nbr)
+{
+  return ospf_lsdb_isempty (&nbr->db_sum);
+}
+
+static int
+ospf_db_summary_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      /* Exclude type-9 LSAs that does not have the same "oi" with "nbr". */
+      if (nbr->oi && ospf_if_exists (lsa->oi) != nbr->oi)
+          return 0;
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      /*
+       * It is assured by the caller function "nsm_negotiation_done()"
+       * that every given LSA belongs to the same area with "nbr".
+       */
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+    default:
+      break;
+    }
+
+  /* Stay away from any Local Translated Type-7 LSAs */
+  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+    return 0;
+
+  if (IS_LSA_MAXAGE (lsa))
+    ospf_ls_retransmit_add (nbr, lsa);                      
+  else 
+    ospf_lsdb_add (&nbr->db_sum, lsa);
+
+  return 0;
+}
+
+void
+ospf_db_summary_clear (struct ospf_neighbor *nbr)
+{
+  struct ospf_lsdb *lsdb;
+  int i;
+
+  lsdb = &nbr->db_sum;
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      struct route_table *table = lsdb->type[i].db;
+      struct route_node *rn;
+
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if (rn->info)
+         ospf_lsdb_delete (&nbr->db_sum, rn->info);
+    }
+}
+
+
+
+/* The area link state database consists of the router-LSAs,
+   network-LSAs and summary-LSAs contained in the area structure,
+   along with the AS-external-LSAs contained in the global structure.
+   AS-external-LSAs are omitted from a virtual neighbor's Database
+   summary list.  AS-external-LSAs are omitted from the Database
+   summary list if the area has been configured as a stub. */
+static int
+nsm_negotiation_done (struct ospf_neighbor *nbr)
+{
+  struct ospf_area *area = nbr->oi->area;
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+
+  LSDB_LOOP (ROUTER_LSDB (area), rn, lsa)
+    ospf_db_summary_add (nbr, lsa);
+  LSDB_LOOP (NETWORK_LSDB (area), rn, lsa)
+    ospf_db_summary_add (nbr, lsa);
+  LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+    ospf_db_summary_add (nbr, lsa);
+  LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+    ospf_db_summary_add (nbr, lsa);
+
+  /* Process only if the neighbor is opaque capable. */
+  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+    {
+      LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+       ospf_db_summary_add (nbr, lsa);
+      LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+       ospf_db_summary_add (nbr, lsa);
+    }
+
+  if (CHECK_FLAG (nbr->options, OSPF_OPTION_NP))
+    {
+      LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
+       ospf_db_summary_add (nbr, lsa);
+    }
+
+  if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
+      && area->external_routing == OSPF_AREA_DEFAULT)
+    LSDB_LOOP (EXTERNAL_LSDB (nbr->oi->ospf), rn, lsa)
+      ospf_db_summary_add (nbr, lsa);
+
+  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)
+      && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
+         && area->external_routing == OSPF_AREA_DEFAULT))
+    LSDB_LOOP (OPAQUE_AS_LSDB (nbr->oi->ospf), rn, lsa)
+      ospf_db_summary_add (nbr, lsa);
+
+  /* Send Link State Request. */
+  if (nbr->t_ls_req == NULL)
+    ospf_ls_req_send (nbr);
+
+  return 0;
+}
+
+static int
+nsm_exchange_done (struct ospf_neighbor *nbr)
+{
+  if (ospf_ls_request_isempty (nbr))
+    return NSM_Full;
+  
+  if (nbr->t_ls_req == NULL)
+    ospf_ls_req_send (nbr);
+  
+  return NSM_Loading;
+}
+
+static int
+nsm_adj_ok (struct ospf_neighbor *nbr)
+{
+  int next_state = nbr->state;
+  int adj = nsm_should_adj (nbr);
+
+  if (nbr->state == NSM_TwoWay && adj == 1)
+    next_state = NSM_ExStart;
+  else if (nbr->state >= NSM_ExStart && adj == 0)
+    next_state = NSM_TwoWay;
+
+  return next_state;
+}
+
+/* Clear adjacency related state for a neighbour, intended where nbr
+ * transitions from > ExStart (i.e. a Full or forming adjacency)
+ * to <= ExStart.
+ */
+static void
+nsm_clear_adj (struct ospf_neighbor *nbr)
+{
+  /* Clear Database Summary list. */
+  if (!ospf_db_summary_isempty (nbr))
+    ospf_db_summary_clear (nbr);
+
+  /* Clear Link State Request list. */
+  if (!ospf_ls_request_isempty (nbr))
+    ospf_ls_request_delete_all (nbr);
+
+  /* Clear Link State Retransmission list. */
+  if (!ospf_ls_retransmit_isempty (nbr))
+    ospf_ls_retransmit_clear (nbr);
+
+  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+    UNSET_FLAG (nbr->options, OSPF_OPTION_O);
+}
+
+static int
+nsm_kill_nbr (struct ospf_neighbor *nbr)
+{
+  /* killing nbr_self is invalid */
+  if (nbr == nbr->oi->nbr_self)
+    {
+      assert (nbr != nbr->oi->nbr_self);
+      return 0;
+    }
+  
+  if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL)
+    {
+      struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma;
+
+      nbr_nbma->nbr = NULL;
+      nbr_nbma->state_change = nbr->state_change;
+
+      nbr->nbr_nbma = NULL;
+
+      OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer,
+                         nbr_nbma->v_poll);
+
+      if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
+       zlog_debug ("NSM[%s:%s]: Down (PollIntervalTimer scheduled)",
+                  IF_NAME (nbr->oi), inet_ntoa (nbr->address.u.prefix4));  
+    }
+
+  return 0;
+}
+
+/* Neighbor State Machine */
+struct {
+  int (*func) (struct ospf_neighbor *);
+  int next_state;
+} NSM [OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] =
+{
+  {
+    /* DependUpon: dummy state. */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { NULL,                    NSM_DependUpon }, /* PacketReceived    */
+    { NULL,                    NSM_DependUpon }, /* Start             */
+    { NULL,                    NSM_DependUpon }, /* 2-WayReceived     */
+    { NULL,                    NSM_DependUpon }, /* NegotiationDone   */
+    { NULL,                    NSM_DependUpon }, /* ExchangeDone      */
+    { NULL,                    NSM_DependUpon }, /* BadLSReq          */
+    { NULL,                    NSM_DependUpon }, /* LoadingDone       */
+    { NULL,                    NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_DependUpon }, /* SeqNumberMismatch */
+    { NULL,                    NSM_DependUpon }, /* 1-WayReceived     */
+    { NULL,                    NSM_DependUpon }, /* KillNbr           */
+    { NULL,                    NSM_DependUpon }, /* InactivityTimer   */
+    { NULL,                    NSM_DependUpon }, /* LLDown            */
+  },
+  {
+    /* Deleted: dummy state. */
+    { NULL,                    NSM_Deleted    }, /* NoEvent           */
+    { NULL,                    NSM_Deleted    }, /* PacketReceived    */
+    { NULL,                    NSM_Deleted    }, /* Start             */
+    { NULL,                    NSM_Deleted    }, /* 2-WayReceived     */
+    { NULL,                    NSM_Deleted    }, /* NegotiationDone   */
+    { NULL,                    NSM_Deleted    }, /* ExchangeDone      */
+    { NULL,                    NSM_Deleted    }, /* BadLSReq          */
+    { NULL,                    NSM_Deleted    }, /* LoadingDone       */
+    { NULL,                    NSM_Deleted    }, /* AdjOK?            */
+    { NULL,                    NSM_Deleted    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Deleted    }, /* 1-WayReceived     */
+    { NULL,                    NSM_Deleted    }, /* KillNbr           */
+    { NULL,                    NSM_Deleted    }, /* InactivityTimer   */
+    { NULL,                    NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* Down: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Init       }, /* PacketReceived    */
+    { nsm_start,               NSM_Attempt    }, /* Start             */
+    { NULL,                    NSM_Down       }, /* 2-WayReceived     */
+    { NULL,                    NSM_Down       }, /* NegotiationDone   */
+    { NULL,                    NSM_Down       }, /* ExchangeDone      */
+    { NULL,                    NSM_Down       }, /* BadLSReq          */
+    { NULL,                    NSM_Down       }, /* LoadingDone       */
+    { NULL,                    NSM_Down       }, /* AdjOK?            */
+    { NULL,                    NSM_Down       }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Down       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* Attempt: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Init       }, /* PacketReceived    */
+    { NULL,                    NSM_Attempt    }, /* Start             */
+    { NULL,                    NSM_Attempt    }, /* 2-WayReceived     */
+    { NULL,                    NSM_Attempt    }, /* NegotiationDone   */
+    { NULL,                    NSM_Attempt    }, /* ExchangeDone      */
+    { NULL,                    NSM_Attempt    }, /* BadLSReq          */
+    { NULL,                    NSM_Attempt    }, /* LoadingDone       */
+    { NULL,                    NSM_Attempt    }, /* AdjOK?            */
+    { NULL,                    NSM_Attempt    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Attempt    }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* Init: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Init      }, /* PacketReceived    */
+    { NULL,                    NSM_Init       }, /* Start             */
+    { nsm_twoway_received,     NSM_DependUpon }, /* 2-WayReceived     */
+    { NULL,                    NSM_Init       }, /* NegotiationDone   */
+    { NULL,                    NSM_Init       }, /* ExchangeDone      */
+    { NULL,                    NSM_Init       }, /* BadLSReq          */
+    { NULL,                    NSM_Init       }, /* LoadingDone       */
+    { NULL,                    NSM_Init       }, /* AdjOK?            */
+    { NULL,                    NSM_Init       }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* 2-Way: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_TwoWay     }, /* HelloReceived     */
+    { NULL,                    NSM_TwoWay     }, /* Start             */
+    { NULL,                    NSM_TwoWay     }, /* 2-WayReceived     */
+    { NULL,                    NSM_TwoWay     }, /* NegotiationDone   */
+    { NULL,                    NSM_TwoWay     }, /* ExchangeDone      */
+    { NULL,                    NSM_TwoWay     }, /* BadLSReq          */
+    { NULL,                    NSM_TwoWay     }, /* LoadingDone       */
+    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_TwoWay     }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* ExStart: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_ExStart    }, /* PacaketReceived   */
+    { NULL,                    NSM_ExStart    }, /* Start             */
+    { NULL,                    NSM_ExStart    }, /* 2-WayReceived     */
+    { nsm_negotiation_done,    NSM_Exchange   }, /* NegotiationDone   */
+    { NULL,                    NSM_ExStart    }, /* ExchangeDone      */
+    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
+    { NULL,                    NSM_ExStart    }, /* LoadingDone       */
+    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* Exchange: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Exchange   }, /* PacketReceived    */
+    { NULL,                    NSM_Exchange   }, /* Start             */
+    { NULL,                    NSM_Exchange   }, /* 2-WayReceived     */
+    { NULL,                    NSM_Exchange   }, /* NegotiationDone   */
+    { nsm_exchange_done,       NSM_DependUpon }, /* ExchangeDone      */
+    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
+    { NULL,                    NSM_Exchange   }, /* LoadingDone       */
+    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  {
+    /* Loading: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Loading    }, /* PacketReceived    */
+    { NULL,                    NSM_Loading    }, /* Start             */
+    { NULL,                    NSM_Loading    }, /* 2-WayReceived     */
+    { NULL,                    NSM_Loading    }, /* NegotiationDone   */
+    { NULL,                    NSM_Loading    }, /* ExchangeDone      */
+    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
+    { NULL,                    NSM_Full       }, /* LoadingDone       */
+    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+  { /* Full: */
+    { NULL,                    NSM_DependUpon }, /* NoEvent           */
+    { nsm_packet_received,     NSM_Full       }, /* PacketReceived    */
+    { NULL,                    NSM_Full       }, /* Start             */
+    { NULL,                    NSM_Full       }, /* 2-WayReceived     */
+    { NULL,                    NSM_Full       }, /* NegotiationDone   */
+    { NULL,                    NSM_Full       }, /* ExchangeDone      */
+    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
+    { NULL,                    NSM_Full       }, /* LoadingDone       */
+    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
+    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
+    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
+    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
+  },
+};
+
+static const char *ospf_nsm_event_str[] =
+{
+  "NoEvent",
+  "PacketReceived",
+  "Start",
+  "2-WayReceived",
+  "NegotiationDone",
+  "ExchangeDone",
+  "BadLSReq",
+  "LoadingDone",
+  "AdjOK?",
+  "SeqNumberMismatch",
+  "1-WayReceived",
+  "KillNbr",
+  "InactivityTimer",
+  "LLDown",
+};
+
+static void
+nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event)
+{
+  /* Logging change of status. */
+  if (IS_DEBUG_OSPF (nsm, NSM_STATUS))
+    zlog_debug ("NSM[%s:%s]: State change %s -> %s (%s)",
+               IF_NAME (nbr->oi), inet_ntoa (nbr->router_id),
+               LOOKUP (ospf_nsm_state_msg, nbr->state),
+               LOOKUP (ospf_nsm_state_msg, next_state),
+               ospf_nsm_event_str [event]);
+
+  /* Optionally notify about adjacency changes */
+  if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) &&
+      (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) ||
+       (next_state == NSM_Full) || (next_state < nbr->state)))
+    zlog_notice("AdjChg: Nbr %s on %s: %s -> %s (%s)",
+                inet_ntoa (nbr->router_id), IF_NAME (nbr->oi),
+                LOOKUP (ospf_nsm_state_msg, nbr->state),
+                LOOKUP (ospf_nsm_state_msg, next_state),
+                ospf_nsm_event_str [event]);
+
+  /* Advance in NSM */
+  if (next_state > nbr->state)
+    nbr->ts_last_progress = recent_relative_time ();
+  else /* regression in NSM */
+    {
+      nbr->ts_last_regress = recent_relative_time ();
+      nbr->last_regress_str = ospf_nsm_event_str [event];
+    }
+
+}
+
+static void
+nsm_change_state (struct ospf_neighbor *nbr, int state)
+{
+  struct ospf_interface *oi = nbr->oi;
+  struct ospf_area *vl_area = NULL;
+  u_char old_state;
+  int x;
+  int force = 1;
+  
+  /* Preserve old status. */
+  old_state = nbr->state;
+
+  /* Change to new status. */
+  nbr->state = state;
+
+  /* Statistics. */
+  nbr->state_change++;
+
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    vl_area = ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id);
+
+  /* Generate NeighborChange ISM event.
+   *
+   * In response to NeighborChange, DR election is rerun. The information
+   * from the election process is required by the router-lsa construction.
+   *
+   * Therefore, trigger the event prior to refreshing the LSAs. */
+  switch (oi->state) {
+  case ISM_DROther:
+  case ISM_Backup:
+  case ISM_DR:
+    if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) ||
+        (old_state >= NSM_TwoWay && state < NSM_TwoWay))
+      OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange);
+    break;
+  default:
+    /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */
+    break;
+  }
+
+  /* One of the neighboring routers changes to/from the FULL state. */
+  if ((old_state != NSM_Full && state == NSM_Full) ||
+      (old_state == NSM_Full && state != NSM_Full))
+    {
+      if (state == NSM_Full)
+       {
+         oi->full_nbrs++;
+         oi->area->full_nbrs++;
+
+          ospf_check_abr_status (oi->ospf);
+
+         if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
+            if (++vl_area->full_vls == 1)
+             ospf_schedule_abr_task (oi->ospf);
+
+         /* kevinm: refresh any redistributions */
+         for (x = ZEBRA_ROUTE_SYSTEM; x < ZEBRA_ROUTE_MAX; x++)
+           {
+             if (x == ZEBRA_ROUTE_OSPF || x == ZEBRA_ROUTE_OSPF6)
+               continue;
+             ospf_external_lsa_refresh_type (oi->ospf, x, force);
+           }
+          /* XXX: Clearly some thing is wrong with refresh of external LSAs
+           * this added to hack around defaults not refreshing after a timer
+           * jump.
+           */
+          ospf_external_lsa_refresh_default (oi->ospf);
+       }
+      else
+       {
+         oi->full_nbrs--;
+         oi->area->full_nbrs--;
+
+          ospf_check_abr_status (oi->ospf);
+
+         if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
+           if (vl_area->full_vls > 0)
+             if (--vl_area->full_vls == 0)
+               ospf_schedule_abr_task (oi->ospf);
+       }
+
+      zlog_info ("nsm_change_state(%s, %s -> %s): "
+                "scheduling new router-LSA origination",
+                inet_ntoa (nbr->router_id),
+                LOOKUP(ospf_nsm_state_msg, old_state),
+                LOOKUP(ospf_nsm_state_msg, state));
+
+      ospf_router_lsa_update_area (oi->area);
+
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+       {
+         struct ospf_area *vl_area =
+           ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id);
+         
+         if (vl_area)
+           ospf_router_lsa_update_area (vl_area);
+       }
+
+      /* Originate network-LSA. */
+      if (oi->state == ISM_DR)
+       {
+         if (oi->network_lsa_self && oi->full_nbrs == 0)
+           {
+             ospf_lsa_flush_area (oi->network_lsa_self, oi->area);
+             ospf_lsa_unlock (&oi->network_lsa_self);
+             oi->network_lsa_self = NULL;
+           }
+         else
+           ospf_network_lsa_update (oi);
+       }
+    }
+
+  ospf_opaque_nsm_change (nbr, old_state);
+
+  /* State changes from > ExStart to <= ExStart should clear any Exchange
+   * or Full/LSA Update related lists and state.
+   * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK?
+   */
+  if ((old_state > NSM_ExStart) && (state <= NSM_ExStart))
+    nsm_clear_adj (nbr);
+
+  /* Start DD exchange protocol */
+  if (state == NSM_ExStart)
+    {
+      if (nbr->dd_seqnum == 0)
+       nbr->dd_seqnum = quagga_time (NULL);
+      else
+       nbr->dd_seqnum++;
+
+      nbr->dd_flags = OSPF_DD_FLAG_I|OSPF_DD_FLAG_M|OSPF_DD_FLAG_MS;
+      ospf_db_desc_send (nbr);
+    }
+
+  /* clear cryptographic sequence number */
+  if (state == NSM_Down)
+    nbr->crypt_seqnum = 0;
+  
+  /* Preserve old status? */
+}
+
+/* Execute NSM event process. */
+int
+ospf_nsm_event (struct thread *thread)
+{
+  int event;
+  int next_state;
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  event = THREAD_VAL (thread);
+
+  if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
+    zlog_debug ("NSM[%s:%s]: %s (%s)", IF_NAME (nbr->oi),
+              inet_ntoa (nbr->router_id),
+              LOOKUP (ospf_nsm_state_msg, nbr->state),
+              ospf_nsm_event_str [event]);
+  
+  next_state = NSM [nbr->state][event].next_state;
+
+  /* Call function. */
+  if (NSM [nbr->state][event].func != NULL)
+    {
+      int func_state = (*(NSM [nbr->state][event].func))(nbr);
+      
+      if (NSM [nbr->state][event].next_state == NSM_DependUpon)
+        next_state = func_state;
+      else if (func_state)
+        {
+          /* There's a mismatch between the FSM tables and what an FSM
+           * action/state-change function returned. State changes which
+           * do not have conditional/DependUpon next-states should not
+           * try set next_state.
+           */
+          zlog_warn ("NSM[%s:%s]: %s (%s): "
+                     "Warning: action tried to change next_state to %s",
+                     IF_NAME (nbr->oi), inet_ntoa (nbr->router_id),
+                     LOOKUP (ospf_nsm_state_msg, nbr->state),
+                     ospf_nsm_event_str [event],
+                     LOOKUP (ospf_nsm_state_msg, func_state));
+        }
+    }
+
+  assert (next_state != NSM_DependUpon);
+  
+  /* If state is changed. */
+  if (next_state != nbr->state)
+    {
+      nsm_notice_state_change (nbr, next_state, event);
+#ifdef HAVE_SNMP
+      int send_trap_virt = 0;
+      int send_trap = 0;
+      /* Terminal state or regression */ 
+      if ((next_state == NSM_Full) 
+             || (next_state == NSM_TwoWay)
+             || (next_state < nbr->state))
+      {
+         /* ospfVirtNbrStateChange */
+         if (nbr->oi->type == OSPF_IFTYPE_VIRTUALLINK)
+             send_trap_virt = 1;
+         /* ospfNbrStateChange trap  */
+         else  
+             /* To/From FULL, only managed by DR */
+             if (((next_state != NSM_Full) && (nbr->state != NSM_Full)) 
+                     || (nbr->oi->state == ISM_DR))
+                 send_trap = 1;
+      }
+#endif
+      nsm_change_state (nbr, next_state);
+
+#ifdef HAVE_SNMP
+      if (send_trap_virt) {
+         ospfTrapVirtNbrStateChange(nbr);
+      } else if (send_trap) {
+         ospfTrapNbrStateChange(nbr);
+      }
+#endif
+    }
+
+  /* Make sure timer is set. */
+  nsm_timer_set (nbr);
+
+  /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor
+   * is deleted.
+   *
+   * Rather than encode knowledge here of which events lead to NBR
+   * delete, we take our cue from the NSM table, via the dummy
+   * 'Deleted' neighbour state.
+   */
+  if (nbr->state == NSM_Deleted)
+    ospf_nbr_delete (nbr);
+
+  return 0;
+}
+
+/* Check loading state. */
+void
+ospf_check_nbr_loading (struct ospf_neighbor *nbr)
+{
+  if (nbr->state == NSM_Loading)
+    {
+      if (ospf_ls_request_isempty (nbr))
+       OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_LoadingDone);
+      else if (nbr->ls_req_last == NULL)
+       ospf_ls_req_event (nbr);
+    }
+}
diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h
new file mode 100644 (file)
index 0000000..9b7e14a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * OSPF version 2  Neighbor State Machine
+ *   From RFC2328 [OSPF Version 2]
+ *   Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_NSM_H
+#define _ZEBRA_OSPF_NSM_H
+
+/* OSPF Neighbor State Machine State. */
+#define NSM_DependUpon          0
+#define NSM_Deleted            1
+#define NSM_Down               2
+#define NSM_Attempt            3
+#define NSM_Init               4
+#define NSM_TwoWay             5
+#define NSM_ExStart            6
+#define NSM_Exchange           7
+#define NSM_Loading            8
+#define NSM_Full               9
+#define OSPF_NSM_STATE_MAX     10
+
+/* OSPF Neighbor State Machine Event. */
+#define NSM_NoEvent            0
+#define NSM_PacketReceived     1 /* HelloReceived in the protocol */
+#define NSM_Start              2
+#define NSM_TwoWayReceived     3
+#define NSM_NegotiationDone    4
+#define NSM_ExchangeDone       5
+#define NSM_BadLSReq           6
+#define NSM_LoadingDone                7
+#define NSM_AdjOK              8
+#define NSM_SeqNumberMismatch  9
+#define NSM_OneWayReceived     10
+#define NSM_KillNbr           11
+#define NSM_InactivityTimer    12
+#define NSM_LLDown            13
+#define OSPF_NSM_EVENT_MAX     14
+
+/* Macro for OSPF NSM timer turn on. */
+#define OSPF_NSM_TIMER_ON(T,F,V)                                              \
+      do {                                                                    \
+        if (!(T))                                                             \
+          (T) = thread_add_timer (master, (F), nbr, (V));                     \
+      } while (0)
+
+/* Macro for OSPF NSM timer turn off. */
+#define OSPF_NSM_TIMER_OFF(X)                                                 \
+      do {                                                                    \
+        if (X)                                                                \
+          {                                                                   \
+            thread_cancel (X);                                                \
+            (X) = NULL;                                                       \
+          }                                                                   \
+      } while (0)
+
+/* Macro for OSPF NSM schedule event. */
+#define OSPF_NSM_EVENT_SCHEDULE(N,E)                                          \
+      thread_add_event (master, ospf_nsm_event, (N), (E))
+
+/* Macro for OSPF NSM execute event. */
+#define OSPF_NSM_EVENT_EXECUTE(N,E)                                           \
+      thread_execute (master, ospf_nsm_event, (N), (E))
+
+/* Prototypes. */
+extern int ospf_nsm_event (struct thread *);
+extern void ospf_check_nbr_loading (struct ospf_neighbor *);
+extern int ospf_db_summary_isempty (struct ospf_neighbor *);
+extern int ospf_db_summary_count (struct ospf_neighbor *);
+extern void ospf_db_summary_clear (struct ospf_neighbor *);
+
+#endif /* _ZEBRA_OSPF_NSM_H */
+
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
new file mode 100644 (file)
index 0000000..39465c1
--- /dev/null
@@ -0,0 +1,2173 @@
+/*
+ * This is an implementation of rfc2370.
+ * Copyright (C) 2001 KDD R&D Laboratories, Inc.
+ * http://www.kddlabs.co.jp/
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/***** MTYPE definitions are not reflected to "memory.h" yet. *****/
+#define MTYPE_OSPF_OPAQUE_FUNCTAB      MTYPE_TMP
+#define MTYPE_OPAQUE_INFO_PER_TYPE     MTYPE_TMP
+#define MTYPE_OPAQUE_INFO_PER_ID       MTYPE_TMP
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"         /* for inet_aton() */
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+
+/*------------------------------------------------------------------------*
+ * Followings are initialize/terminate functions for Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+
+#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_ri.h"
+
+#ifdef SUPPORT_OSPF_API
+int ospf_apiserver_init (void);
+void ospf_apiserver_term (void); 
+/* Init apiserver? It's disabled by default. */
+int ospf_apiserver_enable;
+#endif /* SUPPORT_OSPF_API */
+
+static void ospf_opaque_register_vty (void);
+static void ospf_opaque_funclist_init (void);
+static void ospf_opaque_funclist_term (void);
+static void free_opaque_info_per_type (void *val);
+static void free_opaque_info_per_id (void *val);
+static int ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa);
+static int ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa);
+
+void
+ospf_opaque_init (void)
+{
+  ospf_opaque_register_vty ();
+  ospf_opaque_funclist_init ();
+
+  if (ospf_mpls_te_init () != 0)
+    exit (1);
+
+  if (ospf_router_info_init () != 0)
+    exit (1);
+
+#ifdef SUPPORT_OSPF_API
+  if ((ospf_apiserver_enable) && (ospf_apiserver_init () != 0))
+    exit (1);
+#endif /* SUPPORT_OSPF_API */
+
+  return;
+}
+
+void
+ospf_opaque_term (void)
+{
+  ospf_mpls_te_term ();
+
+  ospf_router_info_term ();
+
+#ifdef SUPPORT_OSPF_API
+  ospf_apiserver_term ();
+#endif /* SUPPORT_OSPF_API */
+
+  ospf_opaque_funclist_term ();
+  return;
+}
+
+int
+ospf_opaque_type9_lsa_init (struct ospf_interface *oi)
+{
+  if (oi->opaque_lsa_self != NULL)
+    list_delete (oi->opaque_lsa_self);
+
+  oi->opaque_lsa_self = list_new ();
+  oi->opaque_lsa_self->del = free_opaque_info_per_type;
+  oi->t_opaque_lsa_self = NULL;
+  return 0;
+}
+
+void
+ospf_opaque_type9_lsa_term (struct ospf_interface *oi)
+{
+  OSPF_TIMER_OFF (oi->t_opaque_lsa_self);
+  if (oi->opaque_lsa_self != NULL)
+    list_delete (oi->opaque_lsa_self);
+  oi->opaque_lsa_self = NULL;
+  return;
+}
+
+int
+ospf_opaque_type10_lsa_init (struct ospf_area *area)
+{
+  if (area->opaque_lsa_self != NULL)
+    list_delete (area->opaque_lsa_self);
+
+  area->opaque_lsa_self = list_new ();
+  area->opaque_lsa_self->del = free_opaque_info_per_type;
+  area->t_opaque_lsa_self = NULL;
+
+#ifdef MONITOR_LSDB_CHANGE
+  area->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook;
+  area->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook;
+#endif /* MONITOR_LSDB_CHANGE */
+  return 0;
+}
+
+void
+ospf_opaque_type10_lsa_term (struct ospf_area *area)
+{
+#ifdef MONITOR_LSDB_CHANGE
+  area->lsdb->new_lsa_hook = 
+  area->lsdb->del_lsa_hook = NULL;
+#endif /* MONITOR_LSDB_CHANGE */
+
+  OSPF_TIMER_OFF (area->t_opaque_lsa_self);
+  if (area->opaque_lsa_self != NULL)
+    list_delete (area->opaque_lsa_self);
+  area->opaque_lsa_self = NULL;
+  return;
+}
+
+int
+ospf_opaque_type11_lsa_init (struct ospf *top)
+{
+  if (top->opaque_lsa_self != NULL)
+    list_delete (top->opaque_lsa_self);
+
+  top->opaque_lsa_self = list_new ();
+  top->opaque_lsa_self->del = free_opaque_info_per_type;
+  top->t_opaque_lsa_self = NULL;
+
+#ifdef MONITOR_LSDB_CHANGE
+  top->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook;
+  top->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook;
+#endif /* MONITOR_LSDB_CHANGE */
+  return 0;
+}
+
+void
+ospf_opaque_type11_lsa_term (struct ospf *top)
+{
+#ifdef MONITOR_LSDB_CHANGE
+  top->lsdb->new_lsa_hook = 
+  top->lsdb->del_lsa_hook = NULL;
+#endif /* MONITOR_LSDB_CHANGE */
+
+  OSPF_TIMER_OFF (top->t_opaque_lsa_self);
+  if (top->opaque_lsa_self != NULL)
+    list_delete (top->opaque_lsa_self);
+  top->opaque_lsa_self = NULL;
+  return;
+}
+
+static const char *
+ospf_opaque_type_name (u_char opaque_type)
+{
+  const char *name = "Unknown";
+
+  switch (opaque_type)
+    {
+    case OPAQUE_TYPE_WILDCARD: /* This is a special assignment! */
+      name = "Wildcard";
+      break;
+    case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA:
+      name = "Traffic Engineering LSA";
+      break;
+    case OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC:
+      name = "Sycamore optical topology description";
+      break;
+    case OPAQUE_TYPE_GRACE_LSA:
+      name = "Grace-LSA";
+      break;
+    case OPAQUE_TYPE_INTER_AS_LSA:
+      name = "Inter-AS TE-v2 LSA";
+      break;
+    case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+      name = "Router Information LSA";
+      break;
+    default:
+      if (OPAQUE_TYPE_RANGE_UNASSIGNED (opaque_type))
+        name = "Unassigned";
+      else
+        {
+          u_int32_t bigger_range = opaque_type;
+          /*
+           * Get around type-limits warning: comparison is always true due to limited range of data type
+           */
+          if (OPAQUE_TYPE_RANGE_RESERVED (bigger_range))
+            name = "Private/Experimental";
+        }
+      break;
+    }
+  return name;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are management functions to store user specified callbacks.
+ *------------------------------------------------------------------------*/
+
+struct opaque_info_per_type; /* Forward declaration. */
+
+struct ospf_opaque_functab
+{
+  u_char opaque_type;
+  struct opaque_info_per_type *oipt;
+
+  int (* new_if_hook)(struct interface *ifp);
+  int (* del_if_hook)(struct interface *ifp);
+  void (* ism_change_hook)(struct ospf_interface *oi, int old_status);
+  void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status);
+  void (* config_write_router)(struct vty *vty);
+  void (* config_write_if    )(struct vty *vty, struct interface *ifp);
+  void (* config_write_debug )(struct vty *vty);
+  void (* show_opaque_info   )(struct vty *vty, struct ospf_lsa *lsa);
+  int  (* lsa_originator)(void *arg);
+  struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa);
+  int (* new_lsa_hook)(struct ospf_lsa *lsa);
+  int (* del_lsa_hook)(struct ospf_lsa *lsa);
+};
+
+/* Handle LSA-9/10/11 altogether. */
+static struct list *ospf_opaque_wildcard_funclist;
+static struct list *ospf_opaque_type9_funclist;
+static struct list *ospf_opaque_type10_funclist;
+static struct list *ospf_opaque_type11_funclist;
+
+static void
+ospf_opaque_del_functab (void *val)
+{
+  XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, val);
+  return;
+}
+
+static void
+ospf_opaque_funclist_init (void)
+{
+  struct list *funclist;
+
+  funclist = ospf_opaque_wildcard_funclist = list_new ();
+  funclist->del = ospf_opaque_del_functab;
+
+  funclist = ospf_opaque_type9_funclist  = list_new ();
+  funclist->del = ospf_opaque_del_functab;
+
+  funclist = ospf_opaque_type10_funclist = list_new ();
+  funclist->del = ospf_opaque_del_functab;
+
+  funclist = ospf_opaque_type11_funclist = list_new ();
+  funclist->del = ospf_opaque_del_functab;
+  return;
+}
+
+static void
+ospf_opaque_funclist_term (void)
+{
+  struct list *funclist;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  list_delete (funclist);
+
+  funclist = ospf_opaque_type9_funclist;
+  list_delete (funclist);
+
+  funclist = ospf_opaque_type10_funclist;
+  list_delete (funclist);
+
+  funclist = ospf_opaque_type11_funclist;
+  list_delete (funclist);
+  return;
+}
+
+static struct list *
+ospf_get_opaque_funclist (u_char lsa_type)
+{
+  struct list *funclist = NULL;
+
+  switch (lsa_type)
+    {
+    case OPAQUE_TYPE_WILDCARD:
+      /* XXX
+       * This is an ugly trick to handle type-9/10/11 LSA altogether.
+       * Yes, "OPAQUE_TYPE_WILDCARD (value 0)" is not an LSA-type, nor
+       * an officially assigned opaque-type.
+       * Though it is possible that the value might be officially used
+       * in the future, we use it internally as a special label, for now.
+       */
+      funclist = ospf_opaque_wildcard_funclist;
+      break;
+    case OSPF_OPAQUE_LINK_LSA:
+      funclist = ospf_opaque_type9_funclist;
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      funclist = ospf_opaque_type10_funclist;
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      funclist = ospf_opaque_type11_funclist;
+      break;
+    default:
+      zlog_warn ("ospf_get_opaque_funclist: Unexpected LSA-type(%u)", lsa_type);
+      break;
+    }
+  return funclist;
+}
+
+/* XXX: such a huge argument list can /not/ be healthy... */
+int
+ospf_register_opaque_functab (
+  u_char lsa_type,
+  u_char opaque_type,
+  int (* new_if_hook)(struct interface *ifp),
+  int (* del_if_hook)(struct interface *ifp),
+  void (* ism_change_hook)(struct ospf_interface *oi, int old_status),
+  void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status),
+  void (* config_write_router)(struct vty *vty),
+  void (* config_write_if    )(struct vty *vty, struct interface *ifp),
+  void (* config_write_debug )(struct vty *vty),
+  void (* show_opaque_info   )(struct vty *vty, struct ospf_lsa *lsa),
+  int  (* lsa_originator)(void *arg),
+  struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa),
+  int (* new_lsa_hook)(struct ospf_lsa *lsa),
+  int (* del_lsa_hook)(struct ospf_lsa *lsa))
+{
+  struct list *funclist;
+  struct ospf_opaque_functab *new;
+  int rc = -1;
+
+  if ((funclist = ospf_get_opaque_funclist (lsa_type)) == NULL)
+    {
+      zlog_warn ("ospf_register_opaque_functab: Cannot get funclist"
+                 " for Type-%u LSAs?",
+                 lsa_type);
+      goto out;
+    }
+  else
+    {
+      struct listnode *node, *nnode;
+      struct ospf_opaque_functab *functab;
+      
+      for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+        if (functab->opaque_type == opaque_type)
+          {
+            zlog_warn ("ospf_register_opaque_functab: Duplicated entry?:"
+                       " lsa_type(%u), opaque_type(%u)", 
+                       lsa_type, opaque_type);
+            goto out;
+          }
+    }
+
+  if ((new = XCALLOC (MTYPE_OSPF_OPAQUE_FUNCTAB,
+                     sizeof (struct ospf_opaque_functab))) == NULL)
+    {
+      zlog_warn ("ospf_register_opaque_functab: XMALLOC: %s",
+                 safe_strerror (errno));
+      goto out;
+    }
+
+  new->opaque_type    = opaque_type;
+  new->oipt           = NULL;
+  new->new_if_hook    = new_if_hook;
+  new->del_if_hook    = del_if_hook;
+  new->ism_change_hook     = ism_change_hook;
+  new->nsm_change_hook     = nsm_change_hook;
+  new->config_write_router = config_write_router;
+  new->config_write_if     = config_write_if;
+  new->config_write_debug  = config_write_debug;
+  new->show_opaque_info    = show_opaque_info;
+  new->lsa_originator = lsa_originator;
+  new->lsa_refresher  = lsa_refresher;
+  new->new_lsa_hook   = new_lsa_hook;
+  new->del_lsa_hook   = del_lsa_hook;
+
+  listnode_add (funclist, new);
+  rc = 0;
+
+out:
+  return rc;
+}
+
+void
+ospf_delete_opaque_functab (u_char lsa_type, u_char opaque_type)
+{
+  struct list *funclist;
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  if ((funclist = ospf_get_opaque_funclist (lsa_type)) != NULL)
+    for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+      {
+        if (functab->opaque_type == opaque_type)
+          {
+            /* Cleanup internal control information, if it still remains. */
+            if (functab->oipt != NULL)
+              free_opaque_info_per_type (functab->oipt);
+
+            /* Dequeue listnode entry from the list. */
+            listnode_delete (funclist, functab);
+
+            /* Avoid misjudgement in the next lookup. */
+            if (listcount (funclist) == 0)
+              funclist->head = funclist->tail = NULL;
+
+            XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, functab);
+            break;
+         }
+      }
+
+  return;
+}
+
+static struct ospf_opaque_functab *
+ospf_opaque_functab_lookup (struct ospf_lsa *lsa)
+{
+  struct list *funclist;
+  struct listnode *node;
+  struct ospf_opaque_functab *functab;
+  u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr));
+
+  if ((funclist = ospf_get_opaque_funclist (lsa->data->type)) != NULL)
+    for (ALL_LIST_ELEMENTS_RO (funclist, node, functab))
+      if (functab->opaque_type == key)
+        return functab;
+
+  return NULL;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are management functions for self-originated LSA entries.
+ *------------------------------------------------------------------------*/
+
+/*
+ * Opaque-LSA control information per opaque-type.
+ * Single Opaque-Type may have multiple instances; each of them will be
+ * identified by their opaque-id.
+ */
+struct opaque_info_per_type
+{
+  u_char lsa_type;
+  u_char opaque_type;
+
+  enum { PROC_NORMAL, PROC_SUSPEND } status;
+
+  /*
+   * Thread for (re-)origination scheduling for this opaque-type.
+   *
+   * Initial origination of Opaque-LSAs is controlled by generic
+   * Opaque-LSA handling module so that same opaque-type entries are
+   * called all at once when certain conditions are met.
+   * However, there might be cases that some Opaque-LSA clients need
+   * to (re-)originate their own Opaque-LSAs out-of-sync with others.
+   * This thread is prepared for that specific purpose.
+   */
+  struct thread *t_opaque_lsa_self;
+
+  /*
+   * Backpointer to an "owner" which is LSA-type dependent.
+   *   type-9:  struct ospf_interface
+   *   type-10: struct ospf_area
+   *   type-11: struct ospf
+   */
+  void *owner;
+
+  /* Collection of callback functions for this opaque-type. */
+  struct ospf_opaque_functab *functab;
+
+  /* List of Opaque-LSA control informations per opaque-id. */
+  struct list *id_list;
+};
+
+/* Opaque-LSA control information per opaque-id. */
+struct opaque_info_per_id
+{
+  u_int32_t opaque_id;
+
+  /* Thread for refresh/flush scheduling for this opaque-type/id. */
+  struct thread *t_opaque_lsa_self;
+
+  /* Backpointer to Opaque-LSA control information per opaque-type. */
+  struct opaque_info_per_type *opqctl_type;
+
+  /* Here comes an actual Opaque-LSA entry for this opaque-type/id. */
+  struct ospf_lsa *lsa;
+};
+
+static struct opaque_info_per_type *register_opaque_info_per_type (struct ospf_opaque_functab *functab, struct ospf_lsa *new);
+static struct opaque_info_per_type *lookup_opaque_info_by_type (struct ospf_lsa *lsa);
+static struct opaque_info_per_id *register_opaque_info_per_id (struct opaque_info_per_type *oipt, struct ospf_lsa *new);
+static struct opaque_info_per_id *lookup_opaque_info_by_id (struct opaque_info_per_type *oipt, struct ospf_lsa *lsa);
+static struct opaque_info_per_id *register_opaque_lsa (struct ospf_lsa *new);
+
+
+static struct opaque_info_per_type *
+register_opaque_info_per_type (struct ospf_opaque_functab *functab,
+                               struct ospf_lsa *new)
+{
+  struct ospf *top;
+  struct opaque_info_per_type *oipt;
+
+  if ((oipt = XCALLOC (MTYPE_OPAQUE_INFO_PER_TYPE,
+                      sizeof (struct opaque_info_per_type))) == NULL)
+    {
+      zlog_warn ("register_opaque_info_per_type: XMALLOC: %s", safe_strerror (errno));
+      goto out;
+    }
+
+  switch (new->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      oipt->owner = new->oi;
+      listnode_add (new->oi->opaque_lsa_self, oipt);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      oipt->owner = new->area;
+      listnode_add (new->area->opaque_lsa_self, oipt);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      top = ospf_lookup ();
+      if (new->area != NULL && (top = new->area->ospf) == NULL)
+        {
+          free_opaque_info_per_type ((void *) oipt);
+          oipt = NULL;
+          goto out; /* This case may not exist. */
+        }
+      oipt->owner = top;
+      listnode_add (top->opaque_lsa_self, oipt);
+      break;
+    default:
+      zlog_warn ("register_opaque_info_per_type: Unexpected LSA-type(%u)", new->data->type);
+      free_opaque_info_per_type ((void *) oipt);
+      oipt = NULL;
+      goto out; /* This case may not exist. */
+    }
+
+  oipt->lsa_type = new->data->type;
+  oipt->opaque_type = GET_OPAQUE_TYPE (ntohl (new->data->id.s_addr));
+  oipt->status = PROC_NORMAL;
+  oipt->t_opaque_lsa_self = NULL;
+  oipt->functab = functab;
+  functab->oipt = oipt;
+  oipt->id_list = list_new ();
+  oipt->id_list->del = free_opaque_info_per_id;
+
+out:
+  return oipt;
+}
+
+static void
+free_opaque_info_per_type (void *val)
+{
+  struct opaque_info_per_type *oipt = (struct opaque_info_per_type *) val;
+  struct opaque_info_per_id *oipi;
+  struct ospf_lsa *lsa;
+  struct listnode *node, *nnode;
+
+  /* Control information per opaque-id may still exist. */
+  for (ALL_LIST_ELEMENTS (oipt->id_list, node, nnode, oipi))
+    {
+      if ((lsa = oipi->lsa) == NULL)
+        continue;
+      if (IS_LSA_MAXAGE (lsa))
+        continue;
+      ospf_opaque_lsa_flush_schedule (lsa);
+    }
+
+  /* Remove "oipt" from its owner's self-originated LSA list. */
+  switch (oipt->lsa_type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      {
+        struct ospf_interface *oi = (struct ospf_interface *)(oipt->owner);
+        listnode_delete (oi->opaque_lsa_self, oipt);
+        break;
+      }
+    case OSPF_OPAQUE_AREA_LSA:
+      {
+        struct ospf_area *area = (struct ospf_area *)(oipt->owner);
+        listnode_delete (area->opaque_lsa_self, oipt);
+        break;
+      }
+    case OSPF_OPAQUE_AS_LSA:
+      {
+        struct ospf *top = (struct ospf *)(oipt->owner);
+        listnode_delete (top->opaque_lsa_self, oipt);
+        break;
+      }
+    default:
+      zlog_warn ("free_opaque_info_per_type: Unexpected LSA-type(%u)", oipt->lsa_type);
+      break; /* This case may not exist. */
+    }
+
+  OSPF_TIMER_OFF (oipt->t_opaque_lsa_self);
+  list_delete (oipt->id_list);
+  XFREE (MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
+  return;
+}
+
+static struct opaque_info_per_type *
+lookup_opaque_info_by_type (struct ospf_lsa *lsa)
+{
+  struct ospf *top;
+  struct ospf_area *area;
+  struct ospf_interface *oi;
+  struct list *listtop = NULL;
+  struct listnode *node, *nnode;
+  struct opaque_info_per_type *oipt = NULL;
+  u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr));
+
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      if ((oi = lsa->oi) != NULL)
+        listtop = oi->opaque_lsa_self;
+      else
+        zlog_warn ("Type-9 Opaque-LSA: Reference to OI is missing?");
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      if ((area = lsa->area) != NULL)
+        listtop = area->opaque_lsa_self;
+      else
+        zlog_warn ("Type-10 Opaque-LSA: Reference to AREA is missing?");
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      top = ospf_lookup ();
+      if ((area = lsa->area) != NULL && (top = area->ospf) == NULL)
+        {
+          zlog_warn ("Type-11 Opaque-LSA: Reference to OSPF is missing?");
+          break; /* Unlikely to happen. */
+        }
+      listtop = top->opaque_lsa_self;
+      break;
+    default:
+      zlog_warn ("lookup_opaque_info_by_type: Unexpected LSA-type(%u)", lsa->data->type);
+      break;
+    }
+
+  if (listtop != NULL)
+    for (ALL_LIST_ELEMENTS (listtop, node, nnode, oipt))
+      if (oipt->opaque_type == key)
+        return oipt;
+
+  return NULL;
+}
+
+static struct opaque_info_per_id *
+register_opaque_info_per_id (struct opaque_info_per_type *oipt,
+                             struct ospf_lsa *new)
+{
+  struct opaque_info_per_id *oipi;
+
+  if ((oipi = XCALLOC (MTYPE_OPAQUE_INFO_PER_ID,
+                      sizeof (struct opaque_info_per_id))) == NULL)
+    {
+      zlog_warn ("register_opaque_info_per_id: XMALLOC: %s", safe_strerror (errno));
+      goto out;
+    }
+  oipi->opaque_id = GET_OPAQUE_ID (ntohl (new->data->id.s_addr));
+  oipi->t_opaque_lsa_self = NULL;
+  oipi->opqctl_type = oipt;
+  oipi->lsa = ospf_lsa_lock (new);
+
+  listnode_add (oipt->id_list, oipi);
+
+out:
+  return oipi;
+}
+
+static void
+free_opaque_info_per_id (void *val)
+{
+  struct opaque_info_per_id *oipi = (struct opaque_info_per_id *) val;
+
+  OSPF_TIMER_OFF (oipi->t_opaque_lsa_self);
+  if (oipi->lsa != NULL)
+    ospf_lsa_unlock (&oipi->lsa);
+  XFREE (MTYPE_OPAQUE_INFO_PER_ID, oipi);
+  return;
+}
+
+static struct opaque_info_per_id *
+lookup_opaque_info_by_id (struct opaque_info_per_type *oipt,
+                          struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct opaque_info_per_id   *oipi;
+  u_int32_t key = GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr));
+
+  for (ALL_LIST_ELEMENTS (oipt->id_list, node, nnode, oipi))
+    if (oipi->opaque_id == key)
+      return oipi;
+
+  return NULL;
+}
+
+static struct opaque_info_per_id *
+register_opaque_lsa (struct ospf_lsa *new)
+{
+  struct ospf_opaque_functab *functab;
+  struct opaque_info_per_type *oipt;
+  struct opaque_info_per_id *oipi = NULL;
+
+  if ((functab = ospf_opaque_functab_lookup (new)) == NULL)
+    goto out;
+
+  if ((oipt = lookup_opaque_info_by_type (new)) == NULL
+  &&  (oipt = register_opaque_info_per_type (functab, new)) == NULL)
+    goto out;
+
+  if ((oipi = register_opaque_info_per_id (oipt, new)) == NULL)
+    goto out;
+
+out:
+  return oipi;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are (vty) configuration functions for Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+
+DEFUN (capability_opaque,
+       capability_opaque_cmd,
+       "capability opaque",
+       "Enable specific OSPF feature\n"
+       "Opaque LSA\n")
+{
+  struct ospf *ospf = (struct ospf *) vty->index;
+
+  /* Turn on the "master switch" of opaque-lsa capability. */
+  if (!CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Opaque capability: OFF -> ON");
+
+      SET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE);
+      ospf_renegotiate_optional_capabilities (ospf);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (capability_opaque,
+       ospf_opaque_capable_cmd,
+       "ospf opaque-lsa",
+       "OSPF specific commands\n"
+       "Enable the Opaque-LSA capability (rfc2370)\n")
+
+DEFUN (no_capability_opaque,
+       no_capability_opaque_cmd,
+       "no capability opaque",
+       NO_STR
+       "Enable specific OSPF feature\n"
+       "Opaque LSA\n")
+{
+  struct ospf *ospf = (struct ospf *) vty->index;
+
+  /* Turn off the "master switch" of opaque-lsa capability. */
+  if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Opaque capability: ON -> OFF");
+
+      UNSET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE);
+      ospf_renegotiate_optional_capabilities (ospf);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_capability_opaque,
+       no_ospf_opaque_capable_cmd,
+       "no ospf opaque-lsa",
+       NO_STR
+       "OSPF specific commands\n"
+       "Disable the Opaque-LSA capability (rfc2370)\n")
+
+static void
+ospf_opaque_register_vty (void)
+{
+  install_element (OSPF_NODE, &capability_opaque_cmd);
+  install_element (OSPF_NODE, &no_capability_opaque_cmd);
+  install_element (OSPF_NODE, &ospf_opaque_capable_cmd);
+  install_element (OSPF_NODE, &no_ospf_opaque_capable_cmd);
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are collection of user-registered function callers.
+ *------------------------------------------------------------------------*/
+
+static int
+opaque_lsa_new_if_callback (struct list *funclist, struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+  int rc = -1;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->new_if_hook != NULL)
+      if ((* functab->new_if_hook)(ifp) != 0)
+        goto out;
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+opaque_lsa_del_if_callback (struct list *funclist, struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+  int rc = -1;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->del_if_hook != NULL)
+      if ((* functab->del_if_hook)(ifp) != 0)
+        goto out;
+  rc = 0;
+out:
+  return rc;
+}
+
+static void
+opaque_lsa_ism_change_callback (struct list *funclist,
+                                struct ospf_interface *oi, int old_status)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->ism_change_hook != NULL)
+      (* functab->ism_change_hook)(oi, old_status);
+
+  return;
+}
+
+static void
+opaque_lsa_nsm_change_callback (struct list *funclist,
+                                struct ospf_neighbor *nbr, int old_status)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->nsm_change_hook != NULL)
+      (* functab->nsm_change_hook)(nbr, old_status);
+  return;
+}
+
+static void
+opaque_lsa_config_write_router_callback (struct list *funclist, 
+                                         struct vty *vty)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->config_write_router != NULL)
+      (* functab->config_write_router)(vty);
+  return;
+}
+
+static void
+opaque_lsa_config_write_if_callback (struct list *funclist,
+                                     struct vty *vty, struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->config_write_if != NULL)
+      (* functab->config_write_if)(vty, ifp);
+  return;
+}
+
+static void
+opaque_lsa_config_write_debug_callback (struct list *funclist, struct vty *vty)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->config_write_debug != NULL)
+      (* functab->config_write_debug)(vty);
+  return;
+}
+
+static int
+opaque_lsa_originate_callback (struct list *funclist, void *lsa_type_dependent)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+  int rc = -1;
+
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->lsa_originator != NULL)
+      if ((* functab->lsa_originator)(lsa_type_dependent) != 0)
+         goto out;
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+new_lsa_callback (struct list *funclist, struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+  int rc = -1;
+
+  /* This function handles ALL types of LSAs, not only opaque ones. */
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->new_lsa_hook != NULL)
+      if ((* functab->new_lsa_hook)(lsa) != 0)
+        goto out;
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+del_lsa_callback (struct list *funclist, struct ospf_lsa *lsa)
+{
+  struct listnode *node, *nnode;
+  struct ospf_opaque_functab *functab;
+  int rc = -1;
+
+  /* This function handles ALL types of LSAs, not only opaque ones. */
+  for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab))
+    if (functab->del_lsa_hook != NULL)
+      if ((* functab->del_lsa_hook)(lsa) != 0)
+        goto out;
+  rc = 0;
+out:
+  return rc;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are glue functions to call Opaque-LSA specific processing.
+ *------------------------------------------------------------------------*/
+
+int
+ospf_opaque_new_if (struct interface *ifp)
+{
+  struct list *funclist;
+  int rc = -1;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  if (opaque_lsa_new_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type9_funclist;
+  if (opaque_lsa_new_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type10_funclist;
+  if (opaque_lsa_new_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type11_funclist;
+  if (opaque_lsa_new_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  rc = 0;
+out:
+  return rc;
+}
+
+int
+ospf_opaque_del_if (struct interface *ifp)
+{
+  struct list *funclist;
+  int rc = -1;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  if (opaque_lsa_del_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type9_funclist;
+  if (opaque_lsa_del_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type10_funclist;
+  if (opaque_lsa_del_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type11_funclist;
+  if (opaque_lsa_del_if_callback (funclist, ifp) != 0)
+    goto out;
+
+  rc = 0;
+out:
+  return rc;
+}
+
+void
+ospf_opaque_ism_change (struct ospf_interface *oi, int old_status)
+{
+  struct list *funclist;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  opaque_lsa_ism_change_callback (funclist, oi, old_status);
+
+  funclist = ospf_opaque_type9_funclist;
+  opaque_lsa_ism_change_callback (funclist, oi, old_status);
+
+  funclist = ospf_opaque_type10_funclist;
+  opaque_lsa_ism_change_callback (funclist, oi, old_status);
+
+  funclist = ospf_opaque_type11_funclist;
+  opaque_lsa_ism_change_callback (funclist, oi, old_status);
+
+  return;
+}
+
+void
+ospf_opaque_nsm_change (struct ospf_neighbor *nbr, int old_state)
+{
+  struct ospf *top;
+  struct list *funclist;
+
+  if ((top = oi_to_top (nbr->oi)) == NULL)
+    goto out;
+
+  if (old_state != NSM_Full && nbr->state == NSM_Full)
+    {
+      if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+        {
+          if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT))
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("Opaque-LSA: Now get operational!");
+
+              SET_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT);
+            }
+
+          ospf_opaque_lsa_originate_schedule (nbr->oi, NULL);
+        }
+    }
+  else
+  if (old_state == NSM_Full && nbr->state != NSM_Full)
+    {
+#ifdef NOTYET
+      /*
+       * If no more opaque-capable full-state neighbor remains in the
+       * flooding scope which corresponds to Opaque-LSA type, periodic
+       * LS flooding should be stopped.
+       */
+#endif /* NOTYET */
+      ;
+    }
+
+  funclist = ospf_opaque_wildcard_funclist;
+  opaque_lsa_nsm_change_callback (funclist, nbr, old_state);
+
+  funclist = ospf_opaque_type9_funclist;
+  opaque_lsa_nsm_change_callback (funclist, nbr, old_state);
+
+  funclist = ospf_opaque_type10_funclist;
+  opaque_lsa_nsm_change_callback (funclist, nbr, old_state);
+
+  funclist = ospf_opaque_type11_funclist;
+  opaque_lsa_nsm_change_callback (funclist, nbr, old_state);
+
+out:
+  return;
+}
+
+void
+ospf_opaque_config_write_router (struct vty *vty, struct ospf *ospf)
+{
+  struct list *funclist;
+
+  if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE))
+    vty_out (vty, " capability opaque%s", VTY_NEWLINE);
+
+  funclist = ospf_opaque_wildcard_funclist;
+  opaque_lsa_config_write_router_callback (funclist, vty);
+
+  funclist = ospf_opaque_type9_funclist;
+  opaque_lsa_config_write_router_callback (funclist, vty);
+
+  funclist = ospf_opaque_type10_funclist;
+  opaque_lsa_config_write_router_callback (funclist, vty);
+
+  funclist = ospf_opaque_type11_funclist;
+  opaque_lsa_config_write_router_callback (funclist, vty);
+
+  return;
+}
+
+void
+ospf_opaque_config_write_if (struct vty *vty, struct interface *ifp)
+{
+  struct list *funclist;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  opaque_lsa_config_write_if_callback (funclist, vty, ifp);
+
+  funclist = ospf_opaque_type9_funclist;
+  opaque_lsa_config_write_if_callback (funclist, vty, ifp);
+
+  funclist = ospf_opaque_type10_funclist;
+  opaque_lsa_config_write_if_callback (funclist, vty, ifp);
+
+  funclist = ospf_opaque_type11_funclist;
+  opaque_lsa_config_write_if_callback (funclist, vty, ifp);
+
+  return;
+}
+
+void
+ospf_opaque_config_write_debug (struct vty *vty)
+{
+  struct list *funclist;
+
+  funclist = ospf_opaque_wildcard_funclist;
+  opaque_lsa_config_write_debug_callback (funclist, vty);
+
+  funclist = ospf_opaque_type9_funclist;
+  opaque_lsa_config_write_debug_callback (funclist, vty);
+
+  funclist = ospf_opaque_type10_funclist;
+  opaque_lsa_config_write_debug_callback (funclist, vty);
+
+  funclist = ospf_opaque_type11_funclist;
+  opaque_lsa_config_write_debug_callback (funclist, vty);
+
+  return;
+}
+
+void
+show_opaque_info_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  struct lsa_header *lsah = (struct lsa_header *) lsa->data;
+  u_int32_t lsid = ntohl (lsah->id.s_addr);
+  u_char    opaque_type = GET_OPAQUE_TYPE (lsid);
+  u_int32_t opaque_id = GET_OPAQUE_ID (lsid);
+  struct ospf_opaque_functab *functab;
+
+  /* Switch output functionality by vty address. */
+  if (vty != NULL)
+    {
+      vty_out (vty, "  Opaque-Type %u (%s)%s", opaque_type,
+              ospf_opaque_type_name (opaque_type), VTY_NEWLINE);
+      vty_out (vty, "  Opaque-ID   0x%x%s", opaque_id, VTY_NEWLINE);
+
+      vty_out (vty, "  Opaque-Info: %u octets of data%s%s",
+               ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE,
+               VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)",
+               VTY_NEWLINE);
+    }
+  else
+    {
+      zlog_debug ("    Opaque-Type %u (%s)", opaque_type,
+                ospf_opaque_type_name (opaque_type));
+      zlog_debug ("    Opaque-ID   0x%x", opaque_id);
+
+      zlog_debug ("    Opaque-Info: %u octets of data%s",
+               ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE,
+               VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)");
+    }
+
+  /* Call individual output functions. */
+  if ((functab = ospf_opaque_functab_lookup (lsa)) != NULL)
+    if (functab->show_opaque_info != NULL)
+      (* functab->show_opaque_info)(vty, lsa);
+
+  return;
+}
+
+void
+ospf_opaque_lsa_dump (struct stream *s, u_int16_t length)
+{
+  struct ospf_lsa lsa;
+
+  lsa.data = (struct lsa_header *) STREAM_PNT (s);
+  show_opaque_info_detail (NULL, &lsa);
+  return;
+}
+
+static int
+ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa)
+{
+  struct list *funclist;
+  int rc = -1;
+
+  /*
+   * Some Opaque-LSA user may want to monitor every LSA installation
+   * into the LSDB, regardless with target LSA type.
+   */
+  funclist = ospf_opaque_wildcard_funclist;
+  if (new_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type9_funclist;
+  if (new_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type10_funclist;
+  if (new_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type11_funclist;
+  if (new_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa)
+{
+  struct list *funclist;
+  int rc = -1;
+
+  /*
+   * Some Opaque-LSA user may want to monitor every LSA deletion
+   * from the LSDB, regardless with target LSA type.
+   */
+  funclist = ospf_opaque_wildcard_funclist;
+  if (del_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type9_funclist;
+  if (del_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type10_funclist;
+  if (del_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  funclist = ospf_opaque_type11_funclist;
+  if (del_lsa_callback (funclist, lsa) != 0)
+    goto out;
+
+  rc = 0;
+out:
+  return rc;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are Opaque-LSA origination/refresh management functions.
+ *------------------------------------------------------------------------*/
+
+static int ospf_opaque_type9_lsa_originate (struct thread *t);
+static int ospf_opaque_type10_lsa_originate (struct thread *t);
+static int ospf_opaque_type11_lsa_originate (struct thread *t);
+static void ospf_opaque_lsa_reoriginate_resume (struct list *listtop, void *arg);
+
+void
+ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0)
+{
+  struct ospf *top;
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+  struct opaque_info_per_type *oipt;
+  int delay = 0;
+
+  if ((top = oi_to_top (oi)) == NULL || (area = oi->area) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_originate_schedule: Invalid argument?");
+      goto out;
+    }
+
+  /* It may not a right time to schedule origination now. */
+  if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_opaque_lsa_originate_schedule: Not operational.");
+      goto out; /* This is not an error. */
+    }
+  
+  if (delay0 != NULL)
+    delay = *delay0;
+
+  /*
+   * There might be some entries that have been waiting for triggering
+   * of per opaque-type re-origination get resumed.
+   */
+  ospf_opaque_lsa_reoriginate_resume (  oi->opaque_lsa_self, (void *)   oi);
+  ospf_opaque_lsa_reoriginate_resume (area->opaque_lsa_self, (void *) area);
+  ospf_opaque_lsa_reoriginate_resume ( top->opaque_lsa_self, (void *)  top);
+
+  /*
+   * Now, schedule origination of all Opaque-LSAs per opaque-type.
+   */
+  if (! list_isempty (ospf_opaque_type9_funclist)
+  &&    list_isempty (oi->opaque_lsa_self)
+  &&    oi->t_opaque_lsa_self == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d ms later.", delay);
+      oi->t_opaque_lsa_self =
+       thread_add_timer_msec (master, ospf_opaque_type9_lsa_originate, oi, delay);
+      delay += top->min_ls_interval;
+    }
+
+  if (! list_isempty (ospf_opaque_type10_funclist)
+  &&    list_isempty (area->opaque_lsa_self)
+  &&    area->t_opaque_lsa_self == NULL)
+    {
+      /*
+       * One AREA may contain multiple OIs, but above 2nd and 3rd
+       * conditions prevent from scheduling the originate function
+       * again and again.
+       */
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d ms later.", delay);
+      area->t_opaque_lsa_self =
+        thread_add_timer_msec (master, ospf_opaque_type10_lsa_originate,
+                          area, delay);
+      delay += top->min_ls_interval;
+    }
+
+  if (! list_isempty (ospf_opaque_type11_funclist)
+  &&    list_isempty (top->opaque_lsa_self)
+  &&    top->t_opaque_lsa_self == NULL)
+    {
+      /*
+       * One OSPF may contain multiple AREAs, but above 2nd and 3rd
+       * conditions prevent from scheduling the originate function
+       * again and again.
+       */
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d ms later.", delay);
+      top->t_opaque_lsa_self =
+        thread_add_timer_msec (master, ospf_opaque_type11_lsa_originate,
+                          top, delay);
+      delay += top->min_ls_interval;
+    }
+
+  /*
+   * Following section treats a special situation that this node's
+   * opaque capability has changed as "ON -> OFF -> ON".
+   */
+  if (! list_isempty (ospf_opaque_type9_funclist)
+  &&  ! list_isempty (oi->opaque_lsa_self))
+    {
+      for (ALL_LIST_ELEMENTS (oi->opaque_lsa_self, node, nnode, oipt))
+        {
+         /* 
+          * removed the test for
+          *   (! list_isempty (oipt->id_list))   * Handler is already active. *
+           * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
+          * not being empty.
+          */
+          if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */
+              || oipt->status == PROC_SUSPEND)   /* Cannot originate now. */
+              continue;
+
+          ospf_opaque_lsa_reoriginate_schedule ((void *) oi,
+            OSPF_OPAQUE_LINK_LSA, oipt->opaque_type);
+        }
+    }
+
+  if (! list_isempty (ospf_opaque_type10_funclist)
+  &&  ! list_isempty (area->opaque_lsa_self))
+    {
+      for (ALL_LIST_ELEMENTS (area->opaque_lsa_self, node, nnode, oipt))
+        {
+         /* 
+          * removed the test for
+          *   (! list_isempty (oipt->id_list))   * Handler is already active. *
+           * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
+          * not being empty.
+          */
+          if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */
+              || oipt->status == PROC_SUSPEND)   /* Cannot originate now. */
+            continue;
+
+          ospf_opaque_lsa_reoriginate_schedule ((void *) area,
+            OSPF_OPAQUE_AREA_LSA, oipt->opaque_type);
+        }
+    }
+
+  if (! list_isempty (ospf_opaque_type11_funclist)
+  &&  ! list_isempty (top->opaque_lsa_self))
+    {
+      for (ALL_LIST_ELEMENTS (top->opaque_lsa_self, node, nnode, oipt))
+        {
+         /* 
+          * removed the test for
+          *   (! list_isempty (oipt->id_list))   * Handler is already active. *
+           * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list)
+          * not being empty.
+          */
+          if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */
+              || oipt->status == PROC_SUSPEND)   /* Cannot originate now. */
+            continue;
+
+          ospf_opaque_lsa_reoriginate_schedule ((void *) top,
+            OSPF_OPAQUE_AS_LSA, oipt->opaque_type);
+        }
+    }
+
+  if (delay0 != NULL)
+    *delay0 = delay;
+
+out:
+  return;
+}
+
+static int
+ospf_opaque_type9_lsa_originate (struct thread *t)
+{
+  struct ospf_interface *oi;
+  int rc;
+
+  oi = THREAD_ARG (t);
+  oi->t_opaque_lsa_self = NULL;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s",
+                IF_NAME (oi));
+
+  rc = opaque_lsa_originate_callback (ospf_opaque_type9_funclist, oi);
+
+  return rc;
+}
+
+static int
+ospf_opaque_type10_lsa_originate (struct thread *t)
+{
+  struct ospf_area *area;
+  int rc;
+
+  area = THREAD_ARG (t);
+  area->t_opaque_lsa_self = NULL;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type10-LSA]: Originate Opaque-LSAs for Area %s",
+                inet_ntoa (area->area_id));
+
+  rc = opaque_lsa_originate_callback (ospf_opaque_type10_funclist, area);
+
+  return rc;
+}
+
+static int
+ospf_opaque_type11_lsa_originate (struct thread *t)
+{
+  struct ospf *top;
+  int rc;
+
+  top = THREAD_ARG (t);
+  top->t_opaque_lsa_self = NULL;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type11-LSA]: Originate AS-External Opaque-LSAs");
+
+  rc = opaque_lsa_originate_callback (ospf_opaque_type11_funclist, top);
+
+  return rc;
+}
+
+static void
+ospf_opaque_lsa_reoriginate_resume (struct list *listtop, void *arg)
+{
+  struct listnode *node, *nnode;
+  struct opaque_info_per_type *oipt;
+  struct ospf_opaque_functab *functab;
+
+  if (listtop == NULL)
+    goto out;
+
+  /*
+   * Pickup oipt entries those which in SUSPEND status, and give
+   * them a chance to start re-origination now.
+   */
+  for (ALL_LIST_ELEMENTS (listtop, node, nnode, oipt))
+    {
+      if (oipt->status != PROC_SUSPEND)
+          continue;
+
+      oipt->status = PROC_NORMAL;
+
+      if ((functab = oipt->functab) == NULL
+          || functab->lsa_originator  == NULL)
+        continue;
+
+      if ((* functab->lsa_originator)(arg) != 0)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_resume: Failed (opaque-type=%u)", oipt->opaque_type);
+          continue;
+        }
+    }
+
+out:
+  return;
+}
+
+struct ospf_lsa *
+ospf_opaque_lsa_install (struct ospf_lsa *lsa, int rt_recalc)
+{
+  struct ospf_lsa *new = NULL;
+  struct opaque_info_per_type *oipt;
+  struct opaque_info_per_id *oipi;
+  struct ospf *top;
+
+  /* Don't take "rt_recalc" into consideration for now. *//* XXX */
+
+  if (! IS_LSA_SELF (lsa))
+    {
+      new = lsa; /* Don't touch this LSA. */
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_INSTALL))
+    zlog_debug ("Install Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)));
+
+  /* Replace the existing lsa with the new one. */
+  if ((oipt = lookup_opaque_info_by_type (lsa)) != NULL
+      && (oipi = lookup_opaque_info_by_id (oipt, lsa)) != NULL)
+    {
+      ospf_lsa_unlock (&oipi->lsa);
+      oipi->lsa = ospf_lsa_lock (lsa);
+    }
+  /* Register the new lsa entry and get its control info. */
+  else
+  if ((oipi = register_opaque_lsa (lsa)) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_install: register_opaque_lsa() ?");
+      goto out;
+    }
+
+  /*
+   * Make use of a common mechanism (ospf_lsa_refresh_walker)
+   * for periodic refresh of self-originated Opaque-LSAs.
+   */
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      if ((top = oi_to_top (lsa->oi)) == NULL)
+        {
+          /* Above conditions must have passed. */
+          zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?");
+          goto out;
+        }
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      if (lsa->area == NULL || (top = lsa->area->ospf) == NULL)
+        {
+          /* Above conditions must have passed. */
+          zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?");
+          goto out;
+        }
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      top = ospf_lookup ();
+      if (lsa->area != NULL && (top = lsa->area->ospf) == NULL)
+        {
+          /* Above conditions must have passed. */
+          zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?");
+          goto out;
+        }
+      break;
+    default:
+      zlog_warn ("ospf_opaque_lsa_install: Unexpected LSA-type(%u)", lsa->data->type);
+      goto out;
+    }
+
+  ospf_refresher_register_lsa (top, lsa);
+  new = lsa;
+
+out:
+  return new;
+}
+
+struct ospf_lsa *
+ospf_opaque_lsa_refresh (struct ospf_lsa *lsa)
+{
+  struct ospf *ospf;
+  struct ospf_opaque_functab *functab;
+  struct ospf_lsa *new = NULL;
+  
+  ospf = ospf_lookup ();
+
+  if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL
+      || functab->lsa_refresher == NULL)
+    {
+      /*
+       * Though this LSA seems to have originated on this node, the
+       * handling module for this "lsa-type and opaque-type" was
+       * already deleted sometime ago.
+       * Anyway, this node still has a responsibility to flush this
+       * LSA from the routing domain.
+       */
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("LSA[Type%d:%s]: Flush stray Opaque-LSA", lsa->data->type, inet_ntoa (lsa->data->id));
+
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+      ospf_lsa_flush (ospf, lsa);
+    }
+  else
+    new = (* functab->lsa_refresher)(lsa);
+
+  return new;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are re-origination/refresh/flush operations of Opaque-LSAs,
+ * triggered by external interventions (vty session, signaling, etc).
+ *------------------------------------------------------------------------*/
+
+#define OSPF_OPAQUE_TIMER_ON(T,F,L,V) \
+      if (!(T)) \
+        (T) = thread_add_timer_msec (master, (F), (L), (V))
+
+static struct ospf_lsa *pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, u_char lsa_type, u_char opaque_type);
+static int ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t);
+static int ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t);
+static int ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t);
+static int ospf_opaque_lsa_refresh_timer (struct thread *t);
+
+void
+ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent,
+                                      u_char lsa_type, u_char opaque_type)
+{
+  struct ospf *top;
+  struct ospf_area dummy, *area = NULL;
+  struct ospf_interface *oi = NULL;
+
+  struct ospf_lsa *lsa;
+  struct opaque_info_per_type *oipt;
+  int (*func) (struct thread * t) = NULL;
+  int delay;
+
+  switch (lsa_type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      if ((oi = (struct ospf_interface *) lsa_type_dependent) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " Type-9 Opaque-LSA: Invalid parameter?");
+          goto out;
+        }
+      if ((top = oi_to_top (oi)) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: OI(%s) -> TOP?",
+                     IF_NAME (oi));
+          goto out;
+        }
+      if (!list_isempty (ospf_opaque_type9_funclist)
+          && list_isempty (oi->opaque_lsa_self)
+          && oi->t_opaque_lsa_self != NULL)
+        {
+          zlog_warn ("Type-9 Opaque-LSA (opaque_type=%u):"
+                     " Common origination for OI(%s) has already started",
+                     opaque_type, IF_NAME (oi));
+          goto out;
+        }
+      func = ospf_opaque_type9_lsa_reoriginate_timer;
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      if ((area = (struct ospf_area *) lsa_type_dependent) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " Type-10 Opaque-LSA: Invalid parameter?");
+          goto out;
+        }
+      if ((top = area->ospf) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " AREA(%s) -> TOP?", inet_ntoa (area->area_id));
+          goto out;
+        }
+      if (!list_isempty (ospf_opaque_type10_funclist)
+          && list_isempty (area->opaque_lsa_self)
+          && area->t_opaque_lsa_self != NULL)
+        {
+          zlog_warn ("Type-10 Opaque-LSA (opaque_type=%u):"
+                     " Common origination for AREA(%s) has already started",
+                     opaque_type, inet_ntoa (area->area_id));
+          goto out;
+        }
+      func = ospf_opaque_type10_lsa_reoriginate_timer;
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      if ((top = (struct ospf *) lsa_type_dependent) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " Type-11 Opaque-LSA: Invalid parameter?");
+          goto out;
+        }
+      if (!list_isempty (ospf_opaque_type11_funclist)
+          && list_isempty (top->opaque_lsa_self)
+          && top->t_opaque_lsa_self != NULL)
+        {
+          zlog_warn ("Type-11 Opaque-LSA (opaque_type=%u):"
+                     " Common origination has already started", opaque_type);
+          goto out;
+        }
+
+      /* Fake "area" to pass "ospf" to a lookup function later. */
+      dummy.ospf = top;
+      area = &dummy;
+
+      func = ospf_opaque_type11_lsa_reoriginate_timer;
+      break;
+    default:
+      zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                 " Unexpected LSA-type(%u)",
+                 lsa_type);
+      goto out;
+    }
+
+  /* It may not a right time to schedule reorigination now. */
+  if (!CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_opaque_lsa_reoriginate_schedule: Not operational.");
+      goto out;                 /* This is not an error. */
+    }
+  
+  /* Generate a dummy lsa to be passed for a lookup function. */
+  lsa = pseudo_lsa (oi, area, lsa_type, opaque_type);
+
+  if ((oipt = lookup_opaque_info_by_type (lsa)) == NULL)
+    {
+      struct ospf_opaque_functab *functab;
+      if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " No associated function?: lsa_type(%u),"
+                     " opaque_type(%u)",
+                     lsa_type, opaque_type);
+          goto out;
+        }
+      if ((oipt = register_opaque_info_per_type (functab, lsa)) == NULL)
+        {
+          zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:"
+                     " Cannot get a control info?: lsa_type(%u),"
+                     " opaque_type(%u)",
+                     lsa_type, opaque_type);
+          goto out;
+        }
+    }
+
+  if (oipt->t_opaque_lsa_self != NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Type-%u Opaque-LSA has already scheduled to"
+                   " RE-ORIGINATE: [opaque-type=%u]",
+                   lsa_type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)));
+      goto out;
+    }
+
+  /*
+   * Different from initial origination time, in which various conditions
+   * (opaque capability, neighbor status etc) are assured by caller of
+   * the originating function "ospf_opaque_lsa_originate_schedule ()",
+   * it is highly possible that these conditions might not be satisfied
+   * at the time of re-origination function is to be called.
+   */
+  delay = top->min_ls_interval; /* XXX */
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d"
+               " ms later: [opaque-type=%u]",
+               lsa_type, delay, 
+               GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)));
+
+  OSPF_OPAQUE_TIMER_ON (oipt->t_opaque_lsa_self, func, oipt, delay);
+
+out:
+  return;
+}
+
+static struct ospf_lsa *
+pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area,
+            u_char lsa_type, u_char opaque_type)
+{
+  static struct ospf_lsa lsa = { 0 };
+  static struct lsa_header lsah = { 0 };
+  u_int32_t tmp;
+
+  lsa.oi   = oi;
+  lsa.area = area;
+  lsa.data = &lsah;
+
+  lsah.type = lsa_type;
+  tmp = SET_OPAQUE_LSID (opaque_type, 0); /* Opaque-ID is unused here. */
+  lsah.id.s_addr = htonl (tmp);
+
+  return &lsa;
+}
+
+static int
+ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t)
+{
+  struct opaque_info_per_type *oipt;
+  struct ospf_opaque_functab *functab;
+  struct ospf *top;
+  struct ospf_interface *oi;
+  int rc = -1;
+
+  oipt = THREAD_ARG (t);
+  oipt->t_opaque_lsa_self = NULL;
+
+  if ((functab = oipt->functab) == NULL
+  ||   functab->lsa_originator == NULL)
+    {
+      zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: No associated function?");
+      goto out;
+    }
+
+  oi = (struct ospf_interface *) oipt->owner;
+  if ((top = oi_to_top (oi)) == NULL)
+    {
+      zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: Something wrong?");
+      goto out;
+    }
+
+  if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)
+  ||  ! ospf_if_is_enable (oi)
+  ||    ospf_nbr_count_opaque_capable (oi) == 0)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type);
+    
+      oipt->status = PROC_SUSPEND;
+      rc = 0;
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)", oipt->opaque_type, IF_NAME (oi));
+
+  rc = (* functab->lsa_originator)(oi);
+out:
+  return rc;
+}
+
+static int
+ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t)
+{
+  struct opaque_info_per_type *oipt;
+  struct ospf_opaque_functab *functab;
+  struct listnode *node, *nnode;
+  struct ospf *top;
+  struct ospf_area *area;
+  struct ospf_interface *oi;
+  int n, rc = -1;
+
+  oipt = THREAD_ARG (t);
+  oipt->t_opaque_lsa_self = NULL;
+
+  if ((functab = oipt->functab) == NULL
+  ||   functab->lsa_originator == NULL)
+    {
+      zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: No associated function?");
+      goto out;
+    }
+
+  area = (struct ospf_area *) oipt->owner;
+  if (area == NULL || (top = area->ospf) == NULL)
+    {
+      zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: Something wrong?");
+      goto out;
+    }
+
+  /* There must be at least one "opaque-capable, full-state" neighbor. */
+  n = 0;
+  for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi))
+    {
+      if ((n = ospf_nbr_count_opaque_capable (oi)) > 0)
+        break;
+    }
+
+  if (n == 0 || ! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Suspend re-origination of Type-10 Opaque-LSAs"
+                   " (opaque-type=%u) for a while...", 
+                   oipt->opaque_type);
+
+      oipt->status = PROC_SUSPEND;
+      rc = 0;
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type10-LSA]: Re-originate Opaque-LSAs"
+               " (opaque-type=%u) for Area %s", 
+               oipt->opaque_type, inet_ntoa (area->area_id));
+
+  rc = (* functab->lsa_originator)(area);
+out:
+  return rc;
+}
+
+static int
+ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t)
+{
+  struct opaque_info_per_type *oipt;
+  struct ospf_opaque_functab *functab;
+  struct ospf *top;
+  int rc = -1;
+
+  oipt = THREAD_ARG (t);
+  oipt->t_opaque_lsa_self = NULL;
+
+  if ((functab = oipt->functab) == NULL
+      || functab->lsa_originator == NULL)
+    {
+      zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer:"
+                 " No associated function?");
+      goto out;
+    }
+
+  if ((top = (struct ospf *) oipt->owner) == NULL)
+    {
+      zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer: Something wrong?");
+      goto out;
+    }
+
+  if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type);
+    
+      oipt->status = PROC_SUSPEND;
+      rc = 0;
+      goto out;
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).", oipt->opaque_type);
+
+  rc = (* functab->lsa_originator)(top);
+out:
+  return rc;
+}
+
+void
+ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa0)
+{
+  struct opaque_info_per_type *oipt;
+  struct opaque_info_per_id *oipi;
+  struct ospf_lsa *lsa;
+  struct ospf *top;
+  int delay;
+
+  if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL
+  ||  (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_refresh_schedule: Invalid parameter?");
+      goto out;
+    }
+
+  /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */
+  if ((lsa = oipi->lsa) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_refresh_schedule: Something wrong?");
+      goto out;
+    }
+
+  if (oipi->t_opaque_lsa_self != NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("Type-%u Opaque-LSA has already scheduled to REFRESH: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)));
+      goto out;
+    }
+
+  /* Delete this lsa from neighbor retransmit-list. */
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+      ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      top = ospf_lookup ();
+      if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL))
+        top = lsa0->area->ospf;
+      ospf_ls_retransmit_delete_nbr_as (top, lsa);
+      break;
+    default:
+      zlog_warn ("ospf_opaque_lsa_refresh_schedule: Unexpected LSA-type(%u)", lsa->data->type);
+      goto out;
+    }
+
+  delay = ospf_lsa_refresh_delay (lsa);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", lsa->data->type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)));
+
+  OSPF_OPAQUE_TIMER_ON (oipi->t_opaque_lsa_self,
+                        ospf_opaque_lsa_refresh_timer, oipi, delay * 1000);
+out:
+  return;
+}
+
+static int
+ospf_opaque_lsa_refresh_timer (struct thread *t)
+{
+  struct opaque_info_per_id *oipi;
+  struct ospf_opaque_functab *functab;
+  struct ospf_lsa *lsa;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)");
+
+  oipi = THREAD_ARG (t);
+  oipi->t_opaque_lsa_self = NULL;
+
+  if ((lsa = oipi->lsa) != NULL)
+    if ((functab = oipi->opqctl_type->functab) != NULL)
+      if (functab->lsa_refresher != NULL)
+        (* functab->lsa_refresher)(lsa);
+
+  return 0;
+}
+
+void
+ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0)
+{
+  struct opaque_info_per_type *oipt;
+  struct opaque_info_per_id *oipi;
+  struct ospf_lsa *lsa;
+  struct ospf *top;
+
+  top = ospf_lookup ();
+
+  if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL
+  ||  (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_flush_schedule: Invalid parameter?");
+      goto out;
+    }
+
+  /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */
+  if ((lsa = oipi->lsa) == NULL)
+    {
+      zlog_warn ("ospf_opaque_lsa_flush_schedule: Something wrong?");
+      goto out;
+    }
+
+  /* Delete this lsa from neighbor retransmit-list. */
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+    case OSPF_OPAQUE_AREA_LSA:
+      ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL))
+        top = lsa0->area->ospf;
+      ospf_ls_retransmit_delete_nbr_as (top, lsa);
+      break;
+    default:
+      zlog_warn ("ospf_opaque_lsa_flush_schedule: Unexpected LSA-type(%u)", lsa->data->type);
+      goto out;
+    }
+
+  /* Dequeue listnode entry from the list. */
+  listnode_delete (oipt->id_list, oipi);
+
+  /* Avoid misjudgement in the next lookup. */
+  if (listcount (oipt->id_list) == 0)
+    oipt->id_list->head = oipt->id_list->tail = NULL;
+
+  /* Disassociate internal control information with the given lsa. */
+  free_opaque_info_per_id ((void *) oipi);
+
+  /* Force given lsa's age to MaxAge. */
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)));
+
+  /* This lsa will be flushed and removed eventually. */
+  ospf_lsa_flush (top, lsa);
+
+out:
+  return;
+}
+
+void
+ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, 
+                                          struct ospf_lsa *lsa)
+{
+  struct ospf *top;
+  
+  if ((top = oi_to_top (nbr->oi)) == NULL)
+    return;
+
+  /*
+   * Since these LSA entries are not yet installed into corresponding
+   * LSDB, just flush them without calling ospf_ls_maxage() afterward.
+   */
+  lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);
+  switch (lsa->data->type)
+    {
+    case OSPF_OPAQUE_LINK_LSA:
+      ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa);
+      break;
+    case OSPF_OPAQUE_AREA_LSA:
+      ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa);
+      break;
+    case OSPF_OPAQUE_AS_LSA:
+      ospf_flood_through_as (top, NULL/*inbr*/, lsa);
+      break;
+    default:
+      zlog_warn ("ospf_opaque_self_originated_lsa_received: Unexpected LSA-type(%u)", lsa->data->type);
+      return;
+    }
+  ospf_lsa_discard (lsa); /* List "lsas" will be deleted by caller. */  
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are util functions; probably be used by Opaque-LSAs only...
+ *------------------------------------------------------------------------*/
+
+struct ospf *
+oi_to_top (struct ospf_interface *oi)
+{
+  struct ospf *top = NULL;
+  struct ospf_area *area;
+
+  if (oi == NULL || (area = oi->area) == NULL || (top = area->ospf) == NULL)
+    zlog_warn ("Broken relationship for \"OI -> AREA -> OSPF\"?");
+
+  return top;
+}
+
diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h
new file mode 100644 (file)
index 0000000..2ac9b41
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This is an implementation of rfc2370.
+ * Copyright (C) 2001 KDD R&D Laboratories, Inc.
+ * http://www.kddlabs.co.jp/
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_OPAQUE_H
+#define _ZEBRA_OSPF_OPAQUE_H
+
+#include "vty.h"
+
+#define        IS_OPAQUE_LSA(type) \
+       ((type) == OSPF_OPAQUE_LINK_LSA  || \
+        (type) == OSPF_OPAQUE_AREA_LSA  || \
+        (type) == OSPF_OPAQUE_AS_LSA)
+
+/*
+ * Opaque LSA's link state ID is redefined as follows.
+ *
+ *        24       16        8        0
+ * +--------+--------+--------+--------+
+ * |tttttttt|........|........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<------- Opaque ID ------>|
+ */
+#define LSID_OPAQUE_TYPE_MASK  0xff000000      /*  8 bits */
+#define LSID_OPAQUE_ID_MASK    0x00ffffff      /* 24 bits */
+
+#define        GET_OPAQUE_TYPE(lsid) \
+       (((u_int32_t)(lsid) & LSID_OPAQUE_TYPE_MASK) >> 24)
+
+#define        GET_OPAQUE_ID(lsid) \
+        ((u_int32_t)(lsid) & LSID_OPAQUE_ID_MASK)
+
+#define        SET_OPAQUE_LSID(type, id) \
+       ((((type) << 24) & LSID_OPAQUE_TYPE_MASK) \
+       | ((id)          & LSID_OPAQUE_ID_MASK))
+
+/*
+ * Opaque LSA types will be assigned by IANA.
+ * <http://www.iana.org/assignments/ospf-opaque-types>
+ */
+#define OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA            1
+#define OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC     2
+#define OPAQUE_TYPE_GRACE_LSA                          3
+#define OPAQUE_TYPE_L1VPN_LSA                          5
+#define OPAQUE_TYPE_ROUTER_INFORMATION_LSA             4
+#define OPAQUE_TYPE_INTER_AS_LSA                       6
+#define OPAQUE_TYPE_MAX                                6
+
+/* Followings types are proposed in internet-draft documents. */
+#define OPAQUE_TYPE_8021_QOSPF                         129
+#define OPAQUE_TYPE_SECONDARY_NEIGHBOR_DISCOVERY       224
+#define OPAQUE_TYPE_FLOODGATE                           225
+
+/* Ugly hack to make use of an unallocated value for wildcard matching! */
+#define OPAQUE_TYPE_WILDCARD                           0
+
+#define OPAQUE_TYPE_RANGE_UNASSIGNED(type) \
+       ( OPAQUE_TYPE_MAX  <= (type) && (type) <= 127)
+
+#define OPAQUE_TYPE_RANGE_RESERVED(type) \
+       (127 <  (type) && (type) <= 255)
+
+#define VALID_OPAQUE_INFO_LEN(lsahdr) \
+       ((ntohs((lsahdr)->length) >= sizeof (struct lsa_header)) && \
+       ((ntohs((lsahdr)->length) %  sizeof (u_int32_t)) == 0))
+
+/* Prototypes. */
+
+extern void ospf_opaque_init (void);
+extern void ospf_opaque_term (void);
+extern int ospf_opaque_type9_lsa_init (struct ospf_interface *oi);
+extern void ospf_opaque_type9_lsa_term (struct ospf_interface *oi);
+extern int ospf_opaque_type10_lsa_init (struct ospf_area *area);
+extern void ospf_opaque_type10_lsa_term (struct ospf_area *area);
+extern int ospf_opaque_type11_lsa_init (struct ospf *ospf);
+extern void ospf_opaque_type11_lsa_term (struct ospf *ospf);
+
+extern int
+ospf_register_opaque_functab (
+  u_char lsa_type,
+  u_char opaque_type,
+  int (* new_if_hook)(struct interface *ifp),
+  int (* del_if_hook)(struct interface *ifp),
+  void (* ism_change_hook)(struct ospf_interface *oi, int old_status),
+  void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status),
+  void (* config_write_router)(struct vty *vty),
+  void (* config_write_if    )(struct vty *vty, struct interface *ifp),
+  void (* config_write_debug )(struct vty *vty),
+  void (* show_opaque_info   )(struct vty *vty, struct ospf_lsa *lsa),
+  int  (* lsa_originator)(void *arg),
+  struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa),
+  int (* new_lsa_hook)(struct ospf_lsa *lsa),
+  int (* del_lsa_hook)(struct ospf_lsa *lsa)
+);
+extern void ospf_delete_opaque_functab (u_char lsa_type, u_char opaque_type);
+
+extern int ospf_opaque_new_if (struct interface *ifp);
+extern int ospf_opaque_del_if (struct interface *ifp);
+extern void ospf_opaque_ism_change (struct ospf_interface *oi,
+                                   int old_status);
+extern void ospf_opaque_nsm_change (struct ospf_neighbor *nbr,
+                                   int old_status);
+extern void ospf_opaque_config_write_router (struct vty *vty, struct ospf *);
+extern void ospf_opaque_config_write_if (struct vty *vty,
+                                        struct interface *ifp);
+extern void ospf_opaque_config_write_debug (struct vty *vty);
+extern void show_opaque_info_detail (struct vty *vty, struct ospf_lsa *lsa);
+extern void ospf_opaque_lsa_dump (struct stream *s, u_int16_t length);
+
+extern void ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi,
+                                               int *init_delay);
+extern struct ospf_lsa *ospf_opaque_lsa_install (struct ospf_lsa *,
+                                                int rt_recalc);
+extern struct ospf_lsa *ospf_opaque_lsa_refresh (struct ospf_lsa *lsa);
+
+extern void ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent,
+                                                 u_char lsa_type,
+                                                 u_char opaque_type);
+extern void ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa);
+extern void ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa);
+
+extern void ospf_opaque_self_originated_lsa_received (struct ospf_neighbor
+                                                     *nbr,
+                                                     struct ospf_lsa *lsa);
+extern struct ospf *oi_to_top (struct ospf_interface *oi);
+
+#endif /* _ZEBRA_OSPF_OPAQUE_H */
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
new file mode 100644 (file)
index 0000000..facba89
--- /dev/null
@@ -0,0 +1,3912 @@
+/*
+ * OSPF Sending and Receiving OSPF Packets.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "sockunion.h"
+#include "stream.h"
+#include "log.h"
+#include "sockopt.h"
+#include "checksum.h"
+#include "md5.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_dump.h"
+
+/* Packet Type String. */
+const struct message ospf_packet_type_str[] =
+{
+  { OSPF_MSG_HELLO,   "Hello"                     },
+  { OSPF_MSG_DB_DESC, "Database Description"      },
+  { OSPF_MSG_LS_REQ,  "Link State Request"        },
+  { OSPF_MSG_LS_UPD,  "Link State Update"         },
+  { OSPF_MSG_LS_ACK,  "Link State Acknowledgment" },
+};
+const size_t ospf_packet_type_str_max = sizeof (ospf_packet_type_str) /
+  sizeof (ospf_packet_type_str[0]);
+
+/* Minimum (besides OSPF_HEADER_SIZE) lengths for OSPF packets of
+   particular types, offset is the "type" field of a packet. */
+static const u_int16_t ospf_packet_minlen[] =
+{
+  0,
+  OSPF_HELLO_MIN_SIZE,
+  OSPF_DB_DESC_MIN_SIZE,
+  OSPF_LS_REQ_MIN_SIZE,
+  OSPF_LS_UPD_MIN_SIZE,
+  OSPF_LS_ACK_MIN_SIZE,
+};
+
+/* Minimum (besides OSPF_LSA_HEADER_SIZE) lengths for LSAs of particular
+   types, offset is the "LSA type" field. */
+static const u_int16_t ospf_lsa_minlen[] =
+{
+  0,
+  OSPF_ROUTER_LSA_MIN_SIZE,
+  OSPF_NETWORK_LSA_MIN_SIZE,
+  OSPF_SUMMARY_LSA_MIN_SIZE,
+  OSPF_SUMMARY_LSA_MIN_SIZE,
+  OSPF_AS_EXTERNAL_LSA_MIN_SIZE,
+  0,
+  OSPF_AS_EXTERNAL_LSA_MIN_SIZE,
+  0,
+  0,
+  0,
+  0,
+};
+
+/* for ospf_check_auth() */
+static int ospf_check_sum (struct ospf_header *);
+
+/* OSPF authentication checking function */
+static int
+ospf_auth_type (struct ospf_interface *oi)
+{
+  int auth_type;
+
+  if (OSPF_IF_PARAM (oi, auth_type) == OSPF_AUTH_NOTSET)
+    auth_type = oi->area->auth_type;
+  else
+    auth_type = OSPF_IF_PARAM (oi, auth_type);
+
+  /* Handle case where MD5 key list is not configured aka Cisco */
+  if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC &&
+      list_isempty (OSPF_IF_PARAM (oi, auth_crypt)))
+    return OSPF_AUTH_NULL;
+  
+  return auth_type;
+
+}
+
+struct ospf_packet *
+ospf_packet_new (size_t size)
+{
+  struct ospf_packet *new;
+
+  new = XCALLOC (MTYPE_OSPF_PACKET, sizeof (struct ospf_packet));
+  new->s = stream_new (size);
+
+  return new;
+}
+
+void
+ospf_packet_free (struct ospf_packet *op)
+{
+  if (op->s)
+    stream_free (op->s);
+
+  XFREE (MTYPE_OSPF_PACKET, op);
+
+  op = NULL;
+}
+
+struct ospf_fifo *
+ospf_fifo_new ()
+{
+  struct ospf_fifo *new;
+
+  new = XCALLOC (MTYPE_OSPF_FIFO, sizeof (struct ospf_fifo));
+  return new;
+}
+
+/* Add new packet to fifo. */
+void
+ospf_fifo_push (struct ospf_fifo *fifo, struct ospf_packet *op)
+{
+  if (fifo->tail)
+    fifo->tail->next = op;
+  else
+    fifo->head = op;
+
+  fifo->tail = op;
+
+  fifo->count++;
+}
+
+/* Add new packet to head of fifo. */
+static void
+ospf_fifo_push_head (struct ospf_fifo *fifo, struct ospf_packet *op)
+{
+  op->next = fifo->head;
+  
+  if (fifo->tail == NULL)
+    fifo->tail = op;
+  
+  fifo->head = op;
+  
+  fifo->count++;
+}
+
+/* Delete first packet from fifo. */
+struct ospf_packet *
+ospf_fifo_pop (struct ospf_fifo *fifo)
+{
+  struct ospf_packet *op;
+
+  op = fifo->head;
+
+  if (op)
+    {
+      fifo->head = op->next;
+
+      if (fifo->head == NULL)
+       fifo->tail = NULL;
+
+      fifo->count--;
+    }
+
+  return op;
+}
+
+/* Return first fifo entry. */
+struct ospf_packet *
+ospf_fifo_head (struct ospf_fifo *fifo)
+{
+  return fifo->head;
+}
+
+/* Flush ospf packet fifo. */
+void
+ospf_fifo_flush (struct ospf_fifo *fifo)
+{
+  struct ospf_packet *op;
+  struct ospf_packet *next;
+
+  for (op = fifo->head; op; op = next)
+    {
+      next = op->next;
+      ospf_packet_free (op);
+    }
+  fifo->head = fifo->tail = NULL;
+  fifo->count = 0;
+}
+
+/* Free ospf packet fifo. */
+void
+ospf_fifo_free (struct ospf_fifo *fifo)
+{
+  ospf_fifo_flush (fifo);
+
+  XFREE (MTYPE_OSPF_FIFO, fifo);
+}
+
+void
+ospf_packet_add (struct ospf_interface *oi, struct ospf_packet *op)
+{
+  if (!oi->obuf)
+    {
+      zlog_err("ospf_packet_add(interface %s in state %d [%s], packet type %s, "
+              "destination %s) called with NULL obuf, ignoring "
+              "(please report this bug)!\n",
+              IF_NAME(oi), oi->state, LOOKUP (ospf_ism_state_msg, oi->state),
+              LOOKUP (ospf_packet_type_str, stream_getc_from(op->s, 1)),
+              inet_ntoa (op->dst));
+      return;
+    }
+
+  /* Add packet to end of queue. */
+  ospf_fifo_push (oi->obuf, op);
+
+  /* Debug of packet fifo*/
+  /* ospf_fifo_debug (oi->obuf); */
+}
+
+static void
+ospf_packet_add_top (struct ospf_interface *oi, struct ospf_packet *op)
+{
+  if (!oi->obuf)
+    {
+      zlog_err("ospf_packet_add(interface %s in state %d [%s], packet type %s, "
+              "destination %s) called with NULL obuf, ignoring "
+              "(please report this bug)!\n",
+              IF_NAME(oi), oi->state, LOOKUP (ospf_ism_state_msg, oi->state),
+              LOOKUP (ospf_packet_type_str, stream_getc_from(op->s, 1)),
+              inet_ntoa (op->dst));
+      return;
+    }
+
+  /* Add packet to head of queue. */
+  ospf_fifo_push_head (oi->obuf, op);
+
+  /* Debug of packet fifo*/
+  /* ospf_fifo_debug (oi->obuf); */
+}
+
+void
+ospf_packet_delete (struct ospf_interface *oi)
+{
+  struct ospf_packet *op;
+  
+  op = ospf_fifo_pop (oi->obuf);
+
+  if (op)
+    ospf_packet_free (op);
+}
+
+struct ospf_packet *
+ospf_packet_dup (struct ospf_packet *op)
+{
+  struct ospf_packet *new;
+
+  if (stream_get_endp(op->s) != op->length)
+    /* XXX size_t */
+    zlog_warn ("ospf_packet_dup stream %lu ospf_packet %u size mismatch",
+              (u_long)STREAM_SIZE(op->s), op->length);
+
+  /* Reserve space for MD5 authentication that may be added later. */
+  new = ospf_packet_new (stream_get_endp(op->s) + OSPF_AUTH_MD5_SIZE);
+  stream_copy (new->s, op->s);
+
+  new->dst = op->dst;
+  new->length = op->length;
+
+  return new;
+}
+
+/* XXX inline */
+static unsigned int
+ospf_packet_authspace (struct ospf_interface *oi)
+{
+  int auth = 0;
+
+  if ( ospf_auth_type (oi) == OSPF_AUTH_CRYPTOGRAPHIC)
+    auth = OSPF_AUTH_MD5_SIZE;
+
+  return auth;
+}
+
+static unsigned int
+ospf_packet_max (struct ospf_interface *oi)
+{
+  int max;
+
+  max = oi->ifp->mtu - ospf_packet_authspace(oi);
+
+  max -= (OSPF_HEADER_SIZE + sizeof (struct ip));
+
+  return max;
+}
+
+
+static int
+ospf_check_md5_digest (struct ospf_interface *oi, struct ospf_header *ospfh)
+{
+  MD5_CTX ctx;
+  unsigned char digest[OSPF_AUTH_MD5_SIZE];
+  struct crypt_key *ck;
+  struct ospf_neighbor *nbr;
+  u_int16_t length = ntohs (ospfh->length);
+  
+  /* Get secret key. */
+  ck = ospf_crypt_key_lookup (OSPF_IF_PARAM (oi, auth_crypt),
+                             ospfh->u.crypt.key_id);
+  if (ck == NULL)
+    {
+      zlog_warn ("interface %s: ospf_check_md5 no key %d",
+                IF_NAME (oi), ospfh->u.crypt.key_id);
+      return 0;
+    }
+
+  /* check crypto seqnum. */
+  nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id);
+
+  if (nbr && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum))
+    {
+      zlog_warn ("interface %s: ospf_check_md5 bad sequence %d (expect %d)",
+                IF_NAME (oi),
+                ntohl(ospfh->u.crypt.crypt_seqnum),
+                ntohl(nbr->crypt_seqnum));
+      return 0;
+    }
+      
+  /* Generate a digest for the ospf packet - their digest + our digest. */
+  memset(&ctx, 0, sizeof(ctx));
+  MD5Init(&ctx);
+  MD5Update(&ctx, ospfh, length);
+  MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE);
+  MD5Final(digest, &ctx);
+
+  /* compare the two */
+  if (memcmp ((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE))
+    {
+      zlog_warn ("interface %s: ospf_check_md5 checksum mismatch",
+                IF_NAME (oi));
+      return 0;
+    }
+
+  /* save neighbor's crypt_seqnum */
+  if (nbr)
+    nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum;
+  return 1;
+}
+
+/* This function is called from ospf_write(), it will detect the
+   authentication scheme and if it is MD5, it will change the sequence
+   and update the MD5 digest. */
+static int
+ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op)
+{
+  struct ospf_header *ospfh;
+  unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0};
+  MD5_CTX ctx;
+  void *ibuf;
+  u_int32_t t;
+  struct crypt_key *ck;
+  const u_int8_t *auth_key;
+
+  ibuf = STREAM_DATA (op->s);
+  ospfh = (struct ospf_header *) ibuf;
+
+  if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
+    return 0;
+
+  /* We do this here so when we dup a packet, we don't have to
+     waste CPU rewriting other headers.
+     
+     Note that quagga_time /deliberately/ is not used here */
+  t = (time(NULL) & 0xFFFFFFFF);
+  if (t > oi->crypt_seqnum)
+    oi->crypt_seqnum = t;
+  else
+    oi->crypt_seqnum++;
+  
+  ospfh->u.crypt.crypt_seqnum = htonl (oi->crypt_seqnum); 
+
+  /* Get MD5 Authentication key from auth_key list. */
+  if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt)))
+    auth_key = (const u_int8_t *) digest;
+  else
+    {
+      ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt)));
+      auth_key = ck->auth_key;
+    }
+
+  /* Generate a digest for the entire packet + our secret key. */
+  memset(&ctx, 0, sizeof(ctx));
+  MD5Init(&ctx);
+  MD5Update(&ctx, ibuf, ntohs (ospfh->length));
+  MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+  MD5Final(digest, &ctx);
+
+  /* Append md5 digest to the end of the stream. */
+  stream_put (op->s, digest, OSPF_AUTH_MD5_SIZE);
+
+  /* We do *NOT* increment the OSPF header length. */
+  op->length = ntohs (ospfh->length) + OSPF_AUTH_MD5_SIZE;
+
+  if (stream_get_endp(op->s) != op->length)
+    /* XXX size_t */
+    zlog_warn("ospf_make_md5_digest: length mismatch stream %lu ospf_packet %u",
+             (u_long)stream_get_endp(op->s), op->length);
+
+  return OSPF_AUTH_MD5_SIZE;
+}
+
+
+static int
+ospf_ls_req_timer (struct thread *thread)
+{
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  nbr->t_ls_req = NULL;
+
+  /* Send Link State Request. */
+  if (ospf_ls_request_count (nbr))
+    ospf_ls_req_send (nbr);
+
+  /* Set Link State Request retransmission timer. */
+  OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req);
+
+  return 0;
+}
+
+void
+ospf_ls_req_event (struct ospf_neighbor *nbr)
+{
+  if (nbr->t_ls_req)
+    {
+      thread_cancel (nbr->t_ls_req);
+      nbr->t_ls_req = NULL;
+    }
+  nbr->t_ls_req = thread_add_event (master, ospf_ls_req_timer, nbr, 0);
+}
+
+/* Cyclic timer function.  Fist registered in ospf_nbr_new () in
+   ospf_neighbor.c  */
+int
+ospf_ls_upd_timer (struct thread *thread)
+{
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  nbr->t_ls_upd = NULL;
+
+  /* Send Link State Update. */
+  if (ospf_ls_retransmit_count (nbr) > 0)
+    {
+      struct list *update;
+      struct ospf_lsdb *lsdb;
+      int i;
+      int retransmit_interval;
+
+      retransmit_interval = OSPF_IF_PARAM (nbr->oi, retransmit_interval);
+
+      lsdb = &nbr->ls_rxmt;
+      update = list_new ();
+
+      for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+       {
+         struct route_table *table = lsdb->type[i].db;
+         struct route_node *rn;
+         
+         for (rn = route_top (table); rn; rn = route_next (rn))
+           {
+             struct ospf_lsa *lsa;
+             
+             if ((lsa = rn->info) != NULL)
+               /* Don't retransmit an LSA if we received it within
+                 the last RxmtInterval seconds - this is to allow the
+                 neighbour a chance to acknowledge the LSA as it may
+                 have ben just received before the retransmit timer
+                 fired.  This is a small tweak to what is in the RFC,
+                 but it will cut out out a lot of retransmit traffic
+                 - MAG */
+               if (tv_cmp (tv_sub (recent_relative_time (), lsa->tv_recv), 
+                           int2tv (retransmit_interval)) >= 0)
+                 listnode_add (update, rn->info);
+           }
+       }
+
+      if (listcount (update) > 0)
+       ospf_ls_upd_send (nbr, update, OSPF_SEND_PACKET_DIRECT);
+      list_delete (update);
+    }
+
+  /* Set LS Update retransmission timer. */
+  OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd);
+
+  return 0;
+}
+
+int
+ospf_ls_ack_timer (struct thread *thread)
+{
+  struct ospf_interface *oi;
+
+  oi = THREAD_ARG (thread);
+  oi->t_ls_ack = NULL;
+
+  /* Send Link State Acknowledgment. */
+  if (listcount (oi->ls_ack) > 0)
+    ospf_ls_ack_send_delayed (oi);
+
+  /* Set LS Ack timer. */
+  OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
+
+  return 0;
+}
+
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+static void
+ospf_write_frags (int fd, struct ospf_packet *op, struct ip *iph, 
+                  struct msghdr *msg, unsigned int maxdatasize, 
+                  unsigned int mtu, int flags, u_char type)
+{
+#define OSPF_WRITE_FRAG_SHIFT 3
+  u_int16_t offset;
+  struct iovec *iovp;
+  int ret;
+
+  assert ( op->length == stream_get_endp(op->s) );
+  assert (msg->msg_iovlen == 2);
+
+  /* we can but try.
+   *
+   * SunOS, BSD and BSD derived kernels likely will clear ip_id, as
+   * well as the IP_MF flag, making this all quite pointless.
+   *
+   * However, for a system on which IP_MF is left alone, and ip_id left
+   * alone or else which sets same ip_id for each fragment this might
+   * work, eg linux.
+   *
+   * XXX-TODO: It would be much nicer to have the kernel's use their
+   * existing fragmentation support to do this for us. Bugs/RFEs need to
+   * be raised against the various kernels.
+   */
+  
+  /* set More Frag */
+  iph->ip_off |= IP_MF;
+  
+  /* ip frag offset is expressed in units of 8byte words */
+  offset = maxdatasize >> OSPF_WRITE_FRAG_SHIFT;
+  
+  iovp = &msg->msg_iov[1];
+  
+  while ( (stream_get_endp(op->s) - stream_get_getp (op->s)) 
+         > maxdatasize )
+    {
+      /* data length of this frag is to next offset value */
+      iovp->iov_len = offset << OSPF_WRITE_FRAG_SHIFT;
+      iph->ip_len = iovp->iov_len + sizeof (struct ip);
+      assert (iph->ip_len <= mtu);
+
+      sockopt_iphdrincl_swab_htosys (iph);
+
+      ret = sendmsg (fd, msg, flags);
+      
+      sockopt_iphdrincl_swab_systoh (iph);
+      
+      if (ret < 0)
+        zlog_warn ("*** ospf_write_frags: sendmsg failed to %s,"
+                  " id %d, off %d, len %d, mtu %u failed with %s",
+                  inet_ntoa (iph->ip_dst),
+                  iph->ip_id,
+                  iph->ip_off,
+                  iph->ip_len,
+                  mtu,
+                  safe_strerror (errno));
+      
+      if (IS_DEBUG_OSPF_PACKET (type - 1, SEND))
+        {
+          zlog_debug ("ospf_write_frags: sent id %d, off %d, len %d to %s\n",
+                     iph->ip_id, iph->ip_off, iph->ip_len,
+                     inet_ntoa (iph->ip_dst));
+          if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL))
+            {
+              zlog_debug ("-----------------IP Header Dump----------------------");
+              ospf_ip_header_dump (iph);
+              zlog_debug ("-----------------------------------------------------");
+            }
+        }
+      
+      iph->ip_off += offset;
+      stream_forward_getp (op->s, iovp->iov_len);
+      iovp->iov_base = STREAM_PNT (op->s); 
+    }
+    
+  /* setup for final fragment */
+  iovp->iov_len = stream_get_endp(op->s) - stream_get_getp (op->s);
+  iph->ip_len = iovp->iov_len + sizeof (struct ip);
+  iph->ip_off &= (~IP_MF);
+}
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
+static int
+ospf_write (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+  struct ospf_interface *oi;
+  struct ospf_packet *op;
+  struct sockaddr_in sa_dst;
+  struct ip iph;
+  struct msghdr msg;
+  struct iovec iov[2];
+  u_char type;
+  int ret;
+  int flags = 0;
+  struct listnode *node;
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+  static u_int16_t ipid = 0;
+  u_int16_t maxdatasize;
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+#define OSPF_WRITE_IPHL_SHIFT 2
+  
+  ospf->t_write = NULL;
+
+  node = listhead (ospf->oi_write_q);
+  assert (node);
+  oi = listgetdata (node);
+  assert (oi);
+
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+  /* seed ipid static with low order bits of time */
+  if (ipid == 0)
+    ipid = (time(NULL) & 0xffff);
+
+  /* convenience - max OSPF data per packet,
+   * and reliability - not more data, than our
+   * socket can accept
+   */
+  maxdatasize = MIN (oi->ifp->mtu, ospf->maxsndbuflen) -
+    sizeof (struct ip);
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+  
+  /* Get one packet from queue. */
+  op = ospf_fifo_head (oi->obuf);
+  assert (op);
+  assert (op->length >= OSPF_HEADER_SIZE);
+
+  if (op->dst.s_addr == htonl (OSPF_ALLSPFROUTERS)
+      || op->dst.s_addr == htonl (OSPF_ALLDROUTERS))
+      ospf_if_ipmulticast (ospf, oi->address, oi->ifp->ifindex);
+    
+  /* Rewrite the md5 signature & update the seq */
+  ospf_make_md5_digest (oi, op);
+
+  /* Retrieve OSPF packet type. */
+  stream_set_getp (op->s, 1);
+  type = stream_getc (op->s);
+  
+  /* reset get pointer */
+  stream_set_getp (op->s, 0);
+
+  memset (&iph, 0, sizeof (struct ip));
+  memset (&sa_dst, 0, sizeof (sa_dst));
+  
+  sa_dst.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sa_dst.sin_len = sizeof(sa_dst);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  sa_dst.sin_addr = op->dst;
+  sa_dst.sin_port = htons (0);
+
+  /* Set DONTROUTE flag if dst is unicast. */
+  if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+    if (!IN_MULTICAST (htonl (op->dst.s_addr)))
+      flags = MSG_DONTROUTE;
+
+  iph.ip_hl = sizeof (struct ip) >> OSPF_WRITE_IPHL_SHIFT;
+  /* it'd be very strange for header to not be 4byte-word aligned but.. */
+  if ( sizeof (struct ip) 
+        > (unsigned int)(iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) )
+    iph.ip_hl++; /* we presume sizeof struct ip cant overflow ip_hl.. */
+  
+  iph.ip_v = IPVERSION;
+  iph.ip_tos = IPTOS_PREC_INTERNETCONTROL;
+  iph.ip_len = (iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) + op->length;
+
+#if defined(__DragonFly__)
+  /*
+   * DragonFly's raw socket expects ip_len/ip_off in network byte order.
+   */
+  iph.ip_len = htons(iph.ip_len);
+#endif
+
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+  /* XXX-MT: not thread-safe at all..
+   * XXX: this presumes this is only programme sending OSPF packets 
+   * otherwise, no guarantee ipid will be unique
+   */
+  iph.ip_id = ++ipid;
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
+  iph.ip_off = 0;
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    iph.ip_ttl = OSPF_VL_IP_TTL;
+  else
+    iph.ip_ttl = OSPF_IP_TTL;
+  iph.ip_p = IPPROTO_OSPFIGP;
+  iph.ip_sum = 0;
+  iph.ip_src.s_addr = oi->address->u.prefix4.s_addr;
+  iph.ip_dst.s_addr = op->dst.s_addr;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_name = (caddr_t) &sa_dst;
+  msg.msg_namelen = sizeof (sa_dst); 
+  msg.msg_iov = iov;
+  msg.msg_iovlen = 2;
+  iov[0].iov_base = (char*)&iph;
+  iov[0].iov_len = iph.ip_hl << OSPF_WRITE_IPHL_SHIFT;
+  iov[1].iov_base = STREAM_PNT (op->s);
+  iov[1].iov_len = op->length;
+  
+  /* Sadly we can not rely on kernels to fragment packets because of either
+   * IP_HDRINCL and/or multicast destination being set.
+   */
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+  if ( op->length > maxdatasize )
+    ospf_write_frags (ospf->fd, op, &iph, &msg, maxdatasize, 
+                      oi->ifp->mtu, flags, type);
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
+  /* send final fragment (could be first) */
+  sockopt_iphdrincl_swab_htosys (&iph);
+  ret = sendmsg (ospf->fd, &msg, flags);
+  sockopt_iphdrincl_swab_systoh (&iph);
+  
+  if (ret < 0)
+    zlog_warn ("*** sendmsg in ospf_write failed to %s, "
+              "id %d, off %d, len %d, interface %s, mtu %u: %s",
+              inet_ntoa (iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len,
+              oi->ifp->name, oi->ifp->mtu, safe_strerror (errno));
+
+  /* Show debug sending packet. */
+  if (IS_DEBUG_OSPF_PACKET (type - 1, SEND))
+    {
+      if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL))
+       {
+         zlog_debug ("-----------------------------------------------------");
+         ospf_ip_header_dump (&iph);
+         stream_set_getp (op->s, 0);
+         ospf_packet_dump (op->s);
+       }
+
+      zlog_debug ("%s sent to [%s] via [%s].",
+                LOOKUP (ospf_packet_type_str, type), inet_ntoa (op->dst),
+                IF_NAME (oi));
+
+      if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL))
+       zlog_debug ("-----------------------------------------------------");
+    }
+
+  /* Now delete packet from queue. */
+  ospf_packet_delete (oi);
+
+  /* Move this interface to the tail of write_q to
+        serve everyone in a round robin fashion */
+  listnode_move_to_tail (ospf->oi_write_q, node);
+  if (ospf_fifo_head (oi->obuf) == NULL)
+    {
+      oi->on_write_q = 0;
+      list_delete_node (ospf->oi_write_q, node);
+    }
+  
+  /* If packets still remain in queue, call write thread. */
+  if (!list_isempty (ospf->oi_write_q))
+    ospf->t_write =                                              
+      thread_add_write (master, ospf_write, ospf, ospf->fd);
+
+  return 0;
+}
+
+/* OSPF Hello message read -- RFC2328 Section 10.5. */
+static void
+ospf_hello (struct ip *iph, struct ospf_header *ospfh,
+           struct stream * s, struct ospf_interface *oi, int size)
+{
+  struct ospf_hello *hello;
+  struct ospf_neighbor *nbr;
+  int old_state;
+  struct prefix p;
+
+  /* increment statistics. */
+  oi->hello_in++;
+
+  hello = (struct ospf_hello *) STREAM_PNT (s);
+
+  /* If Hello is myself, silently discard. */
+  if (IPV4_ADDR_SAME (&ospfh->router_id, &oi->ospf->router_id))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        {
+          zlog_debug ("ospf_header[%s/%s]: selforiginated, "
+                     "dropping.",
+                     LOOKUP (ospf_packet_type_str, ospfh->type),
+                     inet_ntoa (iph->ip_src));
+        }
+      return;
+    }
+
+  /* get neighbor prefix. */
+  p.family = AF_INET;
+  p.prefixlen = ip_masklen (hello->network_mask);
+  p.u.prefix4 = iph->ip_src;
+
+  /* Compare network mask. */
+  /* Checking is ignored for Point-to-Point and Virtual link. */
+  if (oi->type != OSPF_IFTYPE_POINTOPOINT 
+      && oi->type != OSPF_IFTYPE_VIRTUALLINK)
+    if (oi->address->prefixlen != p.prefixlen)
+      {
+       zlog_warn ("Packet %s [Hello:RECV]: NetworkMask mismatch on %s (configured prefix length is %d, but hello packet indicates %d).",
+                  inet_ntoa(ospfh->router_id), IF_NAME(oi),
+                  (int)oi->address->prefixlen, (int)p.prefixlen);
+       return;
+      }
+
+  /* Compare Router Dead Interval. */
+  if (OSPF_IF_PARAM (oi, v_wait) != ntohl (hello->dead_interval))
+    {
+      zlog_warn ("Packet %s [Hello:RECV]: RouterDeadInterval mismatch "
+                "(expected %u, but received %u).",
+                inet_ntoa(ospfh->router_id),
+                OSPF_IF_PARAM(oi, v_wait), ntohl(hello->dead_interval));
+      return;
+    }
+
+  /* Compare Hello Interval - ignored if fast-hellos are set. */
+  if (OSPF_IF_PARAM (oi, fast_hello) == 0)
+    {
+      if (OSPF_IF_PARAM (oi, v_hello) != ntohs (hello->hello_interval))
+        {
+          zlog_warn ("Packet %s [Hello:RECV]: HelloInterval mismatch "
+                    "(expected %u, but received %u).",
+                    inet_ntoa(ospfh->router_id),
+                    OSPF_IF_PARAM(oi, v_hello), ntohs(hello->hello_interval));
+          return;
+        }
+    }
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Packet %s [Hello:RECV]: Options %s",
+              inet_ntoa (ospfh->router_id),
+              ospf_options_dump (hello->options));
+
+  /* Compare options. */
+#define REJECT_IF_TBIT_ON      1 /* XXX */
+#ifdef REJECT_IF_TBIT_ON
+  if (CHECK_FLAG (hello->options, OSPF_OPTION_MT))
+    {
+      /*
+       * This router does not support non-zero TOS.
+       * Drop this Hello packet not to establish neighbor relationship.
+       */
+      zlog_warn ("Packet %s [Hello:RECV]: T-bit on, drop it.",
+                inet_ntoa (ospfh->router_id));
+      return;
+    }
+#endif /* REJECT_IF_TBIT_ON */
+
+  if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)
+      && CHECK_FLAG (hello->options, OSPF_OPTION_O))
+    {
+      /*
+       * This router does know the correct usage of O-bit
+       * the bit should be set in DD packet only.
+       */
+      zlog_warn ("Packet %s [Hello:RECV]: O-bit abuse?",
+                inet_ntoa (ospfh->router_id));
+#ifdef STRICT_OBIT_USAGE_CHECK
+      return;                                     /* Reject this packet. */
+#else /* STRICT_OBIT_USAGE_CHECK */
+      UNSET_FLAG (hello->options, OSPF_OPTION_O); /* Ignore O-bit. */
+#endif /* STRICT_OBIT_USAGE_CHECK */
+    }
+
+  /* new for NSSA is to ensure that NP is on and E is off */
+
+  if (oi->area->external_routing == OSPF_AREA_NSSA) 
+    {
+      if (! (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_NP)
+            && CHECK_FLAG (hello->options, OSPF_OPTION_NP)
+            && ! CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E)
+            && ! CHECK_FLAG (hello->options, OSPF_OPTION_E)))
+       {
+         zlog_warn ("NSSA-Packet-%s[Hello:RECV]: my options: %x, his options %x", inet_ntoa (ospfh->router_id), OPTIONS (oi), hello->options);
+         return;
+       }
+      if (IS_DEBUG_OSPF_NSSA)
+        zlog_debug ("NSSA-Hello:RECV:Packet from %s:", inet_ntoa(ospfh->router_id));
+    }
+  else    
+    /* The setting of the E-bit found in the Hello Packet's Options
+       field must match this area's ExternalRoutingCapability A
+       mismatch causes processing to stop and the packet to be
+       dropped. The setting of the rest of the bits in the Hello
+       Packet's Options field should be ignored. */
+    if (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E) !=
+       CHECK_FLAG (hello->options, OSPF_OPTION_E))
+      {
+       zlog_warn ("Packet %s [Hello:RECV]: my options: %x, his options %x",
+                  inet_ntoa(ospfh->router_id), OPTIONS (oi), hello->options);
+       return;
+      }
+  
+  /* get neighbour struct */
+  nbr = ospf_nbr_get (oi, ospfh, iph, &p);
+
+  /* neighbour must be valid, ospf_nbr_get creates if none existed */
+  assert (nbr);
+
+  old_state = nbr->state;
+
+  /* Add event to thread. */
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
+  /*  RFC2328  Section 9.5.1
+      If the router is not eligible to become Designated Router,
+      (snip)   It      must also send an Hello Packet in reply to an
+      Hello Packet received from any eligible neighbor (other than
+      the      current Designated Router and Backup Designated Router).  */
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    if (PRIORITY(oi) == 0 && hello->priority > 0
+       && IPV4_ADDR_CMP(&DR(oi),  &iph->ip_src)
+       && IPV4_ADDR_CMP(&BDR(oi), &iph->ip_src))
+      OSPF_NSM_TIMER_ON (nbr->t_hello_reply, ospf_hello_reply_timer,
+                        OSPF_HELLO_REPLY_DELAY);
+
+  /* on NBMA network type, it happens to receive bidirectional Hello packet
+     without advance 1-Way Received event.
+     To avoid incorrect DR-seletion, raise 1-Way Received event.*/
+  if (oi->type == OSPF_IFTYPE_NBMA &&
+      (old_state == NSM_Down || old_state == NSM_Attempt))
+    {
+      OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived);
+      nbr->priority = hello->priority;
+      nbr->d_router = hello->d_router;
+      nbr->bd_router = hello->bd_router;
+      return;
+    }
+
+  if (ospf_nbr_bidirectional (&oi->ospf->router_id, hello->neighbors,
+                             size - OSPF_HELLO_MIN_SIZE))
+    {
+      OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_TwoWayReceived);
+      nbr->options |= hello->options;
+    }
+  else
+    {
+      OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived);
+      /* Set neighbor information. */
+      nbr->priority = hello->priority;
+      nbr->d_router = hello->d_router;
+      nbr->bd_router = hello->bd_router;
+      return;
+    }
+
+  /* If neighbor itself declares DR and no BDR exists,
+     cause event BackupSeen */
+  if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router))
+    if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting)
+      OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen);
+
+  /* neighbor itself declares BDR. */
+  if (oi->state == ISM_Waiting &&
+      IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router))
+    OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen);
+
+  /* had not previously. */
+  if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router) &&
+       IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->d_router)) ||
+      (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->d_router) &&
+       IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->d_router)))
+    OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+
+  /* had not previously. */
+  if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router) &&
+       IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->bd_router)) ||
+      (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->bd_router) &&
+       IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->bd_router)))
+    OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+
+  /* Neighbor priority check. */
+  if (nbr->priority >= 0 && nbr->priority != hello->priority)
+    OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+
+  /* Set neighbor information. */
+  nbr->priority = hello->priority;
+  nbr->d_router = hello->d_router;
+  nbr->bd_router = hello->bd_router;
+}
+
+/* Save DD flags/options/Seqnum received. */
+static void
+ospf_db_desc_save_current (struct ospf_neighbor *nbr,
+                          struct ospf_db_desc *dd)
+{
+  nbr->last_recv.flags = dd->flags;
+  nbr->last_recv.options = dd->options;
+  nbr->last_recv.dd_seqnum = ntohl (dd->dd_seqnum);
+}
+
+/* Process rest of DD packet. */
+static void
+ospf_db_desc_proc (struct stream *s, struct ospf_interface *oi,
+                  struct ospf_neighbor *nbr, struct ospf_db_desc *dd,
+                  u_int16_t size)
+{
+  struct ospf_lsa *new, *find;
+  struct lsa_header *lsah;
+
+  stream_forward_getp (s, OSPF_DB_DESC_MIN_SIZE);
+  for (size -= OSPF_DB_DESC_MIN_SIZE;
+       size >= OSPF_LSA_HEADER_SIZE; size -= OSPF_LSA_HEADER_SIZE) 
+    {
+      lsah = (struct lsa_header *) STREAM_PNT (s);
+      stream_forward_getp (s, OSPF_LSA_HEADER_SIZE);
+
+      /* Unknown LS type. */
+      if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA)
+       {
+         zlog_warn ("Packet [DD:RECV]: Unknown LS type %d.", lsah->type);
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+         return;
+       }
+
+      if (IS_OPAQUE_LSA (lsah->type)
+      &&  ! CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+        {
+          zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id));
+          OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+          return;
+        }
+
+      switch (lsah->type)
+        {
+        case OSPF_AS_EXTERNAL_LSA:
+       case OSPF_OPAQUE_AS_LSA:
+          /* Check for stub area.  Reject if AS-External from stub but
+             allow if from NSSA. */
+          if (oi->area->external_routing == OSPF_AREA_STUB)
+            {
+              zlog_warn ("Packet [DD:RECV]: LSA[Type%d:%s] from %s area.",
+                         lsah->type, inet_ntoa (lsah->id),
+                         (oi->area->external_routing == OSPF_AREA_STUB) ?\
+                         "STUB" : "NSSA");
+              OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+              return;
+            }
+          break;
+       default:
+         break;
+        }
+
+      /* Create LS-request object. */
+      new = ospf_ls_request_new (lsah);
+
+      /* Lookup received LSA, then add LS request list. */
+      find = ospf_lsa_lookup_by_header (oi->area, lsah);
+      
+      /* ospf_lsa_more_recent is fine with NULL pointers */
+      switch (ospf_lsa_more_recent (find, new))
+        {
+          case -1:
+            /* Neighbour has a more recent LSA, we must request it */
+            ospf_ls_request_add (nbr, new);
+          case 0:
+            /* If we have a copy of this LSA, it's either less recent
+             * and we're requesting it from neighbour (the case above), or
+             * it's as recent and we both have same copy (this case).
+             *
+             * In neither of these two cases is there any point in
+             * describing our copy of the LSA to the neighbour in a
+             * DB-Summary packet, if we're still intending to do so.
+             *
+             * See: draft-ogier-ospf-dbex-opt-00.txt, describing the
+             * backward compatible optimisation to OSPF DB Exchange /
+             * DB Description process implemented here.
+             */
+            if (find)
+              ospf_lsdb_delete (&nbr->db_sum, find);
+            ospf_lsa_discard (new);
+            break;
+          default:
+            /* We have the more recent copy, nothing specific to do:
+             * - no need to request neighbours stale copy
+             * - must leave DB summary list copy alone
+             */
+            if (IS_DEBUG_OSPF_EVENT)
+              zlog_debug ("Packet [DD:RECV]: LSA received Type %d, "
+                         "ID %s is not recent.", lsah->type, inet_ntoa (lsah->id));
+            ospf_lsa_discard (new);
+        }
+    }
+
+  /* Master */
+  if (IS_SET_DD_MS (nbr->dd_flags))
+    {
+      nbr->dd_seqnum++;
+
+      /* Both sides have no More, then we're done with Exchange */
+      if (!IS_SET_DD_M (dd->flags) && !IS_SET_DD_M (nbr->dd_flags))
+       OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone);
+      else
+       ospf_db_desc_send (nbr);
+    }
+  /* Slave */
+  else
+    {
+      nbr->dd_seqnum = ntohl (dd->dd_seqnum);
+
+      /* Send DD packet in reply. 
+       * 
+       * Must be done to acknowledge the Master's DD, regardless of
+       * whether we have more LSAs ourselves to describe.
+       *
+       * This function will clear the 'More' bit, if after this DD
+       * we have no more LSAs to describe to the master..
+       */
+      ospf_db_desc_send (nbr);
+      
+      /* Slave can raise ExchangeDone now, if master is also done */
+      if (!IS_SET_DD_M (dd->flags) && !IS_SET_DD_M (nbr->dd_flags))
+       OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone);
+    }
+  
+  /* Save received neighbor values from DD. */
+  ospf_db_desc_save_current (nbr, dd);
+}
+
+static int
+ospf_db_desc_is_dup (struct ospf_db_desc *dd, struct ospf_neighbor *nbr)
+{
+  /* Is DD duplicated? */
+  if (dd->options == nbr->last_recv.options &&
+      dd->flags == nbr->last_recv.flags &&
+      dd->dd_seqnum == htonl (nbr->last_recv.dd_seqnum))
+    return 1;
+
+  return 0;
+}
+
+/* OSPF Database Description message read -- RFC2328 Section 10.6. */
+static void
+ospf_db_desc (struct ip *iph, struct ospf_header *ospfh,
+             struct stream *s, struct ospf_interface *oi, u_int16_t size)
+{
+  struct ospf_db_desc *dd;
+  struct ospf_neighbor *nbr;
+
+  /* Increment statistics. */
+  oi->db_desc_in++;
+
+  dd = (struct ospf_db_desc *) STREAM_PNT (s);
+
+  nbr = ospf_nbr_lookup (oi, iph, ospfh);
+  if (nbr == NULL)
+    {
+      zlog_warn ("Packet[DD]: Unknown Neighbor %s",
+                inet_ntoa (ospfh->router_id));
+      return;
+    }
+
+  /* Check MTU. */
+  if ((OSPF_IF_PARAM (oi, mtu_ignore) == 0) && 
+      (ntohs (dd->mtu) > oi->ifp->mtu))
+    {
+      zlog_warn ("Packet[DD]: Neighbor %s MTU %u is larger than [%s]'s MTU %u",
+                inet_ntoa (nbr->router_id), ntohs (dd->mtu),
+                IF_NAME (oi), oi->ifp->mtu);
+      return;
+    }
+
+  /* 
+   * XXX HACK by Hasso Tepper. Setting N/P bit in NSSA area DD packets is not
+   * required. In fact at least JunOS sends DD packets with P bit clear. 
+   * Until proper solution is developped, this hack should help.
+   *
+   * Update: According to the RFCs, N bit is specified /only/ for Hello
+   * options, unfortunately its use in DD options is not specified. Hence some
+   * implementations follow E-bit semantics and set it in DD options, and some
+   * treat it as unspecified and hence follow the directive "default for 
+   * options is clear", ie unset.
+   *
+   * Reset the flag, as ospfd follows E-bit semantics.
+   */
+  if ( (oi->area->external_routing == OSPF_AREA_NSSA)
+       && (CHECK_FLAG (nbr->options, OSPF_OPTION_NP))
+       && (!CHECK_FLAG (dd->options, OSPF_OPTION_NP)) )
+    {
+      if (IS_DEBUG_OSPF_EVENT) 
+        zlog_debug ("Packet[DD]: Neighbour %s: Has NSSA capability, sends with N bit clear in DD options",
+                    inet_ntoa (nbr->router_id) );
+      SET_FLAG (dd->options, OSPF_OPTION_NP);
+    }
+
+#ifdef REJECT_IF_TBIT_ON
+  if (CHECK_FLAG (dd->options, OSPF_OPTION_MT))
+    {
+      /*
+       * In Hello protocol, optional capability must have checked
+       * to prevent this T-bit enabled router be my neighbor.
+       */
+      zlog_warn ("Packet[DD]: Neighbor %s: T-bit on?", inet_ntoa (nbr->router_id));
+      return;
+    }
+#endif /* REJECT_IF_TBIT_ON */
+
+  if (CHECK_FLAG (dd->options, OSPF_OPTION_O)
+      && !CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE))
+    {
+      /*
+       * This node is not configured to handle O-bit, for now.
+       * Clear it to ignore unsupported capability proposed by neighbor.
+       */
+      UNSET_FLAG (dd->options, OSPF_OPTION_O);
+    }
+
+  /* Add event to thread. */
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
+  /* Process DD packet by neighbor status. */
+  switch (nbr->state)
+    {
+    case NSM_Down:
+    case NSM_Attempt:
+    case NSM_TwoWay:
+      zlog_warn ("Packet[DD]: Neighbor %s state is %s, packet discarded.",
+                inet_ntoa(nbr->router_id),
+                LOOKUP (ospf_nsm_state_msg, nbr->state));
+      break;
+    case NSM_Init:
+      OSPF_NSM_EVENT_EXECUTE (nbr, NSM_TwoWayReceived);
+      /* If the new state is ExStart, the processing of the current
+        packet should then continue in this new state by falling
+        through to case ExStart below.  */
+      if (nbr->state != NSM_ExStart)
+       break;
+    case NSM_ExStart:
+      /* Initial DBD */
+      if ((IS_SET_DD_ALL (dd->flags) == OSPF_DD_FLAG_ALL) &&
+         (size == OSPF_DB_DESC_MIN_SIZE))
+       {
+         if (IPV4_ADDR_CMP (&nbr->router_id, &oi->ospf->router_id) > 0)
+           {
+             /* We're Slave---obey */
+             zlog_info ("Packet[DD]: Neighbor %s Negotiation done (Slave).",
+                        inet_ntoa(nbr->router_id));
+             nbr->dd_seqnum = ntohl (dd->dd_seqnum);
+             
+             /* Reset I/MS */
+             UNSET_FLAG (nbr->dd_flags, (OSPF_DD_FLAG_MS|OSPF_DD_FLAG_I));
+           }
+         else
+           {
+             /* We're Master, ignore the initial DBD from Slave */
+             zlog_info ("Packet[DD]: Neighbor %s: Initial DBD from Slave, "
+                        "ignoring.", inet_ntoa(nbr->router_id));
+             break;
+           }
+       }
+      /* Ack from the Slave */
+      else if (!IS_SET_DD_MS (dd->flags) && !IS_SET_DD_I (dd->flags) &&
+              ntohl (dd->dd_seqnum) == nbr->dd_seqnum &&
+              IPV4_ADDR_CMP (&nbr->router_id, &oi->ospf->router_id) < 0)
+       {
+         zlog_info ("Packet[DD]: Neighbor %s Negotiation done (Master).",
+                    inet_ntoa(nbr->router_id));
+          /* Reset I, leaving MS */
+          UNSET_FLAG (nbr->dd_flags, OSPF_DD_FLAG_I);
+       }
+      else
+       {
+         zlog_warn ("Packet[DD]: Neighbor %s Negotiation fails.",
+                    inet_ntoa(nbr->router_id));
+         break;
+       }
+      
+      /* This is where the real Options are saved */
+      nbr->options = dd->options;
+
+      if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE))
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("Neighbor[%s] is %sOpaque-capable.",
+                      inet_ntoa (nbr->router_id),
+                      CHECK_FLAG (nbr->options, OSPF_OPTION_O) ? "" : "NOT ");
+
+          if (! CHECK_FLAG (nbr->options, OSPF_OPTION_O)
+          &&  IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4))
+            {
+              zlog_warn ("DR-neighbor[%s] is NOT opaque-capable; "
+                         "Opaque-LSAs cannot be reliably advertised "
+                         "in this network.",
+                         inet_ntoa (nbr->router_id));
+              /* This situation is undesirable, but not a real error. */
+            }
+        }
+
+      OSPF_NSM_EVENT_EXECUTE (nbr, NSM_NegotiationDone);
+
+      /* continue processing rest of packet. */
+      ospf_db_desc_proc (s, oi, nbr, dd, size);
+      break;
+    case NSM_Exchange:
+      if (ospf_db_desc_is_dup (dd, nbr))
+       {
+         if (IS_SET_DD_MS (nbr->dd_flags))
+           /* Master: discard duplicated DD packet. */
+           zlog_info ("Packet[DD] (Master): Neighbor %s packet duplicated.",
+                      inet_ntoa (nbr->router_id));
+         else
+           /* Slave: cause to retransmit the last Database Description. */
+           {
+             zlog_info ("Packet[DD] [Slave]: Neighbor %s packet duplicated.",
+                        inet_ntoa (nbr->router_id));
+             ospf_db_desc_resend (nbr);
+           }
+         break;
+       }
+
+      /* Otherwise DD packet should be checked. */
+      /* Check Master/Slave bit mismatch */
+      if (IS_SET_DD_MS (dd->flags) != IS_SET_DD_MS (nbr->last_recv.flags))
+       {
+         zlog_warn ("Packet[DD]: Neighbor %s MS-bit mismatch.",
+                    inet_ntoa(nbr->router_id));
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("Packet[DD]: dd->flags=%d, nbr->dd_flags=%d",
+                       dd->flags, nbr->dd_flags);
+         break;
+       }
+
+      /* Check initialize bit is set. */
+      if (IS_SET_DD_I (dd->flags))
+       {
+         zlog_info ("Packet[DD]: Neighbor %s I-bit set.",
+                    inet_ntoa(nbr->router_id));
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+         break;
+       }
+
+      /* Check DD Options. */
+      if (dd->options != nbr->options)
+       {
+#ifdef ORIGINAL_CODING
+         /* Save the new options for debugging */
+         nbr->options = dd->options;
+#endif /* ORIGINAL_CODING */
+         zlog_warn ("Packet[DD]: Neighbor %s options mismatch.",
+                    inet_ntoa(nbr->router_id));
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+         break;
+       }
+
+      /* Check DD sequence number. */
+      if ((IS_SET_DD_MS (nbr->dd_flags) &&
+          ntohl (dd->dd_seqnum) != nbr->dd_seqnum) ||
+         (!IS_SET_DD_MS (nbr->dd_flags) &&
+          ntohl (dd->dd_seqnum) != nbr->dd_seqnum + 1))
+       {
+         zlog_warn ("Packet[DD]: Neighbor %s sequence number mismatch.",
+                    inet_ntoa(nbr->router_id));
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+         break;
+       }
+
+      /* Continue processing rest of packet. */
+      ospf_db_desc_proc (s, oi, nbr, dd, size);
+      break;
+    case NSM_Loading:
+    case NSM_Full:
+      if (ospf_db_desc_is_dup (dd, nbr))
+       {
+         if (IS_SET_DD_MS (nbr->dd_flags))
+           {
+             /* Master should discard duplicate DD packet. */
+             zlog_info ("Packet[DD]: Neighbor %s duplicated, "
+                        "packet discarded.",
+                       inet_ntoa(nbr->router_id));
+             break;
+           }
+         else
+           {
+             struct timeval t, now;
+             quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+             t = tv_sub (now, nbr->last_send_ts);
+             if (tv_cmp (t, int2tv (nbr->v_inactivity)) < 0)
+               {
+                 /* In states Loading and Full the slave must resend
+                    its last Database Description packet in response to
+                    duplicate Database Description packets received
+                    from the master.  For this reason the slave must
+                    wait RouterDeadInterval seconds before freeing the
+                    last Database Description packet.  Reception of a
+                    Database Description packet from the master after
+                    this interval will generate a SeqNumberMismatch
+                    neighbor event. RFC2328 Section 10.8 */
+                 ospf_db_desc_resend (nbr);
+                 break;
+               }
+           }
+       }
+
+      OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
+      break;
+    default:
+      zlog_warn ("Packet[DD]: Neighbor %s NSM illegal status %u.",
+                inet_ntoa(nbr->router_id), nbr->state);
+      break;
+    }
+}
+
+#define OSPF_LSA_KEY_SIZE       12 /* type(4) + id(4) + ar(4) */
+
+/* OSPF Link State Request Read -- RFC2328 Section 10.7. */
+static void
+ospf_ls_req (struct ip *iph, struct ospf_header *ospfh,
+            struct stream *s, struct ospf_interface *oi, u_int16_t size)
+{
+  struct ospf_neighbor *nbr;
+  u_int32_t ls_type;
+  struct in_addr ls_id;
+  struct in_addr adv_router;
+  struct ospf_lsa *find;
+  struct list *ls_upd;
+  unsigned int length;
+
+  /* Increment statistics. */
+  oi->ls_req_in++;
+
+  nbr = ospf_nbr_lookup (oi, iph, ospfh);
+  if (nbr == NULL)
+    {
+      zlog_warn ("Link State Request: Unknown Neighbor %s.",
+                inet_ntoa (ospfh->router_id));
+      return;
+    }
+
+  /* Add event to thread. */
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
+  /* Neighbor State should be Exchange or later. */
+  if (nbr->state != NSM_Exchange &&
+      nbr->state != NSM_Loading &&
+      nbr->state != NSM_Full)
+    {
+      zlog_warn ("Link State Request received from %s: "
+                "Neighbor state is %s, packet discarded.",
+                inet_ntoa (ospfh->router_id),
+                LOOKUP (ospf_nsm_state_msg, nbr->state));
+      return;
+    }
+
+  /* Send Link State Update for ALL requested LSAs. */
+  ls_upd = list_new ();
+  length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE;
+
+  while (size >= OSPF_LSA_KEY_SIZE)
+    {
+      /* Get one slice of Link State Request. */
+      ls_type = stream_getl (s);
+      ls_id.s_addr = stream_get_ipv4 (s);
+      adv_router.s_addr = stream_get_ipv4 (s);
+
+      /* Verify LSA type. */
+      if (ls_type < OSPF_MIN_LSA || ls_type >= OSPF_MAX_LSA)
+       {
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq);
+         list_delete (ls_upd);
+         return;
+       }
+
+      /* Search proper LSA in LSDB. */
+      find = ospf_lsa_lookup (oi->area, ls_type, ls_id, adv_router);
+      if (find == NULL)
+       {
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq);
+         list_delete (ls_upd);
+         return;
+       }
+
+      /* Packet overflows MTU size, send immediately. */
+      if (length + ntohs (find->data->length) > ospf_packet_max (oi))
+       {
+         if (oi->type == OSPF_IFTYPE_NBMA)
+           ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT);
+         else
+           ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT);
+
+         /* Only remove list contents.  Keep ls_upd. */
+         list_delete_all_node (ls_upd);
+
+         length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE;
+       }
+
+      /* Append LSA to update list. */
+      listnode_add (ls_upd, find);
+      length += ntohs (find->data->length);
+
+      size -= OSPF_LSA_KEY_SIZE;
+    }
+
+  /* Send rest of Link State Update. */
+  if (listcount (ls_upd) > 0)
+    {
+      if (oi->type == OSPF_IFTYPE_NBMA)
+       ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT);
+      else
+       ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT);
+
+      list_delete (ls_upd);
+    }
+  else
+    list_free (ls_upd);
+}
+
+/* Get the list of LSAs from Link State Update packet.
+   And process some validation -- RFC2328 Section 13. (1)-(2). */
+static struct list *
+ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s,
+                      struct ospf_interface *oi, size_t size)
+{
+  u_int16_t count, sum;
+  u_int32_t length;
+  struct lsa_header *lsah;
+  struct ospf_lsa *lsa;
+  struct list *lsas;
+
+  lsas = list_new ();
+
+  count = stream_getl (s);
+  size -= OSPF_LS_UPD_MIN_SIZE; /* # LSAs */
+
+  for (; size >= OSPF_LSA_HEADER_SIZE && count > 0;
+       size -= length, stream_forward_getp (s, length), count--)
+    {
+      lsah = (struct lsa_header *) STREAM_PNT (s);
+      length = ntohs (lsah->length);
+
+      if (length > size)
+       {
+         zlog_warn ("Link State Update: LSA length exceeds packet size.");
+         break;
+       }
+
+      /* Validate the LSA's LS checksum. */
+      sum = lsah->checksum;
+      if (! ospf_lsa_checksum_valid (lsah))
+       {
+         /* (bug #685) more details in a one-line message make it possible
+          * to identify problem source on the one hand and to have a better
+          * chance to compress repeated messages in syslog on the other */
+         zlog_warn ("Link State Update: LSA checksum error %x/%x, ID=%s from: nbr %s, router ID %s, adv router %s",
+                    sum, lsah->checksum, inet_ntoa (lsah->id),
+                    inet_ntoa (nbr->src), inet_ntoa (nbr->router_id),
+                    inet_ntoa (lsah->adv_router));
+         continue;
+       }
+
+      /* Examine the LSA's LS type. */
+      if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA)
+       {
+         zlog_warn ("Link State Update: Unknown LS type %d", lsah->type);
+         continue;
+       }
+
+      /*
+       * What if the received LSA's age is greater than MaxAge?
+       * Treat it as a MaxAge case -- endo.
+       */
+      if (ntohs (lsah->ls_age) > OSPF_LSA_MAXAGE)
+        lsah->ls_age = htons (OSPF_LSA_MAXAGE);
+
+      if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
+        {
+#ifdef STRICT_OBIT_USAGE_CHECK
+         if ((IS_OPAQUE_LSA(lsah->type) &&
+               ! CHECK_FLAG (lsah->options, OSPF_OPTION_O))
+         ||  (! IS_OPAQUE_LSA(lsah->type) &&
+               CHECK_FLAG (lsah->options, OSPF_OPTION_O)))
+            {
+              /*
+               * This neighbor must know the exact usage of O-bit;
+               * the bit will be set in Type-9,10,11 LSAs only.
+               */
+              zlog_warn ("LSA[Type%d:%s]: O-bit abuse?", lsah->type, inet_ntoa (lsah->id));
+              continue;
+            }
+#endif /* STRICT_OBIT_USAGE_CHECK */
+
+          /* Do not take in AS External Opaque-LSAs if we are a stub. */
+          if (lsah->type == OSPF_OPAQUE_AS_LSA
+             && nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) 
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("LSA[Type%d:%s]: We are a stub, don't take this LSA.", lsah->type, inet_ntoa (lsah->id));
+              continue;
+            }
+        }
+      else if (IS_OPAQUE_LSA(lsah->type))
+        {
+          zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id));
+          continue;
+        }
+
+      /* Create OSPF LSA instance. */
+      lsa = ospf_lsa_new ();
+
+      /* We may wish to put some error checking if type NSSA comes in
+         and area not in NSSA mode */
+      switch (lsah->type)
+        {
+        case OSPF_AS_EXTERNAL_LSA:
+        case OSPF_OPAQUE_AS_LSA:
+          lsa->area = NULL;
+          break;
+        case OSPF_OPAQUE_LINK_LSA:
+          lsa->oi = oi; /* Remember incoming interface for flooding control. */
+          /* Fallthrough */
+        default:
+          lsa->area = oi->area;
+          break;
+        }
+
+      lsa->data = ospf_lsa_data_new (length);
+      memcpy (lsa->data, lsah, length);
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug("LSA[Type%d:%s]: %p new LSA created with Link State Update",
+                 lsa->data->type, inet_ntoa (lsa->data->id), (void *)lsa);
+      listnode_add (lsas, lsa);
+    }
+
+  return lsas;
+}
+
+/* Cleanup Update list. */
+static void
+ospf_upd_list_clean (struct list *lsas)
+{
+  struct listnode *node, *nnode;
+  struct ospf_lsa *lsa;
+
+  for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa))
+    ospf_lsa_discard (lsa);
+
+  list_delete (lsas);
+}
+
+/* OSPF Link State Update message read -- RFC2328 Section 13. */
+static void
+ospf_ls_upd (struct ospf *ospf, struct ip *iph, struct ospf_header *ospfh,
+            struct stream *s, struct ospf_interface *oi, u_int16_t size)
+{
+  struct ospf_neighbor *nbr;
+  struct list *lsas;
+  struct listnode *node, *nnode;
+  struct ospf_lsa *lsa = NULL;
+  /* unsigned long ls_req_found = 0; */
+
+  /* Dis-assemble the stream, update each entry, re-encapsulate for flooding */
+
+  /* Increment statistics. */
+  oi->ls_upd_in++;
+
+  /* Check neighbor. */
+  nbr = ospf_nbr_lookup (oi, iph, ospfh);
+  if (nbr == NULL)
+    {
+      zlog_warn ("Link State Update: Unknown Neighbor %s on int: %s",
+                inet_ntoa (ospfh->router_id), IF_NAME (oi));
+      return;
+    }
+
+  /* Add event to thread. */
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
+  /* Check neighbor state. */
+  if (nbr->state < NSM_Exchange)
+    {
+      zlog_warn ("Link State Update: "
+                "Neighbor[%s] state %s is less than Exchange",
+                inet_ntoa (ospfh->router_id),
+                LOOKUP(ospf_nsm_state_msg, nbr->state));
+      return;
+    }
+
+  /* Get list of LSAs from Link State Update packet. - Also perorms Stages 
+   * 1 (validate LSA checksum) and 2 (check for LSA consistent type) 
+   * of section 13. 
+   */
+  lsas = ospf_ls_upd_list_lsa (nbr, s, oi, size);
+
+#define DISCARD_LSA(L,N) {\
+        if (IS_DEBUG_OSPF_EVENT) \
+          zlog_debug ("ospf_lsa_discard() in ospf_ls_upd() point %d: lsa %p" \
+                      " Type-%d", N, (void *)lsa, (int) lsa->data->type); \
+        ospf_lsa_discard (L); \
+       continue; }
+
+  /* Process each LSA received in the one packet.
+   *
+   * Numbers in parentheses, e.g. (1), (2), etc., and the corresponding
+   * text below are from the steps in RFC 2328, Section 13.
+   */
+  for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa))
+    {
+      struct ospf_lsa *ls_ret, *current;
+      int ret = 1;
+
+      if (IS_DEBUG_OSPF_NSSA)
+       {
+         char buf1[INET_ADDRSTRLEN];
+         char buf2[INET_ADDRSTRLEN];
+         char buf3[INET_ADDRSTRLEN];
+
+         zlog_debug("LSA Type-%d from %s, ID: %s, ADV: %s",
+                 lsa->data->type,
+                 inet_ntop (AF_INET, &ospfh->router_id,
+                            buf1, INET_ADDRSTRLEN),
+                 inet_ntop (AF_INET, &lsa->data->id,
+                            buf2, INET_ADDRSTRLEN),
+                 inet_ntop (AF_INET, &lsa->data->adv_router,
+                            buf3, INET_ADDRSTRLEN));
+       }
+
+      listnode_delete (lsas, lsa); /* We don't need it in list anymore */
+
+      /* (1) Validate Checksum - Done above by ospf_ls_upd_list_lsa() */
+
+      /* (2) LSA Type  - Done above by ospf_ls_upd_list_lsa() */
+   
+      /* (3) Do not take in AS External LSAs if we are a stub or NSSA. */
+
+      /* Do not take in AS NSSA if this neighbor and we are not NSSA */
+
+      /* Do take in Type-7's if we are an NSSA  */ 
+      /* If we are also an ABR, later translate them to a Type-5 packet */
+      /* Later, an NSSA Re-fresh can Re-fresh Type-7's and an ABR will
+        translate them to a separate Type-5 packet.  */
+
+      if (lsa->data->type == OSPF_AS_EXTERNAL_LSA)
+        /* Reject from STUB or NSSA */
+        if (nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) 
+         {
+           if (IS_DEBUG_OSPF_NSSA)
+             zlog_debug("Incoming External LSA Discarded: We are NSSA/STUB Area");
+           DISCARD_LSA (lsa, 1);
+         }
+
+      if (lsa->data->type == OSPF_AS_NSSA_LSA)
+       if (nbr->oi->area->external_routing != OSPF_AREA_NSSA)
+         {
+           if (IS_DEBUG_OSPF_NSSA)
+             zlog_debug("Incoming NSSA LSA Discarded:  Not NSSA Area");
+           DISCARD_LSA (lsa,2);
+         }
+
+      /* VU229804: Router-LSA Adv-ID must be equal to LS-ID */
+      if (lsa->data->type == OSPF_ROUTER_LSA)
+       if (!IPV4_ADDR_SAME(&lsa->data->id, &lsa->data->adv_router))
+         {
+           char buf1[INET_ADDRSTRLEN];
+           char buf2[INET_ADDRSTRLEN];
+           char buf3[INET_ADDRSTRLEN];
+
+           zlog_err("Incoming Router-LSA from %s with "
+                     "Adv-ID[%s] != LS-ID[%s]",
+                     inet_ntop (AF_INET, &ospfh->router_id,
+                                buf1, INET_ADDRSTRLEN),
+                     inet_ntop (AF_INET, &lsa->data->id,
+                                buf2, INET_ADDRSTRLEN),
+                     inet_ntop (AF_INET, &lsa->data->adv_router,
+                                buf3, INET_ADDRSTRLEN));
+           zlog_err("OSPF domain compromised by attack or corruption. "
+                    "Verify correct operation of -ALL- OSPF routers.");
+           DISCARD_LSA (lsa, 0);
+         }
+
+      /* Find the LSA in the current database. */
+
+      current = ospf_lsa_lookup_by_header (oi->area, lsa->data);
+
+      /* (4) If the LSA's LS age is equal to MaxAge, and there is currently
+        no instance of the LSA in the router's link state database,
+        and none of router's neighbors are in states Exchange or Loading,
+        then take the following actions: */
+
+      if (IS_LSA_MAXAGE (lsa) && !current &&
+         ospf_check_nbr_status(oi->ospf))
+       {
+         /* (4a) Response Link State Acknowledgment. */
+         ospf_ls_ack_send (nbr, lsa);
+
+         /* (4b) Discard LSA. */
+          if (IS_DEBUG_OSPF (lsa, LSA))
+            {
+              zlog_debug ("Link State Update[%s]: LS age is equal to MaxAge.",
+                           dump_lsa_key(lsa));
+            }
+          DISCARD_LSA (lsa, 3);
+       }
+
+      if (IS_OPAQUE_LSA (lsa->data->type)
+      &&  IPV4_ADDR_SAME (&lsa->data->adv_router, &oi->ospf->router_id))
+        {
+          /*
+           * Even if initial flushing seems to be completed, there might
+           * be a case that self-originated LSA with MaxAge still remain
+           * in the routing domain.
+           * Just send an LSAck message to cease retransmission.
+           */
+          if (IS_LSA_MAXAGE (lsa))
+            {
+              zlog_warn ("LSA[%s]: Boomerang effect?", dump_lsa_key (lsa));
+              ospf_ls_ack_send (nbr, lsa);
+              ospf_lsa_discard (lsa);
+
+              if (current != NULL && ! IS_LSA_MAXAGE (current))
+                ospf_opaque_lsa_refresh_schedule (current);
+              continue;
+            }
+
+          /*
+           * If an instance of self-originated Opaque-LSA is not found
+           * in the LSDB, there are some possible cases here.
+           *
+           * 1) This node lost opaque-capability after restart.
+           * 2) Else, a part of opaque-type is no more supported.
+           * 3) Else, a part of opaque-id is no more supported.
+           *
+           * Anyway, it is still this node's responsibility to flush it.
+           * Otherwise, the LSA instance remains in the routing domain
+           * until its age reaches to MaxAge.
+           */
+          /* XXX: We should deal with this for *ALL* LSAs, not just opaque */
+          if (current == NULL)
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("LSA[%s]: Previously originated Opaque-LSA,"
+                            "not found in the LSDB.", dump_lsa_key (lsa));
+
+              SET_FLAG (lsa->flags, OSPF_LSA_SELF);
+              
+              ospf_opaque_self_originated_lsa_received (nbr, lsa);
+              ospf_ls_ack_send (nbr, lsa);
+              
+              continue;
+            }
+        }
+
+      /* It might be happen that received LSA is self-originated network LSA, but
+       * router ID is changed. So, we should check if LSA is a network-LSA whose
+       * Link State ID is one of the router's own IP interface addresses but whose
+       * Advertising Router is not equal to the router's own Router ID
+       * According to RFC 2328 12.4.2 and 13.4 this LSA should be flushed.
+       */
+
+      if(lsa->data->type == OSPF_NETWORK_LSA)
+      {
+        struct listnode *oinode, *oinnode;
+        struct ospf_interface *out_if;
+        int Flag = 0;
+
+        for (ALL_LIST_ELEMENTS (oi->ospf->oiflist, oinode, oinnode, out_if))
+        {
+          if(out_if == NULL)
+            break;
+
+          if((IPV4_ADDR_SAME(&out_if->address->u.prefix4, &lsa->data->id)) &&
+              (!(IPV4_ADDR_SAME(&oi->ospf->router_id, &lsa->data->adv_router))))
+          {
+            if(out_if->network_lsa_self)
+            {
+              ospf_lsa_flush_area(lsa,out_if->area);
+              if(IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("ospf_lsa_discard() in ospf_ls_upd() point 9: lsa %p Type-%d",
+                            (void *)lsa, (int) lsa->data->type);
+              ospf_lsa_discard (lsa);
+              Flag = 1;
+            }
+            break;
+          }
+        }
+        if(Flag)
+          continue;
+      }
+
+      /* (5) Find the instance of this LSA that is currently contained
+        in the router's link state database.  If there is no
+        database copy, or the received LSA is more recent than
+        the database copy the following steps must be performed.
+         (The sub steps from RFC 2328 section 13 step (5) will be performed in
+         ospf_flood() ) */
+
+      if (current == NULL ||
+         (ret = ospf_lsa_more_recent (current, lsa)) < 0)
+       {
+         /* Actual flooding procedure. */
+         if (ospf_flood (oi->ospf, nbr, current, lsa) < 0)  /* Trap NSSA later. */
+           DISCARD_LSA (lsa, 4);
+         continue;
+       }
+
+      /* (6) Else, If there is an instance of the LSA on the sending
+        neighbor's Link state request list, an error has occurred in
+        the Database Exchange process.  In this case, restart the
+        Database Exchange process by generating the neighbor event
+        BadLSReq for the sending neighbor and stop processing the
+        Link State Update packet. */
+
+      if (ospf_ls_request_lookup (nbr, lsa))
+       {
+         OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq);
+         zlog_warn("LSA[%s] instance exists on Link state request list",
+                   dump_lsa_key(lsa));
+
+         /* Clean list of LSAs. */
+          ospf_upd_list_clean (lsas);
+         /* this lsa is not on lsas list already. */
+         ospf_lsa_discard (lsa);
+         return;
+       }
+
+      /* If the received LSA is the same instance as the database copy
+        (i.e., neither one is more recent) the following two steps
+        should be performed: */
+
+      if (ret == 0)
+       {
+         /* If the LSA is listed in the Link state retransmission list
+            for the receiving adjacency, the router itself is expecting
+            an acknowledgment for this LSA.  The router should treat the
+            received LSA as an acknowledgment by removing the LSA from
+            the Link state retransmission list.  This is termed an
+            "implied acknowledgment". */
+
+         ls_ret = ospf_ls_retransmit_lookup (nbr, lsa);
+
+         if (ls_ret != NULL)
+           {
+             ospf_ls_retransmit_delete (nbr, ls_ret);
+
+             /* Delayed acknowledgment sent if advertisement received
+                from Designated Router, otherwise do nothing. */
+             if (oi->state == ISM_Backup)
+               if (NBR_IS_DR (nbr))
+                 listnode_add (oi->ls_ack, ospf_lsa_lock (lsa));
+
+              DISCARD_LSA (lsa, 5);
+           }
+         else
+           /* Acknowledge the receipt of the LSA by sending a
+              Link State Acknowledgment packet back out the receiving
+              interface. */
+           {
+             ospf_ls_ack_send (nbr, lsa);
+             DISCARD_LSA (lsa, 6);
+           }
+       }
+
+      /* The database copy is more recent.  If the database copy
+        has LS age equal to MaxAge and LS sequence number equal to
+        MaxSequenceNumber, simply discard the received LSA without
+        acknowledging it. (In this case, the LSA's LS sequence number is
+        wrapping, and the MaxSequenceNumber LSA must be completely
+        flushed before any new LSA instance can be introduced). */
+
+      else if (ret > 0)  /* Database copy is more recent */
+       { 
+         if (IS_LSA_MAXAGE (current) &&
+             current->data->ls_seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER))
+           {
+             DISCARD_LSA (lsa, 7);
+           }
+         /* Otherwise, as long as the database copy has not been sent in a
+            Link State Update within the last MinLSArrival seconds, send the
+            database copy back to the sending neighbor, encapsulated within
+            a Link State Update Packet. The Link State Update Packet should
+            be sent directly to the neighbor. In so doing, do not put the
+            database copy of the LSA on the neighbor's link state
+            retransmission list, and do not acknowledge the received (less
+            recent) LSA instance. */
+         else
+           {
+             struct timeval now;
+             
+             quagga_gettime (QUAGGA_CLK_MONOTONIC, &now);
+             
+             if (tv_cmp (tv_sub (now, current->tv_orig), 
+                         msec2tv (ospf->min_ls_arrival)) >= 0)
+               /* Trap NSSA type later.*/
+               ospf_ls_upd_send_lsa (nbr, current, OSPF_SEND_PACKET_DIRECT);
+             DISCARD_LSA (lsa, 8);
+           }
+       }
+    }
+#undef DISCARD_LSA
+
+  assert (listcount (lsas) == 0);
+  list_delete (lsas);
+}
+
+/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */
+static void
+ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh,
+            struct stream *s, struct ospf_interface *oi, u_int16_t size)
+{
+  struct ospf_neighbor *nbr;
+  
+  /* increment statistics. */
+  oi->ls_ack_in++;
+
+  nbr = ospf_nbr_lookup (oi, iph, ospfh);
+  if (nbr == NULL)
+    {
+      zlog_warn ("Link State Acknowledgment: Unknown Neighbor %s.",
+                inet_ntoa (ospfh->router_id));
+      return;
+    }
+
+  /* Add event to thread. */
+  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived);
+
+  if (nbr->state < NSM_Exchange)
+    {
+      zlog_warn ("Link State Acknowledgment: "
+                "Neighbor[%s] state %s is less than Exchange",
+                inet_ntoa (ospfh->router_id),
+                LOOKUP(ospf_nsm_state_msg, nbr->state));
+      return;
+    }
+  
+  while (size >= OSPF_LSA_HEADER_SIZE)
+    {
+      struct ospf_lsa *lsa, *lsr;
+
+      lsa = ospf_lsa_new ();
+      lsa->data = (struct lsa_header *) STREAM_PNT (s);
+
+      /* lsah = (struct lsa_header *) STREAM_PNT (s); */
+      size -= OSPF_LSA_HEADER_SIZE;
+      stream_forward_getp (s, OSPF_LSA_HEADER_SIZE);
+
+      if (lsa->data->type < OSPF_MIN_LSA || lsa->data->type >= OSPF_MAX_LSA)
+       {
+         lsa->data = NULL;
+         ospf_lsa_discard (lsa);
+         continue;
+       }
+
+      lsr = ospf_ls_retransmit_lookup (nbr, lsa);
+
+      if (lsr != NULL && ospf_lsa_more_recent (lsr, lsa) == 0)
+        ospf_ls_retransmit_delete (nbr, lsr);
+
+      lsa->data = NULL;
+      ospf_lsa_discard (lsa);
+    }
+
+  return;
+}
+
+static struct stream *
+ospf_recv_packet (int fd, struct interface **ifp, struct stream *ibuf)
+{
+  int ret;
+  struct ip *iph;
+  u_int16_t ip_len;
+  ifindex_t ifindex = 0;
+  struct iovec iov;
+  /* Header and data both require alignment. */
+  char buff [CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())];
+  struct msghdr msgh;
+
+  memset (&msgh, 0, sizeof (struct msghdr));
+  msgh.msg_iov = &iov;
+  msgh.msg_iovlen = 1;
+  msgh.msg_control = (caddr_t) buff;
+  msgh.msg_controllen = sizeof (buff);
+  
+  ret = stream_recvmsg (ibuf, fd, &msgh, 0, OSPF_MAX_PACKET_SIZE+1);
+  if (ret < 0)
+    {
+      zlog_warn("stream_recvmsg failed: %s", safe_strerror(errno));
+      return NULL;
+    }
+  if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */
+    {
+      zlog_warn("ospf_recv_packet: discarding runt packet of length %d "
+               "(ip header size is %u)",
+               ret, (u_int)sizeof(iph));
+      return NULL;
+    }
+  
+  /* Note that there should not be alignment problems with this assignment
+     because this is at the beginning of the stream data buffer. */
+  iph = (struct ip *) STREAM_DATA(ibuf);
+  sockopt_iphdrincl_swab_systoh (iph);
+  
+  ip_len = iph->ip_len;
+  
+#if !defined(GNU_LINUX) && (OpenBSD < 200311) && (__FreeBSD_version < 1000000)
+  /*
+   * Kernel network code touches incoming IP header parameters,
+   * before protocol specific processing.
+   *
+   *   1) Convert byteorder to host representation.
+   *      --> ip_len, ip_id, ip_off
+   *
+   *   2) Adjust ip_len to strip IP header size!
+   *      --> If user process receives entire IP packet via RAW
+   *          socket, it must consider adding IP header size to
+   *          the "ip_len" field of "ip" structure.
+   *
+   * For more details, see <netinet/ip_input.c>.
+   */
+  ip_len = ip_len + (iph->ip_hl << 2);
+#endif
+  
+#if defined(__DragonFly__)
+  /*
+   * in DragonFly's raw socket, ip_len/ip_off are read 
+   * in network byte order.
+   * As OpenBSD < 200311 adjust ip_len to strip IP header size!
+   */
+  ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2);
+#endif
+
+  ifindex = getsockopt_ifindex (AF_INET, &msgh);
+  
+  *ifp = if_lookup_by_index (ifindex);
+
+  if (ret != ip_len)
+    {
+      zlog_warn ("ospf_recv_packet read length mismatch: ip_len is %d, "
+                        "but recvmsg returned %d", ip_len, ret);
+      return NULL;
+    }
+  
+  return ibuf;
+}
+
+static struct ospf_interface *
+ospf_associate_packet_vl (struct ospf *ospf, struct interface *ifp, 
+                         struct ip *iph, struct ospf_header *ospfh)
+{
+  struct ospf_interface *rcv_oi;
+  struct ospf_vl_data *vl_data;
+  struct ospf_area *vl_area;
+  struct listnode *node;
+
+  if (IN_MULTICAST (ntohl (iph->ip_dst.s_addr)) ||
+      !OSPF_IS_AREA_BACKBONE (ospfh))
+    return NULL;
+
+  /* look for local OSPF interface matching the destination
+   * to determine Area ID. We presume therefore the destination address
+   * is unique, or at least (for "unnumbered" links), not used in other 
+   * areas
+   */
+  if ((rcv_oi = ospf_if_lookup_by_local_addr (ospf, NULL, 
+                                              iph->ip_dst)) == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data))
+    {
+      vl_area = ospf_area_lookup_by_area_id (ospf, vl_data->vl_area_id);
+      if (!vl_area)
+       continue;
+      
+      if (OSPF_AREA_SAME (&vl_area, &rcv_oi->area) &&
+         IPV4_ADDR_SAME (&vl_data->vl_peer, &ospfh->router_id))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("associating packet with %s",
+                      IF_NAME (vl_data->vl_oi));
+         if (! CHECK_FLAG (vl_data->vl_oi->ifp->flags, IFF_UP))
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("This VL is not up yet, sorry");
+             return NULL;
+           }
+         
+         return vl_data->vl_oi;
+       }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("couldn't find any VL to associate the packet with");
+  
+  return NULL;
+}
+
+static int
+ospf_check_area_id (struct ospf_interface *oi, struct ospf_header *ospfh)
+{
+  /* Check match the Area ID of the receiving interface. */
+  if (OSPF_AREA_SAME (&oi->area, &ospfh))
+    return 1;
+
+  return 0;
+}
+
+/* Unbound socket will accept any Raw IP packets if proto is matched.
+   To prevent it, compare src IP address and i/f address with masking
+   i/f network mask. */
+static int
+ospf_check_network_mask (struct ospf_interface *oi, struct in_addr ip_src)
+{
+  struct in_addr mask, me, him;
+
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
+      oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    return 1;
+
+  masklen2ip (oi->address->prefixlen, &mask);
+
+  me.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
+  him.s_addr = ip_src.s_addr & mask.s_addr;
+
+ if (IPV4_ADDR_SAME (&me, &him))
+   return 1;
+
+ return 0;
+}
+
+/* Return 1, if the packet is properly authenticated and checksummed,
+   0 otherwise. In particular, check that AuType header field is valid and
+   matches the locally configured AuType, and that D.5 requirements are met. */
+static int
+ospf_check_auth (struct ospf_interface *oi, struct ospf_header *ospfh)
+{
+  struct crypt_key *ck;
+  u_int16_t iface_auth_type;
+  u_int16_t pkt_auth_type = ntohs (ospfh->auth_type);
+
+  switch (pkt_auth_type)
+  {
+  case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */
+    if (OSPF_AUTH_NULL != (iface_auth_type = ospf_auth_type (oi)))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Null",
+                   IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type));
+      return 0;
+    }
+    if (! ospf_check_sum (ospfh))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: Null auth OK, but checksum error, Router-ID %s",
+                   IF_NAME (oi), inet_ntoa (ospfh->router_id));
+      return 0;
+    }
+    return 1;
+  case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */
+    if (OSPF_AUTH_SIMPLE != (iface_auth_type = ospf_auth_type (oi)))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Simple",
+                   IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type));
+      return 0;
+    }
+    if (memcmp (OSPF_IF_PARAM (oi, auth_simple), ospfh->u.auth_data, OSPF_AUTH_SIMPLE_SIZE))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: Simple auth failed", IF_NAME (oi));
+      return 0;
+    }
+    if (! ospf_check_sum (ospfh))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: Simple auth OK, checksum error, Router-ID %s",
+                   IF_NAME (oi), inet_ntoa (ospfh->router_id));
+      return 0;
+    }
+    return 1;
+  case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */
+    if (OSPF_AUTH_CRYPTOGRAPHIC != (iface_auth_type = ospf_auth_type (oi)))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Cryptographic",
+                   IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type));
+      return 0;
+    }
+    if (ospfh->checksum)
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: OSPF header checksum is not 0", IF_NAME (oi));
+      return 0;
+    }
+    /* only MD5 crypto method can pass ospf_packet_examin() */
+    if
+    (
+      NULL == (ck = listgetdata (listtail(OSPF_IF_PARAM (oi,auth_crypt)))) ||
+      ospfh->u.crypt.key_id != ck->key_id ||
+      /* Condition above uses the last key ID on the list, which is
+         different from what ospf_crypt_key_lookup() does. A bug? */
+      ! ospf_check_md5_digest (oi, ospfh)
+    )
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+        zlog_warn ("interface %s: MD5 auth failed", IF_NAME (oi));
+      return 0;
+    }
+    return 1;
+  default:
+    if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+      zlog_warn ("interface %s: invalid packet auth-type (%02x)",
+                 IF_NAME (oi), pkt_auth_type);
+    return 0;
+  }
+}
+
+static int
+ospf_check_sum (struct ospf_header *ospfh)
+{
+  u_int32_t ret;
+  u_int16_t sum;
+
+  /* clear auth_data for checksum. */
+  memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE);
+
+  /* keep checksum and clear. */
+  sum = ospfh->checksum;
+  memset (&ospfh->checksum, 0, sizeof (u_int16_t));
+
+  /* calculate checksum. */
+  ret = in_cksum (ospfh, ntohs (ospfh->length));
+
+  if (ret != sum)
+    {
+      zlog_info ("ospf_check_sum(): checksum mismatch, my %X, his %X",
+                ret, sum);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Verify, that given link/TOS records are properly sized/aligned and match
+   Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */
+static unsigned
+ospf_router_lsa_links_examin
+(
+  struct router_lsa_link * link,
+  u_int16_t linkbytes,
+  const u_int16_t num_links
+)
+{
+  unsigned counted_links = 0, thislinklen;
+
+  while (linkbytes)
+  {
+    thislinklen = OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count;
+    if (thislinklen > linkbytes)
+    {
+      if (IS_DEBUG_OSPF_PACKET (0, RECV))
+        zlog_debug ("%s: length error in link block #%u", __func__, counted_links);
+      return MSG_NG;
+    }
+    link = (struct router_lsa_link *)((caddr_t) link + thislinklen);
+    linkbytes -= thislinklen;
+    counted_links++;
+  }
+  if (counted_links != num_links)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: %u link blocks declared, %u present",
+                  __func__, num_links, counted_links);
+    return MSG_NG;
+  }
+  return MSG_OK;
+}
+
+/* Verify, that the given LSA is properly sized/aligned (including type-specific
+   minimum length constraint). */
+static unsigned
+ospf_lsa_examin (struct lsa_header * lsah, const u_int16_t lsalen, const u_char headeronly)
+{
+  unsigned ret;
+  struct router_lsa * rlsa;
+  if
+  (
+    lsah->type < OSPF_MAX_LSA &&
+    ospf_lsa_minlen[lsah->type] &&
+    lsalen < OSPF_LSA_HEADER_SIZE + ospf_lsa_minlen[lsah->type]
+  )
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: undersized (%u B) %s",
+                  __func__, lsalen, LOOKUP (ospf_lsa_type_msg, lsah->type));
+    return MSG_NG;
+  }
+  switch (lsah->type)
+  {
+  case OSPF_ROUTER_LSA:
+    /* RFC2328 A.4.2, LSA header + 4 bytes followed by N>=1 (12+)-byte link blocks */
+    if (headeronly)
+    {
+      ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_ROUTER_LSA_MIN_SIZE) % 4 ? MSG_NG : MSG_OK;
+      break;
+    }
+    rlsa = (struct router_lsa *) lsah;
+    ret = ospf_router_lsa_links_examin
+    (
+      (struct router_lsa_link *) rlsa->link,
+      lsalen - OSPF_LSA_HEADER_SIZE - 4, /* skip: basic header, "flags", 0, "# links" */
+      ntohs (rlsa->links) /* 16 bits */
+    );
+    break;
+  case OSPF_AS_EXTERNAL_LSA:
+    /* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long blocks */
+  case OSPF_AS_NSSA_LSA:
+    /* RFC3101 C, idem */
+    ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_AS_EXTERNAL_LSA_MIN_SIZE) % 12 ? MSG_NG : MSG_OK;
+    break;
+  /* Following LSA types are considered OK length-wise as soon as their minimum
+   * length constraint is met and length of the whole LSA is a multiple of 4
+   * (basic LSA header size is already a multiple of 4). */
+  case OSPF_NETWORK_LSA:
+    /* RFC2328 A.4.3, LSA header + 4 bytes followed by N>=1 router-IDs */
+  case OSPF_SUMMARY_LSA:
+  case OSPF_ASBR_SUMMARY_LSA:
+    /* RFC2328 A.4.4, LSA header + 4 bytes followed by N>=1 4-bytes TOS blocks */
+  case OSPF_OPAQUE_LINK_LSA:
+  case OSPF_OPAQUE_AREA_LSA:
+  case OSPF_OPAQUE_AS_LSA:
+    /* RFC5250 A.2, "some number of octets (of application-specific
+     * data) padded to 32-bit alignment." This is considered equivalent
+     * to 4-byte alignment of all other LSA types, see OSPF-ALIGNMENT.txt
+     * file for the detailed analysis of this passage. */
+    ret = lsalen % 4 ? MSG_NG : MSG_OK;
+    break;
+  default:
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: unsupported LSA type 0x%02x", __func__, lsah->type);
+    return MSG_NG;
+  }
+  if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV))
+    zlog_debug ("%s: alignment error in %s",
+                __func__, LOOKUP (ospf_lsa_type_msg, lsah->type));
+  return ret;
+}
+
+/* Verify if the provided input buffer is a valid sequence of LSAs. This
+   includes verification of LSA blocks length/alignment and dispatching
+   of deeper-level checks. */
+static unsigned
+ospf_lsaseq_examin
+(
+  struct lsa_header *lsah, /* start of buffered data */
+  size_t length,
+  const u_char headeronly,
+  /* When declared_num_lsas is not 0, compare it to the real number of LSAs
+     and treat the difference as an error. */
+  const u_int32_t declared_num_lsas
+)
+{
+  u_int32_t counted_lsas = 0;
+
+  while (length)
+  {
+    u_int16_t lsalen;
+    if (length < OSPF_LSA_HEADER_SIZE)
+    {
+      if (IS_DEBUG_OSPF_PACKET (0, RECV))
+        zlog_debug ("%s: undersized (%zu B) trailing (#%u) LSA header",
+                    __func__, length, counted_lsas);
+      return MSG_NG;
+    }
+    /* save on ntohs() calls here and in the LSA validator */
+    lsalen = ntohs (lsah->length);
+    if (lsalen < OSPF_LSA_HEADER_SIZE)
+    {
+      if (IS_DEBUG_OSPF_PACKET (0, RECV))
+        zlog_debug ("%s: malformed LSA header #%u, declared length is %u B",
+                    __func__, counted_lsas, lsalen);
+      return MSG_NG;
+    }
+    if (headeronly)
+    {
+      /* less checks here and in ospf_lsa_examin() */
+      if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 1))
+      {
+        if (IS_DEBUG_OSPF_PACKET (0, RECV))
+          zlog_debug ("%s: malformed header-only LSA #%u", __func__, counted_lsas);
+        return MSG_NG;
+      }
+      lsah = (struct lsa_header *) ((caddr_t) lsah + OSPF_LSA_HEADER_SIZE);
+      length -= OSPF_LSA_HEADER_SIZE;
+    }
+    else
+    {
+      /* make sure the input buffer is deep enough before further checks */
+      if (lsalen > length)
+      {
+        if (IS_DEBUG_OSPF_PACKET (0, RECV))
+          zlog_debug ("%s: anomaly in LSA #%u: declared length is %u B, buffered length is %zu B",
+                      __func__, counted_lsas, lsalen, length);
+        return MSG_NG;
+      }
+      if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 0))
+      {
+        if (IS_DEBUG_OSPF_PACKET (0, RECV))
+          zlog_debug ("%s: malformed LSA #%u", __func__, counted_lsas);
+        return MSG_NG;
+      }
+      lsah = (struct lsa_header *) ((caddr_t) lsah + lsalen);
+      length -= lsalen;
+    }
+    counted_lsas++;
+  }
+
+  if (declared_num_lsas && counted_lsas != declared_num_lsas)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)",
+                  __func__, declared_num_lsas, counted_lsas);
+    return MSG_NG;
+  }
+  return MSG_OK;
+}
+
+/* Verify a complete OSPF packet for proper sizing/alignment. */
+static unsigned
+ospf_packet_examin (struct ospf_header * oh, const unsigned bytesonwire)
+{
+  u_int16_t bytesdeclared, bytesauth;
+  unsigned ret;
+  struct ospf_ls_update * lsupd;
+
+  /* Length, 1st approximation. */
+  if (bytesonwire < OSPF_HEADER_SIZE)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: undersized (%u B) packet", __func__, bytesonwire);
+    return MSG_NG;
+  }
+  /* Now it is safe to access header fields. Performing length check, allow
+   * for possible extra bytes of crypto auth/padding, which are not counted
+   * in the OSPF header "length" field. */
+  if (oh->version != OSPF_VERSION)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version);
+    return MSG_NG;
+  }
+  bytesdeclared = ntohs (oh->length);
+  if (ntohs (oh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
+    bytesauth = 0;
+  else
+  {
+    if (oh->u.crypt.auth_data_len != OSPF_AUTH_MD5_SIZE)
+    {
+      if (IS_DEBUG_OSPF_PACKET (0, RECV))
+        zlog_debug ("%s: unsupported crypto auth length (%u B)",
+                    __func__, oh->u.crypt.auth_data_len);
+      return MSG_NG;
+    }
+    bytesauth = OSPF_AUTH_MD5_SIZE;
+  }
+  if (bytesdeclared + bytesauth > bytesonwire)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: packet length error (%u real, %u+%u declared)",
+                  __func__, bytesonwire, bytesdeclared, bytesauth);
+    return MSG_NG;
+  }
+  /* Length, 2nd approximation. The type-specific constraint is checked
+     against declared length, not amount of bytes on wire. */
+  if
+  (
+    oh->type >= OSPF_MSG_HELLO &&
+    oh->type <= OSPF_MSG_LS_ACK &&
+    bytesdeclared < OSPF_HEADER_SIZE + ospf_packet_minlen[oh->type]
+  )
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: undersized (%u B) %s packet", __func__,
+                  bytesdeclared, LOOKUP (ospf_packet_type_str, oh->type));
+    return MSG_NG;
+  }
+  switch (oh->type)
+  {
+  case OSPF_MSG_HELLO:
+    /* RFC2328 A.3.2, packet header + OSPF_HELLO_MIN_SIZE bytes followed
+       by N>=0 router-IDs. */
+    ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_HELLO_MIN_SIZE) % 4 ? MSG_NG : MSG_OK;
+    break;
+  case OSPF_MSG_DB_DESC:
+    /* RFC2328 A.3.3, packet header + OSPF_DB_DESC_MIN_SIZE bytes followed
+       by N>=0 header-only LSAs. */
+    ret = ospf_lsaseq_examin
+    (
+      (struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE),
+      bytesdeclared - OSPF_HEADER_SIZE - OSPF_DB_DESC_MIN_SIZE,
+      1, /* header-only LSAs */
+      0
+    );
+    break;
+  case OSPF_MSG_LS_REQ:
+    /* RFC2328 A.3.4, packet header followed by N>=0 12-bytes request blocks. */
+    ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_REQ_MIN_SIZE) %
+      OSPF_LSA_KEY_SIZE ? MSG_NG : MSG_OK;
+    break;
+  case OSPF_MSG_LS_UPD:
+    /* RFC2328 A.3.5, packet header + OSPF_LS_UPD_MIN_SIZE bytes followed
+       by N>=0 full LSAs (with N declared beforehand). */
+    lsupd = (struct ospf_ls_update *) ((caddr_t) oh + OSPF_HEADER_SIZE);
+    ret = ospf_lsaseq_examin
+    (
+      (struct lsa_header *) ((caddr_t) lsupd + OSPF_LS_UPD_MIN_SIZE),
+      bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_UPD_MIN_SIZE,
+      0, /* full LSAs */
+      ntohl (lsupd->num_lsas) /* 32 bits */
+    );
+    break;
+  case OSPF_MSG_LS_ACK:
+    /* RFC2328 A.3.6, packet header followed by N>=0 header-only LSAs. */
+    ret = ospf_lsaseq_examin
+    (
+      (struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_LS_ACK_MIN_SIZE),
+      bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_ACK_MIN_SIZE,
+      1, /* header-only LSAs */
+      0
+    );
+    break;
+  default:
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("%s: invalid packet type 0x%02x", __func__, oh->type);
+    return MSG_NG;
+  }
+  if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV))
+    zlog_debug ("%s: malformed %s packet", __func__, LOOKUP (ospf_packet_type_str, oh->type));
+  return ret;
+}
+
+/* OSPF Header verification. */
+static int
+ospf_verify_header (struct stream *ibuf, struct ospf_interface *oi,
+                   struct ip *iph, struct ospf_header *ospfh)
+{
+  /* Check Area ID. */
+  if (!ospf_check_area_id (oi, ospfh))
+    {
+      zlog_warn ("interface %s: ospf_read invalid Area ID %s.",
+                IF_NAME (oi), inet_ntoa (ospfh->area_id));
+      return -1;
+    }
+
+  /* Check network mask, Silently discarded. */
+  if (! ospf_check_network_mask (oi, iph->ip_src))
+    {
+      zlog_warn ("interface %s: ospf_read network address is not same [%s]",
+                IF_NAME (oi), inet_ntoa (iph->ip_src));
+      return -1;
+    }
+
+  /* Check authentication. The function handles logging actions, where required. */
+  if (! ospf_check_auth (oi, ospfh))
+    return -1;
+
+  return 0;
+}
+
+/* Starting point of packet process function. */
+int
+ospf_read (struct thread *thread)
+{
+  int ret;
+  struct stream *ibuf;
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+  struct ip *iph;
+  struct ospf_header *ospfh;
+  u_int16_t length;
+  struct interface *ifp;
+
+  /* first of all get interface pointer. */
+  ospf = THREAD_ARG (thread);
+
+  /* prepare for next packet. */
+  ospf->t_read = thread_add_read (master, ospf_read, ospf, ospf->fd);
+
+  stream_reset(ospf->ibuf);
+  if (!(ibuf = ospf_recv_packet (ospf->fd, &ifp, ospf->ibuf)))
+    return -1;
+  /* This raw packet is known to be at least as big as its IP header. */
+  
+  /* Note that there should not be alignment problems with this assignment
+     because this is at the beginning of the stream data buffer. */
+  iph = (struct ip *) STREAM_DATA (ibuf);
+  /* Note that sockopt_iphdrincl_swab_systoh was called in ospf_recv_packet. */
+
+  if (ifp == NULL)
+    /* Handle cases where the platform does not support retrieving the ifindex,
+       and also platforms (such as Solaris 8) that claim to support ifindex
+       retrieval but do not. */
+    ifp = if_lookup_address (iph->ip_src);
+  
+  if (ifp == NULL)
+    return 0;
+
+  /* IP Header dump. */
+    if (IS_DEBUG_OSPF_PACKET(0, RECV))
+           ospf_ip_header_dump (iph);
+
+  /* Self-originated packet should be discarded silently. */
+  if (ospf_if_lookup_by_local_addr (ospf, NULL, iph->ip_src))
+    {
+      if (IS_DEBUG_OSPF_PACKET (0, RECV))
+        {
+          zlog_debug ("ospf_read[%s]: Dropping self-originated packet",
+                     inet_ntoa (iph->ip_src));
+        }
+      return 0;
+    }
+
+  /* Advance from IP header to OSPF header (iph->ip_hl has been verified
+     by ospf_recv_packet() to be correct). */
+  stream_forward_getp (ibuf, iph->ip_hl * 4);
+
+  ospfh = (struct ospf_header *) STREAM_PNT (ibuf);
+  if (MSG_OK != ospf_packet_examin (ospfh, stream_get_endp (ibuf) - stream_get_getp (ibuf)))
+    return -1;
+  /* Now it is safe to access all fields of OSPF packet header. */
+
+  /* associate packet with ospf interface */
+  oi = ospf_if_lookup_recv_if (ospf, iph->ip_src, ifp);
+
+  /* ospf_verify_header() relies on a valid "oi" and thus can be called only
+     after the passive/backbone/other checks below are passed. These checks
+     in turn access the fields of unverified "ospfh" structure for their own
+     purposes and must remain very accurate in doing this. */
+
+  /* If incoming interface is passive one, ignore it. */
+  if (oi && OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE)
+    {
+      char buf[3][INET_ADDRSTRLEN];
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ignoring packet from router %s sent to %s, "
+                   "received on a passive interface, %s",
+                   inet_ntop(AF_INET, &ospfh->router_id, buf[0], sizeof(buf[0])),
+                   inet_ntop(AF_INET, &iph->ip_dst, buf[1], sizeof(buf[1])),
+                   inet_ntop(AF_INET, &oi->address->u.prefix4,
+                             buf[2], sizeof(buf[2])));
+
+      if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS))
+       {
+         /* Try to fix multicast membership.
+          * Some OS:es may have problems in this area,
+          * make sure it is removed.
+          */
+         OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS);
+         ospf_if_set_multicast(oi);
+       }
+      return 0;
+  }
+
+
+  /* if no local ospf_interface, 
+   * or header area is backbone but ospf_interface is not
+   * check for VLINK interface
+   */
+  if ( (oi == NULL) ||
+      (OSPF_IS_AREA_ID_BACKBONE(ospfh->area_id)
+      && !OSPF_IS_AREA_ID_BACKBONE(oi->area->area_id))
+     )
+    {
+      if ((oi = ospf_associate_packet_vl (ospf, ifp, iph, ospfh)) == NULL)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("Packet from [%s] received on link %s"
+                        " but no ospf_interface",
+                        inet_ntoa (iph->ip_src), ifp->name);
+          return 0;
+        }
+    }
+
+  /* else it must be a local ospf interface, check it was received on 
+   * correct link 
+   */
+  else if (oi->ifp != ifp)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_warn ("Packet from [%s] received on wrong link %s",
+                   inet_ntoa (iph->ip_src), ifp->name); 
+      return 0;
+    }
+  else if (oi->state == ISM_Down)
+    {
+      char buf[2][INET_ADDRSTRLEN];
+      zlog_warn ("Ignoring packet from %s to %s received on interface that is "
+                "down [%s]; interface flags are %s",
+                inet_ntop(AF_INET, &iph->ip_src, buf[0], sizeof(buf[0])),
+                inet_ntop(AF_INET, &iph->ip_dst, buf[1], sizeof(buf[1])),
+                ifp->name, if_flag_dump(ifp->flags));
+      /* Fix multicast memberships? */
+      if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS))
+        OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS);
+      else if (iph->ip_dst.s_addr == htonl(OSPF_ALLDROUTERS))
+       OI_MEMBER_JOINED(oi, MEMBER_DROUTERS);
+      if (oi->multicast_memberships)
+       ospf_if_set_multicast(oi);
+      return 0;
+    }
+
+  /*
+   * If the received packet is destined for AllDRouters, the packet
+   * should be accepted only if the received ospf interface state is
+   * either DR or Backup -- endo.
+   */
+  if (iph->ip_dst.s_addr == htonl (OSPF_ALLDROUTERS)
+  && (oi->state != ISM_DR && oi->state != ISM_Backup))
+    {
+      zlog_warn ("Dropping packet for AllDRouters from [%s] via [%s] (ISM: %s)",
+                 inet_ntoa (iph->ip_src), IF_NAME (oi),
+                 LOOKUP (ospf_ism_state_msg, oi->state));
+      /* Try to fix multicast membership. */
+      SET_FLAG(oi->multicast_memberships, MEMBER_DROUTERS);
+      ospf_if_set_multicast(oi);
+      return 0;
+    }
+
+  /* Verify more OSPF header fields. */
+  ret = ospf_verify_header (ibuf, oi, iph, ospfh);
+  if (ret < 0)
+  {
+    if (IS_DEBUG_OSPF_PACKET (0, RECV))
+      zlog_debug ("ospf_read[%s]: Header check failed, "
+                  "dropping.",
+                  inet_ntoa (iph->ip_src));
+    return ret;
+  }
+
+  /* Show debug receiving packet. */
+  if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV))
+    {
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL))
+        {
+          zlog_debug ("-----------------------------------------------------");
+          ospf_packet_dump (ibuf);
+        }
+
+      zlog_debug ("%s received from [%s] via [%s]",
+                 LOOKUP (ospf_packet_type_str, ospfh->type),
+                 inet_ntoa (ospfh->router_id), IF_NAME (oi));
+      zlog_debug (" src [%s],", inet_ntoa (iph->ip_src));
+      zlog_debug (" dst [%s]", inet_ntoa (iph->ip_dst));
+
+      if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL))
+       zlog_debug ("-----------------------------------------------------");
+  }
+
+  stream_forward_getp (ibuf, OSPF_HEADER_SIZE);
+
+  /* Adjust size to message length. */
+  length = ntohs (ospfh->length) - OSPF_HEADER_SIZE;
+
+  /* Read rest of the packet and call each sort of packet routine. */
+  switch (ospfh->type)
+    {
+    case OSPF_MSG_HELLO:
+      ospf_hello (iph, ospfh, ibuf, oi, length);
+      break;
+    case OSPF_MSG_DB_DESC:
+      ospf_db_desc (iph, ospfh, ibuf, oi, length);
+      break;
+    case OSPF_MSG_LS_REQ:
+      ospf_ls_req (iph, ospfh, ibuf, oi, length);
+      break;
+    case OSPF_MSG_LS_UPD:
+      ospf_ls_upd (ospf, iph, ospfh, ibuf, oi, length);
+      break;
+    case OSPF_MSG_LS_ACK:
+      ospf_ls_ack (iph, ospfh, ibuf, oi, length);
+      break;
+    default:
+      zlog (NULL, LOG_WARNING,
+           "interface %s: OSPF packet header type %d is illegal",
+           IF_NAME (oi), ospfh->type);
+      break;
+    }
+
+  return 0;
+}
+
+/* Make OSPF header. */
+static void
+ospf_make_header (int type, struct ospf_interface *oi, struct stream *s)
+{
+  struct ospf_header *ospfh;
+
+  ospfh = (struct ospf_header *) STREAM_DATA (s);
+
+  ospfh->version = (u_char) OSPF_VERSION;
+  ospfh->type = (u_char) type;
+
+  ospfh->router_id = oi->ospf->router_id;
+
+  ospfh->checksum = 0;
+  ospfh->area_id = oi->area->area_id;
+  ospfh->auth_type = htons (ospf_auth_type (oi));
+
+  memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE);
+
+  stream_forward_endp (s, OSPF_HEADER_SIZE);
+}
+
+/* Make Authentication Data. */
+static int
+ospf_make_auth (struct ospf_interface *oi, struct ospf_header *ospfh)
+{
+  struct crypt_key *ck;
+
+  switch (ospf_auth_type (oi))
+    {
+    case OSPF_AUTH_NULL:
+      /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */
+      break;
+    case OSPF_AUTH_SIMPLE:
+      memcpy (ospfh->u.auth_data, OSPF_IF_PARAM (oi, auth_simple),
+             OSPF_AUTH_SIMPLE_SIZE);
+      break;
+    case OSPF_AUTH_CRYPTOGRAPHIC:
+      /* If key is not set, then set 0. */
+      if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt)))
+       {
+         ospfh->u.crypt.zero = 0;
+         ospfh->u.crypt.key_id = 0;
+         ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
+       }
+      else
+       {
+         ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt)));
+         ospfh->u.crypt.zero = 0;
+         ospfh->u.crypt.key_id = ck->key_id;
+         ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
+       }
+      /* note: the seq is done in ospf_make_md5_digest() */
+      break;
+    default:
+      /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */
+      break;
+    }
+
+  return 0;
+}
+
+/* Fill rest of OSPF header. */
+static void
+ospf_fill_header (struct ospf_interface *oi,
+                 struct stream *s, u_int16_t length)
+{
+  struct ospf_header *ospfh;
+
+  ospfh = (struct ospf_header *) STREAM_DATA (s);
+
+  /* Fill length. */
+  ospfh->length = htons (length);
+
+  /* Calculate checksum. */
+  if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
+    ospfh->checksum = in_cksum (ospfh, length);
+  else
+    ospfh->checksum = 0;
+
+  /* Add Authentication Data. */
+  ospf_make_auth (oi, ospfh);
+}
+
+static int
+ospf_make_hello (struct ospf_interface *oi, struct stream *s)
+{
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+  u_int16_t length = OSPF_HELLO_MIN_SIZE;
+  struct in_addr mask;
+  unsigned long p;
+  int flag = 0;
+
+  /* Set netmask of interface. */
+  if (!(CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED) &&
+        oi->type == OSPF_IFTYPE_POINTOPOINT) &&
+      oi->type != OSPF_IFTYPE_VIRTUALLINK)
+    masklen2ip (oi->address->prefixlen, &mask);
+  else
+    memset ((char *) &mask, 0, sizeof (struct in_addr));
+  stream_put_ipv4 (s, mask.s_addr);
+
+  /* Set Hello Interval. */
+  if (OSPF_IF_PARAM (oi, fast_hello) == 0)
+    stream_putw (s, OSPF_IF_PARAM (oi, v_hello));
+  else
+    stream_putw (s, 0); /* hello-interval of 0 for fast-hellos */
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("make_hello: options: %x, int: %s",
+              OPTIONS(oi), IF_NAME (oi));
+
+  /* Set Options. */
+  stream_putc (s, OPTIONS (oi));
+
+  /* Set Router Priority. */
+  stream_putc (s, PRIORITY (oi));
+
+  /* Set Router Dead Interval. */
+  stream_putl (s, OSPF_IF_PARAM (oi, v_wait));
+
+  /* Set Designated Router. */
+  stream_put_ipv4 (s, DR (oi).s_addr);
+
+  p = stream_get_endp (s);
+
+  /* Set Backup Designated Router. */
+  stream_put_ipv4 (s, BDR (oi).s_addr);
+
+  /* Add neighbor seen. */
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      if (nbr->router_id.s_addr != 0)  /* Ignore 0.0.0.0 node. */
+       if (nbr->state != NSM_Attempt)  /* Ignore Down neighbor. */
+       if (nbr->state != NSM_Down)     /* This is myself for DR election. */
+         if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id))
+           {
+             /* Check neighbor is sane? */
+             if (nbr->d_router.s_addr != 0
+                 && IPV4_ADDR_SAME (&nbr->d_router, &oi->address->u.prefix4)
+                 && IPV4_ADDR_SAME (&nbr->bd_router, &oi->address->u.prefix4))
+               flag = 1;
+
+             stream_put_ipv4 (s, nbr->router_id.s_addr);
+             length += 4;
+           }
+
+  /* Let neighbor generate BackupSeen. */
+  if (flag == 1)
+    stream_putl_at (s, p, 0); /* ipv4 address, normally */
+
+  return length;
+}
+
+static int
+ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr,
+                  struct stream *s)
+{
+  struct ospf_lsa *lsa;
+  u_int16_t length = OSPF_DB_DESC_MIN_SIZE;
+  u_char options;
+  unsigned long pp;
+  int i;
+  struct ospf_lsdb *lsdb;
+  
+  /* Set Interface MTU. */
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    stream_putw (s, 0);
+  else
+    stream_putw (s, oi->ifp->mtu);
+
+  /* Set Options. */
+  options = OPTIONS (oi);
+  if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE))
+    SET_FLAG (options, OSPF_OPTION_O);
+  stream_putc (s, options);
+
+  /* DD flags */
+  pp = stream_get_endp (s);
+  stream_putc (s, nbr->dd_flags);
+
+  /* Set DD Sequence Number. */
+  stream_putl (s, nbr->dd_seqnum);
+
+  /* shortcut unneeded walk of (empty) summary LSDBs */
+  if (ospf_db_summary_isempty (nbr))
+    goto empty;
+
+  /* Describe LSA Header from Database Summary List. */
+  lsdb = &nbr->db_sum;
+
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      struct route_table *table = lsdb->type[i].db;
+      struct route_node *rn;
+
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if ((lsa = rn->info) != NULL)
+         {
+            if (IS_OPAQUE_LSA (lsa->data->type)
+            && (! CHECK_FLAG (options, OSPF_OPTION_O)))
+              {
+                /* Suppress advertising opaque-informations. */
+                /* Remove LSA from DB summary list. */
+                ospf_lsdb_delete (lsdb, lsa);
+                continue;
+              }
+
+           if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD))
+             {
+               struct lsa_header *lsah;
+               u_int16_t ls_age;
+               
+               /* DD packet overflows interface MTU. */
+               if (length + OSPF_LSA_HEADER_SIZE > ospf_packet_max (oi))
+                 break;
+               
+               /* Keep pointer to LS age. */
+               lsah = (struct lsa_header *) (STREAM_DATA (s) +
+                                             stream_get_endp (s));
+               
+               /* Proceed stream pointer. */
+               stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE);
+               length += OSPF_LSA_HEADER_SIZE;
+               
+               /* Set LS age. */
+               ls_age = LS_AGE (lsa);
+               lsah->ls_age = htons (ls_age);
+               
+             }
+           
+           /* Remove LSA from DB summary list. */
+           ospf_lsdb_delete (lsdb, lsa);
+         }
+    }
+
+  /* Update 'More' bit */
+  if (ospf_db_summary_isempty (nbr))
+    {
+empty:
+      if (nbr->state >= NSM_Exchange)
+        {
+          UNSET_FLAG (nbr->dd_flags, OSPF_DD_FLAG_M);
+          /* Rewrite DD flags */
+          stream_putc_at (s, pp, nbr->dd_flags);
+        }
+      else
+        {
+          assert (IS_SET_DD_M(nbr->dd_flags));
+        }
+    }
+  return length;
+}
+
+static int
+ospf_make_ls_req_func (struct stream *s, u_int16_t *length,
+                      unsigned long delta, struct ospf_neighbor *nbr,
+                      struct ospf_lsa *lsa)
+{
+  struct ospf_interface *oi;
+
+  oi = nbr->oi;
+
+  /* LS Request packet overflows interface MTU. */
+  if (*length + delta > ospf_packet_max(oi))
+    return 0;
+
+  stream_putl (s, lsa->data->type);
+  stream_put_ipv4 (s, lsa->data->id.s_addr);
+  stream_put_ipv4 (s, lsa->data->adv_router.s_addr);
+  
+  ospf_lsa_unlock (&nbr->ls_req_last);
+  nbr->ls_req_last = ospf_lsa_lock (lsa);
+  
+  *length += 12;
+  return 1;
+}
+
+static int
+ospf_make_ls_req (struct ospf_neighbor *nbr, struct stream *s)
+{
+  struct ospf_lsa *lsa;
+  u_int16_t length = OSPF_LS_REQ_MIN_SIZE;
+  unsigned long delta = stream_get_endp(s)+12;
+  struct route_table *table;
+  struct route_node *rn;
+  int i;
+  struct ospf_lsdb *lsdb;
+
+  lsdb = &nbr->ls_req;
+
+  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
+    {
+      table = lsdb->type[i].db;
+      for (rn = route_top (table); rn; rn = route_next (rn))
+       if ((lsa = (rn->info)) != NULL)
+         if (ospf_make_ls_req_func (s, &length, delta, nbr, lsa) == 0)
+           {
+             route_unlock_node (rn);
+             break;
+           }
+    }
+  return length;
+}
+
+static int
+ls_age_increment (struct ospf_lsa *lsa, int delay)
+{
+  int age;
+
+  age = IS_LSA_MAXAGE (lsa) ? OSPF_LSA_MAXAGE : LS_AGE (lsa) + delay;
+
+  return (age > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : age);
+}
+
+static int
+ospf_make_ls_upd (struct ospf_interface *oi, struct list *update, struct stream *s)
+{
+  struct ospf_lsa *lsa;
+  struct listnode *node;
+  u_int16_t length = 0;
+  unsigned int size_noauth;
+  unsigned long delta = stream_get_endp (s);
+  unsigned long pp;
+  int count = 0;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_make_ls_upd: Start");
+
+  pp = stream_get_endp (s);
+  stream_forward_endp (s, OSPF_LS_UPD_MIN_SIZE);
+  length += OSPF_LS_UPD_MIN_SIZE;
+
+  /* Calculate amount of packet usable for data. */
+  size_noauth = stream_get_size(s) - ospf_packet_authspace(oi);
+
+  while ((node = listhead (update)) != NULL)
+    {
+      struct lsa_header *lsah;
+      u_int16_t ls_age;
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_make_ls_upd: List Iteration %d", count);
+
+      lsa = listgetdata (node);
+
+      assert (lsa->data);
+
+      /* Will it fit? */
+      if (length + delta + ntohs (lsa->data->length) > size_noauth)
+        break;
+
+      /* Keep pointer to LS age. */
+      lsah = (struct lsa_header *) (STREAM_DATA (s) + stream_get_endp (s));
+
+      /* Put LSA to Link State Request. */
+      stream_put (s, lsa->data, ntohs (lsa->data->length));
+
+      /* Set LS age. */
+      /* each hop must increment an lsa_age by transmit_delay 
+         of OSPF interface */
+      ls_age = ls_age_increment (lsa, OSPF_IF_PARAM (oi, transmit_delay));
+      lsah->ls_age = htons (ls_age);
+
+      length += ntohs (lsa->data->length);
+      count++;
+
+      list_delete_node (update, node);
+      ospf_lsa_unlock (&lsa); /* oi->ls_upd_queue */
+    }
+
+  /* Now set #LSAs. */
+  stream_putl_at (s, pp, count);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_make_ls_upd: Stop");
+  return length;
+}
+
+static int
+ospf_make_ls_ack (struct ospf_interface *oi, struct list *ack, struct stream *s)
+{
+  struct listnode *node, *nnode;
+  u_int16_t length = OSPF_LS_ACK_MIN_SIZE;
+  unsigned long delta = stream_get_endp(s) + 24;
+  struct ospf_lsa *lsa;
+
+  for (ALL_LIST_ELEMENTS (ack, node, nnode, lsa))
+    {
+      assert (lsa);
+      
+      if (length + delta > ospf_packet_max (oi))
+       break;
+      
+      stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE);
+      length += OSPF_LSA_HEADER_SIZE;
+      
+      listnode_delete (ack, lsa);
+      ospf_lsa_unlock (&lsa); /* oi->ls_ack_direct.ls_ack */
+    }
+  
+  return length;
+}
+
+static void
+ospf_hello_send_sub (struct ospf_interface *oi, in_addr_t addr)
+{
+  struct ospf_packet *op;
+  u_int16_t length = OSPF_HEADER_SIZE;
+
+  op = ospf_packet_new (oi->ifp->mtu);
+
+  /* Prepare OSPF common header. */
+  ospf_make_header (OSPF_MSG_HELLO, oi, op->s);
+
+  /* Prepare OSPF Hello body. */
+  length += ospf_make_hello (oi, op->s);
+
+  /* Fill OSPF header. */
+  ospf_fill_header (oi, op->s, length);
+
+  /* Set packet length. */
+  op->length = length;
+
+  op->dst.s_addr = addr;
+
+  /* Add packet to the top of the interface output queue, so that they
+   * can't get delayed by things like long queues of LS Update packets
+   */
+  ospf_packet_add_top (oi, op);
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+}
+
+static void
+ospf_poll_send (struct ospf_nbr_nbma *nbr_nbma)
+{
+  struct ospf_interface *oi;
+
+  oi = nbr_nbma->oi;
+  assert(oi);
+
+  /* If this is passive interface, do not send OSPF Hello. */
+  if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE)
+    return;
+
+  if (oi->type != OSPF_IFTYPE_NBMA)
+    return;
+
+  if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down)
+    return;
+
+  if (PRIORITY(oi) == 0)
+    return;
+
+  if (nbr_nbma->priority == 0
+      && oi->state != ISM_DR && oi->state != ISM_Backup)
+    return;
+
+  ospf_hello_send_sub (oi, nbr_nbma->addr.s_addr);
+}
+
+int
+ospf_poll_timer (struct thread *thread)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = THREAD_ARG (thread);
+  nbr_nbma->t_poll = NULL;
+
+  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Poll timer expire)",
+    IF_NAME (nbr_nbma->oi), inet_ntoa (nbr_nbma->addr));
+
+  ospf_poll_send (nbr_nbma);
+
+  if (nbr_nbma->v_poll > 0)
+    OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer,
+                       nbr_nbma->v_poll);
+
+  return 0;
+}
+
+
+int
+ospf_hello_reply_timer (struct thread *thread)
+{
+  struct ospf_neighbor *nbr;
+
+  nbr = THREAD_ARG (thread);
+  nbr->t_hello_reply = NULL;
+
+  assert (nbr->oi);
+
+  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
+    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (hello-reply timer expire)",
+         IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
+
+  ospf_hello_send_sub (nbr->oi, nbr->address.u.prefix4.s_addr);
+
+  return 0;
+}
+
+/* Send OSPF Hello. */
+void
+ospf_hello_send (struct ospf_interface *oi)
+{
+  /* If this is passive interface, do not send OSPF Hello. */
+  if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE)
+    return;
+
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    {
+      struct ospf_neighbor *nbr;
+      struct route_node *rn;
+
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info))
+         if (nbr != oi->nbr_self)
+           if (nbr->state != NSM_Down)
+             {
+               /*  RFC 2328  Section 9.5.1
+                   If the router is not eligible to become Designated Router,
+                   it must periodically send Hello Packets to both the
+                   Designated Router and the Backup Designated Router (if they
+                   exist).  */
+               if (PRIORITY(oi) == 0 &&
+                   IPV4_ADDR_CMP(&DR(oi),  &nbr->address.u.prefix4) &&
+                   IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4))
+                 continue;
+
+               /*  If the router is eligible to become Designated Router, it
+                   must periodically send Hello Packets to all neighbors that
+                   are also eligible. In addition, if the router is itself the
+                   Designated Router or Backup Designated Router, it must also
+                   send periodic Hello Packets to all other neighbors. */
+
+               if (nbr->priority == 0 && oi->state == ISM_DROther)
+                 continue;
+               /* if oi->state == Waiting, send hello to all neighbors */
+               ospf_hello_send_sub (oi, nbr->address.u.prefix4.s_addr);
+             }
+    }
+  else
+    {
+      /* Decide destination address. */
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+        ospf_hello_send_sub (oi, oi->vl_data->peer_addr.s_addr);
+      else
+        ospf_hello_send_sub (oi, htonl (OSPF_ALLSPFROUTERS));
+    }
+}
+
+/* Send OSPF Database Description. */
+void
+ospf_db_desc_send (struct ospf_neighbor *nbr)
+{
+  struct ospf_interface *oi;
+  struct ospf_packet *op;
+  u_int16_t length = OSPF_HEADER_SIZE;
+
+  oi = nbr->oi;
+  op = ospf_packet_new (oi->ifp->mtu);
+
+  /* Prepare OSPF common header. */
+  ospf_make_header (OSPF_MSG_DB_DESC, oi, op->s);
+
+  /* Prepare OSPF Database Description body. */
+  length += ospf_make_db_desc (oi, nbr, op->s);
+
+  /* Fill OSPF header. */
+  ospf_fill_header (oi, op->s, length);
+
+  /* Set packet length. */
+  op->length = length;
+
+  /* Decide destination address. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT) 
+    op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+    op->dst = nbr->address.u.prefix4;
+
+  /* Add packet to the interface output queue. */
+  ospf_packet_add (oi, op);
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+
+  /* Remove old DD packet, then copy new one and keep in neighbor structure. */
+  if (nbr->last_send)
+    ospf_packet_free (nbr->last_send);
+  nbr->last_send = ospf_packet_dup (op);
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &nbr->last_send_ts);
+}
+
+/* Re-send Database Description. */
+void
+ospf_db_desc_resend (struct ospf_neighbor *nbr)
+{
+  struct ospf_interface *oi;
+
+  oi = nbr->oi;
+
+  /* Add packet to the interface output queue. */
+  ospf_packet_add (oi, ospf_packet_dup (nbr->last_send));
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+}
+
+/* Send Link State Request. */
+void
+ospf_ls_req_send (struct ospf_neighbor *nbr)
+{
+  struct ospf_interface *oi;
+  struct ospf_packet *op;
+  u_int16_t length = OSPF_HEADER_SIZE;
+
+  oi = nbr->oi;
+  op = ospf_packet_new (oi->ifp->mtu);
+
+  /* Prepare OSPF common header. */
+  ospf_make_header (OSPF_MSG_LS_REQ, oi, op->s);
+
+  /* Prepare OSPF Link State Request body. */
+  length += ospf_make_ls_req (nbr, op->s);
+  if (length == OSPF_HEADER_SIZE)
+    {
+      ospf_packet_free (op);
+      return;
+    }
+
+  /* Fill OSPF header. */
+  ospf_fill_header (oi, op->s, length);
+
+  /* Set packet length. */
+  op->length = length;
+
+  /* Decide destination address. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT) 
+    op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+    op->dst = nbr->address.u.prefix4;
+
+  /* Add packet to the interface output queue. */
+  ospf_packet_add (oi, op);
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+
+  /* Add Link State Request Retransmission Timer. */
+  OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req);
+}
+
+/* Send Link State Update with an LSA. */
+void
+ospf_ls_upd_send_lsa (struct ospf_neighbor *nbr, struct ospf_lsa *lsa,
+                     int flag)
+{
+  struct list *update;
+
+  update = list_new ();
+
+  listnode_add (update, lsa);
+  ospf_ls_upd_send (nbr, update, flag);
+
+  list_delete (update);
+}
+
+/* Determine size for packet. Must be at least big enough to accomodate next
+ * LSA on list, which may be bigger than MTU size.
+ *
+ * Return pointer to new ospf_packet
+ * NULL if we can not allocate, eg because LSA is bigger than imposed limit
+ * on packet sizes (in which case offending LSA is deleted from update list)
+ */
+static struct ospf_packet *
+ospf_ls_upd_packet_new (struct list *update, struct ospf_interface *oi)
+{
+  struct ospf_lsa *lsa;
+  struct listnode *ln;
+  size_t size;
+  static char warned = 0;
+
+  lsa = listgetdata((ln = listhead (update)));
+  assert (lsa->data);
+
+  if ((OSPF_LS_UPD_MIN_SIZE + ntohs (lsa->data->length))
+      > ospf_packet_max (oi))
+    {
+      if (!warned)
+        {
+          zlog_warn ("ospf_ls_upd_packet_new: oversized LSA encountered!"
+                     "will need to fragment. Not optimal. Try divide up"
+                     " your network with areas. Use 'debug ospf packet send'"
+                     " to see details, or look at 'show ip ospf database ..'");
+          warned = 1;
+        }
+
+      if (IS_DEBUG_OSPF_PACKET (0, SEND))
+        zlog_debug ("ospf_ls_upd_packet_new: oversized LSA id:%s,"
+                   " %d bytes originated by %s, will be fragmented!",
+                   inet_ntoa (lsa->data->id),
+                   ntohs (lsa->data->length),
+                   inet_ntoa (lsa->data->adv_router));
+
+      /* 
+       * Allocate just enough to fit this LSA only, to avoid including other
+       * LSAs in fragmented LSA Updates.
+       */
+      size = ntohs (lsa->data->length) + (oi->ifp->mtu - ospf_packet_max (oi))
+             + OSPF_LS_UPD_MIN_SIZE;
+    }
+  else
+    size = oi->ifp->mtu;
+
+  if (size > OSPF_MAX_PACKET_SIZE)
+    {
+      zlog_warn ("ospf_ls_upd_packet_new: oversized LSA id:%s too big,"
+                 " %d bytes, packet size %ld, dropping it completely."
+                 " OSPF routing is broken!",
+                 inet_ntoa (lsa->data->id), ntohs (lsa->data->length),
+                 (long int) size);
+      list_delete_node (update, ln);
+      return NULL;
+    }
+
+  /* IP header is built up separately by ospf_write(). This means, that we must
+   * reduce the "affordable" size just calculated by length of an IP header.
+   * This makes sure, that even if we manage to fill the payload with LSA data
+   * completely, the final packet (our data plus IP header) still fits into
+   * outgoing interface MTU. This correction isn't really meaningful for an
+   * oversized LSA, but for consistency the correction is done for both cases.
+   *
+   * P.S. OSPF_MAX_PACKET_SIZE above already includes IP header size
+   */
+  return ospf_packet_new (size - sizeof (struct ip));
+}
+
+static void
+ospf_ls_upd_queue_send (struct ospf_interface *oi, struct list *update,
+                       struct in_addr addr)
+{
+  struct ospf_packet *op;
+  u_int16_t length = OSPF_HEADER_SIZE;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("listcount = %d, [%s]dst %s", listcount (update), IF_NAME(oi),
+                           inet_ntoa(addr));
+  
+  op = ospf_ls_upd_packet_new (update, oi);
+
+  /* Prepare OSPF common header. */
+  ospf_make_header (OSPF_MSG_LS_UPD, oi, op->s);
+
+  /* Prepare OSPF Link State Update body.
+   * Includes Type-7 translation. 
+   */
+  length += ospf_make_ls_upd (oi, update, op->s);
+
+  /* Fill OSPF header. */
+  ospf_fill_header (oi, op->s, length);
+
+  /* Set packet length. */
+  op->length = length;
+
+  /* Decide destination address. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT) 
+    op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+    op->dst.s_addr = addr.s_addr;
+
+  /* Add packet to the interface output queue. */
+  ospf_packet_add (oi, op);
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+}
+
+static int
+ospf_ls_upd_send_queue_event (struct thread *thread)
+{
+  struct ospf_interface *oi = THREAD_ARG(thread);
+  struct route_node *rn;
+  struct route_node *rnext;
+  struct list *update;
+  char again = 0;
+  
+  oi->t_ls_upd_event = NULL;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ls_upd_send_queue start");
+
+  for (rn = route_top (oi->ls_upd_queue); rn; rn = rnext)
+    {
+      rnext = route_next (rn);
+      
+      if (rn->info == NULL)
+        continue;
+      
+      update = (struct list *)rn->info;
+
+      ospf_ls_upd_queue_send (oi, update, rn->p.u.prefix4);
+      
+      /* list might not be empty. */
+      if (listcount(update) == 0)
+        {
+          list_delete (rn->info);
+          rn->info = NULL;
+          route_unlock_node (rn);
+        }
+      else
+        again = 1;
+    }
+
+  if (again != 0)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_ls_upd_send_queue: update lists not cleared,"
+                   " %d nodes to try again, raising new event", again);
+      oi->t_ls_upd_event = 
+        thread_add_event (master, ospf_ls_upd_send_queue_event, oi, 0);
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_ls_upd_send_queue stop");
+  
+  return 0;
+}
+
+void
+ospf_ls_upd_send (struct ospf_neighbor *nbr, struct list *update, int flag)
+{
+  struct ospf_interface *oi;
+  struct ospf_lsa *lsa;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct listnode *node;
+  
+  oi = nbr->oi;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  
+  /* Decide destination address. */
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    p.prefix = oi->vl_data->peer_addr;
+  else if (oi->type == OSPF_IFTYPE_POINTOPOINT) 
+     p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else if (flag == OSPF_SEND_PACKET_DIRECT)
+     p.prefix = nbr->address.u.prefix4;
+  else if (oi->state == ISM_DR || oi->state == ISM_Backup)
+     p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+     p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+     p.prefix.s_addr = htonl (OSPF_ALLDROUTERS);
+
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    {
+      if (flag == OSPF_SEND_PACKET_INDIRECT)
+       zlog_warn ("* LS-Update is directly sent on NBMA network.");
+      if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix.s_addr))
+       zlog_warn ("* LS-Update is sent to myself.");
+    }
+
+  rn = route_node_get (oi->ls_upd_queue, (struct prefix *) &p);
+
+  if (rn->info == NULL)
+    rn->info = list_new ();
+  else
+    route_unlock_node (rn);
+
+  for (ALL_LIST_ELEMENTS_RO (update, node, lsa))
+    listnode_add (rn->info, ospf_lsa_lock (lsa)); /* oi->ls_upd_queue */
+
+  if (oi->t_ls_upd_event == NULL)
+    oi->t_ls_upd_event =
+      thread_add_event (master, ospf_ls_upd_send_queue_event, oi, 0);
+}
+
+static void
+ospf_ls_ack_send_list (struct ospf_interface *oi, struct list *ack,
+                      struct in_addr dst)
+{
+  struct ospf_packet *op;
+  u_int16_t length = OSPF_HEADER_SIZE;
+
+  op = ospf_packet_new (oi->ifp->mtu);
+
+  /* Prepare OSPF common header. */
+  ospf_make_header (OSPF_MSG_LS_ACK, oi, op->s);
+
+  /* Prepare OSPF Link State Acknowledgment body. */
+  length += ospf_make_ls_ack (oi, ack, op->s);
+
+  /* Fill OSPF header. */
+  ospf_fill_header (oi, op->s, length);
+
+  /* Set packet length. */
+  op->length = length;
+
+  /* Decide destination address. */
+  if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+    op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+    op->dst.s_addr = dst.s_addr;
+
+  /* Add packet to the interface output queue. */
+  ospf_packet_add (oi, op);
+
+  /* Hook thread to write packet. */
+  OSPF_ISM_WRITE_ON (oi->ospf);
+}
+
+static int
+ospf_ls_ack_send_event (struct thread *thread)
+{
+  struct ospf_interface *oi = THREAD_ARG (thread);
+
+  oi->t_ls_ack_direct = NULL;
+  
+  while (listcount (oi->ls_ack_direct.ls_ack))
+    ospf_ls_ack_send_list (oi, oi->ls_ack_direct.ls_ack,
+                          oi->ls_ack_direct.dst);
+
+  return 0;
+}
+
+void
+ospf_ls_ack_send (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+  struct ospf_interface *oi = nbr->oi;
+
+  if (listcount (oi->ls_ack_direct.ls_ack) == 0)
+    oi->ls_ack_direct.dst = nbr->address.u.prefix4;
+  
+  listnode_add (oi->ls_ack_direct.ls_ack, ospf_lsa_lock (lsa));
+  
+  if (oi->t_ls_ack_direct == NULL)
+    oi->t_ls_ack_direct =
+      thread_add_event (master, ospf_ls_ack_send_event, oi, 0);
+}
+
+/* Send Link State Acknowledgment delayed. */
+void
+ospf_ls_ack_send_delayed (struct ospf_interface *oi)
+{
+  struct in_addr dst;
+  
+  /* Decide destination address. */
+  /* RFC2328 Section 13.5                           On non-broadcast
+       networks, delayed Link State Acknowledgment packets must be
+       unicast separately over each adjacency (i.e., neighbor whose
+       state is >= Exchange).  */
+  if (oi->type == OSPF_IFTYPE_NBMA)
+    {
+      struct ospf_neighbor *nbr;
+      struct route_node *rn;
+
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info) != NULL)
+         if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange)
+           while (listcount (oi->ls_ack))
+             ospf_ls_ack_send_list (oi, oi->ls_ack, nbr->address.u.prefix4);
+      return;
+    }
+  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+    dst.s_addr = oi->vl_data->peer_addr.s_addr;
+  else if (oi->state == ISM_DR || oi->state == ISM_Backup)
+    dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+    dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+    dst.s_addr = htonl (OSPF_ALLSPFROUTERS);
+  else
+    dst.s_addr = htonl (OSPF_ALLDROUTERS);
+
+  while (listcount (oi->ls_ack))
+    ospf_ls_ack_send_list (oi, oi->ls_ack, dst);
+}
diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h
new file mode 100644 (file)
index 0000000..337686a
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * OSPF Sending and Receiving OSPF Packets.
+ * Copyright (C) 1999 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_PACKET_H
+#define _ZEBRA_OSPF_PACKET_H
+
+#define OSPF_HEADER_SIZE         24U
+#define OSPF_AUTH_SIMPLE_SIZE     8U
+#define OSPF_AUTH_MD5_SIZE       16U
+
+#define OSPF_MAX_PACKET_SIZE  65535U   /* includes IP Header size. */
+#define OSPF_HELLO_MIN_SIZE      20U   /* not including neighbors */
+#define OSPF_DB_DESC_MIN_SIZE     8U
+#define OSPF_LS_REQ_MIN_SIZE      0U
+#define OSPF_LS_UPD_MIN_SIZE      4U
+#define OSPF_LS_ACK_MIN_SIZE      0U
+
+#define OSPF_MSG_HELLO         1  /* OSPF Hello Message. */
+#define OSPF_MSG_DB_DESC       2  /* OSPF Database Descriptoin Message. */
+#define OSPF_MSG_LS_REQ        3  /* OSPF Link State Request Message. */
+#define OSPF_MSG_LS_UPD        4  /* OSPF Link State Update Message. */
+#define OSPF_MSG_LS_ACK        5  /* OSPF Link State Acknoledgement Message. */
+
+#define OSPF_SEND_PACKET_DIRECT         1
+#define OSPF_SEND_PACKET_INDIRECT       2
+#define OSPF_SEND_PACKET_LOOP           3
+
+#define OSPF_HELLO_REPLY_DELAY          1
+
+/* Return values of functions involved in packet verification, see ospf6d. */
+#define MSG_OK    0
+#define MSG_NG    1
+
+struct ospf_packet
+{
+  struct ospf_packet *next;
+
+  /* Pointer to data stream. */
+  struct stream *s;
+
+  /* IP destination address. */
+  struct in_addr dst;
+
+  /* OSPF packet length. */
+  u_int16_t length;
+};
+
+/* OSPF packet queue structure. */
+struct ospf_fifo
+{
+  unsigned long count;
+
+  struct ospf_packet *head;
+  struct ospf_packet *tail;
+};
+
+/* OSPF packet header structure. */
+struct ospf_header
+{
+  u_char version;                       /* OSPF Version. */
+  u_char type;                          /* Packet Type. */
+  u_int16_t length;                     /* Packet Length. */
+  struct in_addr router_id;             /* Router ID. */
+  struct in_addr area_id;               /* Area ID. */
+  u_int16_t checksum;                   /* Check Sum. */
+  u_int16_t auth_type;                  /* Authentication Type. */
+  /* Authentication Data. */
+  union
+  {
+    /* Simple Authentication. */
+    u_char auth_data [OSPF_AUTH_SIMPLE_SIZE];
+    /* Cryptographic Authentication. */
+    struct
+    {
+      u_int16_t zero;                   /* Should be 0. */
+      u_char key_id;                    /* Key ID. */
+      u_char auth_data_len;             /* Auth Data Length. */
+      u_int32_t crypt_seqnum;           /* Cryptographic Sequence Number. */
+    } crypt;
+  } u;
+};
+
+/* OSPF Hello body format. */
+struct ospf_hello
+{
+  struct in_addr network_mask;
+  u_int16_t hello_interval;
+  u_char options;
+  u_char priority;
+  u_int32_t dead_interval;
+  struct in_addr d_router;
+  struct in_addr bd_router;
+  struct in_addr neighbors[1];
+};
+
+/* OSPF Database Description body format. */
+struct ospf_db_desc
+{
+  u_int16_t mtu;
+  u_char options;
+  u_char flags;
+  u_int32_t dd_seqnum;
+};
+
+struct ospf_ls_update
+{
+  u_int32_t num_lsas;
+};
+
+/* Macros. */
+/* XXX Perhaps obsolete; function in ospf_packet.c */
+#define OSPF_PACKET_MAX(oi)     ospf_packet_max (oi)
+
+#define OSPF_OUTPUT_PNT(S)      ((S)->data + (S)->putp)
+#define OSPF_OUTPUT_LENGTH(S)   ((S)->endp)
+
+#define IS_SET_DD_MS(X)         ((X) & OSPF_DD_FLAG_MS)
+#define IS_SET_DD_M(X)          ((X) & OSPF_DD_FLAG_M)
+#define IS_SET_DD_I(X)          ((X) & OSPF_DD_FLAG_I)
+#define IS_SET_DD_ALL(X)        ((X) & OSPF_DD_FLAG_ALL)
+
+/* Prototypes. */
+extern void ospf_output_forward (struct stream *, int);
+extern struct ospf_packet *ospf_packet_new (size_t);
+extern void ospf_packet_free (struct ospf_packet *);
+extern struct ospf_fifo *ospf_fifo_new (void);
+extern void ospf_fifo_push (struct ospf_fifo *, struct ospf_packet *);
+extern struct ospf_packet *ospf_fifo_pop (struct ospf_fifo *);
+extern struct ospf_packet *ospf_fifo_head (struct ospf_fifo *);
+extern void ospf_fifo_flush (struct ospf_fifo *);
+extern void ospf_fifo_free (struct ospf_fifo *);
+extern void ospf_packet_add (struct ospf_interface *, struct ospf_packet *);
+extern void ospf_packet_delete (struct ospf_interface *);
+extern struct stream *ospf_stream_dup (struct stream *);
+extern struct ospf_packet *ospf_packet_dup (struct ospf_packet *);
+
+extern int ospf_read (struct thread *);
+extern void ospf_hello_send (struct ospf_interface *);
+extern void ospf_db_desc_send (struct ospf_neighbor *);
+extern void ospf_db_desc_resend (struct ospf_neighbor *);
+extern void ospf_ls_req_send (struct ospf_neighbor *);
+extern void ospf_ls_upd_send_lsa (struct ospf_neighbor *, struct ospf_lsa *,
+                                 int);
+extern void ospf_ls_upd_send (struct ospf_neighbor *, struct list *, int);
+extern void ospf_ls_ack_send (struct ospf_neighbor *, struct ospf_lsa *);
+extern void ospf_ls_ack_send_delayed (struct ospf_interface *);
+extern void ospf_ls_retransmit (struct ospf_interface *, struct ospf_lsa *);
+extern void ospf_ls_req_event (struct ospf_neighbor *);
+
+extern int ospf_ls_upd_timer (struct thread *);
+extern int ospf_ls_ack_timer (struct thread *);
+extern int ospf_poll_timer (struct thread *);
+extern int ospf_hello_reply_timer (struct thread *);
+
+extern const struct message ospf_packet_type_str[];
+extern const size_t ospf_packet_type_str_max;
+
+#endif /* _ZEBRA_OSPF_PACKET_H */
diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c
new file mode 100644 (file)
index 0000000..f093208
--- /dev/null
@@ -0,0 +1,1637 @@
+/*
+ * This is an implementation of RFC4970 Router Information
+ * with support of RFC5088 PCE Capabilites announcement
+ *
+ * Module name: Router Information
+ * Version:     0.99.22
+ * Created:     2012-02-01 by Olivier Dugeon
+ * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ *
+ * This file is part of GNU Quagga.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU 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 GNU Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"          /* for inet_aton() */
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_te.h"
+
+struct ospf_pce_info
+{
+
+  /* Store Router Information PCE TLV and SubTLV in network byte order. */
+  struct ri_tlv_pce pce_header;
+  struct ri_pce_subtlv_address pce_address;
+  struct ri_pce_subtlv_path_scope pce_scope;
+  struct list *pce_domain;
+  struct list *pce_neighbor;
+  struct ri_pce_subtlv_cap_flag pce_cap_flag;
+};
+
+/* Following structure are internal use only. */
+struct ospf_router_info
+{
+  status_t status;
+
+  u_int8_t registered;
+  u_int8_t scope;
+
+  /* Flags to manage this router information. */
+#define RIFLG_LOOKUP_DONE                      0x1
+#define RIFLG_LSA_ENGAGED                      0x2
+#define RIFLG_LSA_FORCED_REFRESH       0x4
+  u_int32_t flags;
+
+  /* area pointer if flooding is Type 10 Null if flooding is AS scope */
+  struct ospf_area *area;
+  struct in_addr area_id;
+
+  /* Store Router Information Capabilities LSA */
+  struct ri_tlv_router_cap router_cap;
+
+  /* Store PCE capability LSA */
+  struct ospf_pce_info pce_info;
+};
+
+/*
+ * Global variable to manage Opaque-LSA/Router Information on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_router_info OspfRI;
+
+/*------------------------------------------------------------------------------*
+ * Followings are initialize/terminate functions for Router Information handling.
+ *------------------------------------------------------------------------------*/
+
+static void ospf_router_info_ism_change (struct ospf_interface *oi,
+                                         int old_status);
+static void ospf_router_info_nsm_change (struct ospf_neighbor *nbr,
+                                         int old_status);
+static void ospf_router_info_config_write_router (struct vty *vty);
+static void ospf_router_info_show_info (struct vty *vty,
+                                        struct ospf_lsa *lsa);
+static int ospf_router_info_lsa_originate (void *arg);
+static struct ospf_lsa *ospf_router_info_lsa_refresh (struct ospf_lsa *lsa);
+static void ospf_router_info_lsa_schedule (opcode_t opcode);
+static void ospf_router_info_register_vty (void);
+static void del_pce_info (void *val);
+
+int
+ospf_router_info_init (void)
+{
+
+  memset (&OspfRI, 0, sizeof (struct ospf_router_info));
+  OspfRI.status = disabled;
+  OspfRI.registered = 0;
+  OspfRI.scope = OSPF_OPAQUE_AS_LSA;
+  OspfRI.flags = 0;
+
+  /* Initialize pce domain and neighbor list */
+  OspfRI.pce_info.pce_domain = list_new ();
+  OspfRI.pce_info.pce_domain->del = del_pce_info;
+  OspfRI.pce_info.pce_neighbor = list_new ();
+  OspfRI.pce_info.pce_neighbor->del = del_pce_info;
+
+  ospf_router_info_register_vty ();
+
+  return 0;
+}
+
+static int
+ospf_router_info_register (u_int8_t scope)
+{
+  int rc = 0;
+
+  if (OspfRI.registered)
+    return 0;
+
+  zlog_info ("Register Router Information with scope %s(%d)",
+             scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope);
+  rc = ospf_register_opaque_functab (scope,
+                                     OPAQUE_TYPE_ROUTER_INFORMATION_LSA,
+                                     NULL,    /* new interface */
+                                     NULL,    /* del interface */
+                                     ospf_router_info_ism_change,
+                                     ospf_router_info_nsm_change,
+                                     ospf_router_info_config_write_router,
+                                     NULL,    /* Config. write interface */
+                                     NULL,    /* Config. write debug */
+                                     ospf_router_info_show_info,
+                                     ospf_router_info_lsa_originate,
+                                     ospf_router_info_lsa_refresh,
+                                     NULL,    /* new_lsa_hook */
+                                     NULL);   /* del_lsa_hook */
+
+  if (rc != 0)
+    {
+      zlog_warn ("ospf_router_info_init: Failed to register functions");
+      return rc;
+    }
+
+  OspfRI.registered = 1;
+  OspfRI.scope = scope;
+  return 0;
+}
+
+static int
+ospf_router_info_unregister ()
+{
+
+  if ((OspfRI.scope != OSPF_OPAQUE_AS_LSA)
+      && (OspfRI.scope != OSPF_OPAQUE_AREA_LSA))
+    {
+      zlog_warn ("Unable to unregister Router Info functions: Wrong scope!");
+      return -1;
+    }
+
+  ospf_delete_opaque_functab (OspfRI.scope,
+                              OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+
+  OspfRI.registered = 0;
+  return 0;
+
+}
+
+void
+ospf_router_info_term (void)
+{
+
+  list_delete (OspfRI.pce_info.pce_domain);
+  list_delete (OspfRI.pce_info.pce_neighbor);
+
+  OspfRI.pce_info.pce_domain = NULL;
+  OspfRI.pce_info.pce_neighbor = NULL;
+  OspfRI.status = disabled;
+
+  ospf_router_info_unregister (OspfRI.scope);
+
+  return;
+}
+
+static void
+del_pce_info (void *val)
+{
+  XFREE (MTYPE_OSPF_PCE_PARAMS, val);
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are control functions for ROUTER INFORMATION parameters management.
+ *------------------------------------------------------------------------*/
+
+static void
+set_router_info_capabilities (struct ri_tlv_router_cap *ric, u_int32_t cap)
+{
+  ric->header.type = htons (RI_TLV_CAPABILITIES);
+  ric->header.length = htons (RI_TLV_LENGTH);
+  ric->value = htonl (cap);
+  return;
+}
+
+static int
+set_pce_header (struct ospf_pce_info *pce)
+{
+  u_int16_t length = 0;
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *domain;
+  struct ri_pce_subtlv_neighbor *neighbor;
+
+  /* PCE Address */
+  if (ntohs (pce->pce_address.header.type) != 0)
+    length += RI_TLV_SIZE (&pce->pce_address.header);
+
+  /* PCE Path Scope */
+  if (ntohs (pce->pce_scope.header.type) != 0)
+    length += RI_TLV_SIZE (&pce->pce_scope.header);
+
+  /* PCE Domain */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+    {
+      if (ntohs (domain->header.type) != 0)
+        length += RI_TLV_SIZE (&domain->header);
+    }
+
+  /* PCE Neighbor */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+    {
+      if (ntohs (neighbor->header.type) != 0)
+        length += RI_TLV_SIZE (&neighbor->header);
+    }
+
+  /* PCE Capabilities */
+  if (ntohs (pce->pce_cap_flag.header.type) != 0)
+    length += RI_TLV_SIZE (&pce->pce_cap_flag.header);
+
+  if (length != 0)
+    {
+      pce->pce_header.header.type = htons (RI_TLV_PCE);
+      pce->pce_header.header.length = htons (length);
+    }
+  else
+    {
+      pce->pce_header.header.type = 0;
+      pce->pce_header.header.length = 0;
+    }
+
+  return length;
+}
+
+static void
+set_pce_address (struct in_addr ipv4, struct ospf_pce_info *pce)
+{
+
+  /* Enable PCE Info */
+  pce->pce_header.header.type = htons (RI_TLV_PCE);
+  /* Set PCE Address */
+  pce->pce_address.header.type = htons (RI_PCE_SUBTLV_ADDRESS);
+  pce->pce_address.header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+  pce->pce_address.address.type = htons (PCE_ADDRESS_TYPE_IPV4);
+  pce->pce_address.address.value = ipv4;
+
+  return;
+}
+
+static void
+set_pce_path_scope (u_int32_t scope, struct ospf_pce_info *pce)
+{
+
+  /* Enable PCE Info */
+  pce->pce_header.header.type = htons (RI_TLV_PCE);
+  /* Set PCE Scope */
+  pce->pce_scope.header.type = htons (RI_PCE_SUBTLV_PATH_SCOPE);
+  pce->pce_scope.header.length = htons (RI_TLV_LENGTH);
+  pce->pce_scope.value = htonl (scope);
+
+  return;
+}
+
+static void
+set_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+
+  struct ri_pce_subtlv_domain *new;
+
+  /* Enable PCE Info */
+  pce->pce_header.header.type = htons (RI_TLV_PCE);
+
+  /* Create new domain info */
+  new =
+    XCALLOC (MTYPE_OSPF_PCE_PARAMS,
+             sizeof (struct ri_pce_subtlv_domain));
+
+  new->header.type = htons (RI_PCE_SUBTLV_DOMAIN);
+  new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+  new->type = htons (type);
+  new->value = htonl (domain);
+
+  /* Add new domain to the list */
+  listnode_add (pce->pce_domain, new);
+
+  return;
+}
+
+static void
+unset_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *old = NULL;
+  int found = 0;
+
+  /* Search the corresponding node */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, old))
+    {
+      if ((old->type == htons (type)) && (old->value == htonl (domain)))
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  /* if found remove it */
+  if (found)
+    {
+      listnode_delete (pce->pce_domain, old);
+
+      /* Avoid misjudgement in the next lookup. */
+      if (listcount (pce->pce_domain) == 0)
+        pce->pce_domain->head = pce->pce_domain->tail = NULL;
+
+      /* Finally free the old domain */
+      XFREE (MTYPE_OSPF_PCE_PARAMS, old);
+    }
+}
+
+static void
+set_pce_neighbor (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+
+  struct ri_pce_subtlv_neighbor *new;
+
+  /* Enable PCE Info */
+  pce->pce_header.header.type = htons (RI_TLV_PCE);
+
+  /* Create new neighbor info */
+  new =
+    XCALLOC (MTYPE_OSPF_PCE_PARAMS,
+             sizeof (struct ri_pce_subtlv_neighbor));
+
+  new->header.type = htons (RI_PCE_SUBTLV_NEIGHBOR);
+  new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+  new->type = htons (type);
+  new->value = htonl (domain);
+
+  /* Add new domain to the list */
+  listnode_add (pce->pce_neighbor, new);
+
+  return;
+}
+
+static void
+unset_pce_neighbor (u_int16_t type, u_int32_t domain,
+                    struct ospf_pce_info *pce)
+{
+  struct listnode *node;
+  struct ri_pce_subtlv_neighbor *old = NULL;
+  int found = 0;
+
+  /* Search the corresponding node */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, old))
+    {
+      if ((old->type == htons (type)) && (old->value == htonl (domain)))
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  /* if found remove it */
+  if (found)
+    {
+      listnode_delete (pce->pce_neighbor, old);
+
+      /* Avoid misjudgement in the next lookup. */
+      if (listcount (pce->pce_neighbor) == 0)
+        pce->pce_neighbor->head = pce->pce_neighbor->tail = NULL;
+
+      /* Finally free the old domain */
+      XFREE (MTYPE_OSPF_PCE_PARAMS, old);
+    }
+}
+
+static void
+set_pce_cap_flag (u_int32_t cap, struct ospf_pce_info *pce)
+{
+
+  /* Enable PCE Info */
+  pce->pce_header.header.type = htons (RI_TLV_PCE);
+  /* Set PCE Capabilities flag */
+  pce->pce_cap_flag.header.type = htons (RI_PCE_SUBTLV_CAP_FLAG);
+  pce->pce_cap_flag.header.length = htons (RI_TLV_LENGTH);
+  pce->pce_cap_flag.value = htonl (cap);
+
+  return;
+}
+
+
+static void
+unset_param (struct ri_tlv_header *tlv)
+{
+
+  tlv->type = 0;
+  /* Fill the Value to 0 */
+  memset ((tlv + RI_TLV_HDR_SIZE), 0, RI_TLV_BODY_SIZE (tlv));
+  tlv->length = 0;
+
+  return;
+}
+
+static void
+initialize_params (struct ospf_router_info *ori)
+{
+  u_int32_t cap;
+  struct ospf *top;
+
+  /*
+   * Initialize default Router Information Capabilities.
+   */
+  cap = 0;
+  cap = cap | RI_TE_SUPPORT;
+
+  set_router_info_capabilities (&ori->router_cap, cap);
+
+  /* If Area address is not null and exist, retrieve corresponding structure */
+  top = ospf_lookup ();
+  zlog_info ("RI-> Initialize Router Info for %s scope within area %s",
+             OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS",
+             inet_ntoa (OspfRI.area_id));
+
+  /* Try to get the Area context at this step. Do it latter if not available */
+  if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL))
+    OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id);
+
+  /*
+   * Initialize default PCE Information values
+   */
+  /* PCE address == OSPF Router ID */
+  set_pce_address (top->router_id, &ori->pce_info);
+
+  /* PCE scope */
+  cap = 7;                      /* Set L, R and Rd bits to one = intra & inter-area path computation */
+  set_pce_path_scope (cap, &ori->pce_info);
+
+  /* PCE Capabilities */
+  cap =
+    PCE_CAP_BIDIRECTIONAL | PCE_CAP_DIVERSE_PATH | PCE_CAP_OBJECTIVES |
+    PCE_CAP_ADDITIVE | PCE_CAP_MULTIPLE_REQ;
+  set_pce_cap_flag (cap, &ori->pce_info);
+
+  /* Finally compute PCE header */
+  set_pce_header (&ori->pce_info);
+
+  return;
+}
+
+static int
+is_mandated_params_set (struct ospf_router_info ori)
+{
+  int rc = 0;
+
+  if (ntohs (ori.router_cap.header.type) == 0)
+    goto out;
+
+  if ((ntohs (ori.pce_info.pce_header.header.type) == RI_TLV_PCE)
+      && (ntohs (ori.pce_info.pce_address.header.type) == 0)
+      && (ntohs (ori.pce_info.pce_cap_flag.header.type) == 0))
+    goto out;
+
+  rc = 1;
+
+out:
+  return rc;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are callback functions against generic Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+static void
+ospf_router_info_ism_change (struct ospf_interface *oi, int old_state)
+{
+  /* So far, nothing to do here. */
+  return;
+
+}
+
+static void
+ospf_router_info_nsm_change (struct ospf_neighbor *nbr, int old_state)
+{
+
+  /* So far, nothing to do here. */
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are OSPF protocol processing functions for ROUTER INFORMATION
+ *------------------------------------------------------------------------*/
+
+static void
+build_tlv_header (struct stream *s, struct ri_tlv_header *tlvh)
+{
+
+  stream_put (s, tlvh, sizeof (struct ri_tlv_header));
+  return;
+}
+
+static void
+build_tlv (struct stream *s, struct ri_tlv_header *tlvh)
+{
+
+  if (ntohs (tlvh->type) != 0)
+    {
+      build_tlv_header (s, tlvh);
+      stream_put (s, tlvh + 1, RI_TLV_BODY_SIZE (tlvh));
+    }
+  return;
+}
+
+static void
+ospf_router_info_lsa_body_set (struct stream *s)
+{
+
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *domain;
+  struct ri_pce_subtlv_neighbor *neighbor;
+
+  /* Build Router Information TLV */
+  build_tlv (s, &OspfRI.router_cap.header);
+
+  /* Add RI PCE TLV if it is set */
+  /* Compute PCE Info header first */
+  if ((set_pce_header (&OspfRI.pce_info)) != 0)
+    {
+
+      /* Build PCE TLV */
+      build_tlv_header (s, &OspfRI.pce_info.pce_header.header);
+
+      /* Build PCE address sub-tlv */
+      build_tlv (s, &OspfRI.pce_info.pce_address.header);
+
+      /* Build PCE path scope sub-tlv */
+      build_tlv (s, &OspfRI.pce_info.pce_scope.header);
+
+      /* Build PCE domain sub-tlv */
+      for (ALL_LIST_ELEMENTS_RO (OspfRI.pce_info.pce_domain, node, domain))
+        build_tlv (s, &domain->header);
+
+      /* Build PCE neighbor sub-tlv */
+      for (ALL_LIST_ELEMENTS_RO
+           (OspfRI.pce_info.pce_neighbor, node, neighbor))
+        build_tlv (s, &neighbor->header);
+
+      /* Build PCE cap flag sub-tlv */
+      build_tlv (s, &OspfRI.pce_info.pce_cap_flag.header);
+    }
+
+  return;
+}
+
+/* Create new opaque-LSA. */
+static struct ospf_lsa *
+ospf_router_info_lsa_new ()
+{
+  struct ospf *top;
+  struct stream *s;
+  struct lsa_header *lsah;
+  struct ospf_lsa *new = NULL;
+  u_char options, lsa_type;
+  struct in_addr lsa_id;
+  u_int32_t tmp;
+  u_int16_t length;
+
+  /* Create a stream for LSA. */
+  if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL)
+    {
+      zlog_warn ("ospf_router_info_lsa_new: stream_new() ?");
+      goto out;
+    }
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  options = OSPF_OPTION_E;      /* Enable AS external as we flood RI with Opaque Type 11 */
+  options |= OSPF_OPTION_O;     /* Don't forget this :-) */
+
+  lsa_type = OspfRI.scope;
+  /* LSA ID == 0 for Router Information see RFC 4970 */
+  tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0);
+  lsa_id.s_addr = htonl (tmp);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug
+      ("LSA[Type%d:%s]: Create an Opaque-LSA/ROUTER INFORMATION instance",
+       lsa_type, inet_ntoa (lsa_id));
+
+  top = ospf_lookup ();
+
+  /* Set opaque-LSA header fields. */
+  lsa_header_set (s, options, lsa_type, lsa_id, top->router_id);
+
+  /* Set opaque-LSA body fields. */
+  ospf_router_info_lsa_body_set (s);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Now, create an OSPF LSA instance. */
+  if ((new = ospf_lsa_new ()) == NULL)
+    {
+      zlog_warn ("ospf_router_info_lsa_new: ospf_lsa_new() ?");
+      stream_free (s);
+      goto out;
+    }
+  if ((new->data = ospf_lsa_data_new (length)) == NULL)
+    {
+      zlog_warn ("ospf_router_info_lsa_new: ospf_lsa_data_new() ?");
+      ospf_lsa_unlock (&new);
+      new = NULL;
+      stream_free (s);
+      goto out;
+    }
+
+  new->area = OspfRI.area;      /* Area must be null if the Opaque type is AS scope, fulfill otherwise */
+
+  SET_FLAG (new->flags, OSPF_LSA_SELF);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+out:return new;
+}
+
+static int
+ospf_router_info_lsa_originate1 (void *arg)
+{
+  struct ospf_lsa *new;
+  struct ospf *top;
+  struct ospf_area *area;
+  int rc = -1;
+
+  /* First check if the area is known if flooding scope is Area */
+  if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA)
+    {
+      area = (struct ospf_area *) arg;
+      if (area->area_id.s_addr != OspfRI.area_id.s_addr)
+        {
+          zlog_debug
+            ("RI -> This is not the Router Information Area. Stop processing");
+          goto out;
+        }
+      OspfRI.area = area;
+    }
+
+  /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
+  if ((new = ospf_router_info_lsa_new ()) == NULL)
+    {
+      zlog_warn
+        ("ospf_router_info_lsa_originate1: ospf_router_info_lsa_new() ?");
+      goto out;
+    }
+
+  /* Get ospf info */
+  top = ospf_lookup ();
+
+  /* Install this LSA into LSDB. */
+  if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+    {
+      zlog_warn ("ospf_router_info_lsa_originate1: ospf_lsa_install() ?");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Now this Router Info parameter entry has associated LSA. */
+  SET_FLAG (OspfRI.flags, RIFLG_LSA_ENGAGED);
+
+  /* Update new LSA origination count. */
+  top->lsa_originate_count++;
+
+  /* Flood new LSA through AS. */
+  if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+    ospf_flood_through_as (top, NULL /*nbr */ , new);
+  else
+    ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/ROUTER INFORMATION",
+                  new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  rc = 0;
+out:return rc;
+}
+
+static int
+ospf_router_info_lsa_originate (void *arg)
+{
+
+  int rc = -1;
+
+  if (OspfRI.status == disabled)
+    {
+      zlog_info
+        ("ospf_router_info_lsa_originate: ROUTER INFORMATION is disabled now.");
+      rc = 0;                   /* This is not an error case. */
+      goto out;
+    }
+
+  /* Check if Router Information LSA is already engaged */
+  if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+    {
+      if (OspfRI.flags & RIFLG_LSA_FORCED_REFRESH)
+        {
+          OspfRI.flags &= ~RIFLG_LSA_FORCED_REFRESH;
+          ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+        }
+    }
+  else
+    {
+      if (!is_mandated_params_set (OspfRI))
+        zlog_warn
+          ("ospf_router_info_lsa_originate: lacks mandated ROUTER INFORMATION parameters");
+
+      /* Ok, let's try to originate an LSA */
+      if (ospf_router_info_lsa_originate1 (arg) != 0)
+        goto out;
+    }
+
+  rc = 0;
+out:return rc;
+}
+
+static struct ospf_lsa *
+ospf_router_info_lsa_refresh (struct ospf_lsa *lsa)
+{
+  struct ospf_lsa *new = NULL;
+  struct ospf *top;
+
+  if (OspfRI.status == disabled)
+    {
+      /*
+       * This LSA must have flushed before due to ROUTER INFORMATION status change.
+       * It seems a slip among routers in the routing domain.
+       */
+      zlog_info
+        ("ospf_router_info_lsa_refresh: ROUTER INFORMATION is disabled now.");
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE);      /* Flush it anyway. */
+    }
+
+  /* Verify that the Router Information ID is supported */
+  if (GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)) != 0)
+    {
+      zlog_warn
+        ("ospf_router_info_lsa_refresh: Unsupported Router Information ID");
+      goto out;
+    }
+
+  /* If the lsa's age reached to MaxAge, start flushing procedure. */
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      OspfRI.flags &= ~RIFLG_LSA_ENGAGED;
+      ospf_opaque_lsa_flush_schedule (lsa);
+      goto out;
+    }
+
+  /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
+  if ((new = ospf_router_info_lsa_new ()) == NULL)
+    {
+      zlog_warn
+        ("ospf_router_info_lsa_refresh: ospf_router_info_lsa_new() ?");
+      goto out;
+    }
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  /* Install this LSA into LSDB. */
+  /* Given "lsa" will be freed in the next function. */
+  top = ospf_lookup ();
+  if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+    {
+      zlog_warn ("ospf_router_info_lsa_refresh: ospf_lsa_install() ?");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Flood updated LSA through AS or AREA depending of OspfRI.scope. */
+  if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+    ospf_flood_through_as (top, NULL /*nbr */ , new);
+  else
+    ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Refresh Opaque-LSA/ROUTER INFORMATION",
+                  new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+out:return new;
+}
+
+static void
+ospf_router_info_lsa_schedule (opcode_t opcode)
+{
+  struct ospf_lsa lsa;
+  struct lsa_header lsah;
+  struct ospf *top;
+  u_int32_t tmp;
+
+  memset (&lsa, 0, sizeof (lsa));
+  memset (&lsah, 0, sizeof (lsah));
+
+  zlog_debug ("RI-> LSA schedule %s%s%s",
+              opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+              opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+              opcode == FLUSH_THIS_LSA ? "Flush" : "");
+
+  top = ospf_lookup ();
+  if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL))
+    {
+      zlog_warn
+        ("ospf_router_info_lsa_schedule(): Router Info is Area scope flooding but area is not set");
+      OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id);
+    }
+  lsa.area = OspfRI.area;
+  lsa.data = &lsah;
+  lsah.type = OspfRI.scope;
+
+  /* LSA ID is set to 0 for the Router Information. See RFC 4970 */
+  tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0);
+  lsah.id.s_addr = htonl (tmp);
+
+  switch (opcode)
+    {
+    case REORIGINATE_THIS_LSA:
+      if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA)
+        ospf_opaque_lsa_reoriginate_schedule ((void *) OspfRI.area,
+                                              OSPF_OPAQUE_AREA_LSA,
+                                              OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+      else
+        ospf_opaque_lsa_reoriginate_schedule ((void *) top,
+                                              OSPF_OPAQUE_AS_LSA,
+                                              OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+      break;
+    case REFRESH_THIS_LSA:
+      ospf_opaque_lsa_refresh_schedule (&lsa);
+      break;
+    case FLUSH_THIS_LSA:
+      OspfRI.flags &= ~RIFLG_LSA_ENGAGED;
+      ospf_opaque_lsa_flush_schedule (&lsa);
+      break;
+    default:
+      zlog_warn ("ospf_router_info_lsa_schedule: Unknown opcode (%u)",
+                 opcode);
+      break;
+    }
+
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty session control functions.
+ *------------------------------------------------------------------------*/
+
+static u_int16_t
+show_vty_router_cap (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  struct ri_tlv_router_cap *top = (struct ri_tlv_router_cap *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  Router Capabilities: 0x%x%s", ntohl (top->value),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Router Capabilities: 0x%x", ntohl (top->value));
+
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_address (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  struct ri_pce_subtlv_address *top = (struct ri_pce_subtlv_address *) tlvh;
+
+  if (ntohs (top->address.type) == PCE_ADDRESS_TYPE_IPV4)
+    {
+      if (vty != NULL)
+        vty_out (vty, "  PCE Address: %s%s", inet_ntoa (top->address.value),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE Address: %s", inet_ntoa (top->address.value));
+    }
+  else
+    {
+      /* TODO: Add support to IPv6 with inet_ntop() */
+      if (vty != NULL)
+        vty_out (vty, "  PCE Address: 0x%x%s",
+                 ntohl (top->address.value.s_addr), VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE Address: 0x%x",
+                    ntohl (top->address.value.s_addr));
+    }
+
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_path_scope (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  struct ri_pce_subtlv_path_scope *top =
+    (struct ri_pce_subtlv_path_scope *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  PCE Path Scope: 0x%x%s", ntohl (top->value),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    PCE Path Scope: 0x%x", ntohl (top->value));
+
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_domain (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  struct ri_pce_subtlv_domain *top = (struct ri_pce_subtlv_domain *) tlvh;
+  struct in_addr tmp;
+
+  if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA)
+    {
+      tmp.s_addr = top->value;
+      if (vty != NULL)
+        vty_out (vty, "  PCE domain Area: %s%s", inet_ntoa (tmp),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE domain Area: %s", inet_ntoa (tmp));
+    }
+  else
+    {
+      if (vty != NULL)
+        vty_out (vty, "  PCE domain AS: %d%s", ntohl (top->value),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE domain AS: %d", ntohl (top->value));
+    }
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_neighbor (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+
+  struct ri_pce_subtlv_neighbor *top = (struct ri_pce_subtlv_neighbor *) tlvh;
+  struct in_addr tmp;
+
+  if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA)
+    {
+      tmp.s_addr = top->value;
+      if (vty != NULL)
+        vty_out (vty, "  PCE neighbor Area: %s%s", inet_ntoa (tmp),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE neighbor Area: %s", inet_ntoa (tmp));
+    }
+  else
+    {
+      if (vty != NULL)
+        vty_out (vty, "  PCE neighbor AS: %d%s", ntohl (top->value),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("    PCE neighbor AS: %d", ntohl (top->value));
+    }
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_cap_flag (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  struct ri_pce_subtlv_cap_flag *top = (struct ri_pce_subtlv_cap_flag *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  PCE Capabilities Flag: 0x%x%s", ntohl (top->value),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    PCE Capabilities Flag: 0x%x", ntohl (top->value));
+
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_unknown_tlv (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+  if (vty != NULL)
+    vty_out (vty, "  Unknown TLV: [type(0x%x), length(0x%x)]%s",
+             ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE);
+  else
+    zlog_debug ("    Unknown TLV: [type(0x%x), length(0x%x)]",
+                ntohs (tlvh->type), ntohs (tlvh->length));
+
+  return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_info (struct vty *vty, struct ri_tlv_header *ri, uint32_t total)
+{
+  struct ri_tlv_header *tlvh;
+  u_int16_t sum = 0;
+
+  for (tlvh = ri; sum < total; tlvh = RI_TLV_HDR_NEXT (tlvh))
+    {
+      switch (ntohs (tlvh->type))
+        {
+        case RI_PCE_SUBTLV_ADDRESS:
+          sum += show_vty_pce_subtlv_address (vty, tlvh);
+          break;
+        case RI_PCE_SUBTLV_PATH_SCOPE:
+          sum += show_vty_pce_subtlv_path_scope (vty, tlvh);
+          break;
+        case RI_PCE_SUBTLV_DOMAIN:
+          sum += show_vty_pce_subtlv_domain (vty, tlvh);
+          break;
+        case RI_PCE_SUBTLV_NEIGHBOR:
+          sum += show_vty_pce_subtlv_neighbor (vty, tlvh);
+          break;
+        case RI_PCE_SUBTLV_CAP_FLAG:
+          sum += show_vty_pce_subtlv_cap_flag (vty, tlvh);
+          break;
+        default:
+          sum += show_vty_unknown_tlv (vty, tlvh);
+          break;
+        }
+    }
+  return sum;
+}
+
+static void
+ospf_router_info_show_info (struct vty *vty, struct ospf_lsa *lsa)
+{
+  struct lsa_header *lsah = (struct lsa_header *) lsa->data;
+  struct ri_tlv_header *tlvh;
+  u_int16_t length = 0, sum = 0;
+
+  /* Initialize TLV browsing */
+  length = ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+  for (tlvh = RI_TLV_HDR_TOP (lsah); sum < length;
+       tlvh = RI_TLV_HDR_NEXT (tlvh))
+    {
+      switch (ntohs (tlvh->type))
+        {
+        case RI_TLV_CAPABILITIES:
+          sum += show_vty_router_cap (vty, tlvh);
+          break;
+        case RI_TLV_PCE:
+          tlvh++;
+          sum += RI_TLV_HDR_SIZE;
+          sum += show_vty_pce_info (vty, tlvh, length - sum);
+          break;
+        default:
+          sum += show_vty_unknown_tlv (vty, tlvh);
+          break;
+        }
+    }
+
+  return;
+}
+
+static void
+ospf_router_info_config_write_router (struct vty *vty)
+{
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *domain;
+  struct ri_pce_subtlv_neighbor *neighbor;
+  struct in_addr tmp;
+
+  if (OspfRI.status == enabled)
+    {
+      if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+        vty_out (vty, " router-info as%s", VTY_NEWLINE);
+      else
+        vty_out (vty, " router-info area %s%s", inet_ntoa (OspfRI.area_id),
+                 VTY_NEWLINE);
+
+      if (pce->pce_address.header.type != 0)
+        vty_out (vty, "  pce address %s%s",
+                 inet_ntoa (pce->pce_address.address.value), VTY_NEWLINE);
+
+      if (pce->pce_cap_flag.header.type != 0)
+        vty_out (vty, "  pce flag 0x%x%s", ntohl (pce->pce_cap_flag.value),
+                 VTY_NEWLINE);
+
+      for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+        {
+          if (domain->header.type != 0)
+            {
+              if (domain->type == PCE_DOMAIN_TYPE_AREA)
+                {
+                  tmp.s_addr = domain->value;
+                  vty_out (vty, "  pce domain area %s%s", inet_ntoa (tmp),
+                           VTY_NEWLINE);
+                }
+              else
+                {
+                  vty_out (vty, "  pce domain as %d%s", ntohl (domain->value),
+                           VTY_NEWLINE);
+                }
+            }
+        }
+
+      for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+        {
+          if (neighbor->header.type != 0)
+            {
+              if (neighbor->type == PCE_DOMAIN_TYPE_AREA)
+                {
+                  tmp.s_addr = neighbor->value;
+                  vty_out (vty, "  pce neighbor area %s%s", inet_ntoa (tmp),
+                           VTY_NEWLINE);
+                }
+              else
+                {
+                  vty_out (vty, "  pce neighbor as %d%s",
+                           ntohl (neighbor->value), VTY_NEWLINE);
+                }
+            }
+        }
+
+      if (pce->pce_scope.header.type != 0)
+        vty_out (vty, "  pce scope 0x%x%s",
+                 ntohl (OspfRI.pce_info.pce_scope.value), VTY_NEWLINE);
+    }
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty command functions.
+ *------------------------------------------------------------------------*/
+
+DEFUN (router_info,
+       router_info_area_cmd,
+       "router-info area A.B.C.D",
+       OSPF_RI_STR
+       "Enable the Router Information functionality with Area flooding scope\n"
+       "OSPF area ID in IP format")
+{
+
+  u_int8_t scope;
+
+  if (OspfRI.status == enabled)
+    return CMD_SUCCESS;
+
+  /* Check and get Area value if present */
+  if (argc == 1)
+    {
+      if (!inet_aton (argv[0], &OspfRI.area_id))
+        {
+          vty_out (vty, "Please specify Router Info Area by A.B.C.D%s",
+                   VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      scope = OSPF_OPAQUE_AREA_LSA;
+    }
+  else
+    {
+      OspfRI.area_id.s_addr = 0;
+      scope = OSPF_OPAQUE_AS_LSA;
+    }
+
+  /* First start to register Router Information callbacks */
+  if ((ospf_router_info_register (scope)) != 0)
+    {
+      zlog_warn ("Enable to register Router Information callbacks. Abort!");
+      return CMD_WARNING;
+    }
+
+  OspfRI.status = enabled;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("RI-> Router Information (%s flooding): OFF -> ON",
+                 OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS");
+
+  /*
+   * Following code is intended to handle two cases;
+   *
+   * 1) Router Information was disabled at startup time, but now become enabled.
+   * 2) Router Information was once enabled then disabled, and now enabled again.
+   */
+
+  initialize_params (&OspfRI);
+
+  /* Refresh RI LSA if already engaged */
+  if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+    {
+      zlog_debug ("RI-> Initial origination following configuration");
+      ospf_router_info_lsa_schedule (REORIGINATE_THIS_LSA);
+    }
+  return CMD_SUCCESS;
+
+}
+
+ALIAS (router_info,
+       router_info_as_cmd,
+       "router-info as",
+       OSPF_RI_STR
+       "Enable the Router Information functionality with AS flooding scope\n")
+
+DEFUN (no_router_info,
+       no_router_info_cmd,
+       "no router-info",
+       NO_STR
+       "Disable the Router Information functionality\n")
+{
+
+  if (OspfRI.status == disabled)
+    return CMD_SUCCESS;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("RI-> Router Information: ON -> OFF");
+
+  if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+    ospf_router_info_lsa_schedule (FLUSH_THIS_LSA);
+
+  /* Unregister the callbacks */
+  ospf_router_info_unregister ();
+
+  OspfRI.status = disabled;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (pce_address,
+       pce_address_cmd,
+       "pce address A.B.C.D",
+       PCE_STR
+       "Stable IP address of the PCE\n"
+       "PCE address in IPv4 address format\n")
+{
+  struct in_addr value;
+  struct ospf_pce_info *pi = &OspfRI.pce_info;
+
+  if (!inet_aton (argv[0], &value))
+    {
+      vty_out (vty, "Please specify PCE Address by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ntohs (pi->pce_address.header.type) == 0
+      || ntohl (pi->pce_address.address.value.s_addr) != ntohl (value.s_addr))
+    {
+
+      set_pce_address (value, pi);
+
+      /* Refresh RI LSA if already engaged */
+      if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+        ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_address,
+       no_pce_address_cmd,
+       "no pce address",
+       NO_STR
+       PCE_STR
+       "Disable PCE address\n")
+{
+
+  unset_param (&OspfRI.pce_info.pce_address.header);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (pce_path_scope,
+       pce_path_scope_cmd,
+       "pce scope BITPATTERN",
+       PCE_STR
+       "Path scope visibilities of the PCE for path computation\n"
+       "32-bit Hexadecimal value\n")
+{
+  uint32_t scope;
+  struct ospf_pce_info *pi = &OspfRI.pce_info;
+
+  if (sscanf (argv[0], "0x%x", &scope) != 1)
+    {
+      vty_out (vty, "pce_path_scope: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ntohl (pi->pce_scope.header.type) == 0 || scope != pi->pce_scope.value)
+    {
+      set_pce_path_scope (scope, pi);
+
+      /* Refresh RI LSA if already engaged */
+      if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+        ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_path_scope,
+       no_pce_path_scope_cmd,
+       "no pce scope",
+       NO_STR
+       PCE_STR
+       "Disable PCE path scope\n")
+{
+
+  unset_param (&OspfRI.pce_info.pce_address.header);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (pce_domain,
+       pce_domain_cmd,
+       "pce domain as <0-65535>",
+       PCE_STR
+       "Configure PCE domain AS number\n"
+       "AS number where the PCE as visibilities for path computation\n"
+       "AS number in decimal <0-65535>\n")
+{
+
+  uint32_t as;
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *domain;
+
+  if (sscanf (argv[0], "%d", &as) != 1)
+    {
+      vty_out (vty, "pce_domain: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check if the domain is not already in the domain list */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+    {
+      if (ntohl (domain->header.type) == 0 && as == domain->value)
+        goto out;
+    }
+
+  /* Create new domain if not found */
+  set_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+out:return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_domain,
+       no_pce_domain_cmd,
+       "no pce domain as <0-65535>",
+       NO_STR
+       PCE_STR
+       "Disable PCE domain AS number\n"
+       "AS number where the PCE as visibilities for path computation\n"
+       "AS number in decimal <0-65535>\n")
+{
+
+  uint32_t as;
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+  if (sscanf (argv[0], "%d", &as) != 1)
+    {
+      vty_out (vty, "no_pce_domain: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Unset corresponding PCE domain */
+  unset_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (pce_neigbhor,
+       pce_neighbor_cmd,
+       "pce neighbor as <0-65535>",
+       PCE_STR
+       "Configure PCE neighbor domain AS number\n"
+       "AS number of PCE neighbors\n"
+       "AS number in decimal <0-65535>\n")
+{
+
+  uint32_t as;
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+  struct listnode *node;
+  struct ri_pce_subtlv_neighbor *neighbor;
+
+  if (sscanf (argv[0], "%d", &as) != 1)
+    {
+      vty_out (vty, "pce_neighbor: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check if the domain is not already in the domain list */
+  for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+    {
+      if (ntohl (neighbor->header.type) == 0 && as == neighbor->value)
+        goto out;
+    }
+
+  /* Create new domain if not found */
+  set_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+out:return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_neighbor,
+       no_pce_neighbor_cmd,
+       "no pce neighbor as <0-65535>",
+       NO_STR
+       PCE_STR
+       "Disable PCE neighbor AS number\n"
+       "AS number of PCE neighbor\n"
+       "AS number in decimal <0-65535>\n")
+{
+
+  uint32_t as;
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+  if (sscanf (argv[0], "%d", &as) != 1)
+    {
+      vty_out (vty, "no_pce_neighbor: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Unset corresponding PCE domain */
+  unset_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (pce_cap_flag,
+       pce_cap_flag_cmd,
+       "pce flag BITPATTERN",
+       PCE_STR
+       "Capabilities of the PCE for path computation\n"
+       "32-bit Hexadecimal value\n")
+{
+
+  uint32_t cap;
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+  if (sscanf (argv[0], "0x%x", &cap) != 1)
+    {
+      vty_out (vty, "pce_cap_flag: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ntohl (pce->pce_cap_flag.header.type) == 0
+      || cap != pce->pce_cap_flag.value)
+    {
+      set_pce_cap_flag (cap, pce);
+
+      /* Refresh RI LSA if already engaged */
+      if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+        ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_cap_flag,
+       no_pce_cap_flag_cmd,
+       "no pce flag",
+       NO_STR
+       PCE_STR
+       "Disable PCE capabilities\n")
+{
+
+  unset_param (&OspfRI.pce_info.pce_cap_flag.header);
+
+  /* Refresh RI LSA if already engaged */
+  if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+    ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_router_info,
+       show_ip_ospf_router_info_cmd,
+       "show ip ospf router-info",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "Router Information\n")
+{
+
+  if (OspfRI.status == enabled)
+    {
+      vty_out (vty, "--- Router Information parameters ---%s", VTY_NEWLINE);
+      show_vty_router_cap (vty, &OspfRI.router_cap.header);
+    }
+  else
+    {
+      if (vty != NULL)
+        vty_out (vty, "  Router Information is disabled on this router%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_opsf_router_info_pce,
+       show_ip_ospf_router_info_pce_cmd,
+       "show ip ospf router-info pce",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "Router Information\n"
+       "PCE information\n")
+{
+
+  struct ospf_pce_info *pce = &OspfRI.pce_info;
+  struct listnode *node;
+  struct ri_pce_subtlv_domain *domain;
+  struct ri_pce_subtlv_neighbor *neighbor;
+
+  if (OspfRI.status == enabled)
+    {
+      vty_out (vty, "--- PCE parameters ---%s", VTY_NEWLINE);
+
+      if (pce->pce_address.header.type != 0)
+        show_vty_pce_subtlv_address (vty, &pce->pce_address.header);
+
+      if (pce->pce_scope.header.type != 0)
+        show_vty_pce_subtlv_path_scope (vty, &pce->pce_scope.header);
+
+      for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+        {
+          if (domain->header.type != 0)
+            show_vty_pce_subtlv_domain (vty, &domain->header);
+        }
+
+      for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+        {
+          if (neighbor->header.type != 0)
+            show_vty_pce_subtlv_neighbor (vty, &neighbor->header);
+        }
+
+      if (pce->pce_cap_flag.header.type != 0)
+        show_vty_pce_subtlv_cap_flag (vty, &pce->pce_cap_flag.header);
+
+    }
+  else
+    {
+      vty_out (vty, "  Router Information is disabled on this router%s",
+               VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Install new CLI commands */
+static void
+ospf_router_info_register_vty (void)
+{
+  install_element (VIEW_NODE, &show_ip_ospf_router_info_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_router_info_pce_cmd);
+
+  install_element (OSPF_NODE, &router_info_area_cmd);
+  install_element (OSPF_NODE, &router_info_as_cmd);
+  install_element (OSPF_NODE, &no_router_info_cmd);
+  install_element (OSPF_NODE, &pce_address_cmd);
+  install_element (OSPF_NODE, &pce_path_scope_cmd);
+  install_element (OSPF_NODE, &pce_domain_cmd);
+  install_element (OSPF_NODE, &no_pce_domain_cmd);
+  install_element (OSPF_NODE, &pce_neighbor_cmd);
+  install_element (OSPF_NODE, &no_pce_neighbor_cmd);
+  install_element (OSPF_NODE, &pce_cap_flag_cmd);
+
+  return;
+}
diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h
new file mode 100644 (file)
index 0000000..c507434
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * This is an implementation of RFC4970 Router Information
+ * with support of RFC5088 PCE Capabilites announcement
+ *
+ * Module name: Router Information
+ * Version:     0.99.22
+ * Created:     2012-02-01 by Olivier Dugeon
+ * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ROUTER_INFO_H
+#define _ZEBRA_OSPF_ROUTER_INFO_H
+
+/*
+ * Opaque LSA's link state ID for Router Information is
+ * structured as follows.
+ *
+ *        24       16        8        0
+ * +--------+--------+--------+--------+
+ * |    1   |  MBZ   |........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<Resv'd>|<-- Instance --->|
+ *
+ *
+ * Type:      IANA has assigned '4' for Router Information.
+ * MBZ:       Reserved, must be set to zero.
+ * Instance:  User may select an arbitrary 16-bit value.
+ *
+ */
+
+/*
+ *        24       16        8        0
+ * +--------+--------+--------+--------+ ---
+ * |   LS age        |Options | 9,10,11|  A
+ * +--------+--------+--------+--------+  |
+ * |    4   |   0    |    Instance     |  |
+ * +--------+--------+--------+--------+  |
+ * |        Advertising router         |  |  Standard (Opaque) LSA header;
+ * +--------+--------+--------+--------+  |  Type 9,10 or 11 are used.
+ * |        LS sequence number         |  |
+ * +--------+--------+--------+--------+  |
+ * |   LS checksum   |     Length      |  V
+ * +--------+--------+--------+--------+ ---
+ * |      Type       |     Length      |  A
+ * +--------+--------+--------+--------+  |  TLV part for Router Information; Values might be
+ * |              Values ...           |  V  structured as a set of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used for Router Information.
+ */
+struct ri_tlv_header
+{
+  u_int16_t type;               /* RI_TLV_XXX (see below) */
+  u_int16_t length;             /* Value portion only, in byte */
+};
+
+#define RI_TLV_HDR_SIZE (sizeof (struct ri_tlv_header))
+#define RI_TLV_BODY_SIZE(tlvh) (ROUNDUP (ntohs ((tlvh)->length), sizeof (u_int32_t)))
+#define RI_TLV_SIZE(tlvh) (RI_TLV_HDR_SIZE + RI_TLV_BODY_SIZE(tlvh))
+#define RI_TLV_HDR_TOP(lsah) (struct ri_tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE)
+#define RI_TLV_HDR_NEXT(tlvh) (struct ri_tlv_header *)((char *)(tlvh) + RI_TLV_SIZE(tlvh))
+
+/*
+ * Following section defines TLV body parts.
+ */
+
+/* Up to now, 8 code point have been assigned to Router Information */
+/* Only type 1 Router Capabilities and 6 PCE are supported with this code */
+#define RI_IANA_MAX_TYPE               8
+
+/* RFC4970: Router Information Capabilities TLV */ /* Mandatory */
+#define RI_TLV_CAPABILITIES            1
+
+struct ri_tlv_router_cap
+{
+  struct ri_tlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t value;
+};
+
+#define RI_GRACE_RESTART       0x01
+#define RI_GRACE_HELPER                0x02
+#define RI_STUB_SUPPORT                0x04
+#define RI_TE_SUPPORT          0x08
+#define RI_P2P_OVER_LAN                0x10
+#define RI_TE_EXPERIMENTAL     0x20
+
+#define RI_TLV_LENGTH          4
+
+/* RFC5088: PCE Capabilities TLV */ /* Optional */
+/* RI PCE TLV */
+#define RI_TLV_PCE                     6
+
+struct ri_tlv_pce
+{
+  struct ri_tlv_header header;
+/* A set of PCE-sub-TLVs will follow. */
+};
+
+/* PCE Address Sub-TLV */ /* Mandatory */
+#define        RI_PCE_SUBTLV_ADDRESS           1
+struct ri_pce_subtlv_address
+{
+  struct ri_tlv_header header;  /* Type = 1; Length is 8 (IPv4) or 20 (IPv6) bytes. */
+#define        PCE_ADDRESS_LENGTH_IPV4         8
+#define        PCE_ADDRESS_LENGTH_IPV6         20
+  struct
+  {
+    u_int16_t type;             /* Address type: 1 = IPv4, 2 = IPv6 */
+#define        PCE_ADDRESS_TYPE_IPV4           1
+#define        PCE_ADDRESS_TYPE_IPV6           2
+    u_int16_t reserved;
+    struct in_addr value;      /* PCE address */
+  } address;
+};
+
+/* PCE Path-Scope Sub-TLV */ /* Mandatory */
+#define        RI_PCE_SUBTLV_PATH_SCOPE        2
+struct ri_pce_subtlv_path_scope
+{
+  struct ri_tlv_header header; /* Type = 2; Length = 4 bytes. */
+  u_int32_t value;              /* L, R, Rd, S, Sd, Y, PrefL, PrefR, PrefS and PrefY bits see RFC5088 page 9 */
+};
+
+/* PCE Domain Sub-TLV */ /* Optional */
+#define        RI_PCE_SUBTLV_DOMAIN            3
+
+#define        PCE_DOMAIN_TYPE_AREA            1
+#define        PCE_DOMAIN_TYPE_AS                      2
+
+struct ri_pce_subtlv_domain
+{
+  struct ri_tlv_header header;  /* Type = 3; Length = 8 bytes. */
+  u_int16_t type;               /* Domain type: 1 = OSPF Area ID, 2 = AS Number */
+  u_int16_t reserved;
+  u_int32_t value;
+};
+
+/* PCE Neighbor Sub-TLV */ /* Mandatory if R or S bit is set */
+#define RI_PCE_SUBTLV_NEIGHBOR         4
+struct ri_pce_subtlv_neighbor
+{
+  struct ri_tlv_header header;  /* Type = 4; Length = 8 bytes. */
+  u_int16_t type;               /* Domain type: 1 = OSPF Area ID, 2 = AS Number */
+  u_int16_t reserved;
+  u_int32_t value;
+};
+
+/* PCE Capabilities Flags Sub-TLV */ /* Optional */
+#define RI_PCE_SUBTLV_CAP_FLAG         5
+
+#define PCE_CAP_GMPLS_LINK             0x0001
+#define PCE_CAP_BIDIRECTIONAL  0x0002
+#define PCE_CAP_DIVERSE_PATH   0x0004
+#define PCE_CAP_LOAD_BALANCE   0x0008
+#define PCE_CAP_SYNCHRONIZED   0x0010
+#define PCE_CAP_OBJECTIVES             0x0020
+#define PCE_CAP_ADDITIVE               0x0040
+#define PCE_CAP_PRIORIZATION   0x0080
+#define PCE_CAP_MULTIPLE_REQ   0x0100
+
+struct ri_pce_subtlv_cap_flag
+{
+  struct ri_tlv_header header;  /* Type = 5; Length = n x 4 bytes. */
+  u_int32_t value;
+};
+
+/* Prototypes. */
+extern int ospf_router_info_init (void);
+extern void ospf_router_info_term (void);
+
+#endif /* _ZEBRA_OSPF_ROUTER_INFO_H */
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
new file mode 100644 (file)
index 0000000..eb7829a
--- /dev/null
@@ -0,0 +1,1049 @@
+/*
+ * OSPF routing table.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "linklist.h"
+#include "log.h"
+#include "if.h"
+#include "command.h"
+#include "sockunion.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+
+struct ospf_route *
+ospf_route_new ()
+{
+  struct ospf_route *new;
+
+  new = XCALLOC (MTYPE_OSPF_ROUTE, sizeof (struct ospf_route));
+
+  new->ctime = quagga_time (NULL);
+  new->mtime = new->ctime;
+  new->paths = list_new ();
+  new->paths->del = (void (*) (void *))ospf_path_free;
+
+  return new;
+}
+
+void
+ospf_route_free (struct ospf_route *or)
+{
+  if (or->paths)
+      list_delete (or->paths);
+
+  XFREE (MTYPE_OSPF_ROUTE, or);
+}
+
+struct ospf_path *
+ospf_path_new ()
+{
+  struct ospf_path *new;
+
+  new = XCALLOC (MTYPE_OSPF_PATH, sizeof (struct ospf_path));
+
+  return new;
+}
+
+static struct ospf_path *
+ospf_path_dup (struct ospf_path *path)
+{
+  struct ospf_path *new;
+
+  new = ospf_path_new ();
+  memcpy (new, path, sizeof (struct ospf_path));
+
+  return new;
+}
+
+void
+ospf_path_free (struct ospf_path *op)
+{
+  XFREE (MTYPE_OSPF_PATH, op);
+}
+
+void
+ospf_route_delete (struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      {
+       if (or->type == OSPF_DESTINATION_NETWORK)
+         ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p,
+                                      or);
+       else if (or->type == OSPF_DESTINATION_DISCARD)
+         ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p);
+      }
+}
+
+void
+ospf_route_table_free (struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      {
+       ospf_route_free (or);
+
+       rn->info = NULL;
+       route_unlock_node (rn);
+      }
+
+   route_table_finish (rt);
+}
+
+/* If a prefix exists in the new routing table, then return 1,
+   otherwise return 0. Since the ZEBRA-RIB does an implicit
+   withdraw, it is not necessary to send a delete, an add later
+   will act like an implicit delete. */
+static int
+ospf_route_exist_new_table (struct route_table *rt, struct prefix_ipv4 *prefix)
+{
+  struct route_node *rn;
+
+  assert (rt);
+  assert (prefix);
+
+  rn = route_node_lookup (rt, (struct prefix *) prefix);
+  if (!rn) {
+    return 0;
+  }
+  route_unlock_node (rn);
+
+  if (!rn->info) {
+    return 0;
+  }
+
+  return 1;
+}
+
+/* If a prefix and a nexthop match any route in the routing table,
+   then return 1, otherwise return 0. */
+int
+ospf_route_match_same (struct route_table *rt, struct prefix_ipv4 *prefix,
+                      struct ospf_route *newor)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct ospf_path *op;
+  struct ospf_path *newop;
+  struct listnode *n1;
+  struct listnode *n2;
+
+  if (! rt || ! prefix)
+    return 0;
+
+   rn = route_node_lookup (rt, (struct prefix *) prefix);
+   if (! rn || ! rn->info)
+     return 0;
+   route_unlock_node (rn);
+
+   or = rn->info;
+   if (or->type == newor->type && or->cost == newor->cost)
+     {
+       if (or->type == OSPF_DESTINATION_NETWORK)
+        {
+          if (or->paths->count != newor->paths->count)
+            return 0;
+
+          /* Check each path. */
+          for (n1 = listhead (or->paths), n2 = listhead (newor->paths);
+               n1 && n2; n1 = listnextnode (n1), n2 = listnextnode (n2))
+            { 
+              op = listgetdata (n1);
+              newop = listgetdata (n2);
+
+              if (! IPV4_ADDR_SAME (&op->nexthop, &newop->nexthop))
+                return 0;
+              if (op->ifindex != newop->ifindex)
+                return 0;
+            }
+          return 1;
+        }
+       else if (prefix_same (&rn->p, (struct prefix *) prefix))
+        return 1;
+     }
+  return 0;
+}
+
+/* delete routes generated from AS-External routes if there is a inter/intra
+ * area route
+ */
+static void 
+ospf_route_delete_same_ext(struct route_table *external_routes,
+                     struct route_table *routes)
+{
+  struct route_node *rn,
+                    *ext_rn;
+  
+  if ( (external_routes == NULL) || (routes == NULL) )
+    return;
+  
+  /* Remove deleted routes */
+  for ( rn = route_top (routes); rn; rn = route_next (rn) )
+    {
+      if (rn && rn->info)
+        {
+          struct prefix_ipv4 *p = (struct prefix_ipv4 *)(&rn->p);
+          if ( (ext_rn = route_node_lookup (external_routes, (struct prefix *)p)) )
+            {
+              if (ext_rn->info)
+                {
+                  ospf_zebra_delete (p, ext_rn->info);
+                  ospf_route_free( ext_rn->info);
+                  ext_rn->info = NULL;
+                }
+              route_unlock_node (ext_rn);
+            }
+        }
+    }
+}
+
+/* rt: Old, cmprt: New */
+static void
+ospf_route_delete_uniq (struct route_table *rt, struct route_table *cmprt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL) 
+      if (or->path_type == OSPF_PATH_INTRA_AREA ||
+         or->path_type == OSPF_PATH_INTER_AREA)
+       {
+         if (or->type == OSPF_DESTINATION_NETWORK)
+           {
+             if (! ospf_route_exist_new_table (cmprt,
+                                          (struct prefix_ipv4 *) &rn->p))
+               ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or);
+           }
+         else if (or->type == OSPF_DESTINATION_DISCARD)
+           if (! ospf_route_exist_new_table (cmprt,
+                                        (struct prefix_ipv4 *) &rn->p))
+             ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p);
+       }
+}
+
+/* Install routes to table. */
+void
+ospf_route_install (struct ospf *ospf, struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  /* rt contains new routing table, new_table contains an old one.
+     updating pointers */
+  if (ospf->old_table)
+    ospf_route_table_free (ospf->old_table);
+
+  ospf->old_table = ospf->new_table;
+  ospf->new_table = rt;
+
+  /* Delete old routes. */
+  if (ospf->old_table)
+    ospf_route_delete_uniq (ospf->old_table, rt);
+  if (ospf->old_external_route)
+    ospf_route_delete_same_ext (ospf->old_external_route, rt);
+
+  /* Install new routes. */
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      {
+       if (or->type == OSPF_DESTINATION_NETWORK)
+         {
+           if (! ospf_route_match_same (ospf->old_table,
+                                        (struct prefix_ipv4 *)&rn->p, or))
+             ospf_zebra_add ((struct prefix_ipv4 *) &rn->p, or);
+         }
+       else if (or->type == OSPF_DESTINATION_DISCARD)
+         if (! ospf_route_match_same (ospf->old_table,
+                                      (struct prefix_ipv4 *) &rn->p, or))
+           ospf_zebra_add_discard ((struct prefix_ipv4 *) &rn->p);
+      }
+}
+
+/* RFC2328 16.1. (4). For "router". */
+void
+ospf_intra_add_router (struct route_table *rt, struct vertex *v,
+                      struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct prefix_ipv4 p;
+  struct router_lsa *lsa;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_router: Start");
+
+  lsa = (struct router_lsa *) v->lsa;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_router: LS ID: %s",
+              inet_ntoa (lsa->header.id));
+  
+  if (!OSPF_IS_AREA_BACKBONE(area))
+    ospf_vl_up_check (area, lsa->header.id, v);
+
+  if (!CHECK_FLAG (lsa->flags, ROUTER_LSA_SHORTCUT))
+    area->shortcut_capability = 0;
+
+  /* If the newly added vertex is an area border router or AS boundary
+     router, a routing table entry is added whose destination type is
+     "router". */
+  if (! IS_ROUTER_LSA_BORDER (lsa) && ! IS_ROUTER_LSA_EXTERNAL (lsa))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_intra_add_router: "
+                  "this router is neither ASBR nor ABR, skipping it");
+      return;
+    }
+
+  /* Update ABR and ASBR count in this area. */
+  if (IS_ROUTER_LSA_BORDER (lsa))
+    area->abr_count++;
+  if (IS_ROUTER_LSA_EXTERNAL (lsa))
+    area->asbr_count++;
+
+  /* The Options field found in the associated router-LSA is copied
+     into the routing table entry's Optional capabilities field. Call
+     the newly added vertex Router X. */
+  or = ospf_route_new ();
+
+  or->id = v->id;
+  or->u.std.area_id = area->area_id;
+  or->u.std.external_routing = area->external_routing;
+  or->path_type = OSPF_PATH_INTRA_AREA;
+  or->cost = v->distance;
+  or->type = OSPF_DESTINATION_ROUTER;
+  or->u.std.origin = (struct lsa_header *) lsa;
+  or->u.std.options = lsa->header.options;
+  or->u.std.flags = lsa->flags;
+
+  /* If Router X is the endpoint of one of the calculating router's
+     virtual links, and the virtual link uses Area A as Transit area:
+     the virtual link is declared up, the IP address of the virtual
+     interface is set to the IP address of the outgoing interface
+     calculated above for Router X, and the virtual neighbor's IP
+     address is set to Router X's interface address (contained in
+     Router X's router-LSA) that points back to the root of the
+     shortest- path tree; equivalently, this is the interface that
+     points back to Router X's parent vertex on the shortest-path tree
+     (similar to the calculation in Section 16.1.1). */
+
+  p.family = AF_INET;
+  p.prefix = v->id;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_router: talking about %s/%d",
+              inet_ntoa (p.prefix), p.prefixlen);
+
+  rn = route_node_get (rt, (struct prefix *) &p);
+
+  /* Note that we keep all routes to ABRs and ASBRs, not only the best */
+  if (rn->info == NULL)
+    rn->info = list_new ();
+  else
+    route_unlock_node (rn);
+
+  ospf_route_copy_nexthops_from_vertex (or, v);
+
+  listnode_add (rn->info, or);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_router: Stop");
+}
+
+/* RFC2328 16.1. (4).  For transit network. */
+void
+ospf_intra_add_transit (struct route_table *rt, struct vertex *v,
+                       struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct prefix_ipv4 p;
+  struct network_lsa *lsa;
+
+  lsa = (struct network_lsa*) v->lsa;
+
+  /* If the newly added vertex is a transit network, the routing table
+     entry for the network is located.  The entry's Destination ID is
+     the IP network number, which can be obtained by masking the
+     Vertex ID (Link State ID) with its associated subnet mask (found
+     in the body of the associated network-LSA). */
+  p.family = AF_INET;
+  p.prefix = v->id;
+  p.prefixlen = ip_masklen (lsa->mask);
+  apply_mask_ipv4 (&p);
+
+  rn = route_node_get (rt, (struct prefix *) &p);
+
+  /* If the routing table entry already exists (i.e., there is already
+     an intra-area route to the destination installed in the routing
+     table), multiple vertices have mapped to the same IP network.
+     For example, this can occur when a new Designated Router is being
+     established.  In this case, the current routing table entry
+     should be overwritten if and only if the newly found path is just
+     as short and the current routing table entry's Link State Origin
+     has a smaller Link State ID than the newly added vertex' LSA. */
+  if (rn->info)
+    {
+      struct ospf_route *cur_or;
+
+      route_unlock_node (rn);
+      cur_or = rn->info;
+
+      if (v->distance > cur_or->cost ||
+          IPV4_ADDR_CMP (&cur_or->u.std.origin->id, &lsa->header.id) > 0)
+       return;
+      
+      ospf_route_free (rn->info);
+    }
+
+  or = ospf_route_new ();
+
+  or->id = v->id;
+  or->u.std.area_id = area->area_id;
+  or->u.std.external_routing = area->external_routing;
+  or->path_type = OSPF_PATH_INTRA_AREA;
+  or->cost = v->distance;
+  or->type = OSPF_DESTINATION_NETWORK;
+  or->u.std.origin = (struct lsa_header *) lsa;
+
+  ospf_route_copy_nexthops_from_vertex (or, v);
+  
+  rn->info = or;
+}
+
+/* RFC2328 16.1. second stage. */
+void
+ospf_intra_add_stub (struct route_table *rt, struct router_lsa_link *link,
+                    struct vertex *v, struct ospf_area *area,
+                    int parent_is_root, int lsa_pos)
+{
+  u_int32_t cost;
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct prefix_ipv4 p;
+  struct router_lsa *lsa;
+  struct ospf_interface *oi;
+  struct ospf_path *path;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_stub(): Start");
+
+  lsa = (struct router_lsa *) v->lsa;
+
+  p.family = AF_INET;
+  p.prefix = link->link_id;
+  p.prefixlen = ip_masklen (link->link_data);
+  apply_mask_ipv4 (&p);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_stub(): processing route to %s/%d",  
+              inet_ntoa (p.prefix), p.prefixlen);
+
+  /* (1) Calculate the distance D of stub network from the root.  D is
+     equal to the distance from the root to the router vertex
+     (calculated in stage 1), plus the stub network link's advertised
+     cost. */
+  cost = v->distance + ntohs (link->m[0].metric);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_stub(): calculated cost is %d + %d = %d", 
+              v->distance, ntohs(link->m[0].metric), cost);
+  
+  /* PtP links with /32 masks adds host routes to remote, directly
+   * connected hosts, see RFC 2328, 12.4.1.1, Option 1.
+   * Such routes can just be ignored for the sake of tidyness.
+   */
+  if (parent_is_root && link->link_data.s_addr == 0xffffffff &&
+      ospf_if_lookup_by_local_addr (area->ospf, NULL, link->link_id))
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("%s: ignoring host route %s/32 to self.",
+                    __func__, inet_ntoa (link->link_id));
+      return;
+    }
+  
+  rn = route_node_get (rt, (struct prefix *) &p);
+
+  /* Lookup current routing table. */
+  if (rn->info)
+    {
+      struct ospf_route *cur_or;
+
+      route_unlock_node (rn);
+
+      cur_or = rn->info;
+
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_intra_add_stub(): "
+                  "another route to the same prefix found with cost %u",
+                  cur_or->cost);
+
+      /* Compare this distance to the current best cost to the stub
+        network.  This is done by looking up the stub network's
+        current routing table entry.  If the calculated distance D is
+        larger, go on to examine the next stub network link in the
+        LSA. */
+      if (cost > cur_or->cost)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_intra_add_stub(): old route is better, exit");
+         return;
+       }
+
+      /* (2) If this step is reached, the stub network's routing table
+        entry must be updated.  Calculate the set of next hops that
+        would result from using the stub network link.  This
+        calculation is shown in Section 16.1.1; input to this
+        calculation is the destination (the stub network) and the
+        parent vertex (the router vertex). If the distance D is the
+        same as the current routing table cost, simply add this set
+        of next hops to the routing table entry's list of next hops.
+        In this case, the routing table already has a Link State
+        Origin.  If this Link State Origin is a router-LSA whose Link
+        State ID is smaller than V's Router ID, reset the Link State
+        Origin to V's router-LSA. */
+
+      if (cost == cur_or->cost)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_intra_add_stub(): routes are equal, merge");
+
+         ospf_route_copy_nexthops_from_vertex (cur_or, v);
+
+         if (IPV4_ADDR_CMP (&cur_or->u.std.origin->id, &lsa->header.id) < 0)
+           cur_or->u.std.origin = (struct lsa_header *) lsa;
+         return;
+       }
+
+      /* Otherwise D is smaller than the routing table cost.
+        Overwrite the current routing table entry by setting the
+        routing table entry's cost to D, and by setting the entry's
+        list of next hops to the newly calculated set.  Set the
+        routing table entry's Link State Origin to V's router-LSA.
+        Then go on to examine the next stub network link. */
+
+      if (cost < cur_or->cost)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_intra_add_stub(): new route is better, set it");
+
+         cur_or->cost = cost;
+
+         list_delete_all_node (cur_or->paths);
+
+         ospf_route_copy_nexthops_from_vertex (cur_or, v);
+
+         cur_or->u.std.origin = (struct lsa_header *) lsa;
+         return;
+       }
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_intra_add_stub(): installing new route");
+
+  or = ospf_route_new ();
+
+  or->id = v->id;
+  or->u.std.area_id = area->area_id;
+  or->u.std.external_routing = area->external_routing;
+  or->path_type = OSPF_PATH_INTRA_AREA;
+  or->cost = cost;
+  or->type = OSPF_DESTINATION_NETWORK;
+  or->u.std.origin = (struct lsa_header *) lsa;
+
+  /* Nexthop is depend on connection type. */
+  if (v != area->spf)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_intra_add_stub(): this network is on remote router");
+      ospf_route_copy_nexthops_from_vertex (or, v);
+    }
+  else
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_intra_add_stub(): this network is on this router");
+
+      if ((oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos)))
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_intra_add_stub(): the interface is %s",
+                      IF_NAME (oi));
+
+         path = ospf_path_new ();
+         path->nexthop.s_addr = 0;
+         path->ifindex = oi->ifp->ifindex;
+         listnode_add (or->paths, path);
+       }
+      else
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_intra_add_stub(): where's the interface ?");
+       }
+    }
+
+  rn->info = or;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug("ospf_intra_add_stub(): Stop");
+}
+
+const char *ospf_path_type_str[] =
+{
+  "unknown-type",
+  "intra-area",
+  "inter-area",
+  "type1-external",
+  "type2-external"
+};
+
+void
+ospf_route_table_dump (struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  char buf1[BUFSIZ];
+  char buf2[BUFSIZ];
+  struct listnode *pnode;
+  struct ospf_path *path;
+
+#if 0
+  zlog_debug ("Type   Dest   Area   Path        Type    Cost   Next     Adv.");
+  zlog_debug ("                                        Hop(s)   Router(s)");
+#endif /* 0 */
+
+  zlog_debug ("========== OSPF routing table ==========");
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      {
+        if (or->type == OSPF_DESTINATION_NETWORK)
+         {
+           zlog_debug ("N %s/%d\t%s\t%s\t%d", 
+                      inet_ntop (AF_INET, &rn->p.u.prefix4, buf1, BUFSIZ),
+                      rn->p.prefixlen,
+                      inet_ntop (AF_INET, &or->u.std.area_id, buf2,
+                                 BUFSIZ),
+                      ospf_path_type_str[or->path_type],
+                      or->cost);
+           for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path))
+              zlog_debug ("  -> %s", inet_ntoa (path->nexthop));
+         }
+        else
+         zlog_debug ("R %s\t%s\t%s\t%d", 
+                    inet_ntop (AF_INET, &rn->p.u.prefix4, buf1, BUFSIZ),
+                    inet_ntop (AF_INET, &or->u.std.area_id, buf2,
+                               BUFSIZ),
+                    ospf_path_type_str[or->path_type],
+                    or->cost);
+      }
+  zlog_debug ("========================================");
+}
+
+/* This is 16.4.1 implementation.
+   o Intra-area paths using non-backbone areas are always the most preferred.
+   o The other paths, intra-area backbone paths and inter-area paths,
+     are of equal preference. */
+static int
+ospf_asbr_route_cmp (struct ospf *ospf, struct ospf_route *r1,
+                    struct ospf_route *r2)
+{
+  u_char r1_type, r2_type;
+
+  r1_type = r1->path_type;
+  r2_type = r2->path_type;
+
+  /* r1/r2 itself is backbone, and it's Inter-area path. */
+  if (OSPF_IS_AREA_ID_BACKBONE (r1->u.std.area_id))
+    r1_type = OSPF_PATH_INTER_AREA;
+  if (OSPF_IS_AREA_ID_BACKBONE (r2->u.std.area_id))
+    r2_type = OSPF_PATH_INTER_AREA;
+
+  return (r1_type - r2_type);
+}
+
+/* Compare two routes.
+ ret <  0 -- r1 is better.
+ ret == 0 -- r1 and r2 are the same.
+ ret >  0 -- r2 is better. */
+int
+ospf_route_cmp (struct ospf *ospf, struct ospf_route *r1,
+               struct ospf_route *r2)
+{
+  int ret = 0;
+
+  /* Path types of r1 and r2 are not the same. */
+  if ((ret = (r1->path_type - r2->path_type)))
+    return ret;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Route[Compare]: Path types are the same.");
+  /* Path types are the same, compare any cost. */
+  switch (r1->path_type)
+    {
+    case OSPF_PATH_INTRA_AREA:
+    case OSPF_PATH_INTER_AREA:
+      break;
+    case OSPF_PATH_TYPE1_EXTERNAL:
+      if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+       {
+         ret = ospf_asbr_route_cmp (ospf, r1->u.ext.asbr, r2->u.ext.asbr);
+         if (ret != 0)
+           return ret;
+       }
+      break;
+    case OSPF_PATH_TYPE2_EXTERNAL:
+      if ((ret = (r1->u.ext.type2_cost - r2->u.ext.type2_cost)))
+       return ret;
+
+      if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+       {
+         ret = ospf_asbr_route_cmp (ospf, r1->u.ext.asbr, r2->u.ext.asbr);
+         if (ret != 0)
+           return ret;
+       }
+      break;
+    }      
+
+  /* Anyway, compare the costs. */
+  return (r1->cost - r2->cost);
+}
+
+static int
+ospf_path_exist (struct list *plist, struct in_addr nexthop,
+                struct ospf_interface *oi)
+{
+  struct listnode *node, *nnode;
+  struct ospf_path *path;
+
+  for (ALL_LIST_ELEMENTS (plist, node, nnode, path))
+    if (IPV4_ADDR_SAME (&path->nexthop, &nexthop) &&
+       path->ifindex == oi->ifp->ifindex)
+      return 1;
+
+  return 0;
+}
+
+void
+ospf_route_copy_nexthops_from_vertex (struct ospf_route *to,
+                                     struct vertex *v)
+{
+  struct listnode *node;
+  struct ospf_path *path;
+  struct vertex_nexthop *nexthop;
+  struct vertex_parent *vp;
+
+  assert (to->paths);
+
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
+    {
+      nexthop = vp->nexthop;
+      
+      if (nexthop->oi != NULL) 
+       {
+         if (! ospf_path_exist (to->paths, nexthop->router, nexthop->oi))
+           {
+             path = ospf_path_new ();
+             path->nexthop = nexthop->router;
+             path->ifindex = nexthop->oi->ifp->ifindex;
+             listnode_add (to->paths, path);
+           }
+       }
+    }
+}
+
+struct ospf_path *
+ospf_path_lookup (struct list *plist, struct ospf_path *path)
+{
+  struct listnode *node;
+  struct ospf_path *op;
+
+  for (ALL_LIST_ELEMENTS_RO (plist, node, op))
+  {
+    if (!IPV4_ADDR_SAME (&op->nexthop, &path->nexthop))
+      continue;
+    if (!IPV4_ADDR_SAME (&op->adv_router, &path->adv_router))
+      continue;
+    if (op->ifindex != path->ifindex)
+      continue;
+    return op;
+  }
+  return NULL;
+}
+
+void
+ospf_route_copy_nexthops (struct ospf_route *to, struct list *from)
+{
+  struct listnode *node, *nnode;
+  struct ospf_path *path;
+
+  assert (to->paths);
+
+  for (ALL_LIST_ELEMENTS (from, node, nnode, path))
+    /* The same routes are just discarded. */
+    if (!ospf_path_lookup (to->paths, path))
+      listnode_add (to->paths, ospf_path_dup (path));
+}
+
+void
+ospf_route_subst_nexthops (struct ospf_route *to, struct list *from)
+{
+
+  list_delete_all_node (to->paths);
+  ospf_route_copy_nexthops (to, from);
+}
+
+void
+ospf_route_subst (struct route_node *rn, struct ospf_route *new_or,
+                 struct ospf_route *over)
+{
+  route_lock_node (rn);
+  ospf_route_free (rn->info);
+
+  ospf_route_copy_nexthops (new_or, over->paths);
+  rn->info = new_or;
+  route_unlock_node (rn);
+}
+
+void
+ospf_route_add (struct route_table *rt, struct prefix_ipv4 *p,
+               struct ospf_route *new_or, struct ospf_route *over)
+{
+  struct route_node *rn;
+
+  rn = route_node_get (rt, (struct prefix *) p);
+
+  ospf_route_copy_nexthops (new_or, over->paths);
+
+  if (rn->info)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_route_add(): something's wrong !");
+      route_unlock_node (rn);
+      return;
+    }
+
+  rn->info = new_or;
+}
+
+void
+ospf_prune_unreachable_networks (struct route_table *rt)
+{
+  struct route_node *rn, *next;
+  struct ospf_route *or;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Pruning unreachable networks");
+
+  for (rn = route_top (rt); rn; rn = next)
+    {
+      next = route_next (rn);
+      if (rn->info != NULL)
+       {
+         or = rn->info;
+         if (listcount (or->paths) == 0)
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug ("Pruning route to %s/%d",
+                          inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
+
+             ospf_route_free (or);
+             rn->info = NULL;
+             route_unlock_node (rn);
+           }
+       }
+    }
+}
+
+void
+ospf_prune_unreachable_routers (struct route_table *rtrs)
+{
+  struct route_node *rn, *next;
+  struct ospf_route *or;
+  struct listnode *node, *nnode;
+  struct list *paths;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Pruning unreachable routers");
+
+  for (rn = route_top (rtrs); rn; rn = next)
+    {
+      next = route_next (rn);
+      if ((paths = rn->info) == NULL)
+       continue;
+
+      for (ALL_LIST_ELEMENTS (paths, node, nnode, or))
+       {
+         if (listcount (or->paths) == 0)
+           {
+             if (IS_DEBUG_OSPF_EVENT)
+               {
+                 zlog_debug ("Pruning route to rtr %s",
+                            inet_ntoa (rn->p.u.prefix4));
+                 zlog_debug ("               via area %s",
+                            inet_ntoa (or->u.std.area_id));
+               }
+
+             listnode_delete (paths, or);
+             ospf_route_free (or);
+           }
+       }
+
+      if (listcount (paths) == 0)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("Pruning router node %s", inet_ntoa (rn->p.u.prefix4));
+
+         list_delete (paths);
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+    }
+}
+
+int
+ospf_add_discard_route (struct route_table *rt, struct ospf_area *area,
+                       struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+  struct ospf_route *or, *new_or;
+
+  rn = route_node_get (rt, (struct prefix *) p);
+
+  if (rn == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_add_discard_route(): router installation error");
+      return 0;
+    }
+
+  if (rn->info) /* If the route to the same destination is found */
+    {
+      route_unlock_node (rn);
+
+      or = rn->info;
+
+      if (or->path_type == OSPF_PATH_INTRA_AREA)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_add_discard_route(): "
+                      "an intra-area route exists");
+         return 0;
+       }
+
+      if (or->type == OSPF_DESTINATION_DISCARD)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("ospf_add_discard_route(): "
+                      "discard entry already installed");
+         return 0;
+       }
+
+      ospf_route_free (rn->info);
+  }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_add_discard_route(): "
+               "adding %s/%d", inet_ntoa (p->prefix), p->prefixlen);
+
+  new_or = ospf_route_new ();
+  new_or->type = OSPF_DESTINATION_DISCARD;
+  new_or->id.s_addr = 0;
+  new_or->cost = 0;
+  new_or->u.std.area_id = area->area_id;
+  new_or->u.std.external_routing = area->external_routing;
+  new_or->path_type = OSPF_PATH_INTER_AREA;
+  rn->info = new_or;
+
+  ospf_zebra_add_discard (p);
+
+  return 1;
+}
+
+void
+ospf_delete_discard_route (struct route_table *rt, struct prefix_ipv4 *p)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_delete_discard_route(): "
+               "deleting %s/%d", inet_ntoa (p->prefix), p->prefixlen);
+
+  rn = route_node_lookup (rt, (struct prefix*)p);
+
+  if (rn == NULL)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug("ospf_delete_discard_route(): no route found");
+      return;
+    }
+
+  or = rn->info;
+
+  if (or->path_type == OSPF_PATH_INTRA_AREA)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_delete_discard_route(): "
+                   "an intra-area route exists");
+      return;
+    }
+
+  if (or->type != OSPF_DESTINATION_DISCARD)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("ospf_delete_discard_route(): "
+                   "not a discard entry");
+      return;
+    }
+
+  /* free the route entry and the route node */
+  ospf_route_free (rn->info);
+
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+
+  /* remove the discard entry from the rib */
+  ospf_zebra_delete_discard(p);
+
+  return;
+}
+
diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h
new file mode 100644 (file)
index 0000000..d509e4a
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * OSPF routing table.
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ROUTE_H
+#define _ZEBRA_OSPF_ROUTE_H
+
+#define OSPF_DESTINATION_ROUTER                1
+#define OSPF_DESTINATION_NETWORK       2
+#define OSPF_DESTINATION_DISCARD       3
+
+#define OSPF_PATH_MIN                  0
+#define OSPF_PATH_INTRA_AREA           1
+#define OSPF_PATH_INTER_AREA           2
+#define OSPF_PATH_TYPE1_EXTERNAL       3
+#define OSPF_PATH_TYPE2_EXTERNAL       4
+#define OSPF_PATH_MAX                  5
+
+/* OSPF Path. */
+struct ospf_path
+{
+  struct in_addr nexthop;
+  struct in_addr adv_router;
+  ifindex_t ifindex;
+};
+
+/* Below is the structure linked to every
+   route node. Note that for Network routing
+   entries a single ospf_route is kept, while
+   for ABRs and ASBRs (Router routing entries),
+   we link an instance of ospf_router_route
+   where a list of paths is maintained, so
+
+   nr->info is a (struct ospf_route *) for OSPF_DESTINATION_NETWORK
+   but
+   nr->info is a (struct ospf_router_route *) for OSPF_DESTINATION_ROUTER
+*/
+
+struct route_standard
+{
+  /* Link Sate Origin. */
+  struct lsa_header *origin;
+
+  /* Associated Area. */
+  struct in_addr area_id;      /* The area the route belongs to */
+
+  /*  Area Type */
+  int external_routing;
+
+  /* Optional Capability. */
+  u_char options;              /* Get from LSA header. */
+
+  /*  */
+  u_char flags;                /* From router-LSA */
+};
+
+struct route_external
+{
+  /* Link State Origin. */
+  struct ospf_lsa *origin;
+
+  /* Link State Cost Type2. */
+  u_int32_t type2_cost;
+
+  /* Tag value. */
+  u_int32_t tag;
+
+  /* ASBR route. */
+  struct ospf_route *asbr;
+};
+
+struct ospf_route
+{
+  /* Create time. */
+  time_t ctime;
+
+  /* Modified time. */
+  time_t mtime;
+
+  /* Destination Type. */
+  u_char type;
+
+  /* Destination ID. */                /* i.e. Link State ID. */
+  struct in_addr id;
+
+  /* Address Mask. */
+  struct in_addr mask;         /* Only valid for networks. */
+
+  /* Path Type. */
+  u_char path_type;
+
+  /* List of Paths. */
+  struct list *paths;
+
+  /* Link State Cost. */
+  u_int32_t cost;              /* i.e. metric. */
+
+  /* Route specific info. */
+  union
+  {
+    struct route_standard std;
+    struct route_external ext;
+  } u;
+};
+
+extern struct ospf_path *ospf_path_new (void);
+extern void ospf_path_free (struct ospf_path *);
+extern struct ospf_path *ospf_path_lookup (struct list *, struct ospf_path *);
+extern struct ospf_route *ospf_route_new (void);
+extern void ospf_route_free (struct ospf_route *);
+extern void ospf_route_delete (struct route_table *);
+extern void ospf_route_table_free (struct route_table *);
+
+extern void ospf_route_install (struct ospf *, struct route_table *);
+extern void ospf_route_table_dump (struct route_table *);
+
+extern void ospf_intra_add_router (struct route_table *, struct vertex *,
+                                  struct ospf_area *);
+
+extern void ospf_intra_add_transit (struct route_table *, struct vertex *,
+                                   struct ospf_area *);
+
+extern void ospf_intra_add_stub (struct route_table *,
+                                struct router_lsa_link *, struct vertex *,
+                                struct ospf_area *,
+                                int parent_is_root, int);
+
+extern int ospf_route_cmp (struct ospf *, struct ospf_route *,
+                          struct ospf_route *);
+extern void ospf_route_copy_nexthops (struct ospf_route *, struct list *);
+extern void ospf_route_copy_nexthops_from_vertex (struct ospf_route *,
+                                                 struct vertex *);
+
+extern void ospf_route_subst (struct route_node *, struct ospf_route *,
+                             struct ospf_route *);
+extern void ospf_route_add (struct route_table *, struct prefix_ipv4 *,
+                           struct ospf_route *, struct ospf_route *);
+
+extern void ospf_route_subst_nexthops (struct ospf_route *, struct list *);
+extern void ospf_prune_unreachable_networks (struct route_table *);
+extern void ospf_prune_unreachable_routers (struct route_table *);
+extern int ospf_add_discard_route (struct route_table *, struct ospf_area *,
+                                  struct prefix_ipv4 *);
+extern void ospf_delete_discard_route (struct route_table *, struct prefix_ipv4 *);
+extern int ospf_route_match_same (struct route_table *, struct prefix_ipv4 *,
+                                 struct ospf_route *);
+
+#endif /* _ZEBRA_OSPF_ROUTE_H */
diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c
new file mode 100644 (file)
index 0000000..31d7ce2
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * Route map function of ospfd.
+ * Copyright (C) 2000 IP Infusion Inc.
+ *
+ * Written by Toshiaki Takada.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "routemap.h"
+#include "command.h"
+#include "log.h"
+#include "plist.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+
+/* Hook function for updating route_map assignment. */
+static void
+ospf_route_map_update (const char *name)
+{
+  struct ospf *ospf;
+  int type;
+
+  /* If OSPF instatnce does not exist, return right now. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return;
+
+  /* Update route-map */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      if (ROUTEMAP_NAME (ospf, type)
+         && strcmp (ROUTEMAP_NAME (ospf, type), name) == 0)
+       {
+         /* Keep old route-map. */
+         struct route_map *old = ROUTEMAP (ospf, type);
+
+         /* Update route-map. */
+         ROUTEMAP (ospf, type) =
+           route_map_lookup_by_name (ROUTEMAP_NAME (ospf, type));
+
+         /* No update for this distribute type. */
+         if (old == NULL && ROUTEMAP (ospf, type) == NULL)
+           continue;
+
+         ospf_distribute_list_update (ospf, type);
+       }
+    }
+}
+
+static void
+ospf_route_map_event (route_map_event_t event, const char *name)
+{
+  struct ospf *ospf;
+  int type;
+
+  /* If OSPF instatnce does not exist, return right now. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return;
+
+  /* Update route-map. */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      if (ROUTEMAP_NAME (ospf, type) &&  ROUTEMAP (ospf, type)
+         && !strcmp (ROUTEMAP_NAME (ospf, type), name))
+        {
+          ospf_distribute_list_update (ospf, type);
+        }
+    }
+}
+
+/* Delete rip route map rule. */
+static int
+ospf_route_match_delete (struct vty *vty, struct route_map_index *index,
+                        const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+ospf_route_match_add (struct vty *vty, struct route_map_index *index,
+                     const char *command, const char *arg)
+{                                                                              
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+ospf_route_set_add (struct vty *vty, struct route_map_index *index,
+                   const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Delete rip route map rule. */
+static int
+ospf_route_set_delete (struct vty *vty, struct route_map_index *index,
+                      const char *command, const char *arg)
+{                                              
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+        {
+        case RMAP_RULE_MISSING:
+          vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        case RMAP_COMPILE_ERROR:
+          vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* `match ip netxthop ' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_ip_nexthop (void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  struct external_info *ei = object;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_OSPF)
+    {
+      p.family = AF_INET;
+      p.prefix = ei->nexthop;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+        return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, &p) == FILTER_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip next-hop' match statement. `arg' should be
+   access-list name. */
+static void *
+route_match_ip_nexthop_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_nexthop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+struct route_map_rule_cmd route_match_ip_nexthop_cmd =
+{
+  "ip next-hop",
+  route_match_ip_nexthop,
+  route_match_ip_nexthop_compile,
+  route_match_ip_nexthop_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+  struct external_info *ei = object;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_OSPF)
+    {
+      p.family = AF_INET;
+      p.prefix = ei->nexthop;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, &p) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_next_hop_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_next_hop_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd =
+{
+  "ip next-hop prefix-list",
+  route_match_ip_next_hop_prefix_list,
+  route_match_ip_next_hop_prefix_list_compile,
+  route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ip address IP_ACCESS_LIST' */
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+route_match_ip_address (void *rule, struct prefix *prefix,
+                        route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  /* struct prefix_ipv4 match; */
+
+  if (type == RMAP_OSPF)
+    {
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+        return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, prefix) == FILTER_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip address' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_address_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_address_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_ip_address_cmd =
+{
+  "ip address",
+  route_match_ip_address,
+  route_match_ip_address_compile,
+  route_match_ip_address_free
+};
+
+/* `match ip address prefix-list PREFIX_LIST' */
+static route_map_result_t
+route_match_ip_address_prefix_list (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type == RMAP_OSPF)
+    {
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
+{
+  "ip address prefix-list",
+  route_match_ip_address_prefix_list,
+  route_match_ip_address_prefix_list_compile,
+  route_match_ip_address_prefix_list_free
+};
+
+/* `match interface IFNAME' */
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+route_match_interface (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct interface *ifp;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      ei = object;
+      ifp = if_lookup_by_name ((char *)rule);
+
+      if (ifp == NULL || ifp->ifindex != ei->ifindex)
+       return RMAP_NOMATCH;
+
+      return RMAP_MATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `interface' match statement.  `arg' should be
+   interface name. */
+static void *
+route_match_interface_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `interface' value. */
+static void
+route_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_interface_cmd =
+{
+  "interface",
+  route_match_interface,
+  route_match_interface_compile,
+  route_match_interface_free
+};
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix,
+                 route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      tag = rule;
+      ei = object;
+
+      return ((ei->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH);
+    }
+
+  return RMAP_NOMATCH;
+}
+
+/* Route map commands for tag matching. */
+static struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+
+/* `set metric METRIC' */
+/* Set metric to attribute. */
+static route_map_result_t
+route_set_metric (void *rule, struct prefix *prefix,
+                  route_map_object_t type, void *object)
+{
+  u_int32_t *metric;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      /* Fetch routemap's rule information. */
+      metric = rule;
+      ei = object;
+
+      /* Set metric out value. */
+      ei->route_map_set.metric = *metric;
+    }
+  return RMAP_OKAY;
+}
+
+/* set metric compilation. */
+static void *
+route_set_metric_compile (const char *arg)
+{
+  u_int32_t *metric;
+  int32_t ret;
+
+  /* OSPF doesn't support the +/- in
+     set metric <+/-metric> check
+     Ignore the +/- component */
+  if (! all_digit (arg))
+    {
+      if ((strncmp (arg, "+", 1) == 0 || strncmp (arg, "-", 1) == 0) &&
+         all_digit (arg+1))
+       {
+         zlog_warn ("OSPF does not support 'set metric +/-'");
+         arg++;
+       }
+      else
+       return NULL;
+    }
+
+  metric = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+  ret = atoi (arg);
+
+  if (ret >= 0)
+    {
+      *metric = (u_int32_t)ret;
+      return metric;
+    }
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, metric);
+  return NULL;
+}
+
+/* Free route map's compiled `set metric' value. */
+static void
+route_set_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+struct route_map_rule_cmd route_set_metric_cmd =
+{
+  "metric",
+  route_set_metric,
+  route_set_metric_compile,
+  route_set_metric_free,
+};
+
+/* `set metric-type TYPE' */
+/* Set metric-type to attribute. */
+static route_map_result_t
+route_set_metric_type (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  u_int32_t *metric_type;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      /* Fetch routemap's rule information. */
+      metric_type = rule;
+      ei = object;
+
+      /* Set metric out value. */
+      ei->route_map_set.metric_type = *metric_type;
+    }
+  return RMAP_OKAY;
+}
+
+/* set metric-type compilation. */
+static void *
+route_set_metric_type_compile (const char *arg)
+{
+  u_int32_t *metric_type;
+
+  metric_type = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+  if (strcmp (arg, "type-1") == 0)
+    *metric_type = EXTERNAL_METRIC_TYPE_1;
+  else if (strcmp (arg, "type-2") == 0)
+    *metric_type = EXTERNAL_METRIC_TYPE_2;
+
+  if (*metric_type == EXTERNAL_METRIC_TYPE_1 ||
+      *metric_type == EXTERNAL_METRIC_TYPE_2)
+    return metric_type;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, metric_type);
+  return NULL;
+}
+
+/* Free route map's compiled `set metric-type' value. */
+static void
+route_set_metric_type_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+struct route_map_rule_cmd route_set_metric_type_cmd =
+{
+  "metric-type",
+  route_set_metric_type,
+  route_set_metric_type_compile,
+  route_set_metric_type_free,
+};
+
+static route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix,
+               route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct external_info *ei;
+
+  if (type == RMAP_OSPF)
+    {
+      tag = rule;
+      ei = object;
+
+      /* Set tag value */
+      ei->tag=*tag;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+DEFUN (match_ip_nexthop,
+       match_ip_nexthop_cmd,
+       "match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP access-list name\n")
+{
+  return ospf_route_match_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (no_match_ip_nexthop,
+       no_match_ip_nexthop_cmd,
+       "no match ip next-hop",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "ip next-hop", NULL);
+
+  return ospf_route_match_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_match_ip_nexthop,
+       no_match_ip_nexthop_val_cmd,
+       "no match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP access-list name\n")
+
+DEFUN (match_ip_next_hop_prefix_list,
+       match_ip_next_hop_prefix_list_cmd,
+       "match ip next-hop prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return ospf_route_match_add (vty, vty->index, "ip next-hop prefix-list",
+                              argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_cmd,
+       "no match ip next-hop prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "ip next-hop prefix-list",
+                                   NULL);
+  return ospf_route_match_delete (vty, vty->index, "ip next-hop prefix-list",
+                                 argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_val_cmd,
+       "no match ip next-hop prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_ip_address,
+       match_ip_address_cmd,
+       "match ip address (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP access-list name\n")
+{
+  return ospf_route_match_add (vty, vty->index, "ip address", argv[0]);
+}
+
+DEFUN (no_match_ip_address,
+       no_match_ip_address_cmd,
+       "no match ip address",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "ip address", NULL);
+
+  return ospf_route_match_delete (vty, vty->index, "ip address", argv[0]);
+}
+
+ALIAS (no_match_ip_address,
+       no_match_ip_address_val_cmd,
+       "no match ip address (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP access-list name\n")
+
+DEFUN (match_ip_address_prefix_list,
+       match_ip_address_prefix_list_cmd,
+       "match ip address prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return ospf_route_match_add (vty, vty->index, "ip address prefix-list",
+                              argv[0]);
+}
+
+DEFUN (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_cmd,
+       "no match ip address prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "ip address prefix-list",
+                                   NULL);
+  return ospf_route_match_delete (vty, vty->index, "ip address prefix-list",
+                                 argv[0]);
+}
+
+ALIAS (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_val_cmd,
+       "no match ip address prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_interface,
+       match_interface_cmd,
+       "match interface WORD",
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+{
+  return ospf_route_match_add (vty, vty->index, "interface", argv[0]);
+}
+
+DEFUN (no_match_interface,
+       no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "interface", NULL);
+
+  return ospf_route_match_delete (vty, vty->index, "interface", argv[0]);
+}
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+DEFUN (match_tag,
+       match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+{
+  return ospf_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return ospf_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return ospf_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Tag value\n")
+
+DEFUN (set_metric,
+       set_metric_cmd,
+       "set metric <0-4294967295>",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+{
+  return ospf_route_set_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_set_metric,
+       no_set_metric_cmd,
+       "no set metric",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n")
+{
+  if (argc == 0)
+    return ospf_route_set_delete (vty, vty->index, "metric", NULL);
+
+  return ospf_route_set_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_set_metric,
+       no_set_metric_val_cmd,
+       "no set metric <0-4294967295>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+
+DEFUN (set_metric_type,
+       set_metric_type_cmd,
+       "set metric-type (type-1|type-2)",
+       SET_STR
+       "Type of metric for destination routing protocol\n"
+       "OSPF[6] external type 1 metric\n"
+       "OSPF[6] external type 2 metric\n")
+{
+  if (strcmp (argv[0], "1") == 0)
+    return ospf_route_set_add (vty, vty->index, "metric-type", "type-1");
+  if (strcmp (argv[0], "2") == 0)
+    return ospf_route_set_add (vty, vty->index, "metric-type", "type-2");
+
+  return ospf_route_set_add (vty, vty->index, "metric-type", argv[0]);
+}
+
+DEFUN (no_set_metric_type,
+       no_set_metric_type_cmd,
+       "no set metric-type",
+       NO_STR
+       SET_STR
+       "Type of metric for destination routing protocol\n")
+{
+  if (argc == 0)
+    return ospf_route_set_delete (vty, vty->index, "metric-type", NULL);
+
+  return ospf_route_set_delete (vty, vty->index, "metric-type", argv[0]);
+}
+
+ALIAS (no_set_metric_type,
+       no_set_metric_type_val_cmd,
+       "no set metric-type (type-1|type-2)",
+       NO_STR
+       SET_STR
+       "Type of metric for destination routing protocol\n"
+       "OSPF[6] external type 1 metric\n"
+       "OSPF[6] external type 2 metric\n")
+
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <1-4294967295>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return ospf_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+      ospf_route_set_delete(vty, vty->index, "tag", NULL);
+
+  return ospf_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+/* Route-map init */
+void
+ospf_route_map_init (void)
+{
+  route_map_init ();
+  route_map_init_vty ();
+
+  route_map_add_hook (ospf_route_map_update);
+  route_map_delete_hook (ospf_route_map_update);
+  route_map_event_hook (ospf_route_map_event);
+  
+  route_map_install_match (&route_match_ip_nexthop_cmd);
+  route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd);
+  route_map_install_match (&route_match_ip_address_cmd);
+  route_map_install_match (&route_match_ip_address_prefix_list_cmd);
+  route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_tag_cmd);
+
+  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_set (&route_set_metric_type_cmd);
+  route_map_install_set (&route_set_tag_cmd);
+
+  install_element (RMAP_NODE, &match_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_nexthop_val_cmd);
+  install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_val_cmd);
+  install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
+
+  install_element (RMAP_NODE, &set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+  install_element (RMAP_NODE, &set_metric_type_cmd);
+  install_element (RMAP_NODE, &no_set_metric_type_cmd);
+  install_element (RMAP_NODE, &no_set_metric_type_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
+}
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
new file mode 100644 (file)
index 0000000..1f9b851
--- /dev/null
@@ -0,0 +1,2743 @@
+/* OSPFv2 SNMP support
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 2000 IP Infusion Inc.
+ *
+ * Written by Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "memory.h"
+#include "smux.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_snmp.h"
+
+/* OSPF2-MIB. */
+#define OSPF2MIB 1,3,6,1,2,1,14
+
+/* OSPF MIB General Group values. */
+#define OSPFROUTERID                     1
+#define OSPFADMINSTAT                    2
+#define OSPFVERSIONNUMBER                3
+#define OSPFAREABDRRTRSTATUS             4
+#define OSPFASBDRRTRSTATUS               5
+#define OSPFEXTERNLSACOUNT               6
+#define OSPFEXTERNLSACKSUMSUM            7
+#define OSPFTOSSUPPORT                   8
+#define OSPFORIGINATENEWLSAS             9
+#define OSPFRXNEWLSAS                    10
+#define OSPFEXTLSDBLIMIT                 11
+#define OSPFMULTICASTEXTENSIONS          12
+#define OSPFEXITOVERFLOWINTERVAL         13
+#define OSPFDEMANDEXTENSIONS             14
+
+/* OSPF MIB ospfAreaTable. */
+#define OSPFAREAID                       1
+#define OSPFAUTHTYPE                     2
+#define OSPFIMPORTASEXTERN               3
+#define OSPFSPFRUNS                      4
+#define OSPFAREABDRRTRCOUNT              5
+#define OSPFASBDRRTRCOUNT                6
+#define OSPFAREALSACOUNT                 7
+#define OSPFAREALSACKSUMSUM              8
+#define OSPFAREASUMMARY                  9
+#define OSPFAREASTATUS                   10
+
+/* OSPF MIB ospfStubAreaTable. */
+#define OSPFSTUBAREAID                   1
+#define OSPFSTUBTOS                      2
+#define OSPFSTUBMETRIC                   3
+#define OSPFSTUBSTATUS                   4
+#define OSPFSTUBMETRICTYPE               5
+
+/* OSPF MIB ospfLsdbTable. */
+#define OSPFLSDBAREAID                   1
+#define OSPFLSDBTYPE                     2
+#define OSPFLSDBLSID                     3
+#define OSPFLSDBROUTERID                 4
+#define OSPFLSDBSEQUENCE                 5
+#define OSPFLSDBAGE                      6
+#define OSPFLSDBCHECKSUM                 7
+#define OSPFLSDBADVERTISEMENT            8
+
+/* OSPF MIB ospfAreaRangeTable. */
+#define OSPFAREARANGEAREAID              1
+#define OSPFAREARANGENET                 2
+#define OSPFAREARANGEMASK                3
+#define OSPFAREARANGESTATUS              4
+#define OSPFAREARANGEEFFECT              5
+
+/* OSPF MIB ospfHostTable. */
+#define OSPFHOSTIPADDRESS                1
+#define OSPFHOSTTOS                      2
+#define OSPFHOSTMETRIC                   3
+#define OSPFHOSTSTATUS                   4
+#define OSPFHOSTAREAID                   5
+
+/* OSPF MIB ospfIfTable. */
+#define OSPFIFIPADDRESS                  1
+#define OSPFADDRESSLESSIF                2
+#define OSPFIFAREAID                     3
+#define OSPFIFTYPE                       4
+#define OSPFIFADMINSTAT                  5
+#define OSPFIFRTRPRIORITY                6
+#define OSPFIFTRANSITDELAY               7
+#define OSPFIFRETRANSINTERVAL            8
+#define OSPFIFHELLOINTERVAL              9
+#define OSPFIFRTRDEADINTERVAL            10
+#define OSPFIFPOLLINTERVAL               11
+#define OSPFIFSTATE                      12
+#define OSPFIFDESIGNATEDROUTER           13
+#define OSPFIFBACKUPDESIGNATEDROUTER     14
+#define OSPFIFEVENTS                     15
+#define OSPFIFAUTHKEY                    16
+#define OSPFIFSTATUS                     17
+#define OSPFIFMULTICASTFORWARDING        18
+#define OSPFIFDEMAND                     19
+#define OSPFIFAUTHTYPE                   20
+
+/* OSPF MIB ospfIfMetricTable. */
+#define OSPFIFMETRICIPADDRESS            1
+#define OSPFIFMETRICADDRESSLESSIF        2
+#define OSPFIFMETRICTOS                  3
+#define OSPFIFMETRICVALUE                4
+#define OSPFIFMETRICSTATUS               5
+
+/* OSPF MIB ospfVirtIfTable. */
+#define OSPFVIRTIFAREAID                 1
+#define OSPFVIRTIFNEIGHBOR               2
+#define OSPFVIRTIFTRANSITDELAY           3
+#define OSPFVIRTIFRETRANSINTERVAL        4
+#define OSPFVIRTIFHELLOINTERVAL          5
+#define OSPFVIRTIFRTRDEADINTERVAL        6
+#define OSPFVIRTIFSTATE                  7
+#define OSPFVIRTIFEVENTS                 8
+#define OSPFVIRTIFAUTHKEY                9
+#define OSPFVIRTIFSTATUS                 10
+#define OSPFVIRTIFAUTHTYPE               11
+
+/* OSPF MIB ospfNbrTable. */
+#define OSPFNBRIPADDR                    1
+#define OSPFNBRADDRESSLESSINDEX          2
+#define OSPFNBRRTRID                     3
+#define OSPFNBROPTIONS                   4
+#define OSPFNBRPRIORITY                  5
+#define OSPFNBRSTATE                     6
+#define OSPFNBREVENTS                    7
+#define OSPFNBRLSRETRANSQLEN             8
+#define OSPFNBMANBRSTATUS                9
+#define OSPFNBMANBRPERMANENCE            10
+#define OSPFNBRHELLOSUPPRESSED           11
+
+/* OSPF MIB ospfVirtNbrTable. */
+#define OSPFVIRTNBRAREA                  1
+#define OSPFVIRTNBRRTRID                 2
+#define OSPFVIRTNBRIPADDR                3
+#define OSPFVIRTNBROPTIONS               4
+#define OSPFVIRTNBRSTATE                 5
+#define OSPFVIRTNBREVENTS                6
+#define OSPFVIRTNBRLSRETRANSQLEN         7
+#define OSPFVIRTNBRHELLOSUPPRESSED       8
+
+/* OSPF MIB ospfExtLsdbTable. */
+#define OSPFEXTLSDBTYPE                  1
+#define OSPFEXTLSDBLSID                  2
+#define OSPFEXTLSDBROUTERID              3
+#define OSPFEXTLSDBSEQUENCE              4
+#define OSPFEXTLSDBAGE                   5
+#define OSPFEXTLSDBCHECKSUM              6
+#define OSPFEXTLSDBADVERTISEMENT         7
+
+/* OSPF MIB ospfAreaAggregateTable. */
+#define OSPFAREAAGGREGATEAREAID          1
+#define OSPFAREAAGGREGATELSDBTYPE        2
+#define OSPFAREAAGGREGATENET             3
+#define OSPFAREAAGGREGATEMASK            4
+#define OSPFAREAAGGREGATESTATUS          5
+#define OSPFAREAAGGREGATEEFFECT          6
+
+/* SYNTAX Status from OSPF-MIB. */
+#define OSPF_STATUS_ENABLED  1
+#define OSPF_STATUS_DISABLED 2
+
+/* SNMP value hack. */
+#define COUNTER     ASN_COUNTER
+#define INTEGER     ASN_INTEGER
+#define GAUGE       ASN_GAUGE
+#define TIMETICKS   ASN_TIMETICKS
+#define IPADDRESS   ASN_IPADDRESS
+#define STRING      ASN_OCTET_STR
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* OSPF-MIB instances. */
+oid ospf_oid [] = { OSPF2MIB };
+oid ospf_trap_oid [] = { OSPF2MIB, 16, 2 }; /* Not reverse mappable! */
+
+/* IP address 0.0.0.0. */
+static struct in_addr ospf_empty_addr = { .s_addr = 0 };
+
+/* Hook functions. */
+static u_char *ospfGeneralGroup (struct variable *, oid *, size_t *,
+                                int, size_t *, WriteMethod **);
+static u_char *ospfAreaEntry (struct variable *, oid *, size_t *, int,
+                             size_t *, WriteMethod **);
+static u_char *ospfStubAreaEntry (struct variable *, oid *, size_t *,
+                                 int, size_t *, WriteMethod **);
+static u_char *ospfLsdbEntry (struct variable *, oid *, size_t *, int,
+                             size_t *, WriteMethod **);
+static u_char *ospfAreaRangeEntry (struct variable *, oid *, size_t *, int,
+                                  size_t *, WriteMethod **);
+static u_char *ospfHostEntry (struct variable *, oid *, size_t *, int,
+                             size_t *, WriteMethod **);
+static u_char *ospfIfEntry (struct variable *, oid *, size_t *, int,
+                           size_t *, WriteMethod **);
+static u_char *ospfIfMetricEntry (struct variable *, oid *, size_t *, int,
+                                 size_t *, WriteMethod **);
+static u_char *ospfVirtIfEntry (struct variable *, oid *, size_t *, int,
+                               size_t *, WriteMethod **);
+static u_char *ospfNbrEntry (struct variable *, oid *, size_t *, int,
+                            size_t *, WriteMethod **);
+static u_char *ospfVirtNbrEntry (struct variable *, oid *, size_t *, int,
+                                size_t *, WriteMethod **);
+static u_char *ospfExtLsdbEntry (struct variable *, oid *, size_t *, int,
+                                size_t *, WriteMethod **);
+static u_char *ospfAreaAggregateEntry (struct variable *, oid *, size_t *,
+                                      int, size_t *, WriteMethod **);
+
+struct variable ospf_variables[] = 
+{
+  /* OSPF general variables */
+  {OSPFROUTERID,              IPADDRESS, RWRITE, ospfGeneralGroup,
+   2, {1, 1}},
+  {OSPFADMINSTAT,             INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 2}},
+  {OSPFVERSIONNUMBER,         INTEGER, RONLY, ospfGeneralGroup,
+   2, {1, 3}},
+  {OSPFAREABDRRTRSTATUS,      INTEGER, RONLY, ospfGeneralGroup,
+   2, {1, 4}},
+  {OSPFASBDRRTRSTATUS,        INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 5}},
+  {OSPFEXTERNLSACOUNT,        GAUGE, RONLY, ospfGeneralGroup,
+   2, {1, 6}},
+  {OSPFEXTERNLSACKSUMSUM,     INTEGER, RONLY, ospfGeneralGroup,
+   2, {1, 7}},
+  {OSPFTOSSUPPORT,            INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 8}},
+  {OSPFORIGINATENEWLSAS,      COUNTER, RONLY, ospfGeneralGroup,
+   2, {1, 9}},
+  {OSPFRXNEWLSAS,             COUNTER, RONLY, ospfGeneralGroup,
+   2, {1, 10}},
+  {OSPFEXTLSDBLIMIT,          INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 11}},
+  {OSPFMULTICASTEXTENSIONS,   INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 12}},
+  {OSPFEXITOVERFLOWINTERVAL,  INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 13}},
+  {OSPFDEMANDEXTENSIONS,      INTEGER, RWRITE, ospfGeneralGroup,
+   2, {1, 14}},
+
+  /* OSPF area data structure. */
+  {OSPFAREAID,                IPADDRESS, RONLY, ospfAreaEntry,
+   3, {2, 1, 1}},
+  {OSPFAUTHTYPE,              INTEGER, RWRITE, ospfAreaEntry,
+   3, {2, 1, 2}},
+  {OSPFIMPORTASEXTERN,        INTEGER, RWRITE, ospfAreaEntry,
+   3, {2, 1, 3}},
+  {OSPFSPFRUNS,               COUNTER, RONLY, ospfAreaEntry,
+   3, {2, 1, 4}},
+  {OSPFAREABDRRTRCOUNT,       GAUGE, RONLY, ospfAreaEntry,
+   3, {2, 1, 5}},
+  {OSPFASBDRRTRCOUNT,         GAUGE, RONLY, ospfAreaEntry,
+   3, {2, 1, 6}},
+  {OSPFAREALSACOUNT,          GAUGE, RONLY, ospfAreaEntry,
+   3, {2, 1, 7}},
+  {OSPFAREALSACKSUMSUM,       INTEGER, RONLY, ospfAreaEntry,
+   3, {2, 1, 8}},
+  {OSPFAREASUMMARY,           INTEGER, RWRITE, ospfAreaEntry,
+   3, {2, 1, 9}},
+  {OSPFAREASTATUS,            INTEGER, RWRITE, ospfAreaEntry,
+   3, {2, 1, 10}},
+
+  /* OSPF stub area information. */
+  {OSPFSTUBAREAID,            IPADDRESS, RONLY, ospfStubAreaEntry,
+   3, {3, 1, 1}},
+  {OSPFSTUBTOS,               INTEGER, RONLY, ospfStubAreaEntry,
+   3, {3, 1, 2}},
+  {OSPFSTUBMETRIC,            INTEGER, RWRITE, ospfStubAreaEntry,
+   3, {3, 1, 3}},
+  {OSPFSTUBSTATUS,            INTEGER, RWRITE, ospfStubAreaEntry,
+   3, {3, 1, 4}},
+  {OSPFSTUBMETRICTYPE,        INTEGER, RWRITE, ospfStubAreaEntry,
+   3, {3, 1, 5}},
+
+  /* OSPF link state database. */
+  {OSPFLSDBAREAID,            IPADDRESS, RONLY, ospfLsdbEntry,
+   3, {4, 1, 1}},
+  {OSPFLSDBTYPE,              INTEGER, RONLY, ospfLsdbEntry,
+   3, {4, 1, 2}},
+  {OSPFLSDBLSID,              IPADDRESS, RONLY, ospfLsdbEntry,
+   3, {4, 1, 3}},
+  {OSPFLSDBROUTERID,          IPADDRESS, RONLY, ospfLsdbEntry,
+   3, {4, 1, 4}},
+  {OSPFLSDBSEQUENCE,          INTEGER, RONLY, ospfLsdbEntry,
+   3, {4, 1, 5}},
+  {OSPFLSDBAGE,               INTEGER, RONLY, ospfLsdbEntry,
+   3, {4, 1, 6}},
+  {OSPFLSDBCHECKSUM,          INTEGER, RONLY, ospfLsdbEntry,
+   3, {4, 1, 7}},
+  {OSPFLSDBADVERTISEMENT,     STRING, RONLY, ospfLsdbEntry,
+   3, {4, 1, 8}},
+
+  /* Area range table. */
+  {OSPFAREARANGEAREAID,       IPADDRESS, RONLY, ospfAreaRangeEntry,
+   3, {5, 1, 1}},
+  {OSPFAREARANGENET,          IPADDRESS, RONLY, ospfAreaRangeEntry,
+   3, {5, 1, 2}},
+  {OSPFAREARANGEMASK,         IPADDRESS, RWRITE, ospfAreaRangeEntry,
+   3, {5, 1, 3}},
+  {OSPFAREARANGESTATUS,       INTEGER, RWRITE, ospfAreaRangeEntry,
+   3, {5, 1, 4}},
+  {OSPFAREARANGEEFFECT,       INTEGER, RWRITE, ospfAreaRangeEntry,
+   3, {5, 1, 5}},
+
+  /* OSPF host table. */
+  {OSPFHOSTIPADDRESS,         IPADDRESS, RONLY, ospfHostEntry,
+   3, {6, 1, 1}},
+  {OSPFHOSTTOS,               INTEGER, RONLY, ospfHostEntry,
+   3, {6, 1, 2}},
+  {OSPFHOSTMETRIC,            INTEGER, RWRITE, ospfHostEntry,
+   3, {6, 1, 3}},
+  {OSPFHOSTSTATUS,            INTEGER, RWRITE, ospfHostEntry,
+   3, {6, 1, 4}},
+  {OSPFHOSTAREAID,            IPADDRESS, RONLY, ospfHostEntry,
+   3, {6, 1, 5}},
+
+  /* OSPF interface table. */
+  {OSPFIFIPADDRESS,           IPADDRESS, RONLY, ospfIfEntry,
+   3, {7, 1, 1}},
+  {OSPFADDRESSLESSIF,         INTEGER, RONLY, ospfIfEntry,
+   3, {7, 1, 2}},
+  {OSPFIFAREAID,              IPADDRESS, RWRITE, ospfIfEntry,
+   3, {7, 1, 3}},
+  {OSPFIFTYPE,                INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 4}},
+  {OSPFIFADMINSTAT,           INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 5}},
+  {OSPFIFRTRPRIORITY,         INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 6}},
+  {OSPFIFTRANSITDELAY,        INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 7}},
+  {OSPFIFRETRANSINTERVAL,     INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 8}},
+  {OSPFIFHELLOINTERVAL,       INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 9}},
+  {OSPFIFRTRDEADINTERVAL,     INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 10}},
+  {OSPFIFPOLLINTERVAL,        INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 11}},
+  {OSPFIFSTATE,               INTEGER, RONLY, ospfIfEntry,
+   3, {7, 1, 12}},
+  {OSPFIFDESIGNATEDROUTER,    IPADDRESS, RONLY, ospfIfEntry,
+   3, {7, 1, 13}},
+  {OSPFIFBACKUPDESIGNATEDROUTER, IPADDRESS, RONLY, ospfIfEntry,
+   3, {7, 1, 14}},
+  {OSPFIFEVENTS,              COUNTER, RONLY, ospfIfEntry,
+   3, {7, 1, 15}},
+  {OSPFIFAUTHKEY,             STRING,  RWRITE, ospfIfEntry,
+   3, {7, 1, 16}},
+  {OSPFIFSTATUS,              INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 17}},
+  {OSPFIFMULTICASTFORWARDING, INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 18}},
+  {OSPFIFDEMAND,              INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 19}},
+  {OSPFIFAUTHTYPE,            INTEGER, RWRITE, ospfIfEntry,
+   3, {7, 1, 20}},
+
+  /* OSPF interface metric table. */
+  {OSPFIFMETRICIPADDRESS,     IPADDRESS, RONLY, ospfIfMetricEntry,
+   3, {8, 1, 1}},
+  {OSPFIFMETRICADDRESSLESSIF, INTEGER, RONLY, ospfIfMetricEntry,
+   3, {8, 1, 2}},
+  {OSPFIFMETRICTOS,           INTEGER, RONLY, ospfIfMetricEntry,
+   3, {8, 1, 3}},
+  {OSPFIFMETRICVALUE,         INTEGER, RWRITE, ospfIfMetricEntry,
+   3, {8, 1, 4}},
+  {OSPFIFMETRICSTATUS,        INTEGER, RWRITE, ospfIfMetricEntry,
+   3, {8, 1, 5}},
+
+  /* OSPF virtual interface table. */
+  {OSPFVIRTIFAREAID,          IPADDRESS, RONLY, ospfVirtIfEntry,
+   3, {9, 1, 1}},
+  {OSPFVIRTIFNEIGHBOR,        IPADDRESS, RONLY, ospfVirtIfEntry,
+   3, {9, 1, 2}},
+  {OSPFVIRTIFTRANSITDELAY,    INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 3}},
+  {OSPFVIRTIFRETRANSINTERVAL, INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 4}},
+  {OSPFVIRTIFHELLOINTERVAL,   INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 5}},
+  {OSPFVIRTIFRTRDEADINTERVAL, INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 6}},
+  {OSPFVIRTIFSTATE,           INTEGER, RONLY, ospfVirtIfEntry,
+   3, {9, 1, 7}},
+  {OSPFVIRTIFEVENTS,          COUNTER, RONLY, ospfVirtIfEntry,
+   3, {9, 1, 8}},
+  {OSPFVIRTIFAUTHKEY,         STRING,  RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 9}},
+  {OSPFVIRTIFSTATUS,          INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 10}},
+  {OSPFVIRTIFAUTHTYPE,        INTEGER, RWRITE, ospfVirtIfEntry,
+   3, {9, 1, 11}},
+
+  /* OSPF neighbor table. */
+  {OSPFNBRIPADDR,             IPADDRESS, RONLY, ospfNbrEntry,
+   3, {10, 1, 1}},
+  {OSPFNBRADDRESSLESSINDEX,   INTEGER, RONLY, ospfNbrEntry,
+   3, {10, 1, 2}},
+  {OSPFNBRRTRID,              IPADDRESS, RONLY, ospfNbrEntry,
+   3, {10, 1, 3}},
+  {OSPFNBROPTIONS,            INTEGER, RONLY, ospfNbrEntry,
+   3, {10, 1, 4}},
+  {OSPFNBRPRIORITY,           INTEGER, RWRITE, ospfNbrEntry,
+   3, {10, 1, 5}},
+  {OSPFNBRSTATE,              INTEGER, RONLY, ospfNbrEntry,
+   3, {10, 1, 6}},
+  {OSPFNBREVENTS,             COUNTER, RONLY, ospfNbrEntry,
+   3, {10, 1, 7}},
+  {OSPFNBRLSRETRANSQLEN,      GAUGE, RONLY, ospfNbrEntry,
+   3, {10, 1, 8}},
+  {OSPFNBMANBRSTATUS,         INTEGER, RWRITE, ospfNbrEntry,
+   3, {10, 1, 9}},
+  {OSPFNBMANBRPERMANENCE,     INTEGER, RONLY, ospfNbrEntry,
+   3, {10, 1, 10}},
+  {OSPFNBRHELLOSUPPRESSED,    INTEGER, RONLY, ospfNbrEntry,
+   3, {10, 1, 11}},
+
+  /* OSPF virtual neighbor table. */
+  {OSPFVIRTNBRAREA,           IPADDRESS, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 1}},
+  {OSPFVIRTNBRRTRID,          IPADDRESS, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 2}},
+  {OSPFVIRTNBRIPADDR,         IPADDRESS, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 3}},
+  {OSPFVIRTNBROPTIONS,        INTEGER, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 4}},
+  {OSPFVIRTNBRSTATE,          INTEGER, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 5}},
+  {OSPFVIRTNBREVENTS,         COUNTER, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 6}},
+  {OSPFVIRTNBRLSRETRANSQLEN,  INTEGER, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 7}},
+  {OSPFVIRTNBRHELLOSUPPRESSED, INTEGER, RONLY, ospfVirtNbrEntry,
+   3, {11, 1, 8}},
+
+  /* OSPF link state database, external. */
+  {OSPFEXTLSDBTYPE,           INTEGER, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 1}},
+  {OSPFEXTLSDBLSID,           IPADDRESS, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 2}},
+  {OSPFEXTLSDBROUTERID,       IPADDRESS, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 3}},
+  {OSPFEXTLSDBSEQUENCE,       INTEGER, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 4}},
+  {OSPFEXTLSDBAGE,            INTEGER, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 5}},
+  {OSPFEXTLSDBCHECKSUM,       INTEGER, RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 6}},
+  {OSPFEXTLSDBADVERTISEMENT,  STRING,  RONLY, ospfExtLsdbEntry,
+   3, {12, 1, 7}},
+
+  /* OSPF area aggregate table. */
+  {OSPFAREAAGGREGATEAREAID,   IPADDRESS, RONLY, ospfAreaAggregateEntry, 
+   3, {14, 1, 1}},
+  {OSPFAREAAGGREGATELSDBTYPE, INTEGER, RONLY, ospfAreaAggregateEntry, 
+   3, {14, 1, 2}},
+  {OSPFAREAAGGREGATENET,      IPADDRESS, RONLY, ospfAreaAggregateEntry, 
+   3, {14, 1, 3}},
+  {OSPFAREAAGGREGATEMASK,     IPADDRESS, RONLY, ospfAreaAggregateEntry, 
+   3, {14, 1, 4}},
+  {OSPFAREAAGGREGATESTATUS,   INTEGER, RWRITE, ospfAreaAggregateEntry,
+   3, {14, 1, 5}},
+  {OSPFAREAAGGREGATEEFFECT,   INTEGER, RWRITE, ospfAreaAggregateEntry,
+   3, {14, 1, 6}}
+};
+
+/* The administrative status of OSPF.  When OSPF is enbled on at least
+   one interface return 1. */
+static int
+ospf_admin_stat (struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  if (ospf == NULL)
+    return 0;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    if (oi && oi->address)
+      return 1;
+
+  return 0;
+}
+
+static u_char *
+ospfGeneralGroup (struct variable *v, oid *name, size_t *length,
+                 int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+
+  /* Check whether the instance identifier is valid */
+  if (smux_header_generic (v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFROUTERID:         /* 1 */
+      /* Router-ID of this OSPF instance. */
+      if (ospf)
+       return SNMP_IPADDRESS (ospf->router_id);
+      else
+       return SNMP_IPADDRESS (ospf_empty_addr);
+      break;
+    case OSPFADMINSTAT:                /* 2 */
+      /* The administrative status of OSPF in the router. */
+      if (ospf_admin_stat (ospf))
+       return SNMP_INTEGER (OSPF_STATUS_ENABLED);
+      else
+       return SNMP_INTEGER (OSPF_STATUS_DISABLED);
+      break;
+    case OSPFVERSIONNUMBER:    /* 3 */
+      /* OSPF version 2. */
+      return SNMP_INTEGER (OSPF_VERSION);
+      break;
+    case OSPFAREABDRRTRSTATUS: /* 4 */
+      /* Area Border router status. */
+      if (ospf && CHECK_FLAG (ospf->flags, OSPF_FLAG_ABR))
+       return SNMP_INTEGER (SNMP_TRUE);
+      else
+       return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    case OSPFASBDRRTRSTATUS:   /* 5 */
+      /* AS Border router status. */
+      if (ospf && CHECK_FLAG (ospf->flags, OSPF_FLAG_ASBR))
+       return SNMP_INTEGER (SNMP_TRUE);
+      else
+       return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    case OSPFEXTERNLSACOUNT:   /* 6 */
+      /* External LSA counts. */
+      if (ospf)
+       return SNMP_INTEGER (ospf_lsdb_count_all (ospf->lsdb));
+      else
+       return SNMP_INTEGER (0);
+      break;
+    case OSPFEXTERNLSACKSUMSUM:        /* 7 */
+      /* External LSA checksum. */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFTOSSUPPORT:       /* 8 */
+      /* TOS is not supported. */
+      return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    case OSPFORIGINATENEWLSAS: /* 9 */
+      /* The number of new link-state advertisements. */
+      if (ospf)
+       return SNMP_INTEGER (ospf->lsa_originate_count);
+      else
+       return SNMP_INTEGER (0);
+      break;
+    case OSPFRXNEWLSAS:                /* 10 */
+      /* The number of link-state advertisements received determined
+         to be new instantiations. */
+      if (ospf)
+       return SNMP_INTEGER (ospf->rx_lsa_count);
+      else
+       return SNMP_INTEGER (0);
+      break;
+    case OSPFEXTLSDBLIMIT:     /* 11 */
+      /* There is no limit for the number of non-default
+         AS-external-LSAs. */
+      return SNMP_INTEGER (-1);
+      break;
+    case OSPFMULTICASTEXTENSIONS: /* 12 */
+      /* Multicast Extensions to OSPF is not supported. */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFEXITOVERFLOWINTERVAL: /* 13 */
+      /* Overflow is not supported. */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFDEMANDEXTENSIONS: /* 14 */
+      /* Demand routing is not supported. */
+      return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    default:
+      return NULL;
+    }
+  return NULL;
+}
+
+static struct ospf_area *
+ospf_area_lookup_next (struct ospf *ospf, struct in_addr *area_id, int first)
+{
+  struct ospf_area *area;
+  struct listnode *node;
+
+  if (ospf == NULL)
+    return NULL;
+
+  if (first)
+    {
+      node = listhead (ospf->areas);
+      if (node)
+       {
+         area = listgetdata (node);
+         *area_id = area->area_id;
+         return area;
+       }
+      return NULL;
+    }
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (ntohl (area->area_id.s_addr) > ntohl (area_id->s_addr))
+       {
+         *area_id = area->area_id;
+         return area;
+       }
+    }
+  return NULL;
+}
+
+static struct ospf_area *
+ospfAreaLookup (struct variable *v, oid name[], size_t *length,
+               struct in_addr *addr, int exact)
+{
+  struct ospf *ospf;
+  struct ospf_area *area;
+  int len;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  if (exact)
+    {
+      /* Length is insufficient to lookup OSPF area. */
+      if (*length - v->namelen != sizeof (struct in_addr))
+       return NULL;
+
+      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+      area = ospf_area_lookup_by_area_id (ospf, *addr);
+
+      return area;
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len > 4)
+       len = 4;
+      
+      oid2in_addr (name + v->namelen, len, addr);
+
+      area = ospf_area_lookup_next (ospf, addr, len == 0 ? 1 : 0);
+
+      if (area == NULL)
+       return NULL;
+
+      oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr));
+      *length = sizeof (struct in_addr) + v->namelen;
+
+      return area;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfAreaEntry (struct variable *v, oid *name, size_t *length, int exact,
+              size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_area *area;
+  struct in_addr addr;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+
+  area = ospfAreaLookup (v, name, length, &addr, exact);
+  if (! area)
+    return NULL;
+  
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFAREAID:           /* 1 */
+      return SNMP_IPADDRESS (area->area_id);
+      break;
+    case OSPFAUTHTYPE:         /* 2 */
+      return SNMP_INTEGER (area->auth_type);
+      break;
+    case OSPFIMPORTASEXTERN:   /* 3 */
+      return SNMP_INTEGER (area->external_routing + 1);
+      break;
+    case OSPFSPFRUNS:          /* 4 */
+      return SNMP_INTEGER (area->spf_calculation);
+      break;
+    case OSPFAREABDRRTRCOUNT:  /* 5 */
+      return SNMP_INTEGER (area->abr_count);
+      break;
+    case OSPFASBDRRTRCOUNT:    /* 6 */
+      return SNMP_INTEGER (area->asbr_count);
+      break;
+    case OSPFAREALSACOUNT:     /* 7 */
+      return SNMP_INTEGER (area->lsdb->total);
+      break;
+    case OSPFAREALSACKSUMSUM:  /* 8 */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFAREASUMMARY:      /* 9 */
+#define OSPF_noAreaSummary   1
+#define OSPF_sendAreaSummary 2
+      if (area->no_summary)
+       return SNMP_INTEGER (OSPF_noAreaSummary);
+      else
+       return SNMP_INTEGER (OSPF_sendAreaSummary);
+      break;
+    case OSPFAREASTATUS:       /* 10 */
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_area *
+ospf_stub_area_lookup_next (struct in_addr *area_id, int first)
+{
+  struct ospf_area *area;
+  struct listnode *node;
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (area->external_routing == OSPF_AREA_STUB)
+       {
+         if (first)
+           {
+             *area_id = area->area_id;
+             return area;
+           }
+         else if (ntohl (area->area_id.s_addr) > ntohl (area_id->s_addr))
+           {
+             *area_id = area->area_id;
+             return area;
+           }
+       }
+    }
+  return NULL;
+}
+
+static struct ospf_area *
+ospfStubAreaLookup (struct variable *v, oid name[], size_t *length,
+                   struct in_addr *addr, int exact)
+{
+  struct ospf *ospf;
+  struct ospf_area *area;
+  int len;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  /* Exact lookup. */
+  if (exact)
+    {
+      /* ospfStubAreaID + ospfStubTOS. */
+      if (*length != v->namelen + sizeof (struct in_addr) + 1)
+       return NULL;
+
+      /* Check ospfStubTOS is zero. */
+      if (name[*length - 1] != 0)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+      area = ospf_area_lookup_by_area_id (ospf, *addr);
+
+      if (area->external_routing == OSPF_AREA_STUB)
+       return area;
+      else
+       return NULL;
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len > 4)
+       len = 4;
+      
+      oid2in_addr (name + v->namelen, len, addr);
+
+      area = ospf_stub_area_lookup_next (addr, len == 0 ? 1 : 0);
+
+      if (area == NULL)
+       return NULL;
+
+      oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr));
+      /* Set TOS 0. */
+      name[v->namelen + sizeof (struct in_addr)] = 0;
+      *length = v->namelen + sizeof (struct in_addr) + 1;
+
+      return area;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfStubAreaEntry (struct variable *v, oid *name, size_t *length,
+                  int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_area *area;
+  struct in_addr addr;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+
+  area = ospfStubAreaLookup (v, name, length, &addr, exact);
+  if (! area)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFSTUBAREAID:       /* 1 */
+      /* OSPF stub area id. */
+      return SNMP_IPADDRESS (area->area_id);
+      break;
+    case OSPFSTUBTOS:          /* 2 */
+      /* TOS value is not supported. */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFSTUBMETRIC:       /* 3 */
+      /* Default cost to stub area. */
+      return SNMP_INTEGER (area->default_cost);
+      break;
+    case OSPFSTUBSTATUS:       /* 4 */
+      /* Status of the stub area. */
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFSTUBMETRICTYPE:   /* 5 */
+      /* OSPF Metric type. */
+#define OSPF_ospfMetric     1
+#define OSPF_comparableCost 2
+#define OSPF_nonComparable  3
+      return SNMP_INTEGER (OSPF_ospfMetric);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_lsa *
+lsdb_lookup_next (struct ospf_area *area, u_char *type, int type_next,
+                 struct in_addr *ls_id, int ls_id_next,
+                 struct in_addr *router_id, int router_id_next)
+{
+  struct ospf_lsa *lsa;
+  int i;
+
+  if (type_next)
+    i = OSPF_MIN_LSA;
+  else
+    i = *type;
+
+  /* Sanity check, if LSA type unknwon
+     merley skip any LSA */
+  if ((i < OSPF_MIN_LSA) || (i >= OSPF_MAX_LSA))
+    {
+      zlog_debug("Strange request with LSA type %d\n", i);
+      return NULL;
+    }
+
+  for (; i < OSPF_MAX_LSA; i++)
+    {
+      *type = i;
+
+      lsa = ospf_lsdb_lookup_by_id_next (area->lsdb, *type, *ls_id, *router_id,
+                                       ls_id_next);
+      if (lsa)
+       return lsa;
+
+      ls_id_next = 1;
+    }
+  return NULL;
+}
+
+static struct ospf_lsa *
+ospfLsdbLookup (struct variable *v, oid *name, size_t *length,
+               struct in_addr *area_id, u_char *type,
+               struct in_addr *ls_id, struct in_addr *router_id, int exact)
+{
+  struct ospf *ospf;
+  struct ospf_area *area;
+  struct ospf_lsa *lsa;
+  int len;
+  int type_next;
+  int ls_id_next;
+  int router_id_next;
+  oid *offset;
+  int offsetlen;
+
+  ospf = ospf_lookup ();
+
+#define OSPF_LSDB_ENTRY_OFFSET \
+          (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE)
+
+  if (exact)
+    {
+      /* Area ID + Type + LS ID + Router ID. */
+      if (*length - v->namelen != OSPF_LSDB_ENTRY_OFFSET)
+       return NULL;
+      
+      /* Set OID offset for Area ID. */
+      offset = name + v->namelen;
+
+      /* Lookup area first. */
+      oid2in_addr (offset, IN_ADDR_SIZE, area_id);
+      area = ospf_area_lookup_by_area_id (ospf, *area_id);
+      if (! area)
+       return NULL;
+      offset += IN_ADDR_SIZE;
+
+      /* Type. */
+      *type = *offset;
+      offset++;
+
+      /* LS ID. */
+      oid2in_addr (offset, IN_ADDR_SIZE, ls_id);
+      offset += IN_ADDR_SIZE;
+
+      /* Router ID. */
+      oid2in_addr (offset, IN_ADDR_SIZE, router_id);
+
+      /* Lookup LSDB. */
+      return ospf_lsdb_lookup_by_id (area->lsdb, *type, *ls_id, *router_id);
+    }
+  else
+    {
+      /* Get variable length. */
+      offset = name + v->namelen;
+      offsetlen = *length - v->namelen;
+      len = offsetlen;
+
+      if (len > (int)IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+
+      oid2in_addr (offset, len, area_id);
+
+      /* First we search area. */
+      if (len == IN_ADDR_SIZE)
+       area = ospf_area_lookup_by_area_id (ospf, *area_id);
+      else
+       area = ospf_area_lookup_next (ospf, area_id, 1);
+
+      if (area == NULL)
+       return NULL;
+
+      do 
+       {
+         /* Next we lookup type. */
+         offset += len;
+         offsetlen -= len;
+         len = offsetlen;
+
+         if (len <= 0)
+           type_next = 1;
+         else
+           {
+             len = 1;
+             type_next = 0;
+             *type = *offset;
+           }
+       
+         /* LS ID. */
+         offset++;
+         offsetlen--;
+         len = offsetlen;
+
+         if (len <= 0)
+           ls_id_next = 1;
+         else
+           {
+             ls_id_next = 0;
+             if (len > (int)IN_ADDR_SIZE)
+               len = IN_ADDR_SIZE;
+
+             oid2in_addr (offset, len, ls_id);
+           }
+
+         /* Router ID. */
+         offset += IN_ADDR_SIZE;
+         offsetlen -= IN_ADDR_SIZE;
+         len = offsetlen;
+
+         if (len <= 0)
+           router_id_next = 1;
+         else
+           {
+             router_id_next = 0;
+             if (len > (int)IN_ADDR_SIZE)
+               len = IN_ADDR_SIZE;
+
+             oid2in_addr (offset, len, router_id);
+           }
+
+         lsa = lsdb_lookup_next (area, type, type_next, ls_id, ls_id_next,
+                                 router_id, router_id_next);
+
+         if (lsa)
+           {
+             /* Fill in length. */
+             *length = v->namelen + OSPF_LSDB_ENTRY_OFFSET;
+
+             /* Fill in value. */
+             offset = name + v->namelen;
+             oid_copy_addr (offset, area_id, IN_ADDR_SIZE);
+             offset += IN_ADDR_SIZE;
+             *offset = lsa->data->type;
+             offset++;
+             oid_copy_addr (offset, &lsa->data->id, IN_ADDR_SIZE);
+             offset += IN_ADDR_SIZE;
+             oid_copy_addr (offset, &lsa->data->adv_router, IN_ADDR_SIZE);
+           
+             return lsa;
+           }
+       }
+      while ((area = ospf_area_lookup_next (ospf, area_id, 0)) != NULL);
+    }
+  return NULL;
+}
+
+static u_char *
+ospfLsdbEntry (struct variable *v, oid *name, size_t *length, int exact,
+              size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_lsa *lsa;
+  struct lsa_header *lsah;
+  struct in_addr area_id;
+  u_char type;
+  struct in_addr ls_id;
+  struct in_addr router_id;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* INDEX { ospfLsdbAreaId, ospfLsdbType,
+     ospfLsdbLsid, ospfLsdbRouterId } */
+
+  memset (&area_id, 0, sizeof (struct in_addr));
+  type = 0;
+  memset (&ls_id, 0, sizeof (struct in_addr));
+  memset (&router_id, 0, sizeof (struct in_addr));
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  lsa = ospfLsdbLookup (v, name, length, &area_id, &type, &ls_id, &router_id,
+                       exact);
+  if (! lsa)
+    return NULL;
+
+  lsah = lsa->data;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFLSDBAREAID:       /* 1 */
+      return SNMP_IPADDRESS (lsa->area->area_id);
+      break;
+    case OSPFLSDBTYPE:         /* 2 */
+      return SNMP_INTEGER (lsah->type);
+      break;
+    case OSPFLSDBLSID:         /* 3 */
+      return SNMP_IPADDRESS (lsah->id);
+      break;
+    case OSPFLSDBROUTERID:     /* 4 */
+      return SNMP_IPADDRESS (lsah->adv_router);
+      break;
+    case OSPFLSDBSEQUENCE:     /* 5 */
+      return SNMP_INTEGER (lsah->ls_seqnum);
+      break;
+    case OSPFLSDBAGE:          /* 6 */
+      return SNMP_INTEGER (lsah->ls_age);
+      break;
+    case OSPFLSDBCHECKSUM:     /* 7 */
+      return SNMP_INTEGER (lsah->checksum);
+      break;
+    case OSPFLSDBADVERTISEMENT:        /* 8 */
+      *var_len = ntohs (lsah->length);
+      return (u_char *) lsah;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_area_range *
+ospfAreaRangeLookup (struct variable *v, oid *name, size_t *length,
+                    struct in_addr *area_id, struct in_addr *range_net,
+                    int exact)
+{
+  oid *offset;
+  int offsetlen;
+  int len;
+  struct ospf *ospf;
+  struct ospf_area *area;
+  struct ospf_area_range *range;
+  struct prefix_ipv4 p;
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  ospf = ospf_lookup ();
+
+  if (exact) 
+    {
+      /* Area ID + Range Network. */
+      if (v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE != *length)
+       return NULL;
+
+      /* Set OID offset for Area ID. */
+      offset = name + v->namelen;
+
+      /* Lookup area first. */
+      oid2in_addr (offset, IN_ADDR_SIZE, area_id);
+
+      area = ospf_area_lookup_by_area_id (ospf, *area_id);
+      if (! area)
+       return NULL;
+
+      offset += IN_ADDR_SIZE;
+
+      /* Lookup area range. */
+      oid2in_addr (offset, IN_ADDR_SIZE, range_net);
+      p.prefix = *range_net;
+
+      return ospf_area_range_lookup (area, &p);
+    }
+  else
+    {
+      /* Set OID offset for Area ID. */
+      offset = name + v->namelen;
+      offsetlen = *length - v->namelen;
+
+      len = offsetlen;
+      if (len > (int)IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+
+      oid2in_addr (offset, len, area_id);
+
+      /* First we search area. */
+      if (len == IN_ADDR_SIZE)
+       area = ospf_area_lookup_by_area_id (ospf,*area_id);
+      else
+       area = ospf_area_lookup_next (ospf, area_id, len == 0 ? 1 : 0);
+
+      if (area == NULL)
+       return NULL;
+
+      do 
+       {
+         offset += IN_ADDR_SIZE;
+         offsetlen -= IN_ADDR_SIZE;
+         len = offsetlen;
+
+         if (len < 0)
+           len = 0;
+         if (len > (int)IN_ADDR_SIZE)
+           len = IN_ADDR_SIZE;
+
+         oid2in_addr (offset, len, range_net);
+
+         range = ospf_area_range_lookup_next (area, range_net,
+                                              len == 0 ? 1 : 0);
+
+         if (range)
+           {
+             /* Fill in length. */
+             *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE;
+
+             /* Fill in value. */
+             offset = name + v->namelen;
+             oid_copy_addr (offset, area_id, IN_ADDR_SIZE);
+             offset += IN_ADDR_SIZE;
+             oid_copy_addr (offset, range_net, IN_ADDR_SIZE);
+
+             return range;
+           }
+       }
+      while ((area = ospf_area_lookup_next (ospf, area_id, 0)) != NULL);
+    }
+  return NULL;
+}
+
+static u_char *
+ospfAreaRangeEntry (struct variable *v, oid *name, size_t *length, int exact,
+                   size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_area_range *range;
+  struct in_addr area_id;
+  struct in_addr range_net;
+  struct in_addr mask;
+  struct ospf *ospf;
+  
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  memset (&area_id, 0, IN_ADDR_SIZE);
+  memset (&range_net, 0, IN_ADDR_SIZE);
+
+  range = ospfAreaRangeLookup (v, name, length, &area_id, &range_net, exact);
+  if (! range)
+    return NULL;
+
+  /* Convert prefixlen to network mask format. */
+  masklen2ip (range->subst_masklen, &mask);
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFAREARANGEAREAID:  /* 1 */
+      return SNMP_IPADDRESS (area_id);
+      break;
+    case OSPFAREARANGENET:     /* 2 */
+      return SNMP_IPADDRESS (range_net);
+      break;
+    case OSPFAREARANGEMASK:    /* 3 */
+      return SNMP_IPADDRESS (mask);
+      break;
+    case OSPFAREARANGESTATUS:  /* 4 */
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFAREARANGEEFFECT:  /* 5 */
+#define OSPF_advertiseMatching      1
+#define OSPF_doNotAdvertiseMatching 2
+      return SNMP_INTEGER (OSPF_advertiseMatching);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_nbr_nbma *
+ospfHostLookup (struct variable *v, oid *name, size_t *length,
+               struct in_addr *addr, int exact)
+{
+  int len;
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  if (exact)
+    {
+      /* INDEX { ospfHostIpAddress, ospfHostTOS } */
+      if (*length != v->namelen + IN_ADDR_SIZE + 1)
+       return NULL;
+
+      /* Check ospfHostTOS. */
+      if (name[*length - 1] != 0)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, IN_ADDR_SIZE, addr);
+
+      nbr_nbma = ospf_nbr_nbma_lookup (ospf, *addr);
+
+      return nbr_nbma;
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len > 4)
+       len = 4;
+      
+      oid2in_addr (name + v->namelen, len, addr);
+
+      nbr_nbma = ospf_nbr_nbma_lookup_next (ospf, addr, len == 0 ? 1 : 0);
+
+      if (nbr_nbma == NULL)
+       return NULL;
+
+      oid_copy_addr (name + v->namelen, addr, IN_ADDR_SIZE);
+
+      /* Set TOS 0. */
+      name[v->namelen + IN_ADDR_SIZE] = 0;
+
+      *length = v->namelen + IN_ADDR_SIZE + 1;
+
+      return nbr_nbma;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact,
+              size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct ospf_interface *oi;
+  struct in_addr addr;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+
+  nbr_nbma = ospfHostLookup (v, name, length, &addr, exact);
+  if (nbr_nbma == NULL)
+    return NULL;
+
+  oi = nbr_nbma->oi;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFHOSTIPADDRESS:    /* 1 */
+      return SNMP_IPADDRESS (nbr_nbma->addr);
+      break;
+    case OSPFHOSTTOS:          /* 2 */
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFHOSTMETRIC:       /* 3 */
+      if (oi)
+       return SNMP_INTEGER (oi->output_cost);
+      else
+       return SNMP_INTEGER (1);
+      break;
+    case OSPFHOSTSTATUS:       /* 4 */
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFHOSTAREAID:       /* 5 */
+      if (oi && oi->area)
+       return SNMP_IPADDRESS (oi->area->area_id);
+      else
+       return SNMP_IPADDRESS (ospf_empty_addr);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+struct list *ospf_snmp_iflist;
+
+struct ospf_snmp_if
+{
+  struct in_addr addr;
+  ifindex_t ifindex;
+  struct interface *ifp;
+};
+
+static struct ospf_snmp_if *
+ospf_snmp_if_new (void)
+{
+  return XCALLOC (MTYPE_TMP, sizeof (struct ospf_snmp_if));
+}
+
+static void
+ospf_snmp_if_free (struct ospf_snmp_if *osif)
+{
+  XFREE (MTYPE_TMP, osif);
+}
+
+void
+ospf_snmp_if_delete (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct ospf_snmp_if *osif;
+
+  for (ALL_LIST_ELEMENTS (ospf_snmp_iflist, node, nnode, osif))
+    {
+      if (osif->ifp == ifp)
+       {
+         list_delete_node (ospf_snmp_iflist, node);
+         ospf_snmp_if_free (osif);
+         return;
+       }
+    }
+}
+
+void
+ospf_snmp_if_update (struct interface *ifp)
+{
+  struct listnode *node;
+  struct listnode *pn;
+  struct connected *ifc;
+  struct prefix *p;
+  struct ospf_snmp_if *osif;
+  struct in_addr *addr;
+  ifindex_t ifindex;
+
+  ospf_snmp_if_delete (ifp);
+
+  p = NULL;
+  addr = NULL;
+  ifindex = 0;
+
+  /* Lookup first IPv4 address entry. */
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc))
+    {
+      p = CONNECTED_ID(ifc);
+
+      if (p->family == AF_INET)
+       {
+         addr = &p->u.prefix4;
+         break;
+       }
+    }
+  if (! addr)
+    ifindex = ifp->ifindex;
+
+  /* Add interface to the list. */
+  pn = NULL;
+  for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, node, osif))
+    {
+      if (addr)
+       {
+         /* Usual interfaces --> Sort them based on interface IPv4 addresses */
+         if (ntohl (osif->addr.s_addr) > ntohl (addr->s_addr))
+           break;
+       }
+      else
+       {
+         /* Unnumbered interfaces --> Sort them based on interface indexes */
+         if (osif->addr.s_addr != 0 || osif->ifindex > ifindex)
+           break;
+       }
+      pn = node;
+    }
+
+  osif = ospf_snmp_if_new ();
+  if (addr) /* Usual interface */
+  {
+    osif->addr = *addr;
+    
+    /* This field is used for storing ospfAddressLessIf OID value,
+     * conform to RFC1850 OSPF-MIB specification, it must be 0 for
+     * usual interface */
+    osif->ifindex = 0;
+  }
+  else  /* Unnumbered interface */
+    osif->ifindex = ifindex;
+  osif->ifp = ifp;
+
+  listnode_add_after (ospf_snmp_iflist, pn, osif);
+}
+
+static int
+ospf_snmp_is_if_have_addr (struct interface *ifp)
+{
+  struct listnode *nn;
+  struct connected *ifc;
+
+  /* Is this interface having any connected IPv4 address ? */
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, ifc))
+  {
+    if (CONNECTED_PREFIX(ifc)->family == AF_INET)
+      return 1;
+  }
+  
+  return 0;
+}
+
+static struct ospf_interface *
+ospf_snmp_if_lookup (struct in_addr *ifaddr, ifindex_t *ifindex)
+{
+  struct listnode *node;
+  struct ospf_snmp_if *osif;
+  struct ospf_interface *oi = NULL;
+  struct ospf *ospf = ospf_lookup ();
+
+  for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, node, osif))
+    {  
+      if (ifaddr->s_addr)
+        {
+             if (IPV4_ADDR_SAME (&osif->addr, ifaddr))
+            oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr);
+        }
+      else
+        {
+             if (osif->ifindex == *ifindex)
+            oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr);
+        }
+    }
+  return oi;
+}
+
+static struct ospf_interface *
+ospf_snmp_if_lookup_next (struct in_addr *ifaddr, ifindex_t *ifindex,
+                         int ifaddr_next, ifindex_t ifindex_next)
+{
+  struct ospf_snmp_if *osif;
+  struct listnode *nn;
+  struct ospf *ospf = ospf_lookup ();
+  struct ospf_interface *oi = NULL;
+
+  if (ospf == NULL)
+    return NULL;
+
+  /* No instance is specified --> Return the first OSPF interface */
+  if (ifaddr_next)
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, nn, osif))
+       {
+         osif = listgetdata (nn);
+         *ifaddr = osif->addr;
+         *ifindex = osif->ifindex;
+          /* Because no instance is specified, we don't care about the kind of 
+           * interface (usual or unnumbered), just returning the first valid 
+           * OSPF interface */
+          oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr);
+          if (oi)
+            return (oi);
+       }
+      return NULL;
+    }
+
+  /* An instance is specified --> Return the next OSPF interface */
+  for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, nn, osif))
+    {
+      /* Usual interface */
+      if (ifaddr->s_addr) 
+       {
+         /* The interface must have valid AF_INET connected address */
+         /* it must have lager IPv4 address value than the lookup entry */
+         if ((ospf_snmp_is_if_have_addr(osif->ifp)) &&
+             (ntohl (osif->addr.s_addr) > ntohl (ifaddr->s_addr)))
+           {
+             *ifaddr = osif->addr;
+             *ifindex = osif->ifindex;
+        
+             /* and it must be an OSPF interface */
+             oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr);
+             if (oi)
+               return oi;
+           }
+       }
+      /* Unnumbered interface */
+      else  
+        /* The interface must NOT have valid AF_INET connected address */
+        /* it must have lager interface index than the lookup entry */
+        if ((!ospf_snmp_is_if_have_addr(osif->ifp)) &&
+            (osif->ifindex > *ifindex))
+          {
+            *ifaddr = osif->addr;
+            *ifindex = osif->ifindex;
+        
+            /* and it must be an OSPF interface */
+            oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr);
+            if (oi)
+              return oi;
+          }
+    }
+  return NULL;
+}
+
+static int
+ospf_snmp_iftype (struct interface *ifp)
+{
+#define ospf_snmp_iftype_broadcast         1
+#define ospf_snmp_iftype_nbma              2
+#define ospf_snmp_iftype_pointToPoint      3
+#define ospf_snmp_iftype_pointToMultipoint 5
+  if (if_is_broadcast (ifp))
+    return ospf_snmp_iftype_broadcast;
+  if (if_is_pointopoint (ifp))
+    return ospf_snmp_iftype_pointToPoint;
+  return ospf_snmp_iftype_broadcast;
+}
+
+static struct ospf_interface *
+ospfIfLookup (struct variable *v, oid *name, size_t *length,
+             struct in_addr *ifaddr, ifindex_t *ifindex, int exact)
+{
+  unsigned int len;
+  int ifaddr_next = 0;
+  ifindex_t ifindex_next = 0;
+  struct ospf_interface *oi;
+  oid *offset;
+
+  if (exact)
+    {
+      if (*length != v->namelen + IN_ADDR_SIZE + 1)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, IN_ADDR_SIZE, ifaddr);
+      *ifindex = name[v->namelen + IN_ADDR_SIZE];
+
+      return ospf_snmp_if_lookup (ifaddr, ifindex);
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len >= IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+      if (len <= 0)
+       ifaddr_next = 1;
+
+      oid2in_addr (name + v->namelen, len, ifaddr);
+
+      len = *length - v->namelen - IN_ADDR_SIZE;
+      if (len >= 1)
+       len = 1;
+      else
+       ifindex_next = 1;
+
+      if (len == 1)
+       *ifindex = name[v->namelen + IN_ADDR_SIZE];
+
+      oi = ospf_snmp_if_lookup_next (ifaddr, ifindex, ifaddr_next,
+                                     ifindex_next);
+      if (oi)
+       {
+         *length = v->namelen + IN_ADDR_SIZE + 1;
+         offset = name + v->namelen;
+         oid_copy_addr (offset, ifaddr, IN_ADDR_SIZE);
+         offset += IN_ADDR_SIZE;
+         *offset = *ifindex;
+         return oi;
+       }
+    }
+  return NULL;
+}
+
+static u_char *
+ospfIfEntry (struct variable *v, oid *name, size_t *length, int exact,
+            size_t *var_len, WriteMethod **write_method)
+{
+  ifindex_t ifindex;
+  struct in_addr ifaddr;
+  struct ospf_interface *oi;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  ifindex = 0;
+  memset (&ifaddr, 0, sizeof (struct in_addr));
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  oi = ospfIfLookup (v, name, length, &ifaddr, &ifindex, exact);
+  if (oi == NULL)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFIFIPADDRESS:      /* 1 */
+      return SNMP_IPADDRESS (ifaddr);
+      break;
+    case OSPFADDRESSLESSIF:    /* 2 */
+      return SNMP_INTEGER (ifindex);
+      break;
+    case OSPFIFAREAID:         /* 3 */
+      if (oi->area)
+       return SNMP_IPADDRESS (oi->area->area_id);
+      else
+       return SNMP_IPADDRESS (ospf_empty_addr);
+      break;
+    case OSPFIFTYPE:           /* 4 */
+      return SNMP_INTEGER (ospf_snmp_iftype (oi->ifp));
+      break;
+    case OSPFIFADMINSTAT:      /* 5 */
+      if (oi)
+       return SNMP_INTEGER (OSPF_STATUS_ENABLED);
+      else
+       return SNMP_INTEGER (OSPF_STATUS_DISABLED);
+      break;
+    case OSPFIFRTRPRIORITY:    /* 6 */
+      return SNMP_INTEGER (PRIORITY (oi));
+      break;
+    case OSPFIFTRANSITDELAY:   /* 7 */
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, transmit_delay));
+      break;
+    case OSPFIFRETRANSINTERVAL:        /* 8 */
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, retransmit_interval));
+      break;
+    case OSPFIFHELLOINTERVAL:  /* 9 */
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_hello));
+      break;
+    case OSPFIFRTRDEADINTERVAL:        /* 10 */
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_wait));
+      break;
+    case OSPFIFPOLLINTERVAL:   /* 11 */
+      return SNMP_INTEGER (OSPF_POLL_INTERVAL_DEFAULT);
+      break;
+    case OSPFIFSTATE:          /* 12 */
+      return SNMP_INTEGER (ISM_SNMP(oi->state));
+      break;
+    case OSPFIFDESIGNATEDROUTER: /* 13 */
+      return SNMP_IPADDRESS (DR (oi));
+      break;
+    case OSPFIFBACKUPDESIGNATEDROUTER: /* 14 */
+      return SNMP_IPADDRESS (BDR (oi));
+      break;
+    case OSPFIFEVENTS:         /* 15 */
+      return SNMP_INTEGER (oi->state_change);
+      break;
+    case OSPFIFAUTHKEY:                /* 16 */
+      *var_len = 0;
+      return (u_char *) OSPF_IF_PARAM (oi, auth_simple);
+      break;
+    case OSPFIFSTATUS:         /* 17 */
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFIFMULTICASTFORWARDING: /* 18 */
+#define ospf_snmp_multiforward_blocked    1
+#define ospf_snmp_multiforward_multicast  2
+#define ospf_snmp_multiforward_unicast    3
+      return SNMP_INTEGER (ospf_snmp_multiforward_blocked);
+      break;
+    case OSPFIFDEMAND:         /* 19 */
+      return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    case OSPFIFAUTHTYPE:       /* 20 */
+      if (oi->area)
+       return SNMP_INTEGER (oi->area->auth_type);
+      else
+       return SNMP_INTEGER (0);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+#define OSPF_SNMP_METRIC_VALUE 1
+
+static struct ospf_interface *
+ospfIfMetricLookup (struct variable *v, oid *name, size_t *length,
+                   struct in_addr *ifaddr, ifindex_t *ifindex, int exact)
+{
+  unsigned int len;
+  int ifaddr_next = 0;
+  ifindex_t ifindex_next = 0;
+  struct ospf_interface *oi;
+  oid *offset;
+  int metric;
+
+  if (exact)
+    {
+      if (*length != v->namelen + IN_ADDR_SIZE + 1 + 1)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, IN_ADDR_SIZE, ifaddr);
+      *ifindex = name[v->namelen + IN_ADDR_SIZE];
+      metric = name[v->namelen + IN_ADDR_SIZE + 1];
+
+      if (metric != OSPF_SNMP_METRIC_VALUE)
+       return NULL;
+
+      return ospf_snmp_if_lookup (ifaddr, ifindex);
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len >= IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+      else
+       ifaddr_next = 1;
+
+      oid2in_addr (name + v->namelen, len, ifaddr);
+
+      len = *length - v->namelen - IN_ADDR_SIZE;
+      if (len >= 1)
+       len = 1;
+      else
+       ifindex_next = 1;
+
+      if (len == 1)
+       *ifindex = name[v->namelen + IN_ADDR_SIZE];
+
+      oi = ospf_snmp_if_lookup_next (ifaddr, ifindex, ifaddr_next,
+                                     ifindex_next);
+      if (oi)
+       {
+         *length = v->namelen + IN_ADDR_SIZE + 1 + 1;
+         offset = name + v->namelen;
+         oid_copy_addr (offset, ifaddr, IN_ADDR_SIZE);
+         offset += IN_ADDR_SIZE;
+         *offset = *ifindex;
+         offset++;
+         *offset = OSPF_SNMP_METRIC_VALUE;
+         return oi;
+       }
+    }
+  return NULL;
+}
+
+static u_char *
+ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact,
+                  size_t *var_len, WriteMethod **write_method)
+{
+  /* Currently we support metric 1 only. */
+  ifindex_t ifindex;
+  struct in_addr ifaddr;
+  struct ospf_interface *oi;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  ifindex = 0;
+  memset (&ifaddr, 0, sizeof (struct in_addr));
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  oi = ospfIfMetricLookup (v, name, length, &ifaddr, &ifindex, exact);
+  if (oi == NULL)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFIFMETRICIPADDRESS:
+      return SNMP_IPADDRESS (ifaddr);
+      break;
+    case OSPFIFMETRICADDRESSLESSIF:
+      return SNMP_INTEGER (ifindex);
+      break;
+    case OSPFIFMETRICTOS:
+      return SNMP_INTEGER (0);
+      break;
+    case OSPFIFMETRICVALUE:
+      return SNMP_INTEGER (OSPF_SNMP_METRIC_VALUE);
+      break;
+    case OSPFIFMETRICSTATUS:
+      return SNMP_INTEGER (1);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+struct route_table *ospf_snmp_vl_table;
+
+void
+ospf_snmp_vl_add (struct ospf_vl_data *vl_data)
+{
+  struct prefix_ls lp;
+  struct route_node *rn;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = vl_data->vl_area_id;
+  lp.adv_router = vl_data->vl_peer;
+
+  rn = route_node_get (ospf_snmp_vl_table, (struct prefix *) &lp);
+  if (rn->info)
+    route_unlock_node (rn);
+
+  rn->info = vl_data;
+}
+
+void
+ospf_snmp_vl_delete (struct ospf_vl_data *vl_data)
+{
+  struct prefix_ls lp;
+  struct route_node *rn;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = vl_data->vl_area_id;
+  lp.adv_router = vl_data->vl_peer;
+
+  rn = route_node_lookup (ospf_snmp_vl_table, (struct prefix *) &lp);
+  if (! rn)
+    return;
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+}
+
+static struct ospf_vl_data *
+ospf_snmp_vl_lookup (struct in_addr *area_id, struct in_addr *neighbor)
+{
+  struct prefix_ls lp;
+  struct route_node *rn;
+  struct ospf_vl_data *vl_data;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = *area_id;
+  lp.adv_router = *neighbor;
+
+  rn = route_node_lookup (ospf_snmp_vl_table, (struct prefix *) &lp);
+  if (rn)
+    {
+      vl_data = rn->info;
+      route_unlock_node (rn);
+      return vl_data;
+    }
+  return NULL;
+}
+
+static struct ospf_vl_data *
+ospf_snmp_vl_lookup_next (struct in_addr *area_id, struct in_addr *neighbor,
+                         int first)
+{
+  struct prefix_ls lp;
+  struct route_node *rn;
+  struct ospf_vl_data *vl_data;
+
+  memset (&lp, 0, sizeof (struct prefix_ls));
+  lp.family = 0;
+  lp.prefixlen = 64;
+  lp.id = *area_id;
+  lp.adv_router = *neighbor;
+
+  if (first)
+    rn = route_top (ospf_snmp_vl_table);
+  else
+    {
+      rn = route_node_get (ospf_snmp_vl_table, (struct prefix *) &lp);
+      rn = route_next (rn);
+    }
+
+  for (; rn; rn = route_next (rn))
+    if (rn->info)
+      break;
+
+  if (rn && rn->info)
+    {
+      vl_data = rn->info;
+      *area_id = vl_data->vl_area_id;
+      *neighbor = vl_data->vl_peer;
+      route_unlock_node (rn);
+      return vl_data;
+    }
+  return NULL;
+}
+
+static struct ospf_vl_data *
+ospfVirtIfLookup (struct variable *v, oid *name, size_t *length,
+                 struct in_addr *area_id, struct in_addr *neighbor, int exact)
+{
+  int first;
+  unsigned int len;
+  struct ospf_vl_data *vl_data;
+
+  if (exact)
+    {
+      if (*length != v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, IN_ADDR_SIZE, area_id);
+      oid2in_addr (name + v->namelen + IN_ADDR_SIZE, IN_ADDR_SIZE, neighbor);
+
+      return ospf_snmp_vl_lookup (area_id, neighbor);
+    }
+  else
+    {
+      first = 0;
+
+      len = *length - v->namelen;
+      if (len <= 0)
+       first = 1;
+      if (len > IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+      oid2in_addr (name + v->namelen, len, area_id);
+
+      len = *length - v->namelen - IN_ADDR_SIZE;
+      if (len > IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+      oid2in_addr (name + v->namelen + IN_ADDR_SIZE, len, neighbor);
+
+      vl_data = ospf_snmp_vl_lookup_next (area_id, neighbor, first);
+
+      if (vl_data)
+       {
+         *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE;
+         oid_copy_addr (name + v->namelen, area_id, IN_ADDR_SIZE);
+         oid_copy_addr (name + v->namelen + IN_ADDR_SIZE, neighbor,
+                        IN_ADDR_SIZE);
+         return vl_data;
+       }
+    }
+  return NULL;
+}
+
+static u_char *
+ospfVirtIfEntry (struct variable *v, oid *name, size_t *length, int exact,
+                size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_vl_data *vl_data;
+  struct ospf_interface *oi;
+  struct in_addr area_id;
+  struct in_addr neighbor;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&area_id, 0, sizeof (struct in_addr));
+  memset (&neighbor, 0, sizeof (struct in_addr));
+
+  vl_data = ospfVirtIfLookup (v, name, length, &area_id, &neighbor, exact);
+  if (! vl_data)
+    return NULL;
+  oi = vl_data->vl_oi;
+  if (! oi)
+    return NULL;
+  
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFVIRTIFAREAID:
+      return SNMP_IPADDRESS (area_id);
+      break;
+    case OSPFVIRTIFNEIGHBOR:
+      return SNMP_IPADDRESS (neighbor);
+      break;
+    case OSPFVIRTIFTRANSITDELAY:
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, transmit_delay));
+      break;
+    case OSPFVIRTIFRETRANSINTERVAL:
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, retransmit_interval));
+      break;
+    case OSPFVIRTIFHELLOINTERVAL:
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_hello));
+      break;
+    case OSPFVIRTIFRTRDEADINTERVAL:
+      return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_wait));
+      break;
+    case OSPFVIRTIFSTATE:
+      return SNMP_INTEGER (oi->state);
+      break;
+    case OSPFVIRTIFEVENTS:
+      return SNMP_INTEGER (oi->state_change);
+      break;
+    case OSPFVIRTIFAUTHKEY:
+      *var_len = 0;
+      return (u_char *) OSPF_IF_PARAM (oi, auth_simple);
+      break;
+    case OSPFVIRTIFSTATUS:
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFVIRTIFAUTHTYPE:
+      if (oi->area)
+       return SNMP_INTEGER (oi->area->auth_type);
+      else
+       return SNMP_INTEGER (0);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_neighbor *
+ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr,
+                     ifindex_t *ifindex)
+{
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    {
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info) != NULL
+           && nbr != oi->nbr_self
+/* If EXACT match is needed, provide ALL entry found
+           && nbr->state != NSM_Down
+ */
+           && nbr->src.s_addr != 0)
+         {
+           if (IPV4_ADDR_SAME (&nbr->src, nbr_addr))
+             {
+               route_unlock_node (rn);
+               return nbr;
+             }
+         }
+    }
+  return NULL;
+}
+
+static struct ospf_neighbor *
+ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, ifindex_t *ifindex,
+                          int first)
+{
+  struct listnode *nn;
+  struct ospf_interface *oi;
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+  struct ospf_neighbor *min = NULL;
+  struct ospf *ospf = ospf;
+
+  ospf = ospf_lookup ();
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, nn, oi))
+    {
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info) != NULL
+           && nbr != oi->nbr_self
+           && nbr->state != NSM_Down
+           && nbr->src.s_addr != 0)
+         {
+           if (first)
+             {
+               if (! min)
+                 min = nbr;
+               else if (ntohl (nbr->src.s_addr) < ntohl (min->src.s_addr))
+                 min = nbr;
+             }
+           else if (ntohl (nbr->src.s_addr) > ntohl (nbr_addr->s_addr))
+             {
+               if (! min)
+                 min = nbr;
+               else if (ntohl (nbr->src.s_addr) < ntohl (min->src.s_addr))
+                 min = nbr;
+             }
+         }
+    }
+  if (min)
+    {
+      *nbr_addr = min->src;
+      *ifindex = 0;
+      return min;
+    }
+  return NULL;
+}
+
+static struct ospf_neighbor *
+ospfNbrLookup (struct variable *v, oid *name, size_t *length,
+              struct in_addr *nbr_addr, ifindex_t *ifindex, int exact)
+{
+  unsigned int len;
+  int first;
+  struct ospf_neighbor *nbr;
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+
+  if (! ospf)
+    return NULL;
+
+  if (exact)
+    {
+      if (*length != v->namelen + IN_ADDR_SIZE + 1)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, IN_ADDR_SIZE, nbr_addr);
+      *ifindex = name[v->namelen + IN_ADDR_SIZE];
+
+      return ospf_snmp_nbr_lookup (ospf, nbr_addr, ifindex);
+    }
+  else
+    {
+      first = 0;
+      len = *length - v->namelen;
+
+      if (len <= 0)
+       first = 1;
+
+      if (len > IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+
+      oid2in_addr (name + v->namelen, len, nbr_addr);
+
+      len = *length - v->namelen - IN_ADDR_SIZE;
+      if (len >= 1)
+       *ifindex = name[v->namelen + IN_ADDR_SIZE];
+      
+      nbr = ospf_snmp_nbr_lookup_next (nbr_addr, ifindex, first);
+
+      if (nbr)
+       {
+         *length = v->namelen + IN_ADDR_SIZE + 1;
+         oid_copy_addr (name + v->namelen, nbr_addr, IN_ADDR_SIZE);
+         name[v->namelen + IN_ADDR_SIZE] = *ifindex;
+         return nbr;
+       }
+    }
+  return NULL;
+}
+
+/* map internal quagga neighbor states to official MIB values:
+
+ospfNbrState OBJECT-TYPE
+        SYNTAX   INTEGER    {
+                    down (1),
+                    attempt (2),
+                    init (3),
+                    twoWay (4),
+                    exchangeStart (5),
+                    exchange (6),
+                    loading (7),
+                    full (8)
+                  }
+*/
+static int32_t
+ospf_snmp_neighbor_state(u_char nst)
+{
+  switch (nst)
+    {
+    case NSM_Attempt:
+      return 2;
+    case NSM_Init:
+      return 3;
+    case NSM_TwoWay:
+      return 4;
+    case NSM_ExStart:
+      return 5;
+    case NSM_Exchange:
+      return 6;
+    case NSM_Loading:
+      return 7;
+    case NSM_Full:
+      return 8;
+    default:
+      return 1; /* down */
+    }
+}
+
+static u_char *
+ospfNbrEntry (struct variable *v, oid *name, size_t *length, int exact,
+             size_t *var_len, WriteMethod **write_method)
+{
+  struct in_addr nbr_addr;
+  ifindex_t ifindex;
+  struct ospf_neighbor *nbr;
+  struct ospf_interface *oi;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&nbr_addr, 0, sizeof (struct in_addr));
+  ifindex = 0;
+  
+  nbr = ospfNbrLookup (v, name, length, &nbr_addr, &ifindex, exact);
+  if (! nbr)
+    return NULL;
+  oi = nbr->oi;
+  if (! oi)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFNBRIPADDR:
+      return SNMP_IPADDRESS (nbr_addr);
+      break;
+    case OSPFNBRADDRESSLESSINDEX:
+      return SNMP_INTEGER (ifindex);
+      break;
+    case OSPFNBRRTRID:
+      return SNMP_IPADDRESS (nbr->router_id);
+      break;
+    case OSPFNBROPTIONS:
+      return SNMP_INTEGER (oi->nbr_self->options);
+      break;
+    case OSPFNBRPRIORITY:
+      return SNMP_INTEGER (nbr->priority);
+      break;
+    case OSPFNBRSTATE:
+      return SNMP_INTEGER (ospf_snmp_neighbor_state(nbr->state));
+      break;
+    case OSPFNBREVENTS:
+      return SNMP_INTEGER (nbr->state_change);
+      break;
+    case OSPFNBRLSRETRANSQLEN:
+      return SNMP_INTEGER (ospf_ls_retransmit_count (nbr));
+      break;
+    case OSPFNBMANBRSTATUS:
+      return SNMP_INTEGER (SNMP_VALID);
+      break;
+    case OSPFNBMANBRPERMANENCE:
+      return SNMP_INTEGER (2);
+      break;
+    case OSPFNBRHELLOSUPPRESSED:
+      return SNMP_INTEGER (SNMP_FALSE);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact,
+                 size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_vl_data *vl_data;
+  struct in_addr area_id;
+  struct in_addr neighbor;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&area_id, 0, sizeof (struct in_addr));
+  memset (&neighbor, 0, sizeof (struct in_addr));
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  vl_data = ospfVirtIfLookup (v, name, length, &area_id, &neighbor, exact);
+  if (! vl_data)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFVIRTNBRAREA:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBRRTRID:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBRIPADDR:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBROPTIONS:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBRSTATE:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBREVENTS:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBRLSRETRANSQLEN:
+      return (u_char *) NULL;
+      break;
+    case OSPFVIRTNBRHELLOSUPPRESSED:
+      return (u_char *) NULL;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static struct ospf_lsa *
+ospfExtLsdbLookup (struct variable *v, oid *name, size_t *length, u_char *type,
+                  struct in_addr *ls_id, struct in_addr *router_id, int exact)
+{
+  int first;
+  oid *offset;
+  int offsetlen;
+  u_char lsa_type;
+  unsigned int len;
+  struct ospf_lsa *lsa;
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (exact)
+    {
+      if (*length != v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE)
+       return NULL;
+      
+      offset = name + v->namelen;
+
+      /* Make it sure given value match to type. */
+      lsa_type = *offset;
+      offset++;
+
+      if (lsa_type != *type)
+       return NULL;
+      
+      /* LS ID. */
+      oid2in_addr (offset, IN_ADDR_SIZE, ls_id);
+      offset += IN_ADDR_SIZE;
+
+      /* Router ID. */
+      oid2in_addr (offset, IN_ADDR_SIZE, router_id);
+
+      return ospf_lsdb_lookup_by_id (ospf->lsdb, *type, *ls_id, *router_id);
+    }
+  else
+    {
+      /* Get variable length. */
+      first = 0;
+      offset = name + v->namelen;
+      offsetlen = *length - v->namelen;
+
+      /* LSA type value. */
+      lsa_type = *offset;
+      offset++;
+      offsetlen--;
+
+      if (offsetlen <= 0 || lsa_type < OSPF_AS_EXTERNAL_LSA)
+       first = 1;
+
+      /* LS ID. */
+      len = offsetlen;
+      if (len > IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+
+      oid2in_addr (offset, len, ls_id);
+
+      offset += IN_ADDR_SIZE;
+      offsetlen -= IN_ADDR_SIZE;
+
+      /* Router ID. */
+      len = offsetlen;
+      if (len > IN_ADDR_SIZE)
+       len = IN_ADDR_SIZE;
+
+      oid2in_addr (offset, len, router_id);
+
+      lsa = ospf_lsdb_lookup_by_id_next (ospf->lsdb, *type, *ls_id,
+                                       *router_id, first);
+
+      if (lsa)
+       {
+         /* Fill in length. */
+         *length = v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE;
+
+         /* Fill in value. */
+         offset = name + v->namelen;
+
+         *offset = OSPF_AS_EXTERNAL_LSA;
+         offset++;
+         oid_copy_addr (offset, &lsa->data->id, IN_ADDR_SIZE);
+         offset += IN_ADDR_SIZE;
+         oid_copy_addr (offset, &lsa->data->adv_router, IN_ADDR_SIZE);
+           
+         return lsa;
+       }
+    }
+  return NULL;
+}
+
+static u_char *
+ospfExtLsdbEntry (struct variable *v, oid *name, size_t *length, int exact,
+                 size_t *var_len, WriteMethod **write_method)
+{
+  struct ospf_lsa *lsa;
+  struct lsa_header *lsah;
+  u_char type;
+  struct in_addr ls_id;
+  struct in_addr router_id;
+  struct ospf *ospf;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  type = OSPF_AS_EXTERNAL_LSA;
+  memset (&ls_id, 0, sizeof (struct in_addr));
+  memset (&router_id, 0, sizeof (struct in_addr));
+
+  /* Check OSPF instance. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return NULL;
+
+  lsa = ospfExtLsdbLookup (v, name, length, &type, &ls_id, &router_id, exact);
+  if (! lsa)
+    return NULL;
+
+  lsah = lsa->data;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFEXTLSDBTYPE:
+      return SNMP_INTEGER (OSPF_AS_EXTERNAL_LSA);
+      break;
+    case OSPFEXTLSDBLSID:
+      return SNMP_IPADDRESS (lsah->id);
+      break;
+    case OSPFEXTLSDBROUTERID:
+      return SNMP_IPADDRESS (lsah->adv_router);
+      break;
+    case OSPFEXTLSDBSEQUENCE:
+      return SNMP_INTEGER (lsah->ls_seqnum);
+      break;
+    case OSPFEXTLSDBAGE:
+      return SNMP_INTEGER (lsah->ls_age);
+      break;
+    case OSPFEXTLSDBCHECKSUM:
+      return SNMP_INTEGER (lsah->checksum);
+      break;
+    case OSPFEXTLSDBADVERTISEMENT:
+      *var_len = ntohs (lsah->length);
+      return (u_char *) lsah;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+static u_char *
+ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length,
+                       int exact, size_t *var_len, WriteMethod **write_method)
+{
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Return the current value of the variable */
+  switch (v->magic) 
+    {
+    case OSPFAREAAGGREGATEAREAID:
+      return (u_char *) NULL;
+      break;
+    case OSPFAREAAGGREGATELSDBTYPE:
+      return (u_char *) NULL;
+      break;
+    case OSPFAREAAGGREGATENET:
+      return (u_char *) NULL;
+      break;
+    case OSPFAREAAGGREGATEMASK:
+      return (u_char *) NULL;
+      break;
+    case OSPFAREAAGGREGATESTATUS:
+      return (u_char *) NULL;
+      break;
+    case OSPFAREAAGGREGATEEFFECT:
+      return (u_char *) NULL;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+/* OSPF Traps. */
+#define IFSTATECHANGE      16
+#define VIRTIFSTATECHANGE   1
+#define NBRSTATECHANGE      2
+#define VIRTNBRSTATECHANGE  3
+
+struct trap_object ospfNbrTrapList[] =
+{
+  {-2, {1, OSPFROUTERID}},
+  {3, {10, 1, OSPFNBRIPADDR}},
+  {3, {10, 1, OSPFNBRRTRID}},
+  {3, {10, 1, OSPFNBRSTATE}}
+};
+
+
+struct trap_object ospfVirtNbrTrapList[] =
+{
+  {-2, {1, 1}},
+  {3, {11, 1, OSPFVIRTNBRAREA}},
+  {3, {11, 1, OSPFVIRTNBRRTRID}},
+  {3, {11, 1, OSPFVIRTNBRSTATE}}
+};
+
+struct trap_object ospfIfTrapList[] =
+{
+  {-2, {1, OSPFROUTERID}},
+  {3, {7, 1, OSPFIFIPADDRESS}},
+  {3, {7, 1, OSPFADDRESSLESSIF}},
+  {3, {7, 1, OSPFIFSTATE}}
+};
+
+struct trap_object ospfVirtIfTrapList[] =
+{
+  {-2, {1, OSPFROUTERID}},
+  {3, {9, 1, OSPFVIRTIFAREAID}},
+  {3, {9, 1, OSPFVIRTIFNEIGHBOR}},
+  {3, {9, 1, OSPFVIRTIFSTATE}}
+};
+
+void
+ospfTrapNbrStateChange (struct ospf_neighbor *on)
+{
+  oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
+  char msgbuf[16];
+  
+  ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf));
+  zlog (NULL, LOG_INFO, "ospfTrapNbrStateChange trap sent: %s now %s",
+       inet_ntoa(on->address.u.prefix4), msgbuf);
+
+  oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+  index[IN_ADDR_SIZE] = 0;
+
+  smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable),
+            ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid),
+            ospf_oid, sizeof ospf_oid / sizeof (oid),
+             index,  IN_ADDR_SIZE + 1,
+             ospfNbrTrapList, 
+             sizeof ospfNbrTrapList / sizeof (struct trap_object),
+             NBRSTATECHANGE);
+}
+
+void
+ospfTrapVirtNbrStateChange (struct ospf_neighbor *on)
+{
+  oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
+  
+  zlog (NULL, LOG_INFO, "ospfTrapVirtNbrStateChange trap sent");
+
+  oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+  index[IN_ADDR_SIZE] = 0;
+
+  smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable),
+            ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid),
+            ospf_oid, sizeof ospf_oid / sizeof (oid),
+             index,  IN_ADDR_SIZE + 1,
+             ospfVirtNbrTrapList, 
+             sizeof ospfVirtNbrTrapList / sizeof (struct trap_object),
+             VIRTNBRSTATECHANGE);
+}
+
+void
+ospfTrapIfStateChange (struct ospf_interface *oi)
+{
+  oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
+
+  zlog (NULL, LOG_INFO, "ospfTrapIfStateChange trap sent: %s now %s",
+       inet_ntoa(oi->address->u.prefix4),
+       LOOKUP(ospf_ism_state_msg, oi->state));
+  
+  oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
+  index[IN_ADDR_SIZE] = 0;
+
+  smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable),
+            ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid),
+            ospf_oid, sizeof ospf_oid / sizeof (oid),
+             index, IN_ADDR_SIZE + 1,
+             ospfIfTrapList, 
+             sizeof ospfIfTrapList / sizeof (struct trap_object),
+             IFSTATECHANGE);
+}
+
+void
+ospfTrapVirtIfStateChange (struct ospf_interface *oi)
+{
+  oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)];
+
+  zlog (NULL, LOG_INFO, "ospfTrapVirtIfStateChange trap sent");
+  
+  oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
+  index[IN_ADDR_SIZE] = 0;
+
+  smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable),
+            ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid),
+            ospf_oid, sizeof ospf_oid / sizeof (oid),
+             index, IN_ADDR_SIZE + 1,
+             ospfVirtIfTrapList,
+             sizeof ospfVirtIfTrapList / sizeof (struct trap_object),
+             VIRTIFSTATECHANGE);
+}
+/* Register OSPF2-MIB. */
+void
+ospf_snmp_init ()
+{
+  ospf_snmp_iflist = list_new ();
+  ospf_snmp_vl_table = route_table_init ();
+  smux_init (om->master);
+  REGISTER_MIB("mibII/ospf", ospf_variables, variable, ospf_oid);
+}
+#endif /* HAVE_SNMP */
diff --git a/ospfd/ospf_snmp.h b/ospfd/ospf_snmp.h
new file mode 100644 (file)
index 0000000..413d1d7
--- /dev/null
@@ -0,0 +1,38 @@
+/* OSPFv2 SNMP support
+ * Copyright (C) 2000 IP Infusion Inc.
+ *
+ * Written by Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_OSPF_SNMP_H
+#define _ZEBRA_OSPF_SNMP_H
+
+extern void ospf_snmp_if_update (struct interface *);
+extern void ospf_snmp_if_delete (struct interface *);
+
+extern void ospf_snmp_vl_add (struct ospf_vl_data *);
+extern void ospf_snmp_vl_delete (struct ospf_vl_data *);
+
+extern void ospfTrapIfStateChange (struct ospf_interface *);
+extern void ospfTrapVirtIfStateChange (struct ospf_interface *);
+extern void ospfTrapNbrStateChange (struct ospf_neighbor *);
+extern void ospfTrapVirtNbrStateChange (struct ospf_neighbor *);
+
+#endif /* _ZEBRA_OSPF_SNMP_H */
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
new file mode 100644 (file)
index 0000000..e1c290e
--- /dev/null
@@ -0,0 +1,1490 @@
+/* OSPF SPF calculation.
+   Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "log.h"
+#include "sockunion.h"          /* for inet_ntop () */
+#include "pqueue.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ia.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_dump.h"
+
+/* Variables to ensure a SPF scheduled log message is printed only once */
+
+static unsigned int spf_reason_flags = 0;
+
+static void
+ospf_clear_spf_reason_flags ()
+{
+  spf_reason_flags = 0;
+}
+
+static void 
+ospf_spf_set_reason (ospf_spf_reason_t reason)
+{
+  spf_reason_flags |= 1 << reason;
+}
+
+static void
+ospf_get_spf_reason_str (char *buf)
+{
+  if (!buf)
+   return;
+  buf[0] = '\0';
+  if (spf_reason_flags)
+    {
+      if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL)
+        strcat (buf, "R, ");
+      if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL)
+        strcat (buf, "N, ");
+      if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL)
+        strcat (buf, "S, ");
+      if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)
+        strcat (buf, "AS, ");
+      if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE)
+        strcat (buf, "ABR, ");
+      if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE)
+        strcat (buf, "ASBR, ");
+      if (spf_reason_flags & SPF_FLAG_MAXAGE)
+        strcat (buf, "M, ");
+      buf[strlen(buf)-2] = '\0'; /* skip the last ", " */
+    }
+}
+
+static void ospf_vertex_free (void *);
+/* List of allocated vertices, to simplify cleanup of SPF.
+ * Not thread-safe obviously. If it ever needs to be, it'd have to be
+ * dynamically allocated at begin of ospf_spf_calculate
+ */
+static struct list vertex_list = { .del = ospf_vertex_free };
+
+/* Heap related functions, for the managment of the candidates, to
+ * be used with pqueue. */
+static int
+cmp (void * node1 , void * node2)
+{
+  struct vertex * v1 = (struct vertex *) node1;
+  struct vertex * v2 = (struct vertex *) node2;
+  if (v1 != NULL && v2 != NULL )
+    {
+      /* network vertices must be chosen before router vertices of same
+       * cost in order to find all shortest paths
+       */
+      if ( ((v1->distance - v2->distance) == 0)
+          && (v1->type != v2->type))
+        {
+          switch (v1->type)
+            {
+              case OSPF_VERTEX_NETWORK:
+                return -1;
+              case OSPF_VERTEX_ROUTER:
+                return 1;
+            }
+        }
+      else
+        return (v1->distance - v2->distance);
+    }
+  return 0;
+}
+
+static void
+update_stat (void *node , int position)
+{
+  struct vertex *v = node;
+
+  /* Set the status of the vertex, when its position changes. */
+  *(v->stat) = position;
+}
+
+static struct vertex_nexthop *
+vertex_nexthop_new (void)
+{
+  return XCALLOC (MTYPE_OSPF_NEXTHOP, sizeof (struct vertex_nexthop));
+}
+
+static void
+vertex_nexthop_free (struct vertex_nexthop *nh)
+{
+  XFREE (MTYPE_OSPF_NEXTHOP, nh);
+}
+
+/* Free the canonical nexthop objects for an area, ie the nexthop objects
+ * attached to the first-hop router vertices, and any intervening network
+ * vertices.
+ */
+static void
+ospf_canonical_nexthops_free (struct vertex *root)
+{
+  struct listnode *node, *nnode;
+  struct vertex *child;
+  
+  for (ALL_LIST_ELEMENTS (root->children, node, nnode, child))
+    {
+      struct listnode *n2, *nn2;
+      struct vertex_parent *vp;
+      
+      /* router vertices through an attached network each
+       * have a distinct (canonical / not inherited) nexthop
+       * which must be freed.
+       *
+       * A network vertex can only have router vertices as its
+       * children, so only one level of recursion is possible.
+       */
+      if (child->type == OSPF_VERTEX_NETWORK)
+        ospf_canonical_nexthops_free (child);
+      
+      /* Free child nexthops pointing back to this root vertex */
+      for (ALL_LIST_ELEMENTS (child->parents, n2, nn2, vp))
+        if (vp->parent == root && vp->nexthop)
+          vertex_nexthop_free (vp->nexthop);
+    }
+}      
+
+/* TODO: Parent list should be excised, in favour of maintaining only
+ * vertex_nexthop, with refcounts.
+ */
+static struct vertex_parent *
+vertex_parent_new (struct vertex *v, int backlink, struct vertex_nexthop *hop)
+{
+  struct vertex_parent *new;
+  
+  new = XMALLOC (MTYPE_OSPF_VERTEX_PARENT, sizeof (struct vertex_parent));
+  
+  if (new == NULL)
+    return NULL;
+  
+  new->parent = v;
+  new->backlink = backlink;
+  new->nexthop = hop;
+  return new;
+}
+
+static void
+vertex_parent_free (void *p)
+{
+  XFREE (MTYPE_OSPF_VERTEX_PARENT, p);
+}
+
+static struct vertex *
+ospf_vertex_new (struct ospf_lsa *lsa)
+{
+  struct vertex *new;
+
+  new = XCALLOC (MTYPE_OSPF_VERTEX, sizeof (struct vertex));
+
+  new->flags = 0;
+  new->stat = &(lsa->stat);
+  new->type = lsa->data->type;
+  new->id = lsa->data->id;
+  new->lsa = lsa->data;
+  new->children = list_new ();
+  new->parents = list_new ();
+  new->parents->del = vertex_parent_free;
+  
+  listnode_add (&vertex_list, new);
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("%s: Created %s vertex %s", __func__,
+                new->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
+                inet_ntoa (new->lsa->id));
+  return new;
+}
+
+static void
+ospf_vertex_free (void *data)
+{
+  struct vertex *v = data;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("%s: Free %s vertex %s", __func__,
+                v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
+                inet_ntoa (v->lsa->id));
+  
+  /* There should be no parents potentially holding references to this vertex
+   * Children however may still be there, but presumably referenced by other
+   * vertices
+   */
+  //assert (listcount (v->parents) == 0);
+  
+  if (v->children)
+    list_delete (v->children);
+  v->children = NULL;
+  
+  if (v->parents)
+    list_delete (v->parents);
+  v->parents = NULL;
+  
+  v->lsa = NULL;
+  
+  XFREE (MTYPE_OSPF_VERTEX, v);
+}
+
+static void
+ospf_vertex_dump(const char *msg, struct vertex *v,
+                int print_parents, int print_children)
+{
+  if ( ! IS_DEBUG_OSPF_EVENT)
+    return;
+
+  zlog_debug("%s %s vertex %s  distance %u flags %u",
+            msg,
+           v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
+           inet_ntoa(v->lsa->id),
+           v->distance,
+           (unsigned int)v->flags);
+
+  if (print_parents)
+    {
+      struct listnode *node;
+      struct vertex_parent *vp;
+      
+      for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
+        {
+         char buf1[BUFSIZ];
+         
+         if (vp)
+           {
+             zlog_debug ("parent %s backlink %d nexthop %s  interface %s",
+                        inet_ntoa(vp->parent->lsa->id), vp->backlink,
+                        inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ),
+                        vp->nexthop->oi ? IF_NAME(vp->nexthop->oi) : "NULL");
+           }
+       }
+    }
+
+  if (print_children)
+    {
+      struct listnode *cnode;
+      struct vertex *cv;
+      
+      for (ALL_LIST_ELEMENTS_RO (v->children, cnode, cv))
+        ospf_vertex_dump(" child:", cv, 0, 0);
+    }
+}
+
+
+/* Add a vertex to the list of children in each of its parents. */
+static void
+ospf_vertex_add_parent (struct vertex *v)
+{
+  struct vertex_parent *vp;
+  struct listnode *node;
+  
+  assert (v && v->parents);
+  
+  for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp))
+    {
+      assert (vp->parent && vp->parent->children);
+      
+      /* No need to add two links from the same parent. */
+      if (listnode_lookup (vp->parent->children, v) == NULL)
+        listnode_add (vp->parent->children, v);
+    }
+}
+
+static void
+ospf_spf_init (struct ospf_area *area)
+{
+  struct vertex *v;
+  
+  /* Create root node. */
+  v = ospf_vertex_new (area->router_lsa_self);
+  
+  area->spf = v;
+
+  /* Reset ABR and ASBR router counts. */
+  area->abr_count = 0;
+  area->asbr_count = 0;
+}
+
+/* return index of link back to V from W, or -1 if no link found */
+static int
+ospf_lsa_has_link (struct lsa_header *w, struct lsa_header *v)
+{
+  unsigned int i, length;
+  struct router_lsa *rl;
+  struct network_lsa *nl;
+
+  /* In case of W is Network LSA. */
+  if (w->type == OSPF_NETWORK_LSA)
+    {
+      if (v->type == OSPF_NETWORK_LSA)
+        return -1;
+
+      nl = (struct network_lsa *) w;
+      length = (ntohs (w->length) - OSPF_LSA_HEADER_SIZE - 4) / 4;
+
+      for (i = 0; i < length; i++)
+        if (IPV4_ADDR_SAME (&nl->routers[i], &v->id))
+          return i;
+      return -1;
+    }
+
+  /* In case of W is Router LSA. */
+  if (w->type == OSPF_ROUTER_LSA)
+    {
+      rl = (struct router_lsa *) w;
+
+      length = ntohs (w->length);
+
+      for (i = 0;
+           i < ntohs (rl->links) && length >= sizeof (struct router_lsa);
+           i++, length -= 12)
+        {
+          switch (rl->link[i].type)
+            {
+            case LSA_LINK_TYPE_POINTOPOINT:
+            case LSA_LINK_TYPE_VIRTUALLINK:
+              /* Router LSA ID. */
+              if (v->type == OSPF_ROUTER_LSA &&
+                  IPV4_ADDR_SAME (&rl->link[i].link_id, &v->id))
+                {
+                  return i;
+                }
+              break;
+            case LSA_LINK_TYPE_TRANSIT:
+              /* Network LSA ID. */
+              if (v->type == OSPF_NETWORK_LSA &&
+                  IPV4_ADDR_SAME (&rl->link[i].link_id, &v->id))
+                {
+                  return i;
+                }
+              break;
+            case LSA_LINK_TYPE_STUB:
+              /* Stub can't lead anywhere, carry on */
+              continue;
+            default:
+              break;
+            }
+        }
+    }
+  return -1;
+}
+
+/* Find the next link after prev_link from v to w.  If prev_link is
+ * NULL, return the first link from v to w.  Ignore stub and virtual links;
+ * these link types will never be returned.
+ */
+static struct router_lsa_link *
+ospf_get_next_link (struct vertex *v, struct vertex *w,
+                    struct router_lsa_link *prev_link)
+{
+  u_char *p;
+  u_char *lim;
+  u_char lsa_type =  LSA_LINK_TYPE_TRANSIT;
+  struct router_lsa_link *l;
+
+  if (w->type == OSPF_VERTEX_ROUTER)
+    lsa_type = LSA_LINK_TYPE_POINTOPOINT;
+
+  if (prev_link == NULL)
+    p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4;
+  else
+    {
+      p = (u_char *) prev_link;
+      p += (OSPF_ROUTER_LSA_LINK_SIZE +
+            (prev_link->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE));
+    }
+
+  lim = ((u_char *) v->lsa) + ntohs (v->lsa->length);
+
+  while (p < lim)
+    {
+      l = (struct router_lsa_link *) p;
+
+      p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE));
+
+      if (l->m[0].type != lsa_type)
+        continue;
+
+      if (IPV4_ADDR_SAME (&l->link_id, &w->id))
+        return l;
+    }
+
+  return NULL;
+}
+
+static void
+ospf_spf_flush_parents (struct vertex *w)
+{
+  struct vertex_parent *vp;
+  struct listnode *ln, *nn;
+  
+  /* delete the existing nexthops */
+  for (ALL_LIST_ELEMENTS (w->parents, ln, nn, vp))
+    {
+      list_delete_node (w->parents, ln);
+      vertex_parent_free (vp);
+    }
+}
+
+/* 
+ * Consider supplied next-hop for inclusion to the supplied list of
+ * equal-cost next-hops, adjust list as neccessary.  
+ */
+static void
+ospf_spf_add_parent (struct vertex *v, struct vertex *w,
+                     struct vertex_nexthop *newhop,
+                     unsigned int distance)
+{
+  struct vertex_parent *vp, *wp;
+  struct listnode *node;
+    
+  /* we must have a newhop, and a distance */
+  assert (v && w && newhop);
+  assert (distance);
+  
+  /* IFF w has already been assigned a distance, then we shouldn't get here
+   * unless callers have determined V(l)->W is shortest / equal-shortest
+   * path (0 is a special case distance (no distance yet assigned)).
+   */
+  if (w->distance)
+    assert (distance <= w->distance);
+  else
+    w->distance = distance;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      char buf[2][INET_ADDRSTRLEN];
+      zlog_debug ("%s: Adding %s as parent of %s",
+                __func__,
+                inet_ntop(AF_INET, &v->lsa->id, buf[0], sizeof(buf[0])),
+                inet_ntop(AF_INET, &w->lsa->id, buf[1], sizeof(buf[1])));
+    }           
+
+  /* Adding parent for a new, better path: flush existing parents from W. */
+  if (distance < w->distance)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("%s: distance %d better than %d, flushing existing parents",
+                    __func__, distance, w->distance);
+      ospf_spf_flush_parents (w);
+      w->distance = distance;
+    }
+  
+  /* new parent is <= existing parents, add it to parent list (if nexthop
+   * not on parent list)
+   */  
+  for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp))
+    {
+      if (memcmp(newhop, wp->nexthop, sizeof(*newhop)) == 0)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("%s: ... nexthop already on parent list, skipping add", __func__);
+          return;
+        }
+    }
+
+  vp = vertex_parent_new (v, ospf_lsa_has_link (w->lsa, v->lsa), newhop);
+  listnode_add (w->parents, vp);
+
+  return;
+}
+
+/* 16.1.1.  Calculate nexthop from root through V (parent) to
+ * vertex W (destination), with given distance from root->W.
+ *
+ * The link must be supplied if V is the root vertex. In all other cases
+ * it may be NULL.
+ *
+ * Note that this function may fail, hence the state of the destination
+ * vertex, W, should /not/ be modified in a dependent manner until
+ * this function returns. This function will update the W vertex with the
+ * provided distance as appropriate.
+ */
+static unsigned int
+ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v,
+                          struct vertex *w, struct router_lsa_link *l,
+                          unsigned int distance, int lsa_pos)
+{
+  struct listnode *node, *nnode;
+  struct vertex_nexthop *nh;
+  struct vertex_parent *vp;
+  struct ospf_interface *oi = NULL;
+  unsigned int added = 0;
+  char buf1[BUFSIZ];
+  char buf2[BUFSIZ];
+
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("ospf_nexthop_calculation(): Start");
+      ospf_vertex_dump("V (parent):", v, 1, 1);
+      ospf_vertex_dump("W (dest)  :", w, 1, 1);
+      zlog_debug ("V->W distance: %d", distance);
+    }
+
+  if (v == area->spf)
+    {      
+      /* 16.1.1 para 4.  In the first case, the parent vertex (V) is the
+        root (the calculating router itself).  This means that the 
+        destination is either a directly connected network or directly
+        connected router.  The outgoing interface in this case is simply 
+         the OSPF interface connecting to the destination network/router.
+      */
+
+      /* we *must* be supplied with the link data */
+      assert (l != NULL);
+      oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos);
+      if (!oi)
+       {
+         zlog_debug("%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s",
+                    __func__, lsa_pos,
+                    inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
+                    inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
+         return 0;
+       }
+
+      if (IS_DEBUG_OSPF_EVENT)
+       {
+         zlog_debug("%s: considering link:%s "
+                    "type:%d link_id:%s link_data:%s",
+                    __func__, oi->ifp->name, l->m[0].type,
+                    inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ),
+                    inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ));
+       }
+
+      if (w->type == OSPF_VERTEX_ROUTER)
+        {
+          /* l  is a link from v to w
+           * l2 will be link from w to v
+           */
+          struct router_lsa_link *l2 = NULL;
+
+          if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT)
+            {
+              struct in_addr nexthop = { .s_addr = 0 };
+
+              /* If the destination is a router which connects to
+                 the calculating router via a Point-to-MultiPoint
+                 network, the destination's next hop IP address(es)
+                 can be determined by examining the destination's
+                 router-LSA: each link pointing back to the
+                 calculating router and having a Link Data field
+                 belonging to the Point-to-MultiPoint network
+                 provides an IP address of the next hop router.
+
+                 At this point l is a link from V to W, and V is the
+                 root ("us"). If it is a point-to-multipoint interface,
+                then look through the links in the opposite direction (W to V).
+                If any of them have an address that lands within the
+                 subnet declared by the PtMP link, then that link
+                 is a constituent of the PtMP link, and its address is
+                 a nexthop address for V.
+              */
+             if (oi->type == OSPF_IFTYPE_POINTOPOINT)
+               {
+                 /* Having nexthop = 0 is tempting, but NOT acceptable.
+                    It breaks AS-External routes with a forwarding address,
+                    since ospf_ase_complete_direct_routes() will mistakenly
+                    assume we've reached the last hop and should place the
+                    forwarding address as nexthop.
+                    Also, users may configure multi-access links in p2p mode,
+                    so we need the IP to ARP the nexthop.
+                 */
+                 struct ospf_neighbor *nbr_w;
+
+                 nbr_w = ospf_nbr_lookup_by_routerid (oi->nbrs, &l->link_id);
+                 if (nbr_w != NULL)
+                   {
+                     added = 1;
+                     nexthop = nbr_w->src;
+                   }
+               }
+             else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+               {
+                 struct prefix_ipv4 la;
+
+                 la.family = AF_INET;
+                 la.prefixlen = oi->address->prefixlen;
+
+                 /* V links to W on PtMP interface
+                    - find the interface address on W */
+                 while ((l2 = ospf_get_next_link (w, v, l2)))
+                   {
+                     la.prefix = l2->link_data;
+
+                     if (prefix_cmp ((struct prefix *) &la,
+                                     oi->address) != 0)
+                       continue;
+                     /* link_data is on our PtMP network */
+                     added = 1;
+                     nexthop = l2->link_data;
+                     break;
+                   }
+               }
+
+              if (added)
+                {
+                  /* found all necessary info to build nexthop */
+                  nh = vertex_nexthop_new ();
+                  nh->oi = oi;
+                  nh->router = nexthop;
+                  ospf_spf_add_parent (v, w, nh, distance);
+                  return 1;
+                }
+              else
+               zlog_info("%s: could not determine nexthop for link %s",
+                         __func__, oi->ifp->name);
+            } /* end point-to-point link from V to W */
+          else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK)
+            {
+              struct ospf_vl_data *vl_data;
+              
+              /* VLink implementation limitations: 
+               * a) vl_data can only reference one nexthop, so no ECMP
+               *    to backbone through VLinks. Though transit-area 
+               *    summaries may be considered, and those can be ECMP.
+               * b) We can only use /one/ VLink, even if multiple ones
+               *    exist this router through multiple transit-areas.
+               */
+              vl_data = ospf_vl_lookup (area->ospf, NULL, l->link_id);
+              
+              if (vl_data 
+                  && CHECK_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED))
+                {
+                  nh = vertex_nexthop_new ();
+                  nh->oi = vl_data->nexthop.oi;
+                  nh->router = vl_data->nexthop.router;
+                  ospf_spf_add_parent (v, w, nh, distance);
+                  return 1;
+                }
+              else
+                  zlog_info("ospf_nexthop_calculation(): "
+                            "vl_data for VL link not found");
+            } /* end virtual-link from V to W */
+          return 0;
+        } /* end W is a Router vertex */
+      else
+        {
+          assert(w->type == OSPF_VERTEX_NETWORK);
+
+         nh = vertex_nexthop_new ();
+         nh->oi = oi;
+         nh->router.s_addr = 0; /* Nexthop not required */
+         ospf_spf_add_parent (v, w, nh, distance);
+         return 1;
+        }
+    } /* end V is the root */
+  /* Check if W's parent is a network connected to root. */
+  else if (v->type == OSPF_VERTEX_NETWORK)
+    {
+      /* See if any of V's parents are the root. */
+      for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp))
+        {
+          if (vp->parent == area->spf) /* connects to root? */
+           {
+             /* 16.1.1 para 5. ...the parent vertex is a network that
+              * directly connects the calculating router to the destination
+              * router.  The list of next hops is then determined by
+              * examining the destination's router-LSA...
+              */
+
+             assert(w->type == OSPF_VERTEX_ROUTER);
+              while ((l = ospf_get_next_link (w, v, l)))
+                {
+                 /* ...For each link in the router-LSA that points back to the
+                  * parent network, the link's Link Data field provides the IP
+                  * address of a next hop router.  The outgoing interface to
+                  * use can then be derived from the next hop IP address (or 
+                  * it can be inherited from the parent network).
+                  */
+                 nh = vertex_nexthop_new ();
+                 nh->oi = vp->nexthop->oi;
+                 nh->router = l->link_data;
+                 added = 1;
+                  ospf_spf_add_parent (v, w, nh, distance);
+                }
+              /* Note lack of return is deliberate. See next comment. */
+          }
+        }
+      /* NB: This code is non-trivial.
+       * 
+       * E.g. it is not enough to know that V connects to the root. It is
+       * also important that the while above, looping through all links from
+       * W->V found at least one link, so that we know there is
+       * bi-directional connectivity between V and W (which need not be the
+       * case, e.g.  when OSPF has not yet converged fully).  Otherwise, if
+       * we /always/ return here, without having checked that root->V->-W
+       * actually resulted in a valid nexthop being created, then we we will
+       * prevent SPF from finding/using higher cost paths.
+       *
+       * It is important, if root->V->W has not been added, that we continue
+       * through to the intervening-router nexthop code below.  So as to
+       * ensure other paths to V may be used.  This avoids unnecessary
+       * blackholes while OSPF is convergening.
+       *
+       * I.e. we may have arrived at this function, examining V -> W, via
+       * workable paths other than root -> V, and it's important to avoid
+       * getting "confused" by non-working root->V->W path - it's important
+       * to *not* lose the working non-root paths, just because of a
+       * non-viable root->V->W.
+       *
+       * See also bug #330 (required reading!), and:
+       *
+       * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes
+       */
+      if (added)
+        return added;
+    }
+
+  /* 16.1.1 para 4.  If there is at least one intervening router in the
+   * current shortest path between the destination and the root, the
+   * destination simply inherits the set of next hops from the
+   * parent.
+   */
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("%s: Intervening routers, adding parent(s)", __func__);
+
+  for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp))
+    {
+      added = 1;
+      ospf_spf_add_parent (v, w, vp->nexthop, distance);
+    }
+  
+  return added;
+}
+
+/* RFC2328 Section 16.1 (2).
+ * v is on the SPF tree.  Examine the links in v's LSA.  Update the list
+ * of candidates with any vertices not already on the list.  If a lower-cost
+ * path is found to a vertex already on the candidate list, store the new cost.
+ */
+static void
+ospf_spf_next (struct vertex *v, struct ospf_area *area,
+              struct pqueue * candidate)
+{
+  struct ospf_lsa *w_lsa = NULL;
+  u_char *p;
+  u_char *lim;
+  struct router_lsa_link *l = NULL;
+  struct in_addr *r;
+  int type = 0, lsa_pos=-1, lsa_pos_next=0;
+
+  /* If this is a router-LSA, and bit V of the router-LSA (see Section
+     A.4.2:RFC2328) is set, set Area A's TransitCapability to TRUE.  */
+  if (v->type == OSPF_VERTEX_ROUTER)
+    {
+      if (IS_ROUTER_LSA_VIRTUAL ((struct router_lsa *) v->lsa))
+        area->transit = OSPF_TRANSIT_TRUE;
+    }
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("%s: Next vertex of %s vertex %s",
+                __func__, 
+                v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
+                inet_ntoa(v->lsa->id));
+  
+  p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4;
+  lim = ((u_char *) v->lsa) + ntohs (v->lsa->length);
+
+  while (p < lim)
+    {
+      struct vertex *w;
+      unsigned int distance;
+      
+      /* In case of V is Router-LSA. */
+      if (v->lsa->type == OSPF_ROUTER_LSA)
+        {
+          l = (struct router_lsa_link *) p;
+
+         lsa_pos = lsa_pos_next; /* LSA link position */
+         lsa_pos_next++;
+          p += (OSPF_ROUTER_LSA_LINK_SIZE +
+                (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE));
+
+          /* (a) If this is a link to a stub network, examine the next
+             link in V's LSA.  Links to stub networks will be
+             considered in the second stage of the shortest path
+             calculation. */
+          if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB)
+            continue;
+          
+          /* Infinite distance links shouldn't be followed, except
+           * for local links (a stub-routed router still wants to
+           * calculate tree, so must follow its own links).
+           */
+          if ((v != area->spf) && l->m[0].metric >= OSPF_OUTPUT_COST_INFINITE)
+            continue;
+
+          /* (b) Otherwise, W is a transit vertex (router or transit
+             network).  Look up the vertex W's LSA (router-LSA or
+             network-LSA) in Area A's link state database. */
+          switch (type)
+            {
+            case LSA_LINK_TYPE_POINTOPOINT:
+            case LSA_LINK_TYPE_VIRTUALLINK:
+              if (type == LSA_LINK_TYPE_VIRTUALLINK)
+                {
+                  if (IS_DEBUG_OSPF_EVENT)
+                    zlog_debug ("looking up LSA through VL: %s",
+                               inet_ntoa (l->link_id));
+                }
+
+              w_lsa = ospf_lsa_lookup (area, OSPF_ROUTER_LSA, l->link_id,
+                                       l->link_id);
+              if (w_lsa)
+                {
+                  if (IS_DEBUG_OSPF_EVENT)
+                    zlog_debug ("found Router LSA %s", inet_ntoa (l->link_id));
+                }
+              break;
+            case LSA_LINK_TYPE_TRANSIT:
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("Looking up Network LSA, ID: %s",
+                           inet_ntoa (l->link_id));
+              w_lsa = ospf_lsa_lookup_by_id (area, OSPF_NETWORK_LSA,
+                                             l->link_id);
+              if (w_lsa)
+                if (IS_DEBUG_OSPF_EVENT)
+                  zlog_debug ("found the LSA");
+              break;
+            default:
+              zlog_warn ("Invalid LSA link type %d", type);
+              continue;
+            }
+        }
+      else
+        {
+          /* In case of V is Network-LSA. */
+          r = (struct in_addr *) p;
+          p += sizeof (struct in_addr);
+
+          /* Lookup the vertex W's LSA. */
+          w_lsa = ospf_lsa_lookup_by_id (area, OSPF_ROUTER_LSA, *r);
+          if (w_lsa)
+            {
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("found Router LSA %s", inet_ntoa (w_lsa->data->id));
+            }
+        }
+
+      /* (b cont.) If the LSA does not exist, or its LS age is equal
+         to MaxAge, or it does not have a link back to vertex V,
+         examine the next link in V's LSA.[23] */
+      if (w_lsa == NULL)
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("No LSA found");
+          continue;
+        }
+
+      if (IS_LSA_MAXAGE (w_lsa))
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("LSA is MaxAge");
+          continue;
+        }
+
+      if (ospf_lsa_has_link (w_lsa->data, v->lsa) < 0 )
+        {
+          if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("The LSA doesn't have a link back");
+          continue;
+        }
+
+      /* (c) If vertex W is already on the shortest-path tree, examine
+         the next link in the LSA. */
+      if (w_lsa->stat == LSA_SPF_IN_SPFTREE)
+       {
+         if (IS_DEBUG_OSPF_EVENT)
+           zlog_debug ("The LSA is already in SPF");
+         continue;
+       }
+
+      /* (d) Calculate the link state cost D of the resulting path
+         from the root to vertex W.  D is equal to the sum of the link
+         state cost of the (already calculated) shortest path to
+         vertex V and the advertised cost of the link between vertices
+         V and W.  If D is: */
+
+      /* calculate link cost D. */
+      if (v->lsa->type == OSPF_ROUTER_LSA)
+       distance = v->distance + ntohs (l->m[0].metric);
+      else /* v is not a Router-LSA */
+       distance = v->distance;
+
+      /* Is there already vertex W in candidate list? */
+      if (w_lsa->stat == LSA_SPF_NOT_EXPLORED)
+       {
+          /* prepare vertex W. */
+          w = ospf_vertex_new (w_lsa);
+
+          /* Calculate nexthop to W. */
+          if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos))
+            pqueue_enqueue (w, candidate);
+          else if (IS_DEBUG_OSPF_EVENT)
+            zlog_debug ("Nexthop Calc failed");
+       }
+      else if (w_lsa->stat >= 0)
+       {
+         /* Get the vertex from candidates. */
+         w = candidate->array[w_lsa->stat];
+
+         /* if D is greater than. */  
+         if (w->distance < distance)
+            {
+              continue;
+            }
+          /* equal to. */
+         else if (w->distance == distance)
+            {
+             /* Found an equal-cost path to W.  
+               * Calculate nexthop of to W from V. */
+             ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos);
+            }
+           /* less than. */
+         else
+            {
+              /* Found a lower-cost path to W.
+               * nexthop_calculation is conditional, if it finds
+               * valid nexthop it will call spf_add_parents, which
+               * will flush the old parents
+               */
+             if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos))
+                /* 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. 
+                 * (next pqueu_{de,en}queue will fully re-heap the queue).
+                 */
+                trickle_up (w_lsa->stat, candidate);
+            }
+        } /* end W is already on the candidate list */
+    } /* end loop over the links in V's LSA */
+}
+
+static void
+ospf_spf_dump (struct vertex *v, int i)
+{
+  struct listnode *cnode;
+  struct listnode *nnode;
+  struct vertex_parent *parent;
+
+  if (v->type == OSPF_VERTEX_ROUTER)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("SPF Result: %d [R] %s", i, inet_ntoa (v->lsa->id));
+    }
+  else
+    {
+      struct network_lsa *lsa = (struct network_lsa *) v->lsa;
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("SPF Result: %d [N] %s/%d", i, inet_ntoa (v->lsa->id),
+                   ip_masklen (lsa->mask));
+    }
+
+  if (IS_DEBUG_OSPF_EVENT)
+    for (ALL_LIST_ELEMENTS_RO (v->parents, nnode, parent))
+      {
+        zlog_debug (" nexthop %p %s %s", 
+                    (void *)parent->nexthop,
+                    inet_ntoa (parent->nexthop->router),
+                    parent->nexthop->oi ? IF_NAME(parent->nexthop->oi)
+                                        : "NULL");
+      }
+
+  i++;
+
+  for (ALL_LIST_ELEMENTS_RO (v->children, cnode, v))
+    ospf_spf_dump (v, i);
+}
+
+/* Second stage of SPF calculation. */
+static void
+ospf_spf_process_stubs (struct ospf_area *area, struct vertex *v,
+                        struct route_table *rt,
+                        int parent_is_root)
+{
+  struct listnode *cnode, *cnnode;
+  struct vertex *child;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_process_stub():processing stubs for area %s",
+               inet_ntoa (area->area_id));
+  if (v->type == OSPF_VERTEX_ROUTER)
+    {
+      u_char *p;
+      u_char *lim;
+      struct router_lsa_link *l;
+      struct router_lsa *rlsa;
+      int lsa_pos = 0;
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_process_stubs():processing router LSA, id: %s",
+                   inet_ntoa (v->lsa->id));
+      rlsa = (struct router_lsa *) v->lsa;
+
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_process_stubs(): we have %d links to process",
+                   ntohs (rlsa->links));
+      p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4;
+      lim = ((u_char *) v->lsa) + ntohs (v->lsa->length);
+
+      while (p < lim)
+        {
+          l = (struct router_lsa_link *) p;
+
+          p += (OSPF_ROUTER_LSA_LINK_SIZE +
+                (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE));
+
+          if (l->m[0].type == LSA_LINK_TYPE_STUB)
+           ospf_intra_add_stub (rt, l, v, area, parent_is_root, lsa_pos);
+         lsa_pos++;
+        }
+    }
+
+  ospf_vertex_dump("ospf_process_stubs(): after examining links: ", v, 1, 1);
+
+  for (ALL_LIST_ELEMENTS (v->children, cnode, cnnode, child))
+    {
+      if (CHECK_FLAG (child->flags, OSPF_VERTEX_PROCESSED))
+        continue;
+      
+      /* the first level of routers connected to the root
+       * should have 'parent_is_root' set, including those 
+       * connected via a network vertex.
+       */
+      if (area->spf == v)
+        parent_is_root = 1;
+      else if (v->type == OSPF_VERTEX_ROUTER)
+        parent_is_root = 0;
+        
+      ospf_spf_process_stubs (area, child, rt, parent_is_root);
+
+      SET_FLAG (child->flags, OSPF_VERTEX_PROCESSED);
+    }
+}
+
+void
+ospf_rtrs_free (struct route_table *rtrs)
+{
+  struct route_node *rn;
+  struct list *or_list;
+  struct ospf_route *or;
+  struct listnode *node, *nnode;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Route: Router Routing Table free");
+
+  for (rn = route_top (rtrs); rn; rn = route_next (rn))
+    if ((or_list = rn->info) != NULL)
+      {
+        for (ALL_LIST_ELEMENTS (or_list, node, nnode, or))
+          ospf_route_free (or);
+
+        list_delete (or_list);
+
+        /* Unlock the node. */
+        rn->info = NULL;
+        route_unlock_node (rn);
+      }
+  route_table_finish (rtrs);
+}
+
+#if 0
+static void
+ospf_rtrs_print (struct route_table *rtrs)
+{
+  struct route_node *rn;
+  struct list *or_list;
+  struct listnode *ln;
+  struct listnode *pnode;
+  struct ospf_route *or;
+  struct ospf_path *path;
+  char buf1[BUFSIZ];
+  char buf2[BUFSIZ];
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_rtrs_print() start");
+
+  for (rn = route_top (rtrs); rn; rn = route_next (rn))
+    if ((or_list = rn->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (or_list, ln, or))
+        {
+          switch (or->path_type)
+            {
+            case OSPF_PATH_INTRA_AREA:
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("%s   [%d] area: %s",
+                           inet_ntop (AF_INET, &or->id, buf1, BUFSIZ),
+                           or->cost, inet_ntop (AF_INET, &or->u.std.area_id,
+                                                buf2, BUFSIZ));
+              break;
+            case OSPF_PATH_INTER_AREA:
+              if (IS_DEBUG_OSPF_EVENT)
+                zlog_debug ("%s IA [%d] area: %s",
+                           inet_ntop (AF_INET, &or->id, buf1, BUFSIZ),
+                           or->cost, inet_ntop (AF_INET, &or->u.std.area_id,
+                                                buf2, BUFSIZ));
+              break;
+            default:
+              break;
+            }
+
+          for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path))
+            {
+              if (path->nexthop.s_addr == 0)
+                {
+                  if (IS_DEBUG_OSPF_EVENT)
+                    zlog_debug ("   directly attached to %s\r\n",
+                               ifindex2ifname (path->ifindex));
+                }
+              else
+                {
+                  if (IS_DEBUG_OSPF_EVENT)
+                    zlog_debug ("   via %s, %s\r\n",
+                               inet_ntoa (path->nexthop),
+                               ifindex2ifname (path->ifindex));
+                }
+            }
+        }
+
+  zlog_debug ("ospf_rtrs_print() end");
+}
+#endif
+
+/* Calculating the shortest-path tree for an area. */
+static void
+ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table,
+                    struct route_table *new_rtrs)
+{
+  struct pqueue *candidate;
+  struct vertex *v;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_debug ("ospf_spf_calculate: Start");
+      zlog_debug ("ospf_spf_calculate: running Dijkstra for area %s",
+                 inet_ntoa (area->area_id));
+    }
+
+  /* Check router-lsa-self.  If self-router-lsa is not yet allocated,
+     return this area's calculation. */
+  if (!area->router_lsa_self)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("ospf_spf_calculate: "
+                   "Skip area %s's calculation due to empty router_lsa_self",
+                   inet_ntoa (area->area_id));
+      return;
+    }
+
+  /* RFC2328 16.1. (1). */
+  /* Initialize the algorithm's data structures. */
+  
+  /* This function scans all the LSA database and set the stat field to
+   * LSA_SPF_NOT_EXPLORED. */
+  ospf_lsdb_clean_stat (area->lsdb);
+  /* Create a new heap for the candidates. */ 
+  candidate = pqueue_create();
+  candidate->cmp = cmp;
+  candidate->update = update_stat;
+
+  /* Initialize the shortest-path tree to only the root (which is the
+     router doing the calculation). */
+  ospf_spf_init (area);
+  v = area->spf;
+  /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of the
+   * spanning tree. */
+  *(v->stat) = LSA_SPF_IN_SPFTREE;
+
+  /* Set Area A's TransitCapability to FALSE. */
+  area->transit = OSPF_TRANSIT_FALSE;
+  area->shortcut_capability = 1;
+  
+  for (;;)
+    {
+      /* RFC2328 16.1. (2). */
+      ospf_spf_next (v, area, candidate);
+
+      /* RFC2328 16.1. (3). */
+      /* If at this step the candidate list is empty, the shortest-
+         path tree (of transit vertices) has been completely built and
+         this stage of the procedure terminates. */
+      if (candidate->size == 0)
+        break;
+
+      /* Otherwise, choose the vertex belonging to the candidate list
+         that is closest to the root, and add it to the shortest-path
+         tree (removing it from the candidate list in the
+         process). */
+      /* Extract from the candidates the node with the lower key. */
+      v = (struct vertex *) pqueue_dequeue (candidate);
+      /* Update stat field in vertex. */
+      *(v->stat) = LSA_SPF_IN_SPFTREE;
+
+      ospf_vertex_add_parent (v);
+
+      /* RFC2328 16.1. (4). */
+      if (v->type == OSPF_VERTEX_ROUTER)
+        ospf_intra_add_router (new_rtrs, v, area);
+      else
+        ospf_intra_add_transit (new_table, v, area);
+
+      /* RFC2328 16.1. (5). */
+      /* Iterate the algorithm by returning to Step 2. */
+
+    } /* end loop until no more candidate vertices */
+
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      ospf_spf_dump (area->spf, 0);
+      ospf_route_table_dump (new_table);
+    }
+
+  /* Second stage of SPF calculation procedure's  */
+  ospf_spf_process_stubs (area, area->spf, new_table, 0);
+
+  /* Free candidate queue. */
+  pqueue_delete (candidate);
+
+  ospf_vertex_dump (__func__, area->spf, 0, 1);
+  /* Free nexthop information, canonical versions of which are attached
+   * the first level of router vertices attached to the root vertex, see
+   * ospf_nexthop_calculation.
+   */
+  ospf_canonical_nexthops_free (area->spf);
+
+  /* Increment SPF Calculation Counter. */
+  area->spf_calculation++;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &area->ospf->ts_spf);
+  area->ts_spf = area->ospf->ts_spf;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_spf_calculate: Stop. %zd vertices",
+                mtype_stats_alloc(MTYPE_OSPF_VERTEX));
+
+  /* Free SPF vertices, but not the list. List has ospf_vertex_free
+   * as deconstructor.
+   */
+  list_delete_all_node (&vertex_list);
+}
+
+/* Timer for SPF calculation. */
+static int
+ospf_spf_calculate_timer (struct thread *thread)
+{
+  struct ospf *ospf = THREAD_ARG (thread);
+  struct route_table *new_table, *new_rtrs;
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+  struct timeval start_time, stop_time, spf_start_time;
+  int areas_processed = 0;
+  unsigned long ia_time, prune_time, rt_time;
+  unsigned long abr_time, total_spf_time, spf_time;
+  char rbuf[32];               /* reason_buf */
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("SPF: Timer (SPF calculation expire)");
+
+  ospf->t_spf_calc = NULL;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &spf_start_time);
+  /* Allocate new table tree. */
+  new_table = route_table_init ();
+  new_rtrs = route_table_init ();
+
+  ospf_vl_unapprove (ospf);
+
+  /* Calculate SPF for each area. */
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      /* Do backbone last, so as to first discover intra-area paths
+       * for any back-bone virtual-links
+       */
+      if (ospf->backbone && ospf->backbone == area)
+        continue;
+
+      ospf_spf_calculate (area, new_table, new_rtrs);
+      areas_processed++;
+    }
+
+  /* SPF for backbone, if required */
+  if (ospf->backbone)
+    {
+      ospf_spf_calculate (ospf->backbone, new_table, new_rtrs);
+      areas_processed++;
+    }
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  spf_time = timeval_elapsed (stop_time, spf_start_time);
+
+  ospf_vl_shut_unapproved (ospf);
+
+  start_time = stop_time;      /* saving a call */
+
+  ospf_ia_routing (ospf, new_table, new_rtrs);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  ia_time = timeval_elapsed (stop_time, start_time);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time);
+  ospf_prune_unreachable_networks (new_table);
+  ospf_prune_unreachable_routers (new_rtrs);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  prune_time = timeval_elapsed (stop_time, start_time);
+  /* AS-external-LSA calculation should not be performed here. */
+
+  /* If new Router Route is installed,
+     then schedule re-calculate External routes. */
+  if (1)
+    ospf_ase_calculate_schedule (ospf);
+
+  ospf_ase_calculate_timer_add (ospf);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time);
+
+  /* Update routing table. */
+  ospf_route_install (ospf, new_table);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  rt_time = timeval_elapsed (stop_time, start_time);
+  /* Update ABR/ASBR routing table */
+  if (ospf->old_rtrs)
+    {
+      /* old_rtrs's node holds linked list of ospf_route. --kunihiro. */
+      /* ospf_route_delete (ospf->old_rtrs); */
+      ospf_rtrs_free (ospf->old_rtrs);
+    }
+
+  ospf->old_rtrs = ospf->new_rtrs;
+  ospf->new_rtrs = new_rtrs;
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time);
+  if (IS_OSPF_ABR (ospf))
+    ospf_abr_task (ospf);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  abr_time = timeval_elapsed (stop_time, start_time);
+
+  quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time);
+  total_spf_time = timeval_elapsed (stop_time, spf_start_time);
+  ospf->ts_spf_duration.tv_sec = total_spf_time/1000000;
+  ospf->ts_spf_duration.tv_usec = total_spf_time % 1000000;
+
+  ospf_get_spf_reason_str (rbuf);
+
+  if (IS_DEBUG_OSPF_EVENT)
+    {
+      zlog_info ("SPF Processing Time(usecs): %ld", total_spf_time);
+      zlog_info ("\t    SPF Time: %ld", spf_time);
+      zlog_info ("\t   InterArea: %ld", ia_time);
+      zlog_info ("\t       Prune: %ld", prune_time);
+      zlog_info ("\tRouteInstall: %ld", rt_time);
+      if (IS_OSPF_ABR (ospf))
+        zlog_info ("\t         ABR: %ld (%d areas)",
+                   abr_time, areas_processed);
+      zlog_info ("Reason(s) for SPF: %s", rbuf);
+    }
+
+  ospf_clear_spf_reason_flags ();
+
+  return 0;
+}
+
+/* Add schedule for SPF calculation.  To avoid frequenst SPF calc, we
+   set timer for SPF calc. */
+void
+ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason)
+{
+  unsigned long delay, elapsed, ht;
+  struct timeval result;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("SPF: calculation timer scheduled");
+
+  /* OSPF instance does not exist. */
+  if (ospf == NULL)
+    return;
+  
+  ospf_spf_set_reason (reason);
+  
+  /* SPF calculation timer is already scheduled. */
+  if (ospf->t_spf_calc)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("SPF: calculation timer is already scheduled: %p",
+                    (void *)ospf->t_spf_calc);
+      return;
+    }
+  
+  /* XXX Monotic timers: we only care about relative time here. */
+  result = tv_sub (recent_relative_time (), ospf->ts_spf);
+  
+  elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
+  ht = ospf->spf_holdtime * ospf->spf_hold_multiplier;
+  
+  if (ht > ospf->spf_max_holdtime)
+    ht = ospf->spf_max_holdtime;
+  
+  /* Get SPF calculation delay time. */
+  if (elapsed < ht)
+    {
+      /* Got an event within the hold time of last SPF. We need to
+       * increase the hold_multiplier, if it's not already at/past
+       * maximum value, and wasn't already increased..
+       */
+      if (ht < ospf->spf_max_holdtime)
+        ospf->spf_hold_multiplier++;
+      
+      /* always honour the SPF initial delay */
+      if ( (ht - elapsed) < ospf->spf_delay)
+        delay = ospf->spf_delay;
+      else
+        delay = ht - elapsed;
+    }
+  else
+    {
+      /* Event is past required hold-time of last SPF */
+      delay = ospf->spf_delay;
+      ospf->spf_hold_multiplier = 1;
+    }
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("SPF: calculation timer delay = %ld", delay);
+
+  zlog_info ("SPF: Scheduled in %ld msec", delay);
+
+  ospf->t_spf_calc =
+    thread_add_timer_msec (master, ospf_spf_calculate_timer, ospf, delay);
+}
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
new file mode 100644 (file)
index 0000000..e33b3e5
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * OSPF calculation.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_OSPF_SPF_H
+#define _QUAGGA_OSPF_SPF_H
+
+/* values for vertex->type */
+#define OSPF_VERTEX_ROUTER  1  /* for a Router-LSA */
+#define OSPF_VERTEX_NETWORK 2  /* for a Network-LSA */
+
+/* values for vertex->flags */
+#define OSPF_VERTEX_PROCESSED      0x01
+
+/* The "root" is the node running the SPF calculation */
+
+/* A router or network in an area */
+struct vertex
+{
+  u_char flags;
+  u_char type;         /* copied from LSA header */
+  struct in_addr id;   /* copied from LSA header */
+  struct lsa_header *lsa; /* Router or Network LSA */
+  int *stat;           /* Link to LSA status. */
+  u_int32_t distance;  /* from root to this vertex */  
+  struct list *parents;                /* list of parents in SPF tree */
+  struct list *children;       /* list of children in SPF tree*/
+};
+
+/* A nexthop taken on the root node to get to this (parent) vertex */
+struct vertex_nexthop
+{
+  struct ospf_interface *oi;   /* output intf on root node */
+  struct in_addr router;       /* router address to send to */
+};
+
+struct vertex_parent
+{
+  struct vertex_nexthop *nexthop; /* link to nexthop info for this parent */
+  struct vertex *parent;       /* parent vertex */
+  int backlink;                        /* index back to parent for router-lsa's */
+};
+
+/* What triggered the SPF ? */
+typedef enum {
+  SPF_FLAG_ROUTER_LSA_INSTALL = 1,
+  SPF_FLAG_NETWORK_LSA_INSTALL,
+  SPF_FLAG_SUMMARY_LSA_INSTALL,
+  SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL,
+  SPF_FLAG_MAXAGE,
+  SPF_FLAG_ABR_STATUS_CHANGE,
+  SPF_FLAG_ASBR_STATUS_CHANGE,
+  SPF_FLAG_CONFIG_CHANGE,
+} ospf_spf_reason_t;
+
+extern void ospf_spf_calculate_schedule (struct ospf *, ospf_spf_reason_t);
+extern void ospf_rtrs_free (struct route_table *);
+
+/* void ospf_spf_calculate_timer_add (); */
+#endif /* _QUAGGA_OSPF_SPF_H */
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
new file mode 100644 (file)
index 0000000..47771a1
--- /dev/null
@@ -0,0 +1,2639 @@
+/*
+ * This is an implementation of RFC3630
+ * Copyright (C) 2001 KDD R&D Laboratories, Inc.
+ * http://www.kddlabs.co.jp/
+ *
+ * Copyright (C) 2012 Orange Labs
+ * http://www.orange.com
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* Add support of RFC7471 */
+/* Add support of RFC5392, RFC6827 */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h"         /* for inet_aton() */
+#include "network.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_vty.h"
+
+/*
+ * Global variable to manage Opaque-LSA/MPLS-TE on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+struct ospf_mpls_te OspfMplsTE;
+
+const char *mode2text[] = { "Disable", "AS", "Area", "Emulate" };
+
+enum oifstate
+{
+  OI_ANY, OI_DOWN, OI_UP
+};
+
+/*------------------------------------------------------------------------*
+ * Followings are initialize/terminate functions for MPLS-TE handling.
+ *------------------------------------------------------------------------*/
+
+static int ospf_mpls_te_new_if (struct interface *ifp);
+static int ospf_mpls_te_del_if (struct interface *ifp);
+static void ospf_mpls_te_ism_change (struct ospf_interface *oi,
+                                     int old_status);
+static void ospf_mpls_te_nsm_change (struct ospf_neighbor *nbr, int old_status);
+static void ospf_mpls_te_config_write_router (struct vty *vty);
+static void ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_mpls_te_lsa_originate_area (void *arg);
+static int ospf_mpls_te_lsa_originate_as (void *arg);
+static struct ospf_lsa *ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa);
+
+static void del_mpls_te_link (void *val);
+static void ospf_mpls_te_register_vty (void);
+
+int
+ospf_mpls_te_init (void)
+{
+  int rc;
+
+  rc = ospf_register_opaque_functab (
+                OSPF_OPAQUE_AREA_LSA,
+                OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA,
+               ospf_mpls_te_new_if,
+               ospf_mpls_te_del_if,
+               ospf_mpls_te_ism_change,
+               ospf_mpls_te_nsm_change,
+               ospf_mpls_te_config_write_router,
+               NULL,/*ospf_mpls_te_config_write_if */
+               NULL,/* ospf_mpls_te_config_write_debug */
+                ospf_mpls_te_show_info,
+                ospf_mpls_te_lsa_originate_area,
+                ospf_mpls_te_lsa_refresh,
+               NULL,/* ospf_mpls_te_new_lsa_hook */
+               NULL /* ospf_mpls_te_del_lsa_hook */);
+  if (rc != 0)
+    {
+      zlog_warn ("ospf_mpls_te_init: Failed to register Traffic Engineering functions");
+      goto out;
+    }
+
+  memset (&OspfMplsTE, 0, sizeof (struct ospf_mpls_te));
+  OspfMplsTE.status = disabled;
+  OspfMplsTE.inter_as = Disable;
+  OspfMplsTE.iflist = list_new ();
+  OspfMplsTE.iflist->del = del_mpls_te_link;
+
+  ospf_mpls_te_register_vty ();
+
+out:
+  return rc;
+}
+
+/* Additional register for RFC5392 support */
+static int
+ospf_mpls_te_register (enum inter_as_mode mode)
+{
+  int rc;
+  u_int8_t scope;
+
+  if (OspfMplsTE.inter_as != Disable)
+    return 0;
+
+  if (mode == AS)
+    scope = OSPF_OPAQUE_AS_LSA;
+  else
+    scope = OSPF_OPAQUE_AREA_LSA;
+
+  rc = ospf_register_opaque_functab (scope,
+                                     OPAQUE_TYPE_INTER_AS_LSA,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     ospf_mpls_te_show_info,
+                                     ospf_mpls_te_lsa_originate_as,
+                                     ospf_mpls_te_lsa_refresh, NULL, NULL);
+
+  if (rc != 0)
+    {
+      zlog_warn ("ospf_router_info_init: Failed to register Inter-AS functions");
+      return rc;
+    }
+
+  return 0;
+}
+
+static int
+ospf_mpls_te_unregister ()
+{
+  u_int8_t scope;
+
+  if (OspfMplsTE.inter_as == Disable)
+    return 0;
+
+  if (OspfMplsTE.inter_as == AS)
+    scope = OSPF_OPAQUE_AS_LSA;
+  else
+    scope = OSPF_OPAQUE_AREA_LSA;
+
+  ospf_delete_opaque_functab (scope, OPAQUE_TYPE_INTER_AS_LSA);
+
+  return 0;
+
+}
+
+void
+ospf_mpls_te_term (void)
+{
+  list_delete (OspfMplsTE.iflist);
+  OspfMplsTE.iflist = NULL;
+
+  ospf_delete_opaque_functab (OSPF_OPAQUE_AREA_LSA,
+                              OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
+  OspfMplsTE.status = disabled;
+
+  ospf_mpls_te_unregister ();
+  OspfMplsTE.inter_as = Disable;
+
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are control functions for MPLS-TE parameters management.
+ *------------------------------------------------------------------------*/
+
+static void
+del_mpls_te_link (void *val)
+{
+  XFREE (MTYPE_OSPF_MPLS_TE, val);
+  return;
+}
+
+u_int32_t
+get_mpls_te_instance_value (void)
+{
+  static u_int32_t seqno = 0;
+
+  if (seqno < MAX_LEGAL_TE_INSTANCE_NUM )
+    seqno += 1;
+  else
+    seqno  = 1; /* Avoid zero. */
+
+  return seqno;
+}
+
+static struct ospf_interface *
+lookup_oi_by_ifp (struct interface *ifp,
+                  struct ospf_area *area, enum oifstate oifstate)
+{
+  struct ospf_interface *oi = NULL;
+  struct route_node *rn;
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      if ((oi = rn->info) == NULL)
+        continue;
+
+      switch (oifstate)
+        {
+        case OI_ANY:
+          break;
+        case OI_DOWN:
+          if (ospf_if_is_enable (oi))
+            continue;
+          break;
+        case OI_UP:
+          if (! ospf_if_is_enable (oi))
+            continue;
+          break;
+        default:
+          zlog_warn ("lookup_oi_by_ifp: Unknown oifstate: %x", oifstate);
+          goto out;
+        }
+
+      if (area == NULL || oi->area == area)
+        return oi;
+    }
+out:
+  return NULL;
+}
+
+static struct mpls_te_link *
+lookup_linkparams_by_ifp (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct mpls_te_link *lp;
+
+  for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+    if (lp->ifp == ifp)
+      return lp;
+
+  return NULL;
+}
+
+static struct mpls_te_link *
+lookup_linkparams_by_instance (struct ospf_lsa *lsa)
+{
+  struct listnode *node;
+  struct mpls_te_link *lp;
+  unsigned int key = GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr));
+
+  for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp))
+    if (lp->instance == key)
+      return lp;
+
+  zlog_warn ("lookup_linkparams_by_instance: Entry not found: key(%x)", key);
+  return NULL;
+}
+
+static void
+ospf_mpls_te_foreach_area (void (*func)
+                           (struct mpls_te_link * lp, opcode_t sched_opcode),
+                           opcode_t sched_opcode)
+{
+  struct listnode *node, *nnode; 
+  struct listnode *node2;
+  struct mpls_te_link *lp;
+  struct ospf_area *area;
+
+  for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+    {
+      /* Skip Inter-AS TEv2 Links */
+      if (IS_INTER_AS (lp->type))
+        continue;
+      if ((area = lp->area) == NULL)
+        continue;
+      if CHECK_FLAG (lp->flags, LPFLG_LOOKUP_DONE) continue;
+
+      if (func != NULL)
+        (* func)(lp, sched_opcode);
+
+      for (node2 = listnextnode (node); node2; node2 = listnextnode (node2))
+        if ((lp = listgetdata (node2)) != NULL)
+          if (lp->area != NULL)
+            if (IPV4_ADDR_SAME (&lp->area->area_id, &area->area_id))
+              SET_FLAG (lp->flags, LPFLG_LOOKUP_DONE);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp))
+    if (lp->area != NULL)
+      UNSET_FLAG (lp->flags, LPFLG_LOOKUP_DONE);
+
+  return;
+}
+
+static void
+set_mpls_te_router_addr (struct in_addr ipv4)
+{
+  OspfMplsTE.router_addr.header.type   = htons (TE_TLV_ROUTER_ADDR);
+  OspfMplsTE.router_addr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  OspfMplsTE.router_addr.value = ipv4;
+  return;
+}
+
+static void
+set_linkparams_link_header (struct mpls_te_link *lp)
+{
+  u_int16_t length = 0;
+
+  /* TE_LINK_SUBTLV_LINK_TYPE */
+  if (ntohs (lp->link_type.header.type) != 0)
+    length += TLV_SIZE (&lp->link_type.header);
+
+  /* TE_LINK_SUBTLV_LINK_ID */
+  if (ntohs (lp->link_id.header.type) != 0)
+    length += TLV_SIZE (&lp->link_id.header);
+
+  /* TE_LINK_SUBTLV_LCLIF_IPADDR */
+  if (lp->lclif_ipaddr.header.type != 0)
+    length += TLV_SIZE (&lp->lclif_ipaddr.header);
+
+  /* TE_LINK_SUBTLV_RMTIF_IPADDR */
+  if (lp->rmtif_ipaddr.header.type != 0)
+    length += TLV_SIZE (&lp->rmtif_ipaddr.header);
+
+  /* TE_LINK_SUBTLV_TE_METRIC */
+  if (ntohs (lp->te_metric.header.type) != 0)
+    length += TLV_SIZE (&lp->te_metric.header);
+
+  /* TE_LINK_SUBTLV_MAX_BW */
+  if (ntohs (lp->max_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->max_bw.header);
+
+  /* TE_LINK_SUBTLV_MAX_RSV_BW */
+  if (ntohs (lp->max_rsv_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->max_rsv_bw.header);
+
+  /* TE_LINK_SUBTLV_UNRSV_BW */
+  if (ntohs (lp->unrsv_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->unrsv_bw.header);
+
+  /* TE_LINK_SUBTLV_RSC_CLSCLR */
+  if (ntohs (lp->rsc_clsclr.header.type) != 0)
+    length += TLV_SIZE (&lp->rsc_clsclr.header);
+
+  /* TE_LINK_SUBTLV_LLRI */
+  if (ntohs (lp->llri.header.type) != 0)
+    length += TLV_SIZE (&lp->llri.header);
+
+  /* TE_LINK_SUBTLV_RIP */
+  if (ntohs (lp->rip.header.type) != 0)
+    length += TLV_SIZE (&lp->rip.header);
+
+  /* TE_LINK_SUBTLV_RAS */
+  if (ntohs (lp->ras.header.type) != 0)
+    length += TLV_SIZE (&lp->ras.header);
+
+  /* TE_LINK_SUBTLV_LRRID */
+  if (ntohs (lp->lrrid.header.type) != 0)
+    length += TLV_SIZE (&lp->lrrid.header);
+
+  /* TE_LINK_SUBTLV_AV_DELAY */
+  if (ntohs (lp->av_delay.header.type) != 0)
+    length += TLV_SIZE (&lp->av_delay.header);
+
+  /* TE_LINK_SUBTLV_MM_DELAY */
+  if (ntohs (lp->mm_delay.header.type) != 0)
+    length += TLV_SIZE (&lp->mm_delay.header);
+
+  /* TE_LINK_SUBTLV_DELAY_VAR */
+  if (ntohs (lp->delay_var.header.type) != 0)
+    length += TLV_SIZE (&lp->delay_var.header);
+
+  /* TE_LINK_SUBTLV_PKT_LOSS */
+  if (ntohs (lp->pkt_loss.header.type) != 0)
+    length += TLV_SIZE (&lp->pkt_loss.header);
+
+  /* TE_LINK_SUBTLV_RES_BW */
+  if (ntohs (lp->res_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->res_bw.header);
+
+  /* TE_LINK_SUBTLV_AVA_BW */
+  if (ntohs (lp->ava_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->ava_bw.header);
+
+  /* TE_LINK_SUBTLV_USE_BW */
+  if (ntohs (lp->use_bw.header.type) != 0)
+    length += TLV_SIZE (&lp->use_bw.header);
+
+  lp->link_header.header.type   = htons (TE_TLV_LINK);
+  lp->link_header.header.length = htons (length);
+
+  return;
+}
+
+static void
+set_linkparams_link_type (struct ospf_interface *oi, struct mpls_te_link *lp)
+{
+  lp->link_type.header.type   = htons (TE_LINK_SUBTLV_LINK_TYPE);
+  lp->link_type.header.length = htons (TE_LINK_SUBTLV_TYPE_SIZE);
+
+  switch (oi->type)
+    {
+    case OSPF_IFTYPE_POINTOPOINT:
+      lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_PTP;
+      break;
+    case OSPF_IFTYPE_BROADCAST:
+    case OSPF_IFTYPE_NBMA:
+      lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_MA;
+      break;
+    default:
+      /* Not supported yet. *//* XXX */
+      lp->link_type.header.type = htons (0);
+      break;
+    }
+  return;
+}
+
+static void
+set_linkparams_link_id (struct ospf_interface *oi, struct mpls_te_link *lp)
+{
+  struct ospf_neighbor *nbr;
+  int done = 0;
+
+  lp->link_id.header.type   = htons (TE_LINK_SUBTLV_LINK_ID);
+  lp->link_id.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+
+  /*
+   * The Link ID is identical to the contents of the Link ID field
+   * in the Router LSA for these link types.
+   */
+  switch (oi->type)
+    {
+    case OSPF_IFTYPE_POINTOPOINT:
+      /* Take the router ID of the neighbor. */
+      if ((nbr = ospf_nbr_lookup_ptop (oi)) && nbr->state == NSM_Full)
+        {
+          lp->link_id.value = nbr->router_id;
+          done = 1;
+        }
+      break;
+    case OSPF_IFTYPE_BROADCAST:
+    case OSPF_IFTYPE_NBMA:
+      /* Take the interface address of the designated router. */
+      if ((nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi))) == NULL)
+        break;
+
+      if (nbr->state == NSM_Full
+      || (IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))
+      &&  ospf_nbr_count (oi, NSM_Full) > 0))
+        {
+          lp->link_id.value = DR (oi);
+          done = 1;
+        }
+      break;
+    default:
+      /* Not supported yet. *//* XXX */
+      lp->link_id.header.type = htons (0);
+      break;
+    }
+
+  if (! done)
+    {
+      struct in_addr mask;
+      masklen2ip (oi->address->prefixlen, &mask);
+      lp->link_id.value.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;
+     }
+  return;
+}
+
+static void
+set_linkparams_lclif_ipaddr (struct mpls_te_link *lp, struct in_addr lclif)
+{
+
+  lp->lclif_ipaddr.header.type = htons (TE_LINK_SUBTLV_LCLIF_IPADDR);
+  lp->lclif_ipaddr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->lclif_ipaddr.value[0] = lclif;
+  return;
+}
+
+static void
+set_linkparams_rmtif_ipaddr (struct mpls_te_link *lp, struct in_addr rmtif)
+{
+
+  lp->rmtif_ipaddr.header.type = htons (TE_LINK_SUBTLV_RMTIF_IPADDR);
+  lp->rmtif_ipaddr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->rmtif_ipaddr.value[0] = rmtif;
+  return;
+}
+
+static void
+set_linkparams_te_metric (struct mpls_te_link *lp, u_int32_t te_metric)
+{
+  lp->te_metric.header.type   = htons (TE_LINK_SUBTLV_TE_METRIC);
+  lp->te_metric.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->te_metric.value = htonl (te_metric);
+  return;
+}
+
+static void
+set_linkparams_max_bw (struct mpls_te_link *lp, float fp)
+{
+  lp->max_bw.header.type   = htons (TE_LINK_SUBTLV_MAX_BW);
+  lp->max_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->max_bw.value = htonf (fp);
+  return;
+}
+
+static void
+set_linkparams_max_rsv_bw (struct mpls_te_link *lp, float fp)
+{
+  lp->max_rsv_bw.header.type   = htons (TE_LINK_SUBTLV_MAX_RSV_BW);
+  lp->max_rsv_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->max_rsv_bw.value = htonf (fp);
+  return;
+}
+
+static void
+set_linkparams_unrsv_bw (struct mpls_te_link *lp, int priority, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  lp->unrsv_bw.header.type   = htons (TE_LINK_SUBTLV_UNRSV_BW);
+  lp->unrsv_bw.header.length = htons (TE_LINK_SUBTLV_UNRSV_SIZE);
+  lp->unrsv_bw.value [priority] = htonf (fp);
+  return;
+}
+
+static void
+set_linkparams_rsc_clsclr (struct mpls_te_link *lp, u_int32_t classcolor)
+{
+  lp->rsc_clsclr.header.type   = htons (TE_LINK_SUBTLV_RSC_CLSCLR);
+  lp->rsc_clsclr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->rsc_clsclr.value = htonl (classcolor);
+  return;
+}
+
+static void
+set_linkparams_inter_as (struct mpls_te_link *lp, struct in_addr addr,
+                         u_int32_t as)
+{
+
+  /* Set the Remote ASBR IP address and then the associated AS number */
+  lp->rip.header.type = htons (TE_LINK_SUBTLV_RIP);
+  lp->rip.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->rip.value = addr;
+
+  lp->ras.header.type = htons (TE_LINK_SUBTLV_RAS);
+  lp->ras.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->ras.value = htonl (as);
+}
+
+static void
+unset_linkparams_inter_as (struct mpls_te_link *lp)
+{
+
+  /* Reset the Remote ASBR IP address and then the associated AS number */
+  lp->rip.header.type = htons (0);
+  lp->rip.header.length = htons (0);
+  lp->rip.value.s_addr = htonl (0);
+
+  lp->ras.header.type = htons (0);
+  lp->ras.header.length = htons (0);
+  lp->ras.value = htonl (0);
+}
+
+void
+set_linkparams_llri (struct mpls_te_link *lp, u_int32_t local,
+                     u_int32_t remote)
+{
+
+  lp->llri.header.type = htons (TE_LINK_SUBTLV_LLRI);
+  lp->llri.header.length = htons (TE_LINK_SUBTLV_LLRI_SIZE);
+  lp->llri.local = htonl (local);
+  lp->llri.remote = htonl (remote);
+}
+
+void
+set_linkparams_lrrid (struct mpls_te_link *lp, struct in_addr local,
+                           struct in_addr remote)
+{
+
+  lp->lrrid.header.type = htons (TE_LINK_SUBTLV_LRRID);
+  lp->lrrid.header.length = htons (TE_LINK_SUBTLV_LRRID_SIZE);
+  lp->lrrid.local.s_addr = local.s_addr;
+  lp->lrrid.remote.s_addr = remote.s_addr;
+}
+
+static void
+set_linkparams_av_delay (struct mpls_te_link *lp, u_int32_t delay, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  lp->av_delay.header.type = htons (TE_LINK_SUBTLV_AV_DELAY);
+  lp->av_delay.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  tmp = delay & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  lp->av_delay.value = htonl (tmp);
+  return;
+}
+
+static void
+set_linkparams_mm_delay (struct mpls_te_link *lp, u_int32_t low, u_int32_t high, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  lp->mm_delay.header.type = htons (TE_LINK_SUBTLV_MM_DELAY);
+  lp->mm_delay.header.length = htons (TE_LINK_SUBTLV_MM_DELAY_SIZE);
+  tmp = low & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  lp->mm_delay.low = htonl (tmp);
+  lp->mm_delay.high = htonl (high);
+  return;
+}
+
+static void
+set_linkparams_delay_var (struct mpls_te_link *lp, u_int32_t jitter)
+{
+  /* Note that TLV-length field is the size of array. */
+  lp->delay_var.header.type = htons (TE_LINK_SUBTLV_DELAY_VAR);
+  lp->delay_var.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->delay_var.value = htonl (jitter & TE_EXT_MASK);
+  return;
+}
+
+static void
+set_linkparams_pkt_loss (struct mpls_te_link *lp, u_int32_t loss, u_char anormal)
+{
+  u_int32_t tmp;
+  /* Note that TLV-length field is the size of array. */
+  lp->pkt_loss.header.type = htons (TE_LINK_SUBTLV_PKT_LOSS);
+  lp->pkt_loss.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  tmp = loss & TE_EXT_MASK;
+  if (anormal)
+    tmp |= TE_EXT_ANORMAL;
+  lp->pkt_loss.value = htonl (tmp);
+  return;
+}
+
+static void
+set_linkparams_res_bw (struct mpls_te_link *lp, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  lp->res_bw.header.type = htons (TE_LINK_SUBTLV_RES_BW);
+  lp->res_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->res_bw.value = htonf (fp);
+  return;
+}
+
+static void
+set_linkparams_ava_bw (struct mpls_te_link *lp, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  lp->ava_bw.header.type = htons (TE_LINK_SUBTLV_AVA_BW);
+  lp->ava_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->ava_bw.value = htonf (fp);
+  return;
+}
+
+static void
+set_linkparams_use_bw (struct mpls_te_link *lp, float fp)
+{
+  /* Note that TLV-length field is the size of array. */
+  lp->use_bw.header.type = htons (TE_LINK_SUBTLV_USE_BW);
+  lp->use_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE);
+  lp->use_bw.value = htonf (fp);
+  return;
+}
+
+/* Update TE parameters from Interface */
+static void
+update_linkparams(struct mpls_te_link *lp)
+{
+  int i;
+  struct interface *ifp;
+
+  /* Get the Interface structure */
+  if ((ifp = lp->ifp) == NULL)
+    {
+      zlog_warn("OSPF MPLS-TE: Abort update TE parameters: no interface associated to Link Parameters");
+      return;
+    }
+  if (!HAS_LINK_PARAMS(ifp))
+    {
+      zlog_warn("OSPF MPLS-TE: Abort update TE parameters: no Link Parameters for interface");
+      return;
+    }
+
+  /* RFC3630 metrics */
+  if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP))
+    set_linkparams_rsc_clsclr (lp, ifp->link_params->admin_grp);
+  else
+    TLV_TYPE(lp->rsc_clsclr) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW))
+    set_linkparams_max_bw (lp, ifp->link_params->max_bw);
+  else
+    TLV_TYPE(lp->max_bw) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW))
+    set_linkparams_max_rsv_bw (lp, ifp->link_params->max_rsv_bw);
+  else
+    TLV_TYPE(lp->max_rsv_bw) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW))
+    for (i = 0; i < MAX_CLASS_TYPE; i++)
+      set_linkparams_unrsv_bw (lp, i, ifp->link_params->unrsv_bw[i]);
+  else
+    TLV_TYPE(lp->unrsv_bw) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_TE))
+    set_linkparams_te_metric(lp, ifp->link_params->te_metric);
+  else
+    TLV_TYPE(lp->te_metric) = 0;
+
+  /* TE metric Extensions */
+  if (IS_PARAM_SET(ifp->link_params, LP_DELAY))
+    set_linkparams_av_delay(lp, ifp->link_params->av_delay, 0);
+  else
+    TLV_TYPE(lp->av_delay) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY))
+    set_linkparams_mm_delay(lp, ifp->link_params->min_delay, ifp->link_params->max_delay, 0);
+  else
+    TLV_TYPE(lp->mm_delay) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR))
+    set_linkparams_delay_var(lp, ifp->link_params->delay_var);
+  else
+    TLV_TYPE(lp->delay_var) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS))
+    set_linkparams_pkt_loss(lp, ifp->link_params->pkt_loss, 0);
+  else
+    TLV_TYPE(lp->pkt_loss) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_RES_BW))
+    set_linkparams_res_bw(lp, ifp->link_params->res_bw);
+  else
+    TLV_TYPE(lp->res_bw) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW))
+    set_linkparams_ava_bw(lp, ifp->link_params->ava_bw);
+  else
+    TLV_TYPE(lp->ava_bw) = 0;
+
+  if (IS_PARAM_SET(ifp->link_params, LP_USE_BW))
+    set_linkparams_use_bw(lp, ifp->link_params->use_bw);
+  else
+    TLV_TYPE(lp->use_bw) = 0;
+
+  /* RFC5392 */
+  if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS))
+    {
+      /* Flush LSA if it engaged and was previously a STD_TE one */
+      if (IS_STD_TE(lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED))
+        {
+          if (IS_DEBUG_OSPF_TE)
+            zlog_debug("OSPF MPLS-TE Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]",
+              ifp->name, lp->flags, lp->type);
+
+          ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+          /* Then, switch it to INTER-AS */
+          if (OspfMplsTE.inter_as == AS)
+            lp->flags = INTER_AS | FLOOD_AS;
+          else
+            {
+              lp->flags = INTER_AS | FLOOD_AREA;
+              lp->area = ospf_area_lookup_by_area_id (ospf_lookup(), OspfMplsTE.interas_areaid);
+            }
+        }
+      set_linkparams_inter_as(lp, ifp->link_params->rmt_ip, ifp->link_params->rmt_as);
+    }
+  else
+    {
+      if (IS_DEBUG_OSPF_TE)
+        zlog_debug("OSPF MPLS-TE Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]",
+          ifp->name, lp->flags, lp->type);
+
+      /* reset inter-as TE params */
+      /* Flush LSA if it engaged and was previously an INTER_AS one */
+      if (IS_INTER_AS(lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED))
+        {
+          ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+          /* Then, switch it to Standard TE */
+          lp->flags = STD_TE | FLOOD_AREA;
+        }
+      unset_linkparams_inter_as (lp);
+    }
+}
+
+static void
+initialize_linkparams (struct mpls_te_link *lp)
+{
+  struct interface *ifp = lp->ifp;
+  struct ospf_interface *oi;
+
+  if (IS_DEBUG_OSPF_TE)
+    zlog_debug("MPLS-TE(initialize_linkparams) Initialize Link Parameters for interface %s",
+                ifp->name);
+
+  if ((oi = lookup_oi_by_ifp (ifp, NULL, OI_ANY)) == NULL)
+    {
+      zlog_warn("MPLS-TE(initialize_linkparams) Could not find corresponding OSPF Interface for %s",
+                 ifp->name);
+      return;
+    }
+
+  /*
+   * Try to set initial values those can be derived from
+   * zebra-interface information.
+   */
+  set_linkparams_link_type (oi, lp);
+
+  /* Set local IP addr */
+  set_linkparams_lclif_ipaddr (lp, oi->address->u.prefix4);
+
+  /* Set Remote IP addr if Point to Point Interface */
+  if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP)
+    {
+      struct prefix *pref = CONNECTED_PREFIX(oi->connected);
+      if (pref != NULL)
+        set_linkparams_rmtif_ipaddr(lp, pref->u.prefix4);
+    }
+
+  /* Keep Area information in combination with link parameters. */
+  lp->area = oi->area;
+
+  return;
+}
+
+static int
+is_mandated_params_set (struct mpls_te_link *lp)
+{
+  int rc = 0;
+
+  if (ntohs (OspfMplsTE.router_addr.header.type) == 0)
+    {
+      zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Router Address");
+    goto out;
+    }
+
+  if (ntohs (lp->link_type.header.type) == 0)
+    {
+      zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Link Type");
+    goto out;
+    }
+
+  if (!IS_INTER_AS (lp->type) && (ntohs (lp->link_id.header.type) == 0))
+    {
+      zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Link ID");
+    goto out;
+    }
+
+  rc = 1;
+out:
+  return rc;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are callback functions against generic Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+
+static int
+ospf_mpls_te_new_if (struct interface *ifp)
+{
+  struct mpls_te_link *new;
+  int rc = -1;
+
+  if (IS_DEBUG_OSPF_TE)
+    zlog_debug ("MPLS-TE(ospf_mpls_te_new_if) Add new %s interface %s to MPLS-TE list",
+                 ifp->link_params ? "Active" : "Inactive", ifp->name);
+
+  if (lookup_linkparams_by_ifp (ifp) != NULL)
+    {
+      zlog_warn ("ospf_mpls_te_new_if: ifp(%p) already in use?", (void *)ifp);
+      rc = 0; /* Do nothing here. */
+      goto out;
+    }
+
+  new = XCALLOC (MTYPE_OSPF_MPLS_TE, sizeof (struct mpls_te_link));
+  if (new == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_new_if: XMALLOC: %s", safe_strerror (errno));
+      goto out;
+    }
+
+  new->instance = get_mpls_te_instance_value ();
+  new->ifp = ifp;
+  /* By default TE-Link is RFC3630 compatible flooding in Area and not active */
+  /* This default behavior will be adapted with call to ospf_mpls_te_update_if() */
+  new->type = STD_TE | FLOOD_AREA;
+  new->flags = LPFLG_LSA_INACTIVE;
+
+  /* Initialize Link Parameters from Interface */
+  initialize_linkparams(new);
+
+  /* Set TE Parameters from Interface */
+  update_linkparams(new);
+
+  /* Add Link Parameters structure to the list */
+  listnode_add (OspfMplsTE.iflist, new);
+
+  if (IS_DEBUG_OSPF_TE)
+    zlog_debug("OSPF MPLS-TE New IF: Add new LP context for %s[%d/%d]",
+      ifp->name, new->flags, new->type);
+
+  /* Schedule Opaque-LSA refresh. *//* XXX */
+
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+ospf_mpls_te_del_if (struct interface *ifp)
+{
+  struct mpls_te_link *lp;
+  int rc = -1;
+
+  if ((lp = lookup_linkparams_by_ifp (ifp)) != NULL)
+    {
+      struct list *iflist = OspfMplsTE.iflist;
+
+      /* Dequeue listnode entry from the list. */
+      listnode_delete (iflist, lp);
+
+      /* Avoid misjudgement in the next lookup. */
+      if (listcount (iflist) == 0)
+        iflist->head = iflist->tail = NULL;
+
+      XFREE (MTYPE_OSPF_MPLS_TE, lp);
+    }
+
+  /* Schedule Opaque-LSA refresh. *//* XXX */
+
+  rc = 0;
+/*out:*/
+  return rc;
+}
+
+/* Main initialization / update function of the MPLS TE Link context */
+
+/* Call when interface TE Link parameters are modified */
+void
+ospf_mpls_te_update_if (struct interface *ifp)
+{
+  struct mpls_te_link *lp;
+
+  if (IS_DEBUG_OSPF_TE)
+    zlog_debug ("OSPF MPLS-TE: Update LSA parameters for interface %s [%s]",
+        ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF");
+
+  /* Get Link context from interface */
+  if ((lp = lookup_linkparams_by_ifp(ifp)) == NULL)
+    {
+      zlog_warn ("OSPF MPLS-TE Update: Did not find Link Parameters context for interface %s", ifp->name);
+      return;
+    }
+
+  /* Fulfill MPLS-TE Link TLV from Interface TE Link parameters */
+  if (HAS_LINK_PARAMS(ifp))
+    {
+      SET_FLAG (lp->flags, LPFLG_LSA_ACTIVE);
+
+      /* Update TE parameters */
+      update_linkparams(lp);
+
+      /* Finally Re-Originate or Refresh Opaque LSA if MPLS_TE is enabled */
+      if (OspfMplsTE.status == enabled)
+        if (lp->area != NULL)
+          {
+            if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+                ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA);
+            else
+                ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA);
+          }
+    }
+  else
+    {
+      /* If MPLS TE is disable on this interface, flush LSA if it is already engaged */
+      if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+          ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+      else
+        /* Reset Activity flag */
+        lp->flags = LPFLG_LSA_INACTIVE;
+    }
+
+  return;
+}
+
+static void
+ospf_mpls_te_ism_change (struct ospf_interface *oi, int old_state)
+{
+  struct te_link_subtlv_link_type old_type;
+  struct te_link_subtlv_link_id   old_id;
+  struct mpls_te_link *lp;
+
+  if ((lp = lookup_linkparams_by_ifp (oi->ifp)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_ism_change: Cannot get linkparams from OI(%s)?", IF_NAME (oi));
+      goto out;
+    }
+
+  if (oi->area == NULL || oi->area->ospf == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?", IF_NAME (oi));
+      goto out;
+    }
+#ifdef notyet
+  if ((lp->area != NULL
+  &&   ! IPV4_ADDR_SAME (&lp->area->area_id, &oi->area->area_id))
+  || (lp->area != NULL && oi->area == NULL))
+    {
+      /* How should we consider this case? */
+      zlog_warn ("MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs",
+         IF_NAME (oi), oi->area ? inet_ntoa (oi->area->area_id) : "N/A");
+      ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+    }
+#endif
+  /* Keep Area information in combination with linkparams. */
+  lp->area = oi->area;
+
+  /* Keep interface MPLS-TE status */
+  lp->flags = HAS_LINK_PARAMS(oi->ifp);
+
+  switch (oi->state)
+    {
+    case ISM_PointToPoint:
+    case ISM_DROther:
+    case ISM_Backup:
+    case ISM_DR:
+      old_type = lp->link_type;
+      old_id   = lp->link_id;
+
+      /* Set Link type, Link ID, Local and Remote IP addr */
+      set_linkparams_link_type (oi, lp);
+      set_linkparams_link_id (oi, lp);
+      set_linkparams_lclif_ipaddr (lp, oi->address->u.prefix4);
+
+      if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP)
+        {
+          struct prefix *pref = CONNECTED_PREFIX(oi->connected);
+          if (pref != NULL)
+            set_linkparams_rmtif_ipaddr(lp, pref->u.prefix4);
+        }
+
+      /* Update TE parameters */
+      update_linkparams(lp);
+
+      /* Try to Schedule LSA */
+      if ((ntohs (old_type.header.type) != ntohs (lp->link_type.header.type)
+      ||   old_type.link_type.value     != lp->link_type.link_type.value)
+      ||  (ntohs (old_id.header.type)   != ntohs (lp->link_id.header.type)
+              || ntohl (old_id.value.s_addr) !=
+              ntohl (lp->link_id.value.s_addr)))
+        {
+          if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+            ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA);
+          else
+            ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA);
+
+        }
+      break;
+    default:
+      lp->link_type.header.type = htons (0);
+      lp->link_id.header.type   = htons (0);
+
+      if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+        ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+      break;
+    }
+
+out:
+  return;
+
+}
+
+static void
+ospf_mpls_te_nsm_change (struct ospf_neighbor *nbr, int old_state)
+{
+  /* Nothing to do here */
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are OSPF protocol processing functions for MPLS-TE.
+ *------------------------------------------------------------------------*/
+
+static void
+build_tlv_header (struct stream *s, struct te_tlv_header *tlvh)
+{
+  stream_put (s, tlvh, sizeof (struct te_tlv_header));
+  return;
+}
+
+static void
+build_router_tlv (struct stream *s)
+{
+  struct te_tlv_header *tlvh = &OspfMplsTE.router_addr.header;
+  if (ntohs (tlvh->type) != 0)
+    {
+      build_tlv_header (s, tlvh);
+      stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh));
+    }
+  return;
+}
+
+static void
+build_link_subtlv (struct stream *s, struct te_tlv_header *tlvh)
+    {
+
+  if ((tlvh != NULL) && (ntohs (tlvh->type) != 0))
+    {
+      build_tlv_header (s, tlvh);
+      stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh));
+    }
+  return;
+}
+
+static void
+build_link_tlv (struct stream *s, struct mpls_te_link *lp)
+{
+  set_linkparams_link_header (lp);
+  build_tlv_header (s, &lp->link_header.header);
+
+  build_link_subtlv (s, &lp->link_type.header);
+  build_link_subtlv (s, &lp->link_id.header);
+  build_link_subtlv (s, &lp->lclif_ipaddr.header);
+  build_link_subtlv (s, &lp->rmtif_ipaddr.header);
+  build_link_subtlv (s, &lp->te_metric.header);
+  build_link_subtlv (s, &lp->max_bw.header);
+  build_link_subtlv (s, &lp->max_rsv_bw.header);
+  build_link_subtlv (s, &lp->unrsv_bw.header);
+  build_link_subtlv (s, &lp->rsc_clsclr.header);
+  build_link_subtlv (s, &lp->lrrid.header);
+  build_link_subtlv (s, &lp->llri.header);
+  build_link_subtlv (s, &lp->rip.header);
+  build_link_subtlv (s, &lp->ras.header);
+  build_link_subtlv (s, &lp->av_delay.header);
+  build_link_subtlv (s, &lp->mm_delay.header);
+  build_link_subtlv (s, &lp->delay_var.header);
+  build_link_subtlv (s, &lp->pkt_loss.header);
+  build_link_subtlv (s, &lp->res_bw.header);
+  build_link_subtlv (s, &lp->ava_bw.header);
+  build_link_subtlv (s, &lp->res_bw.header);
+
+  return;
+}
+
+static void
+ospf_mpls_te_lsa_body_set (struct stream *s, struct mpls_te_link *lp)
+{
+  /*
+   * The router address TLV is type 1, and ...
+   *                                      It must appear in exactly one
+   * Traffic Engineering LSA originated by a router.
+   */
+  build_router_tlv (s);
+
+  /*
+   * Only one Link TLV shall be carried in each LSA, allowing for fine
+   * granularity changes in topology.
+   */
+  build_link_tlv (s, lp);
+  return;
+}
+
+/* Create new opaque-LSA. */
+static struct ospf_lsa *
+ospf_mpls_te_lsa_new (struct ospf_area *area, struct mpls_te_link *lp)
+{
+  struct stream *s;
+  struct lsa_header *lsah;
+  struct ospf_lsa *new = NULL;
+  u_char options, lsa_type = 0;
+  struct in_addr lsa_id;
+  u_int32_t tmp;
+  u_int16_t length;
+
+  /* Create a stream for LSA. */
+  if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_new: stream_new() ?");
+      goto out;
+    }
+  lsah = (struct lsa_header *) STREAM_DATA (s);
+
+  options = OSPF_OPTION_O;      /* Don't forget this :-) */
+
+  /* Set opaque-LSA header fields depending of the type of RFC */
+  if (IS_INTER_AS (lp->type))
+    {
+      if IS_FLOOD_AS (lp->type)
+        {
+          options |= OSPF_OPTION_E;     /* Enable AS external as we flood Inter-AS with Opaque Type 11 */
+          lsa_type = OSPF_OPAQUE_AS_LSA;
+        }
+      else
+        {
+          options |= LSA_OPTIONS_GET (area);    /* Get area default option */
+          options |= LSA_OPTIONS_NSSA_GET (area);
+          lsa_type = OSPF_OPAQUE_AREA_LSA;
+        }
+      tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance);
+      lsa_id.s_addr = htonl (tmp);
+
+      struct ospf *top = ospf_lookup ();
+
+      lsa_header_set (s, options, lsa_type, lsa_id, top->router_id);
+    }
+  else
+    {
+      options |= LSA_OPTIONS_GET (area);        /* Get area default option */
+      options |= LSA_OPTIONS_NSSA_GET (area);
+      lsa_type = OSPF_OPAQUE_AREA_LSA;
+      tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance);
+      lsa_id.s_addr = htonl (tmp);
+      lsa_header_set (s, options, lsa_type, lsa_id, area->ospf->router_id);
+    }
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    zlog_debug ("LSA[Type%d:%s]: Create an Opaque-LSA/MPLS-TE instance",
+                lsa_type, inet_ntoa (lsa_id));
+
+  /* Set opaque-LSA body fields. */
+  ospf_mpls_te_lsa_body_set (s, lp);
+
+  /* Set length. */
+  length = stream_get_endp (s);
+  lsah->length = htons (length);
+
+  /* Now, create an OSPF LSA instance. */
+  if ((new = ospf_lsa_new ()) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_new: ospf_lsa_new() ?");
+      stream_free (s);
+      goto out;
+    }
+  if ((new->data = ospf_lsa_data_new (length)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_new: ospf_lsa_data_new() ?");
+      ospf_lsa_unlock (&new);
+      new = NULL;
+      stream_free (s);
+      goto out;
+    }
+
+  new->area = area;
+  SET_FLAG (new->flags, OSPF_LSA_SELF);
+  memcpy (new->data, lsah, length);
+  stream_free (s);
+
+out:
+  return new;
+}
+
+static int
+ospf_mpls_te_lsa_originate1 (struct ospf_area *area, struct mpls_te_link *lp)
+{
+  struct ospf_lsa *new;
+  int rc = -1;
+
+  /* Create new Opaque-LSA/MPLS-TE instance. */
+  if ((new = ospf_mpls_te_lsa_new (area, lp)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_originate1: ospf_mpls_te_lsa_new() ?");
+      goto out;
+    }
+
+  /* Install this LSA into LSDB. */
+  if (ospf_lsa_install (area->ospf, NULL/*oi*/, new) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_originate1: ospf_lsa_install() ?");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Now this link-parameter entry has associated LSA. */
+  SET_FLAG (lp->flags, LPFLG_LSA_ENGAGED);
+  /* Update new LSA origination count. */
+  area->ospf->lsa_originate_count++;
+
+  /* Flood new LSA through area. */
+  ospf_flood_through_area (area, NULL/*nbr*/, new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      char area_id[INET_ADDRSTRLEN];
+      strcpy (area_id, inet_ntoa (area->area_id));
+      zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/MPLS-TE: Area(%s), Link(%s)",
+                   new->data->type, inet_ntoa (new->data->id), area_id, lp->ifp->name);
+      ospf_lsa_header_dump (new->data);
+    }
+
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+ospf_mpls_te_lsa_originate_area (void *arg)
+{
+  struct ospf_area *area = (struct ospf_area *) arg;
+  struct listnode *node, *nnode;
+  struct mpls_te_link *lp;
+  int rc = -1;
+
+  if (OspfMplsTE.status == disabled)
+    {
+      zlog_info ("ospf_mpls_te_lsa_originate_area: MPLS-TE is disabled now.");
+      rc = 0; /* This is not an error case. */
+      goto out;
+    }
+
+  for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+    {
+      /* Process only enabled LSA with area scope flooding */
+      if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE) || IS_FLOOD_AS (lp->type))
+        continue;
+
+      if (lp->area == NULL)
+        continue;
+
+      if (! IPV4_ADDR_SAME (&lp->area->area_id, &area->area_id))
+        continue;
+
+      if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+        {
+          if CHECK_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH)
+            {
+              UNSET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH);
+              zlog_warn ("OSPF MPLS-TE (ospf_mpls_te_lsa_originate_area): Refresh instead of Originate");
+              ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA);
+            }
+          continue;
+        }
+      if (! is_mandated_params_set (lp))
+        {
+          zlog_warn ("ospf_mpls_te_lsa_originate_area: Link(%s) lacks some mandated MPLS-TE parameters.",
+              lp->ifp ? lp->ifp->name : "?");
+          continue;
+        }
+
+      /* Ok, let's try to originate an LSA for this area and Link. */
+      if (IS_DEBUG_OSPF_TE)
+        zlog_debug ("MPLS-TE(ospf_mpls_te_lsa_originate_area) Let's finally reoriginate the LSA %d through the Area %s for Link %s",
+                     lp->instance, inet_ntoa (area->area_id), lp->ifp ? lp->ifp->name : "?");
+      if (ospf_mpls_te_lsa_originate1 (area, lp) != 0)
+        goto out;
+    }
+
+  rc = 0;
+out:
+  return rc;
+}
+
+static int
+ospf_mpls_te_lsa_originate2 (struct ospf *top, struct mpls_te_link *lp)
+{
+  struct ospf_lsa *new;
+  int rc = -1;
+
+  /* Create new Opaque-LSA/Inter-AS instance. */
+  if ((new = ospf_mpls_te_lsa_new (NULL, lp)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_originate2: ospf_router_info_lsa_new() ?");
+      goto out;
+    }
+
+  /* Install this LSA into LSDB. */
+  if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_originate2: ospf_lsa_install() ?");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Now this Router Info parameter entry has associated LSA. */
+  SET_FLAG (lp->flags, LPFLG_LSA_ENGAGED);
+  /* Update new LSA origination count. */
+  top->lsa_originate_count++;
+
+  /* Flood new LSA through AS. */
+  ospf_flood_through_as (top, NULL /*nbr */ , new);
+
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/MPLS-TE Inter-AS",
+                  new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+  rc = 0;
+out:return rc;
+}
+
+static int
+ospf_mpls_te_lsa_originate_as (void *arg)
+{
+  struct ospf *top;
+  struct ospf_area *area;
+  struct listnode *node, *nnode;
+  struct mpls_te_link *lp;
+  int rc = -1;
+
+  if ((OspfMplsTE.status == disabled) || (OspfMplsTE.inter_as == Disable))
+    {
+      zlog_info
+        ("ospf_mpls_te_lsa_originate_as: MPLS-TE Inter-AS is disabled for now.");
+      rc = 0;                   /* This is not an error case. */
+      goto out;
+    }
+
+  for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+    {
+      /* Process only enabled INTER_AS Links or Pseudo-Links */
+      if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE) || !IS_INTER_AS (lp->type))
+        continue;
+
+      if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+        {
+          if CHECK_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH)
+            {
+              UNSET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH);
+              ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA);
+            }
+          continue;
+        }
+      if (!is_mandated_params_set (lp))
+        {
+          zlog_warn ("ospf_mpls_te_lsa_originate_as: Link(%s) lacks some mandated MPLS-TE parameters.",
+             lp->ifp ? lp->ifp->name : "?");
+          continue;
+        }
+
+      /* Ok, let's try to originate an LSA for this AS and Link. */
+      if (IS_DEBUG_OSPF_TE)
+        zlog_debug ("MPLS-TE(ospf_mpls_te_lsa_originate_as) Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s",
+                    lp->instance, IS_FLOOD_AS (lp->type) ? "AS" : "Area", lp->ifp ? lp->ifp->name : "Unknown");
+
+      if (IS_FLOOD_AS (lp->type))
+        {
+          top = (struct ospf *) arg;
+          rc = ospf_mpls_te_lsa_originate2 (top, lp);
+        }
+      else
+        {
+          area = (struct ospf_area *) arg;
+          rc = ospf_mpls_te_lsa_originate1 (area, lp);
+        }
+    }
+
+  rc = 0;
+out:return rc;
+}
+
+static struct ospf_lsa *
+ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa)
+{
+  struct mpls_te_link *lp;
+  struct ospf_area *area = lsa->area;
+  struct ospf *top;
+  struct ospf_lsa *new = NULL;
+
+  if (OspfMplsTE.status == disabled)
+    {
+      /*
+       * This LSA must have flushed before due to MPLS-TE status change.
+       * It seems a slip among routers in the routing domain.
+       */
+      zlog_info ("ospf_mpls_te_lsa_refresh: MPLS-TE is disabled now.");
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */
+    }
+
+  /* At first, resolve lsa/lp relationship. */
+  if ((lp = lookup_linkparams_by_instance (lsa)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_refresh: Invalid parameter?");
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */
+    }
+
+  /* Check if lp was not disable in the interval */
+  if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE))
+    {
+      zlog_warn ("ospf_mpls_te_lsa_refresh: lp was disabled: Flush it!");
+      lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */
+    }
+
+  /* If the lsa's age reached to MaxAge, start flushing procedure. */
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      if (lp)
+        UNSET_FLAG (lp->flags, LPFLG_LSA_ENGAGED);
+      ospf_opaque_lsa_flush_schedule (lsa);
+      goto out;
+    }
+
+  /* Create new Opaque-LSA/MPLS-TE instance. */
+  if ((new = ospf_mpls_te_lsa_new (area, lp)) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_refresh: ospf_mpls_te_lsa_new() ?");
+      goto out;
+    }
+  new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+  /* Install this LSA into LSDB. */
+  /* Given "lsa" will be freed in the next function. */
+  /* As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use ospf_lookup() to get ospf instance */
+  if (area)
+    top = area->ospf;
+  else
+    top = ospf_lookup ();
+
+  if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+    {
+      zlog_warn ("ospf_mpls_te_lsa_refresh: ospf_lsa_install() ?");
+      ospf_lsa_unlock (&new);
+      goto out;
+    }
+
+  /* Flood updated LSA through AS or Area depending of the RFC of the link */
+  if (IS_FLOOD_AS (lp->type))
+    ospf_flood_through_as (top, NULL, new);
+  else
+    ospf_flood_through_area (area, NULL/*nbr*/, new);
+
+  /* Debug logging. */
+  if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+    {
+      zlog_debug ("LSA[Type%d:%s]: Refresh Opaque-LSA/MPLS-TE",
+                new->data->type, inet_ntoa (new->data->id));
+      ospf_lsa_header_dump (new->data);
+    }
+
+out:
+  return new;
+}
+
+void
+ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, opcode_t opcode)
+{
+  struct ospf_lsa lsa;
+  struct lsa_header lsah;
+  struct ospf *top;
+  u_int32_t tmp;
+
+  memset (&lsa, 0, sizeof (lsa));
+  memset (&lsah, 0, sizeof (lsah));
+  top = ospf_lookup ();
+
+  /* Check if the pseudo link is ready to flood */
+  if (!(CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE))
+      || !(IS_FLOOD_AREA (lp->type) || IS_FLOOD_AS (lp->type))) {
+    return;
+  }
+
+  lsa.area = lp->area;
+  lsa.data = &lsah;
+  if (IS_FLOOD_AS (lp->type))
+    {
+      lsah.type = OSPF_OPAQUE_AS_LSA;
+      tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance);
+      lsah.id.s_addr = htonl (tmp);
+    }
+  else
+    {
+      lsah.type = OSPF_OPAQUE_AREA_LSA;
+      if (IS_INTER_AS (lp->type))
+        {
+          /* Set the area context if not know */
+          if (lp->area == NULL)
+            lp->area = ospf_area_lookup_by_area_id (top, OspfMplsTE.interas_areaid);
+          /* Unable to set the area context. Abort! */
+          if (lp->area == NULL)
+            {
+              zlog_warn ("MPLS-TE(ospf_mpls_te_lsa_schedule) Area context is null. Abort !");
+              return;
+            }
+          tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance);
+        }
+      else
+        tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance);
+      lsah.id.s_addr = htonl (tmp);
+    }
+
+  switch (opcode)
+    {
+    case REORIGINATE_THIS_LSA:
+      if (IS_FLOOD_AS (lp->type))
+        {
+          ospf_opaque_lsa_reoriginate_schedule ((void *) top, OSPF_OPAQUE_AS_LSA,
+              OPAQUE_TYPE_INTER_AS_LSA);
+          break;
+        }
+
+      if (IS_FLOOD_AREA (lp->type))
+        {
+          if (IS_INTER_AS (lp->type))
+            ospf_opaque_lsa_reoriginate_schedule ((void *) lp->area, OSPF_OPAQUE_AREA_LSA,
+                OPAQUE_TYPE_INTER_AS_LSA);
+          else
+            ospf_opaque_lsa_reoriginate_schedule ((void *) lp->area, OSPF_OPAQUE_AREA_LSA,
+                OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
+          break;
+        }
+      break;
+    case REFRESH_THIS_LSA:
+      ospf_opaque_lsa_refresh_schedule (&lsa);
+      break;
+    case FLUSH_THIS_LSA:
+      /* Reset Activity flag */
+      lp->flags = LPFLG_LSA_INACTIVE;
+      ospf_opaque_lsa_flush_schedule (&lsa);
+      break;
+    default:
+      zlog_warn ("ospf_mpls_te_lsa_schedule: Unknown opcode (%u)", opcode);
+      break;
+    }
+
+  return;
+}
+
+
+/*------------------------------------------------------------------------*
+ * Followings are vty session control functions.
+ *------------------------------------------------------------------------*/
+
+static u_int16_t
+show_vty_router_addr (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_tlv_router_addr *top = (struct te_tlv_router_addr *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  Router-Address: %s%s", inet_ntoa (top->value),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Router-Address: %s", inet_ntoa (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_header (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_tlv_link *top = (struct te_tlv_link *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  Link: %u octets of data%s", ntohs (top->header.length),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Link: %u octets of data", ntohs (top->header.length));
+
+  return TLV_HDR_SIZE; /* Here is special, not "TLV_SIZE". */
+}
+
+static u_int16_t
+show_vty_link_subtlv_link_type (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_link_type *top;
+  const char *cp = "Unknown";
+
+  top = (struct te_link_subtlv_link_type *) tlvh;
+  switch (top->link_type.value)
+    {
+    case LINK_TYPE_SUBTLV_VALUE_PTP:
+      cp = "Point-to-point";
+      break;
+    case LINK_TYPE_SUBTLV_VALUE_MA:
+      cp = "Multiaccess";
+      break;
+    default:
+      break;
+    }
+
+  if (vty != NULL)
+    vty_out (vty, "  Link-Type: %s (%u)%s", cp, top->link_type.value,
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Link-Type: %s (%u)", cp, top->link_type.value);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_link_id (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_link_id *top;
+
+  top = (struct te_link_subtlv_link_id *) tlvh;
+  if (vty != NULL)
+    vty_out (vty, "  Link-ID: %s%s", inet_ntoa (top->value), VTY_NEWLINE);
+  else
+    zlog_debug ("    Link-ID: %s", inet_ntoa (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_lclif_ipaddr (struct vty *vty,
+                                   struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_lclif_ipaddr *top;
+  int i, n;
+
+  top = (struct te_link_subtlv_lclif_ipaddr *) tlvh;
+  n = ntohs (tlvh->length) / sizeof (top->value[0]);
+
+  if (vty != NULL)
+    vty_out (vty, "  Local Interface IP Address(es): %d%s", n, VTY_NEWLINE);
+  else
+    zlog_debug ("    Local Interface IP Address(es): %d", n);
+
+  for (i = 0; i < n; i++)
+    {
+      if (vty != NULL)
+        vty_out (vty, "    #%d: %s%s", i, inet_ntoa (top->value[i]),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("      #%d: %s", i, inet_ntoa (top->value[i]));
+    }
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_rmtif_ipaddr (struct vty *vty,
+                                   struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_rmtif_ipaddr *top;
+  int i, n;
+
+  top = (struct te_link_subtlv_rmtif_ipaddr *) tlvh;
+  n = ntohs (tlvh->length) / sizeof (top->value[0]);
+  if (vty != NULL)
+    vty_out (vty, "  Remote Interface IP Address(es): %d%s", n, VTY_NEWLINE);
+  else
+    zlog_debug ("    Remote Interface IP Address(es): %d", n);
+
+  for (i = 0; i < n; i++)
+    {
+      if (vty != NULL)
+        vty_out (vty, "    #%d: %s%s", i, inet_ntoa (top->value[i]),
+                 VTY_NEWLINE);
+      else
+        zlog_debug ("      #%d: %s", i, inet_ntoa (top->value[i]));
+    }
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_te_metric (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_te_metric *top;
+
+  top = (struct te_link_subtlv_te_metric *) tlvh;
+  if (vty != NULL)
+    vty_out (vty, "  Traffic Engineering Metric: %u%s",
+             (u_int32_t) ntohl (top->value), VTY_NEWLINE);
+  else
+    zlog_debug ("    Traffic Engineering Metric: %u",
+                (u_int32_t) ntohl (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_max_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_max_bw *top;
+  float fval;
+
+  top = (struct te_link_subtlv_max_bw *) tlvh;
+  fval = ntohf (top->value);
+
+  if (vty != NULL)
+    vty_out (vty, "  Maximum Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE);
+  else
+    zlog_debug ("    Maximum Bandwidth: %g (Bytes/sec)", fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_max_rsv_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_max_rsv_bw *top;
+  float fval;
+
+  top = (struct te_link_subtlv_max_rsv_bw *) tlvh;
+  fval = ntohf (top->value);
+
+  if (vty != NULL)
+    vty_out (vty, "  Maximum Reservable Bandwidth: %g (Bytes/sec)%s", fval,
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Maximum Reservable Bandwidth: %g (Bytes/sec)", fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_unrsv_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_unrsv_bw *top;
+  float fval1, fval2;
+  int i;
+
+  top = (struct te_link_subtlv_unrsv_bw *) tlvh;
+  if (vty != NULL)
+    vty_out (vty, "  Unreserved Bandwidth per Class Type in Byte/s:%s", VTY_NEWLINE);
+  else
+    zlog_debug ("    Unreserved Bandwidth per Class Type in Byte/s:");
+  for (i = 0; i < MAX_CLASS_TYPE; i+=2)
+    {
+      fval1 = ntohf (top->value[i]);
+      fval2 = ntohf (top->value[i+1]);
+
+      if (vty != NULL)
+        vty_out(vty, "    [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s",
+                i, fval1, i+1, fval2, VTY_NEWLINE);
+      else
+        zlog_debug ("      [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)",
+                    i, fval1, i+1, fval2);
+    }
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_rsc_clsclr (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_rsc_clsclr *top;
+
+  top = (struct te_link_subtlv_rsc_clsclr *) tlvh;
+  if (vty != NULL)
+    vty_out (vty, "  Resource class/color: 0x%x%s",
+             (u_int32_t) ntohl (top->value), VTY_NEWLINE);
+  else
+    zlog_debug ("    Resource Class/Color: 0x%x",
+                (u_int32_t) ntohl (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_lrrid (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_lrrid *top;
+
+  top = (struct te_link_subtlv_lrrid *) tlvh;
+
+  if (vty != NULL)
+    {
+      vty_out (vty, "  Local  TE Router ID: %s%s", inet_ntoa (top->local),
+               VTY_NEWLINE);
+      vty_out (vty, "  Remote TE Router ID: %s%s", inet_ntoa (top->remote),
+               VTY_NEWLINE);
+    }
+  else
+    {
+      zlog_debug ("    Local  TE Router ID: %s", inet_ntoa (top->local));
+      zlog_debug ("    Remote TE Router ID: %s", inet_ntoa (top->remote));
+    }
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_llri (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_llri *top;
+
+  top = (struct te_link_subtlv_llri *) tlvh;
+
+  if (vty != NULL)
+    {
+      vty_out (vty, "  Link Local  ID: %d%s", (u_int32_t) ntohl (top->local),
+               VTY_NEWLINE);
+      vty_out (vty, "  Link Remote ID: %d%s", (u_int32_t) ntohl (top->remote),
+               VTY_NEWLINE);
+    }
+  else
+    {
+      zlog_debug ("    Link Local  ID: %d", (u_int32_t) ntohl (top->local));
+      zlog_debug ("    Link Remote ID: %d", (u_int32_t) ntohl (top->remote));
+    }
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_rip (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_rip *top;
+
+  top = (struct te_link_subtlv_rip *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  Inter-AS TE Remote ASBR IP address: %s%s",
+             inet_ntoa (top->value), VTY_NEWLINE);
+  else
+    zlog_debug ("    Inter-AS TE Remote ASBR IP address: %s",
+                inet_ntoa (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_ras (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_ras *top;
+
+  top = (struct te_link_subtlv_ras *) tlvh;
+
+  if (vty != NULL)
+    vty_out (vty, "  Inter-AS TE Remote AS number: %u%s", ntohl (top->value),
+             VTY_NEWLINE);
+  else
+    zlog_debug ("    Inter-AS TE Remote AS number: %u", ntohl (top->value));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_av_delay (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_av_delay *top;
+  u_int32_t delay;
+  u_int32_t anomalous;
+
+  top = (struct te_link_subtlv_av_delay *) tlvh;
+  delay = (u_int32_t) ntohl (top->value) & TE_EXT_MASK;
+  anomalous = (u_int32_t) ntohl (top->value) & TE_EXT_ANORMAL;
+
+  if (vty != NULL)
+    vty_out (vty, "  %s Average Link Delay: %d (micro-sec)%s",
+             anomalous ? "Anomalous" : "Normal", delay, VTY_NEWLINE);
+  else
+    zlog_debug ("    %s Average Link Delay: %d (micro-sec)",
+                anomalous ? "Anomalous" : "Normal", delay);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_mm_delay (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_mm_delay *top;
+  u_int32_t low, high;
+  u_int32_t anomalous;
+
+  top = (struct te_link_subtlv_mm_delay *) tlvh;
+  low = (u_int32_t) ntohl (top->low) & TE_EXT_MASK;
+  anomalous = (u_int32_t) ntohl (top->low) & TE_EXT_ANORMAL;
+  high = (u_int32_t) ntohl (top->high);
+
+  if (vty != NULL)
+    vty_out (vty, "  %s Min/Max Link Delay: %d/%d (micro-sec)%s",
+             anomalous ? "Anomalous" : "Normal", low, high, VTY_NEWLINE);
+  else
+    zlog_debug ("    %s Min/Max Link Delay: %d/%d (micro-sec)",
+                anomalous ? "Anomalous" : "Normal", low, high);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_delay_var (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_delay_var *top;
+  u_int32_t jitter;
+
+  top = (struct te_link_subtlv_delay_var *) tlvh;
+  jitter = (u_int32_t) ntohl (top->value) & TE_EXT_MASK;
+
+  if (vty != NULL)
+    vty_out (vty, "  Delay Variation: %d (micro-sec)%s", jitter, VTY_NEWLINE);
+  else
+    zlog_debug ("    Delay Variation: %d (micro-sec)", jitter);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_pkt_loss (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_pkt_loss *top;
+  u_int32_t loss;
+  u_int32_t anomalous;
+  float fval;
+
+  top = (struct te_link_subtlv_pkt_loss *) tlvh;
+  loss = (u_int32_t) ntohl (top->value) & TE_EXT_MASK;
+  fval = (float) (loss * LOSS_PRECISION);
+  anomalous = (u_int32_t) ntohl (top->value) & TE_EXT_ANORMAL;
+
+  if (vty != NULL)
+    vty_out (vty, "  %s Link Loss: %g (%%)%s", anomalous ? "Anomalous" : "Normal",
+             fval, VTY_NEWLINE);
+  else
+    zlog_debug ("    %s Link Loss: %g (%%)", anomalous ? "Anomalous" : "Normal",
+                fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_res_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_res_bw *top;
+  float fval;
+
+  top = (struct te_link_subtlv_res_bw *) tlvh;
+  fval = ntohf (top->value);
+
+  if (vty != NULL)
+    vty_out (vty, "  Unidirectional Residual Bandwidth: %g (Bytes/sec)%s",
+             fval, VTY_NEWLINE);
+  else
+    zlog_debug ("    Unidirectional Residual Bandwidth: %g (Bytes/sec)",
+                fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_ava_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_ava_bw *top;
+  float fval;
+
+  top = (struct te_link_subtlv_ava_bw *) tlvh;
+  fval = ntohf (top->value);
+
+  if (vty != NULL)
+    vty_out (vty, "  Unidirectional Available Bandwidth: %g (Bytes/sec)%s",
+             fval, VTY_NEWLINE);
+  else
+    zlog_debug ("    Unidirectional Available Bandwidth: %g (Bytes/sec)",
+                fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_link_subtlv_use_bw (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  struct te_link_subtlv_use_bw *top;
+  float fval;
+
+  top = (struct te_link_subtlv_use_bw *) tlvh;
+  fval = ntohf (top->value);
+
+  if (vty != NULL)
+    vty_out (vty, "  Unidirectional Utilized Bandwidth: %g (Bytes/sec)%s",
+             fval, VTY_NEWLINE);
+  else
+    zlog_debug ("    Unidirectional Utilized Bandwidth: %g (Bytes/sec)",
+                fval);
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_unknown_tlv (struct vty *vty, struct te_tlv_header *tlvh)
+{
+  if (vty != NULL)
+    vty_out (vty, "  Unknown TLV: [type(0x%x), length(0x%x)]%s",
+             ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE);
+  else
+    zlog_debug ("    Unknown TLV: [type(0x%x), length(0x%x)]",
+                ntohs (tlvh->type), ntohs (tlvh->length));
+
+  return TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+ospf_mpls_te_show_link_subtlv (struct vty *vty, struct te_tlv_header *tlvh0,
+                               u_int16_t subtotal, u_int16_t total)
+{
+  struct te_tlv_header *tlvh, *next;
+  u_int16_t sum = subtotal;
+
+  for (tlvh = tlvh0; sum < total; tlvh = (next ? next : TLV_HDR_NEXT (tlvh)))
+    {
+      next = NULL;
+      switch (ntohs (tlvh->type))
+        {
+        case TE_LINK_SUBTLV_LINK_TYPE:
+          sum += show_vty_link_subtlv_link_type (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_LINK_ID:
+          sum += show_vty_link_subtlv_link_id (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_LCLIF_IPADDR:
+          sum += show_vty_link_subtlv_lclif_ipaddr (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_RMTIF_IPADDR:
+          sum += show_vty_link_subtlv_rmtif_ipaddr (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_TE_METRIC:
+          sum += show_vty_link_subtlv_te_metric (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_MAX_BW:
+          sum += show_vty_link_subtlv_max_bw (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_MAX_RSV_BW:
+          sum += show_vty_link_subtlv_max_rsv_bw (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_UNRSV_BW:
+          sum += show_vty_link_subtlv_unrsv_bw (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_RSC_CLSCLR:
+          sum += show_vty_link_subtlv_rsc_clsclr (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_LRRID:
+          sum += show_vty_link_subtlv_lrrid (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_LLRI:
+          sum += show_vty_link_subtlv_llri (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_RIP:
+          sum += show_vty_link_subtlv_rip (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_RAS:
+          sum += show_vty_link_subtlv_ras (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_AV_DELAY:
+          sum += show_vty_link_subtlv_av_delay (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_MM_DELAY:
+          sum += show_vty_link_subtlv_mm_delay (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_DELAY_VAR:
+          sum += show_vty_link_subtlv_delay_var (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_PKT_LOSS:
+          sum += show_vty_link_subtlv_pkt_loss (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_RES_BW:
+          sum += show_vty_link_subtlv_res_bw (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_AVA_BW:
+          sum += show_vty_link_subtlv_ava_bw (vty, tlvh);
+          break;
+        case TE_LINK_SUBTLV_USE_BW:
+          sum += show_vty_link_subtlv_use_bw (vty, tlvh);
+          break;
+        default:
+          sum += show_vty_unknown_tlv (vty, tlvh);
+          break;
+        }
+    }
+  return sum;
+}
+
+static void
+ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa)
+{
+  struct lsa_header *lsah = (struct lsa_header *) lsa->data;
+  struct te_tlv_header *tlvh, *next;
+  u_int16_t sum, total;
+  u_int16_t (* subfunc)(struct vty *vty, struct te_tlv_header *tlvh,
+                        u_int16_t subtotal, u_int16_t total) = NULL;
+
+  sum = 0;
+  total = ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+  for (tlvh = TLV_HDR_TOP (lsah); sum < total;
+                       tlvh = (next ? next : TLV_HDR_NEXT (tlvh)))
+    {
+      if (subfunc != NULL)
+        {
+          sum = (* subfunc)(vty, tlvh, sum, total);
+         next = (struct te_tlv_header *)((char *) tlvh + sum);
+          subfunc = NULL;
+          continue;
+        }
+
+      next = NULL;
+      switch (ntohs (tlvh->type))
+        {
+        case TE_TLV_ROUTER_ADDR:
+          sum += show_vty_router_addr (vty, tlvh);
+          break;
+        case TE_TLV_LINK:
+          sum += show_vty_link_header (vty, tlvh);
+         subfunc = ospf_mpls_te_show_link_subtlv;
+         next = tlvh + 1;
+          break;
+        default:
+          sum += show_vty_unknown_tlv (vty, tlvh);
+          break;
+        }
+    }
+  return;
+}
+
+static void
+ospf_mpls_te_config_write_router (struct vty *vty)
+{
+
+  if (OspfMplsTE.status == enabled)
+    {
+      vty_out (vty, "  mpls-te on%s", VTY_NEWLINE);
+      vty_out (vty, "  mpls-te router-address %s%s",
+               inet_ntoa (OspfMplsTE.router_addr.value), VTY_NEWLINE);
+    }
+
+  if (OspfMplsTE.inter_as == AS)
+    vty_out (vty, "  mpls-te inter-as as%s", VTY_NEWLINE);
+  if (OspfMplsTE.inter_as == Area)
+    vty_out (vty, "  mpls-te inter-as area %s %s",
+             inet_ntoa (OspfMplsTE.interas_areaid), VTY_NEWLINE);
+
+  return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty command functions.
+ *------------------------------------------------------------------------*/
+
+DEFUN (ospf_mpls_te_on,
+       ospf_mpls_te_on_cmd,
+       "mpls-te on",
+       MPLS_TE_STR
+       "Enable the MPLS-TE functionality\n")
+{
+  struct listnode *node;
+  struct mpls_te_link *lp;
+
+  if (OspfMplsTE.status == enabled)
+    return CMD_SUCCESS;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("MPLS-TE: OFF -> ON");
+
+  OspfMplsTE.status = enabled;
+
+  /* Reoriginate RFC3630 & RFC6827 Links */
+  ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_THIS_LSA);
+
+  /* Reoriginate LSA if INTER-AS is always on */
+  if (OspfMplsTE.inter_as != Disable)
+    {
+      for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp))
+        {
+          if (IS_INTER_AS (lp->type))
+            {
+              ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA);
+            }
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_mpls_te,
+       no_ospf_mpls_te_cmd,
+       "no mpls-te",
+       NO_STR
+       "Disable the MPLS-TE functionality\n")
+{
+  struct listnode *node, *nnode;
+  struct mpls_te_link *lp;
+
+  if (OspfMplsTE.status == disabled)
+    return CMD_SUCCESS;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("MPLS-TE: ON -> OFF");
+
+  OspfMplsTE.status = disabled;
+
+  for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+    if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)
+        ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_mpls_te_router_addr,
+       ospf_mpls_te_router_addr_cmd,
+       "mpls-te router-address A.B.C.D",
+       MPLS_TE_STR
+       "Stable IP address of the advertising router\n"
+       "MPLS-TE router address in IPv4 address format\n")
+{
+  struct te_tlv_router_addr *ra = &OspfMplsTE.router_addr;
+  struct in_addr value;
+
+  if (! inet_aton (argv[0], &value))
+    {
+      vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ntohs (ra->header.type) == 0
+      || ntohl (ra->value.s_addr) != ntohl (value.s_addr))
+    {
+      struct listnode *node, *nnode;
+      struct mpls_te_link *lp;
+      int need_to_reoriginate = 0;
+
+      set_mpls_te_router_addr (value);
+
+      if (OspfMplsTE.status == disabled)
+        goto out;
+
+      for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+        {
+          if ((lp->area == NULL) || IS_FLOOD_AS (lp->type))
+            continue;
+
+          if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED))
+            {
+              need_to_reoriginate = 1;
+              break;
+            }
+        }
+      
+      for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+        {
+          if ((lp->area == NULL) || IS_FLOOD_AS (lp->type))
+            continue;
+
+          if (need_to_reoriginate)
+            SET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH);
+          else
+            ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA);
+        }
+
+      if (need_to_reoriginate)
+        ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_THIS_LSA);
+    }
+out:
+  return CMD_SUCCESS;
+}
+
+static int
+set_inter_as_mode (struct vty *vty, const char *mode_name,
+                   const char *area_id)
+{
+  enum inter_as_mode mode;
+  struct listnode *node;
+  struct mpls_te_link *lp;
+  int format;
+
+  if (OspfMplsTE.status == enabled)
+    {
+
+      /* Read and Check inter_as mode */
+      if (strcmp (mode_name, "as") == 0)
+        mode = AS;
+      else if (strcmp (mode_name, "area") == 0)
+        {
+          mode = Area;
+          VTY_GET_OSPF_AREA_ID (OspfMplsTE.interas_areaid, format, area_id);
+        }
+      else
+        {
+          vty_out (vty, "Unknown mode. Please choose between as or area%s",
+                   VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      if (IS_DEBUG_OSPF_EVENT)
+        zlog_debug ("MPLS-TE: Inter-AS enable with %s flooding support",
+                    mode2text[mode]);
+
+      /* Register new callbacks regarding the flooding scope (AS or Area) */
+      if (ospf_mpls_te_register (mode) < 0)
+        {
+          vty_out (vty, "Internal error: Unable to register Inter-AS functions%s",
+                   VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      /* Enable mode and re-originate LSA if needed */
+      if ((OspfMplsTE.inter_as == Disable) && (mode != OspfMplsTE.inter_as))
+        {
+          OspfMplsTE.inter_as = mode;
+          /* Re-originate all InterAS-TEv2 LSA */
+          for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp))
+            {
+              if (IS_INTER_AS (lp->type))
+                {
+                  if (mode == AS)
+                    lp->type |= FLOOD_AS;
+                  else
+                    lp->type |= FLOOD_AREA;
+                  ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA);
+                }
+            }
+        }
+      else
+        {
+          vty_out (vty, "Please change Inter-AS support to disable first before going to mode %s%s",
+                   mode2text[mode], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_mpls_te_inter_as_as,
+       ospf_mpls_te_inter_as_cmd,
+       "mpls-te inter-as as",
+       MPLS_TE_STR
+       "Configure MPLS-TE Inter-AS support\n"
+       "AS native mode self originate INTER_AS LSA with Type 11 (as flooding scope)\n")
+{
+  return set_inter_as_mode (vty, "as", "");
+}
+
+DEFUN (ospf_mpls_te_inter_as_area,
+       ospf_mpls_te_inter_as_area_cmd,
+       "mpls-te inter-as area (A.B.C.D|<0-4294967295>)",
+       MPLS_TE_STR
+       "Configure MPLS-TE Inter-AS support\n"
+       "AREA native mode self originate INTER_AS LSA with Type 10 (area flooding scope)\n"
+       "OSPF area ID in IP format\n"
+       "OSPF area ID as decimal value\n")
+{
+  return set_inter_as_mode (vty, "area", argv[0]);
+}
+
+DEFUN (no_ospf_mpls_te_inter_as,
+       no_ospf_mpls_te_inter_as_cmd,
+       "no mpls-te inter-as",
+       NO_STR
+       MPLS_TE_STR
+       "Disable MPLS-TE Inter-AS support\n")
+{
+
+  struct listnode *node, *nnode;
+  struct mpls_te_link *lp;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("MPLS-TE: Inter-AS support OFF");
+
+  if ((OspfMplsTE.status == enabled) && (OspfMplsTE.inter_as != Disable))
+    {
+      OspfMplsTE.inter_as = Disable;
+      /* Flush all Inter-AS LSA */
+      for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp))
+        if (IS_INTER_AS (lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED))
+          ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA);
+    }
+
+  /* Deregister the Callbacks for Inter-AS suport */
+  ospf_mpls_te_unregister ();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_mpls_te_router,
+       show_ip_ospf_mpls_te_router_cmd,
+       "show ip ospf mpls-te router",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "MPLS-TE information\n"
+       "MPLS-TE Router parameters\n")
+{
+  if (OspfMplsTE.status == enabled)
+    {
+      vty_out (vty, "--- MPLS-TE router parameters ---%s", VTY_NEWLINE);
+
+      if (ntohs (OspfMplsTE.router_addr.header.type) != 0)
+        show_vty_router_addr (vty, &OspfMplsTE.router_addr.header);
+      else if (vty != NULL)
+        vty_out (vty, "  N/A%s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+static void
+show_mpls_te_link_sub (struct vty *vty, struct interface *ifp)
+{
+  struct mpls_te_link *lp;
+
+  if ((OspfMplsTE.status == enabled) 
+      && HAS_LINK_PARAMS(ifp) 
+      && !if_is_loopback (ifp) 
+      && if_is_up (ifp)
+      && ((lp = lookup_linkparams_by_ifp (ifp)) != NULL))
+    {
+      /* Continue only if interface is not passive or support Inter-AS TEv2 */
+      if (!(ospf_oi_count (ifp) > 0))
+        {
+          if (IS_INTER_AS (lp->type))
+            {
+              vty_out (vty, "-- Inter-AS TEv2 link parameters for %s --%s",
+                       ifp->name, VTY_NEWLINE);
+            }
+          else
+            {
+              /* MPLS-TE is not activate on this interface */
+              /* or this interface is passive and Inter-AS TEv2 is not activate */
+              vty_out (vty, "  %s: MPLS-TE is disabled on this interface%s",
+                       ifp->name, VTY_NEWLINE);
+              return;
+            }
+        }
+      else
+        {
+      vty_out (vty, "-- MPLS-TE link parameters for %s --%s",
+               ifp->name, VTY_NEWLINE);
+        }
+
+      if (TLV_TYPE(lp->link_type) != 0)
+        show_vty_link_subtlv_link_type (vty, &lp->link_type.header);
+      if (TLV_TYPE(lp->link_id) != 0)
+        show_vty_link_subtlv_link_id (vty, &lp->link_id.header);
+      if (TLV_TYPE(lp->lclif_ipaddr) != 0)
+        show_vty_link_subtlv_lclif_ipaddr (vty, &lp->lclif_ipaddr.header);
+      if (TLV_TYPE(lp->rmtif_ipaddr) != 0)
+        show_vty_link_subtlv_rmtif_ipaddr (vty, &lp->rmtif_ipaddr.header);
+      if (TLV_TYPE(lp->rip) != 0)
+        show_vty_link_subtlv_rip (vty, &lp->rip.header);
+      if (TLV_TYPE(lp->ras) != 0)
+        show_vty_link_subtlv_ras (vty, &lp->ras.header);
+      if (TLV_TYPE(lp->te_metric) != 0)
+        show_vty_link_subtlv_te_metric (vty, &lp->te_metric.header);
+      if (TLV_TYPE(lp->max_bw) != 0)
+        show_vty_link_subtlv_max_bw (vty, &lp->max_bw.header);
+      if (TLV_TYPE(lp->max_rsv_bw) != 0)
+        show_vty_link_subtlv_max_rsv_bw (vty, &lp->max_rsv_bw.header);
+      if (TLV_TYPE(lp->unrsv_bw) != 0)
+        show_vty_link_subtlv_unrsv_bw (vty, &lp->unrsv_bw.header);
+      if (TLV_TYPE(lp->rsc_clsclr) != 0)
+        show_vty_link_subtlv_rsc_clsclr (vty, &lp->rsc_clsclr.header);
+      if (TLV_TYPE(lp->av_delay) != 0)
+        show_vty_link_subtlv_av_delay (vty, &lp->av_delay.header);
+      if (TLV_TYPE(lp->mm_delay) != 0)
+        show_vty_link_subtlv_mm_delay (vty, &lp->mm_delay.header);
+      if (TLV_TYPE(lp->delay_var) != 0)
+        show_vty_link_subtlv_delay_var (vty, &lp->delay_var.header);
+      if (TLV_TYPE(lp->pkt_loss) != 0)
+        show_vty_link_subtlv_pkt_loss (vty, &lp->pkt_loss.header);
+      if (TLV_TYPE(lp->res_bw) != 0)
+        show_vty_link_subtlv_res_bw (vty, &lp->res_bw.header);
+      if (TLV_TYPE(lp->ava_bw) != 0)
+        show_vty_link_subtlv_ava_bw (vty, &lp->ava_bw.header);
+      if (TLV_TYPE(lp->use_bw) != 0)
+        show_vty_link_subtlv_use_bw (vty, &lp->use_bw.header);
+      vty_out (vty, "---------------%s%s", VTY_NEWLINE, VTY_NEWLINE);
+    }
+  else
+    {
+      vty_out (vty, "  %s: MPLS-TE is disabled on this interface%s",
+               ifp->name, VTY_NEWLINE);
+    }
+
+  return;
+}
+
+DEFUN (show_ip_ospf_mpls_te_link,
+       show_ip_ospf_mpls_te_link_cmd,
+       "show ip ospf mpls-te interface [INTERFACE]",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "MPLS-TE information\n"
+       "Interface information\n"
+       "Interface name\n")
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  /* Show All Interfaces. */
+  if (argc == 0)
+    {
+      for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+        show_mpls_te_link_sub (vty, ifp);
+    }
+  /* Interface name is specified. */
+  else
+    {
+      if ((ifp = if_lookup_by_name (argv[0])) == NULL)
+        vty_out (vty, "No such interface name%s", VTY_NEWLINE);
+      else
+        show_mpls_te_link_sub (vty, ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+ospf_mpls_te_register_vty (void)
+{
+  install_element (VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd);
+
+  install_element (OSPF_NODE, &ospf_mpls_te_on_cmd);
+  install_element (OSPF_NODE, &no_ospf_mpls_te_cmd);
+  install_element (OSPF_NODE, &ospf_mpls_te_router_addr_cmd);
+  install_element (OSPF_NODE, &ospf_mpls_te_inter_as_cmd);
+  install_element (OSPF_NODE, &ospf_mpls_te_inter_as_area_cmd);
+  install_element (OSPF_NODE, &no_ospf_mpls_te_inter_as_cmd);
+
+  return;
+}
diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h
new file mode 100644 (file)
index 0000000..8bb77c4
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * This is an implementation of RFC3630, RFC5392 & RFC6827
+ * Copyright (C) 2001 KDD R&D Laboratories, Inc.
+ * http://www.kddlabs.co.jp/
+ *
+ * Copyright (C) 2012 Orange Labs
+ * http://www.orange.com
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* Add support of RFC7471 */
+/* Add support of RFC5392 */
+/* Add support of RFC6827 (partial) */
+
+#ifndef _ZEBRA_OSPF_MPLS_TE_H
+#define _ZEBRA_OSPF_MPLS_TE_H
+
+/*
+ * Opaque LSA's link state ID for Traffic Engineering is
+ * structured as follows.
+ *
+ *        24       16        8        0
+ * +--------+--------+--------+--------+
+ * |    1   |  MBZ   |........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<Resv'd>|<-- Instance --->|
+ *
+ *
+ * Type:      IANA has assigned '1' for Traffic Engineering.
+ * MBZ:       Reserved, must be set to zero.
+ * Instance:  User may select an arbitrary 16-bit value.
+ *
+ */
+
+#define        MAX_LEGAL_TE_INSTANCE_NUM (0xffff)
+#define LEGAL_TE_INSTANCE_RANGE(i)  (0 <= (i) && (i) <= 0xffff)
+
+/*
+ *        24       16        8        0
+ * +--------+--------+--------+--------+ ---
+ * |   LS age        |Options |   10   |  A
+ * +--------+--------+--------+--------+  |
+ * |    1   |   0    |    Instance     |  |
+ * +--------+--------+--------+--------+  |
+ * |        Advertising router         |  |  Standard (Opaque) LSA header;
+ * +--------+--------+--------+--------+  |  Only type-10 is used.
+ * |        LS sequence number         |  |
+ * +--------+--------+--------+--------+  |
+ * |   LS checksum   |     Length      |  V
+ * +--------+--------+--------+--------+ ---
+ * |      Type       |     Length      |  A
+ * +--------+--------+--------+--------+  |  TLV part for TE; Values might be
+ * |              Values ...           |  V  structured as a set of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/* Following define the type of TE link regarding the various RFC */
+#define STD_TE         0x01
+#define GMPLS          0x02
+#define INTER_AS       0x04
+#define PSEUDO_TE      0x08
+#define FLOOD_AREA     0x10
+#define FLOOD_AS       0x20
+#define EMULATED       0x80
+
+#define IS_STD_TE(x)           (x & STD_TE)
+#define IS_PSEUDO_TE(x)                (x & PSEUDO_TE)
+#define IS_INTER_AS(x)                 (x & INTER_AS)
+#define IS_EMULATED(x)         (x & EMULATED)
+#define IS_FLOOD_AREA(x)       (x & FLOOD_AREA)
+#define IS_FLOOD_AS(x)         (x & FLOOD_AS)
+#define IS_INTER_AS_EMU(x)     (x & INTER_AS & EMULATED)
+#define IS_INTER_AS_AS(x)      (x & INTER_AS & FLOOD_AS)
+
+/* Flags to manage TE Link LSA */
+#define LPFLG_LSA_INACTIVE             0x0
+#define LPFLG_LSA_ACTIVE               0x1
+#define LPFLG_LSA_ENGAGED              0x2
+#define LPFLG_LOOKUP_DONE              0x4
+#define LPFLG_LSA_FORCED_REFRESH       0x8
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used for Traffic Engineering.
+ */
+struct te_tlv_header
+{
+  u_int16_t    type;                   /* TE_TLV_XXX (see below) */
+  u_int16_t    length;                 /* Value portion only, in octets */
+};
+
+#define TLV_HDR_SIZE \
+       (sizeof (struct te_tlv_header))
+
+#define TLV_BODY_SIZE(tlvh) \
+       (ROUNDUP (ntohs ((tlvh)->length), sizeof (u_int32_t)))
+
+#define TLV_SIZE(tlvh) \
+       (TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh))
+
+#define TLV_HDR_TOP(lsah) \
+       (struct te_tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE)
+
+#define TLV_HDR_NEXT(tlvh) \
+       (struct te_tlv_header *)((char *)(tlvh) + TLV_SIZE(tlvh))
+
+#define TLV_HDR_SUBTLV(tlvh) \
+       (struct te_tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE)
+
+#define TLV_TYPE(tlvh)     tlvh.header.type
+#define TLV_LEN(tlvh)      tlvh.header.length
+#define TLV_HDR(tlvh)      tlvh.header
+
+
+/*
+ * Following section defines TLV body parts.
+ */
+/* Router Address TLV */ /* Mandatory */
+#define        TE_TLV_ROUTER_ADDR              1
+struct te_tlv_router_addr
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  struct in_addr       value;
+};
+
+/* Link TLV */
+#define        TE_TLV_LINK                     2
+struct te_tlv_link
+{
+  struct te_tlv_header header;
+  /* A set of link-sub-TLVs will follow. */
+};
+
+#define TE_LINK_SUBTLV_DEF_SIZE                4
+
+/* Link Type Sub-TLV */ /* Mandatory */
+#define        TE_LINK_SUBTLV_LINK_TYPE        1
+#define TE_LINK_SUBTLV_TYPE_SIZE       1
+struct te_link_subtlv_link_type
+{
+  struct te_tlv_header header;         /* Value length is 1 octet. */
+  struct
+  {
+#define        LINK_TYPE_SUBTLV_VALUE_PTP      1
+#define        LINK_TYPE_SUBTLV_VALUE_MA       2
+      u_char   value;
+      u_char   padding[3];
+  } link_type;
+};
+
+/* Link Sub-TLV: Link ID */ /* Mandatory */
+#define        TE_LINK_SUBTLV_LINK_ID          2
+struct te_link_subtlv_link_id
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  struct in_addr       value;          /* Same as router-lsa's link-id. */
+};
+
+/* Link Sub-TLV: Local Interface IP Address */ /* Optional */
+#define        TE_LINK_SUBTLV_LCLIF_IPADDR     3
+struct te_link_subtlv_lclif_ipaddr
+{
+  struct te_tlv_header header;         /* Value length is 4 x N octets. */
+  struct in_addr       value[1];       /* Local IP address(es). */
+};
+
+/* Link Sub-TLV: Remote Interface IP Address */ /* Optional */
+#define        TE_LINK_SUBTLV_RMTIF_IPADDR     4
+struct te_link_subtlv_rmtif_ipaddr
+{
+  struct te_tlv_header header;         /* Value length is 4 x N octets. */
+  struct in_addr       value[1];       /* Neighbor's IP address(es). */
+};
+
+/* Link Sub-TLV: Traffic Engineering Metric */ /* Optional */
+#define        TE_LINK_SUBTLV_TE_METRIC        5
+struct te_link_subtlv_te_metric
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  u_int32_t            value;          /* Link metric for TE purpose. */
+};
+
+/* Link Sub-TLV: Maximum Bandwidth */ /* Optional */
+#define        TE_LINK_SUBTLV_MAX_BW           6
+struct te_link_subtlv_max_bw
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  float                        value;          /* bytes/sec */
+};
+
+/* Link Sub-TLV: Maximum Reservable Bandwidth */ /* Optional */
+#define        TE_LINK_SUBTLV_MAX_RSV_BW       7
+struct te_link_subtlv_max_rsv_bw
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  float                        value;          /* bytes/sec */
+};
+
+/* Link Sub-TLV: Unreserved Bandwidth */ /* Optional */
+#define        TE_LINK_SUBTLV_UNRSV_BW         8
+#define TE_LINK_SUBTLV_UNRSV_SIZE      32
+struct te_link_subtlv_unrsv_bw
+{
+  struct te_tlv_header header;         /* Value length is 32 octets. */
+  float                        value[MAX_CLASS_TYPE];  /* One for each priority level. */
+};
+
+/* Link Sub-TLV: Resource Class/Color */ /* Optional */
+#define        TE_LINK_SUBTLV_RSC_CLSCLR       9
+struct te_link_subtlv_rsc_clsclr
+{
+  struct te_tlv_header header;         /* Value length is 4 octets. */
+  u_int32_t            value;          /* Admin. group membership. */
+};
+
+/* For RFC6827 */
+/* Local and Remote TE Router ID */
+#define TE_LINK_SUBTLV_LRRID           10
+#define TE_LINK_SUBTLV_LRRID_SIZE      8
+struct te_link_subtlv_lrrid
+{
+  struct te_tlv_header header;  /* Value length is 8 octets. */
+  struct in_addr local;         /* Local TE Router Identifier */
+  struct in_addr remote;        /* Remote TE Router Identifier */
+};
+
+/* RFC4203: Link Local/Remote Identifiers */
+#define TE_LINK_SUBTLV_LLRI            11
+#define TE_LINK_SUBTLV_LLRI_SIZE       8
+struct te_link_subtlv_llri
+{
+  struct te_tlv_header header;  /* Value length is 8 octets. */
+  u_int32_t local;              /* Link Local Identifier */
+  u_int32_t remote;             /* Link Remote Identifier */
+};
+
+/* Inter-RA Export Upward sub-TLV (12) and Inter-RA Export Downward sub-TLV (13) (RFC6827bis) are not yet supported */
+/* SUBTLV 14-16 (RFC4203) are not yet supported */
+/* Bandwidth Constraints sub-TLV (17) (RFC4124) is not yet supported */
+/* SUBLV 18-20 are for OSPFv6 TE (RFC5329). see ospf6d */
+
+/* For RFC 5392 */
+/* Remote AS Number sub-TLV */
+#define TE_LINK_SUBTLV_RAS             21
+struct te_link_subtlv_ras
+{
+  struct te_tlv_header header;  /* Value length is 4 octets. */
+  u_int32_t value;              /* Remote AS number */
+};
+
+/* IPv4 Remote ASBR ID Sub-TLV */
+#define TE_LINK_SUBTLV_RIP             22
+struct te_link_subtlv_rip
+{
+  struct te_tlv_header header;  /* Value length is 4 octets. */
+  struct in_addr value;         /* Remote ASBR IP address */
+};
+
+/* SUBTLV 24 is IPv6 Remote ASBR ID (RFC5392). see ospf6d */
+
+/* SUBTLV 23 (RFC5330) and 25 (RFC6001) are not yet supported */
+
+/* SUBTLV 26 (RFC7308) is not yet supported */
+
+/* RFC7471 */
+/* Link Sub-TLV: Average Link Delay */ /* Optional */
+#define TE_LINK_SUBTLV_AV_DELAY                27
+struct te_link_subtlv_av_delay
+{
+  struct te_tlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* delay in micro-seconds only 24 bits => 0 ... 16777215
+                                   with Anomalous Bit as Upper most bit */
+};
+
+/* Link Sub-TLV: Low/High Link Delay */
+#define TE_LINK_SUBTLV_MM_DELAY         28
+#define TE_LINK_SUBTLV_MM_DELAY_SIZE    8
+struct te_link_subtlv_mm_delay
+{
+  struct te_tlv_header header;  /* Value length is 8 bytes. */
+  u_int32_t            low;     /* low delay in micro-seconds only 24 bits => 0 ... 16777215
+                                   with Anomalous Bit (A) as Upper most bit */
+  u_int32_t            high;    /* high delay in micro-seconds only 24 bits => 0 ... 16777215 */
+};
+
+/* Link Sub-TLV: Link Delay Variation i.e. Jitter */
+#define TE_LINK_SUBTLV_DELAY_VAR       29
+struct te_link_subtlv_delay_var
+{
+  struct te_tlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* interval in micro-seconds only 24 bits => 0 ... 16777215 */
+};
+
+/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */
+#define TE_LINK_SUBTLV_PKT_LOSS                30
+struct te_link_subtlv_pkt_loss
+{
+  struct te_tlv_header header;  /* Value length is 4 bytes. */
+  u_int32_t            value;   /* in percentage of total traffic only 24 bits (2^24 - 2)
+                                   with Anomalous Bit as Upper most bit */
+};
+
+/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */
+#define TE_LINK_SUBTLV_RES_BW          31
+struct te_link_subtlv_res_bw
+{
+  struct te_tlv_header header;  /* Value length is 4 bytes. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+};
+
+/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */
+#define TE_LINK_SUBTLV_AVA_BW          32
+struct te_link_subtlv_ava_bw
+{
+  struct te_tlv_header header;  /* Value length is 4 octets. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+};
+
+/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */
+#define TE_LINK_SUBTLV_USE_BW           33
+struct te_link_subtlv_use_bw
+{
+  struct te_tlv_header header;  /* Value length is 4 octets. */
+  float                value;   /* bandwidth in IEEE floating point format with units in bytes per second */
+};
+
+#define TE_LINK_SUBTLV_MAX             34      /* Last SUBTLV + 1 */
+
+/* Here are "non-official" architectural constants. */
+#define MPLS_TE_MINIMUM_BANDWIDTH      1.0     /* Reasonable? *//* XXX */
+
+/* Following declaration concerns the MPLS-TE and LINk-TE management */
+typedef enum _opcode_t
+{ REORIGINATE_THIS_LSA, REFRESH_THIS_LSA, FLUSH_THIS_LSA } opcode_t;
+
+typedef enum _status_t
+{ disabled, enabled } status_t;
+
+/* Mode for Inter-AS Opaque-LSA */
+enum inter_as_mode { Disable, AS, Area };
+
+struct te_link_subtlv
+{
+  struct te_tlv_header header;
+  union
+  {
+    u_int32_t link_type;
+    struct in_addr link_id;
+    struct in_addr lclif;
+    struct in_addr rmtif;
+    u_int32_t te_metric;
+    float max_bw;
+    float max_rsv_bw;
+    float unrsv[8];
+    u_int32_t rsc_clsclr;
+    u_int32_t llri[2];
+    u_int32_t ras;
+    struct in_addr rip;
+    struct in_addr lrrid[2];
+    u_int32_t av_delay;
+    u_int32_t mm_delay;
+    u_int32_t delay_var;
+    u_int32_t pkt_loss;
+    float res_bw;
+    float ava_bw;
+    float use_bw;
+  } value;
+};
+
+/* Following structure are internal use only. */
+struct ospf_mpls_te
+{
+  /* Status of MPLS-TE: enable or disbale */
+  status_t status;
+
+  /* RFC5392 */
+  enum inter_as_mode inter_as;
+  struct in_addr interas_areaid;
+
+  /* List elements are zebra-interfaces (ifp), not ospf-interfaces (oi). */
+  struct list *iflist;
+
+  /* Store Router-TLV in network byte order. */
+  struct te_tlv_router_addr router_addr;
+};
+
+struct mpls_te_link
+{
+  /*
+   * According to MPLS-TE (draft) specification, 24-bit Opaque-ID field
+   * is subdivided into 8-bit "unused" field and 16-bit "instance" field.
+   * In this implementation, each Link-TLV has its own instance.
+   */
+  u_int32_t instance;
+
+  /* Reference pointer to a Zebra-interface. */
+  struct interface *ifp;
+
+  /* Area info in which this MPLS-TE link belongs to. */
+  struct ospf_area *area;
+
+  /* Flags to manage this link parameters. */
+  u_int32_t flags;
+
+  /* Type of MPLS-TE link: RFC3630, RFC5392, RFC5392 emulated, RFC6827 */
+  u_int8_t type;
+
+  /* Store Link-TLV in network byte order. */
+  /* RFC3630 & RFC6827 / RFC 6827 */
+  struct te_tlv_link link_header;
+  struct te_link_subtlv_link_type link_type;
+  struct te_link_subtlv_link_id link_id;
+  struct te_link_subtlv_lclif_ipaddr lclif_ipaddr;
+  struct te_link_subtlv_rmtif_ipaddr rmtif_ipaddr;
+  struct te_link_subtlv_te_metric te_metric;
+  struct te_link_subtlv_max_bw max_bw;
+  struct te_link_subtlv_max_rsv_bw max_rsv_bw;
+  struct te_link_subtlv_unrsv_bw unrsv_bw;
+  struct te_link_subtlv_rsc_clsclr rsc_clsclr;
+  /* RFC4203 */
+  struct te_link_subtlv_llri llri;
+  /* RFC5392 */
+  struct te_link_subtlv_ras ras;
+  struct te_link_subtlv_rip rip;
+  /* RFC6827 */
+  struct te_link_subtlv_lrrid lrrid;
+  /* RFC7471 */
+  struct te_link_subtlv_av_delay av_delay;
+  struct te_link_subtlv_mm_delay mm_delay;
+  struct te_link_subtlv_delay_var delay_var;
+  struct te_link_subtlv_pkt_loss pkt_loss;
+  struct te_link_subtlv_res_bw res_bw;
+  struct te_link_subtlv_ava_bw ava_bw;
+  struct te_link_subtlv_use_bw use_bw;
+
+  struct in_addr adv_router;
+  struct in_addr id;
+};
+
+/* Prototypes. */
+extern int ospf_mpls_te_init (void);
+extern void ospf_mpls_te_term (void);
+extern struct ospf_mpls_te *get_ospf_mpls_te (void);
+extern void ospf_mpls_te_update_if (struct interface *);
+extern void ospf_mpls_te_lsa_schedule (struct mpls_te_link *, opcode_t);
+extern u_int32_t get_mpls_te_instance_value (void);
+extern void set_linkparams_llri (struct mpls_te_link *, u_int32_t, u_int32_t);
+extern void set_linkparams_lrrid (struct mpls_te_link *, struct in_addr, struct in_addr);
+
+#endif /* _ZEBRA_OSPF_MPLS_TE_H */
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
new file mode 100644 (file)
index 0000000..478d4ff
--- /dev/null
@@ -0,0 +1,7923 @@
+/* OSPF VTY interface.
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "thread.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "command.h"
+#include "plist.h"
+#include "log.h"
+#include "zclient.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+/*#include "ospfd/ospf_routemap.h" */
+#include "ospfd/ospf_vty.h"
+#include "ospfd/ospf_dump.h"
+
+
+static const char *ospf_network_type_str[] =
+{
+  "Null",
+  "POINTOPOINT",
+  "BROADCAST",
+  "NBMA",
+  "POINTOMULTIPOINT",
+  "VIRTUALLINK",
+  "LOOPBACK"
+};
+
+
+/* Utility functions. */
+int
+ospf_str2area_id (const char *str, struct in_addr *area_id, int *format)
+{
+  char *endptr = NULL;
+  unsigned long ret;
+
+  /* match "A.B.C.D". */
+  if (strchr (str, '.') != NULL)
+    {
+      ret = inet_aton (str, area_id);
+      if (!ret)
+        return -1;
+      *format = OSPF_AREA_ID_FORMAT_ADDRESS;
+    }
+  /* match "<0-4294967295>". */
+  else
+    {
+      if (*str == '-')
+        return -1;
+      errno = 0;
+      ret = strtoul (str, &endptr, 10);
+      if (*endptr != '\0' || errno || ret > UINT32_MAX)
+        return -1;
+
+      area_id->s_addr = htonl (ret);
+      *format = OSPF_AREA_ID_FORMAT_DECIMAL;
+    }
+
+  return 0;
+}
+
+
+static int
+str2metric (const char *str, int *metric)
+{
+  /* Sanity check. */
+  if (str == NULL)
+    return 0;
+
+  *metric = strtol (str, NULL, 10);
+  if (*metric < 0 && *metric > 16777214)
+    {
+      /* vty_out (vty, "OSPF metric value is invalid%s", VTY_NEWLINE); */
+      return 0;
+    }
+
+  return 1;
+}
+
+static int
+str2metric_type (const char *str, int *metric_type)
+{
+  /* Sanity check. */
+  if (str == NULL)
+    return 0;
+
+  if (strncmp (str, "1", 1) == 0)
+    *metric_type = EXTERNAL_METRIC_TYPE_1;
+  else if (strncmp (str, "2", 1) == 0)
+    *metric_type = EXTERNAL_METRIC_TYPE_2;
+  else
+    return 0;
+
+  return 1;
+}
+
+int
+ospf_oi_count (struct interface *ifp)
+{
+  struct route_node *rn;
+  int i = 0;
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    if (rn->info)
+      i++;
+
+  return i;
+}
+
+
+DEFUN (router_ospf,
+       router_ospf_cmd,
+       "router ospf",
+       "Enable a routing process\n"
+       "Start OSPF configuration\n")
+{
+  vty->node = OSPF_NODE;
+  vty->index = ospf_get ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_ospf,
+       no_router_ospf_cmd,
+       "no router ospf",
+       NO_STR
+       "Enable a routing process\n"
+       "Start OSPF configuration\n")
+{
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, "There isn't active ospf instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf_finish (ospf);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_router_id,
+       ospf_router_id_cmd,
+       "ospf router-id A.B.C.D",
+       "OSPF specific commands\n"
+       "router-id for the OSPF process\n"
+       "OSPF router-id in IP address format\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr router_id;
+  int ret;
+
+  ret = inet_aton (argv[0], &router_id);
+  if (!ret)
+    {
+      vty_out (vty, "Please specify Router ID by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf->router_id_static = router_id;
+  
+  ospf_router_id_update (ospf);
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (ospf_router_id,
+       router_ospf_id_cmd,
+       "router-id A.B.C.D",
+       "router-id for the OSPF process\n"
+       "OSPF router-id in IP address format\n")
+
+DEFUN (no_ospf_router_id,
+       no_ospf_router_id_cmd,
+       "no ospf router-id",
+       NO_STR
+       "OSPF specific commands\n"
+       "router-id for the OSPF process\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf->router_id_static.s_addr = 0;
+
+  ospf_router_id_update (ospf);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_router_id,
+       no_router_ospf_id_cmd,
+       "no router-id",
+       NO_STR
+       "router-id for the OSPF process\n")
+
+static void
+ospf_passive_interface_default (struct ospf *ospf, u_char newval)
+{
+  struct listnode *ln;
+  struct interface *ifp;
+  struct ospf_interface *oi;
+  
+  ospf->passive_interface_default = newval;
+
+  for (ALL_LIST_ELEMENTS_RO (om->iflist, ln, ifp))
+    {
+      if (ifp &&
+          OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface))
+        UNSET_IF_PARAM (IF_DEF_PARAMS (ifp), passive_interface);
+    }
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, ln, oi))
+    {
+      if (OSPF_IF_PARAM_CONFIGURED (oi->params, passive_interface))
+        UNSET_IF_PARAM (oi->params, passive_interface);
+      /* update multicast memberships */
+      ospf_if_set_multicast(oi);
+    }
+}
+
+static void
+ospf_passive_interface_update_addr (struct ospf *ospf, struct interface *ifp,
+                               struct ospf_if_params *params, u_char value,
+                               struct in_addr addr)
+{
+  u_char dflt;
+  
+  params->passive_interface = value;
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface))
+        dflt = IF_DEF_PARAMS (ifp)->passive_interface;
+      else
+        dflt = ospf->passive_interface_default;
+      
+      if (value != dflt)
+        SET_IF_PARAM (params, passive_interface);
+      else
+        UNSET_IF_PARAM (params, passive_interface);
+      
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+}
+
+static void
+ospf_passive_interface_update (struct ospf *ospf, struct interface *ifp,
+                               struct ospf_if_params *params, u_char value)
+{
+  params->passive_interface = value;
+  if (params == IF_DEF_PARAMS (ifp))
+    {
+      if (value != ospf->passive_interface_default)
+        SET_IF_PARAM (params, passive_interface);
+      else
+        UNSET_IF_PARAM (params, passive_interface);
+    }
+}
+
+/* get the appropriate ospf parameters structure, checking if
+ * there's a valid interface address at the argi'th argv index
+ */
+enum {
+  VTY_SET = 0,
+  VTY_UNSET,
+};
+#define OSPF_VTY_GET_IF_PARAMS(ifp,params,argi,addr,set) \
+  (params) = IF_DEF_PARAMS ((ifp));           \
+                                              \
+  if (argc == (argi) + 1)                     \
+    {                                         \
+      int ret = inet_aton(argv[(argi)], &(addr)); \
+      if (!ret)                               \
+       {                                     \
+         vty_out (vty, "Please specify interface address by A.B.C.D%s", \
+                  VTY_NEWLINE);              \
+         return CMD_WARNING;                 \
+       }                                     \
+      (params) = ospf_get_if_params ((ifp), (addr)); \
+                                              \
+      if (set)                                \
+        ospf_if_update_params ((ifp), (addr));  \
+      else if ((params) == NULL)              \
+        return CMD_SUCCESS;                   \
+    }
+
+#define OSPF_VTY_PARAM_UNSET(params,var,ifp,addr) \
+  UNSET_IF_PARAM ((params), var);               \
+    if ((params) != IF_DEF_PARAMS ((ifp)))        \
+    {                                             \
+      ospf_free_if_params ((ifp), (addr));        \
+      ospf_if_update_params ((ifp), (addr));      \
+    }
+
+DEFUN (ospf_passive_interface,
+       ospf_passive_interface_addr_cmd,
+       "passive-interface IFNAME A.B.C.D",
+       "Suppress routing updates on an interface\n"
+       "Interface's name\n")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  struct route_node *rn;
+  struct ospf *ospf = vty->index;
+
+  if (argc == 0)
+    {
+      ospf_passive_interface_default (ospf, OSPF_IF_PASSIVE);
+      return CMD_SUCCESS;
+    }
+
+  ifp = if_get_by_name (argv[0]);
+
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+      ospf_passive_interface_update_addr (ospf, ifp, params,
+                                          OSPF_IF_PASSIVE, addr);
+    }
+  
+  ospf_passive_interface_update (ospf, ifp, params, OSPF_IF_PASSIVE);
+
+  /* XXX We should call ospf_if_set_multicast on exactly those
+   * interfaces for which the passive property changed.  It is too much
+   * work to determine this set, so we do this for every interface.
+   * This is safe and reasonable because ospf_if_set_multicast uses a
+   * record of joined groups to avoid systems calls if the desired
+   * memberships match the current memership.
+   */
+
+  for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+
+      if (oi && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_PASSIVE))
+       ospf_if_set_multicast(oi);
+    }
+  /*
+   * XXX It is not clear what state transitions the interface needs to
+   * undergo when going from active to passive.  Fixing this will
+   * require precise identification of interfaces having such a
+   * transition.
+   */
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (ospf_passive_interface,
+       ospf_passive_interface_cmd,
+       "passive-interface IFNAME",
+       "Suppress routing updates on an interface\n"
+       "Interface's name\n")
+
+ALIAS (ospf_passive_interface,
+       ospf_passive_interface_default_cmd,
+       "passive-interface default",
+       "Suppress routing updates on an interface\n"
+       "Suppress routing updates on interfaces by default\n")
+
+DEFUN (no_ospf_passive_interface,
+       no_ospf_passive_interface_addr_cmd,
+       "no passive-interface IFNAME A.B.C.D",
+       NO_STR
+       "Allow routing updates on an interface\n"
+       "Interface's name\n")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  struct ospf_if_params *params;
+  int ret;
+  struct route_node *rn;
+  struct ospf *ospf = vty->index;
+
+  if (argc == 0)
+    {
+      ospf_passive_interface_default (ospf, OSPF_IF_ACTIVE);
+      return CMD_SUCCESS;
+    }
+    
+  ifp = if_get_by_name (argv[0]);
+
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+      ospf_passive_interface_update_addr (ospf, ifp, params, OSPF_IF_ACTIVE, 
+                                          addr);
+    }
+  ospf_passive_interface_update (ospf, ifp, params, OSPF_IF_ACTIVE);
+  
+  /* XXX We should call ospf_if_set_multicast on exactly those
+   * interfaces for which the passive property changed.  It is too much
+   * work to determine this set, so we do this for every interface.
+   * This is safe and reasonable because ospf_if_set_multicast uses a
+   * record of joined groups to avoid systems calls if the desired
+   * memberships match the current memership.
+   */
+  for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+
+      if (oi && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_ACTIVE))
+        ospf_if_set_multicast(oi);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_passive_interface,
+       no_ospf_passive_interface_cmd,
+       "no passive-interface IFNAME",
+       NO_STR
+       "Allow routing updates on an interface\n"
+       "Interface's name\n")
+
+ALIAS (no_ospf_passive_interface,
+       no_ospf_passive_interface_default_cmd,
+       "no passive-interface default",
+       NO_STR
+       "Allow routing updates on an interface\n"
+       "Allow routing updates on interfaces by default\n")
+       
+DEFUN (ospf_network_area,
+       ospf_network_area_cmd,
+       "network A.B.C.D/M area (A.B.C.D|<0-4294967295>)",
+       "Enable routing on an IP network\n"
+       "OSPF network prefix\n"
+       "Set the OSPF area ID\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p;
+  struct in_addr area_id;
+  int ret, format;
+
+  /* Get network prefix and Area ID. */
+  VTY_GET_IPV4_PREFIX ("network prefix", p, argv[0]);
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]);
+
+  ret = ospf_network_set (ospf, &p, area_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "There is already same network statement.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_network_area,
+       no_ospf_network_area_cmd,
+       "no network A.B.C.D/M area (A.B.C.D|<0-4294967295>)",
+       NO_STR
+       "Enable routing on an IP network\n"
+       "OSPF network prefix\n"
+       "Set the OSPF area ID\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n")
+{
+  struct ospf *ospf = (struct ospf *) vty->index;
+  struct prefix_ipv4 p;
+  struct in_addr area_id;
+  int ret, format;
+
+  /* Get network prefix and Area ID. */
+  VTY_GET_IPV4_PREFIX ("network prefix", p, argv[0]);
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]);
+
+  ret = ospf_network_unset (ospf, &p, area_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "Can't find specified network area configuration.%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ospf_area_range,
+       ospf_area_range_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p;
+  struct in_addr area_id;
+  int format;
+  u_int32_t cost;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+  VTY_GET_IPV4_PREFIX ("area range", p, argv[1]);
+
+  ospf_area_range_set (ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE);
+  if (argc > 2)
+    {
+      VTY_GET_INTEGER ("range cost", cost, argv[2]);
+      ospf_area_range_cost_set (ospf, area_id, &p, cost);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ospf_area_range,
+       ospf_area_range_advertise_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "OSPF area range for route advertise (default)\n"
+       "Area range prefix\n"
+       "Advertise this range (default)\n")
+
+ALIAS (ospf_area_range,
+       ospf_area_range_cost_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M cost <0-16777215>",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
+
+ALIAS (ospf_area_range,
+       ospf_area_range_advertise_cost_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise cost <0-16777215>",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "Advertise this range (default)\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
+
+DEFUN (ospf_area_range_not_advertise,
+       ospf_area_range_not_advertise_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M not-advertise",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "DoNotAdvertise this range\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+  VTY_GET_IPV4_PREFIX ("area range", p, argv[1]);
+
+  ospf_area_range_set (ospf, area_id, &p, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_range,
+       no_ospf_area_range_cmd,
+       "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+  VTY_GET_IPV4_PREFIX ("area range", p, argv[1]);
+
+  ospf_area_range_unset (ospf, area_id, &p);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_area_range,
+       no_ospf_area_range_advertise_cmd,
+       "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M (advertise|not-advertise)",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "Advertise this range (default)\n"
+       "DoNotAdvertise this range\n")
+
+ALIAS (no_ospf_area_range,
+       no_ospf_area_range_cost_cmd,
+       "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M cost <0-16777215>",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
+
+ALIAS (no_ospf_area_range,
+       no_ospf_area_range_advertise_cost_cmd,
+       "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise cost <0-16777215>",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "Advertise this range (default)\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
+
+DEFUN (ospf_area_range_substitute,
+       ospf_area_range_substitute_cmd,
+       "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M substitute A.B.C.D/M",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "Announce area range as another prefix\n"
+       "Network prefix to be announced instead of range\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p, s;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+  VTY_GET_IPV4_PREFIX ("area range", p, argv[1]);
+  VTY_GET_IPV4_PREFIX ("substituted network prefix", s, argv[2]);
+
+  ospf_area_range_substitute_set (ospf, area_id, &p, &s);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_range_substitute,
+       no_ospf_area_range_substitute_cmd,
+       "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M substitute A.B.C.D/M",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Summarize routes matching address/mask (border routers only)\n"
+       "Area range prefix\n"
+       "Announce area range as another prefix\n"
+       "Network prefix to be announced instead of range\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p, s;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+  VTY_GET_IPV4_PREFIX ("area range", p, argv[1]);
+  VTY_GET_IPV4_PREFIX ("substituted network prefix", s, argv[2]);
+
+  ospf_area_range_substitute_unset (ospf, area_id, &p);
+
+  return CMD_SUCCESS;
+}
+
+
+/* Command Handler Logic in VLink stuff is delicate!!
+
+       ALTER AT YOUR OWN RISK!!!!
+
+       Various dummy values are used to represent 'NoChange' state for
+       VLink configuration NOT being changed by a VLink command, and
+       special syntax is used within the command strings so that the
+       typed in command verbs can be seen in the configuration command
+       bacckend handler.  This is to drastically reduce the verbeage
+       required to coe up with a reasonably compatible Cisco VLink command
+
+       - Matthew Grant <grantma@anathoth.gen.nz> 
+       Wed, 21 Feb 2001 15:13:52 +1300
+ */
+
+
+/* Configuration data for virtual links 
+ */ 
+struct ospf_vl_config_data {
+  struct vty *vty;             /* vty stuff */
+  struct in_addr area_id;      /* area ID from command line */
+  int format;                  /* command line area ID format */
+  struct in_addr vl_peer;      /* command line vl_peer */
+  int auth_type;               /* Authehntication type, if given */
+  char *auth_key;              /* simple password if present */
+  int crypto_key_id;           /* Cryptographic key ID */
+  char *md5_key;               /* MD5 authentication key */
+  int hello_interval;          /* Obvious what these are... */
+  int retransmit_interval; 
+  int transmit_delay;
+  int dead_interval;
+};
+
+static void
+ospf_vl_config_data_init (struct ospf_vl_config_data *vl_config, 
+                         struct vty *vty)
+{
+  memset (vl_config, 0, sizeof (struct ospf_vl_config_data));
+  vl_config->auth_type = OSPF_AUTH_CMD_NOTSEEN;
+  vl_config->vty = vty;
+}
+
+static struct ospf_vl_data *
+ospf_find_vl_data (struct ospf *ospf, struct ospf_vl_config_data *vl_config)
+{
+  struct ospf_area *area;
+  struct ospf_vl_data *vl_data;
+  struct vty *vty;
+  struct in_addr area_id;
+
+  vty = vl_config->vty;
+  area_id = vl_config->area_id;
+
+  if (area_id.s_addr == OSPF_AREA_BACKBONE)
+    {
+      vty_out (vty, 
+              "Configuring VLs over the backbone is not allowed%s",
+               VTY_NEWLINE);
+      return NULL;
+    }
+  area = ospf_area_get (ospf, area_id, vl_config->format);
+
+  if (area->external_routing != OSPF_AREA_DEFAULT)
+    {
+      if (vl_config->format == OSPF_AREA_ID_FORMAT_ADDRESS)
+       vty_out (vty, "Area %s is %s%s",
+                inet_ntoa (area_id),
+                area->external_routing == OSPF_AREA_NSSA?"nssa":"stub",
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "Area %ld is %s%s",
+                (u_long)ntohl (area_id.s_addr),
+                area->external_routing == OSPF_AREA_NSSA?"nssa":"stub",
+                VTY_NEWLINE);  
+      return NULL;
+    }
+  
+  if ((vl_data = ospf_vl_lookup (ospf, area, vl_config->vl_peer)) == NULL)
+    {
+      vl_data = ospf_vl_data_new (area, vl_config->vl_peer);
+      if (vl_data->vl_oi == NULL)
+       {
+         vl_data->vl_oi = ospf_vl_new (ospf, vl_data);
+         ospf_vl_add (ospf, vl_data);
+         ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE);
+       }
+    }
+  return vl_data;
+}
+
+
+static int
+ospf_vl_set_security (struct ospf_vl_data *vl_data,
+                     struct ospf_vl_config_data *vl_config)
+{
+  struct crypt_key *ck;
+  struct vty *vty;
+  struct interface *ifp = vl_data->vl_oi->ifp;
+
+  vty = vl_config->vty;
+
+  if (vl_config->auth_type != OSPF_AUTH_CMD_NOTSEEN)
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type);
+      IF_DEF_PARAMS (ifp)->auth_type = vl_config->auth_type;
+    }
+
+  if (vl_config->auth_key)
+    {
+      memset(IF_DEF_PARAMS (ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE+1);
+      strncpy ((char *) IF_DEF_PARAMS (ifp)->auth_simple, vl_config->auth_key, 
+              OSPF_AUTH_SIMPLE_SIZE);
+    }
+  else if (vl_config->md5_key)
+    {
+      if (ospf_crypt_key_lookup (IF_DEF_PARAMS (ifp)->auth_crypt, vl_config->crypto_key_id) 
+         != NULL)
+       {
+         vty_out (vty, "OSPF: Key %d already exists%s",
+                  vl_config->crypto_key_id, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      ck = ospf_crypt_key_new ();
+      ck->key_id = vl_config->crypto_key_id;
+      memset(ck->auth_key, 0, OSPF_AUTH_MD5_SIZE+1);
+      strncpy ((char *) ck->auth_key, vl_config->md5_key, OSPF_AUTH_MD5_SIZE);
+      
+      ospf_crypt_key_add (IF_DEF_PARAMS (ifp)->auth_crypt, ck);
+    }
+  else if (vl_config->crypto_key_id != 0)
+    {
+      /* Delete a key */
+
+      if (ospf_crypt_key_lookup (IF_DEF_PARAMS (ifp)->auth_crypt, 
+                                vl_config->crypto_key_id) == NULL)
+       {
+         vty_out (vty, "OSPF: Key %d does not exist%s", 
+                  vl_config->crypto_key_id, VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      
+      ospf_crypt_key_delete (IF_DEF_PARAMS (ifp)->auth_crypt, vl_config->crypto_key_id);
+
+    }
+  
+  return CMD_SUCCESS;
+}
+
+static int
+ospf_vl_set_timers (struct ospf_vl_data *vl_data,
+                   struct ospf_vl_config_data *vl_config)
+{
+  struct interface *ifp = vl_data->vl_oi->ifp;
+  /* Virtual Link data initialised to defaults, so only set
+     if a value given */
+  if (vl_config->hello_interval)
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_hello);
+      IF_DEF_PARAMS (ifp)->v_hello = vl_config->hello_interval;
+    }
+
+  if (vl_config->dead_interval)
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_wait);
+      IF_DEF_PARAMS (ifp)->v_wait = vl_config->dead_interval;
+    }
+
+  if (vl_config->retransmit_interval)
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), retransmit_interval);
+      IF_DEF_PARAMS (ifp)->retransmit_interval = vl_config->retransmit_interval;
+    }
+  
+  if (vl_config->transmit_delay)
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), transmit_delay);
+      IF_DEF_PARAMS (ifp)->transmit_delay = vl_config->transmit_delay;
+    }
+  
+  return CMD_SUCCESS;
+}
+
+
+
+/* The business end of all of the above */
+static int
+ospf_vl_set (struct ospf *ospf, struct ospf_vl_config_data *vl_config)
+{
+  struct ospf_vl_data *vl_data;
+  int ret;
+
+  vl_data = ospf_find_vl_data (ospf, vl_config);
+  if (!vl_data)
+    return CMD_WARNING;
+  
+  /* Process this one first as it can have a fatal result, which can
+     only logically occur if the virtual link exists already
+     Thus a command error does not result in a change to the
+     running configuration such as unexpectedly altered timer 
+     values etc.*/
+  ret = ospf_vl_set_security (vl_data, vl_config);
+  if (ret != CMD_SUCCESS)
+    return ret;
+
+  /* Set any time based parameters, these area already range checked */
+
+  ret = ospf_vl_set_timers (vl_data, vl_config);
+  if (ret != CMD_SUCCESS)
+    return ret;
+
+  return CMD_SUCCESS;
+
+}
+
+/* This stuff exists to make specifying all the alias commands A LOT simpler
+ */
+#define VLINK_HELPSTR_IPADDR \
+       "OSPF area parameters\n" \
+       "OSPF area ID in IP address format\n" \
+       "OSPF area ID as a decimal value\n" \
+       "Configure a virtual link\n" \
+       "Router ID of the remote ABR\n"
+
+#define VLINK_HELPSTR_AUTHTYPE_SIMPLE \
+       "Enable authentication on this virtual link\n" \
+       "dummy string \n" 
+
+#define VLINK_HELPSTR_AUTHTYPE_ALL \
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE \
+       "Use null authentication\n" \
+       "Use message-digest authentication\n"
+
+#define VLINK_HELPSTR_TIME_PARAM_NOSECS \
+       "Time between HELLO packets\n" \
+       "Time between retransmitting lost link state advertisements\n" \
+       "Link state transmit delay\n" \
+       "Interval after which a neighbor is declared dead\n"
+
+#define VLINK_HELPSTR_TIME_PARAM \
+       VLINK_HELPSTR_TIME_PARAM_NOSECS \
+       "Seconds\n"
+
+#define VLINK_HELPSTR_AUTH_SIMPLE \
+       "Authentication password (key)\n" \
+       "The OSPF password (key)"
+
+#define VLINK_HELPSTR_AUTH_MD5 \
+       "Message digest authentication password (key)\n" \
+       "dummy string \n" \
+       "Key ID\n" \
+       "Use MD5 algorithm\n" \
+       "The OSPF password (key)"
+
+DEFUN (ospf_area_vlink,
+       ospf_area_vlink_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D",
+       VLINK_HELPSTR_IPADDR)
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_vl_config_data vl_config;
+  char auth_key[OSPF_AUTH_SIMPLE_SIZE+1];
+  char md5_key[OSPF_AUTH_MD5_SIZE+1]; 
+  int i;
+  int ret;
+  
+  ospf_vl_config_data_init(&vl_config, vty);
+
+  /* Read off first 2 parameters and check them */
+  ret = ospf_str2area_id (argv[0], &vl_config.area_id, &vl_config.format);
+  if (ret < 0)
+    {
+      vty_out (vty, "OSPF area ID is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = inet_aton (argv[1], &vl_config.vl_peer);
+  if (! ret)
+    {
+      vty_out (vty, "Please specify valid Router ID as a.b.c.d%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc <=2)
+    {
+      /* Thats all folks! - BUGS B. strikes again!!!*/
+
+      return  ospf_vl_set (ospf, &vl_config);
+    }
+
+  /* Deal with other parameters */
+  for (i=2; i < argc; i++)
+    {
+
+      /* vty_out (vty, "argv[%d] - %s%s", i, argv[i], VTY_NEWLINE); */
+
+      switch (argv[i][0])
+       {
+
+       case 'a':
+         if (i > 2 || strncmp (argv[i], "authentication-", 15) == 0)
+           {
+             /* authentication-key - this option can occur anywhere on 
+                                     command line.  At start of command line
+                                     must check for authentication option. */
+             memset (auth_key, 0, OSPF_AUTH_SIMPLE_SIZE + 1);
+             strncpy (auth_key, argv[i+1], OSPF_AUTH_SIMPLE_SIZE);
+             vl_config.auth_key = auth_key;
+             i++;
+           }
+         else if (strncmp (argv[i], "authentication", 14) == 0)
+           {
+             /* authentication  - this option can only occur at start
+                                  of command line */
+             vl_config.auth_type = OSPF_AUTH_SIMPLE;
+             if ((i+1) < argc)
+               {
+                 if (strncmp (argv[i+1], "n", 1) == 0)
+                   {
+                     /* "authentication null" */
+                     vl_config.auth_type = OSPF_AUTH_NULL;
+                     i++;
+                   }
+                 else if (strncmp (argv[i+1], "m", 1) == 0
+                          && strcmp (argv[i+1], "message-digest-") != 0)
+                   {
+                     /* "authentication message-digest" */ 
+                     vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+                     i++;
+                   }
+               }
+           }
+         break;
+
+       case 'm':
+         /* message-digest-key */
+         i++;
+         vl_config.crypto_key_id = strtol (argv[i], NULL, 10);
+         if (vl_config.crypto_key_id < 0)
+           return CMD_WARNING;
+         i++;
+         memset(md5_key, 0, OSPF_AUTH_MD5_SIZE+1);
+         strncpy (md5_key, argv[i], OSPF_AUTH_MD5_SIZE);
+         vl_config.md5_key = md5_key; 
+         break;
+
+       case 'h':
+         /* Hello interval */
+         i++;
+         vl_config.hello_interval = strtol (argv[i], NULL, 10);
+         if (vl_config.hello_interval < 0) 
+           return CMD_WARNING;
+         break;
+
+       case 'r':
+         /* Retransmit Interval */
+         i++;
+         vl_config.retransmit_interval = strtol (argv[i], NULL, 10);
+         if (vl_config.retransmit_interval < 0)
+           return CMD_WARNING;
+         break;
+
+       case 't':
+         /* Transmit Delay */
+         i++;
+         vl_config.transmit_delay = strtol (argv[i], NULL, 10);
+         if (vl_config.transmit_delay < 0)
+           return CMD_WARNING;
+         break;
+
+       case 'd':
+         /* Dead Interval */
+         i++;
+         vl_config.dead_interval = strtol (argv[i], NULL, 10);
+         if (vl_config.dead_interval < 0)
+           return CMD_WARNING;
+         break;
+       }
+    }
+
+
+  /* Action configuration */
+
+  return ospf_vl_set (ospf, &vl_config);
+
+}
+
+DEFUN (no_ospf_area_vlink,
+       no_ospf_area_vlink_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D",
+       NO_STR
+       VLINK_HELPSTR_IPADDR)
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct ospf_vl_config_data vl_config;
+  struct ospf_vl_data *vl_data = NULL;
+  char auth_key[OSPF_AUTH_SIMPLE_SIZE+1];
+  int i;
+  int ret, format;
+
+  ospf_vl_config_data_init(&vl_config, vty);
+
+  ret = ospf_str2area_id (argv[0], &vl_config.area_id, &format);
+  if (ret < 0)
+    {
+      vty_out (vty, "OSPF area ID is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  area = ospf_area_lookup_by_area_id (ospf, vl_config.area_id);
+  if (!area)
+    {
+      vty_out (vty, "Area does not exist%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ret = inet_aton (argv[1], &vl_config.vl_peer);
+  if (! ret)
+    {
+      vty_out (vty, "Please specify valid Router ID as a.b.c.d%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc <=2)
+    {
+      /* Basic VLink no command */
+      /* Thats all folks! - BUGS B. strikes again!!!*/
+      if ((vl_data = ospf_vl_lookup (ospf, area, vl_config.vl_peer)))
+       ospf_vl_delete (ospf, vl_data);
+
+      ospf_area_check_free (ospf, vl_config.area_id);
+      
+      return CMD_SUCCESS;
+    }
+
+  /* If we are down here, we are reseting parameters */
+
+  /* Deal with other parameters */
+  for (i=2; i < argc; i++)
+    {
+      /* vty_out (vty, "argv[%d] - %s%s", i, argv[i], VTY_NEWLINE); */
+
+      switch (argv[i][0])
+       {
+
+       case 'a':
+         if (i > 2 || strncmp (argv[i], "authentication-", 15) == 0)
+           {
+             /* authentication-key - this option can occur anywhere on 
+                                     command line.  At start of command line
+                                     must check for authentication option. */
+             memset (auth_key, 0, OSPF_AUTH_SIMPLE_SIZE + 1);
+             vl_config.auth_key = auth_key;
+           }
+         else if (strncmp (argv[i], "authentication", 14) == 0)
+           {
+             /* authentication  - this option can only occur at start
+                                  of command line */
+             vl_config.auth_type = OSPF_AUTH_NOTSET;
+           }
+         break;
+
+       case 'm':
+         /* message-digest-key */
+         /* Delete one key */
+         i++;
+         vl_config.crypto_key_id = strtol (argv[i], NULL, 10);
+         if (vl_config.crypto_key_id < 0)
+           return CMD_WARNING;
+         vl_config.md5_key = NULL; 
+         break;
+
+       case 'h':
+         /* Hello interval */
+         vl_config.hello_interval = OSPF_HELLO_INTERVAL_DEFAULT;
+         break;
+
+       case 'r':
+         /* Retransmit Interval */
+         vl_config.retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+         break;
+
+       case 't':
+         /* Transmit Delay */
+         vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT;
+         break;
+
+       case 'd':
+         /* Dead Interval */
+         i++;
+         vl_config.dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
+         break;
+       }
+    }
+
+
+  /* Action configuration */
+
+  return ospf_vl_set (ospf, &vl_config);
+}
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_param1_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_param1_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_param2_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_param2_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_param3_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_param3_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_param4_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_param4_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval) "
+       "(hello-interval|retransmit-interval|transmit-delay|dead-interval)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM
+       VLINK_HELPSTR_TIME_PARAM)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_args_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) (message-digest|null)",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_ALL)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|)",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_authtype_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_md5_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(message-digest-key|) <1-255> md5 KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTH_MD5)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_md5_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(message-digest-key|) <1-255>",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTH_MD5)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authkey_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication-key|) AUTH_KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTH_SIMPLE)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_authkey_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication-key|)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTH_SIMPLE)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_args_authkey_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) (message-digest|null) "
+       "(authentication-key|) AUTH_KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_ALL
+       VLINK_HELPSTR_AUTH_SIMPLE)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_authkey_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) "
+       "(authentication-key|) AUTH_KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE
+       VLINK_HELPSTR_AUTH_SIMPLE)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_authtype_authkey_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) "
+       "(authentication-key|)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE
+       VLINK_HELPSTR_AUTH_SIMPLE)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_args_md5_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) (message-digest|null) "
+       "(message-digest-key|) <1-255> md5 KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_ALL
+       VLINK_HELPSTR_AUTH_MD5)
+
+ALIAS (ospf_area_vlink,
+       ospf_area_vlink_authtype_md5_cmd,
+       "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) "
+       "(message-digest-key|) <1-255> md5 KEY",
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE
+       VLINK_HELPSTR_AUTH_MD5)
+
+ALIAS (no_ospf_area_vlink,
+       no_ospf_area_vlink_authtype_md5_cmd,
+       "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D "
+       "(authentication|) "
+       "(message-digest-key|)",
+       NO_STR
+       VLINK_HELPSTR_IPADDR
+       VLINK_HELPSTR_AUTHTYPE_SIMPLE
+       VLINK_HELPSTR_AUTH_MD5)
+
+
+DEFUN (ospf_area_shortcut,
+       ospf_area_shortcut_cmd,
+       "area (A.B.C.D|<0-4294967295>) shortcut (default|enable|disable)",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure the area's shortcutting mode\n"
+       "Set default shortcutting behavior\n"
+       "Enable shortcutting through the area\n"
+       "Disable shortcutting through the area\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int mode;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("shortcut", area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+
+  if (strncmp (argv[1], "de", 2) == 0)
+    mode = OSPF_SHORTCUT_DEFAULT;
+  else if (strncmp (argv[1], "di", 2) == 0)
+    mode = OSPF_SHORTCUT_DISABLE;
+  else if (strncmp (argv[1], "e", 1) == 0)
+    mode = OSPF_SHORTCUT_ENABLE;
+  else
+    return CMD_WARNING;
+
+  ospf_area_shortcut_set (ospf, area, mode);
+
+  if (ospf->abr_type != OSPF_ABR_SHORTCUT)
+    vty_out (vty, "Shortcut area setting will take effect "
+            "only when the router is configured as Shortcut ABR%s",
+            VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_shortcut,
+       no_ospf_area_shortcut_cmd,
+       "no area (A.B.C.D|<0-4294967295>) shortcut (enable|disable)",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Deconfigure the area's shortcutting mode\n"
+       "Deconfigure enabled shortcutting through the area\n"
+       "Deconfigure disabled shortcutting through the area\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("shortcut", area_id, format, argv[0]);
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (!area)
+    return CMD_SUCCESS;
+
+  ospf_area_shortcut_unset (ospf, area);
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ospf_area_stub,
+       ospf_area_stub_cmd,
+       "area (A.B.C.D|<0-4294967295>) stub",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as stub\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int ret, format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]);
+
+  ret = ospf_area_stub_set (ospf, area_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "First deconfigure all virtual link through this area%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf_area_no_summary_unset (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_stub_no_summary,
+       ospf_area_stub_no_summary_cmd,
+       "area (A.B.C.D|<0-4294967295>) stub no-summary",
+       "OSPF stub parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as stub\n"
+       "Do not inject inter-area routes into stub\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int ret, format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]);
+
+  ret = ospf_area_stub_set (ospf, area_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "%% Area cannot be stub as it contains a virtual link%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf_area_no_summary_set (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_stub,
+       no_ospf_area_stub_cmd,
+       "no area (A.B.C.D|<0-4294967295>) stub",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as stub\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]);
+
+  ospf_area_stub_unset (ospf, area_id);
+  ospf_area_no_summary_unset (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_stub_no_summary,
+       no_ospf_area_stub_no_summary_cmd,
+       "no area (A.B.C.D|<0-4294967295>) stub no-summary",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as stub\n"
+       "Do not inject inter-area routes into area\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]);
+  ospf_area_no_summary_unset (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+static int
+ospf_area_nssa_cmd_handler (struct vty *vty, int argc, const char *argv[], 
+                            int nosum)
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int ret, format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]);
+
+  ret = ospf_area_nssa_set (ospf, area_id);
+  if (ret == 0)
+    {
+      vty_out (vty, "%% Area cannot be nssa as it contains a virtual link%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    {
+      if (strncmp (argv[1], "translate-c", 11) == 0)
+        ospf_area_nssa_translator_role_set (ospf, area_id,
+                                           OSPF_NSSA_ROLE_CANDIDATE);
+      else if (strncmp (argv[1], "translate-n", 11) == 0)
+        ospf_area_nssa_translator_role_set (ospf, area_id,
+                                           OSPF_NSSA_ROLE_NEVER);
+      else if (strncmp (argv[1], "translate-a", 11) == 0)
+        ospf_area_nssa_translator_role_set (ospf, area_id,
+                                           OSPF_NSSA_ROLE_ALWAYS);
+    }
+  else
+    {
+      ospf_area_nssa_translator_role_set (ospf, area_id,
+                        OSPF_NSSA_ROLE_CANDIDATE);
+    }
+
+  if (nosum)
+    ospf_area_no_summary_set (ospf, area_id);
+  else
+    ospf_area_no_summary_unset (ospf, area_id);
+
+  ospf_schedule_abr_task (ospf);
+    
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_nssa_translate_no_summary,
+       ospf_area_nssa_translate_no_summary_cmd,
+       "area (A.B.C.D|<0-4294967295>) nssa (translate-candidate|translate-never|translate-always) no-summary",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n"
+       "Configure NSSA-ABR for translate election (default)\n"
+       "Configure NSSA-ABR to never translate\n"
+       "Configure NSSA-ABR to always translate\n"
+       "Do not inject inter-area routes into nssa\n")
+{
+   return ospf_area_nssa_cmd_handler (vty, argc, argv, 1);
+}
+
+DEFUN (ospf_area_nssa_translate,
+       ospf_area_nssa_translate_cmd,
+       "area (A.B.C.D|<0-4294967295>) nssa (translate-candidate|translate-never|translate-always)",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n"
+       "Configure NSSA-ABR for translate election (default)\n"
+       "Configure NSSA-ABR to never translate\n"
+       "Configure NSSA-ABR to always translate\n")
+{
+  return ospf_area_nssa_cmd_handler (vty, argc, argv, 0);
+}
+
+DEFUN (ospf_area_nssa,
+       ospf_area_nssa_cmd,
+       "area (A.B.C.D|<0-4294967295>) nssa",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n")
+{
+  return ospf_area_nssa_cmd_handler (vty, argc, argv, 0);
+}
+
+DEFUN (ospf_area_nssa_no_summary,
+       ospf_area_nssa_no_summary_cmd,
+       "area (A.B.C.D|<0-4294967295>) nssa no-summary",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n"
+       "Do not inject inter-area routes into nssa\n")
+{
+  return ospf_area_nssa_cmd_handler (vty, argc, argv, 1);
+}
+
+DEFUN (no_ospf_area_nssa,
+       no_ospf_area_nssa_cmd,
+       "no area (A.B.C.D|<0-4294967295>) nssa",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]);
+
+  ospf_area_nssa_unset (ospf, area_id);
+  ospf_area_no_summary_unset (ospf, area_id);
+
+  ospf_schedule_abr_task (ospf);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_nssa_no_summary,
+       no_ospf_area_nssa_no_summary_cmd,
+       "no area (A.B.C.D|<0-4294967295>) nssa no-summary",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n"
+       "Do not inject inter-area routes into nssa\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]);
+  ospf_area_no_summary_unset (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_default_cost,
+       ospf_area_default_cost_cmd,
+       "area (A.B.C.D|<0-4294967295>) default-cost <0-16777215>",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Set the summary-default cost of a NSSA or stub area\n"
+       "Stub's advertised default summary cost\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  u_int32_t cost;
+  int format;
+  struct prefix_ipv4 p;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("default-cost", area_id, format, argv[0]);
+  VTY_GET_INTEGER_RANGE ("stub default cost", cost, argv[1], 0, 16777215);
+
+  area = ospf_area_get (ospf, area_id, format);
+
+  if (area->external_routing == OSPF_AREA_DEFAULT)
+    {
+      vty_out (vty, "The area is neither stub, nor NSSA%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  area->default_cost = cost;
+
+  p.family = AF_INET;
+  p.prefix.s_addr = OSPF_DEFAULT_DESTINATION;
+  p.prefixlen = 0;
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_stub_defaults(): "
+                "announcing 0.0.0.0/0 to area %s",
+               inet_ntoa (area->area_id));
+  ospf_abr_announce_network_to_area (&p, area->default_cost, area);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_default_cost,
+       no_ospf_area_default_cost_cmd,
+       "no area (A.B.C.D|<0-4294967295>) default-cost <0-16777215>",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Set the summary-default cost of a NSSA or stub area\n"
+       "Stub's advertised default summary cost\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+  struct prefix_ipv4 p;
+
+  VTY_GET_OSPF_AREA_ID_NO_BB ("default-cost", area_id, format, argv[0]);
+  VTY_CHECK_INTEGER_RANGE ("stub default cost", argv[1], 0, OSPF_LS_INFINITY);
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return CMD_SUCCESS;
+
+  if (area->external_routing == OSPF_AREA_DEFAULT)
+    {
+      vty_out (vty, "The area is neither stub, nor NSSA%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  area->default_cost = 1;
+
+  p.family = AF_INET;
+  p.prefix.s_addr = OSPF_DEFAULT_DESTINATION;
+  p.prefixlen = 0;
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("ospf_abr_announce_stub_defaults(): "
+                "announcing 0.0.0.0/0 to area %s",
+               inet_ntoa (area->area_id));
+  ospf_abr_announce_network_to_area (&p, area->default_cost, area);
+
+
+  ospf_area_check_free (ospf, area_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_export_list,
+       ospf_area_export_list_cmd,
+       "area (A.B.C.D|<0-4294967295>) export-list NAME",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Set the filter for networks announced to other areas\n"
+       "Name of the access-list\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+  ospf_area_export_list_set (ospf, area, argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_export_list,
+       no_ospf_area_export_list_cmd,
+       "no area (A.B.C.D|<0-4294967295>) export-list NAME",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Unset the filter for networks announced to other areas\n"
+       "Name of the access-list\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return CMD_SUCCESS;
+
+  ospf_area_export_list_unset (ospf, area);
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ospf_area_import_list,
+       ospf_area_import_list_cmd,
+       "area (A.B.C.D|<0-4294967295>) import-list NAME",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Set the filter for networks from other areas announced to the specified one\n"
+       "Name of the access-list\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+  ospf_area_import_list_set (ospf, area, argv[1]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_import_list,
+       no_ospf_area_import_list_cmd,
+       "no area (A.B.C.D|<0-4294967295>) import-list NAME",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Unset the filter for networks announced to other areas\n"
+       "Name of the access-list\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return CMD_SUCCESS;
+
+  ospf_area_import_list_unset (ospf, area);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_filter_list,
+       ospf_area_filter_list_cmd,
+       "area (A.B.C.D|<0-4294967295>) filter-list prefix WORD (in|out)",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Filter networks between OSPF areas\n"
+       "Filter prefixes between OSPF areas\n"
+       "Name of an IP prefix-list\n"
+       "Filter networks sent to this area\n"
+       "Filter networks sent from this area\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  struct prefix_list *plist;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+  plist = prefix_list_lookup (AFI_IP, argv[1]);
+  if (strncmp (argv[2], "in", 2) == 0)
+    {
+      PREFIX_LIST_IN (area) = plist;
+      if (PREFIX_NAME_IN (area))
+       free (PREFIX_NAME_IN (area));
+
+      PREFIX_NAME_IN (area) = strdup (argv[1]);
+      ospf_schedule_abr_task (ospf);
+    }
+  else
+    {
+      PREFIX_LIST_OUT (area) = plist;
+      if (PREFIX_NAME_OUT (area))
+       free (PREFIX_NAME_OUT (area));
+
+      PREFIX_NAME_OUT (area) = strdup (argv[1]);
+      ospf_schedule_abr_task (ospf);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_filter_list,
+       no_ospf_area_filter_list_cmd,
+       "no area (A.B.C.D|<0-4294967295>) filter-list prefix WORD (in|out)",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Filter networks between OSPF areas\n"
+       "Filter prefixes between OSPF areas\n"
+       "Name of an IP prefix-list\n"
+       "Filter networks sent to this area\n"
+       "Filter networks sent from this area\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  if ((area = ospf_area_lookup_by_area_id (ospf, area_id)) == NULL)
+    return CMD_SUCCESS;
+  
+  if (strncmp (argv[2], "in", 2) == 0)
+    {
+      if (PREFIX_NAME_IN (area))
+       if (strcmp (PREFIX_NAME_IN (area), argv[1]) != 0)
+         return CMD_SUCCESS;
+
+      PREFIX_LIST_IN (area) = NULL;
+      if (PREFIX_NAME_IN (area))
+       free (PREFIX_NAME_IN (area));
+
+      PREFIX_NAME_IN (area) = NULL;
+
+      ospf_schedule_abr_task (ospf);
+    }
+  else
+    {
+      if (PREFIX_NAME_OUT (area))
+       if (strcmp (PREFIX_NAME_OUT (area), argv[1]) != 0)
+         return CMD_SUCCESS;
+
+      PREFIX_LIST_OUT (area) = NULL;
+      if (PREFIX_NAME_OUT (area))
+       free (PREFIX_NAME_OUT (area));
+
+      PREFIX_NAME_OUT (area) = NULL;
+
+      ospf_schedule_abr_task (ospf);
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ospf_area_authentication_message_digest,
+       ospf_area_authentication_message_digest_cmd,
+       "area (A.B.C.D|<0-4294967295>) authentication message-digest",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Enable authentication\n"
+       "Use message-digest authentication\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+  area->auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_area_authentication,
+       ospf_area_authentication_cmd,
+       "area (A.B.C.D|<0-4294967295>) authentication",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Enable authentication\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_get (ospf, area_id, format);
+  area->auth_type = OSPF_AUTH_SIMPLE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_area_authentication,
+       no_ospf_area_authentication_cmd,
+       "no area (A.B.C.D|<0-4294967295>) authentication",
+       NO_STR
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Enable authentication\n")
+{
+  struct ospf *ospf = vty->index;
+  struct ospf_area *area;
+  struct in_addr area_id;
+  int format;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return CMD_SUCCESS;
+
+  area->auth_type = OSPF_AUTH_NULL;
+
+  ospf_area_check_free (ospf, area_id);
+  
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ospf_abr_type,
+       ospf_abr_type_cmd,
+       "ospf abr-type (cisco|ibm|shortcut|standard)",
+       "OSPF specific commands\n"
+       "Set OSPF ABR type\n"
+       "Alternative ABR, cisco implementation\n"
+       "Alternative ABR, IBM implementation\n"
+       "Shortcut ABR\n"
+       "Standard behavior (RFC2328)\n")
+{
+  struct ospf *ospf = vty->index;
+  u_char abr_type = OSPF_ABR_UNKNOWN;
+
+  if (strncmp (argv[0], "c", 1) == 0)
+    abr_type = OSPF_ABR_CISCO;
+  else if (strncmp (argv[0], "i", 1) == 0)
+    abr_type = OSPF_ABR_IBM;
+  else if (strncmp (argv[0], "sh", 2) == 0)
+    abr_type = OSPF_ABR_SHORTCUT;
+  else if (strncmp (argv[0], "st", 2) == 0)
+    abr_type = OSPF_ABR_STAND;
+  else
+    return CMD_WARNING;
+
+  /* If ABR type value is changed, schedule ABR task. */
+  if (ospf->abr_type != abr_type)
+    {
+      ospf->abr_type = abr_type;
+      ospf_schedule_abr_task (ospf);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_abr_type,
+       no_ospf_abr_type_cmd,
+       "no ospf abr-type (cisco|ibm|shortcut|standard)",
+       NO_STR
+       "OSPF specific commands\n"
+       "Set OSPF ABR type\n"
+       "Alternative ABR, cisco implementation\n"
+       "Alternative ABR, IBM implementation\n"
+       "Shortcut ABR\n")
+{
+  struct ospf *ospf = vty->index;
+  u_char abr_type = OSPF_ABR_UNKNOWN;
+
+  if (strncmp (argv[0], "c", 1) == 0)
+    abr_type = OSPF_ABR_CISCO;
+  else if (strncmp (argv[0], "i", 1) == 0)
+    abr_type = OSPF_ABR_IBM;
+  else if (strncmp (argv[0], "sh", 2) == 0)
+    abr_type = OSPF_ABR_SHORTCUT;
+  else if (strncmp (argv[0], "st", 2) == 0)
+    abr_type = OSPF_ABR_STAND;
+  else
+    return CMD_WARNING;
+
+  /* If ABR type value is changed, schedule ABR task. */
+  if (ospf->abr_type == abr_type)
+    {
+      ospf->abr_type = OSPF_ABR_DEFAULT;
+      ospf_schedule_abr_task (ospf);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_log_adjacency_changes,
+       ospf_log_adjacency_changes_cmd,
+       "log-adjacency-changes",
+       "Log changes in adjacency state\n")
+{
+  struct ospf *ospf = vty->index;
+
+  SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_log_adjacency_changes_detail,
+       ospf_log_adjacency_changes_detail_cmd,
+       "log-adjacency-changes detail",
+       "Log changes in adjacency state\n"
+       "Log all state changes\n")
+{
+  struct ospf *ospf = vty->index;
+
+  SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
+  SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_log_adjacency_changes,
+       no_ospf_log_adjacency_changes_cmd,
+       "no log-adjacency-changes",
+       NO_STR
+       "Log changes in adjacency state\n")
+{
+  struct ospf *ospf = vty->index;
+
+  UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL);
+  UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_log_adjacency_changes_detail,
+       no_ospf_log_adjacency_changes_detail_cmd,
+       "no log-adjacency-changes detail",
+       NO_STR
+       "Log changes in adjacency state\n"
+       "Log all state changes\n")
+{
+  struct ospf *ospf = vty->index;
+
+  UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_compatible_rfc1583,
+       ospf_compatible_rfc1583_cmd,
+       "compatible rfc1583",
+       "OSPF compatibility list\n"
+       "compatible with RFC 1583\n")
+{
+  struct ospf *ospf = vty->index;
+
+  if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+    {
+      SET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE);
+      ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_compatible_rfc1583,
+       no_ospf_compatible_rfc1583_cmd,
+       "no compatible rfc1583",
+       NO_STR
+       "OSPF compatibility list\n"
+       "compatible with RFC 1583\n")
+{
+  struct ospf *ospf = vty->index;
+
+  if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+    {
+      UNSET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE);
+      ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE);
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (ospf_compatible_rfc1583,
+       ospf_rfc1583_flag_cmd,
+       "ospf rfc1583compatibility",
+       "OSPF specific commands\n"
+       "Enable the RFC1583Compatibility flag\n")
+
+ALIAS (no_ospf_compatible_rfc1583,
+       no_ospf_rfc1583_flag_cmd,
+       "no ospf rfc1583compatibility",
+       NO_STR
+       "OSPF specific commands\n"
+       "Disable the RFC1583Compatibility flag\n")
+
+static int
+ospf_timers_spf_set (struct vty *vty, unsigned int delay,
+                     unsigned int hold,
+                     unsigned int max)
+{
+  struct ospf *ospf = vty->index;
+  
+  ospf->spf_delay = delay;
+  ospf->spf_holdtime = hold;
+  ospf->spf_max_holdtime = max;
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_timers_min_ls_interval,
+       ospf_timers_min_ls_interval_cmd,
+       "timers throttle lsa all <0-5000>",
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "LSA delay between transmissions\n"
+       NO_STR
+       "Delay (msec) between sending LSAs\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int interval;
+
+  if (argc != 1)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER ("LSA interval", interval, argv[0]);
+
+  ospf->min_ls_interval = interval;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_timers_min_ls_interval,
+       no_ospf_timers_min_ls_interval_cmd,
+       "no timers throttle lsa all",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "LSA delay between transmissions\n")
+{
+  struct ospf *ospf = vty->index;
+  ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_timers_min_ls_arrival,
+       ospf_timers_min_ls_arrival_cmd,
+       "timers lsa arrival <0-1000>",
+       "Adjust routing timers\n"
+       "Throttling link state advertisement delays\n"
+       "OSPF minimum arrival interval delay\n"
+       "Delay (msec) between accepted LSAs\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int arrival;
+
+  if (argc != 1)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("minimum LSA inter-arrival time", arrival, argv[0], 0, 1000);
+
+  ospf->min_ls_arrival = arrival;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_timers_min_ls_arrival,
+       no_ospf_timers_min_ls_arrival_cmd,
+       "no timers lsa arrival",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling link state advertisement delays\n"
+       "OSPF minimum arrival interval delay\n")
+{
+  struct ospf *ospf = vty->index;
+  ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_timers_throttle_spf,
+       ospf_timers_throttle_spf_cmd,
+       "timers throttle spf <0-600000> <0-600000> <0-600000>",
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF SPF timers\n"
+       "Delay (msec) from first change received till SPF calculation\n"
+       "Initial hold time (msec) between consecutive SPF calculations\n"
+       "Maximum hold time (msec)\n")
+{
+  unsigned int delay, hold, max;
+  
+  if (argc != 3)
+    {
+      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000);
+  VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000);
+  
+  return ospf_timers_spf_set (vty, delay, hold, max);
+}
+
+DEFUN_DEPRECATED (ospf_timers_spf,
+       ospf_timers_spf_cmd,
+       "timers spf <0-4294967295> <0-4294967295>",
+       "Adjust routing timers\n"
+       "OSPF SPF timers\n"
+       "Delay (s) between receiving a change to SPF calculation\n"
+       "Hold time (s) between consecutive SPF calculations\n")
+{
+  unsigned int delay, hold;
+  
+  if (argc != 2)
+    {
+      vty_out (vty, "Insufficient number of arguments%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  VTY_GET_INTEGER ("SPF delay timer", delay, argv[0]);
+  VTY_GET_INTEGER ("SPF hold timer", hold, argv[1]);
+  
+  /* truncate down the second values if they're greater than 600000ms */
+  if (delay > (600000 / 1000))
+    delay = 600000;
+  else if (delay == 0)
+    /* 0s delay was probably specified because of lack of ms resolution */
+    delay = OSPF_SPF_DELAY_DEFAULT;
+  if (hold > (600000 / 1000))
+    hold = 600000;
+      
+  return ospf_timers_spf_set (vty, delay * 1000, hold * 1000, hold * 1000);
+}
+
+DEFUN (no_ospf_timers_throttle_spf,
+       no_ospf_timers_throttle_spf_cmd,
+       "no timers throttle spf",
+       NO_STR
+       "Adjust routing timers\n"
+       "Throttling adaptive timer\n"
+       "OSPF SPF timers\n")
+{
+  return ospf_timers_spf_set (vty,
+                              OSPF_SPF_DELAY_DEFAULT,
+                              OSPF_SPF_HOLDTIME_DEFAULT,
+                              OSPF_SPF_MAX_HOLDTIME_DEFAULT);
+}
+
+ALIAS_DEPRECATED (no_ospf_timers_throttle_spf,
+                  no_ospf_timers_spf_cmd,
+                  "no timers spf",
+                  NO_STR
+                  "Adjust routing timers\n"
+                  "OSPF SPF timers\n")
+
+DEFUN (ospf_neighbor,
+       ospf_neighbor_cmd,
+       "neighbor A.B.C.D",
+       NEIGHBOR_STR
+       "Neighbor IP address\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr nbr_addr;
+  unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
+  unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT;
+
+  VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]);
+
+  if (argc > 1)
+    VTY_GET_INTEGER_RANGE ("neighbor priority", priority, argv[1], 0, 255);
+
+  if (argc > 2)
+    VTY_GET_INTEGER_RANGE ("poll interval", interval, argv[2], 1, 65535);
+
+  ospf_nbr_nbma_set (ospf, nbr_addr);
+  if (argc > 1)
+    ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority);
+  if (argc > 2)
+    ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ospf_neighbor,
+       ospf_neighbor_priority_poll_interval_cmd,
+       "neighbor A.B.C.D priority <0-255> poll-interval <1-65535>",
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Neighbor Priority\n"
+       "Priority\n"
+       "Dead Neighbor Polling interval\n"
+       "Seconds\n")
+
+ALIAS (ospf_neighbor,
+       ospf_neighbor_priority_cmd,
+       "neighbor A.B.C.D priority <0-255>",
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Neighbor Priority\n"
+       "Seconds\n")
+
+DEFUN (ospf_neighbor_poll_interval,
+       ospf_neighbor_poll_interval_cmd,
+       "neighbor A.B.C.D poll-interval <1-65535>",
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Dead Neighbor Polling interval\n"
+       "Seconds\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr nbr_addr;
+  unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
+  unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT;
+
+  VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]);
+
+  if (argc > 1)
+    VTY_GET_INTEGER_RANGE ("poll interval", interval, argv[1], 1, 65535);
+
+  if (argc > 2)
+    VTY_GET_INTEGER_RANGE ("neighbor priority", priority, argv[2], 0, 255);
+
+  ospf_nbr_nbma_set (ospf, nbr_addr);
+  if (argc > 1)
+    ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval);
+  if (argc > 2)
+    ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ospf_neighbor_poll_interval,
+       ospf_neighbor_poll_interval_priority_cmd,
+       "neighbor A.B.C.D poll-interval <1-65535> priority <0-255>",
+       NEIGHBOR_STR
+       "Neighbor address\n"
+       "OSPF dead-router polling interval\n"
+       "Seconds\n"
+       "OSPF priority of non-broadcast neighbor\n"
+       "Priority\n")
+
+DEFUN (no_ospf_neighbor,
+       no_ospf_neighbor_cmd,
+       "no neighbor A.B.C.D",
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor IP address\n")
+{
+  struct ospf *ospf = vty->index;
+  struct in_addr nbr_addr;
+
+  VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]);
+
+  (void)ospf_nbr_nbma_unset (ospf, nbr_addr);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_neighbor,
+       no_ospf_neighbor_priority_cmd,
+       "no neighbor A.B.C.D priority <0-255>",
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Neighbor Priority\n"
+       "Priority\n")
+
+ALIAS (no_ospf_neighbor,
+       no_ospf_neighbor_poll_interval_cmd,
+       "no neighbor A.B.C.D poll-interval <1-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Dead Neighbor Polling interval\n"
+       "Seconds\n")
+
+ALIAS (no_ospf_neighbor,
+       no_ospf_neighbor_priority_pollinterval_cmd,
+       "no neighbor A.B.C.D priority <0-255> poll-interval <1-65535>",
+       NO_STR
+       NEIGHBOR_STR
+       "Neighbor IP address\n"
+       "Neighbor Priority\n"
+       "Priority\n"
+       "Dead Neighbor Polling interval\n"
+       "Seconds\n")
+
+
+DEFUN (ospf_refresh_timer, ospf_refresh_timer_cmd,
+       "refresh timer <10-1800>",
+       "Adjust refresh parameters\n"
+       "Set refresh timer\n"
+       "Timer value in seconds\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int interval;
+  
+  VTY_GET_INTEGER_RANGE ("refresh timer", interval, argv[0], 10, 1800);
+  interval = (interval / 10) * 10;
+
+  ospf_timers_refresh_set (ospf, interval);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_refresh_timer, no_ospf_refresh_timer_val_cmd,
+       "no refresh timer <10-1800>",
+       "Adjust refresh parameters\n"
+       "Unset refresh timer\n"
+       "Timer value in seconds\n")
+{
+  struct ospf *ospf = vty->index;
+  unsigned int interval;
+
+  if (argc == 1)
+    {
+      VTY_GET_INTEGER_RANGE ("refresh timer", interval, argv[0], 10, 1800);
+  
+      if (ospf->lsa_refresh_interval != interval ||
+         interval == OSPF_LSA_REFRESH_INTERVAL_DEFAULT)
+       return CMD_SUCCESS;
+    }
+
+  ospf_timers_refresh_unset (ospf);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_refresh_timer,
+       no_ospf_refresh_timer_cmd,
+       "no refresh timer",
+       "Adjust refresh parameters\n"
+       "Unset refresh timer\n")
+
+DEFUN (ospf_auto_cost_reference_bandwidth,
+       ospf_auto_cost_reference_bandwidth_cmd,
+       "auto-cost reference-bandwidth <1-4294967>",
+       "Calculate OSPF interface cost according to bandwidth\n"
+       "Use reference bandwidth method to assign OSPF cost\n"
+       "The reference bandwidth in terms of Mbits per second\n")
+{
+  struct ospf *ospf = vty->index;
+  u_int32_t refbw;
+  struct listnode *node;
+  struct interface *ifp;
+
+  refbw = strtol (argv[0], NULL, 10);
+  if (refbw < 1 || refbw > 4294967)
+    {
+      vty_out (vty, "reference-bandwidth value is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* If reference bandwidth is changed. */
+  if ((refbw * 1000) == ospf->ref_bandwidth)
+    return CMD_SUCCESS;
+  
+  ospf->ref_bandwidth = refbw * 1000;
+  for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
+    ospf_if_recalculate_output_cost (ifp);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_auto_cost_reference_bandwidth,
+       no_ospf_auto_cost_reference_bandwidth_cmd,
+       "no auto-cost reference-bandwidth",
+       NO_STR
+       "Calculate OSPF interface cost according to bandwidth\n"
+       "Use reference bandwidth method to assign OSPF cost\n")
+{
+  struct ospf *ospf = vty->index;
+  struct listnode *node, *nnode;
+  struct interface *ifp;
+
+  if (ospf->ref_bandwidth == OSPF_DEFAULT_REF_BANDWIDTH)
+    return CMD_SUCCESS;
+  
+  ospf->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH;
+  vty_out (vty, "%% OSPF: Reference bandwidth is changed.%s", VTY_NEWLINE);
+  vty_out (vty, "        Please ensure reference bandwidth is consistent across all routers%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS (om->iflist, node, nnode, ifp))
+    ospf_if_recalculate_output_cost (ifp);
+      
+  return CMD_SUCCESS;
+}
+
+const char *ospf_abr_type_descr_str[] = 
+{
+  "Unknown",
+  "Standard (RFC2328)",
+  "Alternative IBM",
+  "Alternative Cisco",
+  "Alternative Shortcut"
+};
+
+const char *ospf_shortcut_mode_descr_str[] = 
+{
+  "Default",
+  "Enabled",
+  "Disabled"
+};
+
+
+
+static void
+show_ip_ospf_area (struct vty *vty, struct ospf_area *area)
+{
+  /* Show Area ID. */
+  vty_out (vty, " Area ID: %s", inet_ntoa (area->area_id));
+
+  /* Show Area type/mode. */
+  if (OSPF_IS_AREA_BACKBONE (area))
+    vty_out (vty, " (Backbone)%s", VTY_NEWLINE);
+  else
+    {
+      if (area->external_routing == OSPF_AREA_STUB)
+        vty_out (vty, " (Stub%s%s)",
+                        area->no_summary ? ", no summary" : "",
+                        area->shortcut_configured ? "; " : "");
+
+      else if (area->external_routing == OSPF_AREA_NSSA)
+        vty_out (vty, " (NSSA%s%s)",
+                 area->no_summary ? ", no summary" : "",
+                 area->shortcut_configured ? "; " : "");
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+      vty_out (vty, "   Shortcutting mode: %s",
+               ospf_shortcut_mode_descr_str[area->shortcut_configured]);
+      vty_out (vty, ", S-bit consensus: %s%s",
+               area->shortcut_capability ? "ok" : "no", VTY_NEWLINE);
+    }
+
+  /* Show number of interfaces. */
+  vty_out (vty, "   Number of interfaces in this area: Total: %d, "
+          "Active: %d%s", listcount (area->oiflist),
+          area->act_ints, VTY_NEWLINE);
+
+  if (area->external_routing == OSPF_AREA_NSSA)
+    {
+      vty_out (vty, "   It is an NSSA configuration. %s   Elected NSSA/ABR performs type-7/type-5 LSA translation. %s", VTY_NEWLINE, VTY_NEWLINE);
+      if (! IS_OSPF_ABR (area->ospf))
+        vty_out (vty, "   It is not ABR, therefore not Translator. %s",
+                 VTY_NEWLINE);
+      else if (area->NSSATranslatorState)
+       {
+         vty_out (vty, "   We are an ABR and ");
+         if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_CANDIDATE)
+           vty_out (vty, "the NSSA Elected Translator. %s", 
+                       VTY_NEWLINE);
+         else if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS)
+           vty_out (vty, "always an NSSA Translator. %s",
+                    VTY_NEWLINE);
+       }
+      else
+       {
+         vty_out (vty, "   We are an ABR, but ");
+         if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_CANDIDATE)
+           vty_out (vty, "not the NSSA Elected Translator. %s",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "never an NSSA Translator. %s", 
+                    VTY_NEWLINE);
+          }
+    }
+  /* Stub-router state for this area */
+  if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+    {
+      char timebuf[OSPF_TIME_DUMP_SIZE];
+      vty_out (vty, "   Originating stub / maximum-distance Router-LSA%s",
+               VTY_NEWLINE);
+      if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED))
+        vty_out (vty, "     Administratively activated (indefinitely)%s",
+                 VTY_NEWLINE);
+      if (area->t_stub_router)
+        vty_out (vty, "     Active from startup, %s remaining%s",
+                 ospf_timer_dump (area->t_stub_router, timebuf, 
+                                  sizeof(timebuf)), VTY_NEWLINE);
+    }
+  
+  /* Show number of fully adjacent neighbors. */
+  vty_out (vty, "   Number of fully adjacent neighbors in this area:"
+                " %d%s", area->full_nbrs, VTY_NEWLINE);
+
+  /* Show authentication type. */
+  vty_out (vty, "   Area has ");
+  if (area->auth_type == OSPF_AUTH_NULL)
+    vty_out (vty, "no authentication%s", VTY_NEWLINE);
+  else if (area->auth_type == OSPF_AUTH_SIMPLE)
+    vty_out (vty, "simple password authentication%s", VTY_NEWLINE);
+  else if (area->auth_type == OSPF_AUTH_CRYPTOGRAPHIC)
+    vty_out (vty, "message digest authentication%s", VTY_NEWLINE);
+
+  if (!OSPF_IS_AREA_BACKBONE (area))
+    vty_out (vty, "   Number of full virtual adjacencies going through"
+            " this area: %d%s", area->full_vls, VTY_NEWLINE);
+
+  /* Show SPF calculation times. */
+  vty_out (vty, "   SPF algorithm executed %d times%s",
+          area->spf_calculation, VTY_NEWLINE);
+
+  /* Show number of LSA. */
+  vty_out (vty, "   Number of LSA %ld%s", area->lsdb->total, VTY_NEWLINE);
+  vty_out (vty, "   Number of router LSA %ld. Checksum Sum 0x%08x%s",
+          ospf_lsdb_count (area->lsdb, OSPF_ROUTER_LSA),
+          ospf_lsdb_checksum (area->lsdb, OSPF_ROUTER_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of network LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_NETWORK_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_NETWORK_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of summary LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_SUMMARY_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_SUMMARY_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of ASBR summary LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_ASBR_SUMMARY_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_ASBR_SUMMARY_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of NSSA LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_AS_NSSA_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_AS_NSSA_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of opaque link LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_OPAQUE_LINK_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_OPAQUE_LINK_LSA), VTY_NEWLINE);
+  vty_out (vty, "   Number of opaque area LSA %ld. Checksum Sum 0x%08x%s",
+           ospf_lsdb_count (area->lsdb, OSPF_OPAQUE_AREA_LSA),
+           ospf_lsdb_checksum (area->lsdb, OSPF_OPAQUE_AREA_LSA), VTY_NEWLINE);
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+DEFUN (show_ip_ospf,
+       show_ip_ospf_cmd,
+       "show ip ospf",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n")
+{
+  struct listnode *node, *nnode;
+  struct ospf_area * area;
+  struct ospf *ospf;
+  struct timeval result;
+  char timebuf[OSPF_TIME_DUMP_SIZE];
+
+  /* Check OSPF is enable. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  /* Show Router ID. */
+  vty_out (vty, " OSPF Routing Process, Router ID: %s%s",
+           inet_ntoa (ospf->router_id),
+           VTY_NEWLINE);
+  
+  /* Graceful shutdown */
+  if (ospf->t_deferred_shutdown)
+    vty_out (vty, " Deferred shutdown in progress, %s remaining%s",
+             ospf_timer_dump (ospf->t_deferred_shutdown,
+                              timebuf, sizeof (timebuf)), VTY_NEWLINE);
+  /* Show capability. */
+  vty_out (vty, " Supports only single TOS (TOS0) routes%s", VTY_NEWLINE);
+  vty_out (vty, " This implementation conforms to RFC2328%s", VTY_NEWLINE);
+  vty_out (vty, " RFC1583Compatibility flag is %s%s",
+          CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE) ?
+          "enabled" : "disabled", VTY_NEWLINE);
+  vty_out (vty, " OpaqueCapability flag is %s%s",
+          CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE) ?
+           "enabled" : "disabled",
+           VTY_NEWLINE);
+  
+  /* Show stub-router configuration */
+  if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED
+      || ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+    {
+      vty_out (vty, " Stub router advertisement is configured%s",
+               VTY_NEWLINE);
+      if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+        vty_out (vty, "   Enabled for %us after start-up%s",
+                 ospf->stub_router_startup_time, VTY_NEWLINE);
+      if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+        vty_out (vty, "   Enabled for %us prior to full shutdown%s",
+                 ospf->stub_router_shutdown_time, VTY_NEWLINE);
+    }
+  
+  /* Show SPF timers. */
+  vty_out (vty, " Initial SPF scheduling delay %d millisec(s)%s"
+                " Minimum hold time between consecutive SPFs %d millisec(s)%s"
+                " Maximum hold time between consecutive SPFs %d millisec(s)%s"
+                " Hold time multiplier is currently %d%s",
+         ospf->spf_delay, VTY_NEWLINE,
+         ospf->spf_holdtime, VTY_NEWLINE,
+         ospf->spf_max_holdtime, VTY_NEWLINE,
+         ospf->spf_hold_multiplier, VTY_NEWLINE);
+  vty_out (vty, " SPF algorithm ");
+  if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec)
+    {
+      result = tv_sub (recent_relative_time (), ospf->ts_spf);
+      vty_out (vty, "last executed %s ago%s",
+               ospf_timeval_dump (&result, timebuf, sizeof (timebuf)),
+               VTY_NEWLINE);
+      vty_out (vty, " Last SPF duration %s%s",
+              ospf_timeval_dump (&ospf->ts_spf_duration, timebuf, sizeof (timebuf)),
+              VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "has not been run%s", VTY_NEWLINE);
+  vty_out (vty, " SPF timer %s%s%s",
+           (ospf->t_spf_calc ? "due in " : "is "),
+           ospf_timer_dump (ospf->t_spf_calc, timebuf, sizeof (timebuf)),
+           VTY_NEWLINE);
+  
+  /* Show refresh parameters. */
+  vty_out (vty, " Refresh timer %d secs%s",
+          ospf->lsa_refresh_interval, VTY_NEWLINE);
+          
+  /* Show ABR/ASBR flags. */
+  if (CHECK_FLAG (ospf->flags, OSPF_FLAG_ABR))
+    vty_out (vty, " This router is an ABR, ABR type is: %s%s",
+             ospf_abr_type_descr_str[ospf->abr_type], VTY_NEWLINE);
+
+  if (CHECK_FLAG (ospf->flags, OSPF_FLAG_ASBR))
+    vty_out (vty, " This router is an ASBR "
+             "(injecting external routing information)%s", VTY_NEWLINE);
+
+  /* Show Number of AS-external-LSAs. */
+  vty_out (vty, " Number of external LSA %ld. Checksum Sum 0x%08x%s",
+          ospf_lsdb_count (ospf->lsdb, OSPF_AS_EXTERNAL_LSA),
+          ospf_lsdb_checksum (ospf->lsdb, OSPF_AS_EXTERNAL_LSA), VTY_NEWLINE);
+  vty_out (vty, " Number of opaque AS LSA %ld. Checksum Sum 0x%08x%s",
+          ospf_lsdb_count (ospf->lsdb, OSPF_OPAQUE_AS_LSA),
+          ospf_lsdb_checksum (ospf->lsdb, OSPF_OPAQUE_AS_LSA), VTY_NEWLINE);
+  /* Show number of areas attached. */
+  vty_out (vty, " Number of areas attached to this router: %d%s",
+           listcount (ospf->areas), VTY_NEWLINE);
+
+  if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES))
+    {
+      if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
+       vty_out(vty, " All adjacency changes are logged%s",VTY_NEWLINE);
+      else
+       vty_out(vty, " Adjacency changes are logged%s",VTY_NEWLINE);
+    }
+
+  vty_out (vty, "%s",VTY_NEWLINE);
+
+  /* Show each area status. */
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    show_ip_ospf_area (vty, area);
+
+  return CMD_SUCCESS;
+}
+
+
+static void
+show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf,
+                           struct interface *ifp)
+{
+  int is_up;
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+
+  /* Is interface up? */
+  vty_out (vty, "%s is %s%s", ifp->name,
+          ((is_up = if_is_operative(ifp)) ? "up" : "down"), VTY_NEWLINE);
+  vty_out (vty, "  ifindex %u, MTU %u bytes, BW %u Kbit %s%s",
+          ifp->ifindex, ifp->mtu, ifp->bandwidth, if_flag_dump(ifp->flags),
+          VTY_NEWLINE);
+
+  /* Is interface OSPF enabled? */
+  if (ospf_oi_count(ifp) == 0)
+    {
+      vty_out (vty, "  OSPF not enabled on this interface%s", VTY_NEWLINE);
+      return;
+    }
+  else if (!is_up)
+    {
+      vty_out (vty, "  OSPF is enabled, but not running on this interface%s",
+              VTY_NEWLINE);
+      return;
+    }
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+      
+      if (oi == NULL)
+       continue;
+      
+      /* Show OSPF interface information. */
+      vty_out (vty, "  Internet Address %s/%d,",
+              inet_ntoa (oi->address->u.prefix4), oi->address->prefixlen);
+
+      if (oi->connected->destination || oi->type == OSPF_IFTYPE_VIRTUALLINK)
+        {
+          struct in_addr *dest;
+          const char *dstr;
+          
+         if (CONNECTED_PEER(oi->connected)
+             || oi->type == OSPF_IFTYPE_VIRTUALLINK)
+            dstr = "Peer";
+          else
+            dstr = "Broadcast";
+          
+          /* For Vlinks, showing the peer address is probably more
+           * informative than the local interface that is being used
+           */
+          if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+            dest = &oi->vl_data->peer_addr;
+          else
+            dest = &oi->connected->destination->u.prefix4;
+          
+         vty_out (vty, " %s %s,", dstr, inet_ntoa (*dest));
+        }
+
+      vty_out (vty, " Area %s%s", ospf_area_desc_string (oi->area),
+              VTY_NEWLINE);
+
+      vty_out (vty, "  MTU mismatch detection:%s%s",
+           OSPF_IF_PARAM(oi, mtu_ignore) ? "disabled" : "enabled", VTY_NEWLINE);
+
+      vty_out (vty, "  Router ID %s, Network Type %s, Cost: %d%s",
+              inet_ntoa (ospf->router_id), ospf_network_type_str[oi->type],
+              oi->output_cost, VTY_NEWLINE);
+
+      vty_out (vty, "  Transmit Delay is %d sec, State %s, Priority %d%s",
+              OSPF_IF_PARAM (oi,transmit_delay), LOOKUP (ospf_ism_state_msg, oi->state),
+              PRIORITY (oi), VTY_NEWLINE);
+
+  /* Show DR information. */
+      if (DR (oi).s_addr == 0)
+       vty_out (vty, "  No designated router on this network%s", VTY_NEWLINE);
+      else
+       {
+         nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi));
+         if (nbr == NULL)
+           vty_out (vty, "  No designated router on this network%s", VTY_NEWLINE);
+         else
+           {
+             vty_out (vty, "  Designated Router (ID) %s,",
+                      inet_ntoa (nbr->router_id));
+             vty_out (vty, " Interface Address %s%s",
+                      inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+           }
+       }
+
+      /* Show BDR information. */
+      if (BDR (oi).s_addr == 0)
+       vty_out (vty, "  No backup designated router on this network%s",
+                VTY_NEWLINE);
+      else
+       {
+         nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &BDR (oi));
+         if (nbr == NULL)
+           vty_out (vty, "  No backup designated router on this network%s",
+                    VTY_NEWLINE);
+         else
+           {
+             vty_out (vty, "  Backup Designated Router (ID) %s,",
+                      inet_ntoa (nbr->router_id));
+             vty_out (vty, " Interface Address %s%s",
+                      inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+           }
+       }
+      
+      /* Next network-LSA sequence number we'll use, if we're elected DR */
+      if (oi->params && ntohl (oi->params->network_lsa_seqnum)
+                          != OSPF_INITIAL_SEQUENCE_NUMBER)
+        vty_out (vty, "  Saved Network-LSA sequence number 0x%x%s",
+                 ntohl (oi->params->network_lsa_seqnum), VTY_NEWLINE);
+      
+      vty_out (vty, "  Multicast group memberships:");
+      if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)
+          || OI_MEMBER_CHECK(oi, MEMBER_DROUTERS))
+        {
+          if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS))
+            vty_out (vty, " OSPFAllRouters");
+          if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS))
+            vty_out (vty, " OSPFDesignatedRouters");
+        }
+      else
+        vty_out (vty, " <None>");
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+      vty_out (vty, "  Timer intervals configured,");
+      vty_out (vty, " Hello ");
+      if (OSPF_IF_PARAM (oi, fast_hello) == 0)
+        vty_out (vty, "%ds,", OSPF_IF_PARAM (oi, v_hello));
+      else
+        vty_out (vty, "%dms,", 1000 / OSPF_IF_PARAM (oi, fast_hello));
+      vty_out (vty, " Dead %ds, Wait %ds, Retransmit %d%s",
+              OSPF_IF_PARAM (oi, v_wait),
+              OSPF_IF_PARAM (oi, v_wait),
+              OSPF_IF_PARAM (oi, retransmit_interval),
+              VTY_NEWLINE);
+      
+      if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_ACTIVE)
+        {
+         char timebuf[OSPF_TIME_DUMP_SIZE];
+         vty_out (vty, "    Hello due in %s%s",
+                  ospf_timer_dump (oi->t_hello, timebuf, sizeof(timebuf)), 
+                  VTY_NEWLINE);
+        }
+      else /* passive-interface is set */
+       vty_out (vty, "    No Hellos (Passive interface)%s", VTY_NEWLINE);
+      
+      vty_out (vty, "  Neighbor Count is %d, Adjacent neighbor count is %d%s",
+              ospf_nbr_count (oi, 0), ospf_nbr_count (oi, NSM_Full),
+              VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_ospf_interface,
+       show_ip_ospf_interface_cmd,
+       "show ip ospf interface [INTERFACE]",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Interface information\n"
+       "Interface name\n")
+{
+  struct interface *ifp;
+  struct ospf *ospf;
+  struct listnode *node;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, "OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  /* Show All Interfaces. */
+  if (argc == 0)
+    for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+      show_ip_ospf_interface_sub (vty, ospf, ifp);
+  /* Interface name is specified. */
+  else
+    {
+      if ((ifp = if_lookup_by_name (argv[0])) == NULL)
+        vty_out (vty, "No such interface name%s", VTY_NEWLINE);
+      else
+        show_ip_ospf_interface_sub (vty, ospf, ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+show_ip_ospf_neighbour_header (struct vty *vty)
+{
+  vty_out (vty, "%s%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s%s",
+           VTY_NEWLINE,
+           "Neighbor ID", "Pri", "State", "Dead Time",
+           "Address", "Interface", "RXmtL", "RqstL", "DBsmL",
+           VTY_NEWLINE);
+}
+
+static void
+show_ip_ospf_neighbor_sub (struct vty *vty, struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+  char msgbuf[16];
+  char timebuf[OSPF_TIME_DUMP_SIZE];
+
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      /* Do not show myself. */
+      if (nbr != oi->nbr_self)
+       /* Down state is not shown. */
+       if (nbr->state != NSM_Down)
+         {
+           ospf_nbr_state_message (nbr, msgbuf, 16);
+
+           if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0)
+             vty_out (vty, "%-15s %3d %-15s ",
+                      "-", nbr->priority,
+                      msgbuf);
+            else
+             vty_out (vty, "%-15s %3d %-15s ",
+                      inet_ntoa (nbr->router_id), nbr->priority,
+                      msgbuf);
+            
+            vty_out (vty, "%9s ",
+                     ospf_timer_dump (nbr->t_inactivity, timebuf, 
+                                      sizeof(timebuf)));
+            
+           vty_out (vty, "%-15s ", inet_ntoa (nbr->src));
+           vty_out (vty, "%-20s %5ld %5ld %5d%s",
+                    IF_NAME (oi), ospf_ls_retransmit_count (nbr),
+                    ospf_ls_request_count (nbr), ospf_db_summary_count (nbr),
+                    VTY_NEWLINE);
+         }
+}
+
+DEFUN (show_ip_ospf_neighbor,
+       show_ip_ospf_neighbor_cmd,
+       "show ip ospf neighbor",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n")
+{
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+  struct listnode *node;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  show_ip_ospf_neighbour_header (vty);
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    show_ip_ospf_neighbor_sub (vty, oi);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_neighbor_all,
+       show_ip_ospf_neighbor_all_cmd,
+       "show ip ospf neighbor all",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "include down status neighbor\n")
+{
+  struct ospf *ospf = ospf_lookup ();
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+  
+  show_ip_ospf_neighbour_header (vty);
+  
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    {
+      struct listnode *nbr_node;
+      struct ospf_nbr_nbma *nbr_nbma;
+
+      show_ip_ospf_neighbor_sub (vty, oi);
+
+    /* print Down neighbor status */
+    for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, nbr_node, nbr_nbma))
+      {
+       if (nbr_nbma->nbr == NULL
+           || nbr_nbma->nbr->state == NSM_Down)
+         {
+           vty_out (vty, "%-15s %3d %-15s %9s ",
+                    "-", nbr_nbma->priority, "Down", "-");
+           vty_out (vty, "%-15s %-20s %5d %5d %5d%s", 
+                    inet_ntoa (nbr_nbma->addr), IF_NAME (oi),
+                    0, 0, 0, VTY_NEWLINE);
+         }
+      }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_neighbor_int,
+       show_ip_ospf_neighbor_int_cmd,
+       "show ip ospf neighbor IFNAME",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "Interface name\n")
+{
+  struct ospf *ospf;
+  struct interface *ifp;
+  struct route_node *rn;
+  ifp = if_lookup_by_name (argv[0]);
+  if (!ifp)
+    {
+      vty_out (vty, "No such interface.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+  
+  show_ip_ospf_neighbour_header (vty);
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+
+      if (oi == NULL)
+       continue;
+
+      show_ip_ospf_neighbor_sub (vty, oi);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+show_ip_ospf_nbr_nbma_detail_sub (struct vty *vty, struct ospf_interface *oi,
+                                 struct ospf_nbr_nbma *nbr_nbma)
+{
+  char timebuf[OSPF_TIME_DUMP_SIZE];
+
+  /* Show neighbor ID. */
+  vty_out (vty, " Neighbor %s,", "-");
+
+  /* Show interface address. */
+  vty_out (vty, " interface address %s%s",
+          inet_ntoa (nbr_nbma->addr), VTY_NEWLINE);
+  /* Show Area ID. */
+  vty_out (vty, "    In the area %s via interface %s%s",
+          ospf_area_desc_string (oi->area), IF_NAME (oi), VTY_NEWLINE);
+  /* Show neighbor priority and state. */
+  vty_out (vty, "    Neighbor priority is %d, State is %s,",
+          nbr_nbma->priority, "Down");
+  /* Show state changes. */
+  vty_out (vty, " %d state changes%s", nbr_nbma->state_change, VTY_NEWLINE);
+
+  /* Show PollInterval */
+  vty_out (vty, "    Poll interval %d%s", nbr_nbma->v_poll, VTY_NEWLINE);
+
+  /* Show poll-interval timer. */
+  vty_out (vty, "    Poll timer due in %s%s",
+          ospf_timer_dump (nbr_nbma->t_poll, timebuf, sizeof(timebuf)),
+           VTY_NEWLINE);
+
+  /* Show poll-interval timer thread. */
+  vty_out (vty, "    Thread Poll Timer %s%s", 
+          nbr_nbma->t_poll != NULL ? "on" : "off", VTY_NEWLINE);
+}
+
+static void
+show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi,
+                                 struct ospf_neighbor *nbr)
+{
+  char timebuf[OSPF_TIME_DUMP_SIZE];
+
+  /* Show neighbor ID. */
+  if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0)
+    vty_out (vty, " Neighbor %s,", "-");
+  else
+  vty_out (vty, " Neighbor %s,", inet_ntoa (nbr->router_id));
+
+  /* Show interface address. */
+  vty_out (vty, " interface address %s%s",
+          inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE);
+  /* Show Area ID. */
+  vty_out (vty, "    In the area %s via interface %s%s",
+          ospf_area_desc_string (oi->area), oi->ifp->name, VTY_NEWLINE);
+  /* Show neighbor priority and state. */
+  vty_out (vty, "    Neighbor priority is %d, State is %s,",
+          nbr->priority, LOOKUP (ospf_nsm_state_msg, nbr->state));
+  /* Show state changes. */
+  vty_out (vty, " %d state changes%s", nbr->state_change, VTY_NEWLINE);
+  if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec)
+    {
+      struct timeval res
+        = tv_sub (recent_relative_time (), nbr->ts_last_progress);
+      vty_out (vty, "    Most recent state change statistics:%s",
+               VTY_NEWLINE);
+      vty_out (vty, "      Progressive change %s ago%s",
+               ospf_timeval_dump (&res, timebuf, sizeof(timebuf)),
+               VTY_NEWLINE);
+    }
+  if (nbr->ts_last_regress.tv_sec || nbr->ts_last_regress.tv_usec)
+    {
+      struct timeval res
+        = tv_sub (recent_relative_time (), nbr->ts_last_regress);
+      vty_out (vty, "      Regressive change %s ago, due to %s%s",
+               ospf_timeval_dump (&res, timebuf, sizeof(timebuf)),
+               (nbr->last_regress_str ? nbr->last_regress_str : "??"),
+               VTY_NEWLINE);
+    }
+  /* Show Designated Rotuer ID. */
+  vty_out (vty, "    DR is %s,", inet_ntoa (nbr->d_router));
+  /* Show Backup Designated Rotuer ID. */
+  vty_out (vty, " BDR is %s%s", inet_ntoa (nbr->bd_router), VTY_NEWLINE);
+  /* Show options. */
+  vty_out (vty, "    Options %d %s%s", nbr->options,
+          ospf_options_dump (nbr->options), VTY_NEWLINE);
+  /* Show Router Dead interval timer. */
+  vty_out (vty, "    Dead timer due in %s%s",
+          ospf_timer_dump (nbr->t_inactivity, timebuf, sizeof (timebuf)),
+           VTY_NEWLINE);
+  /* Show Database Summary list. */
+  vty_out (vty, "    Database Summary List %d%s",
+          ospf_db_summary_count (nbr), VTY_NEWLINE);
+  /* Show Link State Request list. */
+  vty_out (vty, "    Link State Request List %ld%s",
+          ospf_ls_request_count (nbr), VTY_NEWLINE);
+  /* Show Link State Retransmission list. */
+  vty_out (vty, "    Link State Retransmission List %ld%s",
+          ospf_ls_retransmit_count (nbr), VTY_NEWLINE);
+  /* Show inactivity timer thread. */
+  vty_out (vty, "    Thread Inactivity Timer %s%s", 
+          nbr->t_inactivity != NULL ? "on" : "off", VTY_NEWLINE);
+  /* Show Database Description retransmission thread. */
+  vty_out (vty, "    Thread Database Description Retransmision %s%s",
+          nbr->t_db_desc != NULL ? "on" : "off", VTY_NEWLINE);
+  /* Show Link State Request Retransmission thread. */
+  vty_out (vty, "    Thread Link State Request Retransmission %s%s",
+          nbr->t_ls_req != NULL ? "on" : "off", VTY_NEWLINE);
+  /* Show Link State Update Retransmission thread. */
+  vty_out (vty, "    Thread Link State Update Retransmission %s%s%s",
+          nbr->t_ls_upd != NULL ? "on" : "off", VTY_NEWLINE, VTY_NEWLINE);
+}
+
+DEFUN (show_ip_ospf_neighbor_id,
+       show_ip_ospf_neighbor_id_cmd,
+       "show ip ospf neighbor A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "Neighbor ID\n")
+{
+  struct ospf *ospf;
+  struct listnode *node;
+  struct ospf_neighbor *nbr;
+  struct ospf_interface *oi;
+  struct in_addr router_id;
+  int ret;
+
+  ret = inet_aton (argv[0], &router_id);
+  if (!ret)
+    {
+      vty_out (vty, "Please specify Neighbor ID by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    if ((nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &router_id)))
+      show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_neighbor_detail,
+       show_ip_ospf_neighbor_detail_cmd,
+       "show ip ospf neighbor detail",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "detail of all neighbors\n")
+{
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+  struct listnode *node;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    {
+      struct route_node *rn;
+      struct ospf_neighbor *nbr;
+
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info))
+         if (nbr != oi->nbr_self)
+           if (nbr->state != NSM_Down)
+             show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_neighbor_detail_all,
+       show_ip_ospf_neighbor_detail_all_cmd,
+       "show ip ospf neighbor detail all",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "detail of all neighbors\n"
+       "include down status neighbor\n")
+{
+  struct ospf *ospf;
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    {
+      struct route_node *rn;
+      struct ospf_neighbor *nbr;
+      struct ospf_nbr_nbma *nbr_nbma;
+
+      for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+       if ((nbr = rn->info))
+         if (nbr != oi->nbr_self)
+           if (oi->type == OSPF_IFTYPE_NBMA && nbr->state != NSM_Down)
+             show_ip_ospf_neighbor_detail_sub (vty, oi, rn->info);
+
+      if (oi->type == OSPF_IFTYPE_NBMA)
+       {
+         struct listnode *nd;
+
+         for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, nd, nbr_nbma))
+            if (nbr_nbma->nbr == NULL
+                || nbr_nbma->nbr->state == NSM_Down)
+              show_ip_ospf_nbr_nbma_detail_sub (vty, oi, nbr_nbma);
+       }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_neighbor_int_detail,
+       show_ip_ospf_neighbor_int_detail_cmd,
+       "show ip ospf neighbor IFNAME detail",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Neighbor list\n"
+       "Interface name\n"
+       "detail of all neighbors")
+{
+  struct ospf *ospf;
+  struct ospf_interface *oi;
+  struct interface *ifp;
+  struct route_node *rn, *nrn;
+  struct ospf_neighbor *nbr;
+
+  ifp = if_lookup_by_name (argv[0]);
+  if (!ifp)
+    {
+      vty_out (vty, "No such interface.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    if ((oi = rn->info))
+      for (nrn = route_top (oi->nbrs); nrn; nrn = route_next (nrn))
+       if ((nbr = nrn->info))
+         if (nbr != oi->nbr_self)
+           if (nbr->state != NSM_Down)
+             show_ip_ospf_neighbor_detail_sub (vty, oi, nbr);
+
+  return CMD_SUCCESS;
+}
+
+
+/* Show functions */
+static int
+show_lsa_summary (struct vty *vty, struct ospf_lsa *lsa, int self)
+{
+  struct router_lsa *rl;
+  struct summary_lsa *sl;
+  struct as_external_lsa *asel;
+  struct prefix_ipv4 p;
+
+  if (lsa != NULL)
+    /* If self option is set, check LSA self flag. */
+    if (self == 0 || IS_LSA_SELF (lsa))
+      {
+       /* LSA common part show. */
+       vty_out (vty, "%-15s ", inet_ntoa (lsa->data->id));
+       vty_out (vty, "%-15s %4d 0x%08lx 0x%04x",
+                inet_ntoa (lsa->data->adv_router), LS_AGE (lsa),
+                (u_long)ntohl (lsa->data->ls_seqnum), ntohs (lsa->data->checksum));
+       /* LSA specific part show. */
+       switch (lsa->data->type)
+         {
+         case OSPF_ROUTER_LSA:
+           rl = (struct router_lsa *) lsa->data;
+           vty_out (vty, " %-d", ntohs (rl->links));
+           break;
+         case OSPF_SUMMARY_LSA:
+           sl = (struct summary_lsa *) lsa->data;
+
+           p.family = AF_INET;
+           p.prefix = sl->header.id;
+           p.prefixlen = ip_masklen (sl->mask);
+           apply_mask_ipv4 (&p);
+
+           vty_out (vty, " %s/%d", inet_ntoa (p.prefix), p.prefixlen);
+           break;
+         case OSPF_AS_EXTERNAL_LSA:
+         case OSPF_AS_NSSA_LSA:
+           asel = (struct as_external_lsa *) lsa->data;
+
+           p.family = AF_INET;
+           p.prefix = asel->header.id;
+           p.prefixlen = ip_masklen (asel->mask);
+           apply_mask_ipv4 (&p);
+
+           vty_out (vty, " %s %s/%d [0x%lx]",
+                    IS_EXTERNAL_METRIC (asel->e[0].tos) ? "E2" : "E1",
+                    inet_ntoa (p.prefix), p.prefixlen,
+                    (u_long)ntohl (asel->e[0].route_tag));
+           break;
+         case OSPF_NETWORK_LSA:
+         case OSPF_ASBR_SUMMARY_LSA:
+         case OSPF_OPAQUE_LINK_LSA:
+         case OSPF_OPAQUE_AREA_LSA:
+         case OSPF_OPAQUE_AS_LSA:
+         default:
+           break;
+         }
+       vty_out (vty, VTY_NEWLINE);
+      }
+
+  return 0;
+}
+
+static const char *show_database_desc[] =
+{
+  "unknown",
+  "Router Link States",
+  "Net Link States",
+  "Summary Link States",
+  "ASBR-Summary Link States",
+  "AS External Link States",
+  "Group Membership LSA",
+  "NSSA-external Link States",
+  "Type-8 LSA",
+  "Link-Local Opaque-LSA",
+  "Area-Local Opaque-LSA",
+  "AS-external Opaque-LSA",
+};
+
+static const char *show_database_header[] =
+{
+  "",
+  "Link ID         ADV Router      Age  Seq#       CkSum  Link count",
+  "Link ID         ADV Router      Age  Seq#       CkSum",
+  "Link ID         ADV Router      Age  Seq#       CkSum  Route",
+  "Link ID         ADV Router      Age  Seq#       CkSum",
+  "Link ID         ADV Router      Age  Seq#       CkSum  Route",
+  " --- header for Group Member ----",
+  "Link ID         ADV Router      Age  Seq#       CkSum  Route",
+  " --- type-8 ---",
+  "Opaque-Type/Id  ADV Router      Age  Seq#       CkSum",
+  "Opaque-Type/Id  ADV Router      Age  Seq#       CkSum",
+  "Opaque-Type/Id  ADV Router      Age  Seq#       CkSum",
+};
+
+static void
+show_ip_ospf_database_header (struct vty *vty, struct ospf_lsa *lsa)
+{
+  struct router_lsa *rlsa = (struct router_lsa*) lsa->data;
+  
+  vty_out (vty, "  LS age: %d%s", LS_AGE (lsa), VTY_NEWLINE);
+  vty_out (vty, "  Options: 0x%-2x : %s%s", 
+           lsa->data->options,
+           ospf_options_dump(lsa->data->options), 
+           VTY_NEWLINE);
+  vty_out (vty, "  LS Flags: 0x%-2x %s%s",
+           lsa->flags,
+           ((lsa->flags & OSPF_LSA_LOCAL_XLT) ? "(Translated from Type-7)" : ""),
+           VTY_NEWLINE);
+
+  if (lsa->data->type == OSPF_ROUTER_LSA)
+    {
+      vty_out (vty, "  Flags: 0x%x" , rlsa->flags);
+
+      if (rlsa->flags)
+       vty_out (vty, " :%s%s%s%s",
+                IS_ROUTER_LSA_BORDER (rlsa) ? " ABR" : "",
+                IS_ROUTER_LSA_EXTERNAL (rlsa) ? " ASBR" : "",
+                IS_ROUTER_LSA_VIRTUAL (rlsa) ? " VL-endpoint" : "",
+                IS_ROUTER_LSA_SHORTCUT (rlsa) ? " Shortcut" : "");
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  vty_out (vty, "  LS Type: %s%s",
+           LOOKUP (ospf_lsa_type_msg, lsa->data->type), VTY_NEWLINE);
+  vty_out (vty, "  Link State ID: %s %s%s", inet_ntoa (lsa->data->id),
+           LOOKUP (ospf_link_state_id_type_msg, lsa->data->type), VTY_NEWLINE);
+  vty_out (vty, "  Advertising Router: %s%s",
+           inet_ntoa (lsa->data->adv_router), VTY_NEWLINE);
+  vty_out (vty, "  LS Seq Number: %08lx%s", (u_long)ntohl (lsa->data->ls_seqnum),
+           VTY_NEWLINE);
+  vty_out (vty, "  Checksum: 0x%04x%s", ntohs (lsa->data->checksum),
+           VTY_NEWLINE);
+  vty_out (vty, "  Length: %d%s", ntohs (lsa->data->length), VTY_NEWLINE);
+}
+
+const char *link_type_desc[] =
+{
+  "(null)",
+  "another Router (point-to-point)",
+  "a Transit Network",
+  "Stub Network",
+  "a Virtual Link",
+};
+
+const char *link_id_desc[] =
+{
+  "(null)",
+  "Neighboring Router ID",
+  "Designated Router address",
+  "Net",
+  "Neighboring Router ID",
+};
+
+const char *link_data_desc[] =
+{
+  "(null)",
+  "Router Interface address",
+  "Router Interface address",
+  "Network Mask",
+  "Router Interface address",
+};
+
+/* Show router-LSA each Link information. */
+static void
+show_ip_ospf_database_router_links (struct vty *vty,
+                                    struct router_lsa *rl)
+{
+  int len, type;
+  unsigned int i;
+
+  len = ntohs (rl->header.length) - 4;
+  for (i = 0; i < ntohs (rl->links) && len > 0; len -= 12, i++)
+    {
+      type = rl->link[i].type;
+
+      vty_out (vty, "    Link connected to: %s%s",
+              link_type_desc[type], VTY_NEWLINE);
+      vty_out (vty, "     (Link ID) %s: %s%s", link_id_desc[type],
+              inet_ntoa (rl->link[i].link_id), VTY_NEWLINE);
+      vty_out (vty, "     (Link Data) %s: %s%s", link_data_desc[type],
+              inet_ntoa (rl->link[i].link_data), VTY_NEWLINE);
+      vty_out (vty, "      Number of TOS metrics: 0%s", VTY_NEWLINE);
+      vty_out (vty, "       TOS 0 Metric: %d%s",
+              ntohs (rl->link[i].metric), VTY_NEWLINE);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+/* Show router-LSA detail information. */
+static int
+show_router_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      struct router_lsa *rl = (struct router_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+          
+      vty_out (vty, "   Number of Links: %d%s%s", ntohs (rl->links),
+              VTY_NEWLINE, VTY_NEWLINE);
+
+      show_ip_ospf_database_router_links (vty, rl);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+/* Show network-LSA detail information. */
+static int
+show_network_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  int length, i;
+
+  if (lsa != NULL)
+    {
+      struct network_lsa *nl = (struct network_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+
+      vty_out (vty, "  Network Mask: /%d%s",
+              ip_masklen (nl->mask), VTY_NEWLINE);
+
+      length = ntohs (lsa->data->length) - OSPF_LSA_HEADER_SIZE - 4;
+
+      for (i = 0; length > 0; i++, length -= 4)
+       vty_out (vty, "        Attached Router: %s%s",
+                inet_ntoa (nl->routers[i]), VTY_NEWLINE);
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+/* Show summary-LSA detail information. */
+static int
+show_summary_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      struct summary_lsa *sl = (struct summary_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+
+      vty_out (vty, "  Network Mask: /%d%s", ip_masklen (sl->mask),
+              VTY_NEWLINE);
+      vty_out (vty, "        TOS: 0  Metric: %d%s", GET_METRIC (sl->metric),
+              VTY_NEWLINE);
+         vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+/* Show summary-ASBR-LSA detail information. */
+static int
+show_summary_asbr_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      struct summary_lsa *sl = (struct summary_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+
+      vty_out (vty, "  Network Mask: /%d%s",
+              ip_masklen (sl->mask), VTY_NEWLINE);
+      vty_out (vty, "        TOS: 0  Metric: %d%s", GET_METRIC (sl->metric),
+              VTY_NEWLINE);
+         vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+/* Show AS-external-LSA detail information. */
+static int
+show_as_external_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      struct as_external_lsa *al = (struct as_external_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+
+      vty_out (vty, "  Network Mask: /%d%s",
+              ip_masklen (al->mask), VTY_NEWLINE);
+      vty_out (vty, "        Metric Type: %s%s",
+              IS_EXTERNAL_METRIC (al->e[0].tos) ?
+              "2 (Larger than any link state path)" : "1", VTY_NEWLINE);
+      vty_out (vty, "        TOS: 0%s", VTY_NEWLINE);
+      vty_out (vty, "        Metric: %d%s",
+              GET_METRIC (al->e[0].metric), VTY_NEWLINE);
+      vty_out (vty, "        Forward Address: %s%s",
+              inet_ntoa (al->e[0].fwd_addr), VTY_NEWLINE);
+
+      vty_out (vty, "        External Route Tag: %lu%s%s",
+              (u_long)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+#if 0
+static int
+show_as_external_lsa_stdvty (struct ospf_lsa *lsa)
+{
+  struct as_external_lsa *al = (struct as_external_lsa *) lsa->data;
+
+  /* show_ip_ospf_database_header (vty, lsa); */
+
+  zlog_debug( "  Network Mask: /%d%s",
+            ip_masklen (al->mask), "\n");
+  zlog_debug( "        Metric Type: %s%s",
+            IS_EXTERNAL_METRIC (al->e[0].tos) ?
+            "2 (Larger than any link state path)" : "1", "\n");
+  zlog_debug( "        TOS: 0%s", "\n");
+  zlog_debug( "        Metric: %d%s",
+            GET_METRIC (al->e[0].metric), "\n");
+  zlog_debug( "        Forward Address: %s%s",
+            inet_ntoa (al->e[0].fwd_addr), "\n");
+
+  zlog_debug( "        External Route Tag: %lu%s%s",
+            (u_long)ntohl (al->e[0].route_tag), "\n", "\n");
+
+  return 0;
+}
+#endif
+
+/* Show AS-NSSA-LSA detail information. */
+static int
+show_as_nssa_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      struct as_external_lsa *al = (struct as_external_lsa *) lsa->data;
+
+      show_ip_ospf_database_header (vty, lsa);
+
+      vty_out (vty, "  Network Mask: /%d%s",
+              ip_masklen (al->mask), VTY_NEWLINE);
+      vty_out (vty, "        Metric Type: %s%s",
+              IS_EXTERNAL_METRIC (al->e[0].tos) ?
+              "2 (Larger than any link state path)" : "1", VTY_NEWLINE);
+      vty_out (vty, "        TOS: 0%s", VTY_NEWLINE);
+      vty_out (vty, "        Metric: %d%s",
+              GET_METRIC (al->e[0].metric), VTY_NEWLINE);
+      vty_out (vty, "        NSSA: Forward Address: %s%s",
+              inet_ntoa (al->e[0].fwd_addr), VTY_NEWLINE);
+
+      vty_out (vty, "        External Route Tag: %lu%s%s",
+              (u_long)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+static int
+show_func_dummy (struct vty *vty, struct ospf_lsa *lsa)
+{
+  return 0;
+}
+
+static int
+show_opaque_lsa_detail (struct vty *vty, struct ospf_lsa *lsa)
+{
+  if (lsa != NULL)
+    {
+      show_ip_ospf_database_header (vty, lsa);
+      show_opaque_info_detail (vty, lsa);
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return 0;
+}
+
+int (*show_function[])(struct vty *, struct ospf_lsa *) =
+{
+  NULL,
+  show_router_lsa_detail,
+  show_network_lsa_detail,
+  show_summary_lsa_detail,
+  show_summary_asbr_lsa_detail,
+  show_as_external_lsa_detail,
+  show_func_dummy,
+  show_as_nssa_lsa_detail,  /* almost same as external */
+  NULL,                                /* type-8 */
+  show_opaque_lsa_detail,
+  show_opaque_lsa_detail,
+  show_opaque_lsa_detail,
+};
+
+static void
+show_lsa_prefix_set (struct vty *vty, struct prefix_ls *lp, struct in_addr *id,
+                    struct in_addr *adv_router)
+{
+  memset (lp, 0, sizeof (struct prefix_ls));
+  lp->family = 0;
+  if (id == NULL)
+    lp->prefixlen = 0;
+  else if (adv_router == NULL)
+    {
+      lp->prefixlen = 32;
+      lp->id = *id;
+    }
+  else
+    {
+      lp->prefixlen = 64;
+      lp->id = *id;
+      lp->adv_router = *adv_router;
+    }
+}
+
+static void
+show_lsa_detail_proc (struct vty *vty, struct route_table *rt,
+                     struct in_addr *id, struct in_addr *adv_router)
+{
+  struct prefix_ls lp;
+  struct route_node *rn, *start;
+  struct ospf_lsa *lsa;
+
+  show_lsa_prefix_set (vty, &lp, id, adv_router);
+  start = route_node_get (rt, (struct prefix *) &lp);
+  if (start)
+    {
+      route_lock_node (start);
+      for (rn = start; rn; rn = route_next_until (rn, start))
+       if ((lsa = rn->info))
+         {
+           if (show_function[lsa->data->type] != NULL)
+             show_function[lsa->data->type] (vty, lsa);
+         }
+      route_unlock_node (start);
+    }
+}
+
+/* Show detail LSA information
+   -- if id is NULL then show all LSAs. */
+static void
+show_lsa_detail (struct vty *vty, struct ospf *ospf, int type,
+                struct in_addr *id, struct in_addr *adv_router)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  
+  switch (type)
+    {
+    case OSPF_AS_EXTERNAL_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      vty_out (vty, "                %s %s%s",
+               show_database_desc[type],
+               VTY_NEWLINE, VTY_NEWLINE);
+      show_lsa_detail_proc (vty, AS_LSDB (ospf, type), id, adv_router);
+      break;
+    default:
+      for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+        {
+          vty_out (vty, "%s                %s (Area %s)%s%s",
+                   VTY_NEWLINE, show_database_desc[type],
+                   ospf_area_desc_string (area), VTY_NEWLINE, VTY_NEWLINE);
+          show_lsa_detail_proc (vty, AREA_LSDB (area, type), id, adv_router);
+        }
+      break;
+    }
+}
+
+static void
+show_lsa_detail_adv_router_proc (struct vty *vty, struct route_table *rt,
+                                struct in_addr *adv_router)
+{
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((lsa = rn->info))
+      if (IPV4_ADDR_SAME (adv_router, &lsa->data->adv_router))
+       {
+         if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
+           continue;
+         if (show_function[lsa->data->type] != NULL)
+           show_function[lsa->data->type] (vty, lsa);
+       }
+}
+
+/* Show detail LSA information. */
+static void
+show_lsa_detail_adv_router (struct vty *vty, struct ospf *ospf, int type,
+                           struct in_addr *adv_router)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+
+  switch (type)
+    {
+    case OSPF_AS_EXTERNAL_LSA:
+    case OSPF_OPAQUE_AS_LSA:
+      vty_out (vty, "                %s %s%s",
+               show_database_desc[type],
+               VTY_NEWLINE, VTY_NEWLINE);
+      show_lsa_detail_adv_router_proc (vty, AS_LSDB (ospf, type),
+                                       adv_router);
+      break;
+    default:
+      for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+        {
+          vty_out (vty, "%s                %s (Area %s)%s%s",
+                   VTY_NEWLINE, show_database_desc[type],
+                   ospf_area_desc_string (area), VTY_NEWLINE, VTY_NEWLINE);
+          show_lsa_detail_adv_router_proc (vty, AREA_LSDB (area, type),
+                                           adv_router);
+       }
+      break;
+    }
+}
+
+static void
+show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self)
+{
+  struct ospf_lsa *lsa;
+  struct route_node *rn;
+  struct ospf_area *area;
+  struct listnode *node;
+  int type;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++)
+       {
+         switch (type)
+           {
+           case OSPF_AS_EXTERNAL_LSA:
+            case OSPF_OPAQUE_AS_LSA:
+             continue;
+           default:
+             break;
+           }
+          if (ospf_lsdb_count_self (area->lsdb, type) > 0 ||
+              (!self && ospf_lsdb_count (area->lsdb, type) > 0))
+            {
+              vty_out (vty, "                %s (Area %s)%s%s",
+                       show_database_desc[type],
+                      ospf_area_desc_string (area),
+                       VTY_NEWLINE, VTY_NEWLINE);
+              vty_out (vty, "%s%s", show_database_header[type], VTY_NEWLINE);
+
+             LSDB_LOOP (AREA_LSDB (area, type), rn, lsa)
+               show_lsa_summary (vty, lsa, self);
+
+              vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       }
+    }
+
+  for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++)
+    {
+      switch (type)
+        {
+          case OSPF_AS_EXTERNAL_LSA:
+          case OSPF_OPAQUE_AS_LSA:
+            break;
+          default:
+            continue;
+        }
+      if (ospf_lsdb_count_self (ospf->lsdb, type) ||
+         (!self && ospf_lsdb_count (ospf->lsdb, type)))
+        {
+          vty_out (vty, "                %s%s%s",
+              show_database_desc[type],
+              VTY_NEWLINE, VTY_NEWLINE);
+          vty_out (vty, "%s%s", show_database_header[type],
+              VTY_NEWLINE);
+
+         LSDB_LOOP (AS_LSDB (ospf, type), rn, lsa)
+           show_lsa_summary (vty, lsa, self);
+
+          vty_out (vty, "%s", VTY_NEWLINE);
+        }
+    }
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+show_ip_ospf_database_maxage (struct vty *vty, struct ospf *ospf)
+{
+  struct route_node *rn;
+
+  vty_out (vty, "%s                MaxAge Link States:%s%s",
+           VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn))
+    {
+      struct ospf_lsa *lsa;
+
+      if ((lsa = rn->info) != NULL)
+       {
+         vty_out (vty, "Link type: %d%s", lsa->data->type, VTY_NEWLINE);
+         vty_out (vty, "Link State ID: %s%s",
+                  inet_ntoa (lsa->data->id), VTY_NEWLINE);
+         vty_out (vty, "Advertising Router: %s%s",
+                  inet_ntoa (lsa->data->adv_router), VTY_NEWLINE);
+         vty_out (vty, "LSA lock count: %d%s", lsa->lock, VTY_NEWLINE);
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+}
+
+#define OSPF_LSA_TYPE_NSSA_DESC      "NSSA external link state\n"
+#define OSPF_LSA_TYPE_NSSA_CMD_STR   "|nssa-external"
+
+#define OSPF_LSA_TYPE_OPAQUE_LINK_DESC "Link local Opaque-LSA\n"
+#define OSPF_LSA_TYPE_OPAQUE_AREA_DESC "Link area Opaque-LSA\n"
+#define OSPF_LSA_TYPE_OPAQUE_AS_DESC   "Link AS Opaque-LSA\n"
+#define OSPF_LSA_TYPE_OPAQUE_CMD_STR   "|opaque-link|opaque-area|opaque-as"
+
+#define OSPF_LSA_TYPES_CMD_STR                                                \
+    "asbr-summary|external|network|router|summary"                            \
+    OSPF_LSA_TYPE_NSSA_CMD_STR                                                \
+    OSPF_LSA_TYPE_OPAQUE_CMD_STR
+
+#define OSPF_LSA_TYPES_DESC                                                   \
+   "ASBR summary link states\n"                                               \
+   "External link states\n"                                                   \
+   "Network link states\n"                                                    \
+   "Router link states\n"                                                     \
+   "Network summary link states\n"                                            \
+   OSPF_LSA_TYPE_NSSA_DESC                                                    \
+   OSPF_LSA_TYPE_OPAQUE_LINK_DESC                                             \
+   OSPF_LSA_TYPE_OPAQUE_AREA_DESC                                             \
+   OSPF_LSA_TYPE_OPAQUE_AS_DESC     
+
+DEFUN (show_ip_ospf_database,
+       show_ip_ospf_database_cmd,
+       "show ip ospf database",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n")
+{
+  struct ospf *ospf;
+  int type, ret;
+  struct in_addr id, adv_router;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  vty_out (vty, "%s       OSPF Router with ID (%s)%s%s", VTY_NEWLINE,
+           inet_ntoa (ospf->router_id), VTY_NEWLINE, VTY_NEWLINE);
+
+  /* Show all LSA. */
+  if (argc == 0)
+    {
+      show_ip_ospf_database_summary (vty, ospf, 0);
+      return CMD_SUCCESS;
+    }
+
+  /* Set database type to show. */
+  if (strncmp (argv[0], "r", 1) == 0)
+    type = OSPF_ROUTER_LSA;
+  else if (strncmp (argv[0], "ne", 2) == 0)
+    type = OSPF_NETWORK_LSA;
+  else if (strncmp (argv[0], "ns", 2) == 0)
+    type = OSPF_AS_NSSA_LSA;
+  else if (strncmp (argv[0], "su", 2) == 0)
+    type = OSPF_SUMMARY_LSA;
+  else if (strncmp (argv[0], "a", 1) == 0)
+    type = OSPF_ASBR_SUMMARY_LSA;
+  else if (strncmp (argv[0], "e", 1) == 0)
+    type = OSPF_AS_EXTERNAL_LSA;
+  else if (strncmp (argv[0], "se", 2) == 0)
+    {
+      show_ip_ospf_database_summary (vty, ospf, 1);
+      return CMD_SUCCESS;
+    }
+  else if (strncmp (argv[0], "m", 1) == 0)
+    {
+      show_ip_ospf_database_maxage (vty, ospf);
+      return CMD_SUCCESS;
+    }
+  else if (strncmp (argv[0], "opaque-l", 8) == 0)
+    type = OSPF_OPAQUE_LINK_LSA;
+  else if (strncmp (argv[0], "opaque-ar", 9) == 0)
+    type = OSPF_OPAQUE_AREA_LSA;
+  else if (strncmp (argv[0], "opaque-as", 9) == 0)
+    type = OSPF_OPAQUE_AS_LSA;
+  else
+    return CMD_WARNING;
+
+  /* `show ip ospf database LSA'. */
+  if (argc == 1)
+    show_lsa_detail (vty, ospf, type, NULL, NULL);
+  else if (argc >= 2)
+    {
+      ret = inet_aton (argv[1], &id);
+      if (!ret)
+       return CMD_WARNING;
+      
+      /* `show ip ospf database LSA ID'. */
+      if (argc == 2)
+       show_lsa_detail (vty, ospf, type, &id, NULL);
+      /* `show ip ospf database LSA ID adv-router ADV_ROUTER'. */
+      else if (argc == 3)
+       {
+         if (strncmp (argv[2], "s", 1) == 0)
+           adv_router = ospf->router_id;
+         else
+           {
+             ret = inet_aton (argv[2], &adv_router);
+             if (!ret)
+               return CMD_WARNING;
+           }
+         show_lsa_detail (vty, ospf, type, &id, &adv_router);
+       }
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_ospf_database,
+       show_ip_ospf_database_type_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR "|max-age|self-originate)",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "LSAs in MaxAge list\n"
+       "Self-originated link states\n")
+
+ALIAS (show_ip_ospf_database,
+       show_ip_ospf_database_type_id_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "Link State ID (as an IP address)\n")
+
+ALIAS (show_ip_ospf_database,
+       show_ip_ospf_database_type_id_adv_router_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D adv-router A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "Link State ID (as an IP address)\n"
+       "Advertising Router link states\n"
+       "Advertising Router (as an IP address)\n")
+
+ALIAS (show_ip_ospf_database,
+       show_ip_ospf_database_type_id_self_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D (self-originate|)",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "Link State ID (as an IP address)\n"
+       "Self-originated link states\n"
+       "\n")
+
+DEFUN (show_ip_ospf_database_type_adv_router,
+       show_ip_ospf_database_type_adv_router_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") adv-router A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "Advertising Router link states\n"
+       "Advertising Router (as an IP address)\n")
+{
+  struct ospf *ospf;
+  int type, ret;
+  struct in_addr adv_router;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  vty_out (vty, "%s       OSPF Router with ID (%s)%s%s", VTY_NEWLINE,
+           inet_ntoa (ospf->router_id), VTY_NEWLINE, VTY_NEWLINE);
+
+  if (argc != 2)
+    return CMD_WARNING;
+
+  /* Set database type to show. */
+  if (strncmp (argv[0], "r", 1) == 0)
+    type = OSPF_ROUTER_LSA;
+  else if (strncmp (argv[0], "ne", 2) == 0)
+    type = OSPF_NETWORK_LSA;
+  else if (strncmp (argv[0], "ns", 2) == 0)
+    type = OSPF_AS_NSSA_LSA;
+  else if (strncmp (argv[0], "s", 1) == 0)
+    type = OSPF_SUMMARY_LSA;
+  else if (strncmp (argv[0], "a", 1) == 0)
+    type = OSPF_ASBR_SUMMARY_LSA;
+  else if (strncmp (argv[0], "e", 1) == 0)
+    type = OSPF_AS_EXTERNAL_LSA;
+  else if (strncmp (argv[0], "opaque-l", 8) == 0)
+    type = OSPF_OPAQUE_LINK_LSA;
+  else if (strncmp (argv[0], "opaque-ar", 9) == 0)
+    type = OSPF_OPAQUE_AREA_LSA;
+  else if (strncmp (argv[0], "opaque-as", 9) == 0)
+    type = OSPF_OPAQUE_AS_LSA;
+  else
+    return CMD_WARNING;
+
+  /* `show ip ospf database LSA adv-router ADV_ROUTER'. */
+  if (strncmp (argv[1], "s", 1) == 0)
+    adv_router = ospf->router_id;
+  else
+    {
+      ret = inet_aton (argv[1], &adv_router);
+      if (!ret)
+       return CMD_WARNING;
+    }
+
+  show_lsa_detail_adv_router (vty, ospf, type, &adv_router);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_ospf_database_type_adv_router,
+       show_ip_ospf_database_type_self_cmd,
+       "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") (self-originate|)",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "Database summary\n"
+       OSPF_LSA_TYPES_DESC
+       "Self-originated link states\n")
+
+
+DEFUN (ip_ospf_authentication_args,
+       ip_ospf_authentication_args_addr_cmd,
+       "ip ospf authentication (null|message-digest) A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n"
+       "Use null authentication\n"
+       "Use message-digest authentication\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  /* Handle null authentication */
+  if ( argv[0][0] == 'n' )
+    {
+      SET_IF_PARAM (params, auth_type);
+      params->auth_type = OSPF_AUTH_NULL;
+      return CMD_SUCCESS;
+    }
+
+  /* Handle message-digest authentication */
+  if ( argv[0][0] == 'm' )
+    {
+      SET_IF_PARAM (params, auth_type);
+      params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+      return CMD_SUCCESS;
+    }
+
+  vty_out (vty, "You shouldn't get here!%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+ALIAS (ip_ospf_authentication_args,
+       ip_ospf_authentication_args_cmd,
+       "ip ospf authentication (null|message-digest)",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n"
+       "Use null authentication\n"
+       "Use message-digest authentication\n")
+
+DEFUN (ip_ospf_authentication,
+       ip_ospf_authentication_addr_cmd,
+       "ip ospf authentication A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  SET_IF_PARAM (params, auth_type);
+  params->auth_type = OSPF_AUTH_SIMPLE;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_authentication,
+       ip_ospf_authentication_cmd,
+       "ip ospf authentication",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n")
+
+DEFUN (no_ip_ospf_authentication,
+       no_ip_ospf_authentication_addr_cmd,
+       "no ip ospf authentication A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  params->auth_type = OSPF_AUTH_NOTSET;
+  UNSET_IF_PARAM (params, auth_type);
+  
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_authentication,
+       no_ip_ospf_authentication_cmd,
+       "no ip ospf authentication",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable authentication on this interface\n")
+
+DEFUN (ip_ospf_authentication_key,
+       ip_ospf_authentication_key_addr_cmd,
+       "ip ospf authentication-key AUTH_KEY A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Authentication password (key)\n"
+       "The OSPF password (key)\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+
+  memset (params->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE + 1);
+  strncpy ((char *) params->auth_simple, argv[0], OSPF_AUTH_SIMPLE_SIZE);
+  SET_IF_PARAM (params, auth_simple);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_authentication_key,
+       ip_ospf_authentication_key_cmd,
+       "ip ospf authentication-key AUTH_KEY",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Authentication password (key)\n"
+       "The OSPF password (key)")
+
+ALIAS (ip_ospf_authentication_key,
+       ospf_authentication_key_cmd,
+       "ospf authentication-key AUTH_KEY",
+       "OSPF interface commands\n"
+       "Authentication password (key)\n"
+       "The OSPF password (key)")
+
+DEFUN (no_ip_ospf_authentication_key,
+       no_ip_ospf_authentication_key_addr_cmd,
+       "no ip ospf authentication-key A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Authentication password (key)\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  memset (params->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE);
+  UNSET_IF_PARAM (params, auth_simple);
+  
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_authentication_key,
+       no_ip_ospf_authentication_key_cmd,
+       "no ip ospf authentication-key",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Authentication password (key)\n")
+
+ALIAS (no_ip_ospf_authentication_key,
+       no_ospf_authentication_key_cmd,
+       "no ospf authentication-key",
+       NO_STR
+       "OSPF interface commands\n"
+       "Authentication password (key)\n")
+
+DEFUN (ip_ospf_message_digest_key,
+       ip_ospf_message_digest_key_addr_cmd,
+       "ip ospf message-digest-key <1-255> md5 KEY A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n"
+       "Use MD5 algorithm\n"
+       "The OSPF password (key)"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct crypt_key *ck;
+  u_char key_id;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 3)
+    {
+      ret = inet_aton(argv[2], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  key_id = strtol (argv[0], NULL, 10);
+  if (ospf_crypt_key_lookup (params->auth_crypt, key_id) != NULL)
+    {
+      vty_out (vty, "OSPF: Key %d already exists%s", key_id, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ck = ospf_crypt_key_new ();
+  ck->key_id = (u_char) key_id;
+  memset (ck->auth_key, 0, OSPF_AUTH_MD5_SIZE+1);
+  strncpy ((char *) ck->auth_key, argv[1], OSPF_AUTH_MD5_SIZE);
+
+  ospf_crypt_key_add (params->auth_crypt, ck);
+  SET_IF_PARAM (params, auth_crypt);
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_message_digest_key,
+       ip_ospf_message_digest_key_cmd,
+       "ip ospf message-digest-key <1-255> md5 KEY",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n"
+       "Use MD5 algorithm\n"
+       "The OSPF password (key)")
+
+ALIAS (ip_ospf_message_digest_key,
+       ospf_message_digest_key_cmd,
+       "ospf message-digest-key <1-255> md5 KEY",
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n"
+       "Use MD5 algorithm\n"
+       "The OSPF password (key)")
+
+DEFUN (no_ip_ospf_message_digest_key,
+       no_ip_ospf_message_digest_key_addr_cmd,
+       "no ip ospf message-digest-key <1-255> A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n"
+       "Address of interface")
+{
+  struct interface *ifp;
+  struct crypt_key *ck;
+  int key_id;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  key_id = strtol (argv[0], NULL, 10);
+  ck = ospf_crypt_key_lookup (params->auth_crypt, key_id);
+  if (ck == NULL)
+    {
+      vty_out (vty, "OSPF: Key %d does not exist%s", key_id, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ospf_crypt_key_delete (params->auth_crypt, key_id);
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_message_digest_key,
+       no_ip_ospf_message_digest_key_cmd,
+       "no ip ospf message-digest-key <1-255>",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n")
+     
+ALIAS (no_ip_ospf_message_digest_key,
+       no_ospf_message_digest_key_cmd,
+       "no ospf message-digest-key <1-255>",
+       NO_STR
+       "OSPF interface commands\n"
+       "Message digest authentication password (key)\n"
+       "Key ID\n")
+
+DEFUN (ip_ospf_cost,
+       ip_ospf_cost_u32_inet4_cmd,
+       "ip ospf cost <1-65535> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  u_int32_t cost;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+      
+  params = IF_DEF_PARAMS (ifp);
+
+  cost = strtol (argv[0], NULL, 10);
+
+  /* cost range is <1-65535>. */
+  if (cost < 1 || cost > 65535)
+    {
+      vty_out (vty, "Interface output cost is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  SET_IF_PARAM (params, output_cost_cmd);
+  params->output_cost_cmd = cost;
+
+  ospf_if_recalculate_output_cost (ifp);
+    
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_cost,
+       ip_ospf_cost_u32_cmd,
+       "ip ospf cost <1-65535>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost")
+
+ALIAS (ip_ospf_cost,
+       ospf_cost_u32_cmd,
+       "ospf cost <1-65535>",
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost")
+
+ALIAS (ip_ospf_cost,
+       ospf_cost_u32_inet4_cmd,
+       "ospf cost <1-65535> A.B.C.D",
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost\n"
+       "Address of interface")
+
+DEFUN (no_ip_ospf_cost,
+       no_ip_ospf_cost_inet4_cmd,
+       "no ip ospf cost A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, output_cost_cmd);
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  ospf_if_recalculate_output_cost (ifp);
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_cost,
+       no_ip_ospf_cost_cmd,
+       "no ip ospf cost",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n")
+
+ALIAS (no_ip_ospf_cost,
+       no_ospf_cost_cmd,
+       "no ospf cost",
+       NO_STR
+       "OSPF interface commands\n"
+       "Interface cost\n")
+
+ALIAS (no_ip_ospf_cost,
+       no_ospf_cost_inet4_cmd,
+       "no ospf cost A.B.C.D",
+       NO_STR
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Address of interface")
+
+DEFUN (no_ip_ospf_cost2,
+       no_ip_ospf_cost_u32_cmd,
+       "no ip ospf cost <1-65535>",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  u_int32_t cost;
+  int ret;
+  struct ospf_if_params *params;
+
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  /* According to the semantics we are mimicking "no ip ospf cost N" is
+   * always treated as "no ip ospf cost" regardless of the actual value
+   * of N already configured for the interface. Thus the first argument
+   * is always checked to be a number, but is ignored after that.
+   */
+  cost = strtol (argv[0], NULL, 10);
+  if (cost < 1 || cost > 65535)
+    {
+      vty_out (vty, "Interface output cost is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, output_cost_cmd);
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  ospf_if_recalculate_output_cost (ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_cost2,
+       no_ospf_cost_u32_cmd,
+       "no ospf cost <1-65535>",
+       NO_STR
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost")
+
+ALIAS (no_ip_ospf_cost2,
+       no_ip_ospf_cost_u32_inet4_cmd,
+       "no ip ospf cost <1-65535> A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost\n"
+       "Address of interface")
+
+ALIAS (no_ip_ospf_cost2,
+       no_ospf_cost_u32_inet4_cmd,
+       "no ospf cost <1-65535> A.B.C.D",
+       NO_STR
+       "OSPF interface commands\n"
+       "Interface cost\n"
+       "Cost\n"
+       "Address of interface")
+
+static void
+ospf_nbr_timer_update (struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct ospf_neighbor *nbr;
+
+  for (rn = route_top (oi->nbrs); rn; rn = route_next (rn))
+    if ((nbr = rn->info))
+      {
+       nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait);
+       nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval);
+       nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval);
+       nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval);
+      }
+}
+
+static int
+ospf_vty_dead_interval_set (struct vty *vty, const char *interval_str,
+                            const char *nbr_str,
+                            const char *fast_hello_str)
+{
+  struct interface *ifp = vty->index;
+  u_int32_t seconds;
+  u_char hellomult;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  struct ospf_interface *oi;
+  struct route_node *rn;
+      
+  params = IF_DEF_PARAMS (ifp);
+  
+  if (nbr_str)
+    {
+      ret = inet_aton(nbr_str, &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  if (interval_str)
+    {
+      VTY_GET_INTEGER_RANGE ("Router Dead Interval", seconds, interval_str,
+                             1, 65535);
+                             
+      /* reset fast_hello too, just to be sure */
+      UNSET_IF_PARAM (params, fast_hello);
+      params->fast_hello = OSPF_FAST_HELLO_DEFAULT;
+    }
+  else if (fast_hello_str)
+    {
+      VTY_GET_INTEGER_RANGE ("Hello Multiplier", hellomult, fast_hello_str,
+                             1, 10);
+      /* 1s dead-interval with sub-second hellos desired */
+      seconds = OSPF_ROUTER_DEAD_INTERVAL_MINIMAL;
+      SET_IF_PARAM (params, fast_hello);
+      params->fast_hello = hellomult;
+    }
+  else
+    {
+      vty_out (vty, "Please specify dead-interval or hello-multiplier%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+    
+  SET_IF_PARAM (params, v_wait);
+  params->v_wait = seconds;
+  
+  /* Update timer values in neighbor structure. */
+  if (nbr_str)
+    {
+      struct ospf *ospf;
+      if ((ospf = ospf_lookup()))
+       {
+         oi = ospf_if_lookup_by_local_addr (ospf, ifp, addr);
+         if (oi)
+           ospf_nbr_timer_update (oi);
+       }
+    }
+  else
+    {
+      for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+       if ((oi = rn->info))
+         ospf_nbr_timer_update (oi);
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ip_ospf_dead_interval,
+       ip_ospf_dead_interval_addr_cmd,
+       "ip ospf dead-interval <1-65535> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Seconds\n"
+       "Address of interface\n")
+{
+  if (argc == 2)
+    return ospf_vty_dead_interval_set (vty, argv[0], argv[1], NULL);
+  else
+    return ospf_vty_dead_interval_set (vty, argv[0], NULL, NULL);
+}
+
+ALIAS (ip_ospf_dead_interval,
+       ip_ospf_dead_interval_cmd,
+       "ip ospf dead-interval <1-65535>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Seconds\n")
+
+ALIAS (ip_ospf_dead_interval,
+       ospf_dead_interval_cmd,
+       "ospf dead-interval <1-65535>",
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Seconds\n")
+
+DEFUN (ip_ospf_dead_interval_minimal,
+       ip_ospf_dead_interval_minimal_addr_cmd,
+       "ip ospf dead-interval minimal hello-multiplier <1-10> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Minimal 1s dead-interval with fast sub-second hellos\n"
+       "Hello multiplier factor\n"
+       "Number of Hellos to send each second\n"
+       "Address of interface\n")
+{
+  if (argc == 2)
+    return ospf_vty_dead_interval_set (vty, NULL, argv[1], argv[0]);
+  else
+    return ospf_vty_dead_interval_set (vty, NULL, NULL, argv[0]);
+}
+
+ALIAS (ip_ospf_dead_interval_minimal,
+       ip_ospf_dead_interval_minimal_cmd,
+       "ip ospf dead-interval minimal hello-multiplier <1-10>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Minimal 1s dead-interval with fast sub-second hellos\n"
+       "Hello multiplier factor\n"
+       "Number of Hellos to send each second\n")
+
+DEFUN (no_ip_ospf_dead_interval,
+       no_ip_ospf_dead_interval_addr_cmd,
+       "no ip ospf dead-interval <1-65535> A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Seconds\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  struct ospf_interface *oi;
+  struct route_node *rn;
+
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, v_wait);
+  params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
+  
+  UNSET_IF_PARAM (params, fast_hello);
+  params->fast_hello = OSPF_FAST_HELLO_DEFAULT;
+  
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  /* Update timer values in neighbor structure. */
+  if (argc == 1)
+    {
+      struct ospf *ospf;
+      
+      if ((ospf = ospf_lookup()))
+       {
+         oi = ospf_if_lookup_by_local_addr (ospf, ifp, addr);
+         if (oi)
+           ospf_nbr_timer_update (oi);
+       }
+    }
+  else
+    {
+      for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+       if ((oi = rn->info))
+         ospf_nbr_timer_update (oi);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_dead_interval,
+       no_ip_ospf_dead_interval_seconds_cmd,
+       "no ip ospf dead-interval <1-65535>",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n"
+       "Seconds\n")
+
+ALIAS (no_ip_ospf_dead_interval,
+       no_ip_ospf_dead_interval_cmd,
+       "no ip ospf dead-interval",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n")
+
+ALIAS (no_ip_ospf_dead_interval,
+       no_ospf_dead_interval_cmd,
+       "no ospf dead-interval",
+       NO_STR
+       "OSPF interface commands\n"
+       "Interval after which a neighbor is declared dead\n")
+
+DEFUN (ip_ospf_hello_interval,
+       ip_ospf_hello_interval_addr_cmd,
+       "ip ospf hello-interval <1-65535> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n"
+       "Seconds\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  u_int32_t seconds;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+      
+  params = IF_DEF_PARAMS (ifp);
+
+  seconds = strtol (argv[0], NULL, 10);
+  
+  /* HelloInterval range is <1-65535>. */
+  if (seconds < 1 || seconds > 65535)
+    {
+      vty_out (vty, "Hello Interval is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  SET_IF_PARAM (params, v_hello);
+  params->v_hello = seconds;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_hello_interval,
+       ip_ospf_hello_interval_cmd,
+       "ip ospf hello-interval <1-65535>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n"
+       "Seconds\n")
+
+ALIAS (ip_ospf_hello_interval,
+       ospf_hello_interval_cmd,
+       "ospf hello-interval <1-65535>",
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n"
+       "Seconds\n")
+
+DEFUN (no_ip_ospf_hello_interval,
+       no_ip_ospf_hello_interval_addr_cmd,
+       "no ip ospf hello-interval <1-65535> A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n"
+       "Seconds\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, v_hello);
+  params->v_hello = OSPF_HELLO_INTERVAL_DEFAULT;
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_hello_interval,
+       no_ip_ospf_hello_interval_seconds_cmd,
+       "no ip ospf hello-interval <1-65535>",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n"
+       "Seconds\n")
+
+ALIAS (no_ip_ospf_hello_interval,
+       no_ip_ospf_hello_interval_cmd,
+       "no ip ospf hello-interval",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n")
+
+ALIAS (no_ip_ospf_hello_interval,
+       no_ospf_hello_interval_cmd,
+       "no ospf hello-interval",
+       NO_STR
+       "OSPF interface commands\n"
+       "Time between HELLO packets\n")
+
+DEFUN (ip_ospf_network,
+       ip_ospf_network_cmd,
+       "ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Network type\n"
+       "Specify OSPF broadcast multi-access network\n"
+       "Specify OSPF NBMA network\n"
+       "Specify OSPF point-to-multipoint network\n"
+       "Specify OSPF point-to-point network\n")
+{
+  struct interface *ifp = vty->index;
+  int old_type = IF_DEF_PARAMS (ifp)->type;
+
+  if (old_type == OSPF_IFTYPE_LOOPBACK)
+    {
+      vty_out (vty, "This is a loopback interface. Can't set network type.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (strncmp (argv[0], "b", 1) == 0)
+    IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_BROADCAST;
+  else if (strncmp (argv[0], "n", 1) == 0)
+    IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_NBMA;
+  else if (strncmp (argv[0], "point-to-m", 10) == 0)
+    IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
+  else if (strncmp (argv[0], "point-to-p", 10) == 0)
+    IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_POINTOPOINT;
+
+  if (IF_DEF_PARAMS (ifp)->type == old_type)
+    return CMD_SUCCESS;
+
+  SET_IF_PARAM (IF_DEF_PARAMS (ifp), type);
+  ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type);
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_network,
+       ospf_network_cmd,
+       "ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)",
+       "OSPF interface commands\n"
+       "Network type\n"
+       "Specify OSPF broadcast multi-access network\n"
+       "Specify OSPF NBMA network\n"
+       "Specify OSPF point-to-multipoint network\n"
+       "Specify OSPF point-to-point network\n")
+
+DEFUN (no_ip_ospf_network,
+       no_ip_ospf_network_cmd,
+       "no ip ospf network",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Network type\n")
+{
+  struct interface *ifp = vty->index;
+  int old_type = IF_DEF_PARAMS (ifp)->type;
+
+  IF_DEF_PARAMS (ifp)->type = ospf_default_iftype(ifp);
+
+  if (IF_DEF_PARAMS (ifp)->type == old_type)
+    return CMD_SUCCESS;
+  
+  ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type);
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_network,
+       no_ospf_network_cmd,
+       "no ospf network",
+       NO_STR
+       "OSPF interface commands\n"
+       "Network type\n")
+
+DEFUN (ip_ospf_priority,
+       ip_ospf_priority_addr_cmd,
+       "ip ospf priority <0-255> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Router priority\n"
+       "Priority\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  long priority;
+  struct route_node *rn;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+      
+  params = IF_DEF_PARAMS (ifp);
+
+  priority = strtol (argv[0], NULL, 10);
+  
+  /* Router Priority range is <0-255>. */
+  if (priority < 0 || priority > 255)
+    {
+      vty_out (vty, "Router Priority is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  SET_IF_PARAM (params, priority);
+  params->priority = priority;
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+      
+      if (!oi)
+       continue;
+      
+
+      if (PRIORITY (oi) != OSPF_IF_PARAM (oi, priority))
+       {
+         PRIORITY (oi) = OSPF_IF_PARAM (oi, priority);
+         OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+       }
+    }
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_priority,
+       ip_ospf_priority_cmd,
+       "ip ospf priority <0-255>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Router priority\n"
+       "Priority\n")
+
+ALIAS (ip_ospf_priority,
+       ospf_priority_cmd,
+       "ospf priority <0-255>",
+       "OSPF interface commands\n"
+       "Router priority\n"
+       "Priority\n")
+
+DEFUN (no_ip_ospf_priority,
+       no_ip_ospf_priority_addr_cmd,
+       "no ip ospf priority A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Router priority\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct route_node *rn;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, priority);
+  params->priority = OSPF_ROUTER_PRIORITY_DEFAULT;
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      struct ospf_interface *oi = rn->info;
+      
+      if (!oi)
+       continue;
+      
+      
+      if (PRIORITY (oi) != OSPF_IF_PARAM (oi, priority))
+       {
+         PRIORITY (oi) = OSPF_IF_PARAM (oi, priority);
+         OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange);
+       }
+    }
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_priority,
+       no_ip_ospf_priority_cmd,
+       "no ip ospf priority",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Router priority\n")
+
+ALIAS (no_ip_ospf_priority,
+       no_ospf_priority_cmd,
+       "no ospf priority",
+       NO_STR
+       "OSPF interface commands\n"
+       "Router priority\n")
+
+DEFUN (ip_ospf_retransmit_interval,
+       ip_ospf_retransmit_interval_addr_cmd,
+       "ip ospf retransmit-interval <3-65535> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n"
+       "Seconds\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  u_int32_t seconds;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+      
+  params = IF_DEF_PARAMS (ifp);
+  seconds = strtol (argv[0], NULL, 10);
+
+  /* Retransmit Interval range is <3-65535>. */
+  if (seconds < 3 || seconds > 65535)
+    {
+      vty_out (vty, "Retransmit Interval is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  SET_IF_PARAM (params, retransmit_interval);
+  params->retransmit_interval = seconds;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_retransmit_interval,
+       ip_ospf_retransmit_interval_cmd,
+       "ip ospf retransmit-interval <3-65535>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n"
+       "Seconds\n")
+
+ALIAS (ip_ospf_retransmit_interval,
+       ospf_retransmit_interval_cmd,
+       "ospf retransmit-interval <3-65535>",
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n"
+       "Seconds\n")
+
+DEFUN (no_ip_ospf_retransmit_interval,
+       no_ip_ospf_retransmit_interval_addr_cmd,
+       "no ip ospf retransmit-interval A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, retransmit_interval);
+  params->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_retransmit_interval,
+       no_ip_ospf_retransmit_interval_cmd,
+       "no ip ospf retransmit-interval",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n")
+
+ALIAS (no_ip_ospf_retransmit_interval,
+       no_ospf_retransmit_interval_cmd,
+       "no ospf retransmit-interval",
+       NO_STR
+       "OSPF interface commands\n"
+       "Time between retransmitting lost link state advertisements\n")
+
+DEFUN (ip_ospf_transmit_delay,
+       ip_ospf_transmit_delay_addr_cmd,
+       "ip ospf transmit-delay <1-65535> A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Link state transmit delay\n"
+       "Seconds\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  u_int32_t seconds;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+      
+  params = IF_DEF_PARAMS (ifp);
+  seconds = strtol (argv[0], NULL, 10);
+
+  /* Transmit Delay range is <1-65535>. */
+  if (seconds < 1 || seconds > 65535)
+    {
+      vty_out (vty, "Transmit Delay is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 2)
+    {
+      ret = inet_aton(argv[1], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  SET_IF_PARAM (params, transmit_delay); 
+  params->transmit_delay = seconds;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_transmit_delay,
+       ip_ospf_transmit_delay_cmd,
+       "ip ospf transmit-delay <1-65535>",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Link state transmit delay\n"
+       "Seconds\n")
+
+ALIAS (ip_ospf_transmit_delay,
+       ospf_transmit_delay_cmd,
+       "ospf transmit-delay <1-65535>",
+       "OSPF interface commands\n"
+       "Link state transmit delay\n"
+       "Seconds\n")
+
+DEFUN (no_ip_ospf_transmit_delay,
+       no_ip_ospf_transmit_delay_addr_cmd,
+       "no ip ospf transmit-delay A.B.C.D",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Link state transmit delay\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+  struct ospf_if_params *params;
+  
+  ifp = vty->index;
+  params = IF_DEF_PARAMS (ifp);
+
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+       {
+         vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      params = ospf_lookup_if_params (ifp, addr);
+      if (params == NULL)
+       return CMD_SUCCESS;
+    }
+
+  UNSET_IF_PARAM (params, transmit_delay);
+  params->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT;
+
+  if (params != IF_DEF_PARAMS (ifp))
+    {
+      ospf_free_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_transmit_delay,
+       no_ip_ospf_transmit_delay_cmd,
+       "no ip ospf transmit-delay",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Link state transmit delay\n")
+
+ALIAS (no_ip_ospf_transmit_delay,
+       no_ospf_transmit_delay_cmd,
+       "no ospf transmit-delay",
+       NO_STR
+       "OSPF interface commands\n"
+       "Link state transmit delay\n")
+
+DEFUN (ip_ospf_area,
+       ip_ospf_area_cmd,
+       "ip ospf area (A.B.C.D|<0-4294967295>) [A.B.C.D]",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Enable OSPF on this interface\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Address of interface\n")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr area_id;
+  struct in_addr addr;
+  int format;
+  struct ospf_if_params *params;
+
+  VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]);
+
+  OSPF_VTY_GET_IF_PARAMS(ifp, params, 1, addr, VTY_SET);
+  
+  if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
+    {
+      vty_out (vty, "There is already an interface area statement.%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (memcmp (ifp->name, "VLINK", 5) == 0)
+    {
+      vty_out (vty, "Cannot enable OSPF on a virtual link.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  SET_IF_PARAM (params, if_area);
+  params->if_area = area_id;
+  ospf_interface_area_set (ifp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ospf_area,
+       no_ip_ospf_area_cmd,
+       "no ip ospf area [A.B.C.D]",
+       NO_STR
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Disable OSPF on this interface\n"
+       "Address of interface\n")
+{
+  struct interface *ifp = vty->index;
+  struct ospf_if_params *params;
+  struct in_addr addr;
+
+  OSPF_VTY_GET_IF_PARAMS(ifp, params, 0, addr, VTY_UNSET);
+  
+  if (!OSPF_IF_PARAM_CONFIGURED(params, if_area))
+    return CMD_SUCCESS;
+  
+  OSPF_VTY_PARAM_UNSET(params, if_area, ifp, addr);
+  
+  ospf_interface_area_unset (ifp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_redistribute_source,
+       ospf_redistribute_source_cmd,
+       "redistribute " QUAGGA_REDIST_STR_OSPFD
+         " {metric <0-16777214>|metric-type (1|2)|route-map WORD}",
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_OSPFD
+       "Metric for redistributed routes\n"
+       "OSPF default metric\n"
+       "OSPF exterior metric type for redistributed routes\n"
+       "Set OSPF External Type 1 metrics\n"
+       "Set OSPF External Type 2 metrics\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  struct ospf *ospf = vty->index;
+  int source;
+  int type = -1;
+  int metric = -1;
+
+  if (argc < 4)
+    return CMD_WARNING; /* should not happen */
+
+  /* Get distribute source. */
+  source = proto_redistnum(AFI_IP, argv[0]);
+  if (source < 0 || source == ZEBRA_ROUTE_OSPF)
+    return CMD_WARNING;
+
+  /* Get metric value. */
+  if (argv[1] != NULL)
+    if (!str2metric (argv[1], &metric))
+      return CMD_WARNING;
+
+  /* Get metric type. */
+  if (argv[2] != NULL)
+    if (!str2metric_type (argv[2], &type))
+      return CMD_WARNING;
+
+  if (argv[3] != NULL)
+    ospf_routemap_set (ospf, source, argv[3]);
+  else
+    ospf_routemap_unset (ospf, source);
+
+  return ospf_redistribute_set (ospf, source, type, metric);
+}
+
+DEFUN (no_ospf_redistribute_source,
+       no_ospf_redistribute_source_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_OSPFD,
+       NO_STR
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_OSPFD)
+{
+  struct ospf *ospf = vty->index;
+  int source;
+
+  source = proto_redistnum(AFI_IP, argv[0]);
+  if (source < 0 || source == ZEBRA_ROUTE_OSPF)
+    return CMD_WARNING;
+
+  ospf_routemap_unset (ospf, source);
+  return ospf_redistribute_unset (ospf, source);
+}
+
+DEFUN (ospf_distribute_list_out,
+       ospf_distribute_list_out_cmd,
+       "distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD,
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       OUT_STR
+       QUAGGA_REDIST_HELP_STR_OSPFD)
+{
+  struct ospf *ospf = vty->index;
+  int source;
+
+  /* Get distribute source. */
+  source = proto_redistnum(AFI_IP, argv[1]);
+  if (source < 0 || source == ZEBRA_ROUTE_OSPF)
+    return CMD_WARNING;
+
+  return ospf_distribute_list_out_set (ospf, source, argv[0]);
+}
+
+DEFUN (no_ospf_distribute_list_out,
+       no_ospf_distribute_list_out_cmd,
+       "no distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD,
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       OUT_STR
+       QUAGGA_REDIST_HELP_STR_OSPFD)
+{
+  struct ospf *ospf = vty->index;
+  int source;
+
+  source = proto_redistnum(AFI_IP, argv[1]);
+  if (source < 0 || source == ZEBRA_ROUTE_OSPF)
+    return CMD_WARNING;
+
+  return ospf_distribute_list_out_unset (ospf, source, argv[0]);
+}
+
+/* Default information originate. */
+DEFUN (ospf_default_information_originate,
+       ospf_default_information_originate_cmd,
+       "default-information originate "
+         "{always|metric <0-16777214>|metric-type (1|2)|route-map WORD}",
+       "Control distribution of default information\n"
+       "Distribute a default route\n"
+       "Always advertise default route\n"
+       "OSPF default metric\n"
+       "OSPF metric\n"
+       "OSPF metric type for default routes\n"
+       "Set OSPF External Type 1 metrics\n"
+       "Set OSPF External Type 2 metrics\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  struct ospf *ospf = vty->index;
+  int default_originate = DEFAULT_ORIGINATE_ZEBRA;
+  int type = -1;
+  int metric = -1;
+
+  if (argc < 4)
+    return CMD_WARNING; /* this should not happen */
+
+  /* Check whether "always" was specified */
+  if (argv[0] != NULL)
+    default_originate = DEFAULT_ORIGINATE_ALWAYS;
+
+  /* Get metric value. */
+  if (argv[1] != NULL)
+    if (!str2metric (argv[1], &metric))
+      return CMD_WARNING;
+
+  /* Get metric type. */
+  if (argv[2] != NULL)
+    if (!str2metric_type (argv[2], &type))
+      return CMD_WARNING;
+
+  if (argv[3] != NULL)
+    ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[3]);
+  else
+    ospf_routemap_unset (ospf, DEFAULT_ROUTE);
+
+  return ospf_redistribute_default_set (ospf, default_originate,
+                                       type, metric);
+}
+
+DEFUN (no_ospf_default_information_originate,
+       no_ospf_default_information_originate_cmd,
+       "no default-information originate",
+       NO_STR
+       "Control distribution of default information\n"
+       "Distribute a default route\n")
+{
+  struct ospf *ospf = vty->index;
+  struct prefix_ipv4 p;
+    
+  p.family = AF_INET;
+  p.prefix.s_addr = 0;
+  p.prefixlen = 0;
+
+  ospf_external_lsa_flush (ospf, DEFAULT_ROUTE, &p, 0);
+
+  if (EXTERNAL_INFO (DEFAULT_ROUTE)) {
+    ospf_external_info_delete (DEFAULT_ROUTE, p);
+    route_table_finish (EXTERNAL_INFO (DEFAULT_ROUTE));
+    EXTERNAL_INFO (DEFAULT_ROUTE) = NULL;
+  }
+
+  ospf_routemap_unset (ospf, DEFAULT_ROUTE);
+  return ospf_redistribute_default_unset (ospf);
+}
+
+DEFUN (ospf_default_metric,
+       ospf_default_metric_cmd,
+       "default-metric <0-16777214>",
+       "Set metric of redistributed routes\n"
+       "Default metric\n")
+{
+  struct ospf *ospf = vty->index;
+  int metric = -1;
+
+  if (!str2metric (argv[0], &metric))
+    return CMD_WARNING;
+
+  ospf->default_metric = metric;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_default_metric,
+       no_ospf_default_metric_cmd,
+       "no default-metric",
+       NO_STR
+       "Set metric of redistributed routes\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf->default_metric = -1;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ospf_default_metric,
+       no_ospf_default_metric_val_cmd,
+       "no default-metric <0-16777214>",
+       NO_STR
+       "Set metric of redistributed routes\n"
+       "Default metric\n")
+
+DEFUN (ospf_distance,
+       ospf_distance_cmd,
+       "distance <1-255>",
+       "Define an administrative distance\n"
+       "OSPF Administrative distance\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf->distance_all = atoi (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_distance,
+       no_ospf_distance_cmd,
+       "no distance <1-255>",
+       NO_STR
+       "Define an administrative distance\n"
+       "OSPF Administrative distance\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf->distance_all = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_distance_ospf,
+       no_ospf_distance_ospf_cmd,
+       "no distance ospf {intra-area|inter-area|external}",
+       NO_STR
+       "Define an administrative distance\n"
+       "OSPF Administrative distance\n"
+       "OSPF Distance\n"
+       "Intra-area routes\n"
+       "Inter-area routes\n"
+       "External routes\n")
+{
+  struct ospf *ospf = vty->index;
+
+  if (argc < 3)
+    return CMD_WARNING;
+
+  if (argv[0] != NULL)
+    ospf->distance_intra = 0;
+
+  if (argv[1] != NULL)
+    ospf->distance_inter = 0;
+
+  if (argv[2] != NULL)
+    ospf->distance_external = 0;
+
+  if (argv[0] || argv[1] || argv[2])
+    return CMD_SUCCESS;
+
+  /* If no arguments are given, clear all distance information */
+  ospf->distance_intra = 0;
+  ospf->distance_inter = 0;
+  ospf->distance_external = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_distance_ospf,
+       ospf_distance_ospf_cmd,
+       "distance ospf "
+         "{intra-area <1-255>|inter-area <1-255>|external <1-255>}",
+       "Define an administrative distance\n"
+       "OSPF Administrative distance\n"
+       "Intra-area routes\n"
+       "Distance for intra-area routes\n"
+       "Inter-area routes\n"
+       "Distance for inter-area routes\n"
+       "External routes\n"
+       "Distance for external routes\n")
+{
+  struct ospf *ospf = vty->index;
+
+  if (argc < 3) /* should not happen */
+    return CMD_WARNING;
+
+  if (!argv[0] && !argv[1] && !argv[2])
+    {
+      vty_out(vty, "%% Command incomplete. (Arguments required)%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argv[0] != NULL)
+    ospf->distance_intra = atoi(argv[0]);
+
+  if (argv[1] != NULL)
+    ospf->distance_inter = atoi(argv[1]);
+
+  if (argv[2] != NULL)
+    ospf->distance_external = atoi(argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_distance_source,
+       ospf_distance_source_cmd,
+       "distance <1-255> A.B.C.D/M",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf_distance_set (vty, ospf, argv[0], argv[1], NULL);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_distance_source,
+       no_ospf_distance_source_cmd,
+       "no distance <1-255> A.B.C.D/M",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf_distance_unset (vty, ospf, argv[0], argv[1], NULL);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_distance_source_access_list,
+       ospf_distance_source_access_list_cmd,
+       "distance <1-255> A.B.C.D/M WORD",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf_distance_set (vty, ospf, argv[0], argv[1], argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_distance_source_access_list,
+       no_ospf_distance_source_access_list_cmd,
+       "no distance <1-255> A.B.C.D/M WORD",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  struct ospf *ospf = vty->index;
+
+  ospf_distance_unset (vty, ospf, argv[0], argv[1], argv[2]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_ospf_mtu_ignore,
+       ip_ospf_mtu_ignore_addr_cmd,
+       "ip ospf mtu-ignore A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Disable mtu mismatch detection\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+          
+  struct ospf_if_params *params;
+  params = IF_DEF_PARAMS (ifp);
+        
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+        {
+          vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  params->mtu_ignore = 1;
+  if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT)
+    SET_IF_PARAM (params, mtu_ignore);
+  else 
+    {
+      UNSET_IF_PARAM (params, mtu_ignore);
+      if (params != IF_DEF_PARAMS (ifp))
+        {
+          ospf_free_if_params (ifp, addr);
+          ospf_if_update_params (ifp, addr);
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_ospf_mtu_ignore,
+      ip_ospf_mtu_ignore_cmd,
+      "ip ospf mtu-ignore",
+      "IP Information\n"
+      "OSPF interface commands\n"
+      "Disable mtu mismatch detection\n")
+
+    
+DEFUN (no_ip_ospf_mtu_ignore,
+       no_ip_ospf_mtu_ignore_addr_cmd,
+       "no ip ospf mtu-ignore A.B.C.D",
+       "IP Information\n"
+       "OSPF interface commands\n"
+       "Disable mtu mismatch detection\n"
+       "Address of interface")
+{
+  struct interface *ifp = vty->index;
+  struct in_addr addr;
+  int ret;
+          
+  struct ospf_if_params *params;
+  params = IF_DEF_PARAMS (ifp);
+        
+  if (argc == 1)
+    {
+      ret = inet_aton(argv[0], &addr);
+      if (!ret)
+        {
+          vty_out (vty, "Please specify interface address by A.B.C.D%s",
+                  VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      params = ospf_get_if_params (ifp, addr);
+      ospf_if_update_params (ifp, addr);
+    }
+  params->mtu_ignore = 0;
+  if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT)
+    SET_IF_PARAM (params, mtu_ignore);
+  else 
+    {
+      UNSET_IF_PARAM (params, mtu_ignore);
+      if (params != IF_DEF_PARAMS (ifp))
+        {
+          ospf_free_if_params (ifp, addr);
+          ospf_if_update_params (ifp, addr);
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_ospf_mtu_ignore,
+       no_ip_ospf_mtu_ignore_cmd,
+      "no ip ospf mtu-ignore",
+      "IP Information\n"
+      "OSPF interface commands\n"
+      "Disable mtu mismatch detection\n")
+
+DEFUN (ospf_max_metric_router_lsa_admin,
+       ospf_max_metric_router_lsa_admin_cmd,
+       "max-metric router-lsa administrative",
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Administratively applied, for an indefinite period\n")
+{
+  struct listnode *ln;
+  struct ospf_area *area;
+  struct ospf *ospf = vty->index;
+    
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+    {
+      SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED);
+      
+      if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+          ospf_router_lsa_update_area (area);
+    }
+
+  /* Allows for areas configured later to get the property */
+  ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_SET;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_max_metric_router_lsa_admin,
+       no_ospf_max_metric_router_lsa_admin_cmd,
+       "no max-metric router-lsa administrative",
+       NO_STR
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Administratively applied, for an indefinite period\n")
+{
+  struct listnode *ln;
+  struct ospf_area *area;
+  struct ospf *ospf = vty->index;
+    
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+    {
+      UNSET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED);
+      
+      /* Don't trample on the start-up stub timer */
+      if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)
+          && !area->t_stub_router)
+        {
+          UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED);
+          ospf_router_lsa_update_area (area);
+        }
+    }
+  ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_max_metric_router_lsa_startup,
+       ospf_max_metric_router_lsa_startup_cmd,
+       "max-metric router-lsa on-startup <5-86400>",
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Automatically advertise stub Router-LSA on startup of OSPF\n"
+       "Time (seconds) to advertise self as stub-router\n")
+{
+  unsigned int seconds;
+  struct ospf *ospf = vty->index;
+    
+  if (argc != 1)
+    {
+      vty_out (vty, "%% Must supply stub-router period");
+      return CMD_WARNING;
+    }
+  
+  VTY_GET_INTEGER ("stub-router startup period", seconds, argv[0]);
+  
+  ospf->stub_router_startup_time = seconds;
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_max_metric_router_lsa_startup,
+       no_ospf_max_metric_router_lsa_startup_cmd,
+       "no max-metric router-lsa on-startup",
+       NO_STR
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Automatically advertise stub Router-LSA on startup of OSPF\n")
+{
+  struct listnode *ln;
+  struct ospf_area *area;
+  struct ospf *ospf = vty->index;
+  
+  ospf->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED;
+  
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+    {
+      SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED);
+      OSPF_TIMER_OFF (area->t_stub_router);
+      
+      /* Don't trample on admin stub routed */
+      if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED))
+        {
+          UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED);
+          ospf_router_lsa_update_area (area);
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (ospf_max_metric_router_lsa_shutdown,
+       ospf_max_metric_router_lsa_shutdown_cmd,
+       "max-metric router-lsa on-shutdown <5-86400>",
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Advertise stub-router prior to full shutdown of OSPF\n"
+       "Time (seconds) to wait till full shutdown\n")
+{
+  unsigned int seconds;
+  struct ospf *ospf = vty->index;
+    
+  if (argc != 1)
+    {
+      vty_out (vty, "%% Must supply stub-router shutdown period");
+      return CMD_WARNING;
+    }
+  
+  VTY_GET_INTEGER ("stub-router shutdown wait period", seconds, argv[0]);
+  
+  ospf->stub_router_shutdown_time = seconds;
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_max_metric_router_lsa_shutdown,
+       no_ospf_max_metric_router_lsa_shutdown_cmd,
+       "no max-metric router-lsa on-shutdown",
+       NO_STR
+       "OSPF maximum / infinite-distance metric\n"
+       "Advertise own Router-LSA with infinite distance (stub router)\n"
+       "Advertise stub-router prior to full shutdown of OSPF\n")
+{
+  struct ospf *ospf = vty->index;
+    
+  ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED;
+  
+  return CMD_SUCCESS;
+}
+
+static void
+config_write_stub_router (struct vty *vty, struct ospf *ospf)
+{
+  struct listnode *ln;
+  struct ospf_area *area;
+  
+  if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+    vty_out (vty, " max-metric router-lsa on-startup %u%s",
+             ospf->stub_router_startup_time, VTY_NEWLINE);
+  if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+    vty_out (vty, " max-metric router-lsa on-shutdown %u%s",
+             ospf->stub_router_shutdown_time, VTY_NEWLINE);
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+    {
+      if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED))
+        {
+          vty_out (vty, " max-metric router-lsa administrative%s",
+                   VTY_NEWLINE);
+          break;
+        }
+    }
+  return;
+}
+
+static void
+show_ip_ospf_route_network (struct vty *vty, struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct listnode *pnode, *pnnode;
+  struct ospf_path *path;
+
+  vty_out (vty, "============ OSPF network routing table ============%s",
+          VTY_NEWLINE);
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((or = rn->info) != NULL)
+      {
+       char buf1[19];
+       snprintf (buf1, 19, "%s/%d",
+                 inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
+
+       switch (or->path_type)
+         {
+         case OSPF_PATH_INTER_AREA:
+           if (or->type == OSPF_DESTINATION_NETWORK)
+             vty_out (vty, "N IA %-18s    [%d] area: %s%s", buf1, or->cost,
+                      inet_ntoa (or->u.std.area_id), VTY_NEWLINE);
+           else if (or->type == OSPF_DESTINATION_DISCARD)
+             vty_out (vty, "D IA %-18s    Discard entry%s", buf1, VTY_NEWLINE);
+           break;
+         case OSPF_PATH_INTRA_AREA:
+           vty_out (vty, "N    %-18s    [%d] area: %s%s", buf1, or->cost,
+                    inet_ntoa (or->u.std.area_id), VTY_NEWLINE);
+           break;
+         default:
+           break;
+         }
+
+        if (or->type == OSPF_DESTINATION_NETWORK)
+          for (ALL_LIST_ELEMENTS (or->paths, pnode, pnnode, path))
+            {
+              if (if_lookup_by_index(path->ifindex))
+                {
+                  if (path->nexthop.s_addr == 0)
+                    vty_out (vty, "%24s   directly attached to %s%s",
+                             "", ifindex2ifname (path->ifindex), VTY_NEWLINE);
+                  else
+                    vty_out (vty, "%24s   via %s, %s%s", "",
+                             inet_ntoa (path->nexthop),
+                            ifindex2ifname (path->ifindex), VTY_NEWLINE);
+                }
+            }
+      }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+show_ip_ospf_route_router (struct vty *vty, struct route_table *rtrs)
+{
+  struct route_node *rn;
+  struct ospf_route *or;
+  struct listnode *pnode;
+  struct listnode *node;
+  struct ospf_path *path;
+
+  vty_out (vty, "============ OSPF router routing table =============%s",
+          VTY_NEWLINE);
+  for (rn = route_top (rtrs); rn; rn = route_next (rn))
+    if (rn->info)
+      {
+       int flag = 0;
+
+       vty_out (vty, "R    %-15s    ", inet_ntoa (rn->p.u.prefix4));
+
+       for (ALL_LIST_ELEMENTS_RO ((struct list *)rn->info, node, or))
+          {
+            if (flag++)
+          vty_out (vty, "%24s", "");
+
+            /* Show path. */
+            vty_out (vty, "%s [%d] area: %s",
+                     (or->path_type == OSPF_PATH_INTER_AREA ? "IA" : "  "),
+                     or->cost, inet_ntoa (or->u.std.area_id));
+            /* Show flags. */
+            vty_out (vty, "%s%s%s",
+                     (or->u.std.flags & ROUTER_LSA_BORDER ? ", ABR" : ""),
+                     (or->u.std.flags & ROUTER_LSA_EXTERNAL ? ", ASBR" : ""),
+                     VTY_NEWLINE);
+                  
+                  for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path))
+                    {
+                     if (if_lookup_by_index(path->ifindex))
+                       {
+                         if (path->nexthop.s_addr == 0)
+                           vty_out (vty, "%24s   directly attached to %s%s",
+                                    "", ifindex2ifname (path->ifindex),
+                                    VTY_NEWLINE);
+                         else
+                           vty_out (vty, "%24s   via %s, %s%s", "",
+                                    inet_ntoa (path->nexthop),
+                                    ifindex2ifname (path->ifindex),
+                                    VTY_NEWLINE);
+                       }
+                    }
+          }
+      }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+show_ip_ospf_route_external (struct vty *vty, struct route_table *rt)
+{
+  struct route_node *rn;
+  struct ospf_route *er;
+  struct listnode *pnode, *pnnode;
+  struct ospf_path *path;
+
+  vty_out (vty, "============ OSPF external routing table ===========%s",
+          VTY_NEWLINE);
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    if ((er = rn->info) != NULL)
+      {
+       char buf1[19];
+       snprintf (buf1, 19, "%s/%d",
+                 inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
+
+       switch (er->path_type)
+         {
+         case OSPF_PATH_TYPE1_EXTERNAL:
+           vty_out (vty, "N E1 %-18s    [%d] tag: %u%s", buf1,
+                    er->cost, er->u.ext.tag, VTY_NEWLINE);
+           break;
+         case OSPF_PATH_TYPE2_EXTERNAL:
+           vty_out (vty, "N E2 %-18s    [%d/%d] tag: %u%s", buf1, er->cost,
+                    er->u.ext.type2_cost, er->u.ext.tag, VTY_NEWLINE);
+           break;
+         }
+
+        for (ALL_LIST_ELEMENTS (er->paths, pnode, pnnode, path))
+          {
+            if (if_lookup_by_index(path->ifindex))
+              {
+                if (path->nexthop.s_addr == 0)
+                  vty_out (vty, "%24s   directly attached to %s%s",
+                           "", ifindex2ifname (path->ifindex), VTY_NEWLINE);
+                else
+                  vty_out (vty, "%24s   via %s, %s%s", "",
+                           inet_ntoa (path->nexthop),
+                          ifindex2ifname (path->ifindex),
+                           VTY_NEWLINE);
+              }
+           }
+        }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+DEFUN (show_ip_ospf_border_routers,
+       show_ip_ospf_border_routers_cmd,
+       "show ip ospf border-routers",
+       SHOW_STR
+       IP_STR
+       "show all the ABR's and ASBR's\n"
+       "for this area\n")
+{
+  struct ospf *ospf;
+
+  if ((ospf = ospf_lookup ()) == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  if (ospf->new_table == NULL)
+    {
+      vty_out (vty, "No OSPF routing information exist%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  /* Show Network routes.
+  show_ip_ospf_route_network (vty, ospf->new_table);   */
+
+  /* Show Router routes. */
+  show_ip_ospf_route_router (vty, ospf->new_rtrs);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_route,
+       show_ip_ospf_route_cmd,
+       "show ip ospf route",
+       SHOW_STR
+       IP_STR
+       "OSPF information\n"
+       "OSPF routing table\n")
+{
+  struct ospf *ospf;
+
+  if ((ospf = ospf_lookup ()) == NULL)
+    {
+      vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  if (ospf->new_table == NULL)
+    {
+      vty_out (vty, "No OSPF routing information exist%s", VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  /* Show Network routes. */
+  show_ip_ospf_route_network (vty, ospf->new_table);
+
+  /* Show Router routes. */
+  show_ip_ospf_route_router (vty, ospf->new_rtrs);
+
+  /* Show AS External routes. */
+  show_ip_ospf_route_external (vty, ospf->old_external_route);
+
+  return CMD_SUCCESS;
+}
+
+
+const char *ospf_abr_type_str[] = 
+{
+  "unknown",
+  "standard",
+  "ibm",
+  "cisco",
+  "shortcut"
+};
+
+const char *ospf_shortcut_mode_str[] = 
+{
+  "default",
+  "enable",
+  "disable"
+};
+
+
+static void
+area_id2str (char *buf, int length, struct ospf_area *area)
+{
+  memset (buf, 0, length);
+
+  if (area->format == OSPF_AREA_ID_FORMAT_ADDRESS)
+    strncpy (buf, inet_ntoa (area->area_id), length);
+  else
+    sprintf (buf, "%lu", (unsigned long) ntohl (area->area_id.s_addr));
+}
+
+
+const char *ospf_int_type_str[] = 
+{
+  "unknown",           /* should never be used. */
+  "point-to-point",
+  "broadcast",
+  "non-broadcast",
+  "point-to-multipoint",
+  "virtual-link",      /* should never be used. */
+  "loopback"
+};
+
+/* Configuration write function for ospfd. */
+static int
+config_write_interface (struct vty *vty)
+{
+  struct listnode *n1, *n2;
+  struct interface *ifp;
+  struct crypt_key *ck;
+  int write = 0;
+  struct route_node *rn = NULL;
+  struct ospf_if_params *params;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, n1, ifp))
+    {
+      if (memcmp (ifp->name, "VLINK", 5) == 0)
+       continue;
+
+      vty_out (vty, "!%s", VTY_NEWLINE);
+      vty_out (vty, "interface %s%s", ifp->name,
+               VTY_NEWLINE);
+      if (ifp->desc)
+        vty_out (vty, " description %s%s", ifp->desc,
+               VTY_NEWLINE);
+
+      write++;
+
+      params = IF_DEF_PARAMS (ifp);
+      
+      do {
+       /* Interface Network print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, type) &&
+           params->type != OSPF_IFTYPE_LOOPBACK)
+         {
+           if (params->type != ospf_default_iftype(ifp))
+             {
+               vty_out (vty, " ip ospf network %s",
+                        ospf_int_type_str[params->type]);
+               if (params != IF_DEF_PARAMS (ifp))
+                 vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+               vty_out (vty, "%s", VTY_NEWLINE);
+             }
+         }
+       
+       /* OSPF interface authentication print */
+       if (OSPF_IF_PARAM_CONFIGURED (params, auth_type) &&
+           params->auth_type != OSPF_AUTH_NOTSET)
+         {
+           const char *auth_str;
+           
+           /* Translation tables are not that much help here due to syntax
+              of the simple option */
+           switch (params->auth_type)
+             {
+               
+             case OSPF_AUTH_NULL:
+               auth_str = " null";
+               break;
+               
+             case OSPF_AUTH_SIMPLE:
+               auth_str = "";
+               break;
+               
+             case OSPF_AUTH_CRYPTOGRAPHIC:
+               auth_str = " message-digest";
+               break;
+               
+             default:
+               auth_str = "";
+               break;
+             }
+           
+           vty_out (vty, " ip ospf authentication%s", auth_str);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+
+       /* Simple Authentication Password print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, auth_simple) &&
+           params->auth_simple[0] != '\0')
+         {
+           vty_out (vty, " ip ospf authentication-key %s",
+                    params->auth_simple);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       /* Cryptographic Authentication Key print. */
+       for (ALL_LIST_ELEMENTS_RO (params->auth_crypt, n2, ck))
+         {
+           vty_out (vty, " ip ospf message-digest-key %d md5 %s",
+                    ck->key_id, ck->auth_key);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       /* Interface Output Cost print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, output_cost_cmd))
+         {
+           vty_out (vty, " ip ospf cost %u", params->output_cost_cmd);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       /* Hello Interval print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, v_hello) &&
+           params->v_hello != OSPF_HELLO_INTERVAL_DEFAULT)
+         {
+           vty_out (vty, " ip ospf hello-interval %u", params->v_hello);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       
+       /* Router Dead Interval print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, v_wait) &&
+           params->v_wait != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT)
+         {
+           vty_out (vty, " ip ospf dead-interval ");
+           
+           /* fast hello ? */
+           if (OSPF_IF_PARAM_CONFIGURED (params, fast_hello))
+             vty_out (vty, "minimal hello-multiplier %d",
+                      params->fast_hello);
+            else
+              vty_out (vty, "%u", params->v_wait);
+            
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+      /* Router Priority print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, priority) &&
+           params->priority != OSPF_ROUTER_PRIORITY_DEFAULT)
+         {
+           vty_out (vty, " ip ospf priority %u", params->priority);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       /* Retransmit Interval print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, retransmit_interval) &&
+           params->retransmit_interval != OSPF_RETRANSMIT_INTERVAL_DEFAULT)
+         {
+           vty_out (vty, " ip ospf retransmit-interval %u",
+                    params->retransmit_interval);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+       
+       /* Transmit Delay print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, transmit_delay) &&
+           params->transmit_delay != OSPF_TRANSMIT_DELAY_DEFAULT)
+         {
+           vty_out (vty, " ip ospf transmit-delay %u", params->transmit_delay);
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+
+       /* Area  print. */
+       if (OSPF_IF_PARAM_CONFIGURED (params, if_area))
+         {
+           vty_out (vty, " ip ospf area %s", inet_ntoa (params->if_area));
+           if (params != IF_DEF_PARAMS (ifp))
+             vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+            vty_out (vty, "%s", VTY_NEWLINE);
+         }
+
+    /* MTU ignore print. */
+    if (OSPF_IF_PARAM_CONFIGURED (params, mtu_ignore) &&
+       params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT)
+      {
+        if (params->mtu_ignore == 0)
+          vty_out (vty, " no ip ospf mtu-ignore");
+        else
+          vty_out (vty, " ip ospf mtu-ignore");
+        if (params != IF_DEF_PARAMS (ifp))
+           vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4));
+        vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+
+       while (1)
+         {
+           if (rn == NULL)
+             rn = route_top (IF_OIFS_PARAMS (ifp));
+           else
+             rn = route_next (rn);
+
+           if (rn == NULL)
+             break;
+           params = rn->info;
+           if (params != NULL)
+             break;
+         }
+      } while (rn);
+      
+      ospf_opaque_config_write_if (vty, ifp);
+    }
+
+  return write;
+}
+
+static int
+config_write_network_area (struct vty *vty, struct ospf *ospf)
+{
+  struct route_node *rn;
+  u_char buf[INET_ADDRSTRLEN];
+
+  /* `network area' print. */
+  for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+    if (rn->info)
+      {
+       struct ospf_network *n = rn->info;
+
+       memset (buf, 0, INET_ADDRSTRLEN);
+
+       /* Create Area ID string by specified Area ID format. */
+       if (n->format == OSPF_AREA_ID_FORMAT_ADDRESS)
+         strncpy ((char *) buf, inet_ntoa (n->area_id), INET_ADDRSTRLEN);
+       else
+         sprintf ((char *) buf, "%lu", 
+                  (unsigned long int) ntohl (n->area_id.s_addr));
+
+       /* Network print. */
+       vty_out (vty, " network %s/%d area %s%s",
+                inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+                buf, VTY_NEWLINE);
+      }
+
+  return 0;
+}
+
+static int
+config_write_ospf_area (struct vty *vty, struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_area *area;
+  u_char buf[INET_ADDRSTRLEN];
+
+  /* Area configuration print. */
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      struct route_node *rn1;
+
+      area_id2str ((char *) buf, INET_ADDRSTRLEN, area);
+
+      if (area->auth_type != OSPF_AUTH_NULL)
+       {
+         if (area->auth_type == OSPF_AUTH_SIMPLE)
+           vty_out (vty, " area %s authentication%s", buf, VTY_NEWLINE);
+         else
+           vty_out (vty, " area %s authentication message-digest%s",
+                    buf, VTY_NEWLINE);
+       }
+
+      if (area->shortcut_configured != OSPF_SHORTCUT_DEFAULT)
+       vty_out (vty, " area %s shortcut %s%s", buf,
+                ospf_shortcut_mode_str[area->shortcut_configured],
+                VTY_NEWLINE);
+
+      if ((area->external_routing == OSPF_AREA_STUB)
+         || (area->external_routing == OSPF_AREA_NSSA)
+         )
+       {
+         if (area->external_routing == OSPF_AREA_STUB)
+           vty_out (vty, " area %s stub", buf);
+         else if (area->external_routing == OSPF_AREA_NSSA)
+           {
+             vty_out (vty, " area %s nssa", buf);
+             switch (area->NSSATranslatorRole)
+               {
+                 case OSPF_NSSA_ROLE_NEVER:
+                   vty_out (vty, " translate-never");
+                   break;
+                 case OSPF_NSSA_ROLE_ALWAYS:
+                   vty_out (vty, " translate-always");
+                   break;
+                 case OSPF_NSSA_ROLE_CANDIDATE:
+                 default:
+                   vty_out (vty, " translate-candidate");
+               }
+           }
+
+         if (area->no_summary)
+           vty_out (vty, " no-summary");
+
+         vty_out (vty, "%s", VTY_NEWLINE);
+
+         if (area->default_cost != 1)
+           vty_out (vty, " area %s default-cost %d%s", buf, 
+                    area->default_cost, VTY_NEWLINE);
+       }
+
+      for (rn1 = route_top (area->ranges); rn1; rn1 = route_next (rn1))
+       if (rn1->info)
+         {
+           struct ospf_area_range *range = rn1->info;
+
+           vty_out (vty, " area %s range %s/%d", buf,
+                    inet_ntoa (rn1->p.u.prefix4), rn1->p.prefixlen);
+
+           if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC)
+             vty_out (vty, " cost %d", range->cost_config);
+
+           if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE))
+             vty_out (vty, " not-advertise");
+
+           if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE))
+             vty_out (vty, " substitute %s/%d",
+                      inet_ntoa (range->subst_addr), range->subst_masklen);
+
+           vty_out (vty, "%s", VTY_NEWLINE);
+         }
+
+      if (EXPORT_NAME (area))
+       vty_out (vty, " area %s export-list %s%s", buf,
+                EXPORT_NAME (area), VTY_NEWLINE);
+
+      if (IMPORT_NAME (area))
+       vty_out (vty, " area %s import-list %s%s", buf,
+                IMPORT_NAME (area), VTY_NEWLINE);
+
+      if (PREFIX_NAME_IN (area))
+       vty_out (vty, " area %s filter-list prefix %s in%s", buf,
+                PREFIX_NAME_IN (area), VTY_NEWLINE);
+
+      if (PREFIX_NAME_OUT (area))
+       vty_out (vty, " area %s filter-list prefix %s out%s", buf,
+                PREFIX_NAME_OUT (area), VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+static int
+config_write_ospf_nbr_nbma (struct vty *vty, struct ospf *ospf)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct route_node *rn;
+
+  /* Static Neighbor configuration print. */
+  for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn))
+    if ((nbr_nbma = rn->info))
+      {
+       vty_out (vty, " neighbor %s", inet_ntoa (nbr_nbma->addr));
+
+       if (nbr_nbma->priority != OSPF_NEIGHBOR_PRIORITY_DEFAULT)
+         vty_out (vty, " priority %d", nbr_nbma->priority);
+
+       if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT)
+         vty_out (vty, " poll-interval %d", nbr_nbma->v_poll);
+
+       vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+  return 0;
+}
+
+static int
+config_write_virtual_link (struct vty *vty, struct ospf *ospf)
+{
+  struct listnode *node;
+  struct ospf_vl_data *vl_data;
+  u_char buf[INET_ADDRSTRLEN];
+
+  /* Virtual-Link print */
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data))
+    {
+      struct listnode *n2;
+      struct crypt_key *ck;
+      struct ospf_interface *oi;
+
+      if (vl_data != NULL)
+       {
+         memset (buf, 0, INET_ADDRSTRLEN);
+         
+         if (vl_data->format == OSPF_AREA_ID_FORMAT_ADDRESS)
+           strncpy ((char *) buf, inet_ntoa (vl_data->vl_area_id), INET_ADDRSTRLEN);
+         else
+           sprintf ((char *) buf, "%lu", 
+                    (unsigned long int) ntohl (vl_data->vl_area_id.s_addr));
+         oi = vl_data->vl_oi;
+
+         /* timers */
+         if (OSPF_IF_PARAM (oi, v_hello) != OSPF_HELLO_INTERVAL_DEFAULT ||
+             OSPF_IF_PARAM (oi, v_wait) != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT ||
+             OSPF_IF_PARAM (oi, retransmit_interval) != OSPF_RETRANSMIT_INTERVAL_DEFAULT ||
+             OSPF_IF_PARAM (oi, transmit_delay) != OSPF_TRANSMIT_DELAY_DEFAULT)
+           vty_out (vty, " area %s virtual-link %s hello-interval %d retransmit-interval %d transmit-delay %d dead-interval %d%s",
+                    buf,
+                    inet_ntoa (vl_data->vl_peer), 
+                    OSPF_IF_PARAM (oi, v_hello),
+                    OSPF_IF_PARAM (oi, retransmit_interval),
+                    OSPF_IF_PARAM (oi, transmit_delay),
+                    OSPF_IF_PARAM (oi, v_wait),
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, " area %s virtual-link %s%s", buf,
+                    inet_ntoa (vl_data->vl_peer), VTY_NEWLINE);
+         /* Auth key */
+         if (IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_simple[0] != '\0')
+           vty_out (vty, " area %s virtual-link %s authentication-key %s%s",
+                    buf,
+                    inet_ntoa (vl_data->vl_peer),
+                    IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_simple,
+                    VTY_NEWLINE);
+         /* md5 keys */
+         for (ALL_LIST_ELEMENTS_RO (IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_crypt,
+                                     n2, ck))
+            vty_out (vty, " area %s virtual-link %s"
+                     " message-digest-key %d md5 %s%s",
+                     buf,
+                     inet_ntoa (vl_data->vl_peer),
+                     ck->key_id, ck->auth_key, VTY_NEWLINE);
+        
+       }
+    }
+
+  return 0;
+}
+
+
+static int
+config_write_ospf_redistribute (struct vty *vty, struct ospf *ospf)
+{
+  int type;
+
+  /* redistribute print. */
+  for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+    if (type != zclient->redist_default &&
+        vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+      {
+        vty_out (vty, " redistribute %s", zebra_route_string(type));
+       if (ospf->dmetric[type].value >= 0)
+         vty_out (vty, " metric %d", ospf->dmetric[type].value);
+       
+        if (ospf->dmetric[type].type == EXTERNAL_METRIC_TYPE_1)
+         vty_out (vty, " metric-type 1");
+
+       if (ROUTEMAP_NAME (ospf, type))
+         vty_out (vty, " route-map %s", ROUTEMAP_NAME (ospf, type));
+       
+        vty_out (vty, "%s", VTY_NEWLINE);
+      }
+
+  return 0;
+}
+
+static int
+config_write_ospf_default_metric (struct vty *vty, struct ospf *ospf)
+{
+  if (ospf->default_metric != -1)
+    vty_out (vty, " default-metric %d%s", ospf->default_metric,
+            VTY_NEWLINE);
+  return 0;
+}
+
+static int
+config_write_ospf_distribute (struct vty *vty, struct ospf *ospf)
+{
+  int type;
+
+  if (ospf)
+    {
+      /* distribute-list print. */
+      for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+       if (DISTRIBUTE_NAME (ospf, type))
+         vty_out (vty, " distribute-list %s out %s%s", 
+                  DISTRIBUTE_NAME (ospf, type),
+                  zebra_route_string(type), VTY_NEWLINE);
+
+      /* default-information print. */
+      if (ospf->default_originate != DEFAULT_ORIGINATE_NONE)
+       {
+         vty_out (vty, " default-information originate");
+         if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS)
+           vty_out (vty, " always");
+
+         if (ospf->dmetric[DEFAULT_ROUTE].value >= 0)
+           vty_out (vty, " metric %d",
+                    ospf->dmetric[DEFAULT_ROUTE].value);
+         if (ospf->dmetric[DEFAULT_ROUTE].type == EXTERNAL_METRIC_TYPE_1)
+           vty_out (vty, " metric-type 1");
+
+         if (ROUTEMAP_NAME (ospf, DEFAULT_ROUTE))
+           vty_out (vty, " route-map %s",
+                    ROUTEMAP_NAME (ospf, DEFAULT_ROUTE));
+         
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+
+    }
+
+  return 0;
+}
+
+static int
+config_write_ospf_distance (struct vty *vty, struct ospf *ospf)
+{
+  struct route_node *rn;
+  struct ospf_distance *odistance;
+
+  if (ospf->distance_all)
+    vty_out (vty, " distance %d%s", ospf->distance_all, VTY_NEWLINE);
+
+  if (ospf->distance_intra 
+      || ospf->distance_inter 
+      || ospf->distance_external)
+    {
+      vty_out (vty, " distance ospf");
+
+      if (ospf->distance_intra)
+       vty_out (vty, " intra-area %d", ospf->distance_intra);
+      if (ospf->distance_inter)
+       vty_out (vty, " inter-area %d", ospf->distance_inter);
+      if (ospf->distance_external)
+       vty_out (vty, " external %d", ospf->distance_external);
+
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  
+  for (rn = route_top (ospf->distance_table); rn; rn = route_next (rn))
+    if ((odistance = rn->info) != NULL)
+      {
+       vty_out (vty, " distance %d %s/%d %s%s", odistance->distance,
+                inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+                odistance->access_list ? odistance->access_list : "",
+                VTY_NEWLINE);
+      }
+  return 0;
+}
+
+/* OSPF configuration write function. */
+static int
+ospf_config_write (struct vty *vty)
+{
+  struct ospf *ospf;
+  struct interface *ifp;
+  struct ospf_interface *oi;
+  struct listnode *node;
+  int write = 0;
+
+  ospf = ospf_lookup ();
+  if (ospf != NULL)
+    {
+      /* `router ospf' print. */
+      vty_out (vty, "router ospf%s", VTY_NEWLINE);
+
+      write++;
+
+      if (!ospf->networks)
+        return write;
+
+      /* Router ID print. */
+      if (ospf->router_id_static.s_addr != 0)
+        vty_out (vty, " ospf router-id %s%s",
+                 inet_ntoa (ospf->router_id_static), VTY_NEWLINE);
+
+      /* ABR type print. */
+      if (ospf->abr_type != OSPF_ABR_DEFAULT)
+        vty_out (vty, " ospf abr-type %s%s", 
+                 ospf_abr_type_str[ospf->abr_type], VTY_NEWLINE);
+
+      /* log-adjacency-changes flag print. */
+      if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES))
+       {
+         vty_out(vty, " log-adjacency-changes");
+         if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
+           vty_out(vty, " detail");
+         vty_out(vty, "%s", VTY_NEWLINE);
+       }
+
+      /* RFC1583 compatibility flag print -- Compatible with CISCO 12.1. */
+      if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE))
+       vty_out (vty, " compatible rfc1583%s", VTY_NEWLINE);
+
+      /* auto-cost reference-bandwidth configuration.  */
+      if (ospf->ref_bandwidth != OSPF_DEFAULT_REF_BANDWIDTH)
+        {
+          vty_out (vty, "! Important: ensure reference bandwidth "
+                        "is consistent across all routers%s", VTY_NEWLINE);
+          vty_out (vty, " auto-cost reference-bandwidth %d%s",
+                  ospf->ref_bandwidth / 1000, VTY_NEWLINE);
+        }
+
+      /* LSA timers */
+      if (ospf->min_ls_interval != OSPF_MIN_LS_INTERVAL)
+  vty_out (vty, " timers throttle lsa all %d%s",
+     ospf->min_ls_interval, VTY_NEWLINE);
+      if (ospf->min_ls_arrival != OSPF_MIN_LS_ARRIVAL)
+  vty_out (vty, " timers lsa arrival %d%s",
+     ospf->min_ls_arrival, VTY_NEWLINE);
+
+      /* SPF timers print. */
+      if (ospf->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
+         ospf->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
+         ospf->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT)
+       vty_out (vty, " timers throttle spf %d %d %d%s",
+                ospf->spf_delay, ospf->spf_holdtime,
+                ospf->spf_max_holdtime, VTY_NEWLINE);
+      
+      /* Max-metric router-lsa print */
+      config_write_stub_router (vty, ospf);
+      
+      /* SPF refresh parameters print. */
+      if (ospf->lsa_refresh_interval != OSPF_LSA_REFRESH_INTERVAL_DEFAULT)
+       vty_out (vty, " refresh timer %d%s",
+                ospf->lsa_refresh_interval, VTY_NEWLINE);
+
+      /* Redistribute information print. */
+      config_write_ospf_redistribute (vty, ospf);
+
+      /* passive-interface print. */
+      if (ospf->passive_interface_default == OSPF_IF_PASSIVE)
+        vty_out (vty, " passive-interface default%s", VTY_NEWLINE);
+      
+      for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
+        if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface)
+            && IF_DEF_PARAMS (ifp)->passive_interface != 
+                              ospf->passive_interface_default)
+          {
+            vty_out (vty, " %spassive-interface %s%s",
+                     IF_DEF_PARAMS (ifp)->passive_interface ? "" : "no ",
+                     ifp->name, VTY_NEWLINE);
+          }
+      for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+        {
+          if (!OSPF_IF_PARAM_CONFIGURED (oi->params, passive_interface))
+            continue;
+          if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (oi->ifp),
+                                        passive_interface))
+            {
+              if (oi->params->passive_interface == IF_DEF_PARAMS (oi->ifp)->passive_interface)
+                continue;
+            }
+          else if (oi->params->passive_interface == ospf->passive_interface_default)
+            continue;
+          
+          vty_out (vty, " %spassive-interface %s %s%s",
+                   oi->params->passive_interface ? "" : "no ",
+                   oi->ifp->name,
+                   inet_ntoa (oi->address->u.prefix4), VTY_NEWLINE);
+        }
+      
+      /* Network area print. */
+      config_write_network_area (vty, ospf);
+
+      /* Area config print. */
+      config_write_ospf_area (vty, ospf);
+
+      /* static neighbor print. */
+      config_write_ospf_nbr_nbma (vty, ospf);
+
+      /* Virtual-Link print. */
+      config_write_virtual_link (vty, ospf);
+
+      /* Default metric configuration.  */
+      config_write_ospf_default_metric (vty, ospf);
+
+      /* Distribute-list and default-information print. */
+      config_write_ospf_distribute (vty, ospf);
+
+      /* Distance configuration. */
+      config_write_ospf_distance (vty, ospf);
+
+      ospf_opaque_config_write_router (vty, ospf);
+    }
+
+  return write;
+}
+
+void
+ospf_vty_show_init (void)
+{
+  /* "show ip ospf" commands. */
+  install_element (VIEW_NODE, &show_ip_ospf_cmd);
+
+  /* "show ip ospf database" commands. */
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_id_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_id_adv_router_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_adv_router_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_id_self_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_type_self_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_database_cmd);
+
+  /* "show ip ospf interface" commands. */
+  install_element (VIEW_NODE, &show_ip_ospf_interface_cmd);
+
+  /* "show ip ospf neighbor" commands. */
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_detail_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_id_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_detail_all_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_detail_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_neighbor_all_cmd);
+
+  /* "show ip ospf route" commands. */
+  install_element (VIEW_NODE, &show_ip_ospf_route_cmd);
+  install_element (VIEW_NODE, &show_ip_ospf_border_routers_cmd);
+}
+
+
+/* ospfd's interface node. */
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1
+};
+
+/* Initialization of OSPF interface. */
+static void
+ospf_vty_if_init (void)
+{
+  /* Install interface node. */
+  install_node (&interface_node, config_write_interface);
+
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_element (CONFIG_NODE, &no_interface_cmd);
+  install_default (INTERFACE_NODE);
+
+  /* "description" commands. */
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+
+  /* "ip ospf authentication" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_args_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_args_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_authentication_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_authentication_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_key_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_authentication_key_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_authentication_key_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_authentication_key_cmd);
+
+  /* "ip ospf message-digest-key" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_message_digest_key_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_message_digest_key_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_message_digest_key_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_message_digest_key_cmd);
+
+  /* "ip ospf cost" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_cost_u32_inet4_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_cost_u32_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_cost_u32_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_cost_u32_inet4_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_cost_inet4_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_cost_cmd);
+
+  /* "ip ospf mtu-ignore" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_mtu_ignore_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_mtu_ignore_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_mtu_ignore_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_mtu_ignore_cmd);
+
+  /* "ip ospf dead-interval" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_dead_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_dead_interval_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_dead_interval_minimal_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_dead_interval_minimal_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_seconds_cmd);
+  
+  /* "ip ospf hello-interval" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_hello_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_seconds_cmd);
+
+  /* "ip ospf network" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_network_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_network_cmd);
+
+  /* "ip ospf priority" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_priority_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_priority_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_priority_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_priority_cmd);
+
+  /* "ip ospf retransmit-interval" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_retransmit_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_retransmit_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_retransmit_interval_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_retransmit_interval_cmd);
+
+  /* "ip ospf transmit-delay" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd);
+  install_element (INTERFACE_NODE, &ip_ospf_transmit_delay_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_cmd);
+
+  /* "ip ospf area" commands. */
+  install_element (INTERFACE_NODE, &ip_ospf_area_cmd);
+  install_element (INTERFACE_NODE, &no_ip_ospf_area_cmd);
+
+  /* These commands are compatibitliy for previous version. */
+  install_element (INTERFACE_NODE, &ospf_authentication_key_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_authentication_key_cmd);
+  install_element (INTERFACE_NODE, &ospf_message_digest_key_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_message_digest_key_cmd);
+  install_element (INTERFACE_NODE, &ospf_cost_u32_cmd);
+  install_element (INTERFACE_NODE, &ospf_cost_u32_inet4_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_cost_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_cost_u32_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_cost_u32_inet4_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_cost_inet4_cmd);
+  install_element (INTERFACE_NODE, &ospf_dead_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_dead_interval_cmd);
+  install_element (INTERFACE_NODE, &ospf_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_hello_interval_cmd);
+  install_element (INTERFACE_NODE, &ospf_network_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_network_cmd);
+  install_element (INTERFACE_NODE, &ospf_priority_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_priority_cmd);
+  install_element (INTERFACE_NODE, &ospf_retransmit_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_retransmit_interval_cmd);
+  install_element (INTERFACE_NODE, &ospf_transmit_delay_cmd);
+  install_element (INTERFACE_NODE, &no_ospf_transmit_delay_cmd);
+}
+
+static void
+ospf_vty_zebra_init (void)
+{
+  install_element (OSPF_NODE, &ospf_redistribute_source_cmd);
+  install_element (OSPF_NODE, &no_ospf_redistribute_source_cmd);
+
+  install_element (OSPF_NODE, &ospf_distribute_list_out_cmd);
+  install_element (OSPF_NODE, &no_ospf_distribute_list_out_cmd);
+
+  install_element (OSPF_NODE, &ospf_default_information_originate_cmd);
+  install_element (OSPF_NODE, &no_ospf_default_information_originate_cmd);
+
+  install_element (OSPF_NODE, &ospf_default_metric_cmd);
+  install_element (OSPF_NODE, &no_ospf_default_metric_cmd);
+  install_element (OSPF_NODE, &no_ospf_default_metric_val_cmd);
+
+  install_element (OSPF_NODE, &ospf_distance_cmd);
+  install_element (OSPF_NODE, &no_ospf_distance_cmd);
+  install_element (OSPF_NODE, &no_ospf_distance_ospf_cmd);
+  install_element (OSPF_NODE, &ospf_distance_ospf_cmd);
+#if 0
+  install_element (OSPF_NODE, &ospf_distance_source_cmd);
+  install_element (OSPF_NODE, &no_ospf_distance_source_cmd);
+  install_element (OSPF_NODE, &ospf_distance_source_access_list_cmd);
+  install_element (OSPF_NODE, &no_ospf_distance_source_access_list_cmd);
+#endif /* 0 */
+}
+
+static struct cmd_node ospf_node =
+{
+  OSPF_NODE,
+  "%s(config-router)# ",
+  1
+};
+
+static void
+ospf_interface_clear (struct interface *ifp)
+{
+  if (!if_is_operative (ifp)) return;
+
+  if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
+    zlog (NULL, LOG_DEBUG, "ISM[%s]: clear by reset", ifp->name);
+
+  ospf_if_reset(ifp);
+}
+
+DEFUN (clear_ip_ospf_interface,
+       clear_ip_ospf_interface_cmd,
+       "clear ip ospf interface [IFNAME]",
+       CLEAR_STR
+       IP_STR
+       "OSPF information\n"
+       "Interface information\n"
+       "Interface name\n")
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  if (argc == 0) /* Clear all the ospfv2 interfaces. */
+    {
+      for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+        ospf_interface_clear(ifp);
+    }
+  else /* Interface name is specified. */
+    {
+      if ((ifp = if_lookup_by_name (argv[0])) == NULL)
+        vty_out (vty, "No such interface name%s", VTY_NEWLINE);
+      else
+        ospf_interface_clear(ifp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf_vty_clear_init (void)
+{
+  install_element (ENABLE_NODE, &clear_ip_ospf_interface_cmd);
+}
+
+
+/* Install OSPF related vty commands. */
+void
+ospf_vty_init (void)
+{
+  /* Install ospf top node. */
+  install_node (&ospf_node, ospf_config_write);
+
+  /* "router ospf" commands. */
+  install_element (CONFIG_NODE, &router_ospf_cmd);
+  install_element (CONFIG_NODE, &no_router_ospf_cmd);
+
+  install_default (OSPF_NODE);
+
+  /* "ospf router-id" commands. */
+  install_element (OSPF_NODE, &ospf_router_id_cmd);
+  install_element (OSPF_NODE, &no_ospf_router_id_cmd);
+  install_element (OSPF_NODE, &router_ospf_id_cmd);
+  install_element (OSPF_NODE, &no_router_ospf_id_cmd);
+
+  /* "passive-interface" commands. */
+  install_element (OSPF_NODE, &ospf_passive_interface_addr_cmd);
+  install_element (OSPF_NODE, &ospf_passive_interface_cmd);
+  install_element (OSPF_NODE, &ospf_passive_interface_default_cmd);
+  install_element (OSPF_NODE, &no_ospf_passive_interface_addr_cmd);
+  install_element (OSPF_NODE, &no_ospf_passive_interface_cmd);
+  install_element (OSPF_NODE, &no_ospf_passive_interface_default_cmd);
+
+  /* "ospf abr-type" commands. */
+  install_element (OSPF_NODE, &ospf_abr_type_cmd);
+  install_element (OSPF_NODE, &no_ospf_abr_type_cmd);
+
+  /* "ospf log-adjacency-changes" commands. */
+  install_element (OSPF_NODE, &ospf_log_adjacency_changes_cmd);
+  install_element (OSPF_NODE, &ospf_log_adjacency_changes_detail_cmd);
+  install_element (OSPF_NODE, &no_ospf_log_adjacency_changes_cmd);
+  install_element (OSPF_NODE, &no_ospf_log_adjacency_changes_detail_cmd);
+
+  /* "ospf rfc1583-compatible" commands. */
+  install_element (OSPF_NODE, &ospf_rfc1583_flag_cmd);
+  install_element (OSPF_NODE, &no_ospf_rfc1583_flag_cmd);
+  install_element (OSPF_NODE, &ospf_compatible_rfc1583_cmd);
+  install_element (OSPF_NODE, &no_ospf_compatible_rfc1583_cmd);
+
+  /* "network area" commands. */
+  install_element (OSPF_NODE, &ospf_network_area_cmd);
+  install_element (OSPF_NODE, &no_ospf_network_area_cmd);
+
+  /* "area authentication" commands. */
+  install_element (OSPF_NODE, &ospf_area_authentication_message_digest_cmd);
+  install_element (OSPF_NODE, &ospf_area_authentication_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_authentication_cmd);
+
+  /* "area range" commands.  */
+  install_element (OSPF_NODE, &ospf_area_range_cmd);
+  install_element (OSPF_NODE, &ospf_area_range_advertise_cmd);
+  install_element (OSPF_NODE, &ospf_area_range_cost_cmd);
+  install_element (OSPF_NODE, &ospf_area_range_advertise_cost_cmd);
+  install_element (OSPF_NODE, &ospf_area_range_not_advertise_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_range_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_range_advertise_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_range_cost_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_range_advertise_cost_cmd);
+  install_element (OSPF_NODE, &ospf_area_range_substitute_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_range_substitute_cmd);
+
+  /* "area virtual-link" commands. */
+  install_element (OSPF_NODE, &ospf_area_vlink_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_param1_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_param1_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_param2_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_param2_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_param3_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_param3_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_param4_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_param4_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_cmd);
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_md5_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_md5_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_authkey_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_authkey_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_authkey_cmd);
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_authkey_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_authkey_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_md5_cmd);
+  install_element (OSPF_NODE, &ospf_area_vlink_authtype_md5_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_md5_cmd);
+
+  /* "area stub" commands. */
+  install_element (OSPF_NODE, &ospf_area_stub_no_summary_cmd);
+  install_element (OSPF_NODE, &ospf_area_stub_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_stub_no_summary_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_stub_cmd);
+
+  /* "area nssa" commands. */
+  install_element (OSPF_NODE, &ospf_area_nssa_cmd);
+  install_element (OSPF_NODE, &ospf_area_nssa_translate_no_summary_cmd);
+  install_element (OSPF_NODE, &ospf_area_nssa_translate_cmd);
+  install_element (OSPF_NODE, &ospf_area_nssa_no_summary_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_nssa_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_default_cost_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_default_cost_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_shortcut_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_shortcut_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_export_list_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_export_list_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_filter_list_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_filter_list_cmd);
+
+  install_element (OSPF_NODE, &ospf_area_import_list_cmd);
+  install_element (OSPF_NODE, &no_ospf_area_import_list_cmd);
+  
+  /* LSA timer commands */
+  install_element (OSPF_NODE, &ospf_timers_min_ls_interval_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_min_ls_interval_cmd);
+  install_element (OSPF_NODE, &ospf_timers_min_ls_arrival_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_min_ls_arrival_cmd);
+
+  /* SPF timer commands */
+  install_element (OSPF_NODE, &ospf_timers_spf_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_spf_cmd);
+  install_element (OSPF_NODE, &ospf_timers_throttle_spf_cmd);
+  install_element (OSPF_NODE, &no_ospf_timers_throttle_spf_cmd);
+  
+  /* refresh timer commands */
+  install_element (OSPF_NODE, &ospf_refresh_timer_cmd);
+  install_element (OSPF_NODE, &no_ospf_refresh_timer_val_cmd);
+  install_element (OSPF_NODE, &no_ospf_refresh_timer_cmd);
+  
+  /* max-metric commands */
+  install_element (OSPF_NODE, &ospf_max_metric_router_lsa_admin_cmd);
+  install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_admin_cmd);
+  install_element (OSPF_NODE, &ospf_max_metric_router_lsa_startup_cmd);
+  install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_startup_cmd);
+  install_element (OSPF_NODE, &ospf_max_metric_router_lsa_shutdown_cmd);
+  install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_shutdown_cmd);
+  
+  /* reference bandwidth commands */
+  install_element (OSPF_NODE, &ospf_auto_cost_reference_bandwidth_cmd);
+  install_element (OSPF_NODE, &no_ospf_auto_cost_reference_bandwidth_cmd);
+
+  /* "neighbor" commands. */
+  install_element (OSPF_NODE, &ospf_neighbor_cmd);
+  install_element (OSPF_NODE, &ospf_neighbor_priority_poll_interval_cmd);
+  install_element (OSPF_NODE, &ospf_neighbor_priority_cmd);
+  install_element (OSPF_NODE, &ospf_neighbor_poll_interval_cmd);
+  install_element (OSPF_NODE, &ospf_neighbor_poll_interval_priority_cmd);
+  install_element (OSPF_NODE, &no_ospf_neighbor_cmd);
+  install_element (OSPF_NODE, &no_ospf_neighbor_priority_cmd);
+  install_element (OSPF_NODE, &no_ospf_neighbor_poll_interval_cmd);
+
+  /* Init interface related vty commands. */
+  ospf_vty_if_init ();
+
+  /* Init zebra related vty commands. */
+  ospf_vty_zebra_init ();
+}
diff --git a/ospfd/ospf_vty.h b/ospfd/ospf_vty.h
new file mode 100644 (file)
index 0000000..4610638
--- /dev/null
@@ -0,0 +1,59 @@
+/* OSPF VTY interface.
+ * Copyright (C) 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _QUAGGA_OSPF_VTY_H
+#define _QUAGGA_OSPF_VTY_H
+
+/* Macros. */
+#define VTY_GET_OSPF_AREA_ID(V,F,STR)                                         \
+{                                                                             \
+  int retv;                                                                   \
+  retv = ospf_str2area_id ((STR), &(V), &(F));                                \
+  if (retv < 0)                                                               \
+    {                                                                         \
+      vty_out (vty, "%% Invalid OSPF area ID%s", VTY_NEWLINE);                \
+      return CMD_WARNING;                                                     \
+    }                                                                         \
+}
+
+#define VTY_GET_OSPF_AREA_ID_NO_BB(NAME,V,F,STR)                              \
+{                                                                             \
+  int retv;                                                                   \
+  retv = ospf_str2area_id ((STR), &(V), &(F));                                \
+  if (retv < 0)                                                               \
+    {                                                                         \
+      vty_out (vty, "%% Invalid OSPF area ID%s", VTY_NEWLINE);                \
+      return CMD_WARNING;                                                     \
+    }                                                                         \
+  if (OSPF_IS_AREA_ID_BACKBONE ((V)))                                         \
+    {                                                                         \
+      vty_out (vty, "%% You can't configure %s to backbone%s",                \
+               NAME, VTY_NEWLINE);                                            \
+    }                                                                         \
+}
+
+/* Prototypes. */
+extern void ospf_vty_init (void);
+extern void ospf_vty_show_init (void);
+extern int ospf_str2area_id (const char *, struct in_addr *, int *);
+extern void ospf_vty_clear_init (void);
+
+#endif /* _QUAGGA_OSPF_VTY_H */
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
new file mode 100644 (file)
index 0000000..60d9852
--- /dev/null
@@ -0,0 +1,1369 @@
+/*
+ * Zebra connect library for OSPFd
+ * Copyright (C) 1997, 98, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "table.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "filter.h"
+#include "plist.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#ifdef HAVE_SNMP
+#include "ospfd/ospf_snmp.h"
+#endif /* HAVE_SNMP */
+#include "ospfd/ospf_te.h"
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient = NULL;
+
+/* For registering threads. */
+extern struct thread_master *master;
+struct in_addr router_id_zebra;
+
+/* Router-id update message from zebra. */
+static int
+ospf_router_id_update_zebra (int command, struct zclient *zclient,
+                            zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct ospf *ospf;
+  struct prefix router_id;
+  zebra_router_id_update_read(zclient->ibuf,&router_id);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    {
+      char buf[128];
+      prefix2str(&router_id, buf, sizeof(buf));
+      zlog_debug("Zebra rcvd: router id update %s", buf);
+    }
+
+  router_id_zebra = router_id.u.prefix4;
+
+  ospf = ospf_lookup ();
+  
+  if (ospf != NULL)
+    ospf_router_id_update (ospf);
+  
+  return 0;
+}
+
+/* Inteface addition message from zebra. */
+static int
+ospf_interface_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug ("Zebra: interface add %s index %d flags %llx metric %d mtu %d",
+               ifp->name, ifp->ifindex, (unsigned long long)ifp->flags,
+               ifp->metric, ifp->mtu);
+
+  assert (ifp->info);
+
+  if (!OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), type))
+    {
+      SET_IF_PARAM (IF_DEF_PARAMS (ifp), type);
+      IF_DEF_PARAMS (ifp)->type = ospf_default_iftype(ifp);
+    }
+
+  ospf_if_update (NULL, ifp);
+
+#ifdef HAVE_SNMP
+  ospf_snmp_if_update (ifp);
+#endif /* HAVE_SNMP */
+
+  return 0;
+}
+
+static int
+ospf_interface_delete (int command, struct zclient *zclient,
+                       zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct stream *s;
+  struct route_node *rn;
+
+  s = zclient->ibuf;
+  /* zebra_interface_state_read() updates interface structure in iflist */
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (if_is_up (ifp))
+    zlog_warn ("Zebra: got delete of %s, but interface is still up",
+               ifp->name);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug
+      ("Zebra: interface delete %s index %d flags %llx metric %d mtu %d",
+       ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu);
+
+#ifdef HAVE_SNMP
+  ospf_snmp_if_delete (ifp);
+#endif /* HAVE_SNMP */
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    if (rn->info)
+      ospf_if_free ((struct ospf_interface *) rn->info);
+
+  ifp->ifindex = IFINDEX_INTERNAL;
+  return 0;
+}
+
+static struct interface *
+zebra_interface_if_lookup (struct stream *s, vrf_id_t vrf_id)
+{
+  char ifname_tmp[INTERFACE_NAMSIZ];
+
+  /* Read interface name. */
+  stream_get (ifname_tmp, s, INTERFACE_NAMSIZ);
+
+  /* And look it up. */
+  return if_lookup_by_name_len(ifname_tmp,
+                              strnlen(ifname_tmp, INTERFACE_NAMSIZ));
+}
+
+static int
+ospf_interface_state_up (int command, struct zclient *zclient,
+                         zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct ospf_interface *oi;
+  struct route_node *rn;
+
+  ifp = zebra_interface_if_lookup (zclient->ibuf, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  /* Interface is already up. */
+  if (if_is_operative (ifp))
+    {
+      /* Temporarily keep ifp values. */
+      struct interface if_tmp;
+      memcpy (&if_tmp, ifp, sizeof (struct interface));
+
+      zebra_interface_if_set_value (zclient->ibuf, ifp);
+
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+        zlog_debug ("Zebra: Interface[%s] state update.", ifp->name);
+
+      if (if_tmp.bandwidth != ifp->bandwidth)
+        {
+          if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+            zlog_debug ("Zebra: Interface[%s] bandwidth change %d -> %d.",
+                       ifp->name, if_tmp.bandwidth, ifp->bandwidth);
+
+          ospf_if_recalculate_output_cost (ifp);
+        }
+
+      if (if_tmp.mtu != ifp->mtu)
+        {
+          if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+            zlog_debug ("Zebra: Interface[%s] MTU change %u -> %u.",
+                       ifp->name, if_tmp.mtu, ifp->mtu);
+
+         /* Must reset the interface (simulate down/up) when MTU changes. */
+          ospf_if_reset(ifp);
+       }
+      return 0;
+    }
+
+  zebra_interface_if_set_value (zclient->ibuf, ifp);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug ("Zebra: Interface[%s] state change to up.", ifp->name);
+
+  for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn))
+    {
+      if ((oi = rn->info) == NULL)
+        continue;
+
+      ospf_if_up (oi);
+    }
+
+  return 0;
+}
+
+static int
+ospf_interface_state_down (int command, struct zclient *zclient,
+                           zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct ospf_interface *oi;
+  struct route_node *node;
+
+  ifp = zebra_interface_state_read (zclient->ibuf, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug ("Zebra: Interface[%s] state change to down.", ifp->name);
+
+  for (node = route_top (IF_OIFS (ifp)); node; node = route_next (node))
+    {
+      if ((oi = node->info) == NULL)
+        continue;
+      ospf_if_down (oi);
+    }
+
+  return 0;
+}
+
+static int
+ospf_interface_address_add (int command, struct zclient *zclient,
+                            zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+
+  c = zebra_interface_address_read (command, zclient->ibuf, vrf_id);
+
+  if (c == NULL)
+    return 0;
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    {
+      char buf[128];
+      prefix2str(c->address, buf, sizeof(buf));
+      zlog_debug("Zebra: interface %s address add %s", c->ifp->name, buf);
+    }
+
+  ospf_if_update (NULL, c->ifp);
+
+#ifdef HAVE_SNMP
+  ospf_snmp_if_update (c->ifp);
+#endif /* HAVE_SNMP */
+
+  return 0;
+}
+
+static int
+ospf_interface_address_delete (int command, struct zclient *zclient,
+                               zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct interface *ifp;
+  struct ospf_interface *oi;
+  struct route_node *rn;
+  struct prefix p;
+
+  c = zebra_interface_address_read (command, zclient->ibuf, vrf_id);
+
+  if (c == NULL)
+    return 0;
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    {
+      char buf[128];
+      prefix2str(c->address, buf, sizeof(buf));
+      zlog_debug("Zebra: interface %s address delete %s", c->ifp->name, buf);
+    }
+
+  ifp = c->ifp;
+  p = *c->address;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+
+  rn = route_node_lookup (IF_OIFS (ifp), &p);
+  if (!rn)
+    {
+      connected_free (c);
+      return 0;
+    }
+
+  assert (rn->info);
+  oi = rn->info;
+  route_unlock_node (rn);
+
+  /* Call interface hook functions to clean up */
+  ospf_if_free (oi);
+
+#ifdef HAVE_SNMP
+  ospf_snmp_if_update (c->ifp);
+#endif /* HAVE_SNMP */
+
+  connected_free (c);
+
+  return 0;
+}
+
+static int
+ospf_interface_link_params (int command, struct zclient *zclient,
+                        zebra_size_t length)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_link_params_read (zclient->ibuf);
+
+  if (ifp == NULL)
+    return 0;
+
+  /* Update TE TLV */
+  ospf_mpls_te_update_if (ifp);
+
+  return 0;
+}
+
+
+void
+ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  u_char message;
+  u_char distance;
+  u_char flags;
+  int psize;
+  struct stream *s;
+  struct ospf_path *path;
+  struct listnode *node;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT))
+    {
+      message = 0;
+      flags = 0;
+
+      /* OSPF pass nexthop and metric */
+      SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP);
+      SET_FLAG (message, ZAPI_MESSAGE_METRIC);
+
+      /* Distance value. */
+      distance = ospf_distance_apply (p, or);
+      if (distance)
+        SET_FLAG (message, ZAPI_MESSAGE_DISTANCE);
+
+      /* Check if path type is ASE */
+      if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) ||
+          (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) &&
+           (or->u.ext.tag > 0) && (or->u.ext.tag <= ROUTE_TAG_MAX))
+        SET_FLAG (message, ZAPI_MESSAGE_TAG);
+
+      /* Make packet. */
+      s = zclient->obuf;
+      stream_reset (s);
+
+      /* Put command, type, flags, message. */
+      zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT);
+      stream_putc (s, ZEBRA_ROUTE_OSPF);
+      stream_putc (s, flags);
+      stream_putc (s, message);
+      stream_putw (s, SAFI_UNICAST);
+
+      /* Put prefix information. */
+      psize = PSIZE (p->prefixlen);
+      stream_putc (s, p->prefixlen);
+      stream_write (s, (u_char *) & p->prefix, psize);
+
+      /* Nexthop count. */
+      stream_putc (s, or->paths->count);
+
+      /* Nexthop, ifindex, distance and metric information. */
+      for (ALL_LIST_ELEMENTS_RO (or->paths, node, path))
+        {
+          if (path->nexthop.s_addr != INADDR_ANY &&
+             path->ifindex != 0)
+            {
+              stream_putc (s, ZEBRA_NEXTHOP_IPV4_IFINDEX);
+              stream_put_in_addr (s, &path->nexthop);
+             stream_putl (s, path->ifindex);
+            }
+          else if (path->nexthop.s_addr != INADDR_ANY)
+            {
+              stream_putc (s, ZEBRA_NEXTHOP_IPV4);
+              stream_put_in_addr (s, &path->nexthop);
+            }
+          else
+            {
+              stream_putc (s, ZEBRA_NEXTHOP_IFINDEX);
+              if (path->ifindex)
+                stream_putl (s, path->ifindex);
+              else
+                stream_putl (s, 0);
+            }
+
+          if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+            {
+             char buf[2][INET_ADDRSTRLEN];
+             zlog_debug("Zebra: Route add %s/%d nexthop %s",
+                        inet_ntop(AF_INET, &p->prefix,
+                                  buf[0], sizeof(buf[0])),
+                        p->prefixlen,
+                        inet_ntop(AF_INET, &path->nexthop,
+                                  buf[1], sizeof(buf[1])));
+            }
+        }
+
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+        stream_putc (s, distance);
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+        {
+          if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL)
+            stream_putl (s, or->cost + or->u.ext.type2_cost);
+          else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)
+            stream_putl (s, or->u.ext.type2_cost);
+          else
+            stream_putl (s, or->cost);
+        }
+
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG))
+         stream_putl (s, or->u.ext.tag);
+
+      stream_putw_at (s, 0, stream_get_endp (s));
+
+      zclient_send_message(zclient);
+    }
+}
+
+void
+ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  u_char message;
+  u_char distance;
+  u_char flags;
+  int psize;
+  struct stream *s;
+  struct ospf_path *path;
+  struct listnode *node;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT))
+    {
+      message = 0;
+      flags = 0;
+      /* Distance value. */
+      distance = ospf_distance_apply (p, or);
+      /* Make packet. */
+      s = zclient->obuf;
+      stream_reset (s);
+
+      /* Put command, type, flags, message. */
+      zclient_create_header (s, ZEBRA_IPV4_ROUTE_DELETE, VRF_DEFAULT);
+      stream_putc (s, ZEBRA_ROUTE_OSPF);
+      stream_putc (s, flags);
+      stream_putc (s, message);
+      stream_putw (s, SAFI_UNICAST);
+
+      /* Put prefix information. */
+      psize = PSIZE (p->prefixlen);
+      stream_putc (s, p->prefixlen);
+      stream_write (s, (u_char *) & p->prefix, psize);
+
+      /* Nexthop count. */
+      stream_putc (s, or->paths->count);
+
+      /* Nexthop, ifindex, distance and metric information. */
+      for (ALL_LIST_ELEMENTS_RO (or->paths, node, path))
+       {
+         if (path->nexthop.s_addr != INADDR_ANY &&
+             path->ifindex != 0)
+           {
+             stream_putc (s, ZEBRA_NEXTHOP_IPV4_IFINDEX);
+             stream_put_in_addr (s, &path->nexthop);
+             stream_putl (s, path->ifindex);
+           }
+         else if (path->nexthop.s_addr != INADDR_ANY)
+           {
+             stream_putc (s, ZEBRA_NEXTHOP_IPV4);
+             stream_put_in_addr (s, &path->nexthop);
+           }
+         else
+           {
+             stream_putc (s, ZEBRA_NEXTHOP_IFINDEX);
+             stream_putl (s, path->ifindex);
+           }
+
+         if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+           {
+             char buf[2][INET_ADDRSTRLEN];
+             zlog_debug("Zebra: Route delete %s/%d nexthop %s",
+                        inet_ntop(AF_INET, &p->prefix,
+                                  buf[0], sizeof(buf[0])),
+                        p->prefixlen,
+                        inet_ntop(AF_INET, &path->nexthop,
+                                  buf[1], sizeof(buf[1])));
+           }
+       }
+
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+       stream_putc (s, distance);
+      if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+       {
+         if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL)
+           stream_putl (s, or->cost + or->u.ext.type2_cost);
+         else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)
+           stream_putl (s, or->u.ext.type2_cost);
+         else
+           stream_putl (s, or->cost);
+       }
+
+      stream_putw_at (s, 0, stream_get_endp (s));
+
+      zclient_send_message(zclient);
+    }
+}
+
+void
+ospf_zebra_add_discard (struct prefix_ipv4 *p)
+{
+  struct zapi_ipv4 api;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT))
+    {
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_OSPF;
+      api.flags = ZEBRA_FLAG_BLACKHOLE;
+      api.message = 0;
+      api.safi = SAFI_UNICAST;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      api.nexthop_num = 0;
+      api.ifindex_num = 0;
+      api.tag = 0;
+
+      zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, p, &api);
+
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+        zlog_debug ("Zebra: Route add discard %s/%d",
+                   inet_ntoa (p->prefix), p->prefixlen);
+    }
+}
+
+void
+ospf_zebra_delete_discard (struct prefix_ipv4 *p)
+{
+  struct zapi_ipv4 api;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT))
+    {
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_OSPF;
+      api.flags = ZEBRA_FLAG_BLACKHOLE;
+      api.message = 0;
+      api.safi = SAFI_UNICAST;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      api.nexthop_num = 0;
+      api.ifindex_num = 0;
+      api.tag = 0;
+
+      zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, p, &api);
+
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+        zlog_debug ("Zebra: Route delete discard %s/%d",
+                   inet_ntoa (p->prefix), p->prefixlen);
+
+    }
+}
+
+int
+ospf_is_type_redistributed (int type)
+{
+  return (DEFAULT_ROUTE_TYPE (type)) ?
+    vrf_bitmap_check (zclient->default_information, VRF_DEFAULT) : \
+    vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT);
+}
+
+int
+ospf_redistribute_set (struct ospf *ospf, int type, int mtype, int mvalue)
+{
+  int force = 0;
+
+  if (ospf_is_type_redistributed (type))
+    {
+      if (mtype != ospf->dmetric[type].type)
+        {
+          ospf->dmetric[type].type = mtype;
+          force = LSA_REFRESH_FORCE;
+        }
+      if (mvalue != ospf->dmetric[type].value)
+        {
+          ospf->dmetric[type].value = mvalue;
+          force = LSA_REFRESH_FORCE;
+        }
+
+      ospf_external_lsa_refresh_type (ospf, type, force);
+
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+        zlog_debug ("Redistribute[%s]: Refresh  Type[%d], Metric[%d]",
+                   ospf_redist_string(type),
+                   metric_type (ospf, type), metric_value (ospf, type));
+
+      return CMD_SUCCESS;
+    }
+
+  ospf->dmetric[type].type = mtype;
+  ospf->dmetric[type].value = mvalue;
+
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+    zlog_debug ("Redistribute[%s]: Start  Type[%d], Metric[%d]",
+               ospf_redist_string(type),
+               metric_type (ospf, type), metric_value (ospf, type));
+
+  ospf_asbr_status_update (ospf, ++ospf->redistribute);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf_redistribute_unset (struct ospf *ospf, int type)
+{
+  if (type == zclient->redist_default)
+    return CMD_SUCCESS;
+
+  if (!ospf_is_type_redistributed (type))
+    return CMD_SUCCESS;
+
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, VRF_DEFAULT);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+    zlog_debug ("Redistribute[%s]: Stop",
+               ospf_redist_string(type));
+
+  ospf->dmetric[type].type = -1;
+  ospf->dmetric[type].value = -1;
+
+  /* Remove the routes from OSPF table. */
+  ospf_redistribute_withdraw (ospf, type);
+
+  ospf_asbr_status_update (ospf, --ospf->redistribute);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf_redistribute_default_set (struct ospf *ospf, int originate,
+                               int mtype, int mvalue)
+{
+  ospf->default_originate = originate;
+  ospf->dmetric[DEFAULT_ROUTE].type = mtype;
+  ospf->dmetric[DEFAULT_ROUTE].value = mvalue;
+
+  if (ospf_is_type_redistributed (DEFAULT_ROUTE))
+    {
+      /* if ospf->default_originate changes value, is calling
+        ospf_external_lsa_refresh_default sufficient to implement
+        the change? */
+      ospf_external_lsa_refresh_default (ospf);
+
+      if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+        zlog_debug ("Redistribute[%s]: Refresh  Type[%d], Metric[%d]",
+                   ospf_redist_string(DEFAULT_ROUTE),
+                   metric_type (ospf, DEFAULT_ROUTE),
+                   metric_value (ospf, DEFAULT_ROUTE));
+      return CMD_SUCCESS;
+    }
+
+  zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient,
+                                VRF_DEFAULT);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+    zlog_debug ("Redistribute[DEFAULT]: Start  Type[%d], Metric[%d]",
+               metric_type (ospf, DEFAULT_ROUTE),
+               metric_value (ospf, DEFAULT_ROUTE));
+
+  if (ospf->router_id.s_addr == 0)
+    ospf->external_origin |= (1 << DEFAULT_ROUTE);
+  else
+    thread_add_timer (master, ospf_default_originate_timer, ospf, 1);
+
+  ospf_asbr_status_update (ospf, ++ospf->redistribute);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf_redistribute_default_unset (struct ospf *ospf)
+{
+  if (!ospf_is_type_redistributed (DEFAULT_ROUTE))
+    return CMD_SUCCESS;
+
+  ospf->default_originate = DEFAULT_ORIGINATE_NONE;
+  ospf->dmetric[DEFAULT_ROUTE].type = -1;
+  ospf->dmetric[DEFAULT_ROUTE].value = -1;
+
+  zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient,
+                                VRF_DEFAULT);
+
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+    zlog_debug ("Redistribute[DEFAULT]: Stop");
+
+  ospf_asbr_status_update (ospf, --ospf->redistribute);
+
+  return CMD_SUCCESS;
+}
+
+static int
+ospf_external_lsa_originate_check (struct ospf *ospf,
+                                   struct external_info *ei)
+{
+  /* If prefix is multicast, then do not originate LSA. */
+  if (IN_MULTICAST (htonl (ei->p.prefix.s_addr)))
+    {
+      zlog_info ("LSA[Type5:%s]: Not originate AS-external-LSA, "
+                 "Prefix belongs multicast", inet_ntoa (ei->p.prefix));
+      return 0;
+    }
+
+  /* Take care of default-originate. */
+  if (is_prefix_default (&ei->p))
+    if (ospf->default_originate == DEFAULT_ORIGINATE_NONE)
+      {
+        zlog_info ("LSA[Type5:0.0.0.0]: Not originate AS-external-LSA "
+                   "for default");
+        return 0;
+      }
+
+  return 1;
+}
+
+/* If connected prefix is OSPF enable interface, then do not announce. */
+int
+ospf_distribute_check_connected (struct ospf *ospf, struct external_info *ei)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+      if (prefix_match (oi->address, (struct prefix *) &ei->p))
+          return 0;
+  return 1;
+}
+
+/* return 1 if external LSA must be originated, 0 otherwise */
+int
+ospf_redistribute_check (struct ospf *ospf,
+                         struct external_info *ei, int *changed)
+{
+  struct route_map_set_values save_values;
+  struct prefix_ipv4 *p = &ei->p;
+  u_char type = is_prefix_default (&ei->p) ? DEFAULT_ROUTE : ei->type;
+
+  if (changed)
+    *changed = 0;
+
+  if (!ospf_external_lsa_originate_check (ospf, ei))
+    return 0;
+
+  /* Take care connected route. */
+  if (type == ZEBRA_ROUTE_CONNECT &&
+      !ospf_distribute_check_connected (ospf, ei))
+    return 0;
+
+  if (!DEFAULT_ROUTE_TYPE (type) && DISTRIBUTE_NAME (ospf, type))
+    /* distirbute-list exists, but access-list may not? */
+    if (DISTRIBUTE_LIST (ospf, type))
+      if (access_list_apply (DISTRIBUTE_LIST (ospf, type), p) == FILTER_DENY)
+        {
+          if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+            zlog_debug ("Redistribute[%s]: %s/%d filtered by ditribute-list.",
+                       ospf_redist_string(type),
+                       inet_ntoa (p->prefix), p->prefixlen);
+          return 0;
+        }
+
+  save_values = ei->route_map_set;
+  ospf_reset_route_map_set_values (&ei->route_map_set);
+
+  /* apply route-map if needed */
+  if (ROUTEMAP_NAME (ospf, type))
+    {
+      int ret;
+
+      ret = route_map_apply (ROUTEMAP (ospf, type), (struct prefix *) p,
+                             RMAP_OSPF, ei);
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          ei->route_map_set = save_values;
+          if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE))
+            zlog_debug ("Redistribute[%s]: %s/%d filtered by route-map.",
+                       ospf_redist_string(type),
+                       inet_ntoa (p->prefix), p->prefixlen);
+          return 0;
+        }
+
+      /* check if 'route-map set' changed something */
+      if (changed)
+        *changed = !ospf_route_map_set_compare (&ei->route_map_set,
+                                                &save_values);
+    }
+
+  return 1;
+}
+
+/* OSPF route-map set for redistribution */
+void
+ospf_routemap_set (struct ospf *ospf, int type, const char *name)
+{
+  if (ROUTEMAP_NAME (ospf, type))
+    free (ROUTEMAP_NAME (ospf, type));
+
+  ROUTEMAP_NAME (ospf, type) = strdup (name);
+  ROUTEMAP (ospf, type) = route_map_lookup_by_name (name);
+}
+
+void
+ospf_routemap_unset (struct ospf *ospf, int type)
+{
+  if (ROUTEMAP_NAME (ospf, type))
+    free (ROUTEMAP_NAME (ospf, type));
+
+  ROUTEMAP_NAME (ospf, type) = NULL;
+  ROUTEMAP (ospf, type) = NULL;
+}
+
+/* Zebra route add and delete treatment. */
+static int
+ospf_zebra_read_ipv4 (int command, struct zclient *zclient,
+                      zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv4 api;
+  unsigned long ifindex;
+  struct in_addr nexthop;
+  struct prefix_ipv4 p;
+  struct external_info *ei;
+  struct ospf *ospf;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  nexthop.s_addr = 0;
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  if (IPV4_NET127(ntohl(p.prefix.s_addr)))
+    return 0;
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      nexthop.s_addr = stream_get_ipv4 (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      /* XXX assert(api.ifindex_num == 1); */
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return 0;
+
+  if (command == ZEBRA_IPV4_ROUTE_ADD)
+    {
+      /* XXX|HACK|TODO|FIXME:
+       * Maybe we should ignore reject/blackhole routes? Testing shows that
+       * there is no problems though and this is only way to "summarize"
+       * routes in ASBR at the moment. Maybe we need just a better generalised
+       * solution for these types?
+       *
+       * if ( CHECK_FLAG (api.flags, ZEBRA_FLAG_BLACKHOLE)
+       *     || CHECK_FLAG (api.flags, ZEBRA_FLAG_REJECT))
+       * return 0;
+       */
+
+      /* Protocol tag overwrites all other tag value send by zebra */
+      if (ospf->dtag[api.type] > 0)
+       api.tag = ospf->dtag[api.type];
+
+      ei = ospf_external_info_add (api.type, p, ifindex, nexthop, api.tag);
+
+      if (ospf->router_id.s_addr == 0)
+        /* Set flags to generate AS-external-LSA originate event
+           for each redistributed protocols later. */
+        ospf->external_origin |= (1 << api.type);
+      else
+        {
+          if (ei)
+            {
+              if (is_prefix_default (&p))
+                ospf_external_lsa_refresh_default (ospf);
+              else
+                {
+                  struct ospf_lsa *current;
+
+                  current = ospf_external_info_find_lsa (ospf, &ei->p);
+                  if (!current)
+                    ospf_external_lsa_originate (ospf, ei);
+                  else if (IS_LSA_MAXAGE (current))
+                    ospf_external_lsa_refresh (ospf, current,
+                                               ei, LSA_REFRESH_FORCE);
+                  else
+                    zlog_warn ("ospf_zebra_read_ipv4() : %s already exists",
+                               inet_ntoa (p.prefix));
+                }
+            }
+        }
+    }
+  else                          /* if (command == ZEBRA_IPV4_ROUTE_DELETE) */
+    {
+      ospf_external_info_delete (api.type, p);
+      if (is_prefix_default (&p))
+        ospf_external_lsa_refresh_default (ospf);
+      else
+        ospf_external_lsa_flush (ospf, api.type, &p, ifindex /*, nexthop */);
+    }
+
+  return 0;
+}
+
+
+int
+ospf_distribute_list_out_set (struct ospf *ospf, int type, const char *name)
+{
+  /* Lookup access-list for distribute-list. */
+  DISTRIBUTE_LIST (ospf, type) = access_list_lookup (AFI_IP, name);
+
+  /* Clear previous distribute-name. */
+  if (DISTRIBUTE_NAME (ospf, type))
+    free (DISTRIBUTE_NAME (ospf, type));
+
+  /* Set distribute-name. */
+  DISTRIBUTE_NAME (ospf, type) = strdup (name);
+
+  /* If access-list have been set, schedule update timer. */
+  if (DISTRIBUTE_LIST (ospf, type))
+    ospf_distribute_list_update (ospf, type);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf_distribute_list_out_unset (struct ospf *ospf, int type, const char *name)
+{
+  /* Schedule update timer. */
+  if (DISTRIBUTE_LIST (ospf, type))
+    ospf_distribute_list_update (ospf, type);
+
+  /* Unset distribute-list. */
+  DISTRIBUTE_LIST (ospf, type) = NULL;
+
+  /* Clear distribute-name. */
+  if (DISTRIBUTE_NAME (ospf, type))
+    free (DISTRIBUTE_NAME (ospf, type));
+
+  DISTRIBUTE_NAME (ospf, type) = NULL;
+
+  return CMD_SUCCESS;
+}
+
+/* distribute-list update timer. */
+static int
+ospf_distribute_list_update_timer (struct thread *thread)
+{
+  struct route_node *rn;
+  struct external_info *ei;
+  struct route_table *rt;
+  struct ospf_lsa *lsa;
+  int type, default_refresh = 0;
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return 0;
+
+  ospf->t_distribute_update = NULL;
+
+  zlog_info ("Zebra[Redistribute]: distribute-list update timer fired!");
+
+  /* foreach all external info. */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      rt = EXTERNAL_INFO (type);
+      if (!rt)
+       continue;
+      for (rn = route_top (rt); rn; rn = route_next (rn))
+       if ((ei = rn->info) != NULL)
+         {
+           if (is_prefix_default (&ei->p))
+             default_refresh = 1;
+           else if ((lsa = ospf_external_info_find_lsa (ospf, &ei->p)))
+             ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_IF_CHANGED);
+           else
+             ospf_external_lsa_originate (ospf, ei);
+         }
+    }
+  if (default_refresh)
+    ospf_external_lsa_refresh_default (ospf);
+  return 0;
+}
+
+/* Update distribute-list and set timer to apply access-list. */
+void
+ospf_distribute_list_update (struct ospf *ospf, uintptr_t type)
+{
+  struct route_table *rt;
+
+  /* External info does not exist. */
+  if (!(rt = EXTERNAL_INFO (type)))
+    return;
+
+  /* If exists previously invoked thread, then let it continue. */
+  if (ospf->t_distribute_update)
+    return;
+
+  /* Set timer. */
+  ospf->t_distribute_update =
+    thread_add_timer_msec (master, ospf_distribute_list_update_timer,
+                      (void *) type, ospf->min_ls_interval);
+}
+
+/* If access-list is updated, apply some check. */
+static void
+ospf_filter_update (struct access_list *access)
+{
+  struct ospf *ospf;
+  int type;
+  int abr_inv = 0;
+  struct ospf_area *area;
+  struct listnode *node;
+
+  /* If OSPF instatnce does not exist, return right now. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return;
+
+  /* Update distribute-list, and apply filter. */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      if (ROUTEMAP (ospf, type) != NULL)
+        {
+          /* if route-map is not NULL it may be using this access list */
+          ospf_distribute_list_update (ospf, type);
+          continue;
+        }
+
+      /* There is place for route-map for default-information (ZEBRA_ROUTE_MAX),
+       * but no distribute list. */
+      if (type == ZEBRA_ROUTE_MAX)
+       break;
+
+      if (DISTRIBUTE_NAME (ospf, type))
+        {
+          /* Keep old access-list for distribute-list. */
+          struct access_list *old = DISTRIBUTE_LIST (ospf, type);
+
+          /* Update access-list for distribute-list. */
+          DISTRIBUTE_LIST (ospf, type) =
+            access_list_lookup (AFI_IP, DISTRIBUTE_NAME (ospf, type));
+
+          /* No update for this distribute type. */
+          if (old == NULL && DISTRIBUTE_LIST (ospf, type) == NULL)
+            continue;
+
+          /* Schedule distribute-list update timer. */
+          if (DISTRIBUTE_LIST (ospf, type) == NULL ||
+              strcmp (DISTRIBUTE_NAME (ospf, type), access->name) == 0)
+            ospf_distribute_list_update (ospf, type);
+        }
+    }
+
+  /* Update Area access-list. */
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      if (EXPORT_NAME (area))
+        {
+          EXPORT_LIST (area) = NULL;
+          abr_inv++;
+        }
+
+      if (IMPORT_NAME (area))
+        {
+          IMPORT_LIST (area) = NULL;
+          abr_inv++;
+        }
+    }
+
+  /* Schedule ABR tasks -- this will be changed -- takada. */
+  if (IS_OSPF_ABR (ospf) && abr_inv)
+    ospf_schedule_abr_task (ospf);
+}
+
+/* If prefix-list is updated, do some updates. */
+void
+ospf_prefix_list_update (struct prefix_list *plist)
+{
+  struct ospf *ospf;
+  int type;
+  int abr_inv = 0;
+  struct ospf_area *area;
+  struct listnode *node;
+
+  /* If OSPF instatnce does not exist, return right now. */
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return;
+
+  /* Update all route-maps which are used as redistribution filters.
+   * They might use prefix-list.
+   */
+  for (type = 0; type <= ZEBRA_ROUTE_MAX; type++)
+    {
+      if (ROUTEMAP (ospf, type) != NULL)
+        {
+          /* If route-map is not NULL it may be using this prefix list */
+          ospf_distribute_list_update (ospf, type);
+          continue;
+        }
+    }
+
+  /* Update area filter-lists. */
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    {
+      /* Update filter-list in. */
+      if (PREFIX_NAME_IN (area))
+        if (strcmp (PREFIX_NAME_IN (area), prefix_list_name (plist)) == 0)
+          {
+            PREFIX_LIST_IN (area) =
+              prefix_list_lookup (AFI_IP, PREFIX_NAME_IN (area));
+            abr_inv++;
+          }
+
+      /* Update filter-list out. */
+      if (PREFIX_NAME_OUT (area))
+        if (strcmp (PREFIX_NAME_OUT (area), prefix_list_name (plist)) == 0)
+          {
+            PREFIX_LIST_IN (area) =
+              prefix_list_lookup (AFI_IP, PREFIX_NAME_OUT (area));
+            abr_inv++;
+          }
+    }
+
+  /* Schedule ABR task. */
+  if (IS_OSPF_ABR (ospf) && abr_inv)
+    ospf_schedule_abr_task (ospf);
+}
+
+static struct ospf_distance *
+ospf_distance_new (void)
+{
+  return XCALLOC (MTYPE_OSPF_DISTANCE, sizeof (struct ospf_distance));
+}
+
+static void
+ospf_distance_free (struct ospf_distance *odistance)
+{
+  XFREE (MTYPE_OSPF_DISTANCE, odistance);
+}
+
+int
+ospf_distance_set (struct vty *vty, struct ospf *ospf, 
+                   const char *distance_str,
+                   const char *ip_str, 
+                   const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  u_char distance;
+  struct route_node *rn;
+  struct ospf_distance *odistance;
+
+  ret = str2prefix_ipv4 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  /* Get OSPF distance node. */
+  rn = route_node_get (ospf->distance_table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      odistance = rn->info;
+      route_unlock_node (rn);
+    }
+  else
+    {
+      odistance = ospf_distance_new ();
+      rn->info = odistance;
+    }
+
+  /* Set distance value. */
+  odistance->distance = distance;
+
+  /* Reset access-list configuration. */
+  if (odistance->access_list)
+    {
+      free (odistance->access_list);
+      odistance->access_list = NULL;
+    }
+  if (access_list_str)
+    odistance->access_list = strdup (access_list_str);
+
+  return CMD_SUCCESS;
+}
+
+int
+ospf_distance_unset (struct vty *vty, struct ospf *ospf, 
+                     const char *distance_str,
+                     const char *ip_str, char 
+                     const *access_list_str)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct ospf_distance *odistance;
+
+  ret = str2prefix_ipv4 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rn = route_node_lookup (ospf->distance_table, (struct prefix *) &p);
+  if (!rn)
+    {
+      vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  odistance = rn->info;
+
+  if (odistance->access_list)
+    free (odistance->access_list);
+  ospf_distance_free (odistance);
+
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+void
+ospf_distance_reset (struct ospf *ospf)
+{
+  struct route_node *rn;
+  struct ospf_distance *odistance;
+
+  for (rn = route_top (ospf->distance_table); rn; rn = route_next (rn))
+    if ((odistance = rn->info) != NULL)
+      {
+        if (odistance->access_list)
+          free (odistance->access_list);
+        ospf_distance_free (odistance);
+        rn->info = NULL;
+        route_unlock_node (rn);
+      }
+}
+
+u_char
+ospf_distance_apply (struct prefix_ipv4 *p, struct ospf_route *or)
+{
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    return 0;
+
+  if (ospf->distance_intra)
+    if (or->path_type == OSPF_PATH_INTRA_AREA)
+      return ospf->distance_intra;
+
+  if (ospf->distance_inter)
+    if (or->path_type == OSPF_PATH_INTER_AREA)
+      return ospf->distance_inter;
+
+  if (ospf->distance_external)
+    if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL
+        || or->path_type == OSPF_PATH_TYPE2_EXTERNAL)
+      return ospf->distance_external;
+
+  if (ospf->distance_all)
+    return ospf->distance_all;
+
+  return 0;
+}
+
+static void
+ospf_zebra_connected (struct zclient *zclient)
+{
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+void
+ospf_zebra_init (struct thread_master *master)
+{
+  /* Allocate zebra structure. */
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_OSPF);
+  zclient->zebra_connected = ospf_zebra_connected;
+  zclient->router_id_update = ospf_router_id_update_zebra;
+  zclient->interface_add = ospf_interface_add;
+  zclient->interface_delete = ospf_interface_delete;
+  zclient->interface_up = ospf_interface_state_up;
+  zclient->interface_down = ospf_interface_state_down;
+  zclient->interface_address_add = ospf_interface_address_add;
+  zclient->interface_address_delete = ospf_interface_address_delete;
+  zclient->interface_link_params = ospf_interface_link_params;
+
+  zclient->ipv4_route_add = ospf_zebra_read_ipv4;
+  zclient->ipv4_route_delete = ospf_zebra_read_ipv4;
+
+  access_list_add_hook (ospf_filter_update);
+  access_list_delete_hook (ospf_filter_update);
+  prefix_list_add_hook (ospf_prefix_list_update);
+  prefix_list_delete_hook (ospf_prefix_list_update);
+}
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
new file mode 100644 (file)
index 0000000..32a0271
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Zebra connect library for OSPFd
+ * Copyright (C) 1997, 98, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ */
+
+#ifndef _ZEBRA_OSPF_ZEBRA_H
+#define _ZEBRA_OSPF_ZEBRA_H
+
+#include "vty.h"
+
+#define EXTERNAL_METRIC_TYPE_1      0
+#define EXTERNAL_METRIC_TYPE_2      1
+
+#define DEFAULT_ROUTE              ZEBRA_ROUTE_MAX
+#define DEFAULT_ROUTE_TYPE(T) ((T) == DEFAULT_ROUTE)
+
+/* OSPF distance. */
+struct ospf_distance
+{
+  /* Distance value for the IP source prefix. */
+  u_char distance;
+
+  /* Name of the access-list to be matched. */
+  char *access_list;
+};
+
+/* Prototypes */
+extern void ospf_zclient_start (void);
+
+extern void ospf_zebra_add (struct prefix_ipv4 *, struct ospf_route *);
+extern void ospf_zebra_delete (struct prefix_ipv4 *, struct ospf_route *);
+
+extern void ospf_zebra_add_discard (struct prefix_ipv4 *);
+extern void ospf_zebra_delete_discard (struct prefix_ipv4 *);
+
+extern int ospf_redistribute_check (struct ospf *, struct external_info *,
+                                   int *);
+extern int ospf_distribute_check_connected (struct ospf *,
+                                           struct external_info *);
+extern void ospf_distribute_list_update (struct ospf *, uintptr_t);
+
+extern int ospf_is_type_redistributed (int);
+extern void ospf_distance_reset (struct ospf *);
+extern u_char ospf_distance_apply (struct prefix_ipv4 *, struct ospf_route *);
+
+extern int ospf_redistribute_set (struct ospf *, int, int, int);
+extern int ospf_redistribute_unset (struct ospf *, int);
+extern int ospf_redistribute_default_set (struct ospf *, int, int, int);
+extern int ospf_redistribute_default_unset (struct ospf *);
+extern int ospf_distribute_list_out_set (struct ospf *, int, const char *);
+extern int ospf_distribute_list_out_unset (struct ospf *, int, const char *);
+extern void ospf_routemap_set (struct ospf *, int, const char *);
+extern void ospf_routemap_unset (struct ospf *, int);
+extern int ospf_distance_set (struct vty *, struct ospf *, const char *,
+                             const char *, const char *);
+extern int ospf_distance_unset (struct vty *, struct ospf *, const char *,
+                               const char *, const char *);
+extern void ospf_zebra_init (struct thread_master *);
+
+#endif /* _ZEBRA_OSPF_ZEBRA_H */
+
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
new file mode 100644 (file)
index 0000000..98d6d77
--- /dev/null
@@ -0,0 +1,1796 @@
+/* OSPF version 2 daemon program.
+   Copyright (C) 1999, 2000 Toshiaki Takada
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "table.h"
+#include "if.h"
+#include "memory.h"
+#include "stream.h"
+#include "log.h"
+#include "sockunion.h"          /* for inet_aton () */
+#include "zclient.h"
+#include "plist.h"
+#include "sockopt.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+
+
+
+/* OSPF process wide configuration. */
+static struct ospf_master ospf_master;
+
+/* OSPF process wide configuration pointer to export. */
+struct ospf_master *om;
+
+extern struct zclient *zclient;
+extern struct in_addr router_id_zebra;
+
+
+static void ospf_remove_vls_through_area (struct ospf *, struct ospf_area *);
+static void ospf_network_free (struct ospf *, struct ospf_network *);
+static void ospf_area_free (struct ospf_area *);
+static void ospf_network_run (struct prefix *, struct ospf_area *);
+static void ospf_network_run_interface (struct ospf *, struct interface *, 
+                                        struct prefix *, struct ospf_area *);
+static void ospf_network_run_subnet (struct ospf *, struct connected *, 
+                                     struct prefix *, struct ospf_area *);
+static int ospf_network_match_iface (const struct connected *,
+                                    const struct prefix *);
+static void ospf_finish_final (struct ospf *);
+
+#define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1
+
+void
+ospf_router_id_update (struct ospf *ospf)
+{
+  struct in_addr router_id, router_id_old;
+  struct ospf_interface *oi;
+  struct interface *ifp;
+  struct listnode *node;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Router-ID[OLD:%s]: Update", inet_ntoa (ospf->router_id));
+
+  router_id_old = ospf->router_id;
+
+  /* Select the router ID based on these priorities:
+       1. Statically assigned router ID is always the first choice.
+       2. If there is no statically assigned router ID, then try to stick
+          with the most recent value, since changing router ID's is very
+         disruptive.
+       3. Last choice: just go with whatever the zebra daemon recommends.
+  */
+  if (ospf->router_id_static.s_addr != 0)
+    router_id = ospf->router_id_static;
+  else if (ospf->router_id.s_addr != 0)
+    router_id = ospf->router_id;
+  else
+    router_id = router_id_zebra;
+
+  ospf->router_id = router_id;
+  
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Router-ID[NEW:%s]: Update", inet_ntoa (ospf->router_id));
+
+  if (!IPV4_ADDR_SAME (&router_id_old, &router_id))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+       {
+         /* Some nbrs are identified by router_id, these needs
+          * to be rebuilt. Possible optimization would be to do
+          * oi->nbr_self->router_id = router_id for
+          * !(virtual | ptop) links
+          */
+         ospf_nbr_self_reset (oi);
+       }
+
+      /* If AS-external-LSA is queued, then flush those LSAs. */
+      if (router_id_old.s_addr == 0 && ospf->external_origin)
+       {
+         int type;
+         /* Originate each redistributed external route. */
+         for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
+           if (ospf->external_origin & (1 << type))
+             thread_add_event (master, ospf_external_lsa_originate_timer,
+                               ospf, type);
+         /* Originate Deafult. */
+         if (ospf->external_origin & (1 << ZEBRA_ROUTE_MAX))
+           thread_add_event (master, ospf_default_originate_timer, ospf, 0);
+
+         ospf->external_origin = 0;
+       }
+
+      /* update router-lsa's for each area */
+      ospf_router_lsa_update (ospf);
+      
+      /* update ospf_interface's */
+      for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
+        ospf_if_update (ospf, ifp);
+    }
+}
+
+/* For OSPF area sort by area id. */
+static int
+ospf_area_id_cmp (struct ospf_area *a1, struct ospf_area *a2)
+{
+  if (ntohl (a1->area_id.s_addr) > ntohl (a2->area_id.s_addr))
+    return 1;
+  if (ntohl (a1->area_id.s_addr) < ntohl (a2->area_id.s_addr))
+    return -1;
+  return 0;
+}
+
+/* Allocate new ospf structure. */
+static struct ospf *
+ospf_new (void)
+{
+  int i;
+
+  struct ospf *new = XCALLOC (MTYPE_OSPF_TOP, sizeof (struct ospf));
+
+  new->router_id.s_addr = htonl (0);
+  new->router_id_static.s_addr = htonl (0);
+
+  new->abr_type = OSPF_ABR_DEFAULT;
+  new->oiflist = list_new ();
+  new->vlinks = list_new ();
+  new->areas = list_new ();
+  new->areas->cmp = (int (*)(void *, void *)) ospf_area_id_cmp;
+  new->networks = route_table_init ();
+  new->nbr_nbma = route_table_init ();
+
+  new->lsdb = ospf_lsdb_new ();
+
+  new->default_originate = DEFAULT_ORIGINATE_NONE;
+
+  new->passive_interface_default = OSPF_IF_ACTIVE;
+  
+  new->new_external_route = route_table_init ();
+  new->old_external_route = route_table_init ();
+  new->external_lsas = route_table_init ();
+  
+  new->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED;
+  new->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED;
+  new->stub_router_admin_set     = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET;
+
+  /* Distribute parameter init. */
+  for (i = 0; i <= ZEBRA_ROUTE_MAX; i++)
+    {
+      new->dmetric[i].type = -1;
+      new->dmetric[i].value = -1;
+      new->dtag[i] = 0;
+    }
+  new->default_metric = -1;
+  new->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH;
+
+  /* LSA timers */
+  new->min_ls_interval = OSPF_MIN_LS_INTERVAL;
+  new->min_ls_arrival = OSPF_MIN_LS_ARRIVAL;
+
+  /* SPF timer value init. */
+  new->spf_delay = OSPF_SPF_DELAY_DEFAULT;
+  new->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT;
+  new->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT;
+  new->spf_hold_multiplier = 1;
+
+  /* MaxAge init. */
+  new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT;
+  new->maxage_lsa = route_table_init();
+  new->t_maxage_walker =
+    thread_add_timer (master, ospf_lsa_maxage_walker,
+                      new, OSPF_LSA_MAXAGE_CHECK_INTERVAL);
+
+  /* Distance table init. */
+  new->distance_table = route_table_init ();
+
+  new->lsa_refresh_queue.index = 0;
+  new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT;
+  new->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker,
+                                          new, new->lsa_refresh_interval);
+  new->lsa_refresher_started = quagga_time (NULL);
+
+  if ((new->fd = ospf_sock_init()) < 0)
+    {
+      zlog_err("ospf_new: fatal error: ospf_sock_init was unable to open "
+              "a socket");
+      exit(1);
+    }
+  new->maxsndbuflen = getsockopt_so_sendbuf (new->fd);
+  if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE))
+    zlog_debug ("%s: starting with OSPF send buffer size %u",
+      __func__, new->maxsndbuflen);
+  if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE+1)) == NULL)
+    {
+      zlog_err("ospf_new: fatal error: stream_new(%u) failed allocating ibuf",
+              OSPF_MAX_PACKET_SIZE+1);
+      exit(1);
+    }
+  new->t_read = thread_add_read (master, ospf_read, new, new->fd);
+  new->oi_write_q = list_new ();
+  
+  return new;
+}
+
+struct ospf *
+ospf_lookup ()
+{
+  if (listcount (om->ospf) == 0)
+    return NULL;
+
+  return listgetdata ((struct listnode *)listhead (om->ospf));
+}
+
+static int
+ospf_is_ready (struct ospf *ospf)
+{
+  /* OSPF must be on and Router-ID must be configured. */
+  if (!ospf || ospf->router_id.s_addr == 0)
+    return 0;
+  
+  return 1;
+}
+
+static void
+ospf_add (struct ospf *ospf)
+{
+  listnode_add (om->ospf, ospf);
+}
+
+static void
+ospf_delete (struct ospf *ospf)
+{
+  listnode_delete (om->ospf, ospf);
+}
+
+struct ospf *
+ospf_get ()
+{
+  struct ospf *ospf;
+
+  ospf = ospf_lookup ();
+  if (ospf == NULL)
+    {
+      ospf = ospf_new ();
+      ospf_add (ospf);
+
+      if (ospf->router_id_static.s_addr == 0)
+       ospf_router_id_update (ospf);
+
+      ospf_opaque_type11_lsa_init (ospf);
+    }
+
+  return ospf;
+}
+
+/* Handle the second half of deferred shutdown. This is called either
+ * from the deferred-shutdown timer thread, or directly through
+ * ospf_deferred_shutdown_check.
+ *
+ * Function is to cleanup G-R state, if required then call ospf_finish_final
+ * to complete shutdown of this ospf instance. Possibly exit if the
+ * whole process is being shutdown and this was the last OSPF instance.
+ */
+static void
+ospf_deferred_shutdown_finish (struct ospf *ospf)
+{
+  ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED;  
+  OSPF_TIMER_OFF (ospf->t_deferred_shutdown);
+  
+  ospf_finish_final (ospf);
+  
+  /* *ospf is now invalid */
+  
+  /* ospfd being shut-down? If so, was this the last ospf instance? */
+  if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN)
+      && (listcount (om->ospf) == 0))
+    exit (0);
+
+  return;
+}
+
+/* Timer thread for G-R */
+static int
+ospf_deferred_shutdown_timer (struct thread *t)
+{
+  struct ospf *ospf = THREAD_ARG(t);
+  
+  ospf_deferred_shutdown_finish (ospf);
+  
+  return 0;
+}
+
+/* Check whether deferred-shutdown must be scheduled, otherwise call
+ * down directly into second-half of instance shutdown.
+ */
+static void
+ospf_deferred_shutdown_check (struct ospf *ospf)
+{
+  unsigned long timeout;
+  struct listnode *ln;
+  struct ospf_area *area;
+  
+  /* deferred shutdown already running? */
+  if (ospf->t_deferred_shutdown)
+    return;
+  
+  /* Should we try push out max-metric LSAs? */
+  if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED)
+    {
+      for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area))
+        {
+          SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED);
+          
+          if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED))
+            ospf_router_lsa_update_area (area);
+        }
+      timeout = ospf->stub_router_shutdown_time;
+    }
+  else
+    {
+      /* No timer needed */
+      ospf_deferred_shutdown_finish (ospf);
+      return;
+    }
+  
+  OSPF_TIMER_ON (ospf->t_deferred_shutdown, ospf_deferred_shutdown_timer,
+                 timeout);
+  return;
+}
+
+/* Shut down the entire process */
+void
+ospf_terminate (void)
+{
+  struct ospf *ospf;
+  struct listnode *node, *nnode;
+  
+  /* shutdown already in progress */
+  if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN))
+    return;
+  
+  SET_FLAG (om->options, OSPF_MASTER_SHUTDOWN);
+
+  /* exit immediately if OSPF not actually running */
+  if (listcount(om->ospf) == 0)
+    exit(0);
+
+  for (ALL_LIST_ELEMENTS (om->ospf, node, nnode, ospf))
+    ospf_finish (ospf);
+
+  /* Deliberately go back up, hopefully to thread scheduler, as
+   * One or more ospf_finish()'s may have deferred shutdown to a timer
+   * thread
+   */
+}
+
+void
+ospf_finish (struct ospf *ospf)
+{
+  /* let deferred shutdown decide */
+  ospf_deferred_shutdown_check (ospf);
+      
+  /* if ospf_deferred_shutdown returns, then ospf_finish_final is
+   * deferred to expiry of G-S timer thread. Return back up, hopefully
+   * to thread scheduler.
+   */
+  return;
+}
+
+/* Final cleanup of ospf instance */
+static void
+ospf_finish_final (struct ospf *ospf)
+{
+  struct route_node *rn;
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct ospf_lsa *lsa;
+  struct ospf_interface *oi;
+  struct ospf_area *area;
+  struct ospf_vl_data *vl_data;
+  struct listnode *node, *nnode;
+  int i;
+
+  ospf_opaque_type11_lsa_term (ospf);
+  
+  /* be nice if this worked, but it doesn't */
+  /*ospf_flush_self_originated_lsas_now (ospf);*/
+  
+  /* Unregister redistribution */
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    ospf_redistribute_unset (ospf, i);
+  ospf_redistribute_default_unset (ospf);
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    ospf_remove_vls_through_area (ospf, area);
+  
+  for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data))
+    ospf_vl_delete (ospf, vl_data);
+  
+  list_delete (ospf->vlinks);
+
+  /* Reset interface. */
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    ospf_if_free (oi);
+
+  /* Clear static neighbors */
+  for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn))
+    if ((nbr_nbma = rn->info))
+      {
+       OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll);
+
+       if (nbr_nbma->nbr)
+         {
+           nbr_nbma->nbr->nbr_nbma = NULL;
+           nbr_nbma->nbr = NULL;
+         }
+
+       if (nbr_nbma->oi)
+         {
+           listnode_delete (nbr_nbma->oi->nbr_nbma, nbr_nbma);
+           nbr_nbma->oi = NULL;
+         }
+
+       XFREE (MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma);
+      }
+
+  route_table_finish (ospf->nbr_nbma);
+
+  /* Clear networks and Areas. */
+  for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+    {
+      struct ospf_network *network;
+
+      if ((network = rn->info) != NULL)
+       {
+         ospf_network_free (ospf, network);
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+    }
+
+  for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area))
+    {
+      listnode_delete (ospf->areas, area);
+      ospf_area_free (area);
+    }
+
+  /* Cancel all timers. */
+  OSPF_TIMER_OFF (ospf->t_external_lsa);
+  OSPF_TIMER_OFF (ospf->t_spf_calc);
+  OSPF_TIMER_OFF (ospf->t_ase_calc);
+  OSPF_TIMER_OFF (ospf->t_maxage);
+  OSPF_TIMER_OFF (ospf->t_maxage_walker);
+  OSPF_TIMER_OFF (ospf->t_abr_task);
+  OSPF_TIMER_OFF (ospf->t_asbr_check);
+  OSPF_TIMER_OFF (ospf->t_distribute_update);
+  OSPF_TIMER_OFF (ospf->t_lsa_refresher);
+  OSPF_TIMER_OFF (ospf->t_read);
+  OSPF_TIMER_OFF (ospf->t_write);
+  OSPF_TIMER_OFF (ospf->t_opaque_lsa_self);
+
+  close (ospf->fd);
+  stream_free(ospf->ibuf);
+   
+  LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa)
+    ospf_discard_from_db (ospf, ospf->lsdb, lsa);
+  LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa)
+    ospf_discard_from_db (ospf, ospf->lsdb, lsa);
+
+  ospf_lsdb_delete_all (ospf->lsdb);
+  ospf_lsdb_free (ospf->lsdb);
+
+  for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn))
+    {
+      struct ospf_lsa *lsa;
+
+      if ((lsa = rn->info) != NULL)
+       {
+         ospf_lsa_unlock (&lsa);
+         rn->info = NULL;
+       }
+      route_unlock_node (rn);
+    }
+  route_table_finish (ospf->maxage_lsa);
+
+  if (ospf->old_table)
+    ospf_route_table_free (ospf->old_table);
+  if (ospf->new_table)
+    {
+      ospf_route_delete (ospf->new_table);
+      ospf_route_table_free (ospf->new_table);
+    }
+  if (ospf->old_rtrs)
+    ospf_rtrs_free (ospf->old_rtrs);
+  if (ospf->new_rtrs)
+    ospf_rtrs_free (ospf->new_rtrs);
+  if (ospf->new_external_route)
+    {
+      ospf_route_delete (ospf->new_external_route);
+      ospf_route_table_free (ospf->new_external_route);
+    }
+  if (ospf->old_external_route)
+    {
+      ospf_route_delete (ospf->old_external_route);
+      ospf_route_table_free (ospf->old_external_route);
+    }
+  if (ospf->external_lsas)
+    {
+      ospf_ase_external_lsas_finish (ospf->external_lsas);
+    }
+
+  list_delete (ospf->areas);
+  
+  for (i = ZEBRA_ROUTE_SYSTEM; i <= ZEBRA_ROUTE_MAX; i++)
+    if (EXTERNAL_INFO (i) != NULL)
+      for (rn = route_top (EXTERNAL_INFO (i)); rn; rn = route_next (rn))
+       {
+         if (rn->info == NULL)
+           continue;
+         
+         XFREE (MTYPE_OSPF_EXTERNAL_INFO, rn->info);
+         rn->info = NULL;
+         route_unlock_node (rn);
+       }
+
+  ospf_distance_reset (ospf);
+  route_table_finish (ospf->distance_table);
+
+  ospf_delete (ospf);
+
+  XFREE (MTYPE_OSPF_TOP, ospf);
+}
+
+
+/* allocate new OSPF Area object */
+static struct ospf_area *
+ospf_area_new (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *new;
+
+  /* Allocate new config_network. */
+  new = XCALLOC (MTYPE_OSPF_AREA, sizeof (struct ospf_area));
+
+  new->ospf = ospf;
+
+  new->area_id = area_id;
+
+  new->external_routing = OSPF_AREA_DEFAULT;
+  new->default_cost = 1;
+  new->auth_type = OSPF_AUTH_NULL;
+  
+  /* New LSDB init. */
+  new->lsdb = ospf_lsdb_new ();
+
+  /* Self-originated LSAs initialize. */
+  new->router_lsa_self = NULL;
+
+  ospf_opaque_type10_lsa_init (new);
+
+  new->oiflist = list_new ();
+  new->ranges = route_table_init ();
+
+  if (area_id.s_addr == OSPF_AREA_BACKBONE)
+    ospf->backbone = new;
+
+  return new;
+}
+
+static void
+ospf_area_free (struct ospf_area *area)
+{
+  struct route_node *rn;
+  struct ospf_lsa *lsa;
+
+  /* Free LSDBs. */
+  LSDB_LOOP (ROUTER_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+  LSDB_LOOP (NETWORK_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+  LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+  LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+
+  LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+  LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+  LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
+    ospf_discard_from_db (area->ospf, area->lsdb, lsa);
+
+  ospf_lsdb_delete_all (area->lsdb);
+  ospf_lsdb_free (area->lsdb);
+
+  ospf_lsa_unlock (&area->router_lsa_self);
+  
+  route_table_finish (area->ranges);
+  list_delete (area->oiflist);
+
+  if (EXPORT_NAME (area))
+    free (EXPORT_NAME (area));
+
+  if (IMPORT_NAME (area))
+    free (IMPORT_NAME (area));
+
+  /* Cancel timer. */
+  OSPF_TIMER_OFF (area->t_stub_router);
+  OSPF_TIMER_OFF (area->t_opaque_lsa_self);
+  
+  if (OSPF_IS_AREA_BACKBONE (area))
+    area->ospf->backbone = NULL;
+
+  XFREE (MTYPE_OSPF_AREA, area);
+}
+
+void
+ospf_area_check_free (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area &&
+      listcount (area->oiflist) == 0 &&
+      area->ranges->top == NULL &&
+      area->shortcut_configured == OSPF_SHORTCUT_DEFAULT &&
+      area->external_routing == OSPF_AREA_DEFAULT &&
+      area->no_summary == 0 &&
+      area->default_cost == 1 &&
+      EXPORT_NAME (area) == NULL &&
+      IMPORT_NAME (area) == NULL &&
+      area->auth_type == OSPF_AUTH_NULL)
+    {
+      listnode_delete (ospf->areas, area);
+      ospf_area_free (area);
+    }
+}
+
+struct ospf_area *
+ospf_area_get (struct ospf *ospf, struct in_addr area_id, int format)
+{
+  struct ospf_area *area;
+  
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (!area)
+    {
+      area = ospf_area_new (ospf, area_id);
+      area->format = format;
+      listnode_add_sort (ospf->areas, area);
+      ospf_check_abr_status (ospf);  
+      if (ospf->stub_router_admin_set == OSPF_STUB_ROUTER_ADMINISTRATIVE_SET)
+        {
+          SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED);
+        }
+    }
+
+  return area;
+}
+
+struct ospf_area *
+ospf_area_lookup_by_area_id (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area))
+    if (IPV4_ADDR_SAME (&area->area_id, &area_id))
+      return area;
+
+  return NULL;
+}
+
+void
+ospf_area_add_if (struct ospf_area *area, struct ospf_interface *oi)
+{
+  listnode_add (area->oiflist, oi);
+}
+
+void
+ospf_area_del_if (struct ospf_area *area, struct ospf_interface *oi)
+{
+  listnode_delete (area->oiflist, oi);
+}
+
+
+/* Config network statement related functions. */
+static struct ospf_network *
+ospf_network_new (struct in_addr area_id, int format)
+{
+  struct ospf_network *new;
+  new = XCALLOC (MTYPE_OSPF_NETWORK, sizeof (struct ospf_network));
+
+  new->area_id = area_id;
+  new->format = format;
+  
+  return new;
+}
+
+static void 
+add_ospf_interface (struct connected *co, struct ospf_area *area)
+{
+  struct ospf_interface *oi;
+
+  oi = ospf_if_new (area->ospf, co->ifp, co->address);
+  oi->connected = co;
+
+  oi->area = area;
+
+  oi->params = ospf_lookup_if_params (co->ifp, oi->address->u.prefix4);
+  oi->output_cost = ospf_if_get_output_cost (oi);
+
+  /* Relate ospf interface to ospf instance. */
+  oi->ospf = area->ospf;
+
+  /* update network type as interface flag */
+  /* If network type is specified previously,
+     skip network type setting. */
+  oi->type = IF_DEF_PARAMS (co->ifp)->type;
+
+  /* Add pseudo neighbor. */
+  ospf_nbr_self_reset (oi);
+
+  ospf_area_add_if (oi->area, oi);
+
+  /* if router_id is not configured, dont bring up
+   * interfaces.
+   * ospf_router_id_update() will call ospf_if_update
+   * whenever r-id is configured instead.
+   */
+  if ((area->ospf->router_id.s_addr != 0)
+      && if_is_operative (co->ifp)) 
+    ospf_if_up (oi);
+}
+
+static void
+update_redistributed (struct ospf *ospf, int add_to_ospf)
+{
+  struct route_node *rn;
+  struct external_info *ei;
+
+  if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT))
+    if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT))
+      for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT));
+          rn; rn = route_next (rn))
+       if ((ei = rn->info) != NULL)
+         {
+            if (add_to_ospf)
+              {
+                if (ospf_external_info_find_lsa (ospf, &ei->p))
+                  if (!ospf_distribute_check_connected (ospf, ei))
+                    ospf_external_lsa_flush (ospf, ei->type, &ei->p,
+                                              ei->ifindex /*, ei->nexthop */);
+              }
+            else
+              {
+                if (!ospf_external_info_find_lsa (ospf, &ei->p))
+                  if (ospf_distribute_check_connected (ospf, ei))
+                    ospf_external_lsa_originate (ospf, ei);
+              }
+          }
+}
+
+static void
+ospf_network_free (struct ospf *ospf, struct ospf_network *network)
+{
+  ospf_area_check_free (ospf, network->area_id);
+  ospf_schedule_abr_task (ospf);
+  XFREE (MTYPE_OSPF_NETWORK, network);
+}
+
+int
+ospf_network_set (struct ospf *ospf, struct prefix_ipv4 *p,
+                 struct in_addr area_id)
+{
+  struct ospf_network *network;
+  struct ospf_area *area;
+  struct route_node *rn;
+  int ret = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  rn = route_node_get (ospf->networks, (struct prefix *)p);
+  if (rn->info)
+    {
+      /* There is already same network statement. */
+      route_unlock_node (rn);
+      return 0;
+    }
+
+  rn->info = network = ospf_network_new (area_id, ret);
+  area = ospf_area_get (ospf, area_id, ret);
+
+  /* Run network config now. */
+  ospf_network_run ((struct prefix *)p, area);
+
+  /* Update connected redistribute. */
+  update_redistributed(ospf, 1);
+  
+  ospf_area_check_free (ospf, area_id);
+
+  return 1;
+}
+
+int
+ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p,
+                   struct in_addr area_id)
+{
+  struct route_node *rn;
+  struct ospf_network *network;
+  struct listnode *node, *nnode;
+  struct ospf_interface *oi;
+
+  rn = route_node_lookup (ospf->networks, (struct prefix *)p);
+  if (rn == NULL)
+    return 0;
+
+  network = rn->info;
+  route_unlock_node (rn);
+  if (!IPV4_ADDR_SAME (&area_id, &network->area_id))
+    return 0;
+
+  ospf_network_free (ospf, rn->info);
+  rn->info = NULL;
+  route_unlock_node (rn);      /* initial reference */
+
+  /* Find interfaces that not configured already.  */
+  for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi))
+    {
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+        continue;
+      
+      ospf_network_run_subnet (ospf, oi->connected, NULL, NULL);
+    }
+  
+  /* Update connected redistribute. */
+  update_redistributed(ospf, 0);
+  
+  ospf_area_check_free (ospf, area_id);
+  
+  return 1;
+}
+
+/* Ensure there's an OSPF instance, as "ip ospf area" enabled OSPF means
+ * there might not be any 'router ospf' config.
+ *
+ * Otherwise, doesn't do anything different to ospf_if_update for now
+ */
+void
+ospf_interface_area_set (struct interface *ifp)
+{
+  struct ospf *ospf = ospf_get();
+  
+  ospf_if_update (ospf, ifp);
+  /* if_update does a update_redistributed */
+  
+  return;
+}
+
+void
+ospf_interface_area_unset (struct interface *ifp)
+{
+  struct route_node *rn_oi;
+  struct ospf *ospf;
+
+  if ((ospf = ospf_lookup ()) == NULL)
+    return; /* Ospf not ready yet */
+  
+  /* Find interfaces that may need to be removed. */
+  for (rn_oi = route_top (IF_OIFS (ifp)); rn_oi; rn_oi = route_next (rn_oi))
+    {
+      struct ospf_interface *oi;
+
+      if ( (oi = rn_oi->info) == NULL)
+       continue;
+      
+      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+       continue;
+      
+      ospf_network_run_subnet (ospf, oi->connected, NULL, NULL);
+    }
+
+  /* Update connected redistribute. */
+  update_redistributed (ospf, 0); /* interfaces possibly removed */
+  
+  return;
+}
+
+
+/* Check whether interface matches given network
+ * returns: 1, true. 0, false
+ */
+static int
+ospf_network_match_iface(const struct connected *co, const struct prefix *net)
+{
+  /* new approach: more elegant and conceptually clean */
+  return prefix_match(net, CONNECTED_PREFIX(co));
+}
+
+static void
+ospf_update_interface_area (struct connected *co, struct ospf_area *area)
+{
+  struct ospf_interface *oi = ospf_if_table_lookup (co->ifp, co->address);
+  
+  /* nothing to be done case */
+  if (oi && oi->area == area)
+    return;
+  
+  if (oi) 
+    ospf_if_free (oi);
+  
+  add_ospf_interface (co, area);
+}
+
+/* Run OSPF for the given subnet, taking into account the following
+ * possible sources of area configuration, in the given order of preference:
+ *
+ * - Whether there is interface+address specific area configuration
+ * - Whether there is a default area for the interface
+ * - Whether there is an area given as a parameter.
+ * - If no specific network prefix/area is supplied, whether there's
+ *   a matching network configured.
+ */
+static void
+ospf_network_run_subnet (struct ospf *ospf, struct connected *co,
+                         struct prefix *p, struct ospf_area *given_area)
+{
+  struct ospf_interface *oi;
+  struct ospf_if_params *params;
+  struct ospf_area *area = NULL;
+  struct route_node *rn;
+  int configed = 0;
+  
+  if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY))
+    return;
+  
+  if (co->address->family != AF_INET)
+    return;
+  
+  /* Try determine the appropriate area for this interface + address
+   * Start by checking interface config 
+   */   
+  if (!(params = ospf_lookup_if_params (co->ifp, co->address->u.prefix4)))
+    params = IF_DEF_PARAMS (co->ifp);
+  
+  if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
+    area = (ospf_area_get (ospf, params->if_area,
+                           OSPF_AREA_ID_FORMAT_ADDRESS));
+  
+  /* If we've found an interface and/or addr specific area, then we're
+   * done
+   */
+  if (area)
+    {
+      ospf_update_interface_area (co, area);
+      return;
+    }
+  
+  /* Otherwise, only remaining possibility is a matching network statement */
+  if (p)
+    {
+      assert (given_area != NULL);
+      
+      /* Which either was supplied as a parameter.. (e.g. cause a new
+       * network/area was just added)..
+       */
+      if (p->family == co->address->family 
+          && ospf_network_match_iface (co, p))
+        ospf_update_interface_area (co, given_area);
+      
+      return;
+    }
+  
+  /* Else we have to search the existing network/area config to see
+   * if any match..
+   */
+  for (rn = route_top (ospf->networks); rn; rn = route_next (rn))
+    if (rn->info != NULL
+        && ospf_network_match_iface (co, &rn->p))
+      {
+        struct ospf_network *network = (struct ospf_network *) rn->info;
+        area = ospf_area_get (ospf, network->area_id, network->format);
+        ospf_update_interface_area (co, area);
+        configed = 1;
+      }
+  
+  /* If the subnet isn't in any area, deconfigure */
+  if (!configed && (oi = ospf_if_table_lookup (co->ifp, co->address)))
+    ospf_if_free (oi);
+}
+
+static void
+ospf_network_run_interface (struct ospf *ospf, struct interface *ifp,
+                            struct prefix *p,
+                            struct ospf_area *given_area)
+{
+  struct listnode *cnode;
+  struct connected *co;
+  
+  if (memcmp (ifp->name, "VLINK", 5) == 0)
+    return;
+  
+  /* Network prefix without area is nonsensical */
+  if (p)
+    assert (given_area != NULL);
+  
+  /* if interface prefix is match specified prefix,
+     then create socket and join multicast group. */
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, co))
+    ospf_network_run_subnet (ospf, co, p, given_area);  
+}
+
+static void
+ospf_network_run (struct prefix *p, struct ospf_area *area)
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  /* Schedule Router ID Update. */
+  if (area->ospf->router_id.s_addr == 0)
+    ospf_router_id_update (area->ospf);
+  
+  /* Get target interface. */
+  for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp))
+    ospf_network_run_interface (area->ospf, ifp, p, area);
+}
+
+void
+ospf_ls_upd_queue_empty (struct ospf_interface *oi)
+{
+  struct route_node *rn;
+  struct listnode *node, *nnode;
+  struct list *lst;
+  struct ospf_lsa *lsa;
+
+  /* empty ls update queue */
+  for (rn = route_top (oi->ls_upd_queue); rn;
+       rn = route_next (rn))
+    if ((lst = (struct list *) rn->info))
+      {
+       for (ALL_LIST_ELEMENTS (lst, node, nnode, lsa))
+          ospf_lsa_unlock (&lsa); /* oi->ls_upd_queue */
+       list_free (lst);
+       rn->info = NULL;
+      }
+  
+  /* remove update event */
+  if (oi->t_ls_upd_event)
+    {
+      thread_cancel (oi->t_ls_upd_event);
+      oi->t_ls_upd_event = NULL;
+    }
+}
+
+void
+ospf_if_update (struct ospf *ospf, struct interface *ifp)
+{
+  if (!ospf)
+    ospf = ospf_lookup ();
+
+  /* OSPF must be ready. */
+  if (!ospf_is_ready (ospf))
+    return;
+  
+  ospf_network_run_interface (ospf, ifp, NULL, NULL);
+  
+  /* Update connected redistribute. */
+  update_redistributed(ospf, 1);
+}
+
+void
+ospf_remove_vls_through_area (struct ospf *ospf, struct ospf_area *area)
+{
+  struct listnode *node, *nnode;
+  struct ospf_vl_data *vl_data;
+
+  for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data))
+    if (IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id))
+      ospf_vl_delete (ospf, vl_data);
+}
+
+
+static const struct message ospf_area_type_msg[] =
+{
+  { OSPF_AREA_DEFAULT, "Default" },
+  { OSPF_AREA_STUB,     "Stub" },
+  { OSPF_AREA_NSSA,     "NSSA" },
+};
+static const int ospf_area_type_msg_max = OSPF_AREA_TYPE_MAX;
+
+static void
+ospf_area_type_set (struct ospf_area *area, int type)
+{
+  struct listnode *node;
+  struct ospf_interface *oi;
+
+  if (area->external_routing == type)
+    {
+      if (IS_DEBUG_OSPF_EVENT)
+       zlog_debug ("Area[%s]: Types are the same, ignored.",
+                  inet_ntoa (area->area_id));
+      return;
+    }
+
+  area->external_routing = type;
+
+  if (IS_DEBUG_OSPF_EVENT)
+    zlog_debug ("Area[%s]: Configured as %s", inet_ntoa (area->area_id),
+              LOOKUP (ospf_area_type_msg, type));
+
+  switch (area->external_routing)
+    {
+    case OSPF_AREA_DEFAULT:
+      for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+        if (oi->nbr_self != NULL)
+          {
+           UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP);
+           SET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+          }
+      break;
+    case OSPF_AREA_STUB:
+      for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+        if (oi->nbr_self != NULL)
+          {
+            if (IS_DEBUG_OSPF_EVENT)
+              zlog_debug ("setting options on %s accordingly", IF_NAME (oi));
+            UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP);
+            UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+            if (IS_DEBUG_OSPF_EVENT)
+              zlog_debug ("options set on %s: %x",
+                         IF_NAME (oi), OPTIONS (oi));
+          }
+      break;
+    case OSPF_AREA_NSSA:
+      for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi))
+        if (oi->nbr_self != NULL)
+          {
+            zlog_debug ("setting nssa options on %s accordingly", IF_NAME (oi));
+            UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E);
+            SET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP);
+            zlog_debug ("options set on %s: %x", IF_NAME (oi), OPTIONS (oi));
+          }
+      break;
+    default:
+      break;
+    }
+
+  ospf_router_lsa_update_area (area);
+  ospf_schedule_abr_task (area->ospf);
+}
+
+int
+ospf_area_shortcut_set (struct ospf *ospf, struct ospf_area *area, int mode)
+{
+  if (area->shortcut_configured == mode)
+    return 0;
+
+  area->shortcut_configured = mode;
+  ospf_router_lsa_update_area (area);
+  ospf_schedule_abr_task (ospf);
+
+  ospf_area_check_free (ospf, area->area_id);
+
+  return 1;
+}
+
+int
+ospf_area_shortcut_unset (struct ospf *ospf, struct ospf_area *area)
+{
+  area->shortcut_configured = OSPF_SHORTCUT_DEFAULT;
+  ospf_router_lsa_update_area (area);
+  ospf_area_check_free (ospf, area->area_id);
+  ospf_schedule_abr_task (ospf);
+
+  return 1;
+}
+
+static int
+ospf_area_vlink_count (struct ospf *ospf, struct ospf_area *area)
+{
+  struct ospf_vl_data *vl;
+  struct listnode *node;
+  int count = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl))
+    if (IPV4_ADDR_SAME (&vl->vl_area_id, &area->area_id))
+      count++;
+
+  return count;
+}
+
+int
+ospf_area_stub_set (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+  int format = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, format);
+  if (ospf_area_vlink_count (ospf, area))
+    return 0;
+
+  if (area->external_routing != OSPF_AREA_STUB)
+    ospf_area_type_set (area, OSPF_AREA_STUB);
+
+  return 1;
+}
+
+int
+ospf_area_stub_unset (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 1;
+
+  if (area->external_routing == OSPF_AREA_STUB)
+    ospf_area_type_set (area, OSPF_AREA_DEFAULT);
+
+  ospf_area_check_free (ospf, area_id);
+
+  return 1;
+}
+
+int
+ospf_area_no_summary_set (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+  int format = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, format);
+  area->no_summary = 1;
+
+  return 1;
+}
+
+int
+ospf_area_no_summary_unset (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  area->no_summary = 0;
+  ospf_area_check_free (ospf, area_id);
+
+  return 1;
+}
+
+int
+ospf_area_nssa_set (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+  int format = OSPF_AREA_ID_FORMAT_ADDRESS;
+
+  area = ospf_area_get (ospf, area_id, format);
+  if (ospf_area_vlink_count (ospf, area))
+    return 0;
+
+  if (area->external_routing != OSPF_AREA_NSSA)
+    {
+      ospf_area_type_set (area, OSPF_AREA_NSSA);
+      ospf->anyNSSA++;
+    }
+
+  /* set NSSA area defaults */
+  area->no_summary = 0;
+  area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
+  area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
+  area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT;
+
+  return 1;
+}
+
+int
+ospf_area_nssa_unset (struct ospf *ospf, struct in_addr area_id)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  if (area->external_routing == OSPF_AREA_NSSA)
+    {
+      ospf->anyNSSA--;
+      ospf_area_type_set (area, OSPF_AREA_DEFAULT);
+    }
+
+  ospf_area_check_free (ospf, area_id);
+
+  return 1;
+}
+
+int
+ospf_area_nssa_translator_role_set (struct ospf *ospf, struct in_addr area_id,
+                                   int role)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  area->NSSATranslatorRole = role;
+
+  return 1;
+}
+
+#if 0
+/* XXX: unused? Leave for symmetry? */
+static int
+ospf_area_nssa_translator_role_unset (struct ospf *ospf,
+                                     struct in_addr area_id)
+{
+  struct ospf_area *area;
+
+  area = ospf_area_lookup_by_area_id (ospf, area_id);
+  if (area == NULL)
+    return 0;
+
+  area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
+
+  ospf_area_check_free (ospf, area_id);
+
+  return 1;
+}
+#endif
+
+int
+ospf_area_export_list_set (struct ospf *ospf,
+                          struct ospf_area *area, const char *list_name)
+{
+  struct access_list *list;
+  list = access_list_lookup (AFI_IP, list_name);
+
+  EXPORT_LIST (area) = list;
+
+  if (EXPORT_NAME (area))
+    free (EXPORT_NAME (area));
+
+  EXPORT_NAME (area) = strdup (list_name);
+  ospf_schedule_abr_task (ospf);
+
+  return 1;
+}
+
+int
+ospf_area_export_list_unset (struct ospf *ospf, struct ospf_area * area)
+{
+
+  EXPORT_LIST (area) = 0;
+
+  if (EXPORT_NAME (area))
+    free (EXPORT_NAME (area));
+
+  EXPORT_NAME (area) = NULL;
+
+  ospf_area_check_free (ospf, area->area_id);
+  
+  ospf_schedule_abr_task (ospf);
+
+  return 1;
+}
+
+int
+ospf_area_import_list_set (struct ospf *ospf, struct ospf_area *area, 
+                           const char *name)
+{
+  struct access_list *list;
+  list = access_list_lookup (AFI_IP, name);
+
+  IMPORT_LIST (area) = list;
+
+  if (IMPORT_NAME (area))
+    free (IMPORT_NAME (area));
+
+  IMPORT_NAME (area) = strdup (name);
+  ospf_schedule_abr_task (ospf);
+
+  return 1;
+}
+
+int
+ospf_area_import_list_unset (struct ospf *ospf, struct ospf_area * area)
+{
+  IMPORT_LIST (area) = 0;
+
+  if (IMPORT_NAME (area))
+    free (IMPORT_NAME (area));
+
+  IMPORT_NAME (area) = NULL;
+  ospf_area_check_free (ospf, area->area_id);
+
+  ospf_schedule_abr_task (ospf);
+
+  return 1;
+}
+
+int
+ospf_timers_refresh_set (struct ospf *ospf, int interval)
+{
+  int time_left;
+
+  if (ospf->lsa_refresh_interval == interval)
+    return 1;
+
+  time_left = ospf->lsa_refresh_interval -
+    (quagga_time (NULL) - ospf->lsa_refresher_started);
+  
+  if (time_left > interval)
+    {
+      OSPF_TIMER_OFF (ospf->t_lsa_refresher);
+      ospf->t_lsa_refresher =
+       thread_add_timer (master, ospf_lsa_refresh_walker, ospf, interval);
+    }
+  ospf->lsa_refresh_interval = interval;
+
+  return 1;
+}
+
+int
+ospf_timers_refresh_unset (struct ospf *ospf)
+{
+  int time_left;
+
+  time_left = ospf->lsa_refresh_interval -
+    (quagga_time (NULL) - ospf->lsa_refresher_started);
+
+  if (time_left > OSPF_LSA_REFRESH_INTERVAL_DEFAULT)
+    {
+      OSPF_TIMER_OFF (ospf->t_lsa_refresher);
+      ospf->t_lsa_refresher =
+       thread_add_timer (master, ospf_lsa_refresh_walker, ospf,
+                         OSPF_LSA_REFRESH_INTERVAL_DEFAULT);
+    }
+
+  ospf->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT;
+
+  return 1;
+}
+
+
+static struct ospf_nbr_nbma *
+ospf_nbr_nbma_new (void)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = XCALLOC (MTYPE_OSPF_NEIGHBOR_STATIC,
+                     sizeof (struct ospf_nbr_nbma));
+
+  nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
+  nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT;
+
+  return nbr_nbma;
+}
+
+static void
+ospf_nbr_nbma_free (struct ospf_nbr_nbma *nbr_nbma)
+{
+  XFREE (MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma);
+}
+
+static void
+ospf_nbr_nbma_delete (struct ospf *ospf, struct ospf_nbr_nbma *nbr_nbma)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefix = nbr_nbma->addr;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  rn = route_node_lookup (ospf->nbr_nbma, (struct prefix *)&p);
+  if (rn)
+    {
+      ospf_nbr_nbma_free (rn->info);
+      rn->info = NULL;
+      route_unlock_node (rn);
+      route_unlock_node (rn);
+    }
+}
+
+static void
+ospf_nbr_nbma_down (struct ospf_nbr_nbma *nbr_nbma)
+{
+  OSPF_TIMER_OFF (nbr_nbma->t_poll);
+
+  if (nbr_nbma->nbr)
+    {
+      nbr_nbma->nbr->nbr_nbma = NULL;
+      OSPF_NSM_EVENT_EXECUTE (nbr_nbma->nbr, NSM_KillNbr);
+    }
+
+  if (nbr_nbma->oi)
+    listnode_delete (nbr_nbma->oi->nbr_nbma, nbr_nbma);
+}
+
+static void
+ospf_nbr_nbma_add (struct ospf_nbr_nbma *nbr_nbma,
+                  struct ospf_interface *oi)
+{
+  struct ospf_neighbor *nbr;
+  struct route_node *rn;
+  struct prefix p;
+
+  if (oi->type != OSPF_IFTYPE_NBMA)
+    return;
+
+  if (nbr_nbma->nbr != NULL)
+    return;
+
+  if (IPV4_ADDR_SAME (&oi->nbr_self->address.u.prefix4, &nbr_nbma->addr))
+    return;
+      
+  nbr_nbma->oi = oi;
+  listnode_add (oi->nbr_nbma, nbr_nbma);
+
+  /* Get neighbor information from table. */
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.u.prefix4 = nbr_nbma->addr;
+
+  rn = route_node_get (oi->nbrs, (struct prefix *)&p);
+  if (rn->info)
+    {
+      nbr = rn->info;
+      nbr->nbr_nbma = nbr_nbma;
+      nbr_nbma->nbr = nbr;
+
+      route_unlock_node (rn);
+    }
+  else
+    {
+      nbr = rn->info = ospf_nbr_new (oi);
+      nbr->state = NSM_Down;
+      nbr->src = nbr_nbma->addr;
+      nbr->nbr_nbma = nbr_nbma;
+      nbr->priority = nbr_nbma->priority;
+      nbr->address = p;
+
+      nbr_nbma->nbr = nbr;
+
+      OSPF_NSM_EVENT_EXECUTE (nbr, NSM_Start);
+    }
+}
+
+void
+ospf_nbr_nbma_if_update (struct ospf *ospf, struct ospf_interface *oi)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  if (oi->type != OSPF_IFTYPE_NBMA)
+    return;
+
+  for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn))
+    if ((nbr_nbma = rn->info))
+      if (nbr_nbma->oi == NULL && nbr_nbma->nbr == NULL)
+       {
+         p.family = AF_INET;
+         p.prefix = nbr_nbma->addr;
+         p.prefixlen = IPV4_MAX_BITLEN;
+
+         if (prefix_match (oi->address, (struct prefix *)&p))
+           ospf_nbr_nbma_add (nbr_nbma, oi);
+       }
+}
+
+struct ospf_nbr_nbma *
+ospf_nbr_nbma_lookup (struct ospf *ospf, struct in_addr nbr_addr)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefix = nbr_addr;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  rn = route_node_lookup (ospf->nbr_nbma, (struct prefix *)&p);
+  if (rn)
+    {
+      route_unlock_node (rn);
+      return rn->info;
+    }
+  return NULL;
+}
+
+struct ospf_nbr_nbma *
+ospf_nbr_nbma_lookup_next (struct ospf *ospf, struct in_addr *addr, int first)
+{
+#if 0
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct listnode *node;
+#endif
+
+  if (ospf == NULL)
+    return NULL;
+
+#if 0
+  for (ALL_LIST_ELEMENTS_RO (ospf->nbr_nbma, node, nbr_nbma))
+    {
+      if (first)
+       {
+         *addr = nbr_nbma->addr;
+         return nbr_nbma;
+       }
+      else if (ntohl (nbr_nbma->addr.s_addr) > ntohl (addr->s_addr))
+       {
+         *addr = nbr_nbma->addr;
+         return nbr_nbma;
+       }
+    }
+#endif
+  return NULL;
+}
+
+int
+ospf_nbr_nbma_set (struct ospf *ospf, struct in_addr nbr_addr)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+  struct ospf_interface *oi;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct listnode *node;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr);
+  if (nbr_nbma)
+    return 0;
+
+  nbr_nbma = ospf_nbr_nbma_new ();
+  nbr_nbma->addr = nbr_addr;
+
+  p.family = AF_INET;
+  p.prefix = nbr_addr;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  rn = route_node_get (ospf->nbr_nbma, (struct prefix *)&p);
+  if (rn->info)
+    route_unlock_node (rn);
+  rn->info = nbr_nbma;
+
+  for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi))
+    {
+      if (oi->type == OSPF_IFTYPE_NBMA)
+       if (prefix_match (oi->address, (struct prefix *)&p))
+         {
+           ospf_nbr_nbma_add (nbr_nbma, oi);
+           break;
+         }
+    }
+
+  return 1;
+}
+
+int
+ospf_nbr_nbma_unset (struct ospf *ospf, struct in_addr nbr_addr)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr);
+  if (nbr_nbma == NULL)
+    return 0;
+
+  ospf_nbr_nbma_down (nbr_nbma);
+  ospf_nbr_nbma_delete (ospf, nbr_nbma);
+
+  return 1;
+}
+
+int
+ospf_nbr_nbma_priority_set (struct ospf *ospf, struct in_addr nbr_addr,
+                           u_char priority)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr);
+  if (nbr_nbma == NULL)
+    return 0;
+
+  if (nbr_nbma->priority != priority)
+    nbr_nbma->priority = priority;
+
+  return 1;
+}
+
+int
+ospf_nbr_nbma_priority_unset (struct ospf *ospf, struct in_addr nbr_addr)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr);
+  if (nbr_nbma == NULL)
+    return 0;
+
+  if (nbr_nbma != OSPF_NEIGHBOR_PRIORITY_DEFAULT)
+    nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
+
+  return 1;
+}
+
+int
+ospf_nbr_nbma_poll_interval_set (struct ospf *ospf, struct in_addr nbr_addr,
+                                unsigned int interval)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr);
+  if (nbr_nbma == NULL)
+    return 0;
+
+  if (nbr_nbma->v_poll != interval)
+    {
+      nbr_nbma->v_poll = interval;
+      if (nbr_nbma->oi && ospf_if_is_up (nbr_nbma->oi))
+       {
+         OSPF_TIMER_OFF (nbr_nbma->t_poll);
+         OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer,
+                             nbr_nbma->v_poll);
+       }
+    }
+
+  return 1;
+}
+
+int
+ospf_nbr_nbma_poll_interval_unset (struct ospf *ospf, struct in_addr addr)
+{
+  struct ospf_nbr_nbma *nbr_nbma;
+
+  nbr_nbma = ospf_nbr_nbma_lookup (ospf, addr);
+  if (nbr_nbma == NULL)
+    return 0;
+
+  if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT)
+    nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT;
+
+  return 1;
+}
+
+void
+ospf_master_init ()
+{
+  memset (&ospf_master, 0, sizeof (struct ospf_master));
+
+  om = &ospf_master;
+  om->ospf = list_new ();
+  om->master = thread_master_create ();
+  om->start_time = quagga_time (NULL);
+}
diff --git a/ospfd/ospfd.conf.sample b/ospfd/ospfd.conf.sample
new file mode 100644 (file)
index 0000000..0e8ac67
--- /dev/null
@@ -0,0 +1,13 @@
+! -*- ospf -*-
+!
+! OSPFd sample configuration file
+!
+!
+hostname ospfd
+password zebra
+!enable password please-set-at-here
+!
+!router ospf
+!  network 192.168.1.0/24 area 0
+!
+log stdout
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
new file mode 100644 (file)
index 0000000..0315164
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * OSPFd main header.
+ * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ * 
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPFD_H
+#define _ZEBRA_OSPFD_H
+
+#include <zebra.h>
+#include "libospf.h"
+
+#include "filter.h"
+#include "log.h"
+
+#define OSPF_VERSION            2
+
+/* VTY port number. */
+#define OSPF_VTY_PORT          2604
+
+/* IP TTL for OSPF protocol. */
+#define OSPF_IP_TTL             1
+#define OSPF_VL_IP_TTL          100
+
+/* Default configuration file name for ospfd. */
+#define OSPF_DEFAULT_CONFIG   "ospfd.conf"
+
+#define OSPF_NSSA_TRANS_STABLE_DEFAULT         40
+
+#define OSPF_ALLSPFROUTERS              0xe0000005      /* 224.0.0.5 */
+#define OSPF_ALLDROUTERS                0xe0000006      /* 224.0.0.6 */
+
+
+/* OSPF Authentication Type. */
+#define OSPF_AUTH_NULL                      0
+#define OSPF_AUTH_SIMPLE                    1
+#define OSPF_AUTH_CRYPTOGRAPHIC             2
+/* For Interface authentication setting default */
+#define OSPF_AUTH_NOTSET                   -1
+/* For the consumption and sanity of the command handler */ 
+/* DO NIOT REMOVE!!! Need to detect whether a value has
+   been given or not in VLink command handlers */
+#define OSPF_AUTH_CMD_NOTSEEN              -2
+
+/* OSPF options. */
+#define OSPF_OPTION_MT                   0x01  /* M/T */
+#define OSPF_OPTION_E                    0x02
+#define OSPF_OPTION_MC                   0x04
+#define OSPF_OPTION_NP                   0x08
+#define OSPF_OPTION_EA                   0x10
+#define OSPF_OPTION_DC                   0x20
+#define OSPF_OPTION_O                    0x40
+#define OSPF_OPTION_DN                   0x80
+
+/* OSPF Database Description flags. */
+#define OSPF_DD_FLAG_MS                  0x01
+#define OSPF_DD_FLAG_M                   0x02
+#define OSPF_DD_FLAG_I                   0x04
+#define OSPF_DD_FLAG_ALL                 0x07
+
+#define OSPF_LS_REFRESH_SHIFT       (60 * 15)
+#define OSPF_LS_REFRESH_JITTER      60
+
+/* OSPF master for system wide configuration and variables. */
+struct ospf_master
+{
+  /* OSPF instance. */
+  struct list *ospf;
+
+  /* OSPF thread master. */
+  struct thread_master *master;
+
+  /* Zebra interface list. */
+  struct list *iflist;
+
+  /* Redistributed external information. */
+  struct route_table *external_info[ZEBRA_ROUTE_MAX + 1];
+#define EXTERNAL_INFO(T)      om->external_info[T]
+
+  /* OSPF start time. */
+  time_t start_time;
+
+  /* Various OSPF global configuration. */
+  u_char options;
+#define OSPF_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */  
+};
+
+/* OSPF instance structure. */
+struct ospf
+{
+  /* OSPF Router ID. */
+  struct in_addr router_id;            /* Configured automatically. */
+  struct in_addr router_id_static;     /* Configured manually. */
+
+  /* ABR/ASBR internal flags. */
+  u_char flags;
+#define OSPF_FLAG_ABR           0x0001
+#define OSPF_FLAG_ASBR          0x0002
+
+  /* ABR type. */
+  u_char abr_type;
+#define OSPF_ABR_UNKNOWN       0
+#define OSPF_ABR_STAND          1
+#define OSPF_ABR_IBM            2
+#define OSPF_ABR_CISCO          3
+#define OSPF_ABR_SHORTCUT       4
+#define OSPF_ABR_DEFAULT       OSPF_ABR_CISCO
+
+  /* NSSA ABR */
+  u_char anyNSSA;              /* Bump for every NSSA attached. */
+
+  /* Configured variables. */
+  u_char config;
+#define OSPF_RFC1583_COMPATIBLE         (1 << 0)
+#define OSPF_OPAQUE_CAPABLE            (1 << 2)
+#define OSPF_LOG_ADJACENCY_CHANGES     (1 << 3)
+#define OSPF_LOG_ADJACENCY_DETAIL      (1 << 4)
+
+  /* Opaque-LSA administrative flags. */
+  u_char opaque;
+#define OPAQUE_OPERATION_READY_BIT     (1 << 0)
+
+  /* RFC3137 stub router. Configured time to stay stub / max-metric */
+  unsigned int stub_router_startup_time;       /* seconds */
+  unsigned int stub_router_shutdown_time;      /* seconds */
+#define OSPF_STUB_ROUTER_UNCONFIGURED    0
+  u_char       stub_router_admin_set;
+#define OSPF_STUB_ROUTER_ADMINISTRATIVE_SET     1
+#define OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET   0
+
+#define OSPF_STUB_MAX_METRIC_SUMMARY_COST      0x00ff0000
+
+  /* LSA timers */
+  unsigned int min_ls_interval; /* minimum delay between LSAs (in msec) */
+  unsigned int min_ls_arrival; /* minimum interarrival time between LSAs (in msec) */
+
+  /* SPF parameters */
+  unsigned int spf_delay;              /* SPF delay time. */
+  unsigned int spf_holdtime;           /* SPF hold time. */
+  unsigned int spf_max_holdtime;       /* SPF maximum-holdtime */
+  unsigned int spf_hold_multiplier;    /* Adaptive multiplier for hold time */
+  
+  int default_originate;               /* Default information originate. */
+#define DEFAULT_ORIGINATE_NONE         0
+#define DEFAULT_ORIGINATE_ZEBRA                1
+#define DEFAULT_ORIGINATE_ALWAYS       2
+  u_int32_t ref_bandwidth;             /* Reference Bandwidth (Kbps). */
+  struct route_table *networks;         /* OSPF config networks. */
+  struct list *vlinks;                  /* Configured Virtual-Links. */
+  struct list *areas;                   /* OSPF areas. */
+  struct route_table *nbr_nbma;
+  struct ospf_area *backbone;           /* Pointer to the Backbone Area. */
+
+  struct list *oiflist;                 /* ospf interfaces */
+  u_char passive_interface_default;    /* passive-interface default */
+
+  /* LSDB of AS-external-LSAs. */
+  struct ospf_lsdb *lsdb;
+  
+  /* Flags. */
+  int external_origin;                 /* AS-external-LSA origin flag. */
+  int ase_calc;                                /* ASE calculation flag. */
+
+  struct list *opaque_lsa_self;                /* Type-11 Opaque-LSAs */
+
+  /* Routing tables. */
+  struct route_table *old_table;        /* Old routing table. */
+  struct route_table *new_table;        /* Current routing table. */
+
+  struct route_table *old_rtrs;         /* Old ABR/ASBR RT. */
+  struct route_table *new_rtrs;         /* New ABR/ASBR RT. */
+
+  struct route_table *new_external_route;   /* New External Route. */
+  struct route_table *old_external_route;   /* Old External Route. */
+  
+  struct route_table *external_lsas;    /* Database of external LSAs,
+                                          prefix is LSA's adv. network*/
+
+  /* Time stamps */
+  struct timeval ts_spf;               /* SPF calculation time stamp. */
+  struct timeval ts_spf_duration;      /* Execution time of last SPF */
+
+  struct route_table *maxage_lsa;       /* List of MaxAge LSA for deletion. */
+  int redistribute;                     /* Num of redistributed protocols. */
+
+  /* Threads. */
+  struct thread *t_abr_task;            /* ABR task timer. */
+  struct thread *t_asbr_check;          /* ASBR check timer. */
+  struct thread *t_distribute_update;   /* Distirbute list update timer. */
+  struct thread *t_spf_calc;           /* SPF calculation timer. */
+  struct thread *t_ase_calc;           /* ASE calculation timer. */
+  struct thread *t_external_lsa;       /* AS-external-LSA origin timer. */
+  struct thread *t_opaque_lsa_self;    /* Type-11 Opaque-LSAs origin event. */
+
+  unsigned int maxage_delay;           /* Delay on Maxage remover timer, sec */
+  struct thread *t_maxage;              /* MaxAge LSA remover timer. */
+  struct thread *t_maxage_walker;       /* MaxAge LSA checking timer. */
+
+  struct thread *t_deferred_shutdown;  /* deferred/stub-router shutdown timer*/
+
+  struct thread *t_write;
+  struct thread *t_read;
+  int fd;
+  unsigned int maxsndbuflen;
+  struct stream *ibuf;
+  struct list *oi_write_q;
+  
+  /* Distribute lists out of other route sources. */
+  struct 
+  {
+    char *name;
+    struct access_list *list;
+  } dlist[ZEBRA_ROUTE_MAX];
+#define DISTRIBUTE_NAME(O,T)    (O)->dlist[T].name
+#define DISTRIBUTE_LIST(O,T)    (O)->dlist[T].list
+
+  /* Redistribute metric info. */
+  struct 
+  {
+    int type;                   /* External metric type (E1 or E2).  */
+    int value;                 /* Value for static metric (24-bit).
+                                  -1 means metric value is not set. */
+  } dmetric [ZEBRA_ROUTE_MAX + 1];
+
+  /* Redistribute tag info. */
+  route_tag_t dtag [ZEBRA_ROUTE_MAX + 1];
+
+  /* For redistribute route map. */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+  } route_map [ZEBRA_ROUTE_MAX + 1]; /* +1 is for default-information */
+#define ROUTEMAP_NAME(O,T)   (O)->route_map[T].name
+#define ROUTEMAP(O,T)        (O)->route_map[T].map
+  
+  int default_metric;          /* Default metric for redistribute. */
+
+#define OSPF_LSA_REFRESHER_GRANULARITY 10
+#define OSPF_LSA_REFRESHER_SLOTS ((OSPF_LS_REFRESH_TIME + \
+                                  OSPF_LS_REFRESH_SHIFT)/10 + 1)
+  struct
+  {
+    u_int16_t index;
+    struct list *qs[OSPF_LSA_REFRESHER_SLOTS];
+  } lsa_refresh_queue;
+  
+  struct thread *t_lsa_refresher;
+  time_t lsa_refresher_started;
+#define OSPF_LSA_REFRESH_INTERVAL_DEFAULT 10
+  u_int16_t lsa_refresh_interval;
+  
+  /* Distance parameter. */
+  u_char distance_all;
+  u_char distance_intra;
+  u_char distance_inter;
+  u_char distance_external;
+
+  /* Statistics for LSA origination. */
+  u_int32_t lsa_originate_count;
+
+  /* Statistics for LSA used for new instantiation. */
+  u_int32_t rx_lsa_count;
+  struct route_table *distance_table;
+};
+
+/* OSPF area structure. */
+struct ospf_area
+{
+  /* OSPF instance. */
+  struct ospf *ospf;
+
+  /* Zebra interface list belonging to the area. */
+  struct list *oiflist;
+
+  /* Area ID. */
+  struct in_addr area_id;
+
+  /* Area ID format. */
+  char format;
+#define OSPF_AREA_ID_FORMAT_ADDRESS         1
+#define OSPF_AREA_ID_FORMAT_DECIMAL         2
+
+  /* Address range. */
+  struct list *address_range;
+
+  /* Configured variables. */
+  int external_routing;                 /* ExternalRoutingCapability. */
+#define OSPF_AREA_DEFAULT       0
+#define OSPF_AREA_STUB          1
+#define OSPF_AREA_NSSA          2
+#define OSPF_AREA_TYPE_MAX     3
+  int no_summary;                       /* Don't inject summaries into stub.*/
+  int shortcut_configured;              /* Area configured as shortcut. */
+#define OSPF_SHORTCUT_DEFAULT  0
+#define OSPF_SHORTCUT_ENABLE   1
+#define OSPF_SHORTCUT_DISABLE  2
+  int shortcut_capability;              /* Other ABRs agree on S-bit */
+  u_int32_t default_cost;               /* StubDefaultCost. */
+  int auth_type;                        /* Authentication type. */
+  
+
+  u_char NSSATranslatorRole;          /* NSSA configured role */
+#define OSPF_NSSA_ROLE_NEVER     0
+#define OSPF_NSSA_ROLE_CANDIDATE 1
+#define OSPF_NSSA_ROLE_ALWAYS    2
+  u_char NSSATranslatorState;              /* NSSA operational role */
+#define OSPF_NSSA_TRANSLATE_DISABLED 0
+#define OSPF_NSSA_TRANSLATE_ENABLED  1
+  int NSSATranslatorStabilityInterval;
+  
+  u_char transit;                      /* TransitCapability. */
+#define OSPF_TRANSIT_FALSE      0
+#define OSPF_TRANSIT_TRUE       1
+  struct route_table *ranges;          /* Configured Area Ranges. */
+  
+  /* RFC3137 stub router state flags for area */
+  u_char stub_router_state;
+#define OSPF_AREA_ADMIN_STUB_ROUTED    (1 << 0) /* admin stub-router set */
+#define OSPF_AREA_IS_STUB_ROUTED       (1 << 1) /* stub-router active */
+#define OSPF_AREA_WAS_START_STUB_ROUTED        (1 << 2) /* startup SR was done */
+  
+  /* Area related LSDBs[Type1-4]. */
+  struct ospf_lsdb *lsdb;
+
+  /* Self-originated LSAs. */
+  struct ospf_lsa *router_lsa_self;
+  struct list *opaque_lsa_self;                /* Type-10 Opaque-LSAs */
+
+  /* Area announce list. */
+  struct 
+  {
+    char *name;
+    struct access_list *list;
+  } _export;
+#define EXPORT_NAME(A)  (A)->_export.name
+#define EXPORT_LIST(A)  (A)->_export.list
+
+  /* Area acceptance list. */
+  struct 
+  {
+    char *name;
+    struct access_list *list;
+  } import;
+#define IMPORT_NAME(A)  (A)->import.name
+#define IMPORT_LIST(A)  (A)->import.list
+
+  /* Type 3 LSA Area prefix-list. */
+  struct 
+  {
+    char *name;
+    struct prefix_list *list;
+  } plist_in;
+#define PREFIX_LIST_IN(A)   (A)->plist_in.list
+#define PREFIX_NAME_IN(A)   (A)->plist_in.name
+
+  struct
+  {
+    char *name;
+    struct prefix_list *list;
+  } plist_out;
+#define PREFIX_LIST_OUT(A)  (A)->plist_out.list
+#define PREFIX_NAME_OUT(A)  (A)->plist_out.name
+
+  /* Shortest Path Tree. */
+  struct vertex *spf;
+
+  /* Threads. */
+  struct thread *t_stub_router;    /* Stub-router timer */
+  struct thread *t_opaque_lsa_self;    /* Type-10 Opaque-LSAs origin. */
+
+  /* Statistics field. */
+  u_int32_t spf_calculation;   /* SPF Calculation Count. */
+
+  /* Time stamps. */
+  struct timeval ts_spf;               /* SPF calculation time stamp. */
+
+  /* Router count. */
+  u_int32_t abr_count;         /* ABR router in this area. */
+  u_int32_t asbr_count;                /* ASBR router in this area. */
+
+  /* Counters. */
+  u_int32_t act_ints;          /* Active interfaces. */
+  u_int32_t full_nbrs;         /* Fully adjacent neighbors. */
+  u_int32_t full_vls;          /* Fully adjacent virtual neighbors. */
+};
+
+/* OSPF config network structure. */
+struct ospf_network
+{
+  /* Area ID. */
+  struct in_addr area_id;
+  int format;
+};
+
+/* OSPF NBMA neighbor structure. */
+struct ospf_nbr_nbma
+{
+  /* Neighbor IP address. */
+  struct in_addr addr;
+
+  /* OSPF interface. */
+  struct ospf_interface *oi;
+
+  /* OSPF neighbor structure. */
+  struct ospf_neighbor *nbr;
+
+  /* Neighbor priority. */
+  u_char priority;
+
+  /* Poll timer value. */
+  u_int32_t v_poll;
+
+  /* Poll timer thread. */
+  struct thread *t_poll;
+
+  /* State change. */
+  u_int32_t state_change;
+};
+
+/* Macro. */
+#define OSPF_AREA_SAME(X,Y) \
+        (memcmp ((X->area_id), (Y->area_id), IPV4_MAX_BYTELEN) == 0)
+
+#define IS_OSPF_ABR(O)         ((O)->flags & OSPF_FLAG_ABR)
+#define IS_OSPF_ASBR(O)                ((O)->flags & OSPF_FLAG_ASBR)
+
+#define OSPF_IS_AREA_ID_BACKBONE(I) ((I).s_addr == OSPF_AREA_BACKBONE)
+#define OSPF_IS_AREA_BACKBONE(A) OSPF_IS_AREA_ID_BACKBONE ((A)->area_id)
+
+#ifdef roundup
+#  define ROUNDUP(val, gran)   roundup(val, gran)
+#else /* roundup */
+#  define ROUNDUP(val, gran)   (((val) - 1 | (gran) - 1) + 1)
+#endif /* roundup */
+
+#define LSA_OPTIONS_GET(area) \
+        (((area)->external_routing == OSPF_AREA_DEFAULT) ? OSPF_OPTION_E : 0)
+#define LSA_OPTIONS_NSSA_GET(area) \
+        (((area)->external_routing == OSPF_AREA_NSSA)  ? OSPF_OPTION_NP : 0)
+
+#define OSPF_TIMER_ON(T,F,V)                                                  \
+    do {                                                                      \
+      if (!(T))                                                               \
+       (T) = thread_add_timer (master, (F), ospf, (V));                      \
+    } while (0)
+
+#define OSPF_AREA_TIMER_ON(T,F,V)                                             \
+    do {                                                                      \
+      if (!(T))                                                               \
+        (T) = thread_add_timer (master, (F), area, (V));                      \
+    } while (0)
+
+#define OSPF_POLL_TIMER_ON(T,F,V)                                             \
+    do {                                                                      \
+      if (!(T))                                                               \
+        (T) = thread_add_timer (master, (F), nbr_nbma, (V));                  \
+    } while (0)
+
+#define OSPF_POLL_TIMER_OFF(X)         OSPF_TIMER_OFF((X))
+
+#define OSPF_TIMER_OFF(X)                                                     \
+    do {                                                                      \
+      if (X)                                                                  \
+        {                                                                     \
+          thread_cancel (X);                                                  \
+          (X) = NULL;                                                         \
+        }                                                                     \
+    } while (0)
+
+/* Extern variables. */
+extern struct ospf_master *om;
+extern const struct message ospf_ism_state_msg[];
+extern const struct message ospf_nsm_state_msg[];
+extern const struct message ospf_lsa_type_msg[];
+extern const struct message ospf_link_state_id_type_msg[];
+extern const struct message ospf_network_type_msg[];
+extern const int ospf_ism_state_msg_max;
+extern const int ospf_nsm_state_msg_max;
+extern const int ospf_lsa_type_msg_max;
+extern const int ospf_link_state_id_type_msg_max;
+extern const int ospf_redistributed_proto_max;
+extern const int ospf_network_type_msg_max;
+extern struct zclient *zclient;
+extern struct thread_master *master;
+extern int ospf_zlog;
+
+/* Prototypes. */
+extern const char *ospf_redist_string(u_int route_type);
+extern struct ospf *ospf_lookup (void);
+extern struct ospf *ospf_get (void);
+extern void ospf_finish (struct ospf *);
+extern void ospf_router_id_update (struct ospf *ospf);
+extern int ospf_network_set (struct ospf *, struct prefix_ipv4 *,
+                            struct in_addr);
+extern int ospf_network_unset (struct ospf *, struct prefix_ipv4 *,
+                              struct in_addr);
+extern int ospf_area_stub_set (struct ospf *, struct in_addr);
+extern int ospf_area_stub_unset (struct ospf *, struct in_addr);
+extern int ospf_area_no_summary_set (struct ospf *, struct in_addr);
+extern int ospf_area_no_summary_unset (struct ospf *, struct in_addr);
+extern int ospf_area_nssa_set (struct ospf *, struct in_addr);
+extern int ospf_area_nssa_unset (struct ospf *, struct in_addr);
+extern int ospf_area_nssa_translator_role_set (struct ospf *, struct in_addr,
+                                              int);
+extern int ospf_area_export_list_set (struct ospf *, struct ospf_area *,
+                                     const char *);
+extern int ospf_area_export_list_unset (struct ospf *, struct ospf_area *);
+extern int ospf_area_import_list_set (struct ospf *, struct ospf_area *,
+                                     const char *);
+extern int ospf_area_import_list_unset (struct ospf *, struct ospf_area *);
+extern int ospf_area_shortcut_set (struct ospf *, struct ospf_area *, int);
+extern int ospf_area_shortcut_unset (struct ospf *, struct ospf_area *);
+extern int ospf_timers_refresh_set (struct ospf *, int);
+extern int ospf_timers_refresh_unset (struct ospf *);
+extern int ospf_nbr_nbma_set (struct ospf *, struct in_addr);
+extern int ospf_nbr_nbma_unset (struct ospf *, struct in_addr);
+extern int ospf_nbr_nbma_priority_set (struct ospf *, struct in_addr, u_char);
+extern int ospf_nbr_nbma_priority_unset (struct ospf *, struct in_addr);
+extern int ospf_nbr_nbma_poll_interval_set (struct ospf *, struct in_addr,
+                                           unsigned int);
+extern int ospf_nbr_nbma_poll_interval_unset (struct ospf *, struct in_addr);
+extern void ospf_prefix_list_update (struct prefix_list *);
+extern void ospf_init (void);
+extern void ospf_if_update (struct ospf *, struct interface *);
+extern void ospf_ls_upd_queue_empty (struct ospf_interface *);
+extern void ospf_terminate (void);
+extern void ospf_nbr_nbma_if_update (struct ospf *, struct ospf_interface *);
+extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup (struct ospf *,
+                                                  struct in_addr);
+extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next (struct ospf *,
+                                                       struct in_addr *,
+                                                       int);
+extern int ospf_oi_count (struct interface *);
+
+extern struct ospf_area *ospf_area_get (struct ospf *, struct in_addr, int);
+extern void ospf_area_check_free (struct ospf *, struct in_addr);
+extern struct ospf_area *ospf_area_lookup_by_area_id (struct ospf *,
+                                                     struct in_addr);
+extern void ospf_area_add_if (struct ospf_area *, struct ospf_interface *);
+extern void ospf_area_del_if (struct ospf_area *, struct ospf_interface *);
+
+extern void ospf_interface_area_set (struct interface *);
+extern void ospf_interface_area_unset (struct interface *);
+
+extern void ospf_route_map_init (void);
+extern void ospf_snmp_init (void);
+
+extern void ospf_master_init (void);
+
+#endif /* _ZEBRA_OSPFD_H */
diff --git a/pimd/.gitignore b/pimd/.gitignore
new file mode 100644 (file)
index 0000000..51a2ac8
--- /dev/null
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+libpim.a
+pimd
+test_igmpv3_join
+tags
+TAGS
+.deps
+*.o
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/pimd/AUTHORS b/pimd/AUTHORS
new file mode 100644 (file)
index 0000000..f6135a4
--- /dev/null
@@ -0,0 +1,9 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+# Everton da Silva Marques <everton.marques@gmail.com>
+$ more ~/.gitconfig
+[user]
+        name = Everton Marques
+        email = everton.marques@gmail.com
+
+-x-
diff --git a/pimd/CAVEATS b/pimd/CAVEATS
new file mode 100644 (file)
index 0000000..9f07bda
--- /dev/null
@@ -0,0 +1,178 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+C1 IGMPv3 backward compatibility with IGMPv1 and IGMPv2 is not
+   implemented. See RFC 3376, 7.3. Multicast Router Behavior. That's
+   because only Source-Specific Multicast is currently targeted.
+
+C2 IGMPv3 support for forwarding any-source groups is not
+   implemented. Traffic for groups in mode EXCLUDE {empty} won't be
+   forwarded.  See RFC 3376, 6.3. Source-Specific Forwarding
+   Rules. That's because only Source-Specific Multicast is currently
+   targeted.
+
+C3 Load Splitting of IP Multicast Traffic over ECMP is not supported.
+   See also: RFC 2991
+   Multipath Issues in Unicast and Multicast Next-Hop Selection
+   http://www.rfc-editor.org/rfc/rfc2991.txt
+
+C4 IPSec AH authentication is not supported (RFC 4601:
+   6.3. Authentication Using IPsec).
+
+C5 PIM support is limited to SSM mode as defined in section 4.8.2
+   (PIM-SSM-Only Routers) of RFC4601. That's because only
+   Source-Specific Multicast is currently targeted.
+
+C6 PIM implementation currently does not support IPv6. PIM-SSM
+   requires IGMPv3 for IPv4 and MLDv2 for IPv6. MLDv2 is currently
+   missing. See also CAVEAT C9.
+
+C7 FIXED (S,G) Assert state machine (RFC 4601, section 4.6.1) is not
+   implemented. See also TODO T6. See also CAVEAT C10.
+
+C8 It is not possible to disable join suppression in order to
+   explicitly track the join membership of individual downstream
+   routers.
+   - IGMPv3 Explicit Membership Tracking is not supported.
+     When explicit tracking is enabled on a router, the router can
+     individually track the Internet Group Management Protocol (IGMP)
+     membership state of all reporting hosts. This feature allows the
+     router to achieve minimal leave latencies when hosts leave a
+     multicast group or channel. Example:
+       conf t
+        interface eth0
+         ip igmp explicit-tracking
+
+C9 Only IPv4 Address Family (number=1) is supported in the PIM Address
+   Family field.
+   See also RFC 4601: 5.1. PIM Address Family
+   See also CAVEAT C6.
+   See also http://www.iana.org/assignments/address-family-numbers
+
+C10 FIXED Assert metric depends on metric_preference and
+    route_metric. Those parameters should be fetched from RIB
+    (zebra). See also pim_rpf.c, pim_rpf_update().
+
+C11 SSM Mapping is not supported
+
+    SSM Mapping Overview:
+
+    SSM mapping introduces a means for the last hop router to discover
+    sources sending to groups. When SSM mapping is configured, if a
+    router receives an IGMPv1 or IGMPv2 membership report for a
+    particular group G, the router translates this report into one or
+    more (S, G) channel memberships for the well-known sources
+    associated with this group.
+
+    When the router receives an IGMPv1 or IGMPv2 membership report for
+    a group G, the router uses SSM mapping to determine one or more
+    source IP addresses for the group G. SSM mapping then translates
+    the membership report as an IGMPv3 report INCLUDE (G, [S1, G],
+    [S2, G]...[Sn, G] and continues as if it had received an IGMPv3
+    report. The router then sends out PIM joins toward (S1, G) to (Sn,
+    G) and continues to be joined to these groups as long as it
+    continues to receive the IGMPv1 or IGMPv2 membership reports and
+    as long as the SSM mapping for the group remains the same. SSM
+    mapping, thus, enables you to leverage SSM for video delivery to
+    legacy STBs that do not support IGMPv3 or for applications that do
+    not take advantage of the IGMPv3 host stack.
+
+    SSM mapping enables the last hop router to determine the source
+    addresses either by a statically configured table on the router or
+    by consulting a DNS server. When the statically configured table
+    is changed, or when the DNS mapping changes, the router will leave
+    the current sources associated with the joined groups.
+
+C12 FIXED MRIB for incongruent unicast/multicast topologies is not
+    supported.  RPF mechanism currently just looks up the information
+    in the unicast routing table.
+
+    See also:
+    RFC5110: 2.2.3.  Issue: Overlapping Unicast/Multicast Topology
+    
+    Sometimes, multicast RPF mechanisms first look up the multicast
+    routing table, or M-RIB ("topology database") with a longest
+    prefix match algorithm, and if they find any entry (including a
+    default route), that is used; if no match is found, the unicast
+    routing table is used instead.
+
+C13 Can't detect change of primary address before the actual change.
+    Possible approach is to craft old interface address into ip source
+    address by using raw ip socket.
+
+    See also:
+
+    RFC 4601: 4.3.1.  Sending Hello Messages
+
+    Before an interface goes down or changes primary IP address, a
+    Hello message with a zero HoldTime should be sent immediately
+    (with the old IP address if the IP address changed).
+
+    See also pim_sock_delete().
+
+C14 FIXED Detection of interface primary address changes may fail when
+    there are multiple addresses.
+    See also TODO T32.
+
+C15 Changes in interface secondary address list are not immediately
+    detected.
+    See also detect_secondary_address_change
+    See also TODO T31.
+
+C16 AMT Draft (mboned-auto-multicast) is not supported.
+    AMT = Automatic IP Multicast Without Explicit Tunnels
+
+    See also:
+
+    Draft
+    http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast
+    http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast-09
+
+    AMT gateway implementation for Linux
+    http://cs.utdallas.edu/amt/
+
+    AMT for Streaming (IPTV) on Global IP Multicast by Greg Shepherd (Cisco)
+    http://nznog.miniconf.org/nznog-2008-sysadmin-miniconf-greg-shepherd-iptv.pdf
+
+C17 SNMP / RFC 5060 (PIM MIB) is not supported.
+
+C18 MFC never recovers from removal of static route to source
+
+    # route add -host 1.2.3.4 gw 192.168.56.10
+    Before removal:
+    quagga-pimd-router# sh ip mroute
+    Source          Group           Proto Input iVifI Output oVifI TTL Uptime
+    1.2.3.4         232.1.2.3       I     eth1      3 eth0       2   1 00:00:36
+
+    # route del -host 1.2.3.4 gw 192.168.56.10
+    After removal: sh ip mroute --> empty output
+
+    # route add -host 1.2.3.4 gw 192.168.56.10
+    After the route is restored: sh ip mroute --> never recovers (empty output)
+
+    At this point, "no ip pim ssm" on the upstream interface (eth0) crashes pimd:
+
+    2014/02/14 16:30:14 PIM: ifmembership_set: (S,G)=(1.2.3.4,232.1.2.3) membership now is NOINFO on interface eth0
+    2014/02/14 16:30:14 PIM: pim_ifchannel_update_assert_tracking_desired: AssertTrackingDesired(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0
+    2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 2 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3)
+    2014/02/14 16:30:14 PIM: pim_ifchannel_update_could_assert: CouldAssert(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0
+    2014/02/14 16:30:14 PIM: pim_ifchannel_update_my_assert_metric: my_assert_metric(1.2.3.4,232.1.2.3,eth0) changed from 0,0,0,10.0.2.15 to 1,4294967295,4294967295,0.0.0.0
+    2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 1 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3)
+    2014/02/14 16:30:14 PIM: Assertion `!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)' failed in file pim_igmpv3.c, line 412, function igmp_source_delete
+
+C19 Provision to prevent group mode clash
+
+    Beware group mode clash. A host/application issuing IGMPv2
+    any-source joins for a group will disrupt SSM multicast for that
+    group.
+
+    For instance, support for source-specific static igmp WILL FAIL if
+    there is host/application issuing IGMPv2 any-source joins for the
+    same group.
+
+    The reason is the IGMPv2 any-source join forces qpimd to switch
+    the group mode to ASM (any-source multicast); however, qpimd is
+    unable to program ASM groups into the kernel; multicast won't
+    flow. There could be some provision to prevent such a behavior,
+    but currently there is none.
+
+-x-
diff --git a/pimd/COMMANDS b/pimd/COMMANDS
new file mode 100644 (file)
index 0000000..425ac82
--- /dev/null
@@ -0,0 +1,81 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+global configuration commands:
+       pimd:
+       ip multicast-routing    Enable IP multicast forwarding
+       ip ssmpingd             Enable ssmpingd operation
+
+       zebra:
+       ip mroute               Configure static unicast route into MRIB for multicast RPF lookup
+
+interface configuration commands:
+       pimd:
+       ip igmp                                         Enable IGMP operation
+       ip igmp join                                    IGMP join multicast group
+       ip igmp query-interval <1-1800>                 IGMP host query interval
+       ip igmp query-max-response-time <1-25>          IGMP max query response (seconds)
+       ip igmp query-max-response-time-dsec <10-250>   IGMP max query response (deciseconds)
+       ip pim ssm                                      Enable PIM SSM operation
+
+verification commands:
+       pimd:
+       show ip igmp interface                  IGMP interface information
+       show ip igmp join                       IGMP static join information
+       show ip igmp parameters                 IGMP parameters information
+       show ip igmp groups                     IGMP groups information
+       show ip igmp groups retransmissions     IGMP group retransmission
+       show ip igmp sources                    IGMP sources information
+       show ip igmp sources retransmissions    IGMP source retransmission
+       show ip pim address                     PIM interface address
+       show ip pim assert                      PIM interface assert
+       show ip pim assert-internal             PIM interface internal assert state
+       show ip pim assert-metric               PIM interface assert metric
+       show ip pim assert-winner-metric                PIM interface assert winner metric
+       show ip pim designated-router           PIM interface designated router
+       show ip pim hello                       PIM interface hello information
+       show ip pim interface                   PIM interface information
+       show ip pim lan-prune-delay              PIM neighbors LAN prune delay parameters
+       show ip pim local-membership            PIM interface local-membership
+       show ip pim jp-override-interval         PIM interface J/P override interval
+       show ip pim join                         PIM interface join information
+       show ip pim neighbor                    PIM neighbor information
+       show ip pim rpf                         PIM cached source rpf information
+       show ip pim secondary                    PIM neighbor addresses
+       show ip pim upstream                    PIM upstream information
+       show ip pim upstream-join-desired       PIM upstream join-desired
+       show ip pim upstream-rpf                        PIM upstream source rpf
+       show ip multicast                       Multicast global information
+       show ip mroute                          IP multicast routing table
+       show ip mroute count                    Route and packet count data
+       show ip rib                             IP unicast routing table
+       show ip ssmpingd                                ssmpingd operation
+
+       zebra:
+       show ip rpf                             Display RPF information for multicast source
+
+debug commands:
+       pimd:
+       clear ip interfaces             Reset interfaces
+       clear ip igmp interfaces                Reset IGMP interfaces
+       clear ip mroute                  Reset multicast routes
+       clear ip pim interfaces         Reset PIM interfaces
+       clear ip pim oil                        Rescan PIM OIL (output interface list)
+       debug igmp                      IGMP protocol activity
+       debug mroute                    PIM interaction with kernel MFC cache
+       debug pim                       PIM protocol activity
+       debug pim zebra                 ZEBRA protocol activity
+       debug ssmpingd                  ssmpingd activity
+       show debugging                  State of each debugging option
+       test igmp receive report                Test reception of IGMPv3 report
+       test pim receive assert          Test reception of PIM assert
+       test pim receive dump           Test reception of PIM packet dump
+       test pim receive hello          Test reception of PIM hello
+       test pim receive join           Test reception of PIM join
+       test pim receive prune          Test reception of PIM prune
+       test pim receive upcall         Test reception of kernel upcall
+
+statistics commands:
+       pimd:
+       show memory pim         PIM memory statistics
+
+-x-
diff --git a/pimd/COPYING b/pimd/COPYING
new file mode 100644 (file)
index 0000000..3912109
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/pimd/DEBUG b/pimd/DEBUG
new file mode 100644 (file)
index 0000000..72fb826
--- /dev/null
@@ -0,0 +1,86 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+DEBUG HINTS
+
+  - Check the source is issuing multicast packets with TTL high enough
+    to reach the recipients.
+
+  - Check the multicast packets are not being dropped due to
+    fragmentation problems.
+
+  - Three easy options to test IGMPv3 joins from the receiver host:
+
+    1) Configure pimd on the receiver host with "ip igmp join":
+
+       interface eth0
+        ip pim ssm
+        ip igmp join 239.1.1.1 1.1.1.1 
+
+    2) Use test_igmpv3_join command-line utility (provided with qpimd):
+
+       test_igmpv3_join eth0 239.1.1.1 1.1.1.1
+
+    3) User the Stig Venaas' ssmping utility:
+
+       ssmping -I eth0 1.1.1.1
+
+       To see multicast responses with ssmping, you will need run on
+       the host 1.1.1.1 either:
+       a) Stig Venaas' ssmpingd command-line daemon
+          OR
+       b) qpimd built-in ssmpingd service:
+          conf t
+            ip ssmpingd 1.1.1.1
+
+  - Using nepim to generate multicast stream from 1.1.1.1 to 239.1.1.1:
+
+    Notices:
+
+    a) The host unicast address 1.1.1.1 must be reachable from the
+    receiver.
+
+    b) nepim tool requires the receiver must be started *before* the
+    sender.
+
+    First: Start a receiver for that stream by running:
+
+    nepim -q -6 -j 1.1.1.1+239.1.1.1@eth0
+    (Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.)
+
+    Second: Start the sender at host 1.1.1.1.
+
+    The following command generates a 100-kbps multicast stream for
+    channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP
+    packet (to avoid fragmentation):
+
+    nepim -6 -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d
+
+
+
+SAMPLE DEBUG COMMANDS
+
+  conf t
+   int eth0
+    ip pim ssm
+
+  test pim receive hello eth0 192.168.0.2 600 10 111 1000 3000 0
+  test pim receive join eth0 600 192.168.0.1 192.168.0.2 239.1.1.1 1.1.1.1
+
+  show ip pim join                                                         
+
+
+INTEROPERABILITY WITH CISCO
+
+  ! Cisco IP Multicast command reference:
+  ! ftp://ftpeng.cisco.com/ipmulticast/Multicast-Commands
+  !
+  ip pim ssm default ! enable SSM mode for groups 232.0.0.0/8
+  ip multicast-routing
+  ip pim state-refresh disable
+  no ip pim dm-fallback
+  !
+  interface FastEthernet0
+   ip pim sparse-mode
+   ip igmp version 3
+
+-x-
diff --git a/pimd/LINUX_KERNEL_MROUTE_MFC b/pimd/LINUX_KERNEL_MROUTE_MFC
new file mode 100644 (file)
index 0000000..e87e567
--- /dev/null
@@ -0,0 +1,26 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+#
+# The Linux Kernel MFC (Multicast Forwarding Cache)
+#
+
+# Check Linux kernel multicast interfaces:
+cat /proc/net/dev_mcast
+
+# Check that interface eth0 is forwarding multicast:
+cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding
+
+# Check Linux kernel multicast VIFs:
+cat /proc/net/ip_mr_vif
+Interface      BytesIn  PktsIn  BytesOut PktsOut Flags Local    Remote
+
+# Check Linux kernel MFC:
+# Oifs format = vifi:TTL
+cat /proc/net/ip_mr_cache
+Group    Origin   Iif     Pkts    Bytes    Wrong Oifs
+
+# iproute2 can display the MFC:
+ip mroute show
+(2.2.2.2, 239.2.2.2)             Iif: eth1       Oifs: eth0
+
+# -- end-of-file --
diff --git a/pimd/Makefile.am b/pimd/Makefile.am
new file mode 100644 (file)
index 0000000..d045436
--- /dev/null
@@ -0,0 +1,76 @@
+## Process this file with automake to produce Makefile.in.
+## $QuaggaId: $Format:%an, %ai, %h$ $
+
+# qpimd - pimd for quagga
+# Copyright (C) 2008 Everton da Silva Marques
+#
+# qpimd 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.
+# 
+# qpimd 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 qpimd; see the file COPYING.  If not, write
+# to the Free Software Foundation, Inc., 59 Temple Place - Suite
+# 330, Boston, MA 02111-1307, USA.
+
+# PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands
+# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging
+# PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex
+# PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch
+# PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries
+# PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall
+
+PIM_DEFS =
+#PIM_DEFS += -DPIM_DEBUG_BYDEFAULT
+PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY
+#PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH
+PIM_DEFS += -DPIM_ZCLIENT_DEBUG
+PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC
+#PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS)
+INSTALL_SDATA=@INSTALL@ -m 600
+LIBS = @LIBS@
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = libpim.a
+sbin_PROGRAMS = pimd 
+bin_PROGRAMS = test_igmpv3_join
+
+libpim_a_SOURCES = \
+       pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \
+       pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \
+       pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \
+       pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
+       pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
+       pim_msg.c pim_upstream.c pim_rpf.c pim_macro.c \
+       pim_igmp_join.c pim_ssmpingd.c pim_int.c pim_static.c \
+        pim_routemap.c
+
+noinst_HEADERS = \
+       pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
+       pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \
+       pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \
+       pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
+       pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
+       pim_msg.h pim_upstream.h pim_rpf.h pim_macro.h \
+       pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_static.h
+
+pimd_SOURCES = \
+       pim_main.c $(libpim_a_SOURCES)
+
+test_igmpv3_join_SOURCES = \
+       test_igmpv3_join.c pim_igmp_join.c
+
+pimd_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = pimd.conf.sample
diff --git a/pimd/README b/pimd/README
new file mode 100644 (file)
index 0000000..1e3f72c
--- /dev/null
@@ -0,0 +1,164 @@
+#
+# $QuaggaId: $Format:%an, %ai, %h$ $
+#
+
+INTRODUCTION
+
+        qpimd aims to implement a PIM (Protocol Independent Multicast)
+       daemon for the Quagga Routing Suite.
+
+       Initially qpimd targets only PIM SSM (Source-Specific
+       Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only
+       Routers) of RFC 4601.
+
+       In order to deliver end-to-end multicast routing control
+       plane, qpimd includes the router-side of IGMPv3 (RFC 3376).
+
+LICENSE
+
+        qpimd - pimd for quagga
+        Copyright (C) 2008 Everton da Silva Marques
+
+        qpimd 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.
+
+        qpimd 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 qpimd; see the file COPYING.  If not, write
+        to the Free Software Foundation, Inc., 59 Temple Place - Suite
+        330, Boston, MA 02111-1307, USA.
+
+HOME SITE
+
+        qpimd lives at:
+
+        https://github.com/udhos/qpimd
+
+PLATFORMS
+
+       qpimd has been tested with Debian Lenny under Linux 2.6.
+
+REQUIREMENTS
+
+       qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net)
+
+       The GNU Build System (Autotools) is required to build from
+       source code repository.
+
+       gawk is also needed to build with Autotools. Any other awk
+       usually won't work.
+
+BUILDING FROM QUAGGA GIT REPOSITORY
+
+       1) Get the latest quagga source tree
+
+       # git clone git://code.quagga.net/quagga.git quagga
+
+       2) Apply qpimd patch into quagga source tree
+
+       # patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch
+
+       3) Compile and install quagga
+
+       # cd quagga
+       # ./bootstrap.sh
+       # ./configure --prefix=/usr/local/quagga --enable-pimd
+       # make
+       # make install
+
+BUILDING FROM QUAGGA TARBALL
+
+       1) Get the latest quagga tarball
+
+       # wget http://www.quagga.net/download/quagga-0.99.13.tar.gz
+
+       2) Unpack the quagga tarball
+
+       # tar xzf quagga-0.99.13.tar.gz
+
+       3) Apply qpimd patch into quagga source tree
+
+       # patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch
+
+       4) Compile and install quagga
+
+       # cd quagga-0.99.13
+       # ./configure --prefix=/usr/local/quagga --enable-pimd
+       # make
+       # make install
+
+USAGE
+
+       1) Configure and start the zebra daemon
+
+       # cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf
+       # vi /usr/local/quagga/etc/zebra.conf
+       # /usr/local/quagga/sbin/zebra
+
+       2) Configure and start the pimd daemon
+
+       # cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf
+       # vi /usr/local/quagga/etc/pimd.conf
+       # /usr/local/quagga/sbin/pimd
+
+       3) Access pimd vty interface at port TCP 2611
+
+       # telnet localhost 2611
+
+CONFIGURATION COMMANDS
+
+       See available commands in the file pimd/COMMANDS.
+
+KNOWN CAVEATS
+
+       See list of known caveats in the file pimd/CAVEATS.
+
+SUPPORT
+
+       Please post comments, questions, patches, bug reports at the
+       support site:
+
+        https://github.com/udhos/qpimd
+
+RELATED WORK
+
+       igmprt: An IGMPv3-router implementation
+       - http://www.loria.fr/~lahmadi/igmpv3-router.html
+
+       USC pimd: PIMv2-SM daemon
+       - http://netweb.usc.edu/pim/pimd (URL broken in 2008-12-23)
+       - http://packages.debian.org/source/sid/pimd (from Debian)
+
+       troglobit pimd: This is the original USC pimd from
+       http://netweb.usc.edu/pim/. In January 16, 2010 it was revived
+       with the intention to collect patches floating around in
+       Debian, Gentoo, Lintrack and other distribution repositories
+       and to provide a central point of collaboration.
+       - http://github.com/troglobit/pimd
+
+       zpimd: zpimd is not dependent of zebra or any other routing daemon
+       - ftp://robur.slu.se/pub/Routing/Zebra
+       - http://sunsite2.icm.edu.pl/pub/unix/routing/zpimd
+
+       mrd6: an IPv6 Multicast Router for Linux systems
+       - http://fivebits.net/proj/mrd6/
+
+       MBGP: Implementation of RFC 2858 for Quagga
+       - git://git.coplanar.net/~balajig/quagga
+       - http://www.gossamer-threads.com/lists/quagga/dev/18000
+
+REFERENCES
+
+       IANA Protocol Independent Multicast (PIM) Parameters
+       http://www.iana.org/assignments/pim-parameters/pim-parameters.txt
+
+       Address Family Numbers
+       http://www.iana.org/assignments/address-family-numbers
+
+                              -- END --
diff --git a/pimd/TODO b/pimd/TODO
new file mode 100644 (file)
index 0000000..2308573
--- /dev/null
+++ b/pimd/TODO
@@ -0,0 +1,426 @@
+# $QuaggaId: $Format:%an, %ai, %h$ $
+
+T1 DONE Implement debug command
+   test pim receive join
+
+T2 DONE Implement debug command
+   test pim receive prune
+
+T3 DONE Per-interface Downstream (S,G) state machine
+   (RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages)
+
+T4 DONE Upstream (S,G) state machine
+   (RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages)
+
+T5 DONE Verify Data Packet Forwarding Rules
+   RFC 4601 4.2.  Data Packet Forwarding Rules
+   RFC 4601 4.8.2.  PIM-SSM-Only Routers
+
+   Additionally, the Packet forwarding rules of Section 4.2 can be
+   simplified in a PIM-SSM-only router:
+
+     iif is the incoming interface of the packet.
+     oiflist = NULL
+     if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
+       oiflist = inherited_olist(S,G)
+     } else if (iif is in inherited_olist(S,G)) {
+       send Assert(S,G) on iif
+     }
+     oiflist = oiflist (-) iif
+     forward packet on all interfaces in oiflist
+
+   Macro:
+     inherited_olist(S,G) =
+       joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1).
+   Changes in pim_ifchannel.ifassert_winner should trigger
+   pim_upstream_update_join_desired().
+   Depends on TODO T27.
+   Depends on TODO T33.
+   See also CAVEAT C7.
+   See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
+    Transitions from Joined State
+     RPF'(S,G) changes due to an Assert
+
+   http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html:
+
+     The PIM Assert mechanism is used to shutoff duplicate flows onto
+     the same multiaccess network. Routers detect this condiction when
+     they receive an (S,G) packet via a multi-access interface that is
+     in the (S,G) OIL. This causes the routers to send Assert
+     Messages.
+
+   Note that neighbors will not accept Join/Prune or Assert messages
+   from a router unless they have first heard a Hello message from that
+   router.  Thus, if a router needs to send a Join/Prune or Assert
+   message on an interface on which it has not yet sent a Hello message
+   with the currently configured IP address, then it MUST immediately
+   send the relevant Hello message without waiting for the Hello Timer
+   to expire, followed by the Join/Prune or Assert message.
+
+T7 DONE Implement hello option: LAN Prune Delay
+
+T8 DONE Implement J/P_Override_Interval(I)
+   Depends on TODO T7.
+   See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval.
+
+T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update.
+   channel_oil vif index accordingly ?
+   Beware accidentaly adding looped MFC entries (IIF=OIF).
+
+T10 DONE React to (S,G) join directed to another upstream address. See
+    also:
+    
+    RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Messages
+
+    If a router wishes to propagate a Join(S,G) upstream, it must also
+    watch for messages on its upstream interface from other routers on
+    that subnet, and these may modify its behavior.  If it sees a
+    Join(S,G) to the correct upstream neighbor, it should suppress its
+    own Join(S,G).  If it sees a Prune(S,G), Prune(S,G,rpt), or
+    Prune(*,G) to the correct upstream neighbor towards S, it should
+    be prepared to override that prune by scheduling a Join(S,G) to be
+    sent almost immediately.
+
+T11 DONE Review protocol modifications for SSM
+    (RFC 4601 4.8.1.  Protocol Modifications for SSM Destination
+    Addresses)
+
+T12 DONE Review updates of RPF entries.
+    FIXME pim_upstream.c send_join():
+    Currently only one upstream state is affected by detection of RPF change.
+    RPF change should affect all upstream states sharing the RPF cache.
+
+T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually
+    implemented with this strategy:
+    rpf_ifch=find_ifch(up->rpf->interface).
+    See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example.
+
+    $ grep -i macro pimd/*.c
+    pimd/pim_iface.c:  RFC 4601: 4.1.6.  State Summarization Macros
+    pimd/pim_ifchannel.c:    RFC 4601: 4.6.5.  Assert State Macros
+    pimd/pim_ifchannel.c:  RFC 4601: 4.1.6.  State Summarization Macros
+    pimd/pim_ifchannel.c:  RFC 4601: 4.1.6.  State Summarization Macros
+    pimd/pim_ifchannel.c:  RFC 4601: 4.6.5.  Assert State Macros
+    pimd/pim_ifchannel.c:  Macro:
+    pimd/pim_rpf.c:  RFC 4601: 4.1.6.  State Summarization Macros
+
+T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
+    See pim_mroute.c mroute_msg().
+
+T15 DONE Interface command to statically join (S,G).
+    interface eth0
+     ip igmp join-group 239.1.1.1 source 1.1.1.1
+
+T16 DONE RPF'(S,G) lookup is not working for S reachable with default route.
+    See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c.
+    Zebra daemon RIB is not reflecting changes in kernel routes
+    accurately?
+
+T17 DONE Prevent CLI from creating bogus interfaces.
+    Example:
+    conf t
+     interface xxx
+
+T18 Consider reliable pim solution (refresh reduction)
+    A Reliable Transport Mechanism for PIM
+    http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
+    PORT=PIM-Over-Reliable-Transport
+
+T19 DONE Fix self as neighbor 
+    See mailing list post:
+    http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
+
+T20 DONE Fix debug message: "pim_neighbor_update: internal error:
+    trying to replace same prefix list"
+    See mailing list post:
+    http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
+
+T21 DONE Clean-up PIM/IGMP interface mismatch debugging
+    See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am
+    See mailing list post:
+    http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html
+
+T22 DONE IGMP must be protected against adding looped MFC entries
+    created by both source and receiver attached to the same
+    interface.
+
+T23 DONE libzebra crash after zclient_lookup_nexthop.
+    See mailing list post:
+    http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html
+
+T24 DONE zserv may return recursive routes:
+     - nexthop type is set to ZEBRA_NEXTHOP_IPV4
+     - ifindex is not reported
+     - calls expecting ifindex (fib_lookup_if_vif_index) are disrupted
+    See also this mailing list post:
+    [PATCH 21/21] Link detect and recursive routes
+    http://www.gossamer-threads.com/lists/quagga/dev/17564
+
+T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32
+    See also:
+    pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32
+    zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32
+
+T26 DONE Zebra daemon is marking recursive static route as inactive.
+
+    FIXED: zebra daemon was incorrectly marking recursive routes
+    pointing to kernel routes as inactive:
+      zebra/zebra_rib.c nexthop_active_ipv4:
+        -- Original:
+         else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
+        -- Fixed:
+         else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) ||
+                  match->type == ZEBRA_ROUTE_KERNEL)
+
+    Old problem description:
+
+    This prevents rib_match_ipv4 from returning its nexthop:
+    client: pim_zlookup.c zclient_read_nexthop
+    server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4
+
+    Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4
+    Examples:
+    rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
+    rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
+
+    This patch didn't fix the issue:
+    [PATCH 21/21] Link detect and recursive routes
+    http://www.gossamer-threads.com/lists/quagga/dev/17564
+
+    See the example below for the route 2.2.2.2.
+
+bash# route add -host 1.1.1.1 gw 127.0.0.1
+bash# route add -host 2.2.2.2 gw 1.1.1.1
+bash# netstat -nvr
+Kernel IP routing table
+Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
+2.2.2.2         1.1.1.1         255.255.255.255 UGH       0 0          0 lo
+1.1.1.1         127.0.0.1       255.255.255.255 UGH       0 0          0 lo
+192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 eth0
+0.0.0.0         192.168.0.2     0.0.0.0         UG        0 0          0 eth0
+bash# 
+
+zebra# sh ip route         
+Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
+       I - ISIS, B - BGP, > - selected route, * - FIB route
+
+K>* 0.0.0.0/0 via 192.168.0.2, eth0
+K>* 1.1.1.1/32 via 127.0.0.1, lo
+K * 2.2.2.2/32 via 1.1.1.1, lo inactive
+C>* 127.0.0.0/8 is directly connected, lo
+C>* 192.168.0.0/24 is directly connected, eth0
+
+quagga-pimd-router# sh ip route 1.1.1.1
+Address         NextHop         Interface Metric Preference
+1.1.1.1         127.0.0.1       lo             0          0
+quagga-pimd-router# 
+quagga-pimd-router# sh ip route 2.2.2.2
+Address         NextHop         Interface Metric Preference
+2.2.2.2         192.168.0.2     eth0           0          0
+quagga-pimd-router# 
+
+T27 DONE Implement debug command
+    test pim receive assert
+    See also TODO T6: (S,G) Assert state machine.
+
+T28 DONE Bad IPv4 address family=02 in Join/Prune dump
+    Reported by Andrew Lunn <andrew.lunn@ascom.ch>
+    
+    # 58-byte pim v2 Join/Prune dump
+    # ------------------------------
+    # IPv4 address family=02 is wrong, correct IPv4 address family is 01
+    # See http://www.iana.org/assignments/address-family-numbers
+    #
+    c8XX YY03 : ip src 200.xx.yy.3
+    e000 000d : ip dst 224.0.0.13
+    9404 0000 : ip router alert option 148.4.0.0
+    2300 ab13 : pimv2,type=3 res=00 checksum=ab13
+    0200      : upstream family=02, encoding=00
+    c8XX YY08 : upstream 200.xx.yy.8
+    0001 00d2 : res=00 groups=01 holdtime=00d2
+    0200 0020 : group family=02, encoding=00, res=00, mask_len=20
+    ef01 0101 : group address 239.1.1.1
+    0001 0000 : joined=0001 pruned=0000
+    0200 0020 : source family=02, encoding=00, res=00, mask_len=20
+    0101 0101 : source address 1.1.1.1
+
+T29 DONE Reset interface PIM-hello-sent counter when primary address changes
+    See pim_ifp->pim_ifstat_hello_sent
+
+    RFC 4601: 4.3.1.  Sending Hello Messages
+
+    Thus, if a router needs to send a Join/Prune or Assert message on
+    an interface on which it has not yet sent a Hello message with the
+    currently configured IP address, then it MUST immediately send the
+    relevant Hello message without waiting for the Hello Timer to
+    expire, followed by the Join/Prune or Assert message.
+
+T30 DONE Run interface DR election when primary address changes
+    Reported by Andrew Lunn <andrew.lunn@ascom.ch>
+    See pim_if_dr_election().
+
+T31 If an interface changes one of its secondary IP addresses, a Hello
+    message with an updated Address_List option and a non-zero
+    HoldTime should be sent immediately.
+    See also detect_secondary_address_change
+    See also CAVEAT C15.
+    See also RFC 4601: 4.3.1.  Sending Hello Messages
+
+T32 FIXED Detection of interface primary address changes may fail when
+    there are multiple addresses.
+    See also CAVEAT C14.
+
+    pim_find_primary_addr() should return interface primary address
+    from connected list. Currently it returns the first address.
+
+    Zebra daemon "show int" is able to keep the primary address as
+    first address.
+
+T33 DONE Implement debug command: test pim receive upcall
+    See also TODO T6: (S,G) Assert state machine.
+
+T34 DONE assert_action_a1
+
+T35 DONE Review macros depending on interface I.
+
+    See also: grep ,I\) pimd/*.c
+
+    For the case (S,G,I) check if I is either
+    1) interface attached to this per-interface S,G state (don't think so)
+    or
+    2) an arbitrary interface (most probably)
+
+    For the arbitrary interface case (2), consider representing
+    interface ifp as its primary address (struct in_addr ifaddr).  The
+    benefit is in_addr does not need to be dereferenced, so it does
+    not demand protection against crashes.
+
+T36 DONE React to zebra daemon link-detect up/down notification.
+    pim_ifp->primary_address is managed by detect_primary_address_change()
+    depending on to ifp->connected (managed by zebra_interface_address_read()).
+
+T37 DONE Review list of variables which may affect pim_upstream.c
+    pim_upstream_evaluate_join_desired().
+    Call pim_upstream_update_join_desired() accordingly.
+
+    See the order of invokation:
+      pim_if_dr_election(ifp);
+      pim_if_update_join_desired(pim_ifp); /* depends on DR */
+      pim_if_update_could_assert(ifp); /* depends on DR */
+      pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
+
+    join_desired depends on:
+      pim_ifp->primary_address
+      pim_ifp->pim_dr_addr
+      ch->ifassert_winner_metric
+      ch->ifassert_winner
+      ch->local_ifmembership 
+      ch->ifjoin_state
+      ch->upstream->rpf.source_nexthop.mrib_metric_preference
+      ch->upstream->rpf.source_nexthop.mrib_route_metric
+      ch->upstream->rpf.source_nexthop.interface
+
+T38 DONE Detect change in AssertTrackingDesired(S,G,I)
+
+    See the order of invokation:
+      dr_election: none
+      update_join_desired: depends on DR
+      update_tracking_desired: depends on DR, join_desired
+
+    AssertTrackingDesired(S,G,I) depends on:
+      pim_ifp->primary_address
+      pim_ifp->pim_dr_addr
+      ch->local_ifmembership
+      ch->ifassert_winner
+      ch->ifjoin_state
+      ch->upstream->rpf.source_nexthop.interface
+      PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)
+
+T39 DONE AssertTrackingDesired: flags is not matching evaluation
+    
+    # show ip pim assert-internal 
+    CA:   CouldAssert
+    ECA:  Evaluate CouldAssert
+    ATD:  AssertTrackingDesired
+    eATD: Evaluate AssertTrackingDesired
+
+    Interface Address         Source          Group           CA  eCA ATD eATD
+    eth0      192.168.1.100   1.1.1.1         239.1.1.1       no  no  no  yes 
+    # 
+
+T40 Lightweight MLDv2
+    http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
+    http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
+    http://www.ietf.org/html.charters/mboned-charter.html
+
+T41 DONE ssmping support
+
+    See also:
+      http://www.venaas.no/multicast/ssmping/
+      draft-ietf-mboned-ssmping-07
+      http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07
+
+    Example:
+
+    debug ssmpingd
+    
+    conf t
+     ip ssmpingd 1.1.1.1
+    
+    show ip ssmpingd
+
+T42 Static igmp join fails when loading config at boot time
+
+    ! Wrong behavior seen at boot time:
+    !
+    2010/02/22 08:59:00 PIM: igmp_source_forward_start: ignoring request for
+    looped MFC entry (S,G)=(3.3.3.3,239.3.3.3): igmp_sock=12 oif=eth0 vif_index=2
+
+    ! Correct behavior seen later:
+    !
+    2010/02/22 09:03:16 PIM: igmp_source_forward_start: ignoring request for
+    looped MFC entry (S,G)=(2.2.2.2,239.2.2.2): igmp_sock=17 oif=lo vif_index=1
+
+    ! To see the wrong message at boot:    
+    !
+    debug igmp trace
+    !
+    interface lo
+     ip igmp
+     ip igmp join 239.2.2.2 2.2.2.2
+     ip igmp join 239.3.3.3 3.3.3.3
+    !
+
+    ! Interfaces indexes:
+    Interface Address         ifi Vif  PktsIn PktsOut    BytesIn   BytesOut
+    eth0      200.202.112.3     2   2       0       0          0          0
+    lo        127.0.0.1         1   1       0       0          0          0
+
+T43 PIM Neighbor Reduction
+    https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/
+    
+    "In a transit LAN (no directly connected source or receiver), many
+    of the PIM procedures don't apply. (...) This proposal describes
+    a procedure to reduce the amount of neighbors established over a
+    transit LAN."
+
+T44 Single Stream Multicast Fast Reroute (SMFR) Method
+    https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/
+
+    "This document proposes an IP multicast fast convergence method
+    based on differentiating primary and backup PIM join."
+
+T45 RFC5384 - The Join Attribute Format
+    "This document describes a modification of the Join message that
+    allows a node to associate attributes with a particular tree."
+
+T46 PIM Multi-Topology ID (MT-ID) Join-Attribute
+    http://tools.ietf.org/html/draft-cai-pim-mtid-00
+    Depends on T45.
+
+    "This draft introduces a new type of PIM Join Attribute used to
+    encode the identity of the topology PIM uses for RPF."
+    
+-x-
diff --git a/pimd/TROUBLESHOOTING b/pimd/TROUBLESHOOTING
new file mode 100644 (file)
index 0000000..7d1f52d
--- /dev/null
@@ -0,0 +1,33 @@
+TROUBLESHOOTING
+
+# Check kernel mcast cache
+# On Linux:
+ip mroute show
+
+! qpimd on last-hop router
+! . attached to mcast receiver
+! . runnning both PIM-SSM and IGMPv3
+!
+show ip mroute         (kernel mcast programming is correct?)
+show ip pim upstream   (we joined our upstream?)
+show ip pim neighbor   (upstream is neighbor?)
+show ip pim interface  (pim enabled on interfaces?)
+show ip multicast      (multicast enabled at all?)
+show ip rib SRC        (unicast route towards source?)
+
+show ip igmp sources   (receiver joined on interface?)
+show ip igmp interface (igmp enabled on receiver interface?)
+
+! qpimd on intermmediate routers
+! . may be attached to mcast source
+! . runnning only PIM-SSM, not IGMPv3
+!
+show ip mroute         (kernel mcast programming is correct?)
+show ip pim upstream   (we joined our upstream?)
+show ip pim join       (downstream joined us?)
+show ip pim neighbor   (downstream is neighbor?)
+show ip pim interface  (pim enabled on interfaces?)
+show ip multicast      (multicast enabled at all?)
+show ip rib SRC        (unicast route towards source?)
+
+--EOF--
diff --git a/pimd/WHY_SSM b/pimd/WHY_SSM
new file mode 100644 (file)
index 0000000..2e8c966
--- /dev/null
@@ -0,0 +1,32 @@
+WHY SSM
+
+Benefis of PIM SSM over PIM SM
+------------------------------
+
+- SSM consumes minimum link bandwidth
+- SSM simplifies multicast address management (specially important for
+  inter-domain multicast)
+  - SSM (S,G) channels easily provide unique per-application addressing
+  - SSM does not require MSDP between PIM domains
+- SSM does not suffer instabilities from traffic-driven SPT switchover
+- SSM is not suscetible to DoS attack from unwanted sources
+- SSM does not use RP. Some RP issues:
+  - RP is possible point of failure
+  - RP demands redundancy management
+  - RP may require PIM dense mode support for RP election
+  - RP is possible performance bottleneck
+  - RP may demand lots of extra management
+- SSM can be deployed in an existing PIM SM network (only the last hop
+  routers need to support IGMPv3)
+- SSM is easier to deploy and maintain
+
+PIM-SSM drawbacks
+-----------------
+
+- SSM requires IGMPv3 support on both receivers and last-hop routers
+- SSM may be memory intensive when managing (S,G) states for
+  many-to-many multicast distribution
+- SSM will keep (S,G) state as long as there are subscriptions from
+  receivers, even if the source is not actually sending traffic
+
+--EOF--
diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c
new file mode 100644 (file)
index 0000000..ad21e08
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_int.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_macro.h"
+#include "pim_assert.h"
+#include "pim_ifchannel.h"
+
+static int assert_action_a3(struct pim_ifchannel *ch);
+static void assert_action_a2(struct pim_ifchannel *ch,
+                            struct pim_assert_metric winner_metric);
+static void assert_action_a6(struct pim_ifchannel *ch,
+                            struct pim_assert_metric winner_metric);
+
+void pim_ifassert_winner_set(struct pim_ifchannel     *ch,
+                            enum pim_ifassert_state   new_state,
+                            struct in_addr            winner,
+                            struct pim_assert_metric  winner_metric)
+{
+  int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
+  int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
+                                               &winner_metric);
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    if (ch->ifassert_state != new_state) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str,
+               pim_ifchannel_ifassert_name(ch->ifassert_state),
+               pim_ifchannel_ifassert_name(new_state),
+               ch->interface->name);
+    }
+
+    if (winner_changed) {
+      char src_str[100];
+      char grp_str[100];
+      char was_str[100];
+      char winner_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
+      pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
+      zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str,
+               was_str, winner_str, ch->interface->name);
+    }
+  } /* PIM_DEBUG_PIM_EVENTS */
+
+  ch->ifassert_state         = new_state;
+  ch->ifassert_winner        = winner;
+  ch->ifassert_winner_metric = winner_metric;
+  ch->ifassert_creation      = pim_time_monotonic_sec();
+
+  if (winner_changed || metric_changed) {
+    pim_upstream_update_join_desired(ch->upstream);
+    pim_ifchannel_update_could_assert(ch);
+    pim_ifchannel_update_assert_tracking_desired(ch);
+  }
+}
+
+static void on_trace(const char *label,
+                    struct interface *ifp, struct in_addr src)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+    zlog_debug("%s: from %s on %s",
+              label, src_str, ifp->name);
+  }
+}
+
+static int preferred_assert(const struct pim_ifchannel *ch,
+                           const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(recv_metric,
+                                 &ch->ifassert_winner_metric);
+}
+
+static int acceptable_assert(const struct pim_assert_metric *my_metric,
+                            const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(recv_metric,
+                                 my_metric);
+}
+
+static int inferior_assert(const struct pim_assert_metric *my_metric,
+                          const struct pim_assert_metric *recv_metric)
+{
+  return pim_assert_metric_better(my_metric,
+                                 recv_metric);
+}
+
+static int cancel_assert(const struct pim_assert_metric *recv_metric)
+{
+  return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+    &&
+    (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
+}
+
+static void if_could_assert_do_a1(const char *caller,
+                                 struct pim_ifchannel *ch)
+{
+  if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+    if (assert_action_a1(ch)) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+               __PRETTY_FUNCTION__, caller,
+               src_str, grp_str, ch->interface->name);
+      /* log warning only */
+    }
+  }
+}
+
+static int dispatch_assert(struct interface *ifp,
+                          struct in_addr source_addr,
+                          struct in_addr group_addr,
+                          struct pim_assert_metric recv_metric)
+{
+  struct pim_ifchannel *ch;
+
+  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  if (!ch) {
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
+             __PRETTY_FUNCTION__,
+             source_str, group_str, ifp->name);
+    return -1;
+  }
+
+  switch (ch->ifassert_state) {
+  case PIM_IFASSERT_NOINFO:
+    if (recv_metric.rpt_bit_flag) {
+      /* RPT bit set */
+      if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
+    }
+    else {
+      /* RPT bit clear */
+      if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+       if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
+      }
+      else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
+       if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
+         assert_action_a6(ch, recv_metric);
+       }
+      }
+    }
+    break;
+  case PIM_IFASSERT_I_AM_WINNER:
+    if (preferred_assert(ch, &recv_metric)) {
+      assert_action_a2(ch, recv_metric);
+    }
+    else {
+      if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+       zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+       assert_action_a3(ch);
+      }
+    }
+    break;
+  case PIM_IFASSERT_I_AM_LOSER:
+    if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
+      /* Assert from current winner */
+
+      if (cancel_assert(&recv_metric)) {
+       assert_action_a5(ch);
+      }
+      else {
+       if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
+         assert_action_a5(ch);
+       }
+       else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
+         if (!recv_metric.rpt_bit_flag) {
+           assert_action_a2(ch, recv_metric);
+         }
+       }
+      }
+    }
+    else if (preferred_assert(ch, &recv_metric)) {
+      assert_action_a2(ch, recv_metric);
+    }
+    break;
+  default:
+    {
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+      pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+               __PRETTY_FUNCTION__,
+               source_str, group_str, ch->ifassert_state, ifp->name);
+    }
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_assert_recv(struct interface *ifp,
+                   struct pim_neighbor *neigh,
+                   struct in_addr src_addr,
+                   uint8_t *buf, int buf_size)
+{
+  struct prefix            msg_group_addr;
+  struct prefix            msg_source_addr;
+  struct pim_assert_metric msg_metric;
+  int offset;
+  uint8_t *curr;
+  int curr_size;
+
+  on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+  curr      = buf;
+  curr_size = buf_size;
+
+  /*
+    Parse assert group addr
+   */
+  offset = pim_parse_addr_group(ifp->name, src_addr,
+                               &msg_group_addr,
+                               curr, curr_size);
+  if (offset < 1) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, ifp->name);
+    return -1;
+  }
+  curr      += offset;
+  curr_size -= offset;
+
+  /*
+    Parse assert source addr
+  */
+  offset = pim_parse_addr_ucast(ifp->name, src_addr,
+                               &msg_source_addr,
+                               curr, curr_size);
+  if (offset < 1) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, ifp->name);
+    return -2;
+  }
+  curr      += offset;
+  curr_size -= offset;
+
+  if (curr_size != 8) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
+             __PRETTY_FUNCTION__,
+             curr_size,
+             src_str, ifp->name);
+    return -3;
+  }
+
+  /*
+    Parse assert metric preference
+  */
+
+  msg_metric.metric_preference = pim_read_uint32_host(curr);
+
+  msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
+  msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
+
+  curr += 4;
+
+  /*
+    Parse assert route metric
+  */
+
+  msg_metric.route_metric = pim_read_uint32_host(curr);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char neigh_str[100];
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
+    pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
+    zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+              __PRETTY_FUNCTION__, neigh_str, ifp->name,
+              source_str, group_str,
+              msg_metric.metric_preference,
+              msg_metric.route_metric,
+              PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
+  }
+
+  msg_metric.ip_address = src_addr;
+
+  return dispatch_assert(ifp,
+                        msg_source_addr.u.prefix4,
+                        msg_group_addr.u.prefix4,
+                        msg_metric);
+}
+
+/*
+  RFC 4601: 4.6.3.  Assert Metrics
+
+   Assert metrics are defined as:
+
+   When comparing assert_metrics, the rpt_bit_flag, metric_preference,
+   and route_metric field are compared in order, where the first lower
+   value wins.  If all fields are equal, the primary IP address of the
+   router that sourced the Assert message is used as a tie-breaker,
+   with the highest IP address winning.
+*/
+int pim_assert_metric_better(const struct pim_assert_metric *m1,
+                            const struct pim_assert_metric *m2)
+{
+  if (m1->rpt_bit_flag < m2->rpt_bit_flag)
+    return 1;
+  if (m1->rpt_bit_flag > m2->rpt_bit_flag)
+    return 0;
+
+  if (m1->metric_preference < m2->metric_preference)
+    return 1;
+  if (m1->metric_preference > m2->metric_preference)
+    return 0;
+
+  if (m1->route_metric < m2->route_metric)
+    return 1;
+  if (m1->route_metric > m2->route_metric)
+    return 0;
+
+  return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
+}
+
+int pim_assert_metric_match(const struct pim_assert_metric *m1,
+                           const struct pim_assert_metric *m2)
+{
+  if (m1->rpt_bit_flag != m2->rpt_bit_flag)
+    return 0;
+  if (m1->metric_preference != m2->metric_preference)
+    return 0;
+  if (m1->route_metric != m2->route_metric)
+    return 0;
+  
+  return m1->ip_address.s_addr == m2->ip_address.s_addr;
+}
+
+int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
+                        struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr,
+                        uint32_t metric_preference,
+                        uint32_t route_metric,
+                        uint32_t rpt_bit_flag)
+{
+  uint8_t *buf_pastend = pim_msg + buf_size;
+  uint8_t *pim_msg_curr;
+  int pim_msg_size;
+  int remain;
+
+  pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
+
+  /* Encode group */
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+                                               remain,
+                                               group_addr);
+  if (!pim_msg_curr) {
+    char group_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: failure encoding group address %s: space left=%d",
+             __PRETTY_FUNCTION__, group_str, remain);
+    return -1;
+  }
+
+  /* Encode source */
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+                                               remain,
+                                               source_addr);
+  if (!pim_msg_curr) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure encoding source address %s: space left=%d",
+             __PRETTY_FUNCTION__, source_str, remain);
+    return -2;
+  }
+
+  /* Metric preference */
+  pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
+                  metric_preference | 0x80000000 :
+                  metric_preference);
+  pim_msg_curr += 4;
+
+  /* Route metric */
+  pim_write_uint32(pim_msg_curr, route_metric);
+  pim_msg_curr += 4;
+
+  /*
+    Add PIM header
+  */
+  pim_msg_size = pim_msg_curr - pim_msg;
+  pim_msg_build_header(pim_msg, pim_msg_size,
+                      PIM_MSG_TYPE_ASSERT);
+
+  return pim_msg_size;
+}
+
+static int pim_assert_do(struct pim_ifchannel *ch,
+                        struct pim_assert_metric metric)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  uint8_t pim_msg[1000];
+  int pim_msg_size;
+
+  ifp = ch->interface;
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: pim not enabled on interface: %s",
+             __PRETTY_FUNCTION__, ifp->name);
+    return -1;
+  }
+
+  pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
+                                     ch->group_addr, ch->source_addr,
+                                     metric.metric_preference,
+                                     metric.route_metric,
+                                     metric.rpt_bit_flag);
+  if (pim_msg_size < 1) {
+    zlog_warn("%s: failure building PIM assert message: msg_size=%d",
+             __PRETTY_FUNCTION__, pim_msg_size);
+    return -2;
+  }
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+    
+    Thus, if a router needs to send a Join/Prune or Assert message on
+    an interface on which it has not yet sent a Hello message with the
+    currently configured IP address, then it MUST immediately send the
+    relevant Hello message without waiting for the Hello Timer to
+    expire, followed by the Join/Prune or Assert message.
+  */
+  pim_hello_require(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char source_str[100];
+    char group_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
+              __PRETTY_FUNCTION__, 
+              ifp->name, source_str, group_str,
+              metric.metric_preference,
+              metric.route_metric,
+              PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
+  }
+
+  if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  qpim_all_pim_routers_addr,
+                  pim_msg,
+                  pim_msg_size,
+                  ifp->name)) {
+    zlog_warn("%s: could not send PIM message on interface %s",
+             __PRETTY_FUNCTION__, ifp->name);
+    return -3;
+  }
+
+  return 0;
+}
+
+int pim_assert_send(struct pim_ifchannel *ch)
+{
+  return pim_assert_do(ch, ch->ifassert_my_metric);
+}
+
+/*
+  RFC 4601: 4.6.4.  AssertCancel Messages
+
+  An AssertCancel(S,G) is an infinite metric assert with the RPT bit
+  set that names S as the source.
+ */
+static int pim_assert_cancel(struct pim_ifchannel *ch)
+{
+  struct pim_assert_metric metric;
+
+  metric.rpt_bit_flag      = 0;
+  metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
+  metric.route_metric      = PIM_ASSERT_ROUTE_METRIC_MAX;
+  metric.ip_address        = ch->source_addr;
+
+  return pim_assert_do(ch, metric);
+}
+
+static int on_assert_timer(struct thread *t)
+{
+  struct pim_ifchannel *ch;
+  struct interface *ifp;
+
+  zassert(t);
+  ch = THREAD_ARG(t);
+  zassert(ch);
+
+  ifp = ch->interface;
+  zassert(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str, ifp->name);
+  }
+
+  ch->t_ifassert_timer = 0;
+
+  switch (ch->ifassert_state) {
+  case PIM_IFASSERT_I_AM_WINNER:
+    zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+    assert_action_a3(ch);
+    break;
+  case PIM_IFASSERT_I_AM_LOSER:
+    assert_action_a5(ch);
+    break;
+  default:
+    {
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
+               __PRETTY_FUNCTION__,
+               source_str, group_str, ch->ifassert_state, ifp->name);
+    }
+  }
+
+  return 0;
+}
+
+static void assert_timer_off(struct pim_ifchannel *ch)
+{
+  struct interface *ifp;
+
+  zassert(ch);
+  ifp = ch->interface;
+  zassert(ifp);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (ch->t_ifassert_timer) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
+                __PRETTY_FUNCTION__,
+                src_str, grp_str, ifp->name);
+    }
+  }
+  THREAD_OFF(ch->t_ifassert_timer);
+  zassert(!ch->t_ifassert_timer);
+}
+
+static void pim_assert_timer_set(struct pim_ifchannel *ch,
+                                int interval)
+{
+  struct interface *ifp;
+
+  zassert(ch);
+  ifp = ch->interface;
+  zassert(ifp);
+
+  assert_timer_off(ch);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str, interval, ifp->name);
+  }
+
+  THREAD_TIMER_ON(master, ch->t_ifassert_timer,
+                 on_assert_timer,
+                 ch, interval);
+}
+
+static void pim_assert_timer_reset(struct pim_ifchannel *ch)
+{
+  pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A1:  Send Assert(S,G).
+  Set Assert Timer to (Assert_Time - Assert_Override_Interval).
+  Store self as AssertWinner(S,G,I).
+  Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
+*/
+int assert_action_a1(struct pim_ifchannel *ch)
+{
+  struct interface *ifp = ch->interface;
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ifp->name);
+    return -1; /* must return since pim_ifp is used below */
+  }
+
+  /* Switch to I_AM_WINNER before performing action_a3 below */
+  pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
+                         pim_ifp->primary_address,
+                         pim_macro_spt_assert_metric(&ch->upstream->rpf,
+                                                     pim_ifp->primary_address));
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
+  if (assert_action_a3(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ifp->name);
+    /* warning only */
+  }
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  return 0;
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A2:  Store new assert winner as AssertWinner(S,G,I) and assert
+          winner metric as AssertWinnerMetric(S,G,I).
+          Set Assert Timer to Assert_Time.
+*/
+static void assert_action_a2(struct pim_ifchannel *ch,
+                            struct pim_assert_metric winner_metric)
+{
+  pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
+                         winner_metric.ip_address,
+                         winner_metric);
+  
+  pim_assert_timer_set(ch, PIM_ASSERT_TIME);
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A3:  Send Assert(S,G).
+  Set Assert Timer to (Assert_Time - Assert_Override_Interval).
+*/
+static int assert_action_a3(struct pim_ifchannel *ch)
+{
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  pim_assert_timer_reset(ch);
+
+  if (pim_assert_send(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+
+    zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ch->interface->name);
+    return -1;
+  }
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
+
+  return 0;
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A4:  Send AssertCancel(S,G).
+          Delete assert info (AssertWinner(S,G,I) and
+          AssertWinnerMetric(S,G,I) will then return their default
+          values).
+*/
+void assert_action_a4(struct pim_ifchannel *ch)
+{
+  if (pim_assert_cancel(ch)) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ch->interface->name);
+    /* log warning only */
+  }
+
+  assert_action_a5(ch);
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+  A5: Delete assert info (AssertWinner(S,G,I) and
+  AssertWinnerMetric(S,G,I) will then return their default values).
+*/
+void assert_action_a5(struct pim_ifchannel *ch)
+{
+  reset_ifassert_state(ch);
+  zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  (S,G) Assert State machine Actions
+
+     A6:  Store new assert winner as AssertWinner(S,G,I) and assert
+          winner metric as AssertWinnerMetric(S,G,I).
+          Set Assert Timer to Assert_Time.
+          If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
+          set SPTbit(S,G) to TRUE.
+*/
+static void assert_action_a6(struct pim_ifchannel *ch,
+                            struct pim_assert_metric winner_metric)
+{
+  assert_action_a2(ch, winner_metric);
+
+  /*
+    If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
+    SPTbit(S,G) to TRUE.
+    
+    Notice: For PIM SSM, SPTbit(S,G) is already always true.
+  */
+
+  zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
+}
+
diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h
new file mode 100644 (file)
index 0000000..bd3fb3e
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_ASSERT_H
+#define PIM_ASSERT_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_neighbor.h"
+#include "pim_ifchannel.h"
+
+/*
+  RFC 4601: 4.11.  Timer Values
+
+  Note that for historical reasons, the Assert message lacks a
+  Holdtime field.  Thus, changing the Assert Time from the default
+  value is not recommended.
+ */
+#define PIM_ASSERT_OVERRIDE_INTERVAL (3)   /* seconds */
+#define PIM_ASSERT_TIME              (180) /* seconds */
+
+#define PIM_ASSERT_METRIC_PREFERENCE_MAX (0xFFFFFFFF)
+#define PIM_ASSERT_ROUTE_METRIC_MAX      (0xFFFFFFFF)
+
+void pim_ifassert_winner_set(struct pim_ifchannel     *ch,
+                            enum pim_ifassert_state   new_state,
+                            struct in_addr            winner,
+                            struct pim_assert_metric  winner_metric);
+
+int pim_assert_recv(struct interface *ifp,
+                   struct pim_neighbor *neigh,
+                   struct in_addr src_addr,
+                   uint8_t *buf, int buf_size);
+
+int pim_assert_metric_better(const struct pim_assert_metric *m1,
+                            const struct pim_assert_metric *m2);
+int pim_assert_metric_match(const struct pim_assert_metric *m1,
+                           const struct pim_assert_metric *m2);
+
+int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
+                        struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr,
+                        uint32_t metric_preference,
+                        uint32_t route_metric,
+                        uint32_t rpt_bit_flag);
+
+int pim_assert_send(struct pim_ifchannel *ch);
+
+int assert_action_a1(struct pim_ifchannel *ch);
+void assert_action_a4(struct pim_ifchannel *ch);
+void assert_action_a5(struct pim_ifchannel *ch);
+
+#endif /* PIM_ASSERT_H */
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
new file mode 100644 (file)
index 0000000..749eeab
--- /dev/null
@@ -0,0 +1,4951 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <sys/ioctl.h>
+
+#include "command.h"
+#include "if.h"
+#include "prefix.h"
+#include "zclient.h"
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_iface.h"
+#include "pim_vty.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_igmp.h"
+#include "pim_igmpv3.h"
+#include "pim_sock.h"
+#include "pim_time.h"
+#include "pim_util.h"
+#include "pim_oil.h"
+#include "pim_neighbor.h"
+#include "pim_pim.h"
+#include "pim_ifchannel.h"
+#include "pim_hello.h"
+#include "pim_msg.h"
+#include "pim_upstream.h"
+#include "pim_rpf.h"
+#include "pim_macro.h"
+#include "pim_ssmpingd.h"
+#include "pim_zebra.h"
+#include "pim_static.h"
+
+static struct cmd_node pim_global_node = {
+  PIM_NODE,
+  "",
+  1 /* vtysh ? yes */
+};
+
+static struct cmd_node interface_node = {
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1 /* vtysh ? yes */
+};
+
+static void pim_if_membership_clear(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (PIM_IF_TEST_PIM(pim_ifp->options) &&
+      PIM_IF_TEST_IGMP(pim_ifp->options)) {
+    return;
+  }
+
+  pim_ifchannel_membership_clear(ifp);
+}
+
+/*
+  When PIM is disabled on interface, IGMPv3 local membership
+  information is not injected into PIM interface state.
+
+  The function pim_if_membership_refresh() fetches all IGMPv3 local
+  membership information into PIM. It is intented to be called
+  whenever PIM is enabled on the interface in order to collect missed
+  local membership information.
+ */
+static void pim_if_membership_refresh(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *sock_node;
+  struct igmp_sock     *igmp;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (!PIM_IF_TEST_PIM(pim_ifp->options))
+    return;
+  if (!PIM_IF_TEST_IGMP(pim_ifp->options))
+    return;
+
+  /*
+    First clear off membership from all PIM (S,G) entries on the
+    interface
+  */
+
+  pim_ifchannel_membership_clear(ifp);
+
+  /*
+    Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on
+    the interface
+  */
+
+  /* scan igmp sockets */
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+    struct listnode   *grpnode;
+    struct igmp_group *grp;
+    
+    /* scan igmp groups */
+    for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+      struct listnode    *srcnode;
+      struct igmp_source *src;
+      
+      /* scan group sources */
+      for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+
+       if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) {
+         pim_ifchannel_local_membership_add(ifp,
+                                            src->source_addr,
+                                            grp->group_addr);
+       }
+       
+      } /* scan group sources */
+    } /* scan igmp groups */
+  } /* scan igmp sockets */
+
+  /*
+    Finally delete every PIM (S,G) entry lacking all state info
+   */
+
+  pim_ifchannel_delete_on_noinfo(ifp);
+
+}
+
+static void pim_show_assert(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "Interface Address         Source          Group           State  Winner          Uptime   Timer%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+      char winner_str[100];
+      char uptime[10];
+      char timer[10];
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+      pim_inet4_dump("<assrt_win?>", ch->ifassert_winner,
+                    winner_str, sizeof(winner_str));
+
+      pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation);
+      pim_time_timer_to_mmss(timer, sizeof(timer),
+                            ch->t_ifassert_timer);
+
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             pim_ifchannel_ifassert_name(ch->ifassert_state),
+             winner_str,
+             uptime,
+             timer,
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+}
+
+static void pim_show_assert_internal(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  vty_out(vty,
+         "CA:   CouldAssert%s"
+         "ECA:  Evaluate CouldAssert%s"
+         "ATD:  AssertTrackingDesired%s"
+         "eATD: Evaluate AssertTrackingDesired%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  vty_out(vty,
+         "Interface Address         Source          Group           CA  eCA ATD eATD%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no",
+             pim_macro_ch_could_assert_eval(ch) ? "yes" : "no",
+             PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no",
+             pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no",
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+}
+
+static void pim_show_assert_metric(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  
+  vty_out(vty,
+         "Interface Address         Source          Group           RPT Pref Metric Address        %s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+      char addr_str[100];
+      struct pim_assert_metric am;
+
+      am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+      pim_inet4_dump("<addr?>", am.ip_address,
+                    addr_str, sizeof(addr_str));
+
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             am.rpt_bit_flag ? "yes" : "no",
+             am.metric_preference,
+             am.route_metric,
+             addr_str,
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+}
+
+static void pim_show_assert_winner_metric(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  
+  vty_out(vty,
+         "Interface Address         Source          Group           RPT Pref Metric Address        %s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+      char addr_str[100];
+      struct pim_assert_metric *am;
+      char pref_str[5];
+      char metr_str[7];
+
+      am = &ch->ifassert_winner_metric;
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+      pim_inet4_dump("<addr?>", am->ip_address,
+                    addr_str, sizeof(addr_str));
+
+      if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
+       snprintf(pref_str, sizeof(pref_str), "INFI");
+      else
+       snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference);
+
+      if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX)
+       snprintf(metr_str, sizeof(metr_str), "INFI");
+      else
+       snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric);
+
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             am->rpt_bit_flag ? "yes" : "no",
+             pref_str,
+             metr_str,
+             addr_str,
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+}
+
+static void pim_show_membership(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  vty_out(vty,
+         "Interface Address         Source          Group           Membership%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ?
+             "NOINFO" : "INCLUDE",
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+
+}
+
+static void igmp_show_interfaces(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "Interface Address         ifIndex Socket Uptime   Multi Broad MLoop AllMu Prmsc Del%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct listnode *sock_node;
+    struct igmp_sock *igmp;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char uptime[10];
+      int mloop;
+
+      pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation);
+
+      mloop = pim_socket_mcastloop_get(igmp->fd);
+      
+      vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
+             ifp->name,
+             inet_ntoa(igmp->ifaddr),
+             ifp->ifindex,
+             igmp->fd,
+             uptime,
+             if_is_multicast(ifp) ? "yes" : "no",
+             if_is_broadcast(ifp) ? "yes" : "no",
+             (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
+             (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
+             (ifp->flags & IFF_PROMISC) ? "yes" : "no",
+             PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
+             VTY_NEWLINE);
+    }
+  }
+}
+
+static void igmp_show_interface_join(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "Interface Address         Source          Group           Socket Uptime  %s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct listnode *join_node;
+    struct igmp_join *ij;
+    struct in_addr pri_addr;
+    char pri_addr_str[100];
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (!pim_ifp->igmp_join_list)
+      continue;
+
+    pri_addr = pim_find_primary_addr(ifp);
+    pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) {
+      char group_str[100];
+      char source_str[100];
+      char uptime[10];
+
+      pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation);
+      pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
+      pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
+      
+      vty_out(vty, "%-9s %-15s %-15s %-15s %6d %8s%s",
+             ifp->name,
+             pri_addr_str,
+             source_str,
+             group_str,
+             ij->sock_fd,
+             uptime,
+             VTY_NEWLINE);
+    } /* for (pim_ifp->igmp_join_list) */
+
+  } /* for (iflist) */
+
+}
+
+static void show_interface_address(struct vty *vty)
+{
+  struct listnode  *ifpnode;
+  struct interface *ifp;
+  
+  vty_out(vty,
+         "Interface Primary         Secondary      %s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifpnode, ifp)) {
+    struct listnode  *ifcnode;
+    struct connected *ifc;
+    struct in_addr    pri_addr;
+    char pri_addr_str[100];
+
+    pri_addr = pim_find_primary_addr(ifp);
+
+    pim_inet4_dump("<pri?>", pri_addr, pri_addr_str, sizeof(pri_addr_str));
+    
+    for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) {
+      char sec_addr_str[100];
+      struct prefix *p = ifc->address;
+
+      if (p->family != AF_INET)
+       continue;
+
+      if (p->u.prefix4.s_addr == pri_addr.s_addr) {
+       sec_addr_str[0] = '\0';
+      }
+      else {
+       pim_inet4_dump("<sec?>", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str));
+      }
+    
+      vty_out(vty, "%-9s %-15s %-15s%s",
+             ifp->name,
+             pri_addr_str,
+             sec_addr_str,
+             VTY_NEWLINE);
+    }
+  }
+}
+
+static void pim_show_dr(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "NonPri: Number of neighbors missing DR Priority hello option%s"
+         "DrPri: Designated Router Priority sent%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  
+  vty_out(vty, "Interface Address         DR              Uptime   Elections Changes NonPri      DrPri%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    char dr_str[100];
+    char dr_uptime[10];
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime),
+                         now, pim_ifp->pim_dr_election_last);
+
+    pim_inet4_dump("<dr?>", pim_ifp->pim_dr_addr,
+                  dr_str, sizeof(dr_str));
+
+    vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d %10d%s",
+           ifp->name,
+           inet_ntoa(ifaddr),
+           dr_str,
+           dr_uptime,
+           pim_ifp->pim_dr_election_count,
+           pim_ifp->pim_dr_election_changes,
+           pim_ifp->pim_dr_num_nondrpri_neighbors,
+           pim_ifp->pim_dr_priority,
+           VTY_NEWLINE);
+  }
+}
+
+static void pim_show_hello(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+  
+  vty_out(vty, "Interface Address         Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    char hello_period[10];
+    char hello_timer[10];
+    char stat_uptime[10];
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer);
+    pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period);
+    pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start);
+
+    vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s",
+           ifp->name,
+           inet_ntoa(ifaddr),
+           hello_period,
+           hello_timer,
+           stat_uptime,
+           pim_ifp->pim_ifstat_hello_recv,
+           pim_ifp->pim_ifstat_hello_recvfail,
+           pim_ifp->pim_ifstat_hello_sent,
+           pim_ifp->pim_ifstat_hello_sendfail,
+           VTY_NEWLINE);
+  }
+}
+
+static void pim_show_interfaces(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty, "Interface Address         ifIndex Socket Uptime   Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    char uptime[10];
+    int mloop;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation);
+
+    mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd);
+      
+    vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s",
+           ifp->name,
+           inet_ntoa(ifaddr),
+           ifp->ifindex,
+           pim_ifp->pim_sock_fd,
+           uptime,
+           if_is_multicast(ifp) ? "yes" : "no",
+           if_is_broadcast(ifp) ? "yes" : "no",
+           (mloop < 0) ? "?" : (mloop ? "yes" : "no"),
+           (ifp->flags & IFF_ALLMULTI) ? "yes" : "no",
+           (ifp->flags & IFF_PROMISC) ? "yes" : "no",
+           PIM_IF_IS_DELETED(ifp) ? "yes" : "no",
+           VTY_NEWLINE);
+  }
+}
+
+static void pim_show_join(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "Interface Address         Source          Group           State  Uptime   Expire Prune%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *ch_node;
+    struct pim_ifchannel *ch;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+      char ch_src_str[100];
+      char ch_grp_str[100];
+      char uptime[10];
+      char expire[10];
+      char prune[10];
+
+      pim_inet4_dump("<ch_src?>", ch->source_addr,
+                    ch_src_str, sizeof(ch_src_str));
+      pim_inet4_dump("<ch_grp?>", ch->group_addr,
+                    ch_grp_str, sizeof(ch_grp_str));
+
+      pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
+      pim_time_timer_to_mmss(expire, sizeof(expire),
+                            ch->t_ifjoin_expiry_timer);
+      pim_time_timer_to_mmss(prune, sizeof(prune),
+                            ch->t_ifjoin_prune_pending_timer);
+
+      vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             ch_src_str,
+             ch_grp_str,
+             pim_ifchannel_ifjoin_name(ch->ifjoin_state),
+             uptime,
+             expire,
+             prune,
+             VTY_NEWLINE);
+    } /* scan interface channels */
+  } /* scan interfaces */
+
+}
+
+static void pim_show_neighbors(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+  time_t            now;
+  
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty,
+         "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s"
+         "            T=can_disable_join_suppression%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  vty_out(vty, "Interface Address         Neighbor        Uptime   Timer Holdt DrPri GenId    Recv  %s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *neighnode;
+    struct pim_neighbor *neigh;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+      char uptime[10];
+      char holdtime[10];
+      char expire[10];
+      char neigh_src_str[100];
+      char recv[7];
+
+      pim_inet4_dump("<src?>", neigh->source_addr,
+                    neigh_src_str, sizeof(neigh_src_str));
+      pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation);
+      pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime);
+      pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer);
+
+      recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME)                     ? 'H' : ' ';
+      recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY)              ? 'L' : ' ';
+      recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)                  ? 'P' : ' ';
+      recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID)                ? 'G' : ' ';
+      recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST)                 ? 'A' : ' ';
+      recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' ';
+      recv[6] = '\0';
+
+      vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             neigh_src_str,
+             uptime,
+             expire,
+             holdtime,
+             neigh->dr_priority,
+             neigh->generation_id,
+             recv,
+             VTY_NEWLINE);
+    }
+
+
+  }
+}
+
+static void pim_show_lan_prune_delay(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+
+  vty_out(vty,
+         "PrDly=propagation_delay (msec)           OvInt=override_interval (msec)%s"
+         "HiDly=highest_propagation_delay (msec)   HiInt=highest_override_interval (msec)%s"
+         "NoDly=number_of_non_lan_delay_neighbors%s"
+         "T=t_bit LPD=lan_prune_delay_hello_option%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  vty_out(vty, "Interface Address         PrDly OvInt NoDly HiDly HiInt T | Neighbor        LPD PrDly OvInt T%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *neighnode;
+    struct pim_neighbor *neigh;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+      char neigh_src_str[100];
+
+      pim_inet4_dump("<src?>", neigh->source_addr,
+                    neigh_src_str, sizeof(neigh_src_str));
+
+      vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s",
+             ifp->name,
+             inet_ntoa(ifaddr),
+             pim_ifp->pim_propagation_delay_msec,
+             pim_ifp->pim_override_interval_msec,
+             pim_ifp->pim_number_of_nonlandelay_neighbors,
+             pim_ifp->pim_neighbors_highest_propagation_delay_msec,
+             pim_ifp->pim_neighbors_highest_override_interval_msec,
+             PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)),
+             neigh_src_str,
+             PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no",
+             neigh->propagation_delay_msec,
+             neigh->override_interval_msec,
+             PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options,
+                                                 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)),
+             VTY_NEWLINE);
+    }
+
+  }
+}
+
+static void pim_show_jp_override_interval(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+
+  vty_out(vty,
+         "EffPDelay=effective_propagation_delay (msec)%s"
+         "EffOvrInt=override_interval (msec)%s"
+         "JPOvrInt=jp_override_interval (msec)%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  vty_out(vty, "Interface Address         LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s",
+           ifp->name,
+           inet_ntoa(ifaddr),
+           pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled",
+           pim_if_effective_propagation_delay_msec(ifp),
+           pim_if_effective_override_interval_msec(ifp),
+           pim_if_jp_override_interval_msec(ifp),
+           VTY_NEWLINE);
+  }
+}
+
+static void pim_show_neighbors_secondary(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+
+  vty_out(vty, "Interface Address         Neighbor        Secondary      %s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct listnode *neighnode;
+    struct pim_neighbor *neigh;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->pim_sock_fd < 0)
+      continue;
+
+    ifaddr = pim_ifp->primary_address;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+      char neigh_src_str[100];
+      struct listnode *prefix_node;
+      struct prefix *p;
+
+      if (!neigh->prefix_list)
+       continue;
+
+      pim_inet4_dump("<src?>", neigh->source_addr,
+                    neigh_src_str, sizeof(neigh_src_str));
+
+      for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) {
+       char neigh_sec_str[100];
+
+       if (p->family != AF_INET)
+         continue;
+
+       pim_inet4_dump("<src?>", p->u.prefix4,
+                      neigh_sec_str, sizeof(neigh_sec_str));
+
+       vty_out(vty, "%-9s %-15s %-15s %-15s%s",
+               ifp->name,
+               inet_ntoa(ifaddr),
+               neigh_src_str,
+               neigh_sec_str,
+               VTY_NEWLINE);
+      }
+    }
+  }
+}
+
+static void pim_show_upstream(struct vty *vty)
+{
+  struct listnode     *upnode;
+  struct pim_upstream *up;
+  time_t               now;
+
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty, "Source          Group           State Uptime   JoinTimer RefCnt%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
+      char src_str[100];
+      char grp_str[100];
+      char uptime[10];
+      char join_timer[10];
+
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
+      pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer);
+
+      vty_out(vty, "%-15s %-15s %-5s %-8s %-9s %6d%s",
+             src_str,
+             grp_str,
+             up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd",
+             uptime,
+             join_timer,
+             up->ref_count,
+             VTY_NEWLINE);
+  }
+}
+
+static void pim_show_join_desired(struct vty *vty)
+{
+  struct listnode      *ifnode;
+  struct listnode      *chnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+  char src_str[100];
+  char grp_str[100];
+
+  vty_out(vty,
+         "Interface Source          Group           LostAssert Joins PimInclude JoinDesired EvalJD%s",
+         VTY_NEWLINE);
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) {
+      struct pim_upstream *up = ch->upstream;
+
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+
+      vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s",
+             ifp->name,
+             src_str,
+             grp_str,
+             pim_macro_ch_lost_assert(ch) ? "yes" : "no",
+             pim_macro_chisin_joins(ch) ? "yes" : "no",
+             pim_macro_chisin_pim_include(ch) ? "yes" : "no",
+             PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags) ? "yes" : "no",
+             pim_upstream_evaluate_join_desired(up) ? "yes" : "no",
+             VTY_NEWLINE);
+    }
+  }
+}
+
+static void pim_show_upstream_rpf(struct vty *vty)
+{
+  struct listnode     *upnode;
+  struct pim_upstream *up;
+
+  vty_out(vty,
+         "Source          Group           RpfIface RibNextHop      RpfAddress     %s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) {
+    char src_str[100];
+    char grp_str[100];
+    char rpf_nexthop_str[100];
+    char rpf_addr_str[100];
+    struct pim_rpf *rpf;
+    const char *rpf_ifname;
+    
+    rpf = &up->rpf;
+    
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str));
+    pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+    
+    rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
+    
+    vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s",
+           src_str,
+           grp_str,
+           rpf_ifname,
+           rpf_nexthop_str,
+           rpf_addr_str,
+           VTY_NEWLINE);
+  }
+}
+
+static void show_rpf_refresh_stats(struct vty *vty, time_t now)
+{
+  char refresh_uptime[10];
+
+  pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last);
+
+  vty_out(vty, 
+         "RPF Cache Refresh Delay:    %ld msecs%s"
+         "RPF Cache Refresh Timer:    %ld msecs%s"
+         "RPF Cache Refresh Requests: %lld%s"
+         "RPF Cache Refresh Events:   %lld%s"
+         "RPF Cache Refresh Last:     %s%s",
+         qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE,
+         pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE,
+         (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE,
+         (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE,
+         refresh_uptime, VTY_NEWLINE);
+}
+
+static void show_scan_oil_stats(struct vty *vty, time_t now)
+{
+  char uptime_scan_oil[10];
+  char uptime_mroute_add[10];
+  char uptime_mroute_del[10];
+
+  pim_time_uptime_begin(uptime_scan_oil, sizeof(uptime_scan_oil), now, qpim_scan_oil_last);
+  pim_time_uptime_begin(uptime_mroute_add, sizeof(uptime_mroute_add), now, qpim_mroute_add_last);
+  pim_time_uptime_begin(uptime_mroute_del, sizeof(uptime_mroute_del), now, qpim_mroute_del_last);
+
+  vty_out(vty,
+          "Scan OIL - Last: %s  Events: %lld%s"
+          "MFC Add  - Last: %s  Events: %lld%s"
+          "MFC Del  - Last: %s  Events: %lld%s",
+          uptime_scan_oil,   (long long) qpim_scan_oil_events,   VTY_NEWLINE,
+          uptime_mroute_add, (long long) qpim_mroute_add_events, VTY_NEWLINE,
+          uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE);
+}
+
+static void pim_show_rpf(struct vty *vty)
+{
+  struct listnode     *up_node;
+  struct pim_upstream *up;
+  time_t               now = pim_time_monotonic_sec();
+
+  show_rpf_refresh_stats(vty, now);
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+
+  vty_out(vty,
+         "Source          Group           RpfIface RpfAddress      RibNextHop      Metric Pref%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
+    char src_str[100];
+    char grp_str[100];
+    char rpf_addr_str[100];
+    char rib_nexthop_str[100];
+    const char *rpf_ifname;
+    struct pim_rpf  *rpf = &up->rpf;
+    
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<rpf?>", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+    pim_inet4_dump("<nexthop?>", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str));
+    
+    rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>";
+    
+    vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s",
+           src_str,
+           grp_str,
+           rpf_ifname,
+           rpf_addr_str,
+           rib_nexthop_str,
+           rpf->source_nexthop.mrib_route_metric,
+           rpf->source_nexthop.mrib_metric_preference,
+           VTY_NEWLINE);
+  }
+}
+
+static void igmp_show_querier(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+
+  vty_out(vty, "Interface Address         Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char query_hhmmss[10];
+      char other_hhmmss[10];
+
+      pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer);
+      pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer);
+
+      vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s",
+             ifp->name,
+             inet_ntoa(igmp->ifaddr),
+             igmp->t_igmp_query_timer ? "THIS" : "OTHER",
+             igmp->startup_query_count,
+             query_hhmmss,
+             other_hhmmss,
+             VTY_NEWLINE);
+    }
+  }
+}
+
+static void igmp_show_groups(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  time_t            now;
+
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty, "Interface Address         Group           Mode Timer    Srcs V Uptime  %s", VTY_NEWLINE);
+
+  /* scan interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+    
+    /* scan igmp sockets */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char ifaddr_str[100];
+      struct listnode *grpnode;
+      struct igmp_group *grp;
+
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+      /* scan igmp groups */
+      for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+       char group_str[100];
+       char hhmmss[10];
+       char uptime[10];
+
+       pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+       pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer);
+       pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation);
+
+       vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s",
+               ifp->name,
+               ifaddr_str,
+               group_str,
+               grp->group_filtermode_isexcl ? "EXCL" : "INCL",
+               hhmmss,
+               grp->group_source_list ? listcount(grp->group_source_list) : 0,
+               igmp_group_compat_mode(igmp, grp),
+               uptime,
+               VTY_NEWLINE);
+
+      } /* scan igmp groups */
+    } /* scan igmp sockets */
+  } /* scan interfaces */
+}
+
+static void igmp_show_group_retransmission(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  vty_out(vty, "Interface Address         Group           RetTimer Counter RetSrcs%s", VTY_NEWLINE);
+
+  /* scan interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+    
+    /* scan igmp sockets */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char ifaddr_str[100];
+      struct listnode *grpnode;
+      struct igmp_group *grp;
+
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+      /* scan igmp groups */
+      for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+       char group_str[100];
+       char grp_retr_mmss[10];
+       struct listnode    *src_node;
+       struct igmp_source *src;
+       int grp_retr_sources = 0;
+
+       pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+       pim_time_timer_to_mmss(grp_retr_mmss, sizeof(grp_retr_mmss), grp->t_group_query_retransmit_timer);
+
+
+       /* count group sources with retransmission state */
+       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) {
+         if (src->source_query_retransmit_count > 0) {
+           ++grp_retr_sources;
+         }
+       }
+
+       vty_out(vty, "%-9s %-15s %-15s %-8s %7d %7d%s",
+               ifp->name,
+               ifaddr_str,
+               group_str,
+               grp_retr_mmss,
+               grp->group_specific_query_retransmit_count,
+               grp_retr_sources,
+               VTY_NEWLINE);
+
+      } /* scan igmp groups */
+    } /* scan igmp sockets */
+  } /* scan interfaces */
+}
+
+static void igmp_show_parameters(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  vty_out(vty,
+         "QRV: Robustness Variable             SQI: Startup Query Interval%s"
+         "QQI: Query Interval                  OQPI: Other Querier Present Interval%s"
+         "QRI: Query Response Interval         LMQT: Last Member Query Time%s"
+         "GMI: Group Membership Interval       OHPI: Older Host Present Interval%s%s",
+         VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  vty_out(vty,
+         "Interface Address         QRV QQI QRI   GMI   SQI OQPI  LMQT  OHPI %s",
+         VTY_NEWLINE);
+
+  /* scan interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+    
+    /* scan igmp sockets */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char ifaddr_str[100];
+      long gmi_dsec;  /* Group Membership Interval */
+      long oqpi_dsec; /* Other Querier Present Interval */
+      int  sqi;
+      long lmqt_dsec;
+      long ohpi_dsec;
+      long qri_dsec;
+
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+      gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+                                  igmp->querier_query_interval,
+                                  pim_ifp->igmp_query_max_response_time_dsec) / 100;
+
+      sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+
+      oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
+                                    igmp->querier_query_interval,
+                                    pim_ifp->igmp_query_max_response_time_dsec) / 100;
+
+      lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec,
+                                    igmp->querier_robustness_variable) / 100;
+
+      ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
+                                    igmp->querier_query_interval,
+                                    pim_ifp->igmp_query_max_response_time_dsec);
+
+      qri_dsec = pim_ifp->igmp_query_max_response_time_dsec;
+
+      vty_out(vty,
+             "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s",
+             ifp->name,
+             ifaddr_str,
+             igmp->querier_robustness_variable,
+             igmp->querier_query_interval,
+             qri_dsec / 10, qri_dsec % 10,
+             gmi_dsec / 10, gmi_dsec % 10,
+             sqi,
+             oqpi_dsec / 10, oqpi_dsec % 10,
+             lmqt_dsec / 10, lmqt_dsec % 10,
+             ohpi_dsec / 10, ohpi_dsec % 10,
+             VTY_NEWLINE);
+
+    } /* scan igmp sockets */
+  } /* scan interfaces */
+}
+
+static void igmp_show_sources(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+  time_t            now;
+
+  now = pim_time_monotonic_sec();
+
+  vty_out(vty, "Interface Address         Group           Source          Timer Fwd Uptime  %s", VTY_NEWLINE);
+
+  /* scan interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+    
+    /* scan igmp sockets */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char ifaddr_str[100];
+      struct listnode   *grpnode;
+      struct igmp_group *grp;
+
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+      /* scan igmp groups */
+      for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+       char group_str[100];
+       struct listnode    *srcnode;
+       struct igmp_source *src;
+
+       pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+       
+       /* scan group sources */
+       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+         char source_str[100];
+         char mmss[10];
+         char uptime[10];
+
+         pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
+
+         pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer);
+
+         pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation);
+
+         vty_out(vty, "%-9s %-15s %-15s %-15s %5s %3s %8s%s",
+                 ifp->name,
+                 ifaddr_str,
+                 group_str,
+                 source_str,
+                 mmss,
+                 IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" : "N",
+                 uptime,
+                 VTY_NEWLINE);
+         
+       } /* scan group sources */
+      } /* scan igmp groups */
+    } /* scan igmp sockets */
+  } /* scan interfaces */
+}
+
+static void igmp_show_source_retransmission(struct vty *vty)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  vty_out(vty, "Interface Address         Group           Source          Counter%s", VTY_NEWLINE);
+
+  /* scan interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp = ifp->info;
+    struct listnode  *sock_node;
+    struct igmp_sock *igmp;
+    
+    if (!pim_ifp)
+      continue;
+    
+    /* scan igmp sockets */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+      char ifaddr_str[100];
+      struct listnode   *grpnode;
+      struct igmp_group *grp;
+
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+      /* scan igmp groups */
+      for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) {
+       char group_str[100];
+       struct listnode    *srcnode;
+       struct igmp_source *src;
+
+       pim_inet4_dump("<group?>", grp->group_addr, group_str, sizeof(group_str));
+       
+       /* scan group sources */
+       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) {
+         char source_str[100];
+
+         pim_inet4_dump("<source?>", src->source_addr, source_str, sizeof(source_str));
+
+         vty_out(vty, "%-9s %-15s %-15s %-15s %7d%s",
+                 ifp->name,
+                 ifaddr_str,
+                 group_str,
+                 source_str,
+                 src->source_query_retransmit_count,
+                 VTY_NEWLINE);
+         
+       } /* scan group sources */
+      } /* scan igmp groups */
+    } /* scan igmp sockets */
+  } /* scan interfaces */
+}
+
+static void clear_igmp_interfaces()
+{
+  struct listnode  *ifnode;
+  struct listnode  *ifnextnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_if_addr_del_all_igmp(ifp);
+  }
+
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_if_addr_add_all(ifp);
+  }
+}
+
+static void clear_pim_interfaces()
+{
+  struct listnode  *ifnode;
+  struct listnode  *ifnextnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    if (ifp->info) {
+      pim_neighbor_delete_all(ifp, "interface cleared");
+    }
+  }
+}
+
+static void clear_interfaces()
+{
+  clear_igmp_interfaces();
+  clear_pim_interfaces();
+}
+
+DEFUN (pim_interface,
+       pim_interface_cmd,
+       "interface IFNAME",
+       "Select an interface to configure\n"
+       "Interface's name\n")
+{
+  struct interface *ifp;
+  const char *ifname = argv[0];
+  size_t sl;
+
+  sl = strlen(ifname);
+  if (sl > INTERFACE_NAMSIZ) {
+    vty_out(vty, "%% Interface name %s is invalid: length exceeds "
+           "%d characters%s",
+           ifname, INTERFACE_NAMSIZ, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  ifp = if_lookup_by_name_len(ifname, sl);
+  if (!ifp) {
+    vty_out(vty, "%% Interface %s does not exist%s", ifname, VTY_NEWLINE);
+
+    /* Returning here would prevent pimd from booting when there are
+       interface commands in pimd.conf, since all interfaces are
+       unknown at pimd boot time (the zebra daemon has not been
+       contacted for interface discovery). */
+    
+    ifp = if_get_by_name_len(ifname, sl);
+    if (!ifp) {
+      vty_out(vty, "%% Could not create interface %s%s", ifname, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  }
+
+  vty->index = ifp;
+  vty->node = INTERFACE_NODE;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_interfaces,
+       clear_ip_interfaces_cmd,
+       "clear ip interfaces",
+       CLEAR_STR
+       IP_STR
+       "Reset interfaces\n")
+{
+  clear_interfaces();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_igmp_interfaces,
+       clear_ip_igmp_interfaces_cmd,
+       "clear ip igmp interfaces",
+       CLEAR_STR
+       IP_STR
+       CLEAR_IP_IGMP_STR
+       "Reset IGMP interfaces\n")
+{
+  clear_igmp_interfaces();
+
+  return CMD_SUCCESS;
+}
+
+static void mroute_add_all()
+{
+  struct listnode    *node;
+  struct channel_oil *c_oil;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+    if (pim_mroute_add(&c_oil->oil)) {
+      /* just log warning */
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
+                __FILE__, __PRETTY_FUNCTION__,
+                source_str, group_str);
+    }
+  }
+}
+
+static void mroute_del_all()
+{
+  struct listnode    *node;
+  struct channel_oil *c_oil;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+    if (pim_mroute_del(&c_oil->oil)) {
+      /* just log warning */
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
+                __FILE__, __PRETTY_FUNCTION__,
+                source_str, group_str);
+    }
+  }
+}
+
+static void static_mroute_add_all()
+{
+  struct listnode     *node;
+  struct static_route *s_route;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    if (pim_mroute_add(&s_route->mc)) {
+      /* just log warning */
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
+      __FILE__, __PRETTY_FUNCTION__,
+      source_str, group_str);
+    }
+  }
+}
+
+static void static_mroute_del_all()
+{
+   struct listnode     *node;
+   struct static_route *s_route;
+
+   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+     if (pim_mroute_del(&s_route->mc)) {
+       /* just log warning */
+       char source_str[100];
+       char group_str[100];
+       pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, sizeof(source_str));
+       pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str));
+       zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
+       __FILE__, __PRETTY_FUNCTION__,
+       source_str, group_str);
+     }
+   }
+}
+
+DEFUN (clear_ip_mroute,
+       clear_ip_mroute_cmd,
+       "clear ip mroute",
+       CLEAR_STR
+       IP_STR
+       "Reset multicast routes\n")
+{
+  mroute_del_all();
+  mroute_add_all();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_pim_interfaces,
+       clear_ip_pim_interfaces_cmd,
+       "clear ip pim interfaces",
+       CLEAR_STR
+       IP_STR
+       CLEAR_IP_PIM_STR
+       "Reset PIM interfaces\n")
+{
+  clear_pim_interfaces();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_pim_oil,
+       clear_ip_pim_oil_cmd,
+       "clear ip pim oil",
+       CLEAR_STR
+       IP_STR
+       CLEAR_IP_PIM_STR
+       "Rescan PIM OIL (output interface list)\n")
+{
+  pim_scan_oil();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_interface,
+       show_ip_igmp_interface_cmd,
+       "show ip igmp interface",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       "IGMP interface information\n")
+{
+  igmp_show_interfaces(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_join,
+       show_ip_igmp_join_cmd,
+       "show ip igmp join",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       "IGMP static join information\n")
+{
+  igmp_show_interface_join(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_groups,
+       show_ip_igmp_groups_cmd,
+       "show ip igmp groups",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       IGMP_GROUP_STR)
+{
+  igmp_show_groups(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_groups_retransmissions,
+       show_ip_igmp_groups_retransmissions_cmd,
+       "show ip igmp groups retransmissions",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       IGMP_GROUP_STR
+       "IGMP group retransmissions\n")
+{
+  igmp_show_group_retransmission(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_parameters,
+       show_ip_igmp_parameters_cmd,
+       "show ip igmp parameters",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       "IGMP parameters information\n")
+{
+  igmp_show_parameters(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_sources,
+       show_ip_igmp_sources_cmd,
+       "show ip igmp sources",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       IGMP_SOURCE_STR)
+{
+  igmp_show_sources(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_sources_retransmissions,
+       show_ip_igmp_sources_retransmissions_cmd,
+       "show ip igmp sources retransmissions",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       IGMP_SOURCE_STR
+       "IGMP source retransmissions\n")
+{
+  igmp_show_source_retransmission(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_igmp_querier,
+       show_ip_igmp_querier_cmd,
+       "show ip igmp querier",
+       SHOW_STR
+       IP_STR
+       IGMP_STR
+       "IGMP querier information\n")
+{
+  igmp_show_querier(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_address,
+       show_ip_pim_address_cmd,
+       "show ip pim address",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface address\n")
+{
+  show_interface_address(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert,
+       show_ip_pim_assert_cmd,
+       "show ip pim assert",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface assert\n")
+{
+  pim_show_assert(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_internal,
+       show_ip_pim_assert_internal_cmd,
+       "show ip pim assert-internal",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface internal assert state\n")
+{
+  pim_show_assert_internal(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_metric,
+       show_ip_pim_assert_metric_cmd,
+       "show ip pim assert-metric",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface assert metric\n")
+{
+  pim_show_assert_metric(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_assert_winner_metric,
+       show_ip_pim_assert_winner_metric_cmd,
+       "show ip pim assert-winner-metric",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface assert winner metric\n")
+{
+  pim_show_assert_winner_metric(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_dr,
+       show_ip_pim_dr_cmd,
+       "show ip pim designated-router",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface designated router\n")
+{
+  pim_show_dr(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_hello,
+       show_ip_pim_hello_cmd,
+       "show ip pim hello",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface hello information\n")
+{
+  pim_show_hello(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_interface,
+       show_ip_pim_interface_cmd,
+       "show ip pim interface",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface information\n")
+{
+  pim_show_interfaces(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_join,
+       show_ip_pim_join_cmd,
+       "show ip pim join",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface join information\n")
+{
+  pim_show_join(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_lan_prune_delay,
+       show_ip_pim_lan_prune_delay_cmd,
+       "show ip pim lan-prune-delay",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM neighbors LAN prune delay parameters\n")
+{
+  pim_show_lan_prune_delay(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_local_membership,
+       show_ip_pim_local_membership_cmd,
+       "show ip pim local-membership",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface local-membership\n")
+{
+  pim_show_membership(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_jp_override_interval,
+       show_ip_pim_jp_override_interval_cmd,
+       "show ip pim jp-override-interval",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM interface J/P override interval\n")
+{
+  pim_show_jp_override_interval(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_neighbor,
+       show_ip_pim_neighbor_cmd,
+       "show ip pim neighbor",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM neighbor information\n")
+{
+  pim_show_neighbors(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_secondary,
+       show_ip_pim_secondary_cmd,
+       "show ip pim secondary",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM neighbor addresses\n")
+{
+  pim_show_neighbors_secondary(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream,
+       show_ip_pim_upstream_cmd,
+       "show ip pim upstream",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM upstream information\n")
+{
+  pim_show_upstream(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream_join_desired,
+       show_ip_pim_upstream_join_desired_cmd,
+       "show ip pim upstream-join-desired",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM upstream join-desired\n")
+{
+  pim_show_join_desired(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_upstream_rpf,
+       show_ip_pim_upstream_rpf_cmd,
+       "show ip pim upstream-rpf",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM upstream source rpf\n")
+{
+  pim_show_upstream_rpf(vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_pim_rpf,
+       show_ip_pim_rpf_cmd,
+       "show ip pim rpf",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       "PIM cached source rpf information\n")
+{
+  pim_show_rpf(vty);
+
+  return CMD_SUCCESS;
+}
+
+static void show_multicast_interfaces(struct vty *vty)
+{
+  struct listnode  *node;
+  struct interface *ifp;
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+  
+  vty_out(vty, "Interface Address         ifi Vif  PktsIn PktsOut    BytesIn   BytesOut%s",
+         VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct in_addr ifaddr;
+    struct sioc_vif_req vreq;
+
+    pim_ifp = ifp->info;
+    
+    if (!pim_ifp)
+      continue;
+
+    memset(&vreq, 0, sizeof(vreq));
+    vreq.vifi = pim_ifp->mroute_vif_index;
+
+    if (ioctl(qpim_mroute_socket_fd, SIOCGETVIFCNT, &vreq)) {
+      zlog_warn("ioctl(SIOCGETVIFCNT=%lu) failure for interface %s vif_index=%d: errno=%d: %s%s",
+               (unsigned long)SIOCGETVIFCNT,
+               ifp->name,
+               pim_ifp->mroute_vif_index,
+               errno,
+               safe_strerror(errno),
+               VTY_NEWLINE);
+    }
+
+    ifaddr = pim_ifp->primary_address;
+
+    vty_out(vty, "%-9s %-15s %3d %3d %7lu %7lu %10lu %10lu%s",
+           ifp->name,
+           inet_ntoa(ifaddr),
+           ifp->ifindex,
+           pim_ifp->mroute_vif_index,
+           vreq.icount,
+           vreq.ocount,
+           vreq.ibytes,
+           vreq.obytes,
+           VTY_NEWLINE);
+  }
+}
+
+DEFUN (show_ip_multicast,
+       show_ip_multicast_cmd,
+       "show ip multicast",
+       SHOW_STR
+       IP_STR
+       "Multicast global information\n")
+{
+  time_t now = pim_time_monotonic_sec();
+
+  if (PIM_MROUTE_IS_ENABLED) {
+    char uptime[10];
+
+    vty_out(vty, "Mroute socket descriptor: %d%s",
+           qpim_mroute_socket_fd,
+           VTY_NEWLINE);
+
+    pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation);
+    vty_out(vty, "Mroute socket uptime: %s%s",
+           uptime,
+           VTY_NEWLINE);
+  }
+  else {
+    vty_out(vty, "Multicast disabled%s",
+           VTY_NEWLINE);
+  }
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+  vty_out(vty, "Zclient update socket: ");
+  if (qpim_zclient_update) {
+    vty_out(vty, "%d failures=%d%s", qpim_zclient_update->sock,
+           qpim_zclient_update->fail, VTY_NEWLINE);
+  }
+  else {
+    vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
+  }
+  vty_out(vty, "Zclient lookup socket: ");
+  if (qpim_zclient_lookup) {
+    vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock,
+           qpim_zclient_lookup->fail, VTY_NEWLINE);
+  }
+  else {
+    vty_out(vty, "<null zclient>%s", VTY_NEWLINE);
+  }
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+  vty_out(vty, "Current highest VifIndex: %d%s",
+         qpim_mroute_oif_highest_vif_index,
+         VTY_NEWLINE);
+  vty_out(vty, "Maximum highest VifIndex: %d%s",
+         MAXVIFS - 1,
+         VTY_NEWLINE);
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+  vty_out(vty, "Upstream Join Timer: %d secs%s",
+         qpim_t_periodic,
+         VTY_NEWLINE);
+  vty_out(vty, "Join/Prune Holdtime: %d secs%s",
+         PIM_JP_HOLDTIME,
+         VTY_NEWLINE);
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+
+  show_rpf_refresh_stats(vty, now);
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+
+  show_scan_oil_stats(vty, now);
+
+  show_multicast_interfaces(vty);
+  
+  return CMD_SUCCESS;
+}
+
+static void show_mroute(struct vty *vty)
+{
+  struct listnode    *node;
+  struct channel_oil *c_oil;
+  struct static_route *s_route;
+  time_t              now;
+
+  vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  
+  vty_out(vty, "Source          Group           Proto Input iVifI Output oVifI TTL Uptime  %s",
+         VTY_NEWLINE);
+
+  now = pim_time_monotonic_sec();
+
+  /* print list of PIM and IGMP routes */
+  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+    char group_str[100]; 
+    char source_str[100];
+    int oif_vif_index;
+
+    pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    
+    for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+      struct interface *ifp_in;
+      struct interface *ifp_out;
+      char oif_uptime[10];
+      int ttl;
+      char proto[5];
+
+      ttl = c_oil->oil.mfcc_ttls[oif_vif_index];
+      if (ttl < 1)
+       continue;
+
+      ifp_in  = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+      ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+
+      pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]);
+
+      proto[0] = '\0';
+      if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) {
+       strcat(proto, "P");
+      }
+      if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) {
+       strcat(proto, "I");
+      }
+
+      vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
+             source_str,
+             group_str,
+             proto,
+             ifp_in ? ifp_in->name : "<iif?>",
+             c_oil->oil.mfcc_parent,
+             ifp_out ? ifp_out->name : "<oif?>",
+             oif_vif_index,
+             ttl,
+             oif_uptime,
+             VTY_NEWLINE);
+    }
+  }
+
+  /* Print list of static routes */
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    char group_str[100];
+    char source_str[100];
+    int oif_vif_index;
+
+    pim_inet4_dump("<group?>", s_route->group, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", s_route->source, source_str, sizeof(source_str));
+
+    for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+      struct interface *ifp_in;
+      struct interface *ifp_out;
+      char oif_uptime[10];
+      int ttl;
+      char proto[5];
+
+      ttl = s_route->oif_ttls[oif_vif_index];
+      if (ttl < 1)
+         continue;
+
+      ifp_in  = pim_if_find_by_vif_index(s_route->iif);
+      ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+
+      pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - s_route->creation[oif_vif_index]);
+
+      proto[0] = '\0';
+      strcat(proto, "S");
+
+      vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
+         source_str,
+         group_str,
+         proto,
+         ifp_in ? ifp_in->name : "<iif?>",
+         s_route->iif,
+         ifp_out ? ifp_out->name : "<oif?>",
+         oif_vif_index,
+         ttl,
+         oif_uptime,
+         VTY_NEWLINE);
+    }
+  }
+}
+
+DEFUN (show_ip_mroute,
+       show_ip_mroute_cmd,
+       "show ip mroute",
+       SHOW_STR
+       IP_STR
+       MROUTE_STR)
+{
+  show_mroute(vty);
+  return CMD_SUCCESS;
+}
+
+static void show_mroute_count(struct vty *vty)
+{
+  struct listnode    *node;
+  struct channel_oil *c_oil;
+  struct static_route *s_route;
+
+  vty_out(vty, "%s", VTY_NEWLINE);
+  
+  vty_out(vty, "Source          Group           Packets      Bytes WrongIf  %s",
+         VTY_NEWLINE);
+
+  /* Print PIM and IGMP route counts */
+  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+    char group_str[100]; 
+    char source_str[100];
+    struct sioc_sg_req sgreq;
+
+    memset(&sgreq, 0, sizeof(sgreq));
+    sgreq.src = c_oil->oil.mfcc_origin;
+    sgreq.grp = c_oil->oil.mfcc_mcastgrp;
+
+    pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+
+    if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) {
+      int e = errno;
+      vty_out(vty,
+             "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s%s",
+             (unsigned long)SIOCGETSGCNT,
+             source_str,
+             group_str,
+             e,
+             safe_strerror(e),
+             VTY_NEWLINE);           
+      continue;
+    }
+    
+    vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+           source_str,
+           group_str,
+           sgreq.pktcnt,
+           sgreq.bytecnt,
+           sgreq.wrong_if,
+           VTY_NEWLINE);
+  }
+
+   /* Print static route counts */
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    char group_str[100];
+    char source_str[100];
+    struct sioc_sg_req sgreq;
+
+    memset(&sgreq, 0, sizeof(sgreq));
+    sgreq.src = s_route->mc.mfcc_origin;
+    sgreq.grp = s_route->mc.mfcc_mcastgrp;
+
+    pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, sizeof(source_str));
+
+    if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) {
+      int e = errno;
+      vty_out(vty,
+         "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s%s",
+         /* note that typeof ioctl defs can vary across platforms, from
+          * int, to unsigned int, to long unsigned int
+          */
+         (unsigned long)SIOCGETSGCNT,
+         source_str,
+         group_str,
+         e,
+         safe_strerror(e),
+         VTY_NEWLINE);
+      continue;
+    }
+
+    vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+       source_str,
+       group_str,
+       sgreq.pktcnt,
+       sgreq.bytecnt,
+       sgreq.wrong_if,
+       VTY_NEWLINE);
+  }
+}
+
+DEFUN (show_ip_mroute_count,
+       show_ip_mroute_count_cmd,
+       "show ip mroute count",
+       SHOW_STR
+       IP_STR
+       MROUTE_STR
+       "Route and packet count data\n")
+{
+  show_mroute_count(vty);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_rib,
+       show_ip_rib_cmd,
+       "show ip rib A.B.C.D",
+       SHOW_STR
+       IP_STR
+       RIB_STR
+       "Unicast address\n")
+{
+  struct in_addr addr;
+  const char *addr_str;
+  struct pim_nexthop nexthop;
+  char nexthop_addr_str[100];
+  int result;
+
+  addr_str = argv[0];
+  result = inet_pton(AF_INET, addr_str, &addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad unicast address %s: errno=%d: %s%s",
+           addr_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (pim_nexthop_lookup(&nexthop, addr)) {
+    vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s",
+           addr_str, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  vty_out(vty, "Address         NextHop         Interface Metric Preference%s",
+         VTY_NEWLINE);
+
+  pim_inet4_dump("<nexthop?>", nexthop.mrib_nexthop_addr,
+                nexthop_addr_str, sizeof(nexthop_addr_str));
+
+  vty_out(vty, "%-15s %-15s %-9s %6d %10d%s",
+         addr_str,
+         nexthop_addr_str,
+         nexthop.interface ? nexthop.interface->name : "<ifname?>",
+         nexthop.mrib_route_metric,
+         nexthop.mrib_metric_preference,
+         VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+static void show_ssmpingd(struct vty *vty)
+{
+  struct listnode      *node;
+  struct ssmpingd_sock *ss;
+  time_t                now;
+
+  vty_out(vty, "Source          Socket Address          Port Uptime   Requests%s",
+         VTY_NEWLINE);
+
+  if (!qpim_ssmpingd_list)
+    return;
+
+  now = pim_time_monotonic_sec();
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
+    char source_str[100];
+    char ss_uptime[10];
+    struct sockaddr_in bind_addr;
+    socklen_t len = sizeof(bind_addr);
+    char bind_addr_str[100];
+
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+
+    if (pim_socket_getsockname(ss->sock_fd, (struct sockaddr *) &bind_addr, &len)) {
+      vty_out(vty, "%% Failure reading socket name for ssmpingd source %s on fd=%d%s",
+             source_str, ss->sock_fd, VTY_NEWLINE);
+    }
+
+    pim_inet4_dump("<addr?>", bind_addr.sin_addr, bind_addr_str, sizeof(bind_addr_str));
+    pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation);
+
+    vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld%s",
+           source_str,
+           ss->sock_fd,
+           bind_addr_str,
+           ntohs(bind_addr.sin_port),
+           ss_uptime,
+           (long long)ss->requests,
+           VTY_NEWLINE);
+  }
+}
+
+DEFUN (show_ip_ssmpingd,
+       show_ip_ssmpingd_cmd,
+       "show ip ssmpingd",
+       SHOW_STR
+       IP_STR
+       SHOW_SSMPINGD_STR)
+{
+  show_ssmpingd(vty);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_multicast_routing,
+       ip_multicast_routing_cmd,
+       PIM_CMD_IP_MULTICAST_ROUTING,
+       IP_STR
+       "Enable IP multicast forwarding\n")
+{
+  pim_mroute_socket_enable();
+  pim_if_add_vif_all();
+  mroute_add_all();
+  static_mroute_add_all();
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_multicast_routing,
+       no_ip_multicast_routing_cmd,
+       PIM_CMD_NO " " PIM_CMD_IP_MULTICAST_ROUTING,
+       NO_STR
+       IP_STR
+       "Global IP configuration subcommands\n"
+       "Enable IP multicast forwarding\n")
+{
+  mroute_del_all();
+  static_mroute_del_all();
+  pim_if_del_vif_all();
+  pim_mroute_socket_disable();
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_ssmpingd,
+       ip_ssmpingd_cmd,
+       "ip ssmpingd [A.B.C.D]",
+       IP_STR
+       CONF_SSMPINGD_STR
+       "Source address\n")
+{
+  int result;
+  struct in_addr source_addr;
+  const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0";
+
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  result = pim_ssmpingd_start(source_addr);
+  if (result) {
+    vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s",
+           source_str, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ssmpingd,
+       no_ip_ssmpingd_cmd,
+       "no ip ssmpingd [A.B.C.D]",
+       NO_STR
+       IP_STR
+       CONF_SSMPINGD_STR
+       "Source address\n")
+{
+  int result;
+  struct in_addr source_addr;
+  const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0";
+
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "%% Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  result = pim_ssmpingd_stop(source_addr);
+  if (result) {
+    vty_out(vty, "%% Failure stopping ssmpingd for source %s: %d%s",
+           source_str, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_igmp,
+       interface_ip_igmp_cmd,
+       "ip igmp",
+       IP_STR
+       IFACE_IGMP_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    pim_ifp = pim_if_new(ifp, 1 /* igmp=true */, 0 /* pim=false */);
+    if (!pim_ifp) {
+      vty_out(vty, "Could not enable IGMP on interface %s%s",
+             ifp->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  }
+  else {
+    PIM_IF_DO_IGMP(pim_ifp->options);
+  }
+
+  pim_if_addr_add_all(ifp);
+  pim_if_membership_refresh(ifp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp,
+       interface_no_ip_igmp_cmd,
+       "no ip igmp",
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  PIM_IF_DONT_IGMP(pim_ifp->options);
+
+  pim_if_membership_clear(ifp);
+
+  pim_if_addr_del_all_igmp(ifp);
+
+  if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+    pim_if_delete(ifp);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_igmp_join,
+       interface_ip_igmp_join_cmd,
+       "ip igmp join A.B.C.D A.B.C.D",
+       IP_STR
+       IFACE_IGMP_STR
+       "IGMP join multicast group\n"
+       "Multicast group address\n"
+       "Source address\n")
+{
+  struct interface *ifp;
+  const char *group_str;
+  const char *source_str;
+  struct in_addr group_addr;
+  struct in_addr source_addr;
+  int result;
+
+  ifp = vty->index;
+
+  /* Group address */
+  group_str = argv[0];
+  result = inet_pton(AF_INET, group_str, &group_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Source address */
+  source_str = argv[1];
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  result = pim_if_igmp_join_add(ifp, group_addr, source_addr);
+  if (result) {
+    vty_out(vty, "%% Failure joining IGMP group %s source %s on interface %s: %d%s",
+           group_str, source_str, ifp->name, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_join,
+       interface_no_ip_igmp_join_cmd,
+       "no ip igmp join A.B.C.D A.B.C.D",
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR
+       "IGMP join multicast group\n"
+       "Multicast group address\n"
+       "Source address\n")
+{
+  struct interface *ifp;
+  const char *group_str;
+  const char *source_str;
+  struct in_addr group_addr;
+  struct in_addr source_addr;
+  int result;
+
+  ifp = vty->index;
+
+  /* Group address */
+  group_str = argv[0];
+  result = inet_pton(AF_INET, group_str, &group_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Source address */
+  source_str = argv[1];
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  result = pim_if_igmp_join_del(ifp, group_addr, source_addr);
+  if (result) {
+    vty_out(vty, "%% Failure leaving IGMP group %s source %s on interface %s: %d%s",
+           group_str, source_str, ifp->name, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+/*
+  CLI reconfiguration affects the interface level (struct pim_interface).
+  This function propagates the reconfiguration to every active socket
+  for that interface.
+ */
+static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  zassert(igmp);
+
+  /* other querier present? */
+
+  if (igmp->t_other_querier_timer)
+    return;
+
+  /* this is the querier */
+
+  zassert(igmp->interface);
+  zassert(igmp->interface->info);
+
+  ifp = igmp->interface;
+  pim_ifp = ifp->info;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char ifaddr_str[100];
+    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+    zlog_debug("%s: Querier %s on %s reconfig query_interval=%d",
+              __PRETTY_FUNCTION__,
+              ifaddr_str,
+              ifp->name,
+              pim_ifp->igmp_default_query_interval);
+  }
+
+  /*
+    igmp_startup_mode_on() will reset QQI:
+
+    igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+  */
+  igmp_startup_mode_on(igmp);
+}
+
+static void igmp_sock_query_reschedule(struct igmp_sock *igmp)
+{
+  if (igmp->t_igmp_query_timer) {
+    /* other querier present */
+    zassert(igmp->t_igmp_query_timer);
+    zassert(!igmp->t_other_querier_timer);
+
+    pim_igmp_general_query_off(igmp);
+    pim_igmp_general_query_on(igmp);
+
+    zassert(igmp->t_igmp_query_timer);
+    zassert(!igmp->t_other_querier_timer);
+  }
+  else {
+    /* this is the querier */
+
+    zassert(!igmp->t_igmp_query_timer);
+    zassert(igmp->t_other_querier_timer);
+
+    pim_igmp_other_querier_timer_off(igmp);
+    pim_igmp_other_querier_timer_on(igmp);
+
+    zassert(!igmp->t_igmp_query_timer);
+    zassert(igmp->t_other_querier_timer);
+  }
+}
+
+static void change_query_interval(struct pim_interface *pim_ifp,
+                                 int query_interval)
+{
+  struct listnode  *sock_node;
+  struct igmp_sock *igmp;
+
+  pim_ifp->igmp_default_query_interval = query_interval;
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+    igmp_sock_query_interval_reconfig(igmp);
+    igmp_sock_query_reschedule(igmp);
+  }
+}
+
+static void change_query_max_response_time(struct pim_interface *pim_ifp,
+                                          int query_max_response_time_dsec)
+{
+  struct listnode  *sock_node;
+  struct igmp_sock *igmp;
+
+  pim_ifp->igmp_query_max_response_time_dsec = query_max_response_time_dsec;
+
+  /*
+    Below we modify socket/group/source timers in order to quickly
+    reflect the change. Otherwise, those timers would eventually catch
+    up.
+   */
+
+  /* scan all sockets */
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+    struct listnode   *grp_node;
+    struct igmp_group *grp;
+
+    /* reschedule socket general query */
+    igmp_sock_query_reschedule(igmp);
+
+    /* scan socket groups */
+    for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, grp)) {
+      struct listnode    *src_node;
+      struct igmp_source *src;
+
+      /* reset group timers for groups in EXCLUDE mode */
+      if (grp->group_filtermode_isexcl) {
+       igmp_group_reset_gmi(grp);
+      }
+
+      /* scan group sources */
+      for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) {
+
+       /* reset source timers for sources with running timers */
+       if (src->t_source_timer) {
+         igmp_source_reset_gmi(igmp, grp, src);
+       }
+      }
+    }
+  }
+}
+
+#define IGMP_QUERY_INTERVAL_MIN (1)
+#define IGMP_QUERY_INTERVAL_MAX (1800)
+
+DEFUN (interface_ip_igmp_query_interval,
+       interface_ip_igmp_query_interval_cmd,
+       PIM_CMD_IP_IGMP_QUERY_INTERVAL " <1-1800>",
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_INTERVAL_STR
+       "Query interval in seconds\n")
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int query_interval;
+  int query_interval_dsec;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty,
+           "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+           ifp->name,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  query_interval = atoi(argv[0]);
+  query_interval_dsec = 10 * query_interval;
+
+  /*
+    It seems we don't need to check bounds since command.c does it
+    already, but we verify them anyway for extra safety.
+  */
+  if (query_interval < IGMP_QUERY_INTERVAL_MIN) {
+    vty_out(vty, "General query interval %d lower than minimum %d%s",
+           query_interval,
+           IGMP_QUERY_INTERVAL_MIN,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (query_interval > IGMP_QUERY_INTERVAL_MAX) {
+    vty_out(vty, "General query interval %d higher than maximum %d%s",
+           query_interval,
+           IGMP_QUERY_INTERVAL_MAX,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) {
+    vty_out(vty,
+           "Can't set general query interval %d dsec <= query max response time %d dsec.%s",
+           query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_interval(pim_ifp, query_interval);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_interval,
+       interface_no_ip_igmp_query_interval_cmd,
+       PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_INTERVAL,
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_INTERVAL_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int default_query_interval_dsec;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  default_query_interval_dsec = IGMP_GENERAL_QUERY_INTERVAL * 10;
+
+  if (default_query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) {
+    vty_out(vty,
+           "Can't set default general query interval %d dsec <= query max response time %d dsec.%s",
+           default_query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_interval(pim_ifp, IGMP_GENERAL_QUERY_INTERVAL);
+
+  return CMD_SUCCESS;
+}
+
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1)
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25)
+
+DEFUN (interface_ip_igmp_query_max_response_time,
+       interface_ip_igmp_query_max_response_time_cmd,
+       PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME " <1-25>",
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR
+       "Query response value in seconds\n")
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int query_max_response_time;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty,
+           "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+           ifp->name,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  query_max_response_time = atoi(argv[0]);
+
+  /*
+    It seems we don't need to check bounds since command.c does it
+    already, but we verify them anyway for extra safety.
+  */
+  if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) {
+    vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s",
+           query_max_response_time,
+           IGMP_QUERY_MAX_RESPONSE_TIME_MIN,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) {
+    vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s",
+           query_max_response_time,
+           IGMP_QUERY_MAX_RESPONSE_TIME_MAX,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (query_max_response_time >= pim_ifp->igmp_default_query_interval) {
+    vty_out(vty,
+           "Can't set query max response time %d sec >= general query interval %d sec%s",
+           query_max_response_time, pim_ifp->igmp_default_query_interval,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_max_response_time(pim_ifp, 10 * query_max_response_time);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_max_response_time,
+       interface_no_ip_igmp_query_max_response_time_cmd,
+       PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME,
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int default_query_interval_dsec;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+  if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
+    vty_out(vty,
+           "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
+           IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
+
+  return CMD_SUCCESS;
+}
+
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10)
+#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250)
+
+DEFUN (interface_ip_igmp_query_max_response_time_dsec,
+       interface_ip_igmp_query_max_response_time_dsec_cmd,
+       PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC " <10-250>",
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR
+       "Query response value in deciseconds\n")
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int query_max_response_time_dsec;
+  int default_query_interval_dsec;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty,
+           "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+           ifp->name,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  query_max_response_time_dsec = atoi(argv[0]);
+
+  /*
+    It seems we don't need to check bounds since command.c does it
+    already, but we verify them anyway for extra safety.
+  */
+  if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) {
+    vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s",
+           query_max_response_time_dsec,
+           IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) {
+    vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s",
+           query_max_response_time_dsec,
+           IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+  if (query_max_response_time_dsec >= default_query_interval_dsec) {
+    vty_out(vty,
+           "Can't set query max response time %d dsec >= general query interval %d dsec%s",
+           query_max_response_time_dsec, default_query_interval_dsec,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_max_response_time(pim_ifp, query_max_response_time_dsec);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_query_max_response_time_dsec,
+       interface_no_ip_igmp_query_max_response_time_dsec_cmd,
+       PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,
+       NO_STR
+       IP_STR
+       IFACE_IGMP_STR
+       IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int default_query_interval_dsec;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval;
+
+  if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) {
+    vty_out(vty,
+           "Can't set default query max response time %d dsec >= general query interval %d dsec.%s",
+           IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec,
+           VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_pim_drprio,
+       interface_ip_pim_drprio_cmd,
+       "ip pim drpriority <1-4294967295>",
+       IP_STR
+       PIM_STR
+       "Set the Designated Router Election Priority\n"
+       "Value of the new DR Priority\n")
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  uint32_t old_dr_prio;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty, "Please enable PIM on interface, first%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  old_dr_prio = pim_ifp->pim_dr_priority;
+
+  pim_ifp->pim_dr_priority = strtol(argv[0], NULL, 10);
+
+  if (old_dr_prio != pim_ifp->pim_dr_priority) {
+    if (pim_if_dr_election(ifp))
+      pim_hello_restart_now(ifp);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_pim_drprio,
+       interface_no_ip_pim_drprio_cmd,
+       "no ip pim drpriority {<1-4294967295>}",
+       IP_STR
+       PIM_STR
+       "Revert the Designated Router Priority to default\n"
+       "Old Value of the Priority\n")
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) {
+    pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
+    if (pim_if_dr_election(ifp))
+      pim_hello_restart_now(ifp);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_pim_ssm,
+       interface_ip_pim_ssm_cmd,
+       "ip pim ssm",
+       IP_STR
+       PIM_STR
+       IFACE_PIM_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */);
+    if (!pim_ifp) {
+      vty_out(vty, "Could not enable PIM on interface%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  }
+  else {
+    PIM_IF_DO_PIM(pim_ifp->options);
+  }
+
+  pim_if_addr_add_all(ifp);
+  pim_if_membership_refresh(ifp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_pim_ssm,
+       interface_no_ip_pim_ssm_cmd,
+       "no ip pim ssm",
+       NO_STR
+       IP_STR
+       PIM_STR
+       IFACE_PIM_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return CMD_SUCCESS;
+
+  PIM_IF_DONT_PIM(pim_ifp->options);
+
+  pim_if_membership_clear(ifp);
+
+  /*
+    pim_if_addr_del_all() removes all sockets from
+    pim_ifp->igmp_socket_list.
+   */
+  pim_if_addr_del_all(ifp);
+
+  /*
+    pim_sock_delete() removes all neighbors from
+    pim_ifp->pim_neighbor_list.
+   */
+  pim_sock_delete(ifp, "pim unconfigured on interface");
+
+  if (!PIM_IF_TEST_IGMP(pim_ifp->options)) {
+    pim_if_delete(ifp);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_mroute,
+       interface_ip_mroute_cmd,
+       "ip mroute INTERFACE A.B.C.D",
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_addr.s_addr = INADDR_ANY;
+
+   if (pim_static_add(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to add route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_mroute_source,
+       interface_ip_mroute_source_cmd,
+       "ip mroute INTERFACE A.B.C.D A.B.C.D",
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group address\n"
+       "Source address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   const char       *src_str;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_str = argv[2];
+   result = inet_pton(AF_INET, src_str, &src_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+        src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   if (pim_static_add(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to add route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_mroute,
+       interface_no_ip_mroute_cmd,
+       "no ip mroute INTERFACE A.B.C.D",
+       NO_STR
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group Address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_addr.s_addr = INADDR_ANY;
+
+   if (pim_static_del(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to remove route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_mroute_source,
+       interface_no_ip_mroute_source_cmd,
+       "no ip mroute INTERFACE A.B.C.D A.B.C.D",
+       NO_STR
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group Address\n"
+       "Source Address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   const char       *src_str;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_str = argv[2];
+   result = inet_pton(AF_INET, src_str, &src_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+        src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   if (pim_static_del(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to remove route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_pim_hello,
+       interface_ip_pim_hello_cmd,
+       "ip pim hello <1-180>",
+       IP_STR
+       PIM_STR
+       IFACE_PIM_HELLO_STR
+       IFACE_PIM_HELLO_TIME_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  pim_ifp->pim_hello_period = strtol(argv[0], NULL, 10);
+
+  if (argc == 2)
+    pim_ifp->pim_default_holdtime = strtol(argv[1], NULL, 10);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (interface_ip_pim_hello,
+       interface_ip_pim_hello_hold_cmd,
+       "ip pim hello <1-180> <1-180>",
+       IP_STR
+       PIM_STR
+       IFACE_PIM_HELLO_STR
+       IFACE_PIM_HELLO_TIME_STR
+       IFACE_PIM_HELLO_HOLD_STR)
+
+
+DEFUN (interface_no_ip_pim_hello,
+       interface_no_ip_pim_hello_cmd,
+       "no ip pim hello {<1-180> <1-180>}",
+       NO_STR
+       IP_STR
+       PIM_STR
+       IFACE_PIM_HELLO_STR
+       IFACE_PIM_HELLO_TIME_STR
+       IFACE_PIM_HELLO_HOLD_STR)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+
+  ifp = vty->index;
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  pim_ifp->pim_hello_period     = PIM_DEFAULT_HELLO_PERIOD;
+  pim_ifp->pim_default_holdtime = -1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_igmp,
+       debug_igmp_cmd,
+       "debug igmp",
+       DEBUG_STR
+       DEBUG_IGMP_STR)
+{
+  PIM_DO_DEBUG_IGMP_EVENTS;
+  PIM_DO_DEBUG_IGMP_PACKETS;
+  PIM_DO_DEBUG_IGMP_TRACE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp,
+       no_debug_igmp_cmd,
+       "no debug igmp",
+       NO_STR
+       DEBUG_STR
+       DEBUG_IGMP_STR)
+{
+  PIM_DONT_DEBUG_IGMP_EVENTS;
+  PIM_DONT_DEBUG_IGMP_PACKETS;
+  PIM_DONT_DEBUG_IGMP_TRACE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp,
+       undebug_igmp_cmd,
+       "undebug igmp",
+       UNDEBUG_STR
+       DEBUG_IGMP_STR)
+
+DEFUN (debug_igmp_events,
+       debug_igmp_events_cmd,
+       "debug igmp events",
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_EVENTS_STR)
+{
+  PIM_DO_DEBUG_IGMP_EVENTS;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_events,
+       no_debug_igmp_events_cmd,
+       "no debug igmp events",
+       NO_STR
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_EVENTS_STR)
+{
+  PIM_DONT_DEBUG_IGMP_EVENTS;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_events,
+       undebug_igmp_events_cmd,
+       "undebug igmp events",
+       UNDEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_EVENTS_STR)
+
+DEFUN (debug_igmp_packets,
+       debug_igmp_packets_cmd,
+       "debug igmp packets",
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_PACKETS_STR)
+{
+  PIM_DO_DEBUG_IGMP_PACKETS;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_packets,
+       no_debug_igmp_packets_cmd,
+       "no debug igmp packets",
+       NO_STR
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_PACKETS_STR)
+{
+  PIM_DONT_DEBUG_IGMP_PACKETS;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_packets,
+       undebug_igmp_packets_cmd,
+       "undebug igmp packets",
+       UNDEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_PACKETS_STR)
+
+DEFUN (debug_igmp_trace,
+       debug_igmp_trace_cmd,
+       "debug igmp trace",
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_TRACE_STR)
+{
+  PIM_DO_DEBUG_IGMP_TRACE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_igmp_trace,
+       no_debug_igmp_trace_cmd,
+       "no debug igmp trace",
+       NO_STR
+       DEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_TRACE_STR)
+{
+  PIM_DONT_DEBUG_IGMP_TRACE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_igmp_trace,
+       undebug_igmp_trace_cmd,
+       "undebug igmp trace",
+       UNDEBUG_STR
+       DEBUG_IGMP_STR
+       DEBUG_IGMP_TRACE_STR)
+
+DEFUN (debug_mroute,
+       debug_mroute_cmd,
+       "debug mroute",
+       DEBUG_STR
+       DEBUG_MROUTE_STR)
+{
+  PIM_DO_DEBUG_MROUTE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_mroute,
+       no_debug_mroute_cmd,
+       "no debug mroute",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MROUTE_STR)
+{
+  PIM_DONT_DEBUG_MROUTE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_mroute,
+       undebug_mroute_cmd,
+       "undebug mroute",
+       UNDEBUG_STR
+       DEBUG_MROUTE_STR)
+
+DEFUN (debug_static,
+       debug_static_cmd,
+       "debug static",
+       DEBUG_STR
+       DEBUG_STATIC_STR)
+{
+  PIM_DO_DEBUG_STATIC;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_static,
+       no_debug_static_cmd,
+       "no debug static",
+       NO_STR
+       DEBUG_STR
+       DEBUG_STATIC_STR)
+{
+  PIM_DONT_DEBUG_STATIC;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_static,
+       undebug_static_cmd,
+       "undebug static",
+       UNDEBUG_STR
+       DEBUG_STATIC_STR)
+
+DEFUN (debug_pim,
+       debug_pim_cmd,
+       "debug pim",
+       DEBUG_STR
+       DEBUG_PIM_STR)
+{
+  PIM_DO_DEBUG_PIM_EVENTS;
+  PIM_DO_DEBUG_PIM_PACKETS;
+  PIM_DO_DEBUG_PIM_TRACE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim,
+       no_debug_pim_cmd,
+       "no debug pim",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR)
+{
+  PIM_DONT_DEBUG_PIM_EVENTS;
+  PIM_DONT_DEBUG_PIM_PACKETS;
+  PIM_DONT_DEBUG_PIM_TRACE;
+
+  PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+  PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim,
+       undebug_pim_cmd,
+       "undebug pim",
+       UNDEBUG_STR
+       DEBUG_PIM_STR)
+
+DEFUN (debug_pim_events,
+       debug_pim_events_cmd,
+       "debug pim events",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_EVENTS_STR)
+{
+  PIM_DO_DEBUG_PIM_EVENTS;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_events,
+       no_debug_pim_events_cmd,
+       "no debug pim events",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_EVENTS_STR)
+{
+  PIM_DONT_DEBUG_PIM_EVENTS;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_events,
+       undebug_pim_events_cmd,
+       "undebug pim events",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_EVENTS_STR)
+
+DEFUN (debug_pim_packets,
+       debug_pim_packets_cmd,
+       "debug pim packets",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETS_STR)
+{
+    PIM_DO_DEBUG_PIM_PACKETS;
+    vty_out (vty, "PIM Packet debugging is on %s", VTY_NEWLINE);
+    return CMD_SUCCESS;
+}
+
+DEFUN (debug_pim_packets_filter,
+       debug_pim_packets_filter_cmd,
+       "debug pim packets (hello|joins)",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETS_STR
+       DEBUG_PIM_HELLO_PACKETS_STR
+       DEBUG_PIM_J_P_PACKETS_STR)
+{
+    if (strncmp(argv[0],"h",1) == 0) 
+    {
+      PIM_DO_DEBUG_PIM_HELLO;
+      vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE);
+    }
+    else if (strncmp(argv[0],"j",1) == 0)
+    {
+      PIM_DO_DEBUG_PIM_J_P;
+      vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packets,
+       no_debug_pim_packets_cmd,
+       "no debug pim packets",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETS_STR
+       DEBUG_PIM_HELLO_PACKETS_STR
+       DEBUG_PIM_J_P_PACKETS_STR)
+{
+  PIM_DONT_DEBUG_PIM_PACKETS;
+  vty_out (vty, "PIM Packet debugging is off %s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packets_filter,
+       no_debug_pim_packets_filter_cmd,
+       "no debug pim packets (hello|joins)",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETS_STR
+       DEBUG_PIM_HELLO_PACKETS_STR
+       DEBUG_PIM_J_P_PACKETS_STR)
+{
+    if (strncmp(argv[0],"h",1) == 0) 
+    {
+      PIM_DONT_DEBUG_PIM_HELLO;
+      vty_out (vty, "PIM Hello debugging is off %s", VTY_NEWLINE);
+    }
+    else if (strncmp(argv[0],"j",1) == 0)
+    {
+      PIM_DONT_DEBUG_PIM_J_P;
+      vty_out (vty, "PIM Join/Prune debugging is off %s", VTY_NEWLINE);
+    }
+    return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packets,
+       undebug_pim_packets_cmd,
+       "undebug pim packets",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETS_STR)
+
+DEFUN (debug_pim_packetdump_send,
+       debug_pim_packetdump_send_cmd,
+       "debug pim packet-dump send",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_SEND_STR)
+{
+  PIM_DO_DEBUG_PIM_PACKETDUMP_SEND;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packetdump_send,
+       no_debug_pim_packetdump_send_cmd,
+       "no debug pim packet-dump send",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_SEND_STR)
+{
+  PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packetdump_send,
+       undebug_pim_packetdump_send_cmd,
+       "undebug pim packet-dump send",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_SEND_STR)
+
+DEFUN (debug_pim_packetdump_recv,
+       debug_pim_packetdump_recv_cmd,
+       "debug pim packet-dump receive",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_RECV_STR)
+{
+  PIM_DO_DEBUG_PIM_PACKETDUMP_RECV;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_packetdump_recv,
+       no_debug_pim_packetdump_recv_cmd,
+       "no debug pim packet-dump receive",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_RECV_STR)
+{
+  PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_packetdump_recv,
+       undebug_pim_packetdump_recv_cmd,
+       "undebug pim packet-dump receive",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_PACKETDUMP_STR
+       DEBUG_PIM_PACKETDUMP_RECV_STR)
+
+DEFUN (debug_pim_trace,
+       debug_pim_trace_cmd,
+       "debug pim trace",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_TRACE_STR)
+{
+  PIM_DO_DEBUG_PIM_TRACE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_trace,
+       no_debug_pim_trace_cmd,
+       "no debug pim trace",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_TRACE_STR)
+{
+  PIM_DONT_DEBUG_PIM_TRACE;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_trace,
+       undebug_pim_trace_cmd,
+       "undebug pim trace",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_TRACE_STR)
+
+DEFUN (debug_ssmpingd,
+       debug_ssmpingd_cmd,
+       "debug ssmpingd",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_SSMPINGD_STR)
+{
+  PIM_DO_DEBUG_SSMPINGD;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ssmpingd,
+       no_debug_ssmpingd_cmd,
+       "no debug ssmpingd",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_SSMPINGD_STR)
+{
+  PIM_DONT_DEBUG_SSMPINGD;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_ssmpingd,
+       undebug_ssmpingd_cmd,
+       "undebug ssmpingd",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_SSMPINGD_STR)
+
+DEFUN (debug_pim_zebra,
+       debug_pim_zebra_cmd,
+       "debug pim zebra",
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_ZEBRA_STR)
+{
+  PIM_DO_DEBUG_ZEBRA;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_pim_zebra,
+       no_debug_pim_zebra_cmd,
+       "no debug pim zebra",
+       NO_STR
+       DEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_ZEBRA_STR)
+{
+  PIM_DONT_DEBUG_ZEBRA;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_pim_zebra,
+       undebug_pim_zebra_cmd,
+       "undebug pim zebra",
+       UNDEBUG_STR
+       DEBUG_PIM_STR
+       DEBUG_PIM_ZEBRA_STR)
+
+DEFUN (show_debugging_pim,
+       show_debugging_pim_cmd,
+       "show debugging pim",
+       SHOW_STR
+       DEBUG_STR
+       PIM_STR)
+{
+  pim_debug_config_write(vty);
+  return CMD_SUCCESS;
+}
+
+static struct igmp_sock *find_igmp_sock_by_fd(int fd)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    struct pim_interface *pim_ifp;
+    struct igmp_sock     *igmp;
+    
+    if (!ifp->info)
+      continue;
+
+    pim_ifp = ifp->info;
+
+    /* lookup igmp socket under current interface */
+    igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd);
+    if (igmp)
+      return igmp;
+  }
+
+  return 0;
+}
+
+DEFUN (test_igmp_receive_report,
+       test_igmp_receive_report_cmd,
+       "test igmp receive report <0-65535> A.B.C.D <1-6> .LINE",
+       "Test\n"
+       "Test IGMP protocol\n"
+       "Test IGMP message\n"
+       "Test IGMP report\n"
+       "Socket\n"
+       "IGMP group address\n"
+       "Record type\n"
+       "Sources\n")
+{
+  char              buf[1000];
+  char             *igmp_msg;
+  struct ip        *ip_hdr;
+  size_t            ip_hlen; /* ip header length in bytes */
+  int               ip_msg_len;
+  int               igmp_msg_len;
+  const char       *socket;
+  int               socket_fd;
+  const char       *grp_str;
+  struct in_addr    grp_addr;
+  const char       *record_type_str;
+  int               record_type;
+  const char       *src_str;
+  int               result;
+  struct igmp_sock *igmp;
+  char             *group_record;
+  int               num_sources;
+  struct in_addr   *sources;
+  struct in_addr   *src_addr;
+  int               argi;
+
+  socket = argv[0];
+  socket_fd = atoi(socket);
+  igmp = find_igmp_sock_by_fd(socket_fd);
+  if (!igmp) {
+    vty_out(vty, "Could not find IGMP socket %s: fd=%d%s",
+           socket, socket_fd, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  grp_str = argv[1];
+  result = inet_pton(AF_INET, grp_str, &grp_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  record_type_str = argv[2];
+  record_type = atoi(record_type_str);
+
+  /*
+    Tweak IP header
+   */
+  ip_hdr = (struct ip *) buf;
+  ip_hdr->ip_p = PIM_IP_PROTO_IGMP;
+  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
+  ip_hdr->ip_src = igmp->ifaddr;
+  ip_hdr->ip_dst = igmp->ifaddr;
+
+  /*
+    Build IGMP v3 report message
+   */
+  igmp_msg = buf + ip_hlen;
+  group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+  *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */
+  *(uint16_t *)      (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+  *(uint16_t *)      (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */
+  *(uint8_t  *)      (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type;
+  memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr));
+
+  /* Scan LINE sources */
+  sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET);
+  src_addr = sources;
+  for (argi = 3; argi < argc; ++argi,++src_addr) {
+    src_str = argv[argi];
+    result = inet_pton(AF_INET, src_str, src_addr);
+    if (result <= 0) {
+      vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+             src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  }
+  num_sources = src_addr - sources;
+
+  *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources);
+
+  igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4);   /* v3 report for one single group record */
+
+  /* compute checksum */
+  *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len);
+
+  /* "receive" message */
+
+  ip_msg_len = ip_hlen + igmp_msg_len;
+  result = pim_igmp_packet(igmp, buf, ip_msg_len);
+  if (result) {
+    vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s",
+           ip_msg_len, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+static int hexval(uint8_t ch)
+{
+  return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a');
+}
+
+DEFUN (test_pim_receive_dump,
+       test_pim_receive_dump_cmd,
+       "test pim receive dump INTERFACE A.B.C.D .LINE",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test PIM packet dump reception from neighbor\n"
+       "Interface\n"
+       "Neighbor address\n"
+       "Packet dump\n")
+{
+  uint8_t           buf[1000];
+  uint8_t          *pim_msg;
+  struct ip        *ip_hdr;
+  size_t            ip_hlen; /* ip header length in bytes */
+  int               ip_msg_len;
+  int               pim_msg_size;
+  const char       *neigh_str;
+  struct in_addr    neigh_addr;
+  const char       *ifname;
+  struct interface *ifp;
+  int               argi;
+  int               result;
+
+  /* Find interface */
+  ifname = argv[0];
+  ifp = if_lookup_by_name(ifname);
+  if (!ifp) {
+    vty_out(vty, "No such interface name %s%s",
+           ifname, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Neighbor address */
+  neigh_str = argv[1];
+  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /*
+    Tweak IP header
+   */
+  ip_hdr = (struct ip *) buf;
+  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
+  ip_hdr->ip_src = neigh_addr;
+  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+  /*
+    Build PIM hello message
+  */
+  pim_msg = buf + ip_hlen;
+  pim_msg_size = 0;
+
+  /* Scan LINE dump into buffer */
+  for (argi = 2; argi < argc; ++argi) {
+    const char *str = argv[argi];
+    int str_len = strlen(str);
+    int str_last = str_len - 1;
+    int i;
+
+    if (str_len % 2) {
+      vty_out(vty, "%% Uneven hex array arg %d=%s%s",
+             argi, str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+    for (i = 0; i < str_last; i += 2) {
+      uint8_t octet;
+      int left;
+      uint8_t h1 = str[i];
+      uint8_t h2 = str[i + 1];
+
+      if (!isxdigit(h1) || !isxdigit(h2)) {
+       vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s",
+               h1, h2, argi, str, VTY_NEWLINE);
+       return CMD_WARNING;
+      }
+      octet = (hexval(h1) << 4) + hexval(h2);
+
+      left = sizeof(buf) - ip_hlen - pim_msg_size;
+      if (left < 1) {
+       vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s",
+               sizeof(buf), left, argi, str, octet, VTY_NEWLINE);
+       return CMD_WARNING;
+      }
+      
+      pim_msg[pim_msg_size++] = octet;
+    }
+  }
+
+  ip_msg_len = ip_hlen + pim_msg_size;
+
+  vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s",
+         sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE);
+
+  /* "receive" message */
+
+  result = pim_pim_packet(ifp, buf, ip_msg_len);
+  if (result) {
+    vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s",
+           ip_msg_len, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_hello,
+       test_pim_receive_hello_cmd,
+       "test pim receive hello INTERFACE A.B.C.D <0-65535> <0-65535> <0-65535> <0-32767> <0-65535> <0-1>[LINE]",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test PIM hello reception from neighbor\n"
+       "Interface\n"
+       "Neighbor address\n"
+       "Neighbor holdtime\n"
+       "Neighbor DR priority\n"
+       "Neighbor generation ID\n"
+       "Neighbor propagation delay (msec)\n"
+       "Neighbor override interval (msec)\n"
+       "Neighbor LAN prune delay T-bit\n"
+       "Neighbor secondary addresses\n")
+{
+  uint8_t           buf[1000];
+  uint8_t          *pim_msg;
+  struct ip        *ip_hdr;
+  size_t            ip_hlen; /* ip header length in bytes */
+  int               ip_msg_len;
+  int               pim_tlv_size;
+  int               pim_msg_size;
+  const char       *neigh_str;
+  struct in_addr    neigh_addr;
+  const char       *ifname;
+  struct interface *ifp;
+  uint16_t          neigh_holdtime;
+  uint16_t          neigh_propagation_delay;
+  uint16_t          neigh_override_interval;
+  int               neigh_can_disable_join_suppression;
+  uint32_t          neigh_dr_priority;
+  uint32_t          neigh_generation_id;
+  int               argi;
+  int               result;
+
+  /* Find interface */
+  ifname = argv[0];
+  ifp = if_lookup_by_name(ifname);
+  if (!ifp) {
+    vty_out(vty, "No such interface name %s%s",
+           ifname, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Neighbor address */
+  neigh_str = argv[1];
+  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  neigh_holdtime                     = atoi(argv[2]);
+  neigh_dr_priority                  = atoi(argv[3]);
+  neigh_generation_id                = atoi(argv[4]);
+  neigh_propagation_delay            = atoi(argv[5]);
+  neigh_override_interval            = atoi(argv[6]);
+  neigh_can_disable_join_suppression = atoi(argv[7]);
+
+  /*
+    Tweak IP header
+   */
+  ip_hdr = (struct ip *) buf;
+  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
+  ip_hdr->ip_src = neigh_addr;
+  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+  /*
+    Build PIM hello message
+  */
+  pim_msg = buf + ip_hlen;
+
+  /* Scan LINE addresses */
+  for (argi = 8; argi < argc; ++argi) {
+    const char *sec_str = argv[argi];
+    struct in_addr sec_addr;
+    result = inet_pton(AF_INET, sec_str, &sec_addr);
+    if (result <= 0) {
+      vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s",
+             sec_str, errno, safe_strerror(errno), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+    vty_out(vty,
+           "FIXME WRITEME consider neighbor secondary address %s%s",
+           sec_str, VTY_NEWLINE);
+  }
+
+  pim_tlv_size = pim_hello_build_tlv(ifp->name,
+                                    pim_msg + PIM_PIM_MIN_LEN,
+                                    sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN,
+                                    neigh_holdtime,
+                                    neigh_dr_priority,
+                                    neigh_generation_id,
+                                    neigh_propagation_delay,
+                                    neigh_override_interval,
+                                    neigh_can_disable_join_suppression,
+                                    0 /* FIXME secondary address list */);
+  if (pim_tlv_size < 0) {
+    vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s",
+           pim_tlv_size, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+
+  pim_msg_build_header(pim_msg, pim_msg_size,
+                      PIM_MSG_TYPE_HELLO);
+
+  /* "receive" message */
+
+  ip_msg_len = ip_hlen + pim_msg_size;
+  result = pim_pim_packet(ifp, buf, ip_msg_len);
+  if (result) {
+    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+           ip_msg_len, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_assert,
+       test_pim_receive_assert_cmd,
+       "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D <0-65535> <0-65535> <0-1>",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test reception of PIM assert\n"
+       "Interface\n"
+       "Neighbor address\n"
+       "Assert multicast group address\n"
+       "Assert unicast source address\n"
+       "Assert metric preference\n"
+       "Assert route metric\n"
+       "Assert RPT bit flag\n")
+{
+  uint8_t           buf[1000];
+  uint8_t          *buf_pastend = buf + sizeof(buf);
+  uint8_t          *pim_msg;
+  struct ip        *ip_hdr;
+  size_t            ip_hlen; /* ip header length in bytes */
+  int               ip_msg_len;
+  int               pim_msg_size;
+  const char       *neigh_str;
+  struct in_addr    neigh_addr;
+  const char       *group_str;
+  struct in_addr    group_addr;
+  const char       *source_str;
+  struct in_addr    source_addr;
+  const char       *ifname;
+  struct interface *ifp;
+  uint32_t          assert_metric_preference;
+  uint32_t          assert_route_metric;
+  uint32_t          assert_rpt_bit_flag;
+  int               remain;
+  int               result;
+
+  /* Find interface */
+  ifname = argv[0];
+  ifp = if_lookup_by_name(ifname);
+  if (!ifp) {
+    vty_out(vty, "No such interface name %s%s",
+           ifname, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Neighbor address */
+  neigh_str = argv[1];
+  result = inet_pton(AF_INET, neigh_str, &neigh_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s",
+           neigh_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Group address */
+  group_str = argv[2];
+  result = inet_pton(AF_INET, group_str, &group_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Source address */
+  source_str = argv[3];
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  assert_metric_preference = atoi(argv[4]);
+  assert_route_metric      = atoi(argv[5]);
+  assert_rpt_bit_flag      = atoi(argv[6]);
+
+  remain = buf_pastend - buf;
+  if (remain < (int) sizeof(struct ip)) {
+    vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s",
+           remain, sizeof(struct ip), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /*
+    Tweak IP header
+   */
+  ip_hdr = (struct ip *) buf;
+  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
+  ip_hdr->ip_src = neigh_addr;
+  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+  /*
+    Build PIM assert message
+  */
+  pim_msg = buf + ip_hlen; /* skip ip header */
+
+  pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp,
+                                     group_addr, source_addr,
+                                     assert_metric_preference,
+                                     assert_route_metric,
+                                     assert_rpt_bit_flag);
+  if (pim_msg_size < 0) {
+    vty_out(vty, "Failure building PIM assert message: size=%d%s",
+           pim_msg_size, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* "receive" message */
+
+  ip_msg_len = ip_hlen + pim_msg_size;
+  result = pim_pim_packet(ifp, buf, ip_msg_len);
+  if (result) {
+    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+           ip_msg_len, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+static int recv_joinprune(struct vty *vty,
+                         const char *argv[],
+                         int src_is_join)
+{
+  uint8_t           buf[1000];
+  const uint8_t    *buf_pastend = buf + sizeof(buf);
+  uint8_t          *pim_msg;
+  uint8_t          *pim_msg_curr;
+  int               pim_msg_size;
+  struct ip        *ip_hdr;
+  size_t            ip_hlen; /* ip header length in bytes */
+  int               ip_msg_len;
+  uint16_t          neigh_holdtime;
+  const char       *neigh_dst_str;
+  struct in_addr    neigh_dst_addr;
+  const char       *neigh_src_str;
+  struct in_addr    neigh_src_addr;
+  const char       *group_str;
+  struct in_addr    group_addr;
+  const char       *source_str;
+  struct in_addr    source_addr;
+  const char       *ifname;
+  struct interface *ifp;
+  int               result;
+  int               remain;
+  uint16_t          num_joined;
+  uint16_t          num_pruned;
+
+  /* Find interface */
+  ifname = argv[0];
+  ifp = if_lookup_by_name(ifname);
+  if (!ifp) {
+    vty_out(vty, "No such interface name %s%s",
+           ifname, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  neigh_holdtime = atoi(argv[1]);
+
+  /* Neighbor destination address */
+  neigh_dst_str = argv[2];
+  result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s",
+           neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Neighbor source address */
+  neigh_src_str = argv[3];
+  result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s",
+           neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Multicast group address */
+  group_str = argv[4];
+  result = inet_pton(AF_INET, group_str, &group_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Multicast source address */
+  source_str = argv[5];
+  result = inet_pton(AF_INET, source_str, &source_addr);
+  if (result <= 0) {
+    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /*
+    Tweak IP header
+   */
+  ip_hdr = (struct ip *) buf;
+  ip_hdr->ip_p = PIM_IP_PROTO_PIM;
+  ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */
+  ip_hdr->ip_hl = ip_hlen >> 2;    /* ip header length in 4-byte words */
+  ip_hdr->ip_src = neigh_src_addr;
+  ip_hdr->ip_dst = qpim_all_pim_routers_addr;
+
+  /*
+    Build PIM message
+  */
+  pim_msg = buf + ip_hlen;
+
+  /* skip room for pim header */
+  pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN;
+
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+                                               remain,
+                                               neigh_dst_addr);
+  if (!pim_msg_curr) {
+    vty_out(vty, "Failure encoding destination address %s: space left=%d%s",
+           neigh_dst_str, remain, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  remain = buf_pastend - pim_msg_curr;
+  if (remain < 4) {
+    vty_out(vty, "Group will not fit: space left=%d%s",
+           remain, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  *pim_msg_curr = 0; /* reserved */
+  ++pim_msg_curr;
+  *pim_msg_curr = 1; /* number of groups */
+  ++pim_msg_curr;
+  *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+                                               remain,
+                                               group_addr);
+  if (!pim_msg_curr) {
+    vty_out(vty, "Failure encoding group address %s: space left=%d%s",
+           group_str, remain, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  remain = buf_pastend - pim_msg_curr;
+  if (remain < 4) {
+    vty_out(vty, "Sources will not fit: space left=%d%s",
+           remain, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (src_is_join) {
+    num_joined = 1;
+    num_pruned = 0;
+  }
+  else {
+    num_joined = 0;
+    num_pruned = 1;
+  }
+
+  /* number of joined sources */
+  *((uint16_t *) pim_msg_curr) = htons(num_joined);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  /* number of pruned sources */
+  *((uint16_t *) pim_msg_curr) = htons(num_pruned);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = buf_pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
+                                                remain,
+                                                source_addr);
+  if (!pim_msg_curr) {
+    vty_out(vty, "Failure encoding source address %s: space left=%d%s",
+           source_str, remain, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Add PIM header */
+
+  pim_msg_size = pim_msg_curr - pim_msg;
+
+  pim_msg_build_header(pim_msg, pim_msg_size,
+                      PIM_MSG_TYPE_JOIN_PRUNE);
+
+  /*
+    "Receive" message
+  */
+
+  ip_msg_len = ip_hlen + pim_msg_size;
+  result = pim_pim_packet(ifp, buf, ip_msg_len);
+  if (result) {
+    vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s",
+           ip_msg_len, result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (test_pim_receive_join,
+       test_pim_receive_join_cmd,
+       "test pim receive join INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test PIM join reception from neighbor\n"
+       "Interface\n"
+       "Neighbor holdtime\n"
+       "Upstream neighbor unicast destination address\n"
+       "Downstream neighbor unicast source address\n"
+       "Multicast group address\n"
+       "Unicast source address\n")
+{
+  return recv_joinprune(vty, argv, 1 /* src_is_join=true */);
+}
+
+DEFUN (test_pim_receive_prune,
+       test_pim_receive_prune_cmd,
+       "test pim receive prune INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test PIM prune reception from neighbor\n"
+       "Interface\n"
+       "Neighbor holdtime\n"
+       "Upstream neighbor unicast destination address\n"
+       "Downstream neighbor unicast source address\n"
+       "Multicast group address\n"
+       "Unicast source address\n")
+{
+  return recv_joinprune(vty, argv, 0 /* src_is_join=false */);
+}
+
+DEFUN (test_pim_receive_upcall,
+       test_pim_receive_upcall_cmd,
+       "test pim receive upcall (nocache|wrongvif|wholepkt) <0-65535> A.B.C.D A.B.C.D",
+       "Test\n"
+       "Test PIM protocol\n"
+       "Test PIM message reception\n"
+       "Test reception of kernel upcall\n"
+       "NOCACHE kernel upcall\n"
+       "WRONGVIF kernel upcall\n"
+       "WHOLEPKT kernel upcall\n"
+       "Input interface vif index\n"
+       "Multicast group address\n"
+       "Multicast source address\n")
+{
+  struct igmpmsg msg;
+  const char *upcall_type;
+  const char *group_str;
+  const char *source_str;
+  int result;
+
+  upcall_type = argv[0];
+
+  if (upcall_type[0] == 'n')
+    msg.im_msgtype = IGMPMSG_NOCACHE;
+  else if (upcall_type[1] == 'r')
+    msg.im_msgtype = IGMPMSG_WRONGVIF;
+  else if (upcall_type[1] == 'h')
+    msg.im_msgtype = IGMPMSG_WHOLEPKT;
+  else {
+    vty_out(vty, "Unknown kernel upcall type: %s%s",
+           upcall_type, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  msg.im_vif = atoi(argv[1]);
+
+  /* Group address */
+  group_str = argv[2];
+  result = inet_pton(AF_INET, group_str, &msg.im_dst);
+  if (result <= 0) {
+    vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+           group_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  /* Source address */
+  source_str = argv[3];
+  result = inet_pton(AF_INET, source_str, &msg.im_src);
+  if (result <= 0) {
+    vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+           source_str, errno, safe_strerror(errno), VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  msg.im_mbz = 0; /* Must be zero */
+
+  result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg));
+  if (result) {
+    vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s",
+           sizeof(msg), result, VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  return CMD_SUCCESS;
+}
+
+void pim_cmd_init()
+{
+  install_node (&pim_global_node, pim_global_config_write);       /* PIM_NODE */
+  install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */
+
+  install_element (CONFIG_NODE, &ip_multicast_routing_cmd);
+  install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd);
+  install_element (CONFIG_NODE, &ip_ssmpingd_cmd);
+  install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd); 
+#if 0
+  install_element (CONFIG_NODE, &interface_cmd); /* from if.h */
+#else
+  install_element (CONFIG_NODE, &pim_interface_cmd);
+#endif
+  install_element (CONFIG_NODE, &no_interface_cmd); /* from if.h */
+
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_ip_igmp_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); 
+  install_element (INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
+  install_element (INTERFACE_NODE, &interface_ip_pim_drprio_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_pim_drprio_cmd);
+  install_element (INTERFACE_NODE, &interface_ip_pim_hello_cmd);
+  install_element (INTERFACE_NODE, &interface_ip_pim_hello_hold_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_pim_hello_cmd);
+
+  // Static mroutes NEB
+  install_element (INTERFACE_NODE, &interface_ip_mroute_cmd);
+  install_element (INTERFACE_NODE, &interface_ip_mroute_source_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_mroute_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_mroute_source_cmd);
+
+  install_element (VIEW_NODE, &show_ip_igmp_interface_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_join_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_groups_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_sources_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd);
+  install_element (VIEW_NODE, &show_ip_igmp_querier_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_assert_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_dr_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_hello_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_interface_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_join_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_rpf_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_secondary_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_upstream_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
+  install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
+  install_element (VIEW_NODE, &show_ip_multicast_cmd);
+  install_element (VIEW_NODE, &show_ip_mroute_cmd);
+  install_element (VIEW_NODE, &show_ip_mroute_count_cmd);
+  install_element (VIEW_NODE, &show_ip_rib_cmd);
+  install_element (VIEW_NODE, &show_ip_ssmpingd_cmd);
+  install_element (VIEW_NODE, &show_debugging_pim_cmd);
+
+  install_element (ENABLE_NODE, &clear_ip_interfaces_cmd);
+  install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd);
+  install_element (ENABLE_NODE, &clear_ip_mroute_cmd);
+  install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd);
+  install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd);
+
+  install_element (ENABLE_NODE, &test_igmp_receive_report_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_assert_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_dump_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_hello_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_join_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_prune_cmd);
+  install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd);
+
+  install_element (ENABLE_NODE, &debug_igmp_cmd);
+  install_element (ENABLE_NODE, &no_debug_igmp_cmd);
+  install_element (ENABLE_NODE, &undebug_igmp_cmd);
+  install_element (ENABLE_NODE, &debug_igmp_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_igmp_events_cmd);
+  install_element (ENABLE_NODE, &undebug_igmp_events_cmd);
+  install_element (ENABLE_NODE, &debug_igmp_packets_cmd);
+  install_element (ENABLE_NODE, &no_debug_igmp_packets_cmd);
+  install_element (ENABLE_NODE, &undebug_igmp_packets_cmd);
+  install_element (ENABLE_NODE, &debug_igmp_trace_cmd);
+  install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd);
+  install_element (ENABLE_NODE, &undebug_igmp_trace_cmd);
+  install_element (ENABLE_NODE, &debug_mroute_cmd);
+  install_element (ENABLE_NODE, &no_debug_mroute_cmd);
+  install_element (ENABLE_NODE, &debug_static_cmd);
+  install_element (ENABLE_NODE, &no_debug_static_cmd);
+  install_element (ENABLE_NODE, &debug_pim_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_cmd);
+  install_element (ENABLE_NODE, &debug_pim_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_events_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_events_cmd);
+  install_element (ENABLE_NODE, &debug_pim_packets_cmd);
+  install_element (ENABLE_NODE, &debug_pim_packets_filter_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_packets_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_packets_filter_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_packets_cmd);
+  install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_packetdump_send_cmd);
+  install_element (ENABLE_NODE, &debug_pim_packetdump_recv_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_packetdump_recv_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_packetdump_recv_cmd);
+  install_element (ENABLE_NODE, &debug_pim_trace_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_trace_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_trace_cmd);
+  install_element (ENABLE_NODE, &debug_ssmpingd_cmd);
+  install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd);
+  install_element (ENABLE_NODE, &undebug_ssmpingd_cmd);
+  install_element (ENABLE_NODE, &debug_pim_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd);
+  install_element (ENABLE_NODE, &undebug_pim_zebra_cmd);
+
+  install_element (CONFIG_NODE, &debug_igmp_cmd);
+  install_element (CONFIG_NODE, &no_debug_igmp_cmd);
+  install_element (CONFIG_NODE, &undebug_igmp_cmd);
+  install_element (CONFIG_NODE, &debug_igmp_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_igmp_events_cmd);
+  install_element (CONFIG_NODE, &undebug_igmp_events_cmd);
+  install_element (CONFIG_NODE, &debug_igmp_packets_cmd);
+  install_element (CONFIG_NODE, &no_debug_igmp_packets_cmd);
+  install_element (CONFIG_NODE, &undebug_igmp_packets_cmd);
+  install_element (CONFIG_NODE, &debug_igmp_trace_cmd);
+  install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd);
+  install_element (CONFIG_NODE, &undebug_igmp_trace_cmd);
+  install_element (CONFIG_NODE, &debug_mroute_cmd);
+  install_element (CONFIG_NODE, &no_debug_mroute_cmd);
+  install_element (CONFIG_NODE, &debug_static_cmd);
+  install_element (CONFIG_NODE, &no_debug_static_cmd);
+  install_element (CONFIG_NODE, &debug_pim_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_cmd);
+  install_element (CONFIG_NODE, &undebug_pim_cmd);
+  install_element (CONFIG_NODE, &debug_pim_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_events_cmd);
+  install_element (CONFIG_NODE, &undebug_pim_events_cmd);
+  install_element (CONFIG_NODE, &debug_pim_packets_cmd);
+  install_element (CONFIG_NODE, &debug_pim_packets_filter_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_packets_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_packets_filter_cmd);
+  install_element (CONFIG_NODE, &undebug_pim_packets_cmd);
+  install_element (CONFIG_NODE, &debug_pim_trace_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_trace_cmd);
+  install_element (CONFIG_NODE, &undebug_pim_trace_cmd);
+  install_element (CONFIG_NODE, &debug_ssmpingd_cmd);
+  install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd);
+  install_element (CONFIG_NODE, &undebug_ssmpingd_cmd);
+  install_element (CONFIG_NODE, &debug_pim_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd);
+  install_element (CONFIG_NODE, &undebug_pim_zebra_cmd);
+}
diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h
new file mode 100644 (file)
index 0000000..a1cb581
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_CMD_H
+#define PIM_CMD_H
+
+#define PIM_STR                                "PIM information\n"
+#define IGMP_STR                               "IGMP information\n"
+#define IGMP_GROUP_STR                         "IGMP groups information\n"
+#define IGMP_SOURCE_STR                        "IGMP sources information\n"
+#define CONF_SSMPINGD_STR                      "Enable ssmpingd operation\n"
+#define SHOW_SSMPINGD_STR                      "ssmpingd operation\n"
+#define IFACE_PIM_STR                          "Enable PIM SSM operation\n"
+#define IFACE_PIM_HELLO_STR                    "Hello Interval\n"
+#define IFACE_PIM_HELLO_TIME_STR               "Time in seconds for Hello Interval\n"
+#define IFACE_PIM_HELLO_HOLD_STR               "Time in seconds for Hold Interval\n"
+#define IFACE_IGMP_STR                         "Enable IGMP operation\n"
+#define IFACE_IGMP_QUERY_INTERVAL_STR          "IGMP host query interval\n"
+#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR      "IGMP max query response value (seconds)\n"
+#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n"
+#define DEBUG_IGMP_STR                              "IGMP protocol activity\n"
+#define DEBUG_IGMP_EVENTS_STR                       "IGMP protocol events\n"
+#define DEBUG_IGMP_PACKETS_STR                      "IGMP protocol packets\n"
+#define DEBUG_IGMP_TRACE_STR                        "IGMP internal daemon activity\n"
+#define DEBUG_MROUTE_STR                            "PIM interaction with kernel MFC cache\n"
+#define DEBUG_STATIC_STR                            "PIM Static Multicast Route activity\n"
+#define DEBUG_PIM_STR                               "PIM protocol activity\n"
+#define DEBUG_PIM_EVENTS_STR                        "PIM protocol events\n"
+#define DEBUG_PIM_PACKETS_STR                       "PIM protocol packets\n"
+#define DEBUG_PIM_HELLO_PACKETS_STR                 "PIM Hello protocol packets\n"
+#define DEBUG_PIM_J_P_PACKETS_STR                   "PIM Join/Prune protocol packets\n"
+#define DEBUG_PIM_PACKETDUMP_STR                    "PIM packet dump\n"
+#define DEBUG_PIM_PACKETDUMP_SEND_STR               "Dump sent packets\n"
+#define DEBUG_PIM_PACKETDUMP_RECV_STR               "Dump received packets\n"
+#define DEBUG_PIM_TRACE_STR                         "PIM internal daemon activity\n"
+#define DEBUG_PIM_ZEBRA_STR                         "ZEBRA protocol activity\n"
+#define DEBUG_SSMPINGD_STR                          "ssmpingd activity\n"
+#define CLEAR_IP_IGMP_STR                           "IGMP clear commands\n"
+#define CLEAR_IP_PIM_STR                            "PIM clear commands\n"
+#define MROUTE_STR                                  "IP multicast routing table\n"
+#define RIB_STR                                     "IP unicast routing table\n"
+
+#define PIM_CMD_NO                                   "no"
+#define PIM_CMD_IP_MULTICAST_ROUTING                 "ip multicast-routing"
+#define PIM_CMD_IP_IGMP_QUERY_INTERVAL               "ip igmp query-interval"
+#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME      "ip igmp query-max-response-time"
+#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "ip igmp query-max-response-time-dsec"
+
+void pim_cmd_init(void);
+
+#endif /* PIM_CMD_H */
diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c
new file mode 100644 (file)
index 0000000..bfc128b
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_util.h"
+#include "pim_hello.h"
+#include "pim_iface.h"
+#include "pim_neighbor.h"
+#include "pim_upstream.h"
+
+static void on_trace(const char *label,
+                    struct interface *ifp, struct in_addr src)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+    zlog_debug("%s: from %s on %s",
+              label, src_str, ifp->name);
+  }
+}
+
+static void tlv_trace_bool(const char *label, const char *tlv_name,
+                          const char *ifname, struct in_addr src_addr,
+                          int isset, int value)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
+              label, 
+              src_str, ifname,
+              tlv_name, value);
+  }
+}
+
+static void tlv_trace_uint16(const char *label, const char *tlv_name,
+                            const char *ifname, struct in_addr src_addr,
+                            int isset, uint16_t value)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
+              label, 
+              src_str, ifname,
+              tlv_name, value);
+  }
+}
+
+static void tlv_trace_uint32(const char *label, const char *tlv_name,
+                            const char *ifname, struct in_addr src_addr,
+                            int isset, uint32_t value)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
+              label, 
+              src_str, ifname,
+              tlv_name, value);
+  }
+}
+
+static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
+                                const char *ifname, struct in_addr src_addr,
+                                int isset, uint32_t value)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
+              label, 
+              src_str, ifname,
+              tlv_name, value);
+  }
+}
+
+#if 0
+static void tlv_trace(const char *label, const char *tlv_name,
+                     const char *ifname, struct in_addr src_addr,
+                     int isset)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s",
+              label, 
+              src_str, ifname,
+              tlv_name);
+  }
+}
+#endif
+
+static void tlv_trace_list(const char *label, const char *tlv_name,
+                          const char *ifname, struct in_addr src_addr,
+                          int isset, struct list *addr_list)
+{
+  if (isset) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
+              label, 
+              src_str, ifname,
+              tlv_name,
+              addr_list ? ((int) listcount(addr_list)) : -1,
+              (void *) addr_list);
+  }
+}
+
+#define FREE_ADDR_LIST \
+  if (hello_option_addr_list) { \
+    list_delete(hello_option_addr_list); \
+  }
+
+#define FREE_ADDR_LIST_THEN_RETURN(code) \
+{ \
+  FREE_ADDR_LIST \
+  return (code); \
+}
+
+int pim_hello_recv(struct interface *ifp,
+                  struct in_addr src_addr,
+                  uint8_t *tlv_buf, int tlv_buf_size)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+  uint8_t *tlv_curr;
+  uint8_t *tlv_pastend;
+  pim_hello_options hello_options = 0; /* bit array recording options found */
+  uint16_t hello_option_holdtime = 0;
+  uint16_t hello_option_propagation_delay = 0;
+  uint16_t hello_option_override_interval = 0;
+  uint32_t hello_option_dr_priority = 0;
+  uint32_t hello_option_generation_id = 0;
+  struct list *hello_option_addr_list = 0;
+
+  if (PIM_DEBUG_PIM_HELLO)
+    on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  ++pim_ifp->pim_ifstat_hello_recv;
+
+  /*
+    Parse PIM hello TLVs
+   */
+  zassert(tlv_buf_size >= 0);
+  tlv_curr = tlv_buf;
+  tlv_pastend = tlv_buf + tlv_buf_size;
+
+  while (tlv_curr < tlv_pastend) {
+    uint16_t option_type; 
+    uint16_t option_len;
+    int remain = tlv_pastend - tlv_curr;
+
+    if (remain < PIM_TLV_MIN_SIZE) {
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
+                  __PRETTY_FUNCTION__,
+                  remain, PIM_TLV_MIN_SIZE,
+                  src_str, ifp->name);
+      }
+      FREE_ADDR_LIST_THEN_RETURN(-1);
+    }
+
+    option_type = PIM_TLV_GET_TYPE(tlv_curr);
+    tlv_curr += PIM_TLV_TYPE_SIZE;
+    option_len = PIM_TLV_GET_LENGTH(tlv_curr);
+    tlv_curr += PIM_TLV_LENGTH_SIZE;
+
+    if ((tlv_curr + option_len) > tlv_pastend) {
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
+                  __PRETTY_FUNCTION__,
+                  option_type, option_len, tlv_pastend - tlv_curr,
+                  src_str, ifp->name);
+      }
+      FREE_ADDR_LIST_THEN_RETURN(-2);
+    }
+
+    if (PIM_DEBUG_PIM_HELLO) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
+                __PRETTY_FUNCTION__,
+                remain,
+                option_type, option_len,
+                src_str, ifp->name);
+    }
+
+    switch (option_type) {
+    case PIM_MSG_OPTION_TYPE_HOLDTIME:
+      if (pim_tlv_parse_holdtime(ifp->name, src_addr,
+                                &hello_options,
+                                &hello_option_holdtime,
+                                option_len,
+                                tlv_curr)) {
+       FREE_ADDR_LIST_THEN_RETURN(-3);
+      }
+      break;
+    case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
+      if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
+                                       &hello_options,
+                                       &hello_option_propagation_delay,
+                                       &hello_option_override_interval,
+                                       option_len,
+                                       tlv_curr)) {
+       FREE_ADDR_LIST_THEN_RETURN(-4);
+      }
+      break;
+    case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
+      if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
+                                   &hello_options,
+                                   &hello_option_dr_priority,
+                                   option_len,
+                                   tlv_curr)) {
+       FREE_ADDR_LIST_THEN_RETURN(-5);
+      }
+      break;
+    case PIM_MSG_OPTION_TYPE_GENERATION_ID:
+      if (pim_tlv_parse_generation_id(ifp->name, src_addr,
+                                     &hello_options,
+                                     &hello_option_generation_id,
+                                     option_len,
+                                     tlv_curr)) {
+       FREE_ADDR_LIST_THEN_RETURN(-6);
+      }
+      break;
+    case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
+      if (pim_tlv_parse_addr_list(ifp->name, src_addr,
+                                 &hello_options,
+                                 &hello_option_addr_list,
+                                 option_len,
+                                 tlv_curr)) {
+       return -7;
+      }
+      break;
+    case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
+                  __PRETTY_FUNCTION__,
+                  option_type, option_len,
+                  src_str, ifp->name);
+      }
+      break;
+    default:
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
+                  __PRETTY_FUNCTION__,
+                  option_type, option_len,
+                  src_str, ifp->name);
+      }
+    }
+
+    tlv_curr += option_len;
+  }
+
+  /*
+    Check received PIM hello options
+  */
+
+  if (PIM_DEBUG_PIM_HELLO) {
+    tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
+                    ifp->name, src_addr,
+                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
+                    hello_option_holdtime);
+    tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
+                    ifp->name, src_addr,
+                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+                    hello_option_propagation_delay);
+    tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
+                    ifp->name, src_addr,
+                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+                    hello_option_override_interval);
+    tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
+                  ifp->name, src_addr,
+                  PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
+                  PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
+    tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
+                    ifp->name, src_addr,
+                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
+                    hello_option_dr_priority);
+    tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
+                        ifp->name, src_addr,
+                        PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
+                        hello_option_generation_id);
+    tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
+                  ifp->name, src_addr,
+                  PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
+                  hello_option_addr_list);
+  }
+
+  if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, ifp->name);
+    }
+  }
+
+  /*
+    New neighbor?
+  */
+
+  neigh = pim_neighbor_find(ifp, src_addr);
+  if (!neigh) {
+    /* Add as new neighbor */
+    
+    neigh = pim_neighbor_add(ifp, src_addr,
+                            hello_options,
+                            hello_option_holdtime,
+                            hello_option_propagation_delay,
+                            hello_option_override_interval,
+                            hello_option_dr_priority,
+                            hello_option_generation_id,
+                            hello_option_addr_list);
+    if (!neigh) {
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
+                 __PRETTY_FUNCTION__,
+                 src_str, ifp->name);
+      }
+      FREE_ADDR_LIST_THEN_RETURN(-8);
+    }
+
+    /* actual addr list has been saved under neighbor */
+    return 0;
+  }
+
+  /*
+    Received generation ID ?
+  */
+  
+  if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
+    /* GenID mismatch ? */
+    if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
+       (hello_option_generation_id != neigh->generation_id)) {
+
+      /* GenID changed */
+
+      pim_upstream_rpf_genid_changed(neigh->source_addr);
+
+      /* GenID mismatch, then replace neighbor */
+      
+      if (PIM_DEBUG_PIM_HELLO) {
+       char src_str[100];
+       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+       zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
+                  __PRETTY_FUNCTION__,
+                  hello_option_generation_id,
+                  neigh->generation_id,
+                  src_str, ifp->name);
+      }
+
+      pim_upstream_rpf_genid_changed(neigh->source_addr);
+      
+      pim_neighbor_delete(ifp, neigh, "GenID mismatch");
+      neigh = pim_neighbor_add(ifp, src_addr,
+                              hello_options,
+                              hello_option_holdtime,
+                              hello_option_propagation_delay,
+                              hello_option_override_interval,
+                              hello_option_dr_priority,
+                              hello_option_generation_id,
+                              hello_option_addr_list);
+      if (!neigh) {
+       if (PIM_DEBUG_PIM_HELLO) {
+         char src_str[100];
+         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+         zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
+                    __PRETTY_FUNCTION__,
+                    src_str, ifp->name);
+       }
+       FREE_ADDR_LIST_THEN_RETURN(-9);
+      }
+      /* actual addr list is saved under neighbor */
+      return 0;
+
+    } /* GenId mismatch: replace neighbor */
+    
+  } /* GenId received */
+
+  /*
+    Update existing neighbor
+  */
+
+  pim_neighbor_update(neigh,
+                     hello_options,
+                     hello_option_holdtime,
+                     hello_option_dr_priority,
+                     hello_option_addr_list);
+  /* actual addr list is saved under neighbor */
+  return 0;
+}
+
+int pim_hello_build_tlv(const char *ifname,
+                       uint8_t *tlv_buf, int tlv_buf_size,
+                       uint16_t holdtime,
+                       uint32_t dr_priority,
+                       uint32_t generation_id,
+                       uint16_t propagation_delay,
+                       uint16_t override_interval,
+                       int can_disable_join_suppression,
+                       struct list *ifconnected)
+{
+  uint8_t *curr = tlv_buf;
+  uint8_t *pastend = tlv_buf + tlv_buf_size;
+  uint8_t *tmp;
+
+  /*
+   * Append options
+   */
+
+  /* Holdtime */
+  curr = pim_tlv_append_uint16(curr,
+                              pastend,
+                              PIM_MSG_OPTION_TYPE_HOLDTIME,
+                              holdtime);
+  if (!curr) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
+                __PRETTY_FUNCTION__, ifname);
+    }
+    return -1;
+  }
+
+  /* LAN Prune Delay */
+  tmp = pim_tlv_append_2uint16(curr,
+                              pastend,
+                              PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
+                              propagation_delay,
+                              override_interval);
+  if (!tmp) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
+                __PRETTY_FUNCTION__, ifname);
+    }
+    return -1;
+  }
+  if (can_disable_join_suppression) {
+    *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
+  }
+  curr = tmp;
+
+  /* DR Priority */
+  curr = pim_tlv_append_uint32(curr,
+                              pastend,
+                              PIM_MSG_OPTION_TYPE_DR_PRIORITY,
+                              dr_priority);
+  if (!curr) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
+                __PRETTY_FUNCTION__, ifname);
+    }
+    return -2;
+  }
+
+  /* Generation ID */
+  curr = pim_tlv_append_uint32(curr,
+                              pastend,
+                              PIM_MSG_OPTION_TYPE_GENERATION_ID,
+                              generation_id);
+  if (!curr) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
+                __PRETTY_FUNCTION__, ifname);
+    }
+    return -3;
+  }
+
+  /* Secondary Address List */
+  if (ifconnected) {
+    curr = pim_tlv_append_addrlist_ucast(curr,
+                                        pastend,
+                                        ifconnected);
+    if (!curr) {
+      if (PIM_DEBUG_PIM_HELLO) {
+       zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
+                 __PRETTY_FUNCTION__, ifname);
+      }
+      return -4;
+    }
+  }
+
+  return curr - tlv_buf;
+}
+
+/*
+  RFC 4601: 4.3.1.  Sending Hello Messages
+
+  Thus, if a router needs to send a Join/Prune or Assert message on an
+  interface on which it has not yet sent a Hello message with the
+  currently configured IP address, then it MUST immediately send the
+  relevant Hello message without waiting for the Hello Timer to
+  expire, followed by the Join/Prune or Assert message.
+*/
+void pim_hello_require(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+
+  zassert(pim_ifp);
+
+  if (pim_ifp->pim_ifstat_hello_sent)
+    return;
+
+  pim_hello_restart_now(ifp); /* Send hello and restart timer */
+}
diff --git a/pimd/pim_hello.h b/pimd/pim_hello.h
new file mode 100644 (file)
index 0000000..b5e272d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_HELLO_H
+#define PIM_HELLO_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+int pim_hello_recv(struct interface *ifp,
+                  struct in_addr src_addr,
+                  uint8_t *tlv_buf, int tlv_buf_size);
+
+int pim_hello_build_tlv(const char *ifname,
+                       uint8_t *tlv_buf, int tlv_buf_size,
+                       uint16_t holdtime,
+                       uint32_t dr_priority,
+                       uint32_t generation_id,
+                       uint16_t propagation_delay,
+                       uint16_t override_interval,
+                       int can_disable_join_suppression,
+                       struct list *ifconnected);
+
+void pim_hello_require(struct interface *ifp);
+
+#endif /* PIM_HELLO_H */
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
new file mode 100644 (file)
index 0000000..ddad6cb
--- /dev/null
@@ -0,0 +1,1194 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "vty.h"
+#include "memory.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_iface.h"
+#include "pim_igmp.h"
+#include "pim_mroute.h"
+#include "pim_oil.h"
+#include "pim_str.h"
+#include "pim_pim.h"
+#include "pim_neighbor.h"
+#include "pim_ifchannel.h"
+#include "pim_sock.h"
+#include "pim_time.h"
+#include "pim_ssmpingd.h"
+
+static void pim_if_igmp_join_del_all(struct interface *ifp);
+
+static void *if_list_clean(struct pim_interface *pim_ifp)
+{
+  if (pim_ifp->igmp_join_list) {
+    list_delete(pim_ifp->igmp_join_list);
+  }
+
+  if (pim_ifp->igmp_socket_list) {
+    list_delete(pim_ifp->igmp_socket_list);
+  }
+
+  if (pim_ifp->pim_neighbor_list) {
+    list_delete(pim_ifp->pim_neighbor_list);
+  }
+
+  if (pim_ifp->pim_ifchannel_list) {
+    list_delete(pim_ifp->pim_ifchannel_list);
+  }
+
+  XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
+
+  return 0;
+}
+
+struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  zassert(!ifp->info);
+
+  pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp));
+  if (!pim_ifp) {
+    zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp));
+    return 0;
+  }
+
+  pim_ifp->options                           = 0;
+  pim_ifp->mroute_vif_index                  = -1;
+
+  pim_ifp->igmp_default_robustness_variable           = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
+  pim_ifp->igmp_default_query_interval                = IGMP_GENERAL_QUERY_INTERVAL;
+  pim_ifp->igmp_query_max_response_time_dsec          = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
+  pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
+
+  /*
+    RFC 3376: 8.3. Query Response Interval
+    The number of seconds represented by the [Query Response Interval]
+    must be less than the [Query Interval].
+   */
+  zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval);
+
+  if (pim)
+    PIM_IF_DO_PIM(pim_ifp->options);
+  if (igmp)
+    PIM_IF_DO_IGMP(pim_ifp->options);
+
+#if 0
+  /* FIXME: Should join? */
+  PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options);
+#endif
+
+  pim_ifp->igmp_join_list = 0;
+  pim_ifp->igmp_socket_list = 0;
+  pim_ifp->pim_neighbor_list = 0;
+  pim_ifp->pim_ifchannel_list = 0;
+  pim_ifp->pim_generation_id = 0;
+
+  /* list of struct igmp_sock */
+  pim_ifp->igmp_socket_list = list_new();
+  if (!pim_ifp->igmp_socket_list) {
+    zlog_err("%s %s: failure: igmp_socket_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return if_list_clean(pim_ifp);
+  }
+  pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free;
+
+  /* list of struct pim_neighbor */
+  pim_ifp->pim_neighbor_list = list_new();
+  if (!pim_ifp->pim_neighbor_list) {
+    zlog_err("%s %s: failure: pim_neighbor_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return if_list_clean(pim_ifp);
+  }
+  pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free;
+
+  /* list of struct pim_ifchannel */
+  pim_ifp->pim_ifchannel_list = list_new();
+  if (!pim_ifp->pim_ifchannel_list) {
+    zlog_err("%s %s: failure: pim_ifchannel_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return if_list_clean(pim_ifp);
+  }
+  pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free;
+
+  ifp->info = pim_ifp;
+
+  pim_sock_reset(ifp);
+
+  zassert(PIM_IF_TEST_PIM(pim_ifp->options) || PIM_IF_TEST_IGMP(pim_ifp->options));
+
+  if (PIM_MROUTE_IS_ENABLED) {
+    pim_if_add_vif(ifp);
+  }
+
+  return pim_ifp;
+}
+
+void pim_if_delete(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (pim_ifp->igmp_join_list) {
+    pim_if_igmp_join_del_all(ifp);
+  }
+  zassert(!pim_ifp->igmp_join_list);
+
+  zassert(pim_ifp->igmp_socket_list);
+  zassert(!listcount(pim_ifp->igmp_socket_list));
+
+  zassert(pim_ifp->pim_neighbor_list);
+  zassert(!listcount(pim_ifp->pim_neighbor_list));
+
+  zassert(pim_ifp->pim_ifchannel_list);
+  zassert(!listcount(pim_ifp->pim_ifchannel_list));
+
+  if (PIM_MROUTE_IS_ENABLED) {
+    pim_if_del_vif(ifp);
+  }
+
+  list_delete(pim_ifp->igmp_socket_list);
+  list_delete(pim_ifp->pim_neighbor_list);
+  list_delete(pim_ifp->pim_ifchannel_list);
+
+  XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
+
+  ifp->info = 0;
+}
+
+void pim_if_update_could_assert(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct listnode      *next_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+    pim_ifchannel_update_could_assert(ch);
+  }
+}
+
+static void pim_if_update_my_assert_metric(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct listnode      *next_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+    pim_ifchannel_update_my_assert_metric(ch);
+  }
+}
+
+static void pim_addr_change(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  pim_if_dr_election(ifp); /* router's own DR Priority (addr) changes -- Done TODO T30 */
+  pim_if_update_join_desired(pim_ifp); /* depends on DR */
+  pim_if_update_could_assert(ifp); /* depends on DR */
+  pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
+  pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+
+    1) Before an interface goes down or changes primary IP address, a
+    Hello message with a zero HoldTime should be sent immediately
+    (with the old IP address if the IP address changed).
+    -- FIXME See CAVEAT C13
+
+    2) After an interface has changed its IP address, it MUST send a
+    Hello message with its new IP address.
+    -- DONE below
+
+    3) If an interface changes one of its secondary IP addresses, a
+    Hello message with an updated Address_List option and a non-zero
+    HoldTime should be sent immediately.
+    -- FIXME See TODO T31
+   */
+  pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */
+  if (pim_ifp->pim_sock_fd < 0)
+    return;
+  pim_hello_restart_now(ifp);         /* send hello and restart timer */
+}
+
+static int detect_primary_address_change(struct interface *ifp,
+                                        int force_prim_as_any,
+                                        const char *caller)
+{
+  struct pim_interface *pim_ifp;
+  struct in_addr new_prim_addr;
+  int changed;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return 0;
+
+  if (force_prim_as_any)
+    new_prim_addr = qpim_inaddr_any;
+  else
+    new_prim_addr = pim_find_primary_addr(ifp);
+
+  changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr;
+
+  if (PIM_DEBUG_ZEBRA) {
+    char new_prim_str[100];
+    char old_prim_str[100];
+    pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str));
+    pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str));
+    zlog_debug("%s: old=%s new=%s on interface %s: %s",
+              __PRETTY_FUNCTION__, 
+              old_prim_str, new_prim_str, ifp->name,
+              changed ? "changed" : "unchanged");
+  }
+
+  if (changed) {
+    pim_ifp->primary_address = new_prim_addr;
+
+    if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+      return changed;
+    }
+
+    pim_addr_change(ifp);
+  }
+
+  return changed;
+}
+
+static void detect_secondary_address_change(struct interface *ifp,
+                                           const char *caller)
+{
+  struct pim_interface *pim_ifp;
+  int changed;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+
+  changed = 1; /* true */
+  if (PIM_DEBUG_ZEBRA)
+    zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change",
+             __PRETTY_FUNCTION__, ifp->name);
+
+  if (!changed) {
+    return;
+  }
+
+  if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
+    return;
+  }
+
+  pim_addr_change(ifp);
+}
+
+static void detect_address_change(struct interface *ifp,
+                                int force_prim_as_any,
+                                const char *caller)
+{
+  int prim_changed;
+
+  prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller);
+  if (prim_changed) {
+    /* no need to detect secondary change because
+       the reaction would be the same */
+    return;
+  }
+
+  detect_secondary_address_change(ifp, caller);
+}
+
+void pim_if_addr_add(struct connected *ifc)
+{
+  struct pim_interface *pim_ifp;
+  struct interface *ifp;
+  struct in_addr ifaddr;
+
+  zassert(ifc);
+
+  ifp = ifc->ifp;
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+
+  if (!if_is_operative(ifp))
+    return;
+
+  if (PIM_DEBUG_ZEBRA) {
+    char buf[BUFSIZ];
+    prefix2str(ifc->address, buf, BUFSIZ);
+    zlog_debug("%s: %s ifindex=%d connected IP address %s %s",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, buf,
+              CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ?
+              "secondary" : "primary");
+  }
+
+  ifaddr = ifc->address->u.prefix4;
+
+  detect_address_change(ifp, 0, __PRETTY_FUNCTION__);
+
+  if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
+    struct igmp_sock *igmp;
+
+    /* lookup IGMP socket */
+    igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
+                                      ifaddr);
+    if (!igmp) {
+      /* if addr new, add IGMP socket */
+      pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp);
+    }
+  } /* igmp */
+
+  if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+
+    /* Interface has a valid primary address ? */
+    if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
+
+      /* Interface has a valid socket ? */
+      if (pim_ifp->pim_sock_fd < 0) {
+       if (pim_sock_add(ifp)) {
+         zlog_warn("Failure creating PIM socket for interface %s",
+                   ifp->name);
+       }
+      }
+
+    }
+  } /* pim */
+
+  if (PIM_MROUTE_IS_ENABLED) {
+    /*
+      PIM or IGMP is enabled on interface, and there is at least one
+      address assigned, then try to create a vif_index.
+    */
+    if (pim_ifp->mroute_vif_index < 0) {
+      pim_if_add_vif(ifp);
+    }
+  }
+}
+
+static void pim_if_addr_del_igmp(struct connected *ifc)
+{
+  struct pim_interface *pim_ifp = ifc->ifp->info;
+  struct igmp_sock *igmp;
+  struct in_addr ifaddr;
+
+  if (ifc->address->family != AF_INET) {
+    /* non-IPv4 address */
+    return;
+  }
+
+  if (!pim_ifp) {
+    /* IGMP not enabled on interface */
+    return;
+  }
+
+  ifaddr = ifc->address->u.prefix4;
+
+  /* lookup IGMP socket */
+  igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
+                                    ifaddr);
+  if (igmp) {
+    /* if addr found, del IGMP socket */
+    igmp_sock_delete(igmp);
+  }
+}
+
+static void pim_if_addr_del_pim(struct connected *ifc)
+{
+  struct pim_interface *pim_ifp = ifc->ifp->info;
+
+  if (ifc->address->family != AF_INET) {
+    /* non-IPv4 address */
+    return;
+  }
+
+  if (!pim_ifp) {
+    /* PIM not enabled on interface */
+    return;
+  }
+
+  if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
+    /* Interface keeps a valid primary address */
+    return;
+  }
+
+  if (pim_ifp->pim_sock_fd < 0) {
+    /* Interface does not hold a valid socket any longer */
+    return;
+  }
+
+  /*
+    pim_sock_delete() closes the socket, stops read and timer threads,
+    and kills all neighbors.
+   */
+  pim_sock_delete(ifc->ifp, "last address has been removed from interface");
+}
+
+void pim_if_addr_del(struct connected *ifc, int force_prim_as_any)
+{
+  struct interface *ifp;
+
+  zassert(ifc);
+  ifp = ifc->ifp;
+  zassert(ifp);
+
+  if (PIM_DEBUG_ZEBRA) {
+    char buf[BUFSIZ];
+    prefix2str(ifc->address, buf, BUFSIZ);
+    zlog_debug("%s: %s ifindex=%d disconnected IP address %s %s",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, buf,
+              CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ?
+              "secondary" : "primary");
+  }
+
+  detect_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__);
+
+  pim_if_addr_del_igmp(ifc);
+  pim_if_addr_del_pim(ifc);
+}
+
+void pim_if_addr_add_all(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+  struct listnode *nextnode;
+
+  /* PIM/IGMP enabled ? */
+  if (!ifp->info)
+    return;
+
+  for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    pim_if_addr_add(ifc);
+  }
+}
+
+void pim_if_addr_del_all(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+  struct listnode *nextnode;
+
+  /* PIM/IGMP enabled ? */
+  if (!ifp->info)
+    return;
+
+  for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */);
+  }
+}
+
+void pim_if_addr_del_all_igmp(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+  struct listnode *nextnode;
+
+  /* PIM/IGMP enabled ? */
+  if (!ifp->info)
+    return;
+
+  for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    pim_if_addr_del_igmp(ifc);
+  }
+}
+
+void pim_if_addr_del_all_pim(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+  struct listnode *nextnode;
+
+  /* PIM/IGMP enabled ? */
+  if (!ifp->info)
+    return;
+
+  for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    pim_if_addr_del_pim(ifc);
+  }
+}
+
+static struct in_addr find_first_nonsec_addr(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+  struct in_addr addr;
+
+  for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
+      zlog_warn("%s: null IPv4 address connected to interface %s",
+               __PRETTY_FUNCTION__, ifp->name);
+      continue;
+    }
+
+    if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
+      continue;
+
+    return p->u.prefix4;
+  }
+
+  addr.s_addr = PIM_NET_INADDR_ANY;
+
+  return addr;
+}
+
+struct in_addr pim_find_primary_addr(struct interface *ifp)
+{
+  return find_first_nonsec_addr(ifp);
+}
+
+/*
+  pim_if_add_vif() uses ifindex as vif_index
+
+  see also pim_if_find_vifindex_by_ifindex()
+ */
+int pim_if_add_vif(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+  struct in_addr ifaddr;
+
+  zassert(pim_ifp);
+
+  if (pim_ifp->mroute_vif_index > 0) {
+    zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d",
+             __PRETTY_FUNCTION__,
+             pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
+    return -1;
+  }
+
+  if (ifp->ifindex < 1) {
+    zlog_warn("%s: ifindex=%d < 1 on interface %s",
+             __PRETTY_FUNCTION__,
+             ifp->ifindex, ifp->name);
+    return -2;
+  }
+
+  if (ifp->ifindex >= MAXVIFS) {
+    zlog_warn("%s: ifindex=%d >= MAXVIFS=%d on interface %s",
+             __PRETTY_FUNCTION__,
+             ifp->ifindex, MAXVIFS, ifp->name);
+    return -3;
+  }
+
+  ifaddr = pim_ifp->primary_address;
+  if (PIM_INADDR_IS_ANY(ifaddr)) {
+    zlog_warn("%s: could not get address for interface %s ifindex=%d",
+             __PRETTY_FUNCTION__,
+             ifp->name, ifp->ifindex);
+    return -4;
+  }
+
+  if (pim_mroute_add_vif(ifp->ifindex, ifaddr)) {
+    /* pim_mroute_add_vif reported error */
+    return -5;
+  }
+
+  pim_ifp->mroute_vif_index = ifp->ifindex;
+
+  /*
+    Update highest vif_index
+   */
+  if (pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) {
+    qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index;
+  }
+
+  return 0;
+}
+
+static int iflist_find_highest_vif_index()
+{
+  struct listnode      *ifnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  int                   highest_vif_index = -1;
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    if (pim_ifp->mroute_vif_index > highest_vif_index) {
+      highest_vif_index = pim_ifp->mroute_vif_index;
+    }
+  }
+
+  return highest_vif_index;
+}
+
+int pim_if_del_vif(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+  int old_vif_index;
+
+  if (pim_ifp->mroute_vif_index < 1) {
+    zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d",
+             __PRETTY_FUNCTION__,
+             pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex);
+    return -1;
+  }
+
+  if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) {
+    /* pim_mroute_del_vif reported error */
+    return -2;
+  }
+
+  /*
+    Update highest vif_index
+   */
+
+  /* save old vif_index in order to compare with highest below */
+  old_vif_index = pim_ifp->mroute_vif_index;
+
+  pim_ifp->mroute_vif_index = -1;
+
+  if (old_vif_index == qpim_mroute_oif_highest_vif_index) {
+    qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index();
+  }
+
+  return 0;
+}
+
+void pim_if_add_vif_all()
+{
+  struct listnode  *ifnode;
+  struct listnode  *ifnextnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    if (!ifp->info)
+      continue;
+
+    pim_if_add_vif(ifp);
+  }
+}
+
+void pim_if_del_vif_all()
+{
+  struct listnode  *ifnode;
+  struct listnode  *ifnextnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    if (!ifp->info)
+      continue;
+
+    pim_if_del_vif(ifp);
+  }
+}
+
+struct interface *pim_if_find_by_vif_index(int vif_index)
+{
+  struct listnode  *ifnode;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) {
+    if (ifp->info) {
+      struct pim_interface *pim_ifp;
+      pim_ifp = ifp->info;
+      if (vif_index == pim_ifp->mroute_vif_index)
+       return ifp;
+    }
+  }
+
+  return 0;
+}
+
+/*
+  pim_if_add_vif() uses ifindex as vif_index
+ */
+int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex)
+{
+  return ifindex;
+}
+
+int pim_if_lan_delay_enabled(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+  zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0);
+
+  return pim_ifp->pim_number_of_nonlandelay_neighbors == 0;
+}
+
+uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp)
+{
+  if (pim_if_lan_delay_enabled(ifp)) {
+    struct pim_interface *pim_ifp;
+    pim_ifp = ifp->info;
+    return pim_ifp->pim_neighbors_highest_propagation_delay_msec;
+  }
+  else {
+    return PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
+  }
+}
+
+uint16_t pim_if_effective_override_interval_msec(struct interface *ifp)
+{
+  if (pim_if_lan_delay_enabled(ifp)) {
+    struct pim_interface *pim_ifp;
+    pim_ifp = ifp->info;
+    return pim_ifp->pim_neighbors_highest_override_interval_msec;
+  }
+  else {
+    return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
+  }
+}
+
+int pim_if_t_override_msec(struct interface *ifp)
+{
+  int effective_override_interval_msec;
+  int t_override_msec;
+
+  effective_override_interval_msec =
+    pim_if_effective_override_interval_msec(ifp);
+
+  t_override_msec = random() % (effective_override_interval_msec + 1);
+
+  return t_override_msec;
+}
+
+uint16_t pim_if_jp_override_interval_msec(struct interface *ifp)
+{
+  return pim_if_effective_propagation_delay_msec(ifp) +
+    pim_if_effective_override_interval_msec(ifp);
+}
+
+/*
+  RFC 4601: 4.1.6.  State Summarization Macros
+
+  The function NBR( I, A ) uses information gathered through PIM Hello
+  messages to map the IP address A of a directly connected PIM
+  neighbor router on interface I to the primary IP address of the same
+  router (Section 4.3.4).  The primary IP address of a neighbor is the
+  address that it uses as the source of its PIM Hello messages.
+*/
+struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
+                                         struct in_addr addr)
+{
+  struct listnode *neighnode;
+  struct pim_neighbor *neigh;
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             ifp->name);
+    return 0;
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) {
+
+    /* primary address ? */
+    if (neigh->source_addr.s_addr == addr.s_addr)
+      return neigh;
+
+    /* secondary address ? */
+    if (pim_neighbor_find_secondary(neigh, addr))
+       return neigh;
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_debug("%s: neighbor not found for address %s on interface %s",
+              __PRETTY_FUNCTION__, 
+              addr_str, ifp->name);
+  }
+
+  return 0;
+}
+
+long pim_if_t_suppressed_msec(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  long t_suppressed_msec;
+  uint32_t ramount = 0;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  /* join suppression disabled ? */
+  if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options))
+    return 0;
+
+  /* t_suppressed = t_periodic * rand(1.1, 1.4) */
+  ramount = 1100 + (random() % (1400 - 1100 + 1));
+  t_suppressed_msec = qpim_t_periodic * ramount;
+
+  return t_suppressed_msec;
+}
+
+static void igmp_join_free(struct igmp_join *ij)
+{
+  XFREE(MTYPE_PIM_IGMP_JOIN, ij);
+}
+
+static struct igmp_join *igmp_join_find(struct list *join_list,
+                                       struct in_addr group_addr,
+                                       struct in_addr source_addr)
+{
+  struct listnode *node;
+  struct igmp_join *ij;
+
+  zassert(join_list);
+
+  for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) {
+    if ((group_addr.s_addr == ij->group_addr.s_addr) &&
+       (source_addr.s_addr == ij->source_addr.s_addr))
+      return ij;
+  }
+
+  return 0;
+}
+
+static int igmp_join_sock(const char *ifname,
+                         ifindex_t ifindex,
+                         struct in_addr group_addr,
+                         struct in_addr source_addr)
+{
+  int join_fd;
+
+  join_fd = pim_socket_raw(IPPROTO_IGMP);
+  if (join_fd < 0) {
+    return -1;
+  }
+
+  if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) {
+    close(join_fd);
+    return -2;
+  }
+
+  return join_fd;
+}
+
+static struct igmp_join *igmp_join_new(struct interface *ifp,
+                                      struct in_addr group_addr,
+                                      struct in_addr source_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct igmp_join *ij;
+  int join_fd;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr);
+  if (join_fd < 0) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
+             __PRETTY_FUNCTION__,
+             group_str, source_str, ifp->name);
+    return 0;
+  }
+
+  ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij));
+  if (!ij) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s",
+            __PRETTY_FUNCTION__,
+            sizeof(*ij), group_str, source_str, ifp->name);
+    close(join_fd);
+    return 0;
+  }
+
+  ij->sock_fd       = join_fd;
+  ij->group_addr    = group_addr;
+  ij->source_addr   = source_addr;
+  ij->sock_creation = pim_time_monotonic_sec();
+
+  listnode_add(pim_ifp->igmp_join_list, ij);
+
+  return ij;
+}
+
+int pim_if_igmp_join_add(struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct igmp_join *ij;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__, 
+             ifp->name);
+    return -1;
+  }
+
+  if (!pim_ifp->igmp_join_list) {
+    pim_ifp->igmp_join_list = list_new();
+    if (!pim_ifp->igmp_join_list) {
+      zlog_err("%s %s: failure: igmp_join_list=list_new()",
+              __FILE__, __PRETTY_FUNCTION__);
+      return -2;
+    }
+    pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free;
+  }
+
+  ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
+  if (ij) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s",
+             __PRETTY_FUNCTION__,
+             group_str, source_str, ifp->name);
+    return -3;
+  }
+
+  ij = igmp_join_new(ifp, group_addr, source_addr);
+  if (!ij) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s",
+             __PRETTY_FUNCTION__,
+             group_str, source_str, ifp->name);
+    return -4;
+  }
+
+  if (PIM_DEBUG_IGMP_EVENTS) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s",
+             __PRETTY_FUNCTION__,
+             source_str, group_str, ifp->name);
+  }
+
+  return 0;
+}
+
+
+
+int pim_if_igmp_join_del(struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct igmp_join *ij;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__, 
+             ifp->name);
+    return -1;
+  }
+
+  if (!pim_ifp->igmp_join_list) {
+    zlog_warn("%s: no IGMP join on interface %s",
+             __PRETTY_FUNCTION__, 
+             ifp->name);
+    return -2;
+  }
+
+  ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr);
+  if (!ij) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: could not find IGMP group %s source %s on interface %s",
+             __PRETTY_FUNCTION__,
+             group_str, source_str, ifp->name);
+    return -3;
+  }
+
+  if (close(ij->sock_fd)) {
+    int e = errno;
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
+             __PRETTY_FUNCTION__,
+             ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e));
+    /* warning only */
+  }
+  listnode_delete(pim_ifp->igmp_join_list, ij);
+  igmp_join_free(ij);
+  if (listcount(pim_ifp->igmp_join_list) < 1) {
+    list_delete(pim_ifp->igmp_join_list);
+    pim_ifp->igmp_join_list = 0;
+  }
+
+  return 0;
+}
+
+static void pim_if_igmp_join_del_all(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *node;
+  struct listnode *nextnode;
+  struct igmp_join *ij;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    zlog_warn("%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__, 
+             ifp->name);
+    return;
+  }
+
+  if (!pim_ifp->igmp_join_list)
+    return;
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij))
+    pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr);
+}
+
+/*
+  RFC 4601
+
+  Transitions from "I am Assert Loser" State
+
+  Current Winner's GenID Changes or NLT Expires
+
+  The Neighbor Liveness Timer associated with the current winner
+  expires or we receive a Hello message from the current winner
+  reporting a different GenID from the one it previously reported.
+  This indicates that the current winner's interface or router has
+  gone down (and may have come back up), and so we must assume it no
+  longer knows it was the winner.
+ */
+void pim_if_assert_on_neighbor_down(struct interface *ifp,
+                                   struct in_addr neigh_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct listnode      *next_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+    /* Is (S,G,I) assert loser ? */
+    if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER)
+      continue;
+    /* Dead neighbor was winner ? */
+    if (ch->ifassert_winner.s_addr != neigh_addr.s_addr)
+      continue;
+    
+    assert_action_a5(ch);
+  }
+}
+
+void pim_if_update_join_desired(struct pim_interface *pim_ifp)
+{
+  struct listnode      *ch_node;
+  struct pim_ifchannel *ch;
+
+  /* clear off flag from interface's upstreams */
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+    PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags);
+  }
+
+  /* scan per-interface (S,G,I) state on this I interface */
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+    struct pim_upstream *up = ch->upstream;
+
+    if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags))
+      continue;
+
+    /* update join_desired for the global (S,G) state */
+    pim_upstream_update_join_desired(up);
+    PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags);
+  }
+}
+
+void pim_if_update_assert_tracking_desired(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct listnode      *next_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+    pim_ifchannel_update_assert_tracking_desired(ch);
+  }
+}
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
new file mode 100644 (file)
index 0000000..8cad3d1
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IFACE_H
+#define PIM_IFACE_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vty.h"
+
+#include "pim_igmp.h"
+#include "pim_upstream.h"
+
+#define PIM_IF_MASK_PIM                             (1 << 0)
+#define PIM_IF_MASK_IGMP                            (1 << 1)
+#define PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS          (1 << 2)
+#define PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION (1 << 3)
+
+#define PIM_IF_IS_DELETED(ifp) ((ifp)->ifindex == IFINDEX_INTERNAL)
+
+#define PIM_IF_TEST_PIM(options) (PIM_IF_MASK_PIM & (options))
+#define PIM_IF_TEST_IGMP(options) (PIM_IF_MASK_IGMP & (options))
+#define PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(options) (PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS & (options))
+#define PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) (PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION & (options))
+
+#define PIM_IF_DO_PIM(options) ((options) |= PIM_IF_MASK_PIM)
+#define PIM_IF_DO_IGMP(options) ((options) |= PIM_IF_MASK_IGMP)
+#define PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(options) ((options) |= PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
+#define PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) |= PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
+
+#define PIM_IF_DONT_PIM(options) ((options) &= ~PIM_IF_MASK_PIM)
+#define PIM_IF_DONT_IGMP(options) ((options) &= ~PIM_IF_MASK_IGMP)
+#define PIM_IF_DONT_IGMP_LISTEN_ALLROUTERS(options) ((options) &= ~PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS)
+#define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION)
+
+struct pim_interface {
+  uint32_t       options;                            /* bit vector */
+  int            mroute_vif_index;
+  struct in_addr primary_address; /* remember addr to detect change */
+
+  int          igmp_default_robustness_variable;            /* IGMPv3 QRV */
+  int          igmp_default_query_interval;                 /* IGMPv3 secs between general queries */
+  int          igmp_query_max_response_time_dsec;           /* IGMPv3 Max Response Time in dsecs for general queries */
+  int          igmp_specific_query_max_response_time_dsec;  /* IGMPv3 Max Response Time in dsecs for specific queries */
+  struct list *igmp_socket_list;                            /* list of struct igmp_sock */
+  struct list *igmp_join_list;                              /* list of struct igmp_join */
+
+  int            pim_sock_fd;       /* PIM socket file descriptor */
+  struct thread *t_pim_sock_read;   /* thread for reading PIM socket */
+  int64_t        pim_sock_creation; /* timestamp of PIM socket creation */
+
+  struct thread *t_pim_hello_timer;
+  int            pim_hello_period;
+  int            pim_default_holdtime;
+  int            pim_triggered_hello_delay;
+  uint32_t       pim_generation_id;
+  uint16_t       pim_propagation_delay_msec; /* config */
+  uint16_t       pim_override_interval_msec; /* config */
+  struct list   *pim_neighbor_list; /* list of struct pim_neighbor */
+  struct list   *pim_ifchannel_list; /* list of struct pim_ifchannel */
+
+  /* neighbors without lan_delay */
+  int            pim_number_of_nonlandelay_neighbors;
+  uint16_t       pim_neighbors_highest_propagation_delay_msec;
+  uint16_t       pim_neighbors_highest_override_interval_msec;
+
+  /* DR Election */
+  int64_t        pim_dr_election_last; /* timestamp */
+  int            pim_dr_election_count;
+  int            pim_dr_election_changes;
+  struct in_addr pim_dr_addr;
+  uint32_t       pim_dr_priority;            /* config */
+  int            pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */
+
+  int64_t        pim_ifstat_start; /* start timestamp for stats */
+  uint32_t       pim_ifstat_hello_sent;
+  uint32_t       pim_ifstat_hello_sendfail;
+  uint32_t       pim_ifstat_hello_recv;
+  uint32_t       pim_ifstat_hello_recvfail;
+};
+
+/*
+  if default_holdtime is set (>= 0), use it;
+  otherwise default_holdtime is 3.5 * hello_period
+ */
+#define PIM_IF_DEFAULT_HOLDTIME(pim_ifp) \
+  (((pim_ifp)->pim_default_holdtime < 0) ? \
+  ((pim_ifp)->pim_hello_period * 7 / 2) : \
+  ((pim_ifp)->pim_default_holdtime))
+
+struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim);
+void                  pim_if_delete(struct interface *ifp);
+void pim_if_addr_add(struct connected *ifc);
+void pim_if_addr_del(struct connected *ifc, int force_prim_as_any);
+void pim_if_addr_add_all(struct interface *ifp);
+void pim_if_addr_del_all(struct interface *ifp);
+void pim_if_addr_del_all_igmp(struct interface *ifp);
+void pim_if_addr_del_all_pim(struct interface *ifp);
+
+int pim_if_add_vif(struct interface *ifp);
+int pim_if_del_vif(struct interface *ifp);
+void pim_if_add_vif_all(void);
+void pim_if_del_vif_all(void);
+
+struct interface *pim_if_find_by_vif_index(ifindex_t vif_index);
+int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex);
+
+int pim_if_lan_delay_enabled(struct interface *ifp);
+uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp);
+uint16_t pim_if_effective_override_interval_msec(struct interface *ifp);
+uint16_t pim_if_jp_override_interval_msec(struct interface *ifp);
+struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp,
+                                         struct in_addr addr);
+
+long pim_if_t_suppressed_msec(struct interface *ifp);
+int pim_if_t_override_msec(struct interface *ifp);
+
+struct in_addr pim_find_primary_addr(struct interface *ifp);
+
+int pim_if_igmp_join_add(struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr);
+int pim_if_igmp_join_del(struct interface *ifp,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr);
+
+void pim_if_update_could_assert(struct interface *ifp);
+
+void pim_if_assert_on_neighbor_down(struct interface *ifp,
+                                   struct in_addr neigh_addr);
+
+void pim_if_rpf_interface_changed(struct interface *old_rpf_ifp,
+                                 struct pim_upstream *up);
+
+void pim_if_update_join_desired(struct pim_interface *pim_ifp);
+
+void pim_if_update_assert_tracking_desired(struct interface *ifp);
+
+#endif /* PIM_IFACE_H */
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
new file mode 100644 (file)
index 0000000..ad97879
--- /dev/null
@@ -0,0 +1,898 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_ifchannel.h"
+#include "pim_zebra.h"
+#include "pim_time.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_join.h"
+#include "pim_rpf.h"
+#include "pim_macro.h"
+
+void pim_ifchannel_free(struct pim_ifchannel *ch)
+{
+  zassert(!ch->t_ifjoin_expiry_timer);
+  zassert(!ch->t_ifjoin_prune_pending_timer);
+  zassert(!ch->t_ifassert_timer);
+
+  XFREE(MTYPE_PIM_IFCHANNEL, ch);
+}
+
+void pim_ifchannel_delete(struct pim_ifchannel *ch)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ch->interface->info;
+  zassert(pim_ifp);
+
+  if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
+    pim_upstream_update_join_desired(ch->upstream);
+  }
+
+  pim_upstream_del(ch->upstream);
+
+  THREAD_OFF(ch->t_ifjoin_expiry_timer);
+  THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+  THREAD_OFF(ch->t_ifassert_timer);
+
+  /*
+    notice that listnode_delete() can't be moved
+    into pim_ifchannel_free() because the later is
+    called by list_delete_all_node()
+  */
+  listnode_delete(pim_ifp->pim_ifchannel_list, ch);
+
+  pim_ifchannel_free(ch);
+}
+
+#define IFCHANNEL_NOINFO(ch)                                   \
+  (                                                            \
+   ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)       \
+   &&                                                          \
+   ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO)                   \
+   &&                                                          \
+   ((ch)->ifassert_state == PIM_IFASSERT_NOINFO)               \
+   )
+   
+static void delete_on_noinfo(struct pim_ifchannel *ch)
+{
+  if (IFCHANNEL_NOINFO(ch)) {
+
+    /* In NOINFO state, timers should have been cleared */
+    zassert(!ch->t_ifjoin_expiry_timer);
+    zassert(!ch->t_ifjoin_prune_pending_timer);
+    zassert(!ch->t_ifassert_timer);
+    
+    pim_ifchannel_delete(ch);
+  }
+}
+
+void pim_ifchannel_ifjoin_switch(const char *caller,
+                                struct pim_ifchannel *ch,
+                                enum pim_ifjoin_state new_state)
+{
+  enum pim_ifjoin_state old_state = ch->ifjoin_state;
+
+  if (old_state == new_state) {
+    if (PIM_DEBUG_PIM_EVENTS) {
+      zlog_debug("%s calledby %s: non-transition on state %d (%s)",
+                __PRETTY_FUNCTION__, caller, new_state,
+                pim_ifchannel_ifjoin_name(new_state));
+    }
+    return;
+  }
+
+  zassert(old_state != new_state);
+
+  ch->ifjoin_state = new_state;
+
+  /* Transition to/from NOINFO ? */
+  if (
+      (old_state == PIM_IFJOIN_NOINFO)
+      ||
+      (new_state == PIM_IFJOIN_NOINFO)
+      ) {
+
+    if (PIM_DEBUG_PIM_EVENTS) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
+                ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
+                src_str, grp_str, ch->interface->name);
+    }
+
+    /*
+      Record uptime of state transition to/from NOINFO
+    */
+    ch->ifjoin_creation = pim_time_monotonic_sec();
+
+    pim_upstream_update_join_desired(ch->upstream);
+    pim_ifchannel_update_could_assert(ch);
+    pim_ifchannel_update_assert_tracking_desired(ch);
+  }
+}
+
+const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
+{
+  switch (ifjoin_state) {
+  case PIM_IFJOIN_NOINFO:        return "NOINFO";
+  case PIM_IFJOIN_JOIN:          return "JOIN";
+  case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
+  }
+
+  return "ifjoin_bad_state";
+}
+
+const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
+{
+  switch (ifassert_state) {
+  case PIM_IFASSERT_NOINFO:      return "NOINFO";
+  case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
+  case PIM_IFASSERT_I_AM_LOSER:  return "LOSER";
+  }
+
+  return "ifassert_bad_state";
+}
+
+/*
+  RFC 4601: 4.6.5.  Assert State Macros
+
+  AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
+  defaults to Infinity when in the NoInfo state.
+*/
+void reset_ifassert_state(struct pim_ifchannel *ch)
+{
+  THREAD_OFF(ch->t_ifassert_timer);
+
+  pim_ifassert_winner_set(ch,
+                         PIM_IFASSERT_NOINFO,
+                         qpim_inaddr_any,
+                         qpim_infinite_assert_metric);
+}
+
+static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
+                                              struct in_addr source_addr,
+                                              struct in_addr group_addr)
+{
+  struct pim_ifchannel *ch;
+  struct pim_interface *pim_ifp;
+  struct pim_upstream  *up;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  up = pim_upstream_add(source_addr, group_addr);
+  if (!up) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
+            __PRETTY_FUNCTION__,
+            src_str, grp_str, ifp->name);
+    return 0;
+  }
+
+  ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
+  if (!ch) {
+    zlog_err("%s: PIM XMALLOC(%zu) failure",
+            __PRETTY_FUNCTION__, sizeof(*ch));
+    return 0;
+  }
+
+  ch->flags                        = 0;
+  ch->upstream                     = up;
+  ch->interface                    = ifp;
+  ch->source_addr                  = source_addr;
+  ch->group_addr                   = group_addr;
+  ch->local_ifmembership           = PIM_IFMEMBERSHIP_NOINFO;
+
+  ch->ifjoin_state                 = PIM_IFJOIN_NOINFO;
+  ch->t_ifjoin_expiry_timer        = 0;
+  ch->t_ifjoin_prune_pending_timer = 0;
+  ch->ifjoin_creation              = 0;
+
+  ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
+  ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
+
+  ch->ifassert_winner.s_addr = 0;
+
+  /* Assert state */
+  ch->t_ifassert_timer   = 0;
+  reset_ifassert_state(ch);
+  if (pim_macro_ch_could_assert_eval(ch))
+    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+  else
+    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+  if (pim_macro_assert_tracking_desired_eval(ch))
+    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+  else
+    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+  /* Attach to list */
+  listnode_add(pim_ifp->pim_ifchannel_list, ch);
+
+  zassert(IFCHANNEL_NOINFO(ch));
+
+  return ch;
+}
+
+struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
+                                        struct in_addr source_addr,
+                                        struct in_addr group_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *ch_node;
+  struct pim_ifchannel *ch;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str,
+             ifp->name);
+    return 0;
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+    if (
+       (source_addr.s_addr == ch->source_addr.s_addr) &&
+       (group_addr.s_addr == ch->group_addr.s_addr)
+       ) {
+      return ch;
+    }
+  }
+
+  return 0;
+}
+
+static void ifmembership_set(struct pim_ifchannel *ch,
+                            enum pim_ifmembership membership)
+{
+  if (ch->local_ifmembership == membership)
+    return;
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str,
+              membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
+              ch->interface->name);
+  }
+  
+  ch->local_ifmembership = membership;
+
+  pim_upstream_update_join_desired(ch->upstream);
+  pim_ifchannel_update_could_assert(ch);
+  pim_ifchannel_update_assert_tracking_desired(ch);
+}
+
+
+void pim_ifchannel_membership_clear(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *ch_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
+    ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
+  }
+}
+
+void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct listnode      *next_node;
+  struct pim_ifchannel *ch;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
+    delete_on_noinfo(ch);
+  }
+}
+
+struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr)
+{
+  struct pim_ifchannel *ch;
+  char src_str[100];
+  char grp_str[100];
+
+  ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+  if (ch)
+    return ch;
+
+  ch = pim_ifchannel_new(ifp, source_addr, group_addr);
+  if (ch)
+    return ch;
+    
+  pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+  pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+  zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
+           __PRETTY_FUNCTION__,
+           src_str, grp_str, ifp->name);
+
+  return 0;
+}
+
+static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
+{
+  pim_forward_stop(ch);
+  pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
+  delete_on_noinfo(ch);
+}
+
+static int on_ifjoin_expiry_timer(struct thread *t)
+{
+  struct pim_ifchannel *ch;
+
+  zassert(t);
+  ch = THREAD_ARG(t);
+  zassert(ch);
+
+  ch->t_ifjoin_expiry_timer = 0;
+
+  zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
+
+  ifjoin_to_noinfo(ch);
+  /* ch may have been deleted */
+
+  return 0;
+}
+
+static void prune_echo(struct interface *ifp,
+                      struct in_addr source_addr,
+                      struct in_addr group_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct in_addr neigh_dst_addr;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  neigh_dst_addr = pim_ifp->primary_address;
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char source_str[100];
+    char group_str[100];
+    char neigh_dst_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
+    zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
+              __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
+  }
+
+  pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
+                    0 /* boolean: send_join=false (prune) */);
+}
+
+static int on_ifjoin_prune_pending_timer(struct thread *t)
+{
+  struct pim_ifchannel *ch;
+  int send_prune_echo; /* boolean */
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct in_addr ch_source;
+  struct in_addr ch_group;
+
+  zassert(t);
+  ch = THREAD_ARG(t);
+  zassert(ch);
+
+  ch->t_ifjoin_prune_pending_timer = 0;
+
+  zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
+
+  /* Send PruneEcho(S,G) ? */
+  ifp = ch->interface;
+  pim_ifp = ifp->info;
+  send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
+
+  /* Save (S,G) */
+  ch_source = ch->source_addr;
+  ch_group = ch->group_addr;
+
+  ifjoin_to_noinfo(ch);
+  /* from here ch may have been deleted */
+
+  if (send_prune_echo)
+    prune_echo(ifp, ch_source, ch_group);
+
+  return 0;
+}
+
+static void check_recv_upstream(int is_join,
+                               struct interface *recv_ifp,
+                               struct in_addr upstream,
+                               struct in_addr source_addr,
+                               struct in_addr group_addr,
+                               uint8_t source_flags,
+                               int holdtime)
+{
+  struct pim_upstream *up;
+
+  /* Upstream (S,G) in Joined state ? */
+  up = pim_upstream_find(source_addr, group_addr);
+  if (!up)
+    return;
+  if (up->join_state != PIM_UPSTREAM_JOINED)
+    return;
+
+  /* Upstream (S,G) in Joined state */
+
+  if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+    /* RPF'(S,G) not found */
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s %s: RPF'(%s,%s) not found",
+             __FILE__, __PRETTY_FUNCTION__, 
+             src_str, grp_str);
+    return;
+  }
+
+  /* upstream directed to RPF'(S,G) ? */
+  if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
+    char src_str[100];
+    char grp_str[100];
+    char up_str[100];
+    char rpf_str[100];
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
+    pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+    zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
+             __FILE__, __PRETTY_FUNCTION__, 
+             src_str, grp_str,
+             up_str, rpf_str, recv_ifp->name);
+    return;
+  }
+  /* upstream directed to RPF'(S,G) */
+
+  if (is_join) {
+    /* Join(S,G) to RPF'(S,G) */
+    pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
+    return;
+  }
+
+  /* Prune to RPF'(S,G) */
+
+  if (source_flags & PIM_RPT_BIT_MASK) {
+    if (source_flags & PIM_WILDCARD_BIT_MASK) {
+      /* Prune(*,G) to RPF'(S,G) */
+      pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
+                                                    up, up->rpf.rpf_addr);
+      return;
+    }
+
+    /* Prune(S,G,rpt) to RPF'(S,G) */
+    pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
+                                                  up, up->rpf.rpf_addr);
+    return;
+  }
+
+  /* Prune(S,G) to RPF'(S,G) */
+  pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
+                                                up->rpf.rpf_addr);
+}
+
+static int nonlocal_upstream(int is_join,
+                            struct interface *recv_ifp,
+                            struct in_addr upstream,
+                            struct in_addr source_addr,
+                            struct in_addr group_addr,
+                            uint8_t source_flags,
+                            uint16_t holdtime)
+{
+  struct pim_interface *recv_pim_ifp;
+  int is_local; /* boolean */
+
+  recv_pim_ifp = recv_ifp->info;
+  zassert(recv_pim_ifp);
+
+  is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
+  
+  if (PIM_DEBUG_PIM_TRACE) {
+    char up_str[100];
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
+             __PRETTY_FUNCTION__,
+             is_join ? "join" : "prune",
+             src_str, grp_str,
+             is_local ? "local" : "non-local",
+             up_str, recv_ifp->name);
+  }
+
+  if (is_local)
+    return 0;
+
+  /*
+    Since recv upstream addr was not directed to our primary
+    address, check if we should react to it in any way.
+  */
+  check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
+                     source_flags, holdtime);
+
+  return 1; /* non-local */
+}
+
+void pim_ifchannel_join_add(struct interface *ifp,
+                           struct in_addr neigh_addr,
+                           struct in_addr upstream,
+                           struct in_addr source_addr,
+                           struct in_addr group_addr,
+                           uint8_t source_flags,
+                           uint16_t holdtime)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  if (nonlocal_upstream(1 /* join */, ifp, upstream,
+                       source_addr, group_addr, source_flags, holdtime)) {
+    return;
+  }
+
+  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  if (!ch)
+    return;
+
+  /*
+    RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+    Transitions from "I am Assert Loser" State
+
+    Receive Join(S,G) on Interface I
+
+    We receive a Join(S,G) that has the Upstream Neighbor Address
+    field set to my primary IP address on interface I.  The action is
+    to transition to NoInfo state, delete this (S,G) assert state
+    (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
+    to operate.
+
+    Notice: The nonlocal_upstream() test above ensures the upstream
+    address of the join message is our primary address.
+   */
+  if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+    char src_str[100];
+    char grp_str[100];
+    char neigh_str[100];
+    pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
+    zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, neigh_str, ifp->name);
+
+    assert_action_a5(ch);
+  }
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  switch (ch->ifjoin_state) {
+  case PIM_IFJOIN_NOINFO:
+    pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+    if (pim_macro_chisin_oiflist(ch)) {
+      pim_forward_start(ch);
+    }
+    break;
+  case PIM_IFJOIN_JOIN:
+    zassert(!ch->t_ifjoin_prune_pending_timer);
+
+    /*
+      In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
+      previously received join message with holdtime=0xFFFF.
+     */
+    if (ch->t_ifjoin_expiry_timer) {
+      unsigned long remain =
+       thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
+      if (remain > holdtime) {
+       /*
+         RFC 4601: 4.5.3.  Receiving (S,G) Join/Prune Messages
+
+         Transitions from Join State
+
+          The (S,G) downstream state machine on interface I remains in
+          Join state, and the Expiry Timer (ET) is restarted, set to
+          maximum of its current value and the HoldTime from the
+          triggering Join/Prune message.
+
+         Conclusion: Do not change the ET if the current value is
+         higher than the received join holdtime.
+        */
+       return;
+      }
+    }
+    THREAD_OFF(ch->t_ifjoin_expiry_timer);
+    break;
+  case PIM_IFJOIN_PRUNE_PENDING:
+    zassert(!ch->t_ifjoin_expiry_timer);
+    zassert(ch->t_ifjoin_prune_pending_timer);
+    THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
+    pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
+    break;
+  }
+
+  zassert(!IFCHANNEL_NOINFO(ch));
+
+  if (holdtime != 0xFFFF) {
+    THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
+                   on_ifjoin_expiry_timer,
+                   ch, holdtime);
+  }
+}
+
+void pim_ifchannel_prune(struct interface *ifp,
+                        struct in_addr upstream,
+                        struct in_addr source_addr,
+                        struct in_addr group_addr,
+                        uint8_t source_flags,
+                        uint16_t holdtime)
+{
+  struct pim_ifchannel *ch;
+  int jp_override_interval_msec;
+
+  if (nonlocal_upstream(0 /* prune */, ifp, upstream,
+                       source_addr, group_addr, source_flags, holdtime)) {
+    return;
+  }
+
+  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  if (!ch)
+    return;
+
+  switch (ch->ifjoin_state) {
+  case PIM_IFJOIN_NOINFO:
+  case PIM_IFJOIN_PRUNE_PENDING:
+    /* nothing to do */
+    break;
+  case PIM_IFJOIN_JOIN:
+    {
+      struct pim_interface *pim_ifp;
+
+      pim_ifp = ifp->info;
+
+      zassert(ch->t_ifjoin_expiry_timer);
+      zassert(!ch->t_ifjoin_prune_pending_timer);
+
+      THREAD_OFF(ch->t_ifjoin_expiry_timer);
+      
+      pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
+      
+      if (listcount(pim_ifp->pim_neighbor_list) > 1) {
+       jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
+      }
+      else {
+       jp_override_interval_msec = 0; /* schedule to expire immediately */
+       /* If we called ifjoin_prune() directly instead, care should
+          be taken not to use "ch" afterwards since it would be
+          deleted. */
+      }
+      
+      THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
+                          on_ifjoin_prune_pending_timer,
+                          ch, jp_override_interval_msec);
+      
+      zassert(!ch->t_ifjoin_expiry_timer);
+      zassert(ch->t_ifjoin_prune_pending_timer);
+    }
+    break;
+  }
+
+}
+
+void pim_ifchannel_local_membership_add(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr)
+{
+  struct pim_ifchannel *ch;
+  struct pim_interface *pim_ifp;
+
+  /* PIM enabled on interface? */
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+  if (!PIM_IF_TEST_PIM(pim_ifp->options))
+    return;
+
+  ch = pim_ifchannel_add(ifp, source_addr, group_addr);
+  if (!ch) {
+    return;
+  }
+
+  ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
+
+  zassert(!IFCHANNEL_NOINFO(ch));
+}
+
+void pim_ifchannel_local_membership_del(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr)
+{
+  struct pim_ifchannel *ch;
+  struct pim_interface *pim_ifp;
+
+  /* PIM enabled on interface? */
+  pim_ifp = ifp->info;
+  if (!pim_ifp)
+    return;
+  if (!PIM_IF_TEST_PIM(pim_ifp->options))
+    return;
+
+  ch = pim_ifchannel_find(ifp, source_addr, group_addr);
+  if (!ch)
+    return;
+
+  ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
+
+  delete_on_noinfo(ch);
+}
+
+void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
+{
+  int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
+  int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
+
+  if (new_couldassert == old_couldassert)
+    return;
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str, ch->interface->name,
+              old_couldassert, new_couldassert);
+  }
+
+  if (new_couldassert) {
+    /* CouldAssert(S,G,I) switched from FALSE to TRUE */
+    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
+  }
+  else {
+    /* CouldAssert(S,G,I) switched from TRUE to FALSE */
+    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
+
+    if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
+      assert_action_a4(ch);
+    }
+  }
+
+  pim_ifchannel_update_my_assert_metric(ch);
+}
+
+/*
+  my_assert_metric may be affected by:
+
+  CouldAssert(S,G)
+  pim_ifp->primary_address
+  rpf->source_nexthop.mrib_metric_preference;
+  rpf->source_nexthop.mrib_route_metric;
+ */
+void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
+{
+  struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
+
+  if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
+      return;
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    char old_addr_str[100];
+    char new_addr_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
+    pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
+    zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str, ch->interface->name,
+              ch->ifassert_my_metric.rpt_bit_flag,
+              ch->ifassert_my_metric.metric_preference,
+              ch->ifassert_my_metric.route_metric,
+              old_addr_str,
+              my_metric_new.rpt_bit_flag,
+              my_metric_new.metric_preference,
+              my_metric_new.route_metric,
+              new_addr_str);
+  }
+
+  ch->ifassert_my_metric = my_metric_new;
+
+  if (pim_assert_metric_better(&ch->ifassert_my_metric,
+                              &ch->ifassert_winner_metric)) {
+    assert_action_a5(ch);
+  }
+}
+
+void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
+{
+  int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
+  int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
+
+  if (new_atd == old_atd)
+    return;
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
+              __PRETTY_FUNCTION__,
+              src_str, grp_str, ch->interface->name,
+              old_atd, new_atd);
+  }
+
+  if (new_atd) {
+    /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
+    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
+  }
+  else {
+    /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
+    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
+
+    if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+      assert_action_a5(ch);
+    }
+  }
+}
diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h
new file mode 100644 (file)
index 0000000..e6f1c29
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IFCHANNEL_H
+#define PIM_IFCHANNEL_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_upstream.h"
+
+enum pim_ifmembership {
+  PIM_IFMEMBERSHIP_NOINFO,
+  PIM_IFMEMBERSHIP_INCLUDE
+};
+
+enum pim_ifjoin_state {
+  PIM_IFJOIN_NOINFO,
+  PIM_IFJOIN_JOIN,
+  PIM_IFJOIN_PRUNE_PENDING
+};
+
+enum pim_ifassert_state {
+  PIM_IFASSERT_NOINFO,
+  PIM_IFASSERT_I_AM_WINNER,
+  PIM_IFASSERT_I_AM_LOSER
+};
+
+struct pim_assert_metric {
+  uint32_t       rpt_bit_flag;
+  uint32_t       metric_preference;
+  uint32_t       route_metric;
+  struct in_addr ip_address; /* neighbor router that sourced the Assert message */
+};
+
+/*
+  Flag to detect change in CouldAssert(S,G,I)
+*/
+#define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0)
+#define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT)
+#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT)
+#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT)
+/*
+  Flag to detect change in AssertTrackingDesired(S,G,I)
+*/
+#define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1)
+#define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED)
+
+/*
+  Per-interface (S,G) state
+*/
+struct pim_ifchannel {
+  struct in_addr            source_addr; /* (S,G) source key */
+  struct in_addr            group_addr;  /* (S,G) group key */
+  struct interface         *interface;   /* backpointer to interface */
+  uint32_t                  flags;
+
+  /* IGMPv3 determined interface has local members for (S,G) ? */
+  enum pim_ifmembership     local_ifmembership;
+
+  /* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */
+  enum pim_ifjoin_state     ifjoin_state;
+  struct thread            *t_ifjoin_expiry_timer;
+  struct thread            *t_ifjoin_prune_pending_timer;
+  int64_t                   ifjoin_creation; /* Record uptime of ifjoin state */
+
+  /* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */
+  enum pim_ifassert_state   ifassert_state;
+  struct thread            *t_ifassert_timer;
+  struct in_addr            ifassert_winner;
+  struct pim_assert_metric  ifassert_winner_metric;
+  int64_t                   ifassert_creation; /* Record uptime of ifassert state */
+  struct pim_assert_metric  ifassert_my_metric;
+
+  /* Upstream (S,G) state */
+  struct pim_upstream      *upstream;
+};
+
+void pim_ifchannel_free(struct pim_ifchannel *ch);
+void pim_ifchannel_delete(struct pim_ifchannel *ch);
+void pim_ifchannel_membership_clear(struct interface *ifp);
+void pim_ifchannel_delete_on_noinfo(struct interface *ifp);
+struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
+                                        struct in_addr source_addr,
+                                        struct in_addr group_addr);
+struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr);
+void pim_ifchannel_join_add(struct interface *ifp,
+                           struct in_addr neigh_addr,
+                           struct in_addr upstream,
+                           struct in_addr source_addr,
+                           struct in_addr group_addr,
+                           uint8_t source_flags,
+                           uint16_t holdtime);
+void pim_ifchannel_prune(struct interface *ifp,
+                        struct in_addr upstream,
+                        struct in_addr source_addr,
+                        struct in_addr group_addr,
+                        uint8_t source_flags,
+                        uint16_t holdtime);
+void pim_ifchannel_local_membership_add(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr);
+void pim_ifchannel_local_membership_del(struct interface *ifp,
+                                       struct in_addr source_addr,
+                                       struct in_addr group_addr);
+
+void pim_ifchannel_ifjoin_switch(const char *caller,
+                                struct pim_ifchannel *ch,
+                                enum pim_ifjoin_state new_state);
+const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state);
+const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state);
+
+int pim_ifchannel_isin_oiflist(struct pim_ifchannel *ch);
+
+void reset_ifassert_state(struct pim_ifchannel *ch);
+
+void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch);
+void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch);
+void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch);
+
+#endif /* PIM_IFCHANNEL_H */
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
new file mode 100644 (file)
index 0000000..ad3c7b5
--- /dev/null
@@ -0,0 +1,1435 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_igmp.h"
+#include "pim_igmpv3.h"
+#include "pim_iface.h"
+#include "pim_sock.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_time.h"
+#include "pim_zebra.h"
+
+#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE        (1)
+#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE        (2)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
+#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES      (5)
+#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES      (6)
+
+static void group_timer_off(struct igmp_group *group);
+
+static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
+                                            struct in_addr group_addr);
+
+static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, 
+                          uint32_t pim_options)
+{
+  int fd;
+  int join = 0;
+  struct in_addr group;
+
+  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */);
+  if (fd < 0)
+    return -1;
+
+  if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
+    if (inet_aton(PIM_ALL_ROUTERS, &group)) {
+      if (!pim_socket_join(fd, group, ifaddr, ifindex))
+       ++join;
+    }
+    else {
+      zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+    }
+  }
+
+  /*
+    IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
+    IGMP routers must receive general queries for querier election.
+  */
+  if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
+    if (!pim_socket_join(fd, group, ifaddr, ifindex))
+      ++join;
+  }
+  else {
+    zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+             PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
+  }
+
+  if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
+    if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
+      ++join;
+    }
+  }
+  else {
+      zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
+               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
+  }    
+
+  if (!join) {
+    zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
+            fd, inet_ntoa(ifaddr));
+    close(fd);
+    fd = -1;
+  }
+
+  return fd;
+}
+
+#undef IGMP_SOCK_DUMP
+
+#ifdef IGMP_SOCK_DUMP
+static void igmp_sock_dump(array_t *igmp_sock_array)
+{
+  int size = array_size(igmp_sock_array);
+  int i;
+  
+  for (i = 0; i < size; ++i) {
+    
+    struct igmp_sock *igmp = array_get(igmp_sock_array, i);
+    
+    zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
+              __FILE__, __PRETTY_FUNCTION__,
+              i, size,
+              inet_ntoa(igmp->ifaddr),
+              igmp->fd);
+  }
+}
+#endif
+
+struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
+                                             struct in_addr ifaddr)
+{
+  struct listnode  *sock_node;
+  struct igmp_sock *igmp;
+
+#ifdef IGMP_SOCK_DUMP
+  igmp_sock_dump(igmp_sock_list);
+#endif
+
+  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+    if (ifaddr.s_addr == igmp->ifaddr.s_addr)
+      return igmp;
+
+  return 0;
+}
+
+struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
+                                        int fd)
+{
+  struct listnode  *sock_node;
+  struct igmp_sock *igmp;
+
+  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
+    if (fd == igmp->fd)
+      return igmp;
+
+  return 0;
+}
+
+static int pim_igmp_other_querier_expire(struct thread *t)
+{
+  struct igmp_sock *igmp;
+
+  zassert(t);
+  igmp = THREAD_ARG(t);
+  zassert(igmp);
+
+  zassert(igmp->t_other_querier_timer);
+  zassert(!igmp->t_igmp_query_timer);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char ifaddr_str[100];
+    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+    zlog_debug("%s: Querier %s resuming",
+              __PRETTY_FUNCTION__,
+              ifaddr_str);
+  }
+
+  igmp->t_other_querier_timer = 0;
+
+  /*
+    We are the current querier, then
+    re-start sending general queries.
+  */
+  pim_igmp_general_query_on(igmp);
+
+  return 0;
+}
+
+void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
+{
+  long other_querier_present_interval_msec;
+  struct pim_interface *pim_ifp;
+
+  zassert(igmp);
+  zassert(igmp->interface);
+  zassert(igmp->interface->info);
+
+  pim_ifp = igmp->interface->info;
+
+  if (igmp->t_other_querier_timer) {
+    /*
+      There is other querier present already,
+      then reset the other-querier-present timer.
+    */
+
+    if (PIM_DEBUG_IGMP_TRACE) {
+      char ifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
+                ifaddr_str);
+    }
+
+    THREAD_OFF(igmp->t_other_querier_timer);
+    zassert(!igmp->t_other_querier_timer);
+  }
+  else {
+    /*
+      We are the current querier, then stop sending general queries:
+      igmp->t_igmp_query_timer = 0;
+    */
+    pim_igmp_general_query_off(igmp);
+  }
+
+  /*
+    Since this socket is starting the other-querier-present timer,
+    there should not be periodic query timer for this socket.
+   */
+  zassert(!igmp->t_igmp_query_timer);
+
+  /*
+    RFC 3376: 8.5. Other Querier Present Interval
+
+    The Other Querier Present Interval is the length of time that must
+    pass before a multicast router decides that there is no longer
+    another multicast router which should be the querier.  This value
+    MUST be ((the Robustness Variable) times (the Query Interval)) plus
+    (one half of one Query Response Interval).
+
+    other_querier_present_interval_msec = \
+      igmp->querier_robustness_variable * \
+      1000 * igmp->querier_query_interval + \
+      100 * (pim_ifp->query_max_response_time_dsec >> 1);
+  */
+  other_querier_present_interval_msec =
+    PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
+                      igmp->querier_query_interval,
+                      pim_ifp->igmp_query_max_response_time_dsec);
+  
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char ifaddr_str[100];
+    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+    zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
+              ifaddr_str,
+              other_querier_present_interval_msec / 1000,
+              other_querier_present_interval_msec % 1000);
+  }
+  
+  THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
+                      pim_igmp_other_querier_expire,
+                      igmp, other_querier_present_interval_msec);
+}
+
+void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
+{
+  zassert(igmp);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    if (igmp->t_other_querier_timer) {
+      char ifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
+                ifaddr_str, igmp->fd, igmp->interface->name);
+    }
+  }
+  THREAD_OFF(igmp->t_other_querier_timer);
+  zassert(!igmp->t_other_querier_timer);
+}
+
+static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
+                          int max_resp_code,
+                          struct in_addr from, const char *from_str,
+                          char *igmp_msg, int igmp_msg_len)
+{
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  uint8_t               resv_s_qrv = 0;
+  uint8_t               s_flag = 0;
+  uint8_t               qrv = 0;
+  struct in_addr        group_addr;
+  uint16_t              recv_checksum;
+  uint16_t              checksum;
+  int                   i;
+
+  //group_addr = *(struct in_addr *)(igmp_msg + 4);
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+  ifp = igmp->interface;
+  pim_ifp = ifp->info;
+
+  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+
+  /* for computing checksum */
+  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+
+  checksum = in_cksum(igmp_msg, igmp_msg_len);
+  if (checksum != recv_checksum) {
+    zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
+             query_version, from_str, ifp->name, recv_checksum, checksum);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
+              query_version, from_str, ifp->name,
+              igmp_msg_len, checksum, group_str);
+  }
+
+  /*
+    RFC 3376: 6.6.2. Querier Election
+
+    When a router receives a query with a lower IP address, it sets
+    the Other-Querier-Present timer to Other Querier Present Interval
+    and ceases to send queries on the network if it was the previously
+    elected querier.
+   */
+  if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
+    
+    if (PIM_DEBUG_IGMP_TRACE) {
+      char ifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
+                ifp->name,
+                ifaddr_str, ntohl(igmp->ifaddr.s_addr),
+                from_str, ntohl(from.s_addr));
+    }
+
+    pim_igmp_other_querier_timer_on(igmp);
+  }
+
+  if (query_version == 3) {
+    /*
+      RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+
+      Routers adopt the QRV value from the most recently received Query
+      as their own [Robustness Variable] value, unless that most
+      recently received QRV was zero, in which case the receivers use
+      the default [Robustness Variable] value specified in section 8.1
+      or a statically configured value.
+    */
+    resv_s_qrv = igmp_msg[8];
+    qrv = 7 & resv_s_qrv;
+    igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+  }
+
+  /*
+    RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+
+    Multicast routers that are not the current querier adopt the QQI
+    value from the most recently received Query as their own [Query
+    Interval] value, unless that most recently received QQI was zero,
+    in which case the receiving routers use the default.
+  */
+  if (igmp->t_other_querier_timer && query_version == 3) {
+    /* other querier present */
+    uint8_t  qqic;
+    uint16_t qqi;
+    qqic = igmp_msg[9];
+    qqi = igmp_msg_decode8to16(qqic);
+    igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+    if (PIM_DEBUG_IGMP_TRACE) {
+      char ifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+                ifaddr_str,
+                qqi ? "recv-non-default" : "default",
+                igmp->querier_query_interval,
+                qqic,
+                from_str);
+    }
+  }
+
+  /*
+    RFC 3376: 6.6.1. Timer Updates
+
+    When a router sends or receives a query with a clear Suppress
+    Router-Side Processing flag, it must update its timers to reflect
+    the correct timeout values for the group or sources being queried.
+
+    General queries don't trigger timer update.
+  */
+  if (query_version == 3) {
+    s_flag = (1 << 3) & resv_s_qrv;
+  }
+  else {
+    /* Neither V1 nor V2 have this field. Pimd should really go into
+     * a compatibility mode here and run as V2 (or V1) but it doesn't
+     * so for now, lets just set the flag to suppress these timer updates.
+     */
+    s_flag = 1;
+  }
+  
+  if (!s_flag) {
+    /* s_flag is clear */
+
+    if (PIM_INADDR_IS_ANY(group_addr)) {
+      /* this is a general query */
+
+      /* log that general query should have the s_flag set */
+      zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
+               query_version, from_str, ifp->name);
+    }
+    else {
+      struct igmp_group *group;
+
+      /* this is a non-general query: perform timer updates */
+
+      group = find_group_by_addr(igmp, group_addr);
+      if (group) {
+       int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+       /*
+         RFC 3376: 6.6.1. Timer Updates
+         Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+         Query Q(G): Group Timer is lowered to LMQT
+       */
+       if (recv_num_sources < 1) {
+         /* Query Q(G): Group Timer is lowered to LMQT */
+
+         igmp_group_timer_lower_to_lmqt(group);
+       }
+       else {
+         /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+         /* Scan sources in query and lower their timers to LMQT */
+         struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+         for (i = 0; i < recv_num_sources; ++i) {
+           //struct in_addr src_addr = sources[i];
+           //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
+           struct in_addr src_addr;
+           struct igmp_source *src;
+            memcpy(&src_addr, sources + i, sizeof(struct in_addr));
+           src = igmp_find_source_by_addr(group, src_addr);
+           if (src) {
+             igmp_source_timer_lower_to_lmqt(src);
+           }
+         }
+       }
+
+      }
+      else {
+       char group_str[100];
+       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+       zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
+                 query_version, from_str, ifp->name, group_str);
+      }
+    }
+  } /* s_flag is clear: timer updates */
+  
+  return 0;
+}
+
+static int igmp_v3_report(struct igmp_sock *igmp,
+                         struct in_addr from, const char *from_str,
+                         char *igmp_msg, int igmp_msg_len)
+{
+  uint16_t          recv_checksum;
+  uint16_t          checksum;
+  int               num_groups;
+  uint8_t          *group_record;
+  uint8_t          *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+  struct interface *ifp = igmp->interface;
+  int               i;
+
+  if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+             from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+    return -1;
+  }
+
+  recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+
+  /* for computing checksum */
+  *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+
+  checksum = in_cksum(igmp_msg, igmp_msg_len);
+  if (checksum != recv_checksum) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+             from_str, ifp->name, recv_checksum, checksum);
+    return -1;
+  }
+
+  num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+  if (num_groups < 1) {
+    zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+             from_str, ifp->name);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+              from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+  }
+
+  group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+  /* Scan groups */
+  for (i = 0; i < num_groups; ++i) {
+    struct in_addr  rec_group;
+    uint8_t        *sources;
+    uint8_t        *src;
+    int             rec_type;
+    int             rec_auxdatalen;
+    int             rec_num_sources;
+    int             j;
+
+    if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+      zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+               from_str, ifp->name);
+      return -1;
+    }
+
+    rec_type        = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+    rec_auxdatalen  = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+    rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+    //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
+    memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
+
+    if (PIM_DEBUG_IGMP_PACKETS) {
+      zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+                from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+    }
+
+    /* Scan sources */
+    
+    sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+    for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+      if ((src + 4) > report_pastend) {
+       zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+                 from_str, ifp->name);
+       return -1;
+      }
+
+      if (PIM_DEBUG_IGMP_PACKETS) {
+       char src_str[200];
+
+       if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+         sprintf(src_str, "<source?>");
+       
+       zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+                  from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+      }
+    } /* for (sources) */
+
+    switch (rec_type) {
+    case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+      igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+      igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+      igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+      igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+      igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+      igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+      break;
+    default:
+      zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+               from_str, ifp->name, rec_type);
+    }
+
+    group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+
+  } /* for (group records) */
+
+  return 0;
+}
+
+static void on_trace(const char *label,
+                    struct interface *ifp, struct in_addr from)
+{
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char from_str[100];
+    pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+    zlog_debug("%s: from %s on %s",
+              label, from_str, ifp->name);
+  }
+}
+
+static int igmp_v2_report(struct igmp_sock *igmp,
+                         struct in_addr from, const char *from_str,
+                         char *igmp_msg, int igmp_msg_len)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+  struct in_addr group_addr;
+
+  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+    zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
+             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_warn("%s %s: FIXME WRITEME",
+             __FILE__, __PRETTY_FUNCTION__);
+  }
+
+  //group_addr = *(struct in_addr *)(igmp_msg + 4);
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return -1;
+  }
+
+  group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
+
+  return 0;
+}
+
+static int igmp_v2_leave(struct igmp_sock *igmp,
+                        struct in_addr from, const char *from_str,
+                        char *igmp_msg, int igmp_msg_len)
+{
+  struct interface *ifp = igmp->interface;
+
+  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+    zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
+             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_warn("%s %s: FIXME WRITEME",
+             __FILE__, __PRETTY_FUNCTION__);
+  }
+
+  return 0;
+}
+
+static int igmp_v1_report(struct igmp_sock *igmp,
+                         struct in_addr from, const char *from_str,
+                         char *igmp_msg, int igmp_msg_len)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+  struct in_addr group_addr;
+
+  on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+    zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
+             from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+    return -1;
+  }
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_warn("%s %s: FIXME WRITEME",
+             __FILE__, __PRETTY_FUNCTION__);
+  }
+
+  //group_addr = *(struct in_addr *)(igmp_msg + 4);
+  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return -1;
+  }
+
+  group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
+
+  return 0;
+}
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+{
+  struct ip *ip_hdr;
+  size_t ip_hlen; /* ip header length in bytes */
+  char *igmp_msg;
+  int igmp_msg_len;
+  int msg_type;
+  char from_str[100];
+  char to_str[100];
+    
+  if (len < sizeof(*ip_hdr)) {
+    zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
+             len, sizeof(*ip_hdr));
+    return -1;
+  }
+
+  ip_hdr = (struct ip *) buf;
+
+  pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
+  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
+
+  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
+              from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
+  }
+
+  if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
+    zlog_warn("IP packet protocol=%d is not IGMP=%d",
+             ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
+    return -1;
+  }
+
+  if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
+    zlog_warn("IP packet header size=%zu shorter than minimum=%d",
+             ip_hlen, PIM_IP_HEADER_MIN_LEN);
+    return -1;
+  }
+  if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
+    zlog_warn("IP packet header size=%zu greater than maximum=%d",
+             ip_hlen, PIM_IP_HEADER_MAX_LEN);
+    return -1;
+  }
+
+  igmp_msg = buf + ip_hlen;
+  msg_type = *igmp_msg;
+  igmp_msg_len = len - ip_hlen;
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
+              from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
+              igmp_msg_len);
+  }
+
+  if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
+    zlog_warn("IGMP message size=%d shorter than minimum=%d",
+             igmp_msg_len, PIM_IGMP_MIN_LEN);
+    return -1;
+  }
+
+  switch (msg_type) {
+  case PIM_IGMP_MEMBERSHIP_QUERY:
+    {
+      int max_resp_code = igmp_msg[1];
+      int query_version;
+
+      /*
+       RFC 3376: 7.1. Query Version Distinctions
+       IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
+       IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
+       IGMPv3 Query: length >= 12 octets
+      */
+
+      if (igmp_msg_len == 8) {
+       query_version = max_resp_code ? 2 : 1;
+      }
+      else if (igmp_msg_len >= 12) {
+       query_version = 3;
+      }
+      else {
+       zlog_warn("Unknown IGMP query version");
+       return -1;
+      }
+
+      return recv_igmp_query(igmp, query_version, max_resp_code,
+                            ip_hdr->ip_src, from_str,
+                            igmp_msg, igmp_msg_len);
+    }
+
+  case PIM_IGMP_V3_MEMBERSHIP_REPORT:
+    return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
+                         igmp_msg, igmp_msg_len);
+
+  case PIM_IGMP_V2_MEMBERSHIP_REPORT:
+    return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
+                         igmp_msg, igmp_msg_len);
+
+  case PIM_IGMP_V1_MEMBERSHIP_REPORT:
+    return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
+                         igmp_msg, igmp_msg_len);
+
+  case PIM_IGMP_V2_LEAVE_GROUP:
+    return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
+                        igmp_msg, igmp_msg_len);
+  }
+
+  zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
+
+  return -1;
+}
+
+static int pim_igmp_general_query(struct thread *t);
+
+void pim_igmp_general_query_on(struct igmp_sock *igmp)
+{
+  struct pim_interface *pim_ifp;
+  int startup_mode;
+  int query_interval;
+
+  zassert(igmp);
+  zassert(igmp->interface);
+
+  /*
+    Since this socket is starting as querier,
+    there should not exist a timer for other-querier-present.
+   */
+  zassert(!igmp->t_other_querier_timer);
+  pim_ifp = igmp->interface->info;
+  zassert(pim_ifp);
+
+  /*
+    RFC 3376: 8.6. Startup Query Interval
+
+    The Startup Query Interval is the interval between General Queries
+    sent by a Querier on startup.  Default: 1/4 the Query Interval.
+  */
+  startup_mode = igmp->startup_query_count > 0;
+  if (startup_mode) {
+    --igmp->startup_query_count;
+
+    /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
+    query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
+  }
+  else {
+    query_interval = igmp->querier_query_interval;
+  }
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char ifaddr_str[100];
+    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+    zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
+              ifaddr_str,
+              query_interval,
+              startup_mode ? "startup" : "non-startup",
+              igmp->fd);
+  }
+  igmp->t_igmp_query_timer = 0;
+  zassert(!igmp->t_igmp_query_timer);
+  THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
+                 pim_igmp_general_query,
+                 igmp, query_interval);
+}
+
+void pim_igmp_general_query_off(struct igmp_sock *igmp)
+{
+  zassert(igmp);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    if (igmp->t_igmp_query_timer) {
+      char ifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+      zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
+                ifaddr_str, igmp->fd, igmp->interface->name);
+    }
+  }
+  THREAD_OFF(igmp->t_igmp_query_timer);
+  zassert(!igmp->t_igmp_query_timer);
+}
+
+/* Issue IGMP general query */
+static int pim_igmp_general_query(struct thread *t)
+{
+  char   query_buf[PIM_IGMP_BUFSIZE_WRITE];
+  struct igmp_sock *igmp;
+  struct in_addr dst_addr;
+  struct in_addr group_addr;
+  struct pim_interface *pim_ifp;
+
+  zassert(t);
+
+  igmp = THREAD_ARG(t);
+
+  zassert(igmp);
+  zassert(igmp->interface);
+  zassert(igmp->interface->info);
+
+  pim_ifp = igmp->interface->info;
+
+  /*
+    RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+    In IGMPv3, General Queries are sent with an IP destination address
+    of 224.0.0.1, the all-systems multicast address.  Group-Specific
+    and Group-and-Source-Specific Queries are sent with an IP
+    destination address equal to the multicast address of interest.
+  */
+
+  dst_addr.s_addr   = htonl(INADDR_ALLHOSTS_GROUP);
+  group_addr.s_addr = PIM_NET_INADDR_ANY;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char querier_str[100];
+    char dst_str[100];
+    pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
+                  sizeof(querier_str));
+    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+    zlog_debug("Querier %s issuing IGMP general query to %s on %s",
+              querier_str, dst_str, igmp->interface->name);
+  }
+
+  pim_igmp_send_membership_query(0 /* igmp_group */,
+                                igmp->fd,
+                                igmp->interface->name,
+                                query_buf,
+                                sizeof(query_buf),
+                                0 /* num_sources */,
+                                dst_addr,
+                                group_addr,
+                                pim_ifp->igmp_query_max_response_time_dsec,
+                                1 /* s_flag: always set for general queries */,
+                                igmp->querier_robustness_variable,
+                                igmp->querier_query_interval);
+
+  pim_igmp_general_query_on(igmp);
+
+  return 0;
+}
+
+static int pim_igmp_read(struct thread *t);
+
+static void igmp_read_on(struct igmp_sock *igmp)
+{
+  zassert(igmp);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
+              igmp->fd);
+  }
+  igmp->t_igmp_read = 0;
+  zassert(!igmp->t_igmp_read);
+  THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
+}
+
+static int pim_igmp_read(struct thread *t)
+{
+  struct igmp_sock *igmp;
+  int fd;
+  struct sockaddr_in from;
+  struct sockaddr_in to;
+  socklen_t fromlen = sizeof(from);
+  socklen_t tolen = sizeof(to);
+  uint8_t buf[PIM_IGMP_BUFSIZE_READ];
+  int len;
+  ifindex_t ifindex = -1;
+  int result = -1; /* defaults to bad */
+
+  zassert(t);
+
+  igmp = THREAD_ARG(t);
+
+  zassert(igmp);
+
+  fd = THREAD_FD(t);
+
+  zassert(fd == igmp->fd);
+
+  len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+                             &from, &fromlen,
+                             &to, &tolen,
+                             &ifindex);
+  if (len < 0) {
+    zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    goto done;
+  }
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    char from_str[100];
+    char to_str[100];
+    
+    if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
+      sprintf(from_str, "<from?>");
+    if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
+      sprintf(to_str, "<to?>");
+    
+    zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
+              len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
+  }
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+  /* ifindex sanity check */
+  if (ifindex != (int) igmp->interface->ifindex) {
+    char from_str[100];
+    char to_str[100];
+    struct interface *ifp;
+
+    if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+      sprintf(from_str, "<from?>");
+    if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+      sprintf(to_str, "<to?>");
+
+    ifp = if_lookup_by_index(ifindex);
+    if (ifp) {
+      zassert(ifindex == (int) ifp->ifindex);
+    }
+
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+    zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+             from_str, to_str, fd,
+             ifindex, ifp ? ifp->name : "<if-notfound>",
+             igmp->interface->ifindex, igmp->interface->name);
+#endif
+    goto done;
+  }
+#endif
+
+  if (pim_igmp_packet(igmp, (char *)buf, len)) {
+    goto done;
+  }
+
+  result = 0; /* good */
+
+ done:
+  igmp_read_on(igmp);
+
+  return result;
+}
+
+static void sock_close(struct igmp_sock *igmp)
+{
+  pim_igmp_other_querier_timer_off(igmp);
+  pim_igmp_general_query_off(igmp);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    if (igmp->t_igmp_read) {
+      zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
+                inet_ntoa(igmp->ifaddr), igmp->fd,
+                igmp->interface->name);
+    }
+  }
+  THREAD_OFF(igmp->t_igmp_read);
+  zassert(!igmp->t_igmp_read);
+  
+  if (close(igmp->fd)) {
+    zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
+            inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
+            errno, safe_strerror(errno));
+  }
+  
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
+              inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
+  }
+}
+
+void igmp_startup_mode_on(struct igmp_sock *igmp)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = igmp->interface->info;
+
+  /*
+    RFC 3376: 8.7. Startup Query Count
+
+    The Startup Query Count is the number of Queries sent out on
+    startup, separated by the Startup Query Interval.  Default: the
+    Robustness Variable.
+  */
+  igmp->startup_query_count = igmp->querier_robustness_variable;
+
+  /*
+    Since we're (re)starting, reset QQI to default Query Interval
+  */
+  igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+}
+
+static void igmp_group_free(struct igmp_group *group)
+{
+  zassert(!group->t_group_query_retransmit_timer);
+  zassert(!group->t_group_timer);
+  zassert(group->group_source_list);
+  zassert(!listcount(group->group_source_list));
+
+  list_free(group->group_source_list);
+
+  XFREE(MTYPE_PIM_IGMP_GROUP, group);
+}
+
+static void igmp_group_delete(struct igmp_group *group)
+{
+  struct listnode *src_node;
+  struct listnode *src_nextnode;
+  struct igmp_source *src;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Deleting IGMP group %s from socket %d interface %s",
+              group_str,
+              group->group_igmp_sock->fd,
+              group->group_igmp_sock->interface->name);
+  }
+
+  for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
+    igmp_source_delete(src);
+  }
+
+  if (group->t_group_query_retransmit_timer) {
+    THREAD_OFF(group->t_group_query_retransmit_timer);
+    zassert(!group->t_group_query_retransmit_timer);
+  }
+
+  group_timer_off(group);
+  listnode_delete(group->group_igmp_sock->igmp_group_list, group);
+  igmp_group_free(group);
+}
+
+void igmp_group_delete_empty_include(struct igmp_group *group)
+{
+  zassert(!group->group_filtermode_isexcl);
+  zassert(!listcount(group->group_source_list));
+
+  igmp_group_delete(group);
+}
+
+void igmp_sock_free(struct igmp_sock *igmp)
+{
+  zassert(!igmp->t_igmp_read);
+  zassert(!igmp->t_igmp_query_timer);
+  zassert(!igmp->t_other_querier_timer);
+  zassert(igmp->igmp_group_list);
+  zassert(!listcount(igmp->igmp_group_list));
+
+  list_free(igmp->igmp_group_list);
+
+  XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
+}
+
+void igmp_sock_delete(struct igmp_sock *igmp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *grp_node;
+  struct listnode      *grp_nextnode;
+  struct igmp_group    *grp;
+
+  for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
+    igmp_group_delete(grp);
+  }
+
+  sock_close(igmp);
+
+  pim_ifp = igmp->interface->info;
+
+  listnode_delete(pim_ifp->igmp_socket_list, igmp);
+
+  igmp_sock_free(igmp);
+}
+
+static struct igmp_sock *igmp_sock_new(int fd,
+                                      struct in_addr ifaddr,
+                                      struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct igmp_sock *igmp;
+
+  pim_ifp = ifp->info;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
+              fd, inet_ntoa(ifaddr), ifp->name);
+  }
+
+  igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
+  if (!igmp) {
+    zlog_warn("%s %s: XMALLOC() failure",
+              __FILE__, __PRETTY_FUNCTION__);
+    return 0;
+  }
+
+  igmp->igmp_group_list = list_new();
+  if (!igmp->igmp_group_list) {
+    zlog_err("%s %s: failure: igmp_group_list = list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return 0;
+  }
+  igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
+
+  igmp->fd                          = fd;
+  igmp->interface                   = ifp;
+  igmp->ifaddr                      = ifaddr;
+  igmp->t_igmp_read                 = 0;
+  igmp->t_igmp_query_timer          = 0;
+  igmp->t_other_querier_timer       = 0; /* no other querier present */
+  igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
+  igmp->sock_creation               = pim_time_monotonic_sec();
+
+  /*
+    igmp_startup_mode_on() will reset QQI:
+
+    igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
+  */
+  igmp_startup_mode_on(igmp);
+
+  igmp_read_on(igmp);
+  pim_igmp_general_query_on(igmp);
+
+  return igmp;
+}
+
+struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
+                                   struct in_addr ifaddr,
+                                   struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct igmp_sock *igmp;
+  int fd;
+
+  pim_ifp = ifp->info;
+
+  fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
+  if (fd < 0) {
+    zlog_warn("Could not open IGMP socket for %s on %s",
+             inet_ntoa(ifaddr), ifp->name);
+    return 0;
+  }
+
+  igmp = igmp_sock_new(fd, ifaddr, ifp);
+  if (!igmp) {
+    zlog_err("%s %s: igmp_sock_new() failure",
+            __FILE__, __PRETTY_FUNCTION__);
+    close(fd);
+    return 0;
+  }
+
+  listnode_add(igmp_sock_list, igmp);
+
+#ifdef IGMP_SOCK_DUMP
+  igmp_sock_dump(igmp_sock_array);
+#endif
+
+  return igmp;
+}
+
+/*
+  RFC 3376: 6.5. Switching Router Filter-Modes
+
+  When a router's filter-mode for a group is EXCLUDE and the group
+  timer expires, the router filter-mode for the group transitions to
+  INCLUDE.
+
+  A router uses source records with running source timers as its state
+  for the switch to a filter-mode of INCLUDE.  If there are any source
+  records with source timers greater than zero (i.e., requested to be
+  forwarded), a router switches to filter-mode of INCLUDE using those
+  source records.  Source records whose timers are zero (from the
+  previous EXCLUDE mode) are deleted.
+ */
+static int igmp_group_timer(struct thread *t)
+{
+  struct igmp_group *group;
+
+  zassert(t);
+  group = THREAD_ARG(t);
+  zassert(group);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: Timer for group %s on interface %s",
+              __PRETTY_FUNCTION__,
+              group_str, group->group_igmp_sock->interface->name);
+  }
+
+  zassert(group->group_filtermode_isexcl);
+
+  group->t_group_timer = 0;
+  group->group_filtermode_isexcl = 0;
+
+  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+  igmp_anysource_forward_stop(group);
+
+  igmp_source_delete_expired(group->group_source_list);
+
+  zassert(!group->t_group_timer);
+  zassert(!group->group_filtermode_isexcl);
+
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+
+    If there are no more source records for the group, delete group
+    record.
+  */
+  if (listcount(group->group_source_list) < 1) {
+    igmp_group_delete_empty_include(group);
+  }
+
+  return 0;
+}
+
+static void group_timer_off(struct igmp_group *group)
+{
+  if (!group->t_group_timer)
+    return;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Cancelling TIMER event for group %s on %s",
+              group_str, group->group_igmp_sock->interface->name);
+  }
+    
+  THREAD_OFF(group->t_group_timer);
+  zassert(!group->t_group_timer);
+}
+
+void igmp_group_timer_on(struct igmp_group *group,
+                        long interval_msec, const char *ifname)
+{
+  group_timer_off(group);
+
+  if (PIM_DEBUG_IGMP_EVENTS) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
+              interval_msec / 1000,
+              interval_msec % 1000,
+              group_str, ifname);
+  }
+
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+
+    The group timer is only used when a group is in EXCLUDE mode and
+    it represents the time for the *filter-mode* of the group to
+    expire and switch to INCLUDE mode.
+  */
+  zassert(group->group_filtermode_isexcl);
+
+  THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
+                      igmp_group_timer,
+                      group, interval_msec);
+}
+
+static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
+                                            struct in_addr group_addr)
+{
+  struct igmp_group *group;
+  struct listnode   *node;
+
+  for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
+    if (group_addr.s_addr == group->group_addr.s_addr)
+      return group;
+
+  return 0;
+}
+
+struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
+                                         struct in_addr group_addr)
+{
+  struct igmp_group *group;
+
+  group = find_group_by_addr(igmp, group_addr);
+  if (group) {
+    return group;
+  }
+
+  /*
+    Non-existant group is created as INCLUDE {empty}:
+
+    RFC 3376 - 5.1. Action on Change of Interface State
+
+    If no interface state existed for that multicast address before
+    the change (i.e., the change consisted of creating a new
+    per-interface record), or if no state exists after the change
+    (i.e., the change consisted of deleting a per-interface record),
+    then the "non-existent" state is considered to have a filter mode
+    of INCLUDE and an empty source list.
+  */
+
+  group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
+  if (!group) {
+    zlog_warn("%s %s: XMALLOC() failure",
+             __FILE__, __PRETTY_FUNCTION__);
+    return 0; /* error, not found, could not create */
+  }
+
+  group->group_source_list = list_new();
+  if (!group->group_source_list) {
+    zlog_warn("%s %s: list_new() failure",
+             __FILE__, __PRETTY_FUNCTION__);
+    XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
+    return 0; /* error, not found, could not initialize */
+  }
+  group->group_source_list->del = (void (*)(void *)) igmp_source_free;
+
+  group->t_group_timer                         = NULL;
+  group->t_group_query_retransmit_timer        = NULL;
+  group->group_specific_query_retransmit_count = 0;
+  group->group_addr                            = group_addr;
+  group->group_igmp_sock                       = igmp;
+  group->last_igmp_v1_report_dsec              = -1;
+  group->last_igmp_v2_report_dsec              = -1;
+  group->group_creation                        = pim_time_monotonic_sec();
+
+  /* initialize new group as INCLUDE {empty} */
+  group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
+
+  listnode_add(igmp->igmp_group_list, group);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Creating new IGMP group %s on socket %d interface %s",
+              group_str, igmp->fd, igmp->interface->name);
+  }
+
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+
+    The group timer is only used when a group is in EXCLUDE mode and
+    it represents the time for the *filter-mode* of the group to
+    expire and switch to INCLUDE mode.
+  */
+  zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
+  zassert(!group->t_group_timer); /* group timer == 0 */
+
+  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+  igmp_anysource_forward_stop(group);
+
+  return group;
+}
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
new file mode 100644 (file)
index 0000000..ab39615
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMP_H
+#define PIM_IGMP_H
+
+#include <netinet/in.h>
+
+#include <zebra.h>
+#include "vty.h"
+#include "linklist.h"
+
+/*
+  The following sizes are likely to support
+  any message sent within local MTU.
+*/
+#define PIM_IGMP_BUFSIZE_READ         (20000)
+#define PIM_IGMP_BUFSIZE_WRITE        (20000)
+
+#define PIM_IGMP_MEMBERSHIP_QUERY     (0x11)
+#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
+#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
+#define PIM_IGMP_V2_LEAVE_GROUP       (0x17)
+#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
+
+#define IGMP_V3_REPORT_HEADER_SIZE    (8)
+#define IGMP_V3_GROUP_RECORD_MIN_SIZE (8)
+#define IGMP_V3_MSG_MIN_SIZE          (IGMP_V3_REPORT_HEADER_SIZE + \
+                                      IGMP_V3_GROUP_RECORD_MIN_SIZE)
+#define IGMP_V12_MSG_SIZE             (8)
+
+#define IGMP_V3_GROUP_RECORD_TYPE_OFFSET       (0)
+#define IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET (1)
+#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2)
+#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET      (4)
+#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET     (8)
+
+/* RFC 3376: 8.1. Robustness Variable - Default: 2 */
+#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE           (2)
+
+/* RFC 3376: 8.2. Query Interval - Default: 125 seconds */
+#define IGMP_GENERAL_QUERY_INTERVAL                (125)
+
+/* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */
+#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC          (100)
+
+/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */
+#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10)
+
+struct igmp_join {
+  struct in_addr group_addr;
+  struct in_addr source_addr;
+  int            sock_fd;
+  time_t         sock_creation;
+};
+
+struct igmp_sock {
+  int               fd;
+  struct interface *interface;
+  struct in_addr    ifaddr;
+  time_t            sock_creation;
+
+  struct thread    *t_igmp_read;                 /* read: IGMP sockets */
+  struct thread    *t_igmp_query_timer; /* timer: issue IGMP general queries */
+  struct thread    *t_other_querier_timer;   /* timer: other querier present */
+
+  int               querier_query_interval;      /* QQI */
+  int               querier_robustness_variable; /* QRV */
+  int               startup_query_count;
+
+  struct list      *igmp_group_list; /* list of struct igmp_group */
+};
+
+struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
+                                             struct in_addr ifaddr);
+struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
+                                        int fd);
+struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
+                                   struct in_addr ifaddr,
+                                   struct interface *ifp);
+void igmp_sock_delete(struct igmp_sock *igmp);
+void igmp_sock_free(struct igmp_sock *igmp);
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
+
+void pim_igmp_general_query_on(struct igmp_sock *igmp);
+void pim_igmp_general_query_off(struct igmp_sock *igmp);
+void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp);
+void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp);
+
+#define IGMP_SOURCE_MASK_FORWARDING        (1 << 0)
+#define IGMP_SOURCE_MASK_DELETE            (1 << 1)
+#define IGMP_SOURCE_MASK_SEND              (1 << 2)
+#define IGMP_SOURCE_TEST_FORWARDING(flags) ((flags) & IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_TEST_DELETE(flags)     ((flags) & IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_TEST_SEND(flags)       ((flags) & IGMP_SOURCE_MASK_SEND)
+#define IGMP_SOURCE_DO_FORWARDING(flags)   ((flags) |= IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_DO_DELETE(flags)       ((flags) |= IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_DO_SEND(flags)         ((flags) |= IGMP_SOURCE_MASK_SEND)
+#define IGMP_SOURCE_DONT_FORWARDING(flags) ((flags) &= ~IGMP_SOURCE_MASK_FORWARDING)
+#define IGMP_SOURCE_DONT_DELETE(flags)     ((flags) &= ~IGMP_SOURCE_MASK_DELETE)
+#define IGMP_SOURCE_DONT_SEND(flags)       ((flags) &= ~IGMP_SOURCE_MASK_SEND)
+
+struct igmp_source {
+  struct in_addr      source_addr;
+  struct thread      *t_source_timer;
+  struct igmp_group  *source_group;    /* back pointer */
+  time_t              source_creation;
+  uint32_t            source_flags;
+  struct channel_oil *source_channel_oil;
+
+  /*
+    RFC 3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+  */
+  int                source_query_retransmit_count;
+};
+
+struct igmp_group {
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+
+    The group timer is only used when a group is in EXCLUDE mode and it
+    represents the time for the *filter-mode* of the group to expire and
+    switch to INCLUDE mode.
+  */
+  struct thread    *t_group_timer;
+
+  /* Shared between group-specific and
+     group-and-source-specific retransmissions */
+  struct thread    *t_group_query_retransmit_timer;
+
+  /* Counter exclusive for group-specific retransmissions
+     (not used by group-and-source-specific retransmissions,
+     since sources have their counters) */
+  int               group_specific_query_retransmit_count;
+
+  struct in_addr    group_addr;
+  int               group_filtermode_isexcl;  /* 0=INCLUDE, 1=EXCLUDE */
+  struct list      *group_source_list;        /* list of struct igmp_source */
+  time_t            group_creation;
+  struct igmp_sock *group_igmp_sock;          /* back pointer */
+  int64_t           last_igmp_v1_report_dsec;
+  int64_t           last_igmp_v2_report_dsec;
+};
+
+struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
+                                         struct in_addr group_addr);
+
+void igmp_group_delete_empty_include(struct igmp_group *group);
+
+void igmp_startup_mode_on(struct igmp_sock *igmp);
+
+void igmp_group_timer_on(struct igmp_group *group,
+                        long interval_msec, const char *ifname);
+
+struct igmp_source *
+source_new (struct igmp_group *group,
+           struct in_addr src_addr);
+#endif /* PIM_IGMP_H */
diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c
new file mode 100644 (file)
index 0000000..042818a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include "zebra.h"
+#include "pim_igmp_join.h"
+
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+
+#ifndef MCAST_JOIN_SOURCE_GROUP
+#define MCAST_JOIN_SOURCE_GROUP 46
+struct group_source_req
+{
+  uint32_t gsr_interface;
+  struct sockaddr_storage gsr_group;
+  struct sockaddr_storage gsr_source;
+};
+#endif
+
+int pim_igmp_join_source(int fd, ifindex_t ifindex,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr)
+{
+  struct group_source_req req;
+  struct sockaddr_in group;
+  struct sockaddr_in source;
+
+  memset(&group, 0, sizeof(group));
+  group.sin_family = AF_INET;
+  group.sin_addr = group_addr;
+  group.sin_port = htons(0);
+  memcpy(&req.gsr_group, &group, sizeof(struct sockaddr_in));
+
+  memset(&source, 0, sizeof(source));
+  source.sin_family = AF_INET;
+  source.sin_addr = source_addr;
+  source.sin_port = htons(0);
+  memcpy(&req.gsr_source, &source, sizeof(struct sockaddr_in));
+
+  req.gsr_interface = ifindex;
+
+  return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP,
+                   &req, sizeof(req));
+
+  return 0;
+}
diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h
new file mode 100644 (file)
index 0000000..67779ff
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMP_JOIN_H
+#define PIM_IGMP_JOIN_H
+
+#include <netinet/in.h>
+#include "if.h"
+
+int pim_igmp_join_source(int fd, ifindex_t ifindex,
+                        struct in_addr group_addr,
+                        struct in_addr source_addr);
+
+#endif /* PIM_IGMP_JOIN_H */
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
new file mode 100644 (file)
index 0000000..180de9d
--- /dev/null
@@ -0,0 +1,1716 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "log.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_iface.h"
+#include "pim_igmp.h"
+#include "pim_igmpv3.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_time.h"
+#include "pim_zebra.h"
+#include "pim_oil.h"
+
+static void group_retransmit_timer_on(struct igmp_group *group);
+static long igmp_group_timer_remain_msec(struct igmp_group *group);
+static long igmp_source_timer_remain_msec(struct igmp_source *source);
+static void group_query_send(struct igmp_group *group);
+static void source_query_send_by_flag(struct igmp_group *group,
+                                     int num_sources_tosend);
+
+static void on_trace(const char *label,
+                    struct interface *ifp, struct in_addr from,
+                    struct in_addr group_addr,
+                    int num_sources, struct in_addr *sources)
+{
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char from_str[100];
+    char group_str[100];
+
+    pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+
+    zlog_debug("%s: from %s on %s: group=%s sources=%d",
+              label, from_str, ifp->name, group_str, num_sources);
+  }
+}
+
+int igmp_group_compat_mode(const struct igmp_sock *igmp,
+                          const struct igmp_group *group)
+{
+  struct pim_interface *pim_ifp;
+  int64_t               now_dsec;
+  long                  older_host_present_interval_dsec;
+
+  zassert(igmp);
+  zassert(igmp->interface);
+  zassert(igmp->interface->info);
+
+  pim_ifp = igmp->interface->info;
+
+  /*
+    RFC 3376: 8.13. Older Host Present Interval
+
+    This value MUST be ((the Robustness Variable) times (the Query
+    Interval)) plus (one Query Response Interval).
+
+    older_host_present_interval_dsec = \
+      igmp->querier_robustness_variable * \
+      10 * igmp->querier_query_interval + \
+      pim_ifp->query_max_response_time_dsec;
+  */
+  older_host_present_interval_dsec =
+    PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
+                      igmp->querier_query_interval,
+                      pim_ifp->igmp_query_max_response_time_dsec);
+
+  now_dsec = pim_time_monotonic_dsec();
+  if (now_dsec < 1) {
+    /* broken timer logged by pim_time_monotonic_dsec() */
+    return 3;
+  }
+
+  if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
+    return 1; /* IGMPv1 */
+
+  if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
+    return 2; /* IGMPv2 */
+
+  return 3; /* IGMPv3 */
+}
+
+void igmp_group_reset_gmi(struct igmp_group *group)
+{
+  long group_membership_interval_msec;
+  struct pim_interface *pim_ifp;
+  struct igmp_sock *igmp;
+  struct interface *ifp;
+
+  igmp = group->group_igmp_sock;
+  ifp = igmp->interface;
+  pim_ifp = ifp->info;
+
+  /*
+    RFC 3376: 8.4. Group Membership Interval
+
+    The Group Membership Interval is the amount of time that must pass
+    before a multicast router decides there are no more members of a
+    group or a particular source on a network.
+
+    This value MUST be ((the Robustness Variable) times (the Query
+    Interval)) plus (one Query Response Interval).
+
+    group_membership_interval_msec = querier_robustness_variable *
+                                     (1000 * querier_query_interval) +
+                                     100 * query_response_interval_dsec;
+  */
+  group_membership_interval_msec =
+    PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+                     igmp->querier_query_interval,
+                     pim_ifp->igmp_query_max_response_time_dsec);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
+              group_str,
+              group_membership_interval_msec / 1000,
+              group_membership_interval_msec % 1000,
+              ifp->name);
+  }
+
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+
+    The group timer is only used when a group is in EXCLUDE mode and
+    it represents the time for the *filter-mode* of the group to
+    expire and switch to INCLUDE mode.
+  */
+  zassert(group->group_filtermode_isexcl);
+
+  igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
+}
+
+static int igmp_source_timer(struct thread *t)
+{
+  struct igmp_source *source;
+  struct igmp_group *group;
+
+  zassert(t);
+  source = THREAD_ARG(t);
+  zassert(source);
+
+  group = source->source_group;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_debug("%s: Source timer expired for group %s source %s on %s",
+              __PRETTY_FUNCTION__,
+              group_str, source_str,
+              group->group_igmp_sock->interface->name);
+  }
+
+  zassert(source->t_source_timer);
+  source->t_source_timer = 0;
+
+  /*
+    RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
+
+    Group
+    Filter-Mode    Source Timer Value    Action
+    -----------    ------------------    ------
+    INCLUDE        TIMER == 0            Suggest to stop forwarding
+                                         traffic from source and
+                                         remove source record.  If
+                                         there are no more source
+                                         records for the group, delete
+                                         group record.
+
+    EXCLUDE        TIMER == 0            Suggest to not forward
+                                         traffic from source
+                                         (DO NOT remove record)
+
+    Source timer switched from (T > 0) to (T == 0): disable forwarding.
+   */
+
+  zassert(!source->t_source_timer);
+
+  if (group->group_filtermode_isexcl) {
+    /* EXCLUDE mode */
+
+    igmp_source_forward_stop(source);
+  }
+  else {
+    /* INCLUDE mode */
+
+    /* igmp_source_delete() will stop forwarding source */
+    igmp_source_delete(source);
+
+    /*
+      If there are no more source records for the group, delete group
+      record.
+    */
+    if (!listcount(group->group_source_list)) {
+      igmp_group_delete_empty_include(group);
+    }
+  }
+
+  return 0;
+}
+
+static void source_timer_off(struct igmp_group *group,
+                            struct igmp_source *source)
+{
+  if (!source->t_source_timer)
+    return;
+  
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_debug("Cancelling TIMER event for group %s source %s on %s",
+              group_str, source_str,
+              group->group_igmp_sock->interface->name);
+  }
+
+  THREAD_OFF(source->t_source_timer);
+  zassert(!source->t_source_timer);
+}
+
+static void igmp_source_timer_on(struct igmp_group *group,
+                                struct igmp_source *source,
+                                long interval_msec)
+{
+  source_timer_off(group, source);
+
+  if (PIM_DEBUG_IGMP_EVENTS) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
+              interval_msec / 1000,
+              interval_msec % 1000,
+              group_str, source_str,
+              group->group_igmp_sock->interface->name);
+  }
+
+  THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
+                      igmp_source_timer,
+                      source, interval_msec);
+  zassert(source->t_source_timer);
+
+  /*
+    RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
+    
+    Source timer switched from (T == 0) to (T > 0): enable forwarding.
+  */
+  igmp_source_forward_start(source);
+}
+
+void igmp_source_reset_gmi(struct igmp_sock *igmp,
+                          struct igmp_group *group,
+                          struct igmp_source *source)
+{
+  long group_membership_interval_msec;
+  struct pim_interface *pim_ifp;
+  struct interface *ifp;
+
+  ifp = igmp->interface;
+  pim_ifp = ifp->info;
+
+  group_membership_interval_msec =
+    PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
+                     igmp->querier_query_interval,
+                     pim_ifp->igmp_query_max_response_time_dsec);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+
+    zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
+              source_str,
+              group_membership_interval_msec / 1000,
+              group_membership_interval_msec % 1000,
+              group_str,
+              ifp->name);
+  }
+
+  igmp_source_timer_on(group, source,
+                      group_membership_interval_msec);
+}
+
+static void source_mark_delete_flag(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct igmp_source *src;
+
+  for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+    IGMP_SOURCE_DO_DELETE(src->source_flags);
+  }
+}
+
+static void source_mark_send_flag(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct igmp_source *src;
+
+  for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+    IGMP_SOURCE_DO_SEND(src->source_flags);
+  }
+}
+
+static int source_mark_send_flag_by_timer(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct igmp_source *src;
+  int                 num_marked_sources = 0;
+
+  for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+    /* Is source timer running? */
+    if (src->t_source_timer) {
+      IGMP_SOURCE_DO_SEND(src->source_flags);
+      ++num_marked_sources;
+    }
+    else {
+      IGMP_SOURCE_DONT_SEND(src->source_flags);
+    }
+  }
+
+  return num_marked_sources;
+}
+
+static void source_clear_send_flag(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct igmp_source *src;
+
+  for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
+    IGMP_SOURCE_DONT_SEND(src->source_flags);
+  }
+}
+
+/*
+  Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
+*/
+static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
+{
+  zassert(group->group_filtermode_isexcl);
+
+  if (listcount(group->group_source_list) < 1) {
+    igmp_anysource_forward_start(group);
+  }
+}
+
+void igmp_source_free(struct igmp_source *source)
+{
+  /* make sure there is no source timer running */
+  zassert(!source->t_source_timer);
+
+  XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
+}
+
+static void source_channel_oil_detach(struct igmp_source *source)
+{
+  if (source->source_channel_oil) {
+    pim_channel_oil_del(source->source_channel_oil);
+    source->source_channel_oil = 0;
+  }
+}
+
+/*
+  igmp_source_delete:       stop fowarding, and delete the source
+  igmp_source_forward_stop: stop fowarding, but keep the source
+*/
+void igmp_source_delete(struct igmp_source *source)
+{
+  struct igmp_group *group;
+
+  group = source->source_group;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
+              source_str, group_str,
+              group->group_igmp_sock->fd,
+              group->group_igmp_sock->interface->name);
+  }
+
+  source_timer_off(group, source);
+  igmp_source_forward_stop(source);
+
+  /* sanity check that forwarding has been disabled */
+  if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
+             __PRETTY_FUNCTION__,
+             source_str, group_str,
+             group->group_igmp_sock->fd,
+             group->group_igmp_sock->interface->name);
+    /* warning only */
+  }
+
+  source_channel_oil_detach(source);
+
+  /*
+    notice that listnode_delete() can't be moved
+    into igmp_source_free() because the later is
+    called by list_delete_all_node()
+  */
+  listnode_delete(group->group_source_list, source);
+
+  igmp_source_free(source);
+
+  if (group->group_filtermode_isexcl) {
+    group_exclude_fwd_anysrc_ifempty(group);
+  }
+}
+
+static void source_delete_by_flag(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct listnode    *src_nextnode;
+  struct igmp_source *src;
+  
+  for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
+    if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
+      igmp_source_delete(src);
+}
+
+void igmp_source_delete_expired(struct list *source_list)
+{
+  struct listnode    *src_node;
+  struct listnode    *src_nextnode;
+  struct igmp_source *src;
+  
+  for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
+    if (!src->t_source_timer)
+      igmp_source_delete(src);
+}
+
+struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
+                                            struct in_addr src_addr)
+{
+  struct listnode    *src_node;
+  struct igmp_source *src;
+
+  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
+    if (src_addr.s_addr == src->source_addr.s_addr)
+      return src;
+
+  return 0;
+}
+
+struct igmp_source *
+source_new (struct igmp_group *group,
+           struct in_addr src_addr)
+{
+  struct igmp_source *src;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
+    zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
+              source_str, group_str,
+              group->group_igmp_sock->fd,
+              group->group_igmp_sock->interface->name);
+  }
+
+  src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
+  if (!src) {
+    zlog_warn("%s %s: XMALLOC() failure",
+             __FILE__, __PRETTY_FUNCTION__);
+    return 0; /* error, not found, could not create */
+  }
+  
+  src->t_source_timer                = NULL;
+  src->source_group                  = group; /* back pointer */
+  src->source_addr                   = src_addr;
+  src->source_creation               = pim_time_monotonic_sec();
+  src->source_flags                  = 0;
+  src->source_query_retransmit_count = 0;
+  src->source_channel_oil            = NULL;
+
+  listnode_add(group->group_source_list, src);
+
+  zassert(!src->t_source_timer); /* source timer == 0 */
+
+  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+  igmp_anysource_forward_stop(group);
+
+  return src;
+}
+
+static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
+                                             struct igmp_group *group,
+                                             struct in_addr src_addr)
+{
+  struct igmp_source *src;
+
+  src = igmp_find_source_by_addr(group, src_addr);
+  if (src) {
+    return src;
+  }
+
+  src = source_new(group, src_addr);
+  if (!src) {
+    return 0;
+  }
+
+  return src;
+}
+
+static void allow(struct igmp_sock *igmp, struct in_addr from,
+                 struct in_addr group_addr,
+                 int num_sources, struct in_addr *sources)
+{
+  struct igmp_group *group;
+  int    i;
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return;
+  }
+
+  /* scan received sources */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    source = add_source_by_addr(igmp, group, *src_addr);
+    if (!source) {
+      continue;
+    }
+
+    /*
+      RFC 3376: 6.4.1. Reception of Current-State Records
+
+      When receiving IS_IN reports for groups in EXCLUDE mode is
+      sources should be moved from set with (timers = 0) to set with
+      (timers > 0).
+
+      igmp_source_reset_gmi() below, resetting the source timers to
+      GMI, accomplishes this.
+    */
+    igmp_source_reset_gmi(igmp, group, source);
+
+  } /* scan received sources */
+}
+
+void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources)
+{
+  on_trace(__PRETTY_FUNCTION__,
+          igmp->interface, from, group_addr, num_sources, sources);
+
+  allow(igmp, from, group_addr, num_sources, sources);
+}
+
+static void isex_excl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  int     i;
+
+  /* EXCLUDE mode */
+  zassert(group->group_filtermode_isexcl);
+  
+  /* E.1: set deletion flag for known sources (X,Y) */
+  source_mark_delete_flag(group->group_source_list);
+
+  /* scan received sources (A) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    /* E.2: lookup reported source from (A) in (X,Y) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
+      IGMP_SOURCE_DONT_DELETE(source->source_flags);
+    }
+    else {
+      /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+      zassert(!source->t_source_timer); /* timer == 0 */
+      igmp_source_reset_gmi(group->group_igmp_sock, group, source);
+      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+    }
+
+  } /* scan received sources */
+
+  /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
+  source_delete_by_flag(group->group_source_list);
+}
+
+static void isex_incl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  int i;
+
+  /* INCLUDE mode */
+  zassert(!group->group_filtermode_isexcl);
+  
+  /* I.1: set deletion flag for known sources (A) */
+  source_mark_delete_flag(group->group_source_list);
+
+  /* scan received sources (B) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    /* I.2: lookup reported source (B) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* I.3: if found, clear deletion flag (A*B) */
+      IGMP_SOURCE_DONT_DELETE(source->source_flags);
+    }
+    else {
+      /* I.4: if not found, create source with timer=0 (B-A) */
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+      zassert(!source->t_source_timer); /* (B-A) timer=0 */
+    }
+
+  } /* scan received sources */
+
+  /* I.5: delete all sources marked with deletion flag (A-B) */
+  source_delete_by_flag(group->group_source_list);
+
+  group->group_filtermode_isexcl = 1; /* boolean=true */
+
+  zassert(group->group_filtermode_isexcl);
+
+  group_exclude_fwd_anysrc_ifempty(group);
+}
+
+void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+
+  on_trace(__PRETTY_FUNCTION__,
+          ifp, from, group_addr, num_sources, sources);
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return;
+  }
+
+  if (group->group_filtermode_isexcl) {
+    /* EXCLUDE mode */
+    isex_excl(group, num_sources, sources);
+  }
+  else {
+    /* INCLUDE mode */
+    isex_incl(group, num_sources, sources);
+    zassert(group->group_filtermode_isexcl);
+  }
+
+  zassert(group->group_filtermode_isexcl);
+
+  igmp_group_reset_gmi(group);
+}
+
+static void toin_incl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  struct igmp_sock *igmp = group->group_igmp_sock;
+  int num_sources_tosend = listcount(group->group_source_list);
+  int i;
+
+  /* Set SEND flag for all known sources (A) */
+  source_mark_send_flag(group->group_source_list);
+
+  /* Scan received sources (B) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    /* Lookup reported source (B) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* If found, clear SEND flag (A*B) */
+      IGMP_SOURCE_DONT_SEND(source->source_flags);
+      --num_sources_tosend;
+    }
+    else {
+      /* If not found, create new source */
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+    }
+
+    /* (B)=GMI */
+    igmp_source_reset_gmi(igmp, group, source);
+  }
+
+  /* Send sources marked with SEND flag: Q(G,A-B) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+}
+
+static void toin_excl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  struct igmp_sock *igmp = group->group_igmp_sock;
+  int num_sources_tosend;
+  int i;
+
+  /* Set SEND flag for X (sources with timer > 0) */
+  num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
+
+  /* Scan received sources (A) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    /* Lookup reported source (A) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      if (source->t_source_timer) {
+       /* If found and timer running, clear SEND flag (X*A) */
+       IGMP_SOURCE_DONT_SEND(source->source_flags);
+       --num_sources_tosend;
+      }
+    }
+    else {
+      /* If not found, create new source */
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+    }
+
+    /* (A)=GMI */
+    igmp_source_reset_gmi(igmp, group, source);
+  }
+
+  /* Send sources marked with SEND flag: Q(G,X-A) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+
+  /* Send Q(G) */
+  group_query_send(group);
+}
+
+void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+
+  on_trace(__PRETTY_FUNCTION__,
+          ifp, from, group_addr, num_sources, sources);
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return;
+  }
+
+  if (group->group_filtermode_isexcl) {
+    /* EXCLUDE mode */
+    toin_excl(group, num_sources, sources);
+  }
+  else {
+    /* INCLUDE mode */
+    toin_incl(group, num_sources, sources);
+  }
+}
+
+static void toex_incl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  int num_sources_tosend = 0;
+  int i;
+
+  zassert(!group->group_filtermode_isexcl);
+
+  /* Set DELETE flag for all known sources (A) */
+  source_mark_delete_flag(group->group_source_list);
+
+  /* Clear off SEND flag from all known sources (A) */
+  source_clear_send_flag(group->group_source_list);
+
+  /* Scan received sources (B) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+
+    src_addr = sources + i;
+
+    /* Lookup reported source (B) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* If found, clear deletion flag: (A*B) */
+      IGMP_SOURCE_DONT_DELETE(source->source_flags);
+      /* and set SEND flag (A*B) */
+      IGMP_SOURCE_DO_SEND(source->source_flags);
+      ++num_sources_tosend;
+    }
+    else {
+      /* If source not found, create source with timer=0: (B-A)=0 */
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+      zassert(!source->t_source_timer); /* (B-A) timer=0 */
+    }
+
+  } /* Scan received sources (B) */
+
+  group->group_filtermode_isexcl = 1; /* boolean=true */
+
+  /* Delete all sources marked with DELETE flag (A-B) */
+  source_delete_by_flag(group->group_source_list);
+
+  /* Send sources marked with SEND flag: Q(G,A*B) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+
+  zassert(group->group_filtermode_isexcl);
+
+  group_exclude_fwd_anysrc_ifempty(group);
+}
+
+static void toex_excl(struct igmp_group *group,
+                     int num_sources, struct in_addr *sources)
+{
+  int num_sources_tosend = 0;
+  int i;
+
+  /* set DELETE flag for all known sources (X,Y) */
+  source_mark_delete_flag(group->group_source_list);
+
+  /* clear off SEND flag from all known sources (X,Y) */
+  source_clear_send_flag(group->group_source_list);
+
+  /* scan received sources (A) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+    
+    src_addr = sources + i;
+    
+    /* lookup reported source (A) in known sources (X,Y) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* if found, clear off DELETE flag from reported source (A) */
+      IGMP_SOURCE_DONT_DELETE(source->source_flags);
+    }
+    else {
+      /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
+      long group_timer_msec;
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+
+      zassert(!source->t_source_timer); /* timer == 0 */
+      group_timer_msec = igmp_group_timer_remain_msec(group);
+      igmp_source_timer_on(group, source, group_timer_msec);
+      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+
+      /* make sure source is created with DELETE flag unset */
+      zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
+    }
+
+    /* make sure reported source has DELETE flag unset */
+    zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
+
+    if (source->t_source_timer) {
+      /* if source timer>0 mark SEND flag: Q(G,A-Y) */
+      IGMP_SOURCE_DO_SEND(source->source_flags);
+      ++num_sources_tosend;
+    }
+
+  } /* scan received sources (A) */
+
+  /*
+    delete all sources marked with DELETE flag:
+    Delete (X-A)
+    Delete (Y-A)
+  */
+  source_delete_by_flag(group->group_source_list);
+  /* send sources marked with SEND flag: Q(G,A-Y) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+}
+
+void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+
+  on_trace(__PRETTY_FUNCTION__,
+          ifp, from, group_addr, num_sources, sources);
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return;
+  }
+
+  if (group->group_filtermode_isexcl) {
+    /* EXCLUDE mode */
+    toex_excl(group, num_sources, sources);
+  }
+  else {
+    /* INCLUDE mode */
+    toex_incl(group, num_sources, sources);
+    zassert(group->group_filtermode_isexcl);
+  }
+  zassert(group->group_filtermode_isexcl);
+
+  /* Group Timer=GMI */
+  igmp_group_reset_gmi(group);
+}
+
+void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
+                        struct in_addr group_addr,
+                        int num_sources, struct in_addr *sources)
+{
+  on_trace(__PRETTY_FUNCTION__,
+          igmp->interface, from, group_addr, num_sources, sources);
+
+  allow(igmp, from, group_addr, num_sources, sources);
+}
+
+/*
+  RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+
+  When transmitting a group specific query, if the group timer is
+  larger than LMQT, the "Suppress Router-Side Processing" bit is set
+  in the query message.
+*/
+static void group_retransmit_group(struct igmp_group *group)
+{
+  char                  query_buf[PIM_IGMP_BUFSIZE_WRITE];
+  struct igmp_sock     *igmp;
+  struct pim_interface *pim_ifp;
+  long                  lmqc;      /* Last Member Query Count */
+  long                  lmqi_msec; /* Last Member Query Interval */
+  long                  lmqt_msec; /* Last Member Query Time */
+  int                   s_flag;
+
+  igmp = group->group_igmp_sock;
+  pim_ifp = igmp->interface->info;
+
+  lmqc      = igmp->querier_robustness_variable;
+  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+  lmqt_msec = lmqc * lmqi_msec;
+
+  /*
+    RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+    
+    When transmitting a group specific query, if the group timer is
+    larger than LMQT, the "Suppress Router-Side Processing" bit is set
+    in the query message.
+  */
+  s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
+              group_str, igmp->interface->name, s_flag,
+              group->group_specific_query_retransmit_count);
+  }
+
+  /*
+    RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+    Group-Specific and Group-and-Source-Specific Queries are sent with
+    an IP destination address equal to the multicast address of
+    interest.
+  */
+
+  pim_igmp_send_membership_query(group,
+                                igmp->fd,
+                                igmp->interface->name,
+                                query_buf,
+                                sizeof(query_buf),
+                                0 /* num_sources_tosend */,
+                                group->group_addr /* dst_addr */,
+                                group->group_addr /* group_addr */,
+                                pim_ifp->igmp_specific_query_max_response_time_dsec,
+                                s_flag,
+                                igmp->querier_robustness_variable,
+                                igmp->querier_query_interval);
+}
+
+/*
+  RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+
+  When building a group and source specific query for a group G, two
+  separate query messages are sent for the group.  The first one has
+  the "Suppress Router-Side Processing" bit set and contains all the
+  sources with retransmission state and timers greater than LMQT.  The
+  second has the "Suppress Router-Side Processing" bit clear and
+  contains all the sources with retransmission state and timers lower
+  or equal to LMQT.  If either of the two calculated messages does not
+  contain any sources, then its transmission is suppressed.
+ */
+static int group_retransmit_sources(struct igmp_group *group,
+                                   int send_with_sflag_set)
+{
+  struct igmp_sock     *igmp;
+  struct pim_interface *pim_ifp;
+  long                  lmqc;      /* Last Member Query Count */
+  long                  lmqi_msec; /* Last Member Query Interval */
+  long                  lmqt_msec; /* Last Member Query Time */
+  char                  query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
+  char                  query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
+  int                   query_buf1_max_sources;
+  int                   query_buf2_max_sources;
+  struct in_addr       *source_addr1;
+  struct in_addr       *source_addr2;
+  int                   num_sources_tosend1;
+  int                   num_sources_tosend2;
+  struct listnode      *src_node;
+  struct igmp_source   *src;
+  int                   num_retransmit_sources_left = 0;
+  
+  query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
+  query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
+  
+  source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
+  source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
+
+  igmp = group->group_igmp_sock;
+  pim_ifp = igmp->interface->info;
+
+  lmqc      = igmp->querier_robustness_variable;
+  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+  lmqt_msec = lmqc * lmqi_msec;
+
+  /* Scan all group sources */
+  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
+
+    /* Source has retransmission state? */
+    if (src->source_query_retransmit_count < 1)
+      continue;
+
+    if (--src->source_query_retransmit_count > 0) {
+      ++num_retransmit_sources_left;
+    }
+
+    /* Copy source address into appropriate query buffer */
+    if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
+      *source_addr1 = src->source_addr;
+      ++source_addr1;
+    }
+    else {
+      *source_addr2 = src->source_addr;
+      ++source_addr2;
+    }
+
+  }
+  
+  num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
+  num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
+              group_str, igmp->interface->name,
+              num_sources_tosend1,
+              num_sources_tosend2,
+              send_with_sflag_set,
+              num_retransmit_sources_left);
+  }
+
+  if (num_sources_tosend1 > 0) {
+    /*
+      Send group-and-source-specific query with s_flag set and all
+      sources with timers greater than LMQT.
+    */
+
+    if (send_with_sflag_set) {
+
+      query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
+      if (num_sources_tosend1 > query_buf1_max_sources) {
+       char group_str[100];
+       pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+       zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
+                 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
+                 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
+      }
+      else {
+       /*
+         RFC3376: 4.1.12. IP Destination Addresses for Queries
+      
+         Group-Specific and Group-and-Source-Specific Queries are sent with
+         an IP destination address equal to the multicast address of
+         interest.
+       */
+    
+       pim_igmp_send_membership_query(group,
+                                      igmp->fd,
+                                      igmp->interface->name,
+                                      query_buf1,
+                                      sizeof(query_buf1),
+                                      num_sources_tosend1,
+                                      group->group_addr,
+                                      group->group_addr,
+                                      pim_ifp->igmp_specific_query_max_response_time_dsec,
+                                      1 /* s_flag */,
+                                      igmp->querier_robustness_variable,
+                                      igmp->querier_query_interval);
+    
+      }
+
+    } /* send_with_sflag_set */
+
+  }
+
+  if (num_sources_tosend2 > 0) {
+    /*
+      Send group-and-source-specific query with s_flag clear and all
+      sources with timers lower or equal to LMQT.
+    */
+  
+    query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
+    if (num_sources_tosend2 > query_buf2_max_sources) {
+      char group_str[100];
+      pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
+               __PRETTY_FUNCTION__, group_str, igmp->interface->name,
+               num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
+    }
+    else {
+      /*
+       RFC3376: 4.1.12. IP Destination Addresses for Queries
+
+       Group-Specific and Group-and-Source-Specific Queries are sent with
+       an IP destination address equal to the multicast address of
+       interest.
+      */
+
+      pim_igmp_send_membership_query(group,
+                                    igmp->fd,
+                                    igmp->interface->name,
+                                    query_buf2,
+                                    sizeof(query_buf2),
+                                    num_sources_tosend2,
+                                    group->group_addr,
+                                    group->group_addr,
+                                    pim_ifp->igmp_specific_query_max_response_time_dsec,
+                                    0 /* s_flag */,
+                                    igmp->querier_robustness_variable,
+                                    igmp->querier_query_interval);
+
+    }
+  }
+
+  return num_retransmit_sources_left;
+}
+
+static int igmp_group_retransmit(struct thread *t)
+{
+  struct igmp_group *group;
+  int num_retransmit_sources_left;
+  int send_with_sflag_set; /* boolean */
+
+  zassert(t);
+  group = THREAD_ARG(t);
+  zassert(group);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("group_retransmit_timer: group %s on %s",
+              group_str, group->group_igmp_sock->interface->name);
+  }
+
+  /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
+  if (group->group_specific_query_retransmit_count > 0) {
+
+    /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
+    group_retransmit_group(group);
+    --group->group_specific_query_retransmit_count;
+
+    /*
+      RFC3376: 6.6.3.2
+      If a group specific query is scheduled to be transmitted at the
+      same time as a group and source specific query for the same group,
+      then transmission of the group and source specific message with the
+      "Suppress Router-Side Processing" bit set may be suppressed.
+    */
+    send_with_sflag_set = 0; /* boolean=false */
+  }
+  else {
+    send_with_sflag_set = 1; /* boolean=true */
+  }
+
+  /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
+  num_retransmit_sources_left = group_retransmit_sources(group,
+                                                        send_with_sflag_set);
+
+  group->t_group_query_retransmit_timer = 0;
+
+  /*
+    Keep group retransmit timer running if there is any retransmit
+    counter pending
+  */
+  if ((num_retransmit_sources_left > 0) ||
+      (group->group_specific_query_retransmit_count > 0)) {
+    group_retransmit_timer_on(group);
+  }
+
+  return 0;
+}
+
+/*
+  group_retransmit_timer_on:
+  if group retransmit timer isn't running, starts it;
+  otherwise, do nothing
+*/
+static void group_retransmit_timer_on(struct igmp_group *group)
+{
+  struct igmp_sock     *igmp;
+  struct pim_interface *pim_ifp;
+  long                  lmqi_msec; /* Last Member Query Interval */
+
+  /* if group retransmit timer is running, do nothing */
+  if (group->t_group_query_retransmit_timer) {
+    return;
+  }
+
+  igmp = group->group_igmp_sock;
+  pim_ifp = igmp->interface->info;
+
+  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
+              lmqi_msec / 1000,
+              lmqi_msec % 1000,
+              group_str,
+              igmp->interface->name);
+  }
+
+  THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
+                      igmp_group_retransmit,
+                      group, lmqi_msec);
+}
+
+static long igmp_group_timer_remain_msec(struct igmp_group *group)
+{
+  return pim_time_timer_remain_msec(group->t_group_timer);
+}
+
+static long igmp_source_timer_remain_msec(struct igmp_source *source)
+{
+  return pim_time_timer_remain_msec(source->t_source_timer);
+}
+
+/*
+  RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
+*/
+static void group_query_send(struct igmp_group *group)
+{
+  long              lmqc;    /* Last Member Query Count */
+
+  lmqc = group->group_igmp_sock->querier_robustness_variable;
+
+  /* lower group timer to lmqt */
+  igmp_group_timer_lower_to_lmqt(group);
+
+  /* reset retransmission counter */
+  group->group_specific_query_retransmit_count = lmqc;
+
+  /* immediately send group specific query (decrease retransmit counter by 1)*/
+  group_retransmit_group(group);
+
+  /* make sure group retransmit timer is running */
+  group_retransmit_timer_on(group);
+}
+
+/*
+  RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+*/
+static void source_query_send_by_flag(struct igmp_group *group,
+                                     int num_sources_tosend)
+{
+  struct igmp_sock     *igmp;
+  struct pim_interface *pim_ifp;
+  struct listnode      *src_node;
+  struct igmp_source   *src;
+  long                  lmqc;      /* Last Member Query Count */
+  long                  lmqi_msec; /* Last Member Query Interval */
+  long                  lmqt_msec; /* Last Member Query Time */
+
+  zassert(num_sources_tosend > 0);
+
+  igmp = group->group_igmp_sock;
+  pim_ifp = igmp->interface->info;
+
+  lmqc      = igmp->querier_robustness_variable;
+  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
+  lmqt_msec = lmqc * lmqi_msec;
+
+  /*
+    RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
+
+    (...) for each of the sources in X of group G, with source timer larger
+    than LMQT:
+    o Set number of retransmissions for each source to [Last Member
+    Query Count].
+    o Lower source timer to LMQT.
+  */
+  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
+    if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
+      /* source "src" in X of group G */
+      if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
+       src->source_query_retransmit_count = lmqc;
+       igmp_source_timer_lower_to_lmqt(src);
+      }
+    }
+  }
+
+  /* send group-and-source specific queries */
+  group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
+
+  /* make sure group retransmit timer is running */
+  group_retransmit_timer_on(group);
+}
+
+static void block_excl(struct igmp_group *group,
+                      int num_sources, struct in_addr *sources)
+{
+  int num_sources_tosend = 0;
+  int i;
+
+  /* 1. clear off SEND flag from all known sources (X,Y) */
+  source_clear_send_flag(group->group_source_list);
+
+  /* 2. scan received sources (A) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+    
+    src_addr = sources + i;
+    
+    /* lookup reported source (A) in known sources (X,Y) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (!source) {
+      /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
+      long group_timer_msec;
+      source = source_new(group, *src_addr);
+      if (!source) {
+       /* ugh, internal malloc failure, skip source */
+       continue;
+      }
+
+      zassert(!source->t_source_timer); /* timer == 0 */
+      group_timer_msec = igmp_group_timer_remain_msec(group);
+      igmp_source_timer_on(group, source, group_timer_msec);
+      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
+    }
+
+    if (source->t_source_timer) {
+      /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
+      IGMP_SOURCE_DO_SEND(source->source_flags);
+      ++num_sources_tosend;
+    }
+  }
+  /* 5. send sources marked with SEND flag: Q(G,A-Y) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+}
+
+static void block_incl(struct igmp_group *group,
+                      int num_sources, struct in_addr *sources)
+{
+  int num_sources_tosend = 0;
+  int i;
+
+  /* 1. clear off SEND flag from all known sources (B) */
+  source_clear_send_flag(group->group_source_list);
+
+  /* 2. scan received sources (A) */
+  for (i = 0; i < num_sources; ++i) {
+    struct igmp_source *source;
+    struct in_addr     *src_addr;
+    
+    src_addr = sources + i;
+    
+    /* lookup reported source (A) in known sources (B) */
+    source = igmp_find_source_by_addr(group, *src_addr);
+    if (source) {
+      /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
+      IGMP_SOURCE_DO_SEND(source->source_flags);
+      ++num_sources_tosend;
+    }
+  } 
+  /* 4. send sources marked with SEND flag: Q(G,A*B) */
+  if (num_sources_tosend > 0) {
+    source_query_send_by_flag(group, num_sources_tosend);
+  }
+}
+
+void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
+                        struct in_addr group_addr,
+                        int num_sources, struct in_addr *sources)
+{
+  struct interface *ifp = igmp->interface;
+  struct igmp_group *group;
+
+  on_trace(__PRETTY_FUNCTION__,
+          ifp, from, group_addr, num_sources, sources);
+
+  /* non-existant group is created as INCLUDE {empty} */
+  group = igmp_add_group_by_addr(igmp, group_addr);
+  if (!group) {
+    return;
+  }
+
+  if (group->group_filtermode_isexcl) {
+    /* EXCLUDE mode */
+    block_excl(group, num_sources, sources);
+  }
+  else {
+    /* INCLUDE mode */
+    block_incl(group, num_sources, sources);
+  }
+}
+
+void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
+{
+  struct igmp_sock     *igmp;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  char                 *ifname;
+  int   lmqi_dsec; /* Last Member Query Interval */
+  int   lmqc;      /* Last Member Query Count */
+  int   lmqt_msec; /* Last Member Query Time */
+
+  /*
+    RFC 3376: 6.2.2. Definition of Group Timers
+    
+    The group timer is only used when a group is in EXCLUDE mode and
+    it represents the time for the *filter-mode* of the group to
+    expire and switch to INCLUDE mode.
+  */
+  if (!group->group_filtermode_isexcl) {
+    return;
+  }
+
+  igmp    = group->group_igmp_sock;
+  ifp     = igmp->interface;
+  pim_ifp = ifp->info;
+  ifname  = ifp->name;
+
+  lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
+  lmqc      = igmp->querier_robustness_variable;
+  lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
+              __PRETTY_FUNCTION__,
+              group_str, ifname,
+              lmqc, lmqi_dsec, lmqt_msec);
+  }
+
+  zassert(group->group_filtermode_isexcl);
+
+  igmp_group_timer_on(group, lmqt_msec, ifname);
+}
+
+void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
+{
+  struct igmp_group    *group;
+  struct igmp_sock     *igmp;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  char                 *ifname;
+  int   lmqi_dsec; /* Last Member Query Interval */
+  int   lmqc;      /* Last Member Query Count */
+  int   lmqt_msec; /* Last Member Query Time */
+
+  group   = source->source_group;
+  igmp    = group->group_igmp_sock;
+  ifp     = igmp->interface;
+  pim_ifp = ifp->info;
+  ifname  = ifp->name;
+
+  lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
+  lmqc      = igmp->querier_robustness_variable;
+  lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
+              __PRETTY_FUNCTION__,
+              group_str, source_str, ifname,
+              lmqc, lmqi_dsec, lmqt_msec);
+  }
+
+  igmp_source_timer_on(group, source, lmqt_msec);
+}
+
+/*
+  Copy sources to message:
+    
+  struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
+  if (num_sources > 0) {
+  struct listnode    *node;
+  struct igmp_source *src;
+  int                 i = 0;
+
+  for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
+  sources[i++] = src->source_addr;
+  }
+  }
+*/
+void pim_igmp_send_membership_query(struct igmp_group *group,
+                                   int fd,
+                                   const char *ifname,
+                                   char *query_buf,
+                                   int query_buf_size,
+                                   int num_sources,
+                                   struct in_addr dst_addr,
+                                   struct in_addr group_addr,
+                                   int query_max_response_time_dsec,
+                                   uint8_t s_flag,
+                                   uint8_t querier_robustness_variable,
+                                   uint16_t querier_query_interval)
+{
+  ssize_t             msg_size;
+  uint8_t             max_resp_code;
+  uint8_t             qqic;
+  ssize_t             sent;
+  struct sockaddr_in  to;
+  socklen_t           tolen;
+  uint16_t            checksum;
+
+  zassert(num_sources >= 0);
+
+  msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
+  if (msg_size > query_buf_size) {
+    zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
+            __FILE__, __PRETTY_FUNCTION__,
+            msg_size, query_buf_size);
+    return;
+  }
+
+  s_flag = PIM_FORCE_BOOLEAN(s_flag);
+  zassert((s_flag == 0) || (s_flag == 1));
+
+  max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
+  qqic          = igmp_msg_encode16to8(querier_query_interval);
+
+  /*
+    RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+
+    If non-zero, the QRV field contains the [Robustness Variable]
+    value used by the querier, i.e., the sender of the Query.  If the
+    querier's [Robustness Variable] exceeds 7, the maximum value of
+    the QRV field, the QRV is set to zero.
+  */
+  if (querier_robustness_variable > 7) {
+    querier_robustness_variable = 0;
+  }
+
+  query_buf[0]                                         = PIM_IGMP_MEMBERSHIP_QUERY;
+  query_buf[1]                                         = max_resp_code;
+  *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
+  memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
+
+  query_buf[8]                                         = (s_flag << 3) | querier_robustness_variable;
+  query_buf[9]                                         = qqic;
+  *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
+
+  checksum = in_cksum(query_buf, msg_size);
+  *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    char dst_str[100];
+    char group_str[100];
+    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
+              __PRETTY_FUNCTION__,
+              dst_str, ifname, group_str, num_sources,
+              msg_size, s_flag, querier_robustness_variable,
+              querier_query_interval, qqic, checksum);
+  }
+
+  memset(&to, 0, sizeof(to));
+  to.sin_family = AF_INET;
+  to.sin_addr = dst_addr;
+  tolen = sizeof(to);
+
+  sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
+                (struct sockaddr *)&to, tolen);
+  if (sent != (ssize_t) msg_size) {
+    int e = errno;
+    char dst_str[100];
+    char group_str[100];
+    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    if (sent < 0) {
+      zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+               __PRETTY_FUNCTION__,
+               dst_str, ifname, group_str, msg_size,
+               e, safe_strerror(e));
+    }
+    else {
+      zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
+               __PRETTY_FUNCTION__,
+               dst_str, ifname, group_str,
+               msg_size, sent);
+    }
+    return;
+  }
+
+  /*
+    s_flag sanity test: s_flag must be set for general queries
+
+    RFC 3376: 6.6.1. Timer Updates
+
+    When a router sends or receives a query with a clear Suppress
+    Router-Side Processing flag, it must update its timers to reflect
+    the correct timeout values for the group or sources being queried.
+
+    General queries don't trigger timer update.
+  */
+  if (!s_flag) {
+    /* general query? */
+    if (PIM_INADDR_IS_ANY(group_addr)) {
+      char dst_str[100];
+      char group_str[100];
+      pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+      pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+      zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
+               __PRETTY_FUNCTION__,
+               dst_str, ifname, group_str, num_sources);
+    }
+  }
+
+}
diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h
new file mode 100644 (file)
index 0000000..bb7e926
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_IGMPV3_H
+#define PIM_IGMPV3_H
+
+#include <zebra.h>
+#include "if.h"
+
+#define IGMP_V3_CHECKSUM_OFFSET            (2)
+#define IGMP_V3_REPORT_NUMGROUPS_OFFSET    (6)
+#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8)
+#define IGMP_V3_NUMSOURCES_OFFSET          (10)
+#define IGMP_V3_SOURCES_OFFSET             (12)
+
+/* GMI: Group Membership Interval */
+#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec))
+
+/* OQPI: Other Querier Present Interval */
+#define PIM_IGMP_OQPI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * ((qri_dsec) >> 1))
+
+/* SQI: Startup Query Interval */
+#define PIM_IGMP_SQI(qi) (((qi) < 4) ? 1 : ((qi) >> 2))
+
+/* LMQT: Last Member Query Time */
+#define PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc) ((lmqc) * (100 * (lmqi_dsec)))
+
+/* OHPI: Older Host Present Interval */
+#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec))
+
+void igmp_group_reset_gmi(struct igmp_group *group);
+void igmp_source_reset_gmi(struct igmp_sock *igmp,
+                          struct igmp_group *group,
+                          struct igmp_source *source);
+
+void igmp_source_free(struct igmp_source *source);
+void igmp_source_delete(struct igmp_source *source);
+void igmp_source_delete_expired(struct list *source_list);
+
+int igmp_group_compat_mode(const struct igmp_sock *igmp,
+                          const struct igmp_group *group);
+
+void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources);
+void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources);
+void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources);
+void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
+                       struct in_addr group_addr,
+                       int num_sources, struct in_addr *sources);
+void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
+                        struct in_addr group_addr,
+                        int num_sources, struct in_addr *sources);
+void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
+                        struct in_addr group_addr,
+                        int num_sources, struct in_addr *sources);
+
+void igmp_group_timer_lower_to_lmqt(struct igmp_group *group);
+void igmp_source_timer_lower_to_lmqt(struct igmp_source *source);
+
+struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
+                                            struct in_addr src_addr);
+
+void pim_igmp_send_membership_query(struct igmp_group *group,
+                                   int fd,
+                                   const char *ifname,
+                                   char *query_buf,
+                                   int query_buf_size,
+                                   int num_sources,
+                                   struct in_addr dst_addr,
+                                   struct in_addr group_addr,
+                                   int query_max_response_time_dsec,
+                                   uint8_t s_flag,
+                                   uint8_t querier_robustness_variable,
+                                   uint16_t querier_query_interval);
+
+#endif /* PIM_IGMPV3_H */
diff --git a/pimd/pim_int.c b/pimd/pim_int.c
new file mode 100644 (file)
index 0000000..2080751
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pim_int.h"
+
+uint32_t pim_read_uint32_host(const uint8_t *buf)
+{
+  uint32_t val;
+  memcpy(&val, buf, sizeof(val));
+  /* val is in netorder */
+  val = ntohl(val);
+  /* val is in hostorder */
+  return val;
+}
+
+void pim_write_uint32(uint8_t *buf, uint32_t val_host)
+{
+  /* val_host is in host order */
+  val_host = htonl(val_host);
+  /* val_host is in netorder */
+  memcpy(buf, &val_host, sizeof(val_host));
+}
diff --git a/pimd/pim_int.h b/pimd/pim_int.h
new file mode 100644 (file)
index 0000000..d64b103
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_INT_H
+#define PIM_INT_H
+
+#include <stdint.h>
+
+uint32_t pim_read_uint32_host(const uint8_t *buf);
+void pim_write_uint32(uint8_t *buf, uint32_t val_host);
+
+#endif /* PIM_INT_H */
diff --git a/pimd/pim_join.c b/pimd/pim_join.c
new file mode 100644 (file)
index 0000000..9d8e001
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_join.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_ifchannel.h"
+
+static void on_trace(const char *label,
+                    struct interface *ifp, struct in_addr src)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+    zlog_debug("%s: from %s on %s",
+              label, src_str, ifp->name);
+  }
+}
+
+static void recv_join(struct interface *ifp,
+                     struct pim_neighbor *neigh,
+                     uint16_t holdtime,
+                     struct in_addr upstream,
+                     struct in_addr group,
+                     struct in_addr source,
+                     uint8_t source_flags)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char up_str[100];
+    char src_str[100];
+    char grp_str[100];
+    char neigh_str[100];
+    pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+    pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+    zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str,
+             source_flags & PIM_RPT_BIT_MASK,
+             source_flags & PIM_WILDCARD_BIT_MASK,
+             up_str, holdtime, neigh_str, ifp->name);
+  }
+  
+  /* Restart join expiry timer */
+  pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
+                        source, group, source_flags, holdtime);
+}
+
+static void recv_prune(struct interface *ifp,
+                      struct pim_neighbor *neigh,
+                      uint16_t holdtime,
+                      struct in_addr upstream,
+                      struct in_addr group,
+                      struct in_addr source,
+                      uint8_t source_flags)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    char up_str[100];
+    char src_str[100];
+    char grp_str[100];
+    char neigh_str[100];
+    pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+    pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+    zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str,
+             source_flags & PIM_RPT_BIT_MASK,
+             source_flags & PIM_WILDCARD_BIT_MASK,
+             up_str, holdtime, neigh_str, ifp->name);
+  }
+  
+  pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
+}
+
+int pim_joinprune_recv(struct interface *ifp,
+                      struct pim_neighbor *neigh,
+                      struct in_addr src_addr,
+                      uint8_t *tlv_buf, int tlv_buf_size)
+{
+  struct prefix   msg_upstream_addr;
+  uint8_t         msg_num_groups;
+  uint16_t        msg_holdtime;
+  int             addr_offset;
+  uint8_t        *buf;
+  uint8_t        *pastend;
+  int             remain;
+  int             group;
+
+  on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+  buf     = tlv_buf;
+  pastend = tlv_buf + tlv_buf_size;
+
+  /*
+    Parse ucast addr
+  */
+  addr_offset = pim_parse_addr_ucast(ifp->name, src_addr,
+                                    &msg_upstream_addr,
+                                    buf, pastend - buf);
+#if 0
+  zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d",
+            __PRETTY_FUNCTION__,
+            addr_offset);
+#endif
+  if (addr_offset < 1) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+             __PRETTY_FUNCTION__,
+             src_str, ifp->name);
+    return -1;
+  }
+  buf += addr_offset;
+
+  /*
+    Check upstream address family
+   */
+  if (msg_upstream_addr.family != AF_INET) {
+    if (PIM_DEBUG_PIM_TRACE) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               msg_upstream_addr.family, src_str, ifp->name);
+    }
+    return -2;
+  }
+
+  remain = pastend - buf;
+  if (remain < 4) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             remain, 4, src_str, ifp->name);
+    return -4;
+  }
+
+  ++buf; /* skip reserved byte */
+  msg_num_groups = *(const uint8_t *) buf;
+  ++buf;
+  msg_holdtime = ntohs(*(const uint16_t *) buf);
+  ++buf;
+  ++buf;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char upstream_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+                  upstream_str, sizeof(upstream_str));
+    zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             upstream_str, msg_num_groups, msg_holdtime,
+             src_str, ifp->name);
+  }
+
+  /* Scan groups */
+  for (group = 0; group < msg_num_groups; ++group) {
+    struct prefix msg_group_addr;
+    struct prefix msg_source_addr;
+    uint8_t       msg_source_flags;
+    uint16_t      msg_num_joined_sources;
+    uint16_t      msg_num_pruned_sources;
+    int           source;
+
+    addr_offset = pim_parse_addr_group(ifp->name, src_addr,
+                                      &msg_group_addr,
+                                      buf, pastend - buf);
+#if 0
+    zlog_warn("%s: pim_parse_addr_group addr_offset=%d",
+              __PRETTY_FUNCTION__,
+              addr_offset);
+#endif
+    if (addr_offset < 1) {
+      return -5;
+    }
+    buf += addr_offset;
+
+    remain = pastend - buf;
+    if (remain < 4) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               remain, 4, src_str, ifp->name);
+      return -6;
+    }
+
+    msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
+    buf += 2;
+    msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
+    buf += 2;
+
+    if (PIM_DEBUG_PIM_TRACE) {
+      char src_str[100];
+      char upstream_str[100];
+      char group_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+                    upstream_str, sizeof(upstream_str));
+      pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
+                    group_str, sizeof(group_str));
+      zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               upstream_str, group_str, msg_group_addr.prefixlen,
+               msg_num_joined_sources, msg_num_pruned_sources,
+               src_str, ifp->name);
+    }
+
+    /* Scan joined sources */
+    for (source = 0; source < msg_num_joined_sources; ++source) {
+      addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+                                         &msg_source_addr,
+                                         &msg_source_flags,
+                                         buf, pastend - buf);
+#if 0
+      zlog_warn("%s: pim_parse_addr_source addr_offset=%d",
+                __PRETTY_FUNCTION__,
+                addr_offset);
+#endif
+      if (addr_offset < 1) {
+       return -7;
+      }
+
+      buf += addr_offset;
+
+      recv_join(ifp, neigh, msg_holdtime,
+               msg_upstream_addr.u.prefix4,
+               msg_group_addr.u.prefix4,
+               msg_source_addr.u.prefix4,
+               msg_source_flags);
+    }
+
+    /* Scan pruned sources */
+    for (source = 0; source < msg_num_pruned_sources; ++source) {
+      addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+                                         &msg_source_addr,
+                                         &msg_source_flags,
+                                         buf, pastend - buf);
+      if (addr_offset < 1) {
+       return -8;
+      }
+
+      buf += addr_offset;
+
+      recv_prune(ifp, neigh, msg_holdtime,
+                msg_upstream_addr.u.prefix4,
+                msg_group_addr.u.prefix4,
+                msg_source_addr.u.prefix4,
+                msg_source_flags);
+    }
+
+  } /* scan groups */
+
+  return 0;
+}
+
+int pim_joinprune_send(struct interface *ifp,
+                      struct in_addr upstream_addr,
+                      struct in_addr source_addr,
+                      struct in_addr group_addr,
+                      int send_join)
+{
+  struct pim_interface *pim_ifp;
+  uint8_t pim_msg[1000];
+  const uint8_t *pastend = pim_msg + sizeof(pim_msg);
+  uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
+  int pim_msg_size;
+  int remain;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+
+  if (!pim_ifp) {
+    zlog_warn("%s: multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             ifp->name);
+    return -1;
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char source_str[100];
+    char group_str[100];
+    char dst_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+    zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
+              __PRETTY_FUNCTION__,
+              send_join ? "Join" : "Prune",
+              source_str, group_str, dst_str, ifp->name);
+  }
+
+  if (PIM_INADDR_IS_ANY(upstream_addr)) {
+    if (PIM_DEBUG_PIM_TRACE) {
+      char source_str[100];
+      char group_str[100];
+      char dst_str[100];
+      pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+      pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+      pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+      zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
+                __PRETTY_FUNCTION__,
+                send_join ? "Join" : "Prune",
+                source_str, group_str, dst_str, ifp->name);
+    }
+    return 0;
+  }
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+
+    Thus, if a router needs to send a Join/Prune or Assert message on
+    an interface on which it has not yet sent a Hello message with the
+    currently configured IP address, then it MUST immediately send the
+    relevant Hello message without waiting for the Hello Timer to
+    expire, followed by the Join/Prune or Assert message.
+  */
+  pim_hello_require(ifp);
+
+  /*
+    Build PIM message
+  */
+
+  remain = pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+                                               remain,
+                                               upstream_addr);
+  if (!pim_msg_curr) {
+    char dst_str[100];
+    pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+    zlog_warn("%s: failure encoding destination address %s: space left=%d",
+             __PRETTY_FUNCTION__, dst_str, remain);
+    return -3;
+  }
+
+  remain = pastend - pim_msg_curr;
+  if (remain < 4) {
+    zlog_warn("%s: group will not fit: space left=%d",
+           __PRETTY_FUNCTION__, remain);
+    return -4;
+  }
+
+  *pim_msg_curr = 0; /* reserved */
+  ++pim_msg_curr;
+  *pim_msg_curr = 1; /* number of groups */
+  ++pim_msg_curr;
+  *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+                                               remain,
+                                               group_addr);
+  if (!pim_msg_curr) {
+    char group_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: failure encoding group address %s: space left=%d",
+             __PRETTY_FUNCTION__, group_str, remain);
+    return -5;
+  }
+
+  remain = pastend - pim_msg_curr;
+  if (remain < 4) {
+    zlog_warn("%s: sources will not fit: space left=%d",
+             __PRETTY_FUNCTION__, remain);
+    return -6;
+  }
+
+  /* number of joined sources */
+  *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  /* number of pruned sources */
+  *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
+  ++pim_msg_curr;
+  ++pim_msg_curr;
+
+  remain = pastend - pim_msg_curr;
+  pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
+                                                remain,
+                                                source_addr);
+  if (!pim_msg_curr) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure encoding source address %s: space left=%d",
+             __PRETTY_FUNCTION__, source_str, remain);
+    return -7;
+  }
+
+  /* Add PIM header */
+
+  pim_msg_size = pim_msg_curr - pim_msg;
+
+  pim_msg_build_header(pim_msg, pim_msg_size,
+                      PIM_MSG_TYPE_JOIN_PRUNE);
+
+  if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  qpim_all_pim_routers_addr,
+                  pim_msg,
+                  pim_msg_size,
+                  ifp->name)) {
+    zlog_warn("%s: could not send PIM message on interface %s",
+             __PRETTY_FUNCTION__, ifp->name);
+    return -8;
+  }
+
+  return 0;
+}
diff --git a/pimd/pim_join.h b/pimd/pim_join.h
new file mode 100644 (file)
index 0000000..37ec0f4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_JOIN_H
+#define PIM_JOIN_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_neighbor.h"
+
+int pim_joinprune_recv(struct interface *ifp,
+                      struct pim_neighbor *neigh,
+                      struct in_addr src_addr,
+                      uint8_t *tlv_buf, int tlv_buf_size);
+
+int pim_joinprune_send(struct interface *ifp,
+                      struct in_addr upstream_addr,
+                      struct in_addr source_addr,
+                      struct in_addr group_addr,
+                      int send_join);
+
+#endif /* PIM_JOIN_H */
diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c
new file mode 100644 (file)
index 0000000..3f56532
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pim_macro.h"
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_ifchannel.h"
+
+#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
+
+/*
+  DownstreamJPState(S,G,I) is the per-interface state machine for
+  receiving (S,G) Join/Prune messages.
+
+  DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+*/
+static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
+{
+  return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
+}
+
+/*
+  The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
+  module or other local membership mechanism has determined that local
+  members on interface I desire to receive traffic sent specifically
+  by S to G.
+*/
+static int local_receiver_include(const struct pim_ifchannel *ch)
+{
+  /* local_receiver_include(S,G,I) ? */
+  return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
+}
+
+/*
+  RFC 4601: 4.1.6.  State Summarization Macros
+
+   The set "joins(S,G)" is the set of all interfaces on which the
+   router has received (S,G) Joins:
+
+   joins(S,G) =
+       { all interfaces I such that
+         DownstreamJPState(S,G,I) is either Join or Prune-Pending }
+
+  DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+*/
+int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
+{
+  return downstream_jpstate_isjoined(ch);
+}
+
+/*
+  RFC 4601: 4.6.5.  Assert State Macros
+
+   The set "lost_assert(S,G)" is the set of all interfaces on which the
+   router has received (S,G) joins but has lost an (S,G) assert.
+
+   lost_assert(S,G) =
+       { all interfaces I such that
+         lost_assert(S,G,I) == TRUE }
+
+     bool lost_assert(S,G,I) {
+       if ( RPF_interface(S) == I ) {
+          return FALSE
+       } else {
+          return ( AssertWinner(S,G,I) != NULL AND
+                   AssertWinner(S,G,I) != me  AND
+                   (AssertWinnerMetric(S,G,I) is better
+                      than spt_assert_metric(S,I) )
+       }
+     }
+
+  AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+  packet that won an Assert.
+*/
+int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_assert_metric spt_assert_metric;
+
+  ifp = ch->interface;
+  if (!ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): null interface",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str);
+    return 0; /* false */
+  }
+
+  /* RPF_interface(S) == I ? */
+  if (ch->upstream->rpf.source_nexthop.interface == ifp)
+    return 0; /* false */
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ifp->name);
+    return 0; /* false */
+  }
+
+  if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
+    return 0; /* false */
+
+  /* AssertWinner(S,G,I) == me ? */
+  if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+    return 0; /* false */
+
+  spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf,
+                                                 pim_ifp->primary_address);
+
+  return pim_assert_metric_better(&ch->ifassert_winner_metric,
+                                 &spt_assert_metric);
+}
+
+/*
+  RFC 4601: 4.1.6.  State Summarization Macros
+
+   pim_include(S,G) =
+       { all interfaces I such that:
+         ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
+           OR AssertWinner(S,G,I) == me )
+          AND  local_receiver_include(S,G,I) }
+
+   AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+   packet that won an Assert.
+*/
+int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
+{
+  struct pim_interface *pim_ifp = ch->interface->info;
+
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ch->interface->name);
+    return 0; /* false */
+  }
+
+  /* local_receiver_include(S,G,I) ? */
+  if (!local_receiver_include(ch))
+    return 0; /* false */
+    
+  /* OR AssertWinner(S,G,I) == me ? */
+  if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+    return 1; /* true */
+    
+  return (
+         /* I_am_DR( I ) ? */
+         PIM_IFP_I_am_DR(pim_ifp)
+         &&
+         /* lost_assert(S,G,I) == FALSE ? */
+         (!pim_macro_ch_lost_assert(ch))
+         );
+}
+
+int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
+{
+  if (pim_macro_chisin_joins(ch))
+    return 1; /* true */
+
+  return pim_macro_chisin_pim_include(ch);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  CouldAssert(S,G,I) =
+  SPTbit(S,G)==TRUE
+  AND (RPF_interface(S) != I)
+  AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+                 (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+                 (-) lost_assert(*,G)
+                 (+) joins(S,G) (+) pim_include(S,G) ) )
+
+  CouldAssert(S,G,I) is true for downstream interfaces that would be in
+  the inherited_olist(S,G) if (S,G) assert information was not taken
+  into account.
+
+  CouldAssert(S,G,I) may be affected by changes in the following:
+
+  pim_ifp->primary_address
+  pim_ifp->pim_dr_addr
+  ch->ifassert_winner_metric
+  ch->ifassert_winner
+  ch->local_ifmembership
+  ch->ifjoin_state
+  ch->upstream->rpf.source_nexthop.mrib_metric_preference
+  ch->upstream->rpf.source_nexthop.mrib_route_metric
+  ch->upstream->rpf.source_nexthop.interface
+*/
+int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
+{
+  struct interface *ifp;
+
+  /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
+
+  ifp = ch->interface;
+  if (!ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): null interface",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str);
+    return 0; /* false */
+  }
+
+  /* RPF_interface(S) != I ? */
+  if (ch->upstream->rpf.source_nexthop.interface == ifp)
+    return 0; /* false */
+
+  /* I in joins(S,G) (+) pim_include(S,G) ? */
+  return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+  RFC 4601: 4.6.3.  Assert Metrics
+
+   spt_assert_metric(S,I) gives the assert metric we use if we're
+   sending an assert based on active (S,G) forwarding state:
+
+    assert_metric
+    spt_assert_metric(S,I) {
+      return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
+    }
+*/
+struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
+                                                    struct in_addr ifaddr)
+{
+  struct pim_assert_metric metric;
+
+  metric.rpt_bit_flag      = 0;
+  metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
+  metric.route_metric      = rpf->source_nexthop.mrib_route_metric;
+  metric.ip_address        = ifaddr;
+
+  return metric;
+}
+
+/*
+  RFC 4601: 4.6.3.  Assert Metrics
+
+   An assert metric for (S,G) to include in (or compare against) an
+   Assert message sent on interface I should be computed using the
+   following pseudocode:
+
+  assert_metric  my_assert_metric(S,G,I) {
+    if( CouldAssert(S,G,I) == TRUE ) {
+      return spt_assert_metric(S,I)
+    } else if( CouldAssert(*,G,I) == TRUE ) {
+      return rpt_assert_metric(G,I)
+    } else {
+      return infinite_assert_metric()
+    }
+  }
+*/
+struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
+{
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ch->interface->info;
+
+  if (pim_ifp) {
+    if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+      return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
+    }
+  }
+
+  return qpim_infinite_assert_metric;
+}
+
+/*
+  RFC 4601 4.2.  Data Packet Forwarding Rules
+  RFC 4601 4.8.2.  PIM-SSM-Only Routers
+  
+  Macro:
+  inherited_olist(S,G) =
+    joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+*/
+static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
+{
+  if (pim_macro_ch_lost_assert(ch))
+    return 0; /* false */
+
+  return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+  RFC 4601 4.2.  Data Packet Forwarding Rules
+  RFC 4601 4.8.2.  PIM-SSM-Only Routers
+
+  Additionally, the Packet forwarding rules of Section 4.2 can be
+  simplified in a PIM-SSM-only router:
+  
+  iif is the incoming interface of the packet.
+  oiflist = NULL
+  if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
+    oiflist = inherited_olist(S,G)
+  } else if (iif is in inherited_olist(S,G)) {
+    send Assert(S,G) on iif
+  }
+  oiflist = oiflist (-) iif
+  forward packet on all interfaces in oiflist
+  
+  Macro:
+  inherited_olist(S,G) =
+    joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+  Note:
+  - The following test is performed as response to WRONGVIF kernel
+    upcall:
+    if (iif is in inherited_olist(S,G)) {
+      send Assert(S,G) on iif
+    }
+    See pim_mroute.c mroute_msg().
+*/
+int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
+{
+  if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
+    /* oiflist is NULL */
+    return 0; /* false */
+  }
+
+  /* oiflist = oiflist (-) iif */
+  if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
+    return 0; /* false */
+
+  return pim_macro_chisin_inherited_olist(ch);
+}
+
+/*
+  RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+  AssertTrackingDesired(S,G,I) =
+  (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+       (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+       (-) lost_assert(*,G)
+       (+) joins(S,G) ) )
+     OR (local_receiver_include(S,G,I) == TRUE
+        AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
+     OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE))
+     OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE)
+        AND (SPTbit(S,G) == FALSE))
+
+  AssertTrackingDesired(S,G,I) is true on any interface in which an
+  (S,G) assert might affect our behavior.
+*/
+int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
+{
+  struct pim_interface *pim_ifp;
+  struct interface *ifp;
+
+  ifp = ch->interface;
+  if (!ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): null interface",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str);
+    return 0; /* false */
+  }
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str, ch->interface->name);
+    return 0; /* false */
+  }
+
+  /* I in joins(S,G) ? */
+  if (pim_macro_chisin_joins(ch))
+    return 1; /* true */
+
+  /* local_receiver_include(S,G,I) ? */
+  if (local_receiver_include(ch)) {
+    /* I_am_DR(I) ? */
+    if (PIM_IFP_I_am_DR(pim_ifp))
+      return 1; /* true */
+
+    /* AssertWinner(S,G,I) == me ? */
+    if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
+      return 1; /* true */
+  }
+
+  /* RPF_interface(S) == I ? */
+  if (ch->upstream->rpf.source_nexthop.interface == ifp) {
+    /* JoinDesired(S,G) ? */
+    if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
+      return 1; /* true */
+  }
+
+  return 0; /* false */
+}
+
diff --git a/pimd/pim_macro.h b/pimd/pim_macro.h
new file mode 100644 (file)
index 0000000..472fa9b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MACRO_H
+#define PIM_MACRO_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+
+int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch);
+int pim_macro_chisin_joins(const struct pim_ifchannel *ch);
+int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch);
+int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch);
+int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch);
+struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
+                                                    struct in_addr ifaddr);
+struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch);
+int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch);
+int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch);
+
+#endif /* PIM_MACRO_H */
diff --git a/pimd/pim_main.c b/pimd/pim_main.c
new file mode 100644 (file)
index 0000000..ea70a8f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "privs.h" 
+#include "version.h"
+#include <getopt.h>
+#include "command.h"
+#include "thread.h"
+#include <signal.h>
+
+#include "memory.h"
+#include "vrf.h"
+#include "filter.h"
+#include "vty.h"
+#include "sigevent.h"
+#include "version.h"
+#include "prefix.h"
+#include "plist.h"
+
+#include "pimd.h"
+#include "pim_version.h"
+#include "pim_signals.h"
+#include "pim_zebra.h"
+
+#ifdef PIM_ZCLIENT_DEBUG
+extern int zclient_debug;
+#endif
+
+extern struct host host;
+
+char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG;
+
+struct option longopts[] = {
+  { "daemon",        no_argument,       NULL, 'd'},
+  { "config_file",   required_argument, NULL, 'f'},
+  { "pid_file",      required_argument, NULL, 'i'},
+  { "vty_addr",      required_argument, NULL, 'A'},
+  { "vty_port",      required_argument, NULL, 'P'},
+  { "version",       no_argument,       NULL, 'v'},
+  { "debug_zclient", no_argument,       NULL, 'Z'},
+  { "help",          no_argument,       NULL, 'h'},
+  { 0 }
+};
+
+/* pimd privileges */
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_ADMIN,
+  ZCAP_SYS_ADMIN,
+  ZCAP_NET_RAW,
+};
+
+/* pimd privileges to run with */
+struct zebra_privs_t pimd_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]),
+  .cap_num_i = 0
+};
+
+char* progname;
+const char *pid_file = PATH_PIMD_PID;
+
+static void usage(int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else {    
+    printf ("Usage : %s [OPTION...]\n\
+Daemon which manages PIM.\n\n\
+-d, --daemon         Run in daemon mode\n\
+-f, --config_file    Set configuration file name\n\
+-i, --pid_file       Set process identifier file name\n\
+-z, --socket         Set path of zebra socket\n\
+-A, --vty_addr       Set vty's bind address\n\
+-P, --vty_port       Set vty's port number\n\
+-v, --version        Print program version\n\
+"
+
+#ifdef PIM_ZCLIENT_DEBUG
+"\
+-Z, --debug_zclient  Enable zclient debugging\n\
+"
+#endif
+
+"\
+-h, --help           Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, PIMD_BUG_ADDRESS);
+  }
+
+  exit (status);
+}
+
+
+int main(int argc, char** argv, char** envp) {
+  char *p;
+  char *vty_addr = NULL;
+  int vty_port = -1;
+  int daemon_mode = 0;
+  char *config_file = NULL;
+  char *zebra_sock_path = NULL;
+  struct thread thread;
+          
+  umask(0027);
+  progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+  zlog_default = openzlog(progname, ZLOG_PIM,
+                         LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+     
+  /* this while just reads the options */                       
+  while (1) {
+    int opt;
+            
+    opt = getopt_long (argc, argv, "df:i:z:A:P:vZh", longopts, 0);
+                      
+    if (opt == EOF)
+      break;
+    
+    switch (opt) {
+    case 0:
+      break;
+    case 'd':
+      daemon_mode = 1;
+      break;
+    case 'f':
+      config_file = optarg;
+      break;
+    case 'i':
+      pid_file = optarg;
+      break;
+    case 'z':
+      zebra_sock_path = optarg;
+      break;
+    case 'A':
+      vty_addr = optarg;
+      break;
+    case 'P':
+      vty_port = atoi (optarg);
+      break;
+    case 'v':
+      printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION);
+      print_version(QUAGGA_PROGNAME);
+      exit (0);
+      break;
+#ifdef PIM_ZCLIENT_DEBUG
+    case 'Z':
+      zclient_debug = 1;
+      break;
+#endif
+    case 'h':
+      usage (0);
+      break;
+    default:
+      usage (1);
+      break;
+    }
+  }
+
+  master = thread_master_create();
+
+  zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting",
+             QUAGGA_VERSION, PIMD_VERSION);
+
+  /* 
+   * Initializations
+   */
+  zprivs_init (&pimd_privs);
+  pim_signals_init();
+  cmd_init(1);
+  vty_init(master);
+  memory_init();
+  vrf_init();
+  access_list_init();
+  prefix_list_init ();
+  pim_route_map_init ();
+  pim_init();
+
+  /*
+   * Initialize zclient "update" and "lookup" sockets
+   */
+  pim_zebra_init (master, zebra_sock_path);
+
+  zlog_notice("Loading configuration - begin");
+
+  /* Get configuration file. */
+  vty_read_config(config_file, config_default);
+
+  /*
+    Starting from here zlog_* functions will log according configuration
+   */
+
+  zlog_notice("Loading configuration - end");
+
+  /* Change to the daemon program. */
+  if (daemon_mode) {
+    if (daemon(0, 0)) {
+      zlog_warn("failed to daemonize");
+    }
+  }
+
+  /* Process ID file creation. */
+  pid_output(pid_file);
+
+  /* Create pimd VTY socket */
+  if (vty_port < 0)
+    vty_port = PIMD_VTY_PORT;
+  vty_serv_sock(vty_addr, vty_port, PIM_VTYSH_PATH);
+
+  zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d",
+             QUAGGA_VERSION, PIMD_VERSION, vty_port);
+
+#ifdef PIM_DEBUG_BYDEFAULT
+  zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands");
+  PIM_DO_DEBUG_PIM_EVENTS;
+  PIM_DO_DEBUG_PIM_PACKETS;
+  PIM_DO_DEBUG_PIM_TRACE;
+  PIM_DO_DEBUG_IGMP_EVENTS;
+  PIM_DO_DEBUG_IGMP_PACKETS;
+  PIM_DO_DEBUG_IGMP_TRACE;
+  PIM_DO_DEBUG_ZEBRA;
+#endif
+
+#ifdef PIM_ZCLIENT_DEBUG
+  zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)",
+             zclient_debug ? "ON" : "OFF");
+#endif
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+  zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex");
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+  zlog_notice("PIM_REPORT_RECV_IFINDEX_MISMATCH: will report sock/recv ifindex mismatch");
+#endif
+#endif
+
+#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
+  zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall");
+#endif
+
+#ifdef HAVE_CLOCK_MONOTONIC
+  zlog_notice("HAVE_CLOCK_MONOTONIC");
+#else
+  zlog_notice("!HAVE_CLOCK_MONOTONIC");
+#endif
+
+  while (thread_fetch(master, &thread))
+    thread_call(&thread);
+
+  zlog_err("%s %s: thread_fetch() returned NULL, exiting",
+          __FILE__, __PRETTY_FUNCTION__);
+
+  /* never reached */
+  return 0;
+}
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
new file mode 100644 (file)
index 0000000..fa460e2
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "log.h"
+#include "privs.h"
+
+#include "pimd.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_macro.h"
+
+/* GLOBAL VARS */
+extern struct zebra_privs_t pimd_privs;
+
+static void mroute_read_on(void);
+
+static int pim_mroute_set(int fd, int enable)
+{
+  int err;
+  int opt = enable ? MRT_INIT : MRT_DONE;
+  socklen_t opt_len = sizeof(opt);
+
+  err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
+  if (err) {
+    int e = errno;
+    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
+    errno = e;
+    return -1;
+  }
+
+#if 0
+  zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
+           __FILE__, __PRETTY_FUNCTION__,
+           fd, opt);
+#endif
+
+  return 0;
+}
+
+int pim_mroute_msg(int fd, const char *buf, int buf_size)
+{
+  struct interface     *ifp;
+  const struct ip      *ip_hdr;
+  const struct igmpmsg *msg;
+  const char *upcall;
+  char src_str[100];
+  char grp_str[100];
+
+  ip_hdr = (const struct ip *) buf;
+
+  /* kernel upcall must have protocol=0 */
+  if (ip_hdr->ip_p) {
+    /* this is not a kernel upcall */
+#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
+    zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
+             __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
+#endif
+    return 0;
+  }
+
+  msg = (const struct igmpmsg *) buf;
+
+  switch (msg->im_msgtype) {
+  case IGMPMSG_NOCACHE:  upcall = "NOCACHE";  break;
+  case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
+  case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
+  default: upcall = "<unknown_upcall?>";
+  }
+  ifp = pim_if_find_by_vif_index(msg->im_vif);
+  pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
+  pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
+    
+  if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
+    struct pim_ifchannel *ch;
+    struct pim_interface *pim_ifp;
+
+    /*
+      Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
+      
+      RFC 4601 4.8.2.  PIM-SSM-Only Routers
+      
+      iif is the incoming interface of the packet.
+      if (iif is in inherited_olist(S,G)) {
+      send Assert(S,G) on iif
+      }
+    */
+
+    if (PIM_DEBUG_PIM_TRACE) {
+      zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
+                __PRETTY_FUNCTION__,
+                fd,
+                src_str,
+                grp_str,
+                ifp ? ifp->name : "<ifname?>",
+                msg->im_vif);
+    }
+
+    if (!ifp) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, msg->im_vif);
+      return -1;
+    }
+
+    pim_ifp = ifp->info;
+    if (!pim_ifp) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, ifp->name);
+      return -2;
+    }
+
+    ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
+    if (!ch) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, ifp->name);
+      return -3;
+    }
+
+    /*
+      RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
+
+      Transitions from NoInfo State
+
+      An (S,G) data packet arrives on interface I, AND
+      CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
+      downstream interface that is in our (S,G) outgoing interface
+      list.  We optimistically assume that we will be the assert
+      winner for this (S,G), and so we transition to the "I am Assert
+      Winner" state and perform Actions A1 (below), which will
+      initiate the assert negotiation for (S,G).
+    */
+
+    if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, ifp->name);
+      return -4;
+    }
+
+    if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, ifp->name);
+      return -5;
+    }
+
+    if (assert_action_a1(ch)) {
+      zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, ifp->name);
+      return -6;
+    }
+
+    return 0;
+  } /* IGMPMSG_WRONGVIF */
+
+  zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
+           __PRETTY_FUNCTION__,
+           upcall,
+           msg->im_msgtype,
+           ip_hdr->ip_p,
+           fd,
+           src_str,
+           grp_str,
+           ifp ? ifp->name : "<ifname?>",
+           msg->im_vif);
+
+  return 0;
+}
+
+static int mroute_read_msg(int fd)
+{
+  const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
+  char buf[1000];
+  int rd;
+
+  if (((int) sizeof(buf)) < msg_min_size) {
+    zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
+            __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
+    return -1;
+  }
+
+  rd = read(fd, buf, sizeof(buf));
+  if (rd < 0) {
+    zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
+             __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+    return -2;
+  }
+
+  if (rd < msg_min_size) {
+    zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
+             __PRETTY_FUNCTION__, fd, rd, msg_min_size);
+    return -3;
+  }
+
+  return pim_mroute_msg(fd, buf, rd);
+}
+
+static int mroute_read(struct thread *t)
+{
+  int fd;
+  int result;
+
+  zassert(t);
+  zassert(!THREAD_ARG(t));
+
+  fd = THREAD_FD(t);
+  zassert(fd == qpim_mroute_socket_fd);
+
+  result = mroute_read_msg(fd);
+
+  /* Keep reading */
+  qpim_mroute_socket_reader = 0;
+  mroute_read_on();
+
+  return result;
+}
+
+static void mroute_read_on()
+{
+  zassert(!qpim_mroute_socket_reader);
+  zassert(PIM_MROUTE_IS_ENABLED);
+
+  THREAD_READ_ON(master, qpim_mroute_socket_reader,
+                mroute_read, 0, qpim_mroute_socket_fd);
+}
+
+static void mroute_read_off()
+{
+  THREAD_OFF(qpim_mroute_socket_reader);
+}
+
+int pim_mroute_socket_enable()
+{
+  int fd;
+
+  if (PIM_MROUTE_IS_ENABLED)
+    return -1;
+
+  if ( pimd_privs.change (ZPRIVS_RAISE) )
+    zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
+              safe_strerror (errno) );
+
+  fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+  if ( pimd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
+             safe_strerror (errno) );
+
+  if (fd < 0) {
+    zlog_warn("Could not create mroute socket: errno=%d: %s",
+             errno, safe_strerror(errno));
+    return -2;
+  }
+
+  if (pim_mroute_set(fd, 1)) {
+    zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    close(fd);
+    return -3;
+  }
+
+  qpim_mroute_socket_fd       = fd;
+  qpim_mroute_socket_creation = pim_time_monotonic_sec();
+  mroute_read_on();
+
+  zassert(PIM_MROUTE_IS_ENABLED);
+
+  return 0;
+}
+
+int pim_mroute_socket_disable()
+{
+  if (PIM_MROUTE_IS_DISABLED)
+    return -1;
+
+  if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
+    zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
+             qpim_mroute_socket_fd, errno, safe_strerror(errno));
+    return -2;
+  }
+
+  if (close(qpim_mroute_socket_fd)) {
+    zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
+             qpim_mroute_socket_fd, errno, safe_strerror(errno));
+    return -3;
+  }
+
+  mroute_read_off();
+  qpim_mroute_socket_fd = -1;
+
+  zassert(PIM_MROUTE_IS_DISABLED);
+
+  return 0;
+}
+
+/*
+  For each network interface (e.g., physical or a virtual tunnel) that
+  would be used for multicast forwarding, a corresponding multicast
+  interface must be added to the kernel.
+ */
+int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
+{
+  struct vifctl vc;
+  int err;
+
+  if (PIM_MROUTE_IS_DISABLED) {
+    zlog_warn("%s: global multicast is disabled",
+             __PRETTY_FUNCTION__);
+    return -1;
+  }
+
+  memset(&vc, 0, sizeof(vc));
+  vc.vifc_vifi = vif_index;
+  vc.vifc_flags = 0;
+  vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
+  vc.vifc_rate_limit = 0;
+  memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
+
+#ifdef PIM_DVMRP_TUNNEL  
+  if (vc.vifc_flags & VIFF_TUNNEL) {
+    memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
+  }
+#endif
+
+  err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); 
+  if (err) {
+    char ifaddr_str[100];
+    int e = errno;
+
+    pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
+
+    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             qpim_mroute_socket_fd, vif_index, ifaddr_str,
+             e, safe_strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_mroute_del_vif(int vif_index)
+{
+  struct vifctl vc;
+  int err;
+
+  if (PIM_MROUTE_IS_DISABLED) {
+    zlog_warn("%s: global multicast is disabled",
+             __PRETTY_FUNCTION__);
+    return -1;
+  }
+
+  memset(&vc, 0, sizeof(vc));
+  vc.vifc_vifi = vif_index;
+
+  err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); 
+  if (err) {
+    int e = errno;
+    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             qpim_mroute_socket_fd, vif_index,
+             e, safe_strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_mroute_add(struct mfcctl *mc)
+{
+  int err;
+
+  qpim_mroute_add_last = pim_time_monotonic_sec();
+  ++qpim_mroute_add_events;
+
+  if (PIM_MROUTE_IS_DISABLED) {
+    zlog_warn("%s: global multicast is disabled",
+             __PRETTY_FUNCTION__);
+    return -1;
+  }
+
+  err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
+                  mc, sizeof(*mc));
+  if (err) {
+    int e = errno;
+    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             qpim_mroute_socket_fd,
+             e, safe_strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_mroute_del(struct mfcctl *mc)
+{
+  int err;
+
+  qpim_mroute_del_last = pim_time_monotonic_sec();
+  ++qpim_mroute_del_events;
+
+  if (PIM_MROUTE_IS_DISABLED) {
+    zlog_warn("%s: global multicast is disabled",
+             __PRETTY_FUNCTION__);
+    return -1;
+  }
+
+  err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
+  if (err) {
+    int e = errno;
+    zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             qpim_mroute_socket_fd,
+             e, safe_strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h
new file mode 100644 (file)
index 0000000..125d190
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MROUTE_H
+#define PIM_MROUTE_H
+
+/*
+  For msghdr.msg_control in Solaris 10
+*/
+#ifndef _XPG4_2
+#define _XPG4_2
+#endif
+#ifndef __EXTENSIONS__
+#define __EXTENSIONS__
+#endif
+
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_IP_MROUTE_H
+#include <netinet/ip_mroute.h>
+#endif
+
+#define PIM_MROUTE_MIN_TTL (1)
+
+#if defined(HAVE_LINUX_MROUTE_H)
+#include <linux/mroute.h>
+#else
+/*
+  Below: from <linux/mroute.h>
+*/
+
+#ifndef MAXVIFS
+#define MAXVIFS (32)
+#endif
+
+#ifndef SIOCGETVIFCNT
+#define SIOCGETVIFCNT   SIOCPROTOPRIVATE        /* IP protocol privates */
+#define SIOCGETSGCNT    (SIOCPROTOPRIVATE+1)
+#define SIOCGETRPF      (SIOCPROTOPRIVATE+2)
+#endif
+
+#ifndef MRT_INIT
+#define MRT_BASE     200
+#define MRT_INIT     (MRT_BASE)      /* Activate the kernel mroute code      */
+#define MRT_DONE     (MRT_BASE+1)    /* Shutdown the kernel mroute           */
+#define MRT_ADD_VIF  (MRT_BASE+2)    /* Add a virtual interface              */
+#define MRT_DEL_VIF  (MRT_BASE+3)    /* Delete a virtual interface           */
+#define MRT_ADD_MFC  (MRT_BASE+4)    /* Add a multicast forwarding entry     */
+#define MRT_DEL_MFC  (MRT_BASE+5)    /* Delete a multicast forwarding entry  */
+#define MRT_VERSION  (MRT_BASE+6)    /* Get the kernel multicast version     */
+#define MRT_ASSERT   (MRT_BASE+7)    /* Activate PIM assert mode             */
+#define MRT_PIM      (MRT_BASE+8)    /* enable PIM code      */
+#endif
+
+#ifndef HAVE_VIFI_T
+typedef unsigned short vifi_t;
+#endif
+
+#ifndef HAVE_STRUCT_VIFCTL
+struct vifctl {
+       vifi_t  vifc_vifi;              /* Index of VIF */
+       unsigned char vifc_flags;       /* VIFF_ flags */
+       unsigned char vifc_threshold;   /* ttl limit */
+       unsigned int vifc_rate_limit;   /* Rate limiter values (NI) */
+       struct in_addr vifc_lcl_addr;   /* Our address */
+       struct in_addr vifc_rmt_addr;   /* IPIP tunnel addr */
+};
+#endif
+
+#ifndef HAVE_STRUCT_MFCCTL
+struct mfcctl {
+  struct in_addr mfcc_origin;             /* Origin of mcast      */
+  struct in_addr mfcc_mcastgrp;           /* Group in question    */
+  vifi_t         mfcc_parent;             /* Where it arrived     */
+  unsigned char  mfcc_ttls[MAXVIFS];      /* Where it is going    */
+  unsigned int   mfcc_pkt_cnt;            /* pkt count for src-grp */
+  unsigned int   mfcc_byte_cnt;
+  unsigned int   mfcc_wrong_if;
+  int            mfcc_expire;
+};
+#endif
+
+/*
+ *      Group count retrieval for mrouted
+ */
+/*
+  struct sioc_sg_req sgreq;
+  memset(&sgreq, 0, sizeof(sgreq));
+  memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src));
+  memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp));
+  ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq);
+ */
+#ifndef HAVE_STRUCT_SIOC_SG_REQ
+struct sioc_sg_req {
+  struct in_addr src;
+  struct in_addr grp;
+  unsigned long pktcnt;
+  unsigned long bytecnt;
+  unsigned long wrong_if;
+};
+#endif
+
+/*
+ *      To get vif packet counts
+ */
+/*
+  struct sioc_vif_req vreq;
+  memset(&vreq, 0, sizeof(vreq));
+  vreq.vifi = vif_index;
+  ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq);
+ */
+#ifndef HAVE_STRUCT_SIOC_VIF_REQ
+struct sioc_vif_req {
+  vifi_t  vifi;           /* Which iface */
+  unsigned long icount;   /* In packets */
+  unsigned long ocount;   /* Out packets */
+  unsigned long ibytes;   /* In bytes */
+  unsigned long obytes;   /* Out bytes */
+};
+#endif
+
+/*
+ *      Pseudo messages used by mrouted
+ */
+#ifndef IGMPMSG_NOCACHE
+#define IGMPMSG_NOCACHE         1               /* Kern cache fill request to mrouted */
+#define IGMPMSG_WRONGVIF        2               /* For PIM assert processing (unused) */
+#define IGMPMSG_WHOLEPKT        3               /* For PIM Register processing */
+#endif
+
+#ifndef HAVE_STRUCT_IGMPMSG
+struct igmpmsg
+{
+  uint32_t unused1,unused2;
+  unsigned char im_msgtype;               /* What is this */
+  unsigned char im_mbz;                   /* Must be zero */
+  unsigned char im_vif;                   /* Interface (this ought to be a vifi_t!) */
+  unsigned char unused3;
+  struct in_addr im_src,im_dst;
+};
+#endif
+#endif
+
+/*
+  Above: from <linux/mroute.h>
+*/
+
+int pim_mroute_socket_enable(void);
+int pim_mroute_socket_disable(void);
+
+int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr);
+int pim_mroute_del_vif(int vif_index);
+
+int pim_mroute_add(struct mfcctl *mc);
+int pim_mroute_del(struct mfcctl *mc);
+
+int pim_mroute_msg(int fd, const char *buf, int buf_size);
+
+#endif /* PIM_MROUTE_H */
diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c
new file mode 100644 (file)
index 0000000..8ead7ce
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_msg.h"
+#include "pim_util.h"
+
+void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
+                         uint8_t pim_msg_type)
+{
+  uint16_t checksum;
+
+  zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
+
+  /*
+   * Write header
+   */
+
+  *(uint8_t *) PIM_MSG_HDR_OFFSET_VERSION(pim_msg) = (PIM_PROTO_VERSION << 4) | pim_msg_type;
+  *(uint8_t *) PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) = 0;
+
+  /*
+   * Compute checksum
+   */
+
+  *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
+  checksum = in_cksum(pim_msg, pim_msg_size);
+  *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum;
+}
+
+uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf,
+                                       int buf_size,
+                                       struct in_addr addr)
+{
+  const int ENCODED_IPV4_UCAST_SIZE = 6;
+
+  if (buf_size < ENCODED_IPV4_UCAST_SIZE) {
+    return 0;
+  }
+
+  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+  buf[1] = '\0';    /* native encoding */
+  memcpy(buf+2, &addr, sizeof(struct in_addr));
+
+  return buf + ENCODED_IPV4_UCAST_SIZE;
+}
+
+uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
+                                       int buf_size,
+                                       struct in_addr addr)
+{
+  const int ENCODED_IPV4_GROUP_SIZE = 8;
+
+  if (buf_size < ENCODED_IPV4_GROUP_SIZE) {
+    return 0;
+  }
+
+  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+  buf[1] = '\0';    /* native encoding */
+  buf[2] = '\0';    /* reserved */
+  buf[3] = 32;      /* mask len */
+  memcpy(buf+4, &addr, sizeof(struct in_addr));
+
+  return buf + ENCODED_IPV4_GROUP_SIZE;
+}
+
+uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
+                                        int buf_size,
+                                        struct in_addr addr)
+{
+  const int ENCODED_IPV4_SOURCE_SIZE = 8;
+
+  if (buf_size < ENCODED_IPV4_SOURCE_SIZE) {
+    return 0;
+  }
+
+  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
+  buf[1] = '\0';    /* native encoding */
+  buf[2] = 4;       /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */
+  buf[3] = 32;      /* mask len */
+  memcpy(buf+4, &addr, sizeof(struct in_addr));
+
+  return buf + ENCODED_IPV4_SOURCE_SIZE;
+}
diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h
new file mode 100644 (file)
index 0000000..a884fc8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_MSG_H
+#define PIM_MSG_H
+
+#include <netinet/in.h>
+
+/*
+  Number       Description       
+  ----------   ------------------
+  0            Reserved
+  1            IP (IP version 4)
+  2            IP6 (IP version 6)
+
+  From:
+  http://www.iana.org/assignments/address-family-numbers
+*/
+#define PIM_MSG_ADDRESS_FAMILY_IPV4 (1)
+
+void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size,
+                         uint8_t pim_msg_type);
+uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf,
+                                       int buf_size,
+                                       struct in_addr addr);
+uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf,
+                                       int buf_size,
+                                       struct in_addr addr);
+uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf,
+                                        int buf_size,
+                                        struct in_addr addr);
+
+#endif /* PIM_MSG_H */
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
new file mode 100644 (file)
index 0000000..8932dc3
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_neighbor.h"
+#include "pim_time.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_pim.h"
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+
+static void dr_election_by_addr(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct pim_neighbor  *neigh;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  pim_ifp->pim_dr_addr = pim_ifp->primary_address;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("%s: on interface %s",
+              __PRETTY_FUNCTION__,
+              ifp->name);
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+    if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) {
+      pim_ifp->pim_dr_addr = neigh->source_addr;
+    }
+  }
+}
+
+static void dr_election_by_pri(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct pim_neighbor  *neigh;
+  uint32_t              dr_pri;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  pim_ifp->pim_dr_addr = pim_ifp->primary_address;
+  dr_pri = pim_ifp->pim_dr_priority;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("%s: dr pri %u on interface %s",
+              __PRETTY_FUNCTION__,
+              dr_pri, ifp->name);
+  }
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+    if (PIM_DEBUG_PIM_TRACE) {
+      zlog_info("%s: neigh pri %u addr %x if dr addr %x",
+               __PRETTY_FUNCTION__,
+               neigh->dr_priority,
+               ntohl(neigh->source_addr.s_addr),
+               ntohl(pim_ifp->pim_dr_addr.s_addr));
+    }
+    if (
+       (neigh->dr_priority > dr_pri) ||
+       (
+        (neigh->dr_priority == dr_pri) &&
+        (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr))
+        )
+       ) {
+      pim_ifp->pim_dr_addr = neigh->source_addr;
+      dr_pri               = neigh->dr_priority;
+    }
+  }
+}
+
+/*
+  RFC 4601: 4.3.2.  DR Election
+
+  A router's idea of the current DR on an interface can change when a
+  PIM Hello message is received, when a neighbor times out, or when a
+  router's own DR Priority changes.
+ */
+int pim_if_dr_election(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+  struct in_addr old_dr_addr;
+
+  ++pim_ifp->pim_dr_election_count;
+
+  old_dr_addr = pim_ifp->pim_dr_addr;
+
+  if (pim_ifp->pim_dr_num_nondrpri_neighbors) {
+    dr_election_by_addr(ifp);
+  }
+  else {
+    dr_election_by_pri(ifp);
+  }
+
+  /* DR changed ? */
+  if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) {
+
+    if (PIM_DEBUG_PIM_EVENTS) {
+      char dr_old_str[100];
+      char dr_new_str[100];
+      pim_inet4_dump("<old_dr?>", old_dr_addr, dr_old_str, sizeof(dr_old_str));
+      pim_inet4_dump("<new_dr?>", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str));
+      zlog_debug("%s: DR was %s now is %s on interface %s",
+                __PRETTY_FUNCTION__,
+                dr_old_str, dr_new_str, ifp->name);
+    }
+
+    pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */
+    ++pim_ifp->pim_dr_election_changes; 
+    pim_if_update_join_desired(pim_ifp);
+    pim_if_update_could_assert(ifp);
+    pim_if_update_assert_tracking_desired(ifp);
+    return 1;
+  }
+
+  return 0;
+}
+
+static void update_dr_priority(struct pim_neighbor *neigh,
+                              pim_hello_options hello_options,
+                              uint32_t dr_priority)
+{
+  pim_hello_options will_set_pri; /* boolean */
+  pim_hello_options bit_flip;     /* boolean */
+  pim_hello_options pri_change;   /* boolean */
+
+  will_set_pri = PIM_OPTION_IS_SET(hello_options,
+                                  PIM_OPTION_MASK_DR_PRIORITY);
+
+  bit_flip =
+    (
+     will_set_pri !=
+     PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)
+     );
+
+  if (bit_flip) {
+    struct pim_interface *pim_ifp = neigh->interface->info;
+
+    /* update num. of neighbors without dr_pri */
+
+    if (will_set_pri) {
+      --pim_ifp->pim_dr_num_nondrpri_neighbors; 
+    }
+    else {
+      ++pim_ifp->pim_dr_num_nondrpri_neighbors; 
+    }
+  }
+
+  pri_change = 
+    (
+     bit_flip
+     ||
+     (neigh->dr_priority != dr_priority)
+     );
+
+  if (will_set_pri) {
+    neigh->dr_priority = dr_priority;
+  }
+  else {
+    neigh->dr_priority = 0; /* cosmetic unset */
+  }
+
+  if (pri_change) {
+    /*
+      RFC 4601: 4.3.2.  DR Election
+      
+      A router's idea of the current DR on an interface can change when a
+      PIM Hello message is received, when a neighbor times out, or when a
+      router's own DR Priority changes.
+    */
+    pim_if_dr_election(neigh->interface); // router's own DR Priority changes
+  }
+}
+
+static int on_neighbor_timer(struct thread *t)
+{
+  struct pim_neighbor *neigh;
+  struct interface *ifp;
+  char msg[100];
+
+  zassert(t);
+  neigh = THREAD_ARG(t);
+  zassert(neigh);
+
+  ifp = neigh->interface;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+    zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s",
+              neigh->holdtime, src_str, ifp->name);
+  }
+
+  neigh->t_expire_timer = 0;
+
+  snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime);
+  pim_neighbor_delete(ifp, neigh, msg);
+
+  /*
+    RFC 4601: 4.3.2.  DR Election
+    
+    A router's idea of the current DR on an interface can change when a
+    PIM Hello message is received, when a neighbor times out, or when a
+    router's own DR Priority changes.
+  */
+  pim_if_dr_election(ifp); // neighbor times out
+
+  return 0;
+}
+
+static void neighbor_timer_off(struct pim_neighbor *neigh)
+{
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (neigh->t_expire_timer) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+      zlog_debug("%s: cancelling timer for neighbor %s on %s",
+                __PRETTY_FUNCTION__,
+                src_str, neigh->interface->name);
+    }
+  }
+  THREAD_OFF(neigh->t_expire_timer);
+  zassert(!neigh->t_expire_timer);
+}
+
+void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime)
+{
+  neigh->holdtime = holdtime;
+
+  neighbor_timer_off(neigh);
+
+  /*
+    0xFFFF is request for no holdtime
+   */
+  if (neigh->holdtime == 0xFFFF) {
+    return;
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+    zlog_debug("%s: starting %u sec timer for neighbor %s on %s",
+              __PRETTY_FUNCTION__,
+              neigh->holdtime, src_str, neigh->interface->name);
+  }
+
+  THREAD_TIMER_ON(master, neigh->t_expire_timer,
+                 on_neighbor_timer,
+                 neigh, neigh->holdtime);
+}
+
+static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
+                                            struct in_addr source_addr,
+                                            pim_hello_options hello_options,
+                                            uint16_t holdtime,
+                                            uint16_t propagation_delay,
+                                            uint16_t override_interval,
+                                            uint32_t dr_priority,
+                                            uint32_t generation_id,
+                                            struct list *addr_list)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+  char src_str[100];
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
+  if (!neigh) {
+    zlog_err("%s: PIM XMALLOC(%zu) failure",
+            __PRETTY_FUNCTION__, sizeof(*neigh));
+    return 0;
+  }
+
+  neigh->creation               = pim_time_monotonic_sec();
+  neigh->source_addr            = source_addr;
+  neigh->hello_options          = hello_options;
+  neigh->propagation_delay_msec = propagation_delay;
+  neigh->override_interval_msec = override_interval;
+  neigh->dr_priority            = dr_priority;
+  neigh->generation_id          = generation_id;
+  neigh->prefix_list            = addr_list;
+  neigh->t_expire_timer         = 0;
+  neigh->interface              = ifp;
+
+  pim_neighbor_timer_reset(neigh, holdtime);
+
+  pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    zlog_debug("%s: creating PIM neighbor %s on interface %s",
+              __PRETTY_FUNCTION__,
+              src_str, ifp->name);
+  }
+
+  zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s",
+           src_str, ifp->name);
+
+  if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
+    pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec;
+  }
+  if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) {
+    pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec;
+  }
+
+  if (!PIM_OPTION_IS_SET(neigh->hello_options,
+                        PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
+    /* update num. of neighbors without hello option lan_delay */
+    ++pim_ifp->pim_number_of_nonlandelay_neighbors; 
+  }
+
+  if (!PIM_OPTION_IS_SET(neigh->hello_options,
+                        PIM_OPTION_MASK_DR_PRIORITY)) {
+    /* update num. of neighbors without hello option dr_pri */
+    ++pim_ifp->pim_dr_num_nondrpri_neighbors; 
+  }
+
+  return neigh;
+}
+
+static void delete_prefix_list(struct pim_neighbor *neigh)
+{
+  if (neigh->prefix_list) {
+
+#ifdef DUMP_PREFIX_LIST
+    struct listnode *p_node;
+    struct prefix *p;
+    char addr_str[10];
+    int list_size = neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1;
+    int i = 0;
+    for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) {
+      pim_inet4_dump("<addr?>", p->u.prefix4, addr_str, sizeof(addr_str));
+      zlog_debug("%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]",
+                __PRETTY_FUNCTION__,
+                (unsigned) neigh, (unsigned) neigh->prefix_list, (unsigned) p,
+                addr_str, i, list_size);
+      ++i;
+    }
+#endif
+
+    list_delete(neigh->prefix_list);
+    neigh->prefix_list = 0;
+  }
+}
+
+void pim_neighbor_free(struct pim_neighbor *neigh)
+{
+  zassert(!neigh->t_expire_timer);
+
+  delete_prefix_list(neigh);
+
+  XFREE(MTYPE_PIM_NEIGHBOR, neigh);
+}
+
+struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
+                                      struct in_addr source_addr)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode      *node;
+  struct pim_neighbor  *neigh;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
+    if (source_addr.s_addr == neigh->source_addr.s_addr) {
+      return neigh;
+    }
+  }
+
+  return 0;
+}
+
+struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
+                                     struct in_addr source_addr,
+                                     pim_hello_options hello_options,
+                                     uint16_t holdtime,
+                                     uint16_t propagation_delay,
+                                     uint16_t override_interval,
+                                     uint32_t dr_priority,
+                                     uint32_t generation_id,
+                                     struct list *addr_list)
+{
+  struct pim_interface *pim_ifp;
+  struct pim_neighbor *neigh;
+
+  neigh = pim_neighbor_new(ifp, source_addr,
+                          hello_options,
+                          holdtime,
+                          propagation_delay,
+                          override_interval,
+                          dr_priority,
+                          generation_id,
+                          addr_list);
+  if (!neigh) {
+    return 0;
+  }
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  listnode_add(pim_ifp->pim_neighbor_list, neigh);
+
+  /*
+    RFC 4601: 4.3.2.  DR Election
+
+    A router's idea of the current DR on an interface can change when a
+    PIM Hello message is received, when a neighbor times out, or when a
+    router's own DR Priority changes.
+  */
+  pim_if_dr_election(neigh->interface); // new neighbor -- should not trigger dr election...
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+
+    To allow new or rebooting routers to learn of PIM neighbors quickly,
+    when a Hello message is received from a new neighbor, or a Hello
+    message with a new GenID is received from an existing neighbor, a
+    new Hello message should be sent on this interface after a
+    randomized delay between 0 and Triggered_Hello_Delay.
+  */
+  pim_hello_restart_triggered(neigh->interface);
+
+  return neigh;
+}
+
+static uint16_t
+find_neighbors_next_highest_propagation_delay_msec(struct interface *ifp,
+                                                  struct pim_neighbor *highest_neigh)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *neigh_node;
+  struct pim_neighbor *neigh;
+  uint16_t next_highest_delay_msec;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec;
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
+    if (neigh == highest_neigh)
+      continue;
+    if (neigh->propagation_delay_msec > next_highest_delay_msec)
+      next_highest_delay_msec = neigh->propagation_delay_msec;
+  }
+
+  return next_highest_delay_msec;
+}
+
+static uint16_t
+find_neighbors_next_highest_override_interval_msec(struct interface *ifp,
+                                                  struct pim_neighbor *highest_neigh)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *neigh_node;
+  struct pim_neighbor *neigh;
+  uint16_t next_highest_interval_msec;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  next_highest_interval_msec = pim_ifp->pim_override_interval_msec;
+
+  for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) {
+    if (neigh == highest_neigh)
+      continue;
+    if (neigh->override_interval_msec > next_highest_interval_msec)
+      next_highest_interval_msec = neigh->override_interval_msec;
+  }
+
+  return next_highest_interval_msec;
+}
+
+void pim_neighbor_delete(struct interface *ifp,
+                        struct pim_neighbor *neigh,
+                        const char *delete_message)
+{
+  struct pim_interface *pim_ifp;
+  char src_str[100];
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+  zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
+           src_str, ifp->name, delete_message);
+
+  neighbor_timer_off(neigh);
+
+  pim_if_assert_on_neighbor_down(ifp, neigh->source_addr);
+
+  if (!PIM_OPTION_IS_SET(neigh->hello_options,
+                         PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
+    /* update num. of neighbors without hello option lan_delay */
+
+    --pim_ifp->pim_number_of_nonlandelay_neighbors;
+  }
+
+  if (!PIM_OPTION_IS_SET(neigh->hello_options,
+                        PIM_OPTION_MASK_DR_PRIORITY)) {
+    /* update num. of neighbors without dr_pri */
+
+    --pim_ifp->pim_dr_num_nondrpri_neighbors; 
+  }
+
+  zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+  zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec);
+
+  if (pim_if_lan_delay_enabled(ifp)) {
+
+    /* will delete a neighbor with highest propagation delay? */
+    if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
+      /* then find the next highest propagation delay */
+      pim_ifp->pim_neighbors_highest_propagation_delay_msec =
+       find_neighbors_next_highest_propagation_delay_msec(ifp, neigh);
+    }
+
+    /* will delete a neighbor with highest override interval? */
+    if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) {
+      /* then find the next highest propagation delay */
+      pim_ifp->pim_neighbors_highest_override_interval_msec =
+       find_neighbors_next_highest_override_interval_msec(ifp, neigh);
+    }
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("%s: deleting PIM neighbor %s on interface %s",
+              __PRETTY_FUNCTION__,
+              src_str, ifp->name);
+  }
+
+  listnode_delete(pim_ifp->pim_neighbor_list, neigh);
+
+  pim_neighbor_free(neigh);
+}
+
+void pim_neighbor_delete_all(struct interface *ifp,
+                            const char *delete_message)
+{
+  struct pim_interface *pim_ifp;
+  struct listnode *neigh_node;
+  struct listnode *neigh_nextnode;
+  struct pim_neighbor *neigh;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node,
+                        neigh_nextnode, neigh)) {
+    pim_neighbor_delete(ifp, neigh, delete_message);
+  }
+}
+
+struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
+                                          struct in_addr addr)
+{
+  struct listnode *node;
+  struct prefix   *p;
+
+  if (!neigh->prefix_list)
+    return 0;
+
+  for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) {
+    if (p->family == AF_INET) {
+      if (addr.s_addr == p->u.prefix4.s_addr) {
+       return p;
+      }
+    }
+  }
+
+  return 0;
+}
+
+/*
+  RFC 4601: 4.3.4.  Maintaining Secondary Address Lists
+  
+  All the advertised secondary addresses in received Hello messages
+  must be checked against those previously advertised by all other
+  PIM neighbors on that interface.  If there is a conflict and the
+  same secondary address was previously advertised by another
+  neighbor, then only the most recently received mapping MUST be
+  maintained, and an error message SHOULD be logged to the
+  administrator in a rate-limited manner.
+*/
+static void delete_from_neigh_addr(struct interface *ifp,
+                                  struct list *addr_list,
+                                  struct in_addr neigh_addr)
+{
+  struct listnode      *addr_node;
+  struct prefix        *addr;
+  struct pim_interface *pim_ifp;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  zassert(addr_list);
+
+  /*
+    Scan secondary address list
+  */
+  for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node,
+                           addr)) {
+    struct listnode      *neigh_node;
+    struct pim_neighbor  *neigh;
+
+    if (addr->family != AF_INET)
+      continue;
+
+    /*
+      Scan neighbors
+    */
+    for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node,
+                             neigh)) {
+      {
+       struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4);
+       if (p) {
+         char addr_str[100];
+         char this_neigh_str[100];
+         char other_neigh_str[100];
+         
+         pim_inet4_dump("<addr?>", addr->u.prefix4, addr_str, sizeof(addr_str));
+         pim_inet4_dump("<neigh1?>", neigh_addr, this_neigh_str, sizeof(this_neigh_str));
+         pim_inet4_dump("<neigh2?>", neigh->source_addr, other_neigh_str, sizeof(other_neigh_str));
+         
+         zlog_info("secondary addr %s recvd from neigh %s deleted from neigh %s on %s",
+                   addr_str, this_neigh_str, other_neigh_str, ifp->name);
+         
+         listnode_delete(neigh->prefix_list, p);
+         prefix_free(p);
+       }
+      }
+
+    } /* scan neighbors */
+    
+  } /* scan addr list */
+
+}
+
+void pim_neighbor_update(struct pim_neighbor *neigh,
+                        pim_hello_options hello_options,
+                        uint16_t holdtime,
+                        uint32_t dr_priority,
+                        struct list *addr_list)
+{
+  struct pim_interface *pim_ifp = neigh->interface->info;
+
+  /* Received holdtime ? */
+  if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
+    pim_neighbor_timer_reset(neigh, holdtime);
+  }
+  else {
+    pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+  }
+
+#ifdef DUMP_PREFIX_LIST
+  zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d",
+            __PRETTY_FUNCTION__,
+            (unsigned) neigh->prefix_list,
+            neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1,
+            (unsigned) addr_list,
+            addr_list ? (int) listcount(addr_list) : -1);
+#endif
+
+  if (neigh->prefix_list == addr_list) {
+    if (addr_list) {
+      zlog_err("%s: internal error: trying to replace same prefix list=%p",
+              __PRETTY_FUNCTION__, (void *) addr_list);
+    }
+  }
+  else {
+    /* Delete existing secondary address list */
+    delete_prefix_list(neigh);
+  }
+
+  if (addr_list) {
+    delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr);
+  }
+
+  /* Replace secondary address list */
+  neigh->prefix_list = addr_list;
+
+  update_dr_priority(neigh,
+                    hello_options,
+                    dr_priority);
+  /*
+    Copy flags
+   */
+  neigh->hello_options = hello_options;
+}
diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h
new file mode 100644 (file)
index 0000000..5b2172d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_NEIGHBOR_H
+#define PIM_NEIGHBOR_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+
+#include "pim_tlv.h"
+
+struct pim_neighbor {
+  int64_t            creation; /* timestamp of creation */
+  struct in_addr     source_addr;
+  pim_hello_options  hello_options;
+  uint16_t           holdtime;
+  uint16_t           propagation_delay_msec;
+  uint16_t           override_interval_msec;
+  uint32_t           dr_priority;
+  uint32_t           generation_id;
+  struct list       *prefix_list; /* list of struct prefix */
+  struct thread     *t_expire_timer;
+  struct interface  *interface;
+};
+
+void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime);
+void pim_neighbor_free(struct pim_neighbor *neigh);
+struct pim_neighbor *pim_neighbor_find(struct interface *ifp,
+                                      struct in_addr source_addr);
+struct pim_neighbor *pim_neighbor_add(struct interface *ifp,
+                                     struct in_addr source_addr,
+                                     pim_hello_options hello_options,
+                                     uint16_t holdtime,
+                                     uint16_t propagation_delay,
+                                     uint16_t override_interval,
+                                     uint32_t dr_priority,
+                                     uint32_t generation_id,
+                                     struct list *addr_list);
+void pim_neighbor_delete(struct interface *ifp,
+                        struct pim_neighbor *neigh,
+                        const char *delete_message);
+void pim_neighbor_delete_all(struct interface *ifp,
+                            const char *delete_message);
+void pim_neighbor_update(struct pim_neighbor *neigh,
+                        pim_hello_options hello_options,
+                        uint16_t holdtime,
+                        uint32_t dr_priority,
+                        struct list *addr_list);
+struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh,
+                                          struct in_addr addr);
+int pim_if_dr_election(struct interface *ifp);
+
+#endif /* PIM_NEIGHBOR_H */
diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c
new file mode 100644 (file)
index 0000000..1aaece3
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_oil.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+
+void pim_channel_oil_free(struct channel_oil *c_oil)
+{
+  XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
+}
+
+static void pim_channel_oil_delete(struct channel_oil *c_oil)
+{
+  /*
+    notice that listnode_delete() can't be moved
+    into pim_channel_oil_free() because the later is
+    called by list_delete_all_node()
+  */
+  listnode_delete(qpim_channel_oil_list, c_oil);
+
+  pim_channel_oil_free(c_oil);
+}
+
+static struct channel_oil *channel_oil_new(struct in_addr group_addr,
+                                          struct in_addr source_addr,
+                                          int input_vif_index)
+{
+  struct channel_oil *c_oil;
+  struct interface *ifp_in;
+
+  ifp_in = pim_if_find_by_vif_index(input_vif_index);
+  if (!ifp_in) {
+    /* warning only */
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
+             __PRETTY_FUNCTION__,
+             source_str, group_str, input_vif_index);
+  }
+
+  c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
+  if (!c_oil) {
+    zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
+    return 0;
+  }
+
+  c_oil->oil.mfcc_mcastgrp = group_addr;
+  c_oil->oil.mfcc_origin   = source_addr;
+  c_oil->oil.mfcc_parent   = input_vif_index;
+  c_oil->oil_ref_count     = 1;
+
+  zassert(c_oil->oil_size == 0);
+
+  return c_oil;
+}
+
+static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
+                                              struct in_addr source_addr,
+                                              int input_vif_index)
+{
+  struct channel_oil *c_oil;
+
+  c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
+  if (!c_oil) {
+    zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
+    return 0;
+  }
+
+  listnode_add(qpim_channel_oil_list, c_oil);
+
+  return c_oil;
+}
+
+static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
+                                               struct in_addr source_addr)
+{
+  struct listnode    *node;
+  struct channel_oil *c_oil;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
+    if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
+       (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
+      return c_oil;
+  }
+  
+  return 0;
+}
+
+struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
+                                       struct in_addr source_addr,
+                                       int input_vif_index)
+{
+  struct channel_oil *c_oil;
+
+  c_oil = pim_find_channel_oil(group_addr, source_addr);
+  if (c_oil) {
+    ++c_oil->oil_ref_count;
+    return c_oil;
+  }
+
+  return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
+}
+
+void pim_channel_oil_del(struct channel_oil *c_oil)
+{
+  --c_oil->oil_ref_count;
+
+  if (c_oil->oil_ref_count < 1) {
+    pim_channel_oil_delete(c_oil);
+  }
+}
diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h
new file mode 100644 (file)
index 0000000..1753545
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_OIL_H
+#define PIM_OIL_H
+
+#include "pim_mroute.h"
+
+#define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) /* bitmask 1 */
+#define PIM_OIF_FLAG_PROTO_PIM  (1 << 1) /* bitmask 2 */
+#define PIM_OIF_FLAG_PROTO_ANY  (3)      /* bitmask (1 | 2) */
+
+/*
+  qpim_channel_oil_list holds a list of struct channel_oil.
+
+  Each channel_oil.oil is used to control an (S,G) entry in the Kernel
+  Multicast Forwarding Cache.
+*/
+
+struct channel_oil {
+  struct mfcctl oil;
+  int           oil_size;
+  int           oil_ref_count;
+  time_t        oif_creation[MAXVIFS];
+  uint32_t      oif_flags[MAXVIFS];
+};
+
+void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
+                                       struct in_addr source_addr,
+                                       int input_vif_index);
+void pim_channel_oil_del(struct channel_oil *c_oil);
+
+#endif /* PIM_OIL_H */
diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c
new file mode 100644 (file)
index 0000000..c52b0d3
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pim_util.h"
+#include "pim_tlv.h"
+#include "pim_neighbor.h"
+#include "pim_hello.h"
+#include "pim_join.h"
+#include "pim_assert.h"
+#include "pim_msg.h"
+
+static int on_pim_hello_send(struct thread *t);
+static int pim_hello_send(struct interface *ifp,
+                         uint16_t holdtime);
+
+static void sock_close(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp = ifp->info;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (pim_ifp->t_pim_sock_read) {
+      zlog_debug("Cancelling READ event for PIM socket fd=%d on interface %s",
+                pim_ifp->pim_sock_fd,
+                ifp->name);
+    }
+  }
+  THREAD_OFF(pim_ifp->t_pim_sock_read);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (pim_ifp->t_pim_hello_timer) {
+      zlog_debug("Cancelling PIM hello timer for interface %s",
+                ifp->name);
+    }
+  }
+  THREAD_OFF(pim_ifp->t_pim_hello_timer);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("Deleting PIM socket fd=%d on interface %s",
+              pim_ifp->pim_sock_fd, ifp->name);
+  }
+
+  if (close(pim_ifp->pim_sock_fd)) {
+    zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
+             pim_ifp->pim_sock_fd, ifp->name,
+             errno, safe_strerror(errno));
+  }
+  
+  pim_ifp->pim_sock_fd = -1;
+  pim_ifp->pim_sock_creation = 0;
+
+  zassert(pim_ifp->pim_sock_fd < 0);
+  zassert(!pim_ifp->t_pim_sock_read);
+  zassert(!pim_ifp->t_pim_hello_timer);
+  zassert(!pim_ifp->pim_sock_creation);
+}
+
+void pim_sock_delete(struct interface *ifp, const char *delete_message)
+{
+  zlog_info("PIM INTERFACE DOWN: on interface %s: %s",
+           ifp->name, delete_message);
+
+  if (!ifp->info) {
+    zlog_err("%s: %s: but PIM not enabled on interface %s (!)",
+            __PRETTY_FUNCTION__, delete_message, ifp->name);
+    return;
+  }
+
+  /*
+    RFC 4601: 4.3.1.  Sending Hello Messages
+    
+    Before an interface goes down or changes primary IP address, a Hello
+    message with a zero HoldTime should be sent immediately (with the
+    old IP address if the IP address changed).
+  */
+  pim_hello_send(ifp, 0 /* zero-sec holdtime */);
+
+  pim_neighbor_delete_all(ifp, delete_message);
+
+  sock_close(ifp);
+}
+
+int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len)
+{
+  struct ip *ip_hdr;
+  size_t ip_hlen; /* ip header length in bytes */
+  char src_str[100];
+  char dst_str[100];
+  uint8_t *pim_msg;
+  int pim_msg_len;
+  uint8_t pim_version;
+  uint8_t pim_type;
+  uint16_t pim_checksum; /* received checksum */
+  uint16_t checksum;     /* computed checksum */
+  struct pim_neighbor *neigh;
+
+  if (!ifp->info) {
+    zlog_warn("%s: PIM not enabled on interface %s",
+             __PRETTY_FUNCTION__, ifp->name);
+    return -1;
+  }
+    
+  if (len < sizeof(*ip_hdr)) {
+    zlog_warn("PIM packet size=%zu shorter than minimum=%zu",
+             len, sizeof(*ip_hdr));
+    return -1;
+  }
+
+  ip_hdr = (struct ip *) buf;
+
+  pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
+  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, dst_str, sizeof(dst_str));
+
+  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+
+  if (PIM_DEBUG_PIM_PACKETS) {
+    zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
+              src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p);
+  }
+
+  if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) {
+    zlog_warn("IP packet protocol=%d is not PIM=%d",
+             ip_hdr->ip_p, PIM_IP_PROTO_PIM);
+    return -1;
+  }
+
+  if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
+    zlog_warn("IP packet header size=%zu shorter than minimum=%d",
+             ip_hlen, PIM_IP_HEADER_MIN_LEN);
+    return -1;
+  }
+  if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
+    zlog_warn("IP packet header size=%zu greater than maximum=%d",
+             ip_hlen, PIM_IP_HEADER_MAX_LEN);
+    return -1;
+  }
+
+  pim_msg = buf + ip_hlen;
+  pim_msg_len = len - ip_hlen;
+
+  if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+    pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len);
+  }
+
+  if (pim_msg_len < PIM_PIM_MIN_LEN) {
+    zlog_warn("PIM message size=%d shorter than minimum=%d",
+             pim_msg_len, PIM_PIM_MIN_LEN);
+    return -1;
+  }
+
+  pim_version = PIM_MSG_HDR_GET_VERSION(pim_msg);
+  pim_type    = PIM_MSG_HDR_GET_TYPE(pim_msg);
+
+  if (pim_version != PIM_PROTO_VERSION) {
+    zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d",
+             ifp->name, pim_version);
+    return -1;
+  }
+
+  /* save received checksum */
+  pim_checksum = PIM_MSG_HDR_GET_CHECKSUM(pim_msg);
+
+  /* for computing checksum */
+  *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0;
+
+  checksum = in_cksum(pim_msg, pim_msg_len);
+  if (checksum != pim_checksum) {
+    zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
+             ifp->name, pim_checksum, checksum);
+    return -1;
+  }
+
+  if (PIM_DEBUG_PIM_PACKETS) {
+    zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x",
+              src_str, dst_str, ifp->name, ip_hdr->ip_ttl,
+              pim_version, pim_type, pim_msg_len, checksum);
+  }
+
+  if (pim_type == PIM_MSG_TYPE_REGISTER  ||
+      pim_type == PIM_MSG_TYPE_REG_STOP  ||
+      pim_type == PIM_MSG_TYPE_BOOTSTRAP ||
+      pim_type == PIM_MSG_TYPE_GRAFT     ||
+      pim_type == PIM_MSG_TYPE_GRAFT_ACK ||
+      pim_type == PIM_MSG_TYPE_CANDIDATE)
+    {
+      if (PIM_DEBUG_PIM_PACKETS) {
+       zlog_debug("Recv PIM packet type %d which is not currently understood",
+                  pim_type);
+      }
+      return -1;
+    }
+
+  if (pim_type == PIM_MSG_TYPE_HELLO) {
+    int result = pim_hello_recv(ifp,
+                 ip_hdr->ip_src,
+                 pim_msg + PIM_MSG_HEADER_LEN,
+                 pim_msg_len - PIM_MSG_HEADER_LEN);
+    return result;
+  }
+
+  neigh = pim_neighbor_find(ifp, ip_hdr->ip_src);
+  if (!neigh) {
+    zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             pim_type, src_str, ifp->name);
+    return -1;
+  }
+
+  switch (pim_type) {
+  case PIM_MSG_TYPE_JOIN_PRUNE:
+    return pim_joinprune_recv(ifp, neigh,
+                             ip_hdr->ip_src,
+                             pim_msg + PIM_MSG_HEADER_LEN,
+                             pim_msg_len - PIM_MSG_HEADER_LEN);
+  case PIM_MSG_TYPE_ASSERT:
+    return pim_assert_recv(ifp, neigh,
+                          ip_hdr->ip_src,
+                          pim_msg + PIM_MSG_HEADER_LEN,
+                          pim_msg_len - PIM_MSG_HEADER_LEN);
+  default:
+    zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             pim_type, src_str, ifp->name);
+  }
+
+  return -1;
+}
+
+static void pim_sock_read_on(struct interface *ifp);
+
+static int pim_sock_read(struct thread *t)
+{
+  struct interface *ifp;
+  struct pim_interface *pim_ifp;
+  int fd;
+  struct sockaddr_in from;
+  struct sockaddr_in to;
+  socklen_t fromlen = sizeof(from);
+  socklen_t tolen = sizeof(to);
+  uint8_t buf[PIM_PIM_BUFSIZE_READ];
+  int len;
+  ifindex_t ifindex = -1;
+  int result = -1; /* defaults to bad */
+
+  zassert(t);
+
+  ifp = THREAD_ARG(t);
+  zassert(ifp);
+
+  fd = THREAD_FD(t);
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  zassert(fd == pim_ifp->pim_sock_fd);
+
+  len = pim_socket_recvfromto(fd, buf, sizeof(buf),
+                             &from, &fromlen,
+                             &to, &tolen,
+                             &ifindex);
+  if (len < 0) {
+    zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    goto done;
+  }
+
+  if (PIM_DEBUG_PIM_PACKETS) {
+    char from_str[100];
+    char to_str[100];
+    
+    if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
+      sprintf(from_str, "<from?>");
+    if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
+      sprintf(to_str, "<to?>");
+    
+    zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
+              len, from_str, to_str, fd, ifindex, ifp->ifindex);
+  }
+
+  if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+    pim_pkt_dump(__PRETTY_FUNCTION__, buf, len);
+  }
+
+#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
+  /* ifindex sanity check */
+  if (ifindex != (int) ifp->ifindex) {
+    char from_str[100];
+    char to_str[100];
+    struct interface *recv_ifp;
+
+    if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
+      sprintf(from_str, "<from?>");
+    if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
+      sprintf(to_str, "<to?>");
+
+    recv_ifp = if_lookup_by_index(ifindex);
+    if (recv_ifp) {
+      zassert(ifindex == (int) recv_ifp->ifindex);
+    }
+
+#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
+    zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
+             from_str, to_str, fd,
+             ifindex, recv_ifp ? recv_ifp->name : "<if-notfound>",
+             ifp->ifindex, ifp->name);
+#endif
+    goto done;
+  }
+#endif
+
+  int fail = pim_pim_packet(ifp, buf, len);
+  if (fail) {
+    zlog_warn("%s: pim_pim_packet() return=%d",
+              __PRETTY_FUNCTION__, fail);
+    goto done;
+  }
+
+  result = 0; /* good */
+
+ done:
+  pim_sock_read_on(ifp);
+
+  if (result) {
+    ++pim_ifp->pim_ifstat_hello_recvfail;
+  }
+
+  return result;
+}
+
+static void pim_sock_read_on(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  zassert(ifp->info);
+
+  pim_ifp = ifp->info;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("Scheduling READ event on PIM socket fd=%d",
+              pim_ifp->pim_sock_fd);
+  }
+  pim_ifp->t_pim_sock_read = 0;
+  zassert(!pim_ifp->t_pim_sock_read);
+  THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp,
+                pim_ifp->pim_sock_fd);
+}
+
+static int pim_sock_open(struct in_addr ifaddr, ifindex_t ifindex)
+{
+  int fd;
+
+  fd = pim_socket_mcast(IPPROTO_PIM, ifaddr, 0 /* loop=false */);
+  if (fd < 0)
+    return -1;
+
+  if (pim_socket_join(fd, qpim_all_pim_routers_addr, ifaddr, ifindex)) {
+    close(fd);
+    return -2;
+  }
+
+  return fd;
+}
+
+void pim_ifstat_reset(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+  if (!pim_ifp) {
+    return;
+  }
+
+  pim_ifp->pim_ifstat_start          = pim_time_monotonic_sec();
+  pim_ifp->pim_ifstat_hello_sent     = 0;
+  pim_ifp->pim_ifstat_hello_sendfail = 0;
+  pim_ifp->pim_ifstat_hello_recv     = 0;
+  pim_ifp->pim_ifstat_hello_recvfail = 0;
+}
+
+void pim_sock_reset(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  zassert(ifp->info);
+
+  pim_ifp = ifp->info;
+
+  pim_ifp->primary_address = pim_find_primary_addr(ifp);
+
+  pim_ifp->pim_sock_fd       = -1;
+  pim_ifp->pim_sock_creation = 0;
+  pim_ifp->t_pim_sock_read   = 0;
+
+  pim_ifp->t_pim_hello_timer          = 0;
+  pim_ifp->pim_hello_period           = PIM_DEFAULT_HELLO_PERIOD;
+  pim_ifp->pim_default_holdtime       = -1; /* unset: means 3.5 * pim_hello_period */
+  pim_ifp->pim_triggered_hello_delay  = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
+  pim_ifp->pim_dr_priority            = PIM_DEFAULT_DR_PRIORITY;
+  pim_ifp->pim_propagation_delay_msec = PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
+  pim_ifp->pim_override_interval_msec = PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
+  if (PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION) {
+    PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
+  }
+  else {
+    PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options);
+  }
+
+  /* neighbors without lan_delay */
+  pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
+  pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
+  pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
+
+  /* DR Election */
+  pim_ifp->pim_dr_election_last          = 0; /* timestamp */
+  pim_ifp->pim_dr_election_count         = 0;
+  pim_ifp->pim_dr_election_changes       = 0;
+  pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */
+  pim_ifp->pim_dr_addr                   = pim_ifp->primary_address;
+
+  pim_ifstat_reset(ifp);
+}
+
+int pim_msg_send(int fd,
+                struct in_addr dst,
+                uint8_t *pim_msg,
+                int pim_msg_size,
+                const char *ifname)
+{
+  ssize_t            sent;
+  struct sockaddr_in to;
+  socklen_t          tolen;
+
+  if (PIM_DEBUG_PIM_PACKETS) {
+    char dst_str[100];
+    pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
+    zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x",
+              __PRETTY_FUNCTION__,
+              dst_str, ifname, pim_msg_size,
+              *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg));
+  }
+
+  memset(&to, 0, sizeof(to));
+  to.sin_family = AF_INET;
+  to.sin_addr = dst;
+  tolen = sizeof(to);
+
+  if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
+    pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size);
+  }
+
+  sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT,
+                (struct sockaddr *)&to, tolen);
+  if (sent != (ssize_t) pim_msg_size) {
+    int e = errno;
+    char dst_str[100];
+    pim_inet4_dump("<dst?>", dst, dst_str, sizeof(dst_str));
+    if (sent < 0) {
+      zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__,
+               dst_str, ifname, fd, pim_msg_size,
+               e, safe_strerror(e));
+    }
+    else {
+      zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd",
+               __PRETTY_FUNCTION__,
+               dst_str, ifname, fd,
+               pim_msg_size, sent);
+    }
+    return -1;
+  }
+
+  return 0;
+}
+
+static int hello_send(struct interface *ifp,
+                     uint16_t holdtime)
+{
+  uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
+  struct pim_interface *pim_ifp;
+  int pim_tlv_size;
+  int pim_msg_size;
+
+  pim_ifp = ifp->info;
+
+  if (PIM_DEBUG_PIM_HELLO) {
+    char dst_str[100];
+    pim_inet4_dump("<dst?>", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str));
+    zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
+              __PRETTY_FUNCTION__,
+              dst_str, ifp->name,
+              holdtime,
+              pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec,
+              PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
+              pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
+              listcount(ifp->connected));
+  }
+
+  pim_tlv_size = pim_hello_build_tlv(ifp->name,
+                                    pim_msg + PIM_PIM_MIN_LEN,
+                                    sizeof(pim_msg) - PIM_PIM_MIN_LEN,
+                                    holdtime,
+                                    pim_ifp->pim_dr_priority,
+                                    pim_ifp->pim_generation_id,
+                                    pim_ifp->pim_propagation_delay_msec,
+                                    pim_ifp->pim_override_interval_msec,
+                                    PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options),
+                                    ifp->connected);
+  if (pim_tlv_size < 0) {
+    return -1;
+  }
+
+  pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
+
+  zassert(pim_msg_size >= PIM_PIM_MIN_LEN);
+  zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
+
+  pim_msg_build_header(pim_msg, pim_msg_size,
+                      PIM_MSG_TYPE_HELLO);
+
+  if (pim_msg_send(pim_ifp->pim_sock_fd,
+                  qpim_all_pim_routers_addr,
+                  pim_msg,
+                  pim_msg_size,
+                  ifp->name)) {
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_debug("%s: could not send PIM message on interface %s",
+                __PRETTY_FUNCTION__, ifp->name);
+    }
+    return -2;
+  }
+
+  return 0;
+}
+
+static int pim_hello_send(struct interface *ifp,
+                         uint16_t holdtime)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (hello_send(ifp, holdtime)) {
+    ++pim_ifp->pim_ifstat_hello_sendfail;
+
+    if (PIM_DEBUG_PIM_HELLO) {
+      zlog_warn("Could not send PIM hello on interface %s",
+               ifp->name);
+    }
+    return -1;
+  }
+
+  ++pim_ifp->pim_ifstat_hello_sent;
+
+  return 0;
+}
+
+static void hello_resched(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (PIM_DEBUG_PIM_HELLO) {
+    zlog_debug("Rescheduling %d sec hello on interface %s",
+              pim_ifp->pim_hello_period, ifp->name);
+  }
+  THREAD_OFF(pim_ifp->t_pim_hello_timer);
+  THREAD_TIMER_ON(master, pim_ifp->t_pim_hello_timer,
+                 on_pim_hello_send,
+                 ifp, pim_ifp->pim_hello_period);
+}
+
+/*
+  Periodic hello timer
+ */
+static int on_pim_hello_send(struct thread *t)
+{
+  struct pim_interface *pim_ifp;
+  struct interface *ifp;
+
+  zassert(t);
+  ifp = THREAD_ARG(t);
+  zassert(ifp);
+
+  pim_ifp = ifp->info;
+
+  /*
+   * Schedule next hello
+   */
+  pim_ifp->t_pim_hello_timer = 0;
+  hello_resched(ifp);
+
+  /*
+   * Send hello
+   */
+  return pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+}
+
+/*
+  RFC 4601: 4.3.1.  Sending Hello Messages
+
+  Thus, if a router needs to send a Join/Prune or Assert message on an
+  interface on which it has not yet sent a Hello message with the
+  currently configured IP address, then it MUST immediately send the
+  relevant Hello message without waiting for the Hello Timer to
+  expire, followed by the Join/Prune or Assert message.
+ */
+void pim_hello_restart_now(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  /*
+   * Reset next hello timer
+   */
+  hello_resched(ifp);
+
+  /*
+   * Immediately send hello
+   */
+  pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+}
+
+/*
+  RFC 4601: 4.3.1.  Sending Hello Messages
+
+  To allow new or rebooting routers to learn of PIM neighbors quickly,
+  when a Hello message is received from a new neighbor, or a Hello
+  message with a new GenID is received from an existing neighbor, a
+  new Hello message should be sent on this interface after a
+  randomized delay between 0 and Triggered_Hello_Delay.
+ */
+void pim_hello_restart_triggered(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  int triggered_hello_delay_msec;
+  int random_msec;
+
+  zassert(ifp);
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay;
+
+  if (pim_ifp->t_pim_hello_timer) {
+    long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
+    if (remain_msec <= triggered_hello_delay_msec) {
+      /* Rescheduling hello would increase the delay, then it's faster
+        to just wait for the scheduled periodic hello. */
+      return;
+    }
+
+    THREAD_OFF(pim_ifp->t_pim_hello_timer);
+    pim_ifp->t_pim_hello_timer = 0;
+  }
+  zassert(!pim_ifp->t_pim_hello_timer);
+
+  random_msec = random() % (triggered_hello_delay_msec + 1);
+
+  if (PIM_DEBUG_PIM_HELLO) {
+    zlog_debug("Scheduling %d msec triggered hello on interface %s",
+              random_msec, ifp->name);
+  }
+
+  THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer,
+                      on_pim_hello_send,
+                      ifp, random_msec);
+}
+
+int pim_sock_add(struct interface *ifp)
+{
+  struct pim_interface *pim_ifp;
+  struct in_addr ifaddr;
+  uint32_t old_genid;
+
+  pim_ifp = ifp->info;
+  zassert(pim_ifp);
+
+  if (pim_ifp->pim_sock_fd >= 0) {
+    zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s",
+             pim_ifp->pim_sock_fd, ifp->name);
+    return -1;
+  }
+
+  ifaddr = pim_ifp->primary_address;
+
+  pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex);
+  if (pim_ifp->pim_sock_fd < 0) {
+    zlog_warn("Could not open PIM socket on interface %s",
+             ifp->name);
+    return -2;
+  }
+
+  pim_ifp->t_pim_sock_read   = 0;
+  pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
+
+  /*
+   * Just ensure that the new generation id
+   * actually chooses something different.
+   * Actually ran across a case where this
+   * happened, pre-switch to random().
+   * While this is unlikely to happen now
+   * let's make sure it doesn't.
+   */
+  old_genid = pim_ifp->pim_generation_id;
+
+  while (old_genid == pim_ifp->pim_generation_id)
+    pim_ifp->pim_generation_id = random();
+
+  zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d",
+           ifp->name, ifp->ifindex);
+
+  /*
+   * Start receiving PIM messages
+   */
+  pim_sock_read_on(ifp);
+
+  /*
+   * Start sending PIM hello's
+   */
+  pim_hello_restart_triggered(ifp);
+
+  return 0;
+}
diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h
new file mode 100644 (file)
index 0000000..4b378fb
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_PIM_H
+#define PIM_PIM_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#define PIM_PIM_BUFSIZE_READ  (20000)
+#define PIM_PIM_BUFSIZE_WRITE (20000)
+
+#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20)
+
+#define PIM_DEFAULT_HELLO_PERIOD                 (30)   /* seconds, RFC 4601: 4.11 */
+#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY        (5)    /* seconds, RFC 4601: 4.11 */
+#define PIM_DEFAULT_DR_PRIORITY                  (1)    /* RFC 4601: 4.3.1 */
+#define PIM_DEFAULT_PROPAGATION_DELAY_MSEC       (500)  /* RFC 4601: 4.11.  Timer Values */
+#define PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC       (2500) /* RFC 4601: 4.11.  Timer Values */
+#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0)    /* boolean */
+#define PIM_DEFAULT_T_PERIODIC                   (60)   /* RFC 4601: 4.11.  Timer Values */
+
+#define PIM_MSG_TYPE_HELLO      (0)
+#define PIM_MSG_TYPE_REGISTER   (1)
+#define PIM_MSG_TYPE_REG_STOP   (2)
+#define PIM_MSG_TYPE_JOIN_PRUNE (3)
+#define PIM_MSG_TYPE_BOOTSTRAP  (4)
+#define PIM_MSG_TYPE_ASSERT     (5)
+#define PIM_MSG_TYPE_GRAFT      (6)
+#define PIM_MSG_TYPE_GRAFT_ACK  (7)
+#define PIM_MSG_TYPE_CANDIDATE  (8)
+
+#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg)
+#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg)
+#define PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) (((char *)(pim_msg)) + 1)
+#define PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) (((char *)(pim_msg)) + 2)
+
+#define PIM_MSG_HDR_GET_VERSION(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_VERSION(pim_msg)) >> 4)
+#define PIM_MSG_HDR_GET_TYPE(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_TYPE(pim_msg)) & 0xF)
+#define PIM_MSG_HDR_GET_CHECKSUM(pim_msg) (*(uint16_t*) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg))
+
+void pim_ifstat_reset(struct interface *ifp);
+void pim_sock_reset(struct interface *ifp);
+int pim_sock_add(struct interface *ifp);
+void pim_sock_delete(struct interface *ifp, const char *delete_message);
+void pim_hello_restart_now(struct interface *ifp);
+void pim_hello_restart_triggered(struct interface *ifp);
+
+int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len);
+
+int pim_msg_send(int fd,
+                struct in_addr dst,
+                uint8_t *pim_msg,
+                int pim_msg_size,
+                const char *ifname);
+
+#endif /* PIM_PIM_H */
diff --git a/pimd/pim_routemap.c b/pimd/pim_routemap.c
new file mode 100644 (file)
index 0000000..9cc13be
--- /dev/null
@@ -0,0 +1,32 @@
+/* PIM Route-map Code
+ * Copyright (C) 2016 Cumulus Networks <sharpd@cumulusnetworks.com>
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "routemap.h"
+
+#include "pimd.h"
+
+void
+pim_route_map_init (void)
+{
+  route_map_init ();
+  route_map_init_vty ();
+}
diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c
new file mode 100644 (file)
index 0000000..e7619a5
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+
+#include "pimd.h"
+#include "pim_rpf.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_iface.h"
+#include "pim_zlookup.h"
+#include "pim_ifchannel.h"
+
+static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up);
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop,
+                      struct in_addr addr)
+{
+  struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+  int num_ifindex;
+  struct interface *ifp;
+  int first_ifindex;
+
+  num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
+                                      PIM_NEXTHOP_IFINDEX_TAB_SIZE,
+                                      addr, PIM_NEXTHOP_LOOKUP_MAX);
+  if (num_ifindex < 1) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             addr_str);
+    return -1;
+  }
+
+  first_ifindex = nexthop_tab[0].ifindex;
+
+  if (num_ifindex > 1) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+             __FILE__, __PRETTY_FUNCTION__,
+             num_ifindex, addr_str, first_ifindex);
+    /* debug warning only, do not return */
+  }
+
+  ifp = if_lookup_by_index(first_ifindex);
+  if (!ifp) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: could not find interface for ifindex %d (address %s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             first_ifindex, addr_str);
+    return -2;
+  }
+
+  if (!ifp->info) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
+             __PRETTY_FUNCTION__,
+             ifp->name, first_ifindex, addr_str);
+    /* debug warning only, do not return */
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char nexthop_str[100];
+    char addr_str[100];
+    pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
+              __FILE__, __PRETTY_FUNCTION__,
+              nexthop_str, addr_str,
+              ifp->name, first_ifindex,
+              nexthop_tab[0].route_metric,
+              nexthop_tab[0].protocol_distance);
+  }
+
+  /* update nextop data */
+  nexthop->interface              = ifp;
+  nexthop->mrib_nexthop_addr      = nexthop_tab[0].nexthop_addr;
+  nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance;
+  nexthop->mrib_route_metric      = nexthop_tab[0].route_metric;
+
+  return 0;
+}
+
+static int nexthop_mismatch(const struct pim_nexthop *nh1,
+                           const struct pim_nexthop *nh2)
+{
+  return (nh1->interface != nh2->interface) 
+    ||
+    (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr)
+    ||
+    (nh1->mrib_metric_preference != nh2->mrib_metric_preference)
+    ||
+    (nh1->mrib_route_metric != nh2->mrib_route_metric);
+}
+
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
+                                  struct pim_rpf *old_rpf)
+{
+  struct pim_nexthop  save_nexthop;
+  struct pim_rpf         save_rpf;
+  struct pim_rpf     *rpf = &up->rpf;
+
+  save_nexthop  = rpf->source_nexthop; /* detect change in pim_nexthop */
+  save_rpf = up->rpf;
+
+  if (pim_nexthop_lookup(&rpf->source_nexthop,
+                        up->source_addr)) {
+    return PIM_RPF_FAILURE;
+  }
+
+  rpf->rpf_addr = pim_rpf_find_rpf_addr(up);
+  if (PIM_INADDR_IS_ANY(rpf->rpf_addr) && PIM_DEBUG_PIM_EVENTS) {
+    /* RPF'(S,G) not found */
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s %s: RPF'(%s,%s) not found: won't send join upstream",
+              __FILE__, __PRETTY_FUNCTION__,
+              src_str, grp_str);
+    /* warning only */
+  }
+
+  /* detect change in pim_nexthop */
+  if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) {
+
+    if (PIM_DEBUG_PIM_EVENTS) {
+      char src_str[100];
+      char grp_str[100];
+      char nhaddr_str[100];
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str));
+      zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+                __FILE__, __PRETTY_FUNCTION__,
+                src_str, grp_str,
+                rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
+                nhaddr_str,
+                rpf->source_nexthop.mrib_metric_preference,
+                rpf->source_nexthop.mrib_route_metric);
+      /* warning only */
+    }
+
+    pim_upstream_update_join_desired(up);
+    pim_upstream_update_could_assert(up);
+    pim_upstream_update_my_assert_metric(up);
+  }
+
+  /* detect change in RPF_interface(S) */
+  if (save_nexthop.interface != rpf->source_nexthop.interface) {
+
+    if (PIM_DEBUG_PIM_EVENTS) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s",
+                __FILE__, __PRETTY_FUNCTION__,
+                src_str, grp_str,
+                save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>",
+                rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
+      /* warning only */
+    }
+
+    pim_upstream_rpf_interface_changed(up, save_nexthop.interface);
+  }
+
+  /* detect change in RPF'(S,G) */
+  if (save_rpf.rpf_addr.s_addr != rpf->rpf_addr.s_addr) {
+
+    /* return old rpf to caller ? */
+    if (old_rpf)
+      *old_rpf = save_rpf;
+
+    return PIM_RPF_CHANGED;
+  }
+
+  return PIM_RPF_OK;
+}
+
+/*
+  RFC 4601: 4.1.6.  State Summarization Macros
+
+     neighbor RPF'(S,G) {
+         if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
+              return AssertWinner(S, G, RPF_interface(S) )
+         } else {
+              return NBR( RPF_interface(S), MRIB.next_hop( S ) )
+         }
+     }
+
+  RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
+  packets should be coming and to which joins should be sent on the RP
+  tree and SPT, respectively.
+*/
+static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up)
+{
+  struct pim_ifchannel *rpf_ch;
+  struct pim_neighbor *neigh;
+  struct in_addr rpf_addr;
+
+  if (!up->rpf.source_nexthop.interface) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)",
+             __PRETTY_FUNCTION__,
+             src_str, grp_str);
+
+    rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+    return rpf_addr;
+  }
+
+  rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface,
+                             up->source_addr, up->group_addr);
+  if (rpf_ch) {
+    if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+      return rpf_ch->ifassert_winner;
+    }
+  }
+
+  /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
+
+  neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface,
+                              up->rpf.source_nexthop.mrib_nexthop_addr);
+  if (neigh)
+    rpf_addr = neigh->source_addr;
+  else
+    rpf_addr.s_addr = PIM_NET_INADDR_ANY;
+
+  return rpf_addr;
+}
diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h
new file mode 100644 (file)
index 0000000..9a48ea0
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_RPF_H
+#define PIM_RPF_H
+
+#include <zebra.h>
+
+#include "pim_upstream.h"
+#include "pim_neighbor.h"
+
+int pim_nexthop_lookup(struct pim_nexthop *nexthop,
+                      struct in_addr addr);
+enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
+                                  struct pim_rpf *old_rpf);
+
+#endif /* PIM_RPF_H */
diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c
new file mode 100644 (file)
index 0000000..3549331
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <signal.h>
+
+#include "sigevent.h"
+#include "memory.h"
+#include "log.h"
+
+#include "pim_signals.h"
+#include "pimd.h"
+
+/*
+ * Signal handlers
+ */
+
+static void pim_sighup()
+{
+  zlog_info ("SIGHUP received, ignoring");
+}
+
+static void pim_sigint()
+{
+  zlog_notice("Terminating on signal SIGINT");
+  pim_terminate();
+  exit(1);
+}
+
+static void pim_sigterm()
+{
+  zlog_notice("Terminating on signal SIGTERM");
+  pim_terminate();
+  exit(1);
+}
+
+static void pim_sigusr1()
+{
+  zlog_info ("SIGUSR1 received");
+  zlog_rotate (NULL);
+}
+
+static struct quagga_signal_t pimd_signals[] =
+{
+  {
+   .signal = SIGHUP,
+   .handler = &pim_sighup,
+   },
+  {
+   .signal = SIGUSR1,
+   .handler = &pim_sigusr1,
+   },
+  {
+   .signal = SIGINT,
+   .handler = &pim_sigint,
+   },
+  {
+   .signal = SIGTERM,
+   .handler = &pim_sigterm,
+   },
+};
+
+void pim_signals_init()
+{
+  signal_init(master, array_size(pimd_signals), pimd_signals);
+}
+
diff --git a/pimd/pim_signals.h b/pimd/pim_signals.h
new file mode 100644 (file)
index 0000000..62523c0
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SIGNALS_H
+#define PIM_SIGNALS_H
+
+void pim_signals_init(void);
+
+#endif /* PIM_SIGNALS_H */
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
new file mode 100644 (file)
index 0000000..ceae4f2
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "pim_mroute.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/igmp.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "log.h"
+#include "privs.h"
+
+#include "pimd.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pim_igmp_join.h"
+
+/* GLOBAL VARS */
+extern struct zebra_privs_t pimd_privs;
+
+int pim_socket_raw(int protocol)
+{
+  int fd;
+
+  if ( pimd_privs.change (ZPRIVS_RAISE) )
+       zlog_err ("pim_sockek_raw: could not raise privs, %s",
+                 safe_strerror (errno) );
+
+  fd = socket(AF_INET, SOCK_RAW, protocol);
+
+  if ( pimd_privs.change (ZPRIVS_LOWER) )
+       zlog_err ("pim_socket_raw: could not lower privs, %s",
+                 safe_strerror (errno) );
+
+  if (fd < 0) {
+    zlog_warn("Could not create raw socket: errno=%d: %s",
+             errno, safe_strerror(errno));
+    return PIM_SOCK_ERR_SOCKET;
+  }
+  
+  return fd;
+}
+
+int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop)
+{
+  int fd;
+
+  fd = pim_socket_raw(protocol);
+  if (fd < 0) {
+    zlog_warn("Could not create multicast socket: errno=%d: %s",
+             errno, safe_strerror(errno));
+    return PIM_SOCK_ERR_SOCKET;
+  }
+
+  /* Needed to obtain destination address from recvmsg() */
+  {
+#if defined(HAVE_IP_PKTINFO)
+    /* Linux and Solaris IP_PKTINFO */
+    int opt = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
+      zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+    }
+#elif defined(HAVE_IP_RECVDSTADDR)
+    /* BSD IP_RECVDSTADDR */
+    int opt = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
+      zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+    }
+#else
+    zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
+            __FILE__, __PRETTY_FUNCTION__);
+    close(fd);
+    return PIM_SOCK_ERR_DSTADDR;
+#endif
+  }
+
+  
+  /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
+  if (protocol == IPPROTO_IGMP) {
+    char ra[4];
+    ra[0] = 148;
+    ra[1] = 4;
+    ra[2] = 0;
+    ra[3] = 0;
+    if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
+      zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_RA;
+    }
+  }
+
+  {
+    int reuse = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+                  (void *) &reuse, sizeof(reuse))) {
+      zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_REUSE;
+    }
+  }
+
+  {
+    const int MTTL = 1;
+    int ttl = MTTL;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+                  (void *) &ttl, sizeof(ttl))) {
+      zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
+               MTTL, fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_TTL;
+    }
+  }
+
+  if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+                (void *) &loop, sizeof(loop))) {
+    zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+             loop ? "enable" : "disable",
+             fd, errno, safe_strerror(errno));
+    close(fd);
+    return PIM_SOCK_ERR_LOOP;
+  }
+
+  if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+                (void *) &ifaddr, sizeof(ifaddr))) {
+    zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    close(fd);
+    return PIM_SOCK_ERR_IFACE;
+  }
+
+  {
+    long flags;
+
+    flags = fcntl(fd, F_GETFL, 0);
+    if (flags < 0) {
+      zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_NONBLOCK_GETFL;
+    }
+
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+      zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_NONBLOCK_SETFL;
+    }
+  }
+
+  return fd;
+}
+
+int pim_socket_join(int fd, struct in_addr group,
+                   struct in_addr ifaddr, ifindex_t ifindex)
+{
+  int ret;
+
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+  struct ip_mreqn opt;
+#else
+  struct ip_mreq opt;
+#endif
+
+  opt.imr_multiaddr = group;
+
+#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+  opt.imr_address   = ifaddr;
+  opt.imr_ifindex   = ifindex;
+#else
+  opt.imr_interface = ifaddr;
+#endif
+
+  ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
+  if (ret) {
+    char group_str[100];
+    char ifaddr_str[100];
+    if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
+      sprintf(group_str, "<group?>");
+    if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
+      sprintf(ifaddr_str, "<ifaddr?>");
+
+    zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
+            fd, group_str, ifaddr_str, errno, safe_strerror(errno));
+    return ret;
+  }
+
+  if (PIM_DEBUG_TRACE) {
+    char group_str[100];
+    char ifaddr_str[100];
+    if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
+      sprintf(group_str, "<group?>");
+    if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
+      sprintf(ifaddr_str, "<ifaddr?>");
+
+    zlog_debug("Socket fd=%d joined group %s on interface address %s",
+              fd, group_str, ifaddr_str);
+  }
+
+  return ret;
+}
+
+int pim_socket_join_source(int fd, ifindex_t ifindex,
+                          struct in_addr group_addr,
+                          struct in_addr source_addr,
+                          const char *ifname)
+{
+  if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
+    int e = errno;
+    char group_str[100];
+    char source_str[100];
+    pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
+             __PRETTY_FUNCTION__,
+             fd, group_str, source_str, ifindex, ifname,
+             e, safe_strerror(e));
+    return -1;
+  }
+
+  return 0;
+}
+
+int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
+                         struct sockaddr_in *from, socklen_t *fromlen,
+                         struct sockaddr_in *to, socklen_t *tolen,
+                         ifindex_t *ifindex)
+{
+  struct msghdr msgh;
+  struct cmsghdr *cmsg;
+  struct iovec iov;
+  char cbuf[1000];
+  int err;
+
+  /*
+   * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
+   * Use getsockname() to get sin_port.
+   */
+  if (to) {
+    struct sockaddr_in si;
+    socklen_t si_len = sizeof(si);
+    
+    ((struct sockaddr_in *) to)->sin_family = AF_INET;
+
+    if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
+      ((struct sockaddr_in *) to)->sin_port        = ntohs(0);
+      ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
+    }
+    else {
+      ((struct sockaddr_in *) to)->sin_port = si.sin_port;
+      ((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
+    }
+
+    if (tolen) 
+      *tolen = sizeof(si);
+  }
+
+  memset(&msgh, 0, sizeof(struct msghdr));
+  iov.iov_base = buf;
+  iov.iov_len  = len;
+  msgh.msg_control = cbuf;
+  msgh.msg_controllen = sizeof(cbuf);
+  msgh.msg_name = from;
+  msgh.msg_namelen = fromlen ? *fromlen : 0;
+  msgh.msg_iov  = &iov;
+  msgh.msg_iovlen = 1;
+  msgh.msg_flags = 0;
+
+  err = recvmsg(fd, &msgh, 0);
+  if (err < 0)
+    return err;
+
+  if (fromlen)
+    *fromlen = msgh.msg_namelen;
+
+  for (cmsg = CMSG_FIRSTHDR(&msgh);
+       cmsg != NULL;
+       cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+
+#ifdef HAVE_IP_PKTINFO
+    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
+      struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
+      if (to)
+       ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr;
+      if (tolen)
+       *tolen = sizeof(struct sockaddr_in);
+      if (ifindex)
+       *ifindex = i->ipi_ifindex;
+
+      if (to && PIM_DEBUG_PACKETS) {
+       char to_str[100];
+       pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+       zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
+                  __PRETTY_FUNCTION__,
+                  to_str, ntohs(to->sin_port));
+      }
+
+      break;
+    }
+#endif
+
+#ifdef HAVE_IP_RECVDSTADDR
+    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
+      struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
+      if (to)
+       ((struct sockaddr_in *) to)->sin_addr = *i;
+      if (tolen)
+       *tolen = sizeof(struct sockaddr_in);
+
+      if (to && PIM_DEBUG_PACKETS) {
+       char to_str[100];
+       pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+       zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
+                  __PRETTY_FUNCTION__,
+                  to_str, ntohs(to->sin_port));
+      }
+
+      break;
+    }
+#endif
+
+#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
+      if (cmsg->cmsg_type == IP_RECVIF)
+       if (ifindex)
+         *ifindex = CMSG_IFINDEX(cmsg);
+#endif
+
+  } /* for (cmsg) */
+
+  return err; /* len */
+}
+
+int pim_socket_mcastloop_get(int fd)
+{
+  int loop;
+  socklen_t loop_len = sizeof(loop);
+  
+  if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+                &loop, &loop_len)) {
+    int e = errno;
+    zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    errno = e;
+    return PIM_SOCK_ERR_LOOP;
+  }
+  
+  return loop;
+}
+
+int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
+{
+  if (getsockname(fd, name, namelen)) {
+    int e = errno;
+    zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
+             fd, errno, safe_strerror(errno));
+    errno = e;
+    return PIM_SOCK_ERR_NAME;
+  }
+
+  return PIM_SOCK_ERR_NONE;
+}
diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h
new file mode 100644 (file)
index 0000000..622fb47
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SOCK_H
+#define PIM_SOCK_H
+
+#include <netinet/in.h>
+
+#define PIM_SOCK_ERR_NONE    (0)  /* No error */
+#define PIM_SOCK_ERR_SOCKET  (-1) /* socket() */
+#define PIM_SOCK_ERR_RA      (-2) /* Router Alert option */
+#define PIM_SOCK_ERR_REUSE   (-3) /* Reuse option */
+#define PIM_SOCK_ERR_TTL     (-4) /* TTL option */
+#define PIM_SOCK_ERR_LOOP    (-5) /* Loopback option */
+#define PIM_SOCK_ERR_IFACE   (-6) /* Outgoing interface option */
+#define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */
+#define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */
+#define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */
+#define PIM_SOCK_ERR_NAME    (-10) /* Socket name (getsockname) */
+
+int pim_socket_raw(int protocol);
+int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop);
+int pim_socket_join(int fd, struct in_addr group,
+                   struct in_addr ifaddr, ifindex_t ifindex);
+int pim_socket_join_source(int fd, ifindex_t ifindex,
+                          struct in_addr group_addr,
+                          struct in_addr source_addr,
+                          const char *ifname);
+int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
+                         struct sockaddr_in *from, socklen_t *fromlen,
+                         struct sockaddr_in *to, socklen_t *tolen,
+                         ifindex_t *ifindex);
+
+int pim_socket_mcastloop_get(int fd);
+
+int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen);
+
+#endif /* PIM_SOCK_H */
diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c
new file mode 100644 (file)
index 0000000..fe88eba
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "memory.h"
+
+#include "pim_ssmpingd.h"
+#include "pim_time.h"
+#include "pim_sock.h"
+#include "pim_str.h"
+#include "pimd.h"
+
+static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
+
+enum {
+  PIM_SSMPINGD_REQUEST = 'Q',
+  PIM_SSMPINGD_REPLY   = 'A'
+};
+
+static void ssmpingd_read_on(struct ssmpingd_sock *ss);
+
+void pim_ssmpingd_init()
+{
+  int result;
+
+  zassert(!qpim_ssmpingd_list);
+
+  result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
+  
+  zassert(result > 0);
+}
+
+void pim_ssmpingd_destroy()
+{
+  if (qpim_ssmpingd_list) {
+    list_free(qpim_ssmpingd_list);
+    qpim_ssmpingd_list = 0;
+  }
+}
+
+static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
+{
+  struct listnode      *node;
+  struct ssmpingd_sock *ss;
+
+  if (!qpim_ssmpingd_list)
+    return 0;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
+    if (source_addr.s_addr == ss->source_addr.s_addr)
+      return ss;
+
+  return 0;
+}
+
+static void ssmpingd_free(struct ssmpingd_sock *ss)
+{
+  XFREE(MTYPE_PIM_SSMPINGD, ss);
+}
+
+static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
+{
+  struct sockaddr_in sockaddr;
+  int fd;
+
+  fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (fd < 0) {
+    zlog_err("%s: could not create socket: errno=%d: %s",
+            __PRETTY_FUNCTION__, errno, safe_strerror(errno));
+    return -1;
+  }
+
+  sockaddr.sin_family = AF_INET;
+  sockaddr.sin_addr   = addr;
+  sockaddr.sin_port   = htons(port);
+
+  if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
+             __PRETTY_FUNCTION__,
+             fd, addr_str, port, sizeof(sockaddr),
+             errno, safe_strerror(errno));
+    close(fd);
+    return -1;
+  }
+
+  /* Needed to obtain destination address from recvmsg() */
+  {
+#if defined(HAVE_IP_PKTINFO)
+    /* Linux and Solaris IP_PKTINFO */
+    int opt = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
+      zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+    }
+#elif defined(HAVE_IP_RECVDSTADDR)
+    /* BSD IP_RECVDSTADDR */
+    int opt = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
+      zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+    }
+#else
+    zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
+            __FILE__, __PRETTY_FUNCTION__);
+    close(fd);
+    return -1;
+#endif
+  }
+  
+  {
+    int reuse = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+                  (void *) &reuse, sizeof(reuse))) {
+      zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+      close(fd);
+      return -1;
+    }
+  }
+
+  if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+                (void *) &mttl, sizeof(mttl))) {
+    zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
+             __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
+    close(fd);
+    return -1;
+  }
+
+  {
+    int loop = 0;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+                  (void *) &loop, sizeof(loop))) {
+      zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__,
+               loop ? "enable" : "disable",
+               fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_LOOP;
+    }
+  }
+
+  if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+                (void *) &addr, sizeof(addr))) {
+    zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+             __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+    close(fd);
+    return -1;
+  }
+
+  {
+    long flags;
+
+    flags = fcntl(fd, F_GETFL, 0);
+    if (flags < 0) {
+      zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+      close(fd);
+      return -1;
+    }
+
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+      zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
+      close(fd);
+      return -1;
+    }
+  }
+
+  return fd;
+}
+
+static void ssmpingd_delete(struct ssmpingd_sock *ss)
+{
+  zassert(ss);
+  zassert(qpim_ssmpingd_list);
+
+  THREAD_OFF(ss->t_sock_read);
+
+  if (close(ss->sock_fd)) {
+    int e = errno;
+    char source_str[100];
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
+             __PRETTY_FUNCTION__,
+             ss->sock_fd, source_str, e, safe_strerror(e));
+    /* warning only */
+  }
+
+  listnode_delete(qpim_ssmpingd_list, ss);
+  ssmpingd_free(ss);
+}
+
+static void ssmpingd_sendto(struct ssmpingd_sock *ss,
+                           const uint8_t *buf,
+                           int len,
+                           struct sockaddr_in to)
+{
+  socklen_t tolen = sizeof(to);
+  int sent;
+
+  sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
+                (struct sockaddr *)&to, tolen);
+  if (sent != len) {
+    int e = errno;
+    char to_str[100];
+    pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+    if (sent < 0) {
+      zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
+               __PRETTY_FUNCTION__,
+               to_str, ntohs(to.sin_port), ss->sock_fd, len,
+               e, safe_strerror(e));
+    }
+    else {
+      zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
+               __PRETTY_FUNCTION__,
+               to_str, ntohs(to.sin_port), ss->sock_fd,
+               len, sent);
+    }
+  }
+}
+
+static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
+{
+  struct interface *ifp;
+  struct sockaddr_in from;
+  struct sockaddr_in to;
+  socklen_t fromlen = sizeof(from);
+  socklen_t tolen = sizeof(to);
+  ifindex_t ifindex = -1;
+  uint8_t buf[1000];
+  int len;
+
+  ++ss->requests;
+
+  len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
+                             &from, &fromlen,
+                             &to, &tolen,
+                             &ifindex);
+  if (len < 0) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
+             __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
+    return -1;
+  }
+
+  ifp = if_lookup_by_index(ifindex);
+
+  if (buf[0] != PIM_SSMPINGD_REQUEST) {
+    char source_str[100];
+    char from_str[100];
+    char to_str[100];
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
+    pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+    zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
+             __PRETTY_FUNCTION__,
+             buf[0],
+             from_str, ntohs(from.sin_port),
+             to_str, ntohs(to.sin_port),
+             ifp ? ifp->name : "<iface?>",
+             ifindex, ss->sock_fd,
+             source_str);
+    return 0;
+  }
+
+  if (PIM_DEBUG_SSMPINGD) {
+    char source_str[100];
+    char from_str[100];
+    char to_str[100];
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
+    pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+    zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
+              __PRETTY_FUNCTION__,
+              from_str, ntohs(from.sin_port),
+              to_str, ntohs(to.sin_port),
+              ifp ? ifp->name : "<iface?>",
+              ifindex, ss->sock_fd,
+              source_str);
+  }
+
+  buf[0] = PIM_SSMPINGD_REPLY;
+
+  /* unicast reply */
+  ssmpingd_sendto(ss, buf, len, from);
+
+  /* multicast reply */
+  from.sin_addr = qpim_ssmpingd_group_addr;
+  ssmpingd_sendto(ss, buf, len, from);
+
+  return 0;
+}
+
+static int ssmpingd_sock_read(struct thread *t)
+{
+  struct ssmpingd_sock *ss;
+  int sock_fd;
+  int result;
+
+  zassert(t);
+
+  ss = THREAD_ARG(t);
+  zassert(ss);
+
+  sock_fd = THREAD_FD(t);
+  zassert(sock_fd == ss->sock_fd);
+
+  result = ssmpingd_read_msg(ss);
+
+  /* Keep reading */
+  ss->t_sock_read = 0;
+  ssmpingd_read_on(ss);
+
+  return result;
+}
+
+static void ssmpingd_read_on(struct ssmpingd_sock *ss)
+{
+  zassert(!ss->t_sock_read);
+  THREAD_READ_ON(master, ss->t_sock_read,
+                ssmpingd_sock_read, ss, ss->sock_fd);
+}
+
+static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
+{
+  struct ssmpingd_sock *ss;
+  int sock_fd;
+
+  if (!qpim_ssmpingd_list) {
+    qpim_ssmpingd_list = list_new();
+    if (!qpim_ssmpingd_list) {
+      zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
+              __FILE__, __PRETTY_FUNCTION__);
+      return 0;
+    }
+    qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
+  }
+
+  sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
+  if (sock_fd < 0) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: ssmpingd_socket() failure for source %s",
+             __PRETTY_FUNCTION__, source_str);
+    return 0;
+  }
+
+  ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
+  if (!ss) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
+            __PRETTY_FUNCTION__,
+            sizeof(*ss), source_str);
+    close(sock_fd);
+    return 0;
+  }
+
+  ss->sock_fd     = sock_fd;
+  ss->t_sock_read = 0;
+  ss->source_addr = source_addr;
+  ss->creation    = pim_time_monotonic_sec();
+  ss->requests    = 0;
+
+  listnode_add(qpim_ssmpingd_list, ss);
+
+  ssmpingd_read_on(ss);
+
+  return ss;
+}
+
+int pim_ssmpingd_start(struct in_addr source_addr)
+{
+  struct ssmpingd_sock *ss;
+
+  ss = ssmpingd_find(source_addr);
+  if (ss) {
+    /* silently ignore request to recreate entry */
+    return 0;
+  }
+
+  {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_info("%s: starting ssmpingd for source %s",
+             __PRETTY_FUNCTION__, source_str);
+  }
+
+  ss = ssmpingd_new(source_addr);
+  if (!ss) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: ssmpingd_new() failure for source %s",
+             __PRETTY_FUNCTION__, source_str);
+    return -1;
+  }
+
+  return 0;
+}
+
+int pim_ssmpingd_stop(struct in_addr source_addr)
+{
+  struct ssmpingd_sock *ss;
+
+  ss = ssmpingd_find(source_addr);
+  if (!ss) {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_warn("%s: could not find ssmpingd for source %s",
+             __PRETTY_FUNCTION__, source_str);
+    return -1;
+  }
+
+  {
+    char source_str[100];
+    pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+    zlog_info("%s: stopping ssmpingd for source %s",
+             __PRETTY_FUNCTION__, source_str);
+  }
+
+  ssmpingd_delete(ss);
+
+  return 0;
+}
diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h
new file mode 100644 (file)
index 0000000..4bef20b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_SSMPINGD_H
+#define PIM_SSMPINGD_H
+
+#include <zebra.h>
+
+#include "if.h"
+
+#include "pim_iface.h"
+
+struct ssmpingd_sock {
+  int            sock_fd;     /* socket */
+  struct thread *t_sock_read; /* thread for reading socket */
+  struct in_addr source_addr; /* source address */
+  int64_t        creation;    /* timestamp of socket creation */
+  int64_t        requests;    /* counter */
+};
+
+void pim_ssmpingd_init(void);
+void pim_ssmpingd_destroy(void);
+int pim_ssmpingd_start(struct in_addr source_addr);
+int pim_ssmpingd_stop(struct in_addr source_addr);
+
+#endif /* PIM_SSMPINGD_H */
diff --git a/pimd/pim_static.c b/pimd/pim_static.c
new file mode 100644 (file)
index 0000000..078beab
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+  PIM for Quagga: add the ability to configure multicast static routes
+  Copyright (C) 2014  Nathan Bahr, ATCorp
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "vty.h"
+
+#include "pim_static.h"
+#include "pim_time.h"
+#include "pim_str.h"
+#include "pimd.h"
+#include "pim_iface.h"
+#include "log.h"
+#include "memory.h"
+#include "linklist.h"
+
+void pim_static_route_free(struct static_route *s_route)
+{
+  XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
+}
+
+static struct static_route * static_route_alloc()
+{
+   struct static_route *s_route;
+
+   s_route = XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(*s_route));
+   if (!s_route) {
+     zlog_err("PIM XCALLOC(%zu) failure", sizeof(*s_route));
+     return 0;
+   }
+   return s_route;
+}
+
+static struct static_route *static_route_new(unsigned int   iif,
+                                             unsigned int   oif,
+                                             struct in_addr group,
+                                             struct in_addr source)
+{
+  struct static_route * s_route;
+  s_route = static_route_alloc();
+  if (!s_route) {
+     return 0;
+  }
+
+  s_route->group             = group;
+  s_route->source            = source;
+  s_route->iif               = iif;
+  s_route->oif_ttls[oif]     = 1;
+  s_route->oif_count         = 1;
+  s_route->mc.mfcc_origin    = source;
+  s_route->mc.mfcc_mcastgrp  = group;
+  s_route->mc.mfcc_parent    = iif;
+  s_route->mc.mfcc_ttls[oif] = 1;
+  s_route->creation[oif] = pim_time_monotonic_sec();
+
+  return s_route;
+}
+
+
+int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
+{
+   struct listnode *node = 0;
+   struct static_route *s_route = 0;
+   struct static_route *original_s_route = 0;
+   struct pim_interface *pim_iif = iif ? iif->info : 0;
+   struct pim_interface *pim_oif = oif ? oif->info : 0;
+   unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
+   unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
+
+   if (!iif_index || !oif_index) {
+      zlog_warn("%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
+               __FILE__, __PRETTY_FUNCTION__,
+               iif_index,
+               oif_index);
+      return -2;
+   }
+
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+   if (iif_index == oif_index) {
+      /* looped MFC entry */
+      zlog_warn("%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
+               __FILE__, __PRETTY_FUNCTION__,
+               iif_index,
+               oif_index);
+      return -4;
+   }
+#endif
+
+   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+      if (s_route->group.s_addr == group.s_addr &&
+          s_route->source.s_addr == source.s_addr) {
+         if (s_route->iif == iif_index &&
+             s_route->oif_ttls[oif_index]) {
+            char gifaddr_str[100];
+            char sifaddr_str[100];
+            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+            zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
+                     __FILE__, __PRETTY_FUNCTION__,
+                     iif_index,
+                     oif_index,
+                     gifaddr_str,
+                     sifaddr_str);
+            return -3;
+         }
+
+         /* Ok, from here on out we will be making changes to the s_route structure, but if
+          * for some reason we fail to commit these changes to the kernel, we want to be able
+          * restore the state of the list. So copy the node data and if need be, we can copy
+          * back if it fails.
+          */
+         original_s_route = static_route_alloc();
+         if (!original_s_route) {
+            return -5;
+         }
+         memcpy(original_s_route, s_route, sizeof(struct static_route));
+
+         /* Route exists and has the same input interface, but adding a new output interface */
+         if (s_route->iif == iif_index) {
+            s_route->oif_ttls[oif_index] = 1;
+            s_route->mc.mfcc_ttls[oif_index] = 1;
+            s_route->creation[oif_index] = pim_time_monotonic_sec();
+            ++s_route->oif_count;
+         } else {
+            /* input interface changed */
+            s_route->iif = iif_index;
+            s_route->mc.mfcc_parent = iif_index;
+
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+            /* check to make sure the new input was not an old output */
+            if (s_route->oif_ttls[iif_index]) {
+               s_route->oif_ttls[iif_index] = 0;
+               s_route->creation[iif_index] = 0;
+               s_route->mc.mfcc_ttls[iif_index] = 0;
+               --s_route->oif_count;
+            }
+#endif
+
+            /* now add the new output, if it is new */
+            if (!s_route->oif_ttls[oif_index]) {
+               s_route->oif_ttls[oif_index] = 1;
+               s_route->creation[oif_index] = pim_time_monotonic_sec();
+               s_route->mc.mfcc_ttls[oif_index] = 1;
+               ++s_route->oif_count;
+            }
+         }
+
+         break;
+      }
+   }
+
+   /* If node is null then we reached the end of the list without finding a match */
+   if (!node) {
+      s_route = static_route_new(iif_index, oif_index, group, source);
+      listnode_add(qpim_static_route_list, s_route);
+   }
+
+   if (pim_mroute_add(&(s_route->mc)))
+   {
+      char gifaddr_str[100];
+      char sifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+      pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+      zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               iif_index,
+               oif_index,
+               gifaddr_str,
+               sifaddr_str);
+
+      /* Need to put s_route back to the way it was */
+      if (original_s_route) {
+         memcpy(s_route, original_s_route, sizeof(struct static_route));
+      } else {
+         /* we never stored off a copy, so it must have been a fresh new route */
+         listnode_delete(qpim_static_route_list, s_route);
+         pim_static_route_free(s_route);
+      }
+
+      if (original_s_route) {
+         pim_static_route_free(original_s_route);
+      }
+
+      return -1;
+   }
+
+   /* Make sure we free the memory for the route copy if used */
+   if (original_s_route) {
+      pim_static_route_free(original_s_route);
+   }
+
+   if (PIM_DEBUG_STATIC) {
+     char gifaddr_str[100];
+     char sifaddr_str[100];
+     pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+     pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+     zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
+           __PRETTY_FUNCTION__,
+           iif_index,
+           oif_index,
+           gifaddr_str,
+           sifaddr_str);
+   }
+
+   return 0;
+}
+
+int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
+{
+   struct listnode *node = 0;
+   struct listnode *nextnode = 0;
+   struct static_route *s_route = 0;
+   struct pim_interface *pim_iif = iif ? iif->info : 0;
+   struct pim_interface *pim_oif = oif ? oif->info : 0;
+   unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
+   unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
+
+   if (!iif_index || !oif_index) {
+      zlog_warn("%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
+               __FILE__, __PRETTY_FUNCTION__,
+               iif_index,
+               oif_index);
+      return -2;
+   }
+
+   for (ALL_LIST_ELEMENTS(qpim_static_route_list, node, nextnode, s_route)) {
+      if (s_route->iif == iif_index &&
+          s_route->group.s_addr == group.s_addr &&
+          s_route->source.s_addr == source.s_addr &&
+          s_route->oif_ttls[oif_index]) {
+         s_route->oif_ttls[oif_index] = 0;
+         s_route->mc.mfcc_ttls[oif_index] = 0;
+         --s_route->oif_count;
+
+         /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
+         if (s_route->oif_count <= 0 ? pim_mroute_del(&s_route->mc) : pim_mroute_add(&s_route->mc)) {
+            char gifaddr_str[100];
+            char sifaddr_str[100];
+            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+            zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
+                     __FILE__, __PRETTY_FUNCTION__,
+                     iif_index,
+                     oif_index,
+                     gifaddr_str,
+                     sifaddr_str);
+
+            s_route->oif_ttls[oif_index] = 1;
+            s_route->mc.mfcc_ttls[oif_index] = 1;
+            ++s_route->oif_count;
+
+            return -1;
+         }
+
+         s_route->creation[oif_index] = 0;
+
+         if (s_route->oif_count <= 0) {
+            listnode_delete(qpim_static_route_list, s_route);
+            pim_static_route_free(s_route);
+         }
+
+         if (PIM_DEBUG_STATIC) {
+           char gifaddr_str[100];
+           char sifaddr_str[100];
+           pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+           pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+           zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
+                 __PRETTY_FUNCTION__,
+                 iif_index,
+                 oif_index,
+                 gifaddr_str,
+                 sifaddr_str);
+         }
+
+         break;
+      }
+   }
+
+   if (!node) {
+      char gifaddr_str[100];
+      char sifaddr_str[100];
+      pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
+      pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
+      zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               iif_index,
+               oif_index,
+               gifaddr_str,
+               sifaddr_str);
+      return -3;
+   }
+
+   return 0;
+}
+
+int
+pim_static_write_mroute (struct vty *vty, struct interface *ifp)
+{
+  struct listnode *node;
+  struct static_route *sroute;
+  int count = 0;
+  char sbuf[100];
+  char gbuf[100];
+
+  for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute))
+    {
+      pim_inet4_dump ("<ifaddr?>", sroute->group, gbuf, sizeof (gbuf));
+      pim_inet4_dump ("<ifaddr?>", sroute->source, sbuf, sizeof (sbuf));
+      if (sroute->iif == ifp->ifindex)
+       {
+         int i;
+         for (i = 0; i < MAXVIFS; i++)
+           if (sroute->oif_ttls[i])
+             {
+               struct interface *oifp = if_lookup_by_index (i);
+               vty_out (vty, " ip mroute %s %s %s%s", oifp->name, gbuf, sbuf, VTY_NEWLINE);
+               count ++;
+             }
+       }
+    }
+
+  return count;
+}
diff --git a/pimd/pim_static.h b/pimd/pim_static.h
new file mode 100644 (file)
index 0000000..b3be09e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+  PIM for Quagga: add the ability to configure multicast static routes
+  Copyright (C) 2014  Nathan Bahr, ATCorp
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_STATIC_H_
+#define PIM_STATIC_H_
+
+#include <zebra.h>
+#include "pim_mroute.h"
+#include "if.h"
+
+struct static_route {
+   /* Each static route is unique by these pair of addresses */
+   struct in_addr group;
+   struct in_addr source;
+
+   unsigned int   iif;
+   unsigned char  oif_ttls[MAXVIFS];
+   int            oif_count;
+   struct mfcctl  mc;
+   time_t         creation[MAXVIFS];
+};
+
+void pim_static_route_free(struct static_route *s_route);
+
+int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source);
+int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source);
+int pim_static_write_mroute (struct vty *vty, struct interface *ifp);
+
+#endif /* PIM_STATIC_H_ */
diff --git a/pimd/pim_str.c b/pimd/pim_str.c
new file mode 100644 (file)
index 0000000..3a8353c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "log.h"
+
+#include "pim_str.h"
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size)
+{
+  int save_errno = errno;
+
+  if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
+    int e = errno;
+    zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s",
+             buf_size, e, safe_strerror(e));
+    if (onfail)
+      snprintf(buf, buf_size, "%s", onfail);
+  }
+
+  errno = save_errno;
+}
diff --git a/pimd/pim_str.h b/pimd/pim_str.h
new file mode 100644 (file)
index 0000000..925f17f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_STR_H
+#define PIM_STR_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size);
+
+#endif
diff --git a/pimd/pim_time.c b/pimd/pim_time.c
new file mode 100644 (file)
index 0000000..4e5832c
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "log.h"
+#include "thread.h"
+
+#include "pim_time.h"
+
+static int gettime_monotonic(struct timeval *tv)
+{
+  int result;
+
+  result = gettimeofday(tv, 0);
+  if (result) {
+    zlog_err("%s: gettimeofday() failure: errno=%d: %s",
+            __PRETTY_FUNCTION__,
+            errno, safe_strerror(errno));
+  }
+
+  return result;
+}
+
+/*
+  pim_time_monotonic_sec():
+  number of seconds since some unspecified starting point
+*/
+int64_t pim_time_monotonic_sec()
+{
+  struct timeval now_tv;
+
+  if (gettime_monotonic(&now_tv)) {
+    zlog_err("%s: gettime_monotonic() failure: errno=%d: %s",
+            __PRETTY_FUNCTION__,
+            errno, safe_strerror(errno));
+    return -1;
+  }
+
+  return now_tv.tv_sec;
+}
+
+/*
+  pim_time_monotonic_dsec():
+  number of deciseconds since some unspecified starting point
+*/
+int64_t pim_time_monotonic_dsec()
+{
+  struct timeval now_tv;
+  int64_t        now_dsec;
+
+  if (gettime_monotonic(&now_tv)) {
+    zlog_err("%s: gettime_monotonic() failure: errno=%d: %s",
+            __PRETTY_FUNCTION__,
+            errno, safe_strerror(errno));
+    return -1;
+  }
+
+  now_dsec = ((int64_t) now_tv.tv_sec) * 10 + ((int64_t) now_tv.tv_usec) / 100000;
+
+  return now_dsec;
+}
+
+int pim_time_mmss(char *buf, int buf_size, long sec)
+{
+  long mm;
+  int wr;
+
+  zassert(buf_size >= 5);
+
+  mm = sec / 60;
+  sec %= 60;
+  
+  wr = snprintf(buf, buf_size, "%02ld:%02ld", mm, sec);
+
+  return wr != 8;
+}
+
+static int pim_time_hhmmss(char *buf, int buf_size, long sec)
+{
+  long hh;
+  long mm;
+  int wr;
+
+  zassert(buf_size >= 8);
+
+  hh = sec / 3600;
+  sec %= 3600;
+  mm = sec / 60;
+  sec %= 60;
+  
+  wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec);
+
+  return wr != 8;
+}
+
+void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer)
+{
+  if (t_timer) {
+    pim_time_mmss(buf, buf_size,
+                 thread_timer_remain_second(t_timer));
+  }
+  else {
+    snprintf(buf, buf_size, "--:--");
+  }
+}
+
+void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer)
+{
+  if (t_timer) {
+    pim_time_hhmmss(buf, buf_size,
+                   thread_timer_remain_second(t_timer));
+  }
+  else {
+    snprintf(buf, buf_size, "--:--:--");
+  }
+}
+
+void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec)
+{
+  zassert(buf_size >= 8);
+
+  pim_time_hhmmss(buf, buf_size, uptime_sec);
+}
+
+void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin)
+{
+  if (begin > 0)
+    pim_time_uptime(buf, buf_size, now - begin);
+  else
+    snprintf(buf, buf_size, "--:--:--");
+}
+
+long pim_time_timer_remain_msec(struct thread *t_timer)
+{
+  /* FIXME: Actually fetch msec resolution from thread */
+
+  /* no timer thread running means timer has expired: return 0 */
+
+  return t_timer ?
+    1000 * thread_timer_remain_second(t_timer) :
+    0;
+}
diff --git a/pimd/pim_time.h b/pimd/pim_time.h
new file mode 100644 (file)
index 0000000..2984d9a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_TIME_H
+#define PIM_TIME_H
+
+#include <stdint.h>
+
+#include <zebra.h>
+#include "thread.h"
+
+int64_t pim_time_monotonic_sec(void);
+int64_t pim_time_monotonic_dsec(void);
+int pim_time_mmss(char *buf, int buf_size, long sec);
+void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t);
+void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t);
+void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec);
+void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin);
+long pim_time_timer_remain_msec(struct thread *t_timer);
+
+#endif /* PIM_TIME_H */
diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c
new file mode 100644 (file)
index 0000000..ed4dbba
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_int.h"
+#include "pim_tlv.h"
+#include "pim_str.h"
+#include "pim_msg.h"
+
+uint8_t *pim_tlv_append_uint16(uint8_t *buf,
+                              const uint8_t *buf_pastend,
+                              uint16_t option_type,
+                              uint16_t option_value)
+{
+  uint16_t option_len = 2;
+
+  if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
+    return NULL;
+
+  *(uint16_t *) buf = htons(option_type);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_len);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_value);
+  buf += option_len;
+
+  return buf;
+}
+
+uint8_t *pim_tlv_append_2uint16(uint8_t *buf,
+                               const uint8_t *buf_pastend,
+                               uint16_t option_type,
+                               uint16_t option_value1,
+                               uint16_t option_value2)
+{
+  uint16_t option_len = 4;
+
+  if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
+    return NULL;
+
+  *(uint16_t *) buf = htons(option_type);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_len);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_value1);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_value2);
+  buf += 2;
+
+  return buf;
+}
+
+uint8_t *pim_tlv_append_uint32(uint8_t *buf,
+                              const uint8_t *buf_pastend,
+                              uint16_t option_type,
+                              uint32_t option_value)
+{
+  uint16_t option_len = 4;
+
+  if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend)
+    return NULL;
+
+  *(uint16_t *) buf = htons(option_type);
+  buf += 2;
+  *(uint16_t *) buf = htons(option_len);
+  buf += 2;
+  pim_write_uint32(buf, option_value);
+  buf += option_len;
+
+  return buf;
+}
+
+#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr))
+
+uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
+                                      const uint8_t *buf_pastend,
+                                      struct list *ifconnected)
+{
+  struct listnode *node;
+  uint16_t option_len = 0;
+
+  uint8_t *curr;
+
+  node = listhead(ifconnected);
+
+  /* Empty address list ? */
+  if (!node) {
+    return buf;
+  }
+
+  /* Skip first address (primary) */
+  node = listnextnode(node);
+
+  /* Scan secondary address list */
+  curr = buf + 4; /* skip T and L */
+  for (; node; node = listnextnode(node)) {
+    struct connected *ifc = listgetdata(node);
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+
+    if ((curr + ucast_ipv4_encoding_len) > buf_pastend)
+      return 0;
+
+    /* Write encoded unicast IPv4 address */
+    *(uint8_t *) curr = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+    ++curr;
+    *(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */
+    ++curr;
+    memcpy(curr, &p->u.prefix4, sizeof(struct in_addr));
+    curr += sizeof(struct in_addr);
+
+    option_len += ucast_ipv4_encoding_len; 
+  }
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_debug("%s: number of encoded secondary unicast IPv4 addresses: %zu",
+              __PRETTY_FUNCTION__,
+              option_len / ucast_ipv4_encoding_len);
+  }
+
+  if (option_len < 1) {
+    /* Empty secondary unicast IPv4 address list */
+    return buf;
+  }
+
+  /*
+   * Write T and L
+   */
+  *(uint16_t *) buf       = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST);
+  *(uint16_t *) (buf + 2) = htons(option_len);
+
+  return curr;
+}
+
+static int check_tlv_length(const char *label, const char *tlv_name,
+                           const char *ifname, struct in_addr src_addr,
+                           int correct_len, int option_len)
+{
+  if (option_len != correct_len) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s",
+             label, tlv_name,
+             option_len, correct_len,
+             src_str, ifname);
+    return -1;
+  }
+
+  return 0;
+}
+
+static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name,
+                                         const char *ifname, struct in_addr src_addr,
+                                         pim_hello_options options,
+                                         pim_hello_options opt_mask,
+                                         uint16_t new, uint16_t old)
+{
+  if (PIM_OPTION_IS_SET(options, opt_mask)) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
+             label, tlv_name,
+             new, old,
+             src_str, ifname);
+  }
+}
+
+static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name,
+                                         const char *ifname, struct in_addr src_addr,
+                                         pim_hello_options options,
+                                         pim_hello_options opt_mask,
+                                         uint32_t new, uint32_t old)
+{
+  if (PIM_OPTION_IS_SET(options, opt_mask)) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s",
+             label, tlv_name,
+             new, old,
+             src_str, ifname);
+  }
+}
+
+static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name,
+                                             const char *ifname, struct in_addr src_addr,
+                                             pim_hello_options options,
+                                             pim_hello_options opt_mask,
+                                             uint32_t new, uint32_t old)
+{
+  if (PIM_OPTION_IS_SET(options, opt_mask)) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s",
+             label, tlv_name,
+             new, old,
+             src_str, ifname);
+  }
+}
+
+int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
+                          pim_hello_options *hello_options,
+                          uint16_t *hello_option_holdtime,
+                          uint16_t option_len,
+                          const uint8_t *tlv_curr) 
+{
+  const char *label = "holdtime";
+
+  if (check_tlv_length(__PRETTY_FUNCTION__, label,
+                      ifname, src_addr,
+                      sizeof(uint16_t), option_len)) {
+    return -1;
+  }
+  
+  check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label,
+                               ifname, src_addr,
+                               *hello_options, PIM_OPTION_MASK_HOLDTIME,
+                               PIM_TLV_GET_HOLDTIME(tlv_curr),
+                               *hello_option_holdtime);
+  
+  PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME);
+  
+  *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr);
+  
+  return 0;
+}
+
+int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
+                                 pim_hello_options *hello_options,
+                                 uint16_t *hello_option_propagation_delay,
+                                 uint16_t *hello_option_override_interval,
+                                 uint16_t option_len,
+                                 const uint8_t *tlv_curr) 
+{
+  if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay",
+                      ifname, src_addr,
+                      sizeof(uint32_t), option_len)) {
+    return -1;
+  }
+  
+  check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay",
+                               ifname, src_addr,
+                               *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY,
+                               PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr),
+                               *hello_option_propagation_delay);
+  
+  PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY);
+  
+  *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr);
+  if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) {
+    PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
+  }
+  else {
+    PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION);
+  }
+  ++tlv_curr;
+  ++tlv_curr;
+  *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr);
+  
+  return 0;
+}
+
+int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
+                             pim_hello_options *hello_options,
+                             uint32_t *hello_option_dr_priority,
+                             uint16_t option_len,
+                             const uint8_t *tlv_curr) 
+{
+  const char *label = "dr_priority";
+
+  if (check_tlv_length(__PRETTY_FUNCTION__, label,
+                      ifname, src_addr,
+                      sizeof(uint32_t), option_len)) {
+    return -1;
+  }
+  
+  check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label,
+                               ifname, src_addr,
+                               *hello_options, PIM_OPTION_MASK_DR_PRIORITY,
+                               PIM_TLV_GET_DR_PRIORITY(tlv_curr),
+                               *hello_option_dr_priority);
+  
+  PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY);
+  
+  *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr);
+
+  return 0;
+}
+
+int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
+                               pim_hello_options *hello_options,
+                               uint32_t *hello_option_generation_id,
+                               uint16_t option_len,
+                               const uint8_t *tlv_curr) 
+{
+  const char *label = "generation_id";
+
+  if (check_tlv_length(__PRETTY_FUNCTION__, label,
+                      ifname, src_addr,
+                      sizeof(uint32_t), option_len)) {
+    return -1;
+  }
+  
+  check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label,
+                                   ifname, src_addr,
+                                   *hello_options, PIM_OPTION_MASK_GENERATION_ID,
+                                   PIM_TLV_GET_GENERATION_ID(tlv_curr),
+                                   *hello_option_generation_id);
+  
+  PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID);
+  
+  *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr);
+  
+  return 0;
+}
+
+int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
+                        struct prefix *p,
+                        const uint8_t *buf,
+                        int buf_size)
+{
+  const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */
+  const uint8_t *addr;
+  const uint8_t *pastend;
+  int family;
+  int type;
+
+  if (buf_size < ucast_encoding_min_len) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             buf_size, ucast_encoding_min_len,
+             src_str, ifname);
+    return -1;
+  }
+
+  addr = buf;
+  pastend = buf + buf_size;
+
+  family = *addr++;
+  type = *addr++;
+
+  switch (family) {
+  case PIM_MSG_ADDRESS_FAMILY_IPV4:
+    if (type) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown unicast address encoding type=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               type, src_str, ifname);
+      return -2;
+    }
+
+    if ((addr + sizeof(struct in_addr)) > pastend) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu from %s on %s",
+               __PRETTY_FUNCTION__,
+               pastend - addr, sizeof(struct in_addr),
+               src_str, ifname);
+      return -3;
+    }
+
+    p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+    memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
+
+    addr += sizeof(struct in_addr);
+
+    break;
+  default:
+    {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown unicast address encoding family=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               family, src_str, ifname);
+      return -4;
+    }
+  }
+
+  return addr - buf;
+}
+
+int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
+                        struct prefix *p,
+                        const uint8_t *buf,
+                        int buf_size)
+{
+  const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
+  const uint8_t *addr;
+  const uint8_t *pastend;
+  int family;
+  int type;
+  int mask_len;
+
+  if (buf_size < grp_encoding_min_len) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: group address encoding overflow: left=%d needed=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             buf_size, grp_encoding_min_len,
+             src_str, ifname);
+    return -1;
+  }
+
+  addr = buf;
+  pastend = buf + buf_size;
+
+  family = *addr++;
+  type = *addr++;
+  //++addr;
+  ++addr; /* skip b_reserved_z fields */
+  mask_len = *addr++;
+
+  switch (family) {
+  case PIM_MSG_ADDRESS_FAMILY_IPV4:
+    if (type) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown group address encoding type=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               type, src_str, ifname);
+      return -2;
+    }
+
+    if ((addr + sizeof(struct in_addr)) > pastend) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from %s on %s",
+               __PRETTY_FUNCTION__,
+               pastend - addr, sizeof(struct in_addr),
+               src_str, ifname);
+      return -3;
+    }
+
+    p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+    memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
+    p->prefixlen = mask_len;
+
+    addr += sizeof(struct in_addr);
+
+    break;
+  default:
+    {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown group address encoding family=%d from %s on %s",
+               __PRETTY_FUNCTION__,
+               family, src_str, ifname);
+      return -4;
+    }
+  }
+
+  return addr - buf;
+}
+
+int pim_parse_addr_source(const char *ifname,
+                         struct in_addr src_addr,
+                         struct prefix *p,
+                         uint8_t *flags,
+                         const uint8_t *buf,
+                         int buf_size)
+{
+  const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */
+  const uint8_t *addr;
+  const uint8_t *pastend;
+  int family;
+  int type;
+  int mask_len;
+
+  if (buf_size < src_encoding_min_len) {
+    char src_str[100];
+    pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+    zlog_warn("%s: source address encoding overflow: left=%d needed=%d from %s on %s",
+             __PRETTY_FUNCTION__,
+             buf_size, src_encoding_min_len,
+             src_str, ifname);
+    return -1;
+  }
+
+  addr = buf;
+  pastend = buf + buf_size;
+
+  family = *addr++;
+  type = *addr++;
+  *flags = *addr++;
+  mask_len = *addr++;
+
+  switch (family) {
+  case PIM_MSG_ADDRESS_FAMILY_IPV4:
+    if (type) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown source address encoding type=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x",
+               __PRETTY_FUNCTION__,
+               type, src_str, ifname,
+               buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+      return -2;
+    }
+
+    if ((addr + sizeof(struct in_addr)) > pastend) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu from %s on %s",
+               __PRETTY_FUNCTION__,
+               pastend - addr, sizeof(struct in_addr),
+               src_str, ifname);
+      return -3;
+    }
+
+    p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */
+    memcpy(&p->u.prefix4, addr, sizeof(struct in_addr));
+    p->prefixlen = mask_len;
+
+    /* 
+       RFC 4601: 4.9.1  Encoded Source and Group Address Formats
+
+       Encoded-Source Address
+
+       The mask length MUST be equal to the mask length in bits for
+       the given Address Family and Encoding Type (32 for IPv4 native
+       and 128 for IPv6 native).  A router SHOULD ignore any messages
+       received with any other mask length.
+    */
+    if (p->prefixlen != 32) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", p->u.prefix4, src_str, sizeof(src_str));
+      zlog_warn("%s: IPv4 bad source address mask: %s/%d",
+               __PRETTY_FUNCTION__, src_str, p->prefixlen);
+      return -4;
+    }
+
+    addr += sizeof(struct in_addr);
+
+    break;
+  default:
+    {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: unknown source address encoding family=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x",
+               __PRETTY_FUNCTION__,
+               family, src_str, ifname,
+               buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+      return -5;
+    }
+  }
+
+  return addr - buf;
+}
+
+#define FREE_ADDR_LIST(hello_option_addr_list) \
+{ \
+  if (hello_option_addr_list) { \
+    list_delete(hello_option_addr_list); \
+    hello_option_addr_list = 0; \
+  } \
+}
+
+int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
+                           pim_hello_options *hello_options,
+                           struct list **hello_option_addr_list,
+                           uint16_t option_len,
+                           const uint8_t *tlv_curr) 
+{
+  const uint8_t *addr;
+  const uint8_t *pastend;
+
+  zassert(hello_option_addr_list);
+
+  /*
+    Scan addr list
+   */
+  addr = tlv_curr;
+  pastend = tlv_curr + option_len;
+  while (addr < pastend) {
+    struct prefix tmp;
+    int addr_offset;
+
+    /*
+      Parse ucast addr
+     */
+    addr_offset = pim_parse_addr_ucast(ifname, src_addr, &tmp,
+                                      addr, pastend - addr);
+    if (addr_offset < 1) {
+      char src_str[100];
+      pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+      zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+               __PRETTY_FUNCTION__,
+               src_str, ifname);
+      FREE_ADDR_LIST(*hello_option_addr_list);
+      return -1;
+    }
+    addr += addr_offset;
+
+    /*
+      Debug
+     */
+    if (PIM_DEBUG_PIM_TRACE) {
+      switch (tmp.family) {
+      case AF_INET:
+       {
+         char addr_str[100];
+         char src_str[100];
+         pim_inet4_dump("<addr?>", tmp.u.prefix4, addr_str, sizeof(addr_str));
+         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+         zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s",
+                    __PRETTY_FUNCTION__,
+                    *hello_option_addr_list ?
+                    ((int) listcount(*hello_option_addr_list)) : -1,
+                    addr_str, src_str, ifname);
+       }
+       break;
+      default:
+       {
+         char src_str[100];
+         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+         zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s",
+                    __PRETTY_FUNCTION__,
+                    *hello_option_addr_list ?
+                    ((int) listcount(*hello_option_addr_list)) : -1,
+                    src_str, ifname);
+       }
+      }
+    }
+
+    /*
+      Exclude neighbor's primary address if incorrectly included in
+      the secondary address list
+     */
+    if (tmp.family == AF_INET) {
+      if (tmp.u.prefix4.s_addr == src_addr.s_addr) {
+         char src_str[100];
+         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+         zlog_warn("%s: ignoring primary address in secondary list from %s on %s",
+                   __PRETTY_FUNCTION__,
+                   src_str, ifname);
+         continue;
+      }
+    }
+
+    /*
+      Allocate list if needed
+     */
+    if (!*hello_option_addr_list) {
+      *hello_option_addr_list = list_new();
+      if (!*hello_option_addr_list) {
+       zlog_err("%s %s: failure: hello_option_addr_list=list_new()",
+                __FILE__, __PRETTY_FUNCTION__);
+       return -2;
+      }
+      (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free;
+    }
+
+    /*
+      Attach addr to list
+     */
+    {
+      struct prefix *p;
+      p = prefix_new();
+      if (!p) {
+       zlog_err("%s %s: failure: prefix_new()",
+                __FILE__, __PRETTY_FUNCTION__);
+       FREE_ADDR_LIST(*hello_option_addr_list);
+       return -3;
+      }
+      p->family = tmp.family;
+      p->u.prefix4 = tmp.u.prefix4;
+      listnode_add(*hello_option_addr_list, p);
+    }
+
+  } /* while (addr < pastend) */
+  
+  /*
+    Mark hello option
+   */
+  PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST);
+  
+  return 0;
+}
diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h
new file mode 100644 (file)
index 0000000..b802cf8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_TLV_H
+#define PIM_TLV_H
+
+#include <zebra.h>
+
+#include "config.h"
+#include "if.h"
+#include "linklist.h"
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+
+#define PIM_MSG_OPTION_TYPE_HOLDTIME         (1)
+#define PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY  (2)
+#define PIM_MSG_OPTION_TYPE_DR_PRIORITY      (19)
+#define PIM_MSG_OPTION_TYPE_GENERATION_ID    (20)
+#define PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH (21)
+#define PIM_MSG_OPTION_TYPE_ADDRESS_LIST     (24)
+
+typedef uint32_t pim_hello_options;
+#define PIM_OPTION_MASK_HOLDTIME                     (1 << 0) /* recv holdtime */
+#define PIM_OPTION_MASK_LAN_PRUNE_DELAY              (1 << 1) /* recv lan_prune_delay */
+#define PIM_OPTION_MASK_DR_PRIORITY                  (1 << 2) /* recv dr_priority */
+#define PIM_OPTION_MASK_GENERATION_ID                (1 << 3) /* recv generation_id */
+#define PIM_OPTION_MASK_ADDRESS_LIST                 (1 << 4) /* recv secondary address list */
+#define PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION (1 << 5) /* T bit value (valid if recv lan_prune_delay) */
+
+#define PIM_RPT_BIT_MASK      (1 << 0)
+#define PIM_WILDCARD_BIT_MASK (1 << 1)
+
+#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask))
+#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask))
+#define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask))
+
+#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf))
+#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf))
+#define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_PROPAGATION_DELAY(buf) (PIM_TLV_GET_UINT16(buf) & 0x7FFF)
+#define PIM_TLV_GET_OVERRIDE_INTERVAL(buf) PIM_TLV_GET_UINT16(buf)
+#define PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(buf) ((*(const uint8_t *)(buf)) & 0x80)
+#define PIM_TLV_GET_DR_PRIORITY(buf) PIM_TLV_GET_UINT32(buf)
+#define PIM_TLV_GET_GENERATION_ID(buf) PIM_TLV_GET_UINT32(buf)
+
+#define PIM_TLV_TYPE_SIZE               (2)
+#define PIM_TLV_LENGTH_SIZE             (2)
+#define PIM_TLV_MIN_SIZE                (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE)
+#define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len))
+
+uint8_t *pim_tlv_append_uint16(uint8_t *buf,
+                              const uint8_t *buf_pastend,
+                              uint16_t option_type,
+                              uint16_t option_value);
+uint8_t *pim_tlv_append_2uint16(uint8_t *buf,
+                               const uint8_t *buf_pastend,
+                               uint16_t option_type,
+                               uint16_t option_value1,
+                               uint16_t option_value2);
+uint8_t *pim_tlv_append_uint32(uint8_t *buf,
+                              const uint8_t *buf_pastend,
+                              uint16_t option_type,
+                              uint32_t option_value);
+uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf,
+                                      const uint8_t *buf_pastend,
+                                      struct list *ifconnected);
+
+int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr,
+                          pim_hello_options *hello_options,
+                          uint16_t *hello_option_holdtime,
+                          uint16_t option_len,
+                          const uint8_t *tlv_curr);
+int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr,
+                                 pim_hello_options *hello_options,
+                                 uint16_t *hello_option_propagation_delay,
+                                 uint16_t *hello_option_override_interval,
+                                 uint16_t option_len,
+                                 const uint8_t *tlv_curr);
+int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr,
+                             pim_hello_options *hello_options,
+                             uint32_t *hello_option_dr_priority,
+                             uint16_t option_len,
+                             const uint8_t *tlv_curr);
+int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr,
+                               pim_hello_options *hello_options,
+                               uint32_t *hello_option_generation_id,
+                               uint16_t option_len,
+                               const uint8_t *tlv_curr);
+int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr,
+                           pim_hello_options *hello_options,
+                           struct list **hello_option_addr_list,
+                           uint16_t option_len,
+                           const uint8_t *tlv_curr);
+
+int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr,
+                        struct prefix *p,
+                        const uint8_t *buf,
+                        int buf_size);
+int pim_parse_addr_group(const char *ifname, struct in_addr src_addr,
+                        struct prefix *p,
+                        const uint8_t *buf,
+                        int buf_size);
+int pim_parse_addr_source(const char *ifname,
+                         struct in_addr src_addr,
+                         struct prefix *p,
+                         uint8_t *flags,
+                         const uint8_t *buf,
+                         int buf_size);
+
+#endif /* PIM_TLV_H */
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
new file mode 100644 (file)
index 0000000..a4d274a
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "zebra/rib.h"
+
+#include "log.h"
+#include "zclient.h"
+#include "memory.h"
+#include "thread.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_join.h"
+#include "pim_zlookup.h"
+#include "pim_upstream.h"
+#include "pim_ifchannel.h"
+#include "pim_neighbor.h"
+#include "pim_rpf.h"
+#include "pim_zebra.h"
+#include "pim_oil.h"
+#include "pim_macro.h"
+
+static void join_timer_start(struct pim_upstream *up);
+static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
+
+void pim_upstream_free(struct pim_upstream *up)
+{
+  XFREE(MTYPE_PIM_UPSTREAM, up);
+}
+
+static void upstream_channel_oil_detach(struct pim_upstream *up)
+{
+  if (up->channel_oil) {
+    pim_channel_oil_del(up->channel_oil);
+    up->channel_oil = 0;
+  }
+}
+
+void pim_upstream_delete(struct pim_upstream *up)
+{
+  THREAD_OFF(up->t_join_timer);
+
+  upstream_channel_oil_detach(up);
+
+  /*
+    notice that listnode_delete() can't be moved
+    into pim_upstream_free() because the later is
+    called by list_delete_all_node()
+  */
+  listnode_delete(qpim_upstream_list, up);
+
+  pim_upstream_free(up);
+}
+
+static void send_join(struct pim_upstream *up)
+{
+  zassert(up->join_state == PIM_UPSTREAM_JOINED);
+
+  
+  if (PIM_DEBUG_PIM_TRACE) {
+    if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
+      char src_str[100];
+      char grp_str[100];
+      char rpf_str[100];
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
+      zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
+               __PRETTY_FUNCTION__,
+               src_str, grp_str, rpf_str);
+      /* warning only */
+    }
+  }
+  
+  /* send Join(S,G) to the current upstream neighbor */
+  pim_joinprune_send(up->rpf.source_nexthop.interface,
+                    up->rpf.rpf_addr,
+                    up->source_addr,
+                    up->group_addr,
+                    1 /* join */);
+}
+
+static int on_join_timer(struct thread *t)
+{
+  struct pim_upstream *up;
+
+  zassert(t);
+  up = THREAD_ARG(t);
+  zassert(up);
+
+  send_join(up);
+
+  up->t_join_timer = 0;
+  join_timer_start(up);
+
+  return 0;
+}
+
+static void join_timer_start(struct pim_upstream *up)
+{
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
+              __PRETTY_FUNCTION__,
+              qpim_t_periodic,
+              src_str, grp_str);
+  }
+
+  zassert(!up->t_join_timer);
+
+  THREAD_TIMER_ON(master, up->t_join_timer,
+                 on_join_timer,
+                 up, qpim_t_periodic);
+}
+
+void pim_upstream_join_timer_restart(struct pim_upstream *up)
+{
+  THREAD_OFF(up->t_join_timer);
+  join_timer_start(up);
+}
+
+static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
+                                                int interval_msec)
+{
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
+              __PRETTY_FUNCTION__,
+              interval_msec,
+              src_str, grp_str);
+  }
+
+  THREAD_OFF(up->t_join_timer);
+  THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
+                      on_join_timer,
+                      up, interval_msec);
+}
+
+void pim_upstream_join_suppress(struct pim_upstream *up,
+                               struct in_addr rpf_addr,
+                               int holdtime)
+{
+  long t_joinsuppress_msec;
+  long join_timer_remain_msec;
+
+  t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
+                           1000 * holdtime);
+
+  join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    char rpf_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
+    zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
+              __FILE__, __PRETTY_FUNCTION__, 
+              src_str, grp_str,
+              rpf_str,
+              join_timer_remain_msec, t_joinsuppress_msec);
+  }
+
+  if (join_timer_remain_msec < t_joinsuppress_msec) {
+    if (PIM_DEBUG_PIM_TRACE) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
+                __FILE__, __PRETTY_FUNCTION__, 
+                src_str, grp_str, t_joinsuppress_msec);
+    }
+
+    pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
+  }
+}
+
+void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
+                                                   struct pim_upstream *up,
+                                                   struct in_addr rpf_addr)
+{
+  long join_timer_remain_msec;
+  int t_override_msec;
+
+  join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
+  t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char src_str[100];
+    char grp_str[100];
+    char rpf_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
+    zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
+              debug_label,
+              src_str, grp_str, rpf_str,
+              join_timer_remain_msec, t_override_msec);
+  }
+    
+  if (join_timer_remain_msec > t_override_msec) {
+    if (PIM_DEBUG_PIM_TRACE) {
+      char src_str[100];
+      char grp_str[100];
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
+                debug_label,
+                src_str, grp_str,
+                t_override_msec);
+    }
+
+    pim_upstream_join_timer_restart_msec(up, t_override_msec);
+  }
+}
+
+static void forward_on(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+      if (ch->upstream != up)
+       continue;
+
+      if (pim_macro_chisin_oiflist(ch))
+       pim_forward_start(ch);
+
+    } /* scan iface channel list */
+  } /* scan iflist */
+}
+
+static void forward_off(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+      if (ch->upstream != up)
+       continue;
+
+      pim_forward_stop(ch);
+
+    } /* scan iface channel list */
+  } /* scan iflist */
+}
+
+static void pim_upstream_switch(struct pim_upstream *up,
+                               enum pim_upstream_state new_state)
+{
+  enum pim_upstream_state old_state = up->join_state;
+
+  zassert(old_state != new_state);
+  
+  up->join_state       = new_state;
+  up->state_transition = pim_time_monotonic_sec();
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    char src_str[100];
+    char grp_str[100];
+    pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+    pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+    zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
+              __PRETTY_FUNCTION__,
+              ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
+              src_str, grp_str);
+  }
+
+  pim_upstream_update_assert_tracking_desired(up);
+
+  if (new_state == PIM_UPSTREAM_JOINED) {
+    forward_on(up);
+    send_join(up);
+    join_timer_start(up);
+  }
+  else {
+    forward_off(up);
+    pim_joinprune_send(up->rpf.source_nexthop.interface,
+                      up->rpf.rpf_addr,
+                      up->source_addr,
+                      up->group_addr,
+                      0 /* prune */);
+    zassert(up->t_join_timer);
+    THREAD_OFF(up->t_join_timer);
+  }
+
+}
+
+static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
+                                            struct in_addr group_addr)
+{
+  struct pim_upstream *up;
+  enum pim_rpf_result rpf_result;
+
+  up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+  if (!up) {
+    zlog_err("%s: PIM XMALLOC(%zu) failure",
+            __PRETTY_FUNCTION__, sizeof(*up));
+    return 0;
+  }
+  
+  up->source_addr                = source_addr;
+  up->group_addr                 = group_addr;
+  up->flags                      = 0;
+  up->ref_count                  = 1;
+  up->t_join_timer               = 0;
+  up->join_state                 = 0;
+  up->state_transition           = pim_time_monotonic_sec();
+  up->channel_oil                = 0;
+
+  up->rpf.source_nexthop.interface                = 0;
+  up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+  up->rpf.source_nexthop.mrib_metric_preference   = qpim_infinite_assert_metric.metric_preference;
+  up->rpf.source_nexthop.mrib_route_metric        = qpim_infinite_assert_metric.route_metric;
+  up->rpf.rpf_addr.s_addr                         = PIM_NET_INADDR_ANY;
+
+  rpf_result = pim_rpf_update(up, 0);
+  if (rpf_result == PIM_RPF_FAILURE) {
+    XFREE(MTYPE_PIM_UPSTREAM, up);
+    return NULL;
+  }
+
+  listnode_add(qpim_upstream_list, up);
+
+  return up;
+}
+
+struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
+                                      struct in_addr group_addr)
+{
+  struct listnode     *up_node;
+  struct pim_upstream *up;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
+    if (
+       (source_addr.s_addr == up->source_addr.s_addr) &&
+       (group_addr.s_addr == up->group_addr.s_addr)
+       ) {
+      return up;
+    }
+  }
+
+  return 0;
+}
+
+struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
+                                     struct in_addr group_addr)
+{
+  struct pim_upstream *up;
+
+  up = pim_upstream_find(source_addr, group_addr);
+  if (up) {
+    ++up->ref_count;
+  }
+  else {
+    up = pim_upstream_new(source_addr, group_addr);
+  }
+
+  return up;
+}
+
+void pim_upstream_del(struct pim_upstream *up)
+{
+  --up->ref_count;
+
+  if (up->ref_count < 1) {
+    pim_upstream_delete(up);
+  }
+}
+
+/*
+  Evaluate JoinDesired(S,G):
+
+  JoinDesired(S,G) is true if there is a downstream (S,G) interface I
+  in the set:
+
+  inherited_olist(S,G) =
+  joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+  JoinDesired(S,G) may be affected by changes in the following:
+
+  pim_ifp->primary_address
+  pim_ifp->pim_dr_addr
+  ch->ifassert_winner_metric
+  ch->ifassert_winner
+  ch->local_ifmembership 
+  ch->ifjoin_state
+  ch->upstream->rpf.source_nexthop.mrib_metric_preference
+  ch->upstream->rpf.source_nexthop.mrib_route_metric
+  ch->upstream->rpf.source_nexthop.interface
+
+  See also pim_upstream_update_join_desired() below.
+ */
+int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+      if (ch->upstream != up)
+       continue;
+
+      if (pim_macro_ch_lost_assert(ch))
+       continue; /* keep searching */
+
+      if (pim_macro_chisin_joins_or_include(ch))
+       return 1; /* true */
+    } /* scan iface channel list */
+  } /* scan iflist */
+
+  return 0; /* false */
+}
+
+/*
+  See also pim_upstream_evaluate_join_desired() above.
+*/
+void pim_upstream_update_join_desired(struct pim_upstream *up)
+{
+  int was_join_desired; /* boolean */
+  int is_join_desired; /* boolean */
+
+  was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
+
+  is_join_desired = pim_upstream_evaluate_join_desired(up);
+  if (is_join_desired)
+    PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
+  else
+    PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
+
+  /* switched from false to true */
+  if (is_join_desired && !was_join_desired) {
+    zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
+    pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
+    return;
+  }
+      
+  /* switched from true to false */
+  if (!is_join_desired && was_join_desired) {
+    zassert(up->join_state == PIM_UPSTREAM_JOINED);
+    pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
+    return;
+  }
+}
+
+/*
+  RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
+  Transitions from Joined State
+  RPF'(S,G) GenID changes
+
+  The upstream (S,G) state machine remains in Joined state.  If the
+  Join Timer is set to expire in more than t_override seconds, reset
+  it so that it expires after t_override seconds.
+*/
+void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
+{
+  struct listnode     *up_node;
+  struct listnode     *up_nextnode;
+  struct pim_upstream *up;
+
+  /*
+    Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
+  */
+  for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+
+    if (PIM_DEBUG_PIM_TRACE) {
+      char neigh_str[100];
+      char src_str[100];
+      char grp_str[100];
+      char rpf_addr_str[100];
+      pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
+      pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
+      pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
+      pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
+      zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
+                __PRETTY_FUNCTION__,
+                neigh_str, src_str, grp_str,
+                up->join_state == PIM_UPSTREAM_JOINED,
+                rpf_addr_str);
+    }
+
+    /* consider only (S,G) upstream in Joined state */
+    if (up->join_state != PIM_UPSTREAM_JOINED)
+      continue;
+
+    /* match RPF'(S,G)=neigh_addr */
+    if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
+      continue;
+
+    pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
+                                                  up, neigh_addr);
+  }
+}
+
+
+void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
+                                       struct interface *old_rpf_ifp)
+{
+  struct listnode  *ifnode;
+  struct listnode  *ifnextnode;
+  struct interface *ifp;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    struct listnode      *chnode;
+    struct listnode      *chnextnode;
+    struct pim_ifchannel *ch;
+    struct pim_interface *pim_ifp;
+
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* search all ifchannels */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+      if (ch->upstream != up)
+       continue;
+
+      if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
+       if (
+           /* RPF_interface(S) was NOT I */
+           (old_rpf_ifp == ch->interface)
+           &&
+           /* RPF_interface(S) stopped being I */
+           (ch->upstream->rpf.source_nexthop.interface != ch->interface)
+           ) {
+         assert_action_a5(ch);
+       }
+      } /* PIM_IFASSERT_I_AM_LOSER */
+
+      pim_ifchannel_update_assert_tracking_desired(ch);
+    }
+  }
+}
+
+void pim_upstream_update_could_assert(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+      if (ch->upstream != up)
+       continue;
+
+      pim_ifchannel_update_could_assert(ch);
+
+    } /* scan iface channel list */
+  } /* scan iflist */
+}
+
+void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+      if (ch->upstream != up)
+       continue;
+
+      pim_ifchannel_update_my_assert_metric(ch);
+
+    } /* scan iface channel list */
+  } /* scan iflist */
+}
+
+static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
+{
+  struct listnode      *ifnode;
+  struct listnode      *ifnextnode;
+  struct listnode      *chnode;
+  struct listnode      *chnextnode;
+  struct interface     *ifp;
+  struct pim_interface *pim_ifp;
+  struct pim_ifchannel *ch;
+
+  /* scan all interfaces */
+  for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
+    pim_ifp = ifp->info;
+    if (!pim_ifp)
+      continue;
+
+    /* scan per-interface (S,G) state */
+    for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
+
+      if (ch->upstream != up)
+       continue;
+
+      pim_ifchannel_update_assert_tracking_desired(ch);
+
+    } /* scan iface channel list */
+  } /* scan iflist */
+}
diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h
new file mode 100644 (file)
index 0000000..5b5182d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_UPSTREAM_H
+#define PIM_UPSTREAM_H
+
+#include <zebra.h>
+
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED         (1 << 0)
+#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0)
+
+#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
+#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
+
+/*
+  RFC 4601:
+
+  Metric Preference
+    Preference value assigned to the unicast routing protocol that
+    provided the route to the multicast source or Rendezvous-Point.
+
+  Metric
+    The unicast routing table metric associated with the route used to
+    reach the multicast source or Rendezvous-Point.  The metric is in
+    units applicable to the unicast routing protocol used.
+*/
+struct pim_nexthop {
+  struct interface *interface;              /* RPF_interface(S) */
+  struct in_addr    mrib_nexthop_addr;      /* MRIB.next_hop(S) */
+  uint32_t          mrib_metric_preference; /* MRIB.pref(S) */
+  uint32_t          mrib_route_metric;      /* MRIB.metric(S) */
+};
+
+struct pim_rpf {
+  struct pim_nexthop source_nexthop;
+  struct in_addr     rpf_addr;               /* RPF'(S,G) */
+};
+
+enum pim_rpf_result {
+  PIM_RPF_OK = 0,
+  PIM_RPF_CHANGED,
+  PIM_RPF_FAILURE
+};
+
+enum pim_upstream_state {
+  PIM_UPSTREAM_NOTJOINED,
+  PIM_UPSTREAM_JOINED
+};
+
+/*
+  Upstream (S,G) channel in Joined state
+  
+  (S,G) in the "Not Joined" state is not represented
+  
+  See RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Message
+*/
+struct pim_upstream {
+  struct in_addr           source_addr;  /* (S,G) source key */
+  struct in_addr           group_addr;   /* (S,G) group key */
+  uint32_t                 flags;
+  struct channel_oil      *channel_oil;
+
+  enum pim_upstream_state  join_state;
+  int                      ref_count;
+
+  struct pim_rpf           rpf;
+
+  struct thread           *t_join_timer;
+  int64_t                  state_transition; /* Record current state uptime */
+};
+
+void pim_upstream_free(struct pim_upstream *up);
+void pim_upstream_delete(struct pim_upstream *up);
+struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
+                                      struct in_addr group_addr);
+struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
+                                     struct in_addr group_addr);
+void pim_upstream_del(struct pim_upstream *up);
+
+int pim_upstream_evaluate_join_desired(struct pim_upstream *up);
+void pim_upstream_update_join_desired(struct pim_upstream *up);
+
+void pim_upstream_join_suppress(struct pim_upstream *up,
+                               struct in_addr rpf_addr,
+                               int holdtime);
+void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
+                                                   struct pim_upstream *up,
+                                                   struct in_addr rpf_addr);
+void pim_upstream_join_timer_restart(struct pim_upstream *up);
+void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr);
+void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
+                                       struct interface *old_rpf_ifp);
+
+void pim_upstream_update_could_assert(struct pim_upstream *up);
+void pim_upstream_update_my_assert_metric(struct pim_upstream *up);
+
+#endif /* PIM_UPSTREAM_H */
diff --git a/pimd/pim_util.c b/pimd/pim_util.c
new file mode 100644 (file)
index 0000000..fdfed2b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+
+#include "pim_util.h"
+
+/*
+  RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+  
+  If QQIC < 128,  QQI = QQIC
+  If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
+  
+  0 1 2 3 4 5 6 7
+  +-+-+-+-+-+-+-+-+
+  |1| exp | mant  |
+  +-+-+-+-+-+-+-+-+
+  
+  Since exp=0..7 then (exp+3)=3..10, then QQI has
+  one of the following bit patterns:
+  
+  exp=0: QQI = 0000.0000.1MMM.M000
+  exp=1: QQI = 0000.0001.MMMM.0000
+  ...
+  exp=6: QQI = 001M.MMM0.0000.0000
+  exp=7: QQI = 01MM.MM00.0000.0000
+  --------- ---------
+  0x4  0x0  0x0  0x0
+*/
+uint8_t igmp_msg_encode16to8(uint16_t value)
+{
+  uint8_t code;
+
+  if (value < 128) {
+    code = value;
+  }
+  else {
+    uint16_t mask = 0x4000;
+    uint8_t  exp;
+    uint16_t mant;
+    for (exp = 7; exp > 0; --exp) {
+      if (mask & value)
+       break;
+      mask >>= 1;
+    }
+    mant = 0x000F & (value >> (exp + 3));
+    code = ((uint8_t) 1 << 7) | ((uint8_t) exp << 4) | (uint8_t) mant;
+  }
+
+  return code;
+}
+
+/*
+  RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+  
+  If QQIC < 128,  QQI = QQIC
+  If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3)
+  
+  0 1 2 3 4 5 6 7
+  +-+-+-+-+-+-+-+-+
+  |1| exp | mant  |
+  +-+-+-+-+-+-+-+-+
+*/
+uint16_t igmp_msg_decode8to16(uint8_t code)
+{
+  uint16_t value;
+
+  if (code < 128) {
+    value = code;
+  }
+  else {
+    uint16_t mant = (code & 0x0F);
+    uint8_t  exp  = (code & 0x70) >> 4;
+    value = (mant | 0x10) << (exp + 3);
+  }
+
+  return value;
+}
+
+void pim_pkt_dump(const char *label, const uint8_t *buf, int size)
+{
+  char dump_buf[1000];
+  int i = 0;
+  int j = 0;
+
+  for (; i < size; ++i, j += 2) {
+    int left = sizeof(dump_buf) - j;
+    if (left < 4) {
+      if (left > 1) {
+       strcat(dump_buf + j, "!"); /* mark as truncated */
+      }
+      break;
+    }
+    snprintf(dump_buf + j, left, "%02x", buf[i]);
+  }
+
+  zlog_debug("%s: pkt dump size=%d: %s",
+            label,
+            size,
+            dump_buf);
+}
diff --git a/pimd/pim_util.h b/pimd/pim_util.h
new file mode 100644 (file)
index 0000000..a8613e2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_UTIL_H
+#define PIM_UTIL_H
+
+#include <stdint.h>
+
+#include <zebra.h>
+
+#include "checksum.h"
+
+uint8_t igmp_msg_encode16to8(uint16_t value);
+uint16_t igmp_msg_decode8to16(uint8_t code);
+
+void pim_pkt_dump(const char *label, const uint8_t *buf, int size);
+
+#endif /* PIM_UTIL_H */
diff --git a/pimd/pim_version.c b/pimd/pim_version.c
new file mode 100644 (file)
index 0000000..f3a5ee3
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "pim_version.h"
+
+const char * const PIMD_VERSION = PIMD_VERSION_STR;
diff --git a/pimd/pim_version.h b/pimd/pim_version.h
new file mode 100644 (file)
index 0000000..ef9f370
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_VERSION_H
+#define PIM_VERSION_H
+
+#define PIMD_VERSION_STR "0.166"
+
+const char * const PIMD_VERSION;
+
+#endif /* PIM_VERSION_H */
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
new file mode 100644 (file)
index 0000000..56de83c
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+
+#include "pimd.h"
+#include "pim_vty.h"
+#include "pim_iface.h"
+#include "pim_cmd.h"
+#include "pim_str.h"
+#include "pim_ssmpingd.h"
+#include "pim_pim.h"
+#include "pim_static.h"
+
+int pim_debug_config_write(struct vty *vty)
+{
+  int writes = 0;
+
+  if (PIM_DEBUG_IGMP_EVENTS) {
+    vty_out(vty, "debug igmp events%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_IGMP_PACKETS) {
+    vty_out(vty, "debug igmp packets%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_IGMP_TRACE) {
+    vty_out(vty, "debug igmp trace%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_MROUTE) {
+    vty_out(vty, "debug mroute%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_PIM_EVENTS) {
+    vty_out(vty, "debug pim events%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_PIM_PACKETS) {
+    vty_out(vty, "debug pim packets%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
+    vty_out(vty, "debug pim packet-dump send%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_PIM_PACKETDUMP_RECV) {
+    vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE);
+    ++writes;
+  }
+  if (PIM_DEBUG_PIM_TRACE) {
+    vty_out(vty, "debug pim trace%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_ZEBRA) {
+    vty_out(vty, "debug pim zebra%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (PIM_DEBUG_SSMPINGD) {
+    vty_out(vty, "debug ssmpingd%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  return writes;
+}
+
+int pim_global_config_write(struct vty *vty)
+{
+  int writes = 0;
+
+  if (PIM_MROUTE_IS_ENABLED) {
+    vty_out(vty, "%s%s", PIM_CMD_IP_MULTICAST_ROUTING, VTY_NEWLINE);
+    ++writes;
+  }
+
+  if (qpim_ssmpingd_list) {
+    struct listnode *node;
+    struct ssmpingd_sock *ss;
+    vty_out(vty, "!%s", VTY_NEWLINE);
+    ++writes;
+    for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
+      char source_str[100];
+      pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+      vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE);
+      ++writes;
+    }
+  }
+
+  return writes;
+}
+
+int pim_interface_config_write(struct vty *vty)
+{
+  int writes = 0;
+  struct listnode *node;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+
+    /* IF name */
+    vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+    ++writes;
+
+    if (ifp->info) {
+      struct pim_interface *pim_ifp = ifp->info;
+
+      /* IF ip pim ssm */
+      if (PIM_IF_TEST_PIM(pim_ifp->options)) {
+       vty_out(vty, " ip pim ssm%s", VTY_NEWLINE);
+       ++writes;
+      }
+
+      /* IF ip pim drpriority */
+      if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) {
+       vty_out(vty, " ip pim drpriority %d%s", pim_ifp->pim_dr_priority,
+               VTY_NEWLINE);
+       ++writes;
+      }
+
+      /* IF ip pim hello */
+      if (pim_ifp->pim_hello_period != PIM_DEFAULT_HELLO_PERIOD) {
+       vty_out(vty, " ip pim hello %d", pim_ifp->pim_hello_period);
+       if (pim_ifp->pim_default_holdtime != -1)
+         vty_out(vty, " %d", pim_ifp->pim_default_holdtime);
+       vty_out(vty, "%s", VTY_NEWLINE);
+      }
+
+      /* IF ip igmp */
+      if (PIM_IF_TEST_IGMP(pim_ifp->options)) {
+       vty_out(vty, " ip igmp%s", VTY_NEWLINE);
+       ++writes;
+      }
+
+      /* IF ip igmp query-interval */
+      if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
+       {
+         vty_out(vty, " %s %d%s",
+                 PIM_CMD_IP_IGMP_QUERY_INTERVAL,
+                 pim_ifp->igmp_default_query_interval,
+                 VTY_NEWLINE);
+         ++writes;
+       }
+
+      /* IF ip igmp query-max-response-time */
+      if (pim_ifp->igmp_query_max_response_time_dsec != IGMP_QUERY_MAX_RESPONSE_TIME_DSEC)
+       {
+         vty_out(vty, " %s %d%s",
+                 PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,
+                 pim_ifp->igmp_query_max_response_time_dsec,
+                 VTY_NEWLINE);
+         ++writes;
+       }
+
+      /* IF ip igmp join */
+      if (pim_ifp->igmp_join_list) {
+       struct listnode *node;
+       struct igmp_join *ij;
+       for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) {
+         char group_str[100];
+         char source_str[100];
+         pim_inet4_dump("<grp?>", ij->group_addr, group_str, sizeof(group_str));
+         pim_inet4_dump("<src?>", ij->source_addr, source_str, sizeof(source_str));
+         vty_out(vty, " ip igmp join %s %s%s",
+                 group_str, source_str,
+                 VTY_NEWLINE);
+         ++writes;
+       }
+      }
+
+       writes += pim_static_write_mroute (vty, ifp);
+    }
+    vty_out(vty, "!%s", VTY_NEWLINE);
+    ++writes;
+  }
+
+  return writes;
+}
diff --git a/pimd/pim_vty.h b/pimd/pim_vty.h
new file mode 100644 (file)
index 0000000..904ee55
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_VTY_H
+#define PIM_VTY_H
+
+#include "vty.h"
+
+int pim_debug_config_write(struct vty *vty);
+int pim_global_config_write(struct vty *vty);
+int pim_interface_config_write(struct vty *vty);
+
+#endif /* PIM_VTY_H */
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
new file mode 100644 (file)
index 0000000..efff100
--- /dev/null
@@ -0,0 +1,1300 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "zebra/rib.h"
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_zebra.h"
+#include "pim_iface.h"
+#include "pim_str.h"
+#include "pim_oil.h"
+#include "pim_rpf.h"
+#include "pim_time.h"
+#include "pim_join.h"
+#include "pim_zlookup.h"
+#include "pim_ifchannel.h"
+
+#undef PIM_DEBUG_IFADDR_DUMP
+#define PIM_DEBUG_IFADDR_DUMP
+
+static int fib_lookup_if_vif_index(struct in_addr addr);
+static int del_oif(struct channel_oil *channel_oil,
+                  struct interface *oif,
+                  uint32_t proto_mask);
+
+/* Router-id update message from zebra. */
+static int pim_router_id_update_zebra(int command, struct zclient *zclient,
+                                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct prefix router_id;
+
+  zebra_router_id_update_read(zclient->ibuf, &router_id);
+
+  return 0;
+}
+
+static int pim_zebra_if_add(int command, struct zclient *zclient,
+                           zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  /*
+    zebra api adds/dels interfaces using the same call
+    interface_add_read below, see comments in lib/zclient.c
+  */
+  ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
+  if (!ifp)
+    return 0;
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+              ifp->mtu, if_is_operative(ifp));
+  }
+
+  if (if_is_operative(ifp))
+    pim_if_addr_add_all(ifp);
+
+  return 0;
+}
+
+static int pim_zebra_if_del(int command, struct zclient *zclient,
+                           zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  /*
+    zebra api adds/dels interfaces using the same call
+    interface_add_read below, see comments in lib/zclient.c
+    
+    comments in lib/zclient.c seem to indicate that calling
+    zebra_interface_add_read is the correct call, but that
+    results in an attemted out of bounds read which causes
+    pimd to assert. Other clients use zebra_interface_state_read
+    and it appears to work just fine.
+  */
+  ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+  if (!ifp)
+    return 0;
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+              ifp->mtu, if_is_operative(ifp));
+  }
+
+  if (!if_is_operative(ifp))
+    pim_if_addr_del_all(ifp);
+
+  return 0;
+}
+
+static int pim_zebra_if_state_up(int command, struct zclient *zclient,
+                                zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  /*
+    zebra api notifies interface up/down events by using the same call
+    zebra_interface_state_read below, see comments in lib/zclient.c
+  */
+  ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+  if (!ifp)
+    return 0;
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+              ifp->mtu, if_is_operative(ifp));
+  }
+
+  if (if_is_operative(ifp)) {
+    /*
+      pim_if_addr_add_all() suffices for bringing up both IGMP and PIM
+    */
+    pim_if_addr_add_all(ifp);
+  }
+
+  return 0;
+}
+
+static int pim_zebra_if_state_down(int command, struct zclient *zclient,
+                                  zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  /*
+    zebra api notifies interface up/down events by using the same call
+    zebra_interface_state_read below, see comments in lib/zclient.c
+  */
+  ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
+  if (!ifp)
+    return 0;
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
+              __PRETTY_FUNCTION__,
+              ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
+              ifp->mtu, if_is_operative(ifp));
+  }
+
+  if (!if_is_operative(ifp)) {
+    /*
+      pim_if_addr_del_all() suffices for shutting down IGMP,
+      but not for shutting down PIM
+    */
+    pim_if_addr_del_all(ifp);
+
+    /*
+      pim_sock_delete() closes the socket, stops read and timer threads,
+      and kills all neighbors.
+    */
+    if (ifp->info) {
+      pim_sock_delete(ifp, "link down");
+    }
+  }
+
+  return 0;
+}
+
+#ifdef PIM_DEBUG_IFADDR_DUMP
+static void dump_if_address(struct interface *ifp)
+{
+  struct connected *ifc;
+  struct listnode *node;
+
+  zlog_debug("%s %s: interface %s addresses:",
+            __FILE__, __PRETTY_FUNCTION__,
+            ifp->name);
+  
+  for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+    struct prefix *p = ifc->address;
+    
+    if (p->family != AF_INET)
+      continue;
+    
+    zlog_debug("%s %s: interface %s address %s %s",
+              __FILE__, __PRETTY_FUNCTION__,
+              ifp->name,
+              inet_ntoa(p->u.prefix4),
+              CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? 
+              "secondary" : "primary");
+  }
+}
+#endif
+
+static int pim_zebra_if_address_add(int command, struct zclient *zclient,
+                                   zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct prefix *p;
+
+  /*
+    zebra api notifies address adds/dels events by using the same call
+    interface_add_read below, see comments in lib/zclient.c
+
+    zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
+    will add address to interface list by calling
+    connected_add_by_prefix()
+  */
+  c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+  if (!c)
+    return 0;
+
+  p = c->address;
+  if (p->family != AF_INET)
+    return 0;
+  
+  if (PIM_DEBUG_ZEBRA) {
+    char buf[BUFSIZ];
+    prefix2str(p, buf, BUFSIZ);
+    zlog_debug("%s: %s connected IP address %s flags %u %s",
+              __PRETTY_FUNCTION__,
+              c->ifp->name, buf, c->flags,
+              CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary");
+    
+#ifdef PIM_DEBUG_IFADDR_DUMP
+    dump_if_address(c->ifp);
+#endif
+  }
+
+  if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
+    /* trying to add primary address */
+
+    struct in_addr primary_addr = pim_find_primary_addr(c->ifp);
+    if (primary_addr.s_addr != p->u.prefix4.s_addr) {
+      if (PIM_DEBUG_ZEBRA) {
+       /* but we had a primary address already */
+
+       char buf[BUFSIZ];
+       char old[100];
+
+       prefix2str(p, buf, BUFSIZ);
+       pim_inet4_dump("<old?>", primary_addr, old, sizeof(old));
+
+       zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s",
+                 __PRETTY_FUNCTION__,
+                 c->ifp->name, old, buf);
+      }
+      SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
+    }
+  }
+
+  pim_if_addr_add(c);
+
+  return 0;
+}
+
+static int pim_zebra_if_address_del(int command, struct zclient *client,
+                                   zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct prefix *p;
+
+  /*
+    zebra api notifies address adds/dels events by using the same call
+    interface_add_read below, see comments in lib/zclient.c
+
+    zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
+    will remove address from interface list by calling
+    connected_delete_by_prefix()
+  */
+  c = zebra_interface_address_read(command, client->ibuf, vrf_id);
+  if (!c)
+    return 0;
+  
+  p = c->address;
+  if (p->family != AF_INET)
+    return 0;
+  
+  if (PIM_DEBUG_ZEBRA) {
+    char buf[BUFSIZ];
+    prefix2str(p, buf, BUFSIZ);
+    zlog_debug("%s: %s disconnected IP address %s flags %u %s",
+              __PRETTY_FUNCTION__,
+              c->ifp->name, buf, c->flags,
+              CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary");
+    
+#ifdef PIM_DEBUG_IFADDR_DUMP
+    dump_if_address(c->ifp);
+#endif
+  }
+
+  pim_if_addr_del(c, 0);
+  
+  return 0;
+}
+
+static void scan_upstream_rpf_cache()
+{
+  struct listnode     *up_node;
+  struct listnode     *up_nextnode;
+  struct pim_upstream *up;
+
+  for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
+    struct pim_rpf             old_rpf;
+    enum pim_rpf_result rpf_result;
+
+    rpf_result = pim_rpf_update(up, &old_rpf);
+    if (rpf_result == PIM_RPF_FAILURE)
+      continue;
+
+    if (rpf_result == PIM_RPF_CHANGED) {
+      
+      if (up->join_state == PIM_UPSTREAM_JOINED) {
+       
+       /*
+         RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Messages
+         
+         Transitions from Joined State
+         
+         RPF'(S,G) changes not due to an Assert
+         
+         The upstream (S,G) state machine remains in Joined
+         state. Send Join(S,G) to the new upstream neighbor, which is
+         the new value of RPF'(S,G).  Send Prune(S,G) to the old
+         upstream neighbor, which is the old value of RPF'(S,G).  Set
+         the Join Timer (JT) to expire after t_periodic seconds.
+       */
+
+    
+       /* send Prune(S,G) to the old upstream neighbor */
+       pim_joinprune_send(old_rpf.source_nexthop.interface,
+                          old_rpf.rpf_addr,
+                          up->source_addr,
+                          up->group_addr,
+                          0 /* prune */);
+       
+       /* send Join(S,G) to the current upstream neighbor */
+       pim_joinprune_send(up->rpf.source_nexthop.interface,
+                          up->rpf.rpf_addr,
+                          up->source_addr,
+                          up->group_addr,
+                          1 /* join */);
+
+       pim_upstream_join_timer_restart(up);
+      } /* up->join_state == PIM_UPSTREAM_JOINED */
+
+      /* FIXME can join_desired actually be changed by pim_rpf_update()
+        returning PIM_RPF_CHANGED ? */
+      pim_upstream_update_join_desired(up);
+
+    } /* PIM_RPF_CHANGED */
+
+  } /* for (qpim_upstream_list) */
+  
+}
+
+void pim_scan_oil()
+{
+  struct listnode    *node;
+  struct listnode    *nextnode;
+  struct channel_oil *c_oil;
+
+  qpim_scan_oil_last = pim_time_monotonic_sec();
+  ++qpim_scan_oil_events;
+
+  for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) {
+    int old_vif_index;
+    int input_iface_vif_index = fib_lookup_if_vif_index(c_oil->oil.mfcc_origin);
+    if (input_iface_vif_index < 1) {
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_warn("%s %s: could not find input interface for (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str, group_str);
+      continue;
+    }
+
+    if (input_iface_vif_index == c_oil->oil.mfcc_parent) {
+      /* RPF unchanged */
+      continue;
+    }
+
+    if (PIM_DEBUG_ZEBRA) {
+      struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
+      struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
+                __FILE__, __PRETTY_FUNCTION__,
+                source_str, group_str,
+                old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+                new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+    }
+
+    /* new iif loops to existing oif ? */
+    if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) {
+      struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+
+      if (PIM_DEBUG_ZEBRA) {
+       char source_str[100];
+       char group_str[100];
+       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+       zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  source_str, group_str,
+                  new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+      }
+
+      del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
+    }
+
+    /* update iif vif_index */
+    old_vif_index = c_oil->oil.mfcc_parent;
+    c_oil->oil.mfcc_parent = input_iface_vif_index;
+
+    /* update kernel multicast forwarding cache (MFC) */
+    if (pim_mroute_add(&c_oil->oil)) {
+      /* just log warning */
+      struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
+      struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
+      char source_str[100];
+      char group_str[100]; 
+      pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
+                __FILE__, __PRETTY_FUNCTION__,
+                source_str, group_str,
+                old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
+                new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
+      continue;
+    }
+
+  } /* for (qpim_channel_oil_list) */
+}
+
+static int on_rpf_cache_refresh(struct thread *t)
+{
+  zassert(t);
+  zassert(qpim_rpf_cache_refresher);
+
+  qpim_rpf_cache_refresher = 0;
+
+  /* update PIM protocol state */
+  scan_upstream_rpf_cache();
+
+  /* update kernel multicast forwarding cache (MFC) */
+  pim_scan_oil();
+
+  qpim_rpf_cache_refresh_last = pim_time_monotonic_sec();
+  ++qpim_rpf_cache_refresh_events;
+
+  return 0;
+}
+
+static void sched_rpf_cache_refresh()
+{
+  ++qpim_rpf_cache_refresh_requests;
+
+  if (qpim_rpf_cache_refresher) {
+    /* Refresh timer is already running */
+    return;
+  }
+
+  /* Start refresh timer */
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s: triggering %ld msec timer",
+               __PRETTY_FUNCTION__,
+               qpim_rpf_cache_refresh_delay_msec);
+  }
+
+  THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher,
+                       on_rpf_cache_refresh,
+                       0, qpim_rpf_cache_refresh_delay_msec);
+}
+
+static int redist_read_ipv4_route(int command, struct zclient *zclient,
+                                 zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv4 api;
+  ifindex_t ifindex;
+  struct in_addr nexthop;
+  struct prefix_ipv4 p;
+  int min_len = 4;
+
+  if (length < min_len) {
+    zlog_warn("%s %s: short buffer: length=%d min=%d",
+             __FILE__, __PRETTY_FUNCTION__,
+             length, min_len);
+    return -1;
+  }
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  nexthop.s_addr = 0;
+
+  /* Type, flags, message. */
+  api.type = stream_getc(s);
+  api.flags = stream_getc(s);
+  api.message = stream_getc(s);
+
+  /* IPv4 prefix length. */
+  memset(&p, 0, sizeof(struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = stream_getc(s);
+
+  min_len +=
+    PSIZE(p.prefixlen) +
+    CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 +
+    CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 +
+    CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 +
+    CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0;
+
+  if (PIM_DEBUG_ZEBRA) {
+    zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s",
+              __FILE__, __PRETTY_FUNCTION__,
+              length, min_len,
+              CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
+              CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
+              CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
+              CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
+  }
+
+  if (length < min_len) {
+    zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s",
+             __FILE__, __PRETTY_FUNCTION__,
+             length, min_len,
+             CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
+             CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
+             CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
+             CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
+    return -1;
+  }
+
+  /* IPv4 prefix. */
+  stream_get(&p.prefix, s, PSIZE(p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
+    api.nexthop_num = stream_getc(s);
+    nexthop.s_addr = stream_get_ipv4(s);
+  }
+  if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) {
+    api.ifindex_num = stream_getc(s);
+    ifindex = stream_getl(s);
+  }
+
+  api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ?
+    stream_getc(s) :
+    0;
+
+  api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ?
+    stream_getl(s) :
+    0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  switch (command) {
+  case ZEBRA_IPV4_ROUTE_ADD:
+    if (PIM_DEBUG_ZEBRA) {
+      char buf[2][INET_ADDRSTRLEN];
+      zlog_debug("%s: add %s %s/%d "
+                "nexthop %s ifindex %d metric%s %u distance%s %u",
+                __PRETTY_FUNCTION__,
+                zebra_route_string(api.type),
+                inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+                p.prefixlen,
+                inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+                ifindex,
+                CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
+                api.metric,
+                CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
+                api.distance);
+    }
+    break;
+  case ZEBRA_IPV4_ROUTE_DELETE:
+    if (PIM_DEBUG_ZEBRA) {
+      char buf[2][INET_ADDRSTRLEN];
+      zlog_debug("%s: delete %s %s/%d "
+                "nexthop %s ifindex %d metric%s %u distance%s %u",
+                __PRETTY_FUNCTION__,
+                zebra_route_string(api.type),
+                inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
+                p.prefixlen,
+                inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
+                ifindex,
+                CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
+                api.metric,
+                CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
+                api.distance);
+    }
+    break;
+  default:
+    zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command);
+    return -1;
+  }
+
+  sched_rpf_cache_refresh();
+
+  return 0;
+}
+
+static void pim_zebra_connected(struct zclient *zclient)
+{
+  zclient_send_requests(zclient, VRF_DEFAULT);
+}
+
+void pim_zebra_init (struct thread_master *master, char *zebra_sock_path)
+{
+  int i;
+
+  if (zebra_sock_path)
+    zclient_serv_path_set(zebra_sock_path);
+
+#ifdef HAVE_TCP_ZEBRA
+  zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT);
+#else
+  zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", zclient_serv_path_get());
+#endif
+
+  /* Socket for receiving updates from Zebra daemon */
+  qpim_zclient_update = zclient_new (master);
+
+  qpim_zclient_update->zebra_connected          = pim_zebra_connected;
+  qpim_zclient_update->router_id_update         = pim_router_id_update_zebra;
+  qpim_zclient_update->interface_add            = pim_zebra_if_add;
+  qpim_zclient_update->interface_delete         = pim_zebra_if_del;
+  qpim_zclient_update->interface_up             = pim_zebra_if_state_up;
+  qpim_zclient_update->interface_down           = pim_zebra_if_state_down;
+  qpim_zclient_update->interface_address_add    = pim_zebra_if_address_add;
+  qpim_zclient_update->interface_address_delete = pim_zebra_if_address_del;
+  qpim_zclient_update->ipv4_route_add           = redist_read_ipv4_route;
+  qpim_zclient_update->ipv4_route_delete        = redist_read_ipv4_route;
+
+  zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM);
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_info("zclient_init cleared redistribution request");
+  }
+
+  zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM);
+
+  /* Request all redistribution */
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+    if (i == qpim_zclient_update->redist_default)
+      continue;
+    vrf_bitmap_set(qpim_zclient_update->redist[i], VRF_DEFAULT);
+    if (PIM_DEBUG_PIM_TRACE) {
+      zlog_debug("%s: requesting redistribution for %s (%i)", 
+                __PRETTY_FUNCTION__, zebra_route_string(i), i);
+    }
+  }
+
+  /* Request default information */
+  vrf_bitmap_set(qpim_zclient_update->default_information, VRF_DEFAULT);
+  if (PIM_DEBUG_PIM_TRACE) {
+    zlog_info("%s: requesting default information redistribution",
+             __PRETTY_FUNCTION__);
+
+    zlog_notice("%s: zclient update socket initialized",
+               __PRETTY_FUNCTION__);
+  }
+
+  zassert(!qpim_zclient_lookup);
+  qpim_zclient_lookup = zclient_lookup_new();
+  zassert(qpim_zclient_lookup);
+}
+
+void igmp_anysource_forward_start(struct igmp_group *group)
+{
+  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+  zassert(group->group_filtermode_isexcl);
+  zassert(listcount(group->group_source_list) < 1);
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_debug("%s %s: UNIMPLEMENTED",
+              __FILE__, __PRETTY_FUNCTION__);
+  }
+}
+
+void igmp_anysource_forward_stop(struct igmp_group *group)
+{
+  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+  zassert((!group->group_filtermode_isexcl) || (listcount(group->group_source_list) > 0));
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    zlog_debug("%s %s: UNIMPLEMENTED",
+              __FILE__, __PRETTY_FUNCTION__);
+  }
+}
+
+static int fib_lookup_if_vif_index(struct in_addr addr)
+{
+  struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
+  int num_ifindex;
+  int vif_index;
+  ifindex_t first_ifindex;
+
+  num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
+                                      PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
+                                      PIM_NEXTHOP_LOOKUP_MAX);
+  if (num_ifindex < 1) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: could not find nexthop ifindex for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             addr_str);
+    return -1;
+  }
+  
+  first_ifindex = nexthop_tab[0].ifindex;
+  
+  if (num_ifindex > 1) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
+              __FILE__, __PRETTY_FUNCTION__,
+              num_ifindex, addr_str, first_ifindex);
+    /* debug warning only, do not return */
+  }
+  
+  if (PIM_DEBUG_ZEBRA) {
+    char addr_str[100];
+    pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
+    zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
+              __FILE__, __PRETTY_FUNCTION__,
+              first_ifindex, ifindex2ifname(first_ifindex), addr_str);
+  }
+
+  vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
+
+  if (vif_index < 1) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             vif_index, addr_str);
+    return -2;
+  }
+
+  zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+
+  if (vif_index > qpim_mroute_oif_highest_vif_index) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
+
+    zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
+             __FILE__, __PRETTY_FUNCTION__,
+             ifindex2ifname(vif_index),
+             vif_index);
+
+    return -3;
+  }
+
+  return vif_index;
+}
+
+static int add_oif(struct channel_oil *channel_oil,
+                  struct interface *oif,
+                  uint32_t proto_mask)
+{
+  struct pim_interface *pim_ifp;
+  int old_ttl;
+
+  zassert(channel_oil);
+
+  pim_ifp = oif->info;
+
+  if (PIM_DEBUG_MROUTE) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
+              __FILE__, __PRETTY_FUNCTION__,
+              source_str, group_str,
+              proto_mask, oif->name, pim_ifp->mroute_vif_index);
+  }
+
+  if (pim_ifp->mroute_vif_index < 1) {
+    zlog_warn("%s %s: interface %s vif_index=%d < 1",
+             __FILE__, __PRETTY_FUNCTION__,
+             oif->name, pim_ifp->mroute_vif_index);
+    return -1;
+  }
+
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+  /*
+    Prevent creating MFC entry with OIF=IIF.
+
+    This is a protection against implementation mistakes.
+
+    PIM protocol implicitely ensures loopfree multicast topology.
+
+    IGMP must be protected against adding looped MFC entries created
+    by both source and receiver attached to the same interface. See
+    TODO T22.
+  */
+  if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             proto_mask, oif->name, pim_ifp->mroute_vif_index,
+             source_str, group_str);
+    return -2;
+  }
+#endif
+
+  zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+  zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
+
+  /* Prevent single protocol from subscribing same interface to
+     channel (S,G) multiple times */
+  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             proto_mask, oif->name, pim_ifp->mroute_vif_index,
+             channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+             source_str, group_str);
+    return -3;
+  }
+
+  /* Allow other protocol to request subscription of same interface to
+     channel (S,G) multiple times, by silently ignoring further
+     requests */
+  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
+
+    /* Check the OIF really exists before returning, and only log
+       warning otherwise */
+    if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
+      char group_str[100]; 
+      char source_str[100];
+      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               proto_mask, oif->name, pim_ifp->mroute_vif_index,
+               channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+               source_str, group_str);
+    }
+
+    return 0;
+  }
+
+  old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
+
+  if (old_ttl > 0) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             oif->name, pim_ifp->mroute_vif_index,
+             source_str, group_str);
+    return -4;
+  }
+
+  channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
+
+  if (pim_mroute_add(&channel_oil->oil)) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             oif->name, pim_ifp->mroute_vif_index,
+             source_str, group_str);
+
+    channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
+    return -5;
+  }
+
+  channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
+  ++channel_oil->oil_size;
+  channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
+
+  if (PIM_DEBUG_MROUTE) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
+              __FILE__, __PRETTY_FUNCTION__,
+              source_str, group_str,
+              proto_mask, oif->name, pim_ifp->mroute_vif_index);
+  }
+
+  return 0;
+}
+
+static int del_oif(struct channel_oil *channel_oil,
+                  struct interface *oif,
+                  uint32_t proto_mask)
+{
+  struct pim_interface *pim_ifp;
+  int old_ttl;
+
+  zassert(channel_oil);
+
+  pim_ifp = oif->info;
+
+  zassert(pim_ifp->mroute_vif_index >= 1);
+  zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
+  zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
+
+  if (PIM_DEBUG_MROUTE) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
+              __FILE__, __PRETTY_FUNCTION__,
+              source_str, group_str,
+              proto_mask, oif->name, pim_ifp->mroute_vif_index);
+  }
+
+  /* Prevent single protocol from unsubscribing same interface from
+     channel (S,G) multiple times */
+  if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             proto_mask, oif->name, pim_ifp->mroute_vif_index,
+             channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+             source_str, group_str);
+    return -2;
+  }
+
+  /* Mark that protocol is no longer interested in this OIF */
+  channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
+
+  /* Allow multiple protocols to unsubscribe same interface from
+     channel (S,G) multiple times, by silently ignoring requests while
+     there is at least one protocol interested in the channel */
+  if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
+
+    /* Check the OIF keeps existing before returning, and only log
+       warning otherwise */
+    if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
+      char group_str[100]; 
+      char source_str[100];
+      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               proto_mask, oif->name, pim_ifp->mroute_vif_index,
+               channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
+               source_str, group_str);
+    }
+
+    return 0;
+  }
+
+  old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
+
+  if (old_ttl < 1) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             oif->name, pim_ifp->mroute_vif_index,
+             source_str, group_str);
+    return -3;
+  }
+
+  channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+
+  if (pim_mroute_add(&channel_oil->oil)) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
+             __FILE__, __PRETTY_FUNCTION__,
+             oif->name, pim_ifp->mroute_vif_index,
+             source_str, group_str);
+    
+    channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
+    return -4;
+  }
+
+  --channel_oil->oil_size;
+
+  if (channel_oil->oil_size < 1) {
+    if (pim_mroute_del(&channel_oil->oil)) {
+      /* just log a warning in case of failure */
+      char group_str[100]; 
+      char source_str[100];
+      pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+      zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str, group_str);
+    }
+  }
+
+  if (PIM_DEBUG_MROUTE) {
+    char group_str[100]; 
+    char source_str[100];
+    pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
+    zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
+              __FILE__, __PRETTY_FUNCTION__,
+              source_str, group_str,
+              proto_mask, oif->name, pim_ifp->mroute_vif_index);
+  }
+
+  return 0;
+}
+
+void igmp_source_forward_start(struct igmp_source *source)
+{
+  struct igmp_group *group;
+  int result;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char source_str[100];
+    char group_str[100]; 
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+              __PRETTY_FUNCTION__,
+              source_str, group_str,
+              source->source_group->group_igmp_sock->fd,
+              source->source_group->group_igmp_sock->interface->name,
+              IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+  }
+
+  /* Prevent IGMP interface from installing multicast route multiple
+     times */
+  if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+    return;
+  }
+
+  group = source->source_group;
+
+  if (!source->source_channel_oil) {
+    struct pim_interface *pim_oif;
+    int input_iface_vif_index = fib_lookup_if_vif_index(source->source_addr);
+    if (input_iface_vif_index < 1) {
+      char source_str[100];
+      pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+      zlog_warn("%s %s: could not find input interface for source %s",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str);
+      return;
+    }
+
+    /*
+      Protect IGMP against adding looped MFC entries created by both
+      source and receiver attached to the same interface. See TODO
+      T22.
+    */
+    pim_oif = source->source_group->group_igmp_sock->interface->info;
+    if (!pim_oif) {
+      zlog_warn("%s: multicast not enabled on oif=%s ?",
+               __PRETTY_FUNCTION__,
+               source->source_group->group_igmp_sock->interface->name);
+      return;
+    }
+    if (pim_oif->mroute_vif_index < 1) {
+      zlog_warn("%s %s: oif=%s vif_index=%d < 1",
+               __FILE__, __PRETTY_FUNCTION__,
+               source->source_group->group_igmp_sock->interface->name,
+               pim_oif->mroute_vif_index);
+      return;
+    }
+    if (input_iface_vif_index == pim_oif->mroute_vif_index) {
+      /* ignore request for looped MFC entry */
+      if (PIM_DEBUG_IGMP_TRACE) {
+       char source_str[100];
+       char group_str[100]; 
+       pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+       pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+       zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
+                  __PRETTY_FUNCTION__,
+                  source_str, group_str,
+                  source->source_group->group_igmp_sock->fd,
+                  source->source_group->group_igmp_sock->interface->name,
+                  input_iface_vif_index);
+      }
+      return;
+    }
+
+    source->source_channel_oil = pim_channel_oil_add(group->group_addr,
+                                                    source->source_addr,
+                                                    input_iface_vif_index);
+    if (!source->source_channel_oil) {
+      char group_str[100]; 
+      char source_str[100];
+      pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+      zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str, group_str);
+      return;
+    }
+  }
+
+  result = add_oif(source->source_channel_oil,
+                  group->group_igmp_sock->interface,
+                  PIM_OIF_FLAG_PROTO_IGMP);
+  if (result) {
+    zlog_warn("%s: add_oif() failed with return=%d",
+             __func__, result);
+    return;
+  }
+
+  /*
+    Feed IGMPv3-gathered local membership information into PIM
+    per-interface (S,G) state.
+   */
+  pim_ifchannel_local_membership_add(group->group_igmp_sock->interface,
+                                    source->source_addr, group->group_addr);
+
+  IGMP_SOURCE_DO_FORWARDING(source->source_flags);
+}
+
+/*
+  igmp_source_forward_stop: stop fowarding, but keep the source
+  igmp_source_delete:       stop fowarding, and delete the source
+ */
+void igmp_source_forward_stop(struct igmp_source *source)
+{
+  struct igmp_group *group;
+  int result;
+
+  if (PIM_DEBUG_IGMP_TRACE) {
+    char source_str[100];
+    char group_str[100]; 
+    pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
+              __PRETTY_FUNCTION__,
+              source_str, group_str,
+              source->source_group->group_igmp_sock->fd,
+              source->source_group->group_igmp_sock->interface->name,
+              IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+  }
+
+  /* Prevent IGMP interface from removing multicast route multiple
+     times */
+  if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+    return;
+  }
+
+  group = source->source_group;
+
+  /*
+   It appears that in certain circumstances that 
+   igmp_source_forward_stop is called when IGMP forwarding
+   was not enabled in oif_flags for this outgoing interface.
+   Possibly because of multiple calls. When that happens, we
+   enter the below if statement and this function returns early
+   which in turn triggers the calling function to assert.
+   Making the call to del_oif and ignoring the return code 
+   fixes the issue without ill effect, similar to 
+   pim_forward_stop below.   
+  */
+  result = del_oif(source->source_channel_oil,
+                  group->group_igmp_sock->interface,
+                  PIM_OIF_FLAG_PROTO_IGMP);
+  if (result) {
+    zlog_warn("%s: del_oif() failed with return=%d",
+             __func__, result);
+    return;
+  }
+
+  /*
+    Feed IGMPv3-gathered local membership information into PIM
+    per-interface (S,G) state.
+   */
+  pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
+                                    source->source_addr, group->group_addr);
+
+  IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
+}
+
+void pim_forward_start(struct pim_ifchannel *ch)
+{
+  struct pim_upstream *up = ch->upstream;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char source_str[100];
+    char group_str[100]; 
+    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+              __PRETTY_FUNCTION__,
+              source_str, group_str, ch->interface->name);
+  }
+
+  if (!up->channel_oil) {
+    int input_iface_vif_index = fib_lookup_if_vif_index(up->source_addr);
+    if (input_iface_vif_index < 1) {
+      char source_str[100];
+      pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
+      zlog_warn("%s %s: could not find input interface for source %s",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str);
+      return;
+    }
+
+    up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
+                                         input_iface_vif_index);
+    if (!up->channel_oil) {
+      char group_str[100]; 
+      char source_str[100];
+      pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
+      pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
+      zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
+               __FILE__, __PRETTY_FUNCTION__,
+               source_str, group_str);
+      return;
+    }
+  }
+
+  add_oif(up->channel_oil,
+         ch->interface,
+         PIM_OIF_FLAG_PROTO_PIM);
+}
+
+void pim_forward_stop(struct pim_ifchannel *ch)
+{
+  struct pim_upstream *up = ch->upstream;
+
+  if (PIM_DEBUG_PIM_TRACE) {
+    char source_str[100];
+    char group_str[100]; 
+    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+    zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
+              __PRETTY_FUNCTION__,
+              source_str, group_str, ch->interface->name);
+  }
+
+  if (!up->channel_oil) {
+    char source_str[100];
+    char group_str[100]; 
+    pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
+    zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
+              __PRETTY_FUNCTION__,
+              source_str, group_str, ch->interface->name);
+
+    return;
+  }
+
+  del_oif(up->channel_oil,
+         ch->interface,
+         PIM_OIF_FLAG_PROTO_PIM);
+}
diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h
new file mode 100644 (file)
index 0000000..af5baef
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_ZEBRA_H
+#define PIM_ZEBRA_H
+
+#include "pim_igmp.h"
+#include "pim_ifchannel.h"
+
+void pim_zebra_init (struct thread_master *master, char *zebra_sock_path);
+
+void pim_scan_oil(void);
+
+void igmp_anysource_forward_start(struct igmp_group *group);
+void igmp_anysource_forward_stop(struct igmp_group *group);
+
+void igmp_source_forward_start(struct igmp_source *source);
+void igmp_source_forward_stop(struct igmp_source *source);
+
+void pim_forward_start(struct pim_ifchannel *ch);
+void pim_forward_stop(struct pim_ifchannel *ch);
+
+#endif /* PIM_ZEBRA_H */
diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c
new file mode 100644 (file)
index 0000000..770fbf7
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+#include "zebra/rib.h"
+
+#include "log.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "thread.h"
+
+#include "pimd.h"
+#include "pim_pim.h"
+#include "pim_str.h"
+#include "pim_zlookup.h"
+
+extern int zclient_debug;
+
+static void zclient_lookup_sched(struct zclient *zlookup, int delay);
+
+/* Connect to zebra for nexthop lookup. */
+static int zclient_lookup_connect(struct thread *t)
+{
+  struct zclient *zlookup;
+
+  zlookup = THREAD_ARG(t);
+  zlookup->t_connect = NULL;
+
+  if (zlookup->sock >= 0) {
+    return 0;
+  }
+
+  if (zclient_socket_connect(zlookup) < 0) {
+    ++zlookup->fail;
+    zlog_warn("%s: failure connecting zclient socket: failures=%d",
+             __PRETTY_FUNCTION__, zlookup->fail);
+  }
+  else {
+    zlookup->fail = 0; /* reset counter on connection */
+  }
+
+  zassert(!zlookup->t_connect);
+  if (zlookup->sock < 0) {
+    /* Since last connect failed, retry within 10 secs */
+    zclient_lookup_sched(zlookup, 10);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* Schedule connection with delay. */
+static void zclient_lookup_sched(struct zclient *zlookup, int delay)
+{
+  zassert(!zlookup->t_connect);
+
+  THREAD_TIMER_ON(master, zlookup->t_connect,
+                 zclient_lookup_connect,
+                 zlookup, delay);
+
+  zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
+             __PRETTY_FUNCTION__, delay);
+}
+
+/* Schedule connection for now. */
+static void zclient_lookup_sched_now(struct zclient *zlookup)
+{
+  zassert(!zlookup->t_connect);
+
+  zlookup->t_connect = thread_add_event(master, zclient_lookup_connect,
+                                       zlookup, 0);
+
+  zlog_notice("%s: zclient lookup immediate connection scheduled",
+             __PRETTY_FUNCTION__);
+}
+
+/* Schedule reconnection, if needed. */
+static void zclient_lookup_reconnect(struct zclient *zlookup)
+{
+  if (zlookup->t_connect) {
+    return;
+  }
+
+  zclient_lookup_sched_now(zlookup);
+}
+
+static void zclient_lookup_failed(struct zclient *zlookup)
+{
+  if (zlookup->sock >= 0) {
+    if (close(zlookup->sock)) {
+      zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock,
+               errno, safe_strerror(errno));
+    }
+    zlookup->sock = -1;
+  }
+
+  zclient_lookup_reconnect(zlookup);
+}
+
+struct zclient *zclient_lookup_new()
+{
+  struct zclient *zlookup;
+
+  zlookup = zclient_new (master);
+  if (!zlookup) {
+    zlog_err("%s: zclient_new() failure",
+            __PRETTY_FUNCTION__);
+    return 0;
+  }
+
+  zlookup->sock = -1;
+  zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
+  zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
+  zlookup->t_connect = 0;
+
+  zclient_lookup_sched_now(zlookup);
+
+  zlog_notice("%s: zclient lookup socket initialized",
+             __PRETTY_FUNCTION__);
+
+  return zlookup;
+}
+
+static int zclient_read_nexthop(struct zclient *zlookup,
+                               struct pim_zlookup_nexthop nexthop_tab[],
+                               const int tab_size,
+                               struct in_addr addr)
+{
+  int num_ifindex = 0;
+  struct stream *s;
+  const uint16_t MIN_LEN = 10; /* getipv4=4 getc=1 getl=4 getc=1 */
+  uint16_t length;
+  u_char marker;
+  u_char version;
+  uint16_t vrf_id;
+  uint16_t command;
+  struct in_addr raddr;
+  uint8_t distance;
+  uint32_t metric;
+  int nexthop_num;
+  int i, err;
+
+  if (PIM_DEBUG_ZEBRA) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_debug("%s: addr=%s", 
+              __PRETTY_FUNCTION__,
+              addr_str);
+  }
+
+  s = zlookup->ibuf;
+  stream_reset(s);
+
+  err = zclient_read_header (s, zlookup->sock, &length, &marker, &version,
+                             &vrf_id, &command);
+  if (err < 0) {
+    zlog_err("%s %s: zclient_read_header() failed",
+            __FILE__, __PRETTY_FUNCTION__);
+    zclient_lookup_failed(zlookup);
+    return -1;
+  }
+
+  if (length < MIN_LEN) {
+    zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d",
+            __FILE__, __PRETTY_FUNCTION__, length, MIN_LEN);
+    zclient_lookup_failed(zlookup);
+    return -2;
+  }
+  
+  if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB) {
+    zlog_err("%s: socket %d command mismatch: %d",
+            __func__, zlookup->sock, command);
+    return -5;
+  }
+
+  raddr.s_addr = stream_get_ipv4(s);
+
+  if (raddr.s_addr != addr.s_addr) {
+    char addr_str[100];
+    char raddr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    pim_inet4_dump("<raddr?>", raddr, raddr_str, sizeof(raddr_str));
+    zlog_warn("%s: address mismatch: addr=%s raddr=%s", 
+              __PRETTY_FUNCTION__,
+              addr_str, raddr_str);
+    /* warning only */
+  }
+
+  distance = stream_getc(s);
+  metric = stream_getl(s);
+  nexthop_num = stream_getc(s);
+
+  if (nexthop_num < 1) {
+    zlog_err("%s: socket %d bad nexthop_num=%d",
+            __func__, zlookup->sock, nexthop_num);
+    return -6;
+  }
+
+  length -= MIN_LEN;
+
+  for (i = 0; i < nexthop_num; ++i) {
+    enum nexthop_types_t nexthop_type;
+
+    if (length < 1) {
+      zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d",
+              __func__, zlookup->sock, length);
+      return -7;
+    }
+    
+    nexthop_type = stream_getc(s);
+    --length;
+
+    switch (nexthop_type) {
+    case ZEBRA_NEXTHOP_IFINDEX:
+    case ZEBRA_NEXTHOP_IFNAME:
+    case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+      if (num_ifindex >= tab_size) {
+       char addr_str[100];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+                __FILE__, __PRETTY_FUNCTION__,
+                (num_ifindex + 1), tab_size, addr_str);
+       return num_ifindex;
+      }
+      if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) {
+       if (length < 4) {
+         zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d",
+                  __func__, zlookup->sock, length);
+         return -8;
+       }
+       nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
+       length -= 4;
+      }
+      else {
+       nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
+      }
+      nexthop_tab[num_ifindex].ifindex           = stream_getl(s);
+      nexthop_tab[num_ifindex].protocol_distance = distance;
+      nexthop_tab[num_ifindex].route_metric      = metric;
+      ++num_ifindex;
+      break;
+    case ZEBRA_NEXTHOP_IPV4:
+      if (num_ifindex >= tab_size) {
+       char addr_str[100];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s",
+                __FILE__, __PRETTY_FUNCTION__,
+                (num_ifindex + 1), tab_size, addr_str);
+       return num_ifindex;
+      }
+      nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s);
+      length -= 4;
+      nexthop_tab[num_ifindex].ifindex             = 0;
+      nexthop_tab[num_ifindex].protocol_distance   = distance;
+      nexthop_tab[num_ifindex].route_metric        = metric;
+      if (PIM_DEBUG_ZEBRA) {
+       char addr_str[100];
+       char nexthop_str[100];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       pim_inet4_dump("<nexthop?>", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str));
+       zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  nexthop_str, addr_str);
+      }
+      ++num_ifindex;
+      break;
+    default:
+      /* do nothing */
+      {
+       char addr_str[100];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s",
+                __FILE__, __PRETTY_FUNCTION__,
+                 nexthop_type, addr_str);
+      }
+      break;
+    }
+  }
+
+  return num_ifindex;
+}
+
+static int zclient_lookup_nexthop_once(struct zclient *zlookup,
+                                      struct pim_zlookup_nexthop nexthop_tab[],
+                                      const int tab_size,
+                                      struct in_addr addr)
+{
+  struct stream *s;
+  int ret;
+
+  if (PIM_DEBUG_ZEBRA) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_debug("%s: addr=%s", 
+              __PRETTY_FUNCTION__,
+              addr_str);
+  }
+
+  /* Check socket. */
+  if (zlookup->sock < 0) {
+    zlog_err("%s %s: zclient lookup socket is not connected",
+            __FILE__, __PRETTY_FUNCTION__);
+    zclient_lookup_failed(zlookup);
+    return -1;
+  }
+  
+  s = zlookup->obuf;
+  stream_reset(s);
+  zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, VRF_DEFAULT);
+  stream_put_in_addr(s, &addr);
+  stream_putw_at(s, 0, stream_get_endp(s));
+  
+  ret = writen(zlookup->sock, s->data, stream_get_endp(s));
+  if (ret < 0) {
+    zlog_err("%s %s: writen() failure writing to zclient lookup socket",
+            __FILE__, __PRETTY_FUNCTION__);
+    zclient_lookup_failed(zlookup);
+    return -2;
+  }
+  if (ret == 0) {
+    zlog_err("%s %s: connection closed on zclient lookup socket",
+            __FILE__, __PRETTY_FUNCTION__);
+    zclient_lookup_failed(zlookup);
+    return -3;
+  }
+  
+  return zclient_read_nexthop(zlookup, nexthop_tab,
+                             tab_size, addr);
+}
+
+int zclient_lookup_nexthop(struct zclient *zlookup,
+                          struct pim_zlookup_nexthop nexthop_tab[],
+                          const int tab_size,
+                          struct in_addr addr,
+                          int max_lookup)
+{
+  int lookup;
+  uint32_t route_metric = 0xFFFFFFFF;
+  uint8_t  protocol_distance = 0xFF;
+
+  for (lookup = 0; lookup < max_lookup; ++lookup) {
+    int num_ifindex;
+    int first_ifindex;
+    struct in_addr nexthop_addr;
+
+    num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab,
+                                             PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr);
+    if ((num_ifindex < 1) && PIM_DEBUG_ZEBRA) {
+      char addr_str[100];
+      pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+      zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s",
+               __FILE__, __PRETTY_FUNCTION__,
+               lookup, max_lookup, addr_str);
+      return -1;
+    }
+
+    if (lookup < 1) {
+      /* this is the non-recursive lookup - save original metric/distance */
+      route_metric = nexthop_tab[0].route_metric;
+      protocol_distance = nexthop_tab[0].protocol_distance;
+    }
+    
+    /*
+      FIXME: Non-recursive nexthop ensured only for first ifindex.
+      However, recursive route lookup should really be fixed in zebra daemon.
+      See also TODO T24.
+     */
+    first_ifindex = nexthop_tab[0].ifindex;
+    nexthop_addr = nexthop_tab[0].nexthop_addr;
+    if (first_ifindex > 0) {
+      /* found: first ifindex is non-recursive nexthop */
+
+      if ((lookup > 0) && PIM_DEBUG_ZEBRA) {
+       /* Report non-recursive success after first lookup */
+       char addr_str[100];
+       pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+       zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d",
+                  __FILE__, __PRETTY_FUNCTION__,
+                  lookup, max_lookup, first_ifindex, addr_str,
+                  nexthop_tab[0].protocol_distance,
+                  nexthop_tab[0].route_metric);
+
+       /* use last address as nexthop address */
+       nexthop_tab[0].nexthop_addr = addr;
+
+       /* report original route metric/distance */
+       nexthop_tab[0].route_metric = route_metric;
+       nexthop_tab[0].protocol_distance = protocol_distance;
+      }
+
+      return num_ifindex;
+    }
+
+    if (PIM_DEBUG_ZEBRA) {
+      char addr_str[100];
+      char nexthop_str[100];
+      pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+      pim_inet4_dump("<nexthop?>", nexthop_addr, nexthop_str, sizeof(nexthop_str));
+      zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d",
+               __FILE__, __PRETTY_FUNCTION__,
+               lookup, max_lookup, nexthop_str, addr_str,
+               nexthop_tab[0].protocol_distance,
+               nexthop_tab[0].route_metric);
+    }
+
+    addr = nexthop_addr; /* use nexthop addr for recursive lookup */
+
+  } /* for (max_lookup) */
+
+  if (PIM_DEBUG_ZEBRA) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s",
+             __FILE__, __PRETTY_FUNCTION__,
+             lookup, max_lookup, addr_str);
+  }
+
+  return -2;
+}
diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h
new file mode 100644 (file)
index 0000000..f2be6d4
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIM_ZLOOKUP_H
+#define PIM_ZLOOKUP_H
+
+#include <zebra.h>
+
+#include "zclient.h"
+
+#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */
+
+struct pim_zlookup_nexthop {
+  struct in_addr nexthop_addr;
+  ifindex_t      ifindex;
+  uint32_t       route_metric;
+  uint8_t        protocol_distance;
+};
+
+struct zclient *zclient_lookup_new(void);
+
+int zclient_lookup_nexthop(struct zclient *zlookup,
+                          struct pim_zlookup_nexthop nexthop_tab[],
+                          const int tab_size,
+                          struct in_addr addr,
+                          int max_lookup);
+
+#endif /* PIM_ZLOOKUP_H */
diff --git a/pimd/pimd.c b/pimd/pimd.c
new file mode 100644 (file)
index 0000000..97fb223
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "vrf.h"
+
+#include "pimd.h"
+#include "pim_cmd.h"
+#include "pim_iface.h"
+#include "pim_zebra.h"
+#include "pim_str.h"
+#include "pim_oil.h"
+#include "pim_pim.h"
+#include "pim_upstream.h"
+#include "pim_rpf.h"
+#include "pim_ssmpingd.h"
+#include "pim_static.h"
+
+const char *const PIM_ALL_SYSTEMS      = MCAST_ALL_SYSTEMS;
+const char *const PIM_ALL_ROUTERS      = MCAST_ALL_ROUTERS;
+const char *const PIM_ALL_PIM_ROUTERS  = MCAST_ALL_PIM_ROUTERS;
+const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS;
+
+struct thread_master     *master = 0;
+uint32_t                  qpim_debugs = 0;
+int                       qpim_mroute_socket_fd = -1;
+int64_t                   qpim_mroute_socket_creation = 0; /* timestamp of creation */
+struct thread            *qpim_mroute_socket_reader = 0;
+int                       qpim_mroute_oif_highest_vif_index = -1;
+struct list              *qpim_channel_oil_list = 0;
+int                       qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */
+struct list              *qpim_upstream_list = 0;
+struct zclient           *qpim_zclient_update = 0;
+struct zclient           *qpim_zclient_lookup = 0;
+struct pim_assert_metric  qpim_infinite_assert_metric;
+long                      qpim_rpf_cache_refresh_delay_msec = 10000;
+struct thread            *qpim_rpf_cache_refresher = 0;
+int64_t                   qpim_rpf_cache_refresh_requests = 0;
+int64_t                   qpim_rpf_cache_refresh_events = 0;
+int64_t                   qpim_rpf_cache_refresh_last =  0;
+struct in_addr            qpim_inaddr_any;
+struct list              *qpim_ssmpingd_list = 0;
+struct in_addr            qpim_ssmpingd_group_addr;
+int64_t                   qpim_scan_oil_events = 0;
+int64_t                   qpim_scan_oil_last = 0;
+int64_t                   qpim_mroute_add_events = 0;
+int64_t                   qpim_mroute_add_last = 0;
+int64_t                   qpim_mroute_del_events = 0;
+int64_t                   qpim_mroute_del_last = 0;
+struct list              *qpim_static_route_list = 0;
+
+static void pim_free()
+{
+  pim_ssmpingd_destroy();
+
+  if (qpim_channel_oil_list)
+    list_free(qpim_channel_oil_list);
+
+  if (qpim_upstream_list)
+    list_free(qpim_upstream_list);
+
+  if (qpim_static_route_list)
+     list_free(qpim_static_route_list);
+}
+
+void pim_init()
+{
+  srandom(time(NULL));
+
+  if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) {
+    zlog_err("%s %s: could not solve %s to group address: errno=%d: %s",
+            __FILE__, __PRETTY_FUNCTION__,
+            PIM_ALL_PIM_ROUTERS, errno, safe_strerror(errno));
+    zassert(0);
+    return;
+  }
+
+  qpim_channel_oil_list = list_new();
+  if (!qpim_channel_oil_list) {
+    zlog_err("%s %s: failure: channel_oil_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    return;
+  }
+  qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free;
+
+  qpim_upstream_list = list_new();
+  if (!qpim_upstream_list) {
+    zlog_err("%s %s: failure: upstream_list=list_new()",
+            __FILE__, __PRETTY_FUNCTION__);
+    pim_free();
+    return;
+  }
+  qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
+
+  qpim_static_route_list = list_new();
+  if (!qpim_static_route_list) {
+    zlog_err("%s %s: failure: static_route_list=list_new()",
+        __FILE__, __PRETTY_FUNCTION__);
+    return;
+  }
+  qpim_static_route_list->del = (void (*)(void *)) pim_static_route_free;
+
+  qpim_mroute_socket_fd = -1; /* mark mroute as disabled */
+  qpim_mroute_oif_highest_vif_index = -1;
+
+  zassert(!qpim_debugs);
+  zassert(!PIM_MROUTE_IS_ENABLED);
+
+  qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY;
+
+  /*
+    RFC 4601: 4.6.3.  Assert Metrics
+
+    assert_metric
+    infinite_assert_metric() {
+    return {1,infinity,infinity,0}
+    }
+  */
+  qpim_infinite_assert_metric.rpt_bit_flag      = 1;
+  qpim_infinite_assert_metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
+  qpim_infinite_assert_metric.route_metric      = PIM_ASSERT_ROUTE_METRIC_MAX;
+  qpim_infinite_assert_metric.ip_address        = qpim_inaddr_any;
+
+  pim_cmd_init();
+  pim_ssmpingd_init();
+}
+
+void pim_terminate()
+{
+  vrf_terminate();
+  pim_free();
+}
diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample
new file mode 100644 (file)
index 0000000..6753085
--- /dev/null
@@ -0,0 +1,41 @@
+!
+! pimd sample configuration file
+! $QuaggaId: $Format:%an, %ai, %h$ $
+!
+hostname quagga-pimd-router
+password zebra
+!enable password zebra
+!
+!log file pimd.log
+log stdout
+!
+line vty
+ exec-timeout 60
+!
+!debug igmp
+!debug pim
+!debug pim zebra
+!
+ip multicast-routing
+!
+! ! You may want to enable ssmpingd for troubleshooting
+! ! See http://www.venaas.no/multicast/ssmping/
+! !
+! ip ssmpingd 1.1.1.1
+! ip ssmpingd 2.2.2.2
+!
+! ! HINTS:
+! !  - Enable "ip pim ssm" on the interface directly attached to the
+! !    multicast source host (if this is the first-hop router)
+! !  - Enable "ip pim ssm" on pim-routers-facing interfaces
+! !  - Enable "ip igmp" on IGMPv3-hosts-facing interfaces
+! !  - In order to inject IGMPv3 local membership information in the
+! !    PIM protocol state, enable both "ip pim ssm" and "ip igmp" on
+! !    the same interface; otherwise PIM won't advertise
+! !    IGMPv3-learned membership to other PIM routers
+!
+interface eth0
+ ip pim ssm
+ ip igmp
+
+! -x-
diff --git a/pimd/pimd.h b/pimd/pimd.h
new file mode 100644 (file)
index 0000000..9a7e605
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#ifndef PIMD_H
+#define PIMD_H
+
+#include <stdint.h>
+
+#include "pim_mroute.h"
+#include "pim_assert.h"
+
+#define PIMD_PROGNAME       "pimd"
+#define PIMD_DEFAULT_CONFIG "pimd.conf"
+#define PIMD_VTY_PORT       2611
+#define PIMD_BUG_ADDRESS    "https://github.com/udhos/qpimd"
+
+#define PIM_IP_HEADER_MIN_LEN         (20)
+#define PIM_IP_HEADER_MAX_LEN         (60)
+#define PIM_IP_PROTO_IGMP             (2)
+#define PIM_IP_PROTO_PIM              (103)
+#define PIM_IGMP_MIN_LEN              (8)
+#define PIM_MSG_HEADER_LEN            (4)
+#define PIM_PIM_MIN_LEN               PIM_MSG_HEADER_LEN
+#define PIM_PROTO_VERSION             (2)
+
+#define MCAST_ALL_SYSTEMS      "224.0.0.1"
+#define MCAST_ALL_ROUTERS      "224.0.0.2"
+#define MCAST_ALL_PIM_ROUTERS  "224.0.0.13"
+#define MCAST_ALL_IGMP_ROUTERS "224.0.0.22"
+
+#define PIM_FORCE_BOOLEAN(expr) ((expr) != 0)
+
+#define PIM_NET_INADDR_ANY (htonl(INADDR_ANY))
+#define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */
+#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */
+
+#define PIM_MASK_PIM_EVENTS          (1 << 0)
+#define PIM_MASK_PIM_PACKETS         (1 << 1)
+#define PIM_MASK_PIM_PACKETDUMP_SEND (1 << 2)
+#define PIM_MASK_PIM_PACKETDUMP_RECV (1 << 3)
+#define PIM_MASK_PIM_TRACE           (1 << 4)
+#define PIM_MASK_IGMP_EVENTS         (1 << 5)
+#define PIM_MASK_IGMP_PACKETS        (1 << 6)
+#define PIM_MASK_IGMP_TRACE          (1 << 7)
+#define PIM_MASK_ZEBRA               (1 << 8)
+#define PIM_MASK_SSMPINGD            (1 << 9)
+#define PIM_MASK_MROUTE              (1 << 10)
+#define PIM_MASK_PIM_HELLO           (1 << 11)
+#define PIM_MASK_PIM_J_P             (1 << 12)
+#define PIM_MASK_STATIC              (1 << 13)
+
+const char *const PIM_ALL_SYSTEMS;
+const char *const PIM_ALL_ROUTERS;
+const char *const PIM_ALL_PIM_ROUTERS;
+const char *const PIM_ALL_IGMP_ROUTERS;
+
+struct thread_master     *master;
+uint32_t                  qpim_debugs;
+int                       qpim_mroute_socket_fd;
+int64_t                   qpim_mroute_socket_creation; /* timestamp of creation */
+struct thread            *qpim_mroute_socket_reader;
+int                       qpim_mroute_oif_highest_vif_index;
+struct list              *qpim_channel_oil_list; /* list of struct channel_oil */
+struct in_addr            qpim_all_pim_routers_addr;
+int                       qpim_t_periodic; /* Period between Join/Prune Messages */
+struct list              *qpim_upstream_list; /* list of struct pim_upstream */
+struct zclient           *qpim_zclient_update;
+struct zclient           *qpim_zclient_lookup;
+struct pim_assert_metric  qpim_infinite_assert_metric;
+long                      qpim_rpf_cache_refresh_delay_msec;
+struct thread            *qpim_rpf_cache_refresher;
+int64_t                   qpim_rpf_cache_refresh_requests;
+int64_t                   qpim_rpf_cache_refresh_events;
+int64_t                   qpim_rpf_cache_refresh_last;
+struct in_addr            qpim_inaddr_any;
+struct list              *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */
+struct in_addr            qpim_ssmpingd_group_addr;
+int64_t                   qpim_scan_oil_events;
+int64_t                   qpim_scan_oil_last;
+int64_t                   qpim_mroute_add_events;
+int64_t                   qpim_mroute_add_last;
+int64_t                   qpim_mroute_del_events;
+int64_t                   qpim_mroute_del_last;
+struct list              *qpim_static_route_list; /* list of routes added statically */
+
+#define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)
+
+#define PIM_MROUTE_IS_ENABLED  (qpim_mroute_socket_fd >= 0)
+#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0)
+
+#define PIM_DEBUG_PIM_EVENTS          (qpim_debugs & PIM_MASK_PIM_EVENTS)
+#define PIM_DEBUG_PIM_PACKETS         (qpim_debugs & PIM_MASK_PIM_PACKETS)
+#define PIM_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DEBUG_PIM_TRACE           (qpim_debugs & PIM_MASK_PIM_TRACE)
+#define PIM_DEBUG_IGMP_EVENTS         (qpim_debugs & PIM_MASK_IGMP_EVENTS)
+#define PIM_DEBUG_IGMP_PACKETS        (qpim_debugs & PIM_MASK_IGMP_PACKETS)
+#define PIM_DEBUG_IGMP_TRACE          (qpim_debugs & PIM_MASK_IGMP_TRACE)
+#define PIM_DEBUG_ZEBRA               (qpim_debugs & PIM_MASK_ZEBRA)
+#define PIM_DEBUG_SSMPINGD            (qpim_debugs & PIM_MASK_SSMPINGD)
+#define PIM_DEBUG_MROUTE              (qpim_debugs & PIM_MASK_MROUTE)
+#define PIM_DEBUG_PIM_HELLO           (qpim_debugs & PIM_MASK_PIM_HELLO)
+#define PIM_DEBUG_PIM_J_P             (qpim_debugs & PIM_MASK_PIM_J_P)
+#define PIM_DEBUG_STATIC              (qpim_debugs & PIM_MASK_STATIC)
+
+#define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS))
+#define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS))
+#define PIM_DEBUG_TRACE        (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE))
+
+#define PIM_DO_DEBUG_PIM_EVENTS          (qpim_debugs |= PIM_MASK_PIM_EVENTS)
+#define PIM_DO_DEBUG_PIM_PACKETS         (qpim_debugs |= PIM_MASK_PIM_PACKETS)
+#define PIM_DO_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DO_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DO_DEBUG_PIM_TRACE           (qpim_debugs |= PIM_MASK_PIM_TRACE)
+#define PIM_DO_DEBUG_IGMP_EVENTS         (qpim_debugs |= PIM_MASK_IGMP_EVENTS)
+#define PIM_DO_DEBUG_IGMP_PACKETS        (qpim_debugs |= PIM_MASK_IGMP_PACKETS)
+#define PIM_DO_DEBUG_IGMP_TRACE          (qpim_debugs |= PIM_MASK_IGMP_TRACE)
+#define PIM_DO_DEBUG_ZEBRA               (qpim_debugs |= PIM_MASK_ZEBRA)
+#define PIM_DO_DEBUG_SSMPINGD            (qpim_debugs |= PIM_MASK_SSMPINGD)
+#define PIM_DO_DEBUG_MROUTE              (qpim_debugs |= PIM_MASK_MROUTE)
+#define PIM_DO_DEBUG_PIM_HELLO           (qpim_debugs |= PIM_MASK_PIM_HELLO)
+#define PIM_DO_DEBUG_PIM_J_P             (qpim_debugs |= PIM_MASK_PIM_J_P)
+#define PIM_DO_DEBUG_STATIC              (qpim_debugs |= PIM_MASK_STATIC)
+
+#define PIM_DONT_DEBUG_PIM_EVENTS          (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
+#define PIM_DONT_DEBUG_PIM_PACKETS         (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
+#define PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_SEND)
+#define PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_RECV)
+#define PIM_DONT_DEBUG_PIM_TRACE           (qpim_debugs &= ~PIM_MASK_PIM_TRACE)
+#define PIM_DONT_DEBUG_IGMP_EVENTS         (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS)
+#define PIM_DONT_DEBUG_IGMP_PACKETS        (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS)
+#define PIM_DONT_DEBUG_IGMP_TRACE          (qpim_debugs &= ~PIM_MASK_IGMP_TRACE)
+#define PIM_DONT_DEBUG_ZEBRA               (qpim_debugs &= ~PIM_MASK_ZEBRA)
+#define PIM_DONT_DEBUG_SSMPINGD            (qpim_debugs &= ~PIM_MASK_SSMPINGD)
+#define PIM_DONT_DEBUG_MROUTE              (qpim_debugs &= ~PIM_MASK_MROUTE)
+#define PIM_DONT_DEBUG_PIM_HELLO           (qpim_debugs &= ~PIM_MASK_PIM_HELLO)
+#define PIM_DONT_DEBUG_PIM_J_P             (qpim_debugs &= ~PIM_MASK_PIM_J_P)
+#define PIM_DONT_DEBUG_STATIC              (qpim_debugs &= ~PIM_MASK_STATIC)
+
+void pim_init(void);
+void pim_terminate(void);
+
+extern void pim_route_map_init (void);
+
+#endif /* PIMD_H */
diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c
new file mode 100644 (file)
index 0000000..81026ae
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+  PIM for Quagga
+  Copyright (C) 2008  Everton da Silva Marques
+
+  This program 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 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; see the file COPYING; if not, write to the
+  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+  MA 02110-1301 USA
+  
+  $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include "pim_igmp_join.h"
+
+const char *prog_name = 0;
+
+static int iface_solve_index(const char *ifname)
+{
+  struct if_nameindex *ini;
+  ifindex_t ifindex = -1;
+  int i;
+
+  if (!ifname)
+    return -1;
+
+  ini = if_nameindex();
+  if (!ini) {
+    int err = errno;
+    fprintf(stderr,
+           "%s: interface=%s: failure solving index: errno=%d: %s\n",
+           prog_name, ifname, err, strerror(err));
+    errno = err;
+    return -1;
+  }
+
+  for (i = 0; ini[i].if_index; ++i) {
+#if 0
+    fprintf(stderr,
+           "%s: interface=%s matching against local ifname=%s ifindex=%d\n",
+           prog_name, ifname, ini[i].if_name, ini[i].if_index);
+#endif
+    if (!strcmp(ini[i].if_name, ifname)) {
+      ifindex = ini[i].if_index;
+      break;
+    }
+  }
+
+  if_freenameindex(ini);
+
+  return ifindex;
+}
+
+int main(int argc, const char *argv[])
+{
+  struct in_addr group_addr;
+  struct in_addr source_addr;
+  const char *ifname;
+  const char *group;
+  const char *source;
+  ifindex_t ifindex;
+  int result;
+  int fd;
+
+  prog_name = argv[0];
+
+  fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  if (fd < 0) {
+    fprintf(stderr,
+           "%s: could not create socket: socket(): errno=%d: %s\n",
+           prog_name, errno, strerror(errno));
+    exit(1);
+  }
+
+  if (argc != 4) {
+    fprintf(stderr,
+           "usage:   %s interface group     source\n"
+           "example: %s eth0      232.1.1.1 1.1.1.1\n",
+           prog_name, prog_name);
+    exit(1);
+  }
+
+  ifname = argv[1];
+  group  = argv[2];
+  source = argv[3];
+
+  ifindex = iface_solve_index(ifname);
+  if (ifindex < 0) {
+    fprintf(stderr, "%s: could not find interface: %s\n",
+           prog_name, ifname);
+    exit(1);
+  }
+
+  result = inet_pton(AF_INET, group, &group_addr);
+  if (result <= 0) {
+    fprintf(stderr, "%s: bad group address: %s\n",
+           prog_name, group);
+    exit(1);
+  }
+
+  result = inet_pton(AF_INET, source, &source_addr);
+  if (result <= 0) {
+    fprintf(stderr, "%s: bad source address: %s\n",
+           prog_name, source);
+    exit(1);
+  }
+
+  result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr);
+  if (result) {
+    fprintf(stderr,
+           "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n",
+           prog_name, fd, group, source, ifindex, ifname,
+           errno, strerror(errno));
+    exit(1);
+  }
+
+  printf("%s: joined channel (S,G)=(%s,%s) on interface %s\n",
+        prog_name, source, group, ifname);
+
+  printf("%s: waiting...\n", prog_name);
+
+  getchar();
+
+  close(fd);
+
+  printf("%s: left channel (S,G)=(%s,%s) on interface %s\n",
+        prog_name, source, group, ifname);
+
+  exit(0);
+}
diff --git a/pkgsrc/.gitignore b/pkgsrc/.gitignore
new file mode 100644 (file)
index 0000000..63e9a66
--- /dev/null
@@ -0,0 +1,8 @@
+Makefile
+Makefile.in
+*.sh
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/pkgsrc/Makefile.am b/pkgsrc/Makefile.am
new file mode 100644 (file)
index 0000000..622fbf0
--- /dev/null
@@ -0,0 +1,3 @@
+rcdir=@pkgsrcrcdir@
+
+rc_SCRIPTS = bgpd.sh ospf6d.sh ospfd.sh ripd.sh ripngd.sh zebra.sh
diff --git a/pkgsrc/README.txt b/pkgsrc/README.txt
new file mode 100644 (file)
index 0000000..13ec449
--- /dev/null
@@ -0,0 +1,7 @@
+$Id: README.txt,v 1.1 2004/08/27 15:57:35 gdt Exp $
+
+This directory contains files for use with the pkgsrc framework
+(http://www.pkgsrc.org) used with NetBSD and other operating systems.
+Eventually it will be hooked into automake such that they can be
+installed in /usr/pkg/etc/rc.d (via configure option, probably).
+
diff --git a/pkgsrc/bgpd.sh.in b/pkgsrc/bgpd.sh.in
new file mode 100644 (file)
index 0000000..d234b54
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# bgpd is part of the quagga routing beast
+#
+# PROVIDE: bgpd
+# REQUIRE: zebra
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="bgpd"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/pkgsrc/ospf6d.sh.in b/pkgsrc/ospf6d.sh.in
new file mode 100644 (file)
index 0000000..3fbdb81
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# ospf6d is part of the quagga routing beast
+#
+# PROVIDE: ospf6d
+# REQUIRE: zebra
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="ospf6d"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/pkgsrc/ospfd.sh.in b/pkgsrc/ospfd.sh.in
new file mode 100644 (file)
index 0000000..daa2252
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# ospfd is part of the quagga routing beast
+#
+# PROVIDE: ospfd
+# REQUIRE: zebra
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="ospfd"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/pkgsrc/ripd.sh.in b/pkgsrc/ripd.sh.in
new file mode 100644 (file)
index 0000000..3157541
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# ripd is part of the quagga routing beast
+#
+# PROVIDE: ripd
+# REQUIRE: zebra
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="ripd"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/pkgsrc/ripngd.sh.in b/pkgsrc/ripngd.sh.in
new file mode 100644 (file)
index 0000000..d06ac90
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# ripngd is part of the quagga routing beast
+#
+# PROVIDE: ripngd
+# REQUIRE: zebra
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="ripngd"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/pkgsrc/zebra.sh.in b/pkgsrc/zebra.sh.in
new file mode 100644 (file)
index 0000000..c2f12a7
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# zebra is the head of the quagga routing beast
+#
+# PROVIDE: zebra
+# REQUIRE: NETWORKING
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="zebra"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+stop_postcmd="zebra_postcmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    mkdir -p "${socket_dir}"
+    chown quagga.quagga "${socket_dir}"
+    chmod 750 "${socket_dir}"
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+zebra_postcmd()
+{
+    if [ -d "${socketdir}" ]; then
+        rmdir ${socketdir}
+    fi
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/ports/.gitignore b/ports/.gitignore
new file mode 100644 (file)
index 0000000..dd5bf7c
--- /dev/null
@@ -0,0 +1,6 @@
+.arch-inventory
+.arch-ids
+
+*~
+*.loT
+
diff --git a/ports/Makefile b/ports/Makefile
new file mode 100644 (file)
index 0000000..86f77bd
--- /dev/null
@@ -0,0 +1,59 @@
+# New ports collection makefile for:   zebra
+# Version required:    2.1.5
+# Date created:                28 Feb 1998
+# Whom:                        seirios@matrix.iri.co.jp
+#
+
+#DISTNAME=     zebra-980224
+DISTNAME=      zebra-current
+PKGNAME=       zebra
+CATEGORIES=    net
+MASTER_SITES=  ftp://ftp.zebra.org/pub/zebra/
+
+MAINTAINER=    seirios@matrix.iri.co.jp
+
+WRKSRC=                ${WRKDIR}/zebra-current
+
+#### Under constructing, We cannot support md5
+NO_CHECKSUM=   yes
+
+do-build:
+       @(cd ${WRKSRC}; sh ./configure; make)
+
+post-install:
+       @if [ ! -f ${PREFIX}/etc/rc.d/zebra.sh ]; then \
+               echo "Installing ${PREFIX}/etc/rc.d/zebra.sh startup file."; \
+               echo "#!/bin/sh" > ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "# zebra" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "if [ -x /usr/local/sbin/zebra -a ! -f /var/run/zebra.pid -a -f /usr/local/etc/zebra.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  /usr/local/sbin/zebra -d -f /usr/local/etc/zebra.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  echo -n ' zebra'" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "# bgpd" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "if [ -x /usr/local/sbin/bgpd -a ! -f /var/run/bgpd.pid -a -f /usr/local/etc/bgpd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  /usr/local/sbin/bgpd -d -f /usr/local/etc/bgpd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  echo -n ' bgpd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "# ripd" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "if [ -x /usr/local/sbin/ripd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  /usr/local/sbin/ripd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  echo -n ' ripd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "# ripngd" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "if [ -x /usr/local/sbin/ripngd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  /usr/local/sbin/ripngd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "  echo -n ' ripngd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \
+               chmod 751 ${PREFIX}/etc/rc.d/zebra.sh; \
+       fi
+       @echo "If you will access zebra,bgpd,ripd,ripngd with telnet,";
+       @echo "then you add some line (written under this line) to /etc/services";
+       @echo " zebrasrv      2600/tcp            # zebra service";
+       @echo " zebra         2601/tcp            # zebra vty";
+       @echo " ripd          2602/tcp            # RIPd vty";
+       @echo " ripngd        2603/tcp            # RIPngd vty";
+       @echo " ospfd         2604/tcp            # OSPFd vty";
+       @echo " bgpd          2605/tcp            # BGPd vty";
+       @echo " pimd          2611/tcp            # PIMd vty";
+
+.include <bsd.port.mk>
diff --git a/ports/README b/ports/README
new file mode 100644 (file)
index 0000000..a650eaa
--- /dev/null
@@ -0,0 +1 @@
+This directory contain files for making FreeBSD package.
diff --git a/ports/files/.gitignore b/ports/files/.gitignore
new file mode 100644 (file)
index 0000000..dd5bf7c
--- /dev/null
@@ -0,0 +1,6 @@
+.arch-inventory
+.arch-ids
+
+*~
+*.loT
+
diff --git a/ports/files/md5 b/ports/files/md5
new file mode 100644 (file)
index 0000000..520c348
--- /dev/null
@@ -0,0 +1 @@
+MD5 (zebra-980224.tar.gz) = c6887645741200c43341156c168c7034
diff --git a/ports/pkg/.gitignore b/ports/pkg/.gitignore
new file mode 100644 (file)
index 0000000..dd5bf7c
--- /dev/null
@@ -0,0 +1,6 @@
+.arch-inventory
+.arch-ids
+
+*~
+*.loT
+
diff --git a/ports/pkg/COMMENT b/ports/pkg/COMMENT
new file mode 100644 (file)
index 0000000..53c55e3
--- /dev/null
@@ -0,0 +1 @@
+Zebra Routing protocol daemon
diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR
new file mode 100644 (file)
index 0000000..aeb1950
--- /dev/null
@@ -0,0 +1,76 @@
+=============
+WHAT IS ZEBRA
+=============
+  Zebra is a free software that manages TCP/IP based routing protocol.
+  It takes multi-server and multi-thread approach to resolve the current
+complexity of the Internet.
+
+  Currently zebra is still under development, so If you want to use zebra,
+I strongly recommend you to get the latest version of zebra.
+  Zebra snapshot is released on every monday.
+
+===================
+SUPPORTED Protocols
+===================
+  Zebra supports both IPv4 and IPv6 :-)
+  For supporting IPv4 Routing protocols is here
+       RIP (both version1 and version2)
+               RIPv2 supports both Multicast and Broadcast
+       BGP (only support BGP4)
+
+  For supporting IPv6 Routing protocols is here
+       RIPng
+       BGP4+
+
+===================
+Supported plat-home
+===================
+  Now zebra is testing on 
+  o FreeBSD 2.2.8
+      -- without IPv6 ;-)
+      -- with KAME
+      -- with INRIA IPv6 protocol stack.
+
+  o GNU/Linux 2.2.2
+  o GNU/Linux 2.0.36
+
+===========
+ZEBRA Ports
+===========
+  Each daemon has each own terminal interface.  Also zebra has communication
+port which provides several services to other daemons.  Below is zebra ports
+list.
+
+zebrasrv      2600/tcp           # zebra service
+zebra        2601/tcp            # zebra vty
+ripd         2602/tcp            # RIPd vty
+ripngd       2603/tcp            # RIPngd vty
+ospfd        2604/tcp            # OSPFd vty
+bgpd         2605/tcp            # BGPd vty
+pimd         2611/tcp            # PIMd vty
+
+I recommend you to add upper list to /etc/services.
+
+====================
+For More Information
+====================
+  Web page is located at:
+       http://www.zebra.org/
+
+  Alpha version source file can be found at:
+       ftp://ftp.zebra.org/pub/zebra/
+
+  Mailing List is here
+       zebra@zebra.org
+       zebra-jp@zebra.org
+
+    If you want to join zebra mailing list, mail to 
+       majordomo@zebra.org
+    and you write
+       subscribe zebra
+         -- if you want to talk with English
+       subscribe zebra-jp
+         -- if you want to talk with Japanese
+    on Mail BODY (Not Subject).
+
+Enjoy.
diff --git a/ports/pkg/PLIST b/ports/pkg/PLIST
new file mode 100644 (file)
index 0000000..ccc69eb
--- /dev/null
@@ -0,0 +1,8 @@
+sbin/zebra
+sbin/bgpd
+sbin/ripd
+etc/bgpd.conf.sample
+etc/ripd.conf.sample
+etc/zebra.conf.sample
+etc/rc.d/zebra.sh
+info/zebra.info
diff --git a/qpb/.gitignore b/qpb/.gitignore
new file mode 100644 (file)
index 0000000..b133c52
--- /dev/null
@@ -0,0 +1,15 @@
+Makefile
+Makefile.in
+*.o
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.a
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/qpb/Makefile.am b/qpb/Makefile.am
new file mode 100644 (file)
index 0000000..0fbda61
--- /dev/null
@@ -0,0 +1,30 @@
+include ../common.am
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES)
+
+PROTOBUF_INCLUDES=-I$(top_srcdir)
+PROTOBUF_PACKAGE = qpb
+
+lib_LTLIBRARIES = libquagga_pb.la
+libquagga_pb_la_LDFLAGS = -version-info 0:0:0
+
+if HAVE_PROTOBUF
+protobuf_srcs =                                        \
+       qpb_allocator.c
+
+protobuf_srcs_nodist =                         \
+       qpb.pb-c.c
+endif
+
+libquagga_pb_la_SOURCES =                      \
+       linear_allocator.h                      \
+       qpb.h                                   \
+       qpb.c                                   \
+       qpb_allocator.h                         \
+       $(protobuf_srcs)
+
+nodist_libquagga_pb_la_SOURCES = $(protobuf_srcs_nodist)
+
+CLEANFILES = $(Q_CLEANFILES)
+BUILT_SOURCES = $(Q_PROTOBUF_SRCS)
+EXTRA_DIST = qpb.proto
diff --git a/qpb/README.txt b/qpb/README.txt
new file mode 100644 (file)
index 0000000..99ccd05
--- /dev/null
@@ -0,0 +1 @@
+Protobuf definitions and code that is applicable to all of quagga.
diff --git a/qpb/linear_allocator.h b/qpb/linear_allocator.h
new file mode 100644 (file)
index 0000000..e3ebbc6
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * linear_allocator.h
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Header file for the linear allocator.
+ *
+ * An allocator that allocates memory by walking down towards the end
+ * of a buffer. No attempt is made to reuse blocks that are freed
+ * subsequently. The assumption is that the buffer is big enough to
+ * cover allocations for a given purpose.
+ */
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * Alignment for block allocated by the allocator. Must be a power of 2.
+ */
+#define LINEAR_ALLOCATOR_ALIGNMENT        8
+
+#define LINEAR_ALLOCATOR_ALIGN(value)                                  \
+  (((value) + LINEAR_ALLOCATOR_ALIGNMENT - 1) & ~(LINEAR_ALLOCATOR_ALIGNMENT - 1));
+
+/*
+ * linear_allocator_align_ptr
+ */
+static inline char *
+linear_allocator_align_ptr (char *ptr)
+{
+  return (char *) LINEAR_ALLOCATOR_ALIGN ((intptr_t) ptr);
+}
+
+typedef struct linear_allocator_t_
+{
+  char *buf;
+
+  /*
+   * Current location in the buffer.
+   */
+  char *cur;
+
+  /*
+   * End of buffer.
+   */
+  char *end;
+
+  /*
+   * Version number of the allocator, this is bumped up when the allocator
+   * is reset and helps identifies bad frees.
+   */
+  uint32_t version;
+
+  /*
+   * The number of blocks that are currently allocated.
+   */
+  int num_allocated;
+} linear_allocator_t;
+
+/*
+ * linear_allocator_block_t
+ *
+ * Header structure at the begining of each block.
+ */
+typedef struct linear_allocator_block_t_
+{
+  uint32_t flags;
+
+  /*
+   * The version of the allocator when this block was allocated.
+   */
+  uint32_t version;
+  char data[0];
+} linear_allocator_block_t;
+
+#define LINEAR_ALLOCATOR_BLOCK_IN_USE 0x01
+
+#define LINEAR_ALLOCATOR_HDR_SIZE (sizeof(linear_allocator_block_t))
+
+/*
+ * linear_allocator_block_size
+ *
+ * The total amount of space a block will take in the buffer,
+ * including the size of the header.
+ */
+static inline size_t
+linear_allocator_block_size (size_t user_size)
+{
+  return LINEAR_ALLOCATOR_ALIGN (LINEAR_ALLOCATOR_HDR_SIZE + user_size);
+}
+
+/*
+ * linear_allocator_ptr_to_block
+ */
+static inline linear_allocator_block_t *
+linear_allocator_ptr_to_block (void *ptr)
+{
+  void *block_ptr;
+  block_ptr = ((char *) ptr) - offsetof (linear_allocator_block_t, data);
+  return block_ptr;
+}
+
+/*
+ * linear_allocator_init
+ */
+static inline void
+linear_allocator_init (linear_allocator_t * allocator, char *buf,
+                      size_t buf_len)
+{
+  memset (allocator, 0, sizeof (*allocator));
+
+  assert (linear_allocator_align_ptr (buf) == buf);
+  allocator->buf = buf;
+  allocator->cur = buf;
+  allocator->end = buf + buf_len;
+}
+
+/*
+ * linear_allocator_reset
+ *
+ * Prepare an allocator for reuse.
+ *
+ * *** NOTE ** This implicitly frees all the blocks in the allocator.
+ */
+static inline void
+linear_allocator_reset (linear_allocator_t *allocator)
+{
+  allocator->num_allocated = 0;
+  allocator->version++;
+  allocator->cur = allocator->buf;
+}
+
+/*
+ * linear_allocator_alloc
+ */
+static inline void *
+linear_allocator_alloc (linear_allocator_t *allocator, size_t user_size)
+{
+  size_t block_size;
+  linear_allocator_block_t *block;
+
+  block_size = linear_allocator_block_size (user_size);
+
+  if (allocator->cur + block_size > allocator->end)
+    {
+      return NULL;
+    }
+
+  block = (linear_allocator_block_t *) allocator->cur;
+  allocator->cur += block_size;
+
+  block->flags = LINEAR_ALLOCATOR_BLOCK_IN_USE;
+  block->version = allocator->version;
+  allocator->num_allocated++;
+  return block->data;
+}
+
+/*
+ * linear_allocator_free
+ */
+static inline void
+linear_allocator_free (linear_allocator_t *allocator, void *ptr)
+{
+  linear_allocator_block_t *block;
+
+  if (((char *) ptr) < allocator->buf || ((char *) ptr) >= allocator->end)
+    {
+      assert (0);
+      return;
+    }
+
+  block = linear_allocator_ptr_to_block (ptr);
+  if (block->version != allocator->version)
+    {
+      assert (0);
+      return;
+    }
+
+  block->flags = block->flags & ~LINEAR_ALLOCATOR_BLOCK_IN_USE;
+
+  if (--allocator->num_allocated < 0)
+    {
+      assert (0);
+    }
+}
diff --git a/qpb/qpb.c b/qpb/qpb.c
new file mode 100644 (file)
index 0000000..1b2b47f
--- /dev/null
+++ b/qpb/qpb.c
@@ -0,0 +1,29 @@
+/*
+ * qpb.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Main file for the qpb library.
+ */
+
diff --git a/qpb/qpb.h b/qpb/qpb.h
new file mode 100644 (file)
index 0000000..55c1deb
--- /dev/null
+++ b/qpb/qpb.h
@@ -0,0 +1,372 @@
+/*
+ * qpb.h
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Main public header file for the quagga protobuf library.
+ */
+
+#ifndef _QPB_H
+#define _QPB_H
+
+#include "prefix.h"
+
+#include "qpb/qpb.pb-c.h"
+
+#include "qpb/qpb_allocator.h"
+
+/*
+ * qpb__address_family__set
+ */
+#define qpb_address_family_set qpb__address_family__set
+static inline int
+qpb__address_family__set (Qpb__AddressFamily *pb_family, u_char family)
+{
+  switch (family) {
+  case AF_INET:
+    *pb_family = QPB__ADDRESS_FAMILY__IPV4;
+    return 1;
+
+  case AF_INET6:
+    *pb_family = QPB__ADDRESS_FAMILY__IPV6;
+    return 1;
+
+  default:
+    *pb_family = QPB__ADDRESS_FAMILY__UNKNOWN_AF;
+  }
+
+  return 0;
+}
+
+/*
+ * qpb__address_family__get
+ */
+#define qpb_address_family_get qpb__address_family__get
+static inline int
+qpb__address_family__get (Qpb__AddressFamily pb_family, u_char *family)
+{
+
+  switch (pb_family) {
+  case QPB__ADDRESS_FAMILY__IPV4:
+    *family = AF_INET;
+    return 1;
+
+  case QPB__ADDRESS_FAMILY__IPV6:
+    *family = AF_INET6;
+    return 1;
+
+  case QPB__ADDRESS_FAMILY__UNKNOWN_AF:
+    return 0;
+  }
+
+  return 0;
+}
+
+/*
+ * qpb__l3_prefix__create
+ */
+#define qpb_l3_prefix_create qpb__l3_prefix__create
+static inline Qpb__L3Prefix *
+qpb__l3_prefix__create (qpb_allocator_t *allocator, struct prefix *p)
+{
+  Qpb__L3Prefix *prefix;
+
+  prefix = QPB_ALLOC(allocator, typeof(*prefix));
+  if (!prefix) {
+    return NULL;
+  }
+  qpb__l3_prefix__init(prefix);
+  prefix->length = p->prefixlen;
+  prefix->bytes.len = (p->prefixlen + 7)/8;
+  prefix->bytes.data = qpb_alloc(allocator, prefix->bytes.len);
+  if (!prefix->bytes.data) {
+    return NULL;
+  }
+
+  memcpy(prefix->bytes.data, &p->u.prefix, prefix->bytes.len);
+
+  return prefix;
+}
+
+/*
+ * qpb__l3_prefix__get
+ */
+#define qpb_l3_prefix_get qpb__l3_prefix__get
+static inline int
+qpb__l3_prefix__get (const Qpb__L3Prefix *pb_prefix, u_char family,
+                    struct prefix *prefix)
+{
+
+  switch (family)
+    {
+
+    case AF_INET:
+      memset(prefix, 0, sizeof(struct prefix_ipv4));
+      break;
+
+    case AF_INET6:
+      memset(prefix, 0, sizeof(struct prefix_ipv6));
+      break;
+
+    default:
+      memset(prefix, 0, sizeof(*prefix));
+    }
+
+  prefix->prefixlen = pb_prefix->length;
+  prefix->family = family;
+  memcpy(&prefix->u.prefix, pb_prefix->bytes.data, pb_prefix->bytes.len);
+  return 1;
+}
+
+/*
+ * qpb__protocol__set
+ *
+ * Translate a quagga route type to a protobuf protocol.
+ */
+#define qpb_protocol_set qpb__protocol__set
+static inline int
+qpb__protocol__set (Qpb__Protocol *pb_proto, int route_type)
+{
+  switch (route_type) {
+  case ZEBRA_ROUTE_KERNEL:
+    *pb_proto = QPB__PROTOCOL__KERNEL;
+    break;
+
+  case ZEBRA_ROUTE_CONNECT:
+    *pb_proto = QPB__PROTOCOL__CONNECTED;
+    break;
+
+  case ZEBRA_ROUTE_STATIC:
+    *pb_proto = QPB__PROTOCOL__STATIC;
+    break;
+
+  case ZEBRA_ROUTE_RIP:
+    *pb_proto = QPB__PROTOCOL__RIP;
+    break;
+
+  case ZEBRA_ROUTE_RIPNG:
+    *pb_proto = QPB__PROTOCOL__RIPNG;
+    break;
+
+  case ZEBRA_ROUTE_OSPF:
+  case ZEBRA_ROUTE_OSPF6:
+    *pb_proto = QPB__PROTOCOL__OSPF;
+    break;
+
+  case ZEBRA_ROUTE_ISIS:
+    *pb_proto = QPB__PROTOCOL__ISIS;
+    break;
+
+  case ZEBRA_ROUTE_BGP:
+    *pb_proto = QPB__PROTOCOL__BGP;
+    break;
+
+  case ZEBRA_ROUTE_HSLS:
+  case ZEBRA_ROUTE_OLSR:
+  case ZEBRA_ROUTE_BABEL:
+  case ZEBRA_ROUTE_MAX:
+  case ZEBRA_ROUTE_SYSTEM:
+  default:
+    *pb_proto = QPB__PROTOCOL__OTHER;
+  }
+
+  return 1;
+}
+
+/*
+ * qpb__ipv4_address__create
+ */
+static inline Qpb__Ipv4Address *
+qpb__ipv4_address__create (qpb_allocator_t *allocator,
+                          struct in_addr *addr)
+{
+  Qpb__Ipv4Address *v4;
+
+  v4 = QPB_ALLOC(allocator, typeof(*v4));
+  if (!v4) {
+    return NULL;
+  }
+  qpb__ipv4_address__init(v4);
+
+  v4->value = ntohl(addr->s_addr);
+  return v4;
+}
+
+/*
+ * qpb__ipv4_address__get
+ */
+static inline int
+qpb__ipv4_address__get (const Qpb__Ipv4Address *v4, struct in_addr *addr)
+{
+  addr->s_addr = htonl(v4->value);
+  return 1;
+}
+
+/*
+ * qpb__ipv6_address__create
+ */
+static inline Qpb__Ipv6Address *
+qpb__ipv6_address__create (qpb_allocator_t *allocator, struct in6_addr *addr)
+{
+  Qpb__Ipv6Address *v6;
+
+  v6 = QPB_ALLOC(allocator, typeof(*v6));
+  if (!v6)
+    return NULL;
+
+  qpb__ipv6_address__init(v6);
+  v6->bytes.len = 16;
+  v6->bytes.data = qpb_alloc(allocator, 16);
+  if (!v6->bytes.data)
+    return NULL;
+
+  memcpy(v6->bytes.data, addr->s6_addr, v6->bytes.len);
+  return v6;
+}
+
+/*
+ * qpb__ipv6_address__get
+ *
+ * Read out information from a protobuf ipv6 address structure.
+ */
+static inline int
+qpb__ipv6_address__get (const Qpb__Ipv6Address *v6, struct in6_addr *addr)
+{
+  if (v6->bytes.len != 16)
+    return 0;
+
+  memcpy(addr->s6_addr, v6->bytes.data, v6->bytes.len);
+  return 1;
+}
+
+/*
+ * qpb__l3_address__create
+ */
+#define qpb_l3_address_create qpb__l3_address__create
+static inline Qpb__L3Address *
+qpb__l3_address__create (qpb_allocator_t *allocator, union g_addr *addr,
+                        u_char family)
+{
+  Qpb__L3Address *l3_addr;
+
+  l3_addr = QPB_ALLOC(allocator, typeof(*l3_addr));
+  if (!l3_addr)
+    return NULL;
+
+  qpb__l3_address__init(l3_addr);
+
+  switch (family) {
+
+  case AF_INET:
+    l3_addr->v4 = qpb__ipv4_address__create (allocator, &addr->ipv4);
+    if (!l3_addr->v4)
+      return NULL;
+
+    break;
+
+  case AF_INET6:
+    l3_addr->v6 = qpb__ipv6_address__create (allocator, &addr->ipv6);
+    if (!l3_addr->v6)
+      return NULL;
+
+    break;
+  }
+  return l3_addr;
+}
+
+/*
+ * qpb__l3_address__get
+ *
+ * Read out a gateway address from a protobuf l3 address.
+ */
+#define qpb_l3_address_get qpb__l3_address__get
+static inline int
+qpb__l3_address__get (const Qpb__L3Address *l3_addr,
+                     u_char *family, union g_addr *addr)
+{
+  if (l3_addr->v4)
+    {
+      qpb__ipv4_address__get (l3_addr->v4, &addr->ipv4);
+      *family = AF_INET;
+      return 1;
+    }
+
+  if (l3_addr->v6)
+    {
+      qpb__ipv6_address__get(l3_addr->v6, &addr->ipv6);
+      *family = AF_INET6;
+      return 1;
+    }
+
+  return 0;
+}
+
+/*
+ * qpb__if_identifier__create
+ */
+#define qpb_if_identifier_create qpb__if_identifier__create
+static inline Qpb__IfIdentifier *
+qpb__if_identifier__create (qpb_allocator_t *allocator, uint if_index)
+{
+  Qpb__IfIdentifier *if_id;
+
+  if_id = QPB_ALLOC(allocator, typeof(*if_id));
+  if (!if_id) {
+    return NULL;
+  }
+  qpb__if_identifier__init(if_id);
+  if_id->has_index = 1;
+  if_id->index = if_index;
+  return if_id;
+}
+
+/*
+ * qpb__if_identifier__get
+ *
+ * Get interface name and/or if_index from an if identifier.
+ */
+#define qpb_if_identifier_get qpb__if_identifier__get
+static inline int
+qpb__if_identifier__get (Qpb__IfIdentifier *if_id, uint *if_index,
+                        char **name)
+{
+  char *str;
+  uint ix;
+
+  if (!if_index)
+    if_index = &ix;
+
+  if (!name)
+    name = &str;
+
+  if (if_id->has_index)
+    *if_index = if_id->index;
+  else
+    *if_index = 0;
+
+  *name = if_id->name;
+  return 1;
+}
+
+#endif
diff --git a/qpb/qpb.proto b/qpb/qpb.proto
new file mode 100644 (file)
index 0000000..8323d3e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * qpb.proto
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software
+ * for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear
+ * in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Protobuf definitions pertaining to the Quagga Protobuf component.
+ */
+package qpb;
+
+enum AddressFamily {
+  UNKNOWN_AF = 0;
+  IPV4 = 1;            // IP version 4
+  IPV6 = 2;            // IP version 6
+};
+
+enum SubAddressFamily {
+  UNKNOWN_SAF = 0;
+  UNICAST = 1;
+  MULTICAST = 2;
+};
+
+//
+// An IP version 4 address, such as 10.1.1.1.
+//
+message Ipv4Address {
+  required fixed32 value = 1 ;
+};
+
+message Ipv6Address {
+
+  // 16 bytes.
+  required bytes bytes = 1;
+};
+
+//
+// An IP version 4 or IP version 6 address.
+//
+message L3Address {
+  optional Ipv4Address v4 = 1;
+  optional Ipv6Address v6 = 2;
+};
+
+//
+// An IP prefix, such as 10.1/16.
+// We use the message below to represent both IPv4 and IPv6 prefixes.
+message L3Prefix {
+  required uint32 length = 1;
+  required bytes bytes = 2;
+};
+
+//
+// Something that identifies an interface on a machine. It can either
+// be a name (for instance, 'eth0') or a number currently.
+//
+message IfIdentifier {
+  optional uint32 index = 1;
+  optional string name = 2;
+};
+
+enum Protocol {
+  UNKNOWN_PROTO = 0;
+  LOCAL = 1;
+  CONNECTED = 2;
+  KERNEL = 3;
+  STATIC = 4;
+  RIP = 5;
+  RIPNG = 6;
+  OSPF = 7;
+  ISIS = 8;
+  BGP = 9;
+  OTHER = 10;
+}
\ No newline at end of file
diff --git a/qpb/qpb_allocator.c b/qpb/qpb_allocator.c
new file mode 100644 (file)
index 0000000..4b4830a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * qpb_allocator.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "linear_allocator.h"
+
+#include "qpb_allocator.h"
+
+/*
+ * _qpb_alloc
+ */
+static void *
+_qpb_alloc (void *allocator_data, size_t size)
+{
+  return linear_allocator_alloc (allocator_data, size);
+}
+
+/*
+ * _qpb_free
+ */
+static void
+_qpb_free (void *allocator_data, void *ptr)
+{
+  linear_allocator_free (allocator_data, ptr);
+}
+
+static ProtobufCAllocator allocator_template = {
+  _qpb_alloc,
+  _qpb_free,
+  NULL,
+  8192,
+  NULL
+};
+
+/*
+ * qpb_allocator_init_linear
+ *
+ * Initialize qpb_allocator_t with the given linear allocator.
+ */
+void
+qpb_allocator_init_linear (qpb_allocator_t *allocator,
+                          linear_allocator_t *linear_allocator)
+{
+  *allocator = allocator_template;
+  allocator->allocator_data = linear_allocator;
+}
diff --git a/qpb/qpb_allocator.h b/qpb/qpb_allocator.h
new file mode 100644 (file)
index 0000000..83ddf56
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * qpb_allocator.h
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Header file for quagga protobuf memory management code.
+ */
+
+#ifndef _QPB_ALLOCATOR_H_
+#define _QPB_ALLOCATOR_H_
+
+#include <google/protobuf-c/protobuf-c.h>
+
+struct linear_allocator_t_;
+
+/*
+ * Alias for ProtobufCAllocator that is easier on the fingers.
+ */
+typedef ProtobufCAllocator qpb_allocator_t;
+
+/*
+ * qpb_alloc
+ */
+static inline void *
+qpb_alloc (qpb_allocator_t *allocator, size_t size)
+{
+  return allocator->alloc (allocator->allocator_data, size);
+}
+
+/*
+ * qpb_alloc_ptr_array
+ *
+ * Allocate space for the specified number of pointers.
+ */
+static inline void *
+qpb_alloc_ptr_array (qpb_allocator_t *allocator, size_t num_ptrs)
+{
+  return qpb_alloc (allocator, num_ptrs * sizeof (void *));
+}
+
+/*
+ * qpb_free
+ */
+static inline void
+qpb_free (qpb_allocator_t *allocator, void *ptr)
+{
+  allocator->free (allocator->allocator_data, ptr);
+}
+
+/*
+ * QPB_ALLOC
+ *
+ * Convenience macro to reduce the probability of allocating memory of
+ * incorrect size. It returns enough memory to store the given type,
+ * and evaluates to an appropriately typed pointer.
+ */
+#define QPB_ALLOC(allocator, type)             \
+  (type *) qpb_alloc(allocator, sizeof(type))
+
+
+/*
+ * Externs.
+ */
+extern void qpb_allocator_init_linear (qpb_allocator_t *,
+                                      struct linear_allocator_t_ *);
+
+/*
+ * The following macros are for the common case where a qpb allocator
+ * is being used alongside a linear allocator that allocates memory
+ * off of the stack.
+ */
+#define QPB_DECLARE_STACK_ALLOCATOR(allocator, size)   \
+    qpb_allocator_t allocator;                         \
+    linear_allocator_t lin_ ## allocator;              \
+    char lin_ ## allocator ## _buf[size]
+
+#define QPB_INIT_STACK_ALLOCATOR(allocator)                            \
+  do                                                                   \
+    {                                                                  \
+      linear_allocator_init(&(lin_ ## allocator),                      \
+                           lin_ ## allocator ## _buf,                  \
+                           sizeof(lin_ ## allocator ## _buf));         \
+      qpb_allocator_init_linear(&allocator, &(lin_ ## allocator));     \
+    } while (0)
+
+#define QPB_RESET_STACK_ALLOCATOR(allocator)           \
+  do                                                   \
+    {                                                  \
+      linear_allocator_reset (&(lin_ ## allocator));   \
+    } while (0)
+
+#endif /* _QPB_ALLOCATOR_H_ */
diff --git a/redhat/.gitignore b/redhat/.gitignore
new file mode 100644 (file)
index 0000000..e399bd8
--- /dev/null
@@ -0,0 +1,10 @@
+zebra.spec
+quagga.spec
+Makefile
+Makefile.in
+.nfs*
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/redhat/Makefile.am b/redhat/Makefile.am
new file mode 100644 (file)
index 0000000..b1d49ac
--- /dev/null
@@ -0,0 +1,9 @@
+
+EXTRA_DIST = bgpd.init bgpd.service isisd.init \
+       isisd.service ospf6d.init ospf6d.service ospfd.init ospfd.service \
+       quagga.logrotate quagga.pam quagga.spec \
+       quagga.sysconfig ripd.init ripd.service ripngd.init ripngd.service \
+       watchquagga.init pimd.init pimd.service zebra.init zebra.service \
+       nhrpd.service \
+       quagga-filter-perl-requires.sh quagga-tmpfs.conf \
+       README.rpm_build.md
diff --git a/redhat/README.rpm_build.md b/redhat/README.rpm_build.md
new file mode 100644 (file)
index 0000000..3e8fa05
--- /dev/null
@@ -0,0 +1,128 @@
+Building your own Quagga RPM
+============================
+(Tested on CentOS 6, CentOS 7 and Fedora 22.)
+
+1. Install the following packages to build the RPMs:
+
+               yum install git autoconf automake libtool make gawk readline-devel \
+               texinfo dejagnu net-snmp-devel groff rpm-build net-snmp-devel \
+               libcap-devel texi2html
+
+       (use `dnf install` on new Fedora instead of `yum install        `)
+       
+2. Checkout Quagga under a **unpriviledged** user account
+
+               git clone git://git.savannah.nongnu.org/quagga.git quagga
+
+3. Run Bootstrap and make distribution tar.gz
+
+               cd quagga
+               ./bootstrap.sh
+               ./configure --with-pkg-extra-version=-MyRPMVersion
+               make dist
+                       
+       Note: configure parameters are not important for the RPM building - except the
+       `with-pkg-extra-version` if you want to give the RPM a specific name to
+       mark your own unoffical build
+
+4. Create RPM directory structure and populate with sources
+
+               mkdir rpmbuild
+               mkdir rpmbuild/SOURCES
+               mkdir rpmbuild/SPECS
+               cp redhat/*.spec rpmbuild/SPECS/
+               cp quagga*.tar.gz rpmbuild/SOURCES/
+
+5. Edit rpm/SPECS/quagga.spec with configuration as needed
+       Look at the beginning of the file and adjust the following parameters to enable
+       or disable features as required:
+       
+               ################# Quagga configure options ####################
+               # with-feature options
+        %{!?with_snmp:         %global  with_snmp       1 }
+        %{!?with_vtysh:        %global  with_vtysh      1 }
+        %{!?with_ospf_te:      %global  with_ospf_te    1 }
+               %{!?with_opaque_lsa:   %global  with_opaque_lsa 1 }
+               %{!?with_tcp_zebra:        %global  with_tcp_zebra  0 }
+               %{!?with_vtysh:        %global  with_vtysh      1 }
+               %{!?with_pam:          %global  with_pam        1 }
+        %{!?with_ospfclient:   %global  with_ospfclient 1 }
+               %{!?with_ospfapi:      %global  with_ospfapi    1 }
+               %{!?with_irdp:         %global  with_irdp       1 }
+               %{!?with_rtadv:        %global  with_rtadv      1 }
+               %{!?with_isisd:        %global  with_isisd      1 }
+               %{!?with_pimd:         %global  with_pimd       1 }
+               %{!?with_shared:       %global  with_shared     1 }
+               %{!?with_multipath:    %global  with_multipath  64 }
+               %{!?quagga_user:       %global  quagga_user     quagga }
+               %{!?vty_group:         %global  vty_group       quaggavt }
+               %{!?with_fpm:          %global  with_fpm        0 }
+               %{!?with_watchquagga:  %global  with_watchquagga 1 }
+
+6. Build the RPM
+
+               rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/quagga.spec
+
+DONE.
+
+If all works correctly, then you should end up with the RPMs under `rpmbuild/RPMS`
+and the Source RPM under `rpmbuild/SRPMS`
+
+
+Enabling daemons after installation of the package:
+---------------------------------------------------
+
+### init.d based systems (ie CentOS 6):
+
+1. Enable the daemons as needed to run after boot (Zebra is mandatory)
+       
+               chkconfig zebra on
+               chkconfig ospfd on
+               chkconfig ospf6d on
+               chkconfig bgpd on
+               ... etc
+
+2. If you want to run `watchquagga`, then configure `/etc/sysconfig/quagga` 
+   and uncomment the line with the daemons for `watchquagga` to monitor,
+   then enable watchquagga
+
+               chkconfig watchquagga on
+
+3. Check your firewall / IPtables to make sure the routing protocols are
+allowed.
+               
+4. Start the daemons (or reboot)
+
+               service zebra start
+               service bgpd start
+               service ospfd start
+               ... etc
+                       
+Configuration is stored in `/etc/quagga/*.conf` files.
+
+
+### systemd based systems (ie CentOS 7, Fedora 22)
+
+1. Enable the daemons as needed to run after boot (Zebra is mandatory)
+       
+               systemctl enable zebra
+               systemctl enable ospfd
+               systemctl enable ospf6d
+               systemctl enable bgpd
+               ... etc
+
+       Note: There is no watchquagga on systemd based systems. Systemd contains
+       the functionality of monitoring and restarting daemons.
+
+2. Check your firewall / IPtables to make sure the routing protocols are
+allowed.
+               
+3. Start the daemons (or reboot)
+
+               systemctl start zebra
+               systemctl start bgpd
+               systemctl start ospfd
+               ... etc
+                       
+Configuration is stored in `/etc/quagga/*.conf` files.
+
diff --git a/redhat/bgpd.init b/redhat/bgpd.init
new file mode 100644 (file)
index 0000000..e18511a
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/bgpd.conf
+
+### BEGIN INIT INFO
+# Provides: bgpd
+# Short-Description: BGP routing engine
+# Description: BGP routing engine for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="bgpd"
+cmd=bgpd
+LOCK_FILE=/var/lock/subsys/bgpd
+CONF_FILE=/etc/quagga/bgpd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $BGPD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/bgpd.service b/redhat/bgpd.service
new file mode 100644 (file)
index 0000000..ef24841
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=BGP routing daemon
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/bgpd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/bgpd -d $BGPD_OPTS -f /etc/quagga/bgpd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/isisd.init b/redhat/isisd.init
new file mode 100644 (file)
index 0000000..9e80530
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/isisd.conf
+
+### BEGIN INIT INFO
+# Provides: isisd
+# Short-Description: IS-IS routing engine
+# Description: IS-IS routing engine for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="isisd"
+cmd=isisd
+LOCK_FILE=/var/lock/subsys/isisd
+CONF_FILE=/etc/quagga/isisd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $ISISD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/isisd.service b/redhat/isisd.service
new file mode 100644 (file)
index 0000000..edb6eea
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=IS-IS routing daemon
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/isisd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/isisd -d $ISISD_OPTS -f /etc/quagga/isisd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/nhrpd.service b/redhat/nhrpd.service
new file mode 100644 (file)
index 0000000..63f138c
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=Quagga NHRP daemon
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/nhrpd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/nhrpd -d $NHRPD_OPTS -f /etc/quagga/nhrpdd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/ospf6d.init b/redhat/ospf6d.init
new file mode 100644 (file)
index 0000000..4133b4a
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/ospf6d.conf
+
+### BEGIN INIT INFO
+# Provides: ospf6d
+# Short-Description: OSPF routing engine for IPv6
+# Description: OSPF routing engine for use with Zebra and IPv6
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="ospf6d"
+cmd=ospf6d
+LOCK_FILE=/var/lock/subsys/ospf6d
+CONF_FILE=/etc/quagga/ospf6d.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $OSPF6D_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/ospf6d.service b/redhat/ospf6d.service
new file mode 100644 (file)
index 0000000..b53b970
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=OSPF routing daemon for IPv6
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/ospf6d.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/ospf6d -d $OSPF6D_OPTS -f /etc/quagga/ospf6d.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/ospfd.init b/redhat/ospfd.init
new file mode 100644 (file)
index 0000000..d964f38
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/ospfd.conf
+
+### BEGIN INIT INFO
+# Provides: ospfd
+# Short-Description: OSPF routing engine
+# Description: OSPF routing engine for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="ospfd"
+cmd=ospfd
+LOCK_FILE=/var/lock/subsys/ospfd
+CONF_FILE=/etc/quagga/ospfd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $OSPFD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/ospfd.service b/redhat/ospfd.service
new file mode 100644 (file)
index 0000000..5d6c5bb
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=OSPF routing daemon
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/ospfd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/ospfd -d $OSPFD_OPTS -f /etc/quagga/ospfd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/pimd.init b/redhat/pimd.init
new file mode 100644 (file)
index 0000000..49f9075
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/pimd.conf
+
+### BEGIN INIT INFO
+# Provides: pimd
+# Short-Description: PIM multicast routing engine
+# Description: PIM routing engine for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="pimd"
+cmd=pimd
+LOCK_FILE=/var/lock/subsys/pimd
+CONF_FILE=/etc/quagga/pimd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $PIMD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/pimd.service b/redhat/pimd.service
new file mode 100644 (file)
index 0000000..d62fe64
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=PIM multicast routing engine
+BindTo=zebra.service
+After=syslog.target network.target zebra.service
+ConditionPathExists=/etc/quagga/pimd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/pimd -d $PIMD_OPTS -f /etc/quagga/pimd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=network.target
diff --git a/redhat/quagga-filter-perl-requires.sh b/redhat/quagga-filter-perl-requires.sh
new file mode 100755 (executable)
index 0000000..db82cfd
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/usr/lib/rpm/perl.req $* | grep -v "Net::Telnet"
diff --git a/redhat/quagga-tmpfs.conf b/redhat/quagga-tmpfs.conf
new file mode 100644 (file)
index 0000000..8974b64
--- /dev/null
@@ -0,0 +1 @@
+d      /var/run/quagga 0755 quagga quagga
diff --git a/redhat/quagga.logrotate b/redhat/quagga.logrotate
new file mode 100644 (file)
index 0000000..afbd40c
--- /dev/null
@@ -0,0 +1,55 @@
+/var/log/quagga/zebra.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/zebra.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/bgpd.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/bgpd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/isisd.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/isisd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/ospfd.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/ospfd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/ospf6d.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/ospf6d.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/ripd.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/ripd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
+
+/var/log/quagga/ripngd.log {
+    notifempty
+    missingok
+    postrotate
+       /bin/kill -USR1 `cat /var/run/quagga/ripngd.pid 2> /dev/null` 2> /dev/null || true
+    endscript
+}
diff --git a/redhat/quagga.pam b/redhat/quagga.pam
new file mode 100644 (file)
index 0000000..9a91ad8
--- /dev/null
@@ -0,0 +1,26 @@
+#%PAM-1.0
+#
+
+##### if running quagga as root:
+# Only allow root (and possibly wheel) to use this because enable access
+# is unrestricted.
+auth       sufficient   pam_rootok.so
+
+# Uncomment the following line to implicitly trust users in the "wheel" group.
+#auth       sufficient   pam_wheel.so trust use_uid
+# Uncomment the following line to require a user to be in the "wheel" group.
+#auth       required     pam_wheel.so use_uid
+###########################################################
+
+# If using quagga privileges and with a seperate group for vty access, then
+# access can be controlled via the vty access group, and pam can simply
+# check for valid user/password, eg:
+#
+# only allow local users.
+#auth       required     pam_securetty.so
+#auth       include      system-auth
+#auth       required     pam_nologin.so
+#account    include      system-auth
+#password   include      system-auth
+#session    include      system-auth
+#session    optional     pam_console.so
diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in
new file mode 100644 (file)
index 0000000..3bf5b9d
--- /dev/null
@@ -0,0 +1,799 @@
+# configure options
+#
+# Some can be overriden on rpmbuild commandline with:
+# rpmbuild --define 'variable value'
+#   (use any value, ie 1 for flag "with_XXXX" definitions)
+#
+# E.g. rpmbuild --define 'release_rev 02' may be useful if building
+# rpms again and again on the same day, so the newer rpms can be installed.
+# bumping the number each time.
+
+####################### Quagga configure options #########################
+# with-feature options
+%{!?with_snmp:                 %global with_snmp               1 }
+%{!?with_vtysh:                        %global with_vtysh              1 }
+%{!?with_tcp_zebra:            %global with_tcp_zebra          0 }
+%{!?with_vtysh:                        %global with_vtysh              1 }
+%{!?with_pam:                  %global with_pam                1 }
+%{!?with_ospfclient:           %global with_ospfclient         1 }
+%{!?with_ospfapi:              %global with_ospfapi            1 }
+%{!?with_irdp:                 %global with_irdp               1 }
+%{!?with_rtadv:                        %global with_rtadv              1 }
+%{!?with_isisd:                        %global with_isisd              1 }
+%{!?with_pimd:                 %global with_pimd               1 }
+%{!?with_nhrpd:                        %global with_nhrpd              1 }
+%{!?with_shared:               %global with_shared             1 }
+%{!?with_multipath:            %global with_multipath          64 }
+%{!?quagga_user:               %global quagga_user             quagga }
+%{!?vty_group:                 %global vty_group               quaggavt }
+%{!?with_fpm:                  %global with_fpm                0 }
+%{!?with_watchquagga:          %global with_watchquagga        1 }
+# whether to build doc/quagga.html - requires a lot of TeX packages
+%{!?with_texi2html:            %global with_texi2html          0 }
+
+# path defines
+%define                _sysconfdir     /etc/quagga
+%define                zeb_src         %{_builddir}/%{name}-%{quaggaversion}
+%define                zeb_rh_src      %{zeb_src}/redhat
+%define                zeb_docs        %{zeb_src}/doc
+
+# defines for configure
+%define                _localstatedir  /var/run/quagga
+############################################################################
+
+#### Version String tweak
+# Remove invalid characters form version string and replace with _
+%{expand: %%global rpmversion %(echo '@VERSION@' | tr [:blank:]- _ )}
+%define                quaggaversion   @VERSION@
+
+#### Check version of texi2html 
+# Old versions don't support "--number-footnotes" option.
+%if %{with_texi2html}
+       %{expand: %%global texi2htmlversion %(type texi2html >/dev/null 2>&1 && (rpm -q --qf '%%{VERSION}' texi2html | cut -d. -f1) || echo 0 )}
+%endif
+
+#### Check for systemd or init.d (upstart)
+# Check for init.d (upstart) as used in CentOS 6 or systemd (ie CentOS 7)
+%{expand: %%global initsystem %(if [[ `/sbin/init --version 2> /dev/null` =~ upstart ]]; then echo upstart; elif [[ `systemctl` =~ -\.mount ]]; then echo systemd; fi)}
+#
+# If init system is systemd, then always disable watchquagga
+#
+%if "%{initsystem}" == "systemd"
+       # Note: For systems with systemd, watchquagga will NOT be built. Systemd
+       # takes over the role of restarting crashed processes. Value will
+       # be overwritten with 0 below for systemd independent on the setting here
+       %global with_watchquagga 0
+%endif
+
+# if FPM is enabled, then enable tcp_zebra as well
+#
+%if %{with_fpm}
+       %global with_tcp_zebra  1
+%endif
+
+# misc internal defines
+%{!?quagga_uid:                %global         quagga_uid      92 }
+%{!?quagga_gid:                %global         quagga_gid      92 }
+%{!?vty_gid:           %global         vty_gid         85 }
+
+%define                daemon_list     zebra ripd ospfd bgpd
+
+%define                daemonv6_list   ripngd ospf6d
+
+%if %{with_isisd}
+%define                daemon_isisd    isisd
+%else
+%define                daemon_isisd    ""
+%endif
+
+%if %{with_pimd}
+%define                daemon_pimd     pimd
+%else
+%define                daemon_pimd     ""
+%endif
+
+%if %{with_nhrpd}
+%define                daemon_nhrpd    nhrpd
+%else
+%define                daemon_nhrpd    ""
+%endif
+
+%if %{with_watchquagga}
+%define                daemon_watchquagga      watchquagga
+%else
+%define                daemon_watchquagga      ""
+%endif
+
+%define                all_daemons     %{daemon_list} %{daemonv6_list} %{daemon_isisd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_watchquagga}
+
+# allow build dir to be kept
+%{!?keep_build:                %global         keep_build      0 }
+
+#release sub-revision (the two digits after the CONFDATE)
+%{!?release_rev:       %global         release_rev     01 }
+
+Summary: Routing daemon
+Name:                  quagga
+Version:               %{rpmversion}
+Release:               @CONFDATE@%{release_rev}%{?dist}
+License:               GPLv2+
+Group:                 System Environment/Daemons
+Source0:               https://download.savannah.gnu.org/releases/quagga/%{name}-%{quaggaversion}.tar.gz
+URL:                   https://www.quagga.net
+Requires:              ncurses
+Requires(pre): /sbin/install-info
+Requires(preun): /sbin/install-info
+Requires(post):        /sbin/install-info
+BuildRequires: autoconf patch libcap-devel groff
+BuildRequires: perl-generators pkgconfig
+%if %{with_texi2html}
+BuildRequires: texi2html
+%endif
+%if %{with_snmp}
+BuildRequires: net-snmp-devel
+Requires:      net-snmp
+%endif
+%if %{with_vtysh}
+BuildRequires: readline readline-devel ncurses ncurses-devel
+Requires:      ncurses
+%endif
+%if %{with_pam}
+BuildRequires: pam-devel
+Requires:      pam
+%endif
+%if %{with_nhrpd}
+BuildRequires: c-ares-devel
+Requires:      c-ares
+%endif
+%if "%{initsystem}" == "systemd"
+BuildRequires:         systemd
+Requires(post):                systemd
+Requires(preun):       systemd
+Requires(postun):      systemd
+%else
+# Initscripts > 5.60 is required for IPv6 support
+Requires(pre):         initscripts >= 5.60
+%endif
+Provides:                      routingdaemon = %{version}-%{release}
+BuildRoot:                     %{_tmppath}/%{name}-%{version}-root
+#Obsoletes:                    mrt zebra quagga-sysvinit
+
+%define __perl_requires %{zeb_rh_src}/quagga-filter-perl-requires.sh
+
+%description
+Quagga is a free software routing protocol suite. 
+
+Quagga supports BGP, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM-SSM and NHRP.
+
+%package contrib
+Summary: contrib tools for quagga
+Group: System Environment/Daemons
+
+%description contrib
+Contributed/3rd party tools which may be of use with quagga.
+
+%package devel
+Summary: Header and object files for quagga development
+Group: System Environment/Daemons
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+The quagga-devel package contains the header and object files neccessary for
+developing OSPF-API and quagga applications.
+
+%prep
+%setup -q -n quagga-%{quaggaversion}
+
+%build
+
+%configure \
+    --sysconfdir=%{_sysconfdir} \
+    --libdir=%{_libdir}/quagga \
+    --libexecdir=%{_libexecdir} \
+    --localstatedir=%{_localstatedir} \
+       --disable-werror \
+%if !%{with_shared}
+       --disable-shared \
+%endif
+%if %{with_snmp}
+       --enable-snmp \
+%endif
+%if %{with_multipath}
+       --enable-multipath=%{with_multipath} \
+%endif
+%if %{with_tcp_zebra}
+       --enable-tcp-zebra \
+%endif
+%if %{with_vtysh}
+       --enable-vtysh \
+%endif
+%if %{with_ospfclient}
+       --enable-ospfclient=yes \
+%else
+       --enable-ospfclient=no\
+%endif
+%if %{with_ospfapi}
+       --enable-ospfapi=yes \
+%else
+       --enable-ospfapi=no \
+%endif
+%if %{with_irdp}
+       --enable-irdp=yes \
+%else
+       --enable-irdp=no \
+%endif
+%if %{with_rtadv}
+       --enable-rtadv=yes \
+%else
+       --enable-rtadv=no \
+%endif
+%if %{with_isisd}
+       --enable-isisd \
+%else
+       --disable-isisd \
+%endif
+%if %{with_nhrpd}
+       --enable-nhrpd \
+%else
+       --disable-nhrpd \
+%endif
+%if %{with_pam}
+       --with-libpam \
+%endif
+%if 0%{?quagga_user:1}
+       --enable-user=%quagga_user \
+       --enable-group=%quagga_user \
+%endif
+%if 0%{?vty_group:1}
+       --enable-vty-group=%vty_group \
+%endif
+%if %{with_fpm}
+       --enable-fpm \
+%else
+       --disable-fpm \
+%endif
+%if %{with_watchquagga}
+       --enable-watchquagga \
+%else
+       --disable-watchquagga \
+%endif
+       --enable-gcc-rdynamic
+
+make %{?_smp_mflags}
+
+%if %{with_texi2html}
+       pushd doc
+       %if %{texi2htmlversion} < 5
+               texi2html --number-sections quagga.texi
+       %else
+               texi2html --number-footnotes  --number-sections quagga.texi
+       %endif
+       popd
+%endif
+
+%install
+mkdir -p %{buildroot}/etc/{quagga,sysconfig,logrotate.d,pam.d} \
+       %{buildroot}/var/log/quagga %{buildroot}%{_infodir}
+make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" install
+
+# Remove this file, as it is uninstalled and causes errors when building on RH9
+rm -rf %{buildroot}/usr/share/info/dir
+
+# install /etc sources
+%if "%{initsystem}" == "systemd"
+mkdir -p %{buildroot}%{_unitdir}
+for daemon in %{all_daemons} ; do
+       if [ x"${daemon}" != x"" ] ; then
+               install %{zeb_rh_src}/${daemon}.service \
+                       %{buildroot}%{_unitdir}/${daemon}.service
+       fi
+done
+%else
+mkdir -p %{buildroot}/etc/rc.d/init.d
+for daemon in %{all_daemons} ; do
+       if [ x"${daemon}" != x"" ] ; then
+               install %{zeb_rh_src}/${daemon}.init \
+                       %{buildroot}/etc/rc.d/init.d/${daemon}
+       fi
+done
+%endif
+
+install -m644 %{zeb_rh_src}/quagga.pam \
+       %{buildroot}/etc/pam.d/quagga
+install -m644 %{zeb_rh_src}/quagga.logrotate \
+       %{buildroot}/etc/logrotate.d/quagga
+install -m644 %{zeb_rh_src}/quagga.sysconfig \
+       %{buildroot}/etc/sysconfig/quagga
+install -d -m750  %{buildroot}/var/run/quagga
+
+
+%if 0%{?_tmpfilesdir:1}
+       install -d -m 755 %{buildroot}/%{_tmpfilesdir}
+       install -p -m 644 %{zeb_rh_src}/quagga-tmpfs.conf \
+                %{buildroot}/%{_tmpfilesdir}/quagga.conf
+%endif
+
+%pre
+# add vty_group
+%if 0%{?vty_group:1}
+if getent group %vty_group > /dev/null ; then : ; else \
+       /usr/sbin/groupadd -r -g %vty_gid %vty_group > /dev/null || : ; fi
+%endif
+
+# add quagga user and group
+%if 0%{?quagga_user:1}
+# Ensure that quagga_gid gets correctly allocated
+if getent group %quagga_user >/dev/null; then : ; else \
+       /usr/sbin/groupadd -g %quagga_gid %quagga_user > /dev/null || : ; \
+fi
+if getent passwd %quagga_user >/dev/null ; then : ; else \
+       /usr/sbin/useradd  -u %quagga_uid -g %quagga_gid -G %vty_group \
+               -M -r -s /sbin/nologin -c "Quagga routing suite" \
+               -d %_localstatedir %quagga_user 2> /dev/null || : ; \
+fi
+%endif
+
+%post
+# zebra_spec_add_service <service name> <port/proto> <comment>
+# e.g. zebra_spec_add_service zebrasrv 2600/tcp "zebra service"
+
+zebra_spec_add_service ()
+{
+  # Add port /etc/services entry if it isn't already there 
+  if [ -f /etc/services ] && \
+      ! %__sed -e 's/#.*$//' /etc/services | %__grep -wq $1 ; then
+    echo "$1           $2                      # $3"  >> /etc/services
+  fi
+}
+
+zebra_spec_add_service zebrasrv 2600/tcp "zebra service"
+zebra_spec_add_service zebra   2601/tcp "zebra vty"
+zebra_spec_add_service ripd    2602/tcp "RIPd vty"
+zebra_spec_add_service ripngd  2603/tcp "RIPngd vty"
+zebra_spec_add_service ospfd   2604/tcp "OSPFd vty"
+zebra_spec_add_service bgpd    2605/tcp "BGPd vty"
+zebra_spec_add_service ospf6d  2606/tcp "OSPF6d vty"
+%if %{with_ospfapi}
+zebra_spec_add_service ospfapi 2607/tcp "OSPF-API"
+%endif
+%if %{with_isisd}
+zebra_spec_add_service isisd   2608/tcp "ISISd vty"
+%endif
+%if %{with_pimd}
+zebra_spec_add_service pimd    2611/tcp "PIMd vty"
+%endif
+%if %{with_nhrpd}
+zebra_spec_add_service nhrpd   2612/tcp "NHRPd vty"
+%endif
+
+%if "%{initsystem}" == "systemd"
+for daemon in %all_daemons ; do
+       %systemd_post ${daemon}.service
+done
+%else
+for daemon in %all_daemons ; do
+       /sbin/chkconfig --add ${daemon}
+done
+%endif
+
+if [ -f %{_infodir}/%{name}.inf* ]; then
+       /sbin/install-info %{_infodir}/quagga.info %{_infodir}/dir
+fi
+
+# Create dummy files if they don't exist so basic functions can be used.
+if [ ! -e %{_sysconfdir}/zebra.conf ]; then
+       echo "hostname `hostname`" > %{_sysconfdir}/zebra.conf
+%if 0%{?quagga_user:1}
+       chown %quagga_user:%quagga_user %{_sysconfdir}/zebra.conf*
+%endif
+       chmod 640 %{_sysconfdir}/zebra.conf
+fi
+for daemon in %{all_daemons} ; do
+       if [ ! -e %{_sysconfdir}/${daemon}.conf ]; then
+               touch %{_sysconfdir}/${daemon}.conf
+               %if 0%{?quagga_user:1}
+                       chown %quagga_user:%quagga_user %{_sysconfdir}/${daemon}.conf*
+               %endif
+       fi
+done
+%if %{with_watchquagga}
+       # No config for watchquagga - this is part of /etc/sysconfig/quagga
+       rm -f %{_sysconfdir}/watchquagga.*
+%endif
+
+if [ ! -e %{_sysconfdir}/vtysh.conf ]; then
+       touch %{_sysconfdir}/vtysh.conf
+       chmod 640 %{_sysconfdir}/vtysh.conf
+%if 0%{?vty_group:1}
+    chown quagga:%{vty_group} %{_sysconfdir}/vtysh.conf*
+%endif
+fi
+
+%postun
+if [ "$1" -ge 1 ]; then
+       # Find out which daemons need to be restarted.
+       for daemon in %all_daemons ; do
+               if [ -f /var/lock/subsys/${daemon} ]; then
+                       eval restart_${daemon}=yes
+               else
+                       eval restart_${daemon}=no
+               fi
+       done
+       # Rename restart flags for daemons handled specially.
+       running_zebra="$restart_zebra"
+       restart_zebra=no
+       %if %{with_watchquagga}
+               running_watchquagga="$restart_watchquagga"
+               restart_watchquagga=no
+       %endif
+       
+       %if "%{initsystem}" == "systemd"
+               ##
+               ## Systemd Version
+               ##
+               # No watchquagga for systemd version
+               #
+               # Stop all daemons other than zebra.
+               for daemon in %all_daemons ; do
+                       eval restart=\$restart_${daemon}
+                       [ "$restart" = yes ] && \
+                               %systemd_postun_with_restart ${daemon}.service
+               done
+               # Restart zebra.
+               [ "$running_zebra" = yes ] && \
+                       %systemd_postun_with_restart $daemon.service
+               # Start all daemons other than zebra.
+               for daemon in %all_daemons ; do
+                       eval restart=\$restart_${daemon}
+                       [ "$restart" = yes ] && \
+                               %systemd_post ${daemon}.service
+               done
+       %else
+               ##
+               ## init.d Version
+               ##
+               %if %{with_watchquagga}
+                       # Stop watchquagga first.
+                       [ "$running_watchquagga" = yes ] && \
+                               /etc/rc.d/init.d/watchquagga stop >/dev/null 2>&1
+               %endif
+               # Stop all daemons other than zebra and watchquagga.
+               for daemon in %all_daemons ; do
+                       eval restart=\$restart_${daemon}
+                       [ "$restart" = yes ] && \
+                               /etc/rc.d/init.d/${daemon} stop >/dev/null 2>&1
+               done
+               # Restart zebra.
+               [ "$running_zebra" = yes ] && \
+                       /etc/rc.d/init.d/zebra restart >/dev/null 2>&1
+               # Start all daemons other than zebra and watchquagga.
+               for daemon in %all_daemons ; do
+                       eval restart=\$restart_${daemon}
+                       [ "$restart" = yes ] && \
+                               /etc/rc.d/init.d/${daemon} start >/dev/null 2>&1
+               done
+               %if %{with_watchquagga}
+                       # Start watchquagga last.
+                       # Avoid postun scriptlet error if watchquagga is not running. 
+                       [ "$running_watchquagga" = yes ] && \
+                               /etc/rc.d/init.d/watchquagga start >/dev/null 2>&1 || :
+               %endif  
+       %endif
+fi
+
+if [ -f %{_infodir}/%{name}.inf* ]; then
+       /sbin/install-info --delete %{_infodir}/quagga.info %{_infodir}/dir
+fi
+
+%preun
+%if "%{initsystem}" == "systemd"
+       ##
+       ## Systemd Version
+       ##
+       if [ "$1" = "0" ]; then
+               for daemon in %all_daemons ; do
+                       %systemd_preun ${daemon}.service
+               done
+       fi
+%else
+       ##
+       ## init.d Version
+       ##
+       if [ "$1" = "0" ]; then
+               for daemon in %all_daemons ; do
+                       /etc/rc.d/init.d/${daemon} stop >/dev/null 2>&1
+                       /sbin/chkconfig --del ${daemon}
+               done
+       fi
+%endif
+
+%clean
+%if !0%{?keep_build:1}
+rm -rf %{buildroot}
+%endif
+
+%files
+%defattr(-,root,root)
+%doc */*.sample* AUTHORS COPYING
+%if %{with_texi2html}
+       %doc doc/quagga.html
+%endif
+%doc doc/mpls
+%doc ChangeLog INSTALL NEWS README REPORTING-BUGS SERVICES TODO
+%if 0%{?quagga_user:1}
+%dir %attr(751,%quagga_user,%quagga_user) %{_sysconfdir}
+%dir %attr(750,%quagga_user,%quagga_user) /var/log/quagga 
+%dir %attr(751,%quagga_user,%quagga_user) /var/run/quagga
+%else
+%dir %attr(750,root,root) %{_sysconfdir}
+%dir %attr(750,root,root) /var/log/quagga
+%dir %attr(750,root,root) /var/run/quagga
+%endif
+%if 0%{?vty_group:1}
+%attr(750,%quagga_user,%vty_group) %{_sysconfdir}/vtysh.conf.sample
+%endif
+%{_infodir}/quagga.info*.gz
+%{_mandir}/man*/*
+%if %{with_watchquagga}
+       %{_mandir}/man*/watchquagga.*
+%endif
+%{_sbindir}/zebra
+%{_sbindir}/ospfd
+%{_sbindir}/ripd
+%{_sbindir}/bgpd
+%if %{with_watchquagga}
+       %{_sbindir}/watchquagga
+%endif
+%{_sbindir}/ripngd
+%{_sbindir}/ospf6d
+%if %{with_pimd}
+%{_sbindir}/pimd
+%endif
+%if %{with_isisd}
+%{_sbindir}/isisd
+%endif
+%if %{with_nhrpd}
+%{_sbindir}/nhrpd
+%endif
+%if %{with_shared}
+%{_libdir}/quagga/lib*.so
+%{_libdir}/quagga/lib*.so.?
+%attr(755,root,root) %{_libdir}/quagga/lib*.so.?.?.?
+%endif
+%if %{with_vtysh}
+%{_bindir}/*
+%endif
+%config /etc/quagga/[!v]*
+%if "%{initsystem}" == "systemd"
+       %{_unitdir}/*.service
+%else
+       %config /etc/rc.d/init.d/zebra
+       %if %{with_watchquagga}
+               %config /etc/rc.d/init.d/watchquagga
+       %endif
+       %config /etc/rc.d/init.d/ripd
+       %config /etc/rc.d/init.d/ospfd
+       %config /etc/rc.d/init.d/bgpd
+       %config /etc/rc.d/init.d/ripngd
+       %config /etc/rc.d/init.d/ospf6d
+       %if %{with_isisd}
+               %config /etc/rc.d/init.d/isisd
+       %endif
+       %if %{with_pimd}
+               %config /etc/rc.d/init.d/pimd
+       %endif
+       %if %{with_nhrpd}
+               %config /etc/rc.d/init.d/nhrpd
+       %endif
+%endif
+%config(noreplace) /etc/sysconfig/quagga
+%config(noreplace) /etc/pam.d/quagga
+%config(noreplace) %attr(640,root,root) /etc/logrotate.d/*
+%{_tmpfilesdir}/quagga.conf
+
+%files contrib
+%defattr(-,root,root)
+%doc AUTHORS COPYING %attr(0644,root,root) tools
+
+%files devel
+%defattr(-,root,root)
+%doc AUTHORS COPYING
+%if %{with_ospfclient}
+%{_sbindir}/ospfclient
+%endif
+%dir %{_libdir}/quagga/
+%{_libdir}/quagga/*.a
+%{_libdir}/quagga/*.la
+%dir %attr(755,root,root) %{_includedir}/%{name}
+%{_includedir}/%name/*.h
+%dir %attr(755,root,root) %{_includedir}/%{name}/ospfd
+%{_includedir}/%name/ospfd/*.h
+%if %{with_ospfapi}
+%dir %attr(755,root,root) %{_includedir}/%{name}/ospfapi
+%{_includedir}/%name/ospfapi/*.h
+%endif
+
+%changelog
+* Sun Mar 5 2017 Paul Jakma <paul@jakma.org>
+- Fix lint errors
+- Make texi2html conditional, disable by default to avoid needing TeX
+  by default
+
+* Mon Feb 27 2017 Paul Jakma <paul@jakma.org>
+- Apply 0001-systemd-various-service-file-improvements.patch from Fedora
+- Review Fedora spec file and sync up with any useful differences, inc:
+- Add tmpfiles.d config for Quagga from Fedora
+- Add quagga-filter-perl-requires.sh from Fedora.
+- Move libs to %%{_libdir}/quagga as per Fedora
+- use systemd_postun_with_restart for postun
+
+* Tue Feb 14 2017 Timo Teräs <timo.teras@iki.fi>
+- add nhrpd
+
+* Thu Feb 11 2016 Paul Jakma <paul@jakma.org>
+- remove with_ipv6 conditionals, always build v6
+- Fix UTF-8 char in spec changelog
+- remove quagga.pam.stack, long deprecated.
+
+* Thu Oct 22 2015 Martin Winter <mwinter@opensourcerouting.org>
+- Cleanup configure: remove --enable-ipv6 (default now), --enable-nssa,
+    --enable-netlink
+- Remove support for old fedora 4/5
+- Fix for package nameing
+- Fix Weekdays of previous changelogs (bogus dates)
+- Add conditional logic to only build tex footnotes with supported texi2html 
+- Added pimd to files section and fix double listing of /var/lib*/quagga
+- Numerous fixes to unify upstart/systemd startup into same spec file
+- Only allow use of watchquagga for non-systemd systems. no need with systemd
+
+* Fri Sep  4 2015 Paul Jakma <paul@jakma.org>
+- buildreq updates
+- add a default define for with_pimd
+
+* Mon Sep 12 2005 Paul Jakma <paul@dishone.st>
+- Steal some changes from Fedora spec file:
+- Add with_rtadv variable
+- Test for groups/users with getent before group/user adding
+- Readline need not be an explicit prerequisite
+- install-info delete should be postun, not preun
+
+* Wed Jan 12 2005 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
+- on package upgrade, implement careful, phased restart logic
+- use gcc -rdynamic flag when linking for better backtraces
+
+* Wed Dec 22 2004 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
+- daemonv6_list should contain only IPv6 daemons
+
+* Wed Dec 22 2004 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
+- watchquagga added
+- on upgrade, all daemons should be condrestart'ed
+- on removal, all daemons should be stopped
+
+* Mon Nov 08 2004 Paul Jakma <paul@dishone.st>
+- Use makeinfo --html to generate quagga.html
+
+* Sun Nov 07 2004 Paul Jakma <paul@dishone.st>
+- Fix with_ipv6 set to 0 build
+
+* Sat Oct 23 2004 Paul Jakma <paul@dishone.st>
+- Update to 0.97.2
+
+* Sat Oct 23 2004 Andrew J. Schorr <aschorr@telemetry-investments.com>
+- Make directories be owned by the packages concerned
+- Update logrotate scripts to use correct path to killall and use pid files
+
+* Fri Oct 08 2004 Paul Jakma <paul@dishone.st>
+- Update to 0.97.0
+
+* Wed Sep 15 2004 Paul Jakma <paul@dishone.st>
+- build snmp support by default
+- build irdp support
+- build with shared libs
+- devel subpackage for archives and headers
+
+* Thu Jan 08 2004 Paul Jakma <paul@dishone.st>
+- updated sysconfig files to specify local dir
+- added ospf_dump.c crash quick fix patch
+- added ospfd persistent interface configuration patch
+
+* Tue Dec 30 2003 Paul Jakma <paul@dishone.st>
+- sync to CVS
+- integrate RH sysconfig patch to specify daemon options (RH)
+- default to have vty listen only to 127.1 (RH)
+- add user with fixed UID/GID (RH)
+- create user with shell /sbin/nologin rather than /bin/false (RH)
+- stop daemons on uninstall (RH)
+- delete info file on preun, not postun to avoid deletion on upgrade. (RH)
+- isisd added
+- cleanup tasks carried out for every daemon
+
+* Sun Nov 2 2003 Paul Jakma <paul@dishone.st>
+- Fix -devel package to include all files
+- Sync to 0.96.4
+
+* Tue Aug 12 2003 Paul Jakma <paul@dishone.st>
+- Renamed to Quagga
+- Sync to Quagga release 0.96
+
+* Thu Mar 20 2003 Paul Jakma <paul@dishone.st>
+- zebra privileges support
+
+* Tue Mar 18 2003 Paul Jakma <paul@dishone.st>
+- Fix mem leak in 'show thread cpu'
+- Ralph Keller's OSPF-API
+- Amir: Fix configure.ac for net-snmp
+
+* Sat Mar 1 2003 Paul Jakma <paul@dishone.st>
+- ospfd IOS prefix to interface matching for 'network' statement
+- temporary fix for PtP and IPv6
+- sync to zebra.org CVS
+
+* Mon Jan 20 2003 Paul Jakma <paul@dishone.st>
+- update to latest cvs
+- Yon's "show thread cpu" patch - 17217
+- walk up tree - 17218
+- ospfd NSSA fixes - 16681
+- ospfd nsm fixes - 16824
+- ospfd OLSA fixes and new feature - 16823 
+- KAME and ifindex fixes - 16525
+- spec file changes to allow redhat files to be in tree
+
+* Sat Dec 28 2002 Alexander Hoogerhuis <alexh@ihatent.com>
+- Added conditionals for building with(out) IPv6, vtysh, RIP, BGP
+- Fixed up some build requirements (patch)
+- Added conditional build requirements for vtysh / snmp
+- Added conditional to files for _bindir depending on vtysh
+
+* Mon Nov 11 2002 Paul Jakma <paulj@alphyra.ie>
+- update to latest CVS
+- add Greg Troxel's md5 buffer copy/dup fix
+- add RIPv1 fix
+- add Frank's multicast flag fix
+
+* Wed Oct 09 2002 Paul Jakma <paulj@alphyra.ie>
+- update to latest CVS
+- timestamped crypt_seqnum patch
+- oi->on_write_q fix
+
+* Mon Sep 30 2002 Paul Jakma <paulj@alphyra.ie>
+- update to latest CVS
+- add vtysh 'write-config (integrated|daemon)' patch
+- always 'make rebuild' in vtysh/ to catch new commands
+
+* Fri Sep 13 2002 Paul Jakma <paulj@alphyra.ie>
+- update to 0.93b
+
+* Wed Sep 11 2002 Paul Jakma <paulj@alphyra.ie>
+- update to latest CVS
+- add "/sbin/ip route flush proto zebra" to zebra RH init on startup
+
+* Sat Aug 24 2002 Paul Jakma <paulj@alphyra.ie>
+- update to current CVS
+- add OSPF point to multipoint patch
+- add OSPF bugfixes
+- add BGP hash optimisation patch
+
+* Fri Jun 14 2002 Paul Jakma <paulj@alphyra.ie>
+- update to 0.93-pre1 / CVS
+- add link state detection support
+- add generic PtP and RFC3021 support
+- various bug fixes
+
+* Thu Aug 09 2001 Elliot Lee <sopwith@redhat.com> 0.91a-6
+- Fix bug #51336
+
+* Wed Aug  1 2001 Trond Eivind Glomsrød <teg@redhat.com> 0.91a-5
+- Use generic initscript strings instead of initscript specific
+  ( "Starting foo: " -> "Starting $prog:" )
+
+* Fri Jul 27 2001 Elliot Lee <sopwith@redhat.com> 0.91a-4
+- Bump the release when rebuilding into the dist.
+
+* Tue Feb  6 2001 Tim Powers <timp@redhat.com>
+- built for Powertools
+
+* Sun Feb  4 2001 Pekka Savola <pekkas@netcore.fi> 
+- Hacked up from PLD Linux 0.90-1, Mandrake 0.90-1mdk and one from zebra.org.
+- Update to 0.91a
+- Very heavy modifications to init.d/*, .spec, pam, i18n, logrotate, etc.
+- Should be quite Red Hat'isque now.
diff --git a/redhat/quagga.sysconfig b/redhat/quagga.sysconfig
new file mode 100644 (file)
index 0000000..caa0fff
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Default: Bind all daemon vtys to the loopback(s) only
+#
+BABELD_OPTS="-A 127.0.0.1"
+BGPD_OPTS="-A 127.0.0.1"
+ISISD_OPTS="-A ::1"
+OSPF6D_OPTS="-A ::1"
+OSPFD_OPTS="-A 127.0.0.1"
+RIPD_OPTS="-A 127.0.0.1"
+RIPNGD_OPTS="-A ::1"
+ZEBRA_OPTS="-A 127.0.0.1"
+PIMD_OPTS="-A 127.0.0.1"
+
+# Watchquagga configuration for LSB initscripts
+#
+# (Not needed with systemd: the service files are configured to automatically
+# restart any daemon on failure. If zebra fails, all running daemons will be
+# stopped; zebra will be started again; and then the previously running daemons
+# will be started again.)
+#
+# Uncomment and edit this line to reflect the daemons you are actually using:
+#WATCH_DAEMONS="zebra bgpd ospfd ospf6d ripd ripngd"
+#
+# Timer values can be adjusting by editing this line:
+WATCH_OPTS="-Az -b_ -r/sbin/service_%s_restart -s/sbin/service_%s_start -k/sbin/service_%s_stop"
diff --git a/redhat/ripd.init b/redhat/ripd.init
new file mode 100644 (file)
index 0000000..9b412cb
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/ripd.conf
+
+### BEGIN INIT INFO
+# Provides: ripd
+# Short-Description: RIP routing engine
+# Description: RIP routing engine for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="ripd"
+cmd=ripd
+LOCK_FILE=/var/lock/subsys/ripd
+CONF_FILE=/etc/quagga/ripd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $RIPD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/ripd.service b/redhat/ripd.service
new file mode 100644 (file)
index 0000000..ed7f922
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=RIP routing daemon
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/ripd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/ripd -d $RIPD_OPTS -f /etc/quagga/ripd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/ripngd.init b/redhat/ripngd.init
new file mode 100644 (file)
index 0000000..88f346f
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+# chkconfig: - 16 84
+# config: /etc/quagga/ripngd.conf
+
+### BEGIN INIT INFO
+# Provides: ripngd
+# Short-Description: RIP routing engine for IPv6
+# Description: RIP routing engine for use with Zebra and IPv6
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="ripngd"
+cmd=ripngd
+LOCK_FILE=/var/lock/subsys/ripngd
+CONF_FILE=/etc/quagga/ripngd.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $RIPNGD_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/ripngd.service b/redhat/ripngd.service
new file mode 100644 (file)
index 0000000..2519b31
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=RIP routing daemon for IPv6
+BindsTo=zebra.service
+Wants=network.target
+After=zebra.service network-pre.target
+Before=network.target
+ConditionPathExists=/etc/quagga/ripngd.conf
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/quagga
+ExecStart=/usr/sbin/ripngd -d $RIPNGD_OPTS -f /etc/quagga/ripngd.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/watchquagga.init b/redhat/watchquagga.init
new file mode 100644 (file)
index 0000000..dda3506
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+# chkconfig: 2345 17 83
+
+### BEGIN INIT INFO
+# Provides: watchquagga
+# Short-Description: Quagga watchdog
+# Description: Quagga watchdog for use with Zebra
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="watchquagga"
+cmd=watchquagga
+LOCK_FILE=/var/lock/subsys/watchquagga
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # Check that there are daemons to be monitored.
+       [ -z "$WATCH_DAEMONS" ] && exit 1
+
+       echo -n $"Starting $PROG: "
+       daemon $cmd -d $WATCH_OPTS $WATCH_DAEMONS
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/zebra.init b/redhat/zebra.init
new file mode 100644 (file)
index 0000000..4242b16
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/bash
+# chkconfig: - 15 85
+# config: /etc/quagga/zebra.conf
+
+### BEGIN INIT INFO
+# Provides: zebra
+# Short-Description: GNU Zebra routing manager
+# Description: GNU Zebra routing manager
+### END INIT INFO
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+# Get network config
+. /etc/sysconfig/network
+
+# quagga command line options
+. /etc/sysconfig/quagga
+
+RETVAL=0
+PROG="zebra"
+cmd=zebra
+LOCK_FILE=/var/lock/subsys/zebra
+CONF_FILE=/etc/quagga/zebra.conf
+
+case "$1" in
+  start)
+       # Check that networking is up.
+       [ "${NETWORKING}" = "no" ] && exit 1
+
+       # The process must be configured first.
+       [ -f $CONF_FILE ] || exit 6
+       if [ `id -u` -ne 0 ]; then
+               echo $"Insufficient privilege" 1>&2
+               exit 4
+       fi
+
+       echo -n $"Starting $PROG: "
+       /sbin/ip route flush proto zebra
+       daemon $cmd -d $ZEBRA_OPTS -f $CONF_FILE
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && touch $LOCK_FILE
+       echo
+       ;;
+  stop)
+       echo -n $"Shutting down $PROG: "
+       killproc $cmd
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
+       echo
+       ;;
+  restart|reload|force-reload)
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  condrestart|try-restart)
+       if [ -f $LOCK_FILE ]; then
+               $0 stop
+               $0 start
+       fi
+       RETVAL=$?
+       ;;
+  status)
+       status $cmd
+       RETVAL=$?
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       exit 2
+esac
+
+exit $RETVAL
diff --git a/redhat/zebra.service b/redhat/zebra.service
new file mode 100644 (file)
index 0000000..f9107f1
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=GNU Zebra routing manager
+Wants=network.target
+Before=network.target
+After=network-pre.target
+ConditionPathExists=/etc/quagga/zebra.conf
+
+[Service]
+Type=forking
+EnvironmentFile=-/etc/sysconfig/quagga
+ExecStartPre=/sbin/ip route flush proto zebra
+ExecStart=/usr/sbin/zebra -d $ZEBRA_OPTS -f /etc/quagga/zebra.conf
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/release.sh b/release.sh
new file mode 100755 (executable)
index 0000000..d57ea98
--- /dev/null
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+if [ $# -ne 2 ]  ; then
+        echo "usage: $0 <quagga-release-tag> <quagga-previous-release-tag>"
+        exit
+fi
+
+errmsg () {
+       echo "Error occurred. To rerun you may first need to delete the tag".
+       exit 1
+}
+
+trap errmsg ERR
+
+REL=${1:?Release version must be given as first argument!}
+PREV=${2:?Previous release version must be given as second argument!}
+
+TMPDIR=`mktemp -d /tmp/quagga-rel-XXXXXXXXX`
+
+if [ ! -d $TMPDIR ] ; then 
+       echo "Problem making temp directory ${TMPDIR}!"
+       exit 1;
+fi
+
+echo "Tagging branch head as release ${REL}"
+
+git tag -u 0x6FE57CA8C1A4AEA6 -m "Quagga release $REL" ${REL}
+
+mkdir -p ${TMPDIR}/a || exit 1
+mkdir -p ${TMPDIR}/verify || exit 1
+
+echo "Making git archive"
+
+( git archive ${REL} | tar xC ${TMPDIR}/a ) || exit 1
+
+git log ${PREV}..${REL} > ${TMPDIR}/a/${REL}.changelog.txt || exit 1
+git log --pretty=%s ${PREV}..${REL} > ${TMPDIR}/a/${REL}.subjects.txt || exit 1
+
+cd ${TMPDIR}/a || exit 1
+
+echo "Doing test build of archive file and making dist tarball"
+
+(autoreconf -i && ./configure && make -j && make dist-gzip) || exit 1
+
+echo "Verifying dist tarball"
+
+cp ${REL}.tar.gz ${TMPDIR}/verify || exit 1
+
+cd ${TMPDIR}/verify || exit 1
+tar -zxf ${REL}.tar.gz || exit 1
+cd ${REL} || exit 1
+autoreconf -i && ./configure && make -j
+
+cd ${TMPDIR}/a || exit 1
+gpg -u 0x6FE57CA8C1A4AEA6 -a --detach-sign ${REL}.tar.gz
+
+cat <<- EOF
+
+Release tagged as: ${REL}
+
+Release files are in ${TMPDIR}/a:
+
+       ${TMPDIR}/a/${REL}.tar.gz
+       ${TMPDIR}/a/${REL}.tar.gz.asc
+       ${TMPDIR}/a/${REL}.changelog.txt
+
+If you need to redo the release, you must delete the tag first:
+
+       git tag -d ${REL}
+
+To finish the release:
+
+* push the tag to savannah:
+
+       git push <savannah remote name> tag ${REL}
+
+* Upload the 3 files to the savannah releases area:
+
+       scp ${TMPDIR}/a/${REL}.tar.gz \
+               ${TMPDIR}/a/${REL}.tar.gz.asc \
+               ${TMPDIR}/a/${REL}.changelog.txt
+               <username>@dl.sv.nongnu.org:/releases/quagga
+
+* Update the version list in bugzilla:
+
+  https://bugzilla.quagga.net/editversions.cgi?action=add&product=Quagga
+  
+* Add a news entry to the Savannah front page. The short list of commit
+  subjects (${TMPDIR}/a/${REL}.subjects.txt) may be useful here.
+
+* Email the quagga-dev and quagga-users lists
+
+EOF
diff --git a/ripd/.gitignore b/ripd/.gitignore
new file mode 100644 (file)
index 0000000..9bcfb63
--- /dev/null
@@ -0,0 +1,17 @@
+Makefile
+Makefile.in
+*.o
+ripd
+ripd.conf
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+*.a
diff --git a/ripd/Makefile.am b/ripd/Makefile.am
new file mode 100644 (file)
index 0000000..571a499
--- /dev/null
@@ -0,0 +1,28 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = librip.a
+sbin_PROGRAMS = ripd
+
+librip_a_SOURCES = \
+       ripd.c rip_zebra.c rip_interface.c rip_debug.c rip_snmp.c \
+       rip_routemap.c rip_peer.c rip_offset.c
+
+noinst_HEADERS = \
+       ripd.h rip_debug.h rip_interface.h
+
+ripd_SOURCES = \
+       rip_main.c $(librip_a_SOURCES)
+
+ripd_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = ripd.conf.sample
+
+EXTRA_DIST = RIPv2-MIB.txt
+
diff --git a/ripd/RIPv2-MIB.txt b/ripd/RIPv2-MIB.txt
new file mode 100644 (file)
index 0000000..6c92fb5
--- /dev/null
@@ -0,0 +1,530 @@
+   RIPv2-MIB DEFINITIONS ::= BEGIN
+
+   IMPORTS
+       MODULE-IDENTITY, OBJECT-TYPE, Counter32,
+       TimeTicks, IpAddress                     FROM SNMPv2-SMI
+       TEXTUAL-CONVENTION, RowStatus            FROM SNMPv2-TC
+       MODULE-COMPLIANCE, OBJECT-GROUP          FROM SNMPv2-CONF
+       mib-2                                    FROM RFC1213-MIB;
+
+   --  This MIB module uses the extended OBJECT-TYPE macro as
+   --  defined in [9].
+
+   rip2  MODULE-IDENTITY
+           LAST-UPDATED "9407272253Z"      -- Wed Jul 27 22:53:04 PDT 1994
+           ORGANIZATION "IETF RIP-II Working Group"
+           CONTACT-INFO
+          "       Fred Baker
+          Postal: Cisco Systems
+                  519 Lado Drive
+                  Santa Barbara, California 93111
+          Tel:    +1 805 681 0115
+          E-Mail: fbaker@cisco.com
+
+          Postal: Gary Malkin
+                  Xylogics, Inc.
+                  53 Third Avenue
+                  Burlington, MA  01803
+
+          Phone:  (617) 272-8140
+          EMail:  gmalkin@Xylogics.COM"
+      DESCRIPTION
+         "The MIB module to describe the RIP2 Version 2 Protocol"
+     ::= { mib-2 23 }
+
+ --  RIP-2 Management Information Base
+
+ -- the RouteTag type represents the contents of the
+ -- Route Domain field in the packet header or route entry.
+ -- The use of the Route Domain is deprecated.
+
+ RouteTag ::= TEXTUAL-CONVENTION
+     STATUS      current
+     DESCRIPTION
+        "the RouteTag type represents the contents of the Route Domain
+        field in the packet header or route entry"
+    SYNTAX      OCTET STRING (SIZE (2))
+
+--4.1 Global Counters
+
+--      The RIP-2 Globals Group.
+--      Implementation of this group is mandatory for systems
+--      which implement RIP-2.
+
+-- These counters are intended to facilitate debugging quickly
+-- changing routes or failing neighbors
+
+rip2Globals OBJECT IDENTIFIER ::= { rip2 1 }
+
+    rip2GlobalRouteChanges OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of route changes made to the IP Route
+           Database by RIP.  This does not include the refresh
+           of a route's age."
+       ::= { rip2Globals 1 }
+
+    rip2GlobalQueries OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of responses sent to RIP queries
+           from other systems."
+       ::= { rip2Globals 2 }
+
+--4.2 RIP Interface Tables
+
+--  RIP Interfaces Groups
+--  Implementation of these Groups is mandatory for systems
+--  which implement RIP-2.
+
+-- The RIP Interface Status Table.
+
+    rip2IfStatTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF Rip2IfStatEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A list of subnets which require separate
+           status monitoring in RIP."
+       ::= { rip2 2 }
+
+   rip2IfStatEntry OBJECT-TYPE
+       SYNTAX   Rip2IfStatEntry
+       MAX-ACCESS   not-accessible
+       STATUS   current
+       DESCRIPTION
+          "A Single Routing Domain in a single Subnet."
+      INDEX { rip2IfStatAddress }
+      ::= { rip2IfStatTable 1 }
+
+    Rip2IfStatEntry ::=
+        SEQUENCE {
+            rip2IfStatAddress
+                IpAddress,
+            rip2IfStatRcvBadPackets
+                Counter32,
+            rip2IfStatRcvBadRoutes
+                Counter32,
+            rip2IfStatSentUpdates
+                Counter32,
+            rip2IfStatStatus
+                RowStatus
+    }
+
+    rip2IfStatAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address of this system on the indicated
+           subnet. For unnumbered interfaces, the value 0.0.0.N,
+           where the least significant 24 bits (N) is the ifIndex
+           for the IP Interface in network byte order."
+       ::= { rip2IfStatEntry 1 }
+
+    rip2IfStatRcvBadPackets OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of RIP response packets received by
+           the RIP process which were subsequently discarded
+           for any reason (e.g. a version 0 packet, or an
+           unknown command type)."
+       ::= { rip2IfStatEntry 2 }
+
+    rip2IfStatRcvBadRoutes OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of routes, in valid RIP packets,
+           which were ignored for any reason (e.g. unknown
+           address family, or invalid metric)."
+       ::= { rip2IfStatEntry 3 }
+
+    rip2IfStatSentUpdates OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of triggered RIP updates actually
+           sent on this interface.  This explicitly does
+           NOT include full updates sent containing new
+           information."
+       ::= { rip2IfStatEntry 4 }
+
+    rip2IfStatStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "Writing invalid has the effect of deleting
+           this interface."
+       ::= { rip2IfStatEntry 5 }
+
+-- The RIP Interface Configuration Table.
+
+    rip2IfConfTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF Rip2IfConfEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A list of subnets which require separate
+           configuration in RIP."
+       ::= { rip2 3 }
+
+   rip2IfConfEntry OBJECT-TYPE
+       SYNTAX   Rip2IfConfEntry
+       MAX-ACCESS   not-accessible
+       STATUS   current
+       DESCRIPTION
+          "A Single Routing Domain in a single Subnet."
+      INDEX { rip2IfConfAddress }
+      ::= { rip2IfConfTable 1 }
+
+    Rip2IfConfEntry ::=
+        SEQUENCE {
+            rip2IfConfAddress
+                IpAddress,
+            rip2IfConfDomain
+                RouteTag,
+            rip2IfConfAuthType
+                INTEGER,
+            rip2IfConfAuthKey
+                OCTET STRING (SIZE(0..16)),
+            rip2IfConfSend
+                INTEGER,
+            rip2IfConfReceive
+                INTEGER,
+            rip2IfConfDefaultMetric
+                INTEGER,
+            rip2IfConfStatus
+                RowStatus,
+            rip2IfConfSrcAddress
+                IpAddress
+    }
+
+    rip2IfConfAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address of this system on the indicated
+           subnet.  For unnumbered interfaces, the value 0.0.0.N,
+           where the least significant 24 bits (N) is the ifIndex
+           for the IP Interface in network byte order."
+       ::= { rip2IfConfEntry 1 }
+
+    rip2IfConfDomain OBJECT-TYPE
+        SYNTAX   RouteTag
+        MAX-ACCESS   read-create
+        STATUS   obsolete
+        DESCRIPTION
+           "Value inserted into the Routing Domain field
+           of all RIP packets sent on this interface."
+       DEFVAL { '0000'h }
+       ::= { rip2IfConfEntry 2 }
+
+    rip2IfConfAuthType OBJECT-TYPE
+        SYNTAX   INTEGER {
+                    noAuthentication (1),
+                    simplePassword (2),
+                    md5 (3)
+                 }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The type of Authentication used on this
+           interface."
+       DEFVAL { noAuthentication }
+       ::= { rip2IfConfEntry 3 }
+
+    rip2IfConfAuthKey OBJECT-TYPE
+        SYNTAX   OCTET STRING (SIZE(0..16))
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The value to be used as the Authentication Key
+           whenever the corresponding instance of
+           rip2IfConfAuthType has a value other than
+           noAuthentication.  A modification of the corresponding
+           instance of rip2IfConfAuthType does not modify
+           the rip2IfConfAuthKey value.  If a string shorter
+           than 16 octets is supplied, it will be left-
+           justified and padded to 16 octets, on the right,
+           with nulls (0x00).
+
+           Reading this object always results in an  OCTET
+           STRING of length zero; authentication may not
+           be bypassed by reading the MIB object."
+       DEFVAL { ''h }
+       ::= { rip2IfConfEntry 4 }
+
+    rip2IfConfSend OBJECT-TYPE
+        SYNTAX   INTEGER {
+                    doNotSend (1),
+                    ripVersion1 (2),
+                    rip1Compatible (3),
+                    ripVersion2 (4),
+                    ripV1Demand (5),
+                    ripV2Demand (6)
+                 }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "What the router sends on this interface.
+           ripVersion1 implies sending RIP updates compliant
+           with  RFC  1058.   rip1Compatible implies
+           broadcasting RIP-2 updates using RFC 1058 route
+           subsumption rules.  ripVersion2 implies
+           multicasting RIP-2 updates.  ripV1Demand indicates
+           the use of Demand RIP on a WAN interface under RIP
+           Version 1 rules.  ripV2Demand indicates the use of
+           Demand RIP on a WAN interface under Version 2 rules."
+       DEFVAL { rip1Compatible }
+       ::= { rip2IfConfEntry 5 }
+
+    rip2IfConfReceive OBJECT-TYPE
+        SYNTAX   INTEGER {
+                    rip1 (1),
+                    rip2 (2),
+                    rip1OrRip2 (3),
+                    doNotRecieve (4)
+                 }
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This indicates which version of RIP updates
+           are to be accepted.  Note that rip2 and
+           rip1OrRip2 implies reception of multicast
+           packets."
+       DEFVAL { rip1OrRip2 }
+       ::= { rip2IfConfEntry 6 }
+
+    rip2IfConfDefaultMetric OBJECT-TYPE
+        SYNTAX   INTEGER ( 0..15 )
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "This variable indicates the metric that is to
+           be used for the default route entry in RIP updates
+           originated on this interface.  A value of zero
+           indicates that no default route should be
+           originated; in this case, a default route via
+           another router may be propagated."
+       ::= { rip2IfConfEntry 7 }
+
+    rip2IfConfStatus OBJECT-TYPE
+        SYNTAX   RowStatus
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "Writing invalid has  the  effect  of  deleting
+           this interface."
+       ::= { rip2IfConfEntry 8 }
+
+    rip2IfConfSrcAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-create
+        STATUS   current
+        DESCRIPTION
+           "The IP Address this system will use as a source
+            address on this interface.  If it is a numbered
+            interface, this MUST be the same value as
+            rip2IfConfAddress.  On unnumbered interfaces,
+            it must be the value of rip2IfConfAddress for
+            some interface on the system."
+       ::= { rip2IfConfEntry 9 }
+
+--4.3 Peer Table
+
+--  Peer Table
+
+--      The RIP Peer Group
+--      Implementation of this Group is Optional
+
+--      This group provides information about active peer
+--      relationships intended to assist in debugging.  An
+--      active peer is a router from which a valid RIP
+--      updated has been heard in the last 180 seconds.
+
+    rip2PeerTable OBJECT-TYPE
+        SYNTAX   SEQUENCE OF Rip2PeerEntry
+        MAX-ACCESS   not-accessible
+        STATUS   current
+        DESCRIPTION
+           "A list of RIP Peers."
+       ::= { rip2 4 }
+
+   rip2PeerEntry OBJECT-TYPE
+       SYNTAX   Rip2PeerEntry
+       MAX-ACCESS   not-accessible
+       STATUS   current
+       DESCRIPTION
+          "Information regarding a single routing peer."
+      INDEX { rip2PeerAddress, rip2PeerDomain }
+      ::= { rip2PeerTable 1 }
+
+    Rip2PeerEntry ::=
+        SEQUENCE {
+            rip2PeerAddress
+                IpAddress,
+            rip2PeerDomain
+                RouteTag,
+            rip2PeerLastUpdate
+                TimeTicks,
+            rip2PeerVersion
+                INTEGER,
+            rip2PeerRcvBadPackets
+                Counter32,
+            rip2PeerRcvBadRoutes
+                Counter32
+            }
+
+    rip2PeerAddress OBJECT-TYPE
+        SYNTAX   IpAddress
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The IP Address that the peer is using as its source
+            address.  Note that on an unnumbered link, this may
+            not be a member of any subnet on the system."
+       ::= { rip2PeerEntry 1 }
+
+    rip2PeerDomain OBJECT-TYPE
+        SYNTAX   RouteTag
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The value in the Routing Domain field  in  RIP
+           packets received from the peer.  As domain suuport
+           is deprecated, this must be zero."
+       ::= { rip2PeerEntry 2 }
+
+    rip2PeerLastUpdate OBJECT-TYPE
+        SYNTAX   TimeTicks
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The value of sysUpTime when the most recent
+           RIP update was received from this system."
+       ::= { rip2PeerEntry 3 }
+
+    rip2PeerVersion OBJECT-TYPE
+        SYNTAX   INTEGER ( 0..255 )
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The RIP version number in the header of the
+           last RIP packet received."
+       ::= { rip2PeerEntry 4 }
+
+    rip2PeerRcvBadPackets OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of RIP response packets from this
+           peer discarded as invalid."
+       ::= { rip2PeerEntry 5 }
+
+
+    rip2PeerRcvBadRoutes OBJECT-TYPE
+        SYNTAX   Counter32
+        MAX-ACCESS   read-only
+        STATUS   current
+        DESCRIPTION
+           "The number of routes from this peer that were
+           ignored because the entry format was invalid."
+       ::= { rip2PeerEntry 6 }
+
+-- conformance information
+
+rip2Conformance OBJECT IDENTIFIER ::= { rip2 5 }
+
+rip2Groups      OBJECT IDENTIFIER ::= { rip2Conformance 1 }
+rip2Compliances OBJECT IDENTIFIER ::= { rip2Conformance 2 }
+
+-- compliance statements
+rip2Compliance MODULE-COMPLIANCE
+    STATUS  current
+    DESCRIPTION
+       "The compliance statement "
+    MODULE  -- this module
+    MANDATORY-GROUPS {
+                 rip2GlobalGroup,
+                 rip2IfStatGroup,
+                 rip2IfConfGroup,
+                 rip2PeerGroup
+        }
+    GROUP       rip2GlobalGroup
+    DESCRIPTION
+       "This group defines global controls for RIP-II systems."
+    GROUP       rip2IfStatGroup
+    DESCRIPTION
+       "This group defines interface statistics for RIP-II systems."
+    GROUP       rip2IfConfGroup
+    DESCRIPTION
+       "This group defines interface configuration for RIP-II systems."
+    GROUP       rip2PeerGroup
+    DESCRIPTION
+       "This group defines peer information for RIP-II systems."
+    ::= { rip2Compliances 1 }
+
+-- units of conformance
+
+rip2GlobalGroup    OBJECT-GROUP
+    OBJECTS {
+                rip2GlobalRouteChanges,
+                rip2GlobalQueries
+    }
+    STATUS  current
+    DESCRIPTION
+       "This group defines global controls for RIP-II systems."
+    ::= { rip2Groups 1 }
+rip2IfStatGroup    OBJECT-GROUP
+    OBJECTS {
+            rip2IfStatAddress,
+            rip2IfStatRcvBadPackets,
+            rip2IfStatRcvBadRoutes,
+            rip2IfStatSentUpdates,
+            rip2IfStatStatus
+    }
+    STATUS  current
+    DESCRIPTION
+       "This group defines interface statistics for RIP-II systems."
+    ::= { rip2Groups 2 }
+rip2IfConfGroup    OBJECT-GROUP
+    OBJECTS {
+            rip2IfConfAddress,
+            rip2IfConfAuthType,
+            rip2IfConfAuthKey,
+            rip2IfConfSend,
+            rip2IfConfReceive,
+            rip2IfConfDefaultMetric,
+            rip2IfConfStatus,
+            rip2IfConfSrcAddress
+    }
+    STATUS  current
+    DESCRIPTION
+       "This group defines interface configuration for RIP-II systems."
+    ::= { rip2Groups 3 }
+rip2PeerGroup    OBJECT-GROUP
+    OBJECTS {
+            rip2PeerAddress,
+            rip2PeerDomain,
+            rip2PeerLastUpdate,
+            rip2PeerVersion,
+            rip2PeerRcvBadPackets,
+            rip2PeerRcvBadRoutes
+    }
+    STATUS  current
+    DESCRIPTION
+       "This group defines peer information for RIP-II systems."
+    ::= { rip2Groups 4 }
+END
diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c
new file mode 100644 (file)
index 0000000..a267874
--- /dev/null
@@ -0,0 +1,284 @@
+/* RIP debug routines
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "ripd/rip_debug.h"
+
+/* For debug statement. */
+unsigned long rip_debug_event = 0;
+unsigned long rip_debug_packet = 0;
+unsigned long rip_debug_zebra = 0;
+
+DEFUN (show_debugging_rip,
+       show_debugging_rip_cmd,
+       "show debugging rip",
+       SHOW_STR
+       DEBUG_STR
+       RIP_STR)
+{
+  vty_out (vty, "RIP debugging status:%s", VTY_NEWLINE);
+
+  if (IS_RIP_DEBUG_EVENT)
+    vty_out (vty, "  RIP event debugging is on%s", VTY_NEWLINE);
+
+  if (IS_RIP_DEBUG_PACKET)
+    {
+      if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV)
+       {
+         vty_out (vty, "  RIP packet debugging is on%s",
+                  VTY_NEWLINE);
+       }
+      else
+       {
+         if (IS_RIP_DEBUG_SEND)
+           vty_out (vty, "  RIP packet send debugging is on%s",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "  RIP packet receive debugging is on%s",
+                    VTY_NEWLINE);
+       }
+    }
+
+  if (IS_RIP_DEBUG_ZEBRA)
+    vty_out (vty, "  RIP zebra debugging is on%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_events,
+       debug_rip_events_cmd,
+       "debug rip events",
+       DEBUG_STR
+       RIP_STR
+       "RIP events\n")
+{
+  rip_debug_event = RIP_DEBUG_EVENT;
+  return CMD_WARNING;
+}
+
+DEFUN (debug_rip_packet,
+       debug_rip_packet_cmd,
+       "debug rip packet",
+       DEBUG_STR
+       RIP_STR
+       "RIP packet\n")
+{
+  rip_debug_packet = RIP_DEBUG_PACKET;
+  rip_debug_packet |= RIP_DEBUG_SEND;
+  rip_debug_packet |= RIP_DEBUG_RECV;
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_packet_direct,
+       debug_rip_packet_direct_cmd,
+       "debug rip packet (recv|send)",
+       DEBUG_STR
+       RIP_STR
+       "RIP packet\n"
+       "RIP receive packet\n"
+       "RIP send packet\n")
+{
+  rip_debug_packet |= RIP_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    rip_debug_packet |= RIP_DEBUG_SEND;
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    rip_debug_packet |= RIP_DEBUG_RECV;
+  return CMD_SUCCESS;
+}
+
+/* N.B. the "detail" modifier is a no-op.  we leave this command
+   for legacy compatibility. */
+DEFUN_DEPRECATED (debug_rip_packet_detail,
+       debug_rip_packet_detail_cmd,
+       "debug rip packet (recv|send) detail",
+       DEBUG_STR
+       RIP_STR
+       "RIP packet\n"
+       "RIP receive packet\n"
+       "RIP send packet\n"
+       "Detailed information display\n")
+{
+  rip_debug_packet |= RIP_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    rip_debug_packet |= RIP_DEBUG_SEND;
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    rip_debug_packet |= RIP_DEBUG_RECV;
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rip_zebra,
+       debug_rip_zebra_cmd,
+       "debug rip zebra",
+       DEBUG_STR
+       RIP_STR
+       "RIP and ZEBRA communication\n")
+{
+  rip_debug_zebra = RIP_DEBUG_ZEBRA;
+  return CMD_WARNING;
+}
+
+DEFUN (no_debug_rip_events,
+       no_debug_rip_events_cmd,
+       "no debug rip events",
+       NO_STR
+       DEBUG_STR
+       RIP_STR
+       "RIP events\n")
+{
+  rip_debug_event = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_packet,
+       no_debug_rip_packet_cmd,
+       "no debug rip packet",
+       NO_STR
+       DEBUG_STR
+       RIP_STR
+       "RIP packet\n")
+{
+  rip_debug_packet = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_packet_direct,
+       no_debug_rip_packet_direct_cmd,
+       "no debug rip packet (recv|send)",
+       NO_STR
+       DEBUG_STR
+       RIP_STR
+       "RIP packet\n"
+       "RIP option set for receive packet\n"
+       "RIP option set for send packet\n")
+{
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    {
+      if (IS_RIP_DEBUG_RECV)
+       rip_debug_packet &= ~RIP_DEBUG_SEND;
+      else
+       rip_debug_packet = 0;
+    }
+  else if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    {
+      if (IS_RIP_DEBUG_SEND)
+       rip_debug_packet &= ~RIP_DEBUG_RECV;
+      else
+       rip_debug_packet = 0;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rip_zebra,
+       no_debug_rip_zebra_cmd,
+       "no debug rip zebra",
+       NO_STR
+       DEBUG_STR
+       RIP_STR
+       "RIP and ZEBRA communication\n")
+{
+  rip_debug_zebra = 0;
+  return CMD_WARNING;
+}
+
+/* Debug node. */
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",                          /* Debug node has no interface. */
+  1
+};
+
+static int
+config_write_debug (struct vty *vty)
+{
+  int write = 0;
+
+  if (IS_RIP_DEBUG_EVENT)
+    {
+      vty_out (vty, "debug rip events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_RIP_DEBUG_PACKET)
+    {
+      if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV)
+       {
+         vty_out (vty, "debug rip packet%s",
+                  VTY_NEWLINE);
+         write++;
+       }
+      else
+       {
+         if (IS_RIP_DEBUG_SEND)
+           vty_out (vty, "debug rip packet send%s",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "debug rip packet recv%s",
+                    VTY_NEWLINE);
+         write++;
+       }
+    }
+  if (IS_RIP_DEBUG_ZEBRA)
+    {
+      vty_out (vty, "debug rip zebra%s", VTY_NEWLINE);
+      write++;
+    }
+  return write;
+}
+
+void
+rip_debug_reset (void)
+{
+  rip_debug_event = 0;
+  rip_debug_packet = 0;
+  rip_debug_zebra = 0;
+}
+
+void
+rip_debug_init (void)
+{
+  rip_debug_event = 0;
+  rip_debug_packet = 0;
+  rip_debug_zebra = 0;
+
+  install_node (&debug_node, config_write_debug);
+
+  install_element (ENABLE_NODE, &show_debugging_rip_cmd);
+  install_element (ENABLE_NODE, &debug_rip_events_cmd);
+  install_element (ENABLE_NODE, &debug_rip_packet_cmd);
+  install_element (ENABLE_NODE, &debug_rip_packet_direct_cmd);
+  install_element (ENABLE_NODE, &debug_rip_packet_detail_cmd);
+  install_element (ENABLE_NODE, &debug_rip_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_rip_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_rip_packet_cmd);
+  install_element (ENABLE_NODE, &no_debug_rip_packet_direct_cmd);
+  install_element (ENABLE_NODE, &no_debug_rip_zebra_cmd);
+
+  install_element (CONFIG_NODE, &debug_rip_events_cmd);
+  install_element (CONFIG_NODE, &debug_rip_packet_cmd);
+  install_element (CONFIG_NODE, &debug_rip_packet_direct_cmd);
+  install_element (CONFIG_NODE, &debug_rip_packet_detail_cmd);
+  install_element (CONFIG_NODE, &debug_rip_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_rip_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_rip_packet_cmd);
+  install_element (CONFIG_NODE, &no_debug_rip_packet_direct_cmd);
+  install_element (CONFIG_NODE, &no_debug_rip_zebra_cmd);
+}
diff --git a/ripd/rip_debug.h b/ripd/rip_debug.h
new file mode 100644 (file)
index 0000000..990ec90
--- /dev/null
@@ -0,0 +1,53 @@
+/* RIP debug routines
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIP_DEBUG_H
+#define _ZEBRA_RIP_DEBUG_H
+
+/* RIP debug event flags. */
+#define RIP_DEBUG_EVENT   0x01
+
+/* RIP debug packet flags. */
+#define RIP_DEBUG_PACKET  0x01
+#define RIP_DEBUG_SEND    0x20
+#define RIP_DEBUG_RECV    0x40
+#define RIP_DEBUG_DETAIL  0x80
+
+/* RIP debug zebra flags. */
+#define RIP_DEBUG_ZEBRA   0x01
+
+/* Debug related macro. */
+#define IS_RIP_DEBUG_EVENT  (rip_debug_event & RIP_DEBUG_EVENT)
+
+#define IS_RIP_DEBUG_PACKET (rip_debug_packet & RIP_DEBUG_PACKET)
+#define IS_RIP_DEBUG_SEND   (rip_debug_packet & RIP_DEBUG_SEND)
+#define IS_RIP_DEBUG_RECV   (rip_debug_packet & RIP_DEBUG_RECV)
+
+#define IS_RIP_DEBUG_ZEBRA  (rip_debug_zebra & RIP_DEBUG_ZEBRA)
+
+extern unsigned long rip_debug_event;
+extern unsigned long rip_debug_packet;
+extern unsigned long rip_debug_zebra;
+
+extern void rip_debug_init (void);
+extern void rip_debug_reset (void);
+
+#endif /* _ZEBRA_RIP_DEBUG_H */
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
new file mode 100644 (file)
index 0000000..7521fc7
--- /dev/null
@@ -0,0 +1,2111 @@
+/* Interface related function for RIP.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "memory.h"
+#include "network.h"
+#include "table.h"
+#include "log.h"
+#include "stream.h"
+#include "thread.h"
+#include "zclient.h"
+#include "filter.h"
+#include "sockopt.h"
+#include "privs.h"
+
+#include "zebra/connected.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/* static prototypes */
+static void rip_enable_apply (struct interface *);
+static void rip_passive_interface_apply (struct interface *);
+static int rip_if_down(struct interface *ifp);
+static int rip_enable_if_lookup (const char *ifname);
+static int rip_enable_network_lookup2 (struct connected *connected);
+static void rip_enable_apply_all (void);
+
+const struct message ri_version_msg[] =
+{
+  {RI_RIP_VERSION_1,       "1"},
+  {RI_RIP_VERSION_2,       "2"},
+  {RI_RIP_VERSION_1_AND_2, "1 2"},
+};
+
+extern struct zebra_privs_t ripd_privs;
+
+/* RIP enabled network vector. */
+vector rip_enable_interface;
+
+/* RIP enabled interface table. */
+struct route_table *rip_enable_network;
+
+/* Vector to store passive-interface name. */
+static int passive_default;    /* are we in passive-interface default mode? */
+vector Vrip_passive_nondefault;
+
+/* Join to the RIP version 2 multicast group. */
+static int
+ipv4_multicast_join (int sock, 
+                    struct in_addr group, 
+                    struct in_addr ifa,
+                    ifindex_t ifindex)
+{
+  int ret;
+
+  ret = setsockopt_ipv4_multicast (sock,
+                                  IP_ADD_MEMBERSHIP, 
+                                  group.s_addr, 
+                                  ifindex); 
+
+  if (ret < 0) 
+    zlog (NULL, LOG_INFO, "can't setsockopt IP_ADD_MEMBERSHIP %s",
+         safe_strerror (errno));
+
+  return ret;
+}
+
+/* Leave from the RIP version 2 multicast group. */
+static int
+ipv4_multicast_leave (int sock, 
+                     struct in_addr group, 
+                     struct in_addr ifa,
+                     ifindex_t ifindex)
+{
+  int ret;
+
+  ret = setsockopt_ipv4_multicast (sock,
+                                  IP_DROP_MEMBERSHIP, 
+                                  group.s_addr, 
+                                  ifindex);
+
+  if (ret < 0) 
+    zlog (NULL, LOG_INFO, "can't setsockopt IP_DROP_MEMBERSHIP");
+
+  return ret;
+}
+
+static void rip_interface_reset (struct rip_interface *);
+
+/* Allocate new RIP's interface configuration. */
+static struct rip_interface *
+rip_interface_new (void)
+{
+  struct rip_interface *ri;
+
+  ri = XCALLOC (MTYPE_RIP_INTERFACE, sizeof (struct rip_interface));
+  
+  rip_interface_reset (ri);
+  
+  return ri;
+}
+
+void
+rip_interface_multicast_set (int sock, struct connected *connected)
+{
+  assert (connected != NULL);
+  
+  if (setsockopt_ipv4_multicast_if (sock, connected->ifp->ifindex) < 0)
+    {
+      zlog_warn ("Can't setsockopt IP_MULTICAST_IF on fd %d to "
+                "ifindex %d for interface %s",
+                sock, connected->ifp->ifindex,
+                connected->ifp->name);
+    }
+  
+  return;
+}
+
+/* Send RIP request packet to specified interface. */
+static void
+rip_request_interface_send (struct interface *ifp, u_char version)
+{
+  struct sockaddr_in to;
+
+  /* RIPv2 support multicast. */
+  if (version == RIPv2 && if_is_multicast (ifp))
+    {
+      
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("multicast request on %s", ifp->name);
+
+      rip_request_send (NULL, ifp, version, NULL);
+      return;
+    }
+
+  /* RIPv1 and non multicast interface. */
+  if (if_is_pointopoint (ifp) || if_is_broadcast (ifp))
+    {
+      struct listnode *cnode, *cnnode;
+      struct connected *connected;
+
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("broadcast request to %s", ifp->name);
+
+      for (ALL_LIST_ELEMENTS (ifp->connected, cnode, cnnode, connected))
+       {
+         if (connected->address->family == AF_INET)
+           {
+             memset (&to, 0, sizeof (struct sockaddr_in));
+             to.sin_port = htons (RIP_PORT_DEFAULT);
+              if (connected->destination)
+                /* use specified broadcast or peer destination addr */
+                to.sin_addr = connected->destination->u.prefix4;
+              else if (connected->address->prefixlen < IPV4_MAX_PREFIXLEN)
+               /* calculate the appropriate broadcast address */
+                to.sin_addr.s_addr =
+                 ipv4_broadcast_addr(connected->address->u.prefix4.s_addr,
+                                     connected->address->prefixlen);
+             else
+               /* do not know where to send the packet */
+               continue;
+
+             if (IS_RIP_DEBUG_EVENT)
+               zlog_debug ("SEND request to %s", inet_ntoa (to.sin_addr));
+             
+             rip_request_send (&to, ifp, version, connected);
+           }
+       }
+    }
+}
+
+/* This will be executed when interface goes up. */
+static void
+rip_request_interface (struct interface *ifp)
+{
+  struct rip_interface *ri;
+
+  /* In default ripd doesn't send RIP_REQUEST to the loopback interface. */
+  if (if_is_loopback (ifp))
+    return;
+
+  /* If interface is down, don't send RIP packet. */
+  if (! if_is_operative (ifp))
+    return;
+
+  /* Fetch RIP interface information. */
+  ri = ifp->info;
+
+
+  /* If there is no version configuration in the interface,
+     use rip's version setting. */
+  {
+    int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
+                rip->version_send : ri->ri_send);
+    if (vsend & RIPv1)
+      rip_request_interface_send (ifp, RIPv1);
+    if (vsend & RIPv2)
+      rip_request_interface_send (ifp, RIPv2);
+  }
+}
+
+#if 0
+/* Send RIP request to the neighbor. */
+static void
+rip_request_neighbor (struct in_addr addr)
+{
+  struct sockaddr_in to;
+
+  memset (&to, 0, sizeof (struct sockaddr_in));
+  to.sin_port = htons (RIP_PORT_DEFAULT);
+  to.sin_addr = addr;
+
+  rip_request_send (&to, NULL, rip->version_send, NULL);
+}
+
+/* Request routes at all interfaces. */
+static void
+rip_request_neighbor_all (void)
+{
+  struct route_node *rp;
+
+  if (! rip)
+    return;
+
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("request to the all neighbor");
+
+  /* Send request to all neighbor. */
+  for (rp = route_top (rip->neighbor); rp; rp = route_next (rp))
+    if (rp->info)
+      rip_request_neighbor (rp->p.u.prefix4);
+}
+#endif
+
+/* Multicast packet receive socket. */
+static int
+rip_multicast_join (struct interface *ifp, int sock)
+{
+  struct listnode *cnode;
+  struct connected *ifc;
+
+  if (if_is_operative (ifp) && if_is_multicast (ifp))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("multicast join at %s", ifp->name);
+
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc))
+       {
+         struct prefix_ipv4 *p;
+         struct in_addr group;
+             
+         p = (struct prefix_ipv4 *) ifc->address;
+      
+         if (p->family != AF_INET)
+           continue;
+      
+         group.s_addr = htonl (INADDR_RIP_GROUP);
+         if (ipv4_multicast_join (sock, group, p->prefix, ifp->ifindex) < 0)
+           return -1;
+         else
+           return 0;
+       }
+    }
+  return 0;
+}
+
+/* Leave from multicast group. */
+static void
+rip_multicast_leave (struct interface *ifp, int sock)
+{
+  struct listnode *cnode;
+  struct connected *connected;
+
+  if (if_is_up (ifp) && if_is_multicast (ifp))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("multicast leave from %s", ifp->name);
+
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         struct prefix_ipv4 *p;
+         struct in_addr group;
+          
+         p = (struct prefix_ipv4 *) connected->address;
+         
+         if (p->family != AF_INET)
+           continue;
+      
+         group.s_addr = htonl (INADDR_RIP_GROUP);
+          if (ipv4_multicast_leave (sock, group, p->prefix, ifp->ifindex) == 0)
+           return;
+        }
+    }
+}
+
+/* Is there and address on interface that I could use ? */
+static int
+rip_if_ipv4_address_check (struct interface *ifp)
+{
+  struct listnode *nn;
+  struct connected *connected;
+  int count = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, connected))
+    {
+      struct prefix *p;
+
+      p = connected->address;
+
+      if (p->family == AF_INET)
+        count++;
+    }
+                                               
+  return count;
+}
+                                               
+                                               
+                                               
+
+/* Does this address belongs to me ? */
+int
+if_check_address (struct in_addr addr)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      struct listnode *cnode;
+      struct connected *connected;
+
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected))
+       {
+         struct prefix_ipv4 *p;
+
+         p = (struct prefix_ipv4 *) connected->address;
+
+         if (p->family != AF_INET)
+           continue;
+
+         if (IPV4_ADDR_CMP (&p->prefix, &addr) == 0)
+           return 1;
+       }
+    }
+  return 0;
+}
+
+/* Inteface link down message processing. */
+int
+rip_interface_down (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct stream *s;
+
+  s = zclient->ibuf;  
+
+  /* zebra_interface_state_read() updates interface structure in
+     iflist. */
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  rip_if_down(ifp);
+  if (IS_RIP_DEBUG_ZEBRA)
+    zlog_debug ("interface %s index %d flags %llx metric %d mtu %d is down",
+              ifp->name, ifp->ifindex, (unsigned long long)ifp->flags,
+              ifp->metric, ifp->mtu);
+
+  return 0;
+}
+
+/* Inteface link up message processing */
+int
+rip_interface_up (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  /* zebra_interface_state_read () updates interface structure in
+     iflist. */
+  ifp = zebra_interface_state_read (zclient->ibuf, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (IS_RIP_DEBUG_ZEBRA)
+    zlog_debug ("interface %s index %d flags %#llx metric %d mtu %d is up",
+              ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+              ifp->metric, ifp->mtu);
+
+  /* Check if this interface is RIP enabled or not.*/
+  rip_enable_apply (ifp);
+  /* Check for a passive interface */
+  rip_passive_interface_apply (ifp);
+
+  /* Apply distribute list to the all interface. */
+  rip_distribute_update_interface (ifp);
+
+  return 0;
+}
+
+/* Inteface addition message from zebra. */
+int
+rip_interface_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+
+  if (IS_RIP_DEBUG_ZEBRA)
+    zlog_debug ("interface add %s index %d flags %#llx metric %d mtu %d",
+               ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+               ifp->metric, ifp->mtu);
+
+  /* Check if this interface is RIP enabled or not.*/
+  rip_enable_apply (ifp);
+  /* Check for a passive interface */
+  rip_passive_interface_apply (ifp);
+
+  /* Apply distribute list to the all interface. */
+  rip_distribute_update_interface (ifp);
+
+  /* rip_request_neighbor_all (); */
+
+  /* Check interface routemap. */
+  rip_if_rmap_update_interface (ifp);
+
+  return 0;
+}
+
+int
+rip_interface_delete (int command, struct zclient *zclient,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct stream *s;
+
+
+  s = zclient->ibuf;  
+  /* zebra_interface_state_read() updates interface structure in iflist */
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (if_is_up (ifp)) {
+    rip_if_down(ifp);
+  } 
+  
+  zlog_info("interface delete %s index %d flags %#llx metric %d mtu %d",
+           ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+           ifp->metric, ifp->mtu);
+  
+  /* To support pseudo interface do not free interface structure.  */
+  /* if_delete(ifp); */
+  ifp->ifindex = IFINDEX_INTERNAL;
+
+  return 0;
+}
+
+static void
+rip_interface_clean (struct rip_interface *ri)
+{
+  ri->enable_network = 0;
+  ri->enable_interface = 0;
+  ri->running = 0;
+
+  if (ri->t_wakeup)
+    {
+      thread_cancel (ri->t_wakeup);
+      ri->t_wakeup = NULL;
+    }
+}
+
+void
+rip_interfaces_clean (void)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    rip_interface_clean (ifp->info);
+}
+
+static void
+rip_interface_reset (struct rip_interface *ri)
+{
+  /* Default authentication type is simple password for Cisco
+     compatibility. */
+  ri->auth_type = RIP_NO_AUTH;
+  ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
+
+  /* Set default split-horizon behavior.  If the interface is Frame
+     Relay or SMDS is enabled, the default value for split-horizon is
+     off.  But currently Zebra does detect Frame Relay or SMDS
+     interface.  So all interface is set to split horizon.  */
+  ri->split_horizon_default = RIP_SPLIT_HORIZON;
+  ri->split_horizon = ri->split_horizon_default;
+
+  ri->ri_send = RI_RIP_UNSPEC;
+  ri->ri_receive = RI_RIP_UNSPEC;
+  
+  if (ri->auth_str)
+    {
+      free (ri->auth_str);
+      ri->auth_str = NULL;
+    }
+  if (ri->key_chain)
+    {
+      free (ri->key_chain);
+      ri->key_chain = NULL;
+    }
+
+  ri->list[RIP_FILTER_IN] = NULL;
+  ri->list[RIP_FILTER_OUT] = NULL;
+
+  ri->prefix[RIP_FILTER_IN] = NULL;
+  ri->prefix[RIP_FILTER_OUT] = NULL;
+  
+  ri->recv_badpackets = 0;
+  ri->recv_badroutes = 0;
+  ri->sent_updates = 0;
+
+  ri->passive = 0;
+  
+  rip_interface_clean (ri);
+}
+
+void
+rip_interfaces_reset (void)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    rip_interface_reset (ifp->info);
+}
+
+int
+rip_if_down(struct interface *ifp)
+{
+  struct route_node *rp;
+  struct rip_info *rinfo;
+  struct rip_interface *ri = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL, *nextnode = NULL;
+  if (rip)
+    for (rp = route_top (rip->table); rp; rp = route_next (rp))
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo))
+          if (rinfo->ifindex == ifp->ifindex)
+            rip_ecmp_delete (rinfo);
+
+  ri = ifp->info;
+  
+  if (ri->running)
+   {
+     if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("turn off %s", ifp->name);
+
+     /* Leave from multicast group. */
+     rip_multicast_leave (ifp, rip->sock);
+
+     ri->running = 0;
+   }
+
+  return 0;
+}
+
+/* Needed for stop RIP process. */
+void
+rip_if_down_all ()
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    rip_if_down (ifp);
+}
+
+static void
+rip_apply_address_add (struct connected *ifc)
+{
+  struct prefix_ipv4 address;
+  struct prefix *p;
+
+  if (!rip)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix4;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv4(&address);
+
+  /* Check if this interface is RIP enabled or not
+     or  Check if this address's prefix is RIP enabled */
+  if ((rip_enable_if_lookup(ifc->ifp->name) >= 0) ||
+      (rip_enable_network_lookup2(ifc) >= 0))
+    rip_redistribute_add(ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE,
+                         &address, ifc->ifp->ifindex, NULL, 0, 0, 0);
+
+}
+
+int
+rip_interface_address_add (int command, struct zclient *zclient,
+                          zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *ifc;
+  struct prefix *p;
+
+  ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, 
+                                      zclient->ibuf, vrf_id);
+
+  if (ifc == NULL)
+    return 0;
+
+  p = ifc->address;
+
+  if (p->family == AF_INET)
+    {
+      if (IS_RIP_DEBUG_ZEBRA)
+       zlog_debug ("connected address %s/%d is added", 
+                  inet_ntoa (p->u.prefix4), p->prefixlen);
+
+      rip_enable_apply(ifc->ifp);
+      /* Check if this prefix needs to be redistributed */
+      rip_apply_address_add(ifc);
+
+#ifdef HAVE_SNMP
+      rip_ifaddr_add (ifc->ifp, ifc);
+#endif /* HAVE_SNMP */
+    }
+
+  return 0;
+}
+
+static void
+rip_apply_address_del (struct connected *ifc) {
+  struct prefix_ipv4 address;
+  struct prefix *p;
+
+  if (!rip)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix4;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv4(&address);
+
+  rip_redistribute_delete(ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE,
+                          &address, ifc->ifp->ifindex);
+}
+
+int
+rip_interface_address_delete (int command, struct zclient *zclient,
+                             zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *ifc;
+  struct prefix *p;
+
+  ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE,
+                                      zclient->ibuf, vrf_id);
+  
+  if (ifc)
+    {
+      p = ifc->address;
+      if (p->family == AF_INET)
+       {
+         if (IS_RIP_DEBUG_ZEBRA)
+           zlog_debug ("connected address %s/%d is deleted",
+                      inet_ntoa (p->u.prefix4), p->prefixlen);
+
+#ifdef HAVE_SNMP
+         rip_ifaddr_delete (ifc->ifp, ifc);
+#endif /* HAVE_SNMP */
+
+         /* Chech wether this prefix needs to be removed */
+          rip_apply_address_del(ifc);
+
+       }
+
+      connected_free (ifc);
+
+    }
+
+  return 0;
+}
+
+/* Check interface is enabled by network statement. */
+/* Check wether the interface has at least a connected prefix that
+ * is within the ripng_enable_network table. */
+static int
+rip_enable_network_lookup_if (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct connected *connected;
+  struct prefix_ipv4 address;
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
+    {
+      struct prefix *p; 
+      struct route_node *node;
+
+      p = connected->address;
+
+      if (p->family == AF_INET)
+        {
+          address.family = AF_INET;
+          address.prefix = p->u.prefix4;
+          address.prefixlen = IPV4_MAX_BITLEN;
+          
+          node = route_node_match (rip_enable_network,
+                                   (struct prefix *)&address);
+          if (node)
+            {
+              route_unlock_node (node);
+              return 1;
+            }
+        }
+    }
+  return -1;
+}
+
+/* Check wether connected is within the ripng_enable_network table. */
+int
+rip_enable_network_lookup2 (struct connected *connected)
+{
+  struct prefix_ipv4 address;
+  struct prefix *p;
+
+  p = connected->address;
+
+  if (p->family == AF_INET) {
+    struct route_node *node;
+
+    address.family = p->family;
+    address.prefix = p->u.prefix4;
+    address.prefixlen = IPV4_MAX_BITLEN;
+
+    /* LPM on p->family, p->u.prefix4/IPV4_MAX_BITLEN within rip_enable_network */
+    node = route_node_match (rip_enable_network,
+                             (struct prefix *)&address);
+
+    if (node) {
+      route_unlock_node (node);
+      return 1;
+    }
+  }
+
+  return -1;
+}
+/* Add RIP enable network. */
+static int
+rip_enable_network_add (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_get (rip_enable_network, p);
+
+  if (node->info)
+    {
+      route_unlock_node (node);
+      return -1;
+    }
+  else
+    node->info = (char *) "enabled";
+
+  /* XXX: One should find a better solution than a generic one */
+  rip_enable_apply_all();
+
+  return 1;
+}
+
+/* Delete RIP enable network. */
+static int
+rip_enable_network_delete (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_lookup (rip_enable_network, p);
+  if (node)
+    {
+      node->info = NULL;
+
+      /* Unlock info lock. */
+      route_unlock_node (node);
+
+      /* Unlock lookup lock. */
+      route_unlock_node (node);
+
+      /* XXX: One should find a better solution than a generic one */
+      rip_enable_apply_all ();
+
+      return 1;
+    }
+  return -1;
+}
+
+/* Check interface is enabled by ifname statement. */
+static int
+rip_enable_if_lookup (const char *ifname)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (rip_enable_interface); i++)
+    if ((str = vector_slot (rip_enable_interface, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+       return i;
+  return -1;
+}
+
+/* Add interface to rip_enable_if. */
+static int
+rip_enable_if_add (const char *ifname)
+{
+  int ret;
+
+  ret = rip_enable_if_lookup (ifname);
+  if (ret >= 0)
+    return -1;
+
+  vector_set (rip_enable_interface, strdup (ifname));
+
+  rip_enable_apply_all(); /* TODOVJ */
+
+  return 1;
+}
+
+/* Delete interface from rip_enable_if. */
+static int
+rip_enable_if_delete (const char *ifname)
+{
+  int index;
+  char *str;
+
+  index = rip_enable_if_lookup (ifname);
+  if (index < 0)
+    return -1;
+
+  str = vector_slot (rip_enable_interface, index);
+  free (str);
+  vector_unset (rip_enable_interface, index);
+
+  rip_enable_apply_all(); /* TODOVJ */
+
+  return 1;
+}
+
+/* Join to multicast group and send request to the interface. */
+static int
+rip_interface_wakeup (struct thread *t)
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  /* Get interface. */
+  ifp = THREAD_ARG (t);
+
+  ri = ifp->info;
+  ri->t_wakeup = NULL;
+
+  /* Join to multicast group. */
+  if (rip_multicast_join (ifp, rip->sock) < 0)
+    {
+      zlog_err ("multicast join failed, interface %s not running", ifp->name);
+      return 0;
+    }
+
+  /* Set running flag. */
+  ri->running = 1;
+
+  /* Send RIP request to the interface. */
+  rip_request_interface (ifp);
+
+  return 0;
+}
+
+static void
+rip_connect_set (struct interface *ifp, int set)
+{
+  struct listnode *node, *nnode;
+  struct connected *connected;
+  struct prefix_ipv4 address;
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
+    {
+      struct prefix *p; 
+      p = connected->address;
+
+      if (p->family != AF_INET)
+        continue;
+
+      address.family = AF_INET;
+      address.prefix = p->u.prefix4;
+      address.prefixlen = p->prefixlen;
+      apply_mask_ipv4 (&address);
+
+      if (set) {
+        /* Check once more wether this prefix is within a "network IF_OR_PREF" one */
+        if ((rip_enable_if_lookup(connected->ifp->name) >= 0) ||
+            (rip_enable_network_lookup2(connected) >= 0))
+          rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE,
+                                &address, connected->ifp->ifindex, 
+                                NULL, 0, 0, 0);
+      } else
+        {
+          rip_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE,
+                                   &address, connected->ifp->ifindex);
+          if (rip_redistribute_check (ZEBRA_ROUTE_CONNECT))
+            rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_REDISTRIBUTE,
+                                  &address, connected->ifp->ifindex,
+                                  NULL, 0, 0, 0);
+        }
+    }
+}
+
+/* Update interface status. */
+void
+rip_enable_apply (struct interface *ifp)
+{
+  int ret;
+  struct rip_interface *ri = NULL;
+
+  /* Check interface. */
+  if (! if_is_operative (ifp))
+    return;
+
+  ri = ifp->info;
+
+  /* Check network configuration. */
+  ret = rip_enable_network_lookup_if (ifp);
+
+  /* If the interface is matched. */
+  if (ret > 0)
+    ri->enable_network = 1;
+  else
+    ri->enable_network = 0;
+
+  /* Check interface name configuration. */
+  ret = rip_enable_if_lookup (ifp->name);
+  if (ret >= 0)
+    ri->enable_interface = 1;
+  else
+    ri->enable_interface = 0;
+
+  /* any interface MUST have an IPv4 address */
+  if ( ! rip_if_ipv4_address_check (ifp) )
+    {
+      ri->enable_network = 0;
+      ri->enable_interface = 0;
+    }
+
+  /* Update running status of the interface. */
+  if (ri->enable_network || ri->enable_interface)
+    {
+       {
+         if (IS_RIP_DEBUG_EVENT)
+           zlog_debug ("turn on %s", ifp->name);
+
+         /* Add interface wake up thread. */
+         if (! ri->t_wakeup)
+           ri->t_wakeup = thread_add_timer (master, rip_interface_wakeup,
+                                            ifp, 1);
+          rip_connect_set (ifp, 1);
+       }
+    }
+  else
+    {
+      if (ri->running)
+       {
+         /* Might as well clean up the route table as well
+          * rip_if_down sets to 0 ri->running, and displays "turn off %s"
+          **/ 
+         rip_if_down(ifp);
+
+          rip_connect_set (ifp, 0);
+       }
+    }
+}
+
+/* Apply network configuration to all interface. */
+void
+rip_enable_apply_all ()
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  /* Check each interface. */
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    rip_enable_apply (ifp);
+}
+
+int
+rip_neighbor_lookup (struct sockaddr_in *from)
+{
+  struct prefix_ipv4 p;
+  struct route_node *node;
+
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefix = from->sin_addr;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  node = route_node_lookup (rip->neighbor, (struct prefix *) &p);
+  if (node)
+    {
+      route_unlock_node (node);
+      return 1;
+    }
+  return 0;
+}
+
+/* Add new RIP neighbor to the neighbor tree. */
+static int
+rip_neighbor_add (struct prefix_ipv4 *p)
+{
+  struct route_node *node;
+
+  node = route_node_get (rip->neighbor, (struct prefix *) p);
+
+  if (node->info)
+    return -1;
+
+  node->info = rip->neighbor;
+
+  return 0;
+}
+
+/* Delete RIP neighbor from the neighbor tree. */
+static int
+rip_neighbor_delete (struct prefix_ipv4 *p)
+{
+  struct route_node *node;
+
+  /* Lock for look up. */
+  node = route_node_lookup (rip->neighbor, (struct prefix *) p);
+  if (! node)
+    return -1;
+  
+  node->info = NULL;
+
+  /* Unlock lookup lock. */
+  route_unlock_node (node);
+
+  /* Unlock real neighbor information lock. */
+  route_unlock_node (node);
+
+  return 0;
+}
+
+/* Clear all network and neighbor configuration. */
+void
+rip_clean_network ()
+{
+  unsigned int i;
+  char *str;
+  struct route_node *rn;
+
+  /* rip_enable_network. */
+  for (rn = route_top (rip_enable_network); rn; rn = route_next (rn))
+    if (rn->info)
+      {
+       rn->info = NULL;
+       route_unlock_node (rn);
+      }
+
+  /* rip_enable_interface. */
+  for (i = 0; i < vector_active (rip_enable_interface); i++)
+    if ((str = vector_slot (rip_enable_interface, i)) != NULL)
+      {
+       free (str);
+       vector_slot (rip_enable_interface, i) = NULL;
+      }
+}
+
+/* Utility function for looking up passive interface settings. */
+static int
+rip_passive_nondefault_lookup (const char *ifname)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (Vrip_passive_nondefault); i++)
+    if ((str = vector_slot (Vrip_passive_nondefault, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+       return i;
+  return -1;
+}
+
+void
+rip_passive_interface_apply (struct interface *ifp)
+{
+  struct rip_interface *ri;
+
+  ri = ifp->info;
+
+  ri->passive = ((rip_passive_nondefault_lookup (ifp->name) < 0) ?
+                passive_default : !passive_default);
+
+  if (IS_RIP_DEBUG_ZEBRA)
+    zlog_debug ("interface %s: passive = %d",ifp->name,ri->passive);
+}
+
+static void
+rip_passive_interface_apply_all (void)
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    rip_passive_interface_apply (ifp);
+}
+
+/* Passive interface. */
+static int
+rip_passive_nondefault_set (struct vty *vty, const char *ifname)
+{
+  if (rip_passive_nondefault_lookup (ifname) >= 0)
+    return CMD_WARNING;
+
+  vector_set (Vrip_passive_nondefault, strdup (ifname));
+
+  rip_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+static int
+rip_passive_nondefault_unset (struct vty *vty, const char *ifname)
+{
+  int i;
+  char *str;
+
+  i = rip_passive_nondefault_lookup (ifname);
+  if (i < 0)
+    return CMD_WARNING;
+
+  str = vector_slot (Vrip_passive_nondefault, i);
+  free (str);
+  vector_unset (Vrip_passive_nondefault, i);
+
+  rip_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+/* Free all configured RIP passive-interface settings. */
+void
+rip_passive_nondefault_clean (void)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (Vrip_passive_nondefault); i++)
+    if ((str = vector_slot (Vrip_passive_nondefault, i)) != NULL)
+      {
+       free (str);
+       vector_slot (Vrip_passive_nondefault, i) = NULL;
+      }
+  rip_passive_interface_apply_all ();
+}
+
+/* RIP enable network or interface configuration. */
+DEFUN (rip_network,
+       rip_network_cmd,
+       "network (A.B.C.D/M|WORD)",
+       "Enable routing on an IP network\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Interface name\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+
+  if (ret)
+    ret = rip_enable_network_add ((struct prefix *) &p);
+  else
+    ret = rip_enable_if_add (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "There is a same network configuration %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* RIP enable network or interface configuration. */
+DEFUN (no_rip_network,
+       no_rip_network_cmd,
+       "no network (A.B.C.D/M|WORD)",
+       NO_STR
+       "Enable routing on an IP network\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Interface name\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+
+  if (ret)
+    ret = rip_enable_network_delete ((struct prefix *) &p);
+  else
+    ret = rip_enable_if_delete (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "Can't find network configuration %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* RIP neighbor configuration set. */
+DEFUN (rip_neighbor,
+       rip_neighbor_cmd,
+       "neighbor A.B.C.D",
+       "Specify a neighbor router\n"
+       "Neighbor address\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+
+  if (ret <= 0)
+    {
+      vty_out (vty, "Please specify address by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rip_neighbor_add (&p);
+  
+  return CMD_SUCCESS;
+}
+
+/* RIP neighbor configuration unset. */
+DEFUN (no_rip_neighbor,
+       no_rip_neighbor_cmd,
+       "no neighbor A.B.C.D",
+       NO_STR
+       "Specify a neighbor router\n"
+       "Neighbor address\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+
+  if (ret <= 0)
+    {
+      vty_out (vty, "Please specify address by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rip_neighbor_delete (&p);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_rip_receive_version,
+       ip_rip_receive_version_cmd,
+       "ip rip receive version (1|2)",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement reception\n"
+       "Version control\n"
+       "RIP version 1\n"
+       "RIP version 2\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1. */
+  if (atoi (argv[0]) == 1)
+    {
+      ri->ri_receive = RI_RIP_VERSION_1;
+      return CMD_SUCCESS;
+    }
+  if (atoi (argv[0]) == 2)
+    {
+      ri->ri_receive = RI_RIP_VERSION_2;
+      return CMD_SUCCESS;
+    }
+  return CMD_WARNING;
+}
+
+DEFUN (ip_rip_receive_version_1,
+       ip_rip_receive_version_1_cmd,
+       "ip rip receive version 1 2",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement reception\n"
+       "Version control\n"
+       "RIP version 1\n"
+       "RIP version 2\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1 and 2. */
+  ri->ri_receive = RI_RIP_VERSION_1_AND_2;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_rip_receive_version_2,
+       ip_rip_receive_version_2_cmd,
+       "ip rip receive version 2 1",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement reception\n"
+       "Version control\n"
+       "RIP version 2\n"
+       "RIP version 1\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1 and 2. */
+  ri->ri_receive = RI_RIP_VERSION_1_AND_2;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_rip_receive_version,
+       no_ip_rip_receive_version_cmd,
+       "no ip rip receive version",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement reception\n"
+       "Version control\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  ri->ri_receive = RI_RIP_UNSPEC;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_rip_receive_version,
+       no_ip_rip_receive_version_num_cmd,
+       "no ip rip receive version (1|2)",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement reception\n"
+       "Version control\n"
+       "Version 1\n"
+       "Version 2\n")
+
+DEFUN (ip_rip_send_version,
+       ip_rip_send_version_cmd,
+       "ip rip send version (1|2)",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement transmission\n"
+       "Version control\n"
+       "RIP version 1\n"
+       "RIP version 2\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1. */
+  if (atoi (argv[0]) == 1)
+    {
+      ri->ri_send = RI_RIP_VERSION_1;
+      return CMD_SUCCESS;
+    }
+  if (atoi (argv[0]) == 2)
+    {
+      ri->ri_send = RI_RIP_VERSION_2;
+      return CMD_SUCCESS;
+    }
+  return CMD_WARNING;
+}
+
+DEFUN (ip_rip_send_version_1,
+       ip_rip_send_version_1_cmd,
+       "ip rip send version 1 2",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement transmission\n"
+       "Version control\n"
+       "RIP version 1\n"
+       "RIP version 2\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1 and 2. */
+  ri->ri_send = RI_RIP_VERSION_1_AND_2;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_rip_send_version_2,
+       ip_rip_send_version_2_cmd,
+       "ip rip send version 2 1",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement transmission\n"
+       "Version control\n"
+       "RIP version 2\n"
+       "RIP version 1\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  /* Version 1 and 2. */
+  ri->ri_send = RI_RIP_VERSION_1_AND_2;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_rip_send_version,
+       no_ip_rip_send_version_cmd,
+       "no ip rip send version",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement transmission\n"
+       "Version control\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  ri->ri_send = RI_RIP_UNSPEC;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_rip_send_version,
+       no_ip_rip_send_version_num_cmd,
+       "no ip rip send version (1|2)",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Advertisement transmission\n"
+       "Version control\n"
+       "Version 1\n"
+       "Version 2\n")
+
+DEFUN (ip_rip_authentication_mode,
+       ip_rip_authentication_mode_cmd,
+       "ip rip authentication mode (md5|text)",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+  int auth_type;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  if ( (argc < 1) || (argc > 2) )
+    {
+      vty_out (vty, "incorrect argument count%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+    
+  if (strncmp ("md5", argv[0], strlen (argv[0])) == 0)
+    auth_type = RIP_AUTH_MD5;
+  else if (strncmp ("text", argv[0], strlen (argv[0])) == 0)
+    auth_type = RIP_AUTH_SIMPLE_PASSWORD;
+  else
+    {
+      vty_out (vty, "mode should be md5 or text%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc == 1)
+    {
+      ri->auth_type = auth_type;
+      return CMD_SUCCESS;
+    }
+
+  if ( (argc == 2) && (auth_type != RIP_AUTH_MD5) )
+    {
+      vty_out (vty, "auth length argument only valid for md5%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (strncmp ("r", argv[1], 1) == 0)
+    ri->md5_auth_len = RIP_AUTH_MD5_SIZE;
+  else if (strncmp ("o", argv[1], 1) == 0)
+    ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
+  else 
+    return CMD_WARNING;
+    
+  ri->auth_type = auth_type;
+  
+  return CMD_SUCCESS;
+}
+
+ALIAS (ip_rip_authentication_mode,
+       ip_rip_authentication_mode_authlen_cmd,
+       "ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n"
+       "MD5 authentication data length\n"
+       "RFC compatible\n"
+       "Old ripd compatible\n")
+
+DEFUN (no_ip_rip_authentication_mode,
+       no_ip_rip_authentication_mode_cmd,
+       "no ip rip authentication mode",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  ri->auth_type = RIP_NO_AUTH;
+  ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_rip_authentication_mode,
+       no_ip_rip_authentication_mode_type_cmd,
+       "no ip rip authentication mode (md5|text)",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n")
+
+ALIAS (no_ip_rip_authentication_mode,
+       no_ip_rip_authentication_mode_type_authlen_cmd,
+       "no ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication mode\n"
+       "Keyed message digest\n"
+       "Clear text authentication\n"
+       "MD5 authentication data length\n"
+       "RFC compatible\n"
+       "Old ripd compatible\n")
+
+DEFUN (ip_rip_authentication_string,
+       ip_rip_authentication_string_cmd,
+       "ip rip authentication string LINE",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication string\n"
+       "Authentication string\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  if (strlen (argv[0]) > 16)
+    {
+      vty_out (vty, "%% RIPv2 authentication string must be shorter than 16%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ri->key_chain)
+    {
+      vty_out (vty, "%% key-chain configuration exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ri->auth_str)
+    free (ri->auth_str);
+
+  ri->auth_str = strdup (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_rip_authentication_string,
+       no_ip_rip_authentication_string_cmd,
+       "no ip rip authentication string",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication string\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *)vty->index;
+  ri = ifp->info;
+
+  if (ri->auth_str)
+    free (ri->auth_str);
+
+  ri->auth_str = NULL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_rip_authentication_string,
+       no_ip_rip_authentication_string2_cmd,
+       "no ip rip authentication string LINE",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication string\n"
+       "Authentication string\n")
+
+DEFUN (ip_rip_authentication_key_chain,
+       ip_rip_authentication_key_chain_cmd,
+       "ip rip authentication key-chain LINE",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication key-chain\n"
+       "name of key-chain\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *) vty->index;
+  ri = ifp->info;
+
+  if (ri->auth_str)
+    {
+      vty_out (vty, "%% authentication string configuration exists%s",
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (ri->key_chain)
+    free (ri->key_chain);
+
+  ri->key_chain = strdup (argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_rip_authentication_key_chain,
+       no_ip_rip_authentication_key_chain_cmd,
+       "no ip rip authentication key-chain",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication key-chain\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = (struct interface *) vty->index;
+  ri = ifp->info;
+
+  if (ri->key_chain)
+    free (ri->key_chain);
+
+  ri->key_chain = NULL;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_rip_authentication_key_chain,
+       no_ip_rip_authentication_key_chain2_cmd,
+       "no ip rip authentication key-chain LINE",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Authentication control\n"
+       "Authentication key-chain\n"
+       "name of key-chain\n")
+
+/* CHANGED: ip rip split-horizon
+   Cisco and Zebra's command is
+   ip split-horizon
+ */
+DEFUN (ip_rip_split_horizon,
+       ip_rip_split_horizon_cmd,
+       "ip rip split-horizon",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIP_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_rip_split_horizon_poisoned_reverse,
+       ip_rip_split_horizon_poisoned_reverse_cmd,
+       "ip rip split-horizon poisoned-reverse",
+       IP_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIP_SPLIT_HORIZON_POISONED_REVERSE;
+  return CMD_SUCCESS;
+}
+
+/* CHANGED: no ip rip split-horizon
+   Cisco and Zebra's command is
+   no ip split-horizon
+ */
+DEFUN (no_ip_rip_split_horizon,
+       no_ip_rip_split_horizon_cmd,
+       "no ip rip split-horizon",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIP_NO_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_rip_split_horizon_poisoned_reverse,
+       no_ip_rip_split_horizon_poisoned_reverse_cmd,
+       "no ip rip split-horizon poisoned-reverse",
+       NO_STR
+       IP_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  switch( ri->split_horizon )
+  {
+       case RIP_SPLIT_HORIZON_POISONED_REVERSE:
+               ri->split_horizon = RIP_SPLIT_HORIZON;
+       default:
+               break;
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rip_passive_interface,
+       rip_passive_interface_cmd,
+       "passive-interface (IFNAME|default)",
+       "Suppress routing updates on an interface\n"
+       "Interface name\n"
+       "default for all interfaces\n")
+{
+  const char *ifname = argv[0];
+
+  if (!strcmp(ifname,"default")) {
+    passive_default = 1;
+    rip_passive_nondefault_clean();
+    return CMD_SUCCESS;
+  }
+  if (passive_default)
+    return rip_passive_nondefault_unset (vty, ifname);
+  else
+    return rip_passive_nondefault_set (vty, ifname);
+}
+
+DEFUN (no_rip_passive_interface,
+       no_rip_passive_interface_cmd,
+       "no passive-interface (IFNAME|default)",
+       NO_STR
+       "Suppress routing updates on an interface\n"
+       "Interface name\n"
+       "default for all interfaces\n")
+{
+  const char *ifname = argv[0];
+
+  if (!strcmp(ifname,"default")) {
+    passive_default = 0;
+    rip_passive_nondefault_clean();
+    return CMD_SUCCESS;
+  }
+  if (passive_default)
+    return rip_passive_nondefault_set (vty, ifname);
+  else
+    return rip_passive_nondefault_unset (vty, ifname);
+}
+
+/* Write rip configuration of each interface. */
+static int
+rip_interface_config_write (struct vty *vty)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      struct rip_interface *ri;
+
+      ri = ifp->info;
+
+      /* Do not display the interface if there is no
+       * configuration about it.
+       **/
+      if ((!ifp->desc)                                     &&
+          (ri->split_horizon == ri->split_horizon_default) &&
+          (ri->ri_send == RI_RIP_UNSPEC)                   &&
+          (ri->ri_receive == RI_RIP_UNSPEC)                &&
+          (ri->auth_type != RIP_AUTH_MD5)                  &&
+          (ri->md5_auth_len != RIP_AUTH_MD5_SIZE)          &&
+          (!ri->auth_str)                                  &&
+          (!ri->key_chain)                                 )
+        continue;
+
+      vty_out (vty, "interface %s%s", ifp->name,
+              VTY_NEWLINE);
+
+      if (ifp->desc)
+       vty_out (vty, " description %s%s", ifp->desc,
+                VTY_NEWLINE);
+
+      /* Split horizon. */
+      if (ri->split_horizon != ri->split_horizon_default)
+       {
+          switch (ri->split_horizon) {
+          case RIP_SPLIT_HORIZON:
+            vty_out (vty, " ip rip split-horizon%s", VTY_NEWLINE);
+            break;
+          case RIP_SPLIT_HORIZON_POISONED_REVERSE:
+            vty_out (vty, " ip rip split-horizon poisoned-reverse%s",
+                          VTY_NEWLINE);
+            break;
+          case RIP_NO_SPLIT_HORIZON:
+          default:
+            vty_out (vty, " no ip rip split-horizon%s", VTY_NEWLINE);
+            break;
+          }
+       }
+
+      /* RIP version setting. */
+      if (ri->ri_send != RI_RIP_UNSPEC)
+       vty_out (vty, " ip rip send version %s%s",
+                lookup (ri_version_msg, ri->ri_send),
+                VTY_NEWLINE);
+
+      if (ri->ri_receive != RI_RIP_UNSPEC)
+       vty_out (vty, " ip rip receive version %s%s",
+                lookup (ri_version_msg, ri->ri_receive),
+                VTY_NEWLINE);
+
+      /* RIP authentication. */
+      if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
+       vty_out (vty, " ip rip authentication mode text%s", VTY_NEWLINE);
+
+      if (ri->auth_type == RIP_AUTH_MD5)
+        {
+          vty_out (vty, " ip rip authentication mode md5");
+          if (ri->md5_auth_len == RIP_AUTH_MD5_COMPAT_SIZE)
+            vty_out (vty, " auth-length old-ripd");
+          else 
+            vty_out (vty, " auth-length rfc");
+          vty_out (vty, "%s", VTY_NEWLINE);
+        }
+
+      if (ri->auth_str)
+       vty_out (vty, " ip rip authentication string %s%s",
+                ri->auth_str, VTY_NEWLINE);
+
+      if (ri->key_chain)
+       vty_out (vty, " ip rip authentication key-chain %s%s",
+                ri->key_chain, VTY_NEWLINE);
+
+      vty_out (vty, "!%s", VTY_NEWLINE);
+    }
+  return 0;
+}
+
+int
+config_write_rip_network (struct vty *vty, int config_mode)
+{
+  unsigned int i;
+  char *ifname;
+  struct route_node *node;
+
+  /* Network type RIP enable interface statement. */
+  for (node = route_top (rip_enable_network); node; node = route_next (node))
+    if (node->info)
+      vty_out (vty, "%s%s/%d%s", 
+              config_mode ? " network " : "    ",
+              inet_ntoa (node->p.u.prefix4),
+              node->p.prefixlen,
+              VTY_NEWLINE);
+
+  /* Interface name RIP enable statement. */
+  for (i = 0; i < vector_active (rip_enable_interface); i++)
+    if ((ifname = vector_slot (rip_enable_interface, i)) != NULL)
+      vty_out (vty, "%s%s%s",
+              config_mode ? " network " : "    ",
+              ifname,
+              VTY_NEWLINE);
+
+  /* RIP neighbors listing. */
+  for (node = route_top (rip->neighbor); node; node = route_next (node))
+    if (node->info)
+      vty_out (vty, "%s%s%s", 
+              config_mode ? " neighbor " : "    ",
+              inet_ntoa (node->p.u.prefix4),
+              VTY_NEWLINE);
+
+  /* RIP passive interface listing. */
+  if (config_mode) {
+    if (passive_default)
+      vty_out (vty, " passive-interface default%s", VTY_NEWLINE);
+    for (i = 0; i < vector_active (Vrip_passive_nondefault); i++)
+      if ((ifname = vector_slot (Vrip_passive_nondefault, i)) != NULL)
+       vty_out (vty, " %spassive-interface %s%s",
+                (passive_default ? "no " : ""), ifname, VTY_NEWLINE);
+  }
+
+  return 0;
+}
+
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1,
+};
+
+/* Called when interface structure allocated. */
+static int
+rip_interface_new_hook (struct interface *ifp)
+{
+  ifp->info = rip_interface_new ();
+  return 0;
+}
+
+/* Called when interface structure deleted. */
+static int
+rip_interface_delete_hook (struct interface *ifp)
+{
+  XFREE (MTYPE_RIP_INTERFACE, ifp->info);
+  ifp->info = NULL;
+  return 0;
+}
+
+/* Allocate and initialize interface vector. */
+void
+rip_if_init (void)
+{
+  /* Default initial size of interface vector. */
+  if_add_hook (IF_NEW_HOOK, rip_interface_new_hook);
+  if_add_hook (IF_DELETE_HOOK, rip_interface_delete_hook);
+  
+  /* RIP network init. */
+  rip_enable_interface = vector_init (1);
+  rip_enable_network = route_table_init ();
+
+  /* RIP passive interface. */
+  Vrip_passive_nondefault = vector_init (1);
+
+  /* Install interface node. */
+  install_node (&interface_node, rip_interface_config_write);
+
+  /* Install commands. */
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_element (CONFIG_NODE, &no_interface_cmd);
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+  install_element (RIP_NODE, &rip_network_cmd);
+  install_element (RIP_NODE, &no_rip_network_cmd);
+  install_element (RIP_NODE, &rip_neighbor_cmd);
+  install_element (RIP_NODE, &no_rip_neighbor_cmd);
+
+  install_element (RIP_NODE, &rip_passive_interface_cmd);
+  install_element (RIP_NODE, &no_rip_passive_interface_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_send_version_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_send_version_1_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_send_version_2_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_send_version_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_send_version_num_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_receive_version_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_receive_version_1_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_receive_version_2_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_receive_version_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_receive_version_num_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_authentication_mode_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_authentication_mode_authlen_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_authlen_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_key_chain_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_key_chain2_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_authentication_string_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_string_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_authentication_string2_cmd);
+
+  install_element (INTERFACE_NODE, &ip_rip_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &ip_rip_split_horizon_poisoned_reverse_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &no_ip_rip_split_horizon_poisoned_reverse_cmd);
+}
diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h
new file mode 100644 (file)
index 0000000..d9dfbb7
--- /dev/null
@@ -0,0 +1,37 @@
+/* RIP interface routines
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _QUAGGA_RIP_INTERFACE_H
+#define _QUAGGA_RIP_INTERFACE_H
+
+extern int rip_interface_down (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int rip_interface_up (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int rip_interface_add (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int rip_interface_delete (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int rip_interface_address_add (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int rip_interface_address_delete (int , struct zclient *, zebra_size_t,
+    vrf_id_t);
+
+#endif /* _QUAGGA_RIP_INTERFACE_H */
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
new file mode 100644 (file)
index 0000000..4ead9b0
--- /dev/null
@@ -0,0 +1,321 @@
+/* RIPd main routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "filter.h"
+#include "keychain.h"
+#include "log.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "vrf.h"
+
+#include "ripd/ripd.h"
+
+/* ripd options. */
+static struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "retain",      no_argument,       NULL, 'r'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "version",     no_argument,       NULL, 'v'},
+  { 0 }
+};
+
+/* ripd privileges */
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_RAW,
+  ZCAP_BIND
+};
+
+struct zebra_privs_t ripd_privs =
+{
+#if defined(QUAGGA_USER)
+  .user = QUAGGA_USER,
+#endif
+#if defined QUAGGA_GROUP
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = 2,
+  .cap_num_i = 0
+};
+
+/* Configuration file and directory. */
+char config_default[] = SYSCONFDIR RIPD_DEFAULT_CONFIG;
+char *config_file = NULL;
+
+/* ripd program name */
+
+/* Route retain mode flag. */
+int retain_mode = 0;
+
+/* RIP VTY bind address. */
+char *vty_addr = NULL;
+
+/* RIP VTY connection port. */
+int vty_port = RIP_VTY_PORT;
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_RIPD_PID;
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\
+Daemon which manages RIP version 1 and 2.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-r, --retain       When program terminates, retain added route by ripd.\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-v, --version      Print program version\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog_info ("SIGHUP received");
+  rip_clean ();
+  rip_reset ();
+  zlog_info ("ripd restarting!");
+
+  /* Reload config file. */
+  vty_read_config (config_file, config_default);
+
+  /* Create VTY's socket */
+  vty_serv_sock (vty_addr, vty_port, RIP_VTYSH_PATH);
+
+  /* Try to return to normal operation. */
+}
+
+/* SIGINT handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+
+  if (! retain_mode)
+    rip_clean ();
+
+  exit (0);
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+static struct quagga_signal_t ripd_signals[] =
+{
+  { 
+    .signal = SIGHUP,
+    .handler = &sighup,
+  },
+  { 
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};  
+
+/* Main routine of ripd. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  int daemon_mode = 0;
+  int dryrun = 0;
+  char *progname;
+  struct thread thread;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* Get program name. */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  /* First of all we need logging init. */
+  zlog_default = openzlog (progname, ZLOG_RIP,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  /* Command line option parse. */
+  while (1) 
+    {
+      int opt;
+
+      opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:rvC", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+        case 'i':
+          pid_file = optarg;
+          break;
+       case 'z':
+         zclient_serv_path_set (optarg);
+         break;
+       case 'P':
+          /* Deal with atoi() returning 0 on failure, and ripd not
+             listening on rip port... */
+          if (strcmp(optarg, "0") == 0) 
+            {
+              vty_port = 0;
+              break;
+            } 
+          vty_port = atoi (optarg);
+          if (vty_port <= 0 || vty_port > 0xffff)
+            vty_port = RIP_VTY_PORT;
+         break;
+       case 'r':
+         retain_mode = 1;
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'u':
+         ripd_privs.user = optarg;
+         break;
+       case 'g':
+         ripd_privs.group = optarg;
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Prepare master thread. */
+  master = thread_master_create ();
+
+  /* Library initialization. */
+  zprivs_init (&ripd_privs);
+  signal_init (master, array_size(ripd_signals), ripd_signals);
+  cmd_init (1);
+  vty_init (master);
+  memory_init ();
+  keychain_init ();
+  vrf_init ();
+
+  /* RIP related initialization. */
+  rip_init ();
+  rip_if_init ();
+  rip_zclient_init (master);
+  rip_peer_init ();
+
+  /* Get configuration file. */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if(dryrun)
+    return (0);
+  
+  /* Change to the daemon program. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("RIPd daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Pid file create. */
+  pid_output (pid_file);
+
+  /* Create VTY's socket */
+  vty_serv_sock (vty_addr, vty_port, RIP_VTYSH_PATH);
+
+  /* Print banner. */
+  zlog_notice ("RIPd %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  /* Execute each thread. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  return (0);
+}
diff --git a/ripd/rip_offset.c b/ripd/rip_offset.c
new file mode 100644 (file)
index 0000000..0155f90
--- /dev/null
@@ -0,0 +1,413 @@
+/* RIP offset-list
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "filter.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#include "ripd/ripd.h"
+
+#define RIP_OFFSET_LIST_IN  0
+#define RIP_OFFSET_LIST_OUT 1
+#define RIP_OFFSET_LIST_MAX 2
+
+struct rip_offset_list
+{
+  char *ifname;
+
+  struct 
+  {
+    char *alist_name;
+    /* struct access_list *alist; */
+    int metric;
+  } direct[RIP_OFFSET_LIST_MAX];
+};
+
+static struct list *rip_offset_list_master;
+
+static int
+strcmp_safe (const char *s1, const char *s2)
+{
+  if (s1 == NULL && s2 == NULL)
+    return 0;
+  if (s1 == NULL)
+    return -1;
+  if (s2 == NULL)
+    return 1;
+  return strcmp (s1, s2);
+}
+
+static struct rip_offset_list *
+rip_offset_list_new (void)
+{
+  return XCALLOC (MTYPE_RIP_OFFSET_LIST, sizeof (struct rip_offset_list));
+}
+
+static void
+rip_offset_list_free (struct rip_offset_list *offset)
+{
+  XFREE (MTYPE_RIP_OFFSET_LIST, offset);
+}
+
+static struct rip_offset_list *
+rip_offset_list_lookup (const char *ifname)
+{
+  struct rip_offset_list *offset;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (rip_offset_list_master, node, nnode, offset))
+    {
+      if (strcmp_safe (offset->ifname, ifname) == 0)
+       return offset;
+    }
+  return NULL;
+}
+
+static struct rip_offset_list *
+rip_offset_list_get (const char *ifname)
+{
+  struct rip_offset_list *offset;
+  
+  offset = rip_offset_list_lookup (ifname);
+  if (offset)
+    return offset;
+
+  offset = rip_offset_list_new ();
+  if (ifname)
+    offset->ifname = strdup (ifname);
+  listnode_add_sort (rip_offset_list_master, offset);
+
+  return offset;
+}
+
+static int
+rip_offset_list_set (struct vty *vty, const char *alist, const char *direct_str,
+                    const char *metric_str, const char *ifname)
+{
+  int direct;
+  int metric;
+  struct rip_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIP_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIP_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = rip_offset_list_get (ifname);
+
+  if (offset->direct[direct].alist_name)
+    free (offset->direct[direct].alist_name);
+  offset->direct[direct].alist_name = strdup (alist);
+  offset->direct[direct].metric = metric;
+
+  return CMD_SUCCESS;
+}
+
+static int
+rip_offset_list_unset (struct vty *vty, const char *alist,
+                      const char *direct_str, const char *metric_str,
+                      const char *ifname)
+{
+  int direct;
+  int metric;
+  struct rip_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIP_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIP_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = rip_offset_list_lookup (ifname);
+
+  if (offset)
+    {
+      if (offset->direct[direct].alist_name)
+       free (offset->direct[direct].alist_name);
+      offset->direct[direct].alist_name = NULL;
+
+      if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL &&
+         offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL)
+       {
+         listnode_delete (rip_offset_list_master, offset);
+         if (offset->ifname)
+           free (offset->ifname);
+         rip_offset_list_free (offset);
+       }
+    }
+  else
+    {
+      vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+#define OFFSET_LIST_IN_NAME(O)  ((O)->direct[RIP_OFFSET_LIST_IN].alist_name)
+#define OFFSET_LIST_IN_METRIC(O)  ((O)->direct[RIP_OFFSET_LIST_IN].metric)
+
+#define OFFSET_LIST_OUT_NAME(O)  ((O)->direct[RIP_OFFSET_LIST_OUT].alist_name)
+#define OFFSET_LIST_OUT_METRIC(O)  ((O)->direct[RIP_OFFSET_LIST_OUT].metric)
+
+/* If metric is modifed return 1. */
+int
+rip_offset_list_apply_in (struct prefix_ipv4 *p, struct interface *ifp,
+                         u_int32_t *metric)
+{
+  struct rip_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = rip_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_IN_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  /* Look up offset-list without interface name. */
+  offset = rip_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_IN_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  return 0;
+}
+
+/* If metric is modifed return 1. */
+int
+rip_offset_list_apply_out (struct prefix_ipv4 *p, struct interface *ifp,
+                         u_int32_t *metric)
+{
+  struct rip_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = rip_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_OUT_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+
+  /* Look up offset-list without interface name. */
+  offset = rip_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_OUT_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  return 0;
+}
+
+DEFUN (rip_offset_list,
+       rip_offset_list_cmd,
+       "offset-list WORD (in|out) <0-16>",
+       "Modify RIP metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return rip_offset_list_set (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (rip_offset_list_ifname,
+       rip_offset_list_ifname_cmd,
+       "offset-list WORD (in|out) <0-16> IFNAME",
+       "Modify RIP metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return rip_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+DEFUN (no_rip_offset_list,
+       no_rip_offset_list_cmd,
+       "no offset-list WORD (in|out) <0-16>",
+       NO_STR
+       "Modify RIP metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return rip_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (no_rip_offset_list_ifname,
+       no_rip_offset_list_ifname_cmd,
+       "no offset-list WORD (in|out) <0-16> IFNAME",
+       NO_STR
+       "Modify RIP metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return rip_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+static int
+offset_list_cmp (struct rip_offset_list *o1, struct rip_offset_list *o2)
+{
+  return strcmp_safe (o1->ifname, o2->ifname);
+}
+
+static void
+offset_list_del (struct rip_offset_list *offset)
+{
+  if (OFFSET_LIST_IN_NAME (offset))
+    free (OFFSET_LIST_IN_NAME (offset));
+  if (OFFSET_LIST_OUT_NAME (offset))
+    free (OFFSET_LIST_OUT_NAME (offset));
+  if (offset->ifname)
+    free (offset->ifname);
+  rip_offset_list_free (offset);
+}
+
+void
+rip_offset_init ()
+{
+  rip_offset_list_master = list_new ();
+  rip_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  rip_offset_list_master->del = (void (*)(void *)) offset_list_del;
+
+  install_element (RIP_NODE, &rip_offset_list_cmd);
+  install_element (RIP_NODE, &rip_offset_list_ifname_cmd);
+  install_element (RIP_NODE, &no_rip_offset_list_cmd);
+  install_element (RIP_NODE, &no_rip_offset_list_ifname_cmd);
+}
+
+void
+rip_offset_clean ()
+{
+  list_delete (rip_offset_list_master);
+
+  rip_offset_list_master = list_new ();
+  rip_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  rip_offset_list_master->del = (void (*)(void *)) offset_list_del;
+}
+
+int
+config_write_rip_offset_list (struct vty *vty)
+{
+  struct listnode *node, *nnode;
+  struct rip_offset_list *offset;
+
+  for (ALL_LIST_ELEMENTS (rip_offset_list_master, node, nnode, offset))
+    {
+      if (! offset->ifname)
+       {
+         if (offset->direct[RIP_OFFSET_LIST_IN].alist_name)
+           vty_out (vty, " offset-list %s in %d%s",
+                    offset->direct[RIP_OFFSET_LIST_IN].alist_name,
+                    offset->direct[RIP_OFFSET_LIST_IN].metric,
+                    VTY_NEWLINE);
+         if (offset->direct[RIP_OFFSET_LIST_OUT].alist_name)
+           vty_out (vty, " offset-list %s out %d%s",
+                    offset->direct[RIP_OFFSET_LIST_OUT].alist_name,
+                    offset->direct[RIP_OFFSET_LIST_OUT].metric,
+                    VTY_NEWLINE);
+       }
+      else
+       {
+         if (offset->direct[RIP_OFFSET_LIST_IN].alist_name)
+           vty_out (vty, " offset-list %s in %d %s%s",
+                    offset->direct[RIP_OFFSET_LIST_IN].alist_name,
+                    offset->direct[RIP_OFFSET_LIST_IN].metric,
+                    offset->ifname, VTY_NEWLINE);
+         if (offset->direct[RIP_OFFSET_LIST_OUT].alist_name)
+           vty_out (vty, " offset-list %s out %d %s%s",
+                    offset->direct[RIP_OFFSET_LIST_OUT].alist_name,
+                    offset->direct[RIP_OFFSET_LIST_OUT].metric,
+                    offset->ifname, VTY_NEWLINE);
+       }
+    }
+
+  return 0;
+}
diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c
new file mode 100644 (file)
index 0000000..6a3add6
--- /dev/null
@@ -0,0 +1,207 @@
+/* RIP peer support
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "ripd/ripd.h"
+
+/* Linked list of RIP peer. */
+struct list *peer_list;
+
+static struct rip_peer *
+rip_peer_new (void)
+{
+  return XCALLOC (MTYPE_RIP_PEER, sizeof (struct rip_peer));
+}
+
+static void
+rip_peer_free (struct rip_peer *peer)
+{
+  XFREE (MTYPE_RIP_PEER, peer);
+}
+
+struct rip_peer *
+rip_peer_lookup (struct in_addr *addr)
+{
+  struct rip_peer *peer;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      if (IPV4_ADDR_SAME (&peer->addr, addr))
+       return peer;
+    }
+  return NULL;
+}
+
+struct rip_peer *
+rip_peer_lookup_next (struct in_addr *addr)
+{
+  struct rip_peer *peer;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      if (htonl (peer->addr.s_addr) > htonl (addr->s_addr))
+       return peer;
+    }
+  return NULL;
+}
+
+/* RIP peer is timeout. */
+static int
+rip_peer_timeout (struct thread *t)
+{
+  struct rip_peer *peer;
+
+  peer = THREAD_ARG (t);
+  listnode_delete (peer_list, peer);
+  rip_peer_free (peer);
+
+  return 0;
+}
+
+/* Get RIP peer.  At the same time update timeout thread. */
+static struct rip_peer *
+rip_peer_get (struct in_addr *addr)
+{
+  struct rip_peer *peer;
+
+  peer = rip_peer_lookup (addr);
+
+  if (peer)
+    {
+      if (peer->t_timeout)
+       thread_cancel (peer->t_timeout);
+    }
+  else
+    {
+      peer = rip_peer_new ();
+      peer->addr = *addr;
+      listnode_add_sort (peer_list, peer);
+    }
+
+  /* Update timeout thread. */
+  peer->t_timeout = thread_add_timer (master, rip_peer_timeout, peer,
+                                     RIP_PEER_TIMER_DEFAULT);
+
+  /* Last update time set. */
+  time (&peer->uptime);
+  
+  return peer;
+}
+
+void
+rip_peer_update (struct sockaddr_in *from, u_char version)
+{
+  struct rip_peer *peer;
+  peer = rip_peer_get (&from->sin_addr);
+  peer->version = version;
+}
+
+void
+rip_peer_bad_route (struct sockaddr_in *from)
+{
+  struct rip_peer *peer;
+  peer = rip_peer_get (&from->sin_addr);
+  peer->recv_badroutes++;
+}
+
+void
+rip_peer_bad_packet (struct sockaddr_in *from)
+{
+  struct rip_peer *peer;
+  peer = rip_peer_get (&from->sin_addr);
+  peer->recv_badpackets++;
+}
+
+/* Display peer uptime. */
+static char *
+rip_peer_uptime (struct rip_peer *peer, char *buf, size_t len)
+{
+  time_t uptime;
+  struct tm *tm;
+
+  /* If there is no connection has been done before print `never'. */
+  if (peer->uptime == 0)
+    {
+      snprintf (buf, len, "never   ");
+      return buf;
+    }
+
+  /* Get current time. */
+  uptime = time (NULL);
+  uptime -= peer->uptime;
+  tm = gmtime (&uptime);
+
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+
+  if (uptime < ONE_DAY_SECOND)
+    snprintf (buf, len, "%02d:%02d:%02d", 
+             tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (uptime < ONE_WEEK_SECOND)
+    snprintf (buf, len, "%dd%02dh%02dm", 
+             tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    snprintf (buf, len, "%02dw%dd%02dh", 
+             tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  return buf;
+}
+
+void
+rip_peer_display (struct vty *vty)
+{
+  struct rip_peer *peer;
+  struct listnode *node, *nnode;
+#define RIP_UPTIME_LEN 25
+  char timebuf[RIP_UPTIME_LEN];
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      vty_out (vty, "    %-16s %9d %9d %9d   %s%s", inet_ntoa (peer->addr),
+              peer->recv_badpackets, peer->recv_badroutes,
+              ZEBRA_RIP_DISTANCE_DEFAULT,
+              rip_peer_uptime (peer, timebuf, RIP_UPTIME_LEN),
+              VTY_NEWLINE);
+    }
+}
+
+static int
+rip_peer_list_cmp (struct rip_peer *p1, struct rip_peer *p2)
+{
+  return htonl (p1->addr.s_addr) > htonl (p2->addr.s_addr);
+}
+
+void
+rip_peer_init (void)
+{
+  peer_list = list_new ();
+  peer_list->cmp = (int (*)(void *, void *)) rip_peer_list_cmp;
+}
diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c
new file mode 100644 (file)
index 0000000..4e73694
--- /dev/null
@@ -0,0 +1,1113 @@
+/* RIPv2 routemap.
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "command.h"
+#include "filter.h"
+#include "log.h"
+#include "sockunion.h"         /* for inet_aton () */
+#include "plist.h"
+
+#include "ripd/ripd.h"
+
+struct rip_metric_modifier
+{
+  enum 
+  {
+    metric_increment,
+    metric_decrement,
+    metric_absolute
+  } type;
+
+  u_char metric;
+};
+
+/* Add rip route map rule. */
+static int
+rip_route_match_add (struct vty *vty, struct route_map_index *index,
+                    const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete rip route map rule. */
+static int
+rip_route_match_delete (struct vty *vty, struct route_map_index *index,
+                       const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Add rip route map rule. */
+static int
+rip_route_set_add (struct vty *vty, struct route_map_index *index,
+                  const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         /* rip, ripng and other protocols share the set metric command
+            but only values from 0 to 16 are valid for rip and ripng
+            if metric is out of range for rip and ripng, it is not for
+            other protocols. Do not return an error */
+         if (strcmp(command, "metric")) {
+            vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE);
+            return CMD_WARNING;
+         }
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete rip route map rule. */
+static int
+rip_route_set_delete (struct vty *vty, struct route_map_index *index,
+                     const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Hook function for updating route_map assignment. */
+/* ARGSUSED */
+static void
+rip_route_map_update (const char *notused)
+{
+  int i;
+
+  if (rip) 
+    {
+      for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
+       {
+         if (rip->route_map[i].name)
+           rip->route_map[i].map = 
+             route_map_lookup_by_name (rip->route_map[i].name);
+       }
+    }
+}
+
+/* `match metric METRIC' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_metric (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  u_int32_t *metric;
+  u_int32_t  check;
+  struct rip_info *rinfo;
+
+  if (type == RMAP_RIP)
+    {
+      metric = rule;
+      rinfo = object;
+    
+      /* If external metric is available, the route-map should
+         work on this one (for redistribute purpose)  */
+      check = (rinfo->external_metric) ? rinfo->external_metric :
+                                         rinfo->metric;
+      if (check == *metric)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match metric' match statement. `arg' is METRIC value */
+static void *
+route_match_metric_compile (const char *arg)
+{
+  u_int32_t *metric;
+
+  metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+  *metric = atoi (arg);
+
+  if(*metric > 0)
+    return metric;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, metric);
+  return NULL;
+}
+
+/* Free route map's compiled `match metric' value. */
+static void
+route_match_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+struct route_map_rule_cmd route_match_metric_cmd =
+{
+  "metric",
+  route_match_metric,
+  route_match_metric_compile,
+  route_match_metric_free
+};
+
+/* `match interface IFNAME' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_interface (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct rip_info *rinfo;
+  struct interface *ifp;
+  char *ifname;
+
+  if (type == RMAP_RIP)
+    {
+      ifname = rule;
+      ifp = if_lookup_by_name(ifname);
+
+      if (!ifp)
+       return RMAP_NOMATCH;
+
+      rinfo = object;
+
+      if (rinfo->ifindex_out == ifp->ifindex || rinfo->ifindex == ifp->ifindex)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+/* XXX I don`t know if I need to check does interface exist? */
+static void *
+route_match_interface_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `match interface' value. */
+static void
+route_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for interface matching. */
+struct route_map_rule_cmd route_match_interface_cmd =
+{
+  "interface",
+  route_match_interface,
+  route_match_interface_compile,
+  route_match_interface_free
+};
+
+/* `match ip next-hop IP_ACCESS_LIST' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_ip_next_hop (void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  struct rip_info *rinfo;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_RIP)
+    {
+      rinfo = object;
+      p.family = AF_INET;
+      p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : rinfo->from;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, &p) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip next-hop' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_next_hop_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `. */
+static void
+route_match_ip_next_hop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip next-hop matching. */
+static struct route_map_rule_cmd route_match_ip_next_hop_cmd =
+{
+  "ip next-hop",
+  route_match_ip_next_hop,
+  route_match_ip_next_hop_compile,
+  route_match_ip_next_hop_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+  struct rip_info *rinfo;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_RIP)
+    {
+      rinfo = object;
+      p.family = AF_INET;
+      p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : rinfo->from;
+      p.prefixlen = IPV4_MAX_BITLEN;
+
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, &p) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_next_hop_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_next_hop_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd =
+{
+  "ip next-hop prefix-list",
+  route_match_ip_next_hop_prefix_list,
+  route_match_ip_next_hop_prefix_list_compile,
+  route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ip address IP_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+route_match_ip_address (void *rule, struct prefix *prefix, 
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+
+  if (type == RMAP_RIP)
+    {
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (access_list_apply (alist, prefix) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip address' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_address_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_address_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static struct route_map_rule_cmd route_match_ip_address_cmd =
+{
+  "ip address",
+  route_match_ip_address,
+  route_match_ip_address_compile,
+  route_match_ip_address_free
+};
+
+/* `match ip address prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_address_prefix_list (void *rule, struct prefix *prefix, 
+                                   route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type == RMAP_RIP)
+    {
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
+{
+  "ip address prefix-list",
+  route_match_ip_address_prefix_list,
+  route_match_ip_address_prefix_list_compile,
+  route_match_ip_address_prefix_list_free
+};
+
+/* `match tag TAG' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct rip_info *rinfo;
+
+  if (type == RMAP_RIP)
+    {
+      tag = rule;
+      rinfo = object;
+
+      /* The information stored by rinfo is host ordered. */
+      if (rinfo->tag == *tag)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map commands for tag matching. */
+static struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+static route_map_result_t
+route_set_metric (void *rule, struct prefix *prefix, 
+                 route_map_object_t type, void *object)
+{
+  if (type == RMAP_RIP)
+    {
+      struct rip_metric_modifier *mod;
+      struct rip_info *rinfo;
+
+      mod = rule;
+      rinfo = object;
+
+      if (mod->type == metric_increment)
+       rinfo->metric_out += mod->metric;
+      else if (mod->type == metric_decrement)
+       rinfo->metric_out -= mod->metric;
+      else if (mod->type == metric_absolute)
+       rinfo->metric_out = mod->metric;
+
+      if ((signed int)rinfo->metric_out < 1)
+       rinfo->metric_out = 1;
+      if (rinfo->metric_out > RIP_METRIC_INFINITY)
+       rinfo->metric_out = RIP_METRIC_INFINITY;
+
+      rinfo->metric_set = 1;
+    }
+  return RMAP_OKAY;
+}
+
+/* set metric compilation. */
+static void *
+route_set_metric_compile (const char *arg)
+{
+  int len;
+  const char *pnt;
+  int type;
+  long metric;
+  char *endptr = NULL;
+  struct rip_metric_modifier *mod;
+
+  len = strlen (arg);
+  pnt = arg;
+
+  if (len == 0)
+    return NULL;
+
+  /* Examine first character. */
+  if (arg[0] == '+')
+    {
+      type = metric_increment;
+      pnt++;
+    }
+  else if (arg[0] == '-')
+    {
+      type = metric_decrement;
+      pnt++;
+    }
+  else
+    type = metric_absolute;
+
+  /* Check beginning with digit string. */
+  if (*pnt < '0' || *pnt > '9')
+    return NULL;
+
+  /* Convert string to integer. */
+  metric = strtol (pnt, &endptr, 10);
+
+  if (metric == LONG_MAX || *endptr != '\0')
+    return NULL;
+  if (metric < 0 || metric > RIP_METRIC_INFINITY)
+    return NULL;
+
+  mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, 
+                sizeof (struct rip_metric_modifier));
+  mod->type = type;
+  mod->metric = metric;
+
+  return mod;
+}
+
+/* Free route map's compiled `set metric' value. */
+static void
+route_set_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+static struct route_map_rule_cmd route_set_metric_cmd = 
+{
+  "metric",
+  route_set_metric,
+  route_set_metric_compile,
+  route_set_metric_free,
+};
+
+/* `set ip next-hop IP_ADDRESS' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_ip_nexthop (void *rule, struct prefix *prefix, 
+                     route_map_object_t type, void *object)
+{
+  struct in_addr *address;
+  struct rip_info *rinfo;
+
+  if(type == RMAP_RIP)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->nexthop_out = *address;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function.  Given string is converted
+   to struct in_addr structure. */
+static void *
+route_set_ip_nexthop_compile (const char *arg)
+{
+  int ret;
+  struct in_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+
+  ret = inet_aton (arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void
+route_set_ip_nexthop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+static struct route_map_rule_cmd route_set_ip_nexthop_cmd =
+{
+  "ip next-hop",
+  route_set_ip_nexthop,
+  route_set_ip_nexthop_compile,
+  route_set_ip_nexthop_free
+};
+
+/* `set tag TAG' */
+
+/* Set tag to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix, 
+                     route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct rip_info *rinfo;
+
+  if(type == RMAP_RIP)
+    {
+      /* Fetch routemap's rule information. */
+      tag = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->tag_out = *tag;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free
+};
+
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+
+DEFUN (match_metric, 
+       match_metric_cmd,
+       "match metric <0-4294967295>",
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+{
+  return rip_route_match_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_match_metric,
+       no_match_metric_cmd,
+       "no match metric",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "metric", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_match_metric,
+       no_match_metric_val_cmd,
+       "no match metric <0-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+
+DEFUN (match_interface,
+       match_interface_cmd,
+       "match interface WORD",
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+{
+  return rip_route_match_add (vty, vty->index, "interface", argv[0]);
+}
+
+DEFUN (no_match_interface,
+       no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "interface", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "interface", argv[0]);
+}
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+DEFUN (match_ip_next_hop,
+       match_ip_next_hop_cmd,
+       "match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+{
+  return rip_route_match_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop,
+       no_match_ip_next_hop_cmd,
+       "no match ip next-hop",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "ip next-hop", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop,
+       no_match_ip_next_hop_val_cmd,
+       "no match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+DEFUN (match_ip_next_hop_prefix_list,
+       match_ip_next_hop_prefix_list_cmd,
+       "match ip next-hop prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return rip_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_cmd,
+       "no match ip next-hop prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_val_cmd,
+       "no match ip next-hop prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_ip_address,
+       match_ip_address_cmd,
+       "match ip address (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+{
+  return rip_route_match_add (vty, vty->index, "ip address", argv[0]);
+}
+
+DEFUN (no_match_ip_address, 
+       no_match_ip_address_cmd,
+       "no match ip address",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "ip address", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "ip address", argv[0]);
+}
+
+ALIAS (no_match_ip_address,
+       no_match_ip_address_val_cmd,
+       "no match ip address (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+DEFUN (match_ip_address_prefix_list, 
+       match_ip_address_prefix_list_cmd,
+       "match ip address prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return rip_route_match_add (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_cmd,
+       "no match ip address prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "ip address prefix-list", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_val_cmd,
+       "no match ip address prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_tag, 
+       match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+{
+  return rip_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return rip_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return rip_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+
+/* set functions */
+
+DEFUN (set_metric,
+       set_metric_cmd,
+       "set metric <0-4294967295>",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+{
+  return rip_route_set_add (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (set_metric,
+       set_metric_addsub_cmd,
+       "set metric <+/-metric>",
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Add or subtract metric\n")
+
+DEFUN (no_set_metric,
+       no_set_metric_cmd,
+       "no set metric",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n")
+{
+  if (argc == 0)
+    return rip_route_set_delete (vty, vty->index, "metric", NULL);
+
+  return rip_route_set_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_set_metric,
+       no_set_metric_val_cmd,
+       "no set metric <0-4294967295>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+
+ALIAS (no_set_metric,
+       no_set_metric_addsub_cmd,
+       "no set metric <+/-metric>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Add or subtract metric\n")
+
+DEFUN (set_ip_nexthop,
+       set_ip_nexthop_cmd,
+       "set ip next-hop A.B.C.D",
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "IP address of next hop\n")
+{
+  union sockunion su;
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed next-hop address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return rip_route_set_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (no_set_ip_nexthop,
+       no_set_ip_nexthop_cmd,
+       "no set ip next-hop",
+       NO_STR
+       SET_STR
+       IP_STR
+       "Next hop address\n")
+{
+  if (argc == 0)
+    return rip_route_set_delete (vty, vty->index, "ip next-hop", NULL);
+  
+  return rip_route_set_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_set_ip_nexthop,
+       no_set_ip_nexthop_val_cmd,
+       "no set ip next-hop A.B.C.D",
+       NO_STR
+       SET_STR
+       IP_STR
+       "Next hop address\n"
+       "IP address of next hop\n")
+
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <1-4294967295>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return rip_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+    return rip_route_set_delete (vty, vty->index, "tag", NULL);
+  
+  return rip_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+void
+rip_route_map_reset ()
+{
+  ;
+}
+
+/* Route-map init */
+void
+rip_route_map_init ()
+{
+  route_map_init ();
+  route_map_init_vty ();
+  route_map_add_hook (rip_route_map_update);
+  route_map_delete_hook (rip_route_map_update);
+
+  route_map_install_match (&route_match_metric_cmd);
+  route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_ip_next_hop_cmd);
+  route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd);
+  route_map_install_match (&route_match_ip_address_cmd);
+  route_map_install_match (&route_match_ip_address_prefix_list_cmd);
+  route_map_install_match (&route_match_tag_cmd);
+
+  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_set (&route_set_ip_nexthop_cmd);
+  route_map_install_set (&route_set_tag_cmd);
+
+  install_element (RMAP_NODE, &match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_val_cmd);
+  install_element (RMAP_NODE, &match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_val_cmd);
+  install_element (RMAP_NODE, &match_ip_next_hop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd);
+  install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_val_cmd);
+  install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
+
+  install_element (RMAP_NODE, &set_metric_cmd);
+  install_element (RMAP_NODE, &set_metric_addsub_cmd);
+  install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+  install_element (RMAP_NODE, &no_set_metric_addsub_cmd);
+  install_element (RMAP_NODE, &set_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &no_set_ip_nexthop_cmd);
+  install_element (RMAP_NODE, &no_set_ip_nexthop_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
+}
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
new file mode 100644 (file)
index 0000000..2c7cd2c
--- /dev/null
@@ -0,0 +1,593 @@
+/* RIP SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "table.h"
+#include "smux.h"
+
+#include "ripd/ripd.h"
+
+/* RIPv2-MIB. */
+#define RIPV2MIB 1,3,6,1,2,1,23
+
+/* RIPv2-MIB rip2Globals values. */
+#define RIP2GLOBALROUTECHANGES  1
+#define RIP2GLOBALQUERIES       2
+
+/* RIPv2-MIB rip2IfStatEntry. */
+#define RIP2IFSTATENTRY         1
+
+/* RIPv2-MIB rip2IfStatTable. */
+#define RIP2IFSTATADDRESS       1
+#define RIP2IFSTATRCVBADPACKETS 2
+#define RIP2IFSTATRCVBADROUTES  3
+#define RIP2IFSTATSENTUPDATES   4
+#define RIP2IFSTATSTATUS        5
+
+/* RIPv2-MIB rip2IfConfTable. */
+#define RIP2IFCONFADDRESS       1
+#define RIP2IFCONFDOMAIN        2
+#define RIP2IFCONFAUTHTYPE      3
+#define RIP2IFCONFAUTHKEY       4
+#define RIP2IFCONFSEND          5
+#define RIP2IFCONFRECEIVE       6
+#define RIP2IFCONFDEFAULTMETRIC 7
+#define RIP2IFCONFSTATUS        8
+#define RIP2IFCONFSRCADDRESS    9
+
+/* RIPv2-MIB rip2PeerTable. */
+#define RIP2PEERADDRESS         1
+#define RIP2PEERDOMAIN          2
+#define RIP2PEERLASTUPDATE      3
+#define RIP2PEERVERSION         4
+#define RIP2PEERRCVBADPACKETS   5
+#define RIP2PEERRCVBADROUTES    6
+
+/* SNMP value hack. */
+#define COUNTER     ASN_COUNTER
+#define INTEGER     ASN_INTEGER
+#define TIMETICKS   ASN_TIMETICKS
+#define IPADDRESS   ASN_IPADDRESS
+#define STRING      ASN_OCTET_STR
+
+/* Define SNMP local variables. */
+SNMP_LOCAL_VARIABLES
+
+/* RIP-MIB instances. */
+oid rip_oid [] = { RIPV2MIB };
+
+/* Interface cache table sorted by interface's address. */
+struct route_table *rip_ifaddr_table;
+
+/* Hook functions. */
+static u_char *rip2Globals (struct variable *, oid [], size_t *,
+                           int, size_t *, WriteMethod **);
+static u_char *rip2IfStatEntry (struct variable *, oid [], size_t *,
+                               int, size_t *, WriteMethod **);
+static u_char *rip2IfConfAddress (struct variable *, oid [], size_t *,
+                                 int, size_t *, WriteMethod **);
+static u_char *rip2PeerTable (struct variable *, oid [], size_t *,
+                             int, size_t *, WriteMethod **);
+
+struct variable rip_variables[] = 
+{
+  /* RIP Global Counters. */
+  {RIP2GLOBALROUTECHANGES,    COUNTER, RONLY, rip2Globals,
+   2, {1, 1}},
+  {RIP2GLOBALQUERIES,         COUNTER, RONLY, rip2Globals,
+   2, {1, 2}},
+  /* RIP Interface Tables. */
+  {RIP2IFSTATADDRESS,         IPADDRESS, RONLY, rip2IfStatEntry,
+   3, {2, 1, 1}},
+  {RIP2IFSTATRCVBADPACKETS,   COUNTER, RONLY, rip2IfStatEntry,
+   3, {2, 1, 2}},
+  {RIP2IFSTATRCVBADROUTES,    COUNTER, RONLY, rip2IfStatEntry,
+   3, {2, 1, 3}},
+  {RIP2IFSTATSENTUPDATES,     COUNTER, RONLY, rip2IfStatEntry,
+   3, {2, 1, 4}},
+  {RIP2IFSTATSTATUS,          COUNTER, RWRITE, rip2IfStatEntry,
+   3, {2, 1, 5}},
+  {RIP2IFCONFADDRESS,         IPADDRESS, RONLY, rip2IfConfAddress,
+   /* RIP Interface Configuration Table. */
+   3, {3, 1, 1}},
+  {RIP2IFCONFDOMAIN,          STRING, RONLY, rip2IfConfAddress,
+   3, {3, 1, 2}},
+  {RIP2IFCONFAUTHTYPE,        COUNTER, RONLY, rip2IfConfAddress,
+   3, {3, 1, 3}},
+  {RIP2IFCONFAUTHKEY,         STRING, RONLY, rip2IfConfAddress,
+   3, {3, 1, 4}},
+  {RIP2IFCONFSEND,            COUNTER, RONLY, rip2IfConfAddress,
+   3, {3, 1, 5}},
+  {RIP2IFCONFRECEIVE,         COUNTER, RONLY, rip2IfConfAddress,
+   3, {3, 1, 6}},
+  {RIP2IFCONFDEFAULTMETRIC,   COUNTER, RONLY, rip2IfConfAddress,
+   3, {3, 1, 7}},
+  {RIP2IFCONFSTATUS,          COUNTER, RONLY, rip2IfConfAddress,
+   3, {3, 1, 8}},
+  {RIP2IFCONFSRCADDRESS,      IPADDRESS, RONLY, rip2IfConfAddress,
+   3, {3, 1, 9}},
+  {RIP2PEERADDRESS,           IPADDRESS, RONLY, rip2PeerTable,
+   /* RIP Peer Table. */
+   3, {4, 1, 1}},
+  {RIP2PEERDOMAIN,            STRING, RONLY, rip2PeerTable,
+   3, {4, 1, 2}},
+  {RIP2PEERLASTUPDATE,        TIMETICKS, RONLY, rip2PeerTable,
+   3, {4, 1, 3}},
+  {RIP2PEERVERSION,           INTEGER, RONLY, rip2PeerTable,
+   3, {4, 1, 4}},
+  {RIP2PEERRCVBADPACKETS,     COUNTER, RONLY, rip2PeerTable,
+   3, {4, 1, 5}},
+  {RIP2PEERRCVBADROUTES,      COUNTER, RONLY, rip2PeerTable,
+   3, {4, 1, 6}}
+};
+
+extern struct thread_master *master;
+
+static u_char *
+rip2Globals (struct variable *v, oid name[], size_t *length,
+            int exact, size_t *var_len, WriteMethod **write_method)
+{
+  if (smux_header_generic(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  /* Retrun global counter. */
+  switch (v->magic)
+    {
+    case RIP2GLOBALROUTECHANGES:
+      return SNMP_INTEGER (rip_global_route_changes);
+      break;
+    case RIP2GLOBALQUERIES:
+      return SNMP_INTEGER (rip_global_queries);
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  return NULL;
+}
+
+void
+rip_ifaddr_add (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix *p;
+  struct route_node *rn;
+
+  p = ifc->address;
+
+  if (p->family != AF_INET)
+    return;
+
+  rn = route_node_get (rip_ifaddr_table, p);
+  rn->info = ifp;
+}
+
+void
+rip_ifaddr_delete (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix *p;
+  struct route_node *rn;
+  struct interface *i;
+
+  p = ifc->address;
+
+  if (p->family != AF_INET)
+    return;
+
+  rn = route_node_lookup (rip_ifaddr_table, p);
+  if (! rn)
+    return;
+  i = rn->info;
+  if (rn && !strncmp(i->name,ifp->name,INTERFACE_NAMSIZ))
+    {
+      rn->info = NULL;
+      route_unlock_node (rn);
+      route_unlock_node (rn);
+    }
+}
+
+static struct interface *
+rip_ifaddr_lookup_next (struct in_addr *addr)
+{
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct interface *ifp;
+
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_BITLEN;
+  p.prefix = *addr;
+
+  rn = route_node_get (rip_ifaddr_table, (struct prefix *) &p);
+
+  for (rn = route_next (rn); rn; rn = route_next (rn))
+    if (rn->info)
+      break;
+
+  if (rn && rn->info)
+    {
+      ifp = rn->info;
+      *addr = rn->p.u.prefix4;
+      route_unlock_node (rn);
+      return ifp;
+    }
+  return NULL;
+}
+
+static struct interface *
+rip2IfLookup (struct variable *v, oid name[], size_t *length, 
+             struct in_addr *addr, int exact)
+{
+  int len;
+  struct interface *ifp;
+  
+  if (exact)
+    {
+      /* Check the length. */
+      if (*length - v->namelen != sizeof (struct in_addr))
+       return NULL;
+
+      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+      return if_lookup_exact_address (*addr);
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len > 4) len = 4;
+
+      oid2in_addr (name + v->namelen, len, addr);
+
+      ifp = rip_ifaddr_lookup_next (addr);
+
+      if (ifp == NULL)
+       return NULL;
+
+      oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr));
+
+      *length = v->namelen + sizeof (struct in_addr);
+
+      return ifp;
+    }
+  return NULL;
+}
+
+static struct rip_peer *
+rip2PeerLookup (struct variable *v, oid name[], size_t *length, 
+               struct in_addr *addr, int exact)
+{
+  int len;
+  struct rip_peer *peer;
+  
+  if (exact)
+    {
+      /* Check the length. */
+      if (*length - v->namelen != sizeof (struct in_addr) + 1)
+       return NULL;
+
+      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+      peer = rip_peer_lookup (addr);
+
+      if (peer->domain == (int)name[v->namelen + sizeof (struct in_addr)])
+       return peer;
+
+      return NULL;
+    }
+  else
+    {
+      len = *length - v->namelen;
+      if (len > 4) len = 4;
+
+      oid2in_addr (name + v->namelen, len, addr);
+
+      len = *length - v->namelen;
+      peer = rip_peer_lookup (addr);
+      if (peer)
+       {
+         if ((len < (int)sizeof (struct in_addr) + 1) ||
+             (peer->domain > (int)name[v->namelen + sizeof (struct in_addr)]))
+           {
+             oid_copy_addr (name + v->namelen, &peer->addr,
+                            sizeof (struct in_addr));
+             name[v->namelen + sizeof (struct in_addr)] = peer->domain;
+             *length = sizeof (struct in_addr) + v->namelen + 1;
+             return peer;
+           }
+        } 
+      peer = rip_peer_lookup_next (addr);
+
+      if (! peer)
+       return NULL;
+
+      oid_copy_addr (name + v->namelen, &peer->addr,
+                    sizeof (struct in_addr));
+      name[v->namelen + sizeof (struct in_addr)] = peer->domain;
+      *length = sizeof (struct in_addr) + v->namelen + 1;
+
+      return peer;
+    }
+  return NULL;
+}
+
+static u_char *
+rip2IfStatEntry (struct variable *v, oid name[], size_t *length,
+                int exact, size_t *var_len, WriteMethod **write_method)
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+  static struct in_addr addr;
+  static long valid = SNMP_VALID;
+
+  if (smux_header_table(v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+  
+  /* Lookup interface. */
+  ifp = rip2IfLookup (v, name, length, &addr, exact);
+  if (! ifp)
+    return NULL;
+
+  /* Fetch rip_interface information. */
+  ri = ifp->info;
+
+  switch (v->magic)
+    {
+    case RIP2IFSTATADDRESS:
+      return SNMP_IPADDRESS (addr);
+      break;
+    case RIP2IFSTATRCVBADPACKETS:
+      *var_len = sizeof (long);
+      return (u_char *) &ri->recv_badpackets;
+
+    case RIP2IFSTATRCVBADROUTES:
+      *var_len = sizeof (long);
+      return (u_char *) &ri->recv_badroutes;
+
+    case RIP2IFSTATSENTUPDATES:
+      *var_len = sizeof (long);
+      return (u_char *) &ri->sent_updates;
+
+    case RIP2IFSTATSTATUS:
+      *var_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *) &valid;
+
+    default:
+      return NULL;
+
+    }
+  return NULL;
+}
+
+static long
+rip2IfConfSend (struct rip_interface *ri)
+{
+#define doNotSend       1
+#define ripVersion1     2
+#define rip1Compatible  3
+#define ripVersion2     4
+#define ripV1Demand     5
+#define ripV2Demand     6
+
+  if (! ri->running)
+    return doNotSend;
+    
+  if (ri->ri_send & RIPv2)
+    return ripVersion2;
+  else if (ri->ri_send & RIPv1)
+    return ripVersion1;
+  else if (rip)
+    {
+      if (rip->version_send == RIPv2)
+       return ripVersion2;
+      else if (rip->version_send == RIPv1)
+       return ripVersion1;
+    }
+  return doNotSend;
+}
+
+static long
+rip2IfConfReceive (struct rip_interface *ri)
+{
+#define rip1            1
+#define rip2            2
+#define rip1OrRip2      3
+#define doNotReceive    4
+
+  int recvv;
+
+  if (! ri->running)
+    return doNotReceive;
+
+  recvv = (ri->ri_receive == RI_RIP_UNSPEC) ?  rip->version_recv :
+                                               ri->ri_receive;
+  if (recvv == RI_RIP_VERSION_1_AND_2)
+    return rip1OrRip2;
+  else if (recvv & RIPv2)
+    return rip2;
+  else if (recvv & RIPv1)
+    return rip1;
+  else
+    return doNotReceive;
+}
+
+static u_char *
+rip2IfConfAddress (struct variable *v, oid name[], size_t *length,
+                  int exact, size_t *val_len, WriteMethod **write_method)
+{
+  static struct in_addr addr;
+  static long valid = SNMP_INVALID;
+  static long domain = 0;
+  static long config = 0;
+  static u_int auth = 0;
+  struct interface *ifp;
+  struct rip_interface *ri;
+
+  if (smux_header_table(v, name, length, exact, val_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+  
+  /* Lookup interface. */
+  ifp = rip2IfLookup (v, name, length, &addr, exact);
+  if (! ifp)
+    return NULL;
+
+  /* Fetch rip_interface information. */
+  ri = ifp->info;
+
+  switch (v->magic)
+    {
+    case RIP2IFCONFADDRESS:
+      *val_len = sizeof (struct in_addr);
+      return (u_char *) &addr;
+
+    case RIP2IFCONFDOMAIN:
+      *val_len = 2;
+      return (u_char *) &domain;
+
+    case RIP2IFCONFAUTHTYPE:
+      auth = ri->auth_type;
+      *val_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *)&auth;
+
+    case RIP2IFCONFAUTHKEY:
+      *val_len = 0;
+      return (u_char *) &domain;
+    case RIP2IFCONFSEND:
+      config = rip2IfConfSend (ri);
+      *val_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *) &config;
+    case RIP2IFCONFRECEIVE:
+      config = rip2IfConfReceive (ri);
+      *val_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *) &config;
+
+    case RIP2IFCONFDEFAULTMETRIC:
+      *val_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *) &ifp->metric;
+    case RIP2IFCONFSTATUS:
+      *val_len = sizeof (long);
+      v->type = ASN_INTEGER;
+      return (u_char *) &valid;
+    case RIP2IFCONFSRCADDRESS:
+      *val_len = sizeof (struct in_addr);
+      return (u_char *) &addr;
+
+    default:
+      return NULL;
+
+    }
+  return NULL;
+}
+
+static u_char *
+rip2PeerTable (struct variable *v, oid name[], size_t *length,
+              int exact, size_t *val_len, WriteMethod **write_method)
+{
+  static struct in_addr addr;
+  static int domain = 0;
+  static int version;
+  /* static time_t uptime; */
+
+  struct rip_peer *peer;
+
+  if (smux_header_table(v, name, length, exact, val_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  memset (&addr, 0, sizeof (struct in_addr));
+  
+  /* Lookup interface. */
+  peer = rip2PeerLookup (v, name, length, &addr, exact);
+  if (! peer)
+    return NULL;
+
+  switch (v->magic)
+    {
+    case RIP2PEERADDRESS:
+      *val_len = sizeof (struct in_addr);
+      return (u_char *) &peer->addr;
+
+    case RIP2PEERDOMAIN:
+      *val_len = 2;
+      return (u_char *) &domain;
+
+    case RIP2PEERLASTUPDATE:
+#if 0 
+      /* We don't know the SNMP agent startup time. We have two choices here:
+       * - assume ripd startup time equals SNMP agent startup time
+       * - don't support this variable, at all
+       * Currently, we do the latter...
+       */
+      *val_len = sizeof (time_t);
+      uptime = peer->uptime; /* now - snmp_agent_startup - peer->uptime */
+      return (u_char *) &uptime;
+#else
+      return (u_char *) NULL;
+#endif
+
+    case RIP2PEERVERSION:
+      *val_len = sizeof (int);
+      version = peer->version;
+      return (u_char *) &version;
+
+    case RIP2PEERRCVBADPACKETS:
+      *val_len = sizeof (int);
+      return (u_char *) &peer->recv_badpackets;
+
+    case RIP2PEERRCVBADROUTES:
+      *val_len = sizeof (int);
+      return (u_char *) &peer->recv_badroutes;
+
+    default:
+      return NULL;
+
+    }
+  return NULL;
+}
+
+/* Register RIPv2-MIB. */
+void
+rip_snmp_init ()
+{
+  rip_ifaddr_table = route_table_init ();
+
+  smux_init (master);
+  REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid);
+}
+#endif /* HAVE_SNMP */
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
new file mode 100644 (file)
index 0000000..0b51af5
--- /dev/null
@@ -0,0 +1,762 @@
+/* RIPd and zebra interface.
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "stream.h"
+#include "memory.h"
+#include "routemap.h"
+#include "zclient.h"
+#include "log.h"
+#include "vrf.h"
+#include "ripd/ripd.h"
+#include "ripd/rip_debug.h"
+#include "ripd/rip_interface.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+
+/* Send ECMP routes to zebra. */
+static void
+rip_zebra_ipv4_send (struct route_node *rp, u_char cmd)
+{
+  static struct in_addr **nexthops = NULL;
+  static unsigned int nexthops_len = 0;
+
+  struct list *list = (struct list *)rp->info;
+  struct zapi_ipv4 api;
+  struct listnode *listnode = NULL;
+  struct rip_info *rinfo = NULL;
+  int count = 0;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT))
+    {
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_RIP;
+      api.flags = 0;
+      api.message = 0;
+      api.safi = SAFI_UNICAST;
+
+      if (nexthops_len < listcount (list))
+        {
+          nexthops_len = listcount (list);
+          nexthops = XREALLOC (MTYPE_TMP, nexthops,
+                               nexthops_len * sizeof (struct in_addr *));
+        }
+
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          nexthops[count++] = &rinfo->nexthop;
+          if (cmd == ZEBRA_IPV4_ROUTE_ADD)
+            SET_FLAG (rinfo->flags, RIP_RTF_FIB);
+          else
+            UNSET_FLAG (rinfo->flags, RIP_RTF_FIB);
+        }
+
+      api.nexthop = nexthops;
+      api.nexthop_num = count;
+      api.ifindex_num = 0;
+
+      rinfo = listgetdata (listhead (list));
+
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = rinfo->metric;
+
+      if (rinfo->distance && rinfo->distance != ZEBRA_RIP_DISTANCE_DEFAULT)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+          api.distance = rinfo->distance;
+        }
+
+      if (rinfo->tag)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+          api.tag = rinfo->tag;
+        }
+
+      zapi_ipv4_route (cmd, zclient,
+                       (struct prefix_ipv4 *)&rp->p, &api);
+
+      if (IS_RIP_DEBUG_ZEBRA)
+        {
+          if (rip->ecmp)
+            zlog_debug ("%s: %s/%d nexthops %d",
+                        (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \
+                            "Install into zebra" : "Delete from zebra",
+                        inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen, count);
+          else
+            zlog_debug ("%s: %s/%d",
+                        (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \
+                            "Install into zebra" : "Delete from zebra",
+                        inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
+        }
+
+      rip_global_route_changes++;
+    }
+}
+
+/* Add/update ECMP routes to zebra. */
+void
+rip_zebra_ipv4_add (struct route_node *rp)
+{
+  rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_ADD);
+}
+
+/* Delete ECMP routes from zebra. */
+void
+rip_zebra_ipv4_delete (struct route_node *rp)
+{
+  rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_DELETE);
+}
+
+/* Zebra route add and delete treatment. */
+static int
+rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv4 api;
+  unsigned long ifindex;
+  struct in_addr nexthop;
+  struct prefix_ipv4 p;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  nexthop.s_addr = 0;
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      nexthop.s_addr = stream_get_ipv4 (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 255;
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  /* Then fetch IPv4 prefixes. */
+  if (command == ZEBRA_IPV4_ROUTE_ADD)
+    rip_redistribute_add (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex, 
+                          &nexthop, api.metric, api.distance, api.tag);
+  else
+    rip_redistribute_delete (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex);
+
+  return 0;
+}
+
+void
+rip_zclient_reset (void)
+{
+  zclient_reset (zclient);
+}
+
+/* RIP route-map set for redistribution */
+static void
+rip_routemap_set (int type, const char *name)
+{
+  if (rip->route_map[type].name)
+    free(rip->route_map[type].name);
+
+  rip->route_map[type].name = strdup (name);
+  rip->route_map[type].map = route_map_lookup_by_name (name);
+}
+
+static void
+rip_redistribute_metric_set (int type, unsigned int metric)
+{
+  rip->route_map[type].metric_config = 1;
+  rip->route_map[type].metric = metric;
+}
+
+static int
+rip_metric_unset (int type, unsigned int metric)
+{
+#define DONT_CARE_METRIC_RIP 17  
+  if (metric != DONT_CARE_METRIC_RIP &&
+      rip->route_map[type].metric != metric)
+    return 1;
+  rip->route_map[type].metric_config = 0;
+  rip->route_map[type].metric = 0;
+  return 0;
+}
+
+/* RIP route-map unset for redistribution */
+static int
+rip_routemap_unset (int type, const char *name)
+{
+  if (! rip->route_map[type].name ||
+      (name != NULL && strcmp(rip->route_map[type].name,name)))
+    return 1;
+
+  free (rip->route_map[type].name);
+  rip->route_map[type].name = NULL;
+  rip->route_map[type].map = NULL;
+
+  return 0;
+}
+
+/* Redistribution types */
+static struct {
+  int type;
+  int str_min_len;
+  const char *str;
+} redist_type[] = {
+  {ZEBRA_ROUTE_KERNEL,  1, "kernel"},
+  {ZEBRA_ROUTE_CONNECT, 1, "connected"},
+  {ZEBRA_ROUTE_STATIC,  1, "static"},
+  {ZEBRA_ROUTE_OSPF,    1, "ospf"},
+  {ZEBRA_ROUTE_BGP,     2, "bgp"},
+  {ZEBRA_ROUTE_BABEL,   2, "babel"},
+  {0, 0, NULL}
+};
+
+DEFUN (router_zebra,
+       router_zebra_cmd,
+       "router zebra",
+       "Enable a routing process\n"
+       "Make connection to zebra daemon\n")
+{
+  vty->node = ZEBRA_NODE;
+  zclient->enable = 1;
+  zclient_start (zclient);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_zebra,
+       no_router_zebra_cmd,
+       "no router zebra",
+       NO_STR
+       "Enable a routing process\n"
+       "Make connection to zebra daemon\n")
+{
+  zclient->enable = 0;
+  zclient_stop (zclient);
+  return CMD_SUCCESS;
+}
+
+#if 0
+static int
+rip_redistribute_set (int type)
+{
+  if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return CMD_SUCCESS;
+
+  vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT);
+
+  if (zclient->sock > 0)
+    zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type);
+
+  return CMD_SUCCESS;
+}
+#endif
+
+static int
+rip_redistribute_unset (int type)
+{
+  if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return CMD_SUCCESS;
+
+  vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT);
+
+  if (zclient->sock > 0)
+    zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type,
+                             VRF_DEFAULT);
+
+  /* Remove the routes from RIP table. */
+  rip_redistribute_withdraw (type);
+
+  return CMD_SUCCESS;
+}
+
+int
+rip_redistribute_check (int type)
+{
+  return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT);
+}
+
+void
+rip_redistribute_clean (void)
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++)
+    {
+      if (vrf_bitmap_check (zclient->redist[redist_type[i].type], VRF_DEFAULT))
+       {
+         if (zclient->sock > 0)
+           zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE,
+                                    zclient, redist_type[i].type,
+                                    VRF_DEFAULT);
+
+         vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT);
+
+         /* Remove the routes from RIP table. */
+         rip_redistribute_withdraw (redist_type[i].type);
+       }
+    }
+}
+
+DEFUN (rip_redistribute_rip,
+       rip_redistribute_rip_cmd,
+       "redistribute rip",
+       "Redistribute information from another routing protocol\n"
+       "Routing Information Protocol (RIP)\n")
+{
+  vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_redistribute_rip,
+       no_rip_redistribute_rip_cmd,
+       "no redistribute rip",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       "Routing Information Protocol (RIP)\n")
+{
+  vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (rip_redistribute_type,
+       rip_redistribute_type_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPD,
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD)
+{
+  int i;
+
+  for(i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp (redist_type[i].str, argv[0], 
+                  redist_type[i].str_min_len) == 0) 
+       {
+         zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, 
+                               redist_type[i].type, VRF_DEFAULT);
+         return CMD_SUCCESS;
+       }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (no_rip_redistribute_type,
+       no_rip_redistribute_type_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPD,
+       NO_STR
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD)
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp(redist_type[i].str, argv[0], 
+                 redist_type[i].str_min_len) == 0) 
+       {
+         rip_metric_unset (redist_type[i].type, DONT_CARE_METRIC_RIP);
+         rip_routemap_unset (redist_type[i].type,NULL);
+         rip_redistribute_unset (redist_type[i].type);
+         return CMD_SUCCESS;
+        }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (rip_redistribute_type_routemap,
+       rip_redistribute_type_routemap_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPD " route-map WORD",
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+               redist_type[i].str_min_len) == 0) 
+      {
+       rip_routemap_set (redist_type[i].type, argv[1]);
+       zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type,
+                             VRF_DEFAULT);
+       return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (no_rip_redistribute_type_routemap,
+       no_rip_redistribute_type_routemap_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPD " route-map WORD",
+       NO_STR
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp(redist_type[i].str, argv[0], 
+                 redist_type[i].str_min_len) == 0) 
+       {
+         if (rip_routemap_unset (redist_type[i].type,argv[1]))
+           return CMD_WARNING;
+         rip_redistribute_unset (redist_type[i].type);
+         return CMD_SUCCESS;
+        }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (rip_redistribute_type_metric,
+       rip_redistribute_type_metric_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16>",
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Metric\n"
+       "Metric value\n")
+{
+  int i;
+  int metric;
+
+  metric = atoi (argv[1]);
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+               redist_type[i].str_min_len) == 0) 
+      {
+       rip_redistribute_metric_set (redist_type[i].type, metric);
+       zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type,
+                             VRF_DEFAULT);
+       return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (no_rip_redistribute_type_metric,
+       no_rip_redistribute_type_metric_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16>",
+       NO_STR
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Metric\n"
+       "Metric value\n")
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp(redist_type[i].str, argv[0], 
+                 redist_type[i].str_min_len) == 0) 
+       {
+         if (rip_metric_unset (redist_type[i].type, atoi(argv[1])))
+           return CMD_WARNING;
+         rip_redistribute_unset (redist_type[i].type);
+         return CMD_SUCCESS;
+        }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+DEFUN (rip_redistribute_type_metric_routemap,
+       rip_redistribute_type_metric_routemap_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16> route-map WORD",
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Metric\n"
+       "Metric value\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int i;
+  int metric;
+
+  metric = atoi (argv[1]);
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+               redist_type[i].str_min_len) == 0) 
+      {
+       rip_redistribute_metric_set (redist_type[i].type, metric);
+       rip_routemap_set (redist_type[i].type, argv[2]);
+       zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type,
+                             VRF_DEFAULT);
+       return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+
+DEFUN (no_rip_redistribute_type_metric_routemap,
+       no_rip_redistribute_type_metric_routemap_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPD
+       " metric <0-16> route-map WORD",
+       NO_STR
+       REDIST_STR
+       QUAGGA_REDIST_HELP_STR_RIPD
+       "Metric\n"
+       "Metric value\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp(redist_type[i].str, argv[0], 
+                 redist_type[i].str_min_len) == 0) 
+       {
+         if (rip_metric_unset (redist_type[i].type, atoi(argv[1])))
+           return CMD_WARNING;
+         if (rip_routemap_unset (redist_type[i].type, argv[2]))
+           {
+             rip_redistribute_metric_set(redist_type[i].type, atoi(argv[1]));   
+             return CMD_WARNING;
+           }
+         rip_redistribute_unset (redist_type[i].type);
+         return CMD_SUCCESS;
+        }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+         VTY_NEWLINE);
+
+  return CMD_WARNING;
+}
+
+/* Default information originate. */
+
+DEFUN (rip_default_information_originate,
+       rip_default_information_originate_cmd,
+       "default-information originate",
+       "Control distribution of default route\n"
+       "Distribute a default route\n")
+{
+  struct prefix_ipv4 p;
+
+  if (! rip->default_information)
+    {
+      memset (&p, 0, sizeof (struct prefix_ipv4));
+      p.family = AF_INET;
+
+      rip->default_information = 1;
+  
+      rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0, 
+                            NULL, 0, 0, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_default_information_originate,
+       no_rip_default_information_originate_cmd,
+       "no default-information originate",
+       NO_STR
+       "Control distribution of default route\n"
+       "Distribute a default route\n")
+{
+  struct prefix_ipv4 p;
+
+  if (rip->default_information)
+    {
+      memset (&p, 0, sizeof (struct prefix_ipv4));
+      p.family = AF_INET;
+
+      rip->default_information = 0;
+  
+      rip_redistribute_delete (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* RIP configuration write function. */
+static int
+config_write_zebra (struct vty *vty)
+{
+  if (! zclient->enable)
+    {
+      vty_out (vty, "no router zebra%s", VTY_NEWLINE);
+      return 1;
+    }
+  else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT))
+    {
+      vty_out (vty, "router zebra%s", VTY_NEWLINE);
+      vty_out (vty, " no redistribute rip%s", VTY_NEWLINE);
+      return 1;
+    }
+  return 0;
+}
+
+int
+config_write_rip_redistribute (struct vty *vty, int config_mode)
+{
+  int i;
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    if (i != zclient->redist_default &&
+        vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT))
+      {
+       if (config_mode)
+         {
+           if (rip->route_map[i].metric_config)
+             {
+               if (rip->route_map[i].name)
+                 vty_out (vty, " redistribute %s metric %d route-map %s%s",
+                          zebra_route_string(i), rip->route_map[i].metric,
+                          rip->route_map[i].name,
+                          VTY_NEWLINE);
+               else
+                 vty_out (vty, " redistribute %s metric %d%s",
+                          zebra_route_string(i), rip->route_map[i].metric,
+                          VTY_NEWLINE);
+             }
+           else
+             {
+               if (rip->route_map[i].name)
+                 vty_out (vty, " redistribute %s route-map %s%s",
+                          zebra_route_string(i), rip->route_map[i].name,
+                          VTY_NEWLINE);
+               else
+                 vty_out (vty, " redistribute %s%s", zebra_route_string(i),
+                          VTY_NEWLINE);
+             }
+         }
+       else
+         vty_out (vty, " %s", zebra_route_string(i));
+      }
+  return 0;
+}
+
+/* Zebra node structure. */
+static struct cmd_node zebra_node =
+{
+  ZEBRA_NODE,
+  "%s(config-router)# ",
+};
+
+static void
+rip_zebra_connected (struct zclient *zclient)
+{
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+void
+rip_zclient_init (struct thread_master *master)
+{
+  /* Set default value to the zebra client structure. */
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_RIP);
+  zclient->zebra_connected = rip_zebra_connected;
+  zclient->interface_add = rip_interface_add;
+  zclient->interface_delete = rip_interface_delete;
+  zclient->interface_address_add = rip_interface_address_add;
+  zclient->interface_address_delete = rip_interface_address_delete;
+  zclient->ipv4_route_add = rip_zebra_read_ipv4;
+  zclient->ipv4_route_delete = rip_zebra_read_ipv4;
+  zclient->interface_up = rip_interface_up;
+  zclient->interface_down = rip_interface_down;
+  
+  /* Install zebra node. */
+  install_node (&zebra_node, config_write_zebra);
+
+  /* Install command elements to zebra node. */ 
+  install_element (CONFIG_NODE, &router_zebra_cmd);
+  install_element (CONFIG_NODE, &no_router_zebra_cmd);
+  install_default (ZEBRA_NODE);
+  install_element (ZEBRA_NODE, &rip_redistribute_rip_cmd);
+  install_element (ZEBRA_NODE, &no_rip_redistribute_rip_cmd);
+
+  /* Install command elements to rip node. */
+  install_element (RIP_NODE, &rip_redistribute_type_cmd);
+  install_element (RIP_NODE, &rip_redistribute_type_routemap_cmd);
+  install_element (RIP_NODE, &rip_redistribute_type_metric_cmd);
+  install_element (RIP_NODE, &rip_redistribute_type_metric_routemap_cmd);
+  install_element (RIP_NODE, &no_rip_redistribute_type_cmd);
+  install_element (RIP_NODE, &no_rip_redistribute_type_routemap_cmd);
+  install_element (RIP_NODE, &no_rip_redistribute_type_metric_cmd);
+  install_element (RIP_NODE, &no_rip_redistribute_type_metric_routemap_cmd);
+  install_element (RIP_NODE, &rip_default_information_originate_cmd);
+  install_element (RIP_NODE, &no_rip_default_information_originate_cmd);
+}
diff --git a/ripd/ripd.c b/ripd/ripd.c
new file mode 100644 (file)
index 0000000..c073eca
--- /dev/null
@@ -0,0 +1,4143 @@
+/* RIP version 1 and 2.
+ * Copyright (C) 2005 6WIND <alain.ritoux@6wind.com>
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "thread.h"
+#include "memory.h"
+#include "log.h"
+#include "stream.h"
+#include "filter.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "routemap.h"
+#include "if_rmap.h"
+#include "plist.h"
+#include "distribute.h"
+#include "md5.h"
+#include "keychain.h"
+#include "privs.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_debug.h"
+
+/* UDP receive buffer size */
+#define RIP_UDP_RCV_BUF 41600
+
+/* privileges global */
+extern struct zebra_privs_t ripd_privs;
+
+/* RIP Structure. */
+struct rip *rip = NULL;
+
+/* RIP neighbor address table. */
+struct route_table *rip_neighbor_table;
+
+/* RIP route changes. */
+long rip_global_route_changes = 0;
+
+/* RIP queries. */
+long rip_global_queries = 0;
+
+/* Prototypes. */
+static void rip_event (enum rip_event, int);
+static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char);
+static int rip_triggered_update (struct thread *);
+static int rip_update_jitter (unsigned long);
+
+/* RIP output routes type. */
+enum
+{
+  rip_all_route,
+  rip_changed_route
+};
+
+/* RIP command strings. */
+static const struct message rip_msg[] =
+{
+  {RIP_REQUEST,    "REQUEST"},
+  {RIP_RESPONSE,   "RESPONSE"},
+  {RIP_TRACEON,    "TRACEON"},
+  {RIP_TRACEOFF,   "TRACEOFF"},
+  {RIP_POLL,       "POLL"},
+  {RIP_POLL_ENTRY, "POLL ENTRY"},
+  {0, NULL},
+};
+
+/* Utility function to set boradcast option to the socket. */
+static int
+sockopt_broadcast (int sock)
+{
+  int ret;
+  int on = 1;
+
+  ret = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof on);
+  if (ret < 0)
+    {
+      zlog_warn ("can't set sockopt SO_BROADCAST to socket %d", sock);
+      return -1;
+    }
+  return 0;
+}
+
+static int
+rip_route_rte (struct rip_info *rinfo)
+{
+  return (rinfo->type == ZEBRA_ROUTE_RIP && rinfo->sub_type == RIP_ROUTE_RTE);
+}
+
+static struct rip_info *
+rip_info_new (void)
+{
+  return XCALLOC (MTYPE_RIP_INFO, sizeof (struct rip_info));
+}
+
+void
+rip_info_free (struct rip_info *rinfo)
+{
+  XFREE (MTYPE_RIP_INFO, rinfo);
+}
+
+/* RIP route garbage collect timer. */
+static int
+rip_garbage_collect (struct thread *t)
+{
+  struct rip_info *rinfo;
+  struct route_node *rp;
+
+  rinfo = THREAD_ARG (t);
+  rinfo->t_garbage_collect = NULL;
+
+  /* Off timeout timer. */
+  RIP_TIMER_OFF (rinfo->t_timeout);
+  
+  /* Get route_node pointer. */
+  rp = rinfo->rp;
+
+  /* Unlock route_node. */
+  listnode_delete (rp->info, rinfo);
+  if (list_isempty ((struct list *)rp->info))
+    {
+      list_free (rp->info);
+      rp->info = NULL;
+      route_unlock_node (rp);
+    }
+
+  /* Free RIP routing information. */
+  rip_info_free (rinfo);
+
+  return 0;
+}
+
+static void rip_timeout_update (struct rip_info *rinfo);
+
+/* Add new route to the ECMP list.
+ * RETURN: the new entry added in the list, or NULL if it is not the first
+ *         entry and ECMP is not allowed.
+ */
+struct rip_info *
+rip_ecmp_add (struct rip_info *rinfo_new)
+{
+  struct route_node *rp = rinfo_new->rp;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+
+  if (rp->info == NULL)
+    rp->info = list_new ();
+  list = (struct list *)rp->info;
+
+  /* If ECMP is not allowed and some entry already exists in the list,
+   * do nothing. */
+  if (listcount (list) && !rip->ecmp)
+    return NULL;
+
+  rinfo = rip_info_new ();
+  memcpy (rinfo, rinfo_new, sizeof (struct rip_info));
+  listnode_add (list, rinfo);
+
+  if (rip_route_rte (rinfo))
+    {
+      rip_timeout_update (rinfo);
+      rip_zebra_ipv4_add (rp);
+    }
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update (see section 2.5). */
+  rip_event (RIP_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Replace the ECMP list with the new route.
+ * RETURN: the new entry added in the list
+ */
+struct rip_info *
+rip_ecmp_replace (struct rip_info *rinfo_new)
+{
+  struct route_node *rp = rinfo_new->rp;
+  struct list *list = (struct list *)rp->info;
+  struct rip_info *rinfo = NULL, *tmp_rinfo = NULL;
+  struct listnode *node = NULL, *nextnode = NULL;
+
+  if (list == NULL || listcount (list) == 0)
+    return rip_ecmp_add (rinfo_new);
+
+  /* Get the first entry */
+  rinfo = listgetdata (listhead (list));
+
+  /* Learnt route replaced by a local one. Delete it from zebra. */
+  if (rip_route_rte (rinfo) && !rip_route_rte (rinfo_new))
+    if (CHECK_FLAG (rinfo->flags, RIP_RTF_FIB))
+      rip_zebra_ipv4_delete (rp);
+
+  /* Re-use the first entry, and delete the others. */
+  for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo))
+    if (tmp_rinfo != rinfo)
+      {
+        RIP_TIMER_OFF (tmp_rinfo->t_timeout);
+        RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect);
+        list_delete_node (list, node);
+        rip_info_free (tmp_rinfo);
+      }
+
+  RIP_TIMER_OFF (rinfo->t_timeout);
+  RIP_TIMER_OFF (rinfo->t_garbage_collect);
+  memcpy (rinfo, rinfo_new, sizeof (struct rip_info));
+
+  if (rip_route_rte (rinfo))
+    {
+      rip_timeout_update (rinfo);
+      /* The ADD message implies an update. */
+      rip_zebra_ipv4_add (rp);
+    }
+
+  /* Set the route change flag. */
+  SET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update (see section 2.5). */
+  rip_event (RIP_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Delete one route from the ECMP list.
+ * RETURN:
+ *  null - the entry is freed, and other entries exist in the list
+ *  the entry - the entry is the last one in the list; its metric is set
+ *              to INFINITY, and the garbage collector is started for it
+ */
+struct rip_info *
+rip_ecmp_delete (struct rip_info *rinfo)
+{
+  struct route_node *rp = rinfo->rp;
+  struct list *list = (struct list *)rp->info;
+
+  RIP_TIMER_OFF (rinfo->t_timeout);
+
+  if (listcount (list) > 1)
+    {
+      /* Some other ECMP entries still exist. Just delete this entry. */
+      RIP_TIMER_OFF (rinfo->t_garbage_collect);
+      listnode_delete (list, rinfo);
+      if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB))
+        /* The ADD message implies the update. */
+        rip_zebra_ipv4_add (rp);
+      rip_info_free (rinfo);
+      rinfo = NULL;
+    }
+  else
+    {
+      assert (rinfo == listgetdata (listhead (list)));
+
+      /* This is the only entry left in the list. We must keep it in
+       * the list for garbage collection time, with INFINITY metric. */
+
+      rinfo->metric = RIP_METRIC_INFINITY;
+      RIP_TIMER_ON (rinfo->t_garbage_collect,
+                    rip_garbage_collect, rip->garbage_time);
+
+      if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB))
+        rip_zebra_ipv4_delete (rp);
+    }
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update (see section 2.5). */
+  rip_event (RIP_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Timeout RIP routes. */
+static int
+rip_timeout (struct thread *t)
+{
+  rip_ecmp_delete ((struct rip_info *)THREAD_ARG (t));
+  return 0;
+}
+
+static void
+rip_timeout_update (struct rip_info *rinfo)
+{
+  if (rinfo->metric != RIP_METRIC_INFINITY)
+    {
+      RIP_TIMER_OFF (rinfo->t_timeout);
+      RIP_TIMER_ON (rinfo->t_timeout, rip_timeout, rip->timeout_time);
+    }
+}
+
+static int
+rip_filter (int rip_distribute, struct prefix_ipv4 *p, struct rip_interface *ri)
+{
+  struct distribute *dist;
+  struct access_list *alist;
+  struct prefix_list *plist;
+  int distribute = rip_distribute == RIP_FILTER_OUT ?
+      DISTRIBUTE_V4_OUT : DISTRIBUTE_V4_IN;
+  const char *inout = rip_distribute == RIP_FILTER_OUT ? "out" : "in";
+
+  /* Input distribute-list filtering. */
+  if (ri->list[rip_distribute])
+    {
+      if (access_list_apply (ri->list[rip_distribute],
+                            (struct prefix *) p) == FILTER_DENY)
+       {
+         if (IS_RIP_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by distribute %s",
+                        inet_ntoa (p->prefix), p->prefixlen, inout);
+         return -1;
+       }
+    }
+  if (ri->prefix[rip_distribute])
+    {
+      if (prefix_list_apply (ri->prefix[rip_distribute],
+                            (struct prefix *) p) == PREFIX_DENY)
+       {
+         if (IS_RIP_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by prefix-list %s",
+                        inet_ntoa (p->prefix), p->prefixlen, inout);
+         return -1;
+       }
+    }
+
+  /* All interface filter check. */
+  dist = distribute_lookup (NULL);
+  if (dist)
+    {
+      if (dist->list[distribute])
+       {
+         alist = access_list_lookup (AFI_IP, dist->list[distribute]);
+
+         if (alist)
+           {
+             if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY)
+               {
+                 if (IS_RIP_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by distribute %s",
+                                inet_ntoa (p->prefix), p->prefixlen, inout);
+                 return -1;
+               }
+           }
+       }
+      if (dist->prefix[distribute])
+       {
+         plist = prefix_list_lookup (AFI_IP, dist->prefix[distribute]);
+
+         if (plist)
+           {
+             if (prefix_list_apply (plist,
+                                    (struct prefix *) p) == PREFIX_DENY)
+               {
+                 if (IS_RIP_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by prefix-list %s",
+                                inet_ntoa (p->prefix), p->prefixlen, inout);
+                 return -1;
+               }
+           }
+       }
+    }
+  return 0;
+}
+
+/* Check nexthop address validity. */
+static int
+rip_nexthop_check (struct in_addr *addr)
+{
+  struct listnode *node;
+  struct listnode *cnode;
+  struct interface *ifp;
+  struct connected *ifc;
+  struct prefix *p;
+
+  /* If nexthop address matches local configured address then it is
+     invalid nexthop. */
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc))
+       {           
+         p = ifc->address;
+
+         if (p->family == AF_INET
+             && IPV4_ADDR_SAME (&p->u.prefix4, addr))
+           return -1;
+       }
+    }
+  return 0;
+}
+
+/* RIP add route to routing table. */
+static void
+rip_rte_process (struct rte *rte, struct sockaddr_in *from,
+                 struct interface *ifp)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_node *rp;
+  struct rip_info *rinfo = NULL, newinfo;
+  struct rip_interface *ri;
+  struct in_addr *nexthop;
+  int same = 0;
+  unsigned char old_dist, new_dist;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
+
+  /* Make prefix structure. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefix = rte->prefix;
+  p.prefixlen = ip_masklen (rte->mask);
+
+  /* Make sure mask is applied. */
+  apply_mask_ipv4 (&p);
+
+  /* Apply input filters. */
+  ri = ifp->info;
+
+  ret = rip_filter (RIP_FILTER_IN, &p, ri);
+  if (ret < 0)
+    return;
+
+  memset (&newinfo, 0, sizeof (newinfo));
+  newinfo.type = ZEBRA_ROUTE_RIP;
+  newinfo.sub_type = RIP_ROUTE_RTE;
+  newinfo.nexthop = rte->nexthop;
+  newinfo.from = from->sin_addr;
+  newinfo.ifindex = ifp->ifindex;
+  newinfo.metric = rte->metric;
+  newinfo.metric_out = rte->metric; /* XXX */
+  newinfo.tag = ntohs (rte->tag);   /* XXX */
+
+  /* Modify entry according to the interface routemap. */
+  if (ri->routemap[RIP_FILTER_IN])
+    {
+      int ret;
+
+      /* The object should be of the type of rip_info */
+      ret = route_map_apply (ri->routemap[RIP_FILTER_IN],
+                             (struct prefix *) &p, RMAP_RIP, &newinfo);
+
+      if (ret == RMAP_DENYMATCH)
+        {
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIP %s/%d is filtered by route-map in",
+                       inet_ntoa (p.prefix), p.prefixlen);
+          return;
+        }
+
+      /* Get back the object */
+      rte->nexthop = newinfo.nexthop_out;
+      rte->tag = htons (newinfo.tag_out);       /* XXX */
+      rte->metric = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */
+    }
+
+  /* Once the entry has been validated, update the metric by
+     adding the cost of the network on wich the message
+     arrived. If the result is greater than infinity, use infinity
+     (RFC2453 Sec. 3.9.2) */
+  /* Zebra ripd can handle offset-list in. */
+  ret = rip_offset_list_apply_in (&p, ifp, &rte->metric);
+
+  /* If offset-list does not modify the metric use interface's
+     metric. */
+  if (!ret)
+    rte->metric += ifp->metric ? ifp->metric : 1;
+
+  if (rte->metric > RIP_METRIC_INFINITY)
+    rte->metric = RIP_METRIC_INFINITY;
+
+  /* Set nexthop pointer. */
+  if (rte->nexthop.s_addr == 0)
+    nexthop = &from->sin_addr;
+  else
+    nexthop = &rte->nexthop;
+
+  /* Check if nexthop address is myself, then do nothing. */
+  if (rip_nexthop_check (nexthop) < 0)
+    {
+      if (IS_RIP_DEBUG_PACKET)
+        zlog_debug ("Nexthop address %s is myself", inet_ntoa (*nexthop));
+      return;
+    }
+
+  /* Get index for the prefix. */
+  rp = route_node_get (rip->table, (struct prefix *) &p);
+
+  newinfo.rp = rp;
+  newinfo.nexthop = *nexthop;
+  newinfo.metric = rte->metric;
+  newinfo.tag = ntohs (rte->tag);
+  newinfo.distance = rip_distance_apply (&newinfo);
+
+  new_dist = newinfo.distance ? newinfo.distance : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+  /* Check to see whether there is already RIP route on the table. */
+  if ((list = rp->info) != NULL)
+    for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+      {
+        /* Need to compare with redistributed entry or local entry */
+        if (!rip_route_rte (rinfo))
+          break;
+
+        if (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) &&
+            IPV4_ADDR_SAME (&rinfo->nexthop, nexthop))
+          break;
+
+        if (!listnextnode (node))
+          {
+            /* Not found in the list */
+
+            if (rte->metric > rinfo->metric)
+              {
+                /* New route has a greater metric. Discard it. */
+                route_unlock_node (rp);
+                return;
+              }
+
+            if (rte->metric < rinfo->metric)
+              /* New route has a smaller metric. Replace the ECMP list
+               * with the new one in below. */
+              break;
+
+            /* Metrics are same. We compare the distances. */
+            old_dist = rinfo->distance ? \
+                       rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+            if (new_dist > old_dist)
+              {
+                /* New route has a greater distance. Discard it. */
+                route_unlock_node (rp);
+                return;
+              }
+
+            if (new_dist < old_dist)
+              /* New route has a smaller distance. Replace the ECMP list
+               * with the new one in below. */
+              break;
+
+            /* Metrics and distances are both same. Keep "rinfo" null and
+             * the new route is added in the ECMP list in below. */
+          }
+      }
+
+  if (rinfo)
+    {
+      /* Local static route. */
+      if (rinfo->type == ZEBRA_ROUTE_RIP
+          && ((rinfo->sub_type == RIP_ROUTE_STATIC) ||
+              (rinfo->sub_type == RIP_ROUTE_DEFAULT))
+          && rinfo->metric != RIP_METRIC_INFINITY)
+        {
+          route_unlock_node (rp);
+          return;
+        }
+
+      /* Redistributed route check. */
+      if (rinfo->type != ZEBRA_ROUTE_RIP
+          && rinfo->metric != RIP_METRIC_INFINITY)
+        {
+          old_dist = rinfo->distance;
+          /* Only routes directly connected to an interface (nexthop == 0)
+          * may have a valid NULL distance */
+          if (rinfo->nexthop.s_addr != 0)
+            old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT;
+          /* If imported route does not have STRICT precedence, 
+             mark it as a ghost */
+          if (new_dist <= old_dist && rte->metric != RIP_METRIC_INFINITY)
+            rip_ecmp_replace (&newinfo);
+
+          route_unlock_node (rp);
+          return;
+        }
+    }
+
+  if (!rinfo)
+    {
+      if (rp->info)
+        route_unlock_node (rp);
+
+      /* Now, check to see whether there is already an explicit route
+         for the destination prefix.  If there is no such route, add
+         this route to the routing table, unless the metric is
+         infinity (there is no point in adding a route which
+         unusable). */
+      if (rte->metric != RIP_METRIC_INFINITY)
+        rip_ecmp_add (&newinfo);
+    }
+  else
+    {
+      /* Route is there but we are not sure the route is RIP or not. */
+
+      /* If there is an existing route, compare the next hop address
+         to the address of the router from which the datagram came.
+         If this datagram is from the same router as the existing
+         route, reinitialize the timeout.  */
+      same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr)
+              && (rinfo->ifindex == ifp->ifindex));
+
+      old_dist = rinfo->distance ? \
+                 rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT;
+
+      /* Next, compare the metrics.  If the datagram is from the same
+         router as the existing route, and the new metric is different
+         than the old one; or, if the new metric is lower than the old
+         one, or if the tag has been changed; or if there is a route
+         with a lower administrave distance; or an update of the
+         distance on the actual route; do the following actions: */
+      if ((same && rinfo->metric != rte->metric)
+          || (rte->metric < rinfo->metric)
+          || ((same)
+              && (rinfo->metric == rte->metric)
+              && (newinfo.tag != rinfo->tag))
+          || (old_dist > new_dist)
+          || ((old_dist != new_dist) && same))
+        {
+          if (listcount (list) == 1)
+            {
+              if (newinfo.metric != RIP_METRIC_INFINITY)
+                rip_ecmp_replace (&newinfo);
+              else
+                rip_ecmp_delete (rinfo);
+            }
+          else
+            {
+              if (newinfo.metric < rinfo->metric)
+                rip_ecmp_replace (&newinfo);
+              else if (newinfo.metric > rinfo->metric)
+                rip_ecmp_delete (rinfo);
+              else if (new_dist < old_dist)
+                rip_ecmp_replace (&newinfo);
+              else if (new_dist > old_dist)
+                rip_ecmp_delete (rinfo);
+              else
+                {
+                  int update = CHECK_FLAG (rinfo->flags, RIP_RTF_FIB) ? 1 : 0;
+
+                  assert (newinfo.metric != RIP_METRIC_INFINITY);
+
+                  RIP_TIMER_OFF (rinfo->t_timeout);
+                  RIP_TIMER_OFF (rinfo->t_garbage_collect);
+                  memcpy (rinfo, &newinfo, sizeof (struct rip_info));
+                  rip_timeout_update (rinfo);
+
+                  if (update)
+                    rip_zebra_ipv4_add (rp);
+
+                  /* - Set the route change flag on the first entry. */
+                  rinfo = listgetdata (listhead (list));
+                  SET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+                  rip_event (RIP_TRIGGERED_UPDATE, 0);
+                }
+            }
+        }
+      else /* same & no change */
+        rip_timeout_update (rinfo);
+
+      /* Unlock tempolary lock of the route. */
+      route_unlock_node (rp);
+    }
+}
+
+/* Dump RIP packet */
+static void
+rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv)
+{
+  caddr_t lim;
+  struct rte *rte;
+  const char *command_str;
+  char pbuf[BUFSIZ], nbuf[BUFSIZ];
+  u_char netmask = 0;
+  u_char *p;
+
+  /* Set command string. */
+  if (packet->command > 0 && packet->command < RIP_COMMAND_MAX)
+    command_str = lookup (rip_msg, packet->command);
+  else
+    command_str = "unknown";
+
+  /* Dump packet header. */
+  zlog_debug ("%s %s version %d packet size %d",
+            sndrcv, command_str, packet->version, size);
+
+  /* Dump each routing table entry. */
+  rte = packet->rte;
+  
+  for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
+    {
+      if (packet->version == RIPv2)
+       {
+         netmask = ip_masklen (rte->mask);
+
+          if (rte->family == htons (RIP_FAMILY_AUTH))
+            {
+              if (rte->tag == htons (RIP_AUTH_SIMPLE_PASSWORD))
+               {
+                 p = (u_char *)&rte->prefix;
+
+                 zlog_debug ("  family 0x%X type %d auth string: %s",
+                            ntohs (rte->family), ntohs (rte->tag), p);
+               }
+              else if (rte->tag == htons (RIP_AUTH_MD5))
+               {
+                 struct rip_md5_info *md5;
+
+                 md5 = (struct rip_md5_info *) &packet->rte;
+
+                 zlog_debug ("  family 0x%X type %d (MD5 authentication)",
+                            ntohs (md5->family), ntohs (md5->type));
+                 zlog_debug ("    RIP-2 packet len %d Key ID %d"
+                             " Auth Data len %d",
+                             ntohs (md5->packet_len), md5->keyid,
+                             md5->auth_len);
+                  zlog_debug ("    Sequence Number %ld",
+                             (u_long) ntohl (md5->sequence));
+               }
+              else if (rte->tag == htons (RIP_AUTH_DATA))
+               {
+                 p = (u_char *)&rte->prefix;
+
+                 zlog_debug ("  family 0x%X type %d (MD5 data)",
+                            ntohs (rte->family), ntohs (rte->tag));
+                 zlog_debug ("    MD5: %02X%02X%02X%02X%02X%02X%02X%02X"
+                            "%02X%02X%02X%02X%02X%02X%02X%02X",
+                             p[0], p[1], p[2], p[3], p[4], p[5], p[6],
+                             p[7], p[8], p[9], p[10], p[11], p[12], p[13],
+                             p[14], p[15]);
+               }
+             else
+               {
+                 zlog_debug ("  family 0x%X type %d (Unknown auth type)",
+                            ntohs (rte->family), ntohs (rte->tag));
+               }
+            }
+         else
+           zlog_debug ("  %s/%d -> %s family %d tag %d metric %ld",
+                       inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),
+                       netmask, inet_ntop (AF_INET, &rte->nexthop, nbuf,
+                                           BUFSIZ), ntohs (rte->family),
+                       ntohs (rte->tag), (u_long) ntohl (rte->metric));
+       }
+      else
+       {
+         zlog_debug ("  %s family %d tag %d metric %ld", 
+                    inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ),
+                    ntohs (rte->family), ntohs (rte->tag),
+                    (u_long)ntohl (rte->metric));
+       }
+    }
+}
+
+/* Check if the destination address is valid (unicast; not net 0
+   or 127) (RFC2453 Section 3.9.2 - Page 26).  But we don't
+   check net 0 because we accept default route. */
+static int
+rip_destination_check (struct in_addr addr)
+{
+  u_int32_t destination;
+
+  /* Convert to host byte order. */
+  destination = ntohl (addr.s_addr);
+
+  if (IPV4_NET127 (destination))
+    return 0;
+
+  /* Net 0 may match to the default route. */
+  if (IPV4_NET0 (destination) && destination != 0)
+    return 0;
+
+  /* Unicast address must belong to class A, B, C. */
+  if (IN_CLASSA (destination))
+    return 1;
+  if (IN_CLASSB (destination))
+    return 1;
+  if (IN_CLASSC (destination))
+    return 1;
+
+  return 0;
+}
+
+/* RIP version 2 authentication. */
+static int
+rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from,
+                         struct interface *ifp)
+{
+  struct rip_interface *ri;
+  char *auth_str;
+
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("RIPv2 simple password authentication from %s",
+              inet_ntoa (from->sin_addr));
+
+  ri = ifp->info;
+
+  if (ri->auth_type != RIP_AUTH_SIMPLE_PASSWORD
+      || rte->tag != htons(RIP_AUTH_SIMPLE_PASSWORD))
+    return 0;
+
+  /* Simple password authentication. */
+  if (ri->auth_str)
+    {
+      auth_str = (char *) &rte->prefix;
+         
+      if (strncmp (auth_str, ri->auth_str, 16) == 0)
+       return 1;
+    }
+  if (ri->key_chain)
+    {
+      struct keychain *keychain;
+      struct key *key;
+
+      keychain = keychain_lookup (ri->key_chain);
+      if (keychain == NULL)
+       return 0;
+
+      key = key_match_for_accept (keychain, (char *) &rte->prefix);
+      if (key)
+       return 1;
+    }
+  return 0;
+}
+
+/* RIP version 2 authentication with MD5. */
+static int
+rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from,
+              int length, struct interface *ifp)
+{
+  struct rip_interface *ri;
+  struct rip_md5_info *md5;
+  struct rip_md5_data *md5data;
+  struct keychain *keychain;
+  struct key *key;
+  MD5_CTX ctx;
+  u_char digest[RIP_AUTH_MD5_SIZE];
+  u_int16_t packet_len;
+  char auth_str[RIP_AUTH_MD5_SIZE];
+  
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("RIPv2 MD5 authentication from %s",
+               inet_ntoa (from->sin_addr));
+
+  ri = ifp->info;
+  md5 = (struct rip_md5_info *) &packet->rte;
+
+  /* Check auth type. */
+  if (ri->auth_type != RIP_AUTH_MD5 || md5->type != htons(RIP_AUTH_MD5))
+    return 0;
+
+  /* If the authentication length is less than 16, then it must be wrong for
+   * any interpretation of rfc2082. Some implementations also interpret
+   * this as RIP_HEADER_SIZE+ RIP_AUTH_MD5_SIZE, aka RIP_AUTH_MD5_COMPAT_SIZE.
+   */
+  if ( !((md5->auth_len == RIP_AUTH_MD5_SIZE)
+         || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE)))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+        zlog_debug ("RIPv2 MD5 authentication, strange authentication "
+                   "length field %d", md5->auth_len);
+    return 0;
+    }
+
+  /* grab and verify check packet length */
+  packet_len = ntohs (md5->packet_len);
+
+  if (packet_len > (length - RIP_HEADER_SIZE - RIP_AUTH_MD5_SIZE))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+        zlog_debug ("RIPv2 MD5 authentication, packet length field %d "
+                   "greater than received length %d!",
+                   md5->packet_len, length);
+      return 0;
+    }
+
+  /* retrieve authentication data */
+  md5data = (struct rip_md5_data *) (((u_char *) packet) + packet_len);
+  
+  memset (auth_str, 0, RIP_AUTH_MD5_SIZE);
+
+  if (ri->key_chain)
+    {
+      keychain = keychain_lookup (ri->key_chain);
+      if (keychain == NULL)
+       return 0;
+
+      key = key_lookup_for_accept (keychain, md5->keyid);
+      if (key == NULL)
+       return 0;
+
+      strncpy (auth_str, key->string, RIP_AUTH_MD5_SIZE);
+    }
+  else if (ri->auth_str)
+    strncpy (auth_str, ri->auth_str, RIP_AUTH_MD5_SIZE);
+
+  if (auth_str[0] == 0)
+    return 0;
+  
+  /* MD5 digest authentication. */
+  memset (&ctx, 0, sizeof(ctx));
+  MD5Init(&ctx);
+  MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE);
+  MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
+  MD5Final(digest, &ctx);
+  
+  if (memcmp (md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0)
+    return packet_len;
+  else
+    return 0;
+}
+
+/* Pick correct auth string for sends, prepare auth_str buffer for use.
+ * (left justified and padded).
+ *
+ * presumes one of ri or key is valid, and that the auth strings they point
+ * to are nul terminated. If neither are present, auth_str will be fully
+ * zero padded.
+ *
+ */
+static void
+rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, 
+                           char *auth_str, int len)
+{
+  assert (ri || key);
+
+  memset (auth_str, 0, len);
+  if (key && key->string)
+    strncpy (auth_str, key->string, len);
+  else if (ri->auth_str)
+    strncpy (auth_str, ri->auth_str, len);
+
+  return;
+}
+
+/* Write RIPv2 simple password authentication information
+ *
+ * auth_str is presumed to be 2 bytes and correctly prepared 
+ * (left justified and zero padded).
+ */
+static void
+rip_auth_simple_write (struct stream *s, char *auth_str, int len)
+{
+  assert (s && len == RIP_AUTH_SIMPLE_SIZE);
+  
+  stream_putw (s, RIP_FAMILY_AUTH);
+  stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD);
+  stream_put (s, auth_str, RIP_AUTH_SIMPLE_SIZE);
+  
+  return;
+}
+
+/* write RIPv2 MD5 "authentication header" 
+ * (uses the auth key data field)
+ *
+ * Digest offset field is set to 0.
+ *
+ * returns: offset of the digest offset field, which must be set when
+ * length to the auth-data MD5 digest is known.
+ */
+static size_t
+rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, 
+                       struct key *key)
+{
+  size_t doff = 0;
+
+  assert (s && ri && ri->auth_type == RIP_AUTH_MD5);
+
+  /* MD5 authentication. */
+  stream_putw (s, RIP_FAMILY_AUTH);
+  stream_putw (s, RIP_AUTH_MD5);
+
+  /* MD5 AH digest offset field.
+   *
+   * Set to placeholder value here, to true value when RIP-2 Packet length
+   * is known.  Actual value is set in .....().
+   */
+  doff = stream_get_endp(s);
+  stream_putw (s, 0);
+
+  /* Key ID. */
+  if (key)
+    stream_putc (s, key->index % 256);
+  else
+    stream_putc (s, 1);
+
+  /* Auth Data Len.  Set 16 for MD5 authentication data. Older ripds 
+   * however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for this
+   * to be configurable. 
+   */
+  stream_putc (s, ri->md5_auth_len);
+
+  /* Sequence Number (non-decreasing). */
+  /* RFC2080: The value used in the sequence number is
+     arbitrary, but two suggestions are the time of the
+     message's creation or a simple message counter. */
+  stream_putl (s, time (NULL));
+             
+  /* Reserved field must be zero. */
+  stream_putl (s, 0);
+  stream_putl (s, 0);
+
+  return doff;
+}
+
+/* If authentication is in used, write the appropriate header
+ * returns stream offset to which length must later be written
+ * or 0 if this is not required
+ */
+static size_t
+rip_auth_header_write (struct stream *s, struct rip_interface *ri, 
+                       struct key *key, char *auth_str, int len)
+{
+  assert (ri->auth_type != RIP_NO_AUTH);
+  
+  switch (ri->auth_type)
+    {
+      case RIP_AUTH_SIMPLE_PASSWORD:
+        rip_auth_prepare_str_send (ri, key, auth_str, len);
+        rip_auth_simple_write (s, auth_str, len);
+        return 0;
+      case RIP_AUTH_MD5:
+        return rip_auth_md5_ah_write (s, ri, key);
+    }
+  assert (1);
+  return 0;
+}
+
+/* Write RIPv2 MD5 authentication data trailer */
+static void
+rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff,
+                  char *auth_str, int authlen)
+{
+  unsigned long len;
+  MD5_CTX ctx;
+  unsigned char digest[RIP_AUTH_MD5_SIZE];
+
+  /* Make it sure this interface is configured as MD5
+     authentication. */
+  assert ((ri->auth_type == RIP_AUTH_MD5) && (authlen == RIP_AUTH_MD5_SIZE));
+  assert (doff > 0);
+  
+  /* Get packet length. */
+  len = stream_get_endp(s);
+
+  /* Check packet length. */
+  if (len < (RIP_HEADER_SIZE + RIP_RTE_SIZE))
+    {
+      zlog_err ("rip_auth_md5_set(): packet length %ld is less than minimum length.", len);
+      return;
+    }
+
+  /* Set the digest offset length in the header */
+  stream_putw_at (s, doff, len);
+  
+  /* Set authentication data. */
+  stream_putw (s, RIP_FAMILY_AUTH);
+  stream_putw (s, RIP_AUTH_DATA);
+
+  /* Generate a digest for the RIP packet. */
+  memset(&ctx, 0, sizeof(ctx));
+  MD5Init(&ctx);
+  MD5Update(&ctx, STREAM_DATA (s), stream_get_endp (s));
+  MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
+  MD5Final(digest, &ctx);
+
+  /* Copy the digest to the packet. */
+  stream_write (s, digest, RIP_AUTH_MD5_SIZE);
+}
+
+/* RIP routing information. */
+static void
+rip_response_process (struct rip_packet *packet, int size, 
+                     struct sockaddr_in *from, struct connected *ifc)
+{
+  caddr_t lim;
+  struct rte *rte;
+  struct prefix_ipv4 ifaddr;
+  struct prefix_ipv4 ifaddrclass;
+  int subnetted;
+
+  memset(&ifaddr, 0, sizeof(ifaddr));
+  /* We don't know yet. */
+  subnetted = -1;
+
+  /* The Response must be ignored if it is not from the RIP
+     port. (RFC2453 - Sec. 3.9.2)*/
+  if (from->sin_port != htons(RIP_PORT_DEFAULT))
+    {
+      zlog_info ("response doesn't come from RIP port: %d",
+                from->sin_port);
+      rip_peer_bad_packet (from);
+      return;
+    }
+
+  /* The datagram's IPv4 source address should be checked to see
+     whether the datagram is from a valid neighbor; the source of the
+     datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */
+  if (if_lookup_address(from->sin_addr) == NULL) 
+    {
+      zlog_info ("This datagram doesn't came from a valid neighbor: %s",
+                inet_ntoa (from->sin_addr));
+      rip_peer_bad_packet (from);
+      return;
+    }
+
+  /* It is also worth checking to see whether the response is from one
+     of the router's own addresses. */
+
+  ; /* Alredy done in rip_read () */
+
+  /* Update RIP peer. */
+  rip_peer_update (from, packet->version);
+
+  /* Set RTE pointer. */
+  rte = packet->rte;
+
+  for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
+    {
+      /* RIPv2 authentication check. */
+      /* If the Address Family Identifier of the first (and only the
+        first) entry in the message is 0xFFFF, then the remainder of
+        the entry contains the authentication. */
+      /* If the packet gets here it means authentication enabled */
+      /* Check is done in rip_read(). So, just skipping it */
+      if (packet->version == RIPv2 &&
+         rte == packet->rte &&
+         rte->family == htons(RIP_FAMILY_AUTH))
+       continue;
+
+      if (rte->family != htons(AF_INET))
+       {
+         /* Address family check.  RIP only supports AF_INET. */
+         zlog_info ("Unsupported family %d from %s.",
+                    ntohs (rte->family), inet_ntoa (from->sin_addr));
+         continue;
+       }
+
+      /* - is the destination address valid (e.g., unicast; not net 0
+         or 127) */
+      if (! rip_destination_check (rte->prefix))
+        {
+         zlog_info ("Network is net 0 or net 127 or it is not unicast network");
+         rip_peer_bad_route (from);
+         continue;
+       } 
+
+      /* Convert metric value to host byte order. */
+      rte->metric = ntohl (rte->metric);
+
+      /* - is the metric valid (i.e., between 1 and 16, inclusive) */
+      if (! (rte->metric >= 1 && rte->metric <= 16))
+       {
+         zlog_info ("Route's metric is not in the 1-16 range.");
+         rip_peer_bad_route (from);
+         continue;
+       }
+
+      /* RIPv1 does not have nexthop value. */
+      if (packet->version == RIPv1 && rte->nexthop.s_addr != 0)
+       {
+         zlog_info ("RIPv1 packet with nexthop value %s",
+                    inet_ntoa (rte->nexthop));
+         rip_peer_bad_route (from);
+         continue;
+       }
+
+      /* That is, if the provided information is ignored, a possibly
+        sub-optimal, but absolutely valid, route may be taken.  If
+        the received Next Hop is not directly reachable, it should be
+        treated as 0.0.0.0. */
+      if (packet->version == RIPv2 && rte->nexthop.s_addr != 0)
+       {
+         u_int32_t addrval;
+
+         /* Multicast address check. */
+         addrval = ntohl (rte->nexthop.s_addr);
+         if (IN_CLASSD (addrval))
+           {
+             zlog_info ("Nexthop %s is multicast address, skip this rte",
+                        inet_ntoa (rte->nexthop));
+             continue;
+           }
+
+         if (! if_lookup_address (rte->nexthop))
+           {
+             struct route_node *rn;
+             struct rip_info *rinfo;
+
+             rn = route_node_match_ipv4 (rip->table, &rte->nexthop);
+
+             if (rn)
+               {
+                 rinfo = rn->info;
+
+                 if (rinfo->type == ZEBRA_ROUTE_RIP
+                     && rinfo->sub_type == RIP_ROUTE_RTE)
+                   {
+                     if (IS_RIP_DEBUG_EVENT)
+                       zlog_debug ("Next hop %s is on RIP network.  Set nexthop to the packet's originator", inet_ntoa (rte->nexthop));
+                     rte->nexthop = rinfo->from;
+                   }
+                 else
+                   {
+                     if (IS_RIP_DEBUG_EVENT)
+                       zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop));
+                     rte->nexthop.s_addr = 0;
+                   }
+
+                 route_unlock_node (rn);
+               }
+             else
+               {
+                 if (IS_RIP_DEBUG_EVENT)
+                   zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop));
+                 rte->nexthop.s_addr = 0;
+               }
+
+           }
+       }
+
+     /* For RIPv1, there won't be a valid netmask.  
+
+       This is a best guess at the masks.  If everyone was using old
+       Ciscos before the 'ip subnet zero' option, it would be almost
+       right too :-)
+      
+       Cisco summarize ripv1 advertisments to the classful boundary
+       (/16 for class B's) except when the RIP packet does to inside
+       the classful network in question.  */
+
+      if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) 
+         || (packet->version == RIPv2 
+             && (rte->prefix.s_addr != 0 && rte->mask.s_addr == 0)))
+       {
+         u_int32_t destination;
+
+         if (subnetted == -1)
+            {
+              memcpy (&ifaddr, ifc->address, sizeof (struct prefix_ipv4));
+              memcpy (&ifaddrclass, &ifaddr, sizeof (struct prefix_ipv4));
+              apply_classful_mask_ipv4 (&ifaddrclass);
+              subnetted = 0;
+              if (ifaddr.prefixlen > ifaddrclass.prefixlen)
+                subnetted = 1;
+            }
+
+         destination = ntohl (rte->prefix.s_addr);
+
+         if (IN_CLASSA (destination))
+             masklen2ip (8, &rte->mask);
+         else if (IN_CLASSB (destination))
+             masklen2ip (16, &rte->mask);
+         else if (IN_CLASSC (destination))
+             masklen2ip (24, &rte->mask);
+
+         if (subnetted == 1)
+           masklen2ip (ifaddrclass.prefixlen,
+                       (struct in_addr *) &destination);
+         if ((subnetted == 1) && ((rte->prefix.s_addr & destination) ==
+             ifaddrclass.prefix.s_addr))
+           {
+             masklen2ip (ifaddr.prefixlen, &rte->mask);
+             if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)
+               masklen2ip (32, &rte->mask);
+             if (IS_RIP_DEBUG_EVENT)
+               zlog_debug ("Subnetted route %s", inet_ntoa (rte->prefix));
+           }
+         else
+           {
+             if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)
+               continue;
+           }
+
+         if (IS_RIP_DEBUG_EVENT)
+           {
+             zlog_debug ("Resultant route %s", inet_ntoa (rte->prefix));
+             zlog_debug ("Resultant mask %s", inet_ntoa (rte->mask));
+           }
+       }
+
+      /* In case of RIPv2, if prefix in RTE is not netmask applied one
+         ignore the entry.  */
+      if ((packet->version == RIPv2) 
+         && (rte->mask.s_addr != 0) 
+         && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr))
+       {
+         zlog_warn ("RIPv2 address %s is not mask /%d applied one",
+                    inet_ntoa (rte->prefix), ip_masklen (rte->mask));
+         rip_peer_bad_route (from);
+         continue;
+       }
+      
+      /* Default route sanity check */
+      if (packet->version == RIPv2
+          && (rte->mask.s_addr == 0)
+          && (rte->prefix.s_addr != 0))
+        {
+          if (IS_RIP_DEBUG_EVENT)
+            zlog_warn ("Malformed route, zero netmask "
+                       "with non-zero addr - dropping route!");
+          rip_peer_bad_route (from);
+          continue;
+        }
+      
+      /* Routing table updates. */
+      rip_rte_process (rte, from, ifc->ifp);
+    }
+}
+
+/* Make socket for RIP protocol. */
+static int 
+rip_create_socket (struct sockaddr_in *from)
+{
+  int ret;
+  int sock;
+  struct sockaddr_in addr;
+  
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  
+  if (!from)
+    {
+      addr.sin_family = AF_INET;
+      addr.sin_addr.s_addr = INADDR_ANY;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+      addr.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+    } else {
+      memcpy(&addr, from, sizeof(addr));
+    }
+  
+  /* sending port must always be the RIP port */
+  addr.sin_port = htons (RIP_PORT_DEFAULT);
+  
+  /* Make datagram socket. */
+  sock = socket (AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) 
+    {
+      zlog_err("Cannot create UDP socket: %s", safe_strerror(errno));
+      exit (1);
+    }
+
+  sockopt_broadcast (sock);
+  sockopt_reuseaddr (sock);
+  sockopt_reuseport (sock);
+#ifdef RIP_RECVMSG
+  setsockopt_pktinfo (sock);
+#endif /* RIP_RECVMSG */
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL);
+#endif
+
+  if (ripd_privs.change (ZPRIVS_RAISE))
+      zlog_err ("rip_create_socket: could not raise privs");
+  setsockopt_so_recvbuf (sock, RIP_UDP_RCV_BUF);
+  if ( (ret = bind (sock, (struct sockaddr *) & addr, sizeof (addr))) < 0)
+  
+    {
+      int save_errno = errno;
+      if (ripd_privs.change (ZPRIVS_LOWER))
+        zlog_err ("rip_create_socket: could not lower privs");
+      
+      zlog_err("%s: Can't bind socket %d to %s port %d: %s", __func__,
+              sock, inet_ntoa(addr.sin_addr), 
+              (int) ntohs(addr.sin_port), 
+              safe_strerror(save_errno));
+      
+      close (sock);
+      return ret;
+    }
+  
+  if (ripd_privs.change (ZPRIVS_LOWER))
+      zlog_err ("rip_create_socket: could not lower privs");
+      
+  return sock;
+}
+
+/* RIP packet send to destination address, on interface denoted by
+ * by connected argument. NULL to argument denotes destination should be
+ * should be RIP multicast group
+ */
+static int
+rip_send_packet (u_char * buf, int size, struct sockaddr_in *to,
+                 struct connected *ifc)
+{
+  int ret, send_sock;
+  struct sockaddr_in sin;
+  
+  assert (ifc != NULL);
+  
+  if (IS_RIP_DEBUG_PACKET)
+    {
+#define ADDRESS_SIZE 20
+      char dst[ADDRESS_SIZE];
+      dst[ADDRESS_SIZE - 1] = '\0';
+      
+      if (to)
+        {
+          strncpy (dst, inet_ntoa(to->sin_addr), ADDRESS_SIZE - 1);
+        }
+      else
+        {
+          sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
+          strncpy (dst, inet_ntoa(sin.sin_addr), ADDRESS_SIZE - 1);
+        }
+#undef ADDRESS_SIZE
+      zlog_debug("rip_send_packet %s > %s (%s)",
+                inet_ntoa(ifc->address->u.prefix4),
+                dst, ifc->ifp->name);
+    }
+  
+  if ( CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY) )
+    {
+      /*
+       * ZEBRA_IFA_SECONDARY is set on linux when an interface is configured
+       * with multiple addresses on the same subnet: the first address
+       * on the subnet is configured "primary", and all subsequent addresses
+       * on that subnet are treated as "secondary" addresses. 
+       * In order to avoid routing-table bloat on other rip listeners, 
+       * we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs.
+       * XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY
+       * flag is set, we would end up sending a packet for a "secondary"
+       * source address on non-linux systems.  
+       */
+      if (IS_RIP_DEBUG_PACKET)
+        zlog_debug("duplicate dropped");
+      return 0;
+    }
+
+  /* Make destination address. */
+  memset (&sin, 0, sizeof (struct sockaddr_in));
+  sin.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sin.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  /* When destination is specified, use it's port and address. */
+  if (to)
+    {
+      sin.sin_port = to->sin_port;
+      sin.sin_addr = to->sin_addr;
+      send_sock = rip->sock;
+    }
+  else
+    {
+      struct sockaddr_in from;
+      
+      sin.sin_port = htons (RIP_PORT_DEFAULT);
+      sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
+      
+      /* multicast send should bind to local interface address */
+      memset (&from, 0, sizeof (from));
+      from.sin_family = AF_INET;
+      from.sin_port = htons (RIP_PORT_DEFAULT);
+      from.sin_addr = ifc->address->u.prefix4;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+      from.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+      
+      /*
+       * we have to open a new socket for each packet because this
+       * is the most portable way to bind to a different source
+       * ipv4 address for each packet. 
+       */
+      if ( (send_sock = rip_create_socket (&from)) < 0)
+        {
+          zlog_warn("rip_send_packet could not create socket.");
+          return -1;
+        }
+      rip_interface_multicast_set (send_sock, ifc);
+    }
+
+  ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin,
+               sizeof (struct sockaddr_in));
+
+  if (IS_RIP_DEBUG_EVENT)
+      zlog_debug ("SEND to  %s.%d", inet_ntoa(sin.sin_addr), 
+                  ntohs (sin.sin_port));
+
+  if (ret < 0)
+    zlog_warn ("can't send packet : %s", safe_strerror (errno));
+
+  if (!to)
+    close(send_sock);
+
+  return ret;
+}
+
+/* Add redistributed route to RIP table. */
+void
+rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, 
+                     ifindex_t ifindex, struct in_addr *nexthop,
+                      unsigned int metric, unsigned char distance,
+                      route_tag_t tag)
+{
+  int ret;
+  struct route_node *rp = NULL;
+  struct rip_info *rinfo = NULL, newinfo;
+  struct list *list = NULL;
+
+  /* Redistribute route  */
+  ret = rip_destination_check (p->prefix);
+  if (! ret)
+    return;
+
+  rp = route_node_get (rip->table, (struct prefix *) p);
+
+  memset (&newinfo, 0, sizeof (struct rip_info));
+  newinfo.type = type;
+  newinfo.sub_type = sub_type;
+  newinfo.ifindex = ifindex;
+  newinfo.metric = 1;
+  newinfo.external_metric = metric;
+  newinfo.distance = distance;
+  if (tag <= UINT16_MAX) /* RIP only supports 16 bit tags */
+    newinfo.tag = tag;
+  newinfo.rp = rp;
+  if (nexthop)
+    newinfo.nexthop = *nexthop;
+
+  if ((list = rp->info) != NULL && listcount (list) != 0)
+    {
+      rinfo = listgetdata (listhead (list));
+
+      if (rinfo->type == ZEBRA_ROUTE_CONNECT 
+         && rinfo->sub_type == RIP_ROUTE_INTERFACE
+         && rinfo->metric != RIP_METRIC_INFINITY)
+       {
+         route_unlock_node (rp);
+         return;
+       }
+
+      /* Manually configured RIP route check. */
+      if (rinfo->type == ZEBRA_ROUTE_RIP 
+         && ((rinfo->sub_type == RIP_ROUTE_STATIC) ||
+             (rinfo->sub_type == RIP_ROUTE_DEFAULT)) )
+       {
+         if (type != ZEBRA_ROUTE_RIP || ((sub_type != RIP_ROUTE_STATIC) &&
+                                         (sub_type != RIP_ROUTE_DEFAULT)))
+           {
+             route_unlock_node (rp);
+             return;
+           }
+       }
+
+      rinfo = rip_ecmp_replace (&newinfo);
+      route_unlock_node (rp);
+    }
+  else
+    rinfo = rip_ecmp_add (&newinfo);
+
+  if (IS_RIP_DEBUG_EVENT) {
+    if (!nexthop)
+      zlog_debug ("Redistribute new prefix %s/%d on the interface %s",
+                 inet_ntoa(p->prefix), p->prefixlen,
+                 ifindex2ifname(ifindex));
+    else
+      zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s",
+                 inet_ntoa(p->prefix), p->prefixlen, inet_ntoa(rinfo->nexthop),
+                 ifindex2ifname(ifindex));
+  }
+
+  rip_event (RIP_TRIGGERED_UPDATE, 0);
+}
+
+/* Delete redistributed route from RIP table. */
+void
+rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, 
+                        ifindex_t ifindex)
+{
+  int ret;
+  struct route_node *rp;
+  struct rip_info *rinfo;
+
+  ret = rip_destination_check (p->prefix);
+  if (! ret)
+    return;
+
+  rp = route_node_lookup (rip->table, (struct prefix *) p);
+  if (rp)
+    {
+      struct list *list = rp->info;
+
+      if (list != NULL && listcount (list) != 0)
+        {
+          rinfo = listgetdata (listhead (list));
+          if (rinfo != NULL
+              && rinfo->type == type
+              && rinfo->sub_type == sub_type
+              && rinfo->ifindex == ifindex)
+            {
+              /* Perform poisoned reverse. */
+              rinfo->metric = RIP_METRIC_INFINITY;
+              RIP_TIMER_ON (rinfo->t_garbage_collect,
+                            rip_garbage_collect, rip->garbage_time);
+              RIP_TIMER_OFF (rinfo->t_timeout);
+              rinfo->flags |= RIP_RTF_CHANGED;
+
+              if (IS_RIP_DEBUG_EVENT)
+                zlog_debug ("Poisone %s/%d on the interface %s with an "
+                            "infinity metric [delete]",
+                            inet_ntoa(p->prefix), p->prefixlen,
+                            ifindex2ifname(ifindex));
+
+              rip_event (RIP_TRIGGERED_UPDATE, 0);
+            }
+        }
+      route_unlock_node (rp);
+    }
+}
+
+/* Response to request called from rip_read ().*/
+static void
+rip_request_process (struct rip_packet *packet, int size, 
+                    struct sockaddr_in *from, struct connected *ifc)
+{
+  caddr_t lim;
+  struct rte *rte;
+  struct prefix_ipv4 p;
+  struct route_node *rp;
+  struct rip_info *rinfo;
+  struct rip_interface *ri;
+
+  /* Does not reponse to the requests on the loopback interfaces */
+  if (if_is_loopback (ifc->ifp))
+    return;
+
+  /* Check RIP process is enabled on this interface. */
+  ri = ifc->ifp->info;
+  if (! ri->running)
+    return;
+
+  /* When passive interface is specified, suppress responses */
+  if (ri->passive)
+    return;
+  
+  /* RIP peer update. */
+  rip_peer_update (from, packet->version);
+
+  lim = ((caddr_t) packet) + size;
+  rte = packet->rte;
+
+  /* The Request is processed entry by entry.  If there are no
+     entries, no response is given. */
+  if (lim == (caddr_t) rte)
+    return;
+
+  /* There is one special case.  If there is exactly one entry in the
+     request, and it has an address family identifier of zero and a
+     metric of infinity (i.e., 16), then this is a request to send the
+     entire routing table. */
+  if (lim == ((caddr_t) (rte + 1)) &&
+      ntohs (rte->family) == 0 &&
+      ntohl (rte->metric) == RIP_METRIC_INFINITY)
+    {  
+      /* All route with split horizon */
+      rip_output_process (ifc, from, rip_all_route, packet->version);
+    }
+  else
+    {
+      /* Examine the list of RTEs in the Request one by one.  For each
+        entry, look up the destination in the router's routing
+        database and, if there is a route, put that route's metric in
+        the metric field of the RTE.  If there is no explicit route
+        to the specified destination, put infinity in the metric
+        field.  Once all the entries have been filled in, change the
+        command from Request to Response and send the datagram back
+        to the requestor. */
+      p.family = AF_INET;
+
+      for (; ((caddr_t) rte) < lim; rte++)
+       {
+         p.prefix = rte->prefix;
+         p.prefixlen = ip_masklen (rte->mask);
+         apply_mask_ipv4 (&p);
+         
+         rp = route_node_lookup (rip->table, (struct prefix *) &p);
+         if (rp)
+           {
+             rinfo = listgetdata (listhead ((struct list *)rp->info));
+             rte->metric = htonl (rinfo->metric);
+             route_unlock_node (rp);
+           }
+         else
+           rte->metric = htonl (RIP_METRIC_INFINITY);
+       }
+      packet->command = RIP_RESPONSE;
+
+      rip_send_packet ((u_char *)packet, size, from, ifc);
+    }
+  rip_global_queries++;
+}
+
+#if RIP_RECVMSG
+/* Set IPv6 packet info to the socket. */
+static int
+setsockopt_pktinfo (int sock)
+{
+  int ret;
+  int val = 1;
+    
+  ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
+  if (ret < 0)
+    zlog_warn ("Can't setsockopt IP_PKTINFO : %s", safe_strerror (errno));
+  return ret;
+}
+
+/* Read RIP packet by recvmsg function. */
+int
+rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from,
+            ifindex_t *ifindex)
+{
+  int ret;
+  struct msghdr msg;
+  struct iovec iov;
+  struct cmsghdr *ptr;
+  char adata[1024];
+
+  msg.msg_name = (void *) from;
+  msg.msg_namelen = sizeof (struct sockaddr_in);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = sizeof adata;
+  iov.iov_base = buf;
+  iov.iov_len = size;
+
+  ret = recvmsg (sock, &msg, 0);
+  if (ret < 0)
+    return ret;
+
+  for (ptr = ZCMSG_FIRSTHDR(&msg); ptr != NULL; ptr = CMSG_NXTHDR(&msg, ptr))
+    if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO) 
+      {
+       struct in_pktinfo *pktinfo;
+       int i;
+
+       pktinfo = (struct in_pktinfo *) CMSG_DATA (ptr);
+       i = pktinfo->ipi_ifindex;
+      }
+  return ret;
+}
+
+/* RIP packet read function. */
+int
+rip_read_new (struct thread *t)
+{
+  int ret;
+  int sock;
+  char buf[RIP_PACKET_MAXSIZ];
+  struct sockaddr_in from;
+  ifindex_t ifindex;
+  
+  /* Fetch socket then register myself. */
+  sock = THREAD_FD (t);
+  rip_event (RIP_READ, sock);
+
+  /* Read RIP packet. */
+  ret = rip_recvmsg (sock, buf, RIP_PACKET_MAXSIZ, &from, (int *)&ifindex);
+  if (ret < 0)
+    {
+      zlog_warn ("Can't read RIP packet: %s", safe_strerror (errno));
+      return ret;
+    }
+
+  return ret;
+}
+#endif /* RIP_RECVMSG */
+
+/* First entry point of RIP packet. */
+static int
+rip_read (struct thread *t)
+{
+  int sock;
+  int ret;
+  int rtenum;
+  union rip_buf rip_buf;
+  struct rip_packet *packet;
+  struct sockaddr_in from;
+  int len;
+  int vrecv;
+  socklen_t fromlen;
+  struct interface *ifp;
+  struct connected *ifc;
+  struct rip_interface *ri;
+
+  /* Fetch socket then register myself. */
+  sock = THREAD_FD (t);
+  rip->t_read = NULL;
+
+  /* Add myself to tne next event */
+  rip_event (RIP_READ, sock);
+
+  /* RIPd manages only IPv4. */
+  memset (&from, 0, sizeof (struct sockaddr_in));
+  fromlen = sizeof (struct sockaddr_in);
+
+  len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0, 
+                 (struct sockaddr *) &from, &fromlen);
+  if (len < 0) 
+    {
+      zlog_info ("recvfrom failed: %s", safe_strerror (errno));
+      return len;
+    }
+
+  /* Check is this packet comming from myself? */
+  if (if_check_address (from.sin_addr)) 
+    {
+      if (IS_RIP_DEBUG_PACKET)
+       zlog_debug ("ignore packet comes from myself");
+      return -1;
+    }
+
+  /* Which interface is this packet comes from. */
+  ifp = if_lookup_address (from.sin_addr);
+  
+  /* RIP packet received */
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("RECV packet from %s port %d on %s",
+              inet_ntoa (from.sin_addr), ntohs (from.sin_port),
+              ifp ? ifp->name : "unknown");
+
+  /* If this packet come from unknown interface, ignore it. */
+  if (ifp == NULL)
+    {
+      zlog_info ("rip_read: cannot find interface for packet from %s port %d",
+                inet_ntoa(from.sin_addr), ntohs (from.sin_port));
+      return -1;
+    }
+  
+  ifc = connected_lookup_address (ifp, from.sin_addr);
+  
+  if (ifc == NULL)
+    {
+      zlog_info ("rip_read: cannot find connected address for packet from %s "
+                "port %d on interface %s",
+                inet_ntoa(from.sin_addr), ntohs (from.sin_port), ifp->name);
+      return -1;
+    }
+
+  /* Packet length check. */
+  if (len < RIP_PACKET_MINSIZ)
+    {
+      zlog_warn ("packet size %d is smaller than minimum size %d",
+                len, RIP_PACKET_MINSIZ);
+      rip_peer_bad_packet (&from);
+      return len;
+    }
+  if (len > RIP_PACKET_MAXSIZ)
+    {
+      zlog_warn ("packet size %d is larger than max size %d",
+                len, RIP_PACKET_MAXSIZ);
+      rip_peer_bad_packet (&from);
+      return len;
+    }
+
+  /* Packet alignment check. */
+  if ((len - RIP_PACKET_MINSIZ) % 20)
+    {
+      zlog_warn ("packet size %d is wrong for RIP packet alignment", len);
+      rip_peer_bad_packet (&from);
+      return len;
+    }
+
+  /* Set RTE number. */
+  rtenum = ((len - RIP_PACKET_MINSIZ) / 20);
+
+  /* For easy to handle. */
+  packet = &rip_buf.rip_packet;
+
+  /* RIP version check. */
+  if (packet->version == 0)
+    {
+      zlog_info ("version 0 with command %d received.", packet->command);
+      rip_peer_bad_packet (&from);
+      return -1;
+    }
+
+  /* Dump RIP packet. */
+  if (IS_RIP_DEBUG_RECV)
+    rip_packet_dump (packet, len, "RECV");
+
+  /* RIP version adjust.  This code should rethink now.  RFC1058 says
+     that "Version 1 implementations are to ignore this extra data and
+     process only the fields specified in this document.". So RIPv3
+     packet should be treated as RIPv1 ignoring must be zero field. */
+  if (packet->version > RIPv2)
+    packet->version = RIPv2;
+
+  /* Is RIP running or is this RIP neighbor ?*/
+  ri = ifp->info;
+  if (! ri->running && ! rip_neighbor_lookup (&from))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("RIP is not enabled on interface %s.", ifp->name);
+      rip_peer_bad_packet (&from);
+      return -1;
+    }
+
+  /* RIP Version check. RFC2453, 4.6 and 5.1 */
+  vrecv = ((ri->ri_receive == RI_RIP_UNSPEC) ?
+           rip->version_recv : ri->ri_receive);
+  if ((packet->version == RIPv1) && !(vrecv & RIPv1))
+    {
+      if (IS_RIP_DEBUG_PACKET)
+        zlog_debug ("  packet's v%d doesn't fit to if version spec", 
+                   packet->version);
+      rip_peer_bad_packet (&from);
+      return -1;
+    }
+  if ((packet->version == RIPv2) && !(vrecv & RIPv2))
+    {
+      if (IS_RIP_DEBUG_PACKET)
+        zlog_debug ("  packet's v%d doesn't fit to if version spec", 
+                   packet->version);
+      rip_peer_bad_packet (&from);
+      return -1;
+    }
+  
+  /* RFC2453 5.2 If the router is not configured to authenticate RIP-2
+     messages, then RIP-1 and unauthenticated RIP-2 messages will be
+     accepted; authenticated RIP-2 messages shall be discarded.  */
+  if ((ri->auth_type == RIP_NO_AUTH) 
+      && rtenum 
+      && (packet->version == RIPv2) 
+      && (packet->rte->family == htons(RIP_FAMILY_AUTH)))
+    {
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("packet RIPv%d is dropped because authentication disabled", 
+                  packet->version);
+      rip_peer_bad_packet (&from);
+      return -1;
+    }
+  
+  /* RFC:
+     If the router is configured to authenticate RIP-2 messages, then
+     RIP-1 messages and RIP-2 messages which pass authentication
+     testing shall be accepted; unauthenticated and failed
+     authentication RIP-2 messages shall be discarded.  For maximum
+     security, RIP-1 messages should be ignored when authentication is
+     in use (see section 4.1); otherwise, the routing information from
+     authenticated messages will be propagated by RIP-1 routers in an
+     unauthenticated manner. 
+  */
+  /* We make an exception for RIPv1 REQUEST packets, to which we'll
+   * always reply regardless of authentication settings, because:
+   *
+   * - if there other authorised routers on-link, the REQUESTor can
+   *   passively obtain the routing updates anyway
+   * - if there are no other authorised routers on-link, RIP can
+   *   easily be disabled for the link to prevent giving out information
+   *   on state of this routers RIP routing table..
+   *
+   * I.e. if RIPv1 has any place anymore these days, it's as a very
+   * simple way to distribute routing information (e.g. to embedded
+   * hosts / appliances) and the ability to give out RIPv1
+   * routing-information freely, while still requiring RIPv2
+   * authentication for any RESPONSEs might be vaguely useful.
+   */
+  if (ri->auth_type != RIP_NO_AUTH 
+      && packet->version == RIPv1)
+    {
+      /* Discard RIPv1 messages other than REQUESTs */
+      if (packet->command != RIP_REQUEST)
+        {
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIPv1" " dropped because authentication enabled");
+          rip_peer_bad_packet (&from);
+          return -1;
+        }
+    }
+  else if (ri->auth_type != RIP_NO_AUTH)
+    {
+      const char *auth_desc;
+      
+      if (rtenum == 0)
+        {
+          /* There definitely is no authentication in the packet. */
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIPv2 authentication failed: no auth RTE in packet");
+          rip_peer_bad_packet (&from);
+          return -1;
+        }
+      
+      /* First RTE must be an Authentication Family RTE */
+      if (packet->rte->family != htons(RIP_FAMILY_AUTH))
+        {
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIPv2" " dropped because authentication enabled");
+         rip_peer_bad_packet (&from);
+         return -1;
+        }
+      
+      /* Check RIPv2 authentication. */
+      switch (ntohs(packet->rte->tag))
+        {
+          case RIP_AUTH_SIMPLE_PASSWORD:
+            auth_desc = "simple";
+            ret = rip_auth_simple_password (packet->rte, &from, ifp);
+            break;
+          
+          case RIP_AUTH_MD5:
+            auth_desc = "MD5";
+            ret = rip_auth_md5 (packet, &from, len, ifp);
+            /* Reset RIP packet length to trim MD5 data. */
+            len = ret;
+            break;
+          
+          default:
+            ret = 0;
+            auth_desc = "unknown type";
+            if (IS_RIP_DEBUG_PACKET)
+              zlog_debug ("RIPv2 Unknown authentication type %d",
+                          ntohs (packet->rte->tag));
+        }
+      
+      if (ret)
+        {
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIPv2 %s authentication success", auth_desc);
+        }
+      else
+        {
+          if (IS_RIP_DEBUG_PACKET)
+            zlog_debug ("RIPv2 %s authentication failure", auth_desc);
+          rip_peer_bad_packet (&from);
+          return -1;
+        }
+    }
+  
+  /* Process each command. */
+  switch (packet->command)
+    {
+    case RIP_RESPONSE:
+      rip_response_process (packet, len, &from, ifc);
+      break;
+    case RIP_REQUEST:
+    case RIP_POLL:
+      rip_request_process (packet, len, &from, ifc);
+      break;
+    case RIP_TRACEON:
+    case RIP_TRACEOFF:
+      zlog_info ("Obsolete command %s received, please sent it to routed", 
+                lookup (rip_msg, packet->command));
+      rip_peer_bad_packet (&from);
+      break;
+    case RIP_POLL_ENTRY:
+      zlog_info ("Obsolete command %s received", 
+                lookup (rip_msg, packet->command));
+      rip_peer_bad_packet (&from);
+      break;
+    default:
+      zlog_info ("Unknown RIP command %d received", packet->command);
+      rip_peer_bad_packet (&from);
+      break;
+    }
+
+  return len;
+}
+
+/* Write routing table entry to the stream and return next index of
+   the routing table entry in the stream. */
+static int
+rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p,
+               u_char version, struct rip_info *rinfo)
+{
+  struct in_addr mask;
+
+  /* Write routing table entry. */
+  if (version == RIPv1)
+    {
+      stream_putw (s, AF_INET);
+      stream_putw (s, 0);
+      stream_put_ipv4 (s, p->prefix.s_addr);
+      stream_put_ipv4 (s, 0);
+      stream_put_ipv4 (s, 0);
+      stream_putl (s, rinfo->metric_out);
+    }
+  else
+    {
+      masklen2ip (p->prefixlen, &mask);
+
+      stream_putw (s, AF_INET);
+      stream_putw (s, rinfo->tag_out);
+      stream_put_ipv4 (s, p->prefix.s_addr);
+      stream_put_ipv4 (s, mask.s_addr);
+      stream_put_ipv4 (s, rinfo->nexthop_out.s_addr);
+      stream_putl (s, rinfo->metric_out);
+    }
+
+  return ++num;
+}
+
+/* Send update to the ifp or spcified neighbor. */
+void
+rip_output_process (struct connected *ifc, struct sockaddr_in *to, 
+                    int route_type, u_char version)
+{
+  int ret;
+  struct stream *s;
+  struct route_node *rp;
+  struct rip_info *rinfo;
+  struct rip_interface *ri;
+  struct prefix_ipv4 *p;
+  struct prefix_ipv4 classfull;
+  struct prefix_ipv4 ifaddrclass;
+  struct key *key = NULL;
+  /* this might need to made dynamic if RIP ever supported auth methods
+     with larger key string sizes */
+  char auth_str[RIP_AUTH_SIMPLE_SIZE];
+  size_t doff = 0; /* offset of digest offset field */
+  int num = 0;
+  int rtemax;
+  int subnetted = 0;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  /* Logging output event. */
+  if (IS_RIP_DEBUG_EVENT)
+    {
+      if (to)
+       zlog_debug ("update routes to neighbor %s", inet_ntoa (to->sin_addr));
+      else
+       zlog_debug ("update routes on interface %s ifindex %d",
+                  ifc->ifp->name, ifc->ifp->ifindex);
+    }
+
+  /* Set output stream. */
+  s = rip->obuf;
+
+  /* Reset stream and RTE counter. */
+  stream_reset (s);
+  rtemax = RIP_MAX_RTE;
+
+  /* Get RIP interface. */
+  ri = ifc->ifp->info;
+    
+  /* If output interface is in simple password authentication mode, we
+     need space for authentication data.  */
+  if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD)
+    rtemax -= 1;
+
+  /* If output interface is in MD5 authentication mode, we need space
+     for authentication header and data. */
+  if (ri->auth_type == RIP_AUTH_MD5)
+    rtemax -= 2;
+
+  /* If output interface is in simple password authentication mode
+     and string or keychain is specified we need space for auth. data */
+  if (ri->auth_type != RIP_NO_AUTH)
+    {
+      if (ri->key_chain)
+       {
+         struct keychain *keychain;
+
+         keychain = keychain_lookup (ri->key_chain);
+         if (keychain)
+           key = key_lookup_for_send (keychain);
+       }
+      /* to be passed to auth functions later */
+      rip_auth_prepare_str_send (ri, key, auth_str, RIP_AUTH_SIMPLE_SIZE);
+    }
+
+  if (version == RIPv1)
+    {
+      memcpy (&ifaddrclass, ifc->address, sizeof (struct prefix_ipv4));
+      apply_classful_mask_ipv4 (&ifaddrclass);
+      subnetted = 0;
+      if (ifc->address->prefixlen > ifaddrclass.prefixlen)
+        subnetted = 1;
+    }
+
+  for (rp = route_top (rip->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL && listcount (list) != 0)
+      {
+        rinfo = listgetdata (listhead (list));
+       /* For RIPv1, if we are subnetted, output subnets in our network    */
+       /* that have the same mask as the output "interface". For other     */
+       /* networks, only the classfull version is output.                  */
+       
+       if (version == RIPv1)
+         {
+           p = (struct prefix_ipv4 *) &rp->p;
+
+           if (IS_RIP_DEBUG_PACKET)
+             zlog_debug("RIPv1 mask check, %s/%d considered for output",
+                       inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
+
+           if (subnetted &&
+               prefix_match ((struct prefix *) &ifaddrclass, &rp->p))
+             {
+               if ((ifc->address->prefixlen != rp->p.prefixlen) &&
+                   (rp->p.prefixlen != 32))
+                 continue;
+             }
+           else
+             {
+               memcpy (&classfull, &rp->p, sizeof(struct prefix_ipv4));
+               apply_classful_mask_ipv4(&classfull);
+               if (rp->p.u.prefix4.s_addr != 0 &&
+                   classfull.prefixlen != rp->p.prefixlen)
+                 continue;
+             }
+           if (IS_RIP_DEBUG_PACKET)
+             zlog_debug("RIPv1 mask check, %s/%d made it through",
+                       inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen);
+         }
+       else 
+         p = (struct prefix_ipv4 *) &rp->p;
+
+       /* Apply output filters. */
+       ret = rip_filter (RIP_FILTER_OUT, p, ri);
+       if (ret < 0)
+         continue;
+
+       /* Changed route only output. */
+       if (route_type == rip_changed_route &&
+           (! (rinfo->flags & RIP_RTF_CHANGED)))
+         continue;
+
+       /* Split horizon. */
+       /* if (split_horizon == rip_split_horizon) */
+       if (ri->split_horizon == RIP_SPLIT_HORIZON)
+         {
+           /* 
+            * We perform split horizon for RIP and connected route. 
+            * For rip routes, we want to suppress the route if we would
+             * end up sending the route back on the interface that we
+             * learned it from, with a higher metric. For connected routes,
+             * we suppress the route if the prefix is a subset of the
+             * source address that we are going to use for the packet 
+             * (in order to handle the case when multiple subnets are
+             * configured on the same interface).
+             */
+           int suppress = 0;
+           struct rip_info *tmp_rinfo = NULL;
+
+           for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+             if (tmp_rinfo->type == ZEBRA_ROUTE_RIP &&
+                 tmp_rinfo->ifindex == ifc->ifp->ifindex)
+               {
+                 suppress = 1;
+                 break;
+               }
+
+           if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT &&
+                 prefix_match((struct prefix *)p, ifc->address))
+             suppress = 1;
+
+           if (suppress)
+             continue;
+         }
+
+       /* Preparation for route-map. */
+       rinfo->metric_set = 0;
+       rinfo->nexthop_out.s_addr = 0;
+       rinfo->metric_out = rinfo->metric;
+       rinfo->tag_out = rinfo->tag;
+       rinfo->ifindex_out = ifc->ifp->ifindex;
+
+       /* In order to avoid some local loops,
+        * if the RIP route has a nexthop via this interface, keep the nexthop,
+        * otherwise set it to 0. The nexthop should not be propagated
+        * beyond the local broadcast/multicast area in order
+        * to avoid an IGP multi-level recursive look-up.
+        * see (4.4)
+        */
+       if (rinfo->ifindex == ifc->ifp->ifindex)
+         rinfo->nexthop_out = rinfo->nexthop;
+
+       /* Interface route-map */
+       if (ri->routemap[RIP_FILTER_OUT])
+         {
+           ret = route_map_apply (ri->routemap[RIP_FILTER_OUT], 
+                                    (struct prefix *) p, RMAP_RIP, 
+                                    rinfo);
+
+           if (ret == RMAP_DENYMATCH)
+             {
+               if (IS_RIP_DEBUG_PACKET)
+                 zlog_debug ("RIP %s/%d is filtered by route-map out",
+                            inet_ntoa (p->prefix), p->prefixlen);
+                continue;
+             }
+         }
+           
+       /* Apply redistribute route map - continue, if deny */
+       if (rip->route_map[rinfo->type].name
+           && rinfo->sub_type != RIP_ROUTE_INTERFACE)
+         {
+           ret = route_map_apply (rip->route_map[rinfo->type].map,
+                                  (struct prefix *)p, RMAP_RIP, rinfo);
+
+           if (ret == RMAP_DENYMATCH) 
+             {
+               if (IS_RIP_DEBUG_PACKET)
+                 zlog_debug ("%s/%d is filtered by route-map",
+                            inet_ntoa (p->prefix), p->prefixlen);
+               continue;
+             }
+         }
+
+       /* When route-map does not set metric. */
+       if (! rinfo->metric_set)
+         {
+           /* If redistribute metric is set. */
+           if (rip->route_map[rinfo->type].metric_config
+               && rinfo->metric != RIP_METRIC_INFINITY)
+             {
+               rinfo->metric_out = rip->route_map[rinfo->type].metric;
+             }
+           else
+             {
+               /* If the route is not connected or localy generated
+                  one, use default-metric value*/
+               if (rinfo->type != ZEBRA_ROUTE_RIP 
+                   && rinfo->type != ZEBRA_ROUTE_CONNECT
+                   && rinfo->metric != RIP_METRIC_INFINITY)
+                 rinfo->metric_out = rip->default_metric;
+             }
+         }
+
+       /* Apply offset-list */
+       if (rinfo->metric != RIP_METRIC_INFINITY)
+         rip_offset_list_apply_out (p, ifc->ifp, &rinfo->metric_out);
+
+       if (rinfo->metric_out > RIP_METRIC_INFINITY)
+         rinfo->metric_out = RIP_METRIC_INFINITY;
+
+       /* Perform split-horizon with poisoned reverse 
+        * for RIP and connected routes.
+        **/
+       if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
+           /* 
+            * We perform split horizon for RIP and connected route. 
+            * For rip routes, we want to suppress the route if we would
+             * end up sending the route back on the interface that we
+             * learned it from, with a higher metric. For connected routes,
+             * we suppress the route if the prefix is a subset of the
+             * source address that we are going to use for the packet 
+             * (in order to handle the case when multiple subnets are
+             * configured on the same interface).
+             */
+         struct rip_info *tmp_rinfo = NULL;
+
+         for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+            {
+             if (tmp_rinfo->type == ZEBRA_ROUTE_RIP  &&
+                 tmp_rinfo->ifindex == ifc->ifp->ifindex)
+               rinfo->metric_out = RIP_METRIC_INFINITY;
+             if (tmp_rinfo->type == ZEBRA_ROUTE_CONNECT &&
+                prefix_match((struct prefix *)p, ifc->address))
+               rinfo->metric_out = RIP_METRIC_INFINITY;
+            }
+       }
+       
+       /* Prepare preamble, auth headers, if needs be */
+       if (num == 0)
+         {
+           stream_putc (s, RIP_RESPONSE);
+           stream_putc (s, version);
+           stream_putw (s, 0);
+           
+           /* auth header for !v1 && !no_auth */
+            if ( (ri->auth_type != RIP_NO_AUTH) && (version != RIPv1) )
+              doff = rip_auth_header_write (s, ri, key, auth_str, 
+                                              RIP_AUTH_SIMPLE_SIZE);
+          }
+        
+       /* Write RTE to the stream. */
+       num = rip_write_rte (num, s, p, version, rinfo);
+       if (num == rtemax)
+         {
+           if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
+              rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE);
+
+           ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s),
+                                  to, ifc);
+
+           if (ret >= 0 && IS_RIP_DEBUG_SEND)
+             rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
+                              stream_get_endp(s), "SEND");
+           num = 0;
+           stream_reset (s);
+         }
+      }
+
+  /* Flush unwritten RTE. */
+  if (num != 0)
+    {
+      if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
+        rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE);
+
+      ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifc);
+
+      if (ret >= 0 && IS_RIP_DEBUG_SEND)
+       rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
+                        stream_get_endp (s), "SEND");
+      num = 0;
+      stream_reset (s);
+    }
+
+  /* Statistics updates. */
+  ri->sent_updates++;
+}
+
+/* Send RIP packet to the interface. */
+static void
+rip_update_interface (struct connected *ifc, u_char version, int route_type)
+{
+  struct sockaddr_in to;
+
+  /* When RIP version is 2 and multicast enable interface. */
+  if (version == RIPv2 && if_is_multicast (ifc->ifp)) 
+    {
+      if (IS_RIP_DEBUG_EVENT)
+       zlog_debug ("multicast announce on %s ", ifc->ifp->name);
+
+      rip_output_process (ifc, NULL, route_type, version);
+      return;
+    }
+  
+  /* If we can't send multicast packet, send it with unicast. */
+  if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp))
+    {
+      if (ifc->address->family == AF_INET)
+        {
+          /* Destination address and port setting. */
+          memset (&to, 0, sizeof (struct sockaddr_in));
+          if (ifc->destination)
+            /* use specified broadcast or peer destination addr */
+            to.sin_addr = ifc->destination->u.prefix4;
+          else if (ifc->address->prefixlen < IPV4_MAX_PREFIXLEN)
+            /* calculate the appropriate broadcast address */
+            to.sin_addr.s_addr =
+              ipv4_broadcast_addr(ifc->address->u.prefix4.s_addr,
+                                  ifc->address->prefixlen);
+         else
+           /* do not know where to send the packet */
+           return;
+          to.sin_port = htons (RIP_PORT_DEFAULT);
+
+          if (IS_RIP_DEBUG_EVENT)
+            zlog_debug("%s announce to %s on %s",
+                      CONNECTED_PEER(ifc) ? "unicast" : "broadcast",
+                      inet_ntoa (to.sin_addr), ifc->ifp->name);
+
+          rip_output_process (ifc, &to, route_type, version);
+        }
+    }
+}
+
+/* Update send to all interface and neighbor. */
+static void
+rip_update_process (int route_type)
+{
+  struct listnode *node;
+  struct listnode *ifnode, *ifnnode;
+  struct connected *connected;
+  struct interface *ifp;
+  struct rip_interface *ri;
+  struct route_node *rp;
+  struct sockaddr_in to;
+  struct prefix_ipv4 *p;
+
+  /* Send RIP update to each interface. */
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      if (if_is_loopback (ifp))
+       continue;
+
+      if (! if_is_operative (ifp))
+       continue;
+
+      /* Fetch RIP interface information. */
+      ri = ifp->info;
+
+      /* When passive interface is specified, suppress announce to the
+         interface. */
+      if (ri->passive)
+       continue;
+
+      if (ri->running)
+       {
+         /* 
+          * If there is no version configuration in the interface,
+          * use rip's version setting. 
+          */
+         int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
+                      rip->version_send : ri->ri_send);
+
+         if (IS_RIP_DEBUG_EVENT) 
+           zlog_debug("SEND UPDATE to %s ifindex %d",
+                      ifp->name, ifp->ifindex);
+
+          /* send update on each connected network */
+         for (ALL_LIST_ELEMENTS (ifp->connected, ifnode, ifnnode, connected))
+           {
+             if (connected->address->family == AF_INET)
+               {
+                 if (vsend & RIPv1)
+                   rip_update_interface (connected, RIPv1, route_type);
+                 if (vsend & RIPv2) 
+                   rip_update_interface (connected, RIPv2, route_type);
+               }
+           }
+       }
+    }
+
+  /* RIP send updates to each neighbor. */
+  for (rp = route_top (rip->neighbor); rp; rp = route_next (rp))
+    if (rp->info != NULL)
+      {
+       p = (struct prefix_ipv4 *) &rp->p;
+
+       ifp = if_lookup_address (p->prefix);
+       if (! ifp)
+         {
+           zlog_warn ("Neighbor %s doesnt have connected interface!",
+                      inet_ntoa (p->prefix));
+           continue;
+         }
+        
+        if ( (connected = connected_lookup_address (ifp, p->prefix)) == NULL)
+          {
+            zlog_warn ("Neighbor %s doesnt have connected network",
+                       inet_ntoa (p->prefix));
+            continue;
+          }
+        
+       /* Set destination address and port */
+       memset (&to, 0, sizeof (struct sockaddr_in));
+       to.sin_addr = p->prefix;
+       to.sin_port = htons (RIP_PORT_DEFAULT);
+
+       /* RIP version is rip's configuration. */
+       rip_output_process (connected, &to, route_type, rip->version_send);
+      }
+}
+
+/* RIP's periodical timer. */
+static int
+rip_update (struct thread *t)
+{
+  /* Clear timer pointer. */
+  rip->t_update = NULL;
+
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("update timer fire!");
+
+  /* Process update output. */
+  rip_update_process (rip_all_route);
+
+  /* Triggered updates may be suppressed if a regular update is due by
+     the time the triggered update would be sent. */
+  if (rip->t_triggered_interval)
+    {
+      thread_cancel (rip->t_triggered_interval);
+      rip->t_triggered_interval = NULL;
+    }
+  rip->trigger = 0;
+
+  /* Register myself. */
+  rip_event (RIP_UPDATE_EVENT, 0);
+
+  return 0;
+}
+
+/* Walk down the RIP routing table then clear changed flag. */
+static void
+rip_clear_changed_flag (void)
+{
+  struct route_node *rp;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  for (rp = route_top (rip->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          UNSET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+          /* This flag can be set only on the first entry. */
+          break;
+        }
+}
+
+/* Triggered update interval timer. */
+static int
+rip_triggered_interval (struct thread *t)
+{
+  int rip_triggered_update (struct thread *);
+
+  rip->t_triggered_interval = NULL;
+
+  if (rip->trigger)
+    {
+      rip->trigger = 0;
+      rip_triggered_update (t);
+    }
+  return 0;
+}     
+
+/* Execute triggered update. */
+static int
+rip_triggered_update (struct thread *t)
+{
+  int interval;
+
+  /* Clear thred pointer. */
+  rip->t_triggered_update = NULL;
+
+  /* Cancel interval timer. */
+  if (rip->t_triggered_interval)
+    {
+      thread_cancel (rip->t_triggered_interval);
+      rip->t_triggered_interval = NULL;
+    }
+  rip->trigger = 0;
+
+  /* Logging triggered update. */
+  if (IS_RIP_DEBUG_EVENT)
+    zlog_debug ("triggered update!");
+
+  /* Split Horizon processing is done when generating triggered
+     updates as well as normal updates (see section 2.6). */
+  rip_update_process (rip_changed_route);
+
+  /* Once all of the triggered updates have been generated, the route
+     change flags should be cleared. */
+  rip_clear_changed_flag ();
+
+  /* After a triggered update is sent, a timer should be set for a
+   random interval between 1 and 5 seconds.  If other changes that
+   would trigger updates occur before the timer expires, a single
+   update is triggered when the timer expires. */
+  interval = (random () % 5) + 1;
+
+  rip->t_triggered_interval = 
+    thread_add_timer (master, rip_triggered_interval, NULL, interval);
+
+  return 0;
+}
+
+/* Withdraw redistributed route. */
+void
+rip_redistribute_withdraw (int type)
+{
+  struct route_node *rp;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+
+  if (!rip)
+    return;
+
+  for (rp = route_top (rip->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL)
+      {
+       rinfo = listgetdata (listhead (list));
+       if (rinfo->type == type
+           && rinfo->sub_type != RIP_ROUTE_INTERFACE)
+         {
+           /* Perform poisoned reverse. */
+           rinfo->metric = RIP_METRIC_INFINITY;
+           RIP_TIMER_ON (rinfo->t_garbage_collect, 
+                         rip_garbage_collect, rip->garbage_time);
+           RIP_TIMER_OFF (rinfo->t_timeout);
+           rinfo->flags |= RIP_RTF_CHANGED;
+
+           if (IS_RIP_DEBUG_EVENT) {
+              struct prefix_ipv4 *p = (struct prefix_ipv4 *) &rp->p;
+
+              zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [withdraw]",
+                         inet_ntoa(p->prefix), p->prefixlen,
+                         ifindex2ifname(rinfo->ifindex));
+           }
+
+           rip_event (RIP_TRIGGERED_UPDATE, 0);
+         }
+      }
+}
+
+/* Create new RIP instance and set it to global variable. */
+static int
+rip_create (void)
+{
+  rip = XCALLOC (MTYPE_RIP, sizeof (struct rip));
+
+  /* Set initial value. */
+  rip->version_send = RI_RIP_VERSION_2;
+  rip->version_recv = RI_RIP_VERSION_1_AND_2;
+  rip->update_time = RIP_UPDATE_TIMER_DEFAULT;
+  rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT;
+  rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT;
+  rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT;
+
+  /* Initialize RIP routig table. */
+  rip->table = route_table_init ();
+  rip->route = route_table_init ();
+  rip->neighbor = route_table_init ();
+
+  /* Make output stream. */
+  rip->obuf = stream_new (1500);
+
+  /* Make socket. */
+  rip->sock = rip_create_socket (NULL);
+  if (rip->sock < 0)
+    return rip->sock;
+
+  /* Create read and timer thread. */
+  rip_event (RIP_READ, rip->sock);
+  rip_event (RIP_UPDATE_EVENT, 1);
+
+  return 0;
+}
+
+/* Sned RIP request to the destination. */
+int
+rip_request_send (struct sockaddr_in *to, struct interface *ifp,
+                 u_char version, struct connected *connected)
+{
+  struct rte *rte;
+  struct rip_packet rip_packet;
+  struct listnode *node, *nnode;
+
+  memset (&rip_packet, 0, sizeof (rip_packet));
+
+  rip_packet.command = RIP_REQUEST;
+  rip_packet.version = version;
+  rte = rip_packet.rte;
+  rte->metric = htonl (RIP_METRIC_INFINITY);
+
+  if (connected) 
+    {
+      /* 
+       * connected is only sent for ripv1 case, or when
+       * interface does not support multicast.  Caller loops
+       * over each connected address for this case.
+       */
+      if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), 
+                            to, connected) != sizeof (rip_packet))
+        return -1;
+      else
+        return sizeof (rip_packet);
+    }
+       
+  /* send request on each connected network */
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
+    {
+      struct prefix_ipv4 *p;
+
+      p = (struct prefix_ipv4 *) connected->address;
+
+      if (p->family != AF_INET)
+        continue;
+
+      if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), 
+                            to, connected) != sizeof (rip_packet))
+        return -1;
+    }
+  return sizeof (rip_packet);
+}
+
+static int
+rip_update_jitter (unsigned long time)
+{
+#define JITTER_BOUND 4
+  /* We want to get the jitter to +/- 1/JITTER_BOUND the interval.
+     Given that, we cannot let time be less than JITTER_BOUND seconds.
+     The RIPv2 RFC says jitter should be small compared to
+     update_time.  We consider 1/JITTER_BOUND to be small.
+  */
+  
+  int jitter_input = time;
+  int jitter;
+  
+  if (jitter_input < JITTER_BOUND)
+    jitter_input = JITTER_BOUND;
+  
+  jitter = (((random () % ((jitter_input * 2) + 1)) - jitter_input));  
+
+  return jitter/JITTER_BOUND;
+}
+
+void
+rip_event (enum rip_event event, int sock)
+{
+  int jitter = 0;
+
+  switch (event)
+    {
+    case RIP_READ:
+      rip->t_read = thread_add_read (master, rip_read, NULL, sock);
+      break;
+    case RIP_UPDATE_EVENT:
+      if (rip->t_update)
+       {
+         thread_cancel (rip->t_update);
+         rip->t_update = NULL;
+       }
+      jitter = rip_update_jitter (rip->update_time);
+      rip->t_update = 
+       thread_add_timer (master, rip_update, NULL, 
+                         sock ? 2 : rip->update_time + jitter);
+      break;
+    case RIP_TRIGGERED_UPDATE:
+      if (rip->t_triggered_interval)
+       rip->trigger = 1;
+      else if (! rip->t_triggered_update)
+       rip->t_triggered_update = 
+         thread_add_event (master, rip_triggered_update, NULL, 0);
+      break;
+    default:
+      break;
+    }
+}
+
+DEFUN (router_rip,
+       router_rip_cmd,
+       "router rip",
+       "Enable a routing process\n"
+       "Routing Information Protocol (RIP)\n")
+{
+  int ret;
+
+  /* If rip is not enabled before. */
+  if (! rip)
+    {
+      ret = rip_create ();
+      if (ret < 0)
+       {
+         zlog_info ("Can't create RIP");
+         return CMD_WARNING;
+       }
+    }
+  vty->node = RIP_NODE;
+  vty->index = rip;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_rip,
+       no_router_rip_cmd,
+       "no router rip",
+       NO_STR
+       "Enable a routing process\n"
+       "Routing Information Protocol (RIP)\n")
+{
+  if (rip)
+    rip_clean ();
+  return CMD_SUCCESS;
+}
+
+DEFUN (rip_version,
+       rip_version_cmd,
+       "version <1-2>",
+       "Set routing protocol version\n"
+       "version\n")
+{
+  int version;
+
+  version = atoi (argv[0]);
+  if (version != RIPv1 && version != RIPv2)
+    {
+      vty_out (vty, "invalid rip version %d%s", version,
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  rip->version_send = version;
+  rip->version_recv = version;
+
+  return CMD_SUCCESS;
+} 
+
+DEFUN (no_rip_version,
+       no_rip_version_cmd,
+       "no version",
+       NO_STR
+       "Set routing protocol version\n")
+{
+  /* Set RIP version to the default. */
+  rip->version_send = RI_RIP_VERSION_2;
+  rip->version_recv = RI_RIP_VERSION_1_AND_2;
+
+  return CMD_SUCCESS;
+} 
+
+ALIAS (no_rip_version,
+       no_rip_version_val_cmd,
+       "no version <1-2>",
+       NO_STR
+       "Set routing protocol version\n"
+       "version\n")
+
+DEFUN (rip_route,
+       rip_route_cmd,
+       "route A.B.C.D/M",
+       "RIP static route configuration\n"
+       "IP prefix <network>/<length>\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_node *node;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv4 (&p);
+
+  /* For router rip configuration. */
+  node = route_node_get (rip->route, (struct prefix *) &p);
+
+  if (node->info)
+    {
+      vty_out (vty, "There is already same static route.%s", VTY_NEWLINE);
+      route_unlock_node (node);
+      return CMD_WARNING;
+    }
+
+  node->info = (char *)"static";
+
+  rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_route,
+       no_rip_route_cmd,
+       "no route A.B.C.D/M",
+       NO_STR
+       "RIP static route configuration\n"
+       "IP prefix <network>/<length>\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_node *node;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret < 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv4 (&p);
+
+  /* For router rip configuration. */
+  node = route_node_lookup (rip->route, (struct prefix *) &p);
+  if (! node)
+    {
+      vty_out (vty, "Can't find route %s.%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rip_redistribute_delete (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0);
+  route_unlock_node (node);
+
+  node->info = NULL;
+  route_unlock_node (node);
+
+  return CMD_SUCCESS;
+}
+
+#if 0
+static void
+rip_update_default_metric (void)
+{
+  struct route_node *np;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  for (np = route_top (rip->table); np; np = route_next (np))
+    if ((list = np->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT)
+          rinfo->metric = rip->default_metric;
+}
+#endif
+
+DEFUN (rip_default_metric,
+       rip_default_metric_cmd,
+       "default-metric <1-16>",
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+{
+  if (rip)
+    {
+      rip->default_metric = atoi (argv[0]);
+      /* rip_update_default_metric (); */
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_default_metric,
+       no_rip_default_metric_cmd,
+       "no default-metric",
+       NO_STR
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+{
+  if (rip)
+    {
+      rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT;
+      /* rip_update_default_metric (); */
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_rip_default_metric,
+       no_rip_default_metric_val_cmd,
+       "no default-metric <1-16>",
+       NO_STR
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+
+DEFUN (rip_timers,
+       rip_timers_cmd,
+       "timers basic <5-2147483647> <5-2147483647> <5-2147483647>",
+       "Adjust routing timers\n"
+       "Basic routing protocol update timers\n"
+       "Routing table update timer value in second. Default is 30.\n"
+       "Routing information timeout timer. Default is 180.\n"
+       "Garbage collection timer. Default is 120.\n")
+{
+  unsigned long update;
+  unsigned long timeout;
+  unsigned long garbage;
+  char *endptr = NULL;
+  unsigned long RIP_TIMER_MAX = 2147483647;
+  unsigned long RIP_TIMER_MIN = 5;
+
+  update = strtoul (argv[0], &endptr, 10);
+  if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0')  
+    {
+      vty_out (vty, "update timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  timeout = strtoul (argv[1], &endptr, 10);
+  if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0') 
+    {
+      vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  garbage = strtoul (argv[2], &endptr, 10);
+  if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0') 
+    {
+      vty_out (vty, "garbage timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Set each timer value. */
+  rip->update_time = update;
+  rip->timeout_time = timeout;
+  rip->garbage_time = garbage;
+
+  /* Reset update timer thread. */
+  rip_event (RIP_UPDATE_EVENT, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_timers,
+       no_rip_timers_cmd,
+       "no timers basic",
+       NO_STR
+       "Adjust routing timers\n"
+       "Basic routing protocol update timers\n")
+{
+  /* Set each timer value to the default. */
+  rip->update_time = RIP_UPDATE_TIMER_DEFAULT;
+  rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT;
+  rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT;
+
+  /* Reset update timer thread. */
+  rip_event (RIP_UPDATE_EVENT, 0);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_rip_timers,
+       no_rip_timers_val_cmd,
+       "no timers basic <0-65535> <0-65535> <0-65535>",
+       NO_STR
+       "Adjust routing timers\n"
+       "Basic routing protocol update timers\n"
+       "Routing table update timer value in second. Default is 30.\n"
+       "Routing information timeout timer. Default is 180.\n"
+       "Garbage collection timer. Default is 120.\n")
+
+
+struct route_table *rip_distance_table;
+
+struct rip_distance
+{
+  /* Distance value for the IP source prefix. */
+  u_char distance;
+
+  /* Name of the access-list to be matched. */
+  char *access_list;
+};
+
+static struct rip_distance *
+rip_distance_new (void)
+{
+  return XCALLOC (MTYPE_RIP_DISTANCE, sizeof (struct rip_distance));
+}
+
+static void
+rip_distance_free (struct rip_distance *rdistance)
+{
+  XFREE (MTYPE_RIP_DISTANCE, rdistance);
+}
+
+static int
+rip_distance_set (struct vty *vty, const char *distance_str, const char *ip_str,
+                 const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  u_char distance;
+  struct route_node *rn;
+  struct rip_distance *rdistance;
+
+  ret = str2prefix_ipv4 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  distance = atoi (distance_str);
+
+  /* Get RIP distance node. */
+  rn = route_node_get (rip_distance_table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      rdistance = rn->info;
+      route_unlock_node (rn);
+    }
+  else
+    {
+      rdistance = rip_distance_new ();
+      rn->info = rdistance;
+    }
+
+  /* Set distance value. */
+  rdistance->distance = distance;
+
+  /* Reset access-list configuration. */
+  if (rdistance->access_list)
+    {
+      free (rdistance->access_list);
+      rdistance->access_list = NULL;
+    }
+  if (access_list_str)
+    rdistance->access_list = strdup (access_list_str);
+
+  return CMD_SUCCESS;
+}
+
+static int
+rip_distance_unset (struct vty *vty, const char *distance_str,
+                   const char *ip_str, const char *access_list_str)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct rip_distance *rdistance;
+
+  ret = str2prefix_ipv4 (ip_str, &p);
+  if (ret == 0)
+    {
+      vty_out (vty, "Malformed prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rn = route_node_lookup (rip_distance_table, (struct prefix *)&p);
+  if (! rn)
+    {
+      vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rdistance = rn->info;
+
+  if (rdistance->access_list)
+    free (rdistance->access_list);
+  rip_distance_free (rdistance);
+
+  rn->info = NULL;
+  route_unlock_node (rn);
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+static void
+rip_distance_reset (void)
+{
+  struct route_node *rn;
+  struct rip_distance *rdistance;
+
+  for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
+    if ((rdistance = rn->info) != NULL)
+      {
+       if (rdistance->access_list)
+         free (rdistance->access_list);
+       rip_distance_free (rdistance);
+       rn->info = NULL;
+       route_unlock_node (rn);
+      }
+}
+
+/* Apply RIP information to distance method. */
+u_char
+rip_distance_apply (struct rip_info *rinfo)
+{
+  struct route_node *rn;
+  struct prefix_ipv4 p;
+  struct rip_distance *rdistance;
+  struct access_list *alist;
+
+  if (! rip)
+    return 0;
+
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefix = rinfo->from;
+  p.prefixlen = IPV4_MAX_BITLEN;
+
+  /* Check source address. */
+  rn = route_node_match (rip_distance_table, (struct prefix *) &p);
+  if (rn)
+    {
+      rdistance = rn->info;
+      route_unlock_node (rn);
+
+      if (rdistance->access_list)
+       {
+         alist = access_list_lookup (AFI_IP, rdistance->access_list);
+         if (alist == NULL)
+           return 0;
+         if (access_list_apply (alist, &rinfo->rp->p) == FILTER_DENY)
+           return 0;
+
+         return rdistance->distance;
+       }
+      else
+       return rdistance->distance;
+    }
+
+  if (rip->distance)
+    return rip->distance;
+
+  return 0;
+}
+
+static void
+rip_distance_show (struct vty *vty)
+{
+  struct route_node *rn;
+  struct rip_distance *rdistance;
+  int header = 1;
+  char buf[BUFSIZ];
+  
+  vty_out (vty, "  Distance: (default is %d)%s",
+          rip->distance ? rip->distance :ZEBRA_RIP_DISTANCE_DEFAULT,
+          VTY_NEWLINE);
+
+  for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
+    if ((rdistance = rn->info) != NULL)
+      {
+       if (header)
+         {
+           vty_out (vty, "    Address           Distance  List%s",
+                    VTY_NEWLINE);
+           header = 0;
+         }
+       sprintf (buf, "%s/%d", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen);
+       vty_out (vty, "    %-20s  %4d  %s%s",
+                buf, rdistance->distance,
+                rdistance->access_list ? rdistance->access_list : "",
+                VTY_NEWLINE);
+      }
+}
+
+DEFUN (rip_distance,
+       rip_distance_cmd,
+       "distance <1-255>",
+       "Administrative distance\n"
+       "Distance value\n")
+{
+  rip->distance = atoi (argv[0]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_distance,
+       no_rip_distance_cmd,
+       "no distance <1-255>",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n")
+{
+  rip->distance = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (rip_distance_source,
+       rip_distance_source_cmd,
+       "distance <1-255> A.B.C.D/M",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  rip_distance_set (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_distance_source,
+       no_rip_distance_source_cmd,
+       "no distance <1-255> A.B.C.D/M",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n")
+{
+  rip_distance_unset (vty, argv[0], argv[1], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (rip_distance_source_access_list,
+       rip_distance_source_access_list_cmd,
+       "distance <1-255> A.B.C.D/M WORD",
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  rip_distance_set (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_distance_source_access_list,
+       no_rip_distance_source_access_list_cmd,
+       "no distance <1-255> A.B.C.D/M WORD",
+       NO_STR
+       "Administrative distance\n"
+       "Distance value\n"
+       "IP source prefix\n"
+       "Access list name\n")
+{
+  rip_distance_unset (vty, argv[0], argv[1], argv[2]);
+  return CMD_SUCCESS;
+}
+
+/* Update ECMP routes to zebra when ECMP is disabled. */
+static void
+rip_ecmp_disable (void)
+{
+  struct route_node *rp;
+  struct rip_info *rinfo, *tmp_rinfo;
+  struct list *list;
+  struct listnode *node, *nextnode;
+
+  if (!rip)
+    return;
+
+  for (rp = route_top (rip->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL && listcount (list) > 1)
+      {
+        rinfo = listgetdata (listhead (list));
+        if (!rip_route_rte (rinfo))
+          continue;
+
+        /* Drop all other entries, except the first one. */
+        for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo))
+          if (tmp_rinfo != rinfo)
+            {
+              RIP_TIMER_OFF (tmp_rinfo->t_timeout);
+              RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect);
+              list_delete_node (list, node);
+              rip_info_free (tmp_rinfo);
+            }
+
+        /* Update zebra. */
+        rip_zebra_ipv4_add (rp);
+
+        /* Set the route change flag. */
+        SET_FLAG (rinfo->flags, RIP_RTF_CHANGED);
+
+        /* Signal the output process to trigger an update. */
+        rip_event (RIP_TRIGGERED_UPDATE, 0);
+      }
+}
+
+DEFUN (rip_allow_ecmp,
+       rip_allow_ecmp_cmd,
+       "allow-ecmp",
+       "Allow Equal Cost MultiPath\n")
+{
+  if (rip->ecmp)
+    {
+      vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rip->ecmp = 1;
+  zlog_info ("ECMP is enabled.");
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rip_allow_ecmp,
+       no_rip_allow_ecmp_cmd,
+       "no allow-ecmp",
+       NO_STR
+       "Allow Equal Cost MultiPath\n")
+{
+  if (!rip->ecmp)
+    {
+      vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rip->ecmp = 0;
+  zlog_info ("ECMP is disabled.");
+  rip_ecmp_disable ();
+  return CMD_SUCCESS;
+}
+
+/* Print out routes update time. */
+static void
+rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo)
+{
+  time_t clock;
+  struct tm *tm;
+#define TIME_BUF 25
+  char timebuf [TIME_BUF];
+  struct thread *thread;
+
+  if ((thread = rinfo->t_timeout) != NULL)
+    {
+      clock = thread_timer_remain_second (thread);
+      tm = gmtime (&clock);
+      strftime (timebuf, TIME_BUF, "%M:%S", tm);
+      vty_out (vty, "%5s", timebuf);
+    }
+  else if ((thread = rinfo->t_garbage_collect) != NULL)
+    {
+      clock = thread_timer_remain_second (thread);
+      tm = gmtime (&clock);
+      strftime (timebuf, TIME_BUF, "%M:%S", tm);
+      vty_out (vty, "%5s", timebuf);
+    }
+}
+
+static const char *
+rip_route_type_print (int sub_type)
+{
+  switch (sub_type)
+    {
+      case RIP_ROUTE_RTE:
+       return "n";
+      case RIP_ROUTE_STATIC:
+       return "s";
+      case RIP_ROUTE_DEFAULT:
+       return "d";
+      case RIP_ROUTE_REDISTRIBUTE:
+       return "r";
+      case RIP_ROUTE_INTERFACE:
+       return "i";
+      default:
+       return "?";
+    }
+}
+
+DEFUN (show_ip_rip,
+       show_ip_rip_cmd,
+       "show ip rip",
+       SHOW_STR
+       IP_STR
+       "Show RIP routes\n")
+{
+  struct route_node *np;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  if (! rip)
+    return CMD_SUCCESS;
+
+  vty_out (vty, "Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP%s"
+          "Sub-codes:%s"
+           "      (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
+          "      (i) - interface%s%s"
+          "     Network            Next Hop         Metric From            Tag Time%s",
+          VTY_NEWLINE, VTY_NEWLINE,  VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  
+  for (np = route_top (rip->table); np; np = route_next (np))
+    if ((list = np->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+      {
+       int len;
+
+       len = vty_out (vty, "%c(%s) %s/%d",
+                      /* np->lock, For debugging. */
+                      zebra_route_char(rinfo->type),
+                      rip_route_type_print (rinfo->sub_type),
+                      inet_ntoa (np->p.u.prefix4), np->p.prefixlen);
+       
+       len = 24 - len;
+
+       if (len > 0)
+         vty_out (vty, "%*s", len, " ");
+
+        if (rinfo->nexthop.s_addr) 
+         vty_out (vty, "%-20s %2d ", inet_ntoa (rinfo->nexthop),
+                  rinfo->metric);
+        else
+         vty_out (vty, "0.0.0.0              %2d ", rinfo->metric);
+
+       /* Route which exist in kernel routing table. */
+       if ((rinfo->type == ZEBRA_ROUTE_RIP) && 
+           (rinfo->sub_type == RIP_ROUTE_RTE))
+         {
+           vty_out (vty, "%-15s ", inet_ntoa (rinfo->from));
+           vty_out (vty, "%3d ", rinfo->tag);
+           rip_vty_out_uptime (vty, rinfo);
+         }
+       else if (rinfo->metric == RIP_METRIC_INFINITY)
+         {
+           vty_out (vty, "self            ");
+           vty_out (vty, "%3d ", rinfo->tag);
+           rip_vty_out_uptime (vty, rinfo);
+         }
+       else
+         {
+           if (rinfo->external_metric)
+             {
+               len = vty_out (vty, "self (%s:%d)", 
+                              zebra_route_string(rinfo->type),
+                              rinfo->external_metric);
+               len = 16 - len;
+               if (len > 0)
+                 vty_out (vty, "%*s", len, " ");
+             }
+           else
+             vty_out (vty, "self            ");
+           vty_out (vty, "%3d", rinfo->tag);
+         }
+
+       vty_out (vty, "%s", VTY_NEWLINE);
+      }
+  return CMD_SUCCESS;
+}
+
+/* Vincent: formerly, it was show_ip_protocols_rip: "show ip protocols" */
+DEFUN (show_ip_rip_status,
+       show_ip_rip_status_cmd,
+       "show ip rip status",
+       SHOW_STR
+       IP_STR
+       "Show RIP routes\n"
+       "IP routing protocol process parameters and statistics\n")
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct rip_interface *ri;
+  extern const struct message ri_version_msg[];
+  const char *send_version;
+  const char *receive_version;
+
+  if (! rip)
+    return CMD_SUCCESS;
+
+  vty_out (vty, "Routing Protocol is \"rip\"%s", VTY_NEWLINE);
+  vty_out (vty, "  Sending updates every %ld seconds with +/-50%%,",
+          rip->update_time);
+  vty_out (vty, " next due in %lu seconds%s", 
+          thread_timer_remain_second(rip->t_update),
+          VTY_NEWLINE);
+  vty_out (vty, "  Timeout after %ld seconds,", rip->timeout_time);
+  vty_out (vty, " garbage collect after %ld seconds%s", rip->garbage_time,
+          VTY_NEWLINE);
+
+  /* Filtering status show. */
+  config_show_distribute (vty);
+                
+  /* Default metric information. */
+  vty_out (vty, "  Default redistribution metric is %d%s",
+          rip->default_metric, VTY_NEWLINE);
+
+  /* Redistribute information. */
+  vty_out (vty, "  Redistributing:");
+  config_write_rip_redistribute (vty, 0);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "  Default version control: send version %s,",
+          lookup(ri_version_msg,rip->version_send));
+  if (rip->version_recv == RI_RIP_VERSION_1_AND_2)
+    vty_out (vty, " receive any version %s", VTY_NEWLINE);
+  else
+    vty_out (vty, " receive version %s %s",
+            lookup(ri_version_msg,rip->version_recv), VTY_NEWLINE);
+
+  vty_out (vty, "    Interface        Send  Recv   Key-chain%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      ri = ifp->info;
+
+      if (!ri->running)
+       continue;
+
+      if (ri->enable_network || ri->enable_interface)
+       {
+         if (ri->ri_send == RI_RIP_UNSPEC)
+           send_version = lookup (ri_version_msg, rip->version_send);
+         else
+           send_version = lookup (ri_version_msg, ri->ri_send);
+
+         if (ri->ri_receive == RI_RIP_UNSPEC)
+           receive_version = lookup (ri_version_msg, rip->version_recv);
+         else
+           receive_version = lookup (ri_version_msg, ri->ri_receive);
+       
+         vty_out (vty, "    %-17s%-3s   %-3s    %s%s", ifp->name,
+                  send_version,
+                  receive_version,
+                  ri->key_chain ? ri->key_chain : "",
+                  VTY_NEWLINE);
+       }
+    }
+
+  vty_out (vty, "  Routing for Networks:%s", VTY_NEWLINE);
+  config_write_rip_network (vty, 0);  
+
+  {
+    int found_passive = 0;
+    for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+      {
+       ri = ifp->info;
+
+       if ((ri->enable_network || ri->enable_interface) && ri->passive)
+         {
+           if (!found_passive)
+             {
+               vty_out (vty, "  Passive Interface(s):%s", VTY_NEWLINE);
+               found_passive = 1;
+             }
+           vty_out (vty, "    %s%s", ifp->name, VTY_NEWLINE);
+         }
+      }
+  }
+
+  vty_out (vty, "  Routing Information Sources:%s", VTY_NEWLINE);
+  vty_out (vty, "    Gateway          BadPackets BadRoutes  Distance Last Update%s", VTY_NEWLINE);
+  rip_peer_display (vty);
+
+  rip_distance_show (vty);
+
+  return CMD_SUCCESS;
+}
+
+/* RIP configuration write function. */
+static int
+config_write_rip (struct vty *vty)
+{
+  int write = 0;
+  struct route_node *rn;
+  struct rip_distance *rdistance;
+
+  if (rip)
+    {
+      /* Router RIP statement. */
+      vty_out (vty, "router rip%s", VTY_NEWLINE);
+      write++;
+  
+      /* RIP version statement.  Default is RIP version 2. */
+      if (rip->version_send != RI_RIP_VERSION_2
+         || rip->version_recv != RI_RIP_VERSION_1_AND_2)
+       vty_out (vty, " version %d%s", rip->version_send,
+                VTY_NEWLINE);
+      /* RIP timer configuration. */
+      if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT 
+         || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT 
+         || rip->garbage_time != RIP_GARBAGE_TIMER_DEFAULT)
+       vty_out (vty, " timers basic %lu %lu %lu%s",
+                rip->update_time,
+                rip->timeout_time,
+                rip->garbage_time,
+                VTY_NEWLINE);
+
+      /* Default information configuration. */
+      if (rip->default_information)
+       {
+         if (rip->default_information_route_map)
+           vty_out (vty, " default-information originate route-map %s%s",
+                    rip->default_information_route_map, VTY_NEWLINE);
+         else
+           vty_out (vty, " default-information originate%s",
+                    VTY_NEWLINE);
+       }
+
+      /* Redistribute configuration. */
+      config_write_rip_redistribute (vty, 1);
+
+      /* RIP offset-list configuration. */
+      config_write_rip_offset_list (vty);
+
+      /* RIP enabled network and interface configuration. */
+      config_write_rip_network (vty, 1);
+                       
+      /* RIP default metric configuration */
+      if (rip->default_metric != RIP_DEFAULT_METRIC_DEFAULT)
+        vty_out (vty, " default-metric %d%s",
+                rip->default_metric, VTY_NEWLINE);
+
+      /* Distribute configuration. */
+      write += config_write_distribute (vty);
+
+      /* Interface routemap configuration */
+      write += config_write_if_rmap (vty);
+
+      /* Distance configuration. */
+      if (rip->distance)
+       vty_out (vty, " distance %d%s", rip->distance, VTY_NEWLINE);
+
+      /* RIP source IP prefix distance configuration. */
+      for (rn = route_top (rip_distance_table); rn; rn = route_next (rn))
+       if ((rdistance = rn->info) != NULL)
+         vty_out (vty, " distance %d %s/%d %s%s", rdistance->distance,
+                  inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen,
+                  rdistance->access_list ? rdistance->access_list : "",
+                  VTY_NEWLINE);
+
+      /* ECMP configuration. */
+      if (rip->ecmp)
+        vty_out (vty, " allow-ecmp%s", VTY_NEWLINE);
+
+      /* RIP static route configuration. */
+      for (rn = route_top (rip->route); rn; rn = route_next (rn))
+       if (rn->info)
+         vty_out (vty, " route %s/%d%s", 
+                  inet_ntoa (rn->p.u.prefix4),
+                  rn->p.prefixlen,
+                  VTY_NEWLINE);
+
+    }
+  return write;
+}
+
+/* RIP node structure. */
+static struct cmd_node rip_node =
+{
+  RIP_NODE,
+  "%s(config-router)# ",
+  1
+};
+
+/* Distribute-list update functions. */
+static void
+rip_distribute_update (struct distribute *dist)
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+  struct access_list *alist;
+  struct prefix_list *plist;
+
+  if (! dist->ifname)
+    return;
+
+  ifp = if_lookup_by_name (dist->ifname);
+  if (ifp == NULL)
+    return;
+
+  ri = ifp->info;
+
+  if (dist->list[DISTRIBUTE_V4_IN])
+    {
+      alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_IN]);
+      if (alist)
+       ri->list[RIP_FILTER_IN] = alist;
+      else
+       ri->list[RIP_FILTER_IN] = NULL;
+    }
+  else
+    ri->list[RIP_FILTER_IN] = NULL;
+
+  if (dist->list[DISTRIBUTE_V4_OUT])
+    {
+      alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_OUT]);
+      if (alist)
+       ri->list[RIP_FILTER_OUT] = alist;
+      else
+       ri->list[RIP_FILTER_OUT] = NULL;
+    }
+  else
+    ri->list[RIP_FILTER_OUT] = NULL;
+
+  if (dist->prefix[DISTRIBUTE_V4_IN])
+    {
+      plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_IN]);
+      if (plist)
+       ri->prefix[RIP_FILTER_IN] = plist;
+      else
+       ri->prefix[RIP_FILTER_IN] = NULL;
+    }
+  else
+    ri->prefix[RIP_FILTER_IN] = NULL;
+
+  if (dist->prefix[DISTRIBUTE_V4_OUT])
+    {
+      plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_OUT]);
+      if (plist)
+       ri->prefix[RIP_FILTER_OUT] = plist;
+      else
+       ri->prefix[RIP_FILTER_OUT] = NULL;
+    }
+  else
+    ri->prefix[RIP_FILTER_OUT] = NULL;
+}
+
+void
+rip_distribute_update_interface (struct interface *ifp)
+{
+  struct distribute *dist;
+
+  dist = distribute_lookup (ifp->name);
+  if (dist)
+    rip_distribute_update (dist);
+}
+
+/* Update all interface's distribute list. */
+/* ARGSUSED */
+static void
+rip_distribute_update_all (struct prefix_list *notused)
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    rip_distribute_update_interface (ifp);
+}
+/* ARGSUSED */
+static void
+rip_distribute_update_all_wrapper(struct access_list *notused)
+{
+        rip_distribute_update_all(NULL);
+}
+
+/* Delete all added rip route. */
+void
+rip_clean (void)
+{
+  int i;
+  struct route_node *rp;
+  struct rip_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  if (rip)
+    {
+      /* Clear RIP routes */
+      for (rp = route_top (rip->table); rp; rp = route_next (rp))
+        if ((list = rp->info) != NULL)
+          {
+            rinfo = listgetdata (listhead (list));
+            if (rip_route_rte (rinfo))
+              rip_zebra_ipv4_delete (rp);
+
+            for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+              {
+                RIP_TIMER_OFF (rinfo->t_timeout);
+                RIP_TIMER_OFF (rinfo->t_garbage_collect);
+                rip_info_free (rinfo);
+              }
+            list_delete (list);
+            rp->info = NULL;
+            route_unlock_node (rp);
+          }
+
+      /* Cancel RIP related timers. */
+      RIP_TIMER_OFF (rip->t_update);
+      RIP_TIMER_OFF (rip->t_triggered_update);
+      RIP_TIMER_OFF (rip->t_triggered_interval);
+
+      /* Cancel read thread. */
+      if (rip->t_read)
+       {
+         thread_cancel (rip->t_read);
+         rip->t_read = NULL;
+       }
+
+      /* Close RIP socket. */
+      if (rip->sock >= 0)
+       {
+         close (rip->sock);
+         rip->sock = -1;
+       }
+
+      /* Static RIP route configuration. */
+      for (rp = route_top (rip->route); rp; rp = route_next (rp))
+       if (rp->info)
+         {
+           rp->info = NULL;
+           route_unlock_node (rp);
+         }
+
+      /* RIP neighbor configuration. */
+      for (rp = route_top (rip->neighbor); rp; rp = route_next (rp))
+       if (rp->info)
+         {
+           rp->info = NULL;
+           route_unlock_node (rp);
+         }
+
+      /* Redistribute related clear. */
+      if (rip->default_information_route_map)
+       free (rip->default_information_route_map);
+
+      for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+       if (rip->route_map[i].name)
+         free (rip->route_map[i].name);
+
+      XFREE (MTYPE_ROUTE_TABLE, rip->table);
+      XFREE (MTYPE_ROUTE_TABLE, rip->route);
+      XFREE (MTYPE_ROUTE_TABLE, rip->neighbor);
+      
+      XFREE (MTYPE_RIP, rip);
+      rip = NULL;
+    }
+
+  rip_clean_network ();
+  rip_passive_nondefault_clean ();
+  rip_offset_clean ();
+  rip_interfaces_clean ();
+  rip_distance_reset ();
+  rip_redistribute_clean ();
+}
+
+/* Reset all values to the default settings. */
+void
+rip_reset (void)
+{
+  /* Reset global counters. */
+  rip_global_route_changes = 0;
+  rip_global_queries = 0;
+
+  /* Call ripd related reset functions. */
+  rip_debug_reset ();
+  rip_route_map_reset ();
+
+  /* Call library reset functions. */
+  vty_reset ();
+  access_list_reset ();
+  prefix_list_reset ();
+
+  distribute_list_reset ();
+
+  rip_interfaces_reset ();
+  rip_distance_reset ();
+
+  rip_zclient_reset ();
+}
+
+static void
+rip_if_rmap_update (struct if_rmap *if_rmap)
+{
+  struct interface *ifp;
+  struct rip_interface *ri;
+  struct route_map *rmap;
+
+  ifp = if_lookup_by_name (if_rmap->ifname);
+  if (ifp == NULL)
+    return;
+
+  ri = ifp->info;
+
+  if (if_rmap->routemap[IF_RMAP_IN])
+    {
+      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_IN]);
+      if (rmap)
+       ri->routemap[IF_RMAP_IN] = rmap;
+      else
+       ri->routemap[IF_RMAP_IN] = NULL;
+    }
+  else
+    ri->routemap[RIP_FILTER_IN] = NULL;
+
+  if (if_rmap->routemap[IF_RMAP_OUT])
+    {
+      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_OUT]);
+      if (rmap)
+       ri->routemap[IF_RMAP_OUT] = rmap;
+      else
+       ri->routemap[IF_RMAP_OUT] = NULL;
+    }
+  else
+    ri->routemap[RIP_FILTER_OUT] = NULL;
+}
+
+void
+rip_if_rmap_update_interface (struct interface *ifp)
+{
+  struct if_rmap *if_rmap;
+
+  if_rmap = if_rmap_lookup (ifp->name);
+  if (if_rmap)
+    rip_if_rmap_update (if_rmap);
+}
+
+static void
+rip_routemap_update_redistribute (void)
+{
+  int i;
+
+  if (rip)
+    {
+      for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
+       {
+         if (rip->route_map[i].name)
+           rip->route_map[i].map = 
+             route_map_lookup_by_name (rip->route_map[i].name);
+       }
+    }
+}
+
+/* ARGSUSED */
+static void
+rip_routemap_update (const char *notused)
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    rip_if_rmap_update_interface (ifp);
+
+  rip_routemap_update_redistribute ();
+}
+
+/* Allocate new rip structure and set default value. */
+void
+rip_init (void)
+{
+  /* Randomize for triggered update random(). */
+  srandom (time (NULL));
+
+  /* Install top nodes. */
+  install_node (&rip_node, config_write_rip);
+
+  /* Install rip commands. */
+  install_element (VIEW_NODE, &show_ip_rip_cmd);
+  install_element (VIEW_NODE, &show_ip_rip_status_cmd);
+  install_element (CONFIG_NODE, &router_rip_cmd);
+  install_element (CONFIG_NODE, &no_router_rip_cmd);
+
+  install_default (RIP_NODE);
+  install_element (RIP_NODE, &rip_version_cmd);
+  install_element (RIP_NODE, &no_rip_version_cmd);
+  install_element (RIP_NODE, &no_rip_version_val_cmd);
+  install_element (RIP_NODE, &rip_default_metric_cmd);
+  install_element (RIP_NODE, &no_rip_default_metric_cmd);
+  install_element (RIP_NODE, &no_rip_default_metric_val_cmd);
+  install_element (RIP_NODE, &rip_timers_cmd);
+  install_element (RIP_NODE, &no_rip_timers_cmd);
+  install_element (RIP_NODE, &no_rip_timers_val_cmd);
+  install_element (RIP_NODE, &rip_route_cmd);
+  install_element (RIP_NODE, &no_rip_route_cmd);
+  install_element (RIP_NODE, &rip_distance_cmd);
+  install_element (RIP_NODE, &no_rip_distance_cmd);
+  install_element (RIP_NODE, &rip_distance_source_cmd);
+  install_element (RIP_NODE, &no_rip_distance_source_cmd);
+  install_element (RIP_NODE, &rip_distance_source_access_list_cmd);
+  install_element (RIP_NODE, &no_rip_distance_source_access_list_cmd);
+  install_element (RIP_NODE, &rip_allow_ecmp_cmd);
+  install_element (RIP_NODE, &no_rip_allow_ecmp_cmd);
+
+  /* Debug related init. */
+  rip_debug_init ();
+
+  /* SNMP init. */
+#ifdef HAVE_SNMP
+  rip_snmp_init ();
+#endif /* HAVE_SNMP */
+
+  /* Access list install. */
+  access_list_init ();
+  access_list_add_hook (rip_distribute_update_all_wrapper);
+  access_list_delete_hook (rip_distribute_update_all_wrapper);
+
+  /* Prefix list initialize.*/
+  prefix_list_init ();
+  prefix_list_add_hook (rip_distribute_update_all);
+  prefix_list_delete_hook (rip_distribute_update_all);
+
+  /* Distribute list install. */
+  distribute_list_init (RIP_NODE);
+  distribute_list_add_hook (rip_distribute_update);
+  distribute_list_delete_hook (rip_distribute_update);
+
+  /* Route-map */
+  rip_route_map_init ();
+  rip_offset_init ();
+
+  route_map_add_hook (rip_routemap_update);
+  route_map_delete_hook (rip_routemap_update);
+
+  if_rmap_init (RIP_NODE);
+  if_rmap_hook_add (rip_if_rmap_update);
+  if_rmap_hook_delete (rip_if_rmap_update);
+
+  /* Distance control. */
+  rip_distance_table = route_table_init ();
+}
diff --git a/ripd/ripd.conf.sample b/ripd/ripd.conf.sample
new file mode 100644 (file)
index 0000000..2902ff9
--- /dev/null
@@ -0,0 +1,24 @@
+! -*- rip -*-
+!
+! RIPd sample configuration file
+!
+! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
+!
+hostname ripd
+password zebra
+!
+! debug rip events
+! debug rip packet
+!
+router rip
+! network 11.0.0.0/8
+! network eth0
+! route 10.0.0.0/8
+! distribute-list private-only in eth0
+!
+!access-list private-only permit 10.0.0.0/8
+!access-list private-only deny any
+! 
+!log file ripd.log
+!
+log stdout
diff --git a/ripd/ripd.h b/ripd/ripd.h
new file mode 100644 (file)
index 0000000..4f82525
--- /dev/null
@@ -0,0 +1,449 @@
+/* RIP related values and structures.
+ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIP_H
+#define _ZEBRA_RIP_H
+
+/* RIP version number. */
+#define RIPv1                            1
+#define RIPv2                            2
+/* N.B. stuff will break if
+       (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */
+
+
+/* RIP command list. */
+#define RIP_REQUEST                      1
+#define RIP_RESPONSE                     2
+#define RIP_TRACEON                      3     /* Obsolete */
+#define RIP_TRACEOFF                     4     /* Obsolete */
+#define RIP_POLL                         5
+#define RIP_POLL_ENTRY                   6
+#define RIP_COMMAND_MAX                  7
+
+/* RIP metric infinity value.*/
+#define RIP_METRIC_INFINITY             16
+
+/* Normal RIP packet min and max size. */
+#define RIP_PACKET_MINSIZ                4
+#define RIP_PACKET_MAXSIZ              512
+
+#define RIP_HEADER_SIZE                  4
+#define RIP_RTE_SIZE                    20
+
+/* Max count of routing table entry in one rip packet. */
+#define RIP_MAX_RTE   ((RIP_PACKET_MAXSIZ - RIP_HEADER_SIZE) / RIP_RTE_SIZE)
+
+/* RIP version 2 multicast address. */
+#ifndef INADDR_RIP_GROUP
+#define INADDR_RIP_GROUP        0xe0000009    /* 224.0.0.9 */
+#endif
+
+/* RIP timers */
+#define RIP_UPDATE_TIMER_DEFAULT        30
+#define RIP_TIMEOUT_TIMER_DEFAULT      180
+#define RIP_GARBAGE_TIMER_DEFAULT      120
+
+/* RIP peer timeout value. */
+#define RIP_PEER_TIMER_DEFAULT         180
+
+/* RIP port number. */
+#define RIP_PORT_DEFAULT               520
+#define RIP_VTY_PORT                  2602
+
+/* Default configuration file name. */
+#define RIPD_DEFAULT_CONFIG    "ripd.conf"
+
+/* RIP route types. */
+#define RIP_ROUTE_RTE                    0
+#define RIP_ROUTE_STATIC                 1
+#define RIP_ROUTE_DEFAULT                2
+#define RIP_ROUTE_REDISTRIBUTE           3
+#define RIP_ROUTE_INTERFACE              4
+
+/* RIPv2 special RTE family types */
+#define RIP_FAMILY_AUTH                  0xffff
+
+/* RIPv2 authentication types, for RIP_FAMILY_AUTH RTE's */
+#define RIP_NO_AUTH                0
+#define RIP_AUTH_DATA              1
+#define RIP_AUTH_SIMPLE_PASSWORD   2
+#define RIP_AUTH_MD5               3
+
+/* RIPv2 Simple authentication */
+#define RIP_AUTH_SIMPLE_SIZE           16
+
+/* RIPv2 MD5 authentication. */
+#define RIP_AUTH_MD5_SIZE               16
+#define RIP_AUTH_MD5_COMPAT_SIZE        RIP_RTE_SIZE
+
+/* RIP structure. */
+struct rip 
+{
+  /* RIP socket. */
+  int sock;
+
+  /* Default version of rip instance. */
+  int version_send;    /* version 1 or 2 (but not both) */
+  int version_recv;    /* version 1 or 2 or both */
+
+  /* Output buffer of RIP. */
+  struct stream *obuf;
+
+  /* RIP routing information base. */
+  struct route_table *table;
+
+  /* RIP only static routing information. */
+  struct route_table *route;
+  
+  /* RIP neighbor. */
+  struct route_table *neighbor;
+  
+  /* RIP threads. */
+  struct thread *t_read;
+
+  /* Update and garbage timer. */
+  struct thread *t_update;
+
+  /* Triggered update hack. */
+  int trigger;
+  struct thread *t_triggered_update;
+  struct thread *t_triggered_interval;
+
+  /* RIP timer values. */
+  unsigned long update_time;
+  unsigned long timeout_time;
+  unsigned long garbage_time;
+
+  /* RIP default metric. */
+  int default_metric;
+
+  /* RIP default-information originate. */
+  u_char default_information;
+  char *default_information_route_map;
+
+  /* RIP default distance. */
+  u_char distance;
+  struct route_table *distance_table;
+
+  /* RIP ECMP flag */
+  unsigned int ecmp;
+
+  /* For redistribute route map. */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+    int metric_config;
+    u_int32_t metric;
+  } route_map[ZEBRA_ROUTE_MAX];
+};
+
+/* RIP routing table entry which belong to rip_packet. */
+struct rte
+{
+  u_int16_t family;            /* Address family of this route. */
+  u_int16_t tag;               /* Route Tag which included in RIP2 packet. */
+  struct in_addr prefix;       /* Prefix of rip route. */
+  struct in_addr mask;         /* Netmask of rip route. */
+  struct in_addr nexthop;      /* Next hop of rip route. */
+  u_int32_t metric;            /* Metric value of rip route. */
+};
+
+/* RIP packet structure. */
+struct rip_packet
+{
+  unsigned char command;       /* Command type of RIP packet. */
+  unsigned char version;       /* RIP version which coming from peer. */
+  unsigned char pad1;          /* Padding of RIP packet header. */
+  unsigned char pad2;          /* Same as above. */
+  struct rte rte[1];           /* Address structure. */
+};
+
+/* Buffer to read RIP packet. */
+union rip_buf
+{
+  struct rip_packet rip_packet;
+  char buf[RIP_PACKET_MAXSIZ];
+};
+
+/* RIP route information. */
+struct rip_info
+{
+  /* This route's type. */
+  int type;
+
+  /* Sub type. */
+  int sub_type;
+
+  /* RIP nexthop. */
+  struct in_addr nexthop;
+  struct in_addr from;
+
+  /* Which interface does this route come from. */
+  ifindex_t ifindex;
+
+  /* Metric of this route. */
+  u_int32_t metric;
+
+  /* External metric of this route. 
+     if learnt from an externalm proto */
+  u_int32_t external_metric;
+
+  /* Tag information of this route. */
+  u_int16_t tag;
+
+  /* Flags of RIP route. */
+#define RIP_RTF_FIB      1
+#define RIP_RTF_CHANGED  2
+  u_char flags;
+
+  /* Garbage collect timer. */
+  struct thread *t_timeout;
+  struct thread *t_garbage_collect;
+
+  /* Route-map futures - this variables can be changed. */
+  struct in_addr nexthop_out;
+  u_char metric_set;
+  u_int32_t metric_out;
+  u_int16_t tag_out;
+  ifindex_t ifindex_out;
+
+  struct route_node *rp;
+
+  u_char distance;
+
+#ifdef NEW_RIP_TABLE
+  struct rip_info *next;
+  struct rip_info *prev;
+#endif /* NEW_RIP_TABLE */
+};
+
+typedef enum {
+  RIP_NO_SPLIT_HORIZON = 0,
+  RIP_SPLIT_HORIZON,
+  RIP_SPLIT_HORIZON_POISONED_REVERSE
+} split_horizon_policy_t;
+
+/* RIP specific interface configuration. */
+struct rip_interface
+{
+  /* RIP is enabled on this interface. */
+  int enable_network;
+  int enable_interface;
+
+  /* RIP is running on this interface. */
+  int running;
+
+  /* RIP version control. */
+  int ri_send;
+  int ri_receive;
+
+  /* RIPv2 authentication type. */
+  int auth_type;
+
+  /* RIPv2 authentication string. */
+  char *auth_str;
+
+  /* RIPv2 authentication key chain. */
+  char *key_chain;
+
+  /* value to use for md5->auth_len */
+  u_int8_t md5_auth_len;
+
+  /* Split horizon flag. */
+  split_horizon_policy_t split_horizon;
+  split_horizon_policy_t split_horizon_default;
+
+  /* For filter type slot. */
+#define RIP_FILTER_IN  0
+#define RIP_FILTER_OUT 1
+#define RIP_FILTER_MAX 2
+
+  /* Access-list. */
+  struct access_list *list[RIP_FILTER_MAX];
+
+  /* Prefix-list. */
+  struct prefix_list *prefix[RIP_FILTER_MAX];
+
+  /* Route-map. */
+  struct route_map *routemap[RIP_FILTER_MAX];
+
+  /* Wake up thread. */
+  struct thread *t_wakeup;
+
+  /* Interface statistics. */
+  int recv_badpackets;
+  int recv_badroutes;
+  int sent_updates;
+
+  /* Passive interface. */
+  int passive;
+};
+
+/* RIP peer information. */
+struct rip_peer
+{
+  /* Peer address. */
+  struct in_addr addr;
+
+  /* Peer RIP tag value. */
+  int domain;
+
+  /* Last update time. */
+  time_t uptime;
+
+  /* Peer RIP version. */
+  u_char version;
+
+  /* Statistics. */
+  int recv_badpackets;
+  int recv_badroutes;
+
+  /* Timeout thread. */
+  struct thread *t_timeout;
+};
+
+struct rip_md5_info
+{
+  u_int16_t family;
+  u_int16_t type;
+  u_int16_t packet_len;
+  u_char keyid;
+  u_char auth_len;
+  u_int32_t sequence;
+  u_int32_t reserv1;
+  u_int32_t reserv2;
+};
+
+struct rip_md5_data
+{
+  u_int16_t family;
+  u_int16_t type;
+  u_char digest[16];
+};
+
+/* RIP accepet/announce methods. */
+#define RI_RIP_UNSPEC                      0
+#define RI_RIP_VERSION_1                   1
+#define RI_RIP_VERSION_2                   2
+#define RI_RIP_VERSION_1_AND_2             3
+/* N.B. stuff will break if
+       (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */
+
+/* Default value for "default-metric" command. */
+#define RIP_DEFAULT_METRIC_DEFAULT         1
+
+/* RIP event. */
+enum rip_event 
+{
+  RIP_READ,
+  RIP_UPDATE_EVENT,
+  RIP_TRIGGERED_UPDATE,
+};
+
+/* Macro for timer turn on. */
+#define RIP_TIMER_ON(T,F,V) \
+  do { \
+    if (!(T)) \
+      (T) = thread_add_timer (master, (F), rinfo, (V)); \
+  } while (0)
+
+/* Macro for timer turn off. */
+#define RIP_TIMER_OFF(X) \
+  do { \
+    if (X) \
+      { \
+        thread_cancel (X); \
+        (X) = NULL; \
+      } \
+  } while (0)
+
+/* Prototypes. */
+extern void rip_init (void);
+extern void rip_reset (void);
+extern void rip_clean (void);
+extern void rip_clean_network (void);
+extern void rip_interfaces_clean (void);
+extern void rip_interfaces_reset (void);
+extern void rip_passive_nondefault_clean (void);
+extern void rip_if_init (void);
+extern void rip_if_down_all (void);
+extern void rip_route_map_init (void);
+extern void rip_route_map_reset (void);
+extern void rip_snmp_init (void);
+extern void rip_zclient_init (struct thread_master *);
+extern void rip_zclient_start (void);
+extern void rip_zclient_reset (void);
+extern void rip_offset_init (void);
+extern int if_check_address (struct in_addr addr);
+
+extern int rip_request_send (struct sockaddr_in *, struct interface *, u_char,
+                      struct connected *);
+extern int rip_neighbor_lookup (struct sockaddr_in *);
+
+extern int rip_redistribute_check (int);
+extern void rip_redistribute_add (int, int, struct prefix_ipv4 *, ifindex_t,
+                          struct in_addr *, unsigned int, unsigned char,
+                          route_tag_t);
+extern void rip_redistribute_delete (int, int, struct prefix_ipv4 *, ifindex_t);
+extern void rip_redistribute_withdraw (int);
+extern void rip_zebra_ipv4_add (struct route_node *);
+extern void rip_zebra_ipv4_delete (struct route_node *);
+extern void rip_interface_multicast_set (int, struct connected *);
+extern void rip_distribute_update_interface (struct interface *);
+extern void rip_if_rmap_update_interface (struct interface *);
+
+extern int config_write_rip_network (struct vty *, int);
+extern int config_write_rip_offset_list (struct vty *);
+extern int config_write_rip_redistribute (struct vty *, int);
+
+extern void rip_peer_init (void);
+extern void rip_peer_update (struct sockaddr_in *, u_char);
+extern void rip_peer_bad_route (struct sockaddr_in *);
+extern void rip_peer_bad_packet (struct sockaddr_in *);
+extern void rip_peer_display (struct vty *);
+extern struct rip_peer *rip_peer_lookup (struct in_addr *);
+extern struct rip_peer *rip_peer_lookup_next (struct in_addr *);
+
+extern int rip_offset_list_apply_in (struct prefix_ipv4 *, struct interface *, u_int32_t *);
+extern int rip_offset_list_apply_out (struct prefix_ipv4 *, struct interface *, u_int32_t *);
+extern void rip_offset_clean (void);
+
+extern void rip_info_free (struct rip_info *);
+extern u_char rip_distance_apply (struct rip_info *);
+extern void rip_redistribute_clean (void);
+extern void rip_ifaddr_add (struct interface *, struct connected *);
+extern void rip_ifaddr_delete (struct interface *, struct connected *);
+
+extern struct rip_info *rip_ecmp_add (struct rip_info *);
+extern struct rip_info *rip_ecmp_replace (struct rip_info *);
+extern struct rip_info *rip_ecmp_delete (struct rip_info *);
+
+/* There is only one rip strucutre. */
+extern struct rip *rip;
+
+/* Master thread strucutre. */
+extern struct thread_master *master;
+
+/* RIP statistics for SNMP. */
+extern long rip_global_route_changes;
+extern long rip_global_queries;
+#endif /* _ZEBRA_RIP_H */
diff --git a/ripngd/.gitignore b/ripngd/.gitignore
new file mode 100644 (file)
index 0000000..e871fae
--- /dev/null
@@ -0,0 +1,17 @@
+Makefile
+Makefile.in
+*.o
+ripngd
+ripngd.conf
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+*.a
diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am
new file mode 100644 (file)
index 0000000..df0f7d3
--- /dev/null
@@ -0,0 +1,26 @@
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(WERROR)
+
+noinst_LIBRARIES = libripng.a
+sbin_PROGRAMS = ripngd
+
+libripng_a_SOURCES = \
+       ripng_interface.c ripngd.c ripng_zebra.c ripng_route.c ripng_debug.c \
+       ripng_routemap.c ripng_offset.c ripng_peer.c ripng_nexthop.c
+
+noinst_HEADERS = \
+       ripng_debug.h ripng_route.h ripngd.h ripng_nexthop.h
+
+ripngd_SOURCES = \
+       ripng_main.c $(libripng_a_SOURCES)
+
+ripngd_LDADD = ../lib/libzebra.la @LIBCAP@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = ripngd.conf.sample
+
diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c
new file mode 100644 (file)
index 0000000..eb8ff5d
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * RIPng debug output routines
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "ripngd/ripng_debug.h"
+
+/* For debug statement. */
+unsigned long ripng_debug_event = 0;
+unsigned long ripng_debug_packet = 0;
+unsigned long ripng_debug_zebra = 0;
+
+DEFUN (show_debugging_ripng,
+       show_debugging_ripng_cmd,
+       "show debugging ripng",
+       SHOW_STR
+       DEBUG_STR
+       "RIPng configuration\n")
+{
+  vty_out (vty, "RIPng debugging status:%s", VTY_NEWLINE);
+
+  if (IS_RIPNG_DEBUG_EVENT)
+    vty_out (vty, "  RIPng event debugging is on%s", VTY_NEWLINE);
+
+  if (IS_RIPNG_DEBUG_PACKET)
+    {
+      if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV)
+       {
+         vty_out (vty, "  RIPng packet debugging is on%s",
+                  VTY_NEWLINE);
+       }
+      else
+       {
+         if (IS_RIPNG_DEBUG_SEND)
+           vty_out (vty, "  RIPng packet send debugging is on%s",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "  RIPng packet receive debugging is on%s",
+                    VTY_NEWLINE);
+       }
+    }
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    vty_out (vty, "  RIPng zebra debugging is on%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_events,
+       debug_ripng_events_cmd,
+       "debug ripng events",
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng events\n")
+{
+  ripng_debug_event = RIPNG_DEBUG_EVENT;
+  return CMD_WARNING;
+}
+
+DEFUN (debug_ripng_packet,
+       debug_ripng_packet_cmd,
+       "debug ripng packet",
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng packet\n")
+{
+  ripng_debug_packet = RIPNG_DEBUG_PACKET;
+  ripng_debug_packet |= RIPNG_DEBUG_SEND;
+  ripng_debug_packet |= RIPNG_DEBUG_RECV;
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_packet_direct,
+       debug_ripng_packet_direct_cmd,
+       "debug ripng packet (recv|send)",
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n")
+{
+  ripng_debug_packet |= RIPNG_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    ripng_debug_packet |= RIPNG_DEBUG_SEND;
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    ripng_debug_packet |= RIPNG_DEBUG_RECV;
+
+  return CMD_SUCCESS;
+}
+
+/* N.B. the "detail" modifier is a no-op.  we leave this command
+   for legacy compatibility. */
+DEFUN_DEPRECATED (debug_ripng_packet_detail,
+       debug_ripng_packet_detail_cmd,
+       "debug ripng packet (recv|send) detail",
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n"
+       "Debug option set detaied information\n")
+{
+  ripng_debug_packet |= RIPNG_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    ripng_debug_packet |= RIPNG_DEBUG_SEND;
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    ripng_debug_packet |= RIPNG_DEBUG_RECV;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_ripng_zebra,
+       debug_ripng_zebra_cmd,
+       "debug ripng zebra",
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng and zebra communication\n")
+{
+  ripng_debug_zebra = RIPNG_DEBUG_ZEBRA;
+  return CMD_WARNING;
+}
+
+DEFUN (no_debug_ripng_events,
+       no_debug_ripng_events_cmd,
+       "no debug ripng events",
+       NO_STR
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng events\n")
+{
+  ripng_debug_event = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_packet,
+       no_debug_ripng_packet_cmd,
+       "no debug ripng packet",
+       NO_STR
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng packet\n")
+{
+  ripng_debug_packet = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_packet_direct,
+       no_debug_ripng_packet_direct_cmd,
+       "no debug ripng packet (recv|send)",
+       NO_STR
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n")
+{
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    {
+      if (IS_RIPNG_DEBUG_RECV)
+       ripng_debug_packet &= ~RIPNG_DEBUG_SEND;
+      else
+       ripng_debug_packet = 0;
+    }
+  else if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    {
+      if (IS_RIPNG_DEBUG_SEND)
+       ripng_debug_packet &= ~RIPNG_DEBUG_RECV;
+      else
+       ripng_debug_packet = 0;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ripng_zebra,
+       no_debug_ripng_zebra_cmd,
+       "no debug ripng zebra",
+       NO_STR
+       DEBUG_STR
+       "RIPng configuration\n"
+       "Debug option set for ripng and zebra communication\n")
+{
+  ripng_debug_zebra = 0;
+  return CMD_WARNING;
+}
+
+/* Debug node. */
+static struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",                          /* Debug node has no interface. */
+  1 /* VTYSH */
+};
+
+static int
+config_write_debug (struct vty *vty)
+{
+  int write = 0;
+
+  if (IS_RIPNG_DEBUG_EVENT)
+    {
+      vty_out (vty, "debug ripng events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_RIPNG_DEBUG_PACKET)
+    {
+      if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV)
+       {
+         vty_out (vty, "debug ripng packet%s",
+                  VTY_NEWLINE);
+         write++;
+       }
+      else
+       {
+         if (IS_RIPNG_DEBUG_SEND)
+           vty_out (vty, "debug ripng packet send%s",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "debug ripng packet recv%s",
+                    VTY_NEWLINE);
+         write++;
+       }
+    }
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    {
+      vty_out (vty, "debug ripng zebra%s", VTY_NEWLINE);
+      write++;
+    }
+  return write;
+}
+
+void
+ripng_debug_reset ()
+{
+  ripng_debug_event = 0;
+  ripng_debug_packet = 0;
+  ripng_debug_zebra = 0;
+}
+
+void
+ripng_debug_init ()
+{
+  ripng_debug_event = 0;
+  ripng_debug_packet = 0;
+  ripng_debug_zebra = 0;
+
+  install_node (&debug_node, config_write_debug);
+
+  install_element (VIEW_NODE, &show_debugging_ripng_cmd);
+
+  install_element (ENABLE_NODE, &debug_ripng_events_cmd);
+  install_element (ENABLE_NODE, &debug_ripng_packet_cmd);
+  install_element (ENABLE_NODE, &debug_ripng_packet_direct_cmd);
+  install_element (ENABLE_NODE, &debug_ripng_packet_detail_cmd);
+  install_element (ENABLE_NODE, &debug_ripng_zebra_cmd);
+  install_element (ENABLE_NODE, &no_debug_ripng_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_ripng_packet_cmd);
+  install_element (ENABLE_NODE, &no_debug_ripng_packet_direct_cmd);
+  install_element (ENABLE_NODE, &no_debug_ripng_zebra_cmd);
+
+  install_element (CONFIG_NODE, &debug_ripng_events_cmd);
+  install_element (CONFIG_NODE, &debug_ripng_packet_cmd);
+  install_element (CONFIG_NODE, &debug_ripng_packet_direct_cmd);
+  install_element (CONFIG_NODE, &debug_ripng_packet_detail_cmd);
+  install_element (CONFIG_NODE, &debug_ripng_zebra_cmd);
+  install_element (CONFIG_NODE, &no_debug_ripng_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_ripng_packet_cmd);
+  install_element (CONFIG_NODE, &no_debug_ripng_packet_direct_cmd);
+  install_element (CONFIG_NODE, &no_debug_ripng_zebra_cmd);
+}
diff --git a/ripngd/ripng_debug.h b/ripngd/ripng_debug.h
new file mode 100644 (file)
index 0000000..674345c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * RIPng debug output routines
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIPNG_DEBUG_H
+#define _ZEBRA_RIPNG_DEBUG_H
+
+/* Debug flags. */
+#define RIPNG_DEBUG_EVENT   0x01
+
+#define RIPNG_DEBUG_PACKET  0x01
+#define RIPNG_DEBUG_SEND    0x20
+#define RIPNG_DEBUG_RECV    0x40
+
+#define RIPNG_DEBUG_ZEBRA   0x01
+
+/* Debug related macro. */
+#define IS_RIPNG_DEBUG_EVENT  (ripng_debug_event & RIPNG_DEBUG_EVENT)
+
+#define IS_RIPNG_DEBUG_PACKET (ripng_debug_packet & RIPNG_DEBUG_PACKET)
+#define IS_RIPNG_DEBUG_SEND   (ripng_debug_packet & RIPNG_DEBUG_SEND)
+#define IS_RIPNG_DEBUG_RECV   (ripng_debug_packet & RIPNG_DEBUG_RECV)
+
+#define IS_RIPNG_DEBUG_ZEBRA  (ripng_debug_zebra & RIPNG_DEBUG_ZEBRA)
+
+extern unsigned long ripng_debug_event;
+extern unsigned long ripng_debug_packet;
+extern unsigned long ripng_debug_zebra;
+
+extern void ripng_debug_init (void);
+extern void ripng_debug_reset (void);
+
+#endif /* _ZEBRA_RIPNG_DEBUG_H */
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
new file mode 100644 (file)
index 0000000..1d3757a
--- /dev/null
@@ -0,0 +1,1214 @@
+/*
+ * Interface related function for RIPng.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "memory.h"
+#include "network.h"
+#include "filter.h"
+#include "log.h"
+#include "stream.h"
+#include "zclient.h"
+#include "command.h"
+#include "table.h"
+#include "thread.h"
+#include "privs.h"
+#include "vrf.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+
+/* If RFC2133 definition is used. */
+#ifndef IPV6_JOIN_GROUP
+#define IPV6_JOIN_GROUP  IPV6_ADD_MEMBERSHIP 
+#endif
+#ifndef IPV6_LEAVE_GROUP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 
+#endif
+
+extern struct zebra_privs_t ripngd_privs;
+
+/* Static utility function. */
+static void ripng_enable_apply (struct interface *);
+static void ripng_passive_interface_apply (struct interface *);
+static int ripng_enable_if_lookup (const char *);
+static int ripng_enable_network_lookup2 (struct connected *);
+static void ripng_enable_apply_all (void);
+
+/* Join to the all rip routers multicast group. */
+static int
+ripng_multicast_join (struct interface *ifp)
+{
+  int ret;
+  struct ipv6_mreq mreq;
+  int save_errno;
+
+  if (if_is_up (ifp) && if_is_multicast (ifp)) {
+    memset (&mreq, 0, sizeof (mreq));
+    inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+    mreq.ipv6mr_interface = ifp->ifindex;
+
+    /*
+     * NetBSD 1.6.2 requires root to join groups on gif(4).
+     * While this is bogus, privs are available and easy to use
+     * for this call as a workaround.
+     */
+    if (ripngd_privs.change (ZPRIVS_RAISE))
+      zlog_err ("ripng_multicast_join: could not raise privs");
+
+    ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+                     (char *) &mreq, sizeof (mreq));
+    save_errno = errno;
+
+    if (ripngd_privs.change (ZPRIVS_LOWER))
+      zlog_err ("ripng_multicast_join: could not lower privs");
+
+    if (ret < 0 && save_errno == EADDRINUSE)
+      {
+       /*
+        * Group is already joined.  This occurs due to sloppy group
+        * management, in particular declining to leave the group on
+        * an interface that has just gone down.
+        */
+       zlog_warn ("ripng join on %s EADDRINUSE (ignoring)\n", ifp->name);
+       return 0;               /* not an error */
+      }
+
+    if (ret < 0)
+      zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s",
+                safe_strerror (save_errno));
+
+    if (IS_RIPNG_DEBUG_EVENT)
+      zlog_debug ("RIPng %s join to all-rip-routers multicast group", ifp->name);
+
+    if (ret < 0)
+      return -1;
+  }
+  return 0;
+}
+
+/* Leave from the all rip routers multicast group. */
+static int
+ripng_multicast_leave (struct interface *ifp)
+{
+  int ret;
+  struct ipv6_mreq mreq;
+
+  if (if_is_up (ifp) && if_is_multicast (ifp)) {
+    memset (&mreq, 0, sizeof (mreq));
+    inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+    mreq.ipv6mr_interface = ifp->ifindex;
+
+    ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+                     (char *) &mreq, sizeof (mreq));
+    if (ret < 0)
+      zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s\n", safe_strerror (errno));
+
+    if (IS_RIPNG_DEBUG_EVENT)
+      zlog_debug ("RIPng %s leave from all-rip-routers multicast group",
+                ifp->name);
+
+    if (ret < 0)
+      return -1;
+  }
+
+  return 0;
+}
+
+/* How many link local IPv6 address could be used on the interface ? */
+static int
+ripng_if_ipv6_lladdress_check (struct interface *ifp)
+{
+  struct listnode *nn;
+  struct connected *connected;
+  int count = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, connected))
+    {
+      struct prefix *p;
+      p = connected->address;
+
+      if ((p->family == AF_INET6) &&
+          IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6))
+        count++;
+    }
+
+  return count;
+}
+
+static int
+ripng_if_down (struct interface *ifp)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_interface *ri;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL, *nextnode = NULL;
+
+  if (ripng)
+    for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo))
+          if (rinfo->ifindex == ifp->ifindex)
+            ripng_ecmp_delete (rinfo);
+
+  ri = ifp->info;
+  
+  if (ri->running)
+   {
+     if (IS_RIPNG_DEBUG_EVENT)
+       zlog_debug ("turn off %s", ifp->name);
+
+     /* Leave from multicast group. */
+     ripng_multicast_leave (ifp);
+
+     ri->running = 0;
+   }
+
+  return 0;
+}
+
+/* Inteface link up message processing. */
+int
+ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct interface *ifp;
+
+  /* zebra_interface_state_read() updates interface structure in iflist. */
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_debug ("interface up %s index %d flags %llx metric %d mtu %d",
+              ifp->name, ifp->ifindex, (unsigned long long)ifp->flags,
+              ifp->metric, ifp->mtu6);
+
+  /* Check if this interface is RIPng enabled or not. */
+  ripng_enable_apply (ifp);
+
+  /* Check for a passive interface. */
+  ripng_passive_interface_apply (ifp);
+
+  /* Apply distribute list to the all interface. */
+  ripng_distribute_update_interface (ifp);
+
+  return 0;
+}
+
+/* Inteface link down message processing. */
+int
+ripng_interface_down (int command, struct zclient *zclient,
+                     zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct interface *ifp;
+
+  /* zebra_interface_state_read() updates interface structure in iflist. */
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  ripng_if_down (ifp);
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_debug ("interface down %s index %d flags %#llx metric %d mtu %d",
+               ifp->name, ifp->ifindex,
+               (unsigned long long) ifp->flags, ifp->metric, ifp->mtu6);
+
+  return 0;
+}
+
+/* Inteface addition message from zebra. */
+int
+ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length,
+    vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf, vrf_id);
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_debug ("RIPng interface add %s index %d flags %#llx metric %d mtu %d",
+               ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+               ifp->metric, ifp->mtu6);
+
+  /* Check is this interface is RIP enabled or not.*/
+  ripng_enable_apply (ifp);
+
+  /* Apply distribute list to the interface. */
+  ripng_distribute_update_interface (ifp);
+
+  /* Check interface routemap. */
+  ripng_if_rmap_update_interface (ifp);
+
+  return 0;
+}
+
+int
+ripng_interface_delete (int command, struct zclient *zclient,
+                       zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct interface *ifp;
+  struct stream *s;
+
+  s = zclient->ibuf;
+  /*  zebra_interface_state_read() updates interface structure in iflist */
+  ifp = zebra_interface_state_read (s, vrf_id);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (if_is_up (ifp)) {
+    ripng_if_down(ifp);
+  }
+
+  zlog_info("interface delete %s index %d flags %#llx metric %d mtu %d",
+            ifp->name, ifp->ifindex, (unsigned long long) ifp->flags,
+           ifp->metric, ifp->mtu6);
+
+  /* To support pseudo interface do not free interface structure.  */
+  /* if_delete(ifp); */
+  ifp->ifindex = IFINDEX_INTERNAL;
+
+  return 0;
+}
+
+void
+ripng_interface_clean (void)
+{
+  struct listnode *node, *nnode;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    {
+      ri = ifp->info;
+
+      ri->enable_network = 0;
+      ri->enable_interface = 0;
+      ri->running = 0;
+
+      if (ri->t_wakeup)
+        {
+          thread_cancel (ri->t_wakeup);
+          ri->t_wakeup = NULL;
+        }
+    }
+}
+
+void
+ripng_interface_reset (void)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      ri = ifp->info;
+
+      ri->enable_network = 0;
+      ri->enable_interface = 0;
+      ri->running = 0;
+
+      ri->split_horizon = RIPNG_NO_SPLIT_HORIZON;
+      ri->split_horizon_default = RIPNG_NO_SPLIT_HORIZON;
+
+      ri->list[RIPNG_FILTER_IN] = NULL;
+      ri->list[RIPNG_FILTER_OUT] = NULL;
+
+      ri->prefix[RIPNG_FILTER_IN] = NULL;
+      ri->prefix[RIPNG_FILTER_OUT] = NULL;
+
+      if (ri->t_wakeup)
+        {
+          thread_cancel (ri->t_wakeup);
+          ri->t_wakeup = NULL;
+        }
+
+      ri->passive = 0;
+    }
+}
+
+static void
+ripng_apply_address_add (struct connected *ifc) {
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  if (!ripng)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix6;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv6(&address);
+
+  /* Check if this interface is RIP enabled or not
+     or  Check if this address's prefix is RIP enabled */
+  if ((ripng_enable_if_lookup(ifc->ifp->name) >= 0) ||
+      (ripng_enable_network_lookup2(ifc) >= 0))
+    ripng_redistribute_add(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                           &address, ifc->ifp->ifindex, NULL, 0);
+
+}
+
+int
+ripng_interface_address_add (int command, struct zclient *zclient,
+                            zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *c;
+  struct prefix *p;
+
+  c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, 
+                                    zclient->ibuf, vrf_id);
+
+  if (c == NULL)
+    return 0;
+
+  p = c->address;
+
+  if (p->family == AF_INET6)
+    {
+      struct ripng_interface *ri = c->ifp->info;
+      
+      if (IS_RIPNG_DEBUG_ZEBRA)
+       zlog_debug ("RIPng connected address %s/%d add",
+                  inet6_ntoa(p->u.prefix6),
+                  p->prefixlen);
+      
+      /* Check is this prefix needs to be redistributed. */
+      ripng_apply_address_add(c);
+
+      /* Let's try once again whether the interface could be activated */
+      if (!ri->running) {
+        /* Check if this interface is RIP enabled or not.*/
+        ripng_enable_apply (c->ifp);
+
+        /* Apply distribute list to the interface. */
+        ripng_distribute_update_interface (c->ifp);
+
+        /* Check interface routemap. */
+        ripng_if_rmap_update_interface (c->ifp);
+      }
+
+    }
+
+  return 0;
+}
+
+static void
+ripng_apply_address_del (struct connected *ifc) {
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  if (!ripng)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix6;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv6(&address);
+
+  ripng_redistribute_delete(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                            &address, ifc->ifp->ifindex);
+}
+
+int
+ripng_interface_address_delete (int command, struct zclient *zclient,
+                               zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct connected *ifc;
+  struct prefix *p;
+  char buf[INET6_ADDRSTRLEN];
+
+  ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, 
+                                      zclient->ibuf, vrf_id);
+  
+  if (ifc)
+    {
+      p = ifc->address;
+
+      if (p->family == AF_INET6)
+       {
+         if (IS_RIPNG_DEBUG_ZEBRA)
+           zlog_debug ("RIPng connected address %s/%d delete",
+                      inet_ntop (AF_INET6, &p->u.prefix6, buf,
+                                 INET6_ADDRSTRLEN),
+                      p->prefixlen);
+
+         /* Check wether this prefix needs to be removed. */
+         ripng_apply_address_del(ifc);
+       }
+      connected_free (ifc);
+    }
+
+  return 0;
+}
+
+/* RIPng enable interface vector. */
+vector ripng_enable_if;
+
+/* RIPng enable network table. */
+struct route_table *ripng_enable_network;
+
+/* Lookup RIPng enable network. */
+/* Check wether the interface has at least a connected prefix that
+ * is within the ripng_enable_network table. */
+static int
+ripng_enable_network_lookup_if (struct interface *ifp)
+{
+  struct listnode *node;
+  struct connected *connected;
+  struct prefix_ipv6 address;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+    {
+      struct prefix *p; 
+      struct route_node *node;
+
+      p = connected->address;
+
+      if (p->family == AF_INET6)
+        {
+          address.family = AF_INET6;
+          address.prefix = p->u.prefix6;
+          address.prefixlen = IPV6_MAX_BITLEN;
+
+          node = route_node_match (ripng_enable_network,
+                                   (struct prefix *)&address);
+          if (node)
+            {
+              route_unlock_node (node);
+              return 1;
+            }
+        }
+    }
+  return -1;
+}
+
+/* Check wether connected is within the ripng_enable_network table. */
+static int
+ripng_enable_network_lookup2 (struct connected *connected)
+{
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  p = connected->address;
+
+  if (p->family == AF_INET6) {
+    struct route_node *node;
+
+    address.family = p->family;
+    address.prefix = p->u.prefix6;
+    address.prefixlen = IPV6_MAX_BITLEN;
+
+    /* LPM on p->family, p->u.prefix6/IPV6_MAX_BITLEN within ripng_enable_network */
+    node = route_node_match (ripng_enable_network,
+                             (struct prefix *)&address);
+
+    if (node) {
+      route_unlock_node (node);
+      return 1;
+    }
+  }
+
+  return -1;
+}
+
+/* Add RIPng enable network. */
+static int
+ripng_enable_network_add (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_get (ripng_enable_network, p);
+
+  if (node->info)
+    {
+      route_unlock_node (node);
+      return -1;
+    }
+  else
+    node->info = (char *) "enabled";
+
+  /* XXX: One should find a better solution than a generic one */
+  ripng_enable_apply_all();
+
+  return 1;
+}
+
+/* Delete RIPng enable network. */
+static int
+ripng_enable_network_delete (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_lookup (ripng_enable_network, p);
+  if (node)
+    {
+      node->info = NULL;
+
+      /* Unlock info lock. */
+      route_unlock_node (node);
+
+      /* Unlock lookup lock. */
+      route_unlock_node (node);
+
+      return 1;
+    }
+  return -1;
+}
+
+/* Lookup function. */
+static int
+ripng_enable_if_lookup (const char *ifname)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (ripng_enable_if); i++)
+    if ((str = vector_slot (ripng_enable_if, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+       return i;
+  return -1;
+}
+
+/* Add interface to ripng_enable_if. */
+static int
+ripng_enable_if_add (const char *ifname)
+{
+  int ret;
+
+  ret = ripng_enable_if_lookup (ifname);
+  if (ret >= 0)
+    return -1;
+
+  vector_set (ripng_enable_if, strdup (ifname));
+
+  ripng_enable_apply_all();
+
+  return 1;
+}
+
+/* Delete interface from ripng_enable_if. */
+static int
+ripng_enable_if_delete (const char *ifname)
+{
+  int index;
+  char *str;
+
+  index = ripng_enable_if_lookup (ifname);
+  if (index < 0)
+    return -1;
+
+  str = vector_slot (ripng_enable_if, index);
+  free (str);
+  vector_unset (ripng_enable_if, index);
+
+  ripng_enable_apply_all();
+
+  return 1;
+}
+
+/* Wake up interface. */
+static int
+ripng_interface_wakeup (struct thread *t)
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  /* Get interface. */
+  ifp = THREAD_ARG (t);
+
+  ri = ifp->info;
+  ri->t_wakeup = NULL;
+
+  /* Join to multicast group. */
+  if (ripng_multicast_join (ifp) < 0) {
+    zlog_err ("multicast join failed, interface %s not running", ifp->name);
+    return 0;
+  }
+    
+  /* Set running flag. */
+  ri->running = 1;
+
+  /* Send RIP request to the interface. */
+  ripng_request (ifp);
+
+  return 0;
+}
+
+static void
+ripng_connect_set (struct interface *ifp, int set)
+{
+  struct listnode *node, *nnode;
+  struct connected *connected;
+  struct prefix_ipv6 address;
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected))
+    {
+      struct prefix *p;
+      p = connected->address;
+
+      if (p->family != AF_INET6)
+        continue;
+
+      address.family = AF_INET6;
+      address.prefix = p->u.prefix6;
+      address.prefixlen = p->prefixlen;
+      apply_mask_ipv6 (&address);
+
+      if (set) {
+        /* Check once more wether this prefix is within a "network IF_OR_PREF" one */
+        if ((ripng_enable_if_lookup(connected->ifp->name) >= 0) ||
+            (ripng_enable_network_lookup2(connected) >= 0))
+          ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                                  &address, connected->ifp->ifindex, NULL, 0);
+      } else {
+        ripng_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                                   &address, connected->ifp->ifindex);
+        if (ripng_redistribute_check (ZEBRA_ROUTE_CONNECT))
+          ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_REDISTRIBUTE,
+                                  &address, connected->ifp->ifindex, NULL, 0);
+      }
+    }
+}
+
+/* Check RIPng is enabed on this interface. */
+void
+ripng_enable_apply (struct interface *ifp)
+{
+  int ret;
+  struct ripng_interface *ri = NULL;
+
+  /* Check interface. */
+  if (! if_is_up (ifp))
+    return;
+  
+  ri = ifp->info;
+
+  /* Is this interface a candidate for RIPng ? */
+  ret = ripng_enable_network_lookup_if (ifp);
+
+  /* If the interface is matched. */
+  if (ret > 0)
+    ri->enable_network = 1;
+  else
+    ri->enable_network = 0;
+
+  /* Check interface name configuration. */
+  ret = ripng_enable_if_lookup (ifp->name);
+  if (ret >= 0)
+    ri->enable_interface = 1;
+  else
+    ri->enable_interface = 0;
+
+  /* any candidate interface MUST have a link-local IPv6 address */
+  if ((! ripng_if_ipv6_lladdress_check (ifp)) &&
+      (ri->enable_network || ri->enable_interface)) {
+    ri->enable_network = 0;
+    ri->enable_interface = 0;
+    zlog_warn("Interface %s does not have any link-local address",
+              ifp->name);
+  }
+
+  /* Update running status of the interface. */
+  if (ri->enable_network || ri->enable_interface)
+    {
+       {
+         if (IS_RIPNG_DEBUG_EVENT)
+           zlog_debug ("RIPng turn on %s", ifp->name);
+
+         /* Add interface wake up thread. */
+         if (! ri->t_wakeup)
+           ri->t_wakeup = thread_add_timer (master, ripng_interface_wakeup,
+                                            ifp, 1);
+
+         ripng_connect_set (ifp, 1);
+       }
+    }
+  else
+    {
+      if (ri->running)
+       {
+         /* Might as well clean up the route table as well
+          * ripng_if_down sets to 0 ri->running, and displays "turn off %s"
+          **/
+         ripng_if_down(ifp);
+
+         ripng_connect_set (ifp, 0);
+       }
+    }
+}
+
+/* Set distribute list to all interfaces. */
+static void
+ripng_enable_apply_all (void)
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_enable_apply (ifp);
+}
+
+/* Clear all network and neighbor configuration */
+void
+ripng_clean_network ()
+{
+  unsigned int i;
+  char *str;
+  struct route_node *rn;
+
+  /* ripng_enable_network */
+  for (rn = route_top (ripng_enable_network); rn; rn = route_next (rn))
+    if (rn->info) {
+      rn->info = NULL;
+      route_unlock_node(rn);
+    }
+
+  /* ripng_enable_if */
+  for (i = 0; i < vector_active (ripng_enable_if); i++)
+    if ((str = vector_slot (ripng_enable_if, i)) != NULL) {
+      free (str);
+      vector_slot (ripng_enable_if, i) = NULL;
+    }
+}
+
+/* Vector to store passive-interface name. */
+vector Vripng_passive_interface;
+
+/* Utility function for looking up passive interface settings. */
+static int
+ripng_passive_interface_lookup (const char *ifname)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (Vripng_passive_interface); i++)
+    if ((str = vector_slot (Vripng_passive_interface, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+       return i;
+  return -1;
+}
+
+void
+ripng_passive_interface_apply (struct interface *ifp)
+{
+  int ret;
+  struct ripng_interface *ri;
+
+  ri = ifp->info;
+
+  ret = ripng_passive_interface_lookup (ifp->name);
+  if (ret < 0)
+    ri->passive = 0;
+  else
+    ri->passive = 1;
+}
+
+static void
+ripng_passive_interface_apply_all (void)
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_passive_interface_apply (ifp);
+}
+
+/* Passive interface. */
+static int
+ripng_passive_interface_set (struct vty *vty, const char *ifname)
+{
+  if (ripng_passive_interface_lookup (ifname) >= 0)
+    return CMD_WARNING;
+
+  vector_set (Vripng_passive_interface, strdup (ifname));
+
+  ripng_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+static int
+ripng_passive_interface_unset (struct vty *vty, const char *ifname)
+{
+  int i;
+  char *str;
+
+  i = ripng_passive_interface_lookup (ifname);
+  if (i < 0)
+    return CMD_WARNING;
+
+  str = vector_slot (Vripng_passive_interface, i);
+  free (str);
+  vector_unset (Vripng_passive_interface, i);
+
+  ripng_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+/* Free all configured RIP passive-interface settings. */
+void
+ripng_passive_interface_clean (void)
+{
+  unsigned int i;
+  char *str;
+
+  for (i = 0; i < vector_active (Vripng_passive_interface); i++)
+    if ((str = vector_slot (Vripng_passive_interface, i)) != NULL)
+      {
+       free (str);
+       vector_slot (Vripng_passive_interface, i) = NULL;
+      }
+  ripng_passive_interface_apply_all ();
+}
+
+/* Write RIPng enable network and interface to the vty. */
+int
+ripng_network_write (struct vty *vty, int config_mode)
+{
+  unsigned int i;
+  const char *ifname;
+  struct route_node *node;
+  char buf[BUFSIZ];
+
+  /* Write enable network. */
+  for (node = route_top (ripng_enable_network); node; node = route_next (node))
+    if (node->info)
+      {
+       struct prefix *p = &node->p;
+       vty_out (vty, "%s%s/%d%s", 
+                config_mode ? " network " : "    ",
+                inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+                p->prefixlen,
+                VTY_NEWLINE);
+
+      }
+  
+  /* Write enable interface. */
+  for (i = 0; i < vector_active (ripng_enable_if); i++)
+    if ((ifname = vector_slot (ripng_enable_if, i)) != NULL)
+      vty_out (vty, "%s%s%s",
+              config_mode ? " network " : "    ",
+              ifname,
+              VTY_NEWLINE);
+
+  /* Write passive interface. */
+  if (config_mode)
+    for (i = 0; i < vector_active (Vripng_passive_interface); i++)
+      if ((ifname = vector_slot (Vripng_passive_interface, i)) != NULL)
+        vty_out (vty, " passive-interface %s%s", ifname, VTY_NEWLINE);
+
+  return 0;
+}
+
+/* RIPng enable on specified interface or matched network. */
+DEFUN (ripng_network,
+       ripng_network_cmd,
+       "network IF_OR_ADDR",
+       "RIPng enable on specified interface or network.\n"
+       "Interface or address")
+{
+  int ret;
+  struct prefix p;
+
+  ret = str2prefix (argv[0], &p);
+
+  /* Given string is IPv6 network or interface name. */
+  if (ret)
+    ret = ripng_enable_network_add (&p);
+  else
+    ret = ripng_enable_if_add (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "There is same network configuration %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* RIPng enable on specified interface or matched network. */
+DEFUN (no_ripng_network,
+       no_ripng_network_cmd,
+       "no network IF_OR_ADDR",
+       NO_STR
+       "RIPng enable on specified interface or network.\n"
+       "Interface or address")
+{
+  int ret;
+  struct prefix p;
+
+  ret = str2prefix (argv[0], &p);
+
+  /* Given string is interface name. */
+  if (ret)
+    ret = ripng_enable_network_delete (&p);
+  else
+    ret = ripng_enable_if_delete (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "can't find network %s%s", argv[0],
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ripng_split_horizon,
+       ipv6_ripng_split_horizon_cmd,
+       "ipv6 ripng split-horizon",
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ripng_split_horizon_poisoned_reverse,
+       ipv6_ripng_split_horizon_poisoned_reverse_cmd,
+       "ipv6 ripng split-horizon poisoned-reverse",
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_SPLIT_HORIZON_POISONED_REVERSE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ripng_split_horizon,
+       no_ipv6_ripng_split_horizon_cmd,
+       "no ipv6 ripng split-horizon",
+       NO_STR
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_NO_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_ripng_split_horizon,
+       no_ipv6_ripng_split_horizon_poisoned_reverse_cmd,
+       "no ipv6 ripng split-horizon poisoned-reverse",
+       NO_STR
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+
+DEFUN (ripng_passive_interface,
+       ripng_passive_interface_cmd,
+       "passive-interface IFNAME",
+       "Suppress routing updates on an interface\n"
+       "Interface name\n")
+{
+  return ripng_passive_interface_set (vty, argv[0]);
+}
+
+DEFUN (no_ripng_passive_interface,
+       no_ripng_passive_interface_cmd,
+       "no passive-interface IFNAME",
+       NO_STR
+       "Suppress routing updates on an interface\n"
+       "Interface name\n")
+{
+  return ripng_passive_interface_unset (vty, argv[0]);
+}
+
+static struct ripng_interface *
+ri_new (void)
+{
+  struct ripng_interface *ri;
+  ri = XCALLOC (MTYPE_IF, sizeof (struct ripng_interface));
+
+  /* Set default split-horizon behavior.  If the interface is Frame
+     Relay or SMDS is enabled, the default value for split-horizon is
+     off.  But currently Zebra does detect Frame Relay or SMDS
+     interface.  So all interface is set to split horizon.  */
+  ri->split_horizon_default = RIPNG_SPLIT_HORIZON;
+  ri->split_horizon = ri->split_horizon_default;
+
+  return ri;
+}
+
+static int
+ripng_if_new_hook (struct interface *ifp)
+{
+  ifp->info = ri_new ();
+  return 0;
+}
+
+/* Called when interface structure deleted. */
+static int
+ripng_if_delete_hook (struct interface *ifp)
+{
+  XFREE (MTYPE_IF, ifp->info);
+  ifp->info = NULL;
+  return 0;
+}
+
+/* Configuration write function for ripngd. */
+static int
+interface_config_write (struct vty *vty)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+  int write = 0;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      ri = ifp->info;
+
+      /* Do not display the interface if there is no
+       * configuration about it.
+       **/
+      if ((!ifp->desc) &&
+          (ri->split_horizon == ri->split_horizon_default))
+        continue;
+
+      vty_out (vty, "interface %s%s", ifp->name,
+              VTY_NEWLINE);
+      if (ifp->desc)
+       vty_out (vty, " description %s%s", ifp->desc,
+                VTY_NEWLINE);
+
+      /* Split horizon. */
+      if (ri->split_horizon != ri->split_horizon_default)
+       {
+          switch (ri->split_horizon) {
+          case RIPNG_SPLIT_HORIZON:
+            vty_out (vty, " ipv6 ripng split-horizon%s", VTY_NEWLINE);
+            break;
+          case RIPNG_SPLIT_HORIZON_POISONED_REVERSE:
+            vty_out (vty, " ipv6 ripng split-horizon poisoned-reverse%s",
+                          VTY_NEWLINE);
+            break;
+          case RIPNG_NO_SPLIT_HORIZON:
+          default:
+            vty_out (vty, " no ipv6 ripng split-horizon%s", VTY_NEWLINE);
+            break;
+          }
+       }
+
+      vty_out (vty, "!%s", VTY_NEWLINE);
+
+      write++;
+    }
+  return write;
+}
+
+/* ripngd's interface node. */
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1 /* VTYSH */
+};
+
+/* Initialization of interface. */
+void
+ripng_if_init ()
+{
+  /* Interface initialize. */
+  if_add_hook (IF_NEW_HOOK, ripng_if_new_hook);
+  if_add_hook (IF_DELETE_HOOK, ripng_if_delete_hook);
+
+  /* RIPng enable network init. */
+  ripng_enable_network = route_table_init ();
+
+  /* RIPng enable interface init. */
+  ripng_enable_if = vector_init (1);
+
+  /* RIPng passive interface. */
+  Vripng_passive_interface = vector_init (1);
+
+  /* Install interface node. */
+  install_node (&interface_node, interface_config_write);
+  
+  /* Install commands. */
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_element (CONFIG_NODE, &no_interface_cmd);
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+
+  install_element (RIPNG_NODE, &ripng_network_cmd);
+  install_element (RIPNG_NODE, &no_ripng_network_cmd);
+  install_element (RIPNG_NODE, &ripng_passive_interface_cmd);
+  install_element (RIPNG_NODE, &no_ripng_passive_interface_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_poisoned_reverse_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_poisoned_reverse_cmd);
+}
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
new file mode 100644 (file)
index 0000000..1c184e2
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * RIPngd main routine.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "thread.h"
+#include "log.h"
+#include "prefix.h"
+#include "if.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "vrf.h"
+
+#include "ripngd/ripngd.h"
+
+/* Configuration filename and directory. */
+char config_default[] = SYSCONFDIR RIPNG_DEFAULT_CONFIG;
+char *config_file = NULL;
+
+/* RIPngd options. */
+struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "retain",      no_argument,       NULL, 'r'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "version",     no_argument,       NULL, 'v'},
+  { 0 }
+};
+
+/* ripngd privileges */
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_RAW,
+  ZCAP_BIND
+};
+
+struct zebra_privs_t ripngd_privs =
+{
+#if defined(QUAGGA_USER)
+  .user = QUAGGA_USER,
+#endif
+#if defined QUAGGA_GROUP
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = 2,
+  .cap_num_i = 0
+};
+
+
+/* RIPngd program name */
+
+/* Route retain mode flag. */
+int retain_mode = 0;
+
+/* RIPng VTY bind address. */
+char *vty_addr = NULL;
+
+/* RIPng VTY connection port. */
+int vty_port = RIPNG_VTY_PORT;
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_RIPNGD_PID;
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\
+Daemon which manages RIPng.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-i, --pid_file     Set process identifier file name\n\
+-z, --socket       Set path of zebra socket\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-r, --retain       When program terminates, retain added route by ripngd.\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-v, --version      Print program version\n\
+-C, --dryrun       Check configuration for validity and exit\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+  exit (status);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog_info ("SIGHUP received");
+  ripng_clean ();
+  ripng_reset ();
+
+  /* Reload config file. */
+  vty_read_config (config_file, config_default);
+  /* Create VTY's socket */
+  vty_serv_sock (vty_addr, vty_port, RIPNG_VTYSH_PATH);
+
+  /* Try to return to normal operation. */
+}
+
+/* SIGINT handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+
+  if (! retain_mode)
+    ripng_clean ();
+
+  exit (0);
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t ripng_signals[] =
+{
+  { 
+    .signal = SIGHUP, 
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};
+
+/* RIPngd main routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  int vty_port = RIPNG_VTY_PORT;
+  int daemon_mode = 0;
+  char *progname;
+  struct thread thread;
+  int dryrun = 0;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* get program name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  zlog_default = openzlog(progname, ZLOG_RIPNG,
+                         LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  while (1) 
+    {
+      int opt;
+
+      opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:vC", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+        case 'i':
+          pid_file = optarg;
+          break;
+       case 'z':
+         zclient_serv_path_set (optarg);
+         break;
+       case 'P':
+          /* Deal with atoi() returning 0 on failure, and ripngd not
+             listening on ripngd port... */
+          if (strcmp(optarg, "0") == 0) 
+            {
+              vty_port = 0;
+              break;
+            } 
+          vty_port = atoi (optarg);
+          if (vty_port <= 0 || vty_port > 0xffff)
+            vty_port = RIPNG_VTY_PORT;
+          break;
+       case 'r':
+         retain_mode = 1;
+         break;
+       case 'u':
+         ripngd_privs.user = optarg;
+         break;
+       case 'g':
+         ripngd_privs.group = optarg;
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  master = thread_master_create ();
+
+  /* Library inits. */
+  zprivs_init (&ripngd_privs);
+  signal_init (master, array_size(ripng_signals), ripng_signals);
+  cmd_init (1);
+  vty_init (master);
+  memory_init ();
+  vrf_init ();
+
+  /* RIPngd inits. */
+  ripng_init ();
+  zebra_init (master);
+  ripng_peer_init ();
+
+  /* Get configuration file. */
+  vty_read_config (config_file, config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if(dryrun)
+    return(0);
+  
+  /* Change to the daemon program. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("RIPNGd daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Create VTY socket */
+  vty_serv_sock (vty_addr, vty_port, RIPNG_VTYSH_PATH);
+
+  /* Process id file create. */
+  pid_output (pid_file);
+
+  /* Print banner. */
+  zlog_notice ("RIPNGd %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  /* Fetch next active thread. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  return 0;
+}
diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c
new file mode 100644 (file)
index 0000000..b966af0
--- /dev/null
@@ -0,0 +1,214 @@
+/* RIPngd Zebra
+ * Copyright (C) 2002 6WIND <vincent.jardin@6wind.com>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* This file is required in order to support properly the RIPng nexthop
+ * feature.
+ */
+
+#include <zebra.h>
+
+/* For struct udphdr. */
+#include <netinet/udp.h>
+
+#include "linklist.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+#include "vty.h"
+#include "if.h"
+#include "prefix.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_nexthop.h"
+
+#define DEBUG 1
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+struct ripng_rte_data {
+  struct prefix_ipv6 *p;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+};
+
+void _ripng_rte_del(struct ripng_rte_data *A);
+int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B);
+
+#define METRIC_OUT(a) \
+    ((a)->rinfo ?  (a)->rinfo->metric_out : (a)->aggregate->metric_out)
+#define NEXTHOP_OUT_PTR(a) \
+    ((a)->rinfo ?  &((a)->rinfo->nexthop_out) : &((a)->aggregate->nexthop_out))
+#define TAG_OUT(a) \
+    ((a)->rinfo ?  (a)->rinfo->tag_out : (a)->aggregate->tag_out)
+
+struct list *
+ripng_rte_new(void) {
+  struct list *rte;
+
+  rte = list_new();
+  rte->cmp = (int (*)(void *, void *)) _ripng_rte_cmp;
+  rte->del = (void (*)(void *)) _ripng_rte_del;
+
+  return rte;
+}
+
+void
+ripng_rte_free(struct list *ripng_rte_list) {
+  list_delete(ripng_rte_list);
+}
+
+/* Delete RTE */
+void
+_ripng_rte_del(struct ripng_rte_data *A) {
+  XFREE(MTYPE_RIPNG_RTE_DATA, A);
+}
+
+/* Compare RTE:
+ *  return +  if A > B
+ *         0  if A = B
+ *         -  if A < B
+ */
+int
+_ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B) {
+  return addr6_cmp(NEXTHOP_OUT_PTR(A), NEXTHOP_OUT_PTR(B));
+}
+
+/* Add routing table entry */
+void
+ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+              struct ripng_info *rinfo, struct ripng_aggregate *aggregate) {
+
+  struct ripng_rte_data *data;
+
+  /* At least one should not be null */
+  assert(!rinfo || !aggregate);
+
+  data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data));
+  data->p     = p;
+  data->rinfo = rinfo;
+  data->aggregate = aggregate;
+
+  listnode_add_sort(ripng_rte_list, data);
+} 
+
+/* Send the RTE with the nexthop support
+ */
+void
+ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+               struct sockaddr_in6 *to) {
+
+  struct ripng_rte_data *data;
+  struct listnode *node, *nnode;
+
+  struct in6_addr last_nexthop;
+  struct in6_addr myself_nexthop;
+
+  struct stream *s;
+  int num;
+  int mtu;
+  int rtemax;
+  int ret;
+
+  /* Most of the time, there is no nexthop */
+  memset(&last_nexthop, 0, sizeof(last_nexthop));
+
+  /* Use myself_nexthop if the nexthop is not a link-local address, because
+   * we remain a right path without beeing the optimal one.
+   */
+  memset(&myself_nexthop, 0, sizeof(myself_nexthop));
+
+  /* Output stream get from ripng structre.  XXX this should be
+     interface structure. */
+  s = ripng->obuf;
+
+  /* Reset stream and RTE counter. */
+  stream_reset (s);
+  num = 0;
+
+  mtu = ifp->mtu6;
+  if (mtu < 0)
+    mtu = IFMINMTU;
+
+  rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) -
+           IPV6_HDRLEN - 
+           sizeof (struct udphdr) -
+           sizeof (struct ripng_packet) +
+           sizeof (struct rte)) / sizeof (struct rte);
+
+  for (ALL_LIST_ELEMENTS (ripng_rte_list, node, nnode, data)) {
+    /* (2.1) Next hop support */
+    if (!IPV6_ADDR_SAME(&last_nexthop, NEXTHOP_OUT_PTR(data))) {
+
+      /* A nexthop entry should be at least followed by 1 RTE */
+      if (num == (rtemax-1)) {
+       ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
+                                to, ifp);
+
+        if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+          ripng_packet_dump((struct ripng_packet *)STREAM_DATA (s),
+                           stream_get_endp(s), "SEND");
+        num = 0;
+        stream_reset (s);
+      }
+
+      /* Add the nexthop (2.1) */
+
+      /* If the received next hop address is not a link-local address,
+       * it should be treated as 0:0:0:0:0:0:0:0.
+       */
+      if (!IN6_IS_ADDR_LINKLOCAL(NEXTHOP_OUT_PTR(data)))
+        last_nexthop = myself_nexthop;
+      else
+       last_nexthop = *NEXTHOP_OUT_PTR(data);
+
+      num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
+    } else {
+      /* Rewrite the nexthop for each new packet */
+      if ((num == 0) && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop))
+        num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
+    }
+    num = ripng_write_rte(num, s, data->p, NULL,
+                         TAG_OUT(data), METRIC_OUT(data));
+
+    if (num == rtemax) {
+      ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
+                              to, ifp);
+
+      if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+        ripng_packet_dump((struct ripng_packet *)STREAM_DATA (s),
+                         stream_get_endp(s), "SEND");
+      num = 0;
+      stream_reset (s);
+    }
+  }
+
+  /* If unwritten RTE exist, flush it. */
+  if (num != 0) {
+    ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
+                            to, ifp);
+
+    if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
+      ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s),
+                        stream_get_endp (s), "SEND");
+    stream_reset (s);
+  }
+}
diff --git a/ripngd/ripng_nexthop.h b/ripngd/ripng_nexthop.h
new file mode 100644 (file)
index 0000000..19bd32b
--- /dev/null
@@ -0,0 +1,64 @@
+/* RIPng nexthop support
+ * Copyright (C) 6WIND Vincent Jardin <vincent.jardin@6wind.com>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+#define _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+
+#include <zebra.h>
+#include "linklist.h"
+#include "ripngd/ripng_route.h"
+#include "ripngd/ripngd.h"
+
+extern struct list * ripng_rte_new(void);
+extern void ripng_rte_free(struct list *ripng_rte_list);
+extern void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+                          struct ripng_info *rinfo,
+                          struct ripng_aggregate *aggregate);
+extern void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+                           struct sockaddr_in6 *to);
+
+/***
+ * 1 if A > B
+ * 0 if A = B
+ * -1 if A < B
+ **/
+static inline int
+addr6_cmp(struct in6_addr *A, struct in6_addr *B)
+{
+#define a(i) A->s6_addr32[i]
+#define b(i) B->s6_addr32[i]
+
+  if (a(3) > b(3))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) > b(2)))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) > b(1)))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) > b(0)))
+    return 1;
+
+  if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) == b(0)))
+    return 0;
+
+  return -1;
+}
+
+#endif /* _ZEBRA_RIPNG_RIPNG_NEXTHOP_H */
diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c
new file mode 100644 (file)
index 0000000..5bc2568
--- /dev/null
@@ -0,0 +1,421 @@
+/* RIPng offset-list
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+ /* RIPng support by Vincent Jardin <vincent.jardin@6wind.com>
+  * Copyright (C) 2002 6WIND
+  */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "filter.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#include "ripngd/ripngd.h"
+
+#define RIPNG_OFFSET_LIST_IN  0
+#define RIPNG_OFFSET_LIST_OUT 1
+#define RIPNG_OFFSET_LIST_MAX 2
+
+struct ripng_offset_list
+{
+  char *ifname;
+
+  struct 
+  {
+    char *alist_name;
+    /* struct access_list *alist; */
+    int metric;
+  } direct[RIPNG_OFFSET_LIST_MAX];
+};
+
+static struct list *ripng_offset_list_master;
+
+static int
+strcmp_safe (const char *s1, const char *s2)
+{
+  if (s1 == NULL && s2 == NULL)
+    return 0;
+  if (s1 == NULL)
+    return -1;
+  if (s2 == NULL)
+    return 1;
+  return strcmp (s1, s2);
+}
+
+static struct ripng_offset_list *
+ripng_offset_list_new ()
+{
+  struct ripng_offset_list *new;
+
+  new = XCALLOC (MTYPE_RIPNG_OFFSET_LIST, sizeof (struct ripng_offset_list));
+  return new;
+}
+
+static void
+ripng_offset_list_free (struct ripng_offset_list *offset)
+{
+  XFREE (MTYPE_RIPNG_OFFSET_LIST, offset);
+}
+
+static struct ripng_offset_list *
+ripng_offset_list_lookup (const char *ifname)
+{
+  struct ripng_offset_list *offset;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset))
+    {
+      if (strcmp_safe (offset->ifname, ifname) == 0)
+       return offset;
+    }
+  return NULL;
+}
+
+static struct ripng_offset_list *
+ripng_offset_list_get (const char *ifname)
+{
+  struct ripng_offset_list *offset;
+  
+  offset = ripng_offset_list_lookup (ifname);
+  if (offset)
+    return offset;
+
+  offset = ripng_offset_list_new ();
+  if (ifname)
+    offset->ifname = strdup (ifname);
+  listnode_add_sort (ripng_offset_list_master, offset);
+
+  return offset;
+}
+
+static int
+ripng_offset_list_set (struct vty *vty, const char *alist,
+                      const char *direct_str, const char *metric_str,
+                      const char *ifname)
+{
+  int direct;
+  int metric;
+  struct ripng_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = ripng_offset_list_get (ifname);
+
+  if (offset->direct[direct].alist_name)
+    free (offset->direct[direct].alist_name);
+  offset->direct[direct].alist_name = strdup (alist);
+  offset->direct[direct].metric = metric;
+
+  return CMD_SUCCESS;
+}
+
+static int
+ripng_offset_list_unset (struct vty *vty, const char *alist,
+                        const char *direct_str, const char *metric_str,
+                        const char *ifname)
+{
+  int direct;
+  int metric;
+  struct ripng_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = ripng_offset_list_lookup (ifname);
+
+  if (offset)
+    {
+      if (offset->direct[direct].alist_name)
+       free (offset->direct[direct].alist_name);
+      offset->direct[direct].alist_name = NULL;
+
+      if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL &&
+         offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
+       {
+         listnode_delete (ripng_offset_list_master, offset);
+         if (offset->ifname)
+           free (offset->ifname);
+         ripng_offset_list_free (offset);
+       }
+    }
+  else
+    {
+      vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+#define OFFSET_LIST_IN_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+#define OFFSET_LIST_IN_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].metric)
+
+#define OFFSET_LIST_OUT_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+#define OFFSET_LIST_OUT_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].metric)
+
+/* If metric is modifed return 1. */
+int
+ripng_offset_list_apply_in (struct prefix_ipv6 *p, struct interface *ifp,
+                           u_char *metric)
+{
+  struct ripng_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = ripng_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_IN_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  /* Look up offset-list without interface name. */
+  offset = ripng_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_IN_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  return 0;
+}
+
+/* If metric is modifed return 1. */
+int
+ripng_offset_list_apply_out (struct prefix_ipv6 *p, struct interface *ifp,
+                            u_char *metric)
+{
+  struct ripng_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = ripng_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_OUT_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+
+  /* Look up offset-list without interface name. */
+  offset = ripng_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+         && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+       {
+         *metric += OFFSET_LIST_OUT_METRIC (offset);
+         return 1;
+       }
+      return 0;
+    }
+  return 0;
+}
+
+DEFUN (ripng_offset_list,
+       ripng_offset_list_cmd,
+       "offset-list WORD (in|out) <0-16>",
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (ripng_offset_list_ifname,
+       ripng_offset_list_ifname_cmd,
+       "offset-list WORD (in|out) <0-16> IFNAME",
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+DEFUN (no_ripng_offset_list,
+       no_ripng_offset_list_cmd,
+       "no offset-list WORD (in|out) <0-16>",
+       NO_STR
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (no_ripng_offset_list_ifname,
+       no_ripng_offset_list_ifname_cmd,
+       "no offset-list WORD (in|out) <0-16> IFNAME",
+       NO_STR
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+static int
+offset_list_cmp (struct ripng_offset_list *o1, struct ripng_offset_list *o2)
+{
+  return strcmp_safe (o1->ifname, o2->ifname);
+}
+
+static void
+offset_list_del (struct ripng_offset_list *offset)
+{
+  if (OFFSET_LIST_IN_NAME (offset))
+    free (OFFSET_LIST_IN_NAME (offset));
+  if (OFFSET_LIST_OUT_NAME (offset))
+    free (OFFSET_LIST_OUT_NAME (offset));
+  if (offset->ifname)
+    free (offset->ifname);
+  ripng_offset_list_free (offset);
+}
+
+void
+ripng_offset_init (void)
+{
+  ripng_offset_list_master = list_new ();
+  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
+
+  install_element (RIPNG_NODE, &ripng_offset_list_cmd);
+  install_element (RIPNG_NODE, &ripng_offset_list_ifname_cmd);
+  install_element (RIPNG_NODE, &no_ripng_offset_list_cmd);
+  install_element (RIPNG_NODE, &no_ripng_offset_list_ifname_cmd);
+}
+
+void
+ripng_offset_clean (void)
+{
+  list_delete (ripng_offset_list_master);
+
+  ripng_offset_list_master = list_new ();
+  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
+}
+
+int
+config_write_ripng_offset_list (struct vty *vty)
+{
+  struct listnode *node, *nnode;
+  struct ripng_offset_list *offset;
+
+  for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset))
+    {
+      if (! offset->ifname)
+       {
+         if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+           vty_out (vty, " offset-list %s in %d%s",
+                    offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
+                    offset->direct[RIPNG_OFFSET_LIST_IN].metric,
+                    VTY_NEWLINE);
+         if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+           vty_out (vty, " offset-list %s out %d%s",
+                    offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
+                    offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
+                    VTY_NEWLINE);
+       }
+      else
+       {
+         if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+           vty_out (vty, " offset-list %s in %d %s%s",
+                    offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
+                    offset->direct[RIPNG_OFFSET_LIST_IN].metric,
+                    offset->ifname, VTY_NEWLINE);
+         if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+           vty_out (vty, " offset-list %s out %d %s%s",
+                    offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
+                    offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
+                    offset->ifname, VTY_NEWLINE);
+       }
+    }
+
+  return 0;
+}
diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c
new file mode 100644 (file)
index 0000000..b12e146
--- /dev/null
@@ -0,0 +1,216 @@
+/* RIPng peer support
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* RIPng support added by Vincent Jardin <vincent.jardin@6wind.com>
+ * Copyright (C) 2002 6WIND
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nexthop.h"
+
+
+/* Linked list of RIPng peer. */
+struct list *peer_list;
+
+static struct ripng_peer *
+ripng_peer_new (void)
+{
+  return XCALLOC (MTYPE_RIPNG_PEER, sizeof (struct ripng_peer));
+}
+
+static void
+ripng_peer_free (struct ripng_peer *peer)
+{
+  XFREE (MTYPE_RIPNG_PEER, peer);
+}
+
+struct ripng_peer *
+ripng_peer_lookup (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      if (IPV6_ADDR_SAME (&peer->addr, addr))
+       return peer;
+    }
+  return NULL;
+}
+
+struct ripng_peer *
+ripng_peer_lookup_next (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      if (addr6_cmp(&peer->addr, addr) > 0) 
+       return peer;
+    }
+  return NULL;
+}
+
+/* RIPng peer is timeout.
+ * Garbage collector.
+ **/
+static int
+ripng_peer_timeout (struct thread *t)
+{
+  struct ripng_peer *peer;
+
+  peer = THREAD_ARG (t);
+  listnode_delete (peer_list, peer);
+  ripng_peer_free (peer);
+
+  return 0;
+}
+
+/* Get RIPng peer.  At the same time update timeout thread. */
+static struct ripng_peer *
+ripng_peer_get (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+
+  peer = ripng_peer_lookup (addr);
+
+  if (peer)
+    {
+      if (peer->t_timeout)
+       thread_cancel (peer->t_timeout);
+    }
+  else
+    {
+      peer = ripng_peer_new ();
+      peer->addr = *addr; /* XXX */
+      listnode_add_sort (peer_list, peer);
+    }
+
+  /* Update timeout thread. */
+  peer->t_timeout = thread_add_timer (master, ripng_peer_timeout, peer,
+                                     RIPNG_PEER_TIMER_DEFAULT);
+
+  /* Last update time set. */
+  time (&peer->uptime);
+  
+  return peer;
+}
+
+void
+ripng_peer_update (struct sockaddr_in6 *from, u_char version)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->version = version;
+}
+
+void
+ripng_peer_bad_route (struct sockaddr_in6 *from)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->recv_badroutes++;
+}
+
+void
+ripng_peer_bad_packet (struct sockaddr_in6 *from)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->recv_badpackets++;
+}
+
+/* Display peer uptime. */
+static char *
+ripng_peer_uptime (struct ripng_peer *peer, char *buf, size_t len)
+{
+  time_t uptime;
+  struct tm *tm;
+
+  /* If there is no connection has been done before print `never'. */
+  if (peer->uptime == 0)
+    {
+      snprintf (buf, len, "never   ");
+      return buf;
+    }
+
+  /* Get current time. */
+  uptime = time (NULL);
+  uptime -= peer->uptime;
+  tm = gmtime (&uptime);
+
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+
+  if (uptime < ONE_DAY_SECOND)
+    snprintf (buf, len, "%02d:%02d:%02d", 
+             tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (uptime < ONE_WEEK_SECOND)
+    snprintf (buf, len, "%dd%02dh%02dm", 
+             tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    snprintf (buf, len, "%02dw%dd%02dh", 
+             tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  return buf;
+}
+
+void
+ripng_peer_display (struct vty *vty)
+{
+  struct ripng_peer *peer;
+  struct listnode *node, *nnode;
+#define RIPNG_UPTIME_LEN 25
+  char timebuf[RIPNG_UPTIME_LEN];
+
+  for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer))
+    {
+      vty_out (vty, "    %s %s%14s %10d %10d %10d      %s%s", inet6_ntoa (peer->addr),
+               VTY_NEWLINE, " ",
+              peer->recv_badpackets, peer->recv_badroutes,
+              ZEBRA_RIPNG_DISTANCE_DEFAULT,
+              ripng_peer_uptime (peer, timebuf, RIPNG_UPTIME_LEN),
+              VTY_NEWLINE);
+    }
+}
+
+static int
+ripng_peer_list_cmp (struct ripng_peer *p1, struct ripng_peer *p2)
+{
+  return addr6_cmp(&p1->addr, &p2->addr) > 0;
+}
+
+void
+ripng_peer_init ()
+{
+  peer_list = list_new ();
+  peer_list->cmp = (int (*)(void *, void *)) ripng_peer_list_cmp;
+}
diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c
new file mode 100644 (file)
index 0000000..f26302e
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * RIPng routes function.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "if.h"
+#include "vty.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_route.h"
+
+static struct ripng_aggregate *
+ripng_aggregate_new ()
+{
+  struct ripng_aggregate *new;
+
+  new = XCALLOC (MTYPE_RIPNG_AGGREGATE, sizeof (struct ripng_aggregate));
+  return new;
+}
+
+void
+ripng_aggregate_free (struct ripng_aggregate *aggregate)
+{
+  XFREE (MTYPE_RIPNG_AGGREGATE, aggregate);
+}
+
+/* Aggregate count increment check. */
+void
+ripng_aggregate_increment (struct route_node *child, struct ripng_info *rinfo)
+{
+  struct route_node *np;
+  struct ripng_aggregate *aggregate;
+
+  for (np = child; np; np = np->parent)
+    if ((aggregate = np->aggregate) != NULL)
+      {
+       aggregate->count++;
+       rinfo->suppress++;
+      }
+}
+
+/* Aggregate count decrement check. */
+void
+ripng_aggregate_decrement (struct route_node *child, struct ripng_info *rinfo)
+{
+  struct route_node *np;
+  struct ripng_aggregate *aggregate;
+
+  for (np = child; np; np = np->parent)
+    if ((aggregate = np->aggregate) != NULL)
+      {
+       aggregate->count--;
+       rinfo->suppress--;
+      }
+}
+
+/* Aggregate count decrement check for a list. */
+void
+ripng_aggregate_decrement_list (struct route_node *child, struct list *list)
+{
+  struct route_node *np;
+  struct ripng_aggregate *aggregate;
+  struct ripng_info *rinfo = NULL;
+  struct listnode *node = NULL;
+
+  for (np = child; np; np = np->parent)
+    if ((aggregate = np->aggregate) != NULL)
+      aggregate->count -= listcount (list);
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+    rinfo->suppress--;
+}
+
+/* RIPng routes treatment. */
+int
+ripng_aggregate_add (struct prefix *p)
+{
+  struct route_node *top;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+  struct ripng_aggregate *sub;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
+
+  /* Get top node for aggregation. */
+  top = route_node_get (ripng->table, p);
+
+  /* Allocate new aggregate. */
+  aggregate = ripng_aggregate_new ();
+  aggregate->metric = 1;
+
+  top->aggregate = aggregate;
+
+  /* Suppress routes match to the aggregate. */
+  for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top))
+    {
+      /* Suppress normal route. */
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+          {
+            aggregate->count++;
+            rinfo->suppress++;
+          }
+      /* Suppress aggregate route.  This may not need. */
+      if (rp != top && (sub = rp->aggregate) != NULL)
+       {
+         aggregate->count++;
+         sub->suppress++;
+       }
+    }
+
+  return 0;
+}
+
+/* Delete RIPng static route. */
+int
+ripng_aggregate_delete (struct prefix *p)
+{
+  struct route_node *top;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+  struct ripng_aggregate *sub;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
+
+  /* Get top node for aggregation. */
+  top = route_node_get (ripng->table, p);
+
+  /* Allocate new aggregate. */
+  aggregate = top->aggregate;
+
+  /* Suppress routes match to the aggregate. */
+  for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top))
+    {
+      /* Suppress normal route. */
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+          {
+            aggregate->count--;
+            rinfo->suppress--;
+          }
+
+      if (rp != top && (sub = rp->aggregate) != NULL)
+       {
+         aggregate->count--;
+         sub->suppress--;
+       }
+    }
+
+  top->aggregate = NULL;
+  ripng_aggregate_free (aggregate);
+
+  route_unlock_node (top);
+  route_unlock_node (top);
+
+  return 0;
+}
diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h
new file mode 100644 (file)
index 0000000..9ff90aa
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * RIPng daemon
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIPNG_ROUTE_H
+#define _ZEBRA_RIPNG_ROUTE_H
+
+struct ripng_aggregate
+{
+  /* Aggregate route count. */
+  unsigned int count;
+
+  /* Suppressed route count. */
+  unsigned int suppress;
+
+  /* Metric of this route.  */
+  u_char metric;               
+
+  /* Tag field of RIPng packet.*/
+  u_int16_t tag;
+
+  /* Route-map futures - this variables can be changed. */
+  struct in6_addr nexthop_out;
+  u_char metric_set;
+  u_char metric_out;
+  u_int16_t tag_out;
+};
+
+extern void ripng_aggregate_increment (struct route_node *rp,
+                                       struct ripng_info *rinfo);
+extern void ripng_aggregate_decrement (struct route_node *rp,
+                                       struct ripng_info *rinfo);
+extern void ripng_aggregate_decrement_list (struct route_node *rp,
+                                       struct list *list);
+extern int ripng_aggregate_add (struct prefix *p);
+extern int ripng_aggregate_delete (struct prefix *p);
+extern void ripng_aggregate_free (struct ripng_aggregate *aggregate);
+
+#endif /* _ZEBRA_RIPNG_ROUTE_H */
diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c
new file mode 100644 (file)
index 0000000..c596aec
--- /dev/null
@@ -0,0 +1,707 @@
+/* RIPng routemap.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "memory.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "command.h"
+#include "sockunion.h"
+
+#include "ripngd/ripngd.h"
+
+struct rip_metric_modifier
+{
+  enum 
+  {
+    metric_increment,
+    metric_decrement,
+    metric_absolute
+  } type;
+
+  u_char metric;
+};
+
+
+static int
+ripng_route_match_add (struct vty *vty, struct route_map_index *index,
+                      const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+ripng_route_match_delete (struct vty *vty, struct route_map_index *index,
+                         const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+ripng_route_set_add (struct vty *vty, struct route_map_index *index,
+                    const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+ripng_route_set_delete (struct vty *vty, struct route_map_index *index,
+                       const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* `match metric METRIC' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_metric (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  u_int32_t *metric;
+  struct ripng_info *rinfo;
+
+  if (type == RMAP_RIPNG)
+    {
+      metric = rule;
+      rinfo = object;
+    
+      if (rinfo->metric == *metric)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match metric' match statement. `arg' is METRIC value */
+static void *
+route_match_metric_compile (const char *arg)
+{
+  u_int32_t *metric;
+
+  metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+  *metric = atoi (arg);
+
+  if(*metric > 0)
+    return metric;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, metric);
+  return NULL;
+}
+
+/* Free route map's compiled `match metric' value. */
+static void
+route_match_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+static struct route_map_rule_cmd route_match_metric_cmd =
+{
+  "metric",
+  route_match_metric,
+  route_match_metric_compile,
+  route_match_metric_free
+};
+
+/* `match interface IFNAME' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_interface (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct ripng_info *rinfo;
+  struct interface *ifp;
+  char *ifname;
+
+  if (type == RMAP_RIPNG)
+    {
+      ifname = rule;
+      ifp = if_lookup_by_name(ifname);
+
+      if (!ifp)
+       return RMAP_NOMATCH;
+
+      rinfo = object;
+
+      if (rinfo->ifindex == ifp->ifindex)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+static void *
+route_match_interface_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_interface_cmd =
+{
+  "interface",
+  route_match_interface,
+  route_match_interface_compile,
+  route_match_interface_free
+};
+
+/* `match tag TAG' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix, 
+                   route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct ripng_info *rinfo;
+
+  if (type == RMAP_RIPNG)
+    {
+      tag = rule;
+      rinfo = object;
+
+      /* The information stored by rinfo is host ordered. */
+      if (rinfo->tag == *tag)
+       return RMAP_MATCH;
+      else
+       return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+static struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free,
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+static route_map_result_t
+route_set_metric (void *rule, struct prefix *prefix, 
+                 route_map_object_t type, void *object)
+{
+  if (type == RMAP_RIPNG)
+    {
+      struct rip_metric_modifier *mod;
+      struct ripng_info *rinfo;
+
+      mod = rule;
+      rinfo = object;
+
+      if (mod->type == metric_increment)
+       rinfo->metric_out += mod->metric;
+      else if (mod->type == metric_decrement)
+       rinfo->metric_out-= mod->metric;
+      else if (mod->type == metric_absolute)
+       rinfo->metric_out = mod->metric;
+
+      if (rinfo->metric_out < 1)
+       rinfo->metric_out = 1;
+      if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+       rinfo->metric_out = RIPNG_METRIC_INFINITY;
+
+      rinfo->metric_set = 1;
+    }
+  return RMAP_OKAY;
+}
+
+/* set metric compilation. */
+static void *
+route_set_metric_compile (const char *arg)
+{
+  int len;
+  const char *pnt;
+  int type;
+  long metric;
+  char *endptr = NULL;
+  struct rip_metric_modifier *mod;
+
+  len = strlen (arg);
+  pnt = arg;
+
+  if (len == 0)
+    return NULL;
+
+  /* Examine first character. */
+  if (arg[0] == '+')
+    {
+      type = metric_increment;
+      pnt++;
+    }
+  else if (arg[0] == '-')
+    {
+      type = metric_decrement;
+      pnt++;
+    }
+  else
+    type = metric_absolute;
+
+  /* Check beginning with digit string. */
+  if (*pnt < '0' || *pnt > '9')
+    return NULL;
+
+  /* Convert string to integer. */
+  metric = strtol (pnt, &endptr, 10);
+
+  if (metric == LONG_MAX || *endptr != '\0')
+    return NULL;
+  /* Commented out by Hasso Tepper, to avoid problems in vtysh. */
+  /* if (metric < 0 || metric > RIPNG_METRIC_INFINITY) */
+  if (metric < 0)
+    return NULL;
+
+  mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, 
+                sizeof (struct rip_metric_modifier));
+  mod->type = type;
+  mod->metric = metric;
+
+  return mod;
+}
+
+/* Free route map's compiled `set metric' value. */
+static void
+route_set_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_set_metric_cmd = 
+{
+  "metric",
+  route_set_metric,
+  route_set_metric_compile,
+  route_set_metric_free,
+};
+
+/* `set ipv6 next-hop local IP_ADDRESS' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix, 
+                     route_map_object_t type, void *object)
+{
+  struct in6_addr *address;
+  struct ripng_info *rinfo;
+
+  if(type == RMAP_RIPNG)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->nexthop_out = *address;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ipv6 nexthop local' compile function.  Given string is converted
+   to struct in6_addr structure. */
+static void *
+route_set_ipv6_nexthop_local_compile (const char *arg)
+{
+  int ret;
+  struct in6_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Free route map's compiled `ipv6 nexthop local' value. */
+static void
+route_set_ipv6_nexthop_local_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ipv6 nexthop local set. */
+static struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd =
+{
+  "ipv6 next-hop local",
+  route_set_ipv6_nexthop_local,
+  route_set_ipv6_nexthop_local_compile,
+  route_set_ipv6_nexthop_local_free
+};
+
+/* `set tag TAG' */
+
+/* Set tag to object.  ojbect must be pointer to struct attr. */
+static route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix, 
+                     route_map_object_t type, void *object)
+{
+  route_tag_t *tag;
+  struct ripng_info *rinfo;
+
+  if(type == RMAP_RIPNG)
+    {
+      /* Fetch routemap's rule information. */
+      tag = rule;
+      rinfo = object;
+
+      /* Set next hop value. */ 
+      rinfo->tag_out = *tag;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_map_rule_tag_compile,
+  route_map_rule_tag_free
+};
+
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+
+DEFUN (match_metric, 
+       match_metric_cmd,
+       "match metric <0-4294967295>",
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+{
+  return ripng_route_match_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_match_metric,
+       no_match_metric_cmd,
+       "no match metric",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n")
+{
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "metric", NULL);
+
+  return ripng_route_match_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_match_metric,
+       no_match_metric_val_cmd,
+       "no match metric <0-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+
+DEFUN (match_interface,
+       match_interface_cmd,
+       "match interface WORD",
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+{
+  return ripng_route_match_add (vty, vty->index, "interface", argv[0]);
+}
+
+DEFUN (no_match_interface,
+       no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n")
+{
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "interface", NULL);
+
+  return ripng_route_match_delete (vty, vty->index, "interface", argv[0]);
+}
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+DEFUN (match_tag,
+       match_tag_cmd,
+       "match tag <1-4294967295>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+{
+  return ripng_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return ripng_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <1-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+
+/* set functions */
+
+DEFUN (set_metric,
+       set_metric_cmd,
+       "set metric <0-4294967295>",
+       "Set value\n"
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+{
+  return ripng_route_set_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_set_metric,
+       no_set_metric_cmd,
+       "no set metric",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n")
+{
+  if (argc == 0)
+    return ripng_route_set_delete (vty, vty->index, "metric", NULL);
+
+  return ripng_route_set_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_set_metric,
+       no_set_metric_val_cmd,
+       "no set metric <0-4294967295>",
+       NO_STR
+       SET_STR
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
+
+DEFUN (set_ipv6_nexthop_local,
+       set_ipv6_nexthop_local_cmd,
+       "set ipv6 next-hop local X:X::X:X",
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+{
+  union sockunion su;
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed next-hop local address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return ripng_route_set_add (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+DEFUN (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_cmd,
+       "no set ipv6 next-hop local",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n")
+{
+  if (argc == 0)
+    return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL);
+
+  return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+ALIAS (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_val_cmd,
+       "no set ipv6 next-hop local X:X::X:X",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <1-4294967295>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return ripng_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+    return ripng_route_set_delete (vty, vty->index, "tag", NULL);
+
+  return ripng_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <1-4294967295>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+void
+ripng_route_map_reset ()
+{
+  /* XXX ??? */
+  ;
+}
+
+void
+ripng_route_map_init ()
+{
+  route_map_init ();
+  route_map_init_vty ();
+
+  route_map_install_match (&route_match_metric_cmd);
+  route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_tag_cmd);
+
+  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_set (&route_set_ipv6_nexthop_local_cmd);
+  route_map_install_set (&route_set_tag_cmd);
+
+  install_element (RMAP_NODE, &match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_val_cmd);
+  install_element (RMAP_NODE, &match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
+
+  install_element (RMAP_NODE, &set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+  install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
+}
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
new file mode 100644 (file)
index 0000000..a35bc99
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ * RIPngd and zebra interface.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "stream.h"
+#include "memory.h"
+#include "routemap.h"
+#include "zclient.h"
+#include "log.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+
+/* Send ECMP routes to zebra. */
+static void
+ripng_zebra_ipv6_send (struct route_node *rp, u_char cmd)
+{
+  static struct in6_addr **nexthops = NULL;
+  static ifindex_t *ifindexes = NULL;
+  static unsigned int nexthops_len = 0;
+
+  struct list *list = (struct list *)rp->info;
+  struct zapi_ipv6 api;
+  struct listnode *listnode = NULL;
+  struct ripng_info *rinfo = NULL;
+  int count = 0;
+
+  if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT))
+    {
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_RIPNG;
+      api.flags = 0;
+      api.message = 0;
+      api.safi = SAFI_UNICAST;
+
+      if (nexthops_len < listcount (list))
+        {
+          nexthops_len = listcount (list);
+          nexthops = XREALLOC (MTYPE_TMP, nexthops,
+                               nexthops_len * sizeof (struct in6_addr *));
+          ifindexes = XREALLOC (MTYPE_TMP, ifindexes,
+                                nexthops_len * sizeof (unsigned int));
+        }
+
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+      SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          nexthops[count] = &rinfo->nexthop;
+          ifindexes[count] = rinfo->ifindex;
+          count++;
+          if (cmd == ZEBRA_IPV6_ROUTE_ADD)
+            SET_FLAG (rinfo->flags, RIPNG_RTF_FIB);
+          else
+            UNSET_FLAG (rinfo->flags, RIPNG_RTF_FIB);
+        }
+
+      api.nexthop = nexthops;
+      api.nexthop_num = count;
+      api.ifindex = ifindexes;
+      api.ifindex_num = count;
+
+      rinfo = listgetdata (listhead (list));
+
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = rinfo->metric;
+
+      if (rinfo->tag)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_TAG);
+          api.tag = rinfo->tag;
+        }
+
+      zapi_ipv6_route (cmd, zclient,
+                       (struct prefix_ipv6 *)&rp->p, &api);
+
+      if (IS_RIPNG_DEBUG_ZEBRA)
+        {
+          if (ripng->ecmp)
+            zlog_debug ("%s: %s/%d nexthops %d",
+                        (cmd == ZEBRA_IPV6_ROUTE_ADD) ? \
+                            "Install into zebra" : "Delete from zebra",
+                        inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen, count);
+          else
+            zlog_debug ("%s: %s/%d",
+                        (cmd == ZEBRA_IPV6_ROUTE_ADD) ? \
+                            "Install into zebra" : "Delete from zebra",
+                        inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen);
+        }
+    }
+}
+
+/* Add/update ECMP routes to zebra. */
+void
+ripng_zebra_ipv6_add (struct route_node *rp)
+{
+  ripng_zebra_ipv6_send (rp, ZEBRA_IPV6_ROUTE_ADD);
+}
+
+/* Delete ECMP routes from zebra. */
+void
+ripng_zebra_ipv6_delete (struct route_node *rp)
+{
+  ripng_zebra_ipv6_send (rp, ZEBRA_IPV6_ROUTE_DELETE);
+}
+
+/* Zebra route add and delete treatment. */
+static int
+ripng_zebra_read_ipv6 (int command, struct zclient *zclient,
+                      zebra_size_t length, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv6 api;
+  unsigned long ifindex;
+  struct in6_addr nexthop;
+  struct prefix_ipv6 p;
+  unsigned char plength = 0;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  memset (&nexthop, 0, sizeof (struct in6_addr));
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv6 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  plength = stream_getc (s);
+  p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      stream_get (&nexthop, s, 16);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  if (command == ZEBRA_IPV6_ROUTE_ADD)
+    ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p,
+                            ifindex, &nexthop, api.tag);
+  else
+    ripng_redistribute_delete (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex);
+
+  return 0;
+}
+
+void
+ripng_zclient_reset (void)
+{
+  zclient_reset (zclient);
+}
+
+static int
+ripng_redistribute_unset (int type)
+{
+  if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT))
+    return CMD_SUCCESS;
+
+  vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT);
+
+  if (zclient->sock > 0)
+    zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type,
+                             VRF_DEFAULT);
+
+  ripng_redistribute_withdraw (type);
+  
+  return CMD_SUCCESS;
+}
+
+int
+ripng_redistribute_check (int type)
+{
+  return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT);
+}
+
+static void
+ripng_redistribute_metric_set (int type, int metric)
+{
+  ripng->route_map[type].metric_config = 1;
+  ripng->route_map[type].metric = metric;
+}
+
+static int
+ripng_redistribute_metric_unset (int type)
+{
+  ripng->route_map[type].metric_config = 0;
+  ripng->route_map[type].metric = 0;
+  return 0;
+}
+
+static void
+ripng_redistribute_routemap_set (int type, const char *name)
+{
+  if (ripng->route_map[type].name)
+    free (ripng->route_map[type].name);
+
+  ripng->route_map[type].name = strdup (name);
+  ripng->route_map[type].map = route_map_lookup_by_name (name);
+}
+
+static void
+ripng_redistribute_routemap_unset (int type)
+{
+  if (ripng->route_map[type].name)
+    free (ripng->route_map[type].name);
+
+  ripng->route_map[type].name = NULL;
+  ripng->route_map[type].map = NULL;
+}
+
+/* Redistribution types */
+static struct {
+  int type;
+  int str_min_len;
+  const char *str;
+} redist_type[] = {
+  {ZEBRA_ROUTE_KERNEL,  1, "kernel"},
+  {ZEBRA_ROUTE_CONNECT, 1, "connected"},
+  {ZEBRA_ROUTE_STATIC,  1, "static"},
+  {ZEBRA_ROUTE_OSPF6,   1, "ospf6"},
+  {ZEBRA_ROUTE_BGP,     2, "bgp"},
+  {ZEBRA_ROUTE_BABEL,   2, "babel"},
+  {0, 0, NULL}
+};
+
+void
+ripng_redistribute_clean ()
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++)
+    {
+      if (vrf_bitmap_check (zclient->redist[redist_type[i].type], VRF_DEFAULT))
+        {
+          if (zclient->sock > 0)
+            zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE,
+                                     zclient, redist_type[i].type,
+                                     VRF_DEFAULT);
+
+          vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT);
+
+          /* Remove the routes from RIPng table. */
+          ripng_redistribute_withdraw (redist_type[i].type);
+        }
+    }
+}
+
+DEFUN (router_zebra,
+       router_zebra_cmd,
+       "router zebra",
+       "Enable a routing process\n"
+       "Make connection to zebra daemon\n")
+{
+  vty->node = ZEBRA_NODE;
+  zclient->enable = 1;
+  zclient_start (zclient);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_zebra,
+       no_router_zebra_cmd,
+       "no router zebra",
+       NO_STR
+       "Disable a routing process\n"
+       "Stop connection to zebra daemon\n")
+{
+  zclient->enable = 0;
+  zclient_stop (zclient);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_redistribute_ripng,
+       ripng_redistribute_ripng_cmd,
+       "redistribute ripng",
+       "Redistribute information from another routing protocol\n"
+       "RIPng route\n")
+{
+  vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_redistribute_ripng,
+       no_ripng_redistribute_ripng_cmd,
+       "no redistribute ripng",
+       NO_STR
+       "Redistribute information from another routing protocol\n"
+       "RIPng route\n")
+{
+  vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_redistribute_type,
+       ripng_redistribute_type_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPNGD,
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD)
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+
+  if (type < 0)
+    {
+      vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPNGD,
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD)
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+
+  if (type < 0)
+    {
+      vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_redistribute_metric_unset (type);
+  ripng_redistribute_routemap_unset (type);
+  return ripng_redistribute_unset (type);
+}
+
+
+DEFUN (ripng_redistribute_type_metric,
+       ripng_redistribute_type_metric_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16>",
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Metric\n"
+       "Metric value\n")
+{
+  int type;
+  int metric;
+
+  metric = atoi (argv[1]);
+  type = proto_redistnum(AFI_IP6, argv[0]);
+
+  if (type < 0)
+    {
+      vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_redistribute_metric_set (type, metric);
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_metric_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16>",
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Metric\n"
+       "Metric value\n")
+
+DEFUN (ripng_redistribute_type_routemap,
+       ripng_redistribute_type_routemap_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPNGD " route-map WORD",
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+
+  if (type < 0)
+    {
+      vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_redistribute_routemap_set (type, argv[1]);
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_routemap_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPNGD " route-map WORD",
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+DEFUN (ripng_redistribute_type_metric_routemap,
+       ripng_redistribute_type_metric_routemap_cmd,
+       "redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16> route-map WORD",
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Metric\n"
+       "Metric value\n"
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+{
+  int type;
+  int metric;
+
+  type = proto_redistnum(AFI_IP6, argv[0]);
+  metric = atoi (argv[1]);
+
+  if (type < 0)
+    {
+      vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_redistribute_metric_set (type, metric);
+  ripng_redistribute_routemap_set (type, argv[2]);
+  zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_metric_routemap_cmd,
+       "no redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16> route-map WORD",
+       NO_STR
+       "Redistribute\n"
+       QUAGGA_REDIST_HELP_STR_RIPNGD
+       "Route map reference\n"
+       "Pointer to route-map entries\n")
+
+void
+ripng_redistribute_write (struct vty *vty, int config_mode)
+{
+  int i;
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    if (i != zclient->redist_default &&
+        vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT))
+      {
+      if (config_mode)
+       {
+         if (ripng->route_map[i].metric_config)
+           {
+             if (ripng->route_map[i].name)
+               vty_out (vty, " redistribute %s metric %d route-map %s%s",
+                        zebra_route_string(i), ripng->route_map[i].metric,
+                       ripng->route_map[i].name, VTY_NEWLINE);
+             else
+               vty_out (vty, " redistribute %s metric %d%s",
+                       zebra_route_string(i), ripng->route_map[i].metric,
+                       VTY_NEWLINE);
+           }
+         else
+           {
+             if (ripng->route_map[i].name)
+               vty_out (vty, " redistribute %s route-map %s%s",
+                        zebra_route_string(i), ripng->route_map[i].name,
+                        VTY_NEWLINE);
+             else
+               vty_out (vty, " redistribute %s%s", zebra_route_string(i),
+                        VTY_NEWLINE);
+           }
+       }
+      else
+       vty_out (vty, "    %s", zebra_route_string(i));
+      }
+}
+
+/* RIPng configuration write function. */
+static int
+zebra_config_write (struct vty *vty)
+{
+  if (! zclient->enable)
+    {
+      vty_out (vty, "no router zebra%s", VTY_NEWLINE);
+      return 1;
+    }
+  else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT))
+    {
+      vty_out (vty, "router zebra%s", VTY_NEWLINE);
+      vty_out (vty, " no redistribute ripng%s", VTY_NEWLINE);
+      return 1;
+    }
+  return 0;
+}
+
+/* Zebra node structure. */
+static struct cmd_node zebra_node =
+{
+  ZEBRA_NODE,
+  "%s(config-router)# ",
+};
+
+static void
+ripng_zebra_connected (struct zclient *zclient)
+{
+  zclient_send_requests (zclient, VRF_DEFAULT);
+}
+
+/* Initialize zebra structure and it's commands. */
+void
+zebra_init (struct thread_master *master)
+{
+  /* Allocate zebra structure. */
+  zclient = zclient_new (master);
+  zclient_init (zclient, ZEBRA_ROUTE_RIPNG);
+
+  zclient->zebra_connected = ripng_zebra_connected;
+  zclient->interface_up = ripng_interface_up;
+  zclient->interface_down = ripng_interface_down;
+  zclient->interface_add = ripng_interface_add;
+  zclient->interface_delete = ripng_interface_delete;
+  zclient->interface_address_add = ripng_interface_address_add;
+  zclient->interface_address_delete = ripng_interface_address_delete;
+  zclient->ipv6_route_add = ripng_zebra_read_ipv6;
+  zclient->ipv6_route_delete = ripng_zebra_read_ipv6;
+  
+  /* Install zebra node. */
+  install_node (&zebra_node, zebra_config_write);
+
+  /* Install command element for zebra node. */ 
+  install_element (CONFIG_NODE, &router_zebra_cmd);
+  install_element (CONFIG_NODE, &no_router_zebra_cmd);
+  install_default (ZEBRA_NODE);
+  install_element (ZEBRA_NODE, &ripng_redistribute_ripng_cmd);
+  install_element (ZEBRA_NODE, &no_ripng_redistribute_ripng_cmd);
+
+  /* Install command elements to ripng node */
+  install_element (RIPNG_NODE, &ripng_redistribute_type_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_routemap_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_metric_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_metric_routemap_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_routemap_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_routemap_cmd);
+}
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
new file mode 100644 (file)
index 0000000..824b3a4
--- /dev/null
@@ -0,0 +1,3092 @@
+/* RIPng daemon
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "log.h"
+#include "thread.h"
+#include "memory.h"
+#include "if.h"
+#include "stream.h"
+#include "table.h"
+#include "command.h"
+#include "sockopt.h"
+#include "distribute.h"
+#include "plist.h"
+#include "routemap.h"
+#include "if_rmap.h"
+#include "privs.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_route.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_nexthop.h"
+
+/* RIPng structure which includes many parameters related to RIPng
+   protocol. If ripng couldn't active or ripng doesn't configured,
+   ripng->fd must be negative value. */
+struct ripng *ripng = NULL;
+
+enum
+{
+  ripng_all_route,
+  ripng_changed_route,
+};
+
+extern struct zebra_privs_t ripngd_privs;
+
+/* Prototypes. */
+void
+ripng_output_process (struct interface *, struct sockaddr_in6 *, int);
+
+int
+ripng_triggered_update (struct thread *);
+
+/* RIPng next hop specification. */
+struct ripng_nexthop
+{
+  enum ripng_nexthop_type
+  {
+    RIPNG_NEXTHOP_UNSPEC,
+    RIPNG_NEXTHOP_ADDRESS
+  } flag;
+  struct in6_addr address;
+};
+
+static int
+ripng_route_rte (struct ripng_info *rinfo)
+{
+  return (rinfo->type == ZEBRA_ROUTE_RIPNG && rinfo->sub_type == RIPNG_ROUTE_RTE);
+}
+
+/* Allocate new ripng information. */
+struct ripng_info *
+ripng_info_new ()
+{
+  struct ripng_info *new;
+
+  new = XCALLOC (MTYPE_RIPNG_ROUTE, sizeof (struct ripng_info));
+  return new;
+}
+
+/* Free ripng information. */
+void
+ripng_info_free (struct ripng_info *rinfo)
+{
+  XFREE (MTYPE_RIPNG_ROUTE, rinfo);
+}
+
+/* Create ripng socket. */
+static int 
+ripng_make_socket (void)
+{
+  int ret;
+  int sock;
+  struct sockaddr_in6 ripaddr;
+
+  sock = socket (AF_INET6, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't make ripng socket");
+      return sock;
+    }
+
+  ret = setsockopt_so_recvbuf (sock, 8096);
+  if (ret < 0)
+    return ret;
+  ret = setsockopt_ipv6_pktinfo (sock, 1);
+  if (ret < 0)
+    return ret;
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  ret = setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL);
+  if (ret < 0)
+    return ret;
+#endif
+  ret = setsockopt_ipv6_multicast_hops (sock, 255);
+  if (ret < 0)
+    return ret;
+  ret = setsockopt_ipv6_multicast_loop (sock, 0);
+  if (ret < 0)
+    return ret;
+  ret = setsockopt_ipv6_hoplimit (sock, 1);
+  if (ret < 0)
+    return ret;
+
+  memset (&ripaddr, 0, sizeof (ripaddr));
+  ripaddr.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+  ripaddr.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* SIN6_LEN */
+  ripaddr.sin6_port = htons (RIPNG_PORT_DEFAULT);
+
+  if (ripngd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("ripng_make_socket: could not raise privs");
+  
+  ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr));
+  if (ret < 0)
+  {
+    zlog (NULL, LOG_ERR, "Can't bind ripng socket: %s.", safe_strerror (errno));
+    if (ripngd_privs.change (ZPRIVS_LOWER))
+      zlog_err ("ripng_make_socket: could not lower privs");
+    return ret;
+  }
+  if (ripngd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("ripng_make_socket: could not lower privs");
+  return sock;
+}
+
+/* Send RIPng packet. */
+int
+ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to, 
+                  struct interface *ifp)
+{
+  int ret;
+  struct msghdr msg;
+  struct iovec iov;
+  struct cmsghdr  *cmsgptr;
+  char adata [256];
+  struct in6_pktinfo *pkt;
+  struct sockaddr_in6 addr;
+
+  if (IS_RIPNG_DEBUG_SEND) {
+    if (to)
+      zlog_debug ("send to %s", inet6_ntoa (to->sin6_addr));
+    zlog_debug ("  send interface %s", ifp->name);
+    zlog_debug ("  send packet size %d", bufsize);
+  }
+
+  memset (&addr, 0, sizeof (struct sockaddr_in6));
+  addr.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+  addr.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* SIN6_LEN */
+  addr.sin6_flowinfo = htonl (RIPNG_PRIORITY_DEFAULT);
+
+  /* When destination is specified. */
+  if (to != NULL)
+    {
+      addr.sin6_addr = to->sin6_addr;
+      addr.sin6_port = to->sin6_port;
+    }
+  else
+    {
+      inet_pton(AF_INET6, RIPNG_GROUP, &addr.sin6_addr);
+      addr.sin6_port = htons (RIPNG_PORT_DEFAULT);
+    }
+
+  msg.msg_name = (void *) &addr;
+  msg.msg_namelen = sizeof (struct sockaddr_in6);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+  iov.iov_base = buf;
+  iov.iov_len = bufsize;
+
+  cmsgptr = (struct cmsghdr *)adata;
+  cmsgptr->cmsg_len = CMSG_LEN(sizeof (struct in6_pktinfo));
+  cmsgptr->cmsg_level = IPPROTO_IPV6;
+  cmsgptr->cmsg_type = IPV6_PKTINFO;
+
+  pkt = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
+  memset (&pkt->ipi6_addr, 0, sizeof (struct in6_addr));
+  pkt->ipi6_ifindex = ifp->ifindex;
+
+  ret = sendmsg (ripng->sock, &msg, 0);
+
+  if (ret < 0) {
+    if (to)
+      zlog_err ("RIPng send fail on %s to %s: %s", ifp->name, 
+                inet6_ntoa (to->sin6_addr), safe_strerror (errno));
+    else
+      zlog_err ("RIPng send fail on %s: %s", ifp->name, safe_strerror (errno));
+  }
+
+  return ret;
+}
+
+/* Receive UDP RIPng packet from socket. */
+static int
+ripng_recv_packet (int sock, u_char *buf, int bufsize,
+                  struct sockaddr_in6 *from, ifindex_t *ifindex, 
+                  int *hoplimit)
+{
+  int ret;
+  struct msghdr msg;
+  struct iovec iov;
+  struct cmsghdr  *cmsgptr;
+  struct in6_addr dst = { .s6_addr = { 0 } };
+
+  /* Ancillary data.  This store cmsghdr and in6_pktinfo.  But at this
+     point I can't determine size of cmsghdr */
+  char adata[1024];
+
+  /* Fill in message and iovec. */
+  msg.msg_name = (void *) from;
+  msg.msg_namelen = sizeof (struct sockaddr_in6);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = sizeof adata;
+  iov.iov_base = buf;
+  iov.iov_len = bufsize;
+
+  /* If recvmsg fail return minus value. */
+  ret = recvmsg (sock, &msg, 0);
+  if (ret < 0)
+    return ret;
+
+  for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+       cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) 
+    {
+      /* I want interface index which this packet comes from. */
+      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+         cmsgptr->cmsg_type == IPV6_PKTINFO) 
+       {
+         struct in6_pktinfo *ptr;
+         
+         ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
+         *ifindex = ptr->ipi6_ifindex;
+         dst = ptr->ipi6_addr;
+
+         if (*ifindex == 0)
+           zlog_warn ("Interface index returned by IPV6_PKTINFO is zero");
+        }
+
+      /* Incoming packet's multicast hop limit. */
+      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+         cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+       {
+         int *phoplimit = (int *) CMSG_DATA (cmsgptr);
+         *hoplimit = *phoplimit;
+       }
+    }
+
+  /* Hoplimit check shold be done when destination address is
+     multicast address. */
+  if (! IN6_IS_ADDR_MULTICAST (&dst))
+    *hoplimit = -1;
+
+  return ret;
+}
+
+/* Dump rip packet */
+void
+ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
+{
+  caddr_t lim;
+  struct rte *rte;
+  const char *command_str;
+
+  /* Set command string. */
+  if (packet->command == RIPNG_REQUEST)
+    command_str = "request";
+  else if (packet->command == RIPNG_RESPONSE)
+    command_str = "response";
+  else
+    command_str = "unknown";
+
+  /* Dump packet header. */
+  zlog_debug ("%s %s version %d packet size %d", 
+            sndrcv, command_str, packet->version, size);
+
+  /* Dump each routing table entry. */
+  rte = packet->rte;
+
+  for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
+    {
+      if (rte->metric == RIPNG_METRIC_NEXTHOP)
+       zlog_debug ("  nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen);
+      else
+       zlog_debug ("  %s/%d metric %d tag %d", 
+                  inet6_ntoa (rte->addr), rte->prefixlen, 
+                  rte->metric, ntohs (rte->tag));
+    }
+}
+
+/* RIPng next hop address RTE (Route Table Entry). */
+static void
+ripng_nexthop_rte (struct rte *rte,
+                  struct sockaddr_in6 *from,
+                  struct ripng_nexthop *nexthop)
+{
+  char buf[INET6_BUFSIZ];
+
+  /* Logging before checking RTE. */
+  if (IS_RIPNG_DEBUG_RECV)
+    zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d",
+              inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen);
+
+  /* RFC2080 2.1.1 Next Hop: 
+   The route tag and prefix length in the next hop RTE must be
+   set to zero on sending and ignored on receiption.  */
+  if (ntohs (rte->tag) != 0)
+    zlog_warn ("RIPng nexthop RTE with non zero tag value %d from %s",
+              ntohs (rte->tag), inet6_ntoa (from->sin6_addr));
+
+  if (rte->prefixlen != 0)
+    zlog_warn ("RIPng nexthop RTE with non zero prefixlen value %d from %s",
+              rte->prefixlen, inet6_ntoa (from->sin6_addr));
+
+  /* Specifying a value of 0:0:0:0:0:0:0:0 in the prefix field of a
+   next hop RTE indicates that the next hop address should be the
+   originator of the RIPng advertisement.  An address specified as a
+   next hop must be a link-local address.  */
+  if (IN6_IS_ADDR_UNSPECIFIED (&rte->addr))
+    {
+      nexthop->flag = RIPNG_NEXTHOP_UNSPEC;
+      memset (&nexthop->address, 0, sizeof (struct in6_addr));
+      return;
+    }
+
+  if (IN6_IS_ADDR_LINKLOCAL (&rte->addr))
+    {
+      nexthop->flag = RIPNG_NEXTHOP_ADDRESS;
+      IPV6_ADDR_COPY (&nexthop->address, &rte->addr);
+      return;
+    }
+
+  /* The purpose of the next hop RTE is to eliminate packets being
+   routed through extra hops in the system.  It is particularly useful
+   when RIPng is not being run on all of the routers on a network.
+   Note that next hop RTE is "advisory".  That is, if the provided
+   information is ignored, a possibly sub-optimal, but absolutely
+   valid, route may be taken.  If the received next hop address is not
+   a link-local address, it should be treated as 0:0:0:0:0:0:0:0.  */
+  zlog_warn ("RIPng nexthop RTE with non link-local address %s from %s",
+            inet6_ntoa (rte->addr),
+            inet_ntop (AF_INET6, &from->sin6_addr, buf, INET6_BUFSIZ));
+
+  nexthop->flag = RIPNG_NEXTHOP_UNSPEC;
+  memset (&nexthop->address, 0, sizeof (struct in6_addr));
+
+  return;
+}
+
+/* If ifp has same link-local address then return 1. */
+static int
+ripng_lladdr_check (struct interface *ifp, struct in6_addr *addr)
+{
+  struct listnode *node;
+  struct connected *connected;
+  struct prefix *p;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+    {
+      p = connected->address;
+
+      if (p->family == AF_INET6 &&
+          IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6) &&
+          IN6_ARE_ADDR_EQUAL (&p->u.prefix6, addr))
+        return 1;
+    }
+  return 0;
+}
+
+/* RIPng route garbage collect timer. */
+static int
+ripng_garbage_collect (struct thread *t)
+{
+  struct ripng_info *rinfo;
+  struct route_node *rp;
+
+  rinfo = THREAD_ARG (t);
+  rinfo->t_garbage_collect = NULL;
+
+  /* Off timeout timer. */
+  RIPNG_TIMER_OFF (rinfo->t_timeout);
+  
+  /* Get route_node pointer. */
+  rp = rinfo->rp;
+
+  /* Unlock route_node. */
+  listnode_delete (rp->info, rinfo);
+  if (list_isempty ((struct list *)rp->info))
+    {
+      list_free (rp->info);
+      rp->info = NULL;
+      route_unlock_node (rp);
+    }
+
+  /* Free RIPng routing information. */
+  ripng_info_free (rinfo);
+
+  return 0;
+}
+
+static void ripng_timeout_update (struct ripng_info *rinfo);
+
+/* Add new route to the ECMP list.
+ * RETURN: the new entry added in the list, or NULL if it is not the first
+ *         entry and ECMP is not allowed.
+ */
+struct ripng_info *
+ripng_ecmp_add (struct ripng_info *rinfo_new)
+{
+  struct route_node *rp = rinfo_new->rp;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
+
+  if (rp->info == NULL)
+    rp->info = list_new ();
+  list = (struct list *)rp->info;
+
+  /* If ECMP is not allowed and some entry already exists in the list,
+   * do nothing. */
+  if (listcount (list) && !ripng->ecmp)
+    return NULL;
+
+  rinfo = ripng_info_new ();
+  memcpy (rinfo, rinfo_new, sizeof (struct ripng_info));
+  listnode_add (list, rinfo);
+
+  if (ripng_route_rte (rinfo))
+    {
+      ripng_timeout_update (rinfo);
+      ripng_zebra_ipv6_add (rp);
+    }
+
+  ripng_aggregate_increment (rp, rinfo);
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update. */
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Replace the ECMP list with the new route.
+ * RETURN: the new entry added in the list
+ */
+struct ripng_info *
+ripng_ecmp_replace (struct ripng_info *rinfo_new)
+{
+  struct route_node *rp = rinfo_new->rp;
+  struct list *list = (struct list *)rp->info;
+  struct ripng_info *rinfo = NULL, *tmp_rinfo = NULL;
+  struct listnode *node = NULL, *nextnode = NULL;
+
+  if (list == NULL || listcount (list) == 0)
+    return ripng_ecmp_add (rinfo_new);
+
+  /* Get the first entry */
+  rinfo = listgetdata (listhead (list));
+
+  /* Learnt route replaced by a local one. Delete it from zebra. */
+  if (ripng_route_rte (rinfo) && !ripng_route_rte (rinfo_new))
+    if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+      ripng_zebra_ipv6_delete (rp);
+
+  if (rinfo->metric != RIPNG_METRIC_INFINITY)
+    ripng_aggregate_decrement_list (rp, list);
+
+  /* Re-use the first entry, and delete the others. */
+  for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo))
+    if (tmp_rinfo != rinfo)
+      {
+        RIPNG_TIMER_OFF (tmp_rinfo->t_timeout);
+        RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect);
+        list_delete_node (list, node);
+        ripng_info_free (tmp_rinfo);
+      }
+
+  RIPNG_TIMER_OFF (rinfo->t_timeout);
+  RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+  memcpy (rinfo, rinfo_new, sizeof (struct ripng_info));
+
+  if (ripng_route_rte (rinfo))
+    {
+      ripng_timeout_update (rinfo);
+      /* The ADD message implies an update. */
+      ripng_zebra_ipv6_add (rp);
+    }
+
+  ripng_aggregate_increment (rp, rinfo);
+
+  /* Set the route change flag. */
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update. */
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Delete one route from the ECMP list.
+ * RETURN:
+ *  null - the entry is freed, and other entries exist in the list
+ *  the entry - the entry is the last one in the list; its metric is set
+ *              to INFINITY, and the garbage collector is started for it
+ */
+struct ripng_info *
+ripng_ecmp_delete (struct ripng_info *rinfo)
+{
+  struct route_node *rp = rinfo->rp;
+  struct list *list = (struct list *)rp->info;
+
+  RIPNG_TIMER_OFF (rinfo->t_timeout);
+
+  if (rinfo->metric != RIPNG_METRIC_INFINITY)
+    ripng_aggregate_decrement (rp, rinfo);
+
+  if (listcount (list) > 1)
+    {
+      /* Some other ECMP entries still exist. Just delete this entry. */
+      RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+      listnode_delete (list, rinfo);
+      if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+        /* The ADD message implies the update. */
+        ripng_zebra_ipv6_add (rp);
+      ripng_info_free (rinfo);
+      rinfo = NULL;
+    }
+  else
+    {
+      assert (rinfo == listgetdata (listhead (list)));
+
+      /* This is the only entry left in the list. We must keep it in
+       * the list for garbage collection time, with INFINITY metric. */
+
+      rinfo->metric = RIPNG_METRIC_INFINITY;
+      RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+                      ripng_garbage_collect, ripng->garbage_time);
+
+      if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+        ripng_zebra_ipv6_delete (rp);
+    }
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update. */
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Timeout RIPng routes. */
+static int
+ripng_timeout (struct thread *t)
+{
+  ripng_ecmp_delete ((struct ripng_info *)THREAD_ARG (t));
+  return 0;
+}
+
+static void
+ripng_timeout_update (struct ripng_info *rinfo)
+{
+  if (rinfo->metric != RIPNG_METRIC_INFINITY)
+    {
+      RIPNG_TIMER_OFF (rinfo->t_timeout);
+      RIPNG_TIMER_ON (rinfo->t_timeout, ripng_timeout, ripng->timeout_time);
+    }
+}
+
+static int
+ripng_filter (int ripng_distribute, struct prefix_ipv6 *p,
+              struct ripng_interface *ri)
+{
+  struct distribute *dist;
+  struct access_list *alist;
+  struct prefix_list *plist;
+  int distribute = ripng_distribute == RIPNG_FILTER_OUT ?
+      DISTRIBUTE_V6_OUT : DISTRIBUTE_V6_IN;
+  const char *inout = ripng_distribute == RIPNG_FILTER_OUT ? "out" : "in";
+
+  /* Input distribute-list filtering. */
+  if (ri->list[ripng_distribute])
+    {
+      if (access_list_apply (ri->list[ripng_distribute],
+                            (struct prefix *) p) == FILTER_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by distribute %s",
+                        inet6_ntoa (p->prefix), p->prefixlen, inout);
+         return -1;
+       }
+    }
+  if (ri->prefix[ripng_distribute])
+    {
+      if (prefix_list_apply (ri->prefix[ripng_distribute],
+                            (struct prefix *) p) == PREFIX_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by prefix-list %s",
+                        inet6_ntoa (p->prefix), p->prefixlen, inout);
+         return -1;
+       }
+    }
+
+  /* All interface filter check. */
+  dist = distribute_lookup (NULL);
+  if (dist)
+    {
+      if (dist->list[distribute])
+       {
+         alist = access_list_lookup (AFI_IP6, dist->list[distribute]);
+
+         if (alist)
+           {
+             if (access_list_apply (alist,
+                                    (struct prefix *) p) == FILTER_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by distribute %s",
+                                inet6_ntoa (p->prefix), p->prefixlen, inout);
+                 return -1;
+               }
+           }
+       }
+      if (dist->prefix[distribute])
+       {
+         plist = prefix_list_lookup (AFI_IP6, dist->prefix[distribute]);
+
+         if (plist)
+           {
+             if (prefix_list_apply (plist,
+                                    (struct prefix *) p) == PREFIX_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by prefix-list %s",
+                                inet6_ntoa (p->prefix), p->prefixlen, inout);
+                 return -1;
+               }
+           }
+       }
+    }
+  return 0;
+}
+
+/* Process RIPng route according to RFC2080. */
+static void
+ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
+                    struct ripng_nexthop *ripng_nexthop,
+                    struct interface *ifp)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_node *rp;
+  struct ripng_info *rinfo = NULL, newinfo;
+  struct ripng_interface *ri;
+  struct in6_addr *nexthop;
+  int same = 0;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
+
+  /* Make prefix structure. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  /* p.prefix = rte->addr; */
+  IPV6_ADDR_COPY (&p.prefix, &rte->addr);
+  p.prefixlen = rte->prefixlen;
+
+  /* Make sure mask is applied. */
+  /* XXX We have to check the prefix is valid or not before call
+     apply_mask_ipv6. */
+  apply_mask_ipv6 (&p);
+
+  /* Apply input filters. */
+  ri = ifp->info;
+
+  ret = ripng_filter (RIPNG_FILTER_IN, &p, ri);
+  if (ret < 0)
+    return;
+
+  memset (&newinfo, 0, sizeof (newinfo));
+  newinfo.type = ZEBRA_ROUTE_RIPNG;
+  newinfo.sub_type = RIPNG_ROUTE_RTE;
+  if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
+    newinfo.nexthop = ripng_nexthop->address;
+  else
+    newinfo.nexthop = from->sin6_addr;
+  newinfo.from = from->sin6_addr;
+  newinfo.ifindex = ifp->ifindex;
+  newinfo.metric = rte->metric;
+  newinfo.metric_out = rte->metric; /* XXX */
+  newinfo.tag = ntohs (rte->tag);   /* XXX */
+
+  /* Modify entry. */
+  if (ri->routemap[RIPNG_FILTER_IN])
+    {
+      int ret;
+
+      ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN], 
+                            (struct prefix *)&p, RMAP_RIPNG, &newinfo);
+
+      if (ret == RMAP_DENYMATCH)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("RIPng %s/%d is filtered by route-map in",
+                      inet6_ntoa (p.prefix), p.prefixlen);
+         return;
+       }
+
+      /* Get back the object */
+      if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) {
+       if (! IPV6_ADDR_SAME(&newinfo.nexthop, &ripng_nexthop->address) ) {
+         /* the nexthop get changed by the routemap */
+         if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop))
+           ripng_nexthop->address = newinfo.nexthop;
+         else
+           ripng_nexthop->address = in6addr_any;
+       }
+      } else {
+       if (! IPV6_ADDR_SAME(&newinfo.nexthop, &from->sin6_addr) ) {
+         /* the nexthop get changed by the routemap */
+         if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop)) {
+           ripng_nexthop->flag = RIPNG_NEXTHOP_ADDRESS;
+           ripng_nexthop->address = newinfo.nexthop;
+         }
+       }
+      }
+      rte->tag     = htons(newinfo.tag_out); /* XXX */
+      rte->metric  = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */
+    }
+
+  /* Once the entry has been validated, update the metric by
+   * adding the cost of the network on wich the message
+   * arrived. If the result is greater than infinity, use infinity
+   * (RFC2453 Sec. 3.9.2)
+   **/
+  /* Zebra ripngd can handle offset-list in. */
+  ret = ripng_offset_list_apply_in (&p, ifp, &rte->metric);
+
+  /* If offset-list does not modify the metric use interface's
+   * one. */
+  if (! ret)
+    rte->metric += ifp->metric ? ifp->metric : 1;
+
+  if (rte->metric > RIPNG_METRIC_INFINITY)
+    rte->metric = RIPNG_METRIC_INFINITY;
+
+  /* Set nexthop pointer. */
+  if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
+    nexthop = &ripng_nexthop->address;
+  else
+    nexthop = &from->sin6_addr;
+
+  /* Lookup RIPng routing table. */
+  rp = route_node_get (ripng->table, (struct prefix *) &p);
+
+  newinfo.rp = rp;
+  newinfo.nexthop = *nexthop;
+  newinfo.metric = rte->metric;
+  newinfo.tag = ntohs (rte->tag);
+
+  /* Check to see whether there is already RIPng route on the table. */
+  if ((list = rp->info) != NULL)
+    for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+      {
+        /* Need to compare with redistributed entry or local entry */
+        if (!ripng_route_rte (rinfo))
+          break;
+
+        if (IPV6_ADDR_SAME (&rinfo->from, &from->sin6_addr) &&
+            IPV6_ADDR_SAME (&rinfo->nexthop, nexthop))
+          break;
+
+        if (!listnextnode (node))
+          {
+            /* Not found in the list */
+
+            if (rte->metric > rinfo->metric)
+              {
+                /* New route has a greater metric. Discard it. */
+                route_unlock_node (rp);
+                return;
+              }
+
+            if (rte->metric < rinfo->metric)
+              /* New route has a smaller metric. Replace the ECMP list
+               * with the new one in below. */
+              break;
+
+            /* Metrics are same. Keep "rinfo" null and the new route
+             * is added in the ECMP list in below. */
+          }
+      }
+
+  if (rinfo)
+    {
+      /* Redistributed route check. */
+      if (rinfo->type != ZEBRA_ROUTE_RIPNG
+         && rinfo->metric != RIPNG_METRIC_INFINITY)
+        {
+          route_unlock_node (rp);
+          return;
+        }
+
+      /* Local static route. */
+      if (rinfo->type == ZEBRA_ROUTE_RIPNG
+         && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) ||
+             (rinfo->sub_type == RIPNG_ROUTE_DEFAULT))
+         && rinfo->metric != RIPNG_METRIC_INFINITY)
+        {
+          route_unlock_node (rp);
+          return;
+        }
+    }
+
+  if (!rinfo)
+    {
+      /* Now, check to see whether there is already an explicit route
+        for the destination prefix.  If there is no such route, add
+        this route to the routing table, unless the metric is
+        infinity (there is no point in adding a route which
+        unusable). */
+      if (rte->metric != RIPNG_METRIC_INFINITY)
+        ripng_ecmp_add (&newinfo);
+    }
+  else
+    {
+      /* If there is an existing route, compare the next hop address
+        to the address of the router from which the datagram came.
+        If this datagram is from the same router as the existing
+        route, reinitialize the timeout.  */
+      same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) 
+             && (rinfo->ifindex == ifp->ifindex));
+
+      /* Next, compare the metrics.  If the datagram is from the same
+        router as the existing route, and the new metric is different
+        than the old one; or, if the new metric is lower than the old
+        one; do the following actions: */
+      if ((same && rinfo->metric != rte->metric) ||
+         rte->metric < rinfo->metric)
+       {
+          if (listcount (list) == 1)
+            {
+              if (newinfo.metric != RIPNG_METRIC_INFINITY)
+                ripng_ecmp_replace (&newinfo);
+              else
+                ripng_ecmp_delete (rinfo);
+            }
+          else
+            {
+              if (newinfo.metric < rinfo->metric)
+                ripng_ecmp_replace (&newinfo);
+              else /* newinfo.metric > rinfo->metric */
+                ripng_ecmp_delete (rinfo);
+            }
+       }
+      else /* same & no change */
+        ripng_timeout_update (rinfo);
+
+      /* Unlock tempolary lock of the route. */
+      route_unlock_node (rp);
+    }
+}
+
+/* Add redistributed route to RIPng table. */
+void
+ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, 
+                       ifindex_t ifindex, struct in6_addr *nexthop,
+                       route_tag_t tag)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo = NULL, newinfo;
+  struct list *list = NULL;
+
+  /* Redistribute route  */
+  if (IN6_IS_ADDR_LINKLOCAL (&p->prefix))
+    return;
+  if (IN6_IS_ADDR_LOOPBACK (&p->prefix))
+    return;
+
+  rp = route_node_get (ripng->table, (struct prefix *) p);
+
+  memset (&newinfo, 0, sizeof (struct ripng_info));
+  newinfo.type = type;
+  newinfo.sub_type = sub_type;
+  newinfo.ifindex = ifindex;
+  newinfo.metric = 1;
+  if (tag <= UINT16_MAX) /* RIPng only supports 16 bit tags */
+    newinfo.tag = tag;
+  newinfo.rp = rp;
+  if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
+    newinfo.nexthop = *nexthop;
+
+  if ((list = rp->info) != NULL && listcount (list) != 0)
+    {
+      rinfo = listgetdata (listhead (list));
+
+      if (rinfo->type == ZEBRA_ROUTE_CONNECT
+          && rinfo->sub_type == RIPNG_ROUTE_INTERFACE
+         && rinfo->metric != RIPNG_METRIC_INFINITY) {
+        route_unlock_node (rp);
+          return;
+      }
+
+      /* Manually configured RIPng route check.
+       * They have the precedence on all the other entries.
+       **/
+      if (rinfo->type == ZEBRA_ROUTE_RIPNG
+          && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) ||
+              (rinfo->sub_type == RIPNG_ROUTE_DEFAULT)) ) {
+        if (type != ZEBRA_ROUTE_RIPNG || ((sub_type != RIPNG_ROUTE_STATIC) &&
+                                          (sub_type != RIPNG_ROUTE_DEFAULT))) {
+         route_unlock_node (rp);
+         return;
+       }
+      }
+
+      rinfo = ripng_ecmp_replace (&newinfo);
+      route_unlock_node (rp);
+    }
+  else
+    rinfo = ripng_ecmp_add (&newinfo);
+
+  if (IS_RIPNG_DEBUG_EVENT) {
+    if (!nexthop)
+      zlog_debug ("Redistribute new prefix %s/%d on the interface %s",
+                 inet6_ntoa(p->prefix), p->prefixlen,
+                 ifindex2ifname(ifindex));
+    else
+      zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s",
+                 inet6_ntoa(p->prefix), p->prefixlen, inet6_ntoa(*nexthop),
+                 ifindex2ifname(ifindex));
+  }
+
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+}
+
+/* Delete redistributed route to RIPng table. */
+void
+ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, 
+                          ifindex_t ifindex)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+
+  if (IN6_IS_ADDR_LINKLOCAL (&p->prefix))
+    return;
+  if (IN6_IS_ADDR_LOOPBACK (&p->prefix))
+    return;
+
+  rp = route_node_lookup (ripng->table, (struct prefix *) p);
+
+  if (rp)
+    {
+      struct list *list = rp->info;
+
+      if (list != NULL && listcount (list) != 0)
+        {
+          rinfo = listgetdata (listhead (list));
+          if (rinfo != NULL
+              && rinfo->type == type
+              && rinfo->sub_type == sub_type
+              && rinfo->ifindex == ifindex)
+            {
+              /* Perform poisoned reverse. */
+              rinfo->metric = RIPNG_METRIC_INFINITY;
+              RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+                              ripng_garbage_collect, ripng->garbage_time);
+              RIPNG_TIMER_OFF (rinfo->t_timeout);
+
+              /* Aggregate count decrement. */
+              ripng_aggregate_decrement (rp, rinfo);
+
+              rinfo->flags |= RIPNG_RTF_CHANGED;
+
+              if (IS_RIPNG_DEBUG_EVENT)
+                zlog_debug ("Poisone %s/%d on the interface %s with an "
+                            "infinity metric [delete]",
+                            inet6_ntoa (p->prefix), p->prefixlen,
+                            ifindex2ifname (ifindex));
+
+              ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+            }
+        }
+      route_unlock_node (rp);
+    }
+}
+
+/* Withdraw redistributed route. */
+void
+ripng_redistribute_withdraw (int type)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
+
+  if (!ripng)
+    return;
+  
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL)
+      {
+       rinfo = listgetdata (listhead (list));
+       if ((rinfo->type == type)
+           && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE))
+         {
+           /* Perform poisoned reverse. */
+           rinfo->metric = RIPNG_METRIC_INFINITY;
+           RIPNG_TIMER_ON (rinfo->t_garbage_collect, 
+                         ripng_garbage_collect, ripng->garbage_time);
+           RIPNG_TIMER_OFF (rinfo->t_timeout);
+
+           /* Aggregate count decrement. */
+           ripng_aggregate_decrement (rp, rinfo);
+
+           rinfo->flags |= RIPNG_RTF_CHANGED;
+
+           if (IS_RIPNG_DEBUG_EVENT) {
+             struct prefix_ipv6 *p = (struct prefix_ipv6 *) &rp->p;
+
+             zlog_debug ("Poisone %s/%d on the interface %s [withdraw]",
+                        inet6_ntoa(p->prefix), p->prefixlen,
+                        ifindex2ifname(rinfo->ifindex));
+           }
+
+           ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+         }
+      }
+}
+
+/* RIP routing information. */
+static void
+ripng_response_process (struct ripng_packet *packet, int size, 
+                       struct sockaddr_in6 *from, struct interface *ifp,
+                       int hoplimit)
+{
+  caddr_t lim;
+  struct rte *rte;
+  struct ripng_nexthop nexthop;
+
+  /* RFC2080 2.4.2  Response Messages:
+   The Response must be ignored if it is not from the RIPng port.  */
+  if (ntohs (from->sin6_port) != RIPNG_PORT_DEFAULT)
+    {
+      zlog_warn ("RIPng packet comes from non RIPng port %d from %s",
+                ntohs (from->sin6_port), inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
+      return;
+    }
+
+  /* The datagram's IPv6 source address should be checked to see
+   whether the datagram is from a valid neighbor; the source of the
+   datagram must be a link-local address.  */
+  if (! IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr))
+   {
+      zlog_warn ("RIPng packet comes from non link local address %s",
+                inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
+      return;
+    }
+
+  /* It is also worth checking to see whether the response is from one
+   of the router's own addresses.  Interfaces on broadcast networks
+   may receive copies of their own multicasts immediately.  If a
+   router processes its own output as new input, confusion is likely,
+   and such datagrams must be ignored. */
+  if (ripng_lladdr_check (ifp, &from->sin6_addr))
+    {
+      zlog_warn ("RIPng packet comes from my own link local address %s",
+                inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
+      return;
+    }
+
+  /* As an additional check, periodic advertisements must have their
+   hop counts set to 255, and inbound, multicast packets sent from the
+   RIPng port (i.e. periodic advertisement or triggered update
+   packets) must be examined to ensure that the hop count is 255. */
+  if (hoplimit >= 0 && hoplimit != 255)
+    {
+      zlog_warn ("RIPng packet comes with non 255 hop count %d from %s",
+                hoplimit, inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
+      return;
+    }
+
+  /* Update RIPng peer. */
+  ripng_peer_update (from, packet->version);
+  
+  /* Reset nexthop. */
+  memset (&nexthop, 0, sizeof (struct ripng_nexthop));
+  nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
+
+  /* Set RTE pointer. */
+  rte = packet->rte;
+
+  for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++) 
+    {
+      /* First of all, we have to check this RTE is next hop RTE or
+         not.  Next hop RTE is completely different with normal RTE so
+         we need special treatment. */
+      if (rte->metric == RIPNG_METRIC_NEXTHOP)
+       {
+         ripng_nexthop_rte (rte, from, &nexthop);
+         continue;
+       }
+
+      /* RTE information validation. */
+
+      /* - is the destination prefix valid (e.g., not a multicast
+         prefix and not a link-local address) A link-local address
+         should never be present in an RTE. */
+      if (IN6_IS_ADDR_MULTICAST (&rte->addr))
+       {
+         zlog_warn ("Destination prefix is a multicast address %s/%d [%d]",
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
+         continue;
+       }
+      if (IN6_IS_ADDR_LINKLOCAL (&rte->addr))
+       {
+         zlog_warn ("Destination prefix is a link-local address %s/%d [%d]",
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
+         continue;
+       }
+      if (IN6_IS_ADDR_LOOPBACK (&rte->addr))
+       {
+         zlog_warn ("Destination prefix is a loopback address %s/%d [%d]",
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
+         continue;
+       }
+
+      /* - is the prefix length valid (i.e., between 0 and 128,
+         inclusive) */
+      if (rte->prefixlen > 128)
+       {
+         zlog_warn ("Invalid prefix length %s/%d from %s%%%s",
+                    inet6_ntoa (rte->addr), rte->prefixlen,
+                    inet6_ntoa (from->sin6_addr), ifp->name);
+         ripng_peer_bad_route (from);
+         continue;
+       }
+
+      /* - is the metric valid (i.e., between 1 and 16, inclusive) */
+      if (! (rte->metric >= 1 && rte->metric <= 16))
+       {
+         zlog_warn ("Invalid metric %d from %s%%%s", rte->metric,
+                    inet6_ntoa (from->sin6_addr), ifp->name);
+         ripng_peer_bad_route (from);
+         continue;
+       }
+
+      /* Vincent: XXX Should we compute the direclty reachable nexthop
+       * for our RIPng network ?
+       **/
+
+      /* Routing table updates. */
+      ripng_route_process (rte, from, &nexthop, ifp);
+    }
+}
+
+/* Response to request message. */
+static void
+ripng_request_process (struct ripng_packet *packet,int size, 
+                      struct sockaddr_in6 *from, struct interface *ifp)
+{
+  caddr_t lim;
+  struct rte *rte;
+  struct prefix_ipv6 p;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_interface *ri;
+
+  /* Does not reponse to the requests on the loopback interfaces */
+  if (if_is_loopback (ifp))
+    return;
+
+  /* Check RIPng process is enabled on this interface. */
+  ri = ifp->info;
+  if (! ri->running)
+    return;
+
+  /* When passive interface is specified, suppress responses */
+  if (ri->passive)
+    return;
+
+  /* RIPng peer update. */
+  ripng_peer_update (from, packet->version);
+
+  lim = ((caddr_t) packet) + size;
+  rte = packet->rte;
+
+  /* The Request is processed entry by entry.  If there are no
+     entries, no response is given. */
+  if (lim == (caddr_t) rte)
+    return;
+
+  /* There is one special case.  If there is exactly one entry in the
+     request, and it has a destination prefix of zero, a prefix length
+     of zero, and a metric of infinity (i.e., 16), then this is a
+     request to send the entire routing table.  In that case, a call
+     is made to the output process to send the routing table to the
+     requesting address/port. */
+  if (lim == ((caddr_t) (rte + 1)) &&
+      IN6_IS_ADDR_UNSPECIFIED (&rte->addr) &&
+      rte->prefixlen == 0 &&
+      rte->metric == RIPNG_METRIC_INFINITY)
+    {  
+      /* All route with split horizon */
+      ripng_output_process (ifp, from, ripng_all_route);
+    }
+  else
+    {
+      /* Except for this special case, processing is quite simple.
+        Examine the list of RTEs in the Request one by one.  For each
+        entry, look up the destination in the router's routing
+        database and, if there is a route, put that route's metric in
+        the metric field of the RTE.  If there is no explicit route
+        to the specified destination, put infinity in the metric
+        field.  Once all the entries have been filled in, change the
+        command from Request to Response and send the datagram back
+        to the requestor. */
+      memset (&p, 0, sizeof (struct prefix_ipv6));
+      p.family = AF_INET6;
+
+      for (; ((caddr_t) rte) < lim; rte++)
+       {
+         p.prefix = rte->addr;
+         p.prefixlen = rte->prefixlen;
+         apply_mask_ipv6 (&p);
+         
+         rp = route_node_lookup (ripng->table, (struct prefix *) &p);
+
+         if (rp)
+           {
+             rinfo = listgetdata (listhead ((struct list *)rp->info));
+             rte->metric = rinfo->metric;
+             route_unlock_node (rp);
+           }
+         else
+           rte->metric = RIPNG_METRIC_INFINITY;
+       }
+      packet->command = RIPNG_RESPONSE;
+
+      ripng_send_packet ((caddr_t) packet, size, from, ifp);
+    }
+}
+
+/* First entry point of reading RIPng packet. */
+static int
+ripng_read (struct thread *thread)
+{
+  int len;
+  int sock;
+  struct sockaddr_in6 from;
+  struct ripng_packet *packet;
+  ifindex_t ifindex = 0;
+  struct interface *ifp;
+  int hoplimit = -1;
+
+  /* Check ripng is active and alive. */
+  assert (ripng != NULL);
+  assert (ripng->sock >= 0);
+
+  /* Fetch thread data and set read pointer to empty for event
+     managing.  `sock' sould be same as ripng->sock. */
+  sock = THREAD_FD (thread);
+  ripng->t_read = NULL;
+
+  /* Add myself to the next event. */
+  ripng_event (RIPNG_READ, sock);
+
+  /* Read RIPng packet. */
+  len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf), 
+                          STREAM_SIZE (ripng->ibuf), &from, &ifindex,
+                          &hoplimit);
+  if (len < 0) 
+    {
+      zlog_warn ("RIPng recvfrom failed: %s.", safe_strerror (errno));
+      return len;
+    }
+
+  /* Check RTE boundary.  RTE size (Packet length - RIPng header size
+     (4)) must be multiple size of one RTE size (20). */
+  if (((len - 4) % 20) != 0)
+    {
+      zlog_warn ("RIPng invalid packet size %d from %s", len,
+                inet6_ntoa (from.sin6_addr));
+      ripng_peer_bad_packet (&from);
+      return 0;
+    }
+
+  packet = (struct ripng_packet *) STREAM_DATA (ripng->ibuf);
+  ifp = if_lookup_by_index (ifindex);
+
+  /* RIPng packet received. */
+  if (IS_RIPNG_DEBUG_EVENT)
+    zlog_debug ("RIPng packet received from %s port %d on %s",
+              inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port), 
+              ifp ? ifp->name : "unknown");
+
+  /* Logging before packet checking. */
+  if (IS_RIPNG_DEBUG_RECV)
+    ripng_packet_dump (packet, len, "RECV");
+
+  /* Packet comes from unknown interface. */
+  if (ifp == NULL)
+    {
+      zlog_warn ("RIPng packet comes from unknown interface %d", ifindex);
+      return 0;
+    }
+
+  /* Packet version mismatch checking. */
+  if (packet->version != ripng->version) 
+    {
+      zlog_warn ("RIPng packet version %d doesn't fit to my version %d", 
+                packet->version, ripng->version);
+      ripng_peer_bad_packet (&from);
+      return 0;
+    }
+
+  /* Process RIPng packet. */
+  switch (packet->command)
+    {
+    case RIPNG_REQUEST:
+      ripng_request_process (packet, len, &from, ifp);
+      break;
+    case RIPNG_RESPONSE:
+      ripng_response_process (packet, len, &from, ifp, hoplimit);
+      break;
+    default:
+      zlog_warn ("Invalid RIPng command %d", packet->command);
+      ripng_peer_bad_packet (&from);
+      break;
+    }
+  return 0;
+}
+
+/* Walk down the RIPng routing table then clear changed flag. */
+static void
+ripng_clear_changed_flag (void)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          UNSET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+          /* This flag can be set only on the first entry. */
+          break;
+        }
+}
+
+/* Regular update of RIPng route.  Send all routing formation to RIPng
+   enabled interface. */
+static int
+ripng_update (struct thread *t)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  /* Clear update timer thread. */
+  ripng->t_update = NULL;
+
+  /* Logging update event. */
+  if (IS_RIPNG_DEBUG_EVENT)
+    zlog_debug ("RIPng update timer expired!");
+
+  /* Supply routes to each interface. */
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      ri = ifp->info;
+
+      if (if_is_loopback (ifp) || ! if_is_up (ifp))
+       continue;
+
+      if (! ri->running)
+       continue;
+
+      /* When passive interface is specified, suppress announce to the
+         interface. */
+      if (ri->passive)
+       continue;
+
+#if RIPNG_ADVANCED
+      if (ri->ri_send == RIPNG_SEND_OFF)
+       {
+         if (IS_RIPNG_DEBUG_EVENT)
+           zlog (NULL, LOG_DEBUG, 
+                 "[Event] RIPng send to if %d is suppressed by config",
+                ifp->ifindex);
+         continue;
+       }
+#endif /* RIPNG_ADVANCED */
+
+      ripng_output_process (ifp, NULL, ripng_all_route);
+    }
+
+  /* Triggered updates may be suppressed if a regular update is due by
+     the time the triggered update would be sent. */
+  if (ripng->t_triggered_interval)
+    {
+      thread_cancel (ripng->t_triggered_interval);
+      ripng->t_triggered_interval = NULL;
+    }
+  ripng->trigger = 0;
+
+  /* Reset flush event. */
+  ripng_event (RIPNG_UPDATE_EVENT, 0);
+
+  return 0;
+}
+
+/* Triggered update interval timer. */
+static int
+ripng_triggered_interval (struct thread *t)
+{
+  ripng->t_triggered_interval = NULL;
+
+  if (ripng->trigger)
+    {
+      ripng->trigger = 0;
+      ripng_triggered_update (t);
+    }
+  return 0;
+}     
+
+/* Execute triggered update. */
+int
+ripng_triggered_update (struct thread *t)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+  int interval;
+
+  ripng->t_triggered_update = NULL;
+
+  /* Cancel interval timer. */
+  if (ripng->t_triggered_interval)
+    {
+      thread_cancel (ripng->t_triggered_interval);
+      ripng->t_triggered_interval = NULL;
+    }
+  ripng->trigger = 0;
+
+  /* Logging triggered update. */
+  if (IS_RIPNG_DEBUG_EVENT)
+    zlog_debug ("RIPng triggered update!");
+
+  /* Split Horizon processing is done when generating triggered
+     updates as well as normal updates (see section 2.6). */
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      ri = ifp->info;
+
+      if (if_is_loopback (ifp) || ! if_is_up (ifp))
+       continue;
+
+      if (! ri->running)
+       continue;
+
+      /* When passive interface is specified, suppress announce to the
+         interface. */
+      if (ri->passive)
+       continue;
+
+      ripng_output_process (ifp, NULL, ripng_changed_route);
+    }
+
+  /* Once all of the triggered updates have been generated, the route
+     change flags should be cleared. */
+  ripng_clear_changed_flag ();
+
+  /* After a triggered update is sent, a timer should be set for a
+     random interval between 1 and 5 seconds.  If other changes that
+     would trigger updates occur before the timer expires, a single
+     update is triggered when the timer expires. */
+  interval = (random () % 5) + 1;
+
+  ripng->t_triggered_interval = 
+    thread_add_timer (master, ripng_triggered_interval, NULL, interval);
+
+  return 0;
+}
+
+/* Write routing table entry to the stream and return next index of
+   the routing table entry in the stream. */
+int
+ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
+                struct in6_addr *nexthop, u_int16_t tag, u_char metric)
+{
+  /* RIPng packet header. */
+  if (num == 0)
+    {
+      stream_putc (s, RIPNG_RESPONSE);
+      stream_putc (s, RIPNG_V1);
+      stream_putw (s, 0);
+    }
+
+  /* Write routing table entry. */
+  if (!nexthop)
+    stream_write (s, (u_char *) &p->prefix, sizeof (struct in6_addr));
+  else
+    stream_write (s, (u_char *) nexthop, sizeof (struct in6_addr));
+  stream_putw (s, tag);
+  if (p)
+    stream_putc (s, p->prefixlen);
+  else
+    stream_putc (s, 0);
+  stream_putc (s, metric);
+
+  return ++num;
+}
+
+/* Send RESPONSE message to specified destination. */
+void
+ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
+                     int route_type)
+{
+  int ret;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_interface *ri;
+  struct ripng_aggregate *aggregate;
+  struct prefix_ipv6 *p;
+  struct list * ripng_rte_list;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  if (IS_RIPNG_DEBUG_EVENT) {
+    if (to)
+      zlog_debug ("RIPng update routes to neighbor %s",
+                 inet6_ntoa(to->sin6_addr));
+    else
+      zlog_debug ("RIPng update routes on interface %s", ifp->name);
+  }
+
+  /* Get RIPng interface. */
+  ri = ifp->info;
+  ripng_rte_list = ripng_rte_new();
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    {
+      if ((list = rp->info) != NULL &&
+          (rinfo = listgetdata (listhead (list))) != NULL &&
+          rinfo->suppress == 0)
+       {
+         /* If no route-map are applied, the RTE will be these following
+          * informations.
+          */
+         p = (struct prefix_ipv6 *) &rp->p;
+         rinfo->metric_out = rinfo->metric;
+         rinfo->tag_out    = rinfo->tag;
+         memset(&rinfo->nexthop_out, 0, sizeof(rinfo->nexthop_out));
+         /* In order to avoid some local loops,
+          * if the RIPng route has a nexthop via this interface, keep the nexthop,
+          * otherwise set it to 0. The nexthop should not be propagated
+          * beyond the local broadcast/multicast area in order
+          * to avoid an IGP multi-level recursive look-up.
+          */
+         if (rinfo->ifindex == ifp->ifindex)
+           rinfo->nexthop_out = rinfo->nexthop;
+
+         /* Apply output filters. */
+         ret = ripng_filter (RIPNG_FILTER_OUT, p, ri);
+         if (ret < 0)
+           continue;
+
+         /* Changed route only output. */
+         if (route_type == ripng_changed_route &&
+             (! (rinfo->flags & RIPNG_RTF_CHANGED)))
+           continue;
+
+         /* Split horizon. */
+         if (ri->split_horizon == RIPNG_SPLIT_HORIZON)
+         {
+           /* We perform split horizon for RIPng routes. */
+           int suppress = 0;
+           struct ripng_info *tmp_rinfo = NULL;
+
+           for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+             if (tmp_rinfo->type == ZEBRA_ROUTE_RIPNG &&
+                 tmp_rinfo->ifindex == ifp->ifindex)
+               {
+                 suppress = 1;
+                 break;
+               }
+           if (suppress)
+             continue;
+         }
+
+         /* Preparation for route-map. */
+         rinfo->metric_set = 0;
+         /* nexthop_out,
+          * metric_out
+          * and tag_out are already initialized.
+          */
+
+         /* Interface route-map */
+         if (ri->routemap[RIPNG_FILTER_OUT])
+           {
+             int ret;
+
+             ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], 
+                                    (struct prefix *) p, RMAP_RIPNG, 
+                                    rinfo);
+
+             if (ret == RMAP_DENYMATCH)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("RIPng %s/%d is filtered by route-map out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
+               }
+
+           }
+
+         /* Redistribute route-map. */
+         if (ripng->route_map[rinfo->type].name)
+           {
+             int ret;
+
+             ret = route_map_apply (ripng->route_map[rinfo->type].map,
+                                    (struct prefix *) p, RMAP_RIPNG,
+                                    rinfo);
+
+             if (ret == RMAP_DENYMATCH)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("RIPng %s/%d is filtered by route-map",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
+               }
+           }
+
+         /* When the route-map does not set metric. */
+         if (! rinfo->metric_set)
+           {
+             /* If the redistribute metric is set. */
+             if (ripng->route_map[rinfo->type].metric_config
+                 && rinfo->metric != RIPNG_METRIC_INFINITY)
+               {
+                 rinfo->metric_out = ripng->route_map[rinfo->type].metric;
+               }
+             else
+               {
+                 /* If the route is not connected or localy generated
+                    one, use default-metric value */
+                 if (rinfo->type != ZEBRA_ROUTE_RIPNG
+                     && rinfo->type != ZEBRA_ROUTE_CONNECT
+                     && rinfo->metric != RIPNG_METRIC_INFINITY)
+                   rinfo->metric_out = ripng->default_metric;
+               }
+           }
+
+          /* Apply offset-list */
+         if (rinfo->metric_out != RIPNG_METRIC_INFINITY)
+            ripng_offset_list_apply_out (p, ifp, &rinfo->metric_out);
+
+          if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+            rinfo->metric_out = RIPNG_METRIC_INFINITY;
+
+         /* Perform split-horizon with poisoned reverse 
+          * for RIPng routes.
+          **/
+         if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) {
+           struct ripng_info *tmp_rinfo = NULL;
+
+           for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+             if ((tmp_rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+                  tmp_rinfo->ifindex == ifp->ifindex)
+               rinfo->metric_out = RIPNG_METRIC_INFINITY;
+         }
+
+         /* Add RTE to the list */
+         ripng_rte_add(ripng_rte_list, p, rinfo, NULL);
+       }
+
+      /* Process the aggregated RTE entry */
+      if ((aggregate = rp->aggregate) != NULL && 
+         aggregate->count > 0 && 
+         aggregate->suppress == 0)
+       {
+         /* If no route-map are applied, the RTE will be these following
+          * informations.
+          */
+         p = (struct prefix_ipv6 *) &rp->p;
+         aggregate->metric_set = 0;
+         aggregate->metric_out = aggregate->metric;
+         aggregate->tag_out    = aggregate->tag;
+         memset(&aggregate->nexthop_out, 0, sizeof(aggregate->nexthop_out));
+
+         /* Apply output filters.*/
+         ret = ripng_filter (RIPNG_FILTER_OUT, p, ri);
+         if (ret < 0)
+           continue;
+
+         /* Interface route-map */
+         if (ri->routemap[RIPNG_FILTER_OUT])
+           {
+             int ret;
+             struct ripng_info newinfo;
+
+             /* let's cast the aggregate structure to ripng_info */
+             memset (&newinfo, 0, sizeof (struct ripng_info));
+             /* the nexthop is :: */
+             newinfo.metric = aggregate->metric;
+             newinfo.metric_out = aggregate->metric_out;
+             newinfo.tag = aggregate->tag;
+             newinfo.tag_out = aggregate->tag_out;
+
+             ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], 
+                                    (struct prefix *) p, RMAP_RIPNG, 
+                                    &newinfo);
+
+             if (ret == RMAP_DENYMATCH)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("RIPng %s/%d is filtered by route-map out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
+               }
+
+             aggregate->metric_out = newinfo.metric_out;
+             aggregate->tag_out = newinfo.tag_out;
+             if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop_out))
+               aggregate->nexthop_out = newinfo.nexthop_out;
+           }
+
+         /* There is no redistribute routemap for the aggregated RTE */
+
+         /* Changed route only output. */
+         /* XXX, vincent, in order to increase time convergence,
+          * it should be announced if a child has changed.
+          */
+         if (route_type == ripng_changed_route)
+           continue;
+
+         /* Apply offset-list */
+         if (aggregate->metric_out != RIPNG_METRIC_INFINITY)
+           ripng_offset_list_apply_out (p, ifp, &aggregate->metric_out);
+
+         if (aggregate->metric_out > RIPNG_METRIC_INFINITY)
+           aggregate->metric_out = RIPNG_METRIC_INFINITY;
+
+         /* Add RTE to the list */
+         ripng_rte_add(ripng_rte_list, p, NULL, aggregate);
+       }
+
+    }
+
+  /* Flush the list */
+  ripng_rte_send(ripng_rte_list, ifp, to);
+  ripng_rte_free(ripng_rte_list);
+}
+
+/* Create new RIPng instance and set it to global variable. */
+static int
+ripng_create (void)
+{
+  /* ripng should be NULL. */
+  assert (ripng == NULL);
+
+  /* Allocaste RIPng instance. */
+  ripng = XCALLOC (MTYPE_RIPNG, sizeof (struct ripng));
+
+  /* Default version and timer values. */
+  ripng->version = RIPNG_V1;
+  ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT;
+  ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT;
+  ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT;
+  ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT;
+  
+  /* Make buffer.  */
+  ripng->ibuf = stream_new (RIPNG_MAX_PACKET_SIZE * 5);
+  ripng->obuf = stream_new (RIPNG_MAX_PACKET_SIZE);
+
+  /* Initialize RIPng routig table. */
+  ripng->table = route_table_init ();
+  ripng->route = route_table_init ();
+  ripng->aggregate = route_table_init ();
+  /* Make socket. */
+  ripng->sock = ripng_make_socket ();
+  if (ripng->sock < 0)
+    return ripng->sock;
+
+  /* Threads. */
+  ripng_event (RIPNG_READ, ripng->sock);
+  ripng_event (RIPNG_UPDATE_EVENT, 1);
+
+  return 0;
+}
+
+/* Send RIPng request to the interface. */
+int
+ripng_request (struct interface *ifp)
+{
+  struct rte *rte;
+  struct ripng_packet ripng_packet;
+
+  /* In default ripd doesn't send RIP_REQUEST to the loopback interface. */
+  if (if_is_loopback(ifp))
+    return 0;
+
+  /* If interface is down, don't send RIP packet. */
+  if (! if_is_up (ifp))
+    return 0;
+
+  if (IS_RIPNG_DEBUG_EVENT)
+    zlog_debug ("RIPng send request to %s", ifp->name);
+
+  memset (&ripng_packet, 0, sizeof (ripng_packet));
+  ripng_packet.command = RIPNG_REQUEST;
+  ripng_packet.version = RIPNG_V1;
+  rte = ripng_packet.rte;
+  rte->metric = RIPNG_METRIC_INFINITY;
+
+  return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet), 
+                           NULL, ifp);
+}
+
+
+static int
+ripng_update_jitter (int time)
+{
+  return ((random () % (time + 1)) - (time / 2));
+}
+
+void
+ripng_event (enum ripng_event event, int sock)
+{
+  int jitter = 0;
+
+  switch (event)
+    {
+    case RIPNG_READ:
+      if (!ripng->t_read)
+       ripng->t_read = thread_add_read (master, ripng_read, NULL, sock);
+      break;
+    case RIPNG_UPDATE_EVENT:
+      if (ripng->t_update)
+       {
+         thread_cancel (ripng->t_update);
+         ripng->t_update = NULL;
+       }
+      /* Update timer jitter. */
+      jitter = ripng_update_jitter (ripng->update_time);
+
+      ripng->t_update = 
+       thread_add_timer (master, ripng_update, NULL, 
+                         sock ? 2 : ripng->update_time + jitter);
+      break;
+    case RIPNG_TRIGGERED_UPDATE:
+      if (ripng->t_triggered_interval)
+       ripng->trigger = 1;
+      else if (! ripng->t_triggered_update)
+       ripng->t_triggered_update = 
+         thread_add_event (master, ripng_triggered_update, NULL, 0);
+      break;
+    default:
+      break;
+    }
+}
+
+
+/* Print out routes update time. */
+static void
+ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo)
+{
+  time_t clock;
+  struct tm *tm;
+#define TIME_BUF 25
+  char timebuf [TIME_BUF];
+  struct thread *thread;
+  
+  if ((thread = rinfo->t_timeout) != NULL)
+    {
+      clock = thread_timer_remain_second (thread);
+      tm = gmtime (&clock);
+      strftime (timebuf, TIME_BUF, "%M:%S", tm);
+      vty_out (vty, "%5s", timebuf);
+    }
+  else if ((thread = rinfo->t_garbage_collect) != NULL)
+    {
+      clock = thread_timer_remain_second (thread);
+      tm = gmtime (&clock);
+      strftime (timebuf, TIME_BUF, "%M:%S", tm);
+      vty_out (vty, "%5s", timebuf);
+    }
+}
+
+static char *
+ripng_route_subtype_print (struct ripng_info *rinfo)
+{
+  static char str[3];
+  memset(str, 0, 3);
+
+  if (rinfo->suppress)
+    strcat(str, "S");
+
+  switch (rinfo->sub_type)
+    {
+       case RIPNG_ROUTE_RTE:
+         strcat(str, "n");
+         break;
+       case RIPNG_ROUTE_STATIC:
+         strcat(str, "s");
+         break;
+       case RIPNG_ROUTE_DEFAULT:
+         strcat(str, "d");
+         break;
+       case RIPNG_ROUTE_REDISTRIBUTE:
+         strcat(str, "r");
+         break;
+       case RIPNG_ROUTE_INTERFACE:
+         strcat(str, "i");
+         break;
+       default:
+         strcat(str, "?");
+         break;
+    }
+  return str;
+}
+
+DEFUN (show_ipv6_ripng,
+       show_ipv6_ripng_cmd,
+       "show ipv6 ripng",
+       SHOW_STR
+       IPV6_STR
+       "Show RIPng routes\n")
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+  struct prefix_ipv6 *p;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+  int len;
+
+  if (! ripng)
+    return CMD_SUCCESS;
+
+  /* Header of display. */ 
+  vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s"
+          "Sub-codes:%s"
+          "      (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
+          "      (i) - interface, (a/S) - aggregated/Suppressed%s%s"
+          "   Network      Next Hop                      Via     Metric Tag Time%s",
+          VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+          VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+  
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    {
+      if ((aggregate = rp->aggregate) != NULL)
+       {
+         p = (struct prefix_ipv6 *) &rp->p;
+
+#ifdef DEBUG
+         len = vty_out (vty, "R(a) %d/%d %s/%d ",
+                        aggregate->count, aggregate->suppress,
+                        inet6_ntoa (p->prefix), p->prefixlen);
+#else
+         len = vty_out (vty, "R(a) %s/%d ", 
+                        inet6_ntoa (p->prefix), p->prefixlen);
+#endif /* DEBUG */
+         vty_out (vty, "%s", VTY_NEWLINE);
+         vty_out (vty, "%*s", 18, " ");
+
+         vty_out (vty, "%*s", 28, " ");
+         vty_out (vty, "self      %2d  %3d%s", aggregate->metric,
+                  aggregate->tag,
+                  VTY_NEWLINE);
+       }
+
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+       {
+         p = (struct prefix_ipv6 *) &rp->p;
+
+#ifdef DEBUG
+         len = vty_out (vty, "%c(%s) 0/%d %s/%d ",
+                        zebra_route_char(rinfo->type),
+                        ripng_route_subtype_print(rinfo),
+                        rinfo->suppress,
+                        inet6_ntoa (p->prefix), p->prefixlen);
+#else
+         len = vty_out (vty, "%c(%s) %s/%d ",
+                        zebra_route_char(rinfo->type),
+                        ripng_route_subtype_print(rinfo),
+                        inet6_ntoa (p->prefix), p->prefixlen);
+#endif /* DEBUG */
+         vty_out (vty, "%s", VTY_NEWLINE);
+         vty_out (vty, "%*s", 18, " ");
+         len = vty_out (vty, "%s", inet6_ntoa (rinfo->nexthop));
+
+         len = 28 - len;
+         if (len > 0)
+           len = vty_out (vty, "%*s", len, " ");
+
+         /* from */
+         if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && 
+           (rinfo->sub_type == RIPNG_ROUTE_RTE))
+         {
+           len = vty_out (vty, "%s", ifindex2ifname(rinfo->ifindex));
+         } else if (rinfo->metric == RIPNG_METRIC_INFINITY)
+         {
+           len = vty_out (vty, "kill");
+         } else
+           len = vty_out (vty, "self");
+
+         len = 9 - len;
+         if (len > 0)
+           vty_out (vty, "%*s", len, " ");
+
+         vty_out (vty, " %2d  %3d  ",
+                  rinfo->metric, rinfo->tag);
+
+         /* time */
+         if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && 
+           (rinfo->sub_type == RIPNG_ROUTE_RTE))
+         {
+           /* RTE from remote RIP routers */
+           ripng_vty_out_uptime (vty, rinfo);
+         } else if (rinfo->metric == RIPNG_METRIC_INFINITY)
+         {
+           /* poisonous reversed routes (gc) */
+           ripng_vty_out_uptime (vty, rinfo);
+         }
+
+         vty_out (vty, "%s", VTY_NEWLINE);
+       }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_ripng_status,
+       show_ipv6_ripng_status_cmd,
+       "show ipv6 ripng status",
+       SHOW_STR
+       IPV6_STR
+       "Show RIPng routes\n"
+       "IPv6 routing protocol process parameters and statistics\n")
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  if (! ripng)
+    return CMD_SUCCESS;
+
+  vty_out (vty, "Routing Protocol is \"RIPng\"%s", VTY_NEWLINE);
+  vty_out (vty, "  Sending updates every %ld seconds with +/-50%%,",
+           ripng->update_time);
+  vty_out (vty, " next due in %lu seconds%s",
+           thread_timer_remain_second (ripng->t_update),
+           VTY_NEWLINE);
+  vty_out (vty, "  Timeout after %ld seconds,", ripng->timeout_time);
+  vty_out (vty, " garbage collect after %ld seconds%s", ripng->garbage_time,
+           VTY_NEWLINE);
+
+  /* Filtering status show. */
+  config_show_distribute (vty);
+
+  /* Default metric information. */
+  vty_out (vty, "  Default redistribution metric is %d%s",
+           ripng->default_metric, VTY_NEWLINE);
+
+  /* Redistribute information. */
+  vty_out (vty, "  Redistributing:");
+  ripng_redistribute_write (vty, 0);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "  Default version control: send version %d,", ripng->version);
+  vty_out (vty, " receive version %d %s", ripng->version,
+           VTY_NEWLINE);
+
+  vty_out (vty, "    Interface        Send  Recv%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      struct ripng_interface *ri;
+      
+      ri = ifp->info;
+
+      if (ri->enable_network || ri->enable_interface)
+       {
+
+         vty_out (vty, "    %-17s%-3d   %-3d%s", ifp->name,
+                  ripng->version,
+                  ripng->version,
+                  VTY_NEWLINE);
+       }
+    }
+
+  vty_out (vty, "  Routing for Networks:%s", VTY_NEWLINE);
+  ripng_network_write (vty, 0);
+
+  vty_out (vty, "  Routing Information Sources:%s", VTY_NEWLINE);
+  vty_out (vty, "    Gateway          BadPackets BadRoutes  Distance Last Update%s", VTY_NEWLINE);
+  ripng_peer_display (vty);
+
+  return CMD_SUCCESS;  
+}
+
+DEFUN (router_ripng,
+       router_ripng_cmd,
+       "router ripng",
+       "Enable a routing process\n"
+       "Make RIPng instance command\n")
+{
+  int ret;
+
+  vty->node = RIPNG_NODE;
+
+  if (!ripng)
+    {
+      ret = ripng_create ();
+
+      /* Notice to user we couldn't create RIPng. */
+      if (ret < 0)
+       {
+         zlog_warn ("can't create RIPng");
+         return CMD_WARNING;
+       }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_router_ripng,
+       no_router_ripng_cmd,
+       "no router ripng",
+       NO_STR
+       "Enable a routing process\n"
+       "Make RIPng instance command\n")
+{
+  if(ripng)
+    ripng_clean();
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_route,
+       ripng_route_cmd,
+       "route IPV6ADDR",
+       "Static route setup\n"
+       "Set static RIPng route announcement\n")
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_node *rp;
+
+  ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv6 (&p);
+
+  rp = route_node_get (ripng->route, (struct prefix *) &p);
+  if (rp->info)
+    {
+      vty_out (vty, "There is already same static route.%s", VTY_NEWLINE);
+      route_unlock_node (rp);
+      return CMD_WARNING;
+    }
+  rp->info = (void *)1;
+
+  ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_route,
+       no_ripng_route_cmd,
+       "no route IPV6ADDR",
+       NO_STR
+       "Static route setup\n"
+       "Delete static RIPng route announcement\n")
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_node *rp;
+
+  ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv6 (&p);
+
+  rp = route_node_lookup (ripng->route, (struct prefix *) &p);
+  if (! rp)
+    {
+      vty_out (vty, "Can't find static route.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0);
+  route_unlock_node (rp);
+
+  rp->info = NULL;
+  route_unlock_node (rp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_aggregate_address,
+       ripng_aggregate_address_cmd,
+       "aggregate-address X:X::X:X/M",
+       "Set aggregate RIPng route announcement\n"
+       "Aggregate network\n")
+{
+  int ret;
+  struct prefix p;
+  struct route_node *node;
+
+  ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check aggregate alredy exist or not. */
+  node = route_node_get (ripng->aggregate, &p);
+  if (node->info)
+    {
+      vty_out (vty, "There is already same aggregate route.%s", VTY_NEWLINE);
+      route_unlock_node (node);
+      return CMD_WARNING;
+    }
+  node->info = (void *)1;
+
+  ripng_aggregate_add (&p);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_aggregate_address,
+       no_ripng_aggregate_address_cmd,
+       "no aggregate-address X:X::X:X/M",
+       NO_STR
+       "Delete aggregate RIPng route announcement\n"
+       "Aggregate network")
+{
+  int ret;
+  struct prefix p;
+  struct route_node *rn;
+
+  ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *) &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rn = route_node_lookup (ripng->aggregate, &p);
+  if (! rn)
+    {
+      vty_out (vty, "Can't find aggregate route.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  route_unlock_node (rn);
+  rn->info = NULL;
+  route_unlock_node (rn);
+
+  ripng_aggregate_delete (&p);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_default_metric,
+       ripng_default_metric_cmd,
+       "default-metric <1-16>",
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+{
+  if (ripng)
+    {
+      ripng->default_metric = atoi (argv[0]);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_default_metric,
+       no_ripng_default_metric_cmd,
+       "no default-metric",
+       NO_STR
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+{
+  if (ripng)
+    {
+      ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT;
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_default_metric,
+       no_ripng_default_metric_val_cmd,
+       "no default-metric <1-16>",
+       NO_STR
+       "Set a metric of redistribute routes\n"
+       "Default metric\n")
+
+#if 0
+/* RIPng update timer setup. */
+DEFUN (ripng_update_timer,
+       ripng_update_timer_cmd,
+       "update-timer SECOND",
+       "Set RIPng update timer in seconds\n"
+       "Seconds\n")
+{
+  unsigned long update;
+  char *endptr = NULL;
+
+  update = strtoul (argv[0], &endptr, 10);
+  if (update == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "update timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng->update_time = update;
+
+  ripng_event (RIPNG_UPDATE_EVENT, 0);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_update_timer,
+       no_ripng_update_timer_cmd,
+       "no update-timer SECOND",
+       NO_STR
+       "Unset RIPng update timer in seconds\n"
+       "Seconds\n")
+{
+  ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT;
+  ripng_event (RIPNG_UPDATE_EVENT, 0);
+  return CMD_SUCCESS;
+}
+
+/* RIPng timeout timer setup. */
+DEFUN (ripng_timeout_timer,
+       ripng_timeout_timer_cmd,
+       "timeout-timer SECOND",
+       "Set RIPng timeout timer in seconds\n"
+       "Seconds\n")
+{
+  unsigned long timeout;
+  char *endptr = NULL;
+
+  timeout = strtoul (argv[0], &endptr, 10);
+  if (timeout == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng->timeout_time = timeout;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_timeout_timer,
+       no_ripng_timeout_timer_cmd,
+       "no timeout-timer SECOND",
+       NO_STR
+       "Unset RIPng timeout timer in seconds\n"
+       "Seconds\n")
+{
+  ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT;
+  return CMD_SUCCESS;
+}
+
+/* RIPng garbage timer setup. */
+DEFUN (ripng_garbage_timer,
+       ripng_garbage_timer_cmd,
+       "garbage-timer SECOND",
+       "Set RIPng garbage timer in seconds\n"
+       "Seconds\n")
+{
+  unsigned long garbage;
+  char *endptr = NULL;
+
+  garbage = strtoul (argv[0], &endptr, 10);
+  if (garbage == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "garbage timer value error%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng->garbage_time = garbage;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_garbage_timer,
+       no_ripng_garbage_timer_cmd,
+       "no garbage-timer SECOND",
+       NO_STR
+       "Unset RIPng garbage timer in seconds\n"
+       "Seconds\n")
+{
+  ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT;
+  return CMD_SUCCESS;
+}
+#endif /* 0 */
+
+DEFUN (ripng_timers,
+       ripng_timers_cmd,
+       "timers basic <0-65535> <0-65535> <0-65535>",
+       "RIPng timers setup\n"
+       "Basic timer\n"
+       "Routing table update timer value in second. Default is 30.\n"
+       "Routing information timeout timer. Default is 180.\n"
+       "Garbage collection timer. Default is 120.\n")
+{
+  unsigned long update;
+  unsigned long timeout;
+  unsigned long garbage;
+
+  VTY_GET_INTEGER_RANGE("update timer", update, argv[0], 0, 65535);
+  VTY_GET_INTEGER_RANGE("timeout timer", timeout, argv[1], 0, 65535);
+  VTY_GET_INTEGER_RANGE("garbage timer", garbage, argv[2], 0, 65535);
+
+  /* Set each timer value. */
+  ripng->update_time = update;
+  ripng->timeout_time = timeout;
+  ripng->garbage_time = garbage;
+
+  /* Reset update timer thread. */
+  ripng_event (RIPNG_UPDATE_EVENT, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_timers,
+       no_ripng_timers_cmd,
+       "no timers basic",
+       NO_STR
+       "RIPng timers setup\n"
+       "Basic timer\n")
+{
+  /* Set each timer value to the default. */
+  ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT;
+  ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT;
+  ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT;
+
+  /* Reset update timer thread. */
+  ripng_event (RIPNG_UPDATE_EVENT, 0);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_timers,
+       no_ripng_timers_val_cmd,
+       "no timers basic <0-65535> <0-65535> <0-65535>",
+       NO_STR
+       "RIPng timers setup\n"
+       "Basic timer\n"
+       "Routing table update timer value in second. Default is 30.\n"
+       "Routing information timeout timer. Default is 180.\n"
+       "Garbage collection timer. Default is 120.\n")
+
+DEFUN (show_ipv6_protocols, show_ipv6_protocols_cmd,
+       "show ipv6 protocols",
+       SHOW_STR
+       IPV6_STR
+       "Routing protocol information")
+{
+  if (! ripng)
+    return CMD_SUCCESS;
+
+  vty_out (vty, "Routing Protocol is \"ripng\"%s", VTY_NEWLINE);
+  
+  vty_out (vty, "Sending updates every %ld seconds, next due in %d seconds%s",
+          ripng->update_time, 0,
+          VTY_NEWLINE);
+
+  vty_out (vty, "Timerout after %ld seconds, garbage correct %ld%s",
+          ripng->timeout_time,
+          ripng->garbage_time,
+          VTY_NEWLINE);
+
+  vty_out (vty, "Outgoing update filter list for all interfaces is not set");
+  vty_out (vty, "Incoming update filter list for all interfaces is not set");
+
+  return CMD_SUCCESS;
+}
+
+/* Please be carefull to use this command. */
+DEFUN (ripng_default_information_originate,
+       ripng_default_information_originate_cmd,
+       "default-information originate",
+       "Default route information\n"
+       "Distribute default route\n")
+{
+  struct prefix_ipv6 p;
+
+  if (! ripng ->default_information) {
+    ripng->default_information = 1;
+
+    str2prefix_ipv6 ("::/0", &p);
+    ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0);
+  }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_default_information_originate,
+       no_ripng_default_information_originate_cmd,
+       "no default-information originate",
+       NO_STR
+       "Default route information\n"
+       "Distribute default route\n")
+{
+  struct prefix_ipv6 p;
+
+  if (ripng->default_information) {
+    ripng->default_information = 0;
+
+    str2prefix_ipv6 ("::/0", &p);
+    ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0);
+  }
+
+  return CMD_SUCCESS;
+}
+
+/* Update ECMP routes to zebra when ECMP is disabled. */
+static void
+ripng_ecmp_disable (void)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo, *tmp_rinfo;
+  struct list *list;
+  struct listnode *node, *nextnode;
+
+  if (!ripng)
+    return;
+
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL && listcount (list) > 1)
+      {
+        rinfo = listgetdata (listhead (list));
+        if (!ripng_route_rte (rinfo))
+          continue;
+
+        /* Drop all other entries, except the first one. */
+        for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo))
+          if (tmp_rinfo != rinfo)
+            {
+              RIPNG_TIMER_OFF (tmp_rinfo->t_timeout);
+              RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect);
+              list_delete_node (list, node);
+              ripng_info_free (tmp_rinfo);
+            }
+
+        /* Update zebra. */
+        ripng_zebra_ipv6_add (rp);
+
+        /* Set the route change flag. */
+        SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+        /* Signal the output process to trigger an update. */
+        ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+      }
+}
+
+DEFUN (ripng_allow_ecmp,
+       ripng_allow_ecmp_cmd,
+       "allow-ecmp",
+       "Allow Equal Cost MultiPath\n")
+{
+  if (ripng->ecmp)
+    {
+      vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng->ecmp = 1;
+  zlog_info ("ECMP is enabled.");
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_allow_ecmp,
+       no_ripng_allow_ecmp_cmd,
+       "no allow-ecmp",
+       NO_STR
+       "Allow Equal Cost MultiPath\n")
+{
+  if (!ripng->ecmp)
+    {
+      vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng->ecmp = 0;
+  zlog_info ("ECMP is disabled.");
+  ripng_ecmp_disable ();
+  return CMD_SUCCESS;
+}
+
+/* RIPng configuration write function. */
+static int
+ripng_config_write (struct vty *vty)
+{
+  int ripng_network_write (struct vty *, int);
+  void ripng_redistribute_write (struct vty *, int);
+  int write = 0;
+  struct route_node *rp;
+
+  if (ripng)
+    {
+
+      /* RIPng router. */
+      vty_out (vty, "router ripng%s", VTY_NEWLINE);
+
+      if (ripng->default_information)
+       vty_out (vty, " default-information originate%s", VTY_NEWLINE);
+
+      ripng_network_write (vty, 1);
+
+      /* RIPng default metric configuration */
+      if (ripng->default_metric != RIPNG_DEFAULT_METRIC_DEFAULT)
+        vty_out (vty, " default-metric %d%s",
+                ripng->default_metric, VTY_NEWLINE);
+
+      ripng_redistribute_write (vty, 1);
+
+      /* RIP offset-list configuration. */
+      config_write_ripng_offset_list (vty);
+      
+      /* RIPng aggregate routes. */
+      for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
+       if (rp->info != NULL)
+         vty_out (vty, " aggregate-address %s/%d%s", 
+                  inet6_ntoa (rp->p.u.prefix6),
+                  rp->p.prefixlen, 
+
+                  VTY_NEWLINE);
+
+      /* ECMP configuration. */
+      if (ripng->ecmp)
+        vty_out (vty, " allow-ecmp%s", VTY_NEWLINE);
+
+      /* RIPng static routes. */
+      for (rp = route_top (ripng->route); rp; rp = route_next (rp))
+       if (rp->info != NULL)
+         vty_out (vty, " route %s/%d%s", inet6_ntoa (rp->p.u.prefix6),
+                  rp->p.prefixlen,
+                  VTY_NEWLINE);
+
+      /* RIPng timers configuration. */
+      if (ripng->update_time != RIPNG_UPDATE_TIMER_DEFAULT ||
+         ripng->timeout_time != RIPNG_TIMEOUT_TIMER_DEFAULT ||
+         ripng->garbage_time != RIPNG_GARBAGE_TIMER_DEFAULT)
+       {
+         vty_out (vty, " timers basic %ld %ld %ld%s",
+                  ripng->update_time,
+                  ripng->timeout_time,
+                  ripng->garbage_time,
+                  VTY_NEWLINE);
+       }
+#if 0
+      if (ripng->update_time != RIPNG_UPDATE_TIMER_DEFAULT)
+       vty_out (vty, " update-timer %d%s", ripng->update_time,
+                VTY_NEWLINE);
+      if (ripng->timeout_time != RIPNG_TIMEOUT_TIMER_DEFAULT)
+       vty_out (vty, " timeout-timer %d%s", ripng->timeout_time,
+                VTY_NEWLINE);
+      if (ripng->garbage_time != RIPNG_GARBAGE_TIMER_DEFAULT)
+       vty_out (vty, " garbage-timer %d%s", ripng->garbage_time,
+                VTY_NEWLINE);
+#endif /* 0 */
+
+      write += config_write_distribute (vty);
+
+      write += config_write_if_rmap (vty);
+
+      write++;
+    }
+  return write;
+}
+
+/* RIPng node structure. */
+static struct cmd_node cmd_ripng_node =
+{
+  RIPNG_NODE,
+  "%s(config-router)# ",
+  1,
+};
+
+static void
+ripng_distribute_update (struct distribute *dist)
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+  struct access_list *alist;
+  struct prefix_list *plist;
+
+  if (! dist->ifname)
+    return;
+
+  ifp = if_lookup_by_name (dist->ifname);
+  if (ifp == NULL)
+    return;
+
+  ri = ifp->info;
+
+  if (dist->list[DISTRIBUTE_V6_IN])
+    {
+      alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_IN]);
+      if (alist)
+       ri->list[RIPNG_FILTER_IN] = alist;
+      else
+       ri->list[RIPNG_FILTER_IN] = NULL;
+    }
+  else
+    ri->list[RIPNG_FILTER_IN] = NULL;
+
+  if (dist->list[DISTRIBUTE_V6_OUT])
+    {
+      alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_OUT]);
+      if (alist)
+       ri->list[RIPNG_FILTER_OUT] = alist;
+      else
+       ri->list[RIPNG_FILTER_OUT] = NULL;
+    }
+  else
+    ri->list[RIPNG_FILTER_OUT] = NULL;
+
+  if (dist->prefix[DISTRIBUTE_V6_IN])
+    {
+      plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_IN]);
+      if (plist)
+       ri->prefix[RIPNG_FILTER_IN] = plist;
+      else
+       ri->prefix[RIPNG_FILTER_IN] = NULL;
+    }
+  else
+    ri->prefix[RIPNG_FILTER_IN] = NULL;
+
+  if (dist->prefix[DISTRIBUTE_V6_OUT])
+    {
+      plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_OUT]);
+      if (plist)
+       ri->prefix[RIPNG_FILTER_OUT] = plist;
+      else
+       ri->prefix[RIPNG_FILTER_OUT] = NULL;
+    }
+  else
+    ri->prefix[RIPNG_FILTER_OUT] = NULL;
+}
+
+void
+ripng_distribute_update_interface (struct interface *ifp)
+{
+  struct distribute *dist;
+
+  dist = distribute_lookup (ifp->name);
+  if (dist)
+    ripng_distribute_update (dist);
+}
+
+/* Update all interface's distribute list. */
+static void
+ripng_distribute_update_all (struct prefix_list *notused)
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_distribute_update_interface (ifp);
+}
+
+static void
+ripng_distribute_update_all_wrapper (struct access_list *notused)
+{
+  ripng_distribute_update_all(NULL);
+}
+
+/* delete all the added ripng routes. */
+void
+ripng_clean()
+{
+  int i;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  if (ripng) {
+    /* Clear RIPng routes */
+    for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+      {
+        if ((list = rp->info) != NULL)
+          {
+            rinfo = listgetdata (listhead (list));
+            if (ripng_route_rte (rinfo))
+              ripng_zebra_ipv6_delete (rp);
+
+            for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+              {
+                RIPNG_TIMER_OFF (rinfo->t_timeout);
+                RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+                ripng_info_free (rinfo);
+              }
+            list_delete (list);
+            rp->info = NULL;
+            route_unlock_node (rp);
+          }
+
+        if ((aggregate = rp->aggregate) != NULL)
+          {
+            ripng_aggregate_free (aggregate);
+            rp->aggregate = NULL;
+            route_unlock_node (rp);
+          }
+    }
+
+    /* Cancel the RIPng timers */
+    RIPNG_TIMER_OFF (ripng->t_update);
+    RIPNG_TIMER_OFF (ripng->t_triggered_update);
+    RIPNG_TIMER_OFF (ripng->t_triggered_interval);
+
+    /* Cancel the read thread */
+    if (ripng->t_read) {
+      thread_cancel (ripng->t_read);
+      ripng->t_read = NULL;
+    }
+
+    /* Close the RIPng socket */
+    if (ripng->sock >= 0) {
+      close(ripng->sock);
+      ripng->sock = -1;
+    }
+
+    /* Static RIPng route configuration. */
+    for (rp = route_top (ripng->route); rp; rp = route_next (rp))
+      if (rp->info) {
+        rp->info = NULL;
+        route_unlock_node (rp);
+    }
+
+    /* RIPng aggregated prefixes */
+    for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
+      if (rp->info) {
+          rp->info = NULL;
+          route_unlock_node (rp);
+    }
+
+    for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+      if (ripng->route_map[i].name)
+        free (ripng->route_map[i].name);
+
+    XFREE (MTYPE_ROUTE_TABLE, ripng->table);
+    XFREE (MTYPE_ROUTE_TABLE, ripng->route);
+    XFREE (MTYPE_ROUTE_TABLE, ripng->aggregate);
+
+    XFREE (MTYPE_RIPNG, ripng);
+    ripng = NULL;
+  } /* if (ripng) */
+
+  ripng_clean_network();
+  ripng_passive_interface_clean ();
+  ripng_offset_clean ();
+  ripng_interface_clean ();
+  ripng_redistribute_clean ();
+}
+
+/* Reset all values to the default settings. */
+void
+ripng_reset ()
+{
+  /* Call ripd related reset functions. */
+  ripng_debug_reset ();
+  ripng_route_map_reset ();
+
+  /* Call library reset functions. */
+  vty_reset ();
+  access_list_reset ();
+  prefix_list_reset ();
+
+  distribute_list_reset ();
+
+  ripng_interface_reset ();
+
+  ripng_zclient_reset ();
+}
+
+static void
+ripng_if_rmap_update (struct if_rmap *if_rmap)
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+  struct route_map *rmap;
+
+  ifp = if_lookup_by_name (if_rmap->ifname);
+  if (ifp == NULL)
+    return;
+
+  ri = ifp->info;
+
+  if (if_rmap->routemap[IF_RMAP_IN])
+    {
+      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_IN]);
+      if (rmap)
+       ri->routemap[IF_RMAP_IN] = rmap;
+      else
+       ri->routemap[IF_RMAP_IN] = NULL;
+    }
+  else
+    ri->routemap[RIPNG_FILTER_IN] = NULL;
+
+  if (if_rmap->routemap[IF_RMAP_OUT])
+    {
+      rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_OUT]);
+      if (rmap)
+       ri->routemap[IF_RMAP_OUT] = rmap;
+      else
+       ri->routemap[IF_RMAP_OUT] = NULL;
+    }
+  else
+    ri->routemap[RIPNG_FILTER_OUT] = NULL;
+}
+
+void
+ripng_if_rmap_update_interface (struct interface *ifp)
+{
+  struct if_rmap *if_rmap;
+
+  if_rmap = if_rmap_lookup (ifp->name);
+  if (if_rmap)
+    ripng_if_rmap_update (if_rmap);
+}
+
+static void
+ripng_routemap_update_redistribute (void)
+{
+  int i;
+
+  if (ripng)
+    {
+      for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
+       {
+         if (ripng->route_map[i].name)
+           ripng->route_map[i].map = 
+             route_map_lookup_by_name (ripng->route_map[i].name);
+       }
+    }
+}
+
+static void
+ripng_routemap_update (const char *unused)
+{
+  struct interface *ifp;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_if_rmap_update_interface (ifp);
+
+  ripng_routemap_update_redistribute ();
+}
+
+/* Initialize ripng structure and set commands. */
+void
+ripng_init ()
+{
+  /* Randomize. */
+  srandom (time (NULL));
+
+  /* Install RIPNG_NODE. */
+  install_node (&cmd_ripng_node, ripng_config_write);
+
+  /* Install ripng commands. */
+  install_element (VIEW_NODE, &show_ipv6_ripng_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd);
+
+  install_element (CONFIG_NODE, &router_ripng_cmd);
+  install_element (CONFIG_NODE, &no_router_ripng_cmd);
+
+  install_default (RIPNG_NODE);
+  install_element (RIPNG_NODE, &ripng_route_cmd);
+  install_element (RIPNG_NODE, &no_ripng_route_cmd);
+  install_element (RIPNG_NODE, &ripng_aggregate_address_cmd);
+  install_element (RIPNG_NODE, &no_ripng_aggregate_address_cmd);
+
+  install_element (RIPNG_NODE, &ripng_default_metric_cmd);
+  install_element (RIPNG_NODE, &no_ripng_default_metric_cmd);
+  install_element (RIPNG_NODE, &no_ripng_default_metric_val_cmd);
+
+  install_element (RIPNG_NODE, &ripng_timers_cmd);
+  install_element (RIPNG_NODE, &no_ripng_timers_cmd);
+  install_element (RIPNG_NODE, &no_ripng_timers_val_cmd);
+#if 0
+  install_element (RIPNG_NODE, &ripng_update_timer_cmd);
+  install_element (RIPNG_NODE, &no_ripng_update_timer_cmd);
+  install_element (RIPNG_NODE, &ripng_timeout_timer_cmd);
+  install_element (RIPNG_NODE, &no_ripng_timeout_timer_cmd);
+  install_element (RIPNG_NODE, &ripng_garbage_timer_cmd);
+  install_element (RIPNG_NODE, &no_ripng_garbage_timer_cmd);
+#endif /* 0 */
+
+  install_element (RIPNG_NODE, &ripng_default_information_originate_cmd);
+  install_element (RIPNG_NODE, &no_ripng_default_information_originate_cmd);
+
+  install_element (RIPNG_NODE, &ripng_allow_ecmp_cmd);
+  install_element (RIPNG_NODE, &no_ripng_allow_ecmp_cmd);
+
+  ripng_if_init ();
+  ripng_debug_init ();
+
+  /* Access list install. */
+  access_list_init ();
+  access_list_add_hook (ripng_distribute_update_all_wrapper);
+  access_list_delete_hook (ripng_distribute_update_all_wrapper);
+
+  /* Prefix list initialize.*/
+  prefix_list_init ();
+  prefix_list_add_hook (ripng_distribute_update_all);
+  prefix_list_delete_hook (ripng_distribute_update_all);
+
+  /* Distribute list install. */
+  distribute_list_init (RIPNG_NODE);
+  distribute_list_add_hook (ripng_distribute_update);
+  distribute_list_delete_hook (ripng_distribute_update);
+
+  /* Route-map for interface. */
+  ripng_route_map_init ();
+  ripng_offset_init ();
+
+  route_map_add_hook (ripng_routemap_update);
+  route_map_delete_hook (ripng_routemap_update);
+
+  if_rmap_init (RIPNG_NODE);
+  if_rmap_hook_add (ripng_if_rmap_update);
+  if_rmap_hook_delete (ripng_if_rmap_update);
+}
diff --git a/ripngd/ripngd.conf.sample b/ripngd/ripngd.conf.sample
new file mode 100644 (file)
index 0000000..ad673e5
--- /dev/null
@@ -0,0 +1,22 @@
+! -*- rip -*-
+!
+! RIPngd sample configuration file
+!
+! $Id: ripngd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
+!
+hostname ripngd
+password zebra
+!
+! debug ripng events
+! debug ripng packet
+!
+!
+router ripng
+! network sit1
+! route 3ffe:506::0/32
+! distribute-list local-only out sit1
+!
+!ipv6 access-list local-only permit 3ffe:506::0/32
+!ipv6 access-list local-only deny any
+!
+log stdout
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
new file mode 100644 (file)
index 0000000..a0c6a4e
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * RIPng related value and structure.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIPNG_RIPNGD_H
+#define _ZEBRA_RIPNG_RIPNGD_H
+
+#include <zclient.h>
+#include <vty.h>
+
+/* RIPng version and port number. */
+#define RIPNG_V1                         1
+#define RIPNG_PORT_DEFAULT             521
+#define RIPNG_VTY_PORT                2603
+#define RIPNG_MAX_PACKET_SIZE         1500
+#define RIPNG_PRIORITY_DEFAULT           0
+
+/* RIPng commands. */
+#define RIPNG_REQUEST                    1
+#define RIPNG_RESPONSE                   2
+
+/* RIPng metric and multicast group address. */
+#define RIPNG_METRIC_INFINITY           16
+#define RIPNG_METRIC_NEXTHOP          0xff
+#define RIPNG_GROUP              "ff02::9"
+
+/* RIPng timers. */
+#define RIPNG_UPDATE_TIMER_DEFAULT      30
+#define RIPNG_TIMEOUT_TIMER_DEFAULT    180
+#define RIPNG_GARBAGE_TIMER_DEFAULT    120
+
+/* RIPng peer timeout value. */
+#define RIPNG_PEER_TIMER_DEFAULT       180
+
+/* Default config file name. */
+#define RIPNG_DEFAULT_CONFIG "ripngd.conf"
+
+/* RIPng route types. */
+#define RIPNG_ROUTE_RTE                  0
+#define RIPNG_ROUTE_STATIC               1
+#define RIPNG_ROUTE_DEFAULT              2
+#define RIPNG_ROUTE_REDISTRIBUTE         3
+#define RIPNG_ROUTE_INTERFACE            4
+#define RIPNG_ROUTE_AGGREGATE            5
+
+/* Interface send/receive configuration. */
+#define RIPNG_SEND_UNSPEC                0
+#define RIPNG_SEND_OFF                   1
+#define RIPNG_RECEIVE_UNSPEC             0
+#define RIPNG_RECEIVE_OFF                1
+
+/* RIP default route's accept/announce methods. */
+#define RIPNG_DEFAULT_ADVERTISE_UNSPEC   0
+#define RIPNG_DEFAULT_ADVERTISE_NONE     1
+#define RIPNG_DEFAULT_ADVERTISE          2
+
+#define RIPNG_DEFAULT_ACCEPT_UNSPEC      0
+#define RIPNG_DEFAULT_ACCEPT_NONE        1
+#define RIPNG_DEFAULT_ACCEPT             2
+
+/* Default value for "default-metric" command. */
+#define RIPNG_DEFAULT_METRIC_DEFAULT     1
+
+/* For max RTE calculation. */
+#ifndef IPV6_HDRLEN
+#define IPV6_HDRLEN 40
+#endif /* IPV6_HDRLEN */
+
+#ifndef IFMINMTU
+#define IFMINMTU    576
+#endif /* IFMINMTU */
+
+/* RIPng structure. */
+struct ripng 
+{
+  /* RIPng socket. */
+  int sock;
+
+  /* RIPng Parameters.*/
+  u_char command;
+  u_char version;
+  unsigned long update_time;
+  unsigned long timeout_time;
+  unsigned long garbage_time;
+  int max_mtu;
+  int default_metric;
+  int default_information;
+
+  /* Input/output buffer of RIPng. */
+  struct stream *ibuf;
+  struct stream *obuf;
+
+  /* RIPng routing information base. */
+  struct route_table *table;
+
+  /* RIPng only static route information. */
+  struct route_table *route;
+
+  /* RIPng aggregate route information. */
+  struct route_table *aggregate;
+
+  /* RIPng threads. */
+  struct thread *t_read;
+  struct thread *t_write;
+  struct thread *t_update;
+  struct thread *t_garbage;
+  struct thread *t_zebra;
+
+  /* Triggered update hack. */
+  int trigger;
+  struct thread *t_triggered_update;
+  struct thread *t_triggered_interval;
+
+  /* RIPng ECMP flag */
+  unsigned int ecmp;
+
+  /* For redistribute route map. */
+  struct
+  {
+    char *name;
+    struct route_map *map;
+    int metric_config;
+    u_int32_t metric;
+  } route_map[ZEBRA_ROUTE_MAX];
+};
+
+/* Routing table entry. */
+struct rte
+{
+  struct in6_addr addr;        /* RIPng destination prefix */
+  u_int16_t tag;               /* RIPng tag */
+  u_char prefixlen;    /* Length of the RIPng prefix */
+  u_char metric;       /* Metric of the RIPng route */
+                       /* The nexthop is stored by the structure
+                        * ripng_nexthop within ripngd.c */
+};
+
+/* RIPNG send packet. */
+struct ripng_packet
+{
+  u_char command;
+  u_char version;
+  u_int16_t zero; 
+  struct rte rte[1];
+};
+
+/* Each route's information. */
+struct ripng_info
+{
+  /* This route's type.  Static, ripng or aggregate. */
+  u_char type;
+
+  /* Sub type for static route. */
+  u_char sub_type;
+
+  /* RIPng specific information */
+  struct in6_addr nexthop;     
+  struct in6_addr from;
+
+  /* Which interface does this route come from. */
+  ifindex_t ifindex;           
+
+  /* Metric of this route.  */
+  u_char metric;               
+
+  /* Tag field of RIPng packet.*/
+  u_int16_t tag;               
+
+  /* For aggregation. */
+  unsigned int suppress;
+
+  /* Flags of RIPng route. */
+#define RIPNG_RTF_FIB      1
+#define RIPNG_RTF_CHANGED  2
+  u_char flags;
+
+  /* Garbage collect timer. */
+  struct thread *t_timeout;
+  struct thread *t_garbage_collect;
+
+  /* Route-map features - this variables can be changed. */
+  struct in6_addr nexthop_out;
+  u_char metric_set;
+  u_char metric_out;
+  u_int16_t tag_out;
+
+  struct route_node *rp;
+};
+
+#ifdef notyet
+#if 0
+/* RIPng tag structure. */
+struct ripng_tag
+{
+  /* Tag value. */
+  u_int16_t tag;
+
+  /* Port. */
+  u_int16_t port;
+
+  /* Multicast group. */
+  struct in6_addr maddr;
+
+  /* Table number. */
+  int table;
+
+  /* Distance. */
+  int distance;
+
+  /* Split horizon. */
+  u_char split_horizon;
+
+  /* Poison reverse. */
+  u_char poison_reverse;
+};
+#endif /* 0 */
+#endif /* not yet */
+
+typedef enum {
+  RIPNG_NO_SPLIT_HORIZON = 0,
+  RIPNG_SPLIT_HORIZON,
+  RIPNG_SPLIT_HORIZON_POISONED_REVERSE
+} split_horizon_policy_t;
+
+/* RIPng specific interface configuration. */
+struct ripng_interface
+{
+  /* RIPng is enabled on this interface. */
+  int enable_network;
+  int enable_interface;
+
+  /* RIPng is running on this interface. */
+  int running;
+
+  /* Split horizon flag. */
+  split_horizon_policy_t split_horizon;
+  split_horizon_policy_t split_horizon_default;
+  
+  /* For filter type slot. */
+#define RIPNG_FILTER_IN  0
+#define RIPNG_FILTER_OUT 1
+#define RIPNG_FILTER_MAX 2
+
+  /* Access-list. */
+  struct access_list *list[RIPNG_FILTER_MAX];
+
+  /* Prefix-list. */
+  struct prefix_list *prefix[RIPNG_FILTER_MAX];
+
+  /* Route-map. */
+  struct route_map *routemap[RIPNG_FILTER_MAX];
+
+#ifdef notyet
+#if 0
+  /* RIPng tag configuration. */
+  struct ripng_tag *rtag;
+#endif /* 0 */
+#endif /* notyet */
+
+  /* Default information originate. */
+  u_char default_originate;
+
+  /* Default information only. */
+  u_char default_only;
+
+  /* Wake up thread. */
+  struct thread *t_wakeup;
+
+  /* Passive interface. */
+  int passive;
+};
+
+/* RIPng peer information. */
+struct ripng_peer
+{
+  /* Peer address. */
+  struct in6_addr addr;
+
+  /* Peer RIPng tag value. */
+  int domain;
+
+  /* Last update time. */
+  time_t uptime;
+
+  /* Peer RIP version. */
+  u_char version;
+
+  /* Statistics. */
+  int recv_badpackets;
+  int recv_badroutes;
+
+  /* Timeout thread. */
+  struct thread *t_timeout;
+};
+
+/* All RIPng events. */
+enum ripng_event
+{
+  RIPNG_READ,
+  RIPNG_ZEBRA,
+  RIPNG_REQUEST_EVENT,
+  RIPNG_UPDATE_EVENT,
+  RIPNG_TRIGGERED_UPDATE,
+};
+
+/* RIPng timer on/off macro. */
+#define RIPNG_TIMER_ON(T,F,V) \
+do { \
+   if (!(T)) \
+      (T) = thread_add_timer (master, (F), rinfo, (V)); \
+} while (0)
+
+#define RIPNG_TIMER_OFF(T) \
+do { \
+   if (T) \
+     { \
+       thread_cancel(T); \
+       (T) = NULL; \
+     } \
+} while (0)
+
+/* Extern variables. */
+extern struct ripng *ripng;
+
+extern struct thread_master *master;
+
+/* Prototypes. */
+extern void ripng_init (void);
+extern void ripng_reset (void);
+extern void ripng_clean (void);
+extern void ripng_clean_network (void);
+extern void ripng_interface_clean (void);
+extern void ripng_interface_reset (void);
+extern void ripng_passive_interface_clean (void);
+extern void ripng_if_init (void);
+extern void ripng_route_map_init (void);
+extern void ripng_route_map_reset (void);
+extern void ripng_terminate (void);
+ /* zclient_init() is done by ripng_zebra.c:zebra_init() */
+extern void zebra_init (struct thread_master *);
+extern void ripng_zclient_start (void);
+extern void ripng_zclient_reset (void);
+extern void ripng_offset_init (void);
+
+extern int config_write_ripng_offset_list (struct vty *);
+
+extern void ripng_peer_init (void);
+extern void ripng_peer_update (struct sockaddr_in6 *, u_char);
+extern void ripng_peer_bad_route (struct sockaddr_in6 *);
+extern void ripng_peer_bad_packet (struct sockaddr_in6 *);
+extern void ripng_peer_display (struct vty *);
+extern struct ripng_peer *ripng_peer_lookup (struct in6_addr *);
+extern struct ripng_peer *ripng_peer_lookup_next (struct in6_addr *);
+
+extern int ripng_offset_list_apply_in (struct prefix_ipv6 *,
+                                       struct interface *, u_char *);
+extern int ripng_offset_list_apply_out (struct prefix_ipv6 *,
+                                        struct interface *, u_char *);
+extern void ripng_offset_clean (void);
+
+extern struct ripng_info * ripng_info_new (void);
+extern void ripng_info_free (struct ripng_info *rinfo);
+extern void ripng_event (enum ripng_event, int);
+extern int ripng_request (struct interface *ifp);
+extern void ripng_redistribute_add (int, int, struct prefix_ipv6 *,
+                                    ifindex_t, struct in6_addr *, route_tag_t);
+extern void ripng_redistribute_delete (int, int, struct prefix_ipv6 *,
+                                       ifindex_t);
+extern void ripng_redistribute_withdraw (int type);
+
+extern void ripng_distribute_update_interface (struct interface *);
+extern void ripng_if_rmap_update_interface (struct interface *);
+
+extern void ripng_zebra_ipv6_add (struct route_node *);
+extern void ripng_zebra_ipv6_delete (struct route_node *);
+
+extern void ripng_redistribute_clean (void);
+extern int ripng_redistribute_check (int);
+extern void ripng_redistribute_write (struct vty *, int);
+
+extern int ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
+                            struct in6_addr *nexthop,
+                            u_int16_t tag, u_char metric);
+extern int ripng_send_packet (caddr_t buf, int bufsize,
+                              struct sockaddr_in6 *to, struct interface *ifp);
+
+extern void ripng_packet_dump (struct ripng_packet *packet, int size,
+                               const char *sndrcv);
+
+extern int ripng_interface_up (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int ripng_interface_down (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int ripng_interface_add (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int ripng_interface_delete (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int ripng_interface_address_add (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+extern int ripng_interface_address_delete (int command, struct zclient *, zebra_size_t,
+    vrf_id_t);
+
+extern int ripng_network_write (struct vty *, int);
+
+extern struct ripng_info *ripng_ecmp_add (struct ripng_info *);
+extern struct ripng_info *ripng_ecmp_replace (struct ripng_info *);
+extern struct ripng_info *ripng_ecmp_delete (struct ripng_info *);
+
+#endif /* _ZEBRA_RIPNG_RIPNGD_H */
diff --git a/solaris/.gitignore b/solaris/.gitignore
new file mode 100644 (file)
index 0000000..63afe3f
--- /dev/null
@@ -0,0 +1,22 @@
+Makefile
+Makefile.in
+?.manifest
+*.xml
+pkginfo.*.full
+pkginfo.tmpl
+prototype.daemons
+prototype.dev
+prototype.doc
+prototype.libs
+prototype.smf
+depend.daemons
+depend.dev
+depend.doc
+depend.libs
+depend.smf
+quagga.init
+*.pkg
+*.pkg.gz
+*~
+*.loT
+*.a
\ No newline at end of file
diff --git a/solaris/Makefile.am b/solaris/Makefile.am
new file mode 100644 (file)
index 0000000..dcee240
--- /dev/null
@@ -0,0 +1,124 @@
+# Solaris packages automake file
+
+# XXX This file uses GNU make extensions.
+
+.PHONY: packages
+
+# the names of the various subpackages, and some convenient 
+# derived variables.
+pkg_names = daemons dev doc libs smf
+pkg_quagga_daemons = zebra bgpd ospfd ospf6d ripd ripngd
+pkg_name_rev = @PACKAGE_VERSION@-@CONFDATE@-@target_os@-@target_cpu@
+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
+
+# pkgmk variable substitutions wont grok ${variable} in prototype
+# file, so we cant let autoconf generate the file sadly
+# wish automake would just provide a template for this
+edit = $(SED) \
+       -e 's,@prefix\@,$(prefix),g' \
+       -e 's,@exec_prefix,$(exec_prefix),g' \
+       -e 's,@bindir\@,$(bindir),g' \
+       -e 's,@sbindir\@,$(sbindir),g' \
+       -e 's,@libexecdir\@,$(libexecdir),g' \
+       -e 's,@datadir\@,$(datadir),g' \
+       -e 's,@sysconfdir\@,$(sysconfdir),g' \
+       -e 's,@sharedstatedir\@,$(sharedstatedir),g' \
+       -e 's,@localstatedir\@,$(localstatedir),g' \
+       -e 's,@libdir\@,$(libdir),g' \
+       -e 's,@includedir\@,$(includedir),g' \
+       -e 's,@infodir\@,$(infodir),g' \
+       -e 's,@mandir\@,$(mandir),g' \
+       -e 's,@enable_user\@,$(enable_user),g' \
+       -e 's,@enable_group\@,$(enable_group),g' \
+       -e 's,@enable_vty_group\@,$(enable_vty_group),g' \
+       -e 's,@quagga_statedir\@,$(quagga_statedir),g' \
+       -e 's,[@]PACKAGE_NAME[@],@PACKAGE_NAME@,g' \
+       -e 's,[@]PACKAGE_TARNAME[@],@PACKAGE_TARNAME@,g' \
+       -e 's,[@]PACKAGE_VERSION[@],@PACKAGE_VERSION@,g' \
+       -e 's,[@]PACKAGE_BUGREPORT[@],@PACKAGE_BUGREPORT@,g' \
+       -e 's,[@]CONFDATE[@],@CONFDATE@,g' \
+       -e 's,[@]target_cpu[@],$(target_cpu),g' \
+       -e 's,[@]target_host[@],$(target_host),g' \
+       -e 's,[@]target_os[@],$(target_os),g'
+
+# common options for pkgmk
+pkg_make_vars = exec_prefix=@exec_prefix@ prefix=@prefix@ \
+       builddir=@builddir@ srcdir=@srcdir@ \
+       top_builddir=@top_builddir@ top_srcdir=@top_srcdir@ \
+       abs_builddir=@abs_builddir@ abs_srcdir=@abs_srcdir@ \
+       abs_top_builddir=@abs_top_builddir@ abs_top_srcdir=@abs_top_srcdir@
+
+# pkgmk: write the package to spool in build dir, to avoid root dependencies
+pkg_make = pkgmk -o -d @abs_builddir@ \
+       -f $< DESTDIR="$(DESTDIR)/" $(pkg_make_vars)
+
+# pkgtrans: write a pkg file stream, shame we cant pipe directly to it from
+# pkgmk..
+pkg_trans = pkgtrans -s @abs_builddir@ "@abs_builddir@/$@"
+
+# pkgmk can only cope with a single pkginfo, cant 'stack' various 
+# pkginfo template files and a package specific pkginfo file in the prototype
+# Create the package specific template here, and create the full pkginfo
+# by cating this and the common pkginfo.tmpl together.
+pkginfo.tmpl: $(srcdir)/pkginfo.tmpl.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+
+pkginfo.%.tmpl: $(srcdir)/pkginfo.%.tmpl.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+
+pkginfo.%.full: pkginfo.%.tmpl pkginfo.tmpl Makefile
+       cat pkginfo.tmpl pkginfo.$*.tmpl > $@
+
+# use 'edit' above to transform prototype.in to pkgmk acceptable prototype
+prototype.%: $(srcdir)/prototype.%.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+
+# use edit to construct the SMF manifest files
+%.xml: $(srcdir)/%.xml.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+# use edit to construct the depend files
+depend.%: $(srcdir)/depend.%.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+
+# method file (bit like init script)
+quagga.init: $(srcdir)/quagga.init.in Makefile
+       rm -f $@
+       $(edit) $< > $@
+
+# construct the pkg
+@PACKAGE_TARNAME@-%-$(pkg_name_rev).pkg: prototype.% \
+               depend.% quagga.init pkginfo.%.full
+       ($(pkg_make) && \
+       $(pkg_trans) "QUAGGA$*")
+
+%.pkg.gz : %.pkg
+       (gzip -c $< > $@)
+
+# pkginfo.package and prototype.package are all built sources
+#BUILT_SOURCES = pkginfo.daemons pkginfo.dev pkginfo.doc pkginfo.libs \
+#      prototype.daemons prototype.dev prototype.doc prototype.libs
+BUILT_SOURCES = $(pkg_pkginfos) pkginfo.tmpl $(pkg_prototypes) \
+       $(pkg_manifests) $(pkg_depends) quagga.init
+
+CLEANFILES = $(BUILT_SOURCES) $(pkg_packages)
+
+EXTRA_DIST = $(pkg_manifests:%=%.in) $(pkg_prototypes:%=%.in) \
+       $(pkg_names:%=pkginfo.%.tmpl.in) $(srcdir)/pkginfo.tmpl.in \
+       $(pkg_depends:%=%.in) quagga.init.in README.txt
+
+pkg-root-install:
+       (cd $(top_builddir) && \
+        $(MAKE) DESTDIR=$(abs_builddir)/quagga-root install)
+
+packages: $(pkg_packages)
+
+#nodist_pkgdata_DATA = $(pkg_packages)
diff --git a/solaris/README.txt b/solaris/README.txt
new file mode 100644 (file)
index 0000000..811e9d0
--- /dev/null
@@ -0,0 +1,188 @@
+To build packages for Solaris 10:
+
+Requirements:
+-------------
+
+- Development environment including gcc (eg as shipped with Solaris 10)
+
+- The Package tools from Solaris 10 or Solaris Nevada/Express.
+
+- i.manifest and r.manifest scripts as supplied with Solaris Express
+  in /usr/sadm/install/scripts/ or from OpenSolaris.org:
+
+  http://cvs.opensolaris.org/source/xref/usr/src/pkgdefs/common_files/i.manifest
+  http://cvs.opensolaris.org/source/xref/usr/src/pkgdefs/common_files/r.manifest
+  
+  i.manifest must be at least version 1.5. Place these scripts in
+  this directory if you are using Solaris 10 GA (which does not ship with
+  these scripts), or in the solaris/ directory in the Quagga source.
+
+
+Package creation instructions:
+------------------------------
+
+1. Configure and build Quagga in the top level build directory as per
+normal, eg:
+
+       ./configure --prefix=/usr/local/quagga \
+               --localstatedir=/var/run/quagga
+               --enable-gcc-rdynamic --enable-opaque-lsa --enable-ospf-te \
+               --enable-multipath=64 --enable-user=quagga \
+               --enable-ospfclient=yes --enable-ospfapi=yes  \
+               --enable-group=quagga --enable-nssa --enable-opaque-lsa
+
+You will need /usr/sfw/bin and /usr/ccs/bin in your path.
+
+2. make install in the top-level build directory, it's a good idea to make
+use of DESTDIR to install to an alternate root, eg:
+
+       gmake DESTDIR=/var/tmp/qroot install
+
+3. In this directory (solaris/), run make packages, specifying DESTDIR if
+appropriate, eg:
+
+       gmake DESTDIR=/var/tmp/qroot packages
+
+This should result in 4 packages being created:
+
+       quagga-libs-...-$ARCH.pkg       - QUAGGAlibs
+       quagga-daemons-...-$ARCH.pkg    - QUAGGAdaemons
+       quagga-doc-...-$ARCH.pkg        - QUAGGAdoc
+       quagga-dev-...-$ARCH.pkg        - QUAGGAdev
+       quagga-smf-...-$ARCH.pkg        - QUAGGAsmf
+
+QUAGGAlibs and QUAGGAdaemons are needed for daemon runtime. QUAGGAsmf
+provides the required bits for Solaris 10+ SMF support.
+
+
+Install and post-install configuration notes:
+---------------------------------------------
+
+- If you specified a user/group which does not exist per default on Solaris
+  (eg quagga/quagga) you *must* create these before installing these on a
+  system. The packages do *not* create the users.
+
+- The configuration files are not created. You must create the configuration
+  file yourself, either with your complete desired configuration, or else if
+  you wish to use the telnet interface for further configuration you must
+  create them containing at least:
+
+        password whatever
+
+  The user which quagga runs as must have write permissions on this file, no
+  other user should have read permissions, and you would also have to enable
+  the telnet interface (see below).
+
+- SMF notes:
+
+  - QUAGGAsmf installs a svc:/network/routing/quagga service, with an
+    instance for each daemon
+  
+  - The state of all instances of quagga service can be inspected with:
+  
+       svcs -l svc:/network/routing/quagga
+  
+    or typically just with a shortcut of 'quagga':
+    
+       svcs -l quagga
+  
+  - A specific instance of the quagga service can be inspected by specifying
+    the daemon name as the instance, ie quagga:<daemon>:
+    
+       svcs -l svc:/network/routing/quagga:zebra
+       svcs -l svc:/network/routing/quagga:ospfd
+       <etc>
+
+    or typically just with the shortcut of 'quagga:<daemon>' or even
+    <daemon>:
+    
+       svcs -l quagga:zebra
+       svcs -l ospfd
+    
+    Eg:
+    
+    # # svcs -l ripd
+    fmri         svc:/network/routing/quagga:ripd
+    name         Quagga: ripd, RIPv1/2 IPv4 routing protocol daemon.
+    enabled      true
+    state        online
+    next_state   none
+    state_time   Wed Jun 15 16:21:02 2005
+    logfile      /var/svc/log/network-routing-quagga:ripd.log
+    restarter    svc:/system/svc/restarter:default
+    contract_id  93 
+    dependency   require_all/restart svc:/network/routing/quagga:zebra (online)
+    dependency   require_all/restart file://localhost//usr/local/quagga/etc/ripd.conf (online)
+    dependency   require_all/none svc:/system/filesystem/usr:default (online)
+    dependency   require_all/none svc:/network/loopback (online)
+
+  - Configuration of startup options is by way of SMF properties in a
+    property group named 'quagga'. The defaults should automatically be
+    inline with how you configured Quagga in Step 1 above. 
+  
+  - By default the VTY interface is disabled. To change this, see below for
+    how to set the 'quagga/vty_port' property as appropriate for
+    /each/ service. Also, the VTY is set to listen only to localhost by
+    default, you may change the 'quagga/vty_addr' property as appropriate
+    for both of the 'quagga' service and specific individual instances of
+    the 'quagga' service (ie quagga:zebra, quagga:ospfd, etc..).
+    
+  - Properties belonging to the 'quagga' service are inherited by all
+    instances. Eg:
+    
+    # svcprop -p quagga svc:/network/routing/quagga
+    quagga/group astring root
+    quagga/retain boolean false
+    quagga/user astring root
+    quagga/vty_addr astring 127.1
+    quagga/vty_port integer 0
+    
+    # svcprop -p quagga svc:/network/routing/quagga:ospfd
+    quagga/retain_routes boolean false
+    quagga/group astring root
+    quagga/retain boolean false
+    quagga/user astring root
+    quagga/vty_addr astring 127.1
+    quagga/vty_port integer 0
+    
+    All instances will inherit these properties, unless the instance itself
+    overrides these defaults. This also implies one can modify properties of
+    the 'quagga' service and have them apply to all daemons.
+    
+    # svccfg -s svc:/network/routing/quagga \
+       setprop quagga/vty_addr = astring: ::1
+    
+    # svcprop -p quagga svc:/network/routing/quagga
+    quagga/group astring root
+    quagga/retain boolean false
+    quagga/user astring root
+    quagga/vty_port integer 0
+    quagga/vty_addr astring ::1
+    
+    # # You *must* refresh instances to have the property change
+    # # take affect for the 'running snapshot' of service state.
+    # svcadm refresh quagga:ospfd
+    
+    # svcprop -p quagga svc:/network/routing/quagga:ospfd
+    quagga/retain_routes boolean false
+    quagga/group astring root
+    quagga/retain boolean false
+    quagga/user astring root
+    quagga/vty_port integer 0
+    quagga/vty_addr astring ::1
+    
+    Other daemon-specific options/properties may be available, however they
+    are not yet honoured/used (eg ospfd/apiserver on svc:/network/ospf).
+
+  - As SMF is dependency aware, restarting network/zebra will restart all the
+    other daemons.
+  
+  - To upgrade from one set of Quagga packages to a newer release, one must
+    first pkgrm the installed packages. When one pkgrm's QUAGGAsmf all
+    property configuration will be lost, and any customisations will have to
+    redone after installing the updated QUAGGAsmf package.
+  
+- These packages are not supported by Sun Microsystems, report bugs via the
+  usual Quagga channels, ie Bugzilla. Improvements/contributions of course
+  would be greatly appreciated.
+
diff --git a/solaris/depend.daemons.in b/solaris/depend.daemons.in
new file mode 100644 (file)
index 0000000..3430e8c
--- /dev/null
@@ -0,0 +1,8 @@
+P QUAGGAlibs Quagga common runtime libraries
+       @PACKAGE_VERSION@,REV=@CONFDATE@
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcsr Core Solaris Libraries (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+I SUNWzebrar
+I SUNWzebrau
+I CSWzebra
diff --git a/solaris/depend.dev.in b/solaris/depend.dev.in
new file mode 100644 (file)
index 0000000..8f23482
--- /dev/null
@@ -0,0 +1,2 @@
+P QUAGGAlibs Quagga common runtime libraries
+       @PACKAGE_VERSION@,REV=@CONFDATE@
diff --git a/solaris/depend.doc.in b/solaris/depend.doc.in
new file mode 100644 (file)
index 0000000..b337929
--- /dev/null
@@ -0,0 +1 @@
+P SUNWdoc Documentation Tools
diff --git a/solaris/depend.libs.in b/solaris/depend.libs.in
new file mode 100644 (file)
index 0000000..4185977
--- /dev/null
@@ -0,0 +1,5 @@
+P SUNWcslr  Core Solaris Libraries (Root)
+P SUNWcsl Core Solaris, (Shared Libs)
+P SUNWlibmsr Math & Microtasking Libraries (Root)
+R QUAGGAdaemons Quagga daemons
+R QUAGGAdev
diff --git a/solaris/depend.smf.in b/solaris/depend.smf.in
new file mode 100644 (file)
index 0000000..6d928d2
--- /dev/null
@@ -0,0 +1,8 @@
+P QUAGGAdaemons Quagga daemons
+       @PACKAGE_VERSION@,REV=@CONFDATE@
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcsr Core Solaris Libraries (Root)
+P SUNWroute Network Routing daemons/commands (Usr)
+I SUNWzebrar
+I SUNWzebrau
+I CSWzebra
diff --git a/solaris/pkginfo.daemons.tmpl.in b/solaris/pkginfo.daemons.tmpl.in
new file mode 100644 (file)
index 0000000..cab0e3c
--- /dev/null
@@ -0,0 +1,2 @@
+PKG="QUAGGAdaemons"
+NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ daemons"
diff --git a/solaris/pkginfo.dev.tmpl.in b/solaris/pkginfo.dev.tmpl.in
new file mode 100644 (file)
index 0000000..9c5d23e
--- /dev/null
@@ -0,0 +1,3 @@
+PKG=QUAGGAdev
+NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ development files"
+
diff --git a/solaris/pkginfo.doc.tmpl.in b/solaris/pkginfo.doc.tmpl.in
new file mode 100644 (file)
index 0000000..809ec77
--- /dev/null
@@ -0,0 +1,2 @@
+PKG=QUAGGAdoc
+NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ documentation"
diff --git a/solaris/pkginfo.libs.tmpl.in b/solaris/pkginfo.libs.tmpl.in
new file mode 100644 (file)
index 0000000..42adc6e
--- /dev/null
@@ -0,0 +1,2 @@
+PKG=QUAGGAlibs
+NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ common runtime libraries"
diff --git a/solaris/pkginfo.smf.tmpl.in b/solaris/pkginfo.smf.tmpl.in
new file mode 100644 (file)
index 0000000..4aa0393
--- /dev/null
@@ -0,0 +1,2 @@
+PKG="QUAGGAsmf"
+NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ SMF support"
diff --git a/solaris/pkginfo.tmpl.in b/solaris/pkginfo.tmpl.in
new file mode 100644 (file)
index 0000000..2dd27fd
--- /dev/null
@@ -0,0 +1,10 @@
+ARCH="@target_cpu@"
+CATEGORY="system"
+VERSION="@PACKAGE_VERSION@,REV=@CONFDATE@"
+VENDOR="http://www.quagga.net/"
+HOTLINE="@PACKAGE_BUGREPORT@"
+EMAIL=paul@quagga.net
+DESC="@PACKAGE_NAME@ Routing Protocols"
+MAXINST=1
+CLASSES="none preserve renamenew manifest"
+BASEDIR=/
diff --git a/solaris/prototype.daemons.in b/solaris/prototype.daemons.in
new file mode 100644 (file)
index 0000000..ce65d5e
--- /dev/null
@@ -0,0 +1,20 @@
+i pkginfo=$abs_builddir/pkginfo.daemons.full
+i depend=$abs_builddir/depend.daemons
+i copying=$abs_top_srcdir/COPYING
+d none @sbindir@=$DESTDIR/@sbindir@ 0755 root bin
+f none @sbindir@/zebra=$DESTDIR/@sbindir@/zebra 0755 root bin
+f none @sbindir@/bgpd=$DESTDIR/@sbindir@/bgpd 0755 root bin
+f none @sbindir@/ripd=$DESTDIR/@sbindir@/ripd 0755 root bin
+f none @sbindir@/ripngd=$DESTDIR/@sbindir@/ripngd 0755 root bin
+f none @sbindir@/ospfd=$DESTDIR/@sbindir@/ospfd 0755 root bin
+f none @sbindir@/ospf6d=$DESTDIR/@sbindir@/ospf6d 0755 root bin
+f none @sbindir@/watchquagga=$DESTDIR/@sbindir@/watchquagga 0755 root bin
+d none @sysconfdir@=$DESTDIR/@sysconfdir@ 0711 @enable_user@ @enable_group@
+f none @sysconfdir@/zebra.conf.sample=$DESTDIR/@sysconfdir@/zebra.conf.sample 0644 root bin
+f none @sysconfdir@/bgpd.conf.sample=$DESTDIR/@sysconfdir@/bgpd.conf.sample 0644 root bin
+f none @sysconfdir@/bgpd.conf.sample2=$DESTDIR/@sysconfdir@/bgpd.conf.sample2 0644 root bin
+f none @sysconfdir@/ripd.conf.sample=$DESTDIR/@sysconfdir@/ripd.conf.sample 0644 root bin
+f none @sysconfdir@/ripngd.conf.sample=$DESTDIR/@sysconfdir@/ripngd.conf.sample 0644 root bin
+f none @sysconfdir@/ospfd.conf.sample=$DESTDIR/@sysconfdir@/ospfd.conf.sample 0644 root bin
+f none @sysconfdir@/ospf6d.conf.sample=$DESTDIR/@sysconfdir@/ospf6d.conf.sample 0644 root bin
+d none @quagga_statedir@=$DESTDIR/@quagga_statedir@ 0711 @enable_user@ @enable_group@
diff --git a/solaris/prototype.dev.in b/solaris/prototype.dev.in
new file mode 100644 (file)
index 0000000..2ad937b
--- /dev/null
@@ -0,0 +1,57 @@
+i pkginfo=$abs_builddir/pkginfo.dev.full
+i depend=$abs_builddir/depend.dev
+i copying=$abs_top_srcdir/COPYING
+f none @libdir@/libzebra.la=$DESTDIR/@libdir@/libzebra.la 0755 root bin
+f none @libdir@/libzebra.a=$DESTDIR/@libdir@/libzebra.a 0644 root bin
+f none @libdir@/libospf.la=$DESTDIR/@libdir@/libospf.la 0755 root bin
+f none @libdir@/libospf.a=$DESTDIR/@libdir@/libospf.a 0644 root bin
+f none @libdir@/libospfapiclient.la=$DESTDIR/@libdir@/libospfapiclient.la 0755 root bin
+f none @libdir@/libospfapiclient.a=$DESTDIR/@libdir@/libospfapiclient.a 0644 root bin
+d none @includedir@=$DESTDIR/@includedir@ 0755 root bin
+d none @includedir@/quagga=$DESTDIR/@includedir@/quagga 0755 root bin
+d none @includedir@/quagga/ospfd=$DESTDIR/@includedir@/quagga/ospfd 0755 root bin
+f none @includedir@/quagga/ospfd/ospf_api.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_api.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_asbr.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_asbr.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_dump.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_dump.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_lsa.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_lsa.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_lsdb.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_lsdb.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_nsm.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_nsm.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_ism.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_ism.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospf_opaque.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_opaque.h 0644 root bin
+f none @includedir@/quagga/ospfd/ospfd.h=$DESTDIR/@includedir@/quagga/ospfd/ospfd.h 0644 root bin
+f none @includedir@/quagga/buffer.h=$DESTDIR/@includedir@/quagga/buffer.h 0644 root bin
+f none @includedir@/quagga/command.h=$DESTDIR/@includedir@/quagga/command.h 0644 root bin
+f none @includedir@/quagga/filter.h=$DESTDIR/@includedir@/quagga/filter.h 0644 root bin
+f none @includedir@/quagga/getopt.h=$DESTDIR/@includedir@/quagga/getopt.h 0644 root bin
+f none @includedir@/quagga/hash.h=$DESTDIR/@includedir@/quagga/hash.h 0644 root bin
+f none @includedir@/quagga/if.h=$DESTDIR/@includedir@/quagga/if.h 0644 root bin
+f none @includedir@/quagga/linklist.h=$DESTDIR/@includedir@/quagga/linklist.h 0644 root bin
+f none @includedir@/quagga/log.h=$DESTDIR/@includedir@/quagga/log.h 0644 root bin
+f none @includedir@/quagga/memory.h=$DESTDIR/@includedir@/quagga/memory.h 0644 root bin
+f none @includedir@/quagga/network.h=$DESTDIR/@includedir@/quagga/network.h 0644 root bin
+f none @includedir@/quagga/prefix.h=$DESTDIR/@includedir@/quagga/prefix.h 0644 root bin
+f none @includedir@/quagga/routemap.h=$DESTDIR/@includedir@/quagga/routemap.h 0644 root bin
+f none @includedir@/quagga/distribute.h=$DESTDIR/@includedir@/quagga/distribute.h 0644 root bin
+f none @includedir@/quagga/sockunion.h=$DESTDIR/@includedir@/quagga/sockunion.h 0644 root bin
+f none @includedir@/quagga/str.h=$DESTDIR/@includedir@/quagga/str.h 0644 root bin
+f none @includedir@/quagga/stream.h=$DESTDIR/@includedir@/quagga/stream.h 0644 root bin
+f none @includedir@/quagga/table.h=$DESTDIR/@includedir@/quagga/table.h 0644 root bin
+f none @includedir@/quagga/thread.h=$DESTDIR/@includedir@/quagga/thread.h 0644 root bin
+f none @includedir@/quagga/vector.h=$DESTDIR/@includedir@/quagga/vector.h 0644 root bin
+f none @includedir@/quagga/version.h=$DESTDIR/@includedir@/quagga/version.h 0644 root bin
+f none @includedir@/quagga/vty.h=$DESTDIR/@includedir@/quagga/vty.h 0644 root bin
+f none @includedir@/quagga/zebra.h=$DESTDIR/@includedir@/quagga/zebra.h 0644 root bin
+f none @includedir@/quagga/plist.h=$DESTDIR/@includedir@/quagga/plist.h 0644 root bin
+f none @includedir@/quagga/zclient.h=$DESTDIR/@includedir@/quagga/zclient.h 0644 root bin
+f none @includedir@/quagga/sockopt.h=$DESTDIR/@includedir@/quagga/sockopt.h 0644 root bin
+f none @includedir@/quagga/smux.h=$DESTDIR/@includedir@/quagga/smux.h 0644 root bin
+f none @includedir@/quagga/md5.h=$DESTDIR/@includedir@/quagga/md5.h 0644 root bin
+f none @includedir@/quagga/if_rmap.h=$DESTDIR/@includedir@/quagga/if_rmap.h 0644 root bin
+f none @includedir@/quagga/keychain.h=$DESTDIR/@includedir@/quagga/keychain.h 0644 root bin
+f none @includedir@/quagga/privs.h=$DESTDIR/@includedir@/quagga/privs.h 0644 root bin
+f none @includedir@/quagga/sigevent.h=$DESTDIR/@includedir@/quagga/sigevent.h 0644 root bin
+f none @includedir@/quagga/pqueue.h=$DESTDIR/@includedir@/quagga/pqueue.h 0644 root bin
+f none @includedir@/quagga/jhash.h=$DESTDIR/@includedir@/quagga/jhash.h 0644 root bin
+f none @includedir@/quagga/zassert.h=$DESTDIR/@includedir@/quagga/zassert.h 0644 root bin
+d none @includedir@/quagga/ospfapi=$DESTDIR/@includedir@/quagga/ospfapi 0755 root bin
+f none @includedir@/quagga/ospfapi/ospf_apiclient.h=$DESTDIR/@includedir@/quagga/ospfapi/ospf_apiclient.h 0644 root bin
diff --git a/solaris/prototype.doc.in b/solaris/prototype.doc.in
new file mode 100644 (file)
index 0000000..42b076d
--- /dev/null
@@ -0,0 +1,17 @@
+i pkginfo=$abs_builddir/pkginfo.doc.full
+i depend=$abs_builddir/depend.doc
+i copying=$abs_top_srcdir/COPYING
+d none @infodir@=$DESTDIR/@infodir@ 0755 root bin
+#f none @infodir@/dir=$DESTDIR/@infodir@/dir 0644 root bin
+f none @infodir@/quagga.info=$DESTDIR/@infodir@/quagga.info 0644 root bin
+d none @mandir@=$DESTDIR/@mandir@ 0755 root bin
+d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin
+f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin
+d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin
+f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin
+f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin
+f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin
+f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin
+f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin
+f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin
+f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin
diff --git a/solaris/prototype.libs.in b/solaris/prototype.libs.in
new file mode 100644 (file)
index 0000000..71f4fd5
--- /dev/null
@@ -0,0 +1,13 @@
+i pkginfo=$abs_builddir/pkginfo.libs.full
+i depend=$abs_builddir/depend.libs
+i copying=$abs_top_srcdir/COPYING
+d none @libdir@=$DESTDIR/@libdir@ 0755 root bin
+s none @libdir@/libzebra.so.0=libzebra.so.0.0.0
+f none @libdir@/libzebra.so.0.0.0=$DESTDIR/@libdir@/libzebra.so.0.0.0 0755 root bin
+s none @libdir@/libzebra.so=libzebra.so.0.0.0
+s none @libdir@/libospf.so.0=libospf.so.0.0.0
+f none @libdir@/libospf.so.0.0.0=$DESTDIR/@libdir@/libospf.so.0.0.0 0755 root bin
+s none @libdir@/libospf.so=libospf.so.0.0.0
+f none @libdir@/libospfapiclient.so.0.0.0=$DESTDIR/@libdir@/libospfapiclient.so.0.0.0 0755 root bin
+s none @libdir@/libospfapiclient.so.0=libospfapiclient.so.0.0.0
+s none @libdir@/libospfapiclient.so=libospfapiclient.so.0.0.0
diff --git a/solaris/prototype.smf.in b/solaris/prototype.smf.in
new file mode 100644 (file)
index 0000000..3c80f39
--- /dev/null
@@ -0,0 +1,8 @@
+i pkginfo=$abs_builddir/pkginfo.smf.full
+i depend=$abs_builddir/depend.smf
+i copying=$abs_top_srcdir/COPYING
+i i.manifest
+i r.manifest
+f manifest var/svc/manifest/network/quagga.xml 0444 root bin
+#f none var/svc/profile/@PACKAGE_TARNAME@_options.xml=$abs_builddir/options.xml 0755 root sys
+f none lib/svc/method/quagga=$abs_builddir/quagga.init 0755 root bin
diff --git a/solaris/quagga.init.in b/solaris/quagga.init.in
new file mode 100755 (executable)
index 0000000..ed3350e
--- /dev/null
@@ -0,0 +1,280 @@
+#!/sbin/sh
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This file is part of Quagga.
+#
+# Quagga is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any
+# later version.
+#
+# Quagga is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Quagga; see the file COPYING.  If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# 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@
+
+# handle upgrade of daemon-args SMF property to new routeadm properties
+# used during upgrade too by routeadm.
+# relevant to S10U4+ only.
+handle_routeadm_upgrade () {
+       GLOBAL_OPTIONS="PAfiug"
+       
+       daemon_args=`get_daemon_args $SMF_FMRI`
+       
+       if [ -n "$daemon_args" ]; then
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "P" vty_port 0
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "A" vty_address
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "f" config_file
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "i" pid_file
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "u" user
+               set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                       "$GLOBAL_OPTIONS" "g" group
+               
+               case "$1" in
+               zebra)
+                       set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \
+                               "${GLOBAL_OPTIONS}b" "b" batch true false
+                       ;;
+               ripd|ripngd)
+                       set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \
+                               "${GLOBAL_OPTIONS}r" "r" retain true false
+                       ;;
+               bgpd)
+                       set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \
+                               "${GLOBAL_OPTIONS}rnp" "r" retain true false
+                       set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \
+                               "${GLOBAL_OPTIONS}rnp" "n" no_kernel true false
+                       set_daemon_value_property "$SMF_FMRI" "$daemon_args" \
+                               "${GLOBAL_OPTIONS}rnp" "p" bgp_port
+               esac
+               clear_daemon_args $SMF_FMRI
+       fi
+}
+
+upgrade_config () {
+       DAEMON=$1
+       # handle upgrade of SUNWzebra to Quagga
+       if [ -d "/etc/quagga" -a ! -f "/etc/quagga/${DAEMON}.conf" ] ; then
+               if [ -f "/etc/sfw/zebra/${DAEMON}.conf" ] ; then
+                       cp "/etc/sfw/zebra/${DAEMON}.conf" \
+                               "/etc/quagga/${DAEMON}.conf.upgrade" \
+                               || exit $SMF_EXIT_ERR_FATAL
+                       chown "${USER}:${GROUP}" "/etc/quagga/${DAEMON}.conf.upgrade" \
+                               || exit $SMF_EXIT_ERR_FATAL
+                       chmod 0600 "/etc/quagga/${DAEMON}.conf.upgrade" \
+                               || exit $SMF_EXIT_ERR_FATAL
+                       mv "/etc/quagga/${DAEMON}.conf.upgrade" "/etc/quagga/${DAEMON}.conf" \
+                               || exit $SMF_EXIT_ERR_FATAL
+               fi
+       fi
+
+       if [ ! -f "/etc/quagga/${DAEMON}.conf" ] ; then
+               touch "/etc/quagga/${DAEMON}.conf.new" \
+                       || exit $SMF_EXIT_ERR_FATAL
+               chown "${USER}:${GROUP}" "/etc/quagga/${DAEMON}.conf.new" \
+                       || exit $SMF_EXIT_ERR_FATAL
+               chmod 0600 "/etc/quagga/${DAEMON}.conf.new" \
+                       || exit $SMF_EXIT_ERR_FATAL
+               mv "/etc/quagga/${DAEMON}.conf.new" "/etc/quagga/${DAEMON}.conf" \
+                       || exit $SMF_EXIT_ERR_FATAL
+       fi
+}
+
+# Relevant to S10+
+quagga_is_globalzone () {
+       if [ "${QUAGGA_INIT_ZONENAME:=`/sbin/zonename`}" = "global" \
+               -o `/sbin/zonename -t` = "exclusive" ]; then
+               return 0
+       else
+               return 1
+       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`"
+       
+       # 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
+
+       case $1 in
+       zebra)
+               args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI batch -b 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}
+}
+
+# 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";
+       
+       # source the SMF-routeadm include if present..
+       if [ -f "$ROUTEADMINCLUDE" ] ; then
+               . "$ROUTEADMINCLUDE"
+       fi
+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
+       
+# if there's no SMF, set some default DAEMON_ARGS
+smf_present || DAEMON_ARGS=""
+
+usage () {
+       if smf_present ; then
+               echo "Usage: $0 <daemon>";
+       else
+               echo "Usage: $0 <stop|start> <daemon> <daemon arguments>";
+       fi
+       echo "The --pid_file argument is implied";
+       echo "This help message: $0 <help|usage>";
+}
+
+# parse arguments, different according to SMF or not.
+case $1 in
+       'help' | 'usage')
+               usage
+               exit $SMF_EXIT_OK
+               ;;
+esac
+
+if smf_present ; then
+       QUAGGA_METHOD="start"
+else
+       QUAGGA_METHOD="$1"
+       shift;
+fi
+
+DAEMON="$1"
+
+# daemon path must be given
+if [ "$DAEMON_PATH/$DAEMON" = "/" ]; then
+       usage
+       exit $SMF_EXIT_ERR_FATAL
+fi
+
+# only bgpd is suitable for running in a non-global zone, at this
+# time.
+case "${DAEMON}" in
+       bgpd)
+       ;;
+       zebra | ospfd | ospf6d | ripd | ripngd )
+               quagga_is_globalzone || exit $SMF_EXIT_OK
+       ;;
+       *)
+               usage
+               exit $SMF_EXIT_ERR_CONFIG;
+       ;;
+esac
+
+# Older Quagga SMF packages pass daemon args on the commandline
+# Newer SMF routeadm model uses properties for each argument
+# so we must handle that.
+if [ smf_present -a -f "$ROUTEADMINCLUDE" ]; then
+       handle_routeadm_upgrade $DAEMON;
+       DAEMON_ARGS=`routeadm_daemon_args`;
+else
+       if [ $# -gt 0 ] ; then
+               shift
+               DAEMON_ARGS="$@"
+       fi
+fi
+
+upgrade_config "$DAEMON"
+
+CONF_FILE=`get_routeadm_property $SMF_FMRI config_file`
+if [ -z "$CONF_FILE" ] ; then
+        CONF_FILE="@sysconfdir@/${DAEMON}.conf"
+fi
+if [ ! -f "$CONF_FILE" ] ; then
+        echo "Could not find config file, $CONF_FILE"
+        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@
+       chown @enable_user@:@enable_group@ @quagga_statedir@
+       chmod 751 @quagga_statedir@
+fi
+
+PIDFILE="@quagga_statedir@/${DAEMON}.pid"
+
+start () {
+       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_by_pidfile () {
+       if [ -f "${PIDFILE}" ]; then
+               /usr/bin/kill -TERM `/usr/bin/cat "${PIDFILE}"`
+       fi
+}
+
+case "$QUAGGA_METHOD" in
+'start')
+       start
+       ;;
+'stop')
+       stop_by_pidfile
+       ;;
+
+*)
+       usage
+       exit $SMF_EXIT_ERR_FATAL
+       ;;
+esac   
+
+exit $SMF_EXIT_OK;
diff --git a/solaris/quagga.xml.in b/solaris/quagga.xml.in
new file mode 100644 (file)
index 0000000..60427b0
--- /dev/null
@@ -0,0 +1,828 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+       This file is part of Quagga.
+
+       Quagga is free software; you can redistribute it and/or modify it
+       under the terms of the GNU General Public License as published by the
+       Free Software Foundation; either version 2, or (at your option) any
+       later version.
+
+       Quagga is distributed in the hope that it will be useful, but 
+       WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+       General Public License for more details.
+
+       You should have received a copy of the GNU General Public License  
+       along with Quagga; see the file COPYING.  If not, write to the Free
+       Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+       02111-1307, USA.
+
+       Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+       Use is subject to license terms.
+
+    Copyright 2015 Joyent, Inc.
+
+       ident   "@(#)quagga.xml 1.0     05/03/15 SMI"
+-->
+
+<service_bundle type='manifest' name='SUNWquagga-daemons:quagga'>
+
+<service
+       name='network/routing/zebra'
+       type='service'
+       version='1'>
+       
+       <single_instance />
+       <instance name='quagga' enabled='false'>
+       
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+       
+       <dependency name='net'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri value='svc:/network/initial' />
+       </dependency>
+       
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga zebra'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='60'>
+       </exec_method>
+
+       <!--    if we define these properties at the service level, each
+               instance inherits them, and it can override with
+               desired values.
+       -->
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+       
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/zebra' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <!-- zebra should not contribute to ipv4/ipv6 routing state -->
+               <propval name='protocol' type='astring' value='zebra' />
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <stability value='Evolving' />
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+               
+               <!-- Options common to Quagga daemons
+                    Property names are equivalent to the long
+                    option name, consult Quagga documentation -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+               
+               <!-- Options specific to zebra -->
+               <propval name='batch' type='boolean' value='false' />
+       </property_group>
+       
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: zebra, RIB, kernel intermediary and misc daemon
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='zebra' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+
+<service
+       name='network/routing/rip'
+       type='service'
+       version='1'>
+
+       <instance name='quagga' enabled='false'>
+
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+
+       <dependency
+               name='ipv4-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv4-forwarding' />
+       </dependency>
+       
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+       
+       <!-- ensure that restart of zebra is propogated to daemon -->
+       <dependency
+               name='zebra'
+               grouping='require_all'
+               restart_on='restart'
+               type='service'>
+               <service_fmri value='svc:/network/routing/zebra:quagga' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga ripd'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='60'>
+       </exec_method>
+
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/ripd' />
+               <propval name='legacy-daemon' type='astring'
+                       value='/usr/sfw/sbin/ripdstart' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <propval name='protocol' type='astring' value='ipv4' />
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+
+               <!-- Options common to Quagga daemons -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+               
+               <!-- Options specific to ripd -->
+               <propval name='retain' type='boolean' value='false' />
+       </property_group>
+
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: ripd, RIPv1/2 IPv4 routing protocol daemon.
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='ripd' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+
+<service
+       name='network/routing/ripng'
+       type='service'
+       version='1'>
+
+       <instance name='quagga' enabled='false'>
+
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+       
+       <dependency
+               name='ipv6-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv6-forwarding' />
+       </dependency>
+
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+
+       <!-- ensure that restart of zebra is propogated to daemon -->
+       <dependency
+               name='zebra'
+               grouping='require_all'
+               restart_on='restart'
+               type='service'>
+               <service_fmri value='svc:/network/routing/zebra:quagga' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga ripngd'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='60' >
+       </exec_method>
+
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/ripngd' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <propval name='protocol' type='astring' value='ipv6'/>
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+
+               <!-- Options common to Quagga daemons -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+
+               <!-- Options specific to ripngd -->
+               <propval name='retain' type='boolean' value='false' />
+       </property_group>
+
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: ripngd, RIPng IPv6 routing protocol daemon.
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='ripngd' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+
+<service
+       name='network/routing/ospf'
+       type='service'
+       version='1'>
+
+       <instance name='quagga' enabled='false'>
+
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+       
+       <dependency
+               name='ipv4-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv4-forwarding' />
+       </dependency>
+
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+
+       <!-- ensure that restart of zebra is propogated to daemon -->
+       <dependency
+               name='zebra'
+               grouping='require_all'
+               restart_on='restart'
+               type='service'>
+               <service_fmri value='svc:/network/routing/zebra:quagga' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga ospfd'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <!-- ospfd can take a long time to shutdown, due to graceful 
+            shutdown 
+        -->
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='600'>
+       </exec_method>
+
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/ospfd' />
+               <propval name='legacy-daemon' type='astring'
+                       value='/usr/sfw/sbin/ospfdstart' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <propval name='protocol' type='astring' value='ipv4'/>
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+
+               <!-- Options common to Quagga daemons -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+       </property_group>
+
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: ospfd, OSPFv2 IPv4 routing protocol daemon.
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='ospfd' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+
+<service
+       name='network/routing/ospf6'
+       type='service'
+       version='1'>
+
+       <instance name='quagga' enabled='false'>
+
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+       
+       <dependency
+               name='ipv6-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv6-forwarding' />
+       </dependency>
+
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+
+       <!-- ensure that restart of zebra is propogated to daemon -->
+       <dependency
+               name='zebra'
+               grouping='require_all'
+               restart_on='restart'
+               type='service'>
+               <service_fmri value='svc:/network/routing/zebra:quagga' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga ospf6d'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='60'>
+       </exec_method>
+
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/ospf6d' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <propval name='protocol' type='astring' value='ipv6'/>
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+
+               <!-- Options common to Quagga daemons -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+       </property_group>
+
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: ospf6d, OSPFv3 IPv6 routing protocol daemon.
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='ospf6d' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+
+
+<service
+       name='network/routing/bgp'
+       type='service'
+       version='1'>
+
+       <instance name='quagga' enabled='false'>
+
+       <dependency name='fs'
+               grouping='require_all'
+               restart_on='none'
+               type='service'>
+               <service_fmri
+                       value='svc:/system/filesystem/usr:default' />
+       </dependency>
+       
+       <dependency
+               name='ipv6-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv6-forwarding' />
+       </dependency>
+
+       <dependency
+               name='ipv4-forwarding'
+               grouping='optional_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/ipv4-forwarding' />
+       </dependency>
+       
+       <!-- do not not run unless routing-setup has run -->
+       <dependency
+               name='network_routing_setup'
+               grouping='require_all'
+               restart_on='refresh'
+               type='service'>
+               <service_fmri value='svc:/network/routing-setup' />
+       </dependency>
+
+       <!-- ensure that restart of zebra is propogated to daemon -->
+       <dependency
+               name='zebra'
+               grouping='require_all'
+               restart_on='restart'
+               type='service'>
+               <service_fmri value='svc:/network/routing/zebra:quagga' />
+       </dependency>
+
+       <exec_method
+               type='method'
+               name='start'
+               exec='/lib/svc/method/quagga bgpd'
+               timeout_seconds='60'>
+               <method_context>
+                 <method_credential
+                  user='root' group='root'/>
+               </method_context>
+       </exec_method>
+
+       <exec_method
+               type='method'
+               name='stop'
+               exec=':kill'
+               timeout_seconds='60' >
+       </exec_method>
+
+       <property_group name='startd'
+               type='framework'>
+               <!-- sub-process core dumps shouldn't restart session -->
+               <propval name='ignore_error'
+                   type='astring' value='core,signal' />
+       </property_group>
+
+       <!-- Properties in this group are used by routeadm (1M) -->
+       <property_group name='routeadm' type='application'>
+               <stability value='Unstable' />
+               <!-- Identifies service as a routing service -->
+               <propval name='daemon' type='astring'
+                       value='@sbindir@/bgpd' />
+               <propval name='legacy-daemon' type='astring'
+                       value='/usr/sfw/sbin/bgpdstart' />
+               <propval name='value_authorization' type='astring'
+                       value='solaris.smf.value.routing' />
+               <property name='protocol' type='astring'>
+                       <astring_list>
+                               <value_node value='ipv4'/>
+                               <value_node value='ipv6'/>
+                       </astring_list>
+               </property>
+       </property_group>
+       
+       <!-- Properties in this group are modifiable via routeadm (1M) -->
+       <property_group name='routing' type='application'>
+               <propval name='value_authorization' type='astring' 
+                        value='solaris.smf.value.routing' />
+
+               <!-- Options common to Quagga daemons. -->
+               <!-- The config file to use, if not the default -->
+               <propval name='config_file' type='astring' value=''/>
+               <!-- The vty_port to listen on if not the default.
+                    0 to disable --> 
+               <propval name='vty_port' type='integer' value='0' />
+               <!-- The address to bind the VTY interface to, if not any. -->
+               <propval name='vty_address' type='astring' value='' />
+               <!-- The user to switch to after startup, if not the default -->
+               <propval name='user' type='astring' value='' />
+               <!-- The group to switch to, if not the default.
+                    If user is specified, this defaults to a group with
+                    same name as user -->
+               <propval name='group' type='astring' value='' />
+               <!-- The pidfile to use, if not the default of
+                    @quagga_statedir@ -->
+               <propval name='pid_file' type='astring' value='' />
+
+               <!-- Options specific to bgpd -->
+               <propval name='retain' type='boolean' value='false' />
+               <propval name='no_kernel' type='boolean' value='false' />
+               <propval name='bgp_port' type='astring' value='' />
+               
+               <!--
+                       If enable_zebra is false, it will not be switched
+                       on by the start method.
+               -->
+               <propval name='enable_zebra' type='boolean' value='true' />
+       </property_group>
+
+       <property_group name='general' type='framework'>
+               <!-- to start stop routing services -->
+               <propval name='action_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+               <propval name='value_authorization' type='astring'
+                        value='solaris.smf.manage.routing' />
+       </property_group>
+
+       <template>
+               <common_name>
+                       <loctext xml:lang='C'>
+                       Quagga: bgpd, BGP routing protocol daemon.
+                       </loctext>
+               </common_name>
+               <documentation>
+                       <manpage title='bgpd' section='1M'
+                               manpath='@mandir@' />
+                       <doc_link name='quagga.net' 
+                               uri='http://www.quagga.net/' />
+               </documentation>
+       </template>
+       </instance>
+       <stability value='Unstable' />
+</service>
+</service_bundle>
diff --git a/stamp-h.in b/stamp-h.in
new file mode 100644 (file)
index 0000000..9788f70
--- /dev/null
@@ -0,0 +1 @@
+timestamp
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644 (file)
index 0000000..3002b27
--- /dev/null
@@ -0,0 +1,41 @@
+Makefile
+Makefile.in
+*.o
+tags
+TAGS
+.deps
+.nfs*
+*~
+*.loT
+*.lo
+*.la
+*.libs
+*.bak
+*.log
+*.sum
+*.xml
+.arch-inventory
+.arch-ids
+aspathtest
+ecommtest
+heavy
+heavythread
+heavywq
+tabletest
+test-timer-correctness
+test-timer-performance
+testbgpcap
+testbgpmpath
+testbgpmpattr
+testbuffer
+testchecksum
+testcli
+testmemory
+testprivs
+testsegv
+testsig
+teststream
+testnexthopiter
+testcommands
+test-commands-defun.c
+site.exp
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644 (file)
index 0000000..b13e903
--- /dev/null
@@ -0,0 +1,92 @@
+AUTOMAKE_OPTIONS    = dejagnu
+DEJATOOL = libzebra
+
+SUBDIRS = \
+       bgpd.tests \
+       libzebra.tests
+
+EXTRA_DIST = \
+       config/unix.exp \
+       lib/bgpd.exp \
+       lib/libzebra.exp \
+       global-conf.exp \
+       testcommands.in \
+       testcommands.refout \
+       testcli.in \
+       testcli.refout
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\"
+
+if BGPD
+TESTS_BGPD = aspathtest testbgpcap ecommtest testbgpmpattr testbgpmpath
+DEJATOOL += bgpd
+else
+TESTS_BGPD =
+endif
+
+check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \
+               testprivs teststream testchecksum tabletest testnexthopiter \
+               testcommands test-timer-correctness test-timer-performance \
+               testcli \
+               $(TESTS_BGPD)
+
+../vtysh/vtysh_cmd.c:
+       $(MAKE) -C ../vtysh vtysh_cmd.c
+
+test-commands-defun.c: ../vtysh/vtysh_cmd.c
+       sed \
+               -e 's/"vtysh\.h"/"tests.h"/' \
+               -e 's/vtysh_init_cmd/test_init_cmd/' \
+               -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \
+               < ../vtysh/vtysh_cmd.c \
+               > test-commands-defun.c
+
+BUILT_SOURCES = test-commands-defun.c
+CLEANFILES = test-commands-defun.c bgpd libzebra
+
+noinst_HEADERS = prng.h tests.h common-cli.h
+
+testcli_SOURCES = test-cli.c common-cli.c
+testsig_SOURCES = test-sig.c
+testsegv_SOURCES = test-segv.c
+testbuffer_SOURCES = test-buffer.c
+testmemory_SOURCES = test-memory.c
+testprivs_SOURCES = test-privs.c
+teststream_SOURCES = test-stream.c
+heavy_SOURCES = heavy.c main.c
+heavywq_SOURCES = heavy-wq.c main.c
+heavythread_SOURCES = heavy-thread.c main.c
+aspathtest_SOURCES = aspath_test.c
+testbgpcap_SOURCES = bgp_capability_test.c
+ecommtest_SOURCES = ecommunity_test.c
+testbgpmpattr_SOURCES =  bgp_mp_attr_test.c
+testchecksum_SOURCES = test-checksum.c
+testbgpmpath_SOURCES = bgp_mpath_test.c
+tabletest_SOURCES = table_test.c
+testnexthopiter_SOURCES = test-nexthop-iter.c prng.c
+testcommands_SOURCES = test-commands-defun.c test-commands.c prng.c
+test_timer_correctness_SOURCES = test-timer-correctness.c prng.c
+test_timer_performance_SOURCES = test-timer-performance.c prng.c
+
+testcli_LDADD = ../lib/libzebra.la @LIBCAP@
+testsig_LDADD = ../lib/libzebra.la @LIBCAP@
+testsegv_LDADD = ../lib/libzebra.la @LIBCAP@
+testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@
+testmemory_LDADD = ../lib/libzebra.la @LIBCAP@
+testprivs_LDADD = ../lib/libzebra.la @LIBCAP@
+teststream_LDADD = ../lib/libzebra.la @LIBCAP@
+heavy_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
+heavywq_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
+heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
+aspathtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+testbgpcap_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+ecommtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+testbgpmpattr_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ 
+testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
+testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@
+testcommands_LDADD = ../lib/libzebra.la @LIBCAP@
+test_timer_correctness_LDADD = ../lib/libzebra.la @LIBCAP@
+test_timer_performance_LDADD = ../lib/libzebra.la @LIBCAP@
diff --git a/tests/aspath_test.c b/tests/aspath_test.c
new file mode 100644 (file)
index 0000000..5a0899e
--- /dev/null
@@ -0,0 +1,1387 @@
+/* 
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+/* need these to link in libbgp */
+struct zebra_privs_t *bgpd_privs = NULL;
+struct thread_master *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec 
+{
+  const char *shouldbe; /* the string the path should parse to */
+  const char *shouldbe_delete_confed; /* ditto, but once confeds are deleted */
+  const unsigned int hops; /* aspath_count_hops result */
+  const unsigned int confeds; /* aspath_count_confeds */
+  const int private_as; /* whether the private_as check should pass or fail */
+#define NOT_ALL_PRIVATE 0
+#define ALL_PRIVATE 1
+  const as_t does_loop; /* an ASN which should trigger loop-check */
+  const as_t doesnt_loop; /* one which should not */
+  const as_t first; /* the first ASN, if there is one */
+#define NULL_ASN 0
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+  const char *name;
+  const char *desc;
+  const u_char asdata[1024];
+  int len;
+  struct test_spec sp;
+} test_segments [] = 
+{
+  { /* 0 */ 
+    "seq1",
+    "seq(8466,3,52737,4096)",
+    { 0x2,0x4, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00 },
+    10,
+    { "8466 3 52737 4096",
+      "8466 3 52737 4096",
+      4, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
+  },
+  { /* 1 */
+    "seq2",
+    "seq(8722) seq(4)",
+    { 0x2,0x1, 0x22,0x12,
+      0x2,0x1, 0x00,0x04 },
+    8,
+    { "8722 4",
+      "8722 4",
+      2, 0, NOT_ALL_PRIVATE, 4, 5, 8722, },
+  },
+  { /* 2 */
+    "seq3",
+    "seq(8466,3,52737,4096,8722,4)",
+    { 0x2,0x6, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 
+               0x22,0x12, 0x00,0x04},
+    14,
+    { "8466 3 52737 4096 8722 4",
+      "8466 3 52737 4096 8722 4",
+       6, 0, NOT_ALL_PRIVATE, 3, 5, 8466 },
+  },
+  { /* 3 */
+    "seqset",
+    "seq(8482,51457) set(5204)",
+    { 0x2,0x2, 0x21,0x22, 0xc9,0x01,
+      0x1,0x1, 0x14,0x54 },
+    10,
+    { "8482 51457 {5204}",
+      "8482 51457 {5204}",
+      3, 0, NOT_ALL_PRIVATE, 5204, 51456, 8482},
+  },
+  { /* 4 */
+    "seqset2",
+    "seq(8467, 59649) set(4196,48658) set(17322,30745)",
+    { 0x2,0x2, 0x21,0x13, 0xe9,0x01,
+      0x1,0x2, 0x10,0x64, 0xbe,0x12,
+      0x1,0x2, 0x43,0xaa, 0x78,0x19 },    
+    18,
+    { "8467 59649 {4196,48658} {17322,30745}",
+      "8467 59649 {4196,48658} {17322,30745}",
+      4, 0, NOT_ALL_PRIVATE, 48658, 1, 8467},
+  },
+  { /* 5 */
+    "multi",
+    "seq(6435,59408,21665) set(2457,61697,4369), seq(1842,41590,51793)",
+    { 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1,
+      0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11,
+      0x2,0x3, 0x07,0x32, 0xa2,0x76, 0xca,0x51 },
+    24,
+    { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+      "6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+      7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 },
+  },
+  { /* 6 */
+    "confed",
+    "confseq(123,456,789)",
+    { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15 },
+    8,
+    { "(123 456 789)",
+      "",
+      0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN },
+  },
+  { /* 7 */
+    "confed2",
+    "confseq(123,456,789) confseq(111,222)",
+    { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15,
+      0x3,0x2, 0x00,0x6f, 0x00,0xde },
+    14,
+    { "(123 456 789) (111 222)",
+      "",
+      0, 5, NOT_ALL_PRIVATE, 111, 1, NULL_ASN },
+  },
+  { /* 8 */
+    "confset",
+    "confset(456,123,789)",
+    { 0x4,0x3, 0x01,0xc8, 0x00,0x7b, 0x03,0x15 },
+    8,
+    { "[123,456,789]",
+      "[123,456,789]",
+      0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN },
+  },
+  { /* 9 */
+    "confmulti",
+    "confseq(123,456,789) confset(222,111) seq(8722) set(4196,48658)",
+    { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15,
+      0x4,0x2, 0x00,0xde, 0x00,0x6f,
+      0x2,0x1, 0x22,0x12,
+      0x1,0x2, 0x10,0x64, 0xbe,0x12 },
+    24,
+    { "(123 456 789) [111,222] 8722 {4196,48658}",
+      "8722 {4196,48658}",
+      2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN },
+  },
+  { /* 10 */
+    "seq4",
+    "seq(8466,2,52737,4096,8722,4)",
+    { 0x2,0x6, 0x21,0x12, 0x00,0x02, 0xce,0x01, 0x10,0x00, 
+               0x22,0x12, 0x00,0x04},
+    14,
+    { "8466 2 52737 4096 8722 4",
+      "8466 2 52737 4096 8722 4",
+      6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 },
+  },
+  { /* 11 */
+    "tripleseq1",
+    "seq(8466,2,52737) seq(4096,8722,4) seq(8722)",
+    { 0x2,0x3, 0x21,0x12, 0x00,0x02, 0xce,0x01, 
+      0x2,0x3, 0x10,0x00, 0x22,0x12, 0x00,0x04,
+      0x2,0x1, 0x22,0x12},
+    20,
+    { "8466 2 52737 4096 8722 4 8722",
+      "8466 2 52737 4096 8722 4 8722",
+      7, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 },
+  },
+  { /* 12 */ 
+    "someprivate",
+    "seq(8466,64512,52737,65535)",
+    { 0x2,0x4, 0x21,0x12, 0xfc,0x00, 0xce,0x01, 0xff,0xff },
+    10,
+    { "8466 64512 52737 65535",
+      "8466 64512 52737 65535",
+      4, 0, NOT_ALL_PRIVATE, 65535, 4, 8466 },
+  },
+  { /* 13 */ 
+    "allprivate",
+    "seq(65534,64512,64513,65535)",
+    { 0x2,0x4, 0xff,0xfe, 0xfc,0x00, 0xfc,0x01, 0xff,0xff },
+    10,
+    { "65534 64512 64513 65535",
+      "65534 64512 64513 65535",
+      4, 0, ALL_PRIVATE, 65534, 4, 65534 },
+  },
+  { /* 14 */ 
+    "long",
+    "seq(8466,3,52737,4096,34285,<repeated 49 more times>)",
+    { 0x2,0xfa, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed,
+                0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, },
+    502,
+    { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+      
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+      250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
+  },
+  { /* 15 */ 
+    "seq1extra",
+    "seq(8466,3,52737,4096,3456)",
+    { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 },
+    12,
+    { "8466 3 52737 4096 3456",
+      "8466 3 52737 4096 3456",
+      5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
+  },
+  { /* 16 */
+    "empty",
+    "<empty>",
+    {},
+    0,
+    { "", "", 0, 0, 0, 0, 0, 0 },
+  },
+  { /* 17 */ 
+    "redundantset",
+    "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153)",
+    { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80,
+      0x1,0x4, 0x1b,0xbb, 0x1f,0xd9, 0x1f,0xd9, 0x1f,0xd9 },
+    22,
+    {
+     /* We shouldn't ever /generate/ such paths. However, we should
+      * cope with them fine.
+      */
+     "8466 3 52737 4096 3456 {7099,8153}",
+      "8466 3 52737 4096 3456 {7099,8153}",
+      6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
+  },
+  { /* 18 */
+    "reconcile_lead_asp",
+    "seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
+    { 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1,
+      0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0,
+      0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 },
+    24,
+    { "6435 59408 21665 {23456} 23456 23456 23456",
+      "6435 59408 21665 {23456} 23456 23456 23456",
+      7, 0, NOT_ALL_PRIVATE, 23456, 1, 6435 },
+  },
+  { /* 19 */
+    "reconcile_new_asp",
+    "set(2457,61697,4369), seq(1842,41591,51793)",
+    { 
+      0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11,
+      0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51 },
+    16,
+    { "{2457,4369,61697} 1842 41591 51793",
+      "{2457,4369,61697} 1842 41591 51793",
+      4, 0, NOT_ALL_PRIVATE, 51793, 1, 2457 },
+  },
+  { /* 20 */
+    "reconcile_confed",
+    "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)"
+    " set(23456,23456,23456), seq(23456,23456,23456)",
+    { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15,
+      0x4,0x3, 0x01,0xc8, 0x00,0x7c, 0x03,0x14,
+      0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1,
+      0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0,
+      0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 },
+    40,
+    { "(123 456 789) [124,456,788] 6435 59408 21665"
+      " {23456} 23456 23456 23456",
+      "6435 59408 21665 {23456} 23456 23456 23456",
+      7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435 },
+  },
+  { /* 21 */
+    "reconcile_start_trans",
+    "seq(23456,23456,23456) seq(6435,59408,21665)",
+    { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0,
+      0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, },
+    16,
+    { "23456 23456 23456 6435 59408 21665",
+      "23456 23456 23456 6435 59408 21665",
+      6, 0, NOT_ALL_PRIVATE, 21665, 1, 23456 },
+  },
+  { /* 22 */
+    "reconcile_start_trans4",
+    "seq(1842,41591,51793) seq(6435,59408,21665)",
+    { 0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51,
+      0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, },
+    16,
+    { "1842 41591 51793 6435 59408 21665",
+      "1842 41591 51793 6435 59408 21665",
+      6, 0, NOT_ALL_PRIVATE, 41591, 1, 1842 },
+  },
+  { /* 23 */
+    "reconcile_start_trans_error",
+    "seq(23456,23456,23456) seq(6435,59408)",
+    { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0,
+      0x2,0x2, 0x19,0x23, 0xe8,0x10, },
+    14,
+    { "23456 23456 23456 6435 59408",
+      "23456 23456 23456 6435 59408",
+      5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456 },
+  },
+  { /* 24 */ 
+    "redundantset2",
+    "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)",
+    { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80,
+      0x1,0x5, 0x1b,0xbb, 0x1f,0xd9, 0x1f,0xd9, 0x1f,0xd9, 0x1b,0xbb,},
+    24,
+    {
+     /* We should weed out duplicate set members. */
+     "8466 3 52737 4096 3456 {7099,8153}",
+      "8466 3 52737 4096 3456 {7099,8153}",
+      6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 },
+  },
+  { /* 25 */ 
+    "zero-size overflow",
+    "#ASNs = 0, data = seq(8466 3 52737 4096 3456)",
+    { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 },
+    12,
+    { NULL, NULL,
+      0, 0, 0, 0, 0, 0 },
+  },
+  { /* 26  */ 
+    "zero-size overflow + valid segment",
+    "seq(#AS=0:8466 3 52737),seq(4096 3456)",
+    { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, 
+      0x2,0x2, 0x10,0x00, 0x0d,0x80 },
+    14
+    ,
+    { NULL, NULL,
+      0, 0, 0, 0, 0, 0 },
+  },
+  { /* 27  */ 
+    "invalid segment type",
+    "type=8(4096 3456)",
+    { 0x8,0x2, 0x10,0x00, 0x0d,0x80 },
+    14
+    ,
+    { NULL, NULL,
+      0, 0, 0, 0, 0, 0 },
+  },  { NULL, NULL, {0}, 0, { NULL, 0, 0 } }
+};
+
+#define COMMON_ATTRS \
+      BGP_ATTR_FLAG_TRANS, \
+      BGP_ATTR_ORIGIN, \
+      1, \
+      BGP_ORIGIN_EGP, \
+      BGP_ATTR_FLAG_TRANS, \
+      BGP_ATTR_NEXT_HOP, \
+      4, 192, 0, 2, 0
+#define COMMON_ATTR_SIZE 11
+
+/* */
+static struct aspath_tests {
+  const char *desc;
+  const struct test_segment *segment;
+  const char *shouldbe;  /* String it should evaluate to */
+  const enum as4 { AS4_DATA, AS2_DATA }
+          as4;         /* whether data should be as4 or not (ie as2) */
+  const int result;    /* expected result for bgp_attr_parse */
+  const int cap;       /* capabilities to set for peer */
+  const char attrheader [1024];
+  size_t len;
+  const struct test_segment *old_segment;
+} aspath_tests [] =
+{
+  /* 0 */
+  {
+    "basic test",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS2_DATA, 0,
+    0,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH,
+      10,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 1 */
+  {
+    "length too short",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS2_DATA, -1,
+    0,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      8,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 2 */
+  {
+    "length too long",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS2_DATA, -1,
+    0,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      12,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 3 */
+  {
+    "incorrect flag",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS2_DATA, -1,
+    0,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS_PATH, 
+      10,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 4 */
+  {
+    "as4_path, with as2 format data",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS2_DATA, -1,
+    0,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS4_PATH, 
+      10,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 5 */
+  {
+    "as4, with incorrect attr length",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, -1,
+    PEER_CAP_AS4_RCV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS4_PATH, 
+      10,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 6 */
+  {
+    "basic 4-byte as-path",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, 0,
+    PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      18,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 7 */
+  {
+    "4b AS_PATH: too short",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, -1,
+    PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      16,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 8 */
+  {
+    "4b AS_PATH: too long",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, -1,
+    PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      20,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 9 */
+  {
+    "4b AS_PATH: too long2",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, -1,
+    PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS,
+      BGP_ATTR_AS_PATH, 
+      22,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 10 */
+  {
+    "4b AS_PATH: bad flags",
+    &test_segments[0],
+    "8466 3 52737 4096",
+    AS4_DATA, -1,
+    PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS_PATH, 
+      18,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 11 */
+  {
+    "4b AS4_PATH w/o AS_PATH",
+    &test_segments[6],
+    NULL,
+    AS4_DATA, -1,
+    PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS4_PATH, 
+      14,
+    },
+    COMMON_ATTR_SIZE + 3,
+  },
+  /* 12 */
+  {
+    "4b AS4_PATH: confed",
+    &test_segments[6],
+    "8466 3 52737 4096 (123 456 789)",
+    AS4_DATA, 0,
+    PEER_CAP_AS4_ADV,
+    { COMMON_ATTRS,
+      BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL,
+      BGP_ATTR_AS4_PATH, 
+      14,
+    },
+    COMMON_ATTR_SIZE + 3,
+    &test_segments[0],
+  },
+  { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 },
+};
+
+/* prepending tests */
+static struct tests {
+  const struct test_segment *test1;
+  const struct test_segment *test2;
+  struct test_spec sp;
+} prepend_tests[] = 
+{
+  /* 0 */
+  { &test_segments[0], &test_segments[1],
+    { "8466 3 52737 4096 8722 4",
+      "8466 3 52737 4096 8722 4",
+      6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 },
+  },
+  /* 1 */
+  { &test_segments[1], &test_segments[3],
+    { "8722 4 8482 51457 {5204}",
+      "8722 4 8482 51457 {5204}",
+      5, 0, NOT_ALL_PRIVATE, 5204, 1, 8722 }
+  },
+  /* 2 */
+  { &test_segments[3], &test_segments[4],
+    { "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}",
+      "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}",
+      7, 0, NOT_ALL_PRIVATE, 5204, 1, 8482 },
+  },
+  /* 3 */
+  { &test_segments[4], &test_segments[5],
+    { "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665"
+      " {2457,4369,61697} 1842 41590 51793",
+      "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665"
+      " {2457,4369,61697} 1842 41590 51793",
+      11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467 }
+  },
+  /* 4 */
+  { &test_segments[5], &test_segments[6],
+    { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+      "6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+      7, 0, NOT_ALL_PRIVATE, 1842, 1, 6435 },
+  },
+  /* 5 */
+  { &test_segments[6], &test_segments[7],
+    { "(123 456 789) (123 456 789) (111 222)",
+      "",
+      0, 8, NOT_ALL_PRIVATE, 111, 1, 0 }
+  },
+  { &test_segments[7], &test_segments[8],
+    { "(123 456 789) (111 222) [123,456,789]",
+      "",
+      0, 6, NOT_ALL_PRIVATE, 111, 1, 0 }
+  },
+  { &test_segments[8], &test_segments[9],
+    { "[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}",
+      "[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}",
+      2, 5, NOT_ALL_PRIVATE, 456, 1, NULL_ASN },
+  },
+  { &test_segments[9], &test_segments[8],
+    { "(123 456 789) [111,222] 8722 {4196,48658} [123,456,789]",
+      "8722 {4196,48658} [123,456,789]",
+      2, 5, NOT_ALL_PRIVATE, 48658, 1, NULL_ASN },
+  },
+  { &test_segments[14], &test_segments[11],
+    { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 2 52737 4096 8722 4 8722",
+      
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 3 52737 4096 34285 8466 3 52737 4096 34285 "
+      "8466 2 52737 4096 8722 4 8722",
+      257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466 },
+  },
+  { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } },
+};
+
+struct tests reconcile_tests[] =
+{
+  { &test_segments[18], &test_segments[19],
+    { "6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+      "6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+      7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 },
+  },
+  { &test_segments[19], &test_segments[18],
+    /* AS_PATH (19) has more hops than NEW_AS_PATH,
+     * so just AS_PATH should be used (though, this practice
+     * is bad imho).
+     */
+    { "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+      "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+      11, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 },
+  },
+  { &test_segments[20], &test_segments[19],
+    { "(123 456 789) [124,456,788] 6435 59408 21665"
+      " {2457,4369,61697} 1842 41591 51793",
+      "6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+      7, 4, NOT_ALL_PRIVATE, 51793, 1, 6435 },
+  },
+  { &test_segments[21], &test_segments[22],
+    { "1842 41591 51793 6435 59408 21665",
+      "1842 41591 51793 6435 59408 21665",
+      6, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 },
+  },
+  { &test_segments[23], &test_segments[22],
+    { "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+      "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+      11, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 },
+  },
+  { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } },
+};
+  
+struct tests aggregate_tests[] =
+{
+  { &test_segments[0], &test_segments[2],
+    { "8466 3 52737 4096 {4,8722}",
+      "8466 3 52737 4096 {4,8722}",
+      5, 0, NOT_ALL_PRIVATE, 4, 1, 8466 },
+  },
+  { &test_segments[2], &test_segments[0],
+    { "8466 3 52737 4096 {4,8722}",
+      "8466 3 52737 4096 {4,8722}",
+      5, 0, NOT_ALL_PRIVATE, 8722, 1, 8466 },
+  },
+  { &test_segments[2], &test_segments[10],
+    { "8466 {2,3,4,4096,8722,52737}",
+      "8466 {2,3,4,4096,8722,52737}",
+      2, 0, NOT_ALL_PRIVATE, 8722, 5, 8466 },
+  },
+  { &test_segments[10], &test_segments[2],
+    { "8466 {2,3,4,4096,8722,52737}",
+      "8466 {2,3,4,4096,8722,52737}",
+      2, 0, NOT_ALL_PRIVATE, 2, 20000, 8466 },
+  },
+
+  { &test_segments[5], &test_segments[18],
+    { "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}",
+      "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}",
+      4, 0, NOT_ALL_PRIVATE, 41590, 1, 6435 },
+  },
+
+  { NULL, NULL, { NULL, 0, 0}  },
+};
+
+struct compare_tests 
+{
+  int test_index1;
+  int test_index2;
+#define CMP_RES_YES 1
+#define CMP_RES_NO 0
+  char shouldbe_cmp;
+  char shouldbe_confed;
+} left_compare [] =
+{
+  { 0, 1, CMP_RES_NO, CMP_RES_NO },
+  { 0, 2, CMP_RES_YES, CMP_RES_NO },
+  { 0, 11, CMP_RES_YES, CMP_RES_NO },
+  { 0, 15, CMP_RES_YES, CMP_RES_NO },
+  { 0, 16, CMP_RES_NO, CMP_RES_NO },
+  { 1, 11, CMP_RES_NO, CMP_RES_NO },
+  { 6, 7, CMP_RES_NO, CMP_RES_YES },
+  { 6, 8, CMP_RES_NO, CMP_RES_NO },
+  { 7, 8, CMP_RES_NO, CMP_RES_NO },
+  { 1, 9, CMP_RES_YES, CMP_RES_NO },
+  { 0, 9, CMP_RES_NO, CMP_RES_NO },
+  { 3, 9, CMP_RES_NO, CMP_RES_NO },
+  { 0, 6, CMP_RES_NO, CMP_RES_NO },
+  { 1, 6, CMP_RES_NO, CMP_RES_NO },
+  { 0, 8, CMP_RES_NO, CMP_RES_NO },
+  { 1, 8, CMP_RES_NO, CMP_RES_NO },
+  { 11, 6, CMP_RES_NO, CMP_RES_NO },
+  { 11, 7, CMP_RES_NO, CMP_RES_NO },
+  { 11, 8, CMP_RES_NO, CMP_RES_NO },
+  { 9, 6, CMP_RES_NO, CMP_RES_YES },
+  { 9, 7, CMP_RES_NO, CMP_RES_YES },
+  { 9, 8, CMP_RES_NO, CMP_RES_NO },
+};
+
+/* make an aspath from a data stream */
+static struct aspath *
+make_aspath (const u_char *data, size_t len, int use32bit)
+{
+  struct stream *s = NULL;
+  struct aspath *as;
+  
+  if (len)
+    {
+      s = stream_new (len);
+      stream_put (s, data, len);
+    }
+  as = aspath_parse (s, len, use32bit);
+  
+  if (s)
+    stream_free (s);
+  
+  return as;
+}
+
+static void
+printbytes (const u_char *bytes, int len)
+{
+  int i = 0;
+  while (i < len)
+    {
+      if (i % 2)
+        printf ("%02hhx%s", bytes[i], " ");
+      else
+        printf ("0x%02hhx", bytes[i]);
+      i++;
+    }
+  printf ("\n");
+}  
+
+/* validate the given aspath */
+static int
+validate (struct aspath *as, const struct test_spec *sp)
+{
+  size_t bytes, bytes4;
+  int fails = 0;
+  const u_char *out;
+  static struct stream *s;
+  struct aspath *asinout, *asconfeddel, *asstr, *as4;
+  
+  if (as == NULL && sp->shouldbe == NULL)
+    {
+      printf ("Correctly failed to parse\n");
+      return fails;
+    }
+  
+  out = aspath_snmp_pathseg (as, &bytes);
+  asinout = make_aspath (out, bytes, 0);
+  
+  /* Excercise AS4 parsing a bit, with a dogfood test */
+  if (!s)
+    s = stream_new (4096);
+  bytes4 = aspath_put (s, as, 1);
+  as4 = make_aspath (STREAM_DATA(s), bytes4, 1);
+  
+  asstr = aspath_str2aspath (sp->shouldbe);
+  
+  asconfeddel = aspath_delete_confed_seq (aspath_dup (asinout));
+  
+  printf ("got: %s\n", aspath_print(as));
+  
+  /* the parsed path should match the specified 'shouldbe' string.
+   * We should pass the "eat our own dog food" test, be able to output
+   * this path and then input it again. Ie the path resulting from:
+   *
+   *   aspath_parse(aspath_put(as)) 
+   *
+   * should:
+   *
+   * - also match the specified 'shouldbe' value
+   * - hash to same value as original path
+   * - have same hops and confed counts as original, and as the
+   *   the specified counts
+   *
+   * aspath_str2aspath() and shouldbe should match
+   *
+   * We do the same for:
+   *
+   *   aspath_parse(aspath_put(as,USE32BIT))
+   *
+   * Confederation related tests: 
+   * - aspath_delete_confed_seq(aspath) should match shouldbe_confed
+   * - aspath_delete_confed_seq should be idempotent.
+   */
+  if (strcmp(aspath_print (as), sp->shouldbe)
+         /* hash validation */
+      || (aspath_key_make (as) != aspath_key_make (asinout))
+         /* by string */
+      || strcmp(aspath_print (asinout), sp->shouldbe)
+         /* By 4-byte parsing */
+      || strcmp(aspath_print (as4), sp->shouldbe)
+         /* by various path counts */
+      || (aspath_count_hops (as) != sp->hops)
+      || (aspath_count_confeds (as) != sp->confeds)
+      || (aspath_count_hops (asinout) != sp->hops)
+      || (aspath_count_confeds (asinout) != sp->confeds))
+    {
+      failed++;
+      fails++;
+      printf ("shouldbe:\n%s\n", sp->shouldbe);
+      printf ("as4:\n%s\n", aspath_print (as4));
+      printf ("hash keys: in: %d out->in: %d\n", 
+              aspath_key_make (as), aspath_key_make (asinout));
+      printf ("hops: %d, counted %d %d\n", sp->hops, 
+              aspath_count_hops (as),
+              aspath_count_hops (asinout) );
+      printf ("confeds: %d, counted %d %d\n", sp->confeds,
+              aspath_count_confeds (as),
+              aspath_count_confeds (asinout));
+      printf ("out->in:\n%s\nbytes: ", aspath_print(asinout));
+      printbytes (out, bytes);
+    }
+         /* basic confed related tests */
+  if ((aspath_print (asconfeddel) == NULL 
+          && sp->shouldbe_delete_confed != NULL)
+      || (aspath_print (asconfeddel) != NULL 
+          && sp->shouldbe_delete_confed == NULL)
+      || strcmp(aspath_print (asconfeddel), sp->shouldbe_delete_confed)
+         /* delete_confed_seq should be idempotent */
+      || (aspath_key_make (asconfeddel) 
+          != aspath_key_make (aspath_delete_confed_seq (asconfeddel))))
+    {
+      failed++;
+      fails++;
+      printf ("confed_del: %s\n", aspath_print (asconfeddel));
+      printf ("should be: %s\n", sp->shouldbe_delete_confed);
+    }
+      /* aspath_str2aspath test */
+  if ((aspath_print (asstr) == NULL && sp->shouldbe != NULL)
+      || (aspath_print (asstr) != NULL && sp->shouldbe == NULL)
+      || strcmp(aspath_print (asstr), sp->shouldbe))
+    {
+      failed++;
+      fails++;
+      printf ("asstr: %s\n", aspath_print (asstr));
+    }
+  
+    /* loop, private and first as checks */
+  if ((sp->does_loop && aspath_loop_check (as, sp->does_loop) == 0)
+      || (sp->doesnt_loop && aspath_loop_check (as, sp->doesnt_loop) != 0)
+      || (aspath_private_as_check (as) != sp->private_as)
+      || (aspath_firstas_check (as,sp->first)
+          && sp->first == 0))
+    {
+      failed++;
+      fails++;
+      printf ("firstas: %d,  got %d\n", sp->first,
+              aspath_firstas_check (as,sp->first));
+      printf ("loop does: %d %d, doesnt: %d %d\n",
+              sp->does_loop, aspath_loop_check (as, sp->does_loop),
+              sp->doesnt_loop, aspath_loop_check (as, sp->doesnt_loop));
+      printf ("private check: %d %d\n", sp->private_as,
+              aspath_private_as_check (as));
+    }
+  aspath_unintern (&asinout);
+  aspath_unintern (&as4);
+  
+  aspath_free (asconfeddel);
+  aspath_free (asstr);
+  stream_reset (s);
+  
+  return fails;
+}
+
+static void
+empty_get_test ()
+{
+  struct aspath *as = aspath_empty_get ();
+  struct test_spec sp = { "", "", 0, 0, 0, 0, 0, 0 };
+
+  printf ("empty_get_test, as: %s\n",aspath_print (as));
+  if (!validate (as, &sp))
+    printf ("%s\n", OK);
+  else
+    printf ("%s!\n", FAILED);
+  
+  printf ("\n");
+  
+  aspath_free (as);
+}
+
+/* basic parsing test */
+static void
+parse_test (struct test_segment *t)
+{
+  struct aspath *asp;
+  
+  printf ("%s: %s\n", t->name, t->desc);
+
+  asp = make_aspath (t->asdata, t->len, 0);
+  
+  printf ("aspath: %s\nvalidating...:\n", aspath_print (asp));
+
+  if (!validate (asp, &t->sp))
+    printf (OK "\n");
+  else
+    printf (FAILED "\n");
+  
+  printf ("\n");
+  
+  if (asp)
+    aspath_unintern (&asp);
+}
+
+/* prepend testing */
+static void
+prepend_test (struct tests *t)
+{
+  struct aspath *asp1, *asp2, *ascratch;
+  
+  printf ("prepend %s: %s\n", t->test1->name, t->test1->desc);
+  printf ("to %s: %s\n", t->test2->name, t->test2->desc);
+  
+  asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
+  asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
+  
+  ascratch = aspath_dup (asp2);
+  aspath_unintern (&asp2);
+  
+  asp2 = aspath_prepend (asp1, ascratch);
+  
+  printf ("aspath: %s\n", aspath_print (asp2));
+  
+  if (!validate (asp2, &t->sp))
+    printf ("%s\n", OK);
+  else
+    printf ("%s!\n", FAILED);
+  
+  printf ("\n");
+  aspath_unintern (&asp1);
+  aspath_free (asp2);
+}
+
+/* empty-prepend testing */
+static void
+empty_prepend_test (struct test_segment *t)
+{
+  struct aspath *asp1, *asp2, *ascratch;
+  
+  printf ("empty prepend %s: %s\n", t->name, t->desc);
+  
+  asp1 = make_aspath (t->asdata, t->len, 0);
+  asp2 = aspath_empty ();
+  
+  ascratch = aspath_dup (asp2);
+  aspath_unintern (&asp2);
+  
+  asp2 = aspath_prepend (asp1, ascratch);
+  
+  printf ("aspath: %s\n", aspath_print (asp2));
+  
+  if (!validate (asp2, &t->sp))
+    printf (OK "\n");
+  else
+    printf (FAILED "!\n");
+  
+  printf ("\n");
+  if (asp1)
+    aspath_unintern (&asp1);
+  aspath_free (asp2);
+}
+
+/* as2+as4 reconciliation testing */
+static void
+as4_reconcile_test (struct tests *t)
+{
+  struct aspath *asp1, *asp2, *ascratch;
+  
+  printf ("reconciling %s:\n  %s\n", t->test1->name, t->test1->desc);
+  printf ("with %s:\n  %s\n", t->test2->name, t->test2->desc);
+  
+  asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
+  asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
+  
+  ascratch = aspath_reconcile_as4 (asp1, asp2);
+  
+  if (!validate (ascratch, &t->sp))
+    printf (OK "\n");
+  else
+    printf (FAILED "!\n");
+  
+  printf ("\n");
+  aspath_unintern (&asp1);
+  aspath_unintern (&asp2);
+  aspath_free (ascratch);
+}
+
+
+/* aggregation testing */
+static void
+aggregate_test (struct tests *t)
+{
+  struct aspath *asp1, *asp2, *ascratch;
+  
+  printf ("aggregate %s: %s\n", t->test1->name, t->test1->desc);
+  printf ("with %s: %s\n", t->test2->name, t->test2->desc);
+  
+  asp1 = make_aspath (t->test1->asdata, t->test1->len, 0);
+  asp2 = make_aspath (t->test2->asdata, t->test2->len, 0);
+  
+  ascratch = aspath_aggregate (asp1, asp2);
+  
+  if (!validate (ascratch, &t->sp))
+    printf (OK "\n");
+  else
+    printf (FAILED "!\n");
+  
+  printf ("\n");
+  aspath_unintern (&asp1);
+  aspath_unintern (&asp2);
+  aspath_free (ascratch);
+/*  aspath_unintern (ascratch);*/
+}
+
+/* cmp_left tests  */
+static void
+cmp_test ()
+{
+  unsigned int i;
+#define CMP_TESTS_MAX \
+  (sizeof(left_compare) / sizeof (struct compare_tests))
+
+  for (i = 0; i < CMP_TESTS_MAX; i++)
+    {
+      struct test_segment *t1 = &test_segments[left_compare[i].test_index1];
+      struct test_segment *t2 = &test_segments[left_compare[i].test_index2];
+      struct aspath *asp1, *asp2;
+      
+      printf ("left cmp %s: %s\n", t1->name, t1->desc);
+      printf ("and %s: %s\n", t2->name, t2->desc);
+      
+      asp1 = make_aspath (t1->asdata, t1->len, 0);
+      asp2 = make_aspath (t2->asdata, t2->len, 0);
+      
+      if (aspath_cmp_left (asp1, asp2) != left_compare[i].shouldbe_cmp
+          || aspath_cmp_left (asp2, asp1) != left_compare[i].shouldbe_cmp
+          || aspath_cmp_left_confed (asp1, asp2) 
+               != left_compare[i].shouldbe_confed
+          || aspath_cmp_left_confed (asp2, asp1) 
+               != left_compare[i].shouldbe_confed)
+        {
+          failed++;
+          printf (FAILED "\n");
+          printf ("result should be: cmp: %d, confed: %d\n", 
+                  left_compare[i].shouldbe_cmp,
+                  left_compare[i].shouldbe_confed);
+          printf ("got: cmp %d, cmp_confed: %d\n",
+                  aspath_cmp_left (asp1, asp2),
+                  aspath_cmp_left_confed (asp1, asp2));
+          printf("path1: %s\npath2: %s\n", aspath_print (asp1),
+                 aspath_print (asp2));
+        }
+      else
+        printf (OK "\n");
+      
+      printf ("\n");
+      aspath_unintern (&asp1);
+      aspath_unintern (&asp2);
+    }
+}
+
+static int
+handle_attr_test (struct aspath_tests *t)
+{
+  struct bgp bgp = { 0 }; 
+  struct peer peer = { 0 };
+  struct attr attr = { 0 };  
+  int ret;
+  int initfail = failed;
+  struct aspath *asp;
+  size_t datalen;
+  
+  asp = make_aspath (t->segment->asdata, t->segment->len, 0);
+    
+  peer.ibuf = stream_new (BGP_MAX_PACKET_SIZE);
+  peer.obuf = stream_fifo_new ();
+  peer.bgp = &bgp;
+  peer.host = (char *)"none";
+  peer.fd = -1;
+  peer.cap = t->cap;
+  
+  stream_write (peer.ibuf, t->attrheader, t->len);
+  datalen = aspath_put (peer.ibuf, asp, t->as4 == AS4_DATA);
+  if (t->old_segment)
+    {
+      char dummyaspath[] = { BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH,
+                             t->old_segment->len };
+      stream_write (peer.ibuf, dummyaspath, sizeof (dummyaspath));
+      stream_write (peer.ibuf, t->old_segment->asdata, t->old_segment->len);
+      datalen += sizeof (dummyaspath) + t->old_segment->len;
+    }
+  
+  ret = bgp_attr_parse (&peer, &attr, t->len + datalen, NULL, NULL);
+  
+  if (ret != t->result)
+    {
+      printf ("bgp_attr_parse returned %d, expected %d\n", ret, t->result);
+      printf ("datalen %zd\n", datalen);
+      failed++;
+    }
+  if (ret != 0)
+    goto out;
+  
+  if (t->shouldbe && attr.aspath == NULL)
+    {
+      printf ("aspath is NULL, but should be: %s\n", t->shouldbe);
+      failed++;
+    }
+  if (t->shouldbe && attr.aspath && strcmp (attr.aspath->str, t->shouldbe))
+    {
+      printf ("attr str and 'shouldbe' mismatched!\n"
+              "attr str:  %s\n"
+              "shouldbe:  %s\n",
+              attr.aspath->str, t->shouldbe);
+      failed++;
+    }
+  if (!t->shouldbe && attr.aspath)
+    {
+      printf ("aspath should be NULL, but is: %s\n", attr.aspath->str);
+      failed++;
+    }
+
+out:
+  if (attr.aspath)
+    aspath_unintern (&attr.aspath);
+  if (asp)
+    aspath_unintern (&asp);
+  return failed - initfail;
+}
+
+static void
+attr_test (struct aspath_tests *t)
+{
+    printf ("%s\n", t->desc);
+    printf ("%s\n\n", handle_attr_test (t) ? FAILED : OK);  
+}
+
+int
+main (void)
+{
+  int i = 0;
+  bgp_master_init ();
+  master = bm->master;
+  bgp_option_set (BGP_OPT_NO_LISTEN);
+  bgp_attr_init ();
+  
+  while (test_segments[i].name)
+    {
+      printf ("test %u\n", i);
+      parse_test (&test_segments[i]);
+      empty_prepend_test (&test_segments[i++]);
+    }
+  
+  i = 0;
+  while (prepend_tests[i].test1)
+    {
+      printf ("prepend test %u\n", i);
+      prepend_test (&prepend_tests[i++]);
+    }
+  
+  i = 0;
+  while (aggregate_tests[i].test1)
+    {
+      printf ("aggregate test %u\n", i);
+      aggregate_test (&aggregate_tests[i++]);
+    }
+  
+  i = 0;
+  
+  while (reconcile_tests[i].test1)
+    {
+      printf ("reconcile test %u\n", i);
+      as4_reconcile_test (&reconcile_tests[i++]);
+    }
+  
+  i = 0;
+  
+  cmp_test();
+  
+  i = 0;
+  
+  empty_get_test();
+  
+  i = 0;
+  
+  while (aspath_tests[i].desc)
+    {
+      printf ("aspath_attr test %d\n", i);
+      attr_test (&aspath_tests[i++]);
+    }
+  
+  printf ("failures: %d\n", failed);
+  printf ("aspath count: %ld\n", aspath_count());
+  
+  return (failed + aspath_count());
+}
diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c
new file mode 100644 (file)
index 0000000..a381351
--- /dev/null
@@ -0,0 +1,697 @@
+/* 
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+
+#define CAPABILITY 0
+#define DYNCAP     1
+#define OPT_PARAM  2
+
+/* need these to link in libbgp */
+struct zebra_privs_t *bgpd_privs = NULL;
+struct thread_master *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+  const char *name;
+  const char *desc;
+  const u_char data[1024];
+  int len;
+#define SHOULD_PARSE   0
+#define SHOULD_ERR     -1
+  int parses; /* whether it should parse or not */
+  as_t peek_for; /* what peek_for_as4_capability should say */
+  
+  /* AFI/SAFI validation */
+  int validate_afi;
+  afi_t afi;
+  safi_t safi;
+#define VALID_AFI 1
+#define INVALID_AFI 0
+  int afi_valid;
+} test_segments [] = 
+{
+  /* 0 */
+  { "caphdr", 
+    "capability header, and no more",
+    { CAPABILITY_CODE_REFRESH, 0x0 },
+    2, SHOULD_PARSE,
+  },
+  /* 1 */
+  { "nodata",
+    "header, no data but length says there is",
+    { 0x1, 0xa },
+    2, SHOULD_ERR,
+  },
+  /* 2 */
+  { "padded",
+    "valid, with padding",
+    { CAPABILITY_CODE_REFRESH, 0x2, 0x0, 0x0 },
+    4, SHOULD_PARSE,
+  },
+  /* 3 */
+  { "minsize",
+    "violates minsize requirement",
+    { CAPABILITY_CODE_ORF, 0x2, 0x0, 0x0 },
+    4, SHOULD_ERR,
+  },
+  { NULL, NULL, {0}, 0, 0},
+};
+
+static struct test_segment mp_segments[] =
+{
+  { "MP4",
+    "MP IP/Uni",
+    { 0x1, 0x4, 0x0, 0x1, 0x0, 0x1 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "MPv6",
+    "MP IPv6/Uni",
+    { 0x1, 0x4, 0x0, 0x2, 0x0, 0x1 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  /* 5 */
+  { "MP2",
+    "MP IP/Multicast",
+    { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x2 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP, SAFI_MULTICAST, VALID_AFI,
+  },
+  /* 6 */
+  { "MP3",
+    "MP IP6/MPLS-labeled VPN",
+    { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x80 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP6, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  /* 7 */
+  { "MP5",
+    "MP IP6/MPLS-VPN",
+    { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x4 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP6, SAFI_MPLS_VPN, VALID_AFI,
+  },
+  /* 8 */
+  { "MP6",
+    "MP IP4/MPLS-laveled VPN",
+    { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80 },
+    6, SHOULD_PARSE, 0,
+    1, AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },  
+  /* 10 */
+  { "MP8",
+    "MP unknown AFI/SAFI",
+    { CAPABILITY_CODE_MP, 0x4, 0x0, 0xa, 0x0, 0x81 },
+    6, SHOULD_PARSE, 0,
+    1, 0xa, 0x81, INVALID_AFI, /* parses, but unknown */
+  },
+  /* 11 */
+  { "MP-short",
+    "MP IP4/Unicast, length too short (< minimum)",
+    { CAPABILITY_CODE_MP, 0x2, 0x0, 0x1, 0x0, 0x1 },
+    6, SHOULD_ERR,
+  },
+  /* 12 */
+  { "MP-overflow",
+    "MP IP4/Unicast, length too long",
+    { CAPABILITY_CODE_MP, 0x6, 0x0, 0x1, 0x0, 0x1 },
+    6, SHOULD_ERR, 0,
+    1, AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { NULL, NULL, {0}, 0, 0}
+};
+
+static struct test_segment misc_segments[] =
+{
+  /* 13 */
+  { "ORF",
+    "ORF, simple, single entry, single tuple",
+    { /* hdr */                CAPABILITY_CODE_ORF, 0x7, 
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x1, 
+      /* tuples */     0x40, 0x3
+    },
+    9, SHOULD_PARSE,
+  },
+  /* 14 */
+  { "ORF-many",
+    "ORF, multi entry/tuple",
+    { /* hdr */                CAPABILITY_CODE_ORF, 0x21,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, ORF_MODE_BOTH,
+                        0x80, ORF_MODE_RECEIVE,
+                        0x80, ORF_MODE_SEND,
+      /* mpc */                0x0, 0x2, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, ORF_MODE_BOTH,
+                        0x80, ORF_MODE_RECEIVE,
+                        0x80, ORF_MODE_SEND,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3, 
+      /* tuples */     0x40, ORF_MODE_RECEIVE,
+                        0x80, ORF_MODE_SEND,
+                        0x80, ORF_MODE_BOTH,
+    },
+    35, SHOULD_PARSE,
+  },
+  /* 15 */
+  { "ORFlo",
+    "ORF, multi entry/tuple, hdr length too short",
+    { /* hdr */                CAPABILITY_CODE_ORF, 0x15,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+    },
+    35, SHOULD_ERR, /* It should error on invalid Route-Refresh.. */
+  },
+  /* 16 */
+  { "ORFlu",
+    "ORF, multi entry/tuple, length too long",
+    { /* hdr */                0x3, 0x22,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+    },
+    35, SHOULD_ERR
+  },
+  /* 17 */
+  { "ORFnu",
+    "ORF, multi entry/tuple, entry number too long",
+    { /* hdr */                0x3, 0x21,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x1, 
+      /* num */                0x4, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+    },
+    35, SHOULD_PARSE, /* parses, but last few tuples should be gibberish */
+  },
+  /* 18 */
+  { "ORFno",
+    "ORF, multi entry/tuple, entry number too short",
+    { /* hdr */                0x3, 0x21,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x1, 
+      /* num */                0x1, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3,
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+    },
+    35, SHOULD_PARSE, /* Parses, but should get gibberish afi/safis */
+  },
+  /* 17 */
+  { "ORFpad",
+    "ORF, multi entry/tuple, padded to align",
+    { /* hdr */                0x3, 0x22,
+      /* mpc */                0x0, 0x1, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x1, 
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+      /* mpc */                0x0, 0x2, 0x0, 0x2,
+      /* num */                0x3, 
+      /* tuples */     0x40, 0x3,
+                        0x80, 0x1,
+                        0x80, 0x2,
+                        0x00,
+    },
+    36, SHOULD_PARSE,
+  },
+  /* 19 */
+  { "AS4",
+    "AS4 capability",
+    { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12 }, /* AS: 2882400018 */
+    6, SHOULD_PARSE, 2882400018,
+  },
+  { "AS4",
+    "AS4 capability: short",
+    { 0x41, 0x4, 0xab, 0xcd, 0xef }, /* AS: 2882400018 */
+    5, SHOULD_ERR,
+  },
+  { "AS4",
+    "AS4 capability: long",
+    { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12, 0x12 },
+    7, SHOULD_ERR, 2882400018,
+  },
+  { "GR",
+    "GR capability",
+    { /* hdr */                CAPABILITY_CODE_RESTART, 0xe,
+      /* R-bit, time */        0xf1, 0x12,
+      /* afi */                0x0, 0x1,
+      /* safi */       0x1,
+      /* flags */      0xf,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x1,
+      /* flags */      0x0,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x2,
+      /* flags */      0x1,
+    },
+    16, SHOULD_PARSE,
+  },
+  { "GR-short",
+    "GR capability, but header length too short",
+    { /* hdr */                0x40, 0xa,
+      /* R-bit, time */        0xf1, 0x12,
+      /* afi */                0x0, 0x1,
+      /* safi */       0x1,
+      /* flags */      0xf,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x1,
+      /* flags */      0x0,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x2,
+      /* flags */      0x1,
+    },
+    15 /* array is 16 though */, SHOULD_ERR,
+  },
+  { "GR-long",
+    "GR capability, but header length too long",
+    { /* hdr */                0x40, 0xf,
+      /* R-bit, time */        0xf1, 0x12,
+      /* afi */                0x0, 0x1,
+      /* safi */       0x1,
+      /* flags */      0xf,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x1,
+      /* flags */      0x0,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x2,
+      /* flags */      0x01,
+    },
+    16, SHOULD_ERR,
+  },
+  { "GR-trunc",
+    "GR capability, but truncated",
+    { /* hdr */                0x40, 0xf,
+      /* R-bit, time */        0xf1, 0x12,
+      /* afi */                0x0, 0x1,
+      /* safi */       0x1,
+      /* flags */      0xf,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x1,
+      /* flags */      0x0,
+      /* afi */                0x0, 0x2,
+      /* safi */       0x2,
+      /* flags */      0x1,
+    },
+    15, SHOULD_ERR,
+  },
+  { "GR-empty",
+    "GR capability, but empty.",
+    { /* hdr */                0x40, 0x0,
+    },
+    2, SHOULD_ERR,
+  },
+  { "MP-empty",
+    "MP capability, but empty.",
+    { /* hdr */                0x1, 0x0,
+    },
+    2, SHOULD_ERR,
+  },
+  { "ORF-empty",
+    "ORF capability, but empty.",
+    { /* hdr */                0x3, 0x0,
+    },
+    2, SHOULD_ERR,
+  },
+  { "AS4-empty",
+    "AS4 capability, but empty.",
+    { /* hdr */                0x41, 0x0,
+    },
+    2, SHOULD_ERR,
+  },
+  { "dyn-empty",
+    "Dynamic capability, but empty.",
+    { /* hdr */                0x42, 0x0,
+    },
+    2, SHOULD_PARSE,
+  },
+  { "dyn-old",
+    "Dynamic capability (deprecated version)",
+    { CAPABILITY_CODE_DYNAMIC, 0x0 },
+    2, SHOULD_PARSE,
+  },
+  { NULL, NULL, {0}, 0, 0}
+};
+
+/* DYNAMIC message */
+struct test_segment dynamic_cap_msgs[] = 
+{
+  { "DynCap",
+    "Dynamic Capability Message, IP/Multicast",
+    { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2 },
+      7, SHOULD_PARSE, /* horrible alignment, just as with ORF */
+  },
+  { "DynCapLong",
+    "Dynamic Capability Message, IP/Multicast, truncated",
+    { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2 },
+      5, SHOULD_ERR,
+  },
+  { "DynCapPadded",
+    "Dynamic Capability Message, IP/Multicast, padded",
+    { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2, 0x0 },
+      8, SHOULD_ERR, /* No way to tell padding from data.. */
+  },
+  { "DynCapMPCpadded",
+    "Dynamic Capability Message, IP/Multicast, cap data padded",
+    { 0x0, 0x1, 0x5, 0x0, 0x1, 0x0, 0x2, 0x0 },
+      8, SHOULD_PARSE, /* You can though add padding to the capability data */
+  },
+  { "DynCapMPCoverflow",
+    "Dynamic Capability Message, IP/Multicast, cap data != length",
+    { 0x0, 0x1, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0 },
+      8, SHOULD_ERR,
+  },
+  { NULL, NULL, {0}, 0, 0}
+};
+
+/* Entire Optional-Parameters block */
+struct test_segment opt_params[] =
+{
+  { "Cap-singlets",
+    "One capability per Optional-Param",
+    { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+      0x02, 0x02, 0x80, 0x00, /* RR (old) */
+      0x02, 0x02, 0x02, 0x00, /* RR */  
+    },
+    24, SHOULD_PARSE,
+  },
+  { "Cap-series",
+    "Series of capability, one Optional-Param",
+    { 0x02, 0x10,
+      0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+      0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+      0x80, 0x00, /* RR (old) */
+      0x02, 0x00, /* RR */  
+    },
+    18, SHOULD_PARSE,
+  },
+  { "AS4more",
+    "AS4 capability after other caps (singlets)",
+    { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+      0x02, 0x02, 0x80, 0x00, /* RR (old) */
+      0x02, 0x02, 0x02, 0x00, /* RR */
+      0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06  /* AS4: 1996614 */
+    },
+    32, SHOULD_PARSE, 196614,
+  },
+  { "AS4series",
+    "AS4 capability, in series of capabilities",
+    { 0x02, 0x16,
+      0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+      0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+      0x80, 0x00, /* RR (old) */
+      0x02, 0x00, /* RR */  
+      0x41, 0x04, 0x00, 0x03, 0x00, 0x06  /* AS4: 1996614 */
+    },
+    24, SHOULD_PARSE, 196614,
+  },
+  { "AS4real",
+    "AS4 capability, in series of capabilities",
+    {
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/uni */
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/uni */
+      0x02, 0x02, 0x80, 0x00, /* RR old */
+      0x02, 0x02, 0x02, 0x00, /* RR */
+      0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06, /* AS4 */
+    },
+    32, SHOULD_PARSE, 196614,
+  },
+  { "AS4real2",
+    "AS4 capability, in series of capabilities",
+    {
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01,
+      0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01,
+      0x02, 0x02, 0x80, 0x00,
+      0x02, 0x02, 0x02, 0x00,
+      0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0xfc, 0x03,
+      0x02, 0x09, 0x82, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x80, 0x03,
+      0x02, 0x09, 0x03, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x40, 0x03,
+      0x02, 0x02, 0x42, 0x00,
+    },
+    58, SHOULD_PARSE, 64515,
+  },
+
+  { NULL, NULL, {0}, 0, 0}
+};
+
+/* basic parsing test */
+static void
+parse_test (struct peer *peer, struct test_segment *t, int type)
+{
+  int ret;
+  int capability = 0;
+  as_t as4 = 0;
+  int oldfailed = failed;
+  int len = t->len;
+#define RANDOM_FUZZ 35
+  
+  stream_reset (peer->ibuf);
+  stream_put (peer->ibuf, NULL, RANDOM_FUZZ);
+  stream_set_getp (peer->ibuf, RANDOM_FUZZ);
+  
+  switch (type)
+    {
+      case CAPABILITY:
+        stream_putc (peer->ibuf, BGP_OPEN_OPT_CAP);
+        stream_putc (peer->ibuf, t->len);
+        break;
+      case DYNCAP:
+/*        for (i = 0; i < BGP_MARKER_SIZE; i++)
+          stream_putc (peer->, 0xff);
+        stream_putw (s, 0);
+        stream_putc (s, BGP_MSG_CAPABILITY);*/
+        break;
+    }
+  stream_write (peer->ibuf, t->data, t->len);
+  
+  printf ("%s: %s\n", t->name, t->desc);
+
+  switch (type)
+    {
+      case CAPABILITY:
+        len += 2; /* to cover the OPT-Param header */
+      case OPT_PARAM:
+        printf ("len: %u\n", len);
+        /* peek_for_as4 wants getp at capibility*/
+        as4 = peek_for_as4_capability (peer, len);
+        printf ("peek_for_as4: as4 is %u\n", as4);
+        /* and it should leave getp as it found it */
+        assert (stream_get_getp (peer->ibuf) == RANDOM_FUZZ);
+        
+        ret = bgp_open_option_parse (peer, len, &capability);
+        break;
+      case DYNCAP:
+        ret = bgp_capability_receive (peer, t->len);
+        break;
+      default:
+        printf ("unknown type %u\n", type);
+        exit(1);
+    }
+  
+  if (!ret && t->validate_afi)
+    {
+      safi_t safi = t->safi;
+      
+      if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid)
+        failed++;
+      
+      printf ("MP: %u/%u (%u): recv %u, nego %u\n",
+              t->afi, t->safi, safi,
+              peer->afc_recv[t->afi][safi],
+              peer->afc_nego[t->afi][safi]);
+        
+      if (t->afi_valid == VALID_AFI)
+        {
+        
+          if (!peer->afc_recv[t->afi][safi])
+            failed++;
+          if (!peer->afc_nego[t->afi][safi])
+            failed++;
+        }
+    }
+  
+  if (as4 != t->peek_for)
+    {
+      printf ("as4 %u != %u\n", as4, t->peek_for);
+      failed++;
+    }
+  
+  printf ("parsed?: %s\n", ret ? "no" : "yes");
+  
+  if (ret != t->parses)
+    failed++;
+  
+  if (tty)
+    printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET 
+                                         : VT100_GREEN "OK" VT100_RESET);
+  else
+    printf ("%s", (failed > oldfailed) ? "failed!" : "OK" );
+  
+  if (failed)
+    printf (" (%u)", failed);
+  
+  printf ("\n\n");
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int
+main (void)
+{
+  struct peer *peer;
+  int i, j;
+  
+  conf_bgp_debug_fsm = -1UL;
+  conf_bgp_debug_events = -1UL;
+  conf_bgp_debug_packet = -1UL;
+  conf_bgp_debug_normal = -1UL;
+  conf_bgp_debug_as4 = -1UL;
+  term_bgp_debug_fsm = -1UL;
+  term_bgp_debug_events = -1UL;
+  term_bgp_debug_packet = -1UL;
+  term_bgp_debug_normal = -1UL;
+  term_bgp_debug_as4 = -1UL;
+  
+  master = thread_master_create ();
+  bgp_master_init ();
+  bgp_option_set (BGP_OPT_NO_LISTEN);
+  
+  if (fileno (stdout) >= 0) 
+    tty = isatty (fileno (stdout));
+  
+  if (bgp_get (&bgp, &asn, NULL))
+    return -1;
+  
+  peer = peer_create_accept (bgp);
+  peer->host = (char *) "foo";
+  
+  for (i = AFI_IP; i < AFI_MAX; i++)
+    for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+      {
+        peer->afc[i][j] = 1;
+        peer->afc_adv[i][j] = 1;
+      }
+  
+  i = 0;
+  while (mp_segments[i].name)
+    parse_test (peer, &mp_segments[i++], CAPABILITY);
+
+  /* These tests assume mp_segments tests set at least
+   * one of the afc_nego's
+   */
+  i = 0;
+  while (test_segments[i].name)   
+    parse_test (peer, &test_segments[i++], CAPABILITY);
+  
+  i = 0;
+  while (misc_segments[i].name)
+    parse_test (peer, &misc_segments[i++], CAPABILITY);
+
+  i = 0;
+  while (opt_params[i].name)
+    parse_test (peer, &opt_params[i++], OPT_PARAM);
+
+  SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV);
+  peer->status = Established;
+  
+  i = 0;
+  while (dynamic_cap_msgs[i].name)
+    parse_test (peer, &dynamic_cap_msgs[i++], DYNCAP);
+  
+  printf ("failures: %d\n", failed);
+  return failed;
+}
diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c
new file mode 100644 (file)
index 0000000..3b1bf14
--- /dev/null
@@ -0,0 +1,790 @@
+/* 
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+
+#define CAPABILITY 0
+#define DYNCAP     1
+#define OPT_PARAM  2
+
+/* need these to link in libbgp */
+struct zebra_privs_t *bgpd_privs = NULL;
+struct thread_master *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+  const char *name;
+  const char *desc;
+  const u_char data[1024];
+  int len;
+#define SHOULD_PARSE   0
+#define SHOULD_ERR     -1
+  int parses; /* whether it should parse or not */
+  
+  /* AFI/SAFI validation */
+  afi_t afi;
+  safi_t safi;
+#define VALID_AFI 1
+#define INVALID_AFI 0
+  int afi_valid;
+} mp_reach_segments [] = 
+{
+  { "IPv6",
+    "IPV6 MP Reach, global nexthop, 1 NLRI", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      16,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+    },
+    (4 + 16 + 1 + 5), 
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-2",
+    "IPV6 MP Reach, global nexthop, 2 NLRIs", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      16,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,   /* ffee:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+    },
+    (4 + 16 + 1 + 5 + 9), 
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-default",
+    "IPV6 MP Reach, global nexthop, 2 NLRIs + default", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      16,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (4 + 16 + 1 + 5 + 9 + 1),
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-lnh",
+    "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      32,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* Nexthop (local) */     0xfe, 0x80, 0x0,  0x0,  /* fe80::210:2ff:.. */
+                                0x0,  0x0,  0x0,  0x0,
+                                0x2,  0x10, 0x2,  0xff,
+                                0x1,  0x2,  0x3,  0x4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (4 + 32 + 1 + 5 + 9 + 1),
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-nhlen",
+    "IPV6 MP Reach, inappropriate nexthop length", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      4,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* Nexthop (local) */     0xfe, 0x80, 0x0,  0x0,  /* fe80::210:2ff:.. */
+                                0x0,  0x0,  0x0,  0x0,
+                                0x2,  0x10, 0x2,  0xff,
+                                0x1,  0x2,  0x3,  0x4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (4 + 32 + 1 + 5 + 9 + 1),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-nhlen2",
+    "IPV6 MP Reach, invalid nexthop length", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      5,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* Nexthop (local) */     0xfe, 0x80, 0x0,  0x0,  /* fe80::210:2ff:.. */
+                                0x0,  0x0,  0x0,  0x0,
+                                0x2,  0x10, 0x2,  0xff,
+                                0x1,  0x2,  0x3,  0x4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (4 + 32 + 1 + 5 + 9 + 1),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-nhlen3",
+    "IPV6 MP Reach, nexthop length overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      32,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+    },
+    (4 + 16),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-nhlen4",
+    "IPV6 MP Reach, nexthop length short", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      16,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* Nexthop (local) */     0xfe, 0x80, 0x0,  0x0,  /* fe80::210:2ff:.. */
+                                0x0,  0x0,  0x0,  0x0,
+                                0x2,  0x10, 0x2,  0xff,
+                                0x1,  0x2,  0x3,  0x4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (4 + 32 + 1 + 5 + 9 + 1),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-nlri",
+    "IPV6 MP Reach, NLRI bitlen overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* nexthop bytes */      32,
+      /* Nexthop (global) */   0xff, 0xfe, 0x1,  0x2,  /* fffe:102:... */
+                                0xaa, 0xbb, 0xcc, 0xdd,
+                                0x3,  0x4,  0x5,  0x6,
+                                0xa1, 0xa2, 0xa3, 0xa4,
+      /* Nexthop (local) */     0xfe, 0x80, 0x0,  0x0,  /* fe80::210:2ff:.. */
+                                0x0,  0x0,  0x0,  0x0,
+                                0x2,  0x10, 0x2,  0xff,
+                                0x1,  0x2,  0x3,  0x4,
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                120, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0, /* ::/0 */
+    },
+    (4 + 32 + 1 + 5 + 9 + 1),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4",
+    "IPv4 MP Reach, 2 NLRIs + default", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_UNICAST,
+      /* nexthop bytes */      4,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                17, 10, 2, 3, /* 10.2.3/17 */
+                                0, /* 0/0 */
+    },
+    (4 + 4 + 1 + 3 + 4 + 1),
+    SHOULD_PARSE,
+    AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-nhlen",
+    "IPv4 MP Reach, nexthop lenth overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_UNICAST,
+      /* nexthop bytes */      32,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                17, 10, 2, 3, /* 10.2.3/17 */
+                                0, /* 0/0 */
+    },
+    (4 + 4 + 1 + 3 + 4 + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-nlrilen",
+    "IPv4 MP Reach, nlri lenth overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_UNICAST,
+      /* nexthop bytes */      4,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                30, 10, 
+                                0, /* 0/0 */
+    },
+    (4 + 4 + 1 + 3 + 2 + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-VPNv4",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_PARSE,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-bogus-plen",
+    "IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 1, 2,
+                                0, 0xff, 3, 4,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                17, 10, 2, 3,  /* 10.2.3/17 */
+                                0, /* 0/0 */
+    },
+    (3 + 1 + 3*4 + 1 + 3 + 4 + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-plen1-short",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 1,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-plen1-long",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 32,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-plenn-long",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+                                88 + 1, /* bogus */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3) + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-plenn-short",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 2,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-bogus-rd-type",
+    "IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0xff, 0, /* Bogus RD */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_PARSE,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { "IPv4-VPNv4-0-nlri",
+    "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* nexthop bytes */      12,
+      /* RD */                 0, 0, 0, 0, /* RD defined to be 0 */
+                                0, 0, 0, 0,
+      /* Nexthop */            192, 168,   0,  1, 
+      /* SNPA (defunct, MBZ) */        0x0,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+                                0 /* 0/0, bogus for vpnv4 ?? */
+    },
+    (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3) + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+
+  /* From bug #385 */
+  { "IPv6-bug",
+    "IPv6, global nexthop, 1 default NLRI", 
+    {
+      /* AFI / SAFI */         0x0, 0x2, 0x1,
+      /* nexthop bytes */      0x20,
+      /* Nexthop (global) */   0x20, 0x01, 0x04, 0x70, 
+                                0x00, 0x01, 0x00, 0x06,
+                                0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x01, 
+      /* Nexthop (local) */     0xfe, 0x80, 0x00, 0x00, 
+                                0x00, 0x00, 0x00, 0x00,
+                                0x02, 0x0c, 0xdb, 0xff, 
+                                0xfe, 0xfe, 0xeb, 0x00,
+      /* SNPA (defunct, MBZ) */        0,
+      /* NLRI tuples */                /* Should have 0 here for ::/0, but dont */
+    },
+    37,
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  
+  { NULL, NULL, {0}, 0, 0}
+};
+
+/* MP_UNREACH_NLRI tests */
+static struct test_segment mp_unreach_segments [] =
+{
+  { "IPv6-unreach",
+    "IPV6 MP Unreach, 1 NLRI", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* NLRI tuples */                32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+    },
+    (3 + 5), 
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-unreach2",
+    "IPV6 MP Unreach, 2 NLRIs", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+    },
+    (3 + 5 + 9), 
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-unreach-default",
+    "IPV6 MP Unreach, 2 NLRIs + default", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* NLRI tuples */                32, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0x0, /* ::/0 */
+    },
+    (3 + 5 + 9 + 1),
+    SHOULD_PARSE,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv6-unreach-nlri",
+    "IPV6 MP Unreach, NLRI bitlen overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP6, SAFI_UNICAST,
+      /* NLRI tuples */                120, 
+                                0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */
+                                64,
+                                0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+                                0x0,  0x2,  0x0, 0x3,
+                                0, /* ::/0 */
+    },
+    (3 + 5 + 9 + 1),
+    SHOULD_ERR,
+    AFI_IP6, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-unreach",
+    "IPv4 MP Unreach, 2 NLRIs + default", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_UNICAST,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                17, 10, 2, 3, /* 10.2.3/17 */
+                                0, /* 0/0 */
+    },
+    (3 + 3 + 4 + 1),
+    SHOULD_PARSE,
+    AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-unreach-nlrilen",
+    "IPv4 MP Unreach, nlri length overflow", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_UNICAST,
+      /* NLRI tuples */                16, 10, 1,    /* 10.1/16 */
+                                30, 10, 
+                                0, /* 0/0 */
+    },
+    (3 + 3 + 2 + 1),
+    SHOULD_ERR,
+    AFI_IP, SAFI_UNICAST, VALID_AFI,
+  },
+  { "IPv4-unreach-VPNv4",
+    "IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs", 
+    {
+      /* AFI / SAFI */         0x0, AFI_IP, SAFI_MPLS_LABELED_VPN,
+      /* NLRI tuples */                88 + 16,
+                                  0, 1, 2,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_AS */
+                                    0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+                                  10, 1,    /* 10.1/16 */
+                                88 + 17,
+                                  0xff, 0, 0,   /* tag */
+                                  /* rd, 8 octets */
+                                    0, 0, /* RD_TYPE_IP */
+                                    192, 168, 0, 1, /* IPv4 */
+                                  10, 2, 3,  /* 10.2.3/17 */
+    },
+    (3 + (1+3+8+2) + (1+3+8+3)),
+    SHOULD_PARSE,
+    AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI,
+  },
+  { NULL, NULL, {0}, 0, 0}
+};
+
+/* nlri_parse indicates 0 on successful parse, and -1 otherwise.
+ * attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success,
+ * and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err.
+ */
+static void
+handle_result (struct peer *peer, struct test_segment *t,
+               int parse_ret, int nlri_ret)
+{
+  int oldfailed = failed;
+  
+  if (!parse_ret)
+    {
+      safi_t safi = t->safi;
+      
+      if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid)
+        failed++;
+      
+      printf ("MP: %u/%u (%u): recv %u, nego %u\n",
+              t->afi, t->safi, safi,
+              peer->afc_recv[t->afi][safi],
+              peer->afc_nego[t->afi][safi]);
+    }
+  
+  printf ("mp attr parsed?: %s\n", parse_ret ? "no" : "yes");
+  if (!parse_ret)
+    printf ("nrli parsed?:  %s\n", nlri_ret ? "no" : "yes");
+  printf ("should parse?:  %s\n", t->parses ? "no" : "yes");
+  
+  if ((parse_ret != 0 || nlri_ret != 0) != (t->parses != 0))
+    failed++;
+  
+    
+  if (tty)
+    printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET 
+                                         : VT100_GREEN "OK" VT100_RESET);
+  else
+    printf ("%s", (failed > oldfailed) ? "failed!" : "OK" );
+  
+  if (failed)
+    printf (" (%u)", failed);
+  
+  printf ("\n\n");
+}
+
+/* basic parsing test */
+static void
+parse_test (struct peer *peer, struct test_segment *t, int type)
+{
+  int parse_ret = 0, nlri_ret = 0;
+  struct attr attr = { };
+  struct bgp_nlri nlri = { };
+  struct bgp_attr_parser_args attr_args = {
+    .peer = peer,
+    .length = t->len,
+    .total = 1,
+    .attr = &attr,
+    .type = type,
+    .flags = BGP_ATTR_FLAG_OPTIONAL, 
+    .startp = BGP_INPUT_PNT (peer),
+  };
+#define RANDOM_FUZZ 35
+  
+  stream_reset (peer->ibuf);
+  stream_put (peer->ibuf, NULL, RANDOM_FUZZ);
+  stream_set_getp (peer->ibuf, RANDOM_FUZZ);
+  
+  stream_write (peer->ibuf, t->data, t->len);
+  
+  printf ("%s: %s\n", t->name, t->desc);
+  
+  if (type == BGP_ATTR_MP_REACH_NLRI)
+    parse_ret = bgp_mp_reach_parse (&attr_args, &nlri);
+  else
+    parse_ret = bgp_mp_unreach_parse (&attr_args, &nlri);
+  
+  if (parse_ret == 0 && t->afi_valid == VALID_AFI)
+    assert (nlri.afi == t->afi && nlri.safi == t->safi);
+  
+  if (!parse_ret)
+    {
+      if (type == BGP_ATTR_MP_REACH_NLRI)
+        nlri_ret = bgp_nlri_parse (peer, &attr, &nlri);
+      else
+        nlri_ret = bgp_nlri_parse (peer, NULL, &nlri);
+    }
+  
+  handle_result (peer, t, parse_ret, nlri_ret);
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int
+main (void)
+{
+  struct peer *peer;
+  int i, j;
+  
+  conf_bgp_debug_fsm = -1UL;
+  conf_bgp_debug_events = -1UL;
+  conf_bgp_debug_packet = -1UL;
+  conf_bgp_debug_normal = -1UL;
+  conf_bgp_debug_as4 = -1UL;
+  term_bgp_debug_fsm = -1UL;
+  term_bgp_debug_events = -1UL;
+  term_bgp_debug_packet = -1UL;
+  term_bgp_debug_normal = -1UL;
+  term_bgp_debug_as4 = -1UL;
+  
+  master = thread_master_create ();
+  bgp_master_init ();
+  bgp_option_set (BGP_OPT_NO_LISTEN);
+  bgp_attr_init ();
+  bgp_address_init ();
+  bgp_scan_init ();
+  
+  if (fileno (stdout) >= 0) 
+    tty = isatty (fileno (stdout));
+  
+  if (bgp_get (&bgp, &asn, NULL))
+    return -1;
+  
+  peer = peer_create_accept (bgp);
+  peer->host = (char *)"foo";
+  peer->status = Established;
+  
+  for (i = AFI_IP; i < AFI_MAX; i++)
+    for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+      {
+        peer->afc[i][j] = 1;
+        peer->afc_adv[i][j] = 1;
+      }
+  
+  i = 0;
+  while (mp_reach_segments[i].name)
+    parse_test (peer, &mp_reach_segments[i++], BGP_ATTR_MP_REACH_NLRI);
+
+  i = 0;
+  while (mp_unreach_segments[i].name)
+    parse_test (peer, &mp_unreach_segments[i++], BGP_ATTR_MP_UNREACH_NLRI);
+
+  printf ("failures: %d\n", failed);
+  return failed;
+}
diff --git a/tests/bgp_mpath_test.c b/tests/bgp_mpath_test.c
new file mode 100644 (file)
index 0000000..174d299
--- /dev/null
@@ -0,0 +1,488 @@
+/* $QuaggaId: Format:%an, %ai, %h$ $
+ *
+ * BGP Multipath Unit Test
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "linklist.h"
+#include "memory.h"
+#include "zclient.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mpath.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+#define TEST_PASSED 0
+#define TEST_FAILED -1
+
+#define EXPECT_TRUE(expr, res)                                          \
+  if (!(expr))                                                          \
+    {                                                                   \
+      printf ("Test failure in %s line %u: %s\n",                       \
+              __FUNCTION__, __LINE__, #expr);                           \
+      (res) = TEST_FAILED;                                              \
+    }
+
+typedef struct testcase_t__ testcase_t;
+
+typedef int (*test_setup_func)(testcase_t *);
+typedef int (*test_run_func)(testcase_t *);
+typedef int (*test_cleanup_func)(testcase_t *);
+
+struct testcase_t__ {
+  const char *desc;
+  void *test_data;
+  void *verify_data;
+  void *tmp_data;
+  test_setup_func setup;
+  test_run_func run;
+  test_cleanup_func cleanup;
+};
+
+/* need these to link in libbgp */
+struct thread_master *master = NULL;
+struct zclient *zclient;
+struct zebra_privs_t bgpd_privs =
+{
+  .user = NULL,
+  .group = NULL,
+  .vty_group = NULL,
+};
+
+static int tty = 0;
+
+/* Create fake bgp instance */
+static struct bgp *
+bgp_create_fake (as_t *as, const char *name)
+{
+  struct bgp *bgp;
+  afi_t afi;
+  safi_t safi;
+
+  if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL)
+    return NULL;
+
+  bgp_lock (bgp);
+  //bgp->peer_self = peer_new (bgp);
+  //bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement");
+
+  bgp->peer = list_new ();
+  //bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp;
+
+  bgp->group = list_new ();
+  //bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;
+
+  bgp->rsclient = list_new ();
+  //bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+        bgp->route[afi][safi] = bgp_table_init (afi, safi);
+        bgp->aggregate[afi][safi] = bgp_table_init (afi, safi);
+        bgp->rib[afi][safi] = bgp_table_init (afi, safi);
+        bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
+        bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
+      }
+
+  bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+  bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+  bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+  bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+  bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+
+  bgp->as = *as;
+
+  if (name)
+    bgp->name = strdup (name);
+
+  return bgp;
+}
+
+/*=========================================================
+ * Testcase for maximum-paths configuration
+ */
+static int
+setup_bgp_cfg_maximum_paths (testcase_t *t)
+{
+  as_t asn = 1;
+  t->tmp_data = bgp_create_fake (&asn, NULL);
+  if (!t->tmp_data)
+    return -1;
+  return 0;
+}
+
+static int
+run_bgp_cfg_maximum_paths (testcase_t *t)
+{
+  afi_t afi;
+  safi_t safi;
+  struct bgp *bgp;
+  int api_result;
+  int test_result = TEST_PASSED;
+
+  bgp = t->tmp_data;
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+        /* test bgp_maximum_paths_set */
+        api_result = bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_EBGP, 10);
+        EXPECT_TRUE (api_result == 0, test_result);
+        api_result = bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_IBGP, 10);
+        EXPECT_TRUE (api_result == 0, test_result);
+        EXPECT_TRUE (bgp->maxpaths[afi][safi].maxpaths_ebgp == 10, test_result);
+        EXPECT_TRUE (bgp->maxpaths[afi][safi].maxpaths_ibgp == 10, test_result);
+
+        /* test bgp_maximum_paths_unset */
+        api_result = bgp_maximum_paths_unset (bgp, afi, safi, BGP_PEER_EBGP);
+        EXPECT_TRUE (api_result == 0, test_result);
+        api_result = bgp_maximum_paths_unset (bgp, afi, safi, BGP_PEER_IBGP);
+        EXPECT_TRUE (api_result == 0, test_result);
+        EXPECT_TRUE ((bgp->maxpaths[afi][safi].maxpaths_ebgp ==
+                      BGP_DEFAULT_MAXPATHS), test_result);
+        EXPECT_TRUE ((bgp->maxpaths[afi][safi].maxpaths_ibgp ==
+                      BGP_DEFAULT_MAXPATHS), test_result);
+      }
+
+  return test_result;
+}
+
+static int
+cleanup_bgp_cfg_maximum_paths (testcase_t *t)
+{
+  return bgp_delete ((struct bgp *)t->tmp_data);
+}
+
+testcase_t test_bgp_cfg_maximum_paths = {
+  .desc = "Test bgp maximum-paths config",
+  .setup = setup_bgp_cfg_maximum_paths,
+  .run = run_bgp_cfg_maximum_paths,
+  .cleanup = cleanup_bgp_cfg_maximum_paths,
+};
+
+/*=========================================================
+ * Testcase for bgp_mp_list
+ */
+
+struct bgp test_mp_bgp;
+
+struct peer test_mp_list_peer[] = {
+  { .local_as = 1, .as = 2, .bgp = &test_mp_bgp },
+  { .local_as = 1, .as = 2, .bgp = &test_mp_bgp },
+  { .local_as = 1, .as = 2, .bgp = &test_mp_bgp },
+  { .local_as = 1, .as = 2, .bgp = &test_mp_bgp },
+  { .local_as = 1, .as = 2, .bgp = &test_mp_bgp },
+};
+int test_mp_list_peer_count = sizeof (test_mp_list_peer)/ sizeof (struct peer);
+struct attr test_mp_list_attr[4];
+struct bgp_info test_mp_list_info[] = {
+  { .peer = &test_mp_list_peer[0], .attr = &test_mp_list_attr[0] },
+  { .peer = &test_mp_list_peer[1], .attr = &test_mp_list_attr[1] },
+  { .peer = &test_mp_list_peer[2], .attr = &test_mp_list_attr[1] },
+  { .peer = &test_mp_list_peer[3], .attr = &test_mp_list_attr[2] },
+  { .peer = &test_mp_list_peer[4], .attr = &test_mp_list_attr[3] },
+};
+int test_mp_list_info_count =
+  sizeof (test_mp_list_info)/sizeof (struct bgp_info);
+
+static int
+setup_bgp_mp_list (testcase_t *t)
+{
+  test_mp_list_attr[0].nexthop.s_addr = 0x01010101;
+  test_mp_list_attr[1].nexthop.s_addr = 0x02020202;
+  test_mp_list_attr[2].nexthop.s_addr = 0x03030303;
+  test_mp_list_attr[3].nexthop.s_addr = 0x04040404;
+
+  if ((test_mp_list_peer[0].su_remote = sockunion_str2su ("1.1.1.1")) == NULL)
+    return -1;
+  if ((test_mp_list_peer[1].su_remote = sockunion_str2su ("2.2.2.2")) == NULL)
+    return -1;
+  if ((test_mp_list_peer[2].su_remote = sockunion_str2su ("3.3.3.3")) == NULL)
+    return -1;
+  if ((test_mp_list_peer[3].su_remote = sockunion_str2su ("4.4.4.4")) == NULL)
+    return -1;
+  if ((test_mp_list_peer[4].su_remote = sockunion_str2su ("5.5.5.5")) == NULL)
+    return -1;
+
+  return 0;
+}
+
+static int
+run_bgp_mp_list (testcase_t *t)
+{
+  struct list mp_list;
+  struct listnode *mp_node;
+  struct bgp_info *info;
+  int i;
+  int test_result = TEST_PASSED;
+  bgp_mp_list_init (&mp_list);
+  EXPECT_TRUE (listcount(&mp_list) == 0, test_result);
+
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[1]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[4]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[2]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[3]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[0]);
+
+  for (i = 0, mp_node = mp_list.head; i < test_mp_list_info_count;
+       i++, mp_node = listnextnode(mp_node))
+    {
+      info = listgetdata(mp_node);
+      EXPECT_TRUE (info == &test_mp_list_info[i], test_result);
+    }
+
+  bgp_mp_list_clear (&mp_list);
+  EXPECT_TRUE (listcount(&mp_list) == 0, test_result);
+
+  return test_result;
+}
+
+static int
+cleanup_bgp_mp_list (testcase_t *t)
+{
+  int i;
+
+  for (i = 0; i < test_mp_list_peer_count; i++)
+    sockunion_free (test_mp_list_peer[i].su_remote);
+
+  return 0;
+}
+
+testcase_t test_bgp_mp_list = {
+  .desc = "Test bgp_mp_list",
+  .setup = setup_bgp_mp_list,
+  .run = run_bgp_mp_list,
+  .cleanup = cleanup_bgp_mp_list,
+};
+
+/*=========================================================
+ * Testcase for bgp_info_mpath_update
+ */
+
+struct bgp_node test_rn;
+
+static int
+setup_bgp_info_mpath_update (testcase_t *t)
+{
+  int i;
+  str2prefix ("42.1.1.0/24", &test_rn.p);
+  setup_bgp_mp_list (t);
+  for (i = 0; i < test_mp_list_info_count; i++)
+    bgp_info_add (&test_rn, &test_mp_list_info[i]);
+  return 0;
+}
+
+static int
+run_bgp_info_mpath_update (testcase_t *t)
+{
+  struct bgp_info *new_best, *old_best, *mpath;
+  struct list mp_list;
+
+  test_mp_bgp.maxpaths[AFI_IP][SAFI_UNICAST].maxpaths_ebgp = 3;
+  test_mp_bgp.maxpaths[AFI_IP][SAFI_UNICAST].maxpaths_ibgp = 3;
+
+  int test_result = TEST_PASSED;
+  bgp_mp_list_init (&mp_list);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[4]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[3]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[0]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[1]);
+  new_best = &test_mp_list_info[3];
+  old_best = NULL;
+  bgp_info_mpath_update (&test_rn, new_best, old_best, &mp_list, AFI_IP, SAFI_UNICAST);
+  bgp_mp_list_clear (&mp_list);
+  EXPECT_TRUE (bgp_info_mpath_count (new_best) == 2, test_result);
+  mpath = bgp_info_mpath_first (new_best);
+  EXPECT_TRUE (mpath == &test_mp_list_info[0], test_result);
+  EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result);
+  mpath = bgp_info_mpath_next (mpath);
+  EXPECT_TRUE (mpath == &test_mp_list_info[1], test_result);
+  EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result);
+
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[0]);
+  bgp_mp_list_add (&mp_list, &test_mp_list_info[1]);
+  new_best = &test_mp_list_info[0];
+  old_best = &test_mp_list_info[3];
+  bgp_info_mpath_update (&test_rn, new_best, old_best, &mp_list, AFI_IP, SAFI_UNICAST);
+  bgp_mp_list_clear (&mp_list);
+  EXPECT_TRUE (bgp_info_mpath_count (new_best) == 1, test_result);
+  mpath = bgp_info_mpath_first (new_best);
+  EXPECT_TRUE (mpath == &test_mp_list_info[1], test_result);
+  EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result);
+  EXPECT_TRUE (!CHECK_FLAG (test_mp_list_info[0].flags, BGP_INFO_MULTIPATH),
+               test_result);
+
+  return test_result;
+}
+
+static int
+cleanup_bgp_info_mpath_update (testcase_t *t)
+{
+  int i;
+
+  for (i = 0; i < test_mp_list_peer_count; i++)
+    sockunion_free (test_mp_list_peer[i].su_remote);
+
+  return 0;
+}
+
+testcase_t test_bgp_info_mpath_update = {
+  .desc = "Test bgp_info_mpath_update",
+  .setup = setup_bgp_info_mpath_update,
+  .run = run_bgp_info_mpath_update,
+  .cleanup = cleanup_bgp_info_mpath_update,
+};
+
+/*=========================================================
+ * Set up testcase vector
+ */
+testcase_t *all_tests[] = {
+  &test_bgp_cfg_maximum_paths,
+  &test_bgp_mp_list,
+  &test_bgp_info_mpath_update,
+};
+
+int all_tests_count = (sizeof(all_tests)/sizeof(testcase_t *));
+
+/*=========================================================
+ * Test Driver Functions
+ */
+static int
+global_test_init (void)
+{
+  master = thread_master_create ();
+  zclient = zclient_new (master);
+  bgp_master_init ();
+  bgp_option_set (BGP_OPT_NO_LISTEN);
+  
+  if (fileno (stdout) >= 0)
+    tty = isatty (fileno (stdout));
+  return 0;
+}
+
+static int
+global_test_cleanup (void)
+{
+  if (zclient != NULL)
+    zclient_free (zclient);
+  thread_master_free (master);
+  return 0;
+}
+
+static void
+display_result (testcase_t *test, int result)
+{
+  if (tty)
+    printf ("%s: %s\n", test->desc, result == TEST_PASSED ? OK : FAILED);
+  else
+    printf ("%s: %s\n", test->desc, result == TEST_PASSED ? "OK" : "FAILED");
+}
+
+static int
+setup_test (testcase_t *t)
+{
+  int res = 0;
+  if (t->setup)
+    res = t->setup (t);
+  return res;
+}
+
+static int
+cleanup_test (testcase_t *t)
+{
+  int res = 0;
+  if (t->cleanup)
+    res = t->cleanup (t);
+  return res;
+}
+
+static void
+run_tests (testcase_t *tests[], int num_tests, int *pass_count, int *fail_count)
+{
+  int test_index, result;
+  testcase_t *cur_test;
+
+  *pass_count = *fail_count = 0;
+
+  for (test_index = 0; test_index < num_tests; test_index++)
+    {
+      cur_test = tests[test_index];
+      if (!cur_test->desc)
+        {
+          printf ("error: test %d has no description!\n", test_index);
+          continue;
+        }
+      if (!cur_test->run)
+        {
+          printf ("error: test %s has no run function!\n", cur_test->desc);
+          continue;
+        }
+      if (setup_test (cur_test) != 0)
+        {
+          printf ("error: setup failed for test %s\n", cur_test->desc);
+          continue;
+        }
+      result = cur_test->run (cur_test);
+      if (result == TEST_PASSED)
+        *pass_count += 1;
+      else
+        *fail_count += 1;
+      display_result (cur_test, result);
+      if (cleanup_test (cur_test) != 0)
+        {
+          printf ("error: cleanup failed for test %s\n", cur_test->desc);
+          continue;
+        }
+    }
+}
+
+int
+main (void)
+{
+  int pass_count, fail_count;
+  time_t cur_time;
+
+  time (&cur_time);
+  printf("BGP Multipath Tests Run at %s", ctime(&cur_time));
+  if (global_test_init () != 0)
+    {
+      printf("Global init failed. Terminating.\n");
+      exit(1);
+    }
+  run_tests (all_tests, all_tests_count, &pass_count, &fail_count);
+  global_test_cleanup ();
+  printf("Total pass/fail: %d/%d\n", pass_count, fail_count);
+  return fail_count;
+}
diff --git a/tests/bgpd.tests/Makefile.am b/tests/bgpd.tests/Makefile.am
new file mode 100644 (file)
index 0000000..5900186
--- /dev/null
@@ -0,0 +1,7 @@
+EXTRA_DIST = \
+       aspathtest.exp \
+       ecommtest.exp \
+       testbgpcap.exp \
+       testbgpmpath.exp \
+       testbgpmpattr.exp
+
diff --git a/tests/bgpd.tests/aspathtest.exp b/tests/bgpd.tests/aspathtest.exp
new file mode 100644 (file)
index 0000000..dfecec7
--- /dev/null
@@ -0,0 +1,76 @@
+set timeout 10
+set testprefix "aspathtest "
+set aborted 0
+set color 1
+
+spawn "./aspathtest"
+
+# proc onetest { test_name note start } {
+# proc headerline { line } {
+set parserno 0
+proc parsertest { test_name } {
+       global parserno
+       headerline "test $parserno"
+       onetest "parse $test_name" " ($parserno)" "$test_name:"
+       onetest "parse $test_name +empty_prepend" " (#$parserno)" "empty prepend $test_name:"
+       incr parserno 1
+}
+set attrno 0
+proc attrtest { test_name } {
+       global attrno
+       headerline "aspath_attr test $attrno"
+       onetest "attr $test_name" " (#$attrno)" "$test_name"
+       incr attrno 1
+}
+
+
+parsertest "seq1"
+parsertest "seq2"
+parsertest "seq3"
+parsertest "seqset"
+parsertest "seqset2"
+parsertest "multi"
+parsertest "confed"
+parsertest "confed2"
+parsertest "confset"
+parsertest "confmulti"
+parsertest "seq4"
+parsertest "tripleseq1"
+parsertest "someprivate"
+parsertest "allprivate"
+parsertest "long"
+parsertest "seq1extra"
+parsertest "empty"
+parsertest "redundantset"
+parsertest "reconcile_lead_asp"
+parsertest "reconcile_new_asp"
+parsertest "reconcile_confed"
+parsertest "reconcile_start_trans"
+parsertest "reconcile_start_trans4"
+parsertest "reconcile_start_trans_error"
+parsertest "redundantset2"
+parsertest "zero-size overflow"
+parsertest "zero-size overflow + valid segment"
+parsertest "invalid segment type"
+
+for {set i 0} {$i < 10} {incr i 1} { onetest "prepend $i" "" "prepend test $i"; }
+for {set i 0} {$i <  5} {incr i 1} { onetest "aggregate $i" "" "aggregate test $i"; }
+for {set i 0} {$i <  5} {incr i 1} { onetest "reconcile $i" "" "reconcile test $i"; }
+for {set i 0} {$i < 22} {incr i 1} { onetest "compare $i" "" "left cmp "; }
+
+onetest "empty_get" "" "empty_get_test"
+attrtest "basic test"
+attrtest "length too short"
+attrtest "length too long"
+attrtest "incorrect flag"
+attrtest "as4_path, with as2 format data"
+attrtest "as4, with incorrect attr length"
+attrtest "basic 4-byte as-path"
+attrtest "4b AS_PATH: too short"
+attrtest "4b AS_PATH: too long"
+attrtest "4b AS_PATH: too long2"
+attrtest "4b AS_PATH: bad flags"
+attrtest "4b AS4_PATH w/o AS_PATH"
+attrtest "4b AS4_PATH: confed"
+
diff --git a/tests/bgpd.tests/ecommtest.exp b/tests/bgpd.tests/ecommtest.exp
new file mode 100644 (file)
index 0000000..074952f
--- /dev/null
@@ -0,0 +1,13 @@
+set timeout 10
+set testprefix "ecommtest "
+set aborted 0
+set color 0
+
+spawn "./ecommtest"
+
+# proc simpletest { start } {
+
+simpletest "ipaddr"
+simpletest "ipaddr-so"
+simpletest "asn"
+simpletest "asn4"
diff --git a/tests/bgpd.tests/testbgpcap.exp b/tests/bgpd.tests/testbgpcap.exp
new file mode 100644 (file)
index 0000000..2572623
--- /dev/null
@@ -0,0 +1,51 @@
+set timeout 10
+set testprefix "testbgpcap "
+set aborted 0
+set color 1
+
+spawn "./testbgpcap"
+
+# proc simpletest { start } {
+
+simpletest "MP4: MP IP/Uni"
+simpletest "MPv6: MP IPv6/Uni"
+simpletest "MP2: MP IP/Multicast"
+simpletest "MP3: MP IP6/MPLS-labeled VPN"
+simpletest "MP5: MP IP6/MPLS-VPN"
+simpletest "MP6: MP IP4/MPLS-laveled VPN"
+simpletest "MP8: MP unknown AFI/SAFI"
+simpletest "MP-short: MP IP4/Unicast, length too short (< minimum)"
+simpletest "MP-overflow: MP IP4/Unicast, length too long"
+simpletest "caphdr: capability header, and no more"
+simpletest "nodata: header, no data but length says there is"
+simpletest "padded: valid, with padding"
+simpletest "minsize: violates minsize requirement"
+simpletest "ORF: ORF, simple, single entry, single tuple"
+simpletest "ORF-many: ORF, multi entry/tuple"
+simpletest "ORFlo: ORF, multi entry/tuple, hdr length too short"
+simpletest "ORFlu: ORF, multi entry/tuple, length too long"
+simpletest "ORFnu: ORF, multi entry/tuple, entry number too long"
+simpletest "ORFno: ORF, multi entry/tuple, entry number too short"
+simpletest "ORFpad: ORF, multi entry/tuple, padded to align"
+simpletest "AS4: AS4 capability"
+simpletest "GR: GR capability"
+simpletest "GR-short: GR capability, but header length too short"
+simpletest "GR-long: GR capability, but header length too long"
+simpletest "GR-trunc: GR capability, but truncated"
+simpletest "GR-empty: GR capability, but empty."
+simpletest "MP-empty: MP capability, but empty."
+simpletest "ORF-empty: ORF capability, but empty."
+simpletest "AS4-empty: AS4 capability, but empty."
+simpletest "dyn-empty: Dynamic capability, but empty."
+simpletest "dyn-old: Dynamic capability (deprecated version)"
+simpletest "Cap-singlets: One capability per Optional-Param"
+simpletest "Cap-series: Series of capability, one Optional-Param"
+simpletest "AS4more: AS4 capability after other caps (singlets)"
+simpletest "AS4series: AS4 capability, in series of capabilities"
+simpletest "AS4real: AS4 capability, in series of capabilities"
+simpletest "AS4real2: AS4 capability, in series of capabilities"
+simpletest "DynCap: Dynamic Capability Message, IP/Multicast"
+simpletest "DynCapLong: Dynamic Capability Message, IP/Multicast, truncated"
+simpletest "DynCapPadded: Dynamic Capability Message, IP/Multicast, padded"
+simpletest "DynCapMPCpadded: Dynamic Capability Message, IP/Multicast, cap data padded"
+simpletest "DynCapMPCoverflow: Dynamic Capability Message, IP/Multicast, cap data != length"
diff --git a/tests/bgpd.tests/testbgpmpath.exp b/tests/bgpd.tests/testbgpmpath.exp
new file mode 100644 (file)
index 0000000..96a51e3
--- /dev/null
@@ -0,0 +1,12 @@
+set timeout 10
+set testprefix "testbgpmpath "
+set aborted 0
+set color 1
+
+spawn "./testbgpmpath"
+
+# proc simpletest { start } {
+
+simpletest "bgp maximum-paths config"
+simpletest "bgp_mp_list"
+simpletest "bgp_info_mpath_update"
diff --git a/tests/bgpd.tests/testbgpmpattr.exp b/tests/bgpd.tests/testbgpmpattr.exp
new file mode 100644 (file)
index 0000000..e6d7305
--- /dev/null
@@ -0,0 +1,38 @@
+set timeout 10
+set testprefix "testbgpmpattr "
+set aborted 0
+set color 1
+
+spawn "./testbgpmpattr"
+
+# proc simpletest { start } {
+
+simpletest "IPv6: IPV6 MP Reach, global nexthop, 1 NLRI"
+simpletest "IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs"
+simpletest "IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default"
+simpletest "IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default"
+simpletest "IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length"
+simpletest "IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length"
+simpletest "IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow"
+simpletest "IPv6-nhlen4: IPV6 MP Reach, nexthop length short"
+simpletest "IPv6-nlri: IPV6 MP Reach, NLRI bitlen overflow"
+simpletest "IPv4: IPv4 MP Reach, 2 NLRIs + default"
+simpletest "IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow"
+simpletest "IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow"
+simpletest "IPv4-VPNv4: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs"
+simpletest "IPv4-VPNv4-bogus-plen: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len"
+simpletest "IPv4-VPNv4-plen1-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short"
+simpletest "IPv4-VPNv4-plen1-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long"
+simpletest "IPv4-VPNv4-plenn-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long"
+simpletest "IPv4-VPNv4-plenn-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short"
+simpletest "IPv4-VPNv4-bogus-rd-type: IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)"
+simpletest "IPv4-VPNv4-0-nlri: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus"
+simpletest "IPv6-bug: IPv6, global nexthop, 1 default NLRI"
+simpletest "IPv6-unreach: IPV6 MP Unreach, 1 NLRI"
+simpletest "IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs"
+simpletest "IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default"
+simpletest "IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow"
+simpletest "IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default"
+simpletest "IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow"
+simpletest "IPv4-unreach-VPNv4: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs"
+
diff --git a/tests/common-cli.c b/tests/common-cli.c
new file mode 100644 (file)
index 0000000..7135856
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ *                   for Open Source Routing / NetDEF, Inc.
+ *
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+
+#include "common-cli.h"
+
+struct thread_master *master;
+
+int dump_args(struct vty *vty, const char *descr,
+              int argc, const char **argv)
+{
+  int i;
+  vty_out (vty, "%s with %d args.%s", descr, argc, VTY_NEWLINE);
+  for (i = 0; i < argc; i++)
+    {
+      vty_out (vty, "[%02d]: %s%s", i, argv[i], VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void vty_do_exit(void)
+{
+  printf ("\nend.\n");
+  exit (0);
+}
+
+/* main routine. */
+int
+main (int argc, char **argv)
+{
+  struct thread thread;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* master init. */
+  master = thread_master_create ();
+
+  zlog_default = openzlog ("common-cli", ZLOG_NONE,
+                           LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, LOG_DEBUG);
+
+  /* Library inits. */
+  cmd_init (1);
+  host.name = strdup ("test");
+
+  vty_init (master);
+  memory_init ();
+
+  test_init ();
+
+  vty_stdio (vty_do_exit);
+
+  /* Fetch next active thread. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  exit (0);
+}
+
diff --git a/tests/common-cli.h b/tests/common-cli.h
new file mode 100644 (file)
index 0000000..8f67515
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ *                   for Open Source Routing / NetDEF, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _COMMON_CLI_H
+#define _COMMON_CLI_H
+
+#include "zebra.h"
+#include "vty.h"
+#include "command.h"
+
+/* function to be implemented by test */
+extern void test_init (void);
+
+/* functions provided by common cli
+ * (includes main())
+ */
+extern struct thread_master *master;
+
+extern int dump_args(struct vty *vty, const char *descr,
+              int argc, const char **argv);
+
+#define DUMMY_HELPSTR \
+       "00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n" \
+       "10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n" \
+       "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n"
+#define DUMMY_DEFUN(name, cmdstr) \
+        DEFUN (name, name ## _cmd, cmdstr, DUMMY_HELPSTR) \
+        { return dump_args(vty, #name, argc, argv); }
+
+#endif /* _COMMON_CLI_H */
diff --git a/tests/config/unix.exp b/tests/config/unix.exp
new file mode 100644 (file)
index 0000000..2f6bcea
--- /dev/null
@@ -0,0 +1,102 @@
+
+# every test should always be run and always return some status.
+# so, if we lose sync with a multi-test program, aborted will be used
+# to flag the remainder of the tests as untested.
+#set aborted 0
+
+# only match with color codes since "failed" / "OK" might otherwise
+# be part of the output...
+#set color 1
+
+set xfail 0
+
+proc onesimple { test_name match } {
+       global verbose
+       global aborted
+       global testprefix
+       if { $aborted > 0 } {
+               untested "$testprefix$test_name"
+               return
+       }
+       if { $verbose > 0 } {
+               send_user "$testprefix$test_name$note\n"
+       }
+       expect {
+               "$match"        { pass "$testprefix$test_name"; }
+               eof             { fail "$testprefix$test_name"; set aborted 1; }
+               timeout         { unresolved "$testprefix$test_name"; set aborted 1; }
+       }
+}
+
+proc onetest { test_name note start } {
+       global aborted
+       global testprefix
+       global verbose
+       global color
+       global xfail
+
+       if { $aborted > 0 } {
+               untested "$testprefix$test_name"
+               return
+       }
+
+       if { $verbose > 0 } {
+               send_user "$testprefix$test_name$note\n"
+       }
+       expect {
+               "$start"        { }
+
+               eof     { unresolved "$testprefix$test_name"; set aborted 1; }
+               timeout { unresolved "$testprefix$test_name"; set aborted 1; }
+       }
+
+       if { $aborted > 0 } {
+               send_user "sync failed: $testprefix$test_name$note -- $testprefix aborted!\n"
+               return
+       }
+
+       if { $color } {
+               set pat "(32mOK|31mfailed)"
+       } else {
+               set pat "(OK|failed)"
+       }
+       expect {
+               # need this because otherwise expect will skip over a "failed" and
+               # grab the next "OK" (or the other way around)
+               -re "$pat"  {
+                       if { "$expect_out(0,string)" == "32mOK" || "$expect_out(0,string)" == "OK" } {
+                               pass "$testprefix$test_name"
+                       } else {
+                               if { $xfail } {
+                                       xfail "$testprefix$test_name"
+                               } else {
+                                       fail "$testprefix$test_name"
+                               }
+                       }
+                       return
+               }
+
+               eof     { unresolved "$testprefix$test_name"; set aborted 1; }
+               timeout { unresolved "$testprefix$test_name"; set aborted 1; }
+       }
+
+       if { $aborted > 0 } {
+               send_user "failed: $testprefix$test_name$note -- $testprefix aborted!\n"
+               return
+       }
+}
+
+proc headerline { line } {
+       global aborted
+       if { $aborted > 0 } { return; }
+       expect {
+               $line   { return; }
+               eof     { send_user "numbering mismatch!\n"; set aborted 1; }
+               timeout { send_user "numbering mismatch!\n"; set aborted 1; }
+       }
+}
+
+proc simpletest { start } {
+       onetest "$start" "" "$start"
+}
+
diff --git a/tests/ecommunity_test.c b/tests/ecommunity_test.c
new file mode 100644 (file)
index 0000000..23ee405
--- /dev/null
@@ -0,0 +1,162 @@
+/* 
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* need these to link in libbgp */
+struct zebra_privs_t *bgpd_privs = NULL;
+struct thread_master *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec 
+{
+  const char *shouldbe; /* the string the path should parse to */
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+  const char *name;
+  const char *desc;
+  const u_int8_t data[1024];
+  int len;
+  struct test_spec sp;
+} test_segments [] = 
+{
+  { /* 0 */
+    "ipaddr",
+    "rt 1.2.3.4:257",
+    { ECOMMUNITY_ENCODE_IP, ECOMMUNITY_ROUTE_TARGET,
+      0x1,0x2,0x3,0x4, 0x1,0x1 },
+    8,
+    { "rt 1.2.3.4:257" }
+  },
+  { /* 1 */
+    "ipaddr-so",
+    "soo 1.2.3.4:257",
+    { ECOMMUNITY_ENCODE_IP, ECOMMUNITY_SITE_ORIGIN,
+      0x1,0x2,0x3,0x4, 0x1,0x1},
+    8,
+    { "soo 1.2.3.4:257" }
+  },
+  { /* 2 */
+    "asn",
+    "rt 23456:987654321",
+    { ECOMMUNITY_ENCODE_AS, ECOMMUNITY_SITE_ORIGIN,
+      0x5b,0xa0,       0x3a,0xde,0x68,0xb1 },
+    8,
+    { "soo 23456:987654321" }
+  },
+  { /* 3 */
+    "asn4",
+    "rt 168450976:4321",
+    { ECOMMUNITY_ENCODE_AS4, ECOMMUNITY_SITE_ORIGIN,
+      0xa,0xa,0x5b,0xa0,       0x10,0xe1 },
+    8,
+    { "soo 168450976:4321" }
+  },
+  { NULL, NULL, {0}, 0, { NULL } }
+};
+
+
+/* validate the given aspath */
+static int
+validate (struct ecommunity *ecom, const struct test_spec *sp)
+{
+  int fails = 0;
+  struct ecommunity *etmp;
+  char *str1, *str2;
+    
+  printf ("got:\n  %s\n", ecommunity_str (ecom));
+  str1 = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
+  etmp = ecommunity_str2com (str1, 0, 1);
+  if (etmp)
+    str2 = ecommunity_ecom2str (etmp, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
+  else
+    str2 = NULL;
+  
+  if (strcmp (sp->shouldbe, str1))
+    {
+      failed++;
+      fails++;
+      printf ("shouldbe: %s\n%s\n", str1, sp->shouldbe);
+    }
+  if (!etmp || strcmp (str1, str2))
+    {
+      failed++;
+      fails++;
+      printf ("dogfood: in %s\n"
+              "    in->out %s\n",
+              str1, 
+              (etmp && str2) ? str2 : "NULL");
+    }
+  ecommunity_free (&etmp);
+  XFREE (MTYPE_ECOMMUNITY_STR, str1);
+  XFREE (MTYPE_ECOMMUNITY_STR, str2);
+  
+  return fails;
+}
+
+/* basic parsing test */
+static void
+parse_test (struct test_segment *t)
+{
+  struct ecommunity *ecom;
+  
+  printf ("%s: %s\n", t->name, t->desc);
+
+  ecom = ecommunity_parse ((u_int8_t *)t->data, t->len);
+
+  printf ("ecom: %s\nvalidating...:\n", ecommunity_str (ecom));
+
+  if (!validate (ecom, &t->sp))
+    printf ("OK\n");
+  else
+    printf ("failed\n");
+  
+  printf ("\n");
+  ecommunity_unintern (&ecom);
+}
+
+     
+int
+main (void)
+{
+  int i = 0;
+  ecommunity_init();
+  while (test_segments[i].name)
+    parse_test (&test_segments[i++]);
+  
+  printf ("failures: %d\n", failed);
+  //printf ("aspath count: %ld\n", aspath_count());
+  return failed;
+  //return (failed + aspath_count());
+}
diff --git a/tests/global-conf.exp b/tests/global-conf.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/heavy-thread.c b/tests/heavy-thread.c
new file mode 100644 (file)
index 0000000..c2e71c1
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * $Id: heavy-thread.c,v 1.2 2005/04/25 16:42:24 paul Exp $
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model, as demonstrated by heavy.c, and how
+ * they can be mitigated using a background thread.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that, unlike heavy.c, the vty interface
+ * remains responsive.
+ */
+#include <zebra.h>
+#include <math.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+
+#include "tests.h"
+
+extern struct thread_master *master;
+
+enum
+{
+  ITERS_FIRST = 0,
+  ITERS_ERR = 100,
+  ITERS_LATER = 400,
+  ITERS_PRINT = 10,
+  ITERS_MAX = 1000,
+};
+
+struct work_state {
+  struct vty *vty;
+  char *str;
+  int i;
+};
+
+static void
+slow_func (struct vty *vty, const char *str, const int i)
+{
+  double x = 1;
+  int j;
+  
+  for (j = 0; j < 300; j++)
+    x += sin(x)*j;
+  
+  if ((i % ITERS_LATER) == 0)
+    printf ("%s: %d, temporary error, save this somehow and do it later..\n",
+            __func__, i);
+  
+  if ((i % ITERS_ERR) == 0)
+    printf ("%s: hard error\n", __func__);
+  
+  if ((i % ITERS_PRINT) == 0)
+    printf ("%s did %d, x = %g\n", str, i, x);
+}
+
+static int
+clear_something (struct thread *thread)
+{
+  struct work_state *ws = THREAD_ARG(thread);
+  
+  /* this could be like iterating through 150k of route_table 
+   * or worse, iterating through a list of peers, to bgp_stop them with
+   * each having 150k route tables to process...
+   */
+  while (ws->i < ITERS_MAX)
+    {
+      slow_func(ws->vty, ws->str, ws->i);
+      ws->i++;
+      if (thread_should_yield(thread))
+        {
+         thread_add_background(master, clear_something, ws, 0);
+         return 0;
+        }
+    }
+  
+  /* All done! */
+  XFREE (MTYPE_TMP, ws->str);
+  XFREE (MTYPE_TMP, ws);
+  return 0;
+}
+
+DEFUN (clear_foo,
+       clear_foo_cmd,
+       "clear foo .LINE",
+       "clear command\n"
+       "arbitrary string\n")
+{
+  char *str;
+  struct work_state *ws;
+
+  if (!argc)
+    {
+      vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  str = argv_concat (argv, argc, 0);
+  
+  if ((ws = XMALLOC(MTYPE_TMP, sizeof(*ws))) == NULL)
+    {
+      zlog_err ("%s: unable to allocate work_state", __func__);
+      return CMD_WARNING;
+    }
+  
+  if (!(ws->str = XSTRDUP (MTYPE_TMP, str)))
+    {
+      zlog_err ("%s: unable to xstrdup", __func__);
+      XFREE (MTYPE_TMP, ws);
+      return CMD_WARNING;
+    }
+  
+  ws->vty = vty;
+  ws->i = ITERS_FIRST;
+
+  thread_add_background(master, clear_something, ws, 0);
+
+  return CMD_SUCCESS;
+}
+
+void
+test_init()
+{
+  install_element (VIEW_NODE, &clear_foo_cmd);
+}
diff --git a/tests/heavy-wq.c b/tests/heavy-wq.c
new file mode 100644 (file)
index 0000000..2f133cc
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that the vty interface is unresponsive
+ * for quite a period of time, due to the clear_something command
+ * taking a very long time to complete.
+ */
+#include <zebra.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+#include "workqueue.h"
+#include <math.h>
+
+#include "tests.h"
+
+extern struct thread_master *master;
+static struct work_queue *heavy_wq;
+
+struct heavy_wq_node
+{
+  char *str;
+  int i;
+};
+
+enum
+{
+  ITERS_FIRST = 0,
+  ITERS_ERR = 100,
+  ITERS_LATER = 400,
+  ITERS_PRINT = 10,
+  ITERS_MAX = 1000,
+};
+
+static void
+heavy_wq_add (struct vty *vty, const char *str, int i)
+{
+  struct heavy_wq_node *hn;
+
+  if ((hn = XCALLOC (MTYPE_PREFIX_LIST, sizeof(struct heavy_wq_node))) == NULL)
+    {
+      zlog_err ("%s: unable to allocate hn", __func__);
+      return;
+    }
+  
+  hn->i = i;
+  if (!(hn->str = XSTRDUP (MTYPE_PREFIX_LIST_STR, str)))
+    {
+      zlog_err ("%s: unable to xstrdup", __func__);
+      XFREE (MTYPE_PREFIX_LIST, hn);
+      return;
+    }
+  
+  work_queue_add (heavy_wq, hn);
+  
+  return;
+}
+
+static void
+slow_func_err (struct work_queue *wq, struct work_queue_item *item)
+{
+  printf ("%s: running error function\n", __func__);
+}
+
+static void
+slow_func_del (struct work_queue *wq, void *data)
+{
+  struct heavy_wq_node *hn = data;
+  assert (hn && hn->str);
+  printf ("%s: %s\n", __func__, hn->str);
+  XFREE (MTYPE_PREFIX_LIST_STR, hn->str);
+  hn->str = NULL;  
+  XFREE(MTYPE_PREFIX_LIST, hn);
+}
+
+static wq_item_status
+slow_func (struct work_queue *wq, void *data)
+{
+  struct heavy_wq_node *hn = data;
+  double x = 1;
+  int j;
+  
+  assert (hn && hn->str);
+  
+  for (j = 0; j < 300; j++)
+    x += sin(x)*j;
+  
+  if ((hn->i % ITERS_LATER) == 0)
+    return WQ_RETRY_LATER;
+  
+  if ((hn->i % ITERS_ERR) == 0)
+    return WQ_RETRY_NOW;
+  
+  if ((hn->i % ITERS_PRINT) == 0)
+    printf ("%s did %d, x = %g\n", hn->str, hn->i, x);
+
+  return WQ_SUCCESS;
+}
+
+static void
+clear_something (struct vty *vty, const char *str)
+{
+  int i;
+  
+  /* this could be like iterating through 150k of route_table 
+   * or worse, iterating through a list of peers, to bgp_stop them with
+   * each having 150k route tables to process...
+   */
+  for (i = ITERS_FIRST; i < ITERS_MAX; i++)
+    heavy_wq_add (vty, str, i);
+}
+
+DEFUN (clear_foo,
+       clear_foo_cmd,
+       "clear foo .LINE",
+       "clear command\n"
+       "arbitrary string\n")
+{
+  char *str;
+  if (!argc)
+    {
+      vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  str = argv_concat (argv, argc, 0);
+  
+  clear_something (vty, str);
+  XFREE (MTYPE_TMP, str);
+  return CMD_SUCCESS;
+}
+
+static int
+heavy_wq_init ()
+{
+  if (! (heavy_wq = work_queue_new (master, "heavy_work_queue")))
+    {
+      zlog_err ("%s: could not get new work queue!", __func__);
+      return -1;
+    }
+  
+  heavy_wq->spec.workfunc = &slow_func;
+  heavy_wq->spec.errorfunc = &slow_func_err;
+  heavy_wq->spec.del_item_data = &slow_func_del;
+  heavy_wq->spec.max_retries = 3;
+  heavy_wq->spec.hold = 1000;
+  
+  return 0;
+}
+
+void
+test_init()
+{
+  install_element (VIEW_NODE, &clear_foo_cmd);
+  heavy_wq_init();
+}
diff --git a/tests/heavy.c b/tests/heavy.c
new file mode 100644 (file)
index 0000000..9af46c8
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * $Id: heavy.c,v 1.3 2005/04/25 16:42:24 paul Exp $
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* This programme shows the effects of 'heavy' long-running functions
+ * on the cooperative threading model.
+ *
+ * Run it with a config file containing 'password whatever', telnet to it
+ * (it defaults to port 4000) and enter the 'clear foo string' command.
+ * then type whatever and observe that the vty interface is unresponsive
+ * for quite a period of time, due to the clear_something command
+ * taking a very long time to complete.
+ */
+#include <zebra.h>
+
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include <math.h>
+
+#include "tests.h"
+
+enum
+{
+  ITERS_FIRST = 0,
+  ITERS_ERR = 100,
+  ITERS_LATER = 400,
+  ITERS_PRINT = 10,
+  ITERS_MAX = 1000,
+};
+
+static void
+slow_func (struct vty *vty, const char *str, const int i)
+{
+  double x = 1;
+  int j;
+  
+  for (j = 0; j < 300; j++)
+    x += sin(x)*j;
+  
+  if ((i % ITERS_LATER) == 0)
+    printf ("%s: %d, temporary error, save this somehow and do it later..\n", 
+            __func__, i);
+  
+  if ((i % ITERS_ERR) == 0)
+    printf ("%s: hard error\n", __func__);
+  
+  if ((i % ITERS_PRINT) == 0)
+    printf ("%s did %d, x = %g%s", str, i, x, VTY_NEWLINE);  
+}
+
+static void
+clear_something (struct vty *vty, const char *str)
+{
+  int i;
+  
+  /* this could be like iterating through 150k of route_table 
+   * or worse, iterating through a list of peers, to bgp_stop them with
+   * each having 150k route tables to process...
+   */
+  for (i = ITERS_FIRST; i < ITERS_MAX; i++)
+    slow_func (vty, str, i);
+}
+
+DEFUN (clear_foo,
+       clear_foo_cmd,
+       "clear foo .LINE",
+       "clear command\n"
+       "arbitrary string\n")
+{
+  char *str;
+  if (!argc)
+    {
+      vty_out (vty, "%% string argument required%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  str = argv_concat (argv, argc, 0);
+  
+  clear_something (vty, str);
+  XFREE (MTYPE_TMP, str);
+  return CMD_SUCCESS;
+}
+
+static void
+slow_vty_init()
+{
+  install_element (VIEW_NODE, &clear_foo_cmd);
+}
+
+void
+test_init()
+{
+  slow_vty_init();
+}
diff --git a/tests/lib/bgpd.exp b/tests/lib/bgpd.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am
new file mode 100644 (file)
index 0000000..4b74e2d
--- /dev/null
@@ -0,0 +1,6 @@
+EXTRA_DIST = \
+       tabletest.exp \
+       test-timer-correctness.exp \
+       testcommands.exp \
+       testcli.exp \
+       testnexthopiter.exp
diff --git a/tests/libzebra.tests/tabletest.exp b/tests/libzebra.tests/tabletest.exp
new file mode 100644 (file)
index 0000000..5838d4f
--- /dev/null
@@ -0,0 +1,9 @@
+set timeout 10
+set testprefix "tabletest "
+set aborted 0
+
+spawn "./tabletest"
+
+for {set i 0} {$i <  6} {incr i 1} { onesimple "cmp $i" "Verifying cmp"; }
+for {set i 0} {$i < 11} {incr i 1} { onesimple "succ $i" "Verifying successor"; }
+onesimple "pause" "Verified pausing"
diff --git a/tests/libzebra.tests/test-timer-correctness.exp b/tests/libzebra.tests/test-timer-correctness.exp
new file mode 100644 (file)
index 0000000..83531c7
--- /dev/null
@@ -0,0 +1,7 @@
+set timeout 10
+set testprefix "test-timer-correctness"
+set aborted 0
+
+spawn "./test-timer-correctness"
+
+onesimple "" "Expected output and actual output match."
diff --git a/tests/libzebra.tests/testcli.exp b/tests/libzebra.tests/testcli.exp
new file mode 100644 (file)
index 0000000..778bd0c
--- /dev/null
@@ -0,0 +1,23 @@
+set timeout 30
+set test_name "testcli"
+
+spawn sh -c "./testcli < $env(srcdir)/testcli.in | diff -au $env(srcdir)/testcli.refout -"
+
+expect {
+       eof {
+       }
+       timeout {
+               exp_close
+               fail "$test_name: timeout"
+       }
+}
+
+catch wait result
+set os_error    [lindex $result 2]
+set exit_status [lindex $result 3]
+
+if { $os_error == 0 && $exit_status == 0 } {
+       pass "$test_name"
+} else {
+       fail "$test_name"
+}
diff --git a/tests/libzebra.tests/testcommands.exp b/tests/libzebra.tests/testcommands.exp
new file mode 100644 (file)
index 0000000..d4bfc82
--- /dev/null
@@ -0,0 +1,31 @@
+set timeout 30
+set test_name "testcommands"
+
+if {![info exists env(QUAGGA_TEST_COMMANDS)]} {
+       # sadly, the test randomly fails when configure parameters differ from
+       # what was used to create testcommands.refout.  this can be fixed by
+       # shipping a matching vtysh_cmd.c, which we'll add after 0.99.23
+       unresolved "$test_name"
+       exit 0
+}
+
+spawn sh -c "./testcommands -e 0 < $env(srcdir)/testcommands.in | diff -au - $env(srcdir)/testcommands.refout"
+
+expect {
+       eof {
+       }
+       timeout {
+               exp_close
+               fail "$test_name: timeout"
+       }
+}
+
+catch wait result
+set os_error    [lindex $result 2]
+set exit_status [lindex $result 3]
+
+if { $os_error == 0 && $exit_status == 0 } {
+       pass "$test_name"
+} else {
+       fail "$test_name"
+}
diff --git a/tests/libzebra.tests/testnexthopiter.exp b/tests/libzebra.tests/testnexthopiter.exp
new file mode 100644 (file)
index 0000000..be35a0a
--- /dev/null
@@ -0,0 +1,8 @@
+set timeout 10
+set testprefix "testnexthopiter "
+set aborted 0
+
+spawn "./testnexthopiter"
+
+onesimple "simple" "Simple test passed."
+onesimple "prng" "PRNG test passed."
diff --git a/tests/libzebra.tests/teststream.exp b/tests/libzebra.tests/teststream.exp
new file mode 100644 (file)
index 0000000..ca602e3
--- /dev/null
@@ -0,0 +1,28 @@
+set timeout 10
+spawn "./teststream"
+
+expect {
+       "endp: 15, readable: 15, writeable: 1009" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "endp: 15, readable: 15, writeable: 0" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "c: 0xef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "w: 0xbeef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "l: 0xdeadbeef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+expect {
+       "q: 0xdeadbeefdeadbeef" { }
+       eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } }
+pass "teststream"
diff --git a/tests/main.c b/tests/main.c
new file mode 100644 (file)
index 0000000..5396c7d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * $Id: main.c,v 1.1 2005/04/25 16:42:24 paul Exp $
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+
+extern void test_init();
+
+struct thread_master *master;
+
+struct option longopts[] = 
+{
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "version",     no_argument,       NULL, 'v'},
+  { 0 }
+};
+
+DEFUN (daemon_exit,
+       daemon_exit_cmd,
+       "daemon-exit",
+       "Make the daemon exit\n")
+{
+  exit(0);
+}
+
+static int timer_count;
+static int
+test_timer (struct thread *thread)
+{
+  int *count = THREAD_ARG(thread);
+  
+  printf ("run %d of timer\n", (*count)++);
+  thread_add_timer (master, test_timer, count, 5);
+  return 0;
+}
+
+static void
+test_timer_init()
+{
+  thread_add_timer (master, test_timer, &timer_count, 10);
+}
+
+static void
+test_vty_init()
+{
+  install_element (VIEW_NODE, &daemon_exit_cmd);
+}
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\
+Daemon which does 'slow' things.\n\n\
+-d, --daemon       Runs in daemon mode\n\
+-f, --config_file  Set configuration file name\n\
+-A, --vty_addr     Set vty's bind address\n\
+-P, --vty_port     Set vty's port number\n\
+-v, --version      Print program version\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+  exit (status);
+}
+
+
+/* main routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  char *vty_addr = NULL;
+  int vty_port = 4000;
+  int daemon_mode = 0;
+  char *progname;
+  struct thread thread;
+  char *config_file = NULL;
+  
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* get program name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  /* master init. */
+  master = thread_master_create ();
+
+  while (1) 
+    {
+      int opt;
+
+      opt = getopt_long (argc, argv, "dhf:A:P:v", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+        case 'f':
+          config_file = optarg;
+          break;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+       case 'P':
+          /* Deal with atoi() returning 0 on failure */
+          if (strcmp(optarg, "0") == 0)
+            {
+              vty_port = 0;
+              break;
+            } 
+          vty_port = atoi (optarg);
+          vty_port = (vty_port ? vty_port : 4000);
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Library inits. */
+  cmd_init (1);
+  vty_init (master);
+  memory_init ();
+
+  /* OSPF vty inits. */
+  test_vty_init ();
+
+  /* Change to the daemon program. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      fprintf(stderr, "daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Create VTY socket */
+  vty_serv_sock (vty_addr, vty_port, "/tmp/.heavy.sock");
+  
+  /* Configuration file read*/
+  if (!config_file)
+    usage (progname, 1);
+  vty_read_config (config_file, NULL);
+  
+  test_timer_init();
+  
+  test_init();  
+  
+  /* Fetch next active thread. */
+  while (thread_fetch (master, &thread))
+    thread_call (&thread);
+
+  /* Not reached. */
+  exit (0);
+}
+
diff --git a/tests/prng.c b/tests/prng.c
new file mode 100644 (file)
index 0000000..bdcfb07
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Very simple prng to allow for randomized tests with reproducable
+ * results.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "prng.h"
+
+struct prng
+{
+  unsigned long long state1;
+  unsigned long long state2;
+};
+
+static char
+prng_bit(struct prng *prng)
+{
+  prng->state1 *= 2416;
+  prng->state1 += 374441;
+  prng->state1 %= 1771875;
+
+  if (prng->state1 % 2)
+    {
+      prng->state2 *= 84589;
+      prng->state2 += 45989;
+      prng->state2 %= 217728;
+    }
+
+  return prng->state2 % 2;
+}
+
+struct prng*
+prng_new(unsigned long long seed)
+{
+  struct prng *rv = calloc(sizeof(*rv), 1);
+  assert(rv);
+
+  rv->state1 = rv->state2 = seed;
+
+  return rv;
+}
+
+unsigned int
+prng_rand(struct prng *prng)
+{
+  unsigned int i, rv = 0;
+
+  for (i = 0; i < 32; i++)
+    {
+      rv |= prng_bit(prng);
+      rv <<= 1;
+    }
+  return rv;
+}
+
+const char *
+prng_fuzz(struct prng *prng,
+          const char *string,
+          const char *charset,
+          unsigned int operations)
+{
+  static char buf[256];
+  unsigned int charset_len;
+  unsigned int i;
+  unsigned int offset;
+  unsigned int op;
+  unsigned int character;
+
+  assert(strlen(string) < sizeof(buf));
+
+  strncpy(buf, string, sizeof(buf));
+  charset_len = strlen(charset);
+
+  for (i = 0; i < operations; i++)
+    {
+      offset = prng_rand(prng) % strlen(buf);
+      op = prng_rand(prng) % 3;
+
+      switch (op)
+        {
+        case 0:
+          /* replace */
+          character = prng_rand(prng) % charset_len;
+          buf[offset] = charset[character];
+          break;
+        case 1:
+          /* remove */
+          memmove(buf + offset, buf + offset + 1, strlen(buf) - offset);
+          break;
+        case 2:
+          /* insert */
+          assert(strlen(buf) + 1 < sizeof(buf));
+
+          memmove(buf + offset + 1, buf + offset, strlen(buf) + 1 - offset);
+          character = prng_rand(prng) % charset_len;
+          buf[offset] = charset[character];
+          break;
+        }
+    }
+  return buf;
+}
+
+void
+prng_free(struct prng *prng)
+{
+  free(prng);
+}
diff --git a/tests/prng.h b/tests/prng.h
new file mode 100644 (file)
index 0000000..cf0bacc
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Very simple prng to allow for randomized tests with reproducable
+ * results.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef _PRNG_H
+#define _PRNG_H
+
+struct prng;
+
+struct prng* prng_new(unsigned long long seed);
+unsigned int prng_rand(struct prng*);
+const char * prng_fuzz(struct prng*,
+                       const char *string,
+                       const char *charset,
+                       unsigned int operations);
+void prng_free(struct prng *);
+
+#endif
diff --git a/tests/table_test.c b/tests/table_test.c
new file mode 100644 (file)
index 0000000..fc9cc3d
--- /dev/null
@@ -0,0 +1,555 @@
+/* $QuaggaId: Format:%an, %ai, %h$ $
+ *
+ * Routing table test
+ * Copyright (C) 2012 OSR.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+
+/*
+ * test_node_t
+ *
+ * Information that is kept for each node in the radix tree.
+ */
+typedef struct test_node_t_
+{
+
+  /*
+   * Human readable representation of the string. Allocated using
+   * malloc()/dup().
+   */
+  char *prefix_str;
+} test_node_t;
+
+struct thread_master *master;
+
+/*
+ * add_node
+ *
+ * Add the given prefix (passed in as a string) to the given table.
+ */
+static void
+add_node (struct route_table *table, const char *prefix_str)
+{
+  struct prefix_ipv4 p;
+  test_node_t *node;
+  struct route_node *rn;
+
+  assert (prefix_str);
+
+  if (str2prefix_ipv4 (prefix_str, &p) <= 0)
+    {
+      assert (0);
+    }
+
+  rn = route_node_get (table, (struct prefix *) &p);
+  if (rn->info)
+    {
+      assert (0);
+      return;
+    }
+
+  node = malloc (sizeof (test_node_t));
+  assert (node);
+  node->prefix_str = strdup (prefix_str);
+  assert (node->prefix_str);
+  rn->info = node;
+}
+
+/*
+ * add_nodes
+ *
+ * Convenience function to add a bunch of nodes together.
+ *
+ * The arguments must be prefixes in string format, with a NULL as the
+ * last argument.
+ */
+static void
+add_nodes (struct route_table *table, ...)
+{
+  va_list arglist;
+  char *prefix;
+
+  va_start (arglist, table);
+
+  prefix = va_arg (arglist, char *);
+  while (prefix)
+    {
+      add_node (table, prefix);
+      prefix = va_arg (arglist, char *);
+    }
+
+  va_end (arglist);
+}
+
+/*
+ * print_subtree
+ *
+ * Recursive function to print a route node and its children.
+ *
+ * @see print_table
+ */
+static void
+print_subtree (struct route_node *rn, const char *legend, int indent_level)
+{
+  char buf[INET_ADDRSTRLEN + 4];
+  int i;
+
+  /*
+   * Print this node first.
+   */
+  for (i = 0; i < indent_level; i++)
+    {
+      printf ("  ");
+    }
+
+  prefix2str (&rn->p, buf, sizeof (buf));
+  printf ("%s: %s", legend, buf);
+  if (!rn->info)
+    {
+      printf (" (internal)");
+    }
+  printf ("\n");
+  if (rn->l_left)
+    {
+      print_subtree (rn->l_left, "Left", indent_level + 1);
+    }
+  if (rn->l_right)
+    {
+      print_subtree (rn->l_right, "Right", indent_level + 1);
+    }
+}
+
+/*
+ * print_table
+ *
+ * Function that prints out the internal structure of a route table.
+ */
+static void
+print_table (struct route_table *table)
+{
+  struct route_node *rn;
+
+  rn = table->top;
+
+  if (!rn)
+    {
+      printf ("<Empty Table>\n");
+      return;
+    }
+
+  print_subtree (rn, "Top", 0);
+}
+
+/*
+ * clear_table
+ *
+ * Remove all nodes from the given table.
+ */
+static void
+clear_table (struct route_table *table)
+{
+  route_table_iter_t iter;
+  struct route_node *rn;
+  test_node_t *node;
+
+  route_table_iter_init (&iter, table);
+
+  while ((rn = route_table_iter_next (&iter)))
+    {
+      node = rn->info;
+      if (!node)
+       {
+         continue;
+       }
+      rn->info = NULL;
+      route_unlock_node (rn);
+      free (node->prefix_str);
+      free (node);
+    }
+
+  route_table_iter_cleanup (&iter);
+
+  assert (table->top == NULL);
+}
+
+/*
+ * verify_next_by_iterating
+ *
+ * Iterate over the tree to make sure that the first prefix after
+ * target_pfx is the expected one. Note that target_pfx may not be
+ * present in the tree.
+ */
+static void
+verify_next_by_iterating (struct route_table *table,
+                         struct prefix *target_pfx, struct prefix *next_pfx)
+{
+  route_table_iter_t iter;
+  struct route_node *rn;
+
+  route_table_iter_init (&iter, table);
+  while ((rn = route_table_iter_next (&iter)))
+    {
+      if (route_table_prefix_iter_cmp (&rn->p, target_pfx) > 0)
+       {
+         assert (!prefix_cmp (&rn->p, next_pfx));
+         break;
+       }
+    }
+
+  if (!rn)
+    {
+      assert (!next_pfx);
+    }
+
+  route_table_iter_cleanup (&iter);
+}
+
+/*
+ * verify_next
+ *
+ * Verifies that route_table_get_next() returns the expected result
+ * (result) for the prefix string 'target'.
+ */
+static void
+verify_next (struct route_table *table, const char *target, const char *next)
+{
+  struct prefix_ipv4 target_pfx, next_pfx;
+  struct route_node *rn;
+  char result_buf[INET_ADDRSTRLEN + 4];
+
+  if (str2prefix_ipv4 (target, &target_pfx) <= 0)
+    {
+      assert (0);
+    }
+
+  rn = route_table_get_next (table, (struct prefix *) &target_pfx);
+  if (rn)
+    {
+      prefix2str (&rn->p, result_buf, sizeof (result_buf));
+    }
+  else
+    {
+      snprintf (result_buf, sizeof (result_buf), "(Null)");
+    }
+
+  printf ("\n");
+  print_table (table);
+  printf ("Verifying successor of %s. Expected: %s, Result: %s\n", target,
+         next ? next : "(Null)", result_buf);
+
+  if (!rn)
+    {
+      assert (!next);
+      verify_next_by_iterating (table, (struct prefix *) &target_pfx, NULL);
+      return;
+    }
+
+  assert (next);
+
+  if (str2prefix_ipv4 (next, &next_pfx) <= 0)
+    {
+      assert (0);
+    }
+
+  if (prefix_cmp (&rn->p, (struct prefix *) &next_pfx))
+    {
+      assert (0);
+    }
+  route_unlock_node (rn);
+
+  verify_next_by_iterating (table, (struct prefix *) &target_pfx,
+                           (struct prefix *) &next_pfx);
+}
+
+/*
+ * test_get_next
+ */
+static void
+test_get_next (void)
+{
+  struct route_table *table;
+
+  printf ("\n\nTesting route_table_get_next()\n");
+  table = route_table_init ();
+
+  /*
+   * Target exists in tree, but has no successor.
+   */
+  add_nodes (table, "1.0.1.0/24", NULL);
+  verify_next (table, "1.0.1.0/24", NULL);
+  clear_table (table);
+
+  /*
+   * Target exists in tree, and there is a node in its left subtree.
+   */
+  add_nodes (table, "1.0.1.0/24", "1.0.1.0/25", NULL);
+  verify_next (table, "1.0.1.0/24", "1.0.1.0/25");
+  clear_table (table);
+
+  /*
+   * Target exists in tree, and there is a node in its right subtree.
+   */
+  add_nodes (table, "1.0.1.0/24", "1.0.1.128/25", NULL);
+  verify_next (table, "1.0.1.0/24", "1.0.1.128/25");
+  clear_table (table);
+
+  /*
+   * Target exists in the tree, next node is outside subtree.
+   */
+  add_nodes (table, "1.0.1.0/24", "1.1.0.0/16", NULL);
+  verify_next (table, "1.0.1.0/24", "1.1.0.0/16");
+  clear_table (table);
+
+  /*
+   * The target node does not exist in the tree for all the test cases
+   * below this point.
+   */
+
+  /*
+   * There is no successor in the tree.
+   */
+  add_nodes (table, "1.0.0.0/16", NULL);
+  verify_next (table, "1.0.1.0/24", NULL);
+  clear_table (table);
+
+  /*
+   * There exists a node that would be in the target's left subtree.
+   */
+  add_nodes (table, "1.0.0.0/16", "1.0.1.0/25", NULL);
+  verify_next (table, "1.0.1.0/24", "1.0.1.0/25");
+  clear_table (table);
+
+  /*
+   * There exists a node would be in the target's right subtree.
+   */
+  add_nodes (table, "1.0.0.0/16", "1.0.1.128/25", NULL);
+  verify_next (table, "1.0.1.0/24", "1.0.1.128/25");
+  clear_table (table);
+
+  /*
+   * A search for the target reaches a node where there are no child
+   * nodes in the direction of the target (left), but the node has a
+   * right child.
+   */
+  add_nodes (table, "1.0.0.0/16", "1.0.128.0/17", NULL);
+  verify_next (table, "1.0.0.0/17", "1.0.128.0/17");
+  clear_table (table);
+
+  /*
+   * A search for the target reaches a node with no children. We have
+   * to go upwards in the tree to find a successor.
+   */
+  add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/24",
+            "1.0.128.0/17", NULL);
+  verify_next (table, "1.0.1.0/25", "1.0.128.0/17");
+  clear_table (table);
+
+  /*
+   * A search for the target reaches a node where neither the node nor
+   * the target prefix contain each other.
+   *
+   * In first case below the node succeeds the target.
+   *
+   * In the second case, the node comes before the target, so we have
+   * to go up the tree looking for a successor.
+   */
+  add_nodes (table, "1.0.0.0/16", "1.0.1.0/24", NULL);
+  verify_next (table, "1.0.0.0/24", "1.0.1.0/24");
+  clear_table (table);
+
+  add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/25",
+            "1.0.128.0/17", NULL);
+  verify_next (table, "1.0.1.128/25", "1.0.128.0/17");
+  clear_table (table);
+
+  route_table_finish (table);
+}
+
+/*
+ * verify_prefix_iter_cmp
+ */
+static void
+verify_prefix_iter_cmp (const char *p1, const char *p2, int exp_result)
+{
+  struct prefix_ipv4 p1_pfx, p2_pfx;
+  int result;
+
+  if (str2prefix_ipv4 (p1, &p1_pfx) <= 0)
+    {
+      assert (0);
+    }
+
+  if (str2prefix_ipv4 (p2, &p2_pfx) <= 0)
+    {
+      assert (0);
+    }
+
+  result = route_table_prefix_iter_cmp ((struct prefix *) &p1_pfx,
+                                       (struct prefix *) &p2_pfx);
+
+  printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
+
+  assert (exp_result == result);
+
+  /*
+   * Also check the reverse comparision.
+   */
+  result = route_table_prefix_iter_cmp ((struct prefix *) &p2_pfx,
+                                       (struct prefix *) &p1_pfx);
+
+  if (exp_result)
+    {
+      exp_result = -exp_result;
+    }
+
+  printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result);
+  assert (result == exp_result);
+}
+
+/*
+ * test_prefix_iter_cmp
+ *
+ * Tests comparision of prefixes according to order of iteration.
+ */
+static void
+test_prefix_iter_cmp ()
+{
+  printf ("\n\nTesting route_table_prefix_iter_cmp()\n");
+
+  verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/8", 0);
+
+  verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/16", -1);
+
+  verify_prefix_iter_cmp ("1.0.0.0/16", "1.128.0.0/16", -1);
+}
+
+/*
+ * verify_iter_with_pause
+ *
+ * Iterates over a tree using two methods: 'normal' iteration, and an
+ * iterator that pauses at each node. Verifies that the two methods
+ * yield the same results.
+ */
+static void
+verify_iter_with_pause (struct route_table *table)
+{
+  unsigned long num_nodes;
+  struct route_node *rn, *iter_rn;
+  route_table_iter_t iter_space;
+  route_table_iter_t *iter = &iter_space;
+
+  route_table_iter_init (iter, table);
+  num_nodes = 0;
+
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    {
+      num_nodes++;
+      route_table_iter_pause (iter);
+
+      assert (iter->current == NULL);
+      if (route_table_iter_started (iter))
+       {
+         assert (iter->state == RT_ITER_STATE_PAUSED);
+       }
+      else
+       {
+         assert (rn == table->top);
+         assert (iter->state == RT_ITER_STATE_INIT);
+       }
+
+      iter_rn = route_table_iter_next (iter);
+
+      /*
+       * Make sure both iterations return the same node.
+       */
+      assert (rn == iter_rn);
+    }
+
+  assert (num_nodes == route_table_count (table));
+
+  route_table_iter_pause (iter);
+  iter_rn = route_table_iter_next (iter);
+
+  assert (iter_rn == NULL);
+  assert (iter->state == RT_ITER_STATE_DONE);
+
+  assert (route_table_iter_next (iter) == NULL);
+  assert (iter->state == RT_ITER_STATE_DONE);
+
+  route_table_iter_cleanup (iter);
+
+  print_table (table);
+  printf ("Verified pausing iteration on tree with %lu nodes\n", num_nodes);
+}
+
+/*
+ * test_iter_pause
+ */
+static void
+test_iter_pause (void)
+{
+  struct route_table *table;
+  int i, num_prefixes;
+  const char *prefixes[] = {
+    "1.0.1.0/24",
+    "1.0.1.0/25",
+    "1.0.1.128/25",
+    "1.0.2.0/24",
+    "2.0.0.0/8"
+  };
+
+  num_prefixes = sizeof (prefixes) / sizeof (prefixes[0]);
+
+  printf ("\n\nTesting that route_table_iter_pause() works as expected\n");
+  table = route_table_init ();
+  for (i = 0; i < num_prefixes; i++)
+    {
+      add_nodes (table, prefixes[i], NULL);
+    }
+
+  verify_iter_with_pause (table);
+
+  clear_table (table);
+  route_table_finish (table);
+}
+
+/*
+ * run_tests
+ */
+static void
+run_tests (void)
+{
+  test_prefix_iter_cmp ();
+  test_get_next ();
+  test_iter_pause ();
+}
+
+/*
+ * main
+ */
+int
+main (void)
+{
+  run_tests ();
+}
diff --git a/tests/test-buffer.c b/tests/test-buffer.c
new file mode 100644 (file)
index 0000000..e95d6fb
--- /dev/null
@@ -0,0 +1,59 @@
+/* 
+ * Copyright (C) 2004 Paul Jakma
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <memory.h>
+#include <buffer.h>
+
+struct thread_master *master;
+
+int
+main(int argc, char **argv)
+{
+  struct buffer *b1, *b2;
+  int n;
+  char junk[3];
+  char c = 'a';
+
+  memory_init();
+  
+  if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1))
+    {
+      fprintf(stderr, "Usage: %s <number of chars to simulate>\n", *argv);
+      return 1;
+    }
+
+  b1 = buffer_new(0);
+  b2 = buffer_new(1024);
+  
+  while (n-- > 0)
+    {
+      buffer_put(b1, &c, 1);
+      buffer_put(b2, &c, 1);
+      if (c++ == 'z')
+        c = 'a';
+      buffer_reset(b1);
+      buffer_reset(b2);
+    }
+  buffer_free(b1);
+  buffer_free(b2);
+  return 0;
+}
diff --git a/tests/test-checksum.c b/tests/test-checksum.c
new file mode 100644 (file)
index 0000000..b6741f3
--- /dev/null
@@ -0,0 +1,550 @@
+/* 
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "checksum.h"
+
+struct thread_master *master;
+
+struct acc_vals {
+  int c0;
+  int c1;
+};
+
+struct csum_vals {
+  struct acc_vals a;
+  int x; 
+  int y;
+};
+
+static struct csum_vals ospfd_vals, isisd_vals;
+
+typedef size_t testsz_t;
+typedef uint16_t testoff_t;
+
+/* Fletcher Checksum -- Refer to RFC1008. */
+#define MODX                 4102
+
+/* The final reduction phase.
+ * This one should be the original ospfd version 
+ */
+static u_int16_t 
+reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+
+  x = ((len - off - 1) * c0 - c1) % 255;
+  
+  if (x <= 0)
+    x += 255;
+  y = 510 - c0 - x;
+  if (y > 255)
+    y -= 255;
+
+   /* take care endian issue. */
+   return htons ((x << 8) + y); 
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* slightly different concatenation */
+static u_int16_t 
+reduce_ospfd1 (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+
+  x = ((len - off - 1) * c0 - c1) % 255;
+  if (x <= 0)
+    x += 255;
+  y = 510 - c0 - x;
+  if (y > 255)
+    y -= 255;
+
+   /* take care endian issue. */
+   return htons ((x << 8) | (y & 0xff)); 
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* original isisd version */
+static u_int16_t 
+reduce_isisd (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+  u_int32_t mul;
+  
+  mul = (len - off)*(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;
+
+  return htons ((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Is the -1 in y wrong perhaps? */
+static u_int16_t 
+reduce_isisd_yfix (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+  u_int32_t mul;
+  
+  mul = (len - off)*(c0);
+  x = mul - c0 - c1;
+  y = c1 - mul;
+
+  if (y > 0)
+    y++;
+  if (x < 0)
+    x--;
+
+  x %= 255;
+  y %= 255;
+
+  if (x == 0)
+    x = 255;
+  if (y == 0)
+    y = 1;
+
+  return htons ((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Move the mods yp */
+static u_int16_t 
+reduce_isisd_mod (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+  u_int32_t mul;
+  
+  mul = (len - off)*(c0);
+  x = mul - c1 - c0;
+  y = c1 - mul - 1;
+
+  x %= 255;
+  y %= 255;
+
+  if (y > 0)
+    y++;
+  if (x < 0)
+    x--;
+
+  if (x == 0)
+    x = 255;
+  if (y == 0)
+    y = 1;
+
+  return htons ((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+/* Move the mods up + fix y */
+static u_int16_t 
+reduce_isisd_mody (struct csum_vals *vals, testsz_t len, testoff_t off)
+{
+#define x vals->x
+#define y vals->y
+#define c0 vals->a.c0
+#define c1 vals->a.c1
+  u_int32_t mul;
+  
+  mul = (len - off)*(c0);
+  x = mul - c0 - c1;
+  y = c1 - mul;
+
+  x %= 255;
+  y %= 255;
+
+  if (y > 0)
+    y++;
+  if (x < 0)
+    x--;
+
+  if (x == 0)
+    x = 255;
+  if (y == 0)
+    y = 1;
+
+  return htons ((x << 8) | (y & 0xff));
+
+#undef x
+#undef y
+#undef c0
+#undef c1
+}
+
+struct reductions_t {
+  const char *name;
+  u_int16_t (*f) (struct csum_vals *, testsz_t, testoff_t);
+} reducts[] = {
+  { .name = "ospfd",           .f = reduce_ospfd },
+  { .name = "ospfd-1",         .f = reduce_ospfd1 },
+  { .name = "isisd",           .f = reduce_isisd },
+  { .name = "isisd-yfix",      .f = reduce_isisd_yfix },
+  { .name = "isisd-mod",       .f = reduce_isisd_mod },
+  { .name = "isisd-mody",      .f = reduce_isisd_mody },
+  { NULL, NULL },
+};
+
+/* The original ospfd checksum */
+static u_int16_t
+ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off)
+{
+  u_char *sp, *ep, *p, *q;
+  int c0 = 0, c1 = 0;
+  int x, y;
+  u_int16_t checksum, *csum;
+
+  csum = (u_int16_t *) (buffer + off);
+  *(csum) = 0;
+  
+  sp = buffer;
+
+  for (ep = sp + len; 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;
+    }
+  
+  ospfd_vals.a.c0 = c0;
+  ospfd_vals.a.c1 = c1;
+  
+  //printf ("%s: len %u, off %u, c0 %d, c1 %d\n",
+  //        __func__, len, off, c0, c1);
+
+  x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255;
+  
+  if (x <= 0)
+    x += 255;
+  y = 510 - c0 - x;
+  if (y > 255)
+    y -= 255;
+  
+  ospfd_vals.x = x;
+  ospfd_vals.y = y;
+  
+  buffer[off] = x;
+  buffer[off + 1] = y;
+  
+  /* take care endian issue. */
+  checksum = htons ((x << 8) | (y & 0xff));
+
+  return (checksum);
+}
+
+/* the original, broken isisd checksum */
+static u_int16_t
+iso_csum_create (u_char * buffer, testsz_t len, testoff_t off)
+{
+
+  u_int8_t *p;
+  int x;
+  int y;
+  u_int32_t mul;
+  u_int32_t c0;
+  u_int32_t c1;
+  u_int16_t checksum, *csum;
+  int i, init_len, partial_len;
+
+  checksum = 0;
+  
+  csum = (u_int16_t *) (buffer + off);
+  *(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;
+    }
+
+  isisd_vals.a.c0 = c0;
+  isisd_vals.a.c1 = c1;
+  
+  mul = (init_len - off) * c0;
+
+  x = mul - c1 - c0;
+  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;
+  
+  isisd_vals.x = x;
+  isisd_vals.y = y;
+  
+  checksum = htons((x << 8) | (y & 0xFF));
+  
+  *(csum) = checksum;
+  
+  /* return the checksum for user usage */
+  return checksum;
+}
+
+static int
+verify (u_char * buffer, testsz_t len)
+{
+  u_int8_t *p;
+  u_int32_t c0;
+  u_int32_t c1;
+  int i, partial_len;
+  p = buffer;
+
+  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;
+
+  return 1;
+}
+
+static int  /* return checksum in low-order 16 bits */
+in_cksum_optimized(void *parg, int nbytes)
+{
+       u_short *ptr = parg;
+       register long           sum;            /* assumes long == 32 bits */
+       register u_short        answer;         /* assumes u_short == 16 bits */
+       register int count;
+       /*
+        * Our algorithm is simple, using a 32-bit accumulator (sum),
+        * we add sequential 16-bit words to it, and at the end, fold back
+        * all the carry bits from the top 16 bits into the lower 16 bits.
+        */
+
+       sum = 0;
+       count = nbytes >> 1; /* div by 2 */
+       for(ptr--; count; --count)
+         sum += *++ptr;
+
+       if (nbytes & 1) /* Odd */
+         sum += *(u_char *)(++ptr);   /* one byte only */
+
+       /*
+        * Add back carry outs from top 16 bits to low 16 bits.
+        */
+
+       sum  = (sum >> 16) + (sum & 0xffff);    /* add high-16 to low-16 */
+       sum += (sum >> 16);                     /* add carry */
+       answer = ~sum;          /* ones-complement, then truncate to 16 bits */
+       return(answer);
+}
+
+
+static int /* return checksum in low-order 16 bits */
+in_cksum_rfc(void *parg, int count)
+/* from RFC 1071 */
+{
+       u_short *addr = parg;
+       /* Compute Internet Checksum for "count" bytes
+        *         beginning at location "addr".
+        */
+       register long  sum = 0;
+
+       while (count > 1)  {
+         /*  This is the inner loop */
+         sum += *addr++;
+         count -= 2;
+       }
+       /*  Add left-over byte, if any */
+       if (count > 0) {
+         sum += *(u_char *)addr;
+       }
+
+       /*  Fold 32-bit sum to 16 bits */
+       while (sum>>16)
+           sum = (sum & 0xffff) + (sum >> 16);
+       return ~sum;
+}
+
+
+int
+main(int argc, char **argv)
+{
+/* 60017 65629 702179 */
+#define MAXDATALEN 60017
+#define BUFSIZE MAXDATALEN + sizeof(u_int16_t)
+  u_char buffer[BUFSIZE];
+  int exercise = 0;
+#define EXERCISESTEP 257
+  
+  srandom (time (NULL));
+  
+  while (1) {
+    u_int16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc;
+    int i,j;
+
+    exercise += EXERCISESTEP;
+    exercise %= MAXDATALEN;
+    
+    for (i = 0; i < exercise; i += sizeof (long int)) {
+      long int rand = random ();
+      
+      for (j = sizeof (long int); j > 0; j--)
+        buffer[i + (sizeof (long int) - j)] = (rand >> (j * 8)) & 0xff;
+    }
+    
+    in_csum = in_cksum(buffer, exercise);
+    in_csum_res = in_cksum_optimized(buffer, exercise);
+    in_csum_rfc = in_cksum_rfc(buffer, exercise);
+    if (in_csum_res != in_csum || in_csum != in_csum_rfc)
+      printf ("verify: in_chksum failed in_csum:%x, in_csum_res:%x,"
+             "in_csum_rfc %x, len:%d\n", 
+             in_csum, in_csum_res, in_csum_rfc, exercise);
+
+    ospfd = ospfd_checksum (buffer, exercise + sizeof(u_int16_t), exercise);
+    if (verify (buffer, exercise + sizeof(u_int16_t)))
+      printf ("verify: ospfd failed\n");
+    isisd = iso_csum_create (buffer, exercise + sizeof(u_int16_t), exercise);
+    if (verify (buffer, exercise + sizeof(u_int16_t)))
+      printf ("verify: isisd failed\n");
+    lib = fletcher_checksum (buffer, exercise + sizeof(u_int16_t), exercise);
+    if (verify (buffer, exercise + sizeof(u_int16_t)))
+      printf ("verify: lib failed\n");
+    
+    if (ospfd != lib) {
+      printf ("Mismatch in values at size %u\n"
+              "ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
+              "isisd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
+              "lib: 0x%04x\n",
+              exercise,
+              ospfd, ospfd_vals.a.c0, ospfd_vals.a.c1, ospfd_vals.x, ospfd_vals.y,
+              isisd, isisd_vals.a.c0, isisd_vals.a.c1, isisd_vals.x, isisd_vals.y,
+              lib
+              );
+      
+      /* Investigate reduction phase discrepencies */
+      if (ospfd_vals.a.c0 == isisd_vals.a.c0
+          && ospfd_vals.a.c1 == isisd_vals.a.c1) {
+        printf ("\n");
+        for (i = 0; reducts[i].name != NULL; i++) {    
+          ospfd = reducts[i].f (&ospfd_vals,
+                                exercise + sizeof (u_int16_t),
+                                exercise);
+          printf ("%20s: x: %02x, y %02x, checksum 0x%04x\n",
+                  reducts[i].name, ospfd_vals.x & 0xff, ospfd_vals.y & 0xff, ospfd);
+        }
+      }
+              
+      printf ("\n  u_char testdata [] = {\n  ");
+      for (i = 0; i < exercise; i++) {
+        printf ("0x%02x,%s",
+                buffer[i],
+                (i + 1) % 8 ? " " : "\n  ");
+      }
+      printf ("\n}\n");
+      exit (1);
+    }
+  }
+}
diff --git a/tests/test-cli.c b/tests/test-cli.c
new file mode 100644 (file)
index 0000000..6fab6d5
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * CLI/command dummy handling tester
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ *                   for Open Source Routing / NetDEF, Inc.
+ *
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "common-cli.h"
+
+DUMMY_DEFUN(cmd0,  "arg ipv4 A.B.C.D");
+DUMMY_DEFUN(cmd1,  "arg ipv4m A.B.C.D/M");
+DUMMY_DEFUN(cmd2,  "arg ipv6 X:X::X:X");
+DUMMY_DEFUN(cmd3,  "arg ipv6m X:X::X:X/M");
+DUMMY_DEFUN(cmd4,  "arg range <5-15>");
+DUMMY_DEFUN(cmd5,  "pat a ( a|b)");
+DUMMY_DEFUN(cmd6,  "pat b  (a|)");
+DUMMY_DEFUN(cmd7,  "pat c (a | b|c) A.B.C.D");
+DUMMY_DEFUN(cmd8,  "pat d {  foo A.B.C.D|bar   X:X::X:X| baz }");
+DUMMY_DEFUN(cmd9,  "pat e [ WORD ]");
+DUMMY_DEFUN(cmd10, "pat f [key]");
+DUMMY_DEFUN(cmd11, "alt a WORD");
+DUMMY_DEFUN(cmd12, "alt a A.B.C.D");
+DUMMY_DEFUN(cmd13, "alt a X:X::X:X");
+
+void test_init(void)
+{
+  install_element (ENABLE_NODE, &cmd0_cmd);
+  install_element (ENABLE_NODE, &cmd1_cmd);
+  install_element (ENABLE_NODE, &cmd2_cmd);
+  install_element (ENABLE_NODE, &cmd3_cmd);
+  install_element (ENABLE_NODE, &cmd4_cmd);
+  install_element (ENABLE_NODE, &cmd5_cmd);
+  install_element (ENABLE_NODE, &cmd6_cmd);
+  install_element (ENABLE_NODE, &cmd7_cmd);
+  install_element (ENABLE_NODE, &cmd8_cmd);
+  install_element (ENABLE_NODE, &cmd9_cmd);
+  install_element (ENABLE_NODE, &cmd10_cmd);
+  install_element (ENABLE_NODE, &cmd11_cmd);
+  install_element (ENABLE_NODE, &cmd12_cmd);
+  install_element (ENABLE_NODE, &cmd13_cmd);
+}
diff --git a/tests/test-commands.c b/tests/test-commands.c
new file mode 100644 (file)
index 0000000..18b3b50
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Test code for lib/command.c
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This program reads in a list of commandlines from stdin
+ * and calls all the public functions of lib/command.c for
+ * both the given command lines and fuzzed versions thereof.
+ *
+ * The output is currently not validated but only logged. It can
+ * be diffed to find regressions between versions.
+ *
+ * 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.
+ */
+
+#define REALLY_NEED_PLAIN_GETOPT 1
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "command.h"
+#include "memory.h"
+#include "vector.h"
+#include "prng.h"
+
+extern vector cmdvec;
+extern struct cmd_node vty_node;
+extern void test_init_cmd(void); /* provided in test-commands-defun.c */
+
+struct thread_master *master; /* dummy for libzebra*/
+
+static vector test_cmds;
+static char test_buf[32768];
+
+static struct cmd_node bgp_node =
+{
+  BGP_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node rip_node =
+{
+  RIP_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node isis_node =
+{
+  ISIS_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+};
+
+static struct cmd_node rmap_node =
+{
+  RMAP_NODE,
+  "%s(config-route-map)# "
+};
+
+static struct cmd_node zebra_node =
+{
+  ZEBRA_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node bgp_vpnv4_node =
+{
+  BGP_VPNV4_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv4_node =
+{
+  BGP_IPV4_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv4m_node =
+{
+  BGP_IPV4M_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv6_node =
+{
+  BGP_IPV6_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv6m_node =
+{
+  BGP_IPV6M_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node ospf_node =
+{
+  OSPF_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node ripng_node =
+{
+  RIPNG_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node ospf6_node =
+{
+  OSPF6_NODE,
+  "%s(config-ospf6)# "
+};
+
+static struct cmd_node babel_node =
+{
+  BABEL_NODE,
+  "%s(config-babel)# "
+};
+
+static struct cmd_node keychain_node =
+{
+  KEYCHAIN_NODE,
+  "%s(config-keychain)# "
+};
+
+static struct cmd_node keychain_key_node =
+{
+  KEYCHAIN_KEY_NODE,
+  "%s(config-keychain-key)# "
+};
+
+static int
+test_callback(struct cmd_element *cmd, struct vty *vty, int argc, const char *argv[])
+{
+  int offset;
+  int rv;
+  int i;
+
+  offset = 0;
+  rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string);
+  if (rv < 0)
+    abort();
+
+  offset += rv;
+
+  for (i = 0; i < argc; i++)
+    {
+      rv = snprintf(test_buf + offset, sizeof(test_buf) - offset, "%s'%s'",
+                    (i == 0) ? ": " : ", ", argv[i]);
+      if (rv < 0)
+        abort();
+      offset += rv;
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+test_load(void)
+{
+  char line[4096];
+
+  test_cmds = vector_init(VECTOR_MIN_SIZE);
+
+  while (fgets(line, sizeof(line), stdin) != NULL)
+    {
+      if (strlen(line))
+        line[strlen(line) - 1] = '\0';
+      if (line[0] == '#')
+        continue;
+      vector_set(test_cmds, XSTRDUP(MTYPE_STRVEC, line));
+    }
+}
+
+static void
+test_init(void)
+{
+  unsigned int node;
+  unsigned int i;
+  struct cmd_node *cnode;
+  struct cmd_element *cmd;
+
+  cmd_init(1);
+
+  install_node (&bgp_node, NULL);
+  install_node (&rip_node, NULL);
+  install_node (&interface_node, NULL);
+  install_node (&rmap_node, NULL);
+  install_node (&zebra_node, NULL);
+  install_node (&bgp_vpnv4_node, NULL);
+  install_node (&bgp_ipv4_node, NULL);
+  install_node (&bgp_ipv4m_node, NULL);
+  install_node (&bgp_ipv6_node, NULL);
+  install_node (&bgp_ipv6m_node, NULL);
+  install_node (&ospf_node, NULL);
+  install_node (&ripng_node, NULL);
+  install_node (&ospf6_node, NULL);
+  install_node (&babel_node, NULL);
+  install_node (&keychain_node, NULL);
+  install_node (&keychain_key_node, NULL);
+  install_node (&isis_node, NULL);
+  install_node (&vty_node, NULL);
+
+  test_init_cmd();
+
+  for (node = 0; node < vector_active(cmdvec); node++)
+    if ((cnode = vector_slot(cmdvec, node)) != NULL)
+      for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+        if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL)
+          {
+            cmd->daemon = 0;
+            cmd->func = test_callback;
+          }
+  test_load();
+  vty_init_vtysh();
+}
+
+static void
+test_terminate(void)
+{
+  unsigned int i;
+
+  vty_terminate();
+  for (i = 0; i < vector_active(test_cmds); i++)
+    XFREE(MTYPE_STRVEC, vector_slot(test_cmds, i));
+  vector_free(test_cmds);
+  cmd_terminate();
+}
+
+static void
+test_run(struct prng *prng, struct vty *vty, const char *cmd, unsigned int edit_dist, unsigned int node_index, int verbose)
+{
+  const char *test_str;
+  vector vline;
+  int ret;
+  unsigned int i;
+  char **completions;
+  unsigned int j;
+  struct cmd_node *cnode;
+  vector descriptions;
+  int appended_null;
+  int no_match;
+
+  test_str = prng_fuzz(prng, cmd, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /", edit_dist);
+  vline = cmd_make_strvec(test_str);
+
+  if (vline == NULL)
+    return;
+
+  appended_null = 0;
+  for (i = 0; i < vector_active(cmdvec); i++)
+    if ((cnode = vector_slot(cmdvec, i)) != NULL)
+      {
+        if (node_index != (unsigned int)-1 && i != node_index)
+          continue;
+
+        if (appended_null)
+          {
+            vector_unset(vline, vector_active(vline) - 1);
+            appended_null = 0;
+          }
+        vty->node = cnode->node;
+        test_buf[0] = '\0';
+        ret = cmd_execute_command(vline, vty, NULL, 0);
+        no_match = (ret == CMD_ERR_NO_MATCH);
+        if (verbose || !no_match)
+          printf("execute relaxed '%s'@%d: rv==%d%s%s\n",
+                 test_str,
+                 cnode->node,
+                 ret,
+                 (test_buf[0] != '\0') ? ", " : "",
+                 test_buf);
+
+        vty->node = cnode->node;
+        test_buf[0] = '\0';
+        ret = cmd_execute_command_strict(vline, vty, NULL);
+        if (verbose || !no_match)
+          printf("execute strict '%s'@%d: rv==%d%s%s\n",
+                 test_str,
+                 cnode->node,
+                 ret,
+                 (test_buf[0] != '\0') ? ", " : "",
+                 test_buf);
+
+        if (isspace((int) test_str[strlen(test_str) - 1]))
+          {
+            vector_set (vline, NULL);
+            appended_null = 1;
+          }
+
+        vty->node = cnode->node;
+        completions = cmd_complete_command(vline, vty, &ret);
+        if (verbose || !no_match)
+          printf("complete '%s'@%d: rv==%d\n",
+                 test_str,
+                 cnode->node,
+                 ret);
+        if (completions != NULL)
+          {
+            for (j = 0; completions[j] != NULL; j++)
+              {
+                printf("  '%s'\n", completions[j]);
+                XFREE(MTYPE_TMP, completions[j]);
+              }
+            XFREE(MTYPE_VECTOR_INDEX, completions);
+          }
+
+        vty->node = cnode->node;
+        descriptions = cmd_describe_command(vline, vty, &ret);
+        if (verbose || !no_match)
+          printf("describe '%s'@%d: rv==%d\n",
+                 test_str,
+                 cnode->node,
+                 ret);
+        if (descriptions != NULL)
+          {
+            for (j = 0; j < vector_active(descriptions); j++)
+              {
+                struct cmd_token *cmd = vector_slot(descriptions, j);
+                printf("  '%s' '%s'\n", cmd->cmd, cmd->desc);
+              }
+            vector_free(descriptions);
+          }
+      }
+  cmd_free_strvec(vline);
+}
+
+int
+main(int argc, char **argv)
+{
+  int opt;
+  struct prng *prng;
+  struct vty *vty;
+  unsigned int edit_distance;
+  unsigned int max_edit_distance;
+  unsigned int node_index;
+  int verbose;
+  unsigned int test_cmd;
+  unsigned int iteration;
+  unsigned int num_iterations;
+
+  max_edit_distance = 3;
+  node_index = -1;
+  verbose = 0;
+
+  while ((opt = getopt(argc, argv, "e:n:v")) != -1)
+    {
+      switch (opt)
+        {
+        case 'e':
+          max_edit_distance = atoi(optarg);
+          break;
+        case 'n':
+          node_index = atoi(optarg);
+          break;
+        case 'v':
+          verbose++;
+          break;
+        default:
+          fprintf(stderr, "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n", argv[0]);
+          exit(1);
+          break;
+        }
+    }
+
+  test_init();
+  prng = prng_new(0);
+
+  vty = vty_new();
+  vty->type = VTY_TERM;
+
+  fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds));
+  for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++)
+    {
+      for (edit_distance = 0;
+           edit_distance <= max_edit_distance;
+           edit_distance++)
+        {
+          num_iterations = 1 << edit_distance;
+          num_iterations *= num_iterations * num_iterations;
+
+          for (iteration = 0; iteration < num_iterations; iteration++)
+            test_run(prng, vty, vector_slot(test_cmds, test_cmd), edit_distance, node_index, verbose);
+        }
+      fprintf(stderr, "\r%u/%u", test_cmd + 1, vector_active(test_cmds));
+    }
+  fprintf(stderr, "\nDone.\n");
+
+  vty_close(vty);
+  prng_free(prng);
+  test_terminate();
+  return 0;
+}
diff --git a/tests/test-memory.c b/tests/test-memory.c
new file mode 100644 (file)
index 0000000..807249e
--- /dev/null
@@ -0,0 +1,122 @@
+/* 
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <memory.h>
+
+/* Memory torture tests
+ *
+ * Tests below are generic but comments are focused on interaction with
+ * Paul's proposed memory 'quick' cache, which may never be included in
+ * CVS
+ */
+
+struct thread_master *master;
+
+#if 0 /* set to 1 to use system alloc directly */
+#undef XMALLOC
+#undef XCALLOC
+#undef XREALLOC
+#undef XFREE
+#define XMALLOC(T,S) malloc((S))
+#define XCALLOC(T,S) calloc(1, (S))
+#define XREALLOC(T,P,S) realloc((P),(S))
+#define XFREE(T,P) free((P))
+#endif
+
+#define TIMES 10
+
+int
+main(int argc, char **argv)
+{
+  void *a[10];
+  int i;
+
+  printf ("malloc x, malloc x, free, malloc x, free free\n\n");
+  /* simple case, test cache */
+  for (i = 0; i < TIMES; i++)
+    {
+      a[0] = XMALLOC (MTYPE_VTY, 1024);
+      memset (a[0], 1, 1024);
+      a[1] = XMALLOC (MTYPE_VTY, 1024);
+      memset (a[1], 1, 1024);
+      XFREE(MTYPE_VTY, a[0]); /* should go to cache */
+      a[0] = XMALLOC (MTYPE_VTY, 1024); /* should be satisfied from cache */
+      XFREE(MTYPE_VTY, a[0]);
+      XFREE(MTYPE_VTY, a[1]);
+    }
+  
+  printf ("malloc x, malloc y, free x, malloc y, free free\n\n");
+  /* cache should go invalid, valid, invalid, etc.. */
+  for (i = 0; i < TIMES; i++)
+    {
+      a[0] = XMALLOC (MTYPE_VTY, 512);
+      memset (a[0], 1, 512);
+      a[1] = XMALLOC (MTYPE_VTY, 1024); /* invalidate cache */
+      memset (a[1], 1, 1024);
+      XFREE(MTYPE_VTY, a[0]);
+      a[0] = XMALLOC (MTYPE_VTY, 1024);
+      XFREE(MTYPE_VTY, a[0]);
+      XFREE(MTYPE_VTY, a[1]);
+      /* cache should become valid again on next request */
+    }
+
+  printf ("calloc\n\n");
+  /* test calloc */
+  for (i = 0; i < TIMES; i++)
+    {
+      a[0] = XCALLOC (MTYPE_VTY, 1024);
+      memset (a[0], 1, 1024);
+      a[1] = XCALLOC (MTYPE_VTY, 512); /* invalidate cache */
+      memset (a[1], 1, 512);
+      XFREE(MTYPE_VTY, a[1]);
+      XFREE(MTYPE_VTY, a[0]);
+      /* alloc == 0, cache can become valid again on next request */
+    }
+  
+  printf ("calloc and realloc\n\n");
+  /* check calloc + realloc */
+  for (i = 0; i < TIMES; i++)
+    {
+      printf ("calloc a0 1024\n");
+      a[0] = XCALLOC (MTYPE_VTY, 1024);
+      memset (a[0], 1, 1024/2);
+      
+      printf ("calloc 1 1024\n");
+      a[1] = XCALLOC (MTYPE_VTY, 1024);
+      memset (a[1], 1, 1024/2);
+      
+      printf ("realloc 0 1024\n");
+      a[3] = XREALLOC (MTYPE_VTY, a[0], 2048); /* invalidate cache */
+      if (a[3] != NULL)
+        a[0] = a[3];
+      memset (a[0], 1, 1024);
+      
+      printf ("calloc 2 512\n");
+      a[2] = XCALLOC (MTYPE_VTY, 512);
+      memset (a[2], 1, 512);
+      
+      printf ("free 1 0 2\n");
+      XFREE(MTYPE_VTY, a[1]);
+      XFREE(MTYPE_VTY, a[0]);
+      XFREE(MTYPE_VTY, a[2]);
+      /* alloc == 0, cache valid next request */
+    }
+  return 0;
+}
diff --git a/tests/test-nexthop-iter.c b/tests/test-nexthop-iter.c
new file mode 100644 (file)
index 0000000..2503793
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Recursive Nexthop Iterator test.
+ * This tests the ALL_NEXTHOPS_RO macro.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "prng.h"
+
+struct thread_master *master;
+static int verbose;
+
+static void
+str_append(char **buf, const char *repr)
+{
+  if (*buf)
+    {
+      *buf = realloc(*buf, strlen(*buf) + strlen(repr) + 1);
+      assert(*buf);
+      strncpy((*buf) + strlen(*buf), repr, strlen(repr) + 1);
+    }
+  else
+    {
+      *buf = strdup(repr);
+      assert(*buf);
+    }
+}
+
+static void
+str_appendf(char **buf, const char *format, ...)
+{
+  va_list ap;
+  int rv;
+  char *pbuf;
+
+  va_start(ap, format);
+  rv = vasprintf(&pbuf, format, ap);
+  va_end(ap);
+  assert(rv >= 0);
+
+  str_append(buf, pbuf);
+  free(pbuf);
+}
+
+/* This structure contains a nexthop chain
+ * and its expected representation */
+struct nexthop_chain
+{
+  /* Head of the chain */
+  struct nexthop *head;
+  /* Last nexthop in top chain */
+  struct nexthop *current_top;
+  /* Last nexthop in current recursive chain */
+  struct nexthop *current_recursive;
+  /* Expected string representation. */
+  char *repr;
+};
+
+static struct nexthop_chain*
+nexthop_chain_new(void)
+{
+  struct nexthop_chain *rv;
+
+  rv = calloc(sizeof(*rv), 1);
+  assert(rv);
+  return rv;
+}
+
+static void
+nexthop_chain_add_top(struct nexthop_chain *nc)
+{
+  struct nexthop *nh;
+
+  nh = calloc(sizeof(*nh), 1);
+  assert(nh);
+
+  if (nc->head)
+    {
+      nc->current_top->next = nh;
+      nh->prev = nc->current_top;
+      nc->current_top = nh;
+    }
+  else
+    {
+      nc->head = nc->current_top = nh;
+    }
+  nc->current_recursive = NULL;
+  str_appendf(&nc->repr, "%p\n", nh);
+}
+
+static void
+nexthop_chain_add_recursive(struct nexthop_chain *nc)
+{
+  struct nexthop *nh;
+
+  nh = calloc(sizeof(*nh), 1);
+  assert(nh);
+
+  assert(nc->current_top);
+  if (nc->current_recursive)
+    {
+      nc->current_recursive->next = nh;
+      nh->prev = nc->current_recursive;
+      nc->current_recursive = nh;
+    }
+  else
+    {
+      SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE);
+      nc->current_top->resolved = nh;
+      nc->current_recursive = nh;
+    }
+  str_appendf(&nc->repr, "  %p\n", nh);
+}
+
+static void
+nexthop_chain_clear(struct nexthop_chain *nc)
+{
+  struct nexthop *tcur, *tnext;
+
+  for (tcur = nc->head; tcur; tcur = tnext)
+    {
+      tnext = tcur->next;
+      if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE))
+        {
+          struct nexthop *rcur, *rnext;
+          for (rcur = tcur->resolved; rcur; rcur = rnext)
+            {
+              rnext = rcur->next;
+              free(rcur);
+            }
+        }
+      free(tcur);
+    }
+  nc->head = nc->current_top = nc->current_recursive = NULL;
+  free(nc->repr);
+  nc->repr = NULL;
+}
+
+static void
+nexthop_chain_free(struct nexthop_chain *nc)
+{
+  if (!nc)
+    return;
+  nexthop_chain_clear(nc);
+  free(nc);
+}
+
+/* This function builds a string representation of
+ * the nexthop chain using the ALL_NEXTHOPS_RO macro.
+ * It verifies that the ALL_NEXTHOPS_RO macro iterated
+ * correctly over the nexthop chain by comparing the
+ * generated representation with the expected representation.
+ */
+static void
+nexthop_chain_verify_iter(struct nexthop_chain *nc)
+{
+  struct nexthop *nh, *tnh;
+  int recursing;
+  char *repr = NULL;
+
+  for (ALL_NEXTHOPS_RO(nc->head, nh, tnh, recursing))
+    {
+      if (recursing)
+        str_appendf(&repr, "  %p\n", nh);
+      else
+        str_appendf(&repr, "%p\n", nh);
+    }
+
+  if (repr && verbose)
+    printf("===\n%s", repr);
+  assert((!repr && !nc->repr) || (repr && nc->repr && !strcmp(repr, nc->repr)));
+  free(repr);
+}
+
+/* This test run builds a simple nexthop chain
+ * with some recursive nexthops and verifies that
+ * the iterator works correctly in each stage along
+ * the way.
+ */
+static void
+test_run_first(void)
+{
+  struct nexthop_chain *nc;
+
+  nc = nexthop_chain_new();
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_top(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_top(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_recursive(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_recursive(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_top(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_top(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_top(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_recursive(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_recursive(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_add_recursive(nc);
+  nexthop_chain_verify_iter(nc);
+
+  nexthop_chain_free(nc);
+}
+
+/* This test run builds numerous random
+ * nexthop chain configurations and verifies
+ * that the iterator correctly progresses
+ * through each. */
+static void
+test_run_prng(void)
+{
+  struct nexthop_chain *nc;
+  struct prng *prng;
+  int i;
+
+  nc = nexthop_chain_new();
+  prng = prng_new(0);
+
+  for (i = 0; i < 1000000; i++)
+    {
+      switch (prng_rand(prng) % 10)
+        {
+        case 0:
+          nexthop_chain_clear(nc);
+          break;
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+          nexthop_chain_add_top(nc);
+          break;
+        case 6:
+        case 7:
+        case 8:
+        case 9:
+          if (nc->current_top)
+            nexthop_chain_add_recursive(nc);
+          break;
+        }
+      nexthop_chain_verify_iter(nc);
+    }
+  nexthop_chain_free(nc);
+  prng_free(prng);
+}
+
+int main(int argc, char **argv)
+{
+  if (argc >= 2 && !strcmp("-v", argv[1]))
+    verbose = 1;
+  test_run_first();
+  printf("Simple test passed.\n");
+  test_run_prng();
+  printf("PRNG test passed.\n");
+}
diff --git a/tests/test-privs.c b/tests/test-privs.c
new file mode 100644 (file)
index 0000000..beae81f
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * $Id: test-privs.c,v 1.1 2005/10/11 03:48:28 paul Exp $
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "privs.h"
+#include "memory.h"
+
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_RAW,
+  ZCAP_BIND,
+  ZCAP_NET_ADMIN,
+  ZCAP_DAC_OVERRIDE,
+};
+
+struct zebra_privs_t test_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#if defined(VTY_GROUP)
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]),
+  .cap_num_i = 0
+};
+
+struct option longopts[] = 
+{
+  { "help",        no_argument,       NULL, 'h'},
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { 0 }
+};
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\
+Daemon which does 'slow' things.\n\n\
+-u, --user         User to run as\n\
+-g, --group        Group to run as\n\
+-h, --help         Display this help and exit\n\
+\n\
+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+  exit (status);
+}
+
+struct thread_master *master;
+/* main routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  char *progname;
+  struct zprivs_ids_t ids;
+  
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* get program name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  while (1) 
+    {
+      int opt;
+
+      opt = getopt_long (argc, argv, "hu:g:", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+        case 'u':
+          test_privs.user = optarg;
+          break;
+        case 'g':
+          test_privs.group = optarg;
+          break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Library inits. */
+  memory_init ();
+  zprivs_init (&test_privs);
+
+#define PRIV_STATE() \
+  ((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")
+  
+  printf ("%s\n", PRIV_STATE());
+  test_privs.change(ZPRIVS_RAISE);
+  
+  printf ("%s\n", PRIV_STATE());
+  test_privs.change(ZPRIVS_LOWER);
+  
+  printf ("%s\n", PRIV_STATE());
+  zprivs_get_ids (&ids);  
+  
+  /* terminate privileges */
+  zprivs_terminate(&test_privs);
+  
+  /* but these should continue to work... */
+  printf ("%s\n", PRIV_STATE());
+  test_privs.change(ZPRIVS_RAISE);
+  
+  printf ("%s\n", PRIV_STATE());
+  test_privs.change(ZPRIVS_LOWER);
+  
+  printf ("%s\n", PRIV_STATE());
+  zprivs_get_ids (&ids);  
+  
+  printf ("terminating\n");
+  return 0;
+}
diff --git a/tests/test-segv.c b/tests/test-segv.c
new file mode 100644 (file)
index 0000000..1b851fc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SEGV / backtrace handling test.
+ *
+ * copied from test-sig.c
+ *
+ * Copyright (C) 2013 by David Lamparter, Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include "lib/log.h"
+#include "lib/memory.h"
+
+struct quagga_signal_t sigs[] = 
+{
+};
+
+struct thread_master *master;
+
+static int
+threadfunc (struct thread *thread)
+{
+  int *null = NULL;
+  *null += 1;
+  return 0;
+}
+
+int
+main (void)
+{
+  master = thread_master_create ();
+  signal_init (master, array_size(sigs), sigs);
+
+  zlog_default = openzlog("testsegv", ZLOG_NONE,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+
+  thread_execute (master, threadfunc, 0, 0);
+
+  exit (0);
+}
diff --git a/tests/test-sig.c b/tests/test-sig.c
new file mode 100644 (file)
index 0000000..f24da96
--- /dev/null
@@ -0,0 +1,78 @@
+/* 
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include "lib/log.h"
+#include "lib/memory.h"
+
+static void
+sighup (void)
+{
+  printf ("processed hup\n");
+}
+
+static void
+sigusr1 (void)
+{
+  printf ("processed usr1\n");
+}
+
+static void
+sigusr2 (void)
+{
+  printf ("processed usr2\n");
+}
+
+struct quagga_signal_t sigs[] = 
+{
+  {
+    .signal = SIGHUP,
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGUSR2,
+    .handler = &sigusr2,
+  }
+};
+
+struct thread_master *master;
+struct thread t;
+
+int
+main (void)
+{
+  master = thread_master_create ();
+  signal_init (master, array_size(sigs), sigs);
+  
+  zlog_default = openzlog("testsig", ZLOG_NONE,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+  zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
+  zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+  
+  while (thread_fetch (master, &t))
+    thread_call (&t);
+
+  exit (0);
+}
diff --git a/tests/test-stream.c b/tests/test-stream.c
new file mode 100644 (file)
index 0000000..7ef6374
--- /dev/null
@@ -0,0 +1,76 @@
+/* Simple stream test.
+ * 
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <stream.h>
+#include <thread.h>
+
+static unsigned long long ham = 0xdeadbeefdeadbeef;
+struct thread_master *master;
+
+static void
+print_stream (struct stream *s)
+{
+  size_t getp = stream_get_getp (s);
+  
+  printf ("endp: %zu, readable: %zu, writeable: %zu\n",
+          stream_get_endp (s),
+          STREAM_READABLE (s),
+          STREAM_WRITEABLE (s));
+  
+  while (STREAM_READABLE (s))
+    {
+      printf ("0x%x ", *stream_pnt (s));
+      stream_forward_getp (s, 1);
+    }
+  
+  printf ("\n");
+  
+  /* put getp back to where it was */
+  stream_set_getp (s, getp);
+}
+
+int
+main (void)
+{
+  struct stream *s;
+  
+  s = stream_new (1024);
+  
+  stream_putc (s, ham);
+  stream_putw (s, ham);
+  stream_putl (s, ham);
+  stream_putq (s, ham);
+  
+  print_stream (s);
+  
+  stream_resize (s, stream_get_endp (s));
+  
+  print_stream (s);
+  
+  printf ("c: 0x%hhx\n", stream_getc (s));
+  printf ("w: 0x%hx\n", stream_getw (s));
+  printf ("l: 0x%x\n", stream_getl (s));
+  printf ("q: 0x%" PRIu64 "\n", stream_getq (s));
+  
+  return 0;
+}
diff --git a/tests/test-timer-correctness.c b/tests/test-timer-correctness.c
new file mode 100644 (file)
index 0000000..e523929
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Test program to verify that scheduled timers are executed in the
+ * correct order.
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "memory.h"
+#include "pqueue.h"
+#include "prng.h"
+#include "thread.h"
+
+#define SCHEDULE_TIMERS 800
+#define REMOVE_TIMERS   200
+
+#define TIMESTR_LEN strlen("4294967296.999999")
+
+struct thread_master *master;
+
+static size_t log_buf_len;
+static size_t log_buf_pos;
+static char *log_buf;
+
+static size_t expected_buf_len;
+static size_t expected_buf_pos;
+static char *expected_buf;
+
+static struct prng *prng;
+
+static struct thread **timers;
+
+static int timers_pending;
+
+static void terminate_test(void)
+{
+  int exit_code;
+
+  if (strcmp(log_buf, expected_buf))
+    {
+      fprintf(stderr, "Expected output and received output differ.\n");
+      fprintf(stderr, "---Expected output: ---\n%s", expected_buf);
+      fprintf(stderr, "---Actual output: ---\n%s", log_buf);
+      exit_code = 1;
+    }
+  else
+    {
+      printf("Expected output and actual output match.\n");
+      exit_code = 0;
+    }
+
+  thread_master_free(master);
+  XFREE(MTYPE_TMP, log_buf);
+  XFREE(MTYPE_TMP, expected_buf);
+  prng_free(prng);
+  XFREE(MTYPE_TMP, timers);
+
+  exit(exit_code);
+}
+
+static int timer_func(struct thread *thread)
+{
+  int rv;
+
+  rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos,
+                "%s\n", (char*)thread->arg);
+  assert(rv >= 0);
+  log_buf_pos += rv;
+  assert(log_buf_pos < log_buf_len);
+  XFREE(MTYPE_TMP, thread->arg);
+
+  timers_pending--;
+  if (!timers_pending)
+    terminate_test();
+
+  return 0;
+}
+
+static int cmp_timeval(const void* a, const void *b)
+{
+  const struct timeval *ta = *(struct timeval * const *)a;
+  const struct timeval *tb = *(struct timeval * const *)b;
+
+  if (timercmp(ta, tb, <))
+    return -1;
+  if (timercmp(ta, tb, >))
+    return 1;
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  int i, j;
+  struct thread t;
+  struct timeval **alarms;
+
+  master = thread_master_create();
+
+  log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1;
+  log_buf_pos = 0;
+  log_buf = XMALLOC(MTYPE_TMP, log_buf_len);
+
+  expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1;
+  expected_buf_pos = 0;
+  expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len);
+
+  prng = prng_new(0);
+
+  timers = XMALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers));
+
+  for (i = 0; i < SCHEDULE_TIMERS; i++)
+    {
+      long interval_msec;
+      int ret;
+      char *arg;
+
+      /* Schedule timers to expire in 0..5 seconds */
+      interval_msec = prng_rand(prng) % 5000;
+      arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1);
+      timers[i] = thread_add_timer_msec(master, timer_func, arg, interval_msec);
+      ret = snprintf(arg, TIMESTR_LEN + 1, "%lld.%06lld",
+                     (long long)timers[i]->u.sands.tv_sec,
+                     (long long)timers[i]->u.sands.tv_usec);
+      assert(ret > 0);
+      assert((size_t)ret < TIMESTR_LEN + 1);
+      timers_pending++;
+    }
+
+  for (i = 0; i < REMOVE_TIMERS; i++)
+    {
+      int index;
+
+      index = prng_rand(prng) % SCHEDULE_TIMERS;
+      if (!timers[index])
+        continue;
+
+      XFREE(MTYPE_TMP, timers[index]->arg);
+      thread_cancel(timers[index]);
+      timers[index] = NULL;
+      timers_pending--;
+    }
+
+  /* We create an array of pointers to the alarm times and sort
+   * that array. That sorted array is used to generate a string
+   * representing the expected "output" of the timers when they
+   * are run. */
+  j = 0;
+  alarms = XMALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms));
+  for (i = 0; i < SCHEDULE_TIMERS; i++)
+    {
+      if (!timers[i])
+        continue;
+      alarms[j++] = &timers[i]->u.sands;
+    }
+  qsort(alarms, j, sizeof(*alarms), cmp_timeval);
+  for (i = 0; i < j; i++)
+    {
+      int ret;
+
+      ret = snprintf(expected_buf + expected_buf_pos,
+                     expected_buf_len - expected_buf_pos,
+                     "%lld.%06lld\n",
+                     (long long)alarms[i]->tv_sec,
+                     (long long)alarms[i]->tv_usec);
+      assert(ret > 0);
+      expected_buf_pos += ret;
+      assert(expected_buf_pos < expected_buf_len);
+    }
+  XFREE(MTYPE_TMP, alarms);
+
+  while (thread_fetch(master, &t))
+    thread_call(&t);
+
+  return 0;
+}
diff --git a/tests/test-timer-performance.c b/tests/test-timer-performance.c
new file mode 100644 (file)
index 0000000..ee45ede
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Test program which measures the time it takes to schedule and
+ * remove timers.
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "thread.h"
+#include "pqueue.h"
+#include "prng.h"
+
+#define SCHEDULE_TIMERS 1000000
+#define REMOVE_TIMERS    500000
+
+struct thread_master *master;
+
+static int dummy_func(struct thread *thread)
+{
+  return 0;
+}
+
+int main(int argc, char **argv)
+{
+  struct prng *prng;
+  int i;
+  struct thread **timers;
+  struct timeval tv_start, tv_lap, tv_stop;
+  unsigned long t_schedule, t_remove;
+
+  master = thread_master_create();
+  prng = prng_new(0);
+  timers = calloc(SCHEDULE_TIMERS, sizeof(*timers));
+
+  /* create thread structures so they won't be allocated during the
+   * time measurement */
+  for (i = 0; i < SCHEDULE_TIMERS; i++)
+    timers[i] = thread_add_timer_msec(master, dummy_func, NULL, 0);
+  for (i = 0; i < SCHEDULE_TIMERS; i++)
+    thread_cancel(timers[i]);
+
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_start);
+
+  for (i = 0; i < SCHEDULE_TIMERS; i++)
+    {
+      long interval_msec;
+
+      interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS);
+      timers[i] = thread_add_timer_msec(master, dummy_func,
+                                        NULL, interval_msec);
+    }
+
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_lap);
+
+  for (i = 0; i < REMOVE_TIMERS; i++)
+    {
+      int index;
+
+      index = prng_rand(prng) % SCHEDULE_TIMERS;
+      if (timers[index])
+        thread_cancel(timers[index]);
+      timers[index] = NULL;
+    }
+
+  quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_stop);
+
+  t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec);
+  t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000;
+
+  t_remove = 1000 * (tv_stop.tv_sec - tv_lap.tv_sec);
+  t_remove += (tv_stop.tv_usec - tv_lap.tv_usec) / 1000;
+
+  printf("Scheduling %d random timers took %ld.%03ld seconds.\n",
+         SCHEDULE_TIMERS, t_schedule/1000, t_schedule%1000);
+  printf("Removing %d random timers took %ld.%03ld seconds.\n",
+         REMOVE_TIMERS, t_remove/1000, t_remove%1000);
+  fflush(stdout);
+
+  free(timers);
+  thread_master_free(master);
+  prng_free(prng);
+  return 0;
+}
diff --git a/tests/testcli.in b/tests/testcli.in
new file mode 100644 (file)
index 0000000..f4212b9
--- /dev/null
@@ -0,0 +1,93 @@
+echo this is a  test message
+echo  foo bla  ?  baz
+echo
+
+arg ipv4 1.2.3.4
+arg ipv4 1.2.?3.4
+arg ipv4 1.2.3
+arg ipv4 1.2.3.4.5
+arg ipv4 1.a.3.4
+arg ipv4 blah
+
+arg ipv4m 1.2.3.0/24
+arg ipv4m 1.2.?3.0/24
+arg ipv4m 1.2.3/9
+arg ipv4m 1.2.3.4.5/6
+arg ipv4m 1.a.3.4
+arg ipv4m blah
+arg ipv4m 1.2.3.0/999
+arg ipv4m 1.2.3.0/a9
+arg ipv4m 1.2.3.0/9a
+
+arg ipv6 de4d:b33f::cafe
+arg ipv6 de4d:b3?3f::caf?e
+arg ipv6 de4d:b3       3f::caf?e
+arg ipv6 de4d:b33f:z::cafe
+arg ipv6 de4d:b33f:cafe:
+arg ipv6 ::
+arg ipv6 ::/
+arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0
+arg ipv6 12::34::56
+arg ipv6m dead:beef:cafe::/64
+arg ipv6m dead:be?ef:cafe:?:/64
+
+arg range 4
+arg range 5
+arg range 9?
+arg range 15
+arg range 16
+arg range -1
+arg range 99999999999999999999999999999999999999999
+
+arg ?
+
+pa     
+pat    
+
+pat a
+pat a a
+pat a ?b
+pat a c?
+pat a a x
+
+pat b
+pat b ?a
+pat b x
+pat b x y
+
+pat c a
+pat c a 1.2.3.4
+pat c b 2.3.4
+pat c c ?x
+
+pat d
+pat d  
+pat d foo 1.2.3.4
+pat d foo
+pat d noooo
+pat d bar 1::2
+pat d bar 1::2 foo 3.4.5.6
+pat d ba?z
+pat d foo 3.4.5.6 baz
+
+pat e
+pat e f
+pat e f g
+pat e 1.2.3.4
+
+pat f
+pat f foo
+pat f key
+
+alt a  a?b
+alt a 1        .2?.3.4
+alt a 1        :2?     ::?3
+
+conf t
+do pat d baz
+exit
+
+show run
+conf t
+hostname foohost
+do show run
diff --git a/tests/testcli.refout b/tests/testcli.refout
new file mode 100644 (file)
index 0000000..1515ea2
--- /dev/null
@@ -0,0 +1,290 @@
+test# echo this is a  test message\r
+this is a test message\r
+test# echo  foo bla  \r
+  MESSAGE  The message to echo\r
+  <cr>     \r
+test# echo  foo bla    baz\r
+foo bla baz\r
+test# echo\r
+% Command incomplete.\r
+test# \r
+test# arg ipv4 1.2.3.4\r
+cmd0 with 1 args.\r
+[00]: 1.2.3.4\r
+test# arg ipv4 1.2.\r
+  A.B.C.D  02\r
+test# arg ipv4 1.2.3.4\r
+cmd0 with 1 args.\r
+[00]: 1.2.3.4\r
+test# arg ipv4 1.2.3\r
+cmd0 with 1 args.\r
+[00]: 1.2.3\r
+test# arg ipv4 1.2.3.4.5\r
+% [NONE] Unknown command: arg ipv4 1.2.3.4.5\r
+test# arg ipv4 1.a.3.4\r
+% [NONE] Unknown command: arg ipv4 1.a.3.4\r
+test# arg ipv4 blah\r
+% [NONE] Unknown command: arg ipv4 blah\r
+test# \r
+test# arg ipv4m 1.2.3.0/24\r
+cmd1 with 1 args.\r
+[00]: 1.2.3.0/24\r
+test# arg ipv4m 1.2.\r
+  A.B.C.D/M  02\r
+test# arg ipv4m 1.2.3.0/24\r
+cmd1 with 1 args.\r
+[00]: 1.2.3.0/24\r
+test# arg ipv4m 1.2.3/9\r
+% [NONE] Unknown command: arg ipv4m 1.2.3/9\r
+test# arg ipv4m 1.2.3.4.5/6\r
+% [NONE] Unknown command: arg ipv4m 1.2.3.4.5/6\r
+test# arg ipv4m 1.a.3.4\r
+% [NONE] Unknown command: arg ipv4m 1.a.3.4\r
+test# arg ipv4m blah\r
+% [NONE] Unknown command: arg ipv4m blah\r
+test# arg ipv4m 1.2.3.0/999\r
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/999\r
+test# arg ipv4m 1.2.3.0/a9\r
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/a9\r
+test# arg ipv4m 1.2.3.0/9a\r
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/9a\r
+test# \r
+test# arg ipv6 de4d:b33f::cafe\r
+cmd2 with 1 args.\r
+[00]: de4d:b33f::cafe\r
+test# arg ipv6 de4d:b3\r
+% There is no matched command.\r
+test# arg ipv6 de4d:b33f::caf\r
+  X:X::X:X  02\r
+test# arg ipv6 de4d:b33f::cafe\r
+cmd2 with 1 args.\r
+[00]: de4d:b33f::cafe\r
+test# arg ipv6 de4d:b3\r
+test# arg ipv6 de4d:b33f::caf\r
+  X:X::X:X  02\r
+test# arg ipv6 de4d:b33f::cafe\r
+cmd2 with 1 args.\r
+[00]: de4d:b33f::cafe\r
+test# arg ipv6 de4d:b33f:z::cafe\r
+% [NONE] Unknown command: arg ipv6 de4d:b33f:z::cafe\r
+test# arg ipv6 de4d:b33f:cafe:\r
+% [NONE] Unknown command: arg ipv6 de4d:b33f:cafe:\r
+test# arg ipv6 ::\r
+cmd2 with 1 args.\r
+[00]: ::\r
+test# arg ipv6 ::/\r
+% [NONE] Unknown command: arg ipv6 ::/\r
+test# arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0\r
+% [NONE] Unknown command: arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0\r
+test# arg ipv6 12::34::56\r
+% [NONE] Unknown command: arg ipv6 12::34::56\r
+test# arg ipv6m dead:beef:cafe::/64\r
+cmd3 with 1 args.\r
+[00]: dead:beef:cafe::/64\r
+test# arg ipv6m dead:be\r
+  X:X::X:X/M  02\r
+test# arg ipv6m dead:beef:cafe:\r
+  X:X::X:X/M  02\r
+test# arg ipv6m dead:beef:cafe::/64\r
+cmd3 with 1 args.\r
+[00]: dead:beef:cafe::/64\r
+test# \r
+test# arg range 4\r
+% [NONE] Unknown command: arg range 4\r
+test# arg range 5\r
+cmd4 with 1 args.\r
+[00]: 5\r
+test# arg range 9\r
+  <5-15>  02\r
+test# arg range 9\r
+cmd4 with 1 args.\r
+[00]: 9\r
+test# arg range 15\r
+cmd4 with 1 args.\r
+[00]: 15\r
+test# arg range 16\r
+% [NONE] Unknown command: arg range 16\r
+test# arg range -1\r
+% [NONE] Unknown command: arg range -1\r
+test# arg range 99999999999999999999999999999999999999999\r
+% [NONE] Unknown command: arg range 99999999999999999999999999999999999999999\r
+test# \r
+test# arg \r
+  ipv4   01\r
+  ipv4m  01\r
+  ipv6   01\r
+  ipv6m  01\r
+  range  01\r
+test# arg \r
+% Command incomplete.\r
+test# \r
+test# pa\r
+test# pa\b\bpat \r
+% Command incomplete.\r
+test# pat \r
+a          b          c          d          e          f          \r
+test# pat \r
+% Command incomplete.\r
+test# \r
+test# pat a\r
+% Command incomplete.\r
+test# pat a a\r
+cmd5 with 1 args.\r
+[00]: a\r
+test# pat a \r
+  a  02\r
+  b  03\r
+test# pat a b\r
+cmd5 with 1 args.\r
+[00]: b\r
+test# pat a c\r
+% There is no matched command.\r
+test# pat a c\r
+% [NONE] Unknown command: pat a c\r
+test# pat a a x\r
+% [NONE] Unknown command: pat a a x\r
+test# \r
+test# pat b\r
+% Command incomplete.\r
+test# pat b \r
+  a  02\r
+test# pat b a\r
+cmd6 with 1 args.\r
+[00]: a\r
+test# pat b x\r
+% [NONE] Unknown command: pat b x\r
+test# pat b x y\r
+% [NONE] Unknown command: pat b x y\r
+test# \r
+test# pat c a\r
+% Command incomplete.\r
+test# pat c a 1.2.3.4\r
+cmd7 with 2 args.\r
+[00]: a\r
+[01]: 1.2.3.4\r
+test# pat c b 2.3.4\r
+cmd7 with 2 args.\r
+[00]: b\r
+[01]: 2.3.4\r
+test# pat c c \r
+  A.B.C.D  05\r
+test# pat c c x\r
+% [NONE] Unknown command: pat c c x\r
+test# \r
+test# pat d\r
+cmd8 with 3 args.\r
+[00]: (null)\r
+[01]: (null)\r
+[02]: (null)\r
+test# pat d \r
+bar        baz        foo        \r
+test# pat d \r
+cmd8 with 3 args.\r
+[00]: (null)\r
+[01]: (null)\r
+[02]: (null)\r
+test# pat d foo 1.2.3.4\r
+cmd8 with 3 args.\r
+[00]: 1.2.3.4\r
+[01]: (null)\r
+[02]: (null)\r
+test# pat d foo\r
+% Command incomplete.\r
+test# pat d noooo\r
+% [NONE] Unknown command: pat d noooo\r
+test# pat d bar 1::2\r
+cmd8 with 3 args.\r
+[00]: (null)\r
+[01]: 1::2\r
+[02]: (null)\r
+test# pat d bar 1::2 foo 3.4.5.6\r
+cmd8 with 3 args.\r
+[00]: 3.4.5.6\r
+[01]: 1::2\r
+[02]: (null)\r
+test# pat d ba\r
+  bar  04\r
+  baz  06\r
+test# pat d baz\r
+cmd8 with 3 args.\r
+[00]: (null)\r
+[01]: (null)\r
+[02]: baz\r
+test# pat d foo 3.4.5.6 baz\r
+cmd8 with 3 args.\r
+[00]: 3.4.5.6\r
+[01]: (null)\r
+[02]: baz\r
+test# \r
+test# pat e\r
+% Command incomplete.\r
+test# pat e f\r
+% Command incomplete.\r
+test# pat e f g\r
+% Command incomplete.\r
+test# pat e 1.2.3.4\r
+% Command incomplete.\r
+test# \r
+test# pat f\r
+cmd10 with 0 args.\r
+test# pat f foo\r
+cmd10 with 1 args.\r
+[00]: foo\r
+test# pat f key\r
+cmd10 with 1 args.\r
+[00]: key\r
+test# \r
+test# alt a \r
+test# alt a a\r
+  WORD  02\r
+test# alt a ab\r
+cmd11 with 1 args.\r
+[00]: ab\r
+test# alt a 1\r
+test# alt a 1.2\r
+  A.B.C.D  02\r
+  WORD     02\r
+test# alt a 1.2.3.4\r
+cmd12 with 1 args.\r
+[00]: 1.2.3.4\r
+test# alt a 1\r
+test# alt a 1:2\r
+  WORD  02\r
+test# alt a 1:2\r
+test# alt a 1:2::\r
+  WORD      02\r
+  X:X::X:X  02\r
+test# alt a 1:2::3\r
+cmd13 with 1 args.\r
+[00]: 1:2::3\r
+test# \r
+test# conf t\r
+test(config)# do pat d baz\r
+cmd8 with 3 args.\r
+[00]: (null)\r
+[01]: (null)\r
+[02]: baz\r
+test(config)# exit\r
+test# \r
+test# show run\r
+\r
+Current configuration:\r
+!\r
+hostname test\r
+!\r
+line vty\r
+!\r
+end\r
+test# conf t\r
+test(config)# hostname foohost\r
+foohost(config)# do show run\r
+\r
+Current configuration:\r
+!\r
+hostname foohost\r
+!\r
+line vty\r
+!\r
+end\r
+foohost(config)# 
+end.
diff --git a/tests/testcommands.in b/tests/testcommands.in
new file mode 100644 (file)
index 0000000..7fe6279
--- /dev/null
@@ -0,0 +1,216 @@
+#
+#
+# Some randomly chosen valid commands
+#
+#
+area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE
+area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1
+area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1
+clear bgp 1 out
+clear bgp ipv6 2001:db8::1 out
+clear bgp view VARIABLE * soft
+clear ip bgp 1.2.3.4 ipv4 multicast out
+ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig
+ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1
+network 1.0.0.0/8 area 0
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay
+no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval
+no bgp graceful-restart
+no ipv6 nd mtu 1
+no neighbor 1.2.3.4 distribute-list 1 in
+no neighbor 2001:db8::1 send-community both
+no neighbor VARIABLE maximum-prefix
+redistribute isis route-map VARIABLE metric 0 metric-type 2
+redistribute rip metric 0 route-map VARIABLE metric-type 1
+show bgp community VARIABLE local-AS no-export VARIABLE exact-match
+show bgp ipv6 community no-advertise no-export no-export no-export
+show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match
+show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match
+show bgp view VARIABLE
+show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE
+show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS
+show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise
+show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE
+show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS
+show ip bgp community no-advertise local-AS no-advertise VARIABLE
+show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match
+show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match
+show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match
+show ipv6 bgp community no-export no-export VARIABLE VARIABLE
+show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match
+show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match
+show ipv6 mbgp community local-AS local-AS no-export no-export exact-match
+show ipv6 mbgp community no-export no-export local-AS no-export exact-match
+show ipv6 ospf6 database as-external dump
+show ipv6 ospf6 database inter-prefix 1.2.3.4 detail
+show ipv6 ospf6 database intra-prefix 1.2.3.4 internal
+#
+#
+# Slightly Fuzzed commands
+#
+#
+a8ra 0 range 1.0.0.0/8 adverOise
+accept-lifetime VARIABE 1 VA6IABLE 19I3 VARIABLE 1 VARIABLE 1993
+arAea 1.2.M.4 virtual-link 1.2.3.4 dead-interval 1 dead-interval 1 dead-inter6val 1 transmit-delay 1
+area 0 virtu0al-link 1.2.3.i hello-interval 1 ello-interval 1 transmit-delay 1 retransmit-interval 1
+area 0 virtual-lin 1.2.3.4 retransmit-interval 1 tranwmit-delay 1 retransmit-interval 1 retransmit-interval 1
+area 0 virtual-link 1.2.3.4 retransmit-interal 1 trasmit-dely 1
+area 1.2.3.4 virtual-link 1.2.3.4 deadCinterval 1  dead-intervalK 1 retransmit-interval 1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 dead-intervalo I1 dead-interval 1 retransmit-interval1 dead-interval 1
+area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1
+area 1.2.3.4 virtuyl-link 1.2.3.4 dead-interval 1 dead-inervalI 1 retransmit-interval 1 dead-interval 1
+area 1.2.3.4 virual-link 1.2.34 retransmit-interval 1 dead-interval 1 dead-interva 1
+area1.2.83.4 virtual-link 1.2.3.4 retra0smit-interval 1 dead-interval 1 dead-interval 1
+clear bgAp 2001g:dbK::1
+clear ip bgp 1.2.3.4 pv4 mlticat out
+cleau bg i2001:db8::1 rsclient
+de:ug ospf6 messag2 lsreq :recv
+how ip bgp communiQy no-advertise no-adve:tise no-advertise
+ip route 1.0Q0.0/8 1.2.3.s4 reGject
+ipv6 nd prefix 2O01:db8::/32 0 infinEite off-link
+ipv6 nwd prefix 2001:db8::/32 0 infinite oUUff-link
+ipv6 route 2001:db8::/32q2001:db8:k: blackhole 1
+kshow ip rIute bgp
+matcch peer .2.30.4
+mcogin
+mhow ipv6 mbgp community o-advertise yocal-AS no-advertise
+neighbor1.2..4 attribute-unchnged next-hop
+neihbcr 2001:d b8::1 distribute-list 1 in
+nko key-tqring
+no area 0 viertual-link 1.2.3k.4 retransmit-iterval retransmit-interval retransmit-interval hello-interval
+no area 0 virtual-link 1.2.3.4 dead-intaerval dead-intervIl hello-interval retransmit-interval
+no area 0 virtual-link 1.2.3.4 retransmit-interval retransmit-intervIl dead-interval tranImit-deqlay
+no area 0 virtual-link S1.2.3.4 d-ead-interval hello-interval transmit-deay transmit-delay
+no area 1.2.3.4 virtua -link 1.2.3.4 transmit-delay hello-interval hello-interval retransmt-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 dea-iterval retransmit-interva- dead-interval hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 hello-interSval dead-interval retransmit-interval transmitdelay
+no a:rea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interSvalW dead-interval retransmit-interval hello-interval
+noarea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval trynsmit-delay hello-interval
+no area 1.2.3.4 virtual-link 1.2.3.4 transmt:delay retransmit-interval retransmit-interval dead-Mnterval
+no ares 1.2.3.4 virtual-link 1.2.3.4 dead-interval retransmit-interval dead-inesval retransmit-interval
+no ayrea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval transmi-delay hello-interval
+no bg2 grace2fuy-restart
+no debug ospk6 nter2face
+noimatch ipv6 addrMss VARIABLE
+nomStch iA next-hop prefix-list
+no neighbCr 200 :db8::1oroute-map VARIABLE export
+no neighbor VARIABLE attributeaw8changed next-hop
+no orea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval ead-interval retransmit-inteSval hello-interval
+no ospcdead-inkerval
+no redistribute kernelrote-map VARIABLE metric 0
+no redistribute s4taik metric 0
+nos Ceighbor 1.2.3.4 route-mapEVARIABLE in
+o :neighbor VAIABLE attribute-unchanged next-hop
+ooa router ip
+redistribute isis meGtric-type2 Q route-map VARIABLE
+redistribute static metric-type 1 metri 0 rowute-map VARIABLE
+set-Koveroadbit
+sh2w ipv6 mbgp  comAunity VARIABLE
+shgw bgp ipv6 community no-export VARIABLE no-xport no-expmrt
+shiow Wgp neighbors
+shoAw ip bgpipv4 unicast com6munity no-export no-export no-advertise no-export exact-match
+sho bgp view gARIABLE nyeighbors 2001:db8::1 received-routes
+shoow bgp ommunity local-AS no--export
+show6 bgp community no-advertise local4-AS no-advertise VARIABLE exact-math
+show8 bgp view VARIABLE ipv4 multicast community ARIABLE VARIABLE local-S
+show bgp cCommunity VARIABLE VOARIABL no-advertise
+show bgp cimAunity loal-AS local-AS no-export local-AS
+show bgp cmmunity n-advertise no-export local-S no-advertise
+show bgp communi0y no-export no-Cexport no-0xport no-export
+show bgp communityOlocal-A no-advertise local-WAS
+show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match
+show bgp communiy no-export no-adsvertise VARIABLOE local-AS
+show bgp communiYty no-export VARIABLE VARIABLE locali-AS exact-math
+show bgp commuUityW no-advertis local-AS no-advertise no-advertise
+show bgp commuWnity VAIABLE local-AS no-advertise n-export
+show bgp com:unity no-exportqno-export VARIABLE no-expoIrt exact-match
+show bgp ipv6 community local-AS no-expor no-xport VARIABCLE
+show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE
+show bgp ipv6 community no-advertise no6-export lcal-AS local-AS
+show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math
+show bgp ipv6 comm-unity no-advertise no-export local-AS local-kS exact-match
+show bgp ipv6 community no-export local-AS no-adertise no-adve-tie
+show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE
+show bgp naighbors 201:db8::1 rUeceived-routes
+show bgp viewVAIABLE ipv4 multicast community VARIABLE4no-export no-advertise local-AS
+show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS
+show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export
+show bgp view VARIABLE ipv4 multicast omsunity local-AS VARIABLE no-advertise nUo-export
+show bgp view VARIABLE ipv4 mutiast community no-export no-export VARIBLE no-export
+show bgp view VARIABLE ipv4 unicast 0community VARIABqLE local-AS no-export VARIABwE
+show bgp view VARIABLE ipv4 unicast communeity no-export AcRIABLE no-advertise local-AS
+show bgp view VARIABLE ipv4 unicasU comunity no-export VARIABL no-advertise
+show bgp view VARIABLE ipv6 unicast cocmmunity VARIABLE no-advet6ise VARIABLE
+show bgp view VARIABLE ipvk4 unicast communty no-advertie local-AS local-AS no-export
+show bgp view VARIALE ipv4 multicast cyommunity no-xport local-AS local-AS
+show i6 bge community no-export VARIABLE no-advegtise VARIABLE exact-match
+show iI bgp community no-advertise no-ad2vertsse VARIABLE exact-match
+show ip6osp6 database dump
+show ipA6 bgp community local-AS local-AS no-advertse lo:cal-AS
+show ip bg comunity VARIABLE lcal-AS no-advertise
+show ip bgp communityno-export2no-export no-advertise locaE-AS
+Show ip bgp community no-export loqcal-AS no-adverise no-export
+show ip bgp community no-expor VARIABLEono-export VARIAuBLE
+show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match
+show ip bgp cWmmunity no-expoWrt VARIABLE no-advertise VARIABLEexact-match
+show ip bgp ip4 nicast community no-advertise no-expoIt local-AS local-AS exact-match
+show ip bgp ipAv4 multicast community no-export no-export no-export no-advertiqe exact-mach
+show ip bgp ipv4 Aulticast community no-advertise VARIABLE no-advertisKe no-exort
+show ip bgp ipv4 meuqlticast community VARIABLE VARIABLE no-export n-export
+show ip bgp ipv4 mlticast coQmmunity localg-AS local-AS no-advertise local-AS
+show ip bgp ipv4 multicast communiy VARIABLE no-export VARIABLE no-advertise yxact-atch
+show ip bgp ipv4 unicast commu0nity local-AS no-export no-exrt VARIABLE exact-match
+show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match
+showip bgp ipv4 unicast community no-export VARIABLE no-exp-ort VAR6IABLE exact-match
+show ip bgp ipv4 unicat community no-exportlocal-AS VARIABLE no-export exa0t-match
+show ip bgp ipv4 unicst community no-advertiseG local-AS no-advertise
+show ip bgp i:v4 multicast community VARIABLE VARIABLE VARIABLE no-export eMxact-match
+show ip bgp Mv4 unicast community no-export VARIABLE VARIABLE VAoRIABLE
+show ipgexecommunity-list 1
+show ipkv6 bgp community no-export no-export VARIABL VARIBLE
+show ipv6 bgp commu2nity local-AS local-AS noEadvertise local-AS
+show ipv6 bgp communitK VARIABLE lcocal-AS no-advertie no-advertise exact-match
+show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match
+show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE
+show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match
+show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE
+show ipv6 bgp comu-ity VARIABLE local-AS no-advertise no-export exact-match
+show ipv6 bgp comunity no- export local-AS no-advertisge VARIABLE
+show ipv6 bgp ommunity sno-advcrtise VARIABLE no-export no-advertise exact-match
+show ipv6 igp community no-advertise no-advertise no-ecxpo0rt no-export
+show ipv6 mb communyty VARIABLE
+show ipv6 osp8f6 database nQtwork adv-ruter 1.2.3.4 detail
+show ipv6 ospf6 dataase type-7 adv-router 1.2.3.4 inernal
+show ipv6 ospf6 Edatabase intuer8-prefix 1.2.3.4 detail
+show ipvq6 ospf6 database as-externa detil
+show ip Wbgp ipv4 unicast community no-advertise no-exprt no-export VARIABLEK exact-match
+show ip Ybgp attribute-in ufo
+showMbgp ipv6 community ARIABLE local-AS local-AS no8advertise exact-match
+show p bgp community no-dvertise no-export no-advertiseIno-export exact-match
+show uipv6 mbgp coqmmunKty VARIABLE
+shQw ipv6 mbgp community no-advetise local-AS no-export no-export ex8ct-match
+shuw ipv6 mbgp community VARIABLyUE no-export no-export no-advertise
+shw bgp view VARIABLE ipv4 un0icast Gcommunity no-export VARIABLE no-advertise
+sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise
+sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4
+Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS
+Wneighbor 1.2.3.4 dot-capabiliy-negotiate
+#
+#
+# Some teststrings explicitly used for keyword commands
+#
+#
+redistribute bgp
+redistribute bgp m 10
+redistribute bgp metric 10 metric-type 1
+redistribute bgp metric 10 metric 10
+redistribute bgp route-map RMAP_REDIST_BGP
+default-information originate metric-type 1 metric 10
+default-information originate always metric-type 1 metric 10
+default-information originate route-map RMAP_DEFAULT
+default-information originate route-map RMAP_DEFAULT metric 10
+default-information originate always metric-type 2 metric 23
diff --git a/tests/testcommands.refout b/tests/testcommands.refout
new file mode 100644 (file)
index 0000000..11483b8
--- /dev/null
@@ -0,0 +1,1007 @@
+execute relaxed 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE'
+execute strict 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE'
+complete 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0
+  'KEY' 'The OSPF password (key)'
+execute relaxed 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+execute strict 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+complete 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0
+  '<1-65535>' 'Seconds'
+execute relaxed 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1'
+execute strict 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1'
+complete 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==2
+describe 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0
+  '<1-65535>' 'Seconds'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1'
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0
+  '<1-65535>' 'Seconds'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1'
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0
+  '<1-65535>' 'Seconds'
+execute relaxed 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1'
+execute strict 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1'
+complete 'clear bgp 1 out'@4: rv==7
+  'out'
+describe 'clear bgp 1 out'@4: rv==0
+  'out' 'Soft reconfig outbound update'
+execute relaxed 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1'
+execute strict 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1'
+complete 'clear bgp ipv6 2001:db8::1 out'@4: rv==7
+  'out'
+describe 'clear bgp ipv6 2001:db8::1 out'@4: rv==0
+  'out' 'Soft reconfig outbound update'
+execute relaxed 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE'
+execute strict 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE'
+complete 'clear bgp view VARIABLE * soft'@4: rv==7
+  'soft'
+describe 'clear bgp view VARIABLE * soft'@4: rv==0
+  'soft' 'Soft reconfig'
+execute relaxed 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast'
+execute strict 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast'
+complete 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==7
+  'out'
+describe 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0
+  'out' 'Soft reconfig outbound update'
+execute relaxed 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig'
+execute strict 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig'
+complete 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==7
+  'no-autoconfig'
+describe 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0
+  'no-autoconfig' 'Do not use prefix for autoconfiguration'
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0
+  '<1-255>' 'Distance value for this prefix'
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2
+execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1'
+execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2
+execute relaxed 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0'
+execute strict 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0'
+complete 'network 1.0.0.0/8 area 0'@23: rv==2
+describe 'network 1.0.0.0/8 area 0'@23: rv==0
+  '<0-4294967295>' 'OSPF area ID as a decimal value'
+  'A.B.C.D' 'OSPF area ID in IP address format'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==7
+  'transmit-delay'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0
+  'transmit-delay' 'Seconds'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==7
+  'transmit-delay'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0
+  'transmit-delay' 'Seconds'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==7
+  'hello-interval'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0
+  'hello-interval' 'Link state transmit delay'
+execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval'
+execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval'
+complete 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==7
+  'hello-interval'
+describe 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0
+  'hello-interval' 'Link state transmit delay'
+execute relaxed 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart'
+complete 'no bgp graceful-restart'@17: rv==7
+  'graceful-restart'
+describe 'no bgp graceful-restart'@17: rv==0
+  'graceful-restart' 'Graceful restart capability parameters'
+execute relaxed 'no bgp graceful-restart'@18: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@18: rv==2
+complete 'no bgp graceful-restart'@18: rv==2
+describe 'no bgp graceful-restart'@18: rv==2
+execute relaxed 'no bgp graceful-restart'@19: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@19: rv==2
+complete 'no bgp graceful-restart'@19: rv==2
+describe 'no bgp graceful-restart'@19: rv==2
+execute relaxed 'no bgp graceful-restart'@20: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@20: rv==2
+complete 'no bgp graceful-restart'@20: rv==2
+describe 'no bgp graceful-restart'@20: rv==2
+execute relaxed 'no bgp graceful-restart'@21: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@21: rv==2
+complete 'no bgp graceful-restart'@21: rv==2
+describe 'no bgp graceful-restart'@21: rv==2
+execute relaxed 'no bgp graceful-restart'@22: rv==0, 'no bgp graceful-restart'
+execute strict 'no bgp graceful-restart'@22: rv==2
+complete 'no bgp graceful-restart'@22: rv==2
+describe 'no bgp graceful-restart'@22: rv==2
+execute relaxed 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1'
+execute strict 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1'
+complete 'no ipv6 nd mtu 1'@11: rv==2
+describe 'no ipv6 nd mtu 1'@11: rv==0
+  '<1-65535>' 'MTU in bytes'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in'
+complete 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==7
+  'in'
+describe 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0
+  'in' 'Filter incoming updates'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@17: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@17: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@18: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@18: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@19: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@19: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@20: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@20: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@21: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@21: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+execute strict 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both'
+complete 'no neighbor 2001:db8::1 send-community both'@22: rv==7
+  'both'
+describe 'no neighbor 2001:db8::1 send-community both'@22: rv==0
+  'both' 'Send Standard and Extended Community attributes'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@17: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@17: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@18: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@18: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@19: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@19: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@20: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@20: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@21: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@21: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+execute strict 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE'
+complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7
+  'maximum-prefix'
+describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0
+  'maximum-prefix' 'Maximum number of prefix accept from this peer'
+execute relaxed 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE'
+execute strict 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE'
+complete 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==7
+  '2'
+describe 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0
+  '2' 'Set OSPF External Type 2 metrics'
+execute relaxed 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE'
+execute strict 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE'
+complete 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==7
+  '1'
+describe 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0
+  '1' 'Set OSPF External Type 1 metrics'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7
+  'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==7
+  'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==7
+  'exact-match'
+describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==7
+  'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==7
+  'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export'
+complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==7
+  'no-export'
+describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise'
+complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS'
+complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==7
+  'exact-match'
+describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp view VARIABLE'@1: rv==4
+execute strict 'show bgp view VARIABLE'@1: rv==4
+complete 'show bgp view VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE'@1: rv==0
+  'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE'
+execute strict 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE'
+complete 'show bgp view VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE'@2: rv==0
+  'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE'
+execute strict 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE'
+complete 'show bgp view VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE'@4: rv==0
+  'WORD' 'View name'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==7
+  'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0
+  'AA:NN' 'community number'
+  'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==7
+  'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0
+  'AA:NN' 'community number'
+  'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise'
+complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==7
+  'no-advertise'
+describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0
+  'AA:NN' 'community number'
+  'no-advertise' 'Do not advertise to any peer (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE'
+complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==2
+describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==7
+  'local-AS'
+describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE'
+complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==2
+describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS'
+complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==2
+describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE'
+complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==2
+describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise'
+complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE'
+complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export'
+complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export'
+complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+execute strict 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+complete 'show ipv6 ospf6 database as-external dump'@2: rv==7
+  'dump'
+describe 'show ipv6 ospf6 database as-external dump'@2: rv==0
+  'dump' 'Dump LSAs'
+execute relaxed 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+execute strict 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump'
+complete 'show ipv6 ospf6 database as-external dump'@4: rv==7
+  'dump'
+describe 'show ipv6 ospf6 database as-external dump'@4: rv==0
+  'dump' 'Dump LSAs'
+execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==7
+  'detail'
+describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0
+  'detail' 'Display details of LSAs'
+execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail'
+complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==7
+  'detail'
+describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0
+  'detail' 'Display details of LSAs'
+execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==7
+  'internal'
+describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0
+  'internal' 'Display LSA's internal information'
+execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal'
+complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==7
+  'internal'
+describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0
+  'internal' 'Display LSA's internal information'
+execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'dead-interva', '1', 'retransmit-interval', '1', 'transmit-delay', '1'
+execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2
+describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0
+  '<1-65535>' 'Seconds'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==7
+  'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==7
+  'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise'
+complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==7
+  'exact-match'
+describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE'
+complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==2
+describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==2
+describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==7
+  'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==7
+  'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS'
+complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==7
+  'local-AS'
+describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math'
+complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==2
+describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie'
+complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==2
+describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE'
+complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==2
+describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==7
+  'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==7
+  'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS'
+complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==7
+  'local-AS'
+describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0
+  'AA:NN' 'community number'
+  'local-AS' 'Do not send outside local AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==7
+  'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==7
+  'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export'
+complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==7
+  'no-export'
+describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0
+  'AA:NN' 'community number'
+  'no-export' 'Do not export to next AS (well-known community)'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE'
+complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==2
+describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match'
+complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==2
+describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export'
+complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==7
+  'exact-match'
+describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise'
+complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==2
+describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE'
+complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==2
+describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS'
+complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==7
+  'exact-match'
+describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0
+  'exact-match' 'Exact match of the communities'
+execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==2
+describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE'
+complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2
+describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0
+  'AA:NN' 'community number'
+execute relaxed 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp'
+execute strict 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp'
+complete 'redistribute bgp'@14: rv==7
+  'bgp'
+describe 'redistribute bgp'@14: rv==0
+  'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp'
+execute strict 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp'
+complete 'redistribute bgp'@15: rv==7
+  'bgp'
+describe 'redistribute bgp'@15: rv==0
+  'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp'
+execute strict 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp'
+complete 'redistribute bgp'@16: rv==7
+  'bgp'
+describe 'redistribute bgp'@16: rv==0
+  'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)'
+execute strict 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)'
+complete 'redistribute bgp'@23: rv==7
+  'bgp'
+describe 'redistribute bgp'@23: rv==0
+  'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp'
+execute strict 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp'
+complete 'redistribute bgp'@24: rv==7
+  'bgp'
+describe 'redistribute bgp'@24: rv==0
+  'bgp' 'Border Gateway Protocol (BGP)'
+execute relaxed 'redistribute bgp m 10'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) metric <0-16>': 'bgp', '10'
+execute strict 'redistribute bgp m 10'@14: rv==2
+complete 'redistribute bgp m 10'@14: rv==2
+describe 'redistribute bgp m 10'@14: rv==0
+  '<0-16>' 'Metric value'
+execute relaxed 'redistribute bgp m 10'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) metric <0-16>': 'bgp', '10'
+execute strict 'redistribute bgp m 10'@15: rv==2
+complete 'redistribute bgp m 10'@15: rv==2
+describe 'redistribute bgp m 10'@15: rv==0
+  '<0-16>' 'Metric value'
+execute relaxed 'redistribute bgp m 10'@23: rv==3
+execute strict 'redistribute bgp m 10'@23: rv==2
+complete 'redistribute bgp m 10'@23: rv==3
+describe 'redistribute bgp m 10'@23: rv==3
+execute relaxed 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)'
+execute strict 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)'
+complete 'redistribute bgp metric 10 metric-type 1'@23: rv==7
+  '1'
+describe 'redistribute bgp metric 10 metric-type 1'@23: rv==0
+  '1' 'Set OSPF External Type 1 metrics'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0
+  'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0
+  'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0
+  'WORD' 'Pointer to route-map entries'
+execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP'
+complete 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==2
+describe 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0
+  'WORD' 'Route map name'
+execute relaxed 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)'
+execute strict 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)'
+complete 'default-information originate metric-type 1 metric 10'@23: rv==2
+describe 'default-information originate metric-type 1 metric 10'@23: rv==0
+  '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)'
+execute strict 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)'
+complete 'default-information originate always metric-type 1 metric 10'@23: rv==2
+describe 'default-information originate always metric-type 1 metric 10'@23: rv==0
+  '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT'
+execute strict 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT'
+complete 'default-information originate route-map RMAP_DEFAULT'@23: rv==2
+describe 'default-information originate route-map RMAP_DEFAULT'@23: rv==0
+  'WORD' 'Pointer to route-map entries'
+execute relaxed 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT'
+execute strict 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT'
+complete 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==2
+describe 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0
+  '<0-16777214>' 'OSPF metric'
+execute relaxed 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)'
+execute strict 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)'
+complete 'default-information originate always metric-type 2 metric 23'@23: rv==2
+describe 'default-information originate always metric-type 2 metric 23'@23: rv==0
+  '<0-16777214>' 'OSPF metric'
diff --git a/tests/tests.h b/tests/tests.h
new file mode 100644 (file)
index 0000000..a528e55
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Test wrappers common header file
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ *                    for Open Source Routing./ NetDEF, Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_TESTS_H
+#define _QUAGGA_TESTS_H
+
+extern void test_init (void);
+extern void test_init_cmd (void);
+
+#endif /* _QUAGGA_TESTS_H */
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644 (file)
index 0000000..dd5bf7c
--- /dev/null
@@ -0,0 +1,6 @@
+.arch-inventory
+.arch-ids
+
+*~
+*.loT
+
diff --git a/tools/mrlg.txt b/tools/mrlg.txt
new file mode 100644 (file)
index 0000000..0ebf7ee
--- /dev/null
@@ -0,0 +1,5 @@
+The Multi-Router Looking Glass (MRLG) CGI script now lives at:
+
+  http://mrlg.op-sec.us/
+
+Please obtain the latest version from there.
diff --git a/tools/multiple-bgpd.sh b/tools/multiple-bgpd.sh
new file mode 100644 (file)
index 0000000..c5668e1
--- /dev/null
@@ -0,0 +1,458 @@
+#!/bin/bash
+
+# Public domain, not copyrighted..
+
+set -u
+
+# number of bgpd instances, not more than 255 at this point.  At least 3 are
+# needed to connect in a ring.
+NUM=7
+
+# The NUM peers can be connected in a ring topology.
+#
+# This sets the proportion of other peers that each peer should be
+# configured to connect to E.g., 20 means each BGP instance will peer with
+# 20% of the other peers before and after it in the ring.  So 10% of the
+# peers prior to this instance in the ring, and 10% of the following peers. 
+# 100 should lead to a full-mesh, for an odd total number of peers.
+#
+# A value of 1 will result in each instance having at least 2 peers in the ring.
+#
+# A value of 0 will disable creating a ring, in which case the only peers 
+# configured will be those in the EXPEERS list.
+PEERPROP=100
+
+# number of routes each BGP instance should advertise
+ADV=10
+# First octet to use for the IPv4 advertisements.  The advertisements
+# will be /32s under this /8.  E.g.  ADVPREF=10 will mean
+# 10.x.y.z/32's are advertised.
+ADVPREF=10
+
+# Base VTY port to allocate Quagga telnet vtys from. VTYBASE+ID will be
+# the port.
+VTYBASE=2610
+# Base ASN to allocate ASNs to instances.
+ASBASE=64500
+PREFIX=192.168.145.
+#PREFIX=3ffe:123:456::
+ADDRPLEN=32
+CONFBASE=/tmp
+PIDBASE=/var/run/quagga
+USER=quagga
+GROUP=quagga
+
+# MRAI to specify, where an implementation supports it.
+MRAI=1
+# Connect retry timer
+CONNECTRETRY=1
+
+# The binary locations for BGP instances. 
+declare -A BGP_BINS=(
+       [quagga]=/usr/sbin/bgpd
+       [bird]=/usr/sbin/bird
+       [birdgit]=/home/paul/code/bird/bird
+       [quaggagit]=/home/paul/code/quagga/bgpd/bgpd
+       [exabgp]=/home/paul/code/exabgp/sbin/exabgp
+)
+
+# Configuration generation functions for the BGP instances.
+declare -A BGP_CONFIGGEN=(
+       [quagga]=quagga_config
+       [quaggagit]=quagga_config
+       [bird]=bird_config
+       [birdgit]=bird_config
+       [exabgp]=exabgp_config
+)
+
+# Launch functions for the BGP instances.
+declare -A BGP_LAUNCH=(
+       [quagga]=quagga_launch
+       [quaggagit]=quagga_launch
+       [bird]=bird_launch
+       [birdgit]=bird_launch
+       [quaggagit]=quagga_launch
+       [exabgp]=exabgp_launch
+)
+
+# the instances to run, in the order they should appear in the ring
+# (repeated over until there are $NUM instances).  The value must exist as a
+# key into the above two arrays.
+declare -a BGP_INSTANCES=(
+       quagga
+       bird
+       quaggagit
+       exabgp
+)
+
+# Peers to configure, that are external to this script. One list of IPs, with
+# corresponding list of their ASes.
+#
+# e.g.:
+#EXPEERS=(192.168.147.{1..10})
+#EXPEERASES=($(seq $((ASBASE+11)) $(($ASBASE+20))))
+
+EXPEERS=()
+EXPEERASES=()
+
+############################################################################
+# Can override any of the above from a supplied file with declarations
+CONFWRITE=Y
+if [ $# -gt 0 ] ; then
+       echo "multiple-bgpd.sh: sourcing config from $1"
+       [ -f "$1" ] && . "$1"
+       
+       # keep config, if exists
+       [ $# -gt 1 ] && [ "$2" = "k" ] && CONFWRITE=N   
+fi
+
+############################################################################
+# Internal variables.
+
+# Number of peers for each instance to peer with
+PEERNUM=$(( ($NUM-1) * $PEERPROP / 100  ))
+[ "$PEERNUM" -gt $(($NUM-1)) ] && PEERNUM=$(($NUM-1))
+
+# the 'range', i.e.  how many of the previous and next peers in the ring to
+# connect to
+PEERRANGE=$(( $PEERNUM/2 ))
+[ "$PEERPROP" -gt 0 -a "$NUM" -ge 3 -a  "$PEERRANGE" -le 0 ] && PEERRANGE=1
+
+# and a convenience expansion
+PEEREXP=""
+if [ "$PEERRANGE" -gt 0 ]; then
+       PEEREXP=($(seq -${PEERRANGE} ${PEERRANGE}))
+       # dont need 0
+       unset PEEREXP[PEERRANGE]
+fi
+
+#echo ${PEEREXP[@]}
+
+############################################################################
+## helpers
+
+# translate instance ID to its address.
+id2addr () {
+       local ID=$1
+       echo ${PREFIX}${ID}
+}
+
+# return the ID of a peer, in terms of an offset on the given instance's ID.
+#
+# E.g., given an ID of 1 and an offset of -1, if there are 10 instances overall,
+# this will return 10.
+peeridoff () {
+       local ID=$1
+       local OFF=$2
+       echo $(( (($ID + $OFF - 1  + $NUM) % $NUM) + 1  ))
+}
+
+# return IPv4 address to advertise, for given instance ID and number.
+advipaddr () {
+       local ID=$1
+       local N=$2
+       echo "$ADVPREF.$(( ($N >> 16) %256 )).$(( ($N >> 8) % 256 )).$(( $N % 256  ))"
+}
+
+############################################################################
+# launch functions
+#
+# do not daemonise, so that all launched instances can be killed by killing
+# the script.
+#
+
+quagga_launch () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+       local BIN=$4
+       local CONF=$5
+       ${BIN} -i "${PIDBASE}"/bgpd${ID}.pid \
+                  -l ${ADDR} \
+                  -f "${CONF}" \
+                  -u $USER -g $GROUP \
+                  -P $((${VTYBASE}+${ID}))
+}
+
+exabgp_launch () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+       local BIN=$4
+       local CONF=$5
+       
+       env exabgp.api.file="${PIDBASE}"/exabgp${ID}.ctl \
+       exabgp.daemon.pid="${PIDBASE}"/bgpd${ID}.pid \
+       exabgp.daemon.daemonize=false \
+       exabgp.tcp.bind=${ADDR} \
+       exabgp.log.enable=false \
+       exabgp.daemon.user=quagga \
+       ${BIN} ${CONF}
+}
+
+bird_launch () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+       local BIN=$4
+       local CONF=$5
+       ${BIN} -P "${PIDBASE}"/bird${ID}.pid \
+                  -c "${CONF}" \
+                  -s "${PIDBASE}"/bird${ID}.ctl \
+                  -f
+}
+
+#######################################################################
+#
+# functions to write the configuration for instances
+#
+
+exabgp_config () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+       
+       local N
+       local P
+       
+       cat <<- EOF
+               group default {
+                 local-address $ADDR;
+                 local-as $ASN;
+                 router-id $ADDR;
+                 
+                 capability {
+                   asn4 enable;
+                 }
+       EOF
+       
+       for N in $(seq 1 $ADV) ; do
+               echo "  static {"
+               echo "    route `advipaddr $ID $N`/32 {"
+               echo "      next-hop $ADDR;"
+               echo "    }"
+               echo "  }" 
+       done
+
+       for P in ${PEEREXP[@]};  do
+               [ "$P" -eq 0 ] && continue;
+               
+               #local PID=$(( (($ID + $P - 1  + $NUM) % $NUM) + 1  ))
+               local PID=`peeridoff $ID $P`
+               #local PADDR="${PREFIX}${PID}"
+               local PADDR=`id2addr $PID`
+               local PAS=$((${ASBASE} + $PID))
+               
+               echo "  neighbor $PADDR {"
+               #echo "    local-address $ADDR;"
+               #echo "    local-as $ASN;"
+               #echo "    graceful-restart;"
+               #echo "    router-id $ADDR;"
+               echo "    peer-as $PAS;"
+               echo "  }"
+       done
+       
+       for P in ${!EXPEERS[@]};  do
+               echo "  neighbor ${EXPEERS[$P]} {"
+               echo "    peer-as ${EXPEERASES[$P]};"
+               echo "  }"
+       done
+       
+       cat <<- EOF
+               }
+       EOF
+}
+
+quagga_config () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+       
+       local N
+       local P
+       
+       # Edit config to suit.
+       cat <<- EOF
+               password foo
+               service advanced-vty
+               !
+               router bgp ${ASN}
+                bgp router-id ${ADDR}
+                !maximum-paths 32
+                !bgp bestpath as-path multipath-relax
+       EOF
+
+       for N in $(seq 1 $ADV) ; do
+               echo " network `advipaddr $ID $N`/32"
+       done
+
+       cat <<- EOF
+                neighbor default peer-group
+                neighbor default update-source ${ADDR}
+                neighbor default capability orf prefix-list both
+                !neighbor default soft-reconfiguration inbound
+                neighbor default advertisement-interval $MRAI
+                neighbor default timers connect $CONNECTRETRY
+                neighbor default route-map test out
+       EOF
+
+       for P in ${PEEREXP[@]};  do
+               [ "$P" -eq 0 ] && continue;
+               
+               local PID=`peeridoff $ID $P`
+               local PADDR=`id2addr $PID`
+               local PAS=$((${ASBASE} + $PID))
+               echo " neighbor ${PADDR} remote-as ${PAS}"
+               echo " neighbor ${PADDR} peer-group default"
+       done
+
+       for P in ${!EXPEERS[@]};  do
+               echo " neighbor ${EXPEERS[$P]} remote-as ${EXPEERASES[$P]}"
+               echo " neighbor ${EXPEERS[$P]} peer-group default"
+       done
+
+       cat <<- EOF
+               !
+                address-family ipv6
+                network 3ffe:${ID}::/48
+                network 3ffe:${ID}:1::/48 pathlimit 1
+                network 3ffe:${ID}:2::/48 pathlimit 3
+                network 3ffe:${ID}:3::/48 pathlimit 3
+                neighbor default activate
+                neighbor default capability orf prefix-list both
+                neighbor default default-originate
+                neighbor default route-map test out
+       EOF
+
+       for P in ${PEEREXP[@]};  do
+               [ "$P" -eq 0 ] && continue;
+               
+               local PID=`peeridoff $ID $P`
+               local PADDR=`id2addr $PID`
+               local PAS=$((${ASBASE} + $PID))
+               echo " neighbor ${PADDR} peer-group default"
+       done
+
+       cat <<- EOF
+                exit-address-family
+               !
+               ! bgpd still has problems with extcommunity rt/soo
+               route-map test permit 10
+                set extcommunity rt ${ASN}:1
+                set extcommunity soo ${ASN}:2
+                set community ${ASN}:1
+               !
+               line vty
+                exec-timeout 0 0
+               !
+               end
+       EOF
+}
+
+bird_config () {
+       local ID=$1
+       local ASN=$2
+       local ADDR=$3
+
+       cat <<- EOF
+               #log "/var/log/bird.log" all;
+               #debug protocols all;
+
+               # Override router ID
+               router id ${ADDR};
+               listen bgp address ${ADDR};
+               
+               protocol kernel { device routes; import all; }
+               protocol device { import all; }
+               
+               function avoid_martians()
+               prefix set martians;
+               {
+                 martians = [ 
+                              224.0.0.0/4+, 240.0.0.0/4+
+                            ];
+
+                 # Avoid RFC1918 and similar networks
+                 if net ~ martians then return false;
+                 return true;
+               }
+               
+               filter import_filter
+               {
+                 if ! (avoid_martians()) then reject;
+                 accept;
+               }
+               
+               filter set_comm
+               {
+                 bgp_community.add ((${ASN}, 1));
+                 accept;
+               }
+               
+               template bgp peer_conf {
+                 local as ${ASN};
+                 source address ${ADDR};
+                 import filter import_filter;
+                 export filter set_comm;
+                 multihop;
+               }
+       EOF
+       
+       local P;
+       
+       for P in ${PEEREXP[@]};  do
+               [ "$P" -eq 0 ] && continue;
+               
+               local PID=`peeridoff $ID $P`
+               local PADDR=`id2addr $PID`
+               local PAS=$((${ASBASE} + $PID))
+               echo "protocol bgp from peer_conf {"
+               echo " neighbor ${PADDR} as  ${PAS};"
+               echo "}"
+       done
+       
+       for P in ${!EXPEERS[@]};  do
+               echo "protocol bgp from peer_conf {"
+               echo " neighbor ${EXPEERS[$P]} as ${EXPEERASES[$P]};"
+               echo "}"
+       done
+       
+       
+       for N in $(seq 1 $ADV) ; do
+               echo " network `advipaddr $ID $N`/32"
+       done
+}
+
+#######################################################################
+
+for ID in $(seq 1 $NUM); do
+       BGP_INST=${BGP_INSTANCES[${ID} % ${#BGP_INSTANCES[@]}]}
+       BGPBIN=${BGP_BINS[$BGP_INST]}
+       CONF="${CONFBASE}"/${BGP_INST}_bgpd${ID}.conf
+       ASN=$(($ASBASE + ${ID}))
+       ADDR=`id2addr $ID`
+       
+       #if [ ! -e "$CONF" ] ; then
+       if [ ! -e "$CONF" -o "$CONFWRITE" = "Y" ] ; then 
+               ${BGP_CONFIGGEN[$BGP_INST]} $ID $ASN $ADDR > "$CONF"
+               chown $USER:$GROUP "$CONF"
+       fi
+       # You may want to automatically add configure a local address
+       # on a loop interface.
+       #
+       # Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up
+       # Linux:
+       #ip address add ${ADDR}/${ADDRPLEN} dev lo 2> /dev/null
+       
+       ip link add dummy${ID} type dummy 2> /dev/null
+       ip link set dev dummy${ID} up
+       ip address add ${ADDR}/${ADDRPLEN} dev dummy${ID} 2> /dev/null
+       
+       ${BGP_LAUNCH[$BGP_INST]} $ID $ASN $ADDR $BGPBIN $CONF &
+       
+       sleep 0.1
+done
+
+echo "multiple-bgpd.sh: waiting..."
+
+wait
diff --git a/tools/zebra.el b/tools/zebra.el
new file mode 100644 (file)
index 0000000..01ff09f
--- /dev/null
@@ -0,0 +1,108 @@
+;; -*- lisp -*-
+;;; zebra-mode.el -- major mode for editing zebra configuration file.
+
+;; Copyright (C) 1998 Kunihiro Ishiguro
+
+;; Author:     1998 Kunihiro Ishiguro
+;;                  SeonMeyong HEO
+;; Maintainer: kunihiro@zebra.org
+;;             seirios@Matrix.IRI.Co.JP
+;; Created:    Jan 28 1998
+;; Version:    Alpha 0.2
+;; Keywords:   zebra bgpd ripd ripngd languages
+
+;; You can get the latest version of zebra from
+;;
+;;    http://www.zebra.org/
+;;
+;; Install this Emacs Lisp code
+;;
+;; Compile zebra.el
+;;   % $(EMACS) -batch -f batch-byte-compile zebra.el
+;; Install zebra.el,zebra.elc to Emacs-load-path
+;;   % cp zebra.el zebra.elc $(emacs-load-path)
+;; Add .emacs or (site-load.el | site-start.el)
+;;   (auto-load 'zebra-mode "zebra" nil t)
+;;   (auto-load 'bgp-mode "zebra" nil t)
+;;   (auto-load 'rip-mode "zebra" nil t)
+;;
+
+;;; Code:
+
+;; Set keywords
+
+(defvar zebra-font-lock-keywords
+  (list
+   '("#.*$" . font-lock-comment-face)
+   '("!.*$" . font-lock-comment-face)
+   '("no\\|interface" . font-lock-type-face)
+   '("ip6\\|ip\\|route\\|address" . font-lock-function-name-face)
+   '("ipforward\\|ipv6forward" . font-lock-keyword-face)
+   '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face))
+  "Default value to highlight in zebra mode.")
+
+(defvar bgp-font-lock-keywords
+  (list
+   '("#.*$" . font-lock-comment-face)
+   '("!.*$" . font-lock-comment-face)
+   '("no\\|router" . font-lock-type-face)
+   '("bgp\\|router-id\\|neighbor\\|network" . font-lock-function-name-face)
+   '("ebgp\\|multihop\\|next\\|zebra\\|remote-as" . font-lock-keyword-face)
+   '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face))
+  "Default value to highlight in bgp mode.")
+
+(defvar rip-font-lock-keywords
+  (list
+   '("#.*$" . font-lock-comment-face)
+   '("!.*$" . font-lock-comment-face)
+   '("no\\|router\\|interface\\|ipv6\\|ip6\\|ip" . font-lock-type-face)
+   '("ripng\\|rip\\|recive\\|advertize\\|accept" . font-lock-function-name-face)
+   '("version\\|network" . font-lock-function-name-face)
+   '("default\\|none\\|zebra" . font-lock-keyword-face)
+   '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face))
+  "Default value to highlight in bgp mode.")
+
+;; set font-lock-mode
+
+(defun zebra-font-lock ()
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults '(zebra-font-lock-keywords nil t)))
+
+(defun bgp-font-lock ()
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults '(bgp-font-lock-keywords nil t)))
+
+(defun rip-font-lock ()
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults '(rip-font-lock-keywords nil t)))
+
+;; define Major mode
+
+(defun major-mode-define ()
+  (interactive)
+  (progn
+    (setq comment-start "[#!]"
+         comment-end ""
+         comment-start-skip "!+ ")
+    (run-hooks 'zebra-mode-hook)
+    (cond
+     ((string< "20" emacs-version)
+      (font-lock-mode)))))
+
+(defun zebra-mode ()
+  (progn
+    (setq mode-name "zebra")
+    (zebra-font-lock))
+  (major-mode-define))
+
+(defun bgp-mode ()
+  (progn
+    (setq mode-name "bgp") 
+    (bgp-font-lock))
+  (major-mode-define))
+
+(defun rip-mode ()
+  (progn
+    (setq mode-name "rip")
+    (rip-font-lock))
+  (major-mode-define))
diff --git a/update-autotools b/update-autotools
new file mode 100755 (executable)
index 0000000..d5db16d
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+#
+# When local system does not have the latest autoconf/automake
+#        -- Kunihiro Ishiguro <kunihiro@zebra.org>
+#
+
+rm -f config.cache Makefile.in aclocal.m4 config.h.in configure
+rm -rf config.guess config.sub ltmain.sh
+rm -rf autom4te.cache
+
+echo "This $0 script is deprecated, and will be removed at some stage."
+echo "Please use the 'autoreconf' command included with autoconf."
+
+echo "TOOLS VERIONS:"
+for tool in autoheader autoconf libtool libtoolize aclocal automake; do
+  $tool --version | head -1
+done
+
+echo "ACLOCAL:"
+aclocal -I m4
+echo "AUTOHEADER:"
+autoheader
+echo "AUTOCONF:"
+autoconf
+echo "LIBTOOLIZE:"
+libtoolize -c
+echo "AUTOMAKE"
+automake --gnu --add-missing --copy
diff --git a/vtysh/.gitignore b/vtysh/.gitignore
new file mode 100644 (file)
index 0000000..5856eac
--- /dev/null
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+*.o
+vtysh
+tags
+TAGS
+.deps
+vtysh_cmd.c
+.nfs*
+extract.pl
+.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
new file mode 100644 (file)
index 0000000..983103f
--- /dev/null
@@ -0,0 +1,41 @@
+## Process this file with Automake to create Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+
+LIBS = @LIBS@ @CURSES@ @LIBPAM@
+
+AM_CFLAGS = $(WERROR)
+
+bin_PROGRAMS = vtysh
+
+vtysh_SOURCES = vtysh_main.c vtysh.c vtysh_user.c vtysh_config.c
+nodist_vtysh_SOURCES = vtysh_cmd.c
+CLEANFILES = vtysh_cmd.c
+noinst_HEADERS = vtysh.h vtysh_user.h
+vtysh_LDADD = ../lib/libzebra.la @LIBCAP@ @LIBREADLINE@
+
+examplesdir = $(exampledir)
+dist_examples_DATA = vtysh.conf.sample
+
+EXTRA_DIST = extract.pl
+
+vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \
+                 $(top_srcdir)/ospfd/*.c $(top_srcdir)/ospf6d/*.c \
+                 $(top_srcdir)/ripd/*.c $(top_srcdir)/ripngd/*.c \
+                 $(top_srcdir)/pimd/pim_cmd.c \
+                 $(top_srcdir)/nhrpd/nhrp_vty.c \
+                 $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
+                 $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
+                 $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
+                 $(top_srcdir)/lib/vrf.c \
+                 $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
+                 $(top_srcdir)/zebra/interface.c \
+                 $(top_srcdir)/zebra/irdp_interface.c \
+                 $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
+                 $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \
+                 $(top_srcdir)/zebra/zebra_routemap.c \
+                 $(top_srcdir)/zebra/zebra_fpm.c
+
+vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl
+       ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
new file mode 100755 (executable)
index 0000000..6e24b77
--- /dev/null
@@ -0,0 +1,243 @@
+#! @PERL@
+##
+## @configure_input@
+##
+## Virtual terminal interface shell command extractor.
+## Copyright (C) 2000 Kunihiro Ishiguro
+## 
+## This file is part of GNU Zebra.
+## 
+## GNU Zebra 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.
+## 
+## GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+## 02111-1307, USA.  
+##
+
+print <<EOF;
+#include <zebra.h>
+#include "command.h"
+#include "vtysh.h"
+
+EOF
+
+$ignore{'"interface IFNAME"'} = "ignore";
+$ignore{'"interface IFNAME " "vrf <0-65535>"'} = "ignore";
+$ignore{'"link-params"'} = "ignore";
+$ignore{'"ip vrf NAME"'} = "ignore";
+$ignore{'"router rip"'} = "ignore";
+$ignore{'"router ripng"'} = "ignore";
+$ignore{'"router ospf"'} = "ignore";
+$ignore{'"router ospf <0-65535>"'} = "ignore";
+$ignore{'"router ospf6"'} = "ignore";
+$ignore{'"router bgp " "<1-4294967295>"'} = "ignore";
+$ignore{'"router bgp " "<1-4294967295>" " view WORD"'} = "ignore";
+$ignore{'"router isis WORD"'} = "ignore";
+$ignore{'"router zebra"'} = "ignore";
+$ignore{'"address-family ipv4"'} = "ignore";
+$ignore{'"address-family ipv4 (unicast|multicast)"'} = "ignore";
+$ignore{'"address-family ipv6"'} = "ignore";
+$ignore{'"address-family ipv6 (unicast|multicast)"'} = "ignore";
+$ignore{'"address-family vpnv4"'} = "ignore";
+$ignore{'"address-family vpnv4 unicast"'} = "ignore";
+$ignore{'"address-family vpnv6"'} = "ignore";
+$ignore{'"address-family vpnv6 unicast"'} = "ignore";
+$ignore{'"address-family ipv4 vrf NAME"'} = "ignore";
+$ignore{'"address-family encap"'} = "ignore";
+$ignore{'"address-family encapv4"'} = "ignore";
+$ignore{'"address-family encapv6"'} = "ignore";
+$ignore{'"exit-address-family"'} = "ignore";
+$ignore{'"exit-link-params"'} = "ignore";
+$ignore{'"vnc defaults"'} = "ignore";
+$ignore{'"vnc nve-group NAME"'} = "ignore";
+$ignore{'"exit-vnc"'} = "ignore";
+$ignore{'"key chain WORD"'} = "ignore";
+$ignore{'"key <0-2147483647>"'} = "ignore";
+$ignore{'"route-map WORD (deny|permit) <1-65535>"'} = "ignore";
+$ignore{'"show route-map"'} = "ignore";
+$ignore{'"line vty"'} = "ignore";
+$ignore{'"who"'} = "ignore";
+$ignore{'"terminal monitor"'} = "ignore";
+$ignore{'"terminal no monitor"'} = "ignore";
+$ignore{'"show history"'} = "ignore";
+
+my $cli_stomp = 0;
+
+foreach (@ARGV) {
+    $file = $_;
+
+    open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology @CPPFLAGS@ $file |");
+    local $/; undef $/;
+    $line = <FH>;
+    close (FH);
+
+    @defun = ($line =~ /(?:DEFUN|ALIAS)\s*\((.+?)\);?\s?\s?\n/sg);
+    @install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg);
+
+    # DEFUN process
+    foreach (@defun) {
+       my (@defun_array);
+       @defun_array = split (/,/);
+       $defun_array[0] = '';
+
+
+       # Actual input command string.
+       $str = "$defun_array[2]";
+       $str =~ s/^\s+//g;
+       $str =~ s/\s+$//g;
+
+       # Get VTY command structure.  This is needed for searching
+       # install_element() command.
+       $cmd = "$defun_array[1]";
+       $cmd =~ s/^\s+//g;
+       $cmd =~ s/\s+$//g;
+
+        # $protocol is VTYSH_PROTO format for redirection of user input
+        if ($file =~ /lib\/keychain\.c$/) {
+            $protocol = "VTYSH_RIPD";
+        }
+        elsif ($file =~ /lib\/routemap\.c$/) {
+            $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
+        }
+        elsif ($file =~ /lib\/filter\.c$/) {
+            $protocol = "VTYSH_ALL";
+        }
+        elsif ($file =~ /lib\/vrf\.c$/) {
+            $protocol = "VTYSH_ZEBRA";
+        }
+        elsif ($file =~ /lib\/plist\.c$/) {
+            if ($defun_array[1] =~ m/ipv6/) {
+                $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
+            } else {
+                $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA";
+            }
+        }
+        elsif ($file =~ /lib\/distribute\.c$/) {
+            if ($defun_array[1] =~ m/ipv6/) {
+                $protocol = "VTYSH_RIPNGD";
+            } else {
+                $protocol = "VTYSH_RIPD";
+            }
+        }
+        elsif ($file =~ /lib\/if_rmap\.c$/) {
+            if ($defun_array[1] =~ m/ipv6/) {
+                $protocol = "VTYSH_RIPNGD";
+            } else {
+                $protocol = "VTYSH_RIPD";
+            }
+        }
+        elsif ($file =~ /lib\/vty\.c$/) {
+           $protocol = "VTYSH_ALL";
+        }
+       else {
+           ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/);
+           $protocol = "VTYSH_" . uc $protocol;
+        }
+
+       # Append _vtysh to structure then build DEFUN again
+       $defun_array[1] = $cmd . "_vtysh";
+       $defun_body = join (", ", @defun_array);
+
+       # $cmd -> $str hash for lookup
+       if (exists($cmd2str{$cmd})) {
+           warn "Duplicate CLI Function: $cmd\n";
+           warn "\tFrom cli: $cmd2str{$cmd} to New cli: $str\n";
+           warn "\tOriginal Protocol: $cmd2proto{$cmd} to New Protocol: $protocol\n";
+          $cli_stomp++;
+       }
+       $cmd2str{$cmd} = $str;
+       $cmd2defun{$cmd} = $defun_body;
+       $cmd2proto{$cmd} = $protocol;
+    }
+
+    # install_element() process
+    foreach (@install) {
+       my (@element_array);
+       @element_array = split (/,/);
+
+       # Install node
+       $enode = $element_array[0];
+       $enode =~ s/^\s+//g;
+       $enode =~ s/\s+$//g;
+       ($enode) = ($enode =~ /([0-9A-Z_]+)$/);
+
+       # VTY command structure.
+       ($ecmd) = ($element_array[1] =~ /&([^\)]+)/);
+       $ecmd =~ s/^\s+//g;
+       $ecmd =~ s/\s+$//g;
+
+       # Register $ecmd
+       if (defined ($cmd2str{$ecmd})
+           && ! defined ($ignore{$cmd2str{$ecmd}})) {
+           my ($key);
+           $key = $enode . "," . $cmd2str{$ecmd};
+           $ocmd{$key} = $ecmd;
+           $odefun{$key} = $cmd2defun{$ecmd};
+           push (@{$oproto{$key}}, $cmd2proto{$ecmd});
+       }
+    }
+}
+
+my $bad_cli_stomps = 102;
+# Currently we have $bad_cli_stomps.  This was determined by
+# running this script and counting up the collisions from what
+# was returned.
+#
+# When we have cli commands that map to the same function name, we
+# can introduce subtle bugs due to code not being called when
+# we think it is.
+#
+# If extract.pl fails with a error message and you've been
+# modifying the cli, then go back and fix your code to
+# not have cli command function collisions.
+#
+# If you've removed a cli overwrite, you can safely subtract
+# one from $bad_cli_stomps.  If you've added to the problem
+# please fix your code before submittal
+if ($cli_stomp != $bad_cli_stomps) {
+    warn "Expected $bad_cli_stomps command line stomps, but got $cli_stomp instead\n";
+    exit $cli_stomp;
+}
+
+# Check finaly alive $cmd;
+foreach (keys %odefun) {
+    my ($node, $str) = (split (/,/));
+    my ($cmd) = $ocmd{$_};
+    $live{$cmd} = $_;
+}
+
+# Output DEFSH
+foreach (keys %live) {
+    my ($proto);
+    my ($key);
+    $key = $live{$_};
+    $proto = join ("|", @{$oproto{$key}});
+    printf "DEFSH ($proto$odefun{$key})\n\n";
+}
+
+# Output install_element
+print <<EOF;
+void
+vtysh_init_cmd (void)
+{
+EOF
+
+foreach (keys %odefun) {
+    my ($node, $str) = (split (/,/));
+    $cmd = $ocmd{$_};
+    $cmd =~ s/_cmd/_cmd_vtysh/;
+    printf "  install_element ($node, &$cmd);\n";
+}
+
+print <<EOF
+}
+EOF
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
new file mode 100644 (file)
index 0000000..9a8aedd
--- /dev/null
@@ -0,0 +1,2876 @@
+/* Virtual terminal interface shell.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <sys/un.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "command.h"
+#include "memory.h"
+#include "vtysh/vtysh.h"
+#include "log.h"
+#include "bgpd/bgp_vty.h"
+#include "vrf.h"
+
+/* Struct VTY. */
+struct vty *vty;
+
+/* VTY shell pager name. */
+char *vtysh_pager_name = NULL;
+
+/* VTY shell client structure. */
+struct vtysh_client
+{
+  int fd;
+  const char *name;
+  int flag;
+  const char *path;
+} vtysh_client[] =
+{
+  { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH},
+  { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH},
+  { .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH},
+  { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH},
+  { .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH},
+  { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
+  { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
+  { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH},
+  { .fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .path = NHRP_VTYSH_PATH},
+};
+
+
+/* We need direct access to ripd to implement vtysh_exit_ripd_only. */
+static struct vtysh_client *ripd_client = NULL;
+
+/* Using integrated config from Quagga.conf. Default is no. */
+int vtysh_writeconfig_integrated = 0;
+
+extern char config_default[];
+
+static void
+vclient_close (struct vtysh_client *vclient)
+{
+  if (vclient->fd >= 0)
+    {
+      fprintf(stderr,
+             "Warning: closing connection to %s because of an I/O error!\n",
+             vclient->name);
+      close (vclient->fd);
+      vclient->fd = -1;
+    }
+}
+
+/* Return true if str begins with prefix, else return false */
+static int
+begins_with(const char *str, const char *prefix)
+{
+  if (!str || !prefix)
+    return 0;
+  size_t lenstr = strlen(str);
+  size_t lenprefix = strlen(prefix);
+  if (lenprefix >  lenstr)
+    return 0;
+  return strncmp(str, prefix, lenprefix) == 0;
+}
+
+/* Following filled with debug code to trace a problematic condition
+ * under load - it SHOULD handle it. */
+#define ERR_WHERE_STRING "vtysh(): vtysh_client_execute(): "
+static int
+vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
+{
+  int ret;
+  char *buf;
+  size_t bufsz;
+  char *pbuf;
+  size_t left;
+  char *eoln;
+  int nbytes;
+  int i;
+  int readln;
+  int numnulls = 0;
+
+  if (vclient->fd < 0)
+    return CMD_SUCCESS;
+
+  ret = write (vclient->fd, line, strlen (line) + 1);
+  if (ret <= 0)
+    {
+      vclient_close (vclient);
+      return CMD_SUCCESS;
+    }
+       
+  /* Allow enough room for buffer to read more than a few pages from socket. */
+  bufsz = 5 * getpagesize() + 1;
+  buf = XMALLOC(MTYPE_TMP, bufsz);
+  memset(buf, 0, bufsz);
+  pbuf = buf;
+
+  while (1)
+    {
+      if (pbuf >= ((buf + bufsz) -1))
+       {
+         fprintf (stderr, ERR_WHERE_STRING \
+                  "warning - pbuf beyond buffer end.\n");
+         XFREE(MTYPE_TMP, buf);
+         return CMD_WARNING;
+       }
+
+      readln = (buf + bufsz) - pbuf - 1;
+      nbytes = read (vclient->fd, pbuf, readln);
+
+      if (nbytes <= 0)
+       {
+
+         if (errno == EINTR)
+           continue;
+
+         fprintf(stderr, ERR_WHERE_STRING "(%u)", errno);
+         perror("");
+
+         if (errno == EAGAIN || errno == EIO)
+           continue;
+
+         vclient_close (vclient);
+         XFREE(MTYPE_TMP, buf);
+         return CMD_SUCCESS;
+       }
+      /* If we have already seen 3 nulls, then current byte is ret code */
+      if ((numnulls == 3) && (nbytes == 1))
+        {
+           ret = pbuf[0];
+           break;
+        }
+
+      pbuf[nbytes] = '\0';
+
+       /* If the config needs to be written in file or stdout */
+       if (fp)
+       {
+         fputs(pbuf, fp);
+         fflush (fp);
+       }
+
+       /* At max look last four bytes */
+       if (nbytes >= 4)
+       {
+         i = nbytes - 4;
+         numnulls = 0;
+       }
+       else
+         i = 0;
+
+       /* Count the numnulls */ 
+       while (i < nbytes && numnulls <3)
+       {
+         if (pbuf[i++] == '\0')
+            numnulls++;
+         else
+            numnulls = 0;
+       }
+       /* We might have seen 3 consecutive nulls so store the ret code before updating pbuf*/
+       ret = pbuf[nbytes-1];
+       pbuf += nbytes;
+
+       /* See if a line exists in buffer, if so parse and consume it, and
+        * reset read position. If 3 nulls has been encountered consume the buffer before 
+        * next read.
+        */
+       if (((eoln = strrchr(buf, '\n')) == NULL) && (numnulls<3))
+         continue;
+
+       if (eoln >= ((buf + bufsz) - 1))
+       {
+          fprintf (stderr, ERR_WHERE_STRING \
+               "warning - eoln beyond buffer end.\n");
+       }
+
+       /* If the config needs parsing, consume it */
+       if(!fp)
+         vtysh_config_parse(buf);
+
+       eoln++;
+       left = (size_t)(buf + bufsz - eoln);
+       /*
+        * This check is required since when a config line split between two consecutive reads, 
+        * then buf will have first half of config line and current read will bring rest of the 
+        * line. So in this case eoln will be 1 here, hence calculation of left will be wrong. 
+        * In this case we don't need to do memmove, because we have already seen 3 nulls.  
+        */
+       if(left < bufsz)
+         memmove(buf, eoln, left);
+
+       buf[bufsz-1] = '\0';
+       pbuf = buf + strlen(buf);
+       /* got 3 or more trailing NULs? */
+       if ((numnulls >=3) && (i < nbytes))
+       {
+          break;
+       }
+    }
+
+  if(!fp)
+    vtysh_config_parse (buf);
+
+  XFREE(MTYPE_TMP, buf);
+  return ret;
+}
+
+void
+vtysh_pager_init (void)
+{
+  char *pager_defined;
+
+  pager_defined = getenv ("VTYSH_PAGER");
+
+  if (pager_defined)
+    vtysh_pager_name = strdup (pager_defined);
+  else
+    vtysh_pager_name = strdup ("more");
+}
+
+/* Command execution over the vty interface. */
+static int
+vtysh_execute_func (const char *line, int pager)
+{
+  int ret, cmd_stat;
+  u_int i;
+  vector vline;
+  struct cmd_element *cmd;
+  FILE *fp = NULL;
+  int closepager = 0;
+  int tried = 0;
+  int saved_ret, saved_node;
+
+  /* Split readline string up into the vector. */
+  vline = cmd_make_strvec (line);
+
+  if (vline == NULL)
+    return CMD_SUCCESS;
+
+  saved_ret = ret = cmd_execute_command (vline, vty, &cmd, 1);
+  saved_node = vty->node;
+
+  /* If command doesn't succeeded in current node, try to walk up in node tree.
+   * Changing vty->node is enough to try it just out without actual walkup in
+   * the vtysh. */
+  while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING
+        && vty->node > CONFIG_NODE)
+    {
+      vty->node = node_parent(vty->node);
+      ret = cmd_execute_command (vline, vty, &cmd, 1);
+      tried++;
+    }
+
+  vty->node = saved_node;
+
+  /* If command succeeded in any other node than current (tried > 0) we have
+   * to move into node in the vtysh where it succeeded. */
+  if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING)
+    {
+      if ((saved_node == BGP_VPNV4_NODE || saved_node == BGP_VPNV6_NODE
+          || saved_node == BGP_ENCAP_NODE || saved_node == BGP_ENCAPV6_NODE
+           || saved_node == BGP_IPV4_NODE
+          || saved_node == BGP_IPV6_NODE || saved_node == BGP_IPV4M_NODE
+          || saved_node == BGP_IPV6M_NODE)
+         && (tried == 1))
+       {
+         vtysh_execute("exit-address-family");
+       }
+      else if ((saved_node == KEYCHAIN_KEY_NODE) && (tried == 1))
+       {
+         vtysh_execute("exit");
+       }
+      else if (tried)
+       {
+         vtysh_execute ("end");
+         vtysh_execute ("configure terminal");
+       }
+    }
+  /* If command didn't succeed in any node, continue with return value from
+   * first try. */
+  else if (tried)
+    {
+      ret = saved_ret;
+    }
+
+  cmd_free_strvec (vline);
+
+  cmd_stat = ret;
+  switch (ret)
+    {
+    case CMD_WARNING:
+      if (vty->type == VTY_FILE)
+       fprintf (stdout,"Warning...\n");
+      break;
+    case CMD_ERR_AMBIGUOUS:
+      fprintf (stdout,"%% Ambiguous command.\n");
+      break;
+    case CMD_ERR_NO_MATCH:
+      fprintf (stdout,"%% Unknown command.\n");
+      break;
+    case CMD_ERR_INCOMPLETE:
+      fprintf (stdout,"%% Command incomplete.\n");
+      break;
+    case CMD_SUCCESS_DAEMON:
+      {
+       /* FIXME: Don't open pager for exit commands. popen() causes problems
+        * if exited from vtysh at all. This hack shouldn't cause any problem
+        * but is really ugly. */
+       if (pager && vtysh_pager_name && (strncmp(line, "exit", 4) != 0))
+         {
+           fp = popen (vtysh_pager_name, "w");
+           if (fp == NULL)
+             {
+               perror ("popen failed for pager");
+               fp = stdout;
+             }
+           else
+             closepager=1;
+         }
+       else
+         fp = stdout;
+
+       if (! strcmp(cmd->string,"configure terminal"))
+         {
+           for (i = 0; i < array_size(vtysh_client); i++)
+             {
+               cmd_stat = vtysh_client_execute(&vtysh_client[i], line, fp);
+               if (cmd_stat == CMD_WARNING)
+                 break;
+             }
+
+           if (cmd_stat)
+             {
+               line = "end";
+               vline = cmd_make_strvec (line);
+
+               if (vline == NULL)
+                 {
+                   if (pager && vtysh_pager_name && fp && closepager)
+                     {
+                       if (pclose (fp) == -1)
+                         {
+                           perror ("pclose failed for pager");
+                         }
+                       fp = NULL;
+                     }
+                   return CMD_SUCCESS;
+                 }
+
+               ret = cmd_execute_command (vline, vty, &cmd, 1);
+               cmd_free_strvec (vline);
+               if (ret != CMD_SUCCESS_DAEMON)
+                 break;
+             }
+           else
+             if (cmd->func)
+               {
+                 (*cmd->func) (cmd, vty, 0, NULL);
+                 break;
+               }
+         }
+
+       cmd_stat = CMD_SUCCESS;
+       for (i = 0; i < array_size(vtysh_client); i++)
+         {
+           if (cmd->daemon & vtysh_client[i].flag)
+             {
+               cmd_stat = vtysh_client_execute(&vtysh_client[i], line, fp);
+               if (cmd_stat != CMD_SUCCESS)
+                 break;
+             }
+         }
+       if (cmd_stat != CMD_SUCCESS)
+         break;
+
+       if (cmd->func)
+         (*cmd->func) (cmd, vty, 0, NULL);
+      }
+    }
+  if (pager && vtysh_pager_name && fp && closepager)
+    {
+      if (pclose (fp) == -1)
+       {
+         perror ("pclose failed for pager");
+       }
+      fp = NULL;
+    }
+  return cmd_stat;
+}
+
+int
+vtysh_execute_no_pager (const char *line)
+{
+  return vtysh_execute_func (line, 0);
+}
+
+int
+vtysh_execute (const char *line)
+{
+  return vtysh_execute_func (line, 1);
+}
+
+/* Configration make from file. */
+int
+vtysh_config_from_file (struct vty *vty, FILE *fp)
+{
+  int ret;
+  struct cmd_element *cmd;
+
+  while (fgets (vty->buf, vty->max, fp))
+    {
+      ret = command_config_read_one_line (vty, &cmd, 1);
+
+      switch (ret)
+       {
+       case CMD_WARNING:
+         if (vty->type == VTY_FILE)
+           fprintf (stdout,"Warning...\n");
+         break;
+       case CMD_ERR_AMBIGUOUS:
+         fprintf (stdout,"%% Ambiguous command.\n");
+         break;
+       case CMD_ERR_NO_MATCH:
+         fprintf (stdout,"%% Unknown command: %s", vty->buf);
+         break;
+       case CMD_ERR_INCOMPLETE:
+         fprintf (stdout,"%% Command incomplete.\n");
+         break;
+       case CMD_SUCCESS_DAEMON:
+         {
+           u_int i;
+           int cmd_stat = CMD_SUCCESS;
+
+           for (i = 0; i < array_size(vtysh_client); i++)
+             {
+               if (cmd->daemon & vtysh_client[i].flag)
+                 {
+                   cmd_stat = vtysh_client_execute (&vtysh_client[i],
+                                                    vty->buf, stdout);
+                   if (cmd_stat != CMD_SUCCESS)
+                     break;
+                 }
+             }
+           if (cmd_stat != CMD_SUCCESS)
+             break;
+
+           if (cmd->func)
+             (*cmd->func) (cmd, vty, 0, NULL);
+         }
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* We don't care about the point of the cursor when '?' is typed. */
+static int
+vtysh_rl_describe (void)
+{
+  int ret;
+  unsigned int i;
+  vector vline;
+  vector describe;
+  int width;
+  struct cmd_token *token;
+
+  vline = cmd_make_strvec (rl_line_buffer);
+
+  /* In case of '> ?'. */
+  if (vline == NULL)
+    {
+      vline = vector_init (1);
+      vector_set (vline, NULL);
+    }
+  else 
+    if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
+      vector_set (vline, NULL);
+
+  describe = cmd_describe_command (vline, vty, &ret);
+
+  fprintf (stdout,"\n");
+
+  /* Ambiguous and no match error. */
+  switch (ret)
+    {
+    case CMD_ERR_AMBIGUOUS:
+      cmd_free_strvec (vline);
+      fprintf (stdout,"%% Ambiguous command.\n");
+      rl_on_new_line ();
+      return 0;
+      break;
+    case CMD_ERR_NO_MATCH:
+      cmd_free_strvec (vline);
+      fprintf (stdout,"%% There is no matched command.\n");
+      rl_on_new_line ();
+      return 0;
+      break;
+    }  
+
+  /* Get width of command string. */
+  width = 0;
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
+      {
+       int len;
+
+       if (token->cmd[0] == '\0')
+         continue;
+
+       len = strlen (token->cmd);
+       if (token->cmd[0] == '.')
+         len--;
+
+       if (width < len)
+         width = len;
+      }
+
+  for (i = 0; i < vector_active (describe); i++)
+    if ((token = vector_slot (describe, i)) != NULL)
+      {
+       if (token->cmd[0] == '\0')
+         continue;
+
+       if (! token->desc)
+         fprintf (stdout,"  %-s\n",
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd);
+       else
+         fprintf (stdout,"  %-*s  %s\n",
+                  width,
+                  token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+                  token->desc);
+      }
+
+  cmd_free_strvec (vline);
+  vector_free (describe);
+
+  rl_on_new_line();
+
+  return 0;
+}
+
+/* Result of cmd_complete_command() call will be stored here
+ * and used in new_completion() in order to put the space in
+ * correct places only. */
+int complete_status;
+
+static char *
+command_generator (const char *text, int state)
+{
+  vector vline;
+  static char **matched = NULL;
+  static int index = 0;
+
+  /* First call. */
+  if (! state)
+    {
+      index = 0;
+
+      if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+       return NULL;
+
+      vline = cmd_make_strvec (rl_line_buffer);
+      if (vline == NULL)
+       return NULL;
+
+      if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
+        vector_set (vline, NULL);
+
+      matched = cmd_complete_command (vline, vty, &complete_status);
+    }
+
+  if (matched && matched[index])
+    return matched[index++];
+
+  return NULL;
+}
+
+static char **
+new_completion (char *text, int start, int end)
+{
+  char **matches;
+
+  matches = rl_completion_matches (text, command_generator);
+
+  if (matches)
+    {
+      rl_point = rl_end;
+      if (complete_status != CMD_COMPLETE_FULL_MATCH)
+        /* only append a space on full match */
+        rl_completion_append_character = '\0';
+    }
+
+  return matches;
+}
+
+#if 0
+/* This function is not actually being used. */
+static char **
+vtysh_completion (char *text, int start, int end)
+{
+  int ret;
+  vector vline;
+  char **matched = NULL;
+
+  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+    return NULL;
+
+  vline = cmd_make_strvec (rl_line_buffer);
+  if (vline == NULL)
+    return NULL;
+
+  /* In case of 'help \t'. */
+  if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
+    vector_set (vline, '\0');
+
+  matched = cmd_complete_command (vline, vty, &ret);
+
+  cmd_free_strvec (vline);
+
+  return (char **) matched;
+}
+#endif
+
+/* Vty node structures. */
+static struct cmd_node bgp_node =
+{
+  BGP_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node rip_node =
+{
+  RIP_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node isis_node =
+{
+  ISIS_NODE,
+  "%s(config-router)# ",
+};
+
+static struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+};
+
+static struct cmd_node rmap_node =
+{
+  RMAP_NODE,
+  "%s(config-route-map)# "
+};
+
+static struct cmd_node zebra_node =
+{
+  ZEBRA_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node bgp_vpnv4_node =
+{
+  BGP_VPNV4_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_vpnv6_node =
+{
+  BGP_VPNV6_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_encap_node =
+{
+  BGP_ENCAP_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_encapv6_node =
+{
+  BGP_ENCAPV6_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv4_node =
+{
+  BGP_IPV4_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv4m_node =
+{
+  BGP_IPV4M_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv6_node =
+{
+  BGP_IPV6_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node bgp_ipv6m_node =
+{
+  BGP_IPV6M_NODE,
+  "%s(config-router-af)# "
+};
+
+static struct cmd_node ospf_node =
+{
+  OSPF_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node ripng_node =
+{
+  RIPNG_NODE,
+  "%s(config-router)# "
+};
+
+static struct cmd_node ospf6_node =
+{
+  OSPF6_NODE,
+  "%s(config-ospf6)# "
+};
+
+static struct cmd_node babel_node =
+{
+  BABEL_NODE,
+  "%s(config-babel)# "
+};
+
+static struct cmd_node keychain_node =
+{
+  KEYCHAIN_NODE,
+  "%s(config-keychain)# "
+};
+
+static struct cmd_node keychain_key_node =
+{
+  KEYCHAIN_KEY_NODE,
+  "%s(config-keychain-key)# "
+};
+
+struct cmd_node link_params_node =
+{
+  LINK_PARAMS_NODE,
+  "%s(config-link-params)# ",
+};
+
+/* Defined in lib/vty.c */
+extern struct cmd_node vty_node;
+
+/* When '^Z' is received from vty, move down to the enable mode. */
+static int
+vtysh_end (void)
+{
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+      /* Nothing to do. */
+      break;
+    default:
+      vty->node = ENABLE_NODE;
+      break;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_end_all,
+        vtysh_end_all_cmd,
+        "end",
+        "End current mode and change to enable mode\n")
+{
+  return vtysh_end ();
+}
+
+DEFUNSH (VTYSH_BGPD,
+        router_bgp,
+        router_bgp_cmd,
+        "router bgp " CMD_AS_RANGE,
+        ROUTER_STR
+        BGP_STR
+        AS_STR)
+{
+  vty->node = BGP_NODE;
+  return CMD_SUCCESS;
+}
+
+ALIAS_SH (VTYSH_BGPD,
+         router_bgp,
+         router_bgp_view_cmd,
+         "router bgp " CMD_AS_RANGE " view WORD",
+         ROUTER_STR
+         BGP_STR
+         AS_STR
+         "BGP view\n"
+         "view name\n")
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_vpnv4,
+        address_family_vpnv4_cmd,
+        "address-family vpnv4",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_VPNV4_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_vpnv4_unicast,
+        address_family_vpnv4_unicast_cmd,
+        "address-family vpnv4 unicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_VPNV4_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_vpnv6,
+        address_family_vpnv6_cmd,
+        "address-family vpnv6",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_VPNV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_vpnv6_unicast,
+        address_family_vpnv6_unicast_cmd,
+        "address-family vpnv6 unicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_VPNV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_encap,
+        address_family_encap_cmd,
+        "address-family encap",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_ENCAP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_encapv4,
+        address_family_encapv4_cmd,
+        "address-family encapv4",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_ENCAP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_encapv6,
+        address_family_encapv6_cmd,
+        "address-family encapv6",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_ENCAPV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_ipv4_unicast,
+        address_family_ipv4_unicast_cmd,
+        "address-family ipv4 unicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_IPV4_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_ipv4_multicast,
+        address_family_ipv4_multicast_cmd,
+        "address-family ipv4 multicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_IPV4M_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_ipv6,
+        address_family_ipv6_cmd,
+        "address-family ipv6",
+        "Enter Address Family command mode\n"
+        "Address family\n")
+{
+  vty->node = BGP_IPV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_ipv6_unicast,
+        address_family_ipv6_unicast_cmd,
+        "address-family ipv6 unicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_IPV6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+        address_family_ipv6_multicast,
+        address_family_ipv6_multicast_cmd,
+        "address-family ipv6 multicast",
+        "Enter Address Family command mode\n"
+        "Address family\n"
+        "Address Family Modifier\n")
+{
+  vty->node = BGP_IPV6M_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_RIPD,
+        key_chain,
+        key_chain_cmd,
+        "key chain WORD",
+        "Authentication key management\n"
+        "Key-chain management\n"
+        "Key-chain name\n")
+{
+  vty->node = KEYCHAIN_NODE;
+  return CMD_SUCCESS;
+}       
+
+DEFUNSH (VTYSH_RIPD,
+        key,
+        key_cmd,
+        "key <0-2147483647>",
+        "Configure a key\n"
+        "Key identifier number\n")
+{
+  vty->node = KEYCHAIN_KEY_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_RIPD,
+        router_rip,
+        router_rip_cmd,
+        "router rip",
+        ROUTER_STR
+        "RIP")
+{
+  vty->node = RIP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_RIPNGD,
+        router_ripng,
+        router_ripng_cmd,
+        "router ripng",
+        ROUTER_STR
+        "RIPng")
+{
+  vty->node = RIPNG_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_OSPFD,
+        router_ospf,
+        router_ospf_cmd,
+        "router ospf",
+        "Enable a routing process\n"
+        "Start OSPF configuration\n")
+{
+  vty->node = OSPF_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_OSPF6D,
+        router_ospf6,
+        router_ospf6_cmd,
+        "router ospf6",
+        OSPF6_ROUTER_STR
+        OSPF6_STR)
+{
+  vty->node = OSPF6_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ISISD,
+        router_isis,
+        router_isis_cmd,
+        "router isis WORD",
+        ROUTER_STR
+        "ISO IS-IS\n"
+        "ISO Routing area tag")
+{
+  vty->node = ISIS_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_RMAP,
+        route_map,
+        route_map_cmd,
+        "route-map WORD (deny|permit) <1-65535>",
+        "Create route-map or enter route-map command mode\n"
+        "Route map tag\n"
+        "Route map denies set operations\n"
+        "Route map permits set operations\n"
+        "Sequence to insert to/delete from existing route-map entry\n")
+{
+  vty->node = RMAP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_line_vty,
+        vtysh_line_vty_cmd,
+        "line vty",
+        "Configure a terminal line\n"
+        "Virtual terminal\n")
+{
+  vty->node = VTY_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_enable, 
+        vtysh_enable_cmd,
+        "enable",
+        "Turn on privileged mode command\n")
+{
+  vty->node = ENABLE_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_disable, 
+        vtysh_disable_cmd,
+        "disable",
+        "Turn off privileged mode command\n")
+{
+  if (vty->node == ENABLE_NODE)
+    vty->node = VIEW_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_config_terminal,
+        vtysh_config_terminal_cmd,
+        "configure terminal",
+        "Configuration from vty interface\n"
+        "Configuration terminal\n")
+{
+  vty->node = CONFIG_NODE;
+  return CMD_SUCCESS;
+}
+
+static int
+vtysh_exit (struct vty *vty)
+{
+  switch (vty->node)
+    {
+    case VIEW_NODE:
+    case ENABLE_NODE:
+      exit (0);
+      break;
+    case CONFIG_NODE:
+      vty->node = ENABLE_NODE;
+      break;
+    case INTERFACE_NODE:
+    case ZEBRA_NODE:
+    case BGP_NODE:
+    case RIP_NODE:
+    case RIPNG_NODE:
+    case OSPF_NODE:
+    case OSPF6_NODE:
+    case BABEL_NODE:
+    case ISIS_NODE:
+    case MASC_NODE:
+    case RMAP_NODE:
+    case VTY_NODE:
+    case KEYCHAIN_NODE:
+      vtysh_execute("end");
+      vtysh_execute("configure terminal");
+      vty->node = CONFIG_NODE;
+      break;
+    case BGP_VPNV4_NODE:
+    case BGP_VPNV6_NODE:
+    case BGP_ENCAP_NODE:
+    case BGP_ENCAPV6_NODE:
+    case BGP_IPV4_NODE:
+    case BGP_IPV4M_NODE:
+    case BGP_IPV6_NODE:
+    case BGP_IPV6M_NODE:
+      vty->node = BGP_NODE;
+      break;
+    case KEYCHAIN_KEY_NODE:
+      vty->node = KEYCHAIN_NODE;
+      break;
+    case LINK_PARAMS_NODE:
+      vty->node = INTERFACE_NODE;
+      break;
+    default:
+      break;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_exit_all,
+        vtysh_exit_all_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_all,
+       vtysh_quit_all_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_BGPD,
+        exit_address_family,
+        exit_address_family_cmd,
+        "exit-address-family",
+        "Exit from Address Family configuration mode\n")
+{
+  if (vty->node == BGP_IPV4_NODE
+      || vty->node == BGP_IPV4M_NODE
+      || vty->node == BGP_VPNV4_NODE
+      || vty->node == BGP_VPNV6_NODE
+      || vty->node == BGP_ENCAP_NODE
+      || vty->node == BGP_ENCAPV6_NODE
+      || vty->node == BGP_IPV6_NODE
+      || vty->node == BGP_IPV6M_NODE)
+    vty->node = BGP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ZEBRA,
+        vtysh_exit_zebra,
+        vtysh_exit_zebra_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_zebra,
+       vtysh_quit_zebra_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_RIPD,
+        vtysh_exit_ripd,
+        vtysh_exit_ripd_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_ripd,
+       vtysh_quit_ripd_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_RIPNGD,
+        vtysh_exit_ripngd,
+        vtysh_exit_ripngd_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_ripngd,
+       vtysh_quit_ripngd_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_RMAP,
+        vtysh_exit_rmap,
+        vtysh_exit_rmap_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_rmap,
+       vtysh_quit_rmap_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_BGPD,
+        vtysh_exit_bgpd,
+        vtysh_exit_bgpd_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_bgpd,
+       vtysh_quit_bgpd_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_OSPFD,
+        vtysh_exit_ospfd,
+        vtysh_exit_ospfd_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_ospfd,
+       vtysh_quit_ospfd_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_OSPF6D,
+        vtysh_exit_ospf6d,
+        vtysh_exit_ospf6d_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_ospf6d,
+       vtysh_quit_ospf6d_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_ISISD,
+        vtysh_exit_isisd,
+        vtysh_exit_isisd_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_isisd,
+       vtysh_quit_isisd_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_ALL,
+         vtysh_exit_line_vty,
+         vtysh_exit_line_vty_cmd,
+         "exit",
+         "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_line_vty,
+       vtysh_quit_line_vty_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUNSH (VTYSH_INTERFACE,
+        vtysh_interface,
+        vtysh_interface_cmd,
+        "interface IFNAME",
+        "Select an interface to configure\n"
+        "Interface's name\n")
+{
+  vty->node = INTERFACE_NODE;
+  return CMD_SUCCESS;
+}
+
+ALIAS_SH (VTYSH_ZEBRA,
+        vtysh_interface,
+        vtysh_interface_vrf_cmd,
+        "interface IFNAME " VRF_CMD_STR,
+        "Select an interface to configure\n"
+        "Interface's name\n"
+        VRF_CMD_HELP_STR)
+
+DEFSH (VTYSH_INTERFACE,
+       vtysh_no_interface_cmd,
+       "no interface IFNAME",
+       NO_STR
+       "Delete a pseudo interface's configuration\n"
+       "Interface's name\n")
+
+DEFSH (VTYSH_ZEBRA,
+       vtysh_no_interface_vrf_cmd,
+       "no interface IFNAME " VRF_CMD_STR,
+       NO_STR
+       "Delete a pseudo interface's configuration\n"
+       "Interface's name\n"
+       VRF_CMD_HELP_STR)
+
+/* TODO Implement interface description commands in ripngd, ospf6d
+ * and isisd. */
+DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD,
+       interface_desc_cmd,
+       "description .LINE",
+       "Interface specific description\n"
+       "Characters describing this interface\n")
+       
+DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD,
+       no_interface_desc_cmd,
+       "no description",
+       NO_STR
+       "Interface specific description\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       distribute_list_all_cmd,
+       "distribute-list WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       no_distribute_list_all_cmd,
+       "no distribute-list WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       distribute_list_cmd,
+       "distribute-list WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       no_distribute_list_cmd,
+       "no distribute-list WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       distribute_list_prefix_all_cmd,
+       "distribute-list prefix WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       no_distribute_list_prefix_all_cmd,
+       "no distribute-list prefix WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       distribute_list_prefix_cmd,
+       "distribute-list prefix WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPD|VTYSH_RIPNGD,
+       no_distribute_list_prefix_cmd,
+       "no distribute-list prefix WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPNGD,
+       ipv6_distribute_list_all_cmd,
+       "ipv6 distribute-list WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPNGD,
+       no_ipv6_distribute_list_all_cmd,
+       "no ipv6 distribute-list WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPNGD,
+       ipv6_distribute_list_cmd,
+       "ipv6 distribute-list WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPNGD,
+       no_ipv6_distribute_list_cmd,
+       "no ipv6 distribute-list WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Access-list name\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPNGD,
+       ipv6_distribute_list_prefix_all_cmd,
+       "ipv6 distribute-list prefix WORD (in|out)",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPNGD,
+       no_ipv6_distribute_list_prefix_all_cmd,
+       "no ipv6 distribute-list prefix WORD (in|out)",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n")
+
+DEFSH (VTYSH_RIPNGD,
+       ipv6_distribute_list_prefix_cmd,
+       "ipv6 distribute-list prefix WORD (in|out) WORD",
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFSH (VTYSH_RIPNGD,
+       no_ipv6_distribute_list_prefix_cmd,
+       "no ipv6 distribute-list prefix WORD (in|out) WORD",
+       NO_STR
+       "Filter networks in routing updates\n"
+       "Filter prefixes in routing updates\n"
+       "Name of an IP prefix-list\n"
+       "Filter incoming routing updates\n"
+       "Filter outgoing routing updates\n"
+       "Interface name\n")
+
+DEFUNSH (VTYSH_INTERFACE,
+        vtysh_exit_interface,
+        vtysh_exit_interface_cmd,
+        "exit",
+        "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_interface,
+       vtysh_quit_interface_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
+DEFUN (vtysh_show_thread,
+       vtysh_show_thread_cmd,
+       "show thread cpu [FILTER]",
+      SHOW_STR
+      "Thread information\n"
+      "Thread CPU usage\n"
+      "Display filter (rwtexb)\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+  char line[100];
+
+  sprintf(line, "show thread cpu %s\n", (argc == 1) ? argv[0] : "");
+  for (i = 0; i < array_size(vtysh_client); i++)
+    if ( vtysh_client[i].fd >= 0 )
+      {
+        fprintf (stdout, "Thread statistics for %s:\n",
+                 vtysh_client[i].name);
+        ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
+        fprintf (stdout,"\n");
+      }
+  return ret;
+}
+
+DEFUN (vtysh_show_work_queues,
+       vtysh_show_work_queues_cmd,
+       "show work-queues",
+       SHOW_STR
+       "Work Queue information\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+  char line[] = "show work-queues\n";
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    if ( vtysh_client[i].fd >= 0 )
+      {
+        fprintf (stdout, "Work queue statistics for %s:\n",
+                 vtysh_client[i].name);
+        ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
+        fprintf (stdout,"\n");
+      }
+
+  return ret;
+}
+
+DEFUN (vtysh_show_work_queues_daemon,
+       vtysh_show_work_queues_daemon_cmd,
+       "show work-queues (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd)",
+       SHOW_STR
+       "Work Queue information\n"
+       "For the zebra daemon\n"
+       "For the rip daemon\n"
+       "For the ripng daemon\n"
+       "For the ospf daemon\n"
+       "For the ospfv6 daemon\n"
+       "For the bgp daemon\n"
+       "For the isis daemon\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    {
+      if (begins_with(vtysh_client[i].name, argv[0]))
+        break;
+    }
+
+  ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n", stdout);
+
+  return ret;
+}
+
+DEFUNSH (VTYSH_ZEBRA,
+         vtysh_link_params,
+         vtysh_link_params_cmd,
+         "link-params",
+         LINK_PARAMS_STR
+         )
+{
+  vty->node = LINK_PARAMS_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ZEBRA,
+        exit_link_params,
+        exit_link_params_cmd,
+        "exit-link-params",
+        "Exit from Link Params configuration node\n")
+{
+  if (vty->node == LINK_PARAMS_NODE)
+    vty->node = INTERFACE_NODE;
+  return CMD_SUCCESS;
+}
+
+/* Memory */
+DEFUN (vtysh_show_memory,
+       vtysh_show_memory_cmd,
+       "show memory",
+       SHOW_STR
+       "Memory statistics\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+  char line[] = "show memory\n";
+  
+  for (i = 0; i < array_size(vtysh_client); i++)
+    if ( vtysh_client[i].fd >= 0 )
+      {
+        fprintf (stdout, "Memory statistics for %s:\n", 
+                 vtysh_client[i].name);
+        ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
+        fprintf (stdout,"\n");
+      }
+  
+  return ret;
+}
+
+/* Logging commands. */
+DEFUN (vtysh_show_logging,
+       vtysh_show_logging_cmd,
+       "show logging",
+       SHOW_STR
+       "Show current logging configuration\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+  char line[] = "show logging\n";
+  
+  for (i = 0; i < array_size(vtysh_client); i++)
+    if ( vtysh_client[i].fd >= 0 )
+      {
+        fprintf (stdout,"Logging configuration for %s:\n", 
+                 vtysh_client[i].name);
+        ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
+        fprintf (stdout,"\n");
+      }
+  
+  return ret;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_stdout,
+        vtysh_log_stdout_cmd,
+        "log stdout",
+        "Logging control\n"
+        "Set stdout logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_stdout_level,
+        vtysh_log_stdout_level_cmd,
+        "log stdout "LOG_LEVELS,
+        "Logging control\n"
+        "Set stdout logging level\n"
+        LOG_LEVEL_DESC)
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_stdout,
+        no_vtysh_log_stdout_cmd,
+        "no log stdout [LEVEL]",
+        NO_STR
+        "Logging control\n"
+        "Cancel logging to stdout\n"
+        "Logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_file,
+        vtysh_log_file_cmd,
+        "log file FILENAME",
+        "Logging control\n"
+        "Logging to file\n"
+        "Logging filename\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_file_level,
+        vtysh_log_file_level_cmd,
+        "log file FILENAME "LOG_LEVELS,
+        "Logging control\n"
+        "Logging to file\n"
+        "Logging filename\n"
+        LOG_LEVEL_DESC)
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_file,
+        no_vtysh_log_file_cmd,
+        "no log file [FILENAME]",
+        NO_STR
+        "Logging control\n"
+        "Cancel logging to file\n"
+        "Logging file name\n")
+{
+  return CMD_SUCCESS;
+}
+
+ALIAS_SH (VTYSH_ALL,
+         no_vtysh_log_file,
+         no_vtysh_log_file_level_cmd,
+         "no log file FILENAME LEVEL",
+         NO_STR
+         "Logging control\n"
+         "Cancel logging to file\n"
+         "Logging file name\n"
+         "Logging level\n")
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_monitor,
+        vtysh_log_monitor_cmd,
+        "log monitor",
+        "Logging control\n"
+        "Set terminal line (monitor) logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_monitor_level,
+        vtysh_log_monitor_level_cmd,
+        "log monitor "LOG_LEVELS,
+        "Logging control\n"
+        "Set terminal line (monitor) logging level\n"
+        LOG_LEVEL_DESC)
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_monitor,
+        no_vtysh_log_monitor_cmd,
+        "no log monitor [LEVEL]",
+        NO_STR
+        "Logging control\n"
+        "Disable terminal line (monitor) logging\n"
+        "Logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_syslog,
+        vtysh_log_syslog_cmd,
+        "log syslog",
+        "Logging control\n"
+        "Set syslog logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_syslog_level,
+        vtysh_log_syslog_level_cmd,
+        "log syslog "LOG_LEVELS,
+        "Logging control\n"
+        "Set syslog logging level\n"
+        LOG_LEVEL_DESC)
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_syslog,
+        no_vtysh_log_syslog_cmd,
+        "no log syslog [LEVEL]",
+        NO_STR
+        "Logging control\n"
+        "Cancel logging to syslog\n"
+        "Logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_facility,
+        vtysh_log_facility_cmd,
+        "log facility "LOG_FACILITIES,
+        "Logging control\n"
+        "Facility parameter for syslog messages\n"
+        LOG_FACILITY_DESC)
+
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_facility,
+        no_vtysh_log_facility_cmd,
+        "no log facility [FACILITY]",
+        NO_STR
+        "Logging control\n"
+        "Reset syslog facility to default (daemon)\n"
+        "Syslog facility\n")
+
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH_DEPRECATED (VTYSH_ALL,
+                   vtysh_log_trap,
+                   vtysh_log_trap_cmd,
+                   "log trap "LOG_LEVELS,
+                   "Logging control\n"
+                   "(Deprecated) Set logging level and default for all destinations\n"
+                   LOG_LEVEL_DESC)
+
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH_DEPRECATED (VTYSH_ALL,
+                   no_vtysh_log_trap,
+                   no_vtysh_log_trap_cmd,
+                   "no log trap [LEVEL]",
+                   NO_STR
+                   "Logging control\n"
+                   "Permit all logging information\n"
+                   "Logging level\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_record_priority,
+        vtysh_log_record_priority_cmd,
+        "log record-priority",
+        "Logging control\n"
+        "Log the priority of the message within the message\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_record_priority,
+        no_vtysh_log_record_priority_cmd,
+        "no log record-priority",
+        NO_STR
+        "Logging control\n"
+        "Do not log the priority of the message within the message\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_log_timestamp_precision,
+        vtysh_log_timestamp_precision_cmd,
+        "log timestamp precision <0-6>",
+        "Logging control\n"
+        "Timestamp configuration\n"
+        "Set the timestamp precision\n"
+        "Number of subsecond digits\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_log_timestamp_precision,
+        no_vtysh_log_timestamp_precision_cmd,
+        "no log timestamp precision",
+        NO_STR
+        "Logging control\n"
+        "Timestamp configuration\n"
+        "Reset the timestamp precision to the default value of 0\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_service_password_encrypt,
+        vtysh_service_password_encrypt_cmd,
+        "service password-encryption",
+        "Set up miscellaneous service\n"
+        "Enable encrypted passwords\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_service_password_encrypt,
+        no_vtysh_service_password_encrypt_cmd,
+        "no service password-encryption",
+        NO_STR
+        "Set up miscellaneous service\n"
+        "Enable encrypted passwords\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_config_password,
+        vtysh_password_cmd,
+        "password (8|) WORD",
+        "Assign the terminal connection password\n"
+        "Specifies a HIDDEN password will follow\n"
+        "dummy string \n"
+        "The HIDDEN line password string\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_password_text,
+        vtysh_password_text_cmd,
+        "password LINE",
+        "Assign the terminal connection password\n"
+        "The UNENCRYPTED (cleartext) line password\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_config_enable_password,
+        vtysh_enable_password_cmd,
+        "enable password (8|) WORD",
+        "Modify enable password parameters\n"
+        "Assign the privileged level password\n"
+        "Specifies a HIDDEN password will follow\n"
+        "dummy string \n"
+        "The HIDDEN 'enable' password string\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        vtysh_enable_password_text,
+        vtysh_enable_password_text_cmd,
+        "enable password LINE",
+        "Modify enable password parameters\n"
+        "Assign the privileged level password\n"
+        "The UNENCRYPTED (cleartext) 'enable' password\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_ALL,
+        no_vtysh_config_enable_password,
+        no_vtysh_enable_password_cmd,
+        "no enable password",
+        NO_STR
+        "Modify enable password parameters\n"
+        "Assign the privileged level password\n")
+{
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_write_terminal,
+       vtysh_write_terminal_cmd,
+       "write terminal",
+       "Write running configuration to memory, network, or terminal\n"
+       "Write to terminal\n")
+{
+  u_int i;
+  char line[] = "write terminal\n";
+  FILE *fp = NULL;
+
+  if (vtysh_pager_name)
+    {
+      fp = popen (vtysh_pager_name, "w");
+      if (fp == NULL)
+       {
+         perror ("popen");
+         exit (1);
+       }
+    }
+  else
+    fp = stdout;
+
+  vty_out (vty, "Building configuration...%s", VTY_NEWLINE);
+  vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
+          VTY_NEWLINE);
+  vty_out (vty, "!%s", VTY_NEWLINE);
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    vtysh_client_execute (&vtysh_client[i], line, NULL);
+
+  /* Integrate vtysh specific configuration. */
+  vtysh_config_write ();
+
+  vtysh_config_dump (fp);
+
+  if (vtysh_pager_name && fp)
+    {
+      fflush (fp);
+      if (pclose (fp) == -1)
+       {
+         perror ("pclose");
+         exit (1);
+       }
+      fp = NULL;
+    }
+
+  vty_out (vty, "end%s", VTY_NEWLINE);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_write_terminal_daemon,
+       vtysh_write_terminal_daemon_cmd,
+       "write terminal (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|babeld)",
+       "Write running configuration to memory, network, or terminal\n"
+       "Write to terminal\n"
+       "For the zebra daemon\n"
+       "For the rip daemon\n"
+       "For the ripng daemon\n"
+       "For the ospf daemon\n"
+       "For the ospfv6 daemon\n"
+       "For the bgp daemon\n"
+       "For the isis daemon\n"
+       "For the babel daemon\n")
+{
+  unsigned int i;
+  int ret = CMD_SUCCESS;
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    {
+      if (strcmp(vtysh_client[i].name, argv[0]) == 0)
+       break;
+    }
+
+  if (i == array_size(vtysh_client))
+    return CMD_ERR_NO_MATCH;
+
+  ret = vtysh_client_execute(&vtysh_client[i], "show running-config\n", stdout);
+
+  return ret;
+}
+
+DEFUN (vtysh_integrated_config,
+       vtysh_integrated_config_cmd,
+       "service integrated-vtysh-config",
+       "Set up miscellaneous service\n"
+       "Write configuration into integrated file\n")
+{
+  vtysh_writeconfig_integrated = 1;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_vtysh_integrated_config,
+       no_vtysh_integrated_config_cmd,
+       "no service integrated-vtysh-config",
+       NO_STR
+       "Set up miscellaneous service\n"
+       "Write configuration into integrated file\n")
+{
+  vtysh_writeconfig_integrated = 0;
+  return CMD_SUCCESS;
+}
+
+static int
+write_config_integrated(void)
+{
+  u_int i;
+  char line[] = "write terminal\n";
+  FILE *fp;
+  char *integrate_sav = NULL;
+
+  integrate_sav = malloc (strlen (integrate_default) +
+                         strlen (CONF_BACKUP_EXT) + 1);
+  strcpy (integrate_sav, integrate_default);
+  strcat (integrate_sav, CONF_BACKUP_EXT);
+
+  fprintf (stdout,"Building Configuration...\n");
+
+  /* Move current configuration file to backup config file. */
+  unlink (integrate_sav);
+  rename (integrate_default, integrate_sav);
+  free (integrate_sav);
+  fp = fopen (integrate_default, "w");
+  if (fp == NULL)
+    {
+      fprintf (stdout,"%% Can't open configuration file %s.\n",
+              integrate_default);
+      return CMD_SUCCESS;
+    }
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    vtysh_client_execute (&vtysh_client[i], line, NULL);
+
+  vtysh_config_write ();
+  vtysh_config_dump (fp);
+
+  fclose (fp);
+
+  if (chmod (integrate_default, CONFIGFILE_MASK) != 0)
+    {
+      fprintf (stdout,"%% Can't chmod configuration file %s: %s (%d)\n", 
+       integrate_default, safe_strerror(errno), errno);
+      return CMD_WARNING;
+    }
+
+  fprintf(stdout,"Integrated configuration saved to %s\n",integrate_default);
+
+  fprintf (stdout,"[OK]\n");
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_write_memory,
+       vtysh_write_memory_cmd,
+       "write memory",
+       "Write running configuration to memory, network, or terminal\n"
+       "Write configuration to the file (same as write file)\n")
+{
+  int ret = CMD_SUCCESS;
+  char line[] = "write memory\n";
+  u_int i;
+  
+  /* If integrated Quagga.conf explicitely set. */
+  if (vtysh_writeconfig_integrated)
+    return write_config_integrated();
+
+  fprintf (stdout,"Building Configuration...\n");
+         
+  for (i = 0; i < array_size(vtysh_client); i++)
+    ret = vtysh_client_execute (&vtysh_client[i], line, stdout);
+  
+  fprintf (stdout,"[OK]\n");
+
+  return ret;
+}
+
+ALIAS (vtysh_write_memory,
+       vtysh_copy_runningconfig_startupconfig_cmd,
+       "copy running-config startup-config",  
+       "Copy from one file to another\n"
+       "Copy from current system configuration\n"
+       "Copy to startup configuration\n")
+
+ALIAS (vtysh_write_memory,
+       vtysh_write_file_cmd,
+       "write file",
+       "Write running configuration to memory, network, or terminal\n"
+       "Write configuration to the file (same as write memory)\n")
+
+ALIAS (vtysh_write_memory,
+       vtysh_write_cmd,
+       "write",
+       "Write running configuration to memory, network, or terminal\n")
+
+ALIAS (vtysh_write_terminal,
+       vtysh_show_running_config_cmd,
+       "show running-config",
+       SHOW_STR
+       "Current operating configuration\n")
+
+ALIAS (vtysh_write_terminal_daemon,
+       vtysh_show_running_config_daemon_cmd,
+       "show running-config (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|babeld)",
+       SHOW_STR
+       "Current operating configuration\n"
+       "For the zebra daemon\n"
+       "For the rip daemon\n"
+       "For the ripng daemon\n"
+       "For the ospf daemon\n"
+       "For the ospfv6 daemon\n"
+       "For the bgp daemon\n"
+       "For the isis daemon\n"
+       "For the babel daemon\n")
+
+DEFUN (vtysh_terminal_length,
+       vtysh_terminal_length_cmd,
+       "terminal length <0-512>",
+       "Set terminal line parameters\n"
+       "Set number of lines on a screen\n"
+       "Number of lines on screen (0 for no pausing)\n")
+{
+  int lines;
+  char *endptr = NULL;
+  char default_pager[10];
+
+  lines = strtol (argv[0], &endptr, 10);
+  if (lines < 0 || lines > 512 || *endptr != '\0')
+    {
+      vty_out (vty, "length is malformed%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (vtysh_pager_name)
+    {
+      free (vtysh_pager_name);
+      vtysh_pager_name = NULL;
+    }
+
+  if (lines != 0)
+    {
+      snprintf(default_pager, 10, "more -%i", lines);
+      vtysh_pager_name = strdup (default_pager);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_terminal_no_length,
+       vtysh_terminal_no_length_cmd,
+       "terminal no length",
+       "Set terminal line parameters\n"
+       NO_STR
+       "Set number of lines on a screen\n")
+{
+  if (vtysh_pager_name)
+    {
+      free (vtysh_pager_name);
+      vtysh_pager_name = NULL;
+    }
+
+  vtysh_pager_init();
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_show_daemons,
+       vtysh_show_daemons_cmd,
+       "show daemons",
+       SHOW_STR
+       "Show list of running daemons\n")
+{
+  u_int i;
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    if ( vtysh_client[i].fd >= 0 )
+      vty_out(vty, " %s", vtysh_client[i].name);
+  vty_out(vty, "%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+/* Execute command in child process. */
+static int
+execute_command (const char *command, int argc, const char *arg1,
+                const char *arg2)
+{
+  pid_t pid;
+  int status;
+
+  /* Call fork(). */
+  pid = fork ();
+
+  if (pid < 0)
+    {
+      /* Failure of fork(). */
+      fprintf (stderr, "Can't fork: %s\n", safe_strerror (errno));
+      exit (1);
+    }
+  else if (pid == 0)
+    {
+      /* This is child process. */
+      switch (argc)
+       {
+       case 0:
+         execlp (command, command, (const char *)NULL);
+         break;
+       case 1:
+         execlp (command, command, arg1, (const char *)NULL);
+         break;
+       case 2:
+         execlp (command, command, arg1, arg2, (const char *)NULL);
+         break;
+       }
+
+      /* When execlp suceed, this part is not executed. */
+      fprintf (stderr, "Can't execute %s: %s\n", command, safe_strerror (errno));
+      exit (1);
+    }
+  else
+    {
+      /* This is parent. */
+      execute_flag = 1;
+      wait4 (pid, &status, 0, NULL);
+      execute_flag = 0;
+    }
+  return 0;
+}
+
+DEFUN (vtysh_ping,
+       vtysh_ping_cmd,
+       "ping WORD",
+       "Send echo messages\n"
+       "Ping destination address or hostname\n")
+{
+  execute_command ("ping", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (vtysh_ping,
+       vtysh_ping_ip_cmd,
+       "ping ip WORD",
+       "Send echo messages\n"
+       "IP echo\n"
+       "Ping destination address or hostname\n")
+
+DEFUN (vtysh_traceroute,
+       vtysh_traceroute_cmd,
+       "traceroute WORD",
+       "Trace route to destination\n"
+       "Trace route to destination address or hostname\n")
+{
+  execute_command ("traceroute", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+
+ALIAS (vtysh_traceroute,
+       vtysh_traceroute_ip_cmd,
+       "traceroute ip WORD",
+       "Trace route to destination\n"
+       "IP trace\n"
+       "Trace route to destination address or hostname\n")
+
+#ifdef HAVE_IPV6
+DEFUN (vtysh_ping6,
+       vtysh_ping6_cmd,
+       "ping ipv6 WORD",
+       "Send echo messages\n"
+       "IPv6 echo\n"
+       "Ping destination address or hostname\n")
+{
+  execute_command ("ping6", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_traceroute6,
+       vtysh_traceroute6_cmd,
+       "traceroute ipv6 WORD",
+       "Trace route to destination\n"
+       "IPv6 trace\n"
+       "Trace route to destination address or hostname\n")
+{
+  execute_command ("traceroute6", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+#endif
+
+DEFUN (vtysh_telnet,
+       vtysh_telnet_cmd,
+       "telnet WORD",
+       "Open a telnet connection\n"
+       "IP address or hostname of a remote system\n")
+{
+  execute_command ("telnet", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_telnet_port,
+       vtysh_telnet_port_cmd,
+       "telnet WORD PORT",
+       "Open a telnet connection\n"
+       "IP address or hostname of a remote system\n"
+       "TCP Port number\n")
+{
+  execute_command ("telnet", 2, argv[0], argv[1]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_ssh,
+       vtysh_ssh_cmd,
+       "ssh WORD",
+       "Open an ssh connection\n"
+       "[user@]host\n")
+{
+  execute_command ("ssh", 1, argv[0], NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_start_shell,
+       vtysh_start_shell_cmd,
+       "start-shell",
+       "Start UNIX shell\n")
+{
+  execute_command ("sh", 0, NULL, NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_start_bash,
+       vtysh_start_bash_cmd,
+       "start-shell bash",
+       "Start UNIX shell\n"
+       "Start bash\n")
+{
+  execute_command ("bash", 0, NULL, NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vtysh_start_zsh,
+       vtysh_start_zsh_cmd,
+       "start-shell zsh",
+       "Start UNIX shell\n"
+       "Start Z shell\n")
+{
+  execute_command ("zsh", 0, NULL, NULL);
+  return CMD_SUCCESS;
+}
+
+static void
+vtysh_install_default (enum node_type node)
+{
+  install_element (node, &config_list_cmd);
+}
+
+/* Making connection to protocol daemon. */
+static int
+vtysh_connect (struct vtysh_client *vclient)
+{
+  int ret;
+  int sock, len;
+  struct sockaddr_un addr;
+  struct stat s_stat;
+
+  /* Stat socket to see if we have permission to access it. */
+  ret = stat (vclient->path, &s_stat);
+  if (ret < 0 && errno != ENOENT)
+    {
+      fprintf  (stderr, "vtysh_connect(%s): stat = %s\n", 
+               vclient->path, safe_strerror(errno)); 
+      exit(1);
+    }
+  
+  if (ret >= 0)
+    {
+      if (! S_ISSOCK(s_stat.st_mode))
+       {
+         fprintf (stderr, "vtysh_connect(%s): Not a socket\n",
+                  vclient->path);
+         exit (1);
+       }
+      
+    }
+
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+#ifdef DEBUG
+      fprintf(stderr, "vtysh_connect(%s): socket = %s\n", vclient->path,
+             safe_strerror(errno));
+#endif /* DEBUG */
+      return -1;
+    }
+
+  memset (&addr, 0, sizeof (struct sockaddr_un));
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, vclient->path, strlen (vclient->path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+  len = addr.sun_len = SUN_LEN(&addr);
+#else
+  len = sizeof (addr.sun_family) + strlen (addr.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+  ret = connect (sock, (struct sockaddr *) &addr, len);
+  if (ret < 0)
+    {
+#ifdef DEBUG
+      fprintf(stderr, "vtysh_connect(%s): connect = %s\n", vclient->path,
+             safe_strerror(errno));
+#endif /* DEBUG */
+      close (sock);
+      return -1;
+    }
+  vclient->fd = sock;
+
+  return 0;
+}
+
+int
+vtysh_connect_all(const char *daemon_name)
+{
+  u_int i;
+  int rc = 0;
+  int matches = 0;
+
+  for (i = 0; i < array_size(vtysh_client); i++)
+    {
+      if (!daemon_name || !strcmp(daemon_name, vtysh_client[i].name))
+       {
+         matches++;
+         if (vtysh_connect(&vtysh_client[i]) == 0)
+           rc++;
+         /* We need direct access to ripd in vtysh_exit_ripd_only. */
+         if (vtysh_client[i].flag == VTYSH_RIPD)
+           ripd_client = &vtysh_client[i];
+       }
+    }
+  if (!matches)
+    fprintf(stderr, "Error: no daemons match name %s!\n", daemon_name);
+  return rc;
+}
+
+/* To disable readline's filename completion. */
+static char *
+vtysh_completion_entry_function (const char *ignore, int invoking_key)
+{
+  return NULL;
+}
+
+void
+vtysh_readline_init (void)
+{
+  /* readline related settings. */
+  rl_bind_key ('?', (rl_command_func_t *) vtysh_rl_describe);
+  rl_completion_entry_function = vtysh_completion_entry_function;
+  rl_attempted_completion_function = (rl_completion_func_t *)new_completion;
+}
+
+char *
+vtysh_prompt (void)
+{
+  static struct utsname names;
+  static char buf[100];
+  const char*hostname;
+  extern struct host host;
+
+  hostname = host.name;
+
+  if (!hostname)
+    {
+      if (!names.nodename[0])
+       uname (&names);
+      hostname = names.nodename;
+    }
+
+  snprintf (buf, sizeof buf, cmd_prompt (vty->node), hostname);
+
+  return buf;
+}
+
+void
+vtysh_init_vty (void)
+{
+  /* Make vty structure. */
+  vty = vty_new ();
+  vty->type = VTY_SHELL;
+  vty->node = VIEW_NODE;
+
+  /* Initialize commands. */
+  cmd_init (0);
+
+  /* Install nodes. */
+  install_node (&bgp_node, NULL);
+  install_node (&rip_node, NULL);
+  install_node (&interface_node, NULL);
+  install_node (&link_params_node, NULL);
+  install_node (&rmap_node, NULL);
+  install_node (&zebra_node, NULL);
+  install_node (&bgp_vpnv4_node, NULL);
+  install_node (&bgp_vpnv6_node, NULL);
+  install_node (&bgp_encap_node, NULL);
+  install_node (&bgp_encapv6_node, NULL);
+  install_node (&bgp_ipv4_node, NULL);
+  install_node (&bgp_ipv4m_node, NULL);
+/* #ifdef HAVE_IPV6 */
+  install_node (&bgp_ipv6_node, NULL);
+  install_node (&bgp_ipv6m_node, NULL);
+/* #endif */
+  install_node (&ospf_node, NULL);
+/* #ifdef HAVE_IPV6 */
+  install_node (&ripng_node, NULL);
+  install_node (&ospf6_node, NULL);
+/* #endif */
+  install_node (&babel_node, NULL);
+  install_node (&keychain_node, NULL);
+  install_node (&keychain_key_node, NULL);
+  install_node (&isis_node, NULL);
+  install_node (&vty_node, NULL);
+
+  vtysh_install_default (VIEW_NODE);
+  vtysh_install_default (ENABLE_NODE);
+  vtysh_install_default (CONFIG_NODE);
+  vtysh_install_default (BGP_NODE);
+  vtysh_install_default (RIP_NODE);
+  vtysh_install_default (INTERFACE_NODE);
+  vtysh_install_default (LINK_PARAMS_NODE);
+  vtysh_install_default (RMAP_NODE);
+  vtysh_install_default (ZEBRA_NODE);
+  vtysh_install_default (BGP_VPNV4_NODE);
+  vtysh_install_default (BGP_VPNV6_NODE);
+  vtysh_install_default (BGP_ENCAP_NODE);
+  vtysh_install_default (BGP_ENCAPV6_NODE);
+  vtysh_install_default (BGP_IPV4_NODE);
+  vtysh_install_default (BGP_IPV4M_NODE);
+  vtysh_install_default (BGP_IPV6_NODE);
+  vtysh_install_default (BGP_IPV6M_NODE);
+  vtysh_install_default (OSPF_NODE);
+  vtysh_install_default (RIPNG_NODE);
+  vtysh_install_default (OSPF6_NODE);
+  vtysh_install_default (BABEL_NODE);
+  vtysh_install_default (ISIS_NODE);
+  vtysh_install_default (KEYCHAIN_NODE);
+  vtysh_install_default (KEYCHAIN_KEY_NODE);
+  vtysh_install_default (VTY_NODE);
+
+  install_element (VIEW_NODE, &vtysh_enable_cmd);
+  install_element (ENABLE_NODE, &vtysh_config_terminal_cmd);
+  install_element (ENABLE_NODE, &vtysh_disable_cmd);
+
+  /* "exit" command. */
+  install_element (VIEW_NODE, &vtysh_exit_all_cmd);
+  install_element (VIEW_NODE, &vtysh_quit_all_cmd);
+  install_element (CONFIG_NODE, &vtysh_exit_all_cmd);
+  /* install_element (CONFIG_NODE, &vtysh_quit_all_cmd); */
+  install_element (ENABLE_NODE, &vtysh_exit_all_cmd);
+  install_element (ENABLE_NODE, &vtysh_quit_all_cmd);
+  install_element (RIP_NODE, &vtysh_exit_ripd_cmd);
+  install_element (RIP_NODE, &vtysh_quit_ripd_cmd);
+  install_element (RIPNG_NODE, &vtysh_exit_ripngd_cmd);
+  install_element (RIPNG_NODE, &vtysh_quit_ripngd_cmd);
+  install_element (OSPF_NODE, &vtysh_exit_ospfd_cmd);
+  install_element (OSPF_NODE, &vtysh_quit_ospfd_cmd);
+  install_element (OSPF6_NODE, &vtysh_exit_ospf6d_cmd);
+  install_element (OSPF6_NODE, &vtysh_quit_ospf6d_cmd);
+  install_element (BGP_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_VPNV4_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_VPNV4_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_VPNV6_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_VPNV6_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_ENCAP_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_ENCAP_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_ENCAPV6_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_ENCAPV6_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_IPV4_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_IPV4M_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_IPV6_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (ISIS_NODE, &vtysh_exit_isisd_cmd);
+  install_element (ISIS_NODE, &vtysh_quit_isisd_cmd);
+  install_element (KEYCHAIN_NODE, &vtysh_exit_ripd_cmd);
+  install_element (KEYCHAIN_NODE, &vtysh_quit_ripd_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &vtysh_exit_ripd_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd);
+  install_element (RMAP_NODE, &vtysh_exit_rmap_cmd);
+  install_element (RMAP_NODE, &vtysh_quit_rmap_cmd);
+  install_element (VTY_NODE, &vtysh_exit_line_vty_cmd);
+  install_element (VTY_NODE, &vtysh_quit_line_vty_cmd);
+
+  /* "end" command. */
+  install_element (CONFIG_NODE, &vtysh_end_all_cmd);
+  install_element (ENABLE_NODE, &vtysh_end_all_cmd);
+  install_element (RIP_NODE, &vtysh_end_all_cmd);
+  install_element (RIPNG_NODE, &vtysh_end_all_cmd);
+  install_element (OSPF_NODE, &vtysh_end_all_cmd);
+  install_element (OSPF6_NODE, &vtysh_end_all_cmd);
+  install_element (BABEL_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_IPV4_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_IPV4M_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_VPNV4_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_VPNV6_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_ENCAP_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd);
+  install_element (ISIS_NODE, &vtysh_end_all_cmd);
+  install_element (KEYCHAIN_NODE, &vtysh_end_all_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd);
+  install_element (RMAP_NODE, &vtysh_end_all_cmd);
+  install_element (VTY_NODE, &vtysh_end_all_cmd);
+
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+  install_element (INTERFACE_NODE, &vtysh_end_all_cmd);
+  install_element (INTERFACE_NODE, &vtysh_exit_interface_cmd);
+  install_element (LINK_PARAMS_NODE, &exit_link_params_cmd);
+  install_element (LINK_PARAMS_NODE, &vtysh_end_all_cmd);
+  install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd);
+  install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd);
+  install_element (CONFIG_NODE, &router_rip_cmd);
+#ifdef HAVE_IPV6
+  install_element (CONFIG_NODE, &router_ripng_cmd);
+#endif
+  install_element (CONFIG_NODE, &router_ospf_cmd);
+#ifdef HAVE_IPV6
+  install_element (CONFIG_NODE, &router_ospf6_cmd);
+#endif
+  install_element (CONFIG_NODE, &router_isis_cmd);
+  install_element (CONFIG_NODE, &router_bgp_cmd);
+  install_element (CONFIG_NODE, &router_bgp_view_cmd);
+  install_element (BGP_NODE, &address_family_vpnv4_cmd);
+  install_element (BGP_NODE, &address_family_vpnv4_unicast_cmd);
+  install_element (BGP_NODE, &address_family_vpnv6_cmd);
+  install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd);
+  install_element (BGP_NODE, &address_family_encap_cmd);
+  install_element (BGP_NODE, &address_family_encapv6_cmd);
+  install_element (BGP_NODE, &address_family_ipv4_unicast_cmd);
+  install_element (BGP_NODE, &address_family_ipv4_multicast_cmd);
+#ifdef HAVE_IPV6
+  install_element (BGP_NODE, &address_family_ipv6_cmd);
+  install_element (BGP_NODE, &address_family_ipv6_unicast_cmd);
+  install_element (BGP_NODE, &address_family_ipv6_multicast_cmd);
+#endif
+  install_element (BGP_VPNV4_NODE, &exit_address_family_cmd);
+  install_element (BGP_VPNV6_NODE, &exit_address_family_cmd);
+  install_element (BGP_ENCAP_NODE, &exit_address_family_cmd);
+  install_element (BGP_ENCAPV6_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV4_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV4M_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV6_NODE, &exit_address_family_cmd);
+  install_element (BGP_IPV6M_NODE, &exit_address_family_cmd);
+  install_element (CONFIG_NODE, &key_chain_cmd);
+  install_element (CONFIG_NODE, &route_map_cmd);
+  install_element (CONFIG_NODE, &vtysh_line_vty_cmd);
+  install_element (KEYCHAIN_NODE, &key_cmd);
+  install_element (KEYCHAIN_NODE, &key_chain_cmd);
+  install_element (KEYCHAIN_KEY_NODE, &key_chain_cmd);
+  install_element (CONFIG_NODE, &vtysh_interface_cmd);
+  install_element (CONFIG_NODE, &vtysh_no_interface_cmd);
+  install_element (CONFIG_NODE, &vtysh_interface_vrf_cmd);
+  install_element (CONFIG_NODE, &vtysh_no_interface_vrf_cmd);
+  install_element (INTERFACE_NODE, &vtysh_link_params_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_running_config_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_running_config_daemon_cmd);
+  install_element (ENABLE_NODE, &vtysh_copy_runningconfig_startupconfig_cmd);
+  install_element (ENABLE_NODE, &vtysh_write_file_cmd);
+  install_element (ENABLE_NODE, &vtysh_write_cmd);
+  /* distribute-list commands. (based on lib/distribute.c distribute_list_init()) */
+  install_element (RIP_NODE, &distribute_list_all_cmd);
+  install_element (RIP_NODE, &no_distribute_list_all_cmd);
+  install_element (RIP_NODE, &distribute_list_cmd);
+  install_element (RIP_NODE, &no_distribute_list_cmd);
+  install_element (RIP_NODE, &distribute_list_prefix_all_cmd);
+  install_element (RIP_NODE, &no_distribute_list_prefix_all_cmd);
+  install_element (RIP_NODE, &distribute_list_prefix_cmd);
+  install_element (RIP_NODE, &no_distribute_list_prefix_cmd);
+  install_element (RIPNG_NODE, &ipv6_distribute_list_all_cmd);
+  install_element (RIPNG_NODE, &no_ipv6_distribute_list_all_cmd);
+  install_element (RIPNG_NODE, &ipv6_distribute_list_cmd);
+  install_element (RIPNG_NODE, &no_ipv6_distribute_list_cmd);
+  install_element (RIPNG_NODE, &ipv6_distribute_list_prefix_all_cmd);
+  install_element (RIPNG_NODE, &no_ipv6_distribute_list_prefix_all_cmd);
+  install_element (RIPNG_NODE, &ipv6_distribute_list_prefix_cmd);
+  install_element (RIPNG_NODE, &no_ipv6_distribute_list_prefix_cmd);
+  install_element (RIPNG_NODE, &distribute_list_all_cmd);
+  install_element (RIPNG_NODE, &no_distribute_list_all_cmd);
+  install_element (RIPNG_NODE, &distribute_list_cmd);
+  install_element (RIPNG_NODE, &no_distribute_list_cmd);
+  install_element (RIPNG_NODE, &distribute_list_prefix_all_cmd);
+  install_element (RIPNG_NODE, &no_distribute_list_prefix_all_cmd);
+  install_element (RIPNG_NODE, &distribute_list_prefix_cmd);
+  install_element (RIPNG_NODE, &no_distribute_list_prefix_cmd);
+
+  /* "write terminal" command. */
+  install_element (ENABLE_NODE, &vtysh_write_terminal_cmd);
+  install_element (ENABLE_NODE, &vtysh_write_terminal_daemon_cmd);
+
+  install_element (CONFIG_NODE, &vtysh_integrated_config_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_integrated_config_cmd);
+
+  /* "write memory" command. */
+  install_element (ENABLE_NODE, &vtysh_write_memory_cmd);
+
+  install_element (VIEW_NODE, &vtysh_terminal_length_cmd);
+  install_element (ENABLE_NODE, &vtysh_terminal_length_cmd);
+  install_element (VIEW_NODE, &vtysh_terminal_no_length_cmd);
+  install_element (ENABLE_NODE, &vtysh_terminal_no_length_cmd);
+  install_element (VIEW_NODE, &vtysh_show_daemons_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_daemons_cmd);
+
+  install_element (VIEW_NODE, &vtysh_ping_cmd);
+  install_element (VIEW_NODE, &vtysh_ping_ip_cmd);
+  install_element (VIEW_NODE, &vtysh_traceroute_cmd);
+  install_element (VIEW_NODE, &vtysh_traceroute_ip_cmd);
+#ifdef HAVE_IPV6
+  install_element (VIEW_NODE, &vtysh_ping6_cmd);
+  install_element (VIEW_NODE, &vtysh_traceroute6_cmd);
+#endif
+  install_element (VIEW_NODE, &vtysh_telnet_cmd);
+  install_element (VIEW_NODE, &vtysh_telnet_port_cmd);
+  install_element (VIEW_NODE, &vtysh_ssh_cmd);
+  install_element (ENABLE_NODE, &vtysh_ping_cmd);
+  install_element (ENABLE_NODE, &vtysh_ping_ip_cmd);
+  install_element (ENABLE_NODE, &vtysh_traceroute_cmd);
+  install_element (ENABLE_NODE, &vtysh_traceroute_ip_cmd);
+#ifdef HAVE_IPV6
+  install_element (ENABLE_NODE, &vtysh_ping6_cmd);
+  install_element (ENABLE_NODE, &vtysh_traceroute6_cmd);
+#endif
+  install_element (ENABLE_NODE, &vtysh_telnet_cmd);
+  install_element (ENABLE_NODE, &vtysh_telnet_port_cmd);
+  install_element (ENABLE_NODE, &vtysh_ssh_cmd);
+  install_element (ENABLE_NODE, &vtysh_start_shell_cmd);
+  install_element (ENABLE_NODE, &vtysh_start_bash_cmd);
+  install_element (ENABLE_NODE, &vtysh_start_zsh_cmd);
+  
+  install_element (VIEW_NODE, &vtysh_show_memory_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_memory_cmd);
+
+  install_element (VIEW_NODE, &vtysh_show_work_queues_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_work_queues_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_work_queues_daemon_cmd);
+  install_element (VIEW_NODE, &vtysh_show_work_queues_daemon_cmd);
+
+  install_element (VIEW_NODE, &vtysh_show_thread_cmd);
+  install_element (ENABLE_NODE, &vtysh_show_thread_cmd);
+
+  /* Logging */
+  install_element (ENABLE_NODE, &vtysh_show_logging_cmd);
+  install_element (VIEW_NODE, &vtysh_show_logging_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_stdout_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_stdout_level_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_stdout_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_file_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_file_level_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_file_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_file_level_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_monitor_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_monitor_level_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_monitor_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_syslog_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_syslog_level_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_syslog_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_trap_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_trap_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_facility_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_facility_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_record_priority_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_record_priority_cmd);
+  install_element (CONFIG_NODE, &vtysh_log_timestamp_precision_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_log_timestamp_precision_cmd);
+
+  install_element (CONFIG_NODE, &vtysh_service_password_encrypt_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd);
+
+  install_element (CONFIG_NODE, &vtysh_password_cmd);
+  install_element (CONFIG_NODE, &vtysh_password_text_cmd);
+  install_element (CONFIG_NODE, &vtysh_enable_password_cmd);
+  install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
+  install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
+
+}
diff --git a/vtysh/vtysh.conf.sample b/vtysh/vtysh.conf.sample
new file mode 100644 (file)
index 0000000..4e0a2be
--- /dev/null
@@ -0,0 +1,7 @@
+!
+! Sample configuration file for vtysh.
+!
+!service integrated-vtysh-config
+!hostname quagga-router
+!username root nopassword
+!
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
new file mode 100644 (file)
index 0000000..02ff7fd
--- /dev/null
@@ -0,0 +1,72 @@
+/* Virtual terminal interface shell.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef VTYSH_H
+#define VTYSH_H
+
+#define VTYSH_ZEBRA  0x01
+#define VTYSH_RIPD   0x02
+#define VTYSH_RIPNGD 0x04
+#define VTYSH_OSPFD  0x08
+#define VTYSH_OSPF6D 0x10
+#define VTYSH_BGPD   0x20
+#define VTYSH_ISISD  0x40
+#define VTYSH_BABELD  0x80
+#define VTYSH_PIMD   0x100
+#define VTYSH_NHRPD  0x200
+#define VTYSH_ALL        VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD|VTYSH_NHRPD
+#define VTYSH_RMAP       VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_BABELD
+#define VTYSH_INTERFACE          VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD|VTYSH_NHRPD
+
+/* vtysh local configuration file. */
+#define VTYSH_DEFAULT_CONFIG "vtysh.conf"
+
+void vtysh_init_vty (void);
+void vtysh_init_cmd (void);
+extern int vtysh_connect_all (const char *optional_daemon_name);
+void vtysh_readline_init (void);
+void vtysh_user_init (void);
+
+int vtysh_execute (const char *);
+int vtysh_execute_no_pager (const char *);
+
+char *vtysh_prompt (void);
+
+void vtysh_config_write (void);
+
+int vtysh_config_from_file (struct vty *, FILE *);
+
+int vtysh_read_config (char *);
+
+void vtysh_config_parse (char *);
+
+void vtysh_config_dump (FILE *);
+
+void vtysh_config_init (void);
+
+void vtysh_pager_init (void);
+
+/* Child process execution flag. */
+extern int execute_flag;
+
+extern struct vty *vty;
+
+#endif /* VTYSH_H */
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
new file mode 100644 (file)
index 0000000..2834ef4
--- /dev/null
@@ -0,0 +1,451 @@
+/* Configuration generator.
+   Copyright (C) 2000 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#include "vtysh/vtysh.h"
+
+vector configvec;
+
+extern int vtysh_writeconfig_integrated;
+
+struct config
+{
+  /* Configuration node name. */
+  char *name;
+
+  /* Configuration string line. */
+  struct list *line;
+
+  /* Configuration can be nest. */
+  struct config *config;
+
+  /* Index of this config. */
+  u_int32_t index;
+};
+
+struct list *config_top;
+
+static int
+line_cmp (char *c1, char *c2)
+{
+  return strcmp (c1, c2);
+}
+
+static void
+line_del (char *line)
+{
+  XFREE (MTYPE_VTYSH_CONFIG_LINE, line);
+}
+
+static struct config *
+config_new ()
+{
+  struct config *config;
+  config = XCALLOC (MTYPE_VTYSH_CONFIG, sizeof (struct config));
+  return config;
+}
+
+static int
+config_cmp (struct config *c1, struct config *c2)
+{
+  return strcmp (c1->name, c2->name);
+}
+
+static void
+config_del (struct config* config)
+{
+  list_delete (config->line);
+  if (config->name)
+    XFREE (MTYPE_VTYSH_CONFIG_LINE, config->name);
+  XFREE (MTYPE_VTYSH_CONFIG, config);
+}
+
+static struct config *
+config_get (int index, const char *line)
+{
+  struct config *config;
+  struct config *config_loop;
+  struct list *master;
+  struct listnode *node, *nnode;
+
+  config = config_loop = NULL;
+
+  master = vector_lookup_ensure (configvec, index);
+
+  if (! master)
+    {
+      master = list_new ();
+      master->del = (void (*) (void *))config_del;
+      master->cmp = (int (*)(void *, void *)) config_cmp;
+      vector_set_index (configvec, index, master);
+    }
+  
+  for (ALL_LIST_ELEMENTS (master, node, nnode, config_loop))
+    {
+      if (strcmp (config_loop->name, line) == 0)
+       config = config_loop;
+    }
+
+  if (! config)
+    {
+      config = config_new ();
+      config->line = list_new ();
+      config->line->del = (void (*) (void *))line_del;
+      config->line->cmp = (int (*)(void *, void *)) line_cmp;
+      config->name = XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line);
+      config->index = index;
+      listnode_add (master, config);
+    }
+  return config;
+}
+
+static void
+config_add_line (struct list *config, const char *line)
+{
+  listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
+}
+
+static void
+config_add_line_uniq (struct list *config, const char *line)
+{
+  struct listnode *node, *nnode;
+  char *pnt;
+
+  for (ALL_LIST_ELEMENTS (config, node, nnode, pnt))
+    {
+      if (strcmp (pnt, line) == 0)
+       return;
+    }
+  listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
+}
+
+static void
+vtysh_config_parse_line (const char *line)
+{
+  char c;
+  static struct config *config = NULL;
+
+  if (! line)
+    return;
+
+  c = line[0];
+
+  if (c == '\0')
+    return;
+
+  /* printf ("[%s]\n", line); */
+
+  switch (c)
+    {
+    case '!':
+    case '#':
+      break;
+    case ' ':
+      /* Store line to current configuration. */
+      if (config)
+       {
+          if (strncmp (line, " address-family vpnv4",
+             strlen (" address-family vpnv4")) == 0)
+           config = config_get (BGP_VPNV4_NODE, line);
+         else if (strncmp (line, " address-family vpn6",
+             strlen (" address-family vpn6")) == 0)
+           config = config_get (BGP_VPNV6_NODE, line);
+         else if (strncmp (line, " address-family encapv6",
+             strlen (" address-family encapv6")) == 0)
+           config = config_get (BGP_ENCAPV6_NODE, line);
+         else if (strncmp (line, " address-family encap",
+             strlen (" address-family encap")) == 0)
+           config = config_get (BGP_ENCAP_NODE, line);
+         else if (strncmp (line, " address-family ipv4 multicast",
+                  strlen (" address-family ipv4 multicast")) == 0)
+           config = config_get (BGP_IPV4M_NODE, line);
+         else if (strncmp (line, " address-family ipv6",
+                  strlen (" address-family ipv6")) == 0)
+           config = config_get (BGP_IPV6_NODE, line);
+         else if (strncmp (line, " link-params", strlen (" link-params")) == 0)
+           {
+             config_add_line (config->line, line);
+             config->index = LINK_PARAMS_NODE;
+           }
+         else if (config->index == LINK_PARAMS_NODE &&
+                  strncmp (line, "  exit-link-params", strlen ("  exit")) == 0)
+           {
+             config_add_line (config->line, line);
+             config->index = INTERFACE_NODE;
+           }
+         else if (config->index == RMAP_NODE ||
+             config->index == INTERFACE_NODE ||
+             config->index == VTY_NODE)
+           config_add_line_uniq (config->line, line);
+         else
+           config_add_line (config->line, line);
+       }
+      else
+       config_add_line (config_top, line);
+      break;
+    default:
+      if (strncmp (line, "interface", strlen ("interface")) == 0)
+       config = config_get (INTERFACE_NODE, line);
+      else if (strncmp (line, "router-id", strlen ("router-id")) == 0)
+       config = config_get (ZEBRA_NODE, line);
+      else if (strncmp (line, "router rip", strlen ("router rip")) == 0)
+       config = config_get (RIP_NODE, line);
+      else if (strncmp (line, "router ripng", strlen ("router ripng")) == 0)
+       config = config_get (RIPNG_NODE, line);
+      else if (strncmp (line, "router ospf", strlen ("router ospf")) == 0)
+       config = config_get (OSPF_NODE, line);
+      else if (strncmp (line, "router ospf6", strlen ("router ospf6")) == 0)
+       config = config_get (OSPF6_NODE, line);
+      else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0)
+       config = config_get (BGP_NODE, line);
+      else if (strncmp (line, "router isis", strlen ("router isis")) == 0)
+       config = config_get (ISIS_NODE, line);
+      else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0)
+       config = config_get (BGP_NODE, line);
+      else if (strncmp (line, "route-map", strlen ("route-map")) == 0)
+       config = config_get (RMAP_NODE, line);
+      else if (strncmp (line, "access-list", strlen ("access-list")) == 0)
+       config = config_get (ACCESS_NODE, line);
+      else if (strncmp (line, "ipv6 access-list",
+              strlen ("ipv6 access-list")) == 0)
+       config = config_get (ACCESS_IPV6_NODE, line);
+      else if (strncmp (line, "ip prefix-list",
+              strlen ("ip prefix-list")) == 0)
+       config = config_get (PREFIX_NODE, line);
+      else if (strncmp (line, "ipv6 prefix-list",
+              strlen ("ipv6 prefix-list")) == 0)
+       config = config_get (PREFIX_IPV6_NODE, line);
+      else if (strncmp (line, "ip as-path access-list",
+              strlen ("ip as-path access-list")) == 0)
+       config = config_get (AS_LIST_NODE, line);
+      else if (strncmp (line, "ip community-list",
+              strlen ("ip community-list")) == 0)
+       config = config_get (COMMUNITY_LIST_NODE, line);
+      else if (strncmp (line, "ip route", strlen ("ip route")) == 0)
+       config = config_get (IP_NODE, line);
+      else if (strncmp (line, "ipv6 route", strlen ("ipv6 route")) == 0)
+       config = config_get (IP_NODE, line);
+      else if (strncmp (line, "key", strlen ("key")) == 0)
+       config = config_get (KEYCHAIN_NODE, line);
+      else if (strncmp (line, "line", strlen ("line")) == 0)
+       config = config_get (VTY_NODE, line);
+      else if ( (strncmp (line, "ipv6 forwarding",
+                strlen ("ipv6 forwarding")) == 0)
+              || (strncmp (line, "ip forwarding",
+                  strlen ("ip forwarding")) == 0) )
+       config = config_get (FORWARDING_NODE, line);
+      else if (strncmp (line, "service", strlen ("service")) == 0)
+       config = config_get (SERVICE_NODE, line);
+      else if (strncmp (line, "debug", strlen ("debug")) == 0)
+       config = config_get (DEBUG_NODE, line);
+      else if (strncmp (line, "password", strlen ("password")) == 0
+              || strncmp (line, "enable password",
+                          strlen ("enable password")) == 0)
+       config = config_get (AAA_NODE, line);
+      else if (strncmp (line, "ip protocol", strlen ("ip protocol")) == 0)
+       config = config_get (PROTOCOL_NODE, line);
+      else
+       {
+         if (strncmp (line, "log", strlen ("log")) == 0
+             || strncmp (line, "hostname", strlen ("hostname")) == 0
+            )
+           config_add_line_uniq (config_top, line);
+         else
+           config_add_line (config_top, line);
+         config = NULL;
+       }
+      break;
+    }
+}
+
+void
+vtysh_config_parse (char *line)
+{
+  char *begin;
+  char *pnt;
+  
+  begin = pnt = line;
+
+  while (*pnt != '\0')
+    {
+      if (*pnt == '\n')
+       {
+         *pnt++ = '\0';
+         vtysh_config_parse_line (begin);
+         begin = pnt;
+       }
+      else
+       {
+         pnt++;
+       }
+    }
+}
+
+/* Macro to check delimiter is needed between each configuration line
+ * or not. */
+#define NO_DELIMITER(I)  \
+  ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
+   || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \
+   (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \
+   || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \
+   || (I) == AAA_NODE)
+
+/* Display configuration to file pointer. */
+void
+vtysh_config_dump (FILE *fp)
+{
+  struct listnode *node, *nnode;
+  struct listnode *mnode, *mnnode;
+  struct config *config;
+  struct list *master;
+  char *line;
+  unsigned int i;
+
+  for (ALL_LIST_ELEMENTS (config_top, node, nnode, line))
+    {
+      fprintf (fp, "%s\n", line);
+      fflush (fp);
+    }
+  fprintf (fp, "!\n");
+  fflush (fp);
+
+  for (i = 0; i < vector_active (configvec); i++)
+    if ((master = vector_slot (configvec, i)) != NULL)
+      {
+       for (ALL_LIST_ELEMENTS (master, node, nnode, config))
+         {
+           fprintf (fp, "%s\n", config->name);
+           fflush (fp);
+
+           for (ALL_LIST_ELEMENTS (config->line, mnode, mnnode, line))
+             {
+               fprintf  (fp, "%s\n", line);
+               fflush (fp);
+             }
+           if (! NO_DELIMITER (i))
+             {
+               fprintf (fp, "!\n");
+               fflush (fp);
+             }
+         }
+       if (NO_DELIMITER (i))
+         {
+           fprintf (fp, "!\n");
+           fflush (fp);
+         }
+      }
+
+  for (i = 0; i < vector_active (configvec); i++)
+    if ((master = vector_slot (configvec, i)) != NULL)
+      {
+       list_delete (master);
+       vector_slot (configvec, i) = NULL;
+      }
+  list_delete_all_node (config_top);
+}
+
+/* Read up configuration file from file_name. */
+static void
+vtysh_read_file (FILE *confp)
+{
+  int ret;
+  struct vty *vty;
+
+  vty = vty_new ();
+  vty->fd = 0;                 /* stdout */
+  vty->type = VTY_TERM;
+  vty->node = CONFIG_NODE;
+  
+  vtysh_execute_no_pager ("enable");
+  vtysh_execute_no_pager ("configure terminal");
+
+  /* Execute configuration file. */
+  ret = vtysh_config_from_file (vty, confp);
+
+  vtysh_execute_no_pager ("end");
+  vtysh_execute_no_pager ("disable");
+
+  vty_close (vty);
+
+  if (ret != CMD_SUCCESS) 
+    {
+      switch (ret)
+       {
+       case CMD_ERR_AMBIGUOUS:
+         fprintf (stderr, "Ambiguous command.\n");
+         break;
+       case CMD_ERR_NO_MATCH:
+         fprintf (stderr, "There is no such command.\n");
+         break;
+       }
+      fprintf (stderr, "Error occured during reading below line.\n%s\n", 
+              vty->buf);
+      exit (1);
+    }
+}
+
+/* Read up configuration file from config_default_dir. */
+int
+vtysh_read_config (char *config_default_dir)
+{
+  FILE *confp = NULL;
+
+  confp = fopen (config_default_dir, "r");
+  if (confp == NULL)
+    return (1);
+
+  vtysh_read_file (confp);
+  fclose (confp);
+  host_config_set (config_default_dir);
+
+  return (0);
+}
+
+/* We don't write vtysh specific into file from vtysh. vtysh.conf should
+ * be edited by hand. So, we handle only "write terminal" case here and
+ * integrate vtysh specific conf with conf from daemons.
+ */
+void
+vtysh_config_write ()
+{
+  char line[81];
+  extern struct host host;
+
+  if (host.name)
+    {
+      sprintf (line, "hostname %s", host.name);
+      vtysh_config_parse_line(line);
+    }
+  if (vtysh_writeconfig_integrated)
+    vtysh_config_parse_line ("service integrated-vtysh-config");
+}
+
+void
+vtysh_config_init ()
+{
+  config_top = list_new ();
+  config_top->del = (void (*) (void *))line_del;
+  configvec = vector_init (1);
+}
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
new file mode 100644 (file)
index 0000000..e82771b
--- /dev/null
@@ -0,0 +1,439 @@
+/* Virtual terminal interface shell.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <sys/un.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+#include <pwd.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "command.h"
+#include "memory.h"
+
+#include "vtysh/vtysh.h"
+#include "vtysh/vtysh_user.h"
+
+/* VTY shell program name. */
+char *progname;
+
+/* Configuration file name and directory. */
+char config_default[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG;
+char history_file[MAXPATHLEN];
+
+/* Flag for indicate executing child command. */
+int execute_flag = 0;
+
+/* For sigsetjmp() & siglongjmp(). */
+static sigjmp_buf jmpbuf;
+
+/* Flag for avoid recursive siglongjmp() call. */
+static int jmpflag = 0;
+
+/* A static variable for holding the line. */
+static char *line_read;
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* Command logging */
+FILE *logfile;
+
+/* SIGTSTP handler.  This function care user's ^Z input. */
+static void
+sigtstp (int sig)
+{
+  /* Execute "end" command. */
+  vtysh_execute ("end");
+  
+  /* Initialize readline. */
+  rl_initialize ();
+  printf ("\n");
+
+  /* Check jmpflag for duplicate siglongjmp(). */
+  if (! jmpflag)
+    return;
+
+  jmpflag = 0;
+
+  /* Back to main command loop. */
+  siglongjmp (jmpbuf, 1);
+}
+
+/* SIGINT handler.  This function care user's ^Z input.  */
+static void
+sigint (int sig)
+{
+  /* Check this process is not child process. */
+  if (! execute_flag)
+    {
+      rl_initialize ();
+      printf ("\n");
+      rl_forced_update_display ();
+    }
+}
+
+/* Signale wrapper for vtysh. We don't use sigevent because
+ * vtysh doesn't use threads. TODO */
+static void
+vtysh_signal_set (int signo, void (*func)(int))
+{
+  struct sigaction sig;
+  struct sigaction osig;
+
+  sig.sa_handler = func;
+  sigemptyset (&sig.sa_mask);
+  sig.sa_flags = 0;
+#ifdef SA_RESTART
+  sig.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+
+  sigaction (signo, &sig, &osig);
+}
+
+/* Initialization of signal handles. */
+static void
+vtysh_signal_init ()
+{
+  vtysh_signal_set (SIGINT, sigint);
+  vtysh_signal_set (SIGTSTP, sigtstp);
+  vtysh_signal_set (SIGPIPE, SIG_IGN);
+}
+
+/* Help information display. */
+static void
+usage (int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    printf ("Usage : %s [OPTION...]\n\n" \
+           "Integrated shell for Quagga routing software suite. \n\n" \
+           "-b, --boot               Execute boot startup configuration\n" \
+           "-c, --command            Execute argument as command\n" \
+           "-d, --daemon             Connect only to the specified daemon\n" \
+           "-E, --echo               Echo prompt and command in -c mode\n" \
+           "-C, --dryrun             Check configuration for validity and exit\n" \
+           "-h, --help               Display this help and exit\n\n" \
+           "Note that multiple commands may be executed from the command\n" \
+           "line by passing multiple -c args, or by embedding linefeed\n" \
+           "characters in one or more of the commands.\n\n" \
+           "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+
+  exit (status);
+}
+
+/* VTY shell options, we use GNU getopt library. */
+struct option longopts[] = 
+{
+  { "boot",                 no_argument,             NULL, 'b'},
+  /* For compatibility with older zebra/quagga versions */
+  { "eval",                 required_argument,       NULL, 'e'},
+  { "command",              required_argument,       NULL, 'c'},
+  { "daemon",               required_argument,       NULL, 'd'},
+  { "echo",                 no_argument,             NULL, 'E'},
+  { "dryrun",              no_argument,             NULL, 'C'},
+  { "help",                 no_argument,             NULL, 'h'},
+  { "noerror",             no_argument,             NULL, 'n'},
+  { 0 }
+};
+
+/* Read a string, and return a pointer to it.  Returns NULL on EOF. */
+static char *
+vtysh_rl_gets ()
+{
+  HIST_ENTRY *last;
+  /* If the buffer has already been allocated, return the memory
+   * to the free pool. */
+  if (line_read)
+    {
+      free (line_read);
+      line_read = NULL;
+    }
+     
+  /* Get a line from the user.  Change prompt according to node.  XXX. */
+  line_read = readline (vtysh_prompt ());
+     
+  /* If the line has any text in it, save it on the history. But only if
+   * last command in history isn't the same one. */
+  if (line_read && *line_read)
+    {
+      using_history();
+      last = previous_history();
+      if (!last || strcmp (last->line, line_read) != 0) {
+       add_history (line_read);
+       append_history(1,history_file);
+      }
+    }
+     
+  return (line_read);
+}
+
+static void log_it(const char *line)
+{
+  time_t t = time(NULL);
+  struct tm *tmp = localtime(&t);
+  const char *user = getenv("USER");
+  char tod[64];
+
+  if (!user)
+    user = "boot";
+
+  strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
+  
+  fprintf(logfile, "%s:%s %s\n", tod, user, line);
+}
+
+/* VTY shell main routine. */
+int
+main (int argc, char **argv, char **env)
+{
+  char *p;
+  int opt;
+  int dryrun = 0;
+  int boot_flag = 0;
+  const char *daemon_name = NULL;
+  struct cmd_rec {
+    const char *line;
+    struct cmd_rec *next;
+  } *cmd = NULL;
+  struct cmd_rec *tail = NULL;
+  int echo_command = 0;
+  int no_error = 0;
+  char *homedir = NULL;
+
+  /* Preserve name of myself. */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  /* if logging open now */
+  if ((p = getenv("VTYSH_LOG")) != NULL)
+      logfile = fopen(p, "a");
+
+  /* Option handling. */
+  while (1) 
+    {
+      opt = getopt_long (argc, argv, "be:c:d:nEhC", longopts, 0);
+    
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'b':
+         boot_flag = 1;
+         break;
+       case 'e':
+       case 'c':
+         {
+           struct cmd_rec *cr;
+           cr = XMALLOC(MTYPE_TMP, sizeof(*cr));
+           cr->line = optarg;
+           cr->next = NULL;
+           if (tail)
+             tail->next = cr;
+           else
+             cmd = cr;
+           tail = cr;
+         }
+         break;
+       case 'd':
+         daemon_name = optarg;
+         break;
+       case 'n':
+         no_error = 1;
+         break;
+       case 'E':
+         echo_command = 1;
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'h':
+         usage (0);
+         break;
+       default:
+         usage (1);
+         break;
+       }
+    }
+
+  /* Initialize user input buffer. */
+  line_read = NULL;
+  setlinebuf(stdout);
+
+  /* Signal and others. */
+  vtysh_signal_init ();
+
+  /* Make vty structure and register commands. */
+  vtysh_init_vty ();
+  vtysh_init_cmd ();
+  vtysh_user_init ();
+  vtysh_config_init ();
+
+  vty_init_vtysh ();
+
+  /* Read vtysh configuration file before connecting to daemons. */
+  vtysh_read_config (config_default);
+
+  /* Start execution only if not in dry-run mode */
+  if(dryrun)
+    return(0);
+  
+  /* Ignore error messages */
+  if (no_error)
+    freopen("/dev/null", "w", stdout);
+
+  /* Make sure we pass authentication before proceeding. */
+  vtysh_auth ();
+
+  /* Do not connect until we have passed authentication. */
+  if (vtysh_connect_all (daemon_name) <= 0)
+    {
+      fprintf(stderr, "Exiting: failed to connect to any daemons.\n");
+      exit(1);
+    }
+
+  /*
+   * Setup history file for use by both -c and regular input
+   * If we can't find the home directory, then don't store
+   * the history information
+   */
+  homedir = vtysh_get_home ();
+  if (homedir)
+    {
+      snprintf(history_file, sizeof(history_file), "%s/.history_quagga", homedir);
+      if (read_history (history_file) != 0)
+       {
+         int fp;
+
+         fp = open (history_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+         if (fp)
+           close (fp);
+
+         read_history (history_file);
+       }
+    }
+
+  /* If eval mode. */
+  if (cmd)
+    {
+      /* Enter into enable node. */
+      vtysh_execute ("enable");
+
+      while (cmd != NULL)
+        {
+         int ret;
+         char *eol;
+
+         while ((eol = strchr(cmd->line, '\n')) != NULL)
+           {
+             *eol = '\0';
+
+             add_history (cmd->line);
+             append_history (1, history_file);
+
+             if (echo_command)
+               printf("%s%s\n", vtysh_prompt(), cmd->line);
+             
+             if (logfile)
+               log_it(cmd->line);
+
+             ret = vtysh_execute_no_pager(cmd->line);
+             if (!no_error &&
+                 ! (ret == CMD_SUCCESS ||
+                    ret == CMD_SUCCESS_DAEMON ||
+                    ret == CMD_WARNING))
+               exit(1);
+
+             cmd->line = eol+1;
+           }
+
+         add_history (cmd->line);
+         append_history (1, history_file);
+
+         if (echo_command)
+           printf("%s%s\n", vtysh_prompt(), cmd->line);
+
+         if (logfile)
+           log_it(cmd->line);
+
+         ret = vtysh_execute_no_pager(cmd->line);
+         if (!no_error &&
+             ! (ret == CMD_SUCCESS ||
+                ret == CMD_SUCCESS_DAEMON ||
+                ret == CMD_WARNING))
+           exit(1);
+
+         {
+           struct cmd_rec *cr;
+           cr = cmd;
+           cmd = cmd->next;
+           XFREE(0, cr);
+         }
+        }
+
+      history_truncate_file(history_file,1000);
+      exit (0);
+    }
+  
+  /* Boot startup configuration file. */
+  if (boot_flag)
+    {
+      if (vtysh_read_config (integrate_default))
+       {
+         fprintf (stderr, "Can't open configuration file [%s]\n",
+                  integrate_default);
+         exit (1);
+       }
+      else
+       exit (0);
+    }
+
+  vtysh_pager_init ();
+
+  vtysh_readline_init ();
+
+  vty_hello (vty);
+
+  /* Enter into enable node. */
+  vtysh_execute ("enable");
+
+  /* Preparation for longjmp() in sigtstp(). */
+  sigsetjmp (jmpbuf, 1);
+  jmpflag = 1;
+
+  /* Main command loop. */
+  while (vtysh_rl_gets ())
+    vtysh_execute (line_read);
+
+  history_truncate_file(history_file,1000);
+  printf ("\n");
+
+  /* Rest in peace. */
+  exit (0);
+}
diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c
new file mode 100644 (file)
index 0000000..584b61f
--- /dev/null
@@ -0,0 +1,213 @@
+/* User authentication for vtysh.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <lib/version.h>
+
+#include <pwd.h>
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#ifdef HAVE_PAM_MISC_H
+#include <security/pam_misc.h>
+#endif
+#ifdef HAVE_OPENPAM_H
+#include <security/openpam.h>
+#endif
+#endif /* USE_PAM */
+
+#include "memory.h"
+#include "linklist.h"
+#include "command.h"
+#include "vtysh_user.h"
+
+#ifdef USE_PAM
+static struct pam_conv conv = 
+{
+  PAM_CONV_FUNC,
+  NULL
+};
+
+static int
+vtysh_pam (const char *user)
+{
+  int ret;
+  pam_handle_t *pamh = NULL;
+
+  /* Start PAM. */
+  ret = pam_start(QUAGGA_PROGNAME, user, &conv, &pamh);
+  /* printf ("ret %d\n", ret); */
+
+  /* Is user really user? */
+  if (ret == PAM_SUCCESS)
+    ret = pam_authenticate (pamh, 0);
+  /* printf ("ret %d\n", ret); */
+  
+#if 0
+  /* Permitted access? */
+  if (ret == PAM_SUCCESS)
+    ret = pam_acct_mgmt (pamh, 0);
+  printf ("ret %d\n", ret);
+
+  if (ret == PAM_AUTHINFO_UNAVAIL)
+    ret = PAM_SUCCESS;
+#endif /* 0 */
+  
+  /* This is where we have been authorized or not. */
+#ifdef DEBUG
+  if (ret == PAM_SUCCESS)
+    printf("Authenticated\n");
+  else
+    printf("Not Authenticated\n");
+#endif /* DEBUG */
+
+  /* close Linux-PAM */
+  if (pam_end (pamh, ret) != PAM_SUCCESS) 
+    {
+      pamh = NULL;
+      fprintf(stderr, "vtysh_pam: failed to release authenticator\n");
+      exit(1);
+    }
+
+  return ret == PAM_SUCCESS ? 0 : 1;
+}
+#endif /* USE_PAM */
+
+struct vtysh_user
+{
+  char *name;
+  u_char nopassword;
+};
+
+struct list *userlist;
+
+static struct vtysh_user *
+user_new ()
+{
+  return XCALLOC (MTYPE_TMP, sizeof (struct vtysh_user));
+}
+
+#if 0
+static void
+user_free (struct vtysh_user *user)
+{
+  XFREE (0, user);
+}
+#endif
+
+static struct vtysh_user *
+user_lookup (const char *name)
+{
+  struct listnode *node, *nnode;
+  struct vtysh_user *user;
+
+  for (ALL_LIST_ELEMENTS (userlist, node, nnode, user))
+    {
+      if (strcmp (user->name, name) == 0)
+       return user;
+    }
+  return NULL;
+}
+
+#if 0
+static void
+user_config_write ()
+{
+  struct listnode *node, *nnode;
+  struct vtysh_user *user;
+
+  for (ALL_LIST_ELEMENTS (userlist, node, nnode, user))
+    {
+      if (user->nopassword)
+       printf (" username %s nopassword\n", user->name);
+    }
+}
+#endif
+
+static struct vtysh_user *
+user_get (const char *name)
+{
+  struct vtysh_user *user;
+  user = user_lookup (name);
+  if (user)
+    return user;
+
+  user = user_new ();
+  user->name = strdup (name);
+  listnode_add (userlist, user);
+
+  return user;
+}
+
+DEFUN (username_nopassword,
+       username_nopassword_cmd,
+       "username WORD nopassword",
+       "\n"
+       "\n"
+       "\n")
+{
+  struct vtysh_user *user;
+  user = user_get (argv[0]);
+  user->nopassword = 1;
+  return CMD_SUCCESS;
+}
+
+int
+vtysh_auth (void)
+{
+  struct vtysh_user *user;
+  struct passwd *passwd;
+
+  if ((passwd = getpwuid (geteuid ())) == NULL)
+  {
+    fprintf (stderr, "could not lookup user ID %d\n", (int) geteuid());
+    exit (1);
+  }
+
+  user = user_lookup (passwd->pw_name);
+  if (user && user->nopassword)
+    /* Pass through */;
+  else
+    {
+#ifdef USE_PAM
+      if (vtysh_pam (passwd->pw_name))
+       exit (0);
+#endif /* USE_PAM */
+    }
+  return 0;
+}
+
+char *
+vtysh_get_home (void)
+{
+  struct passwd *passwd;
+
+  passwd = getpwuid (getuid ());
+
+  return passwd ? passwd->pw_dir : NULL;
+}
+
+void
+vtysh_user_init (void)
+{
+  userlist = list_new ();
+  install_element (CONFIG_NODE, &username_nopassword_cmd);
+}
diff --git a/vtysh/vtysh_user.h b/vtysh/vtysh_user.h
new file mode 100644 (file)
index 0000000..a6c8b99
--- /dev/null
@@ -0,0 +1,30 @@
+/* User authentication for vtysh.
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _VTYSH_USER_H
+#define _VTYSH_USER_H
+
+int vtysh_auth (void);
+void vtysh_user_init (void);
+
+char *vtysh_get_home (void);
+
+#endif /* _VTYSH_USER_H */
diff --git a/watchquagga/.gitignore b/watchquagga/.gitignore
new file mode 100644 (file)
index 0000000..b6226d5
--- /dev/null
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+*.o
+watchquagga
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+
diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am
new file mode 100644 (file)
index 0000000..1f05f26
--- /dev/null
@@ -0,0 +1,11 @@
+## Process this file with Automake to create Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\"
+
+AM_CFLAGS = $(WERROR)
+
+sbin_PROGRAMS = watchquagga
+
+watchquagga_SOURCES = watchquagga.c
+watchquagga_LDADD = ../lib/libzebra.la @LIBCAP@
diff --git a/watchquagga/watchquagga.c b/watchquagga/watchquagga.c
new file mode 100644 (file)
index 0000000..9bd7a5f
--- /dev/null
@@ -0,0 +1,1399 @@
+/*
+    Monitor status of quagga daemons and restart if necessary.
+
+    Copyright (C) 2004  Andrew J. Schorr
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <zebra.h>
+#include <thread.h>
+#include <log.h>
+#include <network.h>
+#include <sigevent.h>
+#include <lib/version.h>
+#include <getopt.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <memory.h>
+
+#ifndef MIN
+#define MIN(X,Y) (((X) <= (Y)) ? (X) : (Y))
+#endif
+
+/* Macros to help randomize timers. */
+#define JITTER(X) ((random() % ((X)+1))-((X)/2))
+#define FUZZY(X) ((X)+JITTER((X)/20))
+
+#define DEFAULT_PERIOD         5
+#define DEFAULT_TIMEOUT                10
+#define DEFAULT_RESTART_TIMEOUT        20
+#define DEFAULT_LOGLEVEL       LOG_INFO
+#define DEFAULT_MIN_RESTART    60
+#define DEFAULT_MAX_RESTART    600
+#ifdef PATH_WATCHQUAGGA_PID
+#define DEFAULT_PIDFILE                PATH_WATCHQUAGGA_PID
+#else
+#define DEFAULT_PIDFILE                STATEDIR "/watchquagga.pid"
+#endif
+#ifdef DAEMON_VTY_DIR
+#define VTYDIR                 DAEMON_VTY_DIR
+#else
+#define VTYDIR                 STATEDIR
+#endif
+
+#define PING_TOKEN     "PING"
+
+/* Needs to be global, referenced somewhere inside libzebra. */
+struct thread_master *master;
+
+typedef enum
+{
+  MODE_MONITOR = 0,
+  MODE_GLOBAL_RESTART,
+  MODE_SEPARATE_RESTART,
+  MODE_PHASED_ZEBRA_RESTART,
+  MODE_PHASED_ALL_RESTART
+} watch_mode_t;
+
+static const char *mode_str[] =
+{
+  "monitor",
+  "global restart",
+  "individual daemon restart",
+  "phased zebra restart",
+  "phased global restart for any failure",
+};
+
+typedef enum
+{
+  PHASE_NONE = 0,
+  PHASE_STOPS_PENDING,
+  PHASE_WAITING_DOWN,
+  PHASE_ZEBRA_RESTART_PENDING,
+  PHASE_WAITING_ZEBRA_UP
+} restart_phase_t;
+
+static const char *phase_str[] =
+{
+  "None",
+  "Stop jobs running",
+  "Waiting for other daemons to come down",
+  "Zebra restart job running",
+  "Waiting for zebra to come up",
+  "Start jobs running",
+};
+
+#define PHASE_TIMEOUT (3*gs.restart_timeout)
+
+struct restart_info
+{
+  const char *name;
+  const char *what;
+  pid_t pid;
+  struct timeval time;
+  long interval;
+  struct thread *t_kill;
+  int kills;
+};
+
+static struct global_state
+{
+  watch_mode_t mode;
+  restart_phase_t phase;
+  struct thread *t_phase_hanging;
+  const char *vtydir;
+  long period;
+  long timeout;
+  long restart_timeout;
+  long min_restart_interval;
+  long max_restart_interval;
+  int do_ping;
+  struct daemon *daemons;
+  const char *restart_command;
+  const char *start_command;
+  const char *stop_command;
+  struct restart_info restart;
+  int unresponsive_restart;
+  int loglevel;
+  struct daemon *special;      /* points to zebra when doing phased restart */
+  int numdaemons;
+  int numpids;
+  int numdown;         /* # of daemons that are not UP or UNRESPONSIVE */
+} gs = {
+  .mode = MODE_MONITOR,
+  .phase = PHASE_NONE,
+  .vtydir = VTYDIR,
+  .period = 1000*DEFAULT_PERIOD,
+  .timeout = DEFAULT_TIMEOUT,
+  .restart_timeout = DEFAULT_RESTART_TIMEOUT,
+  .loglevel = DEFAULT_LOGLEVEL,
+  .min_restart_interval = DEFAULT_MIN_RESTART,
+  .max_restart_interval = DEFAULT_MAX_RESTART,
+  .do_ping = 1,
+};
+
+typedef enum
+{
+  DAEMON_INIT,
+  DAEMON_DOWN,
+  DAEMON_CONNECTING,
+  DAEMON_UP,
+  DAEMON_UNRESPONSIVE
+} daemon_state_t;
+
+#define IS_UP(DMN) \
+  (((DMN)->state == DAEMON_UP) || ((DMN)->state == DAEMON_UNRESPONSIVE))
+
+static const char *state_str[] =
+{
+  "Init",
+  "Down",
+  "Connecting",
+  "Up",
+  "Unresponsive",
+};
+
+struct daemon {
+  const char *name;
+  daemon_state_t state;
+  int fd;
+  struct timeval echo_sent;
+  u_int connect_tries;
+  struct thread *t_wakeup;
+  struct thread *t_read;
+  struct thread *t_write;
+  struct daemon *next;
+  struct restart_info restart;
+};
+
+static const struct option longopts[] = 
+{
+  { "daemon", no_argument, NULL, 'd'},
+  { "statedir", required_argument, NULL, 'S'},
+  { "no-echo", no_argument, NULL, 'e'},
+  { "loglevel", required_argument, NULL, 'l'},
+  { "interval", required_argument, NULL, 'i'},
+  { "timeout", required_argument, NULL, 't'},
+  { "restart-timeout", required_argument, NULL, 'T'},
+  { "restart", required_argument, NULL, 'r'},
+  { "start-command", required_argument, NULL, 's'},
+  { "kill-command", required_argument, NULL, 'k'},
+  { "restart-all", required_argument, NULL, 'R'},
+  { "all-restart", no_argument, NULL, 'a'},
+  { "always-all-restart", no_argument, NULL, 'A'},
+  { "unresponsive-restart", no_argument, NULL, 'z'},
+  { "min-restart-interval", required_argument, NULL, 'm'},
+  { "max-restart-interval", required_argument, NULL, 'M'},
+  { "pid-file", required_argument, NULL, 'p'},
+  { "blank-string", required_argument, NULL, 'b'},
+  { "help", no_argument, NULL, 'h'},
+  { "version", no_argument, NULL, 'v'},
+  { NULL, 0, NULL, 0 }
+};
+
+static int try_connect(struct daemon *dmn);
+static int wakeup_send_echo(struct thread *t_wakeup);
+static void try_restart(struct daemon *dmn);
+static void phase_check(void);
+
+static int
+usage(const char *progname, int status)
+{
+  if (status != 0)
+    fprintf(stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {
+      printf("Usage : %s [OPTION...] <daemon name> ...\n\n\
+Watchdog program to monitor status of quagga daemons and try to restart\n\
+them if they are down or unresponsive.  It determines whether a daemon is\n\
+up based on whether it can connect to the daemon's vty unix stream socket.\n\
+It then repeatedly sends echo commands over that socket to determine whether\n\
+the daemon is responsive.  If the daemon crashes, we will receive an EOF\n\
+on the socket connection and know immediately that the daemon is down.\n\n\
+The daemons to be monitored should be listed on the command line.\n\n\
+This program can run in one of 5 modes:\n\n\
+0. Mode: %s.\n\
+  Just monitor and report on status changes.  Example:\n\
+    %s -d zebra ospfd bgpd\n\n\
+1. Mode: %s.\n\
+  Whenever any daemon hangs or crashes, use the given command to restart\n\
+  them all.  Example:\n\
+    %s -dz \\\n\
+      -R '/sbin/service zebra restart; /sbin/service ospfd restart' \\\n\
+      zebra ospfd\n\n\
+2. Mode: %s.\n\
+  When any single daemon hangs or crashes, restart only the daemon that's\n\
+  in trouble using the supplied restart command.  Example:\n\
+    %s -dz -r '/sbin/service %%s restart' zebra ospfd bgpd\n\n\
+3. Mode: %s.\n\
+  The same as the previous mode, except that there is special treatment when\n\
+  the zebra daemon is in trouble.  In that case, a phased restart approach\n\
+  is used: 1. stop all other daemons; 2. restart zebra; 3. start the other\n\
+  daemons.  Example:\n\
+    %s -adz -r '/sbin/service %%s restart' \\\n\
+      -s '/sbin/service %%s start' \\\n\
+      -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\
+4. Mode: %s.\n\
+  This is the same as the previous mode, except that the phased restart\n\
+  procedure is used whenever any of the daemons hangs or crashes.  Example:\n\
+    %s -Adz -r '/sbin/service %%s restart' \\\n\
+      -s '/sbin/service %%s start' \\\n\
+      -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\
+As of this writing, it is believed that mode 2 [%s]\n\
+is not safe, and mode 3 [%s] may not be safe with some of the\n\
+routing daemons.\n\n\
+In order to avoid attempting to restart the daemons in a fast loop,\n\
+the -m and -M options allow you to control the minimum delay between\n\
+restart commands.  The minimum restart delay is recalculated each time\n\
+a restart is attempted: if the time since the last restart attempt exceeds\n\
+twice the -M value, then the restart delay is set to the -m value.\n\
+Otherwise, the interval is doubled (but capped at the -M value).\n\n",
+        progname,mode_str[0],progname,mode_str[1],progname,mode_str[2],
+        progname,mode_str[3],progname,mode_str[4],progname,mode_str[2],
+        mode_str[3]);
+
+      printf("Options:\n\
+-d, --daemon   Run in daemon mode.  In this mode, error messages are sent\n\
+               to syslog instead of stdout.\n\
+-S, --statedir Set the vty socket directory (default is %s)\n\
+-e, --no-echo  Do not ping the daemons to test responsiveness (this\n\
+               option is necessary if the daemons do not support the\n\
+               echo command)\n\
+-l, --loglevel Set the logging level (default is %d).\n\
+               The value should range from %d (LOG_EMERG) to %d (LOG_DEBUG),\n\
+               but it can be set higher than %d if extra-verbose debugging\n\
+               messages are desired.\n\
+-m, --min-restart-interval\n\
+               Set the minimum seconds to wait between invocations of daemon\n\
+               restart commands (default is %d).\n\
+-M, --max-restart-interval\n\
+               Set the maximum seconds to wait between invocations of daemon\n\
+               restart commands (default is %d).\n\
+-i, --interval Set the status polling interval in seconds (default is %d)\n\
+-t, --timeout  Set the unresponsiveness timeout in seconds (default is %d)\n\
+-T, --restart-timeout\n\
+               Set the restart (kill) timeout in seconds (default is %d).\n\
+               If any background jobs are still running after this much\n\
+               time has elapsed, they will be killed.\n\
+-r, --restart  Supply a Bourne shell command to use to restart a single\n\
+               daemon.  The command string should include '%%s' where the\n\
+               name of the daemon should be substituted.\n\
+               Note that -r and -R are incompatible.\n\
+-s, --start-command\n\
+               Supply a Bourne shell to command to use to start a single\n\
+               daemon.  The command string should include '%%s' where the\n\
+               name of the daemon should be substituted.\n\
+-k, --kill-command\n\
+               Supply a Bourne shell to command to use to stop a single\n\
+               daemon.  The command string should include '%%s' where the\n\
+               name of the daemon should be substituted.\n\
+-R, --restart-all\n\
+               When one or more daemons is down, try to restart everything\n\
+               using the Bourne shell command supplied as the argument.\n\
+               Note that -r and -R are incompatible.\n\
+-z, --unresponsive-restart\n\
+               When a daemon is unresponsive, treat it as being down for\n\
+               restart purposes.\n\
+-a, --all-restart\n\
+               When zebra hangs or crashes, restart all daemons using\n\
+               this phased approach: 1. stop all other daemons; 2. restart\n\
+               zebra; 3. start other daemons.  Requires -r, -s, and -k.\n\
+-A, --always-all-restart\n\
+               When any daemon (not just zebra) hangs or crashes, use the\n\
+               same phased restart mechanism described above for -a.\n\
+               Requires -r, -s, and -k.\n\
+-p, --pid-file Set process identifier file name\n\
+               (default is %s).\n\
+-b, --blank-string\n\
+               When the supplied argument string is found in any of the\n\
+               various shell command arguments (-r, -s, -k, or -R), replace\n\
+               it with a space.  This is an ugly hack to circumvent problems\n\
+               passing command-line arguments with embedded spaces.\n\
+-v, --version  Print program version\n\
+-h, --help     Display this help and exit\n",
+        VTYDIR,DEFAULT_LOGLEVEL,LOG_EMERG,LOG_DEBUG,LOG_DEBUG,
+        DEFAULT_MIN_RESTART,DEFAULT_MAX_RESTART,
+        DEFAULT_PERIOD,DEFAULT_TIMEOUT,DEFAULT_RESTART_TIMEOUT,
+        DEFAULT_PIDFILE);
+    }
+
+  return status;
+}
+
+static pid_t
+run_background(char *shell_cmd)
+{
+  pid_t child;
+
+  switch (child = fork())
+    {
+    case -1:
+      zlog_err("fork failed, cannot run command [%s]: %s",
+              shell_cmd,safe_strerror(errno));
+      return -1;
+    case 0:
+      /* Child process. */
+      /* Use separate process group so child processes can be killed easily. */
+      if (setpgid(0,0) < 0)
+        zlog_warn("warning: setpgid(0,0) failed: %s",safe_strerror(errno));
+      {
+       char shell[] = "sh";
+       char dashc[] = "-c";
+        char *const argv[4] = { shell, dashc, shell_cmd, NULL};
+       execv("/bin/sh", argv);
+       zlog_err("execv(/bin/sh -c '%s') failed: %s",
+                shell_cmd,safe_strerror(errno));
+       _exit(127);
+      }
+    default:
+      /* Parent process: we will reap the child later. */
+      zlog_err("Forked background command [pid %d]: %s",(int)child,shell_cmd);
+      return child;
+    }
+}
+
+static struct timeval *
+time_elapsed(struct timeval *result, const struct timeval *start_time)
+{
+  gettimeofday(result,NULL);
+  result->tv_sec -= start_time->tv_sec;
+  result->tv_usec -= start_time->tv_usec;
+  while (result->tv_usec < 0)
+    {
+      result->tv_usec += 1000000L;
+      result->tv_sec--;
+    }
+  return result;
+}
+
+static int
+restart_kill(struct thread *t_kill)
+{
+  struct restart_info *restart = THREAD_ARG(t_kill);
+  struct timeval delay;
+
+  time_elapsed(&delay,&restart->time);
+  zlog_warn("Warning: %s %s child process %d still running after "
+           "%ld seconds, sending signal %d",
+           restart->what,restart->name,(int)restart->pid, (long)delay.tv_sec,
+           (restart->kills ? SIGKILL : SIGTERM));
+  kill(-restart->pid,(restart->kills ? SIGKILL : SIGTERM));
+  restart->kills++;
+  restart->t_kill = thread_add_timer(master,restart_kill,restart,
+                                    gs.restart_timeout);
+  return 0;
+}
+
+static struct restart_info *
+find_child(pid_t child)
+{
+  if (gs.mode == MODE_GLOBAL_RESTART)
+    {
+      if (gs.restart.pid == child)
+        return &gs.restart;
+    }
+  else
+    {
+      struct daemon *dmn;
+      for (dmn = gs.daemons; dmn; dmn = dmn->next)
+        {
+         if (dmn->restart.pid == child)
+           return &dmn->restart;
+        }
+    }
+  return NULL;
+}
+
+static void
+sigchild(void)
+{
+  pid_t child;
+  int status;
+  const char *name;
+  const char *what;
+  struct restart_info *restart;
+
+  switch (child = waitpid(-1,&status,WNOHANG)) 
+    {
+    case -1:
+      zlog_err("waitpid failed: %s",safe_strerror(errno));
+      return;
+    case 0:
+      zlog_warn("SIGCHLD received, but waitpid did not reap a child");
+      return;
+    }
+
+  if ((restart = find_child(child)) != NULL)
+    {
+      name = restart->name;
+      what = restart->what;
+      restart->pid = 0;
+      gs.numpids--;
+      thread_cancel(restart->t_kill);
+      restart->t_kill = NULL;
+      /* Update restart time to reflect the time the command completed. */
+      gettimeofday(&restart->time,NULL);
+    }
+  else
+    {
+      zlog_err("waitpid returned status for an unknown child process %d",
+              (int)child);
+      name = "(unknown)";
+      what = "background";
+    }
+  if (WIFSTOPPED(status))
+      zlog_warn("warning: %s %s process %d is stopped",
+               what,name,(int)child);
+  else if (WIFSIGNALED(status))
+    zlog_warn("%s %s process %d terminated due to signal %d",
+             what,name,(int)child,WTERMSIG(status));
+  else if (WIFEXITED(status))
+    {
+      if (WEXITSTATUS(status) != 0)
+       zlog_warn("%s %s process %d exited with non-zero status %d",
+                 what,name,(int)child,WEXITSTATUS(status));
+      else
+       zlog_debug("%s %s process %d exited normally",what,name,(int)child);
+    }
+  else
+    zlog_err("cannot interpret %s %s process %d wait status 0x%x",
+            what,name,(int)child,status);
+  phase_check();
+}
+
+static int
+run_job(struct restart_info *restart, const char *cmdtype, const char *command,
+       int force, int update_interval)
+{
+  struct timeval delay;
+
+  if (gs.loglevel > LOG_DEBUG+1)
+    zlog_debug("attempting to %s %s",cmdtype,restart->name);
+
+  if (restart->pid)
+    {
+      if (gs.loglevel > LOG_DEBUG+1)
+        zlog_debug("cannot %s %s, previous pid %d still running",
+                  cmdtype,restart->name,(int)restart->pid);
+      return -1;
+    }
+
+  /* Note: time_elapsed test must come before the force test, since we need
+     to make sure that delay is initialized for use below in updating the
+     restart interval. */
+  if ((time_elapsed(&delay,&restart->time)->tv_sec < restart->interval) &&
+      !force)
+    {
+      if (gs.loglevel > LOG_DEBUG+1)
+        zlog_debug("postponing %s %s: "
+                  "elapsed time %ld < retry interval %ld",
+                  cmdtype,restart->name,(long)delay.tv_sec,restart->interval);
+      return -1;
+    }
+
+  gettimeofday(&restart->time,NULL);
+  restart->kills = 0;
+  {
+    char cmd[strlen(command)+strlen(restart->name)+1];
+    snprintf(cmd,sizeof(cmd),command,restart->name);
+    if ((restart->pid = run_background(cmd)) > 0)
+      {
+       restart->t_kill = thread_add_timer(master,restart_kill,restart,
+                                          gs.restart_timeout);
+       restart->what = cmdtype;
+       gs.numpids++;
+      }
+    else
+      restart->pid = 0;
+  }
+
+  /* Calculate the new restart interval. */
+  if (update_interval)
+    {
+      if (delay.tv_sec > 2*gs.max_restart_interval)
+       restart->interval = gs.min_restart_interval;
+      else if ((restart->interval *= 2) > gs.max_restart_interval)
+       restart->interval = gs.max_restart_interval;
+      if (gs.loglevel > LOG_DEBUG+1)
+       zlog_debug("restart %s interval is now %ld",
+                  restart->name,restart->interval);
+    }
+  return restart->pid;
+}
+
+#define SET_READ_HANDLER(DMN) \
+  (DMN)->t_read = thread_add_read(master,handle_read,(DMN),(DMN)->fd)
+
+#define SET_WAKEUP_DOWN(DMN)   \
+  (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_down,(DMN),    \
+                                         FUZZY(gs.period))
+
+#define SET_WAKEUP_UNRESPONSIVE(DMN)   \
+  (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_unresponsive,(DMN), \
+                                         FUZZY(gs.period))
+
+#define SET_WAKEUP_ECHO(DMN) \
+  (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_send_echo,(DMN), \
+                                         FUZZY(gs.period))
+
+static int
+wakeup_down(struct thread *t_wakeup)
+{
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+
+  dmn->t_wakeup = NULL;
+  if (try_connect(dmn) < 0)
+    SET_WAKEUP_DOWN(dmn);
+  if ((dmn->connect_tries > 1) && (dmn->state != DAEMON_UP))
+    try_restart(dmn);
+  return 0;
+}
+
+static int
+wakeup_init(struct thread *t_wakeup)
+{
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+
+  dmn->t_wakeup = NULL;
+  if (try_connect(dmn) < 0)
+    {
+      SET_WAKEUP_DOWN(dmn);
+      zlog_err("%s state -> down : initial connection attempt failed",
+              dmn->name);
+      dmn->state = DAEMON_DOWN;
+    }
+  return 0;
+}
+
+static void
+daemon_down(struct daemon *dmn, const char *why)
+{
+  if (IS_UP(dmn) || (dmn->state == DAEMON_INIT))
+    zlog_err("%s state -> down : %s",dmn->name,why);
+  else if (gs.loglevel > LOG_DEBUG)
+    zlog_debug("%s still down : %s",dmn->name,why);
+  if (IS_UP(dmn))
+    gs.numdown++;
+  dmn->state = DAEMON_DOWN;
+  if (dmn->fd >= 0)
+    {
+      close(dmn->fd);
+      dmn->fd = -1;
+    }
+  THREAD_OFF(dmn->t_read);
+  THREAD_OFF(dmn->t_write);
+  THREAD_OFF(dmn->t_wakeup);
+  if (try_connect(dmn) < 0)
+    SET_WAKEUP_DOWN(dmn);
+  phase_check();
+}
+
+static int
+handle_read(struct thread *t_read)
+{
+  struct daemon *dmn = THREAD_ARG(t_read);
+  static const char resp[sizeof(PING_TOKEN)+4] = PING_TOKEN "\n";
+  char buf[sizeof(resp)+100];
+  ssize_t rc;
+  struct timeval delay;
+
+  dmn->t_read = NULL;
+  if ((rc = read(dmn->fd,buf,sizeof(buf))) < 0)
+    {
+      char why[100];
+
+      if (ERRNO_IO_RETRY(errno))
+       {
+         /* Pretend it never happened. */
+         SET_READ_HANDLER(dmn);
+         return 0;
+       }
+      snprintf(why,sizeof(why),"unexpected read error: %s",
+              safe_strerror(errno));
+      daemon_down(dmn,why);
+      return 0;
+    }
+  if (rc == 0)
+    {
+      daemon_down(dmn,"read returned EOF");
+      return 0;
+    }
+  if (!dmn->echo_sent.tv_sec)
+    {
+      char why[sizeof(buf)+100];
+      snprintf(why,sizeof(why),"unexpected read returns %d bytes: %.*s",
+              (int)rc,(int)rc,buf);
+      daemon_down(dmn,why);
+      return 0;
+    }
+
+  /* We are expecting an echo response: is there any chance that the
+     response would not be returned entirely in the first read?  That
+     seems inconceivable... */
+  if ((rc != sizeof(resp)) || memcmp(buf,resp,sizeof(resp)))
+    {
+      char why[100+sizeof(buf)];
+      snprintf(why,sizeof(why),"read returned bad echo response of %d bytes "
+                              "(expecting %u): %.*s",
+              (int)rc,(u_int)sizeof(resp),(int)rc,buf);
+      daemon_down(dmn,why);
+      return 0;
+    }
+
+  time_elapsed(&delay,&dmn->echo_sent);
+  dmn->echo_sent.tv_sec = 0;
+  if (dmn->state == DAEMON_UNRESPONSIVE)
+    {
+      if (delay.tv_sec < gs.timeout)
+       {
+         dmn->state = DAEMON_UP;
+         zlog_warn("%s state -> up : echo response received after %ld.%06ld "
+                   "seconds", dmn->name,
+                   (long)delay.tv_sec, (long)delay.tv_usec);
+       }
+      else
+       zlog_warn("%s: slow echo response finally received after %ld.%06ld "
+                 "seconds", dmn->name,
+                 (long)delay.tv_sec, (long)delay.tv_usec);
+    }
+  else if (gs.loglevel > LOG_DEBUG+1)
+    zlog_debug("%s: echo response received after %ld.%06ld seconds",
+              dmn->name, (long)delay.tv_sec, (long)delay.tv_usec);
+
+  SET_READ_HANDLER(dmn);
+  if (dmn->t_wakeup)
+    thread_cancel(dmn->t_wakeup);
+  SET_WAKEUP_ECHO(dmn);
+
+  return 0;
+}
+
+static void
+daemon_up(struct daemon *dmn, const char *why)
+{
+  dmn->state = DAEMON_UP;
+  gs.numdown--;
+  dmn->connect_tries = 0;
+  zlog_notice("%s state -> up : %s",dmn->name,why);
+  if (gs.do_ping)
+    SET_WAKEUP_ECHO(dmn);
+  phase_check();
+}
+
+static int
+check_connect(struct thread *t_write)
+{
+  struct daemon *dmn = THREAD_ARG(t_write);
+  int sockerr;
+  socklen_t reslen = sizeof(sockerr);
+
+  dmn->t_write = NULL;
+  if (getsockopt(dmn->fd,SOL_SOCKET,SO_ERROR,(char *)&sockerr,&reslen) < 0)
+    {
+      zlog_warn("%s: check_connect: getsockopt failed: %s",
+               dmn->name,safe_strerror(errno));
+      daemon_down(dmn,"getsockopt failed checking connection success");
+      return 0;
+    }
+  if ((reslen == sizeof(sockerr)) && sockerr)
+    {
+      char why[100];
+      snprintf(why,sizeof(why),
+              "getsockopt reports that connection attempt failed: %s",
+              safe_strerror(sockerr));
+      daemon_down(dmn,why);
+      return 0;
+    }
+
+  daemon_up(dmn,"delayed connect succeeded");
+  return 0;
+}
+
+static int
+wakeup_connect_hanging(struct thread *t_wakeup)
+{
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+  char why[100];
+
+  dmn->t_wakeup = NULL;
+  snprintf(why,sizeof(why),"connection attempt timed out after %ld seconds",
+          gs.timeout);
+  daemon_down(dmn,why);
+  return 0;
+}
+
+/* Making connection to protocol daemon. */
+static int
+try_connect(struct daemon *dmn)
+{
+  int sock;
+  struct sockaddr_un addr;
+  socklen_t len;
+
+  if (gs.loglevel > LOG_DEBUG+1)
+    zlog_debug("%s: attempting to connect",dmn->name);
+  dmn->connect_tries++;
+
+  memset (&addr, 0, sizeof (struct sockaddr_un));
+  addr.sun_family = AF_UNIX;
+  snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s.vty",
+          gs.vtydir,dmn->name);
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+  len = addr.sun_len = SUN_LEN(&addr);
+#else
+  len = sizeof (addr.sun_family) + strlen (addr.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+  /* Quick check to see if we might succeed before we go to the trouble
+     of creating a socket. */
+  if (access(addr.sun_path, W_OK) < 0)
+    {
+      if (errno != ENOENT)
+        zlog_err("%s: access to socket %s denied: %s",
+               dmn->name,addr.sun_path,safe_strerror(errno));
+      return -1;
+    }
+
+  if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
+    {
+      zlog_err("%s(%s): cannot make socket: %s",
+              __func__,addr.sun_path, safe_strerror(errno));
+      return -1;
+    }
+
+  if (set_nonblocking(sock) < 0)
+    {
+      zlog_err("%s(%s): set_nonblocking(%d) failed",
+              __func__, addr.sun_path, sock);
+      close(sock);
+      return -1;
+    }
+
+  if (connect (sock, (struct sockaddr *) &addr, len) < 0)
+    {
+      if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK))
+       {
+         if (gs.loglevel > LOG_DEBUG)
+           zlog_debug("%s(%s): connect failed: %s",
+                      __func__,addr.sun_path, safe_strerror(errno));
+         close (sock);
+         return -1;
+       }
+      if (gs.loglevel > LOG_DEBUG)
+       zlog_debug("%s: connection in progress",dmn->name);
+      dmn->state = DAEMON_CONNECTING;
+      dmn->fd = sock;
+      dmn->t_write = thread_add_write(master,check_connect,dmn,dmn->fd);
+      dmn->t_wakeup = thread_add_timer(master,wakeup_connect_hanging,dmn,
+                                      gs.timeout);
+      SET_READ_HANDLER(dmn);
+      return 0;
+    }
+
+  dmn->fd = sock;
+  SET_READ_HANDLER(dmn);
+  daemon_up(dmn,"connect succeeded");
+  return 1;
+}
+
+static int
+phase_hanging(struct thread *t_hanging)
+{
+  gs.t_phase_hanging = NULL;
+  zlog_err("Phase [%s] hanging for %ld seconds, aborting phased restart",
+           phase_str[gs.phase],PHASE_TIMEOUT);
+  gs.phase = PHASE_NONE;
+  return 0;
+}
+
+static void
+set_phase(restart_phase_t new_phase)
+{
+  gs.phase = new_phase;
+  if (gs.t_phase_hanging)
+    thread_cancel(gs.t_phase_hanging);
+  gs.t_phase_hanging = thread_add_timer(master,phase_hanging,NULL,
+                                       PHASE_TIMEOUT);
+}
+
+static void
+phase_check(void)
+{
+  switch (gs.phase)
+    {
+    case PHASE_NONE:
+      break;
+    case PHASE_STOPS_PENDING:
+      if (gs.numpids)
+       break;
+      zlog_info("Phased restart: all routing daemon stop jobs have completed.");
+      set_phase(PHASE_WAITING_DOWN);
+      /*FALLTHRU*/
+    case PHASE_WAITING_DOWN:
+      if (gs.numdown+IS_UP(gs.special) < gs.numdaemons)
+        break;
+      zlog_info("Phased restart: all routing daemons now down.");
+      run_job(&gs.special->restart,"restart",gs.restart_command,1,1);
+      set_phase(PHASE_ZEBRA_RESTART_PENDING);
+      /*FALLTHRU*/
+    case PHASE_ZEBRA_RESTART_PENDING:
+      if (gs.special->restart.pid)
+       break;
+      zlog_info("Phased restart: %s restart job completed.",gs.special->name);
+      set_phase(PHASE_WAITING_ZEBRA_UP);
+      /*FALLTHRU*/
+    case PHASE_WAITING_ZEBRA_UP:
+      if (!IS_UP(gs.special))
+        break;
+      zlog_info("Phased restart: %s is now up.",gs.special->name);
+      {
+        struct daemon *dmn;
+       for (dmn = gs.daemons; dmn; dmn = dmn->next)
+         {
+           if (dmn != gs.special)
+             run_job(&dmn->restart,"start",gs.start_command,1,0);
+         }
+      }
+      gs.phase = PHASE_NONE;
+      THREAD_OFF(gs.t_phase_hanging);
+      zlog_notice("Phased global restart has completed.");
+      break;
+    }
+}
+
+static void
+try_restart(struct daemon *dmn)
+{
+  switch (gs.mode)
+  {
+  case MODE_MONITOR:
+    return;
+  case MODE_GLOBAL_RESTART:
+    run_job(&gs.restart,"restart",gs.restart_command,0,1);
+    break;
+  case MODE_SEPARATE_RESTART:
+    run_job(&dmn->restart,"restart",gs.restart_command,0,1);
+    break;
+  case MODE_PHASED_ZEBRA_RESTART:
+    if (dmn != gs.special)
+      {
+        if ((gs.special->state == DAEMON_UP) && (gs.phase == PHASE_NONE))
+         run_job(&dmn->restart,"restart",gs.restart_command,0,1);
+       else
+         zlog_debug("%s: postponing restart attempt because master %s daemon "
+                    "not up [%s], or phased restart in progress",
+                    dmn->name,gs.special->name,state_str[gs.special->state]);
+       break;
+      }
+    /*FALLTHRU*/
+  case MODE_PHASED_ALL_RESTART:
+    if ((gs.phase != PHASE_NONE) || gs.numpids)
+      {
+       if (gs.loglevel > LOG_DEBUG+1)
+         zlog_debug("postponing phased global restart: restart already in "
+                    "progress [%s], or outstanding child processes [%d]",
+                    phase_str[gs.phase],gs.numpids);
+        break;
+      }
+    /* Is it too soon for a restart? */
+    {
+      struct timeval delay;
+      if (time_elapsed(&delay,&gs.special->restart.time)->tv_sec <
+         gs.special->restart.interval)
+       {
+         if (gs.loglevel > LOG_DEBUG+1)
+           zlog_debug("postponing phased global restart: "
+                      "elapsed time %ld < retry interval %ld",
+                      (long)delay.tv_sec,gs.special->restart.interval);
+         break;
+       }
+    }
+    zlog_info("Phased restart: stopping all routing daemons.");
+    /* First step: stop all other daemons. */
+    for (dmn = gs.daemons; dmn; dmn = dmn->next)
+      {
+        if (dmn != gs.special)
+         run_job(&dmn->restart,"stop",gs.stop_command,1,1);
+      }
+    set_phase(PHASE_STOPS_PENDING);
+    break;
+  default:
+    zlog_err("error: unknown restart mode %d",gs.mode);
+    break;
+  }
+}
+
+static int
+wakeup_unresponsive(struct thread *t_wakeup)
+{
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+
+  dmn->t_wakeup = NULL;
+  if (dmn->state != DAEMON_UNRESPONSIVE)
+    zlog_err("%s: no longer unresponsive (now %s), "
+            "wakeup should have been cancelled!",
+            dmn->name,state_str[dmn->state]);
+  else
+    {
+      SET_WAKEUP_UNRESPONSIVE(dmn);
+      try_restart(dmn);
+    }
+  return 0;
+}
+
+static int
+wakeup_no_answer(struct thread *t_wakeup)
+{
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+
+  dmn->t_wakeup = NULL;
+  dmn->state = DAEMON_UNRESPONSIVE;
+  zlog_err("%s state -> unresponsive : no response yet to ping "
+          "sent %ld seconds ago",dmn->name,gs.timeout);
+  if (gs.unresponsive_restart)
+    {
+      SET_WAKEUP_UNRESPONSIVE(dmn);
+      try_restart(dmn);
+    }
+  return 0;
+}
+
+static int
+wakeup_send_echo(struct thread *t_wakeup)
+{
+  static const char echocmd[] = "echo " PING_TOKEN;
+  ssize_t rc;
+  struct daemon *dmn = THREAD_ARG(t_wakeup);
+
+  dmn->t_wakeup = NULL;
+  if (((rc = write(dmn->fd,echocmd,sizeof(echocmd))) < 0) ||
+      ((size_t)rc != sizeof(echocmd)))
+    {
+      char why[100+sizeof(echocmd)];
+      snprintf(why,sizeof(why),"write '%s' returned %d instead of %u",
+               echocmd,(int)rc,(u_int)sizeof(echocmd));
+      daemon_down(dmn,why);
+    }
+  else
+    {
+      gettimeofday(&dmn->echo_sent,NULL);
+      dmn->t_wakeup = thread_add_timer(master,wakeup_no_answer,dmn,gs.timeout);
+    }
+  return 0;
+}
+
+static void
+sigint(void)
+{
+  zlog_notice("Terminating on signal");
+  exit(0);
+}
+
+static int
+valid_command(const char *cmd)
+{
+  char *p;
+
+  return ((p = strchr(cmd,'%')) != NULL) && (*(p+1) == 's') && !strchr(p+1,'%');
+}
+
+/* This is an ugly hack to circumvent problems with passing command-line
+   arguments that contain spaces.  The fix is to use a configuration file. */
+static char *
+translate_blanks(const char *cmd, const char *blankstr)
+{
+  char *res;
+  char *p;
+  size_t bslen = strlen(blankstr);
+
+  if (!(res = strdup(cmd)))
+    {
+      perror("strdup");
+      exit(1);
+    }
+  while ((p = strstr(res,blankstr)) != NULL)
+    {
+      *p = ' ';
+      if (bslen != 1)
+        memmove(p+1,p+bslen,strlen(p+bslen)+1);
+    }
+  return res;
+}
+
+int
+main(int argc, char **argv)
+{
+  const char *progname;
+  int opt;
+  int daemon_mode = 0;
+  const char *pidfile = DEFAULT_PIDFILE;
+  const char *special = "zebra";
+  const char *blankstr = NULL;
+  static struct quagga_signal_t my_signals[] =
+  {
+    {
+      .signal = SIGINT,
+      .handler = sigint,
+    },
+    {
+      .signal = SIGTERM,
+      .handler = sigint,
+    },
+    {
+      .signal = SIGCHLD,
+      .handler = sigchild,
+    },
+  };
+
+  if ((progname = strrchr (argv[0], '/')) != NULL)
+    progname++;
+  else
+    progname = argv[0];
+
+  gs.restart.name = "all";
+  while ((opt = getopt_long(argc, argv, "aAb:dek:l:m:M:i:p:r:R:S:s:t:T:zvh",
+                           longopts, 0)) != EOF)
+    {
+      switch (opt)
+        {
+       case 0:
+         break;
+        case 'a':
+         if ((gs.mode != MODE_MONITOR) && (gs.mode != MODE_SEPARATE_RESTART))
+           {
+             fputs("Ambiguous operating mode selected.\n",stderr);
+             return usage(progname,1);
+           }
+         gs.mode = MODE_PHASED_ZEBRA_RESTART;
+         break;
+        case 'A':
+         if ((gs.mode != MODE_MONITOR) && (gs.mode != MODE_SEPARATE_RESTART))
+           {
+             fputs("Ambiguous operating mode selected.\n",stderr);
+             return usage(progname,1);
+           }
+         gs.mode = MODE_PHASED_ALL_RESTART;
+         break;
+       case 'b':
+         blankstr = optarg;
+         break;
+        case 'd':
+         daemon_mode = 1;
+         break;
+        case 'e':
+         gs.do_ping = 0;
+         break;
+        case 'k':
+         if (!valid_command(optarg))
+         {
+           fprintf(stderr,"Invalid kill command, must contain '%%s': %s\n",
+                   optarg);
+           return usage(progname,1);
+         }
+         gs.stop_command = optarg;
+         break;
+       case 'l':
+         {
+           char garbage[3];
+           if ((sscanf(optarg,"%d%1s",&gs.loglevel,garbage) != 1) ||
+               (gs.loglevel < LOG_EMERG))
+             {
+               fprintf(stderr,"Invalid loglevel argument: %s\n",optarg);
+               return usage(progname,1);
+             }
+         }
+         break;
+       case 'm':
+         {
+           char garbage[3];
+           if ((sscanf(optarg,"%ld%1s",
+                       &gs.min_restart_interval,garbage) != 1) ||
+               (gs.min_restart_interval < 0))
+             {
+               fprintf(stderr,"Invalid min_restart_interval argument: %s\n",
+                       optarg);
+               return usage(progname,1);
+             }
+         }
+         break;
+       case 'M':
+         {
+           char garbage[3];
+           if ((sscanf(optarg,"%ld%1s",
+                       &gs.max_restart_interval,garbage) != 1) ||
+               (gs.max_restart_interval < 0))
+             {
+               fprintf(stderr,"Invalid max_restart_interval argument: %s\n",
+                       optarg);
+               return usage(progname,1);
+             }
+         }
+         break;
+       case 'i':
+         {
+           char garbage[3];
+           int period;
+           if ((sscanf(optarg,"%d%1s",&period,garbage) != 1) ||
+               (gs.period < 1))
+             {
+               fprintf(stderr,"Invalid interval argument: %s\n",optarg);
+               return usage(progname,1);
+             }
+           gs.period = 1000*period;
+         }
+         break;
+        case 'p':
+         pidfile = optarg;
+         break;
+        case 'r':
+         if ((gs.mode == MODE_GLOBAL_RESTART) ||
+             (gs.mode == MODE_SEPARATE_RESTART))
+           {
+             fputs("Ambiguous operating mode selected.\n",stderr);
+             return usage(progname,1);
+           }
+         if (!valid_command(optarg))
+         {
+           fprintf(stderr,
+                   "Invalid restart command, must contain '%%s': %s\n",
+                   optarg);
+           return usage(progname,1);
+         }
+         gs.restart_command = optarg;
+         if (gs.mode == MODE_MONITOR)
+           gs.mode = MODE_SEPARATE_RESTART;
+         break;
+        case 'R':
+         if (gs.mode != MODE_MONITOR)
+           {
+             fputs("Ambiguous operating mode selected.\n",stderr);
+             return usage(progname,1);
+           }
+         if (strchr(optarg,'%'))
+           {
+             fprintf(stderr,
+                     "Invalid restart-all arg, must not contain '%%s': %s\n",
+                     optarg);
+             return usage(progname,1);
+           }
+         gs.restart_command = optarg;
+         gs.mode = MODE_GLOBAL_RESTART;
+         break;
+        case 's':
+         if (!valid_command(optarg))
+         {
+           fprintf(stderr,"Invalid start command, must contain '%%s': %s\n",
+                   optarg);
+           return usage(progname,1);
+         }
+         gs.start_command = optarg;
+         break;
+       case 'S':
+         gs.vtydir = optarg;
+         break;
+       case 't':
+         {
+           char garbage[3];
+           if ((sscanf(optarg,"%ld%1s",&gs.timeout,garbage) != 1) ||
+               (gs.timeout < 1))
+             {
+               fprintf(stderr,"Invalid timeout argument: %s\n",optarg);
+               return usage(progname,1);
+             }
+         }
+         break;
+       case 'T':
+         {
+           char garbage[3];
+           if ((sscanf(optarg,"%ld%1s",&gs.restart_timeout,garbage) != 1) ||
+               (gs.restart_timeout < 1))
+             {
+               fprintf(stderr,"Invalid restart timeout argument: %s\n",optarg);
+               return usage(progname,1);
+             }
+         }
+         break;
+        case 'z':
+         gs.unresponsive_restart = 1;
+         break;
+       case 'v':
+         printf ("%s version %s\n", progname, QUAGGA_VERSION);
+         puts("Copyright 2004 Andrew J. Schorr");
+         return 0;
+        case 'h':
+         return usage(progname,0);
+        default:
+         fputs("Invalid option.\n",stderr);
+         return usage(progname,1);
+        }
+    }
+  
+  if (gs.unresponsive_restart && (gs.mode == MODE_MONITOR))
+    {
+      fputs("Option -z requires a -r or -R restart option.\n",stderr);
+      return usage(progname,1);
+    }
+  switch (gs.mode)
+    {
+    case MODE_MONITOR:
+      if (gs.restart_command || gs.start_command || gs.stop_command)
+        {
+         fprintf(stderr,"No kill/(re)start commands needed for %s mode.\n",
+                 mode_str[gs.mode]);
+         return usage(progname,1);
+       }
+      break;
+    case MODE_GLOBAL_RESTART:
+    case MODE_SEPARATE_RESTART:
+      if (!gs.restart_command || gs.start_command || gs.stop_command)
+        {
+         fprintf(stderr,"No start/kill commands needed in [%s] mode.\n",
+                 mode_str[gs.mode]);
+         return usage(progname,1);
+       }
+      break;
+    case MODE_PHASED_ZEBRA_RESTART:
+    case MODE_PHASED_ALL_RESTART:
+      if (!gs.restart_command || !gs.start_command || !gs.stop_command)
+        {
+         fprintf(stderr,
+                 "Need start, kill, and restart commands in [%s] mode.\n",
+                 mode_str[gs.mode]);
+         return usage(progname,1);
+       }
+      break;
+    }
+
+  if (blankstr)
+    {
+      if (gs.restart_command)
+        gs.restart_command = translate_blanks(gs.restart_command,blankstr);
+      if (gs.start_command)
+        gs.start_command = translate_blanks(gs.start_command,blankstr);
+      if (gs.stop_command)
+        gs.stop_command = translate_blanks(gs.stop_command,blankstr);
+    }
+      
+  gs.restart.interval = gs.min_restart_interval;
+  master = thread_master_create();
+  signal_init (master, array_size(my_signals), my_signals);
+  srandom(time(NULL));
+
+  {
+    int i;
+    struct daemon *tail = NULL;
+
+    for (i = optind; i < argc; i++)
+      {
+       struct daemon *dmn;
+
+       if (!(dmn = (struct daemon *)calloc(1,sizeof(*dmn))))
+         {
+           fprintf(stderr,"calloc(1,%u) failed: %s\n",
+                   (u_int)sizeof(*dmn), safe_strerror(errno));
+           return 1;
+         }
+       dmn->name = dmn->restart.name = argv[i];
+       dmn->state = DAEMON_INIT;
+       gs.numdaemons++;
+       gs.numdown++;
+       dmn->fd = -1;
+       dmn->t_wakeup = thread_add_timer_msec(master,wakeup_init,dmn,
+                                             100+(random() % 900));
+       dmn->restart.interval = gs.min_restart_interval;
+       if (tail)
+         tail->next = dmn;
+       else
+         gs.daemons = dmn;
+       tail = dmn;
+
+       if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) ||
+            (gs.mode == MODE_PHASED_ALL_RESTART)) &&
+           !strcmp(dmn->name,special))
+         gs.special = dmn;
+      }
+  }
+  if (!gs.daemons)
+    {
+      fputs("Must specify one or more daemons to monitor.\n",stderr);
+      return usage(progname,1);
+    }
+  if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) ||
+      (gs.mode == MODE_PHASED_ALL_RESTART)) && !gs.special)
+    {
+      fprintf(stderr,"In mode [%s], but cannot find master daemon %s\n",
+             mode_str[gs.mode],special);
+      return usage(progname,1);
+    }
+  if (gs.special && (gs.numdaemons < 2))
+    {
+      fprintf(stderr,"Mode [%s] does not make sense with only 1 daemon "
+                    "to watch.\n",mode_str[gs.mode]);
+      return usage(progname,1);
+    }
+
+  zlog_default = openzlog(progname, ZLOG_NONE,
+                         LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+  zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+  if (daemon_mode)
+    {
+      zlog_set_level(NULL, ZLOG_DEST_SYSLOG, MIN(gs.loglevel,LOG_DEBUG));
+      if (daemon (0, 0) < 0)
+       {
+         fprintf(stderr, "Watchquagga daemon failed: %s", strerror(errno));
+         exit (1);
+       }
+    }
+  else
+    zlog_set_level(NULL, ZLOG_DEST_STDOUT, MIN(gs.loglevel,LOG_DEBUG));
+
+  /* Make sure we're not already running. */
+  pid_output (pidfile);
+
+  /* Announce which daemons are being monitored. */
+  {
+    struct daemon *dmn;
+    size_t len = 0;
+
+    for (dmn = gs.daemons; dmn; dmn = dmn->next)
+      len += strlen(dmn->name)+1;
+
+    {
+      char buf[len+1];
+      char *p = buf;
+
+      for (dmn = gs.daemons; dmn; dmn = dmn->next)
+       {
+         if (p != buf)
+           *p++ = ' ';
+         strcpy(p,dmn->name);
+         p += strlen(p);
+       }
+      zlog_notice("%s %s watching [%s], mode [%s]",
+                 progname, QUAGGA_VERSION, buf, mode_str[gs.mode]);
+    }
+  }
+
+  {
+    struct thread thread;
+
+    while (thread_fetch (master, &thread))
+      thread_call (&thread);
+  }
+
+  /* Not reached. */
+  return 0;
+}
diff --git a/zebra/.gitignore b/zebra/.gitignore
new file mode 100644 (file)
index 0000000..df703b6
--- /dev/null
@@ -0,0 +1,16 @@
+Makefile
+Makefile.in
+*.o
+zebra
+zebra.conf
+client
+testzebra
+tags
+TAGS
+.deps
+.nfs*
+.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/zebra/GNOME-PRODUCT-ZEBRA-MIB b/zebra/GNOME-PRODUCT-ZEBRA-MIB
new file mode 100644 (file)
index 0000000..96bcec5
--- /dev/null
@@ -0,0 +1,78 @@
+GNOME-PRODUCT-ZEBRA-MIB DEFINITIONS ::= BEGIN\r
+\r
+IMPORTS\r
+       MODULE-IDENTITY,\r
+       OBJECT-IDENTITY\r
+               FROM SNMPv2-SMI\r
+       gnomeProducts\r
+               FROM GNOME-SMI;\r
+\r
+zebra MODULE-IDENTITY\r
+       LAST-UPDATED "200004250000Z"\r
+       ORGANIZATION "GNOME project"\r
+       CONTACT-INFO\r
+               "GNU Network Object Model Environment project\r
+               \r
+               see http://www.gnome.org for contact persons of a particular\r
+               area or subproject of GNOME.\r
+\r
+               Administrative contact for MIB module:\r
+\r
+               Jochen Friedrich\r
+               Wingertstr. 70/1\r
+               68809 Neulussheim\r
+               Germany \r
+\r
+               email: snmp@gnome.org"\r
+       DESCRIPTION\r
+               "The product registrations for the various zebra subdeamons.\r
+               These registrations are guaranteed to be unique and are used\r
+               for SMUX registration by default (if not overridden manually)."\r
+       ::= { gnomeProducts 2 }\r
+\r
+zserv OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "zserv is part of the zebra project which again is a GNU\r
+               endorsed internet routing program.\r
+               zserv is the main zebra process which implements routing\r
+               entries with the kernel and handles routing updates between\r
+               other routing protocols."\r
+       ::= { zebra 1 }\r
+\r
+bgpd OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "bgpd is part of the zebra project which again is a GNU\r
+               endorsed internet routing program."\r
+       ::= { zebra 2 }\r
+\r
+ripd OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "ripd is part of the zebra project which again is a GNU\r
+               endorsed internet routing program."\r
+       ::= { zebra 3 }\r
+\r
+ripngd OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "ripngd is part of the zebra project which again is a GNU\r
+               endorsed internet routing program."\r
+       ::= { zebra 4 }\r
+\r
+ospfd OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "ospfd is part of the zebra project which again is a GNU\r
+               endorsed internet routing program."\r
+       ::= { zebra 5 }\r
+\r
+ospf6d OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "ospf6d is part of the zebra project which again is a GNU\r
+               endorsed internet routing program."\r
+       ::= { zebra 6 }\r
+\r
+END\r
diff --git a/zebra/GNOME-SMI b/zebra/GNOME-SMI
new file mode 100644 (file)
index 0000000..164732b
--- /dev/null
@@ -0,0 +1,53 @@
+GNOME-SMI DEFINITIONS ::= BEGIN\r
+\r
+IMPORTS\r
+       MODULE-IDENTITY,\r
+       OBJECT-IDENTITY,\r
+       enterprises\r
+               FROM SNMPv2-SMI;\r
+\r
+gnome MODULE-IDENTITY\r
+       LAST-UPDATED "9809010000Z"\r
+       ORGANIZATION "GNOME project"\r
+       CONTACT-INFO\r
+               "GNU Network Object Model Environment project\r
+               \r
+               see http://www.gnome.org for contact persons of a particular\r
+               area or subproject of GNOME.\r
+\r
+               Administrative contact for MIB module:\r
+\r
+               Jochen Friedrich\r
+               Wingertstr. 70/1\r
+               68809 Neulussheim\r
+               Germany \r
+\r
+               email: snmp@gnome.org"\r
+       DESCRIPTION\r
+               "The Structure of GNOME."\r
+       ::= { enterprises 3317 }        -- assigned by IANA\r
+\r
+gnomeProducts OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "gnomeProducts is the root OBJECT IDENTIFIER from\r
+               which sysObjectID values are assigned."\r
+       ::= { gnome 1 }\r
+\r
+gnomeMgmt OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "gnomeMgmt defines the subtree for production GNOME related\r
+               MIB registrations."\r
+       ::= { gnome 2 }\r
+\r
+gnomeTest OBJECT-IDENTITY\r
+       STATUS  current\r
+       DESCRIPTION\r
+               "gnomeTest defines the subtree for testing GNOME related\r
+               MIB registrations."\r
+       ::= { gnome 3 }\r
+\r
+-- more to come if necessary.\r
+\r
+END\r
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
new file mode 100644 (file)
index 0000000..b23f9f1
--- /dev/null
@@ -0,0 +1,76 @@
+include ../common.am
+
+## Process this file with automake to produce Makefile.in.
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+LIBCAP = @LIBCAP@
+
+ipforward = @IPFORWARD@
+if_method = @IF_METHOD@
+rt_method = @RT_METHOD@
+rtread_method = @RTREAD_METHOD@
+kernel_method = @KERNEL_METHOD@
+ioctl_method = @IOCTL_METHOD@
+
+otherobj = $(ioctl_method) $(ipforward) $(if_method) \
+       $(rt_method) $(rtread_method) $(kernel_method)
+
+if HAVE_NETLINK
+othersrc = zebra_fpm_netlink.c
+endif
+
+if HAVE_PROTOBUF
+protobuf_srcs = zebra_fpm_protobuf.c
+endif
+
+if DEV_BUILD
+dev_srcs = zebra_fpm_dt.c
+endif
+
+AM_CFLAGS = $(WERROR)
+
+sbin_PROGRAMS = zebra
+
+noinst_PROGRAMS = testzebra
+
+zebra_SOURCES = \
+       zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \
+       redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \
+       irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \
+       zebra_rnh.c \
+       $(othersrc) $(protobuf_srcs) $(dev_srcs)
+
+testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
+       zebra_vty.c \
+       kernel_null.c  redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c
+
+noinst_HEADERS = \
+       connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \
+       interface.h ipforward.h irdp.h router-id.h kernel_socket.h \
+       rt_netlink.h zebra_fpm.h zebra_fpm_private.h \
+       ioctl_solaris.h zebra_rnh.h
+
+zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS)
+
+testzebra_LDADD = ../lib/libzebra.la $(LIBCAP)
+
+zebra_DEPENDENCIES = $(otherobj)
+
+EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \
+        if_sysctl.c ipforward_proc.c \
+       ipforward_solaris.c ipforward_sysctl.c rt_netlink.c \
+       rt_socket.c rtread_netlink.c rtread_sysctl.c \
+       rtread_getmsg.c kernel_socket.c kernel_netlink.c \
+       ioctl.c ioctl_solaris.c \
+       GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB
+
+client : client_main.o ../lib/libzebra.la
+       $(CC) -g -o client client_main.o ../liblzebra.la $(LIBS) $(LIB_IPV6)
+
+quaggaconfdir = $(sysconfdir)
+
+examplesdir = $(exampledir)
+dist_examples_DATA = zebra.conf.sample
diff --git a/zebra/client_main.c b/zebra/client_main.c
new file mode 100644 (file)
index 0000000..75c8867
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * $Quagga: $Format:%an, %ai, %h$ $
+ *
+ * GNU Zebra client test main routine.
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "stream.h"
+#include "zclient.h"
+#include "thread.h"
+#include "table.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+
+struct thread *master;
+
+/* Zebra client structure. */
+struct zclient *zclient = NULL;
+
+/* Zebra socket. */
+int sock;
+
+/* IPv4 route add and delete test. */
+void
+zebra_test_ipv4 (int command, int type, char *prefix, char *gateway,
+                u_char distance)
+{
+  struct zapi_ipv4 api;
+  struct prefix_ipv4 p;
+  struct in_addr gate;
+  struct in_addr *gpnt;
+
+  str2prefix_ipv4 (prefix, &p);
+  inet_aton (gateway, &gate);
+  gpnt = &gate;
+
+  api.vrf_id = VRF_DEFAULT;
+  api.type = type;
+  api.flags = 0;
+
+  api.message = 0;
+  SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
+  api.nexthop_num = 1;
+  api.nexthop = &gpnt;
+  api.ifindex_num = 0;
+  if (distance)
+    {
+      SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+      api.distance = distance;
+    }
+  
+
+  switch (command)
+    {
+    case ZEBRA_IPV4_ROUTE_ADD:
+      zapi_ipv4_add (zclient, &p, &api);
+      break;
+    case ZEBRA_IPV4_ROUTE_DELETE:
+      zapi_ipv4_delete (zclient, &p, &api);
+      break;
+    }
+}
+
+#ifdef HAVE_IPV6
+/* IPv6 route add and delete test. */
+void
+zebra_test_v6 (int sock)
+{
+  struct prefix_ipv6 p;
+  struct in6_addr nexthop;
+
+  str2prefix_ipv6 ("3ffe:506::2/128", &p);
+  inet_pton (AF_INET6, "::1", &nexthop);
+
+  /* zebra_ipv6_add (sock, ZEBRA_ROUTE_STATIC, 0, &p, &nexthop, 1); */
+
+  sleep (5);
+  /* zebra_ipv6_delete (sock, ZEBRA_ROUTE_STATIC, 0, &p, &nexthop, 1); */
+}
+#endif /* HAVE_IPV6 */
+
+/* Print out usage and exit. */
+void
+usage_exit ()
+{
+  fprintf (stderr, "Usage: client filename\n");
+  exit (1);
+}
+
+struct zebra_info 
+{
+  char *str;
+  int type;
+} zebra_type[] = 
+{
+  { "static", ZEBRA_ROUTE_STATIC },
+  { "rip",    ZEBRA_ROUTE_RIP },
+  { "ripng",  ZEBRA_ROUTE_RIPNG },
+  { "babel",  ZEBRA_ROUTE_BABEL },
+  { "ospf",   ZEBRA_ROUTE_OSPF },
+  { "ospf6",  ZEBRA_ROUTE_OSPF6 },
+  { "bgp",    ZEBRA_ROUTE_BGP },
+  { "nhrp",   ZEBRA_ROUTE_NHRP },
+  { NULL,     0 }
+};
+
+/* Zebra route simulator. */
+void
+zebra_sim (FILE *fp)
+{
+  char buf[BUFSIZ];
+  char distance_str[BUFSIZ];
+  u_char distance;
+
+  while (fgets (buf, sizeof buf, fp))
+    {
+      int i;
+      int ret;
+      int type;
+      char str[BUFSIZ], command[BUFSIZ], prefix[BUFSIZ], gateway[BUFSIZ];
+
+      distance = 0;
+
+      if (*buf == '#')
+       continue;
+
+      type = ZEBRA_ROUTE_STATIC;
+
+      ret = sscanf (buf, "%s %s %s %s %s\n", command, str, prefix, gateway,
+                   distance_str);
+
+      if (ret == 5)
+       {
+         distance = atoi (distance_str);
+       }
+      else
+       {
+         ret = sscanf (buf, "%s %s %s %s\n", command, str, prefix, gateway);
+
+         if (ret != 4)
+           continue;
+       }
+
+      for (i = 0; i < 10; i++)
+       {
+         if (!zebra_type[i].str)
+           break;
+         if (strcmp (zebra_type[i].str, str) == 0)
+           {
+             type = zebra_type[i].type;
+             break;
+           }
+       }
+      
+      if (strcmp (command, "add") == 0)
+       {
+         zebra_test_ipv4 (ZEBRA_IPV4_ROUTE_ADD, type, prefix, gateway,
+                          distance);
+         printf ("%s", buf);
+         continue;
+       }
+
+      if (strcmp (command, "del") == 0)
+       {
+         zebra_test_ipv4 (ZEBRA_IPV4_ROUTE_DELETE, type, prefix, gateway,
+                          distance);
+         printf ("%s", buf);
+         continue;
+       }
+    }
+}
+
+/* Test zebra client main routine. */
+int
+main (int argc, char **argv)
+{
+  struct thread_master *master;
+  FILE *fp;
+
+  if (argc == 1)
+      usage_exit ();
+
+  master = thread_master_create ();
+  /* Establish connection to zebra. */
+  zclient = zclient_new (master);
+  zclient->enable = 1;
+#ifdef HAVE_TCP_ZEBRA
+  zclient->sock = zclient_socket ();
+#else
+  zclient->sock = zclient_socket_un (ZEBRA_SERV_PATH);
+#endif /* HAVE_TCP_ZEBRA */
+
+  /* Open simulation file. */
+  fp = fopen (argv[1], "r");
+  if (fp == NULL)
+    {
+      fprintf (stderr, "can't open %s\n", argv[1]);
+      exit (1);
+    }
+
+  /* Do main work. */
+  zebra_sim (fp);
+
+  sleep (100);
+
+  fclose (fp);
+  close (sock);
+
+  return 0;
+}
diff --git a/zebra/connected.c b/zebra/connected.c
new file mode 100644 (file)
index 0000000..1980007
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Address linked list routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "linklist.h"
+#include "if.h"
+#include "table.h"
+#include "rib.h"
+#include "table.h"
+#include "log.h"
+#include "memory.h"
+
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/interface.h"
+#include "zebra/connected.h"
+extern struct zebra_t zebrad;
+
+/* communicate the withdrawal of a connected address */
+static void
+connected_withdraw (struct connected *ifc)
+{
+  if (! ifc)
+    return;
+
+  /* Update interface address information to protocol daemon. */
+  if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+    {
+      zebra_interface_address_delete_update (ifc->ifp, ifc);
+
+      if (ifc->address->family == AF_INET)
+        if_subnet_delete (ifc->ifp, ifc);
+
+      if (ifc->address->family == AF_INET)
+        connected_down_ipv4 (ifc->ifp, ifc);
+#ifdef HAVE_IPV6
+      else
+        connected_down_ipv6 (ifc->ifp, ifc);
+#endif
+
+      UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL);
+    }
+
+  /* The address is not in the kernel anymore, so clear the flag */
+  UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+  if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+    {
+      listnode_delete (ifc->ifp->connected, ifc);
+      connected_free (ifc);
+    }
+}
+
+static void
+connected_announce (struct interface *ifp, struct connected *ifc)
+{
+  if (!ifc)
+    return;
+
+  if (!if_is_loopback(ifp) && ifc->address->family == AF_INET)
+    {
+      if (ifc->address->prefixlen == 32)
+        SET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED);
+      else
+        UNSET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED);
+    }
+
+  listnode_add (ifp->connected, ifc);
+
+  /* Update interface address information to protocol daemon. */
+  if (ifc->address->family == AF_INET)
+    if_subnet_add (ifp, ifc);
+
+  zebra_interface_address_add_update (ifp, ifc);
+
+  if (if_is_operative(ifp))
+    {
+      if (ifc->address->family == AF_INET)
+        connected_up_ipv4 (ifp, ifc);
+#ifdef HAVE_IPV6
+      else
+        connected_up_ipv6 (ifp, ifc);
+#endif
+    }
+}
+
+/* If same interface address is already exist... */
+struct connected *
+connected_check (struct interface *ifp, struct prefix *p)
+{
+  struct connected *ifc;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc))
+    if (prefix_same (ifc->address, p))
+      return ifc;
+
+  return NULL;
+}
+
+/* Check if two ifc's describe the same address in the same state */
+static int
+connected_same (struct connected *ifc1, struct connected *ifc2)
+{
+  if (ifc1->ifp != ifc2->ifp)
+    return 0;
+  
+  if (ifc1->destination)
+    if (!ifc2->destination)
+      return 0;
+  if (ifc2->destination)
+    if (!ifc1->destination)
+      return 0;
+  
+  if (ifc1->destination && ifc2->destination)
+    if (!prefix_same (ifc1->destination, ifc2->destination))
+      return 0;
+
+  if (ifc1->flags != ifc2->flags)
+    return 0;
+
+  if (ifc1->conf != ifc2->conf)
+    return 0;
+  
+  return 1;
+}
+
+/* Handle changes to addresses and send the neccesary announcements
+ * to clients. */
+static void
+connected_update(struct interface *ifp, struct connected *ifc)
+{
+  struct connected *current;
+  
+  /* Check same connected route. */
+  if ((current = connected_check (ifp, (struct prefix *) ifc->address)))
+    {
+      if (CHECK_FLAG(current->conf, ZEBRA_IFC_CONFIGURED))
+        SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
+       
+      /* Avoid spurious withdraws, this might be just the kernel 'reflecting'
+       * back an address we have already added.
+       */
+      if (connected_same (current, ifc))
+        {
+          /* nothing to do */
+          connected_free (ifc);
+          return;
+        }
+
+      /* Clear the configured flag on the old ifc, so it will be freed by
+       * connected withdraw. */
+      UNSET_FLAG(current->conf, ZEBRA_IFC_CONFIGURED);
+      connected_withdraw (current); /* implicit withdraw - freebsd does this */
+    }
+
+  /* If the connected is new or has changed, announce it, if it is usable */
+  if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
+    connected_announce(ifp, ifc);
+}
+
+/* Called from if_up(). */
+void
+connected_up_ipv4 (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix_ipv4 p;
+
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+    return;
+
+  PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
+
+  /* Apply mask to the network. */
+  apply_mask_ipv4 (&p);
+
+  /* In case of connected address is 0.0.0.0/0 we treat it tunnel
+     address. */
+  if (prefix_ipv4_any (&p))
+    return;
+
+  rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex,
+       ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST);
+
+  rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex,
+       ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_MULTICAST);
+
+  rib_update (ifp->vrf_id);
+}
+
+/* Add connected IPv4 route to the interface. */
+void
+connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, 
+                   u_char prefixlen, struct in_addr *broad, 
+                   const char *label)
+{
+  struct prefix_ipv4 *p;
+  struct connected *ifc;
+
+  /* Make connected structure. */
+  ifc = connected_new ();
+  ifc->ifp = ifp;
+  ifc->flags = flags;
+  /* If we get a notification from the kernel,
+   * we can safely assume the address is known to the kernel */
+  SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+  /* Allocate new connected address. */
+  p = prefix_ipv4_new ();
+  p->family = AF_INET;
+  p->prefix = *addr;
+  p->prefixlen = prefixlen;
+  ifc->address = (struct prefix *) p;
+  
+  /* If there is broadcast or peer address. */
+  if (broad)
+    {
+      p = prefix_ipv4_new ();
+      p->family = AF_INET;
+      p->prefix = *broad;
+      p->prefixlen = prefixlen;
+      ifc->destination = (struct prefix *) p;
+
+      /* validate the destination address */
+      if (CONNECTED_PEER(ifc))
+        {
+         if (IPV4_ADDR_SAME(addr,broad))
+           zlog_warn("warning: interface %s has same local and peer "
+                     "address %s, routing protocols may malfunction",
+                     ifp->name,inet_ntoa(*addr));
+        }
+      else
+        {
+         if (broad->s_addr != ipv4_broadcast_addr(addr->s_addr,prefixlen))
+           {
+             char buf[2][INET_ADDRSTRLEN];
+             struct in_addr bcalc;
+             bcalc.s_addr = ipv4_broadcast_addr(addr->s_addr,prefixlen);
+             zlog_warn("warning: interface %s broadcast addr %s/%d != "
+                       "calculated %s, routing protocols may malfunction",
+                       ifp->name,
+                       inet_ntop (AF_INET, broad, buf[0], sizeof(buf[0])),
+                       prefixlen,
+                       inet_ntop (AF_INET, &bcalc, buf[1], sizeof(buf[1])));
+           }
+        }
+
+    }
+  else
+    {
+      if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER))
+        {
+         zlog_warn("warning: %s called for interface %s "
+                   "with peer flag set, but no peer address supplied",
+                   __func__, ifp->name);
+         UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+       }
+
+      /* no broadcast or destination address was supplied */
+      if ((prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp))
+       zlog_warn("warning: PtP interface %s with addr %s/%d needs a "
+                 "peer address",ifp->name,inet_ntoa(*addr),prefixlen);
+    }
+
+  /* Label of this address. */
+  if (label)
+    ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label);
+
+  /* For all that I know an IPv4 address is always ready when we receive
+   * the notification. So it should be safe to set the REAL flag here. */
+  SET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
+
+  connected_update(ifp, ifc);
+}
+
+void
+connected_down_ipv4 (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix_ipv4 p;
+
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+    return;
+
+  PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
+
+  /* Apply mask to the network. */
+  apply_mask_ipv4 (&p);
+
+  /* In case of connected address is 0.0.0.0/0 we treat it tunnel
+     address. */
+  if (prefix_ipv4_any (&p))
+    return;
+
+  /* Same logic as for connected_up_ipv4(): push the changes into the head. */
+  rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id,
+                   SAFI_UNICAST);
+
+  rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id,
+                   SAFI_MULTICAST);
+
+  rib_update (ifp->vrf_id);
+}
+
+/* Delete connected IPv4 route to the interface. */
+void
+connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr,
+                      u_char prefixlen, struct in_addr *broad)
+{
+  struct prefix_ipv4 p;
+  struct connected *ifc;
+
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefix = *addr;
+  p.prefixlen = prefixlen;
+
+  ifc = connected_check (ifp, (struct prefix *) &p);
+  if (! ifc)
+    return;
+    
+  connected_withdraw (ifc);
+
+  rib_update (ifp->vrf_id);
+}
+
+#ifdef HAVE_IPV6
+void
+connected_up_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix_ipv6 p;
+
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+    return;
+
+  PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc));
+
+  /* Apply mask to the network. */
+  apply_mask_ipv6 (&p);
+
+#ifndef LINUX
+  /* XXX: It is already done by rib_bogus_ipv6 within rib_add_ipv6 */
+  if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix))
+    return;
+#endif
+
+  rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id,
+                RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST);
+
+  rib_update (ifp->vrf_id);
+}
+
+/* Add connected IPv6 route to the interface. */
+void
+connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr,
+                   u_char prefixlen, struct in6_addr *broad,
+                   const char *label)
+{
+  struct prefix_ipv6 *p;
+  struct connected *ifc;
+
+  /* Make connected structure. */
+  ifc = connected_new ();
+  ifc->ifp = ifp;
+  ifc->flags = flags;
+  /* If we get a notification from the kernel,
+   * we can safely assume the address is known to the kernel */
+  SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+  /* Allocate new connected address. */
+  p = prefix_ipv6_new ();
+  p->family = AF_INET6;
+  IPV6_ADDR_COPY (&p->prefix, addr);
+  p->prefixlen = prefixlen;
+  ifc->address = (struct prefix *) p;
+
+  /* If there is broadcast or peer address. */
+  if (broad)
+    {
+      if (IN6_IS_ADDR_UNSPECIFIED(broad))
+       zlog_warn("warning: %s called for interface %s with unspecified "
+                 "destination address; ignoring!", __func__, ifp->name);
+      else
+       {
+         p = prefix_ipv6_new ();
+         p->family = AF_INET6;
+         IPV6_ADDR_COPY (&p->prefix, broad);
+         p->prefixlen = prefixlen;
+         ifc->destination = (struct prefix *) p;
+       }
+    }
+  if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER) && !ifc->destination)
+    {
+      zlog_warn("warning: %s called for interface %s "
+               "with peer flag set, but no peer address supplied",
+               __func__, ifp->name);
+      UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+    }
+
+  /* Label of this address. */
+  if (label)
+    ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label);
+
+  /* On Linux, we only get here when DAD is complete, therefore we can set
+   * ZEBRA_IFC_REAL.
+   *
+   * On BSD, there currently doesn't seem to be a way to check for completion of
+   * DAD, so we replicate the old behaviour and set ZEBRA_IFC_REAL, although DAD
+   * might still be running.
+   */
+  SET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
+  connected_update(ifp, ifc);
+}
+
+void
+connected_down_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  struct prefix_ipv6 p;
+
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+    return;
+
+  PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc));
+
+  apply_mask_ipv6 (&p);
+
+  if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix))
+    return;
+
+  rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id,
+                   SAFI_UNICAST);
+
+  rib_update (ifp->vrf_id);
+}
+
+void
+connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address,
+                      u_char prefixlen, struct in6_addr *broad)
+{
+  struct prefix_ipv6 p;
+  struct connected *ifc;
+  
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  memcpy (&p.prefix, address, sizeof (struct in6_addr));
+  p.prefixlen = prefixlen;
+
+  ifc = connected_check (ifp, (struct prefix *) &p);
+  if (! ifc)
+    return;
+
+  connected_withdraw (ifc);
+
+  rib_update (ifp->vrf_id);
+}
+#endif /* HAVE_IPV6 */
diff --git a/zebra/connected.h b/zebra/connected.h
new file mode 100644 (file)
index 0000000..9595ddb
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Interface's address and mask.
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_CONNECTED_H
+#define _ZEBRA_CONNECTED_H
+
+extern struct connected *
+connected_check (struct interface *ifp, struct prefix *p);
+
+extern void
+connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, 
+                   u_char prefixlen, struct in_addr *broad, 
+                   const char *label);
+
+extern void
+connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr,
+                      u_char prefixlen, struct in_addr *broad);
+
+extern void connected_up_ipv4 (struct interface *, struct connected *);
+extern void connected_down_ipv4 (struct interface *, struct connected *);
+
+#ifdef HAVE_IPV6
+extern void
+connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *address,
+                   u_char prefixlen, struct in6_addr *broad,
+                   const char *label);
+extern void
+connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address,
+                      u_char prefixlen, struct in6_addr *broad);
+
+extern void connected_up_ipv6 (struct interface *, struct connected *);
+extern void connected_down_ipv6 (struct interface *ifp, struct connected *);
+
+#endif /* HAVE_IPV6 */
+
+#endif /*_ZEBRA_CONNECTED_H */
diff --git a/zebra/debug.c b/zebra/debug.c
new file mode 100644 (file)
index 0000000..7d023ce
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Zebra debug related function
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "debug.h"
+
+/* For debug statement. */
+unsigned long zebra_debug_event;
+unsigned long zebra_debug_packet;
+unsigned long zebra_debug_kernel;
+unsigned long zebra_debug_rib;
+unsigned long zebra_debug_fpm;
+unsigned long zebra_debug_nht;
+
+DEFUN (show_debugging_zebra,
+       show_debugging_zebra_cmd,
+       "show debugging zebra",
+       SHOW_STR
+       "Debugging information\n"
+       "Zebra configuration\n")
+{
+  vty_out (vty, "Zebra debugging status:%s", VTY_NEWLINE);
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    vty_out (vty, "  Zebra event debugging is on%s", VTY_NEWLINE);
+
+  if (IS_ZEBRA_DEBUG_PACKET)
+    {
+      if (IS_ZEBRA_DEBUG_SEND && IS_ZEBRA_DEBUG_RECV)
+       {
+         vty_out (vty, "  Zebra packet%s debugging is on%s",
+                  IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                  VTY_NEWLINE);
+       }
+      else
+       {
+         if (IS_ZEBRA_DEBUG_SEND)
+           vty_out (vty, "  Zebra packet send%s debugging is on%s",
+                    IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "  Zebra packet receive%s debugging is on%s",
+                    IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                    VTY_NEWLINE);
+       }
+    }
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    vty_out (vty, "  Zebra kernel debugging is on%s", VTY_NEWLINE);
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    vty_out (vty, "  Zebra RIB debugging is on%s", VTY_NEWLINE);
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    vty_out (vty, "  Zebra RIB queue debugging is on%s", VTY_NEWLINE);
+
+  if (IS_ZEBRA_DEBUG_FPM)
+    vty_out (vty, "  Zebra FPM debugging is on%s", VTY_NEWLINE);
+  if (IS_ZEBRA_DEBUG_NHT)
+    vty_out (vty, "  Zebra next-hop tracking debugging is on%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_events,
+       debug_zebra_events_cmd,
+       "debug zebra events",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra events\n")
+{
+  zebra_debug_event = ZEBRA_DEBUG_EVENT;
+  return CMD_WARNING;
+}
+
+DEFUN (debug_zebra_nht,
+       debug_zebra_nht_cmd,
+       "debug zebra nht",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra next hop tracking\n")
+{
+  zebra_debug_nht = ZEBRA_DEBUG_NHT;
+  return CMD_WARNING;
+}
+
+DEFUN (debug_zebra_packet,
+       debug_zebra_packet_cmd,
+       "debug zebra packet",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra packet\n")
+{
+  zebra_debug_packet = ZEBRA_DEBUG_PACKET;
+  SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND);
+  SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_packet_direct,
+       debug_zebra_packet_direct_cmd,
+       "debug zebra packet (recv|send|detail)",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n")
+{
+  zebra_debug_packet = ZEBRA_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND);
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV);
+  if (strncmp ("detail", argv[0], strlen (argv[0])) == 0)
+    SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_packet_detail,
+       debug_zebra_packet_detail_cmd,
+       "debug zebra packet (recv|send) detail",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n"
+       "Debug option set detailed information\n")
+{
+  zebra_debug_packet = ZEBRA_DEBUG_PACKET;
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND);
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV);
+  SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_DETAIL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_kernel,
+       debug_zebra_kernel_cmd,
+       "debug zebra kernel",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra between kernel interface\n")
+{
+  zebra_debug_kernel = ZEBRA_DEBUG_KERNEL;
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_rib,
+       debug_zebra_rib_cmd,
+       "debug zebra rib",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug RIB events\n")
+{
+  SET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_RIB);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_rib_q,
+       debug_zebra_rib_q_cmd,
+       "debug zebra rib queue",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug RIB events\n"
+       "Debug RIB queueing\n")
+{
+  SET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_RIB_Q);
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_zebra_fpm,
+       debug_zebra_fpm_cmd,
+       "debug zebra fpm",
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug zebra FPM events\n")
+{
+  SET_FLAG (zebra_debug_fpm, ZEBRA_DEBUG_FPM);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_events,
+       no_debug_zebra_events_cmd,
+       "no debug zebra events",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra events\n")
+{
+  zebra_debug_event = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_nht,
+       no_debug_zebra_nht_cmd,
+       "no debug zebra nht",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra next hop tracking\n")
+{
+  zebra_debug_nht = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_packet,
+       no_debug_zebra_packet_cmd,
+       "no debug zebra packet",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra packet\n")
+{
+  zebra_debug_packet = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_packet_direct,
+       no_debug_zebra_packet_direct_cmd,
+       "no debug zebra packet (recv|send)",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra packet\n"
+       "Debug option set for receive packet\n"
+       "Debug option set for send packet\n")
+{
+  if (strncmp ("send", argv[0], strlen (argv[0])) == 0)
+    UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND);
+  if (strncmp ("recv", argv[0], strlen (argv[0])) == 0)
+    UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_kernel,
+       no_debug_zebra_kernel_cmd,
+       "no debug zebra kernel",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug option set for zebra between kernel interface\n")
+{
+  zebra_debug_kernel = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_rib,
+       no_debug_zebra_rib_cmd,
+       "no debug zebra rib",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug zebra RIB\n")
+{
+  zebra_debug_rib = 0;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_rib_q,
+       no_debug_zebra_rib_q_cmd,
+       "no debug zebra rib queue",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug zebra RIB\n"
+       "Debug RIB queueing\n")
+{
+  UNSET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_RIB_Q);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_zebra_fpm,
+       no_debug_zebra_fpm_cmd,
+       "no debug zebra fpm",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug zebra FPM events\n")
+{
+  zebra_debug_fpm = 0;
+  return CMD_SUCCESS;
+}
+
+/* Debug node. */
+struct cmd_node debug_node =
+{
+  DEBUG_NODE,
+  "",                          /* Debug node has no interface. */
+  1
+};
+
+static int
+config_write_debug (struct vty *vty)
+{
+  int write = 0;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    {
+      vty_out (vty, "debug zebra events%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_ZEBRA_DEBUG_PACKET)
+    {
+      if (IS_ZEBRA_DEBUG_SEND && IS_ZEBRA_DEBUG_RECV)
+       {
+         vty_out (vty, "debug zebra packet%s%s",
+                  IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                  VTY_NEWLINE);
+         write++;
+       }
+      else
+       {
+         if (IS_ZEBRA_DEBUG_SEND)
+           vty_out (vty, "debug zebra packet send%s%s",
+                    IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                    VTY_NEWLINE);
+         else
+           vty_out (vty, "debug zebra packet recv%s%s",
+                    IS_ZEBRA_DEBUG_DETAIL ? " detail" : "",
+                    VTY_NEWLINE);
+         write++;
+       }
+    }
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      vty_out (vty, "debug zebra kernel%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_ZEBRA_DEBUG_RIB)
+    {
+      vty_out (vty, "debug zebra rib%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    {
+      vty_out (vty, "debug zebra rib queue%s", VTY_NEWLINE);
+      write++;
+    }
+  if (IS_ZEBRA_DEBUG_FPM)
+    {
+      vty_out (vty, "debug zebra fpm%s", VTY_NEWLINE);
+      write++;
+    }
+  return write;
+}
+
+void
+zebra_debug_init (void)
+{
+  zebra_debug_event = 0;
+  zebra_debug_packet = 0;
+  zebra_debug_kernel = 0;
+  zebra_debug_rib = 0;
+  zebra_debug_fpm = 0;
+
+  install_node (&debug_node, config_write_debug);
+
+  install_element (VIEW_NODE, &show_debugging_zebra_cmd);
+
+  install_element (ENABLE_NODE, &debug_zebra_events_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_nht_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_packet_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_packet_direct_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_packet_detail_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_kernel_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_rib_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_rib_q_cmd);
+  install_element (ENABLE_NODE, &debug_zebra_fpm_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_nht_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_rib_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_rib_q_cmd);
+  install_element (ENABLE_NODE, &no_debug_zebra_fpm_cmd);
+
+  install_element (CONFIG_NODE, &debug_zebra_events_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_nht_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_packet_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_packet_direct_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_packet_detail_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_kernel_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_rib_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_rib_q_cmd);
+  install_element (CONFIG_NODE, &debug_zebra_fpm_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_nht_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_rib_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_rib_q_cmd);
+  install_element (CONFIG_NODE, &no_debug_zebra_fpm_cmd);
+}
diff --git a/zebra/debug.h b/zebra/debug.h
new file mode 100644 (file)
index 0000000..0fb4dd9
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Zebra debug related function
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_DEBUG_H
+#define _ZEBRA_DEBUG_H
+
+/* Debug flags. */
+#define ZEBRA_DEBUG_EVENT   0x01
+
+#define ZEBRA_DEBUG_PACKET  0x01
+#define ZEBRA_DEBUG_SEND    0x20
+#define ZEBRA_DEBUG_RECV    0x40
+#define ZEBRA_DEBUG_DETAIL  0x80
+
+#define ZEBRA_DEBUG_KERNEL  0x01
+
+#define ZEBRA_DEBUG_RIB     0x01
+#define ZEBRA_DEBUG_RIB_Q   0x02
+
+#define ZEBRA_DEBUG_FPM     0x01
+#define ZEBRA_DEBUG_NHT     0x01
+
+/* Debug related macro. */
+#define IS_ZEBRA_DEBUG_EVENT  (zebra_debug_event & ZEBRA_DEBUG_EVENT)
+
+#define IS_ZEBRA_DEBUG_PACKET (zebra_debug_packet & ZEBRA_DEBUG_PACKET)
+#define IS_ZEBRA_DEBUG_SEND   (zebra_debug_packet & ZEBRA_DEBUG_SEND)
+#define IS_ZEBRA_DEBUG_RECV   (zebra_debug_packet & ZEBRA_DEBUG_RECV)
+#define IS_ZEBRA_DEBUG_DETAIL (zebra_debug_packet & ZEBRA_DEBUG_DETAIL)
+
+#define IS_ZEBRA_DEBUG_KERNEL (zebra_debug_kernel & ZEBRA_DEBUG_KERNEL)
+
+#define IS_ZEBRA_DEBUG_RIB  (zebra_debug_rib & ZEBRA_DEBUG_RIB)
+#define IS_ZEBRA_DEBUG_RIB_Q  (zebra_debug_rib & ZEBRA_DEBUG_RIB_Q)
+
+#define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM)
+#define IS_ZEBRA_DEBUG_NHT  (zebra_debug_nht & ZEBRA_DEBUG_NHT)
+
+extern unsigned long zebra_debug_event;
+extern unsigned long zebra_debug_packet;
+extern unsigned long zebra_debug_kernel;
+extern unsigned long zebra_debug_rib;
+extern unsigned long zebra_debug_fpm;
+extern unsigned long zebra_debug_nht;
+
+extern void zebra_debug_init (void);
+
+#endif /* _ZEBRA_DEBUG_H */
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c
new file mode 100644 (file)
index 0000000..99328a1
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Interface looking up by ioctl ().
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "memory.h"
+#include "log.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "zebra/interface.h"
+#include "zebra/rib.h"
+
+/* Interface looking up using infamous SIOCGIFCONF. */
+static int
+interface_list_ioctl (void)
+{
+  int ret;
+  int sock;
+#define IFNUM_BASE 32
+  int ifnum;
+  struct ifreq *ifreq;
+  struct ifconf ifconf;
+  struct interface *ifp;
+  int n;
+  int lastlen;
+
+  /* Normally SIOCGIFCONF works with AF_INET socket. */
+  sock = socket (AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) 
+    {
+      zlog_warn ("Can't make AF_INET socket stream: %s", safe_strerror (errno));
+      return -1;
+    }
+
+  /* Set initial ifreq count.  This will be double when SIOCGIFCONF
+     fail.  Solaris has SIOCGIFNUM. */
+#ifdef SIOCGIFNUM
+  ret = ioctl (sock, SIOCGIFNUM, &ifnum);
+  if (ret < 0)
+    ifnum = IFNUM_BASE;
+  else
+    ifnum++;
+#else
+  ifnum = IFNUM_BASE;
+#endif /* SIOCGIFNUM */
+
+  ifconf.ifc_buf = NULL;
+
+  lastlen = 0;
+  /* Loop until SIOCGIFCONF success. */
+  for (;;) 
+    {
+      ifconf.ifc_len = sizeof (struct ifreq) * ifnum;
+      ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len);
+
+      ret = ioctl(sock, SIOCGIFCONF, &ifconf);
+
+      if (ret < 0) 
+       {
+         zlog_warn ("SIOCGIFCONF: %s", safe_strerror(errno));
+         goto end;
+       }
+      /* Repeatedly get info til buffer fails to grow. */
+      if (ifconf.ifc_len > lastlen)
+       {
+          lastlen = ifconf.ifc_len;
+         ifnum += 10;
+         continue;
+       }
+      /* Success. */
+      break;
+    }
+
+  /* Allocate interface. */
+  ifreq = ifconf.ifc_req;
+
+#ifdef OPEN_BSD
+  for (n = 0; n < ifconf.ifc_len; )
+    {
+      int size;
+
+      ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n);
+      ifp = if_get_by_name_len(ifreq->ifr_name,
+                              strnlen(ifreq->ifr_name,
+                                      sizeof(ifreq->ifr_name)));
+      if_add_update (ifp);
+      size = ifreq->ifr_addr.sa_len;
+      if (size < sizeof (ifreq->ifr_addr))
+       size = sizeof (ifreq->ifr_addr);
+      size += sizeof (ifreq->ifr_name);
+      n += size;
+    }
+#else
+  for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq))
+    {
+      ifp = if_get_by_name_len(ifreq->ifr_name,
+                              strnlen(ifreq->ifr_name,
+                                      sizeof(ifreq->ifr_name)));
+      if_add_update (ifp);
+      ifreq++;
+    }
+#endif /* OPEN_BSD */
+
+ end:
+  close (sock);
+  XFREE (MTYPE_TMP, ifconf.ifc_buf);
+
+  return ret;
+}
+
+/* Get interface's index by ioctl. */
+static int
+if_get_index (struct interface *ifp)
+{
+#if defined(HAVE_IF_NAMETOINDEX)
+  /* Modern systems should have if_nametoindex(3). */
+  ifp->ifindex = if_nametoindex(ifp->name);
+#elif defined(SIOCGIFINDEX) && !defined(HAVE_BROKEN_ALIASES)
+  /* Fall-back for older linuxes. */
+  int ret;
+  struct ifreq ifreq;
+  static int if_fake_index;
+
+  ifreq_set_name (&ifreq, ifp);
+
+  ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
+  if (ret < 0)
+    {
+      /* Linux 2.0.X does not have interface index. */
+      ifp->ifindex = if_fake_index++;
+      return ifp->ifindex;
+    }
+
+  /* OK we got interface index. */
+#ifdef ifr_ifindex
+  ifp->ifindex = ifreq.ifr_ifindex;
+#else
+  ifp->ifindex = ifreq.ifr_index;
+#endif
+
+#else
+/* Linux 2.2.X does not provide individual interface index 
+   for aliases and we know it. For others issue a warning. */
+#if !defined(HAVE_BROKEN_ALIASES)
+#warning "Using if_fake_index. You may want to add appropriate"
+#warning "mapping from ifname to ifindex for your system..."
+#endif
+  /* This branch probably won't provide usable results, but anyway... */
+  static int if_fake_index = 1;
+  ifp->ifindex = if_fake_index++;
+#endif
+
+  return ifp->ifindex;
+}
+
+#ifdef SIOCGIFHWADDR
+static int
+if_get_hwaddr (struct interface *ifp)
+{
+  int ret;
+  struct ifreq ifreq;
+  int i;
+
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+  ifreq.ifr_addr.sa_family = AF_INET;
+
+  /* Fetch Hardware address if available. */
+  ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
+  if (ret < 0)
+    ifp->hw_addr_len = 0;
+  else
+    {
+      memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
+
+      for (i = 0; i < 6; i++)
+       if (ifp->hw_addr[i] != 0)
+         break;
+
+      if (i == 6)
+       ifp->hw_addr_len = 0;
+      else
+       ifp->hw_addr_len = 6;
+    }
+  return 0;
+}
+#endif /* SIOCGIFHWADDR */
+
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+
+static int
+if_getaddrs (void)
+{
+  int ret;
+  struct ifaddrs *ifap;
+  struct ifaddrs *ifapfree;
+  struct interface *ifp;
+  int prefixlen;
+
+  ret = getifaddrs (&ifap); 
+  if (ret != 0)
+    {
+      zlog_err ("getifaddrs(): %s", safe_strerror (errno));
+      return -1;
+    }
+
+  for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
+    {
+      if (ifap->ifa_addr == NULL)
+        {
+          zlog_err ("%s: nonsensical ifaddr with NULL ifa_addr, ifname %s",
+                    __func__, (ifap->ifa_name ? ifap->ifa_name : "(null)"));
+          continue;
+        }
+       
+      ifp = if_lookup_by_name (ifap->ifa_name);
+      if (ifp == NULL)
+       {
+         zlog_err ("if_getaddrs(): Can't lookup interface %s\n",
+                   ifap->ifa_name);
+         continue;
+       }
+
+      if (ifap->ifa_addr->sa_family == AF_INET)
+       {
+         struct sockaddr_in *addr;
+         struct sockaddr_in *mask;
+         struct sockaddr_in *dest;
+         struct in_addr *dest_pnt;
+         int flags = 0;
+
+         addr = (struct sockaddr_in *) ifap->ifa_addr;
+         mask = (struct sockaddr_in *) ifap->ifa_netmask;
+         prefixlen = ip_masklen (mask->sin_addr);
+
+         dest_pnt = NULL;
+
+         if (ifap->ifa_dstaddr &&
+             !IPV4_ADDR_SAME(&addr->sin_addr,
+                             &((struct sockaddr_in *)
+                               ifap->ifa_dstaddr)->sin_addr))
+           {
+             dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
+             dest_pnt = &dest->sin_addr;
+             flags = ZEBRA_IFA_PEER;
+           }
+         else if (ifap->ifa_broadaddr &&
+                  !IPV4_ADDR_SAME(&addr->sin_addr,
+                                  &((struct sockaddr_in *)
+                                    ifap->ifa_broadaddr)->sin_addr))
+           {
+             dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
+             dest_pnt = &dest->sin_addr;
+           }
+
+         connected_add_ipv4 (ifp, flags, &addr->sin_addr,
+                             prefixlen, dest_pnt, NULL);
+       }
+#ifdef HAVE_IPV6
+      if (ifap->ifa_addr->sa_family == AF_INET6)
+       {
+         struct sockaddr_in6 *addr;
+         struct sockaddr_in6 *mask;
+         struct sockaddr_in6 *dest;
+         struct in6_addr *dest_pnt;
+         int flags = 0;
+
+         addr = (struct sockaddr_in6 *) ifap->ifa_addr;
+         mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
+         prefixlen = ip6_masklen (mask->sin6_addr);
+
+         dest_pnt = NULL;
+
+         if (ifap->ifa_dstaddr &&
+             !IPV6_ADDR_SAME(&addr->sin6_addr,
+                             &((struct sockaddr_in6 *)
+                               ifap->ifa_dstaddr)->sin6_addr))
+           {
+             dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
+             dest_pnt = &dest->sin6_addr;
+             flags = ZEBRA_IFA_PEER;
+           }
+         else if (ifap->ifa_broadaddr &&
+                  !IPV6_ADDR_SAME(&addr->sin6_addr,
+                                  &((struct sockaddr_in6 *)
+                                    ifap->ifa_broadaddr)->sin6_addr))
+           {
+             dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
+             dest_pnt = &dest->sin6_addr;
+           }
+
+#if defined(KAME)
+         if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) 
+           {
+             addr->sin6_scope_id =
+                       ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]);
+             addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0;
+           }   
+#endif          
+
+         connected_add_ipv6 (ifp, flags, &addr->sin6_addr, prefixlen, 
+                             dest_pnt, NULL);
+       }
+#endif /* HAVE_IPV6 */
+    }
+
+  freeifaddrs (ifapfree);
+
+  return 0; 
+}
+#else /* HAVE_GETIFADDRS */
+/* Interface address lookup by ioctl.  This function only looks up
+   IPv4 address. */
+int
+if_get_addr (struct interface *ifp)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in mask;
+  struct sockaddr_in dest;
+  struct in_addr *dest_pnt;
+  u_char prefixlen;
+  int flags = 0;
+
+  /* Interface's name and address family. */
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+  ifreq.ifr_addr.sa_family = AF_INET;
+
+  /* Interface's address. */
+  ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      if (errno != EADDRNOTAVAIL)
+       {
+         zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno));
+         return ret;
+       }
+      return 0;
+    }
+  memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
+
+  /* Interface's network mask. */
+  ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      if (errno != EADDRNOTAVAIL) 
+       {
+         zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno));
+         return ret;
+       }
+      return 0;
+    }
+#ifdef ifr_netmask
+  memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
+#else
+  memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
+#endif /* ifr_netmask */
+  prefixlen = ip_masklen (mask.sin_addr);
+
+  /* Point to point or borad cast address pointer init. */
+  dest_pnt = NULL;
+
+  ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      if (errno != EADDRNOTAVAIL) 
+       zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno));
+    }
+  else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_dstaddr.sin_addr))
+    {
+      memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
+      dest_pnt = &dest.sin_addr;
+      flags = ZEBRA_IFA_PEER;
+    }
+  if (!dest_pnt)
+    {
+      ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
+      if (ret < 0) 
+       {
+         if (errno != EADDRNOTAVAIL) 
+           zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno));
+       }
+      else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_broadaddr.sin_addr))
+        {
+         memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
+         dest_pnt = &dest.sin_addr;
+        }
+    }
+
+
+  /* Set address to the interface. */
+  connected_add_ipv4 (ifp, flags, &addr.sin_addr, prefixlen, dest_pnt, NULL);
+
+  return 0;
+}
+#endif /* HAVE_GETIFADDRS */
+
+/* Fetch interface information via ioctl(). */
+static void
+interface_info_ioctl ()
+{
+  struct listnode *node, *nnode;
+  struct interface *ifp;
+  
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    {
+      if_get_index (ifp);
+#ifdef SIOCGIFHWADDR
+      if_get_hwaddr (ifp);
+#endif /* SIOCGIFHWADDR */
+      if_get_flags (ifp);
+#ifndef HAVE_GETIFADDRS
+      if_get_addr (ifp);
+#endif /* ! HAVE_GETIFADDRS */
+      if_get_mtu (ifp);
+      if_get_metric (ifp);
+    }
+}
+
+/* Lookup all interface information. */
+void
+interface_list (struct zebra_vrf *zvrf)
+{
+  if (zvrf->vrf_id != VRF_DEFAULT)
+    {
+      zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
+      return;
+    }
+  /* Linux can do both proc & ioctl, ioctl is the only way to get
+     interface aliases in 2.2 series kernels. */
+#ifdef HAVE_PROC_NET_DEV
+  interface_list_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+  interface_list_ioctl ();
+
+  /* After listing is done, get index, address, flags and other
+     interface's information. */
+  interface_info_ioctl ();
+
+#ifdef HAVE_GETIFADDRS
+  if_getaddrs ();
+#endif /* HAVE_GETIFADDRS */
+
+#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
+  /* Linux provides interface's IPv6 address via
+     /proc/net/if_inet6. */
+  ifaddr_proc_ipv6 ();
+#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
+}
diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c
new file mode 100644 (file)
index 0000000..b399812
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Interface looking up by ioctl () on Solaris.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "memory.h"
+#include "log.h"
+#include "privs.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "zebra/interface.h"
+#include "zebra/ioctl_solaris.h"
+#include "zebra/rib.h"
+
+static int if_get_addr (struct interface *, struct sockaddr *, const char *);
+static void interface_info_ioctl (struct interface *);
+extern struct zebra_privs_t zserv_privs;
+
+static int
+interface_list_ioctl (int af)
+{
+  int ret;
+  int sock;
+#define IFNUM_BASE 32
+  struct lifnum lifn;
+  int ifnum;
+  struct lifreq *lifreq;
+  struct lifconf lifconf;
+  struct interface *ifp;
+  int n;
+  int save_errno;
+  size_t needed, lastneeded = 0;
+  char *buf = NULL;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  
+  sock = socket (af, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      zlog_warn ("Can't make %s socket stream: %s",
+                 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
+                 
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+        
+      return -1;
+    }
+
+calculate_lifc_len:     /* must hold privileges to enter here */
+  lifn.lifn_family = af;
+  lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
+  ret = ioctl (sock, SIOCGLIFNUM, &lifn);
+  save_errno = errno;
+  
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  if (ret < 0)
+    {
+      zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
+                 safe_strerror (save_errno));
+      close (sock);
+      return -1;
+    }
+  ifnum = lifn.lifn_count;
+
+  /*
+   * When calculating the buffer size needed, add a small number
+   * of interfaces to those we counted.  We do this to capture
+   * the interface status of potential interfaces which may have
+   * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
+   */
+  needed = (ifnum + 4) * sizeof (struct lifreq);
+  if (needed > lastneeded || needed < lastneeded / 2)
+    {
+      if (buf != NULL)
+        XFREE (MTYPE_TMP, buf);
+      if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
+        {
+          zlog_warn ("interface_list_ioctl: malloc failed");
+          close (sock);
+          return -1;
+        }
+    }
+  lastneeded = needed;
+
+  lifconf.lifc_family = af;
+  lifconf.lifc_flags = LIFC_NOXMIT;
+  lifconf.lifc_len = needed;
+  lifconf.lifc_buf = buf;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+    
+  ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
+
+  if (ret < 0)
+    {
+      if (errno == EINVAL)
+        goto calculate_lifc_len; /* deliberately hold privileges */
+
+      zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
+
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+      goto end;
+    }
+
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+    
+  /* Allocate interface. */
+  lifreq = lifconf.lifc_req;
+
+  for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
+    {
+      /* we treat Solaris logical interfaces as addresses, because that is
+       * how PF_ROUTE on Solaris treats them. Hence we can not directly use
+       * the lifreq_name to get the ifp.  We need to normalise the name
+       * before attempting get.
+       *
+       * Solaris logical interface names are in the form of:
+       * <interface name>:<logical interface id>
+       */
+      unsigned int normallen = 0;
+      uint64_t lifflags;
+      
+      /* We should exclude ~IFF_UP interfaces, as we'll find out about them
+       * coming up later through RTM_NEWADDR message on the route socket.
+       */
+      if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
+                           lifreq->lifr_addr.ss_family)
+          || !CHECK_FLAG (lifflags, IFF_UP))
+        {
+          lifreq++;
+          continue;
+        }
+      
+      /* Find the normalised name */
+      while ( (normallen < sizeof(lifreq->lifr_name))
+             && ( *(lifreq->lifr_name + normallen) != '\0')
+             && ( *(lifreq->lifr_name + normallen) != ':') )
+        normallen++;
+      
+      ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
+
+      if (lifreq->lifr_addr.ss_family == AF_INET)
+        ifp->flags |= IFF_IPV4;
+
+      if (lifreq->lifr_addr.ss_family == AF_INET6)
+        {
+#ifdef HAVE_IPV6
+          ifp->flags |= IFF_IPV6;
+#else
+          lifreq++;
+          continue;
+#endif /* HAVE_IPV6 */
+        }
+        
+      if_add_update (ifp);
+
+      interface_info_ioctl (ifp);
+      
+      /* If a logical interface pass the full name so it can be
+       * as a label on the address
+       */
+      if ( *(lifreq->lifr_name + normallen) != '\0')
+        if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
+                     lifreq->lifr_name);
+      else
+        if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
+        
+      /* Poke the interface flags. Lets IFF_UP mangling kick in */
+      if_flags_update (ifp, ifp->flags);
+      
+      lifreq++;
+    }
+
+end:
+  close (sock);
+  XFREE (MTYPE_TMP, lifconf.lifc_buf);
+  return ret;
+}
+
+/* Get interface's index by ioctl. */
+static int
+if_get_index (struct interface *ifp)
+{
+  int ret;
+  struct lifreq lifreq;
+
+  lifreq_set_name (&lifreq, ifp->name);
+
+  if (ifp->flags & IFF_IPV4)
+    ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
+  else if (ifp->flags & IFF_IPV6)
+    ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
+  else
+    ret = -1;
+
+  if (ret < 0)
+    {
+      zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
+      return ret;
+    }
+
+  /* OK we got interface index. */
+#ifdef ifr_ifindex
+  ifp->ifindex = lifreq.lifr_ifindex;
+#else
+  ifp->ifindex = lifreq.lifr_index;
+#endif
+  return ifp->ifindex;
+
+}
+
+
+/* Interface address lookup by ioctl.  This function only looks up
+   IPv4 address. */
+#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ?  \
+                sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
+
+#define SIN(s) ((struct sockaddr_in *)(s))
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+
+/* Retrieve address information for the given ifp */
+static int
+if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
+{
+  int ret;
+  struct lifreq lifreq;
+  struct sockaddr_storage mask, dest;
+  char *dest_pnt = NULL;
+  u_char prefixlen = 0;
+  afi_t af;
+  int flags = 0;
+
+  /* Interface's name and address family.
+   * We need to use the logical interface name / label, if we've been
+   * given one, in order to get the right address
+   */
+  strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
+
+  /* Interface's address. */
+  memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
+  af = addr->sa_family;
+
+  /* Point to point or broad cast address pointer init. */
+  dest_pnt = NULL;
+
+  if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0)
+    {
+      memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
+      if (af == AF_INET)
+        dest_pnt = (char *) &(SIN (&dest)->sin_addr);
+      else
+        dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
+      flags = ZEBRA_IFA_PEER;
+    }
+
+  if (af == AF_INET)
+    {
+      ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
+      
+      if (ret < 0)
+        {
+          if (errno != EADDRNOTAVAIL)
+            {
+              zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
+                         safe_strerror (errno));
+              return ret;
+            }
+          return 0;
+        }
+      memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
+
+      prefixlen = ip_masklen (SIN (&mask)->sin_addr);
+      if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
+       {
+          memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
+          dest_pnt = (char *) &SIN (&dest)->sin_addr;
+        }
+    }
+#ifdef HAVE_IPV6
+  else if (af == AF_INET6)
+    {
+      if (if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq) < 0)
+       {
+         if (ifp->flags & IFF_POINTOPOINT)
+           prefixlen = IPV6_MAX_BITLEN;
+         else
+           zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
+                      ifp->name, safe_strerror (errno));
+       }
+      else
+       {
+         prefixlen = lifreq.lifr_addrlen;
+       }
+    }
+#endif /* HAVE_IPV6 */
+
+  /* Set address to the interface. */
+  if (af == AF_INET)
+    connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
+                        (struct in_addr *) dest_pnt, label);
+#ifdef HAVE_IPV6
+  else if (af == AF_INET6)
+    connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
+                        (struct in6_addr *) dest_pnt, label);
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/* Fetch interface information via ioctl(). */
+static void
+interface_info_ioctl (struct interface *ifp)
+{
+  if_get_index (ifp);
+  if_get_flags (ifp);
+  if_get_mtu (ifp);
+  if_get_metric (ifp);
+}
+
+/* Lookup all interface information. */
+void
+interface_list (struct zebra_vrf *zvrf)
+{
+  if (zvrf->vrf_id != VRF_DEFAULT)
+    {
+      zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
+      return;
+    }
+  interface_list_ioctl (AF_INET);
+  interface_list_ioctl (AF_INET6);
+  interface_list_ioctl (AF_UNSPEC);
+}
+
+struct connected *
+if_lookup_linklocal (struct interface *ifp)
+{
+#ifdef HAVE_IPV6
+  struct listnode *node;
+  struct connected *ifc;
+
+  if (ifp == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
+    {
+      if ((ifc->address->family == AF_INET6) &&
+          (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
+        return ifc;
+    }
+#endif /* HAVE_IPV6 */
+
+  return NULL;
+}
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
new file mode 100644 (file)
index 0000000..245b7b2
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Interface looking up by netlink.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "zebra/zserv.h"
+#include "rt_netlink.h"
+
+/* Interface information read by netlink. */
+void
+interface_list (struct zebra_vrf *zvrf)
+{
+  interface_lookup_netlink (zvrf);
+}
diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c
new file mode 100644 (file)
index 0000000..bb48f61
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Get interface's address and mask information by sysctl() function.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "connected.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "log.h"
+#include "interface.h"
+#include "vrf.h"
+
+#include "zebra/rt.h"
+#include "zebra/kernel_socket.h"
+#include "zebra/rib.h"
+
+void
+ifstat_update_sysctl (void)
+{
+  caddr_t ref, buf, end;
+  size_t bufsiz;
+  struct if_msghdr *ifm;
+  struct interface *ifp;
+
+#define MIBSIZ 6
+  int mib[MIBSIZ] =
+  { 
+    CTL_NET,
+    PF_ROUTE,
+    0,
+    0, /*  AF_INET & AF_INET6 */
+    NET_RT_IFLIST,
+    0 
+  };
+
+  /* Query buffer size. */
+  if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog_warn ("sysctl() error by %s", safe_strerror (errno));
+      return;
+    }
+
+  /* We free this memory at the end of this function. */
+  ref = buf = XMALLOC (MTYPE_TMP, bufsiz);
+
+  /* Fetch interface informations into allocated buffer. */
+  if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno));
+      return;
+    }
+
+  /* Parse both interfaces and addresses. */
+  for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) 
+    {
+      ifm = (struct if_msghdr *) buf;
+      if (ifm->ifm_type == RTM_IFINFO)
+       {
+         ifp = if_lookup_by_index (ifm->ifm_index);
+         if (ifp)
+           ifp->stats = ifm->ifm_data;
+       }
+    }
+
+  /* Free sysctl buffer. */
+  XFREE (MTYPE_TMP, ref);
+
+  return;
+}
+
+/* Interface listing up function using sysctl(). */
+void
+interface_list (struct zebra_vrf *zvrf)
+{
+  caddr_t ref, buf, end;
+  size_t bufsiz;
+  struct if_msghdr *ifm;
+
+#define MIBSIZ 6
+  int mib[MIBSIZ] =
+  { 
+    CTL_NET,
+    PF_ROUTE,
+    0,
+    0, /*  AF_INET & AF_INET6 */
+    NET_RT_IFLIST,
+    0 
+  };
+
+  if (zvrf->vrf_id != VRF_DEFAULT)
+    {
+      zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
+      return;
+    }
+
+  /* Query buffer size. */
+  if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog (NULL, LOG_WARNING, "sysctl() error by %s", safe_strerror (errno));
+      return;
+    }
+
+  /* We free this memory at the end of this function. */
+  ref = buf = XMALLOC (MTYPE_TMP, bufsiz);
+
+  /* Fetch interface informations into allocated buffer. */
+  if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno));
+      return;
+    }
+
+  /* Parse both interfaces and addresses. */
+  for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) 
+    {
+      ifm = (struct if_msghdr *) buf;
+
+      switch (ifm->ifm_type) 
+       {
+       case RTM_IFINFO:
+         ifm_read (ifm);
+         break;
+       case RTM_NEWADDR:
+         ifam_read ((struct ifa_msghdr *) ifm);
+         break;
+       default:
+         zlog_info ("interfaces_list(): unexpected message type");
+         XFREE (MTYPE_TMP, ref);
+         return;
+         break;
+       }
+    }
+
+  /* Free sysctl buffer. */
+  XFREE (MTYPE_TMP, ref);
+}
diff --git a/zebra/interface.c b/zebra/interface.c
new file mode 100644 (file)
index 0000000..f8b946f
--- /dev/null
@@ -0,0 +1,2817 @@
+/*
+ * Interface function.
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "log.h"
+#include "zclient.h"
+#include "vrf.h"
+#include "command.h"
+
+#include "zebra/interface.h"
+#include "zebra/rtadv.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/irdp.h"
+
+#if defined (HAVE_RTADV)
+/* Order is intentional.  Matches RFC4191.  This array is also used for
+   command matching, so only modify with care. */
+const char *rtadv_pref_strs[] = { "medium", "high", "INVALID", "low", 0 };
+#endif /* HAVE_RTADV */
+
+/* We don't have a tidy top-level instance object for zebra, or interfaces */
+static struct zebra_if_defaults zif_defaults = {
+  .linkdetect = IF_LINKDETECT_UNSPEC,
+};
+
+/* helper only for if_zebra_linkdetect */
+static void
+if_zebra_linkdetect_set_val (struct interface *ifp, zebra_if_linkdetect val)
+{
+  switch (val)
+    {
+      case IF_LINKDETECT_ON:
+        SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+        break;
+      case IF_LINKDETECT_OFF:
+        UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+        break;
+      default: break;
+    }
+}
+
+static void
+if_zebra_linkdetect_set (struct interface *ifp)
+{
+  struct zebra_if *zif = ifp->info;
+  assert (zif != NULL);
+  int if_was_operative = if_is_operative(ifp);
+  
+  /* If user has explicitly configured for the interface, let that set */
+  if (zif->linkdetect != IF_LINKDETECT_UNSPEC)
+    if_zebra_linkdetect_set_val (ifp, zif->linkdetect);
+  else 
+    {
+      /* general compiled in default is to set */
+      SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+      /* but user can specify a default too */
+      if_zebra_linkdetect_set_val (ifp, zif_defaults.linkdetect);
+    }
+  /* When linkdetection is enabled, interface might come down */
+  if (!if_is_operative(ifp) && if_was_operative) if_down(ifp);
+  /* Alternatively, it may come up after disabling link detection */
+  if (if_is_operative(ifp) && !if_was_operative) if_up(ifp);
+}
+
+/* Called when new interface is added. */
+static int
+if_zebra_new_hook (struct interface *ifp)
+{
+  struct zebra_if *zebra_if;
+
+  zebra_if = XCALLOC (MTYPE_TMP, sizeof (struct zebra_if));
+
+  zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC;
+  zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF;
+
+  switch (zif_defaults.linkdetect)
+    {
+      case IF_LINKDETECT_OFF:
+        UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+        break;
+      case IF_LINKDETECT_UNSPEC:
+      case IF_LINKDETECT_ON:
+      default:
+        SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+        break;
+    }
+  
+#if defined (HAVE_RTADV)
+  {
+    /* Set default router advertise values. */
+    struct rtadvconf *rtadv;
+
+    rtadv = &zebra_if->rtadv;
+
+    rtadv->AdvSendAdvertisements = 0;
+    rtadv->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
+    rtadv->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
+    rtadv->AdvIntervalTimer = 0;
+    rtadv->AdvManagedFlag = 0;
+    rtadv->AdvOtherConfigFlag = 0;
+    rtadv->AdvHomeAgentFlag = 0;
+    rtadv->AdvLinkMTU = 0;
+    rtadv->AdvReachableTime = 0;
+    rtadv->AdvRetransTimer = 0;
+    rtadv->AdvCurHopLimit = 0;
+    rtadv->AdvDefaultLifetime = -1; /* derive from MaxRtrAdvInterval */
+    rtadv->HomeAgentPreference = 0;
+    rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */
+    rtadv->AdvIntervalOption = 0;
+    rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
+
+    rtadv->AdvPrefixList = list_new ();
+  }    
+#endif /* HAVE_RTADV */
+
+  /* Initialize installed address chains tree. */
+  zebra_if->ipv4_subnets = route_table_init ();
+
+  ifp->info = zebra_if;
+  return 0;
+}
+
+/* Called when interface is deleted. */
+static int
+if_zebra_delete_hook (struct interface *ifp)
+{
+  struct zebra_if *zebra_if;
+  
+  if (ifp->info)
+    {
+      zebra_if = ifp->info;
+
+      /* Free installed address chains tree. */
+      if (zebra_if->ipv4_subnets)
+       route_table_finish (zebra_if->ipv4_subnets);
+
+      XFREE (MTYPE_TMP, zebra_if);
+    }
+
+  return 0;
+}
+
+/* Tie an interface address to its derived subnet list of addresses. */
+int
+if_subnet_add (struct interface *ifp, struct connected *ifc)
+{
+  struct route_node *rn;
+  struct zebra_if *zebra_if;
+  struct prefix cp;
+  struct list *addr_list;
+
+  assert (ifp && ifp->info && ifc);
+  zebra_if = ifp->info;
+
+  /* Get address derived subnet node and associated address list, while marking
+     address secondary attribute appropriately. */
+  cp = *ifc->address;
+  apply_mask (&cp);
+  rn = route_node_get (zebra_if->ipv4_subnets, &cp);
+
+  if ((addr_list = rn->info))
+    SET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY);
+  else
+    {
+      UNSET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY);
+      rn->info = addr_list = list_new ();
+      route_lock_node (rn);
+    }
+
+  /* Tie address at the tail of address list. */
+  listnode_add (addr_list, ifc);
+  
+  /* Return list element count. */
+  return (addr_list->count);
+}
+
+/* Untie an interface address from its derived subnet list of addresses. */
+int
+if_subnet_delete (struct interface *ifp, struct connected *ifc)
+{
+  struct route_node *rn;
+  struct zebra_if *zebra_if;
+  struct list *addr_list;
+
+  assert (ifp && ifp->info && ifc);
+  zebra_if = ifp->info;
+
+  /* Get address derived subnet node. */
+  rn = route_node_lookup (zebra_if->ipv4_subnets, ifc->address);
+  if (! (rn && rn->info))
+    {
+      zlog_warn("Trying to remove an address from an unknown subnet."
+                " (please report this bug)");
+      return -1;
+    }
+  route_unlock_node (rn);
+  
+  /* Untie address from subnet's address list. */
+  addr_list = rn->info;
+
+  /* Deleting an address that is not registered is a bug.
+   * In any case, we shouldn't decrement the lock counter if the address
+   * is unknown. */
+  if (!listnode_lookup(addr_list, ifc))
+    {
+      zlog_warn("Trying to remove an address from a subnet where it is not"
+                " currently registered. (please report this bug)");
+      return -1;
+    }
+
+  listnode_delete (addr_list, ifc);
+  route_unlock_node (rn);
+
+  /* Return list element count, if not empty. */
+  if (addr_list->count)
+    {
+      /* If deleted address is primary, mark subsequent one as such and distribute. */
+      if (! CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY))
+       {
+         ifc = listgetdata ((struct listnode *)listhead (addr_list));
+         zebra_interface_address_delete_update (ifp, ifc);
+         UNSET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY);
+         /* XXX: Linux kernel removes all the secondary addresses when the primary
+          * address is removed. We could try to work around that, though this is
+          * non-trivial. */
+         zebra_interface_address_add_update (ifp, ifc);
+       }
+      
+      return addr_list->count;
+    }
+  
+  /* Otherwise, free list and route node. */
+  list_free (addr_list);
+  rn->info = NULL;
+  route_unlock_node (rn);
+
+  return 0;
+}
+
+/* if_flags_mangle: A place for hacks that require mangling
+ * or tweaking the interface flags.
+ *
+ * ******************** Solaris flags hacks **************************
+ *
+ * Solaris IFF_UP flag reflects only the primary interface as the
+ * routing socket only sends IFINFO for the primary interface.  Hence  
+ * ~IFF_UP does not per se imply all the logical interfaces are also   
+ * down - which we only know of as addresses. Instead we must determine
+ * whether the interface really is up or not according to how many   
+ * addresses are still attached. (Solaris always sends RTM_DELADDR if
+ * an interface, logical or not, goes ~IFF_UP).
+ *
+ * Ie, we mangle IFF_UP to *additionally* reflect whether or not there
+ * are addresses left in struct connected, not just the actual underlying
+ * IFF_UP flag.
+ *
+ * We must hence remember the real state of IFF_UP, which we do in
+ * struct zebra_if.primary_state.
+ *
+ * Setting IFF_UP within zebra to administratively shutdown the
+ * interface will affect only the primary interface/address on Solaris.
+ ************************End Solaris flags hacks ***********************
+ */
+static void
+if_flags_mangle (struct interface *ifp, uint64_t *newflags)
+{
+#ifdef SUNOS_5
+  struct zebra_if *zif = ifp->info;
+  
+  zif->primary_state = *newflags & (IFF_UP & 0xff);
+  
+  if (CHECK_FLAG (zif->primary_state, IFF_UP)
+      || listcount(ifp->connected) > 0)
+    SET_FLAG (*newflags, IFF_UP);
+  else
+    UNSET_FLAG (*newflags, IFF_UP);
+#endif /* SUNOS_5 */
+}
+
+/* Update the flags field of the ifp with the new flag set provided.
+ * Take whatever actions are required for any changes in flags we care
+ * about.
+ *
+ * newflags should be the raw value, as obtained from the OS.
+ */
+void
+if_flags_update (struct interface *ifp, uint64_t newflags)
+{
+  if_flags_mangle (ifp, &newflags);
+    
+  if (if_is_operative (ifp))
+    {
+      /* operative -> inoperative? */
+      ifp->flags = newflags;
+      if (!if_is_operative (ifp))
+        if_down (ifp);
+    }
+  else
+    {
+      /* inoperative -> operative? */
+      ifp->flags = newflags;
+      if (if_is_operative (ifp))
+        if_up (ifp);
+    }
+}
+
+/* Wake up configured address if it is not in current kernel
+   address. */
+static void
+if_addr_wakeup (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct connected *ifc;
+  struct prefix *p;
+  int ret;
+
+  for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc))
+    {
+      p = ifc->address;
+       
+      if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)
+         && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED))
+       {
+         /* Address check. */
+         if (p->family == AF_INET)
+           {
+             if (! if_is_up (ifp))
+               {
+                 /* Assume zebra is configured like following:
+                  *
+                  *   interface gre0
+                  *    ip addr 192.0.2.1/24
+                  *   !
+                  *
+                  * As soon as zebra becomes first aware that gre0 exists in the
+                  * kernel, it will set gre0 up and configure its addresses.
+                  *
+                  * (This may happen at startup when the interface already exists
+                  * or during runtime when the interface is added to the kernel)
+                  *
+                  * XXX: IRDP code is calling here via if_add_update - this seems
+                  * somewhat weird.
+                  * XXX: RUNNING is not a settable flag on any system
+                  * I (paulj) am aware of.
+                 */
+                 if_set_flags (ifp, IFF_UP | IFF_RUNNING);
+                 if_refresh (ifp);
+               }
+
+             ret = if_set_prefix (ifp, ifc);
+             if (ret < 0)
+               {
+                 zlog_warn ("Can't set interface's address: %s", 
+                            safe_strerror(errno));
+                 continue;
+               }
+
+             SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+             /* The address will be advertised to zebra clients when the notification
+              * from the kernel has been received.
+              * It will also be added to the interface's subnet list then. */
+           }
+#ifdef HAVE_IPV6
+         if (p->family == AF_INET6)
+           {
+             if (! if_is_up (ifp))
+               {
+                 /* See long comment above */
+                 if_set_flags (ifp, IFF_UP | IFF_RUNNING);
+                 if_refresh (ifp);
+               }
+
+             ret = if_prefix_add_ipv6 (ifp, ifc);
+             if (ret < 0)
+               {
+                 zlog_warn ("Can't set interface's address: %s", 
+                            safe_strerror(errno));
+                 continue;
+               }
+
+             SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+             /* The address will be advertised to zebra clients when the notification
+              * from the kernel has been received. */
+           }
+#endif /* HAVE_IPV6 */
+       }
+    }
+}
+
+static void if_count_up(struct zebra_if *zif)
+{
+  event_counter_inc(&zif->up_events);
+}
+
+static void if_count_down(struct zebra_if *zif)
+{
+  event_counter_inc(&zif->down_events);
+}
+
+void
+if_startup_count_up (void)
+{
+  vrf_iter_t iter;
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct listnode *node;
+
+  for (iter = vrf_first(); iter != VRF_ITER_INVALID; iter = vrf_next(iter))
+    {
+      for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist(iter), node, ifp))
+        {
+          zif = ifp->info;
+          if (!zif->up_events.count && if_is_operative(ifp))
+            if_count_up(zif);
+        }
+    }
+}
+
+/* Handle interface addition */
+void
+if_add_update (struct interface *ifp)
+{
+  struct zebra_if *if_data;
+
+  if_data = ifp->info;
+  assert(if_data);
+
+  if (if_data->multicast == IF_ZEBRA_MULTICAST_ON)
+    if_set_flags (ifp, IFF_MULTICAST);
+  else if (if_data->multicast == IF_ZEBRA_MULTICAST_OFF)
+    if_unset_flags (ifp, IFF_MULTICAST);
+
+  zebra_interface_add_update (ifp);
+
+  if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      SET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE);
+
+      if (if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)
+       {
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           zlog_debug ("interface %s vrf %u index %d is shutdown. "
+                       "Won't wake it up.",
+                       ifp->name, ifp->vrf_id, ifp->ifindex);
+         return;
+       }
+
+      if_addr_wakeup (ifp);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+       zlog_debug ("interface %s vrf %u index %d becomes active.",
+                   ifp->name, ifp->vrf_id, ifp->ifindex);
+    }
+  else
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+       zlog_debug ("interface %s vrf %u index %d is added.",
+                   ifp->name, ifp->vrf_id, ifp->ifindex);
+    }
+
+  if (host_config_get())
+    {
+      /* If configuration and therefore link-detect have already been
+       * loaded, count an initial up event when new interfaces are added
+       * in up state.
+       * If configuration has not been loaded yet, this is handled by
+       * if_startup_count_up which is called after reading the config. */
+      if (!if_data->up_events.count && if_is_operative(ifp))
+        if_count_up(if_data);
+    }
+}
+
+/* Handle an interface delete event */
+void 
+if_delete_update (struct interface *ifp)
+{
+  struct connected *ifc;
+  struct prefix *p;
+  struct route_node *rn;
+  struct zebra_if *zebra_if;
+
+  zebra_if = ifp->info;
+
+  if (if_is_up(ifp))
+    {
+      zlog_err ("interface %s vrf %u index %d is still up while being deleted.",
+                ifp->name, ifp->vrf_id, ifp->ifindex);
+      return;
+    }
+
+  /* Mark interface as inactive */
+  UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE);
+  
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("interface %s vrf %u index %d is now inactive.",
+                ifp->name, ifp->vrf_id, ifp->ifindex);
+
+  /* Delete connected routes from the kernel. */
+  if (ifp->connected)
+    {
+      struct listnode *node;
+      struct listnode *last = NULL;
+
+      while ((node = (last ? last->next : listhead (ifp->connected))))
+       {
+         ifc = listgetdata (node);
+         p = ifc->address;
+         
+         if (p->family == AF_INET
+             && (rn = route_node_lookup (zebra_if->ipv4_subnets, p)))
+           {
+             struct listnode *anode;
+             struct listnode *next;
+             struct listnode *first;
+             struct list *addr_list;
+             
+             route_unlock_node (rn);
+             addr_list = (struct list *) rn->info;
+             
+             /* Remove addresses, secondaries first. */
+             first = listhead (addr_list);
+             for (anode = first->next; anode || first; anode = next)
+               {
+                 if (!anode)
+                   {
+                     anode = first;
+                     first = NULL;
+                   }
+                 next = anode->next;
+
+                 ifc = listgetdata (anode);
+                 p = ifc->address;
+                 connected_down_ipv4 (ifp, ifc);
+
+                 /* XXX: We have to send notifications here explicitly, because we destroy
+                  * the ifc before receiving the notification about the address being deleted.
+                  */
+                 zebra_interface_address_delete_update (ifp, ifc);
+
+                 UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL);
+                 UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+
+                 /* Remove from subnet chain. */
+                 list_delete_node (addr_list, anode);
+                 route_unlock_node (rn);
+                 
+                 /* Remove from interface address list (unconditionally). */
+                 if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+                   {
+                     listnode_delete (ifp->connected, ifc);
+                     connected_free (ifc);
+                    }
+                  else
+                    last = node;
+               }
+
+             /* Free chain list and respective route node. */
+             list_delete (addr_list);
+             rn->info = NULL;
+             route_unlock_node (rn);
+           }
+#ifdef HAVE_IPV6
+         else if (p->family == AF_INET6)
+           {
+             connected_down_ipv6 (ifp, ifc);
+
+             zebra_interface_address_delete_update (ifp, ifc);
+
+             UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL);
+             UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+
+             if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+               last = node;
+             else
+               {
+                 listnode_delete (ifp->connected, ifc);
+                 connected_free (ifc);
+               }
+           }
+#endif /* HAVE_IPV6 */
+         else
+           {
+             last = node;
+           }
+       }
+    }
+  zebra_interface_delete_update (ifp);
+
+  /* Update ifindex after distributing the delete message.  This is in
+     case any client needs to have the old value of ifindex available
+     while processing the deletion.  Each client daemon is responsible
+     for setting ifindex to IFINDEX_INTERNAL after processing the
+     interface deletion message. */
+  ifp->ifindex = IFINDEX_INTERNAL;
+}
+
+/* Interface is up. */
+void
+if_up (struct interface *ifp)
+{
+  struct listnode *node;
+  struct listnode *next;
+  struct connected *ifc;
+  struct prefix *p;
+
+  if_count_up(ifp->info);
+
+  /* Notify the protocol daemons. */
+  zebra_interface_up_update (ifp);
+
+  /* Install connected routes to the kernel. */
+  if (ifp->connected)
+    {
+      for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc))
+       {
+         p = ifc->address;
+
+         if (p->family == AF_INET)
+           connected_up_ipv4 (ifp, ifc);
+#ifdef HAVE_IPV6
+         else if (p->family == AF_INET6)
+           connected_up_ipv6 (ifp, ifc);
+#endif /* HAVE_IPV6 */
+       }
+    }
+
+  /* Examine all static routes. */
+  rib_update (ifp->vrf_id);
+}
+
+/* Interface goes down.  We have to manage different behavior of based
+   OS. */
+void
+if_down (struct interface *ifp)
+{
+  struct listnode *node;
+  struct listnode *next;
+  struct connected *ifc;
+  struct prefix *p;
+  struct zebra_if *zif;
+
+  zif = ifp->info;
+  if (zif->up_events.count)
+    if_count_down(zif);
+
+  /* Notify to the protocol daemons. */
+  zebra_interface_down_update (ifp);
+
+  /* Delete connected routes from the kernel. */
+  if (ifp->connected)
+    {
+      for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc))
+       {
+         p = ifc->address;
+
+         if (p->family == AF_INET)
+           connected_down_ipv4 (ifp, ifc);
+#ifdef HAVE_IPV6
+         else if (p->family == AF_INET6)
+           connected_down_ipv6 (ifp, ifc);
+#endif /* HAVE_IPV6 */
+       }
+    }
+
+  /* Examine all static routes which direct to the interface. */
+  rib_update (ifp->vrf_id);
+}
+
+void
+if_refresh (struct interface *ifp)
+{
+  if_get_flags (ifp);
+}
+
+/* Output prefix string to vty. */
+static int
+prefix_vty_out (struct vty *vty, struct prefix *p)
+{
+  char str[INET6_ADDRSTRLEN];
+
+  inet_ntop (p->family, &p->u.prefix, str, sizeof (str));
+  vty_out (vty, "%s", str);
+  return strlen (str);
+}
+
+/* Dump if address information to vty. */
+static void
+connected_dump_vty (struct vty *vty, struct connected *connected)
+{
+  struct prefix *p;
+
+  /* Print interface address. */
+  p = connected->address;
+  vty_out (vty, "  %s ", prefix_family_str (p));
+  prefix_vty_out (vty, p);
+  vty_out (vty, "/%d", p->prefixlen);
+
+  /* If there is destination address, print it. */
+  if (connected->destination)
+    {
+      vty_out (vty, (CONNECTED_PEER(connected) ? " peer " : " broadcast "));
+      prefix_vty_out (vty, connected->destination);
+    }
+
+  if (CHECK_FLAG (connected->flags, ZEBRA_IFA_SECONDARY))
+    vty_out (vty, " secondary");
+
+  if (CHECK_FLAG (connected->flags, ZEBRA_IFA_UNNUMBERED))
+    vty_out (vty, " unnumbered");
+
+  if (connected->label)
+    vty_out (vty, " %s", connected->label);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+#if defined (HAVE_RTADV)
+/* Dump interface ND information to vty. */
+static void
+nd_dump_vty (struct vty *vty, struct interface *ifp)
+{
+  struct zebra_if *zif;
+  struct rtadvconf *rtadv;
+  int interval;
+
+  zif = (struct zebra_if *) ifp->info;
+  rtadv = &zif->rtadv;
+
+  if (rtadv->AdvSendAdvertisements)
+    {
+      vty_out (vty, "  ND advertised reachable time is %d milliseconds%s",
+              rtadv->AdvReachableTime, VTY_NEWLINE);
+      vty_out (vty, "  ND advertised retransmit interval is %d milliseconds%s",
+              rtadv->AdvRetransTimer, VTY_NEWLINE);
+      interval = rtadv->MaxRtrAdvInterval;
+      if (interval % 1000)
+        vty_out (vty, "  ND router advertisements are sent every "
+                       "%d milliseconds%s", interval,
+                VTY_NEWLINE);
+      else
+        vty_out (vty, "  ND router advertisements are sent every "
+                       "%d seconds%s", interval / 1000,
+                VTY_NEWLINE);
+      if (rtadv->AdvDefaultLifetime != -1)
+       vty_out (vty, "  ND router advertisements live for %d seconds%s",
+                rtadv->AdvDefaultLifetime, VTY_NEWLINE);
+      else
+       vty_out (vty, "  ND router advertisements lifetime tracks ra-interval%s",
+                VTY_NEWLINE);
+      vty_out (vty, "  ND router advertisement default router preference is "
+                       "%s%s", rtadv_pref_strs[rtadv->DefaultPreference],
+                VTY_NEWLINE);
+      if (rtadv->AdvManagedFlag)
+       vty_out (vty, "  Hosts use DHCP to obtain routable addresses.%s",
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "  Hosts use stateless autoconfig for addresses.%s",
+                VTY_NEWLINE);
+      if (rtadv->AdvHomeAgentFlag)
+      {
+       vty_out (vty, "  ND router advertisements with "
+                               "Home Agent flag bit set.%s",
+                VTY_NEWLINE);
+       if (rtadv->HomeAgentLifetime != -1)
+         vty_out (vty, "  Home Agent lifetime is %u seconds%s",
+                  rtadv->HomeAgentLifetime, VTY_NEWLINE);
+       else
+         vty_out (vty, "  Home Agent lifetime tracks ra-lifetime%s",
+                  VTY_NEWLINE);
+       vty_out (vty, "  Home Agent preference is %u%s",
+                rtadv->HomeAgentPreference, VTY_NEWLINE);
+      }
+      if (rtadv->AdvIntervalOption)
+       vty_out (vty, "  ND router advertisements with Adv. Interval option.%s",
+                VTY_NEWLINE);
+    }
+}
+#endif /* HAVE_RTADV */
+
+/* Interface's information print out to vty interface. */
+static void
+if_dump_vty (struct vty *vty, struct interface *ifp)
+{
+  struct connected *connected;
+  struct listnode *node;
+  struct route_node *rn;
+  struct zebra_if *zebra_if;
+
+  zebra_if = ifp->info;
+
+  vty_out (vty, "Interface %s is ", ifp->name);
+  if (if_is_up(ifp)) {
+    vty_out (vty, "up, line protocol ");
+    
+    if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) {
+      if (if_is_running(ifp))
+       vty_out (vty, "is up%s", VTY_NEWLINE);
+      else
+       vty_out (vty, "is down%s", VTY_NEWLINE);
+    } else {
+      vty_out (vty, "detection is disabled%s", VTY_NEWLINE);
+    }
+  } else {
+    vty_out (vty, "down%s", VTY_NEWLINE);
+  }
+
+  vty_out (vty, "  Link ups:   %s%s",
+           event_counter_format(&zebra_if->up_events), VTY_NEWLINE);
+  vty_out (vty, "  Link downs: %s%s",
+           event_counter_format(&zebra_if->down_events), VTY_NEWLINE);
+
+  vty_out (vty, "  vrf: %u%s", ifp->vrf_id, VTY_NEWLINE);
+
+  if (ifp->desc)
+    vty_out (vty, "  Description: %s%s", ifp->desc,
+            VTY_NEWLINE);
+  if (ifp->ifindex == IFINDEX_INTERNAL)
+    {
+      vty_out(vty, "  pseudo interface%s", VTY_NEWLINE);
+      return;
+    }
+  else if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      vty_out(vty, "  index %d inactive interface%s", 
+             ifp->ifindex, 
+             VTY_NEWLINE);
+      return;
+    }
+
+  vty_out (vty, "  index %d metric %d mtu %d ",
+          ifp->ifindex, ifp->metric, ifp->mtu);
+#ifdef HAVE_IPV6
+  if (ifp->mtu6 != ifp->mtu)
+    vty_out (vty, "mtu6 %d ", ifp->mtu6);
+#endif 
+  vty_out (vty, "%s  flags: %s%s", VTY_NEWLINE,
+           if_flag_dump (ifp->flags), VTY_NEWLINE);
+  
+  /* Hardware address. */
+  vty_out (vty, "  Type: %s%s", if_link_type_str (ifp->ll_type), VTY_NEWLINE);
+  if (ifp->hw_addr_len != 0)
+    {
+      int i;
+
+      vty_out (vty, "  HWaddr: ");
+      for (i = 0; i < ifp->hw_addr_len; i++)
+       vty_out (vty, "%s%02x", i == 0 ? "" : ":", ifp->hw_addr[i]);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  
+  /* Bandwidth in kbps */
+  if (ifp->bandwidth != 0)
+    {
+      vty_out(vty, "  bandwidth %u kbps", ifp->bandwidth);
+      vty_out(vty, "%s", VTY_NEWLINE);
+    }
+
+  for (rn = route_top (zebra_if->ipv4_subnets); rn; rn = route_next (rn))
+    {
+      if (! rn->info)
+       continue;
+
+      for (ALL_LIST_ELEMENTS_RO ((struct list *)rn->info, node, connected))
+        connected_dump_vty (vty, connected);
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+    {
+      if (CHECK_FLAG (connected->conf, ZEBRA_IFC_REAL) &&
+         (connected->address->family == AF_INET6))
+       connected_dump_vty (vty, connected);
+    }
+
+  if (HAS_LINK_PARAMS(ifp))
+    {
+      int i;
+      struct if_link_params *iflp = ifp->link_params;
+      vty_out(vty, "  Traffic Engineering Link Parameters:%s", VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_TE))
+        vty_out(vty, "    TE metric %u%s",iflp->te_metric, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_MAX_BW))
+        vty_out(vty, "    Maximum Bandwidth %g (Byte/s)%s", iflp->max_bw, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW))
+        vty_out(vty, "    Maximum Reservable Bandwidth %g (Byte/s)%s", iflp->max_rsv_bw, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) {
+        vty_out(vty, "    Unreserved Bandwidth per Class Type in Byte/s:%s", VTY_NEWLINE);
+        for (i = 0; i < MAX_CLASS_TYPE; i+=2)
+          vty_out(vty, "      [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s",
+                  i, iflp->unrsv_bw[i], i+1, iflp->unrsv_bw[i+1], VTY_NEWLINE);
+      }
+
+      if (IS_PARAM_SET(iflp, LP_ADM_GRP))
+        vty_out(vty, "    Administrative Group:%u%s", iflp->admin_grp, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_DELAY))
+        {
+          vty_out(vty, "    Link Delay Average: %u (micro-sec.)", iflp->av_delay);
+          if (IS_PARAM_SET(iflp, LP_MM_DELAY))
+            {
+              vty_out(vty, " Min:  %u (micro-sec.)", iflp->min_delay);
+              vty_out(vty, " Max:  %u (micro-sec.)", iflp->max_delay);
+            }
+          vty_out(vty, "%s", VTY_NEWLINE);
+        }
+      if (IS_PARAM_SET(iflp, LP_DELAY_VAR))
+        vty_out(vty, "    Link Delay Variation %u (micro-sec.)%s", iflp->delay_var, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_PKT_LOSS))
+        vty_out(vty, "    Link Packet Loss %g (in %%)%s", iflp->pkt_loss, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_AVA_BW))
+        vty_out(vty, "    Available Bandwidth %g (Byte/s)%s", iflp->ava_bw, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_RES_BW))
+        vty_out(vty, "    Residual Bandwidth %g (Byte/s)%s", iflp->res_bw, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_USE_BW))
+        vty_out(vty, "    Utilized Bandwidth %g (Byte/s)%s", iflp->use_bw, VTY_NEWLINE);
+      if (IS_PARAM_SET(iflp, LP_RMT_AS))
+        vty_out(vty, "    Neighbor ASBR IP: %s AS: %u %s", inet_ntoa(iflp->rmt_ip), iflp->rmt_as, VTY_NEWLINE);
+    }
+
+ #ifdef RTADV
+   nd_dump_vty (vty, ifp);
+ #endif /* RTADV */
+#if defined (HAVE_RTADV)
+  nd_dump_vty (vty, ifp);
+#endif /* HAVE_RTADV */
+
+#ifdef HAVE_PROC_NET_DEV
+  /* Statistics print out using proc file system. */
+  vty_out (vty, "    %lu input packets (%lu multicast), %lu bytes, "
+          "%lu dropped%s",
+          ifp->stats.rx_packets, ifp->stats.rx_multicast,
+          ifp->stats.rx_bytes, ifp->stats.rx_dropped, VTY_NEWLINE);
+
+  vty_out (vty, "    %lu input errors, %lu length, %lu overrun,"
+          " %lu CRC, %lu frame%s",
+          ifp->stats.rx_errors, ifp->stats.rx_length_errors,
+          ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors,
+          ifp->stats.rx_frame_errors, VTY_NEWLINE);
+
+  vty_out (vty, "    %lu fifo, %lu missed%s", ifp->stats.rx_fifo_errors,
+          ifp->stats.rx_missed_errors, VTY_NEWLINE);
+
+  vty_out (vty, "    %lu output packets, %lu bytes, %lu dropped%s",
+          ifp->stats.tx_packets, ifp->stats.tx_bytes,
+          ifp->stats.tx_dropped, VTY_NEWLINE);
+
+  vty_out (vty, "    %lu output errors, %lu aborted, %lu carrier,"
+          " %lu fifo, %lu heartbeat%s",
+          ifp->stats.tx_errors, ifp->stats.tx_aborted_errors,
+          ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors,
+          ifp->stats.tx_heartbeat_errors, VTY_NEWLINE);
+
+  vty_out (vty, "    %lu window, %lu collisions%s",
+          ifp->stats.tx_window_errors, ifp->stats.collisions, VTY_NEWLINE);
+#endif /* HAVE_PROC_NET_DEV */
+
+#ifdef HAVE_NET_RT_IFLIST
+#if defined (__bsdi__) || defined (__NetBSD__)
+  /* Statistics print out using sysctl (). */
+  vty_out (vty, "    input packets %llu, bytes %llu, dropped %llu,"
+           " multicast packets %llu%s",
+           (unsigned long long)ifp->stats.ifi_ipackets,
+           (unsigned long long)ifp->stats.ifi_ibytes,
+           (unsigned long long)ifp->stats.ifi_iqdrops,
+           (unsigned long long)ifp->stats.ifi_imcasts,
+           VTY_NEWLINE);
+
+  vty_out (vty, "    input errors %llu%s",
+           (unsigned long long)ifp->stats.ifi_ierrors, VTY_NEWLINE);
+
+  vty_out (vty, "    output packets %llu, bytes %llu,"
+           " multicast packets %llu%s",
+           (unsigned long long)ifp->stats.ifi_opackets,
+           (unsigned long long)ifp->stats.ifi_obytes,
+           (unsigned long long)ifp->stats.ifi_omcasts,
+           VTY_NEWLINE);
+
+  vty_out (vty, "    output errors %llu%s",
+           (unsigned long long)ifp->stats.ifi_oerrors, VTY_NEWLINE);
+
+  vty_out (vty, "    collisions %llu%s",
+           (unsigned long long)ifp->stats.ifi_collisions, VTY_NEWLINE);
+#else
+  /* Statistics print out using sysctl (). */
+  vty_out (vty, "    input packets %lu, bytes %lu, dropped %lu,"
+          " multicast packets %lu%s",
+          ifp->stats.ifi_ipackets, ifp->stats.ifi_ibytes,
+          ifp->stats.ifi_iqdrops, ifp->stats.ifi_imcasts,
+          VTY_NEWLINE);
+
+  vty_out (vty, "    input errors %lu%s",
+          ifp->stats.ifi_ierrors, VTY_NEWLINE);
+
+  vty_out (vty, "    output packets %lu, bytes %lu, multicast packets %lu%s",
+          ifp->stats.ifi_opackets, ifp->stats.ifi_obytes,
+          ifp->stats.ifi_omcasts, VTY_NEWLINE);
+
+  vty_out (vty, "    output errors %lu%s",
+          ifp->stats.ifi_oerrors, VTY_NEWLINE);
+
+  vty_out (vty, "    collisions %lu%s",
+          ifp->stats.ifi_collisions, VTY_NEWLINE);
+#endif /* __bsdi__ || __NetBSD__ */
+#endif /* HAVE_NET_RT_IFLIST */
+}
+
+/* Wrapper hook point for zebra daemon so that ifindex can be set 
+ * DEFUN macro not used as extract.pl HAS to ignore this
+ * See also interface_cmd in lib/if.c
+ */ 
+DEFUN_NOSH (zebra_interface,
+           zebra_interface_cmd,
+           "interface IFNAME",
+           "Select an interface to configure\n"
+           "Interface's name\n")
+{
+  int ret;
+  struct interface *ifp;
+  
+  /* Call lib interface() */
+  if ((ret = interface_cmd.func (self, vty, argc, argv)) != CMD_SUCCESS)
+    return ret;
+
+  ifp = vty->index;
+
+  if (ifp->ifindex == IFINDEX_INTERNAL)
+    /* Is this really necessary?  Shouldn't status be initialized to 0
+       in that case? */
+    UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE);
+
+  return ret;
+}
+
+ALIAS (zebra_interface,
+       zebra_interface_vrf_cmd,
+       "interface IFNAME " VRF_CMD_STR,
+       "Select an interface to configure\n"
+       "Interface's name\n"
+       VRF_CMD_HELP_STR)
+
+struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+  1
+};
+
+/* Show all interfaces to vty. */
+DEFUN (show_interface, show_interface_cmd,
+       "show interface",
+       SHOW_STR
+       "Interface status and configuration\n")
+{
+  struct listnode *node;
+  struct interface *ifp;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+#ifdef HAVE_PROC_NET_DEV
+  /* If system has interface statistics via proc file system, update
+     statistics. */
+  ifstat_update_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+  ifstat_update_sysctl ();
+#endif /* HAVE_NET_RT_IFLIST */
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  /* All interface print. */
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    if_dump_vty (vty, ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_interface,
+       show_interface_vrf_cmd,
+       "show interface " VRF_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       VRF_CMD_HELP_STR)
+
+/* Show all interfaces to vty. */
+DEFUN (show_interface_vrf_all, show_interface_vrf_all_cmd,
+       "show interface " VRF_ALL_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  vrf_iter_t iter;
+
+#ifdef HAVE_PROC_NET_DEV
+  /* If system has interface statistics via proc file system, update
+     statistics. */
+  ifstat_update_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+  ifstat_update_sysctl ();
+#endif /* HAVE_NET_RT_IFLIST */
+
+  /* All interface print. */
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp))
+      if_dump_vty (vty, ifp);
+
+  return CMD_SUCCESS;
+}
+
+/* Show specified interface to vty. */
+DEFUN (show_interface_name, show_interface_name_cmd,
+       "show interface IFNAME",
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Inteface name\n")
+{
+  struct interface *ifp;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+#ifdef HAVE_PROC_NET_DEV
+  /* If system has interface statistics via proc file system, update
+     statistics. */
+  ifstat_update_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+  ifstat_update_sysctl ();
+#endif /* HAVE_NET_RT_IFLIST */
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  /* Specified interface print. */
+  ifp = if_lookup_by_name_vrf (argv[0], vrf_id);
+  if (ifp == NULL)
+    {
+      vty_out (vty, "%% Can't find interface %s%s", argv[0],
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if_dump_vty (vty, ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_interface_name,
+       show_interface_name_vrf_cmd,
+       "show interface IFNAME " VRF_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Inteface name\n"
+       VRF_CMD_HELP_STR)
+
+/* Show specified interface to vty. */
+DEFUN (show_interface_name_vrf_all, show_interface_name_vrf_all_cmd,
+       "show interface IFNAME " VRF_ALL_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Inteface name\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct interface *ifp;
+  vrf_iter_t iter;
+  int found = 0;
+
+#ifdef HAVE_PROC_NET_DEV
+  /* If system has interface statistics via proc file system, update
+     statistics. */
+  ifstat_update_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+  ifstat_update_sysctl ();
+#endif /* HAVE_NET_RT_IFLIST */
+
+  /* All interface print. */
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      /* Specified interface print. */
+      ifp = if_lookup_by_name_vrf (argv[0], vrf_iter2id (iter));
+      if (ifp)
+        {
+          if_dump_vty (vty, ifp);
+          found++;
+        }
+    }
+
+  if (!found)
+    {
+      vty_out (vty, "%% Can't find interface %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+if_show_description (struct vty *vty, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  vty_out (vty, "Interface       Status  Protocol  Description%s", VTY_NEWLINE);
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp))
+    {
+      int len;
+
+      len = vty_out (vty, "%s", ifp->name);
+      vty_out (vty, "%*s", (16 - len), " ");
+      
+      if (if_is_up(ifp))
+       {
+         vty_out (vty, "up      ");
+         if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
+           {
+             if (if_is_running(ifp))
+               vty_out (vty, "up        ");
+             else
+               vty_out (vty, "down      ");
+           }
+         else
+           {
+             vty_out (vty, "unknown   ");
+           }
+       }
+      else
+       {
+         vty_out (vty, "down    down      ");
+       }
+
+      if (ifp->desc)
+       vty_out (vty, "%s", ifp->desc);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_interface_desc,
+       show_interface_desc_cmd,
+       "show interface description",
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Interface description\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  if_show_description (vty, vrf_id);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_interface_desc,
+       show_interface_desc_vrf_cmd,
+       "show interface description " VRF_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Interface description\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_interface_desc_vrf_all,
+       show_interface_desc_vrf_all_cmd,
+       "show interface description " VRF_ALL_CMD_STR,
+       SHOW_STR
+       "Interface status and configuration\n"
+       "Interface description\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if (!list_isempty (vrf_iter2iflist (iter)))
+      {
+        vty_out (vty, "%s\tVRF %u%s%s", VTY_NEWLINE,
+                 vrf_iter2id (iter),
+                 VTY_NEWLINE, VTY_NEWLINE);
+        if_show_description (vty, vrf_iter2id (iter));
+      }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (multicast,
+       multicast_cmd,
+       "multicast",
+       "Set multicast flag to interface\n")
+{
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *if_data;
+
+  ifp = (struct interface *) vty->index;
+  if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      ret = if_set_flags (ifp, IFF_MULTICAST);
+      if (ret < 0)
+       {
+         vty_out (vty, "Can't set multicast flag%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      if_refresh (ifp);
+    }
+  if_data = ifp->info;
+  if_data->multicast = IF_ZEBRA_MULTICAST_ON;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_multicast,
+       no_multicast_cmd,
+       "no multicast",
+       NO_STR
+       "Unset multicast flag to interface\n")
+{
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *if_data;
+
+  ifp = (struct interface *) vty->index;
+  if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      ret = if_unset_flags (ifp, IFF_MULTICAST);
+      if (ret < 0)
+       {
+         vty_out (vty, "Can't unset multicast flag%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      if_refresh (ifp);
+    }
+  if_data = ifp->info;
+  if_data->multicast = IF_ZEBRA_MULTICAST_OFF;
+
+  return CMD_SUCCESS;
+}
+
+/* Hacky: create a dummy node just to hang a config-writer callback off it */
+static struct cmd_node zebra_if_defaults_node = {
+  ZEBRA_IF_DEFAULTS_NODE,
+  "",
+  1,
+};
+
+static int
+config_write_zebra_if_defaults (struct vty *vty)
+{
+  if (zif_defaults.linkdetect != IF_LINKDETECT_UNSPEC)
+    vty_out (vty, "default link-detect %s%s",
+             zif_defaults.linkdetect == IF_LINKDETECT_ON ? "on" : "off",
+             VTY_NEWLINE);
+  return 0;
+}
+
+DEFUN(default_linkdetect,
+      default_linkdetect_cmd,
+      "default link-detect (on|off)",
+      "Configure defaults of settings\n"
+      "Interface link detection\n"
+      "Interface link-detect defaults to enabled\n"
+      "Interface link-detect defaults to disabled\n")
+{
+  zebra_if_linkdetect prev = zif_defaults.linkdetect;
+  struct listnode *node;
+  struct interface *ifp;
+  vrf_iter_t iter;
+  
+  if (strcmp (argv[1], "on") == 0)
+    zif_defaults.linkdetect = IF_LINKDETECT_ON;
+  else
+    zif_defaults.linkdetect = IF_LINKDETECT_OFF;
+    
+  if (zif_defaults.linkdetect != prev)
+    for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+      for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp))
+        if_zebra_linkdetect_set (ifp);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (linkdetect,
+       linkdetect_cmd,
+       "link-detect [default]",
+       "Enable link detection on interface\n"
+       "Leave link-detect to the default\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+  assert (zif != NULL);
+  
+  zif->linkdetect = IF_LINKDETECT_ON;
+  if_zebra_linkdetect_set (ifp);
+  
+  /* FIXME: Will defer status change forwarding if interface
+     does not come down! */
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (no_linkdetect,
+       no_linkdetect_cmd,
+       "no link-detect",
+       NO_STR
+       "Disable link detection on interface\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+  assert (zif != NULL);
+  
+  zif->linkdetect = IF_LINKDETECT_OFF;
+  if_zebra_linkdetect_set (ifp);
+  
+  /* FIXME: see linkdetect_cmd */
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (shutdown_if,
+       shutdown_if_cmd,
+       "shutdown",
+       "Shutdown the selected interface\n")
+{
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *if_data;
+
+  ifp = (struct interface *) vty->index;
+  if (ifp->ifindex != IFINDEX_INTERNAL)
+    {
+        ret = if_unset_flags (ifp, IFF_UP);
+        if (ret < 0)
+          {
+            vty_out (vty, "Can't shutdown interface%s", VTY_NEWLINE);
+            return CMD_WARNING;
+          }
+        if_refresh (ifp);
+    }
+  if_data = ifp->info;
+  if_data->shutdown = IF_ZEBRA_SHUTDOWN_ON;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_shutdown_if,
+       no_shutdown_if_cmd,
+       "no shutdown",
+       NO_STR
+       "Shutdown the selected interface\n")
+{
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *if_data;
+
+  ifp = (struct interface *) vty->index;
+
+  if (ifp->ifindex != IFINDEX_INTERNAL)
+    {
+      ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING);
+      if (ret < 0)
+       {
+         vty_out (vty, "Can't up interface%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      if_refresh (ifp);
+
+      /* Some addresses (in particular, IPv6 addresses on Linux) get
+       * removed when the interface goes down. They need to be readded.
+       */
+      if_addr_wakeup(ifp);
+    }
+
+  if_data = ifp->info;
+  if_data->shutdown = IF_ZEBRA_SHUTDOWN_OFF;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (bandwidth_if,
+       bandwidth_if_cmd,
+       "bandwidth <1-10000000>",
+       "Set bandwidth informational parameter\n"
+       "Bandwidth in kilobits\n")
+{
+  struct interface *ifp;   
+  unsigned int bandwidth;
+  
+  ifp = (struct interface *) vty->index;
+  bandwidth = strtol(argv[0], NULL, 10);
+
+  /* bandwidth range is <1-10000000> */
+  if (bandwidth < 1 || bandwidth > 10000000)
+    {
+      vty_out (vty, "Bandwidth is invalid%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  ifp->bandwidth = bandwidth;
+
+  /* force protocols to recalculate routes due to cost change */
+  if (if_is_operative (ifp))
+    zebra_interface_up_update (ifp);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bandwidth_if,
+       no_bandwidth_if_cmd,
+       "no bandwidth",
+       NO_STR
+       "Set bandwidth informational parameter\n")
+{
+  struct interface *ifp;   
+  
+  ifp = (struct interface *) vty->index;
+
+  ifp->bandwidth = 0;
+  
+  /* force protocols to recalculate routes due to cost change */
+  if (if_is_operative (ifp))
+    zebra_interface_up_update (ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_bandwidth_if,
+       no_bandwidth_if_val_cmd,
+       "no bandwidth <1-10000000>",
+       NO_STR
+       "Set bandwidth informational parameter\n"
+       "Bandwidth in kilobits\n")
+
+struct cmd_node link_params_node =
+{
+  LINK_PARAMS_NODE,
+  "%s(config-link-params)# ",
+  1,
+};
+
+static void
+link_param_cmd_set_uint32 (struct interface *ifp, uint32_t *field, 
+                           uint32_t type, uint32_t value)
+{
+  /* Update field as needed */
+  if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value)
+    {
+      *field = value;
+      SET_PARAM(ifp->link_params, type);
+
+      /* force protocols to update LINK STATE due to parameters change */
+      if (if_is_operative (ifp))
+        zebra_interface_parameters_update (ifp);
+    }
+}
+static void
+link_param_cmd_set_float (struct interface *ifp, float *field,
+                          uint32_t type, float value)
+{
+
+  /* Update field as needed */
+  if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value)
+    {
+      *field = value;
+      SET_PARAM(ifp->link_params, type);
+
+      /* force protocols to update LINK STATE due to parameters change */
+      if (if_is_operative (ifp))
+        zebra_interface_parameters_update (ifp);
+    }
+}
+
+static void
+link_param_cmd_unset (struct interface *ifp, uint32_t type)
+{
+
+  /* Unset field */
+  UNSET_PARAM(ifp->link_params, type);
+
+  /* force protocols to update LINK STATE due to parameters change */
+  if (if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+}
+
+DEFUN (link_params,
+       link_params_cmd,
+       "link-params",
+       LINK_PARAMS_STR)
+{
+  vty->node = LINK_PARAMS_NODE;
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (exit_link_params,
+       exit_link_params_cmd,
+       "exit-link-params",
+       "Exit from Link Params configuration mode\n")
+{
+  if (vty->node == LINK_PARAMS_NODE)
+    vty->node = INTERFACE_NODE;
+  return CMD_SUCCESS;
+}
+
+/* Specific Traffic Engineering parameters commands */
+DEFUN (link_params_enable,
+       link_params_enable_cmd,
+       "enable",
+       "Activate link parameters on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+    
+  /* This command could be issue at startup, when activate MPLS TE */
+  /* on a new interface or after a ON / OFF / ON toggle */
+  /* In all case, TE parameters are reset to their default factory */
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("Link-params: enable TE link parameters on interface %s", ifp->name);
+  
+  if (!if_link_params_get (ifp)) 
+    {
+      if (IS_ZEBRA_DEBUG_EVENT)
+        zlog_debug ("Link-params: failed to init TE link parameters  %s", ifp->name);
+      
+      return CMD_WARNING;
+    }
+  
+  /* force protocols to update LINK STATE due to parameters change */
+  if (if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_enable,
+       no_link_params_enable_cmd,
+       "no enable",
+       NO_STR
+       "Disable link parameters on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+
+  zlog_debug ("MPLS-TE: disable TE link parameters on interface %s", ifp->name);
+  
+  if_link_params_free (ifp);
+  
+  /* force protocols to update LINK STATE due to parameters change */
+  if (if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+
+  return CMD_SUCCESS;
+}
+
+/* STANDARD TE metrics */
+DEFUN (link_params_metric,
+       link_params_metric_cmd,
+       "metric <0-4294967295>",
+       "Link metric for MPLS-TE purpose\n"
+       "Metric value in decimal\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  u_int32_t metric;
+
+  VTY_GET_ULONG("metric", metric, argv[0]);
+
+  /* Update TE metric if needed */
+  link_param_cmd_set_uint32 (ifp, &iflp->te_metric, LP_TE, metric);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_metric,
+       no_link_params_metric_cmd,
+       "no metric",
+       NO_STR
+       "Disbale Link Metric on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset TE Metric */
+  link_param_cmd_unset(ifp, LP_TE);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_maxbw,
+       link_params_maxbw_cmd,
+       "max-bw BANDWIDTH",
+       "Maximum bandwidth that can be used\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  
+  float bw;
+
+  if (sscanf (argv[0], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_maxbw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that Maximum bandwidth is not lower than other bandwidth parameters */
+  if ((bw <= iflp->max_rsv_bw)
+      || (bw <= iflp->unrsv_bw[0])
+      || (bw <= iflp->unrsv_bw[1])
+      || (bw <= iflp->unrsv_bw[2])
+      || (bw <= iflp->unrsv_bw[3])
+      || (bw <= iflp->unrsv_bw[4])
+      || (bw <= iflp->unrsv_bw[5])
+      || (bw <= iflp->unrsv_bw[6])
+      || (bw <= iflp->unrsv_bw[7])
+      || (bw <= iflp->ava_bw)
+      || (bw <= iflp->res_bw)
+      || (bw <= iflp->use_bw))
+    {
+      vty_out (vty,
+               "Maximum Bandwidth could not be lower than others bandwidth%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Maximum Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->max_bw, LP_MAX_BW, bw);
+    
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_max_rsv_bw,
+       link_params_max_rsv_bw_cmd,
+       "max-rsv-bw BANDWIDTH",
+       "Maximum bandwidth that may be reserved\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  float bw;
+
+  if (sscanf (argv[0], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_max_rsv_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that bandwidth is not greater than maximum bandwidth parameter */
+  if (bw > iflp->max_bw)
+    {
+      vty_out (vty,
+               "Maximum Reservable Bandwidth could not be greater than Maximum Bandwidth (%g)%s",
+               iflp->max_bw, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Maximum Reservable Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->max_rsv_bw, LP_MAX_RSV_BW, bw);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_unrsv_bw,
+       link_params_unrsv_bw_cmd,
+       "unrsv-bw <0-7> BANDWIDTH",
+       "Unreserved bandwidth at each priority level\n"
+       "Priority\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  int priority;
+  float bw;
+
+  /* We don't have to consider about range check here. */
+  if (sscanf (argv[0], "%d", &priority) != 1)
+    {
+      vty_out (vty, "link_params_unrsv_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (sscanf (argv[1], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_unrsv_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that bandwidth is not greater than maximum bandwidth parameter */
+  if (bw > iflp->max_bw)
+    {
+      vty_out (vty,
+               "UnReserved Bandwidth could not be greater than Maximum Bandwidth (%g)%s",
+               iflp->max_bw, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Unreserved Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW, bw);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_admin_grp,
+       link_params_admin_grp_cmd,
+       "admin-grp BITPATTERN",
+       "Administrative group membership\n"
+       "32-bit Hexadecimal value (e.g. 0xa1)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  unsigned long value;
+
+  if (sscanf (argv[0], "0x%lx", &value) != 1)
+    {
+      vty_out (vty, "link_params_admin_grp: fscanf: %s%s",
+               safe_strerror (errno), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Administrative Group if needed */
+  link_param_cmd_set_uint32 (ifp, &iflp->admin_grp, LP_ADM_GRP, value);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_admin_grp,
+       no_link_params_admin_grp_cmd,
+       "no admin-grp",
+       NO_STR
+       "Disbale Administrative group membership on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Admin Group */
+  link_param_cmd_unset(ifp, LP_ADM_GRP);
+  
+  return CMD_SUCCESS;
+}
+
+/* RFC5392 & RFC5316: INTER-AS */
+DEFUN (link_params_inter_as,
+       link_params_inter_as_cmd,
+       "neighbor A.B.C.D as <1-4294967295>",
+       "Configure remote ASBR information (Neighbor IP address and AS number)\n"
+       "Remote IP address in dot decimal A.B.C.D\n"
+       "Remote AS number\n"
+       "AS number in the range <1-4294967295>\n")
+{
+
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  struct in_addr addr;
+  u_int32_t as;
+
+  if (!inet_aton (argv[0], &addr))
+    {
+      vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+    
+  VTY_GET_ULONG("AS number", as, argv[1]);
+  
+  /* Update Remote IP and Remote AS fields if needed */
+  if (IS_PARAM_UNSET(iflp, LP_RMT_AS)
+      || iflp->rmt_as != as
+      || iflp->rmt_ip.s_addr != addr.s_addr)
+    {
+
+      iflp->rmt_as = as;
+      iflp->rmt_ip.s_addr = addr.s_addr;
+      SET_PARAM(iflp, LP_RMT_AS);
+
+      /* force protocols to update LINK STATE due to parameters change */
+      if (if_is_operative (ifp))
+        zebra_interface_parameters_update (ifp);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_inter_as,
+       no_link_params_inter_as_cmd,
+       "no neighbor",
+       NO_STR
+       "Remove Neighbor IP address and AS number for Inter-AS TE\n")
+{
+
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+
+  /* Reset Remote IP and AS neighbor */
+  iflp->rmt_as = 0;
+  iflp->rmt_ip.s_addr = 0;
+  UNSET_PARAM(iflp, LP_RMT_AS);
+
+  /* force protocols to update LINK STATE due to parameters change */
+  if (if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+
+  return CMD_SUCCESS;
+}
+
+/* RFC7471: OSPF Traffic Engineering (TE) Metric extensions & draft-ietf-isis-metric-extensions-07.txt */
+DEFUN (link_params_delay,
+       link_params_delay_cmd,
+       "delay <0-16777215>",
+       "Unidirectional Average Link Delay\n"
+       "Average delay in micro-second as decimal (0...16777215)\n")
+{
+
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  u_int32_t delay = 0, low = 0, high = 0;
+  u_int8_t update = 0;
+
+  /* Get and Check new delay values */
+  VTY_GET_ULONG("delay", delay, argv[0]);
+  switch (argc)
+    {
+    case 1:
+      /* Check new delay value against old Min and Max delays if set */
+      if (IS_PARAM_SET(iflp, LP_MM_DELAY)
+          && (delay <= iflp->min_delay || delay >= iflp->max_delay))
+        {
+          vty_out (vty, "Average delay should be comprise between Min (%d) and Max (%d) delay%s",
+                   iflp->min_delay, iflp->max_delay, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      /* Update delay if value is not set or change */
+      if (IS_PARAM_UNSET(iflp, LP_DELAY)|| iflp->av_delay != delay)
+        {
+          iflp->av_delay = delay;
+          SET_PARAM(iflp, LP_DELAY);
+          update = 1;
+        }
+      /* Unset Min and Max delays if already set */
+      if (IS_PARAM_SET(iflp, LP_MM_DELAY))
+        {
+          iflp->min_delay = 0;
+          iflp->max_delay = 0;
+          UNSET_PARAM(iflp, LP_MM_DELAY);
+          update = 1;
+        }
+      break;
+    case 2:
+      vty_out (vty, "You should specify both Minimum and Maximum delay with Average delay%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    break;
+    case 3:
+      VTY_GET_ULONG("minimum delay", low, argv[1]);
+      VTY_GET_ULONG("maximum delay", high, argv[2]);
+      /* Check new delays value coherency */
+      if (delay <= low || delay >= high)
+        {
+          vty_out (vty, "Average delay should be comprise between Min (%d) and Max (%d) delay%s",
+                   low, high, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      /* Update Delays if needed */
+      if (IS_PARAM_UNSET(iflp, LP_DELAY)
+          || IS_PARAM_UNSET(iflp, LP_MM_DELAY)
+          || iflp->av_delay != delay
+          || iflp->min_delay != low
+          || iflp->max_delay != high)
+        {
+          iflp->av_delay = delay;
+          SET_PARAM(iflp, LP_DELAY);
+          iflp->min_delay = low;
+          iflp->max_delay = high;
+          SET_PARAM(iflp, LP_MM_DELAY);
+          update = 1;
+        }
+      break;
+    default:
+      return CMD_WARNING;
+      break;
+    }
+
+  /* force protocols to update LINK STATE due to parameters change */
+  if (update == 1 && if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (link_params_delay,
+       link_params_delay_mm_cmd,
+       "delay <0-16777215> min <0-16777215> max <0-16777215>",
+       "Unidirectional Average Link Delay (optionally Minimum and Maximum delays)\n"
+       "Average delay in micro-second as decimal (0...16777215)\n"
+       "Minimum delay\n"
+       "Minimum delay in micro-second as decimal (0...16777215)\n"
+       "Maximum delay\n"
+       "Maximum delay in micro-second as decimal (0...16777215)\n")
+
+DEFUN (no_link_params_delay,
+       no_link_params_delay_cmd,
+       "no delay",
+       NO_STR
+       "Disbale Unidirectional Average, Min & Max Link Delay on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  
+  /* Unset Delays */
+  iflp->av_delay = 0;
+  UNSET_PARAM(iflp, LP_DELAY);
+  iflp->min_delay = 0;
+  iflp->max_delay = 0;
+  UNSET_PARAM(iflp, LP_MM_DELAY);
+  
+  /* force protocols to update LINK STATE due to parameters change */
+  if (if_is_operative (ifp))
+    zebra_interface_parameters_update (ifp);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_delay_var,
+       link_params_delay_var_cmd,
+       "delay-variation <0-16777215>",
+       "Unidirectional Link Delay Variation\n"
+       "delay variation in micro-second as decimal (0...16777215)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  u_int32_t value;
+  
+  VTY_GET_ULONG("delay variation", value, argv[0]);
+  
+  /* Update Delay Variation if needed */
+  link_param_cmd_set_uint32 (ifp, &iflp->delay_var, LP_DELAY_VAR, value);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_delay_var,
+       no_link_params_delay_var_cmd,
+       "no delay-variation",
+       NO_STR
+       "Disbale Unidirectional Delay Variation on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Delay Variation */
+  link_param_cmd_unset(ifp, LP_DELAY_VAR);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_pkt_loss,
+       link_params_pkt_loss_cmd,
+       "packet-loss PERCENTAGE",
+       "Unidirectional Link Packet Loss\n"
+       "percentage of total traffic by 0.000003% step and less than 50.331642%\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  float fval;
+
+  if (sscanf (argv[0], "%g", &fval) != 1)
+    {
+      vty_out (vty, "link_params_pkt_loss: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (fval > MAX_PKT_LOSS)
+    fval = MAX_PKT_LOSS;
+
+  /* Update Packet Loss if needed */
+  link_param_cmd_set_float (ifp, &iflp->pkt_loss, LP_PKT_LOSS, fval);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_pkt_loss,
+       no_link_params_pkt_loss_cmd,
+       "no packet-loss",
+       NO_STR
+       "Disbale Unidirectional Link Packet Loss on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Packet Loss */
+  link_param_cmd_unset(ifp, LP_PKT_LOSS);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_res_bw,
+       link_params_res_bw_cmd,
+       "res-bw BANDWIDTH",
+       "Unidirectional Residual Bandwidth\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  float bw;
+
+  if (sscanf (argv[0], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_res_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that bandwidth is not greater than maximum bandwidth parameter */
+  if (bw > iflp->max_bw)
+    {
+      vty_out (vty,
+               "Residual Bandwidth could not be greater than Maximum Bandwidth (%g)%s",
+               iflp->max_bw, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Residual Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->res_bw, LP_RES_BW, bw);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_res_bw,
+       no_link_params_res_bw_cmd,
+       "no res-bw",
+       NO_STR
+       "Disbale Unidirectional Residual Bandwidth on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Residual Bandwidth */
+  link_param_cmd_unset(ifp, LP_RES_BW);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_ava_bw,
+       link_params_ava_bw_cmd,
+       "ava-bw BANDWIDTH",
+       "Unidirectional Available Bandwidth\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  float bw;
+
+  if (sscanf (argv[0], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_ava_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that bandwidth is not greater than maximum bandwidth parameter */
+  if (bw > iflp->max_bw)
+    {
+      vty_out (vty,
+               "Available Bandwidth could not be greater than Maximum Bandwidth (%g)%s",
+               iflp->max_bw, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Residual Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->ava_bw, LP_AVA_BW, bw);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_ava_bw,
+       no_link_params_ava_bw_cmd,
+       "no ava-bw",
+       NO_STR
+       "Disbale Unidirectional Available Bandwidth on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Available Bandwidth */
+  link_param_cmd_unset(ifp, LP_AVA_BW);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (link_params_use_bw,
+       link_params_use_bw_cmd,
+       "use-bw BANDWIDTH",
+       "Unidirectional Utilised Bandwidth\n"
+       "Bytes/second (IEEE floating point format)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct if_link_params *iflp = if_link_params_get (ifp);
+  float bw;
+
+  if (sscanf (argv[0], "%g", &bw) != 1)
+    {
+      vty_out (vty, "link_params_use_bw: fscanf: %s%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check that bandwidth is not greater than maximum bandwidth parameter */
+  if (bw > iflp->max_bw)
+    {
+      vty_out (vty,
+               "Utilised Bandwidth could not be greater than Maximum Bandwidth (%g)%s",
+               iflp->max_bw, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Update Utilized Bandwidth if needed */
+  link_param_cmd_set_float (ifp, &iflp->use_bw, LP_USE_BW, bw);
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_use_bw,
+       no_link_params_use_bw_cmd,
+       "no use-bw",
+       NO_STR
+       "Disbale Unidirectional Utilised Bandwidth on this interface\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  
+  /* Unset Utilised Bandwidth */
+  link_param_cmd_unset(ifp, LP_USE_BW);
+  
+  return CMD_SUCCESS;
+}
+
+static int
+ip_address_install (struct vty *vty, struct interface *ifp,
+                   const char *addr_str, const char *peer_str,
+                   const char *label)
+{
+  struct zebra_if *if_data;
+  struct prefix_ipv4 cp;
+  struct connected *ifc;
+  struct prefix_ipv4 *p;
+  int ret;
+
+  if_data = ifp->info;
+
+  ret = str2prefix_ipv4 (addr_str, &cp);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ifc = connected_check (ifp, (struct prefix *) &cp);
+  if (! ifc)
+    {
+      ifc = connected_new ();
+      ifc->ifp = ifp;
+
+      /* Address. */
+      p = prefix_ipv4_new ();
+      *p = cp;
+      ifc->address = (struct prefix *) p;
+
+      /* Broadcast. */
+      if (p->prefixlen <= IPV4_MAX_PREFIXLEN-2)
+       {
+         p = prefix_ipv4_new ();
+         *p = cp;
+         p->prefix.s_addr = ipv4_broadcast_addr(p->prefix.s_addr,p->prefixlen);
+         ifc->destination = (struct prefix *) p;
+       }
+
+      /* Label. */
+      if (label)
+       ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label);
+
+      /* Add to linked list. */
+      listnode_add (ifp->connected, ifc);
+    }
+
+  /* This address is configured from zebra. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+    SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+  /* In case of this route need to install kernel. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)
+      && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)
+      && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON))
+    {
+      /* Some system need to up the interface to set IP address. */
+      if (! if_is_up (ifp))
+       {
+         if_set_flags (ifp, IFF_UP | IFF_RUNNING);
+         if_refresh (ifp);
+       }
+
+      ret = if_set_prefix (ifp, ifc);
+      if (ret < 0)
+       {
+         vty_out (vty, "%% Can't set interface IP address: %s.%s", 
+                  safe_strerror(errno), VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+      /* The address will be advertised to zebra clients when the notification
+       * from the kernel has been received.
+       * It will also be added to the subnet chain list, then. */
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+ip_address_uninstall (struct vty *vty, struct interface *ifp,
+                     const char *addr_str, const char *peer_str,
+                     const char *label)
+{
+  struct prefix_ipv4 cp;
+  struct connected *ifc;
+  int ret;
+
+  /* Convert to prefix structure. */
+  ret = str2prefix_ipv4 (addr_str, &cp);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check current interface address. */
+  ifc = connected_check (ifp, (struct prefix *) &cp);
+  if (! ifc)
+    {
+      vty_out (vty, "%% Can't find address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* This is not configured address. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+    return CMD_WARNING;
+
+  UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED);
+  
+  /* This is not real address or interface is not active. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)
+      || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      listnode_delete (ifp->connected, ifc);
+      connected_free (ifc);
+      return CMD_WARNING;
+    }
+
+  /* This is real route. */
+  ret = if_unset_prefix (ifp, ifc);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Can't unset interface IP address: %s.%s", 
+              safe_strerror(errno), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+  /* we will receive a kernel notification about this route being removed.
+   * this will trigger its removal from the connected list. */
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_address,
+       ip_address_cmd,
+       "ip address A.B.C.D/M",
+       "Interface Internet Protocol config commands\n"
+       "Set the IP address of an interface\n"
+       "IP address (e.g. 10.0.0.1/8)\n")
+{
+  return ip_address_install (vty, vty->index, argv[0], NULL, NULL);
+}
+
+DEFUN (no_ip_address,
+       no_ip_address_cmd,
+       "no ip address A.B.C.D/M",
+       NO_STR
+       "Interface Internet Protocol config commands\n"
+       "Set the IP address of an interface\n"
+       "IP Address (e.g. 10.0.0.1/8)")
+{
+  return ip_address_uninstall (vty, vty->index, argv[0], NULL, NULL);
+}
+
+#ifdef HAVE_NETLINK
+DEFUN (ip_address_label,
+       ip_address_label_cmd,
+       "ip address A.B.C.D/M label LINE",
+       "Interface Internet Protocol config commands\n"
+       "Set the IP address of an interface\n"
+       "IP address (e.g. 10.0.0.1/8)\n"
+       "Label of this address\n"
+       "Label\n")
+{
+  return ip_address_install (vty, vty->index, argv[0], NULL, argv[1]);
+}
+
+DEFUN (no_ip_address_label,
+       no_ip_address_label_cmd,
+       "no ip address A.B.C.D/M label LINE",
+       NO_STR
+       "Interface Internet Protocol config commands\n"
+       "Set the IP address of an interface\n"
+       "IP address (e.g. 10.0.0.1/8)\n"
+       "Label of this address\n"
+       "Label\n")
+{
+  return ip_address_uninstall (vty, vty->index, argv[0], NULL, argv[1]);
+}
+#endif /* HAVE_NETLINK */
+
+#ifdef HAVE_IPV6
+static int
+ipv6_address_install (struct vty *vty, struct interface *ifp,
+                     const char *addr_str, const char *peer_str,
+                     const char *label, int secondary)
+{
+  struct zebra_if *if_data;
+  struct prefix_ipv6 cp;
+  struct connected *ifc;
+  struct prefix_ipv6 *p;
+  int ret;
+
+  if_data = ifp->info;
+
+  ret = str2prefix_ipv6 (addr_str, &cp);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ifc = connected_check (ifp, (struct prefix *) &cp);
+  if (! ifc)
+    {
+      ifc = connected_new ();
+      ifc->ifp = ifp;
+
+      /* Address. */
+      p = prefix_ipv6_new ();
+      *p = cp;
+      ifc->address = (struct prefix *) p;
+
+      /* Secondary. */
+      if (secondary)
+       SET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY);
+
+      /* Label. */
+      if (label)
+       ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label);
+
+      /* Add to linked list. */
+      listnode_add (ifp->connected, ifc);
+    }
+
+  /* This address is configured from zebra. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+    SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+  /* In case of this route need to install kernel. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)
+      && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)
+      && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON))
+    {
+      /* Some system need to up the interface to set IP address. */
+      if (! if_is_up (ifp))
+       {
+         if_set_flags (ifp, IFF_UP | IFF_RUNNING);
+         if_refresh (ifp);
+       }
+
+      ret = if_prefix_add_ipv6 (ifp, ifc);
+
+      if (ret < 0)
+       {
+         vty_out (vty, "%% Can't set interface IP address: %s.%s", 
+                  safe_strerror(errno), VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+
+      SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+      /* The address will be advertised to zebra clients when the notification
+       * from the kernel has been received. */
+    }
+
+  return CMD_SUCCESS;
+}
+
+static int
+ipv6_address_uninstall (struct vty *vty, struct interface *ifp,
+                       const char *addr_str, const char *peer_str,
+                       const char *label, int secondry)
+{
+  struct prefix_ipv6 cp;
+  struct connected *ifc;
+  int ret;
+
+  /* Convert to prefix structure. */
+  ret = str2prefix_ipv6 (addr_str, &cp);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check current interface address. */
+  ifc = connected_check (ifp, (struct prefix *) &cp);
+  if (! ifc)
+    {
+      vty_out (vty, "%% Can't find address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* This is not configured address. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+    return CMD_WARNING;
+
+  UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+  /* This is not real address or interface is not active. */
+  if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)
+      || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+    {
+      listnode_delete (ifp->connected, ifc);
+      connected_free (ifc);
+      return CMD_WARNING;
+    }
+
+  /* This is real route. */
+  ret = if_prefix_delete_ipv6 (ifp, ifc);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Can't unset interface IP address: %s.%s", 
+              safe_strerror(errno), VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
+  /* This information will be propagated to the zclients when the
+   * kernel notification is received. */
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_address,
+       ipv6_address_cmd,
+       "ipv6 address X:X::X:X/M",
+       "Interface IPv6 config commands\n"
+       "Set the IP address of an interface\n"
+       "IPv6 address (e.g. 3ffe:506::1/48)\n")
+{
+  return ipv6_address_install (vty, vty->index, argv[0], NULL, NULL, 0);
+}
+
+DEFUN (no_ipv6_address,
+       no_ipv6_address_cmd,
+       "no ipv6 address X:X::X:X/M",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Set the IP address of an interface\n"
+       "IPv6 address (e.g. 3ffe:506::1/48)\n")
+{
+  return ipv6_address_uninstall (vty, vty->index, argv[0], NULL, NULL, 0);
+}
+#endif /* HAVE_IPV6 */
+
+static int
+link_params_config_write (struct vty *vty, struct interface *ifp)
+{
+  int i;
+  
+  if ((ifp == NULL) || !HAS_LINK_PARAMS(ifp))
+    return -1;
+  
+  struct if_link_params *iflp = ifp->link_params;
+
+  vty_out (vty, " link-params%s", VTY_NEWLINE);
+  vty_out(vty, "  enable%s", VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_TE))
+    vty_out(vty, "  metric %u%s",iflp->te_metric, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_MAX_BW))
+    vty_out(vty, "  max-bw %g%s", iflp->max_bw, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW))
+    vty_out(vty, "  max-rsv-bw %g%s", iflp->max_rsv_bw, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_UNRSV_BW))
+    {
+      for (i = 0; i < 8; i++)
+        vty_out(vty, "  unrsv-bw %d %g%s",
+            i, iflp->unrsv_bw[i], VTY_NEWLINE);
+    }
+  if (IS_PARAM_SET(iflp, LP_ADM_GRP))
+    vty_out(vty, "  admin-grp %u%s", iflp->admin_grp, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_DELAY))
+    {
+      vty_out(vty, "  delay %u", iflp->av_delay);
+      if (IS_PARAM_SET(iflp, LP_MM_DELAY))
+        {
+          vty_out(vty, " min %u", iflp->min_delay);
+          vty_out(vty, " max %u", iflp->max_delay);
+        }
+      vty_out(vty, "%s", VTY_NEWLINE);
+    }
+  if (IS_PARAM_SET(iflp, LP_DELAY_VAR))
+    vty_out(vty, "  delay-variation %u%s", iflp->delay_var, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_PKT_LOSS))
+    vty_out(vty, "  packet-loss %g%s", iflp->pkt_loss, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_AVA_BW))
+    vty_out(vty, "  ava-bw %g%s", iflp->ava_bw, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_RES_BW))
+    vty_out(vty, "  res-bw %g%s", iflp->res_bw, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_USE_BW))
+    vty_out(vty, "  use-bw %g%s", iflp->use_bw, VTY_NEWLINE);
+  if (IS_PARAM_SET(iflp, LP_RMT_AS))
+    vty_out(vty, "  neighbor %s as %u%s", inet_ntoa(iflp->rmt_ip),
+        iflp->rmt_as, VTY_NEWLINE);
+  vty_out(vty, "  exit-link-params%s", VTY_NEWLINE);
+  return 0;
+}
+
+static int
+if_config_write (struct vty *vty)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+  for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp))
+    {
+      struct zebra_if *if_data;
+      struct listnode *addrnode;
+      struct connected *ifc;
+      struct prefix *p;
+
+      if_data = ifp->info;
+
+      if (ifp->vrf_id == VRF_DEFAULT)
+        vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+      else
+        vty_out (vty, "interface %s vrf %u%s", ifp->name, ifp->vrf_id,
+                 VTY_NEWLINE);
+
+      if (if_data)
+       {
+         if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)
+           vty_out (vty, " shutdown%s", VTY_NEWLINE);
+       }
+
+      if (ifp->desc)
+       vty_out (vty, " description %s%s", ifp->desc,
+                VTY_NEWLINE);
+
+      /* Assign bandwidth here to avoid unnecessary interface flap
+        while processing config script */
+      if (ifp->bandwidth != 0)
+       vty_out(vty, " bandwidth %u%s", ifp->bandwidth, VTY_NEWLINE); 
+      
+      switch (if_data->linkdetect)
+        {
+          case IF_LINKDETECT_ON:
+            vty_out(vty, " link-detect%s", VTY_NEWLINE);
+            break;
+          case IF_LINKDETECT_OFF:
+            vty_out(vty, " no link-detect%s", VTY_NEWLINE);
+            break;
+          default: break;
+        }
+      
+      for (ALL_LIST_ELEMENTS_RO (ifp->connected, addrnode, ifc))
+         {
+           if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
+             {
+               char buf[INET6_ADDRSTRLEN];
+               p = ifc->address;
+               vty_out (vty, " ip%s address %s",
+                        p->family == AF_INET ? "" : "v6",
+                        prefix2str (p, buf, sizeof(buf)));
+
+               if (ifc->label)
+                 vty_out (vty, " label %s", ifc->label);
+
+               vty_out (vty, "%s", VTY_NEWLINE);
+             }
+         }
+
+      if (if_data)
+       {
+         if (if_data->multicast != IF_ZEBRA_MULTICAST_UNSPEC)
+           vty_out (vty, " %smulticast%s",
+                    if_data->multicast == IF_ZEBRA_MULTICAST_ON ? "" : "no ",
+                    VTY_NEWLINE);
+       }
+
+#if defined (HAVE_RTADV)
+      rtadv_config_write (vty, ifp);
+#endif /* HAVE_RTADV */
+
+#ifdef HAVE_IRDP
+      irdp_config_write (vty, ifp);
+#endif /* IRDP */
+
+      link_params_config_write (vty, ifp);
+
+      vty_out (vty, "!%s", VTY_NEWLINE);
+    }
+  return 0;
+}
+
+
+/* Allocate and initialize interface vector. */
+void
+zebra_if_init (void)
+{
+  /* Initialize interface and new hook. */
+  if_add_hook (IF_NEW_HOOK, if_zebra_new_hook);
+  if_add_hook (IF_DELETE_HOOK, if_zebra_delete_hook);
+  
+  /* Install configuration write function. */
+  install_node (&interface_node, if_config_write);
+  
+  install_node (&zebra_if_defaults_node, config_write_zebra_if_defaults);
+
+  install_node (&link_params_node, NULL);
+  
+  install_element (VIEW_NODE, &show_interface_cmd);
+  install_element (VIEW_NODE, &show_interface_vrf_cmd);
+  install_element (VIEW_NODE, &show_interface_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_interface_name_cmd);
+  install_element (VIEW_NODE, &show_interface_name_vrf_cmd);
+  install_element (VIEW_NODE, &show_interface_name_vrf_all_cmd);
+  install_element (CONFIG_NODE, &zebra_interface_cmd);
+  install_element (CONFIG_NODE, &zebra_interface_vrf_cmd);
+  install_element (CONFIG_NODE, &no_interface_cmd);
+  install_element (CONFIG_NODE, &no_interface_vrf_cmd);
+  install_element (CONFIG_NODE, &default_linkdetect_cmd);
+  install_default (INTERFACE_NODE);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+  install_element (INTERFACE_NODE, &multicast_cmd);
+  install_element (INTERFACE_NODE, &no_multicast_cmd);
+  install_element (INTERFACE_NODE, &linkdetect_cmd);
+  install_element (INTERFACE_NODE, &no_linkdetect_cmd);
+  install_element (INTERFACE_NODE, &shutdown_if_cmd);
+  install_element (INTERFACE_NODE, &no_shutdown_if_cmd);
+  install_element (INTERFACE_NODE, &bandwidth_if_cmd);
+  install_element (INTERFACE_NODE, &no_bandwidth_if_cmd);
+  install_element (INTERFACE_NODE, &no_bandwidth_if_val_cmd);
+  install_element (INTERFACE_NODE, &ip_address_cmd);
+  install_element (INTERFACE_NODE, &no_ip_address_cmd);
+#ifdef HAVE_IPV6
+  install_element (INTERFACE_NODE, &ipv6_address_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_address_cmd);
+#endif /* HAVE_IPV6 */
+#ifdef HAVE_NETLINK
+  install_element (INTERFACE_NODE, &ip_address_label_cmd);
+  install_element (INTERFACE_NODE, &no_ip_address_label_cmd);
+#endif /* HAVE_NETLINK */
+  install_element(INTERFACE_NODE, &link_params_cmd);
+  install_default(LINK_PARAMS_NODE);
+  install_element(LINK_PARAMS_NODE, &link_params_enable_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_enable_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_metric_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_maxbw_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_max_rsv_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_unrsv_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_admin_grp_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_admin_grp_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_inter_as_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_inter_as_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_delay_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_delay_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_delay_mm_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_delay_var_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_delay_var_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_pkt_loss_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_pkt_loss_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_ava_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_ava_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_res_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_res_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd);
+  install_element(LINK_PARAMS_NODE, &exit_link_params_cmd);
+}
diff --git a/zebra/interface.h b/zebra/interface.h
new file mode 100644 (file)
index 0000000..223caf8
--- /dev/null
@@ -0,0 +1,264 @@
+/* Interface function header.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_INTERFACE_H
+#define _ZEBRA_INTERFACE_H
+
+#include "redistribute.h"
+#include "event_counter.h"
+
+#ifdef HAVE_IRDP
+#include "zebra/irdp.h"
+#endif
+
+/* For interface multicast configuration. */
+#define IF_ZEBRA_MULTICAST_UNSPEC 0
+#define IF_ZEBRA_MULTICAST_ON     1
+#define IF_ZEBRA_MULTICAST_OFF    2
+
+/* For interface shutdown configuration. */
+#define IF_ZEBRA_SHUTDOWN_OFF    0
+#define IF_ZEBRA_SHUTDOWN_ON     1
+
+/* Global user-configured default for interface link-detect */
+typedef enum {
+  IF_LINKDETECT_UNSPEC = 0,
+  IF_LINKDETECT_ON,
+  IF_LINKDETECT_OFF,
+} zebra_if_linkdetect;
+
+/* Global defaults for interfaces */
+struct zebra_if_defaults {
+  /* Link-detect default configuration */
+  zebra_if_linkdetect linkdetect;
+};
+
+#if defined (HAVE_RTADV)
+/* Router advertisement parameter.  From RFC4861, RFC6275 and RFC4191. */
+struct rtadvconf
+{
+  /* A flag indicating whether or not the router sends periodic Router
+     Advertisements and responds to Router Solicitations.
+     Default: FALSE */
+  int AdvSendAdvertisements;
+
+  /* The maximum time allowed between sending unsolicited multicast
+     Router Advertisements from the interface, in milliseconds.
+     MUST be no less than 70 ms [RFC6275 7.5] and no greater
+     than 1800000 ms [RFC4861 6.2.1].
+
+     Default: 600000 milliseconds */
+  int MaxRtrAdvInterval;
+#define RTADV_MAX_RTR_ADV_INTERVAL 600000
+
+  /* The minimum time allowed between sending unsolicited multicast
+     Router Advertisements from the interface, in milliseconds.
+     MUST be no less than 30 ms [RFC6275 7.5].
+     MUST be no greater than .75 * MaxRtrAdvInterval.
+
+     Default: 0.33 * MaxRtrAdvInterval */
+  int MinRtrAdvInterval; /* This field is currently unused. */
+#define RTADV_MIN_RTR_ADV_INTERVAL (0.33 * RTADV_MAX_RTR_ADV_INTERVAL)
+
+  /* Unsolicited Router Advertisements' interval timer. */
+  int AdvIntervalTimer;
+
+  /* The TRUE/FALSE value to be placed in the "Managed address
+     configuration" flag field in the Router Advertisement.  See
+     [ADDRCONF].
+     Default: FALSE */
+  int AdvManagedFlag;
+
+
+  /* The TRUE/FALSE value to be placed in the "Other stateful
+     configuration" flag field in the Router Advertisement.  See
+     [ADDRCONF].
+
+     Default: FALSE */
+  int AdvOtherConfigFlag;
+
+  /* The value to be placed in MTU options sent by the router.  A
+     value of zero indicates that no MTU options are sent.
+
+     Default: 0 */
+  int AdvLinkMTU;
+
+
+  /* The value to be placed in the Reachable Time field in the Router
+     Advertisement messages sent by the router.  The value zero means
+     unspecified (by this router).  MUST be no greater than 3,600,000
+     milliseconds (1 hour).
+
+     Default: 0 */
+  u_int32_t AdvReachableTime;
+#define RTADV_MAX_REACHABLE_TIME 3600000
+
+
+  /* The value to be placed in the Retrans Timer field in the Router
+     Advertisement messages sent by the router.  The value zero means
+     unspecified (by this router).
+
+     Default: 0 */
+  int AdvRetransTimer;
+
+  /* The default value to be placed in the Cur Hop Limit field in the
+     Router Advertisement messages sent by the router.  The value
+     should be set to that current diameter of the Internet.  The
+     value zero means unspecified (by this router).
+
+     Default: The value specified in the "Assigned Numbers" RFC
+     [ASSIGNED] that was in effect at the time of implementation. */
+  int AdvCurHopLimit;
+
+  /* The value to be placed in the Router Lifetime field of Router
+     Advertisements sent from the interface, in seconds.  MUST be
+     either zero or between MaxRtrAdvInterval and 9000 seconds.  A
+     value of zero indicates that the router is not to be used as a
+     default router.
+
+     Default: 3 * MaxRtrAdvInterval */
+  int AdvDefaultLifetime;
+#define RTADV_MAX_RTRLIFETIME 9000 /* 2.5 hours */
+
+  /* A list of prefixes to be placed in Prefix Information options in
+     Router Advertisement messages sent from the interface.
+
+     Default: all prefixes that the router advertises via routing
+     protocols as being on-link for the interface from which the
+     advertisement is sent. The link-local prefix SHOULD NOT be
+     included in the list of advertised prefixes. */
+  struct list *AdvPrefixList;
+
+  /* The TRUE/FALSE value to be placed in the "Home agent"
+     flag field in the Router Advertisement.  See [RFC6275 7.1].
+
+     Default: FALSE */
+  int AdvHomeAgentFlag;
+#ifndef ND_RA_FLAG_HOME_AGENT
+#define ND_RA_FLAG_HOME_AGENT  0x20
+#endif
+
+  /* The value to be placed in Home Agent Information option if Home 
+     Flag is set.
+     Default: 0 */
+  int HomeAgentPreference;
+
+  /* The value to be placed in Home Agent Information option if Home 
+     Flag is set. Lifetime (seconds) MUST not be greater than 18.2 
+     hours. 
+     The value 0 has special meaning: use of AdvDefaultLifetime value.
+     
+     Default: 0 */
+  int HomeAgentLifetime;
+#define RTADV_MAX_HALIFETIME 65520 /* 18.2 hours */
+
+  /* The TRUE/FALSE value to insert or not an Advertisement Interval
+     option. See [RFC 6275 7.3]
+
+     Default: FALSE */
+  int AdvIntervalOption;
+
+  /* The value to be placed in the Default Router Preference field of
+     a router advertisement. See [RFC 4191 2.1 & 2.2]
+
+     Default: 0 (medium) */
+  int DefaultPreference;
+#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
+};
+
+#endif /* HAVE_RTADV */
+
+/* `zebra' daemon local interface structure. */
+struct zebra_if
+{
+  /* Shutdown configuration. */
+  u_char shutdown;
+
+  /* Multicast configuration. */
+  u_char multicast;
+
+  /* Router advertise configuration. */
+  u_char rtadv_enable;
+  
+  /* Interface specific link-detect configuration state */
+  zebra_if_linkdetect linkdetect;
+  
+  /* Installed addresses chains tree. */
+  struct route_table *ipv4_subnets;
+
+  /* Information about up/down changes */
+  struct event_counter up_events;
+  struct event_counter down_events;
+
+#if defined(HAVE_RTADV)
+  struct rtadvconf rtadv;
+#endif /* RTADV */
+
+#ifdef HAVE_IRDP
+  struct irdp_interface irdp;
+#endif
+
+#ifdef HAVE_STRUCT_SOCKADDR_DL
+  union {
+    /* note that sdl_storage is never accessed, it only exists to make space.
+     * all actual uses refer to sdl - but use sizeof(sdl_storage)!  this fits
+     * best with C aliasing rules. */
+    struct sockaddr_dl sdl;
+    struct sockaddr_storage sdl_storage;
+  };
+#endif
+
+#ifdef SUNOS_5
+  /* the real IFF_UP state of the primary interface.
+   * need this to differentiate between all interfaces being
+   * down (but primary still plumbed) and primary having gone
+   * ~IFF_UP, and all addresses gone.
+   */
+  u_char primary_state;
+#endif /* SUNOS_5 */
+};
+
+extern void if_delete_update (struct interface *ifp);
+extern void if_add_update (struct interface *ifp);
+extern void if_up (struct interface *);
+extern void if_down (struct interface *);
+extern void if_refresh (struct interface *);
+extern void if_flags_update (struct interface *, uint64_t);
+extern void if_startup_count_up (void);
+extern int if_subnet_add (struct interface *, struct connected *);
+extern int if_subnet_delete (struct interface *, struct connected *);
+
+#ifdef HAVE_PROC_NET_DEV
+extern void ifstat_update_proc (void);
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+extern void ifstat_update_sysctl (void);
+
+#endif /* HAVE_NET_RT_IFLIST */
+#ifdef HAVE_PROC_NET_DEV
+extern int interface_list_proc (void);
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_PROC_NET_IF_INET6
+extern int ifaddr_proc_ipv6 (void);
+#endif /* HAVE_PROC_NET_IF_INET6 */
+
+#endif /* _ZEBRA_INTERFACE_H */
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
new file mode 100644 (file)
index 0000000..e1ee429
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Common ioctl functions.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "log.h"
+#include "privs.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/interface.h"
+
+#ifdef HAVE_BSD_LINK_DETECT
+#include <net/if_media.h>
+#endif /* HAVE_BSD_LINK_DETECT*/
+
+extern struct zebra_privs_t zserv_privs;
+
+/* clear and set interface name string */
+void
+ifreq_set_name (struct ifreq *ifreq, struct interface *ifp)
+{
+  strncpy (ifreq->ifr_name, ifp->name, IFNAMSIZ);
+}
+
+/* call ioctl system call */
+int
+if_ioctl (u_long request, caddr_t buffer)
+{
+  int sock;
+  int ret;
+  int err = 0;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  sock = socket (AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      int save_errno = errno;
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno));
+      exit (1);
+    }
+  if ((ret = ioctl (sock, request, buffer)) < 0)
+    err = errno;
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  close (sock);
+  
+  if (ret < 0) 
+    {
+      errno = err;
+      return ret;
+    }
+  return 0;
+}
+
+#ifdef HAVE_IPV6
+static int
+if_ioctl_ipv6 (u_long request, caddr_t buffer)
+{
+  int sock;
+  int ret;
+  int err = 0;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  sock = socket (AF_INET6, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      int save_errno = errno;
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_err("Cannot create IPv6 datagram socket: %s",
+              safe_strerror(save_errno));
+      exit (1);
+    }
+
+  if ((ret = ioctl (sock, request, buffer)) < 0)
+    err = errno;
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  close (sock);
+  
+  if (ret < 0) 
+    {
+      errno = err;
+      return ret;
+    }
+  return 0;
+}
+#endif /* HAVE_IPV6 */
+
+/*
+ * get interface metric
+ *   -- if value is not avaliable set -1
+ */
+void
+if_get_metric (struct interface *ifp)
+{
+#ifdef SIOCGIFMETRIC
+  struct ifreq ifreq;
+
+  ifreq_set_name (&ifreq, ifp);
+
+  if (if_ioctl (SIOCGIFMETRIC, (caddr_t) &ifreq) < 0) 
+    return;
+  ifp->metric = ifreq.ifr_metric;
+  if (ifp->metric == 0)
+    ifp->metric = 1;
+#else /* SIOCGIFMETRIC */
+  ifp->metric = -1;
+#endif /* SIOCGIFMETRIC */
+}
+
+/* get interface MTU */
+void
+if_get_mtu (struct interface *ifp)
+{
+  struct ifreq ifreq;
+
+  ifreq_set_name (&ifreq, ifp);
+
+#if defined(SIOCGIFMTU)
+  if (if_ioctl (SIOCGIFMTU, (caddr_t) & ifreq) < 0) 
+    {
+      zlog_info ("Can't lookup mtu by ioctl(SIOCGIFMTU)");
+      ifp->mtu6 = ifp->mtu = -1;
+      return;
+    }
+
+#ifdef SUNOS_5
+  ifp->mtu6 = ifp->mtu = ifreq.ifr_metric;
+#else
+  ifp->mtu6 = ifp->mtu = ifreq.ifr_mtu;
+#endif /* SUNOS_5 */
+
+  /* propogate */
+  zebra_interface_up_update(ifp);
+
+#else
+  zlog (NULL, LOG_INFO, "Can't lookup mtu on this system");
+  ifp->mtu6 = ifp->mtu = -1;
+#endif
+}
+
+#ifdef HAVE_NETLINK
+/* Interface address setting via netlink interface. */
+int
+if_set_prefix (struct interface *ifp, struct connected *ifc)
+{
+  return kernel_address_add_ipv4 (ifp, ifc);
+}
+
+/* Interface address is removed using netlink interface. */
+int
+if_unset_prefix (struct interface *ifp, struct connected *ifc)
+{
+  return kernel_address_delete_ipv4 (ifp, ifc);
+}
+#else /* ! HAVE_NETLINK */
+#ifdef HAVE_STRUCT_IFALIASREQ
+/* Set up interface's IP address, netmask (and broadcas? ).  *BSD may
+   has ifaliasreq structure.  */
+int
+if_set_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifaliasreq addreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in mask;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *) ifc->address;
+
+  memset (&addreq, 0, sizeof addreq);
+  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  addr.sin_addr = p->prefix;
+  addr.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  addr.sin_len = sizeof (struct sockaddr_in);
+#endif
+  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in));
+
+  memset (&mask, 0, sizeof (struct sockaddr_in));
+  masklen2ip (p->prefixlen, &mask.sin_addr);
+  mask.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  mask.sin_len = sizeof (struct sockaddr_in);
+#endif
+  memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
+  
+  ret = if_ioctl (SIOCAIFADDR, (caddr_t) &addreq);
+  if (ret < 0)
+    return ret;
+  return 0;
+}
+
+/* Set up interface's IP address, netmask (and broadcas? ).  *BSD may
+   has ifaliasreq structure.  */
+int
+if_unset_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifaliasreq addreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in mask;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *)ifc->address;
+
+  memset (&addreq, 0, sizeof addreq);
+  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  addr.sin_addr = p->prefix;
+  addr.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  addr.sin_len = sizeof (struct sockaddr_in);
+#endif
+  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in));
+
+  memset (&mask, 0, sizeof (struct sockaddr_in));
+  masklen2ip (p->prefixlen, &mask.sin_addr);
+  mask.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  mask.sin_len = sizeof (struct sockaddr_in);
+#endif
+  memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
+  
+  ret = if_ioctl (SIOCDIFADDR, (caddr_t) &addreq);
+  if (ret < 0)
+    return ret;
+  return 0;
+}
+#else
+/* Set up interface's address, netmask (and broadcas? ).  Linux or
+   Solaris uses ifname:number semantics to set IP address aliases. */
+int
+if_set_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in broad;
+  struct sockaddr_in mask;
+  struct prefix_ipv4 ifaddr;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *) ifc->address;
+
+  ifaddr = *p;
+
+  ifreq_set_name (&ifreq, ifp);
+
+  addr.sin_addr = p->prefix;
+  addr.sin_family = p->family;
+  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
+  ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq);
+  if (ret < 0)
+    return ret;
+  
+  /* We need mask for make broadcast addr. */
+  masklen2ip (p->prefixlen, &mask.sin_addr);
+
+  if (if_is_broadcast (ifp))
+    {
+      apply_mask_ipv4 (&ifaddr);
+      addr.sin_addr = ifaddr.prefix;
+
+      broad.sin_addr.s_addr = (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr);
+      broad.sin_family = p->family;
+
+      memcpy (&ifreq.ifr_broadaddr, &broad, sizeof (struct sockaddr_in));
+      ret = if_ioctl (SIOCSIFBRDADDR, (caddr_t) &ifreq);
+      if (ret < 0)
+       return ret;
+    }
+
+  mask.sin_family = p->family;
+#ifdef SUNOS_5
+  memcpy (&mask, &ifreq.ifr_addr, sizeof (mask));
+#else
+  memcpy (&ifreq.ifr_netmask, &mask, sizeof (struct sockaddr_in));
+#endif /* SUNOS5 */
+  ret = if_ioctl (SIOCSIFNETMASK, (caddr_t) &ifreq);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+/* Set up interface's address, netmask (and broadcas? ).  Linux or
+   Solaris uses ifname:number semantics to set IP address aliases. */
+int
+if_unset_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *) ifc->address;
+
+  ifreq_set_name (&ifreq, ifp);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  addr.sin_family = p->family;
+  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
+  ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+#endif /* HAVE_STRUCT_IFALIASREQ */
+#endif /* HAVE_NETLINK */
+
+/* get interface flags */
+void
+if_get_flags (struct interface *ifp)
+{
+  int ret;
+  struct ifreq ifreq;
+#ifdef HAVE_BSD_LINK_DETECT
+  struct ifmediareq ifmr;
+#endif /* HAVE_BSD_LINK_DETECT */
+
+  ifreq_set_name (&ifreq, ifp);
+
+  ret = if_ioctl (SIOCGIFFLAGS, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno));
+      return;
+    }
+#ifdef HAVE_BSD_LINK_DETECT /* Detect BSD link-state at start-up */
+
+  /* Per-default, IFF_RUNNING is held high, unless link-detect says
+   * otherwise - we abuse IFF_RUNNING inside zebra as a link-state flag,
+   * following practice on Linux and Solaris kernels
+   */
+  SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
+  
+  if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
+    {
+      (void) memset(&ifmr, 0, sizeof(ifmr));
+      strncpy (ifmr.ifm_name, ifp->name, IFNAMSIZ);
+      
+      /* Seems not all interfaces implement this ioctl */
+      if (if_ioctl(SIOCGIFMEDIA, (caddr_t) &ifmr) < 0)
+        zlog_err("if_ioctl(SIOCGIFMEDIA) failed: %s", safe_strerror(errno));
+      else if (ifmr.ifm_status & IFM_AVALID) /* Link state is valid */
+        {
+          if (ifmr.ifm_status & IFM_ACTIVE)
+            SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
+          else
+            UNSET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
+        }
+  }
+#endif /* HAVE_BSD_LINK_DETECT */
+
+  if_flags_update (ifp, (ifreq.ifr_flags & 0x0000ffff));
+}
+
+/* Set interface flags */
+int
+if_set_flags (struct interface *ifp, uint64_t flags)
+{
+  int ret;
+  struct ifreq ifreq;
+
+  memset (&ifreq, 0, sizeof(struct ifreq));
+  ifreq_set_name (&ifreq, ifp);
+
+  ifreq.ifr_flags = ifp->flags;
+  ifreq.ifr_flags |= flags;
+
+  ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq);
+
+  if (ret < 0)
+    {
+      zlog_info ("can't set interface flags");
+      return ret;
+    }
+  return 0;
+}
+
+/* Unset interface's flag. */
+int
+if_unset_flags (struct interface *ifp, uint64_t flags)
+{
+  int ret;
+  struct ifreq ifreq;
+
+  memset (&ifreq, 0, sizeof(struct ifreq));
+  ifreq_set_name (&ifreq, ifp);
+
+  ifreq.ifr_flags = ifp->flags;
+  ifreq.ifr_flags &= ~flags;
+
+  ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq);
+
+  if (ret < 0)
+    {
+      zlog_info ("can't unset interface flags");
+      return ret;
+    }
+  return 0;
+}
+
+#ifdef HAVE_IPV6
+
+#ifdef LINUX_IPV6
+#ifndef _LINUX_IN6_H
+/* linux/include/net/ipv6.h */
+struct in6_ifreq 
+{
+  struct in6_addr ifr6_addr;
+  u_int32_t ifr6_prefixlen;
+  int ifr6_ifindex;
+};
+#endif /* _LINUX_IN6_H */
+
+/* Interface's address add/delete functions. */
+int
+if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct prefix_ipv6 *p;
+  struct in6_ifreq ifreq;
+
+  p = (struct prefix_ipv6 *) ifc->address;
+
+  memset (&ifreq, 0, sizeof (struct in6_ifreq));
+
+  memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr));
+  ifreq.ifr6_ifindex = ifp->ifindex;
+  ifreq.ifr6_prefixlen = p->prefixlen;
+
+  ret = if_ioctl_ipv6 (SIOCSIFADDR, (caddr_t) &ifreq);
+
+  return ret;
+}
+
+int
+if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct prefix_ipv6 *p;
+  struct in6_ifreq ifreq;
+
+  p = (struct prefix_ipv6 *) ifc->address;
+
+  memset (&ifreq, 0, sizeof (struct in6_ifreq));
+
+  memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr));
+  ifreq.ifr6_ifindex = ifp->ifindex;
+  ifreq.ifr6_prefixlen = p->prefixlen;
+
+  ret = if_ioctl_ipv6 (SIOCDIFADDR, (caddr_t) &ifreq);
+
+  return ret;
+}
+#else /* LINUX_IPV6 */
+#ifdef HAVE_STRUCT_IN6_ALIASREQ
+#ifndef ND6_INFINITE_LIFETIME
+#define ND6_INFINITE_LIFETIME 0xffffffffL
+#endif /* ND6_INFINITE_LIFETIME */
+int
+if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct in6_aliasreq addreq;
+  struct sockaddr_in6 addr;
+  struct sockaddr_in6 mask;
+  struct prefix_ipv6 *p;
+
+  p = (struct prefix_ipv6 * ) ifc->address;
+
+  memset (&addreq, 0, sizeof addreq);
+  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in6));
+  addr.sin6_addr = p->prefix;
+  addr.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  addr.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6));
+
+  memset (&mask, 0, sizeof (struct sockaddr_in6));
+  masklen2ip6 (p->prefixlen, &mask.sin6_addr);
+  mask.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  mask.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+  memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6));
+
+  addreq.ifra_lifetime.ia6t_vltime = 0xffffffff;
+  addreq.ifra_lifetime.ia6t_pltime = 0xffffffff;
+  
+#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME 
+  addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; 
+  addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; 
+#endif
+
+  ret = if_ioctl_ipv6 (SIOCAIFADDR_IN6, (caddr_t) &addreq);
+  if (ret < 0)
+    return ret;
+  return 0;
+}
+
+int
+if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct in6_aliasreq addreq;
+  struct sockaddr_in6 addr;
+  struct sockaddr_in6 mask;
+  struct prefix_ipv6 *p;
+
+  p = (struct prefix_ipv6 *) ifc->address;
+
+  memset (&addreq, 0, sizeof addreq);
+  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in6));
+  addr.sin6_addr = p->prefix;
+  addr.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  addr.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6));
+
+  memset (&mask, 0, sizeof (struct sockaddr_in6));
+  masklen2ip6 (p->prefixlen, &mask.sin6_addr);
+  mask.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  mask.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+  memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6));
+
+#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
+  addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; 
+  addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; 
+#endif
+
+  ret = if_ioctl_ipv6 (SIOCDIFADDR_IN6, (caddr_t) &addreq);
+  if (ret < 0)
+    return ret;
+  return 0;
+}
+#else
+int
+if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  return 0;
+}
+
+int
+if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  return 0;
+}
+#endif /* HAVE_STRUCT_IN6_ALIASREQ */
+
+#endif /* LINUX_IPV6 */
+
+#endif /* HAVE_IPV6 */
diff --git a/zebra/ioctl.h b/zebra/ioctl.h
new file mode 100644 (file)
index 0000000..fee9b72
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Common ioctl functions.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_IOCTL_H
+#define _ZEBRA_IOCTL_H
+
+/* Prototypes. */
+extern void ifreq_set_name (struct ifreq *, struct interface *);
+extern int if_ioctl (u_long, caddr_t);
+
+extern int if_set_flags (struct interface *, uint64_t);
+extern int if_unset_flags (struct interface *, uint64_t);
+extern void if_get_flags (struct interface *);
+
+extern int if_set_prefix (struct interface *, struct connected *);
+extern int if_unset_prefix (struct interface *, struct connected *);
+
+extern void if_get_metric (struct interface *);
+extern void if_get_mtu (struct interface *);
+
+#ifdef HAVE_IPV6
+extern int if_prefix_add_ipv6 (struct interface *, struct connected *);
+extern int if_prefix_delete_ipv6 (struct interface *, struct connected *);
+#endif /* HAVE_IPV6 */
+
+#ifdef SOLARIS_IPV6
+extern int if_ioctl_ipv6(u_long, caddr_t);
+extern struct connected *if_lookup_linklocal( struct interface *);
+
+#define AF_IOCTL(af, request, buffer) \
+        ((af) == AF_INET? if_ioctl(request, buffer) : \
+                          if_ioctl_ipv6(request, buffer))
+#else /* SOLARIS_IPV6 */
+
+#define AF_IOCTL(af, request, buffer)  if_ioctl(request, buffer)
+
+#endif /* SOLARIS_IPV6 */
+
+#endif /* _ZEBRA_IOCTL_H */
diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c
new file mode 100644 (file)
index 0000000..5a8be99
--- /dev/null
@@ -0,0 +1,78 @@
+/* 
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/ioctl.h"
+
+void ifreq_set_name (struct ifreq *a, struct interface *b) { return; }
+
+int if_set_prefix (struct interface *a, struct connected *b)
+{ 
+  kernel_address_add_ipv4 (a, b);
+  return 0;
+}
+
+int if_unset_prefix (struct interface *a, struct connected *b)
+{ 
+  kernel_address_delete_ipv4 (a, b);
+  return 0;
+}
+
+int if_prefix_add_ipv6 (struct interface *a, struct connected *b) { return 0; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak if_prefix_delete_ipv6 = if_prefix_add_ipv6
+#else
+int if_prefix_delete_ipv6 (struct interface *a, struct connected *b) { return 0; }
+#endif
+
+int if_ioctl (u_long a, caddr_t b) { return 0; }
+
+int if_set_flags (struct interface *a, uint64_t b) { return 0; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak if_unset_flags = if_set_flags
+#else
+int if_unset_flags (struct interface *a, uint64_t b) { return 0; }
+#endif
+
+void if_get_flags (struct interface *a) { return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak if_get_metric = if_get_flags
+#pragma weak if_get_mtu = if_get_flags
+#else
+/* void if_get_metric (struct interface *a) { return; } */
+/* void if_get_mtu (struct interface *a) { return; } */
+#endif
+
+#ifdef SOLARIS_IPV6
+#pragma weak if_ioctl_ipv6 = if_ioctl
+struct connected *if_lookup_linklocal(struct interface *a) { return 0; }
+
+#define AF_IOCTL(af, request, buffer) \
+        ((af) == AF_INET? if_ioctl(request, buffer) : \
+                          if_ioctl_ipv6(request, buffer))
+#else /* SOLARIS_IPV6 */
+
+#define AF_IOCTL(af, request, buffer)  if_ioctl(request, buffer)
+
+#endif /* SOLARIS_IPV6 */
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
new file mode 100644 (file)
index 0000000..b5bf1cc
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * Common ioctl functions for Solaris.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "log.h"
+#include "privs.h"
+#include "vty.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/interface.h"
+#include "zebra/ioctl_solaris.h"
+
+extern struct zebra_privs_t zserv_privs;
+
+/* clear and set interface name string */
+void
+lifreq_set_name (struct lifreq *lifreq, const char *ifname)
+{
+  strncpy (lifreq->lifr_name, ifname, IFNAMSIZ);
+}
+
+/* call ioctl system call */
+int
+if_ioctl (u_long request, caddr_t buffer)
+{
+  int sock;
+  int ret;
+  int err;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+    
+  sock = socket (AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      int save_errno = errno;
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno));
+      exit (1);
+    }
+
+  if ((ret = ioctl (sock, request, buffer)) < 0)
+    err = errno;
+  
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  close (sock);
+
+  if (ret < 0)
+    {
+      errno = err;
+      return ret;
+    }
+  return 0;
+}
+
+
+int
+if_ioctl_ipv6 (u_long request, caddr_t buffer)
+{
+#ifdef HAVE_IPV6
+  int sock;
+  int ret;
+  int err;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+
+  sock = socket (AF_INET6, SOCK_DGRAM, 0);
+  if (sock < 0)
+    {
+      int save_errno = errno;
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_err("Cannot create IPv6 datagram socket: %s",
+              safe_strerror(save_errno));
+      exit (1);
+    }
+
+  if ((ret = ioctl (sock, request, buffer)) < 0)
+    err = errno;
+
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  close (sock);
+
+  if (ret < 0)
+    {
+      errno = err;
+      return ret;
+    }
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/*
+ * get interface metric
+ *   -- if value is not avaliable set -1
+ */
+void
+if_get_metric (struct interface *ifp)
+{
+  struct lifreq lifreq;
+  int ret;
+
+  lifreq_set_name (&lifreq, ifp->name);
+
+  if (ifp->flags & IFF_IPV4)
+    ret = AF_IOCTL (AF_INET, SIOCGLIFMETRIC, (caddr_t) & lifreq);
+#ifdef SOLARIS_IPV6
+  else if (ifp->flags & IFF_IPV6)
+    ret = AF_IOCTL (AF_INET6, SIOCGLIFMETRIC, (caddr_t) & lifreq);
+#endif /* SOLARIS_IPV6 */
+  else
+    ret = -1;
+    
+  if (ret < 0)
+    return;
+
+  ifp->metric = lifreq.lifr_metric;
+
+  if (ifp->metric == 0)
+    ifp->metric = 1;
+}
+
+/* get interface MTU */
+void
+if_get_mtu (struct interface *ifp)
+{
+  struct lifreq lifreq;
+  int ret;
+  u_char changed = 0;
+  
+  if (ifp->flags & IFF_IPV4)
+    {
+      lifreq_set_name (&lifreq, ifp->name);
+      ret = AF_IOCTL (AF_INET, SIOCGLIFMTU, (caddr_t) & lifreq);
+      if (ret < 0)
+        {
+          zlog_info ("Can't lookup mtu on %s by ioctl(SIOCGLIFMTU)",
+                     ifp->name);
+          ifp->mtu = -1;
+        }
+      else
+        {
+          ifp->mtu = lifreq.lifr_metric;
+          changed = 1;
+        }
+    }
+
+#ifdef HAVE_IPV6
+  if (ifp->flags & IFF_IPV6)
+  {
+    memset(&lifreq, 0, sizeof(lifreq));
+    lifreq_set_name (&lifreq, ifp->name);
+
+    ret = AF_IOCTL (AF_INET6, SIOCGLIFMTU, (caddr_t) & lifreq);
+    if (ret < 0)
+    {
+      zlog_info ("Can't lookup mtu6 on %s by ioctl(SIOCGIFMTU)", ifp->name);
+      ifp->mtu6 = -1;
+    }
+    else
+    {
+      ifp->mtu6 = lifreq.lifr_metric;
+      changed = 1;
+    }
+  }
+#endif /* HAVE_IPV6 */
+
+  if (changed)
+    zebra_interface_up_update(ifp);
+}
+
+/* Set up interface's address, netmask (and broadcast? ).
+   Solaris uses ifname:number semantics to set IP address aliases. */
+int
+if_set_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in broad;
+  struct sockaddr_in mask;
+  struct prefix_ipv4 ifaddr;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *) ifc->address;
+
+  ifaddr = *p;
+
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+
+  addr.sin_addr = p->prefix;
+  addr.sin_family = p->family;
+  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
+
+  ret = if_ioctl (SIOCSIFADDR, (caddr_t) & ifreq);
+
+  if (ret < 0)
+    return ret;
+
+  /* We need mask for make broadcast addr. */
+  masklen2ip (p->prefixlen, &mask.sin_addr);
+
+  if (if_is_broadcast (ifp))
+    {
+      apply_mask_ipv4 (&ifaddr);
+      addr.sin_addr = ifaddr.prefix;
+
+      broad.sin_addr.s_addr = (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr);
+      broad.sin_family = p->family;
+
+      memcpy (&ifreq.ifr_broadaddr, &broad, sizeof (struct sockaddr_in));
+      ret = if_ioctl (SIOCSIFBRDADDR, (caddr_t) & ifreq);
+      if (ret < 0)
+        return ret;
+    }
+
+  mask.sin_family = p->family;
+#ifdef SUNOS_5
+  memcpy (&mask, &ifreq.ifr_addr, sizeof (mask));
+#else
+  memcpy (&ifreq.ifr_netmask, &mask, sizeof (struct sockaddr_in));
+#endif /* SUNOS_5 */
+  ret = if_ioctl (SIOCSIFNETMASK, (caddr_t) & ifreq);
+
+  return ((ret < 0) ? ret : 0);
+}
+
+/* Set up interface's address, netmask (and broadcast).
+   Solaris uses ifname:number semantics to set IP address aliases. */
+int
+if_unset_prefix (struct interface *ifp, struct connected *ifc)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct prefix_ipv4 *p;
+
+  p = (struct prefix_ipv4 *) ifc->address;
+
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  addr.sin_family = p->family;
+  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
+
+  ret = if_ioctl (SIOCSIFADDR, (caddr_t) & ifreq);
+  
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+/* Get just the flags for the given name.
+ * Used by the normal 'if_get_flags' function, as well
+ * as the bootup interface-list code, which has to peek at per-address
+ * flags in order to figure out which ones should be ignored..
+ */
+int
+if_get_flags_direct (const char *ifname, uint64_t *flags, unsigned int af)
+{
+  struct lifreq lifreq;
+  int ret;
+    
+  lifreq_set_name (&lifreq, ifname);
+  
+  ret = AF_IOCTL (af, SIOCGLIFFLAGS, (caddr_t) &lifreq);
+  
+  if (ret)
+    zlog_debug ("%s: ifname %s, error %s (%d)",
+                __func__, ifname, safe_strerror (errno), errno);
+  
+  *flags = lifreq.lifr_flags;
+  
+  return ret;
+}
+
+/* get interface flags */
+void
+if_get_flags (struct interface *ifp)
+{
+  int ret4 = 0, ret6 = 0;
+  uint64_t newflags = 0;
+  uint64_t tmpflags;
+
+  if (ifp->flags & IFF_IPV4)
+    {
+      ret4 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET);
+      
+      if (!ret4)
+        newflags |= tmpflags;
+      else if (errno == ENXIO)
+        {
+          /* it's gone */
+          UNSET_FLAG (ifp->flags, IFF_UP);
+          if_flags_update (ifp, ifp->flags);
+        }
+    }
+
+  if (ifp->flags & IFF_IPV6)
+    {
+      ret6 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET6);
+      
+      if (!ret6)
+        newflags |= tmpflags;
+      else if (errno == ENXIO)
+        {
+          /* it's gone */
+          UNSET_FLAG (ifp->flags, IFF_UP);
+          if_flags_update (ifp, ifp->flags);
+        }
+    }
+  
+  /* only update flags if one of above succeeded */
+  if ( !(ret4 && ret6) )
+    if_flags_update (ifp, newflags);
+}
+
+/* Set interface flags */
+int
+if_set_flags (struct interface *ifp, uint64_t flags)
+{
+  int ret;
+  struct lifreq lifreq;
+
+  lifreq_set_name (&lifreq, ifp->name);
+
+  lifreq.lifr_flags = ifp->flags;
+  lifreq.lifr_flags |= flags;
+
+  if (ifp->flags & IFF_IPV4)
+    ret = AF_IOCTL (AF_INET, SIOCSLIFFLAGS, (caddr_t) & lifreq);
+  else if (ifp->flags & IFF_IPV6)
+    ret = AF_IOCTL (AF_INET6, SIOCSLIFFLAGS, (caddr_t) & lifreq);
+  else
+    ret = -1;
+
+  if (ret < 0)
+    zlog_info ("can't set interface flags on %s: %s", ifp->name,
+               safe_strerror (errno));
+  else
+    ret = 0;
+    
+  return ret;
+}
+
+/* Unset interface's flag. */
+int
+if_unset_flags (struct interface *ifp, uint64_t flags)
+{
+  int ret;
+  struct lifreq lifreq;
+
+  lifreq_set_name (&lifreq, ifp->name);
+
+  lifreq.lifr_flags = ifp->flags;
+  lifreq.lifr_flags &= ~flags;
+
+  if (ifp->flags & IFF_IPV4)
+    ret = AF_IOCTL (AF_INET, SIOCSLIFFLAGS, (caddr_t) & lifreq);
+  else if (ifp->flags & IFF_IPV6)
+    ret = AF_IOCTL (AF_INET6, SIOCSLIFFLAGS, (caddr_t) & lifreq);
+  else
+    ret = -1;
+
+  if (ret < 0)
+    zlog_info ("can't unset interface flags");
+  else
+    ret = 0;
+  
+  return ret;
+}
+
+#ifdef HAVE_IPV6
+
+/* Interface's address add/delete functions. */
+int
+if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  char addrbuf[PREFIX_STRLEN];
+
+  zlog_warn ("Can't set %s on interface %s",
+             prefix2str(ifc->address, addrbuf, sizeof(addrbuf)),
+             ifp->name);
+
+  return 0;
+
+}
+
+int
+if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+  char addrbuf[PREFIX_STRLEN];
+
+  zlog_warn ("Can't delete %s on interface %s",
+             prefix2str(ifc->address, addrbuf, sizeof(addrbuf)),
+             ifp->name);
+
+  return 0;
+
+}
+
+#endif /* HAVE_IPV6 */
diff --git a/zebra/ioctl_solaris.h b/zebra/ioctl_solaris.h
new file mode 100644 (file)
index 0000000..188986b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Interface looking up by ioctl () on Solaris.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_IF_IOCTL_SOLARIS_H
+#define _ZEBRA_IF_IOCTL_SOLARIS_H
+
+void lifreq_set_name (struct lifreq *, const char *);
+int if_get_flags_direct (const char *, uint64_t *, unsigned int af);
+
+#endif /* _ZEBRA_IF_IOCTL_SOLARIS_H */
diff --git a/zebra/ipforward.h b/zebra/ipforward.h
new file mode 100644 (file)
index 0000000..8a935c1
--- /dev/null
@@ -0,0 +1,35 @@
+/* IP forward settings.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_IPFORWARD_H
+#define _ZEBRA_IPFORWARD_H
+
+extern int ipforward (void);
+extern int ipforward_on (void);
+extern int ipforward_off (void);
+
+#ifdef HAVE_IPV6
+extern int ipforward_ipv6 (void);
+extern int ipforward_ipv6_on (void);
+extern int ipforward_ipv6_off (void);
+#endif /* HAVE_IPV6 */
+
+#endif /* _ZEBRA_IPFORWARD_H */
diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c
new file mode 100644 (file)
index 0000000..2876ede
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Fetch ipforward value by reading /proc filesystem.
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "privs.h"
+
+#include "zebra/ipforward.h"
+
+extern struct zebra_privs_t zserv_privs;
+
+char proc_net_snmp[] = "/proc/net/snmp";
+
+static void
+dropline (FILE *fp)
+{
+  int c;
+
+  while ((c = getc (fp)) != '\n')
+    ;
+}
+
+int
+ipforward (void)
+{
+  FILE *fp;
+  int ipforwarding = 0;
+  char buf[10];
+
+  fp = fopen (proc_net_snmp, "r");
+
+  if (fp == NULL)
+    return -1;
+
+  /* We don't care about the first line. */
+  dropline (fp);
+  
+  /* Get ip_statistics.IpForwarding : 
+     1 => ip forwarding enabled 
+     2 => ip forwarding off. */
+  if (fgets (buf, 6, fp))
+    sscanf (buf, "Ip: %d", &ipforwarding);
+
+  fclose(fp);
+  
+  if (ipforwarding == 1)
+    return 1;
+
+  return 0;
+}
+
+/* char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/conf/all/forwarding"; */
+char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/ip_forward";
+
+int
+ipforward_on (void)
+{
+  FILE *fp;
+  
+  if ( zserv_privs.change(ZPRIVS_RAISE) )
+       zlog_err ("Can't raise privileges, %s", safe_strerror (errno) );
+
+  fp = fopen (proc_ipv4_forwarding, "w");
+
+  if (fp == NULL) {
+    if ( zserv_privs.change(ZPRIVS_LOWER) )
+      zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+    return -1;
+  }
+
+  fprintf (fp, "1\n");
+
+  fclose (fp);
+
+  if ( zserv_privs.change(ZPRIVS_LOWER) )
+    zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+
+  return ipforward ();
+}
+
+int
+ipforward_off (void)
+{
+  FILE *fp;
+
+  if ( zserv_privs.change(ZPRIVS_RAISE) )
+       zlog_err ("Can't raise privileges, %s", safe_strerror (errno));
+
+  fp = fopen (proc_ipv4_forwarding, "w");
+
+  if (fp == NULL) {
+    if ( zserv_privs.change(ZPRIVS_LOWER) )
+      zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+    return -1;
+  }
+
+  fprintf (fp, "0\n");
+
+  fclose (fp);
+
+  if ( zserv_privs.change(ZPRIVS_LOWER) )
+    zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+
+  return ipforward ();
+}
+#ifdef HAVE_IPV6
+
+char proc_ipv6_forwarding[] = "/proc/sys/net/ipv6/conf/all/forwarding";
+
+int
+ipforward_ipv6 (void)
+{
+  FILE *fp;
+  char buf[5];
+  int ipforwarding = 0;
+
+  fp = fopen (proc_ipv6_forwarding, "r");
+
+  if (fp == NULL)
+    return -1;
+
+  if (fgets (buf, 2, fp))
+    sscanf (buf, "%d", &ipforwarding);
+
+  fclose (fp);
+  return ipforwarding;
+}
+
+int
+ipforward_ipv6_on (void)
+{
+  FILE *fp;
+
+  if ( zserv_privs.change(ZPRIVS_RAISE) )
+       zlog_err ("Can't raise privileges, %s", safe_strerror (errno));
+
+  fp = fopen (proc_ipv6_forwarding, "w");
+
+  if (fp == NULL) {
+    if ( zserv_privs.change(ZPRIVS_LOWER) )
+      zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+    return -1;
+  }
+
+  fprintf (fp, "1\n");
+
+  fclose (fp);
+
+  if ( zserv_privs.change(ZPRIVS_LOWER) )
+    zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+
+  return ipforward_ipv6 ();
+}
+
+int
+ipforward_ipv6_off (void)
+{
+  FILE *fp;
+
+  if ( zserv_privs.change(ZPRIVS_RAISE) )
+       zlog_err ("Can't raise privileges, %s", safe_strerror (errno));
+
+  fp = fopen (proc_ipv6_forwarding, "w");
+
+  if (fp == NULL) {
+    if ( zserv_privs.change(ZPRIVS_LOWER) )
+      zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+    return -1;
+  }
+
+  fprintf (fp, "0\n");
+
+  fclose (fp);
+
+  if ( zserv_privs.change(ZPRIVS_LOWER) )
+    zlog_err ("Can't lower privileges, %s", safe_strerror (errno));
+
+  return ipforward_ipv6 ();
+}
+#endif /* HAVE_IPV6 */
diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c
new file mode 100644 (file)
index 0000000..4aa1b79
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * ipforward value get function for solaris.
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "prefix.h"
+
+#include "privs.h"
+#include "zebra/ipforward.h"
+
+/*
+** Solaris should define IP_DEV_NAME in <inet/ip.h>, but we'll save
+** configure.in changes for another day.  We can use the same device
+** for both IPv4 and IPv6.
+*/
+/* #include <inet/ip.h> */
+#ifndef IP_DEV_NAME
+#define IP_DEV_NAME "/dev/ip"
+#endif
+
+
+extern struct zebra_privs_t zserv_privs;
+
+/* This is a limited ndd style function that operates one integer
+** value only.  Errors return -1. ND_SET commands return 0 on
+** success. ND_GET commands return the value on success (which could
+** be -1 and be confused for an error).  The parameter is the string
+** name of the parameter being referenced.
+*/
+
+static int
+solaris_nd(const int cmd, const char* parameter, const int value)
+{
+#define ND_BUFFER_SIZE 1024
+  int fd;
+  char nd_buf[ND_BUFFER_SIZE];
+  struct strioctl strioctl;
+  const char* device = IP_DEV_NAME;
+  int retval;
+  memset(nd_buf, '\0', ND_BUFFER_SIZE);
+  /*
+  ** ND_SET takes a NULL delimited list of strings further terminated
+  ** buy a NULL.  ND_GET returns a list in a similar layout, although
+  ** here we only use the first result.
+  */
+  if (cmd == ND_SET)
+    snprintf(nd_buf, ND_BUFFER_SIZE, "%s%c%d%c", parameter, '\0', value,'\0');
+  else if (cmd == ND_GET)
+    snprintf(nd_buf, ND_BUFFER_SIZE, "%s", parameter);
+  else {
+    zlog_err("internal error - inappropriate command given to "
+             "solaris_nd()%s:%d", __FILE__, __LINE__);
+    return -1;
+  }
+
+  strioctl.ic_cmd = cmd;
+  strioctl.ic_timout = 0;
+  strioctl.ic_len = ND_BUFFER_SIZE;
+  strioctl.ic_dp = nd_buf;
+  
+  if ( zserv_privs.change (ZPRIVS_RAISE) )
+       zlog_err ("solaris_nd: Can't raise privileges");
+  if ((fd = open (device, O_RDWR)) < 0) 
+    {
+      zlog_warn("failed to open device %s - %s", device, safe_strerror(errno));
+      if ( zserv_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("solaris_nd: Can't lower privileges");
+      return -1;
+    }
+  if (ioctl (fd, I_STR, &strioctl) < 0) 
+    {
+      int save_errno = errno;
+      if ( zserv_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("solaris_nd: Can't lower privileges");
+      close (fd);
+      zlog_warn("ioctl I_STR failed on device %s - %s",
+               device, safe_strerror(save_errno));
+      return -1;
+    }
+  close(fd);
+  if ( zserv_privs.change (ZPRIVS_LOWER) )
+         zlog_err ("solaris_nd: Can't lower privileges");
+  
+  if (cmd == ND_GET) 
+    {
+      errno = 0;
+      retval = atoi(nd_buf);
+      if (errno) 
+        {
+          zlog_warn("failed to convert returned value to integer - %s",
+                    safe_strerror(errno));
+          retval = -1;
+        }
+    } 
+  else 
+    {
+      retval = 0;
+    }
+  return retval;
+}
+
+static int
+solaris_nd_set(const char* parameter, const int value) {
+  return solaris_nd(ND_SET, parameter, value);
+}
+static int
+solaris_nd_get(const char* parameter) {
+  return solaris_nd(ND_GET, parameter, 0);
+}
+int
+ipforward(void)
+{
+  return solaris_nd_get("ip_forwarding");
+}
+
+int
+ipforward_on (void)
+{
+  (void) solaris_nd_set("ip_forwarding", 1);
+  return ipforward();
+}
+
+int
+ipforward_off (void)
+{
+  (void) solaris_nd_set("ip_forwarding", 0);
+  return ipforward();
+}
+#ifdef HAVE_IPV6
+int ipforward_ipv6(void)
+{
+  return solaris_nd_get("ip6_forwarding");
+}
+int
+ipforward_ipv6_on (void)
+{
+  (void) solaris_nd_set("ip6_forwarding", 1);
+  return ipforward_ipv6();
+}
+int
+ipforward_ipv6_off (void)
+{
+  (void) solaris_nd_set("ip6_forwarding", 0);
+  return ipforward_ipv6();
+}
+#endif /* HAVE_IPV6 */
diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c
new file mode 100644 (file)
index 0000000..57ed185
--- /dev/null
@@ -0,0 +1,176 @@
+/* IP forward control by sysctl function.
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "privs.h"
+#include "zebra/ipforward.h"
+
+#include "log.h"
+
+#define MIB_SIZ 4
+
+extern struct zebra_privs_t zserv_privs;
+
+/* IPv4 forwarding control MIB. */
+int mib[MIB_SIZ] =
+{
+  CTL_NET,
+  PF_INET,
+  IPPROTO_IP,
+  IPCTL_FORWARDING
+};
+
+int
+ipforward (void)
+{
+  size_t len;
+  int ipforwarding = 0;
+
+  len = sizeof ipforwarding;
+  if (sysctl (mib, MIB_SIZ, &ipforwarding, &len, 0, 0) < 0) 
+    {
+      zlog_warn ("Can't get ipforwarding value");
+      return -1;
+    }
+  return ipforwarding;
+}
+
+int
+ipforward_on (void)
+{
+  size_t len;
+  int ipforwarding = 1;
+
+  len = sizeof ipforwarding;
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0)
+    {
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_warn ("Can't set ipforwarding on");
+      return -1;
+    }
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  return ipforwarding;
+}
+
+int
+ipforward_off (void)
+{
+  size_t len;
+  int ipforwarding = 0;
+
+  len = sizeof ipforwarding;
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0)
+    {
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_warn ("Can't set ipforwarding on");
+      return -1;
+    }
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  return ipforwarding;
+}
+
+#ifdef HAVE_IPV6
+
+/* IPv6 forwarding control MIB. */
+int mib_ipv6[MIB_SIZ] = 
+{
+  CTL_NET,
+  PF_INET6,
+#if defined(KAME)
+  IPPROTO_IPV6,
+  IPV6CTL_FORWARDING
+#else /* NOT KAME */
+  IPPROTO_IP,
+  IP6CTL_FORWARDING
+#endif /* KAME */
+}; 
+
+int
+ipforward_ipv6 (void)
+{
+  size_t len;
+  int ip6forwarding = 0;
+
+  len = sizeof ip6forwarding;
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  if (sysctl (mib_ipv6, MIB_SIZ, &ip6forwarding, &len, 0, 0) < 0)
+    {
+     if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_warn ("can't get ip6forwarding value");
+      return -1;
+    }
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  return ip6forwarding;
+}
+
+int
+ipforward_ipv6_on (void)
+{
+  size_t len;
+  int ip6forwarding = 1;
+
+  len = sizeof ip6forwarding;
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0)
+    {
+     if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_warn ("can't get ip6forwarding value");
+      return -1;
+    }
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  return ip6forwarding;
+}
+
+int
+ipforward_ipv6_off (void)
+{
+  size_t len;
+  int ip6forwarding = 0;
+
+  len = sizeof ip6forwarding;
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0)
+    {
+      if (zserv_privs.change(ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
+      zlog_warn ("can't get ip6forwarding value");
+      return -1;
+    }
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+  return ip6forwarding;
+}
+#endif /* HAVE_IPV6 */
diff --git a/zebra/irdp.h b/zebra/irdp.h
new file mode 100644 (file)
index 0000000..9ce55e5
--- /dev/null
@@ -0,0 +1,157 @@
+/* ICMP Router Discovery Messages
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* 
+ * This file is modified and completed for the Zebra IRDP implementation
+ * by Robert Olsson, Swedish University of Agricultural Sciences
+ */
+
+#ifndef _IRDP_H
+#define _IRDP_H
+
+#include "lib/vty.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/* ICMP Messages */
+#ifndef ICMP_ROUTERADVERT
+#define ICMP_ROUTERADVERT 9
+#endif /* ICMP_ROUTERADVERT */
+
+#ifndef ICMP_ROUTERSOLICIT
+#define ICMP_ROUTERSOLICIT 10
+#endif /* ICMP_ROUTERSOLICT */
+
+/* Multicast groups */
+#ifndef INADDR_ALLHOSTS_GROUP
+#define INADDR_ALLHOSTS_GROUP 0xe0000001U    /* 224.0.0.1 */
+#endif /* INADDR_ALLHOSTS_GROUP */
+
+#ifndef INADDR_ALLRTRS_GROUP
+#define INADDR_ALLRTRS_GROUP  0xe0000002U    /* 224.0.0.2 */
+#endif /* INADDR_ALLRTRS_GROUP */
+
+/* Default irdp packet interval */
+#define IRDP_DEFAULT_INTERVAL 300 
+
+/* Router constants from RFC1256 */
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTISEMENTS   3
+#define MAX_RESPONSE_DELAY           2
+
+#define IRDP_MAXADVERTINTERVAL 600
+#define IRDP_MINADVERTINTERVAL 450 /* 0.75*600 */
+#define IRDP_LIFETIME         1350 /* 3*450 */
+#define IRDP_PREFERENCE 0
+
+#define ICMP_MINLEN 8
+
+#define IRDP_LAST_ADVERT_MESSAGES 2 /* The last adverts with Holdtime 0 */
+
+#define IRDP_RX_BUF 1500
+
+/* 
+     Comments comes from RFC1256 ICMP Router Discovery Messages. 
+
+     The IP destination address to be used for multicast Router
+     Advertisements sent from the interface.  The only permissible
+     values are the all-systems multicast address, 224.0.0.1, or the
+     limited-broadcast address, 255.255.255.255.  (The all-systems
+     address is preferred wherever possible, i.e., on any link where
+     all listening hosts support IP multicast.)
+
+     Default: 224.0.0.1 if the router supports IP multicast on the
+     interface, else 255.255.255.255 
+
+     The maximum time allowed between sending multicast Router
+     Advertisements from the interface, in seconds.  Must be no less
+     than 4 seconds and no greater than 1800 seconds.
+
+     Default: 600 seconds 
+
+     The minimum time allowed between sending unsolicited multicast
+     Router Advertisements from the interface, in seconds.  Must be no
+     less than 3 seconds and no greater than MaxAdvertisementInterval.
+
+     Default: 0.75 * MaxAdvertisementInterval 
+
+     The value to be placed in the Lifetime field of Router
+     Advertisements sent from the interface, in seconds.  Must be no
+     less than MaxAdvertisementInterval and no greater than 9000
+     seconds.
+
+     Default: 3 * MaxAdvertisementInterval 
+
+     The preferability of the address as a default router address,
+     relative to other router addresses on the same subnet.  A 32-bit,
+     signed, twos-complement integer, with higher values meaning more
+     preferable.  The minimum value (hex 80000000) is used to indicate
+     that the address, even though it may be advertised, is not to be
+     used by neighboring hosts as a default router address.
+
+     Default: 0 
+*/
+
+struct irdp_interface 
+{
+  unsigned long MaxAdvertInterval;
+  unsigned long MinAdvertInterval;
+  unsigned long Preference;
+
+  u_int32_t flags;
+
+#define IF_ACTIVE               (1<<0) /* ICMP Active */
+#define IF_BROADCAST            (1<<1) /* 255.255.255.255 */
+#define IF_SOLICIT              (1<<2) /* Solicit active */
+#define IF_DEBUG_MESSAGES       (1<<3) 
+#define IF_DEBUG_PACKET         (1<<4) 
+#define IF_DEBUG_MISC           (1<<5) 
+#define IF_SHUTDOWN             (1<<6) 
+
+  struct interface *ifp;
+  struct thread *t_advertise;
+  unsigned long irdp_sent;
+  u_int16_t Lifetime;
+
+ struct list *AdvPrefList;
+
+};
+
+struct Adv 
+{
+  struct in_addr ip;
+  int pref;
+};
+
+extern void irdp_init(void);
+extern int irdp_sock_init(void);
+extern void irdp_finish(void);
+extern void irdp_config_write (struct vty *, struct interface *);
+extern int irdp_send_thread(struct thread *t_advert);
+extern void irdp_advert_off(struct interface *ifp);
+extern void process_solicit (struct interface *ifp);
+extern int irdp_read_raw(struct thread *r);
+extern void send_packet(struct interface *ifp, struct stream *s,
+                       u_int32_t dst, struct prefix *p, u_int32_t ttl);
+
+
+#endif /* _IRDP_H */
diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c
new file mode 100644 (file)
index 0000000..43c63a8
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ *
+ * Copyright (C) 2000  Robert Olsson.
+ * Swedish University of Agricultural Sciences
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* 
+ * This work includes work with the following copywrite:
+ *
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ */
+
+/* 
+ * Thanks to Jens Låås at Swedish University of Agricultural Sciences
+ * for reviewing and tests.
+ */
+
+
+#include <zebra.h>
+
+#ifdef HAVE_IRDP 
+
+#include "if.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "stream.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "log.h"
+#include "zclient.h"
+#include "thread.h"
+#include "zebra/interface.h"
+#include "zebra/rtadv.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/irdp.h"
+#include <netinet/ip_icmp.h>
+#include "if.h"
+#include "sockunion.h"
+#include "log.h"
+
+
+/* Master of threads. */
+extern struct zebra_t zebrad;
+
+extern int irdp_sock;
+
+static const char *
+inet_2a(u_int32_t a, char *b)
+{
+  sprintf(b, "%u.%u.%u.%u",
+          (a    ) & 0xFF,
+          (a>> 8) & 0xFF,
+          (a>>16) & 0xFF,
+          (a>>24) & 0xFF);
+  return  b;
+}
+
+
+static struct prefix *
+irdp_get_prefix(struct interface *ifp)
+{
+  struct listnode *node;
+  struct connected *ifc;
+  
+  if (ifp->connected)
+    for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc))
+      return ifc->address;
+
+  return NULL;
+}
+
+/* Join to the add/leave multicast group. */
+static int
+if_group (struct interface *ifp, 
+         int sock, 
+         u_int32_t group, 
+         int add_leave)
+{
+  struct ip_mreq m;
+  struct prefix *p;
+  int ret;
+  char b1[INET_ADDRSTRLEN];
+
+  memset (&m, 0, sizeof (m));
+  m.imr_multiaddr.s_addr = htonl (group);
+  p = irdp_get_prefix(ifp);
+
+  if(!p) {
+        zlog_warn ("IRDP: can't get address for %s", ifp->name);
+       return 1;
+  }
+
+  m.imr_interface = p->u.prefix4;
+
+  ret = setsockopt (sock, IPPROTO_IP, add_leave,
+                   (char *) &m, sizeof (struct ip_mreq));
+  if (ret < 0)
+    zlog_warn ("IRDP: %s can't setsockopt %s: %s",
+              add_leave == IP_ADD_MEMBERSHIP? "join group":"leave group", 
+              inet_2a(group, b1),
+              safe_strerror (errno));
+
+  return ret;
+}
+
+static int
+if_add_group (struct interface *ifp)
+{
+  struct zebra_if *zi= ifp->info;
+  struct irdp_interface *irdp = &zi->irdp;
+  int ret;
+  char b1[INET_ADDRSTRLEN];
+
+  ret = if_group (ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_ADD_MEMBERSHIP);
+  if (ret < 0) {
+    return ret;
+  }
+
+  if(irdp->flags & IF_DEBUG_MISC )
+    zlog_debug("IRDP: Adding group %s for %s", 
+              inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1),
+              ifp->name);
+  return 0;
+}
+
+static int
+if_drop_group (struct interface *ifp)
+{
+  struct zebra_if *zi= ifp->info;
+  struct irdp_interface *irdp = &zi->irdp;
+  int ret;
+  char b1[INET_ADDRSTRLEN];
+
+  ret = if_group (ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_DROP_MEMBERSHIP);
+  if (ret < 0)
+    return ret;
+
+  if(irdp->flags & IF_DEBUG_MISC)
+    zlog_debug("IRDP: Leaving group %s for %s", 
+              inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1),
+              ifp->name);
+  return 0;
+}
+
+static void
+if_set_defaults(struct interface *ifp)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+
+  irdp->MaxAdvertInterval = IRDP_MAXADVERTINTERVAL;
+  irdp->MinAdvertInterval = IRDP_MINADVERTINTERVAL;
+  irdp->Preference = IRDP_PREFERENCE;
+  irdp->Lifetime = IRDP_LIFETIME;
+}
+
+
+static struct Adv *Adv_new (void)
+{
+  return XCALLOC (MTYPE_TMP, sizeof (struct Adv));
+}
+
+static void
+Adv_free (struct Adv *adv)
+{
+  XFREE (MTYPE_TMP, adv);
+}
+
+static void
+irdp_if_start(struct interface *ifp, int multicast, int set_defaults)
+{
+  struct zebra_if *zi= ifp->info;
+  struct irdp_interface *irdp = &zi->irdp;
+  struct listnode *node;
+  struct connected *ifc;
+  u_int32_t timer, seed;
+
+  if (irdp->flags & IF_ACTIVE ) {
+    zlog_warn("IRDP: Interface is already active %s", ifp->name);
+    return;
+  }
+  if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) {
+    zlog_warn("IRDP: Cannot activate interface %s (cannot create "
+             "IRDP socket)", ifp->name);
+    return;
+  }
+  irdp->flags |= IF_ACTIVE;
+
+  if(!multicast) 
+    irdp->flags |= IF_BROADCAST;
+
+  if_add_update(ifp);
+
+  if (! (ifp->flags & IFF_UP)) {
+    zlog_warn("IRDP: Interface is down %s", ifp->name);
+  }
+
+  /* Shall we cancel if_start if if_add_group fails? */
+
+  if( multicast) {
+    if_add_group(ifp);
+    
+    if (! (ifp->flags & (IFF_MULTICAST|IFF_ALLMULTI))) {
+      zlog_warn("IRDP: Interface not multicast enabled %s", ifp->name);
+    }
+  }
+
+  if(set_defaults) 
+    if_set_defaults(ifp);
+
+  irdp->irdp_sent = 0;
+
+  /* The spec suggests this for randomness */
+
+  seed = 0;
+  if( ifp->connected)
+    for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc))
+      {
+        seed = ifc->address->u.prefix4.s_addr;
+        break;
+      }
+  
+  srandom(seed);
+  timer =  (random () % IRDP_DEFAULT_INTERVAL) + 1; 
+
+  irdp->AdvPrefList = list_new();
+  irdp->AdvPrefList->del =  (void (*)(void *)) Adv_free; /* Destructor */
+
+
+  /* And this for startup. Speed limit from 1991 :-). But it's OK*/
+
+  if(irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS &&
+     timer > MAX_INITIAL_ADVERT_INTERVAL ) 
+         timer= MAX_INITIAL_ADVERT_INTERVAL;
+
+  
+  if(irdp->flags & IF_DEBUG_MISC)
+    zlog_debug("IRDP: Init timer for %s set to %u", 
+              ifp->name, 
+              timer);
+
+  irdp->t_advertise = thread_add_timer(zebrad.master, 
+                                      irdp_send_thread, 
+                                      ifp, 
+                                      timer);
+}
+
+static void
+irdp_if_stop(struct interface *ifp)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  
+  if (irdp == NULL) {
+    zlog_warn ("Interface %s structure is NULL", ifp->name);
+    return;
+  }
+
+  if (! (irdp->flags & IF_ACTIVE )) {
+    zlog_warn("Interface is not active %s", ifp->name);
+    return;
+  }
+
+  if(! (irdp->flags & IF_BROADCAST)) 
+    if_drop_group(ifp);
+
+  irdp_advert_off(ifp);
+
+  list_delete(irdp->AdvPrefList);
+  irdp->AdvPrefList=NULL;
+
+  irdp->flags = 0;
+}
+
+
+static void
+irdp_if_shutdown(struct interface *ifp)
+{
+  struct zebra_if *zi= ifp->info;
+  struct irdp_interface *irdp = &zi->irdp;
+
+  if (irdp->flags & IF_SHUTDOWN ) {
+    zlog_warn("IRDP: Interface is already shutdown %s", ifp->name);
+    return;
+  }
+
+  irdp->flags |= IF_SHUTDOWN;
+  irdp->flags &= ~IF_ACTIVE;
+
+  if(! (irdp->flags & IF_BROADCAST)) 
+    if_drop_group(ifp);
+  
+  /* Tell the hosts we are out of service */
+  irdp_advert_off(ifp);
+}
+
+static void
+irdp_if_no_shutdown(struct interface *ifp)
+{
+  struct zebra_if *zi= ifp->info;
+  struct irdp_interface *irdp = &zi->irdp;
+
+  if (! (irdp->flags & IF_SHUTDOWN )) {
+    zlog_warn("IRDP: Interface is not shutdown %s", ifp->name);
+    return;
+  }
+
+  irdp->flags &= ~IF_SHUTDOWN;
+
+  irdp_if_start(ifp, irdp->flags & IF_BROADCAST? FALSE : TRUE, FALSE); 
+
+}
+
+
+/* Write configuration to user */
+
+void irdp_config_write (struct vty *vty, struct interface *ifp)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  struct Adv *adv;
+  struct listnode *node;
+  char b1[INET_ADDRSTRLEN];
+
+  if(irdp->flags & IF_ACTIVE || irdp->flags & IF_SHUTDOWN) {
+
+    if( irdp->flags & IF_SHUTDOWN) 
+      vty_out (vty, " ip irdp shutdown %s",  VTY_NEWLINE);
+
+    if( irdp->flags & IF_BROADCAST) 
+      vty_out (vty, " ip irdp broadcast%s",  VTY_NEWLINE);
+    else 
+      vty_out (vty, " ip irdp multicast%s",  VTY_NEWLINE);
+
+    vty_out (vty, " ip irdp preference %ld%s",  
+            irdp->Preference, VTY_NEWLINE);
+
+    for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv))
+      vty_out (vty, " ip irdp address %s preference %d%s",
+                    inet_2a(adv->ip.s_addr, b1),
+                    adv->pref, 
+                    VTY_NEWLINE);
+
+    vty_out (vty, " ip irdp holdtime %d%s",  
+            irdp->Lifetime, VTY_NEWLINE);
+
+    vty_out (vty, " ip irdp minadvertinterval %ld%s",  
+            irdp->MinAdvertInterval, VTY_NEWLINE);
+
+    vty_out (vty, " ip irdp maxadvertinterval %ld%s",  
+            irdp->MaxAdvertInterval, VTY_NEWLINE);
+
+  }
+}
+
+
+DEFUN (ip_irdp_multicast,
+       ip_irdp_multicast_cmd,
+       "ip irdp multicast",
+       IP_STR
+       "ICMP Router discovery on this interface using multicast\n")
+{
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  irdp_if_start(ifp, TRUE, TRUE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_broadcast,
+       ip_irdp_broadcast_cmd,
+       "ip irdp broadcast",
+       IP_STR
+       "ICMP Router discovery on this interface using broadcast\n")
+{
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  irdp_if_start(ifp, FALSE, TRUE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_irdp,
+       no_ip_irdp_cmd,
+       "no ip irdp",
+       NO_STR
+       IP_STR
+       "Disable ICMP Router discovery on this interface\n")
+{
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  irdp_if_stop(ifp);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_shutdown,
+       ip_irdp_shutdown_cmd,
+       "ip irdp shutdown",
+       IP_STR
+       "ICMP Router discovery shutdown on this interface\n")
+{
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  irdp_if_shutdown(ifp);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_irdp_shutdown,
+       no_ip_irdp_shutdown_cmd,
+       "no ip irdp shutdown",
+       NO_STR
+       IP_STR
+       "ICMP Router discovery no shutdown on this interface\n")
+{
+  struct interface *ifp;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  irdp_if_no_shutdown(ifp);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_holdtime,
+       ip_irdp_holdtime_cmd,
+       "ip irdp holdtime <0-9000>",
+       IP_STR
+       "ICMP Router discovery on this interface\n"
+       "Set holdtime value\n"
+       "Holdtime value in seconds. Default is 1800 seconds\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->Lifetime = atoi(argv[0]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_minadvertinterval,
+       ip_irdp_minadvertinterval_cmd,
+       "ip irdp minadvertinterval <3-1800>",
+       IP_STR
+       "ICMP Router discovery on this interface\n"
+       "Set minimum time between advertisement\n"
+       "Minimum advertisement interval in seconds\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  if( (unsigned) atoi(argv[0]) <= irdp->MaxAdvertInterval) {
+      irdp->MinAdvertInterval = atoi(argv[0]);
+
+      return CMD_SUCCESS;
+  }
+
+  vty_out (vty, "ICMP warning maxadvertinterval is greater or equal than minadvertinterval%s", 
+            VTY_NEWLINE);
+
+  vty_out (vty, "Please correct!%s", 
+            VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+DEFUN (ip_irdp_maxadvertinterval,
+       ip_irdp_maxadvertinterval_cmd,
+       "ip irdp maxadvertinterval <4-1800>",
+       IP_STR
+       "ICMP Router discovery on this interface\n"
+       "Set maximum time between advertisement\n"
+       "Maximum advertisement interval in seconds\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+
+  if( irdp->MinAdvertInterval <= (unsigned) atoi(argv[0]) ) {
+    irdp->MaxAdvertInterval = atoi(argv[0]);
+
+      return CMD_SUCCESS;
+  }
+
+  vty_out (vty, "ICMP warning maxadvertinterval is greater or equal than minadvertinterval%s", 
+            VTY_NEWLINE);
+
+  vty_out (vty, "Please correct!%s", 
+            VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+/* DEFUN needs to be fixed for negative ranages...
+ * "ip irdp preference <-2147483648-2147483647>",
+ * Be positive for now. :-)
+ */
+
+DEFUN (ip_irdp_preference,
+       ip_irdp_preference_cmd,
+       "ip irdp preference <0-2147483647>",
+       IP_STR
+       "ICMP Router discovery on this interface\n"
+       "Set default preference level for this interface\n"
+       "Preference level\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->Preference = atoi(argv[0]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_address_preference,
+       ip_irdp_address_preference_cmd,
+       "ip irdp address A.B.C.D preference <0-2147483647>",
+       IP_STR
+       "Alter ICMP Router discovery preference this interface\n"
+       "Specify IRDP non-default preference to advertise\n"
+       "Set IRDP address for advertise\n"
+       "Preference level\n")
+{
+  struct listnode *node;
+  struct in_addr ip; 
+  int pref;
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  struct Adv *adv;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  ret = inet_aton(argv[0], &ip);
+  if(!ret) return CMD_WARNING;
+
+  pref = atoi(argv[1]);
+
+  for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv))
+    if(adv->ip.s_addr == ip.s_addr) 
+      return CMD_SUCCESS;
+
+  adv = Adv_new();
+  adv->ip = ip;
+  adv->pref = pref;
+  listnode_add(irdp->AdvPrefList, adv);
+
+  return CMD_SUCCESS;
+
+}
+
+DEFUN (no_ip_irdp_address_preference,
+       no_ip_irdp_address_preference_cmd,
+       "no ip irdp address A.B.C.D preference <0-2147483647>",
+       NO_STR
+       IP_STR
+       "Alter ICMP Router discovery preference this interface\n"
+       "Removes IRDP non-default preference\n"
+       "Select IRDP address\n"
+       "Old preference level\n")
+{
+  struct listnode *node, *nnode;
+  struct in_addr ip; 
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  struct Adv *adv;
+
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  ret = inet_aton(argv[0], &ip);
+  if (!ret) 
+    return CMD_WARNING;
+
+  for (ALL_LIST_ELEMENTS (irdp->AdvPrefList, node, nnode, adv))
+    {
+      if(adv->ip.s_addr == ip.s_addr )
+        {
+          listnode_delete(irdp->AdvPrefList, adv);
+          break;
+        }
+    }
+  
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_debug_messages,
+       ip_irdp_debug_messages_cmd,
+       "ip irdp debug messages",
+       IP_STR
+       "ICMP Router discovery debug Averts. and Solicits (short)\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->flags |= IF_DEBUG_MESSAGES;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_debug_misc,
+       ip_irdp_debug_misc_cmd,
+       "ip irdp debug misc",
+       IP_STR
+       "ICMP Router discovery debug Averts. and Solicits (short)\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->flags |= IF_DEBUG_MISC;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_irdp_debug_packet,
+       ip_irdp_debug_packet_cmd,
+       "ip irdp debug packet",
+       IP_STR
+       "ICMP Router discovery debug Averts. and Solicits (short)\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->flags |= IF_DEBUG_PACKET;
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (ip_irdp_debug_disable,
+       ip_irdp_debug_disable_cmd,
+       "ip irdp debug disable",
+       IP_STR
+       "ICMP Router discovery debug Averts. and Solicits (short)\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  ifp = (struct interface *) vty->index;
+  if(!ifp) {
+         return CMD_WARNING;
+  }
+
+  zi=ifp->info;
+  irdp=&zi->irdp;
+
+  irdp->flags &= ~IF_DEBUG_PACKET;
+  irdp->flags &= ~IF_DEBUG_MESSAGES;
+  irdp->flags &= ~IF_DEBUG_MISC;
+
+  return CMD_SUCCESS;
+}
+
+void
+irdp_init ()
+{
+  install_element (INTERFACE_NODE, &ip_irdp_broadcast_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_multicast_cmd);
+  install_element (INTERFACE_NODE, &no_ip_irdp_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_shutdown_cmd);
+  install_element (INTERFACE_NODE, &no_ip_irdp_shutdown_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_holdtime_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_maxadvertinterval_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_minadvertinterval_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_preference_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_address_preference_cmd);
+  install_element (INTERFACE_NODE, &no_ip_irdp_address_preference_cmd);
+
+  install_element (INTERFACE_NODE, &ip_irdp_debug_messages_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_debug_misc_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_debug_packet_cmd);
+  install_element (INTERFACE_NODE, &ip_irdp_debug_disable_cmd);
+}
+
+#endif /* HAVE_IRDP */
diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c
new file mode 100644 (file)
index 0000000..cf78a54
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ *
+ * Copyright (C) 2000  Robert Olsson.
+ * Swedish University of Agricultural Sciences
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* 
+ * This work includes work with the following copywrite:
+ *
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ */
+
+/* 
+ * Thanks to Jens Låås at Swedish University of Agricultural Sciences
+ * for reviewing and tests.
+ */
+
+
+#include <zebra.h>
+
+#ifdef HAVE_IRDP
+
+#include "if.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "stream.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "log.h"
+#include "zclient.h"
+#include "thread.h"
+#include "privs.h"
+#include "zebra/interface.h"
+#include "zebra/rtadv.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/irdp.h"
+#include <netinet/ip_icmp.h>
+
+#include "checksum.h"
+#include "if.h"
+#include "sockunion.h"
+#include "log.h"
+
+/* GLOBAL VARS */
+
+extern struct zebra_privs_t zserv_privs;
+
+/* Master of threads. */
+extern struct zebra_t zebrad;
+struct thread *t_irdp_raw;
+
+/* Timer interval of irdp. */
+int irdp_timer_interval = IRDP_DEFAULT_INTERVAL;
+
+int
+irdp_sock_init (void)
+{
+  int ret, i;
+  int save_errno;
+  int sock;
+
+  if ( zserv_privs.change (ZPRIVS_RAISE) )
+       zlog_err ("irdp_sock_init: could not raise privs, %s",
+                  safe_strerror (errno) );
+
+  sock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  save_errno = errno;
+
+  if ( zserv_privs.change (ZPRIVS_LOWER) )
+       zlog_err ("irdp_sock_init: could not lower privs, %s",
+             safe_strerror (errno) );
+
+  if (sock < 0) {
+    zlog_warn ("IRDP: can't create irdp socket %s", safe_strerror(save_errno));
+    return sock;
+  };
+  
+  i = 1;
+  ret = setsockopt (sock, IPPROTO_IP, IP_TTL, 
+                        (void *) &i, sizeof (i));
+  if (ret < 0) {
+    zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno));
+    close(sock);
+    return ret;
+  };
+  
+  ret = setsockopt_ifindex (AF_INET, sock, 1);
+  if (ret < 0) {
+    zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno));
+    close(sock);
+    return ret;
+  };
+
+  t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, sock); 
+
+  return sock;
+}
+
+
+static int
+get_pref(struct irdp_interface *irdp, struct prefix *p)
+{
+  struct listnode *node;
+  struct Adv *adv;
+
+  /* Use default preference or use the override pref */
+  
+  if( irdp->AdvPrefList == NULL )
+    return irdp->Preference;
+  
+  for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv))
+    if( p->u.prefix4.s_addr == adv->ip.s_addr )
+      return adv->pref;
+
+  return irdp->Preference;
+}
+
+/* Make ICMP Router Advertisement Message. */
+static int
+make_advertisement_packet (struct interface *ifp, 
+                          struct prefix *p,
+                          struct stream *s)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  int size;
+  int pref;
+  u_int16_t checksum;
+
+  pref =  get_pref(irdp, p);
+
+  stream_putc (s, ICMP_ROUTERADVERT); /* Type. */
+  stream_putc (s, 0);          /* Code. */
+  stream_putw (s, 0);          /* Checksum. */
+  stream_putc (s, 1);          /* Num address. */
+  stream_putc (s, 2);          /* Address Entry Size. */
+
+  if(irdp->flags & IF_SHUTDOWN)  
+    stream_putw (s, 0);
+  else 
+    stream_putw (s, irdp->Lifetime);
+
+  stream_putl (s, htonl(p->u.prefix4.s_addr)); /* Router address. */
+  stream_putl (s, pref);
+
+  /* in_cksum return network byte order value */
+  size = 16;
+  checksum = in_cksum (s->data, size);
+  stream_putw_at (s, 2, htons(checksum));
+
+  return size;
+}
+
+static void
+irdp_send(struct interface *ifp, struct prefix *p, struct stream *s)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  char buf[PREFIX_STRLEN];
+  u_int32_t dst;
+  u_int32_t ttl=1;
+
+  if (! (ifp->flags & IFF_UP)) return; 
+
+  if (irdp->flags & IF_BROADCAST) 
+    dst =INADDR_BROADCAST ;
+  else 
+    dst = htonl(INADDR_ALLHOSTS_GROUP);
+
+  if(irdp->flags & IF_DEBUG_MESSAGES) 
+    zlog_debug("IRDP: TX Advert on %s %s Holdtime=%d Preference=%d",
+             ifp->name,
+             prefix2str(p, buf, sizeof buf),
+             irdp->flags & IF_SHUTDOWN? 0 : irdp->Lifetime,
+             get_pref(irdp, p));
+
+  send_packet (ifp, s, dst, p, ttl);
+}
+
+static void irdp_advertisement (struct interface *ifp, struct prefix *p)
+{
+  struct stream *s;
+  s = stream_new (128);
+  make_advertisement_packet (ifp, p, s);
+  irdp_send(ifp, p, s);
+  stream_free (s);
+}
+
+int irdp_send_thread(struct thread *t_advert)
+{
+  u_int32_t timer, tmp;
+  struct interface *ifp = THREAD_ARG (t_advert);
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  struct prefix *p;
+  struct listnode *node, *nnode;
+  struct connected *ifc;
+
+  irdp->flags &= ~IF_SOLICIT;
+
+  if(ifp->connected) 
+    for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc))
+      {
+        p = ifc->address;
+        
+        if (p->family != AF_INET)
+          continue;
+        
+        irdp_advertisement(ifp, p);
+        irdp->irdp_sent++;
+      }
+
+  tmp = irdp->MaxAdvertInterval-irdp->MinAdvertInterval;
+  timer =  (random () % tmp ) + 1;
+  timer = irdp->MinAdvertInterval + timer;
+
+  if(irdp->irdp_sent <  MAX_INITIAL_ADVERTISEMENTS &&
+     timer > MAX_INITIAL_ADVERT_INTERVAL ) 
+         timer= MAX_INITIAL_ADVERT_INTERVAL;
+
+  if(irdp->flags & IF_DEBUG_MISC)
+    zlog_debug("IRDP: New timer for %s set to %u\n", ifp->name, timer);
+
+  irdp->t_advertise = thread_add_timer(zebrad.master, irdp_send_thread, ifp, timer);
+  return 0;
+}
+
+void irdp_advert_off(struct interface *ifp)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  struct listnode *node, *nnode;
+  int i;
+  struct connected *ifc;
+  struct prefix *p;
+
+  if(irdp->t_advertise)  thread_cancel(irdp->t_advertise);
+  irdp->t_advertise = NULL;
+  
+  if(ifp->connected) 
+    for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc))
+      {
+        p = ifc->address;
+
+        /* Output some packets with Lifetime 0 
+           we should add a wait...
+        */
+
+        for(i=0; i< IRDP_LAST_ADVERT_MESSAGES; i++) 
+          {
+            irdp->irdp_sent++;
+            irdp_advertisement(ifp, p);
+          }
+      }
+}
+
+
+void process_solicit (struct interface *ifp)
+{
+  struct zebra_if *zi=ifp->info;
+  struct irdp_interface *irdp=&zi->irdp;
+  u_int32_t timer;
+
+  /* When SOLICIT is active we reject further incoming solicits 
+     this keeps down the answering rate so we don't have think
+     about DoS attacks here. */
+
+  if( irdp->flags & IF_SOLICIT) return;
+
+  irdp->flags |= IF_SOLICIT;
+  if(irdp->t_advertise)  thread_cancel(irdp->t_advertise);
+  irdp->t_advertise = NULL;
+
+  timer =  (random () % MAX_RESPONSE_DELAY) + 1;
+
+  irdp->t_advertise = thread_add_timer(zebrad.master, 
+                                      irdp_send_thread, 
+                                      ifp, 
+                                      timer);
+}
+
+void irdp_finish()
+{
+
+  struct listnode *node, *nnode;
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+
+  zlog_info("IRDP: Received shutdown notification.");
+  
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    {
+      zi = ifp->info;
+      
+      if (!zi) 
+        continue;
+      irdp = &zi->irdp;
+      if (!irdp) 
+        continue;
+
+      if (irdp->flags & IF_ACTIVE ) 
+        {
+         irdp->flags |= IF_SHUTDOWN;
+         irdp_advert_off(ifp);
+        }
+    }
+}
+
+#endif /* HAVE_IRDP */
diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c
new file mode 100644 (file)
index 0000000..0d31050
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ *
+ * Copyright (C) 2000  Robert Olsson.
+ * Swedish University of Agricultural Sciences
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* 
+ * This work includes work with the following copywrite:
+ *
+ * Copyright (C) 1997, 2000 Kunihiro Ishiguro
+ *
+ */
+
+/* 
+ * Thanks to Jens Låås at Swedish University of Agricultural Sciences
+ * for reviewing and tests.
+ */
+
+
+#include <zebra.h>
+
+
+#ifdef HAVE_IRDP
+
+#include "if.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "stream.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "log.h"
+#include "zclient.h"
+#include "thread.h"
+#include "zebra/interface.h"
+#include "zebra/rtadv.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/irdp.h"
+#include <netinet/ip_icmp.h>
+#include "if.h"
+#include "checksum.h"
+#include "sockunion.h"
+#include "log.h"
+#include "sockopt.h"
+
+
+/* GLOBAL VARS */
+
+int irdp_sock = -1;
+
+extern struct zebra_t zebrad;
+extern struct thread *t_irdp_raw;
+
+static void
+parse_irdp_packet(char *p, 
+                 int len, 
+                 struct interface *ifp)
+{
+  struct ip *ip = (struct ip *)p ;
+  struct icmphdr *icmp;
+  struct in_addr src;
+  int ip_hlen, iplen, datalen;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+
+  zi = ifp->info;
+  if (!zi) 
+    return;
+
+  irdp = &zi->irdp;
+  if (!irdp) 
+    return;
+
+  ip_hlen = ip->ip_hl << 2;
+  
+  sockopt_iphdrincl_swab_systoh (ip);
+  
+  iplen = ip->ip_len;
+  datalen = len - ip_hlen;
+  src = ip->ip_src;
+
+  if (len != iplen)
+    {
+      zlog_err ("IRDP: RX length doesnt match IP length");
+      return;
+    }
+
+  if (iplen < ICMP_MINLEN) 
+    {
+      zlog_err ("IRDP: RX ICMP packet too short from %s\n",
+             inet_ntoa (src));
+      return;
+    }
+    
+  /* XXX: RAW doesnt receive link-layer, surely? ??? */
+  /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen +
+   len of IP-header) 14+20 */
+  if (iplen > IRDP_RX_BUF-34) 
+    {
+      zlog_err ("IRDP: RX ICMP packet too long from %s\n",
+               inet_ntoa (src));
+      return;
+    }
+
+  icmp = (struct icmphdr *) (p+ip_hlen);
+
+  /* check icmp checksum */    
+  if (in_cksum (icmp, datalen) != icmp->checksum) 
+    {
+      zlog_warn ("IRDP: RX ICMP packet from %s. Bad checksum, silently ignored",
+                 inet_ntoa (src));
+      return;
+    }
+  
+  /* Handle just only IRDP */
+  if (!(icmp->type == ICMP_ROUTERADVERT
+        || icmp->type == ICMP_ROUTERSOLICIT))
+    return;
+  
+  if (icmp->code != 0) 
+    {
+      zlog_warn ("IRDP: RX packet type %d from %s. Bad ICMP type code,"
+                 " silently ignored",
+                 icmp->type, inet_ntoa (src));
+      return;
+    }
+
+  if (! ((ntohl (ip->ip_dst.s_addr) == INADDR_BROADCAST)
+         && (irdp->flags & IF_BROADCAST))
+        ||
+        (ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP
+         && !(irdp->flags & IF_BROADCAST)))
+    {
+      zlog_warn ("IRDP: RX illegal from %s to %s while %s operates in %s\n",
+                 inet_ntoa (src),
+                 ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP ?
+                 "multicast" : inet_ntoa (ip->ip_dst),
+                 ifp->name,
+                 irdp->flags & IF_BROADCAST ? "broadcast" : "multicast");
+
+      zlog_warn ("IRDP: Please correct settings\n");
+      return;
+    }
+
+  switch (icmp->type) 
+    {
+    case ICMP_ROUTERADVERT:
+      break;
+
+    case ICMP_ROUTERSOLICIT:
+
+      if(irdp->flags & IF_DEBUG_MESSAGES) 
+       zlog_debug ("IRDP: RX Solicit on %s from %s\n",
+                   ifp->name,
+                   inet_ntoa (src));
+
+      process_solicit(ifp);
+      break;
+
+    default:
+      zlog_warn ("IRDP: RX type %d from %s. Bad ICMP type, silently ignored",
+                icmp->type,
+                inet_ntoa (src));
+    }
+}
+
+static int
+irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex)
+{
+  struct msghdr msg;
+  struct iovec iov;
+  char adata[CMSG_SPACE( SOPT_SIZE_CMSG_PKTINFO_IPV4() )];
+  int ret;
+
+  msg.msg_name = (void *)0;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = sizeof adata;
+
+  iov.iov_base = buf;
+  iov.iov_len = size;
+
+  ret = recvmsg (sock, &msg, 0);
+  if (ret < 0) {
+    zlog_warn("IRDP: recvmsg: read error %s", safe_strerror(errno));
+    return ret;
+  }
+
+  if (msg.msg_flags & MSG_TRUNC) {
+    zlog_warn("IRDP: recvmsg: truncated message");
+    return ret;
+  }
+  if (msg.msg_flags & MSG_CTRUNC) {
+    zlog_warn("IRDP: recvmsg: truncated control message");
+    return ret;
+  }
+
+  *ifindex = getsockopt_ifindex (AF_INET, &msg);
+
+  return ret;
+}
+
+int irdp_read_raw(struct thread *r)
+{
+  struct interface *ifp;
+  struct zebra_if *zi;
+  struct irdp_interface *irdp;
+  char buf[IRDP_RX_BUF];
+  int ret, ifindex = 0;
+  
+  int irdp_sock = THREAD_FD (r);
+  t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, irdp_sock);
+  
+  ret = irdp_recvmsg (irdp_sock, (u_char *) buf, IRDP_RX_BUF,  &ifindex);
+  if (ret < 0) zlog_warn ("IRDP: RX Error length = %d", ret);
+
+  ifp = if_lookup_by_index(ifindex);
+  if(! ifp ) return ret;
+
+  zi= ifp->info;
+  if(! zi ) return ret;
+
+  irdp = &zi->irdp;
+  if(! irdp ) return ret;
+
+  if(! (irdp->flags & IF_ACTIVE)) {
+
+    if(irdp->flags & IF_DEBUG_MISC) 
+      zlog_debug("IRDP: RX ICMP for disabled interface %s\n", ifp->name);
+    return 0;
+  }
+
+  if(irdp->flags & IF_DEBUG_PACKET) {
+    int i;
+    zlog_debug("IRDP: RX (idx %d) ", ifindex);
+    for(i=0; i < ret; i++) zlog_debug( "IRDP: RX %x ", buf[i]&0xFF);
+  }
+
+  parse_irdp_packet(buf, ret, ifp);
+
+  return ret;
+}
+
+void 
+send_packet(struct interface *ifp, 
+           struct stream *s,
+           u_int32_t dst,
+           struct prefix *p,
+           u_int32_t ttl)
+{
+  static struct sockaddr_in sockdst = {AF_INET};
+  struct ip *ip;
+  struct icmphdr *icmp;
+  struct msghdr *msg;
+  struct cmsghdr *cmsg;
+  struct iovec iovector;
+  char msgbuf[256];
+  char buf[256];
+  struct in_pktinfo *pktinfo;
+  u_long src;
+  int on;
+  if (!(ifp->flags & IFF_UP))
+    return;
+
+  if (p)
+    src = ntohl(p->u.prefix4.s_addr);
+  else 
+    src = 0; /* Is filled in */
+  
+  ip = (struct ip *) buf;
+  ip->ip_hl = sizeof(struct ip) >> 2;
+  ip->ip_v = IPVERSION;
+  ip->ip_tos = 0xC0;
+  ip->ip_off = 0L;
+  ip->ip_p = 1;       /* IP_ICMP */
+  ip->ip_ttl = ttl;
+  ip->ip_src.s_addr = src;
+  ip->ip_dst.s_addr = dst;
+  icmp = (struct icmphdr *) (buf + sizeof (struct ip));
+
+  /* Merge IP header with icmp packet */
+  assert (stream_get_endp(s) < (sizeof (buf) - sizeof (struct ip)));
+  stream_get(icmp, s, stream_get_endp(s));
+
+  /* icmp->checksum is already calculated */
+  ip->ip_len  = sizeof(struct ip) + stream_get_endp(s);
+
+  on = 1;
+  if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL,
+                (char *) &on, sizeof(on)) < 0)
+    zlog_warn("sendto %s", safe_strerror (errno));
+
+
+  if(dst == INADDR_BROADCAST ) {
+    on = 1;
+    if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST,
+                  (char *) &on, sizeof(on)) < 0)
+      zlog_warn("sendto %s", safe_strerror (errno));
+  }
+
+  if(dst !=  INADDR_BROADCAST) {
+      on = 0; 
+      if( setsockopt(irdp_sock,IPPROTO_IP, IP_MULTICAST_LOOP, 
+                    (char *)&on,sizeof(on)) < 0)
+       zlog_warn("sendto %s", safe_strerror (errno));
+  }
+
+  memset(&sockdst,0,sizeof(sockdst));
+  sockdst.sin_family=AF_INET;
+  sockdst.sin_addr.s_addr = dst;
+
+  cmsg = (struct cmsghdr *) (msgbuf + sizeof(struct msghdr));
+  cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo);
+  cmsg->cmsg_level = SOL_IP;
+  cmsg->cmsg_type = IP_PKTINFO;
+  pktinfo = (struct in_pktinfo *) CMSG_DATA(cmsg);
+  pktinfo->ipi_ifindex = ifp->ifindex;
+  pktinfo->ipi_spec_dst.s_addr = src;
+  pktinfo->ipi_addr.s_addr = src;
+  iovector.iov_base = (void *) buf;
+  iovector.iov_len = ip->ip_len; 
+  msg = (struct msghdr *) msgbuf;
+  msg->msg_name = &sockdst;
+  msg->msg_namelen = sizeof(sockdst);
+  msg->msg_iov = &iovector;
+  msg->msg_iovlen = 1;
+  msg->msg_control = cmsg;
+  msg->msg_controllen = cmsg->cmsg_len;
+  sockopt_iphdrincl_swab_htosys (ip);
+  
+  if (sendmsg(irdp_sock, msg, 0) < 0) {
+    zlog_warn("sendto %s", safe_strerror (errno));
+  }
+  /*   printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */
+}
+
+
+#endif /* HAVE_IRDP */
+
+
+
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
new file mode 100644 (file)
index 0000000..23b2153
--- /dev/null
@@ -0,0 +1,20 @@
+/* Kernel communication using netlink interface.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c
new file mode 100644 (file)
index 0000000..1a16a75
--- /dev/null
@@ -0,0 +1,62 @@
+/* NULL kernel methods for testing. */
+
+/* 
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <log.h>
+
+#include "zebra/zserv.h"
+#include "zebra/rt.h"
+#include "zebra/redistribute.h"
+#include "zebra/connected.h"
+#include "zebra/rib.h"
+
+int kernel_route_rib (struct prefix *a, struct rib *old, struct rib *new) { return 0; }
+
+int kernel_add_route (struct prefix_ipv4 *a, struct in_addr *b, int c, int d)
+{ return 0; }
+
+int kernel_address_add_ipv4 (struct interface *a, struct connected *b)
+{
+  zlog_debug ("%s", __func__);
+  SET_FLAG (b->conf, ZEBRA_IFC_REAL);
+  connected_add_ipv4 (a, 0, &b->address->u.prefix4, b->address->prefixlen, 
+                      (b->destination ? &b->destination->u.prefix4 : NULL), 
+                      NULL);
+  return 0;
+}
+
+int kernel_address_delete_ipv4 (struct interface *a, struct connected *b)
+{
+  zlog_debug ("%s", __func__);
+  connected_delete_ipv4 (a, 0, &b->address->u.prefix4, b->address->prefixlen, 
+                         (b->destination ? &b->destination->u.prefix4 : NULL));
+  return 0;
+}
+
+void kernel_init (struct zebra_vrf *zvrf) { return; }
+void kernel_terminate (struct zebra_vrf *zvrf) { return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak route_read = kernel_init
+#else
+void route_read (struct zebra_vrf *zvrf) { return; }
+#endif
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
new file mode 100644 (file)
index 0000000..64c6cbb
--- /dev/null
@@ -0,0 +1,1367 @@
+/* Kernel communication using routing socket.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <net/if_types.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "sockunion.h"
+#include "connected.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "log.h"
+#include "str.h"
+#include "table.h"
+#include "rib.h"
+#include "privs.h"
+#include "vrf.h"
+
+#include "zebra/interface.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/kernel_socket.h"
+#include "zebra/rib.h"
+
+extern struct zebra_privs_t zserv_privs;
+extern struct zebra_t zebrad;
+
+/*
+ * Historically, the BSD routing socket has aligned data following a
+ * struct sockaddr to sizeof(long), which was 4 bytes on some
+ * platforms, and 8 bytes on others.  NetBSD 6 changed the routing
+ * socket to align to sizeof(uint64_t), which is 8 bytes.  OS X
+ * appears to align to sizeof(int), which is 4 bytes.
+ *
+ * Alignment of zero-sized sockaddrs is nonsensical, but historically
+ * BSD defines RT_ROUNDUP(0) to be the alignment interval (rather than
+ * 0).  We follow this practice without questioning it, but it is a
+ * bug if quagga calls ROUNDUP with 0.
+ */
+
+/*
+ * Because of these varying conventions, the only sane approach is for
+ * the <net/route.h> header to define some flavor of ROUNDUP macro.
+ */
+
+#if defined(SA_SIZE)
+/* SAROUNDUP is the only thing we need, and SA_SIZE provides that */
+#define SAROUNDUP(a)   SA_SIZE(a)
+#else /* !SA_SIZE */
+
+#if defined(RT_ROUNDUP)
+#define ROUNDUP(a)     RT_ROUNDUP(a)
+#endif /* defined(RT_ROUNDUP) */
+
+#if defined(SUNOS_5)
+/* Solaris has struct sockaddr_in[6] definitions at 16 / 32 bytes size,
+ * so the whole concept doesn't really apply. */
+#define ROUNDUP(a)      (a)
+#endif
+
+/*
+ * If ROUNDUP has not yet been defined in terms of platform-provided
+ * defines, attempt to cope with heuristics.
+ */
+#if !defined(ROUNDUP)
+
+/*
+ * It's a bug for a platform not to define rounding/alignment for
+ * sockaddrs on the routing socket.  This warning really is
+ * intentional, to provoke filing bug reports with operating systems
+ * that don't define RT_ROUNDUP or equivalent.
+ */
+#warning "net/route.h does not define RT_ROUNDUP; making unwarranted assumptions!"
+
+/* OS X (Xcode as of 2014-12) is known not to define RT_ROUNDUP */
+#ifdef __APPLE__
+#define ROUNDUP_TYPE   int
+#else
+#define ROUNDUP_TYPE   long
+#endif
+
+#define ROUNDUP(a) \
+  ((a) > 0 ? (1 + (((a) - 1) | (sizeof(ROUNDUP_TYPE) - 1))) : sizeof(ROUNDUP_TYPE))
+
+#endif /* defined(ROUNDUP) */
+
+/*
+ * Given a pointer (sockaddr or void *), return the number of bytes
+ * taken up by the sockaddr and any padding needed for alignment.
+ */
+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
+#define SAROUNDUP(X)   ROUNDUP(((struct sockaddr *)(X))->sa_len)
+#elif defined(HAVE_IPV6)
+/*
+ * One would hope all fixed-size structure definitions are aligned,
+ * but round them up nonetheless.
+ */
+#define SAROUNDUP(X) \
+    (((struct sockaddr *)(X))->sa_family == AF_INET ?   \
+      ROUNDUP(sizeof(struct sockaddr_in)):\
+      (((struct sockaddr *)(X))->sa_family == AF_INET6 ? \
+       ROUNDUP(sizeof(struct sockaddr_in6)) :  \
+       (((struct sockaddr *)(X))->sa_family == AF_LINK ? \
+         ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr))))
+#else /* HAVE_IPV6 */ 
+#define SAROUNDUP(X) \
+      (((struct sockaddr *)(X))->sa_family == AF_INET ?   \
+        ROUNDUP(sizeof(struct sockaddr_in)):\
+         (((struct sockaddr *)(X))->sa_family == AF_LINK ? \
+           ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr)))
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+#endif /* !SA_SIZE */
+
+/*
+ * We use a call to an inline function to copy (PNT) to (DEST)
+ * 1. Calculating the length of the copy requires an #ifdef to determine
+ *    if sa_len is a field and can't be used directly inside a #define
+ * 2. So the compiler doesn't complain when DEST is NULL, which is only true
+ *    when we are skipping the copy and incrementing to the next SA
+ */
+static inline void
+rta_copy (union sockunion *dest, caddr_t src) {
+  int len;
+  if (!dest)
+    return;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+  len = (((struct sockaddr *)src)->sa_len > sizeof (*dest)) ?
+            sizeof (*dest) : ((struct sockaddr *)src)->sa_len ;
+#else
+  len = (SAROUNDUP (src) > sizeof (*dest)) ?
+            sizeof (*dest) : SAROUNDUP (src) ;
+#endif
+  memcpy (dest, src, len);
+}
+
+#define RTA_ADDR_GET(DEST, RTA, RTMADDRS, PNT) \
+  if ((RTMADDRS) & (RTA)) \
+    { \
+      int len = SAROUNDUP ((PNT)); \
+      if (af_check (((struct sockaddr *)(PNT))->sa_family)) \
+        rta_copy((DEST), (PNT)); \
+      (PNT) += len; \
+    }
+#define RTA_ATTR_GET(DEST, RTA, RTMADDRS, PNT) \
+  if ((RTMADDRS) & (RTA)) \
+    { \
+      int len = SAROUNDUP ((PNT)); \
+      rta_copy((DEST), (PNT)); \
+      (PNT) += len; \
+    }
+
+#define RTA_NAME_GET(DEST, RTA, RTMADDRS, PNT, LEN) \
+  if ((RTMADDRS) & (RTA)) \
+    { \
+      u_char *pdest = (u_char *) (DEST); \
+      int len = SAROUNDUP ((PNT)); \
+      struct sockaddr_dl *sdl = (struct sockaddr_dl *)(PNT); \
+      if (IS_ZEBRA_DEBUG_KERNEL) \
+        zlog_debug ("%s: RTA_SDL_GET nlen %d, alen %d", \
+                    __func__, sdl->sdl_nlen, sdl->sdl_alen); \
+      if ( ((DEST) != NULL) && (sdl->sdl_family == AF_LINK) \
+           && (sdl->sdl_nlen < IFNAMSIZ) && (sdl->sdl_nlen <= len) ) \
+        { \
+          memcpy (pdest, sdl->sdl_data, sdl->sdl_nlen); \
+          pdest[sdl->sdl_nlen] = '\0'; \
+          (LEN) = sdl->sdl_nlen; \
+        } \
+      (PNT) += len; \
+    } \
+  else \
+    { \
+      (LEN) = 0; \
+    }
+/* Routing socket message types. */
+const struct message rtm_type_str[] =
+{
+  {RTM_ADD,      "RTM_ADD"},
+  {RTM_DELETE,   "RTM_DELETE"},
+  {RTM_CHANGE,   "RTM_CHANGE"},
+  {RTM_GET,      "RTM_GET"},
+  {RTM_LOSING,   "RTM_LOSING"},
+  {RTM_REDIRECT, "RTM_REDIRECT"},
+  {RTM_MISS,     "RTM_MISS"},
+  {RTM_LOCK,     "RTM_LOCK"},
+#ifdef OLDADD
+  {RTM_OLDADD,   "RTM_OLDADD"},
+#endif /* RTM_OLDADD */
+#ifdef RTM_OLDDEL
+  {RTM_OLDDEL,   "RTM_OLDDEL"},
+#endif /* RTM_OLDDEL */
+  {RTM_RESOLVE,  "RTM_RESOLVE"},
+  {RTM_NEWADDR,  "RTM_NEWADDR"},
+  {RTM_DELADDR,  "RTM_DELADDR"},
+  {RTM_IFINFO,   "RTM_IFINFO"},
+#ifdef RTM_OIFINFO
+  {RTM_OIFINFO,   "RTM_OIFINFO"},
+#endif /* RTM_OIFINFO */
+#ifdef RTM_NEWMADDR
+  {RTM_NEWMADDR, "RTM_NEWMADDR"},
+#endif /* RTM_NEWMADDR */
+#ifdef RTM_DELMADDR
+  {RTM_DELMADDR, "RTM_DELMADDR"},
+#endif /* RTM_DELMADDR */
+#ifdef RTM_IFANNOUNCE
+  {RTM_IFANNOUNCE, "RTM_IFANNOUNCE"},
+#endif /* RTM_IFANNOUNCE */
+  {0,            NULL}
+};
+
+static const struct message rtm_flag_str[] =
+{
+  {RTF_UP,        "UP"},
+  {RTF_GATEWAY,   "GATEWAY"},
+  {RTF_HOST,      "HOST"},
+  {RTF_REJECT,    "REJECT"},
+  {RTF_DYNAMIC,   "DYNAMIC"},
+  {RTF_MODIFIED,  "MODIFIED"},
+  {RTF_DONE,      "DONE"},
+#ifdef RTF_MASK
+  {RTF_MASK,      "MASK"},
+#endif /* RTF_MASK */
+#ifdef RTF_CLONING
+  {RTF_CLONING,   "CLONING"},
+#endif /* RTF_CLONING */
+#ifdef RTF_XRESOLVE
+  {RTF_XRESOLVE,  "XRESOLVE"},
+#endif /* RTF_XRESOLVE */
+#ifdef RTF_LLINFO
+  {RTF_LLINFO,    "LLINFO"},
+#endif /* RTF_LLINFO */
+  {RTF_STATIC,    "STATIC"},
+  {RTF_BLACKHOLE, "BLACKHOLE"},
+#ifdef RTF_PRIVATE
+  {RTF_PRIVATE,          "PRIVATE"},
+#endif /* RTF_PRIVATE */
+  {RTF_PROTO1,    "PROTO1"},
+  {RTF_PROTO2,    "PROTO2"},
+#ifdef RTF_PRCLONING
+  {RTF_PRCLONING, "PRCLONING"},
+#endif /* RTF_PRCLONING */
+#ifdef RTF_WASCLONED
+  {RTF_WASCLONED, "WASCLONED"},
+#endif /* RTF_WASCLONED */
+#ifdef RTF_PROTO3
+  {RTF_PROTO3,    "PROTO3"},
+#endif /* RTF_PROTO3 */
+#ifdef RTF_PINNED
+  {RTF_PINNED,    "PINNED"},
+#endif /* RTF_PINNED */
+#ifdef RTF_LOCAL
+  {RTF_LOCAL,    "LOCAL"},
+#endif /* RTF_LOCAL */
+#ifdef RTF_BROADCAST
+  {RTF_BROADCAST, "BROADCAST"},
+#endif /* RTF_BROADCAST */
+#ifdef RTF_MULTICAST
+  {RTF_MULTICAST, "MULTICAST"},
+#endif /* RTF_MULTICAST */
+#ifdef RTF_MULTIRT
+  {RTF_MULTIRT,   "MULTIRT"},
+#endif /* RTF_MULTIRT */
+#ifdef RTF_SETSRC
+  {RTF_SETSRC,    "SETSRC"},
+#endif /* RTF_SETSRC */
+  {0,             NULL}
+};
+
+/* Kernel routing update socket. */
+int routing_sock = -1;
+
+/* Yes I'm checking ugly routing socket behavior. */
+/* #define DEBUG */
+
+/* Supported address family check. */
+static inline int
+af_check (int family)
+{
+  if (family == AF_INET)
+    return 1;
+#ifdef HAVE_IPV6
+  if (family == AF_INET6)
+    return 1;
+#endif /* HAVE_IPV6 */
+  return 0;
+}
+
+/* Dump routing table flag for debug purpose. */
+static void
+rtm_flag_dump (int flag)
+{
+  const struct message *mes;
+  static char buf[BUFSIZ];
+
+  buf[0] = '\0';
+  for (mes = rtm_flag_str; mes->key != 0; mes++)
+    {
+      if (mes->key & flag)
+       {
+         strlcat (buf, mes->str, BUFSIZ);
+         strlcat (buf, " ", BUFSIZ);
+       }
+    }
+  zlog_debug ("Kernel: %s", buf);
+}
+
+#ifdef RTM_IFANNOUNCE
+/* Interface adding function */
+static int
+ifan_read (struct if_announcemsghdr *ifan)
+{
+  struct interface *ifp;
+  
+  ifp = if_lookup_by_index (ifan->ifan_index);
+  
+  if (ifp)
+    assert ( (ifp->ifindex == ifan->ifan_index) 
+             || (ifp->ifindex == IFINDEX_INTERNAL) );
+
+  if ( (ifp == NULL) 
+      || ((ifp->ifindex == IFINDEX_INTERNAL)
+          && (ifan->ifan_what == IFAN_ARRIVAL)) )
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: creating interface for ifindex %d, name %s",
+                    __func__, ifan->ifan_index, ifan->ifan_name);
+      
+      /* Create Interface */
+      ifp = if_get_by_name_len(ifan->ifan_name,
+                              strnlen(ifan->ifan_name,
+                                      sizeof(ifan->ifan_name)));
+      ifp->ifindex = ifan->ifan_index;
+
+      if_get_metric (ifp);
+      if_add_update (ifp);
+    }
+  else if (ifp != NULL && ifan->ifan_what == IFAN_DEPARTURE)
+    if_delete_update (ifp);
+
+  if_get_flags (ifp);
+  if_get_mtu (ifp);
+  if_get_metric (ifp);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s: interface %s index %d", 
+                __func__, ifan->ifan_name, ifan->ifan_index);
+
+  return 0;
+}
+#endif /* RTM_IFANNOUNCE */
+
+#ifdef HAVE_BSD_IFI_LINK_STATE
+/* BSD link detect translation */
+static void
+bsd_linkdetect_translate (struct if_msghdr *ifm)
+{
+  if ((ifm->ifm_data.ifi_link_state >= LINK_STATE_UP) ||
+      (ifm->ifm_data.ifi_link_state == LINK_STATE_UNKNOWN))
+    SET_FLAG(ifm->ifm_flags, IFF_RUNNING);
+  else
+    UNSET_FLAG(ifm->ifm_flags, IFF_RUNNING);
+}
+#endif /* HAVE_BSD_IFI_LINK_STATE */
+
+static enum zebra_link_type
+sdl_to_zebra_link_type (unsigned int sdlt)
+{
+  switch (sdlt)
+  {
+    case IFT_ETHER: return ZEBRA_LLT_ETHER;
+    case IFT_X25: return ZEBRA_LLT_X25;
+    case IFT_FDDI: return ZEBRA_LLT_FDDI;
+    case IFT_PPP: return ZEBRA_LLT_PPP;
+    case IFT_LOOP: return ZEBRA_LLT_LOOPBACK;
+    case IFT_SLIP: return ZEBRA_LLT_SLIP;
+    case IFT_ARCNET: return ZEBRA_LLT_ARCNET;
+    case IFT_ATM: return ZEBRA_LLT_ATM;
+    case IFT_LOCALTALK: return ZEBRA_LLT_LOCALTLK;
+    case IFT_HIPPI: return ZEBRA_LLT_HIPPI;
+#ifdef IFT_IEEE1394
+    case IFT_IEEE1394: return ZEBRA_LLT_IEEE1394;
+#endif
+
+    default: return ZEBRA_LLT_UNKNOWN;
+  }
+}
+
+/*
+ * Handle struct if_msghdr obtained from reading routing socket or
+ * sysctl (from interface_list).  There may or may not be sockaddrs
+ * present after the header.
+ */
+int
+ifm_read (struct if_msghdr *ifm)
+{
+  struct interface *ifp = NULL;
+  struct sockaddr_dl *sdl;
+  char ifname[IFNAMSIZ];
+  short ifnlen = 0;
+  caddr_t cp;
+  
+  /* terminate ifname at head (for strnlen) and tail (for safety) */
+  ifname[IFNAMSIZ - 1] = '\0';
+  
+  /* paranoia: sanity check structure */
+  if (ifm->ifm_msglen < sizeof(struct if_msghdr))
+    {
+      zlog_err ("ifm_read: ifm->ifm_msglen %d too short\n",
+               ifm->ifm_msglen);
+      return -1;
+    }
+
+  /*
+   * Check for a sockaddr_dl following the message.  First, point to
+   * where a socakddr might be if one follows the message.
+   */
+  cp = (void *)(ifm + 1);
+
+#ifdef SUNOS_5
+  /* 
+   * XXX This behavior should be narrowed to only the kernel versions
+   * for which the structures returned do not match the headers.
+   *
+   * if_msghdr_t on 64 bit kernels in Solaris 9 and earlier versions
+   * is 12 bytes larger than the 32 bit version.
+   */
+  if (((struct sockaddr *) cp)->sa_family == AF_UNSPEC)
+       cp = cp + 12;
+#endif
+
+  RTA_ADDR_GET (NULL, RTA_DST, ifm->ifm_addrs, cp);
+  RTA_ADDR_GET (NULL, RTA_GATEWAY, ifm->ifm_addrs, cp);
+  RTA_ATTR_GET (NULL, RTA_NETMASK, ifm->ifm_addrs, cp);
+  RTA_ADDR_GET (NULL, RTA_GENMASK, ifm->ifm_addrs, cp);
+  sdl = (struct sockaddr_dl *)cp;
+  RTA_NAME_GET (ifname, RTA_IFP, ifm->ifm_addrs, cp, ifnlen);
+  RTA_ADDR_GET (NULL, RTA_IFA, ifm->ifm_addrs, cp);
+  RTA_ADDR_GET (NULL, RTA_AUTHOR, ifm->ifm_addrs, cp);
+  RTA_ADDR_GET (NULL, RTA_BRD, ifm->ifm_addrs, cp);
+  
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s: sdl ifname %s", __func__, (ifnlen ? ifname : "(nil)"));
+  
+  /* 
+   * Look up on ifindex first, because ifindices are the primary handle for
+   * interfaces across the user/kernel boundary, for most systems.  (Some
+   * messages, such as up/down status changes on NetBSD, do not include a
+   * sockaddr_dl).
+   */
+  if ( (ifp = if_lookup_by_index (ifm->ifm_index)) != NULL )
+    {
+      /* we have an ifp, verify that the name matches as some systems,
+       * eg Solaris, have a 1:many association of ifindex:ifname
+       * if they dont match, we dont have the correct ifp and should
+       * set it back to NULL to let next check do lookup by name
+       */
+      if (ifnlen && (strncmp (ifp->name, ifname, IFNAMSIZ) != 0) )
+        {
+          if (IS_ZEBRA_DEBUG_KERNEL)
+            zlog_debug ("%s: ifp name %s doesnt match sdl name %s",
+                        __func__, ifp->name, ifname);
+          ifp = NULL;
+        }
+    }
+  
+  /* 
+   * If we dont have an ifp, try looking up by name.  Particularly as some
+   * systems (Solaris) have a 1:many mapping of ifindex:ifname - the ifname
+   * is therefore our unique handle to that interface.
+   *
+   * Interfaces specified in the configuration file for which the ifindex
+   * has not been determined will have ifindex == IFINDEX_INTERNAL, and such
+   * interfaces are found by this search, and then their ifindex values can
+   * be filled in.
+   */
+  if ( (ifp == NULL) && ifnlen)
+    ifp = if_lookup_by_name (ifname);
+
+  /*
+   * If ifp still does not exist or has an invalid index (IFINDEX_INTERNAL),
+   * create or fill in an interface.
+   */
+  if ((ifp == NULL) || (ifp->ifindex == IFINDEX_INTERNAL))
+    {
+      /*
+       * To create or fill in an interface, a sockaddr_dl (via
+       * RTA_IFP) is required.
+       */
+      if (!ifnlen)
+       {
+         zlog_warn ("Interface index %d (new) missing ifname\n",
+                    ifm->ifm_index);
+         return -1;
+       }
+
+#ifndef RTM_IFANNOUNCE
+      /* Down->Down interface should be ignored here.
+       * See further comment below.
+       */
+      if (!CHECK_FLAG (ifm->ifm_flags, IFF_UP))
+        return 0;
+#endif /* !RTM_IFANNOUNCE */
+      
+      if (ifp == NULL)
+        {
+         /* Interface that zebra was not previously aware of, so create. */ 
+         ifp = if_create (ifname, ifnlen);
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           zlog_debug ("%s: creating ifp for ifindex %d", 
+                       __func__, ifm->ifm_index);
+        }
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: updated/created ifp, ifname %s, ifindex %d",
+                    __func__, ifp->name, ifp->ifindex);
+      /* 
+       * Fill in newly created interface structure, or larval
+       * structure with ifindex IFINDEX_INTERNAL.
+       */
+      ifp->ifindex = ifm->ifm_index;
+      
+#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */
+      bsd_linkdetect_translate(ifm);
+#endif /* HAVE_BSD_IFI_LINK_STATE */
+
+      if_flags_update (ifp, ifm->ifm_flags);
+#if defined(__bsdi__)
+      if_kvm_get_mtu (ifp);
+#else
+      if_get_mtu (ifp);
+#endif /* __bsdi__ */
+      if_get_metric (ifp);
+
+      /*
+       * XXX sockaddr_dl contents can be larger than the structure
+       * definition.  There are 2 big families here:
+       *  - BSD has sdl_len + sdl_data[16] + overruns sdl_data
+       *    we MUST use sdl_len here or we'll truncate data.
+       *  - Solaris has no sdl_len, but sdl_data[244]
+       *    presumably, it's not going to run past that, so sizeof()
+       *    is fine here.
+       * a nonzero ifnlen from RTA_NAME_GET() means sdl is valid
+       */
+      ifp->ll_type = ZEBRA_LLT_UNKNOWN;
+      ifp->hw_addr_len = 0;
+      if (ifnlen)
+        {
+#ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN
+          memcpy (&((struct zebra_if *)ifp->info)->sdl, sdl, sdl->sdl_len);
+#else
+          memcpy (&((struct zebra_if *)ifp->info)->sdl, sdl, sizeof (struct sockaddr_dl));
+#endif /* HAVE_STRUCT_SOCKADDR_DL_SDL_LEN */
+
+          ifp->ll_type = sdl_to_zebra_link_type (sdl->sdl_type);
+          if (sdl->sdl_alen <= sizeof(ifp->hw_addr))
+            {
+              memcpy (ifp->hw_addr, LLADDR(sdl), sdl->sdl_alen);
+              ifp->hw_addr_len = sdl->sdl_alen;
+            }
+        }
+
+      if_add_update (ifp);
+    }
+  else
+    /*
+     * Interface structure exists.  Adjust stored flags from
+     * notification.  If interface has up->down or down->up
+     * transition, call state change routines (to adjust routes,
+     * notify routing daemons, etc.).  (Other flag changes are stored
+     * but apparently do not trigger action.)
+     */
+    {
+      if (ifp->ifindex != ifm->ifm_index)
+        {
+          zlog_warn ("%s: index mismatch, ifname %s, ifp index %d, "
+                     "ifm index %d", 
+                     __func__, ifp->name, ifp->ifindex, ifm->ifm_index);
+          return -1;
+        }
+      
+#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */
+      bsd_linkdetect_translate(ifm);
+#endif /* HAVE_BSD_IFI_LINK_STATE */
+
+      /* update flags and handle operative->inoperative transition, if any */
+      if_flags_update (ifp, ifm->ifm_flags);
+      
+#ifndef RTM_IFANNOUNCE
+      if (!if_is_up (ifp))
+          {
+            /* No RTM_IFANNOUNCE on this platform, so we can never
+             * distinguish between ~IFF_UP and delete. We must presume
+             * it has been deleted.
+             * Eg, Solaris will not notify us of unplumb.
+             *
+             * XXX: Fixme - this should be runtime detected
+             * So that a binary compiled on a system with IFANNOUNCE
+             * will still behave correctly if run on a platform without
+             */
+            if_delete_update (ifp);
+          }
+#endif /* RTM_IFANNOUNCE */
+      if (if_is_up (ifp))
+      {
+#if defined(__bsdi__)
+        if_kvm_get_mtu (ifp);
+#else
+        if_get_mtu (ifp);
+#endif /* __bsdi__ */
+        if_get_metric (ifp);
+      }
+    }
+
+#ifdef HAVE_NET_RT_IFLIST
+  ifp->stats = ifm->ifm_data;
+#endif /* HAVE_NET_RT_IFLIST */
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s: interface %s index %d", 
+                __func__, ifp->name, ifp->ifindex);
+
+  return 0;
+}
+
+/* Address read from struct ifa_msghdr. */
+static void
+ifam_read_mesg (struct ifa_msghdr *ifm,
+               union sockunion *addr,
+               union sockunion *mask,
+               union sockunion *brd,
+               char *ifname,
+               short *ifnlen)
+{
+  caddr_t pnt, end;
+  union sockunion dst;
+  union sockunion gateway;
+
+  pnt = (caddr_t)(ifm + 1);
+  end = ((caddr_t)ifm) + ifm->ifam_msglen;
+
+  /* Be sure structure is cleared */
+  memset (mask, 0, sizeof (union sockunion));
+  memset (addr, 0, sizeof (union sockunion));
+  memset (brd, 0, sizeof (union sockunion));
+  memset (&dst, 0, sizeof (union sockunion));
+  memset (&gateway, 0, sizeof (union sockunion));
+
+  /* We fetch each socket variable into sockunion. */
+  RTA_ADDR_GET (&dst, RTA_DST, ifm->ifam_addrs, pnt);
+  RTA_ADDR_GET (&gateway, RTA_GATEWAY, ifm->ifam_addrs, pnt);
+  RTA_ATTR_GET (mask, RTA_NETMASK, ifm->ifam_addrs, pnt);
+  RTA_ADDR_GET (NULL, RTA_GENMASK, ifm->ifam_addrs, pnt);
+  RTA_NAME_GET (ifname, RTA_IFP, ifm->ifam_addrs, pnt, *ifnlen);
+  RTA_ADDR_GET (addr, RTA_IFA, ifm->ifam_addrs, pnt);
+  RTA_ADDR_GET (NULL, RTA_AUTHOR, ifm->ifam_addrs, pnt);
+  RTA_ADDR_GET (brd, RTA_BRD, ifm->ifam_addrs, pnt);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      int family = sockunion_family(addr);
+      switch (family)
+        {
+       case AF_INET:
+#ifdef HAVE_IPV6
+       case AF_INET6:
+#endif
+         {
+           char buf[4][INET6_ADDRSTRLEN];
+           zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x, "
+                       "ifam_flags 0x%x, addr %s/%d broad %s dst %s "
+                       "gateway %s",
+                       __func__, ifm->ifam_index,
+                       (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs,
+                       ifm->ifam_flags,
+                       inet_ntop(family,&addr->sin.sin_addr,
+                                 buf[0],sizeof(buf[0])),
+                       ip_masklen(mask->sin.sin_addr),
+                       inet_ntop(family,&brd->sin.sin_addr,
+                                 buf[1],sizeof(buf[1])),
+                       inet_ntop(family,&dst.sin.sin_addr,
+                                 buf[2],sizeof(buf[2])),
+                       inet_ntop(family,&gateway.sin.sin_addr,
+                                 buf[3],sizeof(buf[3])));
+         }
+         break;
+        default:
+         zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x",
+                     __func__, ifm->ifam_index, 
+                     (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs);
+         break;
+        }
+    }
+
+  /* Assert read up end point matches to end point */
+  if (pnt != end)
+    zlog_warn ("ifam_read() doesn't read all socket data");
+}
+
+/* Interface's address information get. */
+int
+ifam_read (struct ifa_msghdr *ifam)
+{
+  struct interface *ifp = NULL;
+  union sockunion addr, mask, brd;
+  char ifname[INTERFACE_NAMSIZ];
+  short ifnlen = 0;
+  char isalias = 0;
+  int flags = 0;
+  
+  ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0';
+  
+  /* Allocate and read address information. */
+  ifam_read_mesg (ifam, &addr, &mask, &brd, ifname, &ifnlen);
+  
+  if ((ifp = if_lookup_by_index(ifam->ifam_index)) == NULL)
+    {
+      zlog_warn ("%s: no interface for ifname %s, index %d", 
+                 __func__, ifname, ifam->ifam_index);
+      return -1;
+    }
+  
+  if (ifnlen && strncmp (ifp->name, ifname, INTERFACE_NAMSIZ))
+    isalias = 1;
+  
+  /* N.B. The info in ifa_msghdr does not tell us whether the RTA_BRD
+     field contains a broadcast address or a peer address, so we are forced to
+     rely upon the interface type. */
+  if (if_is_pointopoint(ifp))
+    SET_FLAG(flags, ZEBRA_IFA_PEER);
+
+#if 0
+  /* it might seem cute to grab the interface metric here, however
+   * we're processing an address update message, and so some systems
+   * (e.g. FBSD) dont bother to fill in ifam_metric. Disabled, but left
+   * in deliberately, as comment.
+   */
+  ifp->metric = ifam->ifam_metric;
+#endif
+
+  /* Add connected address. */
+  switch (sockunion_family (&addr))
+    {
+    case AF_INET:
+      if (ifam->ifam_type == RTM_NEWADDR)
+       connected_add_ipv4 (ifp, flags, &addr.sin.sin_addr, 
+                           ip_masklen (mask.sin.sin_addr),
+                           &brd.sin.sin_addr,
+                           (isalias ? ifname : NULL));
+      else
+       connected_delete_ipv4 (ifp, flags, &addr.sin.sin_addr, 
+                              ip_masklen (mask.sin.sin_addr),
+                              &brd.sin.sin_addr);
+      break;
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      /* Unset interface index from link-local address when IPv6 stack
+        is KAME. */
+      if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr))
+        {
+          SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0);
+        }
+
+      if (ifam->ifam_type == RTM_NEWADDR)
+       connected_add_ipv6 (ifp, flags, &addr.sin6.sin6_addr, 
+                           ip6_masklen (mask.sin6.sin6_addr),
+                           &brd.sin6.sin6_addr,
+                           (isalias ? ifname : NULL));
+      else
+       connected_delete_ipv6 (ifp,
+                              &addr.sin6.sin6_addr, 
+                              ip6_masklen (mask.sin6.sin6_addr),
+                              &brd.sin6.sin6_addr);
+      break;
+#endif /* HAVE_IPV6 */
+    default:
+      /* Unsupported family silently ignore... */
+      break;
+    }
+  
+  /* Check interface flag for implicit up of the interface. */
+  if_refresh (ifp);
+
+#ifdef SUNOS_5
+  /* In addition to lacking IFANNOUNCE, on SUNOS IFF_UP is strange. 
+   * See comments for SUNOS_5 in interface.c::if_flags_mangle.
+   * 
+   * Here we take care of case where the real IFF_UP was previously
+   * unset (as kept in struct zebra_if.primary_state) and the mangled
+   * IFF_UP (ie IFF_UP set || listcount(connected) has now transitioned
+   * to unset due to the lost non-primary address having DELADDR'd.
+   *
+   * we must delete the interface, because in between here and next
+   * event for this interface-name the administrator could unplumb
+   * and replumb the interface.
+   */
+  if (!if_is_up (ifp))
+    if_delete_update (ifp);
+#endif /* SUNOS_5 */
+  
+  return 0;
+}
+
+/* Interface function for reading kernel routing table information. */
+static int
+rtm_read_mesg (struct rt_msghdr *rtm,
+              union sockunion *dest,
+              union sockunion *mask,
+              union sockunion *gate,
+              char *ifname,
+              short *ifnlen)
+{
+  caddr_t pnt, end;
+
+  /* Pnt points out socket data start point. */
+  pnt = (caddr_t)(rtm + 1);
+  end = ((caddr_t)rtm) + rtm->rtm_msglen;
+
+  /* rt_msghdr version check. */
+  if (rtm->rtm_version != RTM_VERSION) 
+      zlog (NULL, LOG_WARNING,
+             "Routing message version different %d should be %d."
+             "This may cause problem\n", rtm->rtm_version, RTM_VERSION);
+  
+  /* Be sure structure is cleared */
+  memset (dest, 0, sizeof (union sockunion));
+  memset (gate, 0, sizeof (union sockunion));
+  memset (mask, 0, sizeof (union sockunion));
+
+  /* We fetch each socket variable into sockunion. */
+  RTA_ADDR_GET (dest, RTA_DST, rtm->rtm_addrs, pnt);
+  RTA_ADDR_GET (gate, RTA_GATEWAY, rtm->rtm_addrs, pnt);
+  RTA_ATTR_GET (mask, RTA_NETMASK, rtm->rtm_addrs, pnt);
+  RTA_ADDR_GET (NULL, RTA_GENMASK, rtm->rtm_addrs, pnt);
+  RTA_NAME_GET (ifname, RTA_IFP, rtm->rtm_addrs, pnt, *ifnlen);
+  RTA_ADDR_GET (NULL, RTA_IFA, rtm->rtm_addrs, pnt);
+  RTA_ADDR_GET (NULL, RTA_AUTHOR, rtm->rtm_addrs, pnt);
+  RTA_ADDR_GET (NULL, RTA_BRD, rtm->rtm_addrs, pnt);
+
+  /* If there is netmask information set it's family same as
+     destination family*/
+  if (rtm->rtm_addrs & RTA_NETMASK)
+    mask->sa.sa_family = dest->sa.sa_family;
+
+  /* Assert read up to the end of pointer. */
+  if (pnt != end) 
+      zlog (NULL, LOG_WARNING, "rtm_read() doesn't read all socket data.");
+
+  return rtm->rtm_flags;
+}
+
+void
+rtm_read (struct rt_msghdr *rtm)
+{
+  int flags;
+  u_char zebra_flags;
+  union sockunion dest, mask, gate;
+  char ifname[INTERFACE_NAMSIZ + 1];
+  short ifnlen = 0;
+
+  zebra_flags = 0;
+
+  /* Read destination and netmask and gateway from rtm message
+     structure. */
+  flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen);
+  if (!(flags & RTF_DONE))
+    return;
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s: got rtm of type %d (%s)", __func__, rtm->rtm_type,
+      lookup (rtm_type_str, rtm->rtm_type));
+
+#ifdef RTF_CLONED      /*bsdi, netbsd 1.6*/
+  if (flags & RTF_CLONED)
+    return;
+#endif
+#ifdef RTF_WASCLONED   /*freebsd*/
+  if (flags & RTF_WASCLONED)
+    return;
+#endif
+
+  if ((rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) && ! (flags & RTF_UP))
+    return;
+
+  /* This is connected route. */
+  if (! (flags & RTF_GATEWAY))
+      return;
+
+  if (flags & RTF_PROTO1)
+    SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE);
+
+  /* This is persistent route. */
+  if (flags & RTF_STATIC)
+    SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC);
+
+  /* This is a reject or blackhole route */
+  if (flags & RTF_REJECT)
+    SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT);
+  if (flags & RTF_BLACKHOLE)
+    SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE);
+
+  if (dest.sa.sa_family == AF_INET)
+    {
+      struct prefix_ipv4 p;
+
+      p.family = AF_INET;
+      p.prefix = dest.sin.sin_addr;
+      if (flags & RTF_HOST)
+       p.prefixlen = IPV4_MAX_PREFIXLEN;
+      else
+       p.prefixlen = ip_masklen (mask.sin.sin_addr);
+      
+      /* Catch self originated messages and match them against our current RIB.
+       * At the same time, ignore unconfirmed messages, they should be tracked
+       * by rtm_write() and kernel_rtm_ipv4().
+       */
+      if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid)
+      {
+        char buf[PREFIX_STRLEN], gate_buf[INET_ADDRSTRLEN];
+        int ret;
+        if (! IS_ZEBRA_DEBUG_RIB)
+          return;
+        ret = rib_lookup_ipv4_route (&p, &gate, VRF_DEFAULT);
+        prefix2str (&p, buf, sizeof(buf));
+        switch (rtm->rtm_type)
+        {
+          case RTM_ADD:
+          case RTM_GET:
+          case RTM_CHANGE:
+            /* The kernel notifies us about a new route in FIB created by us.
+               Do we have a correspondent entry in our RIB? */
+            switch (ret)
+            {
+              case ZEBRA_RIB_NOTFOUND:
+                zlog_debug ("%s: %s %s: desync: RR isn't yet in RIB, while already in FIB",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf);
+                break;
+              case ZEBRA_RIB_FOUND_CONNECTED:
+              case ZEBRA_RIB_FOUND_NOGATE:
+                inet_ntop (AF_INET, &gate.sin.sin_addr, gate_buf, INET_ADDRSTRLEN);
+                zlog_debug ("%s: %s %s: desync: RR is in RIB, but gate differs (ours is %s)",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, gate_buf);
+                break;
+              case ZEBRA_RIB_FOUND_EXACT: /* RIB RR == FIB RR */
+                zlog_debug ("%s: %s %s: done Ok",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf);
+                rib_lookup_and_dump (&p);
+                return;
+                break;
+            }
+            break;
+          case RTM_DELETE:
+            /* The kernel notifies us about a route deleted by us. Do we still
+               have it in the RIB? Do we have anything instead? */
+            switch (ret)
+            {
+              case ZEBRA_RIB_FOUND_EXACT:
+                zlog_debug ("%s: %s %s: desync: RR is still in RIB, while already not in FIB",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf);
+                rib_lookup_and_dump (&p);
+                break;
+              case ZEBRA_RIB_FOUND_CONNECTED:
+              case ZEBRA_RIB_FOUND_NOGATE:
+                zlog_debug ("%s: %s %s: desync: RR is still in RIB, plus gate differs",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf);
+                rib_lookup_and_dump (&p);
+                break;
+              case ZEBRA_RIB_NOTFOUND: /* RIB RR == FIB RR */
+                zlog_debug ("%s: %s %s: done Ok",
+                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf);
+                rib_lookup_and_dump (&p);
+                return;
+                break;
+            }
+            break;
+          default:
+            zlog_debug ("%s: %s: warning: loopback RTM of type %s received",
+              __func__, buf, lookup (rtm_type_str, rtm->rtm_type));
+        }
+        return;
+      }
+
+      /* Change, delete the old prefix, we have no further information
+       * to specify the route really
+       */
+      if (rtm->rtm_type == RTM_CHANGE)
+        rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
+                         NULL, 0, VRF_DEFAULT, SAFI_UNICAST);
+      
+      if (rtm->rtm_type == RTM_GET 
+          || rtm->rtm_type == RTM_ADD
+          || rtm->rtm_type == RTM_CHANGE)
+        rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr,
+                      NULL, 0, VRF_DEFAULT, 0, 0, 0, 0, SAFI_UNICAST);
+      else
+        rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
+                         &gate.sin.sin_addr, 0, VRF_DEFAULT, SAFI_UNICAST);
+    }
+#ifdef HAVE_IPV6
+  if (dest.sa.sa_family == AF_INET6)
+    {
+      /* One day we might have a debug section here like one in the
+       * IPv4 case above. Just ignore own messages at the moment.
+       */
+      if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid)
+        return;
+      struct prefix_ipv6 p;
+      ifindex_t ifindex = 0;
+
+      p.family = AF_INET6;
+      p.prefix = dest.sin6.sin6_addr;
+      if (flags & RTF_HOST)
+       p.prefixlen = IPV6_MAX_PREFIXLEN;
+      else
+       p.prefixlen = ip6_masklen (mask.sin6.sin6_addr);
+
+#ifdef KAME
+      if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr))
+       {
+         ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr);
+         SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0);
+       }
+#endif /* KAME */
+
+      /* CHANGE: delete the old prefix, we have no further information
+       * to specify the route really
+       */
+      if (rtm->rtm_type == RTM_CHANGE)
+        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
+                         NULL, 0, VRF_DEFAULT, SAFI_UNICAST);
+      
+      if (rtm->rtm_type == RTM_GET 
+          || rtm->rtm_type == RTM_ADD
+          || rtm->rtm_type == RTM_CHANGE)
+        rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr,
+                      ifindex, VRF_DEFAULT, RT_TABLE_MAIN, 0, 0, 0, SAFI_UNICAST);
+      else
+        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
+                         &gate.sin6.sin6_addr, ifindex,
+                         VRF_DEFAULT, SAFI_UNICAST);
+    }
+#endif /* HAVE_IPV6 */
+}
+
+/* Interface function for the kernel routing table updates.  Support
+ * for RTM_CHANGE will be needed.
+ * Exported only for rt_socket.c
+ */
+int
+rtm_write (int message,
+          union sockunion *dest,
+          union sockunion *mask,
+          union sockunion *gate,
+          unsigned int index,
+          int zebra_flags,
+          int metric)
+{
+  int ret;
+  caddr_t pnt;
+  struct interface *ifp;
+
+  /* Sequencial number of routing message. */
+  static int msg_seq = 0;
+
+  /* Struct of rt_msghdr and buffer for storing socket's data. */
+  struct 
+  {
+    struct rt_msghdr rtm;
+    char buf[512];
+  } msg;
+  
+  if (routing_sock < 0)
+    return ZEBRA_ERR_EPERM;
+
+  /* Clear and set rt_msghdr values */
+  memset (&msg, 0, sizeof (struct rt_msghdr));
+  msg.rtm.rtm_version = RTM_VERSION;
+  msg.rtm.rtm_type = message;
+  msg.rtm.rtm_seq = msg_seq++;
+  msg.rtm.rtm_addrs = RTA_DST;
+  msg.rtm.rtm_addrs |= RTA_GATEWAY;
+  msg.rtm.rtm_flags = RTF_UP;
+  msg.rtm.rtm_index = index;
+
+  if (metric != 0)
+    {
+      msg.rtm.rtm_rmx.rmx_hopcount = metric;
+      msg.rtm.rtm_inits |= RTV_HOPCOUNT;
+    }
+
+  ifp = if_lookup_by_index (index);
+
+  if (gate && (message == RTM_ADD || message == RTM_CHANGE))
+    msg.rtm.rtm_flags |= RTF_GATEWAY;
+
+  /* When RTF_CLONING is unavailable on BSD, should we set some
+   * other flag instead?
+   */
+#ifdef RTF_CLONING
+  if (! gate && (message == RTM_ADD || message == RTM_CHANGE) && ifp &&
+      (ifp->flags & IFF_POINTOPOINT) == 0)
+    msg.rtm.rtm_flags |= RTF_CLONING;
+#endif /* RTF_CLONING */
+
+  /* If no protocol specific gateway is specified, use link
+     address for gateway. */
+  if (! gate)
+    {
+      if (!ifp)
+        {
+          char dest_buf[INET_ADDRSTRLEN] = "NULL", mask_buf[INET_ADDRSTRLEN] = "255.255.255.255";
+          if (dest)
+            inet_ntop (AF_INET, &dest->sin.sin_addr, dest_buf, INET_ADDRSTRLEN);
+          if (mask)
+            inet_ntop (AF_INET, &mask->sin.sin_addr, mask_buf, INET_ADDRSTRLEN);
+          zlog_warn ("%s: %s/%s: gate == NULL and no gateway found for ifindex %d",
+            __func__, dest_buf, mask_buf, index);
+          return -1;
+        }
+      gate = (union sockunion *) &((struct zebra_if *)ifp->info)->sdl;
+    }
+
+  if (mask)
+    msg.rtm.rtm_addrs |= RTA_NETMASK;
+  else if (message == RTM_ADD || message == RTM_CHANGE)
+    msg.rtm.rtm_flags |= RTF_HOST;
+
+  /* Tagging route with flags */
+  msg.rtm.rtm_flags |= (RTF_PROTO1);
+
+  /* Additional flags. */
+  if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
+    msg.rtm.rtm_flags |= RTF_BLACKHOLE;
+  if (zebra_flags & ZEBRA_FLAG_REJECT)
+    msg.rtm.rtm_flags |= RTF_REJECT;
+
+
+#define SOCKADDRSET(X,R) \
+  if (msg.rtm.rtm_addrs & (R)) \
+    { \
+      int len = SAROUNDUP (X); \
+      memcpy (pnt, (caddr_t)(X), len); \
+      pnt += len; \
+    }
+
+  pnt = (caddr_t) msg.buf;
+
+  /* Write each socket data into rtm message buffer */
+  SOCKADDRSET (dest, RTA_DST);
+  SOCKADDRSET (gate, RTA_GATEWAY);
+  SOCKADDRSET (mask, RTA_NETMASK);
+
+  msg.rtm.rtm_msglen = pnt - (caddr_t) &msg;
+
+  ret = write (routing_sock, &msg, msg.rtm.rtm_msglen);
+
+  if (ret != msg.rtm.rtm_msglen) 
+    {
+      if (errno == EEXIST) 
+       return ZEBRA_ERR_RTEXIST;
+      if (errno == ENETUNREACH)
+       return ZEBRA_ERR_RTUNREACH;
+      if (errno == ESRCH)
+       return ZEBRA_ERR_RTNOEXIST;
+      
+      zlog_warn ("%s: write : %s (%d)", __func__, safe_strerror (errno), errno);
+      return ZEBRA_ERR_KERNEL;
+    }
+  return ZEBRA_ERR_NOERROR;
+}
+
+
+#include "thread.h"
+#include "zebra/zserv.h"
+
+/* For debug purpose. */
+static void
+rtmsg_debug (struct rt_msghdr *rtm)
+{
+  zlog_debug ("Kernel: Len: %d Type: %s", rtm->rtm_msglen, lookup (rtm_type_str, rtm->rtm_type));
+  rtm_flag_dump (rtm->rtm_flags);
+  zlog_debug ("Kernel: message seq %d", rtm->rtm_seq);
+  zlog_debug ("Kernel: pid %lld, rtm_addrs 0x%x",
+              (long long)rtm->rtm_pid, rtm->rtm_addrs);
+}
+
+/* This is pretty gross, better suggestions welcome -- mhandler */
+#ifndef RTAX_MAX
+#ifdef RTA_NUMBITS
+#define RTAX_MAX       RTA_NUMBITS
+#else
+#define RTAX_MAX       8
+#endif /* RTA_NUMBITS */
+#endif /* RTAX_MAX */
+
+/* Kernel routing table and interface updates via routing socket. */
+static int
+kernel_read (struct thread *thread)
+{
+  int sock;
+  int nbytes;
+  struct rt_msghdr *rtm;
+
+  /*
+   * This must be big enough for any message the kernel might send.
+   * Rather than determining how many sockaddrs of what size might be
+   * in each particular message, just use RTAX_MAX of sockaddr_storage
+   * for each.  Note that the sockaddrs must be after each message
+   * definition, or rather after whichever happens to be the largest,
+   * since the buffer needs to be big enough for a message and the
+   * sockaddrs together.
+   */
+  union 
+  {
+    /* Routing information. */
+    struct 
+    {
+      struct rt_msghdr rtm;
+      struct sockaddr_storage addr[RTAX_MAX];
+    } r;
+
+    /* Interface information. */
+    struct
+    {
+      struct if_msghdr ifm;
+      struct sockaddr_storage addr[RTAX_MAX];
+    } im;
+
+    /* Interface address information. */
+    struct
+    {
+      struct ifa_msghdr ifa;
+      struct sockaddr_storage addr[RTAX_MAX];
+    } ia;
+
+#ifdef RTM_IFANNOUNCE
+    /* Interface arrival/departure */
+    struct
+    {
+      struct if_announcemsghdr ifan;
+      struct sockaddr_storage addr[RTAX_MAX];
+    } ian;
+#endif /* RTM_IFANNOUNCE */
+
+  } buf;
+
+  /* Fetch routing socket. */
+  sock = THREAD_FD (thread);
+
+  nbytes= read (sock, &buf, sizeof buf);
+
+  if (nbytes <= 0)
+    {
+      if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
+       zlog_warn ("routing socket error: %s", safe_strerror (errno));
+      return 0;
+    }
+
+  thread_add_read (zebrad.master, kernel_read, NULL, sock);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    rtmsg_debug (&buf.r.rtm);
+
+  rtm = &buf.r.rtm;
+
+  /*
+   * Ensure that we didn't drop any data, so that processing routines
+   * can assume they have the whole message.
+   */
+  if (rtm->rtm_msglen != nbytes)
+    {
+      zlog_warn ("kernel_read: rtm->rtm_msglen %d, nbytes %d, type %d\n",
+                rtm->rtm_msglen, nbytes, rtm->rtm_type);
+      return -1;
+    }
+
+  switch (rtm->rtm_type)
+    {
+    case RTM_ADD:
+    case RTM_DELETE:
+    case RTM_CHANGE:
+      rtm_read (rtm);
+      break;
+    case RTM_IFINFO:
+      ifm_read (&buf.im.ifm);
+      break;
+    case RTM_NEWADDR:
+    case RTM_DELADDR:
+      ifam_read (&buf.ia.ifa);
+      break;
+#ifdef RTM_IFANNOUNCE
+    case RTM_IFANNOUNCE:
+      ifan_read (&buf.ian.ifan);
+      break;
+#endif /* RTM_IFANNOUNCE */
+    default:
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("Unprocessed RTM_type: %d", rtm->rtm_type);
+      break;
+    }
+  return 0;
+}
+
+/* Make routing socket. */
+static void
+routing_socket (struct zebra_vrf *zvrf)
+{
+  if (zvrf->vrf_id != VRF_DEFAULT)
+    return;
+
+  if ( zserv_privs.change (ZPRIVS_RAISE) )
+    zlog_err ("routing_socket: Can't raise privileges");
+
+  routing_sock = socket (AF_ROUTE, SOCK_RAW, 0);
+
+  if (routing_sock < 0) 
+    {
+      if ( zserv_privs.change (ZPRIVS_LOWER) )
+        zlog_err ("routing_socket: Can't lower privileges");
+      zlog_warn ("Can't init kernel routing socket");
+      return;
+    }
+
+  /* XXX: Socket should be NONBLOCK, however as we currently 
+   * discard failed writes, this will lead to inconsistencies.
+   * For now, socket must be blocking.
+   */
+  /*if (fcntl (routing_sock, F_SETFL, O_NONBLOCK) < 0) 
+    zlog_warn ("Can't set O_NONBLOCK to routing socket");*/
+    
+  if ( zserv_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("routing_socket: Can't lower privileges");
+
+  /* kernel_read needs rewrite. */
+  thread_add_read (zebrad.master, kernel_read, NULL, routing_sock);
+}
+
+/* Exported interface function.  This function simply calls
+   routing_socket (). */
+void
+kernel_init (struct zebra_vrf *zvrf)
+{
+  routing_socket (zvrf);
+}
+
+void
+kernel_terminate (struct zebra_vrf *zvrf)
+{
+  return;
+}
diff --git a/zebra/kernel_socket.h b/zebra/kernel_socket.h
new file mode 100644 (file)
index 0000000..e9558ad
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Exported kernel_socket functions, exported only for convenience of
+ * sysctl methods.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __ZEBRA_KERNEL_SOCKET_H
+#define __ZEBRA_KERNEL_SOCKET_H
+
+extern void rtm_read (struct rt_msghdr *);
+extern int ifam_read (struct ifa_msghdr *);
+extern int ifm_read (struct if_msghdr *);
+extern int rtm_write (int, union sockunion *, union sockunion *,
+                      union sockunion *, unsigned int, int, int);
+extern const struct message rtm_type_str[];
+
+#endif /* __ZEBRA_KERNEL_SOCKET_H */
diff --git a/zebra/main.c b/zebra/main.c
new file mode 100644 (file)
index 0000000..35cb159
--- /dev/null
@@ -0,0 +1,498 @@
+/* zebra daemon main routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "command.h"
+#include "thread.h"
+#include "filter.h"
+#include "memory.h"
+#include "prefix.h"
+#include "log.h"
+#include "plist.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "vrf.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/router-id.h"
+#include "zebra/irdp.h"
+#include "zebra/rtadv.h"
+#include "zebra/zebra_fpm.h"
+
+/* Zebra instance */
+struct zebra_t zebrad =
+{
+  .rtm_table_default = 0,
+};
+
+/* process id. */
+pid_t pid;
+
+/* Pacify zclient.o in libzebra, which expects this variable. */
+struct thread_master *master;
+
+/* Route retain mode flag. */
+int retain_mode = 0;
+
+/* Don't delete kernel route. */
+int keep_kernel_mode = 0;
+
+#ifdef HAVE_NETLINK
+/* Receive buffer size for netlink socket */
+u_int32_t nl_rcvbufsize = 0;
+#endif /* HAVE_NETLINK */
+
+/* Command line options. */
+struct option longopts[] = 
+{
+  { "batch",       no_argument,       NULL, 'b'},
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "keep_kernel", no_argument,       NULL, 'k'},
+  { "fpm_format",  required_argument, NULL, 'F'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "pid_file",    required_argument, NULL, 'i'},
+  { "socket",      required_argument, NULL, 'z'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "retain",      no_argument,       NULL, 'r'},
+  { "dryrun",      no_argument,       NULL, 'C'},
+#ifdef HAVE_NETLINK
+  { "nl-bufsize",  required_argument, NULL, 's'},
+#endif /* HAVE_NETLINK */
+  { "user",        required_argument, NULL, 'u'},
+  { "group",       required_argument, NULL, 'g'},
+  { "version",     no_argument,       NULL, 'v'},
+  { 0 }
+};
+
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_ADMIN,
+  ZCAP_SYS_ADMIN,
+  ZCAP_NET_RAW,
+};
+
+/* zebra privileges to run with */
+struct zebra_privs_t zserv_privs =
+{
+#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
+  .user = QUAGGA_USER,
+  .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+  .caps_p = _caps_p,
+  .cap_num_p = array_size(_caps_p),
+  .cap_num_i = 0
+};
+
+/* Default configuration file path. */
+char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_ZEBRA_PID;
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\n"\
+             "Daemon which manages kernel routing table management and "\
+             "redistribution between different routing protocols.\n\n"\
+             "-b, --batch        Runs in batch mode\n"\
+             "-d, --daemon       Runs in daemon mode\n"\
+             "-f, --config_file  Set configuration file name\n"\
+             "-F, --fpm_format   Set fpm format to 'netlink' or 'protobuf'\n"\
+             "-i, --pid_file     Set process identifier file name\n"\
+             "-z, --socket       Set path of zebra socket\n"\
+             "-k, --keep_kernel  Don't delete old routes which installed by "\
+                                 "zebra.\n"\
+             "-C, --dryrun       Check configuration for validity and exit\n"\
+             "-A, --vty_addr     Set vty's bind address\n"\
+             "-P, --vty_port     Set vty's port number\n"\
+             "-r, --retain       When program terminates, retain added route "\
+                                 "by zebra.\n"\
+             "-u, --user         User to run as\n"\
+             "-g, --group        Group to run as\n", progname);
+#ifdef HAVE_NETLINK
+      printf ("-s, --nl-bufsize   Set netlink receive buffer size\n");
+#endif /* HAVE_NETLINK */
+      printf ("-v, --version      Print program version\n"\
+             "-h, --help         Display this help and exit\n"\
+             "\n"\
+             "Report bugs to %s\n", ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog_info ("SIGHUP received");
+
+  /* Reload of config file. */
+  ;
+}
+
+/* SIGINT handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+
+  if (!retain_mode)
+    rib_close ();
+#ifdef HAVE_IRDP
+  irdp_finish();
+#endif
+
+  exit (0);
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t zebra_signals[] =
+{
+  { 
+    .signal = SIGHUP, 
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};
+
+/* Callback upon creating a new VRF. */
+static int
+zebra_vrf_new (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = *info;
+
+  if (! zvrf)
+    {
+      zvrf = zebra_vrf_alloc (vrf_id);
+      *info = (void *)zvrf;
+      router_id_init (zvrf);
+    }
+
+  return 0;
+}
+
+/* Callback upon enabling a VRF. */
+static int
+zebra_vrf_enable (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info);
+
+  assert (zvrf);
+
+#if defined (HAVE_RTADV)
+  rtadv_init (zvrf);
+#endif
+  kernel_init (zvrf);
+  interface_list (zvrf);
+  route_read (zvrf);
+
+  return 0;
+}
+
+/* Callback upon disabling a VRF. */
+static int
+zebra_vrf_disable (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info);
+  struct listnode *list_node;
+  struct interface *ifp;
+
+  assert (zvrf);
+
+  rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]);
+  rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]);
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), list_node, ifp))
+    {
+      int operative = if_is_operative (ifp);
+      UNSET_FLAG (ifp->flags, IFF_UP);
+      if (operative)
+        if_down (ifp);
+    }
+
+#if defined (HAVE_RTADV)
+  rtadv_terminate (zvrf);
+#endif
+  kernel_terminate (zvrf);
+
+  list_delete_all_node (zvrf->rid_all_sorted_list);
+  list_delete_all_node (zvrf->rid_lo_sorted_list);
+
+  return 0;
+}
+
+/* Zebra VRF initialization. */
+static void
+zebra_vrf_init (void)
+{
+  vrf_add_hook (VRF_NEW_HOOK, zebra_vrf_new);
+  vrf_add_hook (VRF_ENABLE_HOOK, zebra_vrf_enable);
+  vrf_add_hook (VRF_DISABLE_HOOK, zebra_vrf_disable);
+  vrf_init ();
+}
+
+/* Main startup routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  char *vty_addr = NULL;
+  int vty_port = ZEBRA_VTY_PORT;
+  int dryrun = 0;
+  int batch_mode = 0;
+  int daemon_mode = 0;
+  char *config_file = NULL;
+  char *progname;
+  struct thread thread;
+  char *zserv_path = NULL;
+  char *fpm_format = NULL;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* preserve my name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  zlog_default = openzlog (progname, ZLOG_ZEBRA,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  while (1) 
+    {
+      int opt;
+  
+#ifdef HAVE_NETLINK  
+      opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vs:C", longopts, 0);
+#else
+      opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vC", longopts, 0);
+#endif /* HAVE_NETLINK */
+
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'b':
+         batch_mode = 1;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'k':
+         keep_kernel_mode = 1;
+         break;
+       case 'C':
+         dryrun = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'F':
+         fpm_format = optarg;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+        case 'i':
+          pid_file = optarg;
+          break;
+       case 'z':
+         zserv_path = optarg;
+         break;
+       case 'P':
+         /* Deal with atoi() returning 0 on failure, and zebra not
+            listening on zebra port... */
+         if (strcmp(optarg, "0") == 0) 
+           {
+             vty_port = 0;
+             break;
+           } 
+         vty_port = atoi (optarg);
+         if (vty_port <= 0 || vty_port > 0xffff)
+           vty_port = ZEBRA_VTY_PORT;
+         break;
+       case 'r':
+         retain_mode = 1;
+         break;
+#ifdef HAVE_NETLINK
+       case 's':
+         nl_rcvbufsize = atoi (optarg);
+         break;
+#endif /* HAVE_NETLINK */
+       case 'u':
+         zserv_privs.user = optarg;
+         break;
+       case 'g':
+         zserv_privs.group = optarg;
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+
+  /* Make master thread emulator. */
+  zebrad.master = thread_master_create ();
+
+  /* privs initialise */
+  zprivs_init (&zserv_privs);
+
+  /* Vty related initialize. */
+  signal_init (zebrad.master, array_size(zebra_signals), zebra_signals);
+  cmd_init (1);
+  vty_init (zebrad.master);
+  memory_init ();
+
+  /* Zebra related initialize. */
+  zebra_init ();
+  rib_init ();
+  zebra_if_init ();
+  zebra_debug_init ();
+  router_id_cmd_init ();
+  zebra_vty_init ();
+  access_list_init ();
+  prefix_list_init ();
+#if defined (HAVE_RTADV)
+  rtadv_cmd_init ();
+#endif
+#ifdef HAVE_IRDP
+  irdp_init();
+#endif
+
+  /* For debug purpose. */
+  /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
+
+  /* Initialize VRF module, and make kernel routing socket. */
+  zebra_vrf_init ();
+
+#ifdef HAVE_SNMP
+  zebra_snmp_init ();
+#endif /* HAVE_SNMP */
+
+#ifdef HAVE_FPM
+  zfpm_init (zebrad.master, 1, 0, fpm_format);
+#else
+  zfpm_init (zebrad.master, 0, 0, fpm_format);
+#endif
+
+  /* Process the configuration file. Among other configuration
+  *  directives we can meet those installing static routes. Such
+  *  requests will not be executed immediately, but queued in
+  *  zebra->ribq structure until we enter the main execution loop.
+  *  The notifications from kernel will show originating PID equal
+  *  to that after daemon() completes (if ever called).
+  */
+  vty_read_config (config_file, config_default);
+
+  /* Don't start execution if we are in dry-run mode */
+  if (dryrun)
+    return(0);
+
+  /* Count up events for interfaces */
+  if_startup_count_up ();
+
+  /* Clean up rib. */
+  rib_weed_tables ();
+
+  /* Exit when zebra is working in batch mode. */
+  if (batch_mode)
+    exit (0);
+
+  /* Daemonize. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      zlog_err("Zebra daemon failed: %s", strerror(errno));
+      exit (1);
+    }
+
+  /* Output pid of zebra. */
+  pid_output (pid_file);
+
+  /* After we have successfully acquired the pidfile, we can be sure
+  *  about being the only copy of zebra process, which is submitting
+  *  changes to the FIB.
+  *  Clean up zebra-originated routes. The requests will be sent to OS
+  *  immediately, so originating PID in notifications from kernel
+  *  will be equal to the current getpid(). To know about such routes,
+  * we have to have route_read() called before.
+  */
+  if (! keep_kernel_mode)
+    rib_sweep_route ();
+
+  /* Needed for BSD routing socket. */
+  pid = getpid ();
+
+  /* This must be done only after locking pidfile (bug #403). */
+  zebra_zserv_socket_init (zserv_path);
+
+  /* Make vty server socket. */
+  vty_serv_sock (vty_addr, vty_port, ZEBRA_VTYSH_PATH);
+
+  /* Print banner. */
+  zlog_notice ("Zebra %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  while (thread_fetch (zebrad.master, &thread))
+    thread_call (&thread);
+
+  /* Not reached... */
+  return 0;
+}
diff --git a/zebra/misc_null.c b/zebra/misc_null.c
new file mode 100644 (file)
index 0000000..18977d2
--- /dev/null
@@ -0,0 +1,56 @@
+/* 
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "zebra/rtadv.h"
+#include "zebra/irdp.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_fpm.h"
+
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+void _quagga_noop (void);
+void _quagga_noop (void) { return; }
+#pragma weak rtadv_config_write = _quagga_noop
+#pragma weak irdp_config_write = _quagga_noop
+#ifdef HAVE_NET_RT_IFLIST
+#pragma weak ifstat_update_sysctl = _quagga_noop
+#endif
+#ifdef HAVE_PROC_NET_DEV
+#pragma weak ifstat_update_proc = _quagga_noop
+#endif
+#else
+void rtadv_config_write (struct vty *vty, struct interface *ifp) { return; }
+void irdp_config_write (struct vty *vty, struct interface *ifp) { return; }
+#ifdef HAVE_PROC_NET_DEV
+void ifstat_update_proc (void) { return; }
+#endif
+#ifdef HAVE_NET_RT_IFLIST
+void ifstat_update_sysctl (void) { return; }
+#endif
+#endif
+
+void
+zfpm_trigger_update (struct route_node *rn, const char *reason)
+{
+  return;
+}
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
new file mode 100644 (file)
index 0000000..a7a6b25
--- /dev/null
@@ -0,0 +1,427 @@
+/* Redistribution Handler
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "stream.h"
+#include "zclient.h"
+#include "linklist.h"
+#include "log.h"
+#include "vrf.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/router-id.h"
+
+/* master zebra server structure */
+extern struct zebra_t zebrad;
+
+int
+zebra_check_addr (struct prefix *p)
+{
+  if (p->family == AF_INET)
+    {
+      u_int32_t addr;
+
+      addr = p->u.prefix4.s_addr;
+      addr = ntohl (addr);
+
+      if (IPV4_NET127 (addr)
+          || IN_CLASSD (addr)
+          || IPV4_LINKLOCAL(addr))
+       return 0;
+    }
+#ifdef HAVE_IPV6
+  if (p->family == AF_INET6)
+    {
+      if (IN6_IS_ADDR_LOOPBACK (&p->u.prefix6))
+       return 0;
+      if (IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6))
+       return 0;
+    }
+#endif /* HAVE_IPV6 */
+  return 1;
+}
+
+int
+is_default (struct prefix *p)
+{
+  if (p->family == AF_INET)
+    if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0)
+      return 1;
+#ifdef HAVE_IPV6
+#if 0  /* IPv6 default separation is now pending until protocol daemon
+          can handle that. */
+  if (p->family == AF_INET6)
+    if (IN6_IS_ADDR_UNSPECIFIED (&p->u.prefix6) && p->prefixlen == 0)
+      return 1;
+#endif /* 0 */
+#endif /* HAVE_IPV6 */
+  return 0;
+}
+
+static void
+zebra_redistribute_default (struct zserv *client, vrf_id_t vrf_id)
+{
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *newrib;
+#ifdef HAVE_IPV6
+  struct prefix_ipv6 p6;
+#endif /* HAVE_IPV6 */
+
+
+  /* Lookup default route. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (table)
+    {
+      rn = route_node_lookup (table, (struct prefix *)&p);
+      if (rn)
+       {
+         RNODE_FOREACH_RIB (rn, newrib)
+           if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
+               && newrib->distance != DISTANCE_INFINITY)
+             zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib);
+         route_unlock_node (rn);
+       }
+    }
+
+#ifdef HAVE_IPV6
+  /* Lookup default route. */
+  memset (&p6, 0, sizeof (struct prefix_ipv6));
+  p6.family = AF_INET6;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (table)
+    {
+      rn = route_node_lookup (table, (struct prefix *)&p6);
+      if (rn)
+       {
+         RNODE_FOREACH_RIB (rn, newrib)
+           if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
+               && newrib->distance != DISTANCE_INFINITY)
+             zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib);
+         route_unlock_node (rn);
+       }
+    }
+#endif /* HAVE_IPV6 */
+}
+
+/* Redistribute routes. */
+static void
+zebra_redistribute (struct zserv *client, int type, vrf_id_t vrf_id)
+{
+  struct rib *newrib;
+  struct route_table *table;
+  struct route_node *rn;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB (rn, newrib)
+        {
+          if (IS_ZEBRA_DEBUG_EVENT)
+            zlog_debug("%s: checking: selected=%d, type=%d, distance=%d, zebra_check_addr=%d",
+                       __func__, CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED),
+                       newrib->type, newrib->distance, zebra_check_addr (&rn->p));
+          if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
+              && newrib->type == type
+              && newrib->distance != DISTANCE_INFINITY
+              && zebra_check_addr (&rn->p))
+           {
+             client->redist_v4_add_cnt++;
+              zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib);
+            }
+        }
+
+#ifdef HAVE_IPV6
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB (rn, newrib)
+       if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED)
+           && newrib->type == type 
+           && newrib->distance != DISTANCE_INFINITY
+           && zebra_check_addr (&rn->p))
+         {
+           client->redist_v6_add_cnt++;
+           zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib);
+         }
+#endif /* HAVE_IPV6 */
+}
+
+void
+redistribute_add (struct prefix *p, struct rib *rib)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    {
+      if ((is_default (p) &&
+           vrf_bitmap_check (client->redist_default, rib->vrf_id))
+          || vrf_bitmap_check (client->redist[rib->type], rib->vrf_id))
+        {
+          if (p->family == AF_INET)
+           {
+             client->redist_v4_add_cnt++;
+             zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, p, rib);
+           }
+          if (p->family == AF_INET6)
+           {
+             client->redist_v6_add_cnt++;
+             zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib);
+           }
+        }
+    }
+}
+
+void
+redistribute_delete (struct prefix *p, struct rib *rib)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  /* Add DISTANCE_INFINITY check. */
+  if (rib->distance == DISTANCE_INFINITY)
+    return;
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    {
+      if ((is_default (p) &&
+           vrf_bitmap_check (client->redist_default, rib->vrf_id))
+          || vrf_bitmap_check (client->redist[rib->type], rib->vrf_id))
+       {
+         if (p->family == AF_INET)
+           zsend_route_multipath (ZEBRA_IPV4_ROUTE_DELETE, client, p, rib);
+#ifdef HAVE_IPV6
+         if (p->family == AF_INET6)
+           zsend_route_multipath (ZEBRA_IPV6_ROUTE_DELETE, client, p, rib);
+#endif /* HAVE_IPV6 */
+       }
+    }
+}
+
+void
+zebra_redistribute_add (int command, struct zserv *client, int length,
+    vrf_id_t vrf_id)
+{
+  int type;
+
+  type = stream_getc (client->ibuf);
+
+  if (type == 0 || type >= ZEBRA_ROUTE_MAX)
+    return;
+
+  if (! vrf_bitmap_check (client->redist[type], vrf_id))
+    {
+      vrf_bitmap_set (client->redist[type], vrf_id);
+      zebra_redistribute (client, type, vrf_id);
+    }
+}
+
+void
+zebra_redistribute_delete (int command, struct zserv *client, int length,
+    vrf_id_t vrf_id)
+{
+  int type;
+
+  type = stream_getc (client->ibuf);
+
+  if (type == 0 || type >= ZEBRA_ROUTE_MAX)
+    return;
+
+  vrf_bitmap_unset (client->redist[type], vrf_id);
+}
+
+void
+zebra_redistribute_default_add (int command, struct zserv *client, int length,
+    vrf_id_t vrf_id)
+{
+  vrf_bitmap_set (client->redist_default, vrf_id);
+  zebra_redistribute_default (client, vrf_id);
+}     
+
+void
+zebra_redistribute_default_delete (int command, struct zserv *client,
+    int length, vrf_id_t vrf_id)
+{
+  vrf_bitmap_unset (client->redist_default, vrf_id);
+}     
+
+/* Interface up information. */
+void
+zebra_interface_up_update (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("MESSAGE: ZEBRA_INTERFACE_UP %s", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo)
+      {
+        zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp);
+        zsend_interface_link_params (client, ifp);
+      }
+}
+
+/* Interface down information. */
+void
+zebra_interface_down_update (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("MESSAGE: ZEBRA_INTERFACE_DOWN %s", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    {
+      zsend_interface_update (ZEBRA_INTERFACE_DOWN, client, ifp);
+    }
+}
+
+/* Interface information update. */
+void
+zebra_interface_add_update (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADD %s", ifp->name);
+    
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo)
+      {
+       client->ifadd_cnt++;
+       zsend_interface_add (client, ifp);
+        zsend_interface_link_params (client, ifp);
+      }
+}
+
+void
+zebra_interface_delete_update (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("MESSAGE: ZEBRA_INTERFACE_DELETE %s", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo)
+      {
+       client->ifdel_cnt++;
+       zsend_interface_delete (client, ifp);
+      }
+}
+
+/* Interface address addition. */
+void
+zebra_interface_address_add_update (struct interface *ifp,
+                                   struct connected *ifc)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+  struct prefix *p;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    {
+      char buf[PREFIX_STRLEN];
+
+      p = ifc->address;
+      zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %s on %s",
+                 prefix2str (p, buf, sizeof(buf)),
+                 ifc->ifp->name);
+    }
+
+  if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
+    zlog_warn("WARNING: advertising address to clients that is not yet usable.");
+
+  router_id_add_address(ifc);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo && CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+      {
+       client->connected_rt_add_cnt++;
+       zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc);
+      }
+}
+
+/* Interface address deletion. */
+void
+zebra_interface_address_delete_update (struct interface *ifp,
+                                      struct connected *ifc)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+  struct prefix *p;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    {
+      char buf[PREFIX_STRLEN];
+
+      p = ifc->address;
+      zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %s on %s",
+                 prefix2str (p, buf, sizeof(buf)),
+                 ifc->ifp->name);
+    }
+
+  router_id_del_address(ifc);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo && CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL))
+      {
+       client->connected_rt_del_cnt++;
+       zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_DELETE, client, ifp, ifc);
+      }
+}
+
+/* Interface parameters update */
+void
+zebra_interface_parameters_update (struct interface *ifp)
+{
+  struct listnode *node, *nnode;
+  struct zserv *client;
+
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s", ifp->name);
+
+  for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+    if (client->ifinfo)
+      zsend_interface_link_params (client, ifp);
+}
diff --git a/zebra/redistribute.h b/zebra/redistribute.h
new file mode 100644 (file)
index 0000000..ce84009
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Redistribution Handler
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_REDISTRIBUTE_H
+#define _ZEBRA_REDISTRIBUTE_H
+
+#include "table.h"
+#include "zserv.h"
+
+extern void zebra_redistribute_add (int, struct zserv *, int, vrf_id_t);
+extern void zebra_redistribute_delete (int, struct zserv *, int, vrf_id_t);
+
+extern void zebra_redistribute_default_add (int, struct zserv *, int,
+    vrf_id_t);
+extern void zebra_redistribute_default_delete (int, struct zserv *, int,
+    vrf_id_t);
+
+extern void redistribute_add (struct prefix *, struct rib *);
+extern void redistribute_delete (struct prefix *, struct rib *);
+
+extern void zebra_interface_up_update (struct interface *);
+extern void zebra_interface_down_update (struct interface *);
+
+extern void zebra_interface_add_update (struct interface *);
+extern void zebra_interface_delete_update (struct interface *);
+
+extern void zebra_interface_address_add_update (struct interface *,
+                                               struct connected *);
+extern void zebra_interface_address_delete_update (struct interface *,
+                                                  struct connected *c);
+
+extern void zebra_interface_parameters_update (struct interface *);
+
+extern int zebra_check_addr (struct prefix *);
+
+extern int is_default (struct prefix *);
+
+#endif /* _ZEBRA_REDISTRIBUTE_H */
+
diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c
new file mode 100644 (file)
index 0000000..5584d12
--- /dev/null
@@ -0,0 +1,84 @@
+/* 
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+
+#include "zebra/redistribute.h"
+
+void zebra_redistribute_add (int a, struct zserv *b, int c,
+    vrf_id_t vrf_id)
+{ return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak zebra_redistribute_delete = zebra_redistribute_add
+#pragma weak zebra_redistribute_default_add = zebra_redistribute_add
+#pragma weak zebra_redistribute_default_delete = zebra_redistribute_add
+#else
+void zebra_redistribute_delete  (int a, struct zserv *b, int c,
+    vrf_id_t vrf_id)
+{ return; }
+void zebra_redistribute_default_add (int a, struct zserv *b, int c,
+    vrf_id_t vrf_id)
+{ return; }
+void zebra_redistribute_default_delete (int a, struct zserv *b, int c,
+    vrf_id_t vrf_id)
+{ return; }
+#endif
+
+void redistribute_add (struct prefix *a, struct rib *b)
+{ return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak redistribute_delete = redistribute_add
+#else
+void redistribute_delete (struct prefix *a, struct rib *b)
+{ return; }
+#endif
+
+void zebra_interface_up_update (struct interface *a)
+{ return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak zebra_interface_down_update = zebra_interface_up_update
+#pragma weak zebra_interface_add_update = zebra_interface_up_update
+#pragma weak zebra_interface_delete_update = zebra_interface_up_update
+#else
+void zebra_interface_down_update  (struct interface *a)
+{ return; }
+void zebra_interface_add_update (struct interface *a)
+{ return; }
+void zebra_interface_delete_update (struct interface *a)
+{ return; }
+#endif
+
+void zebra_interface_address_add_update (struct interface *a,
+                                               struct connected *b)
+{ return; }
+#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
+#pragma weak zebra_interface_address_delete_update = zebra_interface_address_add_update
+#else
+void zebra_interface_address_delete_update (struct interface *a,
+                                                struct connected *b)
+{ return; }
+#endif
+
+/* Interface parameters update */
+void zebra_interface_parameters_update (struct interface *ifp)
+{ return; };
diff --git a/zebra/rib.h b/zebra/rib.h
new file mode 100644 (file)
index 0000000..0191f57
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Routing Information Base header
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIB_H
+#define _ZEBRA_RIB_H
+
+#include "zebra.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "table.h"
+#include "queue.h"
+#include "nexthop.h"
+
+#define DISTANCE_INFINITY  255
+
+struct rib
+{
+  /* Link list. */
+  struct rib *next;
+  struct rib *prev;
+  
+  /* Nexthop structure */
+  struct nexthop *nexthop;
+  
+  /* Refrence count. */
+  unsigned long refcnt;
+  
+  /* Tag */
+  route_tag_t tag;
+
+  /* Uptime. */
+  time_t uptime;
+
+  /* Type fo this route. */
+  int type;
+
+  /* VRF identifier. */
+  vrf_id_t vrf_id;
+
+  /* Which routing table */
+  int table;                   
+
+  /* Metric */
+  u_int32_t metric;
+
+  /* MTU */
+  u_int32_t mtu;
+  u_int32_t nexthop_mtu;
+
+  /* Distance. */
+  u_char distance;
+
+  /* Flags of this route.
+   * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed
+   * to clients via Zserv
+   */
+  u_char flags;
+
+  /* RIB internal status */
+  u_char status;
+#define RIB_ENTRY_REMOVED      (1 << 0)
+#define RIB_ENTRY_CHANGED      (1 << 1)
+#define RIB_ENTRY_SELECTED_FIB (1 << 2)
+
+  /* Nexthop information. */
+  u_char nexthop_num;
+  u_char nexthop_active_num;
+  u_char nexthop_fib_num;
+};
+
+/* meta-queue structure:
+ * sub-queue 0: connected, kernel
+ * sub-queue 1: static
+ * sub-queue 2: RIP, RIPng, OSPF, OSPF6, IS-IS
+ * sub-queue 3: iBGP, eBGP
+ * sub-queue 4: any other origin (if any)
+ */
+#define MQ_SIZE 5
+struct meta_queue
+{
+  struct list *subq[MQ_SIZE];
+  u_int32_t size; /* sum of lengths of all subqueues */
+};
+
+/*
+ * Structure that represents a single destination (prefix).
+ */
+typedef struct rib_dest_t_
+{
+
+  /*
+   * Back pointer to the route node for this destination. This helps
+   * us get to the prefix that this structure is for.
+   */
+  struct route_node *rnode;
+
+  /*
+   * Doubly-linked list of routes for this prefix.
+   */
+  struct rib *routes;
+
+  /*
+   * Flags, see below.
+   */
+  u_int32_t flags;
+
+  /*
+   * Linkage to put dest on the FPM processing queue.
+   */
+  TAILQ_ENTRY(rib_dest_t_) fpm_q_entries;
+
+} rib_dest_t;
+
+#define RIB_ROUTE_QUEUED(x)    (1 << (x))
+
+/*
+ * The maximum qindex that can be used.
+ */
+#define ZEBRA_MAX_QINDEX        (MQ_SIZE - 1)
+
+/*
+ * This flag indicates that a given prefix has been 'advertised' to
+ * the FPM to be installed in the forwarding plane.
+ */
+#define RIB_DEST_SENT_TO_FPM   (1 << (ZEBRA_MAX_QINDEX + 1))
+
+/*
+ * This flag is set when we need to send an update to the FPM about a
+ * dest.
+ */
+#define RIB_DEST_UPDATE_FPM    (1 << (ZEBRA_MAX_QINDEX + 2))
+
+/*
+ * Macro to iterate over each route for a destination (prefix).
+ */
+#define RIB_DEST_FOREACH_ROUTE(dest, rib)                              \
+  for ((rib) = (dest) ? (dest)->routes : NULL; (rib); (rib) = (rib)->next)
+
+/*
+ * Same as above, but allows the current node to be unlinked.
+ */
+#define RIB_DEST_FOREACH_ROUTE_SAFE(dest, rib, next)   \
+  for ((rib) = (dest) ? (dest)->routes : NULL;         \
+       (rib) && ((next) = (rib)->next, 1);             \
+       (rib) = (next))
+
+#define RNODE_FOREACH_RIB(rn, rib)                             \
+  RIB_DEST_FOREACH_ROUTE (rib_dest_from_rnode (rn), rib)
+
+#define RNODE_FOREACH_RIB_SAFE(rn, rib, next)                          \
+  RIB_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode (rn), rib, next)
+
+/* Static route information. */
+struct static_route
+{
+  /* For linked list. */
+  struct static_route *prev;
+  struct static_route *next;
+
+  /* VRF identifier. */
+  vrf_id_t vrf_id;
+
+  /* Administrative distance. */
+  u_char distance;
+
+  /* Tag */
+  route_tag_t tag;
+
+  /* Flag for this static route's type. */
+  u_char type;
+#define STATIC_IPV4_GATEWAY          1
+#define STATIC_IPV4_IFNAME           2
+#define STATIC_IPV4_BLACKHOLE        3
+#define STATIC_IPV6_GATEWAY          4
+#define STATIC_IPV6_GATEWAY_IFNAME   5
+#define STATIC_IPV6_IFNAME           6
+
+  /* Nexthop value. */
+  union g_addr addr;
+  char *ifname;
+
+  /* bit flags */
+  u_char flags;
+/*
+ see ZEBRA_FLAG_REJECT
+     ZEBRA_FLAG_BLACKHOLE
+ */
+};
+
+/* The following for loop allows to iterate over the nexthop
+ * structure of routes.
+ *
+ * We have to maintain quite a bit of state:
+ *
+ * nexthop:   The pointer to the current nexthop, either in the
+ *            top-level chain or in the resolved chain of ni.
+ * tnexthop:  The pointer to the current nexthop in the top-level
+ *            nexthop chain.
+ * recursing: Information if nh currently is in the top-level chain
+ *            (0) or in a resolved chain (1).
+ *
+ * Initialization: Set `nexthop' and `tnexthop' to the head of the
+ * top-level chain. As nexthop is in the top level chain, set recursing
+ * to 0.
+ *
+ * Iteration check: Check that the `nexthop' pointer is not NULL.
+ *
+ * Iteration step: This is the tricky part. Check if `nexthop' has
+ * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' is in
+ * the top level chain and has at least one nexthop attached to
+ * `nexthop->resolved'. As we want to descend into `nexthop->resolved',
+ * set `recursing' to 1 and set `nexthop' to `nexthop->resolved'.
+ * `tnexthop' is left alone in that case so we can remember which nexthop
+ * in the top level chain we are currently handling.
+ *
+ * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
+ * current chain. If we are recursing, `nexthop' will be set to
+ * `nexthop->next' and `tnexthop' will be left alone. If we are not
+ * recursing, both `tnexthop' and `nexthop' will be set to `nexthop->next'
+ * as we are progressing in the top level chain.
+ *   If we encounter `nexthop->next == NULL', we will clear the `recursing'
+ * flag as we arived either at the end of the resolved chain or at the end
+ * of the top level chain. In both cases, we set `tnexthop' and `nexthop'
+ * to `tnexthop->next', progressing to the next position in the top-level
+ * chain and possibly to its end marked by NULL.
+ */
+#define ALL_NEXTHOPS_RO(head, nexthop, tnexthop, recursing) \
+  (tnexthop) = (nexthop) = (head), (recursing) = 0; \
+  (nexthop); \
+  (nexthop) = CHECK_FLAG((nexthop)->flags, NEXTHOP_FLAG_RECURSIVE) \
+    ? (((recursing) = 1), (nexthop)->resolved) \
+    : ((nexthop)->next ? ((recursing) ? (nexthop)->next \
+                                      : ((tnexthop) = (nexthop)->next)) \
+                       : (((recursing) = 0),((tnexthop) = (tnexthop)->next)))
+
+/* Structure holding nexthop & VRF identifier,
+ * used for applying the route-map. */
+struct nexthop_vrfid
+{
+  struct nexthop *nexthop;
+  vrf_id_t vrf_id;
+};
+
+
+#if defined (HAVE_RTADV)
+/* Structure which hold status of router advertisement. */
+struct rtadv
+{
+  int sock;
+
+  int adv_if_count;
+  int adv_msec_if_count;
+
+  struct thread *ra_read;
+  struct thread *ra_timer;
+};
+#endif /* HAVE_RTADV */
+
+#ifdef HAVE_NETLINK
+/* Socket interface to kernel */
+struct nlsock
+{
+  int sock;
+  int seq;
+  struct sockaddr_nl snl;
+  const char *name;
+};
+#endif
+
+/* Routing table instance.  */
+struct zebra_vrf
+{
+  /* Identifier. */
+  vrf_id_t vrf_id;
+
+  /* Routing table name.  */
+  char *name;
+
+  /* Description.  */
+  char *desc;
+
+  /* FIB identifier.  */
+  u_char fib_id;
+
+  /* Routing table.  */
+  struct route_table *table[AFI_MAX][SAFI_MAX];
+
+  /* Static route configuration.  */
+  struct route_table *stable[AFI_MAX][SAFI_MAX];
+
+#ifdef HAVE_NETLINK
+  struct nlsock netlink;     /* kernel messages */
+  struct nlsock netlink_cmd; /* command channel */
+  struct thread *t_netlink;
+#endif
+
+  /* 2nd pointer type used primarily to quell a warning on
+   * ALL_LIST_ELEMENTS_RO
+   */
+  struct list _rid_all_sorted_list;
+  struct list _rid_lo_sorted_list;
+  struct list *rid_all_sorted_list;
+  struct list *rid_lo_sorted_list;
+  struct prefix rid_user_assigned;
+
+#if defined (HAVE_RTADV)
+  struct rtadv rtadv;
+#endif /* HAVE_RTADV */
+
+  /* Recursive Nexthop table */
+  struct route_table *rnh_table[AFI_MAX];
+};
+
+/*
+ * rib_table_info_t
+ *
+ * Structure that is hung off of a route_table that holds information about
+ * the table.
+ */
+typedef struct rib_table_info_t_
+{
+
+  /*
+   * Back pointer to zebra_vrf.
+   */
+  struct zebra_vrf *zvrf;
+  afi_t afi;
+  safi_t safi;
+
+} rib_table_info_t;
+
+typedef enum
+{
+  RIB_TABLES_ITER_S_INIT,
+  RIB_TABLES_ITER_S_ITERATING,
+  RIB_TABLES_ITER_S_DONE
+} rib_tables_iter_state_t;
+
+/*
+ * Structure that holds state for iterating over all tables in the
+ * Routing Information Base.
+ */
+typedef struct rib_tables_iter_t_
+{
+  vrf_id_t vrf_id;
+  int afi_safi_ix;
+
+  rib_tables_iter_state_t state;
+} rib_tables_iter_t;
+
+/* RPF lookup behaviour */
+enum multicast_mode
+{
+  MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */
+  MCAST_MRIB_ONLY,     /* MRIB only */
+  MCAST_URIB_ONLY,     /* URIB only */
+  MCAST_MIX_MRIB_FIRST,        /* MRIB, if nothing at all then URIB */
+  MCAST_MIX_DISTANCE,  /* MRIB & URIB, lower distance wins */
+  MCAST_MIX_PFXLEN,    /* MRIB & URIB, longer prefix wins */
+                       /* on equal value, MRIB wins for last 2 */
+};
+
+extern void multicast_mode_ipv4_set (enum multicast_mode mode);
+extern enum multicast_mode multicast_mode_ipv4_get (void);
+
+extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type);
+extern struct nexthop *rib_nexthop_ifindex_add (struct rib *, ifindex_t);
+extern struct nexthop *rib_nexthop_ifname_add (struct rib *, char *);
+extern struct nexthop *rib_nexthop_blackhole_add (struct rib *);
+extern struct nexthop *rib_nexthop_ipv4_add (struct rib *, struct in_addr *,
+                                            struct in_addr *);
+extern struct nexthop *rib_nexthop_ipv4_ifindex_add (struct rib *,
+                                                    struct in_addr *,
+                                                    struct in_addr *,
+                                                    ifindex_t);
+
+extern void rib_nexthop_add (struct rib *rib, struct nexthop *nexthop);
+
+extern int nexthop_has_fib_child(struct nexthop *);
+extern void rib_lookup_and_dump (struct prefix_ipv4 *);
+#define rib_dump(prefix ,rib) _rib_dump(__func__, prefix, rib)
+extern void _rib_dump (const char *,
+                      union prefix46constptr, const struct rib *);
+extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *,
+                                  vrf_id_t);
+#define ZEBRA_RIB_LOOKUP_ERROR -1
+#define ZEBRA_RIB_FOUND_EXACT 0
+#define ZEBRA_RIB_FOUND_NOGATE 1
+#define ZEBRA_RIB_FOUND_CONNECTED 2
+#define ZEBRA_RIB_NOTFOUND 3
+
+extern struct nexthop *rib_nexthop_ipv6_add (struct rib *, struct in6_addr *);
+extern struct nexthop *rib_nexthop_ipv6_ifindex_add (struct rib *,
+                                                     struct in6_addr *,
+                                                     ifindex_t);
+
+extern struct zebra_vrf *zebra_vrf_lookup (vrf_id_t vrf_id);
+extern struct zebra_vrf *zebra_vrf_alloc (vrf_id_t);
+extern struct route_table *zebra_vrf_table (afi_t, safi_t, vrf_id_t);
+extern struct route_table *zebra_vrf_static_table (afi_t, safi_t, vrf_id_t);
+
+/* NOTE:
+ * All rib_add_ipv[46]* functions will not just add prefix into RIB, but
+ * also implicitly withdraw equal prefix of same type. */
+extern int rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, 
+                        struct in_addr *gate, struct in_addr *src,
+                        ifindex_t ifindex, vrf_id_t vrf_id, int table_id,
+                        u_int32_t, u_int32_t, u_char, safi_t);
+
+extern int rib_add_ipv4_multipath (struct prefix_ipv4 *, struct rib *, safi_t);
+
+extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
+                           struct in_addr *gate, ifindex_t ifindex, 
+                           vrf_id_t, safi_t safi);
+
+extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi,
+                                       int skip_bgp, struct route_node **rn_out,
+                                       vrf_id_t);
+extern struct rib *rib_match_ipv4_multicast (struct in_addr addr,
+                                            struct route_node **rn_out,
+                                            vrf_id_t);
+
+extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *, vrf_id_t);
+
+extern void rib_update (vrf_id_t);
+extern void rib_weed_tables (void);
+extern void rib_sweep_route (void);
+extern void rib_close_table (struct route_table *);
+extern void rib_close (void);
+extern void rib_init (void);
+extern unsigned long rib_score_proto (u_char proto);
+
+extern int
+static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate,
+                     const char *ifname, u_char flags, route_tag_t, 
+                     u_char distance, vrf_id_t vrf_id);
+extern int
+static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate,
+                        const char *ifname, route_tag_t tag, u_char distance,
+                        vrf_id_t vrf_id);
+
+extern int
+rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
+             struct in6_addr *gate, ifindex_t ifindex, vrf_id_t vrf_id,
+             int table_id, u_int32_t metric, u_int32_t mtu,
+             u_char distance, safi_t safi);
+
+extern int
+rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
+                struct in6_addr *gate, ifindex_t ifindex, vrf_id_t vrf_id, safi_t safi);
+
+extern struct rib *rib_lookup_ipv6 (struct in6_addr *, vrf_id_t);
+
+extern struct rib *rib_match_ipv6 (struct in6_addr *, vrf_id_t);
+
+extern struct route_table *rib_table_ipv6;
+
+extern int
+static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
+                const char *ifname, u_char flags, route_tag_t, 
+                u_char distance, vrf_id_t vrf_id);
+
+extern int
+rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t);
+
+extern int
+static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
+                   const char *ifname, route_tag_t, u_char distance, 
+                   vrf_id_t vrf_id);
+
+extern int rib_gc_dest (struct route_node *rn);
+extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter);
+
+/*
+ * Inline functions.
+ */
+
+/*
+ * rib_table_info
+ */
+static inline rib_table_info_t *
+rib_table_info (struct route_table *table)
+{
+  return (rib_table_info_t *) table->info;
+}
+
+/*
+ * rib_dest_from_rnode
+ */
+static inline rib_dest_t *
+rib_dest_from_rnode (struct route_node *rn)
+{
+  return (rib_dest_t *) rn->info;
+}
+
+/*
+ * rnode_to_ribs
+ *
+ * Returns a pointer to the list of routes corresponding to the given
+ * route_node.
+ */
+static inline struct rib *
+rnode_to_ribs (struct route_node *rn)
+{
+  rib_dest_t *dest;
+
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
+    return NULL;
+
+  return dest->routes;
+}
+
+/*
+ * rib_dest_prefix
+ */
+static inline struct prefix *
+rib_dest_prefix (rib_dest_t *dest)
+{
+  return &dest->rnode->p;
+}
+
+/*
+ * rib_dest_af
+ *
+ * Returns the address family that the destination is for.
+ */
+static inline u_char
+rib_dest_af (rib_dest_t *dest)
+{
+  return dest->rnode->p.family;
+}
+
+/*
+ * rib_dest_table
+ */
+static inline struct route_table *
+rib_dest_table (rib_dest_t *dest)
+{
+  return dest->rnode->table;
+}
+
+/*
+ * rib_dest_vrf
+ */
+static inline struct zebra_vrf *
+rib_dest_vrf (rib_dest_t *dest)
+{
+  return rib_table_info (rib_dest_table (dest))->zvrf;
+}
+
+/*
+ * rib_tables_iter_init
+ */
+static inline void
+rib_tables_iter_init (rib_tables_iter_t *iter)
+
+{
+  memset (iter, 0, sizeof (*iter));
+  iter->state = RIB_TABLES_ITER_S_INIT;
+}
+
+/*
+ * rib_tables_iter_started
+ *
+ * Returns TRUE if this iterator has started iterating over the set of
+ * tables.
+ */
+static inline int
+rib_tables_iter_started (rib_tables_iter_t *iter)
+{
+  return iter->state != RIB_TABLES_ITER_S_INIT;
+}
+
+/*
+ * rib_tables_iter_cleanup
+ */
+static inline void
+rib_tables_iter_cleanup (rib_tables_iter_t *iter)
+{
+  iter->state = RIB_TABLES_ITER_S_DONE;
+}
+
+#endif /*_ZEBRA_RIB_H */
diff --git a/zebra/router-id.c b/zebra/router-id.c
new file mode 100644 (file)
index 0000000..a4eb73a
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Router ID for zebra daemon.
+ *
+ * Copyright (C) 2004 James R. Leu 
+ *
+ * This file is part of Quagga routing suite.
+ *
+ * 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "stream.h"
+#include "command.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "network.h"
+#include "log.h"
+#include "table.h"
+#include "rib.h"
+#include "vrf.h"
+
+#include "zebra/zserv.h"
+#include "zebra/router-id.h"
+#include "zebra/redistribute.h"
+
+/* master zebra server structure */
+extern struct zebra_t zebrad;
+
+static struct connected *
+router_id_find_node (struct list *l, struct connected *ifc)
+{
+  struct listnode *node;
+  struct connected *c;
+
+  for (ALL_LIST_ELEMENTS_RO (l, node, c))
+    if (prefix_same (ifc->address, c->address))
+      return c;
+
+  return NULL;
+}
+
+static int
+router_id_bad_address (struct connected *ifc)
+{
+  if (ifc->address->family != AF_INET)
+    return 1;
+  
+  /* non-redistributable addresses shouldn't be used for RIDs either */
+  if (!zebra_check_addr (ifc->address))
+    return 1;
+  
+  return 0;
+}
+
+void
+router_id_get (struct prefix *p, vrf_id_t vrf_id)
+{
+  struct listnode *node;
+  struct connected *c;
+  struct zebra_vrf *zvrf = vrf_info_get (vrf_id);
+
+  p->u.prefix4.s_addr = 0;
+  p->family = AF_INET;
+  p->prefixlen = 32;
+
+  if (zvrf->rid_user_assigned.u.prefix4.s_addr)
+    p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr;
+  else if (!list_isempty (zvrf->rid_lo_sorted_list))
+    {
+      node = listtail (zvrf->rid_lo_sorted_list);
+      c = listgetdata (node);
+      p->u.prefix4.s_addr = c->address->u.prefix4.s_addr;
+    }
+  else if (!list_isempty (zvrf->rid_all_sorted_list))
+    {
+      node = listtail (zvrf->rid_all_sorted_list);
+      c = listgetdata (node);
+      p->u.prefix4.s_addr = c->address->u.prefix4.s_addr;
+    }
+}
+
+static void
+router_id_set (struct prefix *p, vrf_id_t vrf_id)
+{
+  struct prefix p2;
+  struct listnode *node;
+  struct zserv *client;
+  struct zebra_vrf *zvrf;
+
+  if (p->u.prefix4.s_addr == 0) /* unset */
+    {
+      zvrf = vrf_info_lookup (vrf_id);
+      if (! zvrf)
+        return;
+    }
+  else /* set */
+    zvrf = vrf_info_get (vrf_id);
+
+  zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr;
+
+  router_id_get (&p2, vrf_id);
+
+  for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
+    zsend_router_id_update (client, &p2, vrf_id);
+}
+
+void
+router_id_add_address (struct connected *ifc)
+{
+  struct list *l = NULL;
+  struct listnode *node;
+  struct prefix before;
+  struct prefix after;
+  struct zserv *client;
+  struct zebra_vrf *zvrf = vrf_info_get (ifc->ifp->vrf_id);
+
+  if (router_id_bad_address (ifc))
+    return;
+
+  router_id_get (&before, zvrf->vrf_id);
+
+  if (!strncmp (ifc->ifp->name, "lo", 2)
+      || !strncmp (ifc->ifp->name, "dummy", 5))
+    l = zvrf->rid_lo_sorted_list;
+  else
+    l = zvrf->rid_all_sorted_list;
+  
+  if (!router_id_find_node (l, ifc))
+    listnode_add_sort (l, ifc);
+
+  router_id_get (&after, zvrf->vrf_id);
+
+  if (prefix_same (&before, &after))
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
+    zsend_router_id_update (client, &after, zvrf->vrf_id);
+}
+
+void
+router_id_del_address (struct connected *ifc)
+{
+  struct connected *c;
+  struct list *l;
+  struct prefix after;
+  struct prefix before;
+  struct listnode *node;
+  struct zserv *client;
+  struct zebra_vrf *zvrf = vrf_info_get (ifc->ifp->vrf_id);
+
+  if (router_id_bad_address (ifc))
+    return;
+
+  router_id_get (&before, zvrf->vrf_id);
+
+  if (!strncmp (ifc->ifp->name, "lo", 2)
+      || !strncmp (ifc->ifp->name, "dummy", 5))
+    l = zvrf->rid_lo_sorted_list;
+  else
+    l = zvrf->rid_all_sorted_list;
+
+  if ((c = router_id_find_node (l, ifc)))
+    listnode_delete (l, c);
+
+  router_id_get (&after, zvrf->vrf_id);
+
+  if (prefix_same (&before, &after))
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
+    zsend_router_id_update (client, &after, zvrf->vrf_id);
+}
+
+void
+router_id_write (struct vty *vty)
+{
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      if (zvrf->rid_user_assigned.u.prefix4.s_addr)
+        {
+          if (zvrf->vrf_id == VRF_DEFAULT)
+            vty_out (vty, "router-id %s%s",
+                     inet_ntoa (zvrf->rid_user_assigned.u.prefix4),
+                     VTY_NEWLINE);
+          else
+            vty_out (vty, "router-id %s vrf %u%s",
+                     inet_ntoa (zvrf->rid_user_assigned.u.prefix4),
+                     zvrf->vrf_id,
+                     VTY_NEWLINE);
+        }
+}
+
+DEFUN (router_id,
+       router_id_cmd,
+       "router-id A.B.C.D",
+       "Manually set the router-id\n"
+       "IP address to use for router-id\n")
+{
+  struct prefix rid;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  rid.u.prefix4.s_addr = inet_addr (argv[0]);
+  if (!rid.u.prefix4.s_addr)
+    return CMD_WARNING;
+
+  rid.prefixlen = 32;
+  rid.family = AF_INET;
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  router_id_set (&rid, vrf_id);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (router_id,
+       router_id_vrf_cmd,
+       "router-id A.B.C.D " VRF_CMD_STR,
+       "Manually set the router-id\n"
+       "IP address to use for router-id\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_router_id,
+       no_router_id_cmd,
+       "no router-id",
+       NO_STR
+       "Remove the manually configured router-id\n")
+{
+  struct prefix rid;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  rid.u.prefix4.s_addr = 0;
+  rid.prefixlen = 0;
+  rid.family = AF_INET;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  router_id_set (&rid, vrf_id);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_router_id,
+       no_router_id_vrf_cmd,
+       "no router-id " VRF_CMD_STR,
+       NO_STR
+       "Remove the manually configured router-id\n"
+       VRF_CMD_HELP_STR)
+
+static int
+router_id_cmp (void *a, void *b)
+{
+  const struct connected *ifa = (const struct connected *)a;
+  const struct connected *ifb = (const struct connected *)b;
+
+  return IPV4_ADDR_CMP(&ifa->address->u.prefix4.s_addr,&ifb->address->u.prefix4.s_addr);
+}
+
+void
+router_id_cmd_init (void)
+{
+  install_element (CONFIG_NODE, &router_id_cmd);
+  install_element (CONFIG_NODE, &no_router_id_cmd);
+  install_element (CONFIG_NODE, &router_id_vrf_cmd);
+  install_element (CONFIG_NODE, &no_router_id_vrf_cmd);
+}
+
+void
+router_id_init (struct zebra_vrf *zvrf)
+{
+  zvrf->rid_all_sorted_list = &zvrf->_rid_all_sorted_list;
+  zvrf->rid_lo_sorted_list = &zvrf->_rid_lo_sorted_list;
+
+  memset (zvrf->rid_all_sorted_list, 0, sizeof (zvrf->_rid_all_sorted_list));
+  memset (zvrf->rid_lo_sorted_list, 0, sizeof (zvrf->_rid_lo_sorted_list));
+  memset (&zvrf->rid_user_assigned, 0, sizeof (zvrf->rid_user_assigned));
+
+  zvrf->rid_all_sorted_list->cmp = router_id_cmp;
+  zvrf->rid_lo_sorted_list->cmp = router_id_cmp;
+
+  zvrf->rid_user_assigned.family = AF_INET;
+  zvrf->rid_user_assigned.prefixlen = 32;
+}
diff --git a/zebra/router-id.h b/zebra/router-id.h
new file mode 100644 (file)
index 0000000..46d300e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Router ID for zebra daemon.
+ *
+ * Copyright (C) 2004 James R. Leu
+ *
+ * This file is part of Quagga routing suite.
+ *
+ * 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ROUTER_ID_H_
+#define _ROUTER_ID_H_
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "if.h"
+
+extern void router_id_add_address(struct connected *);
+extern void router_id_del_address(struct connected *);
+extern void router_id_init(struct zebra_vrf *);
+extern void router_id_cmd_init(void);
+extern void router_id_write(struct vty *);
+extern void router_id_get(struct prefix *, vrf_id_t);
+
+#endif
diff --git a/zebra/rt.h b/zebra/rt.h
new file mode 100644 (file)
index 0000000..8c1c476
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * kernel routing table update prototype.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RT_H
+#define _ZEBRA_RT_H
+
+#include "prefix.h"
+#include "if.h"
+#include "zebra/rib.h"
+
+extern int kernel_route_rib (struct prefix *, struct rib *, struct rib *);
+extern int kernel_add_route (struct prefix_ipv4 *, struct in_addr *, int, int);
+extern int kernel_address_add_ipv4 (struct interface *, struct connected *);
+extern int kernel_address_delete_ipv4 (struct interface *, struct connected *);
+
+#endif /* _ZEBRA_RT_H */
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
new file mode 100644 (file)
index 0000000..fc6e373
--- /dev/null
@@ -0,0 +1,2069 @@
+/* Kernel routing table updates using netlink over GNU/Linux system.
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+#include <net/if_arp.h>
+
+/* Hack for GNU libc version 2. */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC      0x20
+#endif /* MSG_TRUNC */
+
+#include "linklist.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "connected.h"
+#include "table.h"
+#include "memory.h"
+#include "rib.h"
+#include "thread.h"
+#include "privs.h"
+#include "vrf.h"
+#include "nexthop.h"
+
+#include "zebra/zserv.h"
+#include "zebra/rt.h"
+#include "zebra/redistribute.h"
+#include "zebra/interface.h"
+#include "zebra/debug.h"
+
+#include "rt_netlink.h"
+
+static const struct message nlmsg_str[] = {
+  {RTM_NEWROUTE, "RTM_NEWROUTE"},
+  {RTM_DELROUTE, "RTM_DELROUTE"},
+  {RTM_GETROUTE, "RTM_GETROUTE"},
+  {RTM_NEWLINK,  "RTM_NEWLINK"},
+  {RTM_DELLINK,  "RTM_DELLINK"},
+  {RTM_GETLINK,  "RTM_GETLINK"},
+  {RTM_NEWADDR,  "RTM_NEWADDR"},
+  {RTM_DELADDR,  "RTM_DELADDR"},
+  {RTM_GETADDR,  "RTM_GETADDR"},
+  {0, NULL}
+};
+
+extern struct zebra_t zebrad;
+
+extern struct zebra_privs_t zserv_privs;
+
+extern u_int32_t nl_rcvbufsize;
+
+static struct {
+  char *p;
+  size_t size;
+} nl_rcvbuf;
+
+/* Note: on netlink systems, there should be a 1-to-1 mapping between interface
+   names and ifindex values. */
+static void
+set_ifindex(struct interface *ifp, ifindex_t ifi_index)
+{
+  struct interface *oifp;
+
+  if (((oifp = if_lookup_by_index(ifi_index)) != NULL) && (oifp != ifp))
+    {
+      if (ifi_index == IFINDEX_INTERNAL)
+        zlog_err("Netlink is setting interface %s ifindex to reserved "
+                "internal value %u", ifp->name, ifi_index);
+      else
+        {
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           zlog_debug("interface index %d was renamed from %s to %s",
+                      ifi_index, oifp->name, ifp->name);
+         if (if_is_up(oifp))
+           zlog_err("interface rename detected on up interface: index %d "
+                    "was renamed from %s to %s, results are uncertain!", 
+                    ifi_index, oifp->name, ifp->name);
+         if_delete_update(oifp);
+        }
+    }
+  ifp->ifindex = ifi_index;
+}
+
+#ifndef SO_RCVBUFFORCE
+#define SO_RCVBUFFORCE  (33)
+#endif
+
+static int
+netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
+{
+  u_int32_t oldsize;
+  socklen_t newlen = sizeof(newsize);
+  socklen_t oldlen = sizeof(oldsize);
+  int ret;
+
+  ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
+      return -1;
+    }
+
+  /* Try force option (linux >= 2.6.14) and fall back to normal set */
+  if ( zserv_privs.change (ZPRIVS_RAISE) )
+    zlog_err ("routing_socket: Can't raise privileges");
+  ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize,
+                  sizeof(nl_rcvbufsize));
+  if ( zserv_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("routing_socket: Can't lower privileges");
+  if (ret < 0)
+     ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
+                     sizeof(nl_rcvbufsize));
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
+      return -1;
+    }
+
+  ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
+      return -1;
+    }
+
+  zlog (NULL, LOG_INFO,
+       "Setting netlink socket receive buffer size: %u -> %u",
+       oldsize, newsize);
+  return 0;
+}
+
+/* Make socket for Linux netlink interface. */
+static int
+netlink_socket (struct nlsock *nl, unsigned long groups, vrf_id_t vrf_id)
+{
+  int ret;
+  struct sockaddr_nl snl;
+  int sock;
+  int namelen;
+  int save_errno;
+
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    {
+      zlog (NULL, LOG_ERR, "Can't raise privileges");
+      return -1;
+    }
+
+  sock = vrf_socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, vrf_id);
+  if (sock < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
+            safe_strerror (errno));
+      return -1;
+    }
+
+  memset (&snl, 0, sizeof snl);
+  snl.nl_family = AF_NETLINK;
+  snl.nl_groups = groups;
+
+  /* Bind the socket to the netlink structure for anything. */
+  ret = bind (sock, (struct sockaddr *) &snl, sizeof snl);
+  save_errno = errno;
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s",
+            nl->name, snl.nl_groups, safe_strerror (save_errno));
+      close (sock);
+      return -1;
+    }
+
+  /* multiple netlink sockets will have different nl_pid */
+  namelen = sizeof snl;
+  ret = getsockname (sock, (struct sockaddr *) &snl, (socklen_t *) &namelen);
+  if (ret < 0 || namelen != sizeof snl)
+    {
+      zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name,
+            safe_strerror (errno));
+      close (sock);
+      return -1;
+    }
+
+  nl->snl = snl;
+  nl->sock = sock;
+  return ret;
+}
+
+/* Get type specified information from netlink. */
+static int
+netlink_request (int family, int type, struct nlsock *nl)
+{
+  int ret;
+  struct sockaddr_nl snl;
+  int save_errno;
+
+  struct
+  {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g;
+  } req;
+
+
+  /* Check netlink socket. */
+  if (nl->sock < 0)
+    {
+      zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name);
+      return -1;
+    }
+
+  memset (&snl, 0, sizeof snl);
+  snl.nl_family = AF_NETLINK;
+
+  memset (&req, 0, sizeof req);
+  req.nlh.nlmsg_len = sizeof req;
+  req.nlh.nlmsg_type = type;
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+  req.nlh.nlmsg_pid = nl->snl.nl_pid;
+  req.nlh.nlmsg_seq = ++nl->seq;
+  req.g.rtgen_family = family;
+
+  /* linux appears to check capabilities on every message 
+   * have to raise caps for every message sent
+   */
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    {
+      zlog (NULL, LOG_ERR, "Can't raise privileges");
+      return -1;
+    }
+
+  ret = sendto (nl->sock, (void *) &req, sizeof req, 0,
+                (struct sockaddr *) &snl, sizeof snl);
+  save_errno = errno;
+
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  if (ret < 0)
+    {
+      zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name,
+            safe_strerror (save_errno));
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Receive message from netlink interface and pass those information
+   to the given function. */
+static int
+netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
+                                   vrf_id_t),
+                    struct nlsock *nl, struct zebra_vrf *zvrf)
+{
+  int status;
+  int ret = 0;
+  int error;
+
+  while (1)
+    {
+      struct iovec iov = {
+        .iov_base = nl_rcvbuf.p,
+        .iov_len = nl_rcvbuf.size,
+      };
+      struct sockaddr_nl snl;
+      struct msghdr msg = {
+        .msg_name = (void *) &snl,
+        .msg_namelen = sizeof snl,
+        .msg_iov = &iov,
+        .msg_iovlen = 1
+      };
+      struct nlmsghdr *h;
+
+      status = recvmsg (nl->sock, &msg, 0);
+      if (status < 0)
+        {
+          if (errno == EINTR)
+            continue;
+          if (errno == EWOULDBLOCK || errno == EAGAIN)
+            break;
+          zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
+               nl->name, safe_strerror(errno));
+          continue;
+        }
+
+      if (status == 0)
+        {
+          zlog (NULL, LOG_ERR, "%s EOF", nl->name);
+          return -1;
+        }
+
+      if (msg.msg_namelen != sizeof snl)
+        {
+          zlog (NULL, LOG_ERR, "%s sender address length error: length %d",
+                nl->name, msg.msg_namelen);
+          return -1;
+        }
+      
+      for (h = (struct nlmsghdr *) nl_rcvbuf.p; 
+           NLMSG_OK (h, (unsigned int) status);
+           h = NLMSG_NEXT (h, status))
+        {
+          /* Finish of reading. */
+          if (h->nlmsg_type == NLMSG_DONE)
+            return ret;
+
+          /* Error handling. */
+          if (h->nlmsg_type == NLMSG_ERROR)
+            {
+              struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
+             int errnum = err->error;
+             int msg_type = err->msg.nlmsg_type;
+
+              /* If the error field is zero, then this is an ACK */
+              if (err->error == 0)
+                {
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug ("%s: %s ACK: type=%s(%u), seq=%u, pid=%u",
+                                 __FUNCTION__, nl->name,
+                                 lookup (nlmsg_str, err->msg.nlmsg_type),
+                                 err->msg.nlmsg_type, err->msg.nlmsg_seq,
+                                 err->msg.nlmsg_pid);
+                    }
+
+                  /* return if not a multipart message, otherwise continue */
+                  if (!(h->nlmsg_flags & NLM_F_MULTI))
+                    {
+                      return 0;
+                    }
+                  continue;
+                }
+
+              if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
+                {
+                  zlog (NULL, LOG_ERR, "%s error: message truncated",
+                        nl->name);
+                  return -1;
+                }
+
+              /* Deal with errors that occur because of races in link handling */
+             if (nl == &zvrf->netlink_cmd
+                 && ((msg_type == RTM_DELROUTE &&
+                      (-errnum == ENODEV || -errnum == ESRCH))
+                     || (msg_type == RTM_NEWROUTE && -errnum == EEXIST)))
+               {
+                 if (IS_ZEBRA_DEBUG_KERNEL)
+                   zlog_debug ("%s: error: %s type=%s(%u), seq=%u, pid=%u",
+                               nl->name, safe_strerror (-errnum),
+                               lookup (nlmsg_str, msg_type),
+                               msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+                 return 0;
+               }
+
+             zlog_err ("%s error: %s, type=%s(%u), seq=%u, pid=%u",
+                       nl->name, safe_strerror (-errnum),
+                       lookup (nlmsg_str, msg_type),
+                       msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+              return -1;
+            }
+
+          /* OK we got netlink message. */
+          if (IS_ZEBRA_DEBUG_KERNEL)
+            zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u",
+                       nl->name,
+                       lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type,
+                       h->nlmsg_seq, h->nlmsg_pid);
+
+          /* skip unsolicited messages originating from command socket
+           * linux sets the originators port-id for {NEW|DEL}ADDR messages,
+           * so this has to be checked here. */
+          if (nl != &zvrf->netlink_cmd
+              && h->nlmsg_pid == zvrf->netlink_cmd.snl.nl_pid
+              && (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR))
+            {
+              if (IS_ZEBRA_DEBUG_KERNEL)
+                zlog_debug ("netlink_parse_info: %s packet comes from %s",
+                            zvrf->netlink_cmd.name, nl->name);
+              continue;
+            }
+
+          error = (*filter) (&snl, h, zvrf->vrf_id);
+          if (error < 0)
+            {
+              zlog (NULL, LOG_ERR, "%s filter function error", nl->name);
+              ret = error;
+            }
+        }
+
+      /* After error care. */
+      if (msg.msg_flags & MSG_TRUNC)
+        {
+          zlog (NULL, LOG_ERR, "%s error: message truncated!", nl->name);
+          zlog (NULL, LOG_ERR, 
+                "Must restart with larger --nl-bufsize value!");
+          continue;
+        }
+      if (status)
+        {
+          zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name,
+                status);
+          return -1;
+        }
+    }
+  return ret;
+}
+
+/* Utility function for parse rtattr. */
+static void
+netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta,
+                      int len)
+{
+  while (RTA_OK (rta, len))
+    {
+      if (rta->rta_type <= max)
+        tb[rta->rta_type] = rta;
+      rta = RTA_NEXT (rta, len);
+    }
+}
+
+/* Utility function to parse hardware link-layer address and update ifp */
+static void
+netlink_interface_update_hw_addr (struct rtattr **tb, struct interface *ifp)
+{
+  int i;
+
+  if (tb[IFLA_ADDRESS])
+    {
+      int hw_addr_len;
+
+      hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]);
+
+      if (hw_addr_len > INTERFACE_HWADDR_MAX)
+        zlog_warn ("Hardware address is too large: %d", hw_addr_len);
+      else
+        {
+          ifp->hw_addr_len = hw_addr_len;
+          memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len);
+
+          for (i = 0; i < hw_addr_len; i++)
+            if (ifp->hw_addr[i] != 0)
+              break;
+
+          if (i == hw_addr_len)
+            ifp->hw_addr_len = 0;
+          else
+            ifp->hw_addr_len = hw_addr_len;
+        }
+    }
+}
+
+static enum zebra_link_type
+netlink_to_zebra_link_type (unsigned int hwt)
+{
+  switch (hwt)
+  {
+    case ARPHRD_ETHER: return ZEBRA_LLT_ETHER;
+    case ARPHRD_EETHER: return ZEBRA_LLT_EETHER;
+    case ARPHRD_AX25: return ZEBRA_LLT_AX25;
+    case ARPHRD_PRONET: return ZEBRA_LLT_PRONET;
+    case ARPHRD_IEEE802: return ZEBRA_LLT_IEEE802;
+    case ARPHRD_ARCNET: return ZEBRA_LLT_ARCNET;
+    case ARPHRD_APPLETLK: return ZEBRA_LLT_APPLETLK;
+    case ARPHRD_DLCI: return ZEBRA_LLT_DLCI;
+    case ARPHRD_ATM: return ZEBRA_LLT_ATM;
+    case ARPHRD_METRICOM: return ZEBRA_LLT_METRICOM;
+    case ARPHRD_IEEE1394: return ZEBRA_LLT_IEEE1394;
+    case ARPHRD_EUI64: return ZEBRA_LLT_EUI64;
+    case ARPHRD_INFINIBAND: return ZEBRA_LLT_INFINIBAND;
+    case ARPHRD_SLIP: return ZEBRA_LLT_SLIP;
+    case ARPHRD_CSLIP: return ZEBRA_LLT_CSLIP;
+    case ARPHRD_SLIP6: return ZEBRA_LLT_SLIP6;
+    case ARPHRD_CSLIP6: return ZEBRA_LLT_CSLIP6;
+    case ARPHRD_RSRVD: return ZEBRA_LLT_RSRVD;
+    case ARPHRD_ADAPT: return ZEBRA_LLT_ADAPT;
+    case ARPHRD_ROSE: return ZEBRA_LLT_ROSE;
+    case ARPHRD_X25: return ZEBRA_LLT_X25;
+    case ARPHRD_PPP: return ZEBRA_LLT_PPP;
+    case ARPHRD_CISCO: return ZEBRA_LLT_CHDLC;
+    case ARPHRD_LAPB: return ZEBRA_LLT_LAPB;
+    case ARPHRD_RAWHDLC: return ZEBRA_LLT_RAWHDLC;
+    case ARPHRD_TUNNEL: return ZEBRA_LLT_IPIP;
+    case ARPHRD_TUNNEL6: return ZEBRA_LLT_IPIP6;
+    case ARPHRD_FRAD: return ZEBRA_LLT_FRAD;
+    case ARPHRD_SKIP: return ZEBRA_LLT_SKIP;
+    case ARPHRD_LOOPBACK: return ZEBRA_LLT_LOOPBACK;
+    case ARPHRD_LOCALTLK: return ZEBRA_LLT_LOCALTLK;
+    case ARPHRD_FDDI: return ZEBRA_LLT_FDDI;
+    case ARPHRD_SIT: return ZEBRA_LLT_SIT;
+    case ARPHRD_IPDDP: return ZEBRA_LLT_IPDDP;
+    case ARPHRD_IPGRE: return ZEBRA_LLT_IPGRE;
+    case ARPHRD_PIMREG: return ZEBRA_LLT_PIMREG;
+    case ARPHRD_HIPPI: return ZEBRA_LLT_HIPPI;
+    case ARPHRD_ECONET: return ZEBRA_LLT_ECONET;
+    case ARPHRD_IRDA: return ZEBRA_LLT_IRDA;
+    case ARPHRD_FCPP: return ZEBRA_LLT_FCPP;
+    case ARPHRD_FCAL: return ZEBRA_LLT_FCAL;
+    case ARPHRD_FCPL: return ZEBRA_LLT_FCPL;
+    case ARPHRD_FCFABRIC: return ZEBRA_LLT_FCFABRIC;
+    case ARPHRD_IEEE802_TR: return ZEBRA_LLT_IEEE802_TR;
+    case ARPHRD_IEEE80211: return ZEBRA_LLT_IEEE80211;
+    case ARPHRD_IEEE802154: return ZEBRA_LLT_IEEE802154;
+#ifdef ARPHRD_IP6GRE
+    case ARPHRD_IP6GRE: return ZEBRA_LLT_IP6GRE;
+#endif
+#ifdef ARPHRD_IEEE802154_PHY
+    case ARPHRD_IEEE802154_PHY: return ZEBRA_LLT_IEEE802154_PHY;
+#endif
+
+    default: return ZEBRA_LLT_UNKNOWN;
+  }
+}
+
+/* Called from interface_lookup_netlink().  This function is only used
+   during bootstrap. */
+static int
+netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  int len;
+  struct ifinfomsg *ifi;
+  struct rtattr *tb[IFLA_MAX + 1];
+  struct interface *ifp;
+  char *name;
+
+  ifi = NLMSG_DATA (h);
+
+  if (h->nlmsg_type != RTM_NEWLINK)
+    return 0;
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg));
+  if (len < 0)
+    return -1;
+
+  /* Looking up interface name. */
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
+  
+#ifdef IFLA_WIRELESS
+  /* check for wireless messages to ignore */
+  if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0))
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: ignoring IFLA_WIRELESS message", __func__);
+      return 0;
+    }
+#endif /* IFLA_WIRELESS */
+
+  if (tb[IFLA_IFNAME] == NULL)
+    return -1;
+  name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
+
+  /* Add interface. */
+  ifp = if_get_by_name_vrf (name, vrf_id);
+  set_ifindex(ifp, ifi->ifi_index);
+  ifp->flags = ifi->ifi_flags & 0x0000fffff;
+  ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]);
+  ifp->metric = 0;
+
+  /* Hardware type and address. */
+  ifp->ll_type = netlink_to_zebra_link_type (ifi->ifi_type);
+  netlink_interface_update_hw_addr (tb, ifp);
+
+  if_add_update (ifp);
+
+  return 0;
+}
+
+/* Lookup interface IPv4/IPv6 address. */
+static int
+netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  int len;
+  struct ifaddrmsg *ifa;
+  struct rtattr *tb[IFA_MAX + 1];
+  struct interface *ifp;
+  void *addr;
+  void *broad;
+  u_char flags = 0;
+  char *label = NULL;
+
+  ifa = NLMSG_DATA (h);
+
+  if (ifa->ifa_family != AF_INET
+#ifdef HAVE_IPV6
+      && ifa->ifa_family != AF_INET6
+#endif /* HAVE_IPV6 */
+    )
+    return 0;
+
+  if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)
+    return 0;
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifaddrmsg));
+  if (len < 0)
+    return -1;
+
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA (ifa), len);
+
+  ifp = if_lookup_by_index_vrf (ifa->ifa_index, vrf_id);
+  if (ifp == NULL)
+    {
+      zlog_err ("netlink_interface_addr can't find interface by index %d vrf %u",
+                ifa->ifa_index, vrf_id);
+      return -1;
+    }
+
+  if (IS_ZEBRA_DEBUG_KERNEL)    /* remove this line to see initial ifcfg */
+    {
+      char buf[BUFSIZ];
+      zlog_debug ("netlink_interface_addr %s %s vrf %u:",
+                 lookup (nlmsg_str, h->nlmsg_type), ifp->name, vrf_id);
+      if (tb[IFA_LOCAL])
+        zlog_debug ("  IFA_LOCAL     %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_LOCAL]),
+                              buf, BUFSIZ), ifa->ifa_prefixlen);
+      if (tb[IFA_ADDRESS])
+        zlog_debug ("  IFA_ADDRESS   %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_ADDRESS]),
+                               buf, BUFSIZ), ifa->ifa_prefixlen);
+      if (tb[IFA_BROADCAST])
+        zlog_debug ("  IFA_BROADCAST %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_BROADCAST]),
+                              buf, BUFSIZ), ifa->ifa_prefixlen);
+      if (tb[IFA_LABEL] && strcmp (ifp->name, RTA_DATA (tb[IFA_LABEL])))
+        zlog_debug ("  IFA_LABEL     %s", (char *)RTA_DATA (tb[IFA_LABEL]));
+      
+      if (tb[IFA_CACHEINFO])
+        {
+          struct ifa_cacheinfo *ci = RTA_DATA (tb[IFA_CACHEINFO]);
+          zlog_debug ("  IFA_CACHEINFO pref %d, valid %d",
+                      ci->ifa_prefered, ci->ifa_valid);
+        }
+    }
+  
+  /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */
+  if (tb[IFA_LOCAL] == NULL)
+    tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+  if (tb[IFA_ADDRESS] == NULL)
+    tb[IFA_ADDRESS] = tb[IFA_LOCAL];
+  
+  /* local interface address */
+  addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL);
+
+  /* is there a peer address? */
+  if (tb[IFA_ADDRESS] &&
+      memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_ADDRESS])))
+    {
+      broad = RTA_DATA(tb[IFA_ADDRESS]);
+      SET_FLAG (flags, ZEBRA_IFA_PEER);
+    }
+  else
+    /* seeking a broadcast address */
+    broad = (tb[IFA_BROADCAST] ? RTA_DATA(tb[IFA_BROADCAST]) : NULL);
+
+  /* addr is primary key, SOL if we don't have one */
+  if (addr == NULL)
+    {
+      zlog_debug ("%s: NULL address", __func__);
+      return -1;
+    }
+
+  /* Flags. */
+  if (ifa->ifa_flags & IFA_F_SECONDARY)
+    SET_FLAG (flags, ZEBRA_IFA_SECONDARY);
+
+  /* Label */
+  if (tb[IFA_LABEL])
+    label = (char *) RTA_DATA (tb[IFA_LABEL]);
+
+  if (ifp && label && strcmp (ifp->name, label) == 0)
+    label = NULL;
+
+  /* Register interface address to the interface. */
+  if (ifa->ifa_family == AF_INET)
+    {
+      if (h->nlmsg_type == RTM_NEWADDR)
+        connected_add_ipv4 (ifp, flags,
+                            (struct in_addr *) addr, ifa->ifa_prefixlen,
+                            (struct in_addr *) broad, label);
+      else
+        connected_delete_ipv4 (ifp, flags,
+                               (struct in_addr *) addr, ifa->ifa_prefixlen,
+                               (struct in_addr *) broad);
+    }
+#ifdef HAVE_IPV6
+  if (ifa->ifa_family == AF_INET6)
+    {
+      if (h->nlmsg_type == RTM_NEWADDR)
+        connected_add_ipv6 (ifp, flags,
+                            (struct in6_addr *) addr, ifa->ifa_prefixlen,
+                            (struct in6_addr *) broad, label);
+      else
+        connected_delete_ipv6 (ifp,
+                               (struct in6_addr *) addr, ifa->ifa_prefixlen,
+                               (struct in6_addr *) broad);
+    }
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/* Looking up routing table by netlink interface. */
+static int
+netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  int len;
+  struct rtmsg *rtm;
+  struct rtattr *tb[RTA_MAX + 1];
+  u_char flags = 0;
+
+  char anyaddr[16] = { 0 };
+
+  int index;
+  int table;
+  u_int32_t mtu = 0;
+
+  void *dest;
+  void *gate;
+  void *src;
+
+  rtm = NLMSG_DATA (h);
+
+  if (h->nlmsg_type != RTM_NEWROUTE)
+    return 0;
+  if (rtm->rtm_type != RTN_UNICAST)
+    return 0;
+
+  table = rtm->rtm_table;
+#if 0                           /* we weed them out later in rib_weed_tables () */
+  if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default)
+    return 0;
+#endif
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
+  if (len < 0)
+    return -1;
+
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len);
+
+  if (rtm->rtm_flags & RTM_F_CLONED)
+    return 0;
+  if (rtm->rtm_protocol == RTPROT_REDIRECT)
+    return 0;
+  if (rtm->rtm_protocol == RTPROT_KERNEL)
+    return 0;
+
+  if (rtm->rtm_src_len != 0)
+    return 0;
+
+  /* Route which inserted by Zebra. */
+  if (rtm->rtm_protocol == RTPROT_ZEBRA)
+    flags |= ZEBRA_FLAG_SELFROUTE;
+
+  index = 0;
+  dest = NULL;
+  gate = NULL;
+  src = NULL;
+
+  if (tb[RTA_OIF])
+    index = *(int *) RTA_DATA (tb[RTA_OIF]);
+
+  if (tb[RTA_DST])
+    dest = RTA_DATA (tb[RTA_DST]);
+  else
+    dest = anyaddr;
+
+  if (tb[RTA_PREFSRC])
+    src = RTA_DATA (tb[RTA_PREFSRC]);
+
+  if (tb[RTA_GATEWAY])
+    gate = RTA_DATA (tb[RTA_GATEWAY]);
+
+  if (tb[RTA_METRICS])
+    {
+      struct rtattr *mxrta[RTAX_MAX+1];
+
+      memset (mxrta, 0, sizeof mxrta);
+      netlink_parse_rtattr (mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
+                            RTA_PAYLOAD(tb[RTA_METRICS]));
+
+      if (mxrta[RTAX_MTU])
+        mtu = *(u_int32_t *) RTA_DATA(mxrta[RTAX_MTU]);
+    }
+
+  if (rtm->rtm_family == AF_INET)
+    {
+      struct prefix_ipv4 p;
+      p.family = AF_INET;
+      memcpy (&p.prefix, dest, 4);
+      p.prefixlen = rtm->rtm_dst_len;
+
+      if (!tb[RTA_MULTIPATH])
+          rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index,
+                        vrf_id, table, 0, mtu, 0, SAFI_UNICAST);
+      else
+        {
+          /* This is a multipath route */
+
+          struct rib *rib;
+          struct rtnexthop *rtnh =
+            (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+          len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+          rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+          rib->type = ZEBRA_ROUTE_KERNEL;
+          rib->distance = 0;
+          rib->flags = flags;
+          rib->metric = 0;
+          rib->mtu = mtu;
+          rib->vrf_id = vrf_id;
+          rib->table = table;
+          rib->nexthop_num = 0;
+          rib->uptime = time (NULL);
+
+          for (;;)
+            {
+              if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+                break;
+
+              index = rtnh->rtnh_ifindex;
+              gate = 0;
+              if (rtnh->rtnh_len > sizeof (*rtnh))
+                {
+                  memset (tb, 0, sizeof (tb));
+                  netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+                                        rtnh->rtnh_len - sizeof (*rtnh));
+                  if (tb[RTA_GATEWAY])
+                    gate = RTA_DATA (tb[RTA_GATEWAY]);
+                }
+
+              if (gate)
+                {
+                  if (index)
+                    rib_nexthop_ipv4_ifindex_add (rib, gate, src, index);
+                  else
+                    rib_nexthop_ipv4_add (rib, gate, src);
+                }
+              else
+                rib_nexthop_ifindex_add (rib, index);
+
+              len -= NLMSG_ALIGN(rtnh->rtnh_len);
+              rtnh = RTNH_NEXT(rtnh);
+            }
+
+          if (rib->nexthop_num == 0)
+            XFREE (MTYPE_RIB, rib);
+          else
+            rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST);
+        }
+    }
+#ifdef HAVE_IPV6
+  if (rtm->rtm_family == AF_INET6)
+    {
+      struct prefix_ipv6 p;
+      p.family = AF_INET6;
+      memcpy (&p.prefix, dest, 16);
+      p.prefixlen = rtm->rtm_dst_len;
+
+      rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, vrf_id,
+                    table, 0, mtu, 0, SAFI_UNICAST);
+    }
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+static const struct message rtproto_str[] = {
+  {RTPROT_REDIRECT, "redirect"},
+  {RTPROT_KERNEL,   "kernel"},
+  {RTPROT_BOOT,     "boot"},
+  {RTPROT_STATIC,   "static"},
+  {RTPROT_GATED,    "GateD"},
+  {RTPROT_RA,       "router advertisement"},
+  {RTPROT_MRT,      "MRT"},
+  {RTPROT_ZEBRA,    "Zebra"},
+#ifdef RTPROT_BIRD
+  {RTPROT_BIRD,     "BIRD"},
+#endif /* RTPROT_BIRD */
+  {0,               NULL}
+};
+
+/* Routing information change from the kernel. */
+static int
+netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  int len;
+  struct rtmsg *rtm;
+  struct rtattr *tb[RTA_MAX + 1];
+  u_char zebra_flags = 0;
+
+  char anyaddr[16] = { 0 };
+
+  int index;
+  int table;
+  u_int32_t mtu = 0;
+
+  void *dest;
+  void *gate;
+  void *src;
+
+  rtm = NLMSG_DATA (h);
+
+  if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE))
+    {
+      /* If this is not route add/delete message print warning. */
+      zlog_warn ("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id);
+      return 0;
+    }
+
+  /* Connected route. */
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("%s %s %s proto %s vrf %u",
+               h->nlmsg_type ==
+               RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+               rtm->rtm_family == AF_INET ? "ipv4" : "ipv6",
+               rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast",
+               lookup (rtproto_str, rtm->rtm_protocol),
+               vrf_id);
+
+  if (rtm->rtm_type != RTN_UNICAST)
+    {
+      return 0;
+    }
+
+  table = rtm->rtm_table;
+  if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default)
+    {
+      return 0;
+    }
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
+  if (len < 0)
+    return -1;
+
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len);
+
+  if (rtm->rtm_flags & RTM_F_CLONED)
+    return 0;
+  if (rtm->rtm_protocol == RTPROT_REDIRECT)
+    return 0;
+  if (rtm->rtm_protocol == RTPROT_KERNEL)
+    return 0;
+
+  if (rtm->rtm_protocol == RTPROT_ZEBRA && h->nlmsg_type == RTM_NEWROUTE)
+    return 0;
+  if (rtm->rtm_protocol == RTPROT_ZEBRA)
+    SET_FLAG(zebra_flags, ZEBRA_FLAG_SELFROUTE);
+
+  if (rtm->rtm_src_len != 0)
+    {
+      zlog_warn ("netlink_route_change(): no src len, vrf %u", vrf_id);
+      return 0;
+    }
+
+  index = 0;
+  dest = NULL;
+  gate = NULL;
+  src = NULL;
+
+  if (tb[RTA_OIF])
+    index = *(int *) RTA_DATA (tb[RTA_OIF]);
+
+  if (tb[RTA_DST])
+    dest = RTA_DATA (tb[RTA_DST]);
+  else
+    dest = anyaddr;
+
+  if (tb[RTA_GATEWAY])
+    gate = RTA_DATA (tb[RTA_GATEWAY]);
+
+  if (tb[RTA_PREFSRC])
+    src = RTA_DATA (tb[RTA_PREFSRC]);
+
+  if (h->nlmsg_type == RTM_NEWROUTE)
+    {
+      if (tb[RTA_METRICS])
+        {
+          struct rtattr *mxrta[RTAX_MAX+1];
+
+          memset (mxrta, 0, sizeof mxrta);
+          netlink_parse_rtattr (mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
+                                RTA_PAYLOAD(tb[RTA_METRICS]));
+
+          if (mxrta[RTAX_MTU])
+            mtu = *(u_int32_t *) RTA_DATA(mxrta[RTAX_MTU]);
+        }
+    }
+
+  if (rtm->rtm_family == AF_INET)
+    {
+      struct prefix_ipv4 p;
+      p.family = AF_INET;
+      memcpy (&p.prefix, dest, 4);
+      p.prefixlen = rtm->rtm_dst_len;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        {
+          char buf[PREFIX_STRLEN];
+          zlog_debug ("%s %s vrf %u",
+                      h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+                      prefix2str (&p, buf, sizeof(buf)), vrf_id);
+        }
+
+      if (h->nlmsg_type == RTM_NEWROUTE)
+        {
+          if (!tb[RTA_MULTIPATH])
+            rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, vrf_id,
+                          table, 0, mtu, 0, SAFI_UNICAST);
+          else
+            {
+              /* This is a multipath route */
+
+              struct rib *rib;
+              struct rtnexthop *rtnh =
+                (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+              len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+              rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+              rib->type = ZEBRA_ROUTE_KERNEL;
+              rib->distance = 0;
+              rib->flags = 0;
+              rib->metric = 0;
+              rib->mtu = mtu;
+              rib->vrf_id = vrf_id;
+              rib->table = table;
+              rib->nexthop_num = 0;
+              rib->uptime = time (NULL);
+
+              for (;;)
+                {
+                  if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+                    break;
+
+                  index = rtnh->rtnh_ifindex;
+                  gate = 0;
+                  if (rtnh->rtnh_len > sizeof (*rtnh))
+                    {
+                      memset (tb, 0, sizeof (tb));
+                      netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+                                            rtnh->rtnh_len - sizeof (*rtnh));
+                      if (tb[RTA_GATEWAY])
+                        gate = RTA_DATA (tb[RTA_GATEWAY]);
+                    }
+
+                  if (gate)
+                    {
+                      if (index)
+                        rib_nexthop_ipv4_ifindex_add (rib, gate, src, index);
+                      else
+                        rib_nexthop_ipv4_add (rib, gate, src);
+                    }
+                  else
+                    rib_nexthop_ifindex_add (rib, index);
+
+                  len -= NLMSG_ALIGN(rtnh->rtnh_len);
+                  rtnh = RTNH_NEXT(rtnh);
+                }
+
+              if (rib->nexthop_num == 0)
+                XFREE (MTYPE_RIB, rib);
+              else
+                rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST);
+            }
+        }
+      else
+        rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, gate,
+                        index, vrf_id, SAFI_UNICAST);
+    }
+
+#ifdef HAVE_IPV6
+  if (rtm->rtm_family == AF_INET6)
+    {
+      struct prefix_ipv6 p;
+
+      p.family = AF_INET6;
+      memcpy (&p.prefix, dest, 16);
+      p.prefixlen = rtm->rtm_dst_len;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        {
+          char buf[PREFIX_STRLEN];
+          zlog_debug ("%s %s vrf %u",
+                      h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+                      prefix2str (&p, buf, sizeof(buf)), vrf_id);
+        }
+
+      if (h->nlmsg_type == RTM_NEWROUTE)
+        rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, vrf_id, table,
+                      0, mtu, 0, SAFI_UNICAST);
+      else
+        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, gate, index, vrf_id,
+                         SAFI_UNICAST);
+    }
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+static int
+netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  int len;
+  struct ifinfomsg *ifi;
+  struct rtattr *tb[IFLA_MAX + 1];
+  struct interface *ifp;
+  char *name;
+
+  ifi = NLMSG_DATA (h);
+
+  if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK))
+    {
+      /* If this is not link add/delete message so print warning. */
+      zlog_warn ("netlink_link_change: wrong kernel message %d vrf %u\n",
+                 h->nlmsg_type, vrf_id);
+      return 0;
+    }
+
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg));
+  if (len < 0)
+    return -1;
+
+  /* Looking up interface name. */
+  memset (tb, 0, sizeof tb);
+  netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
+
+#ifdef IFLA_WIRELESS
+  /* check for wireless messages to ignore */
+  if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0))
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: ignoring IFLA_WIRELESS message, vrf %u", __func__,
+                    vrf_id);
+      return 0;
+    }
+#endif /* IFLA_WIRELESS */
+  
+  if (tb[IFLA_IFNAME] == NULL)
+    return -1;
+  name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
+
+  /* Add interface. */
+  if (h->nlmsg_type == RTM_NEWLINK)
+    {
+      ifp = if_lookup_by_name_vrf (name, vrf_id);
+
+      if (ifp == NULL || !CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+        {
+          if (ifp == NULL)
+            ifp = if_get_by_name_vrf (name, vrf_id);
+
+          set_ifindex(ifp, ifi->ifi_index);
+          ifp->flags = ifi->ifi_flags & 0x0000fffff;
+          ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+          ifp->metric = 0;
+
+          netlink_interface_update_hw_addr (tb, ifp);
+
+          /* If new link is added. */
+          if_add_update (ifp);
+        }
+      else
+        {
+          /* Interface status change. */
+          set_ifindex(ifp, ifi->ifi_index);
+          ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+          ifp->metric = 0;
+
+          netlink_interface_update_hw_addr (tb, ifp);
+
+          if (if_is_operative (ifp))
+            {
+              ifp->flags = ifi->ifi_flags & 0x0000fffff;
+              if (!if_is_operative (ifp))
+                if_down (ifp);
+             else
+               /* Must notify client daemons of new interface status. */
+               zebra_interface_up_update (ifp);
+            }
+          else
+            {
+              ifp->flags = ifi->ifi_flags & 0x0000fffff;
+              if (if_is_operative (ifp))
+                if_up (ifp);
+            }
+        }
+    }
+  else
+    {
+      /* RTM_DELLINK. */
+      ifp = if_lookup_by_name_vrf (name, vrf_id);
+
+      if (ifp == NULL)
+        {
+          zlog_warn ("interface %s vrf %u is deleted but can't find",
+                     name, vrf_id);
+          return 0;
+        }
+
+      if_delete_update (ifp);
+    }
+
+  return 0;
+}
+
+static int
+netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  /* JF: Ignore messages that aren't from the kernel */
+  if ( snl->nl_pid != 0 )
+    {
+      zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl->nl_pid );
+      return 0;
+    }
+
+  switch (h->nlmsg_type)
+    {
+    case RTM_NEWROUTE:
+      return netlink_route_change (snl, h, vrf_id);
+      break;
+    case RTM_DELROUTE:
+      return netlink_route_change (snl, h, vrf_id);
+      break;
+    case RTM_NEWLINK:
+      return netlink_link_change (snl, h, vrf_id);
+      break;
+    case RTM_DELLINK:
+      return netlink_link_change (snl, h, vrf_id);
+      break;
+    case RTM_NEWADDR:
+      return netlink_interface_addr (snl, h, vrf_id);
+      break;
+    case RTM_DELADDR:
+      return netlink_interface_addr (snl, h, vrf_id);
+      break;
+    default:
+      zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type,
+                 vrf_id);
+      break;
+    }
+  return 0;
+}
+
+/* Interface lookup by netlink socket. */
+int
+interface_lookup_netlink (struct zebra_vrf *zvrf)
+{
+  int ret;
+
+  /* Get interface information. */
+  ret = netlink_request (AF_PACKET, RTM_GETLINK, &zvrf->netlink_cmd);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_interface, &zvrf->netlink_cmd, zvrf);
+  if (ret < 0)
+    return ret;
+
+  /* Get IPv4 address of the interfaces. */
+  ret = netlink_request (AF_INET, RTM_GETADDR, &zvrf->netlink_cmd);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_interface_addr, &zvrf->netlink_cmd, zvrf);
+  if (ret < 0)
+    return ret;
+
+#ifdef HAVE_IPV6
+  /* Get IPv6 address of the interfaces. */
+  ret = netlink_request (AF_INET6, RTM_GETADDR, &zvrf->netlink_cmd);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_interface_addr, &zvrf->netlink_cmd, zvrf);
+  if (ret < 0)
+    return ret;
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/* Routing table read function using netlink interface.  Only called
+   bootstrap time. */
+int
+netlink_route_read (struct zebra_vrf *zvrf)
+{
+  int ret;
+
+  /* Get IPv4 routing table. */
+  ret = netlink_request (AF_INET, RTM_GETROUTE, &zvrf->netlink_cmd);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_routing_table, &zvrf->netlink_cmd, zvrf);
+  if (ret < 0)
+    return ret;
+
+#ifdef HAVE_IPV6
+  /* Get IPv6 routing table. */
+  ret = netlink_request (AF_INET6, RTM_GETROUTE, &zvrf->netlink_cmd);
+  if (ret < 0)
+    return ret;
+  ret = netlink_parse_info (netlink_routing_table, &zvrf->netlink_cmd, zvrf);
+  if (ret < 0)
+    return ret;
+#endif /* HAVE_IPV6 */
+
+  return 0;
+}
+
+/* Utility function  comes from iproute2. 
+   Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
+int
+addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, size_t alen)
+{
+  size_t len;
+  struct rtattr *rta;
+
+  len = RTA_LENGTH (alen);
+
+  if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
+    return -1;
+
+  rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
+  rta->rta_type = type;
+  rta->rta_len = len;
+  memcpy (RTA_DATA (rta), data, alen);
+  n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len;
+
+  return 0;
+}
+
+int
+rta_addattr_l (struct rtattr *rta, size_t maxlen, int type, void *data, 
+               size_t alen)
+{
+  size_t len;
+  struct rtattr *subrta;
+
+  len = RTA_LENGTH (alen);
+
+  if (RTA_ALIGN (rta->rta_len) + len > maxlen)
+    return -1;
+
+  subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len));
+  subrta->rta_type = type;
+  subrta->rta_len = len;
+  memcpy (RTA_DATA (subrta), data, alen);
+  rta->rta_len = NLMSG_ALIGN (rta->rta_len) + len;
+
+  return 0;
+}
+
+/* Utility function comes from iproute2. 
+   Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
+int
+addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data)
+{
+  size_t len;
+  struct rtattr *rta;
+
+  len = RTA_LENGTH (4);
+
+  if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
+    return -1;
+
+  rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
+  rta->rta_type = type;
+  rta->rta_len = len;
+  memcpy (RTA_DATA (rta), &data, 4);
+  n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len;
+
+  return 0;
+}
+
+static int
+netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h,
+    vrf_id_t vrf_id)
+{
+  zlog_warn ("netlink_talk: ignoring message type 0x%04x vrf %u", h->nlmsg_type,
+             vrf_id);
+  return 0;
+}
+
+/* sendmsg() to netlink socket then recvmsg(). */
+static int
+netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_vrf *zvrf)
+{
+  int status;
+  struct sockaddr_nl snl;
+  struct iovec iov = {
+    .iov_base = (void *) n,
+    .iov_len = n->nlmsg_len
+  };
+  struct msghdr msg = {
+    .msg_name = (void *) &snl,
+    .msg_namelen = sizeof snl,
+    .msg_iov = &iov,
+    .msg_iovlen = 1,
+  };
+  int save_errno;
+
+  memset (&snl, 0, sizeof snl);
+  snl.nl_family = AF_NETLINK;
+
+  n->nlmsg_seq = ++nl->seq;
+
+  /* Request an acknowledgement by setting NLM_F_ACK */
+  n->nlmsg_flags |= NLM_F_ACK;
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name,
+               lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
+               n->nlmsg_seq);
+
+  /* Send message to netlink interface. */
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+  status = sendmsg (nl->sock, &msg, 0);
+  save_errno = errno;
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  if (status < 0)
+    {
+      zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s",
+            safe_strerror (save_errno));
+      return -1;
+    }
+
+
+  /* 
+   * Get reply from netlink socket. 
+   * The reply should either be an acknowlegement or an error.
+   */
+  return netlink_parse_info (netlink_talk_filter, nl, zvrf);
+}
+
+/* This function takes a nexthop as argument and adds
+ * the appropriate netlink attributes to an existing
+ * netlink message.
+ *
+ * @param routedesc: Human readable description of route type
+ *                   (direct/recursive, single-/multipath)
+ * @param bytelen: Length of addresses in bytes.
+ * @param nexthop: Nexthop information
+ * @param nlmsg: nlmsghdr structure to fill in.
+ * @param req_size: The size allocated for the message.
+ */
+static void
+_netlink_route_build_singlepath(
+        const char *routedesc,
+        int bytelen,
+        struct nexthop *nexthop,
+        struct nlmsghdr *nlmsg,
+        struct rtmsg *rtmsg,
+        size_t req_size)
+{
+  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
+    rtmsg->rtm_flags |= RTNH_F_ONLINK;
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      addattr_l (nlmsg, req_size, RTA_GATEWAY,
+                 &nexthop->gate.ipv4, bytelen);
+      if (nexthop->src.ipv4.s_addr)
+        addattr_l (nlmsg, req_size, RTA_PREFSRC,
+                   &nexthop->src.ipv4, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet_ntoa (nexthop->gate.ipv4),
+                   nexthop->ifindex);
+    }
+#ifdef HAVE_IPV6
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      addattr_l (nlmsg, req_size, RTA_GATEWAY,
+                 &nexthop->gate.ipv6, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet6_ntoa (nexthop->gate.ipv6),
+                   nexthop->ifindex);
+    }
+#endif /* HAVE_IPV6 */
+  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex);
+
+      if (nexthop->src.ipv4.s_addr)
+        addattr_l (nlmsg, req_size, RTA_PREFSRC,
+                   &nexthop->src.ipv4, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+    {
+      addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+}
+
+/* This function takes a nexthop as argument and
+ * appends to the given rtattr/rtnexthop pair the
+ * representation of the nexthop. If the nexthop
+ * defines a preferred source, the src parameter
+ * will be modified to point to that src, otherwise
+ * it will be kept unmodified.
+ *
+ * @param routedesc: Human readable description of route type
+ *                   (direct/recursive, single-/multipath)
+ * @param bytelen: Length of addresses in bytes.
+ * @param nexthop: Nexthop information
+ * @param rta: rtnetlink attribute structure
+ * @param rtnh: pointer to an rtnetlink nexthop structure
+ * @param src: pointer pointing to a location where
+ *             the prefsrc should be stored.
+ */
+static void
+_netlink_route_build_multipath(
+        const char *routedesc,
+        int bytelen,
+        struct nexthop *nexthop,
+        struct rtattr *rta,
+        struct rtnexthop *rtnh,
+        union g_addr **src
+        )
+{
+  rtnh->rtnh_len = sizeof (*rtnh);
+  rtnh->rtnh_flags = 0;
+  rtnh->rtnh_hops = 0;
+  rta->rta_len += rtnh->rtnh_len;
+
+  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
+    rtnh->rtnh_flags |= RTNH_F_ONLINK;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
+                     &nexthop->gate.ipv4, bytelen);
+      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
+
+      if (nexthop->src.ipv4.s_addr)
+        *src = &nexthop->src;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet_ntoa (nexthop->gate.ipv4),
+                   nexthop->ifindex);
+    }
+#ifdef HAVE_IPV6
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
+                     &nexthop->gate.ipv6, bytelen);
+      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet6_ntoa (nexthop->gate.ipv6),
+                   nexthop->ifindex);
+    }
+#endif /* HAVE_IPV6 */
+  /* ifindex */
+  if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME)
+    {
+      rtnh->rtnh_ifindex = nexthop->ifindex;
+      if (nexthop->src.ipv4.s_addr)
+        *src = &nexthop->src;
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+  else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      rtnh->rtnh_ifindex = nexthop->ifindex;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+  else
+    {
+      rtnh->rtnh_ifindex = 0;
+    }
+}
+
+/* Log debug information for netlink_route_multipath
+ * if debug logging is enabled.
+ *
+ * @param cmd: Netlink command which is to be processed
+ * @param p: Prefix for which the change is due
+ * @param nexthop: Nexthop which is currently processed
+ * @param routedesc: Semantic annotation for nexthop
+ *                     (recursive, multipath, etc.)
+ * @param family: Address family which the change concerns
+ */
+static void
+_netlink_route_debug(
+        int cmd,
+        struct prefix *p,
+        struct nexthop *nexthop,
+        const char *routedesc,
+        int family,
+        struct zebra_vrf *zvrf)
+{
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      char buf[PREFIX_STRLEN];
+      zlog_debug ("netlink_route_multipath() (%s): %s %s vrf %u type %s",
+         routedesc,
+         lookup (nlmsg_str, cmd),
+         prefix2str (p, buf, sizeof(buf)),
+         zvrf->vrf_id,
+         nexthop_type_to_str (nexthop->type));
+    }
+}
+
+/* Routing table change via netlink interface. */
+static int
+netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib)
+{
+  int bytelen;
+  struct sockaddr_nl snl;
+  struct nexthop *nexthop = NULL, *tnexthop;
+  int recursing;
+  int nexthop_num;
+  int discard;
+  int family = PREFIX_FAMILY(p);
+  const char *routedesc;
+
+  struct
+  {
+    struct nlmsghdr n;
+    struct rtmsg r;
+    char buf[NL_PKT_BUF_SIZE];
+  } req;
+
+  struct zebra_vrf *zvrf = vrf_info_lookup (rib->vrf_id);
+
+  memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE);
+
+  bytelen = (family == AF_INET ? 4 : 16);
+
+  req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
+  req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_REQUEST;
+  req.n.nlmsg_type = cmd;
+  req.r.rtm_family = family;
+  req.r.rtm_table = rib->table;
+  req.r.rtm_dst_len = p->prefixlen;
+  req.r.rtm_protocol = RTPROT_ZEBRA;
+  req.r.rtm_scope = RT_SCOPE_LINK;
+
+  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
+    discard = 1;
+  else
+    discard = 0;
+
+  if (cmd == RTM_NEWROUTE)
+    {
+      if (discard)
+        {
+          if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
+            req.r.rtm_type = RTN_BLACKHOLE;
+          else if (rib->flags & ZEBRA_FLAG_REJECT)
+            req.r.rtm_type = RTN_UNREACHABLE;
+          else
+            assert (RTN_BLACKHOLE != RTN_UNREACHABLE);  /* false */
+        }
+      else
+        req.r.rtm_type = RTN_UNICAST;
+    }
+
+  addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen);
+
+  /* Metric. */
+  addattr32 (&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
+
+  if (rib->mtu || rib->nexthop_mtu)
+    {
+      char buf[NL_PKT_BUF_SIZE];
+      struct rtattr *rta = (void *) buf;
+      u_int32_t mtu = rib->mtu;
+      if (!mtu || (rib->nexthop_mtu && rib->nexthop_mtu < mtu))
+        mtu = rib->nexthop_mtu;
+      rta->rta_type = RTA_METRICS;
+      rta->rta_len = RTA_LENGTH(0);
+      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu);
+      addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA (rta),
+                 RTA_PAYLOAD (rta));
+    }
+
+  if (discard)
+    {
+      if (cmd == RTM_NEWROUTE)
+        for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+          {
+            /* We shouldn't encounter recursive nexthops on discard routes,
+             * but it is probably better to handle that case correctly anyway.
+             */
+            if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+              continue;
+            SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+          }
+      goto skip;
+    }
+
+  /* Count overall nexthops so we can decide whether to use singlepath
+   * or multipath case. */
+  nexthop_num = 0;
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+      if (cmd == RTM_NEWROUTE && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+        continue;
+      if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+        continue;
+
+      if (nexthop->type != NEXTHOP_TYPE_IFINDEX &&
+          nexthop->type != NEXTHOP_TYPE_IFNAME)
+        req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+
+      nexthop_num++;
+    }
+
+  /* Singlepath case. */
+  if (nexthop_num == 1 || MULTIPATH_NUM == 1)
+    {
+      nexthop_num = 0;
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+        {
+          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+            continue;
+
+          if ((cmd == RTM_NEWROUTE
+               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+              || (cmd == RTM_DELROUTE
+                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
+            {
+              routedesc = recursing ? "recursive, 1 hop" : "single hop";
+
+              _netlink_route_debug(cmd, p, nexthop, routedesc, family, zvrf);
+              _netlink_route_build_singlepath(routedesc, bytelen,
+                                              nexthop, &req.n, &req.r,
+                                              sizeof req);
+
+              if (cmd == RTM_NEWROUTE)
+                SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+              nexthop_num++;
+              break;
+            }
+        }
+    }
+  else
+    {
+      char buf[NL_PKT_BUF_SIZE];
+      struct rtattr *rta = (void *) buf;
+      struct rtnexthop *rtnh;
+      union g_addr *src = NULL;
+
+      rta->rta_type = RTA_MULTIPATH;
+      rta->rta_len = RTA_LENGTH (0);
+      rtnh = RTA_DATA (rta);
+
+      nexthop_num = 0;
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+        {
+          if (nexthop_num >= MULTIPATH_NUM)
+            break;
+
+          if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+            continue;
+
+          if ((cmd == RTM_NEWROUTE
+               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+              || (cmd == RTM_DELROUTE
+                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
+            {
+              routedesc = recursing ? "recursive, multihop" : "multihop";
+              nexthop_num++;
+
+              _netlink_route_debug(cmd, p, nexthop,
+                                   routedesc, family, zvrf);
+              _netlink_route_build_multipath(routedesc, bytelen,
+                                             nexthop, rta, rtnh, &src);
+              rtnh = RTNH_NEXT (rtnh);
+
+              if (cmd == RTM_NEWROUTE)
+                SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+            }
+        }
+      if (src)
+        addattr_l (&req.n, sizeof req, RTA_PREFSRC, &src->ipv4, bytelen);
+
+      if (rta->rta_len > RTA_LENGTH (0))
+        addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta),
+                   RTA_PAYLOAD (rta));
+    }
+
+  /* If there is no useful nexthop then return. */
+  if (nexthop_num == 0)
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("netlink_route_multipath(): No useful nexthop.");
+      return 0;
+    }
+
+skip:
+
+  /* Destination netlink address. */
+  memset (&snl, 0, sizeof snl);
+  snl.nl_family = AF_NETLINK;
+
+  /* Talk to netlink socket. */
+  return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf);
+}
+
+int
+kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new)
+{
+  if (!old && new)
+    return netlink_route_multipath (RTM_NEWROUTE, p, new);
+  if (old && !new)
+    return netlink_route_multipath (RTM_DELROUTE, p, old);
+
+   /* Replace, can be done atomically if metric does not change;
+    * netlink uses [prefix, tos, priority] to identify prefix.
+    * Now metric is not sent to kernel, so we can just do atomic replace. */
+  return netlink_route_multipath (RTM_NEWROUTE, p, new);
+}
+
+/* Interface address modification. */
+static int
+netlink_address (int cmd, int family, struct interface *ifp,
+                 struct connected *ifc)
+{
+  int bytelen;
+  struct prefix *p;
+
+  struct
+  {
+    struct nlmsghdr n;
+    struct ifaddrmsg ifa;
+    char buf[NL_PKT_BUF_SIZE];
+  } req;
+
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  p = ifc->address;
+  memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE);
+
+  bytelen = (family == AF_INET ? 4 : 16);
+
+  req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST;
+  req.n.nlmsg_type = cmd;
+  req.ifa.ifa_family = family;
+
+  req.ifa.ifa_index = ifp->ifindex;
+  req.ifa.ifa_prefixlen = p->prefixlen;
+
+  addattr_l (&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen);
+
+  if (family == AF_INET && cmd == RTM_NEWADDR)
+    {
+      if (!CONNECTED_PEER(ifc) && ifc->destination)
+        {
+          p = ifc->destination;
+          addattr_l (&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix,
+                     bytelen);
+        }
+    }
+
+  if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY))
+    SET_FLAG (req.ifa.ifa_flags, IFA_F_SECONDARY);
+
+  if (ifc->label)
+    addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label,
+               strlen (ifc->label) + 1);
+
+  return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf);
+}
+
+int
+kernel_address_add_ipv4 (struct interface *ifp, struct connected *ifc)
+{
+  return netlink_address (RTM_NEWADDR, AF_INET, ifp, ifc);
+}
+
+int
+kernel_address_delete_ipv4 (struct interface *ifp, struct connected *ifc)
+{
+  return netlink_address (RTM_DELADDR, AF_INET, ifp, ifc);
+}
+
+
+extern struct thread_master *master;
+
+/* Kernel route reflection. */
+static int
+kernel_read (struct thread *thread)
+{
+  struct zebra_vrf *zvrf = (struct zebra_vrf *)THREAD_ARG (thread);
+  netlink_parse_info (netlink_information_fetch, &zvrf->netlink, zvrf);
+  zvrf->t_netlink = thread_add_read (zebrad.master, kernel_read, zvrf,
+                                     zvrf->netlink.sock);
+
+  return 0;
+}
+
+/* Filter out messages from self that occur on listener socket,
+   caused by our actions on the command socket
+ */
+static void netlink_install_filter (int sock, __u32 pid)
+{
+  struct sock_filter filter[] = {
+    /* 0: ldh [4]                */
+    BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+    /* 1: jeq 0x18 jt 3 jf 6  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0),
+    /* 2: jeq 0x19 jt 3 jf 6  */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3),
+    /* 3: ldw [12]               */
+    BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)),
+    /* 4: jeq XX  jt 5 jf 6   */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1),
+    /* 5: ret 0    (skip)     */
+    BPF_STMT(BPF_RET|BPF_K, 0),
+    /* 6: ret 0xffff (keep)   */
+    BPF_STMT(BPF_RET|BPF_K, 0xffff),
+  };
+
+  struct sock_fprog prog = {
+    .len = array_size(filter),
+    .filter = filter,
+  };
+
+  if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
+    zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno));
+}
+
+/* Exported interface function.  This function simply calls
+   netlink_socket (). */
+void
+kernel_init (struct zebra_vrf *zvrf)
+{
+  unsigned long groups;
+
+  groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
+#ifdef HAVE_IPV6
+  groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
+#endif /* HAVE_IPV6 */
+  netlink_socket (&zvrf->netlink, groups, zvrf->vrf_id);
+  netlink_socket (&zvrf->netlink_cmd, 0, zvrf->vrf_id);
+
+  /* Register kernel socket. */
+  if (zvrf->netlink.sock > 0)
+    {
+      size_t bufsize = MAX(nl_rcvbufsize, 2 * sysconf(_SC_PAGESIZE));
+      
+      /* Only want non-blocking on the netlink event socket */
+      if (fcntl (zvrf->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+        zlog_err ("Can't set %s socket flags: %s", zvrf->netlink.name,
+                  safe_strerror (errno));
+
+      /* Set receive buffer size if it's set from command line */
+      if (nl_rcvbufsize)
+        netlink_recvbuf (&zvrf->netlink, nl_rcvbufsize);
+      
+      nl_rcvbuf.p = XMALLOC (MTYPE_NETLINK_RCVBUF, bufsize);
+      nl_rcvbuf.size = bufsize;
+      
+      netlink_install_filter (zvrf->netlink.sock, zvrf->netlink_cmd.snl.nl_pid);
+      zvrf->t_netlink = thread_add_read (zebrad.master, kernel_read, zvrf,
+                                         zvrf->netlink.sock);
+    }
+}
+
+void
+kernel_terminate (struct zebra_vrf *zvrf)
+{
+  THREAD_READ_OFF (zvrf->t_netlink);
+
+  if (zvrf->netlink.sock >= 0)
+    {
+      close (zvrf->netlink.sock);
+      zvrf->netlink.sock = -1;
+    }
+
+  if (zvrf->netlink_cmd.sock >= 0)
+    {
+      close (zvrf->netlink_cmd.sock);
+      zvrf->netlink_cmd.sock = -1;
+    }
+}
+
+/*
+ * nl_msg_type_to_str
+ */
+const char *
+nl_msg_type_to_str (uint16_t msg_type)
+{
+  return lookup (nlmsg_str, msg_type);
+}
+
+/*
+ * nl_rtproto_to_str
+ */
+const char *
+nl_rtproto_to_str (u_char rtproto)
+{
+  return lookup (rtproto_str, rtproto);
+}
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
new file mode 100644 (file)
index 0000000..9fc7001
--- /dev/null
@@ -0,0 +1,50 @@
+/* Header file exported by rt_netlink.c to zebra.
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_RT_NETLINK_H
+#define _ZEBRA_RT_NETLINK_H
+
+#ifdef HAVE_NETLINK
+
+#define NL_PKT_BUF_SIZE 8192
+#define NL_DEFAULT_ROUTE_METRIC 20
+
+extern int
+addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data);
+extern int
+addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, size_t alen);
+
+extern int
+rta_addattr_l (struct rtattr *rta, size_t maxlen, int type, void *data, size_t alen);
+
+extern const char *
+nl_msg_type_to_str (uint16_t msg_type);
+
+extern const char *
+nl_rtproto_to_str (u_char rtproto);
+
+
+extern int interface_lookup_netlink (struct zebra_vrf *zvrf);
+extern int netlink_route_read (struct zebra_vrf *zvrf);
+
+#endif /* HAVE_NETLINK */
+
+#endif /* _ZEBRA_RT_NETLINK_H */
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
new file mode 100644 (file)
index 0000000..9dd4582
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Kernel routing table updates by routing socket.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "sockunion.h"
+#include "log.h"
+#include "str.h"
+#include "privs.h"
+
+#include "zebra/debug.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/kernel_socket.h"
+
+extern struct zebra_privs_t zserv_privs;
+
+/* kernel socket export */
+extern int rtm_write (int message, union sockunion *dest,
+                      union sockunion *mask, union sockunion *gate,
+                      unsigned int index, int zebra_flags, int metric);
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+/* Adjust netmask socket length. Return value is a adjusted sin_len
+   value. */
+static int
+sin_masklen (struct in_addr mask)
+{
+  char *p, *lim;
+  int len;
+  struct sockaddr_in sin;
+
+  if (mask.s_addr == 0) 
+    return sizeof (long);
+
+  sin.sin_addr = mask;
+  len = sizeof (struct sockaddr_in);
+
+  lim = (char *) &sin.sin_addr;
+  p = lim + sizeof (sin.sin_addr);
+
+  while (*--p == 0 && p >= lim) 
+    len--;
+  return len;
+}
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+/* Interface between zebra message and rtm message. */
+static int
+kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib)
+
+{
+  struct sockaddr_in *mask = NULL;
+  struct sockaddr_in sin_dest, sin_mask, sin_gate;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int nexthop_num = 0;
+  ifindex_t ifindex = 0;
+  int gate = 0;
+  int error;
+  char prefix_buf[PREFIX_STRLEN];
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    prefix2str (p, prefix_buf, sizeof(prefix_buf));
+  memset (&sin_dest, 0, sizeof (struct sockaddr_in));
+  sin_dest.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sin_dest.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  sin_dest.sin_addr = p->u.prefix4;
+
+  memset (&sin_mask, 0, sizeof (struct sockaddr_in));
+
+  memset (&sin_gate, 0, sizeof (struct sockaddr_in));
+  sin_gate.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sin_gate.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  /* Make gateway. */
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
+      gate = 0;
+      char gate_buf[INET_ADDRSTRLEN] = "NULL";
+
+      /*
+       * XXX We need to refrain from kernel operations in some cases,
+       * but this if statement seems overly cautious - what about
+       * other than ADD and DELETE?
+       */
+      if ((cmd == RTM_ADD
+          && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         || (cmd == RTM_DELETE
+             && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+             ))
+       {
+         if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
+             nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+           {
+             sin_gate.sin_addr = nexthop->gate.ipv4;
+             gate = 1;
+           }
+         if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+             || nexthop->type == NEXTHOP_TYPE_IFNAME
+             || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+           ifindex = nexthop->ifindex;
+         if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE)
+           {
+             struct in_addr loopback;
+             loopback.s_addr = htonl (INADDR_LOOPBACK);
+             sin_gate.sin_addr = loopback;
+             gate = 1;
+           }
+
+         if (gate && p->prefixlen == 32)
+           mask = NULL;
+         else
+           {
+             masklen2ip (p->prefixlen, &sin_mask.sin_addr);
+             sin_mask.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+             sin_mask.sin_len = sin_masklen (sin_mask.sin_addr);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+             mask = &sin_mask;
+           }
+
+         error = rtm_write (cmd,
+                            (union sockunion *)&sin_dest, 
+                            (union sockunion *)mask, 
+                            gate ? (union sockunion *)&sin_gate : NULL,
+                            ifindex,
+                            rib->flags,
+                            rib->metric);
+
+           if (IS_ZEBRA_DEBUG_RIB)
+           {
+             if (!gate)
+             {
+               zlog_debug ("%s: %s: attention! gate not found for rib %p",
+                 __func__, prefix_buf, rib);
+               rib_dump (p, rib);
+             }
+             else
+               inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN);
+           }
+           switch (error)
+           {
+             /* We only flag nexthops as being in FIB if rtm_write() did its work. */
+             case ZEBRA_ERR_NOERROR:
+               nexthop_num++;
+               if (IS_ZEBRA_DEBUG_RIB)
+                 zlog_debug ("%s: %s: successfully did NH %s",
+                   __func__, prefix_buf, gate_buf);
+               if (cmd == RTM_ADD)
+                 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+               break;
+             /* The only valid case for this error is kernel's failure to install
+              * a multipath route, which is common for FreeBSD. This should be
+              * ignored silently, but logged as an error otherwise.
+              */
+             case ZEBRA_ERR_RTEXIST:
+               if (cmd != RTM_ADD)
+                 zlog_err ("%s: rtm_write() returned %d for command %d",
+                   __func__, error, cmd);
+               continue;
+               break;
+             /* Given that our NEXTHOP_FLAG_FIB matches real kernel FIB, it isn't
+              * normal to get any other messages in ANY case.
+              */
+             case ZEBRA_ERR_RTNOEXIST:
+             case ZEBRA_ERR_RTUNREACH:
+             default:
+               zlog_err ("%s: %s: rtm_write() unexpectedly returned %d for command %s",
+                 __func__, prefix2str(p, prefix_buf, sizeof(prefix_buf)),
+                 error, lookup (rtm_type_str, cmd));
+               break;
+           }
+         } /* if (cmd and flags make sense) */
+       else
+         if (IS_ZEBRA_DEBUG_RIB)
+           zlog_debug ("%s: odd command %s for flags %d",
+             __func__, lookup (rtm_type_str, cmd), nexthop->flags);
+     } /* for (ALL_NEXTHOPS_RO(...))*/
+   /* If there was no useful nexthop, then complain. */
+   if (nexthop_num == 0 && IS_ZEBRA_DEBUG_KERNEL)
+     zlog_debug ("%s: No useful nexthops were found in RIB entry %p", __func__, rib);
+
+  return 0; /*XXX*/
+}
+
+#ifdef HAVE_IPV6
+
+#ifdef SIN6_LEN
+/* Calculate sin6_len value for netmask socket value. */
+static int
+sin6_masklen (struct in6_addr mask)
+{
+  struct sockaddr_in6 sin6;
+  char *p, *lim;
+  int len;
+
+  if (IN6_IS_ADDR_UNSPECIFIED (&mask)) 
+    return sizeof (long);
+
+  sin6.sin6_addr = mask;
+  len = sizeof (struct sockaddr_in6);
+
+  lim = (char *) & sin6.sin6_addr;
+  p = lim + sizeof (sin6.sin6_addr);
+
+  while (*--p == 0 && p >= lim) 
+    len--;
+
+  return len;
+}
+#endif /* SIN6_LEN */
+
+/* Interface between zebra message and rtm message. */
+static int
+kernel_rtm_ipv6 (int cmd, struct prefix *p, struct rib *rib)
+{
+  struct sockaddr_in6 *mask;
+  struct sockaddr_in6 sin_dest, sin_mask, sin_gate;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int nexthop_num = 0;
+  ifindex_t ifindex = 0;
+  int gate = 0;
+  int error;
+
+  memset (&sin_dest, 0, sizeof (struct sockaddr_in6));
+  sin_dest.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+  sin_dest.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* SIN6_LEN */
+  sin_dest.sin6_addr = p->u.prefix6;
+
+  memset (&sin_mask, 0, sizeof (struct sockaddr_in6));
+
+  memset (&sin_gate, 0, sizeof (struct sockaddr_in6));
+  sin_gate.sin6_family = AF_INET6;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  sin_gate.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+
+  /* Make gateway. */
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+       continue;
+
+      gate = 0;
+
+      if ((cmd == RTM_ADD
+          && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         || (cmd == RTM_DELETE
+#if 0
+             && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+#endif
+             ))
+       {
+         if (nexthop->type == NEXTHOP_TYPE_IPV6
+             || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+             || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+           {
+             sin_gate.sin6_addr = nexthop->gate.ipv6;
+             gate = 1;
+           }
+         if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+             || nexthop->type == NEXTHOP_TYPE_IFNAME
+             || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+             || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+           ifindex = nexthop->ifindex;
+
+         if (cmd == RTM_ADD)
+           SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+       }
+
+      /* Under kame set interface index to link local address. */
+#ifdef KAME
+
+#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
+      do { \
+       (a).s6_addr[2] = ((i) >> 8) & 0xff; \
+       (a).s6_addr[3] = (i) & 0xff; \
+      } while (0)
+
+      if (gate && IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6_addr))
+       SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, ifindex);
+#endif /* KAME */
+
+      if (gate && p->prefixlen == 128)
+       mask = NULL;
+      else
+       {
+         masklen2ip6 (p->prefixlen, &sin_mask.sin6_addr);
+         sin_mask.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+         sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr);
+#endif /* SIN6_LEN */
+         mask = &sin_mask;
+       }
+
+      error = rtm_write (cmd,
+                       (union sockunion *) &sin_dest,
+                       (union sockunion *) mask,
+                       gate ? (union sockunion *)&sin_gate : NULL,
+                       ifindex,
+                       rib->flags,
+                       rib->metric);
+
+#if 0
+      if (error)
+       {
+         zlog_info ("kernel_rtm_ipv6(): nexthop %d add error=%d.",
+           nexthop_num, error);
+       }
+#else
+      (void)error;
+#endif
+
+      nexthop_num++;
+    }
+
+  /* If there is no useful nexthop then return. */
+  if (nexthop_num == 0)
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+       zlog_debug ("kernel_rtm_ipv6(): No useful nexthop.");
+      return 0;
+    }
+
+  return 0; /*XXX*/
+}
+
+#endif
+
+static int
+kernel_rtm (int cmd, struct prefix *p, struct rib *rib)
+{
+  switch (PREFIX_FAMILY(p))
+    {
+    case AF_INET:
+      return kernel_rtm_ipv4 (cmd, p, rib);
+    case AF_INET6:
+      return kernel_rtm_ipv6 (cmd, p, rib);
+    }
+  return 0;
+}
+
+int
+kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new)
+{
+  int route = 0;
+
+  if (zserv_privs.change(ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+
+  if (old)
+    route |= kernel_rtm (RTM_DELETE, p, old);
+
+  if (new)
+    route |= kernel_rtm (RTM_ADD, p, new);
+
+  if (zserv_privs.change(ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  return route;
+}
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
new file mode 100644 (file)
index 0000000..2f62714
--- /dev/null
@@ -0,0 +1,1795 @@
+/* Router advertisement
+ * Copyright (C) 2005 6WIND <jean-mickael.guerin@6wind.com>
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "sockopt.h"
+#include "thread.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "command.h"
+#include "privs.h"
+#include "vrf.h"
+
+#include "zebra/interface.h"
+#include "zebra/rtadv.h"
+#include "zebra/debug.h"
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+
+extern struct zebra_privs_t zserv_privs;
+
+#if defined (HAVE_IPV6) && defined (HAVE_RTADV)
+
+#ifdef OPEN_BSD
+#include <netinet/icmp6.h>
+#endif
+
+/* If RFC2133 definition is used. */
+#ifndef IPV6_JOIN_GROUP
+#define IPV6_JOIN_GROUP  IPV6_ADD_MEMBERSHIP 
+#endif
+#ifndef IPV6_LEAVE_GROUP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 
+#endif
+
+#define ALLNODE   "ff02::1"
+#define ALLROUTER "ff02::2"
+
+extern struct zebra_t zebrad;
+
+enum rtadv_event {RTADV_START, RTADV_STOP, RTADV_TIMER, 
+                 RTADV_TIMER_MSEC, RTADV_READ};
+
+static void rtadv_event (struct zebra_vrf *, enum rtadv_event, int);
+
+static int if_join_all_router (int, struct interface *);
+static int if_leave_all_router (int, struct interface *);
+
+static int
+rtadv_recv_packet (int sock, u_char *buf, int buflen,
+                  struct sockaddr_in6 *from, ifindex_t *ifindex,
+                  int *hoplimit)
+{
+  int ret;
+  struct msghdr msg;
+  struct iovec iov;
+  struct cmsghdr  *cmsgptr;
+  struct in6_addr dst;
+
+  char adata[1024];
+
+  /* Fill in message and iovec. */
+  msg.msg_name = (void *) from;
+  msg.msg_namelen = sizeof (struct sockaddr_in6);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = sizeof adata;
+  iov.iov_base = buf;
+  iov.iov_len = buflen;
+
+  /* If recvmsg fail return minus value. */
+  ret = recvmsg (sock, &msg, 0);
+  if (ret < 0)
+    return ret;
+
+  for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+       cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) 
+    {
+      /* I want interface index which this packet comes from. */
+      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+         cmsgptr->cmsg_type == IPV6_PKTINFO) 
+       {
+         struct in6_pktinfo *ptr;
+         
+         ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
+         *ifindex = ptr->ipi6_ifindex;
+         memcpy(&dst, &ptr->ipi6_addr, sizeof(ptr->ipi6_addr));
+        }
+
+      /* Incoming packet's hop limit. */
+      if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+         cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+       {
+         int *hoptr = (int *) CMSG_DATA (cmsgptr);
+         *hoplimit = *hoptr;
+       }
+    }
+  return ret;
+}
+
+#define RTADV_MSG_SIZE 4096
+
+/* Send router advertisement packet. */
+static void
+rtadv_send_packet (int sock, struct interface *ifp)
+{
+  struct msghdr msg;
+  struct iovec iov;
+  struct cmsghdr  *cmsgptr;
+  struct in6_pktinfo *pkt;
+  struct sockaddr_in6 addr;
+#ifdef HAVE_STRUCT_SOCKADDR_DL
+  struct sockaddr_dl *sdl;
+#endif /* HAVE_STRUCT_SOCKADDR_DL */
+  static void *adata = NULL;
+  unsigned char buf[RTADV_MSG_SIZE];
+  struct nd_router_advert *rtadv;
+  int ret;
+  int len = 0;
+  struct zebra_if *zif;
+  struct rtadv_prefix *rprefix;
+  u_char all_nodes_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
+  struct listnode *node;
+  u_int16_t pkt_RouterLifetime;
+
+  /*
+   * Allocate control message bufffer.  This is dynamic because
+   * CMSG_SPACE is not guaranteed not to call a function.  Note that
+   * the size will be different on different architectures due to
+   * differing alignment rules.
+   */
+  if (adata == NULL)
+    {
+      /* XXX Free on shutdown. */
+      adata = malloc(CMSG_SPACE(sizeof(struct in6_pktinfo)));
+          
+      if (adata == NULL)
+       zlog_err("rtadv_send_packet: can't malloc control data\n");
+    }
+
+  /* Logging of packet. */
+  if (IS_ZEBRA_DEBUG_PACKET)
+    zlog_debug ("Router advertisement send to %s", ifp->name);
+
+  /* Fill in sockaddr_in6. */
+  memset (&addr, 0, sizeof (struct sockaddr_in6));
+  addr.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+  addr.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* SIN6_LEN */
+  addr.sin6_port = htons (IPPROTO_ICMPV6);
+  IPV6_ADDR_COPY (&addr.sin6_addr, all_nodes_addr);
+
+  /* Fetch interface information. */
+  zif = ifp->info;
+
+  /* Make router advertisement message. */
+  rtadv = (struct nd_router_advert *) buf;
+
+  rtadv->nd_ra_type = ND_ROUTER_ADVERT;
+  rtadv->nd_ra_code = 0;
+  rtadv->nd_ra_cksum = 0;
+
+  rtadv->nd_ra_curhoplimit = 64;
+
+  /* RFC4191: Default Router Preference is 0 if Router Lifetime is 0. */
+  rtadv->nd_ra_flags_reserved =
+    zif->rtadv.AdvDefaultLifetime == 0 ? 0 : zif->rtadv.DefaultPreference;
+  rtadv->nd_ra_flags_reserved <<= 3;
+
+  if (zif->rtadv.AdvManagedFlag)
+    rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
+  if (zif->rtadv.AdvOtherConfigFlag)
+    rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
+  if (zif->rtadv.AdvHomeAgentFlag)
+    rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_HOME_AGENT;
+  /* Note that according to Neighbor Discovery (RFC 4861 [18]),
+   * AdvDefaultLifetime is by default based on the value of
+   * MaxRtrAdvInterval.  AdvDefaultLifetime is used in the Router Lifetime
+   * field of Router Advertisements.  Given that this field is expressed
+   * in seconds, a small MaxRtrAdvInterval value can result in a zero
+   * value for this field.  To prevent this, routers SHOULD keep
+   * AdvDefaultLifetime in at least one second, even if the use of
+   * MaxRtrAdvInterval would result in a smaller value. -- RFC6275, 7.5 */
+  pkt_RouterLifetime = zif->rtadv.AdvDefaultLifetime != -1 ?
+    zif->rtadv.AdvDefaultLifetime :
+    MAX (1, 0.003 * zif->rtadv.MaxRtrAdvInterval);
+  rtadv->nd_ra_router_lifetime = htons (pkt_RouterLifetime);
+  rtadv->nd_ra_reachable = htonl (zif->rtadv.AdvReachableTime);
+  rtadv->nd_ra_retransmit = htonl (0);
+
+  len = sizeof (struct nd_router_advert);
+
+  /* If both the Home Agent Preference and Home Agent Lifetime are set to
+   * their default values specified above, this option SHOULD NOT be
+   * included in the Router Advertisement messages sent by this home
+   * agent. -- RFC6275, 7.4 */
+  if
+  (
+    zif->rtadv.AdvHomeAgentFlag &&
+    (zif->rtadv.HomeAgentPreference || zif->rtadv.HomeAgentLifetime != -1)
+  )
+    {
+      struct nd_opt_homeagent_info *ndopt_hai = 
+       (struct nd_opt_homeagent_info *)(buf + len);
+      ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION;
+      ndopt_hai->nd_opt_hai_len = 1;
+      ndopt_hai->nd_opt_hai_reserved = 0;
+      ndopt_hai->nd_opt_hai_preference = htons(zif->rtadv.HomeAgentPreference);
+      /* 16-bit unsigned integer.  The lifetime associated with the home
+       * agent in units of seconds.  The default value is the same as the
+       * Router Lifetime, as specified in the main body of the Router
+       * Advertisement.  The maximum value corresponds to 18.2 hours.  A
+       * value of 0 MUST NOT be used. -- RFC6275, 7.5 */
+      ndopt_hai->nd_opt_hai_lifetime = htons
+      (
+        zif->rtadv.HomeAgentLifetime != -1 ?
+        zif->rtadv.HomeAgentLifetime :
+        MAX (1, pkt_RouterLifetime) /* 0 is OK for RL, but not for HAL*/
+      );
+      len += sizeof(struct nd_opt_homeagent_info);
+    }
+
+  if (zif->rtadv.AdvIntervalOption)
+    {
+      struct nd_opt_adv_interval *ndopt_adv = 
+       (struct nd_opt_adv_interval *)(buf + len);
+      ndopt_adv->nd_opt_ai_type = ND_OPT_ADV_INTERVAL;
+      ndopt_adv->nd_opt_ai_len = 1;
+      ndopt_adv->nd_opt_ai_reserved = 0;
+      ndopt_adv->nd_opt_ai_interval = htonl(zif->rtadv.MaxRtrAdvInterval);
+      len += sizeof(struct nd_opt_adv_interval);
+    }
+
+  /* Fill in prefix. */
+  for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix))
+    {
+      struct nd_opt_prefix_info *pinfo;
+
+      pinfo = (struct nd_opt_prefix_info *) (buf + len);
+
+      pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+      pinfo->nd_opt_pi_len = 4;
+      pinfo->nd_opt_pi_prefix_len = rprefix->prefix.prefixlen;
+
+      pinfo->nd_opt_pi_flags_reserved = 0;
+      if (rprefix->AdvOnLinkFlag)
+       pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK;
+      if (rprefix->AdvAutonomousFlag)
+       pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
+      if (rprefix->AdvRouterAddressFlag)
+       pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_RADDR;
+
+      pinfo->nd_opt_pi_valid_time = htonl (rprefix->AdvValidLifetime);
+      pinfo->nd_opt_pi_preferred_time = htonl (rprefix->AdvPreferredLifetime);
+      pinfo->nd_opt_pi_reserved2 = 0;
+
+      IPV6_ADDR_COPY (&pinfo->nd_opt_pi_prefix, &rprefix->prefix.prefix);
+
+#ifdef DEBUG
+      {
+       u_char buf[INET6_ADDRSTRLEN];
+
+       zlog_debug ("DEBUG %s", inet_ntop (AF_INET6, &pinfo->nd_opt_pi_prefix, 
+                  buf, INET6_ADDRSTRLEN));
+
+      }
+#endif /* DEBUG */
+
+      len += sizeof (struct nd_opt_prefix_info);
+    }
+
+  /* Hardware address. */
+  if (ifp->hw_addr_len != 0)
+    {
+      buf[len++] = ND_OPT_SOURCE_LINKADDR;
+
+      /* Option length should be rounded up to next octet if
+         the link address does not end on an octet boundary. */
+      buf[len++] = (ifp->hw_addr_len + 9) >> 3;
+
+      memcpy (buf + len, ifp->hw_addr, ifp->hw_addr_len);
+      len += ifp->hw_addr_len;
+
+      /* Pad option to end on an octet boundary. */
+      memset (buf + len, 0, -(ifp->hw_addr_len + 2) & 0x7);
+      len += -(ifp->hw_addr_len + 2) & 0x7;
+    }
+
+  /* MTU */
+  if (zif->rtadv.AdvLinkMTU)
+    {
+      struct nd_opt_mtu * opt = (struct nd_opt_mtu *) (buf + len);
+      opt->nd_opt_mtu_type = ND_OPT_MTU;
+      opt->nd_opt_mtu_len = 1;
+      opt->nd_opt_mtu_reserved = 0;
+      opt->nd_opt_mtu_mtu = htonl (zif->rtadv.AdvLinkMTU);
+      len += sizeof (struct nd_opt_mtu);
+    }
+
+  msg.msg_name = (void *) &addr;
+  msg.msg_namelen = sizeof (struct sockaddr_in6);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = (void *) adata;
+  msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+  msg.msg_flags = 0;
+  iov.iov_base = buf;
+  iov.iov_len = len;
+
+  cmsgptr = ZCMSG_FIRSTHDR(&msg);
+  cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+  cmsgptr->cmsg_level = IPPROTO_IPV6;
+  cmsgptr->cmsg_type = IPV6_PKTINFO;
+
+  pkt = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
+  memset (&pkt->ipi6_addr, 0, sizeof (struct in6_addr));
+  pkt->ipi6_ifindex = ifp->ifindex;
+
+  ret = sendmsg (sock, &msg, 0);
+  if (ret < 0)
+    {
+      zlog_err ("rtadv_send_packet: sendmsg %d (%s)\n",
+               errno, safe_strerror(errno));
+    }
+}
+
+static int
+rtadv_timer (struct thread *thread)
+{
+  struct zebra_vrf *zvrf = THREAD_ARG (thread);
+  struct listnode *node, *nnode;
+  struct interface *ifp;
+  struct zebra_if *zif;
+  int period;
+
+  zvrf->rtadv.ra_timer = NULL;
+  if (zvrf->rtadv.adv_msec_if_count == 0)
+    {
+      period = 1000; /* 1 s */
+      rtadv_event (zvrf, RTADV_TIMER, 1 /* 1 s */);
+    } 
+  else
+    {
+      period = 10; /* 10 ms */
+      rtadv_event (zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */);
+    }
+
+  for (ALL_LIST_ELEMENTS (vrf_iflist (zvrf->vrf_id), node, nnode, ifp))
+    {
+      if (if_is_loopback (ifp) || ! if_is_operative (ifp))
+       continue;
+
+      zif = ifp->info;
+
+      if (zif->rtadv.AdvSendAdvertisements)
+       { 
+         zif->rtadv.AdvIntervalTimer -= period;
+         if (zif->rtadv.AdvIntervalTimer <= 0)
+           {
+             /* FIXME: using MaxRtrAdvInterval each time isn't what section
+                6.2.4 of RFC4861 tells to do. */
+             zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
+             rtadv_send_packet (zvrf->rtadv.sock, ifp);
+           }
+       }
+    }
+  return 0;
+}
+
+static void
+rtadv_process_solicit (struct interface *ifp)
+{
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  zlog_info ("Router solicitation received on %s vrf %u", ifp->name, zvrf->vrf_id);
+
+  rtadv_send_packet (zvrf->rtadv.sock, ifp);
+}
+
+static void
+rtadv_process_advert (void)
+{
+  zlog_info ("Router advertisement received");
+}
+
+static void
+rtadv_process_packet (u_char *buf, unsigned int len, ifindex_t ifindex,
+    int hoplimit, vrf_id_t vrf_id)
+{
+  struct icmp6_hdr *icmph;
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  /* Interface search. */
+  ifp = if_lookup_by_index_vrf (ifindex, vrf_id);
+  if (ifp == NULL)
+    {
+      zlog_warn ("Unknown interface index: %d, vrf %u", ifindex, vrf_id);
+      return;
+    }
+
+  if (if_is_loopback (ifp))
+    return;
+
+  /* Check interface configuration. */
+  zif = ifp->info;
+  if (! zif->rtadv.AdvSendAdvertisements)
+    return;
+
+  /* ICMP message length check. */
+  if (len < sizeof (struct icmp6_hdr))
+    {
+      zlog_warn ("Invalid ICMPV6 packet length: %d", len);
+      return;
+    }
+
+  icmph = (struct icmp6_hdr *) buf;
+
+  /* ICMP message type check. */
+  if (icmph->icmp6_type != ND_ROUTER_SOLICIT &&
+      icmph->icmp6_type != ND_ROUTER_ADVERT)
+    {
+      zlog_warn ("Unwanted ICMPV6 message type: %d", icmph->icmp6_type);
+      return;
+    }
+
+  /* Hoplimit check. */
+  if (hoplimit >= 0 && hoplimit != 255)
+    {
+      zlog_warn ("Invalid hoplimit %d for router advertisement ICMP packet",
+                hoplimit);
+      return;
+    }
+
+  /* Check ICMP message type. */
+  if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
+    rtadv_process_solicit (ifp);
+  else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
+    rtadv_process_advert ();
+
+  return;
+}
+
+static int
+rtadv_read (struct thread *thread)
+{
+  int sock;
+  int len;
+  u_char buf[RTADV_MSG_SIZE];
+  struct sockaddr_in6 from;
+  ifindex_t ifindex = 0;
+  int hoplimit = -1;
+  struct zebra_vrf *zvrf = THREAD_ARG (thread);
+
+  sock = THREAD_FD (thread);
+  zvrf->rtadv.ra_read = NULL;
+
+  /* Register myself. */
+  rtadv_event (zvrf, RTADV_READ, sock);
+
+  len = rtadv_recv_packet (sock, buf, sizeof (buf), &from, &ifindex, &hoplimit);
+
+  if (len < 0) 
+    {
+      zlog_warn ("router solicitation recv failed: %s.", safe_strerror (errno));
+      return len;
+    }
+
+  rtadv_process_packet (buf, (unsigned)len, ifindex, hoplimit, zvrf->vrf_id);
+
+  return 0;
+}
+
+static int
+rtadv_make_socket (vrf_id_t vrf_id)
+{
+  int sock;
+  int ret;
+  struct icmp6_filter filter;
+
+  if ( zserv_privs.change (ZPRIVS_RAISE) )
+       zlog_err ("rtadv_make_socket: could not raise privs, %s",
+                  safe_strerror (errno) );
+                  
+  sock = vrf_socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, vrf_id);
+
+  if ( zserv_privs.change (ZPRIVS_LOWER) )
+       zlog_err ("rtadv_make_socket: could not lower privs, %s",
+                                safe_strerror (errno) );
+
+  /* When we can't make ICMPV6 socket simply back.  Router
+     advertisement feature will not be supported. */
+  if (sock < 0)
+    {
+      close (sock);
+      return -1;
+    }
+
+  ret = setsockopt_ipv6_pktinfo (sock, 1);
+  if (ret < 0)
+    {
+      close (sock);
+      return ret;
+    }
+  ret = setsockopt_ipv6_multicast_loop (sock, 0);
+  if (ret < 0)
+    {
+      close (sock);
+      return ret;
+    }
+  ret = setsockopt_ipv6_unicast_hops (sock, 255);
+  if (ret < 0)
+    {
+      close (sock);
+      return ret;
+    }
+  ret = setsockopt_ipv6_multicast_hops (sock, 255);
+  if (ret < 0)
+    {
+      close (sock);
+      return ret;
+    }
+  ret = setsockopt_ipv6_hoplimit (sock, 1);
+  if (ret < 0)
+    {
+      close (sock);
+      return ret;
+    }
+
+  ICMP6_FILTER_SETBLOCKALL(&filter);
+  ICMP6_FILTER_SETPASS (ND_ROUTER_SOLICIT, &filter);
+  ICMP6_FILTER_SETPASS (ND_ROUTER_ADVERT, &filter);
+
+  ret = setsockopt (sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+                   sizeof (struct icmp6_filter));
+  if (ret < 0)
+    {
+      zlog_info ("ICMP6_FILTER set fail: %s", safe_strerror (errno));
+      return ret;
+    }
+
+  return sock;
+}
+
+static struct rtadv_prefix *
+rtadv_prefix_new (void)
+{
+  return XCALLOC (MTYPE_RTADV_PREFIX, sizeof (struct rtadv_prefix));
+}
+
+static void
+rtadv_prefix_free (struct rtadv_prefix *rtadv_prefix)
+{
+  XFREE (MTYPE_RTADV_PREFIX, rtadv_prefix);
+}
+
+static struct rtadv_prefix *
+rtadv_prefix_lookup (struct list *rplist, struct prefix_ipv6 *p)
+{
+  struct listnode *node;
+  struct rtadv_prefix *rprefix;
+
+  for (ALL_LIST_ELEMENTS_RO (rplist, node, rprefix))
+    if (prefix_same ((struct prefix *) &rprefix->prefix, (struct prefix *) p))
+      return rprefix;
+  return NULL;
+}
+
+static struct rtadv_prefix *
+rtadv_prefix_get (struct list *rplist, struct prefix_ipv6 *p)
+{
+  struct rtadv_prefix *rprefix;
+  
+  rprefix = rtadv_prefix_lookup (rplist, p);
+  if (rprefix)
+    return rprefix;
+
+  rprefix = rtadv_prefix_new ();
+  memcpy (&rprefix->prefix, p, sizeof (struct prefix_ipv6));
+  listnode_add (rplist, rprefix);
+
+  return rprefix;
+}
+
+static void
+rtadv_prefix_set (struct zebra_if *zif, struct rtadv_prefix *rp)
+{
+  struct rtadv_prefix *rprefix;
+  
+  rprefix = rtadv_prefix_get (zif->rtadv.AdvPrefixList, &rp->prefix);
+
+  /* Set parameters. */
+  rprefix->AdvValidLifetime = rp->AdvValidLifetime;
+  rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime;
+  rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag;
+  rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag;
+  rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag;
+}
+
+static int
+rtadv_prefix_reset (struct zebra_if *zif, struct rtadv_prefix *rp)
+{
+  struct rtadv_prefix *rprefix;
+  
+  rprefix = rtadv_prefix_lookup (zif->rtadv.AdvPrefixList, &rp->prefix);
+  if (rprefix != NULL)
+    {
+      listnode_delete (zif->rtadv.AdvPrefixList, (void *) rprefix);
+      rtadv_prefix_free (rprefix);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+DEFUN (ipv6_nd_suppress_ra,
+       ipv6_nd_suppress_ra_cmd,
+       "ipv6 nd suppress-ra",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Suppress Router Advertisement\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+
+  ifp = vty->index;
+  zif = ifp->info;
+  zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  if (if_is_loopback (ifp))
+    {
+      vty_out (vty, "Invalid interface%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (zif->rtadv.AdvSendAdvertisements)
+    {
+      zif->rtadv.AdvSendAdvertisements = 0;
+      zif->rtadv.AdvIntervalTimer = 0;
+      zvrf->rtadv.adv_if_count--;
+
+      if_leave_all_router (zvrf->rtadv.sock, ifp);
+
+      if (zvrf->rtadv.adv_if_count == 0)
+        rtadv_event (zvrf, RTADV_STOP, 0);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_suppress_ra,
+       no_ipv6_nd_suppress_ra_cmd,
+       "no ipv6 nd suppress-ra",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Suppress Router Advertisement\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+
+  ifp = vty->index;
+  zif = ifp->info;
+  zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  if (if_is_loopback (ifp))
+    {
+      vty_out (vty, "Invalid interface%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (! zif->rtadv.AdvSendAdvertisements)
+    {
+      zif->rtadv.AdvSendAdvertisements = 1;
+      zif->rtadv.AdvIntervalTimer = 0;
+      zvrf->rtadv.adv_if_count++;
+
+      if_join_all_router (zvrf->rtadv.sock, ifp);
+
+      if (zvrf->rtadv.adv_if_count == 1)
+        rtadv_event (zvrf, RTADV_START, zvrf->rtadv.sock);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_ra_interval_msec,
+       ipv6_nd_ra_interval_msec_cmd,
+       "ipv6 nd ra-interval msec <70-1800000>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router Advertisement interval\n"
+       "Router Advertisement interval in milliseconds\n")
+{
+  unsigned interval;
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[0], 70, 1800000);
+  if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime * 1000))
+  {
+    vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (zif->rtadv.MaxRtrAdvInterval % 1000)
+    zvrf->rtadv.adv_msec_if_count--;
+
+  if (interval % 1000)
+    zvrf->rtadv.adv_msec_if_count++;
+  
+  zif->rtadv.MaxRtrAdvInterval = interval;
+  zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
+  zif->rtadv.AdvIntervalTimer = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_ra_interval,
+       ipv6_nd_ra_interval_cmd,
+       "ipv6 nd ra-interval <1-1800>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router Advertisement interval\n"
+       "Router Advertisement interval in seconds\n")
+{
+  unsigned interval;
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[0], 1, 1800);
+  if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime))
+  {
+    vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+
+  if (zif->rtadv.MaxRtrAdvInterval % 1000)
+    zvrf->rtadv.adv_msec_if_count--;
+       
+  /* convert to milliseconds */
+  interval = interval * 1000; 
+       
+  zif->rtadv.MaxRtrAdvInterval = interval;
+  zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
+  zif->rtadv.AdvIntervalTimer = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_ra_interval,
+       no_ipv6_nd_ra_interval_cmd,
+       "no ipv6 nd ra-interval",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router Advertisement interval\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct zebra_vrf *zvrf;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+  zvrf = vrf_info_lookup (ifp->vrf_id);
+
+  if (zif->rtadv.MaxRtrAdvInterval % 1000)
+    zvrf->rtadv.adv_msec_if_count--;
+  
+  zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
+  zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
+  zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_ra_interval,
+       no_ipv6_nd_ra_interval_val_cmd,
+       "no ipv6 nd ra-interval <1-1800>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router Advertisement interval\n")
+
+ALIAS (no_ipv6_nd_ra_interval,
+       no_ipv6_nd_ra_interval_msec_val_cmd,
+       "no ipv6 nd ra-interval msec <1-1800000>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router Advertisement interval\n"
+       "Router Advertisement interval in milliseconds\n")
+
+DEFUN (ipv6_nd_ra_lifetime,
+       ipv6_nd_ra_lifetime_cmd,
+       "ipv6 nd ra-lifetime <0-9000>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router lifetime\n"
+       "Router lifetime in seconds (0 stands for a non-default gw)\n")
+{
+  int lifetime;
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  VTY_GET_INTEGER_RANGE ("router lifetime", lifetime, argv[0], 0, 9000);
+
+  /* The value to be placed in the Router Lifetime field
+   * of Router Advertisements sent from the interface,
+   * in seconds.  MUST be either zero or between
+   * MaxRtrAdvInterval and 9000 seconds. -- RFC4861, 6.2.1 */
+  if ((lifetime != 0 && lifetime * 1000 < zif->rtadv.MaxRtrAdvInterval))
+    {
+      vty_out (vty, "This ra-lifetime would conflict with configured ra-interval%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  zif->rtadv.AdvDefaultLifetime = lifetime;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_ra_lifetime,
+       no_ipv6_nd_ra_lifetime_cmd,
+       "no ipv6 nd ra-lifetime",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router lifetime\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvDefaultLifetime = -1;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_ra_lifetime,
+       no_ipv6_nd_ra_lifetime_val_cmd,
+       "no ipv6 nd ra-lifetime <0-9000>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Router lifetime\n"
+       "Router lifetime in seconds (0 stands for a non-default gw)\n")
+
+DEFUN (ipv6_nd_reachable_time,
+       ipv6_nd_reachable_time_cmd,
+       "ipv6 nd reachable-time <1-3600000>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Reachable time\n"
+       "Reachable time in milliseconds\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  VTY_GET_INTEGER_RANGE ("reachable time", zif->rtadv.AdvReachableTime, argv[0], 1, RTADV_MAX_REACHABLE_TIME);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_reachable_time,
+       no_ipv6_nd_reachable_time_cmd,
+       "no ipv6 nd reachable-time",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Reachable time\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvReachableTime = 0;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_reachable_time,
+       no_ipv6_nd_reachable_time_val_cmd,
+       "no ipv6 nd reachable-time <1-3600000>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Reachable time\n"
+       "Reachable time in milliseconds\n")
+
+DEFUN (ipv6_nd_homeagent_preference,
+       ipv6_nd_homeagent_preference_cmd,
+       "ipv6 nd home-agent-preference <0-65535>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent preference\n"
+       "preference value (default is 0, least preferred)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  VTY_GET_INTEGER_RANGE ("home agent preference", zif->rtadv.HomeAgentPreference, argv[0], 0, 65535);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_homeagent_preference,
+       no_ipv6_nd_homeagent_preference_cmd,
+       "no ipv6 nd home-agent-preference",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent preference\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.HomeAgentPreference = 0;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_homeagent_preference,
+       no_ipv6_nd_homeagent_preference_val_cmd,
+       "no ipv6 nd home-agent-preference <0-65535>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent preference\n"
+       "preference value (default is 0, least preferred)\n")
+
+DEFUN (ipv6_nd_homeagent_lifetime,
+       ipv6_nd_homeagent_lifetime_cmd,
+       "ipv6 nd home-agent-lifetime <0-65520>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent lifetime\n"
+       "Home Agent lifetime in seconds (0 to track ra-lifetime)\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  VTY_GET_INTEGER_RANGE ("home agent lifetime", zif->rtadv.HomeAgentLifetime, argv[0], 0, RTADV_MAX_HALIFETIME);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_homeagent_lifetime,
+       no_ipv6_nd_homeagent_lifetime_cmd,
+       "no ipv6 nd home-agent-lifetime",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent lifetime\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.HomeAgentLifetime = -1;
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_homeagent_lifetime,
+       no_ipv6_nd_homeagent_lifetime_val_cmd,
+       "no ipv6 nd home-agent-lifetime <0-65520>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent lifetime\n"
+       "Home Agent lifetime in seconds (0 to track ra-lifetime)\n")
+
+DEFUN (ipv6_nd_managed_config_flag,
+       ipv6_nd_managed_config_flag_cmd,
+       "ipv6 nd managed-config-flag",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Managed address configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvManagedFlag = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_managed_config_flag,
+       no_ipv6_nd_managed_config_flag_cmd,
+       "no ipv6 nd managed-config-flag",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Managed address configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvManagedFlag = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_homeagent_config_flag,
+       ipv6_nd_homeagent_config_flag_cmd,
+       "ipv6 nd home-agent-config-flag",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvHomeAgentFlag = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_homeagent_config_flag,
+       no_ipv6_nd_homeagent_config_flag_cmd,
+       "no ipv6 nd home-agent-config-flag",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Home Agent configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvHomeAgentFlag = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_adv_interval_config_option,
+       ipv6_nd_adv_interval_config_option_cmd,
+       "ipv6 nd adv-interval-option",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Advertisement Interval Option\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvIntervalOption = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_adv_interval_config_option,
+       no_ipv6_nd_adv_interval_config_option_cmd,
+       "no ipv6 nd adv-interval-option",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Advertisement Interval Option\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvIntervalOption = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_other_config_flag,
+       ipv6_nd_other_config_flag_cmd,
+       "ipv6 nd other-config-flag",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Other statefull configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvOtherConfigFlag = 1;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_other_config_flag,
+       no_ipv6_nd_other_config_flag_cmd,
+       "no ipv6 nd other-config-flag",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Other statefull configuration flag\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.AdvOtherConfigFlag = 0;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_prefix,
+       ipv6_nd_prefix_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|) (router-address|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for onlink determination\n"
+       "Do not use prefix for autoconfiguration\n"
+       "Set Router Address flag\n")
+{
+  int i;
+  int ret;
+  int cursor = 1;
+  struct interface *ifp;
+  struct zebra_if *zebra_if;
+  struct rtadv_prefix rp;
+
+  ifp = (struct interface *) vty->index;
+  zebra_if = ifp->info;
+
+  ret = str2prefix_ipv6 (argv[0], &rp.prefix);
+  if (!ret)
+    {
+      vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */
+  rp.AdvOnLinkFlag = 1;
+  rp.AdvAutonomousFlag = 1;
+  rp.AdvRouterAddressFlag = 0;
+  rp.AdvValidLifetime = RTADV_VALID_LIFETIME;
+  rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME;
+
+  if (argc > 1)
+    {
+      if ((isdigit((unsigned char)argv[1][0]))
+         || strncmp (argv[1], "i", 1) == 0)
+       {
+         if ( strncmp (argv[1], "i", 1) == 0)
+           rp.AdvValidLifetime = UINT32_MAX;
+         else
+           rp.AdvValidLifetime = (u_int32_t) strtoll (argv[1],
+               (char **)NULL, 10);
+      
+         if ( strncmp (argv[2], "i", 1) == 0)
+           rp.AdvPreferredLifetime = UINT32_MAX;
+         else
+           rp.AdvPreferredLifetime = (u_int32_t) strtoll (argv[2],
+               (char **)NULL, 10);
+
+         if (rp.AdvPreferredLifetime > rp.AdvValidLifetime)
+           {
+             vty_out (vty, "Invalid preferred lifetime%s", VTY_NEWLINE);
+             return CMD_WARNING;
+           }
+         cursor = cursor + 2;
+       }
+      if (argc > cursor)
+       {
+         for (i = cursor; i < argc; i++)
+           {
+             if (strncmp (argv[i], "of", 2) == 0)
+               rp.AdvOnLinkFlag = 0;
+             if (strncmp (argv[i], "no", 2) == 0)
+               rp.AdvAutonomousFlag = 0;
+             if (strncmp (argv[i], "ro", 2) == 0)
+               rp.AdvRouterAddressFlag = 1;
+           }
+       }
+    }
+
+  rtadv_prefix_set (zebra_if, &rp);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_nortaddr_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for onlink determination\n"
+       "Do not use prefix for autoconfiguration\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_rev_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for autoconfiguration\n"
+       "Do not use prefix for onlink determination\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_rev_rtaddr_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|) (router-address|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for autoconfiguration\n"
+       "Do not use prefix for onlink determination\n"
+       "Set Router Address flag\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_noauto_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for autoconfiguration")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_offlink_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Do not use prefix for onlink determination\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_rtaddr_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (router-address|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n"
+       "Set Router Address flag\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_val_cmd,
+       "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Valid lifetime in seconds\n"
+       "Infinite valid lifetime\n"
+       "Preferred lifetime in seconds\n"
+       "Infinite preferred lifetime\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_noval_cmd,
+       "ipv6 nd prefix X:X::X:X/M (no-autoconfig|) (off-link|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Do not use prefix for autoconfiguration\n"
+       "Do not use prefix for onlink determination\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_noval_rev_cmd,
+       "ipv6 nd prefix X:X::X:X/M (off-link|) (no-autoconfig|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Do not use prefix for onlink determination\n"
+       "Do not use prefix for autoconfiguration\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_noval_noauto_cmd,
+       "ipv6 nd prefix X:X::X:X/M (no-autoconfig|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Do not use prefix for autoconfiguration\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_noval_offlink_cmd,
+       "ipv6 nd prefix X:X::X:X/M (off-link|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Do not use prefix for onlink determination\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_noval_rtaddr_cmd,
+       "ipv6 nd prefix X:X::X:X/M (router-address|)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n"
+       "Set Router Address flag\n")
+
+ALIAS (ipv6_nd_prefix,
+       ipv6_nd_prefix_prefix_cmd,
+       "ipv6 nd prefix X:X::X:X/M",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n")
+
+DEFUN (no_ipv6_nd_prefix,
+       no_ipv6_nd_prefix_cmd,
+       "no ipv6 nd prefix IPV6PREFIX",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Prefix information\n"
+       "IPv6 prefix\n")
+{
+  int ret;
+  struct interface *ifp;
+  struct zebra_if *zebra_if;
+  struct rtadv_prefix rp;
+
+  ifp = (struct interface *) vty->index;
+  zebra_if = ifp->info;
+
+  ret = str2prefix_ipv6 (argv[0], &rp.prefix);
+  if (!ret)
+    {
+      vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */
+
+  ret = rtadv_prefix_reset (zebra_if, &rp);
+  if (!ret)
+    {
+      vty_out (vty, "Non-exist IPv6 prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_nd_router_preference,
+       ipv6_nd_router_preference_cmd,
+       "ipv6 nd router-preference (high|medium|low)",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Default router preference\n"
+       "High default router preference\n"
+       "Low default router preference\n"
+       "Medium default router preference (default)\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+  int i = 0;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  while (0 != rtadv_pref_strs[i])
+    {
+      if (strncmp (argv[0], rtadv_pref_strs[i], 1) == 0)
+       {
+         zif->rtadv.DefaultPreference = i;
+         return CMD_SUCCESS;
+       }
+      i++;
+    }
+
+  return CMD_ERR_NO_MATCH;
+}
+
+DEFUN (no_ipv6_nd_router_preference,
+       no_ipv6_nd_router_preference_cmd,
+       "no ipv6 nd router-preference",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Default router preference\n")
+{
+  struct interface *ifp;
+  struct zebra_if *zif;
+
+  ifp = (struct interface *) vty->index;
+  zif = ifp->info;
+
+  zif->rtadv.DefaultPreference = RTADV_PREF_MEDIUM; /* Default per RFC4191. */
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_router_preference,
+       no_ipv6_nd_router_preference_val_cmd,
+       "no ipv6 nd router-preference (high|medium|low)",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Default router preference\n"
+       "High default router preference\n"
+       "Low default router preference\n"
+       "Medium default router preference (default)\n")
+
+DEFUN (ipv6_nd_mtu,
+       ipv6_nd_mtu_cmd,
+       "ipv6 nd mtu <1-65535>",
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Advertised MTU\n"
+       "MTU in bytes\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  VTY_GET_INTEGER_RANGE ("MTU", zif->rtadv.AdvLinkMTU, argv[0], 1, 65535);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_nd_mtu,
+       no_ipv6_nd_mtu_cmd,
+       "no ipv6 nd mtu",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Advertised MTU\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct zebra_if *zif = ifp->info;
+  zif->rtadv.AdvLinkMTU = 0;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_nd_mtu,
+       no_ipv6_nd_mtu_val_cmd,
+       "no ipv6 nd mtu <1-65535>",
+       NO_STR
+       "Interface IPv6 config commands\n"
+       "Neighbor discovery\n"
+       "Advertised MTU\n"
+       "MTU in bytes\n")
+
+/* Write configuration about router advertisement. */
+void
+rtadv_config_write (struct vty *vty, struct interface *ifp)
+{
+  struct zebra_if *zif;
+  struct listnode *node;
+  struct rtadv_prefix *rprefix;
+  char buf[PREFIX_STRLEN];
+  int interval;
+
+  zif = ifp->info;
+
+  if (! if_is_loopback (ifp))
+    {
+      if (zif->rtadv.AdvSendAdvertisements)
+       vty_out (vty, " no ipv6 nd suppress-ra%s", VTY_NEWLINE);
+    }
+
+  
+  interval = zif->rtadv.MaxRtrAdvInterval;
+  if (interval % 1000)
+    vty_out (vty, " ipv6 nd ra-interval msec %d%s", interval,
+            VTY_NEWLINE);
+  else
+    if (interval != RTADV_MAX_RTR_ADV_INTERVAL)
+      vty_out (vty, " ipv6 nd ra-interval %d%s", interval / 1000,
+            VTY_NEWLINE);
+
+  if (zif->rtadv.AdvIntervalOption)
+    vty_out (vty, " ipv6 nd adv-interval-option%s", VTY_NEWLINE);
+
+  if (zif->rtadv.AdvDefaultLifetime != -1)
+    vty_out (vty, " ipv6 nd ra-lifetime %d%s", zif->rtadv.AdvDefaultLifetime,
+            VTY_NEWLINE);
+
+  if (zif->rtadv.HomeAgentPreference)
+    vty_out (vty, " ipv6 nd home-agent-preference %u%s",
+            zif->rtadv.HomeAgentPreference, VTY_NEWLINE);
+
+  if (zif->rtadv.HomeAgentLifetime != -1)
+    vty_out (vty, " ipv6 nd home-agent-lifetime %u%s",
+            zif->rtadv.HomeAgentLifetime, VTY_NEWLINE);
+
+  if (zif->rtadv.AdvHomeAgentFlag)
+    vty_out (vty, " ipv6 nd home-agent-config-flag%s", VTY_NEWLINE);
+
+  if (zif->rtadv.AdvReachableTime)
+    vty_out (vty, " ipv6 nd reachable-time %d%s", zif->rtadv.AdvReachableTime,
+            VTY_NEWLINE);
+
+  if (zif->rtadv.AdvManagedFlag)
+    vty_out (vty, " ipv6 nd managed-config-flag%s", VTY_NEWLINE);
+
+  if (zif->rtadv.AdvOtherConfigFlag)
+    vty_out (vty, " ipv6 nd other-config-flag%s", VTY_NEWLINE);
+
+  if (zif->rtadv.DefaultPreference != RTADV_PREF_MEDIUM)
+    vty_out (vty, " ipv6 nd router-preference %s%s",
+            rtadv_pref_strs[zif->rtadv.DefaultPreference],
+            VTY_NEWLINE);
+
+  if (zif->rtadv.AdvLinkMTU)
+    vty_out (vty, " ipv6 nd mtu %d%s", zif->rtadv.AdvLinkMTU, VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix))
+    {
+      vty_out (vty, " ipv6 nd prefix %s",
+               prefix2str (&rprefix->prefix, buf, sizeof(buf)));
+      if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) || 
+         (rprefix->AdvPreferredLifetime != RTADV_PREFERRED_LIFETIME))
+       {
+         if (rprefix->AdvValidLifetime == UINT32_MAX)
+           vty_out (vty, " infinite");
+         else
+           vty_out (vty, " %u", rprefix->AdvValidLifetime);
+         if (rprefix->AdvPreferredLifetime == UINT32_MAX)
+           vty_out (vty, " infinite");
+         else
+           vty_out (vty, " %u", rprefix->AdvPreferredLifetime);
+       }
+      if (!rprefix->AdvOnLinkFlag)
+       vty_out (vty, " off-link");
+      if (!rprefix->AdvAutonomousFlag)
+       vty_out (vty, " no-autoconfig");
+      if (rprefix->AdvRouterAddressFlag)
+       vty_out (vty, " router-address");
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+
+static void
+rtadv_event (struct zebra_vrf *zvrf, enum rtadv_event event, int val)
+{
+  struct rtadv *rtadv = &zvrf->rtadv;
+
+  switch (event)
+    {
+    case RTADV_START:
+      if (! rtadv->ra_read)
+       rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zvrf, val);
+      if (! rtadv->ra_timer)
+       rtadv->ra_timer = thread_add_event (zebrad.master, rtadv_timer,
+                                           zvrf, 0);
+      break;
+    case RTADV_STOP:
+      if (rtadv->ra_timer)
+       {
+         thread_cancel (rtadv->ra_timer);
+         rtadv->ra_timer = NULL;
+       }
+      if (rtadv->ra_read)
+       {
+         thread_cancel (rtadv->ra_read);
+         rtadv->ra_read = NULL;
+       }
+      break;
+    case RTADV_TIMER:
+      if (! rtadv->ra_timer)
+       rtadv->ra_timer = thread_add_timer (zebrad.master, rtadv_timer, zvrf,
+                                           val);
+      break;
+    case RTADV_TIMER_MSEC:
+      if (! rtadv->ra_timer)
+       rtadv->ra_timer = thread_add_timer_msec (zebrad.master, rtadv_timer, 
+                                           zvrf, val);
+      break;
+    case RTADV_READ:
+      if (! rtadv->ra_read)
+       rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zvrf, val);
+      break;
+    default:
+      break;
+    }
+  return;
+}
+
+void
+rtadv_init (struct zebra_vrf *zvrf)
+{
+  zvrf->rtadv.sock = rtadv_make_socket (zvrf->vrf_id);
+}
+
+void
+rtadv_terminate (struct zebra_vrf *zvrf)
+{
+  rtadv_event (zvrf, RTADV_STOP, 0);
+
+  if (zvrf->rtadv.sock >= 0)
+    {
+      close (zvrf->rtadv.sock);
+      zvrf->rtadv.sock = -1;
+    }
+
+  zvrf->rtadv.adv_if_count = 0;
+  zvrf->rtadv.adv_msec_if_count = 0;
+}
+
+void
+rtadv_cmd_init (void)
+{
+  install_element (INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_msec_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_val_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_msec_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_reachable_time_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_reachable_time_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_reachable_time_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_managed_config_flag_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_other_config_flag_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_config_flag_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_adv_interval_config_option_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_adv_interval_config_option_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rev_rtaddr_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_nortaddr_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rev_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_noauto_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_offlink_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rtaddr_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_rev_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_noauto_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_offlink_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_rtaddr_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_prefix_prefix_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_prefix_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_router_preference_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_val_cmd);
+  install_element (INTERFACE_NODE, &ipv6_nd_mtu_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_mtu_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_nd_mtu_val_cmd);
+}
+
+static int
+if_join_all_router (int sock, struct interface *ifp)
+{
+  int ret;
+
+  struct ipv6_mreq mreq;
+
+  memset (&mreq, 0, sizeof (struct ipv6_mreq));
+  inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
+  mreq.ipv6mr_interface = ifp->ifindex;
+
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 
+                   (char *) &mreq, sizeof mreq);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", safe_strerror (errno));
+
+  zlog_info ("rtadv: %s join to all-routers multicast group", ifp->name);
+
+  return 0;
+}
+
+static int
+if_leave_all_router (int sock, struct interface *ifp)
+{
+  int ret;
+
+  struct ipv6_mreq mreq;
+
+  memset (&mreq, 0, sizeof (struct ipv6_mreq));
+  inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr);
+  mreq.ipv6mr_interface = ifp->ifindex;
+
+  ret = setsockopt (sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, 
+                   (char *) &mreq, sizeof mreq);
+  if (ret < 0)
+    zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s", safe_strerror (errno));
+
+  zlog_info ("rtadv: %s leave from all-routers multicast group", ifp->name);
+
+  return 0;
+}
+
+#else
+void
+rtadv_init (struct zebra_vrf *zvrf)
+{
+  /* Empty.*/;
+}
+void
+rtadv_terminate (struct zebra_vrf *zvrf)
+{
+  /* Empty.*/;
+}
+void
+rtadv_cmd_init (void)
+{
+  /* Empty.*/;
+}
+#endif /* HAVE_RTADV && HAVE_IPV6 */
diff --git a/zebra/rtadv.h b/zebra/rtadv.h
new file mode 100644 (file)
index 0000000..160814b
--- /dev/null
@@ -0,0 +1,107 @@
+/* Router advertisement
+ * Copyright (C) 2005 6WIND <jean-mickael.guerin@6wind.com>
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RTADV_H
+#define _ZEBRA_RTADV_H
+
+#include "vty.h"
+#include "zebra/interface.h"
+
+/* NB: RTADV is defined in zebra/interface.h above */
+#if defined (HAVE_RTADV)
+
+/* Router advertisement prefix. */
+struct rtadv_prefix
+{
+  /* Prefix to be advertised. */
+  struct prefix_ipv6 prefix;
+  
+  /* The value to be placed in the Valid Lifetime in the Prefix */
+  u_int32_t AdvValidLifetime;
+#define RTADV_VALID_LIFETIME 2592000
+
+  /* The value to be placed in the on-link flag */
+  int AdvOnLinkFlag;
+
+  /* The value to be placed in the Preferred Lifetime in the Prefix
+     Information option, in seconds.*/
+  u_int32_t AdvPreferredLifetime;
+#define RTADV_PREFERRED_LIFETIME 604800
+
+  /* The value to be placed in the Autonomous Flag. */
+  int AdvAutonomousFlag;
+
+  /* The value to be placed in the Router Address Flag [RFC6275 7.2]. */
+  int AdvRouterAddressFlag;
+#ifndef ND_OPT_PI_FLAG_RADDR
+#define ND_OPT_PI_FLAG_RADDR         0x20
+#endif
+
+};
+
+extern void rtadv_config_write (struct vty *, struct interface *);
+
+/* RFC4584 Extension to Sockets API for Mobile IPv6 */
+
+#ifndef ND_OPT_ADV_INTERVAL
+#define ND_OPT_ADV_INTERVAL    7   /* Adv Interval Option */
+#endif
+#ifndef ND_OPT_HA_INFORMATION
+#define ND_OPT_HA_INFORMATION  8   /* HA Information Option */
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_ADV_INTERVAL
+struct nd_opt_adv_interval {   /* Advertisement interval option */
+        uint8_t        nd_opt_ai_type;
+        uint8_t        nd_opt_ai_len;
+        uint16_t       nd_opt_ai_reserved;
+        uint32_t       nd_opt_ai_interval;
+} __attribute__((__packed__));
+#else
+#ifndef HAVE_STRUCT_ND_OPT_ADV_INTERVAL_ND_OPT_AI_TYPE
+/* fields may have to be renamed */
+#define nd_opt_ai_type         nd_opt_adv_interval_type
+#define nd_opt_ai_len          nd_opt_adv_interval_len
+#define nd_opt_ai_reserved     nd_opt_adv_interval_reserved
+#define nd_opt_ai_interval     nd_opt_adv_interval_ival
+#endif
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_HOMEAGENT_INFO
+struct nd_opt_homeagent_info {  /* Home Agent info */
+        u_int8_t        nd_opt_hai_type;
+        u_int8_t        nd_opt_hai_len;
+        u_int16_t       nd_opt_hai_reserved;
+        u_int16_t       nd_opt_hai_preference;
+        u_int16_t       nd_opt_hai_lifetime;
+} __attribute__((__packed__));
+#endif
+
+extern const char *rtadv_pref_strs[];
+
+#endif /* HAVE_RTADV */
+
+extern void rtadv_init (struct zebra_vrf *);
+extern void rtadv_terminate (struct zebra_vrf *);
+extern void rtadv_cmd_init (void);
+
+#endif /* _ZEBRA_RTADV_H */
diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c
new file mode 100644 (file)
index 0000000..e1ec670
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Kernel routing table readup by getmsg(2)
+ * Copyright (C) 1999 Michael Handler
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "log.h"
+#include "if.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+
+#include <sys/stream.h>
+#include <sys/tihdr.h>
+
+/* Solaris defines these in both <netinet/in.h> and <inet/in.h>, sigh */
+#ifdef SUNOS_5
+#include <sys/tiuser.h>
+#ifndef T_CURRENT
+#define T_CURRENT       MI_T_CURRENT
+#endif /* T_CURRENT */
+#ifndef IRE_CACHE
+#define IRE_CACHE               0x0020  /* Cached Route entry */
+#endif /* IRE_CACHE */
+#ifndef IRE_HOST_REDIRECT
+#define IRE_HOST_REDIRECT       0x0200  /* Host route entry from redirects */
+#endif /* IRE_HOST_REDIRECT */
+#ifndef IRE_CACHETABLE
+#define IRE_CACHETABLE          (IRE_CACHE | IRE_BROADCAST | IRE_LOCAL | \
+                                 IRE_LOOPBACK)
+#endif /* IRE_CACHETABLE */
+#undef IPOPT_EOL
+#undef IPOPT_NOP
+#undef IPOPT_LSRR
+#undef IPOPT_RR
+#undef IPOPT_SSRR
+#endif /* SUNOS_5 */
+
+#include <inet/common.h>
+#include <inet/ip.h>
+#include <inet/mib2.h>
+
+/* device to read IP routing table from */
+#ifndef _PATH_GETMSG_ROUTE
+#define _PATH_GETMSG_ROUTE     "/dev/ip"
+#endif /* _PATH_GETMSG_ROUTE */
+
+#define RT_BUFSIZ              8192
+
+static void 
+handle_route_entry (mib2_ipRouteEntry_t *routeEntry)
+{
+       struct prefix_ipv4      prefix;
+       struct in_addr          tmpaddr, gateway;
+       u_char                  zebra_flags = 0;
+
+       if (routeEntry->ipRouteInfo.re_ire_type & IRE_CACHETABLE)
+               return;
+
+       if (routeEntry->ipRouteInfo.re_ire_type & IRE_HOST_REDIRECT)
+               zebra_flags |= ZEBRA_FLAG_SELFROUTE;
+
+       prefix.family = AF_INET;
+
+       tmpaddr.s_addr = routeEntry->ipRouteDest;
+       prefix.prefix = tmpaddr;
+
+       tmpaddr.s_addr = routeEntry->ipRouteMask;
+       prefix.prefixlen = ip_masklen (tmpaddr);
+
+       gateway.s_addr = routeEntry->ipRouteNextHop;
+
+       rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &prefix,
+                     &gateway, NULL, 0, VRF_DEFAULT, RT_TABLE_MAIN,
+                     0, 0, 0, SAFI_UNICAST);
+}
+
+void
+route_read (struct zebra_vrf *zvrf)
+{
+       char                    storage[RT_BUFSIZ];
+
+       struct T_optmgmt_req    *TLIreq = (struct T_optmgmt_req *) storage;
+       struct T_optmgmt_ack    *TLIack = (struct T_optmgmt_ack *) storage;
+       struct T_error_ack      *TLIerr = (struct T_error_ack *) storage;
+
+       struct opthdr           *MIB2hdr;
+
+       mib2_ipRouteEntry_t     *routeEntry, *lastRouteEntry;
+
+       struct strbuf           msgdata;
+       int                     flags, dev, retval, process;
+
+       if (zvrf->vrf_id != VRF_DEFAULT) {
+               return;
+       }
+
+       if ((dev = open (_PATH_GETMSG_ROUTE, O_RDWR)) == -1) {
+               zlog_warn ("can't open %s: %s", _PATH_GETMSG_ROUTE,
+                       safe_strerror (errno));
+               return;
+       }
+
+       TLIreq->PRIM_type = T_OPTMGMT_REQ;
+       TLIreq->OPT_offset = sizeof (struct T_optmgmt_req);
+       TLIreq->OPT_length = sizeof (struct opthdr);
+       TLIreq->MGMT_flags = T_CURRENT;
+
+       MIB2hdr = (struct opthdr *) &TLIreq[1];
+
+       MIB2hdr->level = MIB2_IP;
+       MIB2hdr->name = 0;
+       MIB2hdr->len = 0;
+       
+       msgdata.buf = storage;
+       msgdata.len = sizeof (struct T_optmgmt_req) + sizeof (struct opthdr);
+
+       flags = 0;
+
+       if (putmsg (dev, &msgdata, NULL, flags) == -1) {
+               zlog_warn ("putmsg failed: %s", safe_strerror (errno));
+               goto exit;
+       }
+
+       MIB2hdr = (struct opthdr *) &TLIack[1];
+       msgdata.maxlen = sizeof (storage);
+
+       while (1) {
+               flags = 0;
+               retval = getmsg (dev, &msgdata, NULL, &flags);
+
+               if (retval == -1) {
+                       zlog_warn ("getmsg(ctl) failed: %s", safe_strerror (errno));
+                       goto exit;
+               }
+
+               /* This is normal loop termination */
+               if (retval == 0 &&
+                       (size_t)msgdata.len >= sizeof (struct T_optmgmt_ack) &&
+                       TLIack->PRIM_type == T_OPTMGMT_ACK &&
+                       TLIack->MGMT_flags == T_SUCCESS &&
+                       MIB2hdr->len == 0)
+                       break;
+
+               if ((size_t)msgdata.len >= sizeof (struct T_error_ack) &&
+                       TLIerr->PRIM_type == T_ERROR_ACK) {
+                       zlog_warn ("getmsg(ctl) returned T_ERROR_ACK: %s",
+                               safe_strerror ((TLIerr->TLI_error == TSYSERR)
+                               ? TLIerr->UNIX_error : EPROTO));
+                       break;
+               }
+
+               /* should dump more debugging info to the log statement,
+                  like what GateD does in this instance, but not
+                  critical yet. */
+               if (retval != MOREDATA ||
+                       (size_t)msgdata.len < sizeof (struct T_optmgmt_ack) ||
+                       TLIack->PRIM_type != T_OPTMGMT_ACK ||
+                       TLIack->MGMT_flags != T_SUCCESS) {
+                       errno = ENOMSG;
+                       zlog_warn ("getmsg(ctl) returned bizarreness");
+                       break;
+               }
+
+               /* MIB2_IP_21 is the the pseudo-MIB2 ipRouteTable
+                  entry, see <inet/mib2.h>. "This isn't the MIB data
+                  you're looking for." */
+               process = (MIB2hdr->level == MIB2_IP &&
+                       MIB2hdr->name == MIB2_IP_21) ? 1 : 0;
+
+               /* getmsg writes the data buffer out completely, not
+                  to the closest smaller multiple. Unless reassembling
+                  data structures across buffer boundaries is your idea
+                  of a good time, set maxlen to the closest smaller
+                  multiple of the size of the datastructure you're
+                  retrieving. */
+               msgdata.maxlen = sizeof (storage) - (sizeof (storage)
+                       % sizeof (mib2_ipRouteEntry_t));
+
+               msgdata.len = 0;
+               flags = 0;
+
+               do {
+                       retval = getmsg (dev, NULL, &msgdata, &flags);
+
+                       if (retval == -1) {
+                               zlog_warn ("getmsg(data) failed: %s",
+                                       safe_strerror (errno));
+                               goto exit;
+                       }
+
+                       if (!(retval == 0 || retval == MOREDATA)) {
+                               zlog_warn ("getmsg(data) returned %d", retval);
+                               goto exit;
+                       }
+
+                       if (process) {
+                               if (msgdata.len %
+                                       sizeof (mib2_ipRouteEntry_t) != 0) {
+                                       zlog_warn ("getmsg(data) returned "
+"msgdata.len = %d (%% sizeof (mib2_ipRouteEntry_t) != 0)", msgdata.len);
+                                       goto exit;
+                               }
+
+                               routeEntry = (mib2_ipRouteEntry_t *)
+                                       msgdata.buf;
+                               lastRouteEntry = (mib2_ipRouteEntry_t *)
+                                       (msgdata.buf + msgdata.len);
+                               do {
+                                       handle_route_entry (routeEntry);
+                               } while (++routeEntry < lastRouteEntry);
+                       }
+               } while (retval == MOREDATA);
+       }
+
+exit:
+       close (dev);
+}
diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c
new file mode 100644 (file)
index 0000000..1f65864
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Kernel routing table readup by netlink
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "zebra/zserv.h"
+#include "rt_netlink.h"
+
+void route_read (struct zebra_vrf *zvrf)
+{
+  netlink_route_read (zvrf);
+}
diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c
new file mode 100644 (file)
index 0000000..385e150
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Kernel routing table read by sysctl function.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "log.h"
+#include "vrf.h"
+
+#include "zebra/zserv.h"
+#include "zebra/rt.h"
+#include "zebra/kernel_socket.h"
+
+/* Kernel routing table read up by sysctl function. */
+void
+route_read (struct zebra_vrf *zvrf)
+{
+  caddr_t buf, end, ref;
+  size_t bufsiz;
+  struct rt_msghdr *rtm;
+  
+#define MIBSIZ 6
+  int mib[MIBSIZ] = 
+  {
+    CTL_NET,
+    PF_ROUTE,
+    0,
+    0,
+    NET_RT_DUMP,
+    0
+  };
+
+  if (zvrf->vrf_id != VRF_DEFAULT)
+    return;
+
+  /* Get buffer size. */
+  if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog_warn ("sysctl fail: %s", safe_strerror (errno));
+      return;
+    }
+
+  /* Allocate buffer. */
+  ref = buf = XMALLOC (MTYPE_TMP, bufsiz);
+  
+  /* Read routing table information by calling sysctl(). */
+  if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) 
+    {
+      zlog_warn ("sysctl() fail by %s", safe_strerror (errno));
+      return;
+    }
+
+  for (end = buf + bufsiz; buf < end; buf += rtm->rtm_msglen) 
+    {
+      rtm = (struct rt_msghdr *) buf;
+      /* We must set RTF_DONE here, so rtm_read() doesn't ignore the message. */
+      SET_FLAG (rtm->rtm_flags, RTF_DONE);
+      rtm_read (rtm);
+    }
+
+  /* Free buffer. */
+  XFREE (MTYPE_TMP, ref);
+
+  return;
+}
diff --git a/zebra/test_main.c b/zebra/test_main.c
new file mode 100644 (file)
index 0000000..09f53ad
--- /dev/null
@@ -0,0 +1,396 @@
+/* main routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "command.h"
+#include "thread.h"
+#include "filter.h"
+#include "memory.h"
+#include "prefix.h"
+#include "log.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "vrf.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/router-id.h"
+#include "zebra/interface.h"
+
+/* Zebra instance */
+struct zebra_t zebrad =
+{
+  .rtm_table_default = 0,
+};
+
+/* process id. */
+pid_t pid;
+
+/* zebra_rib's workqueue hold time. Private export for use by test code only */
+extern int rib_process_hold_time;
+
+/* Pacify zclient.o in libzebra, which expects this variable. */
+struct thread_master *master;
+
+/* Command line options. */
+struct option longopts[] = 
+{
+  { "batch",       no_argument,       NULL, 'b'},
+  { "daemon",      no_argument,       NULL, 'd'},
+  { "config_file", required_argument, NULL, 'f'},
+  { "help",        no_argument,       NULL, 'h'},
+  { "vty_addr",    required_argument, NULL, 'A'},
+  { "vty_port",    required_argument, NULL, 'P'},
+  { "version",     no_argument,       NULL, 'v'},
+  { "rib_hold",           required_argument, NULL, 'r'},
+  { 0 }
+};
+
+zebra_capabilities_t _caps_p [] = 
+{
+  ZCAP_NET_ADMIN,
+  ZCAP_SYS_ADMIN,
+  ZCAP_NET_RAW,
+};
+
+/* Default configuration file path. */
+char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE;
+
+/* Process ID saved for use by init system */
+const char *pid_file = PATH_ZEBRA_PID;
+
+/* Help information display. */
+static void
+usage (char *progname, int status)
+{
+  if (status != 0)
+    fprintf (stderr, "Try `%s --help' for more information.\n", progname);
+  else
+    {    
+      printf ("Usage : %s [OPTION...]\n\n"\
+             "Daemon which manages kernel routing table management and "\
+             "redistribution between different routing protocols.\n\n"\
+             "-b, --batch        Runs in batch mode\n"\
+             "-d, --daemon       Runs in daemon mode\n"\
+             "-f, --config_file  Set configuration file name\n"\
+             "-A, --vty_addr     Set vty's bind address\n"\
+             "-P, --vty_port     Set vty's port number\n"\
+             "-r, --rib_hold     Set rib-queue hold time\n"\
+              "-v, --version      Print program version\n"\
+             "-h, --help         Display this help and exit\n"\
+             "\n"\
+             "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
+    }
+
+  exit (status);
+}
+
+static ifindex_t test_ifindex = 0;
+
+/* testrib commands */
+DEFUN (test_interface_state,
+       test_interface_state_cmd,
+       "state (up|down)",
+       "configure interface\n"
+       "up\n"
+       "down\n")
+{
+  struct interface *ifp;
+  if (argc < 1)
+    return CMD_WARNING;
+  
+  ifp = vty->index;
+  if (ifp->ifindex == IFINDEX_INTERNAL)
+    {
+      ifp->ifindex = ++test_ifindex;
+      ifp->mtu = 1500;
+      ifp->flags = IFF_BROADCAST|IFF_MULTICAST;
+    }
+  
+  switch (argv[0][0])
+    {
+      case 'u':
+        SET_FLAG (ifp->flags, IFF_UP);
+        if_add_update (ifp);
+        printf ("up\n");
+        break;
+      case 'd':
+        UNSET_FLAG (ifp->flags, IFF_UP);
+        if_delete_update (ifp);
+        printf ("down\n");
+        break;
+      default:
+        return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+static void
+test_cmd_init (void)
+{
+  install_element (INTERFACE_NODE, &test_interface_state_cmd);
+}
+
+/* SIGHUP handler. */
+static void 
+sighup (void)
+{
+  zlog_info ("SIGHUP received");
+
+  /* Reload of config file. */
+  ;
+}
+
+/* SIGINT handler. */
+static void
+sigint (void)
+{
+  zlog_notice ("Terminating on signal");
+
+  exit (0);
+}
+
+/* SIGUSR1 handler. */
+static void
+sigusr1 (void)
+{
+  zlog_rotate (NULL);
+}
+
+struct quagga_signal_t zebra_signals[] =
+{
+  { 
+    .signal = SIGHUP, 
+    .handler = &sighup,
+  },
+  {
+    .signal = SIGUSR1,
+    .handler = &sigusr1,
+  },
+  {
+    .signal = SIGINT,
+    .handler = &sigint,
+  },
+  {
+    .signal = SIGTERM,
+    .handler = &sigint,
+  },
+};
+
+/* Callback upon creating a new VRF. */
+static int
+zebra_vrf_new (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = *info;
+
+  if (! zvrf)
+    {
+      zvrf = zebra_vrf_alloc (vrf_id);
+      *info = (void *)zvrf;
+    }
+
+  return 0;
+}
+
+/* Callback upon enabling a VRF. */
+static int
+zebra_vrf_enable (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info);
+
+  assert (zvrf);
+
+  kernel_init (zvrf);
+  route_read (zvrf);
+
+  return 0;
+}
+
+/* Callback upon disabling a VRF. */
+static int
+zebra_vrf_disable (vrf_id_t vrf_id, void **info)
+{
+  struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info);
+  struct listnode *list_node;
+  struct interface *ifp;
+
+  assert (zvrf);
+
+  rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]);
+  rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]);
+
+  for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), list_node, ifp))
+    {
+      int operative = if_is_operative (ifp);
+      UNSET_FLAG (ifp->flags, IFF_UP);
+      if (operative)
+        if_down (ifp);
+    }
+
+  kernel_terminate (zvrf);
+
+  return 0;
+}
+
+/* Zebra VRF initialization. */
+static void
+zebra_vrf_init (void)
+{
+  vrf_add_hook (VRF_NEW_HOOK, zebra_vrf_new);
+  vrf_add_hook (VRF_ENABLE_HOOK, zebra_vrf_enable);
+  vrf_add_hook (VRF_DISABLE_HOOK, zebra_vrf_disable);
+  vrf_init ();
+}
+
+/* Main startup routine. */
+int
+main (int argc, char **argv)
+{
+  char *p;
+  char *vty_addr = NULL;
+  int vty_port = 0;
+  int batch_mode = 0;
+  int daemon_mode = 0;
+  char *config_file = NULL;
+  char *progname;
+  struct thread thread;
+
+  /* Set umask before anything for security */
+  umask (0027);
+
+  /* preserve my name */
+  progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+
+  zlog_default = openzlog (progname, ZLOG_ZEBRA,
+                          LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+  while (1) 
+    {
+      int opt;
+  
+      opt = getopt_long (argc, argv, "bdf:hA:P:r:v", longopts, 0);
+
+      if (opt == EOF)
+       break;
+
+      switch (opt) 
+       {
+       case 0:
+         break;
+       case 'b':
+         batch_mode = 1;
+       case 'd':
+         daemon_mode = 1;
+         break;
+       case 'f':
+         config_file = optarg;
+         break;
+       case 'A':
+         vty_addr = optarg;
+         break;
+       case 'P':
+         /* Deal with atoi() returning 0 on failure, and zebra not
+            listening on zebra port... */
+         if (strcmp(optarg, "0") == 0) 
+           {
+             vty_port = 0;
+             break;
+           } 
+         vty_port = atoi (optarg);
+         break;
+       case 'r':
+         rib_process_hold_time = atoi(optarg);
+         break;
+       case 'v':
+         print_version (progname);
+         exit (0);
+         break;
+       case 'h':
+         usage (progname, 0);
+         break;
+       default:
+         usage (progname, 1);
+         break;
+       }
+    }
+  
+  /* port and conf file mandatory */
+  if (!vty_port || !config_file)
+    {
+      fprintf (stderr, "Error: --vty_port and --config_file arguments"
+                       " are both required\n");
+      usage (progname, 1);
+    }
+  
+  /* Make master thread emulator. */
+  zebrad.master = thread_master_create ();
+
+  /* Vty related initialize. */
+  signal_init (zebrad.master, array_size(zebra_signals), zebra_signals);
+  cmd_init (1);
+  vty_init (zebrad.master);
+  memory_init ();
+  zebra_debug_init ();
+  zebra_if_init ();
+  test_cmd_init ();
+
+  /* Zebra related initialize. */
+  rib_init ();
+  access_list_init ();
+
+  /* Make kernel routing socket. */
+  zebra_vrf_init ();
+  zebra_vty_init();
+
+  /* Configuration file read*/
+  vty_read_config (config_file, config_default);
+
+  /* Clean up rib. */
+  rib_weed_tables ();
+
+  /* Exit when zebra is working in batch mode. */
+  if (batch_mode)
+    exit (0);
+
+  /* Daemonize. */
+  if (daemon_mode && daemon (0, 0) < 0)
+    {
+      perror("daemon start failed");
+      exit (1);
+    }
+
+  /* Needed for BSD routing socket. */
+  pid = getpid ();
+
+  /* Make vty server socket. */
+  vty_serv_sock (vty_addr, vty_port, "/tmp/test_zebra");
+
+  /* Print banner. */
+  zlog_notice ("Zebra %s starting: vty@%d", QUAGGA_VERSION, vty_port);
+
+  while (thread_fetch (zebrad.master, &thread))
+    thread_call (&thread);
+
+  /* Not reached... */
+  return 0;
+}
diff --git a/zebra/testrib.conf b/zebra/testrib.conf
new file mode 100644 (file)
index 0000000..0df7dc2
--- /dev/null
@@ -0,0 +1,76 @@
+!
+! Zebra configuration saved from vty
+!   2007/04/01 17:46:48
+!
+password foo
+log stdout
+service advanced-vty
+!
+debug zebra rib
+debug zebra kernel
+!
+interface eth0
+ ip address 10.0.0.1/24
+ ipv6 address 1::0:1/64
+ state up
+!
+interface eth1
+ ip address 10.0.1.1/24
+ ipv6 address 1::1:1/64
+!
+interface eth2
+ ip address 10.0.2.1/24
+ ipv6 address 1::2:1/64
+!
+! Unnumbered
+interface foo1
+ ip address 192.168.1.1/32
+ ipv6 address 2::1:1/128
+!
+interface foo0
+ ip address 192.168.1.1/32
+ ip address 192.168.1.1/24 label foo
+ ipv6 address 2::1:1/128
+ state up
+!
+
+! statics that should be subsumed by connected routes, according to interface
+! state
+ip route 10.0.0.0/24 10.0.1.254
+ip route 10.0.1.0/24 10.0.2.254
+ip route 10.0.2.0/24 10.0.0.254
+ipv6 route 1::0:0/64 1::1:f
+ipv6 route 1::1:0/64 1::2:f
+ipv6 route 1::2:0/64 1::0:f
+
+! null route
+ip route 10.1.0.1/32 null0
+ipv6 route 100::1:1/128 null0
+
+! normalish routes
+ip route 1.1.2.0/24 10.0.0.2
+ipv6 route 80::/64 1::0:e
+
+! different admin distances
+ip route 1.1.0.2/32 10.0.0.3 10
+ip route 1.1.0.2/32 10.0.0.4 20
+ip route 1.1.0.2/32 10.0.1.3 30
+
+ipv6 route 90::1/128 1::0:a 10
+ipv6 route 90::1/128 1::0:b 20
+ipv6 route 90::1/128 1::1:c 30
+
+! multiple-nexthop + distance
+ip route 1.1.0.2/32 10.0.0.5 10
+ipv6 route 90::1/128 1::0:d 10
+
+! a recursive route, potentially.
+ip route 1.1.3.0/24 10.0.0.2
+! double recursive, potentially
+ip route 1.1.0.1/32 1.1.3.1
+!
+ip route 1.1.1.0/24 1.1.2.2
+
+line vty
+ exec-timeout 0 0
+!
diff --git a/zebra/zebra.conf.sample b/zebra/zebra.conf.sample
new file mode 100644 (file)
index 0000000..a5d0732
--- /dev/null
@@ -0,0 +1,25 @@
+! -*- zebra -*-
+!
+! zebra sample configuration file
+!
+! $Id: zebra.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
+!
+hostname Router
+password zebra
+enable password zebra
+!
+! Interface's description. 
+!
+!interface lo
+! description test of desc.
+!
+!interface sit0
+! multicast
+
+!
+! Static default route sample.
+!
+!ip route 0.0.0.0/0 203.181.89.241
+!
+
+!log file zebra.log
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
new file mode 100644 (file)
index 0000000..22fc6ca
--- /dev/null
@@ -0,0 +1,1756 @@
+/*
+ * Main implementation file for interface to Forwarding Plane Manager.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "stream.h"
+#include "thread.h"
+#include "network.h"
+#include "command.h"
+
+#include "zebra/rib.h"
+
+#include "fpm/fpm.h"
+#include "zebra_fpm.h"
+#include "zebra_fpm_private.h"
+
+/*
+ * Interval at which we attempt to connect to the FPM.
+ */
+#define ZFPM_CONNECT_RETRY_IVL   5
+
+/*
+ * Sizes of outgoing and incoming stream buffers for writing/reading
+ * FPM messages.
+ */
+#define ZFPM_OBUF_SIZE (2 * FPM_MAX_MSG_LEN)
+#define ZFPM_IBUF_SIZE (FPM_MAX_MSG_LEN)
+
+/*
+ * The maximum number of times the FPM socket write callback can call
+ * 'write' before it yields.
+ */
+#define ZFPM_MAX_WRITES_PER_RUN 10
+
+/*
+ * Interval over which we collect statistics.
+ */
+#define ZFPM_STATS_IVL_SECS        10
+
+/*
+ * Structure that holds state for iterating over all route_node
+ * structures that are candidates for being communicated to the FPM.
+ */
+typedef struct zfpm_rnodes_iter_t_
+{
+  rib_tables_iter_t tables_iter;
+  route_table_iter_t iter;
+} zfpm_rnodes_iter_t;
+
+/*
+ * Statistics.
+ */
+typedef struct zfpm_stats_t_ {
+  unsigned long connect_calls;
+  unsigned long connect_no_sock;
+
+  unsigned long read_cb_calls;
+
+  unsigned long write_cb_calls;
+  unsigned long write_calls;
+  unsigned long partial_writes;
+  unsigned long max_writes_hit;
+  unsigned long t_write_yields;
+
+  unsigned long nop_deletes_skipped;
+  unsigned long route_adds;
+  unsigned long route_dels;
+
+  unsigned long updates_triggered;
+  unsigned long redundant_triggers;
+  unsigned long non_fpm_table_triggers;
+
+  unsigned long dests_del_after_update;
+
+  unsigned long t_conn_down_starts;
+  unsigned long t_conn_down_dests_processed;
+  unsigned long t_conn_down_yields;
+  unsigned long t_conn_down_finishes;
+
+  unsigned long t_conn_up_starts;
+  unsigned long t_conn_up_dests_processed;
+  unsigned long t_conn_up_yields;
+  unsigned long t_conn_up_aborts;
+  unsigned long t_conn_up_finishes;
+
+} zfpm_stats_t;
+
+/*
+ * States for the FPM state machine.
+ */
+typedef enum {
+
+  /*
+   * In this state we are not yet ready to connect to the FPM. This
+   * can happen when this module is disabled, or if we're cleaning up
+   * after a connection has gone down.
+   */
+  ZFPM_STATE_IDLE,
+
+  /*
+   * Ready to talk to the FPM and periodically trying to connect to
+   * it.
+   */
+  ZFPM_STATE_ACTIVE,
+
+  /*
+   * In the middle of bringing up a TCP connection. Specifically,
+   * waiting for a connect() call to complete asynchronously.
+   */
+  ZFPM_STATE_CONNECTING,
+
+  /*
+   * TCP connection to the FPM is up.
+   */
+  ZFPM_STATE_ESTABLISHED
+
+} zfpm_state_t;
+
+/*
+ * Message format to be used to communicate with the FPM.
+ */
+typedef enum
+{
+  ZFPM_MSG_FORMAT_NONE,
+  ZFPM_MSG_FORMAT_NETLINK,
+  ZFPM_MSG_FORMAT_PROTOBUF,
+} zfpm_msg_format_e;
+/*
+ * Globals.
+ */
+typedef struct zfpm_glob_t_
+{
+
+  /*
+   * True if the FPM module has been enabled.
+   */
+  int enabled;
+
+  /*
+   * Message format to be used to communicate with the fpm.
+   */
+  zfpm_msg_format_e message_format;
+
+  struct thread_master *master;
+
+  zfpm_state_t state;
+
+  in_addr_t   fpm_server;
+  /*
+   * Port on which the FPM is running.
+   */
+  int fpm_port;
+
+  /*
+   * List of rib_dest_t structures to be processed
+   */
+  TAILQ_HEAD (zfpm_dest_q, rib_dest_t_) dest_q;
+
+  /*
+   * Stream socket to the FPM.
+   */
+  int sock;
+
+  /*
+   * Buffers for messages to/from the FPM.
+   */
+  struct stream *obuf;
+  struct stream *ibuf;
+
+  /*
+   * Threads for I/O.
+   */
+  struct thread *t_connect;
+  struct thread *t_write;
+  struct thread *t_read;
+
+  /*
+   * Thread to clean up after the TCP connection to the FPM goes down
+   * and the state that belongs to it.
+   */
+  struct thread *t_conn_down;
+
+  struct {
+    zfpm_rnodes_iter_t iter;
+  } t_conn_down_state;
+
+  /*
+   * Thread to take actions once the TCP conn to the FPM comes up, and
+   * the state that belongs to it.
+   */
+  struct thread *t_conn_up;
+
+  struct {
+    zfpm_rnodes_iter_t iter;
+  } t_conn_up_state;
+
+  unsigned long connect_calls;
+  time_t last_connect_call_time;
+
+  /*
+   * Stats from the start of the current statistics interval up to
+   * now. These are the counters we typically update in the code.
+   */
+  zfpm_stats_t stats;
+
+  /*
+   * Statistics that were gathered in the last collection interval.
+   */
+  zfpm_stats_t last_ivl_stats;
+
+  /*
+   * Cumulative stats from the last clear to the start of the current
+   * statistics interval.
+   */
+  zfpm_stats_t cumulative_stats;
+
+  /*
+   * Stats interval timer.
+   */
+  struct thread *t_stats;
+
+  /*
+   * If non-zero, the last time when statistics were cleared.
+   */
+  time_t last_stats_clear_time;
+
+} zfpm_glob_t;
+
+static zfpm_glob_t zfpm_glob_space;
+static zfpm_glob_t *zfpm_g = &zfpm_glob_space;
+
+static int zfpm_read_cb (struct thread *thread);
+static int zfpm_write_cb (struct thread *thread);
+
+static void zfpm_set_state (zfpm_state_t state, const char *reason);
+static void zfpm_start_connect_timer (const char *reason);
+static void zfpm_start_stats_timer (void);
+
+/*
+ * zfpm_thread_should_yield
+ */
+static inline int
+zfpm_thread_should_yield (struct thread *t)
+{
+  return thread_should_yield (t);
+}
+
+/*
+ * zfpm_state_to_str
+ */
+static const char *
+zfpm_state_to_str (zfpm_state_t state)
+{
+  switch (state)
+    {
+
+    case ZFPM_STATE_IDLE:
+      return "idle";
+
+    case ZFPM_STATE_ACTIVE:
+      return "active";
+
+    case ZFPM_STATE_CONNECTING:
+      return "connecting";
+
+    case ZFPM_STATE_ESTABLISHED:
+      return "established";
+
+    default:
+      return "unknown";
+    }
+}
+
+/*
+ * zfpm_get_time
+ */
+static time_t
+zfpm_get_time (void)
+{
+  struct timeval tv;
+
+  if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0)
+    zlog_warn ("FPM: quagga_gettime failed!!");
+
+  return tv.tv_sec;
+}
+
+/*
+ * zfpm_get_elapsed_time
+ *
+ * Returns the time elapsed (in seconds) since the given time.
+ */
+static time_t
+zfpm_get_elapsed_time (time_t reference)
+{
+  time_t now;
+
+  now = zfpm_get_time ();
+
+  if (now < reference)
+    {
+      assert (0);
+      return 0;
+    }
+
+  return now - reference;
+}
+
+/*
+ * zfpm_is_table_for_fpm
+ *
+ * Returns TRUE if the the given table is to be communicated to the
+ * FPM.
+ */
+static inline int
+zfpm_is_table_for_fpm (struct route_table *table)
+{
+  rib_table_info_t *info;
+
+  info = rib_table_info (table);
+
+  /*
+   * We only send the unicast tables in the main instance to the FPM
+   * at this point.
+   */
+  if (info->zvrf->vrf_id != 0)
+    return 0;
+
+  if (info->safi != SAFI_UNICAST)
+    return 0;
+
+  return 1;
+}
+
+/*
+ * zfpm_rnodes_iter_init
+ */
+static inline void
+zfpm_rnodes_iter_init (zfpm_rnodes_iter_t *iter)
+{
+  memset (iter, 0, sizeof (*iter));
+  rib_tables_iter_init (&iter->tables_iter);
+
+  /*
+   * This is a hack, but it makes implementing 'next' easier by
+   * ensuring that route_table_iter_next() will return NULL the first
+   * time we call it.
+   */
+  route_table_iter_init (&iter->iter, NULL);
+  route_table_iter_cleanup (&iter->iter);
+}
+
+/*
+ * zfpm_rnodes_iter_next
+ */
+static inline struct route_node *
+zfpm_rnodes_iter_next (zfpm_rnodes_iter_t *iter)
+{
+  struct route_node *rn;
+  struct route_table *table;
+
+  while (1)
+    {
+      rn = route_table_iter_next (&iter->iter);
+      if (rn)
+       return rn;
+
+      /*
+       * We've made our way through this table, go to the next one.
+       */
+      route_table_iter_cleanup (&iter->iter);
+
+      while ((table = rib_tables_iter_next (&iter->tables_iter)))
+       {
+         if (zfpm_is_table_for_fpm (table))
+           break;
+       }
+
+      if (!table)
+       return NULL;
+
+      route_table_iter_init (&iter->iter, table);
+    }
+
+  return NULL;
+}
+
+/*
+ * zfpm_rnodes_iter_pause
+ */
+static inline void
+zfpm_rnodes_iter_pause (zfpm_rnodes_iter_t *iter)
+{
+  route_table_iter_pause (&iter->iter);
+}
+
+/*
+ * zfpm_rnodes_iter_cleanup
+ */
+static inline void
+zfpm_rnodes_iter_cleanup (zfpm_rnodes_iter_t *iter)
+{
+  route_table_iter_cleanup (&iter->iter);
+  rib_tables_iter_cleanup (&iter->tables_iter);
+}
+
+/*
+ * zfpm_stats_init
+ *
+ * Initialize a statistics block.
+ */
+static inline void
+zfpm_stats_init (zfpm_stats_t *stats)
+{
+  memset (stats, 0, sizeof (*stats));
+}
+
+/*
+ * zfpm_stats_reset
+ */
+static inline void
+zfpm_stats_reset (zfpm_stats_t *stats)
+{
+  zfpm_stats_init (stats);
+}
+
+/*
+ * zfpm_stats_copy
+ */
+static inline void
+zfpm_stats_copy (const zfpm_stats_t *src, zfpm_stats_t *dest)
+{
+  memcpy (dest, src, sizeof (*dest));
+}
+
+/*
+ * zfpm_stats_compose
+ *
+ * Total up the statistics in two stats structures ('s1 and 's2') and
+ * return the result in the third argument, 'result'. Note that the
+ * pointer 'result' may be the same as 's1' or 's2'.
+ *
+ * For simplicity, the implementation below assumes that the stats
+ * structure is composed entirely of counters. This can easily be
+ * changed when necessary.
+ */
+static void
+zfpm_stats_compose (const zfpm_stats_t *s1, const zfpm_stats_t *s2,
+                   zfpm_stats_t *result)
+{
+  const unsigned long *p1, *p2;
+  unsigned long *result_p;
+  int i, num_counters;
+
+  p1 = (const unsigned long *) s1;
+  p2 = (const unsigned long *) s2;
+  result_p = (unsigned long *) result;
+
+  num_counters = (sizeof (zfpm_stats_t) / sizeof (unsigned long));
+
+  for (i = 0; i < num_counters; i++)
+    {
+      result_p[i] = p1[i] + p2[i];
+    }
+}
+
+/*
+ * zfpm_read_on
+ */
+static inline void
+zfpm_read_on (void)
+{
+  assert (!zfpm_g->t_read);
+  assert (zfpm_g->sock >= 0);
+
+  THREAD_READ_ON (zfpm_g->master, zfpm_g->t_read, zfpm_read_cb, 0,
+                 zfpm_g->sock);
+}
+
+/*
+ * zfpm_write_on
+ */
+static inline void
+zfpm_write_on (void)
+{
+  assert (!zfpm_g->t_write);
+  assert (zfpm_g->sock >= 0);
+
+  THREAD_WRITE_ON (zfpm_g->master, zfpm_g->t_write, zfpm_write_cb, 0,
+                  zfpm_g->sock);
+}
+
+/*
+ * zfpm_read_off
+ */
+static inline void
+zfpm_read_off (void)
+{
+  THREAD_READ_OFF (zfpm_g->t_read);
+}
+
+/*
+ * zfpm_write_off
+ */
+static inline void
+zfpm_write_off (void)
+{
+  THREAD_WRITE_OFF (zfpm_g->t_write);
+}
+
+/*
+ * zfpm_conn_up_thread_cb
+ *
+ * Callback for actions to be taken when the connection to the FPM
+ * comes up.
+ */
+static int
+zfpm_conn_up_thread_cb (struct thread *thread)
+{
+  struct route_node *rnode;
+  zfpm_rnodes_iter_t *iter;
+  rib_dest_t *dest;
+
+  assert (zfpm_g->t_conn_up);
+  zfpm_g->t_conn_up = NULL;
+
+  iter = &zfpm_g->t_conn_up_state.iter;
+
+  if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
+    {
+      zfpm_debug ("Connection not up anymore, conn_up thread aborting");
+      zfpm_g->stats.t_conn_up_aborts++;
+      goto done;
+    }
+
+  while ((rnode = zfpm_rnodes_iter_next (iter)))
+    {
+      dest = rib_dest_from_rnode (rnode);
+
+      if (dest)
+       {
+         zfpm_g->stats.t_conn_up_dests_processed++;
+         zfpm_trigger_update (rnode, NULL);
+       }
+
+      /*
+       * Yield if need be.
+       */
+      if (!zfpm_thread_should_yield (thread))
+       continue;
+
+      zfpm_g->stats.t_conn_up_yields++;
+      zfpm_rnodes_iter_pause (iter);
+      zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
+                                                zfpm_conn_up_thread_cb,
+                                                0, 0);
+      return 0;
+    }
+
+  zfpm_g->stats.t_conn_up_finishes++;
+
+ done:
+  zfpm_rnodes_iter_cleanup (iter);
+  return 0;
+}
+
+/*
+ * zfpm_connection_up
+ *
+ * Called when the connection to the FPM comes up.
+ */
+static void
+zfpm_connection_up (const char *detail)
+{
+  assert (zfpm_g->sock >= 0);
+  zfpm_read_on ();
+  zfpm_write_on ();
+  zfpm_set_state (ZFPM_STATE_ESTABLISHED, detail);
+
+  /*
+   * Start thread to push existing routes to the FPM.
+   */
+  assert (!zfpm_g->t_conn_up);
+
+  zfpm_rnodes_iter_init (&zfpm_g->t_conn_up_state.iter);
+
+  zfpm_debug ("Starting conn_up thread");
+  zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
+                                            zfpm_conn_up_thread_cb, 0, 0);
+  zfpm_g->stats.t_conn_up_starts++;
+}
+
+/*
+ * zfpm_connect_check
+ *
+ * Check if an asynchronous connect() to the FPM is complete.
+ */
+static void
+zfpm_connect_check ()
+{
+  int status;
+  socklen_t slen;
+  int ret;
+
+  zfpm_read_off ();
+  zfpm_write_off ();
+
+  slen = sizeof (status);
+  ret = getsockopt (zfpm_g->sock, SOL_SOCKET, SO_ERROR, (void *) &status,
+                   &slen);
+
+  if (ret >= 0 && status == 0)
+    {
+      zfpm_connection_up ("async connect complete");
+      return;
+    }
+
+  /*
+   * getsockopt() failed or indicated an error on the socket.
+   */
+  close (zfpm_g->sock);
+  zfpm_g->sock = -1;
+
+  zfpm_start_connect_timer ("getsockopt() after async connect failed");
+  return;
+}
+
+/*
+ * zfpm_conn_down_thread_cb
+ *
+ * Callback that is invoked to clean up state after the TCP connection
+ * to the FPM goes down.
+ */
+static int
+zfpm_conn_down_thread_cb (struct thread *thread)
+{
+  struct route_node *rnode;
+  zfpm_rnodes_iter_t *iter;
+  rib_dest_t *dest;
+
+  assert (zfpm_g->state == ZFPM_STATE_IDLE);
+
+  assert (zfpm_g->t_conn_down);
+  zfpm_g->t_conn_down = NULL;
+
+  iter = &zfpm_g->t_conn_down_state.iter;
+
+  while ((rnode = zfpm_rnodes_iter_next (iter)))
+    {
+      dest = rib_dest_from_rnode (rnode);
+
+      if (dest)
+       {
+         if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM))
+           {
+             TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
+           }
+
+         UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
+         UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
+
+         zfpm_g->stats.t_conn_down_dests_processed++;
+
+         /*
+          * Check if the dest should be deleted.
+          */
+         rib_gc_dest(rnode);
+       }
+
+      /*
+       * Yield if need be.
+       */
+      if (!zfpm_thread_should_yield (thread))
+       continue;
+
+      zfpm_g->stats.t_conn_down_yields++;
+      zfpm_rnodes_iter_pause (iter);
+      zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
+                                                  zfpm_conn_down_thread_cb,
+                                                  0, 0);
+      return 0;
+    }
+
+  zfpm_g->stats.t_conn_down_finishes++;
+  zfpm_rnodes_iter_cleanup (iter);
+
+  /*
+   * Start the process of connecting to the FPM again.
+   */
+  zfpm_start_connect_timer ("cleanup complete");
+  return 0;
+}
+
+/*
+ * zfpm_connection_down
+ *
+ * Called when the connection to the FPM has gone down.
+ */
+static void
+zfpm_connection_down (const char *detail)
+{
+  if (!detail)
+    detail = "unknown";
+
+  assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
+
+  zlog_info ("connection to the FPM has gone down: %s", detail);
+
+  zfpm_read_off ();
+  zfpm_write_off ();
+
+  stream_reset (zfpm_g->ibuf);
+  stream_reset (zfpm_g->obuf);
+
+  if (zfpm_g->sock >= 0) {
+    close (zfpm_g->sock);
+    zfpm_g->sock = -1;
+  }
+
+  /*
+   * Start thread to clean up state after the connection goes down.
+   */
+  assert (!zfpm_g->t_conn_down);
+  zfpm_debug ("Starting conn_down thread");
+  zfpm_rnodes_iter_init (&zfpm_g->t_conn_down_state.iter);
+  zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
+                                              zfpm_conn_down_thread_cb, 0, 0);
+  zfpm_g->stats.t_conn_down_starts++;
+
+  zfpm_set_state (ZFPM_STATE_IDLE, detail);
+}
+
+/*
+ * zfpm_read_cb
+ */
+static int
+zfpm_read_cb (struct thread *thread)
+{
+  size_t already;
+  struct stream *ibuf;
+  uint16_t msg_len;
+  fpm_msg_hdr_t *hdr;
+
+  zfpm_g->stats.read_cb_calls++;
+  assert (zfpm_g->t_read);
+  zfpm_g->t_read = NULL;
+
+  /*
+   * Check if async connect is now done.
+   */
+  if (zfpm_g->state == ZFPM_STATE_CONNECTING)
+    {
+      zfpm_connect_check();
+      return 0;
+    }
+
+  assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
+  assert (zfpm_g->sock >= 0);
+
+  ibuf = zfpm_g->ibuf;
+
+  already = stream_get_endp (ibuf);
+  if (already < FPM_MSG_HDR_LEN)
+    {
+      ssize_t nbyte;
+
+      nbyte = stream_read_try (ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already);
+      if (nbyte == 0 || nbyte == -1)
+       {
+         zfpm_connection_down ("closed socket in read");
+         return 0;
+       }
+
+      if (nbyte != (ssize_t) (FPM_MSG_HDR_LEN - already))
+       goto done;
+
+      already = FPM_MSG_HDR_LEN;
+    }
+
+  stream_set_getp (ibuf, 0);
+
+  hdr = (fpm_msg_hdr_t *) stream_pnt (ibuf);
+
+  if (!fpm_msg_hdr_ok (hdr))
+    {
+      zfpm_connection_down ("invalid message header");
+      return 0;
+    }
+
+  msg_len = fpm_msg_len (hdr);
+
+  /*
+   * Read out the rest of the packet.
+   */
+  if (already < msg_len)
+    {
+      ssize_t nbyte;
+
+      nbyte = stream_read_try (ibuf, zfpm_g->sock, msg_len - already);
+
+      if (nbyte == 0 || nbyte == -1)
+       {
+         zfpm_connection_down ("failed to read message");
+         return 0;
+       }
+
+      if (nbyte != (ssize_t) (msg_len - already))
+       goto done;
+    }
+
+  zfpm_debug ("Read out a full fpm message");
+
+  /*
+   * Just throw it away for now.
+   */
+  stream_reset (ibuf);
+
+ done:
+  zfpm_read_on ();
+  return 0;
+}
+
+/*
+ * zfpm_writes_pending
+ *
+ * Returns TRUE if we may have something to write to the FPM.
+ */
+static int
+zfpm_writes_pending (void)
+{
+
+  /*
+   * Check if there is any data in the outbound buffer that has not
+   * been written to the socket yet.
+   */
+  if (stream_get_endp (zfpm_g->obuf) - stream_get_getp (zfpm_g->obuf))
+    return 1;
+
+  /*
+   * Check if there are any prefixes on the outbound queue.
+   */
+  if (!TAILQ_EMPTY (&zfpm_g->dest_q))
+    return 1;
+
+  return 0;
+}
+
+/*
+ * zfpm_encode_route
+ *
+ * Encode a message to the FPM with information about the given route.
+ *
+ * Returns the number of bytes written to the buffer. 0 or a negative
+ * value indicates an error.
+ */
+static inline int
+zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf,
+                  size_t in_buf_len, fpm_msg_type_e *msg_type)
+{
+  size_t len;
+  int cmd;
+  len = 0;
+
+  *msg_type = FPM_MSG_TYPE_NONE;
+
+  switch (zfpm_g->message_format) {
+
+  case ZFPM_MSG_FORMAT_PROTOBUF:
+#ifdef HAVE_PROTOBUF
+    len = zfpm_protobuf_encode_route (dest, rib, (uint8_t *) in_buf,
+                                     in_buf_len);
+    *msg_type = FPM_MSG_TYPE_PROTOBUF;
+#endif
+    break;
+
+  case ZFPM_MSG_FORMAT_NETLINK:
+#ifdef HAVE_NETLINK
+    *msg_type = FPM_MSG_TYPE_NETLINK;
+    cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE;
+    len = zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len);
+    assert(fpm_msg_align(len) == len);
+    *msg_type = FPM_MSG_TYPE_NETLINK;
+#endif /* HAVE_NETLINK */
+    break;
+
+  default:
+    break;
+  }
+
+  return len;
+
+}
+
+/*
+ * zfpm_route_for_update
+ *
+ * Returns the rib that is to be sent to the FPM for a given dest.
+ */
+struct rib *
+zfpm_route_for_update (rib_dest_t *dest)
+{
+  struct rib *rib;
+
+  RIB_DEST_FOREACH_ROUTE (dest, rib)
+    {
+      if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+       continue;
+
+      return rib;
+    }
+
+  /*
+   * We have no route for this destination.
+   */
+  return NULL;
+}
+
+/*
+ * zfpm_build_updates
+ *
+ * Process the outgoing queue and write messages to the outbound
+ * buffer.
+ */
+static void
+zfpm_build_updates (void)
+{
+  struct stream *s;
+  rib_dest_t *dest;
+  unsigned char *buf, *data, *buf_end;
+  size_t msg_len;
+  size_t data_len;
+  fpm_msg_hdr_t *hdr;
+  struct rib *rib;
+  int is_add, write_msg;
+  fpm_msg_type_e msg_type;
+
+  s = zfpm_g->obuf;
+
+  assert (stream_empty (s));
+
+  do {
+
+    /*
+     * Make sure there is enough space to write another message.
+     */
+    if (STREAM_WRITEABLE (s) < FPM_MAX_MSG_LEN)
+      break;
+
+    buf = STREAM_DATA (s) + stream_get_endp (s);
+    buf_end = buf + STREAM_WRITEABLE (s);
+
+    dest = TAILQ_FIRST (&zfpm_g->dest_q);
+    if (!dest)
+      break;
+
+    assert (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM));
+
+    hdr = (fpm_msg_hdr_t *) buf;
+    hdr->version = FPM_PROTO_VERSION;
+
+    data = fpm_msg_data (hdr);
+
+    rib = zfpm_route_for_update (dest);
+    is_add = rib ? 1 : 0;
+
+    write_msg = 1;
+
+    /*
+     * If this is a route deletion, and we have not sent the route to
+     * the FPM previously, skip it.
+     */
+    if (!is_add && !CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
+      {
+       write_msg = 0;
+       zfpm_g->stats.nop_deletes_skipped++;
+      }
+
+    if (write_msg) {
+      data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data,
+                                   &msg_type);
+
+      assert (data_len);
+      if (data_len)
+       {
+         hdr->msg_type = msg_type;
+         msg_len = fpm_data_len_to_msg_len (data_len);
+         hdr->msg_len = htons (msg_len);
+         stream_forward_endp (s, msg_len);
+
+         if (is_add)
+           zfpm_g->stats.route_adds++;
+         else
+           zfpm_g->stats.route_dels++;
+       }
+    }
+
+    /*
+     * Remove the dest from the queue, and reset the flag.
+     */
+    UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
+    TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
+
+    if (is_add)
+      {
+       SET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
+      }
+    else
+      {
+       UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
+      }
+
+    /*
+     * Delete the destination if necessary.
+     */
+    if (rib_gc_dest (dest->rnode))
+      zfpm_g->stats.dests_del_after_update++;
+
+  } while (1);
+
+}
+
+/*
+ * zfpm_write_cb
+ */
+static int
+zfpm_write_cb (struct thread *thread)
+{
+  struct stream *s;
+  int num_writes;
+
+  zfpm_g->stats.write_cb_calls++;
+  assert (zfpm_g->t_write);
+  zfpm_g->t_write = NULL;
+
+  /*
+   * Check if async connect is now done.
+   */
+  if (zfpm_g->state == ZFPM_STATE_CONNECTING)
+    {
+      zfpm_connect_check ();
+      return 0;
+    }
+
+  assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
+  assert (zfpm_g->sock >= 0);
+
+  num_writes = 0;
+
+  do
+    {
+      int bytes_to_write, bytes_written;
+
+      s = zfpm_g->obuf;
+
+      /*
+       * If the stream is empty, try fill it up with data.
+       */
+      if (stream_empty (s))
+       {
+         zfpm_build_updates ();
+       }
+
+      bytes_to_write = stream_get_endp (s) - stream_get_getp (s);
+      if (!bytes_to_write)
+       break;
+
+      bytes_written = write (zfpm_g->sock, STREAM_PNT (s), bytes_to_write);
+      zfpm_g->stats.write_calls++;
+      num_writes++;
+
+      if (bytes_written < 0)
+       {
+         if (ERRNO_IO_RETRY (errno))
+           break;
+
+         zfpm_connection_down ("failed to write to socket");
+         return 0;
+       }
+
+      if (bytes_written != bytes_to_write)
+       {
+
+         /*
+          * Partial write.
+          */
+         stream_forward_getp (s, bytes_written);
+         zfpm_g->stats.partial_writes++;
+         break;
+       }
+
+      /*
+       * We've written out the entire contents of the stream.
+       */
+      stream_reset (s);
+
+      if (num_writes >= ZFPM_MAX_WRITES_PER_RUN)
+       {
+         zfpm_g->stats.max_writes_hit++;
+         break;
+       }
+
+      if (zfpm_thread_should_yield (thread))
+       {
+         zfpm_g->stats.t_write_yields++;
+         break;
+       }
+    } while (1);
+
+  if (zfpm_writes_pending ())
+      zfpm_write_on ();
+
+  return 0;
+}
+
+/*
+ * zfpm_connect_cb
+ */
+static int
+zfpm_connect_cb (struct thread *t)
+{
+  int sock, ret;
+  struct sockaddr_in serv;
+
+  assert (zfpm_g->t_connect);
+  zfpm_g->t_connect = NULL;
+  assert (zfpm_g->state == ZFPM_STATE_ACTIVE);
+
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      zfpm_debug ("Failed to create socket for connect(): %s", strerror(errno));
+      zfpm_g->stats.connect_no_sock++;
+      return 0;
+    }
+
+  set_nonblocking(sock);
+
+  /* Make server socket. */
+  memset (&serv, 0, sizeof (serv));
+  serv.sin_family = AF_INET;
+  serv.sin_port = htons (zfpm_g->fpm_port);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  serv.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  if (!zfpm_g->fpm_server)
+    serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+  else 
+    serv.sin_addr.s_addr = (zfpm_g->fpm_server);
+
+  /*
+   * Connect to the FPM.
+   */
+  zfpm_g->connect_calls++;
+  zfpm_g->stats.connect_calls++;
+  zfpm_g->last_connect_call_time = zfpm_get_time ();
+
+  ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv));
+  if (ret >= 0)
+    {
+      zfpm_g->sock = sock;
+      zfpm_connection_up ("connect succeeded");
+      return 1;
+    }
+
+  if (errno == EINPROGRESS)
+    {
+      zfpm_g->sock = sock;
+      zfpm_read_on ();
+      zfpm_write_on ();
+      zfpm_set_state (ZFPM_STATE_CONNECTING, "async connect in progress");
+      return 0;
+    }
+
+  zlog_info ("can't connect to FPM %d: %s", sock, safe_strerror (errno));
+  close (sock);
+
+  /*
+   * Restart timer for retrying connection.
+   */
+  zfpm_start_connect_timer ("connect() failed");
+  return 0;
+}
+
+/*
+ * zfpm_set_state
+ *
+ * Move state machine into the given state.
+ */
+static void
+zfpm_set_state (zfpm_state_t state, const char *reason)
+{
+  zfpm_state_t cur_state = zfpm_g->state;
+
+  if (!reason)
+    reason = "Unknown";
+
+  if (state == cur_state)
+    return;
+
+  zfpm_debug("beginning state transition %s -> %s. Reason: %s",
+            zfpm_state_to_str (cur_state), zfpm_state_to_str (state),
+            reason);
+
+  switch (state) {
+
+  case ZFPM_STATE_IDLE:
+    assert (cur_state == ZFPM_STATE_ESTABLISHED);
+    break;
+
+  case ZFPM_STATE_ACTIVE:
+     assert (cur_state == ZFPM_STATE_IDLE ||
+            cur_state == ZFPM_STATE_CONNECTING);
+    assert (zfpm_g->t_connect);
+    break;
+
+  case ZFPM_STATE_CONNECTING:
+    assert (zfpm_g->sock);
+    assert (cur_state == ZFPM_STATE_ACTIVE);
+    assert (zfpm_g->t_read);
+    assert (zfpm_g->t_write);
+    break;
+
+  case ZFPM_STATE_ESTABLISHED:
+    assert (cur_state == ZFPM_STATE_ACTIVE ||
+           cur_state == ZFPM_STATE_CONNECTING);
+    assert (zfpm_g->sock);
+    assert (zfpm_g->t_read);
+    assert (zfpm_g->t_write);
+    break;
+  }
+
+  zfpm_g->state = state;
+}
+
+/*
+ * zfpm_calc_connect_delay
+ *
+ * Returns the number of seconds after which we should attempt to
+ * reconnect to the FPM.
+ */
+static long
+zfpm_calc_connect_delay (void)
+{
+  time_t elapsed;
+
+  /*
+   * Return 0 if this is our first attempt to connect.
+   */
+  if (zfpm_g->connect_calls == 0)
+    {
+      return 0;
+    }
+
+  elapsed = zfpm_get_elapsed_time (zfpm_g->last_connect_call_time);
+
+  if (elapsed > ZFPM_CONNECT_RETRY_IVL) {
+    return 0;
+  }
+
+  return ZFPM_CONNECT_RETRY_IVL - elapsed;
+}
+
+/*
+ * zfpm_start_connect_timer
+ */
+static void
+zfpm_start_connect_timer (const char *reason)
+{
+  long delay_secs;
+
+  assert (!zfpm_g->t_connect);
+  assert (zfpm_g->sock < 0);
+
+  assert(zfpm_g->state == ZFPM_STATE_IDLE ||
+        zfpm_g->state == ZFPM_STATE_ACTIVE ||
+        zfpm_g->state == ZFPM_STATE_CONNECTING);
+
+  delay_secs = zfpm_calc_connect_delay();
+  zfpm_debug ("scheduling connect in %ld seconds", delay_secs);
+
+  THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_connect, zfpm_connect_cb, 0,
+                  delay_secs);
+  zfpm_set_state (ZFPM_STATE_ACTIVE, reason);
+}
+
+/*
+ * zfpm_is_enabled
+ *
+ * Returns TRUE if the zebra FPM module has been enabled.
+ */
+static inline int
+zfpm_is_enabled (void)
+{
+  return zfpm_g->enabled;
+}
+
+/*
+ * zfpm_conn_is_up
+ *
+ * Returns TRUE if the connection to the FPM is up.
+ */
+static inline int
+zfpm_conn_is_up (void)
+{
+  if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
+    return 0;
+
+  assert (zfpm_g->sock >= 0);
+
+  return 1;
+}
+
+/*
+ * zfpm_trigger_update
+ *
+ * The zebra code invokes this function to indicate that we should
+ * send an update to the FPM about the given route_node.
+ */
+void
+zfpm_trigger_update (struct route_node *rn, const char *reason)
+{
+  rib_dest_t *dest;
+  char buf[PREFIX_STRLEN];
+
+  /*
+   * Ignore if the connection is down. We will update the FPM about
+   * all destinations once the connection comes up.
+   */
+  if (!zfpm_conn_is_up ())
+    return;
+
+  dest = rib_dest_from_rnode (rn);
+
+  /*
+   * Ignore the trigger if the dest is not in a table that we would
+   * send to the FPM.
+   */
+  if (!zfpm_is_table_for_fpm (rib_dest_table (dest)))
+    {
+      zfpm_g->stats.non_fpm_table_triggers++;
+      return;
+    }
+
+  if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) {
+    zfpm_g->stats.redundant_triggers++;
+    return;
+  }
+
+  if (reason)
+    {
+      zfpm_debug ("%s triggering update to FPM - Reason: %s",
+                 prefix2str (&rn->p, buf, sizeof(buf)), reason);
+    }
+
+  SET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
+  TAILQ_INSERT_TAIL (&zfpm_g->dest_q, dest, fpm_q_entries);
+  zfpm_g->stats.updates_triggered++;
+
+  /*
+   * Make sure that writes are enabled.
+   */
+  if (zfpm_g->t_write)
+    return;
+
+  zfpm_write_on ();
+}
+
+/*
+ * zfpm_stats_timer_cb
+ */
+static int
+zfpm_stats_timer_cb (struct thread *t)
+{
+  assert (zfpm_g->t_stats);
+  zfpm_g->t_stats = NULL;
+
+  /*
+   * Remember the stats collected in the last interval for display
+   * purposes.
+   */
+  zfpm_stats_copy (&zfpm_g->stats, &zfpm_g->last_ivl_stats);
+
+  /*
+   * Add the current set of stats into the cumulative statistics.
+   */
+  zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
+                     &zfpm_g->cumulative_stats);
+
+  /*
+   * Start collecting stats afresh over the next interval.
+   */
+  zfpm_stats_reset (&zfpm_g->stats);
+
+  zfpm_start_stats_timer ();
+
+  return 0;
+}
+
+/*
+ * zfpm_stop_stats_timer
+ */
+static void
+zfpm_stop_stats_timer (void)
+{
+  if (!zfpm_g->t_stats)
+    return;
+
+  zfpm_debug ("Stopping existing stats timer");
+  THREAD_TIMER_OFF (zfpm_g->t_stats);
+}
+
+/*
+ * zfpm_start_stats_timer
+ */
+void
+zfpm_start_stats_timer (void)
+{
+  assert (!zfpm_g->t_stats);
+
+  THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_stats, zfpm_stats_timer_cb, 0,
+                  ZFPM_STATS_IVL_SECS);
+}
+
+/*
+ * Helper macro for zfpm_show_stats() below.
+ */
+#define ZFPM_SHOW_STAT(counter)                                                \
+  do {                                                                 \
+    vty_out (vty, "%-40s %10lu %16lu%s", #counter, total_stats.counter,        \
+            zfpm_g->last_ivl_stats.counter, VTY_NEWLINE);              \
+  } while (0)
+
+/*
+ * zfpm_show_stats
+ */
+static void
+zfpm_show_stats (struct vty *vty)
+{
+  zfpm_stats_t total_stats;
+  time_t elapsed;
+
+  vty_out (vty, "%s%-40s %10s     Last %2d secs%s%s", VTY_NEWLINE, "Counter",
+          "Total", ZFPM_STATS_IVL_SECS, VTY_NEWLINE, VTY_NEWLINE);
+
+  /*
+   * Compute the total stats up to this instant.
+   */
+  zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
+                     &total_stats);
+
+  ZFPM_SHOW_STAT (connect_calls);
+  ZFPM_SHOW_STAT (connect_no_sock);
+  ZFPM_SHOW_STAT (read_cb_calls);
+  ZFPM_SHOW_STAT (write_cb_calls);
+  ZFPM_SHOW_STAT (write_calls);
+  ZFPM_SHOW_STAT (partial_writes);
+  ZFPM_SHOW_STAT (max_writes_hit);
+  ZFPM_SHOW_STAT (t_write_yields);
+  ZFPM_SHOW_STAT (nop_deletes_skipped);
+  ZFPM_SHOW_STAT (route_adds);
+  ZFPM_SHOW_STAT (route_dels);
+  ZFPM_SHOW_STAT (updates_triggered);
+  ZFPM_SHOW_STAT (non_fpm_table_triggers);
+  ZFPM_SHOW_STAT (redundant_triggers);
+  ZFPM_SHOW_STAT (dests_del_after_update);
+  ZFPM_SHOW_STAT (t_conn_down_starts);
+  ZFPM_SHOW_STAT (t_conn_down_dests_processed);
+  ZFPM_SHOW_STAT (t_conn_down_yields);
+  ZFPM_SHOW_STAT (t_conn_down_finishes);
+  ZFPM_SHOW_STAT (t_conn_up_starts);
+  ZFPM_SHOW_STAT (t_conn_up_dests_processed);
+  ZFPM_SHOW_STAT (t_conn_up_yields);
+  ZFPM_SHOW_STAT (t_conn_up_aborts);
+  ZFPM_SHOW_STAT (t_conn_up_finishes);
+
+  if (!zfpm_g->last_stats_clear_time)
+    return;
+
+  elapsed = zfpm_get_elapsed_time (zfpm_g->last_stats_clear_time);
+
+  vty_out (vty, "%sStats were cleared %lu seconds ago%s", VTY_NEWLINE,
+          (unsigned long) elapsed, VTY_NEWLINE);
+}
+
+/*
+ * zfpm_clear_stats
+ */
+static void
+zfpm_clear_stats (struct vty *vty)
+{
+  if (!zfpm_is_enabled ())
+    {
+      vty_out (vty, "The FPM module is not enabled...%s", VTY_NEWLINE);
+      return;
+    }
+
+  zfpm_stats_reset (&zfpm_g->stats);
+  zfpm_stats_reset (&zfpm_g->last_ivl_stats);
+  zfpm_stats_reset (&zfpm_g->cumulative_stats);
+
+  zfpm_stop_stats_timer ();
+  zfpm_start_stats_timer ();
+
+  zfpm_g->last_stats_clear_time = zfpm_get_time();
+
+  vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE);
+}
+
+/*
+ * show_zebra_fpm_stats
+ */
+DEFUN (show_zebra_fpm_stats,
+       show_zebra_fpm_stats_cmd,
+       "show zebra fpm stats",
+       SHOW_STR
+       "Zebra information\n"
+       "Forwarding Path Manager information\n"
+       "Statistics\n")
+{
+  zfpm_show_stats (vty);
+  return CMD_SUCCESS;
+}
+
+/*
+ * clear_zebra_fpm_stats
+ */
+DEFUN (clear_zebra_fpm_stats,
+       clear_zebra_fpm_stats_cmd,
+       "clear zebra fpm stats",
+       CLEAR_STR
+       "Zebra information\n"
+       "Clear Forwarding Path Manager information\n"
+       "Statistics\n")
+{
+  zfpm_clear_stats (vty);
+  return CMD_SUCCESS;
+}
+
+/*
+ * update fpm connection information 
+ */
+DEFUN ( fpm_remote_ip, 
+        fpm_remote_ip_cmd,
+        "fpm connection ip A.B.C.D port <1-65535>",
+        "fpm connection remote ip and port\n"
+        "Remote fpm server ip A.B.C.D\n"
+        "Enter ip ")
+{
+
+   in_addr_t fpm_server;
+   uint32_t port_no;
+
+   fpm_server = inet_addr (argv[0]);
+   if (fpm_server == INADDR_NONE)
+     return CMD_ERR_INCOMPLETE;
+
+   port_no = atoi (argv[1]);
+   if (port_no < TCP_MIN_PORT || port_no > TCP_MAX_PORT)
+     return CMD_ERR_INCOMPLETE;
+
+   zfpm_g->fpm_server = fpm_server;
+   zfpm_g->fpm_port = port_no;
+
+
+   return CMD_SUCCESS;
+}
+
+DEFUN ( no_fpm_remote_ip, 
+        no_fpm_remote_ip_cmd,
+        "no fpm connection ip A.B.C.D port <1-65535>",
+        "fpm connection remote ip and port\n"
+        "Connection\n"
+        "Remote fpm server ip A.B.C.D\n"
+        "Enter ip ")
+{
+   if (zfpm_g->fpm_server != inet_addr (argv[0]) || 
+              zfpm_g->fpm_port !=  atoi (argv[1]))
+       return CMD_ERR_NO_MATCH;
+
+   zfpm_g->fpm_server = FPM_DEFAULT_IP;
+   zfpm_g->fpm_port = FPM_DEFAULT_PORT;
+
+   return CMD_SUCCESS;
+}
+
+
+/*
+ * zfpm_init_message_format
+ */
+static inline void
+zfpm_init_message_format (const char *format)
+{
+  int have_netlink, have_protobuf;
+
+  have_netlink = have_protobuf = 0;
+
+#ifdef HAVE_NETLINK
+  have_netlink = 1;
+#endif
+
+#ifdef HAVE_PROTOBUF
+  have_protobuf = 1;
+#endif
+
+  zfpm_g->message_format = ZFPM_MSG_FORMAT_NONE;
+
+  if (!format)
+    {
+      if (have_netlink)
+       {
+         zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK;
+       }
+      else if (have_protobuf)
+       {
+         zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;
+       }
+      return;
+    }
+
+  if (!strcmp ("netlink", format))
+    {
+      if (!have_netlink)
+       {
+         zlog_err ("FPM netlink message format is not available");
+         return;
+       }
+      zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK;
+      return;
+    }
+
+  if (!strcmp ("protobuf", format))
+    {
+      if (!have_protobuf)
+       {
+         zlog_err ("FPM protobuf message format is not available");
+         return;
+       }
+      zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;
+      return;
+    }
+
+  zlog_warn ("Unknown fpm format '%s'", format);
+}
+
+/**
+ * fpm_remote_srv_write 
+ *
+ * Module to write remote fpm connection 
+ *
+ * Returns ZERO on success.
+ */
+
+int fpm_remote_srv_write (struct vty *vty )
+{
+   struct in_addr in;
+
+   in.s_addr = zfpm_g->fpm_server;
+
+   if (zfpm_g->fpm_server != FPM_DEFAULT_IP || 
+          zfpm_g->fpm_port != FPM_DEFAULT_PORT)
+      vty_out (vty,"fpm connection ip %s port %d%s", inet_ntoa (in),zfpm_g->fpm_port,VTY_NEWLINE);
+
+   return 0;
+}
+
+
+/**
+ * zfpm_init
+ *
+ * One-time initialization of the Zebra FPM module.
+ *
+ * @param[in] port port at which FPM is running.
+ * @param[in] enable TRUE if the zebra FPM module should be enabled
+ * @param[in] format to use to talk to the FPM. Can be 'netink' or 'protobuf'.
+ *
+ * Returns TRUE on success.
+ */
+int
+zfpm_init (struct thread_master *master, int enable, uint16_t port,
+          const char *format)
+{
+  static int initialized = 0;
+
+  if (initialized) {
+    return 1;
+  }
+
+  initialized = 1;
+
+  memset (zfpm_g, 0, sizeof (*zfpm_g));
+  zfpm_g->master = master;
+  TAILQ_INIT(&zfpm_g->dest_q);
+  zfpm_g->sock = -1;
+  zfpm_g->state = ZFPM_STATE_IDLE;
+
+  zfpm_stats_init (&zfpm_g->stats);
+  zfpm_stats_init (&zfpm_g->last_ivl_stats);
+  zfpm_stats_init (&zfpm_g->cumulative_stats);
+
+  install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd);
+  install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd);
+  install_element (CONFIG_NODE, &fpm_remote_ip_cmd);
+  install_element (CONFIG_NODE, &no_fpm_remote_ip_cmd);
+
+  zfpm_init_message_format(format);
+
+  /*
+   * Disable FPM interface if no suitable format is available.
+   */
+  if (zfpm_g->message_format == ZFPM_MSG_FORMAT_NONE)
+      enable = 0;
+
+  zfpm_g->enabled = enable;
+
+  if (!enable) {
+    return 1;
+  }
+
+  if (!zfpm_g->fpm_server)
+     zfpm_g->fpm_server = FPM_DEFAULT_IP;
+
+  if (!port)
+    port = FPM_DEFAULT_PORT;
+
+  zfpm_g->fpm_port = port;
+
+  zfpm_g->obuf = stream_new (ZFPM_OBUF_SIZE);
+  zfpm_g->ibuf = stream_new (ZFPM_IBUF_SIZE);
+
+  zfpm_start_stats_timer ();
+  zfpm_start_connect_timer ("initialized");
+
+  return 1;
+}
diff --git a/zebra/zebra_fpm.h b/zebra/zebra_fpm.h
new file mode 100644 (file)
index 0000000..ecd23c7
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Header file exported by the zebra FPM module to zebra.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_FPM_H
+#define _ZEBRA_FPM_H
+
+/*
+ * Externs.
+ */
+extern int zfpm_init (struct thread_master *master, int enable, uint16_t port,
+                     const char *message_format);
+extern void zfpm_trigger_update (struct route_node *rn, const char *reason);
+
+#endif /* _ZEBRA_FPM_H */
diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c
new file mode 100644 (file)
index 0000000..bd171c8
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * zebra_fpm_dt.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Developer tests for the zebra code that interfaces with the
+ * forwarding plane manager.
+ *
+ * The functions here are built into developer builds of zebra (when
+ * DEV_BUILD is defined), and can be called via the 'invoke' cli
+ * command.
+ *
+ * For example:
+ *
+ * # invoke zebra function zfpm_dt_benchmark_protobuf_encode 100000
+ *
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "vrf.h"
+
+#include "zebra/rib.h"
+
+#include "zebra_fpm_private.h"
+
+#include "qpb/qpb_allocator.h"
+#include "qpb/linear_allocator.h"
+
+#include "qpb/qpb.h"
+#include "fpm/fpm.pb-c.h"
+
+/*
+ * Externs.
+ */
+extern int zfpm_dt_benchmark_netlink_encode (int argc, const char **argv);
+extern int zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv);
+extern int zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv);
+
+/*
+ * zfpm_dt_find_route
+ *
+ * Selects a suitable rib destination for fpm interface tests.
+ */
+static int
+zfpm_dt_find_route (rib_dest_t **dest_p, struct rib **rib_p)
+{
+  struct route_node *rnode;
+  route_table_iter_t iter;
+  struct route_table *table;
+  rib_dest_t *dest;
+  struct rib *rib;
+  int ret;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (!table)
+    return 0;
+
+  route_table_iter_init(&iter, table);
+  while ((rnode = route_table_iter_next(&iter)))
+    {
+      dest = rib_dest_from_rnode (rnode);
+
+      if (!dest)
+         continue;
+
+      rib = zfpm_route_for_update(dest);
+      if (!rib)
+       continue;
+
+      if (rib->nexthop_active_num <= 0)
+       continue;
+
+      *dest_p = dest;
+      *rib_p = rib;
+      ret = 1;
+      goto done;
+    }
+
+    ret = 0;
+
+  done:
+    route_table_iter_cleanup(&iter);
+    return ret;
+}
+#ifdef HAVE_NETLINK
+
+/*
+ * zfpm_dt_benchmark_netlink_encode
+ */
+int
+zfpm_dt_benchmark_netlink_encode (int argc, const char **argv)
+{
+  int times, i, len;
+  rib_dest_t *dest;
+  struct rib *rib;
+  char buf[4096];
+
+  times = 100000;
+  if (argc > 0) {
+    times = atoi(argv[0]);
+  }
+
+  if (!zfpm_dt_find_route(&dest, &rib)) {
+    return 1;
+  }
+
+  for (i = 0; i < times; i++) {
+    len = zfpm_netlink_encode_route(RTM_NEWROUTE, dest, rib, buf, sizeof(buf));
+    if (len <= 0) {
+      return 2;
+    }
+  }
+  return 0;
+}
+
+#endif /* HAVE_NETLINK */
+
+#ifdef HAVE_PROTOBUF
+
+/*
+ * zfpm_dt_benchmark_protobuf_encode
+ */
+int
+zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv)
+{
+  int times, i, len;
+  rib_dest_t *dest;
+  struct rib *rib;
+  uint8_t buf[4096];
+
+  times = 100000;
+  if (argc > 0) {
+    times = atoi(argv[0]);
+  }
+
+  if (!zfpm_dt_find_route(&dest, &rib)) {
+    return 1;
+  }
+
+  for (i = 0; i < times; i++) {
+    len = zfpm_protobuf_encode_route(dest, rib, buf, sizeof(buf));
+    if (len <= 0) {
+      return 2;
+    }
+  }
+  return 0;
+}
+
+/*
+ * zfpm_dt_log_fpm_message
+ */
+static void
+zfpm_dt_log_fpm_message (Fpm__Message *msg)
+{
+  Fpm__AddRoute *add_route;
+  Fpm__Nexthop *nexthop;
+  struct prefix prefix;
+  u_char family, nh_family;
+  uint if_index;
+  char *if_name;
+  size_t i;
+  char buf[INET6_ADDRSTRLEN];
+  union g_addr nh_addr;
+
+  if (msg->type != FPM__MESSAGE__TYPE__ADD_ROUTE)
+    return;
+
+  zfpm_debug ("Add route message");
+  add_route = msg->add_route;
+
+  if (!qpb_address_family_get (add_route->address_family, &family))
+    return;
+
+  if (!qpb_l3_prefix_get (add_route->key->prefix, family, &prefix))
+    return;
+
+  zfpm_debug ("Vrf id: %d, Prefix: %s/%d, Metric: %d", add_route->vrf_id,
+             inet_ntop (family, &prefix.u.prefix, buf, sizeof (buf)),
+             prefix.prefixlen, add_route->metric);
+
+  /*
+   * Go over nexthops.
+   */
+  for (i = 0; i < add_route->n_nexthops; i++)
+    {
+      nexthop = add_route->nexthops[i];
+      if (!qpb_if_identifier_get (nexthop->if_id, &if_index, &if_name))
+       continue;
+
+      if (nexthop->address)
+       qpb_l3_address_get (nexthop->address, &nh_family, &nh_addr);
+
+      zfpm_debug ("Nexthop - if_index: %d (%s), gateway: %s, ", if_index,
+                 if_name ? if_name : "name not specified",
+                 nexthop->address ? inet_ntoa (nh_addr.ipv4) : "None");
+    }
+}
+
+/*
+ * zfpm_dt_benchmark_protobuf_decode
+ */
+int
+zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv)
+{
+  int times, i, len;
+  rib_dest_t *dest;
+  struct rib *rib;
+  uint8_t msg_buf[4096];
+  QPB_DECLARE_STACK_ALLOCATOR (allocator, 8192);
+  Fpm__Message *fpm_msg;
+
+  QPB_INIT_STACK_ALLOCATOR (allocator);
+
+  times = 100000;
+  if (argc > 0)
+    times = atoi(argv[0]);
+
+  if (!zfpm_dt_find_route (&dest, &rib))
+    return 1;
+
+  /*
+   * Encode the route into the message buffer once only.
+   */
+  len = zfpm_protobuf_encode_route (dest, rib, msg_buf, sizeof (msg_buf));
+  if (len <= 0)
+    return 2;
+
+  // Decode once, and display the decoded message
+  fpm_msg = fpm__message__unpack(&allocator, len, msg_buf);
+
+  if (fpm_msg)
+    {
+      zfpm_dt_log_fpm_message(fpm_msg);
+      QPB_RESET_STACK_ALLOCATOR (allocator);
+    }
+
+  /*
+   * Decode encoded message the specified number of times.
+   */
+  for (i = 0; i < times; i++)
+    {
+      fpm_msg = fpm__message__unpack (&allocator, len, msg_buf);
+
+      if (!fpm_msg)
+       return 3;
+
+      // fpm__message__free_unpacked(msg, NULL);
+      QPB_RESET_STACK_ALLOCATOR (allocator);
+  }
+  return 0;
+}
+
+#endif /* HAVE_PROTOBUF */
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
new file mode 100644 (file)
index 0000000..175d351
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Code for encoding/decoding FPM messages that are in netlink format.
+ *
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "rib.h"
+
+#include "rt_netlink.h"
+#include "nexthop.h"
+
+#include "zebra_fpm_private.h"
+
+/*
+ * addr_to_a
+ *
+ * Returns string representation of an address of the given AF.
+ */
+static inline const char *
+addr_to_a (u_char af, void *addr)
+{
+  if (!addr)
+    return "<No address>";
+
+  switch (af)
+    {
+
+    case AF_INET:
+      return inet_ntoa (*((struct in_addr *) addr));
+
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return inet6_ntoa (*((struct in6_addr *) addr));
+#endif
+
+    default:
+      return "<Addr in unknown AF>";
+    }
+}
+
+/*
+ * prefix_addr_to_a
+ *
+ * Convience wrapper that returns a human-readable string for the
+ * address in a prefix.
+ */
+static const char *
+prefix_addr_to_a (struct prefix *prefix)
+{
+  if (!prefix)
+    return "<No address>";
+
+  return addr_to_a (prefix->family, &prefix->u.prefix);
+}
+
+/*
+ * af_addr_size
+ *
+ * The size of an address in a given address family.
+ */
+static size_t
+af_addr_size (u_char af)
+{
+  switch (af)
+    {
+
+    case AF_INET:
+      return 4;
+
+#ifdef HAVE_IPV6
+    case AF_INET6:
+      return 16;
+#endif
+
+    default:
+      assert(0);
+      return 16;
+    }
+}
+
+/*
+ * netlink_nh_info_t
+ *
+ * Holds information about a single nexthop for netlink. These info
+ * structures are transient and may contain pointers into rib
+ * data structures for convenience.
+ */
+typedef struct netlink_nh_info_t_
+{
+  uint32_t if_index;
+  union g_addr *gateway;
+
+  /*
+   * Information from the struct nexthop from which this nh was
+   * derived. For debug purposes only.
+   */
+  int recursive;
+  enum nexthop_types_t type;
+} netlink_nh_info_t;
+
+/*
+ * netlink_route_info_t
+ *
+ * A structure for holding information for a netlink route message.
+ */
+typedef struct netlink_route_info_t_
+{
+  uint16_t nlmsg_type;
+  u_char rtm_type;
+  uint32_t rtm_table;
+  u_char rtm_protocol;
+  u_char af;
+  struct prefix *prefix;
+  uint32_t *metric;
+  int num_nhs;
+
+  /*
+   * Nexthop structures
+   */
+  netlink_nh_info_t nhs[MULTIPATH_NUM];
+  union g_addr *pref_src;
+} netlink_route_info_t;
+
+/*
+ * netlink_route_info_add_nh
+ *
+ * Add information about the given nexthop to the given route info
+ * structure.
+ *
+ * Returns TRUE if a nexthop was added, FALSE otherwise.
+ */
+static int
+netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop,
+                          int recursive)
+{
+  netlink_nh_info_t nhi;
+  union g_addr *src;
+
+  memset (&nhi, 0, sizeof (nhi));
+  src = NULL;
+
+  if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs))
+    return 0;
+
+  nhi.recursive = recursive;
+  nhi.type = nexthop->type;
+  nhi.if_index = nexthop->ifindex;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      nhi.gateway = &nexthop->gate;
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+#ifdef HAVE_IPV6
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      nhi.gateway = &nexthop->gate;
+    }
+#endif /* HAVE_IPV6 */
+
+  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME)
+    {
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+  if (!nhi.gateway && nhi.if_index == 0)
+    return 0;
+
+  /*
+   * We have a valid nhi. Copy the structure over to the route_info.
+   */
+  ri->nhs[ri->num_nhs] = nhi;
+  ri->num_nhs++;
+
+  if (src && !ri->pref_src)
+    ri->pref_src = src;
+
+  return 1;
+}
+
+/*
+ * netlink_proto_from_route_type
+ */
+static u_char
+netlink_proto_from_route_type (int type)
+{
+  switch (type)
+    {
+    case ZEBRA_ROUTE_KERNEL:
+    case ZEBRA_ROUTE_CONNECT:
+      return RTPROT_KERNEL;
+
+    default:
+      return RTPROT_ZEBRA;
+    }
+}
+
+/*
+ * netlink_route_info_fill
+ *
+ * Fill out the route information object from the given route.
+ *
+ * Returns TRUE on success and FALSE on failure.
+ */
+static int
+netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
+                        rib_dest_t *dest, struct rib *rib)
+{
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int discard;
+
+  memset (ri, 0, sizeof (*ri));
+
+  ri->prefix = rib_dest_prefix (dest);
+  ri->af = rib_dest_af (dest);
+
+  ri->nlmsg_type = cmd;
+  ri->rtm_table = rib_dest_vrf (dest)->vrf_id;
+  ri->rtm_protocol = RTPROT_UNSPEC;
+
+  /*
+   * An RTM_DELROUTE need not be accompanied by any nexthops,
+   * particularly in our communication with the FPM.
+   */
+  if (cmd == RTM_DELROUTE)
+    goto skip;
+
+  if (!rib)
+    {
+      zlog_err("netlink_route_info_fill RTM_ADDROUTE called without rib info");
+      return 0;
+    }
+
+  ri->rtm_protocol = netlink_proto_from_route_type (rib->type);
+
+  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
+    discard = 1;
+  else
+    discard = 0;
+
+  if (cmd == RTM_NEWROUTE)
+    {
+      if (discard)
+        {
+          if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
+            ri->rtm_type = RTN_BLACKHOLE;
+          else if (rib->flags & ZEBRA_FLAG_REJECT)
+            ri->rtm_type = RTN_UNREACHABLE;
+          else
+            assert (0);
+        }
+      else
+        ri->rtm_type = RTN_UNICAST;
+    }
+
+  ri->metric = &rib->metric;
+
+  if (discard)
+    {
+      goto skip;
+    }
+
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (ri->num_nhs >= MULTIPATH_NUM)
+        break;
+
+      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
+      if ((cmd == RTM_NEWROUTE
+           && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+          || (cmd == RTM_DELROUTE
+              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
+        {
+          netlink_route_info_add_nh (ri, nexthop, recursing);
+        }
+    }
+
+  /* If there is no useful nexthop then return. */
+  if (ri->num_nhs == 0)
+    {
+      zfpm_debug ("netlink_encode_route(): No useful nexthop.");
+      return 0;
+    }
+
+ skip:
+  return 1;
+}
+
+/*
+ * netlink_route_info_encode
+ *
+ * Returns the number of bytes written to the buffer. 0 or a negative
+ * value indicates an error.
+ */
+static int
+netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf,
+                          size_t in_buf_len)
+{
+  size_t bytelen;
+  int nexthop_num = 0;
+  size_t buf_offset;
+  netlink_nh_info_t *nhi;
+
+  struct
+  {
+    struct nlmsghdr n;
+    struct rtmsg r;
+    char buf[1];
+  } *req;
+
+  req = (void *) in_buf;
+
+  buf_offset = ((char *) req->buf) - ((char *) req);
+
+  if (in_buf_len < buf_offset) {
+    assert(0);
+    return 0;
+  }
+
+  memset (req, 0, buf_offset);
+
+  bytelen = af_addr_size (ri->af);
+
+  req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
+  req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+  req->n.nlmsg_type = ri->nlmsg_type;
+  req->r.rtm_family = ri->af;
+  req->r.rtm_table = ri->rtm_table;
+  req->r.rtm_dst_len = ri->prefix->prefixlen;
+  req->r.rtm_protocol = ri->rtm_protocol;
+  req->r.rtm_scope = RT_SCOPE_UNIVERSE;
+
+  addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen);
+
+  req->r.rtm_type = ri->rtm_type;
+
+  /* Metric. */
+  if (ri->metric)
+    addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric);
+
+  if (ri->num_nhs == 0)
+    goto done;
+
+  if (ri->num_nhs == 1)
+    {
+      nhi = &ri->nhs[0];
+
+      if (nhi->gateway)
+       {
+         addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway,
+                    bytelen);
+       }
+
+      if (nhi->if_index)
+       {
+         addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index);
+       }
+
+      goto done;
+
+    }
+
+  /*
+   * Multipath case.
+   */
+  char buf[NL_PKT_BUF_SIZE];
+  struct rtattr *rta = (void *) buf;
+  struct rtnexthop *rtnh;
+
+  rta->rta_type = RTA_MULTIPATH;
+  rta->rta_len = RTA_LENGTH (0);
+  rtnh = RTA_DATA (rta);
+
+  for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++)
+    {
+      nhi = &ri->nhs[nexthop_num];
+
+      rtnh->rtnh_len = sizeof (*rtnh);
+      rtnh->rtnh_flags = 0;
+      rtnh->rtnh_hops = 0;
+      rtnh->rtnh_ifindex = 0;
+      rta->rta_len += rtnh->rtnh_len;
+
+      if (nhi->gateway)
+       {
+         rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen);
+         rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
+       }
+
+      if (nhi->if_index)
+       {
+         rtnh->rtnh_ifindex = nhi->if_index;
+       }
+
+      rtnh = RTNH_NEXT (rtnh);
+    }
+
+  assert (rta->rta_len > RTA_LENGTH (0));
+  addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta),
+            RTA_PAYLOAD (rta));
+
+done:
+
+  if (ri->pref_src)
+    {
+      addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen);
+    }
+
+  assert (req->n.nlmsg_len < in_buf_len);
+  return req->n.nlmsg_len;
+}
+
+/*
+ * zfpm_log_route_info
+ *
+ * Helper function to log the information in a route_info structure.
+ */
+static void
+zfpm_log_route_info (netlink_route_info_t *ri, const char *label)
+{
+  netlink_nh_info_t *nhi;
+  int i;
+
+  zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label,
+             nl_msg_type_to_str (ri->nlmsg_type),
+             prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen,
+             nl_rtproto_to_str (ri->rtm_protocol),
+             ri->metric ? *ri->metric : 0);
+
+  for (i = 0; i < ri->num_nhs; i++)
+    {
+      nhi = &ri->nhs[i];
+      zfpm_debug("  Intf: %u, Gateway: %s, Recursive: %s, Type: %s",
+                nhi->if_index, addr_to_a (ri->af, nhi->gateway),
+                nhi->recursive ? "yes" : "no",
+                nexthop_type_to_str (nhi->type));
+    }
+}
+
+/*
+ * zfpm_netlink_encode_route
+ *
+ * Create a netlink message corresponding to the given route in the
+ * given buffer space.
+ *
+ * Returns the number of bytes written to the buffer. 0 or a negative
+ * value indicates an error.
+ */
+int
+zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib,
+                          char *in_buf, size_t in_buf_len)
+{
+  netlink_route_info_t ri_space, *ri;
+
+  ri = &ri_space;
+
+  if (!netlink_route_info_fill (ri, cmd, dest, rib))
+    return 0;
+
+  zfpm_log_route_info (ri, __FUNCTION__);
+
+  return netlink_route_info_encode (ri, in_buf, in_buf_len);
+}
diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h
new file mode 100644 (file)
index 0000000..1c4fd4c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Private header file for the zebra FPM module.
+ *
+ * Copyright (C) 2012 by Open Source Routing.
+ * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_FPM_PRIVATE_H
+#define _ZEBRA_FPM_PRIVATE_H
+
+#include "zebra/debug.h"
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+
+#define zfpm_debug(...)                                                \
+  do {                                                         \
+    if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " __VA_ARGS__);   \
+  } while(0)
+
+#elif defined __GNUC__
+
+#define zfpm_debug(_args...)                           \
+  do {                                                 \
+    if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " _args); \
+  } while(0)
+
+#else
+static inline void zfpm_debug(const char *format, ...) { return; }
+#endif
+
+
+/*
+ * Externs
+ */
+extern int
+zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib,
+                          char *in_buf, size_t in_buf_len);
+
+extern int
+zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib,
+                           uint8_t *in_buf, size_t in_buf_len);
+
+extern struct rib *zfpm_route_for_update (rib_dest_t *dest);
+#endif /* _ZEBRA_FPM_PRIVATE_H */
diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c
new file mode 100644 (file)
index 0000000..beef310
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * zebra_fpm_protobuf.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "log.h"
+#include "rib.h"
+
+#include "qpb/qpb.pb-c.h"
+#include "qpb/qpb.h"
+#include "qpb/qpb_allocator.h"
+#include "qpb/linear_allocator.h"
+#include "fpm/fpm_pb.h"
+
+#include "zebra_fpm_private.h"
+
+/*
+ * create_delete_route_message
+ */
+static Fpm__DeleteRoute *
+create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                            struct rib *rib)
+{
+  Fpm__DeleteRoute *msg;
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__delete_route__init(msg);
+  msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
+
+  qpb_address_family_set(&msg->address_family, rib_dest_af(dest));
+
+  /*
+   * XXX Hardcode subaddress family for now.
+   */
+  msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
+  msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
+  if (!msg->key) {
+    assert(0);
+    return NULL;
+  }
+
+  return msg;
+}
+
+/*
+ * add_nexthop
+ */
+static inline int
+add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest,
+            struct nexthop *nexthop)
+{
+  uint32_t if_index;
+  union g_addr *gateway, *src;
+
+  gateway = src = NULL;
+
+  if_index = nexthop->ifindex;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      gateway = &nexthop->gate;
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      gateway = &nexthop->gate;
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME)
+    {
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+  if (!gateway && if_index == 0)
+    return 0;
+
+  /*
+   * We have a valid nexthop.
+   */
+  {
+    Fpm__Nexthop *pb_nh;
+    pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh));
+    if (!pb_nh) {
+      assert(0);
+      return 0;
+    }
+
+    fpm__nexthop__init(pb_nh);
+
+    if (if_index != 0) {
+      pb_nh->if_id = qpb_if_identifier_create (allocator, if_index);
+    }
+
+    if (gateway) {
+      pb_nh->address = qpb_l3_address_create (allocator, gateway,
+                                             rib_dest_af(dest));
+    }
+
+    msg->nexthops[msg->n_nexthops++] = pb_nh;
+  }
+
+  // TODO: Use src.
+
+  return 1;
+}
+
+/*
+ * create_add_route_message
+ */
+static Fpm__AddRoute *
+create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                         struct rib *rib)
+{
+  Fpm__AddRoute *msg;
+  int discard;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  uint num_nhs, u;
+  struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)];
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__add_route__init(msg);
+
+  msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
+
+  qpb_address_family_set (&msg->address_family, rib_dest_af(dest));
+
+  /*
+   * XXX Hardcode subaddress family for now.
+   */
+  msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
+  msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
+  qpb_protocol_set (&msg->protocol, rib->type);
+
+  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
+    discard = 1;
+  else
+    discard = 0;
+
+  if (discard)
+    {
+      if (rib->flags & ZEBRA_FLAG_BLACKHOLE) {
+       msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE;
+      } else if (rib->flags & ZEBRA_FLAG_REJECT) {
+       msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE;
+      } else {
+       assert (0);
+      }
+      return msg;
+    }
+  else {
+    msg->route_type = FPM__ROUTE_TYPE__NORMAL;
+  }
+
+  msg->metric = rib->metric;
+
+  /*
+   * Figure out the set of nexthops to be added to the message.
+   */
+  num_nhs = 0;
+  for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM)
+        break;
+
+      if (num_nhs >= ZEBRA_NUM_OF(nexthops))
+       break;
+
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
+      if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+       continue;
+
+      nexthops[num_nhs] = nexthop;
+      num_nhs++;
+    }
+
+  if (!num_nhs) {
+    zfpm_debug ("netlink_encode_route(): No useful nexthop.");
+    assert(0);
+    return NULL;
+  }
+
+  /*
+   * And add them to the message.
+   */
+  if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) {
+    assert(0);
+    return NULL;
+  }
+
+  msg->n_nexthops = 0;
+  for (u = 0; u < num_nhs; u++) {
+    if (!add_nexthop(allocator, msg, dest, nexthops[u])) {
+      assert(0);
+      return NULL;
+    }
+  }
+
+  assert(msg->n_nexthops == num_nhs);
+
+  return msg;
+}
+
+/*
+ * create_route_message
+ */
+static Fpm__Message *
+create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                     struct rib *rib)
+{
+  Fpm__Message *msg;
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__message__init(msg);
+
+  if (!rib) {
+    msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE;
+    msg->delete_route = create_delete_route_message(allocator, dest, rib);
+    if (!msg->delete_route) {
+      assert(0);
+      return NULL;
+    }
+    return msg;
+  }
+
+  msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE;
+  msg->add_route = create_add_route_message(allocator, dest, rib);
+  if (!msg->add_route) {
+    assert(0);
+    return NULL;
+  }
+
+  return msg;
+}
+
+/*
+ * zfpm_protobuf_encode_route
+ *
+ * Create a protobuf message corresponding to the given route in the
+ * given buffer space.
+ *
+ * Returns the number of bytes written to the buffer. 0 or a negative
+ * value indicates an error.
+ */
+int
+zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib,
+                           uint8_t *in_buf, size_t in_buf_len)
+{
+  Fpm__Message *msg;
+  QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096);
+  size_t len;
+
+  QPB_INIT_STACK_ALLOCATOR (allocator);
+
+  msg = create_route_message(&allocator, dest, rib);
+  if (!msg) {
+    assert(0);
+    return 0;
+  }
+
+  len = fpm__message__pack(msg, (uint8_t *) in_buf);
+  assert(len <= in_buf_len);
+
+  QPB_RESET_STACK_ALLOCATOR (allocator);
+  return len;
+}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
new file mode 100644 (file)
index 0000000..9ca0290
--- /dev/null
@@ -0,0 +1,3347 @@
+/* Routing Information Base.
+ * Copyright (C) 1997, 98, 99, 2001 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "str.h"
+#include "command.h"
+#include "if.h"
+#include "log.h"
+#include "sockunion.h"
+#include "linklist.h"
+#include "thread.h"
+#include "workqueue.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "vrf.h"
+#include "nexthop.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_fpm.h"
+#include "zebra/zebra_rnh.h"
+
+/* Default rtm_table for all clients */
+extern struct zebra_t zebrad;
+
+/* Hold time for RIB process, should be very minimal.
+ * it is useful to able to set it otherwise for testing, hence exported
+ * as global here for test-rig code.
+ */
+int rib_process_hold_time = 10;
+
+/* Each route type's string and default distance value. */
+static const struct
+{  
+  int key;
+  int distance;
+} route_info[ZEBRA_ROUTE_MAX] =
+{
+  [ZEBRA_ROUTE_SYSTEM]  = {ZEBRA_ROUTE_SYSTEM,    0},
+  [ZEBRA_ROUTE_KERNEL]  = {ZEBRA_ROUTE_KERNEL,    0},
+  [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT,   0},
+  [ZEBRA_ROUTE_STATIC]  = {ZEBRA_ROUTE_STATIC,    1},
+  [ZEBRA_ROUTE_RIP]     = {ZEBRA_ROUTE_RIP,     120},
+  [ZEBRA_ROUTE_RIPNG]   = {ZEBRA_ROUTE_RIPNG,   120},
+  [ZEBRA_ROUTE_OSPF]    = {ZEBRA_ROUTE_OSPF,    110},
+  [ZEBRA_ROUTE_OSPF6]   = {ZEBRA_ROUTE_OSPF6,   110},
+  [ZEBRA_ROUTE_ISIS]    = {ZEBRA_ROUTE_ISIS,    115},
+  [ZEBRA_ROUTE_BGP]     = {ZEBRA_ROUTE_BGP,      20  /* IBGP is 200. */},
+  [ZEBRA_ROUTE_BABEL]   = {ZEBRA_ROUTE_BABEL,    95},
+  [ZEBRA_ROUTE_NHRP]    = {ZEBRA_ROUTE_NHRP,     10},
+  /* no entry/default: 150 */
+};
+
+/* RPF lookup behaviour */
+static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG;
+
+static void __attribute__((format (printf, 4, 5)))
+_rnode_zlog(const char *_func, struct route_node *rn, int priority,
+           const char *msgfmt, ...)
+{
+  char prefix[PREFIX_STRLEN], buf[256];
+  char msgbuf[512];
+  va_list ap;
+
+  va_start(ap, msgfmt);
+  vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap);
+  va_end(ap);
+
+  if (rn)
+    {
+      rib_table_info_t *info = rn->table->info;
+
+      snprintf(buf, sizeof(buf), "%s%s vrf %u",
+               prefix2str(&rn->p, prefix, sizeof(prefix)),
+               info->safi == SAFI_MULTICAST ? " (MRIB)" : "",
+               info->zvrf->vrf_id);
+    }
+  else
+    {
+      snprintf(buf, sizeof(buf), "{(route_node *) NULL}");
+    }
+
+  zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf);
+}
+
+#define rnode_debug(node, ...) \
+       _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__)
+#define rnode_info(node, ...) \
+       _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__)
+
+/* Add nexthop to the end of a rib node's nexthop list */
+void
+rib_nexthop_add (struct rib *rib, struct nexthop *nexthop)
+{
+  nexthop_add(&rib->nexthop, nexthop);
+  rib->nexthop_num++;
+}
+
+/* Delete specified nexthop from the list. */
+static void
+rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop)
+{
+  if (nexthop->next)
+    nexthop->next->prev = nexthop->prev;
+  if (nexthop->prev)
+    nexthop->prev->next = nexthop->next;
+  else
+    rib->nexthop = nexthop->next;
+  rib->nexthop_num--;
+}
+
+struct nexthop *
+rib_nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IFINDEX;
+  nexthop->ifindex = ifindex;
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_ifname_add (struct rib *rib, char *ifname)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IFNAME;
+  nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname);
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IPV4;
+  nexthop->gate.ipv4 = *ipv4;
+  if (src)
+    nexthop->src.ipv4 = *src;
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, 
+                             struct in_addr *src, ifindex_t ifindex)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+  nexthop->gate.ipv4 = *ipv4;
+  if (src)
+    nexthop->src.ipv4 = *src;
+  nexthop->ifindex = ifindex;
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IPV6;
+  nexthop->gate.ipv6 = *ipv6;
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+static struct nexthop *
+rib_nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
+                            char *ifname)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME;
+  nexthop->gate.ipv6 = *ipv6;
+  nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname);
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
+                             ifindex_t ifindex)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+  nexthop->gate.ipv6 = *ipv6;
+  nexthop->ifindex = ifindex;
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+struct nexthop *
+rib_nexthop_blackhole_add (struct rib *rib)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new ();
+  nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
+  SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE);
+
+  rib_nexthop_add (rib, nexthop);
+
+  return nexthop;
+}
+
+/* This method checks whether a recursive nexthop has at
+ * least one resolved nexthop in the fib.
+ */
+int
+nexthop_has_fib_child(struct nexthop *nexthop)
+{
+  struct nexthop *nh;
+
+  if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+    return 0;
+
+  for (nh = nexthop->resolved; nh; nh = nh->next)
+    if (CHECK_FLAG (nh->flags, NEXTHOP_FLAG_FIB))
+      return 1;
+
+  return 0;
+}
+
+/* If force flag is not set, do not modify falgs at all for uninstall
+   the route from FIB. */
+static int
+nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
+                    struct route_node *top)
+{
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  int resolved;
+  struct nexthop *newhop;
+  struct nexthop *resolved_hop;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4)
+    nexthop->ifindex = 0;
+
+  if (set)
+    {
+      UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      nexthops_free(nexthop->resolved);
+      nexthop->resolved = NULL;
+      rib->nexthop_mtu = 0;
+    }
+
+  /* Make lookup prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = nexthop->gate.ipv4;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, rib->vrf_id);
+  if (! table)
+    return 0;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  while (rn)
+    {
+      route_unlock_node (rn);
+      
+      /* If lookup self prefix return immediately. */
+      if (rn == top)
+       return 0;
+
+      /* Pick up selected route. */
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+           break;
+       }
+
+      /* If there is no selected route or matched route is EGP, go up
+         tree. */
+      if (! match 
+         || match->type == ZEBRA_ROUTE_BGP)
+       {
+         do {
+           rn = rn->parent;
+         } while (rn && rn->info == NULL);
+         if (rn)
+           route_lock_node (rn);
+       }
+      else
+       {
+         /* If the longest prefix match for the nexthop yields
+          * a blackhole, mark it as inactive. */
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+             || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+           return 0;
+
+         if (match->type == ZEBRA_ROUTE_CONNECT)
+           {
+             /* Directly point connected route. */
+             newhop = match->nexthop;
+             if (newhop && nexthop->type == NEXTHOP_TYPE_IPV4)
+               nexthop->ifindex = newhop->ifindex;
+             
+             return 1;
+           }
+         else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
+           {
+             resolved = 0;
+             for (newhop = match->nexthop; newhop; newhop = newhop->next)
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
+                   && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
+                 {
+                   if (set)
+                     {
+                       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* If the resolving route specifies a gateway, use it */
+                       if (newhop->type == NEXTHOP_TYPE_IPV4
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME)
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+                           resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       /* If the resolving route is an interface route, it
+                        * means the gateway we are looking up is connected
+                        * to that interface. Therefore, the resolved route
+                        * should have the original gateway as nexthop as it
+                        * is directly connected. */
+                       if (newhop->type == NEXTHOP_TYPE_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                           resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+                           resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+                           resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       nexthop_add(&nexthop->resolved, resolved_hop);
+                     }
+                   resolved = 1;
+                 }
+              if (resolved && set)
+                rib->nexthop_mtu = match->mtu;
+             return resolved;
+           }
+         else
+           {
+             return 0;
+           }
+       }
+    }
+  return 0;
+}
+
+/* If force flag is not set, do not modify falgs at all for uninstall
+   the route from FIB. */
+static int
+nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
+                    struct route_node *top)
+{
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  int resolved;
+  struct nexthop *newhop;
+  struct nexthop *resolved_hop;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV6)
+    nexthop->ifindex = 0;
+
+  if (set)
+    {
+      UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      nexthops_free(nexthop->resolved);
+      nexthop->resolved = NULL;
+    }
+
+  /* Make lookup prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = IPV6_MAX_PREFIXLEN;
+  p.prefix = nexthop->gate.ipv6;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, rib->vrf_id);
+  if (! table)
+    return 0;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  while (rn)
+    {
+      route_unlock_node (rn);
+      
+      /* If lookup self prefix return immediately. */
+      if (rn == top)
+       return 0;
+
+      /* Pick up selected route. */
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+           break;
+       }
+
+      /* If there is no selected route or matched route is EGP, go up
+         tree. */
+      if (! match
+         || match->type == ZEBRA_ROUTE_BGP)
+       {
+         do {
+           rn = rn->parent;
+         } while (rn && rn->info == NULL);
+         if (rn)
+           route_lock_node (rn);
+       }
+      else
+       {
+         /* If the longest prefix match for the nexthop yields
+          * a blackhole, mark it as inactive. */
+         if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+             || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+           return 0;
+
+         if (match->type == ZEBRA_ROUTE_CONNECT)
+           {
+             /* Directly point connected route. */
+             newhop = match->nexthop;
+
+             if (newhop && nexthop->type == NEXTHOP_TYPE_IPV6)
+               nexthop->ifindex = newhop->ifindex;
+             
+             return 1;
+           }
+         else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
+           {
+             resolved = 0;
+             for (newhop = match->nexthop; newhop; newhop = newhop->next)
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
+                   && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
+                 {
+                   if (set)
+                     {
+                       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* See nexthop_active_ipv4 for a description how the
+                        * resolved nexthop is constructed. */
+                       if (newhop->type == NEXTHOP_TYPE_IPV6
+                           || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+                           if (newhop->ifindex)
+                             {
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->ifindex = newhop->ifindex;
+                             }
+                         }
+
+                       if (newhop->type == NEXTHOP_TYPE_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                               resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+                               resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       nexthop_add(&nexthop->resolved, resolved_hop);
+                     }
+                   resolved = 1;
+                 }
+             return resolved;
+           }
+         else
+           {
+             return 0;
+           }
+       }
+    }
+  return 0;
+}
+
+struct rib *
+rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp,
+                    struct route_node **rn_out, vrf_id_t vrf_id)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  struct nexthop *newhop, *tnewhop;
+  int recursing;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, safi, vrf_id);
+  if (! table)
+    return 0;
+
+  rn = route_node_match_ipv4 (table, &addr);
+
+  while (rn)
+    {
+      route_unlock_node (rn);
+
+      /* Pick up selected route. */
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+           break;
+       }
+
+      /* If there is no selected route or matched route is EGP, go up
+         tree. */
+      if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP)))
+       {
+         do {
+           rn = rn->parent;
+         } while (rn && rn->info == NULL);
+         if (rn)
+           route_lock_node (rn);
+       }
+      else
+       {
+         if (match->type != ZEBRA_ROUTE_CONNECT)
+           {
+             int found = 0;
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
+                 {
+                   found = 1;
+                   break;
+                 }
+             if (!found)
+               return NULL;
+           }
+
+         if (rn_out)
+           *rn_out = rn;
+         return match;
+       }
+    }
+  return NULL;
+}
+
+struct rib *
+rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out,
+    vrf_id_t vrf_id)
+{
+  struct rib *rib = NULL, *mrib = NULL, *urib = NULL;
+  struct route_node *m_rn = NULL, *u_rn = NULL;
+  int skip_bgp = 0; /* bool */
+
+  switch (ipv4_multicast_mode)
+    {
+    case MCAST_MRIB_ONLY:
+      return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out,
+                                  vrf_id);
+    case MCAST_URIB_ONLY:
+      return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out,
+                                  vrf_id);
+    case MCAST_NO_CONFIG:
+    case MCAST_MIX_MRIB_FIRST:
+      rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
+                                        vrf_id);
+      if (!mrib)
+        rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
+                                          vrf_id);
+      break;
+    case MCAST_MIX_DISTANCE:
+      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
+                                  vrf_id);
+      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
+                                  vrf_id);
+      if (mrib && urib)
+       rib = urib->distance < mrib->distance ? urib : mrib;
+      else if (mrib)
+       rib = mrib;
+      else if (urib)
+       rib = urib;
+      break;
+    case MCAST_MIX_PFXLEN:
+      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
+                                  vrf_id);
+      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
+                                  vrf_id);
+      if (mrib && urib)
+       rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib;
+      else if (mrib)
+       rib = mrib;
+      else if (urib)
+       rib = urib;
+      break;
+  }
+
+  if (rn_out)
+    *rn_out = (rib == mrib) ? m_rn : u_rn;
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    {
+      char buf[BUFSIZ];
+      inet_ntop (AF_INET, &addr, buf, BUFSIZ);
+
+      zlog_debug("%s: %s vrf %u: found %s, using %s",
+                __func__, buf, vrf_id,
+                 mrib ? (urib ? "MRIB+URIB" : "MRIB") :
+                         urib ? "URIB" : "nothing",
+                rib == urib ? "URIB" : rib == mrib ? "MRIB" : "none");
+    }
+  return rib;
+}
+
+void
+multicast_mode_ipv4_set (enum multicast_mode mode)
+{
+  if (IS_ZEBRA_DEBUG_RIB)
+    zlog_debug("%s: multicast lookup mode set (%d)", __func__, mode);
+  ipv4_multicast_mode = mode;
+}
+
+enum multicast_mode
+multicast_mode_ipv4_get (void)
+{
+  return ipv4_multicast_mode;
+}
+
+struct rib *
+rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return 0;
+
+  rn = route_node_lookup (table, (struct prefix *) p);
+
+  /* No route for this prefix. */
+  if (! rn)
+    return NULL;
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+       continue;
+      if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+       break;
+    }
+
+  if (! match || match->type == ZEBRA_ROUTE_BGP)
+    return NULL;
+
+  if (match->type == ZEBRA_ROUTE_CONNECT)
+    return match;
+  
+  for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
+    if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+      return match;
+
+  return NULL;
+}
+
+/*
+ * This clone function, unlike its original rib_lookup_ipv4(), checks
+ * if specified IPv4 route record (prefix/mask -> gate) exists in
+ * the whole RIB and has ZEBRA_FLAG_SELECTED set.
+ *
+ * Return values:
+ * -1: error
+ * 0: exact match found
+ * 1: a match was found with a different gate
+ * 2: connected route found
+ * 3: no matches found
+ */
+int
+rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate,
+    vrf_id_t vrf_id)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int nexthops_active;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return ZEBRA_RIB_LOOKUP_ERROR;
+
+  /* Scan the RIB table for exactly matching RIB entry. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+
+  /* No route for this prefix. */
+  if (! rn)
+    return ZEBRA_RIB_NOTFOUND;
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  /* Find out if a "selected" RR for the discovered RIB entry exists ever. */
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+       continue;
+      if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+       break;
+    }
+
+  /* None such found :( */
+  if (!match)
+    return ZEBRA_RIB_NOTFOUND;
+
+  if (match->type == ZEBRA_ROUTE_CONNECT)
+    return ZEBRA_RIB_FOUND_CONNECTED;
+  
+  /* Ok, we have a cood candidate, let's check it's nexthop list... */
+  nexthops_active = 0;
+  for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
+    if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+      {
+        nexthops_active = 1;
+        if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate))
+          return ZEBRA_RIB_FOUND_EXACT;
+        if (IS_ZEBRA_DEBUG_RIB)
+          {
+            char gate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN];
+            inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN);
+            inet_ntop (AF_INET, &sockunion2ip(qgate), qgate_buf, INET_ADDRSTRLEN);
+            zlog_debug ("%s: qgate == %s, %s == %s", __func__,
+                        qgate_buf, recursing ? "rgate" : "gate", gate_buf);
+          }
+      }
+
+  if (nexthops_active)
+    return ZEBRA_RIB_FOUND_NOGATE;
+
+  return ZEBRA_RIB_NOTFOUND;
+}
+
+struct rib *
+rib_match_ipv6 (struct in6_addr *addr, vrf_id_t vrf_id)
+{
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *match;
+  struct nexthop *newhop, *tnewhop;
+  int recursing;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return 0;
+
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = IPV6_MAX_PREFIXLEN;
+  IPV6_ADDR_COPY (&p.prefix, addr);
+
+  rn = route_node_match (table, (struct prefix *) &p);
+
+  while (rn)
+    {
+      route_unlock_node (rn);
+      
+      /* Pick up selected route. */
+      RNODE_FOREACH_RIB (rn, match)
+       {
+         if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
+           continue;
+         if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB))
+           break;
+       }
+
+      /* If there is no selected route or matched route is EGP, go up
+         tree. */
+      if (! match 
+         || match->type == ZEBRA_ROUTE_BGP)
+       {
+         do {
+           rn = rn->parent;
+         } while (rn && rn->info == NULL);
+         if (rn)
+           route_lock_node (rn);
+       }
+      else
+       {
+         if (match->type == ZEBRA_ROUTE_CONNECT)
+           /* Directly point connected route. */
+           return match;
+         else
+           {
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
+                 return match;
+             return NULL;
+           }
+       }
+    }
+  return NULL;
+}
+
+#define RIB_SYSTEM_ROUTE(R) \
+        ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
+
+/* This function verifies reachability of one given nexthop, which can be
+ * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
+ * in nexthop->flags field. If the 4th parameter, 'set', is non-zero,
+ * nexthop->ifindex will be updated appropriately as well.
+ * An existing route map can turn (otherwise active) nexthop into inactive, but
+ * not vice versa.
+ *
+ * The return value is the final value of 'ACTIVE' flag.
+ */
+
+static unsigned
+nexthop_active_check (struct route_node *rn, struct rib *rib,
+                     struct nexthop *nexthop, int set)
+{
+  rib_table_info_t *info = rn->table->info;
+  struct interface *ifp;
+  route_map_result_t ret = RMAP_MATCH;
+  extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1];
+  struct route_map *rmap;
+  int family;
+
+  family = 0;
+  switch (nexthop->type)
+    {
+    case NEXTHOP_TYPE_IFINDEX:
+      ifp = if_lookup_by_index_vrf (nexthop->ifindex, rib->vrf_id);
+      if (ifp && if_is_operative(ifp))
+       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      else
+       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      break;
+    case NEXTHOP_TYPE_IPV6_IFNAME:
+      family = AFI_IP6;
+    case NEXTHOP_TYPE_IFNAME:
+      ifp = if_lookup_by_name_vrf (nexthop->ifname, rib->vrf_id);
+      if (ifp && if_is_operative(ifp))
+       {
+         if (set)
+           nexthop->ifindex = ifp->ifindex;
+         SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+       }
+      else
+       {
+         if (set)
+           nexthop->ifindex = 0;
+         UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+       }
+      break;
+    case NEXTHOP_TYPE_IPV4:
+    case NEXTHOP_TYPE_IPV4_IFINDEX:
+      family = AFI_IP;
+      if (nexthop_active_ipv4 (rib, nexthop, set, rn))
+       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      else
+       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      break;
+    case NEXTHOP_TYPE_IPV6:
+      family = AFI_IP6;
+      if (nexthop_active_ipv6 (rib, nexthop, set, rn))
+       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      else
+       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      break;
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+      family = AFI_IP6;
+      if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6))
+       {
+         ifp = if_lookup_by_index_vrf (nexthop->ifindex, rib->vrf_id);
+         if (ifp && if_is_operative(ifp))
+           SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+         else
+           UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+       }
+      else
+       {
+         if (nexthop_active_ipv6 (rib, nexthop, set, rn))
+           SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+         else
+           UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+       }
+      break;
+    case NEXTHOP_TYPE_BLACKHOLE:
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+      break;
+    default:
+      break;
+    }
+  if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+    return 0;
+
+  /* XXX: What exactly do those checks do? Do we support
+   * e.g. IPv4 routes with IPv6 nexthops or vice versa? */
+  if (RIB_SYSTEM_ROUTE(rib) ||
+      (family == AFI_IP && rn->p.family != AF_INET) ||
+      (family == AFI_IP6 && rn->p.family != AF_INET6))
+    return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+
+  /* The original code didn't determine the family correctly
+   * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
+   * from the rib_table_info in those cases.
+   * Possibly it may be better to use only the rib_table_info
+   * in every case.
+   */
+  if (!family)
+    family = info->afi;
+
+  rmap = 0;
+  if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX &&
+               proto_rm[family][rib->type])
+    rmap = route_map_lookup_by_name (proto_rm[family][rib->type]);
+  if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX])
+    rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]);
+  if (rmap) {
+      struct nexthop_vrfid nh_vrf = {nexthop, rib->vrf_id};
+      ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, &nh_vrf);
+  }
+
+  if (ret == RMAP_DENYMATCH)
+    UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+  return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+}
+
+/* Iterate over all nexthops of the given RIB entry and refresh their
+ * ACTIVE flag. rib->nexthop_active_num is updated accordingly. If any
+ * nexthop is found to toggle the ACTIVE flag, the whole rib structure
+ * is flagged with RIB_ENTRY_CHANGED. The 4th 'set' argument is
+ * transparently passed to nexthop_active_check().
+ *
+ * Return value is the new number of active nexthops.
+ */
+
+static int
+nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
+{
+  struct nexthop *nexthop;
+  unsigned int prev_active, new_active;
+  ifindex_t prev_index;
+  
+  rib->nexthop_active_num = 0;
+
+  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  {
+    prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+    prev_index = nexthop->ifindex;
+    if ((new_active = nexthop_active_check (rn, rib, nexthop, set)))
+      rib->nexthop_active_num++;
+    if (prev_active != new_active ||
+       prev_index != nexthop->ifindex)
+      SET_FLAG (rib->status, RIB_ENTRY_CHANGED);
+  }
+  return rib->nexthop_active_num;
+}
+
+
+
+static int
+rib_update_kernel (struct route_node *rn, struct rib *old, struct rib *new)
+{
+  int ret = 0;
+  struct nexthop *nexthop, *tnexthop;
+  rib_table_info_t *info = rn->table->info;
+  int recursing;
+
+  if (info->safi != SAFI_UNICAST)
+    {
+      if (new)
+        for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing))
+          SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+      if (old)
+        for (ALL_NEXTHOPS_RO(old->nexthop, nexthop, tnexthop, recursing))
+          UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+      return 0;
+    }
+
+  /*
+   * Make sure we update the FPM any time we send new information to
+   * the kernel.
+   */
+  zfpm_trigger_update (rn, "updating in kernel");
+
+  ret = kernel_route_rib (&rn->p, old, new);
+
+  /* This condition is never met, if we are using rt_socket.c */
+  if (ret < 0 && new)
+    {
+      for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing))
+        UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+  else if (old && old != new)
+    {
+      for (ALL_NEXTHOPS_RO(old->nexthop, nexthop, tnexthop, recursing))
+        UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+
+  return ret;
+}
+
+/* Uninstall the route from kernel. */
+static void
+rib_uninstall (struct route_node *rn, struct rib *rib)
+{
+  rib_table_info_t *info = rn->table->info;
+
+  if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+    {
+      if (info->safi == SAFI_UNICAST)
+        zfpm_trigger_update (rn, "rib_uninstall");
+
+      redistribute_delete (&rn->p, rib);
+      if (! RIB_SYSTEM_ROUTE (rib))
+       rib_update_kernel (rn, rib, NULL);
+      UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED);
+    }
+}
+
+static void rib_unlink (struct route_node *, struct rib *);
+
+/*
+ * rib_can_delete_dest
+ *
+ * Returns TRUE if the given dest can be deleted from the table.
+ */
+static int
+rib_can_delete_dest (rib_dest_t *dest)
+{
+  if (dest->routes)
+    {
+      return 0;
+    }
+
+  /*
+   * Don't delete the dest if we have to update the FPM about this
+   * prefix.
+   */
+  if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM) ||
+      CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
+    return 0;
+
+  return 1;
+}
+
+/*
+ * rib_gc_dest
+ *
+ * Garbage collect the rib dest corresponding to the given route node
+ * if appropriate.
+ *
+ * Returns TRUE if the dest was deleted, FALSE otherwise.
+ */
+int
+rib_gc_dest (struct route_node *rn)
+{
+  rib_dest_t *dest;
+
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
+    return 0;
+
+  if (!rib_can_delete_dest (dest))
+    return 0;
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    rnode_debug (rn, "removing dest from table");
+
+  dest->rnode = NULL;
+  XFREE (MTYPE_RIB_DEST, dest);
+  rn->info = NULL;
+
+  /*
+   * Release the one reference that we keep on the route node.
+   */
+  route_unlock_node (rn);
+  return 1;
+}
+
+/* Check if 'alternate' RIB entry is better than 'current'. */
+static struct rib *
+rib_choose_best (struct rib *current, struct rib *alternate)
+{
+  if (current == NULL)
+    return alternate;
+
+  /* filter route selection in following order:
+   * - connected beats other types
+   * - lower distance beats higher
+   * - lower metric beats higher for equal distance
+   * - last, hence oldest, route wins tie break.
+   */
+
+  /* Connected routes. Pick the last connected
+   * route of the set of lowest metric connected routes.
+   */
+  if (alternate->type == ZEBRA_ROUTE_CONNECT)
+    {
+      if (current->type != ZEBRA_ROUTE_CONNECT
+          || alternate->metric <= current->metric)
+        return alternate;
+
+      return current;
+    }
+
+  if (current->type == ZEBRA_ROUTE_CONNECT)
+    return current;
+
+  /* higher distance loses */
+  if (alternate->distance < current->distance)
+    return alternate;
+  if (current->distance < alternate->distance)
+    return current;
+
+  /* metric tie-breaks equal distance */
+  if (alternate->metric <= current->metric)
+    return alternate;
+
+  return current;
+}
+
+/* Core function for processing routing information base. */
+static void
+rib_process (struct route_node *rn)
+{
+  struct rib *rib;
+  struct rib *next;
+  struct rib *old_selected = NULL;
+  struct rib *new_selected = NULL;
+  struct rib *old_fib = NULL;
+  struct rib *new_fib = NULL;
+  int installed = 0;
+  struct nexthop *nexthop = NULL, *tnexthop;
+  int recursing;
+  rib_table_info_t *info;
+
+  assert (rn);
+
+  info = rn->table->info;
+
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED);
+
+      /* Currently installed rib. */
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+        {
+          assert (old_selected == NULL);
+          old_selected = rib;
+        }
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+        {
+          assert (old_fib == NULL);
+          old_fib = rib;
+        }
+
+      /* Skip deleted entries from selection */
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+      
+      /* Skip unreachable nexthop. */
+      if (! nexthop_active_update (rn, rib, 0))
+        continue;
+
+      /* Infinit distance. */
+      if (rib->distance == DISTANCE_INFINITY)
+        continue;
+
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE))
+        new_fib = rib_choose_best(new_fib, rib);
+      else
+        new_selected = rib_choose_best(new_selected, rib);
+    } /* RNODE_FOREACH_RIB_SAFE */
+
+  /* If no FIB override route, use the selected route also for FIB */
+  if (new_fib == NULL)
+    new_fib = new_selected;
+
+  /* After the cycle is finished, the following pointers will be set:
+   * old_selected --- RIB entry currently having SELECTED
+   * new_selected --- RIB entry that is newly SELECTED
+   * old_fib      --- RIB entry currently in kernel FIB
+   * new_fib      --- RIB entry that is newly to be in kernel FIB
+   *
+   * new_selected will get SELECTED flag, and is going to be redistributed
+   * the zclients. new_fib (which can be new_selected) will be installed in kernel.
+   */
+
+  /* Set real nexthops. */
+  if (new_fib)
+    nexthop_active_update (rn, new_fib, 1);
+  if (new_selected && new_selected != new_fib)
+    nexthop_active_update (rn, new_selected, 1);
+
+  /* Update kernel if FIB entry has changed */
+  if (old_fib != new_fib
+      || (new_fib && CHECK_FLAG (new_fib->status, RIB_ENTRY_CHANGED)))
+    {
+        if (old_fib && old_fib != new_fib)
+          {
+            if (! RIB_SYSTEM_ROUTE (old_fib) && (! new_fib || RIB_SYSTEM_ROUTE (new_fib)))
+              rib_update_kernel (rn, old_fib, NULL);
+            UNSET_FLAG (old_fib->status, RIB_ENTRY_SELECTED_FIB);
+          }
+
+        if (new_fib)
+          {
+            /* Install new or replace existing FIB entry */
+            SET_FLAG (new_fib->status, RIB_ENTRY_SELECTED_FIB);
+            if (! RIB_SYSTEM_ROUTE (new_fib))
+              rib_update_kernel (rn, old_fib, new_fib);
+          }
+
+        if (info->safi == SAFI_UNICAST)
+          zfpm_trigger_update (rn, "updating existing route");
+    }
+  else if (old_fib == new_fib && new_fib && ! RIB_SYSTEM_ROUTE (new_fib))
+    {
+      /* Housekeeping code to deal with race conditions in kernel with
+       * linux netlink reporting interface up before IPv4 or IPv6 protocol
+       * is ready to add routes. This makes sure routes are IN the kernel.
+       */
+      for (ALL_NEXTHOPS_RO(new_fib->nexthop, nexthop, tnexthop, recursing))
+        if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+          {
+            installed = 1;
+            break;
+          }
+      if (! installed)
+        rib_update_kernel (rn, NULL, new_fib);
+    }
+
+  /* Redistribute SELECTED entry */
+  if (old_selected != new_selected
+      || (new_selected && CHECK_FLAG (new_selected->status, RIB_ENTRY_CHANGED)))
+    {
+      if (old_selected)
+        {
+          if (! new_selected)
+            redistribute_delete (&rn->p, old_selected);
+          if (old_selected != new_selected)
+            UNSET_FLAG (old_selected->flags, ZEBRA_FLAG_SELECTED);
+        }
+
+      if (new_selected)
+        {
+          /* Install new or replace existing redistributed entry */
+          SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED);
+          redistribute_add (&rn->p, new_selected);
+        }
+     }
+
+  /* Remove all RIB entries queued for removal */
+  RNODE_FOREACH_RIB_SAFE (rn, rib, next)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        {
+          if (IS_ZEBRA_DEBUG_RIB)
+            rnode_debug (rn, "rn %p, removing rib %p",
+                        (void *)rn, (void *)rib);
+          rib_unlink (rn, rib);
+        }
+    }
+
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    rnode_debug (rn, "rn %p dequeued", (void *)rn);
+
+  /*
+   * Check if the dest can be deleted now.
+   */
+  rib_gc_dest (rn);
+}
+
+/* Take a list of route_node structs and return 1, if there was a record
+ * picked from it and processed by rib_process(). Don't process more, 
+ * than one RN record; operate only in the specified sub-queue.
+ */
+static unsigned int
+process_subq (struct list * subq, u_char qindex)
+{
+  struct listnode *lnode  = listhead (subq);
+  struct route_node *rnode;
+
+  if (!lnode)
+    return 0;
+
+  rnode = listgetdata (lnode);
+  rib_process (rnode);
+
+  if (rnode->info)
+    UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex));
+
+#if 0
+  else
+    {
+      zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+                  __func__, rnode, rnode->lock);
+      zlog_backtrace(LOG_DEBUG);
+    }
+#endif
+  route_unlock_node (rnode);
+  list_delete_node (subq, lnode);
+  return 1;
+}
+
+/*
+ * All meta queues have been processed. Trigger next-hop evaluation.
+ */
+static void
+meta_queue_process_complete (struct work_queue *dummy)
+{
+  zebra_evaluate_rnh_table(0, AF_INET);
+#ifdef HAVE_IPV6
+  zebra_evaluate_rnh_table(0, AF_INET6);
+#endif /* HAVE_IPV6 */
+}
+
+/* Dispatch the meta queue by picking, processing and unlocking the next RN from
+ * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data
+ * is pointed to the meta queue structure.
+ */
+static wq_item_status
+meta_queue_process (struct work_queue *dummy, void *data)
+{
+  struct meta_queue * mq = data;
+  unsigned i;
+
+  for (i = 0; i < MQ_SIZE; i++)
+    if (process_subq (mq->subq[i], i))
+      {
+       mq->size--;
+       break;
+      }
+  return mq->size ? WQ_REQUEUE : WQ_SUCCESS;
+}
+
+/*
+ * Map from rib types to queue type (priority) in meta queue
+ */
+static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = {
+  [ZEBRA_ROUTE_SYSTEM]  = 4,
+  [ZEBRA_ROUTE_KERNEL]  = 0,
+  [ZEBRA_ROUTE_CONNECT] = 0,
+  [ZEBRA_ROUTE_STATIC]  = 1,
+  [ZEBRA_ROUTE_RIP]     = 2,
+  [ZEBRA_ROUTE_RIPNG]   = 2,
+  [ZEBRA_ROUTE_OSPF]    = 2,
+  [ZEBRA_ROUTE_OSPF6]   = 2,
+  [ZEBRA_ROUTE_ISIS]    = 2,
+  [ZEBRA_ROUTE_BGP]     = 3,
+  [ZEBRA_ROUTE_HSLS]    = 4,
+  [ZEBRA_ROUTE_BABEL]   = 2,
+  [ZEBRA_ROUTE_NHRP]    = 2,
+};
+
+/* Look into the RN and queue it into one or more priority queues,
+ * increasing the size for each data push done.
+ */
+static void
+rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
+{
+  struct rib *rib;
+
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      u_char qindex = meta_queue_map[rib->type];
+
+      /* Invariant: at this point we always have rn->info set. */
+      if (CHECK_FLAG (rib_dest_from_rnode (rn)->flags,
+                     RIB_ROUTE_QUEUED (qindex)))
+       {
+         if (IS_ZEBRA_DEBUG_RIB_Q)
+           rnode_debug (rn, "rn %p is already queued in sub-queue %u",
+                        (void *)rn, qindex);
+         continue;
+       }
+
+      SET_FLAG (rib_dest_from_rnode (rn)->flags, RIB_ROUTE_QUEUED (qindex));
+      listnode_add (mq->subq[qindex], rn);
+      route_lock_node (rn);
+      mq->size++;
+
+      if (IS_ZEBRA_DEBUG_RIB_Q)
+       rnode_debug (rn, "queued rn %p into sub-queue %u",
+                     (void *)rn, qindex);
+    }
+}
+
+/* Add route_node to work queue and schedule processing */
+static void
+rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
+{
+  assert (zebra && rn);
+
+  /* Pointless to queue a route_node with no RIB entries to add or remove */
+  if (!rnode_to_ribs (rn))
+    {
+      zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+                  __func__, (void *)rn, rn->lock);
+      zlog_backtrace(LOG_DEBUG);
+      return;
+    }
+
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    rnode_info (rn, "work queue added");
+
+  assert (zebra);
+
+  if (zebra->ribq == NULL)
+    {
+      zlog_err ("%s: work_queue does not exist!", __func__);
+      return;
+    }
+
+  /*
+   * The RIB queue should normally be either empty or holding the only
+   * work_queue_item element. In the latter case this element would
+   * hold a pointer to the meta queue structure, which must be used to
+   * actually queue the route nodes to process. So create the MQ
+   * holder, if necessary, then push the work into it in any case.
+   * This semantics was introduced after 0.99.9 release.
+   */
+  if (!zebra->ribq->items->count)
+    work_queue_add (zebra->ribq, zebra->mq);
+
+  rib_meta_queue_add (zebra->mq, rn);
+
+  if (IS_ZEBRA_DEBUG_RIB_Q)
+    rnode_debug (rn, "rn %p queued", (void *)rn);
+
+  return;
+}
+
+/* Create new meta queue.
+   A destructor function doesn't seem to be necessary here.
+ */
+static struct meta_queue *
+meta_queue_new (void)
+{
+  struct meta_queue *new;
+  unsigned i;
+
+  new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct meta_queue));
+  assert(new);
+
+  for (i = 0; i < MQ_SIZE; i++)
+    {
+      new->subq[i] = list_new ();
+      assert(new->subq[i]);
+    }
+
+  return new;
+}
+
+/* initialise zebra rib work queue */
+static void
+rib_queue_init (struct zebra_t *zebra)
+{
+  assert (zebra);
+  
+  if (! (zebra->ribq = work_queue_new (zebra->master, 
+                                       "route_node processing")))
+    {
+      zlog_err ("%s: could not initialise work queue!", __func__);
+      return;
+    }
+
+  /* fill in the work queue spec */
+  zebra->ribq->spec.workfunc = &meta_queue_process;
+  zebra->ribq->spec.errorfunc = NULL;
+  zebra->ribq->spec.completion_func = &meta_queue_process_complete;
+  /* XXX: TODO: These should be runtime configurable via vty */
+  zebra->ribq->spec.max_retries = 3;
+  zebra->ribq->spec.hold = rib_process_hold_time;
+  
+  if (!(zebra->mq = meta_queue_new ()))
+  {
+    zlog_err ("%s: could not initialise meta queue!", __func__);
+    return;
+  }
+  return;
+}
+
+/* RIB updates are processed via a queue of pointers to route_nodes.
+ *
+ * The queue length is bounded by the maximal size of the routing table,
+ * as a route_node will not be requeued, if already queued.
+ *
+ * RIBs are submitted via rib_addnode or rib_delnode which set minimal
+ * state, or static_install_route (when an existing RIB is updated)
+ * and then submit route_node to queue for best-path selection later.
+ * Order of add/delete state changes are preserved for any given RIB.
+ *
+ * Deleted RIBs are reaped during best-path selection.
+ *
+ * rib_addnode
+ * |-> rib_link or unset RIB_ENTRY_REMOVE        |->Update kernel with
+ *       |-------->|                             |  best RIB, if required
+ *                 |                             |
+ * static_install->|->rib_addqueue...... -> rib_process
+ *                 |                             |
+ *       |-------->|                             |-> rib_unlink
+ * |-> set RIB_ENTRY_REMOVE                           |
+ * rib_delnode                                  (RIB freed)
+ *
+ * The 'info' pointer of a route_node points to a rib_dest_t
+ * ('dest'). Queueing state for a route_node is kept on the dest. The
+ * dest is created on-demand by rib_link() and is kept around at least
+ * as long as there are ribs hanging off it (@see rib_gc_dest()).
+ * 
+ * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
+ *
+ * - route_nodes: refcounted by:
+ *   - dest attached to route_node:
+ *     - managed by: rib_link/rib_gc_dest
+ *   - route_node processing queue
+ *     - managed by: rib_addqueue, rib_process.
+ *
+ */
+/* Add RIB to head of the route node. */
+static void
+rib_link (struct route_node *rn, struct rib *rib)
+{
+  struct rib *head;
+  rib_dest_t *dest;
+
+  assert (rib && rn);
+  
+  if (IS_ZEBRA_DEBUG_RIB)
+    rnode_debug (rn, "rn %p, rib %p", (void *)rn, (void *)rib);
+
+  dest = rib_dest_from_rnode (rn);
+  if (!dest)
+    {
+      if (IS_ZEBRA_DEBUG_RIB)
+       rnode_debug (rn, "adding dest to table");
+
+      dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t));
+      route_lock_node (rn); /* rn route table reference */
+      rn->info = dest;
+      dest->rnode = rn;
+    }
+
+  head = dest->routes;
+  if (head)
+    {
+      head->prev = rib;
+    }
+  rib->next = head;
+  dest->routes = rib;
+  rib_queue_add (&zebrad, rn);
+}
+
+static void
+rib_addnode (struct route_node *rn, struct rib *rib)
+{
+  /* RIB node has been un-removed before route-node is processed. 
+   * route_node must hence already be on the queue for processing.. 
+   */
+  if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+    {
+      if (IS_ZEBRA_DEBUG_RIB)
+        rnode_debug (rn, "rn %p, un-removed rib %p", (void *)rn, (void *)rib);
+
+      UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED);
+      return;
+    }
+  rib_link (rn, rib);
+}
+
+/*
+ * rib_unlink
+ *
+ * Detach a rib structure from a route_node.
+ *
+ * Note that a call to rib_unlink() should be followed by a call to
+ * rib_gc_dest() at some point. This allows a rib_dest_t that is no
+ * longer required to be deleted.
+ */
+static void
+rib_unlink (struct route_node *rn, struct rib *rib)
+{
+  rib_dest_t *dest;
+
+  assert (rn && rib);
+
+  if (IS_ZEBRA_DEBUG_RIB)
+    rnode_debug (rn, "rn %p, rib %p", (void *)rn, (void *)rib);
+
+  dest = rib_dest_from_rnode (rn);
+
+  if (rib->next)
+    rib->next->prev = rib->prev;
+
+  if (rib->prev)
+    rib->prev->next = rib->next;
+  else
+    {
+      dest->routes = rib->next;
+    }
+
+  /* free RIB and nexthops */
+  nexthops_free(rib->nexthop);
+  XFREE (MTYPE_RIB, rib);
+
+}
+
+static void
+rib_delnode (struct route_node *rn, struct rib *rib)
+{
+  if (IS_ZEBRA_DEBUG_RIB)
+    rnode_debug (rn, "rn %p, rib %p, removing", (void *)rn, (void *)rib);
+  SET_FLAG (rib->status, RIB_ENTRY_REMOVED);
+  rib_queue_add (&zebrad, rn);
+}
+
+int
+rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, 
+             struct in_addr *gate, struct in_addr *src,
+             ifindex_t ifindex, vrf_id_t vrf_id, int table_id,
+             u_int32_t metric, u_int32_t mtu, u_char distance, safi_t safi)
+{
+  struct rib *rib;
+  struct rib *same = NULL;
+  struct route_table *table;
+  struct route_node *rn;
+  struct nexthop *nexthop;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, safi, vrf_id);
+  if (! table)
+    return 0;
+
+  /* Make it sure prefixlen is applied to the prefix. */
+  apply_mask_ipv4 (p);
+
+  /* Set default distance by route type. */
+  if (distance == 0)
+    {
+      if ((unsigned)type >= array_size(route_info))
+       distance = 150;
+      else
+        distance = route_info[type].distance;
+
+      /* iBGP distance is 200. */
+      if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
+       distance = 200;
+    }
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+      
+      if (rib->type != type)
+       continue;
+      if (rib->type != ZEBRA_ROUTE_CONNECT)
+        {
+          same = rib;
+          break;
+        }
+      /* Duplicate connected route comes in. */
+      else if ((nexthop = rib->nexthop) &&
+              nexthop->type == NEXTHOP_TYPE_IFINDEX &&
+              nexthop->ifindex == ifindex &&
+              !CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+       {
+         rib->refcnt++;
+         return 0 ;
+       }
+    }
+
+  /* Allocate new rib structure. */
+  rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+  rib->type = type;
+  rib->distance = distance;
+  rib->flags = flags;
+  rib->metric = metric;
+  rib->mtu = mtu;
+  rib->vrf_id = vrf_id;
+  rib->table = table_id;
+  rib->nexthop_num = 0;
+  rib->uptime = time (NULL);
+
+  /* Nexthop settings. */
+  if (gate)
+    {
+      if (ifindex)
+       rib_nexthop_ipv4_ifindex_add (rib, gate, src, ifindex);
+      else
+       rib_nexthop_ipv4_add (rib, gate, src);
+    }
+  else
+    rib_nexthop_ifindex_add (rib, ifindex);
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+  /* Link new rib to node.*/
+  if (IS_ZEBRA_DEBUG_RIB)
+    zlog_debug ("%s: calling rib_addnode (%p, %p)",
+               __func__, (void *)rn, (void *)rib);
+  rib_addnode (rn, rib);
+  
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+      zlog_debug ("%s: calling rib_delnode (%p, %p)",
+                 __func__, (void *)rn, (void *)rib);
+    rib_delnode (rn, same);
+  }
+  
+  route_unlock_node (rn);
+  return 0;
+}
+
+/* This function dumps the contents of a given RIB entry into
+ * standard debug log. Calling function name and IP prefix in
+ * question are passed as 1st and 2nd arguments.
+ */
+
+void _rib_dump (const char * func,
+               union prefix46constptr pp, const struct rib * rib)
+{
+  const struct prefix *p = pp.p;
+  char straddr[PREFIX_STRLEN];
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+
+  zlog_debug ("%s: dumping RIB entry %p for %s vrf %u", func, (void *)rib,
+              prefix2str(p, straddr, sizeof(straddr)), rib->vrf_id);
+  zlog_debug
+  (
+    "%s: refcnt == %lu, uptime == %lu, type == %u, table == %d",
+    func,
+    rib->refcnt,
+    (unsigned long) rib->uptime,
+    rib->type,
+    rib->table
+  );
+  zlog_debug
+  (
+    "%s: metric == %u, distance == %u, flags == %u, status == %u",
+    func,
+    rib->metric,
+    rib->distance,
+    rib->flags,
+    rib->status
+  );
+  zlog_debug
+  (
+    "%s: nexthop_num == %u, nexthop_active_num == %u, nexthop_fib_num == %u",
+    func,
+    rib->nexthop_num,
+    rib->nexthop_active_num,
+    rib->nexthop_fib_num
+  );
+
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      inet_ntop (p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN);
+      zlog_debug
+      (
+        "%s: %s %s with flags %s%s%s",
+        func,
+        (recursing ? "  NH" : "NH"),
+        straddr,
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""),
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""),
+        (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "")
+      );
+    }
+  zlog_debug ("%s: dump complete", func);
+}
+
+/* This is an exported helper to rtm_read() to dump the strange
+ * RIB entry found by rib_lookup_ipv4_route()
+ */
+
+void rib_lookup_and_dump (struct prefix_ipv4 * p)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  char prefix_buf[INET_ADDRSTRLEN];
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (! table)
+  {
+    zlog_err ("%s: zebra_vrf_table() returned NULL", __func__);
+    return;
+  }
+
+  /* Scan the RIB table for exactly matching RIB entry. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+
+  /* No route for this prefix. */
+  if (! rn)
+  {
+    zlog_debug ("%s: lookup failed for %s", __func__,
+                prefix2str((struct prefix*) p, prefix_buf, sizeof(prefix_buf)));
+    return;
+  }
+
+  /* Unlock node. */
+  route_unlock_node (rn);
+
+  /* let's go */
+  RNODE_FOREACH_RIB (rn, rib)
+  {
+    zlog_debug
+    (
+      "%s: rn %p, rib %p: %s, %s",
+      __func__,
+      (void *)rn,
+      (void *)rib,
+      (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"),
+      (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected")
+    );
+    rib_dump (p, rib);
+  }
+}
+
+int
+rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *same;
+  struct nexthop *nexthop;
+  int ret = 0;
+  
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, safi, rib->vrf_id);
+  if (! table)
+    return 0;
+
+  /* Make it sure prefixlen is applied to the prefix. */
+  apply_mask_ipv4 (p);
+
+  /* Set default distance by route type. */
+  if (rib->distance == 0)
+    {
+      rib->distance = route_info[rib->type].distance;
+
+      /* iBGP distance is 200. */
+      if (rib->type == ZEBRA_ROUTE_BGP 
+         && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
+       rib->distance = 200;
+    }
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, same)
+    {
+      if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED))
+        continue;
+      
+      if (same->type == rib->type && same->table == rib->table
+         && same->type != ZEBRA_ROUTE_CONNECT)
+        break;
+    }
+  
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT)
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+  /* Link new rib to node.*/
+  rib_addnode (rn, rib);
+  ret = 1;
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
+                __func__, (void *)rn, (void *)rib);
+    rib_dump (p, rib);
+  }
+
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+                  __func__, (void *)rn, (void *)same);
+      rib_dump (p, same);
+    }
+    rib_delnode (rn, same);
+    ret = -1;
+  }
+  
+  route_unlock_node (rn);
+  return ret;
+}
+
+/* XXX factor with rib_delete_ipv6 */
+int
+rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
+                struct in_addr *gate, ifindex_t ifindex, 
+                vrf_id_t vrf_id, safi_t safi)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *fib = NULL;
+  struct rib *same = NULL;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  char buf1[PREFIX_STRLEN];
+  char buf2[INET_ADDRSTRLEN];
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP, safi, vrf_id);
+  if (! table)
+    return 0;
+
+  /* Apply mask. */
+  apply_mask_ipv4 (p);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      if (gate)
+       zlog_debug ("rib_delete_ipv4(): route delete %s vrf %u via %s ifindex %d",
+                   prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                   inet_ntoa (*gate),
+                   ifindex);
+      else
+       zlog_debug ("rib_delete_ipv4(): route delete %s vrf %u ifindex %d",
+                   prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                   ifindex);
+    }
+
+  /* Lookup route node. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+  if (! rn)
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+       {
+         if (gate)
+           zlog_debug ("route %s vrf %u via %s ifindex %d doesn't exist in rib",
+                      prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                      inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
+                      ifindex);
+         else
+           zlog_debug ("route %s vrf %u ifindex %d doesn't exist in rib",
+                      prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                      ifindex);
+       }
+      return ZEBRA_ERR_RTNOEXIST;
+    }
+
+  /* Lookup same type route. */
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+       fib = rib;
+
+      if (rib->type != type)
+       continue;
+      if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
+         nexthop->type == NEXTHOP_TYPE_IFINDEX)
+       {
+         if (nexthop->ifindex != ifindex)
+           continue;
+         if (rib->refcnt)
+           {
+             rib->refcnt--;
+             route_unlock_node (rn);
+             route_unlock_node (rn);
+             return 0;
+           }
+         same = rib;
+         break;
+       }
+      /* Make sure that the route found has the same gateway. */
+      else
+        {
+          if (gate == NULL)
+            {
+              same = rib;
+              break;
+            }
+          for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+            if (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate))
+              {
+                same = rib;
+                break;
+              }
+          if (same)
+            break;
+        }
+    }
+  /* If same type of route can't be found and this message is from
+     kernel. */
+  if (! same)
+    {
+      if (fib && type == ZEBRA_ROUTE_KERNEL &&
+          CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE))
+        {
+          if (IS_ZEBRA_DEBUG_KERNEL)
+            {
+              zlog_debug ("Zebra route %s/%d was deleted by others from kernel",
+                         inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
+                         p->prefixlen);
+            }
+          /* This means someone else, other than Zebra, has deleted
+           * a Zebra router from the kernel. We will add it back */
+           rib_update_kernel(rn, NULL, fib);
+        }
+      else
+       {
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           {
+             if (gate)
+               zlog_debug ("route %s vrf %u via %s ifindex %d type %d "
+                          "doesn't exist in rib",
+                          prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                          inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
+                          ifindex,
+                          type);
+             else
+               zlog_debug ("route %s vrf %u ifindex %d type %d doesn't exist in rib",
+                          prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                          ifindex,
+                          type);
+           }
+         route_unlock_node (rn);
+         return ZEBRA_ERR_RTNOEXIST;
+       }
+    }
+  
+  if (same)
+    rib_delnode (rn, same);
+  
+  route_unlock_node (rn);
+  return 0;
+}
+
+/* Install static route into rib. */
+static void
+static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_route *si)
+{
+  struct rib *rib;
+  struct route_node *rn;
+  struct route_table *table;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (afi, safi, si->vrf_id);
+  if (! table)
+    return;
+
+  /* Lookup existing route */
+  rn = route_node_get (table, p);
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+       if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+         continue;
+        
+       if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
+         break;
+    }
+
+  if (rib)
+    {
+      /* if tag value changed , update old value in RIB */
+      if (rib->tag != si->tag)
+       rib->tag = si->tag;
+
+      /* Same distance static route is there.  Update it with new
+         nexthop. */
+      route_unlock_node (rn);
+      switch (si->type)
+        {
+       case STATIC_IPV4_GATEWAY:
+         rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+         break;
+       case STATIC_IPV4_IFNAME:
+         rib_nexthop_ifname_add (rib, si->ifname);
+         break;
+       case STATIC_IPV4_BLACKHOLE:
+         rib_nexthop_blackhole_add (rib);
+         break;
+       case STATIC_IPV6_GATEWAY:
+         rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
+         break;
+       case STATIC_IPV6_IFNAME:
+         rib_nexthop_ifname_add (rib, si->ifname);
+         break;
+       case STATIC_IPV6_GATEWAY_IFNAME:
+         rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
+         break;
+        }
+      rib_queue_add (&zebrad, rn);
+    }
+  else
+    {
+      /* This is new static route. */
+      rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+      
+      rib->type = ZEBRA_ROUTE_STATIC;
+      rib->distance = si->distance;
+      rib->metric = 0;
+      rib->vrf_id = si->vrf_id;
+      rib->table = zebrad.rtm_table_default;
+      rib->nexthop_num = 0;
+      rib->tag = si->tag;
+
+      switch (si->type)
+        {
+       case STATIC_IPV4_GATEWAY:
+         rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+         break;
+       case STATIC_IPV4_IFNAME:
+         rib_nexthop_ifname_add (rib, si->ifname);
+         break;
+       case STATIC_IPV4_BLACKHOLE:
+         rib_nexthop_blackhole_add (rib);
+         break;
+       case STATIC_IPV6_GATEWAY:
+         rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
+         break;
+       case STATIC_IPV6_IFNAME:
+         rib_nexthop_ifname_add (rib, si->ifname);
+         break;
+       case STATIC_IPV6_GATEWAY_IFNAME:
+         rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
+         break;
+        }
+
+      /* Save the flags of this static routes (reject, blackhole) */
+      rib->flags = si->flags;
+
+      /* Link this rib to the tree. */
+      rib_addnode (rn, rib);
+    }
+}
+
+static int
+static_nexthop_same (struct nexthop *nexthop, struct static_route *si)
+{
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      && si->type == STATIC_IPV4_GATEWAY
+      && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4))
+    return 1;
+  if (nexthop->type == NEXTHOP_TYPE_IFNAME
+      && si->type == STATIC_IPV4_IFNAME
+      && strcmp (nexthop->ifname, si->ifname) == 0)
+    return 1;
+  if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE
+      && si->type == STATIC_IPV4_BLACKHOLE)
+    return 1;
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      && si->type == STATIC_IPV6_GATEWAY
+      && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6))
+    return 1;
+  if (nexthop->type == NEXTHOP_TYPE_IFNAME
+      && si->type == STATIC_IPV6_IFNAME
+      && strcmp (nexthop->ifname, si->ifname) == 0)
+    return 1;
+  if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      && si->type == STATIC_IPV6_GATEWAY_IFNAME
+      && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6)
+      && strcmp (nexthop->ifname, si->ifname) == 0)
+    return 1;
+  return 0;
+}
+
+/* Uninstall static route from RIB. */
+static void
+static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_route *si)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct nexthop *nexthop;
+  struct route_table *table;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (afi, safi, si->vrf_id);
+  if (! table)
+    return;
+  
+  /* Lookup existing route with type and distance. */
+  rn = route_node_lookup (table, p);
+  if (! rn)
+    return;
+
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance &&
+         rib->tag == si->tag)
+        break;
+    }
+
+  if (! rib)
+    {
+      route_unlock_node (rn);
+      return;
+    }
+
+  /* Lookup nexthop. */
+  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+    if (static_nexthop_same (nexthop, si))
+      break;
+
+  /* Can't find nexthop. */
+  if (! nexthop)
+    {
+      route_unlock_node (rn);
+      return;
+    }
+  
+  /* Check nexthop. */
+  if (rib->nexthop_num == 1)
+    rib_delnode (rn, rib);
+  else
+    {
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+        rib_uninstall (rn, rib);
+      rib_nexthop_delete (rib, nexthop);
+      nexthop_free (nexthop);
+      rib_queue_add (&zebrad, rn);
+    }
+  /* Unlock node. */
+  route_unlock_node (rn);
+}
+
+int
+static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate,
+                     const char *ifname, u_char flags, route_tag_t tag,
+                     u_char distance, vrf_id_t vrf_id)
+{
+  u_char type = 0;
+  struct route_node *rn;
+  struct static_route *si;
+  struct static_route *pp;
+  struct static_route *cp;
+  struct static_route *update = NULL;
+  struct zebra_vrf *zvrf = vrf_info_get (vrf_id);
+  struct route_table *stable = zvrf->stable[AFI_IP][safi];
+
+  if (! stable)
+    return -1;
+  
+  /* Lookup static route prefix. */
+  rn = route_node_get (stable, p);
+
+  /* Make flags. */
+  if (gate)
+    type = STATIC_IPV4_GATEWAY;
+  else if (ifname)
+    type = STATIC_IPV4_IFNAME;
+  else
+    type = STATIC_IPV4_BLACKHOLE;
+
+  /* Do nothing if there is a same static route.  */
+  for (si = rn->info; si; si = si->next)
+    {
+      if (type == si->type
+         && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4))
+         && (! ifname || strcmp (ifname, si->ifname) == 0))
+       {
+         if (distance == si->distance &&
+             tag == si->tag)
+           {
+             route_unlock_node (rn);
+             return 0;
+           }
+         else
+           update = si;
+       }
+    }
+
+  /* Distance or tag changed. */
+  if (update)
+    static_delete_ipv4_safi (safi, p, gate, ifname, update->tag, update->distance, vrf_id);
+
+  /* Make new static route structure. */
+  si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route));
+
+  si->type = type;
+  si->distance = distance;
+  si->tag = tag;
+  si->flags = flags;
+  si->vrf_id = vrf_id;
+
+  if (gate)
+    si->addr.ipv4 = *gate;
+  if (ifname)
+    si->ifname = XSTRDUP (MTYPE_TMP, ifname);
+
+  /* Add new static route information to the tree with sort by
+     distance value and gateway address. */
+  for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next)
+    {
+      if (si->distance < cp->distance)
+       break;
+      if (si->distance > cp->distance)
+       continue;
+      if (si->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY)
+       {
+         if (ntohl (si->addr.ipv4.s_addr) < ntohl (cp->addr.ipv4.s_addr))
+           break;
+         if (ntohl (si->addr.ipv4.s_addr) > ntohl (cp->addr.ipv4.s_addr))
+           continue;
+       }
+    }
+
+  /* Make linked list. */
+  if (pp)
+    pp->next = si;
+  else
+    rn->info = si;
+  if (cp)
+    cp->prev = si;
+  si->prev = pp;
+  si->next = cp;
+
+  /* Install into rib. */
+  static_install_route (AFI_IP, safi, p, si);
+
+  return 1;
+}
+
+int
+static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate,
+                        const char *ifname, route_tag_t tag, u_char distance,
+                        vrf_id_t vrf_id)
+{
+  u_char type = 0;
+  struct route_node *rn;
+  struct static_route *si;
+  struct route_table *stable;
+
+  /* Lookup table.  */
+  stable = zebra_vrf_static_table (AFI_IP, safi, vrf_id);
+  if (! stable)
+    return -1;
+
+  /* Lookup static route prefix. */
+  rn = route_node_lookup (stable, p);
+  if (! rn)
+    return 0;
+
+  /* Make flags. */
+  if (gate)
+    type = STATIC_IPV4_GATEWAY;
+  else if (ifname)
+    type = STATIC_IPV4_IFNAME;
+  else
+    type = STATIC_IPV4_BLACKHOLE;
+
+  /* Find same static route is the tree */
+  for (si = rn->info; si; si = si->next)
+    if (type == si->type
+       && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4))
+       && (! ifname || strcmp (ifname, si->ifname) == 0)
+       && (! tag || (tag == si->tag)))
+      break;
+
+  /* Can't find static route. */
+  if (! si)
+    {
+      route_unlock_node (rn);
+      return 0;
+    }
+
+  /* Install into rib. */
+  static_uninstall_route (AFI_IP, safi, p, si);
+
+  /* Unlink static route from linked list. */
+  if (si->prev)
+    si->prev->next = si->next;
+  else
+    rn->info = si->next;
+  if (si->next)
+    si->next->prev = si->prev;
+  route_unlock_node (rn);
+  
+  /* Free static route configuration. */
+  if (ifname)
+    XFREE (0, si->ifname);
+  XFREE (MTYPE_STATIC_ROUTE, si);
+
+  route_unlock_node (rn);
+
+  return 1;
+}
+
+int
+rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
+             struct in6_addr *gate, ifindex_t ifindex,
+             vrf_id_t vrf_id, int table_id,
+             u_int32_t metric, u_int32_t mtu, u_char distance, safi_t safi)
+{
+  struct rib *rib;
+  struct rib *same = NULL;
+  struct route_table *table;
+  struct route_node *rn;
+  struct nexthop *nexthop;
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, safi, vrf_id);
+  if (! table)
+    return 0;
+
+  /* Make sure mask is applied. */
+  apply_mask_ipv6 (p);
+
+  /* Set default distance by route type. */
+  if (!distance)
+    distance = route_info[type].distance;
+  
+  if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
+    distance = 200;
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (rib->type != type)
+       continue;
+      if (rib->type != ZEBRA_ROUTE_CONNECT)
+       {
+         same = rib;
+         break;
+       }
+      else if ((nexthop = rib->nexthop) &&
+              nexthop->type == NEXTHOP_TYPE_IFINDEX &&
+              nexthop->ifindex == ifindex)
+       {
+         rib->refcnt++;
+         return 0;
+       }
+    }
+
+  /* Allocate new rib structure. */
+  rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+  
+  rib->type = type;
+  rib->distance = distance;
+  rib->flags = flags;
+  rib->metric = metric;
+  rib->mtu = mtu;
+  rib->vrf_id = vrf_id;
+  rib->table = table_id;
+  rib->nexthop_num = 0;
+  rib->uptime = time (NULL);
+
+  /* Nexthop settings. */
+  if (gate)
+    {
+      if (ifindex)
+       rib_nexthop_ipv6_ifindex_add (rib, gate, ifindex);
+      else
+       rib_nexthop_ipv6_add (rib, gate);
+    }
+  else
+    rib_nexthop_ifindex_add (rib, ifindex);
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+  /* Link new rib to node.*/
+  rib_addnode (rn, rib);
+  if (IS_ZEBRA_DEBUG_RIB)
+  {
+    zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
+      __func__, (void *)rn, (void *)rib);
+    rib_dump (p, rib);
+  }
+
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+        __func__, (void *)rn, (void *)same);
+      rib_dump (p, same);
+    }
+    rib_delnode (rn, same);
+  }
+  
+  route_unlock_node (rn);
+  return 0;
+}
+
+int
+rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *same = NULL;
+  struct nexthop *nexthop;
+  int ret = 0;
+
+  if (!rib)
+    return 0;                  /* why are we getting called with NULL rib */
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, safi, rib->vrf_id);
+
+  if (! table)
+    return 0;
+
+  /* Make sure mask is applied. */
+  apply_mask_ipv6 (p);
+
+  /* Set default distance by route type. */
+  if (rib->distance == 0)
+    {
+      rib->distance = route_info[rib->type].distance;
+
+      /* iBGP distance is 200. */
+      if (rib->type == ZEBRA_ROUTE_BGP
+         && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
+       rib->distance = 200;
+    }
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, same) {
+     if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) {
+       continue;
+     }
+     if (same->type != rib->type) {
+       continue;
+     }
+
+     if (same->table != rib->table) {
+       continue;
+     }
+     if (same->type != ZEBRA_ROUTE_CONNECT) {
+       break;
+     }
+  }
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) {
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) {
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+  }
+
+  /* Link new rib to node.*/
+  rib_addnode (rn, rib);
+  ret = 1;
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+        __func__, rn, same);
+      rib_dump ((struct prefix *)p, same);
+    }
+    rib_delnode (rn, same);
+    ret = -1;
+  }
+
+  route_unlock_node (rn);
+  return ret;
+}
+
+/* XXX factor with rib_delete_ipv6 */
+int
+rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
+                struct in6_addr *gate, ifindex_t ifindex,
+                vrf_id_t vrf_id, safi_t safi)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *fib = NULL;
+  struct rib *same = NULL;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  char buf1[PREFIX_STRLEN];
+  char buf2[INET6_ADDRSTRLEN];
+
+  /* Apply mask. */
+  apply_mask_ipv6 (p);
+
+  /* Lookup table.  */
+  table = zebra_vrf_table (AFI_IP6, safi, vrf_id);
+  if (! table)
+    return 0;
+  
+  /* Lookup route node. */
+  rn = route_node_lookup (table, (struct prefix *) p);
+  if (! rn)
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+       {
+         if (gate)
+           zlog_debug ("route %s vrf %u via %s ifindex %d doesn't exist in rib",
+                      prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                      inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
+                      ifindex);
+         else
+           zlog_debug ("route %s vrf %u ifindex %d doesn't exist in rib",
+                      prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                      ifindex);
+       }
+      return ZEBRA_ERR_RTNOEXIST;
+    }
+
+  /* Lookup same type route. */
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
+        continue;
+
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+       fib = rib;
+
+      if (rib->type != type)
+        continue;
+      if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
+         nexthop->type == NEXTHOP_TYPE_IFINDEX)
+       {
+         if (nexthop->ifindex != ifindex)
+           continue;
+         if (rib->refcnt)
+           {
+             rib->refcnt--;
+             route_unlock_node (rn);
+             route_unlock_node (rn);
+             return 0;
+           }
+         same = rib;
+         break;
+       }
+      /* Make sure that the route found has the same gateway. */
+      else
+        {
+          if (gate == NULL)
+            {
+              same = rib;
+              break;
+            }
+          for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+            if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate))
+              {
+                same = rib;
+                break;
+              }
+          if (same)
+            break;
+        }
+    }
+
+  /* If same type of route can't be found and this message is from
+     kernel. */
+  if (! same)
+    {
+      if (fib && type == ZEBRA_ROUTE_KERNEL &&
+          CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE))
+        {
+          if (IS_ZEBRA_DEBUG_KERNEL)
+            {
+              zlog_debug ("Zebra route %s/%d was deleted by others from kernel",
+                         inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
+                         p->prefixlen);
+            }
+          /* This means someone else, other than Zebra, has deleted a Zebra
+           * route from the kernel. We will add it back */
+          rib_update_kernel(rn, NULL, fib);
+        }
+      else
+       {
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           {
+             if (gate)
+               zlog_debug ("route %s vrf %u via %s ifindex %d type %d "
+                          "doesn't exist in rib",
+                          prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                          inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
+                          ifindex,
+                          type);
+             else
+               zlog_debug ("route %s vrf %u ifindex %d type %d doesn't exist in rib",
+                          prefix2str (p, buf1, sizeof(buf1)), vrf_id,
+                          ifindex,
+                          type);
+           }
+         route_unlock_node (rn);
+         return ZEBRA_ERR_RTNOEXIST;
+       }
+    }
+
+  if (same)
+    rib_delnode (rn, same);
+  
+  route_unlock_node (rn);
+  return 0;
+}
+
+/* Add static route into static route configuration. */
+int
+static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
+                const char *ifname, u_char flags, route_tag_t tag,
+                u_char distance, vrf_id_t vrf_id)
+{
+  struct route_node *rn;
+  struct static_route *si;
+  struct static_route *pp;
+  struct static_route *cp;
+  struct static_route *update = NULL;
+  struct zebra_vrf *zvrf = vrf_info_get (vrf_id);
+  struct route_table *stable = zvrf->stable[AFI_IP6][SAFI_UNICAST];
+
+  if (! stable)
+    return -1;
+    
+  if (!gate &&
+      (type == STATIC_IPV6_GATEWAY || type == STATIC_IPV6_GATEWAY_IFNAME))
+    return -1;
+  
+  if (!ifname && 
+      (type == STATIC_IPV6_GATEWAY_IFNAME || type == STATIC_IPV6_IFNAME))
+    return -1;
+
+  /* Lookup static route prefix. */
+  rn = route_node_get (stable, p);
+
+  /* Do nothing if there is a same static route.  */
+  for (si = rn->info; si; si = si->next)
+    {
+      if (type == si->type
+         && tag == si->tag
+         && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6))
+         && (! ifname || strcmp (ifname, si->ifname) == 0))
+       {
+         if (distance == si->distance)
+           {
+             route_unlock_node (rn);
+             return 0;
+           }
+         else
+           update = si;
+       }
+    }
+
+  if (update)
+    static_delete_ipv6(p, type, gate, ifname, tag, update->distance, vrf_id);
+
+  /* Make new static route structure. */
+  si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route));
+
+  si->type = type;
+  si->distance = distance;
+  si->tag = tag;
+  si->flags = flags;
+  si->vrf_id = vrf_id;
+
+  switch (type)
+    {
+    case STATIC_IPV6_GATEWAY:
+      si->addr.ipv6 = *gate;
+      break;
+    case STATIC_IPV6_IFNAME:
+      si->ifname = XSTRDUP (MTYPE_TMP, ifname);
+      break;
+    case STATIC_IPV6_GATEWAY_IFNAME:
+      si->addr.ipv6 = *gate;
+      si->ifname = XSTRDUP (MTYPE_TMP, ifname);
+      break;
+    }
+
+  /* Add new static route information to the tree with sort by
+     distance value and gateway address. */
+  for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next)
+    {
+      if (si->distance < cp->distance)
+       break;
+      if (si->distance > cp->distance)
+       continue;
+    }
+
+  /* Make linked list. */
+  if (pp)
+    pp->next = si;
+  else
+    rn->info = si;
+  if (cp)
+    cp->prev = si;
+  si->prev = pp;
+  si->next = cp;
+
+  /* Install into rib. */
+  static_install_route (AFI_IP6, SAFI_UNICAST, p, si);
+
+  return 1;
+}
+
+/* Delete static route from static route configuration. */
+int
+static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
+                   const char *ifname, route_tag_t tag, u_char distance,
+                   vrf_id_t vrf_id)
+{
+  struct route_node *rn;
+  struct static_route *si;
+  struct route_table *stable;
+
+  /* Lookup table.  */
+  stable = zebra_vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! stable)
+    return -1;
+
+  /* Lookup static route prefix. */
+  rn = route_node_lookup (stable, p);
+  if (! rn)
+    return 0;
+
+  /* Find same static route is the tree */
+  for (si = rn->info; si; si = si->next)
+    if (distance == si->distance 
+       && type == si->type
+       && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6))
+       && (! ifname || strcmp (ifname, si->ifname) == 0)
+       && (! tag || (tag == si->tag)))
+      break;
+
+  /* Can't find static route. */
+  if (! si)
+    {
+      route_unlock_node (rn);
+      return 0;
+    }
+
+  /* Install into rib. */
+  static_uninstall_route (AFI_IP6, SAFI_UNICAST, p, si);
+
+  /* Unlink static route from linked list. */
+  if (si->prev)
+    si->prev->next = si->next;
+  else
+    rn->info = si->next;
+  if (si->next)
+    si->next->prev = si->prev;
+  
+  /* Free static route configuration. */
+  if (ifname)
+    XFREE (0, si->ifname);
+  XFREE (MTYPE_STATIC_ROUTE, si);
+
+  return 1;
+}
+
+/* RIB update function. */
+void
+rib_update (vrf_id_t vrf_id)
+{
+  struct route_node *rn;
+  struct route_table *table;
+  
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      if (rnode_to_ribs (rn))
+        rib_queue_add (&zebrad, rn);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      if (rnode_to_ribs (rn))
+        rib_queue_add (&zebrad, rn);
+}
+
+
+/* Remove all routes which comes from non main table.  */
+static void
+rib_weed_table (struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *next;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
+       {
+         if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+           continue;
+
+         if (rib->table != zebrad.rtm_table_default &&
+             rib->table != RT_TABLE_MAIN)
+            rib_delnode (rn, rib);
+       }
+}
+
+/* Delete all routes from non main table. */
+void
+rib_weed_tables (void)
+{
+  vrf_iter_t iter;
+  struct zebra_vrf *zvrf;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      {
+        rib_weed_table (zvrf->table[AFI_IP][SAFI_UNICAST]);
+        rib_weed_table (zvrf->table[AFI_IP6][SAFI_UNICAST]);
+      }
+}
+
+#if 0
+/* Delete self installed routes after zebra is relaunched.  */
+static void
+rib_sweep_table (struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *next;
+  int ret = 0;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
+       {
+         if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+           continue;
+
+         if (rib->type == ZEBRA_ROUTE_KERNEL && 
+             CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE))
+           {
+             ret = rib_update_kernel (rn, rib, NULL);
+             if (! ret)
+                rib_delnode (rn, rib);
+           }
+       }
+}
+#endif
+
+/* Sweep all RIB tables.  */
+void
+rib_sweep_route (void)
+{
+  vrf_iter_t iter;
+  struct zebra_vrf *zvrf;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      {
+        rib_weed_table (zvrf->table[AFI_IP][SAFI_UNICAST]);
+        rib_weed_table (zvrf->table[AFI_IP6][SAFI_UNICAST]);
+      }
+}
+
+/* Remove specific by protocol routes from 'table'. */
+static unsigned long
+rib_score_proto_table (u_char proto, struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *next;
+  unsigned long n = 0;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB_SAFE (rn, rib, next)
+        {
+          if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+            continue;
+          if (rib->type == proto)
+            {
+              rib_delnode (rn, rib);
+              n++;
+            }
+        }
+
+  return n;
+}
+
+/* Remove specific by protocol routes. */
+unsigned long
+rib_score_proto (u_char proto)
+{
+  vrf_iter_t iter;
+  struct zebra_vrf *zvrf;
+  unsigned long cnt = 0;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      cnt += rib_score_proto_table (proto, zvrf->table[AFI_IP][SAFI_UNICAST])
+            +rib_score_proto_table (proto, zvrf->table[AFI_IP6][SAFI_UNICAST]);
+
+  return cnt;
+}
+
+/* Close RIB and clean up kernel routes. */
+void
+rib_close_table (struct route_table *table)
+{
+  struct route_node *rn;
+  rib_table_info_t *info = table->info;
+  struct rib *rib;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      RNODE_FOREACH_RIB (rn, rib)
+        {
+          if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+           continue;
+
+          if (info->safi == SAFI_UNICAST)
+            zfpm_trigger_update (rn, NULL);
+
+         if (! RIB_SYSTEM_ROUTE (rib))
+           rib_update_kernel (rn, rib, NULL);
+        }
+}
+
+/* Close all RIB tables.  */
+void
+rib_close (void)
+{
+  vrf_iter_t iter;
+  struct zebra_vrf *zvrf;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      {
+        rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]);
+        rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]);
+      }
+}
+
+/* Routing information base initialize. */
+void
+rib_init (void)
+{
+  rib_queue_init (&zebrad);
+}
+
+/*
+ * vrf_id_get_next
+ *
+ * Get the first vrf id that is greater than the given vrf id if any.
+ *
+ * Returns TRUE if a vrf id was found, FALSE otherwise.
+ */
+static inline int
+vrf_id_get_next (vrf_id_t vrf_id, vrf_id_t *next_id_p)
+{
+  vrf_iter_t iter = vrf_iterator (vrf_id);
+  struct zebra_vrf *zvrf = vrf_iter2info (iter);
+
+  /* The same one ? Then find out the next. */
+  if (zvrf && (zvrf->vrf_id == vrf_id))
+    zvrf = vrf_iter2info (vrf_next (iter));
+
+  if (zvrf)
+    {
+      *next_id_p = zvrf->vrf_id;
+      return 1;
+    }
+
+  return 0;
+}
+
+/*
+ * rib_tables_iter_next
+ *
+ * Returns the next table in the iteration.
+ */
+struct route_table *
+rib_tables_iter_next (rib_tables_iter_t *iter)
+{
+  struct route_table *table;
+
+  /*
+   * Array that helps us go over all AFI/SAFI combinations via one
+   * index.
+   */
+  static struct {
+    afi_t afi;
+    safi_t safi;
+  } afi_safis[] = {
+    { AFI_IP, SAFI_UNICAST },
+    { AFI_IP, SAFI_MULTICAST },
+    { AFI_IP6, SAFI_UNICAST },
+    { AFI_IP6, SAFI_MULTICAST },
+  };
+
+  table = NULL;
+
+  switch (iter->state)
+    {
+
+    case RIB_TABLES_ITER_S_INIT:
+      iter->vrf_id = VRF_DEFAULT;
+      iter->afi_safi_ix = -1;
+
+      /* Fall through */
+
+    case RIB_TABLES_ITER_S_ITERATING:
+      iter->afi_safi_ix++;
+      while (1)
+       {
+
+         while (iter->afi_safi_ix < (int) ZEBRA_NUM_OF (afi_safis))
+           {
+             table = zebra_vrf_table (afi_safis[iter->afi_safi_ix].afi,
+                                afi_safis[iter->afi_safi_ix].safi,
+                                iter->vrf_id);
+             if (table)
+               break;
+
+             iter->afi_safi_ix++;
+           }
+
+         /*
+          * Found another table in this vrf.
+          */
+         if (table)
+           break;
+
+         /*
+          * Done with all tables in the current vrf, go to the next
+          * one.
+          */
+         if (!vrf_id_get_next (iter->vrf_id, &iter->vrf_id))
+           break;
+
+         iter->afi_safi_ix = 0;
+       }
+
+      break;
+
+    case RIB_TABLES_ITER_S_DONE:
+      return NULL;
+    }
+
+  if (table)
+    iter->state = RIB_TABLES_ITER_S_ITERATING;
+  else
+    iter->state = RIB_TABLES_ITER_S_DONE;
+
+  return table;
+}
+
+/* Lookup VRF by identifier.  */
+struct zebra_vrf *
+zebra_vrf_lookup (vrf_id_t vrf_id)
+{
+  return vrf_info_lookup (vrf_id);
+}
+
+/*
+ * Create a routing table for the specific AFI/SAFI in the given VRF.
+ */
+static void
+zebra_vrf_table_create (struct zebra_vrf *zvrf, afi_t afi, safi_t safi)
+{
+  rib_table_info_t *info;
+  struct route_table *table;
+
+  assert (!zvrf->table[afi][safi]);
+
+  table = route_table_init ();
+  zvrf->table[afi][safi] = table;
+
+  info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info));
+  info->zvrf = zvrf;
+  info->afi = afi;
+  info->safi = safi;
+  table->info = info;
+}
+
+/* Allocate new zebra VRF. */
+struct zebra_vrf *
+zebra_vrf_alloc (vrf_id_t vrf_id)
+{
+  struct zebra_vrf *zvrf;
+#ifdef HAVE_NETLINK
+  char nl_name[64];
+#endif
+
+  zvrf = XCALLOC (MTYPE_ZEBRA_VRF, sizeof (struct zebra_vrf));
+
+  /* Allocate routing table and static table.  */
+  zebra_vrf_table_create (zvrf, AFI_IP, SAFI_UNICAST);
+  zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_UNICAST);
+  zvrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init ();
+  zvrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init ();
+  zebra_vrf_table_create (zvrf, AFI_IP, SAFI_MULTICAST);
+  zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_MULTICAST);
+  zvrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init ();
+  zvrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
+
+  zvrf->rnh_table[AFI_IP] = route_table_init();
+  zvrf->rnh_table[AFI_IP6] = route_table_init();
+
+  /* Set VRF ID */
+  zvrf->vrf_id = vrf_id;
+
+#ifdef HAVE_NETLINK
+  /* Initialize netlink sockets */
+  snprintf (nl_name, 64, "netlink-listen (vrf %u)", vrf_id);
+  zvrf->netlink.sock = -1;
+  zvrf->netlink.name = XSTRDUP (MTYPE_NETLINK_NAME, nl_name);
+
+  snprintf (nl_name, 64, "netlink-cmd (vrf %u)", vrf_id);
+  zvrf->netlink_cmd.sock = -1;
+  zvrf->netlink_cmd.name = XSTRDUP (MTYPE_NETLINK_NAME, nl_name);
+#endif
+
+  return zvrf;
+}
+
+/* Lookup the routing table in an enabled VRF. */
+struct route_table *
+zebra_vrf_table (afi_t afi, safi_t safi, vrf_id_t vrf_id)
+{
+  struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id);
+
+  if (!zvrf)
+    return NULL;
+
+  if (afi >= AFI_MAX || safi >= SAFI_MAX)
+    return NULL;
+
+  return zvrf->table[afi][safi];
+}
+
+/* Lookup the static routing table in a VRF. */
+struct route_table *
+zebra_vrf_static_table (afi_t afi, safi_t safi, vrf_id_t vrf_id)
+{
+  struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id);
+
+  if (!zvrf)
+    return NULL;
+
+  if (afi >= AFI_MAX || safi >= SAFI_MAX)
+    return NULL;
+
+  return zvrf->stable[afi][safi];
+}
+
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
new file mode 100644 (file)
index 0000000..859b6d7
--- /dev/null
@@ -0,0 +1,629 @@
+/* Zebra next hop tracking code
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "str.h"
+#include "command.h"
+#include "if.h"
+#include "log.h"
+#include "sockunion.h"
+#include "linklist.h"
+#include "thread.h"
+#include "workqueue.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "stream.h"
+#include "nexthop.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_rnh.h"
+
+#define lookup_rnh_table(v, f)                  \
+({                                              \
+  struct zebra_vrf *zvrf;                        \
+  struct route_table *t = NULL;                  \
+  zvrf = zebra_vrf_lookup(v);                    \
+  if (zvrf)                                      \
+    t = zvrf->rnh_table[family2afi(f)];                 \
+  t;                                             \
+})
+
+static void free_state(struct rib *rib);
+static void copy_state(struct rnh *rnh, struct rib *rib);
+static int compare_state(struct rib *r1, struct rib *r2);
+static int send_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id);
+static void print_rnh(struct route_node *rn, struct vty *vty);
+
+char *
+rnh_str (struct rnh *rnh, char *buf, int size)
+{
+  prefix2str(&(rnh->node->p), buf, size);
+  return buf;
+}
+
+struct rnh *
+zebra_add_rnh (struct prefix *p, vrf_id_t vrfid)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rnh *rnh = NULL;
+
+  if (IS_ZEBRA_DEBUG_NHT)
+    {
+      char buf[INET6_ADDRSTRLEN];
+      prefix2str(p, buf, INET6_ADDRSTRLEN);
+      zlog_debug("add rnh %s in vrf %d", buf, vrfid);
+    }
+  table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p));
+  if (!table)
+    {
+      zlog_debug("add_rnh: rnh table not found\n");
+      return NULL;
+    }
+
+  /* Make it sure prefixlen is applied to the prefix. */
+  apply_mask (p);
+
+  /* Lookup (or add) route node.*/
+  rn = route_node_get (table, p);
+
+  if (!rn->info)
+    {
+      rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
+      rnh->client_list = list_new();
+      route_lock_node (rn);
+      rn->info = rnh;
+      rnh->node = rn;
+    }
+
+  route_unlock_node (rn);
+  return (rn->info);
+}
+
+struct rnh *
+zebra_lookup_rnh (struct prefix *p, vrf_id_t vrfid)
+{
+  struct route_table *table;
+  struct route_node *rn;
+
+  table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p));
+  if (!table)
+    return NULL;
+
+  /* Make it sure prefixlen is applied to the prefix. */
+  apply_mask (p);
+
+  /* Lookup route node.*/
+  rn = route_node_lookup (table, p);
+  if (!rn)
+    return NULL;
+
+  route_unlock_node (rn);
+  return (rn->info);
+}
+
+void
+zebra_delete_rnh (struct rnh *rnh)
+{
+  struct route_node *rn;
+
+  if (!rnh || !(rn = rnh->node))
+    return;
+
+  if (IS_ZEBRA_DEBUG_NHT)
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("delete rnh %s", rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+    }
+
+  list_free(rnh->client_list);
+  free_state(rnh->state);
+  XFREE(MTYPE_RNH, rn->info);
+  rn->info = NULL;
+  route_unlock_node (rn);
+  return;
+}
+
+void
+zebra_add_rnh_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id)
+{
+  if (IS_ZEBRA_DEBUG_NHT)
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("client %s registers rnh %s",
+                zebra_route_string(client->proto),
+                rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+    }
+  if (!listnode_lookup(rnh->client_list, client))
+    {
+      listnode_add(rnh->client_list, client);
+      send_client(rnh, client, vrf_id);
+    }
+}
+
+void
+zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client)
+{
+  if (IS_ZEBRA_DEBUG_NHT)
+    {
+      char buf[INET6_ADDRSTRLEN];
+      zlog_debug("client %s unregisters rnh %s",
+                zebra_route_string(client->proto),
+                rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+    }
+  listnode_delete(rnh->client_list, client);
+  if (list_isempty(rnh->client_list))
+    zebra_delete_rnh(rnh);
+}
+
+int
+zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
+{
+  struct route_table *ptable;
+  struct route_table *ntable;
+  struct route_node *prn;
+  struct route_node *nrn;
+  struct rnh *rnh;
+  struct zserv *client;
+  struct listnode *node;
+  struct rib *rib;
+
+  ntable = lookup_rnh_table(vrfid, family);
+  if (!ntable)
+    {
+      zlog_debug("evaluate_rnh_table: rnh table not found\n");
+      return -1;
+    }
+
+  ptable = zebra_vrf_table(family2afi(family), SAFI_UNICAST, vrfid);
+  if (!ptable)
+    {
+      zlog_debug("evaluate_rnh_table: prefix table not found\n");
+      return -1;
+    }
+
+  for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+    {
+      if (!nrn->info)
+         continue;
+      
+      rnh = nrn->info;
+      prn = route_node_match(ptable, &nrn->p);
+      if (!prn)
+       rib = NULL;
+      else
+       {
+         RNODE_FOREACH_RIB(prn, rib)
+           {
+             if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+               continue;
+             if (! CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+               continue;
+
+             if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
+               {
+                 if (rib->type == ZEBRA_ROUTE_CONNECT)
+                   break;
+
+                 if (rib->type == ZEBRA_ROUTE_NHRP)
+                   {
+                     struct nexthop *nexthop;
+                     for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+                       if (nexthop->type == NEXTHOP_TYPE_IFINDEX ||
+                           nexthop->type == NEXTHOP_TYPE_IFNAME)
+                         break;
+                     if (nexthop)
+                       break;
+                   }
+               }
+             else
+               break;
+           }
+       }
+
+      if (compare_state(rib, rnh->state))
+       {
+         if (IS_ZEBRA_DEBUG_NHT)
+           {
+             char bufn[INET6_ADDRSTRLEN];
+             char bufp[INET6_ADDRSTRLEN];
+             prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+             if (prn)
+               prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN);
+             else
+               strcpy(bufp, "null");
+             zlog_debug("rnh %s resolved through route %s - sending "
+                        "nexthop %s event to clients", bufn, bufp,
+                        rib ? "reachable" : "unreachable");
+           }
+         copy_state(rnh, rib);
+         for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+           send_client(rnh, client, vrfid);
+       }
+    }
+  return 1;
+}
+
+int
+zebra_dispatch_rnh_table (vrf_id_t vrfid, int family, struct zserv *client)
+{
+  struct route_table *ntable;
+  struct route_node *nrn;
+  struct rnh *rnh;
+
+  ntable = lookup_rnh_table(vrfid, family);
+  if (!ntable)
+    {
+      zlog_debug("dispatch_rnh_table: rnh table not found\n");
+      return -1;
+    }
+
+  for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+    {
+      if (!nrn->info)
+         continue;
+
+      rnh = nrn->info;
+      if (IS_ZEBRA_DEBUG_NHT)
+       {
+         char bufn[INET6_ADDRSTRLEN];
+         prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+         zlog_debug("rnh %s - sending nexthop %s event to client %s", bufn,
+                    rnh->state ? "reachable" : "unreachable",
+                    zebra_route_string(client->proto));
+       }
+      send_client(rnh, client, vrfid);
+    }
+  return 1;
+}
+
+void
+zebra_print_rnh_table (vrf_id_t vrfid, int af, struct vty *vty)
+{
+  struct route_table *table;
+  struct route_node *rn;
+
+  table = lookup_rnh_table(vrfid, af);
+  if (!table)
+    {
+      zlog_debug("print_rnhs: rnh table not found\n");
+      return;
+    }
+
+  for (rn = route_top(table); rn; rn = route_next(rn))
+      if (rn->info)
+       print_rnh(rn, vty);
+}
+
+int
+zebra_cleanup_rnh_client (vrf_id_t vrfid, int family, struct zserv *client)
+{
+  struct route_table *ntable;
+  struct route_node *nrn;
+  struct rnh *rnh;
+
+  ntable = lookup_rnh_table(vrfid, family);
+  if (!ntable)
+    {
+      zlog_debug("cleanup_rnh_client: rnh table not found\n");
+      return -1;
+    }
+
+  for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+    {
+      if (!nrn->info)
+         continue;
+
+      rnh = nrn->info;
+      if (IS_ZEBRA_DEBUG_NHT)
+       {
+         char bufn[INET6_ADDRSTRLEN];
+         prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+         zlog_debug("rnh %s - cleaning state for client %s", bufn,
+                    zebra_route_string(client->proto));
+       }
+      zebra_remove_rnh_client(rnh, client);
+    }
+  return 1;
+}
+
+/**
+ * free_state - free up the rib structure associated with the rnh.
+ */
+static void
+free_state (struct rib *rib)
+{
+  struct nexthop *nexthop, *next;
+
+  if (!rib)
+    return;
+
+  /* free RIB and nexthops */
+  for (nexthop = rib->nexthop; nexthop; nexthop = next)
+    {
+      next = nexthop->next;
+      nexthop_free (nexthop);
+    }
+  XFREE (MTYPE_RIB, rib);
+}
+
+/**
+ * copy_nexthop - copy a nexthop to the rib structure.
+ */
+static void
+rib_copy_nexthop (struct rib *state, struct nexthop *nh)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new();
+  nexthop->flags = nh->flags;
+  nexthop->type = nh->type;
+  nexthop->ifindex = nh->ifindex;
+  if (nh->ifname)
+    nexthop->ifname = XSTRDUP(0, nh->ifname);
+  memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+  memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+
+  rib_nexthop_add(state, nexthop);
+}
+
+static void
+copy_state (struct rnh *rnh, struct rib *rib)
+{
+  struct rib *state;
+  struct nexthop *nh;
+
+  if (rnh->state)
+    {
+      free_state(rnh->state);
+      rnh->state = NULL;
+    }
+
+  if (!rib)
+    return;
+
+  state = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+  state->type = rib->type;
+  state->metric = rib->metric;
+
+  for (nh = rib->nexthop; nh; nh = nh->next)
+    rib_copy_nexthop(state, nh);
+  rnh->state = state;
+}
+
+static int
+compare_state (struct rib *r1, struct rib *r2)
+{
+  struct nexthop *nh1;
+  struct nexthop *nh2;
+  u_char found_nh = 0;
+
+  if (!r1 && !r2)
+    return 0;
+
+  if ((!r1 && r2) || (r1 && !r2))
+      return 1;
+
+  if (r1->metric != r2->metric)
+      return 1;
+
+  if (r1->nexthop_num != r2->nexthop_num)
+      return 1;
+
+  /* We need to verify that the nexthops for r1 match the nexthops for r2.
+   * Since it is possible for a rib entry to have the same nexthop multiple
+   * times (Example: [a,a]) we need to keep track of which r2 nexthops we have
+   * already used as a match against a r1 nexthop.  We track this
+   * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you
+   * are finished.
+   *
+   * TRUE:  r1 [a,b], r2 [a,b]
+   * TRUE:  r1 [a,b], r2 [b,a]
+   * FALSE: r1 [a,b], r2 [a,c]
+   * FALSE: r1 [a,a], r2 [a,b]
+   */
+  for (nh1 = r1->nexthop; nh1; nh1 = nh1->next)
+    {
+      found_nh = 0;
+      for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+        {
+          if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+            continue;
+
+          if (nexthop_same_no_recurse(nh1, nh2))
+            {
+              SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+              found_nh = 1;
+              break;
+            }
+        }
+
+      if (!found_nh)
+        {
+          for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+            if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+              UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+          return 1;
+        }
+    }
+
+  for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+    if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+      UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+
+  return 0;
+}
+
+static int
+send_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct rib *rib;
+  unsigned long nump;
+  u_char num;
+  struct nexthop *nexthop;
+  struct route_node *rn;
+
+  rn = rnh->node;
+  rib = rnh->state;
+
+  /* Get output stream. */
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_NEXTHOP_UPDATE, vrf_id);
+
+  stream_putw(s, rn->p.family);
+  stream_put_prefix (s, &rn->p);
+
+  if (rib)
+    {
+      stream_putl (s, rib->metric);
+      num = 0;
+      nump = stream_get_endp(s);
+      stream_putc (s, 0);
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) &&
+            ! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) &&
+           CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         {
+           stream_putc (s, nexthop->type);
+           switch (nexthop->type)
+             {
+             case ZEBRA_NEXTHOP_IPV4:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               break;
+             case ZEBRA_NEXTHOP_IFINDEX:
+             case ZEBRA_NEXTHOP_IFNAME:
+               stream_putl (s, nexthop->ifindex);
+               break;
+             case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+             case ZEBRA_NEXTHOP_IPV4_IFNAME:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               stream_putl (s, nexthop->ifindex);
+               break;
+#ifdef HAVE_IPV6
+             case ZEBRA_NEXTHOP_IPV6:
+               stream_put (s, &nexthop->gate.ipv6, 16);
+               break;
+             case ZEBRA_NEXTHOP_IPV6_IFINDEX:
+             case ZEBRA_NEXTHOP_IPV6_IFNAME:
+               stream_put (s, &nexthop->gate.ipv6, 16);
+               stream_putl (s, nexthop->ifindex);
+               break;
+#endif /* HAVE_IPV6 */
+             default:
+                /* do nothing */
+               break;
+             }
+           num++;
+         }
+      stream_putc_at (s, nump, num);
+    }
+  else
+    {
+      stream_putl (s, 0);
+      stream_putc (s, 0);
+    }
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  client->nh_last_upd_time = quagga_time(NULL);
+  client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE;
+  return zebra_server_send_message(client);
+}
+
+static void
+print_nh (struct nexthop *nexthop, struct vty *vty)
+{
+  char buf[BUFSIZ];
+
+  switch (nexthop->type)
+    {
+    case NEXTHOP_TYPE_IPV4:
+    case NEXTHOP_TYPE_IPV4_IFINDEX:
+      vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4));
+      if (nexthop->ifindex)
+       vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex));
+      break;
+    case NEXTHOP_TYPE_IPV6:
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+    case NEXTHOP_TYPE_IPV6_IFNAME:
+      vty_out (vty, " %s",
+              inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+      if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+       vty_out (vty, ", %s", nexthop->ifname);
+      else if (nexthop->ifindex)
+       vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex));
+      break;
+    case NEXTHOP_TYPE_IFINDEX:
+      vty_out (vty, " is directly connected, %s",
+              ifindex2ifname (nexthop->ifindex));
+      break;
+    case NEXTHOP_TYPE_IFNAME:
+      vty_out (vty, " is directly connected, %s", nexthop->ifname);
+      break;
+    case NEXTHOP_TYPE_BLACKHOLE:
+      vty_out (vty, " is directly connected, Null0");
+      break;
+    default:
+      break;
+    }
+  vty_out(vty, "%s", VTY_NEWLINE);
+}
+
+static void
+print_rnh (struct route_node *rn, struct vty *vty)
+{
+  struct rnh *rnh;
+  struct nexthop *nexthop;
+  struct listnode *node;
+  struct zserv *client;
+  char buf[BUFSIZ];
+
+  rnh = rn->info;
+  vty_out(vty, "%s%s", inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
+         VTY_NEWLINE);
+  if (rnh->state)
+    {
+      vty_out(vty, " resolved via %s%s",
+             zebra_route_string(rnh->state->type), VTY_NEWLINE);
+      for (nexthop = rnh->state->nexthop; nexthop; nexthop = nexthop->next)
+       print_nh(nexthop, vty);
+    }
+  else
+    vty_out(vty, " unresolved%s%s",
+           CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED) ? "(Connected)" : "",
+           VTY_NEWLINE);
+
+  vty_out(vty, " Client list:");
+  for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+    vty_out(vty, " %s(fd %d)", zebra_route_string(client->proto),
+           client->sock);
+  vty_out(vty, "%s", VTY_NEWLINE);
+}
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
new file mode 100644 (file)
index 0000000..574c95f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Zebra next hop tracking header
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_RNH_H
+#define _ZEBRA_RNH_H
+
+#include "prefix.h"
+#include "vty.h"
+
+/* Nexthop structure. */
+struct rnh
+{
+  u_char flags;
+#define ZEBRA_NHT_CONNECTED    0x1
+  struct rib *state;
+  struct list *client_list;
+  struct route_node *node;
+};
+
+extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid);
+extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid);
+extern void zebra_delete_rnh(struct rnh *rnh);
+extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id_t);
+extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client);
+extern int zebra_evaluate_rnh_table(vrf_id_t vrfid, int family);
+extern int zebra_dispatch_rnh_table(vrf_id_t vrfid, int family, struct zserv *cl);
+extern void zebra_print_rnh_table(vrf_id_t vrfid, int family, struct vty *vty);
+extern char *rnh_str(struct rnh *rnh, char *buf, int size);
+extern int zebra_cleanup_rnh_client(vrf_id_t vrf, int family, struct zserv *client);
+#endif /*_ZEBRA_RNH_H */
diff --git a/zebra/zebra_rnh_null.c b/zebra/zebra_rnh_null.c
new file mode 100644 (file)
index 0000000..664667d
--- /dev/null
@@ -0,0 +1,10 @@
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_rnh.h"
+
+int zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
+{ return 0; }
+
+void zebra_print_rnh_table (vrf_id_t vrfid, int family, struct vty *vty)
+{}
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
new file mode 100644 (file)
index 0000000..5a6a96b
--- /dev/null
@@ -0,0 +1,715 @@
+/* zebra routemap.
+ * Copyright (C) 2006 IBM Corporation
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "rib.h"
+#include "routemap.h"
+#include "command.h"
+#include "filter.h"
+#include "plist.h"
+#include "vrf.h"
+#include "nexthop.h"
+
+#include "zebra/zserv.h"
+
+/* Add zebra route map rule */
+static int
+zebra_route_match_add(struct vty *vty, struct route_map_index *index,
+                     const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete zebra route map rule. */
+static int
+zebra_route_match_delete (struct vty *vty, struct route_map_index *index,
+                       const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Add zebra route map rule. */
+static int
+zebra_route_set_add (struct vty *vty, struct route_map_index *index,
+                  const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+/* Delete zebra route map rule. */
+static int
+zebra_route_set_delete (struct vty *vty, struct route_map_index *index,
+                     const char *command, const char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+       {
+       case RMAP_RULE_MISSING:
+         vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       case RMAP_COMPILE_ERROR:
+         vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+    }
+  return CMD_SUCCESS;
+}
+
+
+/* `match interface IFNAME' */
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_interface (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct nexthop_vrfid *nh_vrf;
+  struct nexthop *nexthop;
+  char *ifname = rule;
+  ifindex_t ifindex;
+
+  if (type == RMAP_ZEBRA)
+    {
+      if (strcasecmp(ifname, "any") == 0)
+       return RMAP_MATCH;
+      nh_vrf = object;
+      if (!nh_vrf)
+       return RMAP_NOMATCH;
+      ifindex = ifname2ifindex_vrf (ifname, nh_vrf->vrf_id);
+      if (ifindex == 0)
+       return RMAP_NOMATCH;
+      nexthop = nh_vrf->nexthop;
+      if (!nexthop)
+       return RMAP_NOMATCH;
+      if (nexthop->ifindex == ifindex)
+       return RMAP_MATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+static void *
+route_match_interface_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `match interface' value. */
+static void
+route_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for interface matching */
+struct route_map_rule_cmd route_match_interface_cmd =
+{
+   "interface",
+   route_match_interface,
+   route_match_interface_compile,
+   route_match_interface_free
+};
+
+DEFUN (match_interface,
+       match_interface_cmd,
+       "match interface WORD",
+       MATCH_STR
+       "match first hop interface of route\n"
+       "Interface name\n")
+{
+  return zebra_route_match_add (vty, vty->index, "interface", argv[0]);
+}
+
+DEFUN (no_match_interface,
+       no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n")
+{
+  if (argc == 0)
+    return zebra_route_match_delete (vty, vty->index, "interface", NULL);
+
+  return zebra_route_match_delete (vty, vty->index, "interface", argv[0]);
+}
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+DEFUN (match_ip_next_hop,
+       match_ip_next_hop_cmd,
+       "match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+{
+  return zebra_route_match_add (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop,
+       no_match_ip_next_hop_cmd,
+       "no match ip next-hop",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n")
+{
+  if (argc == 0)
+    return zebra_route_match_delete (vty, vty->index, "ip next-hop", NULL);
+
+  return zebra_route_match_delete (vty, vty->index, "ip next-hop", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop,
+       no_match_ip_next_hop_val_cmd,
+       "no match ip next-hop (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+DEFUN (match_ip_next_hop_prefix_list,
+       match_ip_next_hop_prefix_list_cmd,
+       "match ip next-hop prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return zebra_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_cmd,
+       "no match ip next-hop prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return zebra_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL);
+
+  return zebra_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_next_hop_prefix_list,
+       no_match_ip_next_hop_prefix_list_val_cmd,
+       "no match ip next-hop prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match next-hop address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+DEFUN (match_ip_address,
+       match_ip_address_cmd,
+       "match ip address (<1-199>|<1300-2699>|WORD)",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+{
+  return zebra_route_match_add (vty, vty->index, "ip address", argv[0]);
+}
+
+DEFUN (no_match_ip_address, 
+       no_match_ip_address_cmd,
+       "no match ip address",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n")
+{
+  if (argc == 0)
+    return zebra_route_match_delete (vty, vty->index, "ip address", NULL);
+
+  return zebra_route_match_delete (vty, vty->index, "ip address", argv[0]);
+}
+
+ALIAS (no_match_ip_address,
+       no_match_ip_address_val_cmd,
+       "no match ip address (<1-199>|<1300-2699>|WORD)",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "IP access-list number\n"
+       "IP access-list number (expanded range)\n"
+       "IP Access-list name\n")
+
+DEFUN (match_ip_address_prefix_list, 
+       match_ip_address_prefix_list_cmd,
+       "match ip address prefix-list WORD",
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+{
+  return zebra_route_match_add (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+DEFUN (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_cmd,
+       "no match ip address prefix-list",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n")
+{
+  if (argc == 0)
+    return zebra_route_match_delete (vty, vty->index, "ip address prefix-list", NULL);
+
+  return zebra_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]);
+}
+
+ALIAS (no_match_ip_address_prefix_list,
+       no_match_ip_address_prefix_list_val_cmd,
+       "no match ip address prefix-list WORD",
+       NO_STR
+       MATCH_STR
+       IP_STR
+       "Match address of route\n"
+       "Match entries of prefix-lists\n"
+       "IP prefix-list name\n")
+
+/* set functions */
+
+DEFUN (set_src,
+       set_src_cmd,
+       "set src A.B.C.D",
+       SET_STR
+       "src address for route\n"
+       "src address\n")
+{
+  struct in_addr src;
+  struct interface *pif = NULL;
+  vrf_iter_t iter;
+
+  if (inet_pton(AF_INET, argv[0], &src) <= 0)
+    {
+      vty_out (vty, "%% not a local address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((pif = if_lookup_exact_address_vrf (src, vrf_iter2id (iter))) != NULL)
+      break;
+
+  if (!pif)
+    {
+      vty_out (vty, "%% not a local address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return zebra_route_set_add (vty, vty->index, "src", argv[0]);
+}
+
+DEFUN (no_set_src,
+       no_set_src_cmd,
+       "no set src",
+       NO_STR
+       SET_STR
+       "Source address for route\n")
+{
+  if (argc == 0)
+    return zebra_route_set_delete (vty, vty->index, "src", NULL);
+
+  return zebra_route_set_delete (vty, vty->index, "src", argv[0]);
+}
+
+ALIAS (no_set_src,
+       no_set_src_val_cmd,
+       "no set src (A.B.C.D)",
+       NO_STR
+       SET_STR
+       "src address for route\n"
+       "src address\n")
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
+
+/* `match ip next-hop IP_ACCESS_LIST' */
+
+/* Match function return 1 if match is success else return zero. */
+static route_map_result_t
+route_match_ip_next_hop (void *rule, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+  struct nexthop *nexthop;
+  struct nexthop_vrfid *nh_vrf;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_ZEBRA)
+    {
+      nh_vrf = object;
+      nexthop = nh_vrf->nexthop;
+      switch (nexthop->type) {
+      case NEXTHOP_TYPE_IFINDEX:
+      case NEXTHOP_TYPE_IFNAME:
+        /* Interface routes can't match ip next-hop */
+        return RMAP_NOMATCH;
+      case NEXTHOP_TYPE_IPV4_IFINDEX:
+      case NEXTHOP_TYPE_IPV4_IFNAME:
+      case NEXTHOP_TYPE_IPV4:
+        p.family = AF_INET;
+        p.prefix = nexthop->gate.ipv4;
+        p.prefixlen = IPV4_MAX_BITLEN;
+        break;
+      default:
+        return RMAP_NOMATCH;
+      }
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+
+      return (access_list_apply (alist, &p) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip next-hop' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_next_hop_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `. */
+static void
+route_match_ip_next_hop_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip next-hop matching. */
+static struct route_map_rule_cmd route_match_ip_next_hop_cmd =
+{
+  "ip next-hop",
+  route_match_ip_next_hop,
+  route_match_ip_next_hop_compile,
+  route_match_ip_next_hop_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix,
+                                    route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+  struct nexthop *nexthop;
+  struct nexthop_vrfid *nh_vrf;
+  struct prefix_ipv4 p;
+
+  if (type == RMAP_ZEBRA)
+    {
+      nh_vrf = object;
+      nexthop = nh_vrf->nexthop;
+      switch (nexthop->type) {
+      case NEXTHOP_TYPE_IFINDEX:
+      case NEXTHOP_TYPE_IFNAME:
+        /* Interface routes can't match ip next-hop */
+        return RMAP_NOMATCH;
+      case NEXTHOP_TYPE_IPV4_IFINDEX:
+      case NEXTHOP_TYPE_IPV4_IFNAME:
+      case NEXTHOP_TYPE_IPV4:
+        p.family = AF_INET;
+        p.prefix = nexthop->gate.ipv4;
+        p.prefixlen = IPV4_MAX_BITLEN;
+        break;
+      default:
+        return RMAP_NOMATCH;
+      }
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+        return RMAP_NOMATCH;
+
+      return (prefix_list_apply (plist, &p) == PREFIX_DENY ?
+              RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_next_hop_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_next_hop_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd =
+{
+  "ip next-hop prefix-list",
+  route_match_ip_next_hop_prefix_list,
+  route_match_ip_next_hop_prefix_list_compile,
+  route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ip address IP_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+   zero. */
+static route_map_result_t
+route_match_ip_address (void *rule, struct prefix *prefix, 
+                       route_map_object_t type, void *object)
+{
+  struct access_list *alist;
+
+  if (type == RMAP_ZEBRA)
+    {
+      alist = access_list_lookup (AFI_IP, (char *) rule);
+      if (alist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (access_list_apply (alist, prefix) == FILTER_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `ip address' match statement.  `arg' should be
+   access-list name. */
+static void *
+route_match_ip_address_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void
+route_match_ip_address_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static struct route_map_rule_cmd route_match_ip_address_cmd =
+{
+  "ip address",
+  route_match_ip_address,
+  route_match_ip_address_compile,
+  route_match_ip_address_free
+};
+
+/* `match ip address prefix-list PREFIX_LIST' */
+
+static route_map_result_t
+route_match_ip_address_prefix_list (void *rule, struct prefix *prefix, 
+                                   route_map_object_t type, void *object)
+{
+  struct prefix_list *plist;
+
+  if (type == RMAP_ZEBRA)
+    {
+      plist = prefix_list_lookup (AFI_IP, (char *) rule);
+      if (plist == NULL)
+       return RMAP_NOMATCH;
+    
+      return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
+             RMAP_NOMATCH : RMAP_MATCH);
+    }
+  return RMAP_NOMATCH;
+}
+
+static void *
+route_match_ip_address_prefix_list_compile (const char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void
+route_match_ip_address_prefix_list_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd =
+{
+  "ip address prefix-list",
+  route_match_ip_address_prefix_list,
+  route_match_ip_address_prefix_list_compile,
+  route_match_ip_address_prefix_list_free
+};
+
+
+/* `set src A.B.C.D' */
+
+/* Set src. */
+static route_map_result_t
+route_set_src (void *rule, struct prefix *prefix, 
+                 route_map_object_t type, void *object)
+{
+  if (type == RMAP_ZEBRA)
+    {
+      struct nexthop_vrfid *nh_vrf;
+
+      nh_vrf = object;
+      nh_vrf->nexthop->src = *(union g_addr *)rule;
+    }
+  return RMAP_OKAY;
+}
+
+/* set src compilation. */
+static void *
+route_set_src_compile (const char *arg)
+{
+  union g_addr src, *psrc;
+
+  if (inet_pton(AF_INET, arg, &src.ipv4) != 1
+#ifdef HAVE_IPV6
+      && inet_pton(AF_INET6, arg, &src.ipv6) != 1
+#endif /* HAVE_IPV6 */
+     )
+    return NULL;
+
+  psrc = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union g_addr));
+  *psrc = src;
+
+  return psrc;
+}
+
+/* Free route map's compiled `set src' value. */
+static void
+route_set_src_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set src rule structure. */
+static struct route_map_rule_cmd route_set_src_cmd = 
+{
+  "src",
+  route_set_src,
+  route_set_src_compile,
+  route_set_src_free,
+};
+
+void
+zebra_route_map_init ()
+{
+  route_map_init ();
+  route_map_init_vty ();
+
+  route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_ip_next_hop_cmd);
+  route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd);
+  route_map_install_match (&route_match_ip_address_cmd);
+  route_map_install_match (&route_match_ip_address_prefix_list_cmd);
+/* */
+  route_map_install_set (&route_set_src_cmd);
+/* */
+  install_element (RMAP_NODE, &match_interface_cmd);
+  install_element (RMAP_NODE, &no_match_interface_cmd); 
+  install_element (RMAP_NODE, &no_match_interface_val_cmd); 
+  install_element (RMAP_NODE, &match_ip_next_hop_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd); 
+  install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd); 
+  install_element (RMAP_NODE, &match_ip_address_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_address_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_address_val_cmd); 
+  install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); 
+  install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd);
+/* */
+  install_element (RMAP_NODE, &set_src_cmd);
+  install_element (RMAP_NODE, &no_set_src_cmd);
+}
diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c
new file mode 100644 (file)
index 0000000..3d005aa
--- /dev/null
@@ -0,0 +1,582 @@
+/* FIB SNMP.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/*
+ * Currently SNMP is only running properly for MIBs in the default VRF.
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "smux.h"
+#include "table.h"
+#include "vrf.h"
+
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+
+#define IPFWMIB 1,3,6,1,2,1,4,24
+
+/* ipForwardTable */
+#define IPFORWARDDEST                         1
+#define IPFORWARDMASK                         2
+#define IPFORWARDPOLICY                       3
+#define IPFORWARDNEXTHOP                      4
+#define IPFORWARDIFINDEX                      5
+#define IPFORWARDTYPE                         6
+#define IPFORWARDPROTO                        7
+#define IPFORWARDAGE                          8
+#define IPFORWARDINFO                         9
+#define IPFORWARDNEXTHOPAS                   10
+#define IPFORWARDMETRIC1                     11
+#define IPFORWARDMETRIC2                     12
+#define IPFORWARDMETRIC3                     13
+#define IPFORWARDMETRIC4                     14
+#define IPFORWARDMETRIC5                     15
+
+/* ipCidrRouteTable */
+#define IPCIDRROUTEDEST                       1
+#define IPCIDRROUTEMASK                       2
+#define IPCIDRROUTETOS                        3
+#define IPCIDRROUTENEXTHOP                    4
+#define IPCIDRROUTEIFINDEX                    5
+#define IPCIDRROUTETYPE                       6
+#define IPCIDRROUTEPROTO                      7
+#define IPCIDRROUTEAGE                        8
+#define IPCIDRROUTEINFO                       9
+#define IPCIDRROUTENEXTHOPAS                 10
+#define IPCIDRROUTEMETRIC1                   11
+#define IPCIDRROUTEMETRIC2                   12
+#define IPCIDRROUTEMETRIC3                   13
+#define IPCIDRROUTEMETRIC4                   14
+#define IPCIDRROUTEMETRIC5                   15
+#define IPCIDRROUTESTATUS                    16
+
+#define INTEGER32 ASN_INTEGER
+#define GAUGE32 ASN_GAUGE
+#define ENUMERATION ASN_INTEGER
+#define ROWSTATUS ASN_INTEGER
+#define IPADDRESS ASN_IPADDRESS
+#define OBJECTIDENTIFIER ASN_OBJECT_ID
+
+extern struct zebra_t zebrad;
+
+oid ipfw_oid [] = { IPFWMIB };
+
+/* Hook functions. */
+static u_char * ipFwNumber (struct variable *, oid [], size_t *,
+                    int, size_t *, WriteMethod **);
+static u_char * ipFwTable (struct variable *, oid [], size_t *,
+                          int, size_t *, WriteMethod **);
+static u_char * ipCidrNumber (struct variable *, oid [], size_t *,
+                             int, size_t *, WriteMethod **);
+static u_char * ipCidrTable (struct variable *, oid [], size_t *,
+                            int, size_t *, WriteMethod **);
+
+struct variable zebra_variables[] = 
+  {
+    {0, GAUGE32, RONLY, ipFwNumber, 1, {1}},
+    {IPFORWARDDEST, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 1}},
+    {IPFORWARDMASK, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 2}},
+    {IPFORWARDPOLICY, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 3}},
+    {IPFORWARDNEXTHOP, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 4}},
+    {IPFORWARDIFINDEX, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 5}},
+    {IPFORWARDTYPE, ENUMERATION, RONLY, ipFwTable, 3, {2, 1, 6}},
+    {IPFORWARDPROTO, ENUMERATION, RONLY, ipFwTable, 3, {2, 1, 7}},
+    {IPFORWARDAGE, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 8}},
+    {IPFORWARDINFO, OBJECTIDENTIFIER, RONLY, ipFwTable, 3, {2, 1, 9}},
+    {IPFORWARDNEXTHOPAS, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 10}},
+    {IPFORWARDMETRIC1, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 11}},
+    {IPFORWARDMETRIC2, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 12}},
+    {IPFORWARDMETRIC3, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 13}},
+    {IPFORWARDMETRIC4, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 14}},
+    {IPFORWARDMETRIC5, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 15}},
+    {0, GAUGE32, RONLY, ipCidrNumber, 1, {3}},
+    {IPCIDRROUTEDEST, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 1}},
+    {IPCIDRROUTEMASK, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 2}},
+    {IPCIDRROUTETOS, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 3}},
+    {IPCIDRROUTENEXTHOP, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 4}},
+    {IPCIDRROUTEIFINDEX, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 5}},
+    {IPCIDRROUTETYPE, ENUMERATION, RONLY, ipCidrTable, 3, {4, 1, 6}},
+    {IPCIDRROUTEPROTO, ENUMERATION, RONLY, ipCidrTable, 3, {4, 1, 7}},
+    {IPCIDRROUTEAGE, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 8}},
+    {IPCIDRROUTEINFO, OBJECTIDENTIFIER, RONLY, ipCidrTable, 3, {4, 1, 9}},
+    {IPCIDRROUTENEXTHOPAS, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 10}},
+    {IPCIDRROUTEMETRIC1, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 11}},
+    {IPCIDRROUTEMETRIC2, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 12}},
+    {IPCIDRROUTEMETRIC3, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 13}},
+    {IPCIDRROUTEMETRIC4, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 14}},
+    {IPCIDRROUTEMETRIC5, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 15}},
+    {IPCIDRROUTESTATUS, ROWSTATUS, RONLY, ipCidrTable, 3, {4, 1, 16}}
+  };
+
+
+static u_char *
+ipFwNumber (struct variable *v, oid objid[], size_t *objid_len,
+           int exact, size_t *val_len, WriteMethod **write_method)
+{
+  static int result;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+
+  if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED)
+    return NULL;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (! table)
+    return NULL;
+
+  /* Return number of routing entries. */
+  result = 0;
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      result++;
+
+  return (u_char *)&result;
+}
+
+static u_char *
+ipCidrNumber (struct variable *v, oid objid[], size_t *objid_len,
+             int exact, size_t *val_len, WriteMethod **write_method)
+{
+  static int result;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+
+  if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED)
+    return NULL;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (! table)
+    return 0;
+
+  /* Return number of routing entries. */
+  result = 0;
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      result++;
+
+  return (u_char *)&result;
+}
+
+static int
+in_addr_cmp(u_char *p1, u_char *p2)
+{
+  int i;
+
+  for (i=0; i<4; i++)
+    {
+      if (*p1 < *p2)
+        return -1;
+      if (*p1 > *p2)
+        return 1;
+      p1++; p2++;
+    }
+  return 0;
+}
+
+static int 
+in_addr_add(u_char *p, int num)
+{
+  int i, ip0;
+
+  ip0 = *p;
+  p += 4;
+  for (i = 3; 0 <= i; i--) {
+    p--;
+    if (*p + num > 255) {
+      *p += num;
+      num = 1;
+    } else {
+      *p += num;
+      return 1;
+    }
+  }
+  if (ip0 > *p) {
+    /* ip + num > 0xffffffff */
+    return 0;
+  }
+  
+  return 1;
+}
+
+static int
+proto_trans(int type)
+{
+  switch (type)
+    {
+    case ZEBRA_ROUTE_SYSTEM:
+      return 1; /* other */
+    case ZEBRA_ROUTE_KERNEL:
+      return 1; /* other */
+    case ZEBRA_ROUTE_CONNECT:
+      return 2; /* local interface */
+    case ZEBRA_ROUTE_STATIC:
+      return 3; /* static route */
+    case ZEBRA_ROUTE_RIP:
+      return 8; /* rip */
+    case ZEBRA_ROUTE_RIPNG:
+      return 1; /* shouldn't happen */
+    case ZEBRA_ROUTE_OSPF:
+      return 13; /* ospf */
+    case ZEBRA_ROUTE_OSPF6:
+      return 1; /* shouldn't happen */
+    case ZEBRA_ROUTE_BGP:
+      return 14; /* bgp */
+    default:
+      return 1; /* other */
+    }
+}
+
+static void
+check_replace(struct route_node *np2, struct rib *rib2, 
+              struct route_node **np, struct rib **rib)
+{
+  int proto, proto2;
+
+  if (!*np)
+    {
+      *np = np2;
+      *rib = rib2;
+      return;
+    }
+
+  if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) < 0)
+    return;
+  if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) > 0)
+    {
+      *np = np2;
+      *rib = rib2;
+      return;
+    }
+
+  proto = proto_trans((*rib)->type);
+  proto2 = proto_trans(rib2->type);
+
+  if (proto2 > proto)
+    return;
+  if (proto2 < proto)
+    {
+      *np = np2;
+      *rib = rib2;
+      return;
+    }
+
+  if (in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4, 
+                  (u_char *)&rib2->nexthop->gate.ipv4) <= 0)
+    return;
+
+  *np = np2;
+  *rib = rib2;
+  return;
+}
+
+static void
+get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, 
+                      int exact, struct route_node **np, struct rib **rib)
+{
+  struct in_addr dest;
+  struct route_table *table;
+  struct route_node *np2;
+  struct rib *rib2;
+  int proto;
+  int policy;
+  struct in_addr nexthop;
+  u_char *pnt;
+  int i;
+
+  /* Init index variables */
+
+  pnt = (u_char *) &dest;
+  for (i = 0; i < 4; i++)
+    *pnt++ = 0;
+
+  pnt = (u_char *) &nexthop;
+  for (i = 0; i < 4; i++)
+    *pnt++ = 0;
+
+  proto = 0;
+  policy = 0;
+  /* Init return variables */
+
+  *np = NULL;
+  *rib = NULL;
+
+  /* Short circuit exact matches of wrong length */
+
+  if (exact && (*objid_len != (unsigned) v->namelen + 10))
+    return;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (! table)
+    return;
+
+  /* Get INDEX information out of OID.
+   * ipForwardDest, ipForwardProto, ipForwardPolicy, ipForwardNextHop
+   */
+
+  if (*objid_len > (unsigned) v->namelen)
+    oid2in_addr (objid + v->namelen, MIN(4, *objid_len - v->namelen), &dest);
+
+  if (*objid_len > (unsigned) v->namelen + 4)
+    proto = objid[v->namelen + 4];
+
+  if (*objid_len > (unsigned) v->namelen + 5)
+    policy = objid[v->namelen + 5];
+
+  if (*objid_len > (unsigned) v->namelen + 6)
+    oid2in_addr (objid + v->namelen + 6, MIN(4, *objid_len - v->namelen - 6),
+                &nexthop);
+
+  /* Apply GETNEXT on not exact search */
+
+  if (!exact && (*objid_len >= (unsigned) v->namelen + 10))
+    {
+      if (! in_addr_add((u_char *) &nexthop, 1)) 
+        return;
+    }
+
+  /* For exact: search matching entry in rib table. */
+
+  if (exact)
+    {
+      if (policy) /* Not supported (yet?) */
+        return;
+      for (*np = route_top (table); *np; *np = route_next (*np))
+       {
+         if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest))
+           {
+             RNODE_FOREACH_RIB (*np, *rib)
+               {
+                 if (!in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4,
+                                  (u_char *)&nexthop))
+                   if (proto == proto_trans((*rib)->type))
+                     return;
+               }
+           }
+       }
+      return;
+    }
+
+  /* Search next best entry */
+
+  for (np2 = route_top (table); np2; np2 = route_next (np2))
+    {
+
+      /* Check destination first */
+      if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) > 0)
+       RNODE_FOREACH_RIB (np2, rib2)
+         check_replace(np2, rib2, np, rib);
+
+      if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) == 0)
+        { /* have to look at each rib individually */
+         RNODE_FOREACH_RIB (np2, rib2)
+           {
+             int proto2, policy2;
+
+             proto2 = proto_trans(rib2->type);
+             policy2 = 0;
+
+             if ((policy < policy2)
+                 || ((policy == policy2) && (proto < proto2))
+                 || ((policy == policy2) && (proto == proto2)
+                     && (in_addr_cmp((u_char *)&rib2->nexthop->gate.ipv4,
+                                     (u_char *) &nexthop) >= 0)
+                     ))
+               check_replace(np2, rib2, np, rib);
+           }
+       }
+    }
+
+  if (!*rib)
+    return;
+
+  policy = 0;
+  proto = proto_trans((*rib)->type);
+
+  *objid_len = v->namelen + 10;
+  pnt = (u_char *) &(*np)->p.u.prefix;
+  for (i = 0; i < 4; i++)
+    objid[v->namelen + i] = *pnt++;
+
+  objid[v->namelen + 4] = proto;
+  objid[v->namelen + 5] = policy;
+
+  {
+    struct nexthop *nexthop;
+
+    nexthop = (*rib)->nexthop;
+    if (nexthop)
+      {
+       pnt = (u_char *) &nexthop->gate.ipv4;
+       for (i = 0; i < 4; i++)
+         objid[i + v->namelen + 6] = *pnt++;
+      }
+  }
+
+  return;
+}
+
+static u_char *
+ipFwTable (struct variable *v, oid objid[], size_t *objid_len,
+          int exact, size_t *val_len, WriteMethod **write_method)
+{
+  struct route_node *np;
+  struct rib *rib;
+  static int result;
+  static int resarr[2];
+  static struct in_addr netmask;
+  struct nexthop *nexthop;
+
+  if (smux_header_table(v, objid, objid_len, exact, val_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  get_fwtable_route_node(v, objid, objid_len, exact, &np, &rib);
+  if (!np)
+    return NULL;
+
+  nexthop = rib->nexthop;
+  if (! nexthop)
+    return NULL;
+
+  switch (v->magic)
+    {
+    case IPFORWARDDEST:
+      *val_len = 4;
+      return &np->p.u.prefix;
+      break;
+    case IPFORWARDMASK:
+      masklen2ip(np->p.prefixlen, &netmask);
+      *val_len = 4;
+      return (u_char *)&netmask;
+      break;
+    case IPFORWARDPOLICY:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDNEXTHOP:
+      *val_len = 4;
+      return (u_char *)&nexthop->gate.ipv4;
+      break;
+    case IPFORWARDIFINDEX:
+      *val_len = sizeof(int);
+      return (u_char *)&nexthop->ifindex;
+      break;
+    case IPFORWARDTYPE:
+      if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+         || nexthop->type == NEXTHOP_TYPE_IFNAME)
+        result = 3;
+      else
+        result = 4;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDPROTO:
+      result = proto_trans(rib->type);
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDAGE:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDINFO:
+      resarr[0] = 0;
+      resarr[1] = 0;
+      *val_len  = 2 * sizeof(int);
+      return (u_char *)resarr;
+      break;
+    case IPFORWARDNEXTHOPAS:
+      result = -1;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDMETRIC1:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDMETRIC2:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDMETRIC3:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDMETRIC4:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    case IPFORWARDMETRIC5:
+      result = 0;
+      *val_len  = sizeof(int);
+      return (u_char *)&result;
+      break;
+    default:
+      return NULL;
+      break;
+    }  
+  return NULL;
+}
+
+static u_char *
+ipCidrTable (struct variable *v, oid objid[], size_t *objid_len,
+            int exact, size_t *val_len, WriteMethod **write_method)
+{
+  if (smux_header_table(v, objid, objid_len, exact, val_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  switch (v->magic)
+    {
+    case IPCIDRROUTEDEST:
+      break;
+    default:
+      return NULL;
+      break;
+    }  
+  return NULL;
+}
+
+void
+zebra_snmp_init ()
+{
+  smux_init (zebrad.master);
+  REGISTER_MIB("mibII/ipforward", zebra_variables, variable, ipfw_oid);
+}
+#endif /* HAVE_SNMP */
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
new file mode 100644 (file)
index 0000000..0c08990
--- /dev/null
@@ -0,0 +1,5528 @@
+/* Zebra VTY functions
+ * Copyright (C) 2002 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "table.h"
+#include "rib.h"
+#include "vrf.h"
+#include "nexthop.h"
+
+#include "zebra/zserv.h"
+#include "zebra/zebra_rnh.h"
+
+static int do_show_ip_route(struct vty *vty, safi_t safi, vrf_id_t vrf_id);
+static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn,
+                                      int mcast);
+static void vty_show_ip_route (struct vty *vty, struct route_node *rn,
+                               struct rib *rib);
+
+/* General function for static route. */
+static int
+zebra_static_ipv4_safi (struct vty *vty, safi_t safi, int add_cmd,
+                       const char *dest_str, const char *mask_str,
+                       const char *gate_str, const char *flag_str,
+                       const char *tag_str, const char *distance_str,
+                       const char *vrf_id_str)
+{
+  int ret;
+  u_char distance;
+  struct prefix p;
+  struct in_addr gate;
+  struct in_addr mask;
+  const char *ifname;
+  u_char flag = 0;
+  route_tag_t tag = 0;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  
+  ret = str2prefix (dest_str, &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Cisco like mask notation. */
+  if (mask_str)
+    {
+      ret = inet_aton (mask_str, &mask);
+      if (ret == 0)
+        {
+          vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      p.prefixlen = ip_masklen (mask);
+    }
+
+  /* Apply mask for given prefix. */
+  apply_mask (&p);
+
+  /* Administrative distance. */
+  if (distance_str)
+    distance = atoi (distance_str);
+  else
+    distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
+
+  /* tag */
+  if (tag_str)
+    tag = atoi (tag_str);
+
+  /* VRF id */
+  if (vrf_id_str)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, vrf_id_str);
+
+  /* tag */
+  if (tag_str)
+    tag = atoi(tag_str);
+
+  /* Null0 static route.  */
+  if ((gate_str != NULL) && (strncasecmp (gate_str, "Null0", strlen (gate_str)) == 0))
+    {
+      if (flag_str)
+        {
+          vty_out (vty, "%% can not have flag %s with Null0%s", flag_str, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      if (add_cmd)
+        static_add_ipv4_safi (safi, &p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, tag, distance, vrf_id);
+      else
+        static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id);
+      return CMD_SUCCESS;
+    }
+
+  /* Route flags */
+  if (flag_str) {
+    switch(flag_str[0]) {
+      case 'r':
+      case 'R': /* XXX */
+        SET_FLAG (flag, ZEBRA_FLAG_REJECT);
+        break;
+      case 'b':
+      case 'B': /* XXX */
+        SET_FLAG (flag, ZEBRA_FLAG_BLACKHOLE);
+        break;
+      default:
+        vty_out (vty, "%% Malformed flag %s %s", flag_str, VTY_NEWLINE);
+        return CMD_WARNING;
+    }
+  }
+
+  if (gate_str == NULL)
+  {
+    if (add_cmd)
+      static_add_ipv4_safi (safi, &p, NULL, NULL, flag, tag, distance, vrf_id);
+    else
+      static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id);
+
+    return CMD_SUCCESS;
+  }
+  
+  /* When gateway is A.B.C.D format, gate is treated as nexthop
+     address other case gate is treated as interface name. */
+  ret = inet_aton (gate_str, &gate);
+  if (ret)
+    ifname = NULL;
+  else
+    ifname = gate_str;
+
+  if (add_cmd)
+    static_add_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, flag, tag, distance, vrf_id);
+  else
+    static_delete_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, tag, distance, vrf_id);
+
+  return CMD_SUCCESS;
+}
+
+/* Static unicast routes for multicast RPF lookup. */
+DEFUN (ip_mroute_dist,
+       ip_mroute_dist_cmd,
+       "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>",
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       "Distance\n")
+{
+  VTY_WARN_EXPERIMENTAL();
+  return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1],
+                                NULL, NULL, argc > 2 ? argv[2] : NULL, NULL);
+}
+
+ALIAS (ip_mroute_dist,
+       ip_mroute_cmd,
+       "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)",
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n")
+
+DEFUN (ip_mroute_dist_vrf,
+       ip_mroute_dist_vrf_cmd,
+       "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       "Distance\n"
+       VRF_CMD_HELP_STR)
+{
+  VTY_WARN_EXPERIMENTAL();
+  return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1],
+                                NULL, NULL, argc > 3 ? argv[2] : NULL,
+                                argc > 3 ? argv[3] : argv[2]);
+}
+
+ALIAS (ip_mroute_dist_vrf,
+       ip_mroute_vrf_cmd,
+       "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) "VRF_CMD_STR,
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_ip_mroute_dist,
+       no_ip_mroute_dist_cmd,
+       "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>",
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       "Distance\n")
+{
+  VTY_WARN_EXPERIMENTAL();
+  return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1],
+                                NULL, NULL, argc > 2 ? argv[2] : NULL, NULL);
+}
+
+ALIAS (no_ip_mroute_dist,
+       no_ip_mroute_cmd,
+       "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)",
+       NO_STR
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n")
+
+DEFUN (no_ip_mroute_dist_vrf,
+       no_ip_mroute_dist_vrf_cmd,
+       "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       "Distance\n"
+       VRF_CMD_HELP_STR)
+{
+  VTY_WARN_EXPERIMENTAL();
+  return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1],
+                                NULL, NULL, argc > 3 ? argv[2] : NULL,
+                                argc > 3 ? argv[3] : argv[2]);
+}
+
+ALIAS (no_ip_mroute_dist_vrf,
+       no_ip_mroute_vrf_cmd,
+       "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Configure static unicast route into MRIB for multicast RPF lookup\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Nexthop address\n"
+       "Nexthop interface name\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (ip_multicast_mode,
+       ip_multicast_mode_cmd,
+       "ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)",
+       IP_STR
+       "Multicast options\n"
+       "RPF lookup behavior\n"
+       "Lookup in unicast RIB only\n"
+       "Lookup in multicast RIB only\n"
+       "Try multicast RIB first, fall back to unicast RIB\n"
+       "Lookup both, use entry with lower distance\n"
+       "Lookup both, use entry with longer prefix\n")
+{
+  VTY_WARN_EXPERIMENTAL();
+
+  if (!strncmp (argv[0], "u", 1))
+    multicast_mode_ipv4_set (MCAST_URIB_ONLY);
+  else if (!strncmp (argv[0], "mrib-o", 6))
+    multicast_mode_ipv4_set (MCAST_MRIB_ONLY);
+  else if (!strncmp (argv[0], "mrib-t", 6))
+    multicast_mode_ipv4_set (MCAST_MIX_MRIB_FIRST);
+  else if (!strncmp (argv[0], "low", 3))
+    multicast_mode_ipv4_set (MCAST_MIX_DISTANCE);
+  else if (!strncmp (argv[0], "lon", 3))
+    multicast_mode_ipv4_set (MCAST_MIX_PFXLEN);
+  else
+    {
+      vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_multicast_mode,
+       no_ip_multicast_mode_cmd,
+       "no ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)",
+       NO_STR
+       IP_STR
+       "Multicast options\n"
+       "RPF lookup behavior\n"
+       "Lookup in unicast RIB only\n"
+       "Lookup in multicast RIB only\n"
+       "Try multicast RIB first, fall back to unicast RIB\n"
+       "Lookup both, use entry with lower distance\n"
+       "Lookup both, use entry with longer prefix\n")
+{
+  multicast_mode_ipv4_set (MCAST_NO_CONFIG);
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_multicast_mode,
+       no_ip_multicast_mode_noarg_cmd,
+       "no ip multicast rpf-lookup-mode",
+       NO_STR
+       IP_STR
+       "Multicast options\n"
+       "RPF lookup behavior\n")
+
+DEFUN (show_ip_rpf,
+       show_ip_rpf_cmd,
+       "show ip rpf",
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  VTY_WARN_EXPERIMENTAL();
+  return do_show_ip_route(vty, SAFI_MULTICAST, vrf_id);
+}
+
+ALIAS (show_ip_rpf,
+       show_ip_rpf_vrf_cmd,
+       "show ip rpf " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_rpf_addr,
+       show_ip_rpf_addr_cmd,
+       "show ip rpf A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n"
+       "IP multicast source address (e.g. 10.0.0.0)\n")
+{
+  struct in_addr addr;
+  struct route_node *rn;
+  struct rib *rib;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  int ret;
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  VTY_WARN_EXPERIMENTAL();
+
+  ret = inet_aton (argv[0], &addr);
+  if (ret == 0)
+    {
+      vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rib = rib_match_ipv4_multicast (addr, &rn, vrf_id);
+
+  if (rib)
+    vty_show_ip_route_detail (vty, rn, 1);
+  else
+    vty_out (vty, "%% No match for RPF lookup%s", VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_rpf_addr,
+       show_ip_rpf_addr_vrf_cmd,
+       "show ip rpf A.B.C.D " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n"
+       "IP multicast source address (e.g. 10.0.0.0)\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_rpf_vrf_all,
+       show_ip_rpf_vrf_all_cmd,
+       "show ip rpf " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct zebra_vrf *zvrf;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  vrf_iter_t iter;
+  int first = 1;
+
+  VTY_WARN_EXPERIMENTAL();
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_MULTICAST]) == NULL)
+        continue;
+
+      /* Show all IPv4 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          {
+            if (first)
+              {
+                vty_out (vty, SHOW_ROUTE_V4_HEADER);
+                first = 0;
+              }
+            vty_show_ip_route (vty, rn, rib);
+          }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_rpf_addr_vrf_all,
+       show_ip_rpf_addr_vrf_all_cmd,
+       "show ip rpf A.B.C.D " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "Display RPF information for multicast source\n"
+       "IP multicast source address (e.g. 10.0.0.0)\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct in_addr addr;
+  struct route_node *rn;
+  vrf_iter_t iter;
+  int ret;
+
+  VTY_WARN_EXPERIMENTAL();
+
+  ret = inet_aton (argv[0], &addr);
+  if (ret == 0)
+    {
+      vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if (rib_match_ipv4_multicast (addr, &rn, vrf_iter2id (iter)))
+        vty_show_ip_route_detail (vty, rn, 1);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Static route configuration.  */
+DEFUN (ip_route, 
+       ip_route_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                NULL, NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_tag,
+       ip_route_tag_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, argv[2], NULL, NULL);
+}
+
+DEFUN (ip_route_tag_vrf,
+       ip_route_tag_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, argv[2], NULL, argv[3]);
+}
+
+DEFUN (ip_route_flags,
+       ip_route_flags_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_flags_tag,
+       ip_route_flags_tag_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], argv[3], NULL, NULL);
+}
+
+DEFUN (ip_route_flags_tag_vrf,
+       ip_route_flags_tag_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], argv[3], NULL, argv[4]);
+}
+
+DEFUN (ip_route_flags2,
+       ip_route_flags2_cmd,
+       "ip route A.B.C.D/M (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_flags2_tag,
+       ip_route_flags2_tag_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL,
+                                argv[1], argv[2], NULL, NULL);
+}
+
+DEFUN (ip_route_flags2_tag_vrf,
+       ip_route_flags2_tag_vrf_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL,
+                                argv[1], argv[2], NULL, argv[3]);
+}
+
+/* Mask as A.B.C.D format.  */
+DEFUN (ip_route_mask,
+       ip_route_mask_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], NULL, NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_mask_tag,
+       ip_route_mask_tag_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                NULL, argv[3], NULL, NULL);
+}
+
+DEFUN (ip_route_mask_tag_vrf,
+       ip_route_mask_tag_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                NULL, argv[3], NULL, argv[4]);
+}
+
+DEFUN (ip_route_mask_flags,
+       ip_route_mask_flags_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], argv[3], NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_mask_flags_tag,
+       ip_route_mask_flags_tag_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], argv[3], argv[4], NULL, NULL);
+}
+
+DEFUN (ip_route_mask_flags_tag_vrf,
+       ip_route_mask_flags_tag_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], argv[3], argv[4], NULL, argv[5]);
+}
+
+DEFUN (ip_route_mask_flags2,
+       ip_route_mask_flags2_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                NULL, argv[2], NULL, NULL, NULL);
+}
+
+DEFUN (ip_route_mask_flags2_tag,
+       ip_route_mask_flags2_tag_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL,
+                                argv[2], argv[3], NULL, NULL);
+}
+
+DEFUN (ip_route_mask_flags2_tag_vrf,
+       ip_route_mask_flags2_tag_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL,
+                                argv[2], argv[3], NULL, argv[4]);
+}
+
+/* Distance option value.  */
+DEFUN (ip_route_distance,
+       ip_route_distance_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, NULL, argv[2], NULL);
+}
+
+DEFUN (ip_route_tag_distance,
+       ip_route_tag_distance_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, argv[2], argv[3], NULL);
+}
+
+DEFUN (ip_route_tag_distance_vrf,
+       ip_route_tag_distance_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, argv[2], argv[3], argv[4]);
+}
+
+DEFUN (ip_route_flags_distance,
+       ip_route_flags_distance_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], NULL, argv[3], NULL);
+}
+
+DEFUN (ip_route_flags_tag_distance,
+       ip_route_flags_tag_distance_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (ip_route_flags_tag_distance_vrf,
+       ip_route_flags_tag_distance_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1],
+                                argv[2], argv[3], argv[4], argv[5]);
+}
+
+DEFUN (ip_route_flags_distance2,
+       ip_route_flags_distance2_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], NULL, argv[2], NULL);
+}
+
+DEFUN (ip_route_flags_tag_distance2,
+       ip_route_flags_tag_distance2_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], argv[2], argv[3], NULL);
+}
+
+DEFUN (ip_route_flags_tag_distance2_vrf,
+       ip_route_flags_tag_distance2_vrf_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], argv[2], argv[3], argv[4]);
+}
+
+DEFUN (ip_route_mask_distance,
+       ip_route_mask_distance_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                NULL, NULL, argv[3], NULL);
+}
+
+DEFUN (ip_route_mask_tag_distance,
+       ip_route_mask_tag_distance_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], NULL, argv[3], argv[4], NULL);
+}
+
+DEFUN (ip_route_mask_tag_distance_vrf,
+       ip_route_mask_tag_distance_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], NULL, argv[3], argv[4], argv[5]);
+}
+
+
+DEFUN (ip_route_mask_flags_tag_distance,
+       ip_route_mask_flags_tag_distance_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)  tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                argv[3], argv[4], argv[5], NULL);
+}
+
+DEFUN (ip_route_mask_flags_tag_distance_vrf,
+       ip_route_mask_flags_tag_distance_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)  tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                argv[3], argv[4], argv[5], argv[6]);
+}
+
+DEFUN (ip_route_mask_flags_distance,
+       ip_route_mask_flags_distance_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2],
+                                argv[3], NULL, argv[4], NULL);
+}
+
+DEFUN (ip_route_mask_flags_distance2,
+       ip_route_mask_flags_distance2_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                NULL, argv[2], NULL, argv[3], NULL);
+}
+
+DEFUN (ip_route_mask_flags_tag_distance2,
+       ip_route_mask_flags_tag_distance2_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL,
+                                argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (ip_route_mask_flags_tag_distance2_vrf,
+       ip_route_mask_flags_tag_distance2_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL,
+                                argv[2], argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ip_route, 
+       no_ip_route_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL,
+                                argv[1], NULL, NULL, NULL, NULL);
+}
+
+DEFUN (no_ip_route_tag,
+       no_ip_route_tag_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                NULL, argv[2], NULL, NULL);
+}
+
+DEFUN (no_ip_route_tag_vrf,
+       no_ip_route_tag_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                NULL, argv[2], NULL, argv[3]);
+}
+
+ALIAS (no_ip_route,
+       no_ip_route_flags_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+
+ALIAS (no_ip_route_tag,
+       no_ip_route_flags_tag_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n")
+
+DEFUN (no_ip_route_flags2,
+       no_ip_route_flags2_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL,
+                                NULL, NULL, NULL, NULL, NULL);
+}
+
+DEFUN (no_ip_route_flags2_tag,
+       no_ip_route_flags2_tag_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                NULL, argv[1], NULL, NULL);
+}
+
+DEFUN (no_ip_route_flags2_tag_vrf,
+       no_ip_route_flags2_tag_vrf_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                NULL, argv[1], NULL, argv[2]);
+}
+
+DEFUN (no_ip_route_mask,
+       no_ip_route_mask_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, NULL, NULL, NULL);
+}
+
+DEFUN (no_ip_route_mask_tag,
+       no_ip_route_mask_tag_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2],
+                                NULL, argv[3], NULL, NULL);
+}
+
+ALIAS (no_ip_route_mask,
+       no_ip_route_mask_flags_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+
+ALIAS (no_ip_route_mask_tag,
+       no_ip_route_mask_flags_tag_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n")
+
+DEFUN (no_ip_route_mask_flags2,
+       no_ip_route_mask_flags2_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                NULL, NULL, NULL, NULL, NULL);
+}
+
+DEFUN (no_ip_route_mask_flags2_tag,
+       no_ip_route_mask_flags2_tag_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                NULL, NULL, argv[2], NULL, NULL);
+}
+
+DEFUN (no_ip_route_mask_flags2_tag_vrf,
+       no_ip_route_mask_flags2_tag_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                NULL, NULL, argv[2], NULL, argv[3]);
+}
+
+DEFUN (no_ip_route_distance,
+       no_ip_route_distance_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL,
+                                argv[1], NULL, NULL, argv[2], NULL);
+}
+
+DEFUN (no_ip_route_tag_distance,
+       no_ip_route_tag_distance_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                NULL, argv[2], argv[3], NULL);
+}
+
+DEFUN (no_ip_route_tag_distance_vrf,
+       no_ip_route_tag_distance_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                NULL, argv[2], argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_flags_distance,
+       no_ip_route_flags_distance_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL,
+                                argv[1], argv[2], NULL, argv[3], NULL);
+}
+
+DEFUN (no_ip_route_flags_tag_distance,
+       no_ip_route_flags_tag_distance_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ip_route_flags_tag_distance_vrf,
+       no_ip_route_flags_tag_distance_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                argv[2], argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ip_route_flags_distance2,
+       no_ip_route_flags_distance2_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                argv[1], NULL, argv[2], NULL);
+}
+
+DEFUN (no_ip_route_flags_tag_distance2,
+       no_ip_route_flags_tag_distance2_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                argv[1], argv[2] , argv[3], NULL);
+}
+
+DEFUN (no_ip_route_flags_tag_distance2_vrf,
+       no_ip_route_flags_tag_distance2_vrf_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                argv[1], argv[2] , argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_mask_distance,
+       no_ip_route_mask_distance_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, NULL, argv[3], NULL);
+}
+
+DEFUN (no_ip_route_mask_tag_distance,
+       no_ip_route_mask_tag_distance_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ip_route_mask_tag_distance_vrf,
+       no_ip_route_mask_tag_distance_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ip_route_mask_flags_distance,
+       no_ip_route_mask_flags_distance_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], argv[3], NULL, argv[4], NULL);
+}
+
+DEFUN (no_ip_route_mask_flags_tag_distance,
+       no_ip_route_mask_flags_tag_distance_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3],
+                                argv[4], argv[5], NULL);
+}
+
+DEFUN (no_ip_route_mask_flags_tag_distance_vrf,
+       no_ip_route_mask_flags_tag_distance_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3],
+                                argv[4], argv[5], argv[6]);
+}
+
+DEFUN (no_ip_route_mask_flags_distance2,
+       no_ip_route_mask_flags_distance2_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                NULL, argv[2], NULL, argv[3], NULL);
+}
+
+DEFUN (ip_route_vrf,
+       ip_route_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, NULL, NULL, argv[2]);
+}
+
+DEFUN (ip_route_flags_vrf,
+       ip_route_flags_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], argv[2], NULL, NULL, argv[3]);
+}
+
+DEFUN (ip_route_flags2_vrf,
+       ip_route_flags2_vrf_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], NULL, NULL, argv[2]);
+}
+
+/* Mask as A.B.C.D format.  */
+DEFUN (ip_route_mask_vrf,
+       ip_route_mask_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], NULL, NULL, NULL, argv[3]);
+}
+
+DEFUN (ip_route_mask_flags_vrf,
+       ip_route_mask_flags_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], argv[3], NULL, NULL, argv[4]);
+}
+
+DEFUN (ip_route_mask_flags2_vrf,
+       ip_route_mask_flags2_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                NULL, argv[2], NULL, NULL, argv[3]);
+}
+
+/* Distance option value.  */
+DEFUN (ip_route_distance_vrf,
+       ip_route_distance_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], NULL, NULL, argv[2], argv[3]);
+}
+
+DEFUN (ip_route_flags_distance_vrf,
+       ip_route_flags_distance_vrf_cmd,
+       "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                argv[1], argv[2], NULL, argv[3], argv[4]);
+}
+
+DEFUN (ip_route_flags_distance2_vrf,
+       ip_route_flags_distance2_vrf_cmd,
+       "ip route A.B.C.D/M (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL,
+                                NULL, argv[1], NULL, argv[2], argv[3]);
+}
+
+DEFUN (ip_route_mask_distance_vrf,
+       ip_route_mask_distance_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], NULL, NULL, argv[3], argv[4]);
+}
+
+DEFUN (ip_route_mask_flags_distance_vrf,
+       ip_route_mask_flags_distance_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                argv[2], argv[3], NULL, argv[4], argv[5]);
+}
+
+DEFUN (ip_route_mask_flags_distance2_vrf,
+       ip_route_mask_flags_distance2_vrf_cmd,
+       "ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1],
+                                NULL, argv[2], NULL, argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_vrf,
+       no_ip_route_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0],
+                                NULL, argv[1], NULL, NULL, NULL,
+                                (argc > 3) ? argv[3] : argv[2]);
+}
+
+ALIAS (no_ip_route_vrf,
+       no_ip_route_flags_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_ip_route_flags2_vrf,
+       no_ip_route_flags2_vrf_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0],
+                                NULL, NULL, NULL, NULL, NULL, argv[2]);
+}
+
+DEFUN (no_ip_route_mask_vrf,
+       no_ip_route_mask_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, NULL, NULL,
+                                (argc > 4) ? argv[4] : argv[3]);
+}
+
+ALIAS (no_ip_route_mask_vrf,
+       no_ip_route_mask_flags_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_ip_route_mask_flags2_vrf,
+       no_ip_route_mask_flags2_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                NULL, NULL, NULL, NULL, argv[2]);
+}
+
+DEFUN (no_ip_route_distance_vrf,
+       no_ip_route_distance_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL,
+                                argv[1], NULL, NULL, argv[2], argv[3]);
+}
+
+DEFUN (no_ip_route_flags_distance_vrf,
+       no_ip_route_flags_distance_vrf_cmd,
+       "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1],
+                                argv[2], NULL, argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_flags_distance2_vrf,
+       no_ip_route_flags_distance2_vrf_cmd,
+       "no ip route A.B.C.D/M (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix (e.g. 10.0.0.0/8)\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL,
+                                argv[1], NULL, argv[2], argv[3]);
+}
+
+DEFUN (no_ip_route_mask_distance_vrf,
+       no_ip_route_mask_distance_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Null interface\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1],
+                                argv[2], NULL, NULL, argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_mask_flags_distance_vrf,
+       no_ip_route_mask_flags_distance_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "IP gateway address\n"
+       "IP gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2],
+                                argv[3], NULL, argv[4], argv[5]);
+}
+
+DEFUN (no_ip_route_mask_flags_distance2_vrf,
+       no_ip_route_mask_flags_distance2_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL,
+                                 argv[2], NULL, argv[3], argv[4]);
+}
+
+DEFUN (no_ip_route_mask_flags_tag_distance2,
+       no_ip_route_mask_flags_tag_distance2_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n")
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL,
+                                argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ip_route_mask_flags_tag_distance2_vrf,
+       no_ip_route_mask_flags_tag_distance2_vrf_cmd,
+       "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IP destination prefix\n"
+       "IP destination prefix mask\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Tag of this route\n"
+       "Tag value\n"
+       "Distance value for this route\n"
+       VRF_CMD_HELP_STR)
+{
+  return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL,
+                                argv[2], argv[3], argv[4], argv[5]);
+}
+
+char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1];    /* "any" == ZEBRA_ROUTE_MAX */
+
+DEFUN (ip_protocol,
+       ip_protocol_cmd,
+       "ip protocol PROTO route-map ROUTE-MAP",
+       NO_STR
+       "Apply route map to PROTO\n"
+       "Protocol name\n"
+       "Route map name\n")
+{
+  int i;
+
+  if (strcasecmp(argv[0], "any") == 0)
+    i = ZEBRA_ROUTE_MAX;
+  else
+    i = proto_name2num(argv[0]);
+  if (i < 0)
+    {
+      vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (proto_rm[AFI_IP][i])
+    XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]);
+  proto_rm[AFI_IP][i] = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[1]);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_protocol,
+       no_ip_protocol_cmd,
+       "no ip protocol PROTO",
+       NO_STR
+       "Remove route map from PROTO\n"
+       "Protocol name\n")
+{
+  int i;
+
+  if (strcasecmp(argv[0], "any") == 0)
+    i = ZEBRA_ROUTE_MAX;
+  else
+    i = proto_name2num(argv[0]);
+  if (i < 0)
+    {
+      vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "",
+               VTY_NEWLINE);
+     return CMD_WARNING;
+    }
+  if (proto_rm[AFI_IP][i])
+    XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]);
+  proto_rm[AFI_IP][i] = NULL;
+  return CMD_SUCCESS;
+}
+
+/* New RIB.  Detailed information for IPv4 route. */
+static void
+vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast)
+{
+  struct rib *rib;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  char buf[PREFIX_STRLEN];
+
+  RNODE_FOREACH_RIB (rn, rib)
+    {
+      const char *mcast_info = "";
+      if (mcast)
+        {
+          rib_table_info_t *info = rn->table->info;
+          mcast_info = (info->safi == SAFI_MULTICAST)
+                       ? " using Multicast RIB"
+                       : " using Unicast RIB";
+        }
+      vty_out (vty, "Routing entry for %s%s%s",
+               prefix2str (&rn->p, buf, sizeof(buf)), mcast_info,
+              VTY_NEWLINE);
+      vty_out (vty, "  Known via \"%s\"", zebra_route_string (rib->type));
+      vty_out (vty, ", distance %u, metric %u", rib->distance, rib->metric);
+      if (rib->mtu)
+        vty_out (vty, ", mtu %u", rib->mtu);
+      vty_out (vty, ", tag %d", rib->tag);
+      vty_out (vty, ", vrf %u", rib->vrf_id);
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+        vty_out (vty, ", best");
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE))
+        vty_out (vty, ", fib-override");
+      if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
+        vty_out (vty, ", fib");
+      if (rib->refcnt)
+        vty_out (vty, ", refcnt %ld", rib->refcnt);
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE))
+       vty_out (vty, ", blackhole");
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT))
+       vty_out (vty, ", reject");
+      vty_out (vty, "%s", VTY_NEWLINE);
+
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+      if (rib->type == ZEBRA_ROUTE_RIP
+          || rib->type == ZEBRA_ROUTE_RIPNG
+          || rib->type == ZEBRA_ROUTE_OSPF
+          || rib->type == ZEBRA_ROUTE_OSPF6
+          || rib->type == ZEBRA_ROUTE_BABEL
+          || rib->type == ZEBRA_ROUTE_ISIS
+          || rib->type == ZEBRA_ROUTE_NHRP
+          || rib->type == ZEBRA_ROUTE_BGP)
+       {
+         time_t uptime;
+         struct tm *tm;
+
+         uptime = time (NULL);
+         uptime -= rib->uptime;
+         tm = gmtime (&uptime);
+
+         vty_out (vty, "  Last update ");
+
+         if (uptime < ONE_DAY_SECOND)
+           vty_out (vty,  "%02d:%02d:%02d", 
+                    tm->tm_hour, tm->tm_min, tm->tm_sec);
+         else if (uptime < ONE_WEEK_SECOND)
+           vty_out (vty, "%dd%02dh%02dm", 
+                    tm->tm_yday, tm->tm_hour, tm->tm_min);
+         else
+           vty_out (vty, "%02dw%dd%02dh", 
+                    tm->tm_yday/7,
+                    tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+         vty_out (vty, " ago%s", VTY_NEWLINE);
+       }
+
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+        {
+          vty_out (vty, "  %c%c%s",
+                   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? '>' : ' ',
+                   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ',
+                   recursing ? "  " : "");
+
+          switch (nexthop->type)
+            {
+            case NEXTHOP_TYPE_IPV4:
+            case NEXTHOP_TYPE_IPV4_IFINDEX:
+              vty_out (vty, " %s", inet_ntoa (nexthop->gate.ipv4));
+              if (nexthop->ifindex)
+                vty_out (vty, ", via %s",
+                         ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+              break;
+            case NEXTHOP_TYPE_IPV6:
+            case NEXTHOP_TYPE_IPV6_IFINDEX:
+            case NEXTHOP_TYPE_IPV6_IFNAME:
+              vty_out (vty, " %s",
+                       inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, sizeof(buf)));
+              if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+                vty_out (vty, ", %s", nexthop->ifname);
+              else if (nexthop->ifindex)
+                vty_out (vty, ", via %s",
+                         ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+              break;
+            case NEXTHOP_TYPE_IFINDEX:
+              vty_out (vty, " directly connected, %s",
+                       ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+              break;
+            case NEXTHOP_TYPE_IFNAME:
+              vty_out (vty, " directly connected, %s", nexthop->ifname);
+              break;
+            case NEXTHOP_TYPE_BLACKHOLE:
+              vty_out (vty, " directly connected, Null0");
+              break;
+            default:
+              break;
+            }
+          if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+            vty_out (vty, " inactive");
+
+          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
+            vty_out (vty, " onlink");
+
+          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+            vty_out (vty, " (recursive)");
+
+          switch (nexthop->type)
+            {
+            case NEXTHOP_TYPE_IPV4:
+            case NEXTHOP_TYPE_IPV4_IFINDEX:
+            case NEXTHOP_TYPE_IPV4_IFNAME:
+              if (nexthop->src.ipv4.s_addr)
+                {
+                  if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof buf))
+                    vty_out (vty, ", src %s", buf);
+                }
+              break;
+#ifdef HAVE_IPV6
+            case NEXTHOP_TYPE_IPV6:
+            case NEXTHOP_TYPE_IPV6_IFINDEX:
+            case NEXTHOP_TYPE_IPV6_IFNAME:
+              if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
+                {
+                  if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, sizeof buf))
+                    vty_out (vty, ", src %s", buf);
+                }
+              break;
+#endif /* HAVE_IPV6 */
+            default:
+               break;
+            }
+          vty_out (vty, "%s", VTY_NEWLINE);
+        }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+static void
+vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib)
+{
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  int len = 0;
+  char buf[BUFSIZ];
+
+  /* Nexthop information. */
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (nexthop == rib->nexthop)
+       {
+         /* Prefix information. */
+         len = vty_out (vty, "%c%c%c %s",
+                        zebra_route_char (rib->type),
+                        CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)
+                        ? '>' : ' ',
+                        CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+                        ? '*' : ' ',
+                        prefix2str (&rn->p, buf, sizeof buf));
+               
+         /* Distance and metric display. */
+         if (rib->type != ZEBRA_ROUTE_CONNECT 
+             && rib->type != ZEBRA_ROUTE_KERNEL)
+           len += vty_out (vty, " [%d/%d]", rib->distance,
+                           rib->metric);
+
+          if (rib->vrf_id != VRF_DEFAULT)
+            len += vty_out (vty, " [vrf %u]", rib->vrf_id);
+       }
+      else
+       vty_out (vty, "  %c%*c",
+                CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+                ? '*' : ' ',
+                len - 3 + (2 * recursing), ' ');
+
+      switch (nexthop->type)
+        {
+        case NEXTHOP_TYPE_IPV4:
+        case NEXTHOP_TYPE_IPV4_IFINDEX:
+          vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4));
+          if (nexthop->ifindex)
+            vty_out (vty, ", %s",
+                     ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+          break;
+        case NEXTHOP_TYPE_IPV6:
+        case NEXTHOP_TYPE_IPV6_IFINDEX:
+        case NEXTHOP_TYPE_IPV6_IFNAME:
+          vty_out (vty, " via %s",
+                   inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+          if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+            vty_out (vty, ", %s", nexthop->ifname);
+          else if (nexthop->ifindex)
+            vty_out (vty, ", %s",
+                     ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+          break;
+        case NEXTHOP_TYPE_IFINDEX:
+          vty_out (vty, " is directly connected, %s",
+                   ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id));
+          break;
+        case NEXTHOP_TYPE_IFNAME:
+          vty_out (vty, " is directly connected, %s", nexthop->ifname);
+          break;
+        case NEXTHOP_TYPE_BLACKHOLE:
+          vty_out (vty, " is directly connected, Null0");
+          break;
+        default:
+          break;
+        }
+      if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+        vty_out (vty, " inactive");
+
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
+        vty_out (vty, " onlink");
+
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        vty_out (vty, " (recursive)");
+
+      switch (nexthop->type)
+        {
+          case NEXTHOP_TYPE_IPV4:
+          case NEXTHOP_TYPE_IPV4_IFINDEX:
+          case NEXTHOP_TYPE_IPV4_IFNAME:
+            if (nexthop->src.ipv4.s_addr)
+              {
+                if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof buf))
+                  vty_out (vty, ", src %s", buf);
+              }
+            break;
+#ifdef HAVE_IPV6
+          case NEXTHOP_TYPE_IPV6:
+          case NEXTHOP_TYPE_IPV6_IFINDEX:
+          case NEXTHOP_TYPE_IPV6_IFNAME:
+            if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
+              {
+                if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, sizeof buf))
+                  vty_out (vty, ", src %s", buf);
+              }
+            break;
+#endif /* HAVE_IPV6 */
+          default:
+            break;
+        }
+
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE))
+               vty_out (vty, ", bh");
+      if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT))
+               vty_out (vty, ", rej");
+
+      if (rib->type == ZEBRA_ROUTE_RIP
+          || rib->type == ZEBRA_ROUTE_RIPNG
+          || rib->type == ZEBRA_ROUTE_OSPF
+          || rib->type == ZEBRA_ROUTE_OSPF6
+          || rib->type == ZEBRA_ROUTE_BABEL
+          || rib->type == ZEBRA_ROUTE_ISIS
+          || rib->type == ZEBRA_ROUTE_NHRP
+          || rib->type == ZEBRA_ROUTE_BGP)
+       {
+         time_t uptime;
+         struct tm *tm;
+
+         uptime = time (NULL);
+         uptime -= rib->uptime;
+         tm = gmtime (&uptime);
+
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+
+         if (uptime < ONE_DAY_SECOND)
+           vty_out (vty,  ", %02d:%02d:%02d", 
+                    tm->tm_hour, tm->tm_min, tm->tm_sec);
+         else if (uptime < ONE_WEEK_SECOND)
+           vty_out (vty, ", %dd%02dh%02dm", 
+                    tm->tm_yday, tm->tm_hour, tm->tm_min);
+         else
+           vty_out (vty, ", %02dw%dd%02dh", 
+                    tm->tm_yday/7,
+                    tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+       }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_route,
+       show_ip_route_cmd,
+       "show ip route",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  return do_show_ip_route(vty, SAFI_UNICAST, vrf_id);
+}
+
+static int do_show_ip_route(struct vty *vty, safi_t safi, vrf_id_t vrf_id)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+
+  table = zebra_vrf_table (AFI_IP, safi, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show all IPv4 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+       if (first)
+         {
+           vty_out (vty, SHOW_ROUTE_V4_HEADER);
+           first = 0;
+         }
+       vty_show_ip_route (vty, rn, rib);
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route,
+       show_ip_route_vrf_cmd,
+       "show ip route " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_nht,
+       show_ip_nht_cmd,
+       "show ip nht",
+       SHOW_STR
+       IP_STR
+       "IP nexthop tracking table\n")
+{
+  zebra_print_rnh_table(0, AF_INET, vty);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_nht,
+       show_ipv6_nht_cmd,
+       "show ipv6 nht",
+       SHOW_STR
+       IP_STR
+       "IPv6 nexthop tracking table\n")
+{
+  zebra_print_rnh_table(0, AF_INET6, vty);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_tag,
+       show_ip_route_tag_cmd,
+       "show ip route tag <1-4294967295>",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Show only routes with tag\n"
+       "Tag value\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  route_tag_t tag = 0;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argv[0])
+    tag = atoi(argv[0]);
+
+  if (argc == 2)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show all IPv4 routes with matching tag value. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+        if (rib->tag != tag)
+          continue;
+
+        if (first)
+          {
+            vty_out (vty, SHOW_ROUTE_V4_HEADER);
+            first = 0;
+          }
+        vty_show_ip_route (vty, rn, rib);
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_tag,
+       show_ip_route_tag_vrf_cmd,
+       "show ip route tag <1-4294967295>" VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Show only routes with tag\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_prefix_longer,
+       show_ip_route_prefix_longer_cmd,
+       "show ip route A.B.C.D/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Show route matching the specified Network/Mask pair only\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct prefix p;
+  int ret;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix (argv[0], &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show matched type IPv4 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      if (prefix_match (&p, &rn->p))
+       {
+         if (first)
+           {
+             vty_out (vty, SHOW_ROUTE_V4_HEADER);
+             first = 0;
+           }
+         vty_show_ip_route (vty, rn, rib);
+       }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_prefix_longer,
+       show_ip_route_prefix_longer_vrf_cmd,
+       "show ip route A.B.C.D/M longer-prefixes " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Show route matching the specified Network/Mask pair only\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_supernets,
+       show_ip_route_supernets_cmd,
+       "show ip route supernets-only",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Show supernet entries only\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  u_int32_t addr;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show matched type IPv4 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+       addr = ntohl (rn->p.u.prefix4.s_addr);
+
+       if ((IN_CLASSC (addr) && rn->p.prefixlen < 24)
+          || (IN_CLASSB (addr) && rn->p.prefixlen < 16)
+          || (IN_CLASSA (addr) && rn->p.prefixlen < 8))
+         {
+           if (first)
+             {
+               vty_out (vty, SHOW_ROUTE_V4_HEADER);
+               first = 0;
+             }
+           vty_show_ip_route (vty, rn, rib);
+         }
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_supernets,
+       show_ip_route_supernets_vrf_cmd,
+       "show ip route supernets-only " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Show supernet entries only\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_protocol,
+       show_ip_route_protocol_cmd,
+       "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP_REDIST_HELP_STR_ZEBRA)
+{
+  int type;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0)
+    {
+      vty_out (vty, "Unknown route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show matched type IPv4 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      if (rib->type == type)
+       {
+         if (first)
+           {
+             vty_out (vty, SHOW_ROUTE_V4_HEADER);
+             first = 0;
+           }
+         vty_show_ip_route (vty, rn, rib);
+       }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_protocol,
+       show_ip_route_protocol_vrf_cmd,
+       "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA " " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP_REDIST_HELP_STR_ZEBRA
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_addr,
+       show_ip_route_addr_cmd,
+       "show ip route A.B.C.D",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Network in the IP routing table to display\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (! rn)
+    {
+      vty_out (vty, "%% Network not in table%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vty_show_ip_route_detail (vty, rn, 0);
+
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_addr,
+       show_ip_route_addr_vrf_cmd,
+       "show ip route A.B.C.D " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Network in the IP routing table to display\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_prefix,
+       show_ip_route_prefix_cmd,
+       "show ip route A.B.C.D/M",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n")
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (! rn || rn->p.prefixlen != p.prefixlen)
+    {
+      vty_out (vty, "%% Network not in table%s", VTY_NEWLINE);
+      if (rn)
+        route_unlock_node (rn);
+      return CMD_WARNING;
+    }
+
+  vty_show_ip_route_detail (vty, rn, 0);
+
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_prefix,
+       show_ip_route_prefix_vrf_cmd,
+       "show ip route A.B.C.D/M " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       VRF_CMD_HELP_STR)
+
+static void
+vty_show_ip_route_summary (struct vty *vty, struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct nexthop *nexthop;
+#define ZEBRA_ROUTE_IBGP  ZEBRA_ROUTE_MAX
+#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1)
+  u_int32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1];
+  u_int32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
+  u_int32_t i;
+
+  memset (&rib_cnt, 0, sizeof(rib_cnt));
+  memset (&fib_cnt, 0, sizeof(fib_cnt));
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+        {
+         rib_cnt[ZEBRA_ROUTE_TOTAL]++;
+         rib_cnt[rib->type]++;
+         if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+             || nexthop_has_fib_child(nexthop))
+           {
+             fib_cnt[ZEBRA_ROUTE_TOTAL]++;
+             fib_cnt[rib->type]++;
+           }
+         if (rib->type == ZEBRA_ROUTE_BGP && 
+             CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) 
+           {
+             rib_cnt[ZEBRA_ROUTE_IBGP]++;
+             if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+                 || nexthop_has_fib_child(nexthop))
+               fib_cnt[ZEBRA_ROUTE_IBGP]++;
+           }
+       }
+
+  vty_out (vty, "%-20s %-20s %s  (vrf %u)%s",
+           "Route Source", "Routes", "FIB",
+           ((rib_table_info_t *)table->info)->zvrf->vrf_id,
+           VTY_NEWLINE);
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 
+    {
+      if (rib_cnt[i] > 0)
+       {
+         if (i == ZEBRA_ROUTE_BGP)
+           {
+             vty_out (vty, "%-20s %-20d %-20d %s", "ebgp", 
+                      rib_cnt[ZEBRA_ROUTE_BGP] - rib_cnt[ZEBRA_ROUTE_IBGP],
+                      fib_cnt[ZEBRA_ROUTE_BGP] - fib_cnt[ZEBRA_ROUTE_IBGP],
+                      VTY_NEWLINE);
+             vty_out (vty, "%-20s %-20d %-20d %s", "ibgp", 
+                      rib_cnt[ZEBRA_ROUTE_IBGP], fib_cnt[ZEBRA_ROUTE_IBGP],
+                      VTY_NEWLINE);
+           }
+         else 
+           vty_out (vty, "%-20s %-20d %-20d %s", zebra_route_string(i), 
+                    rib_cnt[i], fib_cnt[i], VTY_NEWLINE);
+       }
+    }
+
+  vty_out (vty, "------%s", VTY_NEWLINE);
+  vty_out (vty, "%-20s %-20d %-20d %s", "Totals", rib_cnt[ZEBRA_ROUTE_TOTAL], 
+          fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE);  
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+/*
+ * Implementation of the ip route summary prefix command.
+ *
+ * This command prints the primary prefixes that have been installed by various
+ * protocols on the box.
+ *
+ */
+static void
+vty_show_ip_route_summary_prefix (struct vty *vty, struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct nexthop *nexthop;
+#define ZEBRA_ROUTE_IBGP  ZEBRA_ROUTE_MAX
+#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1)
+  u_int32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1];
+  u_int32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
+  u_int32_t i;
+  int       cnt;
+
+  memset (&rib_cnt, 0, sizeof(rib_cnt));
+  memset (&fib_cnt, 0, sizeof(fib_cnt));
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+
+       /*
+        * In case of ECMP, count only once.
+        */
+       cnt = 0;
+       for (nexthop = rib->nexthop; (!cnt && nexthop); nexthop = nexthop->next)
+         {
+          cnt++;
+          rib_cnt[ZEBRA_ROUTE_TOTAL]++;
+          rib_cnt[rib->type]++;
+          if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+               {
+                fib_cnt[ZEBRA_ROUTE_TOTAL]++;
+             fib_cnt[rib->type]++;
+            }
+             if (rib->type == ZEBRA_ROUTE_BGP &&
+                 CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
+            {
+                rib_cnt[ZEBRA_ROUTE_IBGP]++;
+                    if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+                       fib_cnt[ZEBRA_ROUTE_IBGP]++;
+            }
+            }
+      }
+
+  vty_out (vty, "%-20s %-20s %s  (vrf %u)%s",
+           "Route Source", "Prefix Routes", "FIB",
+           ((rib_table_info_t *)table->info)->zvrf->vrf_id,
+           VTY_NEWLINE);
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    {
+      if (rib_cnt[i] > 0)
+       {
+         if (i == ZEBRA_ROUTE_BGP)
+           {
+             vty_out (vty, "%-20s %-20d %-20d %s", "ebgp",
+                      rib_cnt[ZEBRA_ROUTE_BGP] - rib_cnt[ZEBRA_ROUTE_IBGP],
+                      fib_cnt[ZEBRA_ROUTE_BGP] - fib_cnt[ZEBRA_ROUTE_IBGP],
+                      VTY_NEWLINE);
+             vty_out (vty, "%-20s %-20d %-20d %s", "ibgp",
+                      rib_cnt[ZEBRA_ROUTE_IBGP], fib_cnt[ZEBRA_ROUTE_IBGP],
+                      VTY_NEWLINE);
+           }
+         else
+           vty_out (vty, "%-20s %-20d %-20d %s", zebra_route_string(i),
+                    rib_cnt[i], fib_cnt[i], VTY_NEWLINE);
+       }
+    }
+
+  vty_out (vty, "------%s", VTY_NEWLINE);
+  vty_out (vty, "%-20s %-20d %-20d %s", "Totals", rib_cnt[ZEBRA_ROUTE_TOTAL],
+          fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE);
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+/* Show route summary.  */
+DEFUN (show_ip_route_summary,
+       show_ip_route_summary_cmd,
+       "show ip route summary",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n")
+{
+  struct route_table *table;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  vty_show_ip_route_summary (vty, table);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_summary,
+       show_ip_route_summary_vrf_cmd,
+       "show ip route summary " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n"
+       VRF_CMD_HELP_STR)
+
+/* Show route summary prefix.  */
+DEFUN (show_ip_route_summary_prefix,
+       show_ip_route_summary_prefix_cmd,
+       "show ip route summary prefix",
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n"
+       "Prefix routes\n")
+{
+  struct route_table *table;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  vty_show_ip_route_summary_prefix (vty, table);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ip_route_summary_prefix,
+       show_ip_route_summary_prefix_vrf_cmd,
+       "show ip route summary prefix " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n"
+       "Prefix routes\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ip_route_vrf_all,
+       show_ip_route_vrf_all_cmd,
+       "show ip route " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int first = 1;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show all IPv4 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          {
+            if (first)
+              {
+                vty_out (vty, SHOW_ROUTE_V4_HEADER);
+                first = 0;
+              }
+            vty_show_ip_route (vty, rn, rib);
+          }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_prefix_longer_vrf_all,
+       show_ip_route_prefix_longer_vrf_all_cmd,
+       "show ip route A.B.C.D/M longer-prefixes " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       "Show route matching the specified Network/Mask pair only\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct prefix p;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int ret;
+  int first = 1;
+
+  ret = str2prefix (argv[0], &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show matched type IPv4 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          if (prefix_match (&p, &rn->p))
+            {
+              if (first)
+                {
+                  vty_out (vty, SHOW_ROUTE_V4_HEADER);
+                  first = 0;
+                }
+              vty_show_ip_route (vty, rn, rib);
+            }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_supernets_vrf_all,
+       show_ip_route_supernets_vrf_all_cmd,
+       "show ip route supernets-only " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Show supernet entries only\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  u_int32_t addr;
+  int first = 1;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show matched type IPv4 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          {
+            addr = ntohl (rn->p.u.prefix4.s_addr);
+
+            if ((IN_CLASSC (addr) && rn->p.prefixlen < 24)
+               || (IN_CLASSB (addr) && rn->p.prefixlen < 16)
+               || (IN_CLASSA (addr) && rn->p.prefixlen < 8))
+              {
+                if (first)
+                  {
+                    vty_out (vty, SHOW_ROUTE_V4_HEADER);
+                    first = 0;
+                  }
+                vty_show_ip_route (vty, rn, rib);
+              }
+          }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_protocol_vrf_all,
+       show_ip_route_protocol_vrf_all_cmd,
+       "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA " " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP_REDIST_HELP_STR_ZEBRA
+       VRF_ALL_CMD_HELP_STR)
+{
+  int type;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int first = 1;
+
+  type = proto_redistnum (AFI_IP, argv[0]);
+  if (type < 0)
+    {
+      vty_out (vty, "Unknown route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show matched type IPv4 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          if (rib->type == type)
+            {
+              if (first)
+                {
+                  vty_out (vty, SHOW_ROUTE_V4_HEADER);
+                  first = 0;
+                }
+              vty_show_ip_route (vty, rn, rib);
+            }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_addr_vrf_all,
+       show_ip_route_addr_vrf_all_cmd,
+       "show ip route A.B.C.D " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Network in the IP routing table to display\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      rn = route_node_match (table, (struct prefix *) &p);
+      if (! rn)
+        continue;
+
+      vty_show_ip_route_detail (vty, rn, 0);
+
+      route_unlock_node (rn);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_prefix_vrf_all,
+       show_ip_route_prefix_vrf_all_cmd,
+       "show ip route A.B.C.D/M " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  int ret;
+  struct prefix_ipv4 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  ret = str2prefix_ipv4 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL)
+        continue;
+
+      rn = route_node_match (table, (struct prefix *) &p);
+      if (! rn)
+        continue;
+      if (rn->p.prefixlen != p.prefixlen)
+        {
+          route_unlock_node (rn);
+          continue;
+        }
+
+      vty_show_ip_route_detail (vty, rn, 0);
+
+      route_unlock_node (rn);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_summary_vrf_all,
+       show_ip_route_summary_vrf_all_cmd,
+       "show ip route summary " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      vty_show_ip_route_summary (vty, zvrf->table[AFI_IP][SAFI_UNICAST]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_route_summary_prefix_vrf_all,
+       show_ip_route_summary_prefix_vrf_all_cmd,
+       "show ip route summary prefix " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       "Summary of all routes\n"
+       "Prefix routes\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      vty_show_ip_route_summary_prefix (vty, zvrf->table[AFI_IP][SAFI_UNICAST]);
+
+  return CMD_SUCCESS;
+}
+
+/* Write IPv4 static route configuration. */
+static int
+static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd)
+{
+  struct route_node *rn;
+  struct static_route *si;  
+  struct route_table *stable;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int write;
+
+  write = 0;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (stable = zvrf->stable[AFI_IP][safi]) == NULL)
+        continue;
+
+      for (rn = route_top (stable); rn; rn = route_next (rn))
+        for (si = rn->info; si; si = si->next)
+          {
+            vty_out (vty, "%s %s/%d", cmd, inet_ntoa (rn->p.u.prefix4),
+                     rn->p.prefixlen);
+
+            switch (si->type)
+              {
+                case STATIC_IPV4_GATEWAY:
+                  vty_out (vty, " %s", inet_ntoa (si->addr.ipv4));
+                  break;
+                case STATIC_IPV4_IFNAME:
+                  vty_out (vty, " %s", si->ifname);
+                  break;
+                case STATIC_IPV4_BLACKHOLE:
+                  vty_out (vty, " Null0");
+                  break;
+              }
+
+            /* flags are incompatible with STATIC_IPV4_BLACKHOLE */
+            if (si->type != STATIC_IPV4_BLACKHOLE)
+              {
+                if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT))
+                  vty_out (vty, " %s", "reject");
+
+                if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE))
+                  vty_out (vty, " %s", "blackhole");
+              }
+
+           if (si->tag)
+             vty_out (vty, " tag %d", si->tag);
+
+            if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT)
+              vty_out (vty, " %d", si->distance);
+
+            if (si->vrf_id != VRF_DEFAULT)
+              vty_out (vty, " vrf %u", si->vrf_id);
+
+            vty_out (vty, "%s", VTY_NEWLINE);
+
+            write = 1;
+          }
+    }
+  return write;
+}
+
+DEFUN (show_ip_protocol,
+       show_ip_protocol_cmd,
+       "show ip protocol",
+        SHOW_STR
+        IP_STR
+       "IP protocol filtering status\n")
+{
+    int i; 
+
+    vty_out(vty, "Protocol    : route-map %s", VTY_NEWLINE);
+    vty_out(vty, "------------------------%s", VTY_NEWLINE);
+    for (i=0;i<ZEBRA_ROUTE_MAX;i++)
+    {
+        if (proto_rm[AFI_IP][i])
+          vty_out (vty, "%-10s  : %-10s%s", zebra_route_string(i),
+                                       proto_rm[AFI_IP][i],
+                                       VTY_NEWLINE);
+        else
+          vty_out (vty, "%-10s  : none%s", zebra_route_string(i), VTY_NEWLINE);
+    }
+    if (proto_rm[AFI_IP][i])
+      vty_out (vty, "%-10s  : %-10s%s", "any", proto_rm[AFI_IP][i],
+                                       VTY_NEWLINE);
+    else
+      vty_out (vty, "%-10s  : none%s", "any", VTY_NEWLINE);
+
+    return CMD_SUCCESS;
+}
+
+/* General fucntion for IPv6 static route. */
+static int
+static_ipv6_func (struct vty *vty, int add_cmd, const char *dest_str,
+                 const char *gate_str, const char *ifname,
+                 const char *flag_str, const char *tag_str,
+                 const char *distance_str, const char *vrf_id_str)
+{
+  int ret;
+  u_char distance;
+  struct prefix p;
+  struct in6_addr *gate = NULL;
+  struct in6_addr gate_addr;
+  u_char type = 0;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  u_char flag = 0;
+  route_tag_t tag = 0;
+  
+  ret = str2prefix (dest_str, &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Apply mask for given prefix. */
+  apply_mask (&p);
+
+  /* Route flags */
+  if (flag_str) {
+    switch(flag_str[0]) {
+      case 'r':
+      case 'R': /* XXX */
+        SET_FLAG (flag, ZEBRA_FLAG_REJECT);
+        break;
+      case 'b':
+      case 'B': /* XXX */
+        SET_FLAG (flag, ZEBRA_FLAG_BLACKHOLE);
+        break;
+      default:
+        vty_out (vty, "%% Malformed flag %s %s", flag_str, VTY_NEWLINE);
+        return CMD_WARNING;
+    }
+  }
+
+  /* Administrative distance. */
+  if (distance_str)
+    distance = atoi (distance_str);
+  else
+    distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
+
+  /* tag */
+  if (tag_str)
+    tag = atoi (tag_str);
+
+  /* tag */
+  if (tag_str)
+    tag = atoi(tag_str);
+
+  /* When gateway is valid IPv6 addrees, then gate is treated as
+     nexthop address other case gate is treated as interface name. */
+  ret = inet_pton (AF_INET6, gate_str, &gate_addr);
+
+  if (ifname)
+    {
+      /* When ifname is specified.  It must be come with gateway
+         address. */
+      if (ret != 1)
+       {
+         vty_out (vty, "%% Malformed address%s", VTY_NEWLINE);
+         return CMD_WARNING;
+       }
+      type = STATIC_IPV6_GATEWAY_IFNAME;
+      gate = &gate_addr;
+    }
+  else
+    {
+      if (ret == 1)
+       {
+         type = STATIC_IPV6_GATEWAY;
+         gate = &gate_addr;
+       }
+      else
+       {
+         type = STATIC_IPV6_IFNAME;
+         ifname = gate_str;
+       }
+    }
+
+  /* VRF id */
+  if (vrf_id_str)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, vrf_id_str);
+
+  if (add_cmd)
+    static_add_ipv6 (&p, type, gate, ifname, flag, tag, distance, vrf_id);
+  else
+    static_delete_ipv6 (&p, type, gate, ifname, tag, distance, vrf_id);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_route,
+       ipv6_route_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL,
+                           NULL, NULL);
+}
+
+DEFUN (ipv6_route_tag,
+       ipv6_route_tag_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL);
+}
+
+DEFUN (ipv6_route_tag_vrf,
+       ipv6_route_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]);
+}
+
+DEFUN (ipv6_route_flags,
+       ipv6_route_flags_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL,
+                           NULL, NULL);
+}
+
+DEFUN (ipv6_route_flags_tag,
+       ipv6_route_flags_tag_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL);
+}
+
+DEFUN (ipv6_route_flags_tag_vrf,
+       ipv6_route_flags_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]);
+}
+
+DEFUN (ipv6_route_ifname,
+       ipv6_route_ifname_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL,
+                           NULL, NULL);
+}
+
+DEFUN (ipv6_route_ifname_tag,
+       ipv6_route_ifname_tag_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL);
+}
+
+DEFUN (ipv6_route_ifname_tag_vrf,
+       ipv6_route_ifname_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]);
+}
+
+DEFUN (ipv6_route_ifname_flags,
+       ipv6_route_ifname_flags_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL,
+                           NULL, NULL);
+}
+
+DEFUN (ipv6_route_ifname_flags_tag,
+       ipv6_route_ifname_flags_tag_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL);
+}
+
+DEFUN (ipv6_route_ifname_flags_tag_vrf,
+       ipv6_route_ifname_flags_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]);
+}
+
+DEFUN (ipv6_route_pref,
+       ipv6_route_pref_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2],
+                           NULL);
+}
+
+DEFUN (ipv6_route_pref_tag,
+       ipv6_route_pref_tag_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL);
+}
+
+DEFUN (ipv6_route_pref_tag_vrf,
+       ipv6_route_pref_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]);
+}
+
+DEFUN (ipv6_route_flags_pref,
+       ipv6_route_flags_pref_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3],
+                           NULL);
+}
+
+DEFUN (ipv6_route_flags_pref_tag,
+       ipv6_route_flags_pref_tag_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (ipv6_route_flags_pref_tag_vrf,
+       ipv6_route_flags_pref_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]);
+}
+
+DEFUN (ipv6_route_ifname_pref,
+       ipv6_route_ifname_pref_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3],
+                           NULL);
+}
+
+DEFUN (ipv6_route_ifname_pref_tag,
+       ipv6_route_ifname_pref_tag_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL);
+}
+
+DEFUN (ipv6_route_ifname_pref_tag_vrf,
+       ipv6_route_ifname_pref_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]);
+}
+
+DEFUN (ipv6_route_ifname_flags_pref,
+       ipv6_route_ifname_flags_pref_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4],
+                           NULL);
+}
+
+DEFUN (ipv6_route_ifname_flags_pref_tag,
+       ipv6_route_ifname_flags_pref_tag_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>",
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL);
+}
+
+DEFUN (ipv6_route_ifname_flags_pref_tag_vrf,
+       ipv6_route_ifname_flags_pref_tag_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
+}
+
+DEFUN (no_ipv6_route,
+       no_ipv6_route_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL,
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_tag,
+       no_ipv6_route_tag_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL);
+}
+
+DEFUN (no_ipv6_route_tag_vrf,
+       no_ipv6_route_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]);
+}
+
+ALIAS (no_ipv6_route,
+       no_ipv6_route_flags_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+
+ALIAS (no_ipv6_route_tag,
+       no_ipv6_route_flags_tag_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+
+DEFUN (no_ipv6_route_ifname,
+       no_ipv6_route_ifname_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL,
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_tag,
+       no_ipv6_route_ifname_tag_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_tag_vrf,
+       no_ipv6_route_ifname_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]);
+}
+
+ALIAS (no_ipv6_route_ifname,
+       no_ipv6_route_ifname_flags_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n")
+
+ALIAS (no_ipv6_route_ifname_tag,
+       no_ipv6_route_ifname_flags_tag_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n")
+
+DEFUN (no_ipv6_route_pref,
+       no_ipv6_route_pref_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2],
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_pref_tag,
+       no_ipv6_route_pref_tag_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL);
+}
+
+DEFUN (no_ipv6_route_pref_tag_vrf,
+       no_ipv6_route_pref_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]);
+}
+
+DEFUN (no_ipv6_route_flags_pref,
+       no_ipv6_route_flags_pref_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n")
+{
+  /* We do not care about argv[2] */
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3],
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_flags_pref_tag,
+       no_ipv6_route_flags_pref_tag_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  /* We do not care about argv[2] */
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ipv6_route_flags_pref_tag_vrf,
+       no_ipv6_route_flags_pref_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  /* We do not care about argv[2] */
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ipv6_route_ifname_pref,
+       no_ipv6_route_ifname_pref_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3],
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_pref_tag,
+       no_ipv6_route_ifname_pref_tag_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_pref_tag_vrf,
+       no_ipv6_route_ifname_pref_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]);
+}
+
+DEFUN (no_ipv6_route_ifname_flags_pref,
+       no_ipv6_route_ifname_flags_pref_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4],
+                           NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_flags_pref_tag,
+       no_ipv6_route_ifname_flags_pref_tag_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>",
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n")
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL);
+}
+
+DEFUN (no_ipv6_route_ifname_flags_pref_tag_vrf,
+       no_ipv6_route_ifname_flags_pref_tag_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Set tag for this route\n"
+       "Tag value\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
+}
+
+DEFUN (ipv6_route_vrf,
+       ipv6_route_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL,
+                           argv[2]);
+}
+
+DEFUN (ipv6_route_flags_vrf,
+       ipv6_route_flags_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL,
+                           argv[3]);
+}
+
+DEFUN (ipv6_route_ifname_vrf,
+       ipv6_route_ifname_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL,
+                           argv[3]);
+}
+
+DEFUN (ipv6_route_ifname_flags_vrf,
+       ipv6_route_ifname_flags_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL,
+                           argv[4]);
+}
+
+DEFUN (ipv6_route_pref_vrf,
+       ipv6_route_pref_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2],
+                           argv[3]);
+}
+
+DEFUN (ipv6_route_flags_pref_vrf,
+       ipv6_route_flags_pref_vrf_cmd,
+       "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3],
+                           argv[4]);
+}
+
+DEFUN (ipv6_route_ifname_pref_vrf,
+       ipv6_route_ifname_pref_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3],
+                           argv[4]);
+}
+
+DEFUN (ipv6_route_ifname_flags_pref_vrf,
+       ipv6_route_ifname_flags_pref_vrf_cmd,
+       "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255> " VRF_CMD_STR,
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4],
+                           argv[5]);
+}
+
+DEFUN (no_ipv6_route_vrf,
+       no_ipv6_route_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL,
+                           (argc > 3) ? argv[3] : argv[2]);
+}
+
+ALIAS (no_ipv6_route_vrf,
+       no_ipv6_route_flags_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_ipv6_route_ifname_vrf,
+       no_ipv6_route_ifname_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL,
+                           (argc > 4) ? argv[4] : argv[3]);
+}
+
+ALIAS (no_ipv6_route_ifname_vrf,
+       no_ipv6_route_ifname_flags_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (no_ipv6_route_pref_vrf,
+       no_ipv6_route_pref_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2],
+                           argv[3]);
+}
+
+DEFUN (no_ipv6_route_flags_pref_vrf,
+       no_ipv6_route_flags_pref_vrf_cmd,
+       "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  /* We do not care about argv[2] */
+  return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3],
+                           argv[4]);
+}
+
+DEFUN (no_ipv6_route_ifname_pref_vrf,
+       no_ipv6_route_ifname_pref_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3],
+                           argv[4]);
+}
+
+DEFUN (no_ipv6_route_ifname_flags_pref_vrf,
+       no_ipv6_route_ifname_flags_pref_vrf_cmd,
+       "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255> " VRF_CMD_STR,
+       NO_STR
+       IP_STR
+       "Establish static routes\n"
+       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+       "IPv6 gateway address\n"
+       "IPv6 gateway interface name\n"
+       "Emit an ICMP unreachable when matched\n"
+       "Silently discard pkts when matched\n"
+       "Distance value for this prefix\n"
+       VRF_CMD_HELP_STR)
+{
+  return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4],
+                           argv[5]);
+}
+
+DEFUN (show_ipv6_route,
+       show_ipv6_route_cmd,
+       "show ipv6 route",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show all IPv6 route. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+       if (first)
+         {
+           vty_out (vty, SHOW_ROUTE_V6_HEADER);
+           first = 0;
+         }
+       vty_show_ip_route (vty, rn, rib);
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route,
+       show_ipv6_route_vrf_cmd,
+       "show ipv6 route " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_tag,
+       show_ipv6_route_tag_cmd,
+       "show ipv6 route tag <1-4294967295>",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Show only routes with tag\n"
+       "Tag value\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  route_tag_t tag = 0;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argv[0])
+    tag = atoi(argv[0]);
+
+  if (argc == 2)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show all IPv6 routes with matching tag value. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+        if (rib->tag != tag)
+          continue;
+
+       if (first)
+         {
+           vty_out (vty, SHOW_ROUTE_V6_HEADER);
+           first = 0;
+         }
+       vty_show_ip_route (vty, rn, rib);
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_tag,
+       show_ipv6_route_tag_vrf_cmd,
+       "show ipv6 route tag <1-4294967295>" VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Show only routes with tag\n"
+       "Tag value\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_prefix_longer,
+       show_ipv6_route_prefix_longer_cmd,
+       "show ipv6 route X:X::X:X/M longer-prefixes",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n"
+       "Show route matching the specified Network/Mask pair only\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct prefix p;
+  int ret;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix (argv[0], &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show matched type IPv6 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      if (prefix_match (&p, &rn->p))
+       {
+         if (first)
+           {
+             vty_out (vty, SHOW_ROUTE_V6_HEADER);
+             first = 0;
+           }
+         vty_show_ip_route (vty, rn, rib);
+       }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_prefix_longer,
+       show_ipv6_route_prefix_longer_vrf_cmd,
+       "show ipv6 route X:X::X:X/M longer-prefixes " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n"
+       "Show route matching the specified Network/Mask pair only\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_protocol,
+       show_ipv6_route_protocol_cmd,
+       "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP6_REDIST_HELP_STR_ZEBRA)
+{
+  int type;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0)
+    {
+      vty_out (vty, "Unknown route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show matched type IPv6 routes. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      if (rib->type == type)
+       {
+         if (first)
+           {
+             vty_out (vty, SHOW_ROUTE_V6_HEADER);
+             first = 0;
+           }
+         vty_show_ip_route (vty, rn, rib);
+       }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_protocol,
+       show_ipv6_route_protocol_vrf_cmd,
+       "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA " " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP6_REDIST_HELP_STR_ZEBRA
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_addr,
+       show_ipv6_route_addr_cmd,
+       "show ipv6 route X:X::X:X",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 Address\n")
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix_ipv6 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (! rn)
+    {
+      vty_out (vty, "%% Network not in table%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vty_show_ip_route_detail (vty, rn, 0);
+
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_addr,
+       show_ipv6_route_addr_vrf_cmd,
+       "show ipv6 route X:X::X:X " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 Address\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_prefix,
+       show_ipv6_route_prefix_cmd,
+       "show ipv6 route X:X::X:X/M",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n")
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  ret = str2prefix_ipv6 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (argc > 1)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (! rn || rn->p.prefixlen != p.prefixlen)
+    {
+      vty_out (vty, "%% Network not in table%s", VTY_NEWLINE);
+      if (rn)
+        route_unlock_node (rn);
+      return CMD_WARNING;
+    }
+
+  vty_show_ip_route_detail (vty, rn, 0);
+
+  route_unlock_node (rn);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_prefix,
+       show_ipv6_route_prefix_vrf_cmd,
+       "show ipv6 route X:X::X:X/M " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n"
+       VRF_CMD_HELP_STR)
+
+/* Show route summary.  */
+DEFUN (show_ipv6_route_summary,
+       show_ipv6_route_summary_cmd,
+       "show ipv6 route summary",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n")
+{
+  struct route_table *table;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  vty_show_ip_route_summary (vty, table);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_summary,
+       show_ipv6_route_summary_vrf_cmd,
+       "show ipv6 route summary " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n"
+       VRF_CMD_HELP_STR)
+
+/* Show ipv6 route summary prefix.  */
+DEFUN (show_ipv6_route_summary_prefix,
+       show_ipv6_route_summary_prefix_cmd,
+       "show ipv6 route summary prefix",
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n"
+       "Prefix routes\n")
+{
+  struct route_table *table;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  vty_show_ip_route_summary_prefix (vty, table);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_route_summary_prefix,
+       show_ipv6_route_summary_prefix_vrf_cmd,
+       "show ipv6 route summary prefix " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n"
+       "Prefix routes\n"
+       VRF_CMD_HELP_STR)
+
+/*
+ * Show IPv6 mroute command.Used to dump
+ * the Multicast routing table.
+ */
+
+DEFUN (show_ipv6_mroute,
+       show_ipv6_mroute_cmd,
+       "show ipv6 mroute",
+       SHOW_STR
+       IP_STR
+       "IPv6 Multicast routing table\n")
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  int first = 1;
+  vrf_id_t vrf_id = VRF_DEFAULT;
+
+  if (argc > 0)
+    VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_MULTICAST, vrf_id);
+  if (! table)
+    return CMD_SUCCESS;
+
+  /* Show all IPv6 route. */
+  for (rn = route_top (table); rn; rn = route_next (rn))
+    RNODE_FOREACH_RIB (rn, rib)
+      {
+       if (first)
+         {
+          vty_out (vty, SHOW_ROUTE_V6_HEADER);
+           first = 0;
+         }
+       vty_show_ip_route (vty, rn, rib);
+      }
+  return CMD_SUCCESS;
+}
+
+ALIAS (show_ipv6_mroute,
+       show_ipv6_mroute_vrf_cmd,
+       "show ipv6 mroute " VRF_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 Multicast routing table\n"
+       VRF_CMD_HELP_STR)
+
+DEFUN (show_ipv6_route_vrf_all,
+       show_ipv6_route_vrf_all_cmd,
+       "show ipv6 route " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int first = 1;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show all IPv6 route. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          {
+            if (first)
+              {
+                vty_out (vty, SHOW_ROUTE_V6_HEADER);
+                first = 0;
+              }
+            vty_show_ip_route (vty, rn, rib);
+          }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_route_prefix_longer_vrf_all,
+       show_ipv6_route_prefix_longer_vrf_all_cmd,
+       "show ipv6 route X:X::X:X/M longer-prefixes " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n"
+       "Show route matching the specified Network/Mask pair only\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct prefix p;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int ret;
+  int first = 1;
+
+  ret = str2prefix (argv[0], &p);
+  if (! ret)
+    {
+      vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show matched type IPv6 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          if (prefix_match (&p, &rn->p))
+            {
+              if (first)
+                {
+                  vty_out (vty, SHOW_ROUTE_V6_HEADER);
+                  first = 0;
+                }
+              vty_show_ip_route (vty, rn, rib);
+            }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_route_protocol_vrf_all,
+       show_ipv6_route_protocol_vrf_all_cmd,
+       "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA " " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IP routing table\n"
+       QUAGGA_IP6_REDIST_HELP_STR_ZEBRA
+       VRF_ALL_CMD_HELP_STR)
+{
+  int type;
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int first = 1;
+
+  type = proto_redistnum (AFI_IP6, argv[0]);
+  if (type < 0)
+    {
+      vty_out (vty, "Unknown route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show matched type IPv6 routes. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          if (rib->type == type)
+            {
+              if (first)
+                {
+                  vty_out (vty, SHOW_ROUTE_V6_HEADER);
+                  first = 0;
+                }
+              vty_show_ip_route (vty, rn, rib);
+            }
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_route_addr_vrf_all,
+       show_ipv6_route_addr_vrf_all_cmd,
+       "show ipv6 route X:X::X:X " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 Address\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  ret = str2prefix_ipv6 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      rn = route_node_match (table, (struct prefix *) &p);
+      if (! rn)
+        continue;
+
+      vty_show_ip_route_detail (vty, rn, 0);
+
+      route_unlock_node (rn);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_route_prefix_vrf_all,
+       show_ipv6_route_prefix_vrf_all_cmd,
+       "show ipv6 route X:X::X:X/M " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "IPv6 prefix\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  int ret;
+  struct prefix_ipv6 p;
+  struct route_table *table;
+  struct route_node *rn;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  ret = str2prefix_ipv6 (argv[0], &p);
+  if (ret <= 0)
+    {
+      vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      rn = route_node_match (table, (struct prefix *) &p);
+      if (! rn)
+        continue;
+      if (rn->p.prefixlen != p.prefixlen)
+        {
+          route_unlock_node (rn);
+          continue;
+        }
+
+      vty_show_ip_route_detail (vty, rn, 0);
+
+      route_unlock_node (rn);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* Show route summary.  */
+DEFUN (show_ipv6_route_summary_vrf_all,
+       show_ipv6_route_summary_vrf_all_cmd,
+       "show ipv6 route summary " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      vty_show_ip_route_summary (vty, zvrf->table[AFI_IP6][SAFI_UNICAST]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_mroute_vrf_all,
+       show_ipv6_mroute_vrf_all_cmd,
+       "show ipv6 mroute " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 Multicast routing table\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *rib;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+  int first = 1;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      /* Show all IPv6 route. */
+      for (rn = route_top (table); rn; rn = route_next (rn))
+        RNODE_FOREACH_RIB (rn, rib)
+          {
+           if (first)
+             {
+               vty_out (vty, SHOW_ROUTE_V6_HEADER);
+               first = 0;
+             }
+           vty_show_ip_route (vty, rn, rib);
+          }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_route_summary_prefix_vrf_all,
+       show_ipv6_route_summary_prefix_vrf_all_cmd,
+       "show ipv6 route summary prefix " VRF_ALL_CMD_STR,
+       SHOW_STR
+       IP_STR
+       "IPv6 routing table\n"
+       "Summary of all IPv6 routes\n"
+       "Prefix routes\n"
+       VRF_ALL_CMD_HELP_STR)
+{
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    if ((zvrf = vrf_iter2info (iter)) != NULL)
+      vty_show_ip_route_summary_prefix (vty, zvrf->table[AFI_IP6][SAFI_UNICAST]);
+
+  return CMD_SUCCESS;
+}
+
+/* Write IPv6 static route configuration. */
+static int
+static_config_ipv6 (struct vty *vty)
+{
+  struct route_node *rn;
+  struct static_route *si;  
+  int write;
+  char buf[BUFSIZ];
+  struct route_table *stable;
+  struct zebra_vrf *zvrf;
+  vrf_iter_t iter;
+
+  write = 0;
+
+  for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter))
+    {
+      if ((zvrf = vrf_iter2info (iter)) == NULL ||
+          (stable = zvrf->stable[AFI_IP6][SAFI_UNICAST]) == NULL)
+        continue;
+
+      for (rn = route_top (stable); rn; rn = route_next (rn))
+        for (si = rn->info; si; si = si->next)
+          {
+            vty_out (vty, "ipv6 route %s", prefix2str (&rn->p, buf, sizeof buf));
+
+            switch (si->type)
+              {
+              case STATIC_IPV6_GATEWAY:
+                vty_out (vty, " %s",
+                         inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ));
+                break;
+              case STATIC_IPV6_IFNAME:
+                vty_out (vty, " %s", si->ifname);
+                break;
+              case STATIC_IPV6_GATEWAY_IFNAME:
+                vty_out (vty, " %s %s",
+                         inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ),
+                         si->ifname);
+                break;
+              }
+
+            if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT))
+              vty_out (vty, " %s", "reject");
+
+            if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE))
+              vty_out (vty, " %s", "blackhole");
+
+           if (si->tag)
+             vty_out (vty, " tag %d", si->tag);
+
+            if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT)
+              vty_out (vty, " %d", si->distance);
+
+            if (si->vrf_id != VRF_DEFAULT)
+              vty_out (vty, " vrf %u", si->vrf_id);
+
+            vty_out (vty, "%s", VTY_NEWLINE);
+
+            write = 1;
+          }
+    }
+  return write;
+}
+
+/* Static ip route configuration write function. */
+static int
+zebra_ip_config (struct vty *vty)
+{
+  int write = 0;
+
+  write += static_config_ipv4 (vty, SAFI_UNICAST, "ip route");
+  write += static_config_ipv4 (vty, SAFI_MULTICAST, "ip mroute");
+#ifdef HAVE_IPV6
+  write += static_config_ipv6 (vty);
+#endif /* HAVE_IPV6 */
+
+  return write;
+}
+
+static int config_write_vty(struct vty *vty)
+{
+  int i;
+  enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get ();
+
+  if (ipv4_multicast_mode != MCAST_NO_CONFIG)
+    vty_out (vty, "ip multicast rpf-lookup-mode %s%s",
+             ipv4_multicast_mode == MCAST_URIB_ONLY ? "urib-only" :
+             ipv4_multicast_mode == MCAST_MRIB_ONLY ? "mrib-only" :
+             ipv4_multicast_mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib" :
+             ipv4_multicast_mode == MCAST_MIX_DISTANCE ? "lower-distance" :
+             "longer-prefix",
+             VTY_NEWLINE);
+
+  for (i=0;i<ZEBRA_ROUTE_MAX;i++)
+    {
+      if (proto_rm[AFI_IP][i])
+        vty_out (vty, "ip protocol %s route-map %s%s", zebra_route_string(i),
+                 proto_rm[AFI_IP][i], VTY_NEWLINE);
+    }
+  if (proto_rm[AFI_IP][ZEBRA_ROUTE_MAX])
+      vty_out (vty, "ip protocol %s route-map %s%s", "any",
+               proto_rm[AFI_IP][ZEBRA_ROUTE_MAX], VTY_NEWLINE);
+
+  return 1;
+}   
+
+/* table node for protocol filtering */
+static struct cmd_node protocol_node = { PROTOCOL_NODE, "", 1 };
+
+/* IP node for static routes. */
+static struct cmd_node ip_node = { IP_NODE,  "",  1 };
+
+/* Route VTY.  */
+void
+zebra_vty_init (void)
+{
+  install_node (&ip_node, zebra_ip_config);
+  install_node (&protocol_node, config_write_vty);
+
+  install_element (CONFIG_NODE, &ip_mroute_cmd);
+  install_element (CONFIG_NODE, &ip_mroute_dist_cmd);
+  install_element (CONFIG_NODE, &no_ip_mroute_cmd);
+  install_element (CONFIG_NODE, &no_ip_mroute_dist_cmd);
+  install_element (CONFIG_NODE, &ip_multicast_mode_cmd);
+  install_element (CONFIG_NODE, &no_ip_multicast_mode_cmd);
+  install_element (CONFIG_NODE, &no_ip_multicast_mode_noarg_cmd);
+  install_element (CONFIG_NODE, &ip_protocol_cmd);
+  install_element (CONFIG_NODE, &no_ip_protocol_cmd);
+  install_element (VIEW_NODE, &show_ip_protocol_cmd);
+  install_element (CONFIG_NODE, &ip_route_cmd);
+  install_element (CONFIG_NODE, &ip_route_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags2_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags2_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags2_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags2_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags2_tag_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags2_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags2_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags2_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags2_tag_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags2_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_tag_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_distance2_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_distance2_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_tag_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_tag_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_distance_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_distance2_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_distance2_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_tag_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_tag_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_tag_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_distance2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_tag_distance2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_tag_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_tag_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_tag_distance_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_tag_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_distance2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_tag_distance2_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_tag_distance2_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ip_route_cmd);
+  install_element (VIEW_NODE, &show_ip_route_tag_cmd);
+  install_element (VIEW_NODE, &show_ip_route_tag_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_nht_cmd);
+  install_element (VIEW_NODE, &show_ipv6_nht_cmd);
+  install_element (VIEW_NODE, &show_ip_route_addr_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_longer_cmd);
+  install_element (VIEW_NODE, &show_ip_route_protocol_cmd);
+  install_element (VIEW_NODE, &show_ip_route_supernets_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_prefix_cmd);
+
+  install_element (VIEW_NODE, &show_ip_rpf_cmd);
+  install_element (VIEW_NODE, &show_ip_rpf_addr_cmd);
+
+  /* Commands for VRF */
+
+  install_element (CONFIG_NODE, &ip_mroute_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_mroute_dist_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_mroute_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_mroute_dist_vrf_cmd);
+
+  install_element (CONFIG_NODE, &ip_route_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags2_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags2_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_flags_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &ip_route_mask_flags_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_flags_distance2_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_distance_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ip_route_mask_flags_distance2_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ip_route_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_addr_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_longer_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_protocol_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_supernets_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_prefix_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ip_route_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_addr_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_prefix_longer_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_protocol_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_supernets_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_route_summary_prefix_vrf_all_cmd);
+
+  install_element (VIEW_NODE, &show_ip_rpf_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_rpf_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ip_rpf_addr_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_rpf_addr_vrf_all_cmd);
+
+  install_element (CONFIG_NODE, &ipv6_route_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_pref_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_pref_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_pref_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_pref_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_pref_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_pref_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_pref_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_pref_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_pref_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_pref_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_pref_tag_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_pref_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_pref_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_tag_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_pref_tag_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_pref_tag_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_tag_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_tag_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_protocol_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_addr_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_longer_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_mroute_cmd);
+
+  /* Commands for VRF */
+
+  install_element (CONFIG_NODE, &ipv6_route_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_flags_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &ipv6_route_ifname_flags_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_flags_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_vrf_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_pref_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_route_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_prefix_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_protocol_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_addr_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_longer_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_route_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_summary_prefix_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_protocol_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_addr_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_vrf_all_cmd);
+  install_element (VIEW_NODE, &show_ipv6_route_prefix_longer_vrf_all_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_mroute_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd);
+  install_element (ENABLE_NODE, &show_ipv6_mroute_vrf_all_cmd);
+}
diff --git a/zebra/zserv.c b/zebra/zserv.c
new file mode 100644 (file)
index 0000000..a96bcb1
--- /dev/null
@@ -0,0 +1,2283 @@
+/* Zebra daemon server routine.
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "command.h"
+#include "if.h"
+#include "thread.h"
+#include "stream.h"
+#include "memory.h"
+#include "table.h"
+#include "rib.h"
+#include "network.h"
+#include "sockunion.h"
+#include "log.h"
+#include "zclient.h"
+#include "privs.h"
+#include "network.h"
+#include "buffer.h"
+#include "vrf.h"
+#include "nexthop.h"
+
+#include "zebra/zserv.h"
+#include "zebra/router-id.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/ipforward.h"
+#include "zebra/zebra_rnh.h"
+
+/* Event list of zebra. */
+enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE };
+
+extern struct zebra_t zebrad;
+
+static void zebra_event (enum event event, int sock, struct zserv *client);
+
+extern struct zebra_privs_t zserv_privs;
+
+static void zebra_client_close (struct zserv *client);
+
+static int
+zserv_delayed_close(struct thread *thread)
+{
+  struct zserv *client = THREAD_ARG(thread);
+
+  client->t_suicide = NULL;
+  zebra_client_close(client);
+  return 0;
+}
+
+/* When client connects, it sends hello message
+ * with promise to send zebra routes of specific type.
+ * Zebra stores a socket fd of the client into
+ * this array. And use it to clean up routes that
+ * client didn't remove for some reasons after closing
+ * connection.
+ */
+static int route_type_oaths[ZEBRA_ROUTE_MAX];
+
+static int
+zserv_flush_data(struct thread *thread)
+{
+  struct zserv *client = THREAD_ARG(thread);
+
+  client->t_write = NULL;
+  if (client->t_suicide)
+    {
+      zebra_client_close(client);
+      return -1;
+    }
+  switch (buffer_flush_available(client->wb, client->sock))
+    {
+    case BUFFER_ERROR:
+      zlog_warn("%s: buffer_flush_available failed on zserv client fd %d, "
+               "closing", __func__, client->sock);
+      zebra_client_close(client);
+      break;
+    case BUFFER_PENDING:
+      client->t_write = thread_add_write(zebrad.master, zserv_flush_data,
+                                        client, client->sock);
+      break;
+    case BUFFER_EMPTY:
+      break;
+    }
+
+  client->last_write_time = quagga_time(NULL);
+  return 0;
+}
+
+int
+zebra_server_send_message(struct zserv *client)
+{
+  if (client->t_suicide)
+    return -1;
+
+  stream_set_getp(client->obuf, 0);
+  client->last_write_cmd = stream_getw_from(client->obuf, 4);
+  switch (buffer_write(client->wb, client->sock, STREAM_DATA(client->obuf),
+                      stream_get_endp(client->obuf)))
+    {
+    case BUFFER_ERROR:
+      zlog_warn("%s: buffer_write failed to zserv client fd %d, closing",
+                __func__, client->sock);
+      /* Schedule a delayed close since many of the functions that call this
+         one do not check the return code.  They do not allow for the
+        possibility that an I/O error may have caused the client to be
+        deleted. */
+      client->t_suicide = thread_add_event(zebrad.master, zserv_delayed_close,
+                                          client, 0);
+      return -1;
+    case BUFFER_EMPTY:
+      THREAD_OFF(client->t_write);
+      break;
+    case BUFFER_PENDING:
+      THREAD_WRITE_ON(zebrad.master, client->t_write,
+                     zserv_flush_data, client, client->sock);
+      break;
+    }
+
+  client->last_write_time = quagga_time(NULL);
+  return 0;
+}
+
+void
+zserv_create_header (struct stream *s, uint16_t cmd, vrf_id_t vrf_id)
+{
+  /* length placeholder, caller can update */
+  stream_putw (s, ZEBRA_HEADER_SIZE);
+  stream_putc (s, ZEBRA_HEADER_MARKER);
+  stream_putc (s, ZSERV_VERSION);
+  stream_putw (s, vrf_id);
+  stream_putw (s, cmd);
+}
+
+static void
+zserv_encode_interface (struct stream *s, struct interface *ifp)
+{
+  /* Interface information. */
+  stream_put (s, ifp->name, INTERFACE_NAMSIZ);
+  stream_putl (s, ifp->ifindex);
+  stream_putc (s, ifp->status);
+  stream_putq (s, ifp->flags);
+  stream_putl (s, ifp->metric);
+  stream_putl (s, ifp->mtu);
+  stream_putl (s, ifp->mtu6);
+  stream_putl (s, ifp->bandwidth);
+  stream_putl (s, ifp->ll_type);
+  stream_putl (s, ifp->hw_addr_len);
+  if (ifp->hw_addr_len)
+    stream_put (s, ifp->hw_addr, ifp->hw_addr_len);
+
+  zlog_info("Try to set TE Link Param");
+  /* Then, Traffic Engineering parameters if any */
+  if (HAS_LINK_PARAMS(ifp) && IS_LINK_PARAMS_SET(ifp->link_params))
+    {
+      stream_putc (s, 1);
+      zebra_interface_link_params_write (s, ifp);
+    }
+  else
+    stream_putc (s, 0);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+}
+
+/* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */
+/*
+ * This function is called in the following situations:
+ * - in response to a 3-byte ZEBRA_INTERFACE_ADD request
+ *   from the client.
+ * - at startup, when zebra figures out the available interfaces
+ * - when an interface is added (where support for
+ *   RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when
+ *   an interface is marked IFF_UP (i.e., an RTM_IFINFO message is
+ *   received)
+ */
+int
+zsend_interface_add (struct zserv *client, struct interface *ifp)
+{
+  struct stream *s;
+
+  /* Check this client need interface information. */
+  if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_INTERFACE_ADD, ifp->vrf_id);
+  zserv_encode_interface (s, ifp);
+
+  client->ifadd_cnt++;
+  return zebra_server_send_message(client);
+}
+
+/* Interface deletion from zebra daemon. */
+int
+zsend_interface_delete (struct zserv *client, struct interface *ifp)
+{
+  struct stream *s;
+
+  /* Check this client need interface information. */
+  if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id);
+  zserv_encode_interface (s, ifp);
+
+  client->ifdel_cnt++;
+  return zebra_server_send_message (client);
+}
+
+int
+zsend_interface_link_params (struct zserv *client, struct interface *ifp)
+{
+  struct stream *s;
+  
+  /* Check this client need interface information. */
+  if (! client->ifinfo)
+    return 0;
+  
+  if (!ifp->link_params)
+    return 0;
+  s = client->obuf;
+  stream_reset (s);
+  
+  zserv_create_header (s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id);
+  
+  /* Add Interface Index */
+  stream_putl (s, ifp->ifindex);
+
+  /* Then TE Link Parameters */
+  if (zebra_interface_link_params_write (s, ifp) == 0)
+    return 0;
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+  
+  return zebra_server_send_message (client);
+}
+
+/* Interface address is added/deleted. Send ZEBRA_INTERFACE_ADDRESS_ADD or
+ * ZEBRA_INTERFACE_ADDRESS_DELETE to the client. 
+ *
+ * A ZEBRA_INTERFACE_ADDRESS_ADD is sent in the following situations:
+ * - in response to a 3-byte ZEBRA_INTERFACE_ADD request
+ *   from the client, after the ZEBRA_INTERFACE_ADD has been
+ *   sent from zebra to the client
+ * - redistribute new address info to all clients in the following situations
+ *    - at startup, when zebra figures out the available interfaces
+ *    - when an interface is added (where support for
+ *      RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when
+ *      an interface is marked IFF_UP (i.e., an RTM_IFINFO message is
+ *      received)
+ *    - for the vty commands "ip address A.B.C.D/M [<secondary>|<label LINE>]"
+ *      and "no bandwidth <1-10000000>", "ipv6 address X:X::X:X/M"
+ *    - when an RTM_NEWADDR message is received from the kernel,
+ * 
+ * The call tree that triggers ZEBRA_INTERFACE_ADDRESS_DELETE: 
+ *
+ *                   zsend_interface_address(DELETE)
+ *                           ^                         
+ *                           |                        
+ *          zebra_interface_address_delete_update    
+ *             ^                        ^      ^
+ *             |                        |      if_delete_update
+ *             |                        |
+ *         ip_address_uninstall        connected_delete_ipv4
+ *         [ipv6_addresss_uninstall]   [connected_delete_ipv6]
+ *             ^                        ^
+ *             |                        |
+ *             |                  RTM_NEWADDR on routing/netlink socket
+ *             |
+ *         vty commands:
+ *     "no ip address A.B.C.D/M [label LINE]"
+ *     "no ip address A.B.C.D/M secondary"
+ *     ["no ipv6 address X:X::X:X/M"]
+ *
+ */
+int
+zsend_interface_address (int cmd, struct zserv *client, 
+                         struct interface *ifp, struct connected *ifc)
+{
+  int blen;
+  struct stream *s;
+  struct prefix *p;
+
+  /* Check this client need interface information. */
+  if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+  
+  zserv_create_header (s, cmd, ifp->vrf_id);
+  stream_putl (s, ifp->ifindex);
+
+  /* Interface address flag. */
+  stream_putc (s, ifc->flags);
+
+  /* Prefix information. */
+  p = ifc->address;
+  stream_putc (s, p->family);
+  blen = prefix_blen (p);
+  stream_put (s, &p->u.prefix, blen);
+
+  /* 
+   * XXX gnu version does not send prefixlen for ZEBRA_INTERFACE_ADDRESS_DELETE
+   * but zebra_interface_address_delete_read() in the gnu version 
+   * expects to find it
+   */
+  stream_putc (s, p->prefixlen);
+
+  /* Destination. */
+  p = ifc->destination;
+  if (p)
+    stream_put (s, &p->u.prefix, blen);
+  else
+    stream_put (s, NULL, blen);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  client->connected_rt_add_cnt++;
+  return zebra_server_send_message(client);
+}
+
+/*
+ * The cmd passed to zsend_interface_update  may be ZEBRA_INTERFACE_UP or
+ * ZEBRA_INTERFACE_DOWN.
+ *
+ * The ZEBRA_INTERFACE_UP message is sent from the zebra server to
+ * the clients in one of 2 situations:
+ *   - an if_up is detected e.g., as a result of an RTM_IFINFO message
+ *   - a vty command modifying the bandwidth of an interface is received.
+ * The ZEBRA_INTERFACE_DOWN message is sent when an if_down is detected.
+ */
+int
+zsend_interface_update (int cmd, struct zserv *client, struct interface *ifp)
+{
+  struct stream *s;
+
+  /* Check this client need interface information. */
+  if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, cmd, ifp->vrf_id);
+  zserv_encode_interface (s, ifp);
+
+  if (cmd == ZEBRA_INTERFACE_UP)
+    client->ifup_cnt++;
+  else
+    client->ifdown_cnt++;
+
+  return zebra_server_send_message(client);
+}
+
+/*
+ * The zebra server sends the clients  a ZEBRA_IPV4_ROUTE_ADD or a
+ * ZEBRA_IPV6_ROUTE_ADD via zsend_route_multipath in the following
+ * situations:
+ * - when the client starts up, and requests default information
+ *   by sending a ZEBRA_REDISTRIBUTE_DEFAULT_ADD to the zebra server, in the
+ * - case of rip, ripngd, ospfd and ospf6d, when the client sends a
+ *   ZEBRA_REDISTRIBUTE_ADD as a result of the "redistribute" vty cmd,
+ * - when the zebra server redistributes routes after it updates its rib
+ *
+ * The zebra server sends clients a ZEBRA_IPV4_ROUTE_DELETE or a
+ * ZEBRA_IPV6_ROUTE_DELETE via zsend_route_multipath when:
+ * - a "ip route"  or "ipv6 route" vty command is issued, a prefix is
+ * - deleted from zebra's rib, and this info
+ *   has to be redistributed to the clients 
+ * 
+ * XXX The ZEBRA_IPV*_ROUTE_ADD message is also sent by the client to the
+ * zebra server when the client wants to tell the zebra server to add a
+ * route to the kernel (zapi_ipv4_add etc. ).  Since it's essentially the
+ * same message being sent back and forth, this function and
+ * zapi_ipv{4,6}_{add, delete} should be re-written to avoid code
+ * duplication.
+ */
+int
+zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p,
+                       struct rib *rib)
+{
+  int psize;
+  struct stream *s;
+  struct nexthop *nexthop;
+  unsigned long nhnummark = 0, messmark = 0;
+  int nhnum = 0;
+  u_char zapi_flags = 0;
+
+  /* Check this client need this route. */
+  if (!vrf_bitmap_check (client->redist[rib->type], rib->vrf_id) &&
+      !(is_default (p) &&
+        vrf_bitmap_check (client->redist_default, rib->vrf_id)))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+  
+  zserv_create_header (s, cmd, rib->vrf_id);
+  
+  /* Put type and nexthop. */
+  stream_putc (s, rib->type);
+  stream_putc (s, rib->flags);
+  
+  /* marker for message flags field */
+  messmark = stream_get_endp (s);
+  stream_putc (s, 0);
+
+  /* Prefix. */
+  psize = PSIZE (p->prefixlen);
+  stream_putc (s, p->prefixlen);
+  stream_write (s, (u_char *) & p->u.prefix, psize);
+
+  /* 
+   * XXX The message format sent by zebra below does not match the format
+   * of the corresponding message expected by the zebra server
+   * itself (e.g., see zread_ipv4_add). The nexthop_num is not set correctly,
+   * (is there a bug on the client side if more than one segment is sent?)
+   * nexthop ZEBRA_NEXTHOP_IPV4 is never set, ZEBRA_NEXTHOP_IFINDEX 
+   * is hard-coded.
+   */
+  /* Nexthop */
+  
+  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+    {
+      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+        {
+          SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP);
+          SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX);
+          
+          if (nhnummark == 0)
+            {
+              nhnummark = stream_get_endp (s);
+              stream_putc (s, 1); /* placeholder */
+            }
+          
+          nhnum++;
+
+          switch(nexthop->type) 
+            {
+              case NEXTHOP_TYPE_IPV4:
+              case NEXTHOP_TYPE_IPV4_IFINDEX:
+                stream_put_in_addr (s, &nexthop->gate.ipv4);
+                break;
+#ifdef HAVE_IPV6
+              case NEXTHOP_TYPE_IPV6:
+              case NEXTHOP_TYPE_IPV6_IFINDEX:
+              case NEXTHOP_TYPE_IPV6_IFNAME:
+                stream_write (s, (u_char *) &nexthop->gate.ipv6, 16);
+                break;
+#endif
+              default:
+                if (cmd == ZEBRA_IPV4_ROUTE_ADD 
+                    || cmd == ZEBRA_IPV4_ROUTE_DELETE)
+                  {
+                    struct in_addr empty;
+                    memset (&empty, 0, sizeof (struct in_addr));
+                    stream_write (s, (u_char *) &empty, IPV4_MAX_BYTELEN);
+                  }
+                else
+                  {
+                    struct in6_addr empty;
+                    memset (&empty, 0, sizeof (struct in6_addr));
+                    stream_write (s, (u_char *) &empty, IPV6_MAX_BYTELEN);
+                  }
+              }
+
+          /* Interface index. */
+          stream_putc (s, 1);
+          stream_putl (s, nexthop->ifindex);
+
+          break;
+        }
+    }
+
+  /* Metric */
+  if (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV6_ROUTE_ADD)
+    {
+      SET_FLAG (zapi_flags, ZAPI_MESSAGE_DISTANCE);
+      stream_putc (s, rib->distance);
+      SET_FLAG (zapi_flags, ZAPI_MESSAGE_METRIC);
+      stream_putl (s, rib->metric);
+      SET_FLAG (zapi_flags, ZAPI_MESSAGE_MTU);
+      stream_putl (s, rib->mtu);
+      /* tag */
+      if (rib->tag)
+        {
+          SET_FLAG(zapi_flags, ZAPI_MESSAGE_TAG);
+          stream_putl (s, rib->tag);
+        }
+    }
+  
+  /* write real message flags value */
+  stream_putc_at (s, messmark, zapi_flags);
+  
+  /* Write next-hop number */
+  if (nhnummark)
+    stream_putc_at (s, nhnummark, nhnum);
+  
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zebra_server_send_message(client);
+}
+
+#ifdef HAVE_IPV6
+static int
+zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct rib *rib;
+  unsigned long nump;
+  u_char num;
+  struct nexthop *nexthop;
+
+  /* Lookup nexthop. */
+  rib = rib_match_ipv6 (addr, vrf_id);
+
+  /* Get output stream. */
+  s = client->obuf;
+  stream_reset (s);
+
+  /* Fill in result. */
+  zserv_create_header (s, ZEBRA_IPV6_NEXTHOP_LOOKUP, vrf_id);
+  stream_put (s, addr, 16);
+
+  if (rib)
+    {
+      stream_putl (s, rib->metric);
+      num = 0;
+      nump = stream_get_endp(s);
+      stream_putc (s, 0);
+      /* Only non-recursive routes are elegible to resolve nexthop we
+       * are looking up. Therefore, we will just iterate over the top
+       * chain of nexthops. */
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         {
+           stream_putc (s, nexthop->type);
+           switch (nexthop->type)
+             {
+             case ZEBRA_NEXTHOP_IPV6:
+               stream_put (s, &nexthop->gate.ipv6, 16);
+               break;
+             case ZEBRA_NEXTHOP_IPV6_IFINDEX:
+             case ZEBRA_NEXTHOP_IPV6_IFNAME:
+               stream_put (s, &nexthop->gate.ipv6, 16);
+               stream_putl (s, nexthop->ifindex);
+               break;
+             case ZEBRA_NEXTHOP_IFINDEX:
+             case ZEBRA_NEXTHOP_IFNAME:
+               stream_putl (s, nexthop->ifindex);
+               break;
+             default:
+                /* do nothing */
+               break;
+             }
+           num++;
+         }
+
+      stream_putc_at (s, nump, num);
+    }
+  else
+    {
+      stream_putl (s, 0);
+      stream_putc (s, 0);
+    }
+
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zebra_server_send_message(client);
+}
+#endif /* HAVE_IPV6 */
+
+/*
+  In the case of ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB:
+    query unicast rib if nexthop is not found on mrib
+    and return both route metric and protocol distance.
+*/
+static int
+zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr,
+                          int cmd, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct rib *rib;
+  unsigned long nump;
+  u_char num;
+  struct nexthop *nexthop;
+
+  /* Get output stream. */
+  s = client->obuf;
+  stream_reset (s);
+
+  /* Fill in result. */
+  zserv_create_header (s, cmd, vrf_id);
+  stream_put_in_addr (s, &addr);
+
+  /* Lookup nexthop - eBGP excluded */
+  if (cmd == ZEBRA_IPV4_NEXTHOP_LOOKUP)
+    rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL, vrf_id);
+  else  /* ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB */
+    {
+      rib = rib_match_ipv4_multicast (addr, NULL, vrf_id);
+      /* handle the distance field here since
+       * it is only needed for MRIB command */
+      if (rib)
+       stream_putc (s, rib->distance);
+      else
+        stream_putc (s, 0); /* distance */
+    }
+
+  if (rib)
+    {
+      if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+        zlog_debug("%s: Matching rib entry found.", __func__);
+      stream_putl (s, rib->metric);
+      num = 0;
+      nump = stream_get_endp(s); /* remember position for nexthop_num */
+      stream_putc (s, 0);        /* reserve room for nexthop_num */
+      /* Only non-recursive routes are elegible to resolve the nexthop we
+       * are looking up. Therefore, we will just iterate over the top
+       * chain of nexthops. */
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         {
+           stream_putc (s, nexthop->type);
+           switch (nexthop->type)
+             {
+             case ZEBRA_NEXTHOP_IPV4:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               break;
+             case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               stream_putl (s, nexthop->ifindex);
+               break;
+             case ZEBRA_NEXTHOP_IFINDEX:
+             case ZEBRA_NEXTHOP_IFNAME:
+               stream_putl (s, nexthop->ifindex);
+               break;
+             default:
+               /* do nothing */
+               break;
+             }
+           num++;
+         }
+
+      stream_putc_at (s, nump, num); /* store nexthop_num */
+    }
+  else
+    {
+      if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+        zlog_debug("%s: No matching rib entry found.", __func__);
+      stream_putl (s, 0); /* metric */
+      stream_putc (s, 0); /* nexthop_num */
+    }
+
+  stream_putw_at (s, 0, stream_get_endp (s));
+  
+  return zebra_server_send_message(client);
+}
+
+/* Nexthop register */
+static int
+zserv_nexthop_register (struct zserv *client, int sock, u_short length, vrf_id_t vrf_id)
+{
+  struct rnh *rnh;
+  struct stream *s;
+  struct prefix p;
+  u_short l = 0;
+  u_char connected;
+
+  if (IS_ZEBRA_DEBUG_NHT)
+    zlog_debug("nexthop_register msg from client %s: length=%d\n",
+              zebra_route_string(client->proto), length);
+
+  s = client->ibuf;
+
+  while (l < length)
+    {
+      connected = stream_getc(s);
+      p.family = stream_getw(s);
+      p.prefixlen = stream_getc(s);
+      l += 4;
+      stream_get(&p.u.prefix, s, PSIZE(p.prefixlen));
+      l += PSIZE(p.prefixlen);
+      rnh = zebra_add_rnh(&p, 0);
+
+      client->nh_reg_time = quagga_time(NULL);
+      
+      if (connected)
+       SET_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED);
+
+      zebra_add_rnh_client(rnh, client, vrf_id);
+    }
+  zebra_evaluate_rnh_table(0, AF_INET);
+  zebra_evaluate_rnh_table(0, AF_INET6);
+  return 0;
+}
+
+/* Nexthop register */
+static int
+zserv_nexthop_unregister (struct zserv *client, int sock, u_short length)
+{
+  struct rnh *rnh;
+  struct stream *s;
+  struct prefix p;
+  u_short l = 0;
+
+  if (IS_ZEBRA_DEBUG_NHT)
+    zlog_debug("nexthop_unregister msg from client %s: length=%d\n",
+              zebra_route_string(client->proto), length);
+
+  s = client->ibuf;
+
+  while (l < length)
+    {
+      (void)stream_getc(s);
+      p.family = stream_getw(s);
+      p.prefixlen = stream_getc(s);
+      l += 4;
+      stream_get(&p.u.prefix, s, PSIZE(p.prefixlen));
+      l += PSIZE(p.prefixlen);
+      rnh = zebra_lookup_rnh(&p, 0);
+      if (rnh)
+       {
+         client->nh_dereg_time = quagga_time(NULL);
+         zebra_remove_rnh_client(rnh, client);
+       }
+    }
+  return 0;
+}
+
+static int
+zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  struct rib *rib;
+  unsigned long nump;
+  u_char num;
+  struct nexthop *nexthop;
+
+  /* Lookup nexthop. */
+  rib = rib_lookup_ipv4 (p, vrf_id);
+
+  /* Get output stream. */
+  s = client->obuf;
+  stream_reset (s);
+
+  /* Fill in result. */
+  zserv_create_header (s, ZEBRA_IPV4_IMPORT_LOOKUP, vrf_id);
+  stream_put_in_addr (s, &p->prefix);
+
+  if (rib)
+    {
+      stream_putl (s, rib->metric);
+      num = 0;
+      nump = stream_get_endp(s);
+      stream_putc (s, 0);
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+         {
+           stream_putc (s, nexthop->type);
+           switch (nexthop->type)
+             {
+             case ZEBRA_NEXTHOP_IPV4:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               break;
+             case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+               stream_put_in_addr (s, &nexthop->gate.ipv4);
+               stream_putl (s, nexthop->ifindex);
+               break;
+             case ZEBRA_NEXTHOP_IFINDEX:
+             case ZEBRA_NEXTHOP_IFNAME:
+               stream_putl (s, nexthop->ifindex);
+               break;
+             default:
+                /* do nothing */
+               break;
+             }
+           num++;
+         }
+      stream_putc_at (s, nump, num);
+    }
+  else
+    {
+      stream_putl (s, 0);
+      stream_putc (s, 0);
+    }
+
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zebra_server_send_message(client);
+}
+
+/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */
+int
+zsend_router_id_update (struct zserv *client, struct prefix *p,
+    vrf_id_t vrf_id)
+{
+  struct stream *s;
+  int blen;
+
+  /* Check this client need interface information. */
+  if (! vrf_bitmap_check (client->ridinfo, vrf_id))
+    return 0;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  /* Message type. */
+  zserv_create_header (s, ZEBRA_ROUTER_ID_UPDATE, vrf_id);
+
+  /* Prefix information. */
+  stream_putc (s, p->family);
+  blen = prefix_blen (p);
+  stream_put (s, &p->u.prefix, blen);
+  stream_putc (s, p->prefixlen);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return zebra_server_send_message(client);
+}
+
+/* Register zebra server interface information.  Send current all
+   interface and address information. */
+static int
+zread_interface_add (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  struct listnode *ifnode, *ifnnode;
+  struct listnode *cnode, *cnnode;
+  struct interface *ifp;
+  struct connected *c;
+
+  /* Interface information is needed. */
+  vrf_bitmap_set (client->ifinfo, vrf_id);
+
+  for (ALL_LIST_ELEMENTS (vrf_iflist (vrf_id), ifnode, ifnnode, ifp))
+    {
+      /* Skip pseudo interface. */
+      if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+       continue;
+
+      if (zsend_interface_add (client, ifp) < 0)
+        return -1;
+
+      for (ALL_LIST_ELEMENTS (ifp->connected, cnode, cnnode, c))
+       {
+         if (CHECK_FLAG (c->conf, ZEBRA_IFC_REAL) &&
+             (zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, 
+                                       ifp, c) < 0))
+           return -1;
+       }
+    }
+  return 0;
+}
+
+/* Unregister zebra server interface information. */
+static int
+zread_interface_delete (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  vrf_bitmap_unset (client->ifinfo, vrf_id);
+  return 0;
+}
+
+/* This function support multiple nexthop. */
+/* 
+ * Parse the ZEBRA_IPV4_ROUTE_ADD sent from client. Update rib and
+ * add kernel route. 
+ */
+static int
+zread_ipv4_add (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  int i;
+  struct rib *rib;
+  struct prefix_ipv4 p;
+  u_char message;
+  struct in_addr nexthop;
+  u_char nexthop_num;
+  u_char nexthop_type;
+  struct stream *s;
+  ifindex_t ifindex;
+  u_char ifname_len;
+  safi_t safi; 
+  int ret;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  /* Allocate new rib. */
+  rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+  
+  /* Type, flags, message. */
+  rib->type = stream_getc (s);
+  rib->flags = stream_getc (s);
+  message = stream_getc (s); 
+  safi = stream_getw (s);
+  rib->uptime = time (NULL);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* VRF ID */
+  rib->vrf_id = vrf_id;
+
+  /* Nexthop parse. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      nexthop_num = stream_getc (s);
+
+      for (i = 0; i < nexthop_num; i++)
+       {
+         nexthop_type = stream_getc (s);
+
+         switch (nexthop_type)
+           {
+           case ZEBRA_NEXTHOP_IFINDEX:
+             ifindex = stream_getl (s);
+             rib_nexthop_ifindex_add (rib, ifindex);
+             break;
+           case ZEBRA_NEXTHOP_IFNAME:
+             ifname_len = stream_getc (s);
+             stream_forward_getp (s, ifname_len);
+             break;
+           case ZEBRA_NEXTHOP_IPV4:
+             nexthop.s_addr = stream_get_ipv4 (s);
+             rib_nexthop_ipv4_add (rib, &nexthop, NULL);
+             break;
+           case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+             nexthop.s_addr = stream_get_ipv4 (s);
+             ifindex = stream_getl (s);
+             rib_nexthop_ipv4_ifindex_add (rib, &nexthop, NULL, ifindex);
+             break;
+           case ZEBRA_NEXTHOP_IPV6:
+             stream_forward_getp (s, IPV6_MAX_BYTELEN);
+             break;
+            case ZEBRA_NEXTHOP_BLACKHOLE:
+              rib_nexthop_blackhole_add (rib);
+              break;
+            }
+       }
+    }
+
+  /* Distance. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+    rib->distance = stream_getc (s);
+
+  /* Metric. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+    rib->metric = stream_getl (s);
+    
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_MTU))
+    rib->mtu = stream_getl (s);
+  /* Tag */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG))
+    rib->tag = stream_getl (s);
+  else
+    rib->tag = 0;
+  
+  /* Table */
+  rib->table=zebrad.rtm_table_default;
+  ret = rib_add_ipv4_multipath (&p, rib, safi);
+
+  /* Stats */
+  if (ret > 0)
+    client->v4_route_add_cnt++;
+  else if (ret < 0)
+    client->v4_route_upd8_cnt++;
+  return 0;
+}
+
+/* Zebra server IPv4 prefix delete function. */
+static int
+zread_ipv4_delete (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  int i;
+  struct stream *s;
+  struct zapi_ipv4 api;
+  struct in_addr nexthop, *nexthop_p;
+  unsigned long ifindex;
+  struct prefix_ipv4 p;
+  u_char nexthop_num;
+  u_char nexthop_type;
+  u_char ifname_len;
+  
+  s = client->ibuf;
+  ifindex = 0;
+  nexthop.s_addr = 0;
+  nexthop_p = NULL;
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+  api.safi = stream_getw (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      nexthop_num = stream_getc (s);
+
+      for (i = 0; i < nexthop_num; i++)
+       {
+         nexthop_type = stream_getc (s);
+
+         switch (nexthop_type)
+           {
+           case ZEBRA_NEXTHOP_IFINDEX:
+             ifindex = stream_getl (s);
+             break;
+           case ZEBRA_NEXTHOP_IFNAME:
+             ifname_len = stream_getc (s);
+             stream_forward_getp (s, ifname_len);
+             break;
+           case ZEBRA_NEXTHOP_IPV4:
+             nexthop.s_addr = stream_get_ipv4 (s);
+             nexthop_p = &nexthop;
+             break;
+           case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+             nexthop.s_addr = stream_get_ipv4 (s);
+             nexthop_p = &nexthop;
+             ifindex = stream_getl (s);
+             break;
+           case ZEBRA_NEXTHOP_IPV6:
+             stream_forward_getp (s, IPV6_MAX_BYTELEN);
+             break;
+           }
+       }
+    }
+
+  /* Distance. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+
+  /* Metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+    
+  /* tag */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  rib_delete_ipv4 (api.type, api.flags, &p, nexthop_p, ifindex,
+                   vrf_id, api.safi);
+  client->v4_route_del_cnt++;
+  return 0;
+}
+
+/* Nexthop lookup for IPv4. */
+static int
+zread_ipv4_nexthop_lookup (int cmd, struct zserv *client, u_short length,
+    vrf_id_t vrf_id)
+{
+  struct in_addr addr;
+  char buf[BUFSIZ];
+
+  addr.s_addr = stream_get_ipv4 (client->ibuf);
+  if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+    zlog_debug("%s: looking up %s", __func__,
+               inet_ntop (AF_INET, &addr, buf, BUFSIZ));
+
+  return zsend_ipv4_nexthop_lookup (client, addr, cmd, vrf_id);
+}
+
+/* Nexthop lookup for IPv4. */
+static int
+zread_ipv4_import_lookup (struct zserv *client, u_short length,
+    vrf_id_t vrf_id)
+{
+  struct prefix_ipv4 p;
+
+  p.family = AF_INET;
+  p.prefixlen = stream_getc (client->ibuf);
+  p.prefix.s_addr = stream_get_ipv4 (client->ibuf);
+
+  return zsend_ipv4_import_lookup (client, &p, vrf_id);
+}
+
+#ifdef HAVE_IPV6
+/* Zebra server IPv6 prefix add function. */
+static int
+zread_ipv6_add (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  int i;
+  struct stream *s;
+  struct in6_addr nexthop;
+  struct rib *rib;
+  u_char message;
+  u_char gateway_num;
+  u_char nexthop_type;
+  struct prefix_ipv6 p;
+  safi_t safi;
+  static struct in6_addr nexthops[MULTIPATH_NUM];
+  static unsigned int ifindices[MULTIPATH_NUM];
+  int ret;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  memset (&nexthop, 0, sizeof (struct in6_addr));
+
+  /* Allocate new rib. */
+  rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+
+  /* Type, flags, message. */
+  rib->type = stream_getc (s);
+  rib->flags = stream_getc (s);
+  message = stream_getc (s);
+  safi = stream_getw (s);
+  rib->uptime = time (NULL);
+
+  /* IPv6 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* We need to give nh-addr, nh-ifindex with the same next-hop object
+   * to the rib to ensure that IPv6 multipathing works; need to coalesce
+   * these. Clients should send the same number of paired set of
+   * next-hop-addr/next-hop-ifindices. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      int nh_count = 0;
+      int if_count = 0;
+      int max_nh_if = 0;
+      unsigned int ifindex;
+
+      gateway_num = stream_getc (s);
+      for (i = 0; i < gateway_num; i++)
+       {
+         nexthop_type = stream_getc (s);
+
+         switch (nexthop_type)
+           {
+           case ZEBRA_NEXTHOP_IPV6:
+             stream_get (&nexthop, s, 16);
+              if (nh_count < MULTIPATH_NUM) {
+               nexthops[nh_count++] = nexthop;
+              }
+             break;
+           case ZEBRA_NEXTHOP_IFINDEX:
+             ifindex = stream_getl (s);
+              if (if_count < MULTIPATH_NUM) {
+               ifindices[if_count++] = ifindex;
+              }
+             break;
+           }
+       }
+
+      max_nh_if = (nh_count > if_count) ? nh_count : if_count;
+      for (i = 0; i < max_nh_if; i++)
+        {
+         if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i]))
+           {
+             if ((i < if_count) && ifindices[i])
+               rib_nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]);
+             else
+               rib_nexthop_ipv6_add (rib, &nexthops[i]);
+           }
+          else
+           {
+             if ((i < if_count) && ifindices[i])
+               rib_nexthop_ifindex_add (rib, ifindices[i]);
+           }
+       }
+    }
+
+  /* Distance. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+    rib->distance = stream_getc (s);
+
+  /* Metric. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+    rib->metric = stream_getl (s);
+
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_MTU))
+    rib->mtu = stream_getl (s);
+
+  /* Tag */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG))
+    rib->tag = stream_getl (s);
+  else
+    rib->tag = 0;
+  
+  /* Table */
+  rib->table=zebrad.rtm_table_default;
+  ret = rib_add_ipv6_multipath (&p, rib, safi);
+  /* Stats */
+  if (ret > 0)
+    client->v6_route_add_cnt++;
+  else if (ret < 0)
+    client->v6_route_upd8_cnt++;
+
+  return 0;
+}
+
+/* Zebra server IPv6 prefix delete function. */
+static int
+zread_ipv6_delete (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  int i;
+  struct stream *s;
+  struct zapi_ipv6 api;
+  struct in6_addr nexthop;
+  unsigned long ifindex;
+  struct prefix_ipv6 p;
+  
+  s = client->ibuf;
+  ifindex = 0;
+  memset (&nexthop, 0, sizeof (struct in6_addr));
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+  api.safi = stream_getw (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      u_char nexthop_type;
+
+      api.nexthop_num = stream_getc (s);
+      for (i = 0; i < api.nexthop_num; i++)
+       {
+         nexthop_type = stream_getc (s);
+
+         switch (nexthop_type)
+           {
+           case ZEBRA_NEXTHOP_IPV6:
+             stream_get (&nexthop, s, 16);
+             break;
+           case ZEBRA_NEXTHOP_IFINDEX:
+             ifindex = stream_getl (s);
+             break;
+           }
+       }
+    }
+
+  /* Distance. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+
+  /* Metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+    
+  /* tag */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
+    api.tag = stream_getl (s);
+  else
+    api.tag = 0;
+
+  if (IN6_IS_ADDR_UNSPECIFIED (&nexthop))
+    rib_delete_ipv6 (api.type, api.flags, &p, NULL, ifindex, vrf_id,
+                     api.safi);
+  else
+    rib_delete_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, vrf_id,
+                     api.safi);
+
+  client->v6_route_del_cnt++;
+  return 0;
+}
+
+static int
+zread_ipv6_nexthop_lookup (struct zserv *client, u_short length,
+    vrf_id_t vrf_id)
+{
+  struct in6_addr addr;
+  char buf[BUFSIZ];
+
+  stream_get (&addr, client->ibuf, 16);
+  if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+    zlog_debug("%s: looking up %s", __func__,
+               inet_ntop (AF_INET6, &addr, buf, BUFSIZ));
+
+  return zsend_ipv6_nexthop_lookup (client, &addr, vrf_id);
+}
+#endif /* HAVE_IPV6 */
+
+/* Register zebra server router-id information.  Send current router-id */
+static int
+zread_router_id_add (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  struct prefix p;
+
+  /* Router-id information is needed. */
+  vrf_bitmap_set (client->ridinfo, vrf_id);
+
+  router_id_get (&p, vrf_id);
+
+  return zsend_router_id_update (client, &p, vrf_id);
+}
+
+/* Unregister zebra server router-id information. */
+static int
+zread_router_id_delete (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  vrf_bitmap_unset (client->ridinfo, vrf_id);
+  return 0;
+}
+
+/* Tie up route-type and client->sock */
+static void
+zread_hello (struct zserv *client)
+{
+  /* type of protocol (lib/zebra.h) */
+  u_char proto;
+  proto = stream_getc (client->ibuf);
+
+  /* accept only dynamic routing protocols */
+  if ((proto < ZEBRA_ROUTE_MAX)
+  &&  (proto > ZEBRA_ROUTE_STATIC))
+    {
+      zlog_notice ("client %d says hello and bids fair to announce only %s routes",
+                    client->sock, zebra_route_string(proto));
+
+      /* if route-type was binded by other client */
+      if (route_type_oaths[proto])
+        zlog_warn ("sender of %s routes changed %c->%c",
+                    zebra_route_string(proto), route_type_oaths[proto],
+                    client->sock);
+
+      route_type_oaths[proto] = client->sock;
+      client->proto = proto;
+    }
+}
+
+/* Unregister all information in a VRF. */
+static int
+zread_vrf_unregister (struct zserv *client, u_short length, vrf_id_t vrf_id)
+{
+  int i;
+
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    vrf_bitmap_unset (client->redist[i], vrf_id);
+  vrf_bitmap_unset (client->redist_default, vrf_id);
+  vrf_bitmap_unset (client->ifinfo, vrf_id);
+  vrf_bitmap_unset (client->ridinfo, vrf_id);
+
+  return 0;
+}
+
+/* If client sent routes of specific type, zebra removes it
+ * and returns number of deleted routes.
+ */
+static void
+zebra_score_rib (int client_sock)
+{
+  int i;
+
+  for (i = ZEBRA_ROUTE_RIP; i < ZEBRA_ROUTE_MAX; i++)
+    if (client_sock == route_type_oaths[i])
+      {
+        zlog_notice ("client %d disconnected. %lu %s routes removed from the rib",
+                      client_sock, rib_score_proto (i), zebra_route_string (i));
+        route_type_oaths[i] = 0;
+        break;
+      }
+}
+
+/* Close zebra client. */
+static void
+zebra_client_close (struct zserv *client)
+{
+  zebra_cleanup_rnh_client(0, AF_INET, client);
+  zebra_cleanup_rnh_client(0, AF_INET6, client);
+
+  /* Close file descriptor. */
+  if (client->sock)
+    {
+      close (client->sock);
+      zebra_score_rib (client->sock);
+      client->sock = -1;
+    }
+
+  /* Free stream buffers. */
+  if (client->ibuf)
+    stream_free (client->ibuf);
+  if (client->obuf)
+    stream_free (client->obuf);
+  if (client->wb)
+    buffer_free(client->wb);
+
+  /* Release threads. */
+  if (client->t_read)
+    thread_cancel (client->t_read);
+  if (client->t_write)
+    thread_cancel (client->t_write);
+  if (client->t_suicide)
+    thread_cancel (client->t_suicide);
+
+  /* Free client structure. */
+  listnode_delete (zebrad.client_list, client);
+  XFREE (0, client);
+}
+
+/* Make new client. */
+static void
+zebra_client_create (int sock)
+{
+  struct zserv *client;
+  int i;
+
+  client = XCALLOC (MTYPE_TMP, sizeof (struct zserv));
+
+  /* Make client input/output buffer. */
+  client->sock = sock;
+  client->ibuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
+  client->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
+  client->wb = buffer_new(0);
+
+  /* Set table number. */
+  client->rtm_table = zebrad.rtm_table_default;
+
+  /* Initialize flags */
+  for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+    client->redist[i] = vrf_bitmap_init ();
+  client->redist_default = vrf_bitmap_init ();
+  client->ifinfo = vrf_bitmap_init ();
+  client->ridinfo = vrf_bitmap_init ();
+  client->connect_time = quagga_time(NULL);
+
+  /* Add this client to linked list. */
+  listnode_add (zebrad.client_list, client);
+  
+  /* Make new read thread. */
+  zebra_event (ZEBRA_READ, sock, client);
+}
+
+/* Handler of zebra service request. */
+static int
+zebra_client_read (struct thread *thread)
+{
+  int sock;
+  struct zserv *client;
+  size_t already;
+  uint16_t length, command;
+  uint8_t marker, version;
+  vrf_id_t vrf_id;
+
+  /* Get thread data.  Reset reading thread because I'm running. */
+  sock = THREAD_FD (thread);
+  client = THREAD_ARG (thread);
+  client->t_read = NULL;
+
+  if (client->t_suicide)
+    {
+      zebra_client_close(client);
+      return -1;
+    }
+
+  /* Read length and command (if we don't have it already). */
+  if ((already = stream_get_endp(client->ibuf)) < ZEBRA_HEADER_SIZE)
+    {
+      ssize_t nbyte;
+      if (((nbyte = stream_read_try (client->ibuf, sock,
+                                    ZEBRA_HEADER_SIZE-already)) == 0) ||
+         (nbyte == -1))
+       {
+         if (IS_ZEBRA_DEBUG_EVENT)
+           zlog_debug ("connection closed socket [%d]", sock);
+         zebra_client_close (client);
+         return -1;
+       }
+      if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE-already))
+       {
+         /* Try again later. */
+         zebra_event (ZEBRA_READ, sock, client);
+         return 0;
+       }
+      already = ZEBRA_HEADER_SIZE;
+    }
+
+  /* Reset to read from the beginning of the incoming packet. */
+  stream_set_getp(client->ibuf, 0);
+
+  /* Fetch header values */
+  length = stream_getw (client->ibuf);
+  marker = stream_getc (client->ibuf);
+  version = stream_getc (client->ibuf);
+  vrf_id = stream_getw (client->ibuf);
+  command = stream_getw (client->ibuf);
+
+  if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION)
+    {
+      zlog_err("%s: socket %d version mismatch, marker %d, version %d",
+               __func__, sock, marker, version);
+      zebra_client_close (client);
+      return -1;
+    }
+  if (length < ZEBRA_HEADER_SIZE) 
+    {
+      zlog_warn("%s: socket %d message length %u is less than header size %d",
+               __func__, sock, length, ZEBRA_HEADER_SIZE);
+      zebra_client_close (client);
+      return -1;
+    }
+  if (length > STREAM_SIZE(client->ibuf))
+    {
+      zlog_warn("%s: socket %d message length %u exceeds buffer size %lu",
+               __func__, sock, length, (u_long)STREAM_SIZE(client->ibuf));
+      zebra_client_close (client);
+      return -1;
+    }
+
+  /* Read rest of data. */
+  if (already < length)
+    {
+      ssize_t nbyte;
+      if (((nbyte = stream_read_try (client->ibuf, sock,
+                                    length-already)) == 0) ||
+         (nbyte == -1))
+       {
+         if (IS_ZEBRA_DEBUG_EVENT)
+           zlog_debug ("connection closed [%d] when reading zebra data", sock);
+         zebra_client_close (client);
+         return -1;
+       }
+      if (nbyte != (ssize_t)(length-already))
+        {
+         /* Try again later. */
+         zebra_event (ZEBRA_READ, sock, client);
+         return 0;
+       }
+    }
+
+  length -= ZEBRA_HEADER_SIZE;
+
+  /* Debug packet information. */
+  if (IS_ZEBRA_DEBUG_EVENT)
+    zlog_debug ("zebra message comes from socket [%d]", sock);
+
+  if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+    zlog_debug ("zebra message received [%s] %d in VRF %u",
+              zserv_command_string (command), length, vrf_id);
+
+  client->last_read_time = quagga_time(NULL);
+  client->last_read_cmd = command;
+
+  switch (command) 
+    {
+    case ZEBRA_ROUTER_ID_ADD:
+      zread_router_id_add (client, length, vrf_id);
+      break;
+    case ZEBRA_ROUTER_ID_DELETE:
+      zread_router_id_delete (client, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_ADD:
+      zread_interface_add (client, length, vrf_id);
+      break;
+    case ZEBRA_INTERFACE_DELETE:
+      zread_interface_delete (client, length, vrf_id);
+      break;
+    case ZEBRA_IPV4_ROUTE_ADD:
+      zread_ipv4_add (client, length, vrf_id);
+      break;
+    case ZEBRA_IPV4_ROUTE_DELETE:
+      zread_ipv4_delete (client, length, vrf_id);
+      break;
+#ifdef HAVE_IPV6
+    case ZEBRA_IPV6_ROUTE_ADD:
+      zread_ipv6_add (client, length, vrf_id);
+      break;
+    case ZEBRA_IPV6_ROUTE_DELETE:
+      zread_ipv6_delete (client, length, vrf_id);
+      break;
+#endif /* HAVE_IPV6 */
+    case ZEBRA_REDISTRIBUTE_ADD:
+      zebra_redistribute_add (command, client, length, vrf_id);
+      break;
+    case ZEBRA_REDISTRIBUTE_DELETE:
+      zebra_redistribute_delete (command, client, length, vrf_id);
+      break;
+    case ZEBRA_REDISTRIBUTE_DEFAULT_ADD:
+      zebra_redistribute_default_add (command, client, length, vrf_id);
+      break;
+    case ZEBRA_REDISTRIBUTE_DEFAULT_DELETE:
+      zebra_redistribute_default_delete (command, client, length, vrf_id);
+      break;
+    case ZEBRA_IPV4_NEXTHOP_LOOKUP:
+    case ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB:
+      zread_ipv4_nexthop_lookup (command, client, length, vrf_id);
+      break;
+#ifdef HAVE_IPV6
+    case ZEBRA_IPV6_NEXTHOP_LOOKUP:
+      zread_ipv6_nexthop_lookup (client, length, vrf_id);
+      break;
+#endif /* HAVE_IPV6 */
+    case ZEBRA_IPV4_IMPORT_LOOKUP:
+      zread_ipv4_import_lookup (client, length, vrf_id);
+      break;
+    case ZEBRA_HELLO:
+      zread_hello (client);
+      break;
+    case ZEBRA_VRF_UNREGISTER:
+      zread_vrf_unregister (client, length, vrf_id);
+    case ZEBRA_NEXTHOP_REGISTER:
+      zserv_nexthop_register(client, sock, length, vrf_id);
+      break;
+    case ZEBRA_NEXTHOP_UNREGISTER:
+      zserv_nexthop_unregister(client, sock, length);
+      break;
+    default:
+      zlog_info ("Zebra received unknown command %d", command);
+      break;
+    }
+
+  if (client->t_suicide)
+    {
+      /* No need to wait for thread callback, just kill immediately. */
+      zebra_client_close(client);
+      return -1;
+    }
+
+  stream_reset (client->ibuf);
+  zebra_event (ZEBRA_READ, sock, client);
+  return 0;
+}
+
+
+/* Accept code of zebra server socket. */
+static int
+zebra_accept (struct thread *thread)
+{
+  int accept_sock;
+  int client_sock;
+  struct sockaddr_in client;
+  socklen_t len;
+
+  accept_sock = THREAD_FD (thread);
+
+  /* Reregister myself. */
+  zebra_event (ZEBRA_SERV, accept_sock, NULL);
+
+  len = sizeof (struct sockaddr_in);
+  client_sock = accept (accept_sock, (struct sockaddr *) &client, &len);
+
+  if (client_sock < 0)
+    {
+      zlog_warn ("Can't accept zebra socket: %s", safe_strerror (errno));
+      return -1;
+    }
+
+  /* Make client socket non-blocking.  */
+  set_nonblocking(client_sock);
+  
+  /* Create new zebra client. */
+  zebra_client_create (client_sock);
+
+  return 0;
+}
+
+#ifdef HAVE_TCP_ZEBRA
+/* Make zebra's server socket. */
+static void
+zebra_serv ()
+{
+  int ret;
+  int accept_sock;
+  struct sockaddr_in addr;
+
+  accept_sock = socket (AF_INET, SOCK_STREAM, 0);
+
+  if (accept_sock < 0) 
+    {
+      zlog_warn ("Can't create zserv stream socket: %s", 
+                 safe_strerror (errno));
+      zlog_warn ("zebra can't provice full functionality due to above error");
+      return;
+    }
+
+  memset (&route_type_oaths, 0, sizeof (route_type_oaths));
+  memset (&addr, 0, sizeof (struct sockaddr_in));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons (ZEBRA_PORT);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  addr.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+  sockopt_reuseaddr (accept_sock);
+  sockopt_reuseport (accept_sock);
+
+  if ( zserv_privs.change(ZPRIVS_RAISE) )
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
+    
+  ret  = bind (accept_sock, (struct sockaddr *)&addr, 
+              sizeof (struct sockaddr_in));
+  if (ret < 0)
+    {
+      zlog_warn ("Can't bind to stream socket: %s", 
+                 safe_strerror (errno));
+      zlog_warn ("zebra can't provice full functionality due to above error");
+      close (accept_sock);      /* Avoid sd leak. */
+      return;
+    }
+    
+  if ( zserv_privs.change(ZPRIVS_LOWER) )
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+  ret = listen (accept_sock, 1);
+  if (ret < 0)
+    {
+      zlog_warn ("Can't listen to stream socket: %s", 
+                 safe_strerror (errno));
+      zlog_warn ("zebra can't provice full functionality due to above error");
+      close (accept_sock);     /* Avoid sd leak. */
+      return;
+    }
+
+  zebra_event (ZEBRA_SERV, accept_sock, NULL);
+}
+#else /* HAVE_TCP_ZEBRA */
+
+/* For sockaddr_un. */
+#include <sys/un.h>
+
+/* zebra server UNIX domain socket. */
+static void
+zebra_serv_un (const char *path)
+{
+  int ret;
+  int sock, len;
+  struct sockaddr_un serv;
+  mode_t old_mask;
+
+  /* First of all, unlink existing socket */
+  unlink (path);
+
+  /* Set umask */
+  old_mask = umask (0077);
+
+  /* Make UNIX domain socket. */
+  sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      zlog_warn ("Can't create zserv unix socket: %s", 
+                 safe_strerror (errno));
+      zlog_warn ("zebra can't provide full functionality due to above error");
+      return;
+    }
+
+  memset (&route_type_oaths, 0, sizeof (route_type_oaths));
+
+  /* Make server socket. */
+  memset (&serv, 0, sizeof (struct sockaddr_un));
+  serv.sun_family = AF_UNIX;
+  strncpy (serv.sun_path, path, strlen (path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+  len = serv.sun_len = SUN_LEN(&serv);
+#else
+  len = sizeof (serv.sun_family) + strlen (serv.sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+  ret = bind (sock, (struct sockaddr *) &serv, len);
+  if (ret < 0)
+    {
+      zlog_warn ("Can't bind to unix socket %s: %s", 
+                 path, safe_strerror (errno));
+      zlog_warn ("zebra can't provide full functionality due to above error");
+      close (sock);
+      return;
+    }
+
+  ret = listen (sock, 5);
+  if (ret < 0)
+    {
+      zlog_warn ("Can't listen to unix socket %s: %s", 
+                 path, safe_strerror (errno));
+      zlog_warn ("zebra can't provide full functionality due to above error");
+      close (sock);
+      return;
+    }
+
+  umask (old_mask);
+
+  zebra_event (ZEBRA_SERV, sock, NULL);
+}
+#endif /* HAVE_TCP_ZEBRA */
+
+
+static void
+zebra_event (enum event event, int sock, struct zserv *client)
+{
+  switch (event)
+    {
+    case ZEBRA_SERV:
+      thread_add_read (zebrad.master, zebra_accept, client, sock);
+      break;
+    case ZEBRA_READ:
+      client->t_read = 
+       thread_add_read (zebrad.master, zebra_client_read, client, sock);
+      break;
+    case ZEBRA_WRITE:
+      /**/
+      break;
+    }
+}
+
+#define ZEBRA_TIME_BUF 32
+static char *
+zserv_time_buf(time_t *time1, char *buf, int buflen)
+{
+  struct tm *tm;
+  time_t now;
+
+  assert (buf != NULL);
+  assert (buflen >= ZEBRA_TIME_BUF);
+  assert (time1 != NULL);
+
+  if (!*time1)
+    {
+      snprintf(buf, buflen, "never   ");
+      return (buf);
+    }
+
+  now = quagga_time(NULL);
+  now -= *time1;
+  tm = gmtime(&now);
+
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+
+  if (now < ONE_DAY_SECOND)
+    snprintf (buf, buflen, "%02d:%02d:%02d",
+             tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (now < ONE_WEEK_SECOND)
+    snprintf (buf, buflen, "%dd%02dh%02dm",
+             tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    snprintf (buf, buflen, "%02dw%dd%02dh",
+             tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  return buf;
+}
+
+static void
+zebra_show_client_detail (struct vty *vty, struct zserv *client)
+{
+  char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF];
+  char wbuf[ZEBRA_TIME_BUF], nhbuf[ZEBRA_TIME_BUF], mbuf[ZEBRA_TIME_BUF];
+
+  vty_out (vty, "Client: %s %s",
+          zebra_route_string(client->proto), VTY_NEWLINE);
+  vty_out (vty, "------------------------ %s", VTY_NEWLINE);
+  vty_out (vty, "FD: %d %s", client->sock, VTY_NEWLINE);
+  vty_out (vty, "Route Table ID: %d %s", client->rtm_table, VTY_NEWLINE);
+
+  vty_out (vty, "Connect Time: %s %s",
+          zserv_time_buf(&client->connect_time, cbuf, ZEBRA_TIME_BUF),
+          VTY_NEWLINE);
+  if (client->nh_reg_time)
+    {
+      vty_out (vty, "Nexthop Registry Time: %s %s",
+              zserv_time_buf(&client->nh_reg_time, nhbuf, ZEBRA_TIME_BUF),
+              VTY_NEWLINE);
+      if (client->nh_last_upd_time)
+       vty_out (vty, "Nexthop Last Update Time: %s %s",
+                zserv_time_buf(&client->nh_last_upd_time, mbuf, ZEBRA_TIME_BUF),
+                VTY_NEWLINE);
+      else
+       vty_out (vty, "No Nexthop Update sent%s", VTY_NEWLINE);
+    }
+  else
+    vty_out (vty, "Not registered for Nexthop Updates%s", VTY_NEWLINE);
+
+  vty_out (vty, "Last Msg Rx Time: %s %s",
+          zserv_time_buf(&client->last_read_time, rbuf, ZEBRA_TIME_BUF),
+          VTY_NEWLINE);
+  vty_out (vty, "Last Msg Tx Time: %s %s",
+          zserv_time_buf(&client->last_write_time, wbuf, ZEBRA_TIME_BUF),
+          VTY_NEWLINE);
+  if (client->last_read_time)
+    vty_out (vty, "Last Rcvd Cmd: %s %s",
+            zserv_command_string(client->last_read_cmd), VTY_NEWLINE);
+  if (client->last_write_time)
+    vty_out (vty, "Last Sent Cmd: %s %s",
+            zserv_command_string(client->last_write_cmd), VTY_NEWLINE);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "Type        Add        Update     Del %s", VTY_NEWLINE);
+  vty_out (vty, "================================================== %s", VTY_NEWLINE);
+  vty_out (vty, "IPv4        %-12d%-12d%-12d%s", client->v4_route_add_cnt,
+          client->v4_route_upd8_cnt, client->v4_route_del_cnt, VTY_NEWLINE);
+  vty_out (vty, "IPv6        %-12d%-12d%-12d%s", client->v6_route_add_cnt,
+          client->v6_route_upd8_cnt, client->v6_route_del_cnt, VTY_NEWLINE);
+  vty_out (vty, "Redist:v4   %-12d%-12d%-12d%s", client->redist_v4_add_cnt, 0,
+          client->redist_v4_del_cnt, VTY_NEWLINE);
+  vty_out (vty, "Redist:v6   %-12d%-12d%-12d%s", client->redist_v6_add_cnt, 0,
+          client->redist_v6_del_cnt, VTY_NEWLINE);
+  vty_out (vty, "Connected   %-12d%-12d%-12d%s", client->ifadd_cnt, 0,
+          client->ifdel_cnt, VTY_NEWLINE);
+  vty_out (vty, "Interface Up Notifications: %d%s", client->ifup_cnt,
+          VTY_NEWLINE);
+  vty_out (vty, "Interface Down Notifications: %d%s", client->ifdown_cnt,
+          VTY_NEWLINE);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+  return;
+}
+
+static void
+zebra_show_client_brief (struct vty *vty, struct zserv *client)
+{
+  char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF];
+  char wbuf[ZEBRA_TIME_BUF];
+
+  vty_out (vty, "%-8s%12s %12s%12s%8d/%-8d%8d/%-8d%s",
+          zebra_route_string(client->proto),
+          zserv_time_buf(&client->connect_time, cbuf, ZEBRA_TIME_BUF),
+          zserv_time_buf(&client->last_read_time, rbuf, ZEBRA_TIME_BUF),
+          zserv_time_buf(&client->last_write_time, wbuf, ZEBRA_TIME_BUF),
+          client->v4_route_add_cnt+client->v4_route_upd8_cnt,
+          client->v4_route_del_cnt,
+          client->v6_route_add_cnt+client->v6_route_upd8_cnt,
+          client->v6_route_del_cnt, VTY_NEWLINE);
+
+}
+
+
+/* Display default rtm_table for all clients. */
+DEFUN (show_table,
+       show_table_cmd,
+       "show table",
+       SHOW_STR
+       "default routing table to use for all clients\n")
+{
+  vty_out (vty, "table %d%s", zebrad.rtm_table_default,
+          VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (config_table, 
+       config_table_cmd,
+       "table TABLENO",
+       "Configure target kernel routing table\n"
+       "TABLE integer\n")
+{
+  zebrad.rtm_table_default = strtol (argv[0], (char**)0, 10);
+  return CMD_SUCCESS;
+}
+
+DEFUN (ip_forwarding,
+       ip_forwarding_cmd,
+       "ip forwarding",
+       IP_STR
+       "Turn on IP forwarding")
+{
+  int ret;
+
+  ret = ipforward ();
+  if (ret == 0)
+    ret = ipforward_on ();
+
+  if (ret == 0)
+    {
+      vty_out (vty, "Can't turn on IP forwarding%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_forwarding,
+       no_ip_forwarding_cmd,
+       "no ip forwarding",
+       NO_STR
+       IP_STR
+       "Turn off IP forwarding")
+{
+  int ret;
+
+  ret = ipforward ();
+  if (ret != 0)
+    ret = ipforward_off ();
+
+  if (ret != 0)
+    {
+      vty_out (vty, "Can't turn off IP forwarding%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* This command is for debugging purpose. */
+DEFUN (show_zebra_client,
+       show_zebra_client_cmd,
+       "show zebra client",
+       SHOW_STR
+       "Zebra information"
+       "Client information")
+{
+  struct listnode *node;
+  struct zserv *client;
+
+  for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
+    zebra_show_client_detail(vty, client);
+
+  return CMD_SUCCESS;
+}
+
+/* This command is for debugging purpose. */
+DEFUN (show_zebra_client_summary,
+       show_zebra_client_summary_cmd,
+       "show zebra client summary",
+       SHOW_STR
+       "Zebra information brief"
+       "Client information brief")
+{
+  struct listnode *node;
+  struct zserv *client;
+
+  vty_out (vty, "Name    Connect Time    Last Read  Last Write  IPv4 Routes       IPv6 Routes    %s",
+          VTY_NEWLINE);
+  vty_out (vty,"--------------------------------------------------------------------------------%s",
+          VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
+    zebra_show_client_brief(vty, client);
+
+  vty_out (vty, "Routes column shows (added+updated)/deleted%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/* Table configuration write function. */
+static int
+config_write_table (struct vty *vty)
+{
+  if (zebrad.rtm_table_default)
+    vty_out (vty, "table %d%s", zebrad.rtm_table_default,
+            VTY_NEWLINE);
+  return 0;
+}
+
+/* table node for routing tables. */
+static struct cmd_node table_node =
+{
+  TABLE_NODE,
+  "",                          /* This node has no interface. */
+  1
+};
+
+/* Only display ip forwarding is enabled or not. */
+DEFUN (show_ip_forwarding,
+       show_ip_forwarding_cmd,
+       "show ip forwarding",
+       SHOW_STR
+       IP_STR
+       "IP forwarding status\n")
+{
+  int ret;
+
+  ret = ipforward ();
+
+  if (ret == 0)
+    vty_out (vty, "IP forwarding is off%s", VTY_NEWLINE);
+  else
+    vty_out (vty, "IP forwarding is on%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+#ifdef HAVE_IPV6
+/* Only display ipv6 forwarding is enabled or not. */
+DEFUN (show_ipv6_forwarding,
+       show_ipv6_forwarding_cmd,
+       "show ipv6 forwarding",
+       SHOW_STR
+       "IPv6 information\n"
+       "Forwarding status\n")
+{
+  int ret;
+
+  ret = ipforward_ipv6 ();
+
+  switch (ret)
+    {
+    case -1:
+      vty_out (vty, "ipv6 forwarding is unknown%s", VTY_NEWLINE);
+      break;
+    case 0:
+      vty_out (vty, "ipv6 forwarding is %s%s", "off", VTY_NEWLINE);
+      break;
+    case 1:
+      vty_out (vty, "ipv6 forwarding is %s%s", "on", VTY_NEWLINE);
+      break;
+    default:
+      vty_out (vty, "ipv6 forwarding is %s%s", "off", VTY_NEWLINE);
+      break;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_forwarding,
+       ipv6_forwarding_cmd,
+       "ipv6 forwarding",
+       IPV6_STR
+       "Turn on IPv6 forwarding")
+{
+  int ret;
+
+  ret = ipforward_ipv6 ();
+  if (ret == 0)
+    ret = ipforward_ipv6_on ();
+
+  if (ret == 0)
+    {
+      vty_out (vty, "Can't turn on IPv6 forwarding%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_forwarding,
+       no_ipv6_forwarding_cmd,
+       "no ipv6 forwarding",
+       NO_STR
+       IPV6_STR
+       "Turn off IPv6 forwarding")
+{
+  int ret;
+
+  ret = ipforward_ipv6 ();
+  if (ret != 0)
+    ret = ipforward_ipv6_off ();
+
+  if (ret != 0)
+    {
+      vty_out (vty, "Can't turn off IPv6 forwarding%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+#endif /* HAVE_IPV6 */
+
+/* IPForwarding configuration write function. */
+static int
+config_write_forwarding (struct vty *vty)
+{
+  /* FIXME: Find better place for that. */
+  router_id_write (vty);
+
+  if (ipforward ())
+    vty_out (vty, "ip forwarding%s", VTY_NEWLINE);
+#ifdef HAVE_IPV6
+  if (ipforward_ipv6 ())
+    vty_out (vty, "ipv6 forwarding%s", VTY_NEWLINE);
+#endif /* HAVE_IPV6 */
+  vty_out (vty, "!%s", VTY_NEWLINE);
+  return 0;
+}
+
+/* table node for routing tables. */
+static struct cmd_node forwarding_node =
+{
+  FORWARDING_NODE,
+  "",                          /* This node has no interface. */
+  1
+};
+
+#ifdef HAVE_FPM
+/* function to write the fpm config info */
+static int 
+config_write_fpm (struct vty *vty)
+{
+  return 
+     fpm_remote_srv_write (vty);
+}
+
+/* Zebra node  */
+static struct cmd_node zebra_node = 
+{
+  ZEBRA_NODE,
+  "",
+  1
+};
+#endif
+
+
+/* Initialisation of zebra and installation of commands. */
+void
+zebra_init (void)
+{
+  /* Client list init. */
+  zebrad.client_list = list_new ();
+
+  /* Install configuration write function. */
+  install_node (&table_node, config_write_table);
+  install_node (&forwarding_node, config_write_forwarding);
+#ifdef HAVE_FPM
+  install_node (&zebra_node, config_write_fpm);
+#endif
+
+  install_element (VIEW_NODE, &show_ip_forwarding_cmd);
+  install_element (CONFIG_NODE, &ip_forwarding_cmd);
+  install_element (CONFIG_NODE, &no_ip_forwarding_cmd);
+  install_element (ENABLE_NODE, &show_zebra_client_cmd);
+  install_element (ENABLE_NODE, &show_zebra_client_summary_cmd);
+
+#ifdef HAVE_NETLINK
+  install_element (VIEW_NODE, &show_table_cmd);
+  install_element (CONFIG_NODE, &config_table_cmd);
+#endif /* HAVE_NETLINK */
+
+#ifdef HAVE_IPV6
+  install_element (VIEW_NODE, &show_ipv6_forwarding_cmd);
+  install_element (CONFIG_NODE, &ipv6_forwarding_cmd);
+  install_element (CONFIG_NODE, &no_ipv6_forwarding_cmd);
+#endif /* HAVE_IPV6 */
+
+  /* Route-map */
+  zebra_route_map_init ();
+}
+
+/* Make zebra server socket, wiping any existing one (see bug #403). */
+void
+zebra_zserv_socket_init (char *path)
+{
+#ifdef HAVE_TCP_ZEBRA
+  zebra_serv ();
+#else
+  zebra_serv_un (path ? path : ZEBRA_SERV_PATH);
+#endif /* HAVE_TCP_ZEBRA */
+}
diff --git a/zebra/zserv.h b/zebra/zserv.h
new file mode 100644 (file)
index 0000000..4af92f0
--- /dev/null
@@ -0,0 +1,149 @@
+/* Zebra daemon server header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_ZSERV_H
+#define _ZEBRA_ZSERV_H
+
+#include "rib.h"
+#include "if.h"
+#include "workqueue.h"
+#include "vrf.h"
+
+/* Default port information. */
+#define ZEBRA_VTY_PORT                2601
+
+/* Default configuration filename. */
+#define DEFAULT_CONFIG_FILE "zebra.conf"
+
+/* Client structure. */
+struct zserv
+{
+  /* Client file descriptor. */
+  int sock;
+
+  /* Input/output buffer to the client. */
+  struct stream *ibuf;
+  struct stream *obuf;
+
+  /* Buffer of data waiting to be written to client. */
+  struct buffer *wb;
+
+  /* Threads for read/write. */
+  struct thread *t_read;
+  struct thread *t_write;
+
+  /* Thread for delayed close. */
+  struct thread *t_suicide;
+
+  /* default routing table this client munges */
+  int rtm_table;
+
+  /* This client's redistribute flag. */
+  vrf_bitmap_t redist[ZEBRA_ROUTE_MAX];
+
+  /* Redistribute default route flag. */
+  vrf_bitmap_t redist_default;
+
+  /* Interface information. */
+  vrf_bitmap_t ifinfo;
+
+  /* Router-id information. */
+  vrf_bitmap_t ridinfo;
+
+  /* client's protocol */
+  u_char proto;
+
+  /* Statistics */
+  u_int32_t redist_v4_add_cnt;
+  u_int32_t redist_v4_del_cnt;
+  u_int32_t redist_v6_add_cnt;
+  u_int32_t redist_v6_del_cnt;
+  u_int32_t v4_route_add_cnt;
+  u_int32_t v4_route_upd8_cnt;
+  u_int32_t v4_route_del_cnt;
+  u_int32_t v6_route_add_cnt;
+  u_int32_t v6_route_del_cnt;
+  u_int32_t v6_route_upd8_cnt;
+  u_int32_t connected_rt_add_cnt;
+  u_int32_t connected_rt_del_cnt;
+  u_int32_t ifup_cnt;
+  u_int32_t ifdown_cnt;
+  u_int32_t ifadd_cnt;
+  u_int32_t ifdel_cnt;
+
+  time_t connect_time;
+  time_t last_read_time;
+  time_t last_write_time;
+  time_t nh_reg_time;
+  time_t nh_dereg_time;
+  time_t nh_last_upd_time;
+
+  int last_read_cmd;
+  int last_write_cmd;
+};
+
+/* Zebra instance */
+struct zebra_t
+{
+  /* Thread master */
+  struct thread_master *master;
+  struct list *client_list;
+
+  /* default table */
+  int rtm_table_default;
+
+  /* rib work queue */
+  struct work_queue *ribq;
+  struct meta_queue *mq;
+};
+
+/* Prototypes. */
+extern void zebra_init (void);
+extern void zebra_if_init (void);
+extern void zebra_zserv_socket_init (char *path);
+extern void hostinfo_get (void);
+extern void rib_init (void);
+extern void interface_list (struct zebra_vrf *);
+extern void route_read (struct zebra_vrf *);
+extern void kernel_init (struct zebra_vrf *);
+extern void kernel_terminate (struct zebra_vrf *);
+extern void zebra_route_map_init (void);
+extern void zebra_snmp_init (void);
+extern void zebra_vty_init (void);
+
+extern int zsend_interface_add (struct zserv *, struct interface *);
+extern int zsend_interface_delete (struct zserv *, struct interface *);
+extern int zsend_interface_address (int, struct zserv *, struct interface *,
+                                    struct connected *);
+extern int zsend_interface_update (int, struct zserv *, struct interface *);
+extern int zsend_route_multipath (int, struct zserv *, struct prefix *, 
+                                  struct rib *);
+extern int zsend_router_id_update (struct zserv *, struct prefix *,
+                                   vrf_id_t);
+
+extern int zsend_interface_link_params (struct zserv *, struct interface *);
+
+extern pid_t pid;
+
+extern void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t);
+extern int zebra_server_send_message(struct zserv *client);
+
+#endif /* _ZEBRA_ZEBRA_H */